@orchagent/cli 0.3.10 → 0.3.12
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.
- package/dist/commands/call.js +42 -0
- package/dist/commands/env.js +7 -0
- package/dist/commands/index.js +2 -0
- package/dist/commands/install.js +7 -7
- package/dist/commands/list.js +69 -0
- package/dist/commands/run.js +5 -0
- package/dist/commands/skill.js +178 -14
- package/dist/lib/bundle.js +4 -1
- package/dist/lib/config.js +1 -2
- package/dist/lib/errors.js +22 -0
- package/dist/lib/installed.js +24 -0
- package/package.json +1 -1
package/dist/commands/call.js
CHANGED
|
@@ -13,6 +13,41 @@ const output_1 = require("../lib/output");
|
|
|
13
13
|
const llm_1 = require("../lib/llm");
|
|
14
14
|
const analytics_1 = require("../lib/analytics");
|
|
15
15
|
const DEFAULT_VERSION = 'latest';
|
|
16
|
+
// Keys that might indicate local file path references in JSON payloads
|
|
17
|
+
const LOCAL_PATH_KEYS = ['path', 'directory', 'file', 'filepath', 'dir', 'folder', 'local'];
|
|
18
|
+
/**
|
|
19
|
+
* Check if a parsed JSON object contains keys that might reference local filesystem paths.
|
|
20
|
+
* Returns the first matching key found, or undefined if none found.
|
|
21
|
+
*/
|
|
22
|
+
function findLocalPathKey(obj) {
|
|
23
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const keys = Object.keys(obj);
|
|
27
|
+
for (const key of keys) {
|
|
28
|
+
if (LOCAL_PATH_KEYS.includes(key.toLowerCase())) {
|
|
29
|
+
return key;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Emit a warning to stderr if the payload contains local path references.
|
|
36
|
+
*/
|
|
37
|
+
function warnIfLocalPathReference(jsonBody) {
|
|
38
|
+
try {
|
|
39
|
+
const parsed = JSON.parse(jsonBody);
|
|
40
|
+
const pathKey = findLocalPathKey(parsed);
|
|
41
|
+
if (pathKey) {
|
|
42
|
+
process.stderr.write(`Warning: Your payload contains a local path reference ('${pathKey}').\n` +
|
|
43
|
+
`Remote agents cannot access your local filesystem. The path will be interpreted\n` +
|
|
44
|
+
`by the server, not your local machine. Use --help for more information.\n\n`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// If parsing fails, skip the warning (the actual error will be thrown later)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
16
51
|
function parseAgentRef(value) {
|
|
17
52
|
const [ref, versionPart] = value.split('@');
|
|
18
53
|
const version = versionPart?.trim() || DEFAULT_VERSION;
|
|
@@ -118,6 +153,11 @@ Examples:
|
|
|
118
153
|
orch call acme/image-processor photo.jpg --output result.png
|
|
119
154
|
|
|
120
155
|
Note: Use 'call' for server-side execution (requires login), 'run' for local execution.
|
|
156
|
+
|
|
157
|
+
Important: Remote agents cannot access your local filesystem. If your --data payload
|
|
158
|
+
contains keys like 'path', 'directory', 'file', etc., those values will be interpreted
|
|
159
|
+
by the server, not your local machine. To send local files, use the positional file
|
|
160
|
+
argument or --file option instead.
|
|
121
161
|
`)
|
|
122
162
|
.action(async (agentRef, file, options) => {
|
|
123
163
|
// Merge --input alias into --data
|
|
@@ -194,6 +234,8 @@ Note: Use 'call' for server-side execution (requires login), 'run' for local exe
|
|
|
194
234
|
}
|
|
195
235
|
// Parse JSON and inject llm_credentials if available
|
|
196
236
|
const resolvedBody = await resolveJsonBody(options.data);
|
|
237
|
+
// Warn if payload contains local path references
|
|
238
|
+
warnIfLocalPathReference(resolvedBody);
|
|
197
239
|
if (llmCredentials) {
|
|
198
240
|
const bodyObj = JSON.parse(resolvedBody);
|
|
199
241
|
bodyObj.llm_credentials = llmCredentials;
|
package/dist/commands/env.js
CHANGED
|
@@ -52,6 +52,13 @@ async function resolveWorkspaceId(config, slug) {
|
|
|
52
52
|
const org = await (0, api_1.getOrg)(config);
|
|
53
53
|
return org.id;
|
|
54
54
|
}
|
|
55
|
+
// First check if the target slug matches the user's own org
|
|
56
|
+
// This avoids calling /workspaces which requires Clerk user identity
|
|
57
|
+
const org = await (0, api_1.getOrg)(config);
|
|
58
|
+
if (org.slug === targetSlug) {
|
|
59
|
+
return org.id;
|
|
60
|
+
}
|
|
61
|
+
// Only call /workspaces if accessing a different team workspace
|
|
55
62
|
const response = await (0, api_1.request)(config, 'GET', '/workspaces');
|
|
56
63
|
const workspace = response.workspaces.find((w) => w.slug === targetSlug);
|
|
57
64
|
if (!workspace) {
|
package/dist/commands/index.js
CHANGED
|
@@ -26,6 +26,7 @@ const install_1 = require("./install");
|
|
|
26
26
|
const formats_1 = require("./formats");
|
|
27
27
|
const update_1 = require("./update");
|
|
28
28
|
const env_1 = require("./env");
|
|
29
|
+
const list_1 = require("./list");
|
|
29
30
|
function registerCommands(program) {
|
|
30
31
|
(0, login_1.registerLoginCommand)(program);
|
|
31
32
|
(0, whoami_1.registerWhoamiCommand)(program);
|
|
@@ -52,4 +53,5 @@ function registerCommands(program) {
|
|
|
52
53
|
(0, formats_1.registerFormatsCommand)(program);
|
|
53
54
|
(0, update_1.registerUpdateCommand)(program);
|
|
54
55
|
(0, env_1.registerEnvCommand)(program);
|
|
56
|
+
(0, list_1.registerListCommand)(program);
|
|
55
57
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -14,7 +14,7 @@ const analytics_1 = require("../lib/analytics");
|
|
|
14
14
|
const adapters_1 = require("../adapters");
|
|
15
15
|
const installed_1 = require("../lib/installed");
|
|
16
16
|
const agents_md_utils_1 = require("../lib/agents-md-utils");
|
|
17
|
-
const DEFAULT_VERSION = '
|
|
17
|
+
const DEFAULT_VERSION = 'latest';
|
|
18
18
|
function parseAgentRef(value) {
|
|
19
19
|
const [ref, versionPart] = value.split('@');
|
|
20
20
|
const version = versionPart?.trim() || DEFAULT_VERSION;
|
|
@@ -95,7 +95,7 @@ function registerInstallCommand(program) {
|
|
|
95
95
|
if (jsonMode) {
|
|
96
96
|
result.errors.push('Missing org. Use org/agent format or set default org.');
|
|
97
97
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
98
|
-
process.exit(
|
|
98
|
+
process.exit(errors_1.ExitCodes.INVALID_INPUT);
|
|
99
99
|
}
|
|
100
100
|
throw new errors_1.CliError('Missing org. Use org/agent format or set default org.');
|
|
101
101
|
}
|
|
@@ -111,7 +111,7 @@ function registerInstallCommand(program) {
|
|
|
111
111
|
if (jsonMode) {
|
|
112
112
|
result.errors.push(errMsg);
|
|
113
113
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
114
|
-
process.exit(
|
|
114
|
+
process.exit(errors_1.ExitCodes.INVALID_INPUT);
|
|
115
115
|
}
|
|
116
116
|
throw new errors_1.CliError(errMsg);
|
|
117
117
|
}
|
|
@@ -134,7 +134,7 @@ function registerInstallCommand(program) {
|
|
|
134
134
|
if (jsonMode) {
|
|
135
135
|
result.errors.push(errMsg);
|
|
136
136
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
137
|
-
process.exit(
|
|
137
|
+
process.exit(errors_1.ExitCodes.INVALID_INPUT);
|
|
138
138
|
}
|
|
139
139
|
throw new errors_1.CliError(errMsg);
|
|
140
140
|
}
|
|
@@ -149,7 +149,7 @@ function registerInstallCommand(program) {
|
|
|
149
149
|
if (jsonMode) {
|
|
150
150
|
result.errors.push(err instanceof Error ? err.message : String(err));
|
|
151
151
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
152
|
-
process.exit(
|
|
152
|
+
process.exit(errors_1.ExitCodes.NOT_FOUND);
|
|
153
153
|
}
|
|
154
154
|
throw err;
|
|
155
155
|
}
|
|
@@ -262,11 +262,11 @@ function registerInstallCommand(program) {
|
|
|
262
262
|
if (jsonMode) {
|
|
263
263
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
264
264
|
if (!result.success) {
|
|
265
|
-
process.exit(
|
|
265
|
+
process.exit(errors_1.ExitCodes.GENERAL_ERROR);
|
|
266
266
|
}
|
|
267
267
|
}
|
|
268
268
|
else if (!result.success && !options.dryRun) {
|
|
269
|
-
process.exit(
|
|
269
|
+
process.exit(errors_1.ExitCodes.GENERAL_ERROR);
|
|
270
270
|
}
|
|
271
271
|
});
|
|
272
272
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerListCommand = registerListCommand;
|
|
4
|
+
const installed_1 = require("../lib/installed");
|
|
5
|
+
const output_1 = require("../lib/output");
|
|
6
|
+
function registerListCommand(program) {
|
|
7
|
+
program
|
|
8
|
+
.command('list')
|
|
9
|
+
.alias('ls')
|
|
10
|
+
.description('List installed agents and skills')
|
|
11
|
+
.option('--json', 'Output as JSON')
|
|
12
|
+
.option('-v, --verbose', 'Show format details for each installation')
|
|
13
|
+
.option('--verify', 'Check tracked files exist and remove orphaned entries')
|
|
14
|
+
.action(async (options) => {
|
|
15
|
+
if (options.verify) {
|
|
16
|
+
const { valid, orphaned } = await (0, installed_1.verifyInstalled)(true); // Remove orphaned entries
|
|
17
|
+
if (orphaned.length === 0) {
|
|
18
|
+
process.stdout.write('All tracked installations verified. No orphaned entries found.\n');
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
process.stdout.write(`Removed ${orphaned.length} orphaned entries:\n`);
|
|
22
|
+
for (const entry of orphaned) {
|
|
23
|
+
process.stdout.write(` - ${entry.agent}@${entry.version} (${entry.format})\n`);
|
|
24
|
+
process.stdout.write(` Missing: ${entry.path}\n`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (valid.length > 0) {
|
|
28
|
+
process.stdout.write(`\n${valid.length} valid installations remain.\n`);
|
|
29
|
+
}
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const installed = await (0, installed_1.getInstalled)();
|
|
33
|
+
if (options.json) {
|
|
34
|
+
(0, output_1.printJson)(installed);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (installed.length === 0) {
|
|
38
|
+
process.stdout.write('No agents installed.\n');
|
|
39
|
+
process.stdout.write('Install agents with: orch install <agent>\n');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (options.verbose) {
|
|
43
|
+
// Verbose mode: show all entries with format details
|
|
44
|
+
process.stdout.write('Installed agents:\n\n');
|
|
45
|
+
for (const agent of installed) {
|
|
46
|
+
process.stdout.write(` ${agent.agent}@${agent.version} (${agent.format}, ${agent.scope})\n`);
|
|
47
|
+
process.stdout.write(` Path: ${agent.path}\n`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// Default: deduplicate by agent@version
|
|
52
|
+
const seen = new Map();
|
|
53
|
+
for (const agent of installed) {
|
|
54
|
+
const key = `${agent.agent}@${agent.version}`;
|
|
55
|
+
if (!seen.has(key)) {
|
|
56
|
+
seen.set(key, agent);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
process.stdout.write('Installed agents:\n\n');
|
|
60
|
+
for (const [, agent] of seen) {
|
|
61
|
+
process.stdout.write(` ${agent.agent}@${agent.version}\n`);
|
|
62
|
+
}
|
|
63
|
+
// Hint about verbose mode if there are duplicates
|
|
64
|
+
if (installed.length > seen.size) {
|
|
65
|
+
process.stdout.write(`\n(${installed.length - seen.size} format duplicates hidden, use --verbose to show)\n`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
package/dist/commands/run.js
CHANGED
|
@@ -153,6 +153,11 @@ async function checkDependencies(config, dependencies) {
|
|
|
153
153
|
return results;
|
|
154
154
|
}
|
|
155
155
|
async function promptUserForDeps(depStatuses) {
|
|
156
|
+
// In non-interactive mode (CI, piped input), exit gracefully with helpful message
|
|
157
|
+
if (!process.stdin.isTTY) {
|
|
158
|
+
process.stderr.write('Non-interactive mode detected. Use --with-deps to include dependencies or --no-deps to skip them.\n');
|
|
159
|
+
return 'cancel';
|
|
160
|
+
}
|
|
156
161
|
const readline = await Promise.resolve().then(() => __importStar(require('readline')));
|
|
157
162
|
const rl = readline.createInterface({
|
|
158
163
|
input: process.stdin,
|
package/dist/commands/skill.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -11,8 +44,9 @@ const config_1 = require("../lib/config");
|
|
|
11
44
|
const api_1 = require("../lib/api");
|
|
12
45
|
const errors_1 = require("../lib/errors");
|
|
13
46
|
const analytics_1 = require("../lib/analytics");
|
|
47
|
+
const installed_1 = require("../lib/installed");
|
|
14
48
|
const package_json_1 = __importDefault(require("../../package.json"));
|
|
15
|
-
const DEFAULT_VERSION = '
|
|
49
|
+
const DEFAULT_VERSION = 'latest';
|
|
16
50
|
/**
|
|
17
51
|
* AI tool skill directories.
|
|
18
52
|
*
|
|
@@ -21,7 +55,7 @@ const DEFAULT_VERSION = 'v1';
|
|
|
21
55
|
*
|
|
22
56
|
* TODO: Research and add more AI tool directories as the ecosystem evolves.
|
|
23
57
|
* Known tools to research: Gemini CLI, Aider, OpenCode, Amp, Windsurf, Cline,
|
|
24
|
-
* GitHub Copilot CLI,
|
|
58
|
+
* GitHub Copilot CLI, Qwen Code, Kimi Code, etc.
|
|
25
59
|
*
|
|
26
60
|
* References:
|
|
27
61
|
* - https://github.com/gotalab/skillport
|
|
@@ -31,7 +65,6 @@ const DEFAULT_VERSION = 'v1';
|
|
|
31
65
|
const AI_TOOL_SKILL_DIRS = [
|
|
32
66
|
{ name: 'Claude Code', projectPath: '.claude/skills', userPath: '.claude/skills' },
|
|
33
67
|
{ name: 'Cursor', projectPath: '.cursor/skills', userPath: '.cursor/skills' },
|
|
34
|
-
{ name: 'Codex', projectPath: '.codex/skills', userPath: '.codex/skills' },
|
|
35
68
|
{ name: 'Amp', projectPath: '.agents/skills', userPath: '.agents/skills' },
|
|
36
69
|
{ name: 'OpenCode', projectPath: '.opencode/skill', userPath: '.opencode/skill' },
|
|
37
70
|
{ name: 'Antigravity', projectPath: '.agent/skills', userPath: '.agent/skills' },
|
|
@@ -164,6 +197,7 @@ Instructions and guidance for AI agents...
|
|
|
164
197
|
.option('--scope <scope>', 'Install scope: user or project', 'project')
|
|
165
198
|
.option('--dry-run', 'Show what would be installed without making changes')
|
|
166
199
|
.option('--format <formats>', 'Comma-separated format IDs (e.g., claude-code,cursor)')
|
|
200
|
+
.option('--all-formats', 'Install to all supported AI tool directories')
|
|
167
201
|
.option('--json', 'Output result as JSON (for automation/tooling)')
|
|
168
202
|
.action(async (skillRef, options) => {
|
|
169
203
|
const jsonMode = options.json === true;
|
|
@@ -194,7 +228,7 @@ Instructions and guidance for AI agents...
|
|
|
194
228
|
if (jsonMode) {
|
|
195
229
|
result.errors.push(errMsg);
|
|
196
230
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
197
|
-
process.exit(
|
|
231
|
+
process.exit(errors_1.ExitCodes.INVALID_INPUT);
|
|
198
232
|
}
|
|
199
233
|
throw new errors_1.CliError(errMsg);
|
|
200
234
|
}
|
|
@@ -212,10 +246,48 @@ Instructions and guidance for AI agents...
|
|
|
212
246
|
}
|
|
213
247
|
}
|
|
214
248
|
}
|
|
215
|
-
//
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
249
|
+
// If --all-formats explicitly requested, use all formats
|
|
250
|
+
if (options.allFormats) {
|
|
251
|
+
targetFormats = config_1.VALID_FORMAT_IDS.slice();
|
|
252
|
+
}
|
|
253
|
+
// If no formats configured, prompt user (TTY) or use default (non-TTY)
|
|
254
|
+
if (targetFormats.length === 0) {
|
|
255
|
+
if (process.stdout.isTTY && !jsonMode) {
|
|
256
|
+
// Interactive prompt using readline
|
|
257
|
+
const readline = await Promise.resolve().then(() => __importStar(require('readline/promises')));
|
|
258
|
+
const rl = readline.createInterface({
|
|
259
|
+
input: process.stdin,
|
|
260
|
+
output: process.stdout,
|
|
261
|
+
});
|
|
262
|
+
log('Which AI tools do you use?\n');
|
|
263
|
+
config_1.VALID_FORMAT_IDS.forEach((f, i) => {
|
|
264
|
+
log(` ${i + 1}. ${config_1.FORMAT_SKILL_DIRS[f].name}\n`);
|
|
265
|
+
});
|
|
266
|
+
log('\nEnter numbers separated by commas (e.g., 1,2) or "all": ');
|
|
267
|
+
const answer = await rl.question('');
|
|
268
|
+
rl.close();
|
|
269
|
+
if (answer.toLowerCase() === 'all') {
|
|
270
|
+
targetFormats = config_1.VALID_FORMAT_IDS.slice();
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
const indices = answer.split(',').map(s => parseInt(s.trim(), 10) - 1);
|
|
274
|
+
const validIndices = indices.filter(i => i >= 0 && i < config_1.VALID_FORMAT_IDS.length);
|
|
275
|
+
if (validIndices.length === 0) {
|
|
276
|
+
throw new errors_1.CliError('No valid tools selected. Please run the command again.');
|
|
277
|
+
}
|
|
278
|
+
targetFormats = validIndices.map(i => config_1.VALID_FORMAT_IDS[i]);
|
|
279
|
+
}
|
|
280
|
+
// Save as default for future
|
|
281
|
+
await (0, config_1.setDefaultFormats)(targetFormats);
|
|
282
|
+
log(`\nSaved as your default formats for future installs.\n\n`);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
// Non-TTY fallback
|
|
286
|
+
targetFormats = ['claude-code'];
|
|
287
|
+
log('Note: No default formats configured. Installing to Claude Code only.\n');
|
|
288
|
+
log('Run "orchagent config set-formats" to configure defaults.\n\n');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
219
291
|
const parsed = parseSkillRef(skillRef);
|
|
220
292
|
const org = parsed.org ?? resolved.defaultOrg;
|
|
221
293
|
if (!org) {
|
|
@@ -223,7 +295,7 @@ Instructions and guidance for AI agents...
|
|
|
223
295
|
if (jsonMode) {
|
|
224
296
|
result.errors.push(errMsg);
|
|
225
297
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
226
|
-
process.exit(
|
|
298
|
+
process.exit(errors_1.ExitCodes.INVALID_INPUT);
|
|
227
299
|
}
|
|
228
300
|
throw new errors_1.CliError(errMsg);
|
|
229
301
|
}
|
|
@@ -238,7 +310,7 @@ Instructions and guidance for AI agents...
|
|
|
238
310
|
if (jsonMode) {
|
|
239
311
|
result.errors.push(err instanceof Error ? err.message : String(err));
|
|
240
312
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
241
|
-
process.exit(
|
|
313
|
+
process.exit(errors_1.ExitCodes.NOT_FOUND);
|
|
242
314
|
}
|
|
243
315
|
throw err;
|
|
244
316
|
}
|
|
@@ -247,7 +319,7 @@ Instructions and guidance for AI agents...
|
|
|
247
319
|
if (jsonMode) {
|
|
248
320
|
result.errors.push(errMsg);
|
|
249
321
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
250
|
-
process.exit(
|
|
322
|
+
process.exit(errors_1.ExitCodes.INVALID_INPUT);
|
|
251
323
|
}
|
|
252
324
|
throw new errors_1.CliError('Skill has no content.\n\n' +
|
|
253
325
|
'The skill exists but has an empty prompt. This may be a publishing issue.\n' +
|
|
@@ -269,7 +341,8 @@ ${skillData.prompt}
|
|
|
269
341
|
if (options.dryRun) {
|
|
270
342
|
log(`Would install ${org}/${parsed.skill}@${parsed.version}\n\n`);
|
|
271
343
|
log(`Target directories (scope: ${scope}):\n`);
|
|
272
|
-
for (const
|
|
344
|
+
for (const formatId of targetFormats) {
|
|
345
|
+
const tool = config_1.FORMAT_SKILL_DIRS[formatId];
|
|
273
346
|
const baseDir = scope === 'user' ? os_1.default.homedir() : process.cwd();
|
|
274
347
|
const toolPath = scope === 'user' ? tool.userPath : tool.projectPath;
|
|
275
348
|
const skillDir = path_1.default.join(baseDir, toolPath);
|
|
@@ -286,7 +359,8 @@ ${skillData.prompt}
|
|
|
286
359
|
}
|
|
287
360
|
// Install to target AI tool directories
|
|
288
361
|
const installed = [];
|
|
289
|
-
for (const
|
|
362
|
+
for (const formatId of targetFormats) {
|
|
363
|
+
const tool = config_1.FORMAT_SKILL_DIRS[formatId];
|
|
290
364
|
const baseDir = scope === 'user' ? os_1.default.homedir() : process.cwd();
|
|
291
365
|
const toolPath = scope === 'user' ? tool.userPath : tool.projectPath;
|
|
292
366
|
const skillDir = path_1.default.join(baseDir, toolPath);
|
|
@@ -296,6 +370,18 @@ ${skillData.prompt}
|
|
|
296
370
|
await promises_1.default.writeFile(skillFile, skillContent);
|
|
297
371
|
installed.push(tool.name);
|
|
298
372
|
result.files.push({ path: skillFile, tool: tool.name });
|
|
373
|
+
// Track the installation
|
|
374
|
+
const installedEntry = {
|
|
375
|
+
agent: `${org}/${parsed.skill}`,
|
|
376
|
+
version: skillData.version,
|
|
377
|
+
format: formatId,
|
|
378
|
+
scope: scope,
|
|
379
|
+
path: skillFile,
|
|
380
|
+
installedAt: new Date().toISOString(),
|
|
381
|
+
adapterVersion: package_json_1.default.version,
|
|
382
|
+
contentHash: (0, installed_1.computeHash)(skillContent),
|
|
383
|
+
};
|
|
384
|
+
await (0, installed_1.trackInstall)(installedEntry);
|
|
299
385
|
}
|
|
300
386
|
catch (err) {
|
|
301
387
|
// Skip if we can't write (e.g., permission issues)
|
|
@@ -309,7 +395,7 @@ ${skillData.prompt}
|
|
|
309
395
|
if (jsonMode) {
|
|
310
396
|
result.errors.push(errMsg);
|
|
311
397
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
312
|
-
process.exit(
|
|
398
|
+
process.exit(errors_1.ExitCodes.GENERAL_ERROR);
|
|
313
399
|
}
|
|
314
400
|
throw new errors_1.CliError(errMsg);
|
|
315
401
|
}
|
|
@@ -336,4 +422,82 @@ ${skillData.prompt}
|
|
|
336
422
|
log(`\nLocation: ${scope === 'user' ? '~/' : './'}\n`);
|
|
337
423
|
}
|
|
338
424
|
});
|
|
425
|
+
// orch skill uninstall <skill>
|
|
426
|
+
skill
|
|
427
|
+
.command('uninstall <skill>')
|
|
428
|
+
.description('Uninstall skill from local AI tool directories')
|
|
429
|
+
.option('--global', 'Uninstall from home directory (default: current directory)')
|
|
430
|
+
.option('--scope <scope>', 'Uninstall scope: user or project', 'project')
|
|
431
|
+
.option('--json', 'Output result as JSON')
|
|
432
|
+
.action(async (skillRef, options) => {
|
|
433
|
+
const jsonMode = options.json === true;
|
|
434
|
+
const log = (msg) => { if (!jsonMode)
|
|
435
|
+
process.stdout.write(msg); };
|
|
436
|
+
const result = {
|
|
437
|
+
success: false,
|
|
438
|
+
skill: '',
|
|
439
|
+
scope: '',
|
|
440
|
+
removed: [],
|
|
441
|
+
errors: [],
|
|
442
|
+
};
|
|
443
|
+
const resolved = await (0, config_1.getResolvedConfig)();
|
|
444
|
+
const parsed = parseSkillRef(skillRef);
|
|
445
|
+
const org = parsed.org ?? resolved.defaultOrg;
|
|
446
|
+
if (!org) {
|
|
447
|
+
const errMsg = 'Missing org. Use org/skill or set default org.';
|
|
448
|
+
if (jsonMode) {
|
|
449
|
+
result.errors.push(errMsg);
|
|
450
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
451
|
+
process.exit(errors_1.ExitCodes.INVALID_INPUT);
|
|
452
|
+
}
|
|
453
|
+
throw new errors_1.CliError(errMsg);
|
|
454
|
+
}
|
|
455
|
+
result.skill = `${org}/${parsed.skill}`;
|
|
456
|
+
// Determine scope
|
|
457
|
+
const scope = options.global ? 'user' : (options.scope || 'project');
|
|
458
|
+
result.scope = scope;
|
|
459
|
+
// Remove from all AI tool directories
|
|
460
|
+
for (const formatId of config_1.VALID_FORMAT_IDS) {
|
|
461
|
+
const tool = config_1.FORMAT_SKILL_DIRS[formatId];
|
|
462
|
+
const baseDir = scope === 'user' ? os_1.default.homedir() : process.cwd();
|
|
463
|
+
const toolPath = scope === 'user' ? tool.userPath : tool.projectPath;
|
|
464
|
+
const skillFile = path_1.default.join(baseDir, toolPath, `${parsed.skill}.md`);
|
|
465
|
+
try {
|
|
466
|
+
await promises_1.default.unlink(skillFile);
|
|
467
|
+
result.removed.push({ path: skillFile, tool: tool.name });
|
|
468
|
+
// Untrack from installed.json
|
|
469
|
+
await (0, installed_1.untrackInstall)(`${org}/${parsed.skill}`, formatId, skillFile);
|
|
470
|
+
}
|
|
471
|
+
catch (err) {
|
|
472
|
+
if (err.code !== 'ENOENT') {
|
|
473
|
+
result.errors.push(`Failed to remove ${skillFile}: ${err.message}`);
|
|
474
|
+
}
|
|
475
|
+
// ENOENT is fine - file doesn't exist
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (result.removed.length === 0) {
|
|
479
|
+
const errMsg = `Skill '${org}/${parsed.skill}' not found in ${scope === 'user' ? 'home' : 'project'} directory`;
|
|
480
|
+
if (jsonMode) {
|
|
481
|
+
result.errors.push(errMsg);
|
|
482
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
483
|
+
process.exit(errors_1.ExitCodes.NOT_FOUND);
|
|
484
|
+
}
|
|
485
|
+
throw new errors_1.CliError(errMsg);
|
|
486
|
+
}
|
|
487
|
+
result.success = true;
|
|
488
|
+
await (0, analytics_1.track)('cli_skill_uninstall', {
|
|
489
|
+
skill: `${org}/${parsed.skill}`,
|
|
490
|
+
scope,
|
|
491
|
+
});
|
|
492
|
+
if (jsonMode) {
|
|
493
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
log(`Uninstalled ${org}/${parsed.skill}\n`);
|
|
497
|
+
log(`\nRemoved from:\n`);
|
|
498
|
+
for (const item of result.removed) {
|
|
499
|
+
log(` - ${item.tool}\n`);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
});
|
|
339
503
|
}
|
package/dist/lib/bundle.js
CHANGED
|
@@ -110,7 +110,10 @@ const DEFAULT_EXCLUDES = [
|
|
|
110
110
|
* @returns Promise that resolves with bundle metadata
|
|
111
111
|
*/
|
|
112
112
|
async function createCodeBundle(sourceDir, outputPath, options = {}) {
|
|
113
|
-
|
|
113
|
+
// Build exclude patterns, but remove any that are in the include list
|
|
114
|
+
const includeSet = new Set(options.include || []);
|
|
115
|
+
const excludePatterns = [...DEFAULT_EXCLUDES, ...(options.exclude || [])]
|
|
116
|
+
.filter(pattern => !includeSet.has(pattern));
|
|
114
117
|
// Verify source directory exists
|
|
115
118
|
const stat = await promises_1.default.stat(sourceDir);
|
|
116
119
|
if (!stat.isDirectory()) {
|
package/dist/lib/config.js
CHANGED
|
@@ -50,12 +50,11 @@ const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.orchagent');
|
|
|
50
50
|
const CONFIG_PATH = path_1.default.join(CONFIG_DIR, 'config.json');
|
|
51
51
|
const DEFAULT_API_URL = 'https://api.orchagent.io';
|
|
52
52
|
// Valid format IDs for multi-format agent export
|
|
53
|
-
exports.VALID_FORMAT_IDS = ['claude-code', 'cursor', '
|
|
53
|
+
exports.VALID_FORMAT_IDS = ['claude-code', 'cursor', 'amp', 'opencode', 'antigravity'];
|
|
54
54
|
// Map format IDs to skill directories (used by skill install and agent install)
|
|
55
55
|
exports.FORMAT_SKILL_DIRS = {
|
|
56
56
|
'claude-code': { name: 'Claude Code', projectPath: '.claude/skills', userPath: '.claude/skills' },
|
|
57
57
|
'cursor': { name: 'Cursor', projectPath: '.cursor/skills', userPath: '.cursor/skills' },
|
|
58
|
-
'codex': { name: 'Codex', projectPath: '.codex/skills', userPath: '.codex/skills' },
|
|
59
58
|
'amp': { name: 'Amp', projectPath: '.agents/skills', userPath: '.agents/skills' },
|
|
60
59
|
'opencode': { name: 'OpenCode', projectPath: '.opencode/skill', userPath: '.opencode/skill' },
|
|
61
60
|
'antigravity': { name: 'Antigravity', projectPath: '.agent/skills', userPath: '.agent/skills' },
|
package/dist/lib/errors.js
CHANGED
|
@@ -36,9 +36,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.NetworkError = exports.ExitCodes = exports.CliError = void 0;
|
|
37
37
|
exports.formatError = formatError;
|
|
38
38
|
exports.exitWithError = exitWithError;
|
|
39
|
+
exports.mapHttpStatusToExitCode = mapHttpStatusToExitCode;
|
|
39
40
|
exports.jsonInputError = jsonInputError;
|
|
40
41
|
const Sentry = __importStar(require("@sentry/node"));
|
|
41
42
|
const analytics_1 = require("./analytics");
|
|
43
|
+
const api_1 = require("./api");
|
|
42
44
|
class CliError extends Error {
|
|
43
45
|
exitCode;
|
|
44
46
|
cause;
|
|
@@ -73,6 +75,10 @@ async function exitWithError(err) {
|
|
|
73
75
|
if (err instanceof CliError) {
|
|
74
76
|
process.exit(err.exitCode);
|
|
75
77
|
}
|
|
78
|
+
// Handle API errors with proper exit codes
|
|
79
|
+
if (err instanceof api_1.ApiError) {
|
|
80
|
+
process.exit(mapHttpStatusToExitCode(err.status));
|
|
81
|
+
}
|
|
76
82
|
process.exit(1);
|
|
77
83
|
}
|
|
78
84
|
exports.ExitCodes = {
|
|
@@ -87,6 +93,22 @@ exports.ExitCodes = {
|
|
|
87
93
|
SERVER_ERROR: 8,
|
|
88
94
|
NETWORK_ERROR: 9,
|
|
89
95
|
};
|
|
96
|
+
/**
|
|
97
|
+
* Map HTTP status codes to CLI exit codes.
|
|
98
|
+
*/
|
|
99
|
+
function mapHttpStatusToExitCode(status) {
|
|
100
|
+
if (status === 401)
|
|
101
|
+
return exports.ExitCodes.AUTH_ERROR;
|
|
102
|
+
if (status === 403)
|
|
103
|
+
return exports.ExitCodes.PERMISSION_DENIED;
|
|
104
|
+
if (status === 404)
|
|
105
|
+
return exports.ExitCodes.NOT_FOUND;
|
|
106
|
+
if (status === 429)
|
|
107
|
+
return exports.ExitCodes.RATE_LIMITED;
|
|
108
|
+
if (status >= 500 && status <= 599)
|
|
109
|
+
return exports.ExitCodes.SERVER_ERROR;
|
|
110
|
+
return exports.ExitCodes.GENERAL_ERROR;
|
|
111
|
+
}
|
|
90
112
|
class NetworkError extends CliError {
|
|
91
113
|
constructor(url, cause) {
|
|
92
114
|
const host = new URL(url).host;
|
package/dist/lib/installed.js
CHANGED
|
@@ -11,6 +11,7 @@ exports.getInstalled = getInstalled;
|
|
|
11
11
|
exports.getInstalledByFormat = getInstalledByFormat;
|
|
12
12
|
exports.checkModified = checkModified;
|
|
13
13
|
exports.untrackInstall = untrackInstall;
|
|
14
|
+
exports.verifyInstalled = verifyInstalled;
|
|
14
15
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
15
16
|
const path_1 = __importDefault(require("path"));
|
|
16
17
|
const os_1 = __importDefault(require("os"));
|
|
@@ -92,3 +93,26 @@ async function untrackInstall(agentRef, format, filePath) {
|
|
|
92
93
|
data.installed = data.installed.filter(i => !(i.agent === agentRef && i.format === format && i.path === filePath));
|
|
93
94
|
await saveInstalled(data);
|
|
94
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Verify installed entries and optionally remove orphaned ones
|
|
98
|
+
* Returns { valid: InstalledAgent[], orphaned: InstalledAgent[] }
|
|
99
|
+
*/
|
|
100
|
+
async function verifyInstalled(removeOrphaned = false) {
|
|
101
|
+
const data = await loadInstalled();
|
|
102
|
+
const valid = [];
|
|
103
|
+
const orphaned = [];
|
|
104
|
+
for (const agent of data.installed) {
|
|
105
|
+
try {
|
|
106
|
+
await promises_1.default.access(agent.path);
|
|
107
|
+
valid.push(agent);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
orphaned.push(agent);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (removeOrphaned && orphaned.length > 0) {
|
|
114
|
+
data.installed = valid;
|
|
115
|
+
await saveInstalled(data);
|
|
116
|
+
}
|
|
117
|
+
return { valid, orphaned };
|
|
118
|
+
}
|