agim-cli 1.2.150 → 1.2.152

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 (192) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dist/core/skills/loader.d.ts +54 -0
  3. package/dist/core/skills/loader.d.ts.map +1 -1
  4. package/dist/core/skills/loader.js +221 -96
  5. package/dist/core/skills/loader.js.map +1 -1
  6. package/dist/web/agim-skills-api.d.ts.map +1 -1
  7. package/dist/web/agim-skills-api.js +80 -1
  8. package/dist/web/agim-skills-api.js.map +1 -1
  9. package/dist/web/public/assets/{a2a-Cll3P4QN.js → a2a-DZL1sgVk.js} +2 -2
  10. package/dist/web/public/assets/{a2a-Cll3P4QN.js.map → a2a-DZL1sgVk.js.map} +1 -1
  11. package/dist/web/public/assets/{activity-B7T7YFlD.js → activity-BsCuSHQd.js} +2 -2
  12. package/dist/web/public/assets/{activity-B7T7YFlD.js.map → activity-BsCuSHQd.js.map} +1 -1
  13. package/dist/web/public/assets/{admins-CN7P018S.js → admins-CyG_mVaa.js} +2 -2
  14. package/dist/web/public/assets/{admins-CN7P018S.js.map → admins-CyG_mVaa.js.map} +1 -1
  15. package/dist/web/public/assets/{agents-Bqgq7GBF.js → agents-D2wSzcVZ.js} +2 -2
  16. package/dist/web/public/assets/{agents-Bqgq7GBF.js.map → agents-D2wSzcVZ.js.map} +1 -1
  17. package/dist/web/public/assets/{approvals-C8IUJQ_A.js → approvals-C3uNQTdL.js} +2 -2
  18. package/dist/web/public/assets/{approvals-C8IUJQ_A.js.map → approvals-C3uNQTdL.js.map} +1 -1
  19. package/dist/web/public/assets/{arrow-down-SLWKqtDc.js → arrow-down-CppegASc.js} +2 -2
  20. package/dist/web/public/assets/{arrow-down-SLWKqtDc.js.map → arrow-down-CppegASc.js.map} +1 -1
  21. package/dist/web/public/assets/{arrow-up-BOADc9ce.js → arrow-up-8NH41ZQX.js} +2 -2
  22. package/dist/web/public/assets/{arrow-up-BOADc9ce.js.map → arrow-up-8NH41ZQX.js.map} +1 -1
  23. package/dist/web/public/assets/{asks-C-j-DypC.js → asks-DiT_eq2x.js} +2 -2
  24. package/dist/web/public/assets/{asks-C-j-DypC.js.map → asks-DiT_eq2x.js.map} +1 -1
  25. package/dist/web/public/assets/{audit-DQb-RuXh.js → audit-0T4jT2G0.js} +2 -2
  26. package/dist/web/public/assets/{audit-DQb-RuXh.js.map → audit-0T4jT2G0.js.map} +1 -1
  27. package/dist/web/public/assets/{bell-CV88-ul6.js → bell-BoZsoN58.js} +2 -2
  28. package/dist/web/public/assets/{bell-CV88-ul6.js.map → bell-BoZsoN58.js.map} +1 -1
  29. package/dist/web/public/assets/{bgjobs-CDrK0d-W.js → bgjobs-DbHdPyhE.js} +2 -2
  30. package/dist/web/public/assets/{bgjobs-CDrK0d-W.js.map → bgjobs-DbHdPyhE.js.map} +1 -1
  31. package/dist/web/public/assets/{brain-B7HtSOQU.js → brain-D-zbPJ7o.js} +2 -2
  32. package/dist/web/public/assets/{brain-B7HtSOQU.js.map → brain-D-zbPJ7o.js.map} +1 -1
  33. package/dist/web/public/assets/{briefcase-mdzuIa__.js → briefcase-D3jWpfeB.js} +2 -2
  34. package/dist/web/public/assets/{briefcase-mdzuIa__.js.map → briefcase-D3jWpfeB.js.map} +1 -1
  35. package/dist/web/public/assets/{chat-CSjtY2rN.js → chat-DrYeCiFH.js} +2 -2
  36. package/dist/web/public/assets/{chat-CSjtY2rN.js.map → chat-DrYeCiFH.js.map} +1 -1
  37. package/dist/web/public/assets/{chevron-left-uSfPn636.js → chevron-left-D6-MKmUs.js} +2 -2
  38. package/dist/web/public/assets/{chevron-left-uSfPn636.js.map → chevron-left-D6-MKmUs.js.map} +1 -1
  39. package/dist/web/public/assets/{chevron-right-CtelqacW.js → chevron-right-CHtA6f0-.js} +2 -2
  40. package/dist/web/public/assets/{chevron-right-CtelqacW.js.map → chevron-right-CHtA6f0-.js.map} +1 -1
  41. package/dist/web/public/assets/{circle-check-8dbL-u7O.js → circle-check-B9adx56J.js} +2 -2
  42. package/dist/web/public/assets/{circle-check-8dbL-u7O.js.map → circle-check-B9adx56J.js.map} +1 -1
  43. package/dist/web/public/assets/{circle-check-big-D8-svk9a.js → circle-check-big-E1qhYcsu.js} +2 -2
  44. package/dist/web/public/assets/{circle-check-big-D8-svk9a.js.map → circle-check-big-E1qhYcsu.js.map} +1 -1
  45. package/dist/web/public/assets/{circle-x-rUxzIz5P.js → circle-x-xB2tQMGw.js} +2 -2
  46. package/dist/web/public/assets/{circle-x-rUxzIz5P.js.map → circle-x-xB2tQMGw.js.map} +1 -1
  47. package/dist/web/public/assets/{clock-CG5dlBGB.js → clock-DFU9E0F0.js} +2 -2
  48. package/dist/web/public/assets/{clock-CG5dlBGB.js.map → clock-DFU9E0F0.js.map} +1 -1
  49. package/dist/web/public/assets/{confirm-dialog-DlUsSur3.js → confirm-dialog-C1w1040N.js} +2 -2
  50. package/dist/web/public/assets/{confirm-dialog-DlUsSur3.js.map → confirm-dialog-C1w1040N.js.map} +1 -1
  51. package/dist/web/public/assets/{copy-DnC76wFT.js → copy-UOTkO-2l.js} +2 -2
  52. package/dist/web/public/assets/{copy-DnC76wFT.js.map → copy-UOTkO-2l.js.map} +1 -1
  53. package/dist/web/public/assets/{data-table-DswkWUfG.js → data-table-CBTDQyFx.js} +2 -2
  54. package/dist/web/public/assets/{data-table-DswkWUfG.js.map → data-table-CBTDQyFx.js.map} +1 -1
  55. package/dist/web/public/assets/{dialog-Ceo4YuXy.js → dialog-CqBNfrYQ.js} +2 -2
  56. package/dist/web/public/assets/{dialog-Ceo4YuXy.js.map → dialog-CqBNfrYQ.js.map} +1 -1
  57. package/dist/web/public/assets/{download-DF-46tS4.js → download-BzDp4OCu.js} +2 -2
  58. package/dist/web/public/assets/{download-DF-46tS4.js.map → download-BzDp4OCu.js.map} +1 -1
  59. package/dist/web/public/assets/{email-CZee26-_.js → email-BDd0tUm1.js} +2 -2
  60. package/dist/web/public/assets/{email-CZee26-_.js.map → email-BDd0tUm1.js.map} +1 -1
  61. package/dist/web/public/assets/{empty-state-D9Hi0Atm.js → empty-state-CKPzKsI5.js} +2 -2
  62. package/dist/web/public/assets/{empty-state-D9Hi0Atm.js.map → empty-state-CKPzKsI5.js.map} +1 -1
  63. package/dist/web/public/assets/{external-link-D64iZa9P.js → external-link-Cx0ulEkb.js} +2 -2
  64. package/dist/web/public/assets/{external-link-D64iZa9P.js.map → external-link-Cx0ulEkb.js.map} +1 -1
  65. package/dist/web/public/assets/{eye-sY6WZb7D.js → eye-D9PD2xVj.js} +2 -2
  66. package/dist/web/public/assets/{eye-sY6WZb7D.js.map → eye-D9PD2xVj.js.map} +1 -1
  67. package/dist/web/public/assets/{facts-B7bGGwvi.js → facts-CRpGEODt.js} +2 -2
  68. package/dist/web/public/assets/{facts-B7bGGwvi.js.map → facts-CRpGEODt.js.map} +1 -1
  69. package/dist/web/public/assets/{goals-BfQbsvZv.js → goals-pyF71sFT.js} +2 -2
  70. package/dist/web/public/assets/{goals-BfQbsvZv.js.map → goals-pyF71sFT.js.map} +1 -1
  71. package/dist/web/public/assets/{health-Ba_mY0Ts.js → health-_YLm7LIW.js} +2 -2
  72. package/dist/web/public/assets/{health-Ba_mY0Ts.js.map → health-_YLm7LIW.js.map} +1 -1
  73. package/dist/web/public/assets/{heart-pulse-BjikOVwU.js → heart-pulse-DyxG9jqH.js} +2 -2
  74. package/dist/web/public/assets/{heart-pulse-BjikOVwU.js.map → heart-pulse-DyxG9jqH.js.map} +1 -1
  75. package/dist/web/public/assets/{heartbeat-BM8LlPes.js → heartbeat-bZfXp6Vm.js} +2 -2
  76. package/dist/web/public/assets/{heartbeat-BM8LlPes.js.map → heartbeat-bZfXp6Vm.js.map} +1 -1
  77. package/dist/web/public/assets/{hot-BtuLL6n8.js → hot-CSwCgYXw.js} +2 -2
  78. package/dist/web/public/assets/{hot-BtuLL6n8.js.map → hot-CSwCgYXw.js.map} +1 -1
  79. package/dist/web/public/assets/{index-DEWFfW_Z.js → index-D5DvetP5.js} +11 -11
  80. package/dist/web/public/assets/index-D5DvetP5.js.map +1 -0
  81. package/dist/web/public/assets/{index-f1K5Q2Yz.css → index-uKzGZ_Df.css} +1 -1
  82. package/dist/web/public/assets/injection-CYjttllI.js +2 -0
  83. package/dist/web/public/assets/injection-CYjttllI.js.map +1 -0
  84. package/dist/web/public/assets/installed-C6eNPXLl.js +31 -0
  85. package/dist/web/public/assets/installed-C6eNPXLl.js.map +1 -0
  86. package/dist/web/public/assets/{jobs-Ddy81Udm.js → jobs-CKQvijeH.js} +2 -2
  87. package/dist/web/public/assets/{jobs-Ddy81Udm.js.map → jobs-CKQvijeH.js.map} +1 -1
  88. package/dist/web/public/assets/{layout-Bp4SAA8_.js → layout-2S0YMTH_.js} +2 -2
  89. package/dist/web/public/assets/{layout-Bp4SAA8_.js.map → layout-2S0YMTH_.js.map} +1 -1
  90. package/dist/web/public/assets/{layout-Bn2qUxcK.js → layout-BQGP-bci.js} +2 -2
  91. package/dist/web/public/assets/{layout-Bn2qUxcK.js.map → layout-BQGP-bci.js.map} +1 -1
  92. package/dist/web/public/assets/{layout-CZ9pGnW8.js → layout-C4RTH7Mc.js} +2 -2
  93. package/dist/web/public/assets/{layout-CZ9pGnW8.js.map → layout-C4RTH7Mc.js.map} +1 -1
  94. package/dist/web/public/assets/{layout-BL74fT-L.js → layout-C9vch6up.js} +2 -2
  95. package/dist/web/public/assets/{layout-BL74fT-L.js.map → layout-C9vch6up.js.map} +1 -1
  96. package/dist/web/public/assets/layout-Y4GA9SyV.js +2 -0
  97. package/dist/web/public/assets/layout-Y4GA9SyV.js.map +1 -0
  98. package/dist/web/public/assets/{llm-yp7b5xxL.js → llm-CJvopUnt.js} +2 -2
  99. package/dist/web/public/assets/{llm-yp7b5xxL.js.map → llm-CJvopUnt.js.map} +1 -1
  100. package/dist/web/public/assets/{loader-circle-Bbw4pEyE.js → loader-circle-CgLbYd3g.js} +2 -2
  101. package/dist/web/public/assets/{loader-circle-Bbw4pEyE.js.map → loader-circle-CgLbYd3g.js.map} +1 -1
  102. package/dist/web/public/assets/{map-pin-DIXHUQgM.js → map-pin-CdHDC52F.js} +2 -2
  103. package/dist/web/public/assets/{map-pin-DIXHUQgM.js.map → map-pin-CdHDC52F.js.map} +1 -1
  104. package/dist/web/public/assets/{mcp-DyaljIM_.js → mcp-SI73JwZR.js} +2 -2
  105. package/dist/web/public/assets/{mcp-DyaljIM_.js.map → mcp-SI73JwZR.js.map} +1 -1
  106. package/dist/web/public/assets/{memos-Dkoc157i.js → memos-B-5cdUp2.js} +2 -2
  107. package/dist/web/public/assets/{memos-Dkoc157i.js.map → memos-B-5cdUp2.js.map} +1 -1
  108. package/dist/web/public/assets/{messengers-CcyGDeUI.js → messengers-CUz9DXB5.js} +2 -2
  109. package/dist/web/public/assets/{messengers-CcyGDeUI.js.map → messengers-CUz9DXB5.js.map} +1 -1
  110. package/dist/web/public/assets/{mobile-DqzIv4Xb.js → mobile-CrdzyGXr.js} +2 -2
  111. package/dist/web/public/assets/{mobile-DqzIv4Xb.js.map → mobile-CrdzyGXr.js.map} +1 -1
  112. package/dist/web/public/assets/{native-agent-BQ7WaRGK.js → native-agent-g8BpqzAo.js} +2 -2
  113. package/dist/web/public/assets/{native-agent-BQ7WaRGK.js.map → native-agent-g8BpqzAo.js.map} +1 -1
  114. package/dist/web/public/assets/{network-B_yUFAqC.js → network-DcpeRx8a.js} +2 -2
  115. package/dist/web/public/assets/{network-B_yUFAqC.js.map → network-DcpeRx8a.js.map} +1 -1
  116. package/dist/web/public/assets/{outbox-l8aVOZqO.js → outbox-uR6crgVJ.js} +2 -2
  117. package/dist/web/public/assets/{outbox-l8aVOZqO.js.map → outbox-uR6crgVJ.js.map} +1 -1
  118. package/dist/web/public/assets/{pagination-BAKRGKa9.js → pagination-CxK3lTYb.js} +2 -2
  119. package/dist/web/public/assets/{pagination-BAKRGKa9.js.map → pagination-CxK3lTYb.js.map} +1 -1
  120. package/dist/web/public/assets/{persona-D3VL9Rg1.js → persona-B1k5tc4U.js} +2 -2
  121. package/dist/web/public/assets/{persona-D3VL9Rg1.js.map → persona-B1k5tc4U.js.map} +1 -1
  122. package/dist/web/public/assets/{plans-BBB5e9my.js → plans-BWQSahSt.js} +2 -2
  123. package/dist/web/public/assets/{plans-BBB5e9my.js.map → plans-BWQSahSt.js.map} +1 -1
  124. package/dist/web/public/assets/{play-7-Wd369f.js → play-D2VxB3QN.js} +2 -2
  125. package/dist/web/public/assets/{play-7-Wd369f.js.map → play-D2VxB3QN.js.map} +1 -1
  126. package/dist/web/public/assets/{plus-B0sfZy-j.js → plus-_XZVk5F_.js} +2 -2
  127. package/dist/web/public/assets/{plus-B0sfZy-j.js.map → plus-_XZVk5F_.js.map} +1 -1
  128. package/dist/web/public/assets/{policy-BM1WRXH0.js → policy-D77IdVzS.js} +2 -2
  129. package/dist/web/public/assets/{policy-BM1WRXH0.js.map → policy-D77IdVzS.js.map} +1 -1
  130. package/dist/web/public/assets/{qr-code-DcKs5fi3.js → qr-code-glvcd_3g.js} +2 -2
  131. package/dist/web/public/assets/{qr-code-DcKs5fi3.js.map → qr-code-glvcd_3g.js.map} +1 -1
  132. package/dist/web/public/assets/{refresh-ccw-uNKeBeRl.js → refresh-ccw-MfejQvU6.js} +2 -2
  133. package/dist/web/public/assets/{refresh-ccw-uNKeBeRl.js.map → refresh-ccw-MfejQvU6.js.map} +1 -1
  134. package/dist/web/public/assets/{reminders-DHM8K0_O.js → reminders-CtKa3ECr.js} +2 -2
  135. package/dist/web/public/assets/{reminders-DHM8K0_O.js.map → reminders-CtKa3ECr.js.map} +1 -1
  136. package/dist/web/public/assets/{save-qwJa5_SA.js → save-Cha8EnUJ.js} +2 -2
  137. package/dist/web/public/assets/{save-qwJa5_SA.js.map → save-Cha8EnUJ.js.map} +1 -1
  138. package/dist/web/public/assets/{schedules-Bcd0wbT4.js → schedules-CuQgG6hz.js} +2 -2
  139. package/dist/web/public/assets/{schedules-Bcd0wbT4.js.map → schedules-CuQgG6hz.js.map} +1 -1
  140. package/dist/web/public/assets/{search-i1tP2maJ.js → search-BJqFnKP5.js} +2 -2
  141. package/dist/web/public/assets/{search-i1tP2maJ.js.map → search-BJqFnKP5.js.map} +1 -1
  142. package/dist/web/public/assets/{search-BUlzNWrj.js → search-BpyL8Spy.js} +2 -2
  143. package/dist/web/public/assets/{search-BUlzNWrj.js.map → search-BpyL8Spy.js.map} +1 -1
  144. package/dist/web/public/assets/{security-DgJyTT4g.js → security-Cw_yJiDX.js} +2 -2
  145. package/dist/web/public/assets/{security-DgJyTT4g.js.map → security-Cw_yJiDX.js.map} +1 -1
  146. package/dist/web/public/assets/{service-A0Hzear0.js → service-CgSHzzZ2.js} +2 -2
  147. package/dist/web/public/assets/{service-A0Hzear0.js.map → service-CgSHzzZ2.js.map} +1 -1
  148. package/dist/web/public/assets/{shield-alert-DrnN6fz_.js → shield-alert-C_TX7BBD.js} +2 -2
  149. package/dist/web/public/assets/{shield-alert-DrnN6fz_.js.map → shield-alert-C_TX7BBD.js.map} +1 -1
  150. package/dist/web/public/assets/{status-badge-Ryzf96Pl.js → status-badge-CLMCkv6R.js} +2 -2
  151. package/dist/web/public/assets/{status-badge-Ryzf96Pl.js.map → status-badge-CLMCkv6R.js.map} +1 -1
  152. package/dist/web/public/assets/{subtasks-Bzh3o3EF.js → subtasks-BpXUesU9.js} +2 -2
  153. package/dist/web/public/assets/{subtasks-Bzh3o3EF.js.map → subtasks-BpXUesU9.js.map} +1 -1
  154. package/dist/web/public/assets/{table-BbAOSyc8.js → table-B-dheE8t.js} +2 -2
  155. package/dist/web/public/assets/{table-BbAOSyc8.js.map → table-B-dheE8t.js.map} +1 -1
  156. package/dist/web/public/assets/{topn-DkhYw-Gp.js → topn-CIXROr87.js} +2 -2
  157. package/dist/web/public/assets/{topn-DkhYw-Gp.js.map → topn-CIXROr87.js.map} +1 -1
  158. package/dist/web/public/assets/{trash-2-CA0cLpnU.js → trash-2-CiJPCsGG.js} +2 -2
  159. package/dist/web/public/assets/{trash-2-CA0cLpnU.js.map → trash-2-CiJPCsGG.js.map} +1 -1
  160. package/dist/web/public/assets/use-agim-skills-DDOG9Vno.js +2 -0
  161. package/dist/web/public/assets/use-agim-skills-DDOG9Vno.js.map +1 -0
  162. package/dist/web/public/assets/{use-background-tasks-B64YjlA8.js → use-background-tasks-BT6BkFMM.js} +2 -2
  163. package/dist/web/public/assets/{use-background-tasks-B64YjlA8.js.map → use-background-tasks-BT6BkFMM.js.map} +1 -1
  164. package/dist/web/public/assets/{use-llm-admin-DY2axI4D.js → use-llm-admin-C5QwblGu.js} +2 -2
  165. package/dist/web/public/assets/{use-llm-admin-DY2axI4D.js.map → use-llm-admin-C5QwblGu.js.map} +1 -1
  166. package/dist/web/public/assets/{use-memory-BYEjVWbU.js → use-memory-BaQLelYY.js} +2 -2
  167. package/dist/web/public/assets/{use-memory-BYEjVWbU.js.map → use-memory-BaQLelYY.js.map} +1 -1
  168. package/dist/web/public/assets/{use-observability-Coj02yDo.js → use-observability-DiJjq_81.js} +2 -2
  169. package/dist/web/public/assets/{use-observability-Coj02yDo.js.map → use-observability-DiJjq_81.js.map} +1 -1
  170. package/dist/web/public/assets/{use-settings-i1MhlkyC.js → use-settings-CTuwZEdY.js} +2 -2
  171. package/dist/web/public/assets/{use-settings-i1MhlkyC.js.map → use-settings-CTuwZEdY.js.map} +1 -1
  172. package/dist/web/public/assets/{use-workspace-DgEM35PY.js → use-workspace-42B5n8Aa.js} +2 -2
  173. package/dist/web/public/assets/{use-workspace-DgEM35PY.js.map → use-workspace-42B5n8Aa.js.map} +1 -1
  174. package/dist/web/public/assets/{useQuery-CY2iazjN.js → useQuery-BLyYPRRA.js} +2 -2
  175. package/dist/web/public/assets/{useQuery-CY2iazjN.js.map → useQuery-BLyYPRRA.js.map} +1 -1
  176. package/dist/web/public/assets/{vector-Ic76u2hY.js → vector-BlyEjpL_.js} +2 -2
  177. package/dist/web/public/assets/{vector-Ic76u2hY.js.map → vector-BlyEjpL_.js.map} +1 -1
  178. package/dist/web/public/assets/{viewer-BXbUN1Rl.js → viewer-nKB1rvkV.js} +2 -2
  179. package/dist/web/public/assets/{viewer-BXbUN1Rl.js.map → viewer-nKB1rvkV.js.map} +1 -1
  180. package/dist/web/public/assets/{workspace-CUg0JPn6.js → workspace-C5xO2uoT.js} +2 -2
  181. package/dist/web/public/assets/{workspace-CUg0JPn6.js.map → workspace-C5xO2uoT.js.map} +1 -1
  182. package/dist/web/public/assets/{workspaces-C-wb5FQj.js → workspaces-BncBOneY.js} +2 -2
  183. package/dist/web/public/assets/{workspaces-C-wb5FQj.js.map → workspaces-BncBOneY.js.map} +1 -1
  184. package/dist/web/public/assets/{x-D1iSuoqg.js → x-De6HR8Q5.js} +2 -2
  185. package/dist/web/public/assets/{x-D1iSuoqg.js.map → x-De6HR8Q5.js.map} +1 -1
  186. package/dist/web/public/index.html +2 -2
  187. package/package.json +1 -1
  188. package/dist/web/public/assets/index-DEWFfW_Z.js.map +0 -1
  189. package/dist/web/public/assets/installed-Xr8p31ij.js +0 -31
  190. package/dist/web/public/assets/installed-Xr8p31ij.js.map +0 -1
  191. package/dist/web/public/assets/layout-pasFRkKV.js +0 -2
  192. package/dist/web/public/assets/layout-pasFRkKV.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,128 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.2.152] - 2026-06-15
