@openadapter/koda 1.0.0-beta.3

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 (357) hide show
  1. package/CHANGELOG.md +4448 -0
  2. package/README.md +665 -0
  3. package/dist/bun/cli.d.ts +2 -0
  4. package/dist/bun/cli.js +2 -0
  5. package/dist/bun/register-bedrock.d.ts +1 -0
  6. package/dist/bun/register-bedrock.js +1 -0
  7. package/dist/bun/restore-sandbox-env.d.ts +12 -0
  8. package/dist/bun/restore-sandbox-env.js +1 -0
  9. package/dist/cli/args.d.ts +55 -0
  10. package/dist/cli/args.js +167 -0
  11. package/dist/cli/config-selector.d.ts +13 -0
  12. package/dist/cli/config-selector.js +1 -0
  13. package/dist/cli/file-processor.d.ts +14 -0
  14. package/dist/cli/file-processor.js +7 -0
  15. package/dist/cli/import-sessions.d.ts +34 -0
  16. package/dist/cli/import-sessions.js +6 -0
  17. package/dist/cli/initial-message.d.ts +17 -0
  18. package/dist/cli/initial-message.js +1 -0
  19. package/dist/cli/list-models.d.ts +8 -0
  20. package/dist/cli/list-models.js +2 -0
  21. package/dist/cli/openadapter-setup.d.ts +29 -0
  22. package/dist/cli/openadapter-setup.js +3 -0
  23. package/dist/cli/session-picker.d.ts +8 -0
  24. package/dist/cli/session-picker.js +1 -0
  25. package/dist/cli.d.ts +2 -0
  26. package/dist/cli.js +2 -0
  27. package/dist/config.d.ts +92 -0
  28. package/dist/config.js +1 -0
  29. package/dist/core/agent-session-runtime.d.ts +116 -0
  30. package/dist/core/agent-session-runtime.js +1 -0
  31. package/dist/core/agent-session-services.d.ts +86 -0
  32. package/dist/core/agent-session-services.js +1 -0
  33. package/dist/core/agent-session.d.ts +747 -0
  34. package/dist/core/agent-session.js +32 -0
  35. package/dist/core/auth-guidance.d.ts +4 -0
  36. package/dist/core/auth-guidance.js +8 -0
  37. package/dist/core/auth-storage.d.ts +140 -0
  38. package/dist/core/auth-storage.js +1 -0
  39. package/dist/core/bash-executor.d.ts +31 -0
  40. package/dist/core/bash-executor.js +1 -0
  41. package/dist/core/compaction/branch-summarization.d.ts +89 -0
  42. package/dist/core/compaction/branch-summarization.js +38 -0
  43. package/dist/core/compaction/compaction.d.ts +120 -0
  44. package/dist/core/compaction/compaction.js +104 -0
  45. package/dist/core/compaction/index.d.ts +6 -0
  46. package/dist/core/compaction/index.js +1 -0
  47. package/dist/core/compaction/utils.d.ts +37 -0
  48. package/dist/core/compaction/utils.js +19 -0
  49. package/dist/core/defaults.d.ts +2 -0
  50. package/dist/core/defaults.js +1 -0
  51. package/dist/core/diagnostics.d.ts +14 -0
  52. package/dist/core/diagnostics.js +0 -0
  53. package/dist/core/event-bus.d.ts +8 -0
  54. package/dist/core/event-bus.js +1 -0
  55. package/dist/core/exec.d.ts +28 -0
  56. package/dist/core/exec.js +1 -0
  57. package/dist/core/export-html/ansi-to-html.d.ts +21 -0
  58. package/dist/core/export-html/ansi-to-html.js +1 -0
  59. package/dist/core/export-html/index.d.ts +36 -0
  60. package/dist/core/export-html/index.js +2 -0
  61. package/dist/core/export-html/template.css +1066 -0
  62. package/dist/core/export-html/template.html +55 -0
  63. package/dist/core/export-html/template.js +72 -0
  64. package/dist/core/export-html/tool-renderer.d.ts +33 -0
  65. package/dist/core/export-html/tool-renderer.js +1 -0
  66. package/dist/core/export-html/vendor/highlight.min.js +8 -0
  67. package/dist/core/export-html/vendor/marked.min.js +56 -0
  68. package/dist/core/extensions/index.d.ts +11 -0
  69. package/dist/core/extensions/index.js +1 -0
  70. package/dist/core/extensions/loader.d.ts +23 -0
  71. package/dist/core/extensions/loader.js +1 -0
  72. package/dist/core/extensions/runner.d.ts +160 -0
  73. package/dist/core/extensions/runner.js +1 -0
  74. package/dist/core/extensions/types.d.ts +1180 -0
  75. package/dist/core/extensions/types.js +1 -0
  76. package/dist/core/extensions/wrapper.d.ts +19 -0
  77. package/dist/core/extensions/wrapper.js +1 -0
  78. package/dist/core/footer-data-provider.d.ts +53 -0
  79. package/dist/core/footer-data-provider.js +1 -0
  80. package/dist/core/http-dispatcher.d.ts +20 -0
  81. package/dist/core/http-dispatcher.js +1 -0
  82. package/dist/core/index.d.ts +11 -0
  83. package/dist/core/index.js +1 -0
  84. package/dist/core/keybindings.d.ts +352 -0
  85. package/dist/core/keybindings.js +1 -0
  86. package/dist/core/messages.d.ts +76 -0
  87. package/dist/core/messages.js +17 -0
  88. package/dist/core/model-registry.d.ts +149 -0
  89. package/dist/core/model-registry.js +9 -0
  90. package/dist/core/model-resolver.d.ts +109 -0
  91. package/dist/core/model-resolver.js +1 -0
  92. package/dist/core/output-guard.d.ts +6 -0
  93. package/dist/core/output-guard.js +1 -0
  94. package/dist/core/package-manager.d.ts +203 -0
  95. package/dist/core/package-manager.js +3 -0
  96. package/dist/core/prompt-templates.d.ts +51 -0
  97. package/dist/core/prompt-templates.js +2 -0
  98. package/dist/core/provider-attribution.d.ts +3 -0
  99. package/dist/core/provider-attribution.js +1 -0
  100. package/dist/core/provider-display-names.d.ts +1 -0
  101. package/dist/core/provider-display-names.js +1 -0
  102. package/dist/core/resolve-config-value.d.ts +30 -0
  103. package/dist/core/resolve-config-value.js +1 -0
  104. package/dist/core/resource-loader.d.ts +193 -0
  105. package/dist/core/resource-loader.js +1 -0
  106. package/dist/core/sdk.d.ts +108 -0
  107. package/dist/core/sdk.js +1 -0
  108. package/dist/core/session-cwd.d.ts +18 -0
  109. package/dist/core/session-cwd.js +7 -0
  110. package/dist/core/session-manager.d.ts +331 -0
  111. package/dist/core/session-manager.js +11 -0
  112. package/dist/core/settings-manager.d.ts +265 -0
  113. package/dist/core/settings-manager.js +1 -0
  114. package/dist/core/skills.d.ts +59 -0
  115. package/dist/core/skills.js +4 -0
  116. package/dist/core/slash-commands.d.ts +13 -0
  117. package/dist/core/slash-commands.js +1 -0
  118. package/dist/core/source-info.d.ts +17 -0
  119. package/dist/core/source-info.js +1 -0
  120. package/dist/core/system-prompt.d.ts +27 -0
  121. package/dist/core/system-prompt.js +52 -0
  122. package/dist/core/telemetry.d.ts +2 -0
  123. package/dist/core/telemetry.js +1 -0
  124. package/dist/core/timings.d.ts +7 -0
  125. package/dist/core/timings.js +3 -0
  126. package/dist/core/tools/bash.d.ts +67 -0
  127. package/dist/core/tools/bash.js +18 -0
  128. package/dist/core/tools/edit-diff.d.ts +86 -0
  129. package/dist/core/tools/edit-diff.js +16 -0
  130. package/dist/core/tools/edit.d.ts +50 -0
  131. package/dist/core/tools/edit.js +2 -0
  132. package/dist/core/tools/file-mutation-queue.d.ts +5 -0
  133. package/dist/core/tools/file-mutation-queue.js +1 -0
  134. package/dist/core/tools/find.d.ts +34 -0
  135. package/dist/core/tools/find.js +13 -0
  136. package/dist/core/tools/grep.d.ts +36 -0
  137. package/dist/core/tools/grep.js +13 -0
  138. package/dist/core/tools/index.d.ts +39 -0
  139. package/dist/core/tools/index.js +1 -0
  140. package/dist/core/tools/ls.d.ts +36 -0
  141. package/dist/core/tools/ls.js +9 -0
  142. package/dist/core/tools/output-accumulator.d.ts +51 -0
  143. package/dist/core/tools/output-accumulator.js +4 -0
  144. package/dist/core/tools/path-utils.d.ts +9 -0
  145. package/dist/core/tools/path-utils.js +1 -0
  146. package/dist/core/tools/read.d.ts +34 -0
  147. package/dist/core/tools/read.js +22 -0
  148. package/dist/core/tools/render-utils.d.ts +23 -0
  149. package/dist/core/tools/render-utils.js +4 -0
  150. package/dist/core/tools/tool-definition-wrapper.d.ts +13 -0
  151. package/dist/core/tools/tool-definition-wrapper.js +1 -0
  152. package/dist/core/tools/truncate.d.ts +69 -0
  153. package/dist/core/tools/truncate.js +5 -0
  154. package/dist/core/tools/write.d.ts +25 -0
  155. package/dist/core/tools/write.js +13 -0
  156. package/dist/index.d.ts +30 -0
  157. package/dist/index.js +1 -0
  158. package/dist/main.d.ts +11 -0
  159. package/dist/main.js +1 -0
  160. package/dist/migrations.d.ts +32 -0
  161. package/dist/migrations.js +8 -0
  162. package/dist/modes/index.d.ts +8 -0
  163. package/dist/modes/index.js +1 -0
  164. package/dist/modes/interactive/assets/clankolas.png +0 -0
  165. package/dist/modes/interactive/components/armin.d.ts +33 -0
  166. package/dist/modes/interactive/components/armin.js +1 -0
  167. package/dist/modes/interactive/components/assistant-message.d.ts +19 -0
  168. package/dist/modes/interactive/components/assistant-message.js +1 -0
  169. package/dist/modes/interactive/components/bash-execution.d.ts +33 -0
  170. package/dist/modes/interactive/components/bash-execution.js +13 -0
  171. package/dist/modes/interactive/components/bordered-loader.d.ts +15 -0
  172. package/dist/modes/interactive/components/bordered-loader.js +1 -0
  173. package/dist/modes/interactive/components/branch-summary-message.d.ts +15 -0
  174. package/dist/modes/interactive/components/branch-summary-message.js +3 -0
  175. package/dist/modes/interactive/components/compaction-summary-message.d.ts +15 -0
  176. package/dist/modes/interactive/components/compaction-summary-message.js +3 -0
  177. package/dist/modes/interactive/components/config-selector.d.ts +70 -0
  178. package/dist/modes/interactive/components/config-selector.js +1 -0
  179. package/dist/modes/interactive/components/countdown-timer.d.ts +13 -0
  180. package/dist/modes/interactive/components/countdown-timer.js +1 -0
  181. package/dist/modes/interactive/components/custom-editor.d.ts +20 -0
  182. package/dist/modes/interactive/components/custom-editor.js +1 -0
  183. package/dist/modes/interactive/components/custom-message.d.ts +19 -0
  184. package/dist/modes/interactive/components/custom-message.js +2 -0
  185. package/dist/modes/interactive/components/daxnuts.d.ts +22 -0
  186. package/dist/modes/interactive/components/daxnuts.js +1 -0
  187. package/dist/modes/interactive/components/diff.d.ts +11 -0
  188. package/dist/modes/interactive/components/diff.js +3 -0
  189. package/dist/modes/interactive/components/dynamic-border.d.ts +14 -0
  190. package/dist/modes/interactive/components/dynamic-border.js +1 -0
  191. package/dist/modes/interactive/components/earendil-announcement.d.ts +4 -0
  192. package/dist/modes/interactive/components/earendil-announcement.js +1 -0
  193. package/dist/modes/interactive/components/extension-editor.d.ts +19 -0
  194. package/dist/modes/interactive/components/extension-editor.js +3 -0
  195. package/dist/modes/interactive/components/extension-input.d.ts +22 -0
  196. package/dist/modes/interactive/components/extension-input.js +2 -0
  197. package/dist/modes/interactive/components/extension-selector.d.ts +25 -0
  198. package/dist/modes/interactive/components/extension-selector.js +2 -0
  199. package/dist/modes/interactive/components/footer.d.ts +27 -0
  200. package/dist/modes/interactive/components/footer.js +1 -0
  201. package/dist/modes/interactive/components/index.d.ts +31 -0
  202. package/dist/modes/interactive/components/index.js +1 -0
  203. package/dist/modes/interactive/components/keybinding-hints.d.ts +12 -0
  204. package/dist/modes/interactive/components/keybinding-hints.js +1 -0
  205. package/dist/modes/interactive/components/login-dialog.d.ts +51 -0
  206. package/dist/modes/interactive/components/login-dialog.js +1 -0
  207. package/dist/modes/interactive/components/model-selector.d.ts +46 -0
  208. package/dist/modes/interactive/components/model-selector.js +2 -0
  209. package/dist/modes/interactive/components/oauth-selector.d.ts +30 -0
  210. package/dist/modes/interactive/components/oauth-selector.js +1 -0
  211. package/dist/modes/interactive/components/scoped-models-selector.d.ts +41 -0
  212. package/dist/modes/interactive/components/scoped-models-selector.js +1 -0
  213. package/dist/modes/interactive/components/session-selector-search.d.ts +22 -0
  214. package/dist/modes/interactive/components/session-selector-search.js +1 -0
  215. package/dist/modes/interactive/components/session-selector.d.ts +95 -0
  216. package/dist/modes/interactive/components/session-selector.js +2 -0
  217. package/dist/modes/interactive/components/settings-selector.d.ts +68 -0
  218. package/dist/modes/interactive/components/settings-selector.js +1 -0
  219. package/dist/modes/interactive/components/show-images-selector.d.ts +9 -0
  220. package/dist/modes/interactive/components/show-images-selector.js +1 -0
  221. package/dist/modes/interactive/components/skill-invocation-message.d.ts +16 -0
  222. package/dist/modes/interactive/components/skill-invocation-message.js +3 -0
  223. package/dist/modes/interactive/components/theme-selector.d.ts +10 -0
  224. package/dist/modes/interactive/components/theme-selector.js +1 -0
  225. package/dist/modes/interactive/components/thinking-selector.d.ts +10 -0
  226. package/dist/modes/interactive/components/thinking-selector.js +1 -0
  227. package/dist/modes/interactive/components/tool-execution.d.ts +62 -0
  228. package/dist/modes/interactive/components/tool-execution.js +4 -0
  229. package/dist/modes/interactive/components/tree-selector.d.ts +88 -0
  230. package/dist/modes/interactive/components/tree-selector.js +1 -0
  231. package/dist/modes/interactive/components/user-message-selector.d.ts +29 -0
  232. package/dist/modes/interactive/components/user-message-selector.js +1 -0
  233. package/dist/modes/interactive/components/user-message.d.ts +9 -0
  234. package/dist/modes/interactive/components/user-message.js +1 -0
  235. package/dist/modes/interactive/components/visual-truncate.d.ts +23 -0
  236. package/dist/modes/interactive/components/visual-truncate.js +1 -0
  237. package/dist/modes/interactive/interactive-mode.d.ts +417 -0
  238. package/dist/modes/interactive/interactive-mode.js +116 -0
  239. package/dist/modes/interactive/theme/dark.json +86 -0
  240. package/dist/modes/interactive/theme/light.json +85 -0
  241. package/dist/modes/interactive/theme/theme-schema.json +335 -0
  242. package/dist/modes/interactive/theme/theme.d.ts +101 -0
  243. package/dist/modes/interactive/theme/theme.js +18 -0
  244. package/dist/modes/print-mode.d.ts +27 -0
  245. package/dist/modes/print-mode.js +4 -0
  246. package/dist/modes/rpc/jsonl.d.ts +16 -0
  247. package/dist/modes/rpc/jsonl.js +3 -0
  248. package/dist/modes/rpc/rpc-client.d.ts +226 -0
  249. package/dist/modes/rpc/rpc-client.js +1 -0
  250. package/dist/modes/rpc/rpc-mode.d.ts +19 -0
  251. package/dist/modes/rpc/rpc-mode.js +1 -0
  252. package/dist/modes/rpc/rpc-types.d.ts +419 -0
  253. package/dist/modes/rpc/rpc-types.js +0 -0
  254. package/dist/package-manager-cli.d.ts +3 -0
  255. package/dist/package-manager-cli.js +49 -0
  256. package/dist/utils/ansi.d.ts +1 -0
  257. package/dist/utils/ansi.js +1 -0
  258. package/dist/utils/auto-update.d.ts +13 -0
  259. package/dist/utils/auto-update.js +1 -0
  260. package/dist/utils/changelog.d.ts +20 -0
  261. package/dist/utils/changelog.js +4 -0
  262. package/dist/utils/child-process.d.ts +14 -0
  263. package/dist/utils/child-process.js +1 -0
  264. package/dist/utils/clipboard-image.d.ts +10 -0
  265. package/dist/utils/clipboard-image.js +1 -0
  266. package/dist/utils/clipboard-native.d.ts +9 -0
  267. package/dist/utils/clipboard-native.js +1 -0
  268. package/dist/utils/clipboard.d.ts +1 -0
  269. package/dist/utils/clipboard.js +1 -0
  270. package/dist/utils/deprecation.d.ts +3 -0
  271. package/dist/utils/deprecation.js +1 -0
  272. package/dist/utils/exif-orientation.d.ts +4 -0
  273. package/dist/utils/exif-orientation.js +1 -0
  274. package/dist/utils/frontmatter.d.ts +7 -0
  275. package/dist/utils/frontmatter.js +4 -0
  276. package/dist/utils/fs-watch.d.ts +4 -0
  277. package/dist/utils/fs-watch.js +1 -0
  278. package/dist/utils/git.d.ts +25 -0
  279. package/dist/utils/git.js +1 -0
  280. package/dist/utils/html.d.ts +6 -0
  281. package/dist/utils/html.js +1 -0
  282. package/dist/utils/image-convert.d.ts +8 -0
  283. package/dist/utils/image-convert.js +1 -0
  284. package/dist/utils/image-resize-core.d.ts +29 -0
  285. package/dist/utils/image-resize-core.js +1 -0
  286. package/dist/utils/image-resize-worker.d.ts +1 -0
  287. package/dist/utils/image-resize-worker.js +1 -0
  288. package/dist/utils/image-resize.d.ts +15 -0
  289. package/dist/utils/image-resize.js +1 -0
  290. package/dist/utils/json.d.ts +2 -0
  291. package/dist/utils/json.js +1 -0
  292. package/dist/utils/koda-user-agent.d.ts +1 -0
  293. package/dist/utils/koda-user-agent.js +1 -0
  294. package/dist/utils/mime.d.ts +2 -0
  295. package/dist/utils/mime.js +1 -0
  296. package/dist/utils/paths.d.ts +30 -0
  297. package/dist/utils/paths.js +1 -0
  298. package/dist/utils/photon.d.ts +20 -0
  299. package/dist/utils/photon.js +1 -0
  300. package/dist/utils/shell.d.ts +29 -0
  301. package/dist/utils/shell.js +8 -0
  302. package/dist/utils/sleep.d.ts +4 -0
  303. package/dist/utils/sleep.js +1 -0
  304. package/dist/utils/syntax-highlight.d.ts +11 -0
  305. package/dist/utils/syntax-highlight.js +2 -0
  306. package/dist/utils/tools-manager.d.ts +2 -0
  307. package/dist/utils/tools-manager.js +1 -0
  308. package/dist/utils/version-check.d.ts +14 -0
  309. package/dist/utils/version-check.js +1 -0
  310. package/dist/utils/windows-self-update.d.ts +2 -0
  311. package/dist/utils/windows-self-update.js +1 -0
  312. package/docs/compaction.md +394 -0
  313. package/docs/custom-provider.md +736 -0
  314. package/docs/development.md +71 -0
  315. package/docs/docs.json +148 -0
  316. package/docs/extensions.md +2626 -0
  317. package/docs/images/doom-extension.png +0 -0
  318. package/docs/images/exy.png +0 -0
  319. package/docs/images/interactive-mode.png +0 -0
  320. package/docs/images/tree-view.png +0 -0
  321. package/docs/index.md +80 -0
  322. package/docs/json.md +82 -0
  323. package/docs/keybindings.md +197 -0
  324. package/docs/models.md +493 -0
  325. package/docs/packages.md +226 -0
  326. package/docs/prompt-templates.md +88 -0
  327. package/docs/providers.md +253 -0
  328. package/docs/quickstart.md +165 -0
  329. package/docs/rpc.md +1408 -0
  330. package/docs/sdk.md +1137 -0
  331. package/docs/session-format.md +412 -0
  332. package/docs/sessions.md +145 -0
  333. package/docs/settings.md +281 -0
  334. package/docs/shell-aliases.md +13 -0
  335. package/docs/skills.md +231 -0
  336. package/docs/terminal-setup.md +114 -0
  337. package/docs/termux.md +127 -0
  338. package/docs/themes.md +295 -0
  339. package/docs/tmux.md +61 -0
  340. package/docs/tui.md +927 -0
  341. package/docs/usage.md +288 -0
  342. package/docs/windows.md +17 -0
  343. package/npm-shrinkwrap.json +1792 -0
  344. package/openadapter/extensions/koda-ask.js +12 -0
  345. package/openadapter/extensions/koda-bg.js +14 -0
  346. package/openadapter/extensions/koda-commands.mjs +15 -0
  347. package/openadapter/extensions/koda-help.js +8 -0
  348. package/openadapter/extensions/koda-memory.js +16 -0
  349. package/openadapter/extensions/koda-status.js +1 -0
  350. package/openadapter/extensions/koda-todo.js +4 -0
  351. package/openadapter/extensions/koda-vision.js +4 -0
  352. package/openadapter/extensions/koda-web.js +7 -0
  353. package/openadapter/setup.mjs +173 -0
  354. package/openadapter/skills/code-review/SKILL.md +22 -0
  355. package/openadapter/skills/debugging/SKILL.md +28 -0
  356. package/openadapter/skills/frontend/SKILL.md +38 -0
  357. package/package.json +108 -0
