@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,409 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import os
6
+ import sys
7
+ import urllib.error
8
+ import urllib.parse
9
+ import urllib.request
10
+ import xml.etree.ElementTree as ET
11
+ from dataclasses import asdict, dataclass
12
+ from html.parser import HTMLParser
13
+ from typing import Callable
14
+
15
+ SERVICE_KEY_ENV_VAR = "KIPRIS_PLUS_API_KEY"
16
+ DEFAULT_TIMEOUT = 30
17
+ DEFAULT_NUM_ROWS = 10
18
+ DEFAULT_PAGE_NO = 1
19
+ BASE_API_URL = "https://plus.kipris.or.kr/kipo-api/kipi/patUtiModInfoSearchSevice"
20
+ SEARCH_OPERATION = "getWordSearch"
21
+ DETAIL_OPERATION = "getBibliographyDetailInfoSearch"
22
+ DEFAULT_HEADERS = {
23
+ "Accept": "application/xml,text/xml;q=0.9,*/*;q=0.8",
24
+ "User-Agent": (
25
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
26
+ "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
27
+ ),
28
+ }
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class PatentSearchResult:
33
+ index_no: int | None
34
+ application_number: str
35
+ invention_title: str | None
36
+ register_status: str | None
37
+ application_date: str | None
38
+ open_number: str | None
39
+ open_date: str | None
40
+ publication_number: str | None
41
+ publication_date: str | None
42
+ register_number: str | None
43
+ register_date: str | None
44
+ ipc_number: str | None
45
+ abstract_text: str | None
46
+ applicant_name: str | None
47
+ drawing: str | None
48
+ big_drawing: str | None
49
+
50
+
51
+ @dataclass(frozen=True)
52
+ class PatentSearchResponse:
53
+ query: str
54
+ page_no: int
55
+ num_of_rows: int
56
+ total_count: int
57
+ items: list[PatentSearchResult]
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class PatentDetail:
62
+ application_number: str
63
+ invention_title: str | None
64
+ register_status: str | None
65
+ application_date: str | None
66
+ open_number: str | None
67
+ open_date: str | None
68
+ publication_number: str | None
69
+ publication_date: str | None
70
+ register_number: str | None
71
+ register_date: str | None
72
+ ipc_number: str | None
73
+ abstract_text: str | None
74
+ applicant_name: str | None
75
+ drawing: str | None
76
+ big_drawing: str | None
77
+
78
+
79
+ @dataclass
80
+ class XmlNode:
81
+ tag: str
82
+ children: list["XmlNode"]
83
+ text_chunks: list[str]
84
+
85
+ @property
86
+ def text(self) -> str:
87
+ return "".join(self.text_chunks)
88
+
89
+
90
+ class XmlNodeBuilder(HTMLParser):
91
+ def __init__(self) -> None:
92
+ super().__init__(convert_charrefs=True)
93
+ self.root: XmlNode | None = None
94
+ self.stack: list[XmlNode] = []
95
+
96
+ def handle_starttag(self, tag: str, attrs) -> None: # type: ignore[override]
97
+ node = XmlNode(tag=tag, children=[], text_chunks=[])
98
+ if self.stack:
99
+ self.stack[-1].children.append(node)
100
+ else:
101
+ self.root = node
102
+ self.stack.append(node)
103
+
104
+ def handle_endtag(self, tag: str) -> None: # type: ignore[override]
105
+ if self.stack:
106
+ self.stack.pop()
107
+
108
+ def handle_data(self, data: str) -> None: # type: ignore[override]
109
+ if self.stack:
110
+ self.stack[-1].text_chunks.append(data)
111
+
112
+
113
+ def clean_text(value: str | None) -> str | None:
114
+ if value is None:
115
+ return None
116
+ cleaned = " ".join(value.split()).strip()
117
+ return cleaned or None
118
+
119
+
120
+ def parse_positive_int(raw_value: str) -> int:
121
+ value = int(raw_value)
122
+ if value <= 0:
123
+ raise argparse.ArgumentTypeError("must be a positive integer")
124
+ return value
125
+
126
+
127
+ def resolve_service_key(explicit_key: str | None = None) -> str:
128
+ candidate = clean_text(explicit_key) or clean_text(os.getenv(SERVICE_KEY_ENV_VAR))
129
+ if candidate:
130
+ return urllib.parse.unquote(candidate)
131
+ raise ValueError(
132
+ f"missing {SERVICE_KEY_ENV_VAR}. Export {SERVICE_KEY_ENV_VAR} or pass --service-key "
133
+ "(mapped to the KIPRIS Plus ServiceKey query parameter)."
134
+ )
135
+
136
+
137
+ def build_operation_url(operation: str) -> str:
138
+ return f"{BASE_API_URL}/{operation}"
139
+
140
+
141
+ def build_search_params(
142
+ *,
143
+ query: str,
144
+ year: int | None = None,
145
+ page_no: int = DEFAULT_PAGE_NO,
146
+ num_of_rows: int = DEFAULT_NUM_ROWS,
147
+ patent: bool = True,
148
+ utility: bool = True,
149
+ service_key: str,
150
+ ) -> dict[str, str]:
151
+ if not patent and not utility:
152
+ raise ValueError("At least one of patent or utility must remain enabled for keyword search.")
153
+ params = {
154
+ "word": query,
155
+ "patent": "true" if patent else "false",
156
+ "utility": "true" if utility else "false",
157
+ "pageNo": str(page_no),
158
+ "numOfRows": str(num_of_rows),
159
+ "ServiceKey": urllib.parse.unquote(service_key),
160
+ }
161
+ if year is not None:
162
+ params["year"] = str(year)
163
+ return params
164
+
165
+
166
+ def build_detail_params(*, application_number: str, service_key: str) -> dict[str, str]:
167
+ return {"applicationNumber": application_number, "ServiceKey": urllib.parse.unquote(service_key)}
168
+
169
+
170
+ def fetch_xml(url: str, params: dict[str, str], timeout: int = DEFAULT_TIMEOUT) -> str:
171
+ request_url = f"{url}?{urllib.parse.urlencode(params)}"
172
+ request = urllib.request.Request(request_url, headers=DEFAULT_HEADERS)
173
+ try:
174
+ with urllib.request.urlopen(request, timeout=timeout) as response:
175
+ return response.read().decode("utf-8", errors="replace")
176
+ except urllib.error.HTTPError as exc:
177
+ body = exc.read().decode("utf-8", errors="replace")
178
+ raise RuntimeError(f"KIPRIS Plus HTTP {exc.code}: {body or exc.reason}") from exc
179
+ except urllib.error.URLError as exc:
180
+ raise RuntimeError(f"Failed to reach KIPRIS Plus API: {exc.reason}") from exc
181
+
182
+
183
+ def normalize_tag(tag_name: str) -> str:
184
+ return tag_name.casefold()
185
+
186
+
187
+ def iter_children(element: ET.Element | XmlNode | None) -> list[ET.Element | XmlNode]:
188
+ if element is None:
189
+ return []
190
+ if isinstance(element, XmlNode):
191
+ return element.children
192
+ return list(element)
193
+
194
+
195
+ def find_child(element: ET.Element | XmlNode | None, tag_name: str) -> ET.Element | XmlNode | None:
196
+ normalized_tag = normalize_tag(tag_name)
197
+ for child in iter_children(element):
198
+ if normalize_tag(child.tag) == normalized_tag:
199
+ return child
200
+ return None
201
+
202
+
203
+ def find_children(element: ET.Element | XmlNode | None, tag_name: str) -> list[ET.Element | XmlNode]:
204
+ normalized_tag = normalize_tag(tag_name)
205
+ return [child for child in iter_children(element) if normalize_tag(child.tag) == normalized_tag]
206
+
207
+
208
+ def parse_xml_with_fallback(xml_text: str) -> XmlNode:
209
+ parser = XmlNodeBuilder()
210
+ try:
211
+ parser.feed(xml_text)
212
+ parser.close()
213
+ except Exception as exc: # pragma: no cover - defensive fallback guard
214
+ raise RuntimeError(f"Failed to parse KIPRIS Plus XML response: {exc}") from exc
215
+ if parser.root is None:
216
+ raise RuntimeError("Failed to parse KIPRIS Plus XML response: empty document")
217
+ return parser.root
218
+
219
+
220
+ def get_child_text(element: ET.Element | XmlNode | None, tag_name: str) -> str | None:
221
+ child = find_child(element, tag_name)
222
+ return clean_text(child.text if child is not None else None)
223
+
224
+
225
+ def parse_int(value: str | None) -> int | None:
226
+ if value is None:
227
+ return None
228
+ return int(value)
229
+
230
+
231
+ def parse_xml_response(xml_text: str) -> ET.Element | XmlNode:
232
+ try:
233
+ root = ET.fromstring(xml_text)
234
+ except (ET.ParseError, ImportError):
235
+ root = parse_xml_with_fallback(xml_text)
236
+
237
+ header = find_child(root, "header")
238
+ result_code = get_child_text(header, "resultCode")
239
+ result_msg = get_child_text(header, "resultMsg")
240
+ if result_code and result_code != "00":
241
+ raise RuntimeError(result_msg or f"KIPRIS Plus API error code {result_code}")
242
+ return root
243
+
244
+
245
+ def parse_patent_item(item: ET.Element | XmlNode) -> PatentSearchResult:
246
+ application_number = get_child_text(item, "applicationNumber")
247
+ if not application_number:
248
+ raise RuntimeError("KIPRIS Plus response item is missing applicationNumber")
249
+
250
+ return PatentSearchResult(
251
+ index_no=parse_int(get_child_text(item, "indexNo")),
252
+ application_number=application_number,
253
+ invention_title=get_child_text(item, "inventionTitle"),
254
+ register_status=get_child_text(item, "registerStatus"),
255
+ application_date=get_child_text(item, "applicationDate"),
256
+ open_number=get_child_text(item, "openNumber"),
257
+ open_date=get_child_text(item, "openDate"),
258
+ publication_number=get_child_text(item, "publicationNumber"),
259
+ publication_date=get_child_text(item, "publicationDate"),
260
+ register_number=get_child_text(item, "registerNumber"),
261
+ register_date=get_child_text(item, "registerDate"),
262
+ ipc_number=get_child_text(item, "ipcNumber"),
263
+ abstract_text=get_child_text(item, "astrtCont"),
264
+ applicant_name=get_child_text(item, "applicantName"),
265
+ drawing=get_child_text(item, "drawing"),
266
+ big_drawing=get_child_text(item, "bigDrawing"),
267
+ )
268
+
269
+
270
+ def parse_patent_search_response(xml_text: str, *, query: str) -> PatentSearchResponse:
271
+ root = parse_xml_response(xml_text)
272
+ body = find_child(root, "body")
273
+ items_parent = find_child(body, "items")
274
+ item_elements = find_children(items_parent, "item")
275
+ items = [parse_patent_item(item) for item in item_elements]
276
+ return PatentSearchResponse(
277
+ query=query,
278
+ page_no=parse_int(get_child_text(body, "pageNo")) or DEFAULT_PAGE_NO,
279
+ num_of_rows=parse_int(get_child_text(body, "numOfRows")) or len(items),
280
+ total_count=parse_int(get_child_text(body, "totalCount")) or len(items),
281
+ items=items,
282
+ )
283
+
284
+
285
+ def parse_patent_detail_response(xml_text: str) -> PatentDetail:
286
+ root = parse_xml_response(xml_text)
287
+ body = find_child(root, "body")
288
+ item = find_child(body, "item")
289
+ if item is None and body is not None:
290
+ items_parent = find_child(body, "items")
291
+ item = find_child(items_parent, "item")
292
+ if item is None:
293
+ raise RuntimeError("KIPRIS Plus detail response did not include an item payload")
294
+
295
+ search_item = parse_patent_item(item)
296
+ return PatentDetail(
297
+ application_number=search_item.application_number,
298
+ invention_title=search_item.invention_title,
299
+ register_status=search_item.register_status,
300
+ application_date=search_item.application_date,
301
+ open_number=search_item.open_number,
302
+ open_date=search_item.open_date,
303
+ publication_number=search_item.publication_number,
304
+ publication_date=search_item.publication_date,
305
+ register_number=search_item.register_number,
306
+ register_date=search_item.register_date,
307
+ ipc_number=search_item.ipc_number,
308
+ abstract_text=search_item.abstract_text,
309
+ applicant_name=search_item.applicant_name,
310
+ drawing=search_item.drawing,
311
+ big_drawing=search_item.big_drawing,
312
+ )
313
+
314
+
315
+ def search_patents(
316
+ query: str,
317
+ *,
318
+ year: int | None = None,
319
+ page_no: int = DEFAULT_PAGE_NO,
320
+ num_of_rows: int = DEFAULT_NUM_ROWS,
321
+ patent: bool = True,
322
+ utility: bool = True,
323
+ service_key: str | None = None,
324
+ fetcher: Callable[[str, dict[str, str], int], str] = fetch_xml,
325
+ timeout: int = DEFAULT_TIMEOUT,
326
+ ) -> PatentSearchResponse:
327
+ key = resolve_service_key(service_key)
328
+ xml_text = fetcher(
329
+ build_operation_url(SEARCH_OPERATION),
330
+ build_search_params(
331
+ query=query,
332
+ year=year,
333
+ page_no=page_no,
334
+ num_of_rows=num_of_rows,
335
+ patent=patent,
336
+ utility=utility,
337
+ service_key=key,
338
+ ),
339
+ timeout,
340
+ )
341
+ return parse_patent_search_response(xml_text, query=query)
342
+
343
+
344
+ def get_patent_detail(
345
+ application_number: str,
346
+ *,
347
+ service_key: str | None = None,
348
+ fetcher: Callable[[str, dict[str, str], int], str] = fetch_xml,
349
+ timeout: int = DEFAULT_TIMEOUT,
350
+ ) -> PatentDetail:
351
+ key = resolve_service_key(service_key)
352
+ xml_text = fetcher(
353
+ build_operation_url(DETAIL_OPERATION),
354
+ build_detail_params(application_number=application_number, service_key=key),
355
+ timeout,
356
+ )
357
+ return parse_patent_detail_response(xml_text)
358
+
359
+
360
+ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
361
+ parser = argparse.ArgumentParser(
362
+ description="Search Korean patent information via the official KIPRIS Plus Open API."
363
+ )
364
+ mode = parser.add_mutually_exclusive_group(required=True)
365
+ mode.add_argument("--query", help="Keyword for KIPRIS getWordSearch")
366
+ mode.add_argument("--application-number", help="Application number for bibliography detail lookup")
367
+ parser.add_argument("--year", type=parse_positive_int, help="Optional year filter for keyword search")
368
+ parser.add_argument("--page-no", type=parse_positive_int, default=DEFAULT_PAGE_NO, help="Response page number")
369
+ parser.add_argument("--num-rows", type=parse_positive_int, default=DEFAULT_NUM_ROWS, help="Rows per page")
370
+ parser.add_argument("--service-key", help=f"KIPRIS Plus ServiceKey (defaults to ${SERVICE_KEY_ENV_VAR})")
371
+ parser.add_argument("--exclude-patent", action="store_true", help="Exclude patent results from keyword search")
372
+ parser.add_argument("--exclude-utility", action="store_true", help="Exclude utility-model results from keyword search")
373
+ parser.add_argument("--timeout", type=parse_positive_int, default=DEFAULT_TIMEOUT, help="HTTP timeout seconds")
374
+ return parser.parse_args(argv)
375
+
376
+
377
+ def main(argv: list[str] | None = None) -> int:
378
+ args = parse_args(argv)
379
+ try:
380
+ if args.query:
381
+ payload = search_patents(
382
+ args.query,
383
+ year=args.year,
384
+ page_no=args.page_no,
385
+ num_of_rows=args.num_rows,
386
+ patent=not args.exclude_patent,
387
+ utility=not args.exclude_utility,
388
+ service_key=args.service_key,
389
+ timeout=args.timeout,
390
+ )
391
+ else:
392
+ payload = get_patent_detail(
393
+ args.application_number,
394
+ service_key=args.service_key,
395
+ timeout=args.timeout,
396
+ )
397
+ except ValueError as exc:
398
+ print(str(exc), file=sys.stderr)
399
+ return 2
400
+ except RuntimeError as exc:
401
+ print(str(exc), file=sys.stderr)
402
+ return 1
403
+
404
+ print(json.dumps(asdict(payload), ensure_ascii=False, indent=2))
405
+ return 0
406
+
407
+
408
+ if __name__ == "__main__": # pragma: no cover
409
+ raise SystemExit(main())
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "korean-patent-search",
3
+ "description": "Search Korean patent and utility-model publications through the official KIPRIS Plus Open API with keyword search plus application-number detail lookup.",
4
+ "profiles": [
5
+ "vault",
6
+ "lookup"
7
+ ],
8
+ "frontmatter": "name: korean-patent-search\ndescription: Search Korean patent and utility-model publications through the official KIPRIS Plus Open API with keyword search plus application-number detail lookup.\nlicense: MIT\nmetadata:\n category: ip\n locale: ko-KR\n phase: v1"
9
+ }
@@ -0,0 +1,128 @@
1
+ # Korean Privacy & Terms (thin wrapper)
2
+
3
+ ## What this skill does
4
+
5
+ [`kimlawtech/korean-privacy-terms`](https://github.com/kimlawtech/korean-privacy-terms) (Apache-2.0) ์—…์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•ด ํ•œ๊ตญ ๋ฒ•๋ น ๊ธฐ๋ฐ˜ **๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจยท์ด์šฉ์•ฝ๊ด€ยท์ฟ ํ‚ค ๋ฐฐ๋„ˆยทํšŒ์›๊ฐ€์ž… ๋™์˜ ๋ชจ๋‹ฌ** ์ƒ์„ฑ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. k-skill ์ธก์€ **์–‡์€ wrapper** ๋งŒ ์œ ์ง€ํ•˜๊ณ , ์‹ค์ œ ์ธํ„ฐ๋ทฐ/๋ Œ๋”/์„ค์น˜ ๋กœ์ง์€ ์—…์ŠคํŠธ๋ฆผ์— ์œ„์ž„ํ•œ๋‹ค.
6
+
7
+ ๋ฐ˜์˜ ๊ธฐ์ค€์€ ์—…์ŠคํŠธ๋ฆผ์ด ๊ด€๋ฆฌํ•œ๋‹ค:
8
+
9
+ - ๊ฐœ์ธ์ •๋ณด์œ„์›ํšŒ ์ฒ˜๋ฆฌ๋ฐฉ์นจ ์ž‘์„ฑ์ง€์นจ (2025.4.21)
10
+ - ๊ฐœ์ • ๊ฐœ์ธ์ •๋ณด๋ณดํ˜ธ๋ฒ• (2026.3 ๊ณตํฌ, 2026.9.11 ์‹œํ–‰, ๊ณผ์ง•๊ธˆ ๋งค์ถœ์•ก ์ตœ๋Œ€ 10%)
11
+ - ๊ณต์ •๊ฑฐ๋ž˜์œ„์›ํšŒ ์ „์ž์ƒ๊ฑฐ๋ž˜ ํ‘œ์ค€์•ฝ๊ด€ ์ œ10023ํ˜ธ
12
+ - EU GDPR / ePrivacy (์—…์ŠคํŠธ๋ฆผ v2.0+ ์ง€์›)
13
+
14
+ ์ด ์Šคํ‚ฌ์ด ์ƒ์„ฑํ•˜๋Š” ๋ชจ๋“  ๋ฌธ์„œ๋Š” **์ฐธ๊ณ ์šฉ ์ดˆ์•ˆ**์ด๋ฉฐ **๋ฒ•๋ฅ  ์ž๋ฌธ์ด ์•„๋‹ˆ๋‹ค**. ์‹ค์„œ๋น„์Šค ๋ฐฐํฌ ์ „ ๋ฐ˜๋“œ์‹œ **๋ณ€ํ˜ธ์‚ฌ ๊ฒ€ํ† **๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
15
+
16
+ ## When to use
17
+
18
+ - "๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจ ๋งŒ๋“ค์–ด์ค˜"
19
+ - "์ด์šฉ์•ฝ๊ด€ ์ถ”๊ฐ€ํ•ด์ค˜"
20
+ - "์ฟ ํ‚ค ๋ฐฐ๋„ˆ ๋ถ™์—ฌ์ค˜"
21
+ - "ํšŒ์›๊ฐ€์ž… ๋™์˜ ๋ชจ๋‹ฌ ํ•„์š”ํ•ด"
22
+ - "ํ–‰ํƒœ์ •๋ณด ๊ณ ์ง€ ์ถ”๊ฐ€ํ•ด์ค˜"
23
+ - "๊ฐœ์ธ์ •๋ณด ๋™์˜ UI ๊นŽ์•„์ค˜"
24
+
25
+ ## When not to use
26
+
27
+ - ์‹ค์ œ ๋ฒ•๋ฅ  ์ž๋ฌธ ยท ์†Œ์†ก ์ „๋žต ยท ๊ฐœ๋ณ„ ์•ฝ๊ด€ ๋ถ„์Ÿ ํŒ๋‹จ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ โ†’ ๋ณ€ํ˜ธ์‚ฌ ์ƒ๋‹ด
28
+ - ํ•œ๊ตญ/EU ์™ธ ๊ด€ํ• (๋ฏธ๊ตญ CCPA ยท ์ผ๋ณธ APPI ยท ์ค‘๊ตญ PIPL ๋“ฑ) ์„ ๋‹จ๋…์œผ๋กœ ๋‹ค๋ค„์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ (์—…์ŠคํŠธ๋ฆผ ๋กœ๋“œ๋งต)
29
+ - Next.js App Router ๊ฐ€ ์•„๋‹Œ ํ™˜๊ฒฝ (์—…์ŠคํŠธ๋ฆผ์ด Pages Router ๋ฏธ์ง€์›)
30
+
31
+ ## Prerequisites
32
+
33
+ - ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ (์—…์ŠคํŠธ๋ฆผ clone ์šฉ)
34
+ - `git` 2.20+
35
+ - `bash`
36
+ - ๋Œ€์ƒ ํ”„๋กœ์ ํŠธ๊ฐ€ Next.js 13 ~ 16 App Router ๊ธฐ๋ฐ˜์ผ ๊ฒƒ (์—…์ŠคํŠธ๋ฆผ ์ œ์•ฝ)
37
+
38
+ ## Install (dual-install)
39
+
40
+ ์—…์ŠคํŠธ๋ฆผ์„ `~/.claude/skills/korean-privacy-terms/upstream/` ์™€ `~/.agents/skills/korean-privacy-terms/upstream/` ์–‘์ชฝ์— pinned SHA ๋กœ ์ฒดํฌ์•„์›ƒํ•œ๋‹ค. ๋ ˆํฌ ๋‚ด๋ถ€์— ์—…์ŠคํŠธ๋ฆผ payload ๋ฅผ ์ปค๋ฐ‹ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์‹ค์‚ฌ์šฉ ์ „ ๋ฐ˜๋“œ์‹œ ์ด ๋‹จ๊ณ„๋ฅผ ๊ฑฐ์นœ๋‹ค.
41
+
42
+ ```bash
43
+ bash korean-privacy-terms/scripts/install.sh
44
+ ```
45
+
46
+ ๋ ˆํฌ๋ฅผ ๋กœ์ปฌ์— clone ํ•˜์ง€ ์•Š๊ณ  ์ด๋ฏธ ํ™ˆ ๋””๋ ‰ํ† ๋ฆฌ ์Šคํ‚ฌ ๋ฒˆ๋“ค๋งŒ ๊ฐ€์ง„ ์ƒํƒœ์—์„œ๋„ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค (installer ๋Š” `${BASH_SOURCE[0]}` ๋กœ ์ ˆ๋Œ€๊ฒฝ๋กœ๋ฅผ ํ•ด์„ํ•˜๋ฏ€๋กœ cwd ์— ๊ตฌ์• ๋ฐ›์ง€ ์•Š๋Š”๋‹ค):
47
+
48
+ ```bash
49
+ bash ~/.claude/skills/korean-privacy-terms/scripts/install.sh
50
+ # ๋˜๋Š”
51
+ bash ~/.agents/skills/korean-privacy-terms/scripts/install.sh
52
+ ```
53
+
54
+ ์Šคํฌ๋ฆฝํŠธ๋Š” `korean-privacy-terms/scripts/upstream.pin` ์— ๊ธฐ๋ก๋œ 40์ž๋ฆฌ ์ปค๋ฐ‹ SHA ๋งŒ ์ฒดํฌ์•„์›ƒํ•œ๋‹ค. ๋‘ ๊ฒฝ๋กœ ๋ชจ๋‘์—์„œ `git -C <path> rev-parse HEAD` ๊ฐ€ pin ๊ณผ ๋™์ผํ•ด์•ผ ์„ค์น˜ ์„ฑ๊ณต.
55
+
56
+ ์˜คํ”„๋ผ์ธ ํ™˜๊ฒฝ์ด๋‚˜ ๋„คํŠธ์›Œํฌ ์ฐจ๋‹จ ์ƒํ™ฉ์—์„œ๋Š” ์—…์ŠคํŠธ๋ฆผ clone ์ด ์‹คํŒจํ•˜๋ฏ€๋กœ ์ด ์Šคํ‚ฌ์˜ ์ƒ์„ฑ ํ๋ฆ„์„ ์‹คํ–‰ํ•  ์ˆ˜ ์—†๋‹ค. ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋ช…์‹œ์ ์ธ ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ๋‚จ๊ธฐ๊ณ  ๋น„์ •์ƒ ์ข…๋ฃŒํ•œ๋‹ค.
57
+
58
+ ## Mandatory interview first
59
+
60
+ ์‚ฌ์šฉ์ž๊ฐ€ "๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจ ๋งŒ๋“ค์–ด์ค˜" ๊ฐ™์€ ์ƒ์„ฑ ์š”์ฒญ์„ ํ•˜๋ฉด **๋ฐ”๋กœ ํŒŒ์ผ์„ ๋งŒ๋“ค์ง€ ๋ง๊ณ  ๋จผ์ € ๋˜๋ฌป๋Š”๋‹ค.** ์—…์ŠคํŠธ๋ฆผ `scripts/interview.md` Step 0 ~ 9 ์˜ ์ธํ„ฐ๋ทฐ ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ผ ์ˆœ์ฐจ๋กœ ์งˆ๋ฌธํ•˜๋˜, ์ตœ์†Œํ•œ ์•„๋ž˜ ํ•ญ๋ชฉ์„ ํ™•์ธํ•œ ๋’ค ์ƒ์„ฑ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ„๋‹ค.
61
+
62
+ ๊ถŒ์žฅ ์ฒซ ์งˆ๋ฌธ ๋ฌถ์Œ:
63
+
64
+ - ๋Œ€์ƒ ์‚ฌ์šฉ์ž ๋ฒ”์œ„ (ํ•œ๊ตญ ์‚ฌ์šฉ์ž๋งŒ / ํ•ด์™ธ ์œ„์ฃผ / ์–‘์ชฝ ๊ธ€๋กœ๋ฒŒ) โ€” ๊ด€ํ• ๋ฒ• ๊ฒฐ์ •
65
+ - ์šด์˜ ์ฃผ์ฒด ์†Œ์žฌ์ง€ (ํ•œ๊ตญ / ํ•ด์™ธ)
66
+ - ์„œ๋น„์Šค ์œ ํ˜• (SaaS / ์‡ผํ•‘๋ชฐ / ์ปค๋ฎค๋‹ˆํ‹ฐ / ๋ธ”๋กœ๊ทธ / ํ•€ํ…Œํฌ / AI ์„œ๋น„์Šค)
67
+ - Next.js ๋ฒ„์ „ (13 ~ 16), App Router ์‚ฌ์šฉ ์—ฌ๋ถ€, ์–ธ์–ด (`.ts` / `.tsx` / `.js` / `.jsx`)
68
+ - Tailwind ๋ฒ„์ „ (v3 / v4), ๋ฒˆ๋“ค๋Ÿฌ (Turbopack / Webpack)
69
+ - shadcn/ui ๊ธฐ์„ค์น˜ ์—ฌ๋ถ€, ๊ธฐ์กด ๋””์ž์ธ ์‹œ์Šคํ…œ (Tailwind ์ˆœ์ • / Chakra / MUI / Mantine ๋“ฑ)
70
+ - ์ถœ๋ ฅ ์–ธ์–ด (ํ•œ๊ตญ์–ด๋งŒ / ์˜๋ฌธ๋งŒ / ํ•œ์˜ ๋ณ‘๊ธฐ)
71
+ - 14์„ธ ๋ฏธ๋งŒ ๋Œ€์ƒ ์—ฌ๋ถ€
72
+ - ์ž๋™ํ™”๋œ ๊ฒฐ์ •(AI) ยท ํ–‰ํƒœ์ •๋ณด(์ฟ ํ‚ค) ยท ๋งž์ถคํ˜• ๊ด‘๊ณ  ์ฒ˜๋ฆฌ ์—ฌ๋ถ€
73
+ - ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ์ฑ…์ž„์ž(CPO) ์ •๋ณด, ์‚ฌ์—…์ž ์ƒํ˜ธยท๋Œ€ํ‘œ์žยท์ฃผ์†Œ (์‚ฌ์šฉ์ž๊ฐ€ ์•Œ๋ ค์ค˜์•ผ ํ•จ)
74
+
75
+ ์ธํ„ฐ๋ทฐ๋Š” ํŠน์ • ์—์ด์ „ํŠธ์˜ ์ „์šฉ UI ์ปดํฌ๋„ŒํŠธ ํ˜ธ์ถœ(์งˆ๋ฌธ ์œ„์ ฏ ๋“ฑ) ์— ์˜์กดํ•˜์ง€ ์•Š๋Š” agent-neutral ํ†ค์œผ๋กœ, ํ•œ ๋ฒˆ์— 1 ~ 2๋ฌธํ•ญ์”ฉ ์ง„ํ–‰ํ•œ๋‹ค. ๋ฒ•๋ฅ  ๊ณตํฌ๊ฐ(๊ณผํƒœ๋ฃŒ ํญํƒ„ ๋“ฑ)์„ ์œ ๋ฐœํ•˜๋Š” ํ‘œํ˜„์€ ํ”ผํ•˜๊ณ , "๋ฒ•์ ์œผ๋กœ ํ•„์š”ํ•œ ๋ถ€๋ถ„์ด์—์š”" ์ •๋„์˜ ๋‹ด๋‹ดํ•œ ํ†ค์„ ์œ ์ง€ํ•œ๋‹ค. ๋ชจ๋ฅด๋Š” ํ•ญ๋ชฉ์€ "๋„˜์–ด๊ฐ€๋„ ๋ฉ๋‹ˆ๋‹ค" ๋กœ ์—ด์–ด ๋‘”๋‹ค.
76
+
77
+ ## Workflow
78
+
79
+ 1. ์‚ฌ์šฉ์ž๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ ๋ฌธ๊ตฌ๋ฅผ ๋งํ•˜๋ฉด ๋จผ์ € ์œ„ ์ธํ„ฐ๋ทฐ ๊ฒŒ์ดํŠธ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
80
+ 2. `bash korean-privacy-terms/scripts/install.sh` ๋กœ ์—…์ŠคํŠธ๋ฆผ์„ dual-install ํ•œ๋‹ค (์ด๋ฏธ ์„ค์น˜๋ผ ์žˆ์œผ๋ฉด pin ํ™•์ธ๋งŒ ํ•œ๋‹ค).
81
+ 3. ์—…์ŠคํŠธ๋ฆผ์ด ์ œ๊ณตํ•˜๋Š” ์ˆœ์„œ๋ฅผ ๋”ฐ๋ฅธ๋‹ค: `scripts/interview.md` โ†’ `scripts/render.md` โ†’ `scripts/install.md`.
82
+ 4. ์—…์ŠคํŠธ๋ฆผ์€ Next.js ๊ฐ์ง€ โ†’ MDX/shadcn ์˜์กด์„ฑ ์„ค์น˜ โ†’ ํ…œํ”Œ๋ฆฟ ์น˜ํ™˜ โ†’ `src/app/privacy/page.tsx` ยท `src/app/terms/page.tsx` ยท `src/components/legal/*` ๋“ฑ ํŒŒ์ผ ์ƒ์„ฑ โ†’ ๋ฒ•์ • ํ•„์ˆ˜ 11๊ฐœ ํ•ญ๋ชฉ ๊ฒ€์ฆ โ†’ ๋ฉด์ฑ… ์ฃผ์„ ์‚ฝ์ž… ์„ ์ˆœ์ฐจ ์‹คํ–‰ํ•œ๋‹ค.
83
+ 5. ์ƒ์„ฑ์ด ๋๋‚˜๋ฉด ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด๊ณ ํ•˜๋˜, ์•„๋ž˜ **Response policy** ์˜ ๊ณ ์ • ๋ธ”๋ก์„ ๋ฐ˜๋“œ์‹œ ํฌํ•จํ•œ๋‹ค.
84
+
85
+ ## CLI examples
86
+
87
+ ```bash
88
+ cat korean-privacy-terms/scripts/upstream.pin
89
+
90
+ bash korean-privacy-terms/scripts/install.sh
91
+
92
+ git -C ~/.claude/skills/korean-privacy-terms/upstream rev-parse HEAD
93
+ git -C ~/.agents/skills/korean-privacy-terms/upstream rev-parse HEAD
94
+ ```
95
+
96
+ ## Response policy
97
+
98
+ - ์ƒ์„ฑ๋œ ๋ฌธ์„œ๋Š” **์ฐธ๊ณ ์šฉ ์ดˆ์•ˆ**์ด๋ฉฐ ๋ฒ•๋ฅ  ์ž๋ฌธ์ด ์•„๋‹ˆ๋ผ๋Š” ์ ์„ **๋ชจ๋“  ๋‹ต๋ณ€ ๋ง๋ฏธ**์— ๊ณ ์ง€ํ•œ๋‹ค.
99
+ - ์‹ค์„œ๋น„์Šค ์ ์šฉ ์ „ ๋ฐ˜๋“œ์‹œ **๋ณ€ํ˜ธ์‚ฌ ๊ฒ€ํ† **๊ฐ€ ํ•„์š”ํ•จ์„ ๊ณ ์ • ๋ฌธ๊ตฌ๋กœ ์ถœ๋ ฅํ•œ๋‹ค.
100
+ - 2026.9.11 ์‹œํ–‰ ๊ฐœ์ • ๊ฐœ์ธ์ •๋ณด๋ณดํ˜ธ๋ฒ•์˜ ยง30 ๋ฒ•์ • ํ•ญ๋ชฉ, ๊ณผ์ง•๊ธˆ ์ƒํ–ฅ(๋งค์ถœ์•ก 10%), ์‚ฌ์—…์ฃผ ์ฑ…์ž„ ๋“ฑ์ด ์—…์ŠคํŠธ๋ฆผ pin ์‹œ์  ๊ธฐ์ค€์œผ๋กœ ๋ฐ˜์˜๋ผ ์žˆ์Œ์„ ์•Œ๋ฆฐ๋‹ค. ์ดํ›„ ๊ฐœ์ •์— ๋Œ€ํ•œ ์ตœ์‹  ๋ฐ˜์˜ ์—ฌ๋ถ€๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ํ™•์ธ ์ฑ…์ž„์ด ์žˆ๋‹ค๊ณ  ๋ช…์‹œํ•œ๋‹ค.
101
+ - "๋“ฑ" ์œผ๋กœ ๋ญ‰๋šฑ๊ทธ๋ฆฌ๋Š” ํ‘œํ˜„, ๋ฒ•์ • ํ•„์ˆ˜ 11๊ฐœ ํ•ญ๋ชฉ ๋ˆ„๋ฝ, ๋ฉด์ฑ… ์ฃผ์„ ์ œ๊ฑฐ, ์‚ฌ์šฉ์ž ํ™•์ธ ์—†์ด ํšŒ์‚ฌ๋ช…ยท์ฑ…์ž„์ž๋ช…์„ ์ถ”์ธกํ•ด ์ฑ„์›Œ๋„ฃ๋Š” ํ–‰์œ„๋Š” ๊ธˆ์ง€๋œ๋‹ค (์—…์ŠคํŠธ๋ฆผ ๊ทœ์น™).
102
+ - 14์„ธ ๋ฏธ๋งŒ ๋Œ€์ƒ ์„œ๋น„์Šค์— ์„ฑ์ธ์šฉ ๋ฐฉ์นจ์„ ๊ทธ๋Œ€๋กœ ์ ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
103
+
104
+ ## Done when
105
+
106
+ - ์ธํ„ฐ๋ทฐ ๊ฒŒ์ดํŠธ๊ฐ€ ์‹คํ–‰๋˜์–ด ์ตœ์†Œ jurisdiction ยท ์„œ๋น„์Šค ์œ ํ˜• ยท Next.js ๋ฒ„์ „ ยท App Router ์—ฌ๋ถ€ ยท ์ถœ๋ ฅ ์–ธ์–ด ๊ฐ€ ํ™•์ธ๋˜์—ˆ๋‹ค.
107
+ - `scripts/install.sh` ์ด ์—…์ŠคํŠธ๋ฆผ์„ dual-install (`~/.claude/skills/korean-privacy-terms/upstream/` + `~/.agents/skills/korean-privacy-terms/upstream/`) ํ–ˆ๊ณ , ์–‘์ชฝ ๊ฒฝ๋กœ ๋ชจ๋‘ pin ๊ณผ ๋™์ผํ•œ SHA ์ด๋‹ค.
108
+ - ์—…์ŠคํŠธ๋ฆผ `scripts/interview.md` โ†’ `scripts/render.md` โ†’ `scripts/install.md` ์ˆœ์„œ๋กœ ์ƒ์„ฑ์ด ๋๋‚ฌ๋‹ค.
109
+ - ๋ฒ•์ • ํ•„์ˆ˜ 11๊ฐœ ํ•ญ๋ชฉ ๊ฒ€์ฆ๊ณผ ๋ฉด์ฑ… ์ฃผ์„ ์‚ฝ์ž…์ด ์—…์ŠคํŠธ๋ฆผ ๊ทœ์น™๋Œ€๋กœ ์™„๋ฃŒ๋˜์—ˆ๋‹ค.
110
+ - ์‚ฌ์šฉ์ž์—๊ฒŒ ์ฐธ๊ณ ์šฉ ์ดˆ์•ˆ + ๋ฒ•๋ฅ  ์ž๋ฌธ ์•„๋‹˜ + ๋ณ€ํ˜ธ์‚ฌ ๊ฒ€ํ†  ํ•„์ˆ˜ + 2026.9.11 ๊ฐœ์ • ๋ฐ˜์˜ ๊ธฐ์ค€์ด ํ•จ๊ป˜ ๊ณ ์ง€๋˜์—ˆ๋‹ค.
111
+
112
+ ## Failure modes
113
+
114
+ - ์˜คํ”„๋ผ์ธ ๋˜๋Š” ๋„คํŠธ์›Œํฌ ์ฐจ๋‹จ: `scripts/install.sh` ๊ฐ€ upstream clone ๋‹จ๊ณ„์—์„œ ์‹คํŒจํ•œ๋‹ค. ๋„คํŠธ์›Œํฌ ํ™•๋ณด ํ›„ ์žฌ์‹คํ–‰.
115
+ - pin SHA ๊ฐ€ ์—…์ŠคํŠธ๋ฆผ์—์„œ ์‚ญ์ œ/force-push ๋œ ๊ฒฝ์šฐ: ์Šคํฌ๋ฆฝํŠธ๊ฐ€ SHA mismatch ๋กœ ๋น„์ •์ƒ ์ข…๋ฃŒํ•œ๋‹ค. `scripts/upstream.pin` ์„ ์ตœ์‹  ํƒœ๊ทธ SHA ๋กœ bump ํ•˜๊ณ  PR ์„ ๋งŒ๋“ ๋‹ค.
116
+ - Next.js Pages Router ๋‹จ๋… ํ”„๋กœ์ ํŠธ: ์—…์ŠคํŠธ๋ฆผ์ด ์‹คํ–‰์„ ์ค‘๋‹จํ•œ๋‹ค. ์‚ฌ์šฉ์ž์—๊ฒŒ App Router ์ „ํ™˜์ด ์„ ํ–‰ ์กฐ๊ฑด์ž„์„ ์•ˆ๋‚ดํ•œ๋‹ค.
117
+ - ๋ฒ•๋ฅ  ๊ฐœ์ • ๋“œ๋ฆฌํ”„ํŠธ: ์—…์ŠคํŠธ๋ฆผ์ด CHANGELOG ๋กœ ๋ฐ˜์˜ ๊ธฐ์ค€์„ ๊ด€๋ฆฌํ•œ๋‹ค. pin ๋งŒ ์˜ฌ๋ฆฌ์ง€ ๋ง๊ณ  ์—…์ŠคํŠธ๋ฆผ CHANGELOG ๋ฅผ ํ•จ๊ป˜ ํ™•์ธํ•œ๋‹ค.
118
+
119
+ ## Notes
120
+
121
+ - upstream: https://github.com/kimlawtech/korean-privacy-terms (Apache-2.0)
122
+ - upstream pin: [`scripts/upstream.pin`](./scripts/upstream.pin)
123
+ - installer: [`scripts/install.sh`](./scripts/install.sh)
124
+ - ๋ฒ•๋ฅ  ๋ฉด์ฑ… ์ „๋ฌธ: [`./DISCLAIMER.md`](./DISCLAIMER.md)
125
+ - upstream ์ €์žยท์ปค๋ฎค๋‹ˆํ‹ฐ attribution: [`./NOTICE`](./NOTICE) (@kimlawtech, SpeciAI)
126
+ - Apache-2.0 ์ „๋ฌธ (์—…์ŠคํŠธ๋ฆผ `LICENSE` verbatim): [`./LICENSE.upstream`](./LICENSE.upstream) โ€” Apache License, Version 2.0 ยง4(a) ("give any other recipients of the Work or Derivative Works a copy of this License") ์ค€์ˆ˜๋ฅผ ์œ„ํ•ด `install.sh` ์‹คํ–‰ ์ „์—๋„ ๋ ˆํฌ ํŠธ๋ฆฌ์— ๋ฒˆ๋“คํ•ด ๋‘”๋‹ค. ๋ ˆํฌ ๋ฃจํŠธ์˜ [`../LICENSE`](../LICENSE) (MIT) ๋Š” k-skill ์ž์ฒด ๋ผ์ด์„ ์Šค์ด๋ฉฐ ์ด ์Šคํ‚ฌ ์ƒ๋ถ€์— ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.
127
+ - ๋ณธ ์Šคํ‚ฌ์€ ์—…์ŠคํŠธ๋ฆผ ์‚ฐ์ถœ๋ฌผ์˜ ์žฌ๋ฐฐํฌ์— ํ•ด๋‹นํ•˜๋ฏ€๋กœ Apache License, Version 2.0 ยง4 ์š”๊ฑด (LICENSE ๋ฒˆ๋“ค, NOTICE ํฌํ•จ, ๋ผ์ด์„ ์Šค ๊ณ ์ง€) ์„ ์ค€์ˆ˜ํ•œ๋‹ค.
128
+ - ์ค‘์ฒฉ `SKILL.md` ์•ˆ๋‚ด: `install.sh` ์‹คํ–‰ ํ›„ `~/.claude/skills/korean-privacy-terms/upstream/SKILL.md` ๊ฐ€ ์กด์žฌํ•˜๊ฒŒ ๋˜์ง€๋งŒ, Claude Code / Codex / Vercel Agent Skills ๋“ฑ ์—์ด์ „ํŠธ ํ”Œ๋žซํผ์€ top-level `SKILL.md` ๋งŒ discovery ๋Œ€์ƒ์œผ๋กœ ์‚ผ๋Š”๋‹ค. ์ค‘์ฒฉ ์—…์ŠคํŠธ๋ฆผ `SKILL.md` ๋Š” ์ง์ ‘ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # korean-privacy-terms upstream installer.
4
+ #
5
+ # k-skill ์ธก์€ ์–‡์€ wrapper ๋งŒ ์œ ์ง€ํ•˜๊ณ , ์ธํ„ฐ๋ทฐ/๋ Œ๋”/์„ค์น˜ ๋กœ์ง์€ ์—…์ŠคํŠธ๋ฆผ
6
+ # kimlawtech/korean-privacy-terms (Apache-2.0) ์— ์œ„์ž„ํ•œ๋‹ค. ์ด ์Šคํฌ๋ฆฝํŠธ๋Š”
7
+ # scripts/upstream.pin ์— ๊ธฐ๋ก๋œ ์ปค๋ฐ‹ SHA ๋ฅผ ๋‘ ํ™ˆ ๋””๋ ‰ํ† ๋ฆฌ ์Šคํ‚ฌ ๊ฒฝ๋กœ
8
+ #
9
+ # ~/.claude/skills/korean-privacy-terms/upstream/
10
+ # ~/.agents/skills/korean-privacy-terms/upstream/
11
+ #
12
+ # ์•„๋ž˜์— ๋™์ผํ•˜๊ฒŒ ์ฒดํฌ์•„์›ƒํ•ด ๋‘”๋‹ค. ๋ ˆํฌ ๋‚ด๋ถ€์— ์—…์ŠคํŠธ๋ฆผ์„ ์ปค๋ฐ‹ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ
13
+ # ์‹ค์‚ฌ์šฉ ์ „์—๋Š” ๋ฐ˜๋“œ์‹œ ์ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ•œ ๋ฒˆ ์‹คํ–‰ํ•ด์•ผ ํ•œ๋‹ค.
14
+ #
15
+ # ์‚ฌ์šฉ๋ฒ•:
16
+ # bash korean-privacy-terms/scripts/install.sh
17
+ #
18
+ # AGENTS.md ๊ทœ์น™์— ๋”ฐ๋ผ ~/.claude/skills ์™€ ~/.agents/skills ๋Š” ํ™ˆ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ
19
+ # ์˜ indirection(์˜ˆ: ~/.agents/skills ๊ฐ€ symlink ์ธ ๊ฒฝ์šฐ) ์„ ์กด์ค‘ํ•œ๋‹ค. ๋ ˆํฌ
20
+ # ๋‚ด๋ถ€์— repo-local .claude/ ๋˜๋Š” .agents/ ๋””๋ ‰ํ† ๋ฆฌ๋Š” ์ƒ์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค.
21
+
22
+ set -euo pipefail
23
+
24
+ UPSTREAM_REPO="https://github.com/kimlawtech/korean-privacy-terms.git"
25
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26
+ PIN_FILE="${SCRIPT_DIR}/upstream.pin"
27
+ SKILL_NAME="korean-privacy-terms"
28
+
29
+ if [[ ! -f "${PIN_FILE}" ]]; then
30
+ echo "[korean-privacy-terms] upstream.pin not found at ${PIN_FILE}" >&2
31
+ exit 1
32
+ fi
33
+
34
+ UPSTREAM_SHA="$(tr -d '[:space:]' <"${PIN_FILE}")"
35
+
36
+ if [[ ! "${UPSTREAM_SHA}" =~ ^[0-9a-f]{40}$ ]]; then
37
+ echo "[korean-privacy-terms] upstream.pin must contain a 40-char git SHA (got: ${UPSTREAM_SHA})" >&2
38
+ exit 1
39
+ fi
40
+
41
+ CACHE_DIR="${HOME}/.cache/k-skill/${SKILL_NAME}"
42
+ CLONE_DIR="${CACHE_DIR}/upstream"
43
+
44
+ mkdir -p "${CACHE_DIR}"
45
+
46
+ if [[ ! -d "${CLONE_DIR}/.git" ]]; then
47
+ echo "[korean-privacy-terms] cloning upstream into ${CLONE_DIR}"
48
+ if ! git clone --filter=blob:none "${UPSTREAM_REPO}" "${CLONE_DIR}" >&2; then
49
+ echo "" >&2
50
+ echo "[korean-privacy-terms] upstream clone failed (network required)." >&2
51
+ echo " upstream: ${UPSTREAM_REPO}" >&2
52
+ echo " ์˜คํ”„๋ผ์ธ ํ™˜๊ฒฝ์—์„œ๋Š” ์ด ์Šคํ‚ฌ์˜ ์ƒ์„ฑ ํ๋ฆ„์„ ์‹คํ–‰ํ•  ์ˆ˜ ์—†๋‹ค." >&2
53
+ echo " ๋„คํŠธ์›Œํฌ๋ฅผ ํ™•๋ณดํ•œ ๋’ค ๋‹ค์‹œ ์‹คํ–‰ํ•ด๋‹ฌ๋ผ." >&2
54
+ exit 1
55
+ fi
56
+ fi
57
+
58
+ echo "[korean-privacy-terms] syncing upstream to pinned SHA ${UPSTREAM_SHA}"
59
+ git -C "${CLONE_DIR}" fetch --tags origin "${UPSTREAM_SHA}" >&2 || git -C "${CLONE_DIR}" fetch origin >&2
60
+ git -C "${CLONE_DIR}" checkout --force --detach "${UPSTREAM_SHA}" >&2
61
+
62
+ HEAD_SHA="$(git -C "${CLONE_DIR}" rev-parse HEAD)"
63
+
64
+ if [[ "${HEAD_SHA}" != "${UPSTREAM_SHA}" ]]; then
65
+ echo "[korean-privacy-terms] HEAD (${HEAD_SHA}) does not match pinned SHA (${UPSTREAM_SHA})" >&2
66
+ exit 1
67
+ fi
68
+
69
+ # Dual-install targets. Both paths respect AGENTS.md indirection rules and do not
70
+ # introduce repo-local skill directories.
71
+ HOME_DIRS=(
72
+ "${HOME}/.claude/skills/${SKILL_NAME}"
73
+ "${HOME}/.agents/skills/${SKILL_NAME}"
74
+ )
75
+
76
+ for HOME_SKILL_DIR in "${HOME_DIRS[@]}"; do
77
+ HOME_UPSTREAM="${HOME_SKILL_DIR}/upstream"
78
+ mkdir -p "${HOME_SKILL_DIR}"
79
+
80
+ if [[ -e "${HOME_UPSTREAM}" || -L "${HOME_UPSTREAM}" ]]; then
81
+ rm -rf "${HOME_UPSTREAM}"
82
+ fi
83
+
84
+ # rsync-style copy preserves the git metadata so `git -C <path> rev-parse HEAD`
85
+ # still reports the pinned SHA after install. Prefer rsync when available;
86
+ # otherwise fall back to `cp -a` which is portable on macOS and Linux.
87
+ if command -v rsync >/dev/null 2>&1; then
88
+ rsync -a --delete "${CLONE_DIR}/" "${HOME_UPSTREAM}/"
89
+ else
90
+ cp -a "${CLONE_DIR}/" "${HOME_UPSTREAM}/"
91
+ fi
92
+
93
+ INSTALLED_SHA="$(git -C "${HOME_UPSTREAM}" rev-parse HEAD)"
94
+
95
+ if [[ "${INSTALLED_SHA}" != "${UPSTREAM_SHA}" ]]; then
96
+ echo "[korean-privacy-terms] ${HOME_UPSTREAM} HEAD (${INSTALLED_SHA}) does not match pin (${UPSTREAM_SHA})" >&2
97
+ exit 1
98
+ fi
99
+
100
+ echo "[korean-privacy-terms] installed upstream@${UPSTREAM_SHA} -> ${HOME_UPSTREAM}"
101
+ done
102
+
103
+ echo ""
104
+ echo "[korean-privacy-terms] done."
105
+ echo " pinned upstream SHA: ${UPSTREAM_SHA}"
106
+ echo " upstream repo: ${UPSTREAM_REPO}"
107
+ echo " ์ด ์Šคํ‚ฌ์ด ์ƒ์„ฑํ•˜๋Š” ๋ชจ๋“  ๋ฌธ์„œ๋Š” ์ฐธ๊ณ ์šฉ ์ดˆ์•ˆ์ด๋ฉฐ ๋ฒ•๋ฅ  ์ž๋ฌธ์ด ์•„๋‹ˆ๋‹ค."
108
+ echo " ์‹ค์„œ๋น„์Šค ๋ฐฐํฌ ์ „ ๋ฐ˜๋“œ์‹œ ๋ณ€ํ˜ธ์‚ฌ ๊ฒ€ํ† ๋ฅผ ๋ฐ›์•„๋‹ฌ๋ผ."
@@ -0,0 +1 @@
1
+ e390f7b9feb825e368c26726363ea5ce11a34083
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "korean-privacy-terms",
3
+ "description": "kimlawtech/korean-privacy-terms (Apache-2.0) ์—…์ŠคํŠธ๋ฆผ์„ ๊ฒฝ์œ ํ•ด Next.js ํ”„๋กœ์ ํŠธ์— ํ•œ๊ตญ ๋ฒ•๋ น(๊ฐœ์ธ์ •๋ณด๋ณดํ˜ธ๋ฒ•ยท์•ฝ๊ด€๊ทœ์ œ๋ฒ•ยท์ „์ž์ƒ๊ฑฐ๋ž˜๋ฒ•) ๊ธฐ๋ฐ˜ ๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจยท์ด์šฉ์•ฝ๊ด€ยท์ฟ ํ‚ค ๋ฐฐ๋„ˆยท๋™์˜ ๋ชจ๋‹ฌ์„ ์ƒ์„ฑํ•˜๋Š” thin wrapper ์Šคํ‚ฌ.",
4
+ "profiles": [
5
+ "local"
6
+ ],
7
+ "frontmatter": "name: korean-privacy-terms\ndescription: kimlawtech/korean-privacy-terms (Apache-2.0) ์—…์ŠคํŠธ๋ฆผ์„ ๊ฒฝ์œ ํ•ด Next.js ํ”„๋กœ์ ํŠธ์— ํ•œ๊ตญ ๋ฒ•๋ น(๊ฐœ์ธ์ •๋ณด๋ณดํ˜ธ๋ฒ•ยท์•ฝ๊ด€๊ทœ์ œ๋ฒ•ยท์ „์ž์ƒ๊ฑฐ๋ž˜๋ฒ•) ๊ธฐ๋ฐ˜ ๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจยท์ด์šฉ์•ฝ๊ด€ยท์ฟ ํ‚ค ๋ฐฐ๋„ˆยท๋™์˜ ๋ชจ๋‹ฌ์„ ์ƒ์„ฑํ•˜๋Š” thin wrapper ์Šคํ‚ฌ.\nlicense: Apache-2.0\nmetadata:\n category: legal\n locale: ko-KR\n phase: v1"
8
+ }