8
+
9
+ ### Changed (后台 skills 管理体验)
10
+
11
+ 针对 v1.2.151 后两个反馈做体验优化:
12
+
13
+ #### Q1 — installed 页按 5 bucket 分组 + 默认折叠 + 顶部搜索
14
+
15
+ 364 个 skill 一根长 list 渲染太重;改成 loader 已有的 5 bucket 分组,
16
+ 大组默认折叠:
17
+
18
+ - **Framework**(agim-* 4 条,默认展开)
19
+ - **Agim builtin**(github / weather / 等 8 条,默认展开)
20
+ - **Workspace**(运营者亲装 ~122 条,默认折叠)
21
+ - **ECC 推荐池**(20 条,默认展开)
22
+ - **ECC 余下**(~210 条,默认折叠)
23
+
24
+ 顶部加搜索框(按 name + description 模糊匹配)。搜索激活时所有匹配桶
25
+ 强制展开,并在每个桶头显示命中数 badge。
26
+
27
+ #### Q2 — injection 页 Refresh 升级为"重建默认注入词"
28
+
29
+ v1.2.151 的 Refresh 按钮只 refetch React Query,没真正失效 loader 5min cache
30
+ ——运营者刚新增 skill 后看到的 auto 还是旧的。改成两步:
31
+ 1. POST `/api/agim-skills/refresh`(loader 重扫磁盘 + invalidate cache)
32
+ 2. refetch `/api/agim-skills/injection`(拿到 fresh auto block)
33
+
34
+ 不影响 override(覆盖文件不动)。
35
+
36
+ 按钮文案:中文"重建默认注入词" / 英文"Rebuild auto block"。
37
+
38
+ ### Files
39
+
40
+ - `src/web/agim-skills-api.ts` — `handleList` 返回多加 `bucket: 0..4`(复用 `rankSkillBucket`)
41
+ - `src/web-app/src/hooks/use-agim-skills.ts` — `AgimSkillMeta.bucket?: 0|1|2|3|4`
42
+ - `src/web-app/src/routes/settings/agim-skills/installed.tsx` — `SkillsList` 重构为 bucket 分组 + 折叠 + 顶部搜索;导入 ChevronDown / ChevronRight / Search 图标
43
+ - `src/web-app/src/routes/settings/agim-skills/injection.tsx` — 新增 `useAgimSkillsRefresh` 依赖;`onRefresh` 改两步语义;按钮文案改成"重建"
44
+ - `src/web-app/src/i18n/locales/{zh,en}/settings.json` — 新增 `agimSkills.group.*` / `searchPlaceholder` / `searchEmpty` + `skillInjection.rebuild*` / `rebuiltToast`
45
+
46
+ ### Tests
47
+
48
+ - API list 测试 (`agim-skills-api.test.ts` 13/13 旧 + 6 v1.2.151) + loader (42/42) → 61/61 全过
49
+ - backend typecheck 干净;SPA typecheck 仅遗留 search.tsx 错误(不在本 PR)
50
+ - SPA build 干净
51
+
52
+ ## [1.2.151] - 2026-06-13
53
+
54
+ ### Changed (skills injection — workspace 完整注入铁律 + 编辑入口)
55
+
56
+ v1.2.150 的 cluster 压缩对 workspace 太狠:35 个 `fin-data-*` 折成一行
57
+ `fin-data-* (35): name1 · name2 · …` 后 LLM 路由质量下降——只剩名字猜,没了
58
+ description 关键词。运营者亲装的 workspace skill 是最强 "我要用" 信号,
59
+ 不应该为省 token 牺牲精度。
60
+
61
+ #### 铁律:workspace 完整注入,不折叠
62
+
63
+ `buildSkillsSummary` 拆出 `buildAutoInjectionBlock(all)`,bucket 3(workspace)
64
+ 重新走 `renderSkillRow`(完整描述行):
65
+
66
+ - bucket 0/1/3 全部 full row(agim 框架 / agim builtin / **workspace**)
67
+ - bucket 2 ECC picks 仍 full row(20 个 curated 推荐)
68
+ - 只有 bucket 4(ECC remainder 210 个)才 cluster
69
+
70
+ #### 渲染顺序调整:0 → 1 → 3 → 2 → 4
71
+
72
+ workspace 排在 ECC picks 之前。运营者亲装 > 框架预选 > ECC remainder。
73
+
74
+ #### cluster refine bug fix
75
+
76
+ 旧实现遇到"所有成员都在单个 sub-key 下"(例如全部 `fin-data-*` skill 都共享
77
+ 2 段前缀)时 bottom out 不再递归,导致 fin-data 全部折成一行 cluster。
78
+ 现在加分支:单 sub-key 占全部成员时自动 `segDepth+1` 继续细分,
79
+ 所以 `fin-data-aspd-*` / `fin-data-stock-*` / `fin-data-fund-*` 会拆成
80
+ 独立 cluster 而非合并。
81
+
82
+ #### 编辑入口:~/.agim/skill-injection-override.md
83
+
84
+ 运营者可以覆盖自动生成的 skill 块。文件存在且非空时,**全文替换** auto block
85
+ (路由头 + always-on body 保留)。三个 helper:
86
+ `readInjectionOverride / writeInjectionOverride / deleteInjectionOverride`。
87
+ 环境变量 `IMHUB_SKILLS_INJECTION_OVERRIDE_PATH` 可改路径(测试用)。
88
+
89
+ #### Admin API + SPA 页
90
+
91
+ - `GET /api/agim-skills/injection` → 返回 `{ auto, override, overrideActive, overridePath, maxOverrideChars }`
92
+ - `PUT /api/agim-skills/injection` body `{ body: string }` → 写 override(空 body=删除;>40k 字符 413)
93
+ - `DELETE /api/agim-skills/injection` → 清除 override
94
+ - 受现有 `/api/agim-skills/*` admin allowlist 守卫
95
+ - 新页面 `/settings/agim-skills/injection`:auto 只读 textarea + override 可编辑 textarea +
96
+ Save / Clear / Copy-auto / Refresh 按钮 + 字符计数 + ≥10k 警告
97
+ - 中英 i18n 全套
98
+
99
+ #### 新增 env knob
100
+
101
+ - `IMHUB_SKILLS_ECC_PICKS=name1,name2,...` 覆盖默认 20 picks(推荐池)
102
+ - `IMHUB_SKILLS_INJECT_ECC_REMAINDER=on|off|name-list`:
103
+ - `on`(默认):cluster 模式
104
+ - `off`:完全抑制 ECC remainder 块(省 token)
105
+ - 逗号分隔白名单:只保留指定 skill
106
+
107
+ ### Files
108
+
109
+ - `src/core/skills/loader.ts` — workspace 完整注入 + 顺序调整 + refine bug fix +
110
+ env knob 解析(`resolveEccTopPicks` / `resolveEccRemainderMode`)+
111
+ override 文件 helper(read / write / delete / `injectionOverridePath`)+
112
+ `buildAutoInjectionBlock` 导出供 API 预览
113
+ - `src/web/agim-skills-api.ts` — 新 GET/PUT/DELETE `/api/agim-skills/injection`
114
+ - `src/web/agim-skills-api.test.ts` — +5 case(GET 元数据 / PUT 保存 / DELETE / 空 body=删 / 413 超限)
115
+ - `src/web-app/src/hooks/use-agim-skills.ts` — `useSkillInjection / Save / Delete`
116
+ - `src/web-app/src/routes/settings/agim-skills/injection.tsx` — 新页面
117
+ - `src/web-app/src/routes/settings/agim-skills/layout.tsx` — tab 新增"系统提示注入"
118
+ - `src/web-app/src/router.tsx` — 注册新 route
119
+ - `src/web-app/src/i18n/locales/{zh,en}/settings.json` — `skillInjection.*` 文案
120
+ - `src/core/skills/loader.test.ts` — +5 case(refine 单 sub-key 递归 / picks env / remainder off / remainder allowlist / override 全文替换);2 case 旧断言更新(workspace 不聚类 / workspace 排在 ECC picks 前)
121
+
122
+ ### Tests
123
+
124
+ - `loader.test.ts`:42/42 全过
125
+ - `agim-skills-api.test.ts`:19/19 全过
126
+ - typecheck 干净(search.tsx 是历史遗留,不归本 PR)
127
+ - SPA build 干净(`injection-*.js` chunk 已生成)
128
+
7
129
  ## [1.2.150] - 2026-06-12
