anpm-io 1.0.0

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.
Files changed (139) hide show
  1. package/README.md +174 -0
  2. package/dist/commands/access.d.ts +2 -0
  3. package/dist/commands/access.js +90 -0
  4. package/dist/commands/adopt.d.ts +2 -0
  5. package/dist/commands/adopt.js +177 -0
  6. package/dist/commands/changelog.d.ts +2 -0
  7. package/dist/commands/changelog.js +67 -0
  8. package/dist/commands/check-update.d.ts +2 -0
  9. package/dist/commands/check-update.js +76 -0
  10. package/dist/commands/config.d.ts +2 -0
  11. package/dist/commands/config.js +84 -0
  12. package/dist/commands/create.d.ts +2 -0
  13. package/dist/commands/create.js +227 -0
  14. package/dist/commands/deploy-record.d.ts +2 -0
  15. package/dist/commands/deploy-record.js +93 -0
  16. package/dist/commands/deploy.d.ts +2 -0
  17. package/dist/commands/deploy.js +284 -0
  18. package/dist/commands/diff.d.ts +2 -0
  19. package/dist/commands/diff.js +92 -0
  20. package/dist/commands/feedback.d.ts +2 -0
  21. package/dist/commands/feedback.js +71 -0
  22. package/dist/commands/grant.d.ts +33 -0
  23. package/dist/commands/grant.js +190 -0
  24. package/dist/commands/hub.d.ts +2 -0
  25. package/dist/commands/hub.js +171 -0
  26. package/dist/commands/init.d.ts +13 -0
  27. package/dist/commands/init.js +172 -0
  28. package/dist/commands/install.d.ts +2 -0
  29. package/dist/commands/install.js +626 -0
  30. package/dist/commands/join.d.ts +6 -0
  31. package/dist/commands/join.js +90 -0
  32. package/dist/commands/link.d.ts +2 -0
  33. package/dist/commands/link.js +112 -0
  34. package/dist/commands/list.d.ts +2 -0
  35. package/dist/commands/list.js +144 -0
  36. package/dist/commands/login.d.ts +7 -0
  37. package/dist/commands/login.js +235 -0
  38. package/dist/commands/orgs.d.ts +10 -0
  39. package/dist/commands/orgs.js +128 -0
  40. package/dist/commands/outdated.d.ts +2 -0
  41. package/dist/commands/outdated.js +70 -0
  42. package/dist/commands/package.d.ts +57 -0
  43. package/dist/commands/package.js +569 -0
  44. package/dist/commands/ping.d.ts +2 -0
  45. package/dist/commands/ping.js +40 -0
  46. package/dist/commands/publish.d.ts +98 -0
  47. package/dist/commands/publish.js +899 -0
  48. package/dist/commands/run.d.ts +2 -0
  49. package/dist/commands/run.js +249 -0
  50. package/dist/commands/search.d.ts +2 -0
  51. package/dist/commands/search.js +57 -0
  52. package/dist/commands/status.d.ts +2 -0
  53. package/dist/commands/status.js +159 -0
  54. package/dist/commands/uninstall.d.ts +2 -0
  55. package/dist/commands/uninstall.js +132 -0
  56. package/dist/commands/update.d.ts +2 -0
  57. package/dist/commands/update.js +171 -0
  58. package/dist/commands/versions.d.ts +2 -0
  59. package/dist/commands/versions.js +44 -0
  60. package/dist/index.d.ts +2 -0
  61. package/dist/index.js +91 -0
  62. package/dist/lib/agent-status.d.ts +23 -0
  63. package/dist/lib/agent-status.js +127 -0
  64. package/dist/lib/ai-tools.d.ts +34 -0
  65. package/dist/lib/ai-tools.js +104 -0
  66. package/dist/lib/anpm-config.d.ts +39 -0
  67. package/dist/lib/anpm-config.js +112 -0
  68. package/dist/lib/api.d.ts +24 -0
  69. package/dist/lib/api.js +151 -0
  70. package/dist/lib/auto-detect.d.ts +30 -0
  71. package/dist/lib/auto-detect.js +112 -0
  72. package/dist/lib/cloud-providers/anthropic.d.ts +19 -0
  73. package/dist/lib/cloud-providers/anthropic.js +200 -0
  74. package/dist/lib/cloud-providers/package-mapper.d.ts +9 -0
  75. package/dist/lib/cloud-providers/package-mapper.js +34 -0
  76. package/dist/lib/cloud-providers/provider.d.ts +60 -0
  77. package/dist/lib/cloud-providers/provider.js +7 -0
  78. package/dist/lib/command-adapter.d.ts +41 -0
  79. package/dist/lib/command-adapter.js +188 -0
  80. package/dist/lib/config.d.ts +50 -0
  81. package/dist/lib/config.js +274 -0
  82. package/dist/lib/contact-format.d.ts +7 -0
  83. package/dist/lib/contact-format.js +23 -0
  84. package/dist/lib/device-hash.d.ts +1 -0
  85. package/dist/lib/device-hash.js +16 -0
  86. package/dist/lib/error-report.d.ts +5 -0
  87. package/dist/lib/error-report.js +28 -0
  88. package/dist/lib/git-installer.d.ts +16 -0
  89. package/dist/lib/git-installer.js +97 -0
  90. package/dist/lib/git-operations.d.ts +38 -0
  91. package/dist/lib/git-operations.js +183 -0
  92. package/dist/lib/hub-notify.d.ts +9 -0
  93. package/dist/lib/hub-notify.js +66 -0
  94. package/dist/lib/install-source.d.ts +33 -0
  95. package/dist/lib/install-source.js +98 -0
  96. package/dist/lib/installer.d.ts +40 -0
  97. package/dist/lib/installer.js +358 -0
  98. package/dist/lib/local-installer.d.ts +15 -0
  99. package/dist/lib/local-installer.js +73 -0
  100. package/dist/lib/lockfile.d.ts +13 -0
  101. package/dist/lib/lockfile.js +42 -0
  102. package/dist/lib/manifest.d.ts +65 -0
  103. package/dist/lib/manifest.js +113 -0
  104. package/dist/lib/migration.d.ts +10 -0
  105. package/dist/lib/migration.js +91 -0
  106. package/dist/lib/paths.d.ts +10 -0
  107. package/dist/lib/paths.js +22 -0
  108. package/dist/lib/preamble.d.ts +22 -0
  109. package/dist/lib/preamble.js +133 -0
  110. package/dist/lib/relay-config.d.ts +13 -0
  111. package/dist/lib/relay-config.js +46 -0
  112. package/dist/lib/requires-suggest.d.ts +23 -0
  113. package/dist/lib/requires-suggest.js +295 -0
  114. package/dist/lib/setup-command.d.ts +6 -0
  115. package/dist/lib/setup-command.js +72 -0
  116. package/dist/lib/slug.d.ts +24 -0
  117. package/dist/lib/slug.js +100 -0
  118. package/dist/lib/step-tracker.d.ts +8 -0
  119. package/dist/lib/step-tracker.js +28 -0
  120. package/dist/lib/storage.d.ts +6 -0
  121. package/dist/lib/storage.js +23 -0
  122. package/dist/lib/update-cache.d.ts +2 -0
  123. package/dist/lib/update-cache.js +51 -0
  124. package/dist/lib/version-check.d.ts +10 -0
  125. package/dist/lib/version-check.js +75 -0
  126. package/dist/mcp/server.d.ts +3 -0
  127. package/dist/mcp/server.js +112 -0
  128. package/dist/postinstall.d.ts +8 -0
  129. package/dist/postinstall.js +41 -0
  130. package/dist/prompts/_error-handling.md +38 -0
  131. package/dist/prompts/create.md +170 -0
  132. package/dist/prompts/explore.md +30 -0
  133. package/dist/prompts/index.d.ts +3 -0
  134. package/dist/prompts/index.js +22 -0
  135. package/dist/relay-compat.d.ts +2 -0
  136. package/dist/relay-compat.js +7 -0
  137. package/dist/types.d.ts +118 -0
  138. package/dist/types.js +2 -0
  139. package/package.json +51 -0
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerRun(program: Command): void;
@@ -0,0 +1,249 @@
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.registerRun = registerRun;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const child_process_1 = require("child_process");
11
+ const paths_js_1 = require("../lib/paths.js");
12
+ const manifest_js_1 = require("../lib/manifest.js");
13
+ const config_js_1 = require("../lib/config.js");
14
+ const HARNESS_MAP = {
15
+ claude: { name: 'Claude Code', command: 'claude', skillsDir: '.claude', type: 'cli' },
16
+ codex: { name: 'Codex', command: 'codex', skillsDir: '.codex', type: 'cli' },
17
+ gemini: { name: 'Gemini CLI', command: 'gemini', skillsDir: '.gemini', type: 'cli' },
18
+ cursor: { name: 'Cursor', command: 'cursor', skillsDir: '.cursor', type: 'gui' },
19
+ windsurf: { name: 'Windsurf', command: 'windsurf', skillsDir: '.windsurf', type: 'gui' },
20
+ cline: { name: 'Cline', command: 'cline', skillsDir: '.cline', type: 'cli' },
21
+ roocode: { name: 'RooCode', command: 'roocode', skillsDir: '.roo', type: 'cli' },
22
+ };
23
+ // Dotfiles to inherit from real HOME
24
+ const INHERITED_DOTFILES = [
25
+ '.ssh', '.gitconfig', '.git-credentials', '.npmrc', '.config',
26
+ '.zshrc', '.bashrc', '.profile', '.env',
27
+ ];
28
+ function registerRun(program) {
29
+ program
30
+ .command('run <harness>')
31
+ .description('Run a harness with isolated agent environment')
32
+ .option('--with <agents>', 'Comma-separated agent slugs to activate (overrides relay.yaml)')
33
+ .option('--profile <name>', 'Use a named profile from ~/.relay/profiles/')
34
+ .option('--project <dir>', 'Project root path')
35
+ .allowUnknownOption(true)
36
+ .action(async (harness, opts, cmd) => {
37
+ const json = program.opts().json ?? false;
38
+ const projectPath = (0, paths_js_1.resolveProjectPath)(opts.project);
39
+ // 1. Resolve harness
40
+ const mapping = HARNESS_MAP[harness.toLowerCase()];
41
+ if (!mapping) {
42
+ const available = Object.keys(HARNESS_MAP).join(', ');
43
+ if (json) {
44
+ console.error(JSON.stringify({ error: 'UNKNOWN_HARNESS', message: `Harness '${harness}' not found. Available: ${available}` }));
45
+ }
46
+ else {
47
+ console.error(`\x1b[31m✖ Harness '${harness}' not found.\x1b[0m\n Available: ${available}`);
48
+ }
49
+ process.exit(1);
50
+ }
51
+ // 2. Determine which agents to activate
52
+ let agentSlugs;
53
+ if (opts.with) {
54
+ agentSlugs = opts.with.split(',').map((s) => s.trim());
55
+ }
56
+ else if (opts.profile) {
57
+ agentSlugs = loadProfile(opts.profile);
58
+ }
59
+ else {
60
+ const { manifest } = (0, manifest_js_1.loadManifest)(projectPath);
61
+ if (!manifest?.agents || Object.keys(manifest.agents).length === 0) {
62
+ if (json) {
63
+ console.error(JSON.stringify({ error: 'NO_AGENTS', message: 'No agents specified. Use --with, --profile, or add agents to relay.yaml.' }));
64
+ }
65
+ else {
66
+ console.error('\x1b[31m✖ No agents specified.\x1b[0m');
67
+ console.error(' Use --with <agents>, --profile <name>, or add agents to relay.yaml.');
68
+ }
69
+ process.exit(1);
70
+ }
71
+ agentSlugs = Object.keys(manifest.agents);
72
+ }
73
+ // 3. Resolve agent directories from installed.json
74
+ const { global: globalInstalled, local: localInstalled } = (0, config_js_1.loadMergedInstalled)();
75
+ const allInstalled = { ...globalInstalled, ...localInstalled };
76
+ const agentDirs = [];
77
+ for (const slug of agentSlugs) {
78
+ const entry = allInstalled[slug];
79
+ if (entry?.files?.[0]) {
80
+ agentDirs.push({ slug, dir: entry.files[0] });
81
+ }
82
+ else {
83
+ if (!json)
84
+ console.error(`\x1b[33m⚠ Agent ${slug} not installed, skipping\x1b[0m`);
85
+ }
86
+ }
87
+ if (agentDirs.length === 0) {
88
+ if (json)
89
+ console.error(JSON.stringify({ error: 'NO_AGENTS_INSTALLED', message: 'None of the specified agents are installed.' }));
90
+ else
91
+ console.error('\x1b[31m✖ None of the specified agents are installed.\x1b[0m');
92
+ process.exit(1);
93
+ }
94
+ // 4. Extract passthrough args (everything after --)
95
+ const passthroughArgs = cmd.args.filter((a) => a !== harness);
96
+ // 5. Launch based on harness type
97
+ if (mapping.type === 'cli') {
98
+ await launchCliHarness(mapping, agentDirs, projectPath, passthroughArgs, json);
99
+ }
100
+ else {
101
+ await launchGuiHarness(mapping, agentDirs, projectPath, passthroughArgs, json);
102
+ }
103
+ });
104
+ }
105
+ async function launchCliHarness(mapping, agentDirs, projectPath, passthroughArgs, json) {
106
+ const realHome = os_1.default.homedir();
107
+ const tempHome = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'relay-env-'));
108
+ try {
109
+ // Inherit dotfiles from real HOME
110
+ for (const dotfile of INHERITED_DOTFILES) {
111
+ const src = path_1.default.join(realHome, dotfile);
112
+ if (fs_1.default.existsSync(src)) {
113
+ const dest = path_1.default.join(tempHome, dotfile);
114
+ try {
115
+ fs_1.default.symlinkSync(src, dest);
116
+ }
117
+ catch { /* skip if fails */ }
118
+ }
119
+ }
120
+ // Inherit .relay directory (for token, config, etc.)
121
+ const relaySrc = path_1.default.join(realHome, '.relay');
122
+ if (fs_1.default.existsSync(relaySrc)) {
123
+ fs_1.default.symlinkSync(relaySrc, path_1.default.join(tempHome, '.relay'));
124
+ }
125
+ // Inherit harness settings (credentials, settings.json, etc.)
126
+ const harnessDir = path_1.default.join(realHome, mapping.skillsDir);
127
+ const tempHarnessDir = path_1.default.join(tempHome, mapping.skillsDir);
128
+ fs_1.default.mkdirSync(tempHarnessDir, { recursive: true });
129
+ // Symlink settings files (not skills/)
130
+ if (fs_1.default.existsSync(harnessDir)) {
131
+ for (const entry of fs_1.default.readdirSync(harnessDir, { withFileTypes: true })) {
132
+ if (['skills', 'commands', 'rules', 'agents'].includes(entry.name))
133
+ continue;
134
+ try {
135
+ fs_1.default.symlinkSync(path_1.default.join(harnessDir, entry.name), path_1.default.join(tempHarnessDir, entry.name));
136
+ }
137
+ catch { /* skip */ }
138
+ }
139
+ }
140
+ // Create skills/commands/rules/agents directories with only specified agents
141
+ const contentDirs = ['skills', 'commands', 'rules', 'agents'];
142
+ for (const dir of contentDirs) {
143
+ const targetDir = path_1.default.join(tempHarnessDir, dir);
144
+ fs_1.default.mkdirSync(targetDir, { recursive: true });
145
+ for (const { dir: agentDir } of agentDirs) {
146
+ const srcDir = path_1.default.join(agentDir, dir);
147
+ if (!fs_1.default.existsSync(srcDir))
148
+ continue;
149
+ for (const entry of fs_1.default.readdirSync(srcDir, { withFileTypes: true })) {
150
+ if (entry.name.startsWith('.'))
151
+ continue;
152
+ const srcPath = path_1.default.join(srcDir, entry.name);
153
+ const destPath = path_1.default.join(targetDir, entry.name);
154
+ try {
155
+ fs_1.default.symlinkSync(srcPath, destPath);
156
+ }
157
+ catch { /* skip duplicates */ }
158
+ }
159
+ }
160
+ }
161
+ if (!json) {
162
+ console.error(`\x1b[2manpm run: launching ${mapping.name} with ${agentDirs.length} agent(s)\x1b[0m`);
163
+ for (const { slug } of agentDirs) {
164
+ console.error(` \x1b[36m${slug}\x1b[0m`);
165
+ }
166
+ console.error('');
167
+ }
168
+ // Launch harness with HOME override
169
+ const child = (0, child_process_1.spawn)(mapping.command, passthroughArgs, {
170
+ cwd: projectPath,
171
+ env: { ...process.env, HOME: tempHome },
172
+ stdio: 'inherit',
173
+ });
174
+ await new Promise((resolve, reject) => {
175
+ child.on('close', (code) => {
176
+ if (code && code !== 0)
177
+ reject(new Error(`${mapping.command} exited with code ${code}`));
178
+ else
179
+ resolve();
180
+ });
181
+ child.on('error', (err) => {
182
+ reject(new Error(`Failed to launch ${mapping.command}: ${err.message}`));
183
+ });
184
+ });
185
+ }
186
+ finally {
187
+ // Cleanup temp directory
188
+ try {
189
+ fs_1.default.rmSync(tempHome, { recursive: true, force: true });
190
+ }
191
+ catch { /* best effort */ }
192
+ }
193
+ }
194
+ async function launchGuiHarness(mapping, agentDirs, projectPath, _passthroughArgs, json) {
195
+ // GUI harnesses: manipulate project-local harness directory
196
+ const localHarnessDir = path_1.default.join(projectPath, mapping.skillsDir);
197
+ if (!json) {
198
+ console.error(`\x1b[33m⚠ GUI harness (${mapping.name}): full isolation not supported.\x1b[0m`);
199
+ console.error(` Setting up project-local ${mapping.skillsDir}/ with specified agents.`);
200
+ console.error(` Global agents may still be visible.\n`);
201
+ }
202
+ const contentDirs = ['skills', 'commands', 'rules', 'agents'];
203
+ const created = [];
204
+ for (const dir of contentDirs) {
205
+ const targetDir = path_1.default.join(localHarnessDir, dir);
206
+ fs_1.default.mkdirSync(targetDir, { recursive: true });
207
+ for (const { dir: agentDir } of agentDirs) {
208
+ const srcDir = path_1.default.join(agentDir, dir);
209
+ if (!fs_1.default.existsSync(srcDir))
210
+ continue;
211
+ for (const entry of fs_1.default.readdirSync(srcDir, { withFileTypes: true })) {
212
+ if (entry.name.startsWith('.'))
213
+ continue;
214
+ const srcPath = path_1.default.join(srcDir, entry.name);
215
+ const destPath = path_1.default.join(targetDir, entry.name);
216
+ if (fs_1.default.existsSync(destPath))
217
+ continue;
218
+ try {
219
+ const rel = path_1.default.relative(path_1.default.dirname(destPath), srcPath);
220
+ fs_1.default.symlinkSync(rel, destPath);
221
+ created.push(destPath);
222
+ }
223
+ catch { /* skip */ }
224
+ }
225
+ }
226
+ }
227
+ if (json) {
228
+ console.log(JSON.stringify({ status: 'ok', harness: mapping.name, symlinks: created.length, note: 'GUI harness - partial isolation' }));
229
+ }
230
+ else {
231
+ console.log(`\x1b[32m✓\x1b[0m Set up ${created.length} symlinks in ${mapping.skillsDir}/`);
232
+ console.log(` Open ${mapping.name} to use the configured agents.`);
233
+ }
234
+ }
235
+ function loadProfile(name) {
236
+ const profilePath = path_1.default.join(os_1.default.homedir(), '.relay', 'profiles', `${name}.yaml`);
237
+ if (!fs_1.default.existsSync(profilePath)) {
238
+ throw new Error(`Profile not found: ${profilePath}`);
239
+ }
240
+ try {
241
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
242
+ const yaml = require('js-yaml');
243
+ const raw = yaml.load(fs_1.default.readFileSync(profilePath, 'utf-8'));
244
+ return raw?.agents ?? [];
245
+ }
246
+ catch {
247
+ throw new Error(`Failed to parse profile: ${profilePath}`);
248
+ }
249
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerSearch(program: Command): void;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerSearch = registerSearch;
4
+ const api_js_1 = require("../lib/api.js");
5
+ const step_tracker_js_1 = require("../lib/step-tracker.js");
6
+ function formatTable(results) {
7
+ if (results.length === 0)
8
+ return '검색 결과가 없습니다.';
9
+ const rows = results.map((r) => ({
10
+ slug: r.slug,
11
+ name: r.name,
12
+ description: r.description.length > 50
13
+ ? r.description.slice(0, 47) + '...'
14
+ : r.description,
15
+ installs: String(r.install_count),
16
+ commands: r.commands.map((c) => typeof c === 'string' ? c : c.name).join(', ') || '-',
17
+ }));
18
+ const cols = ['slug', 'name', 'description', 'installs', 'commands'];
19
+ const widths = cols.map((col) => Math.max(col.length, ...rows.map((r) => r[col].length)));
20
+ const header = cols
21
+ .map((col, i) => col.padEnd(widths[i]))
22
+ .join(' ');
23
+ const separator = widths.map((w) => '-'.repeat(w)).join(' ');
24
+ const lines = rows.map((row) => cols.map((col, i) => row[col].padEnd(widths[i])).join(' '));
25
+ return ['\x1b[1m' + header + '\x1b[0m', separator, ...lines].join('\n');
26
+ }
27
+ function registerSearch(program) {
28
+ program
29
+ .command('search <keyword>')
30
+ .description('Space에서 에이전트 검색 (공개 에이전트 + 내 Space 에이전트)')
31
+ .option('--tag <tag>', '태그로 필터링')
32
+ .option('--space <space>', '특정 Space 내에서 검색')
33
+ .action(async (keyword, opts) => {
34
+ const json = program.opts().json ?? false;
35
+ (0, step_tracker_js_1.trackCommand)('search', { slug: keyword });
36
+ try {
37
+ const results = await (0, api_js_1.searchAgents)(keyword, opts.tag);
38
+ if (json) {
39
+ console.log(JSON.stringify({ results }));
40
+ }
41
+ else {
42
+ const spaceSuffix = opts.space ? ` Space: \x1b[35m@${opts.space}\x1b[0m` : '';
43
+ console.log(`\n검색어: \x1b[36m${keyword}\x1b[0m${opts.tag ? ` 태그: \x1b[33m${opts.tag}\x1b[0m` : ''}${spaceSuffix}\n`);
44
+ console.log(formatTable(results));
45
+ console.log(`\n총 ${results.length}건`);
46
+ if (!opts.space && results.length === 0) {
47
+ console.log('\x1b[33m💡 내 Space에서 검색하려면: anpm search <keyword> --space <space-slug>\x1b[0m');
48
+ }
49
+ }
50
+ }
51
+ catch (err) {
52
+ const message = err instanceof Error ? err.message : String(err);
53
+ console.error(JSON.stringify({ error: 'SEARCH_FAILED', message, fix: '검색어를 변경하거나 잠시 후 재시도하세요.' }));
54
+ process.exit(1);
55
+ }
56
+ });
57
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerStatus(program: Command): void;
@@ -0,0 +1,159 @@
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.registerStatus = registerStatus;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const ai_tools_js_1 = require("../lib/ai-tools.js");
10
+ const paths_js_1 = require("../lib/paths.js");
11
+ const config_js_1 = require("../lib/config.js");
12
+ const command_adapter_js_1 = require("../lib/command-adapter.js");
13
+ const agent_status_js_1 = require("../lib/agent-status.js");
14
+ async function resolveUsername(token) {
15
+ try {
16
+ const res = await fetch(`${config_js_1.API_URL}/api/auth/me`, {
17
+ headers: { Authorization: `Bearer ${token}` },
18
+ });
19
+ if (!res.ok)
20
+ return undefined;
21
+ const body = await res.json();
22
+ return body.username;
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ function registerStatus(program) {
29
+ program
30
+ .command('status')
31
+ .description('현재 anpm 환경 상태를 표시합니다')
32
+ .option('--project <dir>', '프로젝트 루트 경로 (기본: cwd, 환경변수: RELAY_PROJECT_PATH)')
33
+ .action(async (opts) => {
34
+ const json = program.opts().json ?? false;
35
+ const projectPath = (0, paths_js_1.resolveProjectPath)(opts.project);
36
+ // 1. 로그인 상태
37
+ const token = await (0, config_js_1.getValidToken)();
38
+ let username;
39
+ if (token) {
40
+ username = await resolveUsername(token);
41
+ }
42
+ // 2. 에이전트 감지
43
+ const detected = (0, ai_tools_js_1.detectAgentCLIs)(projectPath);
44
+ const primaryAgent = detected.length > 0 ? detected[0] : null;
45
+ // 글로벌 커맨드 상태
46
+ const hasGlobal = command_adapter_js_1.USER_COMMANDS.every((cmd) => fs_1.default.existsSync((0, command_adapter_js_1.getGlobalCommandPath)(cmd.id)));
47
+ // 로컬 Builder 커맨드 상태
48
+ let hasLocal = false;
49
+ if (primaryAgent) {
50
+ const localDir = path_1.default.join(projectPath, primaryAgent.skillsDir, 'commands', 'relay');
51
+ hasLocal = command_adapter_js_1.BUILDER_COMMANDS.some((cmd) => fs_1.default.existsSync(path_1.default.join(localDir, `${cmd.id}.md`)));
52
+ }
53
+ // 3. 에이전트 프로젝트 정보
54
+ const relayYamlPath = path_1.default.join(projectPath, '.relay', 'relay.yaml');
55
+ let project = null;
56
+ if (fs_1.default.existsSync(relayYamlPath)) {
57
+ try {
58
+ const yaml = await import('js-yaml');
59
+ const content = fs_1.default.readFileSync(relayYamlPath, 'utf-8');
60
+ const raw = yaml.load(content);
61
+ project = {
62
+ is_agent: true,
63
+ name: String(raw.name ?? ''),
64
+ slug: String(raw.slug ?? ''),
65
+ version: String(raw.version ?? ''),
66
+ };
67
+ }
68
+ catch {
69
+ project = { is_agent: true };
70
+ }
71
+ }
72
+ else {
73
+ project = { is_agent: false };
74
+ }
75
+ // 4. Installed agents status
76
+ const agentEntries = (0, agent_status_js_1.getAgentStatusEntries)();
77
+ const unmanagedItems = (0, agent_status_js_1.findUnmanagedContent)(projectPath);
78
+ // 5. 출력
79
+ if (json) {
80
+ const result = {
81
+ login: { authenticated: !!token, username },
82
+ agent: {
83
+ detected: primaryAgent?.name ?? null,
84
+ global_commands: hasGlobal,
85
+ local_commands: hasLocal,
86
+ },
87
+ project,
88
+ installed_agents: agentEntries,
89
+ unmanaged: unmanagedItems,
90
+ };
91
+ console.log(JSON.stringify(result));
92
+ }
93
+ else {
94
+ console.log('');
95
+ // Login
96
+ if (token && username) {
97
+ console.log(` \x1b[32m✓\x1b[0m Login: \x1b[36m${username}\x1b[0m`);
98
+ }
99
+ else if (token) {
100
+ console.log(` \x1b[32m✓\x1b[0m Login: authenticated`);
101
+ }
102
+ else {
103
+ console.log(` \x1b[31m✗\x1b[0m Login: not authenticated (\x1b[33manpm login\x1b[0m)`);
104
+ }
105
+ // Harness detection
106
+ if (primaryAgent) {
107
+ const globalLabel = hasGlobal ? '\x1b[32mglobal ✓\x1b[0m' : '\x1b[31mglobal ✗\x1b[0m';
108
+ const localLabel = hasLocal ? '\x1b[32mlocal ✓\x1b[0m' : '\x1b[2mlocal —\x1b[0m';
109
+ console.log(` \x1b[32m✓\x1b[0m Harness: \x1b[36m${primaryAgent.name}\x1b[0m (${globalLabel} ${localLabel})`);
110
+ }
111
+ else {
112
+ console.log(` \x1b[31m✗\x1b[0m Harness: not detected`);
113
+ }
114
+ // Agent project
115
+ if (project?.is_agent && project.name) {
116
+ console.log(` \x1b[32m✓\x1b[0m Project: \x1b[36m${project.name}\x1b[0m v${project.version}`);
117
+ }
118
+ else {
119
+ console.log(` \x1b[2m—\x1b[0m Project: not an agent`);
120
+ }
121
+ // Installed agents table
122
+ if (agentEntries.length > 0) {
123
+ console.log(`\n \x1b[1mInstalled agents (${agentEntries.length}):\x1b[0m`);
124
+ for (const entry of agentEntries) {
125
+ const statusIcon = entry.status === 'active' ? '✅' : entry.status === 'broken' ? '⚠️' : '—';
126
+ const sourceLabel = entry.source.startsWith('registry') ? 'registry'
127
+ : entry.source.startsWith('local:') ? 'local'
128
+ : entry.source.startsWith('git:') ? 'git'
129
+ : entry.source.startsWith('link:') ? 'link'
130
+ : entry.source.startsWith('adopted:') ? 'adopted'
131
+ : entry.source;
132
+ const harnessNames = entry.harnesses.length > 0 ? entry.harnesses.join(', ') : '—';
133
+ console.log(` ${statusIcon} \x1b[36m${entry.slug}\x1b[0m \x1b[90m${sourceLabel}\x1b[0m v${entry.version} → ${harnessNames}`);
134
+ }
135
+ // Broken symlink warnings
136
+ const brokenEntries = agentEntries.filter((e) => e.brokenSymlinks.length > 0);
137
+ if (brokenEntries.length > 0) {
138
+ console.log('');
139
+ for (const entry of brokenEntries) {
140
+ console.log(` \x1b[33m⚠️ ${entry.slug}: ${entry.brokenSymlinks.length} broken symlink(s)\x1b[0m`);
141
+ console.log(` Run: anpm uninstall ${entry.slug}`);
142
+ }
143
+ }
144
+ }
145
+ // Unmanaged content
146
+ if (unmanagedItems.length > 0) {
147
+ console.log(`\n \x1b[1mUnmanaged content (${unmanagedItems.length}):\x1b[0m`);
148
+ for (const item of unmanagedItems.slice(0, 10)) {
149
+ console.log(` \x1b[33m⚠️\x1b[0m ${item.type}/${item.name} \x1b[90m(${item.harness})\x1b[0m`);
150
+ }
151
+ if (unmanagedItems.length > 10) {
152
+ console.log(` \x1b[90m...and ${unmanagedItems.length - 10} more\x1b[0m`);
153
+ }
154
+ console.log(`\n \x1b[90mTip: anpm adopt <path> to manage with anpm\x1b[0m`);
155
+ }
156
+ console.log('');
157
+ }
158
+ });
159
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerUninstall(program: Command): void;
@@ -0,0 +1,132 @@
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.registerUninstall = registerUninstall;
7
+ const os_1 = __importDefault(require("os"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const config_js_1 = require("../lib/config.js");
10
+ const installer_js_1 = require("../lib/installer.js");
11
+ const slug_js_1 = require("../lib/slug.js");
12
+ const ai_tools_js_1 = require("../lib/ai-tools.js");
13
+ const paths_js_1 = require("../lib/paths.js");
14
+ /**
15
+ * deployed_files에서 에이전트 설정 디렉토리(skillsDir) 기반 boundary를 추론한다.
16
+ * 예: deployed_files에 '~/.cursor/commands/relay/x.md'가 있으면 boundary는 basePath/.cursor
17
+ */
18
+ function inferBoundary(deployedFiles, basePath) {
19
+ const skillsDirs = ai_tools_js_1.AI_TOOLS.map((t) => t.skillsDir);
20
+ for (const f of deployedFiles) {
21
+ for (const sd of skillsDirs) {
22
+ const prefix = path_1.default.join(basePath, sd);
23
+ if (f.startsWith(prefix)) {
24
+ return prefix;
25
+ }
26
+ }
27
+ }
28
+ // fallback: 첫 번째 파일의 상위 디렉토리 중 basePath 직속 디렉토리
29
+ return path_1.default.join(basePath, '.claude');
30
+ }
31
+ function registerUninstall(program) {
32
+ program
33
+ .command('uninstall <slug>')
34
+ .description('에이전트 제거')
35
+ .option('--project <dir>', '프로젝트 루트 경로 (기본: cwd, 환경변수: RELAY_PROJECT_PATH)')
36
+ .action((slugInput, _opts) => {
37
+ const json = program.opts().json ?? false;
38
+ const localInstalled = (0, config_js_1.loadInstalled)();
39
+ const globalInstalled = (0, config_js_1.loadGlobalInstalled)();
40
+ // Resolve slug — support short names like "cardnews-agent"
41
+ let slug;
42
+ if ((0, slug_js_1.isScopedSlug)(slugInput)) {
43
+ slug = slugInput;
44
+ }
45
+ else {
46
+ const allKeys = [...Object.keys(localInstalled), ...Object.keys(globalInstalled)];
47
+ const match = allKeys.find((key) => {
48
+ const parsed = (0, slug_js_1.parseSlug)(key);
49
+ return parsed && parsed.name === slugInput;
50
+ });
51
+ slug = match ?? slugInput;
52
+ }
53
+ const localEntry = localInstalled[slug];
54
+ const globalEntry = globalInstalled[slug];
55
+ if (!localEntry && !globalEntry) {
56
+ const msg = { error: 'NOT_INSTALLED', message: `'${slugInput}'는 설치되어 있지 않습니다.` };
57
+ if (json) {
58
+ console.error(JSON.stringify(msg));
59
+ }
60
+ else {
61
+ console.error(`\x1b[31m오류:\x1b[0m ${msg.message}`);
62
+ }
63
+ process.exit(1);
64
+ }
65
+ let totalRemoved = 0;
66
+ // Remove from local registry
67
+ if (localEntry) {
68
+ const removed = (0, installer_js_1.uninstallAgent)(localEntry.files);
69
+ totalRemoved += removed.length;
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)
80
+ if (localEntry.deployed_files && localEntry.deployed_files.length > 0) {
81
+ const deployedRemoved = (0, installer_js_1.uninstallAgent)(localEntry.deployed_files);
82
+ totalRemoved += deployedRemoved.length;
83
+ const boundary = inferBoundary(localEntry.deployed_files, (0, paths_js_1.resolveProjectPath)(_opts.project));
84
+ for (const f of deployedRemoved) {
85
+ (0, installer_js_1.cleanEmptyParents)(f, boundary);
86
+ }
87
+ }
88
+ delete localInstalled[slug];
89
+ (0, config_js_1.saveInstalled)(localInstalled);
90
+ }
91
+ // Remove from global registry
92
+ if (globalEntry) {
93
+ // Only remove files if not already handled by local entry
94
+ if (!localEntry) {
95
+ const removed = (0, installer_js_1.uninstallAgent)(globalEntry.files);
96
+ totalRemoved += removed.length;
97
+ }
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)
108
+ if (globalEntry.deployed_files && globalEntry.deployed_files.length > 0) {
109
+ const deployedRemoved = (0, installer_js_1.uninstallAgent)(globalEntry.deployed_files);
110
+ totalRemoved += deployedRemoved.length;
111
+ const boundary = inferBoundary(globalEntry.deployed_files, os_1.default.homedir());
112
+ for (const f of deployedRemoved) {
113
+ (0, installer_js_1.cleanEmptyParents)(f, boundary);
114
+ }
115
+ }
116
+ delete globalInstalled[slug];
117
+ (0, config_js_1.saveGlobalInstalled)(globalInstalled);
118
+ }
119
+ const result = {
120
+ status: 'ok',
121
+ agent: slug,
122
+ files_removed: totalRemoved,
123
+ };
124
+ if (json) {
125
+ console.log(JSON.stringify(result));
126
+ }
127
+ else {
128
+ console.log(`\n\x1b[32m✓ ${slug} 제거 완료\x1b[0m`);
129
+ console.log(` 삭제된 파일: ${totalRemoved}개`);
130
+ }
131
+ });
132
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerUpdate(program: Command): void;