@ikie-dev/cli 9.8.7 → 9.9.1

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 (303) 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/ferment/index.ts +2 -0
  255. package/src/extensions/ferment/tools/escalate.ts +70 -0
  256. package/src/extensions/orchestration/continuation-nudge.test.ts +9 -0
  257. package/src/extensions/orchestration/continuation-nudge.ts +8 -0
  258. package/src/extensions/orchestration/model-roles.test.ts +6 -1
  259. package/src/extensions/orchestration/model-roles.ts +6 -0
  260. package/src/extensions/prompt-construction/prompt-enrichment.test.ts +49 -3
  261. package/src/extensions/prompt-construction/prompt-enrichment.ts +4 -1
  262. package/src/extensions/skills-manager/skill-manager.test.ts +31 -14
  263. package/src/extensions/skills-manager/skill-manager.ts +14 -5
  264. package/src/extensions/skills-manager/skill-sanitizer.test.ts +287 -0
  265. package/src/extensions/skills-manager/skill-sanitizer.ts +269 -0
  266. package/src/models.test.ts +3 -1
  267. package/src/shared/planning/tool-catalog.test.ts +15 -8
  268. package/src/shared/planning/tool-catalog.ts +4 -3
  269. package/vendor/superpowers/hooks/run-hook.cmd +0 -0
  270. package/vendor/superpowers/hooks/session-start +0 -0
  271. package/vendor/superpowers/scripts/bump-version.sh +0 -0
  272. package/vendor/superpowers/scripts/sync-to-codex-plugin.sh +0 -0
  273. package/vendor/superpowers/skills/brainstorming/scripts/start-server.sh +0 -0
  274. package/vendor/superpowers/skills/brainstorming/scripts/stop-server.sh +0 -0
  275. package/vendor/superpowers/skills/systematic-debugging/find-polluter.sh +0 -0
  276. package/vendor/superpowers/skills/writing-skills/render-graphs.js +0 -0
  277. package/vendor/superpowers/tests/brainstorm-server/windows-lifecycle.test.sh +0 -0
  278. package/vendor/superpowers/tests/claude-code/analyze-token-usage.py +0 -0
  279. package/vendor/superpowers/tests/claude-code/run-skill-tests.sh +0 -0
  280. package/vendor/superpowers/tests/claude-code/test-document-review-system.sh +0 -0
  281. package/vendor/superpowers/tests/claude-code/test-helpers.sh +0 -0
  282. package/vendor/superpowers/tests/claude-code/test-requesting-code-review.sh +0 -0
  283. package/vendor/superpowers/tests/claude-code/test-subagent-driven-development-integration.sh +0 -0
  284. package/vendor/superpowers/tests/claude-code/test-subagent-driven-development.sh +0 -0
  285. package/vendor/superpowers/tests/claude-code/test-worktree-native-preference.sh +0 -0
  286. package/vendor/superpowers/tests/codex-plugin-sync/test-sync-to-codex-plugin.sh +0 -0
  287. package/vendor/superpowers/tests/explicit-skill-requests/run-all.sh +0 -0
  288. package/vendor/superpowers/tests/explicit-skill-requests/run-claude-describes-sdd.sh +0 -0
  289. package/vendor/superpowers/tests/explicit-skill-requests/run-extended-multiturn-test.sh +0 -0
  290. package/vendor/superpowers/tests/explicit-skill-requests/run-haiku-test.sh +0 -0
  291. package/vendor/superpowers/tests/explicit-skill-requests/run-multiturn-test.sh +0 -0
  292. package/vendor/superpowers/tests/explicit-skill-requests/run-test.sh +0 -0
  293. package/vendor/superpowers/tests/opencode/run-tests.sh +0 -0
  294. package/vendor/superpowers/tests/opencode/setup.sh +0 -0
  295. package/vendor/superpowers/tests/opencode/test-bootstrap-caching.sh +0 -0
  296. package/vendor/superpowers/tests/opencode/test-plugin-loading.sh +0 -0
  297. package/vendor/superpowers/tests/opencode/test-priority.sh +0 -0
  298. package/vendor/superpowers/tests/opencode/test-tools.sh +0 -0
  299. package/vendor/superpowers/tests/skill-triggering/run-all.sh +0 -0
  300. package/vendor/superpowers/tests/skill-triggering/run-test.sh +0 -0
  301. package/vendor/superpowers/tests/subagent-driven-dev/go-fractals/scaffold.sh +0 -0
  302. package/vendor/superpowers/tests/subagent-driven-dev/run-test.sh +0 -0
  303. package/vendor/superpowers/tests/subagent-driven-dev/svelte-todo/scaffold.sh +0 -0
