relayax-cli 0.3.65 → 0.3.67

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.
@@ -17,7 +17,7 @@ const version_check_js_1 = require("../lib/version-check.js");
17
17
  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
- const index_js_1 = require("../prompts/index.js");
20
+ // GUIDE_INSTRUCTION removed — share text now uses npx install command directly
21
21
  // eslint-disable-next-line @typescript-eslint/no-var-requires
22
22
  const cliPkg = require('../../package.json');
23
23
  const VALID_DIRS = ['skills', 'agents', 'rules', 'commands', 'bin'];
@@ -49,6 +49,7 @@ function parseRelayYaml(content) {
49
49
  visibility,
50
50
  type,
51
51
  source: raw.source ? String(raw.source) : undefined,
52
+ org_slug: raw.org_slug ? String(raw.org_slug) : undefined,
52
53
  };
53
54
  }
54
55
  function detectCommands(agentDir) {
@@ -289,7 +290,11 @@ function registerPublish(program) {
289
290
  .description('현재 에이전트 패키지를 Space에 배포합니다 (relay.yaml 필요)')
290
291
  .option('--token <token>', '인증 토큰')
291
292
  .option('--space <slug>', '배포할 Space 지정')
293
+ .option('--org <slug>', 'Organization slug 지정')
292
294
  .option('--version <version>', '배포 버전 지정 (relay.yaml 업데이트)')
295
+ .option('--patch', 'patch 버전 범프')
296
+ .option('--minor', 'minor 버전 범프')
297
+ .option('--major', 'major 버전 범프')
293
298
  .option('--project <dir>', '프로젝트 루트 경로 (기본: cwd, 환경변수: RELAY_PROJECT_PATH)')
294
299
  .action(async (opts) => {
295
300
  const json = program.opts().json ?? false;
@@ -305,22 +310,6 @@ function registerPublish(program) {
305
310
  console.error(`\n\x1b[33m⚠ relay v${cliUpdate.latest}이 있습니다\x1b[0m (현재 v${cliUpdate.current})`);
306
311
  console.error(' 최신 버전에서는 설치자에게 자동 업데이트 알림이 지원됩니다.');
307
312
  console.error(` 업데이트: \x1b[36mnpm update -g relayax-cli\x1b[0m\n`);
308
- try {
309
- const { confirm } = await import('@inquirer/prompts');
310
- const shouldContinue = await confirm({
311
- message: '현재 버전으로 계속 배포할까요?',
312
- default: true,
313
- });
314
- if (!shouldContinue) {
315
- (0, error_report_js_1.reportCliError)('publish', 'CANCELLED_CLI_UPDATE', `current:${cliUpdate.current} latest:${cliUpdate.latest}`);
316
- console.error('\n배포를 취소했습니다. CLI를 업데이트한 후 다시 시도하세요.');
317
- process.exit(0);
318
- }
319
- console.error('');
320
- }
321
- catch {
322
- // non-interactive fallback: continue
323
- }
324
313
  }
325
314
  }
326
315
  // Check .relay/relay.yaml exists
@@ -399,13 +388,34 @@ function registerPublish(program) {
399
388
  }));
400
389
  process.exit(1);
401
390
  }
402
- // Version bump: --version flag takes priority
391
+ // Version bump: --version flag takes priority, then --patch/--minor/--major
392
+ const hasBumpFlag = Boolean(opts.patch || opts.minor || opts.major);
403
393
  if (opts.version) {
404
394
  config.version = opts.version;
405
395
  const yamlData = js_yaml_1.default.load(fs_1.default.readFileSync(relayYamlPath, 'utf-8'));
406
396
  yamlData.version = opts.version;
407
397
  fs_1.default.writeFileSync(relayYamlPath, js_yaml_1.default.dump(yamlData, { lineWidth: 120 }), 'utf-8');
408
398
  }
