@michaelhartmayer/agentctl 1.1.0 → 1.1.3
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/ctl.js +171 -227
- package/dist/effects.js +61 -0
- package/dist/fs-utils.js +26 -19
- package/dist/index.js +190 -202
- package/dist/logic/ctl.js +172 -0
- package/dist/logic/index.js +64 -0
- package/dist/logic/install.js +57 -0
- package/dist/logic/manifest.js +8 -0
- package/dist/logic/resolve.js +73 -0
- package/dist/logic/skills.js +20 -0
- package/dist/logic/utils.js +31 -0
- package/dist/manifest.js +17 -1
- package/dist/resolve.js +30 -76
- package/dist/skills.js +11 -23
- package/package.json +1 -1
- package/dist/package.json +0 -60
- package/dist/src/ctl.js +0 -316
- package/dist/src/fs-utils.js +0 -35
- package/dist/src/index.js +0 -351
- package/dist/src/manifest.js +0 -19
- package/dist/src/resolve.js +0 -112
- package/dist/src/skills.js +0 -39
package/dist/index.js
CHANGED
|
@@ -5,129 +5,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
|
-
const
|
|
9
|
-
require("fs-extra");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
9
|
const ctl_1 = require("./ctl");
|
|
11
10
|
const resolve_1 = require("./resolve");
|
|
12
|
-
const
|
|
13
|
-
const
|
|
11
|
+
const effects_1 = require("./effects");
|
|
12
|
+
const index_1 = require("./logic/index");
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const pkgPath = path_1.default.join(__dirname, '../package.json');
|
|
16
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8'));
|
|
14
17
|
const program = new commander_1.Command();
|
|
15
|
-
const pkg = require('../package.json');
|
|
16
18
|
program
|
|
17
19
|
.name('agentctl')
|
|
18
20
|
.description('Agent Controller CLI - Unified control plane for humans and agents')
|
|
19
|
-
.version(pkg.version)
|
|
20
|
-
|
|
21
|
-
.helpOption(false) // Disable default help to allow pass-through
|
|
22
|
-
.argument('[command...]', 'Command to run')
|
|
23
|
-
.action(async (args, _options, _command) => {
|
|
24
|
-
// If no args, check for help flag or just show help
|
|
25
|
-
if (!args || args.length === 0) {
|
|
26
|
-
// If they passed --help or -h, show help. If no args at all, show help.
|
|
27
|
-
// Since we ate options, we check raw args or just treat empty args as help.
|
|
28
|
-
// command.opts() won't have help if we disabled it?
|
|
29
|
-
// Actually, if we disable helpOption, --help becomes an unknown option or arg.
|
|
30
|
-
// Let's check process.argv for -h or --help if args is empty?
|
|
31
|
-
// "agentctl --help" -> args=[], options might contain help if we didn't disable it?
|
|
32
|
-
// With helpOption(false), --help is just a flag in argv.
|
|
33
|
-
// If args is empty and we see help flag, show help.
|
|
34
|
-
if (process.argv.includes('--help') || process.argv.includes('-h') || process.argv.length <= 2) {
|
|
35
|
-
program.help();
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
// If args are present, we try to resolve.
|
|
40
|
-
// BUT, "agentctl --help" will result in args being empty if it's parsed as option?
|
|
41
|
-
// Wait, if helpOption(false), then --help is an unknown option.
|
|
42
|
-
// If allowUnknownOption is true, it might not be in 'args' if it looks like a flag.
|
|
43
|
-
// Let's rely on resolveCommand. passed args are variadic.
|
|
44
|
-
// However, "agentctl dev --help" -> args=["dev", "--help"]?
|
|
45
|
-
// My repro says yes: [ 'dev-tools', 'gh', '--help' ].
|
|
46
|
-
// So for "agentctl --help", args might be ["--help"].
|
|
47
|
-
if (args.length === 1 && (args[0] === '--help' || args[0] === '-h')) {
|
|
48
|
-
program.help();
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
// Bypass for ctl subcommand if it slipped through (shouldn't if registered)
|
|
52
|
-
if (args[0] === 'ctl')
|
|
53
|
-
return;
|
|
54
|
-
try {
|
|
55
|
-
// resolveCommand needs to handle flags in args if they are part of the path?
|
|
56
|
-
// No, flags usually come after. resolveCommand stops at first non-matching path part?
|
|
57
|
-
// resolveCommand logic: iterates args.
|
|
58
|
-
// "dev-tools gh --help" -> path "dev-tools gh", remaining "--help"
|
|
59
|
-
const result = await (0, resolve_1.resolveCommand)(args);
|
|
60
|
-
if (!result) {
|
|
61
|
-
// If not found, and they asked for help, show root help?
|
|
62
|
-
// Or if they just typed a wrong command.
|
|
63
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
64
|
-
// Try to show help for the partial command?
|
|
65
|
-
// For now, just show root list/help or error.
|
|
66
|
-
// If it's "agentctl dev --help" and "dev" is a group, resolveCommand SHOULD return the group.
|
|
67
|
-
}
|
|
68
|
-
console.error(chalk_1.default.red(`Command '${args.join(' ')}' not found.`));
|
|
69
|
-
console.log(`Run ${chalk_1.default.cyan('agentctl list')} to see available commands.`);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
const { manifest, args: remainingArgs, scope } = result;
|
|
73
|
-
if (manifest.run) {
|
|
74
|
-
// ... run logic ...
|
|
75
|
-
// remainingArgs should contain --help if it was passed.
|
|
76
|
-
const cmdDir = path_1.default.dirname(result.manifestPath);
|
|
77
|
-
let runCmd = manifest.run;
|
|
78
|
-
// Resolve relative path
|
|
79
|
-
if (runCmd.startsWith('./') || runCmd.startsWith('.\\')) {
|
|
80
|
-
runCmd = path_1.default.resolve(cmdDir, runCmd);
|
|
81
|
-
}
|
|
82
|
-
// Interpolate {{DIR}}
|
|
83
|
-
runCmd = runCmd.replace(/{{DIR}}/g, cmdDir);
|
|
84
|
-
const fullCommand = `${runCmd} ${remainingArgs.join(' ')}`;
|
|
85
|
-
console.log(chalk_1.default.dim(`[${scope}] Running: ${fullCommand}`));
|
|
86
|
-
const child = (0, child_process_1.spawn)(fullCommand, {
|
|
87
|
-
cwd: process.cwd(), // Execute in CWD as discussed
|
|
88
|
-
shell: true,
|
|
89
|
-
stdio: 'inherit',
|
|
90
|
-
env: { ...process.env, AGENTCTL_SCOPE: scope }
|
|
91
|
-
});
|
|
92
|
-
child.on('exit', (code) => {
|
|
93
|
-
process.exit(code || 0);
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
// Group
|
|
98
|
-
console.log(chalk_1.default.blue(chalk_1.default.bold(`${manifest.name}`)));
|
|
99
|
-
console.log(manifest.description || 'No description');
|
|
100
|
-
console.log('\nSubcommands:');
|
|
101
|
-
const all = await (0, ctl_1.list)();
|
|
102
|
-
const prefix = result.cmdPath + ' ';
|
|
103
|
-
// Filter logic roughly for direct children
|
|
104
|
-
const depth = result.cmdPath.split(' ').length;
|
|
105
|
-
const children = all.filter(c => c.path.startsWith(prefix) && c.path !== result.cmdPath);
|
|
106
|
-
const direct = children.filter(c => c.path.split(' ').length === depth + 1);
|
|
107
|
-
if (direct.length === 0 && children.length === 0) {
|
|
108
|
-
console.log(chalk_1.default.dim(' (No subcommands found)'));
|
|
109
|
-
}
|
|
110
|
-
for (const child of direct) {
|
|
111
|
-
console.log(` ${child.path.split(' ').pop()}\t${chalk_1.default.dim(child.description)}`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
catch (e) {
|
|
116
|
-
if (e instanceof Error) {
|
|
117
|
-
console.error(chalk_1.default.red(e.message));
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
console.error(chalk_1.default.red('An unknown error occurred'));
|
|
121
|
-
}
|
|
122
|
-
process.exit(1);
|
|
123
|
-
}
|
|
124
|
-
});
|
|
21
|
+
.version(pkg.version);
|
|
22
|
+
// --- Subcommand: ctl ---
|
|
125
23
|
const ctl = program.command('ctl')
|
|
126
24
|
.description('Agent Controller Management - Create, organize, and manage commands');
|
|
127
|
-
// --- Lifecycle Commands ---
|
|
128
|
-
// We'll stick to flat list but with good descriptions.
|
|
129
|
-
// Helper for consistent error handling
|
|
130
|
-
// Helper for consistent error handling
|
|
131
25
|
const withErrorHandling = (fn) => {
|
|
132
26
|
return async (...args) => {
|
|
133
27
|
try {
|
|
@@ -145,19 +39,21 @@ const withErrorHandling = (fn) => {
|
|
|
145
39
|
};
|
|
146
40
|
};
|
|
147
41
|
ctl.command('scaffold')
|
|
148
|
-
.description('Scaffold a new command
|
|
149
|
-
.argument('[path...]', '
|
|
42
|
+
.description('Scaffold a new command directory with a manifest and starter script.')
|
|
43
|
+
.argument('[path...]', 'Hierarchical path for the new command (e.g., "dev start" or "utils/cleanup")')
|
|
44
|
+
.summary('create a new command')
|
|
150
45
|
.addHelpText('after', `
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
46
|
+
Additional Info:
|
|
47
|
+
This command creates a folder in your local .agentctl directory.
|
|
48
|
+
Inside, it generates:
|
|
49
|
+
- manifest.json: Metadata about the command.
|
|
50
|
+
- command.sh/cmd: A starter script for your logic.
|
|
155
51
|
|
|
156
52
|
Examples:
|
|
157
|
-
$ agentctl ctl scaffold
|
|
53
|
+
$ agentctl ctl scaffold build front
|
|
158
54
|
$ agentctl ctl scaffold utils/backup
|
|
159
55
|
`)
|
|
160
|
-
.action(withErrorHandling(async (pathParts,
|
|
56
|
+
.action(withErrorHandling(async (pathParts, opts, command) => {
|
|
161
57
|
if (!pathParts || pathParts.length === 0) {
|
|
162
58
|
command.help();
|
|
163
59
|
return;
|
|
@@ -165,9 +61,15 @@ Examples:
|
|
|
165
61
|
await (0, ctl_1.scaffold)(pathParts);
|
|
166
62
|
}));
|
|
167
63
|
ctl.command('alias')
|
|
168
|
-
.description('Create
|
|
169
|
-
.argument('[
|
|
170
|
-
.
|
|
64
|
+
.description('Create a command that executes a raw shell string.')
|
|
65
|
+
.argument('[args...]', 'Hierarchical path segments followed by the shell command target')
|
|
66
|
+
.summary('create a shell alias')
|
|
67
|
+
.addHelpText('after', `
|
|
68
|
+
Examples:
|
|
69
|
+
$ agentctl ctl alias dev logs "docker compose logs -f"
|
|
70
|
+
$ agentctl ctl alias list-files "ls -la"
|
|
71
|
+
`)
|
|
72
|
+
.action(withErrorHandling(async (args, opts, command) => {
|
|
171
73
|
if (!args || args.length < 2) {
|
|
172
74
|
command.help();
|
|
173
75
|
return;
|
|
@@ -175,34 +77,21 @@ ctl.command('alias')
|
|
|
175
77
|
const target = args.pop();
|
|
176
78
|
const name = args;
|
|
177
79
|
await (0, ctl_1.alias)(name, target);
|
|
178
|
-
}))
|
|
179
|
-
.addHelpText('after', `
|
|
180
|
-
How it works:
|
|
181
|
-
The last argument is always treated as the shell command to execute.
|
|
182
|
-
All preceding arguments form the hierarchical path.
|
|
183
|
-
|
|
184
|
-
If the shell command contains spaces, wrap it in quotes.
|
|
185
|
-
|
|
186
|
-
Examples:
|
|
187
|
-
$ agentctl ctl alias tools git-status "git status"
|
|
188
|
-
-> Creates 'agentctl tools git-status' which runs 'git status'.
|
|
189
|
-
|
|
190
|
-
$ agentctl ctl alias dev build "npm run build"
|
|
191
|
-
-> Creates 'agentctl dev build' which runs 'npm run build'.
|
|
192
|
-
`);
|
|
80
|
+
}));
|
|
193
81
|
ctl.command('group')
|
|
194
|
-
.description('Create a command group (namespace) to organize
|
|
195
|
-
.argument('[path...]', 'Hierarchical path for the group (e.g
|
|
82
|
+
.description('Create a command group (namespace) to organize subcommands.')
|
|
83
|
+
.argument('[path...]', 'Hierarchical path for the group (e.g., "dev" or "cloud/aws")')
|
|
84
|
+
.summary('create a namespace group')
|
|
196
85
|
.addHelpText('after', `
|
|
197
|
-
|
|
198
|
-
Groups
|
|
199
|
-
|
|
86
|
+
Additional Info:
|
|
87
|
+
Groups allow you to categorize commands. Running a group command without
|
|
88
|
+
subcommands will list all direct subcommands within that group.
|
|
200
89
|
|
|
201
90
|
Examples:
|
|
202
91
|
$ agentctl ctl group dev
|
|
203
|
-
$ agentctl ctl group
|
|
92
|
+
$ agentctl ctl group data/pipelines
|
|
204
93
|
`)
|
|
205
|
-
.action(withErrorHandling(async (parts,
|
|
94
|
+
.action(withErrorHandling(async (parts, opts, command) => {
|
|
206
95
|
if (!parts || parts.length === 0) {
|
|
207
96
|
command.help();
|
|
208
97
|
return;
|
|
@@ -210,13 +99,14 @@ Examples:
|
|
|
210
99
|
await (0, ctl_1.group)(parts);
|
|
211
100
|
}));
|
|
212
101
|
ctl.command('rm')
|
|
213
|
-
.description('
|
|
102
|
+
.description('Permanently remove a command or group.')
|
|
214
103
|
.argument('[path...]', 'Command path to remove')
|
|
215
|
-
.option('--global', 'Remove from global scope')
|
|
104
|
+
.option('-g, --global', 'Remove from global scope instead of local')
|
|
105
|
+
.summary('delete a command')
|
|
216
106
|
.addHelpText('after', `
|
|
217
107
|
Examples:
|
|
218
108
|
$ agentctl ctl rm dev start
|
|
219
|
-
$ agentctl ctl rm
|
|
109
|
+
$ agentctl ctl rm utils --global
|
|
220
110
|
`)
|
|
221
111
|
.action(withErrorHandling(async (parts, opts, command) => {
|
|
222
112
|
if (!parts || parts.length === 0) {
|
|
@@ -226,14 +116,15 @@ Examples:
|
|
|
226
116
|
await (0, ctl_1.rm)(parts, { global: opts.global });
|
|
227
117
|
}));
|
|
228
118
|
ctl.command('mv')
|
|
229
|
-
.description('Move or rename a command
|
|
230
|
-
.argument('[src]', 'Current path
|
|
231
|
-
.argument('[dest]', 'New path
|
|
232
|
-
.option('--global', '
|
|
119
|
+
.description('Move or rename a command/group within its current scope.')
|
|
120
|
+
.argument('[src]', 'Current path (space-separated or quoted)')
|
|
121
|
+
.argument('[dest]', 'New path (space-separated or quoted)')
|
|
122
|
+
.option('-g, --global', 'Operate in global scope')
|
|
123
|
+
.summary('rename/move a command')
|
|
233
124
|
.addHelpText('after', `
|
|
234
125
|
Examples:
|
|
235
|
-
$ agentctl ctl mv "dev start" "dev
|
|
236
|
-
$ agentctl ctl mv
|
|
126
|
+
$ agentctl ctl mv "dev start" "dev begin"
|
|
127
|
+
$ agentctl ctl mv utils scripts --global
|
|
237
128
|
`)
|
|
238
129
|
.action(withErrorHandling(async (src, dest, opts, command) => {
|
|
239
130
|
if (!src || !dest) {
|
|
@@ -242,20 +133,34 @@ Examples:
|
|
|
242
133
|
}
|
|
243
134
|
await (0, ctl_1.mv)(src.split(' '), dest.split(' '), { global: opts.global });
|
|
244
135
|
}));
|
|
245
|
-
// --- Introspection ---
|
|
246
136
|
ctl.command('list')
|
|
247
|
-
.description('List all available commands across local and global scopes')
|
|
137
|
+
.description('List all available commands across local and global scopes.')
|
|
138
|
+
.summary('list all commands')
|
|
139
|
+
.addHelpText('after', `
|
|
140
|
+
Output Columns:
|
|
141
|
+
TYPE - scaffold, alias, or group
|
|
142
|
+
SCOPE - local (project-specific) or global (user-wide)
|
|
143
|
+
COMMAND - The path used to invoke the command
|
|
144
|
+
DESCRIPTION - Brief text from the command's manifest
|
|
145
|
+
`)
|
|
248
146
|
.action(withErrorHandling(async () => {
|
|
249
147
|
const items = await (0, ctl_1.list)();
|
|
250
|
-
console.log('TYPE SCOPE COMMAND DESCRIPTION');
|
|
148
|
+
console.log(chalk_1.default.bold('TYPE SCOPE COMMAND DESCRIPTION'));
|
|
251
149
|
for (const item of items) {
|
|
252
|
-
|
|
150
|
+
const typePipe = item.type.padEnd(9);
|
|
151
|
+
const scopePipe = item.scope === 'local' ? chalk_1.default.cyan(item.scope.padEnd(9)) : chalk_1.default.magenta(item.scope.padEnd(9));
|
|
152
|
+
console.log(`${typePipe} ${scopePipe} ${chalk_1.default.yellow(item.path.padEnd(19))} ${item.description}`);
|
|
253
153
|
}
|
|
254
154
|
}));
|
|
255
155
|
ctl.command('inspect')
|
|
256
|
-
.description('
|
|
156
|
+
.description('Show the internal manifest and file system path of a command.')
|
|
257
157
|
.argument('[path...]', 'Command path to inspect')
|
|
258
|
-
.
|
|
158
|
+
.summary('inspect command details')
|
|
159
|
+
.addHelpText('after', `
|
|
160
|
+
Examples:
|
|
161
|
+
$ agentctl ctl inspect dev start
|
|
162
|
+
`)
|
|
163
|
+
.action(withErrorHandling(async (parts, opts, command) => {
|
|
259
164
|
if (!parts || parts.length === 0) {
|
|
260
165
|
command.help();
|
|
261
166
|
return;
|
|
@@ -265,20 +170,23 @@ ctl.command('inspect')
|
|
|
265
170
|
console.log(JSON.stringify(info, null, 2));
|
|
266
171
|
}
|
|
267
172
|
else {
|
|
268
|
-
console.error('Command not found');
|
|
173
|
+
console.error(chalk_1.default.red('Command not found'));
|
|
269
174
|
process.exit(1);
|
|
270
175
|
}
|
|
271
176
|
}));
|
|
272
|
-
// --- Scoping ---
|
|
273
177
|
ctl.command('global')
|
|
274
|
-
.description('
|
|
275
|
-
.argument('[path...]', 'Local command path')
|
|
276
|
-
.option('--move', 'Move
|
|
277
|
-
.option('--copy', 'Copy (default)')
|
|
178
|
+
.description('Promote a local command to the global scope.')
|
|
179
|
+
.argument('[path...]', 'Local command path to promote')
|
|
180
|
+
.option('-m, --move', 'Move the command (delete local after copying)')
|
|
181
|
+
.option('-c, --copy', 'Copy the command (keep local version, default)')
|
|
182
|
+
.summary('make a command global')
|
|
278
183
|
.addHelpText('after', `
|
|
184
|
+
Additional Info:
|
|
185
|
+
Global commands are stored in your home directory and are available in any project.
|
|
186
|
+
|
|
279
187
|
Examples:
|
|
280
|
-
$ agentctl ctl global
|
|
281
|
-
$ agentctl ctl global
|
|
188
|
+
$ agentctl ctl global utils/cleanup
|
|
189
|
+
$ agentctl ctl global dev/deploy --move
|
|
282
190
|
`)
|
|
283
191
|
.action(withErrorHandling(async (parts, opts, command) => {
|
|
284
192
|
if (!parts || parts.length === 0) {
|
|
@@ -288,13 +196,15 @@ Examples:
|
|
|
288
196
|
await (0, ctl_1.pushGlobal)(parts, { move: opts.move, copy: opts.copy || !opts.move });
|
|
289
197
|
}));
|
|
290
198
|
ctl.command('local')
|
|
291
|
-
.description('Pull a global command
|
|
292
|
-
.argument('[path...]', 'Global command path')
|
|
293
|
-
.option('--move', 'Move
|
|
294
|
-
.option('--copy', 'Copy (default)')
|
|
199
|
+
.description('Pull a global command into the current local project.')
|
|
200
|
+
.argument('[path...]', 'Global command path to pull')
|
|
201
|
+
.option('-m, --move', 'Move the command (delete global after pulling)')
|
|
202
|
+
.option('-c, --copy', 'Copy the command (keep global version, default)')
|
|
203
|
+
.summary('make a command local')
|
|
295
204
|
.addHelpText('after', `
|
|
296
205
|
Examples:
|
|
297
|
-
$ agentctl ctl local
|
|
206
|
+
$ agentctl ctl local utils/shared
|
|
207
|
+
$ agentctl ctl local snippets/js --move
|
|
298
208
|
`)
|
|
299
209
|
.action(withErrorHandling(async (parts, opts, command) => {
|
|
300
210
|
if (!parts || parts.length === 0) {
|
|
@@ -303,49 +213,127 @@ Examples:
|
|
|
303
213
|
}
|
|
304
214
|
await (0, ctl_1.pullLocal)(parts, { move: opts.move, copy: opts.copy || !opts.move });
|
|
305
215
|
}));
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
.option('--global', 'Install skill globally (for supported agents)')
|
|
216
|
+
ctl.command('install-skill')
|
|
217
|
+
.description('Configure a supported AI agent (like Cursor or Gemini) to natively use Agentctl.')
|
|
218
|
+
.argument('[agent]', 'Agent name (cursor, antigravity, agentsmd, gemini)')
|
|
219
|
+
.option('-g, --global', 'Install globally for the agent (if supported)')
|
|
220
|
+
.summary('configure AI agent integration')
|
|
312
221
|
.addHelpText('after', `
|
|
222
|
+
Supported Agents:
|
|
223
|
+
- cursor (Installs to .cursor/skills)
|
|
224
|
+
- antigravity (Installs to .agent/skills or ~/.gemini/antigravity)
|
|
225
|
+
- agentsmd (Installs to .agents/skills)
|
|
226
|
+
- gemini (Installs to .gemini/skills or ~/.gemini/skills)
|
|
227
|
+
|
|
313
228
|
Examples:
|
|
314
|
-
$ agentctl ctl
|
|
315
|
-
$ agentctl ctl
|
|
316
|
-
$ agentctl ctl --install-skill gemini
|
|
229
|
+
$ agentctl ctl install-skill cursor
|
|
230
|
+
$ agentctl ctl install-skill antigravity --global
|
|
317
231
|
`)
|
|
318
|
-
.action(withErrorHandling(async (
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
232
|
+
.action(withErrorHandling(async (agent, opts, command) => {
|
|
233
|
+
if (!agent) {
|
|
234
|
+
command.help();
|
|
235
|
+
return;
|
|
322
236
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
237
|
+
await (0, ctl_1.installSkill)(agent, { global: opts.global });
|
|
238
|
+
}));
|
|
239
|
+
ctl.command('install')
|
|
240
|
+
.description('Install a command group from a remote Git repository.')
|
|
241
|
+
.argument('[repoUrl]', 'URL of the remote Git repository containing an .agentctl folder')
|
|
242
|
+
.argument('[pathParts...]', 'Optional local namespace/group to install into')
|
|
243
|
+
.option('-g, --global', 'Install globally instead of locally')
|
|
244
|
+
.option('--allow-collisions', 'Allow overwriting existing commands or merging into groups')
|
|
245
|
+
.summary('install remote command group')
|
|
246
|
+
.addHelpText('after', `
|
|
247
|
+
Additional Info:
|
|
248
|
+
Fetches the .agentctl folder from the remote repository and installs it into
|
|
249
|
+
your local or global agentctl environment.
|
|
250
|
+
|
|
251
|
+
Examples:
|
|
252
|
+
$ agentctl ctl install https://github.com/org/repo-tools
|
|
253
|
+
$ agentctl ctl install https://github.com/org/deploy-scripts deploy --global
|
|
254
|
+
`)
|
|
255
|
+
.action(withErrorHandling(async (repoUrl, pathParts, opts, command) => {
|
|
256
|
+
if (!repoUrl) {
|
|
257
|
+
command.help();
|
|
258
|
+
return;
|
|
328
259
|
}
|
|
260
|
+
await (0, ctl_1.install)(repoUrl, pathParts, { global: opts.global, allowCollisions: opts.allowCollisions });
|
|
329
261
|
}));
|
|
330
|
-
//
|
|
331
|
-
|
|
262
|
+
// --- Dynamic Command Logic ---
|
|
263
|
+
async function handleDynamicCommand(args) {
|
|
264
|
+
try {
|
|
265
|
+
const result = await (0, resolve_1.resolveCommand)(args);
|
|
266
|
+
if (!result) {
|
|
267
|
+
const effects = index_1.AppLogic.planApp(args, null);
|
|
268
|
+
await (0, effects_1.execute)(effects.map(e => e.type === 'log' ? { ...e, message: chalk_1.default.red(e.message) } : e));
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
if (result.manifest.run) {
|
|
272
|
+
const effects = index_1.AppLogic.planApp(args, result);
|
|
273
|
+
await (0, effects_1.execute)(effects);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
const all = await (0, ctl_1.list)();
|
|
277
|
+
const effects = index_1.AppLogic.planGroupList(result.manifest, result.cmdPath, all);
|
|
278
|
+
await (0, effects_1.execute)(effects.map(e => {
|
|
279
|
+
if (e.type === 'log') {
|
|
280
|
+
if (e.message === result.manifest.name)
|
|
281
|
+
return { ...e, message: chalk_1.default.blue(chalk_1.default.bold(e.message)) };
|
|
282
|
+
if (e.message === '\nSubcommands:')
|
|
283
|
+
return e;
|
|
284
|
+
if (e.message.startsWith(' '))
|
|
285
|
+
return e;
|
|
286
|
+
if (e.message === 'No description')
|
|
287
|
+
return { ...e, message: chalk_1.default.dim(e.message) };
|
|
288
|
+
}
|
|
289
|
+
return e;
|
|
290
|
+
}));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
catch (e) {
|
|
294
|
+
if (e instanceof Error) {
|
|
295
|
+
console.error(chalk_1.default.red(e.message));
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
console.error(chalk_1.default.red('An unknown error occurred'));
|
|
299
|
+
}
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
332
303
|
(async () => {
|
|
304
|
+
// Add help text for user commands dynamically
|
|
333
305
|
try {
|
|
334
306
|
const allCommands = await (0, ctl_1.list)();
|
|
335
|
-
const topLevel = allCommands.filter(c => !c.path.includes(' '));
|
|
307
|
+
const topLevel = allCommands.filter(c => !c.path.includes(' '));
|
|
336
308
|
if (topLevel.length > 0) {
|
|
337
309
|
const lines = [''];
|
|
338
|
-
lines.push('User Commands:');
|
|
310
|
+
lines.push(chalk_1.default.bold('User Commands:'));
|
|
339
311
|
for (const cmd of topLevel) {
|
|
340
|
-
|
|
341
|
-
lines.push(` ${cmd.path.padEnd(27)}${cmd.description}`);
|
|
312
|
+
lines.push(` ${chalk_1.default.yellow(cmd.path.padEnd(27))}${cmd.description}`);
|
|
342
313
|
}
|
|
343
314
|
lines.push('');
|
|
344
315
|
program.addHelpText('after', lines.join('\n'));
|
|
345
316
|
}
|
|
346
317
|
}
|
|
347
318
|
catch {
|
|
348
|
-
// Ignore errors during help generation
|
|
319
|
+
// Ignore errors during help generation
|
|
320
|
+
}
|
|
321
|
+
// Process arguments
|
|
322
|
+
const args = process.argv.slice(2);
|
|
323
|
+
// If no args, show help
|
|
324
|
+
if (args.length === 0) {
|
|
325
|
+
program.help();
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
// Special case: if it starts with 'ctl', or is '--help', '--version', '-h', etc.,
|
|
329
|
+
// let commander handle it natively.
|
|
330
|
+
const firstArg = args[0];
|
|
331
|
+
const isStandardCommand = ['ctl', '--help', '-h', '--version', '-V'].includes(firstArg) || firstArg.startsWith('-');
|
|
332
|
+
if (isStandardCommand) {
|
|
333
|
+
program.parse(process.argv);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
// It's a dynamic command
|
|
337
|
+
await handleDynamicCommand(args);
|
|
349
338
|
}
|
|
350
|
-
program.parse(process.argv);
|
|
351
339
|
})();
|