@michaelhartmayer/agentctl 1.0.2 → 1.1.0
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 +268 -303
- package/dist/index.js +111 -73
- package/dist/manifest.js +7 -18
- package/dist/package.json +60 -0
- package/dist/resolve.js +87 -98
- package/dist/skills.js +28 -39
- package/dist/src/ctl.js +316 -0
- package/dist/src/fs-utils.js +35 -0
- package/dist/src/index.js +351 -0
- package/dist/src/manifest.js +19 -0
- package/dist/src/resolve.js +112 -0
- package/dist/src/skills.js +39 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
6
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
7
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
8
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
9
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
|
-
});
|
|
11
|
-
};
|
|
12
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
5
|
};
|
|
@@ -21,14 +12,15 @@ const resolve_1 = require("./resolve");
|
|
|
21
12
|
const child_process_1 = require("child_process");
|
|
22
13
|
const chalk_1 = __importDefault(require("chalk"));
|
|
23
14
|
const program = new commander_1.Command();
|
|
15
|
+
const pkg = require('../package.json');
|
|
24
16
|
program
|
|
25
17
|
.name('agentctl')
|
|
26
18
|
.description('Agent Controller CLI - Unified control plane for humans and agents')
|
|
27
|
-
.version(
|
|
19
|
+
.version(pkg.version)
|
|
28
20
|
.allowUnknownOption()
|
|
29
21
|
.helpOption(false) // Disable default help to allow pass-through
|
|
30
22
|
.argument('[command...]', 'Command to run')
|
|
31
|
-
.action((args, _options, _command) =>
|
|
23
|
+
.action(async (args, _options, _command) => {
|
|
32
24
|
// If no args, check for help flag or just show help
|
|
33
25
|
if (!args || args.length === 0) {
|
|
34
26
|
// If they passed --help or -h, show help. If no args at all, show help.
|
|
@@ -64,7 +56,7 @@ program
|
|
|
64
56
|
// No, flags usually come after. resolveCommand stops at first non-matching path part?
|
|
65
57
|
// resolveCommand logic: iterates args.
|
|
66
58
|
// "dev-tools gh --help" -> path "dev-tools gh", remaining "--help"
|
|
67
|
-
const result =
|
|
59
|
+
const result = await (0, resolve_1.resolveCommand)(args);
|
|
68
60
|
if (!result) {
|
|
69
61
|
// If not found, and they asked for help, show root help?
|
|
70
62
|
// Or if they just typed a wrong command.
|
|
@@ -95,7 +87,7 @@ program
|
|
|
95
87
|
cwd: process.cwd(), // Execute in CWD as discussed
|
|
96
88
|
shell: true,
|
|
97
89
|
stdio: 'inherit',
|
|
98
|
-
env:
|
|
90
|
+
env: { ...process.env, AGENTCTL_SCOPE: scope }
|
|
99
91
|
});
|
|
100
92
|
child.on('exit', (code) => {
|
|
101
93
|
process.exit(code || 0);
|
|
@@ -106,7 +98,7 @@ program
|
|
|
106
98
|
console.log(chalk_1.default.blue(chalk_1.default.bold(`${manifest.name}`)));
|
|
107
99
|
console.log(manifest.description || 'No description');
|
|
108
100
|
console.log('\nSubcommands:');
|
|
109
|
-
const all =
|
|
101
|
+
const all = await (0, ctl_1.list)();
|
|
110
102
|
const prefix = result.cmdPath + ' ';
|
|
111
103
|
// Filter logic roughly for direct children
|
|
112
104
|
const depth = result.cmdPath.split(' ').length;
|
|
@@ -129,17 +121,17 @@ program
|
|
|
129
121
|
}
|
|
130
122
|
process.exit(1);
|
|
131
123
|
}
|
|
132
|
-
})
|
|
124
|
+
});
|
|
133
125
|
const ctl = program.command('ctl')
|
|
134
|
-
.description('Agent Controller Management - Create,
|
|
126
|
+
.description('Agent Controller Management - Create, organize, and manage commands');
|
|
135
127
|
// --- Lifecycle Commands ---
|
|
136
128
|
// We'll stick to flat list but with good descriptions.
|
|
137
129
|
// Helper for consistent error handling
|
|
138
130
|
// Helper for consistent error handling
|
|
139
131
|
const withErrorHandling = (fn) => {
|
|
140
|
-
return (...args) =>
|
|
132
|
+
return async (...args) => {
|
|
141
133
|
try {
|
|
142
|
-
|
|
134
|
+
await fn(...args);
|
|
143
135
|
}
|
|
144
136
|
catch (e) {
|
|
145
137
|
if (e instanceof Error) {
|
|
@@ -150,87 +142,125 @@ const withErrorHandling = (fn) => {
|
|
|
150
142
|
}
|
|
151
143
|
process.exit(1);
|
|
152
144
|
}
|
|
153
|
-
}
|
|
145
|
+
};
|
|
154
146
|
};
|
|
155
147
|
ctl.command('scaffold')
|
|
156
|
-
.description('
|
|
157
|
-
.argument('
|
|
148
|
+
.description('Scaffold a new command script (creates a manifest and a .sh/.cmd file)')
|
|
149
|
+
.argument('[path...]', 'The hierarchical path for the new command (e.g. "dev start")')
|
|
158
150
|
.addHelpText('after', `
|
|
151
|
+
Description:
|
|
152
|
+
Scaffolding creates a new directory for your command containing a 'manifest.json'
|
|
153
|
+
and a boilerplate script file (.sh on Linux/Mac, .cmd on Windows).
|
|
154
|
+
You can then edit the script to add your own logic.
|
|
155
|
+
|
|
159
156
|
Examples:
|
|
160
157
|
$ agentctl ctl scaffold dev start
|
|
161
|
-
$ agentctl ctl scaffold
|
|
158
|
+
$ agentctl ctl scaffold utils/backup
|
|
162
159
|
`)
|
|
163
|
-
.action(withErrorHandling((pathParts
|
|
164
|
-
|
|
165
|
-
|
|
160
|
+
.action(withErrorHandling(async (pathParts, _options, command) => {
|
|
161
|
+
if (!pathParts || pathParts.length === 0) {
|
|
162
|
+
command.help();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
await (0, ctl_1.scaffold)(pathParts);
|
|
166
|
+
}));
|
|
166
167
|
ctl.command('alias')
|
|
167
|
-
.description('Create
|
|
168
|
-
.argument('
|
|
169
|
-
.action(withErrorHandling((args
|
|
170
|
-
if (args.length < 2) {
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
.description('Create an alias command that executes a shell string')
|
|
169
|
+
.argument('[path_and_cmd...]', 'Hierarchical path segments followed by the shell command')
|
|
170
|
+
.action(withErrorHandling(async (args, _options, command) => {
|
|
171
|
+
if (!args || args.length < 2) {
|
|
172
|
+
command.help();
|
|
173
|
+
return;
|
|
173
174
|
}
|
|
174
175
|
const target = args.pop();
|
|
175
176
|
const name = args;
|
|
176
|
-
|
|
177
|
-
}))
|
|
177
|
+
await (0, ctl_1.alias)(name, target);
|
|
178
|
+
}))
|
|
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
|
+
|
|
179
186
|
Examples:
|
|
180
|
-
$ agentctl ctl alias tools
|
|
187
|
+
$ agentctl ctl alias tools git-status "git status"
|
|
188
|
+
-> Creates 'agentctl tools git-status' which runs 'git status'.
|
|
189
|
+
|
|
181
190
|
$ agentctl ctl alias dev build "npm run build"
|
|
191
|
+
-> Creates 'agentctl dev build' which runs 'npm run build'.
|
|
182
192
|
`);
|
|
183
193
|
ctl.command('group')
|
|
184
|
-
.description('Create a
|
|
185
|
-
.argument('
|
|
194
|
+
.description('Create a command group (namespace) to organize related commands')
|
|
195
|
+
.argument('[path...]', 'Hierarchical path for the group (e.g. "dev")')
|
|
186
196
|
.addHelpText('after', `
|
|
197
|
+
Description:
|
|
198
|
+
Groups are essentially folders that contain other commands.
|
|
199
|
+
They don't execute anything themselves but provide organization.
|
|
200
|
+
|
|
187
201
|
Examples:
|
|
188
202
|
$ agentctl ctl group dev
|
|
189
|
-
$ agentctl ctl group tools
|
|
203
|
+
$ agentctl ctl group tools/internal
|
|
190
204
|
`)
|
|
191
|
-
.action(withErrorHandling((parts
|
|
192
|
-
|
|
193
|
-
|
|
205
|
+
.action(withErrorHandling(async (parts, _options, command) => {
|
|
206
|
+
if (!parts || parts.length === 0) {
|
|
207
|
+
command.help();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
await (0, ctl_1.group)(parts);
|
|
211
|
+
}));
|
|
194
212
|
ctl.command('rm')
|
|
195
213
|
.description('Remove a command or group permanently')
|
|
196
|
-
.argument('
|
|
214
|
+
.argument('[path...]', 'Command path to remove')
|
|
197
215
|
.option('--global', 'Remove from global scope')
|
|
198
216
|
.addHelpText('after', `
|
|
199
217
|
Examples:
|
|
200
218
|
$ agentctl ctl rm dev start
|
|
201
219
|
$ agentctl ctl rm tools --global
|
|
202
220
|
`)
|
|
203
|
-
.action(withErrorHandling((parts, opts) =>
|
|
204
|
-
|
|
205
|
-
|
|
221
|
+
.action(withErrorHandling(async (parts, opts, command) => {
|
|
222
|
+
if (!parts || parts.length === 0) {
|
|
223
|
+
command.help();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
await (0, ctl_1.rm)(parts, { global: opts.global });
|
|
227
|
+
}));
|
|
206
228
|
ctl.command('mv')
|
|
207
|
-
.description('Move a command or group
|
|
208
|
-
.argument('
|
|
209
|
-
.argument('
|
|
210
|
-
.option('--global', '
|
|
229
|
+
.description('Move or rename a command or group')
|
|
230
|
+
.argument('[src]', 'Current path of the command')
|
|
231
|
+
.argument('[dest]', 'New path for the command')
|
|
232
|
+
.option('--global', 'Perform operation in global scope')
|
|
211
233
|
.addHelpText('after', `
|
|
212
234
|
Examples:
|
|
213
235
|
$ agentctl ctl mv "dev start" "dev boot"
|
|
214
236
|
$ agentctl ctl mv tools/gh tools/github --global
|
|
215
237
|
`)
|
|
216
|
-
.action(withErrorHandling((src, dest, opts) =>
|
|
217
|
-
|
|
218
|
-
|
|
238
|
+
.action(withErrorHandling(async (src, dest, opts, command) => {
|
|
239
|
+
if (!src || !dest) {
|
|
240
|
+
command.help();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
await (0, ctl_1.mv)(src.split(' '), dest.split(' '), { global: opts.global });
|
|
244
|
+
}));
|
|
219
245
|
// --- Introspection ---
|
|
220
246
|
ctl.command('list')
|
|
221
247
|
.description('List all available commands across local and global scopes')
|
|
222
|
-
.action(withErrorHandling(() =>
|
|
223
|
-
const items =
|
|
248
|
+
.action(withErrorHandling(async () => {
|
|
249
|
+
const items = await (0, ctl_1.list)();
|
|
224
250
|
console.log('TYPE SCOPE COMMAND DESCRIPTION');
|
|
225
251
|
for (const item of items) {
|
|
226
252
|
console.log(`${item.type.padEnd(9)} ${item.scope.padEnd(9)} ${item.path.padEnd(19)} ${item.description}`);
|
|
227
253
|
}
|
|
228
|
-
}))
|
|
254
|
+
}));
|
|
229
255
|
ctl.command('inspect')
|
|
230
256
|
.description('Inspect the internal manifest and details of a command')
|
|
231
|
-
.argument('
|
|
232
|
-
.action(withErrorHandling((parts
|
|
233
|
-
|
|
257
|
+
.argument('[path...]', 'Command path to inspect')
|
|
258
|
+
.action(withErrorHandling(async (parts, _options, command) => {
|
|
259
|
+
if (!parts || parts.length === 0) {
|
|
260
|
+
command.help();
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const info = await (0, ctl_1.inspect)(parts);
|
|
234
264
|
if (info) {
|
|
235
265
|
console.log(JSON.stringify(info, null, 2));
|
|
236
266
|
}
|
|
@@ -238,11 +268,11 @@ ctl.command('inspect')
|
|
|
238
268
|
console.error('Command not found');
|
|
239
269
|
process.exit(1);
|
|
240
270
|
}
|
|
241
|
-
}))
|
|
271
|
+
}));
|
|
242
272
|
// --- Scoping ---
|
|
243
273
|
ctl.command('global')
|
|
244
274
|
.description('Push a local command to the global scope')
|
|
245
|
-
.argument('
|
|
275
|
+
.argument('[path...]', 'Local command path')
|
|
246
276
|
.option('--move', 'Move instead of copy')
|
|
247
277
|
.option('--copy', 'Copy (default)')
|
|
248
278
|
.addHelpText('after', `
|
|
@@ -250,21 +280,29 @@ Examples:
|
|
|
250
280
|
$ agentctl ctl global sys --move
|
|
251
281
|
$ agentctl ctl global tools --copy
|
|
252
282
|
`)
|
|
253
|
-
.action(withErrorHandling((parts, opts) =>
|
|
254
|
-
|
|
255
|
-
|
|
283
|
+
.action(withErrorHandling(async (parts, opts, command) => {
|
|
284
|
+
if (!parts || parts.length === 0) {
|
|
285
|
+
command.help();
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
await (0, ctl_1.pushGlobal)(parts, { move: opts.move, copy: opts.copy || !opts.move });
|
|
289
|
+
}));
|
|
256
290
|
ctl.command('local')
|
|
257
291
|
.description('Pull a global command to the local scope')
|
|
258
|
-
.argument('
|
|
292
|
+
.argument('[path...]', 'Global command path')
|
|
259
293
|
.option('--move', 'Move instead of copy')
|
|
260
294
|
.option('--copy', 'Copy (default)')
|
|
261
295
|
.addHelpText('after', `
|
|
262
296
|
Examples:
|
|
263
297
|
$ agentctl ctl local tools --copy
|
|
264
298
|
`)
|
|
265
|
-
.action(withErrorHandling((parts, opts) =>
|
|
266
|
-
|
|
267
|
-
|
|
299
|
+
.action(withErrorHandling(async (parts, opts, command) => {
|
|
300
|
+
if (!parts || parts.length === 0) {
|
|
301
|
+
command.help();
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
await (0, ctl_1.pullLocal)(parts, { move: opts.move, copy: opts.copy || !opts.move });
|
|
305
|
+
}));
|
|
268
306
|
// --- Agent Integration ---
|
|
269
307
|
// We attach this to the root `ctl` as options or a sub-command?
|
|
270
308
|
// Original code had it as options on `ctl`. We can make it a command for better help.
|
|
@@ -277,10 +315,10 @@ Examples:
|
|
|
277
315
|
$ agentctl ctl --install-skill antigravity --global
|
|
278
316
|
$ agentctl ctl --install-skill gemini
|
|
279
317
|
`)
|
|
280
|
-
.action(withErrorHandling((op, command) =>
|
|
318
|
+
.action(withErrorHandling(async (op, command) => {
|
|
281
319
|
const opts = ctl.opts();
|
|
282
320
|
if (opts.installSkill) {
|
|
283
|
-
|
|
321
|
+
await (0, ctl_1.installSkill)(opts.installSkill, { global: opts.global });
|
|
284
322
|
}
|
|
285
323
|
else {
|
|
286
324
|
// If no subcmd and no option, show help
|
|
@@ -288,12 +326,12 @@ Examples:
|
|
|
288
326
|
ctl.help();
|
|
289
327
|
}
|
|
290
328
|
}
|
|
291
|
-
}))
|
|
329
|
+
}));
|
|
292
330
|
// Inject dynamic commands into root help
|
|
293
331
|
// We need to do this before parsing
|
|
294
|
-
(() =>
|
|
332
|
+
(async () => {
|
|
295
333
|
try {
|
|
296
|
-
const allCommands =
|
|
334
|
+
const allCommands = await (0, ctl_1.list)();
|
|
297
335
|
const topLevel = allCommands.filter(c => !c.path.includes(' ')); // Only top level
|
|
298
336
|
if (topLevel.length > 0) {
|
|
299
337
|
const lines = [''];
|
|
@@ -306,8 +344,8 @@ Examples:
|
|
|
306
344
|
program.addHelpText('after', lines.join('\n'));
|
|
307
345
|
}
|
|
308
346
|
}
|
|
309
|
-
catch
|
|
347
|
+
catch {
|
|
310
348
|
// Ignore errors during help generation (e.g. if not initialized)
|
|
311
349
|
}
|
|
312
350
|
program.parse(process.argv);
|
|
313
|
-
})
|
|
351
|
+
})();
|
package/dist/manifest.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
@@ -15,15 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
6
|
exports.readManifest = readManifest;
|
|
16
7
|
exports.isCappedManifest = isCappedManifest;
|
|
17
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
18
|
-
function readManifest(p) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
});
|
|
9
|
+
async function readManifest(p) {
|
|
10
|
+
try {
|
|
11
|
+
return await fs_extra_1.default.readJson(p);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
27
16
|
}
|
|
28
17
|
function isCappedManifest(m) {
|
|
29
18
|
return !!m.run || m.type === 'scaffold' || m.type === 'alias';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@michaelhartmayer/agentctl",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"description": "Agent Controller - A unified interface for humans and AI agents",
|
|
7
|
+
"version": "1.1.0",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"bin": {
|
|
10
|
+
"agentctl": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"skills",
|
|
15
|
+
"scripts",
|
|
16
|
+
"agentctl.cmd",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:watch": "vitest",
|
|
23
|
+
"lint": "eslint src tests",
|
|
24
|
+
"register:path": "node scripts/register-path.js",
|
|
25
|
+
"unregister:path": "node scripts/unregister-path.js",
|
|
26
|
+
"release": "standard-version",
|
|
27
|
+
"prepublishOnly": "npm run build && npm run test",
|
|
28
|
+
"prepare": "husky"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"cli",
|
|
32
|
+
"agent",
|
|
33
|
+
"ai",
|
|
34
|
+
"automation",
|
|
35
|
+
"framework"
|
|
36
|
+
],
|
|
37
|
+
"author": "Michael Hartmayer",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"chalk": "^4.1.2",
|
|
41
|
+
"commander": "^14.0.3",
|
|
42
|
+
"fs-extra": "^11.3.3"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@commitlint/cli": "^20.4.1",
|
|
46
|
+
"@commitlint/config-conventional": "^20.4.1",
|
|
47
|
+
"@types/fs-extra": "^11.0.4",
|
|
48
|
+
"@types/node": "^25.2.3",
|
|
49
|
+
"@typescript-eslint/eslint-plugin": "^8.55.0",
|
|
50
|
+
"@typescript-eslint/parser": "^8.55.0",
|
|
51
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
52
|
+
"eslint": "^8.57.1",
|
|
53
|
+
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
54
|
+
"husky": "^9.1.7",
|
|
55
|
+
"standard-version": "^9.5.0",
|
|
56
|
+
"ts-node": "^10.9.2",
|
|
57
|
+
"typescript": "^5.9.3",
|
|
58
|
+
"vitest": "^4.0.18"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/dist/resolve.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
@@ -17,52 +8,65 @@ const path_1 = __importDefault(require("path"));
|
|
|
17
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
18
9
|
const fs_utils_1 = require("./fs-utils");
|
|
19
10
|
const manifest_1 = require("./manifest");
|
|
20
|
-
function resolveCommand(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
localManifest = yield (0, manifest_1.readManifest)(mPath);
|
|
42
|
-
}
|
|
43
|
-
if (!localManifest && (yield fs_extra_1.default.stat(localPath)).isDirectory()) {
|
|
44
|
-
// Implicit group
|
|
45
|
-
localManifest = { name: args[i], type: 'group' };
|
|
46
|
-
}
|
|
11
|
+
async function resolveCommand(args, options = {}) {
|
|
12
|
+
const cwd = options.cwd || process.cwd();
|
|
13
|
+
const localRoot = !options.global ? (0, fs_utils_1.findLocalRoot)(cwd) : null;
|
|
14
|
+
const globalRoot = options.globalDir || (0, fs_utils_1.getGlobalRoot)();
|
|
15
|
+
const localAgentctl = localRoot ? path_1.default.join(localRoot, '.agentctl') : null;
|
|
16
|
+
let currentMatch = null;
|
|
17
|
+
// Iterate through args to find longest match
|
|
18
|
+
for (let i = 0; i < args.length; i++) {
|
|
19
|
+
// Path corresponding to args[0..i]
|
|
20
|
+
const currentArgs = args.slice(0, i + 1);
|
|
21
|
+
const relPath = currentArgs.join(path_1.default.sep);
|
|
22
|
+
const cmdPath = currentArgs.join(' ');
|
|
23
|
+
const localPath = localAgentctl ? path_1.default.join(localAgentctl, relPath) : null;
|
|
24
|
+
const globalPath = path_1.default.join(globalRoot, relPath);
|
|
25
|
+
let localManifest = null;
|
|
26
|
+
let globalManifest = null;
|
|
27
|
+
// check local
|
|
28
|
+
if (localPath && await fs_extra_1.default.pathExists(localPath)) {
|
|
29
|
+
const mPath = path_1.default.join(localPath, 'manifest.json');
|
|
30
|
+
if (await fs_extra_1.default.pathExists(mPath)) {
|
|
31
|
+
localManifest = await (0, manifest_1.readManifest)(mPath);
|
|
47
32
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (yield fs_extra_1.default.pathExists(mPath)) {
|
|
52
|
-
globalManifest = yield (0, manifest_1.readManifest)(mPath);
|
|
53
|
-
}
|
|
54
|
-
if (!globalManifest && (yield fs_extra_1.default.stat(globalPath)).isDirectory()) {
|
|
55
|
-
globalManifest = { name: args[i], type: 'group' };
|
|
56
|
-
}
|
|
33
|
+
if (!localManifest && (await fs_extra_1.default.stat(localPath)).isDirectory()) {
|
|
34
|
+
// Implicit group
|
|
35
|
+
localManifest = { name: args[i], type: 'group' };
|
|
57
36
|
}
|
|
58
|
-
|
|
59
|
-
|
|
37
|
+
}
|
|
38
|
+
// check global
|
|
39
|
+
if (await fs_extra_1.default.pathExists(globalPath)) {
|
|
40
|
+
const mPath = path_1.default.join(globalPath, 'manifest.json');
|
|
41
|
+
if (await fs_extra_1.default.pathExists(mPath)) {
|
|
42
|
+
globalManifest = await (0, manifest_1.readManifest)(mPath);
|
|
60
43
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
44
|
+
if (!globalManifest && (await fs_extra_1.default.stat(globalPath)).isDirectory()) {
|
|
45
|
+
globalManifest = { name: args[i], type: 'group' };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!localManifest && !globalManifest) {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
const remainingArgs = args.slice(i + 1);
|
|
52
|
+
// Priority logic
|
|
53
|
+
// 1. Local Capped -> Return Match immediately.
|
|
54
|
+
if (localManifest && (0, manifest_1.isCappedManifest)(localManifest)) {
|
|
55
|
+
return {
|
|
56
|
+
manifest: localManifest,
|
|
57
|
+
manifestPath: path_1.default.join(localPath, 'manifest.json'),
|
|
58
|
+
args: remainingArgs,
|
|
59
|
+
scope: 'local',
|
|
60
|
+
cmdPath
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// 2. Global Capped
|
|
64
|
+
if (globalManifest && (0, manifest_1.isCappedManifest)(globalManifest)) {
|
|
65
|
+
// Check if shadowed by Local Group
|
|
66
|
+
if (localManifest) {
|
|
67
|
+
// Local exists (must be group since checked capped above).
|
|
68
|
+
// Shadowed. Treat as Local Group.
|
|
69
|
+
currentMatch = {
|
|
66
70
|
manifest: localManifest,
|
|
67
71
|
manifestPath: path_1.default.join(localPath, 'manifest.json'),
|
|
68
72
|
args: remainingArgs,
|
|
@@ -70,54 +74,39 @@ function resolveCommand(args_1) {
|
|
|
70
74
|
cmdPath
|
|
71
75
|
};
|
|
72
76
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
};
|
|
96
|
-
}
|
|
77
|
+
else {
|
|
78
|
+
// Not shadowed. Global Capped wins. Return immediately.
|
|
79
|
+
return {
|
|
80
|
+
manifest: globalManifest,
|
|
81
|
+
manifestPath: path_1.default.join(globalPath, 'manifest.json'),
|
|
82
|
+
args: remainingArgs,
|
|
83
|
+
scope: 'global',
|
|
84
|
+
cmdPath
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// Neither is capped. Both are groups (or one is).
|
|
90
|
+
// Local wins if exists.
|
|
91
|
+
if (localManifest) {
|
|
92
|
+
currentMatch = {
|
|
93
|
+
manifest: localManifest,
|
|
94
|
+
manifestPath: path_1.default.join(localPath, 'manifest.json'),
|
|
95
|
+
args: remainingArgs,
|
|
96
|
+
scope: 'local',
|
|
97
|
+
cmdPath
|
|
98
|
+
};
|
|
97
99
|
}
|
|
98
100
|
else {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
scope: 'local',
|
|
107
|
-
cmdPath
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
currentMatch = {
|
|
112
|
-
manifest: globalManifest,
|
|
113
|
-
manifestPath: path_1.default.join(globalPath, 'manifest.json'),
|
|
114
|
-
args: remainingArgs,
|
|
115
|
-
scope: 'global',
|
|
116
|
-
cmdPath
|
|
117
|
-
};
|
|
118
|
-
}
|
|
101
|
+
currentMatch = {
|
|
102
|
+
manifest: globalManifest,
|
|
103
|
+
manifestPath: path_1.default.join(globalPath, 'manifest.json'),
|
|
104
|
+
args: remainingArgs,
|
|
105
|
+
scope: 'global',
|
|
106
|
+
cmdPath
|
|
107
|
+
};
|
|
119
108
|
}
|
|
120
109
|
}
|
|
121
|
-
|
|
122
|
-
|
|
110
|
+
}
|
|
111
|
+
return currentMatch;
|
|
123
112
|
}
|