@@ -0,0 +1,12 @@
1
+ var v=Object.defineProperty;var l=(r,t)=>v(r,"name",{value:t,configurable:!0});import{Text as k,truncateToWidth as q,wrapTextWithAnsi as b}from"@openadapter/koda-tui";import{Type as h}from"typebox";const x={__chat__:!0},m=l(r=>({content:[{type:"text",text:r}],details:void 0}),"text");class T{static{l(this,"QuestionCard")}qIndex=0;rowIndex=0;picks;freeText;typing=!1;buffer="";width=0;tui;theme;questions;done;constructor(t,e,s,a){this.tui=t,this.theme=e,this.questions=s,this.done=a,this.picks=s.map(()=>new Set),this.freeText=s.map(()=>{})}invalidate(){}get q(){return this.questions[this.qIndex]}get onSubmitTab(){return this.qIndex>=this.questions.length}get rowCount(){return(this.q?.options.length??0)+2}get typeRow(){return this.q.options.length}get chatRow(){return this.q.options.length+1}get singleImmediate(){return this.questions.length===1&&!this.questions[0]?.multiSelect}handleInput(t){if(this.typing)return this.handleTyping(t);const e=t;if(e==="\x1B"||e===""){this.done(x);return}if(e==="\x1B[A")this.moveRow(-1);else if(e==="\x1B[B")this.moveRow(1);else if(e==="\x1B[C"||e===" ")this.moveTab(1);else if(e==="\x1B[D"||e==="\x1B[Z")this.moveTab(-1);else if(e==="\r"||e===`
2
+ `)this.enter();else if(e===" ")this.toggleCurrent();else if(/^[1-9]$/.test(e))this.pickNumber(Number(e)-1);else return;this.tui.requestRender()}handleTyping(t){if(t==="\r"||t===`
3
+ `)this.freeText[this.qIndex]=this.buffer.trim()||void 0,this.typing=!1,this.buffer="",this.advanceAfterAnswer();else if(t==="\x7F"||t==="\b")this.buffer=this.buffer.slice(0,-1);else if(t==="\x1B")this.typing=!1,this.buffer="";else if(t.length===1&&t>=" ")this.buffer+=t;else return;this.tui.requestRender()}moveRow(t){if(this.onSubmitTab)return;const e=this.rowCount;this.rowIndex=(this.rowIndex+t+e)%e}moveTab(t){const e=this.questions.length;this.qIndex=Math.max(0,Math.min(this.qIndex+t,e)),this.rowIndex=0}toggleCurrent(){this.onSubmitTab||this.rowIndex>=this.q.options.length||this.toggleOption(this.rowIndex)}toggleOption(t){const e=this.picks[this.qIndex];this.q.multiSelect?e.has(t)?e.delete(t):e.add(t):(e.clear(),e.add(t))}pickNumber(t){this.onSubmitTab||t>=this.q.options.length||(this.rowIndex=t,this.toggleOption(t),this.q.multiSelect||this.afterSinglePick(),this.tui.requestRender())}enter(){if(this.onSubmitTab){this.finalize();return}if(this.rowIndex===this.chatRow){this.done(x);return}if(this.rowIndex===this.typeRow){this.typing=!0,this.buffer=this.freeText[this.qIndex]??"";return}this.toggleOption(this.rowIndex),this.q.multiSelect||this.afterSinglePick()}afterSinglePick(){if(this.singleImmediate){this.finalize();return}this.advanceAfterAnswer()}advanceAfterAnswer(){this.qIndex<this.questions.length-1?(this.qIndex+=1,this.rowIndex=0):this.qIndex=this.questions.length}finalize(){const t={};this.questions.forEach((e,s)=>{const a=this.freeText[s];if(a){t[e.header]=a;return}const u=[...this.picks[s]],d=u.length?u:[0];t[e.header]=d.map(n=>e.options[n]?.label??"").filter(Boolean).join(", ")}),this.done(t)}render(t){const e=this.theme,s=[];if(s.push(this.renderTabs(t)),s.push(""),this.onSubmitTab)return s.push(e.fg("text",e.bold("Review & submit"))),s.push(""),this.questions.forEach(n=>{const o=this.freeText[this.questions.indexOf(n)],i=this.questions.indexOf(n),c=o??[...this.picks[i]].map(p=>n.options[p]?.label).filter(Boolean).join(", ");s.push(` ${e.fg("accent",n.header)}: ${e.fg("text",c||e.fg("muted","(recommended)"))}`)}),s.push(""),s.push(e.fg("success"," \u2714 Press Enter to submit")),s.push(""),s.push(e.fg("muted","Enter submit \xB7 Tab/\u2190\u2192 switch \xB7 Esc cancel")),s;for(const n of b(e.bold(this.q.question),t))s.push(n);s.push("");const a=l(n=>n?e.fg("accent","\u2192 "):" ","cursor");this.q.options.forEach((n,o)=>{const i=this.rowIndex===o,c=this.picks[this.qIndex].has(o),p=this.q.multiSelect?c?e.fg("success","\u2612"):e.fg("muted","\u2610"):c?e.fg("success","\u2713"):" ",f=`${o+1}. ${n.label}`;if(s.push(`${a(i)}${p} ${i?e.fg("accent",e.bold(f)):e.fg("accent",f)}`),n.description){const g=" ";for(const y of b(e.fg("muted",n.description),Math.max(20,t-g.length)))s.push(g+y)}}),s.push(e.fg("border","\u2500".repeat(Math.min(t,60))));const u=this.rowIndex===this.typeRow;if(this.typing)s.push(`${a(!0)}${e.fg("text",this.buffer)}${e.fg("accent","\u258F")}`),s.push(e.fg("muted"," type your answer \xB7 Enter to confirm \xB7 Esc to cancel"));else{const n=this.q.options.length,o=`${n+1}. Type something`,i=`${n+2}. Chat about this`;s.push(`${a(u)}${u?e.fg("text",e.bold(o)):e.fg("muted",o)}`);const c=this.rowIndex===this.chatRow;s.push(`${a(c)}${c?e.fg("text",e.bold(i)):e.fg("muted",i)}`)}s.push("");const d=this.q.multiSelect?"Space toggle \xB7 Enter next \xB7 \u2191\u2193 move \xB7 Tab/\u2190\u2192 switch \xB7 Esc cancel":"Enter select \xB7 \u2191\u2193 move \xB7 Tab/\u2190\u2192 switch \xB7 1-9 pick \xB7 Esc cancel";return s.push(e.fg("muted",d)),s}renderTabs(t){const e=this.theme,s=[];this.questions.forEach((n,o)=>{const i=this.qIndex===o,f=` ${this.freeText[o]||this.picks[o].size>0?"\u2612":"\u2610"} ${n.header} `;s.push(i?e.fg("accent",`\x1B[7m${f}\x1B[27m`):e.fg("muted",f))});const a=this.onSubmitTab,u=" \u2714 Submit ";s.push(a?e.fg("success",`\x1B[7m${u}\x1B[27m`):e.fg("muted",u));const d=`${e.fg("muted","\u2190 ")}${s.join(e.fg("muted"," "))}${e.fg("muted"," \u2192")}`;return q(d,t,"")}}function A(r){const t=(r||"").trim();if(!t)return!1;const e=t.toLowerCase();if(/[\w.\-]+\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|rb|php|c|cpp|h|hpp|cs|css|scss|html|json|md|sql|ya?ml|toml|sh|swift|kt)\b/.test(e)||/\b(fix|debug|why|explain|refactor|rename|revert|undo|investigate|trace|reproduce|stack ?trace|error:|exception)\b/.test(e)||/[a-z0-9_~.]+\/[a-z0-9_]/i.test(t)||t.length>600)return!1;const s=/\b(build|make|create|set ?up|scaffold|start|bootstrap|generate|spin ?up|add a|i want|i'?d like|we (?:need|want)|help me|let'?s (?:build|make|create|do))\b/.test(e),a=/\b(app|application|site|website|web ?app|api|backend|back-end|frontend|front-end|server|service|tool|cli|bot|dashboard|game|project|landing|page|component|library|package|extension|plugin|prototype|mvp|feature|integration|pipeline|script)\b/.test(e),u=t.split(/\s+/).length;return!!(s&&a||s&&u<=12)}l(A,"isVague");const I=["","<koda-brainstorm>","This request is open-ended. Before writing code:","1. Briefly state the approach you intend to take (one or two sentences).","2. Call the `ask_user` tool to confirm ONLY the decisions that genuinely change the outcome \u2014 e.g. framework, language, storage, styling, scope. Give 2\u20134 options each, your recommended option FIRST, with a one-line description per option. Batch related questions into a single `ask_user` call (up to 4).","Skip anything you can reasonably infer from the existing repo, and don't ask about things you can just decide yourself. Ask at most one round, then build. If nothing is genuinely ambiguous, skip ask_user and proceed.","</koda-brainstorm>"].join(`
4
+ `);function S(r){return Array.isArray(r)?r.map(t=>{const e=Array.isArray(t?.options)?t.options.map(s=>({label:String(s?.label??"").trim(),description:s?.description?String(s.description):void 0})).filter(s=>s.label):[];return{header:String(t?.header??t?.question??"Choose").trim().slice(0,16),question:String(t?.question??"").trim(),options:e,multiSelect:!!t?.multiSelect}}).filter(t=>t.question&&t.options.length>=2).slice(0,4):[]}l(S,"normalizeQuestions");function w(r){return`The user answered:
5
+ ${Object.entries(r).map(([e,s])=>`- ${e}: ${s}`).join(`
6
+ `)}
7
+
8
+ Proceed based on these choices. Do not re-ask what they already decided.`}l(w,"answerText");function $(r){r.registerTool({name:"ask_user",label:"Ask the user",description:"Ask the user to choose between concrete options for a decision that genuinely changes the outcome (framework, language, storage, styling, scope, \u2026). Renders a multiple-choice card. Put YOUR recommended option first. Use this instead of guessing on open-ended requests \u2014 but only for decisions you can't reasonably infer or just decide yourself. Returns the user's choice(s).",promptSnippet:"ask_user: present multiple-choice options to the user for a key decision and get their pick",promptGuidelines:["When a request is open-ended and a decision would change the result (which framework? which DB? what scope?), call ask_user with 1-4 questions, each with 2-4 options, your recommended option first, a one-line description per option.","Do NOT ask about things you can infer from the repo or decide yourself. Don't ask more than one round. After the user answers, build \u2014 don't re-ask."],parameters:h.Object({questions:h.Array(h.Object({header:h.String({description:"Very short tab label, \u226412 chars (e.g. 'Framework', 'Storage')."}),question:h.String({description:"The full question to ask."}),options:h.Array(h.Object({label:h.String({description:"Concise option label (1-5 words)."}),description:h.Optional(h.String({description:"One line explaining the option / trade-off."}))}),{minItems:2,maxItems:4,description:"2-4 options; put your recommendation FIRST."}),multiSelect:h.Optional(h.Boolean({description:"Allow selecting multiple options."}))}),{minItems:1,maxItems:4})}),async execute(t,e,s,a,u){const d=S(e?.questions);if(!d.length)return m("ask_user: no valid questions (need \u22651 question with \u22652 options). Proceed with your best judgement.");if(u?.mode!=="tui"){const o=Object.fromEntries(d.map(i=>[i.header,i.options[0]?.label??""]));return m(`Non-interactive mode \u2014 auto-selected the recommended option for each question:
9
+ ${Object.entries(o).map(([i,c])=>`- ${i}: ${c}`).join(`
10
+ `)}
11
+
12
+ Proceed with these.`)}let n;try{n=await u.ui.custom((o,i,c,p)=>new T(o,i,d,p))}catch{const o=Object.fromEntries(d.map(i=>[i.header,i.options[0]?.label??""]));return m(w(o))}return n?.__chat__?m("The user chose to discuss this rather than pick from the options. Continue the conversation and help them decide \u2014 do not force a selection or re-open the card unless they ask."):m(w(n))},renderCall(t,e){const s=Array.isArray(t?.questions)?t.questions[0]?.question:void 0;return new k(e.fg("toolTitle",e.bold("ask_user "))+e.fg("muted",String(s??"").slice(0,80)),0,0)}}),r.on("before_agent_start",t=>{if(process.env.KODA_ASK!=="0"&&A(t?.prompt??""))return{systemPrompt:`${t.systemPrompt}${I}`}})}l($,"default");export{$ as default};
@@ -0,0 +1,14 @@
1
+ var h=Object.defineProperty;var u=(n,s)=>h(n,"name",{value:s,configurable:!0});import{spawn as f}from"node:child_process";import{openSync as k,readFileSync as y}from"node:fs";import{tmpdir as w}from"node:os";import{join as $}from"node:path";import{Text as _}from"@openadapter/koda-tui";import{Type as i}from"typebox";const d=new Map,c=u(n=>({content:[{type:"text",text:n}]}),"text");function p(n){try{return process.kill(n,0),!0}catch{return!1}}u(p,"alive");function g(n,s=40){try{return y(n,"utf-8").split(`
2
+ `).slice(-s).join(`
3
+ `).trim()}catch{return""}}u(g,"tailLog");function S(n){n.registerTool({name:"run_background",label:"Run in background",description:"Start a long-running command in the BACKGROUND and return immediately \u2014 for anything that does not exit on its own: a dev server, `docker-compose up`, a file watcher, `npm run dev`. Returns the PID, the log path, and any early output. Use this instead of bash for servers/demos; check it with bg_output and stop it with bg_stop.",promptSnippet:"run_background: start a server / long-running process without blocking",promptGuidelines:["For anything that runs continuously (a dev server, docker-compose up, a watcher) use run_background, NOT bash \u2014 bash blocks until the command exits, which never happens for a server. After starting it, check bg_output to confirm it came up."],parameters:i.Object({command:i.String({description:"Shell command to run in the background"}),cwd:i.Optional(i.String({description:"Working directory (default: current)"}))}),async execute(s,t){try{const e=$(w(),`koda-bg-${Date.now()}-${Math.floor(performance.now())}.log`),o=k(e,"a"),r=f(t.command,{shell:!0,detached:!0,stdio:["ignore",o,o],cwd:t.cwd||process.cwd()}),a=r.pid;if(!a)return c("run_background failed: could not start process.");r.unref(),d.set(a,{pid:a,command:t.command,log:e,startedAt:Date.now()}),await new Promise(b=>setTimeout(b,1e3));const l=g(e,20),m=p(a)?`running (pid ${a})`:`exited already (pid ${a}) \u2014 likely failed`;return c(`Started in background \u2014 ${m}.
4
+ Logs: ${e}
5
+ ${l?`
6
+ Early output:
7
+ ${l}`:"(no output yet)"}
8
+
9
+ Check more with bg_output, stop with bg_stop ${a}.`)}catch(e){return c(`run_background failed: ${e.message}`)}},renderCall(s,t){return new _(t.fg("toolTitle",t.bold("run_background "))+t.fg("muted",String(s.command).slice(0,80)),0,0)}}),n.registerTool({name:"bg_output",label:"Background output",description:"Show recent output from a background process (started with run_background). Omit pid for the most recent one.",promptSnippet:"bg_output: read recent output of a background process",parameters:i.Object({pid:i.Optional(i.Number({description:"PID (default: most recent)"})),lines:i.Optional(i.Number({description:"How many trailing lines (default 40)"}))}),async execute(s,t){const e=[...d.values()];if(e.length===0)return c("No background processes.");const o=t.pid?d.get(t.pid):e[e.length-1];if(!o)return c(`No background process with pid ${t.pid}.`);const r=g(o.log,t.lines??40),a=p(o.pid)?"running":"exited";return c(`pid ${o.pid} (${a}) \u2014 ${o.command}
10
+
11
+ ${r||"(no output)"}`)}}),n.registerTool({name:"bg_stop",label:"Stop background",description:"Stop a background process and its children. Pass the pid (omit to stop all background processes).",promptSnippet:"bg_stop: stop a background process",parameters:i.Object({pid:i.Optional(i.Number({description:"PID to stop (default: all)"}))}),async execute(s,t){const e=t.pid?[d.get(t.pid)].filter(Boolean):[...d.values()];if(e.length===0)return c(t.pid?`No background process with pid ${t.pid}.`:"No background processes.");const o=[];for(const r of e){try{process.kill(-r.pid,"SIGTERM")}catch{try{process.kill(r.pid,"SIGTERM")}catch{}}d.delete(r.pid),o.push(r.pid)}return c(`Stopped: ${o.join(", ")}`)}}),n.registerCommand("bg",{description:"List background processes Koda started",handler:u(async(s,t)=>{const e=[...d.values()];if(e.length===0){t.ui.notify("No background processes.","info");return}const o=e.map(r=>`pid ${r.pid} \xB7 ${p(r.pid)?"running":"exited"} \xB7 ${r.command}`);t.ui.notify(`Background processes:
12
+ ${o.join(`
13
+ `)}`,"info")},"handler")}),n.registerTool({name:"bg_list",label:"List background",description:"List the background processes Koda has started this session, with running/exited status.",promptSnippet:"bg_list: list background processes",parameters:i.Object({}),async execute(){const s=[...d.values()];return s.length===0?c("No background processes."):c(s.map(t=>`pid ${t.pid} (${p(t.pid)?"running":"exited"}) \u2014 ${t.command}`).join(`
14
+ `))}})}u(S,"default");export{S as default};
@@ -0,0 +1,15 @@
1
+ var f=Object.defineProperty;var s=(n,t)=>f(n,"name",{value:t,configurable:!0});import{existsSync as c,readdirSync as p,readFileSync as g,statSync as u,unlinkSync as y,writeFileSync as h}from"node:fs";import{homedir as _}from"node:os";import{join as d}from"node:path";function K(){return d(process.env.KODA_CODING_AGENT_DIR||d(_(),".koda","agent"),"checkpoints")}s(K,"checkpointBase");function v(){const n=K();if(!c(n))return null;const t=p(n).map(e=>d(n,e)).filter(e=>{try{return u(e).isDirectory()}catch{return!1}});return t.length?t.sort((e,o)=>u(o).mtimeMs-u(e).mtimeMs)[0]:null}s(v,"latestCheckpointDir");function D(n){n.registerCommand("undo",{description:"Revert file changes Koda made this session (restore from the safety checkpoint)",handler:s(async(t,e)=>{const o=v(),r=[];if(o){for(const i of p(o))if(i.endsWith(".json"))try{r.push(JSON.parse(g(d(o,i),"utf-8")))}catch{}}if(!r.length){e.ui.notify("Nothing to undo \u2014 no recorded file changes.","info");return}if(!await e.ui.confirm("Undo",`Restore ${r.length} file(s) to their pre-session state? This discards Koda's edits to them.`))return;let l=0,m=0;for(const i of r)try{i.existed?(h(i.path,i.content??"","utf-8"),l++):c(i.path)&&(y(i.path),m++)}catch{}e.ui.notify(`Undo complete: ${l} restored, ${m} removed.`,"info")},"handler")}),n.registerCommand("init",{description:"Create an AGENTS.md with project conventions Koda will follow",handler:s(async(t,e)=>{const o=d(process.cwd(),"AGENTS.md");if(c(o)){e.ui.notify("AGENTS.md already exists \u2014 nothing to do.","info");return}h(o,`# Project guide for Koda
2
+
3
+ ## Overview
4
+ _What this project is and what it does._
5
+
6
+ ## Conventions
7
+ - _Coding style, naming, file/module layout._
8
+
9
+ ## Build & test
10
+ - Build: _command_
11
+ - Test: _command_ (run this to verify changes)
12
+
13
+ ## Do / don't
14
+ - _Anything Koda should always or never do in this repo._
15
+ `,"utf-8"),e.ui.notify("Created AGENTS.md \u2014 fill it in; Koda reads it as project context.","info")},"handler")}),n.registerCommand("zenitsu",{description:"Zenitsu mode \u2014 speeds things up (toggle)",handler:s(async(t,e)=>{const o=process.env.KODA_ZENITSU==="1";process.env.KODA_ZENITSU=o?"0":"1",e.ui.notify(o?"Zenitsu off.":"\xBB Zenitsu on \u2014 speeds things up.","info"),e.ui.requestRender?.()},"handler")}),n.registerCommand("plan",{description:"Plan mode \u2014 Koda investigates and proposes a plan; no changes until you run /build",handler:s(async(t,e)=>{process.env.KODA_MODE="plan",e.ui.notify("\u25B9 Plan mode \u2014 Koda will investigate and propose a plan. No files change. Run /build when you're ready to execute.","info"),e.ui.requestRender?.()},"handler")}),n.registerCommand("build",{description:"Build mode \u2014 Koda executes (make changes). Switch here from plan mode to run the plan",handler:s(async(t,e)=>{const o=process.env.KODA_MODE==="plan";process.env.KODA_MODE="build",e.ui.notify(o?"\u25B8 Build mode \u2014 approved. Tell Koda to go ahead and it'll execute the plan.":"\u25B8 Build mode \u2014 Koda will make changes.","info"),e.ui.requestRender?.()},"handler")}),n.registerShortcut("ctrl+alt+p",{description:"Toggle plan / build mode",handler:s(async t=>{const e=process.env.KODA_MODE!=="plan";process.env.KODA_MODE=e?"plan":"build",t.ui.notify(e?"\u25B9 Plan mode \u2014 Koda proposes a plan (no changes). Ctrl+Alt+P or /build to execute.":"\u25B8 Build mode \u2014 Koda will make changes.","info"),t.ui.requestRender?.()},"handler")}),n.registerCommand("help",{description:"List available slash commands",handler:s(async(t,e)=>{const r=n.getCommands().slice().sort((a,l)=>a.name.localeCompare(l.name)).map(a=>`/${a.name}${a.description?` \u2014 ${a.description}`:""}`);await e.ui.select("Koda commands",r)},"handler")})}s(D,"kodaCommands");export{D as default};
@@ -0,0 +1,8 @@
1
+ var h=Object.defineProperty;var a=(o,t)=>h(o,"name",{value:t,configurable:!0});import{readFileSync as y}from"node:fs";import{homedir as _}from"node:os";import{join as f}from"node:path";import{Text as k}from"@openadapter/koda-tui";import{Type as g}from"typebox";function w(){let o=process.env.OPENADAPTER_BASE_URL||"",t=process.env.OPENADAPTER_API_KEY||"";if(!o||!t)try{const n=process.env.KODA_CODING_AGENT_DIR||f(_(),".koda","agent"),e=JSON.parse(y(f(n,"models.json"),"utf-8"));o=o||e?.providers?.openadapter?.baseUrl||"",t=t||e?.providers?.openadapter?.apiKey||""}catch{}return t?{root:(o||"https://api.openadapter.in/v1").replace(/\/v1\/?$/,"").replace(/\/$/,""),key:t}:null}a(w,"getGateway");const E=process.env.KODA_HELPER_MODEL||"glm-5.1";let p;async function b(o,t,n){if(!p){p=new Map;try{const e=await fetch(`${o}/v1/models`,{headers:{Authorization:`Bearer ${t}`}});if(e.ok){const l=await e.json();for(const r of l.data??[])r.tier&&p.set(r.id,r.tier)}}catch{}}return p.get(n)}a(b,"tierOf");function A(o){const t=[];try{for(const n of o.sessionManager.getBranch().slice(-16)){const e=n?.message;if(e)if(e.role==="user"||e.role==="assistant"){const l=typeof e.content=="string"?e.content:Array.isArray(e.content)?e.content.filter(r=>r?.type==="text").map(r=>r.text).join(" "):"";l.trim()&&t.push(`${e.role}: ${l.trim()}`)}else e.role==="toolResult"&&t.push(` result(${e.toolName??""}): ${(typeof e.content=="string"?e.content:"").slice(0,200)}`)}}catch{}return t.join(`
2
+ `).slice(-4e3)}a(A,"recentContext");const i=a(o=>({content:[{type:"text",text:o}]}),"text");function v(o){o.registerTool({name:"ask_for_help",label:"Ask for help",description:"Stuck, unsure how to approach a task, or about to give up or guess? Ask a more capable SENIOR model for a concrete, step-by-step plan, then follow it. Returns the plan \u2014 carry it out yourself.",promptSnippet:"ask_for_help: get a step-by-step plan from a senior model when you're stuck",promptGuidelines:["If you are unsure how to do something, stuck, or tempted to give up or guess, call ask_for_help with a clear description of the task/problem to get a concrete plan from a senior model \u2014 then follow that plan instead of guessing."],parameters:g.Object({question:g.String({description:"What you need help with, e.g. 'give me a plan to X' or 'I'm stuck on Y'"})}),async execute(t,n,e,l,r){const c=w();if(!c)return i("ask_for_help unavailable: no OpenAdapter key.");const u=r?.model?.id;if(u&&await b(c.root,c.key,u)==="max")return i("You are already a top-tier model \u2014 work through this directly; there is no more senior model to consult.");try{const s=await fetch(`${c.root}/v1/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${c.key}`,"Content-Type":"application/json"},body:JSON.stringify({model:E,temperature:0,messages:[{role:"system",content:"You are a senior engineer advising a smaller, less capable model that is working in a coding agent. Given the problem and what it has tried, give a CONCRETE, numbered, step-by-step plan it can follow directly \u2014 name exact files, commands, and tools to use, and call out the likely pitfalls. Be specific and decisive; if you were doing it yourself, lay out exactly what you'd do. Keep it tight (no preamble)."},{role:"user",content:`PROBLEM:
3
+ ${n.question}
4
+
5
+ WHAT'S HAPPENED SO FAR:
6
+ ${A(r)}`}]})});if(!s.ok)return i(`ask_for_help: senior model unavailable (HTTP ${s.status}). Proceed with your best judgement.`);const d=await s.json(),m=(d?.choices?.[0]?.message?.content??d?.choices?.[0]?.message?.reasoning_content??"").trim();return i(m?`Senior model's plan \u2014 follow these steps:
7
+
8
+ ${m}`:"ask_for_help: no plan returned. Proceed with your best judgement.")}catch(s){return i(`ask_for_help failed: ${s.message}. Proceed with your best judgement.`)}},renderCall(t,n){return new k(n.fg("toolTitle",n.bold("ask_for_help "))+n.fg("muted",String(t.question).slice(0,80)),0,0)}})}a(v,"default");export{v as default};
@@ -0,0 +1,16 @@
1
+ var C=Object.defineProperty;var a=(n,e)=>C(n,"name",{value:e,configurable:!0});import{existsSync as O,mkdirSync as S,readFileSync as d,writeFileSync as $}from"node:fs";import{homedir as b}from"node:os";import{dirname as D,join as u}from"node:path";import{matchesKey as A,Text as R,truncateToWidth as v}from"@openadapter/koda-tui";import{Type as l}from"typebox";const x=`# Koda memory
2
+
3
+ `;function j(){const n=process.env.KODA_CODING_AGENT_DIR||u(b(),".koda","agent");return u(n,"koda-memory.md")}a(j,"userMemoryPath");function k(){return u(process.cwd(),"koda-memory.md")}a(k,"projectMemoryPath");function I(n){return n==="user"?j():k()}a(I,"memoryPath");function g(n){if(!O(n))return[];try{return d(n,"utf-8").split(`
4
+ `).filter(e=>e.trim().startsWith("- ")).map(e=>e.trim().slice(2).trim()).filter(Boolean)}catch{return[]}}a(g,"readFacts");function E(n,e){const t=e.trim().replace(/\s+/g," ");if(!t)return!1;const r=I(n);if(g(r).some(i=>i.toLowerCase()===t.toLowerCase()))return!1;S(D(r),{recursive:!0});const s=O(r)?d(r,"utf-8").replace(/\s*$/,""):x.replace(/\s*$/,"");return $(r,`${s}
5
+ - ${t}
6
+ `,"utf-8"),!0}a(E,"addFact");const y=a(n=>({content:[{type:"text",text:n}]}),"textResult");function _(){let n=process.env.OPENADAPTER_BASE_URL||"",e=process.env.OPENADAPTER_API_KEY||"";if(!n||!e)try{const t=process.env.KODA_CODING_AGENT_DIR||u(b(),".koda","agent"),r=JSON.parse(d(u(t,"models.json"),"utf-8"));n=n||r?.providers?.openadapter?.baseUrl||"",e=e||r?.providers?.openadapter?.apiKey||""}catch{}return e?{root:(n||"https://api.openadapter.in/v1").replace(/\/v1\/?$/,"").replace(/\/$/,""),key:e}:null}a(_,"getGateway");const L=process.env.KODA_EMBED_MODEL||"bge-m3";function w(){const n=process.env.KODA_CODING_AGENT_DIR||u(b(),".koda","agent");return u(n,".koda-mem-emb.json")}a(w,"embedCachePath");function G(){try{return JSON.parse(d(w(),"utf-8"))}catch{return{}}}a(G,"readEmbedCache");function K(n){try{S(D(w()),{recursive:!0}),$(w(),JSON.stringify(n),"utf-8")}catch{}}a(K,"writeEmbedCache");async function T(n){const e=_();if(!e||n.length===0)return null;try{const t=await fetch(`${e.root}/v1/embeddings`,{method:"POST",headers:{Authorization:`Bearer ${e.key}`,"Content-Type":"application/json"},body:JSON.stringify({model:L,input:n})});if(!t.ok)return null;const o=((await t.json())?.data||[]).map(s=>s.embedding);return o.length===n.length?o:null}catch{return null}}a(T,"embed");function q(n,e){let t=0,r=0,o=0;for(let s=0;s<n.length;s++)t+=n[s]*e[s],r+=n[s]*n[s],o+=e[s]*e[s];return r&&o?t/(Math.sqrt(r)*Math.sqrt(o)):0}a(q,"cosine");async function U(n,e,t){if(e.length===0)return[];const r=G(),o=e.filter(i=>!r[i]);if(o.length){const i=await T(o);if(!i)return null;o.forEach((c,f)=>{r[c]=i[f]}),K(r)}const s=await T([n]);return s?e.map(i=>({f:i,s:r[i]?q(s[0],r[i]):0})).sort((i,c)=>c.s-i.s).slice(0,t).map(i=>i.f):null}a(U,"semanticRank");function B(){try{const n=process.env.KODA_CODING_AGENT_DIR||u(b(),".koda","agent"),e=JSON.parse(d(u(n,"settings.json"),"utf-8"));if(e?.defaultModel)return e.defaultModel}catch{}return"GLM-4.7"}a(B,"getDefaultModel");function J(n){const e=[];for(const t of n.slice(-40)){const r=t?.message;if(!r||r.role!=="user"&&r.role!=="assistant")continue;let o="";typeof r.content=="string"?o=r.content:Array.isArray(r.content)&&(o=r.content.filter(s=>s?.type==="text").map(s=>s.text).join(" ")),o.trim()&&e.push(`${r.role}: ${o.trim()}`)}return e.join(`
7
+ `).slice(-6e3)}a(J,"extractConvoText");async function F(n){const e=_();if(!e||!n)return[];try{const t=await fetch(`${e.root}/v1/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${e.key}`,"Content-Type":"application/json"},body:JSON.stringify({model:B(),temperature:0,messages:[{role:"system",content:`From the conversation, extract up to 5 DURABLE facts worth remembering long-term about THIS project or the user's preferences \u2014 architecture, where key code lives, decisions made, conventions, non-obvious gotchas, stated preferences. Skip transient details and anything obvious from the code. Reply with ONLY a JSON array of short one-sentence strings, e.g. ["fact one", "fact two"]. If nothing is worth saving, reply [].`},{role:"user",content:n}]})});if(!t.ok)return[];let o=(await t.json())?.choices?.[0]?.message?.content??"";const s=o.match(/\[[\s\S]*\]/);s&&(o=s[0]);const i=JSON.parse(o);return Array.isArray(i)?i.filter(c=>typeof c=="string"&&c.trim()).map(c=>c.trim()).slice(0,5):[]}catch{return[]}}a(F,"extractFacts");class W{constructor(e,t,r,o){this.user=e;this.project=t;this.theme=r;this.onClose=o}user;project;theme;onClose;static{a(this,"MemoryViewComponent")}handleInput(e){(A(e,"escape")||A(e,"ctrl+c"))&&this.onClose()}render(e){const t=this.theme,r=[""],o=a(i=>v(t.fg("borderMuted","\u2500\u2500\u2500 ")+t.fg("accent",i)+t.fg("borderMuted"," "+"\u2500".repeat(Math.max(0,e-i.length-6))),e),"header"),s=a((i,c)=>{if(r.push(o(i)),c.length===0)r.push(v(` ${t.fg("dim","(empty)")}`,e));else for(const f of c)r.push(v(` ${t.fg("accent","\u2022")} ${t.fg("text",f)}`,e));r.push("")},"section");return s(" User memory ",this.user),s(" Project memory ",this.project),r.push(v(` ${t.fg("dim","Press Escape to close")}`,e)),r}invalidate(){}}function H(n){n.registerTool({name:"remember",label:"Remember",description:"Save an important fact to long-term memory so it's available in future sessions. Use scope 'user' for the user's preferences/taste (cross-project) and 'project' for facts about THIS project (architecture, file layout, key decisions, gotchas). Keep each fact one concise sentence.",promptSnippet:"remember: save an important fact to long-term memory (user or project scope)",promptGuidelines:["When you learn a durable, reusable fact \u2014 a user preference, a project convention, where key code lives, a non-obvious gotcha \u2014 save it with the remember tool (project scope for project facts, user scope for preferences). Don't save transient details or anything already obvious from the code."],parameters:l.Object({fact:l.String({description:"The fact to remember, as one concise sentence"}),scope:l.Union([l.Literal("user"),l.Literal("project")],{description:"'user' for cross-project preferences, 'project' for facts about this project"})}),async execute(e,t){const r=E(t.scope,t.fact);return y(r?`Remembered (${t.scope}): ${t.fact.trim()}`:`Already known (${t.scope}) \u2014 not duplicated.`)},renderCall(e,t){return new R(t.fg("toolTitle",t.bold("remember "))+t.fg("muted",`(${e.scope}) `)+t.fg("dim",String(e.fact).slice(0,80)),0,0)}}),n.registerTool({name:"recall",label:"Recall",description:"Look up facts from long-term memory by meaning. Give a query to semantically retrieve the most relevant facts (embeddings; falls back to substring). Omit the query to list everything. Memory also auto-loads at session start \u2014 use this for an explicit lookup mid-task.",promptSnippet:"recall: semantically look up facts from long-term memory",parameters:l.Object({query:l.Optional(l.String({description:"What to recall; semantic match. Omit to list all."})),limit:l.Optional(l.Number({description:"Max facts to return (default 8)"}))}),async execute(e,t){const r=g(j()),o=g(k());if(r.length===0&&o.length===0)return y("Memory is empty.");const s=a((m,h)=>h.length?`${m}:
8
+ ${h.map(M=>`- ${M}`).join(`
9
+ `)}`:"","fmt");if(!t.query)return y([s("User memory",r),s("Project memory",o)].filter(Boolean).join(`
10
+
11
+ `));const i=t.limit??8,c=new Map;for(const m of r)c.set(m,"user");for(const m of o)c.has(m)||c.set(m,"project");const f=[...c.keys()];let p=await U(t.query,f,i);if(!p){const m=t.query.toLowerCase();p=f.filter(h=>h.toLowerCase().includes(m)).slice(0,i)}if(p.length===0)return y(`No memory matches "${t.query}".`);const P=p.filter(m=>c.get(m)==="user"),N=p.filter(m=>c.get(m)==="project");return y([s("User memory",P),s("Project memory",N)].filter(Boolean).join(`
12
+
13
+ `))}}),n.on("session_before_compact",async(e,t)=>{try{if(t.mode!=="tui")return;const r=J(e.branchEntries||[]),o=await F(r);if(o.length===0||!await t.ui.confirm("Save to memory before compacting?",`Koda is about to compact this conversation. Save these facts to project memory so they're not lost?
14
+
15
+ ${o.map(c=>`\u2022 ${c}`).join(`
16
+ `)}`))return;let i=0;for(const c of o)E("project",c)&&i++;t.ui.notify(`Saved ${i} fact(s) to project memory.`,"info")}catch{}}),n.registerCommand("memory",{description:"Show what Koda remembers (user + project)",handler:a(async(e,t)=>{const r=g(j()),o=g(k());if(t.mode!=="tui"){t.ui.notify(`User: ${r.length} fact(s), Project: ${o.length} fact(s)`,"info");return}await t.ui.custom((s,i,c,f)=>new W(r,o,i,()=>f()))},"handler")})}a(H,"default");export{H as default};
@@ -0,0 +1 @@
1
+ var y=Object.defineProperty;var o=(n,e)=>y(n,"name",{value:e,configurable:!0});import{readFileSync as _}from"node:fs";import{homedir as b}from"node:os";import{join as f}from"node:path";import{matchesKey as l,truncateToWidth as u}from"@openadapter/koda-tui";function v(){let n=process.env.OPENADAPTER_BASE_URL||"",e=process.env.OPENADAPTER_API_KEY||"";if(!n||!e)try{const t=process.env.KODA_CODING_AGENT_DIR||f(b(),".koda","agent"),r=JSON.parse(_(f(t,"models.json"),"utf-8"));n=n||r?.providers?.openadapter?.baseUrl||"",e=e||r?.providers?.openadapter?.apiKey||""}catch{}return e?{root:(n||"https://api.openadapter.in/v1").replace(/\/v1\/?$/,"").replace(/\/$/,""),key:e}:null}o(v,"getGateway");function k(n){const e=Math.max(0,Math.min(10,Math.round(n/10)));return"\u2588".repeat(e)+"\u2591".repeat(10-e)}o(k,"bar");function A(n){const e=new Date(n).getTime()-Date.now();if(!Number.isFinite(e)||e<=0)return"now";const t=Math.floor(e/6e4),r=Math.floor(t/60),s=Math.floor(r/24);return s>0?`${s}d ${r%24}h`:r>0?`${r}h ${t%60}m`:`${t}m`}o(A,"until");function p(n){return n>=1e6?`${(n/1e6).toFixed(2)}M`:n>=1e3?`${(n/1e3).toFixed(1)}k`:String(n)}o(p,"fmtN");class M{constructor(e,t,r){this.u=e;this.theme=t;this.onClose=r}u;theme;onClose;static{o(this,"StatusComponent")}cachedW;cachedL;handleInput(e){(l(e,"escape")||l(e,"ctrl+c"))&&this.onClose()}render(e){if(this.cachedL&&this.cachedW===e)return this.cachedL;const t=this.theme,r=[""],s=t.fg("borderMuted","\u2500\u2500\u2500 ")+t.fg("accent","Status")+t.fg("borderMuted"," "+"\u2500".repeat(Math.max(0,e-12)));r.push(u(s,e)),r.push(""),r.push(u(` ${t.fg("muted","Plan")} ${t.fg("text",this.u.plan_name||"\u2014")}`,e)),r.push("");const a=[["5h","5h"],["Week","week"],["Month","month"]];for(const[m,d]of a){const h=this.u.limits?.[d];if(!h)continue;const c=Math.round(h.percent),g=c>=90?"error":c>=70?"warning":"success",$=` ${t.fg("text",m.padEnd(6))}${t.fg(g,k(c))} ${t.fg("text",`${c}%`.padStart(4))} ${t.fg("dim",`${100-c}% left \xB7 resets in ${A(h.resets_at)}`)}`;r.push(u($,e))}r.push("");const i=` ${t.fg("muted","Today")} ${t.fg("text",`${p(this.u.requests_today||0)} requests \xB7 ${p(this.u.tokens_today||0)} tokens`)}`;return r.push(u(i,e)),this.u.credit_balance&&r.push(u(` ${t.fg("muted","Credits")} ${t.fg("text",String(this.u.credit_balance))}`,e)),r.push(""),r.push(u(` ${t.fg("dim","Press Escape to close")}`,e)),this.cachedW=e,this.cachedL=r,r}invalidate(){this.cachedW=void 0,this.cachedL=void 0}}function E(n){n.registerCommand("status",{description:"Show plan usage \u2014 5h / week / month limits, resets, and today's tokens",handler:o(async(e,t)=>{const r=v();if(!r){t.ui.notify("Status unavailable: no OpenAdapter key.","error");return}let s;try{const a=await fetch(`${r.root}/api/usage`,{headers:{Authorization:`Bearer ${r.key}`}});if(!a.ok){t.ui.notify(`Status: usage unavailable (HTTP ${a.status}).`,"error");return}s=await a.json()}catch(a){t.ui.notify(`Status failed: ${a.message}`,"error");return}if(t.mode!=="tui"){const a=o(i=>s.limits?.[i]?`${i} ${Math.round(s.limits[i].percent)}%`:"","f");t.ui.notify(`Plan ${s.plan_name||"?"} \xB7 ${["5h","week","month"].map(a).filter(Boolean).join(" \xB7 ")} \xB7 ${p(s.tokens_today||0)} tokens today`,"info");return}await t.ui.custom((a,i,m,d)=>new M(s,i,()=>d()))},"handler")})}o(E,"default");export{E as default};
@@ -0,0 +1,4 @@
1
+ var y=Object.defineProperty;var x=(u,t)=>y(u,"name",{value:t,configurable:!0});import{StringEnum as v}from"@openadapter/koda-ai";import{matchesKey as T,Text as l,truncateToWidth as p}from"@openadapter/koda-tui";import{Type as h}from"typebox";const b=h.Object({action:v(["list","add","toggle","clear"]),text:h.Optional(h.String({description:"Todo text (for add)"})),id:h.Optional(h.Number({description:"Todo ID (for toggle)"}))});class w{static{x(this,"TodoListComponent")}todos;theme;onClose;cachedWidth;cachedLines;constructor(t,n,i){this.todos=t,this.theme=n,this.onClose=i}handleInput(t){(T(t,"escape")||T(t,"ctrl+c"))&&this.onClose()}render(t){if(this.cachedLines&&this.cachedWidth===t)return this.cachedLines;const n=[],i=this.theme;n.push("");const d=i.fg("accent"," Todos "),e=i.fg("borderMuted","\u2500".repeat(3))+d+i.fg("borderMuted","\u2500".repeat(Math.max(0,t-10)));if(n.push(p(e,t)),n.push(""),this.todos.length===0)n.push(p(` ${i.fg("dim","No todos yet. Ask the agent to add some!")}`,t));else{const s=this.todos.filter(c=>c.done).length,a=this.todos.length;n.push(p(` ${i.fg("muted",`${s}/${a} completed`)}`,t)),n.push("");for(const c of this.todos){const o=c.done?i.fg("success","\u2713"):i.fg("dim","\u25CB"),r=i.fg("accent",`#${c.id}`),f=c.done?i.fg("dim",c.text):i.fg("text",c.text);n.push(p(` ${o} ${r} ${f}`,t))}}return n.push(""),n.push(p(` ${i.fg("dim","Press Escape to close")}`,t)),n.push(""),this.cachedWidth=t,this.cachedLines=n,n}invalidate(){this.cachedWidth=void 0,this.cachedLines=void 0}}function _(u){let t=[],n=1;const i=x(d=>{t=[],n=1;for(const e of d.sessionManager.getBranch()){if(e.type!=="message")continue;const s=e.message;if(s.role!=="toolResult"||s.toolName!=="todo")continue;const a=s.details;a&&(t=a.todos,n=a.nextId)}},"reconstructState");u.on("session_start",async(d,e)=>i(e)),u.on("session_tree",async(d,e)=>i(e)),u.registerTool({name:"todo",label:"Todo",description:"Track a plan as a todo list for multi-step work. Actions: add (text) one item per step, toggle (id) when a step is done, list to review, clear to reset. For any task with more than a couple of steps, add all steps up front, then toggle each as you complete it.",parameters:b,async execute(d,e,s,a,c){switch(e.action){case"list":return{content:[{type:"text",text:t.length?t.map(o=>`[${o.done?"x":" "}] #${o.id}: ${o.text}`).join(`
2
+ `):"No todos"}],details:{action:"list",todos:[...t],nextId:n}};case"add":{if(!e.text)return{content:[{type:"text",text:"Error: text required for add"}],details:{action:"add",todos:[...t],nextId:n,error:"text required"}};const o={id:n++,text:e.text,done:!1};return t.push(o),{content:[{type:"text",text:`Added todo #${o.id}: ${o.text}`}],details:{action:"add",todos:[...t],nextId:n}}}case"toggle":{if(e.id===void 0)return{content:[{type:"text",text:"Error: id required for toggle"}],details:{action:"toggle",todos:[...t],nextId:n,error:"id required"}};const o=t.find(r=>r.id===e.id);return o?(o.done=!o.done,{content:[{type:"text",text:`Todo #${o.id} ${o.done?"completed":"uncompleted"}`}],details:{action:"toggle",todos:[...t],nextId:n}}):{content:[{type:"text",text:`Todo #${e.id} not found`}],details:{action:"toggle",todos:[...t],nextId:n,error:`#${e.id} not found`}}}case"clear":{const o=t.length;return t=[],n=1,{content:[{type:"text",text:`Cleared ${o} todos`}],details:{action:"clear",todos:[],nextId:1}}}default:return{content:[{type:"text",text:`Unknown action: ${e.action}`}],details:{action:"list",todos:[...t],nextId:n,error:`unknown action: ${e.action}`}}}},renderCall(d,e,s){let a=e.fg("toolTitle",e.bold("todo "))+e.fg("muted",d.action);return d.text&&(a+=` ${e.fg("dim",`"${d.text}"`)}`),d.id!==void 0&&(a+=` ${e.fg("accent",`#${d.id}`)}`),new l(a,0,0)},renderResult(d,{expanded:e},s,a){const c=d.details;if(!c){const r=d.content[0];return new l(r?.type==="text"?r.text:"",0,0)}if(c.error)return new l(s.fg("error",`Error: ${c.error}`),0,0);const o=c.todos;switch(c.action){case"list":{if(o.length===0)return new l(s.fg("dim","No todos"),0,0);let r=s.fg("muted",`${o.length} todo(s):`);const f=e?o:o.slice(0,5);for(const g of f){const m=g.done?s.fg("success","\u2713"):s.fg("dim","\u25CB"),$=g.done?s.fg("dim",g.text):s.fg("muted",g.text);r+=`
3
+ ${m} ${s.fg("accent",`#${g.id}`)} ${$}`}return!e&&o.length>5&&(r+=`
4
+ ${s.fg("dim",`... ${o.length-5} more`)}`),new l(r,0,0)}case"add":{const r=o[o.length-1];return new l(s.fg("success","\u2713 Added ")+s.fg("accent",`#${r.id}`)+" "+s.fg("muted",r.text),0,0)}case"toggle":{const r=d.content[0],f=r?.type==="text"?r.text:"";return new l(s.fg("success","\u2713 ")+s.fg("muted",f),0,0)}case"clear":return new l(s.fg("success","\u2713 ")+s.fg("muted","Cleared all todos"),0,0)}}}),u.registerCommand("todos",{description:"Show all todos on the current branch",handler:x(async(d,e)=>{if(e.mode!=="tui"){e.ui.notify("/todos requires interactive mode","error");return}await e.ui.custom((s,a,c,o)=>new w(t,a,()=>o()))},"handler")})}x(_,"default");export{_ as default};
@@ -0,0 +1,4 @@
1
+ var x=Object.defineProperty;var c=(t,e)=>x(t,"name",{value:e,configurable:!0});import{createHash as E}from"node:crypto";import{readFileSync as A}from"node:fs";import{homedir as I}from"node:os";import{join as f}from"node:path";function T(){let t=process.env.OPENADAPTER_BASE_URL||"",e=process.env.OPENADAPTER_API_KEY||"";if(!t||!e)try{const n=process.env.KODA_CODING_AGENT_DIR||f(I(),".koda","agent"),i=JSON.parse(A(f(n,"models.json"),"utf-8"));t=t||i?.providers?.openadapter?.baseUrl||"",e=e||i?.providers?.openadapter?.apiKey||""}catch{}return e?{root:(t||"https://api.openadapter.in/v1").replace(/\/v1\/?$/,"").replace(/\/$/,""),key:e}:null}c(T,"getGateway");let g;async function p(t,e){if(g)return g;g=new Map;try{const n=await fetch(`${t}/v1/models`,{headers:{Authorization:`Bearer ${e}`}});if(n.ok){const i=await n.json();for(const o of i.data??[])g.set(o.id,{vision:!!o.supports_vision,tier:o.tier??""})}}catch{}return g}c(p,"loadModelInfo");const h=["freemium","lite","pro","max"];let m;async function b(t,e){if(process.env.KODA_VISION_MODEL)return process.env.KODA_VISION_MODEL;if(m!==void 0)return m;const i=[...(await p(t,e)).entries()].filter(([,o])=>o.vision);return i.sort(([,o],[,r])=>{const s=h.indexOf(o.tier),a=h.indexOf(r.tier);return(s===-1?99:s)-(a===-1?99:a)}),m=i[0]?.[0]??null,m}c(b,"pickExtractor");async function w(t,e){const n=e?.model?.id;if(n){const r=(await p(t.root,t.key)).get(n);if(r)return r.vision}const i=e?.model?.input;return Array.isArray(i)?i.includes("image"):!1}c(w,"activeModelSeesImages");const y=new Map;function _(t){const e=E("sha1");for(const n of t)e.update(n.mimeType).update(n.data.slice(0,4096)).update(String(n.data.length));return e.digest("hex")}c(_,"imagesKey");const $="You are a vision extractor for a coding agent. The downstream model is blind, so it relies entirely on your description. Transcribe ALL visible text VERBATIM (code, errors, logs, UI labels, URLs), and describe the image in full technical detail: layout/structure, diagrams and their connections, charts, tables, colors and states where meaningful. Be literal, thorough, and well-organized. No preamble, no opinions \u2014 just the faithful contents.";async function O(t,e,n,i){const o=[{type:"text",text:i?`The user's message: "${i}". Extract everything from the attached image(s) relevant to it.`:"Extract everything from the attached image(s)."},...n.map(d=>({type:"image_url",image_url:{url:`data:${d.mimeType};base64,${d.data}`}}))],r=await fetch(`${t.root}/v1/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${t.key}`,"Content-Type":"application/json"},body:JSON.stringify({model:e,temperature:0,messages:[{role:"system",content:$},{role:"user",content:o}]})});if(!r.ok)throw new Error(`vision model HTTP ${r.status}`);const s=await r.json(),a=(s?.choices?.[0]?.message?.content??s?.choices?.[0]?.message?.reasoning_content??"").trim();if(!a)throw new Error("vision model returned empty");return a}c(O,"extract");function S(t){t.on("input",async(e,n)=>{if(process.env.KODA_VISION_BRIDGE==="0")return{action:"continue"};const i=Array.isArray(e?.images)?e.images:[];if(i.length===0)return{action:"continue"};const o=T();if(!o)return{action:"continue"};if(await w(o,n))return{action:"continue"};const r=i.length,s=r>1?`${r} images`:"the image",a=await b(o.root,o.key);if(!a)return n?.ui?.notify?.(`This model can't see images and no vision model is available \u2014 ${s} may be ignored.`,"warning"),{action:"continue"};n?.ui?.notify?.(`This model can't see images \u2014 Koda is using a vision model to read ${s}, then continuing here.`,"warning");let d;const l=_(i),u=y.get(l);try{d=u??await O(o,a,i,String(e?.text??"")),u||y.set(l,d)}catch(v){return n?.ui?.notify?.(`Couldn't read ${s} (vision step failed: ${v.message}). Sending your text only.`,"error"),{action:"transform",text:String(e?.text??""),images:[]}}return{action:"transform",text:`${String(e?.text??"")}
2
+
3
+ [Koda vision bridge \u2014 you attached ${s}; the active model can't see, so a vision model read ${r>1?"them":"it"} for you:]
4
+ ${d}`,images:[]}})}c(S,"default");export{S as default};
@@ -0,0 +1,7 @@
1
+ var P=Object.defineProperty;var d=(o,r)=>P(o,"name",{value:r,configurable:!0});import{readFileSync as h}from"node:fs";import{basename as b}from"node:path";import{Text as m}from"@openadapter/koda-tui";import{Type as c}from"typebox";import{homedir as _}from"node:os";import{join as g}from"node:path";function y(){let o=process.env.OPENADAPTER_BASE_URL||"",r=process.env.OPENADAPTER_API_KEY||"";if(!o||!r)try{const a=process.env.KODA_CODING_AGENT_DIR||g(_(),".koda","agent"),n=JSON.parse(h(g(a,"models.json"),"utf-8"))?.providers?.openadapter;o=o||n?.baseUrl||"",r=r||n?.apiKey||""}catch{}return r?{root:(o||"https://api.openadapter.in/v1").replace(/\/v1\/?$/,"").replace(/\/$/,""),key:r}:{error:"No OpenAdapter API key found (set OPENADAPTER_API_KEY or run setup)."}}d(y,"getGateway");async function w(o,r,e){const a=y();if("error"in a)throw new Error(a.error);const t=await fetch(`${a.root}${o}`,{method:"POST",headers:{Authorization:`Bearer ${a.key}`,"Content-Type":"application/json"},body:JSON.stringify(r),signal:e}),n=await t.text();if(!t.ok)throw new Error(`HTTP ${t.status}: ${n.slice(0,200)}`);try{return JSON.parse(n)}catch{return{raw:n}}}d(w,"postJson");const i=d(o=>({content:[{type:"text",text:o}]}),"textResult");function S(o){o.registerTool({name:"search",label:"Web search",description:"Search the web for current information \u2014 docs, error messages, library APIs, best practices. Returns the top results (title, URL, snippet).",promptSnippet:"search: search the web and get the top results",parameters:c.Object({query:c.String({description:"The search query"}),num_results:c.Optional(c.Number({description:"How many results (default 5)"}))}),async execute(r,e,a){try{const t=await w("/v1/tools/search",{query:e.query,num_results:e.num_results??5},a),n=Array.isArray(t?.results)?t.results:[];if(n.length===0)return i(`No results for "${e.query}".`);const p=n.map((s,l)=>{const f=s.title||s.name||s.url||`result ${l+1}`,u=s.url||s.link||"",T=s.snippet||s.description||s.content||"";return`${l+1}. ${f}
2
+ ${u}
3
+ ${String(T).slice(0,300)}`}).join(`
4
+
5
+ `);return i(p)}catch(t){return i(`search failed: ${t.message}`)}},renderCall(r,e){return new m(e.fg("toolTitle",e.bold("search "))+e.fg("muted",String(r.query)),0,0)}}),o.registerTool({name:"fetch_url",label:"Fetch URL",description:"Fetch a web page and return its main content as clean markdown. Use after search to read a specific page (docs, API reference, an article).",promptSnippet:"fetch_url: read a web page as markdown",parameters:c.Object({url:c.String({description:"The URL to fetch"})}),async execute(r,e,a){try{const t=await w("/v1/tools/scrape/markdown",{url:e.url},a),n=t?.markdown??t?.content??t?.raw??"";if(!n)return i(`No content extracted from ${e.url}.`);const p=t?.title?`# ${t.title}
6
+
7
+ `:"";return i(p+String(n))}catch(t){return i(`fetch_url failed: ${t.message}`)}},renderCall(r,e){return new m(e.fg("toolTitle",e.bold("fetch_url "))+e.fg("muted",String(r.url)),0,0)}}),o.registerTool({name:"doc_parse",label:"Parse document",description:"Parse a local document (PDF, DOCX, PPTX, XLSX, or HTML) into text/markdown so you can read it. Give the file path.",promptSnippet:"doc_parse: extract text from a local PDF/DOCX/PPTX/XLSX/HTML file",parameters:c.Object({path:c.String({description:"Path to the local document file"})}),async execute(r,e,a){try{const t=y();if("error"in t)return i(`doc_parse failed: ${t.error}`);const n=h(e.path),p=new FormData;p.append("file",new Blob([n]),b(e.path));const s=await fetch(`${t.root}/v1/edge/docparse`,{method:"POST",headers:{Authorization:`Bearer ${t.key}`},body:p,signal:a}),l=await s.text();if(!s.ok)return i(`doc_parse failed: HTTP ${s.status}: ${l.slice(0,200)}`);let f=l;try{const u=JSON.parse(l);f=u.markdown??u.text??u.content??l}catch{}return i(String(f))}catch(t){return i(`doc_parse failed: ${t.message}`)}},renderCall(r,e){return new m(e.fg("toolTitle",e.bold("doc_parse "))+e.fg("muted",String(r.path)),0,0)}})}d(S,"default");export{S as default};
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+ // Koda first-run setup (Phase 1 — paste key).
3
+ //
4
+ // Paste an OpenAdapter API key; Koda fetches the live model list from the
5
+ // gateway, registers OpenAdapter as the default provider, and picks a sensible
6
+ // default coding model — so `koda` just works afterward.
7
+ //
8
+ // node scripts/setup-openadapter.mjs <sk-cv-key>
9
+ // OPENADAPTER_API_KEY=sk-cv-... node scripts/setup-openadapter.mjs
10
+ // node scripts/setup-openadapter.mjs # prompts for the key
11
+
12
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync, readdirSync } from "node:fs";
13
+ import { dirname, join } from "node:path";
14
+ import { homedir } from "node:os";
15
+ import { fileURLToPath } from "node:url";
16
+ import { createInterface } from "node:readline/promises";
17
+
18
+ const BASE_URL = (process.env.OPENADAPTER_BASE_URL || "https://api.openadapter.in/v1").replace(/\/$/, "");
19
+ // Honors KODA_CODING_AGENT_DIR (same as the CLI); defaults to ~/.koda/agent
20
+ const AGENT_DIR = process.env.KODA_CODING_AGENT_DIR || join(homedir(), ".koda", "agent");
21
+
22
+ // Default-model preference (first match wins). glm-5.1 leads: it cleanly builds
23
+ // complex projects from scratch (4-step full pass on the spreadsheet-engine eval)
24
+ // where the DeepSeek reasoners "think-to-a-stop". Exact-id match wins over a
25
+ // substring so we pick the direct `glm-5.1` alias, not 0G-/TEE- variants.
26
+ const PREFER = ["glm-5.1", "kimi-k2.6", "kimi-k2.5", "glm-5", "deepseek-v4", "kimi", "glm", "deepseek", "qwen"];
27
+
28
+ async function getKey() {
29
+ const arg = process.argv[2];
30
+ if (arg && arg.startsWith("sk-")) return arg.trim();
31
+ if (process.env.OPENADAPTER_API_KEY) return process.env.OPENADAPTER_API_KEY.trim();
32
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
33
+ const k = (await rl.question("Paste your OpenAdapter API key (sk-cv-...): ")).trim();
34
+ rl.close();
35
+ return k;
36
+ }
37
+
38
+ function pickDefault(ids) {
39
+ const low = ids.map((id) => [id, id.toLowerCase()]);
40
+ for (const p of PREFER) {
41
+ const exact = low.find(([, l]) => l === p);
42
+ if (exact) return exact[0];
43
+ const partial = low.find(([, l]) => l.includes(p));
44
+ if (partial) return partial[0];
45
+ }
46
+ return ids[0];
47
+ }
48
+
49
+ function mergeJson(path, mutate) {
50
+ let obj = {};
51
+ if (existsSync(path)) {
52
+ try { obj = JSON.parse(readFileSync(path, "utf-8")); } catch { /* start fresh on corrupt */ }
53
+ }
54
+ mutate(obj);
55
+ writeFileSync(path, JSON.stringify(obj, null, 2) + "\n", "utf-8");
56
+ }
57
+
58
+ const key = await getKey();
59
+ if (!key || !key.startsWith("sk-")) {
60
+ console.error("✗ No valid OpenAdapter key (expected sk-cv-...).");
61
+ process.exit(1);
62
+ }
63
+
64
+ console.log(`→ Fetching models from ${BASE_URL}/models …`);
65
+ let res;
66
+ try {
67
+ res = await fetch(`${BASE_URL}/models`, { headers: { Authorization: `Bearer ${key}` } });
68
+ } catch (e) {
69
+ console.error(`✗ Network error: ${e.message}`);
70
+ process.exit(1);
71
+ }
72
+ if (!res.ok) {
73
+ console.error(`✗ ${res.status} ${res.statusText}: ${(await res.text()).slice(0, 200)}`);
74
+ process.exit(1);
75
+ }
76
+
77
+ const body = await res.json();
78
+ const all = Array.isArray(body.data) ? body.data : [];
79
+ // Koda is a coding (chat) agent — keep chat models, drop image/audio/embedding endpoints.
80
+ const chat = all.filter((m) => (m.endpoint_format || m.model_type) === "chat");
81
+ if (chat.length === 0) {
82
+ console.error("✗ No chat models returned from the gateway.");
83
+ process.exit(1);
84
+ }
85
+
86
+ const models = chat.map((m) => ({
87
+ id: m.id,
88
+ name: m.id,
89
+ input: m.supports_vision ? ["text", "image"] : ["text"],
90
+ // Generous output budget so the agent can think AND write large files in one
91
+ // turn. Without this, requests defaulted to ~4096 and big writes truncated
92
+ // (stop=length) — the model burned the budget thinking and never wrote.
93
+ // The gateway clamps this down per-provider where needed.
94
+ maxTokens: 16000,
95
+ }));
96
+ const defaultModel = pickDefault(chat.map((m) => m.id));
97
+
98
+ mkdirSync(AGENT_DIR, { recursive: true });
99
+
100
+ mergeJson(join(AGENT_DIR, "models.json"), (cfg) => {
101
+ cfg.providers = cfg.providers || {};
102
+ cfg.providers.openadapter = {
103
+ name: "OpenAdapter",
104
+ baseUrl: BASE_URL,
105
+ apiKey: key,
106
+ api: "openai-completions",
107
+ models,
108
+ };
109
+ });
110
+
111
+ // Auto-load Koda's extensions — absolute paths so they resolve regardless of
112
+ // where `koda` is invoked from:
113
+ // koda-commands.mjs — /undo, /init, /zenitsu, /plan, /build, /help
114
+ // koda-todo.ts — the `todo` planning tool + /todos viewer
115
+ // koda-web.ts — search, fetch_url, doc_parse tools
116
+ // Resolve the extensions dir for both layouts: bundled in the npm package
117
+ // (<pkg>/openadapter/{setup.mjs,extensions,skills}) → "./extensions"; or the
118
+ // repo (scripts/setup-openadapter.mjs + ../extensions) → "../extensions".
119
+ const here = dirname(fileURLToPath(import.meta.url));
120
+ const extDir = existsSync(join(here, "extensions")) ? join(here, "extensions") : join(here, "..", "extensions");
121
+ // Register each extension by whichever file actually ships: published builds bundle
122
+ // minified .js (closed-source), the dev repo has .ts source. Pick the existing one.
123
+ const kodaExtensionBases = [
124
+ "koda-commands",
125
+ "koda-todo",
126
+ "koda-web",
127
+ "koda-memory",
128
+ "koda-help",
129
+ "koda-status",
130
+ "koda-bg",
131
+ "koda-ask",
132
+ "koda-vision",
133
+ ];
134
+ const kodaExtensions = kodaExtensionBases
135
+ .map((base) => {
136
+ for (const ext of [".js", ".mjs", ".ts"]) {
137
+ const p = join(extDir, base + ext);
138
+ if (existsSync(p)) return p;
139
+ }
140
+ return null;
141
+ })
142
+ .filter(Boolean);
143
+ mergeJson(join(AGENT_DIR, "settings.json"), (s) => {
144
+ s.defaultProvider = "openadapter";
145
+ s.defaultModel = defaultModel;
146
+ const exts = new Set([...(Array.isArray(s.extensions) ? s.extensions : []), ...kodaExtensions]);
147
+ s.extensions = [...exts];
148
+ });
149
+
150
+ // Install Koda's built-in skills (frontend, debugging, code-review) so they're
151
+ // available out of the box. Each is a SKILL.md dir auto-discovered from ~/.koda/agent/skills.
152
+ const skillsSrc = join(extDir, "..", "skills");
153
+ const skillsDst = join(AGENT_DIR, "skills");
154
+ try {
155
+ if (existsSync(skillsSrc)) {
156
+ let n = 0;
157
+ for (const name of readdirSync(skillsSrc)) {
158
+ const s = join(skillsSrc, name);
159
+ if (existsSync(join(s, "SKILL.md"))) {
160
+ cpSync(s, join(skillsDst, name), { recursive: true });
161
+ n++;
162
+ }
163
+ }
164
+ if (n) console.log(`✓ Installed ${n} built-in skill(s) → ${skillsDst}`);
165
+ }
166
+ } catch (e) {
167
+ console.error(`(built-in skills skipped: ${e.message})`);
168
+ }
169
+
170
+ console.log(`✓ Registered ${models.length} models from OpenAdapter`);
171
+ console.log(`✓ Default model: openadapter/${defaultModel}`);
172
+ console.log(`✓ Wrote ${join(AGENT_DIR, "models.json")} and settings.json`);
173
+ console.log(`\nDone — run \`koda\` to start.`);
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: code-review
3
+ description: Use when reviewing code or a diff — checking changes for correctness, security, error handling, tests, and consistency before they land.
4
+ ---
5
+
6
+ # Code review
7
+
8
+ Review against what the change is trying to do. Be specific and grounded in the actual code.
9
+
10
+ ## What to check, in priority order
11
+ 1. **Correctness** — does it do what it claims? Walk the logic. Hunt edge cases: empty/null, boundaries, concurrency, large inputs, error paths.
12
+ 2. **Security** — untrusted input validated? No injection (SQL/shell/path), no secrets in code or logs, authz checks present, safe defaults.
13
+ 3. **Error handling** — failures handled, not swallowed; no bare catches that hide problems; resources cleaned up.
14
+ 4. **Tests** — is the new behavior covered? Do existing tests still pass? Are the tests meaningful (not asserting trivia)?
15
+ 5. **Consistency** — matches the codebase's existing patterns, naming, and style. No unused code, no stray debug logs, no commented-out blocks.
16
+ 6. **Clarity** — would the next person understand it? Names say what they mean; non-obvious bits explained.
17
+
18
+ ## How to deliver the review
19
+ - Cite exact `file:line` for each point so it's actionable.
20
+ - **Separate must-fix from nits.** Lead with the things that are actually wrong (bugs, security, broken tests); mark style/preference items clearly as optional.
21
+ - Ground every claim in the code you read — don't speculate about code you haven't opened. If you're unsure something is a real problem, say so rather than asserting it.
22
+ - Acknowledge what's good, briefly. The goal is a better change landing, not a longer list.
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: debugging
3
+ description: Use when something is broken, failing, throwing an error, or behaving unexpectedly — to find and fix the root cause instead of guessing.
4
+ ---
5
+
6
+ # Debugging
7
+
8
+ Don't guess and patch. Find the actual cause, then fix it.
9
+
10
+ ## 1. Reproduce it first
11
+ - Get a reliable, minimal way to trigger the bug before changing anything. If you can't reproduce it, you can't confirm a fix.
12
+ - Capture the exact error message and full stack trace — read them carefully; they usually name the file and line.
13
+
14
+ ## 2. Locate, with evidence
15
+ - grep/read the actual code at the failing site and the values flowing into it. Don't theorize about code you haven't read.
16
+ - Narrow it down: bisect (which change/commit introduced it?), add targeted logging, or build the smallest input that still fails.
17
+ - Form ONE hypothesis at a time and test it. State what you expect vs. what you observe.
18
+
19
+ ## 3. Fix the root cause
20
+ - Fix the underlying cause, not the symptom. A try/catch that swallows the error, or a special-case that hides it, is not a fix.
21
+ - Make the smallest change that addresses the cause; don't refactor unrelated code while debugging.
22
+
23
+ ## 4. Verify
24
+ - Re-run the original reproduction — confirm it's actually gone.
25
+ - Run the project's tests/build to make sure you didn't break something else. Never declare it fixed without re-running and seeing it pass.
26
+ - If there were no tests covering this, consider adding one so it can't regress.
27
+
28
+ Read before you reason; reproduce before you fix; verify before you claim done.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: frontend
3
+ description: Use when building, changing, or styling a user interface — components, layout, responsive design, accessibility, and making it look polished. For web/React/Vue/Svelte/plain HTML UI work.
4
+ ---
5
+
6
+ # Frontend
7
+
8
+ Build UI that fits the project and looks intentional. Work in this order.
9
+
10
+ ## 1. Fit the existing project — don't reinvent
11
+ - Detect the stack first: framework (React/Vue/Svelte/plain), styling (Tailwind / CSS modules / styled-components / plain CSS), component library (if any), router, state approach. Read a couple of existing components before writing new ones.
12
+ - **Reuse what's there.** Use the project's existing components, design tokens, spacing scale, and color variables. Never introduce a new UI library or CSS framework unless asked.
13
+ - Match file layout, naming, and import conventions exactly.
14
+
15
+ ## 2. Structure
16
+ - Small, composable components; one responsibility each. Lift state only as far as it needs to go.
17
+ - Keep data-fetching/logic out of presentational components where the codebase already separates them.
18
+ - Type props (TS) or document them; handle the loading, empty, and error states, not just the happy path.
19
+
20
+ ## 3. Make it look good (this is the point)
21
+ - **Hierarchy & spacing:** consistent spacing scale, clear visual hierarchy, generous whitespace. Align things to a grid; don't eyeball one-off margins.
22
+ - **Typography:** limited type sizes/weights, readable line-length and line-height.
23
+ - **Color:** use the existing palette/tokens; enough contrast; a single accent used consistently.
24
+ - **Interactive states:** every interactive element needs hover, focus, active, disabled, and loading states. Don't ship a button with no feedback.
25
+ - **Responsive:** design mobile-first; verify it doesn't break at narrow and wide widths.
26
+ - **Motion:** subtle, fast transitions (≤200ms); never gratuitous.
27
+
28
+ ## 4. Accessibility (not optional)
29
+ - Semantic HTML (`button`, `nav`, `label`, headings in order) before ARIA. Reach for a `div` last.
30
+ - Labels tied to inputs; alt text on meaningful images; visible focus rings; keyboard-operable.
31
+ - Don't rely on color alone to convey meaning.
32
+
33
+ ## 5. Verify it actually renders
34
+ - Start the dev server with **run_background** (not bash — it won't return), then check **bg_output** to confirm it came up and there are no build errors.
35
+ - Open the page (e.g. `open http://localhost:5173`) when showing the user, and sanity-check the states you built.
36
+ - Never claim a UI works without seeing it build/run clean.
37
+
38
+ Default to doing over describing: read the real components, match them, and verify in the browser.
package/package.json ADDED
@@ -0,0 +1,108 @@
1
+ {
2
+ "name": "@openadapter/koda",
3
+ "version": "1.0.0-beta.3",
4
+ "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
+ "type": "module",
6
+ "piConfig": {
7
+ "name": "koda",
8
+ "configDir": ".koda"
9
+ },
10
+ "bin": {
11
+ "koda": "dist/cli.js"
12
+ },
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ },
20
+ "./hooks": {
21
+ "types": "./dist/core/hooks/index.d.ts",
22
+ "import": "./dist/core/hooks/index.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "docs",
28
+ "openadapter",
29
+ "CHANGELOG.md",
30
+ "npm-shrinkwrap.json"
31
+ ],
32
+ "scripts": {
33
+ "clean": "shx rm -rf dist openadapter",
34
+ "build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js && npm run copy-assets && npm run copy-openadapter",
35
+ "copy-openadapter": "shx rm -rf openadapter && shx mkdir -p openadapter && shx cp -r ../../extensions openadapter/extensions && shx cp -r ../../skills openadapter/skills && shx cp ../../scripts/setup-openadapter.mjs openadapter/setup.mjs",
36
+ "build:binary": "npm --prefix ../tui run build && npm --prefix ../ai run build && npm --prefix ../agent run build && npm run build && bun build --compile ./dist/bun/cli.js ./src/utils/image-resize-worker.ts --outfile dist/koda && npm run copy-binary-assets",
37
+ "copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/modes/interactive/assets && shx cp src/modes/interactive/assets/*.png dist/modes/interactive/assets/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/",
38
+ "copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/assets && shx cp src/modes/interactive/assets/*.png dist/assets/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/",
39
+ "test": "vitest --run",
40
+ "shrinkwrap": "node ../../scripts/generate-coding-agent-shrinkwrap.mjs",
41
+ "prepublishOnly": "npm run clean && npm run build && npm run shrinkwrap",
42
+ "postbuild": "node ../../scripts/minify-pkg-dist.mjs"
43
+ },
44
+ "dependencies": {
45
+ "@openadapter/koda-agent-core": "1.0.0-beta.3",
46
+ "@openadapter/koda-ai": "1.0.0-beta.3",
47
+ "@openadapter/koda-tui": "1.0.0-beta.3",
48
+ "@silvia-odwyer/photon-node": "0.3.4",
49
+ "chalk": "5.6.2",
50
+ "cross-spawn": "7.0.6",
51
+ "diff": "8.0.4",
52
+ "glob": "13.0.6",
53
+ "highlight.js": "10.7.3",
54
+ "hosted-git-info": "9.0.3",
55
+ "ignore": "7.0.5",
56
+ "jiti": "2.7.0",
57
+ "minimatch": "10.2.5",
58
+ "proper-lockfile": "4.1.2",
59
+ "typebox": "1.1.38",
60
+ "undici": "8.3.0",
61
+ "yaml": "2.9.0"
62
+ },
63
+ "overrides": {
64
+ "rimraf": "6.1.2",
65
+ "gaxios": {
66
+ "rimraf": "6.1.2"
67
+ }
68
+ },
69
+ "optionalDependencies": {
70
+ "@mariozechner/clipboard": "0.3.9"
71
+ },
72
+ "devDependencies": {
73
+ "@types/cross-spawn": "6.0.6",
74
+ "@types/diff": "7.0.2",
75
+ "@types/hosted-git-info": "3.0.5",
76
+ "@types/ms": "2.1.0",
77
+ "@types/node": "24.12.4",
78
+ "@types/proper-lockfile": "4.1.4",
79
+ "shx": "0.4.0",
80
+ "typescript": "5.9.3",
81
+ "vitest": "3.2.4"
82
+ },
83
+ "keywords": [
84
+ "coding-agent",
85
+ "ai",
86
+ "llm",
87
+ "cli",
88
+ "tui",
89
+ "agent"
90
+ ],
91
+ "author": "OpenAdapter",
92
+ "license": "MIT",
93
+ "repository": {
94
+ "type": "git",
95
+ "url": "git+https://github.com/claraverse-space/koda.git",
96
+ "directory": "packages/coding-agent"
97
+ },
98
+ "engines": {
99
+ "node": ">=22.19.0"
100
+ },
101
+ "homepage": "https://openadapter.in",
102
+ "bugs": {
103
+ "url": "https://github.com/claraverse-space/koda/issues"
104
+ },
105
+ "publishConfig": {
106
+ "access": "public"
107
+ }
108
+ }