@orchagent/cli 0.2.26 → 0.3.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.
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.agentsMdAdapter = void 0;
4
+ exports.agentsMdAdapter = {
5
+ id: 'agents-md',
6
+ name: 'AGENTS.md (Universal)',
7
+ version: '1.0.0',
8
+ formatVersion: '1.0',
9
+ supportsAgentTypes: ['prompt', 'skill'],
10
+ installPaths: [
11
+ {
12
+ scope: 'project',
13
+ path: './',
14
+ description: 'Project root (AGENTS.md)',
15
+ },
16
+ ],
17
+ canConvert(agent) {
18
+ const warnings = [];
19
+ const errors = [];
20
+ if (agent.type === 'code') {
21
+ errors.push('Code agents cannot be converted to AGENTS.md');
22
+ return { canConvert: false, warnings, errors };
23
+ }
24
+ if (!agent.prompt) {
25
+ errors.push('Agent has no prompt content');
26
+ return { canConvert: false, warnings, errors };
27
+ }
28
+ warnings.push('AGENTS.md content should be appended to existing file, not replaced');
29
+ return { canConvert: true, warnings, errors };
30
+ },
31
+ convert(agent) {
32
+ const agentRef = `${agent.org_slug}/${agent.name}`;
33
+ const description = agent.description || '';
34
+ // Use orchagent markers for managed section
35
+ const content = `<!-- orchagent:${agentRef} -->
36
+ ## ${agent.name}
37
+
38
+ ${description}
39
+
40
+ ${agent.prompt || ''}
41
+ <!-- /orchagent:${agentRef} -->
42
+ `;
43
+ return [
44
+ {
45
+ filename: 'AGENTS.md',
46
+ content,
47
+ installPath: './',
48
+ },
49
+ ];
50
+ },
51
+ };
@@ -0,0 +1,104 @@
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.claudeCodeAdapter = void 0;
7
+ const yaml_1 = __importDefault(require("yaml"));
8
+ // Map Anthropic model names to Claude Code aliases
9
+ function mapModelToAlias(model) {
10
+ if (!model)
11
+ return 'inherit';
12
+ const lower = model.toLowerCase();
13
+ if (lower.includes('opus'))
14
+ return 'opus';
15
+ if (lower.includes('haiku'))
16
+ return 'haiku';
17
+ // Default to sonnet for any Claude model
18
+ if (lower.includes('claude') || lower.includes('sonnet'))
19
+ return 'sonnet';
20
+ return 'inherit';
21
+ }
22
+ // Convert agent name to valid Claude Code name (lowercase + hyphens)
23
+ function normalizeAgentName(name) {
24
+ return name
25
+ .toLowerCase()
26
+ .replace(/[^a-z0-9-]/g, '-')
27
+ .replace(/-+/g, '-')
28
+ .replace(/^-|-$/g, '');
29
+ }
30
+ exports.claudeCodeAdapter = {
31
+ id: 'claude-code',
32
+ name: 'Claude Code Sub-Agent',
33
+ version: '1.0.0',
34
+ formatVersion: '2026-01',
35
+ supportsAgentTypes: ['prompt', 'skill'],
36
+ installPaths: [
37
+ {
38
+ scope: 'user',
39
+ path: '~/.claude/agents/',
40
+ description: 'User-level (available in all projects)',
41
+ },
42
+ {
43
+ scope: 'project',
44
+ path: '.claude/agents/',
45
+ description: 'Project-level (current directory)',
46
+ },
47
+ ],
48
+ canConvert(agent) {
49
+ const warnings = [];
50
+ const errors = [];
51
+ // Code agents cannot be converted
52
+ if (agent.type === 'code') {
53
+ errors.push('Code agents cannot be converted to Claude Code sub-agents (they require execution)');
54
+ return { canConvert: false, warnings, errors };
55
+ }
56
+ // Check for prompt
57
+ if (!agent.prompt) {
58
+ errors.push('Agent has no prompt content');
59
+ return { canConvert: false, warnings, errors };
60
+ }
61
+ // Warnings for fields that can't be fully mapped
62
+ if (agent.input_schema) {
63
+ warnings.push('input_schema will be described in the prompt body, not enforced');
64
+ }
65
+ if (agent.output_schema) {
66
+ warnings.push('output_schema will be described in the prompt body, not enforced');
67
+ }
68
+ return { canConvert: true, warnings, errors };
69
+ },
70
+ convert(agent) {
71
+ const normalizedName = normalizeAgentName(agent.name);
72
+ // Build frontmatter
73
+ const frontmatter = {
74
+ name: normalizedName,
75
+ description: agent.description || `Delegatable agent: ${agent.name}`,
76
+ tools: 'Read, Glob, Grep', // Safe defaults - read-only
77
+ };
78
+ // Map model if specified
79
+ if (agent.default_models?.anthropic) {
80
+ const modelAlias = mapModelToAlias(agent.default_models.anthropic);
81
+ if (modelAlias !== 'inherit') {
82
+ frontmatter.model = modelAlias;
83
+ }
84
+ }
85
+ // Build body
86
+ let body = agent.prompt || '';
87
+ // Add schema descriptions if present
88
+ if (agent.input_schema) {
89
+ body += `\n\n## Input Schema\n\nThis agent expects input matching:\n\`\`\`json\n${JSON.stringify(agent.input_schema, null, 2)}\n\`\`\``;
90
+ }
91
+ if (agent.output_schema) {
92
+ body += `\n\n## Output Schema\n\nThis agent should return output matching:\n\`\`\`json\n${JSON.stringify(agent.output_schema, null, 2)}\n\`\`\``;
93
+ }
94
+ // Combine frontmatter + body
95
+ const content = `---\n${yaml_1.default.stringify(frontmatter).trim()}\n---\n\n${body.trim()}\n`;
96
+ return [
97
+ {
98
+ filename: `${normalizedName}.md`,
99
+ content,
100
+ installPath: '.claude/agents/',
101
+ },
102
+ ];
103
+ },
104
+ };
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cursorAdapter = void 0;
4
+ // Convert agent name to valid filename (lowercase + hyphens)
5
+ function normalizeAgentName(name) {
6
+ return name
7
+ .toLowerCase()
8
+ .replace(/[^a-z0-9-]/g, '-')
9
+ .replace(/-+/g, '-')
10
+ .replace(/^-|-$/g, '');
11
+ }
12
+ exports.cursorAdapter = {
13
+ id: 'cursor',
14
+ name: 'Cursor Rules',
15
+ version: '1.0.0',
16
+ formatVersion: '2026-01',
17
+ supportsAgentTypes: ['prompt', 'skill'],
18
+ installPaths: [
19
+ {
20
+ scope: 'project',
21
+ path: '.cursor/rules/',
22
+ description: 'Project-level Cursor rules',
23
+ },
24
+ ],
25
+ canConvert(agent) {
26
+ const warnings = [];
27
+ const errors = [];
28
+ if (agent.type === 'code') {
29
+ errors.push('Code agents cannot be converted to Cursor rules');
30
+ return { canConvert: false, warnings, errors };
31
+ }
32
+ if (!agent.prompt) {
33
+ errors.push('Agent has no prompt content');
34
+ return { canConvert: false, warnings, errors };
35
+ }
36
+ // Cursor doesn't support input/output schemas
37
+ if (agent.input_schema) {
38
+ warnings.push('input_schema is not supported in Cursor rules');
39
+ }
40
+ if (agent.output_schema) {
41
+ warnings.push('output_schema is not supported in Cursor rules');
42
+ }
43
+ return { canConvert: true, warnings, errors };
44
+ },
45
+ convert(agent) {
46
+ const normalizedName = normalizeAgentName(agent.name);
47
+ const description = agent.description || `Rules from ${agent.name}`;
48
+ // Cursor .mdc format
49
+ const content = `---
50
+ description: ${description}
51
+ globs:
52
+ alwaysApply: false
53
+ ---
54
+
55
+ # ${agent.name}
56
+
57
+ ${agent.prompt || ''}
58
+ `;
59
+ return [
60
+ {
61
+ filename: `${normalizedName}.mdc`,
62
+ content,
63
+ installPath: '.cursor/rules/',
64
+ },
65
+ ];
66
+ },
67
+ };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.agentsMdAdapter = exports.cursorAdapter = exports.claudeCodeAdapter = exports.adapterRegistry = exports.AdapterRegistry = void 0;
18
+ // Export types
19
+ __exportStar(require("./types"), exports);
20
+ // Export registry
21
+ const registry_1 = require("./registry");
22
+ var registry_2 = require("./registry");
23
+ Object.defineProperty(exports, "AdapterRegistry", { enumerable: true, get: function () { return registry_2.AdapterRegistry; } });
24
+ Object.defineProperty(exports, "adapterRegistry", { enumerable: true, get: function () { return registry_2.adapterRegistry; } });
25
+ // Import and register adapters
26
+ const claude_code_1 = require("./claude-code");
27
+ var claude_code_2 = require("./claude-code");
28
+ Object.defineProperty(exports, "claudeCodeAdapter", { enumerable: true, get: function () { return claude_code_2.claudeCodeAdapter; } });
29
+ const cursor_1 = require("./cursor");
30
+ var cursor_2 = require("./cursor");
31
+ Object.defineProperty(exports, "cursorAdapter", { enumerable: true, get: function () { return cursor_2.cursorAdapter; } });
32
+ const agents_md_1 = require("./agents-md");
33
+ var agents_md_2 = require("./agents-md");
34
+ Object.defineProperty(exports, "agentsMdAdapter", { enumerable: true, get: function () { return agents_md_2.agentsMdAdapter; } });
35
+ // Register built-in adapters
36
+ registry_1.adapterRegistry.register(claude_code_1.claudeCodeAdapter);
37
+ registry_1.adapterRegistry.register(cursor_1.cursorAdapter);
38
+ registry_1.adapterRegistry.register(agents_md_1.agentsMdAdapter);
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.adapterRegistry = exports.AdapterRegistry = void 0;
4
+ class AdapterRegistry {
5
+ adapters = new Map();
6
+ /**
7
+ * Register an adapter
8
+ */
9
+ register(adapter) {
10
+ if (this.adapters.has(adapter.id)) {
11
+ throw new Error(`Adapter '${adapter.id}' is already registered`);
12
+ }
13
+ this.adapters.set(adapter.id, adapter);
14
+ }
15
+ /**
16
+ * Get adapter by ID
17
+ */
18
+ get(id) {
19
+ return this.adapters.get(id);
20
+ }
21
+ /**
22
+ * List all registered adapters
23
+ */
24
+ list() {
25
+ return Array.from(this.adapters.values());
26
+ }
27
+ /**
28
+ * Find adapters compatible with an agent
29
+ */
30
+ findCompatible(agent) {
31
+ return this.list().filter(adapter => {
32
+ const result = adapter.canConvert(agent);
33
+ return result.canConvert;
34
+ });
35
+ }
36
+ /**
37
+ * Check if an adapter ID is valid
38
+ */
39
+ has(id) {
40
+ return this.adapters.has(id);
41
+ }
42
+ /**
43
+ * Get all adapter IDs
44
+ */
45
+ getIds() {
46
+ return Array.from(this.adapters.keys());
47
+ }
48
+ }
49
+ exports.AdapterRegistry = AdapterRegistry;
50
+ // Global registry instance
51
+ exports.adapterRegistry = new AdapterRegistry();
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerConfigCommand = registerConfigCommand;
4
+ const config_1 = require("../lib/config");
5
+ const errors_1 = require("../lib/errors");
6
+ const SUPPORTED_KEYS = ['default-format'];
7
+ function isValidKey(key) {
8
+ return SUPPORTED_KEYS.includes(key);
9
+ }
10
+ async function setConfigValue(key, value) {
11
+ if (!isValidKey(key)) {
12
+ throw new errors_1.CliError(`Unknown config key: ${key}. Supported keys: ${SUPPORTED_KEYS.join(', ')}`);
13
+ }
14
+ if (key === 'default-format') {
15
+ const formats = value.split(',').map((f) => f.trim()).filter(Boolean);
16
+ // Validate all format IDs
17
+ const invalidFormats = formats.filter((f) => !config_1.VALID_FORMAT_IDS.includes(f));
18
+ if (invalidFormats.length > 0) {
19
+ throw new errors_1.CliError(`Invalid format ID(s): ${invalidFormats.join(', ')}. Valid formats: ${config_1.VALID_FORMAT_IDS.join(', ')}`);
20
+ }
21
+ await (0, config_1.setDefaultFormats)(formats);
22
+ process.stdout.write(`Set default-format to: ${formats.join(',')}\n`);
23
+ }
24
+ }
25
+ async function getConfigValue(key) {
26
+ if (!isValidKey(key)) {
27
+ throw new errors_1.CliError(`Unknown config key: ${key}. Supported keys: ${SUPPORTED_KEYS.join(', ')}`);
28
+ }
29
+ if (key === 'default-format') {
30
+ const formats = await (0, config_1.getDefaultFormats)();
31
+ if (formats.length === 0) {
32
+ process.stdout.write('(not set)\n');
33
+ }
34
+ else {
35
+ process.stdout.write(`${formats.join(',')}\n`);
36
+ }
37
+ }
38
+ }
39
+ async function listConfigValues() {
40
+ const config = await (0, config_1.loadConfig)();
41
+ process.stdout.write('CLI Configuration:\n\n');
42
+ // default-format
43
+ const formats = config.default_formats ?? [];
44
+ if (formats.length > 0) {
45
+ process.stdout.write(` default-format: ${formats.join(',')}\n`);
46
+ }
47
+ else {
48
+ process.stdout.write(' default-format: (not set)\n');
49
+ }
50
+ process.stdout.write('\n');
51
+ }
52
+ function registerConfigCommand(program) {
53
+ const config = program
54
+ .command('config')
55
+ .description('Manage CLI configuration');
56
+ config
57
+ .command('set <key> <value>')
58
+ .description('Set a configuration value')
59
+ .action(async (key, value) => {
60
+ await setConfigValue(key, value);
61
+ });
62
+ config
63
+ .command('get <key>')
64
+ .description('Get a configuration value')
65
+ .action(async (key) => {
66
+ await getConfigValue(key);
67
+ });
68
+ config
69
+ .command('list')
70
+ .description('List all configuration values')
71
+ .action(async () => {
72
+ await listConfigValues();
73
+ });
74
+ }
@@ -0,0 +1,44 @@
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.registerFormatsCommand = registerFormatsCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const adapters_1 = require("../adapters");
9
+ function registerFormatsCommand(program) {
10
+ program
11
+ .command('formats')
12
+ .description('List available export formats for agents')
13
+ .option('--json', 'Output raw JSON')
14
+ .action(async (options) => {
15
+ const adapters = adapters_1.adapterRegistry.list();
16
+ if (options.json) {
17
+ const data = adapters.map(a => ({
18
+ id: a.id,
19
+ name: a.name,
20
+ version: a.version,
21
+ formatVersion: a.formatVersion,
22
+ supportsAgentTypes: a.supportsAgentTypes,
23
+ installPaths: a.installPaths,
24
+ }));
25
+ process.stdout.write(JSON.stringify(data, null, 2) + '\n');
26
+ return;
27
+ }
28
+ process.stdout.write('\nAvailable export formats:\n\n');
29
+ for (const adapter of adapters) {
30
+ process.stdout.write(` ${chalk_1.default.cyan(adapter.id)} ${adapter.name}\n`);
31
+ process.stdout.write(` Supports: ${adapter.supportsAgentTypes.join(', ')} agents\n`);
32
+ process.stdout.write(` Format version: ${adapter.formatVersion}\n`);
33
+ for (const installPath of adapter.installPaths) {
34
+ const pathDisplay = installPath.scope === 'user'
35
+ ? installPath.path
36
+ : installPath.path;
37
+ process.stdout.write(` ${installPath.scope}: ${pathDisplay}\n`);
38
+ }
39
+ process.stdout.write('\n');
40
+ }
41
+ process.stdout.write('Use with: orch install <agent> --format <id>\n');
42
+ process.stdout.write('Set default: orch config set default-format <ids>\n\n');
43
+ });
44
+ }
@@ -21,6 +21,10 @@ const status_1 = require("./status");
21
21
  const workspace_1 = require("./workspace");
