@dusky-bluehour/agent-service 0.6.3 → 0.6.5

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/README.md CHANGED
@@ -1,251 +1,47 @@
1
1
  # tri-agent-manager
2
2
 
3
- Claude Code, Antigravity, Codex에서 공통으로 사용하실 수 있는 서비스 운영형 스킬/워크플로우 패키지입니다.
4
-
5
- ## 목적
6
-
7
- - 기획(PRD) → 설계 → 개발 → 코드리뷰/보안 → 배포/운영까지 동일한 품질 기준으로 실행하실 수 있습니다.
8
- - 반복 작업(UI 컴포넌트화, Hook 분리, 최적화)을 역할/명령 단위로 고정해 일관성을 높입니다.
9
- - 툴이 달라도 동일한 계약(명령 ID, 품질 게이트, 워크플로우)으로 결과를 맞출 수 있습니다.
10
-
11
- ## 용어 설명
12
-
13
- | 용어 | 의미 |
14
- | --- | --- |
15
- | Skill | 작업 방식/규칙을 담은 실행 단위입니다. 툴마다 포맷은 다르지만 역할은 같습니다. |
16
- | Workflow | 단계형 실행 시나리오입니다. 각 단계는 진입/종료 조건을 가집니다. |
17
- | Command Contract | 입력/실행/품질게이트/산출물을 고정한 명령 계약입니다. |
18
- | Agent / Subagent | 역할별 실행 주체입니다. 리드가 전문 역할에 위임할 수 있습니다. |
19
- | Artifact (Antigravity) | 단계 입출력 계약 문서입니다. 스키마 검증 통과 후 다음 단계로 진행합니다. |
20
- | PRD→구현 파이프라인 | PRD는 대화형으로 작성하고, 구현은 최종 수동 세팅 전까지 순차 자동 실행합니다. |
21
-
22
- ## 핵심 기능
23
-
24
- | 기능 | 설명 | 관련 워크플로우 |
25
- | --- | --- | --- |
26
- | 표준 E2E 딜리버리 | 기획부터 배포 준비까지 표준 단계를 실행합니다. | `WF-SVC-001` |
27
- | 코드리뷰/개선 루프 | 리뷰 지적 → 수정 → 재검증 루프를 수행합니다. | `WF-SVC-003` / `WF-SVC-002` |
28
- | 보안 하드닝 | 위협 모델링 + 취약점 감사 + 재검증을 수행합니다. | `WF-SVC-004` / `WF-SVC-003` |
29
- | 반복 프론트 개선 | 컴포넌트화, Hook 분리, 성능 최적화를 진행합니다. | `WF-SVC-006` / `WF-SVC-002` |
30
- | 장애 대응 | 복구, RCA, 재발 방지를 수행합니다. | `WF-SVC-007` / `WF-SVC-005` |
31
- | PRD→구현 자동 파이프라인 | PRD 대화형 작성 후 구현을 자동 순차 실행합니다. | `WF-PRD-IMPLEMENT-E2E` |
32
-
33
- ## 목록 (토글)
34
-
35
- <details>
36
- <summary><strong>Claude Code - Skills</strong></summary>
37
-
38
- | Skill | 목적 |
39
- | --- | --- |
40
- | `service-lifecycle-orchestration` | 기획~배포 전체 수명주기를 실행합니다. |
41
- | `code-review-and-improvement` | 리뷰/개선 루프를 실행합니다. |
42
- | `security-hardening` | 위협/취약점 기반 보안 강화를 수행합니다. |
43
- | `frontend-repetition-pack` | UI 컴포넌트화/Hook/최적화를 수행합니다. |
44
- | `release-and-operations` | 배포/모니터링/인수인계를 수행합니다. |
45
- | `incident-response` | 장애 대응~재발 방지를 수행합니다. |
46
- | `prd-to-production-pipeline` | PRD 대화형 작성 후 순차 자동 구현을 수행합니다. |
47
-
48
- </details>
49
-
50
- <details>
51
- <summary><strong>Claude Code - Subagents</strong></summary>
52
-
53
- | Subagent | 역할 |
54
- | --- | --- |
55
- | `lead-orchestrator` | 전체 단계를 오케스트레이션합니다. |
56
- | `prd-writer` | 마스터/세부 PRD를 작성합니다. |
57
- | `product-planner` | 요구사항 잠금/범위 관리를 수행합니다. |
58
- | `solution-architect` | ADR/기술 경계를 정의합니다. |
59
- | `backend-engineer` | 계약 우선 API 구현을 수행합니다. |
60
- | `frontend-engineer` | 프론트 기능 구현을 수행합니다. |
61
- | `ui-component-engineer` | 반복 UI를 컴포넌트화합니다. |
62
- | `hook-refactor-engineer` | Hook 분리/정규화를 수행합니다. |
63
- | `performance-engineer` | 성능 병목을 개선합니다. |
64
- | `qa-engineer` | 테스트/회귀 차단을 수행합니다. |
65
- | `code-reviewer` | 코드리뷰/개선안을 도출합니다. |
66
- | `security-engineer` | 위협/취약점 대응을 수행합니다. |
67
- | `sre-release-engineer` | CI/CD 게이트/배포를 수행합니다. |
68
- | `operations-owner` | 운영 대시보드/런북을 관리합니다. |
69
- | `incident-commander` | 장애 워룸/복구 우선순위를 관리합니다. |
70
-
71
- </details>
72
-
73
- <details>
74
- <summary><strong>Antigravity - Skills / Agents</strong></summary>
75
-
76
- | 타입 | ID | 설명 |
77
- | --- | --- | --- |
78
- | Skill | `service-lifecycle-orchestration` | Plan-Implement-Test-Review-Release를 실행합니다. |
79
- | Skill | `code-review-and-improvement` | 리뷰/개선 루프를 실행합니다. |
80
- | Skill | `security-hardening` | 보안 하드닝을 수행합니다. |
81
- | Skill | `frontend-repetition-pack` | 프론트 반복 개선을 수행합니다. |
82
- | Skill | `release-and-operations` | 배포/운영 전환을 수행합니다. |
83
- | Skill | `incident-response` | 장애 대응을 수행합니다. |
84
- | Skill | `prd-to-production-pipeline` | PRD 대화형→자동 구현을 수행합니다. |
85
- | Agent | `manager-agent` | 기본 오케스트레이터 역할을 수행합니다. |
86
- | Agent | `prd-writer-agent` | PRD 대화형 작성을 수행합니다. |
87
- | Agent | `implementation-agent` | 구현 자동 실행을 수행합니다. |
88
- | Agent | `review-security-agent` | 리뷰/보안 검증을 수행합니다. |
89
- | Agent | `release-ops-agent` | 최종 세팅/배포를 수행합니다. |
90
-
91
- </details>
92
-
93
- <details>
94
- <summary><strong>Codex - Skills</strong></summary>
95
-
96
- | Skill | 목적 |
97
- | --- | --- |
98
- | `service-lifecycle-orchestration` | 기획~배포 표준 실행을 수행합니다. |
99
- | `code-review-and-improvement` | 리뷰/개선 루프를 수행합니다. |
100
- | `security-hardening` | 보안 강화를 수행합니다. |
101
- | `frontend-repetition-pack` | UI/Hook/성능 개선을 수행합니다. |
102
- | `release-and-operations` | 릴리즈/운영 전환을 수행합니다. |
103
- | `incident-response` | 장애 대응을 수행합니다. |
104
- | `prd-to-production-pipeline` | PRD 대화형→순차 자동 구현을 수행합니다. |
105
-
106
- </details>
107
-
108
- ## PRD→구현 자동 파이프라인 (신규)
109
-
110
- - 워크플로우: `WF-PRD-IMPLEMENT-E2E`
111
- - 단계:
112
- 1. 마스터 PRD 작성 (`CMD-PLAN-PRD-MASTER`) - 대화형
113
- 2. 세부 PRD 분해 (`CMD-PLAN-PRD-DETAILS`) - 대화형
114
- 3. 구현 부트스트랩 (`CMD-PLAN-IMPLEMENTATION-BOOTSTRAP`) - 대화형
115
- 4. 순차 자동 구현 (`CMD-DEV-SEQUENTIAL-AUTORUN`) - 자동
116
- 5. 검증/보안 - 자동
117
- 6. 최종 세팅/배포 준비 - 수동
118
-
119
- PRD 단계에서는 UI 컴포넌트화, 톤앤매너, 성능, 보안, 가독성 기준을 명시하시고, 자동 단계에서는 배치별 검증을 통과해야 다음 배치로 진행됩니다.
120
-
121
- ## 설치/업데이트 사용법
122
-
123
- ### 가장 쉬운 시작 (pnpm dlx, 전부 대화형)
124
-
125
3
  ```bash
126
- pnpm dlx tri-agent-manager --interactive
4
+ pnpm dlx --package=@dusky-bluehour/agent-service@latest tri-agent-manager --interactive
127
5
  ```
