relayax-cli 0.2.39 → 0.2.41
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/dist/commands/access.d.ts +2 -0
- package/dist/commands/access.js +90 -0
- package/dist/commands/create.js +70 -22
- package/dist/commands/follow.js +2 -2
- package/dist/commands/init.js +18 -3
- package/dist/commands/install.js +100 -20
- package/dist/commands/join.js +5 -3
- package/dist/commands/list.js +2 -2
- package/dist/commands/login.js +1 -1
- package/dist/commands/package.d.ts +2 -0
- package/dist/commands/package.js +287 -0
- package/dist/commands/publish.js +137 -62
- package/dist/commands/search.js +1 -1
- package/dist/commands/spaces.d.ts +1 -1
- package/dist/commands/spaces.js +9 -17
- package/dist/commands/update.js +1 -1
- package/dist/index.js +4 -0
- package/dist/lib/command-adapter.js +207 -72
- package/dist/lib/guide.d.ts +13 -5
- package/dist/lib/guide.js +142 -50
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
package/dist/commands/spaces.js
CHANGED
|
@@ -22,7 +22,7 @@ function registerSpaces(program) {
|
|
|
22
22
|
const token = await (0, config_js_1.getValidToken)();
|
|
23
23
|
if (!token) {
|
|
24
24
|
if (json) {
|
|
25
|
-
console.error(JSON.stringify({ error: 'LOGIN_REQUIRED', message: '로그인이 필요합니다. relay login을 먼저 실행하세요.' }));
|
|
25
|
+
console.error(JSON.stringify({ error: 'LOGIN_REQUIRED', message: '로그인이 필요합니다. relay login을 먼저 실행하세요.', fix: 'relay login 실행 후 재시도하세요.' }));
|
|
26
26
|
}
|
|
27
27
|
else {
|
|
28
28
|
console.error('\x1b[31m오류: 로그인이 필요합니다.\x1b[0m');
|
|
@@ -36,29 +36,21 @@ function registerSpaces(program) {
|
|
|
36
36
|
console.log(JSON.stringify({ spaces }));
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
console.log(`\n\x1b[90m개인 스페이스:\x1b[0m`);
|
|
43
|
-
console.log(` \x1b[36m${personal.slug}\x1b[0m \x1b[1m${personal.name}\x1b[0m`);
|
|
39
|
+
if (spaces.length === 0) {
|
|
40
|
+
console.log('\nSpace가 없습니다.');
|
|
41
|
+
console.log('\x1b[33m💡 Space를 만들려면: www.relayax.com/spaces/new\x1b[0m');
|
|
44
42
|
}
|
|
45
|
-
|
|
46
|
-
console.log(`\n\x1b[1m내 Space\x1b[0m (${
|
|
47
|
-
for (const s of
|
|
43
|
+
else {
|
|
44
|
+
console.log(`\n\x1b[1m내 Space\x1b[0m (${spaces.length}개):\n`);
|
|
45
|
+
for (const s of spaces) {
|
|
48
46
|
const role = s.role === 'owner' ? '\x1b[33m소유자\x1b[0m'
|
|
49
|
-
: s.role === '
|
|
47
|
+
: s.role === 'builder' ? '\x1b[36m빌더\x1b[0m'
|
|
50
48
|
: '\x1b[90m멤버\x1b[0m';
|
|
51
49
|
const desc = s.description
|
|
52
50
|
? ` \x1b[90m${s.description.length > 40 ? s.description.slice(0, 40) + '...' : s.description}\x1b[0m`
|
|
53
51
|
: '';
|
|
54
52
|
console.log(` \x1b[36m${s.slug}\x1b[0m \x1b[1m${s.name}\x1b[0m ${role}${desc}`);
|
|
55
53
|
}
|
|
56
|
-
}
|
|
57
|
-
if (!personal && others.length === 0) {
|
|
58
|
-
console.log('\nSpace가 없습니다.');
|
|
59
|
-
console.log('\x1b[33m💡 Space를 만들려면: www.relayax.com/spaces/new\x1b[0m');
|
|
60
|
-
}
|
|
61
|
-
if (others.length > 0) {
|
|
62
54
|
console.log(`\n\x1b[33m💡 Space 팀 목록: relay list --space <slug>\x1b[0m`);
|
|
63
55
|
console.log(`\x1b[33m💡 비공개 배포: relay.yaml에 visibility: private 설정 후 relay publish\x1b[0m`);
|
|
64
56
|
}
|
|
@@ -66,7 +58,7 @@ function registerSpaces(program) {
|
|
|
66
58
|
catch (err) {
|
|
67
59
|
const message = err instanceof Error ? err.message : String(err);
|
|
68
60
|
if (json) {
|
|
69
|
-
console.error(JSON.stringify({ error: 'FETCH_FAILED', message }));
|
|
61
|
+
console.error(JSON.stringify({ error: 'FETCH_FAILED', message, fix: '네트워크 연결을 확인하거나 잠시 후 재시도하세요.' }));
|
|
70
62
|
}
|
|
71
63
|
else {
|
|
72
64
|
console.error(`\x1b[31m오류: ${message}\x1b[0m`);
|
package/dist/commands/update.js
CHANGED
|
@@ -133,7 +133,7 @@ function registerUpdate(program) {
|
|
|
133
133
|
}
|
|
134
134
|
catch (err) {
|
|
135
135
|
const message = err instanceof Error ? err.message : String(err);
|
|
136
|
-
console.error(JSON.stringify({ error: 'UPDATE_FAILED', message }));
|
|
136
|
+
console.error(JSON.stringify({ error: 'UPDATE_FAILED', message, fix: 'npm update -g relayax-cli로 수동 업데이트하세요.' }));
|
|
137
137
|
process.exit(1);
|
|
138
138
|
}
|
|
139
139
|
finally {
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ const search_js_1 = require("./commands/search.js");
|
|
|
9
9
|
const install_js_1 = require("./commands/install.js");
|
|
10
10
|
const list_js_1 = require("./commands/list.js");
|
|
11
11
|
const uninstall_js_1 = require("./commands/uninstall.js");
|
|
12
|
+
const package_js_1 = require("./commands/package.js");
|
|
12
13
|
const publish_js_1 = require("./commands/publish.js");
|
|
13
14
|
const login_js_1 = require("./commands/login.js");
|
|
14
15
|
const update_js_1 = require("./commands/update.js");
|
|
@@ -20,6 +21,7 @@ const join_js_1 = require("./commands/join.js");
|
|
|
20
21
|
const spaces_js_1 = require("./commands/spaces.js");
|
|
21
22
|
const deploy_record_js_1 = require("./commands/deploy-record.js");
|
|
22
23
|
const ping_js_1 = require("./commands/ping.js");
|
|
24
|
+
const access_js_1 = require("./commands/access.js");
|
|
23
25
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
24
26
|
const pkg = require('../package.json');
|
|
25
27
|
const program = new commander_1.Command();
|
|
@@ -35,6 +37,7 @@ program
|
|
|
35
37
|
(0, install_js_1.registerInstall)(program);
|
|
36
38
|
(0, list_js_1.registerList)(program);
|
|
37
39
|
(0, uninstall_js_1.registerUninstall)(program);
|
|
40
|
+
(0, package_js_1.registerPackage)(program);
|
|
38
41
|
(0, publish_js_1.registerPublish)(program);
|
|
39
42
|
(0, login_js_1.registerLogin)(program);
|
|
40
43
|
(0, update_js_1.registerUpdate)(program);
|
|
@@ -46,4 +49,5 @@ program
|
|
|
46
49
|
(0, spaces_js_1.registerSpaces)(program);
|
|
47
50
|
(0, deploy_record_js_1.registerDeployRecord)(program);
|
|
48
51
|
(0, ping_js_1.registerPing)(program);
|
|
52
|
+
(0, access_js_1.registerAccess)(program);
|
|
49
53
|
program.parse();
|
|
@@ -57,13 +57,46 @@ function getGlobalCommandPathForTool(skillsDir, commandId) {
|
|
|
57
57
|
function formatCommandFile(content) {
|
|
58
58
|
return `---\ndescription: ${content.description}\n---\n\n${content.body}\n`;
|
|
59
59
|
}
|
|
60
|
-
// ───
|
|
61
|
-
const
|
|
62
|
-
###
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
// ─── 에러 처리 가이드 (결정적 실행 + 비결정적 조율) ───
|
|
61
|
+
const ERROR_HANDLING_GUIDE = `
|
|
62
|
+
### 에러 처리 가이드
|
|
63
|
+
|
|
64
|
+
CLI 명령 실행 후 JSON 에러가 반환되면 아래 기준에 따라 처리합니다.
|
|
65
|
+
**원칙: "되돌릴 수 없는 영향이 있는가?"로 판단합니다.**
|
|
66
|
+
|
|
67
|
+
#### 1. 자동 해결 (사용자에게 물어보지 않음)
|
|
68
|
+
되돌릴 수 있고, 부작용 없는 에러:
|
|
69
|
+
|
|
70
|
+
| 에러 코드 | 행동 |
|
|
71
|
+
|-----------|------|
|
|
72
|
+
| \`LOGIN_REQUIRED\` / \`NO_TOKEN\` | \`relay login\` 실행 (timeout 300초, 브라우저 자동 열림) → 성공 후 원래 명령 재시도 |
|
|
73
|
+
| \`NOT_INITIALIZED\` | \`relay init --all --json\` 실행 → 원래 명령 재시도 |
|
|
74
|
+
| \`FETCH_FAILED\` | 3초 대기 후 원래 명령 재시도 (최대 2회). 2회 실패 시 사용자에게 안내 |
|
|
75
|
+
|
|
76
|
+
#### 2. 사용자에게 선택지 제시 (AskUserQuestion)
|
|
77
|
+
\`options\` 필드가 있는 에러:
|
|
78
|
+
|
|
79
|
+
| 에러 코드 | 행동 |
|
|
80
|
+
|-----------|------|
|
|
81
|
+
| \`MISSING_VISIBILITY\` | options의 label을 선택지로 AskUserQuestion 호출 |
|
|
82
|
+
| \`MISSING_FIELD\` | fix 안내 + 사용자에게 값 입력 요청 |
|
|
83
|
+
| \`MISSING_TOOLS\` | options의 감지된 도구 목록을 선택지로 AskUserQuestion 호출 |
|
|
84
|
+
| \`MISSING_SPACE\` | options의 Space 목록을 선택지로 AskUserQuestion 호출 |
|
|
85
|
+
|
|
86
|
+
사용자가 선택하면, 선택된 값을 CLI 플래그에 반영하여 명령을 재호출합니다.
|
|
87
|
+
|
|
88
|
+
#### 3. 사용자에게 안내 (되돌릴 수 없는 에러)
|
|
89
|
+
구매, 접근 권한, 보안 관련:
|
|
90
|
+
|
|
91
|
+
| 에러 코드 | 행동 |
|
|
92
|
+
|-----------|------|
|
|
93
|
+
| \`GATED_ACCESS_REQUIRED\` | purchase_info의 message/url 표시 → "접근 코드가 있으신가요?" AskUserQuestion |
|
|
94
|
+
| \`SPACE_ONLY\` | Space 가입 필요 안내 → "초대 코드가 있으신가요?" AskUserQuestion |
|
|
95
|
+
| \`APPROVAL_REQUIRED\` | 승인 대기 안내 |
|
|
96
|
+
| \`NO_ACCESS\` | 접근 방법 안내 |
|
|
97
|
+
|
|
98
|
+
#### 4. 그 외 에러
|
|
99
|
+
\`fix\` 필드의 메시지를 사용자에게 전달하고, 필요하면 다음 행동을 제안합니다.`;
|
|
67
100
|
// ─── 명함 표시 포맷 ───
|
|
68
101
|
const BUSINESS_CARD_FORMAT = `
|
|
69
102
|
### 빌더 명함 표시
|
|
@@ -128,7 +161,7 @@ slug가 직접 주어지면 (\`/relay-install @alice/doc-writer\`) 이 단계를
|
|
|
128
161
|
|
|
129
162
|
**AskUserQuestion 호출:**
|
|
130
163
|
- question: "어디서 팀을 찾을까요?"
|
|
131
|
-
- options: Space가 있으면 \`["<space1_name>
|
|
164
|
+
- options: Space가 있으면 \`["<space1_name>", "<space2_name>", ...]\`, 없으면 이 단계를 건너뛰고 바로 내 Space 탐색으로 진행
|
|
132
165
|
|
|
133
166
|
**응답 처리:**
|
|
134
167
|
- Space 이름 선택 → 1-2. Space 팀 탐색으로 진행
|
|
@@ -177,13 +210,24 @@ slug가 직접 주어지면 (\`/relay-install @alice/doc-writer\`) 이 단계를
|
|
|
177
210
|
|
|
178
211
|
#### 2-1. 패키지 다운로드
|
|
179
212
|
\`relay install <@space/team> --json\` 명령어를 실행합니다.
|
|
180
|
-
- 공개
|
|
181
|
-
-
|
|
213
|
+
- 공개 팀 (public): \`relay install <@space/team> --json\`
|
|
214
|
+
- 링크 공유 팀 (gated): \`relay install <slug> --json\`
|
|
215
|
+
- 접근 권한이 없으면 CLI가 **purchase_info** (구매 안내 메시지 + URL)를 표시합니다.
|
|
216
|
+
- 접근 링크 코드가 있으면: \`relay access <slug> --code <code>\` 로 접근 부여 + 자동 설치를 한번에 수행합니다.
|
|
217
|
+
- Space 멤버이면 접근 확인 없이 바로 설치됩니다.
|
|
218
|
+
- Space 팀 (비공개/private): \`relay install @<space-slug>/<team-slug> --json\`
|
|
182
219
|
- Space 가입이 필요하면: \`relay join <space-slug> --code <invite-code>\` 를 먼저 실행합니다.
|
|
183
220
|
- 또는 \`--join-code <code>\`로 가입+설치를 한번에 할 수 있습니다.
|
|
184
221
|
- CLI가 init과 login을 자동으로 처리합니다 (사용자가 별도 실행할 필요 없음).
|
|
185
222
|
- JSON 출력에서 \`install_path\` (패키지 경로)를 확인합니다.
|
|
186
223
|
|
|
224
|
+
**gated 팀 접근 거부 처리:**
|
|
225
|
+
- CLI가 403 + \`GATED_ACCESS_REQUIRED\` 에러를 반환하면:
|
|
226
|
+
1. purchase_info의 message와 url을 사용자에게 표시합니다.
|
|
227
|
+
2. "접근 링크 코드가 있으신가요?"라고 물어봅니다.
|
|
228
|
+
3. 코드가 있으면 \`relay access <slug> --code <code>\`를 실행합니다.
|
|
229
|
+
4. 코드가 없으면 purchase_info의 url로 구매 안내합니다.
|
|
230
|
+
|
|
187
231
|
#### 2-2. 배치 범위 선택 (추천 포함)
|
|
188
232
|
|
|
189
233
|
팀의 성격을 분석하여 글로벌/로컬 중 적합한 쪽을 추천합니다.
|
|
@@ -283,7 +327,7 @@ requires 섹션이 없거나 비어있으면 이 단계를 건너뜁니다.
|
|
|
283
327
|
\`\`\`
|
|
284
328
|
⚠️ 필수 요구사항이 충족되지 않았습니다. 팀 기능이 제한될 수 있습니다.
|
|
285
329
|
\`\`\`
|
|
286
|
-
${
|
|
330
|
+
${ERROR_HANDLING_GUIDE}
|
|
287
331
|
|
|
288
332
|
### Step 3. 완료 & 팔로우 제안
|
|
289
333
|
|
|
@@ -308,17 +352,12 @@ ${BUSINESS_CARD_FORMAT}
|
|
|
308
352
|
\`\`\`
|
|
309
353
|
주변인에게 공유하세요:
|
|
310
354
|
|
|
311
|
-
|
|
312
|
-
/
|
|
313
|
-
|
|
314
|
-
상세페이지: relayax.com/@{owner}/{team}
|
|
355
|
+
아래 가이드를 읽고 그대로 따라해줘:
|
|
356
|
+
https://relayax.com/api/registry/{owner}/{slug}/guide.md
|
|
315
357
|
\`\`\`
|
|
316
358
|
|
|
317
|
-
- \`{slug}\`가 \`@owner/team\` 형식이면
|
|
318
|
-
|
|
319
|
-
- type 기반 사용법도 포함:
|
|
320
|
-
- command/hybrid: \`# 사용법: /{첫번째 커맨드}\`
|
|
321
|
-
- passive: \`# 설치하면 자동 적용됩니다\`
|
|
359
|
+
- \`{slug}\`가 \`@owner/team\` 형식이면 \`{owner}\`과 \`{slug}\`를 분리하여 URL을 구성합니다.
|
|
360
|
+
- CLI가 이미 설치된 사용자를 위한 짧은 버전도 함께 표시합니다: \`/relay:relay-install {slug}\`
|
|
322
361
|
|
|
323
362
|
#### 3-4. 사용 제안
|
|
324
363
|
- "바로 사용해볼까요?" 제안
|
|
@@ -332,8 +371,8 @@ npm install -g relayax-cli
|
|
|
332
371
|
|
|
333
372
|
### 인터랙티브 모드 (/relay-install)
|
|
334
373
|
→ relay spaces --json 실행
|
|
335
|
-
→ AskUserQuestion: "어디서 팀을 찾을까요?" → ["
|
|
336
|
-
→ "
|
|
374
|
+
→ AskUserQuestion: "어디서 팀을 찾을까요?" → ["Alice's Space (alice)", "Acme Corp"]
|
|
375
|
+
→ "Alice's Space" 선택 → "어떤 팀을 찾고 계세요?"
|
|
337
376
|
→ relay search "문서" 실행 → 결과 리스트 표시
|
|
338
377
|
→ AskUserQuestion: "어떤 팀을 설치할까요?" → ["1", "2", "3", "다시 검색"]
|
|
339
378
|
→ "1" 선택 (@alice/doc-writer)
|
|
@@ -400,7 +439,6 @@ npm install -g relayax-cli
|
|
|
400
439
|
"slug": "my-space",
|
|
401
440
|
"name": "내 스페이스",
|
|
402
441
|
"description": "설명",
|
|
403
|
-
"is_personal": false,
|
|
404
442
|
"role": "owner"
|
|
405
443
|
}
|
|
406
444
|
]
|
|
@@ -408,9 +446,8 @@ npm install -g relayax-cli
|
|
|
408
446
|
\`\`\`
|
|
409
447
|
|
|
410
448
|
**표시:**
|
|
411
|
-
- \`
|
|
412
|
-
|
|
413
|
-
${LOGIN_JIT_GUIDE}
|
|
449
|
+
- \`role\`: owner → 소유자, builder → 빌더, member → 멤버
|
|
450
|
+
${ERROR_HANDLING_GUIDE}
|
|
414
451
|
- Spaces 조회 실패해도 설치된 팀 목록은 정상 표시합니다 (로컬 데이터).
|
|
415
452
|
|
|
416
453
|
### 3. Space 팀 목록 (옵션)
|
|
@@ -469,58 +506,154 @@ ${LOGIN_JIT_GUIDE}
|
|
|
469
506
|
description: '현재 팀 패키지를 relay Space에 배포합니다',
|
|
470
507
|
body: `현재 디렉토리의 에이전트 팀(.relay/)을 분석하고, 보안 점검 및 requirements를 구성한 뒤, 사용가이드를 생성하고 relay Space에 배포합니다.
|
|
471
508
|
|
|
472
|
-
## 사전 준비
|
|
509
|
+
## 사전 준비
|
|
510
|
+
|
|
511
|
+
### 0-1. 인증 확인
|
|
512
|
+
|
|
513
|
+
- \`relay status --json\` 명령어를 실행하여 로그인 상태를 확인합니다.
|
|
514
|
+
- 인증되어 있으면 다음 단계로 진행합니다.
|
|
515
|
+
- 미인증이면 바로 로그인을 진행합니다:
|
|
516
|
+
1. \`relay login\` 실행 (timeout 300초)
|
|
517
|
+
- 브라우저가 자동으로 열리고, 사용자가 로그인을 완료하면 토큰이 자동 저장됩니다.
|
|
518
|
+
2. 완료 후 \`relay status --json\`으로 로그인 성공을 확인합니다.
|
|
519
|
+
|
|
520
|
+
### 0-2. 소스 패키징 (source → .relay/)
|
|
521
|
+
|
|
522
|
+
\`relay package\` CLI 명령을 사용하여 소스 디렉토리의 콘텐츠를 .relay/로 동기화합니다.
|
|
523
|
+
소스 탐색과 파일 비교는 CLI가 결정적으로 처리하고, 에이전트는 결과를 사용자에게 보여주고 흐름을 조율합니다.
|
|
524
|
+
|
|
525
|
+
#### A. 최초 배포 (.relay/relay.yaml이 없음)
|
|
526
|
+
|
|
527
|
+
##### 1단계: 소스 탐색
|
|
528
|
+
|
|
529
|
+
\`relay package --init --json\` 실행
|
|
530
|
+
- CLI가 프로젝트에서 에이전트 CLI 디렉토리(.claude/, .codex/, .gemini/ 등)를 자동 탐색합니다.
|
|
531
|
+
- JSON 결과의 \`detected\` 배열에 각 디렉토리별 콘텐츠 요약이 포함됩니다:
|
|
532
|
+
\`\`\`json
|
|
533
|
+
{
|
|
534
|
+
"status": "init_required",
|
|
535
|
+
"detected": [
|
|
536
|
+
{ "source": ".claude", "name": "Claude Code", "summary": { "skills": 2, "commands": 3 }, "fileCount": 8 },
|
|
537
|
+
{ "source": ".codex", "name": "Codex", "summary": { "agents": 1 }, "fileCount": 2 }
|
|
538
|
+
]
|
|
539
|
+
}
|
|
540
|
+
\`\`\`
|
|
541
|
+
|
|
542
|
+
- **detected가 0개** → "배포 가능한 에이전트 콘텐츠가 없습니다. skills/이나 commands/를 먼저 만들어주세요." 안내 후 중단
|
|
473
543
|
|
|
474
|
-
|
|
544
|
+
##### 2단계: 콘텐츠 분석 & 팀 포지셔닝
|
|
475
545
|
|
|
476
|
-
|
|
546
|
+
detected된 소스 디렉토리의 skills/, commands/, agents/, rules/ 파일 **내용을 직접 읽어** 팀의 정체성을 파악합니다.
|
|
477
547
|
|
|
478
|
-
|
|
548
|
+
**분석 관점:**
|
|
549
|
+
- 이 팀이 **무엇을 하는 팀**인지 (코드 리뷰? QA? 문서 생성? 데이터 분석?)
|
|
550
|
+
- 어떤 **기술 스택/도메인**에 특화되어 있는지 (Supabase? React? Python?)
|
|
551
|
+
- 설치자에게 **어떤 가치**를 제공하는지
|
|
479
552
|
|
|
480
|
-
|
|
553
|
+
이 분석을 기반으로 팀을 하나의 "제품"으로 포지셔닝합니다.
|
|
481
554
|
|
|
482
|
-
|
|
555
|
+
**중요: 소스 디렉토리 이름(.claude 등)은 인프라 디테일이므로 사용자에게 노출하지 않습니다.**
|
|
556
|
+
사용자에게는 팀의 기능과 정체성 중심으로 질문합니다.
|
|
483
557
|
|
|
484
|
-
|
|
485
|
-
- question: "팀 이름을 입력해주세요"
|
|
486
|
-
- 기본값으로 현재 디렉토리명을 제안합니다.
|
|
558
|
+
##### 3단계: 배포 제안
|
|
487
559
|
|
|
488
|
-
|
|
489
|
-
- question: "팀을 한 줄로 설명해주세요 (Space에 표시됩니다)"
|
|
490
|
-
- 기본값으로 적절한 값을 만들어줍니다.
|
|
560
|
+
분석 결과를 바탕으로 팀 배포를 제안합니다.
|
|
491
561
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
562
|
+
**detected가 1개일 때:**
|
|
563
|
+
|
|
564
|
+
**AskUserQuestion 호출:**
|
|
565
|
+
- question: 콘텐츠 분석 기반의 포지셔닝 질문
|
|
566
|
+
- 예시:
|
|
567
|
+
- "Supabase 웹 개발팀으로 배포할까요? (skills 2개, commands 3개)"
|
|
568
|
+
- "코드 리뷰 자동화 팀으로 배포할까요? (skills 1개, agents 2개)"
|
|
569
|
+
- "Next.js QA 테스트팀으로 배포할까요? (commands 5개)"
|
|
570
|
+
- options: \`["배포", "취소"]\`
|
|
571
|
+
|
|
572
|
+
**detected가 여러 개일 때:**
|
|
573
|
+
각 소스의 콘텐츠를 분석하여 서로 다른 팀으로 포지셔닝합니다.
|
|
574
|
+
|
|
575
|
+
**AskUserQuestion 호출:**
|
|
576
|
+
- question: "어떤 팀으로 배포할까요?"
|
|
577
|
+
- options: 콘텐츠 기반 설명 (예: \`["Supabase 웹 개발팀 (skills 2개, commands 3개)", "QA 자동화 에이전트 (agents 1개)"]\`)
|
|
578
|
+
- 디렉토리 이름은 내부적으로만 매핑하고, 사용자에게는 팀의 기능으로 보여줍니다.
|
|
579
|
+
|
|
580
|
+
##### 4단계: 팀 정보 확정
|
|
581
|
+
|
|
582
|
+
포지셔닝 분석을 기반으로 팀 이름과 설명을 제안합니다.
|
|
583
|
+
|
|
584
|
+
**AskUserQuestion 호출:**
|
|
585
|
+
- question: "팀 이름을 확인해주세요"
|
|
586
|
+
- 분석된 포지셔닝에서 자연스러운 팀 이름을 제안합니다 (예: "supabase-web-dev", "code-reviewer")
|
|
587
|
+
- 현재 디렉토리명이 아닌, **콘텐츠 기반** 이름을 기본값으로 제시합니다.
|
|
588
|
+
|
|
589
|
+
**AskUserQuestion 호출:**
|
|
590
|
+
- question: "팀 설명을 확인해주세요 (Space에 표시됩니다)"
|
|
591
|
+
- 분석한 콘텐츠를 기반으로 설치자 관점의 설명을 제안합니다.
|
|
592
|
+
- 좋은 예: "Supabase 기반 웹앱의 DB 마이그레이션, API 개발, 테스트를 자동화합니다"
|
|
593
|
+
- 나쁜 예: ".claude 디렉토리의 skills와 commands를 패키징한 팀"
|
|
594
|
+
|
|
595
|
+
##### 5단계: 초기화 & 패키징
|
|
596
|
+
|
|
597
|
+
자동 처리:
|
|
598
|
+
- \`.relay/relay.yaml\` 생성:
|
|
599
|
+
\`\`\`yaml
|
|
600
|
+
name: <확정된 이름>
|
|
601
|
+
slug: <이름에서 자동 생성 — 소문자, 특수문자→하이픈>
|
|
602
|
+
description: <확정된 설명>
|
|
603
|
+
source: <선택된 소스 디렉토리> # 예: .claude (내부용)
|
|
604
|
+
version: 1.0.0
|
|
605
|
+
tags: []
|
|
606
|
+
\`\`\`
|
|
607
|
+
- \`relay package --source <선택된 소스> --sync --json\` 실행하여 콘텐츠를 .relay/로 복사
|
|
608
|
+
- \`relay init --auto\` 실행하여 글로벌 커맨드 설치 보장
|
|
609
|
+
- 결과를 사용자에게 표시
|
|
610
|
+
|
|
611
|
+
#### B. 재배포 (.relay/relay.yaml이 있음)
|
|
612
|
+
|
|
613
|
+
1. \`relay package --json\` 실행
|
|
614
|
+
- CLI가 relay.yaml의 \`source\` 필드를 읽고, 소스 디렉토리와 .relay/를 파일 해시로 비교합니다.
|
|
615
|
+
- JSON 결과 예시:
|
|
616
|
+
\`\`\`json
|
|
617
|
+
{
|
|
618
|
+
"source": ".claude",
|
|
619
|
+
"sourceName": "Claude Code",
|
|
620
|
+
"synced": false,
|
|
621
|
+
"diff": [
|
|
622
|
+
{ "relPath": "skills/code-review/SKILL.md", "status": "modified" },
|
|
623
|
+
{ "relPath": "commands/deploy.md", "status": "added" },
|
|
624
|
+
{ "relPath": "commands/old-cmd.md", "status": "deleted" }
|
|
625
|
+
],
|
|
626
|
+
"summary": { "added": 1, "modified": 1, "deleted": 1, "unchanged": 5 }
|
|
627
|
+
}
|
|
501
628
|
\`\`\`
|
|
502
|
-
- \`relay init --update\` 실행하여 글로벌 커맨드 설치 보장
|
|
503
629
|
|
|
504
|
-
|
|
630
|
+
2. **변경이 있으면** (added + modified + deleted > 0) → diff를 사용자에게 보여줍니다:
|
|
631
|
+
\`\`\`
|
|
632
|
+
📦 소스 동기화 (.claude/ → .relay/)
|
|
633
|
+
변경: skills/code-review/SKILL.md
|
|
634
|
+
신규: commands/deploy.md
|
|
635
|
+
삭제: commands/old-cmd.md
|
|
636
|
+
유지: 5개 파일
|
|
637
|
+
\`\`\`
|
|
505
638
|
|
|
506
|
-
|
|
639
|
+
**AskUserQuestion 호출:**
|
|
640
|
+
- question: "소스 변경사항을 .relay/에 반영할까요?"
|
|
641
|
+
- options: \`["반영", "변경 확인", "건너뛰기"]\`
|
|
507
642
|
|
|
508
|
-
|
|
509
|
-
-
|
|
510
|
-
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
643
|
+
**응답 처리:**
|
|
644
|
+
- "반영" → \`relay package --sync --json\` 실행하여 동기화
|
|
645
|
+
- "변경 확인" → 변경된 파일의 내용을 직접 읽어 diff를 상세히 보여준 후 다시 AskUserQuestion
|
|
646
|
+
- "건너뛰기" → 현재 .relay/ 그대로 배포
|
|
647
|
+
|
|
648
|
+
3. **변경이 없으면** → "✓ 소스와 동기화 상태입니다." 표시 후 다음 단계로
|
|
514
649
|
|
|
515
|
-
|
|
516
|
-
- .relay/ 디렉토리의 skills/, agents/, rules/, commands/를 탐색합니다.
|
|
517
|
-
- 각 파일의 이름과 description을 추출합니다.
|
|
650
|
+
4. \`source\` 필드가 없으면 → .relay/ 내 콘텐츠를 직접 편집하는 모드로 간주하고 동기화를 건너뜁니다.
|
|
518
651
|
|
|
519
652
|
## 인터랙션 플로우
|
|
520
653
|
|
|
521
|
-
이 커맨드는
|
|
654
|
+
이 커맨드는 4단계 인터랙션으로 진행됩니다. 각 단계에서 반드시 AskUserQuestion 도구를 사용하세요.
|
|
522
655
|
|
|
523
|
-
### Step
|
|
656
|
+
### Step 1. 버전 범프
|
|
524
657
|
|
|
525
658
|
relay.yaml의 현재 \`version\`을 읽고 semver 범프를 제안합니다.
|
|
526
659
|
|
|
@@ -541,14 +674,14 @@ relay.yaml의 \`visibility\` 설정을 확인합니다.
|
|
|
541
674
|
#### 신규 배포 (visibility 미설정)
|
|
542
675
|
|
|
543
676
|
**AskUserQuestion 호출:**
|
|
544
|
-
- question: "
|
|
545
|
-
- options: \`["공개
|
|
677
|
+
- question: "공개 범위를 선택하세요"
|
|
678
|
+
- options: \`["공개 — 누구나 설치", "링크 공유 — 접근 링크가 있는 사람만 설치", "비공개 — Space 멤버만"]\`
|
|
546
679
|
|
|
547
680
|
**응답 처리:**
|
|
548
681
|
- "공개" → relay.yaml에 \`visibility: public\` 저장
|
|
682
|
+
- "링크 공유" → relay.yaml에 \`visibility: gated\` 저장. 배포 후 웹 대시보드(/dashboard)에서 접근 링크를 생성하고 구매 안내를 설정할 수 있다고 안내.
|
|
549
683
|
- "비공개" → \`relay spaces --json\` 실행 후 Space 목록 표시
|
|
550
|
-
-
|
|
551
|
-
- 팀 Space가 0개이면: "비공개 배포하려면 팀 Space가 필요합니다. www.relayax.com/spaces 에서 Space를 생성하세요."라고 안내하고 중단합니다.
|
|
684
|
+
- Space가 0개이면: "비공개 배포하려면 Space가 필요합니다. www.relayax.com/spaces 에서 Space를 생성하세요."라고 안내하고 중단합니다.
|
|
552
685
|
|
|
553
686
|
**AskUserQuestion 호출 (Space가 1개여도 반드시 호출):**
|
|
554
687
|
- question: "어떤 Space에 배포할까요?"
|
|
@@ -562,7 +695,7 @@ relay.yaml의 \`visibility\` 설정을 확인합니다.
|
|
|
562
695
|
현재 설정을 확인합니다:
|
|
563
696
|
|
|
564
697
|
**AskUserQuestion 호출:**
|
|
565
|
-
- question: 공개일 때 "현재 **공개** 설정입니다.
|
|
698
|
+
- question: 공개일 때 "현재 **공개** 설정입니다. 유지할까요?", 링크공유일 때 "현재 **링크 공유** 설정입니다. 접근 링크가 있는 사람만 설치 가능합니다. 유지할까요?", 비공개일 때 "현재 **비공개** 설정입니다 (Space: {name}). 유지할까요?"
|
|
566
699
|
- options: \`["유지", "변경"]\`
|
|
567
700
|
|
|
568
701
|
"변경" → 신규 배포와 동일한 플로우
|
|
@@ -676,12 +809,13 @@ requires: env 2개, cli 1개
|
|
|
676
809
|
- 출력에 코드블록이 없으면 아래 형태로 직접 생성합니다:
|
|
677
810
|
|
|
678
811
|
\\\`\\\`\\\`
|
|
679
|
-
|
|
680
|
-
/
|
|
812
|
+
아래 가이드를 읽고 그대로 따라해줘:
|
|
813
|
+
https://relayax.com/api/registry/{owner}/{slug}/guide.md
|
|
681
814
|
\\\`\\\`\\\`
|
|
682
815
|
|
|
683
|
-
-
|
|
684
|
-
- "이 블록을 팀원에게 공유하면
|
|
816
|
+
- \`{owner}\`과 \`{slug}\`는 배포된 팀의 실제 슬러그에서 추출합니다 (\`@owner/slug\` → \`owner\`, \`slug\`).
|
|
817
|
+
- "이 블록을 팀원에게 공유하면 Claude가 환경 체크부터 설치까지 자동으로 해줍니다"라고 안내합니다.
|
|
818
|
+
- CLI가 이미 설치된 사용자를 위한 짧은 버전도 함께 표시: \`/relay:relay-install <slug>\`
|
|
685
819
|
${BUSINESS_CARD_FORMAT}
|
|
686
820
|
|
|
687
821
|
## 예시
|
|
@@ -697,10 +831,11 @@ ${BUSINESS_CARD_FORMAT}
|
|
|
697
831
|
→ AskUserQuestion: "이대로 배포할까요?" → ["배포", "취소"]
|
|
698
832
|
→ "배포" → relay publish 실행
|
|
699
833
|
→ "배포 완료! URL: https://relayax.com/@my-space/my-team"
|
|
700
|
-
→ 온보딩 가이드 코드블록
|
|
834
|
+
→ 온보딩 가이드 코드블록 표시
|
|
835
|
+
${ERROR_HANDLING_GUIDE}`,
|
|
701
836
|
},
|
|
702
837
|
];
|
|
703
838
|
// ─── Builder Commands (로컬 설치) ───
|
|
704
839
|
// relay-publish가 글로벌로 승격되어 현재 비어있음.
|
|
705
|
-
// relay init --
|
|
840
|
+
// relay init --auto만 실행하면 모든 커맨드가 한번에 업데이트됨.
|
|
706
841
|
exports.BUILDER_COMMANDS = [];
|
package/dist/lib/guide.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import type { Requires } from '../commands/publish.js';
|
|
2
|
+
interface CommandEntry {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function generateGuide(config: {
|
|
7
|
+
slug: string;
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
version: string;
|
|
11
|
+
visibility?: string;
|
|
12
|
+
}, commands: CommandEntry[], requires?: Requires): string;
|
|
13
|
+
export {};
|