relayax-cli 0.2.25 → 0.2.27

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.
@@ -7,6 +7,8 @@ exports.BUILDER_COMMANDS = exports.USER_COMMANDS = void 0;
7
7
  exports.createAdapter = createAdapter;
8
8
  exports.getGlobalCommandPath = getGlobalCommandPath;
9
9
  exports.getGlobalCommandDir = getGlobalCommandDir;
10
+ exports.getGlobalCommandDirForTool = getGlobalCommandDirForTool;
11
+ exports.getGlobalCommandPathForTool = getGlobalCommandPathForTool;
10
12
  exports.formatCommandFile = formatCommandFile;
11
13
  const os_1 = __importDefault(require("os"));
12
14
  const path_1 = __importDefault(require("path"));
@@ -24,18 +26,31 @@ function createAdapter(tool) {
24
26
  };
25
27
  }
26
28
  /**
27
- * 글로벌 슬래시 커맨드 파일 경로.
29
+ * 글로벌 슬래시 커맨드 파일 경로 (Claude Code 기본).
28
30
  * ~/.claude/commands/relay/{id}.md
29
31
  */
30
32
  function getGlobalCommandPath(commandId) {
31
33
  return path_1.default.join(os_1.default.homedir(), '.claude', 'commands', 'relay', `${commandId}.md`);
32
34
  }
33
35
  /**
34
- * 글로벌 슬래시 커맨드 디렉토리.
36
+ * 글로벌 슬래시 커맨드 디렉토리 (Claude Code 기본).
35
37
  */
36
38
  function getGlobalCommandDir() {
37
39
  return path_1.default.join(os_1.default.homedir(), '.claude', 'commands', 'relay');
38
40
  }
41
+ /**
42
+ * 특정 AI 도구의 글로벌 커맨드 디렉토리.
43
+ * ~/{skillsDir}/commands/relay/
44
+ */
45
+ function getGlobalCommandDirForTool(skillsDir) {
46
+ return path_1.default.join(os_1.default.homedir(), skillsDir, 'commands', 'relay');
47
+ }
48
+ /**
49
+ * 특정 AI 도구의 글로벌 커맨드 파일 경로.
50
+ */
51
+ function getGlobalCommandPathForTool(skillsDir, commandId) {
52
+ return path_1.default.join(os_1.default.homedir(), skillsDir, 'commands', 'relay', `${commandId}.md`);
53
+ }
39
54
  /**
40
55
  * 커맨드 콘텐츠를 파일 형식으로 포맷.
41
56
  */
@@ -52,54 +67,114 @@ const LOGIN_JIT_GUIDE = `
52
67
  // ─── 명함 표시 포맷 ───
53
68
  const BUSINESS_CARD_FORMAT = `
54
69
  ### 빌더 명함 표시
55
- CLI 출력에 포함된 빌더 명함 정보를 반드시 아래 예시와 동일한 형태로 출력합니다.
70
+ JSON 결과의 \`author\`, \`welcome\` 필드를 사용하여 명함을 표시합니다.
56
71
  불릿 리스트(- 또는 *)로 나열하지 마세요. 반드시 인용 블록(>) 안에 넣어야 합니다.
57
72
 
73
+ **JSON 결과에서 사용할 필드:**
74
+ - \`author.display_name\` 또는 \`author.username\` → 명함 제목
75
+ - \`welcome\` → 환영 메시지 (💬)
76
+ - \`author.contact_links\` → 연락처 배열 (\`[{type, label, value}]\`)
77
+ - \`author.username\` → 프로필 링크 (👤)
78
+
58
79
  **예시 (이 형태를 그대로 따르세요):**
59
80
 
60
- > **🪪 devhaemin의 명함**
81
+ JSON 결과 예시:
82
+ \`\`\`json
83
+ {
84
+ "author": { "username": "alice", "display_name": "Alice Kim", "contact_links": [
85
+ {"type": "email", "label": "이메일", "value": "alice@example.com"},
86
+ {"type": "website", "label": "블로그", "value": "https://alice.dev"},
87
+ {"type": "kakao", "label": "카카오", "value": "https://open.kakao.com/o/abc123"}
88
+ ]},
89
+ "welcome": "안녕하세요!\\n에이전트 빌더 Alice입니다.\\n설치해주셔서 감사합니다."
90
+ }
91
+ \`\`\`
92
+
93
+ 출력:
94
+
95
+ > **🪪 Alice Kim의 명함**
61
96
  >
62
- > 💬 "안녕하세요!"
97
+ > 💬 "안녕하세요!
98
+ > 에이전트 빌더 Alice입니다.
99
+ > 설치해주셔서 감사합니다."
63
100
  >
64
- > 📧 haemin@musibe.com
65
- > 👤 relayax.com/@devhaemin
101
+ > 📧 alice@example.com
102
+ > 🔗 블로그: alice.dev
103
+ > 💬 카카오: open.kakao.com/o/abc123
104
+ > 👤 relayax.com/@alice
66
105
 
67
- - 환영 메시지가 없으면 💬 줄을 생략합니다.
106
+ - \`welcome\`이 없으면 💬 줄을 생략합니다.
107
+ - 연락처의 type에 맞는 이모지: 📧 email, 💬 kakao, 🐦 x, 💼 linkedin, 💻 github, 🔗 website/custom
68
108
  - 연락처가 여러 개면 각각 한 줄씩 표시합니다.
69
- - 명함이 비어있으면 명함 블록 전체를 생략합니다.`;
109
+ - \`author\`가 null이면 명함 블록 전체를 생략합니다.`;
70
110
  // ─── User Commands (글로벌 설치) ───