8
130
 
9
131
  ### Changed (skills system-prompt injection — cluster-aware compression)
@@ -104,6 +104,40 @@ export declare function getSkillByName(name: string): SkillMeta | null;
104
104
  * unknown name OR read failure. Honors the MAX_SKILL_BODY_CHARS cap;
105
105
  * oversize files come back truncated with a `[truncated …]` footer. */
106
106
  export declare function readSkillBody(name: string): string | null;
107
+ /**
108
+ * v1.2.151 — Resolve the active ECC top-pick set. Operators can override
109
+ * via `IMHUB_SKILLS_ECC_PICKS=name1,name2,...`. An empty or unset value
110
+ * falls back to the default 20-name list. Validation is intentionally
111
+ * forgiving: unknown names pass through (they just become no-ops when
112
+ * the named skill isn't installed).
113
+ */
114
+ export declare function resolveEccTopPicks(): ReadonlySet<string>;
115
+ /**
116
+ * v1.2.151 — ECC remainder injection policy. Default `on` emits the
117
+ * cluster block. `off` suppresses it entirely (token-saving for ops
118
+ * who only want the recommended picks). A comma-list value flips to
119
+ * "allowlist" mode: only remainder skills in the list are emitted.
120
+ */
121
+ type EccRemainderMode = {
122
+ mode: 'on';
123
+ } | {
124
+ mode: 'off';
125
+ } | {
126
+ mode: 'allowlist';
127
+ names: Set<string>;
128
+ };
129
+ export declare function resolveEccRemainderMode(): EccRemainderMode;
130
+ export declare function injectionOverridePath(): string;
131
+ /** Read the override file. Returns the trimmed body, or null when the
132
+ * file is missing / empty / unreadable. Never throws. */
133
+ export declare function readInjectionOverride(): string | null;
134
+ /** Write (or replace) the override file. The caller is responsible for
135
+ * authorization — this is a thin filesystem helper. Returns true on
136
+ * success, false on any disk error. */
137
+ export declare function writeInjectionOverride(body: string): boolean;
138
+ /** Delete the override file. Returns true when a file was actually
139
+ * removed (or already gone), false on filesystem error. */
140
+ export declare function deleteInjectionOverride(): boolean;
107
141
  /**
108
142
  * v1.2.149 — Rank a skill into one of four buckets for system-prompt
109
143
  * injection. Lower rank wins (fills the visible 32 first). Within a
@@ -165,5 +199,25 @@ export declare function clusterSkills(skills: ReadonlyArray<SkillMeta>, minSize?
165
199
  clusters: SkillCluster[];
166
200
  singletons: SkillMeta[];
167
201
  };
202
+ /**
203
+ * v1.2.151 — Build the auto-generated tier-1 block (no header, no
204
+ * always-on bodies — caller wraps). Separated so the SPA editor can
205
+ * preview "what would auto produce right now" via the admin API.
206
+ *
207
+ * Bucket render order:
208
+ * 0 framework → 1 builtin → **3 workspace** → 2 ECC picks → 4 ECC remainder
209
+ *
210
+ * Operator-installed workspace skills out-rank ECC pinned picks because
211
+ * they are the strongest "I want this" signal. ECC picks (curated
212
+ * harness vocabulary) come right after so the LLM still has them
213
+ * within reach. ECC remainder lives at the bottom as the on-demand
214
+ * pool.
215
+ *
216
+ * Buckets 0/1/2/3 all emit full rows (renderSkillRow). Only bucket 4
217
+ * (ECC remainder) clusters — and even then, the cluster path can be
218
+ * disabled or scoped via `IMHUB_SKILLS_INJECT_ECC_REMAINDER`.
219
+ */
220
+ export declare function buildAutoInjectionBlock(all: ReadonlyArray<SkillMeta>): string;
168
221
  export declare function buildSkillsSummary(): string;