128
6
 
129
- 줄로 아래를 터미널에서 순서대로 선택하실 있습니다.
130
-
131
- 1. 설치 경로 (현재 위치 또는 직접 입력)
132
- 2. 프리셋
133
- 3. 도구(Claude Code/Antigravity/Codex)
134
- 4. 구성요소(스킬, 워크플로우 등)
135
- 5. 실행 확인
136
-
137
- 도구별 실제 설치 루트:
138
-
139
- - Claude Code: `<target>/.claude/*`
140
- - Antigravity: `<target>/.agent/*`
141
- - Codex: `<target>/.codex/*`
142
- - 설치 직후 사용 가이드 자동 생성: `<target>/.tri-agent-manager/USAGE.ko.md`
143
-
144
- 설치 경로 기준(근거 문서):
145
-
146
- - Claude Code: `claude-code/README.md` + Claude Code Subagents 공식 문서
147
- - Antigravity: `antigravity/README.md` 기준의 로컬 워크스페이스 규약(`.agent/*`)
148
- - Codex: `codex/README.md` + Codex 공식 문서
149
-
150
- 조작 키:
7
+ `@dusky-bluehour/agent-service` 패키지로 Claude Code / Antigravity / Codex 운영팩을 설치/업데이트하는 CLI입니다.
151
8
 