22
22
  const tree_1 = require("./tree");
23
23
  const docs_1 = require("./docs");
24
+ const config_1 = require("./config");
25
+ const install_1 = require("./install");
26
+ const formats_1 = require("./formats");
27
+ const update_1 = require("./update");
24
28
  function registerCommands(program) {
25
29
  (0, login_1.registerLoginCommand)(program);
26
30
  (0, whoami_1.registerWhoamiCommand)(program);
@@ -42,4 +46,8 @@ function registerCommands(program) {
42
46
  (0, workspace_1.registerWorkspaceCommand)(program);
43
47
  (0, tree_1.registerTreeCommand)(program);
44
48
  (0, docs_1.registerDocsCommand)(program);
49
+ (0, config_1.registerConfigCommand)(program);
50
+ (0, install_1.registerInstallCommand)(program);
51
+ (0, formats_1.registerFormatsCommand)(program);
52
+ (0, update_1.registerUpdateCommand)(program);
45
53
  }
@@ -0,0 +1,172 @@
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.registerInstallCommand = registerInstallCommand;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const config_1 = require("../lib/config");
11
+ const api_1 = require("../lib/api");
12
+ const errors_1 = require("../lib/errors");
13
+ const analytics_1 = require("../lib/analytics");
14
+ const adapters_1 = require("../adapters");
15
+ const installed_1 = require("../lib/installed");
16
+ const DEFAULT_VERSION = 'v1';
17
+ function parseAgentRef(value) {
18
+ const [ref, versionPart] = value.split('@');
19
+ const version = versionPart?.trim() || DEFAULT_VERSION;
20
+ const segments = ref.split('/');
21
+ if (segments.length === 1) {
22
+ return { name: segments[0], version };
23
+ }
24
+ if (segments.length === 2) {
25
+ return { org: segments[0], name: segments[1], version };
26
+ }
27
+ throw new errors_1.CliError('Invalid agent reference. Use org/agent or agent format.');
28
+ }
29
+ async function downloadAgentWithFallback(config, org, name, version) {
30
+ // Try public endpoint first
31
+ try {
32
+ const agent = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${name}/${version}`);
33
+ return { ...agent, org_slug: org };
34
+ }
35
+ catch (err) {
36
+ if (!(err instanceof api_1.ApiError) || err.status !== 404)
37
+ throw err;
38
+ }
39
+ // Fallback to authenticated endpoint for private agents
40
+ if (!config.apiKey) {
41
+ throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
42
+ }
43
+ const userOrg = await (0, api_1.getOrg)(config);
44
+ if (userOrg.slug !== org) {
45
+ throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
46
+ }
47
+ const agents = await (0, api_1.listMyAgents)(config);
48
+ const matching = agents.filter(a => a.name === name);
49
+ if (matching.length === 0) {
50
+ throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
51
+ }
52
+ let targetAgent;
53
+ if (version === 'latest') {
54
+ targetAgent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
55
+ }
56
+ else {
57
+ const found = matching.find(a => a.version === version);
58
+ if (!found) {
59
+ throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
60
+ }
61
+ targetAgent = found;
62
+ }
63
+ return { ...targetAgent, org_slug: org };
64
+ }
65
+ function registerInstallCommand(program) {
66
+ program
67
+ .command('install <agent>')
68
+ .description('Install agent as sub-agent (Claude Code, Cursor, etc.)')
69
+ .option('--format <formats>', 'Comma-separated format IDs (e.g., claude-code,cursor)')
70
+ .option('--scope <scope>', 'Install scope: user (home dir) or project (current dir)', 'user')
71
+ .option('--dry-run', 'Show what would be installed without making changes')
72
+ .action(async (agentArg, options) => {
73
+ const resolved = await (0, config_1.getResolvedConfig)();
74
+ const parsed = parseAgentRef(agentArg);
75
+ const org = parsed.org ?? resolved.defaultOrg;
76
+ if (!org) {
77
+ throw new errors_1.CliError('Missing org. Use org/agent format or set default org.');
78
+ }
79
+ // Determine target formats
80
+ let targetFormats = [];
81
+ if (options.format) {
82
+ targetFormats = options.format.split(',').map(f => f.trim());
83
+ const invalid = targetFormats.filter(f => !adapters_1.adapterRegistry.has(f));
84
+ if (invalid.length > 0) {
85
+ throw new errors_1.CliError(`Unknown format(s): ${invalid.join(', ')}. Available: ${adapters_1.adapterRegistry.getIds().join(', ')}`);
86
+ }
87
+ }
88
+ else {
89
+ const defaults = await (0, config_1.getDefaultFormats)();
90
+ if (defaults.length > 0) {
91
+ targetFormats = defaults;
92
+ }
93
+ else {
94
+ // No default configured - use claude-code as sensible default
95
+ targetFormats = ['claude-code'];
96
+ }
97
+ }
98
+ // Validate scope
99
+ const scope = options.scope;
100
+ if (scope !== 'user' && scope !== 'project') {
101
+ throw new errors_1.CliError('Scope must be "user" or "project"');
102
+ }
103
+ // Download agent
104
+ process.stdout.write(`Fetching ${org}/${parsed.name}@${parsed.version}...\n`);
105
+ const agent = await downloadAgentWithFallback(resolved, org, parsed.name, parsed.version);
106
+ // Install for each format
107
+ for (const formatId of targetFormats) {
108
+ const adapter = adapters_1.adapterRegistry.get(formatId);
109
+ if (!adapter) {
110
+ process.stderr.write(`Warning: Unknown format '${formatId}', skipping\n`);
111
+ continue;
112
+ }
113
+ // Check if can convert
114
+ const checkResult = adapter.canConvert(agent);
115
+ if (!checkResult.canConvert) {
116
+ process.stderr.write(`Cannot convert to ${adapter.name}:\n`);
117
+ for (const err of checkResult.errors) {
118
+ process.stderr.write(` - ${err}\n`);
119
+ }
120
+ continue;
121
+ }
122
+ // Show warnings
123
+ for (const warn of checkResult.warnings) {
124
+ process.stdout.write(`Warning (${formatId}): ${warn}\n`);
125
+ }
126
+ // Convert
127
+ const files = adapter.convert(agent);
128
+ // Determine base directory
129
+ const baseDir = scope === 'user' ? os_1.default.homedir() : process.cwd();
130
+ // Install each file
131
+ for (const file of files) {
132
+ const fullDir = path_1.default.join(baseDir, file.installPath);
133
+ const fullPath = path_1.default.join(fullDir, file.filename);
134
+ if (options.dryRun) {
135
+ process.stdout.write(`Would install: ${fullPath}\n`);
136
+ process.stdout.write(`Content preview:\n${file.content.slice(0, 500)}...\n\n`);
137
+ continue;
138
+ }
139
+ // Create directory and write file
140
+ await promises_1.default.mkdir(fullDir, { recursive: true });
141
+ await promises_1.default.writeFile(fullPath, file.content);
142
+ // Track installation
143
+ const installedAgent = {
144
+ agent: `${org}/${parsed.name}`,
145
+ version: parsed.version,
146
+ format: formatId,
147
+ scope,
148
+ path: fullPath,
149
+ installedAt: new Date().toISOString(),
150
+ adapterVersion: adapter.version,
151
+ contentHash: (0, installed_1.computeHash)(file.content),
152
+ };
153
+ await (0, installed_1.trackInstall)(installedAgent);
154
+ process.stdout.write(`Installed: ${fullPath}\n`);
155
+ }
156
+ }
157
+ if (!options.dryRun) {
158
+ await (0, analytics_1.track)('cli_agent_install', {
159
+ agent: `${org}/${parsed.name}`,
160
+ formats: targetFormats,
161
+ scope,
162
+ });
163
+ process.stdout.write(`\nAgent installed successfully!\n`);
164
+ if (scope === 'user') {
165
+ process.stdout.write(`Available in all your projects.\n`);
166
+ }
167
+ else {
168
+ process.stdout.write(`Available in this project only.\n`);
169
+ }
170
+ }
171
+ });
172
+ }
@@ -162,8 +162,29 @@ Instructions and guidance for AI agents...
162
162
  .description('Install skill to local AI tool directories (Claude Code, Cursor, etc.)')
163
163
  .option('--global', 'Install to home directory (default: current directory)')
164
164
  .option('--dry-run', 'Show what would be installed without making changes')
165
+ .option('--format <formats>', 'Comma-separated format IDs (e.g., claude-code,cursor)')
165
166
  .action(async (skillRef, options) => {
166
167
  const resolved = await (0, config_1.getResolvedConfig)();
168
+ // Determine target formats
169
+ let targetFormats = [];
170
+ if (options.format) {
171
+ targetFormats = options.format.split(',').map((f) => f.trim());
172
+ // Validate format IDs
173
+ const invalid = targetFormats.filter(f => !config_1.VALID_FORMAT_IDS.includes(f));
174
+ if (invalid.length > 0) {
175
+ throw new errors_1.CliError(`Invalid format ID(s): ${invalid.join(', ')}. Valid: ${config_1.VALID_FORMAT_IDS.join(', ')}`);
176
+ }
177
+ }
178
+ else {
179
+ const defaults = await (0, config_1.getDefaultFormats)();
180
+ if (defaults.length > 0) {
181
+ targetFormats = defaults;
182
+ }
183
+ }
184
+ // Determine target directories
185
+ const toolDirs = targetFormats.length > 0
186
+ ? targetFormats.map(f => config_1.FORMAT_SKILL_DIRS[f])
187
+ : AI_TOOL_SKILL_DIRS; // fallback to all
167
188
  const parsed = parseSkillRef(skillRef);
168
189
  const org = parsed.org ?? resolved.defaultOrg;
169
190
  if (!org) {
@@ -191,7 +212,7 @@ ${skillData.prompt}
191
212
  if (options.dryRun) {
192
213
  process.stdout.write(`Would install ${org}/${parsed.skill}@${parsed.version}\n\n`);
193
214
  process.stdout.write(`Target directories:\n`);
194
- for (const tool of AI_TOOL_SKILL_DIRS) {
215
+ for (const tool of toolDirs) {
195
216
  const skillDir = path_1.default.join(baseDir, tool.path);
196
217
  const skillFile = path_1.default.join(skillDir, `${parsed.skill}.md`);
197
218
  process.stdout.write(` - ${tool.name}: ${skillFile}\n`);
@@ -199,9 +220,9 @@ ${skillData.prompt}
199
220
  process.stdout.write(`\nNo changes made (dry run)\n`);
200
221
  return;
201
222
  }
202
- // Install to all AI tool directories
223
+ // Install to target AI tool directories
203
224
  const installed = [];
204
- for (const tool of AI_TOOL_SKILL_DIRS) {
225
+ for (const tool of toolDirs) {
205
226
  const skillDir = path_1.default.join(baseDir, tool.path);
206
227
  const skillFile = path_1.default.join(skillDir, `${parsed.skill}.md`);
207
228
  try {
@@ -0,0 +1,146 @@
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.registerUpdateCommand = registerUpdateCommand;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const config_1 = require("../lib/config");
11
+ const api_1 = require("../lib/api");
12
+ const analytics_1 = require("../lib/analytics");
13
+ const adapters_1 = require("../adapters");
14
+ const installed_1 = require("../lib/installed");
15
+ async function fetchLatestAgent(config, agentRef) {
16
+ const [org, name] = agentRef.split('/');
17
+ if (!org || !name)
18
+ return null;
19
+ try {
20
+ // Try to get latest version
21
+ const agent = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${name}/latest`);
22
+ return {
23
+ agent: { ...agent, org_slug: org },
24
+ latestVersion: agent.version
25
+ };
26
+ }
27
+ catch (err) {
28
+ if (err instanceof api_1.ApiError && err.status === 404) {
29
+ return null;
30
+ }
31
+ throw err;
32
+ }
33
+ }
34
+ function registerUpdateCommand(program) {
35
+ program
36
+ .command('update [agent]')
37
+ .description('Update installed agents to latest versions')
38
+ .option('--check', 'Check for updates without installing')
39
+ .option('--force', 'Force update even if file was modified locally')
40
+ .action(async (agentRef, options) => {
41
+ const resolved = await (0, config_1.getResolvedConfig)();
42
+ const installed = await (0, installed_1.getInstalled)();
43
+ if (installed.length === 0) {
44
+ process.stdout.write('No agents installed. Use "orch install <agent>" to install agents.\n');
45
+ return;
46
+ }
47
+ // Filter to specific agent if provided
48
+ const toCheck = agentRef
49
+ ? installed.filter(i => i.agent === agentRef)
50
+ : installed;
51
+ if (toCheck.length === 0) {
52
+ process.stdout.write(`Agent "${agentRef}" is not installed.\n`);
53
+ return;
54
+ }
55
+ process.stdout.write(`Checking ${toCheck.length} installed agent(s) for updates...\n\n`);
56
+ let updatesAvailable = 0;
57
+ let updatesApplied = 0;
58
+ let skippedModified = 0;
59
+ for (const item of toCheck) {
60
+ // Check if file was modified locally
61
+ const wasModified = await (0, installed_1.checkModified)(item);
62
+ // Fetch latest version
63
+ const latest = await fetchLatestAgent(resolved, item.agent);
64
+ if (!latest) {
65
+ process.stdout.write(` ${chalk_1.default.yellow('?')} ${item.agent} - could not fetch latest\n`);
66
+ continue;
67
+ }
68
+ const hasUpdate = latest.latestVersion !== item.version;
69
+ if (!hasUpdate && !wasModified) {
70
+ process.stdout.write(` ${chalk_1.default.green('✓')} ${item.agent}@${item.version} - up to date\n`);
71
+ continue;
72
+ }
73
+ if (wasModified && !options.force) {
74
+ process.stdout.write(` ${chalk_1.default.yellow('!')} ${item.agent} - local modifications (use --force to overwrite)\n`);
75
+ skippedModified++;
76
+ continue;
77
+ }
78
+ if (hasUpdate) {
79
+ updatesAvailable++;
80
+ process.stdout.write(` ${chalk_1.default.blue('↑')} ${item.agent}@${item.version} → ${latest.latestVersion}`);
81
+ if (wasModified) {
82
+ process.stdout.write(` ${chalk_1.default.yellow('(modified)')}`);
83
+ }
84
+ process.stdout.write('\n');
85
+ if (options.check) {
86
+ continue;
87
+ }
88
+ // Apply update
89
+ const adapter = adapters_1.adapterRegistry.get(item.format);
90
+ if (!adapter) {
91
+ process.stderr.write(` Skipped: Unknown format "${item.format}"\n`);
92
+ continue;
93
+ }
94
+ const checkResult = adapter.canConvert(latest.agent);
95
+ if (!checkResult.canConvert) {
96
+ process.stderr.write(` Skipped: ${checkResult.errors.join(', ')}\n`);
97
+ continue;
98
+ }
99
+ const files = adapter.convert(latest.agent);
100
+ for (const file of files) {
101
+ // Use the original path from tracking
102
+ const fullPath = item.path;
103
+ try {
104
+ const dir = path_1.default.dirname(fullPath);
105
+ await promises_1.default.mkdir(dir, { recursive: true });
106
+ await promises_1.default.writeFile(fullPath, file.content);
107
+ // Update tracking
108
+ const updatedItem = {
109
+ ...item,
110
+ version: latest.latestVersion,
111
+ installedAt: new Date().toISOString(),
112
+ adapterVersion: adapter.version,
113
+ contentHash: (0, installed_1.computeHash)(file.content),
114
+ };
115
+ await (0, installed_1.trackInstall)(updatedItem);
116
+ process.stdout.write(` Updated: ${fullPath}\n`);
117
+ updatesApplied++;
118
+ }
119
+ catch (err) {
120
+ process.stderr.write(` Error: ${err.message}\n`);
121
+ }
122
+ }
123
+ }
124
+ }
125
+ process.stdout.write('\n');
126
+ if (options.check) {
127
+ process.stdout.write(`Found ${updatesAvailable} update(s) available.\n`);
128
+ if (skippedModified > 0) {
129
+ process.stdout.write(`${skippedModified} agent(s) have local modifications.\n`);
130
+ }
131
+ process.stdout.write('Run "orch update" without --check to apply updates.\n');
132
+ }
133
+ else {
134
+ process.stdout.write(`Applied ${updatesApplied} update(s).\n`);
135
+ if (skippedModified > 0) {
136
+ process.stdout.write(`Skipped ${skippedModified} modified agent(s). Use --force to overwrite.\n`);
137
+ }
138
+ }
139
+ await (0, analytics_1.track)('cli_agent_update', {
140
+ checked: toCheck.length,
141
+ updatesAvailable,
142
+ updatesApplied,
143
+ skippedModified,
144
+ });
145
+ });
146
+ }
@@ -3,16 +3,30 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FORMAT_SKILL_DIRS = exports.VALID_FORMAT_IDS = void 0;
6
7
  exports.loadConfig = loadConfig;