222
+ export {};
169
223
  //# sourceMappingURL=loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/skills/loader.ts"],"names":[],"mappings":"AAiDA;;sEAEsE;AACtE,eAAO,MAAM,oBAAoB,QAAS,CAAA;AAE1C;;gDAEgD;AAChD,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAExC;;oDAEoD;AACpD,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,4EAA4E;IAC5E,MAAM,EAAE,WAAW,GAAG,SAAS,CAAA;IAC/B,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAA;IACX;;2DAEuD;IACvD,MAAM,EAAE,OAAO,CAAA;IACf;qCACiC;IACjC,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB;0DACsD;IACtD,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB;;0EAEsE;IACtE,SAAS,EAAE,OAAO,CAAA;IAClB,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;;mEAE+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAmCD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;AA4BxD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAiB7C;AAED;sEACsE;AACtE,wBAAgB,oBAAoB,IAAI;IACtC,IAAI,EAAE,UAAU,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;CACpB,CAOA;AAuBD,wEAAwE;AACxE,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAmB3E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAajD;AAYD;;oDAEoD;AACpD,wBAAgB,UAAU,IAAI,SAAS,EAAE,CAExC;AAED;;6BAE6B;AAC7B,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAE7D;AAED;;wEAEwE;AACxE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYzD;AAkDD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAsBpD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAA;IACd,yDAAyD;IACzD,KAAK,EAAE,MAAM,EAAE,CAAA;IACf;gEAC4D;IAC5D,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,EAChC,OAAO,SAAI,GACV;IAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;IAAC,UAAU,EAAE,SAAS,EAAE,CAAA;CAAE,CAsFvD;AA4BD,wBAAgB,kBAAkB,IAAI,MAAM,CA8G3C"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/skills/loader.ts"],"names":[],"mappings":"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"}
