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.
package/dist/commands/install.js
CHANGED
|
@@ -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
|
-
// ──
|
|
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 =
|
|
228
|
-
|
|
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
|
-
// ──
|
|
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
|
-
|
|
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
|
|
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' &&
|
|
387
|
-
console.log(`\n\x1b[33m💡 자동 적용 + \x1b[1m/${
|
|
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 (
|
|
390
|
-
console.log(`\n\x1b[33m💡 사용법: \x1b[1m/${
|
|
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) {
|
package/dist/commands/publish.js
CHANGED
|
@@ -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
|
+
}
|