@@ -3,6 +3,7 @@ import { arch, version as osVersion, platform, release, tmpdir } from "node:os"
3
3
  import { join } from "node:path"
4
4
  import type { AssistantMessage, ToolResultMessage } from "@earendil-works/pi-ai"
5
5
  import type { ExtensionAPI, ToolInfo } from "@earendil-works/pi-coding-agent"
6
+ import { loadSkillsFromDir } from "@earendil-works/pi-coding-agent"
6
7
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
7
8
  import * as config from "../../config.js"
8
9
  import type { ModelMetadata } from "../../models.js"
@@ -11,6 +12,7 @@ import * as startupContext from "../../startup-context.js"
11
12
  import * as agentWorkerContext from "../agent-worker-context.js"
12
13
  import { CLAUDE_CODE_SKILLS_RESOURCE_ID } from "../claude-code-skills/definition.js"
13
14
  import type { OrchestratorMessages } from "../orchestration/continuation-nudge.js"
15
+ import * as modelRoles from "../orchestration/model-roles.js"
14
16
  import promptEnrichmentExtension, {
15
17
  stripEmptyToolCalls,
16
18
  _resetDeprecatedNotificationTracking,
@@ -509,6 +511,46 @@ describe("prompt enrichment Claude Code skills", () => {
509
511
 
510
512
  expect(result.systemPrompt).not.toContain("<name>typescript-safety</name>")
511
513
  })
514
+
515
+ it("sanitizes project skill names and emits no name-validation diagnostics", async () => {
516
+ const cwd = join(dir, "project")
517
+ const skillDir = join(cwd, ".ikie", "skills", "ckm-slides")
518
+ writeRawSkill(
519
+ join(skillDir, "SKILL.md"),
520
+ `---
521
+ name: ckm:slides
522
+ description: Ikie project slides skill.
523
+ ---
524
+
525
+ # Slides
526
+
527
+ Slides skill body.
528
+ `,
529
+ )
530
+
531
+ const { createSkillSanitizer } = await import("../skills-manager/skill-sanitizer.js")
532
+ const sanitizer = createSkillSanitizer()
533
+ const sanitizedDir = sanitizer.sanitizePath(skillDir)
534
+ const directLoad = loadSkillsFromDir({ dir: sanitizedDir, source: "project" })
535
+ const nameDiagnostics = directLoad.diagnostics.filter(
536
+ (d) => d.type === "warning" && typeof d.message === "string" && d.message.toLowerCase().includes("name"),
537
+ )
538
+ expect(nameDiagnostics).toEqual([])
539
+ expect(directLoad.skills.map((s) => s.name)).toContain("ckm-slides")
540
+ sanitizer.cleanup()
541
+
542
+ const { beforeAgentStart } = buildPromptExtensionWithHandlers([])
543
+ if (!beforeAgentStart) throw new Error("before_agent_start handler was not registered")
544
+
545
+ const result = (await beforeAgentStart(
546
+ {},
547
+ { cwd, model: undefined, hasUI: false, sessionManager: { getSessionId: () => "session-1" } },
548
+ )) as { systemPrompt: string }
549
+
550
+ expect(result.systemPrompt).toContain("<available_skills>")
551
+ expect(result.systemPrompt).toContain("<name>ckm-slides</name>")
552
+ expect(result.systemPrompt).toContain("Ikie project slides skill.")
553
+ })
512
554
  })
513
555
 
