@orchagent/cli 0.3.11 → 0.3.13
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/list.js +45 -4
- package/dist/commands/run.js +3 -3
- package/dist/commands/skill.js +171 -6
- package/dist/lib/api.js +7 -1
- package/dist/lib/installed.js +24 -0
- package/package.json +1 -1
package/dist/commands/list.js
CHANGED
|
@@ -7,9 +7,28 @@ function registerListCommand(program) {
|
|
|
7
7
|
program
|
|
8
8
|
.command('list')
|
|
9
9
|
.alias('ls')
|
|
10
|
-
.description('List installed agents')
|
|
10
|
+
.description('List installed agents and skills')
|
|
11
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')
|
|
12
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
|
+
}
|
|
13
32
|
const installed = await (0, installed_1.getInstalled)();
|
|
14
33
|
if (options.json) {
|
|
15
34
|
(0, output_1.printJson)(installed);
|
|
@@ -20,9 +39,31 @@ function registerListCommand(program) {
|
|
|
20
39
|
process.stdout.write('Install agents with: orch install <agent>\n');
|
|
21
40
|
return;
|
|
22
41
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
process.stdout.write(
|
|
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
|
+
}
|
|
26
67
|
}
|
|
27
68
|
});
|
|
28
69
|
}
|
package/dist/commands/run.js
CHANGED
|
@@ -153,10 +153,10 @@ 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),
|
|
156
|
+
// In non-interactive mode (CI, piped input), skip deps by default and let agent run
|
|
157
157
|
if (!process.stdin.isTTY) {
|
|
158
|
-
process.stderr.write('Non-interactive mode
|
|
159
|
-
return '
|
|
158
|
+
process.stderr.write('Non-interactive mode: skipping dependencies (use --with-deps to include them).\n');
|
|
159
|
+
return 'local'; // Skip deps, let agent run
|
|
160
160
|
}
|
|
161
161
|
const readline = await Promise.resolve().then(() => __importStar(require('readline')));
|
|
162
162
|
const rl = readline.createInterface({
|
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,6 +44,7 @@ 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
49
|
const DEFAULT_VERSION = 'latest';
|
|
16
50
|
/**
|
|
@@ -163,6 +197,7 @@ Instructions and guidance for AI agents...
|
|
|
163
197
|
.option('--scope <scope>', 'Install scope: user or project', 'project')
|
|
164
198
|
.option('--dry-run', 'Show what would be installed without making changes')
|
|
165
199
|
.option('--format <formats>', 'Comma-separated format IDs (e.g., claude-code,cursor)')
|
|
200
|
+
.option('--all-formats', 'Install to all supported AI tool directories')
|
|
166
201
|
.option('--json', 'Output result as JSON (for automation/tooling)')
|
|
167
202
|
.action(async (skillRef, options) => {
|
|
168
203
|
const jsonMode = options.json === true;
|
|
@@ -211,10 +246,48 @@ Instructions and guidance for AI agents...
|
|
|
211
246
|
}
|
|
212
247
|
}
|
|
213
248
|
}
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
+
}
|
|
218
291
|
const parsed = parseSkillRef(skillRef);
|
|
219
292
|
const org = parsed.org ?? resolved.defaultOrg;
|
|
220
293
|
if (!org) {
|
|
@@ -268,7 +341,8 @@ ${skillData.prompt}
|
|
|
268
341
|
if (options.dryRun) {
|
|
269
342
|
log(`Would install ${org}/${parsed.skill}@${parsed.version}\n\n`);
|
|
270
343
|
log(`Target directories (scope: ${scope}):\n`);
|
|
271
|
-
for (const
|
|
344
|
+
for (const formatId of targetFormats) {
|
|
345
|
+
const tool = config_1.FORMAT_SKILL_DIRS[formatId];
|
|
272
346
|
const baseDir = scope === 'user' ? os_1.default.homedir() : process.cwd();
|
|
273
347
|
const toolPath = scope === 'user' ? tool.userPath : tool.projectPath;
|
|
274
348
|
const skillDir = path_1.default.join(baseDir, toolPath);
|
|
@@ -285,7 +359,8 @@ ${skillData.prompt}
|
|
|
285
359
|
}
|
|
286
360
|
// Install to target AI tool directories
|
|
287
361
|
const installed = [];
|
|
288
|
-
for (const
|
|
362
|
+
for (const formatId of targetFormats) {
|
|
363
|
+
const tool = config_1.FORMAT_SKILL_DIRS[formatId];
|
|
289
364
|
const baseDir = scope === 'user' ? os_1.default.homedir() : process.cwd();
|
|
290
365
|
const toolPath = scope === 'user' ? tool.userPath : tool.projectPath;
|
|
291
366
|
const skillDir = path_1.default.join(baseDir, toolPath);
|
|
@@ -295,6 +370,18 @@ ${skillData.prompt}
|
|
|
295
370
|
await promises_1.default.writeFile(skillFile, skillContent);
|
|
296
371
|
installed.push(tool.name);
|
|
297
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);
|
|
298
385
|
}
|
|
299
386
|
catch (err) {
|
|
300
387
|
// Skip if we can't write (e.g., permission issues)
|
|
@@ -335,4 +422,82 @@ ${skillData.prompt}
|
|
|
335
422
|
log(`\nLocation: ${scope === 'user' ? '~/' : './'}\n`);
|
|
336
423
|
}
|
|
337
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
|
+
});
|
|
338
503
|
}
|
package/dist/lib/api.js
CHANGED
|
@@ -205,7 +205,13 @@ async function request(config, method, path, options = {}) {
|
|
|
205
205
|
return (await response.json());
|
|
206
206
|
}
|
|
207
207
|
async function publicRequest(config, path) {
|
|
208
|
-
|
|
208
|
+
// Pass API key if available - allows server to skip IP-based rate limiting
|
|
209
|
+
// for authenticated users while still using the public endpoint for data
|
|
210
|
+
const headers = {};
|
|
211
|
+
if (config.apiKey) {
|
|
212
|
+
headers['Authorization'] = `Bearer ${config.apiKey}`;
|
|
213
|
+
}
|
|
214
|
+
const response = await safeFetchWithRetry(buildUrl(config.apiUrl, path), { headers });
|
|
209
215
|
if (!response.ok) {
|
|
210
216
|
throw await parseError(response);
|
|
211
217
|
}
|
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
|
+
}
|