152
- - `↑/↓`: 이동
153
- - `←/→`: 해제/선택 또는 항목 이동
154
- - `Space`: 다중 선택 토글
155
- - `Enter`: 확정
156
-
157
- 업데이트도 동일하게 대화형으로 진행하실 수 있습니다.
9
+ ## 빠른 실행 (설치 없이)
158
10
 
159
11
  ```bash
160
- pnpm dlx tri-agent-manager update --interactive
12
+ pnpm dlx --package=@dusky-bluehour/agent-service@latest tri-agent-manager --interactive
161
13
  ```
162
14
 
163
- `setup` 별칭도 동일하게 동작합니다.
15
+ ## 패키지 다운로드 후 사용
164
16
 
165
17
  ```bash
166
- pnpm dlx tri-agent-manager setup
167
- ```
18
+ # 프로젝트에 패키지 설치
19
+ pnpm add -D @dusky-bluehour/agent-service@latest
168
20
 
169
- ### npm 사용 시
170
-
171
- ```bash
172
- npx tri-agent-manager --interactive
173
- npx tri-agent-manager update --interactive
21
+ # 실행
22
+ pnpm exec tri-agent-manager --interactive
174
23
  ```
175
24
 
176
- 이전 CLI 별칭 `tri-agent-os`도 호환용으로 함께 제공됩니다.
177
-
178
- ### 고급 옵션(필요한 경우)
179
-
180
- - `--preset`: 미리 정의된 구성을 선택합니다.
181
- - `--tool`: 툴을 직접 선택합니다. (`codex,claude-code` 등)
182
- - `--components`: 구성요소를 직접 선택합니다. (`skills,workflows,commands` 등)
183
- - `--install-root`: 도구별 설치 루트를 직접 지정합니다. (`codex=.codex,antigravity=.agent`)
184
- - `--interactive`: 한글 대화형 선택 UI를 사용합니다.
185
- - `--dry-run`: 실제 반영 없이 계획만 확인합니다.
186
- - `--yes`: 확인 프롬프트를 생략합니다.
187
-
188
- 중복 선택 입력(예: `codex,codex,claude-code`)은 자동으로 1회로 정리됩니다.
189
-
190
- ### 다른 폴더에 설치하는 명령어
25
+ npm 사용자:
191
26
 
192
27
  ```bash
193
- # 1) 전체 프리셋을 다른 프로젝트 경로에 설치
194
- pnpm dlx --package=@dusky-bluehour/agent-service tri-agent-manager install \
195
- --preset full-suite \
196
- --target /path/to/your-project \
197
- --yes --non-interactive
28
+ npm install -D @dusky-bluehour/agent-service@latest
29
+ npx tri-agent-manager --interactive
198
30
  ```
199
31
 
200
- ```bash
201
- # 2) 특정 도구만 설치 (예: Antigravity)
202
- pnpm dlx --package=@dusky-bluehour/agent-service tri-agent-manager install \
203
- --tool antigravity \
204
- --components skills,agents,artifacts,workflows,commands \
205
- --target /path/to/your-project \
206
- --yes --non-interactive
207
- ```
32
+ ## 자주 쓰는 명령
208
33
 
209
34
  ```bash
210
- # 3) 도구별 설치 루트를 커스텀
211
- pnpm dlx --package=@dusky-bluehour/agent-service tri-agent-manager install \
212
- --preset balanced-core \
213
- --install-root codex=.codex,claude-code=.claude,antigravity=.agent \
214
- --target /path/to/your-project \
215
- --yes --non-interactive
216
- ```
217
-
218
- 설치 후 실행 가이드는 아래 파일에서 확인하실 수 있습니다.
219
-
220
- - `<target>/.tri-agent-manager/USAGE.ko.md`
221
-
222
- ## 프리셋
223
-
224
- - `balanced-core`: 기본 운영팩(추천)
225
- - `full-suite`: 전체 설치
226
- - `security-first`: 보안 우선
227
- - `frontend-refactor`: 프론트 반복개선
228
- - `prd-to-production`: PRD→구현 자동 파이프라인
229
-
230
- ## 체크리스트 자동화
231
-
232
- 배포 전 자동 점검:
35
+ # 업데이트 대화형
36
+ pnpm dlx --package=@dusky-bluehour/agent-service@latest tri-agent-manager update --interactive
233
37
 
