@decencia/ch-cli 1.21.0 → 1.23.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.
@@ -3,7 +3,7 @@ name: 2t-decencia-channel-issue-coder
3
3
  description: GitHub issue 1개를 입력으로 받아 끝까지 처리하는 코딩 에이전트. 이슈 읽기 → 소통채널 spec/SQA 확인 → git worktree+브랜치에서 구현 → 자체 소프트리뷰 루프 → unittest + SQA → PR 생성 → 소통채널 workStatus·이슈 기록. 다수 병렬 실행 전제, PR까지만(머지·base 브랜치 직접 push·이슈 close는 사람).
4
4
  tools: Read, Write, Edit, Grep, Glob, Bash, Agent
5
5
  model: opus
6
- version: 1.21.0
6
+ version: 1.23.0
7
7
  ---
8
8
 
9
9
  ## 역할
@@ -3,7 +3,7 @@ name: 2t-decencia-channel-pr-merger
3
3
  description: issue-coder가 낸 PR을 기본 완전 자율로 base 브랜치(기본 브랜치 또는 sprint 통합 브랜치)에 머지하는 에이전트. 핵심 역량은 다수 병렬 worktree가 만든 충돌을 작업 의도 기반(semantic)으로 해결하는 것. CI green+충돌없음이면 바로 squash 머지, 충돌이면 의도대로 해결 후 테스트로 검증하여 머지. 재SQA·전면 재리뷰는 하지 않음. 다수 PR은 직렬 머지.
4
4
  tools: Read, Write, Edit, Grep, Glob, Bash
5
5
  model: opus
6
- version: 1.21.0
6
+ version: 1.23.0
7
7
  ---
8
8
 
9
9
  ## 역할
@@ -3,7 +3,7 @@ name: 2t-decencia-channel-terraformer
3
3
  description: 소통채널을 쓰지 않던 기존 코드베이스를 역분석하여 소통채널 프로젝트로 편입(terraforming)하는 자율 에이전트. 코드가 single source of truth — 코드를 읽어 DB 스키마 → 기능명세(spec) → PRD를 bottom-up으로 역생성한다(SQA 제외). 대량 분석은 서브에이전트 병렬, 코드로 못 메우는 의도는 [추정]으로 채우고 "사람확인 질문 리스트"를 산출물로 남긴다. PR/머지·이슈 발행은 하지 않음.
4
4
  tools: Read, Write, Edit, Grep, Glob, Bash, Agent
5
5
  model: opus
6
- version: 1.21.0
6
+ version: 1.23.0
7
7
  ---
8
8
 
9
9
  ## 역할
