relayax-cli 0.1.92 → 0.1.94
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/create.d.ts +2 -0
- package/dist/commands/create.js +140 -0
- package/dist/commands/init.js +254 -98
- package/dist/commands/install.js +7 -8
- package/dist/commands/invite.d.ts +2 -0
- package/dist/commands/invite.js +135 -0
- package/dist/commands/publish.js +28 -19
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +116 -0
- package/dist/commands/update.js +1 -1
- package/dist/index.js +4 -0
- package/dist/lib/api.d.ts +1 -1
- package/dist/lib/api.js +2 -3
- package/dist/lib/command-adapter.d.ts +16 -4
- package/dist/lib/command-adapter.js +116 -18
- package/dist/lib/config.js.bak +75 -0
- package/package.json +2 -1
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerCreate = registerCreate;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
10
|
+
const ai_tools_js_1 = require("../lib/ai-tools.js");
|
|
11
|
+
const command_adapter_js_1 = require("../lib/command-adapter.js");
|
|
12
|
+
const DEFAULT_DIRS = ['skills', 'commands'];
|
|
13
|
+
/**
|
|
14
|
+
* 글로벌 User 커맨드가 없으면 설치한다.
|
|
15
|
+
*/
|
|
16
|
+
function ensureGlobalUserCommands() {
|
|
17
|
+
const allExist = command_adapter_js_1.USER_COMMANDS.every((cmd) => fs_1.default.existsSync((0, command_adapter_js_1.getGlobalCommandPath)(cmd.id)));
|
|
18
|
+
if (allExist)
|
|
19
|
+
return false;
|
|
20
|
+
const globalDir = (0, command_adapter_js_1.getGlobalCommandDir)();
|
|
21
|
+
fs_1.default.mkdirSync(globalDir, { recursive: true });
|
|
22
|
+
for (const cmd of command_adapter_js_1.USER_COMMANDS) {
|
|
23
|
+
fs_1.default.writeFileSync((0, command_adapter_js_1.getGlobalCommandPath)(cmd.id), (0, command_adapter_js_1.formatCommandFile)(cmd));
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
function registerCreate(program) {
|
|
28
|
+
program
|
|
29
|
+
.command('create <name>')
|
|
30
|
+
.description('새 에이전트 팀 프로젝트를 생성합니다')
|
|
31
|
+
.action(async (name) => {
|
|
32
|
+
const json = program.opts().json ?? false;
|
|
33
|
+
const projectPath = process.cwd();
|
|
34
|
+
const relayDir = path_1.default.join(projectPath, '.relay');
|
|
35
|
+
const relayYamlPath = path_1.default.join(relayDir, 'relay.yaml');
|
|
36
|
+
const isTTY = Boolean(process.stdin.isTTY) && !json;
|
|
37
|
+
// 1. .relay/relay.yaml 이미 존재하면 에러
|
|
38
|
+
if (fs_1.default.existsSync(relayYamlPath)) {
|
|
39
|
+
if (json) {
|
|
40
|
+
console.error(JSON.stringify({ error: 'ALREADY_EXISTS', message: '.relay/relay.yaml이 이미 존재합니다.' }));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.error('.relay/relay.yaml이 이미 존재합니다. 기존 팀 프로젝트에서는 `relay init`을 사용하세요.');
|
|
44
|
+
}
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
// 2. 메타데이터 수집
|
|
48
|
+
const defaultSlug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
49
|
+
let description = '';
|
|
50
|
+
let tags = [];
|
|
51
|
+
let visibility = 'public';
|
|
52
|
+
if (isTTY) {
|
|
53
|
+
const { input: promptInput, select: promptSelect } = await import('@inquirer/prompts');
|
|
54
|
+
console.log(`\n \x1b[33m⚡\x1b[0m \x1b[1mrelay create\x1b[0m — 새 팀 프로젝트\n`);
|
|
55
|
+
description = await promptInput({
|
|
56
|
+
message: '팀 설명:',
|
|
57
|
+
validate: (v) => v.trim().length > 0 ? true : '설명을 입력해주세요.',
|
|
58
|
+
});
|
|
59
|
+
const tagsRaw = await promptInput({
|
|
60
|
+
message: '태그 (쉼표로 구분, 선택):',
|
|
61
|
+
default: '',
|
|
62
|
+
});
|
|
63
|
+
tags = tagsRaw.split(',').map((t) => t.trim()).filter(Boolean);
|
|
64
|
+
visibility = await promptSelect({
|
|
65
|
+
message: '공개 범위:',
|
|
66
|
+
choices: [
|
|
67
|
+
{ name: '전체 공개', value: 'public' },
|
|
68
|
+
{ name: '로그인 사용자만', value: 'login-only' },
|
|
69
|
+
{ name: '초대 코드 필요', value: 'invite-only' },
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// 3. .relay/relay.yaml 생성
|
|
74
|
+
fs_1.default.mkdirSync(relayDir, { recursive: true });
|
|
75
|
+
const yamlData = {
|
|
76
|
+
name,
|
|
77
|
+
slug: defaultSlug,
|
|
78
|
+
description,
|
|
79
|
+
version: '1.0.0',
|
|
80
|
+
tags,
|
|
81
|
+
visibility,
|
|
82
|
+
};
|
|
83
|
+
fs_1.default.writeFileSync(relayYamlPath, js_yaml_1.default.dump(yamlData, { lineWidth: 120 }), 'utf-8');
|
|
84
|
+
// 4. 디렉토리 구조 생성
|
|
85
|
+
const createdDirs = ['.relay'];
|
|
86
|
+
for (const dir of DEFAULT_DIRS) {
|
|
87
|
+
const dirPath = path_1.default.join(projectPath, dir);
|
|
88
|
+
if (!fs_1.default.existsSync(dirPath)) {
|
|
89
|
+
fs_1.default.mkdirSync(dirPath, { recursive: true });
|
|
90
|
+
createdDirs.push(dir);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// 5. 로컬 Builder 슬래시 커맨드 설치
|
|
94
|
+
const detected = (0, ai_tools_js_1.detectAgentCLIs)(projectPath);
|
|
95
|
+
const localResults = [];
|
|
96
|
+
for (const tool of detected) {
|
|
97
|
+
const adapter = (0, command_adapter_js_1.createAdapter)(tool);
|
|
98
|
+
const installed = [];
|
|
99
|
+
for (const cmd of command_adapter_js_1.BUILDER_COMMANDS) {
|
|
100
|
+
const filePath = path_1.default.join(projectPath, adapter.getFilePath(cmd.id));
|
|
101
|
+
fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
|
|
102
|
+
fs_1.default.writeFileSync(filePath, adapter.formatFile(cmd));
|
|
103
|
+
installed.push(cmd.id);
|
|
104
|
+
}
|
|
105
|
+
localResults.push({ tool: tool.name, commands: installed });
|
|
106
|
+
}
|
|
107
|
+
// 6. 글로벌 User 커맨드 (없으면 설치)
|
|
108
|
+
const globalInstalled = ensureGlobalUserCommands();
|
|
109
|
+
// 7. 출력
|
|
110
|
+
if (json) {
|
|
111
|
+
console.log(JSON.stringify({
|
|
112
|
+
status: 'ok',
|
|
113
|
+
name,
|
|
114
|
+
slug: defaultSlug,
|
|
115
|
+
relay_yaml: 'created',
|
|
116
|
+
directories: createdDirs,
|
|
117
|
+
local_commands: localResults,
|
|
118
|
+
global_commands: globalInstalled ? 'installed' : 'already',
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
console.log(`\n\x1b[32m✓ ${name} 팀 프로젝트 생성 완료\x1b[0m\n`);
|
|
123
|
+
console.log(` .relay/relay.yaml 생성됨`);
|
|
124
|
+
if (createdDirs.length > 0) {
|
|
125
|
+
console.log(` 디렉토리 생성: ${createdDirs.join(', ')}`);
|
|
126
|
+
}
|
|
127
|
+
if (localResults.length > 0) {
|
|
128
|
+
console.log(`\n \x1b[36mBuilder 커맨드 (로컬)\x1b[0m`);
|
|
129
|
+
for (const r of localResults) {
|
|
130
|
+
console.log(` ${r.tool}: ${r.commands.map((c) => `/${c}`).join(', ')}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (globalInstalled) {
|
|
134
|
+
console.log(`\n \x1b[36mUser 커맨드 (글로벌)\x1b[0m — 설치됨`);
|
|
135
|
+
}
|
|
136
|
+
console.log(`\n 다음 단계: \x1b[33m/relay-publish\x1b[0m로 마켓플레이스에 배포`);
|
|
137
|
+
console.log(' IDE를 재시작하면 슬래시 커맨드가 활성화됩니다.\n');
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -28,11 +28,13 @@ function showWelcome() {
|
|
|
28
28
|
' \x1b[33m⚡\x1b[0m \x1b[1mrelay\x1b[0m — Agent Team Marketplace',
|
|
29
29
|
'',
|
|
30
30
|
' 에이전트 CLI에 relay 커맨드를 연결합니다.',
|
|
31
|
-
' 설치 후 에이전트가 팀을 탐색하고 설치할 수 있습니다.',
|
|
32
31
|
'',
|
|
33
|
-
' \x1b[
|
|
34
|
-
'
|
|
35
|
-
'
|
|
32
|
+
' \x1b[2mUser 커맨드 (글로벌)\x1b[0m',
|
|
33
|
+
' /relay-explore 마켓플레이스 탐색',
|
|
34
|
+
' /relay-install 팀 설치',
|
|
35
|
+
' /relay-list 설치된 팀 목록',
|
|
36
|
+
' /relay-update 팀 업데이트',
|
|
37
|
+
' /relay-uninstall 팀 삭제',
|
|
36
38
|
'',
|
|
37
39
|
];
|
|
38
40
|
console.log(lines.join('\n'));
|
|
@@ -54,129 +56,283 @@ async function selectToolsInteractively(detectedIds) {
|
|
|
54
56
|
});
|
|
55
57
|
return selected;
|
|
56
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* 글로벌 User 커맨드를 ~/.claude/commands/relay/에 설치한다.
|
|
61
|
+
*/
|
|
62
|
+
function installGlobalUserCommands() {
|
|
63
|
+
const globalDir = (0, command_adapter_js_1.getGlobalCommandDir)();
|
|
64
|
+
fs_1.default.mkdirSync(globalDir, { recursive: true });
|
|
65
|
+
const commands = [];
|
|
66
|
+
for (const cmd of command_adapter_js_1.USER_COMMANDS) {
|
|
67
|
+
const filePath = (0, command_adapter_js_1.getGlobalCommandPath)(cmd.id);
|
|
68
|
+
fs_1.default.writeFileSync(filePath, (0, command_adapter_js_1.formatCommandFile)(cmd));
|
|
69
|
+
commands.push(cmd.id);
|
|
70
|
+
}
|
|
71
|
+
return { installed: true, commands };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 글로벌 User 커맨드가 이미 설치되어 있는지 확인한다.
|
|
75
|
+
*/
|
|
76
|
+
function hasGlobalUserCommands() {
|
|
77
|
+
return command_adapter_js_1.USER_COMMANDS.every((cmd) => fs_1.default.existsSync((0, command_adapter_js_1.getGlobalCommandPath)(cmd.id)));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 팀 프로젝트인지 감지한다 (.relay/relay.yaml 또는 팀 디렉토리 구조).
|
|
81
|
+
*/
|
|
82
|
+
function isTeamProject(projectPath) {
|
|
83
|
+
if (fs_1.default.existsSync(path_1.default.join(projectPath, '.relay', 'relay.yaml'))) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
return VALID_TEAM_DIRS.some((d) => {
|
|
87
|
+
const dirPath = path_1.default.join(projectPath, d);
|
|
88
|
+
if (!fs_1.default.existsSync(dirPath))
|
|
89
|
+
return false;
|
|
90
|
+
return fs_1.default.readdirSync(dirPath).filter((f) => !f.startsWith('.')).length > 0;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/** 레거시 User 커맨드 ID (이전에 로컬에 설치되던 것) */
|
|
94
|
+
const LEGACY_LOCAL_COMMAND_IDS = ['relay-explore', 'relay-install', 'relay-publish'];
|
|
95
|
+
/**
|
|
96
|
+
* 레거시 구조를 감지하고 마이그레이션한다.
|
|
97
|
+
* - relay.yaml (루트) → .relay/relay.yaml
|
|
98
|
+
* - portfolio/ (루트) → .relay/portfolio/
|
|
99
|
+
* - 로컬 레거시 슬래시 커맨드 제거 (글로벌로 이동되므로)
|
|
100
|
+
*/
|
|
101
|
+
function detectLegacy(projectPath) {
|
|
102
|
+
const details = [];
|
|
103
|
+
if (fs_1.default.existsSync(path_1.default.join(projectPath, 'relay.yaml'))) {
|
|
104
|
+
details.push('relay.yaml → .relay/relay.yaml');
|
|
105
|
+
}
|
|
106
|
+
const legacyPortfolio = path_1.default.join(projectPath, 'portfolio');
|
|
107
|
+
if (fs_1.default.existsSync(legacyPortfolio) && fs_1.default.statSync(legacyPortfolio).isDirectory()) {
|
|
108
|
+
const hasImages = fs_1.default.readdirSync(legacyPortfolio).some((f) => ['.png', '.jpg', '.jpeg', '.webp'].some((ext) => f.toLowerCase().endsWith(ext)));
|
|
109
|
+
if (hasImages) {
|
|
110
|
+
details.push('portfolio/ → .relay/portfolio/');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// 로컬에 레거시 슬래시 커맨드가 있는지 확인
|
|
114
|
+
const detected = (0, ai_tools_js_1.detectAgentCLIs)(projectPath);
|
|
115
|
+
for (const tool of detected) {
|
|
116
|
+
const cmdDir = path_1.default.join(projectPath, tool.skillsDir, 'commands', 'relay');
|
|
117
|
+
if (!fs_1.default.existsSync(cmdDir))
|
|
118
|
+
continue;
|
|
119
|
+
for (const cmdId of LEGACY_LOCAL_COMMAND_IDS) {
|
|
120
|
+
if (fs_1.default.existsSync(path_1.default.join(cmdDir, `${cmdId}.md`))) {
|
|
121
|
+
details.push(`${tool.skillsDir}/commands/relay/${cmdId}.md 제거 (글로벌로 이동)`);
|
|
122
|
+
break; // 한 번만 표시
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { hasLegacy: details.length > 0, details };
|
|
127
|
+
}
|
|
128
|
+
function runMigration(projectPath) {
|
|
129
|
+
const result = { relayYaml: false, portfolio: false, localCommandsCleaned: 0 };
|
|
130
|
+
const relayDir = path_1.default.join(projectPath, '.relay');
|
|
131
|
+
// 1. relay.yaml 이동
|
|
132
|
+
const legacyYaml = path_1.default.join(projectPath, 'relay.yaml');
|
|
133
|
+
const newYaml = path_1.default.join(relayDir, 'relay.yaml');
|
|
134
|
+
if (fs_1.default.existsSync(legacyYaml) && !fs_1.default.existsSync(newYaml)) {
|
|
135
|
+
fs_1.default.mkdirSync(relayDir, { recursive: true });
|
|
136
|
+
fs_1.default.renameSync(legacyYaml, newYaml);
|
|
137
|
+
result.relayYaml = true;
|
|
138
|
+
}
|
|
139
|
+
// 2. portfolio/ 이동
|
|
140
|
+
const legacyPortfolio = path_1.default.join(projectPath, 'portfolio');
|
|
141
|
+
const newPortfolio = path_1.default.join(relayDir, 'portfolio');
|
|
142
|
+
if (fs_1.default.existsSync(legacyPortfolio) && fs_1.default.statSync(legacyPortfolio).isDirectory() && !fs_1.default.existsSync(newPortfolio)) {
|
|
143
|
+
fs_1.default.renameSync(legacyPortfolio, newPortfolio);
|
|
144
|
+
result.portfolio = true;
|
|
145
|
+
}
|
|
146
|
+
// 3. 로컬 레거시 슬래시 커맨드 제거
|
|
147
|
+
const detected = (0, ai_tools_js_1.detectAgentCLIs)(projectPath);
|
|
148
|
+
for (const tool of detected) {
|
|
149
|
+
const cmdDir = path_1.default.join(projectPath, tool.skillsDir, 'commands', 'relay');
|
|
150
|
+
if (!fs_1.default.existsSync(cmdDir))
|
|
151
|
+
continue;
|
|
152
|
+
for (const cmdId of LEGACY_LOCAL_COMMAND_IDS) {
|
|
153
|
+
const cmdPath = path_1.default.join(cmdDir, `${cmdId}.md`);
|
|
154
|
+
if (fs_1.default.existsSync(cmdPath)) {
|
|
155
|
+
fs_1.default.unlinkSync(cmdPath);
|
|
156
|
+
result.localCommandsCleaned++;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// 디렉토리가 비었으면 삭제
|
|
160
|
+
const remaining = fs_1.default.readdirSync(cmdDir);
|
|
161
|
+
if (remaining.length === 0) {
|
|
162
|
+
fs_1.default.rmdirSync(cmdDir);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
57
167
|
function registerInit(program) {
|
|
58
168
|
program
|
|
59
169
|
.command('init')
|
|
60
|
-
.description('에이전트 CLI
|
|
170
|
+
.description('에이전트 CLI에 relay 슬래시 커맨드를 설치합니다')
|
|
61
171
|
.option('--tools <tools>', '설치할 에이전트 CLI 지정 (all 또는 쉼표 구분)')
|
|
62
172
|
.option('--update', '이미 설치된 슬래시 커맨드를 최신 버전으로 업데이트')
|
|
63
173
|
.action(async (opts) => {
|
|
64
174
|
const json = program.opts().json ?? false;
|
|
65
175
|
const projectPath = process.cwd();
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
console.
|
|
176
|
+
// ── 0. 레거시 마이그레이션 ──
|
|
177
|
+
const legacy = detectLegacy(projectPath);
|
|
178
|
+
let migrated = false;
|
|
179
|
+
if (legacy.hasLegacy) {
|
|
180
|
+
if (json) {
|
|
181
|
+
// JSON 모드: 자동 마이그레이션
|
|
182
|
+
const migrationResult = runMigration(projectPath);
|
|
183
|
+
migrated = migrationResult.relayYaml || migrationResult.portfolio || migrationResult.localCommandsCleaned > 0;
|
|
184
|
+
}
|
|
185
|
+
else if (process.stdin.isTTY) {
|
|
186
|
+
console.log('\n \x1b[33m⚠ 레거시 구조 감지\x1b[0m\n');
|
|
187
|
+
for (const d of legacy.details) {
|
|
188
|
+
console.log(` ${d}`);
|
|
79
189
|
}
|
|
80
|
-
|
|
81
|
-
|
|
190
|
+
console.log();
|
|
191
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
192
|
+
const doMigrate = await confirm({ message: '마이그레이션할까요?', default: true });
|
|
193
|
+
if (doMigrate) {
|
|
194
|
+
const migrationResult = runMigration(projectPath);
|
|
195
|
+
migrated = true;
|
|
196
|
+
console.log(`\n \x1b[32m✓ 마이그레이션 완료\x1b[0m`);
|
|
197
|
+
if (migrationResult.relayYaml)
|
|
198
|
+
console.log(' relay.yaml → .relay/relay.yaml');
|
|
199
|
+
if (migrationResult.portfolio)
|
|
200
|
+
console.log(' portfolio/ → .relay/portfolio/');
|
|
201
|
+
if (migrationResult.localCommandsCleaned > 0)
|
|
202
|
+
console.log(` 레거시 로컬 커맨드 ${migrationResult.localCommandsCleaned}개 제거`);
|
|
203
|
+
console.log();
|
|
82
204
|
}
|
|
83
|
-
process.exit(1);
|
|
84
205
|
}
|
|
85
|
-
|
|
206
|
+
else {
|
|
207
|
+
// 비TTY, 비JSON: 자동 마이그레이션
|
|
208
|
+
const migrationResult = runMigration(projectPath);
|
|
209
|
+
migrated = migrationResult.relayYaml || migrationResult.portfolio || migrationResult.localCommandsCleaned > 0;
|
|
210
|
+
}
|
|
86
211
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
212
|
+
const detected = (0, ai_tools_js_1.detectAgentCLIs)(projectPath);
|
|
213
|
+
const detectedIds = new Set(detected.map((t) => t.value));
|
|
214
|
+
const isBuilder = isTeamProject(projectPath);
|
|
215
|
+
// ── 1. 글로벌 User 커맨드 설치 ──
|
|
216
|
+
let globalStatus = 'already';
|
|
217
|
+
if (opts.update || !hasGlobalUserCommands()) {
|
|
218
|
+
installGlobalUserCommands();
|
|
219
|
+
globalStatus = opts.update ? 'updated' : 'installed';
|
|
90
220
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
221
|
+
// ── 2. 로컬 Builder 커맨드 (팀 프로젝트인 경우) ──
|
|
222
|
+
const localResults = [];
|
|
223
|
+
if (isBuilder) {
|
|
224
|
+
// 도구 선택
|
|
225
|
+
let targetToolIds;
|
|
226
|
+
if (opts.update) {
|
|
227
|
+
const installed = detected.filter((tool) => {
|
|
228
|
+
const cmdDir = path_1.default.join(projectPath, tool.skillsDir, 'commands', 'relay');
|
|
229
|
+
return fs_1.default.existsSync(cmdDir);
|
|
230
|
+
});
|
|
231
|
+
if (installed.length === 0 && !json) {
|
|
232
|
+
console.error('로컬에 설치된 relay Builder 커맨드가 없습니다.');
|
|
233
|
+
}
|
|
234
|
+
targetToolIds = installed.map((t) => t.value);
|
|
96
235
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
console.log('\n 선택된 도구가 없습니다.');
|
|
100
|
-
return;
|
|
236
|
+
else if (opts.tools) {
|
|
237
|
+
targetToolIds = resolveTools(opts.tools);
|
|
101
238
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
239
|
+
else if (!json && process.stdin.isTTY) {
|
|
240
|
+
showWelcome();
|
|
241
|
+
if (detected.length > 0) {
|
|
242
|
+
console.log(` 감지된 에이전트 CLI: \x1b[36m${detected.map((t) => t.name).join(', ')}\x1b[0m\n`);
|
|
243
|
+
}
|
|
244
|
+
console.log(' \x1b[2mBuilder 프로젝트 감지 → 로컬 Builder 커맨드도 설치합니다.\x1b[0m\n');
|
|
245
|
+
targetToolIds = await selectToolsInteractively(detectedIds);
|
|
246
|
+
if (targetToolIds.length === 0) {
|
|
247
|
+
console.log('\n 선택된 도구가 없습니다.');
|
|
248
|
+
// 글로벌은 이미 설치됨
|
|
249
|
+
if (globalStatus === 'installed') {
|
|
250
|
+
console.log(' 글로벌 User 커맨드는 설치되었습니다.\n');
|
|
251
|
+
}
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
111
254
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
fs_1.default.writeFileSync(filePath, fileContent);
|
|
127
|
-
installedCommands.push(cmd.id);
|
|
255
|
+
else {
|
|
256
|
+
if (detected.length === 0) {
|
|
257
|
+
if (json) {
|
|
258
|
+
console.error(JSON.stringify({
|
|
259
|
+
error: 'NO_AGENT_CLI',
|
|
260
|
+
message: '에이전트 CLI 디렉토리를 찾을 수 없습니다. --tools 옵션으로 지정하세요.',
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
263
|
+
// 글로벌은 설치했으므로 에러로 종료하지 않음
|
|
264
|
+
targetToolIds = [];
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
targetToolIds = detected.map((t) => t.value);
|
|
268
|
+
}
|
|
128
269
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (hasTeamDirs) {
|
|
145
|
-
const dirName = path_1.default.basename(projectPath);
|
|
146
|
-
const yaml = [
|
|
147
|
-
`name: "${dirName}"`,
|
|
148
|
-
`slug: "${dirName}"`,
|
|
149
|
-
`description: ""`,
|
|
150
|
-
`version: "1.0.0"`,
|
|
151
|
-
`tags: []`,
|
|
152
|
-
].join('\n') + '\n';
|
|
153
|
-
fs_1.default.writeFileSync(relayYamlPath, yaml);
|
|
154
|
-
relayYamlStatus = 'created';
|
|
270
|
+
// Builder 커맨드 설치
|
|
271
|
+
for (const toolId of targetToolIds) {
|
|
272
|
+
const tool = ai_tools_js_1.AI_TOOLS.find((t) => t.value === toolId);
|
|
273
|
+
if (!tool)
|
|
274
|
+
continue;
|
|
275
|
+
const adapter = (0, command_adapter_js_1.createAdapter)(tool);
|
|
276
|
+
const installedCommands = [];
|
|
277
|
+
for (const cmd of command_adapter_js_1.BUILDER_COMMANDS) {
|
|
278
|
+
const filePath = path_1.default.join(projectPath, adapter.getFilePath(cmd.id));
|
|
279
|
+
const fileContent = adapter.formatFile(cmd);
|
|
280
|
+
fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
|
|
281
|
+
fs_1.default.writeFileSync(filePath, fileContent);
|
|
282
|
+
installedCommands.push(cmd.id);
|
|
283
|
+
}
|
|
284
|
+
localResults.push({ tool: tool.name, commands: installedCommands });
|
|
155
285
|
}
|
|
156
286
|
}
|
|
157
|
-
|
|
287
|
+
else if (!json && process.stdin.isTTY) {
|
|
288
|
+
// User 모드: 글로벌만 설치, 안내 표시
|
|
289
|
+
showWelcome();
|
|
290
|
+
}
|
|
291
|
+
// ── 3. 출력 ──
|
|
158
292
|
if (json) {
|
|
159
293
|
console.log(JSON.stringify({
|
|
160
294
|
status: 'ok',
|
|
161
|
-
|
|
162
|
-
|
|
295
|
+
mode: isBuilder ? 'builder' : 'user',
|
|
296
|
+
migrated,
|
|
297
|
+
global: {
|
|
298
|
+
status: globalStatus,
|
|
299
|
+
path: (0, command_adapter_js_1.getGlobalCommandDir)(),
|
|
300
|
+
commands: command_adapter_js_1.USER_COMMANDS.map((c) => c.id),
|
|
301
|
+
},
|
|
302
|
+
local: isBuilder ? localResults : undefined,
|
|
163
303
|
}));
|
|
164
304
|
}
|
|
165
305
|
else {
|
|
166
|
-
console.log(`\n\x1b[32m✓ relay ${opts.update ? '
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
306
|
+
console.log(`\n\x1b[32m✓ relay ${opts.update ? '업데이트' : '초기화'} 완료\x1b[0m\n`);
|
|
307
|
+
// 글로벌
|
|
308
|
+
if (globalStatus !== 'already') {
|
|
309
|
+
console.log(` \x1b[36mUser 커맨드 (글로벌)\x1b[0m — ${globalStatus === 'updated' ? '업데이트됨' : '설치됨'}`);
|
|
310
|
+
console.log(` ${(0, command_adapter_js_1.getGlobalCommandDir)()}`);
|
|
311
|
+
for (const cmd of command_adapter_js_1.USER_COMMANDS) {
|
|
312
|
+
console.log(` /${cmd.id}`);
|
|
171
313
|
}
|
|
314
|
+
console.log();
|
|
172
315
|
}
|
|
173
|
-
|
|
174
|
-
console.log(
|
|
316
|
+
else {
|
|
317
|
+
console.log(` \x1b[36mUser 커맨드 (글로벌)\x1b[0m — 이미 설치됨`);
|
|
318
|
+
console.log();
|
|
319
|
+
}
|
|
320
|
+
// 로컬 Builder
|
|
321
|
+
if (localResults.length > 0) {
|
|
322
|
+
console.log(` \x1b[36mBuilder 커맨드 (로컬)\x1b[0m`);
|
|
323
|
+
for (const r of localResults) {
|
|
324
|
+
console.log(` ${r.tool}`);
|
|
325
|
+
for (const cmd of r.commands) {
|
|
326
|
+
console.log(` /${cmd}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
console.log();
|
|
175
330
|
}
|
|
176
|
-
|
|
177
|
-
console.log(
|
|
331
|
+
if (!isBuilder) {
|
|
332
|
+
console.log(' 팀을 만들려면 \x1b[33mrelay create <name>\x1b[0m을 사용하세요.');
|
|
333
|
+
console.log();
|
|
178
334
|
}
|
|
179
|
-
console.log('
|
|
335
|
+
console.log(' IDE를 재시작하면 슬래시 커맨드가 활성화됩니다.');
|
|
180
336
|
}
|
|
181
337
|
});
|
|
182
338
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -9,8 +9,7 @@ function registerInstall(program) {
|
|
|
9
9
|
program
|
|
10
10
|
.command('install <slug>')
|
|
11
11
|
.description('에이전트 팀 설치 (감지된 에이전트 CLI에 설치)')
|
|
12
|
-
.option('--path <install_path>', '설치 경로 지정
|
|
13
|
-
.option('--code <code>', '초대 코드 (invite-only 팀 설치 시 필요)')
|
|
12
|
+
.option('--path <install_path>', '설치 경로 지정')
|
|
14
13
|
.action(async (slug, opts) => {
|
|
15
14
|
const json = program.opts().json ?? false;
|
|
16
15
|
const installPath = (0, config_js_1.getInstallPath)(opts.path);
|
|
@@ -23,17 +22,17 @@ function registerInstall(program) {
|
|
|
23
22
|
if (visibility === 'login-only') {
|
|
24
23
|
const token = (0, config_js_1.loadToken)();
|
|
25
24
|
if (!token) {
|
|
26
|
-
|
|
27
|
-
console.error(JSON.stringify(err));
|
|
25
|
+
console.error(JSON.stringify({ error: 'LOGIN_REQUIRED', visibility: 'login-only', slug, message: '이 팀은 로그인이 필요합니다.' }));
|
|
28
26
|
process.exit(1);
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
29
|
else if (visibility === 'invite-only') {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
console.error(JSON.stringify(
|
|
30
|
+
const token = (0, config_js_1.loadToken)();
|
|
31
|
+
if (!token) {
|
|
32
|
+
console.error(JSON.stringify({ error: 'LOGIN_REQUIRED', visibility: 'invite-only', slug, message: '이 팀은 초대받은 사용자만 설치할 수 있습니다. 로그인이 필요합니다.' }));
|
|
35
33
|
process.exit(1);
|
|
36
34
|
}
|
|
35
|
+
// 실제 초대 여부는 서버(install API)에서 체크
|
|
37
36
|
}
|
|
38
37
|
// 3. Download package
|
|
39
38
|
const tarPath = await (0, storage_js_1.downloadPackage)(team.package_url, tempDir);
|
|
@@ -51,7 +50,7 @@ function registerInstall(program) {
|
|
|
51
50
|
};
|
|
52
51
|
(0, config_js_1.saveInstalled)(installed);
|
|
53
52
|
// 7. Report install (non-blocking)
|
|
54
|
-
await (0, api_js_1.reportInstall)(slug
|
|
53
|
+
await (0, api_js_1.reportInstall)(slug);
|
|
55
54
|
const result = {
|
|
56
55
|
status: 'ok',
|
|
57
56
|
team: team.name,
|