@asterxsk/kiln 0.3.0 → 0.4.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 (267) hide show
  1. package/README.md +2 -2
  2. package/agent/version.txt +1 -1
  3. package/bin/kiln.js +10 -8
  4. package/package.json +1 -1
  5. package/agent.bak.20260905-190020/AGENTS.md +0 -67
  6. package/agent.bak.20260905-190020/README.md +0 -5
  7. package/agent.bak.20260905-190020/extensions/ask-user/index.ts +0 -418
  8. package/agent.bak.20260905-190020/extensions/ask-user/install.ps1 +0 -23
  9. package/agent.bak.20260905-190020/extensions/ask-user/install.sh +0 -21
  10. package/agent.bak.20260905-190020/extensions/ask-user/package-lock.json +0 -769
  11. package/agent.bak.20260905-190020/extensions/ask-user/package.json +0 -19
  12. package/agent.bak.20260905-190020/extensions/ask-user/prompt.ts +0 -45
  13. package/agent.bak.20260905-190020/extensions/ask-user/tsconfig.json +0 -7
  14. package/agent.bak.20260905-190020/extensions/background-terminals/docs/implementation-guide.md +0 -942
  15. package/agent.bak.20260905-190020/extensions/background-terminals/index.ts +0 -629
  16. package/agent.bak.20260905-190020/extensions/background-terminals/install.ps1 +0 -23
  17. package/agent.bak.20260905-190020/extensions/background-terminals/install.sh +0 -21
  18. package/agent.bak.20260905-190020/extensions/background-terminals/package-lock.json +0 -769
  19. package/agent.bak.20260905-190020/extensions/background-terminals/package.json +0 -17
  20. package/agent.bak.20260905-190020/extensions/background-terminals/src/domain.ts +0 -87
  21. package/agent.bak.20260905-190020/extensions/background-terminals/src/manager.ts +0 -907
  22. package/agent.bak.20260905-190020/extensions/background-terminals/src/output.ts +0 -84
  23. package/agent.bak.20260905-190020/extensions/background-terminals/src/prompt.ts +0 -142
  24. package/agent.bak.20260905-190020/extensions/background-terminals/src/result-delivery.ts +0 -27
  25. package/agent.bak.20260905-190020/extensions/background-terminals/src/runtime.ts +0 -36
  26. package/agent.bak.20260905-190020/extensions/background-terminals/src/ui/output-view.ts +0 -79
  27. package/agent.bak.20260905-190020/extensions/background-terminals/src/ui/ps.ts +0 -621
  28. package/agent.bak.20260905-190020/extensions/background-terminals/src/widget-order.ts +0 -121
  29. package/agent.bak.20260905-190020/extensions/background-terminals/test/manager.test.ts +0 -735
  30. package/agent.bak.20260905-190020/extensions/background-terminals/test/output.test.ts +0 -109
  31. package/agent.bak.20260905-190020/extensions/background-terminals/test/prompt.test.ts +0 -125
  32. package/agent.bak.20260905-190020/extensions/background-terminals/test/ps.test.ts +0 -82
  33. package/agent.bak.20260905-190020/extensions/background-terminals/test/result-delivery.test.ts +0 -44
  34. package/agent.bak.20260905-190020/extensions/background-terminals/tsconfig.json +0 -7
  35. package/agent.bak.20260905-190020/extensions/destructive/README.md +0 -34
  36. package/agent.bak.20260905-190020/extensions/destructive/index.ts +0 -124
  37. package/agent.bak.20260905-190020/extensions/destructive/install.ps1 +0 -23
  38. package/agent.bak.20260905-190020/extensions/destructive/install.sh +0 -21
  39. package/agent.bak.20260905-190020/extensions/file-search/index.ts +0 -459
  40. package/agent.bak.20260905-190020/extensions/file-search/install.ps1 +0 -23
  41. package/agent.bak.20260905-190020/extensions/file-search/install.sh +0 -21
  42. package/agent.bak.20260905-190020/extensions/file-search/package-lock.json +0 -2253
  43. package/agent.bak.20260905-190020/extensions/file-search/package.json +0 -23
  44. package/agent.bak.20260905-190020/extensions/file-search/src/args.ts +0 -122
  45. package/agent.bak.20260905-190020/extensions/file-search/src/binaries.ts +0 -422
  46. package/agent.bak.20260905-190020/extensions/file-search/src/output.ts +0 -126
  47. package/agent.bak.20260905-190020/extensions/file-search/src/process.ts +0 -146
  48. package/agent.bak.20260905-190020/extensions/file-search/src/prompt.ts +0 -52
  49. package/agent.bak.20260905-190020/extensions/file-search/test/index.spec.ts +0 -443
  50. package/agent.bak.20260905-190020/extensions/file-search/tsconfig.json +0 -7
  51. package/agent.bak.20260905-190020/extensions/goal/README.md +0 -50
  52. package/agent.bak.20260905-190020/extensions/goal/index.ts +0 -155
  53. package/agent.bak.20260905-190020/extensions/goal/install.ps1 +0 -23
  54. package/agent.bak.20260905-190020/extensions/goal/install.sh +0 -21
  55. package/agent.bak.20260905-190020/extensions/kiln-update/README.md +0 -25
  56. package/agent.bak.20260905-190020/extensions/kiln-update/index.ts +0 -99
  57. package/agent.bak.20260905-190020/extensions/kiln-update/install.ps1 +0 -23
  58. package/agent.bak.20260905-190020/extensions/kiln-update/install.sh +0 -21
  59. package/agent.bak.20260905-190020/extensions/modelconf/PLAN.md +0 -915
  60. package/agent.bak.20260905-190020/extensions/modelconf/README.md +0 -66
  61. package/agent.bak.20260905-190020/extensions/modelconf/index.ts +0 -296
  62. package/agent.bak.20260905-190020/extensions/modelconf/install.ps1 +0 -23
  63. package/agent.bak.20260905-190020/extensions/modelconf/install.sh +0 -21
  64. package/agent.bak.20260905-190020/extensions/modelconf/package-lock.json +0 -1809
  65. package/agent.bak.20260905-190020/extensions/modelconf/package.json +0 -13
  66. package/agent.bak.20260905-190020/extensions/modelconf/src/fuzzy.ts +0 -26
  67. package/agent.bak.20260905-190020/extensions/modelconf/src/glob.ts +0 -13
  68. package/agent.bak.20260905-190020/extensions/modelconf/src/persistence.test.ts +0 -46
  69. package/agent.bak.20260905-190020/extensions/modelconf/src/persistence.ts +0 -164
  70. package/agent.bak.20260905-190020/extensions/modelconf/src/ui/ModelConfView.test.ts +0 -75
  71. package/agent.bak.20260905-190020/extensions/modelconf/src/ui/ModelConfView.ts +0 -1101
  72. package/agent.bak.20260905-190020/extensions/modelconf/tsconfig.json +0 -15
  73. package/agent.bak.20260905-190020/extensions/pi-web-access/CHANGELOG.md +0 -690
  74. package/agent.bak.20260905-190020/extensions/pi-web-access/LICENSE +0 -21
  75. package/agent.bak.20260905-190020/extensions/pi-web-access/README.md +0 -470
  76. package/agent.bak.20260905-190020/extensions/pi-web-access/SECURITY.md +0 -5
  77. package/agent.bak.20260905-190020/extensions/pi-web-access/activity.ts +0 -101
  78. package/agent.bak.20260905-190020/extensions/pi-web-access/auth-fetch.ts +0 -148
  79. package/agent.bak.20260905-190020/extensions/pi-web-access/banner.png +0 -0
  80. package/agent.bak.20260905-190020/extensions/pi-web-access/brightdata-unlocker.ts +0 -272
  81. package/agent.bak.20260905-190020/extensions/pi-web-access/chrome-cookies.ts +0 -669
  82. package/agent.bak.20260905-190020/extensions/pi-web-access/content-find.ts +0 -139
  83. package/agent.bak.20260905-190020/extensions/pi-web-access/credential-source.ts +0 -191
  84. package/agent.bak.20260905-190020/extensions/pi-web-access/data-uri-sanitize.ts +0 -406
  85. package/agent.bak.20260905-190020/extensions/pi-web-access/datalab-pdf-extract.ts +0 -568
  86. package/agent.bak.20260905-190020/extensions/pi-web-access/declared-web-links.ts +0 -173
  87. package/agent.bak.20260905-190020/extensions/pi-web-access/evidence/CONTRACT-EVIDENCE.md +0 -496
  88. package/agent.bak.20260905-190020/extensions/pi-web-access/evidence/contract-probe.mjs +0 -140
  89. package/agent.bak.20260905-190020/extensions/pi-web-access/exa.ts +0 -526
  90. package/agent.bak.20260905-190020/extensions/pi-web-access/extract.ts +0 -1196
  91. package/agent.bak.20260905-190020/extensions/pi-web-access/feature-config.ts +0 -29
  92. package/agent.bak.20260905-190020/extensions/pi-web-access/fetch-params.ts +0 -111
  93. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-adc.ts +0 -298
  94. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-api.ts +0 -353
  95. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-pdf-extract.ts +0 -108
  96. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-search.ts +0 -21
  97. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-url-context.ts +0 -128
  98. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-web-config.ts +0 -101
  99. package/agent.bak.20260905-190020/extensions/pi-web-access/gemini-web.ts +0 -487
  100. package/agent.bak.20260905-190020/extensions/pi-web-access/github-api.ts +0 -197
  101. package/agent.bak.20260905-190020/extensions/pi-web-access/github-extract.ts +0 -746
  102. package/agent.bak.20260905-190020/extensions/pi-web-access/github-issue-pr.ts +0 -700
  103. package/agent.bak.20260905-190020/extensions/pi-web-access/index.ts +0 -1737
  104. package/agent.bak.20260905-190020/extensions/pi-web-access/package-lock.json +0 -5808
  105. package/agent.bak.20260905-190020/extensions/pi-web-access/package.json +0 -64
  106. package/agent.bak.20260905-190020/extensions/pi-web-access/page-query.ts +0 -96
  107. package/agent.bak.20260905-190020/extensions/pi-web-access/pdf-extract.ts +0 -409
  108. package/agent.bak.20260905-190020/extensions/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
  109. package/agent.bak.20260905-190020/extensions/pi-web-access/promise-try.d.ts +0 -7
  110. package/agent.bak.20260905-190020/extensions/pi-web-access/query-rewrite.ts +0 -51
  111. package/agent.bak.20260905-190020/extensions/pi-web-access/render-search-error.ts +0 -170
  112. package/agent.bak.20260905-190020/extensions/pi-web-access/rsc-extract.ts +0 -338
  113. package/agent.bak.20260905-190020/extensions/pi-web-access/search-types.ts +0 -20
  114. package/agent.bak.20260905-190020/extensions/pi-web-access/source-check.ts +0 -282
  115. package/agent.bak.20260905-190020/extensions/pi-web-access/ssrf-protection.ts +0 -526
  116. package/agent.bak.20260905-190020/extensions/pi-web-access/storage.ts +0 -521
  117. package/agent.bak.20260905-190020/extensions/pi-web-access/summary-model-scope.ts +0 -125
  118. package/agent.bak.20260905-190020/extensions/pi-web-access/test/auth-fetch.test.mjs +0 -208
  119. package/agent.bak.20260905-190020/extensions/pi-web-access/test/brightdata-unlocker.test.mjs +0 -840
  120. package/agent.bak.20260905-190020/extensions/pi-web-access/test/chrome-cookie-extraction.test.mjs +0 -441
  121. package/agent.bak.20260905-190020/extensions/pi-web-access/test/config-path.test.mjs +0 -283
  122. package/agent.bak.20260905-190020/extensions/pi-web-access/test/content-find.test.mjs +0 -25
  123. package/agent.bak.20260905-190020/extensions/pi-web-access/test/credential-source.test.mjs +0 -118
  124. package/agent.bak.20260905-190020/extensions/pi-web-access/test/data-uri-sanitize.test.mjs +0 -210
  125. package/agent.bak.20260905-190020/extensions/pi-web-access/test/datalab-pdf-extract.test.mjs +0 -552
  126. package/agent.bak.20260905-190020/extensions/pi-web-access/test/declared-web-links.test.mjs +0 -212
  127. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-answer-storage.test.mjs +0 -40
  128. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-cache-storage.test.mjs +0 -334
  129. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-content-domain-policy.test.mjs +0 -95
  130. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-modes.test.mjs +0 -53
  131. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-not-found-guidance.test.mjs +0 -92
  132. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-params.test.mjs +0 -86
  133. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-render-call.test.mjs +0 -34
  134. package/agent.bak.20260905-190020/extensions/pi-web-access/test/fetch-routing.test.mjs +0 -173
  135. package/agent.bak.20260905-190020/extensions/pi-web-access/test/gemini-adc-auth.test.mjs +0 -257
  136. package/agent.bak.20260905-190020/extensions/pi-web-access/test/gemini-api-transport.test.mjs +0 -170
  137. package/agent.bak.20260905-190020/extensions/pi-web-access/test/gemini-pdf-extract.test.mjs +0 -133
  138. package/agent.bak.20260905-190020/extensions/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +0 -178
  139. package/agent.bak.20260905-190020/extensions/pi-web-access/test/gemini-web-header-overflow.test.mjs +0 -148
  140. package/agent.bak.20260905-190020/extensions/pi-web-access/test/get-search-content.test.mjs +0 -223
  141. package/agent.bak.20260905-190020/extensions/pi-web-access/test/github-extract.test.mjs +0 -378
  142. package/agent.bak.20260905-190020/extensions/pi-web-access/test/github-issue-pr.test.mjs +0 -565
  143. package/agent.bak.20260905-190020/extensions/pi-web-access/test/inline-content-config.test.mjs +0 -99
  144. package/agent.bak.20260905-190020/extensions/pi-web-access/test/lazy-extract-load.test.mjs +0 -118
  145. package/agent.bak.20260905-190020/extensions/pi-web-access/test/local-video-oversize.test.mjs +0 -52
  146. package/agent.bak.20260905-190020/extensions/pi-web-access/test/package-typebox-dependency.test.mjs +0 -50
  147. package/agent.bak.20260905-190020/extensions/pi-web-access/test/page-query.test.mjs +0 -51
  148. package/agent.bak.20260905-190020/extensions/pi-web-access/test/pdf-config.test.mjs +0 -140
  149. package/agent.bak.20260905-190020/extensions/pi-web-access/test/pdf-extract.test.mjs +0 -500
  150. package/agent.bak.20260905-190020/extensions/pi-web-access/test/proxy-transport.test.mjs +0 -286
  151. package/agent.bak.20260905-190020/extensions/pi-web-access/test/query-rewrite.test.mjs +0 -52
  152. package/agent.bak.20260905-190020/extensions/pi-web-access/test/rsc-fallback.test.mjs +0 -102
  153. package/agent.bak.20260905-190020/extensions/pi-web-access/test/search-error-render.test.mjs +0 -152
  154. package/agent.bak.20260905-190020/extensions/pi-web-access/test/search-providers.test.mjs +0 -274
  155. package/agent.bak.20260905-190020/extensions/pi-web-access/test/source-check.test.mjs +0 -179
  156. package/agent.bak.20260905-190020/extensions/pi-web-access/test/ssrf-allow-ranges-config.test.mjs +0 -205
  157. package/agent.bak.20260905-190020/extensions/pi-web-access/test/ssrf-protection.test.mjs +0 -456
  158. package/agent.bak.20260905-190020/extensions/pi-web-access/test/summary-model-scope.test.mjs +0 -106
  159. package/agent.bak.20260905-190020/extensions/pi-web-access/test/tool-registration-config.test.mjs +0 -182
  160. package/agent.bak.20260905-190020/extensions/pi-web-access/test/web-search-answer-render.test.mjs +0 -66
  161. package/agent.bak.20260905-190020/extensions/pi-web-access/test/youtube-extract-errors.test.mjs +0 -64
  162. package/agent.bak.20260905-190020/extensions/pi-web-access/tsconfig.json +0 -11
  163. package/agent.bak.20260905-190020/extensions/pi-web-access/utils.ts +0 -451
  164. package/agent.bak.20260905-190020/extensions/pi-web-access/video-extract.ts +0 -392
  165. package/agent.bak.20260905-190020/extensions/pi-web-access/youtube-extract.ts +0 -328
  166. package/agent.bak.20260905-190020/extensions/shared/activity-status.ts +0 -31
  167. package/agent.bak.20260905-190020/extensions/shared/child-session.ts +0 -148
  168. package/agent.bak.20260905-190020/extensions/shared/context-utilization.ts +0 -47
  169. package/agent.bak.20260905-190020/extensions/shared/dashboard-state.ts +0 -99
  170. package/agent.bak.20260905-190020/extensions/shared/install.ps1 +0 -23
  171. package/agent.bak.20260905-190020/extensions/shared/install.sh +0 -21
  172. package/agent.bak.20260905-190020/extensions/shared/test/child-session.test.ts +0 -270
  173. package/agent.bak.20260905-190020/extensions/shared/test/context-utilization.test.ts +0 -48
  174. package/agent.bak.20260905-190020/extensions/shared/test/tool-call-timeout.test.ts +0 -117
  175. package/agent.bak.20260905-190020/extensions/shared/test/widget-order.test.ts +0 -167
  176. package/agent.bak.20260905-190020/extensions/shared/tool-call-timeout.ts +0 -104
  177. package/agent.bak.20260905-190020/extensions/skillsconf/README.md +0 -74
  178. package/agent.bak.20260905-190020/extensions/skillsconf/index.ts +0 -92
  179. package/agent.bak.20260905-190020/extensions/skillsconf/install.ps1 +0 -23
  180. package/agent.bak.20260905-190020/extensions/skillsconf/install.sh +0 -21
  181. package/agent.bak.20260905-190020/extensions/skillsconf/package-lock.json +0 -1809
  182. package/agent.bak.20260905-190020/extensions/skillsconf/package.json +0 -18
  183. package/agent.bak.20260905-190020/extensions/skillsconf/src/delete-skill.test.ts +0 -64
  184. package/agent.bak.20260905-190020/extensions/skillsconf/src/delete-skill.ts +0 -45
  185. package/agent.bak.20260905-190020/extensions/skillsconf/src/filter.test.ts +0 -80
  186. package/agent.bak.20260905-190020/extensions/skillsconf/src/filter.ts +0 -74
  187. package/agent.bak.20260905-190020/extensions/skillsconf/src/fuzzy.ts +0 -26
  188. package/agent.bak.20260905-190020/extensions/skillsconf/src/persistence.test.ts +0 -72
  189. package/agent.bak.20260905-190020/extensions/skillsconf/src/persistence.ts +0 -169
  190. package/agent.bak.20260905-190020/extensions/skillsconf/src/ui/SkillConfView.test.ts +0 -337
  191. package/agent.bak.20260905-190020/extensions/skillsconf/src/ui/SkillConfView.ts +0 -758
  192. package/agent.bak.20260905-190020/extensions/skillsconf/src/ui/text-input.ts +0 -91
  193. package/agent.bak.20260905-190020/extensions/skillsconf/src/ui/tui-helpers.ts +0 -144
  194. package/agent.bak.20260905-190020/extensions/skillsconf/tsconfig.json +0 -15
  195. package/agent.bak.20260905-190020/extensions/statusline/index.ts +0 -282
  196. package/agent.bak.20260905-190020/extensions/statusline/install.ps1 +0 -23
  197. package/agent.bak.20260905-190020/extensions/statusline/install.sh +0 -21
  198. package/agent.bak.20260905-190020/extensions/subagents/docs/design-plan.md +0 -568
  199. package/agent.bak.20260905-190020/extensions/subagents/docs/effect-v4-extension-guide.md +0 -354
  200. package/agent.bak.20260905-190020/extensions/subagents/docs/effect-v4-notes.md +0 -571
  201. package/agent.bak.20260905-190020/extensions/subagents/index.ts +0 -826
  202. package/agent.bak.20260905-190020/extensions/subagents/install.ps1 +0 -23
  203. package/agent.bak.20260905-190020/extensions/subagents/install.sh +0 -21
  204. package/agent.bak.20260905-190020/extensions/subagents/package-lock.json +0 -2244
  205. package/agent.bak.20260905-190020/extensions/subagents/package.json +0 -19
  206. package/agent.bak.20260905-190020/extensions/subagents/src/backend.ts +0 -73
  207. package/agent.bak.20260905-190020/extensions/subagents/src/backends/claude.ts +0 -701
  208. package/agent.bak.20260905-190020/extensions/subagents/src/backends/codex.ts +0 -1060
  209. package/agent.bak.20260905-190020/extensions/subagents/src/backends/pi.ts +0 -575
  210. package/agent.bak.20260905-190020/extensions/subagents/src/backends/stub.ts +0 -300
  211. package/agent.bak.20260905-190020/extensions/subagents/src/by-the-way.ts +0 -21
  212. package/agent.bak.20260905-190020/extensions/subagents/src/domain.ts +0 -253
  213. package/agent.bak.20260905-190020/extensions/subagents/src/format.ts +0 -74
  214. package/agent.bak.20260905-190020/extensions/subagents/src/manager.ts +0 -736
  215. package/agent.bak.20260905-190020/extensions/subagents/src/prompt.ts +0 -92
  216. package/agent.bak.20260905-190020/extensions/subagents/src/result-delivery.ts +0 -20
  217. package/agent.bak.20260905-190020/extensions/subagents/src/runtime.ts +0 -53
  218. package/agent.bak.20260905-190020/extensions/subagents/src/ui/takeover.ts +0 -583
  219. package/agent.bak.20260905-190020/extensions/subagents/src/ui/transcript.ts +0 -201
  220. package/agent.bak.20260905-190020/extensions/subagents/src/widget-order.ts +0 -121
  221. package/agent.bak.20260905-190020/extensions/subagents/test/by-the-way.test.ts +0 -29
  222. package/agent.bak.20260905-190020/extensions/subagents/test/claude.test.ts +0 -119
  223. package/agent.bak.20260905-190020/extensions/subagents/test/codex.test.ts +0 -102
  224. package/agent.bak.20260905-190020/extensions/subagents/test/context-usage.test.ts +0 -107
  225. package/agent.bak.20260905-190020/extensions/subagents/test/manager.test.ts +0 -276
  226. package/agent.bak.20260905-190020/extensions/subagents/test/result-delivery.test.ts +0 -27
  227. package/agent.bak.20260905-190020/extensions/subagents/test/takeover.test.ts +0 -29
  228. package/agent.bak.20260905-190020/extensions/subagents/tsconfig.json +0 -7
  229. package/agent.bak.20260905-190020/extensions/taste/index.ts +0 -443
  230. package/agent.bak.20260905-190020/extensions/taste/install.ps1 +0 -23
  231. package/agent.bak.20260905-190020/extensions/taste/install.sh +0 -21
  232. package/agent.bak.20260905-190020/extensions/todo/AGENTS.md +0 -38
  233. package/agent.bak.20260905-190020/extensions/todo/LICENSE +0 -21
  234. package/agent.bak.20260905-190020/extensions/todo/config.ts +0 -55
  235. package/agent.bak.20260905-190020/extensions/todo/index.ts +0 -151
  236. package/agent.bak.20260905-190020/extensions/todo/install.ps1 +0 -23
  237. package/agent.bak.20260905-190020/extensions/todo/install.sh +0 -21
  238. package/agent.bak.20260905-190020/extensions/todo/locales/de.json +0 -17
  239. package/agent.bak.20260905-190020/extensions/todo/locales/en.json +0 -15
  240. package/agent.bak.20260905-190020/extensions/todo/locales/es.json +0 -17
  241. package/agent.bak.20260905-190020/extensions/todo/locales/fr.json +0 -17
  242. package/agent.bak.20260905-190020/extensions/todo/locales/pt-BR.json +0 -17
  243. package/agent.bak.20260905-190020/extensions/todo/locales/pt.json +0 -17
  244. package/agent.bak.20260905-190020/extensions/todo/locales/ru.json +0 -17
  245. package/agent.bak.20260905-190020/extensions/todo/locales/uk.json +0 -17
  246. package/agent.bak.20260905-190020/extensions/todo/locales/zh.json +0 -17
  247. package/agent.bak.20260905-190020/extensions/todo/package-lock.json +0 -3358
  248. package/agent.bak.20260905-190020/extensions/todo/package.json +0 -67
  249. package/agent.bak.20260905-190020/extensions/todo/state/i18n-bridge.ts +0 -64
  250. package/agent.bak.20260905-190020/extensions/todo/state/invariants.ts +0 -20
  251. package/agent.bak.20260905-190020/extensions/todo/state/replay.ts +0 -38
  252. package/agent.bak.20260905-190020/extensions/todo/state/selectors.ts +0 -107
  253. package/agent.bak.20260905-190020/extensions/todo/state/state-reducer.ts +0 -326
  254. package/agent.bak.20260905-190020/extensions/todo/state/state.ts +0 -18
  255. package/agent.bak.20260905-190020/extensions/todo/state/store.ts +0 -82
  256. package/agent.bak.20260905-190020/extensions/todo/state/task-graph.ts +0 -57
  257. package/agent.bak.20260905-190020/extensions/todo/todo-overlay.ts +0 -204
  258. package/agent.bak.20260905-190020/extensions/todo/todo.ts +0 -155
  259. package/agent.bak.20260905-190020/extensions/todo/tool/response-envelope.ts +0 -109
  260. package/agent.bak.20260905-190020/extensions/todo/tool/types.ts +0 -206
  261. package/agent.bak.20260905-190020/extensions/todo/view/format.ts +0 -177
  262. package/agent.bak.20260905-190020/extensions/todo/widget-order.ts +0 -121
  263. package/agent.bak.20260905-190020/extensions/trim-context/README.md +0 -54
  264. package/agent.bak.20260905-190020/extensions/trim-context/index.ts +0 -487
  265. package/agent.bak.20260905-190020/extensions/trim-context/install.ps1 +0 -23
  266. package/agent.bak.20260905-190020/extensions/trim-context/install.sh +0 -21
  267. package/agent.bak.20260905-190020/keybindings.json +0 -7