234
- ```bash
235
- npm run prepublish:check
38
+ # 도움말
39
+ pnpm dlx --package=@dusky-bluehour/agent-service@latest tri-agent-manager --help
236
40
  ```
237
41
 
238
- `prepublishOnly`가 설정되어 있어 `npm publish` 시에도 자동 실행됩니다.
239
-
240
- ## 다음 단계 추천
241
-
242
- 1. `pnpm dlx tri-agent-manager --interactive`로 `prd-to-production` 프리셋을 먼저 설치해 보세요.
243
- 2. 첫 실행은 `--dry-run`으로 계획만 확인하신 뒤 실제 반영을 진행해 주세요.
244
- 3. 반영 후 `npm run prepublish:check`로 검증을 고정하신 뒤 배포해 주세요.
245
-
246
- ## 상세 구성 문서
42
+ ## 설치 결과
247
43
 
248
- - 구조/설계/계약 상세: `docs/COMPOSITION.ko.md`
249
- - 대화형 UX 흐름 그림: `docs/UX-FLOW.ko.md`
250
- - 배포 절차 가이드: `docs/DEPLOYMENT-GUIDE.ko.md`
251
- - 업데이트 절차 가이드: `docs/UPDATE-GUIDE.ko.md`
44
+ - Claude Code: `<target>/.claude/*`
45
+ - Antigravity: `<target>/.agent/*`
46
+ - Codex: `<target>/.codex/*`
47
+ - 가이드: `<target>/.tri-agent-manager/USAGE.ko.md`
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "@dusky-bluehour/agent-service",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
7
  "description": "Service operation skills/workflows pack for Claude Code, Antigravity, and Codex",
8
8
  "type": "module",
9
9
  "bin": {
10
- "tri-agent-manager": "scripts/init.mjs",
11
- "tri-agent-os": "scripts/init.mjs"
10
+ "tri-agent-manager": "scripts/init.mjs"
12
11
  },
13
12
  "scripts": {
14
13
  "validate": "node scripts/validate.mjs",
package/scripts/init.mjs CHANGED
@@ -13,7 +13,6 @@ const rootDir = path.resolve(__dirname, '..');
13
13
  const catalogPath = path.join(rootDir, 'catalog', 'tool-catalog.ko.json');
14
14
  const packageJsonPath = path.join(rootDir, 'package.json');
15
15
  const CLI_NAME = 'tri-agent-manager';
16
- const CLI_COMPAT_NAME = 'tri-agent-os';
17
16
  const STATE_DIR_NAME = '.tri-agent-manager';
18
17
  const LEGACY_STATE_DIR_NAME = '.tri-agent-os';
19
18
  const GUIDE_FILE_NAME = 'USAGE.ko.md';
@@ -42,14 +41,11 @@ const HELP_TEXT = [
42
41
  ' --non-interactive 비대화형 모드 강제',
43
42
  ' (중복 선택 입력은 자동으로 1회로 정리됨)',
44
43
  '',
45
- '호환 별칭:',
46
- ` ${CLI_COMPAT_NAME} ... (동일 동작)`,
47
- '',
48
44
  'pnpm 예시:',
49
- ' pnpm dlx tri-agent-manager --interactive',
50
- ' pnpm dlx tri-agent-manager update --interactive',
51
- ' pnpm dlx tri-agent-manager setup',
52
- ` pnpm exec ${CLI_NAME} list`
45
+ ' pnpm dlx --package=@dusky-bluehour/agent-service@latest tri-agent-manager --interactive',
46
+ ' pnpm dlx --package=@dusky-bluehour/agent-service@latest tri-agent-manager update --interactive',
47
+ ' pnpm dlx --package=@dusky-bluehour/agent-service@latest tri-agent-manager setup',
48
+ ' pnpm dlx --package=@dusky-bluehour/agent-service@latest tri-agent-manager list'
53
49
  ].join('\n');
54
50
 
55
51
  function parseArgs(argv) {
@@ -450,6 +446,52 @@ function getToolExecutionGuide(tool, installRoot) {
450
446
  return ['선택한 workflow의 stage.commands를 순서대로 실행합니다.'];
451
447
  }
452
448
 
449
+ function getToolInteractionTips(tool, installRoot) {
450
+ if (tool.id === 'codex') {
451
+ return [
452
+ 'Codex 대화 입력창에서 `$`를 누르면 사용 가능한 스킬 목록을 바로 열 수 있습니다.',
453
+ `프로젝트 루트에서 스킬 파일을 직접 확인하려면 \`ls ${path.join(
454
+ installRoot,
455
+ 'skills'
456
+ )}\` 를 실행하세요.`,
457
+ `워크플로우 목록과 설명은 \`${path.join(
458
+ installRoot,
459
+ 'workflows',
460
+ 'workflow-catalog.json'
461
+ )}\` 에서 확인하세요.`
462
+ ];
463
+ }
464
+
465
+ if (tool.id === 'claude-code') {
466
+ return [
467
+ `서브에이전트 이름은 \`${path.join(installRoot, 'agents')}\` 경로의 파일명 기준입니다.`,
468
+ `프로젝트 루트에서 \`ls ${path.join(
469
+ installRoot,
470
+ 'agents'
471
+ )} | sed 's/\\.md$//'\` 로 서브에이전트 이름을 확인하세요.`,
472
+ `Claude 스킬 목록은 \`ls ${path.join(installRoot, 'skills')}\` 로 확인하세요.`
473
+ ];
474
+ }
475
+
476
+ if (tool.id === 'antigravity') {
477
+ return [
478
+ `워크플로우 선택은 \`${path.join(
479
+ installRoot,
480
+ 'workflows',
481
+ 'workflow-catalog.json'
482
+ )}\` 에서 WF ID를 먼저 고르는 방식으로 진행하세요.`,
483
+ `에이전트 역할은 \`${path.join(installRoot, 'agents', 'agent-catalog.json')}\` 에서 확인하세요.`,
484
+ `입출력 아티팩트 스키마는 \`${path.join(
485
+ installRoot,
486
+ 'artifacts',
487
+ 'artifact-catalog.json'
488
+ )}\` 기준으로 맞추세요.`
489
+ ];
490
+ }
491
+
492
+ return ['도구별 workflow 문서와 skill 문서를 기준으로 실행 순서를 고정하세요.'];
493
+ }
494
+
453
495
  function renderMarkdownTable(headers, rows) {
454
496
  const head = `| ${headers.join(' | ')} |`;
455
497
  const divider = `| ${headers.map(() => '---').join(' | ')} |`;
@@ -508,6 +550,14 @@ async function writeUsageGuide({ catalog, selection, targetDir, mode }) {
508
550
  lines.push(`${index + 1}. ${step}`);
509
551
  });
510
552
 
553
+ lines.push('');
554
+ lines.push('### 도구별 실행 팁');
555
+ lines.push('아래 팁은 프로젝트 루트에서 바로 확인할 수 있는 방법입니다.');
556
+ const interactionTips = getToolInteractionTips(tool, installRoot);
557
+ interactionTips.forEach((tip, index) => {
558
+ lines.push(`${index + 1}. ${tip}`);
559
+ });
560
+
511
561
  if (workflowSummary.length > 0) {
512
562
  lines.push('');
513
563
  lines.push('### 워크플로우 선택 가이드');
@@ -1146,32 +1196,82 @@ function statusLabel(status) {
1146
1196
  }
1147
1197
  }
1148
1198
 
1149
- function printPlan(catalog, selection, mode, targetDir) {
1150
- console.log('');
1151
- console.log('실행 계획');
1152
- console.log(`- 모드: ${mode}`);
1153
- console.log(`- 프리셋: ${selection.presetId ?? '수동/없음'}`);
1154
- console.log(`- 대상 경로: ${targetDir}`);
1199
+ function statusIcon(status) {
1200
+ switch (status) {
1201
+ case 'copied':
1202
+ return '✅';
1203
+ case 'overwritten':
1204
+ return '♻️';
1205
+ case 'skipped':
1206
+ return '⏭️';
1207
+ case 'would-copy':
1208
+ return '📝';
1209
+ case 'would-overwrite':
1210
+ return '🛠️';
1211
+ default:
1212
+ return '•';
1213
+ }
1214
+ }
1155
1215
 
1156
- for (const toolId of selection.selectedToolIds) {
1216
+ function normalizeChoiceInput(value) {
1217
+ return value
1218
+ .trim()
1219
+ .toLowerCase()
1220
+ .replaceAll('1', '1')
1221
+ .replaceAll('2', '2')
1222
+ .replaceAll('3', '3')
1223
+ .replaceAll('4', '4')
1224
+ .replaceAll('5', '5');
1225
+ }
1226
+
1227
+ function printPlan(catalog, selection, mode, targetDir, dryRun = false) {
1228
+ console.log('');
1229
+ console.log('━━━━━━━━ 실행 계획 ━━━━━━━━');
1230
+ console.log(`모드 : ${mode}`);
1231
+ console.log(`실행 타입 : ${dryRun ? 'dry-run (실제 파일 변경 없음)' : '실행 (파일 변경 반영)'}`);
1232
+ console.log(`프리셋 : ${selection.presetId ?? '수동/없음'}`);
1233
+ console.log(`대상 경로 : ${targetDir}`);
1234
+ console.log(`선택 도구 : ${selection.selectedToolIds.length}개`);
1235
+
1236
+ selection.selectedToolIds.forEach((toolId, index) => {
1157
1237
  const tool = findTool(catalog, toolId);
1238
+ if (!tool) {
1239
+ return;
1240
+ }
1158
1241
  const installRoot = getEffectiveInstallRoot(tool, selection);
1159
1242
  const installReferences = getToolInstallReferences(tool);
1160
1243
  const componentIds = selection.componentSelection[toolId] ?? [];
1161
- const labels = tool.components
1162
- .filter((c) => componentIds.includes(c.id))
1163
- .map((c) => `${c.title}(${c.id} → ${path.join(installRoot, getComponentInstallPath(c))})`)
1164
- .join(', ');
1165
- console.log(
1166
- `- ${tool.title}(${tool.id}) [설치 루트: ${installRoot}]: ${labels || '선택된 항목 없음'}`
1167
- );
1244
+
1245
+ console.log('');
1246
+ console.log(`${index + 1}. ${tool.title} (${tool.id})`);
1247
+ console.log(` 설치 루트 : ${installRoot}`);
1248
+ console.log(' 구성요소 :');
1249
+
1250
+ if (componentIds.length === 0) {
1251
+ console.log(' - 선택된 항목 없음');
1252
+ } else {
1253
+ for (const componentId of componentIds) {
1254
+ const component = tool.components.find((candidate) => candidate.id === componentId);
1255
+ if (!component) continue;
1256
+ console.log(
1257
+ ` - ${component.title} (${component.id}) -> ${path.join(
1258
+ installRoot,
1259
+ getComponentInstallPath(component)
1260
+ )}`
1261
+ );
1262
+ }
1263
+ }
1264
+
1168
1265
  if (tool.install_root_basis) {
1169
- console.log(` 경로 기준: ${tool.install_root_basis}`);
1266
+ console.log(` 경로 기준 : ${tool.install_root_basis}`);
1170
1267
  }
1171
1268
  if (installReferences.length > 0) {
1172
- console.log(` 참고 문서: ${installReferences.join(', ')}`);
1269
+ console.log(' 참고 문서 :');
1270
+ installReferences.forEach((ref) => {
1271
+ console.log(` - ${ref}`);
1272
+ });
1173
1273
  }
1174
- }
1274
+ });
1175
1275
  }
1176
1276
 
1177
1277
  async function confirmProceed(yesFlag) {
@@ -1180,9 +1280,23 @@ async function confirmProceed(yesFlag) {
1180
1280
 
1181
1281
  const rl = createInterface({ input, output });
1182
1282
  try {
1183
- const answer = await rl.question('계속 진행할까요? (y/N)\n> ');
1184
- const normalized = answer.trim().toLowerCase();
1185
- return normalized === 'y' || normalized === 'yes';
1283
+ while (true) {
1284
+ console.log('');
1285
+ console.log('진행 여부를 선택해 주세요.');
1286
+ console.log('1) 진행');
1287
+ console.log('2) 취소 (기본값)');
1288
+ const answer = await rl.question('번호를 입력하세요. [기본: 2]\n> ');
1289
+ const normalized = normalizeChoiceInput(answer);
1290
+
1291
+ if (!normalized || normalized === '2' || normalized === '취소' || normalized === '아니오') {
1292
+ return false;
1293
+ }
1294
+ if (normalized === '1' || normalized === '진행' || normalized === '예') {
1295
+ return true;
1296
+ }
1297
+
1298
+ console.log('잘못된 입력입니다. 1 또는 2를 입력해 주세요.');
1299
+ }
1186
1300
  } finally {
1187
1301
  rl.close();
1188
1302
  }
@@ -1241,6 +1355,15 @@ async function writeState({ catalog, targetDir, packageData, mode, selection })
1241
1355
  await fs.writeFile(statePath, `${JSON.stringify(statePayload, null, 2)}\n`, 'utf8');
1242
1356
  }
1243
1357
 
1358
+ async function cleanupLegacyStateDir(targetDir) {
1359
+ const legacyDir = path.join(targetDir, LEGACY_STATE_DIR_NAME);
1360
+ if (!(await exists(legacyDir))) {
1361
+ return false;
1362
+ }
1363
+ await fs.rm(legacyDir, { recursive: true, force: true });
1364
+ return true;
1365
+ }
1366
+
1244
1367
  async function runInstallOrUpdate({ catalog, packageData, mode, selection, force, dryRun }) {
1245
1368
  const overwrite = mode === 'update' || force;
1246
1369
  const targetDir = selection.targetDir;
@@ -1289,12 +1412,40 @@ async function runInstallOrUpdate({ catalog, packageData, mode, selection, force
1289
1412
  }
1290
1413
 
1291
1414
  console.log('');
1292
- console.log('적용 결과');
1415
+ console.log('━━━━━━━━ 적용 결과 ━━━━━━━━');
1416
+ const summaryCounter = {
1417
+ copied: 0,
1418
+ overwritten: 0,
1419
+ skipped: 0,
1420
+ 'would-copy': 0,
1421
+ 'would-overwrite': 0
1422
+ };
1293
1423
  for (const item of results) {
1294
- console.log(
1295
- `- [${item.toolId}] ${item.source} -> ${item.destination}: ${statusLabel(item.status)}`
1296
- );
1424
+ if (item.status in summaryCounter) {
1425
+ summaryCounter[item.status] += 1;
1426
+ }
1427
+ }
1428
+
1429
+ const groupedResults = new Map();
1430
+ for (const item of results) {
1431
+ const list = groupedResults.get(item.toolId) ?? [];
1432
+ list.push(item);
1433
+ groupedResults.set(item.toolId, list);
1434
+ }
1435
+ for (const toolId of selection.selectedToolIds) {
1436
+ const items = groupedResults.get(toolId) ?? [];
1437
+ if (items.length === 0) continue;
1438
+ console.log(`[${toolId}]`);
1439
+ for (const item of items) {
1440
+ console.log(
1441
+ ` - ${statusIcon(item.status)} ${statusLabel(item.status)} | ${item.source} -> ${item.destination}`
1442
+ );
1443
+ }
1297
1444
  }
1445
+ console.log('');
1446
+ console.log(
1447
+ `요약: 복사 ${summaryCounter.copied} · 업데이트 ${summaryCounter.overwritten} · 건너뜀 ${summaryCounter.skipped} · 복사예정 ${summaryCounter['would-copy']} · 업데이트예정 ${summaryCounter['would-overwrite']}`
1448
+ );
1298
1449
 
1299
1450
  const usageGuidePath = path.join(targetDir, STATE_DIR_NAME, GUIDE_FILE_NAME);
1300
1451
  if (dryRun) {
@@ -1305,54 +1456,73 @@ async function runInstallOrUpdate({ catalog, packageData, mode, selection, force
1305
1456
  if (!dryRun) {
1306
1457
  const generatedGuidePath = await writeUsageGuide({ catalog, selection, targetDir, mode });
1307
1458
  await writeState({ catalog, targetDir, packageData, mode, selection });
1459
+ const legacyStateRemoved = await cleanupLegacyStateDir(targetDir);
1308
1460
  console.log('');
1309
1461
  console.log(`실행 가이드 저장: ${generatedGuidePath}`);
1310
1462
  console.log(`상태 파일 저장: ${path.join(targetDir, STATE_DIR_NAME, 'state.json')}`);
1463
+ if (legacyStateRemoved) {
1464
+ console.log(`레거시 상태 폴더 정리: ${path.join(targetDir, LEGACY_STATE_DIR_NAME)}`);
1465
+ }
1311
1466
  }
1312
1467
  }
1313
1468
 
1314
1469
  function printList(catalog) {
1315
1470
  console.log('');
1316
- console.log(`${CLI_NAME} 카탈로그 (한글)`);
1317
- console.log('');
1318
- for (const tool of catalog.tools) {
1471
+ console.log('━━━━━━━━ 도구 카탈로그 ━━━━━━━━');
1472
+ console.log(`CLI: ${CLI_NAME}`);
1473
+
1474
+ catalog.tools.forEach((tool, toolIndex) => {
1319
1475
  const installRoot = getToolInstallRoot(tool);
1320
1476
  const installReferences = getToolInstallReferences(tool);
1321
- console.log(`- ${tool.title} (${tool.id})`);
1322
- console.log(` 설명: ${tool.description}`);
1323
- console.log(` 설치 루트: ${installRoot}`);
1477
+ console.log('');
1478
+ console.log(`${toolIndex + 1}. ${tool.title} (${tool.id})`);
1479
+ console.log(` 설명 : ${tool.description}`);
1480
+ console.log(` 설치 루트 : ${installRoot}`);
1324
1481
  if (tool.install_root_basis) {
1325
- console.log(` 경로 기준: ${tool.install_root_basis}`);
1482
+ console.log(` 경로 기준 : ${tool.install_root_basis}`);
1326
1483
  }
1327
1484
  if (installReferences.length > 0) {
1328
- console.log(` 참고 문서: ${installReferences.join(', ')}`);
1485
+ console.log(' 참고 문서 :');
1486
+ installReferences.forEach((ref) => {
1487
+ console.log(` - ${ref}`);
1488
+ });
1329
1489
  }
1330
- console.log(' 구성요소:');
1331
- for (const component of tool.components) {
1490
+ console.log(' 구성요소 :');
1491
+ tool.components.forEach((component) => {
1332
1492
  console.log(
1333
- ` - ${component.title} (${component.id}): ${component.description} -> ${path.join(
1493
+ ` - ${component.title} (${component.id}) -> ${path.join(
1334
1494
  installRoot,
1335
1495
  getComponentInstallPath(component)
1336
1496
  )}`
1337
1497
  );
1338
- }
1339
- console.log('');
1340
- }
1498
+ if (component.description) {
1499
+ console.log(` ${component.description}`);
1500
+ }
1501
+ });
1502
+ });
1341
1503
 
1342
1504
  const presets = Array.isArray(catalog.presets) ? catalog.presets : [];
1343
1505
  if (presets.length > 0) {
1344
- console.log('프리셋 목록');
1345
- for (const preset of presets) {
1346
- console.log(`- ${preset.title} (${preset.id})`);
1347
- console.log(` 설명: ${preset.description}`);
1348
- }
1506
+ console.log('');
1507
+ console.log('━━━━━━━━ 프리셋 목록 ━━━━━━━━');
1508
+ presets.forEach((preset, index) => {
1509
+ const toolCount = Array.isArray(preset.tools) ? preset.tools.length : 0;
1510
+ console.log(`${index + 1}. ${preset.title} (${preset.id})`);
1511
+ console.log(` 설명 : ${preset.description}`);
1512
+ console.log(` 포함 도구 : ${toolCount}개 (${(preset.tools ?? []).join(', ')})`);
1513
+ });
1514
+ console.log('');
1515
+ console.log('프리셋 사용 예시:');
1516
+ console.log(
1517
+ ` ${CLI_NAME} install --preset balanced-core --target /path/to/project --yes --non-interactive`
1518
+ );
1349
1519
  console.log('');
1350
1520
  }
1351
1521
 
1352
- console.log('권장 UX 흐름');
1353
- console.log('1) pnpm dlx tri-agent-manager --interactive 로 시작합니다.');
1354
- console.log('2) 설치 후 state.json 기준으로 update를 수행한다.');
1355
- console.log('3) update 시 필요한 도구만 부분 갱신한다.');
1522
+ console.log('━━━━━━━━ 권장 흐름 ━━━━━━━━');
1523
+ console.log(`1) pnpm dlx ${CLI_NAME} --interactive`);
1524
+ console.log('2) 설치 후 .tri-agent-manager/state.json 기준으로 update');
1525
+ console.log('3) update 시 필요한 도구만 부분 갱신');
1356
1526
  }
1357
1527
 
1358
1528
  async function main() {
@@ -1427,7 +1597,7 @@ async function main() {
1427
1597
  })
1428
1598
  };
1429
1599
 
1430
- printPlan(catalog, selection, command, selection.targetDir);
1600
+ printPlan(catalog, selection, command, selection.targetDir, options.dryRun);
1431
1601
 
1432
1602
  const proceed = await confirmProceed(options.yes);
1433
1603
  if (!proceed) {
@@ -1445,7 +1615,12 @@ async function main() {
1445
1615
  });
1446
1616
 
1447
1617
  console.log('');
1448
- console.log('완료: 선택한 운영팩 반영이 끝났습니다.');
1618
+ if (options.dryRun) {
1619
+ console.log('완료: dry-run 계획 확인이 끝났습니다. 실제 반영하려면 --dry-run 없이 다시 실행해 주세요.');
1620
+ } else {
1621
+ console.log('완료: 선택한 운영팩 반영이 끝났습니다.');
1622
+ console.log(`가이드 확인: ${path.join(selection.targetDir, STATE_DIR_NAME, GUIDE_FILE_NAME)}`);
1623
+ }
1449
1624
  }
1450
1625
 
1451
1626
  main().catch((error) => {
@@ -210,6 +210,10 @@ async function validatePackageJson() {
210
210
  }
211
211
  }
212
212
 
213
+ if (!pkg.bin || pkg.bin['tri-agent-manager'] !== 'scripts/init.mjs') {
214
+ fail('[package] bin 설정 오류: tri-agent-manager -> scripts/init.mjs 가 필요합니다.');
215
+ }
216
+
213
217
  const requiredFiles = [
214
218
  'claude-code',
215
219
  'antigravity',
@@ -228,7 +232,8 @@ async function validatePackageJson() {
228
232
  'docs/COMPOSITION.ko.md',
229
233
  'docs/DEPLOYMENT-GUIDE.ko.md',
230
234
  'docs/UPDATE-GUIDE.ko.md',
231
- 'docs/UX-FLOW.ko.md'
235
+ 'docs/UX-FLOW.ko.md',
236
+ 'docs/NPM-PUBLISH-REPO-ONLY.ko.md'
232
237
  ];
233
238
  for (const fileEntry of excludedFromPublish) {
234
239
  if (Array.isArray(pkg.files) && pkg.files.includes(fileEntry)) {