@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/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('1.0.0')
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) => __awaiter(void 0, void 0, void 0, function* () {
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 = yield (0, resolve_1.resolveCommand)(args);
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: Object.assign(Object.assign({}, process.env), { AGENTCTL_SCOPE: scope })
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 = yield (0, ctl_1.list)();
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, organizing, and managing commands');
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) => __awaiter(void 0, void 0, void 0, function* () {
132
+ return async (...args) => {
141
133
  try {
142
- yield fn(...args);
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('Create a new capped command with a script file')
157
- .argument('<path...>', 'Command path segments (e.g., "dev start")')
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 sys backup
158
+ $ agentctl ctl scaffold utils/backup
162
159
  `)
163
- .action(withErrorHandling((pathParts) => __awaiter(void 0, void 0, void 0, function* () {
164
- yield (0, ctl_1.scaffold)(pathParts);
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 a new capped command that runs an inline shell command')
168
- .argument('<args...>', 'Name parts followed by target (e.g., "tools" "gh" "gh")')
169
- .action(withErrorHandling((args) => __awaiter(void 0, void 0, void 0, function* () {
170
- if (args.length < 2) {
171
- console.error('Usage: ctl alias <name...> <target>');
172
- process.exit(1);
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
- yield (0, ctl_1.alias)(name, target);
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 gh "gh"
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 new command group (namespace)')
185
- .argument('<path...>', 'Group path (e.g., "dev")')
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) => __awaiter(void 0, void 0, void 0, function* () {
192
- yield (0, ctl_1.group)(parts);
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('<path...>', 'Command path to remove')
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) => __awaiter(void 0, void 0, void 0, function* () {
204
- yield (0, ctl_1.rm)(parts, { global: opts.global });
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 to a new path')
208
- .argument('<src>', 'Source path (quoted string or single token)')
209
- .argument('<dest>', 'Destination path')
210
- .option('--global', 'Move command in global scope')
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) => __awaiter(void 0, void 0, void 0, function* () {
217
- yield (0, ctl_1.mv)(src.split(' '), dest.split(' '), { global: opts.global });
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(() => __awaiter(void 0, void 0, void 0, function* () {
223
- const items = yield (0, ctl_1.list)();
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('<path...>', 'Command path to inspect')
232
- .action(withErrorHandling((parts) => __awaiter(void 0, void 0, void 0, function* () {
233
- const info = yield (0, ctl_1.inspect)(parts);
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('<path...>', 'Local command path')
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) => __awaiter(void 0, void 0, void 0, function* () {
254
- yield (0, ctl_1.pushGlobal)(parts, { move: opts.move, copy: opts.copy || !opts.move });
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('<path...>', 'Global command path')
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) => __awaiter(void 0, void 0, void 0, function* () {
266
- yield (0, ctl_1.pullLocal)(parts, { move: opts.move, copy: opts.copy || !opts.move });
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) => __awaiter(void 0, void 0, void 0, function* () {
318
+ .action(withErrorHandling(async (op, command) => {
281
319
  const opts = ctl.opts();
282
320
  if (opts.installSkill) {
283
- yield (0, ctl_1.installSkill)(opts.installSkill, { global: opts.global });
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
- (() => __awaiter(void 0, void 0, void 0, function* () {
332
+ (async () => {
295
333
  try {
296
- const allCommands = yield (0, ctl_1.list)();
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 (_a) {
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
- return __awaiter(this, void 0, void 0, function* () {
20
- try {
21
- return yield fs_extra_1.default.readJson(p);
22
- }
23
- catch (_a) {
24
- return null;
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(args_1) {
21
- return __awaiter(this, arguments, void 0, function* (args, options = {}) {
22
- const cwd = options.cwd || process.cwd();
23
- const localRoot = !options.global ? (0, fs_utils_1.findLocalRoot)(cwd) : null;
24
- const globalRoot = options.globalDir || (0, fs_utils_1.getGlobalRoot)();
25
- const localAgentctl = localRoot ? path_1.default.join(localRoot, '.agentctl') : null;
26
- let currentMatch = null;
27
- // Iterate through args to find longest match
28
- for (let i = 0; i < args.length; i++) {
29
- // Path corresponding to args[0..i]
30
- const currentArgs = args.slice(0, i + 1);
31
- const relPath = currentArgs.join(path_1.default.sep);
32
- const cmdPath = currentArgs.join(' ');
33
- const localPath = localAgentctl ? path_1.default.join(localAgentctl, relPath) : null;
34
- const globalPath = path_1.default.join(globalRoot, relPath);
35
- let localManifest = null;
36
- let globalManifest = null;
37
- // check local
38
- if (localPath && (yield fs_extra_1.default.pathExists(localPath))) {
39
- const mPath = path_1.default.join(localPath, 'manifest.json');
40
- if (yield fs_extra_1.default.pathExists(mPath)) {
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
- // check global
49
- if (yield fs_extra_1.default.pathExists(globalPath)) {
50
- const mPath = path_1.default.join(globalPath, 'manifest.json');
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
- if (!localManifest && !globalManifest) {
59
- break;
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
- const remainingArgs = args.slice(i + 1);
62
- // Priority logic
63
- // 1. Local Capped -> Return Match immediately.
64
- if (localManifest && (0, manifest_1.isCappedManifest)(localManifest)) {
65
- return {
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
- // 2. Global Capped
74
- if (globalManifest && (0, manifest_1.isCappedManifest)(globalManifest)) {
75
- // Check if shadowed by Local Group
76
- if (localManifest) {
77
- // Local exists (must be group since checked capped above).
78
- // Shadowed. Treat as Local Group.
79
- currentMatch = {
80
- manifest: localManifest,
81
- manifestPath: path_1.default.join(localPath, 'manifest.json'),
82
- args: remainingArgs,
83
- scope: 'local',
84
- cmdPath
85
- };
86
- }
87
- else {
88
- // Not shadowed. Global Capped wins. Return immediately.
89
- return {
90
- manifest: globalManifest,
91
- manifestPath: path_1.default.join(globalPath, 'manifest.json'),
92
- args: remainingArgs,
93
- scope: 'global',
94
- cmdPath
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
- // Neither is capped. Both are groups (or one is).
100
- // Local wins if exists.
101
- if (localManifest) {
102
- currentMatch = {
103
- manifest: localManifest,
104
- manifestPath: path_1.default.join(localPath, 'manifest.json'),
105
- args: remainingArgs,
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
- return currentMatch;
122
- });
110
+ }
111
+ return currentMatch;
123
112
  }