@asterxsk/kiln 0.2.0 → 0.3.0

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 (303) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +3 -18
  3. package/agent/extensions/AGENTS.md +68 -68
  4. package/agent/extensions/ask-user/install.ps1 +4 -0
  5. package/agent/extensions/ask-user/install.sh +3 -0
  6. package/agent/extensions/background-terminals/install.ps1 +4 -0
  7. package/agent/extensions/background-terminals/install.sh +3 -0
  8. package/agent/extensions/destructive/install.ps1 +4 -0
  9. package/agent/extensions/destructive/install.sh +3 -0
  10. package/agent/extensions/file-search/install.ps1 +4 -0
  11. package/agent/extensions/file-search/install.sh +3 -0
  12. package/agent/extensions/goal/install.ps1 +4 -0
  13. package/agent/extensions/goal/install.sh +3 -0
  14. package/agent/extensions/herdr-agent-state.ts +257 -0
  15. package/agent/extensions/kiln-update/README.md +25 -0
  16. package/agent/extensions/kiln-update/index.ts +99 -0
  17. package/agent/extensions/kiln-update/install.ps1 +27 -0
  18. package/agent/extensions/kiln-update/install.sh +24 -0
  19. package/agent/extensions/modelconf/install.ps1 +4 -0
  20. package/agent/extensions/modelconf/install.sh +3 -0
  21. package/agent/extensions/shared/install.ps1 +4 -0
  22. package/agent/extensions/shared/install.sh +3 -0
  23. package/agent/extensions/skillsconf/install.ps1 +4 -0
  24. package/agent/extensions/skillsconf/install.sh +3 -0
  25. package/agent/extensions/statusline/install.ps1 +4 -0
  26. package/agent/extensions/statusline/install.sh +3 -0
  27. package/agent/extensions/subagents/install.ps1 +4 -0
  28. package/agent/extensions/subagents/install.sh +3 -0
  29. package/agent/extensions/taste/index.ts +443 -0
  30. package/agent/extensions/taste/install.ps1 +27 -0
  31. package/agent/extensions/taste/install.sh +24 -0
  32. package/agent/extensions/todo/install.ps1 +4 -0
  33. package/agent/extensions/todo/install.sh +3 -0
  34. package/agent/extensions/trim-context/install.ps1 +4 -0
  35. package/agent/extensions/trim-context/install.sh +3 -0
  36. package/agent/version.txt +1 -0
  37. package/agent.bak.20260905-190020/AGENTS.md +67 -0
  38. package/agent.bak.20260905-190020/README.md +5 -0
  39. package/agent.bak.20260905-190020/extensions/ask-user/index.ts +418 -0
  40. package/agent.bak.20260905-190020/extensions/ask-user/install.ps1 +23 -0
  41. package/agent.bak.20260905-190020/extensions/ask-user/install.sh +21 -0
  42. package/agent.bak.20260905-190020/extensions/ask-user/package-lock.json +769 -0
  43. package/agent.bak.20260905-190020/extensions/ask-user/package.json +19 -0
  44. package/agent.bak.20260905-190020/extensions/ask-user/prompt.ts +45 -0
  45. package/agent.bak.20260905-190020/extensions/ask-user/tsconfig.json +7 -0
  46. package/agent.bak.20260905-190020/extensions/background-terminals/docs/implementation-guide.md +942 -0
  47. package/agent.bak.20260905-190020/extensions/background-terminals/index.ts +629 -0
  48. package/agent.bak.20260905-190020/extensions/background-terminals/install.ps1 +23 -0
  49. package/agent.bak.20260905-190020/extensions/background-terminals/install.sh +21 -0
  50. package/agent.bak.20260905-190020/extensions/background-terminals/package-lock.json +769 -0
  51. package/agent.bak.20260905-190020/extensions/background-terminals/package.json +17 -0
  52. package/agent.bak.20260905-190020/extensions/background-terminals/src/domain.ts +87 -0
  53. package/agent.bak.20260905-190020/extensions/background-terminals/src/manager.ts +907 -0
  54. package/agent.bak.20260905-190020/extensions/background-terminals/src/output.ts +84 -0
  55. package/agent.bak.20260905-190020/extensions/background-terminals/src/prompt.ts +142 -0
  56. package/agent.bak.20260905-190020/extensions/background-terminals/src/result-delivery.ts +27 -0
  57. package/agent.bak.20260905-190020/extensions/background-terminals/src/runtime.ts +36 -0
  58. package/agent.bak.20260905-190020/extensions/background-terminals/src/ui/output-view.ts +79 -0
  59. package/agent.bak.20260905-190020/extensions/background-terminals/src/ui/ps.ts +621 -0
  60. package/agent.bak.20260905-190020/extensions/background-terminals/src/widget-order.ts +121 -0
  61. package/agent.bak.20260905-190020/extensions/background-terminals/test/manager.test.ts +735 -0
  62. package/agent.bak.20260905-190020/extensions/background-terminals/test/output.test.ts +109 -0
  63. package/agent.bak.20260905-190020/extensions/background-terminals/test/prompt.test.ts +125 -0
  64. package/agent.bak.20260905-190020/extensions/background-terminals/test/ps.test.ts +82 -0
  65. package/agent.bak.20260905-190020/extensions/background-terminals/test/result-delivery.test.ts +44 -0
  66. package/agent.bak.20260905-190020/extensions/background-terminals/tsconfig.json +7 -0
  67. package/agent.bak.20260905-190020/extensions/destructive/README.md +34 -0
  68. package/agent.bak.20260905-190020/extensions/destructive/index.ts +124 -0
  69. package/agent.bak.20260905-190020/extensions/destructive/install.ps1 +23 -0
  70. package/agent.bak.20260905-190020/extensions/destructive/install.sh +21 -0
  71. package/agent.bak.20260905-190020/extensions/file-search/index.ts +459 -0
  72. package/agent.bak.20260905-190020/extensions/file-search/install.ps1 +23 -0
  73. package/agent.bak.20260905-190020/extensions/file-search/install.sh +21 -0
  74. package/agent.bak.20260905-190020/extensions/file-search/package-lock.json +2253 -0
  75. package/agent.bak.20260905-190020/extensions/file-search/package.json +23 -0
  76. package/agent.bak.20260905-190020/extensions/file-search/src/args.ts +122 -0
  77. package/agent.bak.20260905-190020/extensions/file-search/src/binaries.ts +422 -0
  78. package/agent.bak.20260905-190020/extensions/file-search/src/output.ts +126 -0
  79. package/agent.bak.20260905-190020/extensions/file-search/src/process.ts +146 -0
  80. package/agent.bak.20260905-190020/extensions/file-search/src/prompt.ts +52 -0
  81. package/agent.bak.20260905-190020/extensions/file-search/test/index.spec.ts +443 -0
  82. package/agent.bak.20260905-190020/extensions/file-search/tsconfig.json +7 -0
  83. package/agent.bak.20260905-190020/extensions/goal/README.md +50 -0
  84. package/agent.bak.20260905-190020/extensions/goal/index.ts +155 -0
  85. package/agent.bak.20260905-190020/extensions/goal/install.ps1 +23 -0
  86. package/agent.bak.20260905-190020/extensions/goal/install.sh +21 -0
  87. package/agent.bak.20260905-190020/extensions/kiln-update/README.md +25 -0
  88. package/agent.bak.20260905-190020/extensions/kiln-update/index.ts +99 -0
  89. package/agent.bak.20260905-190020/extensions/kiln-update/install.ps1 +23 -0
  90. package/agent.bak.20260905-190020/extensions/kiln-update/install.sh +21 -0
  91. package/agent.bak.20260905-190020/extensions/modelconf/PLAN.md +915 -0
  92. package/agent.bak.20260905-190020/extensions/modelconf/README.md +66 -0
  93. package/agent.bak.20260905-190020/extensions/modelconf/index.ts +296 -0
  94. package/agent.bak.20260905-190020/extensions/modelconf/install.ps1 +23 -0
  95. package/agent.bak.20260905-190020/extensions/modelconf/install.sh +21 -0
  96. package/agent.bak.20260905-190020/extensions/modelconf/package-lock.json +1809 -0
  97. package/agent.bak.20260905-190020/extensions/modelconf/package.json +13 -0
  98. package/agent.bak.20260905-190020/extensions/modelconf/src/fuzzy.ts +26 -0
  99. package/agent.bak.20260905-190020/extensions/modelconf/src/glob.ts +13 -0
  100. package/agent.bak.20260905-190020/extensions/modelconf/src/persistence.test.ts +46 -0
  101. package/agent.bak.20260905-190020/extensions/modelconf/src/persistence.ts +164 -0
  102. package/agent.bak.20260905-190020/extensions/modelconf/src/ui/ModelConfView.test.ts +75 -0
  103. package/agent.bak.20260905-190020/extensions/modelconf/src/ui/ModelConfView.ts +1101 -0
  104. package/agent.bak.20260905-190020/extensions/modelconf/tsconfig.json +15 -0
  105. package/agent.bak.20260905-190020/extensions/pi-web-access/CHANGELOG.md +690 -0
  106. package/agent.bak.20260905-190020/extensions/pi-web-access/LICENSE +21 -0
  107. package/agent.bak.20260905-190020/extensions/pi-web-access/README.md +470 -0
  108. package/agent.bak.20260905-190020/extensions/pi-web-access/SECURITY.md +5 -0
  109. package/agent.bak.20260905-190020/extensions/pi-web-access/activity.ts +101 -0
  110. package/agent.bak.20260905-190020/extensions/pi-web-access/auth-fetch.ts +148 -0
  111. package/agent.bak.20260905-190020/extensions/pi-web-access/banner.png +0 -0
  112. package/agent.bak.20260905-190020/extensions/pi-web-access/brightdata-unlocker.ts +272 -0
  113. package/agent.bak.20260905-190020/extensions/pi-web-access/chrome-cookies.ts +669 -0
  114. package/agent.bak.20260905-190020/extensions/pi-web-access/content-find.ts +139 -0
  115. package/agent.bak.20260905-190020/extensions/pi-web-access/credential-source.ts +191 -0
  116. package/agent.bak.20260905-190020/extensions/pi-web-access/data-uri-sanitize.ts +406 -0
  117. package/agent.bak.20260905-190020/extensions/pi-web-access/datalab-pdf-extract.ts +568 -0
  118. package/agent.bak.20260905-190020/extensions/pi-web-access/declared-web-links.ts +173 -0
  119. package/agent.bak.20260905-190020/extensions/pi-web-access/evidence/CONTRACT-EVIDENCE.md +496 -0
  120. package/agent.bak.20260905-190020/extensions/pi-web-access/evidence/contract-probe.mjs +140 -0
  121. package/agent.bak.20260905-190020/extensions/pi-web-access/exa.ts +526 -0
  122. package/agent.bak.20260905-190020/extensions/pi-web-access/extract.ts +1196 -0
  123. package/agent.bak.20260905-190020/extensions/pi-web-access/feature-config.ts +29 -0
  124. package/agent.bak.20260905-190020/extensions/pi-web-access/fetch-params.ts +111 -0
  125. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-adc.ts +298 -0
  126. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-api.ts +353 -0
  127. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-pdf-extract.ts +108 -0
  128. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-search.ts +21 -0
  129. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-url-context.ts +128 -0
  130. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-web-config.ts +101 -0
  131. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-web.ts +487 -0
  132. package/agent.bak.20260905-190020/extensions/pi-web-access/github-api.ts +197 -0
  133. package/agent.bak.20260905-190020/extensions/pi-web-access/github-extract.ts +746 -0
  134. package/agent.bak.20260905-190020/extensions/pi-web-access/github-issue-pr.ts +700 -0
  135. package/agent.bak.20260905-190020/extensions/pi-web-access/index.ts +1737 -0
  136. package/agent.bak.20260905-190020/extensions/pi-web-access/package-lock.json +5808 -0
  137. package/agent.bak.20260905-190020/extensions/pi-web-access/package.json +64 -0
  138. package/agent.bak.20260905-190020/extensions/pi-web-access/page-query.ts +96 -0
  139. package/agent.bak.20260905-190020/extensions/pi-web-access/pdf-extract.ts +409 -0
  140. package/agent.bak.20260905-190020/extensions/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
  141. package/agent.bak.20260905-190020/extensions/pi-web-access/promise-try.d.ts +7 -0
  142. package/agent.bak.20260905-190020/extensions/pi-web-access/query-rewrite.ts +51 -0
  143. package/agent.bak.20260905-190020/extensions/pi-web-access/render-search-error.ts +170 -0
  144. package/agent.bak.20260905-190020/extensions/pi-web-access/rsc-extract.ts +338 -0
  145. package/agent.bak.20260905-190020/extensions/pi-web-access/search-types.ts +20 -0
  146. package/agent.bak.20260905-190020/extensions/pi-web-access/source-check.ts +282 -0
  147. package/agent.bak.20260905-190020/extensions/pi-web-access/ssrf-protection.ts +526 -0
  148. package/agent.bak.20260905-190020/extensions/pi-web-access/storage.ts +521 -0
  149. package/agent.bak.20260905-190020/extensions/pi-web-access/summary-model-scope.ts +125 -0
  150. package/agent.bak.20260905-190020/extensions/pi-web-access/test/auth-fetch.test.mjs +208 -0
  151. package/agent.bak.20260905-190020/extensions/pi-web-access/test/brightdata-unlocker.test.mjs +840 -0
  152. package/agent.bak.20260905-190020/extensions/pi-web-access/test/chrome-cookie-extraction.test.mjs +441 -0
  153. package/agent.bak.20260905-190020/extensions/pi-web-access/test/config-path.test.mjs +283 -0
  154. package/agent.bak.20260905-190020/extensions/pi-web-access/test/content-find.test.mjs +25 -0
  155. package/agent.bak.20260905-190020/extensions/pi-web-access/test/credential-source.test.mjs +118 -0
  156. package/agent.bak.20260905-190020/extensions/pi-web-access/test/data-uri-sanitize.test.mjs +210 -0
  157. package/agent.bak.20260905-190020/extensions/pi-web-access/test/datalab-pdf-extract.test.mjs +552 -0
  158. package/agent.bak.20260905-190020/extensions/pi-web-access/test/declared-web-links.test.mjs +212 -0
  159. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-answer-storage.test.mjs +40 -0
  160. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-cache-storage.test.mjs +334 -0
  161. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-content-domain-policy.test.mjs +95 -0
  162. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-modes.test.mjs +53 -0
  163. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-not-found-guidance.test.mjs +92 -0
  164. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-params.test.mjs +86 -0
  165. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-render-call.test.mjs +34 -0
  166. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-routing.test.mjs +173 -0
  167. package/agent.bak.20260905-190020/extensions/pi-web-access/test/gemini-adc-auth.test.mjs +257 -0
  168. package/agent.bak.20260905-190020/extensions/pi-web-access/test/gemini-api-transport.test.mjs +170 -0
  169. package/agent.bak.20260905-190020/extensions/pi-web-access/test/gemini-pdf-extract.test.mjs +133 -0
  170. package/agent.bak.20260905-190020/extensions/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +178 -0
  171. package/agent.bak.20260905-190020/extensions/pi-web-access/test/gemini-web-header-overflow.test.mjs +148 -0
  172. package/agent.bak.20260905-190020/extensions/pi-web-access/test/get-search-content.test.mjs +223 -0
  173. package/agent.bak.20260905-190020/extensions/pi-web-access/test/github-extract.test.mjs +378 -0
  174. package/agent.bak.20260905-190020/extensions/pi-web-access/test/github-issue-pr.test.mjs +565 -0
  175. package/agent.bak.20260905-190020/extensions/pi-web-access/test/inline-content-config.test.mjs +99 -0
  176. package/agent.bak.20260905-190020/extensions/pi-web-access/test/lazy-extract-load.test.mjs +118 -0
  177. package/agent.bak.20260905-190020/extensions/pi-web-access/test/local-video-oversize.test.mjs +52 -0
  178. package/agent.bak.20260905-190020/extensions/pi-web-access/test/package-typebox-dependency.test.mjs +50 -0
  179. package/agent.bak.20260905-190020/extensions/pi-web-access/test/page-query.test.mjs +51 -0
  180. package/agent.bak.20260905-190020/extensions/pi-web-access/test/pdf-config.test.mjs +140 -0
  181. package/agent.bak.20260905-190020/extensions/pi-web-access/test/pdf-extract.test.mjs +500 -0
  182. package/agent.bak.20260905-190020/extensions/pi-web-access/test/proxy-transport.test.mjs +286 -0
  183. package/agent.bak.20260905-190020/extensions/pi-web-access/test/query-rewrite.test.mjs +52 -0
  184. package/agent.bak.20260905-190020/extensions/pi-web-access/test/rsc-fallback.test.mjs +102 -0
  185. package/agent.bak.20260905-190020/extensions/pi-web-access/test/search-error-render.test.mjs +152 -0
  186. package/agent.bak.20260905-190020/extensions/pi-web-access/test/search-providers.test.mjs +274 -0
  187. package/agent.bak.20260905-190020/extensions/pi-web-access/test/source-check.test.mjs +179 -0
  188. package/agent.bak.20260905-190020/extensions/pi-web-access/test/ssrf-allow-ranges-config.test.mjs +205 -0
  189. package/agent.bak.20260905-190020/extensions/pi-web-access/test/ssrf-protection.test.mjs +456 -0
  190. package/agent.bak.20260905-190020/extensions/pi-web-access/test/summary-model-scope.test.mjs +106 -0
  191. package/agent.bak.20260905-190020/extensions/pi-web-access/test/tool-registration-config.test.mjs +182 -0
  192. package/agent.bak.20260905-190020/extensions/pi-web-access/test/web-search-answer-render.test.mjs +66 -0
  193. package/agent.bak.20260905-190020/extensions/pi-web-access/test/youtube-extract-errors.test.mjs +64 -0
  194. package/agent.bak.20260905-190020/extensions/pi-web-access/tsconfig.json +11 -0
  195. package/agent.bak.20260905-190020/extensions/pi-web-access/utils.ts +451 -0
  196. package/agent.bak.20260905-190020/extensions/pi-web-access/video-extract.ts +392 -0
  197. package/agent.bak.20260905-190020/extensions/pi-web-access/youtube-extract.ts +328 -0
  198. package/agent.bak.20260905-190020/extensions/shared/activity-status.ts +31 -0
  199. package/agent.bak.20260905-190020/extensions/shared/child-session.ts +148 -0
  200. package/agent.bak.20260905-190020/extensions/shared/context-utilization.ts +47 -0
  201. package/agent.bak.20260905-190020/extensions/shared/dashboard-state.ts +99 -0
  202. package/agent.bak.20260905-190020/extensions/shared/install.ps1 +23 -0
  203. package/agent.bak.20260905-190020/extensions/shared/install.sh +21 -0
  204. package/agent.bak.20260905-190020/extensions/shared/test/child-session.test.ts +270 -0
  205. package/agent.bak.20260905-190020/extensions/shared/test/context-utilization.test.ts +48 -0
  206. package/agent.bak.20260905-190020/extensions/shared/test/tool-call-timeout.test.ts +117 -0
  207. package/agent.bak.20260905-190020/extensions/shared/test/widget-order.test.ts +167 -0
  208. package/agent.bak.20260905-190020/extensions/shared/tool-call-timeout.ts +104 -0
  209. package/agent.bak.20260905-190020/extensions/skillsconf/README.md +74 -0
  210. package/agent.bak.20260905-190020/extensions/skillsconf/index.ts +92 -0
  211. package/agent.bak.20260905-190020/extensions/skillsconf/install.ps1 +23 -0
  212. package/agent.bak.20260905-190020/extensions/skillsconf/install.sh +21 -0
  213. package/agent.bak.20260905-190020/extensions/skillsconf/package-lock.json +1809 -0
  214. package/agent.bak.20260905-190020/extensions/skillsconf/package.json +18 -0
  215. package/agent.bak.20260905-190020/extensions/skillsconf/src/delete-skill.test.ts +64 -0
  216. package/agent.bak.20260905-190020/extensions/skillsconf/src/delete-skill.ts +45 -0
  217. package/agent.bak.20260905-190020/extensions/skillsconf/src/filter.test.ts +80 -0
  218. package/agent.bak.20260905-190020/extensions/skillsconf/src/filter.ts +74 -0
  219. package/agent.bak.20260905-190020/extensions/skillsconf/src/fuzzy.ts +26 -0
  220. package/agent.bak.20260905-190020/extensions/skillsconf/src/persistence.test.ts +72 -0
  221. package/agent.bak.20260905-190020/extensions/skillsconf/src/persistence.ts +169 -0
  222. package/agent.bak.20260905-190020/extensions/skillsconf/src/ui/SkillConfView.test.ts +337 -0
  223. package/agent.bak.20260905-190020/extensions/skillsconf/src/ui/SkillConfView.ts +758 -0
  224. package/agent.bak.20260905-190020/extensions/skillsconf/src/ui/text-input.ts +91 -0
  225. package/agent.bak.20260905-190020/extensions/skillsconf/src/ui/tui-helpers.ts +144 -0
  226. package/agent.bak.20260905-190020/extensions/skillsconf/tsconfig.json +15 -0
  227. package/agent.bak.20260905-190020/extensions/statusline/index.ts +282 -0
  228. package/agent.bak.20260905-190020/extensions/statusline/install.ps1 +23 -0
  229. package/agent.bak.20260905-190020/extensions/statusline/install.sh +21 -0
  230. package/agent.bak.20260905-190020/extensions/subagents/docs/design-plan.md +568 -0
  231. package/agent.bak.20260905-190020/extensions/subagents/docs/effect-v4-extension-guide.md +354 -0
  232. package/agent.bak.20260905-190020/extensions/subagents/docs/effect-v4-notes.md +571 -0
  233. package/agent.bak.20260905-190020/extensions/subagents/index.ts +826 -0
  234. package/agent.bak.20260905-190020/extensions/subagents/install.ps1 +23 -0
  235. package/agent.bak.20260905-190020/extensions/subagents/install.sh +21 -0
  236. package/agent.bak.20260905-190020/extensions/subagents/package-lock.json +2244 -0
  237. package/agent.bak.20260905-190020/extensions/subagents/package.json +19 -0
  238. package/agent.bak.20260905-190020/extensions/subagents/src/backend.ts +73 -0
  239. package/agent.bak.20260905-190020/extensions/subagents/src/backends/claude.ts +701 -0
  240. package/agent.bak.20260905-190020/extensions/subagents/src/backends/codex.ts +1060 -0
  241. package/agent.bak.20260905-190020/extensions/subagents/src/backends/pi.ts +575 -0
  242. package/agent.bak.20260905-190020/extensions/subagents/src/backends/stub.ts +300 -0
  243. package/agent.bak.20260905-190020/extensions/subagents/src/by-the-way.ts +21 -0
  244. package/agent.bak.20260905-190020/extensions/subagents/src/domain.ts +253 -0
  245. package/agent.bak.20260905-190020/extensions/subagents/src/format.ts +74 -0
  246. package/agent.bak.20260905-190020/extensions/subagents/src/manager.ts +736 -0
  247. package/agent.bak.20260905-190020/extensions/subagents/src/prompt.ts +92 -0
  248. package/agent.bak.20260905-190020/extensions/subagents/src/result-delivery.ts +20 -0
  249. package/agent.bak.20260905-190020/extensions/subagents/src/runtime.ts +53 -0
  250. package/agent.bak.20260905-190020/extensions/subagents/src/ui/takeover.ts +583 -0
  251. package/agent.bak.20260905-190020/extensions/subagents/src/ui/transcript.ts +201 -0
  252. package/agent.bak.20260905-190020/extensions/subagents/src/widget-order.ts +121 -0
  253. package/agent.bak.20260905-190020/extensions/subagents/test/by-the-way.test.ts +29 -0
  254. package/agent.bak.20260905-190020/extensions/subagents/test/claude.test.ts +119 -0
  255. package/agent.bak.20260905-190020/extensions/subagents/test/codex.test.ts +102 -0
  256. package/agent.bak.20260905-190020/extensions/subagents/test/context-usage.test.ts +107 -0
  257. package/agent.bak.20260905-190020/extensions/subagents/test/manager.test.ts +276 -0
  258. package/agent.bak.20260905-190020/extensions/subagents/test/result-delivery.test.ts +27 -0
  259. package/agent.bak.20260905-190020/extensions/subagents/test/takeover.test.ts +29 -0
  260. package/agent.bak.20260905-190020/extensions/subagents/tsconfig.json +7 -0
  261. package/agent.bak.20260905-190020/extensions/taste/index.ts +443 -0
  262. package/agent.bak.20260905-190020/extensions/taste/install.ps1 +23 -0
  263. package/agent.bak.20260905-190020/extensions/taste/install.sh +21 -0
  264. package/agent.bak.20260905-190020/extensions/todo/AGENTS.md +38 -0
  265. package/agent.bak.20260905-190020/extensions/todo/LICENSE +21 -0
  266. package/agent.bak.20260905-190020/extensions/todo/config.ts +55 -0
  267. package/agent.bak.20260905-190020/extensions/todo/index.ts +151 -0
  268. package/agent.bak.20260905-190020/extensions/todo/install.ps1 +23 -0
  269. package/agent.bak.20260905-190020/extensions/todo/install.sh +21 -0
  270. package/agent.bak.20260905-190020/extensions/todo/locales/de.json +17 -0
  271. package/agent.bak.20260905-190020/extensions/todo/locales/en.json +15 -0
  272. package/agent.bak.20260905-190020/extensions/todo/locales/es.json +17 -0
  273. package/agent.bak.20260905-190020/extensions/todo/locales/fr.json +17 -0
  274. package/agent.bak.20260905-190020/extensions/todo/locales/pt-BR.json +17 -0
  275. package/agent.bak.20260905-190020/extensions/todo/locales/pt.json +17 -0
  276. package/agent.bak.20260905-190020/extensions/todo/locales/ru.json +17 -0
  277. package/agent.bak.20260905-190020/extensions/todo/locales/uk.json +17 -0
  278. package/agent.bak.20260905-190020/extensions/todo/locales/zh.json +17 -0
  279. package/agent.bak.20260905-190020/extensions/todo/package-lock.json +3358 -0
  280. package/agent.bak.20260905-190020/extensions/todo/package.json +67 -0
  281. package/agent.bak.20260905-190020/extensions/todo/state/i18n-bridge.ts +64 -0
  282. package/agent.bak.20260905-190020/extensions/todo/state/invariants.ts +20 -0
  283. package/agent.bak.20260905-190020/extensions/todo/state/replay.ts +38 -0
  284. package/agent.bak.20260905-190020/extensions/todo/state/selectors.ts +107 -0
  285. package/agent.bak.20260905-190020/extensions/todo/state/state-reducer.ts +326 -0
  286. package/agent.bak.20260905-190020/extensions/todo/state/state.ts +18 -0
  287. package/agent.bak.20260905-190020/extensions/todo/state/store.ts +82 -0
  288. package/agent.bak.20260905-190020/extensions/todo/state/task-graph.ts +57 -0
  289. package/agent.bak.20260905-190020/extensions/todo/todo-overlay.ts +204 -0
  290. package/agent.bak.20260905-190020/extensions/todo/todo.ts +155 -0
  291. package/agent.bak.20260905-190020/extensions/todo/tool/response-envelope.ts +109 -0
  292. package/agent.bak.20260905-190020/extensions/todo/tool/types.ts +206 -0
  293. package/agent.bak.20260905-190020/extensions/todo/view/format.ts +177 -0
  294. package/agent.bak.20260905-190020/extensions/todo/widget-order.ts +121 -0
  295. package/agent.bak.20260905-190020/extensions/trim-context/README.md +54 -0
  296. package/agent.bak.20260905-190020/extensions/trim-context/index.ts +487 -0
  297. package/agent.bak.20260905-190020/extensions/trim-context/install.ps1 +23 -0
  298. package/agent.bak.20260905-190020/extensions/trim-context/install.sh +21 -0
  299. package/agent.bak.20260905-190020/keybindings.json +7 -0
  300. package/bin/kiln.js +588 -471
  301. package/package.json +27 -27
  302. package/agent/install.ps1 +0 -637
  303. package/agent/install.sh +0 -620
