@orchagent/cli 0.3.31 → 0.3.33

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.
@@ -32,13 +32,26 @@ exports.agentsMdAdapter = {
32
32
  const orgSlug = agent.org_slug || 'unknown';
33
33
  const agentRef = `${orgSlug}/${agent.name}`;
34
34
  const description = agent.description || '';
35
+ // Build skills section if present
36
+ let skillsSection = '';
37
+ if (agent.resolvedSkills && agent.resolvedSkills.length > 0) {
38
+ skillsSection = '\n### Bundled Skills\n\nThe following skills are bundled with this agent and must be applied.';
39
+ for (const skill of agent.resolvedSkills) {
40
+ skillsSection += `\n\n#### ${skill.name}`;
41
+ if (skill.description) {
42
+ skillsSection += `\n\n${skill.description}`;
43
+ }
44
+ skillsSection += `\n\n${skill.prompt}`;
45
+ }
46
+ skillsSection += '\n';
47
+ }
35
48
  // Use orchagent markers for managed section
36
49
  const content = `<!-- orchagent:${agentRef} -->
37
50
  ## ${agent.name}
38
51
 
39
52
  ${description}
40
53
 
41
- ${agent.prompt || ''}
54
+ ${agent.prompt || ''}${skillsSection}
42
55
  <!-- /orchagent:${agentRef} -->
43
56
  `;
44
57
  return [
@@ -70,6 +70,17 @@ exports.claudeCodeAdapter = {
70
70
  if (agent.output_schema) {
71
71
  body += `\n\n## Output Schema\n\nThis agent should return output matching:\n\`\`\`json\n${JSON.stringify(agent.output_schema, null, 2)}\n\`\`\``;
72
72
  }
73
+ // Embed resolved skills
74
+ if (agent.resolvedSkills && agent.resolvedSkills.length > 0) {
75
+ body += '\n\n## Bundled Skills\n\nThe following skills are bundled with this agent and must be applied.';
76
+ for (const skill of agent.resolvedSkills) {
77
+ body += `\n\n### ${skill.name}`;
78
+ if (skill.description) {
79
+ body += `\n\n${skill.description}`;
80
+ }
81
+ body += `\n\n${skill.prompt}`;
82
+ }
83
+ }
73
84
  // Combine frontmatter + body
74
85
  const content = `---\n${yaml_1.default.stringify(frontmatter).trim()}\n---\n\n${body.trim()}\n`;
75
86
  return [
@@ -38,6 +38,18 @@ exports.cursorAdapter = {
38
38
  convert(agent) {
39
39
  const normalizedName = (0, utils_1.normalizeAgentName)(agent.name);
40
40
  const description = agent.description || `Rules from ${agent.name}`;
41
+ // Build skills section if present
42
+ let skillsSection = '';
43
+ if (agent.resolvedSkills && agent.resolvedSkills.length > 0) {
44
+ skillsSection = '\n\n## Bundled Skills\n\nThe following skills are bundled with this agent and must be applied.';
45
+ for (const skill of agent.resolvedSkills) {
46
+ skillsSection += `\n\n### ${skill.name}`;
47
+ if (skill.description) {
48
+ skillsSection += `\n\n${skill.description}`;
49
+ }
50
+ skillsSection += `\n\n${skill.prompt}`;
51
+ }
52
+ }
41
53
  // Cursor .mdc format
42
54
  const content = `---
43
55
  description: ${description}
@@ -47,7 +59,7 @@ alwaysApply: false
47
59
 
48
60
  # ${agent.name}
49
61
 
50
- ${agent.prompt || ''}
62
+ ${agent.prompt || ''}${skillsSection}
51
63
  `;
52
64
  return [
53
65
  {
@@ -15,6 +15,8 @@ const llm_1 = require("../lib/llm");
15
15
  const analytics_1 = require("../lib/analytics");
16
16
  const pricing_1 = require("../lib/pricing");
17
17
  const DEFAULT_VERSION = 'latest';
18
+ // Well-known field names for file content in prompt agent schemas (priority order)
19
+ const CONTENT_FIELD_NAMES = ['code', 'content', 'text', 'source', 'input', 'file_content', 'body'];
18
20
  // Keys that might indicate local file path references in JSON payloads
19
21
  const LOCAL_PATH_KEYS = ['path', 'directory', 'file', 'filepath', 'dir', 'folder', 'local'];
20
22
  /**
@@ -43,13 +45,42 @@ function warnIfLocalPathReference(jsonBody) {
43
45
  if (pathKey) {
44
46
  process.stderr.write(`Warning: Your payload contains a local path reference ('${pathKey}').\n` +
45
47
  `Remote agents cannot access your local filesystem. The path will be interpreted\n` +
46
- `by the server, not your local machine. Use --help for more information.\n\n`);
48
+ `by the server, not your local machine.\n\n` +
49
+ `Tip: Use 'orchagent run <agent>' instead to execute locally with filesystem access.\n\n`);
47
50
  }
48
51
  }
49
52
  catch {
50
53
  // If parsing fails, skip the warning (the actual error will be thrown later)
51
54
  }
52
55
  }
56
+ /**
57
+ * Infer the best JSON field name for file content based on the agent's input schema.
58
+ * Returns the field name to use, or 'content' as a safe default.
59
+ */
60
+ function inferFileField(inputSchema) {
61
+ if (!inputSchema || typeof inputSchema !== 'object')
62
+ return 'content';
63
+ const props = inputSchema.properties;
64
+ if (!props || typeof props !== 'object')
65
+ return 'content';
66
+ const properties = props;
67
+ // Check for well-known field names in priority order
68
+ for (const field of CONTENT_FIELD_NAMES) {
69
+ if (properties[field] && properties[field].type === 'string')
70
+ return field;
71
+ }
72
+ // If there's exactly one required string property, use that
73
+ const required = (inputSchema.required ?? []);
74
+ const stringProps = Object.entries(properties)
75
+ .filter(([, v]) => v.type === 'string')
76
+ .map(([k]) => k);
77
+ if (stringProps.length === 1)
78
+ return stringProps[0];
79
+ const requiredStrings = stringProps.filter(k => required.includes(k));
80
+ if (requiredStrings.length === 1)
81
+ return requiredStrings[0];
82
+ return 'content';
83
+ }
53
84
  function parseAgentRef(value) {
54
85
  const [ref, versionPart] = value.split('@');
55
86
  const version = versionPart?.trim() || DEFAULT_VERSION;
@@ -147,16 +178,24 @@ function registerCallCommand(program) {
147
178
  .option('--skills-only <skills>', 'Use only these skills')
148
179
  .option('--no-skills', 'Ignore default skills')
149
180
  .option('--file <path...>', 'File(s) to upload (can specify multiple)')
181
+ .option('--file-field <field>', 'Schema field name for file content (prompt agents)')
150
182
  .option('--metadata <json>', 'JSON metadata to send with files')
151
183
  .addHelpText('after', `
152
184
  Examples:
153
185
  orch call orchagent/invoice-scanner invoice.pdf
186
+ orch call orchagent/useeffect-checker --file src/App.tsx
187
+ orch call orchagent/useeffect-checker --file src/App.tsx --file-field code
154
188
  orch call orchagent/leak-finder --data '{"repo_url": "https://github.com/org/repo"}'
155
189
  cat input.json | orch call acme/agent --data @-
156
190
  orch call acme/image-processor photo.jpg --output result.png
157
191
 
158
192
  Note: Use 'call' for server-side execution (requires login), 'run' for local execution.
159
193
 
194
+ File handling:
195
+ For prompt agents, file content is read and sent as JSON mapped to the agent's
196
+ input schema. Use --file-field to specify the field name (auto-detected by default).
197
+ For code agents, files are uploaded as multipart form data.
198
+
160
199
  Important: Remote agents cannot access your local filesystem. If your --data payload
161
200
  contains keys like 'path', 'directory', 'file', etc., those values will be interpreted
162
201
  by the server, not your local machine. To send local files, use the positional file
@@ -354,8 +393,45 @@ Paid Agents:
354
393
  }
355
394
  headers['Content-Type'] = 'application/json';
356
395
  }
396
+ else if ((filePaths.length > 0 || options.metadata) && agentMeta.type === 'prompt') {
397
+ // Prompt agent + files/metadata: read content and send as JSON
398
+ const fieldName = options.fileField || inferFileField(agentMeta.input_schema);
399
+ let bodyObj = {};
400
+ // Include metadata if provided
401
+ if (options.metadata) {
402
+ try {
403
+ bodyObj = JSON.parse(options.metadata);
404
+ }
405
+ catch {
406
+ throw new errors_1.CliError('--metadata must be valid JSON.');
407
+ }
408
+ }
409
+ if (filePaths.length === 1) {
410
+ // Single file: map content to the inferred/specified schema field
411
+ const fileContent = await promises_1.default.readFile(filePaths[0], 'utf-8');
412
+ bodyObj[fieldName] = fileContent;
413
+ sourceLabel = filePaths[0];
414
+ }
415
+ else if (filePaths.length > 1) {
416
+ // Multiple files: map first to the schema field, add all as files object
417
+ const allContents = {};
418
+ for (const fp of filePaths) {
419
+ allContents[path_1.default.basename(fp)] = await promises_1.default.readFile(fp, 'utf-8');
420
+ }
421
+ // Set the primary field to the first file's content
422
+ const firstContent = await promises_1.default.readFile(filePaths[0], 'utf-8');
423
+ bodyObj[fieldName] = firstContent;
424
+ bodyObj.files = allContents;
425
+ sourceLabel = `${filePaths.length} files`;
426
+ }
427
+ if (llmCredentials) {
428
+ bodyObj.llm_credentials = llmCredentials;
429
+ }
430
+ body = JSON.stringify(bodyObj);
431
+ headers['Content-Type'] = 'application/json';
432
+ }
357
433
  else if (filePaths.length > 0 || options.metadata) {
358
- // Handle multipart file uploads
434
+ // Code agent: handle multipart file uploads
359
435
  // Inject llm_credentials into metadata if available
360
436
  let metadata = options.metadata;
361
437
  if (llmCredentials) {
@@ -10,7 +10,7 @@ function getAllValidFormatIds() {
10
10
  const skillFormatIds = [...config_1.VALID_FORMAT_IDS];
11
11
  return [...new Set([...adapterIds, ...skillFormatIds])];
12
12
  }
13
- const SUPPORTED_KEYS = ['default-format'];
13
+ const SUPPORTED_KEYS = ['default-format', 'default-scope'];
14
14
  function isValidKey(key) {
15
15
  return SUPPORTED_KEYS.includes(key);
16
16
  }
@@ -29,6 +29,13 @@ async function setConfigValue(key, value) {
29
29
  await (0, config_1.setDefaultFormats)(formats);
30
30
  process.stdout.write(`Set default-format to: ${formats.join(',')}\n`);
31
31
  }
32
+ if (key === 'default-scope') {
33
+ if (value !== 'user' && value !== 'project') {
34
+ throw new errors_1.CliError('Invalid scope. Must be "user" or "project"');
35
+ }
36
+ await (0, config_1.setDefaultScope)(value);
37
+ process.stdout.write(`Set default-scope to: ${value}\n`);
38
+ }
32
39
  }
33
40
  async function getConfigValue(key) {
34
41
  if (!isValidKey(key)) {
@@ -44,6 +51,15 @@ async function getConfigValue(key) {
44
51
  process.stdout.write(`${formats.join(',')}\n`);
45
52
  }
46
53
  }
54
+ if (key === 'default-scope') {
55
+ const scope = await (0, config_1.getDefaultScope)();
56
+ if (!scope) {
57
+ process.stdout.write('(not set)\n');
58
+ }
59
+ else {
60
+ process.stdout.write(`${scope}\n`);
61
+ }
62
+ }
47
63
  }
48
64
  async function listConfigValues() {
49
65
  const config = await (0, config_1.loadConfig)();
@@ -56,6 +72,14 @@ async function listConfigValues() {
56
72
  else {
57
73
  process.stdout.write(' default-format: (not set)\n');
58
74
  }
75
+ // default-scope
76
+ const scope = config.default_scope;
77
+ if (scope) {
78
+ process.stdout.write(` default-scope: ${scope}\n`);
79
+ }
80
+ else {
81
+ process.stdout.write(' default-scope: (not set)\n');
82
+ }
59
83
  process.stdout.write('\n');
60
84
  }
61
85
  function registerConfigCommand(program) {
@@ -9,6 +9,7 @@ const chalk_1 = __importDefault(require("chalk"));
9
9
  const config_1 = require("../lib/config");
10
10
  const api_1 = require("../lib/api");
11
11
  const errors_1 = require("../lib/errors");
12
+ const agent_ref_1 = require("../lib/agent-ref");
12
13
  const analytics_1 = require("../lib/analytics");
13
14
  async function promptText(message) {
14
15
  const rl = promises_1.default.createInterface({
@@ -23,16 +24,6 @@ async function promptConfirm(message) {
23
24
  const answer = await promptText(`${message} (y/N): `);
24
25
  return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
25
26
  }
26
- function parseAgentArg(value) {
27
- const atIndex = value.lastIndexOf('@');
28
- if (atIndex === -1 || atIndex === 0) {
29
- return { name: value, version: 'latest' };
30
- }
31
- return {
32
- name: value.slice(0, atIndex),
33
- version: value.slice(atIndex + 1) || 'latest',
34
- };
35
- }
36
27
  function registerDeleteCommand(program) {
37
28
  program
38
29
  .command('delete <agent>')
@@ -41,29 +32,29 @@ function registerDeleteCommand(program) {
41
32
  .option('--dry-run', 'Show what would be deleted without making changes')
42
33
  .addHelpText('after', `
43
34
  Examples:
44
- orch delete my-agent # Delete latest version
45
- orch delete my-agent@v1 # Delete specific version
46
- orch delete my-agent --dry-run # Preview deletion
35
+ orch delete org/my-agent # Delete latest version
36
+ orch delete org/my-agent@v1 # Delete specific version
37
+ orch delete org/my-agent --dry-run # Preview deletion
47
38
  `)
48
39
  .action(async (agent, options) => {
49
- const { name: agentName, version } = parseAgentArg(agent);
40
+ const ref = (0, agent_ref_1.parseAgentRef)(agent);
50
41
  const config = await (0, config_1.getResolvedConfig)();
51
42
  if (!config.apiKey) {
52
43
  throw new errors_1.CliError('Not logged in. Run `orch login` first.');
53
44
  }
54
45
  process.stdout.write('Finding agent...\n');
55
- // Find the agent
46
+ // Find the agent by name, filtering by org if provided
56
47
  const agents = await (0, api_1.listMyAgents)(config);
57
- const matching = agents.filter(a => a.name === agentName);
48
+ const matching = agents.filter(a => a.name === ref.agent && (!a.org_slug || a.org_slug === ref.org));
58
49
  if (matching.length === 0) {
59
- throw new errors_1.CliError(`Agent '${agentName}' not found`);
50
+ throw new errors_1.CliError(`Agent '${ref.org}/${ref.agent}' not found`);
60
51
  }
61
52
  // Select version
62
53
  let selectedAgent;
63
- if (version !== 'latest') {
64
- selectedAgent = matching.find(a => a.version === version);
54
+ if (ref.version !== 'latest') {
55
+ selectedAgent = matching.find(a => a.version === ref.version);
65
56
  if (!selectedAgent) {
66
- throw new errors_1.CliError(`Version '${version}' not found for agent '${agentName}'`);
57
+ throw new errors_1.CliError(`Version '${ref.version}' not found for agent '${ref.org}/${ref.agent}'`);
67
58
  }
68
59
  }
69
60
  else {
@@ -45,6 +45,80 @@ const SCHEMA_TEMPLATE = `{
45
45
  }
46
46
  }
47
47
  `;
48
+ const CODE_TEMPLATE_PY = `"""
49
+ orchagent code agent entrypoint.
50
+
51
+ Reads JSON input from stdin, processes it, and writes JSON output to stdout.
52
+ This is the standard orchagent code agent protocol.
53
+
54
+ Usage:
55
+ echo '{"input": "hello"}' | python main.py
56
+ """
57
+
58
+ import json
59
+ import sys
60
+
61
+
62
+ def main():
63
+ # Read JSON input from stdin
64
+ raw = sys.stdin.read()
65
+ try:
66
+ data = json.loads(raw) if raw.strip() else {}
67
+ except json.JSONDecodeError:
68
+ print(json.dumps({"error": "Invalid JSON input"}))
69
+ sys.exit(1)
70
+
71
+ user_input = data.get("input", "")
72
+
73
+ # --- Your logic here ---
74
+ result = f"Received: {user_input}"
75
+ # --- End your logic ---
76
+
77
+ # Write JSON output to stdout
78
+ print(json.dumps({"result": result}))
79
+
80
+
81
+ if __name__ == "__main__":
82
+ main()
83
+ `;
84
+ function readmeTemplate(agentName, type) {
85
+ const callExample = type === 'code'
86
+ ? `orchagent call ${agentName} input-file.txt`
87
+ : `orchagent call ${agentName} --data '{"input": "Hello world"}'`;
88
+ const runExample = type === 'code'
89
+ ? `orchagent run ${agentName} --input '{"file_path": "src/app.py"}'`
90
+ : `orchagent run ${agentName} --input '{"input": "Hello world"}'`;
91
+ return `# ${agentName}
92
+
93
+ A brief description of what this agent does.
94
+
95
+ ## Usage
96
+
97
+ ### Server execution
98
+
99
+ \`\`\`sh
100
+ ${callExample}
101
+ \`\`\`
102
+
103
+ ### Local execution
104
+
105
+ \`\`\`sh
106
+ ${runExample}
107
+ \`\`\`
108
+
109
+ ## Input
110
+
111
+ | Field | Type | Description |
112
+ |-------|------|-------------|
113
+ | \`input\` | string | The input to process |
114
+
115
+ ## Output
116
+
117
+ | Field | Type | Description |
118
+ |-------|------|-------------|
119
+ | \`result\` | string | The agent's response |
120
+ `;
121
+ }
48
122
  const SKILL_TEMPLATE = `---
49
123
  name: my-skill
50
124
  description: When to use this skill
@@ -63,27 +137,50 @@ function registerInitCommand(program) {
63
137
  .option('--type <type>', 'Type: prompt, code, or skill (default: prompt)', 'prompt')
64
138
  .action(async (name, options) => {
65
139
  const cwd = process.cwd();
140
+ // When a name is provided, create a subdirectory for the project
141
+ const targetDir = name ? path_1.default.join(cwd, name) : cwd;
66
142
  const agentName = name || path_1.default.basename(cwd);
143
+ // Create the subdirectory if a name was provided
144
+ if (name) {
145
+ await promises_1.default.mkdir(targetDir, { recursive: true });
146
+ }
67
147
  // Handle skill type separately
68
148
  if (options.type === 'skill') {
69
- const skillPath = path_1.default.join(cwd, 'SKILL.md');
149
+ const skillPath = path_1.default.join(targetDir, 'SKILL.md');
150
+ // Check if already initialized
151
+ try {
152
+ await promises_1.default.access(skillPath);
153
+ throw new errors_1.CliError(`Already initialized (SKILL.md exists in ${name ? name + '/' : 'current directory'})`);
154
+ }
155
+ catch (err) {
156
+ if (err.code !== 'ENOENT') {
157
+ throw err;
158
+ }
159
+ }
70
160
  const skillContent = SKILL_TEMPLATE.replace('my-skill', agentName);
71
161
  await promises_1.default.writeFile(skillPath, skillContent);
72
- process.stdout.write(`Initialized skill "${agentName}" in ${cwd}\n`);
162
+ process.stdout.write(`Initialized skill "${agentName}" in ${targetDir}\n`);
73
163
  process.stdout.write(`\nFiles created:\n`);
74
- process.stdout.write(` SKILL.md - Skill content with frontmatter\n`);
164
+ process.stdout.write(` ${name ? name + '/' : ''}SKILL.md - Skill content with frontmatter\n`);
75
165
  process.stdout.write(`\nNext steps:\n`);
76
- process.stdout.write(` 1. Edit SKILL.md with your skill content\n`);
77
- process.stdout.write(` 2. Run: orchagent publish\n`);
166
+ if (name) {
167
+ process.stdout.write(` 1. cd ${name}\n`);
168
+ process.stdout.write(` 2. Edit SKILL.md with your skill content\n`);
169
+ process.stdout.write(` 3. Run: orchagent publish\n`);
170
+ }
171
+ else {
172
+ process.stdout.write(` 1. Edit SKILL.md with your skill content\n`);
173
+ process.stdout.write(` 2. Run: orchagent publish\n`);
174
+ }
78
175
  return;
79
176
  }
80
- const manifestPath = path_1.default.join(cwd, 'orchagent.json');
81
- const promptPath = path_1.default.join(cwd, 'prompt.md');
82
- const schemaPath = path_1.default.join(cwd, 'schema.json');
177
+ const manifestPath = path_1.default.join(targetDir, 'orchagent.json');
178
+ const promptPath = path_1.default.join(targetDir, 'prompt.md');
179
+ const schemaPath = path_1.default.join(targetDir, 'schema.json');
83
180
  // Check if already initialized
84
181
  try {
85
182
  await promises_1.default.access(manifestPath);
86
- throw new errors_1.CliError(`Already initialized (orchagent.json exists)`);
183
+ throw new errors_1.CliError(`Already initialized (orchagent.json exists in ${name ? name + '/' : 'current directory'})`);
87
184
  }
88
185
  catch (err) {
89
186
  if (err.code !== 'ENOENT') {
@@ -95,29 +192,49 @@ function registerInitCommand(program) {
95
192
  manifest.name = agentName;
96
193
  manifest.type = ['code', 'skill'].includes(options.type) ? options.type : 'prompt';
97
194
  await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
98
- // Create prompt template (for prompt-based agents)
99
- if (options.type !== 'code') {
195
+ // Create prompt template (for prompt-based agents) or entrypoint (for code agents)
196
+ if (options.type === 'code') {
197
+ const entrypointPath = path_1.default.join(targetDir, 'main.py');
198
+ await promises_1.default.writeFile(entrypointPath, CODE_TEMPLATE_PY);
199
+ }
200
+ else {
100
201
  await promises_1.default.writeFile(promptPath, PROMPT_TEMPLATE);
101
202
  }
102
203
  // Create schema template
103
204
  await promises_1.default.writeFile(schemaPath, SCHEMA_TEMPLATE);
104
- process.stdout.write(`Initialized agent "${agentName}" in ${cwd}\n`);
205
+ // Create README
206
+ const readmePath = path_1.default.join(targetDir, 'README.md');
207
+ await promises_1.default.writeFile(readmePath, readmeTemplate(agentName, options.type));
208
+ process.stdout.write(`Initialized agent "${agentName}" in ${targetDir}\n`);
105
209
  process.stdout.write(`\nFiles created:\n`);
106
- process.stdout.write(` orchagent.json - Agent configuration\n`);
107
- if (options.type !== 'code') {
108
- process.stdout.write(` prompt.md - Prompt template\n`);
210
+ const prefix = name ? name + '/' : '';
211
+ process.stdout.write(` ${prefix}orchagent.json - Agent configuration\n`);
212
+ if (options.type === 'code') {
213
+ process.stdout.write(` ${prefix}main.py - Agent entrypoint (stdin/stdout JSON)\n`);
109
214
  }
110
- process.stdout.write(` schema.json - Input/output schemas\n`);
215
+ else {
216
+ process.stdout.write(` ${prefix}prompt.md - Prompt template\n`);
217
+ }
218
+ process.stdout.write(` ${prefix}schema.json - Input/output schemas\n`);
219
+ process.stdout.write(` ${prefix}README.md - Agent documentation\n`);
111
220
  process.stdout.write(`\nNext steps:\n`);
112
221
  if (options.type !== 'code') {
113
- process.stdout.write(` 1. Edit prompt.md with your prompt template\n`);
114
- process.stdout.write(` 2. Edit schema.json with your input/output schemas\n`);
115
- process.stdout.write(` 3. Run: orchagent publish\n`);
222
+ const stepNum = name ? 2 : 1;
223
+ if (name) {
224
+ process.stdout.write(` 1. cd ${name}\n`);
225
+ }
226
+ process.stdout.write(` ${stepNum}. Edit prompt.md with your prompt template\n`);
227
+ process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
228
+ process.stdout.write(` ${stepNum + 2}. Run: orchagent publish\n`);
116
229
  }
117
230
  else {
118
- process.stdout.write(` 1. Edit schema.json with your input/output schemas\n`);
119
- process.stdout.write(` 2. Deploy your code and get the URL\n`);
120
- process.stdout.write(` 3. Run: orchagent publish --url <your-agent-url>\n`);
231
+ const stepNum = name ? 2 : 1;
232
+ if (name) {
233
+ process.stdout.write(` 1. cd ${name}\n`);
234
+ }
235
+ process.stdout.write(` ${stepNum}. Edit main.py with your agent logic\n`);
236
+ process.stdout.write(` ${stepNum + 1}. Test: echo '{"input": "test"}' | python main.py\n`);
237
+ process.stdout.write(` ${stepNum + 2}. Run: orchagent publish\n`);
121
238
  }
122
239
  });
123
240
  }
@@ -12,6 +12,7 @@ const api_1 = require("../lib/api");
12
12
  const errors_1 = require("../lib/errors");
13
13
  const analytics_1 = require("../lib/analytics");
14
14
  const adapters_1 = require("../adapters");
15
+ const skill_resolve_1 = require("../lib/skill-resolve");
15
16
  const installed_1 = require("../lib/installed");
16
17
  const agents_md_utils_1 = require("../lib/agents-md-utils");
17
18
  const pricing_1 = require("../lib/pricing");
@@ -123,7 +124,7 @@ function registerInstallCommand(program) {
123
124
  .command('install <agent>')
124
125
  .description('Install agent as sub-agent (Claude Code, Cursor, etc.)')
125
126
  .option('--format <formats>', 'Comma-separated format IDs (e.g., claude-code,cursor)')
126
- .option('--scope <scope>', 'Install scope: user (home dir) or project (current dir)', 'user')
127
+ .option('--scope <scope>', 'Install scope: user (home dir) or project (current dir)')
127
128
  .option('--dry-run', 'Show what would be installed without making changes')
128
129
  .option('--json', 'Output result as JSON (for automation/tooling)')
129
130
  .addHelpText('after', `
@@ -187,8 +188,8 @@ Note: Paid agents cannot be installed locally - they run on server only.
187
188
  }
188
189
  }
189
190
  result.formats = targetFormats;
190
- // Validate scope
191
- let scope = options.scope;
191
+ // Resolve scope: CLI flag > config default > fallback to 'user'
192
+ let scope = (options.scope ?? await (0, config_1.getDefaultScope)() ?? 'user');
192
193
  if (scope !== 'user' && scope !== 'project') {
193
194
  const errMsg = 'Scope must be "user" or "project"';
194
195
  if (jsonMode) {
@@ -213,6 +214,18 @@ Note: Paid agents cannot be installed locally - they run on server only.
213
214
  }
214
215
  throw err;
215
216
  }
217
+ // Resolve default skills if present
218
+ if (agent.default_skills && agent.default_skills.length > 0) {
219
+ log(`Resolving ${agent.default_skills.length} bundled skill(s)...\n`);
220
+ const skills = await (0, skill_resolve_1.resolveSkills)(resolved, agent.default_skills, (warning) => {
221
+ result.warnings.push(warning);
222
+ logErr(`Warning: ${warning}\n`);
223
+ });
224
+ if (skills.length > 0) {
225
+ agent.resolvedSkills = skills;
226
+ log(`Bundled ${skills.length} skill(s): ${skills.map(s => s.name).join(', ')}\n`);
227
+ }
228
+ }
216
229
  // Install for each format
217
230
  let filesWritten = 0;
218
231
  for (const formatId of targetFormats) {
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerLlmConfigCommand = registerLlmConfigCommand;
4
+ const config_1 = require("../lib/config");
5
+ const api_1 = require("../lib/api");
6
+ const errors_1 = require("../lib/errors");
7
+ function registerLlmConfigCommand(program) {
8
+ program
9
+ .command('llm-config')
10
+ .description('Set default LLM configuration for prompt-based agents')
11
+ .option('--endpoint <url>', 'LLM API base URL (e.g., https://api.openai.com/v1)')
12
+ .option('--model <model>', 'Default model name (e.g., gpt-4.1-mini)')
13
+ .option('--api-key <key>', 'LLM API key')
14
+ .option('--clear', 'Clear the stored default LLM config')
15
+ .action(async (options) => {
16
+ const config = await (0, config_1.getResolvedConfig)();
17
+ if (!config.apiKey) {
18
+ throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
19
+ }
20
+ if (options.clear) {
21
+ await (0, api_1.updateOrg)(config, { default_llm_config: null });
22
+ process.stdout.write('Cleared default LLM config.\n');
23
+ return;
24
+ }
25
+ const endpoint = options.endpoint?.trim();
26
+ const model = options.model?.trim();
27
+ const apiKey = options.apiKey?.trim();
28
+ if (!endpoint || !model || !apiKey) {
29
+ throw new errors_1.CliError('Missing required flags. Use --endpoint, --model, and --api-key (or --clear).');
30
+ }
31
+ await (0, api_1.updateOrg)(config, {
32
+ default_llm_config: {
33
+ endpoint,
34
+ model,
35
+ api_key: apiKey,
36
+ },
37
+ });
38
+ process.stdout.write('Saved default LLM config.\n');
39
+ });
40
+ }