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,188 @@
1
+ import path from "path"
2
+ import { fileURLToPath, pathToFileURL } from "url"
3
+ import semver from "semver"
4
+ import { BunProc } from "@/bun"
5
+ import { Filesystem } from "@/util/filesystem"
6
+ import { isRecord } from "@/util/record"
7
+
8
+ // Old npm package names for plugins that are now built-in
9
+ export const DEPRECATED_PLUGIN_PACKAGES = ["nikcli-openai-codex-auth", "nikcli-copilot-auth"]
10
+
11
+ export function isDeprecatedPlugin(spec: string) {
12
+ return DEPRECATED_PLUGIN_PACKAGES.some((pkg) => spec.includes(pkg))
13
+ }
14
+
15
+ export function parsePluginSpecifier(spec: string) {
16
+ const lastAt = spec.lastIndexOf("@")
17
+ const pkg = lastAt > 0 ? spec.substring(0, lastAt) : spec
18
+ const version = lastAt > 0 ? spec.substring(lastAt + 1) : "latest"
19
+ return { pkg, version }
20
+ }
21
+
22
+ export type PluginSource = "file" | "npm"
23
+ export type PluginKind = "server" | "tui"
24
+ type PluginMode = "strict" | "detect"
25
+
26
+ export function pluginSource(spec: string): PluginSource {
27
+ return spec.startsWith("file://") ? "file" : "npm"
28
+ }
29
+
30
+ function hasEntrypoint(json: Record<string, unknown>, kind: PluginKind) {
31
+ if (!isRecord(json.exports)) return false
32
+ return `./${kind}` in json.exports
33
+ }
34
+
35
+ function resolveExportPath(raw: string, dir: string) {
36
+ if (raw.startsWith("./") || raw.startsWith("../")) return path.resolve(dir, raw)
37
+ if (raw.startsWith("file://")) return fileURLToPath(raw)
38
+ return raw
39
+ }
40
+
41
+ function extractExportValue(value: unknown): string | undefined {
42
+ if (typeof value === "string") return value
43
+ if (!isRecord(value)) return undefined
44
+ for (const key of ["import", "default"]) {
45
+ const nested = value[key]
46
+ if (typeof nested === "string") return nested
47
+ }
48
+ return undefined
49
+ }
50
+
51
+ export async function resolvePluginEntrypoint(spec: string, target: string, kind: PluginKind) {
52
+ const pkg = await readPluginPackage(target).catch(() => undefined)
53
+ if (!pkg) return target
54
+ if (!hasEntrypoint(pkg.json, kind)) return target
55
+
56
+ const exports = pkg.json.exports
57
+ if (!isRecord(exports)) return target
58
+ const raw = extractExportValue(exports[`./${kind}`])
59
+ if (!raw) return target
60
+
61
+ const resolved = resolveExportPath(raw, pkg.dir)
62
+ const root = Filesystem.resolve(pkg.dir)
63
+ const next = Filesystem.resolve(resolved)
64
+ if (!Filesystem.contains(root, next)) {
65
+ throw new Error(`Plugin ${spec} resolved ${kind} entry outside plugin directory`)
66
+ }
67
+
68
+ return pathToFileURL(next).href
69
+ }
70
+
71
+ export function isPathPluginSpec(spec: string) {
72
+ return spec.startsWith("file://") || spec.startsWith(".") || path.isAbsolute(spec) || /^[A-Za-z]:[\\/]/.test(spec)
73
+ }
74
+
75
+ export async function resolvePathPluginTarget(spec: string) {
76
+ const raw = spec.startsWith("file://") ? fileURLToPath(spec) : spec
77
+ const file = path.isAbsolute(raw) || /^[A-Za-z]:[\\/]/.test(raw) ? raw : path.resolve(raw)
78
+ const stat = await Filesystem.stat(file)
79
+ if (!stat?.isDirectory()) {
80
+ if (spec.startsWith("file://")) return spec
81
+ return pathToFileURL(file).href
82
+ }
83
+
84
+ const pkg = await Filesystem.readJson<Record<string, unknown>>(path.join(file, "package.json")).catch(() => undefined)
85
+ if (!pkg) throw new Error(`Plugin directory ${file} is missing package.json`)
86
+ if (typeof pkg.main !== "string" || !pkg.main.trim()) {
87
+ throw new Error(`Plugin directory ${file} must define package.json main`)
88
+ }
89
+ return pathToFileURL(path.resolve(file, pkg.main)).href
90
+ }
91
+
92
+ export async function checkPluginCompatibility(target: string, opencodeVersion: string) {
93
+ if (!semver.valid(opencodeVersion) || semver.major(opencodeVersion) === 0) return
94
+ const pkg = await readPluginPackage(target).catch(() => undefined)
95
+ if (!pkg) return
96
+ const engines = pkg.json.engines
97
+ if (!isRecord(engines)) return
98
+ const range = engines.opencode
99
+ if (typeof range !== "string") return
100
+ if (!semver.satisfies(opencodeVersion, range)) {
101
+ throw new Error(`Plugin requires opencode ${range} but running ${opencodeVersion}`)
102
+ }
103
+ }
104
+
105
+ export async function resolvePluginTarget(spec: string, parsed = parsePluginSpecifier(spec)) {
106
+ if (isPathPluginSpec(spec)) return resolvePathPluginTarget(spec)
107
+ return BunProc.install(parsed.pkg, parsed.version)
108
+ }
109
+
110
+ export async function readPluginPackage(target: string) {
111
+ const file = target.startsWith("file://") ? fileURLToPath(target) : target
112
+ const stat = await Filesystem.stat(file)
113
+ const dir = stat?.isDirectory() ? file : path.dirname(file)
114
+ const pkg = path.join(dir, "package.json")
115
+ const json = await Filesystem.readJson<Record<string, unknown>>(pkg)
116
+ return { dir, pkg, json }
117
+ }
118
+
119
+ export function readPluginId(id: unknown, spec: string) {
120
+ if (id === undefined) return
121
+ if (typeof id !== "string") throw new TypeError(`Plugin ${spec} has invalid id type ${typeof id}`)
122
+ const value = id.trim()
123
+ if (!value) throw new TypeError(`Plugin ${spec} has an empty id`)
124
+ return value
125
+ }
126
+
127
+ export async function getPluginIdFromPackage(target: string, spec: string): Promise<string | undefined> {
128
+ try {
129
+ const pkg = await readPluginPackage(target)
130
+ if (typeof pkg.json.name !== "string" || !pkg.json.name.trim()) {
131
+ return undefined
132
+ }
133
+ return pkg.json.name.trim()
134
+ } catch {
135
+ return undefined
136
+ }
137
+ }
138
+
139
+ export function readV1Plugin(
140
+ mod: Record<string, unknown>,
141
+ spec: string,
142
+ kind: PluginKind,
143
+ mode: PluginMode = "strict",
144
+ ) {
145
+ const value = mod.default
146
+ if (!isRecord(value)) {
147
+ if (mode === "detect") return
148
+ throw new TypeError(`Plugin ${spec} must default export an object with ${kind}()`)
149
+ }
150
+ if (mode === "detect" && !("id" in value) && !("server" in value) && !("tui" in value)) return
151
+
152
+ const server = "server" in value ? value.server : undefined
153
+ const tui = "tui" in value ? value.tui : undefined
154
+ if (server !== undefined && typeof server !== "function") {
155
+ if (mode === "detect") return
156
+ throw new TypeError(`Plugin ${spec} has invalid server export`)
157
+ }
158
+ if (tui !== undefined && typeof tui !== "function") {
159
+ if (mode === "detect") return
160
+ throw new TypeError(`Plugin ${spec} has invalid tui export`)
161
+ }
162
+ if (server !== undefined && tui !== undefined) {
163
+ if (mode === "detect") return
164
+ throw new TypeError(`Plugin ${spec} must default export either server() or tui(), not both`)
165
+ }
166
+ if (kind === "server" && server === undefined) {
167
+ if (mode === "detect") return
168
+ throw new TypeError(`Plugin ${spec} must default export an object with server()`)
169
+ }
170
+ if (kind === "tui" && tui === undefined) {
171
+ if (mode === "detect") return undefined
172
+ }
173
+
174
+ return value
175
+ }
176
+
177
+ export async function resolvePluginId(source: PluginSource, spec: string, target: string, id: string | undefined) {
178
+ if (source === "file") {
179
+ if (id) return id
180
+ throw new TypeError(`Path plugin ${spec} must export id`)
181
+ }
182
+ if (id) return id
183
+ const pkg = await readPluginPackage(target)
184
+ if (typeof pkg.json.name !== "string" || !pkg.json.name.trim()) {
185
+ throw new TypeError(`Plugin package ${pkg.pkg} is missing name`)
186
+ }
187
+ return pkg.json.name.trim()
188
+ }
@@ -0,0 +1,35 @@
1
+ import { Plugin } from "../plugin"
2
+ import { Format } from "../format"
3
+ import { LSP } from "../lsp"
4
+ import { FileWatcher } from "../file/watcher"
5
+ import { File } from "../file"
6
+ import { Project } from "./project"
7
+ import { Bus } from "../bus"
8
+ import { Command } from "../command"
9
+ import { Instance } from "./instance"
10
+ import { Vcs } from "./vcs"
11
+ import { Log } from "@/util/log"
12
+ import { ShareNext } from "@/share/share-next"
13
+ import { Snapshot } from "../snapshot"
14
+ import { Truncate } from "../tool/truncation"
15
+ import { Todo } from "../session/todo"
16
+
17
+ export async function InstanceBootstrap() {
18
+ Log.Default.info("bootstrapping", { directory: Instance.directory })
19
+ await Plugin.init()
20
+ ShareNext.init()
21
+ Format.init()
22
+ await LSP.init()
23
+ FileWatcher.init()
24
+ File.init()
25
+ Vcs.init()
26
+ Snapshot.init()
27
+ Truncate.init()
28
+ Todo.init()
29
+
30
+ Bus.subscribe(Command.Event.Executed, async (payload) => {
31
+ if (payload.properties.name === Command.Default.INIT) {
32
+ await Project.setInitialized(Instance.project.id)
33
+ }
34
+ })
35
+ }
@@ -0,0 +1,84 @@
1
+ import { Log } from "@/util/log"
2
+ import { Context } from "../util/context"
3
+ import { Project } from "./project"
4
+ import { State } from "./state"
5
+ import { iife } from "@/util/iife"
6
+ import { GlobalBus } from "@/bus/global"
7
+ import { Filesystem } from "@/util/filesystem"
8
+
9
+ interface Context {
10
+ directory: string
11
+ worktree: string
12
+ project: Project.Info
13
+ }
14
+ const context = Context.create<Context>("instance")
15
+ const cache = new Map<string, Promise<Context>>()
16
+
17
+ export const Instance = {
18
+ async provide<R>(input: { directory: string; init?: () => Promise<any>; fn: () => R }): Promise<R> {
19
+ let existing = cache.get(input.directory)
20
+ if (!existing) {
21
+ Log.Default.info("creating instance", { directory: input.directory })
22
+ existing = iife(async () => {
23
+ const { project, sandbox } = await Project.fromDirectory(input.directory)
24
+ const ctx = {
25
+ directory: input.directory,
26
+ worktree: sandbox,
27
+ project,
28
+ }
29
+ await context.provide(ctx, async () => {
30
+ await input.init?.()
31
+ })
32
+ return ctx
33
+ })
34
+ cache.set(input.directory, existing)
35
+ }
36
+ const ctx = await existing
37
+ return context.provide(ctx, async () => {
38
+ return input.fn()
39
+ })
40
+ },
41
+ get directory() {
42
+ return context.use().directory
43
+ },
44
+ get worktree() {
45
+ return context.use().worktree
46
+ },
47
+ get project() {
48
+ return context.use().project
49
+ },
50
+ containsPath(filepath: string) {
51
+ if (Filesystem.contains(Instance.directory, filepath)) return true
52
+ if (Instance.worktree === "/") return false
53
+ return Filesystem.contains(Instance.worktree, filepath)
54
+ },
55
+ state<S>(init: () => S, dispose?: (state: Awaited<S>) => Promise<void>): () => S {
56
+ return State.create(() => Instance.directory, init, dispose)
57
+ },
58
+ async dispose() {
59
+ Log.Default.info("disposing instance", { directory: Instance.directory })
60
+ await State.dispose(Instance.directory)
61
+ cache.delete(Instance.directory)
62
+ GlobalBus.emit("event", {
63
+ directory: Instance.directory,
64
+ payload: {
65
+ type: "server.instance.disposed",
66
+ properties: {
67
+ directory: Instance.directory,
68
+ },
69
+ },
70
+ })
71
+ },
72
+ async disposeAll() {
73
+ Log.Default.info("disposing all instances")
74
+ for (const [_key, value] of cache) {
75
+ const awaited = await value.catch(() => {})
76
+ if (awaited) {
77
+ await context.provide(await value, async () => {
78
+ await Instance.dispose()
79
+ })
80
+ }
81
+ }
82
+ cache.clear()
83
+ },
84
+ }
@@ -0,0 +1,373 @@
1
+ import z from "zod"
2
+ import fs from "fs/promises"
3
+ import { Filesystem } from "../util/filesystem"
4
+ import path from "path"
5
+ import { $ } from "bun"
6
+ import { Storage } from "../storage/storage"
7
+ import { Log } from "../util/log"
8
+ import { Flag } from "@/flag/flag"
9
+ import { Session } from "../session"
10
+ import { work } from "../util/queue"
11
+ import { fn } from "@nikcli-ai/util/fn"
12
+ import { BusEvent } from "@/bus/bus-event"
13
+ import { iife } from "@/util/iife"
14
+ import { GlobalBus } from "@/bus/global"
15
+ import { existsSync } from "fs"
16
+
17
+ export namespace Project {
18
+ const log = Log.create({ service: "project" })
19
+
20
+ async function readCachedID(gitDir?: string) {
21
+ if (!gitDir) return undefined
22
+
23
+ const stat = await fs.stat(gitDir).catch(() => undefined)
24
+ if (!stat?.isDirectory()) return undefined
25
+
26
+ return Bun.file(path.join(gitDir, "nikcli"))
27
+ .text()
28
+ .then((x) => x.trim())
29
+ .catch(() => undefined)
30
+ }
31
+
32
+ async function writeCachedID(gitDir: string | undefined, id: string) {
33
+ if (!gitDir) return
34
+
35
+ const stat = await fs.stat(gitDir).catch(() => undefined)
36
+ if (!stat?.isDirectory()) return
37
+
38
+ await Bun.file(path.join(gitDir, "nikcli"))
39
+ .write(id)
40
+ .catch(() => undefined)
41
+ }
42
+
43
+ function cacheDirToWorktree(gitDir: string, sandbox: string) {
44
+ const dirname = path.dirname(path.relative(sandbox, gitDir) || ".")
45
+ if (dirname === ".") return sandbox
46
+ return path.resolve(sandbox, dirname)
47
+ }
48
+
49
+ export const Info = z
50
+ .object({
51
+ id: z.string(),
52
+ worktree: z.string(),
53
+ vcs: z.literal("git").optional(),
54
+ name: z.string().optional(),
55
+ icon: z
56
+ .object({
57
+ url: z.string().optional(),
58
+ override: z.string().optional(),
59
+ color: z.string().optional(),
60
+ })
61
+ .optional(),
62
+ time: z.object({
63
+ created: z.number(),
64
+ updated: z.number(),
65
+ initialized: z.number().optional(),
66
+ }),
67
+ sandboxes: z.array(z.string()),
68
+ })
69
+ .meta({
70
+ ref: "Project",
71
+ })
72
+ export type Info = z.infer<typeof Info>
73
+
74
+ export const Event = {
75
+ Updated: BusEvent.define("project.updated", Info),
76
+ }
77
+
78
+ export async function fromDirectory(directory: string) {
79
+ log.info("fromDirectory", { directory })
80
+
81
+ const { id, sandbox, worktree, vcs } = await iife(async () => {
82
+ const matches = Filesystem.up({ targets: [".git"], start: directory })
83
+ const git = await matches.next().then((x) => x.value)
84
+ await matches.return()
85
+ if (git) {
86
+ let sandbox = path.dirname(git)
87
+
88
+ const gitBinary = Bun.which("git")
89
+
90
+ let commonGitDir: string | undefined
91
+ let id = await readCachedID(git)
92
+
93
+ if (gitBinary) {
94
+ commonGitDir = await $`git rev-parse --git-common-dir`
95
+ .quiet()
96
+ .nothrow()
97
+ .cwd(sandbox)
98
+ .text()
99
+ .then((x) => path.resolve(sandbox, x.trim()))
100
+ .catch(() => undefined)
101
+
102
+ if (!id && commonGitDir && commonGitDir !== git) {
103
+ id = await readCachedID(commonGitDir)
104
+ }
105
+ }
106
+
107
+ if (!gitBinary) {
108
+ return {
109
+ id: id ?? "global",
110
+ worktree: sandbox,
111
+ sandbox: sandbox,
112
+ vcs: Info.shape.vcs.parse(Flag.NIKCLI_FAKE_VCS),
113
+ }
114
+ }
115
+
116
+ if (!id) {
117
+ const roots = await $`git rev-list --max-parents=0 --all`
118
+ .quiet()
119
+ .nothrow()
120
+ .cwd(sandbox)
121
+ .text()
122
+ .then((x) =>
123
+ x
124
+ .split("\n")
125
+ .filter(Boolean)
126
+ .map((x) => x.trim())
127
+ .toSorted(),
128
+ )
129
+ .catch(() => undefined)
130
+
131
+ if (!roots) {
132
+ return {
133
+ id: "global",
134
+ worktree: sandbox,
135
+ sandbox: sandbox,
136
+ vcs: Info.shape.vcs.parse(Flag.NIKCLI_FAKE_VCS),
137
+ }
138
+ }
139
+
140
+ id = roots[0]
141
+ if (id) {
142
+ const derivedID = id
143
+ const cacheDir = commonGitDir ?? git
144
+ void readCachedID(cacheDir)
145
+ .then((cached) => {
146
+ if (cached || !cacheDir) return
147
+ return writeCachedID(cacheDir, derivedID)
148
+ })
149
+ .catch(() => undefined)
150
+ }
151
+ }
152
+
153
+ if (!id) {
154
+ return {
155
+ id: "global",
156
+ worktree: sandbox,
157
+ sandbox: sandbox,
158
+ vcs: "git",
159
+ }
160
+ }
161
+
162
+ const top = await $`git rev-parse --show-toplevel`
163
+ .quiet()
164
+ .nothrow()
165
+ .cwd(sandbox)
166
+ .text()
167
+ .then((x) => path.resolve(sandbox, x.trim()))
168
+ .catch(() => undefined)
169
+
170
+ if (!top) {
171
+ return {
172
+ id,
173
+ sandbox,
174
+ worktree: sandbox,
175
+ vcs: Info.shape.vcs.parse(Flag.NIKCLI_FAKE_VCS),
176
+ }
177
+ }
178
+
179
+ sandbox = top
180
+
181
+ const worktree = commonGitDir ? cacheDirToWorktree(commonGitDir, sandbox) : undefined
182
+
183
+ if (!worktree) {
184
+ return {
185
+ id,
186
+ sandbox,
187
+ worktree: sandbox,
188
+ vcs: Info.shape.vcs.parse(Flag.NIKCLI_FAKE_VCS),
189
+ }
190
+ }
191
+
192
+ return {
193
+ id,
194
+ sandbox,
195
+ worktree,
196
+ vcs: "git",
197
+ }
198
+ }
199
+
200
+ return {
201
+ id: "global",
202
+ worktree: "/",
203
+ sandbox: "/",
204
+ vcs: Info.shape.vcs.parse(Flag.NIKCLI_FAKE_VCS),
205
+ }
206
+ })
207
+
208
+ let existing = await Storage.read<Info>(["project", id]).catch(() => undefined)
209
+ if (!existing) {
210
+ existing = {
211
+ id,
212
+ worktree,
213
+ vcs: vcs as Info["vcs"],
214
+ sandboxes: [],
215
+ time: {
216
+ created: Date.now(),
217
+ updated: Date.now(),
218
+ },
219
+ }
220
+ if (id !== "global") {
221
+ await migrateFromGlobal(id, worktree)
222
+ }
223
+ }
224
+
225
+ if (!existing.sandboxes) existing.sandboxes = []
226
+
227
+ if (Flag.NIKCLI_EXPERIMENTAL_ICON_DISCOVERY) discover(existing)
228
+
229
+ const result: Info = {
230
+ ...existing,
231
+ worktree,
232
+ vcs: vcs as Info["vcs"],
233
+ time: {
234
+ ...existing.time,
235
+ updated: Date.now(),
236
+ },
237
+ }
238
+ if (sandbox !== result.worktree && !result.sandboxes.includes(sandbox)) result.sandboxes.push(sandbox)
239
+ result.sandboxes = result.sandboxes.filter((x) => existsSync(x))
240
+ await Storage.write<Info>(["project", id], result)
241
+ GlobalBus.emit("event", {
242
+ payload: {
243
+ type: Event.Updated.type,
244
+ properties: result,
245
+ },
246
+ })
247
+ return { project: result, sandbox }
248
+ }
249
+
250
+ export async function discover(input: Info) {
251
+ if (input.vcs !== "git") return
252
+ if (input.icon?.override) return
253
+ if (input.icon?.url) return
254
+ const glob = new Bun.Glob("**/{favicon}.{ico,png,svg,jpg,jpeg,webp}")
255
+ const matches = await Array.fromAsync(
256
+ glob.scan({
257
+ cwd: input.worktree,
258
+ absolute: true,
259
+ onlyFiles: true,
260
+ followSymlinks: false,
261
+ dot: false,
262
+ }),
263
+ )
264
+ const shortest = matches.sort((a, b) => a.length - b.length)[0]
265
+ if (!shortest) return
266
+ const file = Bun.file(shortest)
267
+ const buffer = await file.arrayBuffer()
268
+ const base64 = Buffer.from(buffer).toString("base64")
269
+ const mime = file.type || "image/png"
270
+ const url = `data:${mime};base64,${base64}`
271
+ await update({
272
+ projectID: input.id,
273
+ icon: {
274
+ url,
275
+ },
276
+ })
277
+ return
278
+ }
279
+
280
+ async function migrateFromGlobal(newProjectID: string, worktree: string) {
281
+ const globalProject = await Storage.read<Info>(["project", "global"]).catch(() => undefined)
282
+ if (!globalProject) return
283
+
284
+ const globalSessions = await Storage.list(["session", "global"]).catch(() => [])
285
+ if (globalSessions.length === 0) return
286
+
287
+ log.info("migrating sessions from global", { newProjectID, worktree, count: globalSessions.length })
288
+
289
+ await work(10, globalSessions, async (key) => {
290
+ const sessionID = key[key.length - 1]
291
+ const session = await Storage.read<Session.Info>(key).catch(() => undefined)
292
+ if (!session) return
293
+ if (session.directory && session.directory !== worktree) return
294
+
295
+ session.projectID = newProjectID
296
+ log.info("migrating session", { sessionID, from: "global", to: newProjectID })
297
+ await Storage.write(["session", newProjectID, sessionID], session)
298
+ await Storage.remove(key)
299
+ }).catch((error) => {
300
+ log.error("failed to migrate sessions from global to project", { error, projectId: newProjectID })
301
+ })
302
+ }
303
+
304
+ export async function setInitialized(projectID: string) {
305
+ await Storage.update<Info>(["project", projectID], (draft) => {
306
+ draft.time.initialized = Date.now()
307
+ })
308
+ }
309
+
310
+ export async function list() {
311
+ const keys = await Storage.list(["project"])
312
+ const projects = await Promise.all(keys.map((x) => Storage.read<Info>(x)))
313
+ return projects.map((project) => ({
314
+ ...project,
315
+ sandboxes: project.sandboxes?.filter((x) => existsSync(x)),
316
+ }))
317
+ }
318
+
319
+ export const update = fn(
320
+ z.object({
321
+ projectID: z.string(),
322
+ name: z.string().optional(),
323
+ icon: Info.shape.icon.optional(),
324
+ }),
325
+ async (input) => {
326
+ const result = await Storage.update<Info>(["project", input.projectID], (draft) => {
327
+ if (input.name !== undefined) draft.name = input.name
328
+ if (input.icon !== undefined) {
329
+ draft.icon = {
330
+ ...draft.icon,
331
+ }
332
+ if (input.icon.url !== undefined) draft.icon.url = input.icon.url
333
+ if (input.icon.override !== undefined) draft.icon.override = input.icon.override || undefined
334
+ if (input.icon.color !== undefined) draft.icon.color = input.icon.color
335
+ }
336
+ draft.time.updated = Date.now()
337
+ })
338
+ GlobalBus.emit("event", {
339
+ payload: {
340
+ type: Event.Updated.type,
341
+ properties: result,
342
+ },
343
+ })
344
+ return result
345
+ },
346
+ )
347
+
348
+ export async function sandboxes(projectID: string) {
349
+ const project = await Storage.read<Info>(["project", projectID]).catch(() => undefined)
350
+ if (!project?.sandboxes) return []
351
+ const valid: string[] = []
352
+ for (const dir of project.sandboxes) {
353
+ const stat = await fs.stat(dir).catch(() => undefined)
354
+ if (stat?.isDirectory()) valid.push(dir)
355
+ }
356
+ return valid
357
+ }
358
+
359
+ export async function removeSandbox(projectID: string, directory: string) {
360
+ const result = await Storage.update<Info>(["project", projectID], (draft) => {
361
+ const sandboxes = draft.sandboxes ?? []
362
+ draft.sandboxes = sandboxes.filter((sandbox) => sandbox !== directory)
363
+ draft.time.updated = Date.now()
364
+ })
365
+ GlobalBus.emit("event", {
366
+ payload: {
367
+ type: Event.Updated.type,
368
+ properties: result,
369
+ },
370
+ })
371
+ return result
372
+ }
373
+ }