@@ -0,0 +1,19 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * export 대상 폴더를 엄격 검증한다(파괴적 교체 앞단 안전장치).
4
+ * backup export의 `resolveSafeExportDir` 규칙을 복제하되 **번들 마커만 바꾼다**.
5
+ * - cwd 하위(strict descendant)만 허용 → `..`·절대경로 거부
6
+ * - 심볼릭/junction 우회 차단 → realpath도 cwd 하위여야
7
+ * - 이미 존재하면 비어있거나 이전 핸드오프 번들(01-화면지도.md 보유)일 때만 교체 허용
8
+ *
9
+ * ⚠️ backup은 비어있지 않은 폴더를 manifest.json 유무로 판정하지만 핸드오프 번들에는
10
+ * 매니페스트가 없다. resolveSafeExportDir를 문자 그대로 재사용하면 두 번째 export부터
11
+ * manifest.json 부재로 거부되므로, 마커를 01-화면지도.md로 바꾼 전용 복제본을 쓴다 —
12
+ * 화면이 바뀔 때마다 같은 폴더로 재export하는 흐름(E16 재생성 원칙)이 거부되지 않게.
13
+ */
14
+ export declare function resolveSafeHandoffDir(dir: string): string;
15
+ /**
16
+ * ch handoff — 디자인 handoff 배달 (읽기 전용, ch-api 엔진 호출)
17
+ */
18
+ export declare function registerHandoffCommand(program: Command): void;
19
+ //# sourceMappingURL=handoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff.d.ts","sourceRoot":"","sources":["../../src/commands/handoff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4CpC;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA0BzD;AA8ID;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAS7D"}
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.resolveSafeHandoffDir = resolveSafeHandoffDir;
37
+ exports.registerHandoffCommand = registerHandoffCommand;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const client_1 = require("../client");
41
+ const output_1 = require("../output");
42
+ // export/screen은 읽기 전용 왕복이지만 번들 생성(PRD·화면·명세 읽어 마크다운 조립)이
43
+ // 다소 무거울 수 있어 기본 30s보다 넉넉히 잡는다.
44
+ const HANDOFF_TIMEOUT = 120000;
45
+ // 통합 번들이 항상 포함하는 파일 — 이전 핸드오프 번들 폴더인지 식별하는 마커.
46
+ // (backup export의 manifest.json 역할. 핸드오프 번들에는 매니페스트가 없다 —
47
+ // 서버 파일 세트는 00-제품개요.md·01-화면지도.md·02-{NN}-{epic}.md 뿐이다.)
48
+ const BUNDLE_MARKER = '01-화면지도.md';
49
+ /**
50
+ * export 대상 폴더를 엄격 검증한다(파괴적 교체 앞단 안전장치).
51
+ * backup export의 `resolveSafeExportDir` 규칙을 복제하되 **번들 마커만 바꾼다**.
52
+ * - cwd 하위(strict descendant)만 허용 → `..`·절대경로 거부
53
+ * - 심볼릭/junction 우회 차단 → realpath도 cwd 하위여야
54
+ * - 이미 존재하면 비어있거나 이전 핸드오프 번들(01-화면지도.md 보유)일 때만 교체 허용
55
+ *
56
+ * ⚠️ backup은 비어있지 않은 폴더를 manifest.json 유무로 판정하지만 핸드오프 번들에는
57
+ * 매니페스트가 없다. resolveSafeExportDir를 문자 그대로 재사용하면 두 번째 export부터
58
+ * manifest.json 부재로 거부되므로, 마커를 01-화면지도.md로 바꾼 전용 복제본을 쓴다 —
59
+ * 화면이 바뀔 때마다 같은 폴더로 재export하는 흐름(E16 재생성 원칙)이 거부되지 않게.
60
+ */
61
+ function resolveSafeHandoffDir(dir) {
62
+ const cwd = process.cwd();
63
+ const resolved = path.resolve(cwd, dir);
64
+ const rel = path.relative(cwd, resolved);
65
+ if (rel === '' || rel.startsWith('..') || path.isAbsolute(rel)) {
66
+ throw new Error(`--dir 은 현재 작업 폴더 하위의 전용 폴더여야 합니다: "${dir}"`);
67
+ }
68
+ if (fs.existsSync(resolved)) {
69
+ const realCwd = fs.realpathSync(cwd);
70
+ const real = fs.realpathSync(resolved);
71
+ const realRel = path.relative(realCwd, real);
72
+ if (realRel === '' || realRel.startsWith('..') || path.isAbsolute(realRel)) {
73
+ throw new Error(`--dir 이 작업 폴더 밖을 가리킵니다(symlink?): "${dir}"`);
74
+ }
75
+ const entries = fs.readdirSync(real);
76
+ // 한글 파일명의 유니코드 정규화(NFC/NFD) 차이에 견고하게 비교한다.
77
+ const hasMarker = entries.some((e) => e.normalize('NFC') === BUNDLE_MARKER.normalize('NFC'));
78
+ if (entries.length > 0 && !hasMarker) {
79
+ throw new Error(`--dir "${dir}" 은 비어있지 않고 핸드오프 번들 폴더(${BUNDLE_MARKER})도 아닙니다. 덮어쓰기를 거부합니다.`);
80
+ }
81
+ }
82
+ return resolved;
83
+ }
84
+ function registerHandoffExport(handoff, program) {
85
+ handoff
86
+ .command('export')
87
+ .description('디자인 handoff 통합 번들을 폴더에 개별 .md 파일로 기록 (zip 아님)')
88
+ .option('--dir <dir>', '대상 폴더 (cwd 하위 전용)', 'design-handoff')
89
+ .addHelpText('after', `
90
+ 서버(ch-api SCREEN-023)가 낸 파일 세트를 폴더에 그대로 기록한다:
91
+ 00-제품개요.md · 01-화면지도.md · 02-{NN}-{epic}.md × N
92
+
93
+ 임시 폴더에 먼저 쓰고 원자적으로 교체하므로 쓰다 실패해도 기존 번들이 보존된다.
94
+ 같은 폴더로 재export(화면이 바뀔 때마다)는 허용된다. 화면 0개(레거시) 프로젝트면
95
+ 빈 폴더를 만들지 않고 안내 후 중단한다. --dry-run은 서버를 호출하지 않는다.
96
+ `)
97
+ .action(async (options) => {
98
+ const globalOpts = program.opts();
99
+ try {
100
+ // 로컬 입력 검증을 먼저: 작업 폴더 밖·남의 폴더면 요청을 보내지 않고 종료코드를 세운다.
101
+ let target;
102
+ try {
103
+ target = resolveSafeHandoffDir(options.dir);
104
+ }
105
+ catch (e) {
106
+ (0, output_1.failInput)(e?.message ?? String(e));
107
+ return;
108
+ }
109
+ if (globalOpts.dryRun) {
110
+ (0, output_1.printInfo)(`[DRY-RUN] 디자인 handoff 번들을 "${options.dir}/" 로 내보내기 (서버 미호출)`);
111
+ return;
112
+ }
113
+ const client = (0, client_1.createClient)({
114
+ projectId: globalOpts.project,
115
+ verbose: globalOpts.verbose,
116
+ });
117
+ const { data } = await client.get('/handoff/export', {
118
+ timeout: HANDOFF_TIMEOUT,
119
+ });
120
+ const files = data?.files ?? [];
121
+ const screenCount = data?.screenCount ?? 0;
122
+ // 화면 0개 가드(레거시·screensEnabled 미설정, 절대 제약): 폴더·파일을 만들지 않고
123
+ // 안내만 남긴다. backup의 "내보낼 문서가 없습니다" 선례처럼 경고 후 종료한다.
124
+ if (screenCount === 0 || files.length === 0) {
125
+ (0, output_1.printWarning)('화면이 없어 디자인 handoff를 생성하지 않았습니다. (빈 폴더를 만들지 않았습니다)');
126
+ return;
127
+ }
128
+ // 임시 폴더에 먼저 기록 후 원자적 교체 → 쓰기 실패해도 기존 번들 보존.
129
+ const tmp = `${target}.tmp-${process.pid}`;
130
+ if (fs.existsSync(tmp))
131
+ fs.rmSync(tmp, { recursive: true, force: true });
132
+ for (const f of files) {
133
+ const full = path.join(tmp, ...f.path.split('/'));
134
+ fs.mkdirSync(path.dirname(full), { recursive: true });
135
+ fs.writeFileSync(full, f.content, 'utf-8');
136
+ }
137
+ if (fs.existsSync(target))
138
+ fs.rmSync(target, { recursive: true, force: true });
139
+ fs.renameSync(tmp, target);
140
+ // 기계 모드: stdout에 요약 JSON 하나, 성공 메시지는 stderr로 분리(PLAT-003).
141
+ if ((0, output_1.isMachineMode)()) {
142
+ console.log(JSON.stringify({
143
+ screenCount,
144
+ dir: options.dir,
145
+ files: files.map((f) => ({ path: f.path })),
146
+ }, null, 2));
147
+ }
148
+ (0, output_1.printSuccess)(`디자인 handoff 내보내기 완료 — 화면 ${screenCount}개, 파일 ${files.length}개 → ${options.dir}/`);
149
+ (0, output_1.printInfo)('폴더를 열어 .md 파일을 Claude Design 대화에 드래그하세요 (한 채팅 최대 20개).');
150
+ }
151
+ catch (error) {
152
+ // HTTP 4xx/5xx·연결 실패는 client 인터셉터가 처리(stderr + exitCode).
153
+ }
154
+ });
155
+ }
156
+ function registerHandoffScreen(handoff, program) {
157
+ // 로컬 --json은 두지 않는다. commander가 부모(program)의 전역 옵션을 서브커맨드
158
+ // 뒤에서도 해석하므로 `ch handoff screen <id> --json`이 그대로 동작한다.
159
+ handoff
160
+ .command('screen <screenId>')
161
+ .description('화면 하나의 핸드오프 카드 마크다운을 stdout으로 (Claude Design 붙여넣기용)')
162
+ .action(async (screenId) => {
163
+ const globalOpts = program.opts();
164
+ try {
165
+ const client = (0, client_1.createClient)({
166
+ projectId: globalOpts.project,
167
+ verbose: globalOpts.verbose,
168
+ });
169
+ const { data } = await client.get(`/handoff/screen/${encodeURIComponent(screenId)}`);
170
+ const content = data?.content ?? null;
171
+ // 서버는 없는 화면·화면 0개 프로젝트에 404가 아니라 200 + content:null로 답한다
172
+ // (SCREEN-023 계약). 배달물(카드)이 없으므로 실패로 다룬다 — stderr 안내 +
173
+ // 종료코드 ≠ 0, stdout은 비운다(기계 모드 stdout 오염 방지).
174
+ if (content === null) {
175
+ const screenCount = data?.screenCount ?? 0;
176
+ (0, output_1.failInput)(screenCount === 0
177
+ ? '이 프로젝트에는 화면이 없어 카드를 생성할 수 없습니다. (screensEnabled 미설정)'
178
+ : `화면을 찾을 수 없습니다: ${screenId}`);
179
+ return;
180
+ }
181
+ // 기계 모드: 서버 응답 {content, screenName, screenCount}(SCREEN-023 계약)를
182
+ // 재명명·스트립 없이 그대로 순수 JSON으로 stdout에 낸다(SSOT 드리프트 금지).
183
+ if ((0, output_1.isMachineMode)()) {
184
+ console.log(JSON.stringify(data, null, 2));
185
+ return;
186
+ }
187
+ // 기본: 카드 마크다운 본문 자체가 배달물이라 stdout으로 낸다(붙여넣기용).
188
+ // 컨텍스트·성공 고지는 이미 stderr로 분리돼 있다.
189
+ console.log(content);
190
+ }
191
+ catch (error) {
192
+ // HTTP 4xx/5xx·연결 실패는 client 인터셉터가 처리(stderr + exitCode).
193
+ }
194
+ });
195
+ }
196
+ /**
197
+ * ch handoff — 디자인 handoff 배달 (읽기 전용, ch-api 엔진 호출)
198
+ */
199
+ function registerHandoffCommand(program) {
200
+ const handoff = program
201
+ .command('handoff')
202
+ .description('디자인 handoff 번들 배달 (읽기 전용 — ch-api 엔진 호출, 개발자·에이전트용 부차 경로)');
203
+ registerHandoffExport(handoff, program);
204
+ registerHandoffScreen(handoff, program);
205
+ }
206
+ //# sourceMappingURL=handoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff.js","sourceRoot":"","sources":["../../src/commands/handoff.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -52,6 +52,7 @@ const db_schema_1 = require("./commands/db-schema");
52
52
  const db_tables_1 = require("./commands/db-tables");
