@nomadamas/k-skill 0.1.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 (350) hide show
  1. package/bin/k-skill.js +60 -0
  2. package/package.json +37 -0
  3. package/skills/assembly-bill-vote-search/instruction.md +111 -0
  4. package/skills/assembly-bill-vote-search/skill.json +10 -0
  5. package/skills/biz-health-check/instruction.md +69 -0
  6. package/skills/biz-health-check/scripts/biz_health_check.py +163 -0
  7. package/skills/biz-health-check/skill.json +10 -0
  8. package/skills/bok-ecos-stats/instruction.md +95 -0
  9. package/skills/bok-ecos-stats/scripts/bok_ecos.py +302 -0
  10. package/skills/bok-ecos-stats/skill.json +10 -0
  11. package/skills/building-register-search/instruction.md +69 -0
  12. package/skills/building-register-search/scripts/building_register.py +309 -0
  13. package/skills/building-register-search/scripts/building_register_xml.py +32 -0
  14. package/skills/building-register-search/skill.json +9 -0
  15. package/skills/bunjang-search/instruction.md +154 -0
  16. package/skills/bunjang-search/skill.json +10 -0
  17. package/skills/catchtable-sniper/instruction.md +270 -0
  18. package/skills/catchtable-sniper/skill.json +9 -0
  19. package/skills/cheap-gas-nearby/instruction.md +107 -0
  20. package/skills/cheap-gas-nearby/skill.json +10 -0
  21. package/skills/corporate-registration-consulting/instruction.md +117 -0
  22. package/skills/corporate-registration-consulting/scripts/fill_official_hwp.py +106 -0
  23. package/skills/corporate-registration-consulting/skill.json +8 -0
  24. package/skills/coupang-product-search/instruction.md +219 -0
  25. package/skills/coupang-product-search/scripts/coupang_partners_mcp.py +146 -0
  26. package/skills/coupang-product-search/skill.json +10 -0
  27. package/skills/court-auction-notice-search/instruction.md +201 -0
  28. package/skills/court-auction-notice-search/skill.json +9 -0
  29. package/skills/court-payment-order-assistant/instruction.md +120 -0
  30. package/skills/court-payment-order-assistant/skill.json +10 -0
  31. package/skills/d2b-notice-search/instruction.md +117 -0
  32. package/skills/d2b-notice-search/skill.json +10 -0
  33. package/skills/daangn-cars-search/instruction.md +91 -0
  34. package/skills/daangn-cars-search/scripts/daangn_cars.py +72 -0
  35. package/skills/daangn-cars-search/skill.json +9 -0
  36. package/skills/daangn-jobs-search/instruction.md +90 -0
  37. package/skills/daangn-jobs-search/scripts/daangn_jobs.py +98 -0
  38. package/skills/daangn-jobs-search/skill.json +9 -0
  39. package/skills/daangn-realty-search/instruction.md +114 -0
  40. package/skills/daangn-realty-search/scripts/daangn_detail_ld.py +76 -0
  41. package/skills/daangn-realty-search/scripts/daangn_realty.py +216 -0
  42. package/skills/daangn-realty-search/scripts/daangn_relay_store.py +174 -0
  43. package/skills/daangn-realty-search/skill.json +8 -0
  44. package/skills/daangn-used-goods-search/instruction.md +90 -0
  45. package/skills/daangn-used-goods-search/scripts/daangn_used_goods.py +80 -0
  46. package/skills/daangn-used-goods-search/skill.json +9 -0
  47. package/skills/daishin-report-search/instruction.md +138 -0
  48. package/skills/daishin-report-search/skill.json +10 -0
  49. package/skills/daiso-product-search/instruction.md +172 -0
  50. package/skills/daiso-product-search/skill.json +8 -0
  51. package/skills/danawa-price-search/instruction.md +183 -0
  52. package/skills/danawa-price-search/scripts/danawa_search.py +354 -0
  53. package/skills/danawa-price-search/skill.json +8 -0
  54. package/skills/delivery-tracking/instruction.md +358 -0
  55. package/skills/delivery-tracking/skill.json +8 -0
  56. package/skills/donation-place-search/instruction.md +129 -0
  57. package/skills/donation-place-search/skill.json +9 -0
  58. package/skills/emergency-room-beds/instruction.md +82 -0
  59. package/skills/emergency-room-beds/skill.json +8 -0
  60. package/skills/ev-charger-nearby/instruction.md +80 -0
  61. package/skills/ev-charger-nearby/scripts/ev_charger.py +222 -0
  62. package/skills/ev-charger-nearby/skill.json +10 -0
  63. package/skills/ev-subsidy-status/instruction.md +165 -0
  64. package/skills/ev-subsidy-status/skill.json +10 -0
  65. package/skills/express-bus-booking/instruction.md +207 -0
  66. package/skills/express-bus-booking/references/kobus-http-flow.md +159 -0
  67. package/skills/express-bus-booking/scripts/kobus_express_booking.py +243 -0
  68. package/skills/express-bus-booking/skill.json +8 -0
  69. package/skills/fine-dust-location/instruction.md +89 -0
  70. package/skills/fine-dust-location/skill.json +9 -0
  71. package/skills/flight-ticket-search/instruction.md +237 -0
  72. package/skills/flight-ticket-search/scripts/flight_ticket_search.py +501 -0
  73. package/skills/flight-ticket-search/skill.json +9 -0
  74. package/skills/foresttrip-vacancy/instruction.md +167 -0
  75. package/skills/foresttrip-vacancy/scripts/run_foresttrip_vacancy.py +549 -0
  76. package/skills/foresttrip-vacancy/skill.json +10 -0
  77. package/skills/fsc-corporate-info/instruction.md +57 -0
  78. package/skills/fsc-corporate-info/scripts/fsc_corporate_info.py +113 -0
  79. package/skills/fsc-corporate-info/skill.json +10 -0
  80. package/skills/g2b-order-plan-search/instruction.md +131 -0
  81. package/skills/g2b-order-plan-search/scripts/g2b_order_plan.py +158 -0
  82. package/skills/g2b-order-plan-search/skill.json +10 -0
  83. package/skills/g2b-sanctioned-supplier/instruction.md +61 -0
  84. package/skills/g2b-sanctioned-supplier/scripts/g2b_sanctioned_supplier.py +114 -0
  85. package/skills/g2b-sanctioned-supplier/skill.json +10 -0
  86. package/skills/gangnamunni-clinic-search/instruction.md +113 -0
  87. package/skills/gangnamunni-clinic-search/skill.json +9 -0
  88. package/skills/geeknews-search/instruction.md +69 -0
  89. package/skills/geeknews-search/scripts/geeknews_search.py +296 -0
  90. package/skills/geeknews-search/skill.json +8 -0
  91. package/skills/gongsijiga-search/instruction.md +128 -0
  92. package/skills/gongsijiga-search/skill.json +9 -0
  93. package/skills/gov-overseas-trip-report/instruction.md +488 -0
  94. package/skills/gov-overseas-trip-report/scripts/gov_overseas_trip_report.py +1206 -0
  95. package/skills/gov-overseas-trip-report/skill.json +9 -0
  96. package/skills/han-river-water-level/instruction.md +83 -0
  97. package/skills/han-river-water-level/skill.json +10 -0
  98. package/skills/highway-traffic-status/instruction.md +88 -0
  99. package/skills/highway-traffic-status/scripts/highway_traffic.py +315 -0
  100. package/skills/highway-traffic-status/skill.json +11 -0
  101. package/skills/hipass-receipt/instruction.md +97 -0
  102. package/skills/hipass-receipt/skill.json +10 -0
  103. package/skills/hola-poke-yeoksam/instruction.md +247 -0
  104. package/skills/hola-poke-yeoksam/skill.json +8 -0
  105. package/skills/household-waste-info/instruction.md +117 -0
  106. package/skills/household-waste-info/skill.json +10 -0
  107. package/skills/housing-official-price/instruction.md +177 -0
  108. package/skills/housing-official-price/skill.json +10 -0
  109. package/skills/hwp/instruction.md +206 -0
  110. package/skills/hwp/skill.json +8 -0
  111. package/skills/intercity-bus-booking/instruction.md +189 -0
  112. package/skills/intercity-bus-booking/references/tmoney-intercity-http-flow.md +126 -0
  113. package/skills/intercity-bus-booking/scripts/intercity_bus_search.py +381 -0
  114. package/skills/intercity-bus-booking/skill.json +8 -0
  115. package/skills/iros-registry-automation/instruction.md +229 -0
  116. package/skills/iros-registry-automation/scripts/iros_pdf_summary.py +249 -0
  117. package/skills/iros-registry-automation/scripts/upstream.pin +1 -0
  118. package/skills/iros-registry-automation/skill.json +9 -0
  119. package/skills/job-posting-match/instruction.md +130 -0
  120. package/skills/job-posting-match/scripts/job_posting_match.py +396 -0
  121. package/skills/job-posting-match/scripts/test_job_posting_match.py +54 -0
  122. package/skills/job-posting-match/skill.json +8 -0
  123. package/skills/jobkorea-talent-search/instruction.md +118 -0
  124. package/skills/jobkorea-talent-search/scripts/jobkorea_talent_models.py +27 -0
  125. package/skills/jobkorea-talent-search/scripts/jobkorea_talent_parse.py +186 -0
  126. package/skills/jobkorea-talent-search/scripts/jobkorea_talent_search.py +94 -0
  127. package/skills/jobkorea-talent-search/scripts/jobkorea_talent_search_condition.py +136 -0
  128. package/skills/jobkorea-talent-search/scripts/test_jobkorea_talent_search.py +76 -0
  129. package/skills/jobkorea-talent-search/skill.json +9 -0
  130. package/skills/joseon-sillok-search/instruction.md +76 -0
  131. package/skills/joseon-sillok-search/scripts/sillok_search.py +552 -0
  132. package/skills/joseon-sillok-search/skill.json +8 -0
  133. package/skills/k-dart/instruction.md +406 -0
  134. package/skills/k-dart/skill.json +8 -0
  135. package/skills/k-schoollunch-menu/instruction.md +109 -0
  136. package/skills/k-schoollunch-menu/skill.json +9 -0
  137. package/skills/k-skill-cleaner/instruction.md +80 -0
  138. package/skills/k-skill-cleaner/scripts/k_skill_cleaner.py +410 -0
  139. package/skills/k-skill-cleaner/skill.json +8 -0
  140. package/skills/k-skill-setup/instruction.md +253 -0
  141. package/skills/k-skill-setup/skill.json +11 -0
  142. package/skills/kakao-bar-nearby/instruction.md +76 -0
  143. package/skills/kakao-bar-nearby/skill.json +8 -0
  144. package/skills/kakao-map/instruction.md +176 -0
  145. package/skills/kakao-map/skill.json +10 -0
  146. package/skills/kakaotalk-mac/instruction.md +189 -0
  147. package/skills/kakaotalk-mac/skill.json +8 -0
  148. package/skills/kbl-results/instruction.md +89 -0
  149. package/skills/kbl-results/skill.json +9 -0
  150. package/skills/kbo-results/instruction.md +82 -0
  151. package/skills/kbo-results/skill.json +8 -0
  152. package/skills/keris-academic-search/instruction.md +81 -0
  153. package/skills/keris-academic-search/scripts/keris_academic.py +210 -0
  154. package/skills/keris-academic-search/skill.json +10 -0
  155. package/skills/kleague-results/instruction.md +92 -0
  156. package/skills/kleague-results/skill.json +8 -0
  157. package/skills/kopis-performance-search/instruction.md +109 -0
  158. package/skills/kopis-performance-search/skill.json +10 -0
  159. package/skills/korea-weather/instruction.md +93 -0
  160. package/skills/korea-weather/skill.json +10 -0
  161. package/skills/korean-character-count/instruction.md +87 -0
  162. package/skills/korean-character-count/scripts/korean_character_count.js +268 -0
  163. package/skills/korean-character-count/skill.json +8 -0
  164. package/skills/korean-cinema-search/instruction.md +177 -0
  165. package/skills/korean-cinema-search/skill.json +8 -0
  166. package/skills/korean-heritage-search/instruction.md +106 -0
  167. package/skills/korean-heritage-search/scripts/korean_heritage_search.py +321 -0
  168. package/skills/korean-heritage-search/skill.json +8 -0
  169. package/skills/korean-holiday-calendar/instruction.md +97 -0
  170. package/skills/korean-holiday-calendar/skill.json +9 -0
  171. package/skills/korean-humanizer/instruction.md +389 -0
  172. package/skills/korean-humanizer/references/ai-tell-taxonomy.md +147 -0
  173. package/skills/korean-humanizer/skill.json +8 -0
  174. package/skills/korean-jangbu-for/instruction.md +133 -0
  175. package/skills/korean-jangbu-for/scripts/install.sh +237 -0
  176. package/skills/korean-jangbu-for/scripts/upstream.pin +1 -0
  177. package/skills/korean-jangbu-for/skill.json +8 -0
  178. package/skills/korean-law-search/instruction.md +126 -0
  179. package/skills/korean-law-search/skill.json +10 -0
  180. package/skills/korean-marathon-schedule/instruction.md +111 -0
  181. package/skills/korean-marathon-schedule/skill.json +9 -0
  182. package/skills/korean-middle-korean/instruction.md +79 -0
  183. package/skills/korean-middle-korean/scripts/korean_middle_korean.js +214 -0
  184. package/skills/korean-middle-korean/skill.json +8 -0
  185. package/skills/korean-patent-search/instruction.md +79 -0
  186. package/skills/korean-patent-search/scripts/patent_search.py +409 -0
  187. package/skills/korean-patent-search/skill.json +9 -0
  188. package/skills/korean-privacy-terms/instruction.md +128 -0
  189. package/skills/korean-privacy-terms/scripts/install.sh +108 -0
  190. package/skills/korean-privacy-terms/scripts/upstream.pin +1 -0
  191. package/skills/korean-privacy-terms/skill.json +8 -0
  192. package/skills/korean-scholarship-search/instruction.md +317 -0
  193. package/skills/korean-scholarship-search/references/report-format.md +40 -0
  194. package/skills/korean-scholarship-search/references/school-discovery.md +61 -0
  195. package/skills/korean-scholarship-search/references/search-clues.md +58 -0
  196. package/skills/korean-scholarship-search/references/source-patterns.md +67 -0
  197. package/skills/korean-scholarship-search/scripts/scholarship_filter.py +811 -0
  198. package/skills/korean-scholarship-search/scripts/test_scholarship_filter.py +224 -0
  199. package/skills/korean-scholarship-search/scripts/university_search_plan.py +148 -0
  200. package/skills/korean-scholarship-search/skill.json +8 -0
  201. package/skills/korean-slang-writing/instruction.md +181 -0
  202. package/skills/korean-slang-writing/scripts/_slang_http.py +91 -0
  203. package/skills/korean-slang-writing/scripts/slang_lookup.py +291 -0
  204. package/skills/korean-slang-writing/scripts/slang_search.py +284 -0
  205. package/skills/korean-slang-writing/skill.json +8 -0
  206. package/skills/korean-spell-check/instruction.md +105 -0
  207. package/skills/korean-spell-check/scripts/korean_spell_check.py +523 -0
  208. package/skills/korean-spell-check/skill.json +9 -0
  209. package/skills/korean-stock-search/instruction.md +194 -0
  210. package/skills/korean-stock-search/skill.json +10 -0
  211. package/skills/korean-transit-route/instruction.md +113 -0
  212. package/skills/korean-transit-route/skill.json +10 -0
  213. package/skills/kosis-stats/instruction.md +232 -0
  214. package/skills/kosis-stats/references/kosis-openapi-guide.md +171 -0
  215. package/skills/kosis-stats/scripts/run_kosis_stats.py +896 -0
  216. package/skills/kosis-stats/skill.json +9 -0
  217. package/skills/kr-whois-lookup/instruction.md +107 -0
  218. package/skills/kr-whois-lookup/skill.json +10 -0
  219. package/skills/kstartup-search/instruction.md +186 -0
  220. package/skills/kstartup-search/scripts/run_kstartup.py +424 -0
  221. package/skills/kstartup-search/skill.json +11 -0
  222. package/skills/ktx-booking/instruction.md +244 -0
  223. package/skills/ktx-booking/skill.json +10 -0
  224. package/skills/lck-analytics/instruction.md +192 -0
  225. package/skills/lck-analytics/scripts/_lib.js +103 -0
  226. package/skills/lck-analytics/scripts/analyze-live-game.js +52 -0
  227. package/skills/lck-analytics/scripts/build-match-report.js +44 -0
  228. package/skills/lck-analytics/scripts/sync-oracle.js +50 -0
  229. package/skills/lck-analytics/skill.json +9 -0
  230. package/skills/lh-notice-search/instruction.md +206 -0
  231. package/skills/lh-notice-search/skill.json +10 -0
  232. package/skills/library-book-search/instruction.md +139 -0
  233. package/skills/library-book-search/skill.json +9 -0
  234. package/skills/local-election-candidate-search/instruction.md +77 -0
  235. package/skills/local-election-candidate-search/skill.json +8 -0
  236. package/skills/localdata-business-status/instruction.md +64 -0
  237. package/skills/localdata-business-status/scripts/localdata_business_status.py +206 -0
  238. package/skills/localdata-business-status/skill.json +8 -0
  239. package/skills/lotto-results/instruction.md +80 -0
  240. package/skills/lotto-results/skill.json +8 -0
  241. package/skills/lovebug-report/instruction.md +185 -0
  242. package/skills/lovebug-report/skill.json +9 -0
  243. package/skills/market-kurly-search/instruction.md +125 -0
  244. package/skills/market-kurly-search/skill.json +8 -0
  245. package/skills/mfds-drug-safety/instruction.md +86 -0
  246. package/skills/mfds-drug-safety/scripts/mfds_drug_safety.py +184 -0
  247. package/skills/mfds-drug-safety/skill.json +10 -0
  248. package/skills/mfds-food-safety/instruction.md +126 -0
  249. package/skills/mfds-food-safety/scripts/mfds_food_safety.py +281 -0
  250. package/skills/mfds-food-safety/skill.json +10 -0
  251. package/skills/myrealtrip-search/instruction.md +239 -0
  252. package/skills/myrealtrip-search/scripts/myrealtrip_mcp.py +194 -0
  253. package/skills/myrealtrip-search/scripts/test_myrealtrip_mcp.py +99 -0
  254. package/skills/myrealtrip-search/skill.json +9 -0
  255. package/skills/naming-house/instruction.md +146 -0
  256. package/skills/naming-house/skill.json +8 -0
  257. package/skills/national-pension-workplace/instruction.md +64 -0
  258. package/skills/national-pension-workplace/scripts/national_pension_workplace.py +113 -0
  259. package/skills/national-pension-workplace/skill.json +10 -0
  260. package/skills/naver-ad-performance/instruction.md +108 -0
  261. package/skills/naver-ad-performance/scripts/naver_ad_performance.py +240 -0
  262. package/skills/naver-ad-performance/skill.json +9 -0
  263. package/skills/naver-blog-research/instruction.md +128 -0
  264. package/skills/naver-blog-research/scripts/_naver_http.py +58 -0
  265. package/skills/naver-blog-research/scripts/naver_download_images.py +233 -0
  266. package/skills/naver-blog-research/scripts/naver_read.py +256 -0
  267. package/skills/naver-blog-research/scripts/naver_search.py +192 -0
  268. package/skills/naver-blog-research/skill.json +8 -0
  269. package/skills/naver-news-search/instruction.md +103 -0
  270. package/skills/naver-news-search/skill.json +10 -0
  271. package/skills/naver-shopping-search/instruction.md +94 -0
  272. package/skills/naver-shopping-search/skill.json +11 -0
  273. package/skills/nhis-care-checkup-search/instruction.md +116 -0
  274. package/skills/nhis-care-checkup-search/skill.json +10 -0
  275. package/skills/nts-business-registration/instruction.md +115 -0
  276. package/skills/nts-business-registration/scripts/nts_business_registration.py +215 -0
  277. package/skills/nts-business-registration/skill.json +10 -0
  278. package/skills/nts-tax-delinquency/instruction.md +54 -0
  279. package/skills/nts-tax-delinquency/scripts/nts_tax_delinquency.py +150 -0
  280. package/skills/nts-tax-delinquency/skill.json +8 -0
  281. package/skills/ohou-today-deal/instruction.md +182 -0
  282. package/skills/ohou-today-deal/scripts/ohou_today_deal.py +369 -0
  283. package/skills/ohou-today-deal/skill.json +9 -0
  284. package/skills/olive-young-search/instruction.md +154 -0
  285. package/skills/olive-young-search/skill.json +8 -0
  286. package/skills/parking-lot-search/instruction.md +96 -0
  287. package/skills/parking-lot-search/skill.json +9 -0
  288. package/skills/popbill/instruction.md +146 -0
  289. package/skills/popbill/scripts/popbill_cli.py +284 -0
  290. package/skills/popbill/scripts/popbill_registry.py +54 -0
  291. package/skills/popbill/scripts/popbill_safety.py +37 -0
  292. package/skills/popbill/scripts/popbill_templates.py +71 -0
  293. package/skills/popbill/skill.json +9 -0
  294. package/skills/public-restroom-nearby/instruction.md +89 -0
  295. package/skills/public-restroom-nearby/skill.json +9 -0
  296. package/skills/real-estate-search/instruction.md +172 -0
  297. package/skills/real-estate-search/skill.json +10 -0
  298. package/skills/rhwp-advanced/instruction.md +145 -0
  299. package/skills/rhwp-advanced/skill.json +8 -0
  300. package/skills/rhwp-edit/instruction.md +153 -0
  301. package/skills/rhwp-edit/skill.json +8 -0
  302. package/skills/s2b-notice-search/instruction.md +66 -0
  303. package/skills/s2b-notice-search/skill.json +9 -0
  304. package/skills/saju-fortune/instruction.md +177 -0
  305. package/skills/saju-fortune/skill.json +8 -0
  306. package/skills/saramin-talent-search/instruction.md +119 -0
  307. package/skills/saramin-talent-search/skill.json +9 -0
  308. package/skills/seoul-bike/instruction.md +83 -0
  309. package/skills/seoul-bike/scripts/seoul_bike.py +247 -0
  310. package/skills/seoul-bike/skill.json +10 -0
  311. package/skills/seoul-density/instruction.md +109 -0
  312. package/skills/seoul-density/scripts/seoul_density.py +271 -0
  313. package/skills/seoul-density/skill.json +10 -0
  314. package/skills/seoul-subway-arrival/instruction.md +85 -0
  315. package/skills/seoul-subway-arrival/skill.json +9 -0
  316. package/skills/sh-notice-search/instruction.md +150 -0
  317. package/skills/sh-notice-search/skill.json +9 -0
  318. package/skills/srt-booking/instruction.md +181 -0
  319. package/skills/srt-booking/scripts/srt_booking.py +272 -0
  320. package/skills/srt-booking/scripts/srt_seats.py +156 -0
  321. package/skills/srt-booking/skill.json +20 -0
  322. package/skills/subway-lost-property/instruction.md +93 -0
  323. package/skills/subway-lost-property/scripts/subway_lost_property.py +244 -0
  324. package/skills/subway-lost-property/skill.json +8 -0
  325. package/skills/ticket-availability/instruction.md +175 -0
  326. package/skills/ticket-availability/scripts/ticket_availability.py +430 -0
  327. package/skills/ticket-availability/skill.json +9 -0
  328. package/skills/toss-securities/instruction.md +116 -0
  329. package/skills/toss-securities/skill.json +9 -0
  330. package/skills/used-car-price-search/instruction.md +109 -0
  331. package/skills/used-car-price-search/skill.json +8 -0
  332. package/skills/yebigun-training/instruction.md +177 -0
  333. package/skills/yebigun-training/skill.json +9 -0
  334. package/skills/zipcode-search/instruction.md +159 -0
  335. package/skills/zipcode-search/scripts/zipcode_search.py +150 -0
  336. package/skills/zipcode-search/skill.json +8 -0
  337. package/src/assemble.js +134 -0
  338. package/src/detect.js +18 -0
  339. package/templates/action-account.md +4 -0
  340. package/templates/action-booking.md +5 -0
  341. package/templates/action-commerce.md +5 -0
  342. package/templates/action-communication.md +5 -0
  343. package/templates/action-submission.md +5 -0
  344. package/templates/browser.md +4 -0
  345. package/templates/core.md +5 -0
  346. package/templates/hard-boundary.md +3 -0
  347. package/templates/local.md +2 -0
  348. package/templates/lookup.md +2 -0
  349. package/templates/proxy.md +2 -0
  350. package/templates/vault.md +4 -0