71
111
  exports.USER_COMMANDS = [
72
112
  {
73
- id: 'relay-explore',
74
- description: 'relay 마켓플레이스를 탐색하고 프로젝트에 맞는 팀을 찾습니다',
75
- body: `사용자의 요청이나 현재 프로젝트 맥락에 맞는 에이전트 팀을 relay 마켓플레이스에서 탐색합니다.
113
+ id: 'relay-install',
114
+ description: 'relay 마켓플레이스에서 에이전트 팀을 설치합니다',
115
+ body: `요청된 에이전트 팀을 relay 마켓플레이스에서 다운로드하고, 현재 에이전트 환경에 맞게 구성합니다.
116
+ 인자 없이 호출하면 인터랙티브 탐색 모드로 진입합니다.
76
117
 
77
- ## 실행 방법
118
+ ## 인터랙션 플로우
78
119
 
79
- 1. 사용자의 요청에서 키워드를 추출합니다. 명시적 키워드가 없으면 현재 프로젝트를 분석하여 적절한 검색어를 판단합니다.
80
- 2. \`relay search <keyword>\` 명령어를 실행합니다 (필요하면 여러 키워드로 반복).
81
- 3. 결과를 현재 프로젝트 맥락과 대조하여 가장 도움될 팀을 추천합니다:
82
- - 팀 이름과 설명
83
- - 제공하는 커맨드 목록
84
- - 왜 이 팀이 지금 프로젝트에 맞는지 설명
85
- 4. 관심 있는 팀이 있다면 \`/relay-install <@author/slug>\`로 바로 설치할 수 있다고 안내합니다.
120
+ 커맨드는 3단계 인터랙션으로 진행됩니다. 단계에서 반드시 AskUserQuestion 도구를 사용하세요.
86
121
 
87
- ## 예시
122
+ ### Step 1. 공간 선택 & 팀 탐색 (slug가 없을 때만)
88
123
 
89
- 사용자: /relay-explore 콘텐츠 만들 있는 있어?
90
- → relay search 콘텐츠 실행
91
- → 결과 해석: "contents-team이 카드뉴스, PDF, PPT를 만들 수 있어요"
92
- → 프로젝트 맥락 기반 추천
93
- → "/relay-install @example/contents-team으로 설치할 수 있어요"`,
94
- },
95
- {
96
- id: 'relay-install',
97
- description: 'relay 마켓플레이스에서 에이전트 팀을 설치합니다',
98
- body: `요청된 에이전트 팀을 relay 마켓플레이스에서 다운로드하고, 현재 에이전트 환경에 맞게 구성합니다.
124
+ slug가 직접 주어지면 (\`/relay-install @alice/doc-writer\`) 단계를 건너뛰고 Step 2로 갑니다.
99
125
 
100
- ## 실행 방법
126
+ #### 1-1. 공간 선택
127
+ \`relay spaces --json\` 을 실행하여 사용자의 Space 목록을 가져옵니다.
128
+
129
+ **AskUserQuestion 호출:**
130
+ - question: "어디서 팀을 찾을까요?"
131
+ - options: Space가 있으면 \`["마켓플레이스 (공개)", "<space1_name>", "<space2_name>", ...]\`, 없으면 이 단계를 건너뛰고 바로 마켓 검색으로 진행
132
+
133
+ **응답 처리:**
134
+ - "마켓플레이스 (공개)" → 1-2. 마켓 검색으로 진행
135
+ - Space 이름 선택 → 1-3. Space 팀 목록으로 진행
136
+
137
+ #### 1-2. 마켓 검색
138
+ 사용자의 요청에서 키워드를 추출합니다. 명시적 키워드가 없으면 현재 프로젝트를 분석하여 적절한 검색어를 판단합니다.
139
+ \`relay search <keyword>\` 명령어를 실행합니다 (필요하면 여러 키워드로 반복).
140
+
141
+ 검색 결과를 번호 리스트로 보여줍니다:
142
+
143
+ \`\`\`
144
+ 검색 결과 (3개)
145
+
146
+ 1. @alice/doc-writer — 기술 문서 자동화
147
+ /write-doc, /api-doc
148
+
149
+ 2. @bob/code-reviewer — PR 리뷰 자동화
150
+ /review, /suggest
151
+
152
+ 3. @carol/test-gen — 테스트 코드 생성
153
+ /gen-test, /coverage
154
+ \`\`\`
155
+
156
+ **AskUserQuestion 호출:**
157
+ - question: "어떤 팀을 설치할까요?"
158
+ - options: \`["1", "2", "3", "다시 검색"]\`
159
+
160
+ "다시 검색" → 새 키워드로 1-2 반복
161
+ 번호 선택 → 해당 팀의 slug로 설치 진행
162
+
163
+ #### 1-3. Space 팀 목록
164
+ \`relay list --space <space-slug> --json\` 을 실행합니다.
165
+
166
+ 팀 목록을 번호 리스트로 보여줍니다 (1-2와 동일 형식).
167
+
168
+ **AskUserQuestion 호출:**
169
+ - question: "어떤 팀을 설치할까요?"
170
+ - options: \`["1", "2", ..., "돌아가기"]\`
171
+
172
+ "돌아가기" → 1-1로 돌아감
173
+ 번호 선택 → 해당 팀의 slug로 설치 진행
174
+
175
+ ### Step 2. 설치 & 배치 범위 선택
101
176
 
102
- ### 1. 패키지 다운로드
177
+ #### 2-1. 패키지 다운로드
103
178
  \`relay install <@author/slug> --json\` 명령어를 실행합니다.
104
179
  - Public 마켓 팀: \`relay install <@author/slug> --json\`
105
180
  - Space 팀: \`relay install @spaces/<space-slug>/<team-slug> --json\`
@@ -108,36 +183,30 @@ exports.USER_COMMANDS = [
108
183
  - CLI가 init과 login을 자동으로 처리합니다 (사용자가 별도 실행할 필요 없음).
109
184
  - JSON 출력에서 \`install_path\` (패키지 경로)를 확인합니다.
110
185
 
111
- ### 2. 배치 범위 선택
112
- 사용자에게 설치 범위를 물어봅니다:
186
+ #### 2-2. 배치 범위 선택
113
187
 
114
- - **글로벌** (\`~/.claude/\`): 모든 프로젝트에서 사용 가능
115
- - **로컬** (현재 프로젝트 \`.claude/\`): 프로젝트에서만 사용
188
+ **AskUserQuestion 호출:**
189
+ - question: "어디에 설치할까요?"
190
+ - options: \`["글로벌 (모든 프로젝트)", "로컬 (이 프로젝트만)"]\`
116
191
 
117
- 판단 기준:
118
- - 범용 도구 (카드뉴스, PDF 생성 등): 글로벌 추천
119
- - 프로젝트 전용 팀: 로컬 추천
120
- - Space 비공개 팀: 로컬 추천
192
+ **응답 처리:**
193
+ - "글로벌" \`~/.claude/\`에 배치
194
+ - "로컬" 현재 프로젝트 \`.claude/\`에 배치
121
195
 
122
- 사용자가 별도 지정하지 않으면 글로벌로 진행합니다.
123
-
124
- ### 3. 에이전트 환경에 맞게 배치
196
+ #### 2-3. 에이전트 환경에 맞게 배치
125
197
  다운로드된 패키지(\`install_path\`)에서 파일을 읽고 선택된 범위에 배치합니다:
126
- - Claude Code 글로벌: \`<install_path>/commands/\` → \`~/.claude/commands/\`에 복사
127
- - Claude Code 글로벌: \`<install_path>/skills/\` → \`~/.claude/skills/\`에 복사
128
- - Claude Code 로컬: \`<install_path>/commands/\` → \`.claude/commands/\`에 복사
129
- - Claude Code 로컬: \`<install_path>/skills/\` → \`.claude/skills/\`에 복사
198
+ - 글로벌: \`<install_path>/commands/\` → \`~/.claude/commands/\`에 복사, skills/ 동일
199
+ - 로컬: \`<install_path>/commands/\` → \`.claude/commands/\`에 복사, skills/ 동일
130
200
  - agents/, rules/ 파일도 같은 방식으로 배치합니다.
131
201
  - **충돌 확인**: 같은 이름의 파일이 이미 있으면 사용자에게 덮어쓸지 물어봅니다.
132
202
 
133
- ### 4. 배치 정보 기록 (필수)
134
- 배치 완료 후 반드시 \`relay deploy-record\`를 실행하여 배치 정보를 기록합니다:
203
+ #### 2-4. 배치 정보 기록 (필수)
204
+ 배치 완료 후 반드시 \`relay deploy-record\`를 실행합니다:
135
205
  \`\`\`
136
206
  relay deploy-record <slug> --scope <global|local> --files <배치된_파일1> <배치된_파일2> ...
137
207
  \`\`\`
138
- 이 정보는 \`relay uninstall\` 시 배치된 파일까지 정리하는 데 사용됩니다.
139
208
 
140
- ### 5. Requirements 확인 및 설치
209
+ #### 2-5. Requirements 확인 및 설치
141
210
  \`<install_path>/relay.yaml\`의 \`requires\` 섹션을 읽고 처리합니다:
142
211
  - **cli**: \`which <name>\`으로 확인 → 없으면 install 명령 실행 또는 안내
143
212
  - **npm**: \`npm list <package>\`로 확인 → 없으면 \`npm install\`
@@ -147,125 +216,145 @@ relay deploy-record <slug> --scope <global|local> --files <배치된_파일1> <
147
216
  - **teams**: 의존하는 다른 팀 → \`relay install <@author/team>\`으로 재귀 설치
148
217
  ${LOGIN_JIT_GUIDE}
149
218
 
150
- ### 6. 완료 안내
219
+ ### Step 3. 완료 & 팔로우 제안
220
+
221
+ #### 3-1. 완료 안내
151
222
  - 배치된 파일과 활성화된 커맨드 목록을 보여줍니다.
152
223
  ${BUSINESS_CARD_FORMAT}
153
- - **팔로우 제안**: 사용자에게 직접 질문하는 인터랙티브 도구(예: AskUserQuestion 등)를 사용하여 "@{username}을 팔로우할까요? 새 버전 알림을 받을 수 있습니다."라고 반드시 물어봅니다. 인터랙티브 도구가 없으면 텍스트로 물어봅니다.
154
- - 수락하면: \`relay follow @{username}\` 실행. 로그인이 되어 있으면 먼저 \`relay login\` 실행 후 재시도.
155
- - 거절하면: 건너뜁니다
224
+
225
+ #### 3-2. 팔로우 제안 (필수 단계를 절대 건너뛰지 마세요)
226
+ 명함 표시 직후, 빌더의 username이 JSON 결과에 있으면 **반드시** AskUserQuestion 도구를 호출하세요.
227
+
228
+ **AskUserQuestion 호출:**
229
+ - question: \`@{username}을 팔로우할까요? 새 버전 알림을 받을 수 있습니다.\`
230
+ - options: \`["팔로우", "건너뛰기"]\`
231
+
232
+ **응답 처리:**
233
+ - "팔로우" → \`relay follow @{username}\` 실행. 로그인이 안 되어 있으면 \`relay login\` 먼저 실행 후 재시도.
234
+ - "건너뛰기" → 다음 단계로 진행
235
+
236
+ #### 3-3. 사용 제안
156
237
  - "바로 사용해볼까요?" 제안
157
238
 
158
- ### 7. 업데이트 확인 (설치 완료 후)
239
+ #### 3-4. 업데이트 확인
159
240
  - \`relay check-update\` 명령어를 실행합니다.
160
241
  - CLI 업데이트가 있으면 안내합니다: "relay v{new} available. Run: npm update -g relayax-cli"
161
242
  - 다른 팀 업데이트가 있으면 안내합니다.
162
243
 
163
244
  ## 예시
164
245
 
165
- 사용자: /relay-install @example/contents-team
166
- → relay install @example/contents-team --json 실행 (패키지 다운로드)
167
- 사용자에게 "글로벌 vs 로컬" 선택 질문 글로벌
168
- .relay/teams/ 내용을 ~/.claude/에 배치
169
- → relay deploy-record @example/contents-team --scope global --files ~/.claude/commands/cardnews.md ...
170
- requires 확인: playwright 설치됨, sharp 설치됨
171
- → " 설치 완료! /cardnews를 사용해볼까요?"`,
246
+ ### 인터랙티브 모드 (/relay-install)
247
+ → relay spaces --json 실행
248
+ AskUserQuestion: "어디서 팀을 찾을까요?" ["마켓플레이스 (공개)", "Acme Corp"]
249
+ "마켓플레이스" 선택 "어떤 팀을 찾고 계세요?"
250
+ → relay search "문서" 실행 결과 리스트 표시
251
+ AskUserQuestion: "어떤 팀을 설치할까요?" ["1", "2", "3", "다시 검색"]
252
+ → "1" 선택 (@alice/doc-writer)
253
+ → AskUserQuestion: "어디에 설치할까요?" → ["글로벌 (모든 프로젝트)", "로컬 (이 프로젝트만)"]
254
+ → "글로벌" 선택
255
+ → 설치 + 배치 + deploy-record
256
+ → 명함 표시
257
+ → AskUserQuestion: "@alice을 팔로우할까요?" → ["팔로우", "건너뛰기"]
258
+ → "✓ 설치 완료! /write-doc를 사용해볼까요?"
259
+
260
+ ### 다이렉트 모드 (/relay-install @alice/doc-writer)
261
+ → relay install @alice/doc-writer --json 실행 (Step 1 건너뜀)
262
+ → AskUserQuestion: "어디에 설치할까요?" → ["글로벌 (모든 프로젝트)", "로컬 (이 프로젝트만)"]
263
+ → 설치 + 배치 + deploy-record
264
+ → 명함 표시
265
+ → AskUserQuestion: "@alice을 팔로우할까요?" → ["팔로우", "건너뛰기"]
266
+ → "✓ 설치 완료! /write-doc를 사용해볼까요?"`,
172
267
  },
173
268
  {
174
- id: 'relay-list',
175
- description: '설치된 에이전트 목록을 확인합니다',
176
- body: `현재 설치된 에이전트 목록을 보여줍니다.
269
+ id: 'relay-status',
270
+ description: '설치된 팀과 Space 현황을 확인합니다',
271
+ body: `현재 설치된 에이전트 팀과 소속 Space 현황을 한눈에 보여줍니다.
177
272
 
178
273
  ## 실행 방법
179
274
 
180
- 1. \`relay list --json\` 명령어를 실행합니다.
181
- 2. 결과를 사용자에게 보기 좋게 정리하여 보여줍니다:
182
- - 팀 이름과 버전
183
- - 설치 날짜
184
- - 사용 가능한 커맨드
185
- 3. 설치된 팀이 없으면 \`/relay-explore\`로 팀을 탐색해보라고 안내합니다.
275
+ ### 1. 설치된 목록
186
276
 
187
- ### Space 목록 확인
188
- - 특정 Space에서 사용 가능한 팀 목록을 보려면: \`relay list --space <space-slug> --json\`
189
- - Space에 가입되어 있어야 합니다.
277
+ \`relay list --json\` 명령어를 실행합니다.
190
278
 
191
- ## 예시
192
-
193
- 사용자: /relay-list
194
- relay list --json 실행
195
- → "2개 팀이 설치되어 있어요:"
196
- → " @example/contents-team v1.2.0 (2일 전 설치) - /cardnews, /pdf-report"
197
- → " qa-team v0.5.1 (1주 전 설치) - /qa, /qa-only"
198
-
199
- 사용자: /relay-list --space bobusan
200
- → relay list --space bobusan --json 실행
201
- → "bobusan Space에서 설치 가능한 팀:"
202
- → " pm-bot — 프로젝트 관리 봇"
203
- → " cs-bot — 고객 응대 봇"`,
204
- },
279
+ **JSON 응답 구조:**
280
+ \`\`\`json
281
+ {
282
+ "installed": [
205
283
  {
206
- id: 'relay-update',
207
- description: '설치된 에이전트 팀을 최신 버전으로 업데이트합니다',
208
- body: `설치된 에이전트 팀의 업데이트를 확인하고 적용합니다.
284
+ "slug": "@author/team-name",
285
+ "version": "1.2.0",
286
+ "installed_at": "2026-03-20T12:00:00.000Z",
287
+ "scope": "global",
288
+ "deploy_scope": "global",
289
+ "space_slug": null
290
+ }
291
+ ]
292
+ }
293
+ \`\`\`
209
294
 
210
- ## 실행 방법
295
+ **각 팀을 아래 형식으로 표시:**
211
296
 
212
- ### 0. CLI 업데이트 확인
213
- - 먼저 \`relay check-update\` 명령어를 실행합니다.
214
- - CLI 업데이트가 있으면 사용자에게 안내합니다: "relay v{new} available. Run: npm update -g relayax-cli"
215
- - 업데이트 여부와 관계없이 팀 업데이트를 계속 진행합니다.
216
-
217
- ### 특정 팀 업데이트
218
- - 사용자가 팀 이름을 지정한 경우: \`relay update <@author/slug> --json\` 실행
219
- - 업데이트 결과를 보여줍니다 (이전 버전 → 새 버전)
220
- - **재배치 필요 확인**: JSON 출력에 \`needs_redeploy: true\`가 있으면:
221
- 1. \`previous_deploy_scope\`를 참고하여 같은 범위(글로벌/로컬)로 파일을 다시 배치합니다.
222
- 2. 배치 후 \`relay deploy-record <slug> --scope <scope> --files <...>\`를 실행하여 기록합니다.
223
- ${BUSINESS_CARD_FORMAT}
297
+ | | 버전 | 배포 | 설치일 |
298
+ |---|---|---|---|
299
+ | @author/team-name | v1.2.0 | 글로벌 | 3/20 |
224
300
 
225
- ### 전체 업데이트 확인
226
- - 이름을 지정하지 않은 경우:
227
- 1. \`relay outdated --json\`으로 업데이트 가능한 팀 목록을 확인합니다.
228
- 2. 업데이트 가능한 팀이 있으면 목록을 보여주고 어떤 팀을 업데이트할지 물어봅니다.
229
- 3. 선택된 팀에 대해 \`relay update <@author/slug> --json\`을 실행합니다.
230
- 4. 모두 최신이면 "모든 팀이 최신 버전입니다"라고 안내합니다.
301
+ - \`deploy_scope\`가 \`"global"\` → 글로벌, \`"local"\` → 로컬, 없으면 → 미배치
302
+ - \`space_slug\`가 있으면 \`[Space: slug]\` 표시
231
303
 
232
- ## 예시
304
+ ### 2. Space 목록
233
305
 
234
- 사용자: /relay-update
235
- → relay outdated --json 실행
236
- → "1개 팀 업데이트 가능:"
237
- → " @example/contents-team: v1.2.0 → v1.3.0"
238
- → "업데이트할까요?"
239
- → relay update @example/contents-team --json 실행
240
- → needs_redeploy: true → 글로벌로 재배치
241
- → relay deploy-record @example/contents-team --scope global --files ...
242
- → "✓ @example/contents-team v1.3.0으로 업데이트 완료"`,
243
- },
244
- {
245
- id: 'relay-spaces',
246
- description: '내 Space 목록을 확인합니다',
247
- body: `사용자의 Space 목록을 조회하고 보여줍니다.
306
+ \`relay spaces --json\` 명령어를 실행합니다.
248
307
 
249
- ## 실행 방법
308
+ **JSON 응답 구조:**
309
+ \`\`\`json
310
+ {
311
+ "spaces": [
312
+ {
313
+ "slug": "my-space",
314
+ "name": "내 스페이스",
315
+ "description": "설명",
316
+ "is_personal": false,
317
+ "role": "owner"
318
+ }
319
+ ]
320
+ }
321
+ \`\`\`
250
322
 
251
- 1. \`relay spaces --json\` 명령어를 실행합니다.
252
- 2. 결과를 사용자에게 보기 좋게 정리합니다:
253
- - 개인 스페이스
254
- - 팀 스페이스 (이름, 역할, 설명)
255
- 3. Space가 있으면 관련 활용법을 안내합니다:
256
- - 팀 목록 보기: \`relay list --space <slug>\`
257
- - 비공개 팀 설치: \`relay install @spaces/<slug>/<team>\`
258
- - Space 관리: www.relayax.com/spaces/<slug>
323
+ **표시:**
324
+ - \`is_personal: true\` 개인 스페이스로 분류
325
+ - \`role\`: owner → 소유자, admin → 관리자, member → 멤버
259
326
  ${LOGIN_JIT_GUIDE}
327
+ - Spaces 조회 실패해도 설치된 팀 목록은 정상 표시합니다 (로컬 데이터).
328
+
329
+ ### 3. Space 팀 목록 (옵션)
330
+ - \`--space <slug>\` 인자가 있으면: \`relay list --space <space-slug> --json\`으로 해당 Space에서 설치 가능한 팀 목록도 보여줍니다.
331
+
332
+ ### 4. 안내
333
+ - 설치된 팀이 없으면 \`/relay-install\`로 팀을 탐색·설치해보라고 안내합니다.
334
+ - Space가 있으면 활용법을 안내합니다:
335
+ - 비공개 팀 설치: \`relay install @spaces/<slug>/<team>\`
336
+ - Space 관리: www.relayax.com/spaces/<slug>
260
337
 
261
338
  ## 예시
262
339
 
263
- 사용자: /relay-spaces
264
- → relay spaces --json 실행
265
- "2개 Space가 있어요:"
266
- → " bobusan — 보부산 (소유자)"
267
- " design-lab — 디자인 랩 (멤버)"
268
- → "💡 Space 팀 보기: relay list --space bobusan"`,
340
+ 사용자: /relay-status
341
+ → relay list --json 실행
342
+ relay spaces --json 실행 (병렬 가능)
343
+
344
+ **설치된 (2개)**
345
+
346
+ | 팀 | 버전 | 배포 | 설치일 |
347
+ |---|---|---|---|
348
+ | @alice/doc-writer | v1.2.0 | 글로벌 | 3/20 |
349
+ | @bob/code-reviewer | v0.5.1 | 로컬 | 3/15 |
350
+
351
+ **내 Space (2개)**
352
+ - acme-corp — Acme Corp (소유자)
353
+ - dev-guild — Dev Guild (멤버)
354
+
355
+ 사용자: /relay-status --space acme-corp
356
+ → 위 정보 + \`relay list --space acme-corp --json\` 실행
357
+ → acme-corp Space에서 설치 가능한 팀 목록 추가 표시`,
269
358
  },