514
556
  describe("append system prompt", () => {
@@ -646,9 +688,13 @@ describe("model role startup warnings", () => {
646
688
  expect(warn).not.toHaveBeenCalledWith(expect.stringContaining("[model-roles] Warning:"))
647
689
  })
648
690
 
649
- it("keeps unavailable role warnings when Ikie auth is already configured", () => {
691
+ it("keeps unavailable role warnings for explicit user role overrides when Ikie auth is already configured", () => {
650
692
  vi.spyOn(config, "loadConfig").mockReturnValue({ apiKey: "test-key" } as ReturnType<typeof config.loadConfig>)
651
693
  vi.spyOn(startupContext, "getAvailableModels").mockReturnValue([modelMetadata("different-model")])
694
+ vi.spyOn(modelRoles, "getModelRoles").mockReturnValue({
695
+ ...modelRoles.DEFAULT_MODEL_ROLES,
696
+ builder: "anthropic/claude-sonnet-4-5",
697
+ })
652
698
  const warn = vi.spyOn(console, "warn").mockImplementation(() => {})
653
699
  const pi = {
654
700
  registerFlag: () => {},
@@ -661,7 +707,7 @@ describe("model role startup warnings", () => {
661
707
 
662
708
  promptEnrichmentExtension([])(pi)
663
709
 
664
- expect(warn).toHaveBeenCalledWith(expect.stringContaining("[model-roles] Warning: orchestrator"))
710
+ expect(warn).toHaveBeenCalledWith(expect.stringContaining("[model-roles] Warning: builder"))
665
711
  })
666
712
  })
667
713
 
@@ -1054,7 +1100,7 @@ describe("continuation nudge turn_end handler", () => {
1054
1100
  // The handler should allow a second nudge since the model did not
1055
1101
  // intentionally stop.
1056
1102
  await fire("turn_end", {
1057
- message: makeAssistantWithStop([{ type: "text", text: "I was going to say..." }], "length"),
1103
+ message: makeAssistantWithStop([{ type: "text", text: "I was going to call the Agent tool." }], "length"),
1058
1104
  })
1059
1105
  expect(sendMessageCalls.length).toBe(2)
1060
1106
  })
@@ -63,6 +63,7 @@ import {
63
63
  splitModelRef,
64
64
  validateModelRoles,
65
65
  } from "../orchestration/model-roles.js"
66
+ import { createSkillSanitizer } from "../skills-manager/skill-sanitizer.js"
66
67
  import { getCurrentPhase } from "../tags.js"
67
68
  import { type ContextFile, loadGlobalContextFiles, loadProjectContextFiles } from "./context-files.js"
68
69
  import {
@@ -476,6 +477,8 @@ export default function (skillPaths: string[]) {
476
477
  const cachedUsername = safeUsername()
477
478
  const cachedHomeDir = homedir()
478
479
 
480
+ const skillSanitizer = createSkillSanitizer()
481
+
479
482
  let cachedContextFiles: ContextFile[] | undefined
480
483
  let cachedSkills: Skill[] | undefined
481
484
  let cachedGitRemote: string | undefined | null = null
@@ -505,7 +508,7 @@ export default function (skillPaths: string[]) {
505
508
  cachedSkills = loadSkills({
506
509
  cwd: ctx.cwd,
507
510
  agentDir: getAgentDir(),
508
- skillPaths: allSkillPaths,
511
+ skillPaths: allSkillPaths.map((p) => skillSanitizer.sanitizePath(p)),
509
512
  includeDefaults: false,
510
513
  }).skills
511
514
  }
@@ -15,36 +15,53 @@ import {
15
15
 
16
16
  describe("validateName", () => {
17
17
  const valid = (name: string) => expect(validateName(name)).toBeNull()
18
- const invalid = (name: string, contains: string) => {
19
- const result = validateName(name)
20
- expect(result).not.toBeNull()
21
- expect(result?.toLowerCase()).toContain(contains)
18
+ const invalid = (name: string) => {
19
+ expect(validateName(name)).not.toBeNull()
22
20
  }
23
21
 
24
22
  it("accepts valid names", () => {
23
+ valid("ckm-slides")
24
+ valid("ui-ux-pro-max")
25
25
  valid("systematic-debugging")
26
- valid("debug_v2")
27
- valid("foo.bar")
28
26
  valid("abc123")
29
27
  })
30
28
 
31
- it("rejects names starting with non-alphanumeric", () => {
32
- invalid("-foo", "start")
33
- invalid("_bar", "start")
29
+ it("rejects names with colons", () => {
30
+ invalid("ckm:slides")
31
+ })
32
+
33
+ it("rejects names with underscores", () => {
34
+ invalid("foo_bar")
35
+ invalid("debug_v2")
36
+ })
37
+
38
+ it("rejects names with dots", () => {
39
+ invalid("foo.bar")
40
+ })
41
+
42
+ it("rejects names with a leading hyphen", () => {
43
+ invalid("-bad")
44
+ })
45
+
46
+ it("rejects names with consecutive hyphens", () => {
47
+ invalid("bad--bad")
48
+ })
49
+
50
+ it("rejects names starting with an underscore", () => {
51
+ invalid("_bar")
34
52
  })
35
53
 
36
54
  it("rejects uppercase letters", () => {
37
- invalid("Foo", "lowercase")
38
- invalid("SYSTEM", "lowercase")
55
+ invalid("Foo")
56
+ invalid("SYSTEM")
39
57
  })
40
58
 
41
59
  it("rejects names longer than 64 chars", () => {
42
- invalid("a".repeat(65), "64")
60
+ invalid("a".repeat(65))
43
61
  })
44
62
 
45
63
  it("rejects names with spaces", () => {
46
- const result = validateName("foo bar")
47
- expect(result).not.toBeNull()
64
+ invalid("foo bar")
48
65
  })
49
66
  })
50
67
 
@@ -44,23 +44,32 @@ export interface SkillManageResult {
44
44
  path?: string
45
45
  }
46
46
 
47
- const NAME_RE = /^[a-z0-9][a-z0-9._-]*$/
47
+ // Agent Skills spec: only lowercase a-z, 0-9, hyphens; no leading/trailing
48
+ // hyphens; no consecutive hyphens; max 64 characters.
49
+ const SKILL_NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/
50
+ // Category directory names are filesystem-side only, so they allow a broader
51
+ // set of characters (dots, underscores) but still reject spaces and must
52
+ // start with a lowercase letter or digit.
53
+ const CATEGORY_RE = /^[a-z0-9][a-z0-9._-]*$/
48
54
  const MAX_NAME_LEN = 64
49
55
  const ALLOWED_SUBDIRS = new Set(["references", "templates", "scripts", "assets"])
50
56
 
51
57
  export function validateName(name: string): string | null {
52
58
  if (name.length === 0) return "Name cannot be empty."
53
59
  if (name.length > MAX_NAME_LEN) return `Name must be at most ${MAX_NAME_LEN} characters.`
54
- if (!NAME_RE.test(name)) {
55
- if (!/^[a-z0-9]/.test(name)) return "Name must start with a lowercase letter or digit."
56
- return "Name must contain only lowercase letters, digits, dots, underscores, or hyphens."
60
+ if (!SKILL_NAME_RE.test(name)) {
61
+ return "Name must contain only lowercase letters, digits, and hyphens, with no leading or trailing hyphens or consecutive hyphens."
57
62
  }
58
63
  return null
59
64
  }
60
65
 
61
66
  export function validateCategory(category?: string): string | null {
62
67
  if (!category) return null
63
- return validateName(category)
68
+ if (category.length > MAX_NAME_LEN) return `Category must be at most ${MAX_NAME_LEN} characters.`
69
+ if (!CATEGORY_RE.test(category)) {
70
+ return "Category must start with a lowercase letter or digit and contain only lowercase letters, digits, dots, underscores, or hyphens."
71
+ }
72
+ return null
64
73
  }
65
74
 
66
75
  /**
@@ -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
+ })