nikcli 0.0.6

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 (602) hide show
  1. package/.turbo/turbo-typecheck.log +1 -0
  2. package/AGENTS.md +27 -0
  3. package/Dockerfile +18 -0
  4. package/README.md +15 -0
  5. package/bin/nikcli +84 -0
  6. package/config.json +13 -0
  7. package/docs/tailscale-mobile/01-tailscale-setup.md +94 -0
  8. package/docs/tailscale-mobile/02-host-setup.md +115 -0
  9. package/docs/tailscale-mobile/03-phone-and-serve.md +134 -0
  10. package/docs/tailscale-mobile/README.md +59 -0
  11. package/examples/README.md +54 -0
  12. package/package.json +147 -0
  13. package/parsers-config.ts +253 -0
  14. package/script/build.ts +179 -0
  15. package/script/postinstall.mjs +125 -0
  16. package/script/publish-registries.ts +187 -0
  17. package/script/publish.ts +100 -0
  18. package/script/schema.ts +47 -0
  19. package/script/seed-e2e.ts +50 -0
  20. package/sequential-prancing-forest.md +373 -0
  21. package/src/acp/README.md +164 -0
  22. package/src/acp/agent.ts +1303 -0
  23. package/src/acp/session.ts +105 -0
  24. package/src/acp/types.ts +22 -0
  25. package/src/agent/agent.ts +528 -0
  26. package/src/agent/generate.txt +32 -0
  27. package/src/agent/prompt/compaction.txt +14 -0
  28. package/src/agent/prompt/explore.txt +18 -0
  29. package/src/agent/prompt/summary.txt +11 -0
  30. package/src/agent/prompt/title.txt +44 -0
  31. package/src/auth/index.ts +73 -0
  32. package/src/bun/index.ts +119 -0
  33. package/src/bun/registry.ts +54 -0
  34. package/src/bus/bus-event.ts +43 -0
  35. package/src/bus/global.ts +10 -0
  36. package/src/bus/index.ts +105 -0
  37. package/src/chatbot/handlers.ts +150 -0
  38. package/src/chatbot/index.ts +132 -0
  39. package/src/cli/bootstrap.ts +17 -0
  40. package/src/cli/cmd/acp.ts +69 -0
  41. package/src/cli/cmd/ads.ts +377 -0
  42. package/src/cli/cmd/agent.ts +259 -0
  43. package/src/cli/cmd/auth.ts +400 -0
  44. package/src/cli/cmd/chatbot.ts +420 -0
  45. package/src/cli/cmd/cmd.ts +7 -0
  46. package/src/cli/cmd/companion.ts +81 -0
  47. package/src/cli/cmd/connectors.ts +593 -0
  48. package/src/cli/cmd/debug/agent.ts +166 -0
  49. package/src/cli/cmd/debug/config.ts +16 -0
  50. package/src/cli/cmd/debug/file.ts +97 -0
  51. package/src/cli/cmd/debug/index.ts +48 -0
  52. package/src/cli/cmd/debug/lsp.ts +52 -0
  53. package/src/cli/cmd/debug/ripgrep.ts +87 -0
  54. package/src/cli/cmd/debug/scrap.ts +16 -0
  55. package/src/cli/cmd/debug/skill.ts +16 -0
  56. package/src/cli/cmd/debug/snapshot.ts +52 -0
  57. package/src/cli/cmd/export.ts +88 -0
  58. package/src/cli/cmd/generate.ts +38 -0
  59. package/src/cli/cmd/github.ts +412 -0
  60. package/src/cli/cmd/image-model.ts +128 -0
  61. package/src/cli/cmd/import.ts +201 -0
  62. package/src/cli/cmd/lovable.ts +128 -0
  63. package/src/cli/cmd/mcp.ts +738 -0
  64. package/src/cli/cmd/mobile.ts +223 -0
  65. package/src/cli/cmd/models.ts +77 -0
  66. package/src/cli/cmd/plug.ts +231 -0
  67. package/src/cli/cmd/pr.ts +104 -0
  68. package/src/cli/cmd/rag-model.ts +167 -0
  69. package/src/cli/cmd/remote.ts +416 -0
  70. package/src/cli/cmd/run.ts +589 -0
  71. package/src/cli/cmd/serve.ts +51 -0
  72. package/src/cli/cmd/session.ts +133 -0
  73. package/src/cli/cmd/speak-model.ts +204 -0
  74. package/src/cli/cmd/stats.ts +402 -0
  75. package/src/cli/cmd/tui/app.tsx +841 -0
  76. package/src/cli/cmd/tui/attach.ts +31 -0
  77. package/src/cli/cmd/tui/component/border.tsx +75 -0
  78. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  79. package/src/cli/cmd/tui/component/dialog-command.tsx +172 -0
  80. package/src/cli/cmd/tui/component/dialog-config.tsx +291 -0
  81. package/src/cli/cmd/tui/component/dialog-connectors.tsx +440 -0
  82. package/src/cli/cmd/tui/component/dialog-image-model.tsx +97 -0
  83. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  84. package/src/cli/cmd/tui/component/dialog-model.tsx +234 -0
  85. package/src/cli/cmd/tui/component/dialog-provider.tsx +260 -0
  86. package/src/cli/cmd/tui/component/dialog-rag-model.tsx +217 -0
  87. package/src/cli/cmd/tui/component/dialog-remote.tsx +489 -0
  88. package/src/cli/cmd/tui/component/dialog-session-list.tsx +170 -0
  89. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  90. package/src/cli/cmd/tui/component/dialog-settings/index.tsx +59 -0
  91. package/src/cli/cmd/tui/component/dialog-settings/prompt.tsx +40 -0
  92. package/src/cli/cmd/tui/component/dialog-settings/sidebar.tsx +39 -0
  93. package/src/cli/cmd/tui/component/dialog-settings/spinner.tsx +62 -0
  94. package/src/cli/cmd/tui/component/dialog-settings/ui.tsx +58 -0
  95. package/src/cli/cmd/tui/component/dialog-skills.tsx +117 -0
  96. package/src/cli/cmd/tui/component/dialog-speak-model.tsx +304 -0
  97. package/src/cli/cmd/tui/component/dialog-stash.tsx +87 -0
  98. package/src/cli/cmd/tui/component/dialog-status.tsx +165 -0
  99. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  100. package/src/cli/cmd/tui/component/dialog-theme-create.tsx +717 -0
  101. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +52 -0
  102. package/src/cli/cmd/tui/component/dialog-workspace-list.tsx +350 -0
  103. package/src/cli/cmd/tui/component/error-component.tsx +91 -0
  104. package/src/cli/cmd/tui/component/logo.tsx +103 -0
  105. package/src/cli/cmd/tui/component/plugin-route-missing.tsx +14 -0
  106. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +669 -0
  107. package/src/cli/cmd/tui/component/prompt/frecency.tsx +89 -0
  108. package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
  109. package/src/cli/cmd/tui/component/prompt/index.tsx +2165 -0
  110. package/src/cli/cmd/tui/component/prompt/stash.tsx +63 -0
  111. package/src/cli/cmd/tui/component/spinner.tsx +24 -0
  112. package/src/cli/cmd/tui/component/startup-loading.tsx +63 -0
  113. package/src/cli/cmd/tui/component/table/markdown-table.tsx +267 -0
  114. package/src/cli/cmd/tui/component/table-db/db/connections.ts +75 -0
  115. package/src/cli/cmd/tui/component/table-db/db/db-connection.ts +223 -0
  116. package/src/cli/cmd/tui/component/table-db/db/db-preview.ts +202 -0
  117. package/src/cli/cmd/tui/component/table-db/db/factory.ts +77 -0
  118. package/src/cli/cmd/tui/component/table-db/db/index.ts +9 -0
  119. package/src/cli/cmd/tui/component/table-db/db/mysql-connection.ts +330 -0
  120. package/src/cli/cmd/tui/component/table-db/db/postgres-connection.ts +338 -0
  121. package/src/cli/cmd/tui/component/table-db/db/sqlite-connection.ts +302 -0
  122. package/src/cli/cmd/tui/component/table-db/db/types.ts +108 -0
  123. package/src/cli/cmd/tui/component/table-db/table/dbedit-hooks.ts +74 -0
  124. package/src/cli/cmd/tui/component/table-db/table/index.ts +15 -0
  125. package/src/cli/cmd/tui/component/table-db/table/table-events.ts +54 -0
  126. package/src/cli/cmd/tui/component/table-db/table/table-formatters.ts +191 -0
  127. package/src/cli/cmd/tui/component/table-db/table/table-hooks.ts +105 -0
  128. package/src/cli/cmd/tui/component/table-db/table/table-keyboard-handler.ts +255 -0
  129. package/src/cli/cmd/tui/component/table-db/table/table-layout-engine.ts +208 -0
  130. package/src/cli/cmd/tui/component/table-db/table/table-renderable.ts +486 -0
  131. package/src/cli/cmd/tui/component/table-db/table/table-selection-manager.ts +136 -0
  132. package/src/cli/cmd/tui/component/table-db/table/table-state.ts +198 -0
  133. package/src/cli/cmd/tui/component/table-db/table/types.ts +69 -0
  134. package/src/cli/cmd/tui/component/table-db/ui/db-visualizer.tsx +71 -0
  135. package/src/cli/cmd/tui/component/table-db/ui/index.ts +2 -0
  136. package/src/cli/cmd/tui/component/table-db/ui/table-renderer.ts +607 -0
  137. package/src/cli/cmd/tui/component/textarea-keybindings.ts +73 -0
  138. package/src/cli/cmd/tui/component/tips.tsx +195 -0
  139. package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
  140. package/src/cli/cmd/tui/context/args.tsx +14 -0
  141. package/src/cli/cmd/tui/context/directory.ts +13 -0
  142. package/src/cli/cmd/tui/context/exit.tsx +24 -0
  143. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  144. package/src/cli/cmd/tui/context/keybind.tsx +102 -0
  145. package/src/cli/cmd/tui/context/kv.tsx +52 -0
  146. package/src/cli/cmd/tui/context/local.tsx +458 -0
  147. package/src/cli/cmd/tui/context/plugin-keybinds.ts +41 -0
  148. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  149. package/src/cli/cmd/tui/context/route.tsx +54 -0
  150. package/src/cli/cmd/tui/context/sdk.tsx +128 -0
  151. package/src/cli/cmd/tui/context/server.tsx +8 -0
  152. package/src/cli/cmd/tui/context/sync.tsx +510 -0
  153. package/src/cli/cmd/tui/context/theme/abyss.json +233 -0
  154. package/src/cli/cmd/tui/context/theme/apple.json +235 -0
  155. package/src/cli/cmd/tui/context/theme/arctic.json +232 -0
  156. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  157. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  158. package/src/cli/cmd/tui/context/theme/ayuai.json +229 -0
  159. package/src/cli/cmd/tui/context/theme/blood.json +229 -0
  160. package/src/cli/cmd/tui/context/theme/carbonfox.json +248 -0
  161. package/src/cli/cmd/tui/context/theme/catmoe.json +235 -0
  162. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
  163. package/src/cli/cmd/tui/context/theme/catppuccin-latte.json +233 -0
  164. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  165. package/src/cli/cmd/tui/context/theme/catppuccin.json +259 -0
  166. package/src/cli/cmd/tui/context/theme/charcoal.json +230 -0
  167. package/src/cli/cmd/tui/context/theme/chromatic.json +235 -0
  168. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  169. package/src/cli/cmd/tui/context/theme/cosmic.json +234 -0
  170. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  171. package/src/cli/cmd/tui/context/theme/cyber.json +235 -0
  172. package/src/cli/cmd/tui/context/theme/dawnfox.json +229 -0
  173. package/src/cli/cmd/tui/context/theme/dimension.json +235 -0
  174. package/src/cli/cmd/tui/context/theme/dracula-official.json +222 -0
  175. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  176. package/src/cli/cmd/tui/context/theme/dream.json +235 -0
  177. package/src/cli/cmd/tui/context/theme/duo.json +235 -0
  178. package/src/cli/cmd/tui/context/theme/dusk.json +235 -0
  179. package/src/cli/cmd/tui/context/theme/ebony.json +232 -0
  180. package/src/cli/cmd/tui/context/theme/equilibrium.json +232 -0
  181. package/src/cli/cmd/tui/context/theme/ethereal.json +235 -0
  182. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  183. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  184. package/src/cli/cmd/tui/context/theme/fusion.json +235 -0
  185. package/src/cli/cmd/tui/context/theme/ghost.json +235 -0
  186. package/src/cli/cmd/tui/context/theme/github-dark.json +229 -0
  187. package/src/cli/cmd/tui/context/theme/github-dimmed.json +231 -0
  188. package/src/cli/cmd/tui/context/theme/github-light.json +229 -0
  189. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  190. package/src/cli/cmd/tui/context/theme/glass.json +235 -0
  191. package/src/cli/cmd/tui/context/theme/gold.json +235 -0
  192. package/src/cli/cmd/tui/context/theme/gone.json +234 -0
  193. package/src/cli/cmd/tui/context/theme/greyscale.json +229 -0
  194. package/src/cli/cmd/tui/context/theme/gruvbox.json +242 -0
  195. package/src/cli/cmd/tui/context/theme/hacker.json +229 -0
  196. package/src/cli/cmd/tui/context/theme/holo.json +235 -0
  197. package/src/cli/cmd/tui/context/theme/ink.json +235 -0
  198. package/src/cli/cmd/tui/context/theme/jet.json +233 -0
  199. package/src/cli/cmd/tui/context/theme/kanagawa.json +227 -0
  200. package/src/cli/cmd/tui/context/theme/lavender.json +236 -0
  201. package/src/cli/cmd/tui/context/theme/lightph.json +235 -0
  202. package/src/cli/cmd/tui/context/theme/lucent-orng.json +237 -0
  203. package/src/cli/cmd/tui/context/theme/material-ocean.json +230 -0
  204. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  205. package/src/cli/cmd/tui/context/theme/matrix.json +227 -0
  206. package/src/cli/cmd/tui/context/theme/mercury.json +245 -0
  207. package/src/cli/cmd/tui/context/theme/midnight.json +235 -0
  208. package/src/cli/cmd/tui/context/theme/modern.json +235 -0
  209. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  210. package/src/cli/cmd/tui/context/theme/muted.json +229 -0
  211. package/src/cli/cmd/tui/context/theme/neon.json +229 -0
  212. package/src/cli/cmd/tui/context/theme/neonfusion.json +235 -0
  213. package/src/cli/cmd/tui/context/theme/neutral.json +235 -0
  214. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  215. package/src/cli/cmd/tui/context/theme/nikcli.json +245 -0
  216. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  217. package/src/cli/cmd/tui/context/theme/nordic.json +235 -0
  218. package/src/cli/cmd/tui/context/theme/nova.json +235 -0
  219. package/src/cli/cmd/tui/context/theme/obsidian.json +234 -0
  220. package/src/cli/cmd/tui/context/theme/one-dark.json +231 -0
  221. package/src/cli/cmd/tui/context/theme/one-pro.json +229 -0
  222. package/src/cli/cmd/tui/context/theme/onyx.json +233 -0
  223. package/src/cli/cmd/tui/context/theme/orng.json +249 -0
  224. package/src/cli/cmd/tui/context/theme/osaka-jade.json +240 -0
  225. package/src/cli/cmd/tui/context/theme/oxocarbon.json +229 -0
  226. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  227. package/src/cli/cmd/tui/context/theme/poimandres.json +230 -0
  228. package/src/cli/cmd/tui/context/theme/prism.json +235 -0
  229. package/src/cli/cmd/tui/context/theme/radiant.json +235 -0
  230. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  231. package/src/cli/cmd/tui/context/theme/shadow.json +235 -0
  232. package/src/cli/cmd/tui/context/theme/silicon.json +235 -0
  233. package/src/cli/cmd/tui/context/theme/slate.json +233 -0
  234. package/src/cli/cmd/tui/context/theme/soft.json +235 -0
  235. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  236. package/src/cli/cmd/tui/context/theme/spectrum.json +235 -0
  237. package/src/cli/cmd/tui/context/theme/starlight.json +233 -0
  238. package/src/cli/cmd/tui/context/theme/sunrise.json +235 -0
  239. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  240. package/src/cli/cmd/tui/context/theme/tech.json +235 -0
  241. package/src/cli/cmd/tui/context/theme/tokyonight-storm.json +245 -0
  242. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  243. package/src/cli/cmd/tui/context/theme/vapor.json +235 -0
  244. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  245. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  246. package/src/cli/cmd/tui/context/theme/vivid.json +232 -0
  247. package/src/cli/cmd/tui/context/theme/void.json +235 -0
  248. package/src/cli/cmd/tui/context/theme/vscode.json +235 -0
  249. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  250. package/src/cli/cmd/tui/context/theme/zinc.json +236 -0
  251. package/src/cli/cmd/tui/context/theme.tsx +1303 -0
  252. package/src/cli/cmd/tui/event.ts +48 -0
  253. package/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +152 -0
  254. package/src/cli/cmd/tui/feature-plugins/home/tips.tsx +50 -0
  255. package/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx +63 -0
  256. package/src/cli/cmd/tui/feature-plugins/sidebar/files.tsx +62 -0
  257. package/src/cli/cmd/tui/feature-plugins/sidebar/footer.tsx +93 -0
  258. package/src/cli/cmd/tui/feature-plugins/sidebar/lsp.tsx +66 -0
  259. package/src/cli/cmd/tui/feature-plugins/sidebar/mcp.tsx +96 -0
  260. package/src/cli/cmd/tui/feature-plugins/sidebar/todo.tsx +48 -0
  261. package/src/cli/cmd/tui/feature-plugins/system/plugins.tsx +288 -0
  262. package/src/cli/cmd/tui/plugin/api.tsx +407 -0
  263. package/src/cli/cmd/tui/plugin/index.ts +3 -0
  264. package/src/cli/cmd/tui/plugin/internal.ts +25 -0
  265. package/src/cli/cmd/tui/plugin/runtime.ts +1048 -0
  266. package/src/cli/cmd/tui/plugin/slots.tsx +61 -0
  267. package/src/cli/cmd/tui/routes/home.tsx +153 -0
  268. package/src/cli/cmd/tui/routes/session/dbedit.tsx +474 -0
  269. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +65 -0
  270. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +110 -0
  271. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +105 -0
  272. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  273. package/src/cli/cmd/tui/routes/session/footer.tsx +75 -0
  274. package/src/cli/cmd/tui/routes/session/header.tsx +177 -0
  275. package/src/cli/cmd/tui/routes/session/index.tsx +2280 -0
  276. package/src/cli/cmd/tui/routes/session/permission.tsx +540 -0
  277. package/src/cli/cmd/tui/routes/session/question.tsx +435 -0
  278. package/src/cli/cmd/tui/routes/session/sidebar.tsx +313 -0
  279. package/src/cli/cmd/tui/thread.ts +174 -0
  280. package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
  281. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
  282. package/src/cli/cmd/tui/ui/dialog-export-options.tsx +204 -0
  283. package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
  284. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +102 -0
  285. package/src/cli/cmd/tui/ui/dialog-select.tsx +389 -0
  286. package/src/cli/cmd/tui/ui/dialog.tsx +180 -0
  287. package/src/cli/cmd/tui/ui/link.tsx +34 -0
  288. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  289. package/src/cli/cmd/tui/ui/toast.tsx +138 -0
  290. package/src/cli/cmd/tui/util/clipboard.ts +154 -0
  291. package/src/cli/cmd/tui/util/editor.ts +32 -0
  292. package/src/cli/cmd/tui/util/signal.ts +7 -0
  293. package/src/cli/cmd/tui/util/terminal.ts +114 -0
  294. package/src/cli/cmd/tui/util/transcript.ts +98 -0
  295. package/src/cli/cmd/tui/win32.ts +110 -0
  296. package/src/cli/cmd/tui/worker.ts +156 -0
  297. package/src/cli/cmd/uninstall.ts +357 -0
  298. package/src/cli/cmd/upgrade.ts +72 -0
  299. package/src/cli/cmd/web.ts +87 -0
  300. package/src/cli/cmd/workspace-serve.ts +16 -0
  301. package/src/cli/error.ts +57 -0
  302. package/src/cli/network.ts +55 -0
  303. package/src/cli/remote/index.ts +36 -0
  304. package/src/cli/remote/notifications.ts +104 -0
  305. package/src/cli/remote/qr-renderer.ts +86 -0
  306. package/src/cli/remote/remote-service.ts +757 -0
  307. package/src/cli/remote/session-manager.ts +284 -0
  308. package/src/cli/remote/subagent-hooks.ts +151 -0
  309. package/src/cli/remote/types.ts +121 -0
  310. package/src/cli/ui.ts +96 -0
  311. package/src/cli/upgrade.ts +25 -0
  312. package/src/command/index.ts +174 -0
  313. package/src/command/template/initialize.txt +10 -0
  314. package/src/command/template/review.txt +99 -0
  315. package/src/config/config.ts +1760 -0
  316. package/src/config/markdown.ts +88 -0
  317. package/src/config/migrate-tui-config.ts +155 -0
  318. package/src/config/paths.ts +174 -0
  319. package/src/config/tui-schema.ts +36 -0
  320. package/src/config/tui.ts +209 -0
  321. package/src/connectors/api/base.ts +75 -0
  322. package/src/connectors/api/figma.ts +103 -0
  323. package/src/connectors/api/github.ts +247 -0
  324. package/src/connectors/api/lovable.ts +126 -0
  325. package/src/connectors/api/slack.ts +137 -0
  326. package/src/connectors/auth.ts +68 -0
  327. package/src/connectors/cache.ts +119 -0
  328. package/src/connectors/credentials.ts +81 -0
  329. package/src/connectors/index.ts +202 -0
  330. package/src/connectors/registry.ts +358 -0
  331. package/src/docs/context.ts +120 -0
  332. package/src/docs/library.ts +189 -0
  333. package/src/env/index.ts +26 -0
  334. package/src/file/ignore.ts +83 -0
  335. package/src/file/index.ts +411 -0
  336. package/src/file/ripgrep.ts +402 -0
  337. package/src/file/time.ts +65 -0
  338. package/src/file/watcher.ts +127 -0
  339. package/src/flag/flag.ts +128 -0
  340. package/src/format/formatter.ts +356 -0
  341. package/src/format/index.ts +137 -0
  342. package/src/global/index.ts +57 -0
  343. package/src/id/id.ts +83 -0
  344. package/src/ide/index.ts +76 -0
  345. package/src/index.ts +184 -0
  346. package/src/installation/index.ts +246 -0
  347. package/src/lsp/client.ts +250 -0
  348. package/src/lsp/index.ts +483 -0
  349. package/src/lsp/language.ts +119 -0
  350. package/src/lsp/server.ts +2046 -0
  351. package/src/mcp/auth.ts +121 -0
  352. package/src/mcp/index.ts +860 -0
  353. package/src/mcp/oauth-callback.ts +198 -0
  354. package/src/mcp/oauth-provider.ts +148 -0
  355. package/src/mobile/auth.ts +97 -0
  356. package/src/mobile/github-repo.ts +185 -0
  357. package/src/patch/index.ts +631 -0
  358. package/src/permission/arity.ts +150 -0
  359. package/src/permission/dbedit.ts +236 -0
  360. package/src/permission/index.ts +210 -0
  361. package/src/permission/next.ts +287 -0
  362. package/src/plugin/codex.ts +493 -0
  363. package/src/plugin/copilot.ts +261 -0
  364. package/src/plugin/index.ts +714 -0
  365. package/src/plugin/install.ts +379 -0
  366. package/src/plugin/meta.ts +165 -0
  367. package/src/plugin/shared.ts +188 -0
  368. package/src/project/bootstrap.ts +35 -0
  369. package/src/project/instance.ts +84 -0
  370. package/src/project/project.ts +373 -0
  371. package/src/project/state.ts +66 -0
  372. package/src/project/vcs.ts +76 -0
  373. package/src/prompt/stash-store.ts +93 -0
  374. package/src/provider/auth.ts +147 -0
  375. package/src/provider/models-macro.ts +22 -0
  376. package/src/provider/models.ts +216 -0
  377. package/src/provider/provider.ts +1483 -0
  378. package/src/provider/sdk/openai-compatible/src/README.md +5 -0
  379. package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
  380. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
  381. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
  382. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
  383. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
  384. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
  385. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
  386. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1732 -0
  387. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
  388. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
  389. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
  390. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
  391. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
  392. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
  393. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
  394. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
  395. package/src/provider/transform.ts +828 -0
  396. package/src/pty/index.ts +241 -0
  397. package/src/question/index.ts +171 -0
  398. package/src/rag/chunk.ts +43 -0
  399. package/src/rag/embed.ts +179 -0
  400. package/src/rag/index.ts +376 -0
  401. package/src/rag/storage.ts +76 -0
  402. package/src/scheduler/index.ts +61 -0
  403. package/src/server/error.ts +36 -0
  404. package/src/server/event.ts +7 -0
  405. package/src/server/mdns.ts +59 -0
  406. package/src/server/routes/chatbot.ts +205 -0
  407. package/src/server/routes/companion.ts +729 -0
  408. package/src/server/routes/config.ts +92 -0
  409. package/src/server/routes/connectors.ts +121 -0
  410. package/src/server/routes/dbedit.ts +76 -0
  411. package/src/server/routes/experimental.ts +210 -0
  412. package/src/server/routes/file.ts +197 -0
  413. package/src/server/routes/global.ts +135 -0
  414. package/src/server/routes/mcp.ts +225 -0
  415. package/src/server/routes/mobile.ts +2044 -0
  416. package/src/server/routes/permission.ts +68 -0
  417. package/src/server/routes/project.ts +82 -0
  418. package/src/server/routes/provider.ts +235 -0
  419. package/src/server/routes/pty.ts +169 -0
  420. package/src/server/routes/question.ts +98 -0
  421. package/src/server/routes/session.ts +968 -0
  422. package/src/server/routes/tui.ts +379 -0
  423. package/src/server/routes/workspace.ts +104 -0
  424. package/src/server/server.ts +761 -0
  425. package/src/server/ssh.ts +207 -0
  426. package/src/session/auth.ts +402 -0
  427. package/src/session/compaction.ts +253 -0
  428. package/src/session/generate.ts +38 -0
  429. package/src/session/index.ts +598 -0
  430. package/src/session/llm.ts +273 -0
  431. package/src/session/message-v2.ts +836 -0
  432. package/src/session/message.ts +189 -0
  433. package/src/session/processor.ts +408 -0
  434. package/src/session/prompt/anthropic-20250930.txt +165 -0
  435. package/src/session/prompt/anthropic.txt +105 -0
  436. package/src/session/prompt/anthropic_spoof.txt +1 -0
  437. package/src/session/prompt/beast.txt +147 -0
  438. package/src/session/prompt/build-switch.txt +5 -0
  439. package/src/session/prompt/codex_header.txt +79 -0
  440. package/src/session/prompt/copilot-gpt-5.txt +143 -0
  441. package/src/session/prompt/gemini.txt +155 -0
  442. package/src/session/prompt/max-steps.txt +16 -0
  443. package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
  444. package/src/session/prompt/plan.txt +25 -0
  445. package/src/session/prompt/qwen.txt +108 -0
  446. package/src/session/prompt.ts +1942 -0
  447. package/src/session/retry.ts +90 -0
  448. package/src/session/revert.ts +120 -0
  449. package/src/session/stats.ts +404 -0
  450. package/src/session/status.ts +84 -0
  451. package/src/session/summary.ts +184 -0
  452. package/src/session/system.ts +195 -0
  453. package/src/session/toast.tsx +105 -0
  454. package/src/session/todo.ts +258 -0
  455. package/src/session/uninstall.ts +357 -0
  456. package/src/share/share-next.ts +421 -0
  457. package/src/share/share.ts +92 -0
  458. package/src/shell/shell.ts +65 -0
  459. package/src/skill/index.ts +1 -0
  460. package/src/skill/skill.ts +232 -0
  461. package/src/snapshot/index.ts +297 -0
  462. package/src/storage/storage.ts +227 -0
  463. package/src/tool/apply_patch.ts +288 -0
  464. package/src/tool/apply_patch.txt +33 -0
  465. package/src/tool/bash.ts +252 -0
  466. package/src/tool/bash.txt +115 -0
  467. package/src/tool/batch.ts +175 -0
  468. package/src/tool/batch.txt +24 -0
  469. package/src/tool/codesearch.ts +132 -0
  470. package/src/tool/codesearch.txt +12 -0
  471. package/src/tool/context_collect.ts +152 -0
  472. package/src/tool/context_collect.txt +9 -0
  473. package/src/tool/context_diagnostics.ts +81 -0
  474. package/src/tool/context_diagnostics.txt +5 -0
  475. package/src/tool/context_related.ts +117 -0
  476. package/src/tool/context_related.txt +5 -0
  477. package/src/tool/context_search.ts +108 -0
  478. package/src/tool/context_search.txt +8 -0
  479. package/src/tool/db-diff.ts +434 -0
  480. package/src/tool/db-table.txt +15 -0
  481. package/src/tool/docs_add.ts +50 -0
  482. package/src/tool/docs_add.txt +5 -0
  483. package/src/tool/docs_context.ts +56 -0
  484. package/src/tool/docs_context.txt +4 -0
  485. package/src/tool/docs_gap_report.ts +79 -0
  486. package/src/tool/docs_gap_report.txt +7 -0
  487. package/src/tool/docs_load.ts +41 -0
  488. package/src/tool/docs_load.txt +4 -0
  489. package/src/tool/docs_request.ts +129 -0
  490. package/src/tool/docs_request.txt +7 -0
  491. package/src/tool/docs_search.ts +51 -0
  492. package/src/tool/docs_search.txt +6 -0
  493. package/src/tool/docs_unload.ts +38 -0
  494. package/src/tool/docs_unload.txt +5 -0
  495. package/src/tool/edit.ts +614 -0
  496. package/src/tool/edit.txt +10 -0
  497. package/src/tool/external-directory.ts +32 -0
  498. package/src/tool/generate_image.ts +174 -0
  499. package/src/tool/generate_image.txt +12 -0
  500. package/src/tool/glob.ts +79 -0
  501. package/src/tool/glob.txt +6 -0
  502. package/src/tool/grep.ts +153 -0
  503. package/src/tool/grep.txt +8 -0
  504. package/src/tool/invalid.ts +17 -0
  505. package/src/tool/ls.ts +116 -0
  506. package/src/tool/ls.txt +1 -0
  507. package/src/tool/lsp.ts +96 -0
  508. package/src/tool/lsp.txt +19 -0
  509. package/src/tool/memory_search.ts +141 -0
  510. package/src/tool/memory_search.txt +8 -0
  511. package/src/tool/multiedit.ts +46 -0
  512. package/src/tool/multiedit.txt +41 -0
  513. package/src/tool/plan-enter.txt +14 -0
  514. package/src/tool/plan-exit.txt +13 -0
  515. package/src/tool/plan.ts +130 -0
  516. package/src/tool/question.ts +33 -0
  517. package/src/tool/question.txt +10 -0
  518. package/src/tool/rag_index.ts +77 -0
  519. package/src/tool/rag_index.txt +10 -0
  520. package/src/tool/rag_reset.ts +26 -0
  521. package/src/tool/rag_reset.txt +4 -0
  522. package/src/tool/rag_search.ts +62 -0
  523. package/src/tool/rag_search.txt +6 -0
  524. package/src/tool/rag_status.ts +45 -0
  525. package/src/tool/rag_status.txt +4 -0
  526. package/src/tool/read.ts +203 -0
  527. package/src/tool/read.txt +12 -0
  528. package/src/tool/registry.ts +214 -0
  529. package/src/tool/skill.ts +169 -0
  530. package/src/tool/skill.txt +3 -0
  531. package/src/tool/smart_docs.ts +74 -0
  532. package/src/tool/smart_docs.txt +7 -0
  533. package/src/tool/speak/elevenlabs.ts +201 -0
  534. package/src/tool/speak/openrouter.ts +240 -0
  535. package/src/tool/speak/provider.ts +83 -0
  536. package/src/tool/speak.ts +440 -0
  537. package/src/tool/task.ts +194 -0
  538. package/src/tool/task.txt +60 -0
  539. package/src/tool/todo.ts +53 -0
  540. package/src/tool/todoread.txt +14 -0
  541. package/src/tool/todowrite.txt +167 -0
  542. package/src/tool/tool.ts +87 -0
  543. package/src/tool/tree.ts +218 -0
  544. package/src/tool/tree.txt +8 -0
  545. package/src/tool/truncation.ts +106 -0
  546. package/src/tool/use-connector.ts +47 -0
  547. package/src/tool/voice.ts +188 -0
  548. package/src/tool/webfetch.ts +205 -0
  549. package/src/tool/webfetch.txt +13 -0
  550. package/src/tool/websearch.ts +150 -0
  551. package/src/tool/websearch.txt +14 -0
  552. package/src/tool/write.ts +80 -0
  553. package/src/tool/write.txt +8 -0
  554. package/src/util/archive.ts +16 -0
  555. package/src/util/color.ts +19 -0
  556. package/src/util/context.ts +25 -0
  557. package/src/util/defer.ts +12 -0
  558. package/src/util/error.ts +77 -0
  559. package/src/util/eventloop.ts +20 -0
  560. package/src/util/filesystem.ts +125 -0
  561. package/src/util/flock.ts +329 -0
  562. package/src/util/fn.ts +11 -0
  563. package/src/util/format.ts +20 -0
  564. package/src/util/hash.ts +7 -0
  565. package/src/util/iife.ts +3 -0
  566. package/src/util/keybind.ts +103 -0
  567. package/src/util/lazy.ts +18 -0
  568. package/src/util/locale.ts +81 -0
  569. package/src/util/lock.ts +98 -0
  570. package/src/util/log.ts +180 -0
  571. package/src/util/network.ts +9 -0
  572. package/src/util/process.ts +15 -0
  573. package/src/util/queue.ts +32 -0
  574. package/src/util/record.ts +3 -0
  575. package/src/util/rpc.ts +66 -0
  576. package/src/util/scrap.ts +10 -0
  577. package/src/util/signal.ts +12 -0
  578. package/src/util/timeout.ts +14 -0
  579. package/src/util/token.ts +7 -0
  580. package/src/util/wildcard.ts +56 -0
  581. package/src/workspace/adaptors/index.ts +271 -0
  582. package/src/workspace/adaptors/types.ts +14 -0
  583. package/src/workspace/adaptors/worktree.ts +31 -0
  584. package/src/workspace/config.ts +19 -0
  585. package/src/workspace/index.ts +223 -0
  586. package/src/workspace/session-proxy-middleware.ts +97 -0
  587. package/src/workspace/sse.ts +66 -0
  588. package/src/workspace/workspace-context.ts +23 -0
  589. package/src/workspace/workspace-server/routes.ts +33 -0
  590. package/src/workspace/workspace-server/server.ts +47 -0
  591. package/src/worktree/index.ts +487 -0
  592. package/sst-env.d.ts +10 -0
  593. package/test/benchmark.test.ts +121 -0
  594. package/test/build-optimizations.test.ts +124 -0
  595. package/test/id-benchmark.test.ts +132 -0
  596. package/test/optimizations.test.ts +302 -0
  597. package/test/preload.ts +1 -0
  598. package/test/solidjs-benchmark.test.ts +262 -0
  599. package/test/solidjs-optimizations.test.ts +259 -0
  600. package/test/tui-benchmark.test.ts +230 -0
  601. package/test/wildcard-benchmark.test.ts +180 -0
  602. package/tsconfig.json +26 -0
