@ikie-dev/cli 9.8.7 → 9.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/.ikie/ferments/019f2b5d-426e-741a-a176-6031399e42cd/runtime.json +11 -0
  2. package/.ikie/ferments/019f2b5d-4289-71d5-9c92-949495910599/runtime.json +11 -0
  3. package/.ikie/ferments/019f2b5d-42a0-74f5-b804-1dd55616035a/runtime.json +11 -0
  4. package/.ikie/ferments/019f2b5d-42c5-7381-83e5-548f04ddbc55/runtime.json +12 -0
  5. package/.ikie/ferments/019f2b5d-42fd-702c-baef-81f5354cc24e/runtime.json +11 -0
  6. package/.ikie/ferments/019f2b5d-432e-7322-9af5-5333b4686468/runtime.json +15 -0
  7. package/.ikie/ferments/019f2b5d-434d-7429-b25f-0b58109fa362/runtime.json +15 -0
  8. package/.ikie/ferments/019f2b5d-436e-7389-b2ad-6a02cda4f9bb/runtime.json +15 -0
  9. package/.ikie/ferments/019f2b5d-439d-74dd-9786-13df51571d6b/runtime.json +13 -0
  10. package/.ikie/ferments/019f2b5d-43ce-71c9-81e0-16aa00ff9e28/runtime.json +15 -0
  11. package/.ikie/ferments/019f2b5d-440e-72cc-8509-b6b3fd2b6c6c/runtime.json +15 -0
  12. package/.ikie/ferments/019f2b5d-444f-73c1-8172-dbad1e42640a/runtime.json +11 -0
  13. package/.ikie/ferments/019f2b5d-4469-71c1-97ff-23e2bb7ccc3b/runtime.json +15 -0
  14. package/.ikie/ferments/019f2b5d-448d-714d-b181-338e8a5dc8e1/runtime.json +15 -0
  15. package/.ikie/ferments/019f2b5d-44ad-71fb-b0a9-a1f949f9129a/runtime.json +15 -0
  16. package/.ikie/ferments/019f2b5d-44ca-730d-993c-9a827d415ffd/runtime.json +15 -0
  17. package/.ikie/ferments/019f2b5d-44ec-71c8-8523-031284e26b4b/runtime.json +15 -0
  18. package/.ikie/ferments/019f2b5d-450e-7013-93c5-3dd9b900ebd3/runtime.json +15 -0
  19. package/.ikie/ferments/019f2b5d-452a-714d-aff8-fdaca0988792/runtime.json +15 -0
  20. package/.ikie/ferments/019f2b5d-4550-749d-8807-3e9493ba614b/runtime.json +15 -0
  21. package/.ikie/ferments/019f2b5d-4570-7713-8d0f-5d13e8446bfb/runtime.json +15 -0
  22. package/.ikie/ferments/019f2b5d-459a-778a-a7f7-3317ecd6837f/runtime.json +11 -0
  23. package/.ikie/ferments/019f2b5d-45c1-718d-9ba5-b55dd0160a78/runtime.json +11 -0
  24. package/.ikie/ferments/019f2b5d-45f9-7381-9394-4647f47a2132/reviews/phase-phase-1-1.json +63 -0
  25. package/.ikie/ferments/019f2b5d-45f9-7381-9394-4647f47a2132/runtime.json +17 -0
  26. package/.ikie/ferments/019f2b5d-4782-71d4-89de-9c3e734707c8/reviews/phase-phase-1-1.json +63 -0
  27. package/.ikie/ferments/019f2b5d-4782-71d4-89de-9c3e734707c8/runtime.json +17 -0
  28. package/.ikie/ferments/019f2b5d-4950-728c-bf46-542c8ec21a6c/runtime.json +11 -0
  29. package/.ikie/ferments/019f2b5d-4de5-75df-8d7d-a1cd2588a596/runtime.json +11 -0
  30. package/.ikie/ferments/019f2b5d-4e07-70d4-9152-0925c5f81057/runtime.json +11 -0
  31. package/.ikie/ferments/019f2b5d-4e28-7770-8b24-587c856e4b9b/runtime.json +11 -0
  32. package/.ikie/ferments/019f2b5d-4e48-7159-9cb7-6401c6930ed4/runtime.json +11 -0
  33. package/.ikie/ferments/019f2b5d-4e6e-73a9-8bfd-639e394f48a4/pending-proposal.json +45 -0
  34. package/.ikie/ferments/019f2b5d-4e85-73c4-b099-4f6dc69c5268/pending-proposal.json +45 -0
  35. package/.ikie/ferments/019f2b5d-4fd0-7019-a28a-49af2a0266ed/pending-proposal.json +45 -0
  36. package/.ikie/ferments/019f2b5d-5045-70cc-b084-9ccd595944b3/pending-proposal.json +45 -0
  37. package/.ikie/ferments/019f2b5d-5056-759a-ae60-d4e8d2f7554f/pending-proposal.json +45 -0
  38. package/.ikie/ferments/019f2b5d-883b-759c-8453-2a7e8200e481/runtime.json +13 -0
  39. package/.ikie/ferments/019f2b5d-8873-723e-9b60-75d7ece374f9/runtime.json +11 -0
  40. package/.ikie/ferments/019f2b5d-8898-70a3-8c9d-ef7f569c3e72/runtime.json +11 -0
  41. package/.ikie/ferments/019f2b5d-88b4-710c-ba72-ef0fc3dbf0ae/runtime.json +11 -0
  42. package/.ikie/ferments/019f2b5d-88d2-73cf-8983-953907020e62/runtime.json +11 -0
  43. package/.ikie/ferments/019f2b5d-88f3-73df-9ffa-dd262c3d4163/runtime.json +11 -0
  44. package/.ikie/ferments/019f2b5d-8912-74db-b14a-95e882507c3e/runtime.json +11 -0
  45. package/.ikie/ferments/019f2b5d-892b-76c2-b9b3-42dbf26fa1c5/runtime.json +11 -0
  46. package/.ikie/ferments/019f2b5d-8956-74d1-883d-b974060e341f/runtime.json +9 -0
  47. package/.ikie/ferments/019f2b5d-8979-741b-9ecc-2fe299103e8e/runtime.json +13 -0
  48. package/.ikie/ferments/019f2b5d-89a1-74ed-83f8-f19dd9f63c9d/runtime.json +11 -0
  49. package/.ikie/ferments/019f2b5d-89c9-71d8-88b2-8ded0c6a473e/runtime.json +11 -0
  50. package/.ikie/ferments/019f2b5d-8a28-7309-a773-95dde5282ddf/runtime.json +11 -0
  51. package/.ikie/ferments/019f2b5d-8a3c-7088-85cd-9e45836799c8/runtime.json +9 -0
  52. package/.ikie/ferments/019f2b5d-8a58-727b-8607-94e087e8b56f/runtime.json +9 -0
  53. package/.ikie/ferments/019f2b5d-8a6d-7465-bd8e-ab8c1cdbeef8/runtime.json +11 -0
  54. package/.ikie/ferments/019f2b5d-8b30-75d0-9526-93a548435f3a/runtime.json +11 -0
  55. package/.ikie/ferments/019f2b5d-8b4b-7352-a7a2-899427c50e5d/runtime.json +12 -0
  56. package/.ikie/ferments/019f2b5d-8b6f-723b-83fa-556273e78c4e/runtime.json +11 -0
  57. package/.ikie/ferments/019f2b5d-8b95-74b2-b003-5ecf17ff2936/runtime.json +11 -0
  58. package/.ikie/ferments/019f2b5d-8ba6-71f3-90eb-37751e5bab04/runtime.json +11 -0
  59. package/.ikie/ferments/019f2b5d-8bbe-7251-b4f0-5d52ca8bbd0f/runtime.json +11 -0
  60. package/.ikie/ferments/019f2b5d-8bd6-779d-b55d-3df6cfd3d4ba/runtime.json +13 -0
  61. package/.ikie/ferments/019f2b5d-8bee-75b5-acf7-95f68fd37837/runtime.json +11 -0
  62. package/.ikie/ferments/019f2b5d-9b8e-7307-a95c-7e184eed7946/reviews/phase-phase-1-1.json +41 -0
  63. package/.ikie/ferments/019f2b5d-9b8e-7307-a95c-7e184eed7946/runtime.json +11 -0
  64. package/.ikie/ferments/019f2b5d-9bd7-7418-a977-3d39b5027fcc/reviews/phase-phase-1-1.json +47 -0
  65. package/.ikie/ferments/019f2b5d-9bd7-7418-a977-3d39b5027fcc/runtime.json +13 -0
  66. package/.ikie/ferments/019f2b5d-9c36-738b-9800-c32b17ab0a08/reviews/phase-phase-1-1.json +40 -0
  67. package/.ikie/ferments/019f2b5d-9c36-738b-9800-c32b17ab0a08/runtime.json +9 -0
  68. package/.ikie/ferments/019f2b5d-9c67-76b8-9a49-00bf505ca1b8/reviews/phase-phase-1-1.json +40 -0
  69. package/.ikie/ferments/019f2b5d-9c67-76b8-9a49-00bf505ca1b8/runtime.json +9 -0
  70. package/.ikie/ferments/019f2b5d-9c80-73ba-98ca-fa4595c45658/reviews/phase-phase-1-1.json +40 -0
  71. package/.ikie/ferments/019f2b5d-9c80-73ba-98ca-fa4595c45658/runtime.json +9 -0
  72. package/.ikie/ferments/019f2b5d-9c9e-71a5-bd87-e317142e21fd/reviews/phase-phase-1-1.json +40 -0
  73. package/.ikie/ferments/019f2b5d-9c9e-71a5-bd87-e317142e21fd/runtime.json +9 -0
  74. package/.ikie/ferments/019f2b5d-9cc1-73bf-98ae-cdd3759fdbd4/reviews/phase-phase-1-1.json +40 -0
  75. package/.ikie/ferments/019f2b5d-9cc1-73bf-98ae-cdd3759fdbd4/runtime.json +9 -0
  76. package/.ikie/ferments/019f2b5d-9cd9-7478-aa4d-68ff15ec302e/reviews/phase-phase-1-1.json +62 -0
  77. package/.ikie/ferments/019f2b5d-9cd9-7478-aa4d-68ff15ec302e/runtime.json +9 -0
  78. package/.ikie/ferments/019f2b6e-10f5-71fd-b0ac-906640800f2a/runtime.json +11 -0
  79. package/.ikie/ferments/019f2b6e-111f-75fe-af2b-0a9cf1727058/runtime.json +11 -0
  80. package/.ikie/ferments/019f2b6e-113b-73de-9df2-3dc2058b0037/runtime.json +11 -0
  81. package/.ikie/ferments/019f2b6e-115e-772e-91f2-45da602cc046/runtime.json +12 -0
  82. package/.ikie/ferments/019f2b6e-1174-70a1-a06e-4c9a676e7a44/runtime.json +11 -0
  83. package/.ikie/ferments/019f2b6e-1199-70d9-ae9a-01e965e953f1/runtime.json +11 -0
  84. package/.ikie/ferments/019f2b6e-11d1-74af-ab47-8e94823af880/runtime.json +15 -0
  85. package/.ikie/ferments/019f2b6e-11f3-7087-a610-3673d9a730c8/runtime.json +15 -0
  86. package/.ikie/ferments/019f2b6e-1214-7778-974a-3773b4f88517/runtime.json +15 -0
  87. package/.ikie/ferments/019f2b6e-123b-762d-8600-80a4da430695/runtime.json +13 -0
  88. package/.ikie/ferments/019f2b6e-1271-76b7-8c79-2c86e0e70e8c/runtime.json +15 -0
  89. package/.ikie/ferments/019f2b6e-129a-731e-93aa-a4f113bfeda8/runtime.json +15 -0
  90. package/.ikie/ferments/019f2b6e-12db-728a-bea2-d97e3310b918/runtime.json +11 -0
  91. package/.ikie/ferments/019f2b6e-12f5-72a8-8ea9-e83e907bb297/runtime.json +15 -0
  92. package/.ikie/ferments/019f2b6e-131a-714e-8d20-eb7a6aaeed29/runtime.json +15 -0
  93. package/.ikie/ferments/019f2b6e-1349-72aa-a518-6fac705a8a5f/runtime.json +15 -0
  94. package/.ikie/ferments/019f2b6e-1374-740e-8b66-ccc849baf168/runtime.json +15 -0
  95. package/.ikie/ferments/019f2b6e-13a6-75fd-a594-0e6dec52929b/runtime.json +15 -0
  96. package/.ikie/ferments/019f2b6e-13d3-775b-a8ad-e8efc6332d52/runtime.json +15 -0
  97. package/.ikie/ferments/019f2b6e-13ff-753a-aac4-207bd4d84720/runtime.json +15 -0
  98. package/.ikie/ferments/019f2b6e-1423-71ba-9cb9-8d5dc4ba44af/runtime.json +15 -0
  99. package/.ikie/ferments/019f2b6e-1449-77b9-95f8-c8b9a5cdbe1b/runtime.json +15 -0
  100. package/.ikie/ferments/019f2b6e-1475-771f-95b7-41248e78e547/runtime.json +11 -0
  101. package/.ikie/ferments/019f2b6e-1498-76c2-a4c3-4a9d6ea8727d/runtime.json +11 -0
  102. package/.ikie/ferments/019f2b6e-14bd-77ad-89ea-7706d62850c0/reviews/phase-phase-1-1.json +63 -0
  103. package/.ikie/ferments/019f2b6e-14bd-77ad-89ea-7706d62850c0/runtime.json +17 -0
  104. package/.ikie/ferments/019f2b6e-1627-755e-943c-164dc672353c/reviews/phase-phase-1-1.json +63 -0
  105. package/.ikie/ferments/019f2b6e-1627-755e-943c-164dc672353c/runtime.json +17 -0
  106. package/.ikie/ferments/019f2b6e-17a9-74b2-921b-1d9ec0a01d5f/runtime.json +11 -0
  107. package/.ikie/ferments/019f2b6e-1bb3-71ad-8be1-e8e3cd1a78b8/runtime.json +11 -0
  108. package/.ikie/ferments/019f2b6e-1bde-778e-9fd2-dc5291b667d4/runtime.json +11 -0
  109. package/.ikie/ferments/019f2b6e-1bfd-717c-8ab8-6d6b97e73a76/runtime.json +11 -0
  110. package/.ikie/ferments/019f2b6e-1c1e-724b-80f7-e8084123ea42/runtime.json +11 -0
  111. package/.ikie/ferments/019f2b6e-1c3c-70a8-ab5c-8858a41b1111/pending-proposal.json +45 -0
  112. package/.ikie/ferments/019f2b6e-1c52-71af-975e-f514de2d2a9f/pending-proposal.json +45 -0
  113. package/.ikie/ferments/019f2b6e-1d84-726a-9382-0f49ac269f40/pending-proposal.json +45 -0
  114. package/.ikie/ferments/019f2b6e-1dcb-760e-b2cd-c06ba8d57f7c/pending-proposal.json +45 -0
  115. package/.ikie/ferments/019f2b6e-1de3-74a8-8567-7960dca0da46/pending-proposal.json +45 -0
  116. package/.ikie/ferments/019f2b6e-50b5-77dc-a145-de2797f954ba/runtime.json +13 -0
  117. package/.ikie/ferments/019f2b6e-513b-7019-9404-f46db9a8b3ec/runtime.json +11 -0
  118. package/.ikie/ferments/019f2b6e-5159-7711-9957-3393d05b23a3/runtime.json +11 -0
  119. package/.ikie/ferments/019f2b6e-5175-771d-8391-8f6b9f807ea8/runtime.json +11 -0
  120. package/.ikie/ferments/019f2b6e-519c-730d-bfed-592ef56a2720/runtime.json +11 -0
  121. package/.ikie/ferments/019f2b6e-51cd-709b-b3b5-352e20717ad9/runtime.json +11 -0
  122. package/.ikie/ferments/019f2b6e-51f0-728a-9edf-7e48bd6c69a7/runtime.json +11 -0
  123. package/.ikie/ferments/019f2b6e-521b-763a-9fe9-c7197b8209cd/runtime.json +11 -0
  124. package/.ikie/ferments/019f2b6e-5242-702d-a4aa-feb8569b86ad/runtime.json +9 -0
  125. package/.ikie/ferments/019f2b6e-5269-725a-be96-7135e5857c5f/runtime.json +13 -0
  126. package/.ikie/ferments/019f2b6e-52a5-74c9-b708-335a2e9bf42b/runtime.json +11 -0
  127. package/.ikie/ferments/019f2b6e-52e4-7506-89cb-182398a88f40/runtime.json +11 -0
  128. package/.ikie/ferments/019f2b6e-5389-736c-b328-dd8fa27082f9/runtime.json +11 -0
  129. package/.ikie/ferments/019f2b6e-53be-774f-9a27-291c66f2747a/runtime.json +9 -0
  130. package/.ikie/ferments/019f2b6e-53f4-73ae-bbca-2124c710812b/runtime.json +9 -0
  131. package/.ikie/ferments/019f2b6e-542e-71ee-87b1-37bbc5f40ccd/runtime.json +11 -0
  132. package/.ikie/ferments/019f2b6e-55b4-7238-89f5-a2a50dcaff05/runtime.json +11 -0
  133. package/.ikie/ferments/019f2b6e-55ec-72ba-a3b7-3b7739efe858/runtime.json +12 -0
  134. package/.ikie/ferments/019f2b6e-563d-721b-8ba0-f3286b33f62f/runtime.json +11 -0
  135. package/.ikie/ferments/019f2b6e-565b-7453-9046-2a7e60c63c62/runtime.json +11 -0
  136. package/.ikie/ferments/019f2b6e-5684-7125-a1ef-41494eb8be74/runtime.json +11 -0
  137. package/.ikie/ferments/019f2b6e-56a7-7512-b7e5-3c709a24a5e9/runtime.json +11 -0
  138. package/.ikie/ferments/019f2b6e-56ea-702a-9a97-7911f5f2caad/runtime.json +13 -0
  139. package/.ikie/ferments/019f2b6e-5730-75fb-bad2-18d701837e37/runtime.json +11 -0
  140. package/.ikie/ferments/019f2b6e-645f-7522-a80c-ddcd6935f5dc/reviews/phase-phase-1-1.json +41 -0
  141. package/.ikie/ferments/019f2b6e-645f-7522-a80c-ddcd6935f5dc/runtime.json +11 -0
  142. package/.ikie/ferments/019f2b6e-64aa-7328-ae9b-04f663f377b8/reviews/phase-phase-1-1.json +47 -0
  143. package/.ikie/ferments/019f2b6e-64aa-7328-ae9b-04f663f377b8/runtime.json +13 -0
  144. package/.ikie/ferments/019f2b6e-64fe-723a-99a2-0fab54b29f4a/reviews/phase-phase-1-1.json +40 -0
  145. package/.ikie/ferments/019f2b6e-64fe-723a-99a2-0fab54b29f4a/runtime.json +9 -0
  146. package/.ikie/ferments/019f2b6e-651b-7018-91c0-baa3cf77c217/reviews/phase-phase-1-1.json +40 -0
  147. package/.ikie/ferments/019f2b6e-651b-7018-91c0-baa3cf77c217/runtime.json +9 -0
  148. package/.ikie/ferments/019f2b6e-6533-70ee-a6de-6b3bc5ca17ef/reviews/phase-phase-1-1.json +40 -0
  149. package/.ikie/ferments/019f2b6e-6533-70ee-a6de-6b3bc5ca17ef/runtime.json +9 -0
  150. package/.ikie/ferments/019f2b6e-654d-7760-942f-6ea45bebb394/reviews/phase-phase-1-1.json +40 -0
  151. package/.ikie/ferments/019f2b6e-654d-7760-942f-6ea45bebb394/runtime.json +9 -0
  152. package/.ikie/ferments/019f2b6e-6563-74c5-bfd2-d8075d4005db/reviews/phase-phase-1-1.json +40 -0
  153. package/.ikie/ferments/019f2b6e-6563-74c5-bfd2-d8075d4005db/runtime.json +9 -0
  154. package/.ikie/ferments/019f2b6e-6586-748a-83ac-05ea7a9e24f3/reviews/phase-phase-1-1.json +62 -0
  155. package/.ikie/ferments/019f2b6e-6586-748a-83ac-05ea7a9e24f3/runtime.json +9 -0
  156. package/.ikie/ferments/019f2b8e-485a-722f-ab3e-0b23bdf37851/runtime.json +11 -0
  157. package/.ikie/ferments/019f2b8e-4876-7247-ba41-e38f1dc709af/runtime.json +11 -0
  158. package/.ikie/ferments/019f2b8e-488d-732e-9b99-75f4ad2f9983/runtime.json +11 -0
  159. package/.ikie/ferments/019f2b8e-48ad-70b7-b262-ecb600a8be1a/runtime.json +12 -0
  160. package/.ikie/ferments/019f2b8e-48d9-73af-92cb-aa861441eef8/runtime.json +11 -0
  161. package/.ikie/ferments/019f2b8e-4906-7494-9168-5e15c0e984ed/runtime.json +15 -0
  162. package/.ikie/ferments/019f2b8e-4929-71db-a7b5-b8686b433b49/runtime.json +15 -0
  163. package/.ikie/ferments/019f2b8e-494a-774c-8e7e-e881204e3b2b/runtime.json +15 -0
  164. package/.ikie/ferments/019f2b8e-4972-70ab-8b70-9f8ac3ad9f33/runtime.json +13 -0
  165. package/.ikie/ferments/019f2b8e-49a7-7058-95e7-ea362f0554ba/runtime.json +15 -0
  166. package/.ikie/ferments/019f2b8e-49d5-750e-a1e0-7e7cf027c6c7/runtime.json +15 -0
  167. package/.ikie/ferments/019f2b8e-4a12-7181-a65a-c6bbb6e750f3/runtime.json +11 -0
  168. package/.ikie/ferments/019f2b8e-4a2a-7741-a5d7-8109a9bf4c22/runtime.json +15 -0
  169. package/.ikie/ferments/019f2b8e-4a4e-712f-9830-598e984f59b5/runtime.json +15 -0
  170. package/.ikie/ferments/019f2b8e-4a72-71da-97bf-f2970d34ae8d/runtime.json +15 -0
  171. package/.ikie/ferments/019f2b8e-4a93-76bc-8a04-28cdbb24a987/runtime.json +15 -0
  172. package/.ikie/ferments/019f2b8e-4ab9-770e-b160-59b011c69b81/runtime.json +15 -0
  173. package/.ikie/ferments/019f2b8e-4ae2-7619-8055-ddefbc9f4698/runtime.json +15 -0
  174. package/.ikie/ferments/019f2b8e-4b0c-722e-9afe-6fab90611474/runtime.json +15 -0
  175. package/.ikie/ferments/019f2b8e-4b3f-7143-b577-91721e39f89d/runtime.json +15 -0
  176. package/.ikie/ferments/019f2b8e-4b72-720d-a104-19ce38a362e2/runtime.json +15 -0
  177. package/.ikie/ferments/019f2b8e-4bab-70e9-ac26-136c55e9e4c0/runtime.json +11 -0
  178. package/.ikie/ferments/019f2b8e-4bca-714d-80d0-317ca4733687/runtime.json +11 -0
  179. package/.ikie/ferments/019f2b8e-4bf1-7029-a93f-c509e847ec71/reviews/phase-phase-1-1.json +63 -0
  180. package/.ikie/ferments/019f2b8e-4bf1-7029-a93f-c509e847ec71/runtime.json +17 -0
  181. package/.ikie/ferments/019f2b8e-4d51-75ff-951e-652cf6a5e901/reviews/phase-phase-1-1.json +63 -0
  182. package/.ikie/ferments/019f2b8e-4d51-75ff-951e-652cf6a5e901/runtime.json +17 -0
  183. package/.ikie/ferments/019f2b8e-4ece-70cf-a9bb-9ba0327f74f1/runtime.json +11 -0
  184. package/.ikie/ferments/019f2b8e-52f5-71dc-86d6-d08c3dba9c10/runtime.json +11 -0
  185. package/.ikie/ferments/019f2b8e-5313-747f-b0b7-9ec09f80674a/runtime.json +11 -0
  186. package/.ikie/ferments/019f2b8e-5333-759e-9a2e-2198d28d8a31/runtime.json +11 -0
  187. package/.ikie/ferments/019f2b8e-535d-7501-86c4-2d120359fda5/runtime.json +11 -0
  188. package/.ikie/ferments/019f2b8e-537e-7528-b236-a550ec6f9b9b/pending-proposal.json +45 -0
  189. package/.ikie/ferments/019f2b8e-5393-74aa-86a6-31db0e99c73c/pending-proposal.json +45 -0
  190. package/.ikie/ferments/019f2b8e-54cd-7573-bb52-14899e431b6a/pending-proposal.json +45 -0
  191. package/.ikie/ferments/019f2b8e-551d-755d-9aa9-52bb0c7ce63e/pending-proposal.json +45 -0
  192. package/.ikie/ferments/019f2b8e-5549-708c-8b89-1a6cc53a4ec5/pending-proposal.json +45 -0
  193. package/.ikie/ferments/019f2b8e-8874-7469-a3ca-1f19416a5413/runtime.json +13 -0
  194. package/.ikie/ferments/019f2b8e-88a4-737d-82ba-59fb52017a81/runtime.json +11 -0
  195. package/.ikie/ferments/019f2b8e-88c7-7448-a22d-bc9dcd6e06a2/runtime.json +11 -0
  196. package/.ikie/ferments/019f2b8e-88dc-72a8-813b-c9f1c91a6dd2/runtime.json +11 -0
  197. package/.ikie/ferments/019f2b8e-88ee-7600-a227-18ca80b3d525/runtime.json +11 -0
  198. package/.ikie/ferments/019f2b8e-88ff-7027-9bb9-e583b435e8b0/runtime.json +11 -0
  199. package/.ikie/ferments/019f2b8e-8919-737a-9f89-b74eeb851e5c/runtime.json +11 -0
  200. package/.ikie/ferments/019f2b8e-8936-71cc-9056-bca6ef3cbb91/runtime.json +11 -0
  201. package/.ikie/ferments/019f2b8e-8966-77f5-b367-a9262cc45f25/runtime.json +9 -0
  202. package/.ikie/ferments/019f2b8e-8990-747d-b585-59cd82d765c6/runtime.json +13 -0
  203. package/.ikie/ferments/019f2b8e-89c9-71ed-a2e6-016626cbe5aa/runtime.json +11 -0
  204. package/.ikie/ferments/019f2b8e-89de-741f-ba71-489cca892cc4/runtime.json +11 -0
  205. package/.ikie/ferments/019f2b8e-8a1e-727d-b14a-daf7374500ea/runtime.json +11 -0
  206. package/.ikie/ferments/019f2b8e-8a35-7463-803e-de399c8d3158/runtime.json +9 -0
  207. package/.ikie/ferments/019f2b8e-8a51-72ad-b6d1-966e350960b7/runtime.json +9 -0
  208. package/.ikie/ferments/019f2b8e-8a6c-73e1-a3ab-f1e554db6b03/runtime.json +11 -0
  209. package/.ikie/ferments/019f2b8e-8b16-7688-a299-84f3acc74338/runtime.json +11 -0
  210. package/.ikie/ferments/019f2b8e-8b2f-71bf-8c76-21f5ce28b4fd/runtime.json +12 -0
  211. package/.ikie/ferments/019f2b8e-8b54-711e-b25c-8e06b22d52a3/runtime.json +11 -0
  212. package/.ikie/ferments/019f2b8e-8b66-7039-97d2-00e7b31d85fe/runtime.json +11 -0
  213. package/.ikie/ferments/019f2b8e-8b7a-76c8-8456-727db5f07f3f/runtime.json +11 -0
  214. package/.ikie/ferments/019f2b8e-8b8e-7451-a3d6-7fbcc67a28c9/runtime.json +11 -0
  215. package/.ikie/ferments/019f2b8e-8ba1-73e8-bff2-bfbc47873b3b/runtime.json +13 -0
  216. package/.ikie/ferments/019f2b8e-8bb9-721b-aad5-fab25fa9b1d1/runtime.json +11 -0
  217. package/.ikie/ferments/019f2b8e-982a-74b9-b1b2-ad6c1b598e0a/reviews/phase-phase-1-1.json +41 -0
  218. package/.ikie/ferments/019f2b8e-982a-74b9-b1b2-ad6c1b598e0a/runtime.json +11 -0
  219. package/.ikie/ferments/019f2b8e-986d-7469-949a-be76ce3780d3/reviews/phase-phase-1-1.json +47 -0
  220. package/.ikie/ferments/019f2b8e-986d-7469-949a-be76ce3780d3/runtime.json +13 -0
  221. package/.ikie/ferments/019f2b8e-98ba-749a-839a-f5b64fbca3bc/reviews/phase-phase-1-1.json +40 -0
  222. package/.ikie/ferments/019f2b8e-98ba-749a-839a-f5b64fbca3bc/runtime.json +9 -0
  223. package/.ikie/ferments/019f2b8e-98f7-7482-b89d-ca40d860d72b/reviews/phase-phase-1-1.json +40 -0
  224. package/.ikie/ferments/019f2b8e-98f7-7482-b89d-ca40d860d72b/runtime.json +9 -0
  225. package/.ikie/ferments/019f2b8e-991a-729a-83c8-0da93e3f26ff/reviews/phase-phase-1-1.json +40 -0
  226. package/.ikie/ferments/019f2b8e-991a-729a-83c8-0da93e3f26ff/runtime.json +9 -0
  227. package/.ikie/ferments/019f2b8e-9931-713f-845d-211cb77d0482/reviews/phase-phase-1-1.json +40 -0
  228. package/.ikie/ferments/019f2b8e-9931-713f-845d-211cb77d0482/runtime.json +9 -0
  229. package/.ikie/ferments/019f2b8e-9952-734c-a3b4-8877348a7856/reviews/phase-phase-1-1.json +40 -0
  230. package/.ikie/ferments/019f2b8e-9952-734c-a3b4-8877348a7856/runtime.json +9 -0
  231. package/.ikie/ferments/019f2b8e-9974-700e-b045-cc2244e4d691/reviews/phase-phase-1-1.json +62 -0
  232. package/.ikie/ferments/019f2b8e-9974-700e-b045-cc2244e4d691/runtime.json +9 -0
  233. package/benchmark/audit-session/audit-session.sh +0 -0
  234. package/benchmark/manual/check-session.py +0 -0
  235. package/benchmark/manual/new-session-claude.sh +0 -0
  236. package/benchmark/manual/new-session.sh +0 -0
  237. package/benchmark/manual/run-evaluation.sh +0 -0
  238. package/benchmark/manual/start-self-improvement.sh +0 -0
  239. package/benchmark/terminal-bench-2/scripts/analyze-ferment-bench.py +0 -0
  240. package/benchmark/terminal-bench-2/scripts/run-claude-code-ikie.sh +0 -0
  241. package/benchmark/terminal-bench-2/scripts/run-gsd-ikie.sh +0 -0
  242. package/benchmark/terminal-bench-2/scripts/run-local.sh +0 -0
  243. package/benchmark/terminal-bench-2/scripts/run-opencode-ikie.sh +0 -0
  244. package/benchmark/terminal-bench-2/scripts/run-release.sh +0 -0
  245. package/package.json +103 -125
  246. package/scripts/dev-overlay.sh +0 -0
  247. package/scripts/dev-startup.sh +0 -0
  248. package/scripts/install.sh +0 -0
  249. package/scripts/prepublish-npm.js +1 -1
  250. package/scripts/verify-acp-load.mjs +0 -0
  251. package/src/cli-auth/index.test.ts +14 -10
  252. package/src/extensions/agents/prompt/skill-loader.test.ts +18 -0
  253. package/src/extensions/agents/prompt/skill-loader.ts +4 -1
  254. package/src/extensions/prompt-construction/prompt-enrichment.test.ts +41 -0
  255. package/src/extensions/prompt-construction/prompt-enrichment.ts +4 -1
  256. package/src/extensions/skills-manager/skill-manager.test.ts +31 -14
  257. package/src/extensions/skills-manager/skill-manager.ts +14 -5
  258. package/src/extensions/skills-manager/skill-sanitizer.test.ts +287 -0
  259. package/src/extensions/skills-manager/skill-sanitizer.ts +269 -0
  260. package/src/models.test.ts +3 -1
  261. package/vendor/superpowers/hooks/run-hook.cmd +0 -0
  262. package/vendor/superpowers/hooks/session-start +0 -0
  263. package/vendor/superpowers/scripts/bump-version.sh +0 -0
  264. package/vendor/superpowers/scripts/sync-to-codex-plugin.sh +0 -0
  265. package/vendor/superpowers/skills/brainstorming/scripts/start-server.sh +0 -0
  266. package/vendor/superpowers/skills/brainstorming/scripts/stop-server.sh +0 -0
  267. package/vendor/superpowers/skills/systematic-debugging/find-polluter.sh +0 -0
  268. package/vendor/superpowers/skills/writing-skills/render-graphs.js +0 -0
  269. package/vendor/superpowers/tests/brainstorm-server/windows-lifecycle.test.sh +0 -0
  270. package/vendor/superpowers/tests/claude-code/analyze-token-usage.py +0 -0
  271. package/vendor/superpowers/tests/claude-code/run-skill-tests.sh +0 -0
  272. package/vendor/superpowers/tests/claude-code/test-document-review-system.sh +0 -0
  273. package/vendor/superpowers/tests/claude-code/test-helpers.sh +0 -0
  274. package/vendor/superpowers/tests/claude-code/test-requesting-code-review.sh +0 -0
  275. package/vendor/superpowers/tests/claude-code/test-subagent-driven-development-integration.sh +0 -0
  276. package/vendor/superpowers/tests/claude-code/test-subagent-driven-development.sh +0 -0
  277. package/vendor/superpowers/tests/claude-code/test-worktree-native-preference.sh +0 -0
  278. package/vendor/superpowers/tests/codex-plugin-sync/test-sync-to-codex-plugin.sh +0 -0
  279. package/vendor/superpowers/tests/explicit-skill-requests/run-all.sh +0 -0
  280. package/vendor/superpowers/tests/explicit-skill-requests/run-claude-describes-sdd.sh +0 -0
  281. package/vendor/superpowers/tests/explicit-skill-requests/run-extended-multiturn-test.sh +0 -0
  282. package/vendor/superpowers/tests/explicit-skill-requests/run-haiku-test.sh +0 -0
  283. package/vendor/superpowers/tests/explicit-skill-requests/run-multiturn-test.sh +0 -0
  284. package/vendor/superpowers/tests/explicit-skill-requests/run-test.sh +0 -0
  285. package/vendor/superpowers/tests/opencode/run-tests.sh +0 -0
  286. package/vendor/superpowers/tests/opencode/setup.sh +0 -0
  287. package/vendor/superpowers/tests/opencode/test-bootstrap-caching.sh +0 -0
  288. package/vendor/superpowers/tests/opencode/test-plugin-loading.sh +0 -0
  289. package/vendor/superpowers/tests/opencode/test-priority.sh +0 -0
  290. package/vendor/superpowers/tests/opencode/test-tools.sh +0 -0
  291. package/vendor/superpowers/tests/skill-triggering/run-all.sh +0 -0
  292. package/vendor/superpowers/tests/skill-triggering/run-test.sh +0 -0
  293. package/vendor/superpowers/tests/subagent-driven-dev/go-fractals/scaffold.sh +0 -0
  294. package/vendor/superpowers/tests/subagent-driven-dev/run-test.sh +0 -0
  295. package/vendor/superpowers/tests/subagent-driven-dev/svelte-todo/scaffold.sh +0 -0
