@qualisoft/ai-skills 1.4.0 → 1.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,93 @@
1
1
  # 변경 이력
2
2
 
3
+ ## 1.5.0 — 2026-09-14
4
+
5
+ 대본이 **원칙만 말하고 폼을 말하지 않아** 흉내만 낸 파일이 나오던 것을 고쳤습니다.
6
+
7
+ ### 실제 산출물에서 폼을 뽑았습니다
8
+
9
+ 원칙을 아무리 적어도 "정돈된 표"가 무엇인지는 전해지지 않습니다. 실제로 고객에게
10
+ 나간 엑셀을 열어 구조를 그대로 규칙으로 옮겼습니다 (`_form.md`).
11
+
12
+ | 규칙 | 내용 |
13
+ | --- | --- |
14
+ | 시트 번호 | `1. 구축범위`, `2. 마일스톤` — `Sheet1`·`표1` 금지 |
15
+ | 여백 | 표는 B열·3행부터. A열은 폭 2~3의 여백 |
16
+ | **전제 문구** | 제목 줄 오른쪽에 "협의에 따라 변경될 수 있습니다" — **고객 송부물 필수** |
17
+ | **계층은 병합** | 같은 상위 값을 반복하지 않음. **가장 작은 단위가 한 행** |
18
+ | 통번호 | 분류마다 1부터 다시 시작하지 않음 |
19
+ | **주 단위 간트** | 월 4칸 병합 + `1w 2w 3w 4w` 두 줄 머리행, 막대는 **셀 배경색**(`#B7B7B7`) |
20
+ | 오픈 차수 | 간트 머리행 위에 `1차` `2차` 를 해당 주 위치에 |
21
+
22
+ **계층을 병합으로 묶고 최소 단위를 행으로 쪼개는 것**이 핵심입니다. 그래야 고객이
23
+ "이 화면에 기능이 몇 개인지"를 눈으로 셉니다. 마크다운 표를 그대로 옮기면 이 모양이
24
+ 되지 않습니다.
25
+
26
+ ### 문서별 구성에 시트 이름과 열을 적었습니다
27
+
28
+ `DOC_SHAPE` 가 원칙 한 줄이던 것을 **시트 구성과 열 목록**으로 바꿨습니다.
29
+
30
+ ```
31
+ roadmap → 1. 구축범위 항목 | 번호 | 주요화면 | 구성 | 설명 | 기능 | 비고
32
+ 2. 마일스톤 Level | Task | Memo | 1 month(1w 2w 3w 4w) | …
33
+ ```
34
+
35
+ `roadmap` 을 포함해 **고유 폼 23종**을 정의했습니다. `권한 매트릭스`는 행이
36
+ 기능·열이 역할이고, `리스크 대장`은 고객 결정 요청을 별도 시트로 빼고,
37
+ `검증 계획`은 `담당` 열로 고객 확인(UAT)과 수행사 확인을 구분합니다.
38
+
39
+ ### 문서 이름이 바뀌어도 폼을 잃지 않습니다
40
+
41
+ `DOC_SHAPE_ALIAS` 를 넣어 구버전 이름을 잇습니다 (`prd` → `requirements`,
42
+ `as-is-analysis` → `as-is-to-be` 등 13개). 1.1.0 이전에 만든 프로젝트도
43
+ 고유 폼을 받습니다.
44
+
45
+ ### 검증
46
+
47
+ 실제 프로젝트 문서 35개로 확인했습니다.
48
+
49
+ ```
50
+ 폼 적용 34 / 35 (README 만 기본값 — 문서 체계 안내라 제외)
51
+ 28-roadmap → 구축범위와 일정 (2시트)
52
+ 08-prd → 요구사항 추적표 (별칭)
53
+ 14-role-permission → 권한 매트릭스
54
+ 30-risk-open-questions → 리스크 대장
55
+ 대본 6,891자 · 치환 잔여 0건
56
+ ```
57
+
58
+
59
+ ## 1.4.1 — 2026-09-14
60
+
61
+ `비활성 관리` 를 켰을 때 체크박스가 상태 점에 붙어 보이던 것을 고쳤습니다.
62
+
63
+ 체크박스를 절대 배치해서 상태 점과 **1px 겹쳐 있었습니다.**
64
+
65
+ ```
66
+ 체크박스 left 7px + 폭 12 → 7 ~ 19
67
+ 상태 점 left 18px + 폭 5 → 18 ~ 23 ← 겹침
68
+ ```
69
+
70
+ 행은 이미 `flex` + `gap:8px` 이므로, 체크박스를 절대 배치하지 않고 **흐름 안에
71
+ 두면 간격이 저절로 맞습니다.** 절대 배치와 `padding-left` 로 억지로 맞추던 것을
72
+ 걷어냈습니다.
73
+
74
+ 체크박스가 없는 행(홈 · 가이드 · 추적성 매트릭스)도 같은 만큼 밀어 주어, 관리
75
+ 모드에서 왼쪽 끝이 들쭉날쭉하지 않습니다. 어느 행에 체크박스가 있는지는 JS 가
76
+ `has-check` 클래스로 표시합니다 — `:has()` 선택자에 기대지 않아 브라우저를 가리지
77
+ 않습니다.
78
+
79
+ 곁들여: 체크박스 12px → 13px, 관리 모드에서 행 hover 표시, 상단 관리 줄 여백 정리.
80
+
81
+ ### 검증
82
+
83
+ ```
84
+ 체크박스 16~29 · 상태 점 37~42 · 이름표 50~
85
+ 간격 8px 균일 · 겹침 없음
86
+ 체크박스 없는 행과 점·이름표 좌표 일치 (37 / 50)
87
+ 모드 해제 시 원래 정렬로 복귀 (16 / 16)
88
+ ```
89
+
90
+
3
91
  ## 1.4.0 — 2026-09-14
4
92
 
5
93
  대본을 **고객 송부용**으로 다시 세웠습니다. 세 가지 문제를 고쳤습니다.
package/README.md CHANGED
@@ -173,7 +173,8 @@ large 19~21개 다인원, 6개월 이상
173
173
 
174
174
  | 겹 | 정하는 것 |
175
175
  | --- | --- |
176
- | 구성 | **이 문서는 무엇을 앞에 세우는가** — 일정표는 마일스톤을, 요구사항은 추적 관계를 |
176
+ | 구성 | **시트 이름과 열까지** — 일정표는 `1. 마일스톤` + `2. 과업목록`, 요구사항은 `1. 요구사항` + `2. 미수용` |
177
+ | 폼 | 실제 고객 송부물에서 뽑은 표 문법 — 계층은 병합, 최소 단위가 한 행, 주 단위 간트 |
177
178
  | 목적 | 답하는 질문 한 줄 · 핵심 수치 · 받는 분이 할 일을 맨 앞에 |
178
179
  | 정제 | 담당자 이메일 · 미결정 · 변경 이력 · 내부 표기를 걸러냄 |
179
180
  | 서식 | 색 여섯 개 · 글자 위계 네 단계 · 표 스타일. 값이 고정 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qualisoft/ai-skills",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "기획 인터뷰부터 문서 체계·디자인 시안·구현까지 이어지는 AI 에이전트 스킬 5종. Claude Code · Codex · Gemini 등 어느 도구에서나 쓴다. 외부 의존성 없음.",
5
5
  "keywords": [
6
6
  "ai-skills",
@@ -431,14 +431,42 @@ Excel · PowerPoint · Word · CSV)을 고를 수 있고, **AI 에게 줄 지시
431
431
  핵심 수치 · 받는 분이 할 일)을 맨 앞에 강제하고, 문서 종류별 구성 지침으로 무엇을
432
432
  본표로 세울지 정한다.
433
433
 
434
- `DOC_SHAPE` 는 문서 `name` 으로 찾는다. 예를 들어 `wbs-schedule` 마일스톤을
435
- 본표와 분리해 세우게 하고, `requirements` 는 요구사항 ↔ 과업 ↔ 산출물이 같은 행에
436
- 보이게 한다. 없는 문서는 `DOC_SHAPE_DEFAULT` 를 쓴다 — 결론을 앞으로 올리고,
437
- 행 수가 가장 많은 표를 본표로 삼는다.
434
+ `DOC_SHAPE` 는 **시트 이름과 열까지 적는다.** 원칙만 주면 흉내만 파일이 나온다.
438
435
 
439
- **새 문서 종류를 넣으면 `DOC_SHAPE` 에 한 줄 추가한다.** 안 넣어도 동작하지만,
436
+ ```
437
+ roadmap → 1. 구축범위 항목 | 번호 | 주요화면 | 구성 | 설명 | 기능 | 비고
438
+ 2. 마일스톤 Level | Task | Memo | 1 month(1w 2w 3w 4w) | …
439
+ ```
440
+
441
+ 문서 `name` 으로 찾고, 없으면 `DOC_SHAPE_ALIAS` 를 따라간다. 문서 이름이 바뀌어도
442
+ (`prd` → `requirements`) 이미 만들어진 프로젝트가 폼을 잃지 않는다.
443
+ 둘 다 없으면 `DOC_SHAPE_DEFAULT` — 결론을 앞으로, 행 수가 가장 많은 표를 본표로.
444
+
445
+ **새 문서 종류를 넣으면 `DOC_SHAPE` 에 항목을 추가한다.** 안 넣어도 동작하지만
440
446
  그 문서는 "원본 순서대로"가 된다.
441
447
 
448
+ ### `_form.md` — 폼 문법은 실제 산출물에서 뽑았다
449
+
450
+ 원칙을 아무리 적어도 "정돈된 표"가 무엇인지는 전해지지 않는다. 실제로 고객에게
451
+ 나간 산출물(`구축범위&마일스톤.xlsx`)을 열어 구조를 뽑아 규칙으로 옮겼다.
452
+
453
+ | 규칙 | 내용 |
454
+ | --- | --- |
455
+ | 시트 번호 | `1. 구축범위`, `2. 마일스톤` — `Sheet1`·`표1` 금지 |
456
+ | 여백 | 표는 B열·3행부터. A열은 폭 2~3의 여백 |
457
+ | **전제 문구** | 제목 줄 오른쪽에 "협의에 따라 변경될 수 있습니다" — **고객 송부물 필수** |
458
+ | **계층은 병합** | 같은 상위 값을 반복하지 않는다. **가장 작은 단위가 한 행** |
459
+ | 통번호 | 분류마다 1부터 다시 시작하지 않는다 |
460
+ | **주 단위 간트** | 월 4칸 병합 + `1w 2w 3w 4w` 두 줄 머리행, 막대는 **셀 배경색**(`#B7B7B7`), 칸에 글자 없음 |
461
+ | 오픈 차수 | 간트 머리행 위에 `1차` `2차` 를 해당 주 위치에 |
462
+
463
+ **계층을 병합으로 묶고 최소 단위를 행으로 쪼개는 것**이 핵심이다. 그래야 고객이
464
+ "이 화면에 기능이 몇 개인지"를 눈으로 센다. 마크다운 표를 그대로 옮기면 이 모양이
465
+ 되지 않는다.
466
+
467
+ `_form.md` 는 `pdf` · `excel` · `pptx` · `docx` 대본에 들어간다. CSV 는 병합·간트가
468
+ 성립하지 않아 넣지 않는다.
469
+
442
470
  ### 엑셀 표 손상 — 대본이 막는다
443
471
 
444
472
  `openpyxl` 로 엑셀 표 기능을 쓰면 머리행 이름이 **비거나 중복될 때** 엑셀이
@@ -1322,50 +1322,283 @@ const SCRIPT_FORMATS = [
1322
1322
  * 요구사항 대장은 무엇이 어디로 이어지는지가 보여야 한다. 이 지침이 없으면
1323
1323
  * "원본 순서대로 옮긴 파일"이 나오고, 받는 사람은 무엇을 보라는 것인지 모른다.
1324
1324
  *
1325
+ * **시트 이름과 열까지 적는다.** 원칙만 주면 흉내만 낸 파일이 나온다.
1325
1326
  * 키는 문서 name(모듈에 적힌 것)이다. 없으면 기본 지침을 쓴다. */
1326
1327
  const DOC_SHAPE = {
1327
- "deliverable-register":
1328
- "이 문서는 **납품 목록**입니다. 공정별로 묶고, 각 산출물의 `필수 여부`·" +
1329
- "`고객 공유`·`상태`가 한눈에 보이게 하세요. 맨 앞 수치는 `전체 종수`, " +
1330
- "`필수`, `작성 완료`, `미착수` 입니다. **제외한 산출물** 목록을 빠뜨리지 " +
1331
- "마세요 고객이 '이건 없냐'고 묻는 지점입니다. 버전 이력은 뒤에 둡니다.",
1332
- "wbs-schedule":
1333
- " 문서는 **일정표**입니다. 공정 과업 2단으로 묶고, 과업의 시작·종료·" +
1334
- "담당·진척이 행에 보이게 하세요. 수치는 `전체 과업`, `종합 진척률`, " +
1335
- "`진행 중`, `지연` 입니다. **마일스톤은 본표와 분리해 따로 세우세요** — " +
1336
- "고객이 가장 먼저 보는 것이 보고·승인 시점입니다. 지연 과업은 굵게 표시합니다.",
1337
- requirements:
1338
- "이 문서는 **요구사항 추적표**입니다. 각 요구사항이 `어느 과업`으로 가고 " +
1339
- "`어느 산출물`로 확인되는지가 같은 행에 보여야 합니다. 수치는 " +
1340
- "`전체 요구사항`, `수용`, `추가 검토`, `미수용` 입니다. 부서별로 묶으면 " +
1341
- "현업이 자기 것을 찾기 쉽습니다. **수용하지 않은 요구사항과 그 이유**를 " +
1342
- "반드시 포함하세요.",
1343
- "project-brief":
1344
- " 문서는 **사업 개요**입니다. 목표와 범위가 먼저이고, 특히 `범위 제외`를 " +
1345
- "분명히 세우세요. 맨 앞 수치는 `목표 수`, `기간`, `이해관계자 수` 입니다. " +
1346
- "이해관계자는 이름과 역할만 남기고 연락처는 뺍니다.",
1347
- "screen-spec":
1348
- " 문서는 **화면 설계서**입니다. 화면 목록을 먼저 보여주고, 뒤에 화면별 " +
1349
- "상세를 둡니다. 수치는 `전체 화면 수`와 구분별 개수입니다. " +
1350
- "화면마다 **무엇을 하는 화면인지 한 줄**을 앞에 붙이세요.",
1351
- "data-model":
1352
- " 문서는 **데이터 구조**입니다. 테이블 목록을 먼저, 테이블별 칼럼. " +
1353
- " 수치는 `테이블 수`, `관계 수` 입니다. 고객이 읽을 것이므로 " +
1354
- "**한글 논리명을 물리명보다 앞에** 두세요.",
1355
- "qa-test-plan":
1356
- "이 문서는 **검증 계획**입니다. 무엇을 어떤 기준으로 통과시키는지가 핵심입니다. " +
1357
- "맨 앞 수치는 `시나리오 수`, `필수 통과 항목`, `일정` 입니다. " +
1358
- "고객이 직접 하는 확인(UAT)과 우리가 하는 확인을 **구분해서** 세우세요.",
1359
- "release-ops":
1360
- "이 문서는 **오픈·운영 안내**입니다. 오픈 절차와 이후 지원 체계가 핵심입니다. " +
1361
- " 수치는 `오픈 예정일`, `교육 대상 인원`, `지원 기간` 입니다. " +
1362
- "고객이 해야 준비를 따로 묶어 세우세요.",
1328
+ "wbs-schedule": [
1329
+ "이 문서는 **일정표**입니다. 시트를 개로 나눕니다.",
1330
+ "",
1331
+ "**`1. 마일스톤`** 고객이 가장 먼저 보는 시트입니다.",
1332
+ "상단 요약(`총 기간`, `전략` 차수별)을 두고, 아래 **주 단위 간트**를 그립니다.",
1333
+ "열은 `Level | Task | Memo | 1 month(1w 2w 3w 4w) | 2 month | …` 이고,",
1334
+ "`Level` 공정명으로 병합합니다. 막대는 배경색으로 칠하고 글자를 넣지 않습니다.",
1335
+ "간트 머리행 위에 `1차` `2차` 오픈 차수를 해당 위치에 세웁니다.",
1336
+ "",
1337
+ "**`2. 과업목록`** 열은 `공정 | 번호 | 과업 | 담당 | 시작 | 종료 | 진척 | 산출물 | 비고`.",
1338
+ "`공정` 은 병합하고 번호는 전체 통번호로 붙입니다. **지연 과업은 굵게** 표시합니다.",
1339
+ "",
1340
+ "핵심 수치는 `전체 과업`, `종합 진척률`, `진행 중`, `지연` 입니다.",
1341
+ ].join("\n"),
1342
+
1343
+ roadmap: [
1344
+ "이 문서는 **구축범위와 일정**입니다. 고객이 계약 범위를 확인하는 문서이므로",
1345
+ "시트를 개로 나눕니다.",
1346
+ "",
1347
+ "**`1. 구축범위`** 열은 `항목 | 번호 | 주요화면 | 구성 | 설명 | 기능 | 비고`.",
1348
+ "**기능 하나가 한 행**이고, `항목`(공통·포털별 대분류)·`주요화면`·`구성`·`설명` 은",
1349
+ "병합으로 묶습니다. 번호는 전체 통번호입니다. 이러면 고객이 화면별 기능 수를",
1350
+ "눈으로 셉니다. 원본에 기능이 문단으로 적혀 있으면 **한 기능씩 행으로 쪼개세요.**",
1351
+ "",
1352
+ "**`2. 마일스톤`** — 상단에 `총 기간`과 차수별 `전략`, 그 아래 **주 단위 간트**.",
1353
+ "열은 `Level | Task | Memo | 1 month(1w 2w 3w 4w) | …` 이고 `Level` 은 병합,",
1354
+ "막대는 배경색입니다. 간트 위에 오픈 차수를 세웁니다.",
1355
+ "",
1356
+ "핵심 수치는 `전체 기능 수`, `총 기간`, `오픈 차수`, `마일스톤 수` 입니다.",
1357
+ ].join("\n"),
1358
+
1359
+ "deliverable-register": [
1360
+ "이 문서는 **납품 목록**입니다.",
1361
+ "",
1362
+ "**`1. 산출물목록`** 열은 `공정 | 번호 | 코드 | 산출물명 | 필수 | 고객공유 |",
1363
+ "담당 | 상태 | 비고`. `공정` 은 병합하고 번호는 전체 통번호입니다.",
1364
+ "`필수`·`고객공유`·`상태` 는 글자 그대로 씁니다(아이콘·색 알약 금지).",
1365
+ "",
1366
+ "**`2. 제외항목`** — 열은 `산출물명 | 제외 사유 | 합의 일자`.",
1367
+ "**이 시트를 빠뜨리지 마세요.** 고객이 '이건 왜 없냐'고 묻는 지점입니다.",
1368
+ "",
1369
+ "버전 이력은 마지막 시트에 둡니다. 핵심 수치는 `전체 종수`, `필수`,",
1370
+ "`작성 완료`, `미착수` 입니다.",
1371
+ ].join("\n"),
1372
+
1373
+ requirements: [
1374
+ "이 문서는 **요구사항 추적표**입니다.",
1375
+ "",
1376
+ "**`1. 요구사항`** — 열은 `부서 | 번호 | 영역 | 현행 문제 | 요구 내용 |",
1377
+ "타당성 | 우선순위 | 연계 과업 | 연계 산출물 | 비고`. `부서` 와 `영역` 은",
1378
+ "병합합니다. **연계 과업과 산출물이 같은 행에 보여야** 추적표 구실을 합니다.",
1379
+ "",
1380
+ "**`2. 미수용`** — 열은 `번호 | 요구 내용 | 미수용 사유 | 대안 | 통보 여부`.",
1381
+ "거절한 요구를 감추지 마세요. 기록이 없으면 같은 요구가 다시 올라옵니다.",
1382
+ "",
1383
+ "핵심 수치는 `전체 요구사항`, `수용`, `추가 검토`, `미수용` 입니다.",
1384
+ ].join("\n"),
1385
+
1386
+ "screen-spec": [
1387
+ "이 문서는 **화면 설계서**입니다.",
1388
+ "",
1389
+ "**`1. 화면목록`** — 열은 `구분 | 번호 | 화면군 | 화면명 | 설명 | 주요 기능 | 비고`.",
1390
+ "**기능 하나가 한 행**이고 `구분`·`화면군`·`화면명`·`설명` 은 병합합니다.",
1391
+ "`구축범위` 와 같은 모양입니다.",
1392
+ "",
1393
+ "**`2. 화면상세`** 부터는 화면마다 한 시트 또는 한 쪽. 화면마다 **무엇을 하는",
1394
+ "화면인지 한 줄**을 맨 앞에 붙이고, 그 아래 요소·상태·검증 규칙을 표로 둡니다.",
1395
+ "",
1396
+ "핵심 수치는 `전체 화면 수`와 구분별 개수입니다.",
1397
+ ].join("\n"),
1398
+
1399
+ "project-brief": [
1400
+ "이 문서는 **사업 개요**입니다.",
1401
+ "",
1402
+ "**`1. 개요`** — 목표(`번호 | 목표 | 성공 판단 기준`)와 범위를 둡니다.",
1403
+ "**`범위`는 포함과 제외를 한 표에 `구분 | 내용` 으로 나란히** 둡니다.",
1404
+ "제외를 흐리게 적으면 나중에 분쟁이 됩니다.",
1405
+ "",
1406
+ "**`2. 이해관계자`** — 열은 `역할 | 소속 | 담당 | 결정 권한`.",
1407
+ "**연락처·이메일은 넣지 않습니다.** 이름과 역할만 남깁니다.",
1408
+ "",
1409
+ "핵심 수치는 `목표 수`, `총 기간`, `이해관계자 수` 입니다.",
1410
+ ].join("\n"),
1411
+
1412
+ "as-is-to-be": [
1413
+ "이 문서는 **업무 개선안**입니다. 현행과 개선을 **같은 표에서 나란히** 보여줍니다.",
1414
+ "열은 `업무 | 번호 | 현행(As-Is) | 문제점 | 개선(To-Be) | 기대 효과 | 비고`.",
1415
+ "`업무` 는 병합합니다. 현행과 개선을 다른 시트로 떼면 비교가 안 됩니다.",
1416
+ "",
1417
+ "핵심 수치는 `개선 대상 업무 수`, `자동화 항목`, `유지 항목` 입니다.",
1418
+ ].join("\n"),
1419
+
1420
+ "data-model": [
1421
+ "이 문서는 **데이터 구조**입니다.",
1422
+ "",
1423
+ "**`1. 테이블목록`** — 열은 `구분 | 번호 | 논리명 | 물리명 | 설명 | 주요 관계`.",
1424
+ "고객이 읽으므로 **한글 논리명을 물리명보다 앞에** 둡니다.",
1425
+ "",
1426
+ "**`2. 칼럼정의`** — 열은 `테이블 | 번호 | 논리명 | 물리명 | 타입 | 필수 |",
1427
+ "기본값 | 설명`. `테이블` 은 병합합니다.",
1428
+ "",
1429
+ "핵심 수치는 `테이블 수`, `칼럼 수`, `관계 수` 입니다.",
1430
+ ].join("\n"),
1431
+
1432
+ "api-spec": [
1433
+ "이 문서는 **연동 규약**입니다.",
1434
+ "열은 `구분 | 번호 | 기능 | 메서드 | 경로 | 요청 | 응답 | 인증 | 비고`.",
1435
+ "`구분` 은 병합합니다. 외부 시스템 연동은 별도 시트로 `대상 시스템 |",
1436
+ "연동 방식 | 주기 | 담당 | 상태` 를 둡니다 — 고객사 담당자가 확인할 항목입니다.",
1437
+ "",
1438
+ "핵심 수치는 `엔드포인트 수`, `외부 연동 수` 입니다.",
1439
+ ].join("\n"),
1440
+
1441
+ "qa-test-plan": [
1442
+ "이 문서는 **검증 계획**입니다. 고객이 무엇을 언제 확인해야 하는지가 핵심입니다.",
1443
+ "",
1444
+ "**`1. 검증범위`** — 열은 `구분 | 번호 | 시나리오 | 사전 조건 | 검증 절차 |",
1445
+ "통과 기준 | 담당 | 일정`. **`담당` 열에 `고객`·`수행사`를 분명히 적어** 고객이",
1446
+ "직접 하는 확인(UAT)과 우리가 하는 확인을 구분하세요.",
1447
+ "",
1448
+ "**`2. UAT 일정`** — 고객이 확인할 항목만 추려 `일자 | 대상 | 참여자 | 준비물`.",
1449
+ "",
1450
+ "핵심 수치는 `시나리오 수`, `고객 확인 항목`, `UAT 기간` 입니다.",
1451
+ ].join("\n"),
1452
+
1453
+ "release-ops": [
1454
+ "이 문서는 **오픈·운영 안내**입니다.",
1455
+ "",
1456
+ "**`1. 오픈절차`** — 열은 `단계 | 번호 | 작업 | 담당 | 예정 일시 | 선행 조건 |",
1457
+ "확인 방법`. `단계` 는 병합합니다.",
1458
+ "**`2. 고객 준비사항`** — 고객이 해야 할 일만 추려 `항목 | 기한 | 담당 | 비고`.",
1459
+ "**`3. 교육·지원`** — 대상·일정·지원 기간·연락 창구.",
1460
+ "",
1461
+ "핵심 수치는 `오픈 예정일`, `교육 대상 인원`, `지원 기간` 입니다.",
1462
+ ].join("\n"),
1463
+
1464
+ "design-system": [
1465
+ "이 문서는 **디자인 규칙**입니다. 색·글자·간격 토큰을 **견본과 함께** 보여줍니다.",
1466
+ "색 표에는 `이름 | 값(HEX) | 견본 | 쓰임` 열을 두고 `견본` 칸은 그 색으로 칠합니다.",
1467
+ "글자 표는 `이름 | 크기 | 굵기 | 줄간격 | 쓰임` 이고 실제 크기로 보여주면 좋습니다.",
1468
+ "",
1469
+ "핵심 수치는 `색 토큰 수`, `글자 단계`, `컴포넌트 수` 입니다.",
1470
+ ].join("\n"),
1363
1471
  };
1364
1472
 
1365
- const DOC_SHAPE_DEFAULT =
1366
- "원본의 구성을 따르되, **결론과 전체 그림을 앞으로 올리세요.** " +
1367
- "표가 여러 개면 가장 중요한 것 하나를 먼저 세우고 나머지를 뒤로 보냅니다. " +
1368
- "무엇이 가장 중요한지 판단이 서면, 수가 가장 많은 표가 보통 본표입니다.";
1473
+ const DOC_SHAPE_DEFAULT = [
1474
+ " 문서 종류에는 정해진 폼이 없습니다. 아래를 따르세요.",
1475
+ "",
1476
+ "1. **결론과 전체 그림을 앞으로 올리세요.** 원본 순서를 그대로 따르지 않습니다.",
1477
+ "2. 표가 여러 개면 **행 수가 가장 많은 표**를 본표로 세우고 나머지를 뒤로 보냅니다.",
1478
+ "3. 본표는 `구분 | 번호 | 항목 | 설명 | 비고` 골격으로 만들고, **가장 작은 단위가",
1479
+ " 한 행**이 되게 쪼갠 뒤 상위 분류는 병합으로 묶습니다.",
1480
+ "4. 번호는 전체 통번호로 붙입니다.",
1481
+ "5. 원본이 문단 위주라면 표로 억지로 바꾸지 말고, 절마다 소제목 + 본문으로 두되",
1482
+ " 맨 앞 목적 블록은 반드시 넣습니다.",
1483
+ ].join("\n");
1484
+
1485
+ /* 구버전·유사 문서 이름을 같은 폼으로 잇는다.
1486
+ * 문서 이름이 바뀌어도 이미 만들어진 프로젝트가 폼을 잃지 않게 한다. */
1487
+ const DOC_SHAPE_ALIAS = {
1488
+ prd: "requirements",
1489
+ "as-is-analysis": "as-is-to-be",
1490
+ "to-be-process": "as-is-to-be",
1491
+ "integration-spec": "api-spec",
1492
+ "training-adoption": "release-ops",
1493
+ "content-model": "information-architecture",
1494
+ "performance-budget": "tech-architecture",
1495
+ "security-audit": "tech-architecture",
1496
+ "accessibility-spec": "design-system",
1497
+ "interaction-motion": "design-system",
1498
+ "user-flow": "screen-spec",
1499
+ "value-proposition": "audience-persona",
1500
+ "analytics-measurement": "qa-test-plan",
1501
+ };
1502
+
1503
+ Object.assign(DOC_SHAPE, {
1504
+ "information-architecture": [
1505
+ "이 문서는 **메뉴 구조(IA)** 입니다. 고객이 메뉴 이름을 확인하는 문서입니다.",
1506
+ "열은 `구분 | 번호 | 1단 메뉴 | 2단 메뉴 | 3단 메뉴 | 화면 | 권한 | 비고`.",
1507
+ "**상위 메뉴는 병합**하고 가장 깊은 항목이 한 행이 됩니다. 번호는 전체 통번호.",
1508
+ "메뉴 이름은 **고객이 화면에서 보게 될 그 문구 그대로** 적습니다.",
1509
+ "",
1510
+ "핵심 수치는 `1단 메뉴 수`, `전체 화면 수`, `권한 구분 수` 입니다.",
1511
+ ].join("\n"),
1512
+
1513
+ "role-permission": [
1514
+ "이 문서는 **권한 매트릭스**입니다. 표 모양이 정해져 있습니다.",
1515
+ "",
1516
+ "**행은 기능·메뉴, 열은 역할**입니다.",
1517
+ "`구분 | 번호 | 메뉴·기능` 다음에 역할마다 한 열씩 놓고, 칸에는",
1518
+ "`조회`·`등록`·`수정`·`삭제`·`-` 만 씁니다. 여러 권한이면 `조회·수정` 처럼 적습니다.",
1519
+ "역할이 다섯을 넘으면 권한 등급으로 묶어 열을 줄이세요.",
1520
+ "",
1521
+ "**역할 정의를 별도 시트에** 둡니다 — `역할 | 설명 | 해당 인원 | 승인자`.",
1522
+ "핵심 수치는 `역할 수`, `통제 대상 기능 수` 입니다.",
1523
+ ].join("\n"),
1524
+
1525
+ "audience-persona": [
1526
+ "이 문서는 **사용자 정의**입니다. 문단으로 흐르면 읽히지 않습니다.",
1527
+ "열은 `구분 | 사용자 | 역할·상황 | 목표 | 불편한 점 | 이 서비스에서 얻는 것`.",
1528
+ "사용자 유형 하나가 한 행입니다. **의사결정자와 실사용자가 다르면 행을 나눠**",
1529
+ "분명히 구분하세요 — 고객사에서 결재하는 사람과 쓰는 사람이 다릅니다.",
1530
+ "",
1531
+ "핵심 수치는 `사용자 유형 수`, `예상 사용 인원` 입니다.",
1532
+ ].join("\n"),
1533
+
1534
+ "risk-open-questions": [
1535
+ "이 문서는 **리스크 대장**입니다. 고객에게 보낼 때는 **결정을 요청하는 문서**입니다.",
1536
+ "열은 `등급 | 번호 | 항목 | 현재 상황 | 영향 | 필요한 결정 | 결정 주체 | 기한`.",
1537
+ "`등급` 으로 묶어 병합하고 **Blocker 를 맨 위**에 둡니다.",
1538
+ "`결정 주체` 가 고객인 항목만 추려 별도 시트 `고객 결정 요청` 을 만드세요.",
1539
+ "",
1540
+ "핵심 수치는 `전체 항목`, `Blocker`, `고객 결정 대기` 입니다.",
1541
+ ].join("\n"),
1542
+
1543
+ "decision-log": [
1544
+ "이 문서는 **의사결정 기록**입니다. 시간순으로 쌓인 기록이므로 표 하나로 냅니다.",
1545
+ "열은 `일자 | 번호 | 결정 사항 | 배경 | 근거 | 영향 | 결정 주체`.",
1546
+ "**최신이 위로** 오게 정렬합니다. 고객이 '언제 무엇을 합의했는지' 찾는 문서입니다.",
1547
+ "",
1548
+ "핵심 수치는 `전체 결정 수`, `기간`, `고객 합의 항목 수` 입니다.",
1549
+ ].join("\n"),
1550
+
1551
+ glossary: [
1552
+ "이 문서는 **용어표**입니다. 열은 `분류 | 용어 | 뜻 | 비고`.",
1553
+ "`분류` 는 병합하고 용어는 **가나다순**으로 정렬합니다.",
1554
+ "약어는 `용어` 칸에 `UAT (User Acceptance Test)` 처럼 풀어 함께 적습니다.",
1555
+ "",
1556
+ "핵심 수치는 `분류 수`, `용어 수` 입니다.",
1557
+ ].join("\n"),
1558
+
1559
+ "data-migration": [
1560
+ "이 문서는 **데이터 이행 계획**입니다.",
1561
+ "열은 `구분 | 번호 | 대상 데이터 | 원천 | 대상 | 건수 | 변환 규칙 |",
1562
+ "검증 방법 | 담당 | 일정`. `구분` 은 병합합니다.",
1563
+ "**`건수` 와 `검증 방법` 을 비우지 마세요** — 고객이 가장 먼저 확인합니다.",
1564
+ "이행 리허설 일정을 별도 시트에 둡니다.",
1565
+ "",
1566
+ "핵심 수치는 `대상 테이블 수`, `총 건수`, `이행 예정일` 입니다.",
1567
+ ].join("\n"),
1568
+
1569
+ "seo-spec": [
1570
+ "이 문서는 **검색 노출 설계**입니다.",
1571
+ "열은 `구분 | 번호 | 페이지 | 제목(title) | 설명(description) | 주요 키워드 | 비고`.",
1572
+ "`구분` 은 병합합니다. 제목·설명은 **실제 들어갈 문구 그대로** 적습니다.",
1573
+ "",
1574
+ "핵심 수치는 `대상 페이지 수`, `주요 키워드 수` 입니다.",
1575
+ ].join("\n"),
1576
+
1577
+ "copy-guide": [
1578
+ "이 문서는 **문안 규칙**입니다. 규칙만 나열하면 지켜지지 않습니다.",
1579
+ "열은 `구분 | 규칙 | 좋은 예 | 나쁜 예` 로 두어 **예시를 나란히** 보여주세요.",
1580
+ "금지 표현은 별도 시트에 `표현 | 대신 쓸 말 | 이유` 로 둡니다.",
1581
+ "",
1582
+ "핵심 수치는 `규칙 수`, `금지 표현 수` 입니다.",
1583
+ ].join("\n"),
1584
+
1585
+ "brand-messaging": [
1586
+ "이 문서는 **메시지 정의**입니다. 핵심 문구가 먼저 보여야 합니다.",
1587
+ "맨 앞에 한 줄 슬로건과 세 문장 요약을 크게 두고, 그 아래 표로",
1588
+ "`대상 | 전달할 메시지 | 근거 | 쓰이는 곳` 을 둡니다.",
1589
+ "",
1590
+ "핵심 수치는 `메시지 수`, `대상 구분 수` 입니다.",
1591
+ ].join("\n"),
1592
+
1593
+ "tech-architecture": [
1594
+ "이 문서는 **기술 구성**입니다. 고객에게는 **선택 근거와 비용**이 핵심입니다.",
1595
+ "열은 `구분 | 항목 | 선택 | 버전 | 선택 근거 | 대안 | 비고`. `구분` 은 병합.",
1596
+ "성능·보안 기준은 별도 시트에 `항목 | 목표 | 측정 방법 | 현재` 로 둡니다.",
1597
+ "**내부 서버 주소·계정·키는 절대 넣지 않습니다.**",
1598
+ "",
1599
+ "핵심 수치는 `주요 구성 요소 수`, `외부 의존 수` 입니다.",
1600
+ ].join("\n"),
1601
+ });
1369
1602
 
1370
1603
  function loadScripts(project) {
1371
1604
  const dir = join(SKILL_DIR, "templates", "script");
@@ -1377,12 +1610,14 @@ function loadScripts(project) {
1377
1610
  if (!style) return null;
1378
1611
  const purpose = part("_purpose");
1379
1612
  const scope = part("_scope");
1613
+ const form = part("_form");
1380
1614
  const out = {};
1381
1615
  for (const f of SCRIPT_FORMATS) {
1382
1616
  const path = join(dir, f.id + ".md");
1383
1617
  if (!existsSync(path)) continue;
1384
1618
  out[f.id] = readFileSync(path, "utf8")
1385
1619
  .replace("__STYLE__", style)
1620
+ .replace("__FORM__", form)
1386
1621
  .replace("__PURPOSE__", purpose)
1387
1622
  .replace("__SCOPE__", scope)
1388
1623
  .split("__PROJECT__").join(project || "이");
@@ -1393,7 +1628,8 @@ function loadScripts(project) {
1393
1628
  /** 문서 id(`07-screen-spec`)에서 구성 지침을 찾는다. */
1394
1629
  function docShape(id = "") {
1395
1630
  const name = id.replace(/^\d+-/, "");
1396
- return "## 문서의 구성\n\n" + (DOC_SHAPE[name] || DOC_SHAPE_DEFAULT);
1631
+ const key = DOC_SHAPE[name] ? name : DOC_SHAPE_ALIAS[name] || name;
1632
+ return "## 이 문서의 구성\n\n" + (DOC_SHAPE[key] || DOC_SHAPE_DEFAULT);
1397
1633
  }
1398
1634
 
1399
1635
 
@@ -1889,15 +2125,21 @@ const CSS = `
1889
2125
  background:#000; box-shadow:0 0 0 1px rgba(255,255,255,.22)}
1890
2126
  .nav-item.unused .nav-label{color:var(--tx-faint)}
1891
2127
  .nav-item.unused:hover .nav-label{color:var(--tx-dim)}
1892
- /* 관리 모드 */
1893
- body.inactive-mode .nav-item{position:relative; padding-left:30px}
1894
- body.inactive-mode .nav-item .nav-dot{position:absolute; left:18px}
1895
- .nav-check{position:absolute; left:7px; top:50%; transform:translateY(-50%);
1896
- width:12px; height:12px; margin:0; cursor:pointer; accent-color:var(--accent);
1897
- display:none}
2128
+ /* 관리 모드
2129
+ *
2130
+ * 체크박스를 절대 배치하면 상태 점과 겹친다(7~19 vs 18~23). 행은 이미
2131
+ * flex + gap 이므로 흐름 안에 두면 간격이 저절로 맞는다. */
2132
+ .nav-check{display:none; flex:none; width:13px; height:13px; margin:0;
2133
+ cursor:pointer; accent-color:var(--accent)}
1898
2134
  body.inactive-mode .nav-check{display:block}
2135
+ /* 체크박스가 없는 행(홈·가이드·추적성)도 왼쪽을 맞춘다.
2136
+ * 8 + 13 + 8 = 29 — 체크박스가 차지하는 폭만큼 밀어 준다. */
2137
+ body.inactive-mode .nav-item{padding-left:29px}
2138
+ body.inactive-mode .nav-item.has-check{padding-left:8px}
2139
+ body.inactive-mode .nav-item:hover{background:var(--hover)}
1899
2140
  body.inactive-mode .nav-tools{background:var(--hover); border-radius:7px;
1900
- margin:0 6px 6px; padding:6px 8px}
2141
+ margin:0 6px 8px; padding:7px 9px}
2142
+ body.inactive-mode .nav-toggle{color:var(--tx)}
1901
2143
  html{-webkit-text-size-adjust:100%}
1902
2144
  body{
1903
2145
  margin:0; background:var(--bg); color:var(--tx);
@@ -2300,6 +2542,7 @@ const JS = `
2300
2542
  cb.className = 'nav-check';
2301
2543
  cb.title = '비활성으로 표시';
2302
2544
  item.insertBefore(cb, item.firstChild);
2545
+ item.classList.add('has-check');
2303
2546
  });
2304
2547
 
2305
2548
  /* 체크박스가 <a> 안에 있어 클릭이 그대로 두면 문서 이동까지 간다.
@@ -0,0 +1,95 @@
1
+ ## 산출물 폼 문법 — 이대로 만드세요
2
+
3
+ 실제로 고객에게 나간 산출물에서 뽑은 형태입니다. **"정돈된 표"의 구체적인 모양이
4
+ 이것입니다.** 원본 마크다운 표를 그대로 옮기면 이 모양이 되지 않습니다.
5
+
6
+ ### 1. 시트·쪽에 번호를 붙여 읽는 순서를 못박습니다
7
+
8
+ ```
9
+ 1. 구축범위 2. 마일스톤 3. 산출물
10
+ ```
11
+
12
+ `Sheet1`, `표1` 같은 이름을 쓰지 마세요. **번호 + 내용을 말하는 이름**입니다.
13
+
14
+ ### 2. 표는 B열부터. A열은 여백입니다
15
+
16
+ A열 폭을 2~3으로 좁혀 왼쪽 여백으로 씁니다. 1행도 비웁니다.
17
+ 표가 종이 끝에 붙으면 급하게 만든 문서로 보입니다.
18
+
19
+ ### 3. 제목 줄 오른쪽에 전제를 씁니다 — 빠뜨리지 마세요
20
+
21
+ ```
22
+ B2: 구축범위 │ D2: 추후 세부 업무 협의를 통해 변경 및 추가될 수 있습니다
23
+ B2: 마일스톤 │ D2: 본 일정은 IA 확정 이전 기준이며, 상세 기획 협의에
24
+ │ 따라 일부 변경될 수 있습니다
25
+ ```
26
+
27
+ **고객 송부물에는 이 한 줄이 반드시 필요합니다.** 없으면 그 표가 확정 약속으로
28
+ 읽히고, 나중에 범위·일정 분쟁의 근거가 됩니다. 원본에 근거가 없으면
29
+ `상세 협의에 따라 변경될 수 있습니다` 를 기본으로 씁니다.
30
+
31
+ ### 4. 계층은 병합으로, 가장 작은 단위가 한 행입니다
32
+
33
+ **이것이 가장 중요한 규칙입니다.** 같은 상위 값을 행마다 반복해 찍지 마세요.
34
+ 첫 행에만 쓰고 아래로 병합합니다.
35
+
36
+ ```
37
+ 항목 번호 주요화면 구성 설명 기능
38
+ ─────────────────────────────────────────────────────────────────────
39
+ 공통 1 로그인 로그인 인증 ID/PW 기반 인증 ID·PW 입력 인증
40
+ 2 ↑ ↑ ↑ 역할에 따라 포털 분기
41
+ 3 ↑ ↑ ↑ 5회 오입력 시 잠금
42
+ 4 ↑ 비밀번호 찾기 본인 확인 가입정보 입력·인증
43
+ 관리자 포털 8 밀도·통계 지역(글로벌) 히트맵 조회 검색 필터 & 히트맵
44
+ 9 ↑ ↑ ↑ 지역 밀도 상세보기
45
+ ```
46
+
47
+ `↑` 는 병합을 뜻합니다. **기능 하나가 한 행**이고, 화면·구성·분류는 병합으로
48
+ 묶입니다. 이러면 고객이 "이 화면에 기능이 몇 개인지" 를 눈으로 셉니다.
49
+
50
+ 번호는 **전체 통번호**입니다. 분류마다 1부터 다시 시작하지 마세요.
51
+
52
+ ### 5. 일정은 주 단위 간트로 그립니다
53
+
54
+ 표에 시작일·종료일만 적으면 기간이 보이지 않습니다. **머리행을 두 줄로 만들고
55
+ 칸을 색칠하세요.**
56
+
57
+ ```
58
+ Level Task Memo 1 month 2 month 3 month
59
+ 1w 2w 3w 4w 1w 2w 3w 4w 1w 2w 3w 4w
60
+ ──────────────────────────────────────────────────────────────────────────
61
+ 분석·설계 요구사항 상세화 - IA 확정 ██ ██
62
+ DB 모델링 - 테이블 ██ ██
63
+ UI/UX 와이어프레임 - 화면별 ██ ██ ██
64
+ ```
65
+
66
+ - 윗줄은 `1 month` `2 month` … 를 **4칸씩 병합**
67
+ - 아랫줄은 `1w 2w 3w 4w` 반복
68
+ - 막대는 **셀 배경색 채움** (`#B7B7B7`). 칸에 글자를 넣지 않습니다
69
+ - 주 칸 폭은 3~4로 좁힙니다
70
+ - `Level` 은 병합(분석·설계 / UI·UX / Front-End / Back-End / 외부 연동 / 통합테스트·배포)
71
+ - `Memo` 는 `- ` 로 시작하는 한 줄 설명
72
+
73
+ ### 6. 일정 위에 오픈 차수를 세웁니다
74
+
75
+ 간트 머리행 **위쪽 행**에 `1차` `2차` `3차` 를 해당 주 위치에 놓습니다.
76
+ 고객이 가장 먼저 찾는 것이 "언제 쓸 수 있나" 입니다.
77
+
78
+ ### 7. 상단에 요약 블록
79
+
80
+ 간트 위에 두 줄을 둡니다.
81
+
82
+ ```
83
+ 총 기간 5개월 (개발 4 + 안정화 1)
84
+ 전략 1차 오픈 → 빠른 사용 시작 (목록·기본 기능)
85
+ 2차 오픈 → 고도화 (대시보드)
86
+ 3차 → 사용자 피드백 반영
87
+ 4차 → 외부 연동·고도화
88
+ 5차 → 안정화
89
+ ```
90
+
91
+ 원본에 차수 전략이 없으면 **지어내지 말고** 총 기간과 주요 마일스톤만 씁니다.
92
+
93
+ ### 8. 한 시트에 하나의 표만
94
+
95
+ 구축범위와 마일스톤을 한 시트에 넣지 마세요. 성격이 다르면 시트를 나눕니다.
@@ -13,6 +13,8 @@
13
13
 
14
14
  __SHAPE__
15
15
 
16
+ __FORM__
17
+
16
18
  __PURPOSE__
17
19
 
18
20
  __SCOPE__
@@ -13,6 +13,8 @@
13
13
 
14
14
  __SHAPE__
15
15
 
16
+ __FORM__
17
+
16
18
  __PURPOSE__
17
19
 
18
20
  __SCOPE__
@@ -13,6 +13,8 @@
13
13
 
14
14
  __SHAPE__
15
15
 
16
+ __FORM__
17
+
16
18
  __PURPOSE__
17
19
 
18
20
  __SCOPE__
@@ -13,6 +13,8 @@
13
13
 
14
14
  __SHAPE__
15
15
 
16
+ __FORM__
17
+
16
18
  __PURPOSE__
17
19
 
18
20
  __SCOPE__