@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,369 @@
1
+ #!/usr/bin/env python3
2
+ """ohou-today-deal — 오늘의집 공개 오늘의딜 특가 상품 조회 CLI.
3
+
4
+ 조회 전용. 로그인·장바구니·구매·결제 자동화 없음.
5
+ __NEXT_DATA__ 서버 렌더링 초기 데이터만 읽는 read-only 스킬.
6
+
7
+ Usage:
8
+ ohou-today-deal list [--limit N] [--sort discount|price|review|annual-sales]
9
+ ohou-today-deal list --query 러그 --min-discount 30 --free-delivery
10
+ ohou-today-deal list --html-file ./fixture.html
11
+
12
+ Supported surface:
13
+ https://ohou.se/commerces/today_deals (공개 HTML)
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import argparse
18
+ import html
19
+ import json
20
+ import re
21
+ import sys
22
+ import urllib.request
23
+ from dataclasses import asdict, dataclass
24
+ from datetime import datetime, timedelta, timezone
25
+ from pathlib import Path
26
+ from typing import Any
27
+
28
+ DEFAULT_URL = "https://ohou.se/commerces/today_deals"
29
+ DEFAULT_USER_AGENT = (
30
+ "k-skill-ohou-today-deal/1.0 (+https://github.com/NomaDamas/k-skill)"
31
+ )
32
+ TODAY_DEAL_FEED_KEYS: tuple[tuple[str, ...], ...] = (
33
+ ("today-deal-feed",),
34
+ ("special-today-deal-feed",),
35
+ )
36
+
37
+
38
+ @dataclass(frozen=True)
39
+ class OhouDeal:
40
+ id: str
41
+ title: str
42
+ brand: str | None
43
+ url: str
44
+ image_url: str | None
45
+ original_price: int | None
46
+ selling_price: int | None
47
+ discount_rate: int | None
48
+ best_price: int | None
49
+ best_discount_rate: int | None
50
+ best_discount_description: str | None
51
+ review_count: int
52
+ review_average: float | None
53
+ scrap_count: int | None
54
+ annual_sales: int | None
55
+ free_delivery: bool
56
+ sold_out: bool
57
+ start_at: str | None
58
+ end_at: str | None
59
+
60
+ def to_dict(self) -> dict[str, Any]:
61
+ return asdict(self)
62
+
63
+
64
+ def _to_int(value: Any) -> int | None:
65
+ if value is None or value == "":
66
+ return None
67
+ try:
68
+ return int(value)
69
+ except (TypeError, ValueError):
70
+ return None
71
+
72
+
73
+ def _to_float(value: Any) -> float | None:
74
+ if value is None or value == "":
75
+ return None
76
+ try:
77
+ return float(value)
78
+ except (TypeError, ValueError):
79
+ return None
80
+
81
+
82
+ def fetch_html(url: str = DEFAULT_URL, timeout: int = 20) -> str:
83
+ # ohou.se Akamai 정책: 익명 UA(`python-urllib`, `Mozilla/5.0` 단독)는 403,
84
+ # 봇 이름+contact URL이 포함된 well-formed UA는 허용. 우회가 아닌 자기소개.
85
+ request = urllib.request.Request(
86
+ url,
87
+ headers={
88
+ "User-Agent": DEFAULT_USER_AGENT,
89
+ "Accept": "text/html,application/json",
90
+ },
91
+ )
92
+ with urllib.request.urlopen(request, timeout=timeout) as response:
93
+ charset = response.headers.get_content_charset() or "utf-8"
94
+ return response.read().decode(charset, errors="replace")
95
+
96
+
97
+ def extract_next_data(document: str) -> dict[str, Any]:
98
+ stripped = document.lstrip()
99
+ if stripped.startswith("{"):
100
+ return json.loads(stripped)
101
+
102
+ match = re.search(
103
+ r'<script\b[^>]*\bid=["\']__NEXT_DATA__[^>]*>(.*?)</script>',
104
+ document,
105
+ re.DOTALL,
106
+ )
107
+ if not match:
108
+ raise ValueError("Could not find __NEXT_DATA__ in Today Deal HTML")
109
+ return json.loads(html.unescape(match.group(1)))
110
+
111
+
112
+ def _walk(value: Any):
113
+ """스택 기반 DFS로 JSON 트리의 모든 dict 노드를 순회한다.
114
+
115
+ __NEXT_DATA__는 깊고 거대한 트리 구조를 가질 수 있어
116
+ 재귀 대신 반복문을 사용해 sys.getrecursionlimit() 제한을 회피한다.
117
+ """
118
+ stack = [value]
119
+ while stack:
120
+ curr = stack.pop()
121
+ if isinstance(curr, dict):
122
+ yield curr
123
+ stack.extend(curr.values())
124
+ elif isinstance(curr, list):
125
+ stack.extend(reversed(curr))
126
+
127
+
128
+ def _looks_like_deal_node(node: dict[str, Any]) -> bool:
129
+ deal = node.get("deal")
130
+ return (
131
+ node.get("type") == "DEAL"
132
+ and isinstance(deal, dict)
133
+ and bool(deal.get("id"))
134
+ and bool(deal.get("name"))
135
+ )
136
+
137
+
138
+ def _iter_today_deal_feeds(payload: dict[str, Any]):
139
+ """__NEXT_DATA__에서 today-deal-feed / special-today-deal-feed 쿼리만 골라낸다.
140
+
141
+ React Query dehydrated state는 `props.pageProps.dehydratedState.queries`에
142
+ `[{queryKey, state: {data: {...}}}]` 형태로 저장된다. queryKey가 일치하는
143
+ 엔트리의 `state.data.todayDealFeed.slots`만 today-deal 콘텐츠로 인정한다.
144
+ """
145
+ allowed = {tuple(key) for key in TODAY_DEAL_FEED_KEYS}
146
+ queries = (
147
+ payload.get("props", {})
148
+ .get("pageProps", {})
149
+ .get("dehydratedState", {})
150
+ .get("queries", [])
151
+ )
152
+ if not isinstance(queries, list):
153
+ return
154
+ for entry in queries:
155
+ if not isinstance(entry, dict):
156
+ continue
157
+ query_key = entry.get("queryKey")
158
+ if not isinstance(query_key, list):
159
+ continue
160
+ if tuple(query_key) not in allowed:
161
+ continue
162
+ state = entry.get("state") or {}
163
+ data = state.get("data") or {}
164
+ feed = data.get("todayDealFeed") or {}
165
+ slots = feed.get("slots")
166
+ if isinstance(slots, list):
167
+ yield tuple(query_key), slots
168
+
169
+
170
+ def _normalize_deal(node: dict[str, Any]) -> OhouDeal:
171
+ deal = node.get("deal", {})
172
+ price = deal.get("price") or {}
173
+ best_price = node.get("bestDiscountPrice") or {}
174
+ brand = deal.get("brand") or {}
175
+ review = deal.get("reviewStatistic") or {}
176
+ scrap = deal.get("scrapInfo") or {}
177
+ badge = deal.get("badgeProperties") or {}
178
+ annual_sales = node.get("salesStats", {}).get("annualCumulativeSales")
179
+ deal_id = str(deal.get("id", ""))
180
+
181
+ return OhouDeal(
182
+ id=deal_id,
183
+ title=str(deal.get("name") or node.get("title") or ""),
184
+ brand=brand.get("name"),
185
+ url=f"https://ohou.se/productions/{deal_id}/selling",
186
+ image_url=deal.get("imageUrl"),
187
+ original_price=_to_int(price.get("representativeOriginalPrice")),
188
+ selling_price=_to_int(price.get("representativeSellingPrice")),
189
+ discount_rate=_to_int(price.get("discountRate")),
190
+ best_price=_to_int(best_price.get("price")),
191
+ best_discount_rate=_to_int(best_price.get("discountRate")),
192
+ best_discount_description=best_price.get("discountPlanDescription"),
193
+ review_count=_to_int(review.get("reviewCount")) or 0,
194
+ review_average=_to_float(review.get("reviewAverage")),
195
+ scrap_count=_to_int(scrap.get("scrapCount")),
196
+ annual_sales=_to_int(annual_sales),
197
+ free_delivery=bool(badge.get("isFreeDelivery")),
198
+ sold_out=bool(deal.get("isSoldOut")),
199
+ start_at=node.get("startAt"),
200
+ end_at=node.get("endAt"),
201
+ )
202
+
203
+
204
+ def extract_deals(payload: dict[str, Any]) -> list[OhouDeal]:
205
+ seen: set[str] = set()
206
+ deals: list[OhouDeal] = []
207
+ found_feed = False
208
+
209
+ for _query_key, slots in _iter_today_deal_feeds(payload):
210
+ found_feed = True
211
+ for node in slots:
212
+ if not isinstance(node, dict) or not _looks_like_deal_node(node):
213
+ continue
214
+ deal = _normalize_deal(node)
215
+ if deal.id in seen:
216
+ continue
217
+ seen.add(deal.id)
218
+ deals.append(deal)
219
+
220
+ # Fixture/legacy fallback: payload에 React Query dehydratedState가 없거나
221
+ # queryKey 구조가 다른 경우(테스트 fixture, 단순화된 페이로드)에 한해서만
222
+ # 전체 트리를 DFS로 훑어 DEAL 노드를 수집한다. 라이브 페이지는 항상
223
+ # 위쪽 명시적 분기에서 잡히므로 이 fallback은 영향받지 않는다.
224
+ if not found_feed:
225
+ for node in _walk(payload):
226
+ if not _looks_like_deal_node(node):
227
+ continue
228
+ deal = _normalize_deal(node)
229
+ if deal.id in seen:
230
+ continue
231
+ seen.add(deal.id)
232
+ deals.append(deal)
233
+
234
+ return deals
235
+
236
+
237
+ def filter_deals(
238
+ deals: list[OhouDeal],
239
+ *,
240
+ query: str | None = None,
241
+ min_discount: int | None = None,
242
+ free_delivery: bool = False,
243
+ include_sold_out: bool = False,
244
+ ) -> list[OhouDeal]:
245
+ """단일 루프로 모든 필터 조건을 검사한다."""
246
+ needle = query.casefold() if query else None
247
+ filtered: list[OhouDeal] = []
248
+
249
+ for deal in deals:
250
+ if not include_sold_out and deal.sold_out:
251
+ continue
252
+ if needle and needle not in deal.title.casefold() and needle not in (deal.brand or "").casefold():
253
+ continue
254
+ if min_discount is not None and (deal.best_discount_rate or deal.discount_rate or 0) < min_discount:
255
+ continue
256
+ if free_delivery and not deal.free_delivery:
257
+ continue
258
+ filtered.append(deal)
259
+
260
+ return filtered
261
+
262
+
263
+ def sort_deals(deals: list[OhouDeal], sort_key: str) -> list[OhouDeal]:
264
+ if sort_key == "discount":
265
+ return sorted(
266
+ deals,
267
+ key=lambda deal: (deal.best_discount_rate or deal.discount_rate or -1, deal.review_count),
268
+ reverse=True,
269
+ )
270
+ if sort_key == "price":
271
+ return sorted(deals, key=lambda deal: deal.best_price or deal.selling_price or sys.maxsize)
272
+ if sort_key == "review":
273
+ return sorted(deals, key=lambda deal: (deal.review_count, deal.review_average or 0), reverse=True)
274
+ if sort_key == "annual-sales":
275
+ return sorted(deals, key=lambda deal: deal.annual_sales or -1, reverse=True)
276
+ return deals
277
+
278
+
279
+ def build_payload(args: argparse.Namespace) -> dict[str, Any]:
280
+ document = Path(args.html_file).read_text(encoding="utf-8") if args.html_file else fetch_html(args.url)
281
+ payload = extract_next_data(document)
282
+ deals = extract_deals(payload)
283
+ filtered = filter_deals(
284
+ deals,
285
+ query=args.query,
286
+ min_discount=args.min_discount,
287
+ free_delivery=args.free_delivery,
288
+ include_sold_out=args.include_sold_out,
289
+ )
290
+ sorted_deals = sort_deals(filtered, args.sort)
291
+ limited_deals = sorted_deals[: args.limit]
292
+
293
+ kst = timezone(timedelta(hours=9))
294
+ now_utc = datetime.now(timezone.utc)
295
+
296
+ return {
297
+ "source": {
298
+ "name": "ohou-today-deal",
299
+ "url": args.url,
300
+ "fetched_at": now_utc.isoformat(),
301
+ "fetched_at_kst": now_utc.astimezone(kst).strftime("%Y-%m-%d %H:%M:%S KST"),
302
+ "surface": "__NEXT_DATA__ today-deal-feed + special-today-deal-feed",
303
+ },
304
+ "filters": {
305
+ "query": args.query,
306
+ "min_discount": args.min_discount,
307
+ "free_delivery": args.free_delivery,
308
+ "include_sold_out": args.include_sold_out,
309
+ "sort": args.sort,
310
+ "limit": args.limit,
311
+ },
312
+ "count": len(limited_deals),
313
+ "total_count": len(deals),
314
+ "filtered_count": len(filtered),
315
+ "items": [deal.to_dict() for deal in limited_deals],
316
+ }
317
+
318
+
319
+ def _positive_int(value: str) -> int:
320
+ try:
321
+ parsed = int(value)
322
+ except ValueError as exc:
323
+ raise argparse.ArgumentTypeError(f"expected integer, got {value!r}") from exc
324
+ if parsed <= 0:
325
+ raise argparse.ArgumentTypeError(f"must be a positive integer, got {parsed}")
326
+ return parsed
327
+
328
+
329
+ def _discount_rate(value: str) -> int:
330
+ try:
331
+ parsed = int(value)
332
+ except ValueError as exc:
333
+ raise argparse.ArgumentTypeError(f"expected integer, got {value!r}") from exc
334
+ if not 0 <= parsed <= 100:
335
+ raise argparse.ArgumentTypeError(
336
+ f"discount rate must be between 0 and 100, got {parsed}"
337
+ )
338
+ return parsed
339
+
340
+
341
+ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
342
+ parser = argparse.ArgumentParser(description="Read Ohouse today deal products from public HTML.")
343
+ subparsers = parser.add_subparsers(dest="command", required=True)
344
+
345
+ list_parser = subparsers.add_parser("list", help="오늘의집 오늘의딜 상품 목록")
346
+ list_parser.add_argument("--url", default=DEFAULT_URL)
347
+ list_parser.add_argument("--html-file", help="테스트/오프라인 검증용 HTML 또는 JSON 파일")
348
+ list_parser.add_argument("--query", help="상품명 또는 브랜드 키워드")
349
+ list_parser.add_argument("--min-discount", type=_discount_rate, help="최소 할인율 (0~100)")
350
+ list_parser.add_argument("--free-delivery", action="store_true", help="무료배송 상품만")
351
+ list_parser.add_argument("--include-sold-out", action="store_true", help="품절 상품 포함")
352
+ list_parser.add_argument(
353
+ "--sort",
354
+ choices=["default", "discount", "price", "review", "annual-sales"],
355
+ default="discount",
356
+ )
357
+ list_parser.add_argument("--limit", type=_positive_int, default=10, help="결과 개수 (양의 정수)")
358
+
359
+ return parser.parse_args(argv)
360
+
361
+
362
+ def main(argv: list[str] | None = None) -> None:
363
+ args = parse_args(argv)
364
+ if args.command == "list":
365
+ print(json.dumps(build_payload(args), ensure_ascii=False, indent=2))
366
+
367
+
368
+ if __name__ == "__main__":
369
+ main()
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "ohou-today-deal",
3
+ "description": "오늘의집 공개 오늘의딜 페이지에서 로그인 없이 특가 상품을 조회하고 할인율, 가격, 리뷰, 링크를 정리하는 읽기 전용 스킬. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.",
4
+ "profiles": [
5
+ "browser",
6
+ "action:commerce"
7
+ ],
8
+ "frontmatter": "name: ohou-today-deal\ndescription: 오늘의집 공개 오늘의딜 페이지에서 로그인 없이 특가 상품을 조회하고 할인율, 가격, 리뷰, 링크를 정리하는 읽기 전용 스킬. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.\nlicense: MIT\nmetadata:\n category: retail\n locale: ko-KR\n phase: v1"
9
+ }
@@ -0,0 +1,154 @@
1
+ # Olive Young Search
2
+
3
+ ## What this skill does
4
+
5
+ upstream 원본 [`hmmhmmhm/daiso-mcp`](https://github.com/hmmhmmhm/daiso-mcp) 와 npm package [`daiso`](https://www.npmjs.com/package/daiso) 를 그대로 사용해 **올리브영 매장 검색, 상품 검색, 재고 확인** 흐름을 안내한다.
6
+
7
+ 이 저장소는 원본 MCP 서버 코드를 vendoring 하지 않는다. 대신 **MCP 서버를 Claude Code에 직접 설치하지 않고 CLI 형태로 먼저 확인하는 경로**를 기본값으로 둔다.
8
+
9
+ 핵심 조회 경로:
10
+
11
+ - 매장 검색: `/api/oliveyoung/stores`
12
+ - 상품 검색: `/api/oliveyoung/products`
13
+ - 재고 확인: `/api/oliveyoung/inventory`
14
+ - health check: `npx --yes daiso health`
15
+
16
+ ## When to use
17
+
18
+ - "명동 근처 올리브영 매장 찾아줘"
19
+ - "올리브영 선크림 어떤 거 있나 보여줘"
20
+ - "명동 근처 올리브영에서 선크림 재고 확인해줘"
21
+ - "올리브영 검색용 CLI 붙여줘"
22
+
23
+ ## When not to use
24
+
25
+ - 로그인, 주문, 장바구니, 결제 자동화
26
+ - 올리브영 계정/세션이 필요한 private 기능
27
+ - upstream 서버 코드를 이 저장소 안에 복사해서 유지하려는 경우
28
+
29
+ ## Prerequisites
30
+
31
+ - 인터넷 연결
32
+ - `node` 20 권장 (`hmmhmmhm/daiso-mcp` 2026-04-05 기준 `engines.node` 는 `>=20 <21`)
33
+ - `npx` 또는 `npm`
34
+ - 필요하면 `git`
35
+
36
+ Node 22에서도 로컬 smoke test는 성공했지만 `EBADENGINE` 경고가 보여서, **안정 경로는 Node 20 LTS** 로 본다.
37
+
38
+ ## Preferred setup: CLI first, not direct MCP install
39
+
40
+ 가장 빠른 경로는 MCP 연결부터 하지 않고 upstream CLI로 공개 endpoint를 확인하는 것이다.
41
+
42
+ ```bash
43
+ npx --yes daiso health
44
+ npx --yes daiso get /api/oliveyoung/stores --keyword 명동 --limit 5 --json
45
+ npx --yes daiso get /api/oliveyoung/products --keyword 선크림 --size 5 --json
46
+ npx --yes daiso get /api/oliveyoung/inventory --keyword 선크림 --storeKeyword 명동 --size 5 --json
47
+ ```
48
+
49
+ 반복 사용이면 전역 설치도 가능하다.
50
+
51
+ ```bash
52
+ npm install -g daiso
53
+ export NODE_PATH="$(npm root -g)"
54
+ daiso health
55
+ ```
56
+
57
+ ## Fallback: clone the original repository and run the same CLI locally
58
+
59
+ public endpoint 재시도나 버전 고정이 필요하면 원본 저장소를 clone 해서 build 결과물 `dist/bin.js` 를 `node` 로 직접 실행한다.
60
+ clone checkout 안에서는 `npx daiso ...` 가 `Permission denied` 로 실패할 수 있으므로, local fallback은 아래 경로를 기본으로 둔다.
61
+
62
+ ```bash
63
+ git clone https://github.com/hmmhmmhm/daiso-mcp.git
64
+ cd daiso-mcp
65
+ npm install
66
+ npm run build
67
+ node dist/bin.js health
68
+ node dist/bin.js get /api/oliveyoung/stores --keyword 명동 --limit 5 --json
69
+ node dist/bin.js get /api/oliveyoung/products --keyword 선크림 --size 5 --json
70
+ node dist/bin.js get /api/oliveyoung/inventory --keyword 선크림 --storeKeyword 명동 --size 5 --json
71
+ ```
72
+
73
+ 즉, 이 스킬의 기본 원칙은 **원본 `hmmhmmhm/daiso-mcp`를 설치/실행해서 쓰고, `k-skill`에는 가이드만 추가하는 것**이다.
74
+
75
+ ## Required inputs
76
+
77
+ ### 1. Store or area keyword first when place context is missing
78
+
79
+ - 권장 질문: `어느 지역/매장을 기준으로 볼까요? 예: 명동, 강남역, 성수`
80
+ - 재고 질문인데 지역이 없으면 먼저 지역/매장 키워드를 받는다.
81
+
82
+ ### 2. Product keyword first when inventory is requested
83
+
84
+ - 권장 질문: `찾을 상품 키워드도 알려주세요. 예: 선크림, 립밤, 마스크팩`
85
+ - 상품 종류를 묻는 경우에도 키워드를 너무 넓게 받지 않는다.
86
+
87
+ ## Workflow
88
+
89
+ ### 1. Check server health
90
+
91
+ ```bash
92
+ npx --yes daiso health
93
+ ```
94
+
95
+ ### 2. Resolve nearby stores
96
+
97
+ ```bash
98
+ npx --yes daiso get /api/oliveyoung/stores --keyword 명동 --limit 5 --json
99
+ ```
100
+
101
+ 매장 후보가 여러 개면 상위 2~3개만 요약하고 다시 확인받는다.
102
+
103
+ ### 3. Resolve product candidates
104
+
105
+ ```bash
106
+ npx --yes daiso get /api/oliveyoung/products --keyword 선크림 --size 5 --json
107
+ ```
108
+
109
+ 상품 후보가 많으면 `goodsNumber`, 가격, 이미지 URL, `inStock` 여부를 함께 짧게 정리한다.
110
+
111
+ ### 4. Check inventory for the chosen area/store keyword
112
+
113
+ ```bash
114
+ npx --yes daiso get /api/oliveyoung/inventory --keyword 선크림 --storeKeyword 명동 --size 5 --json
115
+ ```
116
+
117
+ 응답의 `inventory.products[].storeInventory.stores[]` 안에서 다음 값을 우선 본다.
118
+
119
+ - `stockLabel`
120
+ - `remainQuantity`
121
+ - `stockStatus`
122
+ - `storeName`
123
+
124
+ ### 5. Respond conservatively
125
+
126
+ 최종 응답은 아래 순서로 짧게 정리한다.
127
+
128
+ - 기준 지역/매장 키워드
129
+ - 상위 매장 후보
130
+ - 상품 후보 또는 선택 상품
131
+ - 재고 있는 매장 / 품절 / 미판매 구분
132
+ - 필요하면 `imageUrl` 참고 링크
133
+ - 공개 endpoint 특성상 재고는 실시간 100% 보장값이 아니므로 방문 직전 재확인을 권장
134
+
135
+ ## Done when
136
+
137
+ - `hmmhmmhm/daiso-mcp` 원본 repo와 `daiso` CLI 사용 경로를 명시했다.
138
+ - MCP 서버를 직접 설치하는 대신 CLI first 흐름을 제시했다.
139
+ - 매장 검색 → 상품 검색 → 재고 확인 순서를 따랐다.
140
+ - `/api/oliveyoung/stores`, `/api/oliveyoung/products`, `/api/oliveyoung/inventory` 중 필요한 호출을 실제로 안내했다.
141
+ - 재고 결과를 매장별 `stockLabel` 중심으로 요약했다.
142
+
143
+ ## Failure modes
144
+
145
+ - public endpoint는 upstream 내부 수집 경로(Zyte 의존) 사정으로 간헐적인 5xx/503을 줄 수 있다.
146
+ - 지역 키워드가 너무 넓으면 멀리 떨어진 동명이점 매장이 섞일 수 있다.
147
+ - 인기 상품은 검색 결과가 많아 상위 몇 개만 먼저 확인받는 편이 안전하다.
148
+ - 재고 수량은 시점 차이로 실제 방문 시 달라질 수 있다.
149
+
150
+ ## Notes
151
+
152
+ - 원본 프로젝트: `https://github.com/hmmhmmhm/daiso-mcp`
153
+ - npm package: `https://www.npmjs.com/package/daiso`
154
+ - 이 저장소는 upstream 코드를 vendoring 하지 않고 skill/docs만 유지한다.
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "olive-young-search",
3
+ "description": "upstream daiso CLI를 사용해 올리브영 매장 검색, 상품 검색, 재고 확인을 조회한다. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.",
4
+ "profiles": [
5
+ "action:commerce"
6
+ ],
7
+ "frontmatter": "name: olive-young-search\ndescription: upstream daiso CLI를 사용해 올리브영 매장 검색, 상품 검색, 재고 확인을 조회한다. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.\nlicense: MIT\nmetadata:\n category: retail\n locale: ko-KR\n phase: v1"
8
+ }
@@ -0,0 +1,96 @@
1
+ # Parking Lot Search
2
+
3
+ ## What this skill does
4
+
5
+ 유저가 알려준 현재 위치를 기준으로 **근처 공영주차장** 을 찾는다.
6
+
7
+ - 위치는 자동 추정하지 않는다.
8
+ - **반드시 먼저 현재 위치를 질문**한다.
9
+ - 기본값은 `공영` 주차장만 보여준다.
10
+ - 공식 `전국주차장정보표준데이터` Open API를 사용한다.
11
+ - 위치 문자열은 Kakao Map anchor 검색으로 좌표를 잡은 뒤, 공식 주차장 데이터에서 거리순으로 정리한다.
12
+ - 실시간 만차/잔여면/예약 여부는 공식 표준데이터에 없으므로 확정해서 말하지 않는다.
13
+
14
+ ## When to use
15
+
16
+ - "근처 주차장 찾아줘"
17
+ - "광화문 주변 공영주차장 어디 있어?"
18
+ - "서울역 근처 무료 주차장 있어?"
19
+ - "지금 여기서 가까운 공영주차장 지도 링크 줘"
20
+
21
+ ## Mandatory first question
22
+
23
+ 위치 정보 없이 바로 검색하지 말고 반드시 먼저 물어본다.
24
+
25
+ 권장 질문:
26
+
27
+ `현재 위치를 알려주세요. 동네/역명/랜드마크/위도·경도 중 편한 형식으로 보내주시면 근처 공영주차장을 찾아볼게요.`
28
+
29
+ 위치가 애매하면:
30
+
31
+ `가까운 역명이나 동 이름으로 한 번만 더 알려주세요.`
32
+
33
+ ## Official surfaces
34
+
35
+ - 표준데이터 안내: `https://www.data.go.kr/data/15012896/standard.do`
36
+ - Open API 안내: `https://www.data.go.kr/data/15012896/openapi.do`
37
+ - Open API endpoint: `https://api.data.go.kr/openapi/tn_pubr_prkplce_info_api`
38
+ - k-skill proxy: `/v1/parking-lots/search`
39
+ - Kakao Map 모바일 검색: `https://m.map.kakao.com/actions/searchView?q=<query>`
40
+ - Kakao Map 장소 패널 JSON: `https://place-api.map.kakao.com/places/panel3/<confirmId>`
41
+
42
+ ## Workflow
43
+
44
+ 1. 유저에게 반드시 현재 위치를 묻는다.
45
+ 2. 위치 문자열을 받으면 Kakao Map으로 anchor 후보를 고르고 좌표를 확보한다.
46
+ 3. anchor 주소에서 `시도 + 시군구` address hint를 만든다.
47
+ 4. k-skill-proxy `/v1/parking-lots/search` 또는 `parking-lot-search` 패키지로 공식 주차장 데이터를 조회한다.
48
+ 5. 보통 3~5개만 짧게 정리하고, 지도 링크를 같이 준다.
49
+ 6. 요금/운영시간은 데이터 기준일자와 함께 안내하고, 실시간 현황이 아님을 밝힌다.
50
+
51
+ ## Responding
52
+
53
+ 결과는 보통 아래 필드를 포함해 짧게 정리한다.
54
+
55
+ - 주차장명
56
+ - 거리
57
+ - 공영/민영 및 노상/노외/부설 유형
58
+ - 주소
59
+ - 운영요일/운영시간
60
+ - 요금정보, 기본요금, 추가요금
61
+ - 주차구획수
62
+ - 전화번호/관리기관
63
+ - 데이터기준일자
64
+ - 지도 링크
65
+
66
+ ## Node.js example
67
+
68
+ ```js
69
+ const { searchNearbyParkingLotsByLocationQuery } = require("parking-lot-search");
70
+
71
+ async function main() {
72
+ const result = await searchNearbyParkingLotsByLocationQuery("광화문", {
73
+ limit: 3,
74
+ radius: 1500
75
+ });
76
+
77
+ console.log(result.anchor);
78
+ console.log(result.items);
79
+ }
80
+
81
+ main().catch((error) => {
82
+ console.error(error);
83
+ process.exitCode = 1;
84
+ });
85
+ ```
86
+
87
+ ## 모두의주차장 status
88
+
89
+ Issue #135에서 모두의주차장 연동 가능성을 언급했지만, v1은 공식 공공데이터 기반으로 시작한다. 승인된 공식/파트너 API 계약이 확인되기 전에는 모두의주차장 비공식 API 호출이나 scraping을 하지 않는다.
90
+
91
+ ## Done when
92
+
93
+ - 유저의 현재 위치를 먼저 확인했다.
94
+ - 공식 데이터 기반으로 최소 1개 이상 nearby parking lot을 찾았거나, 못 찾은 이유와 다음 질문을 제시했다.
95
+ - 가장 가까운 결과를 3~5개 이내로 정리했다.
96
+ - 실시간 잔여면/예약 가능 여부가 아님을 필요한 경우 명확히 했다.
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "parking-lot-search",
3
+ "description": "Use when the user asks for nearby parking lots or 근처 주차장/공영주차장. Always ask the user's current location first, then use official Data.go.kr public parking data through k-skill-proxy or the parking-lot-search package.",
4
+ "profiles": [
5
+ "proxy",
6
+ "lookup"
7
+ ],
8
+ "frontmatter": "name: parking-lot-search\ndescription: Use when the user asks for nearby parking lots or 근처 주차장/공영주차장. Always ask the user's current location first, then use official Data.go.kr public parking data through k-skill-proxy or the parking-lot-search package.\nlicense: MIT\nmetadata:\n category: convenience\n locale: ko-KR\n phase: v1"
9
+ }