relayax-cli 0.4.29 → 0.4.31

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.
@@ -199,35 +199,11 @@ function registerInstall(program) {
199
199
  const isTTY = Boolean(process.stdin.isTTY);
200
200
  const interactive = isTTY && !json;
201
201
  const defaultScope = resolvedAgent.recommended_scope ?? (resolvedAgent.type === 'passive' ? 'local' : 'global');
202
- // ── Scope 결정: 플래그 > TTY prompt > 자동결정 ──
203
- let scope;
204
- if (_opts.global) {
205
- scope = 'global';
206
- }
207
- else if (_opts.local) {
208
- scope = 'local';
209
- }
210
- else if (interactive) {
211
- const { select } = await import('@inquirer/prompts');
212
- scope = await select({
213
- message: '설치 범위를 선택하세요',
214
- choices: [
215
- { name: '글로벌 (~/.relay/agents/) — 모든 프로젝트에서 사용', value: 'global' },
216
- { name: '로컬 (./.relay/agents/) — 이 프로젝트에서만 사용', value: 'local' },
217
- ],
218
- default: defaultScope,
219
- });
220
- }
221
- else {
222
- scope = defaultScope;
223
- }
224
- // ── AI tools 선택: 감지된 건 pre-checked, 나머지는 선택 가능 ──
202
+ // ── 1. AI tools 선택 (scope 무관, 항상 디렉토리에서 감지) ──
225
203
  let selectedTools;
226
204
  if (interactive) {
227
- const detected = scope === 'global'
228
- ? (0, ai_tools_js_1.detectGlobalCLIs)()
229
- : (0, ai_tools_js_1.detectAgentCLIs)(projectPath);
230
- if (scope === 'global' && !detected.some((t) => t.value === 'claude')) {
205
+ const detected = (0, ai_tools_js_1.detectGlobalCLIs)();
206
+ if (!detected.some((t) => t.value === 'claude')) {
231
207
  detected.push({ name: 'Claude Code', value: 'claude', skillsDir: '.claude' });
232
208
  }
233
209
  const detectedValues = new Set(detected.map((t) => t.value));
@@ -241,7 +217,7 @@ function registerInstall(program) {
241
217
  })),
242
218
  });
243
219
  }
244
- // ── init 통합: 선택된 tools에 글로벌 commands 설치 ──
220
+ // ── 2. 글로벌 slash commands 설치/업데이트 ──
245
221
  if (selectedTools) {
246
222
  (0, init_js_1.installGlobalUserCommands)(selectedTools);
247
223
  }
@@ -251,6 +227,29 @@ function registerInstall(program) {
251
227
  }
252
228
  (0, init_js_1.installGlobalUserCommands)();
253
229
  }
230
+ // ── 3. Scope 결정: 플래그 > TTY prompt > 자동결정 ──
231
+ let scope;
232
+ if (_opts.global) {
233
+ scope = 'global';
234
+ }
235
+ else if (_opts.local) {
236
+ scope = 'local';
237
+ }
238
+ else if (interactive) {
239
+ const { select } = await import('@inquirer/prompts');
240
+ const recommendLabel = defaultScope === 'global' ? '글로벌' : '로컬';
241
+ scope = await select({
242
+ message: `설치 범위를 선택하세요 (제작자 권장: ${recommendLabel})`,
243
+ choices: [
244
+ { name: '글로벌 (~/.relay/agents/) — 모든 프로젝트에서 사용', value: 'global' },
245
+ { name: '로컬 (./.relay/agents/) — 이 프로젝트에서만 사용', value: 'local' },
246
+ ],
247
+ default: defaultScope,
248
+ });
249
+ }
250
+ else {
251
+ scope = defaultScope;
252
+ }
254
253
  const agentDir = scope === 'global'
255
254
  ? path_1.default.join(os_1.default.homedir(), '.relay', 'agents', parsed.owner, parsed.name)
256
255
  : path_1.default.join(projectPath, '.relay', 'agents', parsed.owner, parsed.name);