@@ -0,0 +1,440 @@
1
+ import z from "zod"
2
+ import path from "path"
3
+ import os from "os"
4
+ import { spawn } from "child_process"
5
+ import { unlinkSync } from "fs"
6
+
7
+ import { Tool } from "./tool"
8
+ import { Log } from "@/util/log"
9
+ import { Config } from "@/config/config"
10
+ import { ttsRegistry, type TTSProvider } from "./speak/provider"
11
+ import { ELEVENLABS_VOICES_LIST, elevenLabsProvider } from "./speak/elevenlabs"
12
+ import { OPENROUTER_VOICES_LIST, openRouterProvider } from "./speak/openrouter"
13
+
14
+ const log = Log.create({ service: "tool.speak" })
15
+
16
+ const DEFAULT_VOICE_ID = "YOq2y2Up4RgXP2HyXjE5"
17
+ const DEFAULT_MODEL_ID = "eleven_v3"
18
+ const DEFAULT_OUTPUT_FORMAT = "mp3_44100_128"
19
+ const OPENROUTER_DEFAULT_VOICE_ID = "alloy"
20
+ const OPENROUTER_DEFAULT_MODEL_ID = "openai/gpt-audio-mini"
21
+ const OPENROUTER_DEFAULT_OUTPUT_FORMAT = "mp3"
22
+ const DEFAULT_PROVIDER = "openrouter"
23
+
24
+ // Register built-in providers
25
+ ttsRegistry.register(elevenLabsProvider)
26
+ ttsRegistry.register(openRouterProvider)
27
+
28
+ function defaultVoiceIdForProvider(providerId: string): string {
29
+ if (providerId === "openrouter") return OPENROUTER_DEFAULT_VOICE_ID
30
+ return DEFAULT_VOICE_ID
31
+ }
32
+
33
+ function defaultModelIdForProvider(providerId: string): string {
34
+ if (providerId === "openrouter") return OPENROUTER_DEFAULT_MODEL_ID
35
+ return DEFAULT_MODEL_ID
36
+ }
37
+
38
+ function defaultOutputFormatForProvider(providerId: string): string {
39
+ if (providerId === "openrouter") return OPENROUTER_DEFAULT_OUTPUT_FORMAT
40
+ return DEFAULT_OUTPUT_FORMAT
41
+ }
42
+
43
+ function envVoiceIdForProvider(providerId: string): string | undefined {
44
+ if (providerId === "openrouter") {
45
+ return process.env.NIKCLI_OPENROUTER_VOICE_ID
46
+ }
47
+ return process.env.NIKCLI_ELEVENLABS_VOICE_ID
48
+ }
49
+
50
+ function envModelIdForProvider(providerId: string): string | undefined {
51
+ if (providerId === "openrouter") {
52
+ return process.env.NIKCLI_OPENROUTER_MODEL_ID
53
+ }
54
+ return process.env.NIKCLI_ELEVENLABS_MODEL_ID
55
+ }
56
+
57
+ function envOutputFormatForProvider(providerId: string): string | undefined {
58
+ if (providerId === "openrouter") {
59
+ return process.env.NIKCLI_OPENROUTER_OUTPUT_FORMAT
60
+ }
61
+ return process.env.NIKCLI_ELEVENLABS_OUTPUT_FORMAT
62
+ }
63
+
64
+ // Helper to resolve providers list to try, starting with the primary choice
65
+ async function resolveProvidersToTry(
66
+ providerParam?: string,
67
+ configProvider?: string,
68
+ ): Promise<{ provider: TTSProvider; id: string }[]> {
69
+ const primaryId = providerParam ?? configProvider ?? process.env.NIKCLI_SPEAK_PROVIDER ?? DEFAULT_PROVIDER
70
+
71
+ const primary = ttsRegistry.get(primaryId)
72
+ if (!primary) {
73
+ const available = ttsRegistry
74
+ .list()
75
+ .map((p) => p.id)
76
+ .join(", ")
77
+ throw new Error(`Unknown TTS provider: ${primaryId}. Available providers: ${available}`)
78
+ }
79
+
80
+ // Determine fallback order: primary first, then others
81
+ const others = ttsRegistry.list().filter((p) => p.id !== primaryId)
82
+
83
+ return [{ provider: primary, id: primaryId }, ...others.map((p) => ({ provider: p, id: p.id }))]
84
+ }
85
+
86
+ // Voice validation - currently only validates ElevenLabs voices
87
+ const KNOWN_ELEVENLABS_VOICES = ELEVENLABS_VOICES_LIST.map((voice) => voice.id)
88
+ const KNOWN_OPENROUTER_VOICES = new Set(OPENROUTER_VOICES_LIST.map((voice) => voice.id.toLowerCase()))
89
+
90
+ // ElevenLabs voice IDs are typically 21-character alphanumeric strings
91
+ const VOICE_ID_PATTERN = /^[a-zA-Z0-9_-]{21}$/
92
+
93
+ function validateVoiceId(voiceId: string, providerId: string): { valid: boolean; isKnown: boolean } {
94
+ if (providerId === "openrouter") {
95
+ const normalized = voiceId.toLowerCase()
96
+ const isKnown = KNOWN_OPENROUTER_VOICES.has(normalized)
97
+ return { valid: isKnown, isKnown }
98
+ }
99
+
100
+ // For now, only strictly validate ElevenLabs voices by pattern
101
+ if (providerId !== "elevenlabs") {
102
+ return { valid: true, isKnown: false }
103
+ }
104
+
105
+ const matchesPattern = VOICE_ID_PATTERN.test(voiceId)
106
+ const isKnown = KNOWN_ELEVENLABS_VOICES.includes(voiceId)
107
+ return { valid: matchesPattern, isKnown }
108
+ }
109
+
110
+ function resolveVoiceId(
111
+ inputVoiceId: string | undefined,
112
+ configVoiceId: string | undefined,
113
+ envVoiceId: string | undefined,
114
+ providerId: string,
115
+ ): string {
116
+ const providedVoiceId = inputVoiceId ?? configVoiceId ?? envVoiceId
117
+
118
+ if (!providedVoiceId) {
119
+ return defaultVoiceIdForProvider(providerId)
120
+ }
121
+
122
+ const validation = validateVoiceId(providedVoiceId, providerId)
123
+
124
+ if (!validation.valid) {
125
+ log.warn("invalid voiceId format - using default", {
126
+ provided: providedVoiceId,
127
+ expected: `pattern: ${VOICE_ID_PATTERN.source}`,
128
+ })
129
+ return defaultVoiceIdForProvider(providerId)
130
+ }
131
+
132
+ if (!validation.isKnown) {
133
+ log.warn("voiceId not in known voices list - allowing anyway", {
134
+ voiceId: providedVoiceId,
135
+ providerId,
136
+ hint: "voice may work but is not in the known voices list",
137
+ })
138
+ }
139
+
140
+ return providedVoiceId
141
+ }
142
+
143
+ const DEFAULT_TIMEOUT_MS = 30_000
144
+ const MAX_TIMEOUT_MS = 120_000
145
+ const MAX_TEXT_LENGTH = 800
146
+
147
+ type AudioPlayer = {
148
+ name: "afplay" | "ffplay" | "mpg123"
149
+ command: string
150
+ args: (input: { filePath: string; volume: number }) => string[]
151
+ }
152
+
153
+ function clampNumber(value: number, min: number, max: number): number {
154
+ if (value < min) return min
155
+ if (value > max) return max
156
+ return value
157
+ }
158
+
159
+ function detectPlayer(): AudioPlayer | undefined {
160
+ const afplay = Bun.which("afplay")
161
+ if (process.platform === "darwin" && afplay) {
162
+ return {
163
+ name: "afplay",
164
+ command: afplay,
165
+ args: ({ filePath, volume }) => ["-v", String(clampNumber(volume, 0, 2)), filePath],
166
+ }
167
+ }
168
+
169
+ const ffplay = Bun.which("ffplay")
170
+ if (ffplay) {
171
+ // ffplay expects volume in 0-100.
172
+ return {
173
+ name: "ffplay",
174
+ command: ffplay,
175
+ args: ({ filePath, volume }) => {
176
+ const vol = Math.round(clampNumber(volume, 0, 1) * 100)
177
+ return ["-nodisp", "-autoexit", "-loglevel", "error", "-volume", String(vol), filePath]
178
+ },
179
+ }
180
+ }
181
+
182
+ const mpg123 = Bun.which("mpg123")
183
+ if (mpg123) {
184
+ return {
185
+ name: "mpg123",
186
+ command: mpg123,
187
+ args: ({ filePath }) => ["-q", filePath],
188
+ }
189
+ }
190
+
191
+ return undefined
192
+ }
193
+
194
+ function normalizeText(input: string): { text: string; truncated: boolean } {
195
+ const text = input.trim()
196
+ if (!text) throw new Error("Text is required")
197
+ if (text.length <= MAX_TEXT_LENGTH) return { text, truncated: false }
198
+ return { text: text.slice(0, MAX_TEXT_LENGTH - 3) + "...", truncated: true }
199
+ }
200
+
201
+ function extensionFromContentType(contentType: string) {
202
+ const lower = contentType.toLowerCase()
203
+ if (lower.includes("wav")) return "wav"
204
+ if (lower.includes("mp3")) return "mp3"
205
+ return "bin"
206
+ }
207
+
208
+ function playAudioNonBlocking(player: AudioPlayer, filePath: string, volume: number) {
209
+ const cleanup = () => {
210
+ try {
211
+ unlinkSync(filePath)
212
+ } catch {
213
+ // Ignore cleanup failures.
214
+ }
215
+ }
216
+
217
+ const child = spawn(player.command, player.args({ filePath, volume }), {
218
+ detached: process.platform !== "win32",
219
+ stdio: "ignore",
220
+ })
221
+
222
+ child.unref()
223
+ child.once("exit", cleanup)
224
+ child.once("error", (error) => {
225
+ log.error("audio playback failed", { error: error.message, player: player.name })
226
+ cleanup()
227
+ })
228
+ }
229
+
230
+ const DESCRIPTION = `Convert text to speech and play it on the machine speakers (non-blocking).
231
+
232
+ This tool supports multiple TTS providers (ElevenLabs and OpenRouter).
233
+
234
+ Audio tag examples (ElevenLabs):
235
+ [laughs] [sighs] [excited] [sad] [angry]
236
+ [whispers] [shouts] [dramatically] [calmly]
237
+ [British accent] [strong French accent]
238
+
239
+ Usage guidance:
240
+ - Use in short bursts to notify about important state changes
241
+ - Good for: task completion, errors requiring attention, questions needing user input
242
+ - Keep messages concise (1-2 sentences)
243
+
244
+ Note: audio plays on the device running nikcli.`
245
+
246
+ export const SpeakTool = Tool.define("speak", {
247
+ description: DESCRIPTION,
248
+ parameters: z.object({
249
+ text: z.string().describe("Text to speak. Can include audio tags like [laughs], [whispers], [excited], etc."),
250
+ provider: z.string().optional().describe("TTS provider (e.g., elevenlabs, openrouter)"),
251
+ stability: z
252
+ .number()
253
+ .min(0)
254
+ .max(1)
255
+ .optional()
256
+ .describe("Voice stability (0-1). Lower = more expressive. Default: 0.5"),
257
+ similarityBoost: z.number().min(0).max(1).optional().describe("Voice similarity boost (0-1). Default: 0.75"),
258
+ speed: z.number().min(0.5).max(2).optional().describe("Speech speed multiplier (0.5-2). Default: 1.0"),
259
+ volume: z.number().min(0).max(2).optional().describe("Playback volume (0-2). Default: 1.0"),
260
+ voiceId: z.string().optional().describe("TTS voice ID (provider-dependent default)"),
261
+ modelId: z.string().optional().describe("TTS model ID (provider-dependent default)"),
262
+ outputFormat: z.string().optional().describe("TTS output format (provider-dependent default)"),
263
+ timeoutMs: z
264
+ .number()
265
+ .int()
266
+ .positive()
267
+ .optional()
268
+ .describe(`Request timeout in milliseconds (default: ${DEFAULT_TIMEOUT_MS})`),
269
+ }),
270
+ async execute(params, ctx) {
271
+ const config = await Config.get()
272
+ const speakConfig = config.speak ?? {}
273
+
274
+ const player = detectPlayer()
275
+ if (!player) {
276
+ throw new Error(
277
+ [
278
+ "No supported audio player found.",
279
+ "",
280
+ "Supported players:",
281
+ "- macOS: afplay",
282
+ "- ffmpeg: ffplay",
283
+ "- mpg123: mpg123",
284
+ ].join("\n"),
285
+ )
286
+ }
287
+
288
+ // Resolve providers list
289
+ const providersToTry = await resolveProvidersToTry(params.provider, speakConfig.provider)
290
+ const failureLogs: string[] = []
291
+
292
+ for (const { provider: ttsProvider, id: providerId } of providersToTry) {
293
+ const voiceId = resolveVoiceId(params.voiceId, speakConfig.model, envVoiceIdForProvider(providerId), providerId)
294
+ const modelId =
295
+ params.modelId ??
296
+ speakConfig.modelId ??
297
+ envModelIdForProvider(providerId) ??
298
+ defaultModelIdForProvider(providerId)
299
+ const outputFormat =
300
+ params.outputFormat ??
301
+ speakConfig.outputFormat ??
302
+ envOutputFormatForProvider(providerId) ??
303
+ defaultOutputFormatForProvider(providerId)
304
+
305
+ const stability = params.stability ?? 0.5
306
+ const similarityBoost = params.similarityBoost ?? 0.75
307
+ const speed = params.speed ?? 1.0
308
+ const volume = params.volume ?? 1.0
309
+ const timeoutMs = clampNumber(params.timeoutMs ?? DEFAULT_TIMEOUT_MS, 1000, MAX_TIMEOUT_MS)
310
+
311
+ const normalized = normalizeText(params.text)
312
+
313
+ // Optionally check if provider is valid (auth keys present)
314
+ // This helps quickly skip providers that are unconfigured
315
+ const validation = await ttsProvider.validate().catch((e) => ({ valid: false, error: e.message }))
316
+ if (!validation.valid) {
317
+ log.warn(`Provider ${providerId} validation failed, skipping`, { error: validation.error })
318
+ failureLogs.push(`[${providerId}] Skipped: Configuration invalid or missing API key (${validation.error})`)
319
+ continue
320
+ }
321
+
322
+ try {
323
+ await ctx.ask({
324
+ permission: "speak",
325
+ patterns: [`${providerId}:${voiceId}`],
326
+ always: [`${providerId}*`],
327
+ metadata: {
328
+ provider: providerId,
329
+ voiceId,
330
+ modelId,
331
+ outputFormat,
332
+ player: player.name,
333
+ timeoutMs,
334
+ },
335
+ })
336
+ } catch (askError: any) {
337
+ // If the user rejects the permission, we must throw immediately
338
+ throw askError
339
+ }
340
+
341
+ const controller = new AbortController()
342
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs)
343
+
344
+ try {
345
+ const ttsResponse = await ttsProvider
346
+ .speak(
347
+ {
348
+ text: normalized.text,
349
+ voiceId,
350
+ modelId,
351
+ outputFormat,
352
+ stability,
353
+ similarityBoost,
354
+ speed,
355
+ },
356
+ { signal: AbortSignal.any([controller.signal, ctx.abort]) },
357
+ )
358
+ .finally(() => clearTimeout(timeoutId))
359
+
360
+ const ext = extensionFromContentType(ttsResponse.contentType)
361
+ const tempFile = path.join(
362
+ os.tmpdir(),
363
+ `nikcli-speak-${Date.now()}-${Math.random().toString(16).slice(2)}.${ext}`,
364
+ )
365
+
366
+ await Bun.write(tempFile, ttsResponse.audio)
367
+ playAudioNonBlocking(player, tempFile, volume)
368
+
369
+ const preview = normalized.text.length > 80 ? normalized.text.slice(0, 80) + "..." : normalized.text
370
+ const truncated = normalized.truncated ? " (text truncated)" : ""
371
+
372
+ const outputText = [
373
+ `Playing speech (non-blocking): "${preview}"${truncated}`,
374
+ `Provider: ${ttsProvider.name} (${modelId})`,
375
+ `Voice: ${voiceId}`,
376
+ `Player: ${player.name}`,
377
+ ]
378
+
379
+ if (failureLogs.length > 0) {
380
+ outputText.push(`\nFallback sequence:`)
381
+ outputText.push(...failureLogs)
382
+ outputText.push(`[${providerId}] Success!`)
383
+ }
384
+
385
+ return {
386
+ title: "Speak",
387
+ output: outputText.join("\n"),
388
+ metadata: {
389
+ provider: providerId,
390
+ voiceId,
391
+ modelId,
392
+ outputFormat,
393
+ player: player.name,
394
+ textTruncated: normalized.truncated,
395
+ fallbacks: failureLogs.length,
396
+ },
397
+ }
398
+ } catch (fetchError: any) {
399
+ const errorMessage = fetchError.cause?.message ?? fetchError.message
400
+
401
+ if (fetchError.name === "AbortError") {
402
+ if (ctx.abort.aborted) {
403
+ log.warn("request cancelled by user", { timeoutMs })
404
+ throw new Error("Speech request was cancelled")
405
+ }
406
+ log.error("request timed out", { timeoutMs, voiceId, modelId, provider: providerId })
407
+ failureLogs.push(`[${providerId}] Failed: Request timed out after ${timeoutMs}ms`)
408
+ continue
409
+ }
410
+
411
+ log.error(`network error calling ${ttsProvider.name} API`, {
412
+ error: errorMessage,
413
+ voiceId,
414
+ modelId,
415
+ provider: providerId,
416
+ })
417
+
418
+ if (errorMessage.includes("ENOTFOUND") || errorMessage.includes("dns")) {
419
+ failureLogs.push(`[${providerId}] Failed: DNS/Network error. Check internet connection.`)
420
+ } else if (errorMessage.includes("ECONNREFUSED")) {
421
+ failureLogs.push(`[${providerId}] Failed: Connection refused. Service may be unavailable.`)
422
+ } else {
423
+ failureLogs.push(`[${providerId}] Failed: ${errorMessage}`)
424
+ }
425
+
426
+ // Loop continues to next provider
427
+ }
428
+ }
429
+
430
+ // If we reach here, all providers failed
431
+ if (failureLogs.length > 0) {
432
+ throw new Error(`All TTS providers failed to speak:\n${failureLogs.join("\n")}`)
433
+ } else {
434
+ throw new Error("No available TTS providers could be used.")
435
+ }
436
+ },
437
+ })
438
+
439
+ // Export the provider registry for external use (e.g., CLI commands)
440
+ export { ttsRegistry }
@@ -0,0 +1,194 @@
1
+ import { Tool } from "./tool"
2
+ import DESCRIPTION from "./task.txt"
3
+ import z from "zod"
4
+ import { Session } from "../session"
5
+ import { Bus } from "../bus"
6
+ import { MessageV2 } from "../session/message-v2"
7
+ import { Identifier } from "../id/id"
8
+ import { Agent } from "../agent/agent"
9
+ import { SessionPrompt } from "../session/prompt"
10
+ import { iife } from "@/util/iife"
11
+ import { defer } from "@/util/defer"
12
+ import { Config } from "../config/config"
13
+ import { PermissionNext } from "@/permission/next"
14
+
15
+ const parameters = z.object({
16
+ description: z.string().describe("A short (3-5 words) description of the task"),
17
+ prompt: z.string().describe("The task for the agent to perform"),
18
+ subagent_type: z.string().describe("The type of specialized agent to use for this task"),
19
+ session_id: z.string().describe("Existing Task session to continue").optional(),
20
+ command: z.string().describe("The command that triggered this task").optional(),
21
+ })
22
+
23
+ export type TaskParams = z.infer<typeof parameters>
24
+
25
+ export async function runSubtask(params: TaskParams, ctx: Tool.Context) {
26
+ const config = await Config.get()
27
+ const bypass = Boolean(ctx.extra?.bypassAgentCheck)
28
+
29
+ if (!bypass) {
30
+ await ctx.ask({
31
+ permission: "task",
32
+ patterns: [params.subagent_type],
33
+ always: ["*"],
34
+ metadata: {
35
+ description: params.description,
36
+ subagent_type: params.subagent_type,
37
+ },
38
+ })
39
+ }
40
+
41
+ const agent = await Agent.get(params.subagent_type)
42
+ if (!agent) throw new Error(`Unknown agent type: ${params.subagent_type} is not a valid agent type`)
43
+
44
+ const hasTaskPermission = agent.permission.some((rule) => rule.permission === "task")
45
+
46
+ const session = await iife(async () => {
47
+ if (params.session_id) {
48
+ const found = await Session.get(params.session_id).catch(() => {})
49
+ if (found) return found
50
+ }
51
+
52
+ return await Session.create({
53
+ parentID: ctx.sessionID,
54
+ title: params.description + ` (@${agent.name} subagent)`,
55
+ permission: [
56
+ {
57
+ permission: "todowrite",
58
+ pattern: "*",
59
+ action: "deny",
60
+ },
61
+ {
62
+ permission: "todoread",
63
+ pattern: "*",
64
+ action: "deny",
65
+ },
66
+ ...(hasTaskPermission
67
+ ? []
68
+ : [
69
+ {
70
+ permission: "task" as const,
71
+ pattern: "*" as const,
72
+ action: "deny" as const,
73
+ },
74
+ ]),
75
+ ...(config.experimental?.primary_tools?.map((t) => ({
76
+ pattern: "*",
77
+ action: "allow" as const,
78
+ permission: t,
79
+ })) ?? []),
80
+ ],
81
+ })
82
+ })
83
+
84
+ const msg = await MessageV2.get({ sessionID: ctx.sessionID, messageID: ctx.messageID })
85
+ if (msg.info.role !== "assistant") throw new Error("Not an assistant message")
86
+
87
+ ctx.metadata({
88
+ title: params.description,
89
+ metadata: {
90
+ sessionId: session.id,
91
+ },
92
+ })
93
+
94
+ const messageID = Identifier.ascending("message")
95
+ const parts: Record<string, { id: string; tool: string; state: { status: string; title?: string } }> = {}
96
+ const unsub = Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
97
+ if (evt.properties.part.sessionID !== session.id) return
98
+ if (evt.properties.part.messageID === messageID) return
99
+ if (evt.properties.part.type !== "tool") return
100
+ const part = evt.properties.part
101
+ parts[part.id] = {
102
+ id: part.id,
103
+ tool: part.tool,
104
+ state: {
105
+ status: part.state.status,
106
+ title: part.state.status === "completed" ? part.state.title : undefined,
107
+ },
108
+ }
109
+ ctx.metadata({
110
+ title: params.description,
111
+ metadata: {
112
+ summary: Object.values(parts).sort((a, b) => a.id.localeCompare(b.id)),
113
+ sessionId: session.id,
114
+ },
115
+ })
116
+ })
117
+
118
+ const model = agent.model ?? {
119
+ modelID: msg.info.modelID,
120
+ providerID: msg.info.providerID,
121
+ }
122
+
123
+ function cancel() {
124
+ SessionPrompt.cancel(session.id)
125
+ }
126
+ ctx.abort.addEventListener("abort", cancel)
127
+ using _ = defer(() => ctx.abort.removeEventListener("abort", cancel))
128
+ const promptParts = await SessionPrompt.resolvePromptParts(params.prompt)
129
+
130
+ const result = await SessionPrompt.prompt({
131
+ messageID,
132
+ sessionID: session.id,
133
+ model: {
134
+ modelID: model.modelID,
135
+ providerID: model.providerID,
136
+ },
137
+ agent: agent.name,
138
+ tools: {
139
+ todowrite: false,
140
+ todoread: false,
141
+ ...(hasTaskPermission ? {} : { task: false }),
142
+ ...Object.fromEntries((config.experimental?.primary_tools ?? []).map((t) => [t, false])),
143
+ },
144
+ parts: promptParts,
145
+ })
146
+ unsub()
147
+ const messages = await Session.messages({ sessionID: session.id })
148
+ const summary = messages
149
+ .filter((x) => x.info.role === "assistant")
150
+ .flatMap((msg) => msg.parts.filter((x: any) => x.type === "tool") as MessageV2.ToolPart[])
151
+ .map((part) => ({
152
+ id: part.id,
153
+ tool: part.tool,
154
+ state: {
155
+ status: part.state.status,
156
+ title: part.state.status === "completed" ? part.state.title : undefined,
157
+ },
158
+ }))
159
+ const text = result.parts.findLast((x) => x.type === "text")?.text ?? ""
160
+
161
+ const output = text + "\n\n" + ["<task_metadata>", `session_id: ${session.id}`, "</task_metadata>"].join("\n")
162
+
163
+ return {
164
+ title: params.description,
165
+ metadata: {
166
+ summary,
167
+ sessionId: session.id,
168
+ },
169
+ output,
170
+ }
171
+ }
172
+
173
+ export const TaskTool = Tool.define("task", async (ctx) => {
174
+ const agents = await Agent.list().then((x) => x.filter((a) => a.mode !== "primary"))
175
+
176
+ const caller = ctx?.agent
177
+ const accessibleAgents = caller
178
+ ? agents.filter((a) => PermissionNext.evaluate("task", a.name, caller.permission).action !== "deny")
179
+ : agents
180
+
181
+ const description = DESCRIPTION.replace(
182
+ "{agents}",
183
+ accessibleAgents
184
+ .map((a) => `- ${a.name}: ${a.description ?? "This subagent should only be called manually by the user."}`)
185
+ .join("\n"),
186
+ )
187
+ return {
188
+ description,
189
+ parameters,
190
+ async execute(params: z.infer<typeof parameters>, ctx) {
191
+ return runSubtask(params, ctx)
192
+ },
193
+ }
194
+ })
@@ -0,0 +1,60 @@
1
+ Launch a new agent to handle complex, multistep tasks autonomously.
2
+
3
+ Available agent types and the tools they have access to:
4
+ {agents}
5
+
6
+ When using the Task tool, you must specify a subagent_type parameter to select which agent type to use.
7
+
8
+ When to use the Task tool:
9
+ - When you are instructed to execute custom slash commands. Use the Task tool with the slash command invocation as the entire prompt. The slash command can take arguments. For example: Task(description="Check the file", prompt="/check-file path/to/file.py")
10
+
11
+ When NOT to use the Task tool:
12
+ - If you want to read a specific file path, use the Read or Glob tool instead of the Task tool, to find the match more quickly
13
+ - If you are searching for a specific class definition like "class Foo", use the Glob tool instead, to find the match more quickly
14
+ - If you are searching for code within a specific file or set of 2-3 files, use the Read tool instead of the Task tool, to find the match more quickly
15
+ - Other tasks that are not related to the agent descriptions above
16
+
17
+
18
+ Usage notes:
19
+ 1. Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
20
+ 2. When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.
21
+ 3. Each agent invocation is stateless unless you provide a session_id. Your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.
22
+ 4. The agent's outputs should generally be trusted
23
+ 5. Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent
24
+ 6. If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.
25
+
26
+ Example usage (NOTE: The agents below are fictional examples for illustration only - use the actual agents listed above):
27
+
28
+ <example_agent_descriptions>
29
+ "code-reviewer": use this agent after you are done writing a significant piece of code
30
+ "greeting-responder": use this agent when to respond to user greetings with a friendly joke
31
+ </example_agent_description>
32
+
33
+ <example>
34
+ user: "Please write a function that checks if a number is prime"
35
+ assistant: Sure let me write a function that checks if a number is prime
36
+ assistant: First let me use the Write tool to write a function that checks if a number is prime
37
+ assistant: I'm going to use the Write tool to write the following code:
38
+ <code>
39
+ function isPrime(n) {
40
+ if (n <= 1) return false
41
+ for (let i = 2; i * i <= n; i++) {
42
+ if (n % i === 0) return false
43
+ }
44
+ return true
45
+ }
46
+ </code>
47
+ <commentary>
48
+ Since a significant piece of code was written and the task was completed, now use the code-reviewer agent to review the code
49
+ </commentary>
50
+ assistant: Now let me use the code-reviewer agent to review the code
51
+ assistant: Uses the Task tool to launch the code-reviewer agent
52
+ </example>
53
+
54
+ <example>
55
+ user: "Hello"
56
+ <commentary>
57
+ Since the user is greeting, use the greeting-responder agent to respond with a friendly joke
58
+ </commentary>
59
+ assistant: "I'm going to use the Task tool to launch the with the greeting-responder agent"
60
+ </example>