relayax-cli 0.3.42 → 0.3.44
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/init.js +3 -5
- package/dist/commands/login.js +47 -2
- package/dist/commands/uninstall.js +20 -2
- package/dist/lib/command-adapter.d.ts +4 -3
- package/dist/lib/command-adapter.js +7 -6
- package/dist/lib/guide.js +5 -2
- package/dist/prompts/_error-handling.md +6 -6
- package/dist/prompts/_setup-cli.md +7 -0
- package/dist/prompts/_setup-login.md +29 -1
- package/dist/prompts/_setup-org.md +0 -2
- package/dist/prompts/install.md +21 -17
- package/dist/prompts/publish.md +23 -19
- package/package.json +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -69,10 +69,8 @@ function installGlobalUserCommands() {
|
|
|
69
69
|
const currentIds = new Set(command_adapter_js_1.USER_COMMANDS.map((c) => c.id));
|
|
70
70
|
const commands = [];
|
|
71
71
|
const tools = [];
|
|
72
|
-
// 감지된 CLI가 없으면
|
|
73
|
-
const targetDirs = globalCLIs.
|
|
74
|
-
? globalCLIs.map((t) => ({ name: t.name, dir: (0, command_adapter_js_1.getGlobalCommandDirForTool)(t.skillsDir), getPath: (id) => (0, command_adapter_js_1.getGlobalCommandPathForTool)(t.skillsDir, id) }))
|
|
75
|
-
: [{ name: 'Claude Code', dir: (0, command_adapter_js_1.getGlobalCommandDir)(), getPath: (id) => (0, command_adapter_js_1.getGlobalCommandPath)(id) }];
|
|
72
|
+
// 감지된 CLI가 없으면 설치하지 않음 (사용자가 --tools로 지정하거나 CLI를 먼저 설치해야 함)
|
|
73
|
+
const targetDirs = globalCLIs.map((t) => ({ name: t.name, dir: (0, command_adapter_js_1.getGlobalCommandDirForTool)(t.skillsDir), getPath: (id) => (0, command_adapter_js_1.getGlobalCommandPathForTool)(t.skillsDir, id) }));
|
|
76
74
|
for (const target of targetDirs) {
|
|
77
75
|
fs_1.default.mkdirSync(target.dir, { recursive: true });
|
|
78
76
|
// 기존 파일 중 현재 목록에 없는 것 제거
|
|
@@ -247,7 +245,7 @@ function registerInit(program) {
|
|
|
247
245
|
console.log(`\n\x1b[32m✓ relay 초기화 완료\x1b[0m\n`);
|
|
248
246
|
// 글로벌
|
|
249
247
|
{
|
|
250
|
-
const toolNames = globalTools.length > 0 ? globalTools.join(', ') : '
|
|
248
|
+
const toolNames = globalTools.length > 0 ? globalTools.join(', ') : '(감지된 CLI 없음)';
|
|
251
249
|
console.log(` \x1b[36mUser 커맨드 (글로벌)\x1b[0m — ${globalStatus === 'updated' ? '업데이트됨' : '설치됨'}`);
|
|
252
250
|
console.log(` 감지된 CLI: \x1b[36m${toolNames}\x1b[0m`);
|
|
253
251
|
for (const cmd of command_adapter_js_1.USER_COMMANDS) {
|
package/dist/commands/login.js
CHANGED
|
@@ -119,6 +119,46 @@ async function loginWithBrowser(json) {
|
|
|
119
119
|
}
|
|
120
120
|
return waitForToken(port);
|
|
121
121
|
}
|
|
122
|
+
async function loginWithDevice(json) {
|
|
123
|
+
const res = await fetch(`${config_js_1.API_URL}/api/auth/device/request`, { method: 'POST' });
|
|
124
|
+
if (!res.ok) {
|
|
125
|
+
throw new Error('Device code 발급에 실패했습니다');
|
|
126
|
+
}
|
|
127
|
+
const { device_code, user_code, verification_url, expires_in } = await res.json();
|
|
128
|
+
if (json) {
|
|
129
|
+
console.error(JSON.stringify({ status: 'waiting', verification_url, user_code, expires_in }));
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
console.error(`\n아래 URL에서 코드를 입력하세요:\n`);
|
|
133
|
+
console.error(` ${verification_url}`);
|
|
134
|
+
console.error(`\n 코드: \x1b[1m${user_code}\x1b[0m\n`);
|
|
135
|
+
}
|
|
136
|
+
openBrowser(`${verification_url}?user_code=${user_code}`);
|
|
137
|
+
const deadline = Date.now() + expires_in * 1000;
|
|
138
|
+
while (Date.now() < deadline) {
|
|
139
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
140
|
+
const pollRes = await fetch(`${config_js_1.API_URL}/api/auth/device/poll`, {
|
|
141
|
+
method: 'POST',
|
|
142
|
+
headers: { 'Content-Type': 'application/json' },
|
|
143
|
+
body: JSON.stringify({ device_code }),
|
|
144
|
+
});
|
|
145
|
+
if (!pollRes.ok)
|
|
146
|
+
continue;
|
|
147
|
+
const data = await pollRes.json();
|
|
148
|
+
if (data.status === 'approved' && data.token) {
|
|
149
|
+
return {
|
|
150
|
+
token: data.token,
|
|
151
|
+
refresh_token: data.refresh_token,
|
|
152
|
+
expires_at: data.expires_at ? Number(data.expires_at) : undefined,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (data.status === 'expired') {
|
|
156
|
+
throw new Error('코드가 만료되었습니다. 다시 시도하세요.');
|
|
157
|
+
}
|
|
158
|
+
// pending — continue polling
|
|
159
|
+
}
|
|
160
|
+
throw new Error('로그인 시간이 초과되었습니다 (5분)');
|
|
161
|
+
}
|
|
122
162
|
/**
|
|
123
163
|
* 대화형 로그인 플로우 실행 (auto-login에서 호출).
|
|
124
164
|
* 브라우저에서 로그인 페이지를 열고 토큰을 받아 저장.
|
|
@@ -139,6 +179,7 @@ function registerLogin(program) {
|
|
|
139
179
|
.command('login')
|
|
140
180
|
.description('RelayAX 계정에 로그인합니다')
|
|
141
181
|
.option('--token <token>', '직접 토큰 입력 (브라우저 없이)')
|
|
182
|
+
.option('--device', 'Device code 방식으로 로그인 (샌드박스/원격 환경용)')
|
|
142
183
|
.action(async (opts) => {
|
|
143
184
|
const json = program.opts().json ?? false;
|
|
144
185
|
(0, config_js_1.ensureGlobalRelayDir)();
|
|
@@ -146,8 +187,9 @@ function registerLogin(program) {
|
|
|
146
187
|
let refreshToken;
|
|
147
188
|
let expiresAt;
|
|
148
189
|
if (!accessToken) {
|
|
190
|
+
const loginFn = opts.device ? loginWithDevice : loginWithBrowser;
|
|
149
191
|
try {
|
|
150
|
-
const loginResult = await
|
|
192
|
+
const loginResult = await loginFn(json);
|
|
151
193
|
accessToken = loginResult.token;
|
|
152
194
|
refreshToken = loginResult.refresh_token;
|
|
153
195
|
expiresAt = loginResult.expires_at;
|
|
@@ -155,10 +197,13 @@ function registerLogin(program) {
|
|
|
155
197
|
catch (err) {
|
|
156
198
|
const msg = err instanceof Error ? err.message : '로그인 실패';
|
|
157
199
|
if (json) {
|
|
158
|
-
console.error(JSON.stringify({ error: 'LOGIN_FAILED', message: msg, fix: '
|
|
200
|
+
console.error(JSON.stringify({ error: 'LOGIN_FAILED', message: msg, fix: opts.device ? '다시 시도하세요.' : 'relay login --device를 시도하세요.' }));
|
|
159
201
|
}
|
|
160
202
|
else {
|
|
161
203
|
console.error(`\x1b[31m오류: ${msg}\x1b[0m`);
|
|
204
|
+
if (!opts.device) {
|
|
205
|
+
console.error(`\n\x1b[33m팁: 브라우저 콜백이 안 되는 환경이라면 relay login --device를 시도하세요.\x1b[0m`);
|
|
206
|
+
}
|
|
162
207
|
}
|
|
163
208
|
process.exit(1);
|
|
164
209
|
}
|
|
@@ -9,6 +9,24 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const config_js_1 = require("../lib/config.js");
|
|
10
10
|
const installer_js_1 = require("../lib/installer.js");
|
|
11
11
|
const slug_js_1 = require("../lib/slug.js");
|
|
12
|
+
const ai_tools_js_1 = require("../lib/ai-tools.js");
|
|
13
|
+
/**
|
|
14
|
+
* deployed_files에서 에이전트 설정 디렉토리(skillsDir) 기반 boundary를 추론한다.
|
|
15
|
+
* 예: deployed_files에 '~/.cursor/commands/relay/x.md'가 있으면 boundary는 basePath/.cursor
|
|
16
|
+
*/
|
|
17
|
+
function inferBoundary(deployedFiles, basePath) {
|
|
18
|
+
const skillsDirs = ai_tools_js_1.AI_TOOLS.map((t) => t.skillsDir);
|
|
19
|
+
for (const f of deployedFiles) {
|
|
20
|
+
for (const sd of skillsDirs) {
|
|
21
|
+
const prefix = path_1.default.join(basePath, sd);
|
|
22
|
+
if (f.startsWith(prefix)) {
|
|
23
|
+
return prefix;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// fallback: 첫 번째 파일의 상위 디렉토리 중 basePath 직속 디렉토리
|
|
28
|
+
return path_1.default.join(basePath, '.claude');
|
|
29
|
+
}
|
|
12
30
|
function registerUninstall(program) {
|
|
13
31
|
program
|
|
14
32
|
.command('uninstall <slug>')
|
|
@@ -52,7 +70,7 @@ function registerUninstall(program) {
|
|
|
52
70
|
const deployedRemoved = (0, installer_js_1.uninstallAgent)(localEntry.deployed_files);
|
|
53
71
|
totalRemoved += deployedRemoved.length;
|
|
54
72
|
// Clean empty parent directories
|
|
55
|
-
const boundary =
|
|
73
|
+
const boundary = inferBoundary(localEntry.deployed_files, process.cwd());
|
|
56
74
|
for (const f of deployedRemoved) {
|
|
57
75
|
(0, installer_js_1.cleanEmptyParents)(f, boundary);
|
|
58
76
|
}
|
|
@@ -72,7 +90,7 @@ function registerUninstall(program) {
|
|
|
72
90
|
const deployedRemoved = (0, installer_js_1.uninstallAgent)(globalEntry.deployed_files);
|
|
73
91
|
totalRemoved += deployedRemoved.length;
|
|
74
92
|
// Clean empty parent directories
|
|
75
|
-
const boundary =
|
|
93
|
+
const boundary = inferBoundary(globalEntry.deployed_files, os_1.default.homedir());
|
|
76
94
|
for (const f of deployedRemoved) {
|
|
77
95
|
(0, installer_js_1.cleanEmptyParents)(f, boundary);
|
|
78
96
|
}
|
|
@@ -15,12 +15,13 @@ export interface ToolCommandAdapter {
|
|
|
15
15
|
*/
|
|
16
16
|
export declare function createAdapter(tool: AITool): ToolCommandAdapter;
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* @deprecated getGlobalCommandPathForTool(skillsDir, commandId)를 사용하세요.
|
|
19
|
+
* Claude Code 전용 경로. 멀티 에이전트 지원 시 ForTool 버전 사용 필요.
|
|
20
20
|
*/
|
|
21
21
|
export declare function getGlobalCommandPath(commandId: string): string;
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* @deprecated getGlobalCommandDirForTool(skillsDir)를 사용하세요.
|
|
24
|
+
* Claude Code 전용 경로. 멀티 에이전트 지원 시 ForTool 버전 사용 필요.
|
|
24
25
|
*/
|
|
25
26
|
export declare function getGlobalCommandDir(): string;
|
|
26
27
|
/**
|
|
@@ -27,17 +27,18 @@ function createAdapter(tool) {
|
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
30
|
+
* @deprecated getGlobalCommandPathForTool(skillsDir, commandId)를 사용하세요.
|
|
31
|
+
* Claude Code 전용 경로. 멀티 에이전트 지원 시 ForTool 버전 사용 필요.
|
|
32
32
|
*/
|
|
33
33
|
function getGlobalCommandPath(commandId) {
|
|
34
|
-
return
|
|
34
|
+
return getGlobalCommandPathForTool('.claude', commandId);
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
37
|
+
* @deprecated getGlobalCommandDirForTool(skillsDir)를 사용하세요.
|
|
38
|
+
* Claude Code 전용 경로. 멀티 에이전트 지원 시 ForTool 버전 사용 필요.
|
|
38
39
|
*/
|
|
39
40
|
function getGlobalCommandDir() {
|
|
40
|
-
return
|
|
41
|
+
return getGlobalCommandDirForTool('.claude');
|
|
41
42
|
}
|
|
42
43
|
/**
|
|
43
44
|
* 특정 AI 도구의 글로벌 커맨드 디렉토리.
|
|
@@ -166,7 +167,7 @@ ${index_js_1.ERROR_HANDLING_GUIDE}
|
|
|
166
167
|
1. \`relay uninstall <@author/slug> --json\` 명령어를 실행합니다.
|
|
167
168
|
2. CLI가 자동으로 처리하는 것:
|
|
168
169
|
- \`.relay/agents/\` 패키지 삭제
|
|
169
|
-
- \`deployed_files\`에 기록된 배치 파일 삭제 (
|
|
170
|
+
- \`deployed_files\`에 기록된 배치 파일 삭제 (에이전트 설정 디렉토리 내)
|
|
170
171
|
- 빈 상위 디렉토리 정리
|
|
171
172
|
- installed.json에서 항목 제거 (글로벌/로컬 양쪽)
|
|
172
173
|
3. 삭제 결과를 보여줍니다 (에이전트 이름, 제거된 파일 수).
|
package/dist/lib/guide.js
CHANGED
|
@@ -79,8 +79,11 @@ ${commands.map((cmd) => `- \`/${cmd.name}\`: ${cmd.description}`).join('\n')}
|
|
|
79
79
|
|
|
80
80
|
> ${config.description}
|
|
81
81
|
|
|
82
|
-
이 가이드는
|
|
83
|
-
|
|
82
|
+
이 가이드는 Relay 에이전트를 설치하는 절차입니다.
|
|
83
|
+
각 Step의 명령어를 순서대로 실행하세요. 요약하지 말고 바로 Step 1부터 시작하세요.
|
|
84
|
+
|
|
85
|
+
터미널(bash)을 실행할 수 없는 환경이라면, 아래 도구에서 이 URL을 열고 실행하세요:
|
|
86
|
+
Claude Code · Claude Cowork · Codex · Cursor · Antigravity
|
|
84
87
|
|
|
85
88
|
${setupSection}
|
|
86
89
|
|
|
@@ -12,15 +12,15 @@ CLI 명령 실행 후 JSON 에러가 반환되면 아래 기준에 따라 처리
|
|
|
12
12
|
| `NOT_INITIALIZED` | `relay init --all --json` 실행 → 원래 명령 재시도 |
|
|
13
13
|
| `FETCH_FAILED` | 3초 대기 후 원래 명령 재시도 (최대 2회). 2회 실패 시 사용자에게 안내 |
|
|
14
14
|
|
|
15
|
-
#### 2. 사용자에게 선택지 제시 (
|
|
15
|
+
#### 2. 사용자에게 선택지 제시 (사용자 질문 도구)
|
|
16
16
|
`options` 필드가 있는 에러:
|
|
17
17
|
|
|
18
18
|
| 에러 코드 | 행동 |
|
|
19
19
|
|-----------|------|
|
|
20
|
-
| `MISSING_VISIBILITY` | options의 label을 선택지로
|
|
20
|
+
| `MISSING_VISIBILITY` | options의 label을 선택지로 사용자 질문 도구 호출 |
|
|
21
21
|
| `MISSING_FIELD` | fix 안내 + 사용자에게 값 입력 요청 |
|
|
22
|
-
| `MISSING_TOOLS` | options의 감지된 도구 목록을 선택지로
|
|
23
|
-
| `MISSING_SPACE` | options의 Space 목록을 선택지로
|
|
22
|
+
| `MISSING_TOOLS` | options의 감지된 도구 목록을 선택지로 사용자 질문 도구 호출 |
|
|
23
|
+
| `MISSING_SPACE` | options의 Space 목록을 선택지로 사용자 질문 도구 호출 |
|
|
24
24
|
|
|
25
25
|
사용자가 선택하면, 선택된 값을 CLI 플래그에 반영하여 명령을 재호출합니다.
|
|
26
26
|
|
|
@@ -29,8 +29,8 @@ CLI 명령 실행 후 JSON 에러가 반환되면 아래 기준에 따라 처리
|
|
|
29
29
|
|
|
30
30
|
| 에러 코드 | 행동 |
|
|
31
31
|
|-----------|------|
|
|
32
|
-
| `GATED_ACCESS_REQUIRED` | purchase_info의 message/url 표시 → "접근 코드가 있으신가요?"
|
|
33
|
-
| `SPACE_ONLY` | Space 가입 필요 안내 → "초대 코드가 있으신가요?"
|
|
32
|
+
| `GATED_ACCESS_REQUIRED` | purchase_info의 message/url 표시 → "접근 코드가 있으신가요?" 사용자 질문 도구 호출 |
|
|
33
|
+
| `SPACE_ONLY` | Space 가입 필요 안내 → "초대 코드가 있으신가요?" 사용자 질문 도구 호출 |
|
|
34
34
|
| `APPROVAL_REQUIRED` | 승인 대기 안내 |
|
|
35
35
|
| `NO_ACCESS` | 접근 방법 안내 |
|
|
36
36
|
|
|
@@ -17,3 +17,10 @@ mkdir -p ~/.npm-global && npm config set prefix '~/.npm-global'
|
|
|
17
17
|
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.zshrc && source ~/.zshrc
|
|
18
18
|
npm install -g relayax-cli
|
|
19
19
|
```
|
|
20
|
+
|
|
21
|
+
이미 설치되어 있다면 최신 버전으로 업데이트하고 init을 재실행합니다:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm update -g relayax-cli
|
|
25
|
+
relay init
|
|
26
|
+
```
|
|
@@ -1,7 +1,35 @@
|
|
|
1
1
|
## Step 2. 로그인
|
|
2
2
|
|
|
3
|
+
먼저 `relay whoami`로 로그인 상태를 확인합니다. 이미 로그인되어 있으면 이 단계를 건너뛰세요.
|
|
4
|
+
|
|
5
|
+
로그인이 필요하면 아래 두 방법 중 하나를 사용하세요.
|
|
6
|
+
|
|
7
|
+
### 방법 A: 브라우저 콜백 (로컬 터미널)
|
|
8
|
+
|
|
9
|
+
로컬 머신에서 직접 터미널을 사용하는 경우 이 방법을 사용합니다.
|
|
10
|
+
`relay login`은 로컬 콜백 서버를 띄우고 브라우저 로그인 완료를 기다립니다.
|
|
11
|
+
반드시 포그라운드에서 실행하고, timeout을 300초(5분) 이상으로 설정하세요.
|
|
12
|
+
|
|
3
13
|
```bash
|
|
4
14
|
relay login
|
|
5
15
|
```
|
|
6
16
|
|
|
7
|
-
브라우저가 열리면 GitHub 또는 카카오 계정으로
|
|
17
|
+
- 브라우저가 자동으로 열리면 GitHub 또는 카카오 계정으로 로그인합니다.
|
|
18
|
+
- 브라우저가 열리지 않으면 CLI 출력에서 로그인 URL을 찾아 별도 명령어로 엽니다: `open <URL>` (macOS) / `xdg-open <URL>` (Linux)
|
|
19
|
+
|
|
20
|
+
"✓ 로그인 완료"가 출력되면 다음 단계로 진행합니다.
|
|
21
|
+
|
|
22
|
+
### 방법 B: Device Code (샌드박스/원격 환경)
|
|
23
|
+
|
|
24
|
+
Cowork, 원격 서버 등 브라우저 콜백이 localhost에 도달할 수 없는 환경에서는 이 방법을 사용합니다.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
relay login --device
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- 화면에 URL과 8자리 코드가 표시됩니다.
|
|
31
|
+
- 브라우저가 자동으로 열립니다. 열리지 않으면 표시된 URL을 별도 명령어로 엽니다: `open <URL>` (macOS) / `xdg-open <URL>` (Linux)
|
|
32
|
+
- 브라우저에서 코드를 입력하고 로그인을 승인합니다.
|
|
33
|
+
- CLI가 자동으로 승인을 감지하고 "✓ 로그인 완료"를 출력합니다.
|
|
34
|
+
|
|
35
|
+
`relay whoami`로 로그인 성공을 확인한 후 다음 단계로 진행합니다.
|
package/dist/prompts/install.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
요청된 에이전트를 relay에서 다운로드하고, 현재 에이전트 환경에 맞게 구성합니다.
|
|
2
2
|
인자 없이 호출하면 인터랙티브 탐색 모드로 진입합니다.
|
|
3
3
|
|
|
4
|
+
> **에이전트 호환성**: 이 프롬프트는 다양한 AI 에이전트에서 실행됩니다.
|
|
5
|
+
> 사용자에게 질문할 때는 사용 가능한 사용자 질문 도구를 호출하세요 (AskUserQuestion, ask_user, AskQuestion 등).
|
|
6
|
+
> 반드시 도구를 호출하여 응답을 기다려야 합니다. 텍스트로만 질문하고 세션을 종료하면 안 됩니다.
|
|
7
|
+
|
|
4
8
|
## 인터랙션 플로우
|
|
5
9
|
|
|
6
|
-
이 커맨드는 3단계 인터랙션으로 진행됩니다. 각 단계에서 반드시
|
|
10
|
+
이 커맨드는 3단계 인터랙션으로 진행됩니다. 각 단계에서 반드시 사용자 질문 도구를 호출하세요.
|
|
7
11
|
|
|
8
12
|
### Step 1. Organization 선택 & 에이전트 탐색 (slug가 없을 때만)
|
|
9
13
|
|
|
@@ -12,7 +16,7 @@ slug가 직접 주어지면 (`/relay-install @alice/doc-writer`) 이 단계를
|
|
|
12
16
|
#### 1-1. Organization 선택
|
|
13
17
|
`relay orgs list --json` 을 실행하여 사용자의 Organization 목록을 가져옵니다.
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
**사용자 질문 도구 호출:**
|
|
16
20
|
- question: "어디서 에이전트를 찾을까요?"
|
|
17
21
|
- options: Organization이 있으면 `["<org1_name>", "<org2_name>", ...]`, 없으면 이 단계를 건너뛰고 바로 공개 에이전트 탐색으로 진행
|
|
18
22
|
|
|
@@ -39,7 +43,7 @@ slug가 직접 주어지면 (`/relay-install @alice/doc-writer`) 이 단계를
|
|
|
39
43
|
/gen-test, /coverage
|
|
40
44
|
```
|
|
41
45
|
|
|
42
|
-
|
|
46
|
+
**사용자 질문 도구 호출:**
|
|
43
47
|
- question: "어떤 에이전트를 설치할까요?"
|
|
44
48
|
- options: `["1", "2", "3", "다시 검색", "돌아가기"]`
|
|
45
49
|
|
|
@@ -52,7 +56,7 @@ slug가 직접 주어지면 (`/relay-install @alice/doc-writer`) 이 단계를
|
|
|
52
56
|
|
|
53
57
|
에이전트 목록을 번호 리스트로 보여줍니다 (1-2와 동일 형식).
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
**사용자 질문 도구 호출:**
|
|
56
60
|
- question: "어떤 에이전트를 설치할까요?"
|
|
57
61
|
- options: `["1", "2", ..., "돌아가기"]`
|
|
58
62
|
|
|
@@ -96,7 +100,7 @@ slug가 직접 주어지면 (`/relay-install @alice/doc-writer`) 이 단계를
|
|
|
96
100
|
4. 범용 키워드 (utility, review, docs, testing 등) → 글로벌 추천
|
|
97
101
|
5. 판단이 어려우면 → 글로벌 추천 (기본값)
|
|
98
102
|
|
|
99
|
-
|
|
103
|
+
**사용자 질문 도구 호출:**
|
|
100
104
|
- question: "{추천 이유}. {추천}에 설치할까요?"
|
|
101
105
|
- options: `["글로벌 (모든 프로젝트) ✓ 추천", "로컬 (이 프로젝트만)"]` 또는 `["글로벌 (모든 프로젝트)", "로컬 (이 프로젝트만) ✓ 추천"]`
|
|
102
106
|
|
|
@@ -105,13 +109,13 @@ slug가 직접 주어지면 (`/relay-install @alice/doc-writer`) 이 단계를
|
|
|
105
109
|
- "Next.js + Supabase 전용 에이전트입니다. 이 프로젝트에 설치할까요?" → `["글로벌 (모든 프로젝트)", "로컬 (이 프로젝트만) ✓ 추천"]`
|
|
106
110
|
|
|
107
111
|
**응답 처리:**
|
|
108
|
-
- "글로벌" → `~/.claude
|
|
109
|
-
- "로컬" → 현재
|
|
112
|
+
- "글로벌" → 홈 디렉토리의 에이전트 설정 디렉토리에 배치 (예: `~/.claude/`, `~/.gemini/` 등 현재 에이전트 환경에 따라)
|
|
113
|
+
- "로컬" → 현재 프로젝트의 에이전트 설정 디렉토리에 배치
|
|
110
114
|
|
|
111
115
|
#### 2-3. 에이전트 환경에 맞게 배치
|
|
112
116
|
다운로드된 패키지(`install_path`)에서 파일을 읽고 선택된 범위에 배치합니다:
|
|
113
|
-
- 글로벌: `<install_path>/commands/` →
|
|
114
|
-
- 로컬: `<install_path>/commands/` →
|
|
117
|
+
- 글로벌: `<install_path>/commands/` → 홈 디렉토리의 에이전트 설정 디렉토리 하위 `commands/`에 복사, skills/ 동일
|
|
118
|
+
- 로컬: `<install_path>/commands/` → 프로젝트의 에이전트 설정 디렉토리 하위 `commands/`에 복사, skills/ 동일
|
|
115
119
|
- agents/, rules/ 파일도 같은 방식으로 배치합니다.
|
|
116
120
|
- **충돌 확인**: 같은 이름의 파일이 이미 있으면 사용자에게 덮어쓸지 물어봅니다.
|
|
117
121
|
|
|
@@ -131,9 +135,9 @@ relay deploy-record <slug> --scope <global|local> --files <배치된_파일1> <
|
|
|
131
135
|
{{BUSINESS_CARD_FORMAT}}
|
|
132
136
|
|
|
133
137
|
#### 3-2. 팔로우 제안 (필수 — 이 단계를 절대 건너뛰지 마세요)
|
|
134
|
-
명함 표시 직후, 빌더의 username이 JSON 결과에 있으면 **반드시**
|
|
138
|
+
명함 표시 직후, 빌더의 username이 JSON 결과에 있으면 **반드시** 사용자 질문 도구를 호출하세요.
|
|
135
139
|
|
|
136
|
-
|
|
140
|
+
**사용자 질문 도구 호출:**
|
|
137
141
|
- question: `@{username}을 팔로우할까요? 새 버전 알림을 받을 수 있습니다.`
|
|
138
142
|
- options: `["팔로우", "건너뛰기"]`
|
|
139
143
|
|
|
@@ -166,22 +170,22 @@ https://relayax.com/api/registry/{owner}/{slug}/guide.md
|
|
|
166
170
|
|
|
167
171
|
### 인터랙티브 모드 (/relay-install)
|
|
168
172
|
→ relay orgs list --json 실행
|
|
169
|
-
→
|
|
173
|
+
→ 사용자 질문 도구: "어디서 에이전트를 찾을까요?" → ["Alice's Org (alice)", "Acme Corp"]
|
|
170
174
|
→ "Alice's Org" 선택 → "어떤 에이전트를 찾고 계세요?"
|
|
171
175
|
→ relay search "문서" 실행 → 결과 리스트 표시
|
|
172
|
-
→
|
|
176
|
+
→ 사용자 질문 도구: "어떤 에이전트를 설치할까요?" → ["1", "2", "3", "다시 검색"]
|
|
173
177
|
→ "1" 선택 (@alice/doc-writer)
|
|
174
|
-
→
|
|
178
|
+
→ 사용자 질문 도구: "어디에 설치할까요?" → ["글로벌 (모든 프로젝트)", "로컬 (이 프로젝트만)"]
|
|
175
179
|
→ "글로벌" 선택
|
|
176
180
|
→ 설치 + 배치 + deploy-record
|
|
177
181
|
→ 명함 표시
|
|
178
|
-
→
|
|
182
|
+
→ 사용자 질문 도구: "@alice을 팔로우할까요?" → ["팔로우", "건너뛰기"]
|
|
179
183
|
→ "✓ 설치 완료! /write-doc를 사용해볼까요?"
|
|
180
184
|
|
|
181
185
|
### 다이렉트 모드 (/relay-install @alice/doc-writer)
|
|
182
186
|
→ relay install @alice/doc-writer --json 실행 (Step 1 건너뜀)
|
|
183
|
-
→
|
|
187
|
+
→ 사용자 질문 도구: "어디에 설치할까요?" → ["글로벌 (모든 프로젝트)", "로컬 (이 프로젝트만)"]
|
|
184
188
|
→ 설치 + 배치 + deploy-record
|
|
185
189
|
→ 명함 표시
|
|
186
|
-
→
|
|
190
|
+
→ 사용자 질문 도구: "@alice을 팔로우할까요?" → ["팔로우", "건너뛰기"]
|
|
187
191
|
→ "✓ 설치 완료! /write-doc를 사용해볼까요?"
|
package/dist/prompts/publish.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
현재 디렉토리의 에이전트(.relay/)를 분석하고, 보안 점검 및 requirements를 구성한 뒤, 사용가이드를 생성하고 relay에 배포합니다.
|
|
2
2
|
|
|
3
|
+
> **에이전트 호환성**: 이 프롬프트는 다양한 AI 에이전트에서 실행됩니다.
|
|
4
|
+
> 사용자에게 질문할 때는 사용 가능한 사용자 질문 도구를 호출하세요 (AskUserQuestion, ask_user, AskQuestion 등).
|
|
5
|
+
> 반드시 도구를 호출하여 응답을 기다려야 합니다. 텍스트로만 질문하고 세션을 종료하면 안 됩니다.
|
|
6
|
+
|
|
3
7
|
## 사전 준비
|
|
4
8
|
|
|
5
9
|
### 0-1. 인증 확인
|
|
@@ -69,7 +73,7 @@
|
|
|
69
73
|
|
|
70
74
|
**기존 글로벌 에이전트가 있을 때:**
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
**사용자 질문 도구 호출:**
|
|
73
77
|
- question: "기존 에이전트를 발견했습니다. 어떤 작업을 할까요?"
|
|
74
78
|
- options: `["<name> (v<version>) — 재배포", ..., "새 에이전트 만들기"]`
|
|
75
79
|
- 재배포 선택 시 → B. 재배포 플로우로 이동 (해당 에이전트의 relay.yaml 경로 사용)
|
|
@@ -80,7 +84,7 @@
|
|
|
80
84
|
`sources[]`의 모든 항목을 사용자에게 표시하고, 패키지에 포함할 콘텐츠를 선택받습니다.
|
|
81
85
|
각 항목의 SKILL.md, 에이전트 파일 등의 **내용을 직접 읽어** 기능을 파악한 후 설명과 함께 표시합니다.
|
|
82
86
|
|
|
83
|
-
|
|
87
|
+
**사용자 질문 도구 호출:**
|
|
84
88
|
- question: "배포할 콘텐츠를 선택하세요"
|
|
85
89
|
- options: 모든 소스의 항목을 기능 설명과 함께 나열
|
|
86
90
|
- 예: `["code-review — 코드 리뷰 자동화 (로컬)", "qa-testing — QA 테스트 (글로벌)", "dev-lead — 개발 리드 에이전트 (글로벌)", "전체 선택"]`
|
|
@@ -115,20 +119,20 @@
|
|
|
115
119
|
- **이름(name)**: 마켓플레이스에 표시되는 이름. 한국어 등 자유로운 문자 사용 가능 (예: "콘텐츠 에이전트", "Supabase 웹 개발")
|
|
116
120
|
- **slug**: URL과 `relay install`에 사용되는 식별자. 영문 소문자, 숫자, 하이픈만 가능 (예: "content-agent", "supabase-web-dev")
|
|
117
121
|
|
|
118
|
-
|
|
122
|
+
**사용자 질문 도구 호출:**
|
|
119
123
|
- question: "에이전트 이름을 확인해주세요 (한국어 가능)"
|
|
120
124
|
- 분석된 포지셔닝에서 자연스러운 에이전트 이름을 제안합니다 (예: "콘텐츠 에이전트", "Supabase 웹 개발")
|
|
121
125
|
- 현재 디렉토리명이 아닌, **콘텐츠 기반** 이름을 기본값으로 제시합니다.
|
|
122
126
|
|
|
123
127
|
**한국어 이름은 자동으로 로마자 slug가 생성됩니다.** 자동 생성된 slug를 확인합니다:
|
|
124
128
|
|
|
125
|
-
|
|
129
|
+
**사용자 질문 도구 호출:**
|
|
126
130
|
- question: "Slug를 확인해주세요 (URL/설치용 영문 식별자)"
|
|
127
131
|
- 한국어 이름은 로마자 변환 slug를 기본값으로 제시합니다 (예: "콘텐츠 에이전트" → "kontencheu-eijenteu").
|
|
128
132
|
- 로마자 변환이 길거나 부자연스러우면 콘텐츠 기반 영문 slug를 대안으로 제안합니다 (예: "content-agent").
|
|
129
133
|
- 영문 이름이면 자동 slug를 그대로 사용하고 이 단계를 건너뜁니다.
|
|
130
134
|
|
|
131
|
-
|
|
135
|
+
**사용자 질문 도구 호출:**
|
|
132
136
|
- question: "에이전트 설명을 확인해주세요 (마켓플레이스에 표시됩니다)"
|
|
133
137
|
- 분석한 콘텐츠를 기반으로 설치자 관점의 설명을 제안합니다.
|
|
134
138
|
- 좋은 예: "Supabase 기반 웹앱의 DB 마이그레이션, API 개발, 테스트를 자동화합니다"
|
|
@@ -208,13 +212,13 @@ relay.yaml에 기존 `source` 필드만 있고 `contents`가 없으면:
|
|
|
208
212
|
유지: qa-testing (skill)
|
|
209
213
|
```
|
|
210
214
|
|
|
211
|
-
|
|
215
|
+
**사용자 질문 도구 호출:**
|
|
212
216
|
- question: "변경된 콘텐츠를 반영할까요?"
|
|
213
217
|
- options: `["반영", "변경 확인", "건너뛰기"]`
|
|
214
218
|
|
|
215
219
|
**응답 처리:**
|
|
216
220
|
- "반영" → `relay package --sync --json` 실행하여 동기화
|
|
217
|
-
- "변경 확인" → 변경된 파일의 내용을 직접 읽어 diff를 상세히 보여준 후 다시
|
|
221
|
+
- "변경 확인" → 변경된 파일의 내용을 직접 읽어 diff를 상세히 보여준 후 다시 사용자 질문 도구 호출
|
|
218
222
|
- "건너뛰기" → 현재 .relay/ 그대로 배포
|
|
219
223
|
|
|
220
224
|
**변경이 없으면** → "✓ 모든 콘텐츠가 동기화 상태입니다." 표시 후 다음 단계로
|
|
@@ -228,7 +232,7 @@ relay.yaml에 기존 `source` 필드만 있고 `contents`가 없으면:
|
|
|
228
232
|
|
|
229
233
|
새로 발견된 항목의 파일 내용을 직접 읽어 기능을 파악한 후 표시합니다:
|
|
230
234
|
|
|
231
|
-
|
|
235
|
+
**사용자 질문 도구 호출:**
|
|
232
236
|
- question: "새로 발견된 콘텐츠가 있습니다. 패키지에 추가할까요?"
|
|
233
237
|
- options: 각 항목을 기능 설명과 함께 나열 + "건너뛰기"
|
|
234
238
|
- 예: `["new-skill — 새 유틸리티 스킬", "건너뛰기"]`
|
|
@@ -243,13 +247,13 @@ relay.yaml에 기존 `source` 필드만 있고 `contents`가 없으면:
|
|
|
243
247
|
|
|
244
248
|
## 인터랙션 플로우
|
|
245
249
|
|
|
246
|
-
이 커맨드는 4단계 인터랙션으로 진행됩니다. 각 단계에서 반드시
|
|
250
|
+
이 커맨드는 4단계 인터랙션으로 진행됩니다. 각 단계에서 반드시 사용자 질문 도구를 호출하세요.
|
|
247
251
|
|
|
248
252
|
### Step 1. 버전 범프
|
|
249
253
|
|
|
250
254
|
relay.yaml의 현재 `version`을 읽고 semver 범프를 제안합니다.
|
|
251
255
|
|
|
252
|
-
|
|
256
|
+
**사용자 질문 도구 호출:**
|
|
253
257
|
- question: "버전을 올릴까요? (현재 v{version})"
|
|
254
258
|
- options: `["v{patch} — patch (버그 수정)", "v{minor} — minor (기능 추가)", "v{major} — major (큰 변경)", "v{version} — 유지"]`
|
|
255
259
|
|
|
@@ -265,7 +269,7 @@ relay.yaml의 `visibility` 설정을 확인합니다.
|
|
|
265
269
|
|
|
266
270
|
#### 신규 배포 (visibility 미설정)
|
|
267
271
|
|
|
268
|
-
|
|
272
|
+
**사용자 질문 도구 호출:**
|
|
269
273
|
- question: "공개 범위를 선택하세요"
|
|
270
274
|
- options: `["공개 — 누구나 설치", "링크 공유 — 접근 링크가 있는 사람만 설치", "비공개 — Org 멤버만"]`
|
|
271
275
|
|
|
@@ -275,7 +279,7 @@ relay.yaml의 `visibility` 설정을 확인합니다.
|
|
|
275
279
|
- "비공개" → `relay orgs list --json` 실행 후 Organization 목록 표시
|
|
276
280
|
- Org가 0개이면: "비공개 배포하려면 Organization이 필요합니다. www.relayax.com/orgs 에서 Organization을 생성하세요."라고 안내하고 중단합니다.
|
|
277
281
|
|
|
278
|
-
|
|
282
|
+
**사용자 질문 도구 호출 (Org가 1개여도 반드시 호출):**
|
|
279
283
|
- question: "어떤 Organization에 배포할까요?"
|
|
280
284
|
- options: `["<org1_name>", "<org2_name>", ...]`
|
|
281
285
|
- **중요: Org가 1개라도 자동 선택하지 말고 반드시 사용자에게 확인받으세요.**
|
|
@@ -286,7 +290,7 @@ relay.yaml의 `visibility` 설정을 확인합니다.
|
|
|
286
290
|
|
|
287
291
|
현재 설정을 확인합니다:
|
|
288
292
|
|
|
289
|
-
|
|
293
|
+
**사용자 질문 도구 호출:**
|
|
290
294
|
- question: 공개일 때 "현재 **공개** 설정입니다. 유지할까요?", 링크공유일 때 "현재 **링크 공유** 설정입니다. 접근 링크가 있는 사람만 설치 가능합니다. 유지할까요?", 비공개일 때 "현재 **비공개** 설정입니다 (Org: {name}). 유지할까요?"
|
|
291
295
|
- options: `["유지", "변경"]`
|
|
292
296
|
|
|
@@ -325,7 +329,7 @@ npm: sharp (필수)
|
|
|
325
329
|
MCP: supabase (선택)
|
|
326
330
|
```
|
|
327
331
|
|
|
328
|
-
|
|
332
|
+
**사용자 질문 도구 호출:**
|
|
329
333
|
- question: "requires 설정이 맞나요?"
|
|
330
334
|
- options: `["확인", "수정"]`
|
|
331
335
|
|
|
@@ -386,7 +390,7 @@ Skills: 3개, Commands: 5개
|
|
|
386
390
|
requires: env 2개, cli 1개
|
|
387
391
|
```
|
|
388
392
|
|
|
389
|
-
|
|
393
|
+
**사용자 질문 도구 호출:**
|
|
390
394
|
- question: "이대로 배포할까요?"
|
|
391
395
|
- options: `["배포", "취소"]`
|
|
392
396
|
|
|
@@ -423,7 +427,7 @@ https://relayax.com/api/registry/{owner}/{slug}/guide.md
|
|
|
423
427
|
- **private (org)**: agent 접근 코드 사용. 코드를 사용하면 **이 에이전트에만** 접근 가능 + org 자동 가입.
|
|
424
428
|
- **private (personal)**: agent 접근 코드 사용. 코드를 사용하면 **이 에이전트에만** 접근 가능.
|
|
425
429
|
- `{owner}`과 `{slug}`는 배포된 에이전트의 실제 슬러그에서 추출합니다 (`@owner/slug` → `owner`, `slug`).
|
|
426
|
-
- "이 블록을 동료에게 공유하면
|
|
430
|
+
- "이 블록을 동료에게 공유하면 AI 에이전트가 환경 체크부터 설치까지 자동으로 해줍니다"라고 안내합니다.
|
|
427
431
|
- CLI가 이미 설치된 사용자를 위한 짧은 버전도 함께 표시: `/relay:relay-install <slug>`
|
|
428
432
|
{{BUSINESS_CARD_FORMAT}}
|
|
429
433
|
|
|
@@ -431,13 +435,13 @@ https://relayax.com/api/registry/{owner}/{slug}/guide.md
|
|
|
431
435
|
|
|
432
436
|
사용자: /relay-publish
|
|
433
437
|
→ 인증 확인 ✓, 에이전트 구조 분석 (skills 3개, commands 5개)
|
|
434
|
-
→
|
|
438
|
+
→ 사용자 질문 도구: "어디에 배포할까요?" → ["공개", "링크 공유", "비공개 (Org 전용)"]
|
|
435
439
|
→ "공개" 선택
|
|
436
440
|
→ 보안 스캔 ✓ 시크릿 없음 → requires 분석 결과 표시
|
|
437
|
-
→
|
|
441
|
+
→ 사용자 질문 도구: "requires 설정이 맞나요?" → ["확인", "수정"]
|
|
438
442
|
→ "확인"
|
|
439
443
|
→ 배포 요약 표시
|
|
440
|
-
→
|
|
444
|
+
→ 사용자 질문 도구: "이대로 배포할까요?" → ["배포", "취소"]
|
|
441
445
|
→ "배포" → relay publish 실행
|
|
442
446
|
→ "배포 완료! URL: https://relayax.com/@my-org/my-agent"
|
|
443
447
|
→ 온보딩 가이드 코드블록 표시
|