270
359
  {
271
360
  id: 'relay-uninstall',
@@ -284,9 +373,9 @@ ${LOGIN_JIT_GUIDE}
284
373
 
285
374
  ## 예시
286
375
 
287
- 사용자: /relay-uninstall @example/contents-team
288
- → relay uninstall @example/contents-team --json 실행
289
- → "✓ @example/contents-team 삭제 완료 (12개 파일 제거)"`,
376
+ 사용자: /relay-uninstall @alice/doc-writer
377
+ → relay uninstall @alice/doc-writer --json 실행
378
+ → "✓ @alice/doc-writer 삭제 완료 (12개 파일 제거)"`,
290
379
  },
291
380
  ];
292
381
  // ─── Builder Commands (로컬 설치) ───
@@ -296,51 +385,92 @@ exports.BUILDER_COMMANDS = [
296
385
  description: '현재 팀 패키지를 포트폴리오와 함께 relay 마켓플레이스에 배포합니다',
297
386
  body: `현재 디렉토리의 에이전트 팀(.relay/)을 분석하고, 보안 점검 및 requirements를 구성한 뒤, 사용가이드와 포트폴리오를 생성하고 relay 마켓플레이스에 배포합니다.
298
387
 
299
- ## 실행 단계
388
+ ## 사전 준비 (자동)
300
389
 
301
- ### 1. 인증 확인 (가장 먼저)
390
+ ### 0-1. 인증 확인
302
391
  - \`relay status --json\` 명령어를 실행하여 로그인 상태를 확인합니다.
303
392
  - 미인증이면 즉시 \`relay login\`을 실행합니다.
304
- - 로그인 완료 후 다음 단계로 진행합니다.
305
393
  ${LOGIN_JIT_GUIDE}
306
394
 
307
- ### 2. 팀 구조 분석
395
+ ### 0-2. 팀 구조 분석
308
396
  - .relay/ 디렉토리의 skills/, agents/, rules/, commands/를 탐색합니다.
309
397
  - 각 파일의 이름과 description을 추출합니다.
310
398
  - .relay/relay.yaml이 있으면 읽고, 없으면 사용자에게 팀 정보(name, slug, description, tags)를 물어보고 생성합니다.
311
399
 
312
- ### 3. 보안 점검 & Requirements 구성 (HITL)
400
+ ## 인터랙션 플로우
401
+
402
+ 이 커맨드는 5단계 인터랙션으로 진행됩니다. 각 단계에서 반드시 AskUserQuestion 도구를 사용하세요.
403
+
404
+ ### Step 1. 공개 범위 선택
405
+
406
+ relay.yaml의 \`visibility\` 설정을 확인합니다.
407
+
408
+ #### 신규 배포 (visibility 미설정)
409
+
410
+ **AskUserQuestion 호출:**
411
+ - question: "어디에 배포할까요?"
412
+ - options: \`["공개 (마켓플레이스)", "비공개 (Space 전용)"]\`
413
+
414
+ **응답 처리:**
415
+ - "공개" → relay.yaml에 \`visibility: public\` 저장
416
+ - "비공개" → \`relay spaces --json\` 실행 후 Space 목록 표시
417
+
418
+ **AskUserQuestion 호출:**
419
+ - question: "어떤 Space에 배포할까요?"
420
+ - options: \`["<space1_name>", "<space2_name>", ...]\`
421
+
422
+ → relay.yaml에 \`visibility: private\`, \`space: <selected_slug>\` 저장
423
+
424
+ #### 재배포 (visibility 이미 설정됨)
313
425
 
314
- .relay/ 모든 파일을 읽고 아래 항목을 분석합니다.
426
+ 현재 설정을 확인합니다:
315
427
 
316
- #### 3-1. 시크릿 스캔
428
+ **AskUserQuestion 호출:**
429
+ - question: 공개일 때 "현재 **공개** 설정입니다. 마켓플레이스에 노출됩니다. 유지할까요?", 비공개일 때 "현재 **비공개** 설정입니다 (Space: {name}). 유지할까요?"
430
+ - options: \`["유지", "변경"]\`
431
+
432
+ "변경" → 신규 배포와 동일한 플로우
433
+
434
+ ### Step 2. 보안 점검 & requires 확인
435
+
436
+ .relay/ 내 모든 파일을 자동 분석합니다.
437
+
438
+ #### 2-1. 시크릿 스캔 (자동)
317
439
  - 하드코딩된 API 키, 토큰, 비밀번호, Private Key 등을 탐색합니다.
318
440
  - 예: sk-..., ghp_..., AKIA..., Bearer 토큰, JWT, -----BEGIN PRIVATE KEY----- 등
319
441
  - 발견 시 **즉시 사용자에게 경고**하고, 환경변수로 대체하도록 안내합니다.
320
442
  - 시크릿이 제거되지 않으면 배포를 진행하지 않습니다.
321
443
 
322
- #### 3-2. 환경변수 감지
323
- - 파일 내 환경변수 참조를 감지합니다 (process.env.*, \${VAR}, os.environ 등).
324
- - 환경변수에 대해 사용자에게 확인합니다:
325
- - "이 환경변수가 필수인가요, 선택인가요?"
326
- - 설명을 추가할 수 있습니다.
327
-
328
- #### 3-3. 의존성 감지
329
- 아래 카테고리별로 분석하고, 사용자에게 필수/선택 여부를 확인합니다:
330
-
331
- - **cli**: 파일에서 참조하는 CLI 도구 (playwright, ffmpeg, sharp 등)
444
+ #### 2-2. 환경변수 & 의존성 분석 (자동)
445
+ 분석 대상:
446
+ - **env**: 환경변수 참조 (process.env.*, \${VAR}, os.environ 등)
447
+ - **cli**: 참조하는 CLI 도구 (playwright, ffmpeg, sharp 등)
332
448
  - **npm**: import/require되는 npm 패키지
333
- - **mcp**: MCP 서버 설정 — 외부 서비스 연결 포함 (name, package, config, 필요한 env)
334
- - **runtime**: Node.js/Python 등 최소 버전 요구
335
- - **permissions**: 필요한 에이전트 권한 (filesystem, network, shell)
449
+ - **mcp**: MCP 서버 설정
450
+ - **runtime**: Node.js/Python 등 최소 버전
336
451
  - **teams**: 의존하는 다른 relay 팀
337
452
 
338
- 항목에 대해:
339
- - \`required: true/false\` — 없으면 팀이 동작하지 않는지, 부분 동작인지
340
- - \`description\` — 왜 필요한지 짧은 설명
453
+ 분석 결과를 요약 표시합니다:
454
+
455
+ \`\`\`
456
+ requires 분석 결과
341
457
 
342
- #### 3-4. .relay/relay.yaml에 requires 반영
343
- 분석 결과를 .relay/relay.yaml의 requires 섹션에 저장합니다.
458
+ 환경변수:
459
+ OPENAI_API_KEY 필수 (LLM API 호출)
460
+ SLACK_WEBHOOK_URL — 선택 (알림 전송)
461
+
462
+ CLI: playwright (필수)
463
+ npm: sharp (필수)
464
+ MCP: supabase (선택)
465
+ \`\`\`
466
+
467
+ **AskUserQuestion 호출:**
468
+ - question: "requires 설정이 맞나요?"
469
+ - options: \`["확인", "수정"]\`
470
+
471
+ **응답 처리:**
472
+ - "확인" → .relay/relay.yaml에 requires 섹션 저장, 다음 단계로
473
+ - "수정" → 사용자와 텍스트 대화로 수정 후 다시 확인
344
474
 
345
475
  \`\`\`yaml
346
476
  # .relay/relay.yaml requires 구조
@@ -367,95 +497,116 @@ requires:
367
497
  command: "npx"
368
498
  args: ["-y", "@supabase/mcp-server"]
369
499
  env: [SUPABASE_URL, SUPABASE_SECRET_KEY]
370
- - name: notion
371
- package: "@notionhq/mcp-server"
372
- required: false
373
- env: [NOTION_API_KEY]
374
500
  runtime:
375
501
  node: ">=18"
376
502
  permissions:
377
503
  - filesystem
378
504
  - network
379
505
  teams:
380
- - @example/contents-team
506
+ - @alice/doc-writer
381
507
  \`\`\`
382
508
 
383
- ### 4. 포트폴리오 수집
509
+ ### Step 3. 포트폴리오 선택
510
+
511
+ output/, results/, examples/, portfolio/ 디렉토리를 스캔하여 결과물(PNG, JPG, HTML, PDF)을 찾습니다.
512
+ HTML 파일은 Playwright 스크린샷으로 변환합니다.
384
513
 
385
- - output/, results/, examples/, portfolio/ 디렉토리를 스캔합니다.
386
- - 발견된 결과물(PNG, JPG, HTML, PDF)을 사용자에게 보여줍니다.
387
- - HTML 파일은 Playwright 스크린샷으로 변환합니다.
388
- - 사용자가 포트폴리오에 포함할 항목을 선택합니다.
389
- - 선택된 이미지를 .relay/portfolio/에 저장합니다.
514
+ 발견된 파일을 번호 리스트로 보여줍니다:
390
515
 
391
- ### 5. 사용가이드 생성 (GUIDE.html)
516
+ \`\`\`
517
+ 포트폴리오 후보 (4개)
518
+
519
+ 1. output/report-example.png — 리포트 예시
520
+ 2. output/dashboard.html → 스크린샷 변환
521
+ 3. examples/result-1.png — 결과물 1
522
+ 4. examples/result-2.png — 결과물 2
523
+ \`\`\`
524
+
525
+ **AskUserQuestion 호출:**
526
+ - question: "어떤 결과물을 포트폴리오에 포함할까요? (최대 5개)"
527
+ - options: \`["전체 포함", "1", "2", "3", "4", "건너뛰기"]\`
528
+
529
+ **응답 처리:**
530
+ - "전체 포함" → 모든 파일을 .relay/portfolio/에 저장
531
+ - 번호 선택 → 해당 파일만 포함 (여러 번 선택 가능 — 한 번 선택 후 추가 선택 여부를 다시 물어봄)
532
+ - "건너뛰기" → 포트폴리오 없이 진행
533
+
534
+ 선택된 이미지를 .relay/portfolio/에 저장하고 relay.yaml의 portfolio.gallery에 등록합니다.
535
+
536
+ \`\`\`yaml
537
+ portfolio:
538
+ demo: # 선택
539
+ type: gif
540
+ path: portfolio/demo.gif
541
+ gallery: # 선택, 최대 5장
542
+ - path: portfolio/guide-preview.png
543
+ title: "사용가이드 미리보기"
544
+ - path: portfolio/report-example.png
545
+ title: "리포트 예시"
546
+ \`\`\`
547
+
548
+ 파일이 하나도 발견되지 않으면 이 단계를 건너뜁니다.
549
+
550
+ ### Step 4. GUIDE.html 컨펌
392
551
 
393
552
  설치자가 읽을 수 있는 사용가이드를 HTML로 생성합니다.
394
553
 
395
- #### 5-1. 팀 소스 분석
554
+ #### 4-1. 팀 소스 분석 (자동)
396
555
  - skills/, agents/, commands/ 디렉토리의 **모든 파일 내용**을 읽습니다.
397
556
  - 각 스킬의 SKILL.md, 에이전트 설정, 커맨드 문서를 분석하여 팀의 파이프라인 흐름을 추론합니다.
398
557
 
399
- #### 5-2. GUIDE.html 생성
558
+ #### 4-2. GUIDE.html 생성 (자동)
400
559
  - 팀의 핵심 기능, 시작 방법, 파이프라인 흐름, Q&A를 포함하는 단일 HTML 가이드를 생성합니다.
401
560
  - 디자인: 깔끔한 단일 페이지, 시스템 폰트, 최대 1200px 너비, 라이트 테마.
402
- - 5-1에서 분석한 팀 소스 정보를 기반으로 콘텐츠를 구성합니다.
403
561
  - 파이프라인이 없는 단순한 팀은 시작 방법 + 기능 설명 + Q&A만 포함합니다.
404
562
 
405
- #### 5-3. 미리보기 + 컨펌
406
- - 생성된 GUIDE.html을 브라우저에서 열어 빌더에게 미리보기를 보여줍니다.
407
- - 빌더에게 확인: "사용가이드를 확인해주세요. 이대로 진행할까요?"
563
+ #### 4-3. 미리보기 + 컨펌
564
+ 생성된 GUIDE.html을 브라우저에서 열어 빌더에게 미리보기를 보여줍니다.
408
565
 
409
- #### 5-4. 재생성 루프
410
- - 빌더가 수정을 요청하면 (예: "Q&A 추가해줘", "파이프라인 설명 더 자세히") 요청사항을 반영하여 GUIDE.html을 재생성합니다.
411
- - 재생성 다시 브라우저에서 미리보기를 보여줍니다.
412
- - 빌더가 컨펌할 때까지 반복합니다.
566
+ **AskUserQuestion 호출:**
567
+ - question: "사용가이드를 확인해주세요. 진행할까요?"
568
+ - options: \`["진행", "수정 요청"]\`
413
569
 
414
- #### 5-5. 저장
415
- - 컨펌된 GUIDE.html을 \`.relay/GUIDE.html\`에 저장합니다.
570
+ **응답 처리:**
571
+ - "진행" GUIDE.html을 \`.relay/GUIDE.html\`에 저장
572
+ - "수정 요청" → 사용자와 텍스트 대화로 수정사항 파악 → GUIDE.html 재생성 → 다시 미리보기 → AskUserQuestion 반복
416
573
 
417
- #### 5-6. GUIDE.html 스크린샷 → gallery 등록
418
- - GUIDE.html을 Playwright로 열어 첫 화면(뷰포트 1200x630)을 스크린샷 캡처합니다. (gstack 또는 webapp-testing 스킬 활용)
574
+ #### 4-4. GUIDE.html 스크린샷 → gallery 등록 (자동)
575
+ - GUIDE.html을 Playwright로 열어 첫 화면(뷰포트 1200x630)을 스크린샷 캡처합니다.
419
576
  - 결과 PNG를 \`./portfolio/guide-preview.png\`에 저장합니다.
420
577
  - relay.yaml의 portfolio gallery 첫 번째 항목으로 자동 등록합니다.
421
578
  - 이 이미지는 마켓플레이스 카드 및 OG 이미지의 fallback으로 사용됩니다 (demo > gallery 순).
422
579
 
423
- ### 6. 메타데이터 생성
580
+ ### Step 5. 최종 확인 & 배포
581
+
582
+ #### 5-1. 메타데이터 생성 (자동)
424
583
  - description: skills 내용 기반으로 자동 생성합니다.
425
584
  - long_description: 팀 소개 마크다운을 자동 생성합니다 (README.md가 있으면 활용).
426
585
  - tags: 팀 특성에 맞는 태그를 추천합니다.
427
- - 사용자에게 확인: "이대로 배포할까요?"
428
586
 
429
- ### 7. 공개 범위 확인 (필수)
430
- - .relay/relay.yaml에 \`visibility\`가 반드시 설정되어 있어야 합니다.
431
- - 설정되어 있지 않으면 빌더에게 반드시 물어봅니다:
432
- - "공개 (마켓플레이스에 누구나 검색·설치 가능)" vs "비공개 (Space 멤버만 접근)"
433
- - 선택 결과를 relay.yaml에 저장합니다.
434
- - 이미 설정되어 있으면 현재 값을 확인합니다:
435
- - 공개인 경우: "⚠ 이 팀은 **공개**로 설정되어 있어 마켓플레이스에 노출됩니다. 맞나요?"
436
- - 비공개인 경우: "이 팀은 **비공개**로 설정되어 Space 멤버만 접근 가능합니다."
437
- - 빌더가 변경을 원하면 relay.yaml을 업데이트합니다.
587
+ #### 5-2. 배포 요약 + 최종 확인
588
+ 배포할 내용을 요약 표시합니다:
438
589
 
439
- ### 8. .relay/relay.yaml 업데이트
440
- - 메타데이터, requires, 포트폴리오 슬롯을 .relay/relay.yaml에 반영합니다.
590
+ \`\`\`
591
+ 배포 요약
441
592
 
442
- \`\`\`yaml
443
- portfolio:
444
- demo: # 선택
445
- type: gif
446
- path: portfolio/demo.gif
447
- gallery: # 선택, 최대 5장
448
- - path: portfolio/guide-preview.png
449
- title: "사용가이드 미리보기"
450
- - path: portfolio/example-1.png
451
- title: "카드뉴스 예시"
593
+ 팀: my-team v1.0.0
594
+ 공개: 마켓플레이스 (공개)
595
+ Skills: 3개, Commands: 5개
596
+ 포트폴리오: 2장
597
+ requires: env 2개, cli 1개
452
598
  \`\`\`
453
599
 
454
- ### 9. 배포
455
- - \`relay publish\` 명령어를 실행합니다.
456
- - 배포 결과와 마켓플레이스 URL을 보여줍니다.
600
+ **AskUserQuestion 호출:**
601
+ - question: "이대로 배포할까요?"
602
+ - options: \`["배포", "취소"]\`
603
+
604
+ **응답 처리:**
605
+ - "배포" → \`relay publish\` 실행
606
+ - "취소" → 중단
457
607
 
458
- ### 10. 공유용 온보딩 가이드 제공
608
+ #### 5-3. 배포 완료 & 온보딩 가이드
609
+ - 배포 결과와 마켓플레이스 URL을 보여줍니다.
459
610
  - \`relay publish\` 출력 끝에 코드블록 형태의 온보딩 가이드가 포함됩니다.
460
611
  - 이 코드블록을 사용자에게 그대로 보여줍니다.
461
612
  - 출력에 코드블록이 없으면 아래 형태로 직접 생성합니다:
@@ -473,16 +624,22 @@ ${BUSINESS_CARD_FORMAT}
473
624
  ## 예시
474
625
 
475
626
  사용자: /relay-publish
476
- → 팀 구조 분석: skills 3개, commands 5개
477
- 보안 스캔: 시크릿 없음
478
- 환경변수 감지: OPENAI_API_KEY (필수), DATABASE_URL (선택)
479
- → requires 업데이트 완료
480
- 포트폴리오: output/ 스캔 → "카드뉴스 예시.png 포함?" → Yes
481
- GUIDE.html 생성 → 브라우저에서 미리보기 → 빌더 컨펌
482
- GUIDE.html 스크린샷gallery 첫 번째 이미지로 등록
483
- relay publish 실행
627
+ 인증 확인 ✓, 팀 구조 분석 (skills 3개, commands 5개)
628
+ AskUserQuestion: "어디에 배포할까요?" ["공개 (마켓플레이스)", "비공개 (Space 전용)"]
629
+ "공개" 선택
630
+ 보안 스캔 ✓ 시크릿 없음 → requires 분석 결과 표시
631
+ AskUserQuestion: "requires 설정이 맞나요?" ["확인", "수정"]
632
+ "확인"
633
+ 포트폴리오 후보 스캔 리스트 표시
634
+ AskUserQuestion: "어떤 결과물을 포함할까요?" → ["전체 포함", "1", "2", "건너뛰기"]
635
+ → "1" 선택
636
+ → GUIDE.html 생성 → 브라우저 미리보기
637
+ → AskUserQuestion: "사용가이드 진행할까요?" → ["진행", "수정 요청"]
638
+ → "진행"
639
+ → 배포 요약 표시
640
+ → AskUserQuestion: "이대로 배포할까요?" → ["배포", "취소"]
641
+ → "배포" → relay publish 실행
484
642
  → "배포 완료! URL: https://relayax.com/teams/my-team"
485
- → 온보딩 가이드 코드블록 표시
486
- → "이 블록을 팀원에게 공유하면 바로 설치할 수 있습니다"`,
643
+ → 온보딩 가이드 코드블록 표시`,
487
644
  },
488
645
  ];