@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.
- package/bin/k-skill.js +60 -0
- package/package.json +37 -0
- package/skills/assembly-bill-vote-search/instruction.md +111 -0
- package/skills/assembly-bill-vote-search/skill.json +10 -0
- package/skills/biz-health-check/instruction.md +69 -0
- package/skills/biz-health-check/scripts/biz_health_check.py +163 -0
- package/skills/biz-health-check/skill.json +10 -0
- package/skills/bok-ecos-stats/instruction.md +95 -0
- package/skills/bok-ecos-stats/scripts/bok_ecos.py +302 -0
- package/skills/bok-ecos-stats/skill.json +10 -0
- package/skills/building-register-search/instruction.md +69 -0
- package/skills/building-register-search/scripts/building_register.py +309 -0
- package/skills/building-register-search/scripts/building_register_xml.py +32 -0
- package/skills/building-register-search/skill.json +9 -0
- package/skills/bunjang-search/instruction.md +154 -0
- package/skills/bunjang-search/skill.json +10 -0
- package/skills/catchtable-sniper/instruction.md +270 -0
- package/skills/catchtable-sniper/skill.json +9 -0
- package/skills/cheap-gas-nearby/instruction.md +107 -0
- package/skills/cheap-gas-nearby/skill.json +10 -0
- package/skills/corporate-registration-consulting/instruction.md +117 -0
- package/skills/corporate-registration-consulting/scripts/fill_official_hwp.py +106 -0
- package/skills/corporate-registration-consulting/skill.json +8 -0
- package/skills/coupang-product-search/instruction.md +219 -0
- package/skills/coupang-product-search/scripts/coupang_partners_mcp.py +146 -0
- package/skills/coupang-product-search/skill.json +10 -0
- package/skills/court-auction-notice-search/instruction.md +201 -0
- package/skills/court-auction-notice-search/skill.json +9 -0
- package/skills/court-payment-order-assistant/instruction.md +120 -0
- package/skills/court-payment-order-assistant/skill.json +10 -0
- package/skills/d2b-notice-search/instruction.md +117 -0
- package/skills/d2b-notice-search/skill.json +10 -0
- package/skills/daangn-cars-search/instruction.md +91 -0
- package/skills/daangn-cars-search/scripts/daangn_cars.py +72 -0
- package/skills/daangn-cars-search/skill.json +9 -0
- package/skills/daangn-jobs-search/instruction.md +90 -0
- package/skills/daangn-jobs-search/scripts/daangn_jobs.py +98 -0
- package/skills/daangn-jobs-search/skill.json +9 -0
- package/skills/daangn-realty-search/instruction.md +114 -0
- package/skills/daangn-realty-search/scripts/daangn_detail_ld.py +76 -0
- package/skills/daangn-realty-search/scripts/daangn_realty.py +216 -0
- package/skills/daangn-realty-search/scripts/daangn_relay_store.py +174 -0
- package/skills/daangn-realty-search/skill.json +8 -0
- package/skills/daangn-used-goods-search/instruction.md +90 -0
- package/skills/daangn-used-goods-search/scripts/daangn_used_goods.py +80 -0
- package/skills/daangn-used-goods-search/skill.json +9 -0
- package/skills/daishin-report-search/instruction.md +138 -0
- package/skills/daishin-report-search/skill.json +10 -0
- package/skills/daiso-product-search/instruction.md +172 -0
- package/skills/daiso-product-search/skill.json +8 -0
- package/skills/danawa-price-search/instruction.md +183 -0
- package/skills/danawa-price-search/scripts/danawa_search.py +354 -0
- package/skills/danawa-price-search/skill.json +8 -0
- package/skills/delivery-tracking/instruction.md +358 -0
- package/skills/delivery-tracking/skill.json +8 -0
- package/skills/donation-place-search/instruction.md +129 -0
- package/skills/donation-place-search/skill.json +9 -0
- package/skills/emergency-room-beds/instruction.md +82 -0
- package/skills/emergency-room-beds/skill.json +8 -0
- package/skills/ev-charger-nearby/instruction.md +80 -0
- package/skills/ev-charger-nearby/scripts/ev_charger.py +222 -0
- package/skills/ev-charger-nearby/skill.json +10 -0
- package/skills/ev-subsidy-status/instruction.md +165 -0
- package/skills/ev-subsidy-status/skill.json +10 -0
- package/skills/express-bus-booking/instruction.md +207 -0
- package/skills/express-bus-booking/references/kobus-http-flow.md +159 -0
- package/skills/express-bus-booking/scripts/kobus_express_booking.py +243 -0
- package/skills/express-bus-booking/skill.json +8 -0
- package/skills/fine-dust-location/instruction.md +89 -0
- package/skills/fine-dust-location/skill.json +9 -0
- package/skills/flight-ticket-search/instruction.md +237 -0
- package/skills/flight-ticket-search/scripts/flight_ticket_search.py +501 -0
- package/skills/flight-ticket-search/skill.json +9 -0
- package/skills/foresttrip-vacancy/instruction.md +167 -0
- package/skills/foresttrip-vacancy/scripts/run_foresttrip_vacancy.py +549 -0
- package/skills/foresttrip-vacancy/skill.json +10 -0
- package/skills/fsc-corporate-info/instruction.md +57 -0
- package/skills/fsc-corporate-info/scripts/fsc_corporate_info.py +113 -0
- package/skills/fsc-corporate-info/skill.json +10 -0
- package/skills/g2b-order-plan-search/instruction.md +131 -0
- package/skills/g2b-order-plan-search/scripts/g2b_order_plan.py +158 -0
- package/skills/g2b-order-plan-search/skill.json +10 -0
- package/skills/g2b-sanctioned-supplier/instruction.md +61 -0
- package/skills/g2b-sanctioned-supplier/scripts/g2b_sanctioned_supplier.py +114 -0
- package/skills/g2b-sanctioned-supplier/skill.json +10 -0
- package/skills/gangnamunni-clinic-search/instruction.md +113 -0
- package/skills/gangnamunni-clinic-search/skill.json +9 -0
- package/skills/geeknews-search/instruction.md +69 -0
- package/skills/geeknews-search/scripts/geeknews_search.py +296 -0
- package/skills/geeknews-search/skill.json +8 -0
- package/skills/gongsijiga-search/instruction.md +128 -0
- package/skills/gongsijiga-search/skill.json +9 -0
- package/skills/gov-overseas-trip-report/instruction.md +488 -0
- package/skills/gov-overseas-trip-report/scripts/gov_overseas_trip_report.py +1206 -0
- package/skills/gov-overseas-trip-report/skill.json +9 -0
- package/skills/han-river-water-level/instruction.md +83 -0
- package/skills/han-river-water-level/skill.json +10 -0
- package/skills/highway-traffic-status/instruction.md +88 -0
- package/skills/highway-traffic-status/scripts/highway_traffic.py +315 -0
- package/skills/highway-traffic-status/skill.json +11 -0
- package/skills/hipass-receipt/instruction.md +97 -0
- package/skills/hipass-receipt/skill.json +10 -0
- package/skills/hola-poke-yeoksam/instruction.md +247 -0
- package/skills/hola-poke-yeoksam/skill.json +8 -0
- package/skills/household-waste-info/instruction.md +117 -0
- package/skills/household-waste-info/skill.json +10 -0
- package/skills/housing-official-price/instruction.md +177 -0
- package/skills/housing-official-price/skill.json +10 -0
- package/skills/hwp/instruction.md +206 -0
- package/skills/hwp/skill.json +8 -0
- package/skills/intercity-bus-booking/instruction.md +189 -0
- package/skills/intercity-bus-booking/references/tmoney-intercity-http-flow.md +126 -0
- package/skills/intercity-bus-booking/scripts/intercity_bus_search.py +381 -0
- package/skills/intercity-bus-booking/skill.json +8 -0
- package/skills/iros-registry-automation/instruction.md +229 -0
- package/skills/iros-registry-automation/scripts/iros_pdf_summary.py +249 -0
- package/skills/iros-registry-automation/scripts/upstream.pin +1 -0
- package/skills/iros-registry-automation/skill.json +9 -0
- package/skills/job-posting-match/instruction.md +130 -0
- package/skills/job-posting-match/scripts/job_posting_match.py +396 -0
- package/skills/job-posting-match/scripts/test_job_posting_match.py +54 -0
- package/skills/job-posting-match/skill.json +8 -0
- package/skills/jobkorea-talent-search/instruction.md +118 -0
- package/skills/jobkorea-talent-search/scripts/jobkorea_talent_models.py +27 -0
- package/skills/jobkorea-talent-search/scripts/jobkorea_talent_parse.py +186 -0
- package/skills/jobkorea-talent-search/scripts/jobkorea_talent_search.py +94 -0
- package/skills/jobkorea-talent-search/scripts/jobkorea_talent_search_condition.py +136 -0
- package/skills/jobkorea-talent-search/scripts/test_jobkorea_talent_search.py +76 -0
- package/skills/jobkorea-talent-search/skill.json +9 -0
- package/skills/joseon-sillok-search/instruction.md +76 -0
- package/skills/joseon-sillok-search/scripts/sillok_search.py +552 -0
- package/skills/joseon-sillok-search/skill.json +8 -0
- package/skills/k-dart/instruction.md +406 -0
- package/skills/k-dart/skill.json +8 -0
- package/skills/k-schoollunch-menu/instruction.md +109 -0
- package/skills/k-schoollunch-menu/skill.json +9 -0
- package/skills/k-skill-cleaner/instruction.md +80 -0
- package/skills/k-skill-cleaner/scripts/k_skill_cleaner.py +410 -0
- package/skills/k-skill-cleaner/skill.json +8 -0
- package/skills/k-skill-setup/instruction.md +253 -0
- package/skills/k-skill-setup/skill.json +11 -0
- package/skills/kakao-bar-nearby/instruction.md +76 -0
- package/skills/kakao-bar-nearby/skill.json +8 -0
- package/skills/kakao-map/instruction.md +176 -0
- package/skills/kakao-map/skill.json +10 -0
- package/skills/kakaotalk-mac/instruction.md +189 -0
- package/skills/kakaotalk-mac/skill.json +8 -0
- package/skills/kbl-results/instruction.md +89 -0
- package/skills/kbl-results/skill.json +9 -0
- package/skills/kbo-results/instruction.md +82 -0
- package/skills/kbo-results/skill.json +8 -0
- package/skills/keris-academic-search/instruction.md +81 -0
- package/skills/keris-academic-search/scripts/keris_academic.py +210 -0
- package/skills/keris-academic-search/skill.json +10 -0
- package/skills/kleague-results/instruction.md +92 -0
- package/skills/kleague-results/skill.json +8 -0
- package/skills/kopis-performance-search/instruction.md +109 -0
- package/skills/kopis-performance-search/skill.json +10 -0
- package/skills/korea-weather/instruction.md +93 -0
- package/skills/korea-weather/skill.json +10 -0
- package/skills/korean-character-count/instruction.md +87 -0
- package/skills/korean-character-count/scripts/korean_character_count.js +268 -0
- package/skills/korean-character-count/skill.json +8 -0
- package/skills/korean-cinema-search/instruction.md +177 -0
- package/skills/korean-cinema-search/skill.json +8 -0
- package/skills/korean-heritage-search/instruction.md +106 -0
- package/skills/korean-heritage-search/scripts/korean_heritage_search.py +321 -0
- package/skills/korean-heritage-search/skill.json +8 -0
- package/skills/korean-holiday-calendar/instruction.md +97 -0
- package/skills/korean-holiday-calendar/skill.json +9 -0
- package/skills/korean-humanizer/instruction.md +389 -0
- package/skills/korean-humanizer/references/ai-tell-taxonomy.md +147 -0
- package/skills/korean-humanizer/skill.json +8 -0
- package/skills/korean-jangbu-for/instruction.md +133 -0
- package/skills/korean-jangbu-for/scripts/install.sh +237 -0
- package/skills/korean-jangbu-for/scripts/upstream.pin +1 -0
- package/skills/korean-jangbu-for/skill.json +8 -0
- package/skills/korean-law-search/instruction.md +126 -0
- package/skills/korean-law-search/skill.json +10 -0
- package/skills/korean-marathon-schedule/instruction.md +111 -0
- package/skills/korean-marathon-schedule/skill.json +9 -0
- package/skills/korean-middle-korean/instruction.md +79 -0
- package/skills/korean-middle-korean/scripts/korean_middle_korean.js +214 -0
- package/skills/korean-middle-korean/skill.json +8 -0
- package/skills/korean-patent-search/instruction.md +79 -0
- package/skills/korean-patent-search/scripts/patent_search.py +409 -0
- package/skills/korean-patent-search/skill.json +9 -0
- package/skills/korean-privacy-terms/instruction.md +128 -0
- package/skills/korean-privacy-terms/scripts/install.sh +108 -0
- package/skills/korean-privacy-terms/scripts/upstream.pin +1 -0
- package/skills/korean-privacy-terms/skill.json +8 -0
- package/skills/korean-scholarship-search/instruction.md +317 -0
- package/skills/korean-scholarship-search/references/report-format.md +40 -0
- package/skills/korean-scholarship-search/references/school-discovery.md +61 -0
- package/skills/korean-scholarship-search/references/search-clues.md +58 -0
- package/skills/korean-scholarship-search/references/source-patterns.md +67 -0
- package/skills/korean-scholarship-search/scripts/scholarship_filter.py +811 -0
- package/skills/korean-scholarship-search/scripts/test_scholarship_filter.py +224 -0
- package/skills/korean-scholarship-search/scripts/university_search_plan.py +148 -0
- package/skills/korean-scholarship-search/skill.json +8 -0
- package/skills/korean-slang-writing/instruction.md +181 -0
- package/skills/korean-slang-writing/scripts/_slang_http.py +91 -0
- package/skills/korean-slang-writing/scripts/slang_lookup.py +291 -0
- package/skills/korean-slang-writing/scripts/slang_search.py +284 -0
- package/skills/korean-slang-writing/skill.json +8 -0
- package/skills/korean-spell-check/instruction.md +105 -0
- package/skills/korean-spell-check/scripts/korean_spell_check.py +523 -0
- package/skills/korean-spell-check/skill.json +9 -0
- package/skills/korean-stock-search/instruction.md +194 -0
- package/skills/korean-stock-search/skill.json +10 -0
- package/skills/korean-transit-route/instruction.md +113 -0
- package/skills/korean-transit-route/skill.json +10 -0
- package/skills/kosis-stats/instruction.md +232 -0
- package/skills/kosis-stats/references/kosis-openapi-guide.md +171 -0
- package/skills/kosis-stats/scripts/run_kosis_stats.py +896 -0
- package/skills/kosis-stats/skill.json +9 -0
- package/skills/kr-whois-lookup/instruction.md +107 -0
- package/skills/kr-whois-lookup/skill.json +10 -0
- package/skills/kstartup-search/instruction.md +186 -0
- package/skills/kstartup-search/scripts/run_kstartup.py +424 -0
- package/skills/kstartup-search/skill.json +11 -0
- package/skills/ktx-booking/instruction.md +244 -0
- package/skills/ktx-booking/skill.json +10 -0
- package/skills/lck-analytics/instruction.md +192 -0
- package/skills/lck-analytics/scripts/_lib.js +103 -0
- package/skills/lck-analytics/scripts/analyze-live-game.js +52 -0
- package/skills/lck-analytics/scripts/build-match-report.js +44 -0
- package/skills/lck-analytics/scripts/sync-oracle.js +50 -0
- package/skills/lck-analytics/skill.json +9 -0
- package/skills/lh-notice-search/instruction.md +206 -0
- package/skills/lh-notice-search/skill.json +10 -0
- package/skills/library-book-search/instruction.md +139 -0
- package/skills/library-book-search/skill.json +9 -0
- package/skills/local-election-candidate-search/instruction.md +77 -0
- package/skills/local-election-candidate-search/skill.json +8 -0
- package/skills/localdata-business-status/instruction.md +64 -0
- package/skills/localdata-business-status/scripts/localdata_business_status.py +206 -0
- package/skills/localdata-business-status/skill.json +8 -0
- package/skills/lotto-results/instruction.md +80 -0
- package/skills/lotto-results/skill.json +8 -0
- package/skills/lovebug-report/instruction.md +185 -0
- package/skills/lovebug-report/skill.json +9 -0
- package/skills/market-kurly-search/instruction.md +125 -0
- package/skills/market-kurly-search/skill.json +8 -0
- package/skills/mfds-drug-safety/instruction.md +86 -0
- package/skills/mfds-drug-safety/scripts/mfds_drug_safety.py +184 -0
- package/skills/mfds-drug-safety/skill.json +10 -0
- package/skills/mfds-food-safety/instruction.md +126 -0
- package/skills/mfds-food-safety/scripts/mfds_food_safety.py +281 -0
- package/skills/mfds-food-safety/skill.json +10 -0
- package/skills/myrealtrip-search/instruction.md +239 -0
- package/skills/myrealtrip-search/scripts/myrealtrip_mcp.py +194 -0
- package/skills/myrealtrip-search/scripts/test_myrealtrip_mcp.py +99 -0
- package/skills/myrealtrip-search/skill.json +9 -0
- package/skills/naming-house/instruction.md +146 -0
- package/skills/naming-house/skill.json +8 -0
- package/skills/national-pension-workplace/instruction.md +64 -0
- package/skills/national-pension-workplace/scripts/national_pension_workplace.py +113 -0
- package/skills/national-pension-workplace/skill.json +10 -0
- package/skills/naver-ad-performance/instruction.md +108 -0
- package/skills/naver-ad-performance/scripts/naver_ad_performance.py +240 -0
- package/skills/naver-ad-performance/skill.json +9 -0
- package/skills/naver-blog-research/instruction.md +128 -0
- package/skills/naver-blog-research/scripts/_naver_http.py +58 -0
- package/skills/naver-blog-research/scripts/naver_download_images.py +233 -0
- package/skills/naver-blog-research/scripts/naver_read.py +256 -0
- package/skills/naver-blog-research/scripts/naver_search.py +192 -0
- package/skills/naver-blog-research/skill.json +8 -0
- package/skills/naver-news-search/instruction.md +103 -0
- package/skills/naver-news-search/skill.json +10 -0
- package/skills/naver-shopping-search/instruction.md +94 -0
- package/skills/naver-shopping-search/skill.json +11 -0
- package/skills/nhis-care-checkup-search/instruction.md +116 -0
- package/skills/nhis-care-checkup-search/skill.json +10 -0
- package/skills/nts-business-registration/instruction.md +115 -0
- package/skills/nts-business-registration/scripts/nts_business_registration.py +215 -0
- package/skills/nts-business-registration/skill.json +10 -0
- package/skills/nts-tax-delinquency/instruction.md +54 -0
- package/skills/nts-tax-delinquency/scripts/nts_tax_delinquency.py +150 -0
- package/skills/nts-tax-delinquency/skill.json +8 -0
- package/skills/ohou-today-deal/instruction.md +182 -0
- package/skills/ohou-today-deal/scripts/ohou_today_deal.py +369 -0
- package/skills/ohou-today-deal/skill.json +9 -0
- package/skills/olive-young-search/instruction.md +154 -0
- package/skills/olive-young-search/skill.json +8 -0
- package/skills/parking-lot-search/instruction.md +96 -0
- package/skills/parking-lot-search/skill.json +9 -0
- package/skills/popbill/instruction.md +146 -0
- package/skills/popbill/scripts/popbill_cli.py +284 -0
- package/skills/popbill/scripts/popbill_registry.py +54 -0
- package/skills/popbill/scripts/popbill_safety.py +37 -0
- package/skills/popbill/scripts/popbill_templates.py +71 -0
- package/skills/popbill/skill.json +9 -0
- package/skills/public-restroom-nearby/instruction.md +89 -0
- package/skills/public-restroom-nearby/skill.json +9 -0
- package/skills/real-estate-search/instruction.md +172 -0
- package/skills/real-estate-search/skill.json +10 -0
- package/skills/rhwp-advanced/instruction.md +145 -0
- package/skills/rhwp-advanced/skill.json +8 -0
- package/skills/rhwp-edit/instruction.md +153 -0
- package/skills/rhwp-edit/skill.json +8 -0
- package/skills/s2b-notice-search/instruction.md +66 -0
- package/skills/s2b-notice-search/skill.json +9 -0
- package/skills/saju-fortune/instruction.md +177 -0
- package/skills/saju-fortune/skill.json +8 -0
- package/skills/saramin-talent-search/instruction.md +119 -0
- package/skills/saramin-talent-search/skill.json +9 -0
- package/skills/seoul-bike/instruction.md +83 -0
- package/skills/seoul-bike/scripts/seoul_bike.py +247 -0
- package/skills/seoul-bike/skill.json +10 -0
- package/skills/seoul-density/instruction.md +109 -0
- package/skills/seoul-density/scripts/seoul_density.py +271 -0
- package/skills/seoul-density/skill.json +10 -0
- package/skills/seoul-subway-arrival/instruction.md +85 -0
- package/skills/seoul-subway-arrival/skill.json +9 -0
- package/skills/sh-notice-search/instruction.md +150 -0
- package/skills/sh-notice-search/skill.json +9 -0
- package/skills/srt-booking/instruction.md +181 -0
- package/skills/srt-booking/scripts/srt_booking.py +272 -0
- package/skills/srt-booking/scripts/srt_seats.py +156 -0
- package/skills/srt-booking/skill.json +20 -0
- package/skills/subway-lost-property/instruction.md +93 -0
- package/skills/subway-lost-property/scripts/subway_lost_property.py +244 -0
- package/skills/subway-lost-property/skill.json +8 -0
- package/skills/ticket-availability/instruction.md +175 -0
- package/skills/ticket-availability/scripts/ticket_availability.py +430 -0
- package/skills/ticket-availability/skill.json +9 -0
- package/skills/toss-securities/instruction.md +116 -0
- package/skills/toss-securities/skill.json +9 -0
- package/skills/used-car-price-search/instruction.md +109 -0
- package/skills/used-car-price-search/skill.json +8 -0
- package/skills/yebigun-training/instruction.md +177 -0
- package/skills/yebigun-training/skill.json +9 -0
- package/skills/zipcode-search/instruction.md +159 -0
- package/skills/zipcode-search/scripts/zipcode_search.py +150 -0
- package/skills/zipcode-search/skill.json +8 -0
- package/src/assemble.js +134 -0
- package/src/detect.js +18 -0
- package/templates/action-account.md +4 -0
- package/templates/action-booking.md +5 -0
- package/templates/action-commerce.md +5 -0
- package/templates/action-communication.md +5 -0
- package/templates/action-submission.md +5 -0
- package/templates/browser.md +4 -0
- package/templates/core.md +5 -0
- package/templates/hard-boundary.md +3 -0
- package/templates/local.md +2 -0
- package/templates/lookup.md +2 -0
- package/templates/proxy.md +2 -0
- 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
|
+
}
|