@orchagent/cli 0.3.0 → 0.3.1

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.
@@ -21,11 +21,12 @@ function mapModelToAlias(model) {
21
21
  }
22
22
  // Convert agent name to valid Claude Code name (lowercase + hyphens)
23
23
  function normalizeAgentName(name) {
24
- return name
24
+ const normalized = name
25
25
  .toLowerCase()
26
26
  .replace(/[^a-z0-9-]/g, '-')
27
27
  .replace(/-+/g, '-')
28
28
  .replace(/^-|-$/g, '');
29
+ return normalized || 'agent'; // Fallback if empty
29
30
  }
30
31
  exports.claudeCodeAdapter = {
31
32
  id: 'claude-code',
@@ -3,11 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.cursorAdapter = void 0;
4
4
  // Convert agent name to valid filename (lowercase + hyphens)
5
5
  function normalizeAgentName(name) {
6
- return name
6
+ const normalized = name
7
7
  .toLowerCase()
8
8
  .replace(/[^a-z0-9-]/g, '-')
9
9
  .replace(/-+/g, '-')
10
10
  .replace(/^-|-$/g, '');
11
+ return normalized || 'agent'; // Fallback if empty
11
12
  }
12
13
  exports.cursorAdapter = {
13
14
  id: 'cursor',
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerConfigCommand = registerConfigCommand;
4
4
  const config_1 = require("../lib/config");
5
5
  const errors_1 = require("../lib/errors");
6
+ const adapters_1 = require("../adapters");
6
7
  const SUPPORTED_KEYS = ['default-format'];
7
8
  function isValidKey(key) {
8
9
  return SUPPORTED_KEYS.includes(key);
@@ -13,10 +14,11 @@ async function setConfigValue(key, value) {
13
14
  }
14
15
  if (key === 'default-format') {
15
16
  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));
17
+ // Validate all format IDs against adapter registry
18
+ const validFormatIds = adapters_1.adapterRegistry.getIds();
19
+ const invalidFormats = formats.filter((f) => !validFormatIds.includes(f));
18
20
  if (invalidFormats.length > 0) {
19
- throw new errors_1.CliError(`Invalid format ID(s): ${invalidFormats.join(', ')}. Valid formats: ${config_1.VALID_FORMAT_IDS.join(', ')}`);
21
+ throw new errors_1.CliError(`Invalid format ID(s): ${invalidFormats.join(', ')}. Valid formats: ${validFormatIds.join(', ')}`);
20
22
  }
21
23
  await (0, config_1.setDefaultFormats)(formats);
22
24
  process.stdout.write(`Set default-format to: ${formats.join(',')}\n`);
@@ -14,6 +14,9 @@ const analytics_1 = require("../lib/analytics");
14
14
  const adapters_1 = require("../adapters");
15
15
  const installed_1 = require("../lib/installed");
16
16
  const DEFAULT_VERSION = 'v1';
17
+ function escapeRegex(str) {
18
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
19
+ }
17
20
  function parseAgentRef(value) {
18
21
  const [ref, versionPart] = value.split('@');
19
22
  const version = versionPart?.trim() || DEFAULT_VERSION;
@@ -96,7 +99,7 @@ function registerInstallCommand(program) {
96
99
  }
97
100
  }
98
101
  // Validate scope
99
- const scope = options.scope;
102
+ let scope = options.scope;
100
103
  if (scope !== 'user' && scope !== 'project') {
101
104
  throw new errors_1.CliError('Scope must be "user" or "project"');
102
105
  }
@@ -104,6 +107,7 @@ function registerInstallCommand(program) {
104
107
  process.stdout.write(`Fetching ${org}/${parsed.name}@${parsed.version}...\n`);
105
108
  const agent = await downloadAgentWithFallback(resolved, org, parsed.name, parsed.version);
106
109
  // Install for each format
110
+ let filesWritten = 0;
107
111
  for (const formatId of targetFormats) {
108
112
  const adapter = adapters_1.adapterRegistry.get(formatId);
109
113
  if (!adapter) {
@@ -123,6 +127,14 @@ function registerInstallCommand(program) {
123
127
  for (const warn of checkResult.warnings) {
124
128
  process.stdout.write(`Warning (${formatId}): ${warn}\n`);
125
129
  }
130
+ // Check if adapter supports requested scope
131
+ const supportedScopes = adapter.installPaths.map(p => p.scope);
132
+ if (!supportedScopes.includes(scope)) {
133
+ process.stderr.write(`Warning: ${adapter.name} doesn't support '${scope}' scope. ` +
134
+ `Supported: ${supportedScopes.join(', ')}. Using '${supportedScopes[0]}' instead.\n`);
135
+ // Use the first supported scope
136
+ scope = supportedScopes[0];
137
+ }
126
138
  // Convert
127
139
  const files = adapter.convert(agent);
128
140
  // Determine base directory
@@ -138,7 +150,31 @@ function registerInstallCommand(program) {
138
150
  }
139
151
  // Create directory and write file
140
152
  await promises_1.default.mkdir(fullDir, { recursive: true });
153
+ // Special handling for AGENTS.md - append/replace instead of overwrite
154
+ if (file.filename === 'AGENTS.md') {
155
+ let existingContent = '';
156
+ try {
157
+ existingContent = await promises_1.default.readFile(fullPath, 'utf-8');
158
+ }
159
+ catch {
160
+ // File doesn't exist, will create new
161
+ }
162
+ const agentRef = `${org}/${parsed.name}`;
163
+ const markerStart = `<!-- orchagent:${agentRef} -->`;
164
+ const markerEnd = `<!-- /orchagent:${agentRef} -->`;
165
+ if (existingContent.includes(markerStart)) {
166
+ // Replace existing section
167
+ const regex = new RegExp(`${escapeRegex(markerStart)}[\\s\\S]*?${escapeRegex(markerEnd)}`, 'g');
168
+ file.content = existingContent.replace(regex, file.content.trim());
169
+ }
170
+ else if (existingContent) {
171
+ // Append to existing file
172
+ file.content = existingContent.trimEnd() + '\n\n' + file.content;
173
+ }
174
+ // else: new file, use content as-is
175
+ }
141
176
  await promises_1.default.writeFile(fullPath, file.content);
177
+ filesWritten++;
142
178
  // Track installation
143
179
  const installedAgent = {
144
180
  agent: `${org}/${parsed.name}`,
@@ -155,17 +191,23 @@ function registerInstallCommand(program) {
155
191
  }
156
192
  }
157
193
  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`);
194
+ if (filesWritten > 0) {
195
+ await (0, analytics_1.track)('cli_agent_install', {
196
+ agent: `${org}/${parsed.name}`,
197
+ formats: targetFormats,
198
+ scope,
199
+ });
200
+ process.stdout.write(`\nAgent installed successfully!\n`);
201
+ if (scope === 'user') {
202
+ process.stdout.write(`Available in all your projects.\n`);
203
+ }
204
+ else {
205
+ process.stdout.write(`Available in this project only.\n`);
206
+ }
166
207
  }
167
208
  else {
168
- process.stdout.write(`Available in this project only.\n`);
209
+ process.stderr.write(`\nNo files were installed. Check warnings above.\n`);
210
+ process.exit(1);
169
211
  }
170
212
  }
171
213
  });
@@ -178,7 +178,12 @@ Instructions and guidance for AI agents...
178
178
  else {
179
179
  const defaults = await (0, config_1.getDefaultFormats)();
180
180
  if (defaults.length > 0) {
181
- targetFormats = defaults;
181
+ // Filter to formats that have skill directories
182
+ targetFormats = defaults.filter(f => config_1.VALID_FORMAT_IDS.includes(f));
183
+ const skipped = defaults.filter(f => !config_1.VALID_FORMAT_IDS.includes(f));
184
+ if (skipped.length > 0) {
185
+ process.stderr.write(`Note: Skipping ${skipped.join(', ')} (no skill directory)\n`);
186
+ }
182
187
  }
183
188
  }
184
189
  // Determine target directories
@@ -12,6 +12,9 @@ const api_1 = require("../lib/api");
12
12
  const analytics_1 = require("../lib/analytics");
13
13
  const adapters_1 = require("../adapters");
14
14
  const installed_1 = require("../lib/installed");
15
+ function escapeRegex(str) {
16
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
17
+ }
15
18
  async function fetchLatestAgent(config, agentRef) {
16
19
  const [org, name] = agentRef.split('/');
17
20
  if (!org || !name)
@@ -97,12 +100,39 @@ function registerUpdateCommand(program) {
97
100
  continue;
98
101
  }
99
102
  const files = adapter.convert(latest.agent);
103
+ if (files.length > 1) {
104
+ process.stderr.write(` Skipped: Multi-file adapters not supported for update. Reinstall instead.\n`);
105
+ continue;
106
+ }
100
107
  for (const file of files) {
101
108
  // Use the original path from tracking
102
109
  const fullPath = item.path;
103
110
  try {
104
111
  const dir = path_1.default.dirname(fullPath);
105
112
  await promises_1.default.mkdir(dir, { recursive: true });
113
+ // Special handling for AGENTS.md - append/replace instead of overwrite
114
+ if (file.filename === 'AGENTS.md') {
115
+ let existingContent = '';
116
+ try {
117
+ existingContent = await promises_1.default.readFile(fullPath, 'utf-8');
118
+ }
119
+ catch {
120
+ // File doesn't exist, will create new
121
+ }
122
+ const agentRef = item.agent;
123
+ const markerStart = `<!-- orchagent:${agentRef} -->`;
124
+ const markerEnd = `<!-- /orchagent:${agentRef} -->`;
125
+ if (existingContent.includes(markerStart)) {
126
+ // Replace existing section
127
+ const regex = new RegExp(`${escapeRegex(markerStart)}[\\s\\S]*?${escapeRegex(markerEnd)}`, 'g');
128
+ file.content = existingContent.replace(regex, file.content.trim());
129
+ }
130
+ else if (existingContent) {
131
+ // Append to existing file
132
+ file.content = existingContent.trimEnd() + '\n\n' + file.content;
133
+ }
134
+ // else: new file, use content as-is
135
+ }
106
136
  await promises_1.default.writeFile(fullPath, file.content);
107
137
  // Update tracking
108
138
  const updatedItem = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Command-line interface for the orchagent AI agent marketplace",
5
5
  "license": "MIT",
6
6
  "author": "orchagent <hello@orchagent.io>",