@nomadamas/k-skill 0.4.0 → 0.4.1
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/package.json +1 -1
- package/skills/corporate-registration-consulting/instruction.md +9 -0
- package/skills/iros-registry-automation/instruction.md +7 -0
- package/skills/korean-jangbu-for/instruction.md +9 -0
- package/skills/mfds-drug-safety/instruction.md +2 -0
- package/skills/naming-house/instruction.md +5 -2
- package/skills/nhis-care-checkup-search/instruction.md +2 -0
- package/skills/store-longevity-radar/scripts/__pycache__/store_longevity_download.cpython-312.pyc +0 -0
- package/skills/local-election-candidate-search/instruction.md +0 -77
- package/skills/local-election-candidate-search/skill.json +0 -8
- package/skills/yebigun-training/instruction.md +0 -177
- package/skills/yebigun-training/skill.json +0 -10
package/package.json
CHANGED
|
@@ -5,6 +5,15 @@
|
|
|
5
5
|
이 스킬은 **참고용** 절차 안내와 문서 초안 자동화 도구다. **법률 자문, 세무 자문, 법무사 업무 대행이 아니다.**
|
|
6
6
|
등기소 보정명령·각하, 세금 산정, 정관 유효성, 업종별 인허가 여부는 사건별로 달라질 수 있으므로 제출 전에는 관할 등기소, 위택스/지방자치단체 세무부서, 세무사, 법무사, 변호사 확인을 권한다.
|
|
7
7
|
|
|
8
|
+
## 법적 안전 운영 기준
|
|
9
|
+
|
|
10
|
+
이 스킬은 아래 기준을 지키는 한도에서만 문서 자동화를 수행한다.
|
|
11
|
+
|
|
12
|
+
1. **본인 사건 전용이다.** 사용자가 자신의 회사 설립을 위해 쓰는 참고 도구로 한정하고, 제3자의 설립등기 문서를 대가를 받고 대행 작성·중개·알선하는 흐름으로 확장하지 않는다. 그런 요청이 오면 법무사/변호사 상담을 안내한다.
|
|
13
|
+
2. **산출물은 항상 초안이다.** 정관·첨부서류 HWP 파일 앞부분에 "법적 효력을 보장하지 않는 참고용 초안"임을 표기하고, 제출 전 관할 등기소·세무사·법무사·변호사 확인을 사용자에게 고정 안내한다.
|
|
14
|
+
3. **문구 생성은 최소화한다.** 정관은 저장된 표준정관(법무부 양식)의 구조와 표현을 그대로 따르고, 사용자 회사에 맞게 바꿔야 하는 값(상호, 목적, 자본금 등)만 채운다. 새로운 법률 문구를 창작하지 않는다.
|
|
15
|
+
4. **세액·감면은 확정하지 않는다.** 등록면허세·중과·감면은 예상 체크리스트로만 안내하고, 최종 금액은 위택스/관할 지자체·세무사 확인 결과를 기준으로 한다.
|
|
16
|
+
|
|
8
17
|
## When to use
|
|
9
18
|
|
|
10
19
|
- “주식회사 법인 설립등기 처음 하는데 전체 절차 알려줘”
|
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
- 법인 결제는 upstream 문서 기준 **페이지당 10건** 단위 제약을 전제로 안내한다. 그 이상은 사용자가 10건 단위로 반복 결제한다.
|
|
19
19
|
- 부동산은 인터넷등기소가 로그인 상태에서 10만원 미만 일괄 결제와 일괄열람출력/일괄저장 UI를 제공하므로, v1에서는 장바구니 반복 작업만 자동화 가치가 큰 영역으로 본다.
|
|
20
20
|
|
|
21
|
+
## 법적 안전 운영 기준
|
|
22
|
+
|
|
23
|
+
1. **사용자 직접 발급 보조만 한다.** 이 스킬은 사용자 본인의 로그인·결제 안에서 발급을 돕는 참고 도구로 한정하고, 제3자의 등기부 발급을 대행하거나 영리로 중개·알선하지 않는다. 그런 요청이 오면 법무사/변호사 상담을 안내한다.
|
|
24
|
+
2. **민감정보는 즉시 최소화한다.** 발급 PDF·요약에 포함된 주민등록번호·대표자 주소 등 개인정보는 저장 즉시 마스킹하고, 필요 최소 기간만 보관한 뒤 삭제한다. 저장소·PR·로그에 남기지 않는 기존 원칙은 그대로 유지한다.
|
|
25
|
+
3. **대량 발급은 목적 근거를 확인한다.** 다건(예: 심사용 일괄 수집) 발급 요청에는 활용 목적과 법적 근거를 먼저 묻고, 근거가 불명확하면 진행하지 않는다.
|
|
26
|
+
4. **법률 해석을 하지 않는다.** 발급 결과의 권리관계 해석·법적 유효성 판단은 하지 않고, 필요하면 법무사/변호사 상담을 안내한다.
|
|
27
|
+
|
|
21
28
|
## Prerequisites
|
|
22
29
|
|
|
23
30
|
- Chrome/Chromium 실행 가능한 환경
|
|
@@ -24,6 +24,15 @@
|
|
|
24
24
|
- 정식 외부 제출용 재무제표·공시자료를 자동 산출물만으로 확정하려는 경우.
|
|
25
25
|
- 한국 사업자 장부가 아닌 해외 회계 기준(IFRS/US GAAP 등) 중심 작업.
|
|
26
26
|
|
|
27
|
+
## 법적 안전 운영 기준
|
|
28
|
+
|
|
29
|
+
이 스킬은 세무사법상 세무대리(기장대행·세무신고 대행 등)를 수행하지 않는다는 전제로만 운영한다.
|
|
30
|
+
|
|
31
|
+
1. **참고용 초안 한정이다.** 장부·재무제표·세무사 전달 CSV는 사용자가 세무사와 함께 검토하기 위한 참고 자료로 한정하고, 신고 대행·기장대행·절세 자문 기능은 제공하지 않는다.
|
|
32
|
+
2. **본인 장부 전용이다.** 사용자가 자신의 사업자 데이터를 정리하는 용도로 한정하고, 제3자의 장부를 대가를 받고 대행하는 형태로 운영하지 않는다.
|
|
33
|
+
3. **신고 전 전문가 확인을 강제한다.** 종합소득세·법인세 신고 목적이면 산출물을 최종본으로 단정하지 말고 세무사 검토(외감 대상이면 공인회계사 감사)를 워크플로에 고정한다.
|
|
34
|
+
4. **데이터는 로컬 처리가 기본이다.** CODEF 자격증명은 BYOK 로컬 저장소만 쓰고, 거래내역에 포함된 거래자·직원 개인정보는 마스킹(Level 2) 후에만 LLM fallback에 전달한다. 외부 전송 경로를 사용자 승인 없이 확장하지 않는다.
|
|
35
|
+
|
|
27
36
|
## Prerequisites
|
|
28
37
|
|
|
29
38
|
- 인터넷 연결 (업스트림 clone 용)
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
식약처 공식 OpenAPI를 **`k-skill-proxy` 경유**로 조회해 **의약품개요정보(e약은요)** 와 **안전상비의약품 정보**를 확인한다.
|
|
6
6
|
|
|
7
|
+
> 이 스킬은 식약처 공식 자료를 정리하는 **정보 제공 도구**이며 약사·의사의 진단, 처방 또는 복약지도를 대신하지 않는다. 개인별 복용 여부·용량 변경·중단 판단은 약사 또는 의사에게 확인한다.
|
|
8
|
+
|
|
7
9
|
하지만 사용자가 증상이나 복용 상황을 말하면 **바로 단정하지 말고 먼저 되묻는다.**
|
|
8
10
|
|
|
9
11
|
- 본인/아이/임산부/고령자 여부
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## What this skill does
|
|
4
4
|
|
|
5
|
-
`naming-house` npm package로 생년월일시 기반 사주 오행, 한자/한글 획수, 발음 흐름, 사용자 선호를 함께 보아 한국 이름 후보를 추천·채점한다. 사주 계산은 기존 `saju-fortune` package의 공개 `analyzeSaju` 결과를
|
|
5
|
+
`naming-house` npm package로 생년월일시 기반 사주 오행, 한자/한글 획수, 발음 흐름, 사용자 선호를 함께 보아 한국 이름 후보를 추천·채점한다. 사주 계산은 기존 `saju-fortune` package의 공개 `analyzeSaju` 결과를 사용한다. 한자 획수는 대만 정부 공개 필순 데이터 기반 `hanja`, 한글 fallback은 `korean-stroke`를 로컬에서 호출한다.
|
|
6
6
|
|
|
7
7
|
이 스킬은 작명 결과를 확정 운명이나 법적 이름 검증으로 제시하지 않는다. 후보별 점수, 근거, 한계를 투명하게 보여 주는 성명학 참고 워크플로우다.
|
|
8
8
|
|
|
9
|
+
한자 획수의 `source: "hanja-stroke-order"`는 실제 필순 횟수다. 강희자전 원획수나 81수리 4격과는 기준이 다르므로 해당 값으로 단정하지 않는다.
|
|
10
|
+
|
|
9
11
|
## When to use
|
|
10
12
|
|
|
11
13
|
- "아이 이름 추천해줘"
|
|
@@ -117,7 +119,7 @@ JS
|
|
|
117
119
|
1. 입력 정보와 정확도 한계: 음력 변환 여부, 태어난 시간, 한자 누락 여부를 확인한다.
|
|
118
120
|
2. 사주 오행 요약: 부족/보완 오행과 일간 중심을 짧게 설명한다.
|
|
119
121
|
3. 후보별 점수: 총점, 등급, `elementBalance`, `strokeHarmony`, `soundFlow`, `preferenceFit`를 보여 준다.
|
|
120
|
-
4. 성명학 풀이: 한자
|
|
122
|
+
4. 성명학 풀이: 한자 필순 횟수 또는 한글 fallback, 오행 상생/상극, 발음 흐름을 설명한다.
|
|
121
123
|
5. 추천 순서: 점수가 높은 순서대로 장점과 주의점을 함께 적는다.
|
|
122
124
|
6. 주의 문구: 성명학은 문화적 참고이며 법적 개명·인명용 한자 검증·운명 판단을 대신하지 않는다고 말한다.
|
|
123
125
|
|
|
@@ -136,6 +138,7 @@ JS
|
|
|
136
138
|
- 태어난 시간이 없으면 시주와 시주 기반 해석은 확정할 수 없다.
|
|
137
139
|
- 한자 이름이 없으면 `korean-stroke` 기반 한글 획수 fallback으로 채점하며 정확도가 낮다고 표시한다.
|
|
138
140
|
- 한자 획수나 오행을 확인하지 못하면 해당 후보의 한자 수리 해석을 확정하지 않는다.
|
|
141
|
+
- `hanja-stroke-order` 값을 강희자전 원획수나 81수리 4격 값으로 바꾸어 말하지 않는다.
|
|
139
142
|
- 후보 이름이 없으면 임의·무작위 생성 대신 선호 조건을 더 물어본다.
|
|
140
143
|
|
|
141
144
|
## Notes
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
국민건강보험공단 장기요양기관 검색서비스(data.go.kr `15059029`)와 검진기관 찾기 조회(data.go.kr `15154419`)를 `k-skill-proxy` 경유로 호출해 공개 기관 후보를 조회한다.
|
|
6
6
|
|
|
7
|
+
> 이 스킬은 국민건강보험공단의 공개 기관 정보를 정리하는 **조회 도구**이며 의료 판단, 장기요양 등급 판정 또는 특정 기관의 적합성·서비스 품질을 보증하지 않는다. 실제 이용 조건과 예약 가능 여부는 NHIS 또는 해당 기관에 직접 확인한다.
|
|
8
|
+
|
|
7
9
|
## When to use
|
|
8
10
|
|
|
9
11
|
- "서울 강남 장기요양기관 찾아줘"
|
package/skills/store-longevity-radar/scripts/__pycache__/store_longevity_download.cpython-312.pyc
CHANGED
|
Binary file
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# Local Election Candidate Search
|
|
2
|
-
|
|
3
|
-
## What this skill does
|
|
4
|
-
|
|
5
|
-
중앙선거관리위원회(NEC) 선거통계시스템의 공개 통합검색에서 후보자 이름을 조회하고, 지방선거 관련 후보자 이력만 기본으로 정리한다. 후보자명, 한자명, 생년월일/성별, 선거일, 선거명, 선거종류, 정당, 선거구, 득표, 직업, 학력, 경력 등을 반환한다.
|
|
6
|
-
|
|
7
|
-
## When to use
|
|
8
|
-
|
|
9
|
-
- 사용자가 “지방선거 후보”, “시도지사 후보”, “기초의원 후보”, “교육감 후보” 등을 이름/지역/선거일 기준으로 찾아 달라고 할 때
|
|
10
|
-
- 중앙선관위 선거통계시스템에서 공개된 후보자 이력을 확인해야 할 때
|
|
11
|
-
- 동명이인이 있을 수 있어 후보자명 + 선거종류/지역/연도 필터가 필요한 때
|
|
12
|
-
|
|
13
|
-
## Public access path
|
|
14
|
-
|
|
15
|
-
Chosen path: NEC integrated candidate search.
|
|
16
|
-
|
|
17
|
-
- Entry page: `https://info.nec.go.kr/search/searchCandidate.xhtml`
|
|
18
|
-
- Method: unauthenticated public `POST`
|
|
19
|
-
- Required form field: `searchKeyword=<정확한 후보자 성명>`
|
|
20
|
-
- Helper package: `local-election-candidate-search`
|
|
21
|
-
|
|
22
|
-
Why this path: the visible NEC UI explicitly exposes candidate-name integrated search across recent and historical elections, and it returns the candidate result cards in server-rendered HTML. It is more stable than scraping per-election menu pages because it does not require selecting every city/town/constituency combo first.
|
|
23
|
-
|
|
24
|
-
## Workflow
|
|
25
|
-
|
|
26
|
-
1. Use the package CLI from this repository or installed workspace:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npx local-election-candidate-search 오세훈 --election 시도지사 --region 서울 --limit 5
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
2. Narrow ambiguous/homonym results:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
npx local-election-candidate-search 김동연 --date 2014 --election 기초의원 --region 동작
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
3. Include non-local races only when the user asks for all NEC integrated-search matches:
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
npx local-election-candidate-search 이재명 --all --limit 20
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Inputs
|
|
45
|
-
|
|
46
|
-
- Candidate name: exact Korean name; required.
|
|
47
|
-
- `--election`: one of `시도지사`, `기초단체장`, `광역의원`, `기초의원`, `광역비례`, `기초비례`, `교육감`.
|
|
48
|
-
- `--date` / `--year`: `YYYY`, `YYYYMMDD`, or `YYYY.MM.DD`.
|
|
49
|
-
- `--region`: free text filter against parsed district/region text.
|
|
50
|
-
- `--limit`: max rows, capped at 100.
|
|
51
|
-
- `--all`: include non-local election results.
|
|
52
|
-
|
|
53
|
-
## Outputs
|
|
54
|
-
|
|
55
|
-
Return concise JSON. Each `items[]` row may include:
|
|
56
|
-
|
|
57
|
-
- `name`, `hanja`, `birth_date`, `gender`
|
|
58
|
-
- `election_date`, `election_name`, `election_code`, `election_type`
|
|
59
|
-
- `party`, `district`, `votes`, `vote_share`, `elected`
|
|
60
|
-
- `job`, `education`, `career[]`
|
|
61
|
-
- upstream code fields such as `city_code`, `sgg_city_code`, `town_code`
|
|
62
|
-
|
|
63
|
-
`summary.upstream_result_limit` shows the NEC row count requested before local client-side filters. Filtered searches request up to 100 upstream rows first, then apply exact-name matching, local/election/date/region filters, deduplication, and the final `--limit`.
|
|
64
|
-
|
|
65
|
-
## Failure modes
|
|
66
|
-
|
|
67
|
-
- `no candidate results`: NEC returned no matching card or filters removed all matches.
|
|
68
|
-
- `unexpected NEC search HTML`: upstream may be in maintenance, NetFunnel queue, login/blocked state, or markup changed.
|
|
69
|
-
- `NEC search page was capped`: filtered results are based on the maximum fetched page and may require upstream pagination for exhaustive coverage.
|
|
70
|
-
- Homonyms: the same name can appear across many elections; always show election date/type/district and apply user-provided filters.
|
|
71
|
-
- Future elections: candidate registration data may be incomplete until NEC publishes it.
|
|
72
|
-
|
|
73
|
-
## Done when
|
|
74
|
-
|
|
75
|
-
- Results are sourced from `info.nec.go.kr` public HTML.
|
|
76
|
-
- Local-election filtering is applied unless the user requested `--all`.
|
|
77
|
-
- Any warnings/failure modes are shown instead of silently claiming no results.
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "local-election-candidate-search",
|
|
3
|
-
"description": "중앙선거관리위원회 선거통계시스템 공개 통합검색으로 한국 지방선거 후보자 정보를 이름/선거종류/지역 기준으로 조회한다.",
|
|
4
|
-
"profiles": [
|
|
5
|
-
"lookup"
|
|
6
|
-
],
|
|
7
|
-
"frontmatter": "name: local-election-candidate-search\ndescription: 중앙선거관리위원회 선거통계시스템 공개 통합검색으로 한국 지방선거 후보자 정보를 이름/선거종류/지역 기준으로 조회한다.\nlicense: MIT\nmetadata:\n category: civic\n locale: ko-KR\n phase: v1"
|
|
8
|
-
}
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
# 예비군 훈련정보 조회 & 작년 대비 비교
|
|
2
|
-
|
|
3
|
-
## What this skill does
|
|
4
|
-
|
|
5
|
-
공식 예비군 홈페이지(`https://www.yebigun1.mil.kr`)에서 **이미 로그인된 브라우저 세션**을 재사용해:
|
|
6
|
-
|
|
7
|
-
- "나의 훈련정보"(`IvdTraScheDetail.do`) 페이지를 조회해 소속 정보, 이번 훈련 기간/장소/훈련종류, 그리고 같은 페이지에 이미 표시되는 과거 연도 훈련 기록까지 한 번에 가져온다 (`training-info`).
|
|
8
|
-
- 올해와 작년(기본값) 기록을 필드 단위로 비교한 결과를 함께 돌려준다 — 별도 로컬 기록 단계 없이 페이지 자체에 있는 과거 기록을 바로 비교한다.
|
|
9
|
-
- 민감한 식별 정보(군번/이름/주민등록번호 앞자리/전화번호)가 없는 **조회/목록 화면**(훈련신청 결과, 연기신청 결과, 보류·해소 신청결과, 소속부대 공지사항, 훈련안내, 나의 질의응답, 예비군부대 찾기, 휴일예비군 훈련일정 조회)을 실제로 읽어서 표 형태로 돌려준다 (`view`).
|
|
10
|
-
- 그런 식별 정보가 화면에 그대로 노출되는 화면(훈련 연기신청/보류 신청/해소 신청/훈련일정 자율선택/전국단위 훈련신청/휴일예비군 훈련신청/개인정보수정/예비군 상훈)은 **화면만 열어주고 데이터는 절대 읽지 않는다** (`open-menu`) — 신청/제출도 절대 자동화하지 않는다.
|
|
11
|
-
- 필요하면 로컬에 연도별 기록을 추가로 저장(`record`)하고 비교(`diff`)할 수도 있다 (사이트가 더 오래된 기록을 보여주지 않게 되거나, 메모를 남기고 싶을 때를 위한 보조 기능).
|
|
12
|
-
- 범용 페이지 조회(`inspect`)로 아직 분류 안 된 다른 페이지 구조를 확인할 수도 있다.
|
|
13
|
-
|
|
14
|
-
설계 원칙은 명확하다: **로그인은 항상 사용자가 직접 하고, 이 스킬은 로그인된 세션에서 조회만 한다.**
|
|
15
|
-
|
|
16
|
-
## Hard limits
|
|
17
|
-
|
|
18
|
-
- **로그인은 반드시 사용자가 직접 한다.** PASS 본인인증, 공동인증서, 간편인증, ID/PW 중 어떤 것도 자동 입력하지 않는다.
|
|
19
|
-
- **조회 전용이다.** 훈련 연기 신청, 보류·해소 신청, 훈련일정 자율선택 신청처럼 제출형(side-effect) 액션은 절대 자동화하지 않는다 — 군 의무 관련 법적 효력이 있는 제출이고, 사유 작성처럼 사용자 본인의 판단이 필요한 입력이 끼어 있어서다.
|
|
20
|
-
- **`open-menu`는 "화면 열기"까지만이다.** 버튼 클릭이든(`selfSelect`/`nationalUnit`/`holiday`) 직접 이동이든(`delay`/`hold`/`holdCancel`/`editProfile`/`honors`) 화면으로 이동만 시켜주지만, 그 화면에서 날짜를 고르거나 사유를 입력하거나 제출 버튼을 누르는 것은 절대 하지 않는다. 다음 화면이 뜨면 즉시 멈추고 "여기서부터는 직접 진행하라"고 안내한다 — 사용자의 실제 화면(같은 Chrome 창)에 이미 그 화면이 열려 있다.
|
|
21
|
-
- **`view`는 식별 정보가 없는 페이지에만 쓴다.** `VIEW_MENUS`에 등록된 메뉴만 실제로 읽어서 표(headers/rows)로 돌려준다 — 이름/군번/주민등록번호/전화번호/주소가 마크업에 그대로 노출되는 페이지는 절대 `VIEW_MENUS`에 넣지 않고 `open-menu`(`APPLICATION_MENUS`)로 돌린다.
|
|
22
|
-
- 세션 만료/로그인 필요 응답을 감지하면 즉시 중단하고 재로그인을 안내한다.
|
|
23
|
-
- 개인정보(훈련기간/장소/소속부대/군번 등)가 담긴 기록은 **이 저장소 밖** `~/.cache/k-skill/yebigun-training/history.json`에만 저장한다. 절대 이 git 저장소에 실제 개인 데이터를 커밋하지 않는다 — `test/fixtures/`의 페이지 구조 fixture는 처음부터 끝까지 완전히 가상의 이름/날짜/장소로만 작성됐다(실제 로그인 세션에서 본 마크업 *구조*만 베껴서 재현했고, 그 세션에서 본 실제 값은 어디에도 옮기지 않았다).
|
|
24
|
-
|
|
25
|
-
## Why this design (and how it was verified)
|
|
26
|
-
|
|
27
|
-
- 병무청은 예비군 훈련 일정을 공개 API로 제공하지 않는다. 개인 훈련정보는 `yebigun1.mil.kr`에 PASS/공동인증서/간편인증으로 로그인해야만 보인다 (도메인은 `mma.go.kr`이 아니라 `yebigun1.mil.kr`).
|
|
28
|
-
- v1 설계 당시엔 본인인증 없이 로그인 후 페이지를 미리 볼 방법이 없어 `parseTrainingInfo`를 비워뒀었다. **2026-06-24, 사용자가 직접 로그인한 세션으로 실제 "나의 훈련정보" 페이지(`/dmobis/rfh/rgt/edutrasubjpsn/IvdTraScheDetail.do`)를 `inspect`로 확인하고 구조를 확정했다:**
|
|
29
|
-
- 소속 정보: `<caption>소속</caption>`이 있는 표 (예비군부대/소속/계급/군번/성명/군별/연차/동원구분/비고).
|
|
30
|
-
- 올해 표시 연도: `<h4>훈련내용(YYYY년)</h4>` 헤딩.
|
|
31
|
-
- 올해 훈련 목록: `<table id="detailTb">` — 첫 행은 "총계" 합계 행(건너뜀), 나머지 행에 `data-tra-id` 등 속성과 구분/훈련일자/계획시간/실시시간/잔여시간/훈련결과/훈련장/비고 8개 열.
|
|
32
|
-
- **과거 연도 훈련 목록은 같은 페이지의 `<table id="detail2">`에 이미 들어있다** (화면에는 "이전 훈련내용 열기"를 눌러야 보이지만, `domcontentloaded` 시점에 이미 DOM에 존재함 — 추가 클릭 없이 바로 읽을 수 있다). 같은 8개 열 구조를 공유한다.
|
|
33
|
-
- 이 발견 덕분에 "작년과 비교"가 별도 로컬 저장 없이 **한 번의 `training-info` 호출**로 끝난다: 페이지 자체가 여러 해의 기록을 동시에 보여준다.
|
|
34
|
-
- 같은 페이지의 `goAction(mode)` 자바스크립트 함수가 `훈련일정 자율선택`/`전국단위 훈련신청`/`휴일예비군 훈련신청` 세 버튼을 각각 다른 신청서 URL로 연결한다는 것도 이때 확인했다 — 이게 `open-menu`가 클릭하는 정확한 버튼 레이블의 출처다. (이 페이지엔 `goAction`이 `TRASCHD`라는 네 번째 분기도 갖고 있었지만, 그 분기와 연결된 보이는 버튼을 이번 검증에서 찾지 못해 `APPLICATION_MENUS`에 포함하지 않았다 — 확인 안 된 걸 추측해서 넣지 않는다.)
|
|
35
|
-
- training-info 페이지의 전체 사이트 LNB(`<a href>` 목록)를 같은 `inspect`로 훑어서 `훈련 연기신청`(`/dmobis/rft/rgt/ivdTraDelayApplInForm.do`), `보류 신청`(`/dmobis/rfh/rrm/holdpsn/HoldPsnReqForm.do`), `해소 신청`(`/dmobis/rfh/rrm/holdpsn/HoldPsnCancelReqForm.do`)의 실제 경로도 확인했다 (2026-06-24). 이 셋은 `goAction` 버튼이 아니라 평범한 링크라서 클릭 시뮬레이션 없이 바로 `page.goto`로 이동한다 — `open-menu`가 `mode: "goto"`로 구분해서 처리한다.
|
|
36
|
-
- `훈련 연기신청` 화면은 실제로 열어보니 이름/주민등록번호 앞자리/주소/휴대폰·집·직장 전화번호가 hidden input으로 그대로 박혀 있었다 — training-info보다 훨씬 민감한 페이지다. 이 값들은 어디에도 기록하지 않았고, 오직 페이지 URL(사이트 구조)만 `APPLICATION_MENUS`에 남겼다.
|
|
37
|
-
- 2026-06-24, "모든 조회를 추가하고 민감한 건 안내까지만"이라는 요청에 따라 training-info 페이지의 전체 사이트 LNB(`<a href>` 목록, `사이트맵`과 동일 출처)를 훑어서 10개 후보 페이지를 찾고, 각각 `<thead>` 헤더와 hidden field를 스캔해서 둘로 분류했다:
|
|
38
|
-
- **`VIEW_MENUS`(실제로 읽음)**: 훈련신청 결과(`NationalUnitResevForcesTraRltList.do`), 연기신청 결과(`ivdTraDelayApplRltList.do`), 보류·해소 신청결과(`HoldPsnReqRsltList.do`), 휴일예비군 훈련일정 조회(`HolidayTrainingScheduleList.do`), 소속부대 공지사항(`MyPubAnnounList.do`), 훈련안내(`TraNoticeList.do`), 나의 질의응답(`MyQuestAnsList.do`), 예비군부대 찾기(`listAdminAddr.do`). 헤더에 군번/이름 같은 식별 컬럼이 없는 것만 골랐다.
|
|
39
|
-
- **`APPLICATION_MENUS`로 추가(절대 안 읽음)**: 개인정보수정(`ReserveForceForm.do` — 편집 폼이고 hidden field에 `cellPhone` 등 직접 노출 확인), 예비군 상훈(`ReserveForcePrzdcr.do` — 페이지 상단에 training-info의 "소속" 표와 똑같이 군번/성명이 그대로 들어있는 걸 확인, 그 아래 실제 상훈 목록은 따로 검증하지 않았다).
|
|
40
|
-
- **포함하지 않음**: 카드뉴스/국방영상/공지사항(전체)/자주 묻는 질문/사이버 설문조사/혁신 아이디어 공모전/예비군 제도안내/관련 법령/개인정보처리방침처럼 로그인·개인 계정과 무관한 일반 공개 게시판·안내 페이지는 이 스킬의 목적(내 예비군 신상 조회)과 거리가 멀어 추가하지 않았다. 필요하면 같은 패턴으로 더 추가할 수 있다.
|
|
41
|
-
- `나의 질의응답`(`myQna`)은 실제로 열어보니 "내가 쓴 글"이 아니라 **다른 사용자들의 질문도 함께 보이는 공개 게시판**이었다 (작성자 이름은 사이트 자체가 이미 "민\*연"처럼 마스킹해서 보여준다). 마스킹된 이름만 노출되므로 `VIEW_MENUS`에 남겼지만, 다른 사용자의 (마스킹된) 글 내용을 답변에 과도하게 인용하지 않는다.
|
|
42
|
-
- 여러 `VIEW_MENUS` 페이지(예: 훈련신청 결과)는 초기 HTML에 `Loading...` placeholder 행만 있고 실제 데이터는 AJAX로 나중에 채워진다는 것도 이때 확인했다 — `fetchInquiry`가 이 placeholder를 실제 데이터로 착각해 반환하지 않도록, 사라질 때까지 짧게 폴링한 뒤(최대 약 4.5초) 그래도 안 사라지면 명확한 에러로 멈춘다.
|
|
43
|
-
- 병무청 디지털서비스개방의 "청년 동원훈련 일정조회"(`openservice.go.kr/youthMilTrainSch`)는 이번 검증에서 비교 대상으로 시도하지 않았다. 이미 `yebigun1.mil.kr` 쪽 구조가 안정적으로 파싱돼서 추가 검증이 급하지 않다고 판단했다 — 필요해지면 같은 방식(`inspect`)으로 비교해본다.
|
|
44
|
-
- 사이트가 마크업을 바꾸면 `trainings`가 빈 배열로 나올 수 있다. 이 경우 "훈련이 없다"로 해석하지 말고 구조가 바뀌었다고 보고 재검증한다 (`Failure modes` 참고). `view`도 마찬가지: `headers`가 비어 있으면 "내용이 없다"가 아니라 그 페이지에 `<thead>`가 없다는 뜻이므로 구조 변경을 의심한다.
|
|
45
|
-
|
|
46
|
-
## Prerequisites
|
|
47
|
-
|
|
48
|
-
- macOS 또는 Chrome 실행 가능한 환경
|
|
49
|
-
- `packages/yebigun-training`에서 `npm install` (`playwright-core` 포함)
|
|
50
|
-
- Chrome 원격 디버깅 포트 사용 가능
|
|
51
|
-
- 사용자가 직접 예비군 홈페이지 로그인 가능 (PASS/공동인증서/간편인증 중 본인이 쓰는 방식)
|
|
52
|
-
|
|
53
|
-
## Workflow
|
|
54
|
-
|
|
55
|
-
### 1. 전용 Chrome 프로필로 로그인 브라우저를 띄운다
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
node packages/yebigun-training/src/cli.js chrome-command --profile-dir "$HOME/.cache/k-skill/yebigun-chrome" --debugging-port 9222
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
위 명령이 출력한 Chrome 실행문으로 브라우저를 띄운 뒤, 사용자가 직접 `https://www.yebigun1.mil.kr/`에서 로그인한다.
|
|
62
|
-
|
|
63
|
-
### 2. 이번 훈련정보 + 작년 비교를 한 번에 조회한다
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
node packages/yebigun-training/src/cli.js training-info --cdp-url http://127.0.0.1:9222
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
결과 JSON의 구조:
|
|
70
|
-
|
|
71
|
-
- `member`: 예비군부대/소속/계급/군번/성명/군별/연차/동원구분.
|
|
72
|
-
- `currentDisplayYear`: 사이트가 "올해"로 표시하는 연도.
|
|
73
|
-
- `trainings`: 올해 + 과거 연도 훈련 기록 배열(최신순), 각 항목에 `trainingType`/`startDate`/`endDate`/`plannedHours`/`actualHours`/`remainingHours`/`result`/`location`.
|
|
74
|
-
- `comparison`: `currentDisplayYear` vs `currentDisplayYear - 1`을 자동 비교한 `{ hasCurrentRecord, hasPreviousRecord, current, previous, changes }`.
|
|
75
|
-
|
|
76
|
-
세션이 만료됐으면 "session is not authenticated or has expired" 에러가 즉시 뜬다 — 이때는 사용자에게 재로그인을 안내하고 중단한다.
|
|
77
|
-
|
|
78
|
-
사용자에게 결과를 전달할 때: 올해 훈련의 기간/장소/구분과 `comparison.changes`(필드별 변경점)를 한국어로 자연스럽게 요약한다. `comparison.hasPreviousRecord`가 `false`면 "비교할 작년 기록이 없다"고 그대로 말한다 — 추측하지 않는다.
|
|
79
|
-
|
|
80
|
-
### 3. 그 외 조회 화면을 보고 싶다고 하면: `view`로 실제 데이터를 읽어온다
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
node packages/yebigun-training/src/cli.js view --menu applicationResults --cdp-url http://127.0.0.1:9222
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
`--menu`로 지정 가능한 값 (모두 `VIEW_MENUS`, 식별 정보 없는 조회 전용):
|
|
87
|
-
|
|
88
|
-
| menu 값 | 화면 |
|
|
89
|
-
|---|---|
|
|
90
|
-
| `applicationResults` | 훈련신청 결과 |
|
|
91
|
-
| `delayResults` | 연기신청 결과 |
|
|
92
|
-
| `holdResults` | 보류·해소 신청결과 |
|
|
93
|
-
| `holidaySchedule` | 휴일예비군 훈련일정 조회 |
|
|
94
|
-
| `unitNotices` | 소속부대 공지사항 |
|
|
95
|
-
| `trainingNotices` | 훈련안내 |
|
|
96
|
-
| `myQna` | 나의 질의응답 (다른 사용자의 마스킹된 글도 함께 보이는 공개 게시판이다 — 응답에서 남의 글을 과도하게 인용하지 않는다) |
|
|
97
|
-
| `unitFinder` | 예비군부대 찾기 |
|
|
98
|
-
|
|
99
|
-
결과는 항상 `{ menu, label, headers, rows }` 형태의 일반 표다 (페이지마다 다른 필드명을 따로 만들지 않았다). `headers`는 채워져 있는데 `rows`가 빈 배열이면 "등록된 게 없다"는 뜻이고, `headers`까지 비어 있으면 페이지 구조가 바뀐 것으로 보고 `inspect`로 재확인한다. `예비군부대 찾기`/`휴일예비군 훈련일정 조회`처럼 검색 조건 없이 처음 들어가면 비어 있는 게 정상인 페이지도 있다.
|
|
100
|
-
|
|
101
|
-
### 4. 사용자가 연기/보류/일정선택/개인정보수정 등을 하고 싶다고 하면: 화면까지만 열어준다
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
node packages/yebigun-training/src/cli.js open-menu --menu delay --cdp-url http://127.0.0.1:9222
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
`--menu`로 지정 가능한 값과 동작 방식 (모두 `APPLICATION_MENUS`, 데이터는 절대 읽지 않는다):
|
|
108
|
-
|
|
109
|
-
| menu 값 | 화면 | 방식 |
|
|
110
|
-
|---|---|---|
|
|
111
|
-
| `selfSelect` | 훈련일정 자율선택 | training-info 페이지의 실제 버튼을 클릭 |
|
|
112
|
-
| `nationalUnit` | 전국단위 훈련신청 | training-info 페이지의 실제 버튼을 클릭 |
|
|
113
|
-
| `holiday` | 휴일예비군 훈련신청 | training-info 페이지의 실제 버튼을 클릭 |
|
|
114
|
-
| `delay` | 훈련 연기신청 | `/dmobis/rft/rgt/ivdTraDelayApplInForm.do`로 직접 이동 |
|
|
115
|
-
| `hold` | 보류 신청 | `/dmobis/rfh/rrm/holdpsn/HoldPsnReqForm.do`로 직접 이동 |
|
|
116
|
-
| `holdCancel` | 해소 신청 | `/dmobis/rfh/rrm/holdpsn/HoldPsnCancelReqForm.do`로 직접 이동 |
|
|
117
|
-
| `editProfile` | 개인정보수정 | `/dmobis/rfh/rrm/reserveforce/ReserveForceForm.do`로 직접 이동 |
|
|
118
|
-
| `honors` | 예비군 상훈 | `/dmobis/rfh/rrm/reserveforce/ReserveForcePrzdcr.do`로 직접 이동 |
|
|
119
|
-
|
|
120
|
-
어느 방식이든 **다음 화면이 뜨면 그 자리에서 멈춘다** — 날짜 선택, 사유 입력, 제출은 절대 하지 않는다. `훈련 연기신청`/`개인정보수정` 화면은 실제로 들어가보면 이름/주민등록번호 앞자리/주소/휴대폰·집·직장 전화번호가 그대로 보일 만큼 training-info보다 훨씬 민감하다 — 이 정보를 답변이나 파일에 옮기지 않는다.
|
|
121
|
-
|
|
122
|
-
호출 후 사용자에게는 정확히 이렇게 안내한다: "지금 보고 계신 Chrome 창에 `<label>` 화면이 열렸습니다. 여기서부터 입력과 제출은 직접 해주세요." 표에 없는 메뉴는 `open-menu`/`view` 둘 다 지원하지 않으므로(`Unknown menu` 에러), 사이트맵(`/dmobis/rfh/rgt/sitemap/sitemap.jsp`)이나 `inspect`로 먼저 실제 경로/구조를 확인한 뒤 어느 쪽(조회 vs 안내만)에 넣을지 식별 정보 노출 여부로 판단해서 사용자와 상의한다.
|
|
123
|
-
|
|
124
|
-
### 5. (다른 페이지 구조를 더 봐야 할 때) 범용 조회
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
node packages/yebigun-training/src/cli.js inspect --cdp-url http://127.0.0.1:9222 --path <경로> --full
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
`pageInfo.pageType`이 `login`이면 재로그인을 안내한다.
|
|
131
|
-
|
|
132
|
-
### 6. (선택) 로컬에도 따로 기록해두고 비교하기
|
|
133
|
-
|
|
134
|
-
사이트의 `이전 훈련내용` 표가 보여주는 연도 범위를 벗어나는 기록을 직접 남겨두고 싶을 때만 쓴다. `training-info`의 `trainings` 배열에서 원하는 연도의 항목을 골라 그대로 저장하면 된다.
|
|
135
|
-
|
|
136
|
-
```bash
|
|
137
|
-
node packages/yebigun-training/src/cli.js record --year 2026 --json '{"trainingType":"동원훈련Ⅱ형 1차","startDate":"2026-08-10","endDate":"2026-08-12","location":"OO과학화예비군훈련장"}'
|
|
138
|
-
node packages/yebigun-training/src/cli.js diff --year 2026
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
기본 비교 대상은 `year - 1`이다 (`--compare-year`로 다른 연도 지정 가능). 결과의 `hasPreviousRecord`가 `false`면 "작년 기록이 없어 비교할 수 없다"고 명확히 말한다.
|
|
142
|
-
|
|
143
|
-
## Response policy
|
|
144
|
-
|
|
145
|
-
- "로그인 필수", "세션 만료 시 재로그인 필요"를 항상 명확히 적는다.
|
|
146
|
-
- 훈련 연기/보류/자율선택 같은 제출형 신청은 절대 자동화하지 않고, 사용자가 브라우저에서 직접 하도록 안내한다. `open-menu`로 화면까지 열어준 경우에도 "여기서부터는 직접 진행하세요"를 빼지 않는다.
|
|
147
|
-
- 군번/전화번호 등 식별 정보를 답변에 그대로 길게 인용하지 말고, 훈련 기간/장소/달라진 점 위주로 요약한다.
|
|
148
|
-
- `view`(`myQna` 등) 결과에 다른 사용자의 마스킹된 이름/글이 섞여 있으면, 사용자가 직접 물어본 본인 관련 내용에 집중하고 남의 글을 장문으로 인용하지 않는다.
|
|
149
|
-
- 비교 결과를 보여줄 때 추측을 섞지 않는다 — 기록되지 않은 연도는 "기록 없음"으로만 말한다.
|
|
150
|
-
- `view`가 빈 `rows`를 반환하면 "등록된 게 없다"고 그대로 말한다 (검색 조건이 필요한 페이지일 수도 있음을 함께 안내). `headers`까지 비어 있으면 페이지 구조가 바뀐 것으로 보고 `inspect`로 재확인을 제안한다.
|
|
151
|
-
- 페이지 구조가 바뀐 것으로 보이면(`trainings`가 비정상적으로 비어 있는 등) 그 가능성을 사용자에게 알리고 `inspect`로 재확인을 제안한다.
|
|
152
|
-
|
|
153
|
-
## Verification
|
|
154
|
-
|
|
155
|
-
- 자동 검증: `npm run lint && npm test` — `detectSessionState`/`inspectYebigunPage` 분류, **완전히 가상의 데이터로 작성한 fixture**(`test/fixtures/training-info-page.html`, `test/fixtures/view-list-page.html`) 기반 `parseTrainingInfo`/`parseGenericTable` 단위 테스트, mocked-CDP `inspect`/`fetchTrainingInfo`/`fetchInquiry`(AJAX `Loading...` 폴링 포함)/`openApplicationMenu`, 그리고 `record`/`history`/`diff`의 로컬 JSON 로직 단위 테스트.
|
|
156
|
-
- smoke 검증(로그인 불필요): `chrome-command`로 출력된 명령이 실제 Chrome을 올바른 프로필/포트로 띄우는지 확인.
|
|
157
|
-
- 실서비스 검증: 사용자가 직접 로그인한 BrowserOS/Chrome CDP 세션에서 `training-info`가 소속 정보, 올해 훈련, 과거 연도 기록, 작년 대비 비교를 구조화 JSON으로 반환하는지 확인한다. 공개 문서에는 실제 날짜, 부대, 성명, 군번, 훈련장, 민원/신청 내용 같은 인증 세션 값을 기록하지 않는다.
|
|
158
|
-
|
|
159
|
-
## Failure modes
|
|
160
|
-
|
|
161
|
-
- 세션 만료/미로그인: `inspect`/`training-info`/`view`/`open-menu`가 `pageType: "login"` 또는 "session is not authenticated or has expired" 에러를 반환한다 — 즉시 중단하고 재로그인 안내.
|
|
162
|
-
- `trainings`가 비정상적으로 비어 있거나 `member`가 `null`: 사이트가 마크업을 바꿨을 가능성이 높다. "훈련이 없다"고 단정하지 말고 `inspect --full`로 실제 HTML을 다시 확인하라고 안내한다.
|
|
163
|
-
- `diff`(로컬 기록)에서 비교 연도 기록이 없음: 추측하지 말고 "작년 기록이 없다"고 그대로 전달한다.
|
|
164
|
-
- CDP 연결 실패: 사용자가 `chrome-command`로 띄운 디버깅 포트 Chrome이 켜져 있는지, `--cdp-url`이 맞는지 확인하라고 안내한다.
|
|
165
|
-
- `open-menu`/`view`에 알 수 없는 `--menu` 값: 브라우저를 건드리기 전에 `Unknown menu` 에러로 막는다 (각자 `APPLICATION_MENUS`/`VIEW_MENUS`의 키만 허용).
|
|
166
|
-
- `open-menu`가 버튼을 못 찾음: "Could not find the ... button" 에러를 반환한다 — 사이트가 버튼 레이블/구조를 바꿨을 가능성이 높으므로 `inspect`로 재확인하라고 안내한다.
|
|
167
|
-
- `view`가 AJAX 로딩을 끝내 못 기다림: "did not finish loading in time" 에러를 반환한다 — 한 번 더 `view`를 시도하거나, 계속 반복되면 `inspect`로 실제 응답을 확인하라고 안내한다.
|
|
168
|
-
|
|
169
|
-
## Done when
|
|
170
|
-
|
|
171
|
-
- 로그인된 세션으로 `training-info`가 소속 정보, 올해 훈련 기간/장소/종류, 과거 연도 기록, 작년 대비 비교를 한 번에 정확히 반환한다.
|
|
172
|
-
- 세션 만료 시 명확한 재로그인 에러로 즉시 중단된다.
|
|
173
|
-
- `open-menu`가 화면까지만 이동시키고(클릭이든 직접 이동이든), 그 화면의 입력/제출은 절대 건드리지 않는다 — 신청형 화면뿐 아니라 식별 정보가 노출되는 조회형 화면(개인정보수정/예비군 상훈)도 같은 원칙으로 막는다.
|
|
174
|
-
- `view`가 `VIEW_MENUS`에 등록된, 식별 정보 없는 화면만 실제로 읽어서 일반화된 headers/rows로 돌려준다. AJAX로 늦게 채워지는 목록을 placeholder 상태로 잘못 반환하지 않는다.
|
|
175
|
-
- (로컬 기록을 쓴 경우) `record`로 저장한 연도별 기록이 `~/.cache/k-skill/yebigun-training/history.json`에만 남고 저장소에는 커밋되지 않는다.
|
|
176
|
-
- 어떤 단계에서도 로그인 입력이나 신청/제출 액션이 자동화되지 않았다.
|
|
177
|
-
- 저장소에 커밋된 fixture/테스트 데이터에 실제 개인정보가 한 글자도 섞여 있지 않다.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "yebigun-training",
|
|
3
|
-
"description": "사용자가 직접 로그인한 예비군 홈페이지(yebigun1.mil.kr) Chrome 세션을 재사용해 이번 훈련 일정/장소를 조회하고, 작년 이전 기록과 비교해 무엇이 달라졌는지 알려준다. 조회 전용이며 로그인이나 연기·보류 신청은 절대 자동화하지 않는다. 돌쇠에서는 가능한 가장 가까운 합법적 공식 단계까지 준비한다.",
|
|
4
|
-
"profiles": [
|
|
5
|
-
"vault",
|
|
6
|
-
"browser",
|
|
7
|
-
"legal"
|
|
8
|
-
],
|
|
9
|
-
"frontmatter": "name: yebigun-training\ndescription: 사용자가 직접 로그인한 예비군 홈페이지(yebigun1.mil.kr) Chrome 세션을 재사용해 이번 훈련 일정/장소를 조회하고, 작년 이전 기록과 비교해 무엇이 달라졌는지 알려준다. 조회 전용이며 로그인이나 연기·보류 신청은 절대 자동화하지 않는다. 돌쇠에서는 가능한 가장 가까운 합법적 공식 단계까지 준비한다.\nlicense: MIT\nmetadata:\n category: legal\n locale: ko-KR\n phase: v1"
|
|
10
|
-
}
|