53
53
  const screens_1 = require("./commands/screens");
54
54
  const backup_1 = require("./commands/backup");
55
+ const handoff_1 = require("./commands/handoff");
55
56
  const notifications_1 = require("./commands/notifications");
56
57
  const dashboard_1 = require("./commands/dashboard");
57
58
  const github_1 = require("./commands/github");
@@ -108,6 +109,7 @@ program.hook('preAction', (thisCommand) => {
108
109
  (0, db_tables_1.registerDbTablesCommand)(program);
109
110
  (0, screens_1.registerScreensCommand)(program);
110
111
  (0, backup_1.registerBackupCommand)(program);
112
+ (0, handoff_1.registerHandoffCommand)(program);
111
113
  (0, notifications_1.registerNotificationsCommand)(program);
112
114
  (0, dashboard_1.registerDashboardCommand)(program);
113
115
  (0, github_1.registerGithubCommand)(program);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decencia/ch-cli",
3
- "version": "1.21.0",
3
+ "version": "1.23.0",
4
4
  "description": "Decencia Communication Channel CLI",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -2,7 +2,7 @@
2
2
  name: 2t-decencia-channel-change-manager
3
3
  description: 기존 소통채널 프로젝트의 기능 추가/수정을 사용자와 대화로 명확화 → 전파 스킬로 연관 PRD/spec/DB/SQA 일괄 갱신 → 변경으로 생긴 새 할일을 GitHub 이슈로 발행하는 오케스트레이터 스킬. (1)대화로 변경 내용 확정 → (2)[게이트] 영향 범위 확인 후 전파 적용 → (3)[게이트] 할일 목록 확인 후 이슈 발행. "이 기능 바꿀래"·"기획 수정"·"이거 추가해줘(기존 프로젝트)" 류 요청 시.
4
4
  allowed-tools: Bash(ch:*) Bash(git:*) Bash(gh:*) Read Write
5
- version: 1.21.0
5
+ version: 1.23.0
6
6
  ---
7
7
 
8
8
  <!-- ch-version-gate -->
@@ -85,6 +85,7 @@ ch check
85
85
  - `2t-decencia-channel-change-propagation-v2`로 **영향 분석**: 이 변경이 닿는 PRD·spec(ui·logic·dbTableRefs·points)·**screens(route·목적·접근권한·relatedSpecIds)**·db-schema/db-tables·SQA·workStatus를 빠짐없이 식별.
86
86
  - → **[게이트] 영향 범위를 사용자에게 제시하고 확인받는다** (어떤 문서가 어떻게 바뀌는지 요약). **붙는 화면 / 새로 생기는 화면**도 요약에 포함한다. 승인 전에는 문서를 건드리지 않는다.
87
87
  - 승인되면 전파 스킬 절차대로 **연관 문서를 순차 갱신**. `spec.dbTableRefs` ↔ `dbTable.relatedSpecIds` 양방향 동기화 유지.
88
+ - 전파로 **화면이 바뀌었으면** 디자이너에게 안내한다: 바뀐 화면은 **핸드오프를 다시 받아** Claude Design에 재투입한다(핸드오프는 매번 최신 화면에서 새로 생성된다). 웹 [통합 핸드오프 zip 받기] / `ch handoff export --dir`, 화면 하나면 화면 상세 [이 화면 프롬프트 복사] / `ch handoff screen <화면id>`. 절차는 `docs/design-handoff-sop.md`.
88
89
 
89
90
  ## 3. 할일 → GitHub 이슈 (게이트 ②)
90
91
  *(기획 변경이면 전파 적용 후, 버그면 §1.5 직후)*
@@ -10,7 +10,7 @@ description: |
10
10
  (3) 명세·DB·SQA가 서로 어긋나 있는 의심이 들 때 (정합성 점검),
11
11
  (4) PRD 갱신 후 하위 spec/db/SQA로 변경분을 흘려보내야 할 때,
12
12
  (5) Sprint 진행 중 도메인 규칙·스키마가 흔들렸을 때 연쇄 갱신이 필요할 때.
13
- version: 1.21.0
13
+ version: 1.23.0
14
14
  ---
15
15
 
16
16
  <!-- ch-version-gate -->
@@ -10,7 +10,7 @@ description: |
10
10
  (4) ch screens 명령(list/get/create/update/delete/migrate)으로 화면을 다루거나 레거시 프로젝트를 화면 모드로 이관할 때,
11
11
  (5) spec의 화면 연결을 CLI로 걸거나 풀 때 (`ch specs screen-refs`),
12
12
  (6) Spec ID를 JIRA 스타일(`LOGIN-001`)로 직접 지정할 때 (`ch specs create --id`).
13
- version: 1.21.0
13
+ version: 1.23.0
14
14
  ---
15
15
 
16
16
  <!-- ch-version-gate -->
@@ -324,6 +324,25 @@ ch screens migrate --rollback --force # 되돌아갈 route가 없어
324
324
  - **쓰기 순서**: 화면 생성 → 명세 연결 → `screensEnabled=true`(항상 마지막). 중간에 실패하면 플래그가 꺼진 채 남아 프로젝트는 레거시 모드 그대로다.
325
325
  - `--dry-run`은 계획만 낸다(쓰기 0건). 단, 계획을 서버 데이터로 계산하므로 **읽기 요청은 나간다** — 다른 커맨드의 `--dry-run`과 다른 점이다.
326
326
 
327
+ ## handoff 명령 (디자인 핸드오프)
328
+
329
+ 화면(screens)을 **디자이너용 화면 기획 묶음**으로 내보낸다. **읽기 전용 파생**이다 — 새로 저장하는 데이터가 없다. 생성 로직은 ch-api 단일 생성원이고 웹 버튼·CLI는 그 결과를 **배달만** 한다(웹·CLI에 별도 이미터 없음).
330
+
331
+ ```bash
332
+ ch handoff export --dir <폴더> # 통합 핸드오프 파일셋을 그 폴더에 기록
333
+ ch handoff screen <화면id> # 화면 하나의 프롬프트를 stdout으로 출력
334
+ ```
335
+
336
+ - `ch handoff export --dir <폴더>` — `00-제품개요.md`(PRD content) · `01-화면지도.md`(화면 IA 트리) · `02-{NN}-{epic}.md × N`(epic별 화면 카드)를 그 폴더에 쓴다. **웹 화면 탭 [통합 핸드오프 zip 받기]와 같은 파일셋**이다. `NN`은 그 epic 소속 화면의 최소 order다. 총 파일 수 = 2 + (화면 있는 epic 수).
337
+ - `ch handoff screen <화면id>` — 화면 하나의 카드 마크다운을 stdout으로 낸다. **웹 화면 상세 [이 화면 프롬프트 복사]와 같은 내용**이다. 없는 화면 id면 오류로 끝난다.
338
+ - **기본 폴더**: `--dir` 기본값은 `design-handoff`(눈에 보이는 폴더)다. backup의 `.babelai`와 달리 숨기지 않는다 — 디자이너가 열어 `.md`를 드래그하니까.
339
+ - **폴더 안전**: `--dir`은 cwd 하위 전용 폴더만 받는다(절대경로·`..`·symlink 거부). 임시 폴더에 먼저 쓰고 원자적으로 교체하므로 쓰다 실패해도 기존 번들이 보존된다. 같은 핸드오프 폴더로의 재export(화면이 바뀔 때마다)는 허용된다.
340
+ - **출력 계약**: `--json`이면 `export`는 요약 JSON(`{screenCount, dir, files:[{path}]}`), `screen`은 서버 응답 `{content, screenName, screenCount}`를 재명명 없이 그대로 stdout으로 낸다. 사람용 메시지는 stderr, 실패는 종료코드 ≠ 0.
341
+ - **dry-run**: `export --dry-run`은 서버를 호출하지 않고 대상 폴더만 안내한다. `screen`은 읽기 전용이라 `--dry-run` 대상이 아니다.
342
+ - **레거시 가드**: `screensEnabled` 미설정(화면 0개) 프로젝트면 `screenCount:0`으로 **안내 후 중단**한다. 빈 폴더·빈 파일·크래시를 만들지 않는다.
343
+ - 화면 카드에 담기는 것: 이름·route·접근권한 · 목적 · 화면 상태 · 진입/이탈 · 핵심 액션 · 필수 표시 정보 · 요소별 인터랙션. 한 화면에 명세가 여럿이면 `### 출처: <specId>`로 묶는다. 색·폰트·톤·DB 컬럼·구현 경로는 **일부러 뺀다**(디자인 입력에 불필요).
344
+ - **정본 분리**: 이 섹션이 **명령 정본**(에이전트·개발자용)이다. 디자이너용 단계별 절차서(비전공자 온보딩)는 `docs/design-handoff-sop.md`가 정본이다. 절차를 여기서 중복 서술하지 않는다.
345
+
327
346
  ## 4. 운영 팁
328
347
 
329
348
  - Windows 한글 출력이 비어 보일 때: `ch specs get <id> --json | Out-File spec.json` (PowerShell)
@@ -8,7 +8,7 @@ description: |
8
8
  (2) DB 테이블 단위로 컬럼·인덱스·보안규칙을 등록·수정할 때,
9
9
  (3) 새 테이블을 추가하면서 전체 인벤토리도 함께 업데이트할 때,
10
10
  (4) spec.dbTableRefs와 양방향 동기화가 필요한 작업 시.
11
- version: 1.21.0
11
+ version: 1.23.0
12
12
  ---
13
13
 
14
14
  <!-- ch-version-gate -->
@@ -2,7 +2,7 @@
2
2
  name: 2t-decencia-channel-github-issue
3
3
  description: gh CLI로 현재 repo에 GitHub 이슈를 작성한다. 이슈 1개=유형 1개(feat/fix/bug), 표준 본문 양식(작업내용·관련 명세 백링크·완료조건)으로 발행. "이슈 만들어줘"/"깃헙 이슈로 등록해줘" 요청 시, 또는 소통채널 기획·명세 작업 후 처리할 일들을 GitHub 이슈로 옮길 때 사용. 멱등성/중복방지·확인단계는 다루지 않음(요청대로 바로 생성).
4
4
  allowed-tools: Bash(gh:*) Bash(git:*) Read Write
5
- version: 1.21.0
5
+ version: 1.23.0
6
6
  ---
7
7
 
8
8
  <!-- ch-version-gate -->
@@ -2,7 +2,7 @@
2
2
  name: 2t-decencia-channel-orchestrator
3
3
  description: 소통채널 작업의 단일 진입점(디스패처). 사용자 요청을 듣고 7종(bootstrap·terraformer·change-manager·github-issue·issue-coder·pr-merger·sprint-runner) 중 적절한 곳으로 라우팅하고, 필요하면 여러 단계를 순차 오케스트레이션한다. 대화형 스킬은 메인 세션에서 Skill로, 자율 에이전트는 Agent로 위임. "소통채널 작업 해줘"·"이거 어떻게 처리하지?"처럼 무엇부터 할지 모를 때, 또는 신규기획/코드편입/기획변경/이슈/구현/머지/스프린트실행 어디로든 시작할 때.
4
4
  allowed-tools: Skill Agent Read Bash(ch:*) Bash(gh:*) Bash(git:*)
5
- version: 1.21.0
5
+ version: 1.23.0
6
6
  ---
7
7
 
8
8
  <!-- ch-version-gate -->
@@ -58,6 +58,7 @@ ch check
58
58
  - "이 레포 소통채널에 올리고 스프린트도 짜줘" → 2 terraformer(Agent) → 끝나면 `2t-decencia-channel-sprint-builder-v2`(Skill).
59
59
  - "로그인에 SNS 추가하고 개발까지" → 3 change-manager(Skill, 전파+이슈 발행) → 발행된 이슈로 5 issue-coder(Agent, 병렬) → PR 나오면 6 pr-merger(Agent, 직렬).
60
60
  - "스프린트 N 개발 진행해줘"(한 Sprint 통째) → **7 sprint-runner**(Skill). 4(발행)·5(구현)·6(머지)를 **내부에서 묶어** 돌리고 사람확인에서 멈춘다 → 수동으로 4→5→6 나열하지 말고 7로 보낸다.
61
+ - "화면까지 다 짰고 이제 디자인 넘긴다" → **라우트가 아니다.** 디자이너가 웹 화면 탭 [통합 핸드오프 zip 받기] 또는 `ch handoff export --dir`로 화면 기획 묶음을 받아 Claude Design에 넣는다(절차 `docs/design-handoff-sop.md`, 명령 정본 [[2t-decencia-channel-cli-v2]]). 읽기전용 export라 위임할 대화형 스킬·자율 에이전트가 없다 — 이 단계는 **안내만** 한다.
61
62
  - 한 단계의 산출물(projectId·이슈번호·PR번호)을 다음 단계 입력으로 넘긴다.
62
63
 
63
64
  ## 경계/주의
@@ -7,7 +7,7 @@ description: |
7
7
  (1) PRD를 작성하거나 갱신할 때,
8
8
  (2) PRD 구성에서 Epic/스토리 구조를 후속 spec 작성에 매핑할 때,
9
9
  (3) Read-before-Write 규칙으로 PRD content를 안전하게 수정해야 할 때.
10
- version: 1.21.0
10
+ version: 1.23.0
11
11
  ---
12
12
 
13
13
  <!-- ch-version-gate -->
@@ -2,7 +2,7 @@
2
2
  name: 2t-decencia-channel-project-bootstrap
3
3
  description: 신규 기획을 사용자와 대화하며 step-by-step으로 소통채널 프로젝트로 만드는 오케스트레이터 스킬. (1)대화로 PRD 완성 → (2)기능명세(spec) 내용 확정 → (3)DB 스키마 → (4)spec 업로드 → (5)화면 구성·연결 → (6)SQA 시트. 각 단계 사용자 승인 게이트, 작성 디테일은 기존 2t-decencia-channel-*-v2 스킬을 적극 재사용. "새 기획 만들어줘"·"프로젝트 처음부터 세팅" 류 요청 시.
4
4
  allowed-tools: Bash(ch:*) Bash(git:*) Read Write
5
- version: 1.21.0
5
+ version: 1.23.0
6
6
  ---
7
7
 
8
8
  <!-- ch-version-gate -->
@@ -199,6 +199,9 @@ ch specs screen-refs PROJ-002 --screens SCR-PROJECTS,SCR-PROJECT-DETAIL # 화
199
199
  *(화면 연결 후에만)*
200
200
  - `2t-decencia-channel-sqa-v2`로 SQA 작성: **A축**(명세별 기능 TC — 영역별 번들에서 도출) + **B축**(비기능 표준 40).
201
201
 
202
+ ### 디자인 핸드오프로 이어가기 (안내만)
203
+ 화면이 다 짜였으면 **디자인 핸드오프**로 넘어갈 수 있다. 디자이너가 웹 화면 탭 **[통합 핸드오프 zip 받기]** 또는 `ch handoff export --dir <폴더>`로 화면 기획 묶음을 받아 Claude Design에 넣는다. 절차는 `docs/design-handoff-sop.md`. 읽기전용 export라 이 스킬이 대신 실행하지 않는다 — 화면이 막 만들어진 이 지점에서 **사용자에게 안내만** 한다. (명령 상세는 [[2t-decencia-channel-cli-v2]] handoff 명령.)
204
+
202
205
  ---
203
206
 
204
207
  ## 업로드 주의 (전 단계 공통)
@@ -11,7 +11,7 @@ description: |
11
11
  (2) UI 메타데이터·비즈니스 로직·DB 참조를 함께 다룰 때,
12
12
  (3) Story 규모를 Story Point(`--points`)로 매길 때,
13
13
  (4) 신규 spec ID를 `LOGIN-001` 같은 JIRA 스타일로 부여하고 싶을 때 (`ch specs create --id`).
14
- version: 1.21.0
14
+ version: 1.23.0
15
15
  ---
16
16
 
17
17
  <!-- ch-version-gate -->
@@ -7,7 +7,7 @@ description: |
7
7
  (1) sprint를 구성할 때,
8
8
  (2) Σ spec.points 기반 sprint 용량 산정이 필요할 때,
9
9
  (3) Epic·도메인·의존성을 함께 고려해 spec을 sprint에 배분할 때.
10
- version: 1.21.0
10
+ version: 1.23.0
11
11
  ---
12
12
 
13
13
  <!-- ch-version-gate -->
@@ -2,7 +2,7 @@
2
2
  name: 2t-decencia-channel-sprint-runner
3
3
  description: 특정 Sprint를 끝까지 실행(run)하는 오케스트레이터 스킬. Sprint의 spec을 시드로 이슈 목록을 분해·발행(github-issue)하고, GitHub milestone에 묶인 이슈를 상태별로 분류해 issue-coder(병렬)·pr-merger(직렬)로 전진시킨 뒤, 사람확인이 필요한 곳에서 멈춘다. 사람이 확인·체크 후 재호출하면 멱등하게 이어간다(반복 루프). "스프린트 돌려줘"·"sprint N 실행"·"이 스프린트 개발 진행해줘" 류 요청 시.
4
4
  allowed-tools: Skill Agent Read Bash(ch:*) Bash(gh:*) Bash(git:*)
5
- version: 1.21.0
5
+ version: 1.23.0
6
6
  ---
7
7
 
8
8
  <!-- ch-version-gate -->
@@ -12,7 +12,7 @@ description: |
12
12
  (4) 명세 영역별 완성형 TC 번들(퍼블리싱/프론트/백엔드 디센시아 운영 관점 포함)을 가져다 쓸 때,
13
13
  (5) 새 명세 작업 중 반복 패턴을 발견해 영역별 번들을 보강할 때,
14
14
  (6) Sprint 종료 전 spec별 SQA 통과 여부를 점검할 때.
15
- version: 1.21.0
15
+ version: 1.23.0
16
16
  ---
17
17
 
18
18
  <!-- ch-version-gate -->
@@ -8,7 +8,7 @@ description: |
8
8
  (2) "작업 현황 적어줘", "workStatus 갱신해줘" 요청 시,
9
9
  (3) Sprint 종료 회고나 인수인계용으로 spec별 진행 상태를 정리할 때,
10
10
  (4) `ch specs update --work-status` 또는 웹 "작업 현황" 탭에서 갱신할 때.
11
- version: 1.21.0
11
+ version: 1.23.0
12
12
  ---
13
13
 
14
14
  <!-- ch-version-gate -->