@@ -1,915 +0,0 @@
1
- # modelconf Extension Implementation Plan
2
-
3
- > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
-
5
- **Goal:** Build a `modelconf` pi extension in its own folder (`~/.pi/agent/extensions/modelconf/`) that provides a TUI for browsing models per-provider, fuzzy-filtering, toggling hidden/visible with persisting via `enabledModels`, and bulk glob include/exclude.
6
-
7
- **Architecture:** Single extension folder `modelconf/` with `index.ts` entry registering `/modelconf` command. A state helper reads/writes `settings.json` (`enabledModels`) and diffs against `ctx.modelRegistry.getAll()` vs `getAvailable()`. A full-screen custom TUI component (`ModelConfView`) renders provider-grouped list; sub-overlays handle `/` fuzzy search and `x` glob menu. Fuzzy matching is a local `fuzzyMatch()` (subsequence + scoring) to avoid extra deps. Glob uses `minimatch` or simple `*`->RegExp conversion. Save calls `settingsManager.setEnabledModels()` if available else fs write + `ctx.reload()`.
8
-
9
- **Tech Stack:** TypeScript (strict), `@earendil-works/pi-coding-agent` ExtensionAPI, `@earendil-works/pi-tui` (Container, Input, theme), `typebox` for tool params (not needed), Node `fs/promises`, `os`, `path`. No new runtime deps except optional `minimatch` (prefer zero-dep includes).
10
-
11
- ## Global Constraints
12
-
13
- - Extension must be in its own folder named `modelconf` — discovery via `~/.pi/agent/extensions/` and `settings.json` `extensions: ["extensions/"]`.
14
- - Must support per-provider grouping and indicate visible vs hidden per `enabledModels`.
15
- - Fuzzy search triggered by `/`, hide/show via `Space`, save via `Enter`, bulk glob menu via `x`, close via `Esc`/`q`.
16
- - Persistence must survive reload and affect `pi --list-models` / model picker (i.e., write to settings).
17
- - Follow existing extension patterns in `~/.pi/agent/extensions/ask-user/index.ts` (default export function, `ctx.ui.custom` with `(tui, theme, keybindings, done)`).
18
- - Keep extension import-light; do not add `effect` unless justified.
19
-
20
- ---
21
-
22
- ## File Structure
23
-
24
- ```
25
- ~/.pi/agent/extensions/modelconf/
26
- ├── package.json # type: module, private, scripts: check/build, deps
27
- ├── tsconfig.json # extends strict, outDir dist, rootDir .
28
- ├── index.ts # Extension entry: registers /modelconf, owns state + launch
29
- ├── src/
30
- │ ├── persistence.ts # loadEnabledModels(), saveEnabledModels(), isVisible(), diff
31
- │ ├── fuzzy.ts # fuzzyScore, fuzzyFilter, highlight helpers
32
- │ ├── glob.ts # globToRegExp / matchesGlob, bulk apply helpers
33
- │ └── ui/
34
- │ ├── ModelConfView.ts # Main TUI Component: grouped list, key handling, renders
35
- │ ├── SearchOverlay.ts # Optional: / search input overlay component
36
- │ └── GlobMenu.ts # Optional: x bulk dialog (input + include/exclude choice)
37
- └── README.md # usage / keybindings
38
- ```
39
-
40
- Modify (no deletion of unrelated files):
41
- - `~/.pi/agent/settings.json` — read/write only via extension at runtime (no committed change), but plan verifies write path.
42
-
43
- Existing patterns to reuse:
44
- - `examples/extensions/tools.ts` — `SettingsList` toggle pattern (reference for persist + `pi.appendEntry` branch restore) — read before Task 5.
45
- - `examples/extensions/preset.ts` — `ctx.ui.custom` + `Container` + header rendering.
46
- - `~/.pi/agent/extensions/ask-user/index.ts` — `ctx.ui.custom` returning `{render, handleInput, invalidate}` object, `tui.requestRender()` after state change.
47
-
48
- ---
49
-
50
- ### Task 1: Scaffold `modelconf` extension folder
51
-
52
- **Files:**
53
- - Create: `./package.json`
54
- - Create: `./tsconfig.json`
55
- - Create: `./index.ts` (skeleton)
56
- - Create: `./README.md`
57
- - Create: `./src/persistence.ts` (empty stub)
58
- - Create: `./src/fuzzy.ts` (empty stub)
59
- - Create: `./src/glob.ts` (empty stub)
60
- - Create: `./src/ui/ModelConfView.ts` (empty stub)
61
-
62
- **Interfaces:**
63
- - Consumes: none
64
- - Produces: extension is discoverable via `/reload`, registers `/modelconf` placeholder that opens empty custom UI (used by all later tasks).
65
-
66
- - [ ] **Step 1: Create folder structure**
67
-
68
- ```bash
69
- mkdir -p ~/.pi/agent/extensions/modelconf/src/ui
70
- ```
71
-
72
- - [ ] **Step 2: Write `package.json`**
73
-
74
- ```json
75
- {
76
- "name": "modelconf",
77
- "private": true,
78
- "type": "module",
79
- "version": "0.1.0",
80
- "description": "Per-provider model visibility manager with fuzzy search and bulk glob",
81
- "scripts": { "check": "tsc --noEmit" },
82
- "dependencies": {},
83
- "devDependencies": { "typescript": "^5.6.0" },
84
- "allowScripts": {}
85
- }
86
- ```
87
-
88
- - [ ] **Step 3: Write `tsconfig.json`**
89
-
90
- ```json
91
- {
92
- "compilerOptions": {
93
- "target": "ES2022",
94
- "module": "NodeNext",
95
- "moduleResolution": "NodeNext",
96
- "strict": true,
97
- "esModuleInterop": true,
98
- "skipLibCheck": true,
99
- "outDir": "dist",
100
- "rootDir": ".",
101
- "types": ["node"]
102
- },
103
- "include": ["index.ts", "src/**/*.ts"],
104
- "exclude": ["dist", "node_modules"]
105
- }
106
- ```
107
-
108
- - [ ] **Step 4: Write skeleton `index.ts` that registers `/modelconf` and shows placeholder UI**
109
-
110
- ```typescript
111
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
112
-
113
- export default function modelconf(pi: ExtensionAPI) {
114
- pi.registerCommand("modelconf", {
115
- description: "Manage model visibility per provider (fuzzy search: /, toggle: space, bulk: x, save: enter)",
116
- handler: async (_args, ctx) => {
117
- if (ctx.mode !== "tui") {
118
- ctx.ui.notify("/modelconf requires TUI mode", "error");
119
- return;
120
- }
121
- await ctx.ui.custom((tui, theme, _kb, done) => {
122
- let closed = false;
123
- return {
124
- render(width: number) {
125
- const title = theme.fg("accent", theme.bold("modelconf — scaffold"));
126
- const hint = theme.fg("dim", "Press Esc to close · upcoming: / fuzzy, space toggle, x glob, enter save");
127
- return [title, hint, ""];
128
- },
129
- invalidate() {},
130
- handleInput(data: string) {
131
- if (data === "\x1b" || data === "q") { if (!closed) { closed = true; done(undefined); } }
132
- tui.requestRender();
133
- },
134
- };
135
- });
136
- },
137
- });
138
- }
139
- ```
140
-
141
- - [ ] **Step 5: Verify discovery**
142
-
143
- Run: `pi --help` (check no load error) then inside pi run `/reload` — expect no error and `/modelconf` appears in command list. Test: `pi -e ~/.pi/agent/extensions/modelconf --help` alternative smoke.
144
-
145
- - [ ] **Step 6: Commit**
146
-
147
- ```
148
- feat(modelconf): scaffold extension folder with /modelconf placeholder
149
- ```
150
-
151
- ---
152
-
153
- ### Task 2: Persistence helper — map `enabledModels` to visible/hidden
154
-
155
- **Files:**
156
- - Create: `./src/persistence.ts`
157
- - Modify: `./index.ts` (import helper, log counts — no UI yet)
158
- - Test: `./src/persistence.test.ts` (manual node test script, since plan forbids one-off bash tests — write a dedicated test file)
159
-
160
- **Interfaces:**
161
- - Consumes: `ctx.modelRegistry.getAll(): Model[]`, `ctx.modelRegistry.getAvailable(): Model[]`, `settings.json` on disk
162
- - Produces:
163
- ```typescript
164
- // persistence.ts
165
- export type ModelId = `${string}/${string}`; // provider/modelId
166
- export function toModelId(m: { provider: string; id: string }): ModelId;
167
- export function loadEnabledModels(): Promise<string[] | undefined>;
168
- export function saveEnabledModels(ids: string[] | undefined): Promise<void>;
169
- export function isVisible(modelId: ModelId, enabled: string[] | undefined, allIds: ModelId[]): boolean;
170
- export function getVisibilityMap(all: Array<{provider:string;id:string}>, enabled: string[]|undefined): Map<ModelId, boolean>;
171
- export function toggleInEnabled(modelId: ModelId, enabled: string[] | undefined, allIds: ModelId[]): string[] | undefined;
172
- // helpers for glob bulk — consumed by Task 6/8
173
- export function applyBulk(enabled: string[]|undefined, allIds: ModelId[], matching: ModelId[], action: "include"|"exclude"): string[]|undefined;
174
- ```
175
-
176
- - [ ] **Step 1: Write failing test for visibility semantics**
177
-
178
- ```typescript
179
- // src/persistence.test.ts
180
- import { isVisible, toModelId } from "./persistence.js";
181
- import assert from "node:assert";
182
- const all = ["opencode/a","opencode/b","9router/c"] as const;
183
- // enabled undefined => all visible
184
- assert.equal(isVisible("opencode/a", undefined, [...all] as any), true);
185
- // empty array => none visible (pi convention: empty means none? verify — if pi treats empty as none, mirror that)
186
- assert.equal(isVisible("opencode/a", [], [...all] as any), false);
187
- assert.equal(isVisible("opencode/a", ["opencode/a"], [...all] as any), true);
188
- assert.equal(isVisible("opencode/b", ["opencode/a"], [...all] as any), false);
189
- console.log("persistence test pass");
190
- ```
191
-
192
- - [ ] **Step 2: Run test to verify it fails** (module not implemented)
193
-
194
- Run: `npx tsc --noEmit` then `node --loader tsx src/persistence.test.ts` — expected FAIL `cannot find module`.
195
-
196
- - [ ] **Step 3: Implement `src/persistence.ts` minimal**
197
-
198
- ```typescript
199
- import fs from "node:fs/promises";
200
- import os from "node:os";
201
- import path from "node:path";
202
-
203
- export type ModelId = `${string}/${string}`;
204
-
205
- export function toModelId(m: { provider: string; id: string }): ModelId {
206
- return `${m.provider}/${m.id}` as ModelId;
207
- }
208
-
209
- function settingsPath(): string {
210
- // Global settings — matches pi default. Project-local .pi/settings.json intentionally not handled in v1; document it.
211
- return path.join(os.homedir(), ".pi", "agent", "settings.json");
212
- }
213
-
214
- export async function loadEnabledModels(): Promise<string[] | undefined> {
215
- try {
216
- const raw = await fs.readFile(settingsPath(), "utf8");
217
- const j = JSON.parse(raw) as { enabledModels?: string[] };
218
- return j.enabledModels;
219
- } catch { return undefined; }
220
- }
221
-
222
- export async function saveEnabledModels(ids: string[] | undefined): Promise<void> {
223
- const p = settingsPath();
224
- const raw = await fs.readFile(p, "utf8");
225
- const j = JSON.parse(raw) as Record<string, unknown>;
226
- if (ids === undefined) delete j.enabledModels;
227
- else j.enabledModels = ids;
228
- await fs.writeFile(p, JSON.stringify(j, null, 2) + "\n", "utf8");
229
- }
230
-
231
- export function isVisible(modelId: ModelId, enabled: string[] | undefined, _allIds: ModelId[]): boolean {
232
- if (enabled === undefined) return true; // no filter => all visible
233
- return enabled.includes(modelId);
234
- }
235
-
236
- export function getVisibilityMap(all: Array<{provider:string;id:string}>, enabled: string[]|undefined): Map<ModelId, boolean> {
237
- const map = new Map<ModelId, boolean>();
238
- const allIds = all.map(toModelId);
239
- for (const m of all) map.set(toModelId(m), isVisible(toModelId(m), enabled, allIds));
240
- return map;
241
- }
242
-
243
- export function toggleInEnabled(modelId: ModelId, enabled: string[] | undefined, allIds: ModelId[]): string[] | undefined {
244
- if (enabled === undefined) {
245
- // First toggle while all visible => materialize as "all except one hidden"
246
- return allIds.filter(id => id !== modelId);
247
- }
248
- if (enabled.includes(modelId)) return enabled.filter(id => id !== modelId);
249
- return [...enabled, modelId];
250
- }
251
-
252
- export function applyBulk(enabled: string[]|undefined, allIds: ModelId[], matching: ModelId[], action: "include"|"exclude"): string[]|undefined {
253
- if (action === "include") {
254
- const base = enabled === undefined ? [...allIds] : [...enabled];
255
- const set = new Set(base);
256
- for (const id of matching) set.add(id);
257
- const arr = [...set];
258
- // if all visible, store undefined to keep settings clean
259
- if (arr.length === allIds.length) return undefined;
260
- return arr;
261
- } else {
262
- if (enabled === undefined) return allIds.filter(id => !matching.includes(id));
263
- return enabled.filter(id => !matching.includes(id));
264
- }
265
- }
266
- ```
267
-
268
- Note: Verify against real pi semantics for `enabledModels: []` vs `undefined` by reading `SettingsManager.getEnabledModels()` impl in `dist/modes/interactive/interactive-mode.js:4184` — if empty means "filter to none", the above is correct; if empty is treated as "no filter", adjust `isVisible`.
269
-
270
- - [ ] **Step 4: Run test again — expect PASS**
271
-
272
- Run: `npx tsc --noEmit` → PASS, then `node --loader tsx src/persistence.test.ts`.
273
-
274
- - [ ] **Step 5: Wire a debug log into `index.ts` handler**
275
-
276
- In `/modelconf` handler, before `ctx.ui.custom`, do:
277
- ```typescript
278
- const all = ctx.modelRegistry.getAll().map(m => ({ provider: (m as any).provider, id: m.id }));
279
- const avail = ctx.modelRegistry.getAvailable().map(m => `${(m as any).provider}/${m.id}`);
280
- console.log(`[modelconf] all=${all.length} avail=${avail.length}`);
281
- ```
282
- Verify in pi logs.
283
-
284
- - [ ] **Step 6: Commit**
285
-
286
- ```
287
- feat(modelconf): add persistence helper for enabledModels mapping
288
- ```
289
-
290
- ---
291
-
292
- ### Task 3: Fuzzy search utility (`/`)
293
-
294
- **Files:**
295
- - Create: `./src/fuzzy.ts`
296
- - Test: `./src/fuzzy.test.ts`
297
-
298
- **Interfaces:**
299
- - Consumes: array of `ModelId` strings
300
- - Produces:
301
- ```typescript
302
- export function fuzzyScore(query: string, target: string): number | null; // null = no match
303
- export function fuzzyFilter<T>(query: string, items: T[], key: (t:T)=>string): T[]; // sorted by score
304
- export function highlightMatch(target: string, query: string, theme: any): string; // optional
305
- ```
306
-
307
- - [ ] **Step 1: Write failing test**
308
-
309
- ```typescript
310
- import { fuzzyScore, fuzzyFilter } from "./fuzzy.js";
311
- import assert from "node:assert";
312
- assert.ok(fuzzyScore("gpl", "gpt-5.6-luna") === null);
313
- assert.ok(typeof fuzzyScore("lun", "gpt-5.6-luna") === "number");
314
- const out = fuzzyFilter("lun", [{id:"gpt-5.6-luna"},{id:"minimax-m3"}], x=>x.id);
315
- assert.equal(out[0].id, "gpt-5.6-luna");
316
- console.log("fuzzy pass");
317
- ```
318
-
319
- - [ ] **Step 2: Run to confirm fail**
320
- - [ ] **Step 3: Implement subsequence scorer (no deps)**
321
-
322
- ```typescript
323
- export function fuzzyScore(query: string, target: string): number | null {
324
- if (!query) return 0;
325
- const q = query.toLowerCase();
326
- const t = target.toLowerCase();
327
- let qi = 0, ti = 0, score = 0, last = -2;
328
- for (; qi < q.length && ti < t.length; ti++) {
329
- if (q[qi] === t[ti]) {
330
- // bonus for contiguous + start-of-word
331
- score += (ti === last + 1 ? 2 : 1);
332
- if (ti === 0 || t[ti-1] === "/" || t[ti-1] === "-" || t[ti-1] === "_") score += 2;
333
- last = ti; qi++;
334
- }
335
- }
336
- if (qi !== q.length) return null;
337
- // penalize longer targets slightly
338
- score -= Math.floor(t.length / 20);
339
- return score;
340
- }
341
-
342
- export function fuzzyFilter<T>(query: string, items: T[], key: (t:T)=>string): T[] {
343
- if (!query.trim()) return items;
344
- const scored = items.map(i => ({ i, s: fuzzyScore(query, key(i)) }))
345
- .filter(x => x.s !== null) as Array<{i:T; s:number}>;
346
- scored.sort((a,b)=> b.s - a.s);
347
- return scored.map(x=>x.i);
348
- }
349
- ```
350
-
351
- - [ ] **Step 4: Run test pass + check `npx tsc --noEmit`**
352
-
353
- - [ ] **Step 5: Commit**
354
-
355
- ```
356
- feat(modelconf): add fuzzyScore / fuzzyFilter utility
357
- ```
358
-
359
- ---
360
-
361
- ### Task 4: Glob helper (`x` bulk)
362
-
363
- **Files:**
364
- - Create: `./src/glob.ts`
365
- - Test: `./src/glob.test.ts`
366
-
367
- **Interfaces:**
368
- - Produces:
369
- ```typescript
370
- export function matchesGlob(input: string, pattern: string): boolean;
371
- export function filterByGlob<T>(pattern: string, items: T[], key:(t:T)=>string): T[];
372
- ```
373
-
374
- - [ ] **Step 1: Test**
375
-
376
- ```typescript
377
- import { matchesGlob } from "./glob.js";
378
- import assert from "node:assert";
379
- assert.equal(matchesGlob("gpt-5.6-luna", "*luna*"), true);
380
- assert.equal(matchesGlob("gpt-5.6-luna", "gpt*"), true);
381
- assert.equal(matchesGlob("minimax-m3", "*luna*"), false);
382
- assert.equal(matchesGlob("a/b/c", "*"), true);
383
- console.log("glob pass");
384
- ```
385
-
386
- - [ ] **Step 2: Implement (zero-dep, * and ? plus case-insensitive includes fallback)**
387
-
388
- ```typescript
389
- export function globToRegExp(pattern: string): RegExp {
390
- // Support keyword without * as implicit *keyword* (per spec: "glob a keyword")
391
- let p = pattern;
392
- if (!p.includes("*") && !p.includes("?")) p = `*${p}*`;
393
- const esc = p.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
394
- return new RegExp(`^${esc}$`, "i");
395
- }
396
- export function matchesGlob(input: string, pattern: string): boolean {
397
- return globToRegExp(pattern).test(input);
398
- }
399
- export function filterByGlob<T>(pattern: string, items: T[], key:(t:T)=>string): T[] {
400
- const rx = globToRegExp(pattern);
401
- return items.filter(i => rx.test(key(i)));
402
- }
403
- ```
404
-
405
- Document that `*` semantics covers the spec's "keyword" as substring.
406
-
407
- - [ ] **Step 3: Verify pass + tsc**
408
- - [ ] **Step 4: Commit**
409
-
410
- ```
411
- feat(modelconf): add glob helper for bulk keyword matching
412
- ```
413
-
414
- ---
415
-
416
- ### Task 5: Main TUI view — per-provider grouping + navigation
417
-
418
- **Files:**
419
- - Create: `./src/ui/ModelConfView.ts`
420
- - Modify: `./index.ts` (delegate to ModelConfView)
421
- - Test: manual TUI test (`/modelconf` visual)
422
-
423
- **Interfaces:**
424
- - Consumes: `persistence` helpers, `fuzzy` helpers, `glob` helpers, `ExtensionContext` modelRegistry + `loadEnabledModels`
425
- - Produces: class `ModelConfView implements Component` with contract:
426
-
427
- ```typescript
428
- export interface ModelRow { id: string; provider: string; modelId: string; visible: boolean; }
429
- export class ModelConfView implements Component {
430
- constructor(opts: {
431
- tui: TUI; theme: Theme; keybindings: KeybindingsManager;
432
- allModels: Array<{provider:string; id:string; name?:string}>;
433
- visibleSet: Set<string>;
434
- enabledRaw: string[]|undefined;
435
- onDone: (saved: boolean)=>void;
436
- onPersist: (nextEnabled: string[]|undefined)=>Promise<void>;
437
- });
438
- render(width:number): string[];
439
- handleInput(data:string): void;
440
- invalidate(): void;
441
- }
442
- ```
443
-
444
- Rendering rules:
445
- - Group by `provider` sorted alphabetically; providers collapsible? v1: all expanded, header line per provider: `▸ provider (3/10 visible)` in accent color.
446
- - Rows: ` [x] modelId — name` where `[x]` is `■`/`□` or `☑`/`☐` with `visible ? success : dim`. Use `theme.fg`.
447
- - Cursor line highlighted with `theme.bg("selection")` or `theme.fg("accent")`.
448
- - Footer legend: `/ search space toggle x glob enter save esc cancel ↑↓ navigate`.
449
- - Limit height to `tui` height minus chrome; handle scrolling window.
450
-
451
- Key handling (for this task — aiming for navigation only, search/toggle/bulk deferred):
452
- - `j`/`ArrowDown`, `k`/`ArrowUp` move cursor (also handle Kitty keys from `keybindings`? check `Keyboard`).
453
- - `g`/`Home` top, `G`/`End` bottom.
454
-
455
- - [ ] **Step 1: Read reference implementations**
456
-
457
- Open `examples/extensions/tools.ts` and `examples/extensions/preset.ts` and `src/persistence.ts` to confirm model id shape: `(m as any).provider` fallback to `m.provider` or `"unknown"`.
458
-
459
- - [ ] **Step 2: Write failing visual placeholder — launch ModelConfView from index.ts**
460
-
461
- In `index.ts` handler, construct `allModels` via:
462
- ```typescript
463
- const allModels = ctx.modelRegistry.getAll().map(m => ({
464
- provider: (m as any).provider ?? "unknown",
465
- id: m.id,
466
- name: (m as any).name ?? m.id,
467
- }));
468
- const enabledRaw = await loadEnabledModels();
469
- const map = getVisibilityMap(allModels, enabledRaw);
470
- ```
471
-
472
- Pass to `new ModelConfView({ ... })` and return its `{render, handleInput, invalidate}`.
473
-
474
- - [ ] **Step 3: Implement ModelConfView — grouped list + scroll + selection**
475
-
476
- Pseudocode for `render(width)`:
477
- ```typescript
478
- render(width:number): string[] {
479
- const lines: string[] = [];
480
- lines.push(theme.fg("accent", theme.bold("modelconf")) + theme.fg("dim", ` ${this.rows.length} models ${this.visibleCount} visible`));
481
- lines.push(theme.fg("dim", "─".repeat(width)));
482
- for (const provider of this.sortedProviders) {
483
- const group = this.rowsByProvider.get(provider)!;
484
- const visibleInGroup = group.filter(r=>r.visible).length;
485
- lines.push(theme.fg(provider === this.currentProvider ? "accent" : "muted",
486
- `${this.isProviderCollapsed(provider) ? "▸" : "▾"} ${provider} ${visibleInGroup}/${group.length}`));
487
- if (this.isProviderCollapsed(provider)) continue;
488
- for (const row of group) {
489
- const isCursor = row === this.cursorRow();
490
- const mark = row.visible ? theme.fg("success", "◉") : theme.fg("dim", "○");
491
- const label = `${mark} ${row.modelId}`.padEnd(40) + theme.fg("dim", row.name ?? "");
492
- const line = isCursor ? theme.bg("selection", label) : label;
493
- lines.push(theme.fg(isCursor ? "accent" : undefined as any, ` ${line}`));
494
- }
495
- }
496
- lines.push("", theme.fg("dim", "/ search space toggle x bulk enter save esc cancel"));
497
- return lines.map(l => truncateToWidth(l, width));
498
- }
499
- ```
500
-
501
- Cursor math: flat index over `this.flatRows` (filtered view later expands to include search).
502
-
503
- `handleInput` v1: only up/down/g/q/esc; other keys `done(false)` on esc.
504
-
505
- Call `tui.requestRender()` after any state change.
506
-
507
- - [ ] **Step 4: Test in pi**
508
-
509
- Commands:
510
- ```bash
511
- pi
512
- /modelconf
513
- ```
514
- Verify:
515
- - Providers appear grouped.
516
- - Counts correct vs `cat ~/.pi/agent/settings.json | grep enabledModels`.
517
- - Arrow keys move highlight, no crash on long list (test with `oc` provider with many models).
518
-
519
- - [ ] **Step 5: Handle `width` overflow — truncate row labels to `width-6`**
520
-
521
- Use `truncateToWidth` from `@earendil-works/pi-tui`.
522
-
523
- - [ ] **Step 6: Commit**
524
-
525
- ```
526
- feat(modelconf): main TUI view with per-provider grouping and navigation
527
- ```
528
-
529
- ---
530
-
531
- ### Task 6: Toggle (`Space`) + Save (`Enter`)
532
-
533
- **Files:**
534
- - Modify: `./src/ui/ModelConfView.ts`
535
- - Modify: `./src/persistence.ts` (if toggle semantics need fix)
536
- - Modify: `./index.ts` (persist callback)
537
-
538
- **Interfaces:**
539
- - Consumes: `toggleInEnabled`, `saveEnabledModels`
540
- - Produces: updated `enabledRaw` inside ModelConfView, flush on Enter.
541
-
542
- Behavior:
543
- - `Space` toggles current row's `visible`; updates internal `draftEnabled` (copy of loaded `enabledRaw` mutated via `toggleInEnabled`). Recomputes `visibleSet`. Does not yet write to disk.
544
- - Dirty indicator: title shows `● unsaved` when `draftEnabled !== original`.
545
- - `Enter` calls `onPersist(draftEnabled)` → `saveEnabledModels` → `ctx.ui.notify("Saved N visible", "success")` → optional `ctx.reload()` to refresh model picker. Then `done(true)`.
546
- - `Esc` discards if dirty: prompt confirm via inline footer "Discard changes? (y/N)" — simplest: `Esc` when dirty shows confirmation overlay; second `Esc` discards, `y` discards.
547
-
548
- - [ ] **Step 1: Write unit test for draft toggling**
549
-
550
- ```typescript
551
- import { toggleInEnabled } from "./persistence.js";
552
- let draft: string[]|undefined = undefined;
553
- const all = ["a/x","a/y","b/z"] as any;
554
- draft = toggleInEnabled("a/x", draft, all);
555
- assert.deepEqual(draft, ["a/y","b/z"]); // all-except-x
556
- draft = toggleInEnabled("a/x", draft, all);
557
- assert.ok(draft!.includes("a/x"));
558
- console.log("toggle draft pass");
559
- ```
560
-
561
- - [ ] **Step 2: Implement Space handling in ModelConfView.handleInput**
562
-
563
- ```typescript
564
- if (data === " ") {
565
- const row = this.cursorRow(); if (!row) return;
566
- const modelId = `${row.provider}/${row.modelId}` as ModelId;
567
- this.draftEnabled = toggleInEnabled(modelId, this.draftEnabled, this.allIds);
568
- this.rebuildVisibility();
569
- tui.requestRender();
570
- return;
571
- }
572
- if (data === "\r" || data === "\n") { // Enter
573
- await this.opts.onPersist(this.draftEnabled);
574
- this.opts.onDone(true);
575
- return;
576
- }
577
- ```
578
-
579
- - [ ] **Step 3: Implement dirty check + footer hint**
580
-
581
- ```typescript
582
- private isDirty(): boolean {
583
- const a = JSON.stringify(this.originalEnabled ?? null);
584
- const b = JSON.stringify(this.draftEnabled ?? null);
585
- return a !== b;
586
- }
587
- ```
588
-
589
- Render footer as `theme.fg(isDirty?"warning":"dim", isDirty ? "● unsaved — press Enter to save" : "enter save esc close")`.
590
-
591
- - [ ] **Step 4: Implement persist in `index.ts`**
592
-
593
- ```typescript
594
- onPersist: async (next) => {
595
- await saveEnabledModels(next);
596
- ctx.ui.notify(`modelconf: saved ${next === undefined ? allModels.length : next.length} enabled models — reload to apply`, "info");
597
- // Try graceful reload; ignore if ctx.reload unavailable
598
- try { await (ctx as any).reload?.(); } catch {}
599
- }
600
- ```
601
-
602
- - [ ] **Step 5: Manual test — toggle loop**
603
-
604
- In pi: `/modelconf`, press Space on 2 models, verify mark flips, footer shows unsaved, Enter saves, check `cat ~/.pi/agent/settings.json`, reopen `/modelconf` confirms persisted.
605
-
606
- - [ ] **Step 6: Commit**
607
-
608
- ```
609
- feat(modelconf): space toggles visibility and enter persists enabledModels
610
- ```
611
-
612
- ---
613
-
614
- ### Task 7: Fuzzy search (`/`)
615
-
616
- **Files:**
617
- - Modify: `./src/ui/ModelConfView.ts`
618
- - Create: `./src/ui/SearchOverlay.ts` (optional inline input; can be inline in ModelConfView)
619
- - Modify: `./index.ts` (no change, logic inside view)
620
-
621
- **Interfaces:**
622
- - Consumes: `fuzzyFilter` from `fuzzy.ts`
623
- - Produces: search mode inside ModelConfView (no new external API).
624
-
625
- Behavior:
626
- - Pressing `/` enters search mode: an `Input` (from `@earendil-works/pi-tui`) appears at top/bottom, focused, with IME support (`Focusable` container — propagate `focused` to child Input per `docs/tui.md` container note).
627
- - Typing filters `flatRows` via `fuzzyFilter(query, flatRows, r=>`${r.provider}/${r.modelId} ${r.name}`)`. Cursor resets to 0, scroll resets.
628
- - `Esc` exits search (clears query), `Enter` keeps filtered view but exits input focus, Backspace edits query.
629
- - Highlight matched rows? v1: just filtered list; v2 bonus: dim non-matching.
630
- - Empty query returns full provider-grouped view.
631
-
632
- - [ ] **Step 1: Verify docs/tui.md IME note — container must implement Focusable and propagate to Input**
633
-
634
- Code sketch:
635
-
636
- ```typescript
637
- import { Input, Container, type Focusable } from "@earendil-works/pi-tui";
638
-
639
- class SearchOverlay extends Container implements Focusable {
640
- input = new Input({ placeholder: "fuzzy search…", onChange: ()=> this.opts.onChange(this.input.value) });
641
- private _focused = false;
642
- get focused(){ return this._focused; }
643
- set focused(v:boolean){ this._focused = v; this.input.focused = v; }
644
- }
645
- ```
646
-
647
- Or simpler: keep search as inline string + handleInput char insertion without full Input component (avoid IME complexity) — decision: use Input for correctness.
648
-
649
- - [ ] **Step 2: Add state to ModelConfView**
650
-
651
- ```typescript
652
- private searchActive = false;
653
- private searchQuery = "";
654
- private searchInput = new Input();
655
- private filteredRows: ModelRow[] | null = null;
656
- ```
657
-
658
- In `render`, if `searchActive`, prepend line: `theme.fg("accent", "/") + searchInput.render(...)`.
659
-
660
- - [ ] **Step 3: Handle `/` key to enter search**
661
-
662
- ```typescript
663
- if (!this.searchActive && data === "/") {
664
- this.searchActive = true;
665
- this.searchInput.focused = true;
666
- this.searchInput.value = "";
667
- tui.requestRender(); return;
668
- }
669
- if (this.searchActive) {
670
- if (data === "\x1b") { this.searchActive = false; this.searchInput.focused = false; this.filteredRows = null; this.searchQuery=""; }
671
- else if (data === "\r") { this.searchActive = false; this.searchInput.focused = false; }
672
- else { this.searchInput.handleInput?.(data); this.searchQuery = this.searchInput.value; this.filteredRows = fuzzyFilter(this.searchQuery, this.flatRows, r=>`${r.provider}/${r.modelId}`); this.cursorIndex = 0; }
673
- tui.requestRender(); return;
674
- }
675
- ```
676
-
677
- Route `handleInput` early to `searchInput` when active.
678
-
679
- - [ ] **Step 4: Update rendering to use `filteredRows ?? flatRows` for flat navigation, but keep provider group rendering filtered (hide empty groups)**
680
-
681
- In `render`, compute `rowsToShow = this.filteredRows ?? this.flatRows` but group by provider from that set.
682
-
683
- - [ ] **Step 5: Manual test**
684
-
685
- `/modelconf` → `/` → type `luna` → expect only `gpt-5.6-luna` etc remain; `Esc` returns; arrow navigation works within filtered.
686
-
687
- - [ ] **Step 6: Commit**
688
-
689
- ```
690
- feat(modelconf): fuzzy search on / with Input overlay
691
- ```
692
-
693
- ---
694
-
695
- ### Task 8: Bulk glob menu (`x`)
696
-
697
- **Files:**
698
- - Modify: `./src/ui/ModelConfView.ts`
699
- - Create: `./src/ui/GlobMenu.ts`
700
- - Modify: `./src/persistence.ts` (already has `applyBulk`, now used)
701
- - Test: manual TUI
702
-
703
- **Interfaces:**
704
- - Consumes: `filterByGlob`, `applyBulk`, `loadEnabledModels`
705
- - Produces: overlay dialog returned from `handleInput` when `x` pressed.
706
-
707
- Behavior:
708
- - `x` opens a centered modal (`Container` with bordered box) containing:
709
- - Input line: `Glob/keyword: [________]` (placeholder `e.g. *luna* or luna`)
710
- - Count preview live: `Matches 12 models`
711
- - Two actions: `Include all matches` (enable) and `Exclude all matches` (disable), selectable via `Tab`/`Arrow` or `i`/`e` shortcuts, `Enter` confirms, `Esc` cancels.
712
- - Footer: `Enter confirm Esc cancel Tab switch action`
713
- - Pattern semantics: if pattern lacks `*`/`?`, treat as `*pattern*` (keyword substring, case-insensitive). This satisfies spec's "glob a keyword".
714
- - On confirm, compute `matching = filterByGlob(pattern, allRows, r=>`${r.provider}/${r.modelId}`)` → `modelId`s, then `draftEnabled = applyBulk(draftEnabled, allIds, matchingIds, action)` → rebuild visibility → close modal → show transient notify `Included 12 / Excluded 5`.
715
- - Does not auto-save; dirties draft like Space toggles (so Enter still needed to persist).
716
-
717
- Modal implementation notes:
718
- - Use `Container` + simple box drawing via `theme.fg("border", ...)` (check `examples/extensions/qna.ts` BorderedLoader pattern). Or reuse `SelectList` for action choice.
719
- - Modal captures all input until closed (set `inGlobMenu = true` flag to route `handleInput` to modal).
720
-
721
- - [ ] **Step 1: Write unit test for bulk include/exclude over keyword**
722
-
723
- ```typescript
724
- import { filterByGlob } from "./glob.js";
725
- import { applyBulk } from "./persistence.js";
726
- const all = ["a/luna-1","a/luna-2","a/other"] as any;
727
- const m = filterByGlob("luna", all.map(id=>({id})), x=>x.id);
728
- assert.equal(m.length, 2);
729
- let draft: string[]|undefined = undefined;
730
- draft = applyBulk(draft, all as any, ["a/luna-1","a/luna-2"] as any, "exclude");
731
- assert.deepEqual(draft, ["a/other"]);
732
- console.log("bulk pass");
733
- ```
734
-
735
- - [ ] **Step 2: Implement GlobMenu component**
736
-
737
- ```typescript
738
- export class GlobMenu extends Container implements Focusable {
739
- input = new Input({ placeholder: "*luna* or luna" });
740
- action: "include"|"exclude" = "exclude";
741
- matches: ModelRow[] = [];
742
- constructor(private opts: { allRows: ModelRow[]; onConfirm: (pattern:string, action:"include"|"exclude", matches:ModelRow[])=>void; onCancel: ()=>void; tui: TUI; theme: any; }) { super(); this.addChild(this.input); }
743
- // Focusable propagation...
744
- handleInput(data:string){ ... }
745
- render(width:number): string[] { /* bordered box 60 chars wide centered */ }
746
- }
747
- ```
748
-
749
- Simpler v1: keep menu logic inside `ModelConfView` without separate file — inline state:
750
-
751
- ```typescript
752
- private globOpen = false;
753
- private globPattern = "";
754
- private globAction: "include"|"exclude" = "exclude";
755
- private globInput = new Input();
756
- ```
757
-
758
- Render overlay as centered box when `globOpen`.
759
-
760
- - [ ] **Step 3: Wire `x` key in ModelConfView.handleInput**
761
-
762
- ```typescript
763
- if (!this.globOpen && data === "x") {
764
- this.globOpen = true; this.globInput.focused = true; this.globInput.value=""; this.globPattern=""; this.globAction="exclude";
765
- tui.requestRender(); return;
766
- }
767
- if (this.globOpen) {
768
- if (data === "\x1b") { this.globOpen=false; this.globInput.focused=false; tui.requestRender(); return; }
769
- if (data === "\t") { this.globAction = this.globAction==="exclude" ? "include" : "exclude"; tui.requestRender(); return; }
770
- if (data === "i") { this.globAction="include"; tui.requestRender(); return; }
771
- if (data === "e") { this.globAction="exclude"; tui.requestRender(); return; }
772
- if (data === "\r") {
773
- const pattern = this.globInput.value.trim();
774
- if (!pattern) return;
775
- const matches = filterByGlob(pattern, this.flatRows, r=>`${r.provider}/${r.modelId}`);
776
- this.draftEnabled = applyBulk(this.draftEnabled, this.allIds, matches.map(r=>`${r.provider}/${r.modelId}` as any), this.globAction);
777
- this.rebuildVisibility(); this.globOpen=false; this.globInput.focused=false;
778
- tui.requestRender(); return;
779
- }
780
- this.globInput.handleInput?.(data); tui.requestRender(); return;
781
- }
782
- ```
783
-
784
- Preview count: `const preview = filterByGlob(this.globInput.value || "*", this.flatRows, ...).length`.
785
-
786
- - [ ] **Step 4: Render modal overlay**
787
-
788
- In `render`, if `globOpen`, return overlay lines instead of main list OR composite: render main list dimmed then boxed menu. Simplest: return menu box full-screen centered:
789
-
790
- ```typescript
791
- if (this.globOpen) {
792
- const count = filterByGlob(this.globInput.value||"", this.flatRows, r=>`${r.provider}/${r.modelId}`).length;
793
- return [
794
- theme.fg("accent", theme.bold("Bulk glob — include / exclude")),
795
- theme.fg("dim", "Keyword or glob pattern (e.g. luna or *luna*)"),
796
- `> ${this.globInput.render?.(width) ?? this.globInput.value}_`,
797
- theme.fg("dim", `Matches: ${count} models`),
798
- "",
799
- this.globAction==="include" ? theme.fg("success", "▶ Include (enable)") : theme.fg("dim", " Include (enable)"),
800
- this.globAction==="exclude" ? theme.fg("warning", "▶ Exclude (disable)") : theme.fg("dim", " Exclude (disable)"),
801
- theme.fg("dim", "Tab switch · i/e shortcut · Enter apply · Esc cancel"),
802
- ];
803
- }
804
- ```
805
-
806
- Polish with `Container` border for v2.
807
-
808
- - [ ] **Step 5: Manual test**
809
-
810
- `/modelconf` → `x` → type `luna` → see count `2` → `Tab` to `Include` → `Enter` → verify those rows now `◉` visible; press `x` → `luna` → `Exclude` → rows become `○`; `Enter` to save → check settings.
811
-
812
- - [ ] **Step 6: Commit**
813
-
814
- ```
815
- feat(modelconf): bulk glob menu on x for include/exclude by keyword
816
- ```
817
-
818
- ---
819
-
820
- ### Task 9: Polish, key legend, empty states, persistence edge cases & docs
821
-
822
- **Files:**
823
- - Modify: `./src/ui/ModelConfView.ts` (legend, empty state, scroll window, provider collapse via Enter? optional)
824
- - Modify: `./README.md`
825
- - Modify: `./index.ts` (keybinding `Ctrl+Alt+M` optional shortcut)
826
- - Test: full manual pass + `npx tsc --noEmit`
827
-
828
- **Interfaces:**
829
- - Consumes: all previous
830
- - Produces: shippable extension
831
-
832
- - [ ] **Step 1: Add empty state when filter yields 0**
833
-
834
- In `render`, if `rowsToShow.length === 0`, return `[theme.fg("warning", "No models match query"), theme.fg("dim","Press Esc to clear search")]`.
835
-
836
- - [ ] **Step 2: Clamp scroll window to terminal height**
837
-
838
- Compute `visibleHeight = Math.min(rowsToShow.length + providerHeaders, tui.height ?? 30) - 4`. Slice window around `cursorIndex`.
839
-
840
- - [ ] **Step 3: Add keyboard shortcut registration (optional)**
841
-
842
- In `index.ts`, add:
843
- ```typescript
844
- pi.registerShortcut({ key: "m", ctrl: true, alt: true } as any, {
845
- description: "Open modelconf",
846
- handler: async (ctx) => { /* trigger same as /modelconf command */ }
847
- });
848
- ```
849
- Guard: if `Key` API differs, skip — doc shows `registerShortcut(KeyId)`; verify at impl time.
850
-
851
- - [ ] **Step 4: Handle edge cases**
852
-
853
- - `enabledModels` contains stale ids for removed models → ignore in `isVisible`, but keep in file until user re-saves (filter to `allIds` intersection on save).
854
- - Save writes `undefined` (delete key) when all visible to keep file clean; writes explicit array when filtered.
855
- - File missing → treat as `undefined` (all visible).
856
- - Parse error → notify `settings.json invalid JSON` and treat as `undefined` without overwriting.
857
- - Large catalogs (2000+ models from `models-store.json`) → ensure `fuzzyFilter` stays fast (limit to 200 results shown, scroll paginated).
858
-
859
- - [ ] **Step 5: Write README.md**
860
-
861
- ```markdown
862
- # modelconf
863
-
864
- Per-provider model visibility manager.
865
-
866
- ## Usage
867
- /modelconf
868
- - ↑/↓ or j/k navigate, / to fuzzy search, space toggle, x bulk, enter save, esc cancel.
869
-
870
- ## Persistence
871
- Writes `enabledModels` in `~/.pi/agent/settings.json`. After save, run `/reload` if models list not refreshed.
872
- ```
873
-
874
- - [ ] **Step 6: Final checks**
875
-
876
- Run:
877
- ```bash
878
- npx tsc --noEmit --project ~/.pi/agent/extensions/modelconf/tsconfig.json
879
- pi --help # ensure extension loads without error
880
- ```
881
- Manual: `/modelconf` → fuzzy `mini` → toggle → `x` → `*free*` exclude → `Enter` → `/reload` → `/model` picker confirms hidden.
882
-
883
- - [ ] **Step 7: Commit**
884
-
885
- ```
886
- docs(modelconf): final polish and README
887
- ```
888
-
889
- ---
890
-
891
- ## Verification Checklist
892
-
893
- - [ ] `pi` starts, `/reload` shows no load errors for `modelconf`.
894
- - [ ] `/modelconf` opens grouped list, provider headers with `visible/total`.
895
- - [ ] `/` opens search input (IME cursor visible), typing filters list via fuzzy, `Esc` clears.
896
- - [ ] `Space` flips `◉`/`○` and marks dirty (`● unsaved`), `Enter` writes `settings.json` and notifies, `Esc` with dirty prompts discard.
897
- - [ ] `x` opens glob modal, typing `luna` matches substring, `Tab`/`i`/`e` switches include/exclude, `Enter` bulk-toggles draft, preview count correct.
898
- - [ ] Save with all visible writes `enabledModels: undefined` (key removed); save with some hidden writes exact array; reload and reopen confirms persistence.
899
- - [ ] Large provider (`opencode` ~ thousands in `models-store.json`) scrolls without overflow.
900
- - [ ] `npx tsc --noEmit` passes.
901
-
902
- ## Risks & Mitigations
903
-
904
- - **No public `settingsManager` API in ExtensionAPI** — mitigated by direct `fs` read/write to `~/.pi/agent/settings.json` + documented fallback; spike in Task 2 verifies path and `ctx as any` alternative.
905
- - **IME cursor not showing** — ensure `Container implements Focusable` propagates `focused` to `Input` per `docs/tui.md`; test with CJK input.
906
- - **Performance on 3000+ models** — fuzzyFilter is O(n*q) but n=~11000 max (from `models-store.json`); limit render window and debounce input if needed.
907
- - **Extension not auto-discovered** — must be `~/.pi/agent/extensions/modelconf/index.ts` and `settings.json` `extensions` includes `"extensions/"`; verify via `/reload` logs.
908
-
909
- ## Out of Scope (v2)
910
-
911
- - Provider-level collapse/expand toggle (could be `Enter` on header).
912
- - Regex mode for glob menu.
913
- - Project-local `.pi/settings.json` enabledModels merging.
914
- - Undo history / diff preview before save.
915
-