relayax-cli 0.4.13 → 0.4.15
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 +5 -134
- package/dist/commands/publish.js +55 -21
- package/dist/prompts/create.md +63 -17
- package/package.json +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -14,20 +14,6 @@ const command_adapter_js_1 = require("../lib/command-adapter.js");
|
|
|
14
14
|
const config_js_1 = require("../lib/config.js");
|
|
15
15
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
16
16
|
const pkg = require('../../package.json');
|
|
17
|
-
const VALID_AGENT_DIRS = ['skills', 'agents', 'rules', 'commands'];
|
|
18
|
-
function resolveTools(toolsArg) {
|
|
19
|
-
const raw = toolsArg.trim().toLowerCase();
|
|
20
|
-
if (raw === 'all') {
|
|
21
|
-
return ai_tools_js_1.AI_TOOLS.map((t) => t.value);
|
|
22
|
-
}
|
|
23
|
-
const tokens = raw.split(',').map((t) => t.trim()).filter(Boolean);
|
|
24
|
-
const valid = new Set(ai_tools_js_1.AI_TOOLS.map((t) => t.value));
|
|
25
|
-
const invalid = tokens.filter((t) => !valid.has(t));
|
|
26
|
-
if (invalid.length > 0) {
|
|
27
|
-
throw new Error(`알 수 없는 도구: ${invalid.join(', ')}\n사용 가능: ${[...valid].join(', ')}`);
|
|
28
|
-
}
|
|
29
|
-
return tokens;
|
|
30
|
-
}
|
|
31
17
|
function showWelcome() {
|
|
32
18
|
const lines = [
|
|
33
19
|
'',
|
|
@@ -48,23 +34,6 @@ function showWelcome() {
|
|
|
48
34
|
];
|
|
49
35
|
console.log(lines.join('\n'));
|
|
50
36
|
}
|
|
51
|
-
async function selectToolsInteractively(detectedIds) {
|
|
52
|
-
const { checkbox } = await import('@inquirer/prompts');
|
|
53
|
-
const choices = ai_tools_js_1.AI_TOOLS.map((tool) => {
|
|
54
|
-
const detected = detectedIds.has(tool.value);
|
|
55
|
-
return {
|
|
56
|
-
name: detected ? `${tool.name} \x1b[32m(detected)\x1b[0m` : tool.name,
|
|
57
|
-
value: tool.value,
|
|
58
|
-
checked: detected,
|
|
59
|
-
};
|
|
60
|
-
});
|
|
61
|
-
const selected = await checkbox({
|
|
62
|
-
message: `연결할 에이전트 CLI를 선택하세요`,
|
|
63
|
-
choices,
|
|
64
|
-
pageSize: 8,
|
|
65
|
-
});
|
|
66
|
-
return selected;
|
|
67
|
-
}
|
|
68
37
|
/**
|
|
69
38
|
* 글로벌 User 커맨드를 감지된 모든 에이전트 CLI에 설치한다.
|
|
70
39
|
* ~/{skillsDir}/commands/relay/ 에 설치.
|
|
@@ -111,23 +80,6 @@ function installGlobalUserCommands() {
|
|
|
111
80
|
function hasGlobalUserCommands() {
|
|
112
81
|
return command_adapter_js_1.USER_COMMANDS.every((cmd) => fs_1.default.existsSync((0, command_adapter_js_1.getGlobalCommandPath)(cmd.id)));
|
|
113
82
|
}
|
|
114
|
-
/**
|
|
115
|
-
* 에이전트 프로젝트인지 감지한다 (.relay/ 디렉토리 내 relay.yaml 또는 에이전트 디렉토리 구조).
|
|
116
|
-
*/
|
|
117
|
-
function isAgentProject(projectPath) {
|
|
118
|
-
const relayDir = path_1.default.join(projectPath, '.relay');
|
|
119
|
-
if (!fs_1.default.existsSync(relayDir))
|
|
120
|
-
return false;
|
|
121
|
-
if (fs_1.default.existsSync(path_1.default.join(relayDir, 'relay.yaml'))) {
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
return VALID_AGENT_DIRS.some((d) => {
|
|
125
|
-
const dirPath = path_1.default.join(relayDir, d);
|
|
126
|
-
if (!fs_1.default.existsSync(dirPath))
|
|
127
|
-
return false;
|
|
128
|
-
return fs_1.default.readdirSync(dirPath).filter((f) => !f.startsWith('.')).length > 0;
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
83
|
function registerInit(program) {
|
|
132
84
|
program
|
|
133
85
|
.command('init')
|
|
@@ -142,8 +94,6 @@ function registerInit(program) {
|
|
|
142
94
|
const autoMode = opts.auto === true || opts.all === true || !process.stdin.isTTY;
|
|
143
95
|
const projectPath = (0, paths_js_1.resolveProjectPath)(opts.project);
|
|
144
96
|
const detected = (0, ai_tools_js_1.detectAgentCLIs)(projectPath);
|
|
145
|
-
const detectedIds = new Set(detected.map((t) => t.value));
|
|
146
|
-
const isBuilder = isAgentProject(projectPath);
|
|
147
97
|
// ── 0. --json 모드에서 --tools/--all 없으면 MISSING_TOOLS 에러 ──
|
|
148
98
|
if (json && !opts.tools && !opts.all && !opts.auto) {
|
|
149
99
|
const detectedOptions = detected.map((t) => ({ value: t.value, label: t.name }));
|
|
@@ -177,84 +127,18 @@ function registerInit(program) {
|
|
|
177
127
|
};
|
|
178
128
|
(0, config_js_1.saveInstalled)(installed);
|
|
179
129
|
}
|
|
180
|
-
|
|
181
|
-
// relay-publish가 글로벌로 승격되어 BUILDER_COMMANDS가 비어있으면 스킵
|
|
182
|
-
const localResults = [];
|
|
183
|
-
if (isBuilder && command_adapter_js_1.BUILDER_COMMANDS.length > 0) {
|
|
184
|
-
// 도구 선택
|
|
185
|
-
let targetToolIds;
|
|
186
|
-
if (opts.tools) {
|
|
187
|
-
targetToolIds = resolveTools(opts.tools);
|
|
188
|
-
}
|
|
189
|
-
else if (!autoMode) {
|
|
190
|
-
// interactive mode: only when stdin is a TTY and not --auto/--json
|
|
191
|
-
showWelcome();
|
|
192
|
-
if (detected.length > 0) {
|
|
193
|
-
console.log(` 감지된 에이전트 CLI: \x1b[36m${detected.map((t) => t.name).join(', ')}\x1b[0m\n`);
|
|
194
|
-
}
|
|
195
|
-
console.log(' \x1b[2mBuilder 프로젝트 감지 → 로컬 Builder 커맨드도 설치합니다.\x1b[0m\n');
|
|
196
|
-
targetToolIds = await selectToolsInteractively(detectedIds);
|
|
197
|
-
if (targetToolIds.length === 0) {
|
|
198
|
-
console.log('\n 선택된 도구가 없습니다.');
|
|
199
|
-
// 글로벌은 이미 설치됨
|
|
200
|
-
if (globalStatus === 'installed') {
|
|
201
|
-
console.log(' 글로벌 User 커맨드는 설치되었습니다.\n');
|
|
202
|
-
}
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
207
|
-
// auto mode: use detected CLIs, or all available tools if none detected
|
|
208
|
-
if (detected.length > 0) {
|
|
209
|
-
targetToolIds = detected.map((t) => t.value);
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
targetToolIds = ai_tools_js_1.AI_TOOLS.map((t) => t.value);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
// Builder 커맨드 설치 (기존 파일 중 현재 목록에 없는 것 제거)
|
|
216
|
-
const builderIds = new Set(command_adapter_js_1.BUILDER_COMMANDS.map((c) => c.id));
|
|
217
|
-
for (const toolId of targetToolIds) {
|
|
218
|
-
const tool = ai_tools_js_1.AI_TOOLS.find((t) => t.value === toolId);
|
|
219
|
-
if (!tool)
|
|
220
|
-
continue;
|
|
221
|
-
const adapter = (0, command_adapter_js_1.createAdapter)(tool);
|
|
222
|
-
const localDir = path_1.default.join(projectPath, tool.skillsDir, 'commands', 'relay');
|
|
223
|
-
// 기존 로컬 커맨드 중 Builder 목록에 없는 것 제거
|
|
224
|
-
if (fs_1.default.existsSync(localDir)) {
|
|
225
|
-
for (const file of fs_1.default.readdirSync(localDir)) {
|
|
226
|
-
const id = file.replace(/\.md$/, '');
|
|
227
|
-
if (!builderIds.has(id)) {
|
|
228
|
-
fs_1.default.unlinkSync(path_1.default.join(localDir, file));
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
const installedCommands = [];
|
|
233
|
-
for (const cmd of command_adapter_js_1.BUILDER_COMMANDS) {
|
|
234
|
-
const filePath = path_1.default.join(projectPath, adapter.getFilePath(cmd.id));
|
|
235
|
-
const fileContent = adapter.formatFile(cmd);
|
|
236
|
-
fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
|
|
237
|
-
fs_1.default.writeFileSync(filePath, fileContent);
|
|
238
|
-
installedCommands.push(cmd.id);
|
|
239
|
-
}
|
|
240
|
-
localResults.push({ tool: tool.name, commands: installedCommands });
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
else if (!autoMode) {
|
|
244
|
-
// User 모드: 글로벌만 설치, 안내 표시
|
|
130
|
+
if (!autoMode) {
|
|
245
131
|
showWelcome();
|
|
246
132
|
}
|
|
247
|
-
// ──
|
|
133
|
+
// ── 2. 출력 ──
|
|
248
134
|
if (json) {
|
|
249
135
|
console.log(JSON.stringify({
|
|
250
136
|
status: 'ok',
|
|
251
|
-
mode: isBuilder ? 'builder' : 'user',
|
|
252
137
|
global: {
|
|
253
138
|
status: globalStatus,
|
|
254
139
|
path: (0, command_adapter_js_1.getGlobalCommandDir)(),
|
|
255
140
|
commands: command_adapter_js_1.USER_COMMANDS.map((c) => c.id),
|
|
256
141
|
},
|
|
257
|
-
local: isBuilder ? localResults : undefined,
|
|
258
142
|
}));
|
|
259
143
|
}
|
|
260
144
|
else {
|
|
@@ -270,28 +154,15 @@ function registerInit(program) {
|
|
|
270
154
|
// 글로벌
|
|
271
155
|
{
|
|
272
156
|
const toolNames = globalTools.length > 0 ? globalTools.join(', ') : '(감지된 CLI 없음)';
|
|
273
|
-
console.log(` \x1b[
|
|
157
|
+
console.log(` \x1b[36m커맨드 (글로벌)\x1b[0m — ${globalStatus === 'updated' ? '업데이트됨' : '설치됨'}`);
|
|
274
158
|
console.log(` 감지된 CLI: \x1b[36m${toolNames}\x1b[0m`);
|
|
275
159
|
for (const cmd of command_adapter_js_1.USER_COMMANDS) {
|
|
276
160
|
console.log(` /${cmd.id}`);
|
|
277
161
|
}
|
|
278
162
|
console.log();
|
|
279
163
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
console.log(` \x1b[36mBuilder 커맨드 (로컬)\x1b[0m`);
|
|
283
|
-
for (const r of localResults) {
|
|
284
|
-
console.log(` ${r.tool}`);
|
|
285
|
-
for (const cmd of r.commands) {
|
|
286
|
-
console.log(` /${cmd}`);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
console.log();
|
|
290
|
-
}
|
|
291
|
-
if (!isBuilder) {
|
|
292
|
-
console.log(' 에이전트를 만들려면 \x1b[33mrelay create <name>\x1b[0m을 사용하세요.');
|
|
293
|
-
console.log();
|
|
294
|
-
}
|
|
164
|
+
console.log(' 에이전트를 만들려면 \x1b[33mrelay create <name>\x1b[0m을 사용하세요.');
|
|
165
|
+
console.log();
|
|
295
166
|
console.log(' IDE를 재시작하면 슬래시 커맨드가 활성화됩니다.');
|
|
296
167
|
}
|
|
297
168
|
});
|
package/dist/commands/publish.js
CHANGED
|
@@ -784,28 +784,28 @@ function registerPublish(program) {
|
|
|
784
784
|
// preamble update is best-effort — publish already succeeded
|
|
785
785
|
}
|
|
786
786
|
if (json) {
|
|
787
|
-
|
|
787
|
+
// Enrich JSON output with plugin_url if git_url available
|
|
788
|
+
const jsonResult = { ...result };
|
|
789
|
+
const resultGitUrl = jsonResult.git_url;
|
|
790
|
+
if (resultGitUrl) {
|
|
791
|
+
const pSlug = jsonResult.slug.startsWith('@') ? jsonResult.slug.slice(1) : jsonResult.slug;
|
|
792
|
+
const pName = pSlug.includes('/') ? pSlug.split('/')[1] : pSlug;
|
|
793
|
+
jsonResult.plugin_url = `${config_js_1.API_URL}/api/registry/@${pSlug}/plugin`;
|
|
794
|
+
jsonResult.plugin_install_cmd = `/plugin install ${pName}`;
|
|
795
|
+
}
|
|
796
|
+
jsonResult.platforms = generatedPlatforms;
|
|
797
|
+
console.log(JSON.stringify(jsonResult));
|
|
788
798
|
}
|
|
789
799
|
else {
|
|
790
800
|
console.log(`\n\x1b[32m✓ ${config.name} 배포 완료\x1b[0m v${result.version}`);
|
|
791
801
|
console.log(` 슬러그: \x1b[36m${result.slug}\x1b[0m`);
|
|
792
802
|
console.log(` URL: \x1b[36m${result.url}\x1b[0m`);
|
|
793
|
-
//
|
|
794
|
-
|
|
795
|
-
console.log(`\n \x1b[90m플랫폼 매니페스트:\x1b[0m ${generatedPlatforms.join(', ')}`);
|
|
796
|
-
}
|
|
797
|
-
// Show Claude Code plugin install command if claude-code manifest was generated
|
|
798
|
-
if (generatedPlatforms.includes('claude-code')) {
|
|
799
|
-
const pluginSlug = result.slug.startsWith('@') ? result.slug.slice(1) : result.slug;
|
|
800
|
-
const pluginUrl = `${config_js_1.API_URL}/api/registry/@${pluginSlug}/plugin`;
|
|
801
|
-
console.log(`\n \x1b[90mClaude Code 플러그인:\x1b[0m`);
|
|
802
|
-
console.log(` \x1b[36m/plugin marketplace add ${pluginUrl}\x1b[0m`);
|
|
803
|
-
}
|
|
804
|
-
// Show shareable onboarding guide as a plain copyable block
|
|
805
|
-
if (isTTY) {
|
|
803
|
+
// Build share block
|
|
804
|
+
{
|
|
806
805
|
const detailSlug = result.slug.startsWith('@') ? result.slug.slice(1) : result.slug;
|
|
807
806
|
const accessCode = result.access_code;
|
|
808
|
-
|
|
807
|
+
const gitUrl = result.git_url;
|
|
808
|
+
// CLI install command
|
|
809
809
|
const visibility = config.visibility ?? 'public';
|
|
810
810
|
let installCmd;
|
|
811
811
|
if (visibility === 'internal' && accessCode) {
|
|
@@ -817,12 +817,46 @@ function registerPublish(program) {
|
|
|
817
817
|
else {
|
|
818
818
|
installCmd = `npx relayax-cli install ${result.slug}`;
|
|
819
819
|
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
console.log(
|
|
820
|
+
// Plugin install commands (marketplace add + plugin install)
|
|
821
|
+
const pluginSlug = detailSlug.includes('/') ? detailSlug.split('/')[1] : detailSlug;
|
|
822
|
+
const pluginUrl = gitUrl ? `${config_js_1.API_URL}/api/registry/@${detailSlug}/plugin` : null;
|
|
823
|
+
// ── CLI 설치 (복사용) ──
|
|
824
|
+
console.log(`\n \x1b[1m▸ CLI 설치\x1b[0m`);
|
|
825
|
+
console.log(` ┌─`);
|
|
826
|
+
console.log(` │ ${installCmd}`);
|
|
827
|
+
console.log(` └─`);
|
|
828
|
+
// ── Claude Code Plugin 설치 (복사용) ──
|
|
829
|
+
if (pluginUrl) {
|
|
830
|
+
console.log(`\n \x1b[1m▸ Claude Code Plugin 설치\x1b[0m`);
|
|
831
|
+
console.log(` ┌─`);
|
|
832
|
+
console.log(` │ /plugin marketplace add ${pluginUrl}`);
|
|
833
|
+
console.log(` │ /plugin install ${pluginSlug}`);
|
|
834
|
+
console.log(` └─`);
|
|
835
|
+
}
|
|
836
|
+
// ── 소개 페이지 ──
|
|
837
|
+
console.log(`\n \x1b[90m소개 페이지:\x1b[0m https://relayax.com/@${detailSlug}`);
|
|
838
|
+
// ── 공유 텍스트 (코드블록, 그대로 복붙) ──
|
|
839
|
+
if (isTTY) {
|
|
840
|
+
const shareBlock = [
|
|
841
|
+
`[${config.name}] 설치하기`,
|
|
842
|
+
``,
|
|
843
|
+
`# CLI`,
|
|
844
|
+
installCmd,
|
|
845
|
+
];
|
|
846
|
+
if (pluginUrl) {
|
|
847
|
+
shareBlock.push(``, `# Claude Code Plugin`, `/plugin marketplace add ${pluginUrl}`, `/plugin install ${pluginSlug}`);
|
|
848
|
+
}
|
|
849
|
+
shareBlock.push(``, `소개: https://relayax.com/@${detailSlug}`);
|
|
850
|
+
const maxLen = Math.max(...shareBlock.map((l) => l.length));
|
|
851
|
+
const border = '─'.repeat(maxLen + 2);
|
|
852
|
+
console.log(`\n \x1b[90m┌${border}┐\x1b[0m`);
|
|
853
|
+
for (const line of shareBlock) {
|
|
854
|
+
const pad = ' '.repeat(maxLen - line.length);
|
|
855
|
+
console.log(` \x1b[90m│\x1b[0m ${line}${pad} \x1b[90m│\x1b[0m`);
|
|
856
|
+
}
|
|
857
|
+
console.log(` \x1b[90m└${border}┘\x1b[0m`);
|
|
858
|
+
console.log(` \x1b[90m↑ 팀에 공유하세요\x1b[0m`);
|
|
859
|
+
}
|
|
826
860
|
}
|
|
827
861
|
}
|
|
828
862
|
}
|
package/dist/prompts/create.md
CHANGED
|
@@ -3,6 +3,27 @@ relay.yaml이 없으면 새로 만들고, 있으면 변경사항을 반영합니
|
|
|
3
3
|
|
|
4
4
|
> 빌더는 터미널 환경에서 작업합니다. CLI 명령어를 직접 실행하세요.
|
|
5
5
|
|
|
6
|
+
## 핵심 원칙: 의사결정 포인트에서 사용자 질문
|
|
7
|
+
|
|
8
|
+
각 단계에서 **선택지가 2개 이상**이면 반드시 사용자에게 질문하고 답변을 기다리세요.
|
|
9
|
+
(AskUserQuestion 등 사용자 입력을 받는 도구를 사용하세요.)
|
|
10
|
+
선택지가 1개뿐이거나 자동 판단이 가능하면 결과를 보여주고 바로 진행합니다.
|
|
11
|
+
|
|
12
|
+
### 사용자 입력이 필요한 경우 (멈추고 질문하고 답변을 기다림)
|
|
13
|
+
- 소스가 2개 이상 감지됨 → "어떤 콘텐츠를 포함할까요?"
|
|
14
|
+
- Org가 1개 이상 있음 → "개인 배포 vs Org 배포?"
|
|
15
|
+
- visibility 옵션이 2개 이상 → "공개 범위를 선택해주세요"
|
|
16
|
+
- 포지셔닝 확인 → 분석 결과를 보여주고 "이대로 진행할까요?"
|
|
17
|
+
- 배포 최종 확인 → relay.yaml 요약을 보여주고 "배포할까요?"
|
|
18
|
+
|
|
19
|
+
**질문 후 반드시 사용자의 답변을 받을 때까지 다음 단계로 넘어가지 마세요.**
|
|
20
|
+
텍스트로 질문을 출력한 뒤 혼자 답변하고 진행하면 안 됩니다.
|
|
21
|
+
|
|
22
|
+
### 자동 진행하는 경우 (질문 불필요)
|
|
23
|
+
- 소스가 1개뿐 → 해당 소스 자동 선택, 결과만 보여줌
|
|
24
|
+
- 이미 relay.yaml이 있고 변경사항이 명확함 → 요약 후 진행
|
|
25
|
+
- 로그인이 필요 → 자동으로 `relay login` 실행
|
|
26
|
+
|
|
6
27
|
## 분기: 최초 생성 vs 업데이트
|
|
7
28
|
|
|
8
29
|
`.relay/relay.yaml`이 있는지 확인합니다.
|
|
@@ -17,7 +38,9 @@ relay.yaml이 없으면 새로 만들고, 있으면 변경사항을 반영합니
|
|
|
17
38
|
### 1. 콘텐츠 파악
|
|
18
39
|
|
|
19
40
|
`relay package --init --json`으로 소스를 스캔합니다.
|
|
20
|
-
|
|
41
|
+
|
|
42
|
+
- **소스가 2개 이상** → `sources[]`를 정리하여 보여주고, **사용자에게 질문하여 어떤 콘텐츠를 포함할지 물어봅니다.** 사용자가 답변할 때까지 다음 단계로 넘어가지 마세요.
|
|
43
|
+
- **소스가 1개** → 해당 소스를 자동 선택하고 결과를 보여준 뒤 바로 진행합니다.
|
|
21
44
|
|
|
22
45
|
선택된 콘텐츠의 파일을 직접 읽어 기능을 파악합니다:
|
|
23
46
|
- SKILL.md, 에이전트 파일, 커맨드 파일의 내용
|
|
@@ -35,6 +58,9 @@ relay.yaml이 없으면 새로 만들고, 있으면 변경사항을 반영합니
|
|
|
35
58
|
이름(name)은 한국어 가능. slug는 영문 소문자+하이픈.
|
|
36
59
|
설명은 설치자 관점으로 ("~를 자동화합니다").
|
|
37
60
|
|
|
61
|
+
포지셔닝 결과를 표로 정리하여 보여주고, **사용자에게 질문하여 확인받으세요.**
|
|
62
|
+
("이 포지셔닝으로 진행할까요? 수정할 부분이 있으면 알려주세요.")
|
|
63
|
+
|
|
38
64
|
### 3. requires 판단 + 보안 점검
|
|
39
65
|
|
|
40
66
|
콘텐츠 파일을 읽고 requires를 판단합니다:
|
|
@@ -53,22 +79,30 @@ relay.yaml이 없으면 새로 만들고, 있으면 변경사항을 반영합니
|
|
|
53
79
|
- 파일 컨텍스트를 읽어 실제 시크릿 vs 예시 코드를 구분
|
|
54
80
|
- 발견 시 **반드시 경고**하고 환경변수 대체 안내
|
|
55
81
|
|
|
56
|
-
### 4.
|
|
82
|
+
### 4. 배포 설정
|
|
57
83
|
|
|
58
|
-
|
|
84
|
+
`relay orgs list --json`으로 Org 목록을 조회합니다.
|
|
85
|
+
|
|
86
|
+
**항상 사용자에게 질문합니다** (AskUserQuestion 등 사용자 입력 도구 사용):
|
|
87
|
+
- **Org가 1개 이상** → "개인 배포 / {org이름}에 배포" 선택
|
|
88
|
+
- **Org가 없음** → "개인 배포 / 새 Organization 만들기" 선택
|
|
89
|
+
- "새 Organization 만들기" 선택 시 → `relay orgs create "이름"` 실행 후 해당 Org에 배포
|
|
90
|
+
|
|
91
|
+
선택에 따라 **사용자에게 질문하여 visibility를 물어봅니다:**
|
|
92
|
+
- **Org 없이 배포**: `public`, `private` (2개)
|
|
93
|
+
- **Org에 배포**: `public`, `private`, `internal` (3개)
|
|
94
|
+
- `public` — 누구나 설치
|
|
95
|
+
- `private` — 접근 링크가 있는 사람만 설치
|
|
96
|
+
- `internal` — Org 멤버만 설치 (Org 배포 시에만 선택 가능)
|
|
97
|
+
|
|
98
|
+
### 5. relay.yaml 작성 & 배포
|
|
99
|
+
|
|
100
|
+
위 결과를 relay.yaml에 반영합니다:
|
|
59
101
|
- name, slug, description, version, tags
|
|
60
102
|
- requires (판단 결과)
|
|
61
|
-
- org
|
|
62
|
-
- Org가 있으면: 개인 배포 vs Org 배포를 사용자에게 물어봅니다.
|
|
63
|
-
- Org가 없으면: 개인 배포로 진행합니다.
|
|
64
|
-
- visibility: Org 선택 결과에 따라 옵션이 달라집니다:
|
|
65
|
-
- **Org 없이 배포**: `public`, `private` (2개)
|
|
66
|
-
- **Org에 배포**: `public`, `private`, `internal` (3개)
|
|
67
|
-
- `public` — 누구나 설치
|
|
68
|
-
- `private` — 접근 링크가 있는 사람만 설치
|
|
69
|
-
- `internal` — Org 멤버만 설치 (Org 배포 시에만 선택 가능)
|
|
103
|
+
- org, visibility
|
|
70
104
|
|
|
71
|
-
`relay publish --json`으로 배포합니다.
|
|
105
|
+
**사용자에게 질문하여 최종 확인** 후 `relay publish --json`으로 배포합니다.
|
|
72
106
|
|
|
73
107
|
---
|
|
74
108
|
|
|
@@ -80,7 +114,7 @@ relay.yaml이 없으면 새로 만들고, 있으면 변경사항을 반영합니
|
|
|
80
114
|
- 변경된 콘텐츠 (modified)
|
|
81
115
|
- 새로 추가된 콘텐츠 (new_items)
|
|
82
116
|
|
|
83
|
-
|
|
117
|
+
**사용자에게 질문하여 어떤 부분을 변경하려는지 물어봅니다:**
|
|
84
118
|
- 콘텐츠 변경 반영 (sync)
|
|
85
119
|
- 새 스킬/커맨드 추가
|
|
86
120
|
- 설명/태그 개선
|
|
@@ -96,13 +130,25 @@ relay.yaml이 없으면 새로 만들고, 있으면 변경사항을 반영합니
|
|
|
96
130
|
|
|
97
131
|
### 3. 배포
|
|
98
132
|
|
|
133
|
+
변경 요약을 보여주고 **사용자에게 질문하여 최종 확인** 후 배포합니다.
|
|
99
134
|
`relay publish --json`으로 배포합니다.
|
|
100
|
-
버전 범프가 필요하면 사용자에게 patch/minor/major 중 확인합니다.
|
|
135
|
+
버전 범프가 필요하면 사용자에게 질문하여 patch/minor/major 중 확인합니다.
|
|
101
136
|
|
|
102
137
|
---
|
|
103
138
|
|
|
104
|
-
## 공유
|
|
139
|
+
## 배포 완료 후 공유 안내
|
|
140
|
+
|
|
141
|
+
`relay publish --json` 출력 결과를 파싱하여 다음을 보여주세요:
|
|
105
142
|
|
|
106
|
-
|
|
143
|
+
1. **배포 결과 요약** — slug, 버전, 공개 범위, URL
|
|
144
|
+
2. **설치 방법** — CLI 출력에 코드블록 형태로 이미 포함되어 있으므로, 그 내용을 사용자에게 안내합니다:
|
|
145
|
+
- CLI: `npx relayax-cli install {slug}`
|
|
146
|
+
- 출력에 `plugin_url`이 있으면 Claude Code Plugin:
|
|
147
|
+
```
|
|
148
|
+
/plugin marketplace add {pluginUrl}
|
|
149
|
+
/plugin install {slug}
|
|
150
|
+
```
|
|
151
|
+
- 에이전트 소개 페이지 URL
|
|
152
|
+
3. **공유 텍스트** — CLI 출력의 공유 블록(┌─ ... ─┘)을 그대로 안내합니다. 팀에 바로 복붙할 수 있는 코드블록 형태입니다.
|
|
107
153
|
|
|
108
154
|
{{ERROR_HANDLING_GUIDE}}
|