@@ -0,0 +1,410 @@
1
+ #!/usr/bin/env python3
2
+ """Utilities for the k-skill-cleaner skill.
3
+
4
+ The helper intentionally stays dependency-free: it scans root-level skill
5
+ folders, best-effort local agent logs, and optional interview choices to produce
6
+ a conservative cleanup shortlist. It never deletes files by itself.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import json
13
+ import os
14
+ import re
15
+ from collections.abc import Iterable, Mapping
16
+ from datetime import datetime, timedelta, timezone
17
+ from pathlib import Path
18
+ from typing import Any
19
+
20
+ EXCLUDED_ROOT_DIRS = {
21
+ ".changeset",
22
+ ".claude",
23
+ ".codex",
24
+ ".cursor",
25
+ ".git",
26
+ ".github",
27
+ ".omx",
28
+ ".ouroboros",
29
+ ".vscode",
30
+ "docs",
31
+ "examples",
32
+ "node_modules",
33
+ "packages",
34
+ "python-packages",
35
+ "scripts",
36
+ }
37
+
38
+ AGENT_USAGE_SOURCES = [
39
+ {
40
+ "agent": "Claude Code",
41
+ "paths": ["~/.claude/projects/**/*.jsonl", "~/.claude/transcripts/**/*.jsonl"],
42
+ "method": "Scan JSONL transcript lines for skill-trigger events, $skill mentions, and SKILL.md load markers.",
43
+ "confidence": "best-effort",
44
+ },
45
+ {
46
+ "agent": "Codex",
47
+ "paths": ["~/.codex/sessions/**/*.jsonl", "~/.codex/log/**/*.log", ".omx/logs/**/*.log"],
48
+ "method": "Scan Codex session/log lines for routed skill names, $skill invocations, and SKILL.md reads.",
49
+ "confidence": "best-effort",
50
+ },
51
+ {
52
+ "agent": "OpenCode",
53
+ "paths": ["~/.local/share/opencode/**/*.jsonl", "~/.config/opencode/**/*.jsonl"],
54
+ "method": "Scan OpenCode data/config logs when available; ask for an exported transcript otherwise.",
55
+ "confidence": "best-effort",
56
+ },
57
+ {
58
+ "agent": "OpenClaw/ClawHub",
59
+ "paths": ["~/.openclaw/**/*.jsonl", "~/.clawhub/**/*.jsonl"],
60
+ "method": "No stable public trigger-count schema is assumed; use local logs if present or imported JSON counts.",
61
+ "confidence": "manual-confirm",
62
+ "fallback": "Ask the user to export trigger stats or provide a usage JSON file.",
63
+ },
64
+ {
65
+ "agent": "Hermes Agent",
66
+ "paths": ["~/.hermes/**/*.jsonl", "~/.config/hermes/**/*.jsonl"],
67
+ "method": "No stable public trigger-count schema is assumed; use local logs if present or imported JSON counts.",
68
+ "confidence": "manual-confirm",
69
+ "fallback": "Ask the user to export trigger stats or provide a usage JSON file.",
70
+ },
71
+ ]
72
+
73
+
74
+ def resolve_skills_root(root: Path | str) -> Path:
75
+ """Resolve the directory that contains installable skill directories.
76
+
77
+ Standalone installs tell users to run this helper from inside the
78
+ ``k-skill-cleaner`` directory with ``--skills-root .``. In that layout, the
79
+ current directory is itself a skill, while sibling skill directories live in
80
+ the parent directory. Treat that self-skill root as shorthand for its parent
81
+ so the advertised standalone command scans the installed skill bundle.
82
+ """
83
+
84
+ root_path = Path(root).expanduser().resolve()
85
+ if (root_path / "SKILL.md").is_file():
86
+ parent = root_path.parent
87
+ if any(
88
+ child.is_dir()
89
+ and child.name not in EXCLUDED_ROOT_DIRS
90
+ and (child / "SKILL.md").is_file()
91
+ for child in parent.iterdir()
92
+ ):
93
+ return parent
94
+ return root_path
95
+
96
+
97
+ def find_skill_dirs(root: Path | str) -> list[str]:
98
+ """Return root-level directories that look like installable skills."""
99
+
100
+ root_path = resolve_skills_root(root)
101
+ skills: list[str] = []
102
+ for child in root_path.iterdir():
103
+ if not child.is_dir() or child.name in EXCLUDED_ROOT_DIRS:
104
+ continue
105
+ if (child / "SKILL.md").is_file():
106
+ skills.append(child.name)
107
+ return sorted(skills)
108
+
109
+
110
+ def _walk_strings(value: Any, key_hint: str | None = None) -> Iterable[tuple[str | None, str]]:
111
+ if isinstance(value, str):
112
+ yield key_hint, value
113
+ elif isinstance(value, Mapping):
114
+ for key, child in value.items():
115
+ yield from _walk_strings(child, str(key))
116
+ elif isinstance(value, list):
117
+ for child in value:
118
+ yield from _walk_strings(child, key_hint)
119
+
120
+
121
+ def _line_mentions_skill(line: str, skill: str) -> bool:
122
+ escaped = re.escape(skill)
123
+ patterns = [
124
+ rf"(?<![\w-])\${escaped}(?![\w-])",
125
+ rf"(?i)\bskill(?:[_ -]?name|[_ -]?id)?\s*[:=]\s*['\"]?{escaped}(?![\w-])",
126
+ rf"(?<![\w-]){escaped}/SKILL\.md\b",
127
+ rf"(?i)\bloaded skill\s*[:=]?\s*['\"]?{escaped}(?![\w-])",
128
+ rf"(?i)\busing\s+\${escaped}(?![\w-])",
129
+ ]
130
+ return any(re.search(pattern, line) for pattern in patterns)
131
+
132
+
133
+ def _json_mentions_skill(record: Any, skill: str) -> bool:
134
+ key_names = {"skill", "skillname", "skill_name", "skillid", "skill_id", "name"}
135
+ for key, value in _walk_strings(record):
136
+ normalized_key = (key or "").replace("-", "").replace("_", "").lower()
137
+ if normalized_key in key_names and value == skill:
138
+ return True
139
+ if _line_mentions_skill(value, skill):
140
+ return True
141
+ return False
142
+
143
+
144
+ def _parse_datetime(value: str | datetime | None) -> datetime | None:
145
+ if value is None or isinstance(value, datetime):
146
+ parsed = value
147
+ else:
148
+ raw = value.strip()
149
+ if not raw:
150
+ return None
151
+ if raw.endswith("Z"):
152
+ raw = f"{raw[:-1]}+00:00"
153
+ try:
154
+ parsed = datetime.fromisoformat(raw)
155
+ except ValueError:
156
+ try:
157
+ parsed = datetime.fromisoformat(f"{raw}T00:00:00")
158
+ except ValueError as exc:
159
+ raise ValueError("since must be an ISO date or datetime") from exc
160
+ if parsed is None:
161
+ return None
162
+ if parsed.tzinfo is None:
163
+ return parsed.replace(tzinfo=timezone.utc)
164
+ return parsed.astimezone(timezone.utc)
165
+
166
+
167
+ def _line_datetime_from_json(record: Any) -> datetime | None:
168
+ timestamp_keys = {"timestamp", "time", "created_at", "createdat", "date", "datetime", "ts"}
169
+ if not isinstance(record, Mapping):
170
+ return None
171
+ for key, value in record.items():
172
+ normalized_key = str(key).replace("-", "").replace("_", "").lower()
173
+ if normalized_key in timestamp_keys and isinstance(value, str):
174
+ try:
175
+ return _parse_datetime(value)
176
+ except ValueError:
177
+ return None
178
+ return None
179
+
180
+
181
+ def _line_datetime_from_text(line: str) -> datetime | None:
182
+ match = re.search(r"\b\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)?\b", line)
183
+ if not match:
184
+ return None
185
+ raw = match.group(0)
186
+ if "T" not in raw and " " not in raw:
187
+ raw = f"{raw}T00:00:00"
188
+ if re.search(r"[+-]\d{4}$", raw):
189
+ raw = f"{raw[:-2]}:{raw[-2:]}"
190
+ try:
191
+ return _parse_datetime(raw.replace(" ", "T", 1))
192
+ except ValueError:
193
+ return None
194
+
195
+
196
+ def _mtime_datetime(path: Path) -> datetime:
197
+ return datetime.fromtimestamp(path.stat().st_mtime, tz=timezone.utc)
198
+
199
+
200
+ def _line_is_in_window(path: Path, line: str, parsed: Any | None, since: datetime | None) -> bool:
201
+ if since is None:
202
+ return True
203
+ line_dt = _line_datetime_from_json(parsed) if parsed is not None else None
204
+ if line_dt is None:
205
+ line_dt = _line_datetime_from_text(line)
206
+ if line_dt is None:
207
+ line_dt = _mtime_datetime(path)
208
+ return line_dt >= since
209
+
210
+
211
+ def collect_skill_usage(
212
+ log_paths: Iterable[Path | str],
213
+ skill_names: Iterable[str],
214
+ since: str | datetime | None = None,
215
+ ) -> dict[str, int]:
216
+ """Best-effort count of skill trigger mentions across local agent logs.
217
+
218
+ When ``since`` is provided, timestamped records older than the cutoff are
219
+ skipped. Lines without parseable timestamps fall back to the log file mtime,
220
+ which keeps the selected interview window enforceable even for mixed log
221
+ formats.
222
+ """
223
+
224
+ since_dt = _parse_datetime(since)
225
+ skills = sorted(set(skill_names))
226
+ counts = {skill: 0 for skill in skills}
227
+ for raw_path in log_paths:
228
+ path = Path(raw_path).expanduser()
229
+ if not path.is_file():
230
+ continue
231
+ try:
232
+ with path.open(encoding="utf-8", errors="replace") as handle:
233
+ for line in handle:
234
+ parsed: Any | None = None
235
+ try:
236
+ parsed = json.loads(line)
237
+ except json.JSONDecodeError:
238
+ parsed = None
239
+ if not _line_is_in_window(path, line, parsed, since_dt):
240
+ continue
241
+ for skill in skills:
242
+ if (parsed is not None and _json_mentions_skill(parsed, skill)) or _line_mentions_skill(line, skill):
243
+ counts[skill] += 1
244
+ except OSError:
245
+ continue
246
+ return counts
247
+
248
+
249
+ def load_usage_json(path: Path | str | None) -> dict[str, int]:
250
+ if path is None:
251
+ return {}
252
+ data = json.loads(Path(path).read_text(encoding="utf-8"))
253
+ if not isinstance(data, Mapping):
254
+ raise ValueError("usage JSON must be an object mapping skill names to counts")
255
+ counts: dict[str, int] = {}
256
+ for key, value in data.items():
257
+ try:
258
+ counts[str(key)] = int(value)
259
+ except (TypeError, ValueError) as exc:
260
+ raise ValueError(f"usage count for {key!r} must be an integer") from exc
261
+ return counts
262
+
263
+
264
+ def rank_cleanup_candidates(
265
+ skill_names: Iterable[str],
266
+ usage_counts: Mapping[str, int] | None = None,
267
+ never_use: Iterable[str] | None = None,
268
+ keep: Iterable[str] | None = None,
269
+ low_usage_threshold: int = 1,
270
+ ) -> list[dict[str, Any]]:
271
+ """Rank deletion/review candidates without touching the filesystem."""
272
+
273
+ counts = usage_counts or {}
274
+ never = set(never_use or [])
275
+ protected = set(keep or [])
276
+ candidates: list[dict[str, Any]] = []
277
+
278
+ for skill in sorted(set(skill_names)):
279
+ if skill in protected:
280
+ continue
281
+ count = int(counts.get(skill, 0))
282
+ reasons: list[str] = []
283
+ score = 0
284
+ action = "keep"
285
+
286
+ if skill in never:
287
+ reasons.append("interview_never_use")
288
+ score += 100
289
+ action = "remove"
290
+ if count == 0:
291
+ reasons.append("zero_triggers")
292
+ score += 50
293
+ elif count <= low_usage_threshold:
294
+ reasons.append("low_usage")
295
+ score += 20
296
+ if not reasons:
297
+ continue
298
+ if action != "remove":
299
+ action = "review"
300
+
301
+ candidates.append(
302
+ {
303
+ "skill": skill,
304
+ "action": action,
305
+ "trigger_count": count,
306
+ "score": score,
307
+ "reasons": reasons,
308
+ }
309
+ )
310
+
311
+ return sorted(candidates, key=lambda item: (-item["score"], item["skill"]))
312
+
313
+
314
+ def expand_default_log_paths() -> list[Path]:
315
+ paths: list[Path] = []
316
+ for source in AGENT_USAGE_SOURCES:
317
+ for pattern in source.get("paths", []):
318
+ paths.extend(Path().glob(os.path.expanduser(pattern)) if not pattern.startswith("~") else Path.home().glob(pattern[2:]))
319
+ return sorted({path for path in paths if path.is_file()})
320
+
321
+
322
+ def parse_csv(value: str | None) -> set[str]:
323
+ if not value:
324
+ return set()
325
+ return {item.strip() for item in value.split(",") if item.strip()}
326
+
327
+
328
+ def _resolve_since(days: int | None, since: str | None, now: datetime | None = None) -> datetime | None:
329
+ explicit_since = _parse_datetime(since)
330
+ if explicit_since is not None:
331
+ return explicit_since
332
+ if days is None:
333
+ return None
334
+ if days < 0:
335
+ raise ValueError("days must be zero or greater")
336
+ base = now or datetime.now(timezone.utc)
337
+ if base.tzinfo is None:
338
+ base = base.replace(tzinfo=timezone.utc)
339
+ else:
340
+ base = base.astimezone(timezone.utc)
341
+ return base - timedelta(days=days)
342
+
343
+
344
+ def build_parser() -> argparse.ArgumentParser:
345
+ parser = argparse.ArgumentParser(description="Suggest K-skill cleanup candidates from interviews and usage logs.")
346
+ parser.add_argument(
347
+ "--skills-root",
348
+ default=".",
349
+ help="Directory containing root-level skills; a skill directory with SKILL.md auto-scans its parent",
350
+ )
351
+ parser.add_argument("--usage-json", help="Optional JSON object mapping skill names to trigger counts")
352
+ parser.add_argument("--log", action="append", default=[], help="Agent log file to scan; repeatable")
353
+ parser.add_argument("--scan-default-logs", action="store_true", help="Best-effort scan known local agent log locations")
354
+ parser.add_argument("--never-use", default="", help="Comma-separated skills the user says they never use")
355
+ parser.add_argument("--keep", default="", help="Comma-separated skills to protect from suggestions")
356
+ parser.add_argument("--low-usage-threshold", type=int, default=1, help="Counts at or below this threshold are review candidates")
357
+ parser.add_argument("--days", type=int, help="Only count log records from the last N days; untimestamped lines use file mtime fallback")
358
+ parser.add_argument("--since", help="Only count log records on or after this ISO date/datetime; overrides --days")
359
+ return parser
360
+
361
+
362
+ def main(argv: list[str] | None = None) -> int:
363
+ args = build_parser().parse_args(argv)
364
+ skill_names = find_skill_dirs(args.skills_root)
365
+ usage_counts = {skill: 0 for skill in skill_names}
366
+ usage_counts.update(load_usage_json(args.usage_json))
367
+
368
+ log_paths = [Path(path) for path in args.log]
369
+ if args.scan_default_logs:
370
+ log_paths.extend(expand_default_log_paths())
371
+ since = _resolve_since(args.days, args.since)
372
+ scanned_log_paths = sorted({str(path.expanduser()) for path in log_paths if path.expanduser().is_file()})
373
+ log_counts = collect_skill_usage(log_paths, skill_names, since=since)
374
+ for skill, count in log_counts.items():
375
+ usage_counts[skill] = usage_counts.get(skill, 0) + count
376
+
377
+ report = {
378
+ "skill_count": len(skill_names),
379
+ "candidates": rank_cleanup_candidates(
380
+ skill_names=skill_names,
381
+ usage_counts=usage_counts,
382
+ never_use=parse_csv(args.never_use),
383
+ keep=parse_csv(args.keep),
384
+ low_usage_threshold=args.low_usage_threshold,
385
+ ),
386
+ "agent_usage_sources": AGENT_USAGE_SOURCES,
387
+ "time_window": {
388
+ "since": since.isoformat() if since is not None else None,
389
+ "days": args.days if args.since is None else None,
390
+ "scope": "Applies to scanned logs only; usage JSON counts are merged as already aggregated/pre-windowed input.",
391
+ "fallback": "Untimestamped log lines are included or skipped by log file mtime.",
392
+ },
393
+ "usage_json": {
394
+ "applied": args.usage_json is not None,
395
+ "path": args.usage_json,
396
+ "caveat": "Usage JSON counts are treated as already aggregated/pre-windowed and are not filtered by --days or --since.",
397
+ },
398
+ "scanned_logs": {
399
+ "count": len(scanned_log_paths),
400
+ "paths": scanned_log_paths,
401
+ "caveat": "Unreadable log files are skipped; trigger detection is best-effort.",
402
+ },
403
+ "safety": "No files were deleted. Review candidates and remove skills in a separate explicit edit.",
404
+ }
405
+ print(json.dumps(report, ensure_ascii=False, indent=2))
406
+ return 0
407
+
408
+
409
+ if __name__ == "__main__":
410
+ raise SystemExit(main())
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "k-skill-cleaner",
3
+ "description": "Interview the user and inspect coding-agent skill trigger counts to recommend unused K-skills for removal.",
4
+ "profiles": [
5
+ "local"
6
+ ],
7
+ "frontmatter": "name: k-skill-cleaner\ndescription: Interview the user and inspect coding-agent skill trigger counts to recommend unused K-skills for removal."
8
+ }
@@ -0,0 +1,253 @@
1
+ # k-skill Setup
2
+
3
+ ## Purpose
4
+
5
+ 전체 `k-skill` 설치가 끝난 뒤, 공통 후속 작업을 처리한다.
6
+
7
+ - credential 확보 (에이전트 vault 또는 기본 secrets.env)
8
+ - 런타임 환경변수 확인
9
+ - 선택 사항: 주기적인 업데이트 확인 자동화
10
+ - 선택 사항: GitHub star 여부 확인 및 동의 시 실행
11
+
12
+ 이 스킬의 기본 정책:
13
+
14
+ - 시크릿이 없으면 필요한 값 이름을 사용자에게 정확히 알려준다
15
+ - credential resolution order에 따라 확보한다
16
+ - 필요한 패키지가 없으면 대체 구현을 찾기보다 전역 설치를 먼저 시도한다
17
+ - `cron`, `launchd`, `schtasks`, `gh` 같은 지속성/외부 상태 변경은 자동으로 하지 말고 먼저 사용자 동의를 받는다
18
+ - GitHub star는 사용자가 명시적으로 동의했을 때만 실행한다
19
+
20
+ ## Dolshoi-first runtime policy
21
+
22
+ 돌쇠는 credential, browser, action을 서로 독립적인 capability로 감지한다.
23
+
24
+ 1. `DOLSHOI_ACTION_BROKER_URL`과 실행 가능한 `vault-run`이 모두 있으면 credential action mode다.
25
+ 2. 필요한 login capability가 이미 provision되어 있으면 `vault-run <capability_id> <action> [args...]`를 사용한다.
26
+ 3. capability가 없으면 username/password/key를 채팅으로 묻지 말고 `request_vault_credential` tool로 앱 vault 입력 UI를 호출한다.
27
+ 4. 내장 browser tool이 CloakBrowser를 제공하거나 `CLOAKBROWSER_PEEK_TOKEN`이 있으면 browser action은 CloakBrowser를 우선한다.
28
+ 5. 조회 결과가 실제 action의 입력이면 공식 표면에서 장바구니, 예약, 신청서 작성, 연락 초안 등 가역적 단계를 이어서 수행한다.
29
+ 6. 결제, 실제 메시지/메일 전송, 최종 제출, 취소 같은 비가역 효과 직전에만 `clarify`로 대상·금액/payload·효과를 승인받고, 승인되면 같은 turn에 실행한다.
30
+
31
+ ## Generic credential resolution order
32
+
33
+ 돌쇠 credential mode가 아닐 때만 아래 portable fallback을 따른다.
34
+
35
+ 1. **이미 환경변수에 있으면** 그대로 사용한다.
36
+ 2. **에이전트가 자체 secret vault(1Password CLI, Bitwarden CLI, macOS Keychain 등)를 사용 중이면** 거기서 꺼내 환경변수로 주입해도 된다.
37
+ 3. **`~/.config/k-skill/secrets.env`** (기본 fallback) — plain dotenv 파일, 퍼미션 `0600`.
38
+ 4. **아무것도 없으면** 유저에게 물어서 2 또는 3에 저장한다.
39
+
40
+ 기본 경로에 저장하는 것은 fallback일 뿐, 강제가 아니다.
41
+
42
+ ## Standard file location
43
+
44
+ - secrets file (기본 fallback): `~/.config/k-skill/secrets.env`
45
+
46
+ ## Install
47
+
48
+ 이 스킬은 `k-skill` 전체 스킬 설치가 끝난 뒤 실행하는 것을 기본으로 한다.
49
+
50
+ 예:
51
+
52
+ ```bash
53
+ npx --yes skills add <owner/repo> --all -g
54
+ ```
55
+
56
+ 설치가 끝나면 이 스킬을 호출해 아래 setup 단계를 이어간다.
57
+
58
+ ## Setup steps
59
+
60
+ ### 1. Create the default secrets file (generic fallback only)
61
+
62
+ 돌쇠 credential mode나 다른 host vault를 쓰지 않는 경우에만 기본 fallback 파일을 만든다.
63
+
64
+ ```bash
65
+ mkdir -p ~/.config/k-skill
66
+ cat > ~/.config/k-skill/secrets.env <<'EOF'
67
+ KSKILL_SRT_ID=replace-me
68
+ KSKILL_SRT_PASSWORD=replace-me
69
+ KSKILL_KTX_ID=replace-me
70
+ KSKILL_KTX_PASSWORD=replace-me
71
+ KSKILL_FORESTTRIP_ID=replace-me
72
+ KSKILL_FORESTTRIP_PASSWORD=replace-me
73
+ KSKILL_EV_CHARGER_API_KEY=replace-me
74
+ KSKILL_BUILDING_REGISTER_API_KEY=replace-me
75
+ KSKILL_RISS_API_KEY=replace-me
76
+ LAW_OC=replace-me
77
+ KIPRIS_PLUS_API_KEY=replace-me
78
+ AIR_KOREA_OPEN_API_KEY=replace-me
79
+ KSKILL_PROXY_BASE_URL=
80
+ EOF
81
+ chmod 0600 ~/.config/k-skill/secrets.env
82
+ ```
83
+
84
+ 호스트가 제공하는 가장 안전한 입력 표면으로 실제 값을 받아 채운다. 돌쇠에서는 이 파일을 만들거나 평문 값을 묻지 않는다.
85
+
86
+ 서울 지하철 도착정보, 서울 실시간 혼잡도 조회, 서울 따릉이 실시간 대여소 조회, 한국 날씨, 미세먼지, 한강 수위, 주유소 가격, 생활쓰레기 배출정보 조회, 학교 급식 식단 조회, 의약품 안전 체크, 식품 안전 체크는 `KSKILL_PROXY_BASE_URL` 을 비워 두면 기본 hosted path(`k-skill-proxy.nomadamas.org`)를 그대로 쓴다. 전기차 충전소와 건축물대장 표제부 조회도 같은 기본 hosted path를 쓴다. 별도 self-host proxy를 쓸 때만 `KSKILL_PROXY_BASE_URL` 을 채운다.
87
+
88
+ 한국 법령 검색은 기본 hosted proxy(`k-skill-proxy.nomadamas.org`)의 `/v1/korean-law/...` endpoint를 경유하므로 사용자 쪽 `LAW_OC` 가 불필요하다. self-host proxy 운영자만 서버 환경변수 `LAW_OC` 를 채운다(무료 발급: `https://open.law.go.kr`).
89
+
90
+ 한국 부동산 실거래가 조회는 기본 hosted proxy(`k-skill-proxy.nomadamas.org`)를 경유하므로 사용자 쪽 `DATA_GO_KR_API_KEY` 가 불필요하다.
91
+
92
+ 한국 주식 정보 조회는 기본 hosted proxy(`k-skill-proxy.nomadamas.org`)를 경유하므로 사용자 쪽 `KRX_API_KEY` 가 불필요하다. self-host proxy 운영자만 서버 환경변수 `KRX_API_KEY` 를 사용한다.
93
+
94
+ 도서관 도서 조회는 기본 hosted proxy(`k-skill-proxy.nomadamas.org`)를 경유하므로 사용자 쪽 `DATA4LIBRARY_AUTH_KEY` 가 불필요하다. self-host proxy 운영자만 서버 환경변수 `DATA4LIBRARY_AUTH_KEY` 를 사용한다.
95
+
96
+ 생활쓰레기 배출정보 조회는 `k-skill-proxy`의 `/v1/household-waste/info` 라우트를 호출하고, `serviceKey`(`DATA_GO_KR_API_KEY`)는 proxy 서버에서 주입/관리하므로 사용자 쪽 `DATA_GO_KR_API_KEY` 가 불필요하다.
97
+
98
+ 학교 급식 식단 조회는 `k-skill-proxy`의 `/v1/neis/school-search`·`/v1/neis/school-meal`을 호출하고, `KEDU_INFO_KEY`는 프록시 서버에만 두므로 사용자 쪽에 둘 필요가 없다.
99
+
100
+ 도서관 도서 조회는 `k-skill-proxy`의 `/v1/data4library/*` 라우트를 호출하고, `DATA4LIBRARY_AUTH_KEY`는 프록시 서버에만 두므로 사용자 쪽에 둘 필요가 없다.
101
+
102
+ 근처 가장 싼 주유소 찾기는 기본 hosted proxy를 경유하므로 사용자 쪽 `OPINET_API_KEY` 가 불필요하다.
103
+
104
+ 의약품 안전 체크는 `k-skill-proxy`의 `/v1/mfds/drug-safety/lookup` 라우트를 호출하고, `DATA_GO_KR_API_KEY` 는 프록시 서버에서만 주입/관리하므로 사용자 쪽에 둘 필요가 없다.
105
+
106
+ 식품 안전 체크는 `k-skill-proxy`의 `/v1/mfds/food-safety/search` 라우트를 호출하고, `DATA_GO_KR_API_KEY` 및 선택적 `FOODSAFETYKOREA_API_KEY` 는 프록시 서버에서만 주입/관리하므로 사용자 쪽에 둘 필요가 없다.
107
+
108
+ 창업진흥원 K-Startup 조회는 `k-skill-proxy`의 `/v1/kstartup/*` 라우트를 호출하고, `ServiceKey`(`DATA_GO_KR_API_KEY`)는 프록시 서버에서만 주입/관리하므로 일반 조회는 사용자 쪽에 키가 필요 없다. `--direct` 호출을 쓸 때만 `KSKILL_KSTARTUP_API_KEY` 를 채운다.
109
+
110
+ 전기차 충전소 조회는 `k-skill-proxy`의 `/v1/ev-charger/info`·`/v1/ev-charger/status`를 호출하므로 일반 사용자는 키가 필요 없다. `--direct`에서만 `KSKILL_EV_CHARGER_API_KEY` 또는 `DATA_GO_KR_API_KEY`를 사용하고, 데이터셋 `15076352` 활용신청은 별도로 해야 한다(자동승인).
111
+
112
+ 건축물대장 표제부 조회는 `k-skill-proxy`의 `/v1/building-register/title`을 호출하므로 일반 사용자는 키가 필요 없다. 주소 입력은 같은 proxy의 Kakao geocode를 먼저 사용한다. `--direct`에서만 `KSKILL_BUILDING_REGISTER_API_KEY` 또는 `DATA_GO_KR_API_KEY`를 사용하고 데이터셋 `15134735` 활용신청을 별도로 해야 한다(자동승인).
113
+
114
+ KERIS/RISS 학술자료 검색은 RISS 검색 API가 기관 전용 키를 요구해 `k-skill-proxy`를 거치지 않는다. 사용자가 직접 발급받은 `KSKILL_RISS_API_KEY`(호환 `RISS_API_KEY`)를 설정해 상류를 호출한다. RISS 키는 비영리 기관/대학에만 발급되며 RISS 검색에는 `DATA_GO_KR_API_KEY`를 사용하지 않는다.
115
+
116
+ 한국 특허 정보 검색은 KIPRIS Plus Open API 경로를 쓸 때 `KIPRIS_PLUS_API_KEY` 를 채운다. helper는 이 값을 읽어 실제 요청에서 `ServiceKey` 쿼리 파라미터로 보낸다. 공공데이터포털에서 복사한 percent-encoded key도 그대로 넣어도 된다.
117
+
118
+ ### Missing secret response template
119
+
120
+ 인증 스킬에서 값이 빠졌을 때는 credential resolution order에 따라 확보한다.
121
+
122
+ 필요한 값 예:
123
+
124
+ - SRT: `KSKILL_SRT_ID`, `KSKILL_SRT_PASSWORD`
125
+ - KTX: `KSKILL_KTX_ID`, `KSKILL_KTX_PASSWORD`
126
+ - 자연휴양림 빈 객실 조회: `KSKILL_FORESTTRIP_ID`, `KSKILL_FORESTTRIP_PASSWORD`
127
+ - 한국 법령 검색: 사용자 시크릿 불필요 (기본 hosted proxy 사용, 운영자만 `LAW_OC`)
128
+ - 한국 부동산 실거래가 조회: 사용자 시크릿 불필요 (기본 hosted proxy 사용)
129
+ - 한국 특허 정보 검색: `KIPRIS_PLUS_API_KEY`
130
+ - 한국 주식 정보 조회: 사용자 시크릿 불필요 (기본 hosted proxy 사용, 운영자만 `KRX_API_KEY`)
131
+ - 생활쓰레기 배출정보 조회: 사용자 시크릿 불필요 (`serviceKey`는 proxy 서버 주입, 호출 시 `pageNo=1`·`numOfRows=100` 필수)
132
+ - 학교 급식 식단 조회: 사용자 시크릿 불필요 (`KEDU_INFO_KEY`는 proxy 서버만)
133
+ - 도서관 도서 조회: 사용자 시크릿 불필요 (`DATA4LIBRARY_AUTH_KEY`는 proxy 서버만)
134
+ - 의약품 안전 체크: 사용자 시크릿 불필요 (`DATA_GO_KR_API_KEY`는 proxy 서버만)
135
+ - 식품 안전 체크: 사용자 시크릿 불필요 (`DATA_GO_KR_API_KEY`와 선택적 `FOODSAFETYKOREA_API_KEY`는 proxy 서버만)
136
+ - 창업진흥원 K-Startup 조회: 사용자 시크릿 불필요 (`DATA_GO_KR_API_KEY`는 proxy 서버만; `--direct` 호출 때만 `KSKILL_KSTARTUP_API_KEY`)
137
+ - 전기차 충전소 위치·상태 조회: 사용자 시크릿 불필요 (hosted proxy 사용; `--direct` 때만 `KSKILL_EV_CHARGER_API_KEY` 또는 `DATA_GO_KR_API_KEY`)
138
+ - 건축물대장 표제부 조회: 사용자 시크릿 불필요 (hosted proxy 사용; `--direct` 때만 `KSKILL_BUILDING_REGISTER_API_KEY` 또는 `DATA_GO_KR_API_KEY`)
139
+ - KERIS/RISS 학술자료 검색: 사용자 본인 `KSKILL_RISS_API_KEY`(호환 `RISS_API_KEY`) 필요 (RISS 검색 API는 비영리 기관/대학 전용 키로 직접 호출, proxy 미사용)
140
+ - 근처 가장 싼 주유소 찾기: 사용자 시크릿 불필요 (기본 hosted proxy 사용)
141
+ - 서울 지하철: 사용자 시크릿 불필요 (기본 hosted proxy 사용, 운영자만 `SEOUL_OPEN_API_KEY`)
142
+ - 서울 실시간 혼잡도: 사용자 시크릿 불필요 (기본 hosted proxy 사용, 운영자만 `SEOUL_OPEN_API_KEY`)
143
+ - 한국 날씨: 사용자 시크릿 불필요 (기본 hosted proxy 사용, 운영자만 `KMA_OPEN_API_KEY`)
144
+ - 사용자 위치 미세먼지 조회: `KSKILL_PROXY_BASE_URL` 또는 `AIR_KOREA_OPEN_API_KEY`
145
+
146
+ 시크릿이 비어 있다는 이유로 다른 서비스나 비공식 우회 경로를 자동 선택하지 않는다.
147
+
148
+ ### 2. Verify runtime environment
149
+
150
+ 이 스킬은 `SKILL.md` 단일 파일로 설치될 수 있으므로 별도 스크립트 파일에 의존하지 않고 아래 검증을 직접 실행한다.
151
+
152
+ ```bash
153
+ secrets_file="$HOME/.config/k-skill/secrets.env"
154
+ if [ ! -f "$secrets_file" ]; then
155
+ echo "missing secrets file: $secrets_file"
156
+ echo "next steps:"
157
+ echo " 1. create ~/.config/k-skill/secrets.env with your credentials"
158
+ echo " 2. chmod 0600 ~/.config/k-skill/secrets.env"
159
+ else
160
+ perms=$(stat -f '%Lp' "$secrets_file" 2>/dev/null || stat -c '%a' "$secrets_file" 2>/dev/null)
161
+ if [ "$perms" != "600" ]; then
162
+ echo "insecure permissions on $secrets_file: $perms (expected 600)"
163
+ echo "run: chmod 0600 $secrets_file"
164
+ else
165
+ echo "k-skill setup looks usable"
166
+ fi
167
+ fi
168
+ ```
169
+
170
+ repo 전체를 clone받은 경우에는 같은 검증을 `bash scripts/check-setup.sh` 로 실행해도 된다.
171
+
172
+ ### 3. Offer scheduled update checks
173
+
174
+ setup이 끝나면 사용자에게 주기적인 업데이트 확인 자동화를 원하는지 먼저 묻는다. 원하지 않으면 건너뛴다.
175
+
176
+ 기본 정책:
177
+
178
+ - 자동 설치가 아니라 `업데이트 확인` 만 기본으로 제안한다
179
+ - 지속성 있는 시스템 변경(`crontab`, `launchd`, `schtasks`)은 동의 없이 적용하지 않는다
180
+ - 기본 확인 명령은 `npx --yes skills check`
181
+ - 사용자가 명시적으로 `자동 업데이트` 를 원할 때만 `npx --yes skills update` 기반 스케줄을 별도로 제안한다
182
+ - 주의: `skills` CLI 버전에 따라 `check`에 `-g` 같은 옵션을 붙이면 확인을 넘어 설치본을 덮어쓸 수 있다. 자동화 스크립트에는 옵션 없는 `npx --yes skills check` 만 사용하고, 실제 업데이트 적용은 사용자가 검토 후 직접 실행하도록 안내한다
183
+
184
+ macOS / Linux 예시:
185
+
186
+ ```bash
187
+ mkdir -p ~/.config/k-skill/bin ~/.config/k-skill/logs
188
+ cat > ~/.config/k-skill/bin/check-skill-updates.sh <<'EOF'
189
+ #!/usr/bin/env bash
190
+ set -euo pipefail
191
+ mkdir -p "$HOME/.config/k-skill/logs"
192
+ {
193
+ date '+[%Y-%m-%d %H:%M:%S]'
194
+ npx --yes skills check
195
+ printf '\n'
196
+ } >> "$HOME/.config/k-skill/logs/skills-check.log" 2>&1
197
+ EOF
198
+ chmod +x ~/.config/k-skill/bin/check-skill-updates.sh
199
+ (crontab -l 2>/dev/null; echo "0 9 * * * $HOME/.config/k-skill/bin/check-skill-updates.sh") | crontab -
200
+ ```
201
+
202
+ Windows 예시:
203
+
204
+ ```powershell
205
+ New-Item -ItemType Directory -Force "$HOME/.config/k-skill/bin" | Out-Null
206
+ New-Item -ItemType Directory -Force "$HOME/.config/k-skill/logs" | Out-Null
207
+ @'
208
+ npx --yes skills check >> "$HOME/.config/k-skill/logs/skills-check.log" 2>&1
209
+ '@ | Set-Content "$HOME/.config/k-skill/bin/check-skill-updates.cmd"
210
+ schtasks /Create /SC DAILY /TN "k-skill-update-check" /TR "\"$HOME/.config/k-skill/bin/check-skill-updates.cmd\"" /ST 09:00 /F
211
+ ```
212
+
213
+ 설정 후에는 로그 위치를 짧게 알려준다:
214
+
215
+ - `~/.config/k-skill/logs/skills-check.log`
216
+
217
+ ### 4. Offer GitHub starring with explicit consent
218
+
219
+ setup 마지막에는 다음처럼 짧게 묻는다.
220
+
221
+ ```text
222
+ k-skill 저장소(NomaDamas/k-skill)에 GitHub star를 눌러드릴까요?
223
+ 원하시면 `gh` 로 바로 처리하고, 원하지 않으면 건너뜁니다.
224
+ ```
225
+
226
+ 규칙:
227
+
228
+ - 사용자가 명시적으로 동의하기 전에는 star API를 호출하지 않는다
229
+ - `gh` 가 없거나 인증되지 않았으면 설치/로그인 안내만 하고 자동 우회하지 않는다
230
+ - star 대상 저장소는 `NomaDamas/k-skill` 이다
231
+
232
+ 동의했고 `gh auth status` 가 정상이면 GitHub API로 star를 실행한다. (`gh` CLI에는 `repo star` 서브커맨드가 없다.)
233
+
234
+ ```bash
235
+ # 이미 star 여부 확인: 204 = 이미 star, 404 = 아직
236
+ gh api user/starred/NomaDamas/k-skill >/dev/null 2>&1 && echo "already starred" || echo "not starred yet"
237
+
238
+ # star 실행
239
+ gh api -X PUT user/starred/NomaDamas/k-skill
240
+ ```
241
+
242
+ 성공하면 짧게 완료만 알린다.
243
+
244
+ ## Completion checklist
245
+
246
+ - `~/.config/k-skill/secrets.env` exists with permission `0600` (또는 에이전트가 자체 vault로 credential을 관리 중)
247
+ - 필요한 환경변수가 설정되어 있다
248
+ - 사용자가 원한 경우에만 업데이트 확인 자동화 또는 GitHub star가 설정되었다
249
+
250
+ ## Notes
251
+
252
+ - 기본 흐름은 "전체 스킬 설치 → 이 setup skill 실행 → 개별 기능 사용" 이다
253
+ - 저장소 안에는 secret file을 두지 않는다