@@ -372,29 +371,35 @@ function registerInstall(program) {
372
371
  console.log(` 위치: \x1b[36m${agentDir}\x1b[0m`);
373
372
  console.log(` 범위: ${scopeLabel}`);
374
373
  console.log(` 파일: ${fileCount}개, symlink: ${deploy.symlinks.length}개`);
375
- if (resolvedAgent.commands.length > 0) {
374
+ const userCommands = resolvedAgent.commands.filter((c) => !c.name.startsWith('setup-'));
375
+ if (userCommands.length > 0) {
376
376
  console.log('\n 포함된 커맨드:');
377
- for (const cmd of resolvedAgent.commands) {
377
+ for (const cmd of userCommands) {
378
378
  console.log(` \x1b[33m/${cmd.name}\x1b[0m - ${cmd.description}`);
379
379
  }
380
380
  }
381
- // Usage hint (type-aware)
381
+ // Usage hint (type-aware, setup command 제외)
382
382
  const agentType = resolvedAgent.type;
383
+ const mainCommand = userCommands[0];
383
384
  if (agentType === 'passive') {
384
385
  console.log(`\n\x1b[33m💡 자동 적용됩니다. 별도 실행 없이 동작합니다.\x1b[0m`);
385
386
  }
386
- else if (agentType === 'hybrid' && resolvedAgent.commands && resolvedAgent.commands.length > 0) {
387
- console.log(`\n\x1b[33m💡 자동 적용 + \x1b[1m/${resolvedAgent.commands[0].name}\x1b[0m\x1b[33m 으로 추가 기능을 사용할 수 있습니다.\x1b[0m`);
387
+ else if (agentType === 'hybrid' && mainCommand) {
388
+ console.log(`\n\x1b[33m💡 자동 적용 + \x1b[1m/${mainCommand.name}\x1b[0m\x1b[33m 으로 추가 기능을 사용할 수 있습니다.\x1b[0m`);
388
389
  }
389
- else if (resolvedAgent.commands && resolvedAgent.commands.length > 0) {
390
- console.log(`\n\x1b[33m💡 사용법: \x1b[1m/${resolvedAgent.commands[0].name}\x1b[0m`);
390
+ else if (mainCommand) {
391
+ console.log(`\n\x1b[33m💡 사용법: \x1b[1m/${mainCommand.name}\x1b[0m`);
391
392
  }
392
393
  else {
393
394
  console.log(`\n\x1b[33m💡 설치 완료! AI 에이전트에서 사용할 수 있습니다.\x1b[0m`);
394
395
  }
395
- // Requires check
396
+ // Requires check + setup CTA
396
397
  const requiresResults = (0, installer_js_1.checkRequires)(agentDir);
397
398
  (0, installer_js_1.printRequiresCheck)(requiresResults);
399
+ const setupCmd = resolvedAgent.commands.find((c) => c.name.startsWith('setup-'));
400
+ if (setupCmd && requiresResults.some((r) => r.status === 'missing' || r.status === 'warn')) {
401
+ console.log(`\n \x1b[36m👉 설정이 필요합니다: \x1b[1m/${setupCmd.name}\x1b[0m\x1b[36m 을 실행하세요\x1b[0m`);
402
+ }
398
403
  }
399
404
  }
400
405
  catch (err) {
@@ -15,6 +15,7 @@ const paths_js_1 = require("../lib/paths.js");
15
15
  const error_report_js_1 = require("../lib/error-report.js");
16
16
  const step_tracker_js_1 = require("../lib/step-tracker.js");
17
17
  const git_operations_js_1 = require("../lib/git-operations.js");
18
+ const setup_command_js_1 = require("../lib/setup-command.js");
18
19
  // eslint-disable-next-line @typescript-eslint/no-var-requires
19
20
  const cliPkg = require('../../package.json');
20
21
  const VALID_DIRS = ['skills', 'agents', 'rules', 'commands', 'bin'];
@@ -726,6 +727,13 @@ function registerPublish(program) {
726
727
  const entrySlug = config.slug.startsWith('@') ? config.slug.slice(1) : config.slug;
727
728
  const entryFileName = entrySlug.replace('/', '-') + '.md';
728
729
  fs_1.default.writeFileSync(path_1.default.join(commandsDir, entryFileName), entryContent);
730
+ // Generate setup command if requires exist
731
+ const mainCmd = detectedCommands.find((c) => !c.name.startsWith('setup-'));
732
+ const setupContent = (0, setup_command_js_1.generateSetupCommand)(config.name, config.requires, mainCmd?.name);
733
+ if (setupContent) {
734
+ const setupFileName = `setup-${config.name}.md`;
735
+ fs_1.default.writeFileSync(path_1.default.join(commandsDir, setupFileName), setupContent);
736
+ }
729
737
  // Check git is available
730
738
  try {
731
739
  (0, git_operations_js_1.checkGitInstalled)();
@@ -0,0 +1,6 @@
1
+ import type { Requires } from '../commands/publish.js';
2
+ /**
3
+ * relay.yaml의 requires를 기반으로 setup slash command .md 내용을 생성한다.
4
+ * requires가 없거나 빈 객체이면 null을 반환한다.
5
+ */
6
+ export declare function generateSetupCommand(agentName: string, requires: Requires | undefined, mainCommandName?: string): string | null;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSetupCommand = generateSetupCommand;
4
+ /**
5
+ * relay.yaml의 requires를 기반으로 setup slash command .md 내용을 생성한다.
6
+ * requires가 없거나 빈 객체이면 null을 반환한다.
7
+ */
8
+ function generateSetupCommand(agentName, requires, mainCommandName) {
9
+ if (!requires)
10
+ return null;
11
+ const sections = [];
12
+ // runtime
13
+ if (requires.runtime) {
14
+ const items = [];
15
+ if (requires.runtime.node)
16
+ items.push(`- Node.js \`>=${requires.runtime.node}\``);
17
+ if (requires.runtime.python)
18
+ items.push(`- Python \`>=${requires.runtime.python}\``);
19
+ if (items.length > 0) {
20
+ sections.push(`### 런타임\n${items.join('\n')}`);
21
+ }
22
+ }
23
+ // cli
24
+ if (requires.cli && requires.cli.length > 0) {
25
+ const items = requires.cli.map((c) => {
26
+ const req = c.required !== false ? '필수' : '선택';
27
+ const install = c.install ? ` — 설치: \`${c.install}\`` : '';
28
+ return `- \`${c.name}\` (${req})${install}`;
29
+ });
30
+ sections.push(`### CLI 도구\n${items.join('\n')}`);
31
+ }
32
+ // env
33
+ if (requires.env && requires.env.length > 0) {
34
+ const items = requires.env.map((e) => {
35
+ const req = e.required !== false ? '필수' : '선택';
36
+ const desc = e.description ? ` — ${e.description}` : '';
37
+ const hint = e.setup_hint ? `\n 설정 방법:\n${e.setup_hint.split('\n').map((l) => ` ${l}`).join('\n')}` : '';
38
+ return `- \`${e.name}\` (${req})${desc}${hint}`;
39
+ });
40
+ sections.push(`### 환경변수\n${items.join('\n')}`);
41
+ }
42
+ // npm
43
+ if (requires.npm && requires.npm.length > 0) {
44
+ const items = requires.npm.map((n) => {
45
+ const name = typeof n === 'string' ? n : n.name;
46
+ const req = typeof n === 'string' ? '필수' : (n.required !== false ? '필수' : '선택');
47
+ return `- \`${name}\` (${req})`;
48
+ });
49
+ sections.push(`### npm 패키지\n${items.join('\n')}`);
50
+ }
51
+ // mcp
52
+ if (requires.mcp && requires.mcp.length > 0) {
53
+ const items = requires.mcp.map((m) => {
54
+ const req = m.required !== false ? '필수' : '선택';
55
+ const pkg = m.package ? ` — 패키지: \`${m.package}\`` : '';
56
+ const envList = m.env && m.env.length > 0 ? `\n 필요한 환경변수: ${m.env.map((e) => `\`${e}\``).join(', ')}` : '';
57
+ const config = m.config ? `\n 설정: \`${JSON.stringify(m.config)}\`` : '';
58
+ return `- \`${m.name}\` MCP 서버 (${req})${pkg}${envList}${config}`;
59
+ });
60
+ sections.push(`### MCP 서버\n${items.join('\n')}`);
61
+ }
62
+ if (sections.length === 0)
63
+ return null;
64
+ const body = `# ${agentName} 설정 가이드
65
+
66
+ 아래 요구사항을 각각 체크하고, 미충족 항목이 있으면 사용자가 설정할 수 있도록 안내하세요.
67
+ 모든 항목이 충족될 때까지 멈추지 말고 끝까지 진행하세요.
68
+ ${mainCommandName ? `\n모든 설정이 완료되면 \`/${mainCommandName}\`으로 에이전트를 사용할 수 있다고 안내하세요.` : ''}
69
+
70
+ ${sections.join('\n\n')}`;
71
+ return `---\ndescription: ${agentName} 설정 가이드 — 필수 요구사항을 확인하고 설정합니다\n---\n\n${body}\n`;
72
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relayax-cli",
3
- "version": "0.4.29",
3
+ "version": "0.4.31",
4
4
  "description": "RelayAX Agent Team Marketplace CLI - Install and manage agent teams",
5
5
  "main": "dist/index.js",
6
6
  "bin": {