relayax-cli 0.2.25 → 0.2.27

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/README.md CHANGED
@@ -6,11 +6,11 @@ Agent Team Marketplace CLI - 에이전트 팀을 검색하고 설치하세요.
6
6
 
7
7
  ```bash
8
8
  # 글로벌 설치 없이 바로 사용
9
- npx relay-cli install contents-team
9
+ npx relay-cli install @author/team-name
10
10
 
11
11
  # 또는 글로벌 설치
12
12
  npm install -g relayax-cli
13
- relay install contents-team
13
+ relay install @author/team-name
14
14
  ```
15
15
 
16
16
  ## Commands
@@ -34,9 +34,9 @@ relay CLI는 에이전트가 1차 사용자입니다. 모든 출력은 JSON 기
34
34
 
35
35
  ```bash
36
36
  # 에이전트가 팀 검색
37
- relay search "콘텐츠" | jq '.results[].slug'
37
+ relay search "keyword" | jq '.results[].slug'
38
38
 
39
39
  # 에이전트가 팀 설치
40
- relay install contents-team
41
- # → {"status":"ok","team":"Contents Team","commands":[...]}
40
+ relay install @author/team-name
41
+ # → {"status":"ok","team":"Team Name","commands":[...]}
42
42
  ```
@@ -9,19 +9,15 @@ const path_1 = __importDefault(require("path"));
9
9
  const js_yaml_1 = __importDefault(require("js-yaml"));
10
10
  const ai_tools_js_1 = require("../lib/ai-tools.js");
11
11
  const command_adapter_js_1 = require("../lib/command-adapter.js");
12
+ const init_js_1 = require("./init.js");
12
13
  const DEFAULT_DIRS = ['.relay/skills', '.relay/commands'];
13
14
  /**
14
15
  * 글로벌 User 커맨드가 없으면 설치한다.
15
16
  */
16
17
  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)
18
+ if ((0, init_js_1.hasGlobalUserCommands)())
19
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
- }
20
+ (0, init_js_1.installGlobalUserCommands)();
25
21
  return true;
26
22
  }
