@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,159 @@
1
+ # KOBUS HTTP/API Probe Notes
2
+
3
+ Session-proven on 2026-05-08. Goal: avoid browser automation where possible.
4
+
5
+ ## Base
6
+
7
+ ```text
8
+ https://www.kobus.co.kr
9
+ ```
10
+
11
+ Use a desktop User-Agent, HTTP/1.1 if needed, a cookie jar, and referers.
12
+
13
+ ## Tested Flow
14
+
15
+ ### Route / Terminal Candidates
16
+
17
+ ```text
18
+ POST /mrs/readRotLinInf.ajax
19
+ ```
20
+
21
+ Observed JSON keys:
22
+
23
+ ```text
24
+ tfrLen
25
+ tfrInfList
26
+ len
27
+ codeYn
28
+ rotInfList
29
+ ```
30
+
31
+ One probe returned about 1,208 route records in `rotInfList`.
32
+
33
+ ### Timetable
34
+
35
+ ```text
36
+ POST /mrs/alcnSrch.do
37
+ ```
38
+
39
+ Example tested route/date:
40
+
41
+ ```text
42
+ 서울경부(010) -> 부산(700), 2026-05-09
43
+ ```
44
+
45
+ Observed result:
46
+
47
+ ```text
48
+ 42 schedule links/cards
49
+ 25 selectable seat snippets
50
+ first selectable: 00:30 / 천일고속 / 심야우등 / 10 seats
51
+ ```
52
+
53
+ Typical POST fields:
54
+
55
+ ```text
56
+ deprCd=010
57
+ arvlCd=700
58
+ pathDvs=sngl
59
+ pathStep=1
60
+ deprDtm=YYYYMMDD
61
+ busClsCd=0
62
+ rtrpChc=1
63
+ timeLinkMin=00
64
+ timeLinkMax=23
65
+ ```
66
+
67
+ Parse `fnSatsChc(...)` onclick values for the next step. Example:
68
+
69
+ ```text
70
+ fnSatsChc('20260509','003000','003000','010','700','3','07','0','Y','N','010','700','N','N','N','N')
71
+ ```
72
+
73
+ ### Seat / Fare Stage
74
+
75
+ ```text
76
+ POST /mrs/satschc.do
77
+ ```
78
+
79
+ Send the original `alcnSrchFrm` hidden fields plus selected values from `fnSatsChc(...)`, including values such as:
80
+
81
+ ```text
82
+ deprTime=003000
83
+ alcnDeprTime=003000
84
+ alcnDeprTrmlNo=010
85
+ alcnArvlTrmlNo=700
86
+ indVBusClsCd=3
87
+ cacmCd=07
88
+ dcDvsCd=0
89
+ prvtBbizEmpAcmtRt=N
90
+ chldSftySatsYn=N
91
+ dsprSatsYn=N
92
+ ```
93
+
94
+ Observed response contained `form#satsChcFrm` and hidden values:
95
+
96
+ ```json
97
+ {
98
+ "deprTime": "003000",
99
+ "alcnDeprTrmlNo": "010",
100
+ "alcnArvlTrmlNo": "700",
101
+ "adltFee": "47600",
102
+ "rmnSatsNum": "10",
103
+ "totSatsNum": "28"
104
+ }
105
+ ```
106
+
107
+ ### Temporary Hold
108
+
109
+ ```text
110
+ POST /mrs/setPcpy.ajax
111
+ ```
112
+
113
+ Observed successful response markers:
114
+
115
+ ```text
116
+ MSG_CD=S0000
117
+ pcpyNoAll
118
+ satsNoAll
119
+ ESTM_AMT
120
+ DC_AMT
121
+ TISSU_AMT
122
+ ```
123
+
124
+ ### Hold Cancellation
125
+
126
+ ```text
127
+ POST /mrs/cancPcpy.ajax
128
+ ```
129
+
130
+ Observed success marker:
131
+
132
+ ```text
133
+ MSG_CD=S0000
134
+ ```
135
+
136
+ 2026-05-13 서울→광주 re-verification: `센트럴시티(서울)(021) -> 광주(유·스퀘어)(500)`, 2026-05-20 00:45 중앙고속 심야우등, seat 1. `/mrs/setPcpy.ajax` returned `MSG_CD=S0000`, `pcpyNoAll`, `satsNoAll=01`, `TISSU_AMT=36900`; `/mrs/stplcfmpym.do?keep=/mrs/pay` rendered the official payment-information page; `/mrs/cancPcpy.ajax` returned `MSG_CD=S0000`.
137
+
138
+ ### Checkout Entry
139
+
140
+ ```text
141
+ POST /mrs/stplcfmpym.do
142
+ ```
143
+
144
+ The POST body must include the selected schedule/seat form values plus temporary hold identifiers and fare amounts. A helper page can auto-submit this form to the official KOBUS endpoint.
145
+
146
+ For mobile browsers, use:
147
+
148
+ ```text
149
+ POST /mrs/stplcfmpym.do?keep=/mrs/pay
150
+ ```
151
+
152
+ This preserves the POST body while placing `/mrs/pay` in `location.href`, which avoids a KOBUS client-side mobile redirect condition observed in the common JavaScript.
153
+
154
+ ## Interpretation
155
+
156
+ - Login was not required for route lookup, timetable lookup, seat-selection-page entry, temporary hold, or checkout-entry page display in the tested flow.
157
+ - Page HTML can include login or `grecaptchaToken` forms, but these did not block the tested lookup/seat-stage path.
158
+ - Final payment should remain a manual, explicitly confirmed stage.
159
+ - KOBUS mobile behavior is less stable than desktop because common JavaScript can redirect narrow screens to the mobile main page.
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env python3
2
+ """KOBUS timetable lookup and temporary hold helper.
3
+
4
+ Default mode searches timetables. With --hold-first-seat or --hold-seat it creates
5
+ a temporary seat hold through /mrs/setPcpy.ajax and saves a local auto-submit
6
+ HTML helper for the official KOBUS payment-information page. It never submits
7
+ card fields or final payment.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import html
13
+ import http.cookiejar
14
+ import json
15
+ import re
16
+ import ssl
17
+ import sys
18
+ import tempfile
19
+ import urllib.parse
20
+ import urllib.request
21
+ from dataclasses import asdict, dataclass
22
+ from pathlib import Path
23
+ from typing import Iterable
24
+
25
+ BASE_URL = "https://www.kobus.co.kr"
26
+ UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/125 Safari/537.36"
27
+ FN_SATS_RE = re.compile(r"fnSatsChc\((.*?)\)", re.DOTALL)
28
+ ARG_RE = re.compile(r"'([^']*)'")
29
+ FORM_RE = re.compile(r"<form\b([^>]*)>(.*?)</form>", re.DOTALL | re.IGNORECASE)
30
+ INPUT_RE = re.compile(r"<input\b([^>]+)>", re.DOTALL | re.IGNORECASE)
31
+ ATTR_RE = re.compile(r"([\w:-]+)=[\"']([^\"']*)[\"']")
32
+ SEAT_RE = re.compile(r'<input\b([^>]*name=["\']seatBoxDtl["\'][^>]*)>', re.DOTALL | re.IGNORECASE)
33
+ TAG_RE = re.compile(r"<[^>]+>")
34
+
35
+
36
+ @dataclass
37
+ class Schedule:
38
+ index: int
39
+ departure_time: str | None
40
+ company: str | None
41
+ bus_class: str | None
42
+ remaining_text: str | None
43
+ raw_args: list[str]
44
+
45
+
46
+ @dataclass
47
+ class Hold:
48
+ success: bool
49
+ pcpy_no_all: str | None
50
+ sats_no_all: str | None
51
+ seat: str | None
52
+ estm_amt: str | None
53
+ dc_amt: str | None
54
+ tissu_amt: str | None
55
+ checkout_helper_path: str | None
56
+ checkout_response_path: str | None
57
+ cancel_fields_path: str | None
58
+ raw_response: dict[str, object]
59
+
60
+
61
+ def opener() -> urllib.request.OpenerDirector:
62
+ jar = http.cookiejar.CookieJar()
63
+ ctx = ssl._create_unverified_context()
64
+ try:
65
+ ctx.set_ciphers("DEFAULT@SECLEVEL=1")
66
+ except ssl.SSLError:
67
+ pass
68
+ return urllib.request.build_opener(
69
+ urllib.request.HTTPCookieProcessor(jar),
70
+ urllib.request.HTTPSHandler(context=ctx),
71
+ )
72
+
73
+
74
+ def request(url: str, data: dict[str, str] | list[tuple[str, str]] | None = None, referer: str | None = None) -> urllib.request.Request:
75
+ headers = {"User-Agent": UA}
76
+ if referer:
77
+ headers["Referer"] = referer
78
+ if data is None:
79
+ return urllib.request.Request(url, headers=headers, method="GET")
80
+ headers["Content-Type"] = "application/x-www-form-urlencoded"
81
+ return urllib.request.Request(url, data=urllib.parse.urlencode(data).encode(), headers=headers, method="POST")
82
+
83
+
84
+ def open_text(op: urllib.request.OpenerDirector, req: urllib.request.Request, timeout: int) -> str:
85
+ with op.open(req, timeout=timeout) as resp:
86
+ return resp.read().decode(resp.headers.get_content_charset() or "utf-8", errors="replace")
87
+
88
+
89
+ def attrs(fragment: str) -> dict[str, str]:
90
+ return {k.lower(): html.unescape(v) for k, v in ATTR_RE.findall(fragment)}
91
+
92
+
93
+ def strip_tags(s: str) -> str:
94
+ return re.sub(r"\s+", " ", html.unescape(TAG_RE.sub(" ", s))).strip()
95
+
96
+
97
+ def parse_form(body: str, form_id: str) -> list[tuple[str, str]]:
98
+ for attr_text, form_body in FORM_RE.findall(body):
99
+ a = attrs(attr_text)
100
+ if a.get("id") == form_id or a.get("name") == form_id:
101
+ fields = []
102
+ for input_text in INPUT_RE.findall(form_body):
103
+ ia = attrs(input_text)
104
+ if ia.get("name"):
105
+ fields.append((ia["name"], ia.get("value", "")))
106
+ return fields
107
+ return []
108
+
109
+
110
+ def search(op: urllib.request.OpenerDirector, depart: str, arrive: str, date: str, timeout: int) -> tuple[str, list[Schedule]]:
111
+ open_text(op, request(f"{BASE_URL}/main.do"), timeout)
112
+ body = open_text(
113
+ op,
114
+ request(
115
+ f"{BASE_URL}/mrs/alcnSrch.do",
116
+ {
117
+ "deprCd": depart,
118
+ "arvlCd": arrive,
119
+ "pathDvs": "sngl",
120
+ "pathStep": "1",
121
+ "deprDtm": date,
122
+ "busClsCd": "0",
123
+ "rtrpChc": "1",
124
+ "timeLinkMin": "00",
125
+ "timeLinkMax": "23",
126
+ },
127
+ f"{BASE_URL}/main.do",
128
+ ),
129
+ timeout,
130
+ )
131
+ schedules: list[Schedule] = []
132
+ for idx, m in enumerate(FN_SATS_RE.finditer(body), 1):
133
+ args = ARG_RE.findall(m.group(1))
134
+ context = strip_tags(body[max(0, m.start() - 900) : m.start() + 900])
135
+ departure = args[1][:2] + ":" + args[1][2:4] if len(args) > 1 and len(args[1]) >= 4 else None
136
+ schedules.append(
137
+ Schedule(
138
+ index=idx,
139
+ departure_time=departure,
140
+ company=(re.search(r"\((?:주|유)\)[^\s]+|[가-힣]+고속", context) or [None])[0],
141
+ bus_class=(re.search(r"심야우등|우등|프리미엄|고속", context) or [None])[0],
142
+ remaining_text=(re.search(r"잔여\s*\d+석|\d+\s*/\s*\d+", context) or [None])[0],
143
+ raw_args=args,
144
+ )
145
+ )
146
+ return body, schedules
147
+
148
+
149
+ def seat_stage_fields(search_form: list[tuple[str, str]], schedule: Schedule) -> list[tuple[str, str]]:
150
+ a = schedule.raw_args
151
+ values = dict(search_form)
152
+ updates = {
153
+ "deprTime": a[1],
154
+ "alcnDeprTime": a[2],
155
+ "alcnDeprTrmlNo": a[3],
156
+ "alcnArvlTrmlNo": a[4],
157
+ "indVBusClsCd": a[5],
158
+ "cacmCd": a[6],
159
+ "dcDvsCd": a[7],
160
+ "prvtBbizEmpAcmtRt": a[8],
161
+ "chldSftySatsYn": a[12],
162
+ "dsprSatsYn": a[13],
163
+ }
164
+ return [(k, updates.get(k, v)) for k, v in search_form]
165
+
166
+
167
+ def hold(op: urllib.request.OpenerDirector, alcn_body: str, schedule: Schedule, seat: str | None, out: Path, timeout: int) -> Hold:
168
+ search_form = parse_form(alcn_body, "alcnSrchFrm")
169
+ seat_body = open_text(op, request(f"{BASE_URL}/mrs/satschc.do", seat_stage_fields(search_form, schedule), f"{BASE_URL}/mrs/alcnSrch.do"), timeout)
170
+ fields = parse_form(seat_body, "satsChcFrm")
171
+ field_map = dict(fields)
172
+ seats = []
173
+ for input_text in SEAT_RE.findall(seat_body):
174
+ a = attrs(input_text)
175
+ if "disabled" not in input_text and a.get("value"):
176
+ seats.append(a["value"])
177
+ selected = seat or (seats[0] if seats else None)
178
+ if not selected:
179
+ raise RuntimeError("No selectable KOBUS seat found")
180
+
181
+ def set_field(items: list[tuple[str, str]], key: str, val: str) -> list[tuple[str, str]]:
182
+ return [(k, val if k == key else v) for k, v in items]
183
+
184
+ for key, val in {
185
+ "selSeatNum": selected,
186
+ "selSeatCnt": "1",
187
+ "selAdltCnt": "1",
188
+ "selAdltDcCnt": "0",
189
+ "prmmDcDvsCd": field_map.get("prmmDcDvsCd") or "0",
190
+ }.items():
191
+ fields = set_field(fields, key, val)
192
+ raw = json.loads(open_text(op, request(f"{BASE_URL}/mrs/setPcpy.ajax", fields, f"{BASE_URL}/mrs/satschc.do"), timeout))
193
+ success = raw.get("MSG_CD") == "S0000"
194
+ if not success:
195
+ return Hold(False, None, None, selected, None, None, None, None, None, None, raw)
196
+
197
+ for key, val in {
198
+ "satsNoAll": str(raw.get("satsNoAll", "")),
199
+ "pcpyNoAll": str(raw.get("pcpyNoAll", "")),
200
+ "estmAmt": str(raw.get("ESTM_AMT", "")),
201
+ "dcAmt": str(raw.get("DC_AMT", "")),
202
+ "tissuAmt": str(raw.get("TISSU_AMT", "")),
203
+ "nonMbrsYn": "Y",
204
+ }.items():
205
+ fields = set_field(fields, key, val)
206
+
207
+ out.mkdir(parents=True, exist_ok=True)
208
+ checkout = open_text(op, request(f"{BASE_URL}/mrs/stplcfmpym.do?keep=/mrs/pay", fields, f"{BASE_URL}/mrs/satschc.do"), timeout)
209
+ checkout_path = out / "kobus-checkout-response.html"
210
+ checkout_path.write_text(checkout)
211
+ helper_path = out / "kobus-payment-autosubmit.html"
212
+ inputs = "\n".join(f'<input type="hidden" name="{html.escape(k)}" value="{html.escape(v, quote=True)}">' for k, v in fields)
213
+ helper_path.write_text(f'<!doctype html><meta charset="utf-8"><p>공식 KOBUS 결제정보 입력 페이지로 이동합니다. 결제는 직접 진행하세요.</p><form id="f" method="post" action="{BASE_URL}/mrs/stplcfmpym.do?keep=/mrs/pay">{inputs}</form><script>document.getElementById("f").submit();</script>')
214
+ cancel_path = out / "kobus-cancel-fields.txt"
215
+ cancel_path.write_text("\n".join(f"{k}={v}" for k, v in fields))
216
+ return Hold(True, str(raw.get("pcpyNoAll")), str(raw.get("satsNoAll")), selected, str(raw.get("ESTM_AMT")), str(raw.get("DC_AMT")), str(raw.get("TISSU_AMT")), str(helper_path), str(checkout_path), str(cancel_path), raw)
217
+
218
+
219
+ def main(argv: Iterable[str] | None = None) -> int:
220
+ p = argparse.ArgumentParser()
221
+ p.add_argument("--depart-code", required=True)
222
+ p.add_argument("--arrive-code", required=True)
223
+ p.add_argument("--date", required=True)
224
+ p.add_argument("--select-index", type=int, default=1)
225
+ p.add_argument("--hold-first-seat", action="store_true")
226
+ p.add_argument("--hold-seat")
227
+ p.add_argument("--output-dir")
228
+ p.add_argument("--limit", type=int, default=20)
229
+ p.add_argument("--timeout", type=int, default=20)
230
+ args = p.parse_args(argv)
231
+ op = opener()
232
+ body, schedules = search(op, args.depart_code, args.arrive_code, args.date, args.timeout)
233
+ result: dict[str, object] = {"route": {"depart_code": args.depart_code, "arrive_code": args.arrive_code, "date": args.date}, "count": len(schedules), "items": [asdict(s) for s in schedules[: args.limit]]}
234
+ if (args.hold_first_seat or args.hold_seat) and schedules:
235
+ out = Path(args.output_dir) if args.output_dir else Path(tempfile.mkdtemp(prefix="kobus-hold-"))
236
+ result["hold"] = asdict(hold(op, body, schedules[args.select_index - 1], args.hold_seat, out, args.timeout))
237
+ result["payment_note"] = "Opened/saved the official KOBUS payment-information page; final card entry/payment remains manual."
238
+ print(json.dumps(result, ensure_ascii=False, indent=2))
239
+ return 0
240
+
241
+
242
+ if __name__ == "__main__":
243
+ sys.exit(main())
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "express-bus-booking",
3
+ "description": "Search and assist Korean 고속버스/KOBUS bookings using official HTTP/API-first flows; use for 고속버스 예매, 시간표, 좌석 조회, 임시 선점, and official checkout-entry handoff. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.",
4
+ "profiles": [
5
+ "action:booking"
6
+ ],
7
+ "frontmatter": "name: express-bus-booking\ndescription: Search and assist Korean 고속버스/KOBUS bookings using official HTTP/API-first flows; use for 고속버스 예매, 시간표, 좌석 조회, 임시 선점, and official checkout-entry handoff. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.\nlicense: MIT\nmetadata:\n category: travel\n locale: ko-KR\n phase: v1"
8
+ }
@@ -0,0 +1,89 @@
1
+ # Fine Dust By Location
2
+
3
+ ## What this skill does
4
+
5
+ 기본적으로 `https://k-skill-proxy.nomadamas.org/v1/fine-dust/report` 로 요청해서 PM10 / PM2.5 / 통합대기등급을 요약한다.
6
+
7
+ ## When to use
8
+
9
+ - "지금 내 위치 미세먼지 어때?"
10
+ - "강남 쪽 초미세먼지 수치 알려줘"
11
+ - "여기 공기질 괜찮아?"
12
+
13
+ ## Inputs
14
+
15
+ - 일반 입력: 지역명/행정구역 힌트
16
+ - 재조회 입력: 정확한 측정소명
17
+
18
+ ## Region naming convention
19
+
20
+ 지역명은 아래처럼 **측정소명에 가까운 한국어 행정구역 이름**을 우선 사용한다.
21
+
22
+ - 좋음: `강남구`, `서울 강남구`, `종로구`, `수원시`
23
+ - 애매함: `강남`, `서울 남쪽`, `코엑스 근처`
24
+
25
+ 여러 토큰이 들어오면 helper / proxy 는 보통 **가장 구체적인 토큰**을 우선 본다. 예: `서울 강남구` → `강남구`.
26
+
27
+ ## Default path
28
+
29
+ 추가 client API 레이어는 불필요하다. 그냥 프록시 서버에 HTTP 요청만 넣으면 된다.
30
+
31
+ ```bash
32
+ curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/fine-dust/report' \
33
+ --data-urlencode 'regionHint=서울 강남구'
34
+ ```
35
+
36
+ 스크립트 helper 도 같은 report endpoint 를 기본 경로로 사용한다.
37
+
38
+ ```bash
39
+ python3 scripts/fine_dust.py report --region-hint '서울 강남구' --json
40
+ ```
41
+
42
+ ## Ambiguous locations
43
+
44
+ 입력한 지역명이 단일 측정소로 바로 확정되지 않으면 proxy 는 `ambiguous_location` 과 함께 후보 측정소 목록을 돌려준다.
45
+
46
+ 예:
47
+
48
+ ```bash
49
+ curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/fine-dust/report' \
50
+ --data-urlencode 'regionHint=광주 광산구'
51
+ ```
52
+
53
+ 이때 응답의 `candidate_stations` 중 하나를 골라 다시 `stationName` 으로 조회한다.
54
+
55
+ ```bash
56
+ curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/fine-dust/report' \
57
+ --data-urlencode 'stationName=우산동(광주)'
58
+ ```
59
+
60
+ ## Detailed API paths
61
+
62
+ 원본 AirKorea와 비슷한 passthrough 경로(`/B552584/...`)나 direct fallback 상세는 아래 문서만 참고한다.
63
+
64
+ - `docs/features/fine-dust-location.md`
65
+ - `docs/features/k-skill-proxy.md`
66
+
67
+ ## Keep the answer compact
68
+
69
+ 응답에는 아래만 먼저 정리한다.
70
+
71
+ - 측정소
72
+ - 조회 시각
73
+ - PM10 값과 등급
74
+ - PM2.5 값과 등급
75
+ - 통합대기등급
76
+ - 조회 방식(`fallback`)
77
+
78
+ ## Failure modes
79
+
80
+ - regionHint 가 너무 넓거나 단일 측정소를 확정할 수 없는 경우
81
+ - 프록시 서버가 내려가 있거나 upstream key가 비어 있는 경우
82
+ - 측정소명과 지역명이 달라 직접 fallback 이 필요한 경우
83
+
84
+ ## Notes
85
+
86
+ - 기본 경로는 항상 `k-skill-proxy.nomadamas.org` 의 report endpoint 다.
87
+ - 지역명 조회는 먼저 후보를 얻고, 필요하면 정확한 측정소명으로 재조회한다.
88
+ - passthrough / direct AirKorea 구현 세부는 스킬 본문에 길게 반복하지 않는다.
89
+ - free API 프록시는 공개 endpoint 를 기본으로 둔다.
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "fine-dust-location",
3
+ "description": "에어코리아 기반 미세먼지/초미세먼지를 지역명 또는 위치 힌트로 조회한다. 기본 경로는 k-skill-proxy의 report endpoint다.",
4
+ "profiles": [
5
+ "proxy",
6
+ "lookup"
7
+ ],
8
+ "frontmatter": "name: fine-dust-location\ndescription: 에어코리아 기반 미세먼지/초미세먼지를 지역명 또는 위치 힌트로 조회한다. 기본 경로는 k-skill-proxy의 report endpoint다.\nlicense: MIT\nmetadata:\n category: utility\n locale: ko-KR\n phase: v1"
9
+ }