@ngockhoale/ukit 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/CHANGELOG.md +179 -0
  2. package/LICENSE +21 -0
  3. package/README.md +189 -0
  4. package/bin/ukit +30 -0
  5. package/manifests/platform.full.yaml +1194 -0
  6. package/package.json +71 -0
  7. package/scripts/bug/triage.mjs +37 -0
  8. package/scripts/index/build-index.mjs +35 -0
  9. package/scripts/index/query-index.mjs +92 -0
  10. package/scripts/index/refresh-index.mjs +85 -0
  11. package/scripts/release/verify-release.mjs +56 -0
  12. package/src/bug/triageBug.js +123 -0
  13. package/src/cli/adapters.js +148 -0
  14. package/src/cli/commands/diff.js +51 -0
  15. package/src/cli/commands/doctor.js +125 -0
  16. package/src/cli/commands/indexArgs.js +73 -0
  17. package/src/cli/commands/indexTools.js +509 -0
  18. package/src/cli/commands/install.js +293 -0
  19. package/src/cli/commands/memory.js +126 -0
  20. package/src/cli/commands/status.js +8 -0
  21. package/src/cli/commands/uninstall.js +51 -0
  22. package/src/cli/index.js +109 -0
  23. package/src/context/detectProjectContext.js +49 -0
  24. package/src/context/detectProviders.js +12 -0
  25. package/src/core/applyPlan.js +89 -0
  26. package/src/core/buildPlan.js +228 -0
  27. package/src/core/compact/index.js +294 -0
  28. package/src/core/compact/threshold.js +936 -0
  29. package/src/core/diffPlan.js +73 -0
  30. package/src/core/ensureGitignore.js +117 -0
  31. package/src/core/fileOps.js +188 -0
  32. package/src/core/memory/hygiene.js +160 -0
  33. package/src/core/memory/index.js +2 -0
  34. package/src/core/memory/retrieval.js +476 -0
  35. package/src/core/memory/store.js +202 -0
  36. package/src/core/metadata.js +132 -0
  37. package/src/core/migrateLegacy.js +139 -0
  38. package/src/core/output/index.js +1309 -0
  39. package/src/core/paths.js +13 -0
  40. package/src/core/report.js +17 -0
  41. package/src/core/router/advisor.js +42 -0
  42. package/src/core/router/index.js +2 -0
  43. package/src/core/router/router.js +164 -0
  44. package/src/core/runInstallPipeline.js +365 -0
  45. package/src/core/runtimeConfig.js +190 -0
  46. package/src/core/runtimePaths.js +24 -0
  47. package/src/core/status.js +186 -0
  48. package/src/core/token/index.js +328 -0
  49. package/src/core/uninstall.js +246 -0
  50. package/src/core/validation/confidence.js +89 -0
  51. package/src/core/validation/index.js +2 -0
  52. package/src/core/validation/validator.js +165 -0
  53. package/src/index/buildIndex.js +1392 -0
  54. package/src/index/gitHooks.js +109 -0
  55. package/src/index/importResolution.js +377 -0
  56. package/src/index/languageTools.js +127 -0
  57. package/src/index/paths.js +27 -0
  58. package/src/index/queryIndex.js +637 -0
  59. package/src/index/relatedTests.js +237 -0
  60. package/src/index/resolveContext.js +345 -0
  61. package/src/index/routeCatalog.js +258 -0
  62. package/src/index/taskRouting.js +677 -0
  63. package/src/index/verificationPlan.js +437 -0
  64. package/src/manifest/loadManifest.js +22 -0
  65. package/src/manifest/selectItems.js +78 -0
  66. package/src/manifest/validateManifest.js +115 -0
  67. package/src/render/buildVariables.js +39 -0
  68. package/src/render/renderTemplate.js +44 -0
  69. package/src/stack/detectStack.js +213 -0
  70. package/templates/.claude/agents/bug-debugger.md +57 -0
  71. package/templates/.claude/agents/feature-implementer.md +55 -0
  72. package/templates/.claude/config/providers.md +25 -0
  73. package/templates/.claude/hooks/auto-allow-bash.sh +155 -0
  74. package/templates/.claude/hooks/auto-prune-bash.sh +75 -0
  75. package/templates/.claude/hooks/block-dangerous.sh +54 -0
  76. package/templates/.claude/hooks/compress-output.sh +17 -0
  77. package/templates/.claude/hooks/protect-files.sh +37 -0
  78. package/templates/.claude/hooks/reinject-context.sh +28 -0
  79. package/templates/.claude/hooks/session-start.md +13 -0
  80. package/templates/.claude/hooks/skill-router.sh +1681 -0
  81. package/templates/.claude/hooks/verification-guard.sh +271 -0
  82. package/templates/.claude/settings.json +144 -0
  83. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  84. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  85. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  86. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  87. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  88. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  89. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  90. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  91. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  92. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  93. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  94. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  95. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  96. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  97. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  98. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  99. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  100. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  101. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  102. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  103. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  104. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  105. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  106. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  107. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  108. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  109. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  110. package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  111. package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  112. package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  113. package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  114. package/templates/.claude/skills/_shared/ooxml/schemas/mce/mc.xsd +75 -0
  115. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  116. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  117. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  118. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  119. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  120. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  121. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  122. package/templates/.claude/skills/_shared/ooxml/scripts/pack.py +159 -0
  123. package/templates/.claude/skills/_shared/ooxml/scripts/unpack.py +29 -0
  124. package/templates/.claude/skills/_shared/ooxml/scripts/validate.py +69 -0
  125. package/templates/.claude/skills/_shared/ooxml/scripts/validation/__init__.py +15 -0
  126. package/templates/.claude/skills/_shared/ooxml/scripts/validation/base.py +951 -0
  127. package/templates/.claude/skills/_shared/ooxml/scripts/validation/docx.py +274 -0
  128. package/templates/.claude/skills/_shared/ooxml/scripts/validation/pptx.py +315 -0
  129. package/templates/.claude/skills/_shared/ooxml/scripts/validation/redlining.py +279 -0
  130. package/templates/.claude/skills/backend-api/SKILL.md +26 -0
  131. package/templates/.claude/skills/canvas-design/LICENSE.txt +202 -0
  132. package/templates/.claude/skills/canvas-design/SKILL.md +130 -0
  133. package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  134. package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
  135. package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  136. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  137. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  138. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  139. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
  140. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  141. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  142. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  143. package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  144. package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
  145. package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  146. package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  147. package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  148. package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  149. package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
  150. package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  151. package/templates/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
  152. package/templates/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  153. package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  154. package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
  155. package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  156. package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  157. package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
  158. package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  159. package/templates/.claude/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
  160. package/templates/.claude/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  161. package/templates/.claude/skills/code-review/SKILL.md +97 -0
  162. package/templates/.claude/skills/debugging-toolkit/SKILL.md +156 -0
  163. package/templates/.claude/skills/delivery/SKILL.md +92 -0
  164. package/templates/.claude/skills/discover-security/SKILL.md +86 -0
  165. package/templates/.claude/skills/docker-packaging/SKILL.md +60 -0
  166. package/templates/.claude/skills/docs-manager/SKILL.md +465 -0
  167. package/templates/.claude/skills/docs-manager/init-project-docs.sh +70 -0
  168. package/templates/.claude/skills/docs-manager/templates/README.md.template +50 -0
  169. package/templates/.claude/skills/docs-manager/templates/agent-roles.md.template +24 -0
  170. package/templates/.claude/skills/docs-manager/templates/coding-conventions.md.template +28 -0
  171. package/templates/.claude/skills/docs-manager/templates/memory.md.template +30 -0
  172. package/templates/.claude/skills/docs-manager/templates/onboarding.md.template +20 -0
  173. package/templates/.claude/skills/docs-manager/templates/project.md.template +26 -0
  174. package/templates/.claude/skills/docs-quality/SKILL.md +148 -0
  175. package/templates/.claude/skills/docx/LICENSE.txt +30 -0
  176. package/templates/.claude/skills/docx/SKILL.md +197 -0
  177. package/templates/.claude/skills/docx/docx-js.md +350 -0
  178. package/templates/.claude/skills/docx/ooxml.md +610 -0
  179. package/templates/.claude/skills/docx/scripts/__init__.py +1 -0
  180. package/templates/.claude/skills/docx/scripts/document.py +1276 -0
  181. package/templates/.claude/skills/docx/scripts/templates/comments.xml +3 -0
  182. package/templates/.claude/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  183. package/templates/.claude/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  184. package/templates/.claude/skills/docx/scripts/templates/commentsIds.xml +3 -0
  185. package/templates/.claude/skills/docx/scripts/templates/people.xml +3 -0
  186. package/templates/.claude/skills/docx/scripts/utilities.py +374 -0
  187. package/templates/.claude/skills/duraone/SKILL.md +204 -0
  188. package/templates/.claude/skills/duraone/references/backend.md +636 -0
  189. package/templates/.claude/skills/duraone/references/frontend.md +1506 -0
  190. package/templates/.claude/skills/duraone/references/sql.md +631 -0
  191. package/templates/.claude/skills/duraone/references/workflow.md +520 -0
  192. package/templates/.claude/skills/executing-plans/SKILL.md +76 -0
  193. package/templates/.claude/skills/file-organizer/SKILL.md +433 -0
  194. package/templates/.claude/skills/frontend/SKILL.md +26 -0
  195. package/templates/.claude/skills/frontend-design/LICENSE.txt +177 -0
  196. package/templates/.claude/skills/frontend-design/SKILL.md +42 -0
  197. package/templates/.claude/skills/frontend-vue/SKILL.md +127 -0
  198. package/templates/.claude/skills/frontend-vue/components/Control/Box.vue +137 -0
  199. package/templates/.claude/skills/frontend-vue/components/Control/Button.vue +93 -0
  200. package/templates/.claude/skills/frontend-vue/components/Control/ButtonBar.vue +29 -0
  201. package/templates/.claude/skills/frontend-vue/components/Control/ButtonFloat.vue +62 -0
  202. package/templates/.claude/skills/frontend-vue/components/Control/CheckButton.vue +75 -0
  203. package/templates/.claude/skills/frontend-vue/components/Control/Checkbox.vue +58 -0
  204. package/templates/.claude/skills/frontend-vue/components/Control/Datetime.vue +148 -0
  205. package/templates/.claude/skills/frontend-vue/components/Control/Dropdownlist.vue +156 -0
  206. package/templates/.claude/skills/frontend-vue/components/Control/Input.vue +106 -0
  207. package/templates/.claude/skills/frontend-vue/components/Control/Label.vue +38 -0
  208. package/templates/.claude/skills/frontend-vue/components/Control/Master/BoxColumn.vue +24 -0
  209. package/templates/.claude/skills/frontend-vue/components/Control/Popup/Confirm.vue +33 -0
  210. package/templates/.claude/skills/frontend-vue/components/Control/Popup/Info.vue +32 -0
  211. package/templates/.claude/skills/frontend-vue/components/Control/Popup/ModalInfo.vue +39 -0
  212. package/templates/.claude/skills/frontend-vue/components/Control/Popup/Reject.vue +64 -0
  213. package/templates/.claude/skills/frontend-vue/components/Control/Tag.vue +82 -0
  214. package/templates/.claude/skills/frontend-vue/components/Control/Upload.vue +61 -0
  215. package/templates/.claude/skills/frontend-vue/components/ControlMobile/Dropdownlist.vue +103 -0
  216. package/templates/.claude/skills/frontend-vue/components/ControlMobile/PagingBar.vue +108 -0
  217. package/templates/.claude/skills/frontend-vue/components/ControlMobile/UploadImage.vue +137 -0
  218. package/templates/.claude/skills/frontend-vue/components/Grid/AG.vue +806 -0
  219. package/templates/.claude/skills/frontend-vue/components/Grid/AntTable.vue +253 -0
  220. package/templates/.claude/skills/frontend-vue/components/Grid/CustomDropdownEditor.vue +43 -0
  221. package/templates/.claude/skills/frontend-vue/components/Grid/CustomDropdownEditorEnable.vue +55 -0
  222. package/templates/.claude/skills/frontend-vue/components/Grid/HtmlTable.vue +40 -0
  223. package/templates/.claude/skills/frontend-vue/components/PDFViewer.vue +25 -0
  224. package/templates/.claude/skills/frontend-vue/components/Panel/FormView.vue +309 -0
  225. package/templates/.claude/skills/frontend-vue/components/Partial/Footer.vue +23 -0
  226. package/templates/.claude/skills/frontend-vue/components/Partial/Header.vue +265 -0
  227. package/templates/.claude/skills/frontend-vue/components/Partial/Sidebar.vue +122 -0
  228. package/templates/.claude/skills/frontend-vue/components/Template.vue +16 -0
  229. package/templates/.claude/skills/frontend-vue/components/View/Form.vue +89 -0
  230. package/templates/.claude/skills/frontend-vue/composables/indexDBStore.js +140 -0
  231. package/templates/.claude/skills/frontend-vue/composables/masterApi.js +362 -0
  232. package/templates/.claude/skills/frontend-vue/composables/state.js +578 -0
  233. package/templates/.claude/skills/frontend-vue/composables/useRequest.js +221 -0
  234. package/templates/.claude/skills/frontend-vue/composables/useSession.js +179 -0
  235. package/templates/.claude/skills/frontend-vue/composables/useTranslation.js +54 -0
  236. package/templates/.claude/skills/frontend-vue/composables/useWebSocket.js +257 -0
  237. package/templates/.claude/skills/frontend-vue/composables/userObj.js +111 -0
  238. package/templates/.claude/skills/frontend-vue/composables/utils.js +322 -0
  239. package/templates/.claude/skills/frontend-vue/reference/composables-example.vue +320 -0
  240. package/templates/.claude/skills/frontend-vue/reference/form-example.vue +183 -0
  241. package/templates/.claude/skills/frontend-vue/reference/grid-example.vue +147 -0
  242. package/templates/.claude/skills/frontend-vue/reference/masterdata-example/[id].vue +106 -0
  243. package/templates/.claude/skills/frontend-vue/reference/masterdata-example/index.vue +58 -0
  244. package/templates/.claude/skills/frontend-vue/reference/popup-example.vue +159 -0
  245. package/templates/.claude/skills/pdf/LICENSE.txt +30 -0
  246. package/templates/.claude/skills/pdf/SKILL.md +294 -0
  247. package/templates/.claude/skills/pdf/forms.md +205 -0
  248. package/templates/.claude/skills/pdf/reference.md +612 -0
  249. package/templates/.claude/skills/pdf/scripts/check_bounding_boxes.py +70 -0
  250. package/templates/.claude/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
  251. package/templates/.claude/skills/pdf/scripts/check_fillable_fields.py +12 -0
  252. package/templates/.claude/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
  253. package/templates/.claude/skills/pdf/scripts/create_validation_image.py +41 -0
  254. package/templates/.claude/skills/pdf/scripts/extract_form_field_info.py +152 -0
  255. package/templates/.claude/skills/pdf/scripts/fill_fillable_fields.py +114 -0
  256. package/templates/.claude/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
  257. package/templates/.claude/skills/pdf-processing/SKILL.md +107 -0
  258. package/templates/.claude/skills/pdf-processing-pro/FORMS.md +610 -0
  259. package/templates/.claude/skills/pdf-processing-pro/OCR.md +137 -0
  260. package/templates/.claude/skills/pdf-processing-pro/SKILL.md +296 -0
  261. package/templates/.claude/skills/pdf-processing-pro/TABLES.md +626 -0
  262. package/templates/.claude/skills/pdf-processing-pro/scripts/analyze_form.py +307 -0
  263. package/templates/.claude/skills/postgres/SKILL.md +69 -0
  264. package/templates/.claude/skills/postgres/reference/fn_get_examples.sql +208 -0
  265. package/templates/.claude/skills/postgres/reference/fn_rpt_examples.sql +239 -0
  266. package/templates/.claude/skills/postgres/reference/utility_functions.sql +94 -0
  267. package/templates/.claude/skills/pptx/LICENSE.txt +30 -0
  268. package/templates/.claude/skills/pptx/SKILL.md +484 -0
  269. package/templates/.claude/skills/pptx/html2pptx.md +625 -0
  270. package/templates/.claude/skills/pptx/ooxml.md +427 -0
  271. package/templates/.claude/skills/pptx/scripts/html2pptx.js +979 -0
  272. package/templates/.claude/skills/pptx/scripts/inventory.py +1020 -0
  273. package/templates/.claude/skills/pptx/scripts/rearrange.py +231 -0
  274. package/templates/.claude/skills/pptx/scripts/replace.py +385 -0
  275. package/templates/.claude/skills/pptx/scripts/thumbnail.py +450 -0
  276. package/templates/.claude/skills/repo-maintenance/SKILL.md +97 -0
  277. package/templates/.claude/skills/research/EXAMPLES.md +434 -0
  278. package/templates/.claude/skills/research/REFERENCE.md +399 -0
  279. package/templates/.claude/skills/research/SKILL.md +136 -0
  280. package/templates/.claude/skills/root-cause-tracing/SKILL.md +174 -0
  281. package/templates/.claude/skills/root-cause-tracing/find-polluter.sh +63 -0
  282. package/templates/.claude/skills/sharing-skills/SKILL.md +194 -0
  283. package/templates/.claude/skills/sql-optimization-patterns/SKILL.md +493 -0
  284. package/templates/.claude/skills/subagent-driven-development/SKILL.md +189 -0
  285. package/templates/.claude/skills/systematic-debugging/CREATION-LOG.md +119 -0
  286. package/templates/.claude/skills/systematic-debugging/SKILL.md +295 -0
  287. package/templates/.claude/skills/systematic-debugging/test-academic.md +14 -0
  288. package/templates/.claude/skills/systematic-debugging/test-pressure-1.md +58 -0
  289. package/templates/.claude/skills/systematic-debugging/test-pressure-2.md +68 -0
  290. package/templates/.claude/skills/systematic-debugging/test-pressure-3.md +69 -0
  291. package/templates/.claude/skills/test-driven-development/SKILL.md +364 -0
  292. package/templates/.claude/skills/testing-anti-patterns/SKILL.md +302 -0
  293. package/templates/.claude/skills/testing-quality/SKILL.md +97 -0
  294. package/templates/.claude/skills/verification-before-completion/SKILL.md +139 -0
  295. package/templates/.claude/skills/webapp-testing/LICENSE.txt +202 -0
  296. package/templates/.claude/skills/webapp-testing/SKILL.md +96 -0
  297. package/templates/.claude/skills/webapp-testing/examples/console_logging.py +35 -0
  298. package/templates/.claude/skills/webapp-testing/examples/element_discovery.py +40 -0
  299. package/templates/.claude/skills/webapp-testing/examples/static_html_automation.py +33 -0
  300. package/templates/.claude/skills/webapp-testing/scripts/with_server.py +106 -0
  301. package/templates/.claude/ukit/index/build-index.mjs +28 -0
  302. package/templates/.claude/ukit/index/cache-utils.mjs +140 -0
  303. package/templates/.claude/ukit/index/lib/index-core.mjs +2800 -0
  304. package/templates/.claude/ukit/index/query-index.mjs +150 -0
  305. package/templates/.claude/ukit/index/refresh-index.mjs +57 -0
  306. package/templates/.claude/ukit/index/reset-auto-permissions.mjs +76 -0
  307. package/templates/.claude/ukit/index/resolve-context.mjs +279 -0
  308. package/templates/.claude/ukit/index/route-catalog.mjs +258 -0
  309. package/templates/.claude/ukit/index/route-task.mjs +1994 -0
  310. package/templates/.claude/ukit/index/triage.mjs +133 -0
  311. package/templates/.claude/ukit/index/verify-context.mjs +689 -0
  312. package/templates/.claude/ukit/runtime/compact-threshold.mjs +1013 -0
  313. package/templates/.claude/ukit/runtime/output-compression.mjs +1340 -0
  314. package/templates/.claude/ukit/runtime/reinject-context.mjs +874 -0
  315. package/templates/.claude/ukit/runtime/token-utils.mjs +500 -0
  316. package/templates/.codex/README.md +83 -0
  317. package/templates/.codex/settings.json +187 -0
  318. package/templates/.gitignore +75 -0
  319. package/templates/AGENTS.md +116 -0
  320. package/templates/CLAUDE.md +93 -0
  321. package/templates/adapter-presets/antigravity/README.md +22 -0
  322. package/templates/adapter-presets/antigravity/rules.md +49 -0
  323. package/templates/adapter-presets/claude/settings.local.json +42 -0
  324. package/templates/adapter-presets/codex/settings.local.json +6 -0
  325. package/templates/adapter-presets/opencode/opencode.template.json +1 -0
  326. package/templates/docs/BUGFIX.md +20 -0
  327. package/templates/docs/BUG_INDEX.md +12 -0
  328. package/templates/docs/BUG_METRICS.md +7 -0
  329. package/templates/docs/BUG_TEMPLATE.md +13 -0
  330. package/templates/docs/CODE_MAP.md +35 -0
  331. package/templates/docs/INSTALL.md +113 -0
  332. package/templates/docs/MEMORY.md +49 -0
  333. package/templates/docs/PROJECT.md +50 -0
  334. package/templates/docs/UKIT_USAGE_GUIDE.md +147 -0
  335. package/templates/docs/WORKLOG.md +10 -0
  336. package/templates/ukit/README.md +14 -0
  337. package/templates/ukit/storage/cache/compact-history.json +3 -0
  338. package/templates/ukit/storage/cache/compact-pressure.json +1 -0
  339. package/templates/ukit/storage/cache/output-history.json +3 -0
  340. package/templates/ukit/storage/cache/prompt-cache.json +3 -0
  341. package/templates/ukit/storage/config.json +37 -0
  342. package/templates/ukit/storage/memory/projects/.gitkeep +2 -0
  343. package/templates/ukit/storage/memory/sessions/.gitkeep +0 -0
  344. package/templates/ukit/storage/memory/user.json +5 -0
