@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,1013 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import {
5
+ buildCompactMachineKey,
6
+ buildRuntimePaths,
7
+ compactContextBlock,
8
+ compressLine,
9
+ estimateTokenCount,
10
+ readJson,
11
+ } from './token-utils.mjs';
12
+
13
+ const DEFAULT_MAX_PROMPT_ENTRIES = 12;
14
+ const DEFAULT_MAX_OUTPUT_ENTRIES = 12;
15
+ const DEFAULT_HISTORY_LAYER_COUNT = 2;
16
+ const SOFT_RELEASE_RATIO = 0.9;
17
+ const HARD_RELEASE_RATIO = 0.92;
18
+ const PHASE_COOLDOWN_MS = {
19
+ soft: 5 * 60 * 1000,
20
+ hard: 10 * 60 * 1000,
21
+ };
22
+ const STOPWORDS = new Set([
23
+ 'the', 'a', 'an', 'and', 'or', 'to', 'for', 'of', 'with', 'in', 'on', 'is', 'are',
24
+ 'this', 'that', 'it', 'as', 'by', 'be', 'use', 'using', 'implement', 'fix', 'task',
25
+ 'keep', 'please', 'remember', 'need', 'still',
26
+ 'cần', 'và', 'là', 'cho', 'một', 'những', 'dùng',
27
+ ]);
28
+ const EXPENDABLE_LINE_PATTERNS = [
29
+ /^-\s*debug\b/i,
30
+ /^-\s*progress\b/i,
31
+ /^-\s*download(?:ed|ing)?\b/i,
32
+ /^-\s*spinner\b/i,
33
+ /^-\s*duration\b/i,
34
+ /^-\s*start at\b/i,
35
+ /^-\s*trace\b/i,
36
+ ];
37
+
38
+ function finiteNumber(value, fallback = 0) {
39
+ const number = Number(value);
40
+ return Number.isFinite(number) ? number : fallback;
41
+ }
42
+
43
+ function uniqueStrings(values) {
44
+ const seen = new Set();
45
+ const unique = [];
46
+ for (const value of values ?? []) {
47
+ const normalized = String(value ?? '').trim();
48
+ if (!normalized) continue;
49
+ const key = normalized.toLowerCase();
50
+ if (seen.has(key)) continue;
51
+ seen.add(key);
52
+ unique.push(normalized);
53
+ }
54
+ return unique;
55
+ }
56
+
57
+ function normalizeWhitespace(value) {
58
+ return String(value ?? '')
59
+ .replace(/[^\p{L}\p{N}\s./:_-]/gu, ' ')
60
+ .replace(/\s+/g, ' ')
61
+ .trim();
62
+ }
63
+
64
+ function tokenizeFreeform(value) {
65
+ return normalizeWhitespace(value)
66
+ .split(/\s+/)
67
+ .filter((token) => token && token.length > 1);
68
+ }
69
+
70
+ function compressFreeformText(value, { maxWords = 18 } = {}) {
71
+ const words = tokenizeFreeform(value);
72
+ const selected = [];
73
+ const seen = new Set();
74
+
75
+ for (const word of words) {
76
+ const key = word.toLowerCase();
77
+ const keep = /[./:_-]/.test(word) || !STOPWORDS.has(key);
78
+ if (!keep || seen.has(key)) {
79
+ continue;
80
+ }
81
+ selected.push(word);
82
+ seen.add(key);
83
+ if (selected.length >= maxWords) {
84
+ break;
85
+ }
86
+ }
87
+
88
+ return selected.join(' ');
89
+ }
90
+
91
+ function normalizeTaskMode(taskMode) {
92
+ const normalized = String(taskMode ?? '').trim().toLowerCase();
93
+ return ['coding', 'chat', 'mixed'].includes(normalized) ? normalized : '';
94
+ }
95
+
96
+ function normalizePhase(phase) {
97
+ const normalized = String(phase ?? '').trim().toLowerCase();
98
+ return ['monitor', 'soft', 'hard'].includes(normalized) ? normalized : 'monitor';
99
+ }
100
+
101
+ function normalizeStats(rawStats = {}) {
102
+ return {
103
+ thresholdHits: finiteNumber(rawStats?.thresholdHits, 0),
104
+ softHits: finiteNumber(rawStats?.softHits, 0),
105
+ hardHits: finiteNumber(rawStats?.hardHits, 0),
106
+ };
107
+ }
108
+
109
+ function normalizeLatestPlan(latestPlan = null) {
110
+ if (!latestPlan || typeof latestPlan !== 'object') {
111
+ return null;
112
+ }
113
+
114
+ return {
115
+ ...latestPlan,
116
+ phase: normalizePhase(latestPlan.phase),
117
+ updatedAt: finiteNumber(latestPlan.updatedAt, Date.now()),
118
+ estimatedTotalTokens: finiteNumber(latestPlan.estimatedTotalTokens, 0),
119
+ tokensBefore: finiteNumber(latestPlan.tokensBefore, 0),
120
+ tokensAfter: finiteNumber(latestPlan.tokensAfter, 0),
121
+ savedTokens: finiteNumber(latestPlan.savedTokens, 0),
122
+ reusedCooldown: Boolean(latestPlan.reusedCooldown),
123
+ };
124
+ }
125
+
126
+ function summarizePromptText(promptText, routingContext = {}) {
127
+ const focus = compressFreeformText(promptText || routingContext?.lastExplicitUserPromptText || '', {
128
+ maxWords: 18,
129
+ });
130
+ const parts = [];
131
+ if (routingContext?.targetFile) {
132
+ parts.push(String(routingContext.targetFile).trim());
133
+ }
134
+ if (focus) {
135
+ parts.push(focus);
136
+ }
137
+ if (routingContext?.taskType) {
138
+ parts.unshift(`lane=${String(routingContext.taskType).trim()}`);
139
+ }
140
+ return parts.join(' | ');
141
+ }
142
+
143
+ function summarizeOutputText(summary, command) {
144
+ const summaryLines = String(summary ?? '')
145
+ .split(/\r?\n/)
146
+ .map((line) => compressLine(line))
147
+ .filter(Boolean)
148
+ .filter((line) => !EXPENDABLE_LINE_PATTERNS.some((pattern) => pattern.test(line)));
149
+ const lines = uniqueStrings([
150
+ command ? `command=${String(command).trim()}` : null,
151
+ ...summaryLines.slice(0, 3),
152
+ ]);
153
+ return lines.join(' | ');
154
+ }
155
+
156
+ function normalizePromptEntry(entry) {
157
+ if (!entry || typeof entry !== 'object') {
158
+ return null;
159
+ }
160
+
161
+ const summary = String(entry.summary ?? '').trim();
162
+ if (!summary) {
163
+ return null;
164
+ }
165
+
166
+ return {
167
+ kind: 'prompt',
168
+ timestamp: finiteNumber(entry.timestamp, Date.now()),
169
+ tokens: Math.max(1, finiteNumber(entry.tokens, estimateTokenCount(summary))),
170
+ summary,
171
+ targetFile: entry.targetFile ? String(entry.targetFile).trim() : null,
172
+ taskType: entry.taskType ? String(entry.taskType).trim() : null,
173
+ };
174
+ }
175
+
176
+ function normalizeOutputEntry(entry) {
177
+ if (!entry || typeof entry !== 'object') {
178
+ return null;
179
+ }
180
+
181
+ const summary = String(entry.summary ?? '').trim();
182
+ if (!summary) {
183
+ return null;
184
+ }
185
+
186
+ const tokensBefore = Math.max(1, finiteNumber(entry.tokensBefore, estimateTokenCount(summary)));
187
+ const tokensAfter = Math.max(1, finiteNumber(entry.tokensAfter, estimateTokenCount(summary)));
188
+
189
+ return {
190
+ kind: 'output',
191
+ timestamp: finiteNumber(entry.timestamp, Date.now()),
192
+ command: entry.command ? String(entry.command).trim() : '',
193
+ profile: entry.profile ? String(entry.profile).trim() : 'generic',
194
+ exitCode: Number.isFinite(Number(entry.exitCode)) ? Number(entry.exitCode) : null,
195
+ tokensBefore,
196
+ tokensAfter,
197
+ savedTokens: Math.max(0, finiteNumber(entry.savedTokens, tokensBefore - tokensAfter)),
198
+ summary,
199
+ };
200
+ }
201
+
202
+ function upsertEntry(entries, entry, { maxEntries = 10, keyBuilder } = {}) {
203
+ const key = typeof keyBuilder === 'function' ? keyBuilder(entry) : entry.summary;
204
+ const normalizedKey = String(key ?? '').trim().toLowerCase();
205
+ const remaining = (entries ?? []).filter((candidate) => {
206
+ const candidateKey = String(
207
+ typeof keyBuilder === 'function' ? keyBuilder(candidate) : candidate?.summary,
208
+ ).trim().toLowerCase();
209
+ return candidateKey !== normalizedKey;
210
+ });
211
+
212
+ return [entry, ...remaining].slice(0, maxEntries);
213
+ }
214
+
215
+ function deriveTaskMode({ routingContext = {}, recentOutputs = [], fallback = '' } = {}) {
216
+ const explicit = normalizeTaskMode(fallback);
217
+ const targetFile = String(routingContext?.targetFile ?? '').trim();
218
+ const promptText = String(routingContext?.lastExplicitUserPromptText ?? routingContext?.promptText ?? '').trim();
219
+ if (targetFile || /(src|tests?|docs|app|lib)\//i.test(promptText) || /\b(test|debug|fix|implement|refactor|build)\b/i.test(promptText)) {
220
+ return 'coding';
221
+ }
222
+
223
+ if ((recentOutputs ?? []).some((entry) => /(?:test-runner|search|paths|git|package-install|docker-logs)/.test(String(entry.profile ?? '')))) {
224
+ return 'coding';
225
+ }
226
+
227
+ if (promptText) {
228
+ return 'mixed';
229
+ }
230
+
231
+ if (explicit) {
232
+ return explicit;
233
+ }
234
+
235
+ return 'chat';
236
+ }
237
+
238
+ function summarizeHistoryLayer(items, { label, maxItems = 2 } = {}) {
239
+ const normalized = uniqueStrings((items ?? []).map((item) => String(item ?? '').trim()));
240
+ if (normalized.length === 0) {
241
+ return null;
242
+ }
243
+
244
+ const shown = normalized.slice(0, maxItems);
245
+ const remaining = normalized.length - shown.length;
246
+ return `- ${label}: ${shown.join(' | ')}${remaining > 0 ? ` | +${remaining} more` : ''}`;
247
+ }
248
+
249
+ function firstImportantOutputSignal(summary) {
250
+ const lines = String(summary ?? '')
251
+ .split(/\r?\n/)
252
+ .map((line) => compressLine(line))
253
+ .filter(Boolean)
254
+ .filter((line) => !/^-\s*recent command:/i.test(line));
255
+ return lines[0] ?? compressLine(summary);
256
+ }
257
+
258
+ function compactBridgeValue(value, { maxWords = 8 } = {}) {
259
+ const normalized = normalizeWhitespace(
260
+ String(value ?? '')
261
+ .replace(/\(\+\d+\s+more\)/gi, ' ')
262
+ .split(/\s*[;|]\s*/)[0] ?? '',
263
+ );
264
+ if (!normalized) {
265
+ return '';
266
+ }
267
+
268
+ const words = normalized.split(/\s+/).filter(Boolean);
269
+ return words.slice(0, Math.max(1, maxWords)).join(' ');
270
+ }
271
+
272
+ function parsePreviousContextBridge(previousLine) {
273
+ const summary = {};
274
+ const segments = String(previousLine ?? '')
275
+ .split(/\s+\|\s+(?=\[[^\]]+\])/)
276
+ .map((segment) => segment.trim())
277
+ .filter(Boolean);
278
+
279
+ for (const segment of segments) {
280
+ const clauses = segment
281
+ .split(/\s+—\s+/)
282
+ .map((clause) => clause.trim())
283
+ .filter(Boolean);
284
+ if (clauses.length === 0) {
285
+ continue;
286
+ }
287
+
288
+ const header = clauses.shift() ?? '';
289
+ const projectMatch = header.match(/^\[project\]\s*(.+)$/i);
290
+ const sessionMatch = header.match(/^\[session\]\s*(.+)$/i);
291
+ if (projectMatch && !summary.project) {
292
+ summary.project = compactBridgeValue(projectMatch[1], { maxWords: 6 });
293
+ }
294
+ if (sessionMatch && !summary.task) {
295
+ summary.task = compactBridgeValue(sessionMatch[1], { maxWords: 6 });
296
+ }
297
+
298
+ for (const clause of clauses) {
299
+ const separatorIndex = clause.indexOf(':');
300
+ if (separatorIndex <= 0) {
301
+ continue;
302
+ }
303
+
304
+ const rawKey = clause.slice(0, separatorIndex).trim().toLowerCase();
305
+ const value = compactBridgeValue(clause.slice(separatorIndex + 1), {
306
+ maxWords: rawKey === 'rules' ? 6 : 8,
307
+ });
308
+ if (!value) {
309
+ continue;
310
+ }
311
+
312
+ if (rawKey.startsWith('decision') && !summary.decision) {
313
+ summary.decision = value;
314
+ } else if ((rawKey === 'rule' || rawKey === 'rules') && !summary.rule) {
315
+ summary.rule = value;
316
+ } else if (rawKey === 'next' && !summary.next) {
317
+ summary.next = value;
318
+ } else if ((rawKey === 'action' || rawKey === 'actions') && !summary.action) {
319
+ summary.action = value;
320
+ } else if (rawKey === 'outcome' && !summary.outcome) {
321
+ summary.outcome = value;
322
+ }
323
+ }
324
+ }
325
+
326
+ return summary;
327
+ }
328
+
329
+ function parseRouteBridge(routeLine) {
330
+ const summary = {};
331
+ const segments = String(routeLine ?? '')
332
+ .split(/\s+\|\s+/)
333
+ .map((segment) => segment.trim())
334
+ .filter(Boolean);
335
+
336
+ for (const segment of segments) {
337
+ const separatorIndex = segment.indexOf('=');
338
+ if (separatorIndex <= 0) {
339
+ continue;
340
+ }
341
+
342
+ const rawKey = segment.slice(0, separatorIndex).trim().toLowerCase();
343
+ const value = compactBridgeValue(segment.slice(separatorIndex + 1), {
344
+ maxWords: rawKey === 'targets' ? 4 : 8,
345
+ });
346
+ if (!value) {
347
+ continue;
348
+ }
349
+
350
+ if (rawKey === 'task' && !summary.task) {
351
+ summary.task = value;
352
+ } else if ((rawKey === 'target' || rawKey === 'targets') && !summary.focus) {
353
+ summary.focus = value;
354
+ } else if (rawKey === 'next' && !summary.next) {
355
+ summary.next = value;
356
+ }
357
+ }
358
+
359
+ return summary;
360
+ }
361
+
362
+ function buildMemoryBridgeLine(routeState = {}, state = {}) {
363
+ const previousLine = String(routeState?.previousContext?.line ?? '').trim();
364
+ const routeLine = String(routeState?.routeSummary?.line ?? '').trim();
365
+ const promptSummary = String(state?.recentPrompts?.[0]?.summary ?? '').trim();
366
+ const previousBridge = parsePreviousContextBridge(previousLine);
367
+ const routeBridge = parseRouteBridge(routeLine);
368
+ const promptBridge = compactBridgeValue(promptSummary, { maxWords: 10 });
369
+ const parts = [];
370
+ const seenParts = new Set();
371
+ const trivialRouteTasks = new Set(['trivial', 'non-trivial', 'complex', 'mixed', 'chat', 'coding']);
372
+
373
+ function pushBridgePart(key, value) {
374
+ const normalizedValue = compactBridgeValue(value, {
375
+ maxWords: key === 'task' ? 6 : key === 'focus' ? 4 : 8,
376
+ });
377
+ if (!normalizedValue) {
378
+ return;
379
+ }
380
+
381
+ if (key === 'task' && trivialRouteTasks.has(normalizedValue.toLowerCase())) {
382
+ return;
383
+ }
384
+
385
+ const dedupeKey = `${key}:${normalizedValue}`.toLowerCase();
386
+ if (seenParts.has(dedupeKey)) {
387
+ return;
388
+ }
389
+ seenParts.add(dedupeKey);
390
+ parts.push(`${key}=${normalizedValue}`);
391
+ }
392
+
393
+ pushBridgePart('task', previousBridge.task || routeBridge.task || promptBridge);
394
+ pushBridgePart('decision', previousBridge.decision);
395
+ pushBridgePart('rule', previousBridge.rule);
396
+ pushBridgePart('next', previousBridge.next || routeBridge.next);
397
+ pushBridgePart('focus', routeBridge.focus);
398
+ if (parts.length === 0) {
399
+ pushBridgePart('project', previousBridge.project);
400
+ pushBridgePart('action', previousBridge.action);
401
+ pushBridgePart('outcome', previousBridge.outcome);
402
+ }
403
+
404
+ if (parts.length === 0 && !routeLine && !promptSummary) {
405
+ return null;
406
+ }
407
+
408
+ if (parts.length > 0) {
409
+ return `- Memory bridge: ${parts.join(' | ')}`;
410
+ }
411
+
412
+ return `- Memory bridge: ${uniqueStrings([routeLine, promptSummary]).join(' | ')}`;
413
+ }
414
+
415
+ function computeEstimatedContextTokens(state) {
416
+ return finiteNumber(state?.estimatedContextTokens, 0);
417
+ }
418
+
419
+ function computeEstimatedTotalTokens({
420
+ recentPrompts = [],
421
+ recentOutputs = [],
422
+ estimatedContextTokens = 0,
423
+ baselineTokens = 0,
424
+ }) {
425
+ const promptTokens = recentPrompts.reduce((sum, entry) => sum + finiteNumber(entry.tokens, 0), 0);
426
+ const outputTokens = recentOutputs.reduce((sum, entry) => sum + finiteNumber(entry.tokensBefore ?? entry.tokens, 0), 0);
427
+ return baselineTokens + estimatedContextTokens + promptTokens + outputTokens;
428
+ }
429
+
430
+ export function buildCompactThresholds(config = {}) {
431
+ const softThreshold = Math.max(1, finiteNumber(config?.compact?.tokenThreshold, 100_000));
432
+ const hardThreshold = Math.max(softThreshold + 1, Math.round(softThreshold * 1.2));
433
+ const baselineTokens = Math.max(120, Math.min(18_000, Math.round(softThreshold * 0.18)));
434
+
435
+ return {
436
+ softThreshold,
437
+ hardThreshold,
438
+ baselineTokens,
439
+ };
440
+ }
441
+
442
+ function buildReleaseThresholds(thresholds = buildCompactThresholds()) {
443
+ const softReleaseThreshold = Math.max(
444
+ Math.min(thresholds.softThreshold, Math.round(thresholds.softThreshold * SOFT_RELEASE_RATIO)),
445
+ 1,
446
+ );
447
+ const hardReleaseThreshold = Math.max(
448
+ thresholds.softThreshold,
449
+ Math.round(thresholds.hardThreshold * HARD_RELEASE_RATIO),
450
+ );
451
+
452
+ return {
453
+ softReleaseThreshold,
454
+ hardReleaseThreshold,
455
+ };
456
+ }
457
+
458
+ export function resolveCompactPhase(
459
+ estimatedTotalTokens,
460
+ thresholds = buildCompactThresholds(),
461
+ previousState = {},
462
+ ) {
463
+ const previousPhase = normalizePhase(previousState?.phase ?? previousState);
464
+ const releaseThresholds = buildReleaseThresholds(thresholds);
465
+ if (estimatedTotalTokens >= thresholds.hardThreshold) {
466
+ return 'hard';
467
+ }
468
+ if (previousPhase === 'hard' && estimatedTotalTokens >= releaseThresholds.hardReleaseThreshold) {
469
+ return 'hard';
470
+ }
471
+ if (estimatedTotalTokens >= thresholds.softThreshold) {
472
+ return 'soft';
473
+ }
474
+ if (previousPhase !== 'monitor' && estimatedTotalTokens >= releaseThresholds.softReleaseThreshold) {
475
+ return 'soft';
476
+ }
477
+ return 'monitor';
478
+ }
479
+
480
+ function resolvePhaseCooldownMs(phase) {
481
+ return PHASE_COOLDOWN_MS[normalizePhase(phase)] ?? 0;
482
+ }
483
+
484
+ function resolvePlanDeltaThreshold(phase, thresholds = buildCompactThresholds()) {
485
+ return normalizePhase(phase) === 'hard'
486
+ ? Math.max(60, Math.round(thresholds.hardThreshold * 0.14))
487
+ : Math.max(40, Math.round(thresholds.softThreshold * 0.12));
488
+ }
489
+
490
+ function isMaterialThresholdPlanChange(previousPlan, nextPlan, thresholds) {
491
+ if (!previousPlan || !nextPlan) {
492
+ return true;
493
+ }
494
+
495
+ if (normalizePhase(previousPlan.phase) !== normalizePhase(nextPlan.phase)) {
496
+ return true;
497
+ }
498
+
499
+ const estimatedDelta = Math.abs(
500
+ finiteNumber(nextPlan.estimatedTotalTokens, 0) - finiteNumber(previousPlan.estimatedTotalTokens, 0),
501
+ );
502
+ if (estimatedDelta >= resolvePlanDeltaThreshold(nextPlan.phase, thresholds)) {
503
+ return true;
504
+ }
505
+
506
+ const savedDelta = Math.abs(
507
+ finiteNumber(nextPlan.savedTokens, 0) - finiteNumber(previousPlan.savedTokens, 0),
508
+ );
509
+ if (savedDelta >= 40) {
510
+ return true;
511
+ }
512
+
513
+ const previousLines = Array.isArray(previousPlan.lines)
514
+ ? previousPlan.lines
515
+ : String(previousPlan.summary ?? '').split(/\r?\n/).filter(Boolean);
516
+ const nextLines = Array.isArray(nextPlan.lines)
517
+ ? nextPlan.lines
518
+ : String(nextPlan.summary ?? '').split(/\r?\n/).filter(Boolean);
519
+
520
+ if (previousLines.length === 0 || nextLines.length === 0) {
521
+ return false;
522
+ }
523
+
524
+ return Math.abs(nextLines.length - previousLines.length) >= 2;
525
+ }
526
+
527
+ export function resolveThresholdCompactBudget({
528
+ phase = 'monitor',
529
+ maxTokens = 280,
530
+ maxLines = 12,
531
+ } = {}) {
532
+ const normalizedPhase = normalizePhase(phase);
533
+ if (normalizedPhase === 'hard') {
534
+ return {
535
+ maxTokens: Math.max(170, Math.round(maxTokens * 0.68)),
536
+ maxLines: Math.min(maxLines, 8),
537
+ };
538
+ }
539
+
540
+ if (normalizedPhase === 'soft') {
541
+ return {
542
+ maxTokens: Math.max(190, Math.round(maxTokens * 0.84)),
543
+ maxLines: Math.min(maxLines, 10),
544
+ };
545
+ }
546
+
547
+ return {
548
+ maxTokens,
549
+ maxLines,
550
+ };
551
+ }
552
+
553
+ export function buildCompactPressureState(rawState = null, config = {}) {
554
+ const thresholds = buildCompactThresholds(config);
555
+ const rawSoftThreshold = finiteNumber(rawState?.softThreshold, 0);
556
+ const rawHardThreshold = finiteNumber(rawState?.hardThreshold, 0);
557
+ if (!finiteNumber(config?.compact?.tokenThreshold, 0) && rawSoftThreshold > 0) {
558
+ thresholds.softThreshold = rawSoftThreshold;
559
+ thresholds.hardThreshold = rawHardThreshold > rawSoftThreshold
560
+ ? rawHardThreshold
561
+ : Math.max(rawSoftThreshold + 1, Math.round(rawSoftThreshold * 1.2));
562
+ thresholds.baselineTokens = Math.max(120, Math.min(18_000, Math.round(thresholds.softThreshold * 0.18)));
563
+ }
564
+ const recentPrompts = (Array.isArray(rawState?.recentPrompts) ? rawState.recentPrompts : [])
565
+ .map((entry) => normalizePromptEntry(entry))
566
+ .filter(Boolean)
567
+ .sort((left, right) => (right.timestamp ?? 0) - (left.timestamp ?? 0))
568
+ .slice(0, DEFAULT_MAX_PROMPT_ENTRIES);
569
+ const recentOutputs = (Array.isArray(rawState?.recentOutputs) ? rawState.recentOutputs : [])
570
+ .map((entry) => normalizeOutputEntry(entry))
571
+ .filter(Boolean)
572
+ .sort((left, right) => (right.timestamp ?? 0) - (left.timestamp ?? 0))
573
+ .slice(0, DEFAULT_MAX_OUTPUT_ENTRIES);
574
+ const estimatedContextTokens = computeEstimatedContextTokens(rawState);
575
+ const estimatedTotalTokens = finiteNumber(
576
+ rawState?.estimatedTotalTokens,
577
+ computeEstimatedTotalTokens({
578
+ recentPrompts,
579
+ recentOutputs,
580
+ estimatedContextTokens,
581
+ baselineTokens: thresholds.baselineTokens,
582
+ }),
583
+ );
584
+ const releaseThresholds = buildReleaseThresholds(thresholds);
585
+ const taskMode = deriveTaskMode({
586
+ routingContext: rawState?.routingContext ?? {},
587
+ recentOutputs,
588
+ fallback: rawState?.taskMode,
589
+ });
590
+
591
+ return {
592
+ updatedAt: finiteNumber(rawState?.updatedAt, Date.now()),
593
+ softThreshold: thresholds.softThreshold,
594
+ hardThreshold: thresholds.hardThreshold,
595
+ softReleaseThreshold: releaseThresholds.softReleaseThreshold,
596
+ hardReleaseThreshold: releaseThresholds.hardReleaseThreshold,
597
+ baselineTokens: thresholds.baselineTokens,
598
+ estimatedContextTokens,
599
+ estimatedTotalTokens,
600
+ phase: resolveCompactPhase(estimatedTotalTokens, thresholds, rawState),
601
+ taskMode,
602
+ cooldownUntil: finiteNumber(rawState?.cooldownUntil, 0),
603
+ routingContext: rawState?.routingContext && typeof rawState.routingContext === 'object'
604
+ ? rawState.routingContext
605
+ : {},
606
+ recentPrompts,
607
+ recentOutputs,
608
+ latestPlan: normalizeLatestPlan(rawState?.latestPlan),
609
+ historyLayers: uniqueStrings(rawState?.historyLayers ?? []).slice(0, DEFAULT_HISTORY_LAYER_COUNT),
610
+ stats: normalizeStats(rawState?.stats),
611
+ };
612
+ }
613
+
614
+ export function registerPromptPressure(state, {
615
+ promptText = '',
616
+ routingContext = {},
617
+ previousContext = null,
618
+ routeSummary = null,
619
+ timestamp = Date.now(),
620
+ } = {}, config = {}) {
621
+ const current = buildCompactPressureState(state, config);
622
+ const normalizedPrompt = String(promptText || routingContext?.lastExplicitUserPromptText || '').trim();
623
+ if (!normalizedPrompt) {
624
+ return current;
625
+ }
626
+
627
+ const entry = normalizePromptEntry({
628
+ timestamp,
629
+ tokens: estimateTokenCount(normalizedPrompt),
630
+ summary: summarizePromptText(normalizedPrompt, routingContext),
631
+ targetFile: routingContext?.targetFile ?? null,
632
+ taskType: routingContext?.taskType ?? null,
633
+ });
634
+ const recentPrompts = entry
635
+ ? upsertEntry(current.recentPrompts, entry, {
636
+ maxEntries: DEFAULT_MAX_PROMPT_ENTRIES,
637
+ keyBuilder: (candidate) => `${candidate.targetFile ?? ''}|${candidate.summary ?? ''}`,
638
+ })
639
+ : current.recentPrompts;
640
+
641
+ return buildCompactPressureState({
642
+ ...current,
643
+ updatedAt: timestamp,
644
+ routingContext,
645
+ recentPrompts,
646
+ estimatedTotalTokens: undefined,
647
+ estimatedContextTokens: estimateTokenCount([
648
+ routeSummary?.line ?? '',
649
+ previousContext?.line ?? '',
650
+ summarizePromptText(normalizedPrompt, routingContext),
651
+ ].filter(Boolean).join('\n')),
652
+ taskMode: deriveTaskMode({
653
+ routingContext,
654
+ recentOutputs: current.recentOutputs,
655
+ fallback: current.taskMode,
656
+ }),
657
+ }, config);
658
+ }
659
+
660
+ export function registerOutputPressure(state, {
661
+ command = '',
662
+ profile = 'generic',
663
+ summary = '',
664
+ tokensBefore = 0,
665
+ tokensAfter = 0,
666
+ savedTokens = 0,
667
+ exitCode = null,
668
+ timestamp = Date.now(),
669
+ } = {}, config = {}) {
670
+ const current = buildCompactPressureState(state, config);
671
+ const normalizedSummary = summarizeOutputText(summary, command);
672
+ if (!normalizedSummary) {
673
+ return current;
674
+ }
675
+
676
+ const entry = normalizeOutputEntry({
677
+ timestamp,
678
+ command,
679
+ profile,
680
+ exitCode,
681
+ tokensBefore: Math.max(1, finiteNumber(tokensBefore, estimateTokenCount(summary || normalizedSummary))),
682
+ tokensAfter: Math.max(1, finiteNumber(tokensAfter, estimateTokenCount(normalizedSummary))),
683
+ savedTokens: Math.max(0, finiteNumber(savedTokens, 0)),
684
+ summary: normalizedSummary,
685
+ });
686
+ const recentOutputs = upsertEntry(current.recentOutputs, entry, {
687
+ maxEntries: DEFAULT_MAX_OUTPUT_ENTRIES,
688
+ keyBuilder: (candidate) => `${candidate.command ?? ''}|${candidate.summary ?? ''}`,
689
+ });
690
+
691
+ return buildCompactPressureState({
692
+ ...current,
693
+ updatedAt: timestamp,
694
+ recentOutputs,
695
+ estimatedTotalTokens: undefined,
696
+ taskMode: deriveTaskMode({
697
+ routingContext: current.routingContext ?? {},
698
+ recentOutputs,
699
+ fallback: current.taskMode || (profile ? 'coding' : ''),
700
+ }),
701
+ }, config);
702
+ }
703
+
704
+ export function registerThresholdCompactPlan(state, plan, config = {}) {
705
+ const current = buildCompactPressureState(state, config);
706
+ if (!plan?.active) {
707
+ return current;
708
+ }
709
+
710
+ const thresholds = {
711
+ softThreshold: current.softThreshold,
712
+ hardThreshold: current.hardThreshold,
713
+ };
714
+ const now = Date.now();
715
+ const fingerprint = buildCompactMachineKey('threshold-plan-v1', {
716
+ phase: plan.phase,
717
+ estimatedTotalTokens: plan.estimatedTotalTokens,
718
+ lines: plan.lines,
719
+ });
720
+ if (current.latestPlan?.fingerprint === fingerprint) {
721
+ return buildCompactPressureState({
722
+ ...current,
723
+ updatedAt: now,
724
+ latestPlan: {
725
+ ...current.latestPlan,
726
+ updatedAt: now,
727
+ },
728
+ }, config);
729
+ }
730
+
731
+ const withinCooldown = (
732
+ current.latestPlan
733
+ && normalizePhase(current.latestPlan.phase) === normalizePhase(plan.phase)
734
+ && finiteNumber(current.cooldownUntil, 0) > now
735
+ );
736
+ const materialChange = isMaterialThresholdPlanChange(current.latestPlan, plan, thresholds);
737
+
738
+ if (withinCooldown && !materialChange) {
739
+ return buildCompactPressureState({
740
+ ...current,
741
+ updatedAt: now,
742
+ latestPlan: {
743
+ ...current.latestPlan,
744
+ fingerprint,
745
+ phase: normalizePhase(plan.phase),
746
+ estimatedTotalTokens: finiteNumber(plan.estimatedTotalTokens, 0),
747
+ tokensBefore: finiteNumber(plan.tokensBefore, 0),
748
+ tokensAfter: finiteNumber(plan.tokensAfter, 0),
749
+ savedTokens: finiteNumber(plan.savedTokens, 0),
750
+ summary: Array.isArray(plan.lines) ? plan.lines.join('\n') : current.latestPlan?.summary ?? '',
751
+ updatedAt: now,
752
+ reusedCooldown: true,
753
+ },
754
+ }, config);
755
+ }
756
+
757
+ const stats = normalizeStats(current.stats);
758
+ stats.thresholdHits += 1;
759
+ if (plan.phase === 'hard') {
760
+ stats.hardHits += 1;
761
+ } else if (plan.phase === 'soft') {
762
+ stats.softHits += 1;
763
+ }
764
+
765
+ return buildCompactPressureState({
766
+ ...current,
767
+ updatedAt: now,
768
+ cooldownUntil: now + resolvePhaseCooldownMs(plan.phase),
769
+ historyLayers: uniqueStrings(plan.layers ?? []).slice(0, DEFAULT_HISTORY_LAYER_COUNT),
770
+ latestPlan: {
771
+ fingerprint,
772
+ updatedAt: now,
773
+ phase: plan.phase,
774
+ estimatedTotalTokens: plan.estimatedTotalTokens,
775
+ tokensBefore: plan.tokensBefore,
776
+ tokensAfter: plan.tokensAfter,
777
+ savedTokens: plan.savedTokens,
778
+ summary: Array.isArray(plan.lines) ? plan.lines.join('\n') : '',
779
+ reusedCooldown: false,
780
+ },
781
+ stats,
782
+ }, config);
783
+ }
784
+
785
+ export function buildThresholdCompactPlan({
786
+ state,
787
+ config = {},
788
+ routeState = {},
789
+ staticLines = [],
790
+ dynamicLines = [],
791
+ maxTokens = 280,
792
+ } = {}) {
793
+ const normalizedState = buildCompactPressureState(state, config);
794
+ const thresholds = buildCompactThresholds(config);
795
+ const phase = normalizedState.phase;
796
+ if (phase === 'monitor') {
797
+ return {
798
+ active: false,
799
+ phase,
800
+ estimatedTotalTokens: normalizedState.estimatedTotalTokens,
801
+ lines: [],
802
+ layers: normalizedState.historyLayers,
803
+ tokensBefore: 0,
804
+ tokensAfter: 0,
805
+ savedTokens: 0,
806
+ };
807
+ }
808
+
809
+ const budget = resolveThresholdCompactBudget({
810
+ phase,
811
+ maxTokens,
812
+ maxLines: 12,
813
+ });
814
+ const laneLine = (staticLines ?? []).find((line) => String(line ?? '').startsWith('- Current lane:'))
815
+ ?? (routeState?.routingContext?.taskType
816
+ ? `- Current lane: ${routeState.routingContext.taskType}`
817
+ : null);
818
+ const worklogLine = phase === 'hard'
819
+ ? null
820
+ : ((staticLines ?? []).find((line) => String(line ?? '').startsWith('- WORKLOG:')) ?? null);
821
+ const routeLine = (dynamicLines ?? []).find((line) => String(line ?? '').startsWith('- Recent routed lane:'))
822
+ ?? (routeState?.routeSummary?.line ? `- Recent routed lane: ${routeState.routeSummary.line}` : null);
823
+ const previousLine = (dynamicLines ?? []).find((line) => String(line ?? '').startsWith('- Previous context:'))
824
+ ?? (routeState?.previousContext?.line ? `- Previous context: ${routeState.previousContext.line}` : null);
825
+ const outputLine = (dynamicLines ?? []).find((line) => String(line ?? '').startsWith('- Recent command output:'))
826
+ ?? (
827
+ normalizedState.recentOutputs[0]?.summary
828
+ ? `- Recent command output: ${firstImportantOutputSignal(normalizedState.recentOutputs[0]?.summary)}`
829
+ : null
830
+ );
831
+ const verificationLine = (dynamicLines ?? []).find((line) => /broad verification/i.test(String(line ?? ''))) ?? null;
832
+ const thresholdLine = `- Threshold compact: ${phase} (est ${normalizedState.estimatedTotalTokens} / soft ${thresholds.softThreshold} / hard ${thresholds.hardThreshold}). Compact duplicates/logs only; preserve critical context.`;
833
+ const taskModeLine = `- Task-aware mode: ${normalizedState.taskMode || 'mixed'} => preserve active task, rules, decisions, and current code focus.`;
834
+ const memoryBridgeLine = buildMemoryBridgeLine(routeState, normalizedState);
835
+ const recentFocusLine = summarizeHistoryLayer([
836
+ normalizedState.recentPrompts[0]?.summary,
837
+ firstImportantOutputSignal(normalizedState.recentOutputs[0]?.summary),
838
+ ], {
839
+ label: 'Recent focus',
840
+ maxItems: 2,
841
+ });
842
+ const layerOne = summarizeHistoryLayer([
843
+ ...normalizedState.recentPrompts.slice(1, 3).map((entry) => entry.summary),
844
+ ...normalizedState.recentOutputs.slice(1, 3).map((entry) => firstImportantOutputSignal(entry.summary)),
845
+ ], {
846
+ label: 'Layer 1 summary',
847
+ maxItems: 2,
848
+ });
849
+ const layerTwo = phase === 'hard'
850
+ ? summarizeHistoryLayer([
851
+ ...normalizedState.recentPrompts.slice(3).map((entry) => entry.summary),
852
+ ...normalizedState.recentOutputs.slice(3).map((entry) => firstImportantOutputSignal(entry.summary)),
853
+ ], {
854
+ label: 'Layer 2 summary',
855
+ maxItems: 2,
856
+ })
857
+ : null;
858
+ const askBeforeDropLine = Boolean(config?.compact?.askBeforeDrop)
859
+ ? '- Ask-before-drop: if older decisions, rules, or unresolved failures still matter, confirm with the user before removing them.'
860
+ : null;
861
+
862
+ const rawLines = uniqueStrings([
863
+ thresholdLine,
864
+ taskModeLine,
865
+ laneLine,
866
+ worklogLine,
867
+ routeLine,
868
+ previousLine,
869
+ outputLine,
870
+ memoryBridgeLine,
871
+ recentFocusLine,
872
+ layerOne,
873
+ layerTwo,
874
+ askBeforeDropLine,
875
+ verificationLine,
876
+ ...(dynamicLines ?? []).filter((line) => {
877
+ const normalized = compressLine(line);
878
+ if (!normalized) return false;
879
+ if (normalized === routeLine || normalized === previousLine || normalized === outputLine || normalized === verificationLine) {
880
+ return false;
881
+ }
882
+ return !EXPENDABLE_LINE_PATTERNS.some((pattern) => pattern.test(normalized));
883
+ }),
884
+ ]);
885
+
886
+ const compacted = compactContextBlock(rawLines, {
887
+ maxTokens: budget.maxTokens,
888
+ maxLines: budget.maxLines,
889
+ anchorLines: [
890
+ thresholdLine,
891
+ routeLine,
892
+ previousLine,
893
+ outputLine,
894
+ memoryBridgeLine,
895
+ askBeforeDropLine,
896
+ ].filter(Boolean),
897
+ maxAnchors: 6,
898
+ });
899
+
900
+ return {
901
+ active: true,
902
+ phase,
903
+ estimatedTotalTokens: normalizedState.estimatedTotalTokens,
904
+ taskMode: normalizedState.taskMode,
905
+ budgetTokens: budget.maxTokens,
906
+ budgetLines: budget.maxLines,
907
+ lines: compacted.text ? compacted.text.split(/\r?\n/) : [],
908
+ layers: [layerOne, layerTwo].filter(Boolean),
909
+ tokensBefore: compacted.tokensBefore,
910
+ tokensAfter: compacted.tokensAfter,
911
+ savedTokens: compacted.savedTokens,
912
+ };
913
+ }
914
+
915
+ export async function readCompactPressureState(projectRoot, config = {}) {
916
+ const runtimePaths = buildRuntimePaths(projectRoot);
917
+ return buildCompactPressureState(await readJson(runtimePaths.compactPressurePath, null), config);
918
+ }
919
+
920
+ export async function writeCompactPressureState(projectRoot, state, config = {}) {
921
+ const runtimePaths = buildRuntimePaths(projectRoot);
922
+ const normalized = buildCompactPressureState(state, config);
923
+ await fs.mkdir(path.dirname(runtimePaths.compactPressurePath), { recursive: true });
924
+ await fs.writeFile(runtimePaths.compactPressurePath, `${JSON.stringify(normalized, null, 2)}\n`, 'utf8');
925
+ return normalized;
926
+ }
927
+
928
+ export async function updateCompactPressureFromPrompt(projectRoot, payload, config = {}) {
929
+ const current = await readCompactPressureState(projectRoot, config);
930
+ const next = registerPromptPressure(current, payload, config);
931
+ return writeCompactPressureState(projectRoot, next, config);
932
+ }
933
+
934
+ export async function updateCompactPressureFromOutput(projectRoot, payload, config = {}) {
935
+ const current = await readCompactPressureState(projectRoot, config);
936
+ const next = registerOutputPressure(current, payload, config);
937
+ return writeCompactPressureState(projectRoot, next, config);
938
+ }
939
+
940
+ export async function writeThresholdCompactPlan(projectRoot, plan, config = {}) {
941
+ const current = await readCompactPressureState(projectRoot, config);
942
+ const next = registerThresholdCompactPlan(current, plan, config);
943
+ return writeCompactPressureState(projectRoot, next, config);
944
+ }
945
+
946
+ async function loadRuntimeConfig(projectRoot) {
947
+ const runtimePaths = buildRuntimePaths(projectRoot);
948
+ const raw = await readJson(runtimePaths.configPath, {});
949
+ return raw && typeof raw === 'object' ? raw : {};
950
+ }
951
+
952
+ async function readStdin() {
953
+ if (process.stdin.isTTY) {
954
+ return '';
955
+ }
956
+
957
+ const chunks = [];
958
+ for await (const chunk of process.stdin) {
959
+ chunks.push(typeof chunk === 'string' ? chunk : chunk.toString('utf8'));
960
+ }
961
+ return chunks.join('');
962
+ }
963
+
964
+ function extractPrompt(payload = {}) {
965
+ const candidates = [
966
+ payload.prompt,
967
+ payload.user_prompt,
968
+ payload.text,
969
+ payload.message,
970
+ payload.input,
971
+ ];
972
+ return candidates.find((value) => typeof value === 'string' && value.trim()) || '';
973
+ }
974
+
975
+ async function runCli() {
976
+ const mode = String(process.argv[2] ?? '').trim();
977
+ if (!mode) {
978
+ return;
979
+ }
980
+
981
+ const projectRoot = process.env.CLAUDE_PROJECT_DIR || process.env.PROJECT_ROOT || process.cwd();
982
+ const config = await loadRuntimeConfig(projectRoot);
983
+ const rawInput = await readStdin();
984
+ const payload = (() => {
985
+ try {
986
+ return JSON.parse(rawInput || '{}');
987
+ } catch {
988
+ return {};
989
+ }
990
+ })();
991
+
992
+ if (mode === 'prompt') {
993
+ const routeState = await readJson(path.join(projectRoot, '.claude', 'ukit', 'skill-router-state.json'), {});
994
+ const promptText = extractPrompt(payload);
995
+ if (!promptText.trim()) {
996
+ return;
997
+ }
998
+
999
+ await updateCompactPressureFromPrompt(projectRoot, {
1000
+ promptText,
1001
+ routingContext: routeState?.routingContext ?? {},
1002
+ previousContext: routeState?.previousContext ?? null,
1003
+ routeSummary: routeState?.routeSummary ?? null,
1004
+ }, config);
1005
+ }
1006
+ }
1007
+
1008
+ const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : null;
1009
+ if (invokedPath && pathToFileURL(invokedPath).href === import.meta.url) {
1010
+ runCli().catch(() => {
1011
+ process.exit(0);
1012
+ });
1013
+ }