@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,185 @@
1
+ # Lovebug Report
2
+
3
+ ## What this skill does
4
+
5
+ `러브버그.com`(`https://xn--2i0bt2q2wd1wb.com/`)의 공개 지도/제보 표면을 이용해 러브버그(붉은등우단털파리) 관련 지역 정보를 취득하고, 사용자가 제공한 현재 위치 좌표로 익명 제보를 제출한다.
6
+
7
+ - 수도권 시·구별 러브버그 스코어와 최근 제보량을 조회한다.
8
+ - 동/읍/면 단위 area snapshot을 함께 조회해 어느 동네 제보인지 좁힌다.
9
+ - 사이트가 쓰는 익명 Supabase RPC 표면으로 제보를 보낸다.
10
+ - 좌표가 선택한 구 밖이거나 정확도가 낮거나 오늘 이미 같은 익명 기기에서 제보한 경우 upstream 거절을 그대로 알린다.
11
+
12
+ ## When to use
13
+
14
+ - "우리 동네 러브버그 많아?"
15
+ - "중랑구 러브버그 현황 알려줘"
16
+ - "러브버그 지도에서 동안구 제보량 확인해줘"
17
+ - "현재 위치로 러브버그 많다고 제보해줘"
18
+
19
+ ## Prerequisites
20
+
21
+ - 인터넷 연결
22
+ - Node.js 18+
23
+ - 이 저장소의 `lovebug-report` npm package 또는 동일 로직
24
+ - 제보 제출 시: 사용자가 제공한 현재 좌표(`lng`, `lat`), GPS 정확도, 구 코드(`gu_code`), 안정적인 익명 device hash
25
+
26
+ ## Public access path discovered
27
+
28
+ ### Primary source: lovebug.com public map JSON
29
+
30
+ - site: `https://xn--2i0bt2q2wd1wb.com/`
31
+ - gu score: `GET https://xn--2i0bt2q2wd1wb.com/api/map/gu-score`
32
+ - weekly report count: `GET https://xn--2i0bt2q2wd1wb.com/api/map/weekly-report-count`
33
+ - cluster snapshot: `GET https://xn--2i0bt2q2wd1wb.com/api/map/clusters?level=sigungu&historicalYear=2026`
34
+ - area snapshot: `GET https://xn--2i0bt2q2wd1wb.com/api/map/areas?historicalYear=2026&includePolygon=false`
35
+ - boundary GeoJSON: `GET https://xn--2i0bt2q2wd1wb.com/api/map/boundaries?level=sigungu`
36
+
37
+ Discovery result: the Next.js page loads these unauthenticated JSON endpoints directly. A live smoke on 2026-06-24 returned current `gu-score`, `weekly-report-count`, `clusters`, `areas`, and `boundaries` JSON.
38
+
39
+ No `k-skill-proxy` route is used because these upstream surfaces are public and do not require an API key.
40
+
41
+ ### Anonymous report surface
42
+
43
+ The site bundle calls Supabase RPC `submit_anonymous_report` for logged-out reporting:
44
+
45
+ ```http
46
+ POST https://sewrbxfawkmusnyzjoab.supabase.co/rest/v1/rpc/submit_anonymous_report
47
+ Content-Type: application/json
48
+ apikey: <site anon key>
49
+ Authorization: Bearer <site anon key>
50
+ ```
51
+
52
+ Body:
53
+
54
+ ```json
55
+ {
56
+ "p_gu_code": "11070",
57
+ "p_lng": 127.09,
58
+ "p_lat": 37.59,
59
+ "p_accuracy_m": 25,
60
+ "p_level": 2,
61
+ "p_device_hash": "stable-anonymous-device-id",
62
+ "p_context": "street",
63
+ "p_image_url": null,
64
+ "p_indoor": false
65
+ }
66
+ ```
67
+
68
+ Official level labels:
69
+
70
+ | `p_level` | Label |
71
+ | --- | --- |
72
+ | `0` | 잠잠해요 |
73
+ | `1` | 살짝 보임 |
74
+ | `2` | 많아요 |
75
+ | `3` | 매우 많아요 |
76
+
77
+ Official context labels:
78
+
79
+ | `p_context` | Label |
80
+ | --- | --- |
81
+ | `indoor` | 실내 |
82
+ | `street` | 길거리 |
83
+ | `park` | 공원 |
84
+ | `transit` | 지하철·버스 |
85
+ | `shop` | 상가 |
86
+ | `other` | 기타 |
87
+
88
+ ## Workflow
89
+
90
+ ### 1. Resolve the region
91
+
92
+ Use `searchLovebugRegions` first. It combines gu score and area snapshot rows.
93
+
94
+ ```js
95
+ const { searchLovebugRegions } = require("lovebug-report")
96
+
97
+ const result = await searchLovebugRegions({
98
+ query: "중랑",
99
+ includeAreas: true,
100
+ limit: 5
101
+ })
102
+
103
+ console.log(result.items)
104
+ ```
105
+
106
+ CLI:
107
+
108
+ ```bash
109
+ node packages/lovebug-report/src/cli.js search --query 중랑 --include-areas
110
+ node packages/lovebug-report/src/cli.js list --limit 10
111
+ node packages/lovebug-report/src/cli.js find --query 동안
112
+ ```
113
+
114
+ Returned gu fields include:
115
+
116
+ - `gu_code`, `gu_name`, `sido`
117
+ - `score`, `score_label`, `level`, `level_label`, `advisory`
118
+ - `counts.report`, `counts.report_24h`, `counts.spotted`, `counts.quiet`, `counts.low`, `counts.medium`, `counts.high`
119
+ - `coordinates`
120
+ - `areas[]` when `includeAreas` is enabled
121
+
122
+ ### 2. Answer conservatively
123
+
124
+ State that the score is based on recent lovebug.com user reports, not government-certified pest density.
125
+
126
+ Good answer shape:
127
+
128
+ - 지역: `중랑구`
129
+ - 러브버그 스코어: `77` / `엄청 많아요, 조심!`
130
+ - 최근 제보: `14일 439건`, `24시간 105건`
131
+ - 동네 detail: area rows that matched the query
132
+ - 출처: `러브버그.com 공개 지도 JSON`
133
+
134
+ ### 3. Submit a report only when required inputs are known
135
+
136
+ Do not invent coordinates or device hash. If the user asks to report but the automation environment has no current location, state that `lng`, `lat`, and GPS accuracy are required.
137
+
138
+ ```js
139
+ const { reportLovebug } = require("lovebug-report")
140
+
141
+ await reportLovebug({
142
+ guCode: "11070",
143
+ level: "많아요",
144
+ context: "길거리",
145
+ lng: 127.09,
146
+ lat: 37.59,
147
+ accuracyM: 25,
148
+ deviceHash: "stable-anonymous-device-id"
149
+ })
150
+ ```
151
+
152
+ CLI:
153
+
154
+ ```bash
155
+ node packages/lovebug-report/src/cli.js report \
156
+ --gu-code 11070 \
157
+ --level 많아요 \
158
+ --context 길거리 \
159
+ --lng 127.09 \
160
+ --lat 37.59 \
161
+ --accuracy 25 \
162
+ --device-hash stable-anonymous-device-id
163
+ ```
164
+
165
+ ## Done when
166
+
167
+ - Region status was fetched from `러브버그.com` public JSON.
168
+ - Region ambiguity was resolved with `gu_code`/`gu_name` when needed.
169
+ - Report submission used real current coordinates and the upstream RPC returned success.
170
+ - Source URL and user-report nature were disclosed.
171
+
172
+ ## Failure modes
173
+
174
+ - `ANON_DAILY_DUPLICATE`: the same anonymous device already reported that region today.
175
+ - `OUTSIDE_GU_AREA`: supplied coordinates are outside `p_gu_code`.
176
+ - `ACCURACY_TOO_LOW`: GPS accuracy is too low for the upstream validation.
177
+ - Public Next.js/Supabase bundle keys, routes, table names, or RPC names can change.
178
+ - The map is user-report-based and can lag or be noisy.
179
+ - The site may add stricter rate limits, bot checks, login requirements, or image-upload restrictions.
180
+
181
+ ## Notes
182
+
183
+ - No login, CAPTCHA bypass, queue bypass, or privileged workflow automation.
184
+ - No `k-skill-proxy` because upstream is public and keyless from the user's machine.
185
+ - Image upload is not handled by this skill; pass `imageUrl` only when a site-compatible image URL already exists.
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "lovebug-report",
3
+ "description": "러브버그.com 공개 지도 표면으로 수도권 시·구별 러브버그 출몰 지수·동네별 제보 현황을 조회하고, 사용자가 제공한 현재 좌표로 익명 러브버그 제보를 제출한다. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.",
4
+ "profiles": [
5
+ "proxy",
6
+ "action:submission"
7
+ ],
8
+ "frontmatter": "name: lovebug-report\ndescription: 러브버그.com 공개 지도 표면으로 수도권 시·구별 러브버그 출몰 지수·동네별 제보 현황을 조회하고, 사용자가 제공한 현재 좌표로 익명 러브버그 제보를 제출한다. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.\nlicense: MIT\nmetadata:\n category: local-info\n locale: ko-KR\n phase: v1"
9
+ }
@@ -0,0 +1,125 @@
1
+ # Market Kurly Search
2
+
3
+ ## What this skill does
4
+
5
+ 마켓컬리 웹앱이 실제로 사용하는 **비로그인 검색/상품 상세 표면**을 사용해 아래 흐름을 처리한다.
6
+
7
+ - 키워드로 상품 후보를 검색한다.
8
+ - 현재 가격과 할인 여부를 확인한다.
9
+ - 품절 여부와 배송 타입을 확인한다.
10
+ - 상품 링크를 함께 반환한다.
11
+ - **주문/장바구니 같은 액션은 하지 않는다. 조회형으로만 답한다.**
12
+
13
+ ## When to use
14
+
15
+ - "마켓컬리에서 우유 얼마야?"
16
+ - "컬리에서 딸기 검색해줘"
17
+ - "이 상품 품절인지 보고 링크도 줘"
18
+ - "지금 컬리 가격만 빠르게 보고 싶어"
19
+
20
+ ## When not to use
21
+
22
+ - 주문/장바구니/결제까지 자동화해야 하는 경우
23
+ - 주소 기반 배송 가능 여부나 회원 전용 가격을 확정해야 하는 경우
24
+ - 로그인 세션이 필요한 개인화 추천/찜 정보를 조회해야 하는 경우
25
+
26
+ ## Prerequisites
27
+
28
+ - 인터넷 연결
29
+ - `node` 18+
30
+ - 이 저장소의 `market-kurly-search` package 또는 동일 로직
31
+
32
+ ## Required inputs
33
+
34
+ ### 1. Ask for a product keyword if it is missing
35
+
36
+ 상품명 또는 검색어가 없으면 먼저 물어본다.
37
+
38
+ - 권장 질문: `찾을 마켓컬리 상품명이나 검색어를 알려주세요. 예: 우유, 딸기, 닭가슴살`
39
+ - 너무 넓으면: `검색어가 너무 넓어요. 브랜드나 용량까지 같이 알려주시면 가격 후보를 더 정확히 추릴 수 있어요.`
40
+
41
+ ### 2. Confirm which candidate they want when the query is ambiguous
42
+
43
+ 검색 결과가 여러 개면 상위 2~3개만 보여주고 다시 확인받는다.
44
+
45
+ - 권장 질문: `후보가 여러 개예요. 아래 상품 중 어떤 상품 가격을 볼까요?`
46
+ - 응답에는 상품명 + 현재 가격 + 품절 여부 + 링크를 같이 붙인다.
47
+
48
+ ## Official Market Kurly surfaces
49
+
50
+ - search list: `https://api.kurly.com/search/v4/sites/market/normal-search?keyword=<keyword>&page=1`
51
+ - search count: `https://api.kurly.com/search/v3/sites/market/normal-search/count?keyword=<keyword>&filters=&allow_replace=true`
52
+ - product detail page: `https://www.kurly.com/goods/<productNo>`
53
+
54
+ ## Workflow
55
+
56
+ ### 1. Search by keyword first
57
+
58
+ ```js
59
+ const { searchProducts } = require("market-kurly-search")
60
+
61
+ const result = await searchProducts("우유")
62
+ console.log(result.items.slice(0, 3))
63
+ ```
64
+
65
+ 검색 결과에서는 아래 필드를 우선 본다.
66
+
67
+ - 상품명
68
+ - 현재 가격 (`discountedPrice` 우선, 없으면 `salesPrice`)
69
+ - 할인율
70
+ - 품절 여부
71
+ - 배송 타입
72
+ - 상품 링크
73
+
74
+ ### 2. Use the count endpoint when the result set is broad
75
+
76
+ ```js
77
+ const { countProducts } = require("market-kurly-search")
78
+
79
+ const count = await countProducts("우유")
80
+ console.log(count)
81
+ ```
82
+
83
+ 후보가 너무 많으면 `count` 를 먼저 보여 주고 검색어를 좁히라고 안내한다.
84
+
85
+ ### 3. Use the goods page detail as a fallback or follow-up lookup
86
+
87
+ ```js
88
+ const { getProductDetail } = require("market-kurly-search")
89
+
90
+ const detail = await getProductDetail(5063110)
91
+ console.log(detail)
92
+ ```
93
+
94
+ `goods/<productNo>` HTML 안의 `__NEXT_DATA__` 에서 상품명, 가격, 품절 여부, 배송 타입을 추출한다.
95
+
96
+ ### 4. Respond conservatively
97
+
98
+ 응답은 짧고 보수적으로 정리한다.
99
+
100
+ - 상품명
101
+ - 현재 가격
102
+ - 필요하면 원가/할인가 여부
103
+ - 품절 여부 또는 판매 가능 여부
104
+ - 상품 링크
105
+ - **가격/품절/노출 정보는 시점에 따라 달라질 수 있으니 조회 시각 기준 참고값이라고 분명히 말한다.**
106
+
107
+ ## Done when
108
+
109
+ - 상품 키워드를 확인했다.
110
+ - 검색 결과에서 후보와 현재 가격을 최소 1개 이상 반환했다.
111
+ - 필요하면 상품 상세 페이지로 보조 확인했다.
112
+ - 주문/장바구니 같은 범위 밖 액션은 하지 않았다.
113
+
114
+ ## Failure modes
115
+
116
+ - 검색어가 너무 넓으면 후보가 과도하게 많아질 수 있다.
117
+ - 가격/품절/배송 문구는 시점에 따라 달라질 수 있다.
118
+ - 현재 확인한 표면은 **공식 개발자 Open API가 아니라 웹이 쓰는 공개 표면** 이므로 스키마가 바뀌면 깨질 수 있다.
119
+ - 회원 전용/주소 전용 정보는 비로그인 조회만으로 확정할 수 없다.
120
+
121
+ ## Notes
122
+
123
+ - 조회형 스킬이다.
124
+ - 비로그인 공개 표면 우선 원칙을 유지한다.
125
+ - 주문/장바구니/로그인 요구 기능은 시도하지 않는다.
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "market-kurly-search",
3
+ "description": "로그인 없이 접근 가능한 마켓컬리 검색/상품 상세 표면으로 상품 후보, 현재 가격, 할인 여부, 품절 여부를 조회한다. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.",
4
+ "profiles": [
5
+ "action:commerce"
6
+ ],
7
+ "frontmatter": "name: market-kurly-search\ndescription: 로그인 없이 접근 가능한 마켓컬리 검색/상품 상세 표면으로 상품 후보, 현재 가격, 할인 여부, 품절 여부를 조회한다. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.\nlicense: MIT\nmetadata:\n category: retail\n locale: ko-KR\n phase: v1"
8
+ }
@@ -0,0 +1,86 @@
1
+ # 의약품 안전 체크
2
+
3
+ ## What this skill does
4
+
5
+ 식약처 공식 OpenAPI를 **`k-skill-proxy` 경유**로 조회해 **의약품개요정보(e약은요)** 와 **안전상비의약품 정보**를 확인한다.
6
+
7
+ 하지만 사용자가 증상이나 복용 상황을 말하면 **바로 단정하지 말고 먼저 되묻는다.**
8
+
9
+ - 본인/아이/임산부/고령자 여부
10
+ - 어떤 약을 이미 먹었는지 / 지금 먹으려는지
11
+ - 언제부터 얼마나 복용했는지
12
+ - 현재 증상, 기저질환, 알레르기, 복용 중인 다른 약
13
+ - red flag (`호흡곤란`, `의식저하`, `심한 발진`, `지속되는 구토/흉통`)
14
+
15
+ red flag 가 있으면 API 조회보다 **즉시 119·응급실·의료진 연결**을 우선한다.
16
+
17
+ ## When to use
18
+
19
+ - "이 약이랑 이 약 같이 먹어도 되니?"
20
+ - "타이레놀 먹는 중인데 판콜 같이 먹어도 돼?"
21
+ - "두드러기가 있는데 이 약 계속 먹어도 되나?"
22
+ - "식약처 공식 약 정보로 효능/주의사항 확인해줘"
23
+
24
+ ## Prerequisites
25
+
26
+ - 인터넷 연결
27
+ - `python3`
28
+ - 설치된 skill payload 안에 `scripts/mfds_drug_safety.py` helper 포함
29
+ - `k-skill-proxy`의 `/v1/mfds/drug-safety/lookup` route가 있는 hosted/self-host 프록시에 접근 가능할 것
30
+
31
+ ## Credential requirements
32
+
33
+ - 사용자 측 **필수** 시크릿 없음.
34
+ - `KSKILL_PROXY_BASE_URL` — self-host·별도 프록시를 쓸 때만 설정. 비우면 기본 hosted `https://k-skill-proxy.nomadamas.org` 를 사용한다.
35
+ - `DATA_GO_KR_API_KEY` 는 **프록시 운영 서버** 환경에만 둔다.
36
+
37
+ ## Mandatory interview first
38
+
39
+ 증상/복용상황이 언급되면 바로 결론을 말하지 말고 먼저 되묻는다.
40
+
41
+ 권장 첫 질문 예시:
42
+
43
+ - `누가 복용하려는지(본인/아이/임산부/고령자), 이미 먹은 약 이름, 언제 얼마나 복용했는지, 지금 있는 증상을 먼저 알려주세요.`
44
+ - `호흡곤란, 의식저하, 입술·혀 붓기, 심한 전신 발진이 있으면 즉시 119 또는 응급실로 가야 합니다.`
45
+
46
+ ## Official surfaces
47
+
48
+ - 공공데이터포털 문서: `https://www.data.go.kr/data/15075057/openapi.do`
49
+ - e약은요 endpoint: `https://apis.data.go.kr/1471000/DrbEasyDrugInfoService/getDrbEasyDrugList`
50
+ - 공공데이터포털 문서: `https://www.data.go.kr/data/15097208/openapi.do`
51
+ - 안전상비의약품 endpoint: `https://apis.data.go.kr/1471000/SafeStadDrugService/getSafeStadDrugInq`
52
+ - 프록시 route: `GET /v1/mfds/drug-safety/lookup`
53
+
54
+ ## Workflow
55
+
56
+ 1. 증상/복용상황이 있으면 인터뷰를 먼저 진행한다.
57
+ 2. red flag 가 하나라도 있으면 즉시 응급 안내로 전환한다.
58
+ 3. 약 이름이 확인되면 `k-skill-proxy`의 `/v1/mfds/drug-safety/lookup` 으로 공식 정보를 조회한다.
59
+ 4. 효능, 사용법, 주의사항, 상호작용, 이상반응, 보관법을 짧게 정리한다.
60
+ 5. `같이 먹어도 되나?` 질문에는 공식 상호작용 문구만 근거로 제시하고, 최종 판단은 약사·의료진 확인이 필요하다고 명시한다.
61
+
62
+ ## CLI examples
63
+
64
+ ```bash
65
+ python3 scripts/mfds_drug_safety.py interview \
66
+ --question "타이레놀이랑 판콜 같이 먹어도 되나요?" \
67
+ --symptoms "두드러기와 어지러움"
68
+ ```
69
+
70
+ ```bash
71
+ python3 scripts/mfds_drug_safety.py lookup --item-name "타이레놀" --item-name "판콜"
72
+ ```
73
+
74
+ ## Response policy
75
+
76
+ - 이 스킬은 **진단/처방/복용 지시**를 하지 않는다.
77
+ - 공식 문서에 있는 효능/주의/상호작용 문구만 근거로 요약한다.
78
+ - 상호작용 문구가 모호하거나 red flag 가 있으면 약사·의사 상담으로 넘긴다.
79
+ - 증상이 있는 질문은 인터뷰 없이 바로 답하지 않는다.
80
+
81
+ ## Done when
82
+
83
+ - 증상 또는 복용상황을 먼저 되물었다.
84
+ - red flag 여부를 확인했다.
85
+ - 프록시 route를 통해 공식 endpoint 조회 결과를 JSON으로 정리했다.
86
+ - 최소한 제품명, 업체명, 효능/주의/상호작용이 포함된 요약을 제공했다.
@@ -0,0 +1,184 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import os
6
+ import re
7
+ import sys
8
+ import urllib.error
9
+ import urllib.parse
10
+ import urllib.request
11
+ from html import unescape
12
+ from typing import Any
13
+
14
+ PROXY_BASE_URL_ENV_VAR = "KSKILL_PROXY_BASE_URL"
15
+ DEFAULT_PROXY_BASE_URL = "https://k-skill-proxy.nomadamas.org"
16
+ PROXY_DOWN_MSG = "설정된 k-skill-proxy 서버가 응답하지 않습니다. 잠시 후 재시도하거나 운영자에게 문의하세요."
17
+ PROXY_KEY_NOT_CONFIGURED_MSG = "k-skill-proxy에 필요한 API 키가 설정되어 있지 않습니다. 운영자에게 문의하세요."
18
+
19
+
20
+ class ApiError(RuntimeError):
21
+ def __init__(self, message: str, *, status_code: int | None = None, url: str | None = None):
22
+ super().__init__(message)
23
+ self.status_code = status_code
24
+ self.url = url
25
+
26
+
27
+ def summarize_text(value: Any) -> str:
28
+ if value is None:
29
+ return ""
30
+ text = unescape(str(value))
31
+ text = re.sub(r"<[^>]+>", " ", text)
32
+ text = re.sub(r"\s+", " ", text).strip()
33
+ return text
34
+
35
+
36
+ def resolve_proxy_base_url(explicit_base_url: str | None = None, env: dict[str, str] | None = None) -> str:
37
+ env = env or os.environ
38
+ candidate = summarize_text(explicit_base_url or env.get(PROXY_BASE_URL_ENV_VAR))
39
+ if candidate.casefold() in {"off", "false", "0", "disable", "disabled", "none"}:
40
+ raise ValueError("KSKILL_PROXY_BASE_URL 가 비활성화되어 있습니다.")
41
+ if candidate and candidate != "replace-me":
42
+ return candidate.rstrip("/")
43
+ return DEFAULT_PROXY_BASE_URL
44
+
45
+
46
+ def build_drug_interview(question: str | None = None, symptoms: str | None = None) -> dict[str, Any]:
47
+ return {
48
+ "domain": "drug",
49
+ "question": summarize_text(question),
50
+ "symptoms": summarize_text(symptoms),
51
+ "must_ask": [
52
+ "누가 복용하려는지 알려주세요. (본인/아이/임산부/고령자)",
53
+ "무슨 약을 이미 먹었거나 지금 먹으려는지, 제품명/성분명을 각각 알려주세요.",
54
+ "언제부터, 얼마나 자주, 한 번에 얼마나 복용했는지 알려주세요.",
55
+ "지금 있는 증상과 언제 시작됐는지 알려주세요.",
56
+ "복용 중인 약, 기저질환, 알레르기 여부를 알려주세요.",
57
+ ],
58
+ "red_flags": [
59
+ "호흡곤란 또는 숨쉬기 힘듦",
60
+ "의식저하, 실신, 혼동",
61
+ "입술·혀 붓기 또는 심한 전신 발진",
62
+ "지속되는 구토, 경련, 심한 흉통",
63
+ ],
64
+ "urgent_action": "red flag 가 하나라도 있으면 약 정보 조회보다 즉시 119·응급실·의료진 연결을 우선하세요.",
65
+ "policy": "이 helper 는 진단이나 복용 지시를 하지 않고, 공식 식약처 안전정보 확인 전에 반드시 되묻기 흐름을 제공합니다.",
66
+ }
67
+
68
+
69
+ EASY_FIELD_MAP = {
70
+ "item_name": "item_name",
71
+ "company_name": "company_name",
72
+ "efficacy": "efficacy",
73
+ "how_to_use": "how_to_use",
74
+ "warnings": "warnings",
75
+ "cautions": "cautions",
76
+ "interactions": "interactions",
77
+ "side_effects": "side_effects",
78
+ "storage": "storage",
79
+ "item_seq": "item_seq",
80
+ }
81
+
82
+ SAFE_STAD_FIELD_MAP = {
83
+ "item_name": "item_name",
84
+ "company_name": "company_name",
85
+ "efficacy": "efficacy",
86
+ "how_to_use": "how_to_use",
87
+ "warnings": "warnings",
88
+ "cautions": "cautions",
89
+ "interactions": "interactions",
90
+ "side_effects": "side_effects",
91
+ }
92
+
93
+
94
+ def normalize_easy_drug_item(item: dict[str, Any]) -> dict[str, Any]:
95
+ normalized = {key: summarize_text(item.get(source_key)) for key, source_key in EASY_FIELD_MAP.items()}
96
+ normalized["source"] = "drug_easy_info"
97
+ return normalized
98
+
99
+
100
+ def normalize_safe_stad_item(item: dict[str, Any]) -> dict[str, Any]:
101
+ normalized = {key: summarize_text(item.get(source_key)) for key, source_key in SAFE_STAD_FIELD_MAP.items()}
102
+ normalized["source"] = "safe_standby_medicine"
103
+ return normalized
104
+
105
+
106
+ def read_json_response(request: urllib.request.Request | str) -> dict[str, Any]:
107
+ try:
108
+ with urllib.request.urlopen(request, timeout=30) as response:
109
+ return json.loads(response.read().decode("utf-8"))
110
+ except urllib.error.HTTPError as error:
111
+ body = error.read().decode("utf-8", errors="replace")
112
+ try:
113
+ payload = json.loads(body)
114
+ except json.JSONDecodeError:
115
+ payload = None
116
+ if error.code == 503 and isinstance(payload, dict) and payload.get("error") == "upstream_not_configured":
117
+ raise ApiError(PROXY_KEY_NOT_CONFIGURED_MSG, status_code=error.code, url=getattr(error, "url", None)) from error
118
+
119
+ if isinstance(payload, dict) and payload.get("message"):
120
+ raise ApiError(str(payload["message"]), status_code=error.code, url=getattr(error, "url", None)) from error
121
+ raise ApiError(
122
+ f"MFDS drug proxy request failed with HTTP {error.code}",
123
+ status_code=error.code,
124
+ url=getattr(error, "url", None),
125
+ ) from error
126
+ except urllib.error.URLError as error:
127
+ raise ApiError(f"{PROXY_DOWN_MSG} (상세: {error.reason})") from error
128
+
129
+
130
+ def lookup_drugs(
131
+ item_names: list[str],
132
+ *,
133
+ limit: int = 5,
134
+ base_url: str | None = None,
135
+ request_json: Any = read_json_response,
136
+ ) -> dict[str, Any]:
137
+ resolved_base_url = resolve_proxy_base_url(base_url)
138
+ url = f"{resolved_base_url}/v1/mfds/drug-safety/lookup"
139
+ params: list[tuple[str, str]] = [("itemName", item_name) for item_name in item_names]
140
+ params.append(("limit", str(limit)))
141
+ query = urllib.parse.urlencode(params)
142
+ request = urllib.request.Request(
143
+ f"{url}?{query}",
144
+ headers={"Accept": "application/json", "User-Agent": "k-skill-mfds/1.0"},
145
+ )
146
+ return request_json(request)
147
+
148
+
149
+ def build_parser() -> argparse.ArgumentParser:
150
+ parser = argparse.ArgumentParser(description="MFDS drug-safety helper")
151
+ subparsers = parser.add_subparsers(dest="command", required=True)
152
+
153
+ interview = subparsers.add_parser("interview", help="print the mandatory symptom follow-up interview")
154
+ interview.add_argument("--question", default="")
155
+ interview.add_argument("--symptoms", default="")
156
+
157
+ lookup = subparsers.add_parser("lookup", help="look up official MFDS drug safety records through k-skill-proxy")
158
+ lookup.add_argument("--item-name", action="append", required=True)
159
+ lookup.add_argument("--limit", type=int, default=5)
160
+ lookup.add_argument("--proxy-base-url")
161
+ return parser
162
+
163
+
164
+ def main(argv: list[str] | None = None) -> int:
165
+ args = build_parser().parse_args(argv)
166
+
167
+ if args.command == "interview":
168
+ print(json.dumps(build_drug_interview(question=args.question, symptoms=args.symptoms), ensure_ascii=False, indent=2))
169
+ return 0
170
+
171
+ if args.command == "lookup":
172
+ try:
173
+ payload = lookup_drugs(args.item_name, limit=args.limit, base_url=args.proxy_base_url)
174
+ print(json.dumps(payload, ensure_ascii=False, indent=2))
175
+ return 0
176
+ except (ValueError, ApiError) as error:
177
+ print(json.dumps({"error": str(error)}, ensure_ascii=False, indent=2), file=sys.stderr)
178
+ return 1
179
+
180
+ return 1
181
+
182
+
183
+ if __name__ == "__main__":
184
+ raise SystemExit(main())
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "mfds-drug-safety",
3
+ "description": "식약처 공공 OpenAPI를 k-skill-proxy 경유로 조회하기 전에 증상·복용상황을 반드시 되묻는 인터뷰형 의약품 안전 체크 스킬.",
4
+ "profiles": [
5
+ "proxy",
6
+ "vault",
7
+ "lookup"
8
+ ],
9
+ "frontmatter": "name: mfds-drug-safety\ndescription: 식약처 공공 OpenAPI를 k-skill-proxy 경유로 조회하기 전에 증상·복용상황을 반드시 되묻는 인터뷰형 의약품 안전 체크 스킬.\nlicense: MIT\nmetadata:\n category: public-health\n locale: ko-KR\n phase: v1"
10
+ }