@nomadamas/k-skill 0.6.0 → 0.8.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 (36) hide show
  1. package/package.json +1 -1
  2. package/skills/bccard-eatpl-search/instruction.md +124 -0
  3. package/skills/bccard-eatpl-search/references/DISCLAIMER.md +11 -0
  4. package/skills/bccard-eatpl-search/references/TRADEMARK-LEGAL-STATEMENT.md +7 -0
  5. package/skills/bccard-eatpl-search/skill.json +6 -0
  6. package/skills/komsa-ferry-info/instruction.md +54 -0
  7. package/skills/komsa-ferry-info/scripts/komsa_ferry_info.py +76 -0
  8. package/skills/komsa-ferry-info/skill.json +6 -0
  9. package/skills/korean-privacy-terms/instruction.md +1 -0
  10. package/skills/korean-privacy-terms/scripts/install.sh +17 -0
  11. package/skills/railway-timetable/instruction.md +40 -0
  12. package/skills/railway-timetable/references/AUTOMATION-LEGAL-STATEMENT.md +5 -0
  13. package/skills/railway-timetable/references/DISCLAIMER.md +7 -0
  14. package/skills/railway-timetable/references/TRADEMARK-LEGAL-STATEMENT.md +3 -0
  15. package/skills/{ktx-booking/scripts/ktx_booking.py → railway-timetable/scripts/ktx_backend.py} +0 -0
  16. package/skills/{ktx-booking → railway-timetable}/scripts/ktx_timetable.py +0 -2
  17. package/skills/railway-timetable/scripts/railway_timetable.py +95 -0
  18. package/skills/railway-timetable/scripts/railway_timetable.py.lock +27 -0
  19. package/skills/railway-timetable/skill.json +8 -0
  20. package/skills/religious-facility-search/instruction.md +195 -0
  21. package/skills/religious-facility-search/scripts/religious_facility_search.py +277 -0
  22. package/skills/religious-facility-search/skill.json +9 -0
  23. package/skills/store-longevity-radar/scripts/__pycache__/store_longevity_download.cpython-312.pyc +0 -0
  24. package/skills/ktx-booking/instruction.md +0 -61
  25. package/skills/ktx-booking/references/AUTOMATION-LEGAL-STATEMENT.md +0 -10
  26. package/skills/ktx-booking/references/DISCLAIMER.md +0 -14
  27. package/skills/ktx-booking/references/TRADEMARK-LEGAL-STATEMENT.md +0 -9
  28. package/skills/ktx-booking/skill.json +0 -8
  29. package/skills/srt-booking/instruction.md +0 -75
  30. package/skills/srt-booking/references/AUTOMATION-LEGAL-STATEMENT.md +0 -25
  31. package/skills/srt-booking/references/DISCLAIMER.md +0 -13
  32. package/skills/srt-booking/references/TRADEMARK-LEGAL-STATEMENT.md +0 -9
  33. package/skills/srt-booking/scripts/srt_booking.py +0 -171
  34. package/skills/srt-booking/scripts/srt_booking.py.lock +0 -207
  35. package/skills/srt-booking/skill.json +0 -8
  36. /package/skills/{ktx-booking/scripts/ktx_booking.py.lock → railway-timetable/scripts/ktx_backend.py.lock} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nomadamas/k-skill",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "k-skill unified CLI: assembles runtime-aware skill instructions and ships bundled helper files",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -0,0 +1,124 @@