399
+ else if (hasBumpFlag) {
400
+ const [major, minor, patch] = config.version.split('.').map(Number);
401
+ let newVersion;
402
+ if (opts.major) {
403
+ newVersion = `${major + 1}.0.0`;
404
+ }
405
+ else if (opts.minor) {
406
+ newVersion = `${major}.${minor + 1}.0`;
407
+ }
408
+ else {
409
+ newVersion = `${major}.${minor}.${patch + 1}`;
410
+ }
411
+ config.version = newVersion;
412
+ const yamlData = js_yaml_1.default.load(fs_1.default.readFileSync(relayYamlPath, 'utf-8'));
413
+ yamlData.version = newVersion;
414
+ fs_1.default.writeFileSync(relayYamlPath, js_yaml_1.default.dump(yamlData, { lineWidth: 120 }), 'utf-8');
415
+ if (!json) {
416
+ console.error(` → relay.yaml에 version: ${newVersion} 저장됨\n`);
417
+ }
418
+ }
409
419
  else if (isTTY) {
410
420
  const { select: promptVersion } = await import('@inquirer/prompts');
411
421
  const [major, minor, patch] = config.version.split('.').map(Number);
@@ -462,26 +472,31 @@ function registerPublish(program) {
462
472
  try {
463
473
  const { fetchMyOrgs } = await import('./orgs.js');
464
474
  const orgs = await fetchMyOrgs(token);
465
- // --space flag (legacy alias for --org): resolve Org by slug
466
- if (opts.space) {
467
- const matched = orgs.find((o) => o.slug === opts.space);
475
+ // Determine explicit org slug: --org > --space (legacy) > relay.yaml org_slug
476
+ const explicitOrgSlug = opts.org ?? opts.space ?? config.org_slug;
477
+ // --org / --space / relay.yaml org_slug: resolve Org by slug
478
+ if (explicitOrgSlug) {
479
+ const matched = orgs.find((o) => o.slug === explicitOrgSlug);
468
480
  if (matched) {
469
481
  selectedOrgId = matched.id;
470
482
  selectedOrgSlug = matched.slug;
483
+ if (!json && (opts.org || config.org_slug)) {
484
+ console.error(`\x1b[2m Organization: ${matched.name} (${matched.slug})\x1b[0m\n`);
485
+ }
471
486
  }
472
487
  else {
473
488
  if (json) {
474
489
  console.error(JSON.stringify({
475
490
  error: 'INVALID_ORG',
476
- message: `Organization '${opts.space}'를 찾을 수 없습니다.`,
491
+ message: `Organization '${explicitOrgSlug}'를 찾을 수 없습니다.`,
477
492
  fix: `사용 가능한 Org: ${orgs.map((o) => o.slug).join(', ')}`,
478
493
  options: orgs.map((o) => ({ value: o.slug, label: `${o.name} (${o.slug})` })),
479
494
  }));
480
495
  }
481
496
  else {
482
- console.error(`Organization '${opts.space}'를 찾을 수 없습니다.`);
497
+ console.error(`Organization '${explicitOrgSlug}'를 찾을 수 없습니다.`);
483
498
  }
484
- (0, error_report_js_1.reportCliError)('publish', 'INVALID_ORG', `org:${opts.space}`);
499
+ (0, error_report_js_1.reportCliError)('publish', 'INVALID_ORG', `org:${explicitOrgSlug}`);
485
500
  process.exit(1);
486
501
  }
487
502
  }
@@ -537,26 +552,31 @@ function registerPublish(program) {
537
552
  // Visibility default
538
553
  const defaultVisibility = 'public';
539
554
  // Visibility validation: must be explicitly set
555
+ // internal은 org가 선택된 경우에만 옵션으로 표시
556
+ const hasOrg = !!selectedOrgId;
540
557
  if (!config.visibility) {
541
558
  if (isTTY) {
542
559
  const { select: promptSelect } = await import('@inquirer/prompts');
543
560
  console.error(`\n\x1b[33m⚠ relay.yaml에 visibility가 설정되지 않았습니다.\x1b[0m (기본값: ${defaultVisibility === 'public' ? '공개' : '비공개'})`);
561
+ const visChoices = [
562
+ {
563
+ name: `공개 — 누구나 설치${defaultVisibility === 'public' ? ' ✓ 추천' : ''}`,
564
+ value: 'public',
565
+ },
566
+ {
567
+ name: '링크 공유 — 접근 링크가 있는 사람만 설치',
568
+ value: 'private',
569
+ },
570
+ ];
571
+ if (hasOrg) {
572
+ visChoices.push({
573
+ name: '비공개 — Org 멤버만 접근',
574
+ value: 'internal',
575
+ });
576
+ }
544
577
  config.visibility = await promptSelect({
545
578
  message: '공개 범위를 선택하세요:',
546
- choices: [
547
- {
548
- name: `공개 — 누구나 설치${defaultVisibility === 'public' ? ' ✓ 추천' : ''}`,
549
- value: 'public',
550
- },
551
- {
552
- name: '링크 공유 — 접근 링크가 있는 사람만 설치',
553
- value: 'private',
554
- },
555
- {
556
- name: `비공개 — Org 멤버만 접근`,
557
- value: 'internal',
558
- },
559
- ],
579
+ choices: visChoices,
560
580
  default: defaultVisibility,
561
581
  });
562
582
  // Save back to relay.yaml
@@ -567,21 +587,25 @@ function registerPublish(program) {
567
587
  }
568
588
  else {
569
589
  (0, error_report_js_1.reportCliError)('publish', 'MISSING_VISIBILITY', 'visibility not set in relay.yaml');
590
+ const visOptions = [
591
+ { value: 'public', label: '공개 — 누구나 설치' },
592
+ { value: 'private', label: '링크 공유 — 접근 링크가 있는 사람만 설치' },
593
+ ];
594
+ if (hasOrg) {
595
+ visOptions.push({ value: 'internal', label: '비공개 — Org 멤버만 접근' });
596
+ }
570
597
  console.error(JSON.stringify({
571
598
  error: 'MISSING_VISIBILITY',
572
599
  message: 'relay.yaml에 visibility를 설정해주세요.',
573
- options: [
574
- { value: 'public', label: '공개 — 누구나 설치' },
575
- { value: 'private', label: '링크 공유 — 접근 링크가 있는 사람만 설치' },
576
- { value: 'internal', label: '비공개 — Org 멤버만 접근' },
577
- ],
600
+ options: visOptions,
578
601
  fix: 'relay.yaml의 visibility 필드를 위 옵션 중 하나로 설정하세요.',
579
602
  }));
580
603
  process.exit(1);
581
604
  }
582
605
  }
583
606
  // Confirm visibility before publish (재배포 시 변경 기회 제공)
584
- if (isTTY) {
607
+ // Skip when a bump flag is present and visibility is already set in relay.yaml
608
+ if (isTTY && !hasBumpFlag) {
585
609
  const { select: promptConfirmVis } = await import('@inquirer/prompts');
586
610
  const visLabelMap = {
587
611
  public: '공개',
@@ -589,22 +613,25 @@ function registerPublish(program) {
589
613
  internal: '비공개',
590
614
  };
591
615
  const currentVisLabel = visLabelMap[config.visibility ?? 'public'] ?? config.visibility;
616
+ const confirmVisChoices = [
617
+ {
618
+ name: `공개 — 누구나 설치${defaultVisibility === 'public' ? ' ✓ 추천' : ''}`,
619
+ value: 'public',
620
+ },
621
+ {
622
+ name: '링크공유 — 접근 링크가 있는 사람만 설치',
623
+ value: 'private',
624
+ },
625
+ ];
626
+ if (hasOrg) {
627
+ confirmVisChoices.push({
628
+ name: '비공개 — Org 멤버만 접근',
629
+ value: 'internal',
630
+ });
631
+ }
592
632
  const newVisibility = await promptConfirmVis({
593
633
  message: `공개 범위: ${currentVisLabel} — 유지하거나 변경하세요`,
594
- choices: [
595
- {
596
- name: `공개 — 누구나 설치${defaultVisibility === 'public' ? ' ✓ 추천' : ''}`,
597
- value: 'public',
598
- },
599
- {
600
- name: '링크공유 — 접근 링크가 있는 사람만 설치',
601
- value: 'private',
602
- },
603
- {
604
- name: `비공개 — Org 멤버만 접근`,
605
- value: 'internal',
606
- },
607
- ],
634
+ choices: confirmVisChoices,
608
635
  default: config.visibility ?? defaultVisibility,
609
636
  });
610
637
  if (newVisibility !== config.visibility) {
@@ -701,22 +728,24 @@ function registerPublish(program) {
701
728
  if (isTTY) {
702
729
  const detailSlug = result.slug.startsWith('@') ? result.slug.slice(1) : result.slug;
703
730
  const accessCode = result.access_code;
704
- const guideUrl = accessCode
705
- ? `https://relayax.com/api/registry/${detailSlug}/guide.md?code=${accessCode}`
706
- : `https://relayax.com/api/registry/${detailSlug}/guide.md`;
707
- console.log(`\n \x1b[90m주변인에게 공유하세요:\x1b[0m\n`);
708
- console.log('```');
709
- console.log(index_js_1.GUIDE_INSTRUCTION);
710
- console.log(guideUrl);
711
- console.log('```');
712
- const installCmd = accessCode
713
- ? `/relay-install ${result.slug} --code ${accessCode}`
714
- : `/relay-install ${result.slug}`;
715
- console.log(`\n \x1b[90mrelay CLI 사용자용:\x1b[0m\n`);
716
- console.log('```');
717
- console.log(installCmd);
718
- console.log('```');
719
- console.log(`\n \x1b[90m상세페이지: \x1b[36mrelayax.com/@${detailSlug}\x1b[0m`);
731
+ // Primary: npx 설치 명령어 한 줄
732
+ const visibility = config.visibility ?? 'public';
733
+ let installCmd;
734
+ if (visibility === 'internal' && accessCode) {
735
+ installCmd = `npx relayax-cli install ${result.slug} --join-code ${accessCode}`;
736
+ }
737
+ else if (visibility === 'private' && accessCode) {
738
+ installCmd = `npx relayax-cli install ${result.slug} --code ${accessCode}`;
739
+ }
740
+ else {
741
+ installCmd = `npx relayax-cli install ${result.slug}`;
742
+ }
743
+ console.log(`\n \x1b[90m공유하세요:\x1b[0m`);
744
+ console.log(` ┌${'─'.repeat(installCmd.length + 2)}┐`);
745
+ console.log(` │ ${installCmd} │`);
746
+ console.log(` └${'─'.repeat(installCmd.length + 2)}┘`);
747
+ // Secondary: 에이전트 소개 페이지
748
+ console.log(`\n \x1b[90m에이전트 소개: \x1b[36mhttps://relayax.com/@${detailSlug}\x1b[0m`);
720
749
  }
721
750
  }
722
751
  }
@@ -67,11 +67,19 @@ function registerUninstall(program) {
67
67
  if (localEntry) {
68
68
  const removed = (0, installer_js_1.uninstallAgent)(localEntry.files);
69
69
  totalRemoved += removed.length;
70
- // Remove deployed files
70
+ // Remove deployed symlinks (new)
71
+ if (localEntry.deployed_symlinks && localEntry.deployed_symlinks.length > 0) {
72
+ const symlinkRemoved = (0, installer_js_1.removeSymlinks)(localEntry.deployed_symlinks);
73
+ totalRemoved += symlinkRemoved.length;
74
+ const boundary = inferBoundary(localEntry.deployed_symlinks, (0, paths_js_1.resolveProjectPath)(_opts.project));
75
+ for (const f of symlinkRemoved) {
76
+ (0, installer_js_1.cleanEmptyParents)(f, boundary);
77
+ }
78
+ }
79
+ // Remove deployed files (legacy)
71
80
  if (localEntry.deployed_files && localEntry.deployed_files.length > 0) {
72
81
  const deployedRemoved = (0, installer_js_1.uninstallAgent)(localEntry.deployed_files);
73
82
  totalRemoved += deployedRemoved.length;
74
- // Clean empty parent directories
75
83
  const boundary = inferBoundary(localEntry.deployed_files, (0, paths_js_1.resolveProjectPath)(_opts.project));
76
84
  for (const f of deployedRemoved) {
77
85
  (0, installer_js_1.cleanEmptyParents)(f, boundary);
@@ -87,11 +95,19 @@ function registerUninstall(program) {
87
95
  const removed = (0, installer_js_1.uninstallAgent)(globalEntry.files);
88
96
  totalRemoved += removed.length;
89
97
  }
90
- // Remove globally deployed files
98
+ // Remove deployed symlinks (new)
99
+ if (globalEntry.deployed_symlinks && globalEntry.deployed_symlinks.length > 0) {
100
+ const symlinkRemoved = (0, installer_js_1.removeSymlinks)(globalEntry.deployed_symlinks);
101
+ totalRemoved += symlinkRemoved.length;
102
+ const boundary = inferBoundary(globalEntry.deployed_symlinks, os_1.default.homedir());
103
+ for (const f of symlinkRemoved) {
104
+ (0, installer_js_1.cleanEmptyParents)(f, boundary);
105
+ }
106
+ }
107
+ // Remove globally deployed files (legacy)
91
108
  if (globalEntry.deployed_files && globalEntry.deployed_files.length > 0) {
92
109
  const deployedRemoved = (0, installer_js_1.uninstallAgent)(globalEntry.deployed_files);
93
110
  totalRemoved += deployedRemoved.length;
94
- // Clean empty parent directories
95
111
  const boundary = inferBoundary(globalEntry.deployed_files, os_1.default.homedir());
96
112
  for (const f of deployedRemoved) {
97
113
  (0, installer_js_1.cleanEmptyParents)(f, boundary);
@@ -1,12 +1,19 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.registerUpdate = registerUpdate;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
4
10
  const api_js_1 = require("../lib/api.js");
5
11
  const storage_js_1 = require("../lib/storage.js");
6
12
  const installer_js_1 = require("../lib/installer.js");
7
13
  const config_js_1 = require("../lib/config.js");
8
14
  const slug_js_1 = require("../lib/slug.js");
9
15
  const preamble_js_1 = require("../lib/preamble.js");
16
+ const paths_js_1 = require("../lib/paths.js");
10
17
  function registerUpdate(program) {
11
18
  program
12
19
  .command('update <slug>')
@@ -15,11 +22,12 @@ function registerUpdate(program) {
15
22
  .option('--code <code>', '초대 코드 (비공개 에이전트 업데이트 시 필요)')
16
23
  .action(async (slugInput, opts) => {
17
24
  const json = program.opts().json ?? false;
18
- const installPath = (0, config_js_1.getInstallPath)(opts.path);
19
25
  const tempDir = (0, storage_js_1.makeTempDir)();
26
+ const projectPath = (0, paths_js_1.resolveProjectPath)(opts.path);
20
27
  try {
21
- // Resolve scoped slug (try installed.json first for offline, then server)
22
- const installed = (0, config_js_1.loadInstalled)();
28
+ // Resolve scoped slug
29
+ const localInstalled = (0, config_js_1.loadInstalled)();
30
+ const globalInstalled = (0, config_js_1.loadGlobalInstalled)();
23
31
  let slug;
24
32
  if ((0, slug_js_1.isScopedSlug)(slugInput)) {
25
33
  slug = slugInput;
@@ -28,9 +36,11 @@ function registerUpdate(program) {
28
36
  const parsed = await (0, slug_js_1.resolveSlug)(slugInput);
29
37
  slug = parsed.full;
30
38
  }
31
- // Check installed.json for current version
32
- const currentEntry = installed[slug];
39
+ // Find current entry (check both registries)
40
+ const currentEntry = localInstalled[slug] ?? globalInstalled[slug];
33
41
  const currentVersion = currentEntry?.version ?? null;
42
+ const currentScope = globalInstalled[slug] ? 'global'
43
+ : currentEntry?.deploy_scope ?? 'global';
34
44
  // Fetch latest agent metadata
35
45
  const agent = await (0, api_js_1.fetchAgentInfo)(slug);
36
46
  const latestVersion = agent.version;
@@ -52,39 +62,62 @@ function registerUpdate(program) {
52
62
  process.exit(1);
53
63
  }
54
64
  }
55
- // Download package
65
+ // Clean up old symlinks (new) and deployed_files (legacy migration)
66
+ if (currentEntry?.deployed_symlinks && currentEntry.deployed_symlinks.length > 0) {
67
+ (0, installer_js_1.removeSymlinks)(currentEntry.deployed_symlinks);
68
+ }
69
+ if (currentEntry?.deployed_files && currentEntry.deployed_files.length > 0) {
70
+ (0, installer_js_1.uninstallAgent)(currentEntry.deployed_files);
71
+ }
72
+ // Determine agent directory
73
+ const parsedSlug = (0, slug_js_1.parseSlug)(slug);
74
+ const owner = parsedSlug?.owner ?? 'unknown';
75
+ const name = parsedSlug?.name ?? slug;
76
+ const agentDir = currentScope === 'global'
77
+ ? path_1.default.join(os_1.default.homedir(), '.relay', 'agents', owner, name)
78
+ : path_1.default.join(projectPath, '.relay', 'agents', owner, name);
79
+ // Download & extract
56
80
  const tarPath = await (0, storage_js_1.downloadPackage)(agent.package_url, tempDir);
57
- // Extract
58
- const extractDir = `${tempDir}/extracted`;
59
- await (0, storage_js_1.extractPackage)(tarPath, extractDir);
60
- // Inject preamble (update check) before copying
61
- (0, preamble_js_1.injectPreambleToAgent)(extractDir, slug);
62
- // Copy files to install_path
63
- const files = (0, installer_js_1.installAgent)(extractDir, installPath);
64
- // Preserve deploy info but clear deployed_files (agent needs to re-deploy)
65
- const previousDeployScope = currentEntry?.deploy_scope;
66
- const hadDeployedFiles = (currentEntry?.deployed_files?.length ?? 0) > 0;
67
- // Update installed.json with new version
68
- installed[slug] = {
81
+ if (fs_1.default.existsSync(agentDir)) {
82
+ fs_1.default.rmSync(agentDir, { recursive: true, force: true });
83
+ }
84
+ fs_1.default.mkdirSync(agentDir, { recursive: true });
85
+ await (0, storage_js_1.extractPackage)(tarPath, agentDir);
86
+ // Inject preamble
87
+ (0, preamble_js_1.injectPreambleToAgent)(agentDir, slug);
88
+ // Deploy symlinks (always handles migration from legacy deployed_files)
89
+ const deploy = (0, installer_js_1.deploySymlinks)(agentDir, currentScope, projectPath);
90
+ // Update installed.json
91
+ const installRecord = {
69
92
  agent_id: agent.id,
70
93
  version: latestVersion,
71
94
  installed_at: new Date().toISOString(),
72
- files,
73
- // Keep deploy_scope so agent knows where to re-deploy
74
- ...(previousDeployScope ? { deploy_scope: previousDeployScope } : {}),
75
- // Clear deployed_files — agent must re-deploy and call deploy-record
95
+ files: [agentDir],
96
+ deploy_scope: currentScope,
97
+ deployed_symlinks: deploy.symlinks,
76
98
  };
77
- (0, config_js_1.saveInstalled)(installed);
78
- // Report install (non-blocking, agent_id 기반)
99
+ if (currentScope === 'global') {
100
+ globalInstalled[slug] = installRecord;
101
+ (0, config_js_1.saveGlobalInstalled)(globalInstalled);
102
+ // Clean up local entry if migrating
103
+ if (localInstalled[slug]) {
104
+ delete localInstalled[slug];
105
+ (0, config_js_1.saveInstalled)(localInstalled);
106
+ }
107
+ }
108
+ else {
109
+ localInstalled[slug] = installRecord;
110
+ (0, config_js_1.saveInstalled)(localInstalled);
111
+ }
112
+ // Report
79
113
  await (0, api_js_1.reportInstall)(agent.id, slug, latestVersion);
80
114
  const result = {
81
115
  status: 'updated',
82
116
  slug,
83
117
  from_version: currentVersion,
84
118
  version: latestVersion,
85
- files_installed: files.length,
86
- install_path: installPath,
87
- ...(hadDeployedFiles ? { needs_redeploy: true, previous_deploy_scope: previousDeployScope } : {}),
119
+ scope: currentScope,
120
+ symlinks: deploy.symlinks.length,
88
121
  };
89
122
  if (json) {
90
123
  console.log(JSON.stringify(result));
@@ -92,9 +125,9 @@ function registerUpdate(program) {
92
125
  else {
93
126
  const fromLabel = currentVersion ? `v${currentVersion} → ` : '';
94
127
  console.log(`\n\x1b[32m✓ ${agent.name} ${fromLabel}v${latestVersion} 업데이트 완료\x1b[0m`);
95
- console.log(` 설치 위치: \x1b[36m${installPath}\x1b[0m`);
96
- console.log(` 파일 수: ${files.length}개`);
97
- // Show changelog for this version
128
+ console.log(` 위치: \x1b[36m${agentDir}\x1b[0m`);
129
+ console.log(` symlink: ${deploy.symlinks.length}개`);
130
+ // Show changelog
98
131
  try {
99
132
  const versions = await (0, api_js_1.fetchAgentVersions)(slug);
100
133
  const thisVersion = versions.find((v) => v.version === latestVersion);
@@ -107,8 +140,11 @@ function registerUpdate(program) {
107
140
  }
108
141
  }
109
142
  catch {
110
- // Non-critical: skip changelog display
143
+ // Non-critical
111
144
  }
145
+ // Requires check
146
+ const requiresResults = (0, installer_js_1.checkRequires)(agentDir);
147
+ (0, installer_js_1.printRequiresCheck)(requiresResults);
112
148
  }
113
149
  }
114
150
  catch (err) {
@@ -90,14 +90,13 @@ function getGlobalCommandPathForTool(skillsDir, commandId) {
90
90
  function formatCommandFile(content) {
91
91
  return `---\ndescription: ${content.description}\n---\n\n${content.body}\n`;
92
92
  }
93
- // ─── 프롬프트 조각은 cli/src/prompts/*.md에서 관리 (SSOT) ───
94
- // REQUIREMENTS_CHECK, ERROR_HANDLING_GUIDE → import from '../prompts/index.js'
93
+ // ─── 프롬프트는 cli/src/prompts/*.md에서 관리 (SSOT) ───
95
94
  // ─── User Commands (글로벌 설치) ───
96
95
  exports.USER_COMMANDS = [
97
96
  {
98
- id: 'relay-install',
99
- description: 'relay에서 에이전트를 설치합니다',
100
- body: ENV_PREAMBLE + index_js_1.INSTALL_PROMPT,
97
+ id: 'relay-explore',
98
+ description: 'relay 마켓플레이스를 탐색하고 프로젝트에 맞는 에이전트를 찾습니다',
99
+ body: index_js_1.EXPLORE_PROMPT,
101
100
  },
102
101
  {
103
102
  id: 'relay-status',
@@ -162,7 +161,7 @@ ${index_js_1.ERROR_HANDLING_GUIDE}
162
161
  - \`--org <slug>\` 인자가 있으면: \`relay list --org <org-slug> --json\`으로 해당 Organization의 에이전트 목록도 보여줍니다.
163
162
 
164
163
  ### 4. 안내
165
- - 설치된 에이전트가 없으면 \`/relay-install\`로 에이전트를 탐색·설치해보라고 안내합니다.
164
+ - 설치된 에이전트가 없으면 \`/relay-explore\`로 에이전트를 탐색해보라고 안내합니다.
166
165
  - Org가 있으면 활용법을 안내합니다:
167
166
  - Org 에이전트 설치: \`relay install @<org-slug>/<agent>\`
168
167
  - Org 관리: www.relayax.com/orgs/<slug>
@@ -210,9 +209,9 @@ ${index_js_1.ERROR_HANDLING_GUIDE}
210
209
  → "✓ @alice/doc-writer 삭제 완료 (12개 파일 제거)"`,
211
210
  },
212
211
  {
213
- id: 'relay-publish',
214
- description: '현재 에이전트 패키지를 relay에 배포합니다',
215
- body: ENV_PREAMBLE + index_js_1.PUBLISH_PROMPT,
212
+ id: 'relay-create',
213
+ description: '에이전트를 만들거나 업데이트하여 relay에 배포합니다',
214
+ body: index_js_1.CREATE_PROMPT,
216
215
  },
217
216
  ];
218
217
  // ─── Builder Commands (로컬 설치) ───
@@ -8,6 +8,5 @@ export declare function generateGuide(config: {
8
8
  name: string;
9
9
  description: string;
10
10
  version: string;
11
- visibility?: string;
12
11
  }, commands: CommandEntry[], requires?: Requires): string;
13
12
  export {};