relayax-cli 0.4.28 → 0.4.30
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.d.ts +3 -2
- package/dist/commands/init.js +6 -3
- package/dist/commands/install.js +56 -12
- package/dist/lib/installer.d.ts +2 -1
- package/dist/lib/installer.js +21 -23
- package/package.json +1 -1
package/dist/commands/init.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
|
|
2
|
+
import { type AITool } from '../lib/ai-tools.js';
|
|
3
|
+
export declare function installGlobalUserCommands(overrideTools?: AITool[]): {
|
|
3
4
|
installed: boolean;
|
|
4
5
|
commands: string[];
|
|
5
6
|
tools: string[];
|
|
@@ -8,5 +9,5 @@ export declare function installGlobalUserCommands(): {
|
|
|
8
9
|
/**
|
|
9
10
|
* 글로벌 User 커맨드가 이미 설치되어 있는지 확인한다.
|
|
10
11
|
*/
|
|
11
|
-
export declare function hasGlobalUserCommands(): boolean;
|
|
12
|
+
export declare function hasGlobalUserCommands(overrideTools?: AITool[]): boolean;
|
|
12
13
|
export declare function registerInit(program: Command): void;
|
package/dist/commands/init.js
CHANGED
|
@@ -44,8 +44,8 @@ const LEGACY_COMMANDS = {
|
|
|
44
44
|
'relay-install': 'relay install (CLI) 또는 /relay-explore',
|
|
45
45
|
'relay-publish': 'relay publish --patch (CLI) 또는 /relay-create',
|
|
46
46
|
};
|
|
47
|
-
function installGlobalUserCommands() {
|
|
48
|
-
const globalCLIs = (0, ai_tools_js_1.detectGlobalCLIs)();
|
|
47
|
+
function installGlobalUserCommands(overrideTools) {
|
|
48
|
+
const globalCLIs = overrideTools ?? (0, ai_tools_js_1.detectGlobalCLIs)();
|
|
49
49
|
const currentIds = new Set(command_adapter_js_1.USER_COMMANDS.map((c) => c.id));
|
|
50
50
|
const commands = [];
|
|
51
51
|
const tools = [];
|
|
@@ -77,7 +77,10 @@ function installGlobalUserCommands() {
|
|
|
77
77
|
/**
|
|
78
78
|
* 글로벌 User 커맨드가 이미 설치되어 있는지 확인한다.
|
|
79
79
|
*/
|
|
80
|
-
function hasGlobalUserCommands() {
|
|
80
|
+
function hasGlobalUserCommands(overrideTools) {
|
|
81
|
+
if (overrideTools) {
|
|
82
|
+
return overrideTools.every((tool) => command_adapter_js_1.USER_COMMANDS.every((cmd) => fs_1.default.existsSync((0, command_adapter_js_1.getGlobalCommandPathForTool)(tool.skillsDir, cmd.id))));
|
|
83
|
+
}
|
|
81
84
|
return command_adapter_js_1.USER_COMMANDS.every((cmd) => fs_1.default.existsSync((0, command_adapter_js_1.getGlobalCommandPath)(cmd.id)));
|
|
82
85
|
}
|
|
83
86
|
function registerInit(program) {
|
package/dist/commands/install.js
CHANGED
|
@@ -18,6 +18,7 @@ const paths_js_1 = require("../lib/paths.js");
|
|
|
18
18
|
const error_report_js_1 = require("../lib/error-report.js");
|
|
19
19
|
const step_tracker_js_1 = require("../lib/step-tracker.js");
|
|
20
20
|
const installer_js_1 = require("../lib/installer.js");
|
|
21
|
+
const ai_tools_js_1 = require("../lib/ai-tools.js");
|
|
21
22
|
function registerInstall(program) {
|
|
22
23
|
program
|
|
23
24
|
.command('install <slug>')
|
|
@@ -30,13 +31,6 @@ function registerInstall(program) {
|
|
|
30
31
|
const json = program.opts().json ?? false;
|
|
31
32
|
const projectPath = (0, paths_js_1.resolveProjectPath)(_opts.project);
|
|
32
33
|
const tempDir = (0, storage_js_1.makeTempDir)();
|
|
33
|
-
// Auto-init: 글로벌 커맨드가 없으면 자동 설치
|
|
34
|
-
if (!(0, init_js_1.hasGlobalUserCommands)()) {
|
|
35
|
-
if (!json) {
|
|
36
|
-
console.error('\x1b[33m⚙ 글로벌 커맨드를 자동 설치합니다...\x1b[0m');
|
|
37
|
-
}
|
|
38
|
-
(0, init_js_1.installGlobalUserCommands)();
|
|
39
|
-
}
|
|
40
34
|
(0, step_tracker_js_1.trackCommand)('install', { slug: slugInput });
|
|
41
35
|
try {
|
|
42
36
|
// Resolve scoped slug and fetch agent metadata
|
|
@@ -202,10 +196,60 @@ function registerInstall(program) {
|
|
|
202
196
|
throw new Error('에이전트 정보를 가져오지 못했습니다.');
|
|
203
197
|
// Re-bind as non-optional so TypeScript tracks the narrowing through nested scopes
|
|
204
198
|
let resolvedAgent = agent;
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
199
|
+
const isTTY = Boolean(process.stdin.isTTY);
|
|
200
|
+
const interactive = isTTY && !json;
|
|
201
|
+
const defaultScope = resolvedAgent.recommended_scope ?? (resolvedAgent.type === 'passive' ? 'local' : 'global');
|
|
202
|
+
// ── 1. AI tools 선택 (scope 무관, 항상 홈 디렉토리에서 감지) ──
|
|
203
|
+
let selectedTools;
|
|
204
|
+
if (interactive) {
|
|
205
|
+
const detected = (0, ai_tools_js_1.detectGlobalCLIs)();
|
|
206
|
+
if (!detected.some((t) => t.value === 'claude')) {
|
|
207
|
+
detected.push({ name: 'Claude Code', value: 'claude', skillsDir: '.claude' });
|
|
208
|
+
}
|
|
209
|
+
const detectedValues = new Set(detected.map((t) => t.value));
|
|
210
|
+
const { checkbox } = await import('@inquirer/prompts');
|
|
211
|
+
selectedTools = await checkbox({
|
|
212
|
+
message: '설치할 AI 도구를 선택하��요 (감지된 도구는 자동 선���됨)',
|
|
213
|
+
choices: ai_tools_js_1.AI_TOOLS.map((t) => ({
|
|
214
|
+
name: t.name,
|
|
215
|
+
value: t,
|
|
216
|
+
checked: detectedValues.has(t.value),
|
|
217
|
+
})),
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
// ── 2. 글로벌 slash commands 설치/업데이트 ──
|
|
221
|
+
if (selectedTools) {
|
|
222
|
+
(0, init_js_1.installGlobalUserCommands)(selectedTools);
|
|
223
|
+
}
|
|
224
|
+
else if (!(0, init_js_1.hasGlobalUserCommands)()) {
|
|
225
|
+
if (!json) {
|
|
226
|
+
console.error('\x1b[33m⚙ 글로벌 커맨드를 자동 설치합니다...\x1b[0m');
|
|
227
|
+
}
|
|
228
|
+
(0, init_js_1.installGlobalUserCommands)();
|
|
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
|
+
}
|
|
209
253
|
const agentDir = scope === 'global'
|
|
210
254
|
? path_1.default.join(os_1.default.homedir(), '.relay', 'agents', parsed.owner, parsed.name)
|
|
211
255
|
: path_1.default.join(projectPath, '.relay', 'agents', parsed.owner, parsed.name);
|
|
@@ -256,7 +300,7 @@ function registerInstall(program) {
|
|
|
256
300
|
// 4.5. Inject preamble (update check) into SKILL.md and commands
|
|
257
301
|
(0, preamble_js_1.injectPreambleToAgent)(agentDir, slug);
|
|
258
302
|
// 5. Deploy symlinks to detected AI tool directories
|
|
259
|
-
const deploy = await (0, installer_js_1.deploySymlinks)(agentDir, scope, projectPath);
|
|
303
|
+
const deploy = await (0, installer_js_1.deploySymlinks)(agentDir, scope, projectPath, selectedTools);
|
|
260
304
|
for (const w of deploy.warnings) {
|
|
261
305
|
if (!json)
|
|
262
306
|
console.error(`\x1b[33m${w}\x1b[0m`);
|
package/dist/lib/installer.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AITool } from './ai-tools.js';
|
|
1
2
|
export interface DeployResult {
|
|
2
3
|
symlinks: string[];
|
|
3
4
|
warnings: string[];
|
|
@@ -11,7 +12,7 @@ export interface DeployResult {
|
|
|
11
12
|
* @param scope 'global' | 'local'
|
|
12
13
|
* @param projectPath 프로젝트 루트 경로 (local scope 시 사용)
|
|
13
14
|
*/
|
|
14
|
-
export declare function deploySymlinks(agentDir: string, scope: 'global' | 'local', projectPath: string): Promise<DeployResult>;
|
|
15
|
+
export declare function deploySymlinks(agentDir: string, scope: 'global' | 'local', projectPath: string, overrideTools?: AITool[]): Promise<DeployResult>;
|
|
15
16
|
/**
|
|
16
17
|
* symlink 목록을 기반으로 symlink를 제거한다.
|
|
17
18
|
*/
|
package/dist/lib/installer.js
CHANGED
|
@@ -26,33 +26,31 @@ const SYMLINK_DIRS = ['skills', 'commands', 'agents', 'rules'];
|
|
|
26
26
|
* @param scope 'global' | 'local'
|
|
27
27
|
* @param projectPath 프로젝트 루트 경로 (local scope 시 사용)
|
|
28
28
|
*/
|
|
29
|
-
async function deploySymlinks(agentDir, scope, projectPath) {
|
|
29
|
+
async function deploySymlinks(agentDir, scope, projectPath, overrideTools) {
|
|
30
30
|
const result = { symlinks: [], warnings: [] };
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
: (0, ai_tools_js_1.detectAgentCLIs)(projectPath);
|
|
35
|
-
// 글로벌: Claude Code를 기본으로 포함
|
|
36
|
-
if (scope === 'global') {
|
|
37
|
-
const hasClaudeCode = tools.some((t) => t.value === 'claude');
|
|
38
|
-
if (!hasClaudeCode) {
|
|
39
|
-
tools.push({ name: 'Claude Code', value: 'claude', skillsDir: '.claude' });
|
|
40
|
-
}
|
|
31
|
+
let tools;
|
|
32
|
+
if (overrideTools) {
|
|
33
|
+
tools = overrideTools;
|
|
41
34
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
message: `Select tools to set up (${ai_tools_js_1.AI_TOOLS.length} available)`,
|
|
48
|
-
choices: ai_tools_js_1.AI_TOOLS.map((t) => ({ name: t.name, value: t })),
|
|
49
|
-
});
|
|
50
|
-
tools.push(...selected);
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
// Non-TTY (JSON 모드 등): Claude Code 기본
|
|
35
|
+
else {
|
|
36
|
+
tools = scope === 'global'
|
|
37
|
+
? (0, ai_tools_js_1.detectGlobalCLIs)()
|
|
38
|
+
: (0, ai_tools_js_1.detectAgentCLIs)(projectPath);
|
|
39
|
+
if (scope === 'global' && !tools.some((t) => t.value === 'claude')) {
|
|
54
40
|
tools.push({ name: 'Claude Code', value: 'claude', skillsDir: '.claude' });
|
|
55
41
|
}
|
|
42
|
+
if (scope === 'local' && tools.length === 0) {
|
|
43
|
+
if (process.stdout.isTTY) {
|
|
44
|
+
const { checkbox } = await import('@inquirer/prompts');
|
|
45
|
+
tools = await checkbox({
|
|
46
|
+
message: `Select tools to set up (${ai_tools_js_1.AI_TOOLS.length} available)`,
|
|
47
|
+
choices: ai_tools_js_1.AI_TOOLS.map((t) => ({ name: t.name, value: t })),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
tools = [{ name: 'Claude Code', value: 'claude', skillsDir: '.claude' }];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
56
54
|
}
|
|
57
55
|
for (const tool of tools) {
|
|
58
56
|
const baseDir = scope === 'global'
|