1
+ # BC카드 eat.pl 맛집 검색
2
+
3
+ ## What this skill does
4
+
5
+ 한국에서 맛집, 음식점, 식당, 밥집, 카페, 고깃집, 회식 장소를 찾을 때 쓴다. 사용자가 말한 장소를 먼저 `kakao-map`으로 정확한 도로명주소로 해석한 뒤, 그 주소를 중심으로 BC카드 eat.pl 잇플의 가맹점 검색을 조회한다. 결과는 반환 매출 랭킹 상위 순서를 보존하며, 사용자의 장르·동네·외국인 방문·회식·영업시간·혼잡 시간 조건으로 좁혀 표로 제시한다.
6
+
7
+ 추천은 별점 순위가 아니라 최근 1개월 BC카드 결제 데이터 기반 매출 순이다. 매출액(원)은 없고, 대신 `allSaleRnk`/`ccgSaleRnk` 백분위로 **얼마나 상위인지**를 반드시 숫자로 보여 준다. `srtTime`/`endTime`은 **영업 예상 시간**으로 정리한다. 모든 행에 eat.pl 상세 링크를 첨부한다.
8
+
9
+ ## 사용자 답변 규칙 (필수)
10
+
11
+ 사용자에게 보이는 답변에는 구현·연동 세부사항을 **절대 쓰지 않는다.** 결과는 맛집 추천처럼만 보여 준다.
12
+
13
+ 답변에 넣지 말 것:
14
+
15
+ - endpoint, URL 경로, HTTP 메서드, curl, 프록시, 서버, 환경변수, 기관코드, 토큰, 상태코드
16
+ - 내부 필드명 (`location`, `merTpbuzNm`, `allSaleRnk`, `ccgSaleRnk`, `merUrl`, `frnrVsitYn`, `empyVsitYn`, `srtTime` 등)
17
+ - `k-skill-proxy`, 릴레이, Lightsail, gpu01, API, JSON, 캐시, 좌표 체계
18
+
19
+ 표의 전국·지역 매출 상위 %, 영업 예상 시간, `[eat.pl 상세](url)`, 출처 한 줄은 출력 계약대로 유지한다. 실패해도 오류 코드나 엔드포인트를 말하지 말고, 그 조건으로는 매장을 찾지 못했다고만 안내한다.
20
+
21
+ ## When to use
22
+
23
+ 먹을 곳, 식당, 카페를 달라는 요청이면 이 스킬을 쓴다. 예:
24
+
25
+ - "맛집 추천해줘", "근처 음식점", "밥 먹을 곳", "분위기 좋은 식당"
26
+ - "강남역 근처 고기집 중 매출 높은 곳"
27
+ - "을지로입구역 주변 외국인이 많이 가는 카페"
28
+ - "성수동에서 회식하기 좋은 맛집"
29
+ - "이 동네 카페", "디저트 맛집", "회식 장소"
30
+
31
+ 술집·바만 찾는 요청은 `kakao-bar-nearby`를 우선한다.
32
+
33
+ ## Workflow
34
+
35
+ 아래 절차는 에이전트 내부 작업이다. 사용자 답변에 그대로 옮기지 않는다.
36
+
37
+ 1. 장소명이 모호하면 먼저 행정구역 또는 도로명주소를 확인한다. `kakao-map` 스킬을 우선 사용한다. 이 경로는 k-skill-proxy가 중계하는 **카카오 공식 Local REST API** (`GET /v1/kakao-map/search/keyword`, `coord2address` 등)이다. 카카오맵 웹 페이지를 스크래핑하지 않는다. 사용자가 지정한 경우 다른 지도 스킬로 교차 확인할 수 있다.
38
+ 2. 검색 중심은 장소의 정확한 주소에서 최소한 시·군·구와 도로명/동 단위로 정한다. eat.pl API는 텍스트 주소 부분일치 검색이며 반경(meter) 검색 API가 아니므로, API 결과를 실제 근처로 단정하지 말고 지도 스킬로 위치를 교차 확인한다.
39
+ 3. 다음 프록시 endpoint를 GET으로 호출한다.
40
+
41
+ ```bash
42
+ curl -fsS --get "${KSKILL_PROXY_BASE_URL:-https://k-skill-proxy.nomadamas.org}/v1/bccard-eatpl/search" \
43
+ --data-urlencode 'location=서울 중구 을지로30길 20' \
44
+ --data-urlencode 'merTpbuzNm=카페'
45
+ ```
46
+
47
+ 지원 입력:
48
+
49
+ - `location` 또는 `address`: 정확한 주소/지역 문자열
50
+ - `merTpbuzNm`, `genre`, `business_type`: eat.pl 대분류 또는 소분류
51
+ - `merNm`, `merchant_name`: 가맹점명 일부
52
+
53
+ 4. 최대 100건을 받은 뒤 지도 검색 결과와 주소·상호를 대조하고, 요청 조건에 맞는 매장만 남긴다. 존재하지 않는 리뷰·메뉴·주차·영업 여부를 추측하지 않는다.
54
+ 5. 아래 **출력 계약**을 만족하는 표만 사용자에게 보여 준다. 계약을 빠뜨린 표는 미완성이다.
55
+ 6. 반드시 `eat.pl 잇플 · BC카드 결제 데이터 기반`을 출처로 표기한다.
56
+
57
+ ## 출력 계약 (필수)
58
+
59
+ 표 컬럼: `가맹점명`, `주소`, `업종`, `전국 매출 상위`, `지역 매출 상위`, `동네 맛집`, `외국인 방문`, `회식 적합`, `영업 예상 시간`, `혼잡 요일/시간`, `주요 고객층`, `eat.pl 상세 링크`.
60
+
61
+ ### 매출이 얼마나 높은지
62
+
63
+ - 매장마다 `전국 매출 상위 {allSaleRnk}%`, `지역 매출 상위 {ccgSaleRnk}%`를 빠짐없이 숫자로 쓴다. "매출이 높다"만 쓰지 않는다. 필드 이름 자체는 사용자에게 쓰지 않는다.
64
+ - 백분위 값이 **낮을수록** 최근 1개월 BC카드 결제 매출 순위가 높다. `상위 0.09%`는 `상위 3%`보다 훨씬 위다.
65
+ - 1% 미만은 소수점을 보존한다. 예: `0.04` → `전국 매출 상위 0.04%`.
66
+ - 실제 매출액(원)은 제공되지 않는다. 백분위가 높낮이 정보다.
67
+
68
+ ### 영업 예상 시간
69
+
70
+ - `srtTime`/`endTime`(HHMM)을 `영업 예상 시간 HH:MM–HH:MM`으로 변환한다. 예: `0702`+`2246` → `영업 예상 시간 07:02–22:46`.
71
+ - 종료 시각이 시작보다 이르면 자정 넘김으로 `영업 예상 시간 18:14–익일 05:56`처럼 적는다.
72
+ - 표 밖 한 줄로 "최근 1개월 결제 승인 시각 기반 추정이므로 공식 영업시간이 아님"을 고지한다.
73
+
74
+ ### 상세 링크 강제
75
+
76
+ - **모든 행**에 응답 `merUrl`을 마크다운 링크로 첨부한다: `[eat.pl 상세]({merUrl})`.
77
+ - `merUrl`이 없거나 `https://web.paybooc.ai/`로 시작하지 않으면 그 행을 출력하지 않는다.
78
+ - 링크가 빠진 목록·표는 완성 결과가 아니다. URL 경로를 조합하거나 추측하지 않는다.
79
+
80
+ ## Credentials and proxy operations
81
+
82
+ 사용자에게 credentials를 묻거나 출력하지 않는다. 운영 환경에 다음 값을 secret으로 주입한다.
83
+
84
+ - `BCCARD_EATPL_INST_NM`: 제휴 시 발급된 기관코드
85
+ - `BCCARD_EATPL_API_BASE_URL`: 필수값, 기본값 없음 (미설정 시 503 `upstream_not_configured`). 운영 URL은 `https://api.paybooc.ai/api/mer`
86
+ - `BCCARD_EATPL_RELAY_URL`: 선택값, 고정 outbound IP relay URL. 설정하면 proxy는 이 URL만 호출하고, 토큰 없이는 호출하지 않는다
87
+ - `BCCARD_EATPL_RELAY_TOKEN`: 선택값, relay `Authorization: Bearer` 시크릿. gpu01 `k-skill-proxy` `.env`와 Lightsail relay env에만 저장한다. 사용자·클라이언트는 Lightsail을 직접 호출하지 않는다
88
+ - `BCCARD_EATPL_API_TIMEOUT_MS`: 선택값, 기본 20000
89
+
90
+ `trnsTrceNo`는 사용자가 입력하지 않는다. proxy가 기관코드 + 한국시간 기준 `YYYYMMDD` + 10자리 순번으로 매 요청 생성한다.
91
+
92
+ ## Important interpretation
93
+
94
+ - 결과 자체가 매출 랭킹 상위 순이며, 별도 매출액은 제공되지 않는다. 높낮이는 전국·지역 백분위로만 말한다.
95
+ - `allSaleRnk`/`ccgSaleRnk`는 퍼센트 백분위이며 낮을수록 상위다. 사용자 답변에는 필드명 대신 `전국 매출 상위 N%`로만 쓴다.
96
+ - `srtTime`/`endTime`은 최근 1개월 결제 승인 시각 기반 **영업 예상 시간**이지 영업시간 공시가 아니다.
97
+ - 실시간 영업 여부는 eat.pl 상세 페이지를 확인해야 하며, 조회 시점 관측과 다를 수 있다.
98
+ - 전화번호는 유효하지 않을 수 있다.
99
+ - 좌표는 TM128이므로 WGS84 지도 좌표로 직접 해석하지 않는다. 이 사실을 사용자에게 설명하지 않는다.
100
+
101
+ ## Failure modes
102
+
103
+ 내부 처리 기준이다. 사용자에게 코드명을 말하지 않는다.
104
+
105
+ - `400 bad_request`: 주소·장르·가맹점명 중 하나도 없거나 입력이 잘못됨
106
+ - `503 upstream_not_configured`: `BCCARD_EATPL_INST_NM` 미설정
107
+ - `403/401 upstream_error`: IP 등록, 기관코드, 제휴 권한 또는 upstream 인증 문제
108
+ - `502 upstream_error`: eat.pl 연결·서버 오류
109
+ - `502 upstream_semantic_error`: `rspCode`가 성공이 아니거나 응답 구조가 명세와 다름
110
+ - 개발 서버 연결 타임아웃: 개발 접근 정보 또는 네트워크/IP allowlist를 BC카드 측에 확인
111
+ - 빈 결과: 주소 텍스트가 너무 좁거나 업종 표기가 맞지 않을 수 있으므로 임의 재시도하지 말고 주소 범위를 넓히거나 명세의 업종값을 확인
112
+
113
+ ## Legal and partnership notice
114
+
115
+ 이 스킬은 BC카드와의 공식 제휴 연동을 위해 제공되는 k-skill 구현물이라는 전제에서 사용한다. 다만 이 저장소와 k-skill 자체가 BC카드의 공식 제품·상표·지원 채널이라는 의미는 아니며, 실제 운영 전에는 BC카드의 제휴 승인·기관코드 발급·IP 등록·노출 문구 검수를 완료해야 한다. 출처는 항상 `eat.pl 잇플 · BC카드 결제 데이터 기반`으로 표시한다.
116
+
117
+ ## Done when
118
+
119
+ - 장소를 `kakao-map`(k-skill-proxy 카카오 공식 Local API)으로 주소로 해석했다.
120
+ - eat.pl 결과를 최대 100건까지 받아 지도 정보와 대조했다.
121
+ - 사용자의 조건으로 필터링한 표에 전국·지역 매출 상위 %, 영업 예상 시간, eat.pl 상세 링크가 **모든 행**에 있다.
122
+ - 매출 순 추천과 최근 1개월 결제 데이터 기준임을 설명했다.
123
+ - 출처·제휴 전제·데이터 한계를 함께 고지했다.
124
+ - 사용자 답변에 endpoint, 프록시, 환경변수, 내부 필드명이 없다.
@@ -0,0 +1,11 @@
1
+ # DISCLAIMER — `bccard-eatpl-search`
2
+
3
+ 이 스킬은 BC카드와 공식 제휴 연동을 전제로 설계된 구현물이다. 그러나 이 저장소, k-skill, 또는 이 스킬 자체가 BC카드·eat.pl 잇플의 공식 기능·공식 제품·공식 지원 채널·상표권자라는 뜻은 아니다. 서비스의 공식 제휴·협업 범위는 별도 계약으로 확인하며, 운영 노출 전에는 BC카드의 제휴 승인, 기관코드 발급, 호출 IP 등록, 개발·운영 endpoint 승인 및 표시 문구 검수를 완료해야 한다. 명칭은 기능 설명·조회 대상·링크 식별에만 사용한다.
4
+
5
+ 결과는 최근 1개월 BC카드 결제 데이터에서 집계된 가맹점 단위 정보다. 실제 매출액, 고객 개인 정보, 리뷰 평점, 현재 영업을 보증하지 않는다. `merUrl` 상세 페이지의 실시간 결제 기반 표시는 API의 분석 기반 영업 관측과 다를 수 있다.
6
+
7
+ 대법원 2005. 6. 10. 선고 [2005도1637 판결](https://www.law.go.kr/LSW/precInfoP.do?precSeq=83920)과 대법원 2022. 5. 12. 선고 [2021도1533 판결](https://www.law.go.kr/LSW/precInfoP.do?precSeq=221765)의 취지를 포함해, [상표법 제2조](https://www.law.go.kr/법령/상표법/제2조), [제89조](https://www.law.go.kr/법령/상표법/제89조), [제90조](https://www.law.go.kr/법령/상표법/제90조), [제108조](https://www.law.go.kr/법령/상표법/제108조), [정보통신망법 제48조](https://www.law.go.kr/법령/정보통신망이용촉진및정보보호등에관한법률/제48조), [저작권법 제93조](https://www.law.go.kr/법령/저작권법/제93조), [형법 제314조 제2항](https://www.law.go.kr/법령/형법/제314조) 등 관련 법령과 계약 조건을 별도로 준수해야 한다.
8
+
9
+ 이 스킬은 공개적으로 허용된 공식 API와 지도 서비스만 사용한다. CAPTCHA, 인증, IP 제한, 접근통제, quota 또는 서비스 운영 정책을 우회하지 않는다. 영업 방해나 서비스 장애를 일으키는 조직적·대량 크롤링, 재배포·데이터베이스 구축이 아니라 개인의 장소 선택을 위한 제한된 조회에 사용한다.
10
+
11
+ 이 문서는 법률 자문이나 BC카드의 최종 승인을 대신하지 않는다.
@@ -0,0 +1,7 @@
1
+ # 상표 사용 법적 고지 — `bccard-eatpl-search`
2
+
3
+ 이 스킬에서 `BC카드`, `eat.pl`, `잇플`, `paybooc.ai` 명칭은 연동 대상 서비스와 응답 상세 링크를 식별하기 위해 필요한 범위에서 사용한다. k-skill의 출처를 해당 권리자와 혼동시키려는 사용이 아니다. 실제 공식 제휴·후원·승인 범위는 별도의 계약과 검수로 확인해야 한다.
4
+
5
+ 대법원 2005. 6. 10. 선고 [2005도1637 판결](https://www.law.go.kr/LSW/precInfoP.do?precSeq=83920)은 타인의 표장을 출처표시가 아니라 상품 기능 또는 적용 기종을 밝히기 위해 사용하고 상표 사용으로 인식될 수 없는 경우 침해가 아니라고 판시했다. [상표법 제2조](https://www.law.go.kr/법령/상표법/제2조), [제89조](https://www.law.go.kr/법령/상표법/제89조), [제90조](https://www.law.go.kr/법령/상표법/제90조), [제108조](https://www.law.go.kr/법령/상표법/제108조)에 따른 출처 혼동 판단은 표시 태양과 사용 경위에 따라 별도로 검토해야 한다.
6
+
7
+ 따라서 명칭은 기능 설명과 링크 식별에 필요한 평문으로만 사용하고, 별도 근거 없이 로고·인증·파트너 표현을 사용하지 않는다. 이 문서는 법률 자문이나 BC카드의 최종 승인을 대신하지 않는다.
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "bccard-eatpl-search",
3
+ "description": "대한민국 맛집, 음식점, 식당, 밥집, 카페, 커피, 디저트, 고깃집, 회식 장소를 지역·역·동네 기준으로 찾아 준다. Use when the user asks for 맛집, 음식점, 좋은 식당, 근처 카페, 밥 먹을 곳, 회식하기 좋은 곳, 분위기 좋은 맛집, or Korean restaurant and cafe recommendations. 별점이 아니라 최근 결제 데이터 기반 매출 상위로 정리한다.",
4
+ "profiles": ["proxy", "lookup"],
5
+ "frontmatter": "name: bccard-eatpl-search\ndescription: 대한민국 맛집, 음식점, 식당, 밥집, 카페, 커피, 디저트, 고깃집, 회식 장소를 지역·역·동네 기준으로 찾아 준다. Use when the user asks for 맛집, 음식점, 좋은 식당, 근처 카페, 밥 먹을 곳, 회식하기 좋은 곳, 분위기 좋은 맛집, or Korean restaurant and cafe recommendations. 별점이 아니라 최근 결제 데이터 기반 매출 상위로 정리한다.\nlicense: MIT\nmetadata:\n category: food\n locale: ko-KR\n phase: v1"
6
+ }
@@ -0,0 +1,54 @@
1
+ # KOMSA 연안여객선 정보 조회
2
+
3
+ ## What this skill does
4
+
5
+ 한국해양교통안전공단(KOMSA) MTIS Open API를 통해 연안여객선 운항 일정, 선박 제원, 기항지, 면허항로, 운항항로, 운항상태를 조회한다. 예매·결제·로그인 자동화는 하지 않는다.
6
+
7
+ ## Public access path
8
+
9
+ 기본 경로는 hosted/self-host `k-skill-proxy`의 좁은 read-only route다.
10
+
11
+ - 공식 포털: <https://mtisopenapi.komsa.or.kr/>
12
+ - 운항 일정: `https://mtisopenapi.komsa.or.kr/eopt/api/oprt-schd-info`
13
+ - 운항노선: `https://mtisopenapi.komsa.or.kr/eopt/api/oprt-line-info`
14
+ - 선박 제원: `https://mtisopenapi.komsa.or.kr/eopt/api/psnshp-spec`
15
+ - 프록시: `GET /v1/komsa/ferry/{dataset}`
16
+
17
+ 지원 dataset은 `schedules`, `vessels`, `ports`, `license-routes`, `operation-routes`, `status`다. upstream `serviceKey`는 프록시 서버의 `KOMSA_MTIS_API_KEY` 또는 `KSKILL_KOMSA_MTIS_API_KEY`로만 주입한다.
18
+
19
+ ## Workflow
20
+
21
+ Helper 실행:
22
+
23
+ ```bash
24
+ npx -y @nomadamas/k-skill@0 exec komsa-ferry-info scripts/komsa_ferry_info.py -- \
25
+ schedules --date 20260824 --vessel 섬사랑12호 --limit 10
26
+ ```
27
+
28
+ 프록시를 직접 확인하려면:
29
+
30
+ ```bash
31
+ BASE="${KSKILL_PROXY_BASE_URL:-https://k-skill-proxy.nomadamas.org}"
32
+ curl -fsS --get "$BASE/v1/komsa/ferry/schedules" \
33
+ --data-urlencode "date=20260824" \
34
+ --data-urlencode "vessel=섬사랑12호" \
35
+ --data-urlencode "limit=10"
36
+ ```
37
+
38
+ 입력은 날짜(`YYYYMMDD` 또는 `YYYY-MM-DD`), 선박명, 항로명, 기항지명, 페이지와 결과 수를 사용한다. 공식 MTIS 조회 파라미터는 명칭 필터이며, 매핑되지 않은 코드 필터는 `400`이다. 응답의 `items`는 공식 원문 필드를 보존하며, 일정 응답에는 출항일자·시각·출발지·도착지·항로·운항상태가 포함될 수 있다.
39
+
40
+ ## Done when
41
+
42
+ - 공식 MTIS route를 통해 선택한 dataset을 조회했다.
43
+ - 결과에 조회 dataset, 페이지, 결과 수와 공식 upstream 출처를 함께 표시했다.
44
+ - 데이터가 5분 주기로 갱신될 수 있음을 밝히고, 운항정보를 예매 가능 좌석이나 결제 결과로 해석하지 않았다.
45
+
46
+ ## Failure modes
47
+
48
+ - `400 bad_request`: 허용되지 않은 dataset, 매핑되지 않은 필터, 잘못된 날짜, 페이지 또는 결과 수.
49
+ - `503 upstream_not_configured`: 프록시 운영자에게 `KOMSA_MTIS_API_KEY` 설정이 필요하다.
50
+ - `502 upstream_error` / `502 upstream_invalid_response`: MTIS 인증 오류, quota 초과, upstream 장애 또는 응답 형식 변경.
51
+ - `504 upstream_timeout`: 공식 API가 시간 내 응답하지 않음.
52
+ - `total_count = 0`: 날짜·항로·선박 조건에 맞는 결과 없음.
53
+
54
+ MTIS OpenAPI 이용신청/키 발급은 공식 포털 회원가입·로그인 후 진행한다. 키를 채팅, 저장소, GitHub Actions에 넣지 않는다.
@@ -0,0 +1,76 @@
1
+ """Query KOMSA MTIS ferry information through k-skill-proxy."""
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
+
11
+ DEFAULT_PROXY = "https://k-skill-proxy.nomadamas.org"
12
+
13
+
14
+ def proxy_base_url(value=None, env=None):
15
+ env = os.environ if env is None else env
16
+ candidate = (value or env.get("KSKILL_PROXY_BASE_URL") or DEFAULT_PROXY).strip()
17
+ if candidate.casefold() in {"off", "false", "0", "none"}:
18
+ raise ValueError("KSKILL_PROXY_BASE_URL is disabled.")
19
+ return candidate.rstrip("/")
20
+
21
+
22
+ def query(dataset, params, *, base_url=None, read_json=None):
23
+ url = f"{proxy_base_url(base_url)}/v1/komsa/ferry/{urllib.parse.quote(dataset, safe='')}"
24
+ query_string = urllib.parse.urlencode({k: v for k, v in params.items() if v is not None})
25
+ request = urllib.request.Request(
26
+ f"{url}?{query_string}",
27
+ headers={"Accept": "application/json", "User-Agent": "k-skill-komsa-ferry-info/1.0"},
28
+ method="GET",
29
+ )
30
+ if read_json:
31
+ return read_json(request)
32
+ try:
33
+ with urllib.request.urlopen(request, timeout=30) as response:
34
+ return json.loads(response.read().decode("utf-8"))
35
+ except urllib.error.HTTPError as error:
36
+ body = error.read().decode("utf-8", errors="replace")
37
+ try:
38
+ payload = json.loads(body)
39
+ except json.JSONDecodeError:
40
+ payload = {"error": f"HTTP {error.code}", "message": body}
41
+ raise RuntimeError(json.dumps(payload, ensure_ascii=False)) from error
42
+
43
+
44
+ def parser():
45
+ result = argparse.ArgumentParser(description="KOMSA MTIS 연안여객선 정보 조회")
46
+ result.add_argument("dataset", choices=["schedules", "vessels", "ports", "license-routes", "operation-routes", "status"])
47
+ result.add_argument("--date")
48
+ result.add_argument("--vessel")
49
+ result.add_argument("--route")
50
+ result.add_argument("--port")
51
+ result.add_argument("--page", default="1")
52
+ result.add_argument("--limit", default="10")
53
+ result.add_argument("--proxy-base-url")
54
+ return result
55
+
56
+
57
+ def main(argv=None):
58
+ args = parser().parse_args(argv)
59
+ params = {
60
+ "date": args.date,
61
+ "vessel": args.vessel,
62
+ "route": args.route,
63
+ "port": args.port,
64
+ "page": args.page,
65
+ "limit": args.limit,
66
+ }
67
+ try:
68
+ print(json.dumps(query(args.dataset, params, base_url=args.proxy_base_url), ensure_ascii=False, indent=2))
69
+ return 0
70
+ except (ValueError, RuntimeError) as error:
71
+ print(json.dumps({"error": str(error)}, ensure_ascii=False), file=sys.stderr)
72
+ return 1
73
+
74
+
75
+ if __name__ == "__main__":
76
+ raise SystemExit(main())
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "komsa-ferry-info",
3
+ "description": "한국해양교통안전공단 KOMSA MTIS 공식 API로 연안여객선 운항 일정과 선박·항로 정보를 조회한다.",
4
+ "profiles": ["proxy", "lookup"],
5
+ "frontmatter": "name: komsa-ferry-info\ndescription: 한국해양교통안전공단 KOMSA MTIS 공식 API로 연안여객선 운항 일정과 선박·항로 정보를 조회한다.\nlicense: MIT\nmetadata:\n category: transport\n locale: ko-KR\n phase: v1"
6
+ }
@@ -115,6 +115,7 @@ git -C ~/.agents/skills/korean-privacy-terms/upstream rev-parse HEAD
115
115
  - pin SHA 가 업스트림에서 삭제/force-push 된 경우: 스크립트가 SHA mismatch 로 비정상 종료한다. `scripts/upstream.pin` 을 최신 태그 SHA 로 bump 하고 PR 을 만든다.