@@ -0,0 +1,287 @@
1
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
2
+ import { tmpdir } from "node:os"
3
+ import { join } from "node:path"
4
+ import { loadSkillsFromDir } from "@earendil-works/pi-coding-agent"
5
+ import { afterEach, describe, expect, it } from "vitest"
6
+ import { createSkillSanitizer, isValidSkillName, normalizeSkillName, sanitizeSkillMarkdown } from "./skill-sanitizer.js"
7
+
8
+ describe("normalizeSkillName", () => {
9
+ it("lowercases input", () => {
10
+ expect(normalizeSkillName("FooBar")).toBe("foobar")
11
+ })
12
+
13
+ it("replaces runs of invalid chars with a single hyphen", () => {
14
+ expect(normalizeSkillName("ckm:slides")).toBe("ckm-slides")
15
+ expect(normalizeSkillName("foo_bar")).toBe("foo-bar")
16
+ expect(normalizeSkillName("a b")).toBe("a-b")
17
+ expect(normalizeSkillName("a___b")).toBe("a-b")
18
+ })
19
+
20
+ it("trims leading and trailing hyphens", () => {
21
+ expect(normalizeSkillName("-foo-")).toBe("foo")
22
+ expect(normalizeSkillName("---bar---")).toBe("bar")
23
+ })
24
+
25
+ it("truncates to 64 characters", () => {
26
+ const long = "a".repeat(100)
27
+ expect(normalizeSkillName(long)).toHaveLength(64)
28
+ })
29
+
30
+ it("falls back to 'skill' when empty after normalization", () => {
31
+ expect(normalizeSkillName("")).toBe("skill")
32
+ expect(normalizeSkillName("___")).toBe("skill")
33
+ expect(normalizeSkillName("---")).toBe("skill")
34
+ })
35
+
36
+ it("collapses consecutive hyphens", () => {
37
+ expect(normalizeSkillName("foo--bar")).toBe("foo-bar")
38
+ })
39
+ })
40
+
41
+ describe("isValidSkillName", () => {
42
+ it("accepts valid names", () => {
43
+ expect(isValidSkillName("ckm-slides")).toBe(true)
44
+ expect(isValidSkillName("ui-ux-pro-max")).toBe(true)
45
+ expect(isValidSkillName("abc")).toBe(true)
46
+ expect(isValidSkillName("a1b2")).toBe(true)
47
+ })
48
+
49
+ it("rejects empty or too long", () => {
50
+ expect(isValidSkillName("")).toBe(false)
51
+ expect(isValidSkillName("a".repeat(65))).toBe(false)
52
+ })
53
+
54
+ it("rejects invalid characters", () => {
55
+ expect(isValidSkillName("ckm:slides")).toBe(false)
56
+ expect(isValidSkillName("Foo_Bar")).toBe(false)
57
+ expect(isValidSkillName("foo.bar")).toBe(false)
58
+ })
59
+
60
+ it("rejects leading/trailing hyphens", () => {
61
+ expect(isValidSkillName("-bad")).toBe(false)
62
+ expect(isValidSkillName("bad-")).toBe(false)
63
+ })
64
+
65
+ it("rejects consecutive hyphens", () => {
66
+ expect(isValidSkillName("bad--bad")).toBe(false)
67
+ })
68
+ })
69
+
70
+ describe("sanitizeSkillMarkdown", () => {
71
+ it("leaves valid skill content unchanged", () => {
72
+ const input = `---
73
+ name: ckm-slides
74
+ description: My slides skill
75
+ ---
76
+
77
+ # Body
78
+
79
+ Some content.`
80
+ expect(sanitizeSkillMarkdown(input)).toBe(input)
81
+ })
82
+
83
+ it("rewrites name: ckm:slides to quoted normalized form", () => {
84
+ const input = `---
85
+ name: ckm:slides
86
+ description: slides
87
+ ---
88
+
89
+ Body content`
90
+ const out = sanitizeSkillMarkdown(input)
91
+ expect(out).toContain('name: "ckm-slides"')
92
+ expect(out).not.toContain("ckm:slides")
93
+ })
94
+
95
+ it("rewrites name: Foo_Bar to quoted normalized form", () => {
96
+ const input = `---
97
+ name: Foo_Bar
98
+ description: foo bar
99
+ ---
100
+
101
+ Body`
102
+ const out = sanitizeSkillMarkdown(input)
103
+ expect(out).toContain('name: "foo-bar"')
104
+ expect(out).not.toContain("Foo_Bar")
105
+ })
106
+
107
+ it("preserves body content", () => {
108
+ const input = `---
109
+ name: ckm:slides
110
+ description: slides
111
+ ---
112
+
113
+ # Heading
114
+
115
+ Important body content with code:
116
+
117
+ \`\`\`bash
118
+ echo hi
119
+ \`\`\`
120
+ `
121
+ const out = sanitizeSkillMarkdown(input)
122
+ expect(out).toContain("# Heading")
123
+ expect(out).toContain("Important body content")
124
+ expect(out).toContain("echo hi")
125
+ })
126
+
127
+ it("preserves non-name frontmatter keys", () => {
128
+ const input = `---
129
+ name: ckm:slides
130
+ description: slides
131
+ allowed-tools: Read, Write
132
+ ---
133
+
134
+ Body`
135
+ const out = sanitizeSkillMarkdown(input)
136
+ expect(out).toContain('name: "ckm-slides"')
137
+ expect(out).toContain("allowed-tools: Read, Write")
138
+ expect(out).toContain("description: slides")
139
+ })
140
+
141
+ it("returns content unchanged when name is missing", () => {
142
+ const input = `---
143
+ description: only description
144
+ ---
145
+
146
+ Body`
147
+ expect(sanitizeSkillMarkdown(input)).toBe(input)
148
+ })
149
+
150
+ it("returns content unchanged when there is no frontmatter", () => {
151
+ const input = "Just a body with no frontmatter."
152
+ expect(sanitizeSkillMarkdown(input)).toBe(input)
153
+ })
154
+
155
+ it("returns content unchanged on frontmatter parse failure", () => {
156
+ const input = `---
157
+ name: ckm:slides
158
+ description: [unterminated
159
+ ---
160
+
161
+ Body`
162
+ expect(sanitizeSkillMarkdown(input)).toBe(input)
163
+ })
164
+ })
165
+
166
+ describe("createSkillSanitizer / sanitizeDir", () => {
167
+ let createdTempDirs: string[] = []
168
+
169
+ afterEach(() => {
170
+ for (const d of createdTempDirs) {
171
+ try {
172
+ rmSync(d, { recursive: true, force: true })
173
+ } catch {
174
+ // ignore
175
+ }
176
+ }
177
+ createdTempDirs = []
178
+ })
179
+
180
+ function makeSourceDir(files: Record<string, string>): string {
181
+ const dir = mkdtempSync(join(tmpdir(), "ikie-sanitizer-test-src-"))
182
+ createdTempDirs.push(dir)
183
+ for (const [relPath, content] of Object.entries(files)) {
184
+ const full = join(dir, relPath)
185
+ mkdirSync(join(full, ".."), { recursive: true })
186
+ writeFileSync(full, content, "utf-8")
187
+ }
188
+ return dir
189
+ }
190
+
191
+ it("produces a directory that loads cleanly via upstream loadSkillsFromDir", () => {
192
+ const source = makeSourceDir({
193
+ "ckm:slides/SKILL.md": `---
194
+ name: ckm:slides
195
+ description: slides skill
196
+ ---
197
+
198
+ body`,
199
+ })
200
+
201
+ const sanitizer = createSkillSanitizer()
202
+ const sanitizedDir = sanitizer.sanitizeDir(source)
203
+ createdTempDirs.push(sanitizedDir)
204
+ expect(existsSync(join(sanitizedDir, "ckm:slides", "SKILL.md"))).toBe(true)
205
+
206
+ const result = loadSkillsFromDir({ dir: sanitizedDir, source: "user" })
207
+ const nameDiagnostics = result.diagnostics.filter(
208
+ (d) => typeof d.message === "string" && d.message.toLowerCase().includes("name"),
209
+ )
210
+ expect(nameDiagnostics).toEqual([])
211
+ expect(result.skills.length).toBe(1)
212
+ expect(result.skills[0].name).toBe("ckm-slides")
213
+
214
+ const sanitizedContent = readFileSync(join(sanitizedDir, "ckm:slides", "SKILL.md"), "utf-8")
215
+ expect(sanitizedContent).toContain('name: "ckm-slides"')
216
+
217
+ sanitizer.cleanup()
218
+ })
219
+
220
+ it("sanitizePath handles a directory via sanitizeDir", () => {
221
+ const source = makeSourceDir({
222
+ "ckm:slides/SKILL.md": `---
223
+ name: ckm:slides
224
+ description: slides skill
225
+ ---
226
+
227
+ body`,
228
+ })
229
+
230
+ const sanitizer = createSkillSanitizer()
231
+ const sanitizedDir = sanitizer.sanitizePath(source)
232
+ createdTempDirs.push(sanitizedDir)
233
+ expect(existsSync(join(sanitizedDir, "ckm:slides", "SKILL.md"))).toBe(true)
234
+
235
+ const result = loadSkillsFromDir({ dir: sanitizedDir, source: "user" })
236
+ const nameDiagnostics = result.diagnostics.filter(
237
+ (d) => typeof d.message === "string" && d.message.toLowerCase().includes("name"),
238
+ )
239
+ expect(nameDiagnostics).toEqual([])
240
+ expect(result.skills.length).toBe(1)
241
+ expect(result.skills[0].name).toBe("ckm-slides")
242
+
243
+ sanitizer.cleanup()
244
+ })
245
+
246
+ it("sanitizePath handles a single .md file by copying it into a cached skill directory", () => {
247
+ const sourceDir = mkdtempSync(join(tmpdir(), "ikie-sanitizer-test-src-"))
248
+ createdTempDirs.push(sourceDir)
249
+ const sourceFile = join(sourceDir, "ckm:slides.md")
250
+ writeFileSync(
251
+ sourceFile,
252
+ `---
253
+ name: ckm:slides
254
+ description: slides skill
255
+ ---
256
+
257
+ body content`,
258
+ "utf-8",
259
+ )
260
+
261
+ const sanitizer = createSkillSanitizer()
262
+ const sanitizedDir = sanitizer.sanitizePath(sourceFile)
263
+ createdTempDirs.push(sanitizedDir)
264
+ expect(existsSync(join(sanitizedDir, "SKILL.md"))).toBe(true)
265
+
266
+ const result = loadSkillsFromDir({ dir: sanitizedDir, source: "user" })
267
+ const nameDiagnostics = result.diagnostics.filter(
268
+ (d) => typeof d.message === "string" && d.message.toLowerCase().includes("name"),
269
+ )
270
+ expect(nameDiagnostics).toEqual([])
271
+ expect(result.skills.length).toBe(1)
272
+ expect(result.skills[0].name).toBe("ckm-slides")
273
+
274
+ const sanitizedContent = readFileSync(join(sanitizedDir, "SKILL.md"), "utf-8")
275
+ expect(sanitizedContent).toContain('name: "ckm-slides"')
276
+ expect(sanitizedContent).toContain("body content")
277
+
278
+ sanitizer.cleanup()
279
+ })
280
+
281
+ it("sanitizePath returns the original path for non-existent paths", () => {
282
+ const sanitizer = createSkillSanitizer()
283
+ const missing = join(tmpdir(), "ikie-sanitizer-missing-", Date.now().toString())
284
+ expect(sanitizer.sanitizePath(missing)).toBe(missing)
285
+ sanitizer.cleanup()
286
+ })
287
+ })
@@ -0,0 +1,269 @@
1
+ import { createHash } from "node:crypto"
2
+ import {
3
+ cpSync,
4
+ existsSync,
5
+ mkdirSync,
6
+ mkdtempSync,
7
+ readFileSync,
8
+ readdirSync,
9
+ rmSync,
10
+ statSync,
11
+ writeFileSync,
12
+ } from "node:fs"
13
+ import { tmpdir } from "node:os"
14
+ import { basename, extname, join } from "node:path"
15
+ import { parse as parseYaml } from "yaml"
16
+
17
+ /**
18
+ * Shared skill-name sanitizer for ikie.
19
+ *
20
+ * Mirrors the Agent Skills spec used by `@earendil-works/pi-coding-agent`:
21
+ * - lowercase a-z, digits, hyphens only
22
+ * - no leading/trailing hyphens
23
+ * - no consecutive hyphens
24
+ * - max 64 characters
25
+ */
26
+
27
+ const MAX_NAME_LENGTH = 64
28
+ const VALID_NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/
29
+ const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/
30
+
31
+ /**
32
+ * Normalize a raw skill name to a valid Agent Skills name.
33
+ * - lower-case
34
+ * - replace runs of invalid characters with a single hyphen
35
+ * - trim leading/trailing hyphens
36
+ * - truncate to 64 characters
37
+ * - fall back to `"skill"` if the result would be empty
38
+ */
39
+ export function normalizeSkillName(raw: string): string {
40
+ const normalized = raw
41
+ .toLowerCase()
42
+ .replace(/[^a-z0-9-]+/g, "-")
43
+ .replace(/-+/g, "-")
44
+ .replace(/^-+|-+$/g, "")
45
+ .slice(0, MAX_NAME_LENGTH)
46
+ .replace(/^-+|-+$/g, "")
47
+
48
+ return normalized.length > 0 ? normalized : "skill"
49
+ }
50
+
51
+ /**
52
+ * True iff `name` is a valid Agent Skills name (matches the upstream regex and length limit).
53
+ */
54
+ export function isValidSkillName(name: string): boolean {
55
+ if (typeof name !== "string") return false
56
+ if (name.length === 0 || name.length > MAX_NAME_LENGTH) return false
57
+ return VALID_NAME_RE.test(name)
58
+ }
59
+
60
+ /**
61
+ * Sanitize the frontmatter `name` field of a skill markdown file in-place.
62
+ *
63
+ * - If the content does not start with `---` frontmatter, return as-is.
64
+ * - Parse the frontmatter YAML and read the `name` field.
65
+ * - If `name` is missing or already valid, return the original content unchanged.
66
+ * - If `name` is invalid, rewrite the first top-level `name:` line to
67
+ * `name: <normalized>` (the normalized value is JSON-quoted).
68
+ * - If frontmatter parsing fails, return the original content unchanged.
69
+ */
70
+ export function sanitizeSkillMarkdown(content: string): string {
71
+ const match = FRONTMATTER_REGEX.exec(content)
72
+ if (!match) return content
73
+
74
+ const frontmatter = match[1]
75
+ const fmStart = match.index ?? 0
76
+ const fmEnd = fmStart + match[0].length
77
+
78
+ let parsed: unknown
79
+ try {
80
+ parsed = parseYaml(frontmatter)
81
+ } catch {
82
+ return content
83
+ }
84
+
85
+ if (typeof parsed !== "object" || parsed === null) {
86
+ return content
87
+ }
88
+
89
+ const record = parsed as Record<string, unknown>
90
+ const rawName = record.name
91
+ if (typeof rawName !== "string" || rawName.length === 0) {
92
+ return content
93
+ }
94
+ if (isValidSkillName(rawName)) {
95
+ return content
96
+ }
97
+
98
+ const normalized = normalizeSkillName(rawName)
99
+ const replacement = `name: ${JSON.stringify(normalized)}`
100
+
101
+ // match[1] is the inner frontmatter text between the two `---` markers.
102
+ // Its offset within `content` is right after the opening `---` line.
103
+ const openingPrefix = content.slice(fmStart, fmStart + 4) // "---\n" or "---\r\n"
104
+ const innerStart = fmStart + openingPrefix.length
105
+ const innerEnd = innerStart + match[1].length
106
+
107
+ const inner = content.slice(innerStart, innerEnd)
108
+ const lines = inner.split("\n")
109
+ let replaced = false
110
+ const newLines = lines.map((line) => {
111
+ if (replaced) return line
112
+ // Match top-level "name:" key (no leading whitespace).
113
+ if (/^name:\s*/.test(line)) {
114
+ replaced = true
115
+ return replacement
116
+ }
117
+ return line
118
+ })
119
+
120
+ if (!replaced) {
121
+ // No top-level `name:` line was found; insert one at the top of the frontmatter.
122
+ newLines.unshift(replacement)
123
+ }
124
+
125
+ const newInner = newLines.join("\n")
126
+ return content.slice(0, innerStart) + newInner + content.slice(innerEnd)
127
+ }
128
+
129
+ interface SkillSanitizer {
130
+ sanitizeDir(dir: string): string
131
+ sanitizePath(path: string): string
132
+ cleanup(): void
133
+ }
134
+
135
+ let cachedRoot: string | undefined
136
+ let cleanupRegistered = false
137
+
138
+ /**
139
+ * Create a process-lifetime skill sanitizer. It maintains a single temp
140
+ * directory and caches per-input sanitized copies so callers can re-use
141
+ * the result across invocations.
142
+ *
143
+ * The returned `sanitizeDir(dir)` copies `dir` (recursively) into the cache
144
+ * and rewrites every `SKILL.md` in place with `sanitizeSkillMarkdown`.
145
+ * The cached path is returned.
146
+ *
147
+ * `cleanup()` removes the temp directory; it is also registered as a
148
+ * process-exit handler on first invocation.
149
+ */
150
+ export function createSkillSanitizer(): SkillSanitizer {
151
+ function getRoot(): string {
152
+ if (cachedRoot === undefined) {
153
+ cachedRoot = mkdtempSync(join(tmpdir(), "ikie-sanitized-skills-"))
154
+ if (!cleanupRegistered) {
155
+ cleanupRegistered = true
156
+ process.once("exit", () => {
157
+ if (cachedRoot) {
158
+ try {
159
+ rmSync(cachedRoot, { recursive: true, force: true })
160
+ } catch {
161
+ // best-effort cleanup
162
+ }
163
+ }
164
+ })
165
+ }
166
+ }
167
+ return cachedRoot
168
+ }
169
+
170
+ function sanitizeDir(dir: string): string {
171
+ const root = getRoot()
172
+ const key = createHash("sha256").update(dir).digest("hex").slice(0, 12)
173
+ const target = join(root, key)
174
+ rmSync(target, { recursive: true, force: true })
175
+ mkdirSync(target, { recursive: true })
176
+ cpSync(dir, target, { recursive: true, force: true })
177
+
178
+ rewriteSkillFiles(target)
179
+
180
+ return target
181
+ }
182
+
183
+ function sanitizePath(path: string): string {
184
+ if (!existsSync(path)) {
185
+ return path
186
+ }
187
+
188
+ try {
189
+ const stats = statSync(path)
190
+ if (stats.isDirectory()) {
191
+ return sanitizeDir(path)
192
+ }
193
+ if (stats.isFile() && extname(path).toLowerCase() === ".md") {
194
+ const root = getRoot()
195
+ const key = createHash("sha256").update(path).digest("hex").slice(0, 12)
196
+ const targetDir = join(root, key)
197
+ rmSync(targetDir, { recursive: true, force: true })
198
+ mkdirSync(targetDir, { recursive: true })
199
+
200
+ const stem = basename(path, ".md")
201
+ const skillDir = join(targetDir, stem)
202
+ mkdirSync(skillDir, { recursive: true })
203
+
204
+ const targetFile = join(skillDir, "SKILL.md")
205
+ const original = readFileSync(path, "utf-8")
206
+ const updated = sanitizeSkillMarkdown(original)
207
+ writeFileSync(targetFile, updated, "utf-8")
208
+
209
+ return skillDir
210
+ }
211
+ } catch {
212
+ // fall through to returning the original path
213
+ }
214
+
215
+ return path
216
+ }
217
+
218
+ function cleanup(): void {
219
+ if (cachedRoot !== undefined) {
220
+ try {
221
+ rmSync(cachedRoot, { recursive: true, force: true })
222
+ } catch {
223
+ // best-effort cleanup
224
+ }
225
+ cachedRoot = undefined
226
+ }
227
+ }
228
+
229
+ return { sanitizeDir, sanitizePath, cleanup }
230
+ }
231
+
232
+ function rewriteSkillFiles(dir: string): void {
233
+ let entries: import("node:fs").Dirent[]
234
+ try {
235
+ entries = readdirSync(dir, { withFileTypes: true })
236
+ } catch {
237
+ return
238
+ }
239
+
240
+ for (const entry of entries) {
241
+ const fullPath = join(dir, entry.name.toString())
242
+ if (entry.isDirectory()) {
243
+ rewriteSkillFiles(fullPath)
244
+ continue
245
+ }
246
+ const name = entry.name.toString()
247
+ if (entry.isFile() && name === "SKILL.md") {
248
+ try {
249
+ const original = readFileSync(fullPath, "utf-8")
250
+ const updated = sanitizeSkillMarkdown(original)
251
+ if (updated !== original) {
252
+ writeFileSync(fullPath, updated, "utf-8")
253
+ }
254
+ } catch {
255
+ // skip files we can't read/write
256
+ }
257
+ } else if (entry.isSymbolicLink() && name === "SKILL.md" && existsSync(fullPath)) {
258
+ try {
259
+ const original = readFileSync(fullPath, "utf-8")
260
+ const updated = sanitizeSkillMarkdown(original)
261
+ if (updated !== original) {
262
+ writeFileSync(fullPath, updated, "utf-8")
263
+ }
264
+ } catch {
265
+ // skip
266
+ }
267
+ }
268
+ }
269
+ }
@@ -190,7 +190,9 @@ describe("updateModelsConfig", () => {
190
190
  json: async () => ({ models: [KIMI] }),
191
191
  } as Response)
192
192
 
193
- await updateModelsConfig(modelsJsonPath, "test-key", { endpoint: "https://custom.ikie.example/v1/chat/completions" })
193
+ await updateModelsConfig(modelsJsonPath, "test-key", {
194
+ endpoint: "https://custom.ikie.example/v1/chat/completions",
195
+ })
194
196
 
195
197
  expect(fetch).toHaveBeenCalledWith(
196
198
  "https://custom.ikie.example/models",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes