@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 +22 -226
- package/package.json +2 -3
- package/scripts/init.mjs +232 -57
- package/scripts/validate.mjs +6 -1
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
|
|
12
|
+
pnpm dlx --package=@dusky-bluehour/agent-service@latest tri-agent-manager --interactive
|
|
161
13
|
```
|
|
162
14
|
|
|
163
|
-
|
|
15
|
+
## 패키지 다운로드 후 사용
|
|
164
16
|
|
|
165
17
|
```bash
|
|
166
|
-
|
|
167
|
-
|
|
18
|
+
# 프로젝트에 패키지 설치
|
|
19
|
+
pnpm add -D @dusky-bluehour/agent-service@latest
|
|
168
20
|
|
|
169
|
-
|
|
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
|
-
|
|
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
|
-
|
|
194
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
211
|
-
pnpm dlx --package=@dusky-bluehour/agent-service tri-agent-manager
|
|
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
|
-
|
|
235
|
-
|
|
38
|
+
# 도움말
|
|
39
|
+
pnpm dlx --package=@dusky-bluehour/agent-service@latest tri-agent-manager --help
|
|
236
40
|
```
|
|
237
41
|
|
|
238
|
-
|
|
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
|
-
-
|
|
249
|
-
-
|
|
250
|
-
-
|
|
251
|
-
-
|
|
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
|
+
"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
|
-
|
|
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
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
console.log(
|
|
1166
|
-
|
|
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(`
|
|
1266
|
+
console.log(` 경로 기준 : ${tool.install_root_basis}`);
|
|
1170
1267
|
}
|
|
1171
1268
|
if (installReferences.length > 0) {
|
|
1172
|
-
console.log(
|
|
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
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
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
|
-
|
|
1295
|
-
|
|
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(
|
|
1317
|
-
console.log(
|
|
1318
|
-
|
|
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(
|
|
1322
|
-
console.log(
|
|
1323
|
-
console.log(`
|
|
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(`
|
|
1482
|
+
console.log(` 경로 기준 : ${tool.install_root_basis}`);
|
|
1326
1483
|
}
|
|
1327
1484
|
if (installReferences.length > 0) {
|
|
1328
|
-
console.log(
|
|
1485
|
+
console.log(' 참고 문서 :');
|
|
1486
|
+
installReferences.forEach((ref) => {
|
|
1487
|
+
console.log(` - ${ref}`);
|
|
1488
|
+
});
|
|
1329
1489
|
}
|
|
1330
|
-
console.log('
|
|
1331
|
-
|
|
1490
|
+
console.log(' 구성요소 :');
|
|
1491
|
+
tool.components.forEach((component) => {
|
|
1332
1492
|
console.log(
|
|
1333
|
-
`
|
|
1493
|
+
` - ${component.title} (${component.id}) -> ${path.join(
|
|
1334
1494
|
installRoot,
|
|
1335
1495
|
getComponentInstallPath(component)
|
|
1336
1496
|
)}`
|
|
1337
1497
|
);
|
|
1338
|
-
|
|
1339
|
-
|
|
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
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
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('권장
|
|
1353
|
-
console.log(
|
|
1354
|
-
console.log('2) 설치 후 state.json
|
|
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
|
-
|
|
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) => {
|
package/scripts/validate.mjs
CHANGED
|
@@ -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)) {
|