@@ -0,0 +1,1994 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import crypto from 'node:crypto';
5
+ import * as indexCore from './lib/index-core.mjs';
6
+ import {
7
+ DEFAULT_RECENT_CACHE_MAX_ENTRIES,
8
+ readRecentCacheEntry,
9
+ writeRecentCacheEntry,
10
+ } from './cache-utils.mjs';
11
+ import { ROUTE_CATALOG } from './route-catalog.mjs';
12
+
13
+ const {
14
+ resolveContext,
15
+ buildCodeIndex,
16
+ DEFAULT_INDEX_CACHE_MAX_AGE_MS,
17
+ getIndexArtifactGeneratedAt,
18
+ } = indexCore;
19
+
20
+ const MAX_ACTIVE_ROUTE_SKILLS = 2;
21
+ const STOPWORDS = new Set([
22
+ 'the', 'a', 'an', 'and', 'or', 'to', 'for', 'of', 'with', 'in', 'on', 'is', 'are',
23
+ 'this', 'that', 'it', 'as', 'by', 'be', 'use', 'using', 'implement', 'fix', 'task',
24
+ 'cần', 'và', 'là', 'cho', 'một', 'những', 'dùng',
25
+ ]);
26
+ const INLINE_SUMMARY_MAX_SEGMENTS = 5;
27
+ const INLINE_SUMMARY_SIGNAL_PATTERNS = [
28
+ /\b(error|fail(?:ed)?|warning|assertionerror|exception|fatal|deprecated|unmet peer|ignored build scripts)\b/i,
29
+ /(^|\s)(src|app|lib|docs|ukit|\.claude|\.codex|tests?)\//i,
30
+ /^\s*FAIL\b/i,
31
+ /^\s*stderr\s*\|/i,
32
+ /^\s*stdout\s*\|/i,
33
+ ];
34
+ const INLINE_SUMMARY_TAIL_PATTERNS = [
35
+ /\b(Test Files|Tests|Packages:|Done in|short test summary info)\b/i,
36
+ /\b(Duration|Start at)\b/i,
37
+ ];
38
+
39
+ const buildNormalizedRouteSignalText = typeof indexCore.buildRouteSignalText === 'function'
40
+ ? indexCore.buildRouteSignalText
41
+ : (...values) => values
42
+ .map((value) => String(value || '').trim())
43
+ .filter(Boolean)
44
+ .join('\n')
45
+ .toLowerCase();
46
+
47
+ async function main() {
48
+ const args = process.argv.slice(2);
49
+ const rootDir = getRootDir(args);
50
+ const sharedStatePath = path.join(rootDir, '.claude', 'ukit', 'skill-router-state.json');
51
+ const routeCachePath = path.join(rootDir, '.claude', 'ukit', 'route-cache.json');
52
+ const previousState = await readJson(sharedStatePath, {});
53
+ const targetFile = readFlagValue(args, '--target');
54
+ const taskType = readFlagValue(args, '--type');
55
+ const commandText = readFlagValue(args, '--tool-command') ?? '';
56
+ const lastPrompt = readFlagValue(args, '--last-prompt')
57
+ ?? previousState?.routingContext?.lastExplicitUserPromptText
58
+ ?? '';
59
+ const adapter = readFlagValue(args, '--adapter') ?? inferAdapterFromInvocation(process.argv[1] || '');
60
+ const commandNamespace = adapter === 'codex'
61
+ ? '.codex'
62
+ : (adapter === 'antigravity' ? '.antigravity' : '.claude');
63
+ const promptText = args
64
+ .filter((arg, index) => !isFlagOrValue(args, index))
65
+ .join(' ')
66
+ .trim();
67
+
68
+ if (!promptText && !commandText && !targetFile) {
69
+ console.error('Usage: node .codex/ukit/index/route-task.mjs "<prompt>" [--tool-command <cmd>] [--target <file>] [--type trivial|simple|non-trivial] [--adapter codex|claude|antigravity|opencode]');
70
+ process.exitCode = 1;
71
+ return;
72
+ }
73
+
74
+ const preparedRoute = await prepareTaskRoute({
75
+ rootDir,
76
+ promptText,
77
+ commandText,
78
+ targetFile,
79
+ taskType,
80
+ adapter,
81
+ lastExplicitUserPromptText: lastPrompt,
82
+ commandNamespace,
83
+ });
84
+ const useIndexedContext = shouldUseIndexedContext({
85
+ activeSkills: preparedRoute.activeSkills,
86
+ targetFile: preparedRoute.routingContext?.targetFile ?? null,
87
+ });
88
+ const recentOutputSnapshot = shouldIncludeRecentOutput({
89
+ activeSkills: preparedRoute.activeSkills,
90
+ routingContext: preparedRoute?.routingContext ?? {},
91
+ useIndexedContext,
92
+ })
93
+ ? await buildRecentOutputSnapshot({
94
+ rootDir,
95
+ })
96
+ : null;
97
+ const previousContextSnapshot = shouldIncludePreviousContext({
98
+ routingContext: preparedRoute?.routingContext ?? {},
99
+ useIndexedContext,
100
+ })
101
+ ? await buildPreviousContextSnapshot({
102
+ rootDir,
103
+ routingContext: preparedRoute?.routingContext ?? {},
104
+ })
105
+ : null;
106
+ const indexGeneratedAtMs = useIndexedContext
107
+ ? await ensureFreshIndex({
108
+ rootDir,
109
+ logPrefix: 'route',
110
+ })
111
+ : null;
112
+ const projectVerification = useIndexedContext
113
+ ? await readProjectVerificationFingerprint(rootDir)
114
+ : null;
115
+ const requestKey = buildRouteRequestKey({
116
+ commandNamespace,
117
+ indexGeneratedAtMs,
118
+ projectFingerprint: projectVerification?.fingerprint ?? null,
119
+ activeSkills: preparedRoute.activeSkills,
120
+ routingContext: preparedRoute.routingContext,
121
+ previousContextSnapshot,
122
+ recentOutputSnapshot,
123
+ });
124
+ const reusableState = reuseSharedRouteState({
125
+ previousState,
126
+ requestKey,
127
+ });
128
+ if (reusableState) {
129
+ printRouteState(reusableState);
130
+ return;
131
+ }
132
+
133
+ const cachedRouteState = reuseSharedRouteState({
134
+ previousState: await readRecentCacheEntry(routeCachePath, requestKey, {
135
+ maxEntries: DEFAULT_RECENT_CACHE_MAX_ENTRIES,
136
+ touch: true,
137
+ }),
138
+ requestKey,
139
+ });
140
+ if (cachedRouteState) {
141
+ const sharedState = {
142
+ ...cachedRouteState,
143
+ source: 'task-route',
144
+ ts: Date.now(),
145
+ requestKey,
146
+ };
147
+ await writeJson(sharedStatePath, sharedState);
148
+ printRouteState(sharedState);
149
+ return;
150
+ }
151
+
152
+ const helperCacheSnapshot = await readHelperCacheSnapshot({
153
+ rootDir,
154
+ indexGeneratedAtMs,
155
+ projectFingerprint: projectVerification?.fingerprint ?? '',
156
+ routingContext: preparedRoute.routingContext,
157
+ useIndexedContext,
158
+ });
159
+ const route = await finalizeTaskRoute({
160
+ rootDir,
161
+ commandNamespace,
162
+ preparedRoute,
163
+ projectVerification,
164
+ cachedContextResult: helperCacheSnapshot.contextResult,
165
+ cachedVerificationPlan: helperCacheSnapshot.verificationPlan,
166
+ useIndexedContext,
167
+ previousContextSnapshot,
168
+ recentOutputSnapshot,
169
+ });
170
+ await seedHelperCaches({
171
+ rootDir,
172
+ indexGeneratedAtMs,
173
+ projectFingerprint: projectVerification?.fingerprint ?? '',
174
+ helperCacheSnapshot,
175
+ route,
176
+ });
177
+
178
+ const sharedState = createSharedRouteState({
179
+ route,
180
+ source: 'task-route',
181
+ requestKey,
182
+ indexGeneratedAtMs,
183
+ });
184
+ await writeJson(sharedStatePath, sharedState);
185
+ await writeRecentCacheEntry(routeCachePath, compactRouteCacheState(sharedState), {
186
+ maxEntries: DEFAULT_RECENT_CACHE_MAX_ENTRIES,
187
+ });
188
+
189
+ printRouteState(sharedState);
190
+ }
191
+
192
+ async function ensureFreshIndex({ rootDir, logPrefix }) {
193
+ const lastRefreshMs = await getIndexArtifactGeneratedAt({ rootDir });
194
+ const stale = lastRefreshMs === null
195
+ ? true
196
+ : typeof indexCore.isIndexStale === 'function'
197
+ ? await indexCore.isIndexStale({
198
+ rootDir,
199
+ maxAgeMs: DEFAULT_INDEX_CACHE_MAX_AGE_MS,
200
+ now: Date.now(),
201
+ generatedAtMs: lastRefreshMs,
202
+ })
203
+ : (Date.now() - lastRefreshMs) > DEFAULT_INDEX_CACHE_MAX_AGE_MS;
204
+
205
+ if (!stale) {
206
+ return lastRefreshMs;
207
+ }
208
+
209
+ const summary = await buildCodeIndex({ rootDir });
210
+ if (lastRefreshMs !== null) {
211
+ console.log(`[ukit:${logPrefix}] stale index refreshed (last=${new Date(lastRefreshMs).toISOString()})`);
212
+ } else {
213
+ console.log(`[ukit:${logPrefix}] stale index refreshed (no previous cache timestamp)`);
214
+ }
215
+ return Number.isFinite(summary?.generatedAtMs)
216
+ ? summary.generatedAtMs
217
+ : getIndexArtifactGeneratedAt({ rootDir });
218
+ }
219
+
220
+ async function prepareTaskRoute({
221
+ rootDir = process.cwd(),
222
+ promptText = '',
223
+ commandText = '',
224
+ targetFile = null,
225
+ taskType = null,
226
+ adapter = 'claude',
227
+ lastExplicitUserPromptText = '',
228
+ } = {}) {
229
+ const absoluteRoot = path.resolve(rootDir);
230
+ const normalizedPrompt = String(promptText || '').trim();
231
+ const normalizedCommand = String(commandText || '').trim();
232
+ const normalizedTarget = normalizeRelativeFile(absoluteRoot, targetFile);
233
+ const activeSkills = await selectActiveSkills({
234
+ rootDir: absoluteRoot,
235
+ promptText: normalizedPrompt,
236
+ commandText: normalizedCommand,
237
+ targetFile: normalizedTarget,
238
+ });
239
+ const selectedIds = activeSkills.map((entry) => entry.id);
240
+ const contextIntent = deriveContextIntent({
241
+ promptText: normalizedPrompt,
242
+ commandText: normalizedCommand,
243
+ targetFile: normalizedTarget,
244
+ selectedIds,
245
+ });
246
+ const inferredTaskType = taskType ?? inferTaskType({
247
+ promptText: normalizedPrompt,
248
+ commandText: normalizedCommand,
249
+ selectedIds,
250
+ });
251
+ const preservedPrompt = normalizedPrompt || String(lastExplicitUserPromptText || '').trim();
252
+
253
+ return {
254
+ rootDir: absoluteRoot,
255
+ activeSkills,
256
+ routingContext: {
257
+ adapter: String(adapter || 'claude').trim() || 'claude',
258
+ promptText: normalizedPrompt,
259
+ lastExplicitUserPromptText: preservedPrompt,
260
+ commandText: normalizedCommand,
261
+ targetFile: normalizedTarget,
262
+ contextIntent,
263
+ taskType: inferredTaskType,
264
+ },
265
+ };
266
+ }
267
+
268
+ async function finalizeTaskRoute({
269
+ rootDir = process.cwd(),
270
+ commandNamespace = '.claude',
271
+ preparedRoute,
272
+ projectVerification = null,
273
+ cachedContextResult = null,
274
+ cachedVerificationPlan = null,
275
+ useIndexedContext = true,
276
+ previousContextSnapshot = null,
277
+ recentOutputSnapshot = null,
278
+ } = {}) {
279
+ const absoluteRoot = path.resolve(rootDir);
280
+ const activeSkills = preparedRoute?.activeSkills ?? [];
281
+ const routingContext = preparedRoute?.routingContext ?? {};
282
+ const selectedIds = activeSkills.map((entry) => entry.id);
283
+ const {
284
+ contextIntent,
285
+ targetFile: normalizedTarget,
286
+ taskType: inferredTaskType,
287
+ } = routingContext;
288
+ const contextResult = useIndexedContext
289
+ ? cachedContextResult ?? (
290
+ contextIntent || normalizedTarget
291
+ ? await resolveContext({
292
+ rootDir: absoluteRoot,
293
+ intent: contextIntent,
294
+ targetFile: normalizedTarget,
295
+ taskType: inferredTaskType,
296
+ })
297
+ : null
298
+ )
299
+ : null;
300
+ const enrichedContextResult = expandRouteContext(contextResult);
301
+ const verificationSnapshot = useIndexedContext
302
+ ? projectVerification ?? await readProjectVerificationFingerprint(absoluteRoot)
303
+ : null;
304
+ const verificationPlan = useIndexedContext
305
+ ? cachedVerificationPlan ?? await deriveVerificationPlan({
306
+ rootDir: absoluteRoot,
307
+ intent: contextIntent,
308
+ targetFile: normalizedTarget,
309
+ taskType: inferredTaskType,
310
+ contextResult: enrichedContextResult,
311
+ skillIds: selectedIds,
312
+ fingerprintEntries: verificationSnapshot.entries,
313
+ pkg: verificationSnapshot.pkg,
314
+ })
315
+ : null;
316
+ const contextCommand = buildHelperCommand({ commandNamespace, scriptName: 'resolve-context.mjs', intent: contextIntent, targetFile: normalizedTarget, taskType: inferredTaskType });
317
+ const verifyCommand = buildHelperCommand({ commandNamespace, scriptName: 'verify-context.mjs', intent: contextIntent, targetFile: normalizedTarget, taskType: inferredTaskType });
318
+ const contextRecommendation = useIndexedContext
319
+ ? {
320
+ command: contextCommand,
321
+ preview: enrichedContextResult ? summarizeContext(enrichedContextResult) : null,
322
+ }
323
+ : null;
324
+ const verificationRecommendation = verificationPlan
325
+ ? {
326
+ ...verificationPlan,
327
+ helperCommand: verifyCommand,
328
+ }
329
+ : null;
330
+ const nextAction = deriveNextAction({
331
+ activeSkills,
332
+ contextRecommendation,
333
+ verificationRecommendation,
334
+ });
335
+ const routeSummary = buildRouteSummary({
336
+ activeSkills,
337
+ routingContext,
338
+ contextRecommendation,
339
+ verificationRecommendation,
340
+ nextAction,
341
+ });
342
+
343
+ return {
344
+ activeSkills,
345
+ routingContext,
346
+ previousContext: previousContextSnapshot,
347
+ recentOutput: recentOutputSnapshot,
348
+ contextRecommendation,
349
+ verificationRecommendation,
350
+ resolvedContextResult: contextResult,
351
+ resolvedVerificationPlan: verificationPlan,
352
+ nextAction,
353
+ routeSummary,
354
+ };
355
+ }
356
+
357
+ function printRouteState(state) {
358
+ const compactSummary = formatDisplayRouteSummary(state.routeSummary, state.routingContext);
359
+ const nextDisplay = formatDisplayNextAction(state.routeSummary);
360
+ const previousContextDisplay = formatDisplayPreviousContext(state.previousContext);
361
+ const recentOutputDisplay = formatDisplayRecentOutput(state.recentOutput);
362
+ const helperDisplay = formatDisplayHelperHint(state.routeSummary, compactSummary);
363
+ console.log('[ukit:route]');
364
+ console.log(`skills: ${(state.activeSkills ?? []).map((item) => item.id).join(', ') || 'none'}`);
365
+ if (previousContextDisplay) {
366
+ console.log(`previous-context: ${previousContextDisplay}`);
367
+ }
368
+ if (recentOutputDisplay) {
369
+ console.log(`recent-output: ${recentOutputDisplay}`);
370
+ }
371
+ console.log(`summary: ${compactSummary}`);
372
+ if (nextDisplay) {
373
+ console.log(`next: ${nextDisplay}`);
374
+ }
375
+ if (state.routeSummary?.delegateHint) {
376
+ console.log(`delegate: ${state.routeSummary.delegateHint}`);
377
+ }
378
+ if (helperDisplay) {
379
+ console.log(`helper: ${helperDisplay}`);
380
+ }
381
+ }
382
+
383
+ function hasRouteSummaryField(routeSummary, field) {
384
+ return Boolean(
385
+ routeSummary
386
+ && typeof routeSummary === 'object'
387
+ && Object.prototype.hasOwnProperty.call(routeSummary, field),
388
+ );
389
+ }
390
+
391
+ function extractRouteLineSegment(line, label) {
392
+ if (typeof line !== 'string' || !line.trim()) {
393
+ return null;
394
+ }
395
+
396
+ const match = line.match(new RegExp(`(?:^|\\|\\s*)${label}=([^|]+)`, 'i'));
397
+ return match ? `${label}=${String(match[1] || '').trim()}` : null;
398
+ }
399
+
400
+ function extractRouteLineValue(line, label) {
401
+ const segment = extractRouteLineSegment(line, label);
402
+ return segment ? String(segment.slice(label.length + 1) || '').trim() : '';
403
+ }
404
+
405
+ function formatDisplayRouteSummary(routeSummary = null, routingContext = {}) {
406
+ const line = String(routeSummary?.line ?? '').trim();
407
+ const taskSegment = routingContext?.taskType
408
+ ? `task=${routingContext.taskType}`
409
+ : extractRouteLineSegment(line, 'task');
410
+ const policySegment = routeSummary?.policyMode
411
+ ? `policy=${routeSummary.policyMode}`
412
+ : (
413
+ hasRouteSummaryField(routeSummary, 'policyMode')
414
+ ? null
415
+ : extractRouteLineSegment(line, 'policy')
416
+ );
417
+
418
+ const segments = [
419
+ taskSegment,
420
+ extractRouteLineSegment(line, 'targets'),
421
+ extractRouteLineSegment(line, 'tests'),
422
+ extractRouteLineSegment(line, 'styles'),
423
+ policySegment,
424
+ ].filter(Boolean);
425
+
426
+ return segments.join(' | ') || 'none';
427
+ }
428
+
429
+ function formatDisplayNextAction(routeSummary = null) {
430
+ if (!routeSummary || typeof routeSummary !== 'object') {
431
+ return '';
432
+ }
433
+
434
+ const nextActionCommand = String(routeSummary.nextActionCommand ?? '').trim();
435
+ if (nextActionCommand) {
436
+ return nextActionCommand;
437
+ }
438
+
439
+ const nextActionType = String(routeSummary.nextActionType ?? '').trim();
440
+ if (!nextActionType || nextActionType === 'read-skill-instructions') {
441
+ return '';
442
+ }
443
+
444
+ return nextActionType;
445
+ }
446
+
447
+ function formatDisplayPreviousContext(previousContext = null) {
448
+ const line = typeof previousContext?.line === 'string' ? previousContext.line.trim() : '';
449
+ if (!line) {
450
+ return '';
451
+ }
452
+
453
+ return line
454
+ .split(/\s+\|\s+/)
455
+ .map((segment) => String(segment || '').trim())
456
+ .filter(Boolean)
457
+ .map((segment) => segment
458
+ .replace(/^\[(project|session|user)\]\s*/i, (_, label) => `${String(label || '').toLowerCase()}=`)
459
+ .replace(/\s+—\s+/g, '; ')
460
+ .replace(/\b(decisions|rules|outcome|actions|next):\s*/gi, '$1=')
461
+ .replace(/\(\+(\d+)\s+more\)/gi, '+$1 more'))
462
+ .join(' | ');
463
+ }
464
+
465
+ function formatDisplayRecentOutput(recentOutput = null) {
466
+ const line = typeof recentOutput?.line === 'string' ? recentOutput.line.trim() : '';
467
+ if (!line) {
468
+ return '';
469
+ }
470
+
471
+ return line
472
+ .replace(/^-\s*/, '')
473
+ .replace(/^Recent command:\s*/i, 'cmd=')
474
+ .replace(/\s+\|\s+-\s+/g, ' | ')
475
+ .trim();
476
+ }
477
+
478
+ function formatDisplayHelperHint(routeSummary = null, compactSummary = '') {
479
+ if (!routeSummary || typeof routeSummary !== 'object') {
480
+ return '';
481
+ }
482
+
483
+ if (String(routeSummary.nextActionCommand ?? '').trim()) {
484
+ return '';
485
+ }
486
+
487
+ let helperHint = String(routeSummary.helperHint ?? '').trim().replace(/\s+/g, ' ');
488
+ if (!helperHint) {
489
+ return '';
490
+ }
491
+
492
+ const helperTargetMatch = helperHint.match(/(?:^|\s)target=([^\s]+)/);
493
+ const summaryTargets = extractRouteLineValue(compactSummary, 'targets');
494
+ if (helperTargetMatch && summaryTargets && summaryTargets === String(helperTargetMatch[1] || '').trim()) {
495
+ helperHint = helperHint.replace(` target=${helperTargetMatch[1]}`, '');
496
+ }
497
+
498
+ return helperHint.trim();
499
+ }
500
+
501
+ async function seedHelperCaches({
502
+ rootDir = process.cwd(),
503
+ indexGeneratedAtMs = null,
504
+ projectFingerprint = '',
505
+ helperCacheSnapshot = null,
506
+ route = null,
507
+ } = {}) {
508
+ const absoluteRoot = path.resolve(rootDir);
509
+ const routingContext = route?.routingContext ?? {};
510
+ const normalizedIntent = String(routingContext.contextIntent ?? '').trim();
511
+ const normalizedTargetFile = String(routingContext.targetFile ?? '').trim();
512
+ const normalizedTaskType = String(routingContext.taskType ?? '').trim();
513
+
514
+ if (!normalizedIntent && !normalizedTargetFile) {
515
+ return;
516
+ }
517
+
518
+ const updatedAt = Date.now();
519
+ const resolveContextResult = route?.resolvedContextResult ?? null;
520
+ const verificationPlan = route?.resolvedVerificationPlan ?? null;
521
+ const contextCacheHit = Boolean(helperCacheSnapshot?.contextCacheHit);
522
+ const verificationCacheHit = Boolean(helperCacheSnapshot?.verificationCacheHit);
523
+
524
+ if (resolveContextResult && !contextCacheHit) {
525
+ const resolveContextCachePath = path.join(absoluteRoot, '.claude', 'ukit', 'resolve-context-cache.json');
526
+ await writeRecentCacheEntry(resolveContextCachePath, {
527
+ requestKey: buildResolveContextRequestKey({
528
+ indexGeneratedAtMs,
529
+ intent: normalizedIntent,
530
+ targetFile: normalizedTargetFile,
531
+ taskType: normalizedTaskType,
532
+ }),
533
+ updatedAt,
534
+ result: resolveContextResult,
535
+ }, {
536
+ maxEntries: DEFAULT_RECENT_CACHE_MAX_ENTRIES,
537
+ });
538
+ }
539
+
540
+ if (verificationPlan && !verificationCacheHit) {
541
+ const verifyContextCachePath = path.join(absoluteRoot, '.claude', 'ukit', 'verify-context-cache.json');
542
+ await writeRecentCacheEntry(verifyContextCachePath, {
543
+ requestKey: buildVerifyContextRequestKey({
544
+ indexGeneratedAtMs,
545
+ projectFingerprint,
546
+ intent: normalizedIntent,
547
+ targetFile: normalizedTargetFile,
548
+ taskType: normalizedTaskType,
549
+ }),
550
+ updatedAt,
551
+ result: verificationPlan,
552
+ }, {
553
+ maxEntries: DEFAULT_RECENT_CACHE_MAX_ENTRIES,
554
+ });
555
+ }
556
+ }
557
+
558
+ async function readHelperCacheSnapshot({
559
+ rootDir = process.cwd(),
560
+ indexGeneratedAtMs = null,
561
+ projectFingerprint = '',
562
+ routingContext = {},
563
+ useIndexedContext = true,
564
+ } = {}) {
565
+ if (!useIndexedContext) {
566
+ return {
567
+ contextCacheHit: false,
568
+ verificationCacheHit: false,
569
+ contextResult: null,
570
+ verificationPlan: null,
571
+ };
572
+ }
573
+
574
+ const absoluteRoot = path.resolve(rootDir);
575
+ const normalizedIntent = String(routingContext.contextIntent ?? '').trim();
576
+ const normalizedTargetFile = String(routingContext.targetFile ?? '').trim();
577
+ const normalizedTaskType = String(routingContext.taskType ?? '').trim();
578
+
579
+ if (!normalizedIntent && !normalizedTargetFile) {
580
+ return {
581
+ contextCacheHit: false,
582
+ verificationCacheHit: false,
583
+ contextResult: null,
584
+ verificationPlan: null,
585
+ };
586
+ }
587
+
588
+ const resolveContextCachePath = path.join(absoluteRoot, '.claude', 'ukit', 'resolve-context-cache.json');
589
+ const verifyContextCachePath = path.join(absoluteRoot, '.claude', 'ukit', 'verify-context-cache.json');
590
+ const [cachedContextEntry, cachedVerificationEntry] = await Promise.all([
591
+ readRecentCacheEntry(
592
+ resolveContextCachePath,
593
+ buildResolveContextRequestKey({
594
+ indexGeneratedAtMs,
595
+ intent: normalizedIntent,
596
+ targetFile: normalizedTargetFile,
597
+ taskType: normalizedTaskType,
598
+ }),
599
+ {
600
+ maxEntries: DEFAULT_RECENT_CACHE_MAX_ENTRIES,
601
+ },
602
+ ),
603
+ readRecentCacheEntry(
604
+ verifyContextCachePath,
605
+ buildVerifyContextRequestKey({
606
+ indexGeneratedAtMs,
607
+ projectFingerprint,
608
+ intent: normalizedIntent,
609
+ targetFile: normalizedTargetFile,
610
+ taskType: normalizedTaskType,
611
+ }),
612
+ {
613
+ maxEntries: DEFAULT_RECENT_CACHE_MAX_ENTRIES,
614
+ },
615
+ ),
616
+ ]);
617
+
618
+ return {
619
+ contextCacheHit: Boolean(cachedContextEntry?.result),
620
+ verificationCacheHit: Boolean(cachedVerificationEntry?.result),
621
+ contextResult: cachedContextEntry?.result ?? null,
622
+ verificationPlan: cachedVerificationEntry?.result ?? null,
623
+ };
624
+ }
625
+
626
+ async function selectActiveSkills({ rootDir, promptText, commandText, targetFile }) {
627
+ const routeSignals = {
628
+ promptRawText: String(promptText || '').toLowerCase(),
629
+ promptNormalizedText: buildNormalizedRouteSignalText(promptText),
630
+ commandRawText: String(commandText || '').toLowerCase(),
631
+ commandNormalizedText: buildNormalizedRouteSignalText(commandText),
632
+ fileText: String(targetFile || '').toLowerCase(),
633
+ };
634
+ const scoredEntries = ROUTE_CATALOG
635
+ .map((entry) => scoreSkillRouteEntry(entry, routeSignals))
636
+ .filter((entry) => entry.score > 0)
637
+ .sort((a, b) => b.score - a.score || a.order - b.order);
638
+ const active = [];
639
+
640
+ for (const entry of scoredEntries) {
641
+ if (await pathExists(path.join(rootDir, entry.path))) {
642
+ active.push(entry);
643
+ if (active.length >= MAX_ACTIVE_ROUTE_SKILLS) {
644
+ break;
645
+ }
646
+ }
647
+ }
648
+
649
+ return active.map(({ order, ...rest }) => rest);
650
+ }
651
+
652
+ function scoreSkillRouteEntry(entry, routeSignals = {}) {
653
+ let score = 0;
654
+ const reasons = [];
655
+
656
+ for (const signal of entry.signals) {
657
+ const { directText, normalizedText } = getSignalTexts(signal.type, routeSignals);
658
+ signal.regex.lastIndex = 0;
659
+ const directMatch = signal.regex.test(directText);
660
+ signal.regex.lastIndex = 0;
661
+ const normalizedMatch = !directMatch && Boolean(normalizedText) && signal.regex.test(normalizedText);
662
+ if (!directMatch && !normalizedMatch) continue;
663
+ score += directMatch
664
+ ? signal.score
665
+ : Math.max(1, signal.score - 1);
666
+ reasons.push(signal.type);
667
+ }
668
+
669
+ score += deriveSkillPriorityBoost(entry.id, routeSignals);
670
+
671
+ return {
672
+ id: entry.id,
673
+ path: entry.path,
674
+ contextMode: entry.contextMode ?? 'indexed',
675
+ reasons: [...new Set(reasons)],
676
+ score,
677
+ order: entry.order,
678
+ };
679
+ }
680
+
681
+ function deriveSkillPriorityBoost(skillId, routeSignals = {}) {
682
+ const combinedPromptSignals = [
683
+ routeSignals.promptRawText ?? '',
684
+ routeSignals.promptNormalizedText ?? '',
685
+ routeSignals.commandRawText ?? '',
686
+ routeSignals.commandNormalizedText ?? '',
687
+ ].join('\n');
688
+
689
+ if (
690
+ skillId === 'testing-quality'
691
+ && /\b(test|tests|spec|coverage|mock|fixture|assert|vitest|jest|playwright)\b/.test(combinedPromptSignals)
692
+ ) {
693
+ return 1;
694
+ }
695
+
696
+ return 0;
697
+ }
698
+
699
+ function getSignalTexts(signalType, routeSignals = {}) {
700
+ if (signalType === 'command') {
701
+ return {
702
+ directText: routeSignals.commandRawText ?? '',
703
+ normalizedText: routeSignals.commandNormalizedText ?? '',
704
+ };
705
+ }
706
+
707
+ if (signalType === 'file') {
708
+ return {
709
+ directText: routeSignals.fileText ?? '',
710
+ normalizedText: '',
711
+ };
712
+ }
713
+
714
+ return {
715
+ directText: routeSignals.promptRawText ?? '',
716
+ normalizedText: routeSignals.promptNormalizedText ?? '',
717
+ };
718
+ }
719
+
720
+ function shouldUseIndexedContext({ activeSkills = [], targetFile = null } = {}) {
721
+ if (activeSkills.length === 0) {
722
+ return Boolean(targetFile);
723
+ }
724
+
725
+ const primarySkill = activeSkills[0] ?? null;
726
+ const primaryIsStandalone = primarySkill?.contextMode === 'standalone';
727
+ if (!primaryIsStandalone) {
728
+ return true;
729
+ }
730
+
731
+ if (isStandaloneNonIndexedTarget(primarySkill, targetFile)) {
732
+ return false;
733
+ }
734
+
735
+ if (activeSkills.some((skill) => skill?.contextMode !== 'standalone')) {
736
+ return true;
737
+ }
738
+
739
+ return isCodeIndexedTarget(targetFile);
740
+ }
741
+
742
+ function isStandaloneNonIndexedTarget(primarySkill, targetFile) {
743
+ if (typeof targetFile !== 'string' || !targetFile.trim()) {
744
+ return false;
745
+ }
746
+
747
+ const normalized = targetFile.replaceAll('\\', '/');
748
+ if (primarySkill?.id === 'docker-packaging') {
749
+ return /(?:^|\/)(?:Dockerfile|docker-compose(?:\.ya?ml)?|compose\.ya?ml|\.dockerignore)$/i.test(normalized);
750
+ }
751
+
752
+ return false;
753
+ }
754
+
755
+ function isCodeIndexedTarget(filePath) {
756
+ if (typeof filePath !== 'string' || !filePath.trim()) {
757
+ return false;
758
+ }
759
+
760
+ const normalized = filePath.replaceAll('\\', '/');
761
+ if (/\.(?:[cm]?js|jsx|tsx?|vue|py|rb|go|rs|java|kt|swift|php|cs|c|cc|cpp|cxx|h|hpp|m|mm|scala|sh|bash|zsh|sql|psql|css|scss|sass|less|html|xml|json|ya?ml|toml)$/i.test(normalized)) {
762
+ return true;
763
+ }
764
+
765
+ return /(?:^|\/)(?:src|test|tests|app|lib|components|pages|screens|layouts|composables|api|routes?|controllers?|services?|db|database|migrations?|prisma|SQL)\//i.test(normalized);
766
+ }
767
+
768
+ async function deriveVerificationPlan({
769
+ rootDir = process.cwd(),
770
+ intent = '',
771
+ targetFile = null,
772
+ taskType = null,
773
+ contextResult = null,
774
+ skillIds = [],
775
+ fingerprintEntries = [],
776
+ pkg = null,
777
+ } = {}) {
778
+ const absoluteRoot = path.resolve(rootDir);
779
+ const context = contextResult ?? await resolveContext({
780
+ rootDir: absoluteRoot,
781
+ intent,
782
+ targetFile,
783
+ taskType,
784
+ });
785
+
786
+ const resolvedPkg = pkg ?? await readPackageJson(absoluteRoot);
787
+ const scripts = resolvedPkg?.scripts && typeof resolvedPkg.scripts === 'object' ? resolvedPkg.scripts : {};
788
+ const packageManager = detectPackageManager({ fingerprintEntries, pkg: resolvedPkg });
789
+ const primaryTargets = unique([
790
+ ...(context?.primaryTargets ?? []),
791
+ ...(targetFile ? [targetFile] : []),
792
+ ]);
793
+ const relatedTests = unique([
794
+ ...(context?.relatedTests ?? []),
795
+ ...(targetFile && /\.(test|spec)\./.test(targetFile) ? [targetFile] : []),
796
+ ]);
797
+ const commands = [];
798
+ const fallbackCommands = [];
799
+ const reasons = [];
800
+ const notes = [];
801
+ const docsOnly = primaryTargets.length > 0
802
+ && primaryTargets.every((filePath) => filePath.startsWith('docs/'));
803
+ const risky = context?.taskType === 'non-trivial'
804
+ || skillIds.some((skillId) => ['discover-security', 'repo-maintenance'].includes(skillId));
805
+
806
+ let mode = 'minimal';
807
+
808
+ if (docsOnly) {
809
+ mode = 'docs-only';
810
+ reasons.push('docsOnly');
811
+ if (scripts.lint) {
812
+ fallbackCommands.push(buildScriptCommand(packageManager, 'lint'));
813
+ }
814
+ notes.push('Docs-focused change: prefer docs syntax/format verification if available; broad test runs are not the default.');
815
+ } else {
816
+ if (relatedTests.length > 0) {
817
+ mode = 'targeted-tests-first';
818
+ reasons.push('relatedTests');
819
+ if (scripts.test) {
820
+ for (const testFile of relatedTests.slice(0, 2)) {
821
+ commands.push(buildScriptCommand(packageManager, 'test', [testFile]));
822
+ }
823
+ } else {
824
+ notes.push(`Run related tests first: ${relatedTests.slice(0, 2).join(', ')}`);
825
+ }
826
+ }
827
+
828
+ if (risky) {
829
+ reasons.push('riskySkillOrTask');
830
+ if (scripts.test) {
831
+ fallbackCommands.push(buildScriptCommand(packageManager, 'test'));
832
+ }
833
+ }
834
+
835
+ if (scripts.lint) {
836
+ fallbackCommands.push(buildScriptCommand(packageManager, 'lint'));
837
+ }
838
+ if (scripts.typecheck) {
839
+ fallbackCommands.push(buildScriptCommand(packageManager, 'typecheck'));
840
+ }
841
+
842
+ if (commands.length === 0 && scripts.test && risky) {
843
+ mode = 'broader-verification';
844
+ reasons.push('highRiskFallback');
845
+ commands.push(buildScriptCommand(packageManager, 'test'));
846
+ }
847
+ }
848
+
849
+ if (commands.length === 0 && fallbackCommands.length === 0) {
850
+ notes.push('Match verification effort to scope; no project test/lint/typecheck scripts were detected.');
851
+ }
852
+
853
+ const summarizedContext = summarizeContext(context);
854
+ const primaryCommandList = unique(commands);
855
+ const fallbackCommandList = unique(fallbackCommands.filter((command) => !primaryCommandList.includes(command)));
856
+ const reasonList = unique(reasons);
857
+
858
+ return {
859
+ taskType: context?.taskType ?? taskType ?? 'simple',
860
+ packageManager,
861
+ mode,
862
+ commands: primaryCommandList,
863
+ fallbackCommands: fallbackCommandList,
864
+ reasons: reasonList,
865
+ notes: unique(notes),
866
+ executionPolicy: deriveExecutionPolicy({
867
+ taskType: context?.taskType ?? taskType ?? 'simple',
868
+ mode,
869
+ commands: primaryCommandList,
870
+ fallbackCommands: fallbackCommandList,
871
+ reasons: reasonList,
872
+ context: summarizedContext,
873
+ }),
874
+ };
875
+ }
876
+
877
+ function summarizeContext(contextResult) {
878
+ return {
879
+ primaryTargets: contextResult?.primaryTargets ?? [],
880
+ analogFiles: contextResult?.analogFiles ?? [],
881
+ sharedAbstractions: contextResult?.sharedAbstractions ?? [],
882
+ relatedTests: contextResult?.relatedTests ?? [],
883
+ styleFiles: contextResult?.styleFiles ?? [],
884
+ };
885
+ }
886
+
887
+ function expandRouteContext(contextResult) {
888
+ if (!contextResult) return contextResult;
889
+ const directTestTargets = (contextResult.primaryTargets ?? []).filter(isTestLikeFile);
890
+ if (directTestTargets.length === 0) return contextResult;
891
+ return {
892
+ ...contextResult,
893
+ relatedTests: unique([
894
+ ...(contextResult.relatedTests ?? []),
895
+ ...directTestTargets,
896
+ ]),
897
+ };
898
+ }
899
+
900
+ function deriveNextAction({ activeSkills = [], contextRecommendation = null, verificationRecommendation = null }) {
901
+ const policy = verificationRecommendation?.executionPolicy ?? null;
902
+ const primaryCommands = verificationRecommendation?.commands ?? [];
903
+ const fallbackCommands = verificationRecommendation?.fallbackCommands ?? [];
904
+
905
+ if (policy?.requiresHumanConfirmationForBroadOrRisky) {
906
+ return {
907
+ type: 'ask-user-confirmation',
908
+ reason: 'Ask before broad verification because index evidence could not localize a safe targeted lane.',
909
+ followUpCommands: [...primaryCommands, ...fallbackCommands],
910
+ };
911
+ }
912
+
913
+ if (policy?.shouldAutoRunPrimaryCommands && primaryCommands.length > 0) {
914
+ return {
915
+ type: 'run-primary-verification',
916
+ command: primaryCommands[0],
917
+ reason: 'Targeted related verification is available from indexed context.',
918
+ followUpCommands: fallbackCommands,
919
+ };
920
+ }
921
+
922
+ if (policy?.shouldAutoRunFallbacks && fallbackCommands.length > 0) {
923
+ return {
924
+ type: 'run-fallback-verification',
925
+ command: fallbackCommands[0],
926
+ reason: 'No primary verification command was auto-runnable, but routed fallbacks are allowed.',
927
+ followUpCommands: fallbackCommands.slice(1),
928
+ };
929
+ }
930
+
931
+ if (contextRecommendation?.command) {
932
+ return {
933
+ type: 'pull-indexed-context',
934
+ command: contextRecommendation.command,
935
+ reason: 'Resolve the minimal indexed file bundle before widening reads.',
936
+ };
937
+ }
938
+
939
+ if (activeSkills.length > 0) {
940
+ return {
941
+ type: 'read-skill-instructions',
942
+ skillPaths: activeSkills.map((item) => item.path),
943
+ reason: 'Open the matching project-local skill before acting.',
944
+ };
945
+ }
946
+
947
+ return {
948
+ type: 'inspect-structure',
949
+ reason: 'No strong route found; inspect repository structure and indexed files first.',
950
+ };
951
+ }
952
+
953
+ function deriveDelegationRecommendation({
954
+ activeSkills = [],
955
+ routingContext = {},
956
+ contextRecommendation = null,
957
+ verificationRecommendation = null,
958
+ } = {}) {
959
+ const skillIds = activeSkills.map((item) => item.id);
960
+ const lower = `${routingContext.promptText ?? ''}\n${routingContext.commandText ?? ''}`.toLowerCase();
961
+ const preview = contextRecommendation?.preview ?? {};
962
+ const contextBreadth = countDelegationContextBreadth(preview);
963
+ const hasExplicitTarget = Boolean(routingContext.targetFile);
964
+ const localizedIndexedLane = hasExplicitTarget && contextBreadth > 0 && contextBreadth <= 3;
965
+ const hasRelatedTests = (preview.relatedTests ?? []).length > 0;
966
+ const when = contextRecommendation?.command ? 'after-context' : 'now';
967
+
968
+ if (routingContext.taskType === 'trivial') {
969
+ return null;
970
+ }
971
+
972
+ if (
973
+ skillIds.includes('executing-plans')
974
+ || /\b(execute this plan|follow this plan|implementation plan|rollout plan|controlled batches?|review checkpoints?|batch execution|execute in batches)\b/.test(lower)
975
+ ) {
976
+ return {
977
+ hint: 'subagent-driven-development',
978
+ when,
979
+ reason: 'Explicit plan/batch execution is separable enough for deliberate subagent passes.',
980
+ };
981
+ }
982
+
983
+ const noisyDebugLane = skillIds.includes('debugging-toolkit') && (
984
+ Boolean(String(routingContext.commandText ?? '').trim())
985
+ || /\b(debug|error|crash|stack(?: trace)?|failing|flake|flaky|timeout|triage|root cause)\b/.test(lower)
986
+ || verificationRecommendation?.executionPolicy?.policyMode === 'confirm-then-broad'
987
+ );
988
+ if (noisyDebugLane && (!localizedIndexedLane || !hasRelatedTests || contextBreadth >= 4)) {
989
+ return {
990
+ hint: 'bug-debugger',
991
+ when,
992
+ reason: 'A noisy reproduce/trace/fix loop benefits from isolated debugging context.',
993
+ };
994
+ }
995
+
996
+ const hasImplementationSkill = skillIds.some((id) => DELEGATABLE_IMPLEMENTATION_SKILL_IDS.has(id));
997
+ const clearImplementationSignal = /\b(implement|build|create|add|ship|deliver|refactor|integrat(?:e|ion)|scaffold|feature)\b/.test(lower);
998
+ const multiLaneSignal = /\b(multiple|several|parallel|independent|across files|across modules|batch)\b/.test(lower);
999
+ if (
1000
+ hasImplementationSkill
1001
+ && clearImplementationSignal
1002
+ && routingContext.taskType === 'non-trivial'
1003
+ && (!localizedIndexedLane || !hasExplicitTarget || contextBreadth >= 4 || multiLaneSignal)
1004
+ ) {
1005
+ return {
1006
+ hint: 'feature-implementer',
1007
+ when,
1008
+ reason: 'A broader implementation lane can be delegated without dragging all details through the main context.',
1009
+ };
1010
+ }
1011
+
1012
+ return null;
1013
+ }
1014
+
1015
+ function buildRouteSummary({
1016
+ activeSkills = [],
1017
+ routingContext = {},
1018
+ contextRecommendation = null,
1019
+ verificationRecommendation = null,
1020
+ nextAction = null,
1021
+ } = {}) {
1022
+ const delegationRecommendation = deriveDelegationRecommendation({
1023
+ activeSkills,
1024
+ routingContext,
1025
+ contextRecommendation,
1026
+ verificationRecommendation,
1027
+ });
1028
+ const preview = contextRecommendation?.preview ?? {};
1029
+ const primaryTargets = summarizeCompactList(preview.primaryTargets ?? [], 2);
1030
+ const relatedTests = summarizeCompactList(preview.relatedTests ?? [], 2);
1031
+ const styleFiles = summarizeCompactList(preview.styleFiles ?? [], 1);
1032
+ const primaryCommands = unique(verificationRecommendation?.commands ?? []);
1033
+ const fallbackCommands = unique(verificationRecommendation?.fallbackCommands ?? []);
1034
+ const preferredOrder = unique(
1035
+ verificationRecommendation?.executionPolicy?.preferredOrder
1036
+ ?? [...primaryCommands, ...fallbackCommands],
1037
+ );
1038
+ const policyMode = verificationRecommendation?.executionPolicy?.policyMode ?? null;
1039
+ const compactHelperLane = nextAction?.type === 'pull-indexed-context'
1040
+ && typeof contextRecommendation?.command === 'string'
1041
+ && contextRecommendation.command.trim();
1042
+ const helperHint = compactHelperHint(
1043
+ compactHelperLane
1044
+ ? contextRecommendation?.command
1045
+ : (
1046
+ nextAction?.command
1047
+ ? null
1048
+ : verificationRecommendation?.helperCommand
1049
+ ),
1050
+ );
1051
+ const nextActionCommand = compactHelperLane ? null : nextAction?.command ?? null;
1052
+ const line = [
1053
+ routingContext.taskType ? `task=${routingContext.taskType}` : null,
1054
+ formatCompactSegment('targets', primaryTargets),
1055
+ formatCompactSegment('tests', relatedTests),
1056
+ formatCompactSegment('styles', styleFiles),
1057
+ policyMode ? `policy=${policyMode}` : null,
1058
+ ].filter(Boolean).join(' | ');
1059
+
1060
+ return {
1061
+ primaryCommands,
1062
+ fallbackCommands,
1063
+ preferredOrder,
1064
+ policyMode,
1065
+ delegateHint: delegationRecommendation?.hint ?? null,
1066
+ nextActionType: nextAction?.type ?? null,
1067
+ nextActionCommand,
1068
+ helperHint,
1069
+ line: line || 'task=unknown',
1070
+ };
1071
+ }
1072
+
1073
+ function deriveExecutionPolicy({
1074
+ taskType = 'simple',
1075
+ mode = 'minimal',
1076
+ commands = [],
1077
+ fallbackCommands = [],
1078
+ reasons = [],
1079
+ context = {},
1080
+ } = {}) {
1081
+ const relatedTests = context.relatedTests ?? [];
1082
+ const sharedAbstractions = context.sharedAbstractions ?? [];
1083
+ const docsOnly = mode === 'docs-only' || reasons.includes('docsOnly');
1084
+ const risky = taskType === 'non-trivial'
1085
+ || reasons.includes('riskySkillOrTask')
1086
+ || reasons.includes('highRiskFallback');
1087
+ const hasPrimaryCommands = commands.length > 0;
1088
+ const hasTargetedPrimaryCommands = mode === 'targeted-tests-first' && hasPrimaryCommands;
1089
+ const sharedScope = sharedAbstractions.length > 0;
1090
+ const noRelatedTests = relatedTests.length === 0;
1091
+ const broadOnlyPrimary = hasPrimaryCommands && !hasTargetedPrimaryCommands && noRelatedTests;
1092
+ const requiresHumanConfirmationForBroadOrRisky = risky && broadOnlyPrimary;
1093
+ const shouldAutoRunPrimaryCommands = hasPrimaryCommands && !docsOnly && !requiresHumanConfirmationForBroadOrRisky;
1094
+ const shouldEscalateToFallbacks = !docsOnly && fallbackCommands.length > 0 && (risky || sharedScope);
1095
+ const shouldAutoRunFallbacks = shouldEscalateToFallbacks && !requiresHumanConfirmationForBroadOrRisky;
1096
+
1097
+ let policyMode = 'recommend-only';
1098
+ if (docsOnly) {
1099
+ policyMode = 'docs-light';
1100
+ } else if (hasTargetedPrimaryCommands && shouldEscalateToFallbacks) {
1101
+ policyMode = 'auto-run-targeted-then-fallback';
1102
+ } else if (hasTargetedPrimaryCommands) {
1103
+ policyMode = 'auto-run-targeted';
1104
+ } else if (requiresHumanConfirmationForBroadOrRisky) {
1105
+ policyMode = 'confirm-then-broad';
1106
+ } else if (shouldAutoRunPrimaryCommands) {
1107
+ policyMode = 'auto-run-primary';
1108
+ }
1109
+
1110
+ return {
1111
+ policyMode,
1112
+ shouldAutoRunPrimaryCommands,
1113
+ shouldAutoRunTargetedVerification: hasTargetedPrimaryCommands && shouldAutoRunPrimaryCommands,
1114
+ shouldEscalateToFallbacks,
1115
+ shouldAutoRunFallbacks,
1116
+ requiresHumanConfirmationForBroadOrRisky,
1117
+ preferredOrder: [...commands, ...fallbackCommands],
1118
+ reasons: unique([
1119
+ ...reasons,
1120
+ sharedScope ? 'sharedScope' : null,
1121
+ noRelatedTests ? 'noRelatedTests' : null,
1122
+ ]),
1123
+ };
1124
+ }
1125
+
1126
+ function deriveContextIntent({ promptText, commandText, targetFile, selectedIds }) {
1127
+ if (promptText.trim()) return promptText.trim();
1128
+ if (commandText.includes('triage.mjs')) return `debug ${commandText.trim()}`.trim();
1129
+ if (commandText.includes('query-index.mjs')) return `investigate ${commandText.trim()}`.trim();
1130
+ if (/\b(test|vitest|jest|playwright|cypress)\b/i.test(commandText)) {
1131
+ return targetFile ? `fix failing test around ${targetFile}` : 'fix failing test';
1132
+ }
1133
+ if (targetFile) {
1134
+ if (/^docs\//i.test(targetFile)) return `update docs for ${targetFile}`;
1135
+ return `work on ${targetFile}`;
1136
+ }
1137
+ if (selectedIds.includes('docs-quality')) return 'update documentation';
1138
+ if (selectedIds.includes('debugging-toolkit')) return 'debug failing issue';
1139
+ if (selectedIds.includes('testing-quality')) return 'write targeted test';
1140
+ if (selectedIds.includes('code-review')) return 'review change for regressions';
1141
+ if (selectedIds.includes('repo-maintenance')) return 'refresh stale workspace';
1142
+ if (selectedIds.includes('discover-security')) return 'review security-sensitive change';
1143
+ return '';
1144
+ }
1145
+
1146
+ function inferTaskType({ promptText, commandText, selectedIds }) {
1147
+ const lower = buildNormalizedRouteSignalText(promptText, commandText);
1148
+ if (
1149
+ selectedIds.includes('discover-security')
1150
+ || selectedIds.includes('repo-maintenance')
1151
+ || /\b(auth|security|token|permission|secret|migration|uninstall|delete all|drop table|race|flaky|timeout)\b/.test(lower)
1152
+ ) {
1153
+ return 'non-trivial';
1154
+ }
1155
+ if (/\b(typo|label|text|rename|color|spacing|toggle|comment)\b/.test(lower)) {
1156
+ return 'trivial';
1157
+ }
1158
+ return 'simple';
1159
+ }
1160
+
1161
+ function buildHelperCommand({ commandNamespace = '.claude', scriptName, intent = '', targetFile = null, taskType = null }) {
1162
+ const parts = ['node', `${commandNamespace}/ukit/index/${scriptName}`];
1163
+ if (intent) parts.push(JSON.stringify(intent));
1164
+ if (targetFile) parts.push('--target', JSON.stringify(targetFile));
1165
+ if (taskType) parts.push('--type', taskType);
1166
+ return parts.join(' ');
1167
+ }
1168
+
1169
+ function isTestLikeFile(filePath) {
1170
+ return /\.(test|spec)\.[a-z0-9]+$/i.test(filePath)
1171
+ || /(^|\/)(?:__tests__|tests?|specs?)\//i.test(filePath);
1172
+ }
1173
+
1174
+ function summarizeCompactList(values, limit = 2) {
1175
+ const normalized = unique(values);
1176
+ return {
1177
+ items: normalized.slice(0, limit),
1178
+ total: normalized.length,
1179
+ };
1180
+ }
1181
+
1182
+ function countDelegationContextBreadth(preview = {}) {
1183
+ return unique([
1184
+ ...(preview.primaryTargets ?? []),
1185
+ ...(preview.analogFiles ?? []),
1186
+ ...(preview.sharedAbstractions ?? []),
1187
+ ...(preview.relatedTests ?? []),
1188
+ ]).length;
1189
+ }
1190
+
1191
+ function formatCompactSegment(label, summary) {
1192
+ const items = summary?.items ?? [];
1193
+ if (items.length === 0) return null;
1194
+ const extraCount = Math.max((summary?.total ?? items.length) - items.length, 0);
1195
+ return `${label}=${items.join(',')}${extraCount > 0 ? `,+${extraCount}` : ''}`;
1196
+ }
1197
+
1198
+ function compactHelperHint(command) {
1199
+ if (typeof command !== 'string' || !command.trim()) return null;
1200
+
1201
+ const normalized = command.trim().replace(/\s+/g, ' ');
1202
+ const scriptMatch = normalized.match(/^node\s+([^\s]+)(?:\s+(.*))?$/i);
1203
+ if (!scriptMatch) {
1204
+ return normalized.length > 96 ? `${normalized.slice(0, 93)}...` : normalized;
1205
+ }
1206
+
1207
+ const scriptName = scriptMatch[1].split('/').pop() ?? scriptMatch[1];
1208
+ const rest = scriptMatch[2] ?? '';
1209
+ const targetMatch = rest.match(/--target\s+("[^"]+"|'[^']+'|\S+)/);
1210
+ const typeMatch = rest.match(/--type\s+(\S+)/);
1211
+ const parts = [scriptName];
1212
+
1213
+ if (targetMatch) {
1214
+ parts.push(`target=${stripShellQuotes(targetMatch[1])}`);
1215
+ }
1216
+ if (typeMatch) {
1217
+ parts.push(`type=${typeMatch[1]}`);
1218
+ }
1219
+
1220
+ return parts.join(' ');
1221
+ }
1222
+
1223
+ function stripShellQuotes(value) {
1224
+ return String(value ?? '').replace(/^['"]|['"]$/g, '');
1225
+ }
1226
+
1227
+ async function readPackageJson(rootDir) {
1228
+ try {
1229
+ const raw = await fs.readFile(path.join(rootDir, 'package.json'), 'utf8');
1230
+ return JSON.parse(raw);
1231
+ } catch {
1232
+ return null;
1233
+ }
1234
+ }
1235
+
1236
+ function detectPackageManager({ fingerprintEntries = [], pkg = null } = {}) {
1237
+ const declaredPackageManager = getDeclaredPackageManager(pkg);
1238
+ if (declaredPackageManager) return declaredPackageManager;
1239
+
1240
+ const existingFiles = new Set(
1241
+ fingerprintEntries
1242
+ .filter((entry) => entry && entry.mtimeMs !== null)
1243
+ .map((entry) => entry.filePath),
1244
+ );
1245
+
1246
+ const checks = [
1247
+ ['pnpm-lock.yaml', 'pnpm'],
1248
+ ['yarn.lock', 'yarn'],
1249
+ ['bun.lockb', 'bun'],
1250
+ ['bun.lock', 'bun'],
1251
+ ['package-lock.json', 'npm'],
1252
+ ];
1253
+
1254
+ for (const [lockFile, pm] of checks) {
1255
+ if (existingFiles.has(lockFile)) {
1256
+ return pm;
1257
+ }
1258
+ }
1259
+
1260
+ return 'npm';
1261
+ }
1262
+
1263
+ async function readProjectVerificationFingerprint(rootDir) {
1264
+ const entries = [];
1265
+ let pkg = null;
1266
+
1267
+ try {
1268
+ const packageJsonPath = path.join(rootDir, 'package.json');
1269
+ const [raw, stat] = await Promise.all([
1270
+ fs.readFile(packageJsonPath, 'utf8'),
1271
+ fs.stat(packageJsonPath),
1272
+ ]);
1273
+ pkg = JSON.parse(raw);
1274
+ entries.push({
1275
+ filePath: 'package.json',
1276
+ size: stat.size,
1277
+ mtimeMs: stat.mtimeMs,
1278
+ });
1279
+ } catch {
1280
+ entries.push({
1281
+ filePath: 'package.json',
1282
+ size: null,
1283
+ mtimeMs: null,
1284
+ });
1285
+ }
1286
+
1287
+ const trackedFiles = getDeclaredPackageManager(pkg)
1288
+ ? []
1289
+ : [
1290
+ 'package-lock.json',
1291
+ 'pnpm-lock.yaml',
1292
+ 'yarn.lock',
1293
+ 'bun.lockb',
1294
+ 'bun.lock',
1295
+ ];
1296
+
1297
+ for (const relativePath of trackedFiles) {
1298
+ const absolutePath = path.join(rootDir, relativePath);
1299
+ try {
1300
+ const stat = await fs.stat(absolutePath);
1301
+ entries.push({
1302
+ filePath: relativePath,
1303
+ size: stat.size,
1304
+ mtimeMs: stat.mtimeMs,
1305
+ });
1306
+ } catch {
1307
+ entries.push({
1308
+ filePath: relativePath,
1309
+ size: null,
1310
+ mtimeMs: null,
1311
+ });
1312
+ }
1313
+ }
1314
+
1315
+ return {
1316
+ pkg,
1317
+ entries,
1318
+ fingerprint: JSON.stringify(entries),
1319
+ };
1320
+ }
1321
+
1322
+ function getDeclaredPackageManager(pkg = null) {
1323
+ const declared = String(pkg?.packageManager ?? '').toLowerCase();
1324
+ if (declared.startsWith('pnpm')) return 'pnpm';
1325
+ if (declared.startsWith('yarn')) return 'yarn';
1326
+ if (declared.startsWith('bun')) return 'bun';
1327
+ if (declared.startsWith('npm')) return 'npm';
1328
+ return null;
1329
+ }
1330
+
1331
+ function buildScriptCommand(packageManager, scriptName, args = []) {
1332
+ const filteredArgs = args.filter(Boolean);
1333
+ if (packageManager === 'npm') {
1334
+ if (scriptName === 'test') {
1335
+ return ['npm', 'test', filteredArgs.length > 0 ? '--' : '', ...filteredArgs].filter(Boolean).join(' ');
1336
+ }
1337
+ return ['npm', 'run', scriptName, filteredArgs.length > 0 ? '--' : '', ...filteredArgs].filter(Boolean).join(' ');
1338
+ }
1339
+ return [packageManager, scriptName, ...filteredArgs].join(' ');
1340
+ }
1341
+
1342
+ function normalizeRelativeFile(rootDir, rawFilePath) {
1343
+ if (typeof rawFilePath !== 'string' || !rawFilePath.trim()) return null;
1344
+
1345
+ const trimmed = rawFilePath.trim();
1346
+ if (path.isAbsolute(trimmed)) {
1347
+ const relative = path.relative(rootDir, trimmed);
1348
+ if (relative && !relative.startsWith('..') && !path.isAbsolute(relative)) {
1349
+ return relative.replaceAll('\\', '/');
1350
+ }
1351
+ return trimmed.replaceAll('\\', '/');
1352
+ }
1353
+
1354
+ return trimmed.replace(/^\.\/+/, '').replaceAll('\\', '/');
1355
+ }
1356
+
1357
+ async function pathExists(filePath) {
1358
+ try {
1359
+ await fs.access(filePath);
1360
+ return true;
1361
+ } catch {
1362
+ return false;
1363
+ }
1364
+ }
1365
+
1366
+ function unique(values) {
1367
+ return [...new Set(values.filter(Boolean))];
1368
+ }
1369
+
1370
+ const DELEGATABLE_IMPLEMENTATION_SKILL_IDS = new Set([
1371
+ 'delivery',
1372
+ 'frontend',
1373
+ 'frontend-vue',
1374
+ 'backend-api',
1375
+ 'postgres',
1376
+ 'sql-optimization-patterns',
1377
+ ]);
1378
+
1379
+ function getRootDir(argv) {
1380
+ const value = readFlagValue(argv, '--root');
1381
+ return value ? path.resolve(value) : process.cwd();
1382
+ }
1383
+
1384
+ function readFlagValue(argv, flag) {
1385
+ const exact = argv.indexOf(flag);
1386
+ if (exact >= 0 && argv[exact + 1]) return argv[exact + 1];
1387
+ const withEquals = argv.find((item) => item.startsWith(`${flag}=`));
1388
+ return withEquals ? withEquals.slice(flag.length + 1) : null;
1389
+ }
1390
+
1391
+ function isFlagOrValue(argv, index) {
1392
+ const arg = argv[index];
1393
+ if (!arg.startsWith('--')) {
1394
+ const prev = argv[index - 1];
1395
+ return Boolean(prev && ['--root', '--target', '--type', '--tool-command', '--last-prompt', '--adapter'].includes(prev));
1396
+ }
1397
+ return true;
1398
+ }
1399
+
1400
+ function inferAdapterFromInvocation(invocationPath) {
1401
+ if (invocationPath.includes('.codex/')) return 'codex';
1402
+ if (invocationPath.includes('.antigravity/')) return 'antigravity';
1403
+ return 'claude';
1404
+ }
1405
+
1406
+ function buildResolveContextRequestKey({
1407
+ indexGeneratedAtMs = null,
1408
+ intent = '',
1409
+ targetFile = null,
1410
+ taskType = null,
1411
+ } = {}) {
1412
+ return buildCompactMachineKey('resolve-v1', {
1413
+ indexGeneratedAtMs,
1414
+ intent: String(intent || '').trim(),
1415
+ targetFile: String(targetFile || '').trim(),
1416
+ taskType: String(taskType || '').trim(),
1417
+ });
1418
+ }
1419
+
1420
+ function buildVerifyContextRequestKey({
1421
+ indexGeneratedAtMs = null,
1422
+ projectFingerprint = '',
1423
+ intent = '',
1424
+ targetFile = null,
1425
+ taskType = null,
1426
+ } = {}) {
1427
+ return buildCompactMachineKey('verify-v1', {
1428
+ indexGeneratedAtMs,
1429
+ projectFingerprint,
1430
+ intent: String(intent || '').trim(),
1431
+ targetFile: String(targetFile || '').trim(),
1432
+ taskType: String(taskType || '').trim(),
1433
+ });
1434
+ }
1435
+
1436
+ function buildRouteRequestKey({
1437
+ commandNamespace = '.claude',
1438
+ indexGeneratedAtMs = null,
1439
+ projectFingerprint = null,
1440
+ activeSkills = [],
1441
+ routingContext = {},
1442
+ previousContextSnapshot = null,
1443
+ recentOutputSnapshot = null,
1444
+ } = {}) {
1445
+ return buildCompactMachineKey('route-v2', {
1446
+ commandNamespace,
1447
+ indexGeneratedAtMs,
1448
+ projectFingerprint,
1449
+ previousContextFingerprint: previousContextSnapshot?.fingerprint ?? null,
1450
+ recentOutputFingerprint: recentOutputSnapshot?.fingerprint ?? null,
1451
+ skillIds: activeSkills.map((item) => item.id),
1452
+ skillPaths: activeSkills.map((item) => item.path),
1453
+ skillContextModes: activeSkills.map((item) => item.contextMode ?? 'indexed'),
1454
+ promptText: routingContext.promptText ?? '',
1455
+ lastExplicitUserPromptText: routingContext.lastExplicitUserPromptText ?? '',
1456
+ adapter: routingContext.adapter ?? 'claude',
1457
+ commandText: routingContext.commandText ?? '',
1458
+ targetFile: routingContext.targetFile ?? '',
1459
+ contextIntent: routingContext.contextIntent ?? '',
1460
+ taskType: routingContext.taskType ?? '',
1461
+ });
1462
+ }
1463
+
1464
+ function buildRouteStateFingerprint(route = {}) {
1465
+ return buildCompactMachineKey('routefp-v3', buildStructuredRouteFingerprintPayload(route));
1466
+ }
1467
+
1468
+ function buildStructuredRouteFingerprintPayload(route = {}) {
1469
+ const routeSummary = route.routeSummary ?? null;
1470
+ return {
1471
+ skills: (route.activeSkills ?? []).map((item) => item.id),
1472
+ adapter: route.routingContext?.adapter ?? null,
1473
+ taskType: route.routingContext?.taskType ?? null,
1474
+ previousContextLine: route.previousContext?.line ?? null,
1475
+ previousContextIds: unique(route.previousContext?.selectedIds ?? []),
1476
+ recentOutputLine: route.recentOutput?.line ?? null,
1477
+ policyMode: routeSummary?.policyMode ?? null,
1478
+ delegateHint: routeSummary?.delegateHint ?? null,
1479
+ nextActionType: routeSummary?.nextActionType ?? null,
1480
+ nextActionCommand: routeSummary?.nextActionCommand ?? null,
1481
+ helperHint: routeSummary?.helperHint ?? null,
1482
+ primaryCommands: unique(routeSummary?.primaryCommands ?? []),
1483
+ fallbackCommands: unique(routeSummary?.fallbackCommands ?? []),
1484
+ preferredOrder: unique(routeSummary?.preferredOrder ?? []),
1485
+ };
1486
+ }
1487
+
1488
+ function buildCompactMachineKey(prefix, payload) {
1489
+ return `${prefix}:${stableMachineDigest(payload)}`;
1490
+ }
1491
+
1492
+ function stableMachineDigest(payload) {
1493
+ return crypto
1494
+ .createHash('sha256')
1495
+ .update(JSON.stringify(payload))
1496
+ .digest('base64url');
1497
+ }
1498
+
1499
+ function reuseSharedRouteState({ previousState, requestKey }) {
1500
+ if (!previousState || previousState.requestKey !== requestKey) {
1501
+ return null;
1502
+ }
1503
+
1504
+ if (!previousState.routeSummary || !Array.isArray(previousState.activeSkills)) {
1505
+ return null;
1506
+ }
1507
+
1508
+ return previousState;
1509
+ }
1510
+
1511
+ function createSharedRouteState({
1512
+ route,
1513
+ source = 'task-route',
1514
+ requestKey = null,
1515
+ }) {
1516
+ const helpers = compactHelpers({
1517
+ verificationRecommendation: route.verificationRecommendation ?? null,
1518
+ routeSummary: route.routeSummary ?? null,
1519
+ });
1520
+
1521
+ return {
1522
+ requestKey,
1523
+ fingerprint: buildRouteStateFingerprint(route),
1524
+ ts: Date.now(),
1525
+ source,
1526
+ activeSkills: compactActiveSkills(route.activeSkills ?? []),
1527
+ routingContext: compactRoutingContext(route.routingContext ?? {}),
1528
+ ...(route.previousContext ? { previousContext: compactPreviousContext(route.previousContext) } : {}),
1529
+ ...(route.recentOutput ? { recentOutput: compactRecentOutput(route.recentOutput) } : {}),
1530
+ ...(helpers ? { helpers } : {}),
1531
+ routeSummary: compactRouteSummary(route.routeSummary ?? null),
1532
+ };
1533
+ }
1534
+
1535
+ function compactRouteCacheState(sharedState = {}) {
1536
+ if (!sharedState || typeof sharedState !== 'object') {
1537
+ return sharedState;
1538
+ }
1539
+
1540
+ const compactState = { ...sharedState };
1541
+ delete compactState.source;
1542
+ delete compactState.ts;
1543
+ return compactState;
1544
+ }
1545
+
1546
+ function compactActiveSkills(activeSkills = []) {
1547
+ return activeSkills.map((item) => ({
1548
+ id: item.id,
1549
+ }));
1550
+ }
1551
+
1552
+ function compactRoutingContext(routingContext = {}) {
1553
+ return {
1554
+ adapter: routingContext.adapter ?? 'claude',
1555
+ lastExplicitUserPromptText: routingContext.lastExplicitUserPromptText ?? '',
1556
+ taskType: routingContext.taskType ?? null,
1557
+ };
1558
+ }
1559
+
1560
+ function compactHelpers({
1561
+ verificationRecommendation = null,
1562
+ routeSummary = null,
1563
+ } = {}) {
1564
+ const verificationHelperHint = routeSummary?.helperHint
1565
+ ? null
1566
+ : compactHelperHint(verificationRecommendation?.helperCommand ?? null);
1567
+
1568
+ if (!verificationHelperHint) {
1569
+ return null;
1570
+ }
1571
+
1572
+ return {
1573
+ verificationHelperHint,
1574
+ };
1575
+ }
1576
+
1577
+ function compactPreviousContext(previousContext = null) {
1578
+ if (!previousContext || typeof previousContext !== 'object') {
1579
+ return null;
1580
+ }
1581
+
1582
+ const line = typeof previousContext.line === 'string' ? previousContext.line.trim() : '';
1583
+ if (!line) {
1584
+ return null;
1585
+ }
1586
+
1587
+ return {
1588
+ line,
1589
+ selectedIds: unique(previousContext.selectedIds ?? []).slice(0, 3),
1590
+ };
1591
+ }
1592
+
1593
+ function compactRecentOutput(recentOutput = null) {
1594
+ if (!recentOutput || typeof recentOutput !== 'object') {
1595
+ return null;
1596
+ }
1597
+
1598
+ const line = typeof recentOutput.line === 'string' ? recentOutput.line.trim() : '';
1599
+ if (!line) {
1600
+ return null;
1601
+ }
1602
+
1603
+ return {
1604
+ line,
1605
+ };
1606
+ }
1607
+
1608
+ function compactRouteSummary(routeSummary = null) {
1609
+ if (!routeSummary) {
1610
+ return null;
1611
+ }
1612
+
1613
+ return {
1614
+ line: routeSummary.line ?? 'task=unknown',
1615
+ primaryCommands: unique(routeSummary.primaryCommands ?? []).slice(0, 2),
1616
+ fallbackCommands: unique(routeSummary.fallbackCommands ?? []).slice(0, 3),
1617
+ preferredOrder: unique(routeSummary.preferredOrder ?? []).slice(0, 5),
1618
+ policyMode: routeSummary.policyMode ?? null,
1619
+ delegateHint: routeSummary.delegateHint ?? null,
1620
+ nextActionType: routeSummary.nextActionType ?? null,
1621
+ nextActionCommand: routeSummary.nextActionCommand ?? null,
1622
+ helperHint: routeSummary.helperHint ?? null,
1623
+ };
1624
+ }
1625
+
1626
+ function shouldIncludePreviousContext({
1627
+ routingContext = {},
1628
+ useIndexedContext = true,
1629
+ } = {}) {
1630
+ const taskType = String(routingContext?.taskType ?? '').trim();
1631
+ const hasExplicitTarget = Boolean(String(routingContext?.targetFile ?? '').trim());
1632
+
1633
+ if (!hasExplicitTarget) {
1634
+ return true;
1635
+ }
1636
+
1637
+ if (!useIndexedContext) {
1638
+ return true;
1639
+ }
1640
+
1641
+ return taskType !== 'simple' && taskType !== 'trivial';
1642
+ }
1643
+
1644
+ function shouldIncludeRecentOutput({
1645
+ activeSkills = [],
1646
+ routingContext = {},
1647
+ useIndexedContext = true,
1648
+ } = {}) {
1649
+ const taskType = String(routingContext?.taskType ?? '').trim();
1650
+ const hasExplicitTarget = Boolean(String(routingContext?.targetFile ?? '').trim());
1651
+ const primarySkillId = String(activeSkills?.[0]?.id ?? '').trim();
1652
+
1653
+ if (!useIndexedContext) {
1654
+ return false;
1655
+ }
1656
+
1657
+ if (!hasExplicitTarget) {
1658
+ return true;
1659
+ }
1660
+
1661
+ if (
1662
+ (taskType === 'simple' || taskType === 'trivial')
1663
+ && primarySkillId === 'testing-quality'
1664
+ ) {
1665
+ return false;
1666
+ }
1667
+
1668
+ return true;
1669
+ }
1670
+
1671
+ async function readJson(filePath, fallback = null) {
1672
+ try {
1673
+ return JSON.parse(await fs.readFile(filePath, 'utf8'));
1674
+ } catch {
1675
+ return fallback;
1676
+ }
1677
+ }
1678
+
1679
+ async function writeJson(filePath, value) {
1680
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
1681
+ await fs.writeFile(filePath, JSON.stringify(value), 'utf8');
1682
+ }
1683
+
1684
+ function normalize(text) {
1685
+ return String(text ?? '')
1686
+ .toLowerCase()
1687
+ .replace(/[^\p{L}\p{N}\s./:_-]/gu, ' ')
1688
+ .replace(/\s+/g, ' ')
1689
+ .trim();
1690
+ }
1691
+
1692
+ function tokenize(text) {
1693
+ return normalize(text)
1694
+ .split(/\s+/)
1695
+ .filter((token) => token && !STOPWORDS.has(token) && token.length > 1);
1696
+ }
1697
+
1698
+ function getMemoryTimestamp(item) {
1699
+ return item.content?.updatedAt
1700
+ ?? item.content?.endedAt
1701
+ ?? item.content?.startedAt
1702
+ ?? 0;
1703
+ }
1704
+
1705
+ function buildMemorySegments(item) {
1706
+ const content = item.content ?? {};
1707
+ if (item.type === 'project') {
1708
+ return [
1709
+ { text: content.name, weight: 2 },
1710
+ { text: content.architecture, weight: 3 },
1711
+ { text: (content.techStack ?? []).join(' '), weight: 2 },
1712
+ { text: (content.activeRules ?? []).join(' '), weight: 2 },
1713
+ { text: (content.decisions ?? []).map((decision) => `${decision.what} ${decision.why}`).join(' '), weight: 7 },
1714
+ ];
1715
+ }
1716
+
1717
+ if (item.type === 'session') {
1718
+ return [
1719
+ { text: content.taskDescription, weight: 4 },
1720
+ { text: content.outcome, weight: 1 },
1721
+ { text: (content.keyActions ?? []).join(' '), weight: 3 },
1722
+ { text: (content.nextSteps ?? []).join(' '), weight: 2 },
1723
+ { text: (content.filesChanged ?? []).join(' '), weight: 1 },
1724
+ { text: (content.decisionsMade ?? []).map((decision) => `${decision.what} ${decision.why}`).join(' '), weight: 4 },
1725
+ ];
1726
+ }
1727
+
1728
+ return [
1729
+ { text: JSON.stringify(content.preferences ?? {}), weight: 1 },
1730
+ { text: (content.rules ?? []).join(' '), weight: 2 },
1731
+ ];
1732
+ }
1733
+
1734
+ function scoreMemoryItem(item, queryTokens) {
1735
+ if (queryTokens.length === 0) {
1736
+ return 0;
1737
+ }
1738
+
1739
+ let score = 0;
1740
+ for (const segment of buildMemorySegments(item)) {
1741
+ const segmentTokens = new Set(tokenize(segment.text));
1742
+ for (const token of queryTokens) {
1743
+ if (segmentTokens.has(token)) {
1744
+ score += segment.weight;
1745
+ }
1746
+ }
1747
+ }
1748
+
1749
+ if (score === 0) {
1750
+ return 0;
1751
+ }
1752
+
1753
+ const recencyBonus = getMemoryTimestamp(item) > 0 ? Math.min(1, getMemoryTimestamp(item) / Date.now()) : 0;
1754
+ return score + recencyBonus;
1755
+ }
1756
+
1757
+ async function readDirectoryJsonItems(dirPath) {
1758
+ let entries = [];
1759
+ try {
1760
+ entries = await fs.readdir(dirPath, { withFileTypes: true });
1761
+ } catch {
1762
+ return [];
1763
+ }
1764
+
1765
+ const items = [];
1766
+ for (const entry of entries) {
1767
+ if (!entry.isFile() || !entry.name.endsWith('.json')) {
1768
+ continue;
1769
+ }
1770
+
1771
+ const fullPath = path.join(dirPath, entry.name);
1772
+ const content = await readJson(fullPath, null);
1773
+ if (content && typeof content === 'object') {
1774
+ items.push({
1775
+ fileName: entry.name,
1776
+ filePath: fullPath,
1777
+ content,
1778
+ });
1779
+ }
1780
+ }
1781
+
1782
+ return items.sort((left, right) => left.fileName.localeCompare(right.fileName));
1783
+ }
1784
+
1785
+ async function detectProjectId(rootDir) {
1786
+ const pkg = await readJson(path.join(rootDir, 'package.json'), null);
1787
+ if (typeof pkg?.name === 'string' && pkg.name.trim()) {
1788
+ return pkg.name.trim();
1789
+ }
1790
+
1791
+ return path.basename(rootDir);
1792
+ }
1793
+
1794
+ async function listMemoryItems(rootDir) {
1795
+ const runtimeRoot = path.join(rootDir, '.ukit', 'storage', 'memory');
1796
+ const userMemory = (await readJson(path.join(runtimeRoot, 'user.json'), null)) ?? { preferences: {}, rules: [] };
1797
+ const projectMemories = await readDirectoryJsonItems(path.join(runtimeRoot, 'projects'));
1798
+ const sessionMemories = await readDirectoryJsonItems(path.join(runtimeRoot, 'sessions'));
1799
+
1800
+ return [
1801
+ {
1802
+ id: 'user:user',
1803
+ type: 'user',
1804
+ content: userMemory,
1805
+ },
1806
+ ...projectMemories.map((item) => ({
1807
+ id: `project:${item.content.id ?? item.fileName.replace(/\.json$/, '')}`,
1808
+ type: 'project',
1809
+ content: item.content,
1810
+ })),
1811
+ ...sessionMemories.map((item) => ({
1812
+ id: `session:${item.content.id ?? item.fileName.replace(/\.json$/, '')}`,
1813
+ type: 'session',
1814
+ content: item.content,
1815
+ })),
1816
+ ];
1817
+ }
1818
+
1819
+ function buildPreviousContextSnippet(item) {
1820
+ const content = item.content ?? {};
1821
+ if (item.type === 'project') {
1822
+ const decisions = compactPhraseList((content.decisions ?? []).map((decision) => decision.what), { limit: 1 });
1823
+ const rules = compactPhraseList(content.activeRules ?? [], { limit: 1 });
1824
+ return `[project] ${content.name ?? content.id ?? 'project'} — decisions: ${decisions || 'n/a'}${rules ? ` — rules: ${rules}` : ''}`;
1825
+ }
1826
+
1827
+ if (item.type === 'session') {
1828
+ const actions = compactPhraseList(content.keyActions ?? [], { limit: 1 });
1829
+ const nextSteps = compactPhraseList(content.nextSteps ?? [], { limit: 1 });
1830
+ return `[session] ${content.taskDescription ?? item.id} — outcome: ${content.outcome ?? 'unknown'}${actions ? ` — actions: ${actions}` : ''}${nextSteps ? ` — next: ${nextSteps}` : ''}`;
1831
+ }
1832
+
1833
+ const rules = compactPhraseList(content.rules ?? [], { limit: 1 });
1834
+ return `[user] rules: ${rules || 'n/a'}`;
1835
+ }
1836
+
1837
+ function compactPhraseList(values, { limit = 1, fallback = 'n/a' } = {}) {
1838
+ const normalized = unique(
1839
+ (values ?? [])
1840
+ .map((value) => String(value ?? '').trim())
1841
+ .filter(Boolean),
1842
+ );
1843
+
1844
+ if (normalized.length === 0) {
1845
+ return fallback;
1846
+ }
1847
+
1848
+ const shown = normalized.slice(0, limit);
1849
+ const remaining = normalized.length - shown.length;
1850
+ return remaining > 0
1851
+ ? `${shown.join('; ')} (+${remaining} more)`
1852
+ : shown.join('; ');
1853
+ }
1854
+
1855
+ async function buildPreviousContextSnapshot({ rootDir = process.cwd(), routingContext = {} } = {}) {
1856
+ const taskQuery = String(
1857
+ routingContext?.lastExplicitUserPromptText
1858
+ || routingContext?.promptText
1859
+ || routingContext?.targetFile
1860
+ || ''
1861
+ ).trim();
1862
+ if (!taskQuery) {
1863
+ return null;
1864
+ }
1865
+
1866
+ const projectId = await detectProjectId(rootDir);
1867
+ const items = await listMemoryItems(rootDir);
1868
+ const queryTokens = tokenize(taskQuery);
1869
+ const rankedItems = items
1870
+ .filter((item) => item.type !== 'user')
1871
+ .filter((item) => (
1872
+ item.type === 'project'
1873
+ ? (item.content?.id === projectId)
1874
+ : (item.type === 'session' ? item.content?.projectId === projectId : true)
1875
+ ))
1876
+ .map((item) => ({
1877
+ item,
1878
+ score: scoreMemoryItem(item, queryTokens),
1879
+ }))
1880
+ .filter((entry) => entry.score > 0)
1881
+ .sort((left, right) => (
1882
+ right.score - left.score
1883
+ || getMemoryTimestamp(right.item) - getMemoryTimestamp(left.item)
1884
+ ))
1885
+ .slice(0, 2)
1886
+ .map((entry) => entry.item);
1887
+
1888
+ if (rankedItems.length === 0) {
1889
+ return null;
1890
+ }
1891
+
1892
+ return {
1893
+ line: rankedItems.map((item) => buildPreviousContextSnippet(item)).join(' | '),
1894
+ selectedIds: rankedItems.map((item) => item.id),
1895
+ fingerprint: buildCompactMachineKey('route-memory-v1', {
1896
+ taskQuery: normalize(taskQuery),
1897
+ projectId,
1898
+ items: rankedItems.map((item) => ({
1899
+ id: item.id,
1900
+ timestamp: getMemoryTimestamp(item),
1901
+ })),
1902
+ }),
1903
+ };
1904
+ }
1905
+
1906
+ async function buildRecentOutputSnapshot({ rootDir = process.cwd() } = {}) {
1907
+ const outputHistoryPath = path.join(rootDir, '.ukit', 'storage', 'cache', 'output-history.json');
1908
+ const outputHistory = await readJson(outputHistoryPath, { entries: [] });
1909
+ const entries = Array.isArray(outputHistory?.entries) ? outputHistory.entries : [];
1910
+ const freshEntries = entries
1911
+ .filter((entry) => (
1912
+ typeof entry?.timestamp === 'number'
1913
+ && (Date.now() - entry.timestamp) <= (30 * 60 * 1000)
1914
+ && typeof entry?.summary === 'string'
1915
+ && entry.summary.trim()
1916
+ ))
1917
+ .sort((left, right) => Number(right?.timestamp || 0) - Number(left?.timestamp || 0));
1918
+ const entry = freshEntries[0];
1919
+ if (!entry) {
1920
+ return null;
1921
+ }
1922
+
1923
+ return {
1924
+ line: compactInlineSummary(entry.summary),
1925
+ fingerprint: buildCompactMachineKey('route-output-v1', {
1926
+ command: String(entry.command || ''),
1927
+ summary: String(entry.summary || ''),
1928
+ timestamp: entry.timestamp,
1929
+ savedTokens: Number.isFinite(entry.savedTokens) ? entry.savedTokens : null,
1930
+ }),
1931
+ };
1932
+ }
1933
+
1934
+ function compactInlineSummary(summary, { maxSegments = INLINE_SUMMARY_MAX_SEGMENTS } = {}) {
1935
+ const lines = unique(
1936
+ String(summary || '')
1937
+ .split(/\r?\n/)
1938
+ .map((line) => line.trim())
1939
+ .filter(Boolean),
1940
+ );
1941
+ if (lines.length === 0) {
1942
+ return '';
1943
+ }
1944
+
1945
+ if (lines.length <= maxSegments) {
1946
+ return lines.join(' | ');
1947
+ }
1948
+
1949
+ const firstLine = lines[0];
1950
+ const middleLines = lines.slice(1, -1);
1951
+ const selectedMiddle = [];
1952
+
1953
+ for (const line of middleLines) {
1954
+ if (selectedMiddle.length >= Math.max(0, maxSegments - 2)) {
1955
+ break;
1956
+ }
1957
+ if (!INLINE_SUMMARY_SIGNAL_PATTERNS.some((pattern) => pattern.test(line))) {
1958
+ continue;
1959
+ }
1960
+ if (!selectedMiddle.includes(line)) {
1961
+ selectedMiddle.push(line);
1962
+ }
1963
+ }
1964
+
1965
+ for (const line of middleLines) {
1966
+ if (selectedMiddle.length >= Math.max(0, maxSegments - 2)) {
1967
+ break;
1968
+ }
1969
+ if (!selectedMiddle.includes(line)) {
1970
+ selectedMiddle.push(line);
1971
+ }
1972
+ }
1973
+
1974
+ const tailLine = selectInlineSummaryTail(lines);
1975
+ const selected = unique([firstLine, ...selectedMiddle, tailLine]).slice(0, maxSegments);
1976
+ const remaining = Math.max(0, lines.length - selected.length);
1977
+ return remaining > 0
1978
+ ? `${selected.join(' | ')} | +${remaining} more`
1979
+ : selected.join(' | ');
1980
+ }
1981
+
1982
+ function selectInlineSummaryTail(lines = []) {
1983
+ for (const pattern of INLINE_SUMMARY_TAIL_PATTERNS) {
1984
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
1985
+ if (pattern.test(lines[index])) {
1986
+ return lines[index];
1987
+ }
1988
+ }
1989
+ }
1990
+
1991
+ return lines[lines.length - 1] ?? '';
1992
+ }
1993
+
1994
+ await main();