@@ -315,7 +315,7 @@ export function readSkillBody(name) {
315
315
  *
316
316
  * Add / remove freely — this is a curated list, not a registry.
317
317
  */
318
- const ECC_TOP_PICKS = new Set([
318
+ const ECC_TOP_PICKS_DEFAULT = new Set([
319
319
  // Agent harness engineering (11)
320
320
  'agentic-engineering', 'agent-eval', 'agent-introspection-debugging',
321
321
  'agent-harness-construction', 'autonomous-agent-harness', 'autonomous-loops',
@@ -326,6 +326,105 @@ const ECC_TOP_PICKS = new Set([
326
326
  'code-tour', 'tdd-workflow', 'security-review', 'benchmark',
327
327
  'article-writing', 'content-engine',
328
328
  ]);
329
+ /**
330
+ * v1.2.151 — Resolve the active ECC top-pick set. Operators can override
331
+ * via `IMHUB_SKILLS_ECC_PICKS=name1,name2,...`. An empty or unset value
332
+ * falls back to the default 20-name list. Validation is intentionally
333
+ * forgiving: unknown names pass through (they just become no-ops when
334
+ * the named skill isn't installed).
335
+ */
336
+ export function resolveEccTopPicks() {
337
+ const raw = (process.env.IMHUB_SKILLS_ECC_PICKS || '').trim();
338
+ if (!raw)
339
+ return ECC_TOP_PICKS_DEFAULT;
340
+ const names = raw.split(/[,;\s]+/).map((s) => s.trim()).filter(Boolean);
341
+ if (names.length === 0)
342
+ return ECC_TOP_PICKS_DEFAULT;
343
+ return new Set(names);
344
+ }
345
+ export function resolveEccRemainderMode() {
346
+ const raw = (process.env.IMHUB_SKILLS_INJECT_ECC_REMAINDER || '').trim().toLowerCase();
347
+ if (!raw || raw === 'on' || raw === '1' || raw === 'true')
348
+ return { mode: 'on' };
349
+ if (raw === 'off' || raw === '0' || raw === 'false')
350
+ return { mode: 'off' };
351
+ // Anything else is treated as a comma/whitespace-separated allowlist.
352
+ const names = raw.split(/[,;\s]+/).map((s) => s.trim()).filter(Boolean);
353
+ if (names.length === 0)
354
+ return { mode: 'on' };
355
+ return { mode: 'allowlist', names: new Set(names) };
356
+ }
357
+ // ─── v1.2.151 — operator-tunable injection override ────────────────────
358
+ //
359
+ // Operators frequently want to nudge the auto-generated skills block —
360
+ // pin a specific skill higher, hide one that's noisy, add a routing
361
+ // hint about a custom env, etc. Rather than expanding the env-knob
362
+ // surface for every dimension, agim accepts a verbatim text file at
363
+ // `~/.agim/skill-injection-override.md` that REPLACES the auto block.
364
+ //
365
+ // When the file exists and is non-empty, buildSkillsSummary returns:
366
+ // - the standard "[agim tool routing — skills first]" header
367
+ // - the override file contents (verbatim, trimmed)
368
+ // - the always-on skill bodies (unchanged)
369
+ //
370
+ // The auto-generated block (priority buckets + clusters) is suppressed.
371
+ // Tests and CI override the path via IMHUB_SKILLS_INJECTION_OVERRIDE_PATH
372
+ // to avoid polluting the operator's real ~/.agim/.
373
+ //
374
+ // Editor entrypoint: the SPA page `/settings/skill-injection` calls
375
+ // admin endpoints to read auto + read/write/delete the override.
376
+ const INJECTION_OVERRIDE_FILENAME = 'skill-injection-override.md';
377
+ export function injectionOverridePath() {
378
+ const override = process.env.IMHUB_SKILLS_INJECTION_OVERRIDE_PATH;
379
+ if (override)
380
+ return override;
381
+ return join(AGIM_HOME, INJECTION_OVERRIDE_FILENAME);
382
+ }
383
+ /** Read the override file. Returns the trimmed body, or null when the
384
+ * file is missing / empty / unreadable. Never throws. */
385
+ export function readInjectionOverride() {
386
+ const path = injectionOverridePath();
387
+ try {
388
+ if (!existsSync(path))
389
+ return null;
390
+ const raw = readFileSync(path, 'utf-8').trim();
391
+ return raw || null;
392
+ }
393
+ catch (err) {
394
+ log.warn({ event: 'skills.injection_override.read_failed', path, err: String(err) });
395
+ return null;
396
+ }
397
+ }
398
+ /** Write (or replace) the override file. The caller is responsible for
399
+ * authorization — this is a thin filesystem helper. Returns true on
400
+ * success, false on any disk error. */
401
+ export function writeInjectionOverride(body) {
402
+ const path = injectionOverridePath();
403
+ try {
404
+ mkdirSync(dirname(path), { recursive: true });
405
+ writeFileSync(path, body, 'utf-8');
406
+ return true;
407
+ }
408
+ catch (err) {
409
+ log.warn({ event: 'skills.injection_override.write_failed', path, err: String(err) });
410
+ return false;
411
+ }
412
+ }
413
+ /** Delete the override file. Returns true when a file was actually
414
+ * removed (or already gone), false on filesystem error. */
415
+ export function deleteInjectionOverride() {
416
+ const path = injectionOverridePath();
417
+ try {
418
+ if (!existsSync(path))
419
+ return true;
420
+ rmSync(path, { force: true });
421
+ return true;
422
+ }
423
+ catch (err) {
424
+ log.warn({ event: 'skills.injection_override.delete_failed', path, err: String(err) });
425
+ return false;
426
+ }
427
+ }
329
428
  /**
330
429
  * v1.2.149 — Rank a skill into one of four buckets for system-prompt
331
430
  * injection. Lower rank wins (fills the visible 32 first). Within a
@@ -368,7 +467,7 @@ export function rankSkillBucket(s) {
368
467
  const isEcc = s.source === 'builtin'
369
468
  && (s.description.startsWith('[ECC] ') || /^ECC\b/i.test(s.origin ?? ''));
370
469
  if (isEcc)
371
- return ECC_TOP_PICKS.has(s.name) ? 2 : 4;
470
+ return resolveEccTopPicks().has(s.name) ? 2 : 4;
372
471
  if (s.source === 'workspace')
373
472
  return 3;
374
473
  // non-ECC builtin (agim's own small in-tree pack)
@@ -441,6 +540,14 @@ export function clusterSkills(skills, minSize = 3) {
441
540
  used.add(s.name);
442
541
  }
443
542
  }
543
+ else if (qualified.length === 1 && sub.size === 1) {
544
+ // v1.2.151 — single sub-key contains ALL members (e.g. all `fin-*`
545
+ // are `fin-data-*` at depth 2). Recurse deeper to split by depth+1
546
+ // instead of collapsing into a single coarse `fin-data-*` group.
547
+ // Without this fix, 69 fin-data skills land in one cluster and
548
+ // the LLM can't tell aspd / stock / fund families apart by name.
549
+ refine(sub.get(qualified[0]), segDepth + 1);
550
+ }
444
551
  else {
445
552
  emitCluster(group, segDepth - 1);
446
553
  }
@@ -513,118 +620,136 @@ const BUCKET_HEADINGS = {
513
620
  3: 'Workspace skills (operator-installed business)',
514
621
  4: 'ECC remainder (read full description via mcp__imhub__read_skill)',
515
622
  };
516
- export function buildSkillsSummary() {
517
- const all = listSkills();
518
- if (all.length === 0)
519
- return '';
520
- // v1.2.150 — bucket + cluster-aware emission.
521
- // Buckets 0, 1, 2: render each skill as an individual row with full
522
- // description. Small + curated; the LLM needs the granularity.
523
- // Bucket 3 (workspace): cluster when MIN_CLUSTER_SIZE share a
524
- // prefix; otherwise individual rows. Lets operators with one big
525
- // namespace (`fin-data-*`) get all names visible without burning
526
- // dozens of full-description tokens.
527
- // Bucket 4 (ECC remainder): always cluster aggressively + only
528
- // emit name list (no per-cluster trigger hint) since this is the
529
- // "you can read these on demand" pool. We still show every name
530
- // so the LLM knows the surface.
623
+ /**
624
+ * v1.2.137 — Routing rule prepended to every turn's skill block.
625
+ * v1.2.151 Extracted into a constant so the override path and the
626
+ * auto path share the same header verbatim.
627
+ */
628
+ const SKILLS_ROUTING_HEADER = [
629
+ '[agim tool routing skills first]',
630
+ 'Before calling any generic tool (web_fetch / web_search / native_exec /',
631
+ 'shell-style commands), you MUST scan the skill list below. If any entry\'s',
632
+ 'description matches the user\'s intent even loosely — you MUST call',
633
+ 'mcp__imhub__read_skill(\'<name>\') first to load that skill\'s body. Skill',
634
+ 'bodies contain dedicated tools, endpoints, or queries that produce',
635
+ 'higher-quality, structured results than free-text web pages for the',
636
+ 'same intent. Falling back to the generic tool is only valid when:',
637
+ ' (a) no skill description plausibly matches, OR',
638
+ ' (b) you already read a relevant skill and its body explicitly says',
639
+ ' "this skill is not suitable for X" or lacks the needed coverage.',
640
+ 'This is a routing discipline, not a hint. Skipping it counts as a',
641
+ 'tool-routing error in this conversation.',
642
+ ].join('\n');
643
+ /**
644
+ * v1.2.151 — Render one skill as a tier-1 row with full description.
645
+ * The body is defensive against malformed YAML block-scalar leakage.
646
+ *
647
+ * NOTE: workspace skills (bucket 3) MUST always go through this path
648
+ * — operator-installed business skills are an "I want this" signal
649
+ * from the deployment, so we keep their full descriptions for routing
650
+ * precision regardless of how many there are. The token cost is real
651
+ * but intentional; operators with 100+ workspace skills can use the
652
+ * SPA `/settings/skill-injection` editor to prune (see override path).
653
+ */
654
+ function renderSkillRow(s) {
655
+ const reason = s.available ? '' : ` (unavailable: ${s.unavailableReason ?? 'requires not met'})`;
656
+ const flag = s.always ? ' [always]' : '';
657
+ const descClean = (s.description || '').trim();
658
+ const description = descClean && descClean !== '>-' && descClean !== '|'
659
+ ? descClean
660
+ : '(no description in SKILL.md — read body via mcp__imhub__read_skill)';
661
+ return `- ${s.name}: ${description}${reason}${flag}`;
662
+ }
663
+ /** Inline `always: true` skill bodies. Shared between override + auto. */
664
+ function appendAlwaysOnBodies(all, lines) {
665
+ const alwaysOn = all.filter((s) => s.always && s.available);
666
+ for (const s of alwaysOn) {
667
+ const body = readSkillBody(s.name);
668
+ if (!body)
669
+ continue;
670
+ lines.push('');
671
+ lines.push(`### Always-on skill: ${s.name}`);
672
+ lines.push(body);
673
+ }
674
+ }
675
+ /**
676
+ * v1.2.151 — Build the auto-generated tier-1 block (no header, no
677
+ * always-on bodies — caller wraps). Separated so the SPA editor can
678
+ * preview "what would auto produce right now" via the admin API.
679
+ *
680
+ * Bucket render order:
681
+ * 0 framework → 1 builtin → **3 workspace** → 2 ECC picks → 4 ECC remainder
682
+ *
683
+ * Operator-installed workspace skills out-rank ECC pinned picks because
684
+ * they are the strongest "I want this" signal. ECC picks (curated
685
+ * harness vocabulary) come right after so the LLM still has them
686
+ * within reach. ECC remainder lives at the bottom as the on-demand
687
+ * pool.
688
+ *
689
+ * Buckets 0/1/2/3 all emit full rows (renderSkillRow). Only bucket 4
690
+ * (ECC remainder) clusters — and even then, the cluster path can be
691
+ * disabled or scoped via `IMHUB_SKILLS_INJECT_ECC_REMAINDER`.
692
+ */
693
+ export function buildAutoInjectionBlock(all) {
531
694
  const byBucket = { 0: [], 1: [], 2: [], 3: [], 4: [] };
532
695
  for (const s of all)
533
696
  byBucket[rankSkillBucket(s)].push(s);
534
697
  for (const b of [0, 1, 2, 3, 4]) {
535
698
  byBucket[b].sort((a, b2) => a.name.localeCompare(b2.name));
536
699
  }
537
- const lines = [
538
- // v1.2.137 routing rule, prepended to the skill list. Applies to
539
- // ALL agents (native / claude-code / codex / cursor / opencode) because
540
- // the router wraps this block into every turn's prompt. The rule is
541
- // intentionally generic it references the skill list itself, never
542
- // any specific skill name, so users with zero skills installed see
543
- // an empty block (early return above) and aren't burdened with the
544
- // rule text. The "MUST" framing + explicit fallback whitelist is
545
- // tuned to be hard enough that models don't drift back to web_fetch
546
- // out of habit when a relevant skill exists.
547
- '[agim tool routing — skills first]',
548
- 'Before calling any generic tool (web_fetch / web_search / native_exec /',
549
- 'shell-style commands), you MUST scan the skill list below. If any entry\'s',
550
- 'description matches the user\'s intent — even loosely — you MUST call',
551
- 'mcp__imhub__read_skill(\'<name>\') first to load that skill\'s body. Skill',
552
- 'bodies contain dedicated tools, endpoints, or queries that produce',
553
- 'higher-quality, structured results than free-text web pages for the',
554
- 'same intent. Falling back to the generic tool is only valid when:',
555
- ' (a) no skill description plausibly matches, OR',
556
- ' (b) you already read a relevant skill and its body explicitly says',
557
- ' "this skill is not suitable for X" or lacks the needed coverage.',
558
- 'This is a routing discipline, not a hint. Skipping it counts as a',
559
- 'tool-routing error in this conversation.',
560
- '',
561
- '[agim skills available — read full body with mcp__imhub__read_skill(name)]',
562
- ];
563
- // Helper: render one skill as an individual row with full description.
564
- // Same shape as v1.2.149; defensive fallback for malformed descriptions.
565
- const renderRow = (s) => {
566
- const reason = s.available ? '' : ` (unavailable: ${s.unavailableReason ?? 'requires not met'})`;
567
- const flag = s.always ? ' [always]' : '';
568
- const descClean = (s.description || '').trim();
569
- const description = descClean && descClean !== '>-' && descClean !== '|'
570
- ? descClean
571
- : '(no description in SKILL.md — read body via mcp__imhub__read_skill)';
572
- return `- ${s.name}: ${description}${reason}${flag}`;
573
- };
574
- // Buckets 0, 1, 2: individual rows (curated, small).
575
- for (const b of [0, 1, 2]) {
700
+ const lines = ['[agim skills available — read full body with mcp__imhub__read_skill(name)]'];
701
+ // Render order: 0 framework, 1 builtin, 3 workspace, 2 ECC picks.
702
+ // Workspace before ECC picks per v1.2.151 iron rule: operator-installed
703
+ // business skills rank above framework-curated harness picks.
704
+ for (const b of [0, 1, 3, 2]) {
576
705
  const items = byBucket[b];
577
706
  if (items.length === 0)
578
707
  continue;
579
708
  lines.push('');
580
709
  lines.push(`## ${BUCKET_HEADINGS[b]} (${items.length})`);
581
710
  for (const s of items)
582
- lines.push(renderRow(s));
711
+ lines.push(renderSkillRow(s));
583
712
  }
584
- // Bucket 3 (workspace): cluster when MIN_CLUSTER_SIZE shared prefix,
585
- // else individual rows. Per-cluster trigger hint included (the LLM is
586
- // most likely to route into this bucket on operator-domain queries).
587
- const ws = byBucket[3];
588
- if (ws.length > 0) {
589
- lines.push('');
590
- lines.push(`## ${BUCKET_HEADINGS[3]} (${ws.length})`);
591
- const { clusters, singletons } = clusterSkills(ws, 3);
592
- for (const c of clusters) {
593
- lines.push(`- ${c.prefix}-* (${c.names.length}): ${c.names.join(' · ')}`);
594
- if (c.triggerHint)
595
- lines.push(` → ${c.triggerHint}`);
596
- }
597
- for (const s of singletons)
598
- lines.push(renderRow(s));
599
- }
600
- // Bucket 4 (ECC remainder): cluster aggressively, name-only (no per-
601
- // cluster trigger hint — these are the "on-demand" pool, the LLM gets
602
- // a per-skill description if it actually calls read_skill).
713
+ // Bucket 4 (ECC remainder) cluster, with operator opt-out / allowlist.
603
714
  const eccRem = byBucket[4];
604
715
  if (eccRem.length > 0) {
605
- lines.push('');
606
- lines.push(`## ${BUCKET_HEADINGS[4]} (${eccRem.length})`);
607
- const { clusters, singletons } = clusterSkills(eccRem, 3);
608
- for (const c of clusters) {
609
- lines.push(`- ${c.prefix}-* (${c.names.length}): ${c.names.join(' · ')}`);
610
- }
611
- if (singletons.length > 0) {
612
- const names = singletons.map((s) => s.name).join(' · ');
613
- lines.push(`- (other ECC, ${singletons.length}): ${names}`);
716
+ const remainderMode = resolveEccRemainderMode();
717
+ const filtered = remainderMode.mode === 'off' ? []
718
+ : remainderMode.mode === 'allowlist'
719
+ ? eccRem.filter((s) => remainderMode.names.has(s.name))
720
+ : eccRem;
721
+ if (filtered.length > 0) {
722
+ lines.push('');
723
+ lines.push(`## ${BUCKET_HEADINGS[4]} (${filtered.length})`);
724
+ const { clusters, singletons } = clusterSkills(filtered, 3);
725
+ for (const c of clusters) {
726
+ lines.push(`- ${c.prefix}-* (${c.names.length}): ${c.names.join(' · ')}`);
727
+ }
728
+ if (singletons.length > 0) {
729
+ const names = singletons.map((s) => s.name).join(' · ');
730
+ lines.push(`- (other ECC, ${singletons.length}): ${names}`);
731
+ }
614
732
  }
615
733
  }
616
- // Inline `always: true` skills' full bodies regardless of bucket.
617
- // Cheap when few; operators opt-in by marking `always: true` in their
618
- // SKILL.md, which is rare.
619
- const alwaysOn = all.filter((s) => s.always && s.available);
620
- for (const s of alwaysOn) {
621
- const body = readSkillBody(s.name);
622
- if (!body)
623
- continue;
624
- lines.push('');
625
- lines.push(`### Always-on skill: ${s.name}`);
626
- lines.push(body);
734
+ return lines.join('\n');
735
+ }
736
+ export function buildSkillsSummary() {
737
+ const all = listSkills();
738
+ if (all.length === 0)
739
+ return '';
740
+ // v1.2.151 — operator override path: if `~/.agim/skill-injection-
741
+ // override.md` exists, return its body verbatim (still wrapped by
742
+ // the routing header + always-on bodies). The auto block is
743
+ // suppressed; the operator gets full editorial control. The SPA at
744
+ // `/settings/skill-injection` is the recommended way to author this.
745
+ const override = readInjectionOverride();
746
+ if (override) {
747
+ const lines = [SKILLS_ROUTING_HEADER, '', override];
748
+ appendAlwaysOnBodies(all, lines);
749
+ return `${lines.join('\n')}\n`;
627
750
  }
751
+ const lines = [SKILLS_ROUTING_HEADER, '', buildAutoInjectionBlock(all)];
752
+ appendAlwaysOnBodies(all, lines);
628
753
  return `${lines.join('\n')}\n`;
629
754
  }
630
755
  // ─── Scanning ────────────────────────────────────────────────────────