@@ -0,0 +1,758 @@
1
+ import {
2
+ cloneState,
3
+ normalizeMembers,
4
+ statesEqual,
5
+ toggleMembership,
6
+ togglePackage,
7
+ } from "../persistence.js";
8
+ import type { SkillPackages, SkillState } from "../persistence.js";
9
+ import { deleteSkillFiles, resolveSkillDeleteTarget } from "../delete-skill.js";
10
+ import { fuzzyFilter } from "../fuzzy.js";
11
+ import { TextInput } from "./text-input.js";
12
+ import type { Component, Focusable } from "./tui-helpers.js";
13
+ import { bold, fg, highlight, stripAnsi, truncate, visibleWidth } from "./tui-helpers.js";
14
+
15
+ export interface SkillInfo {
16
+ name: string;
17
+ description: string;
18
+ filePath: string;
19
+ }
20
+
21
+ export interface SkillConfViewOpts {
22
+ tui: any;
23
+ theme: any;
24
+ keybindings: any;
25
+ skills: SkillInfo[];
26
+ disabledRaw: string[] | undefined;
27
+ packagesRaw: SkillPackages;
28
+ onDone: (saved: boolean, discarded?: boolean) => void;
29
+ onPersist: (disabled: string[] | undefined, packages: SkillPackages) => Promise<void>;
30
+ }
31
+
32
+ type Entry = { kind: "pkg"; name: string } | { kind: "skill"; name: string };
33
+
34
+ export class SkillConfView implements Component, Focusable {
35
+ private tui: any;
36
+ private theme: any;
37
+ private keybindings: any;
38
+ private onDone: (saved: boolean, discarded?: boolean) => void;
39
+ private _onPersist: (disabled: string[] | undefined, packages: SkillPackages) => Promise<void>;
40
+
41
+ private skills: SkillInfo[] = [];
42
+ private skillNames: Set<string> = new Set();
43
+ private draftDisabled: Set<string> = new Set();
44
+ private draftPackages: Map<string, string[]> = new Map();
45
+ private original: SkillState;
46
+
47
+ private showPackages: boolean;
48
+ private cursor = 0;
49
+
50
+ private searchActive = false;
51
+ private searchQuery = "";
52
+ private searchInput = new TextInput();
53
+
54
+ private naming = false;
55
+ private nameInput = new TextInput();
56
+
57
+ private assignTarget: string | null = null;
58
+ private assignSnapshot: string[] | null = null;
59
+
60
+ private pendingDelete: { kind: "pkg" | "skill"; name: string; target: string } | null = null;
61
+
62
+ private notice: string | null = null;
63
+ private noticeTimer: ReturnType<typeof setTimeout> | null = null;
64
+
65
+ private _focused = false;
66
+ get focused(): boolean { return this._focused; }
67
+ set focused(v: boolean) {
68
+ this._focused = v;
69
+ this.searchInput.focused = v && this.searchActive;
70
+ this.nameInput.focused = v && this.naming;
71
+ }
72
+
73
+ constructor(opts: SkillConfViewOpts) {
74
+ this.tui = opts.tui;
75
+ this.theme = opts.theme;
76
+ this.keybindings = opts.keybindings;
77
+ this.onDone = opts.onDone;
78
+ this._onPersist = opts.onPersist;
79
+
80
+ this.skills = [...(opts.skills ?? [])].sort((a, b) => a.name.localeCompare(b.name));
81
+ this.skillNames = new Set(this.skills.map((s) => s.name));
82
+ for (const n of opts.disabledRaw ?? []) {
83
+ if (this.skillNames.has(n)) this.draftDisabled.add(n);
84
+ }
85
+ const pkgs = opts.packagesRaw ?? {};
86
+ for (const k of Object.keys(pkgs)) {
87
+ // Stale members (skills no longer on disk) are dropped from the draft;
88
+ // save filters them too, so the next save cleans settings.
89
+ this.draftPackages.set(k, normalizeMembers((pkgs[k] ?? []).filter((m) => this.skillNames.has(m))));
90
+ }
91
+ this.original = this.snapshot();
92
+ this.showPackages = this.draftPackages.size > 0;
93
+ this.cursor = 0;
94
+ }
95
+
96
+ // ---------- draft state ----------
97
+
98
+ private snapshot(): SkillState {
99
+ const packages: SkillPackages = {};
100
+ for (const [k, v] of this.draftPackages) packages[k] = [...v];
101
+ const disabled = this.draftDisabled.size === 0 ? undefined : [...this.draftDisabled].sort();
102
+ return { disabled, packages };
103
+ }
104
+
105
+ private isDirty(): boolean {
106
+ return !statesEqual(this.original, this.snapshot());
107
+ }
108
+
109
+ private knownMembers(pkg: string): string[] {
110
+ return (this.draftPackages.get(pkg) ?? []).filter((m) => this.skillNames.has(m));
111
+ }
112
+
113
+ /** Names of packages containing the skill. */
114
+ private packagesOf(name: string): string[] {
115
+ const out: string[] = [];
116
+ for (const [pkg, members] of this.draftPackages) {
117
+ if (members.includes(name)) out.push(pkg);
118
+ }
119
+ return out;
120
+ }
121
+
122
+ private inAnyPackage(name: string): boolean {
123
+ for (const members of this.draftPackages.values()) {
124
+ if (members.includes(name)) return true;
125
+ }
126
+ return false;
127
+ }
128
+
129
+ private ungroupedCount(): number {
130
+ let n = 0;
131
+ for (const s of this.skills) if (!this.inAnyPackage(s.name)) n++;
132
+ return n;
133
+ }
134
+
135
+ private pkgEnabledCount(pkg: string): { enabled: number; total: number } {
136
+ const members = this.knownMembers(pkg);
137
+ let enabled = 0;
138
+ for (const m of members) if (!this.draftDisabled.has(m)) enabled++;
139
+ return { enabled, total: members.length };
140
+ }
141
+
142
+ private enabledCount(): number {
143
+ let n = 0;
144
+ for (const s of this.skills) if (!this.draftDisabled.has(s.name)) n++;
145
+ return n;
146
+ }
147
+
148
+ // ---------- entries ----------
149
+
150
+ private filteredSkills(): SkillInfo[] {
151
+ if (!this.searchQuery.trim()) return this.skills;
152
+ try {
153
+ return fuzzyFilter(this.searchQuery, this.skills, (s) => `${s.name} ${s.description}`);
154
+ } catch {
155
+ return [];
156
+ }
157
+ }
158
+
159
+ private entries(): Entry[] {
160
+ const out: Entry[] = [];
161
+ const filtering = this.searchQuery.trim().length > 0;
162
+ // Grouped view: skills that live in a package are hidden from the main
163
+ // list (managed via their package row). Flat otherwise: packages hidden,
164
+ // filtering, or assigning (needs every skill visible).
165
+ const grouped = !this.assignTarget && !filtering && this.showPackages && this.draftPackages.size > 0;
166
+ if (!this.assignTarget && !filtering && this.showPackages) {
167
+ for (const name of this.draftPackages.keys()) out.push({ kind: "pkg", name });
168
+ }
169
+ const list = filtering ? this.filteredSkills() : this.skills;
170
+ for (const s of list) {
171
+ if (grouped && this.inAnyPackage(s.name)) continue;
172
+ out.push({ kind: "skill", name: s.name });
173
+ }
174
+ return out;
175
+ }
176
+
177
+ private clampCursor(): void {
178
+ const len = this.entries().length;
179
+ if (len === 0) this.cursor = -1;
180
+ else if (this.cursor < 0 || this.cursor >= len) this.cursor = Math.max(0, Math.min(this.cursor, len - 1));
181
+ }
182
+
183
+ private skillByName(name: string): SkillInfo | undefined {
184
+ return this.skills.find((s) => s.name === name);
185
+ }
186
+
187
+ private setNotice(msg: string | null): void {
188
+ this.notice = msg;
189
+ if (this.noticeTimer) { try { clearTimeout(this.noticeTimer); } catch {} this.noticeTimer = null; }
190
+ if (msg) {
191
+ this.noticeTimer = setTimeout(() => {
192
+ this.notice = null;
193
+ try { this.tui?.requestRender?.(); } catch {}
194
+ }, 4000) as unknown as ReturnType<typeof setTimeout>;
195
+ try {
196
+ (this.noticeTimer as unknown as { unref?: () => void }).unref?.();
197
+ } catch {}
198
+ }
199
+ }
200
+
201
+ private requestRender(): void {
202
+ try { this.tui?.requestRender?.(); } catch {}
203
+ }
204
+
205
+ // ---------- actions ----------
206
+
207
+ private toggleEntry(entry: Entry): void {
208
+ if (entry.kind === "skill") {
209
+ if (this.assignTarget) {
210
+ const members = this.draftPackages.get(this.assignTarget) ?? [];
211
+ this.draftPackages.set(this.assignTarget, toggleMembership(members, entry.name));
212
+ } else {
213
+ if (this.draftDisabled.has(entry.name)) this.draftDisabled.delete(entry.name);
214
+ else this.draftDisabled.add(entry.name);
215
+ }
216
+ return;
217
+ }
218
+ // package: mass disable/enable known members
219
+ const members = this.knownMembers(entry.name);
220
+ if (members.length === 0) {
221
+ this.setNotice(`"${entry.name}" is empty — press a to add skills`);
222
+ return;
223
+ }
224
+ const next = togglePackage(members, [...this.draftDisabled]);
225
+ this.draftDisabled = new Set(next ?? []);
226
+ }
227
+
228
+ private startAssign(pkg: string): void {
229
+ this.assignTarget = pkg;
230
+ this.assignSnapshot = [...(this.draftPackages.get(pkg) ?? [])];
231
+ this.setNotice(`assigning to "${pkg}" — Space toggles, Enter done, Esc cancel`);
232
+ // Move cursor to the first skill entry.
233
+ const list = this.entries();
234
+ const idx = list.findIndex((e) => e.kind === "skill");
235
+ this.cursor = idx === -1 ? -1 : idx;
236
+ }
237
+
238
+ private confirmAssign(): void {
239
+ if (!this.assignTarget) return;
240
+ const members = this.knownMembers(this.assignTarget);
241
+ this.assignTarget = null;
242
+ this.assignSnapshot = null;
243
+ this.setNotice(`package updated (${members.length} skills)`);
244
+ this.clampCursor();
245
+ }
246
+
247
+ private cancelAssign(): void {
248
+ if (this.assignTarget && this.assignSnapshot) {
249
+ this.draftPackages.set(this.assignTarget, this.assignSnapshot);
250
+ }
251
+ this.assignTarget = null;
252
+ this.assignSnapshot = null;
253
+ this.setNotice("assign cancelled");
254
+ this.clampCursor();
255
+ }
256
+
257
+ private executeDelete(del: { kind: "pkg" | "skill"; name: string; target: string }): Promise<void> {
258
+ if (del.kind === "pkg") {
259
+ this.draftPackages.delete(del.name);
260
+ if (this.assignTarget === del.name) {
261
+ this.assignTarget = null;
262
+ this.assignSnapshot = null;
263
+ }
264
+ this.setNotice(`deleted package "${del.name}"`);
265
+ this.clampCursor();
266
+ this.requestRender();
267
+ return Promise.resolve();
268
+ }
269
+ const filePath = this.skillByName(del.name)?.filePath ?? "";
270
+ return deleteSkillFiles(filePath).then(
271
+ (target) => {
272
+ this.skills = this.skills.filter((s) => s.name !== del.name);
273
+ this.skillNames.delete(del.name);
274
+ this.draftDisabled.delete(del.name);
275
+ for (const [k, members] of this.draftPackages) {
276
+ if (members.includes(del.name)) {
277
+ this.draftPackages.set(k, members.filter((m) => m !== del.name));
278
+ }
279
+ }
280
+ this.setNotice(`deleted skill "${del.name}" (${target})`);
281
+ this.clampCursor();
282
+ this.requestRender();
283
+ },
284
+ (err) => {
285
+ this.setNotice(`delete failed: ${err instanceof Error ? err.message : String(err)}`);
286
+ this.requestRender();
287
+ },
288
+ );
289
+ }
290
+
291
+ /** Persist the draft in place; the UI stays open. */
292
+ private saveDraft(): void {
293
+ if (!this.isDirty()) {
294
+ this.setNotice("nothing to save");
295
+ this.requestRender();
296
+ return;
297
+ }
298
+ const snap = this.snapshot();
299
+ Promise.resolve(this._onPersist(snap.disabled, snap.packages)).then(() => {
300
+ this.original = cloneState(snap);
301
+ const np = this.draftPackages.size;
302
+ this.setNotice(
303
+ `saved — ${this.enabledCount()}/${this.skills.length} enabled, ${np} package${np === 1 ? "" : "s"}`,
304
+ );
305
+ this.requestRender();
306
+ }).catch(() => {
307
+ this.setNotice("save failed — see console");
308
+ this.requestRender();
309
+ });
310
+ }
311
+
312
+ private confirmNewPackage(): void {
313
+ const name = this.nameInput.getValue().trim();
314
+ if (!name) {
315
+ this.setNotice("package name is empty");
316
+ return;
317
+ }
318
+ const exists = [...this.draftPackages.keys()].some((k) => k.toLowerCase() === name.toLowerCase());
319
+ if (exists) {
320
+ this.setNotice(`"${name}" already exists`);
321
+ return;
322
+ }
323
+ this.draftPackages.set(name, []);
324
+ this.naming = false;
325
+ this.nameInput.setValue("");
326
+ this.nameInput.focused = false;
327
+ this.showPackages = true;
328
+ this.searchQuery = "";
329
+ this.searchInput.setValue("");
330
+ this.searchActive = false;
331
+ // Jump cursor to the new package.
332
+ const list = this.entries();
333
+ const idx = list.findIndex((e) => e.kind === "pkg" && e.name === name);
334
+ this.cursor = idx === -1 ? 0 : idx;
335
+ this.setNotice(`created "${name}" — press a on it to add skills`);
336
+ }
337
+
338
+ private clearFilter(): void {
339
+ this.searchActive = false;
340
+ this.searchInput.focused = false;
341
+ this.searchInput.setValue("");
342
+ this.searchQuery = "";
343
+ this.clampCursor();
344
+ }
345
+
346
+ // ---------- render ----------
347
+
348
+ private getAvailableHeight(): number {
349
+ let h: number | undefined;
350
+ try {
351
+ const term = (this.tui as unknown as { terminal?: unknown })?.terminal as
352
+ | { rows?: number; height?: number; getSize?: () => { rows?: number } }
353
+ | undefined;
354
+ if (term) {
355
+ if (typeof term.rows === "number") h = term.rows;
356
+ else if (typeof term.height === "number") h = term.height;
357
+ else if (typeof term.getSize === "function") {
358
+ const sz = term.getSize();
359
+ if (sz && typeof sz.rows === "number") h = sz.rows;
360
+ }
361
+ }
362
+ } catch {}
363
+ if (h === undefined || h === null) {
364
+ try {
365
+ const c = (globalThis as { process?: { stdout?: { rows?: number } } }).process?.stdout;
366
+ if (c && typeof c.rows === "number") h = c.rows;
367
+ } catch {}
368
+ }
369
+ if (h === undefined || h === null || h <= 0) h = 24;
370
+ const extra = (this.searchActive ? 1 : 0) + (this.assignTarget ? 1 : 0) + (this.naming ? 1 : 0);
371
+ const half = Math.floor(h / 2);
372
+ const chrome = 9 + extra;
373
+ return Math.min(Math.max(5, half - chrome), 20);
374
+ }
375
+
376
+ /** Package row: accent marker + bold name, no box drawing. */
377
+ private packageRow(name: string): string {
378
+ const { enabled: en, total: tot } = this.pkgEnabledCount(name);
379
+ const mark = tot === 0
380
+ ? fg(this.theme, "dim", "○")
381
+ : en === tot
382
+ ? fg(this.theme, "success", "◉")
383
+ : en === 0
384
+ ? fg(this.theme, "dim", "○")
385
+ : fg(this.theme, "warning", "◍");
386
+ const count = tot === 0
387
+ ? fg(this.theme, "dim", "(empty)")
388
+ : fg(this.theme, "dim", `(${en}/${tot})`);
389
+ const assigning = this.assignTarget === name ? fg(this.theme, "accent", " [assigning]") : "";
390
+ const marker = fg(this.theme, "accent", "▸");
391
+ const label = fg(this.theme, "accent", bold(this.theme, name));
392
+ return `${marker} ${mark} ${label} ${count}${assigning}`;
393
+ }
394
+
395
+ /** Boxed input for the new-package name so the typing affordance is obvious. */
396
+ private nameBox(width: number): string[] {
397
+ const boxW = Math.max(24, Math.min(44, width - 2));
398
+ const inner = boxW - 4;
399
+ const value = this.nameInput.render(inner)[0] ?? "";
400
+ const pad = Math.max(0, inner - visibleWidth(value));
401
+ const title = " new package name ";
402
+ const top =
403
+ fg(this.theme, "accent", "┌─") +
404
+ fg(this.theme, "accent", bold(this.theme, title)) +
405
+ fg(this.theme, "accent", "─".repeat(Math.max(0, boxW - 3 - title.length)) + "┐");
406
+ const mid = fg(this.theme, "accent", "│ ") + value + " ".repeat(pad) + fg(this.theme, "accent", " │");
407
+ const bottom = fg(this.theme, "accent", "└" + "─".repeat(boxW - 2) + "┘");
408
+ return [top, mid, bottom];
409
+ }
410
+
411
+ render(width: number): string[] {
412
+ const lines: string[] = [];
413
+ const total = this.skills.length;
414
+ const enabled = this.enabledCount();
415
+ const np = this.draftPackages.size;
416
+ const dirtyMark = this.isDirty() ? fg(this.theme, "warning", " ● unsaved") : "";
417
+ lines.push(truncate(
418
+ fg(this.theme, "accent", bold(this.theme, "skillsconf")) +
419
+ fg(this.theme, "dim", ` ${enabled}/${total} enabled ${np} package${np === 1 ? "" : "s"}`) + dirtyMark,
420
+ width,
421
+ ));
422
+ lines.push(truncate(fg(this.theme, "dim", "─".repeat(Math.max(0, width))), width));
423
+
424
+ if (this.assignTarget) {
425
+ lines.push(truncate(
426
+ fg(this.theme, "warning", ` assigning → "${this.assignTarget}"`) +
427
+ fg(this.theme, "dim", " Space toggles membership • Enter done • Esc cancel"),
428
+ width,
429
+ ));
430
+ }
431
+ if (this.naming) {
432
+ for (const bl of this.nameBox(width)) lines.push(truncate(bl, width));
433
+ } else if (this.searchActive) {
434
+ const inputWidth = Math.max(10, width - 4);
435
+ const rendered = this.searchInput.render(inputWidth);
436
+ lines.push(truncate(fg(this.theme, "accent", "/ ") + (rendered[0] ?? ""), width));
437
+ } else if (this.searchQuery) {
438
+ lines.push(truncate(
439
+ fg(this.theme, "dim", `filter: "${this.searchQuery}" (${this.entries().length}/${total}) • Esc to clear • / to search`),
440
+ width,
441
+ ));
442
+ }
443
+
444
+ const list = this.entries();
445
+ if (list.length === 0) {
446
+ if (this.searchQuery) {
447
+ lines.push(truncate(fg(this.theme, "warning", ` No skills match '${this.searchQuery}'`), width));
448
+ lines.push(truncate(fg(this.theme, "dim", " Press Esc to clear filter"), width));
449
+ } else {
450
+ lines.push(truncate(fg(this.theme, "dim", " No skills loaded"), width));
451
+ }
452
+ } else {
453
+ const maxVisible = this.getAvailableHeight();
454
+ let start = 0;
455
+ let end = list.length;
456
+ if (list.length > maxVisible) {
457
+ const half = Math.floor(maxVisible / 2);
458
+ start = Math.max(0, Math.min(this.cursor - half, list.length - maxVisible));
459
+ end = Math.min(start + maxVisible, list.length);
460
+ }
461
+ const grouped = !this.searchQuery && !this.assignTarget && this.showPackages && this.draftPackages.size > 0;
462
+ let lastKind: Entry["kind"] | "" = "";
463
+ for (let idx = start; idx < end; idx++) {
464
+ const entry = list[idx];
465
+ if (!this.searchQuery && !this.assignTarget && entry.kind !== lastKind) {
466
+ lastKind = entry.kind;
467
+ if (entry.kind === "pkg") {
468
+ lines.push(truncate(
469
+ fg(this.theme, "accent", bold(this.theme, ` PACKAGES (${this.draftPackages.size}) `)) +
470
+ fg(this.theme, "dim", " Space toggles whole group"),
471
+ width,
472
+ ));
473
+ } else {
474
+ const label = grouped
475
+ ? ` UNGROUPED SKILLS (${this.ungroupedCount()}) `
476
+ : ` SKILLS (${total}) `;
477
+ lines.push(truncate(fg(this.theme, "accent", bold(this.theme, label)), width));
478
+ }
479
+ }
480
+ const isCursor = this.cursor === idx;
481
+ let line: string;
482
+ if (entry.kind === "pkg") {
483
+ line = this.packageRow(entry.name);
484
+ } else {
485
+ const info = this.skillByName(entry.name);
486
+ const showTags = !!this.assignTarget || this.searchQuery.trim().length > 0;
487
+ const tags = showTags ? this.packagesOf(entry.name) : [];
488
+ const tagText = tags.length > 0 ? fg(this.theme, "dim", ` ⟨${tags.join(",")}⟩`) : "";
489
+ if (this.assignTarget) {
490
+ const member = (this.draftPackages.get(this.assignTarget) ?? []).includes(entry.name);
491
+ const mark = member ? fg(this.theme, "success", "+") : fg(this.theme, "dim", "·");
492
+ const desc = info?.description ? fg(this.theme, "dim", ` ${info.description}`) : "";
493
+ line = ` ${mark} ${entry.name}${desc}${tagText}`;
494
+ } else {
495
+ const on = !this.draftDisabled.has(entry.name);
496
+ const mark = on ? fg(this.theme, "success", "◉") : fg(this.theme, "dim", "○");
497
+ const desc = info?.description ? fg(this.theme, "dim", ` ${info.description}`) : "";
498
+ line = ` ${mark} ${entry.name}${desc}${tagText}`;
499
+ }
500
+ }
501
+ lines.push(truncate(isCursor ? highlight(this.theme, line) : line, width));
502
+ }
503
+ if (list.length > maxVisible) {
504
+ const pos = this.cursor >= 0 ? this.cursor + 1 : 0;
505
+ lines.push(truncate(fg(this.theme, "dim", ` (${pos}/${list.length})`), width));
506
+ }
507
+ }
508
+
509
+ lines.push("");
510
+ if (this.pendingDelete) {
511
+ const del = this.pendingDelete;
512
+ const msg = del.kind === "pkg"
513
+ ? `Delete package "${del.name}"? (members keep their on/off state) y delete n cancel`
514
+ : `Delete skill "${del.name}" from disk? ${del.target} y delete n cancel`;
515
+ lines.push(truncate(fg(this.theme, "warning", msg), width));
516
+ } else if (this.notice) {
517
+ lines.push(truncate(fg(this.theme, "accent", this.notice), width));
518
+ } else if (this.isDirty()) {
519
+ lines.push(truncate(fg(this.theme, "warning", "● unsaved — press s to save"), width));
520
+ } else if (this.searchActive) {
521
+ lines.push(truncate(fg(this.theme, "dim", "filtering — ↑↓ exit typing • ←→ edit • Esc clear • Enter keep"), width));
522
+ } else if (this.naming) {
523
+ lines.push(truncate(fg(this.theme, "dim", "Enter create • Esc cancel"), width));
524
+ } else if (this.assignTarget) {
525
+ lines.push(truncate(fg(this.theme, "dim", `${this.knownMembers(this.assignTarget).length} in package`), width));
526
+ } else {
527
+ lines.push(truncate(fg(this.theme, "dim", `${enabled} enabled / ${total} total`), width));
528
+ }
529
+
530
+ const kb = this.assignTarget
531
+ ? ["Space toggle membership │ Enter done │ Esc cancel"]
532
+ : this.naming
533
+ ? ["type a name │ Enter create │ Esc cancel"]
534
+ : [
535
+ "↑↓/j k navigate │ Space toggle │ / filter │ e flat/grouped view",
536
+ "n new package │ a add skills to package │ d delete │ s save │ Esc close",
537
+ ];
538
+ for (const row of kb) {
539
+ const oneLine = fg(this.theme, "dim", row);
540
+ try {
541
+ if (visibleWidth(stripAnsi(oneLine)) + 2 <= width) lines.push(truncate(oneLine, width));
542
+ else lines.push(truncate(oneLine, width));
543
+ } catch {
544
+ lines.push(truncate(oneLine, width));
545
+ }
546
+ }
547
+ return lines.map((l) => truncate(l, width));
548
+ }
549
+
550
+ // ---------- input ----------
551
+
552
+ handleInput(data: string): void {
553
+ if (!data) return;
554
+ const tryKb = (keyId: string): boolean => {
555
+ try {
556
+ if (this.keybindings) {
557
+ if (typeof this.keybindings.matches === "function" && this.keybindings.matches(data, keyId)) return true;
558
+ if (typeof this.keybindings.match === "function" && this.keybindings.match(data, keyId)) return true;
559
+ }
560
+ } catch {}
561
+ return false;
562
+ };
563
+ const isEsc = data === "\x1b" || tryKb("escape") || data.includes("\x1b[27");
564
+ // Bare Enter only: pasted text containing newlines must not confirm/save.
565
+ const isEnter = data === "\r" || data === "\n" || data === "\r\n" || tryKb("enter") || tryKb("return");
566
+ const isQ = data === "q" || data === "Q";
567
+
568
+ // --- naming mode captures everything except Enter/Esc ---
569
+ if (this.naming) {
570
+ if (isEnter) { this.confirmNewPackage(); this.requestRender(); return; }
571
+ if (isEsc) {
572
+ this.naming = false;
573
+ this.nameInput.setValue("");
574
+ this.nameInput.focused = false;
575
+ this.requestRender();
576
+ return;
577
+ }
578
+ try { this.nameInput.handleInput?.(data); } catch {}
579
+ this.requestRender();
580
+ return;
581
+ }
582
+
583
+ // --- pending delete captures everything except y/n/Esc ---
584
+ if (this.pendingDelete) {
585
+ if (data === "y" || data === "Y") {
586
+ const del = this.pendingDelete;
587
+ this.pendingDelete = null;
588
+ void this.executeDelete(del);
589
+ return;
590
+ }
591
+ if (data === "n" || data === "N" || isEsc || isQ) {
592
+ this.pendingDelete = null;
593
+ this.setNotice("delete cancelled");
594
+ this.requestRender();
595
+ return;
596
+ }
597
+ return;
598
+ }
599
+
600
+ // --- search mode ---
601
+ if (!this.searchActive && data === "/") {
602
+ this.searchActive = true;
603
+ this.searchInput.focused = true;
604
+ this._focused = true;
605
+ this.searchInput.setValue("");
606
+ this.searchQuery = "";
607
+ if (!this.assignTarget) this.cursor = this.entries().length > 0 ? 0 : -1;
608
+ this.requestRender();
609
+ return;
610
+ }
611
+ if (this.searchActive) {
612
+ // Up/down leave search typing and return to normal mode with the
613
+ // filter kept (like Enter). Left/right stay in search editing the query.
614
+ const arrowUp = data === "\x1b[A" || data === "\x1bOA" || tryKb("up");
615
+ const arrowDown = data === "\x1b[B" || data === "\x1bOB" || tryKb("down");
616
+ if (arrowUp || arrowDown) {
617
+ this.searchActive = false;
618
+ this.searchInput.focused = false;
619
+ const len = this.entries().length;
620
+ if (len > 0) {
621
+ if (arrowUp) this.cursor = (this.cursor - 1 + len) % len;
622
+ else this.cursor = (this.cursor + 1) % len;
623
+ }
624
+ this.requestRender();
625
+ return;
626
+ }
627
+ const navHome = data === "\x1b[H" || data === "\x1b[1~" || data === "\x1bOH" || tryKb("home");
628
+ const navEnd = data === "\x1b[F" || data === "\x1b[4~" || data === "\x1bOF" || tryKb("end");
629
+ if (navHome || navEnd) {
630
+ const len = this.entries().length;
631
+ if (len > 0) {
632
+ if (navHome) this.cursor = 0;
633
+ else this.cursor = len - 1;
634
+ }
635
+ this.requestRender();
636
+ return;
637
+ }
638
+ if (isEsc) { this.clearFilter(); this.requestRender(); return; }
639
+ if (isEnter) {
640
+ this.searchActive = false;
641
+ this.searchInput.focused = false;
642
+ this.requestRender();
643
+ return;
644
+ }
645
+ if (data === "\t" || data === "\x1b[Z") { this.requestRender(); return; }
646
+ try { this.searchInput.handleInput?.(data); } catch {}
647
+ this.searchQuery = this.searchInput.getValue();
648
+ this.cursor = this.entries().length > 0 ? 0 : -1;
649
+ this.requestRender();
650
+ return;
651
+ }
652
+
653
+ // --- normal / assign keys ---
654
+ if (!this.assignTarget && (data === "e" || data === "E")) {
655
+ this.showPackages = !this.showPackages;
656
+ this.clampCursor();
657
+ this.requestRender();
658
+ return;
659
+ }
660
+ if (!this.assignTarget && (data === "n" || data === "N")) {
661
+ this.naming = true;
662
+ this.nameInput.setValue("");
663
+ this.nameInput.focused = true;
664
+ this._focused = true;
665
+ this.requestRender();
666
+ return;
667
+ }
668
+ if (data === "a" || data === "A") {
669
+ if (this.assignTarget) {
670
+ this.setNotice("already assigning — Enter done, Esc cancel");
671
+ this.requestRender();
672
+ return;
673
+ }
674
+ const cur = this.entries()[this.cursor];
675
+ if (cur && cur.kind === "pkg") {
676
+ this.startAssign(cur.name);
677
+ this.requestRender();
678
+ return;
679
+ }
680
+ this.setNotice("put the cursor on a package and press a");
681
+ this.requestRender();
682
+ return;
683
+ }
684
+ if (data === "d" || data === "D") {
685
+ const cur = this.entries()[this.cursor];
686
+ if (!cur) return;
687
+ if (cur.kind === "pkg") {
688
+ this.pendingDelete = { kind: "pkg", name: cur.name, target: "" };
689
+ } else {
690
+ const info = this.skillByName(cur.name);
691
+ const target = info ? resolveSkillDeleteTarget(info.filePath) : null;
692
+ if (!target) {
693
+ this.setNotice(`cannot delete "${cur.name}": unknown location`);
694
+ this.requestRender();
695
+ return;
696
+ }
697
+ this.pendingDelete = { kind: "skill", name: cur.name, target };
698
+ }
699
+ this.requestRender();
700
+ return;
701
+ }
702
+ if ((isEsc || isQ) && this.searchQuery) {
703
+ this.clearFilter();
704
+ this.requestRender();
705
+ return;
706
+ }
707
+ if (this.assignTarget && isEsc) {
708
+ this.cancelAssign();
709
+ this.requestRender();
710
+ return;
711
+ }
712
+ if (isEsc || isQ) {
713
+ const dirty = this.isDirty();
714
+ this.onDone(false, dirty);
715
+ return;
716
+ }
717
+ if (data === " ") {
718
+ const cur = this.entries()[this.cursor];
719
+ if (!cur) return;
720
+ this.toggleEntry(cur);
721
+ this.clampCursor();
722
+ this.requestRender();
723
+ return;
724
+ }
725
+ if (!this.assignTarget && (data === "s" || data === "S")) {
726
+ this.saveDraft();
727
+ return;
728
+ }
729
+ if (isEnter) {
730
+ if (this.assignTarget) {
731
+ this.confirmAssign();
732
+ this.requestRender();
733
+ }
734
+ // Bare Enter never saves — use s.
735
+ return;
736
+ }
737
+ let handled = false;
738
+ const isUp = data === "k" || data === "\x1b[A" || data === "\x1bOA" || tryKb("up") || tryKb("k");
739
+ const isDown = data === "j" || data === "\x1b[B" || data === "\x1bOB" || tryKb("down") || tryKb("j");
740
+ const isHome = data === "g" || data === "\x1b[H" || data === "\x1b[1~" || data === "\x1bOH" || tryKb("home") || tryKb("g");
741
+ const isEnd = data === "G" || data === "\x1b[F" || data === "\x1b[4~" || data === "\x1bOF" || tryKb("end") || tryKb("G");
742
+ const len = this.entries().length;
743
+ if (isUp) {
744
+ if (len > 0) { this.cursor = (this.cursor - 1 + len) % len; handled = true; }
745
+ } else if (isDown) {
746
+ if (len > 0) { this.cursor = (this.cursor + 1) % len; handled = true; }
747
+ } else if (isHome) {
748
+ if (len > 0) { this.cursor = 0; handled = true; }
749
+ } else if (isEnd) {
750
+ if (len > 0) { this.cursor = len - 1; handled = true; }
751
+ }
752
+ if (handled) this.requestRender();
753
+ }
754
+
755
+ invalidate(): void {
756
+ this.requestRender();
757
+ }
758
+ }