116
116
  - Next.js Pages Router 단독 프로젝트: 업스트림이 실행을 중단한다. 사용자에게 App Router 전환이 선행 조건임을 안내한다.
117
117
  - 법률 개정 드리프트: 업스트림이 CHANGELOG 로 반영 기준을 관리한다. pin 만 올리지 말고 업스트림 CHANGELOG 를 함께 확인한다.
118
+ - 한글 이용약관 템플릿 0바이트 (upstream, #660): pin 시점 기준 `jurisdictions/kr-pipa/terms-of-service.ko.mdx.tmpl` 과 `templates/terms-of-service.mdx.tmpl` 이 빈 파일이라 한국어(`outputLocale: ko`) 경로에서 이용약관을 생성할 수 없다. 개인정보처리방침(ko/en)과 영문 약관은 정상 생성된다. 업스트림에 템플릿이 채워지면 `scripts/upstream.pin` bump 로 해소한다. `install.sh` 가 설치 시 0바이트 템플릿을 경고로 표면화하므로 인터뷰 시작 전에 확인한다.
118
119
 
119
120
  ## Notes
120
121
 
@@ -66,6 +66,23 @@ if [[ "${HEAD_SHA}" != "${UPSTREAM_SHA}" ]]; then
66
66
  exit 1
67
67
  fi
68
68
 
69
+ # 0바이트 템플릿 검증 (#660). 업스트림 파일이 빈 채 커밋돼 있으면 해당 경로의
70
+ # 문서 생성이 조용히 실패하므로 설치 시점에 경고로 표면화한다. 다른 경로의
71
+ # 문서는 정상 생성되므로 경고만 하고 설치는 계속한다.
72
+ EMPTY_TEMPLATES=()
73
+ while IFS= read -r -d '' EMPTY_FILE; do
74
+ EMPTY_TEMPLATES+=("${EMPTY_FILE#"${CLONE_DIR}/"}")
75
+ done < <(find "${CLONE_DIR}" -type f -name '*.tmpl' -size 0 -print0)
76
+
77
+ if (( ${#EMPTY_TEMPLATES[@]} > 0 )); then
78
+ echo "[korean-privacy-terms] warning: pinned upstream 에 0바이트 템플릿 ${#EMPTY_TEMPLATES[@]}건이 있다." >&2
79
+ for EMPTY_TEMPLATE in "${EMPTY_TEMPLATES[@]}"; do
80
+ echo " - ${EMPTY_TEMPLATE}" >&2
81
+ done
82
+ echo " 해당 템플릿 경로의 문서는 생성되지 않는다. 나머지 경로는 정상 생성된다." >&2
83
+ echo " 상세와 해소 상태는 스킬 instruction.md 의 Failure modes 를 확인해달라." >&2
84
+ fi
85
+
69
86
  # Dual-install targets. Both paths respect AGENTS.md indirection rules and do not
70
87
  # introduce repo-local skill directories.
71
88
  HOME_DIRS=(
@@ -0,0 +1,40 @@
1
+ # Unified Railway Timetable Lookup
2
+
3
+ ## What this skill does
4
+
5
+ `railway-timetable`은 코레일 공식 통합 시간표에서 KTX 계열 고속철도 운행 정보를 조회한다.
6
+
7
+ - KTX: 한국철도공사 공식 공개 XLSX 통합 계획 시간표
8
+
9
+ 모든 경로는 조회 전용이다. 로그인·credential, 예약·예약대기·좌석 선점·결제·취소·자동 재조회는 실행하지 않는다.
10
+
11
+ ## Commands
12
+
13
+ ```bash
14
+ npx -y @nomadamas/k-skill@0 exec railway-timetable scripts/railway_timetable.py -- \
15
+ search --dep 서울 --arr 부산 --date 20260904 \
16
+ --time 0600 --time-limit 1200 --limit 5
17
+ ```
18
+
19
+ ```bash
20
+ npx -y @nomadamas/k-skill@0 exec railway-timetable scripts/railway_timetable.py -- source
21
+ ```
22
+
23
+ ## Output
24
+
25
+ - `count`, `trains[]`, `date`
26
+ - 각 열차의 `operator`, `train_no`, `train_type`, `dep`, `arr`, `dep_time`, `arr_time`
27
+ - 공식 `source`와 예약 진입 URL
28
+
29
+ 결과는 코레일 통합 공개 계획 시간표이며 실제 운휴·지연, 예약 성공, 좌석 선점을 보장하지 않는다.
30
+
31
+ ## Sources and failure modes
32
+
33
+ - 코레일: 공개 게시판의 XLSX를 읽는다. 게시판·파일 장애, 적용 시간표 없음, 형식 변경, 구간 결과 없음이 발생할 수 있다.
34
+ - CAPTCHA·인증·접근 차단은 우회하지 않고 코레일 공식 페이지를 안내한다.
35
+
36
+ ## Hard boundaries
37
+
38
+ - 회원 로그인·credential 요청 금지
39
+ - 예약·결제·취소·정확한 좌석 선택·자동 polling 금지
40
+ - 내부 모바일 API, anti-bot, CAPTCHA, 인증 통제 우회 금지
@@ -0,0 +1,5 @@
1
+ # 철도 통합 시간표 조회 관련 고지
2
+
3
+ - KTX 계열 코레일 통합 시간표는 공개 XLSX 계획 시간표만 읽는다.
4
+ - 예약·예약대기·좌석 선점·결제·취소·자동 재조회는 실행하지 않는다.
5
+ - CAPTCHA, 본인인증, anti-bot, 접근 제한을 우회하지 않는다.
@@ -0,0 +1,7 @@
1
+ # DISCLAIMER — `railway-timetable`
2
+
3
+ 이 스킬은 한국철도공사·코레일 또는 다른 철도 운영사의 공식 기능이나 공식 지원 도구가 아니며, 제휴·후원·승인·인증 또는 협업한 사실이 없습니다. 명칭과 상표는 조회 대상 서비스를 식별하기 위해서만 사용합니다.
4
+
5
+ 공개 정보의 개인적 조회는 비조직적 용도로 제한한다. 체계적·대량 수집, 데이터베이스 구축, 접근통제·차단 우회, 서비스 운영을 방해하는 행위에 사용하지 않는다.
6
+
7
+ 관련 기준으로 대법원 2005도1637 및 2021도1533 판결, 정보통신망법 제48조와 저작권법 제93조를 참고한다. 이 문서는 개별 사실관계에 대한 법률 자문이나 위법성 판단이 아니다.
@@ -0,0 +1,3 @@
1
+ # 상표 사용 법적 고지 — `railway-timetable`
2
+
3
+ `KTX`, `코레일`, `Korail` 명칭은 조회 대상 열차·운영기관을 식별하기 위해서만 사용한다. k-skill이 해당 운영기관의 공식 제품이거나 승인받았다는 의미가 아니다.
@@ -120,8 +120,6 @@ def parse_timetable_rows(
120
120
  if section.type_index >= 0
121
121
  else "KTX"
122
122
  )
123
- if "KTX" not in train_type:
124
- continue
125
123
  if section.note_index >= 0 and not runs_on_date(row[section.note_index], requested_date):
126
124
  continue
127
125
  dep_time = normalize_time(row[section.dep_index])
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env -S uv run --locked --script
2
+ # /// script
3
+ # requires-python = ">=3.11"
4
+ # dependencies = ["openpyxl==3.1.5"]
5
+ # ///
6
+ """Read-only Korail integrated railway timetable lookup."""
7
+
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import json
12
+ import sys
13
+ from typing import Any
14
+
15
+ import ktx_backend
16
+
17
+
18
+ def source_info() -> dict[str, Any]:
19
+ return {
20
+ "mode": "plan",
21
+ "transport": "Korail integrated timetable",
22
+ "operator": "한국철도공사",
23
+ "endpoint": ktx_backend.BOARD_URL,
24
+ "authentication": "none",
25
+ "mutation": "none; timetable lookup only",
26
+ "booking_url": ktx_backend.BOOKING_URL,
27
+ }
28
+
29
+
30
+ def search(
31
+ *,
32
+ dep: str,
33
+ arr: str,
34
+ date: str,
35
+ earliest: str,
36
+ latest: str,
37
+ limit: int,
38
+ ) -> dict[str, Any]:
39
+ result = ktx_backend.search_public_timetable(
40
+ dep=dep,
41
+ arr=arr,
42
+ date=date,
43
+ earliest=earliest,
44
+ latest=latest,
45
+ limit=limit,
46
+ )
47
+ return {
48
+ **result,
49
+ "schedule_note": "코레일 통합 공개 운행계획 기준이며 실시간 운휴·지연·잔여석 정보가 아닙니다.",
50
+ "source": {**result["source"], "transport": "Korail integrated timetable"},
51
+ }
52
+
53
+
54
+ def build_parser() -> argparse.ArgumentParser:
55
+ parser = argparse.ArgumentParser(
56
+ description="Korail integrated read-only railway timetable lookup",
57
+ )
58
+ commands = parser.add_subparsers(dest="command", required=True)
59
+ search_parser = commands.add_parser("search", help="search railway timetables")
60
+ search_parser.add_argument("--dep", required=True)
61
+ search_parser.add_argument("--arr", required=True)
62
+ search_parser.add_argument("--date", required=True, help="YYYYMMDD")
63
+ search_parser.add_argument("--time", default="0000", help="earliest departure, HHMM")
64
+ search_parser.add_argument("--time-limit", default="2359", help="latest departure, HHMM")
65
+ search_parser.add_argument("--limit", type=int, default=10)
66
+ commands.add_parser("source", help="show read-only timetable sources")
67
+ return parser
68
+
69
+
70
+ def main(argv: list[str] | None = None) -> int:
71
+ parser = build_parser()
72
+ args = parser.parse_args(argv)
73
+ try:
74
+ if args.command == "source":
75
+ print(json.dumps(source_info(), ensure_ascii=False, indent=2))
76
+ return 0
77
+ if args.limit < 1 or args.limit > 50:
78
+ raise ValueError("--limit must be between 1 and 50")
79
+ result = search(
80
+ dep=args.dep,
81
+ arr=args.arr,
82
+ date=args.date,
83
+ earliest=args.time,
84
+ latest=args.time_limit,
85
+ limit=args.limit,
86
+ )
87
+ print(json.dumps(result, ensure_ascii=False, indent=2))
88
+ return 0
89
+ except (RuntimeError, ValueError) as exc:
90
+ parser.error(str(exc))
91
+ return 2
92
+
93
+
94
+ if __name__ == "__main__":
95
+ sys.exit(main())
@@ -0,0 +1,27 @@
1
+ version = 1
2
+ revision = 1
3
+ requires-python = ">=3.11"
4
+
5
+ [manifest]
6
+ requirements = [{ name = "openpyxl", specifier = "==3.1.5" }]
7
+
8
+ [[package]]
9
+ name = "et-xmlfile"
10
+ version = "2.0.0"
11
+ source = { registry = "https://pypi.org/simple" }
12
+ sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234 }
13
+ wheels = [
14
+ { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059 },
15
+ ]
16
+
17
+ [[package]]
18
+ name = "openpyxl"
19
+ version = "3.1.5"
20
+ source = { registry = "https://pypi.org/simple" }
21
+ dependencies = [
22
+ { name = "et-xmlfile" },
23
+ ]
24
+ sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464 }
25
+ wheels = [
26
+ { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910 },
27
+ ]
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "railway-timetable",
3
+ "description": "Read-only Korail integrated railway timetable lookup covering KTX-family trains. No reservation, payment, cancellation, or seat holding.",
4
+ "profiles": [
5
+ "lookup"
6
+ ],
7
+ "frontmatter": "name: railway-timetable\ndescription: Read-only Korail integrated railway timetable lookup covering KTX-family trains. No reservation, payment, cancellation, or seat holding.\nlicense: MIT\nmetadata:\n category: travel\n locale: ko-KR\n phase: v1"
8
+ }