27
23
  function registerCreate(program) {
@@ -1,11 +1,13 @@
1
1
  import { Command } from 'commander';
2
2
  /**
3
- * 글로벌 User 커맨드를 ~/.claude/commands/relay/에 설치한다.
3
+ * 글로벌 User 커맨드를 감지된 모든 에이전트 CLI에 설치한다.
4
+ * ~/{skillsDir}/commands/relay/ 에 설치.
4
5
  * 기존 파일 중 현재 커맨드 목록에 없는 것은 제거한다.
5
6
  */
6
7
  export declare function installGlobalUserCommands(): {
7
8
  installed: boolean;
8
9
  commands: string[];
10
+ tools: string[];
9
11
  };
10
12
  /**
11
13
  * 글로벌 User 커맨드가 이미 설치되어 있는지 확인한다.
@@ -35,10 +35,8 @@ function showWelcome() {
35
35
  ' 에이전트 CLI에 relay 커맨드를 연결합니다.',
36
36
  '',
37
37
  ' \x1b[2mUser 커맨드 (글로벌)\x1b[0m',
38
- ' /relay-explore 마켓플레이스 탐색',
39
- ' /relay-install 팀 설치',
40
- ' /relay-list 설치된 팀 목록',
41
- ' /relay-update 팀 업데이트',
38
+ ' /relay-install 탐색 & 설치',
39
+ ' /relay-status 설치 현황 & Space',
42
40
  ' /relay-uninstall 팀 삭제',
43
41
  '',
44
42
  ];
@@ -62,31 +60,39 @@ async function selectToolsInteractively(detectedIds) {
62
60
  return selected;
63
61
  }
64
62
  /**
65
- * 글로벌 User 커맨드를 ~/.claude/commands/relay/에 설치한다.
63
+ * 글로벌 User 커맨드를 감지된 모든 에이전트 CLI에 설치한다.
64
+ * ~/{skillsDir}/commands/relay/ 에 설치.
66
65
  * 기존 파일 중 현재 커맨드 목록에 없는 것은 제거한다.
67
66
  */
68
67
  function installGlobalUserCommands() {
69
- const globalDir = (0, command_adapter_js_1.getGlobalCommandDir)();
70
- fs_1.default.mkdirSync(globalDir, { recursive: true });
71
- // 현재 커맨드 ID 세트
68
+ const globalCLIs = (0, ai_tools_js_1.detectGlobalCLIs)();
72
69
  const currentIds = new Set(command_adapter_js_1.USER_COMMANDS.map((c) => c.id));
73
- // 기존 파일 중 현재 목록에 없는 것 제거
74
- if (fs_1.default.existsSync(globalDir)) {
75
- for (const file of fs_1.default.readdirSync(globalDir)) {
70
+ const commands = [];
71
+ const tools = [];
72
+ // 감지된 CLI가 없으면 Claude Code에만 설치 (기본)
73
+ const targetDirs = globalCLIs.length > 0
74
+ ? globalCLIs.map((t) => ({ name: t.name, dir: (0, command_adapter_js_1.getGlobalCommandDirForTool)(t.skillsDir), getPath: (id) => (0, command_adapter_js_1.getGlobalCommandPathForTool)(t.skillsDir, id) }))
75
+ : [{ name: 'Claude Code', dir: (0, command_adapter_js_1.getGlobalCommandDir)(), getPath: (id) => (0, command_adapter_js_1.getGlobalCommandPath)(id) }];
76
+ for (const target of targetDirs) {
77
+ fs_1.default.mkdirSync(target.dir, { recursive: true });
78
+ // 기존 파일 중 현재 목록에 없는 것 제거
79
+ for (const file of fs_1.default.readdirSync(target.dir)) {
76
80
  const id = file.replace(/\.md$/, '');
77
81
  if (!currentIds.has(id)) {
78
- fs_1.default.unlinkSync(path_1.default.join(globalDir, file));
82
+ fs_1.default.unlinkSync(path_1.default.join(target.dir, file));
79
83
  }
80
84
  }
85
+ // 현재 커맨드 설치 (덮어쓰기)
86
+ for (const cmd of command_adapter_js_1.USER_COMMANDS) {
87
+ fs_1.default.writeFileSync(target.getPath(cmd.id), (0, command_adapter_js_1.formatCommandFile)(cmd));
88
+ }
89
+ tools.push(target.name);
81
90
  }
82
- // 현재 커맨드 설치 (덮어쓰기)
83
- const commands = [];
91
+ // commands 목록은 번만
84
92
  for (const cmd of command_adapter_js_1.USER_COMMANDS) {
85
- const filePath = (0, command_adapter_js_1.getGlobalCommandPath)(cmd.id);
86
- fs_1.default.writeFileSync(filePath, (0, command_adapter_js_1.formatCommandFile)(cmd));
87
93
  commands.push(cmd.id);
88
94
  }
89
- return { installed: true, commands };
95
+ return { installed: true, commands, tools };
90
96
  }
91
97
  /**
92
98
  * 글로벌 User 커맨드가 이미 설치되어 있는지 확인한다.
@@ -128,15 +134,17 @@ function registerInit(program) {
128
134
  const isBuilder = isTeamProject(projectPath);
129
135
  // ── 1. 글로벌 User 커맨드 설치 ──
130
136
  let globalStatus = 'already';
137
+ let globalTools = [];
131
138
  if (opts.update || !hasGlobalUserCommands()) {
132
- const { commands } = installGlobalUserCommands();
139
+ const result = installGlobalUserCommands();
133
140
  globalStatus = opts.update ? 'updated' : 'installed';
141
+ globalTools = result.tools;
134
142
  // Register relay-core in installed.json
135
143
  const installed = (0, config_js_1.loadInstalled)();
136
144
  installed['relay-core'] = {
137
145
  version: pkg.version,
138
146
  installed_at: new Date().toISOString(),
139
- files: commands.map((c) => (0, command_adapter_js_1.getGlobalCommandPath)(c)),
147
+ files: result.commands.map((c) => (0, command_adapter_js_1.getGlobalCommandPath)(c)),
140
148
  type: 'system',
141
149
  };
142
150
  (0, config_js_1.saveInstalled)(installed);
@@ -234,8 +242,9 @@ function registerInit(program) {
234
242
  console.log(`\n\x1b[32m✓ relay ${opts.update ? '업데이트' : '초기화'} 완료\x1b[0m\n`);
235
243
  // 글로벌
236
244
  if (globalStatus !== 'already') {
245
+ const toolNames = globalTools.length > 0 ? globalTools.join(', ') : 'Claude Code';
237
246
  console.log(` \x1b[36mUser 커맨드 (글로벌)\x1b[0m — ${globalStatus === 'updated' ? '업데이트됨' : '설치됨'}`);
238
- console.log(` ${(0, command_adapter_js_1.getGlobalCommandDir)()}`);
247
+ console.log(` 감지된 CLI: \x1b[36m${toolNames}\x1b[0m`);
239
248
  for (const cmd of command_adapter_js_1.USER_COMMANDS) {
240
249
  console.log(` /${cmd.id}`);
241
250
  }
@@ -190,8 +190,9 @@ function registerInstall(program) {
190
190
  ...(spaceTarget ? { space_slug: spaceTarget.spaceSlug } : {}),
191
191
  };
192
192
  (0, config_js_1.saveInstalled)(installed);
193
- // 7. Report install (non-blocking)
193
+ // 7. Report install + usage ping (non-blocking)
194
194
  await (0, api_js_1.reportInstall)(slug, team.version);
195
+ (0, api_js_1.sendUsagePing)(slug, team.version);
195
196
  const result = {
196
197
  status: 'ok',
197
198
  team: team.name,
@@ -201,6 +202,13 @@ function registerInstall(program) {
201
202
  files: fileCount,
202
203
  install_path: teamDir,
203
204
  ...(spaceTarget ? { space_slug: spaceTarget.spaceSlug } : {}),
205
+ author: team.author ? {
206
+ username: team.author.username,
207
+ display_name: team.author.display_name ?? null,
208
+ contact_links: team.author.contact_links ?? [],
209
+ } : null,
210
+ welcome: team.welcome ?? null,
211
+ latest_post: team.latest_post ?? null,
204
212
  };
205
213
  if (json) {
206
214
  console.log(JSON.stringify(result));
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerPing(program: Command): void;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPing = registerPing;
4
+ const api_js_1 = require("../lib/api.js");
5
+ const config_js_1 = require("../lib/config.js");
6
+ const slug_js_1 = require("../lib/slug.js");
7
+ function registerPing(program) {
8
+ program
9
+ .command('ping <slug>')
10
+ .description('사용 현황을 기록합니다 (preamble용 경량 명령)')
11
+ .option('--quiet', '출력 없이 실행')
12
+ .action(async (slugInput, opts) => {
13
+ // Resolve slug
14
+ let slug;
15
+ if ((0, slug_js_1.isScopedSlug)(slugInput)) {
16
+ slug = slugInput;
17
+ }
18
+ else {
19
+ const localRegistry = (0, config_js_1.loadInstalled)();
20
+ const globalRegistry = (0, config_js_1.loadGlobalInstalled)();
21
+ const allKeys = [...Object.keys(localRegistry), ...Object.keys(globalRegistry)];
22
+ const match = allKeys.find((key) => {
23
+ const parsed = (0, slug_js_1.parseSlug)(key);
24
+ return parsed && parsed.name === slugInput;
25
+ });
26
+ slug = match ?? slugInput;
27
+ }
28
+ // Resolve version from installed registry
29
+ const local = (0, config_js_1.loadInstalled)();
30
+ const global = (0, config_js_1.loadGlobalInstalled)();
31
+ const entry = local[slug] ?? global[slug];
32
+ const version = entry?.version;
33
+ // Fire-and-forget ping
34
+ await (0, api_js_1.sendUsagePing)(slug, version);
35
+ if (!opts.quiet) {
36
+ console.log(`RELAY_READY: ${slug}`);
37
+ }
38
+ });
39
+ }
@@ -15,7 +15,7 @@ const preamble_js_1 = require("../lib/preamble.js");
15
15
  const version_check_js_1 = require("../lib/version-check.js");
16
16
  // eslint-disable-next-line @typescript-eslint/no-var-requires
17
17
  const cliPkg = require('../../package.json');
18
- const VALID_DIRS = ['skills', 'agents', 'rules', 'commands'];
18
+ const VALID_DIRS = ['skills', 'agents', 'rules', 'commands', 'bin'];
19
19
  const IMAGE_EXTS = ['.png', '.jpg', '.jpeg', '.webp'];
20
20
  /** 개별 포트폴리오 이미지 최대 크기 (2 MB) */
21
21
  const MAX_IMAGE_SIZE = 2 * 1024 * 1024;
@@ -125,20 +125,28 @@ function detectSkills(teamDir) {
125
125
  }
126
126
  return entries;
127
127
  }
128
- function generateRootSkillMd(config, commands, skills, scopedSlug) {
128
+ /**
129
+ * 팀 진입점 커맨드(commands/{author}-{name}.md)를 생성한다.
130
+ * root SKILL.md를 대체하여 팀의 얼굴 역할을 한다.
131
+ */
132
+ function generateEntryCommand(config, commands, skills, scopedSlug) {
129
133
  const lines = [];
130
134
  // Frontmatter
131
135
  lines.push('---');
132
- lines.push(`name: ${config.name}`);
133
136
  lines.push(`description: ${config.description}`);
134
137
  lines.push('---');
135
138
  lines.push('');
136
139
  // Preamble
137
140
  lines.push((0, preamble_js_1.generatePreamble)(scopedSlug));
138
141
  lines.push('');
142
+ // Team header
143
+ lines.push(`## ${config.name}`);
144
+ lines.push('');
145
+ lines.push(`v${config.version} — ${scopedSlug}`);
146
+ lines.push('');
139
147
  // Skills
140
148
  if (skills.length > 0) {
141
- lines.push('## 포함된 스킬');
149
+ lines.push('### 사용 가능한 스킬');
142
150
  lines.push('');
143
151
  for (const s of skills) {
144
152
  lines.push(`- **${s.name}**: ${s.description}`);
@@ -147,68 +155,17 @@ function generateRootSkillMd(config, commands, skills, scopedSlug) {
147
155
  }
148
156
  // Commands
149
157
  if (commands.length > 0) {
150
- lines.push('## 포함된 커맨드');
158
+ lines.push('### 사용 가능한 커맨드');
151
159
  lines.push('');
152
160
  for (const c of commands) {
153
161
  lines.push(`- **/${c.name}**: ${c.description}`);
154
162
  }
155
163
  lines.push('');
156
164
  }
157
- // Requires
158
- const req = config.requires;
159
- if (req) {
160
- lines.push('## 요구사항');
161
- lines.push('');
162
- if (req.env && req.env.length > 0) {
163
- lines.push('### 환경변수');
164
- for (const e of req.env) {
165
- const name = typeof e === 'string' ? e : e.name;
166
- const desc = typeof e === 'string' ? '' : e.description ?? '';
167
- const required = typeof e === 'string' ? true : e.required !== false;
168
- lines.push(`- \`${name}\` ${required ? '(필수)' : '(선택)'}${desc ? ' — ' + desc : ''}`);
169
- }
170
- lines.push('');
171
- }
172
- if (req.cli && req.cli.length > 0) {
173
- lines.push('### CLI 도구');
174
- for (const c of req.cli) {
175
- const install = c.install ? ` — 설치: \`${c.install}\`` : '';
176
- lines.push(`- \`${c.name}\`${install}`);
177
- }
178
- lines.push('');
179
- }
180
- if (req.npm && req.npm.length > 0) {
181
- lines.push('### npm 패키지');
182
- for (const n of req.npm) {
183
- const name = typeof n === 'string' ? n : n.name;
184
- lines.push(`- \`${name}\``);
185
- }
186
- lines.push('');
187
- }
188
- if (req.mcp && req.mcp.length > 0) {
189
- lines.push('### MCP 서버');
190
- for (const m of req.mcp) {
191
- const pkg = m.package ? ` (\`${m.package}\`)` : '';
192
- lines.push(`- **${m.name}**${pkg}`);
193
- }
194
- lines.push('');
195
- }
196
- if (req.runtime) {
197
- lines.push('### 런타임');
198
- if (req.runtime.node)
199
- lines.push(`- Node.js ${req.runtime.node}`);
200
- if (req.runtime.python)
201
- lines.push(`- Python ${req.runtime.python}`);
202
- lines.push('');
203
- }
204
- if (req.teams && req.teams.length > 0) {
205
- lines.push('### 의존 팀');
206
- for (const t of req.teams) {
207
- lines.push(`- \`${t}\``);
208
- }
209
- lines.push('');
210
- }
211
- }
165
+ lines.push('### 시작');
166
+ lines.push('');
167
+ lines.push('원하는 작업을 말하거나 위 커맨드를 직접 실행하세요.');
168
+ lines.push('');
212
169
  return lines.join('\n');
213
170
  }
214
171
  function countDir(teamDir, dirName) {
@@ -292,11 +249,8 @@ function resolveLongDescription(teamDir, yamlValue) {
292
249
  async function createTarball(teamDir) {
293
250
  const tmpFile = path_1.default.join(os_1.default.tmpdir(), `relay-publish-${Date.now()}.tar.gz`);
294
251
  const dirsToInclude = VALID_DIRS.filter((d) => fs_1.default.existsSync(path_1.default.join(teamDir, d)));
295
- // Include root SKILL.md and GUIDE.html if they exist
252
+ // Include GUIDE.html if it exists (root SKILL.md is replaced by entry command)
296
253
  const entries = [...dirsToInclude];
297
- if (fs_1.default.existsSync(path_1.default.join(teamDir, 'SKILL.md'))) {
298
- entries.push('SKILL.md');
299
- }
300
254
  if (fs_1.default.existsSync(path_1.default.join(teamDir, 'GUIDE.html'))) {
301
255
  entries.push('GUIDE.html');
302
256
  }
@@ -591,10 +545,19 @@ function registerPublish(program) {
591
545
  console.error(`포트폴리오 이미지: ${portfolioEntries.length}개`);
592
546
  }
593
547
  }
594
- // Generate root SKILL.md for skill discovery and update checks
548
+ // Generate bin/relay-preamble.sh (self-contained tracking + update check)
549
+ (0, preamble_js_1.generatePreambleBin)(relayDir, config.slug, config_js_1.API_URL);
550
+ // Generate entry command (commands/{author}-{name}.md)
595
551
  const detectedSkills = detectSkills(relayDir);
596
- const rootSkillContent = generateRootSkillMd(config, detectedCommands, detectedSkills, config.slug);
597
- fs_1.default.writeFileSync(path_1.default.join(relayDir, 'SKILL.md'), rootSkillContent);
552
+ const entryContent = generateEntryCommand(config, detectedCommands, detectedSkills, config.slug);
553
+ const commandsDir = path_1.default.join(relayDir, 'commands');
554
+ if (!fs_1.default.existsSync(commandsDir)) {
555
+ fs_1.default.mkdirSync(commandsDir, { recursive: true });
556
+ }
557
+ // slug: @alice/cardnews → alice-cardnews.md
558
+ const entrySlug = config.slug.startsWith('@') ? config.slug.slice(1) : config.slug;
559
+ const entryFileName = entrySlug.replace('/', '-') + '.md';
560
+ fs_1.default.writeFileSync(path_1.default.join(commandsDir, entryFileName), entryContent);
598
561
  let tarPath = null;
599
562
  try {
600
563
  tarPath = await createTarball(relayDir);
@@ -602,13 +565,14 @@ function registerPublish(program) {
602
565
  console.error(`업로드 중...`);
603
566
  }
604
567
  const result = await publishToApi(token, tarPath, metadata, relayDir, portfolioEntries);
605
- // Update local SKILL.md preamble with scoped slug from server (non-fatal)
568
+ // Update entry command preamble with scoped slug from server (non-fatal)
606
569
  try {
607
570
  if (result.slug && result.slug !== config.slug) {
608
- const localSkillMd = path_1.default.join(relayDir, 'SKILL.md');
609
- if (fs_1.default.existsSync(localSkillMd)) {
571
+ const serverSlug = result.slug.startsWith('@') ? result.slug.slice(1) : result.slug;
572
+ const entryFile = path_1.default.join(relayDir, 'commands', serverSlug.replace('/', '-') + '.md');
573
+ if (fs_1.default.existsSync(entryFile)) {
610
574
  const { injectPreamble } = await import('../lib/preamble.js');
611
- injectPreamble(localSkillMd, result.slug);
575
+ injectPreamble(entryFile, result.slug);
612
576
  }
613
577
  }
614
578
  }
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ const changelog_js_1 = require("./commands/changelog.js");
19
19
  const join_js_1 = require("./commands/join.js");
20
20
  const spaces_js_1 = require("./commands/spaces.js");
21
21
  const deploy_record_js_1 = require("./commands/deploy-record.js");
22
+ const ping_js_1 = require("./commands/ping.js");
22
23
  // eslint-disable-next-line @typescript-eslint/no-var-requires
23
24
  const pkg = require('../package.json');
24
25
  const program = new commander_1.Command();
@@ -44,4 +45,5 @@ program
44
45
  (0, join_js_1.registerJoin)(program);
45
46
  (0, spaces_js_1.registerSpaces)(program);
46
47
  (0, deploy_record_js_1.registerDeployRecord)(program);
48
+ (0, ping_js_1.registerPing)(program);
47
49
  program.parse();
@@ -12,3 +12,8 @@ export declare const AI_TOOLS: AITool[];
12
12
  * 프로젝트 디렉토리에서 에이전트 CLI 디렉토리를 감지한다.
13
13
  */
14
14
  export declare function detectAgentCLIs(projectPath: string): AITool[];
15
+ /**
16
+ * 홈 디렉토리에서 글로벌 에이전트 CLI 디렉토리를 감지한다.
17
+ * ~/{skillsDir}/ 가 존재하는 CLI를 반환.
18
+ */
19
+ export declare function detectGlobalCLIs(): AITool[];
@@ -5,7 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.AI_TOOLS = void 0;
7
7
  exports.detectAgentCLIs = detectAgentCLIs;
8
+ exports.detectGlobalCLIs = detectGlobalCLIs;
8
9
  const fs_1 = __importDefault(require("fs"));
10
+ const os_1 = __importDefault(require("os"));
9
11
  const path_1 = __importDefault(require("path"));
10
12
  /**
11
13
  * Agent Skills 표준을 지원하는 에이전트 CLI 목록.
@@ -43,3 +45,11 @@ exports.AI_TOOLS = [
43
45
  function detectAgentCLIs(projectPath) {
44
46
  return exports.AI_TOOLS.filter((tool) => fs_1.default.existsSync(path_1.default.join(projectPath, tool.skillsDir)));
45
47
  }
48
+ /**
49
+ * 홈 디렉토리에서 글로벌 에이전트 CLI 디렉토리를 감지한다.
50
+ * ~/{skillsDir}/ 가 존재하는 CLI를 반환.
51
+ */
52
+ function detectGlobalCLIs() {
53
+ const home = path_1.default.join(os_1.default.homedir());
54
+ return exports.AI_TOOLS.filter((tool) => fs_1.default.existsSync(path_1.default.join(home, tool.skillsDir)));
55
+ }
@@ -15,14 +15,23 @@ export interface ToolCommandAdapter {
15
15
  */
16
16
  export declare function createAdapter(tool: AITool): ToolCommandAdapter;
17
17
  /**
18
- * 글로벌 슬래시 커맨드 파일 경로.
18
+ * 글로벌 슬래시 커맨드 파일 경로 (Claude Code 기본).
19
19
  * ~/.claude/commands/relay/{id}.md
20
20
  */
21
21
  export declare function getGlobalCommandPath(commandId: string): string;
22
22
  /**
23
- * 글로벌 슬래시 커맨드 디렉토리.
23
+ * 글로벌 슬래시 커맨드 디렉토리 (Claude Code 기본).
24
24
  */
25
25
  export declare function getGlobalCommandDir(): string;
26
+ /**
27
+ * 특정 AI 도구의 글로벌 커맨드 디렉토리.
28
+ * ~/{skillsDir}/commands/relay/
29
+ */
30
+ export declare function getGlobalCommandDirForTool(skillsDir: string): string;
31
+ /**
32
+ * 특정 AI 도구의 글로벌 커맨드 파일 경로.
33
+ */
34
+ export declare function getGlobalCommandPathForTool(skillsDir: string, commandId: string): string;
26
35
  /**
27
36
  * 커맨드 콘텐츠를 파일 형식으로 포맷.
28
37
  */