@actiondock/cli 2.0.9 → 2.0.10

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.
@@ -5,13 +5,14 @@ import {
5
5
  findProjectRoot,
6
6
  getRegistryStatus,
7
7
  listLinkedPackages,
8
- loadActions,
8
+ loadManifest,
9
9
  loadPlaybooks,
10
10
  loadProjectConfig,
11
11
  resolvePackageRoot,
12
12
  resolveTarget,
13
13
  } from "@actiondock/core";
14
14
  import { Command } from "commander";
15
+ import { ArgumentError, CliError, ExecutionError, getEffectiveOptions, renderResult } from "@actiondock/runtime-cli";
15
16
  import { resolveIntent } from "../utils/filter";
16
17
 
17
18
  interface AggregatedPackage {
@@ -29,7 +30,16 @@ interface AggregatedPackage {
29
30
 
30
31
  async function getProjectDetailInfo(root: string) {
31
32
  const config = loadProjectConfig(root);
32
- const actions = await loadActions(root, config.actionsDir);
33
+ const manifest = loadManifest(root);
34
+ const actionsMap = new Map<string, { id: string; description?: string }>();
35
+ if (manifest?.actions) {
36
+ for (const [id, item] of Object.entries(manifest.actions)) {
37
+ actionsMap.set(id, {
38
+ id,
39
+ description: item.description,
40
+ });
41
+ }
42
+ }
33
43
  const playbooks = loadPlaybooks(root, config.playbooksDir);
34
44
 
35
45
  return {
@@ -40,13 +50,13 @@ async function getProjectDetailInfo(root: string) {
40
50
  projectRoot: root,
41
51
  actionsDir: config.actionsDir || "actions",
42
52
  playbooksDir: config.playbooksDir || "playbooks",
43
- actionsCount: actions.size,
53
+ actionsCount: actionsMap.size,
44
54
  playbooksCount: playbooks.size,
45
- actions: Array.from(actions.keys()),
55
+ actions: Array.from(actionsMap.keys()),
46
56
  playbooks: Array.from(playbooks.keys()),
47
57
  configDeclared: config.config ? Object.keys(config.config) : [],
48
58
  configDef: config.config,
49
- actionsMap: actions,
59
+ actionsMap,
50
60
  playbooksMap: playbooks,
51
61
  };
52
62
  }
@@ -184,15 +194,21 @@ export function registerInfoCommand(program: Command): void {
184
194
  .option("-p, --profile <name>", "Query against a specific profile")
185
195
  .option("-s, --server <url>", "Remote server URL")
186
196
  .option("-t, --token <token>", "Auth token for remote server")
197
+ .option("--fallback", "Enable fallback to full list when no items match intent")
187
198
  .option(
188
199
  "--no-fallback",
189
200
  "Disable fallback to full list when no items match intent"
190
201
  )
191
202
  .option("--json", "Output information as JSON")
192
- .action(async (patterns: string[] = [], options: any) => {
203
+ .option("--envelope", "Wrap JSON output in standard envelope")
204
+ .option("--data-dir <path>", "Custom database storage directory")
205
+ .action(async (patterns: string[] = [], rawOptions: any, cmd: any) => {
193
206
  try {
207
+ const options = getEffectiveOptions(rawOptions, cmd);
194
208
  const effectiveIntent = resolveIntent(options.intent, patterns);
195
- const shouldFallback = options.fallback !== false;
209
+ const isMachine = Boolean(options.json || options.envelope);
210
+ const fallbackExplicit = options.fallback === true || (Array.isArray(process.argv) && process.argv.includes("--fallback"));
211
+ const shouldFallback = isMachine ? fallbackExplicit : options.fallback !== false;
196
212
 
197
213
  const target = resolveTarget({
198
214
  profile: options.profile,
@@ -212,8 +228,8 @@ export function registerInfoCommand(program: Command): void {
212
228
  }
213
229
  );
214
230
 
215
- if (options.json) {
216
- console.log(JSON.stringify(remoteInfo, null, 2));
231
+ if (isMachine) {
232
+ renderResult(remoteInfo, { json: options.json, envelope: options.envelope });
217
233
  return;
218
234
  }
219
235
 
@@ -304,8 +320,8 @@ export function registerInfoCommand(program: Command): void {
304
320
  // Local Tree
305
321
  if (options.tree) {
306
322
  const status = getRegistryStatus();
307
- if (options.json) {
308
- console.log(JSON.stringify(status, null, 2));
323
+ if (isMachine) {
324
+ renderResult(status, { json: options.json, envelope: options.envelope });
309
325
  } else {
310
326
  printRegistryTree(status);
311
327
  }
@@ -317,17 +333,16 @@ export function registerInfoCommand(program: Command): void {
317
333
  const directRoot = resolvePackageRoot(options.package);
318
334
  if (directRoot) {
319
335
  const detail = await getProjectDetailInfo(directRoot);
320
- if (options.json) {
321
- console.log(JSON.stringify(projectDetailToJson(detail), null, 2));
336
+ if (isMachine) {
337
+ renderResult(projectDetailToJson(detail), { json: options.json, envelope: options.envelope });
322
338
  } else {
323
339
  printProjectDetail(detail);
324
340
  }
325
341
  return;
326
342
  }
327
- console.error(
328
- `Error: Package '${options.package}' not found in linked packages or path`
343
+ throw new ArgumentError(
344
+ `Package '${options.package}' not found in linked packages or path`
329
345
  );
330
- process.exit(1);
331
346
  }
332
347
 
333
348
  // 3. Collect local candidate packages (current project + linked packages)
@@ -339,7 +354,8 @@ export function registerInfoCommand(program: Command): void {
339
354
  if (currentRoot) {
340
355
  try {
341
356
  const config = loadProjectConfig(currentRoot);
342
- const actions = await loadActions(currentRoot, config.actionsDir);
357
+ const manifest = loadManifest(currentRoot);
358
+ const manifestActionIds = manifest?.actions ? Object.keys(manifest.actions) : [];
343
359
  const playbooks = loadPlaybooks(currentRoot, config.playbooksDir);
344
360
  aggregated.push({
345
361
  id: config.id,
@@ -347,9 +363,9 @@ export function registerInfoCommand(program: Command): void {
347
363
  version: config.version,
348
364
  description: config.description,
349
365
  path: currentRoot,
350
- actionsCount: actions.size,
366
+ actionsCount: manifestActionIds.length,
351
367
  playbooksCount: playbooks.size,
352
- actions: Array.from(actions.keys()),
368
+ actions: manifestActionIds,
353
369
  playbooks: Array.from(playbooks.keys()),
354
370
  configDeclared: config.config ? Object.keys(config.config) : [],
355
371
  });
@@ -364,7 +380,8 @@ export function registerInfoCommand(program: Command): void {
364
380
  if (seenPaths.has(pkg.path)) continue;
365
381
  try {
366
382
  const config = loadProjectConfig(pkg.path);
367
- const actions = await loadActions(pkg.path, config.actionsDir);
383
+ const manifest = loadManifest(pkg.path);
384
+ const manifestActionIds = manifest?.actions ? Object.keys(manifest.actions) : [];
368
385
  const playbooks = loadPlaybooks(pkg.path, config.playbooksDir);
369
386
 
370
387
  aggregated.push({
@@ -373,9 +390,9 @@ export function registerInfoCommand(program: Command): void {
373
390
  version: config.version,
374
391
  description: config.description,
375
392
  path: pkg.path,
376
- actionsCount: actions.size,
393
+ actionsCount: manifestActionIds.length,
377
394
  playbooksCount: playbooks.size,
378
- actions: Array.from(actions.keys()),
395
+ actions: manifestActionIds,
379
396
  playbooks: Array.from(playbooks.keys()),
380
397
  configDeclared: config.config ? Object.keys(config.config) : [],
381
398
  });
@@ -389,8 +406,8 @@ export function registerInfoCommand(program: Command): void {
389
406
  if (!effectiveIntent) {
390
407
  if (currentRoot) {
391
408
  const detail = await getProjectDetailInfo(currentRoot);
392
- if (options.json) {
393
- console.log(JSON.stringify(projectDetailToJson(detail), null, 2));
409
+ if (isMachine) {
410
+ renderResult(projectDetailToJson(detail), { json: options.json, envelope: options.envelope });
394
411
  } else {
395
412
  printProjectDetail(detail);
396
413
  }
@@ -398,19 +415,21 @@ export function registerInfoCommand(program: Command): void {
398
415
  }
399
416
 
400
417
  if (aggregated.length === 0) {
418
+ if (isMachine) {
419
+ renderResult({ linkedPackages: [] }, { json: options.json, envelope: options.envelope });
420
+ return;
421
+ }
401
422
  console.log(
402
423
  "No ActionDock project in current directory, and no packages linked."
403
424
  );
404
425
  console.log(
405
- "Run 'ad link' inside an Action package to register it."
426
+ "Run 'ad link' inside an Action package or specify a target package."
406
427
  );
407
428
  return;
408
429
  }
409
430
 
410
- if (options.json) {
411
- console.log(
412
- JSON.stringify({ linkedPackages: aggregated }, null, 2)
413
- );
431
+ if (isMachine) {
432
+ renderResult({ linkedPackages: aggregated }, { json: options.json, envelope: options.envelope });
414
433
  } else {
415
434
  printAggregatedPackages(aggregated);
416
435
  }
@@ -423,8 +442,8 @@ export function registerInfoCommand(program: Command): void {
423
442
  const directRoot = resolvePackageRoot(patterns[0]);
424
443
  if (directRoot) {
425
444
  const detail = await getProjectDetailInfo(directRoot);
426
- if (options.json) {
427
- console.log(JSON.stringify(projectDetailToJson(detail), null, 2));
445
+ if (isMachine) {
446
+ renderResult(projectDetailToJson(detail), { json: options.json, envelope: options.envelope });
428
447
  } else {
429
448
  printProjectDetail(detail);
430
449
  }
@@ -433,10 +452,13 @@ export function registerInfoCommand(program: Command): void {
433
452
  }
434
453
 
435
454
  if (aggregated.length === 0) {
436
- console.error(
437
- `Error: No ActionDock project or linked packages available to match '${effectiveIntent}'`
455
+ if (isMachine) {
456
+ renderResult({ linkedPackages: [], matchedCount: 0, isFallback: false }, { json: options.json, envelope: options.envelope });
457
+ return;
458
+ }
459
+ throw new ExecutionError(
460
+ `No ActionDock project or linked packages available to match '${effectiveIntent}'`
438
461
  );
439
- process.exit(1);
440
462
  }
441
463
 
442
464
  // 5.2 Intent filtering across candidate packages
@@ -456,61 +478,58 @@ export function registerInfoCommand(program: Command): void {
456
478
 
457
479
  // Subcase A: 0 items matched
458
480
  if (filterRes.matchedCount === 0) {
459
- if (!shouldFallback) {
460
- if (options.json) {
461
- console.log(JSON.stringify({ linkedPackages: [] }, null, 2));
462
- } else {
463
- console.error(
464
- `Error: No packages matched intent '${effectiveIntent}'`
465
- );
481
+ if (!filterRes.isFallback) {
482
+ if (isMachine) {
483
+ renderResult({ linkedPackages: [], matchedCount: 0, isFallback: false }, { json: options.json, envelope: options.envelope });
484
+ return;
466
485
  }
467
- process.exit(1);
486
+ throw new ExecutionError(
487
+ `No packages matched intent '${effectiveIntent}'`
488
+ );
468
489
  }
469
490
 
470
491
  // Fallback enabled: display all packages with a notice
471
- if (options.json) {
472
- console.log(
473
- JSON.stringify(
474
- { linkedPackages: filterRes.items, isFallback: true },
475
- null,
476
- 2
477
- )
492
+ if (isMachine) {
493
+ renderResult(
494
+ { linkedPackages: filterRes.items, isFallback: true, matchedCount: 0 },
495
+ { json: options.json, envelope: options.envelope }
478
496
  );
479
- } else {
480
- console.log(
481
- `(No linked packages matched intent '${effectiveIntent}', showing all packages)\n`
482
- );
483
- printAggregatedPackages(filterRes.items);
497
+ return;
484
498
  }
499
+ console.log(
500
+ `(No linked packages matched intent '${effectiveIntent}', showing all packages)\n`
501
+ );
502
+ printAggregatedPackages(filterRes.items);
485
503
  return;
486
504
  }
487
505
 
488
- // Subcase B: Exactly 1 item matched -> Display its full project detail
489
- if (filterRes.matchedCount === 1 && !filterRes.isFallback) {
506
+ // Subcase B: Matched items in machine mode
507
+ if (isMachine) {
508
+ renderResult(
509
+ { linkedPackages: filterRes.items, matchedCount: filterRes.matchedCount, isFallback: false },
510
+ { json: options.json, envelope: options.envelope }
511
+ );
512
+ return;
513
+ }
514
+
515
+ // Subcase C: Exactly 1 item matched in human terminal mode -> Display full project detail
516
+ if (filterRes.matchedCount === 1) {
490
517
  const matchedPkg = filterRes.items[0];
491
518
  const detail = await getProjectDetailInfo(matchedPkg.path);
492
- if (options.json) {
493
- console.log(JSON.stringify(projectDetailToJson(detail), null, 2));
494
- } else {
495
- printProjectDetail(detail);
496
- }
519
+ printProjectDetail(detail);
497
520
  return;
498
521
  }
499
522
 
500
- // Subcase C: Multiple items matched -> Display filtered summary list
501
- if (options.json) {
502
- console.log(
503
- JSON.stringify({ linkedPackages: filterRes.items }, null, 2)
504
- );
505
- } else {
506
- printAggregatedPackages(filterRes.items, {
507
- header: `ActionDock Linked Packages (${filterRes.matchedCount} matches for '${effectiveIntent}'):\n`,
508
- showTip: true,
509
- });
510
- }
523
+ // Subcase D: Multiple items matched in human terminal mode -> Display filtered summary list
524
+ printAggregatedPackages(filterRes.items, {
525
+ header: `ActionDock Linked Packages (${filterRes.matchedCount} matches for '${effectiveIntent}'):\n`,
526
+ showTip: true,
527
+ });
511
528
  } catch (err: any) {
512
- console.error(`Error: ${err.message}`);
513
- process.exit(1);
529
+ if (err instanceof CliError) {
530
+ throw err;
531
+ }
532
+ throw new ExecutionError(err.message);
514
533
  }
515
534
  });
516
535
  }
@@ -1,4 +1,5 @@
1
1
  import { initProject } from "@actiondock/core";
2
+ import { ExecutionError } from "@actiondock/runtime-cli";
2
3
  import { Command } from "commander";
3
4
 
4
5
  export function registerInitCommand(program: Command): void {
@@ -17,8 +18,7 @@ export function registerInitCommand(program: Command): void {
17
18
  });
18
19
  console.log(`Successfully initialized ActionDock project in ${dir}`);
19
20
  } catch (err: any) {
20
- console.error(`Error: ${err.message}`);
21
- process.exit(1);
21
+ throw new ExecutionError(err.message);
22
22
  }
23
23
  });
24
24
  }
@@ -3,6 +3,7 @@ import {
3
3
  pruneRegistry,
4
4
  unlinkPackage,
5
5
  } from "@actiondock/core";
6
+ import { ExecutionError } from "@actiondock/runtime-cli";
6
7
  import { Command } from "commander";
7
8
 
8
9
  export function registerLinkCommands(program: Command): void {
@@ -27,8 +28,7 @@ export function registerLinkCommands(program: Command): void {
27
28
  console.log(`[OK] Linked package '${result.id}' (v${result.version}) from ${result.path}`);
28
29
  }
29
30
  } catch (err: any) {
30
- console.error(`Error: ${err.message}`);
31
- process.exit(1);
31
+ throw new ExecutionError(err.message);
32
32
  }
33
33
  });
34
34
 
@@ -67,8 +67,7 @@ export function registerLinkCommands(program: Command): void {
67
67
  console.log(`Package or workspace '${identifier || process.cwd()}' was not linked in registry`);
68
68
  }
69
69
  } catch (err: any) {
70
- console.error(`Error: ${err.message}`);
71
- process.exit(1);
70
+ throw new ExecutionError(err.message);
72
71
  }
73
72
  });
74
73
  }
@@ -2,6 +2,7 @@ import { resolve } from "node:path";
2
2
  import { findProjectRoot, loadProjectConfig } from "@actiondock/core";
3
3
  import { startMcpHttpServer, startMcpStdio } from "@actiondock/mcp";
4
4
  import { Command } from "commander";
5
+ import { ExecutionError } from "@actiondock/runtime-cli";
5
6
  import { parseByteSize } from "../utils/bytes";
6
7
  import { parseDuration } from "../utils/duration";
7
8
 
@@ -35,8 +36,7 @@ export function registerMcpCommands(program: Command): void {
35
36
  try {
36
37
  timeoutMs = parseDuration(options.timeout);
37
38
  } catch (err: any) {
38
- process.stderr.write(`Error: ${err.message}\n`);
39
- process.exit(1);
39
+ throw new ExecutionError(err.message);
40
40
  }
41
41
  }
42
42
 
@@ -56,8 +56,7 @@ export function registerMcpCommands(program: Command): void {
56
56
  timeoutMs,
57
57
  });
58
58
  } catch (err: any) {
59
- process.stderr.write(`Failed to start MCP STDIO server: ${err.message}\n`);
60
- process.exit(1);
59
+ throw new ExecutionError(`Failed to start MCP STDIO server: ${err.message}`);
61
60
  }
62
61
  });
63
62
 
@@ -106,8 +105,7 @@ export function registerMcpCommands(program: Command): void {
106
105
  try {
107
106
  maxBodyBytes = parseByteSize(options.maxBody);
108
107
  } catch (err: any) {
109
- console.error(`Error: ${err.message}`);
110
- process.exit(1);
108
+ throw new ExecutionError(err.message);
111
109
  }
112
110
  }
113
111
 
@@ -116,8 +114,7 @@ export function registerMcpCommands(program: Command): void {
116
114
  try {
117
115
  timeoutMs = parseDuration(options.timeout);
118
116
  } catch (err: any) {
119
- console.error(`Error: ${err.message}`);
120
- process.exit(1);
117
+ throw new ExecutionError(err.message);
121
118
  }
122
119
  }
123
120
 
@@ -192,8 +189,7 @@ export function registerMcpCommands(program: Command): void {
192
189
  process.exit(0);
193
190
  });
194
191
  } catch (err: any) {
195
- console.error(`Failed to start ActionDock MCP HTTP server: ${err.message}`);
196
- process.exit(1);
192
+ throw new ExecutionError(`Failed to start ActionDock MCP HTTP server: ${err.message}`);
197
193
  }
198
194
  });
199
195
  }