7
8
  exports.saveConfig = saveConfig;
8
9
  exports.getResolvedConfig = getResolvedConfig;
9
10
  exports.getConfigPath = getConfigPath;
11
+ exports.getDefaultFormats = getDefaultFormats;
12
+ exports.setDefaultFormats = setDefaultFormats;
10
13
  const promises_1 = __importDefault(require("fs/promises"));
11
14
  const path_1 = __importDefault(require("path"));
12
15
  const os_1 = __importDefault(require("os"));
13
16
  const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.orchagent');
14
17
  const CONFIG_PATH = path_1.default.join(CONFIG_DIR, 'config.json');
15
18
  const DEFAULT_API_URL = 'https://api.orchagent.io';
19
+ // Valid format IDs for multi-format agent export
20
+ exports.VALID_FORMAT_IDS = ['claude-code', 'cursor', 'codex', 'amp', 'opencode', 'antigravity'];
21
+ // Map format IDs to skill directories (used by skill install and agent install)
22
+ exports.FORMAT_SKILL_DIRS = {
23
+ 'claude-code': { name: 'Claude Code', path: '.claude/skills' },
24
+ 'cursor': { name: 'Cursor', path: '.cursor/skills' },
25
+ 'codex': { name: 'Codex', path: '.codex/skills' },
26
+ 'amp': { name: 'Amp', path: '.agents/skills' },
27
+ 'opencode': { name: 'OpenCode', path: '.opencode/skill' },
28
+ 'antigravity': { name: 'Antigravity', path: '.agent/skills' },
29
+ };
16
30
  async function loadConfig() {
17
31
  try {
18
32
  const raw = await promises_1.default.readFile(CONFIG_PATH, 'utf-8');
@@ -56,3 +70,12 @@ async function getResolvedConfig(overrides = {}, profile) {
56
70
  function getConfigPath() {
57
71
  return CONFIG_PATH;
58
72
  }
73
+ async function getDefaultFormats() {
74
+ const config = await loadConfig();
75
+ return config.default_formats ?? [];
76
+ }
77
+ async function setDefaultFormats(formats) {
78
+ const config = await loadConfig();
79
+ config.default_formats = formats;
80
+ await saveConfig(config);
81
+ }
@@ -0,0 +1,94 @@
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.computeHash = computeHash;
7
+ exports.loadInstalled = loadInstalled;
8
+ exports.saveInstalled = saveInstalled;
9
+ exports.trackInstall = trackInstall;
10
+ exports.getInstalled = getInstalled;
11
+ exports.getInstalledByFormat = getInstalledByFormat;
12
+ exports.checkModified = checkModified;
13
+ exports.untrackInstall = untrackInstall;
14
+ const promises_1 = __importDefault(require("fs/promises"));
15
+ const path_1 = __importDefault(require("path"));
16
+ const os_1 = __importDefault(require("os"));
17
+ const crypto_1 = __importDefault(require("crypto"));
18
+ const ORCHAGENT_DIR = path_1.default.join(os_1.default.homedir(), '.orchagent');
19
+ const INSTALLED_PATH = path_1.default.join(ORCHAGENT_DIR, 'installed.json');
20
+ /**
21
+ * Compute SHA-256 hash of content
22
+ */
23
+ function computeHash(content) {
24
+ return crypto_1.default.createHash('sha256').update(content).digest('hex');
25
+ }
26
+ /**
27
+ * Load installed agents from tracking file
28
+ */
29
+ async function loadInstalled() {
30
+ try {
31
+ const raw = await promises_1.default.readFile(INSTALLED_PATH, 'utf-8');
32
+ return JSON.parse(raw);
33
+ }
34
+ catch (err) {
35
+ if (err.code === 'ENOENT') {
36
+ return { version: 1, installed: [] };
37
+ }
38
+ throw err;
39
+ }
40
+ }
41
+ /**
42
+ * Save installed agents to tracking file
43
+ */
44
+ async function saveInstalled(data) {
45
+ await promises_1.default.mkdir(ORCHAGENT_DIR, { recursive: true });
46
+ await promises_1.default.writeFile(INSTALLED_PATH, JSON.stringify(data, null, 2) + '\n');
47
+ }
48
+ /**
49
+ * Track a newly installed agent
50
+ */
51
+ async function trackInstall(agent) {
52
+ const data = await loadInstalled();
53
+ // Remove any existing entry for same agent/format/scope/path
54
+ data.installed = data.installed.filter(i => !(i.agent === agent.agent && i.format === agent.format && i.path === agent.path));
55
+ // Add new entry
56
+ data.installed.push(agent);
57
+ await saveInstalled(data);
58
+ }
59
+ /**
60
+ * Get all installed agents
61
+ */
62
+ async function getInstalled() {
63
+ const data = await loadInstalled();
64
+ return data.installed;
65
+ }
66
+ /**
67
+ * Get installed agents filtered by format
68
+ */
69
+ async function getInstalledByFormat(format) {
70
+ const data = await loadInstalled();
71
+ return data.installed.filter(i => i.format === format);
72
+ }
73
+ /**
74
+ * Check if a file has been modified since installation
75
+ */
76
+ async function checkModified(installed) {
77
+ try {
78
+ const content = await promises_1.default.readFile(installed.path, 'utf-8');
79
+ const currentHash = computeHash(content);
80
+ return currentHash !== installed.contentHash;
81
+ }
82
+ catch {
83
+ // File doesn't exist or can't be read
84
+ return true;
85
+ }
86
+ }
87
+ /**
88
+ * Remove an installed agent from tracking
89
+ */
90
+ async function untrackInstall(agentRef, format, filePath) {
91
+ const data = await loadInstalled();
92
+ data.installed = data.installed.filter(i => !(i.agent === agentRef && i.format === format && i.path === filePath));
93
+ await saveInstalled(data);
94
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.2.26",
3
+ "version": "0.3.0",
4
4
  "description": "Command-line interface for the orchagent AI agent marketplace",
5
5
  "license": "MIT",
6
6
  "author": "orchagent <hello@orchagent.io>",