@actiondock/cli 2.9.1 → 2.11.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.
Files changed (71) hide show
  1. package/README.md +3 -3
  2. package/bin/ad.js +225 -5
  3. package/dist/commands/action/create.d.ts +1 -1
  4. package/dist/commands/action/create.js +6 -14
  5. package/dist/commands/add.js +1 -1
  6. package/dist/commands/config/delete.js +3 -1
  7. package/dist/commands/config/env.js +2 -1
  8. package/dist/commands/config/get.js +3 -1
  9. package/dist/commands/config/list.js +6 -5
  10. package/dist/commands/config/schema.js +16 -10
  11. package/dist/commands/config/set.js +3 -1
  12. package/dist/commands/describe.js +6 -4
  13. package/dist/commands/export.js +34 -16
  14. package/dist/commands/index.d.ts +37 -24
  15. package/dist/commands/index.js +346 -48
  16. package/dist/commands/info.js +34 -9
  17. package/dist/commands/list.js +13 -2
  18. package/dist/commands/mcp.js +0 -1
  19. package/dist/commands/playbook.js +67 -52
  20. package/dist/commands/profile.js +2 -1
  21. package/dist/commands/remove.js +1 -1
  22. package/dist/commands/run.d.ts +2 -1
  23. package/dist/commands/run.js +19 -56
  24. package/dist/commands/runs.d.ts +6 -1
  25. package/dist/commands/runs.js +274 -11
  26. package/dist/commands/serve.js +5 -2
  27. package/dist/commands/state.js +84 -15
  28. package/dist/errors.d.ts +5 -3
  29. package/dist/errors.js +23 -7
  30. package/dist/index.d.ts +10 -2
  31. package/dist/index.js +15 -12
  32. package/dist/prompt.d.ts +3 -0
  33. package/dist/prompt.js +6 -14
  34. package/dist/renderer.d.ts +8 -151
  35. package/dist/renderer.js +17 -375
  36. package/dist/renderers/action.d.ts +51 -0
  37. package/dist/renderers/action.js +112 -0
  38. package/dist/renderers/config.d.ts +31 -0
  39. package/dist/renderers/config.js +93 -0
  40. package/dist/renderers/playbook.d.ts +19 -0
  41. package/dist/renderers/playbook.js +42 -0
  42. package/dist/renderers/project.d.ts +45 -0
  43. package/dist/renderers/project.js +196 -0
  44. package/dist/renderers/runs.d.ts +15 -0
  45. package/dist/renderers/runs.js +47 -0
  46. package/dist/renderers/state.d.ts +4 -0
  47. package/dist/renderers/state.js +19 -0
  48. package/dist/services/config-merge.js +1 -2
  49. package/dist/services/index.d.ts +1 -0
  50. package/dist/services/index.js +1 -0
  51. package/dist/services/run-watch.d.ts +156 -0
  52. package/dist/services/run-watch.js +395 -0
  53. package/dist/types.d.ts +37 -0
  54. package/dist/utils/cert.js +1 -1
  55. package/dist/utils/index.d.ts +1 -1
  56. package/dist/utils/index.js +1 -1
  57. package/dist/utils/input.d.ts +2 -1
  58. package/dist/utils/input.js +2 -1
  59. package/dist/utils/target.d.ts +6 -2
  60. package/dist/utils/target.js +4 -4
  61. package/dist/utils/utils.d.ts +1 -1
  62. package/dist/utils/utils.js +4 -12
  63. package/dist/utils/warning.d.ts +4 -0
  64. package/dist/utils/warning.js +4 -0
  65. package/package.json +5 -5
  66. package/dist/commands/state-list.d.ts +0 -24
  67. package/dist/commands/state-list.js +0 -81
  68. package/dist/standalone/index.d.ts +0 -12
  69. package/dist/standalone/index.js +0 -13
  70. package/dist/utils/duration.d.ts +0 -1
  71. package/dist/utils/duration.js +0 -1
@@ -1,29 +1,6 @@
1
- import { readFileSync } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { Command } from "commander";
4
- import { registerActionCommands } from "./action/index.js";
5
- import { registerAddCommand } from "./add.js";
6
- import { registerBuildCommand } from "./build.js";
7
- import { registerConfigCommands } from "./config/index.js";
8
- import { registerDescribeCommand } from "./describe.js";
9
- import { registerDoctorCommand } from "./doctor.js";
10
- import { registerExportCommand } from "./export.js";
11
- import { registerGenerateCommands } from "./generate.js";
12
- import { registerInfoCommand } from "./info.js";
13
- import { registerInitCommand } from "./init.js";
14
- import { registerLinkCommands } from "./link.js";
15
- import { registerListCommand } from "./list.js";
16
- import { registerMcpCommands } from "./mcp.js";
17
- import { registerPackCommand } from "./pack.js";
18
- import { registerPlaybookCommands } from "./playbook.js";
19
- import { registerProfileCommands } from "./profile.js";
20
- import { registerRemoveCommand } from "./remove.js";
21
- import { registerRunCommand } from "./run.js";
22
- import { registerRunsCommands } from "./runs.js";
23
- import { registerServeCommand } from "./serve.js";
24
- import { registerStateCommands } from "./state.js";
25
- import { registerTestCommand } from "./test.js";
26
- import { registerValidateCommand } from "./validate.js";
27
4
  /**
28
5
  * 读取 CLI 自身 package.json 的版本号(单一事实源)。
29
6
  * 兼容源码运行(src/commands)与构建产物运行(dist/commands)两种目录层级。
@@ -62,6 +39,288 @@ function applyCommonOptions(cmd) {
62
39
  applyCommonOptions(sub);
63
40
  }
64
41
  }
42
+ /**
43
+ * 动态加载子命令实现模块(兼容构建产物 .js 与开发环境 .ts)。
44
+ */
45
+ function importCommandModule(relativePath) {
46
+ const ext = existsSync(join(import.meta.dirname, "run.js")) ? ".js" : ".ts";
47
+ return import(`${relativePath}${ext}`);
48
+ }
49
+ const COMMAND_DECLARATIONS = [
50
+ {
51
+ name: "init",
52
+ spec: "init [directory]",
53
+ description: "Initialize a new ActionDock project",
54
+ loader: "./init",
55
+ registerFn: "registerInitCommand",
56
+ },
57
+ {
58
+ name: "add",
59
+ spec: "add <package>",
60
+ description: "Install and lock an Action package dependency into current project",
61
+ loader: "./add",
62
+ registerFn: "registerAddCommand",
63
+ },
64
+ {
65
+ name: "remove",
66
+ spec: "remove <package>",
67
+ description: "Remove an Action package dependency and update actiondock.lock.json",
68
+ loader: "./remove",
69
+ registerFn: "registerRemoveCommand",
70
+ },
71
+ {
72
+ name: "action",
73
+ spec: "action",
74
+ description: "Manage Action definitions and scaffolding (create)",
75
+ loader: "./action/create",
76
+ registerFn: "registerActionCommands",
77
+ },
78
+ {
79
+ name: "info",
80
+ spec: "info [patterns...]",
81
+ description: "Display information about current project, linked package, or remote target",
82
+ loader: "./info",
83
+ registerFn: "registerInfoCommand",
84
+ },
85
+ {
86
+ name: "doctor",
87
+ spec: "doctor",
88
+ description: "Check ActionDock environment, registry health, and project diagnostics",
89
+ loader: "./doctor",
90
+ registerFn: "registerDoctorCommand",
91
+ },
92
+ {
93
+ name: "list",
94
+ spec: "list [patterns...]",
95
+ description: "List actions in current project, linked packages, or remote profile",
96
+ loader: "./list",
97
+ registerFn: "registerListCommand",
98
+ },
99
+ {
100
+ name: "describe",
101
+ spec: "describe <id>",
102
+ description: "Show action definition, schema, and description",
103
+ loader: "./describe",
104
+ registerFn: "registerDescribeCommand",
105
+ },
106
+ {
107
+ name: "run",
108
+ spec: "run <id> [params...]",
109
+ description: "Execute an action (from current project, linked packages, or remote profile)",
110
+ loader: "./run",
111
+ registerFn: "registerRunCommand",
112
+ },
113
+ {
114
+ name: "validate",
115
+ spec: "validate [id]",
116
+ description: "Validate action schemas and definitions",
117
+ loader: "./validate",
118
+ registerFn: "registerValidateCommand",
119
+ },
120
+ {
121
+ name: "generate",
122
+ spec: "generate",
123
+ description: "Generate project artifacts such as TypeScript declarations",
124
+ loader: "./generate",
125
+ registerFn: "registerGenerateCommands",
126
+ },
127
+ {
128
+ name: "playbook",
129
+ spec: "playbook",
130
+ description: "Manage task Playbooks (Task SOPs for AI Agents)",
131
+ loader: "./playbook",
132
+ registerFn: "registerPlaybookCommands",
133
+ },
134
+ {
135
+ name: "config",
136
+ spec: "config",
137
+ description: "Manage runtime configuration store (Global & Project-level)",
138
+ loader: "./config/index",
139
+ registerFn: "registerConfigCommands",
140
+ },
141
+ {
142
+ name: "state",
143
+ spec: "state",
144
+ description: "Inspect and manage Shared State store",
145
+ loader: "./state",
146
+ registerFn: "registerStateCommands",
147
+ },
148
+ {
149
+ name: "runs",
150
+ spec: "runs",
151
+ description: "Inspect action execution history",
152
+ loader: "./runs",
153
+ registerFn: "registerRunsCommands",
154
+ },
155
+ {
156
+ name: "test",
157
+ spec: "test [pattern]",
158
+ description: "Run project tests using configured test runner (node:test or bun test)",
159
+ loader: "./test",
160
+ registerFn: "registerTestCommand",
161
+ },
162
+ {
163
+ name: "build",
164
+ spec: "build",
165
+ description: "Build project actions into a runnable Node.js delivery directory",
166
+ loader: "./build",
167
+ registerFn: "registerBuildCommand",
168
+ },
169
+ {
170
+ name: "pack",
171
+ spec: "pack",
172
+ description: "Pack Action package into a standard npm tarball (.tgz) for distribution",
173
+ loader: "./pack",
174
+ registerFn: "registerPackCommand",
175
+ },
176
+ {
177
+ name: "export",
178
+ spec: "export",
179
+ description: "Export project artifacts",
180
+ loader: "./export",
181
+ registerFn: "registerExportCommand",
182
+ },
183
+ {
184
+ name: "link",
185
+ spec: "link [path]",
186
+ description: "Link local Action package(s) or workspace directory into global registry for instant cross-directory execution",
187
+ loader: "./link",
188
+ registerFn: "registerLinkCommands",
189
+ linkedNames: ["link", "unlink"],
190
+ },
191
+ {
192
+ name: "unlink",
193
+ spec: "unlink [identifier]",
194
+ description: "Unlink a package, workspace, or prune stale entries from global developer registry",
195
+ loader: "./link",
196
+ registerFn: "registerLinkCommands",
197
+ linkedNames: ["link", "unlink"],
198
+ },
199
+ {
200
+ name: "profile",
201
+ spec: "profile",
202
+ description: "Manage multi-cloud and remote execution profiles",
203
+ loader: "./profile",
204
+ registerFn: "registerProfileCommands",
205
+ },
206
+ {
207
+ name: "serve",
208
+ spec: "serve",
209
+ description: "Start the ActionDock lightweight HTTP Runner server for remote execution",
210
+ loader: "./serve",
211
+ registerFn: "registerServeCommand",
212
+ },
213
+ {
214
+ name: "mcp",
215
+ spec: "mcp",
216
+ description: "Model Context Protocol (MCP) server for ActionDock Actions (STDIO default)",
217
+ loader: "./mcp",
218
+ registerFn: "registerMcpCommands",
219
+ },
220
+ ];
221
+ const RUNTIME_EXECUTABLES = new Set(["node", "bun", "tsx", "deno"]);
222
+ /**
223
+ * 判断命令行参数数组是否以宿主运行环境(node、bun、tsx 等)及入口脚本为前置元素。
224
+ */
225
+ export function isNodeArgv(argv) {
226
+ if (!Array.isArray(argv) || argv.length < 2)
227
+ return false;
228
+ const first = argv[0];
229
+ if (!first || first.startsWith("-"))
230
+ return false;
231
+ const base = first.split(/[/\\]/).pop()?.toLowerCase().replace(/\.exe$/i, "") ?? "";
232
+ if (RUNTIME_EXECUTABLES.has(base)) {
233
+ return true;
234
+ }
235
+ // 严格排除已知子命令与内置 help 命令,杜绝将纯用户子命令参数错误识别为运行时
236
+ if (COMMAND_DECLARATIONS.some((d) => d.name === first || d.alias === first) ||
237
+ first === "help") {
238
+ return false;
239
+ }
240
+ return false;
241
+ }
242
+ /**
243
+ * 从命令行参数数组中解析出目标子命令名称(跳过全局选项与启动包装参数)。
244
+ *
245
+ * @param argv 命令行原始参数数组
246
+ * @param parseOptions Commander 解析选项(如 { from: 'user' })
247
+ * @returns 目标子命令名称或 null
248
+ */
249
+ export function resolveTargetSubcommand(argv, parseOptions) {
250
+ let startIndex = 0;
251
+ if (parseOptions?.from === "user") {
252
+ startIndex = 0;
253
+ }
254
+ else if (parseOptions?.from === "eval") {
255
+ startIndex = 1;
256
+ }
257
+ else if (parseOptions?.from === "node") {
258
+ startIndex = 2;
259
+ }
260
+ else if (isNodeArgv(argv)) {
261
+ startIndex = 2;
262
+ }
263
+ else {
264
+ startIndex = 0;
265
+ }
266
+ let i = startIndex;
267
+ while (i < argv.length) {
268
+ const arg = argv[i];
269
+ if (arg === "--data-dir") {
270
+ i += 2;
271
+ continue;
272
+ }
273
+ if (arg.startsWith("-")) {
274
+ i++;
275
+ continue;
276
+ }
277
+ // 特殊处理 Commander 内置 help 命令(如 ad help run)
278
+ if (arg === "help" && i + 1 < argv.length && !argv[i + 1].startsWith("-")) {
279
+ return argv[i + 1];
280
+ }
281
+ return arg;
282
+ }
283
+ return null;
284
+ }
285
+ /**
286
+ * 按需加载并挂载目标子命令模块。
287
+ * 基于 Promise 缓存机制消除并发调用时的竞态条件。
288
+ */
289
+ async function loadAndAttachCommand(targetSubcommand, program, placeholders, loadingPromises, context) {
290
+ const decl = COMMAND_DECLARATIONS.find((d) => d.name === targetSubcommand || d.alias === targetSubcommand);
291
+ if (!decl)
292
+ return;
293
+ const existing = loadingPromises.get(decl.loader);
294
+ if (existing) {
295
+ return existing;
296
+ }
297
+ const loadPromise = (async () => {
298
+ // 移除对应的占位命令节点
299
+ const toRemove = decl.linkedNames || [decl.name];
300
+ for (const name of toRemove) {
301
+ const p = placeholders.get(name);
302
+ if (p) {
303
+ const idx = program.commands.indexOf(p);
304
+ if (idx !== -1) {
305
+ program.commands.splice(idx, 1);
306
+ }
307
+ placeholders.delete(name);
308
+ }
309
+ }
310
+ // 动态导入实际命令模块并挂载真实命令
311
+ const mod = await importCommandModule(decl.loader);
312
+ const fn = mod[decl.registerFn];
313
+ if (typeof fn === "function") {
314
+ fn(program, context);
315
+ applyCommonOptions(program);
316
+ }
317
+ })();
318
+ loadingPromises.set(decl.loader, loadPromise);
319
+ loadPromise.catch(() => {
320
+ loadingPromises.delete(decl.loader);
321
+ });
322
+ return loadPromise;
323
+ }
65
324
  export function createCliProgram(context) {
66
325
  const program = new Command();
67
326
  program
@@ -77,30 +336,69 @@ export function createCliProgram(context) {
77
336
  program.exitOverride((err) => {
78
337
  throw err;
79
338
  });
80
- registerInitCommand(program);
81
- registerAddCommand(program, context);
82
- registerRemoveCommand(program, context);
83
- registerActionCommands(program, context);
84
- registerInfoCommand(program, context);
85
- registerDoctorCommand(program, context);
86
- registerListCommand(program, context);
87
- registerDescribeCommand(program, context);
88
- registerRunCommand(program, context);
89
- registerValidateCommand(program, context);
90
- registerGenerateCommands(program, context);
91
- registerPlaybookCommands(program, context);
92
- registerConfigCommands(program, context);
93
- registerStateCommands(program, context);
94
- registerRunsCommands(program, context);
95
- registerTestCommand(program);
96
- registerBuildCommand(program, context);
97
- registerPackCommand(program, context);
98
- registerExportCommand(program, context);
99
- registerLinkCommands(program);
100
- registerProfileCommands(program);
101
- registerServeCommand(program, context);
102
- registerMcpCommands(program, context);
339
+ if (context?.stdout || context?.stderr) {
340
+ program.configureOutput({
341
+ writeOut: (str) => {
342
+ if (context.stdout)
343
+ context.stdout(str);
344
+ else
345
+ process.stdout.write(str);
346
+ },
347
+ writeErr: (str) => {
348
+ if (context.stderr)
349
+ context.stderr(str);
350
+ else
351
+ process.stderr.write(str);
352
+ },
353
+ });
354
+ }
355
+ program.addHelpText("after", `
356
+ Workflow Guidance:
357
+ For multi-step workflows, run 'ad playbook list' before invoking atomic actions.
358
+ Run 'ad info [intent]' to discover packages and playbooks by keyword.`);
359
+ // 注册轻量级占位命令声明(零重型模块导入开销)
360
+ const placeholders = new Map();
361
+ for (const decl of COMMAND_DECLARATIONS) {
362
+ const cmd = program.command(decl.spec).description(decl.description);
363
+ if (decl.alias) {
364
+ cmd.alias(decl.alias);
365
+ }
366
+ placeholders.set(decl.name, cmd);
367
+ }
103
368
  applyCommonOptions(program);
369
+ // 拦截 parseAsync 以在实际执行或具体帮助展示时按需动态加载真实子命令
370
+ const originalParseAsync = program.parseAsync.bind(program);
371
+ const loadingPromises = new Map();
372
+ program.parseAsync = async function (argv, parseOptions) {
373
+ const targetArgv = argv || process.argv;
374
+ const targetSubcommand = resolveTargetSubcommand(targetArgv, parseOptions);
375
+ if (targetSubcommand) {
376
+ await loadAndAttachCommand(targetSubcommand, program, placeholders, loadingPromises, context);
377
+ }
378
+ return originalParseAsync(argv, parseOptions);
379
+ };
104
380
  return program;
105
381
  }
106
- export { registerActionCommands, registerAddCommand, registerBuildCommand, registerConfigCommands, registerDescribeCommand, registerDoctorCommand, registerExportCommand, registerGenerateCommands, registerInfoCommand, registerInitCommand, registerLinkCommands, registerListCommand, registerMcpCommands, registerPackCommand, registerPlaybookCommands, registerProfileCommands, registerRemoveCommand, registerRunCommand, registerRunsCommands, registerServeCommand, registerStateCommands, registerTestCommand, registerValidateCommand, };
382
+ export const registerActionCommands = (program, context) => importCommandModule("./action/create").then((m) => m.registerActionCommands(program, context));
383
+ export const registerAddCommand = (program, context) => importCommandModule("./add").then((m) => m.registerAddCommand(program, context));
384
+ export const registerBuildCommand = (program, context) => importCommandModule("./build").then((m) => m.registerBuildCommand(program, context));
385
+ export const registerConfigCommands = (program, context) => importCommandModule("./config/index").then((m) => m.registerConfigCommands(program, context));
386
+ export const registerDescribeCommand = (program, context) => importCommandModule("./describe").then((m) => m.registerDescribeCommand(program, context));
387
+ export const registerDoctorCommand = (program, context) => importCommandModule("./doctor").then((m) => m.registerDoctorCommand(program, context));
388
+ export const registerExportCommand = (program, context) => importCommandModule("./export").then((m) => m.registerExportCommand(program, context));
389
+ export const registerGenerateCommands = (program, context) => importCommandModule("./generate").then((m) => m.registerGenerateCommands(program, context));
390
+ export const registerInfoCommand = (program, context) => importCommandModule("./info").then((m) => m.registerInfoCommand(program, context));
391
+ export const registerInitCommand = (program) => importCommandModule("./init").then((m) => m.registerInitCommand(program));
392
+ export const registerLinkCommands = (program) => importCommandModule("./link").then((m) => m.registerLinkCommands(program));
393
+ export const registerListCommand = (program, context) => importCommandModule("./list").then((m) => m.registerListCommand(program, context));
394
+ export const registerMcpCommands = (program, context) => importCommandModule("./mcp").then((m) => m.registerMcpCommands(program, context));
395
+ export const registerPackCommand = (program, context) => importCommandModule("./pack").then((m) => m.registerPackCommand(program, context));
396
+ export const registerPlaybookCommands = (program, context) => importCommandModule("./playbook").then((m) => m.registerPlaybookCommands(program, context));
397
+ export const registerProfileCommands = (program) => importCommandModule("./profile").then((m) => m.registerProfileCommands(program));
398
+ export const registerRemoveCommand = (program, context) => importCommandModule("./remove").then((m) => m.registerRemoveCommand(program, context));
399
+ export const registerRunCommand = (program, context) => importCommandModule("./run").then((m) => m.registerRunCommand(program, context));
400
+ export const registerRunsCommands = (program, context) => importCommandModule("./runs").then((m) => m.registerRunsCommands(program, context));
401
+ export const registerServeCommand = (program, context) => importCommandModule("./serve").then((m) => m.registerServeCommand(program, context));
402
+ export const registerStateCommands = (program, context) => importCommandModule("./state").then((m) => m.registerStateCommands(program, context));
403
+ export const registerTestCommand = (program) => importCommandModule("./test").then((m) => m.registerTestCommand(program));
404
+ export const registerValidateCommand = (program, context) => importCommandModule("./validate").then((m) => m.registerValidateCommand(program, context));
@@ -9,6 +9,26 @@ import { applyTargetOptions, getEffectiveOptions, resolveFallbackStrategy, resol
9
9
  * 将远端信息响应归一为本地工程详情视图。
10
10
  */
11
11
  function remoteInfoToDetail(remoteInfo) {
12
+ const actionsMap = new Map();
13
+ const rawActions = remoteInfo.actionsDetail || remoteInfo.actions || [];
14
+ for (const a of rawActions) {
15
+ if (typeof a === "string") {
16
+ actionsMap.set(a, { id: a });
17
+ }
18
+ else if (a && typeof a === "object") {
19
+ actionsMap.set(a.id, { id: a.id, description: a.description });
20
+ }
21
+ }
22
+ const playbooksMap = new Map();
23
+ const rawPlaybooks = remoteInfo.playbooksDetail || remoteInfo.playbooks || [];
24
+ for (const pb of rawPlaybooks) {
25
+ if (typeof pb === "string") {
26
+ playbooksMap.set(pb, { id: pb });
27
+ }
28
+ else if (pb && typeof pb === "object") {
29
+ playbooksMap.set(pb.id, { id: pb.id, description: pb.description });
30
+ }
31
+ }
12
32
  return {
13
33
  id: remoteInfo.id,
14
34
  name: remoteInfo.name || remoteInfo.id,
@@ -17,12 +37,14 @@ function remoteInfoToDetail(remoteInfo) {
17
37
  projectRoot: remoteInfo.path || remoteInfo.projectRoot || "",
18
38
  actionsDir: "remote",
19
39
  playbooksDir: "remote",
20
- actionsCount: remoteInfo.actionsCount || (remoteInfo.actions ? remoteInfo.actions.length : 0),
21
- playbooksCount: remoteInfo.playbooksCount || (remoteInfo.playbooks ? remoteInfo.playbooks.length : 0),
22
- actions: (remoteInfo.actionsDetail || remoteInfo.actions || []).map((a) => typeof a === "string" ? a : a.id),
23
- playbooks: (remoteInfo.playbooksDetail || remoteInfo.playbooks || []).map((pb) => typeof pb === "string" ? pb : pb.id),
40
+ actionsCount: actionsMap.size,
41
+ playbooksCount: playbooksMap.size,
42
+ actions: Array.from(actionsMap.keys()),
43
+ playbooks: Array.from(playbooksMap.keys()),
24
44
  configDeclared: Object.keys(remoteInfo.configDeclared || {}),
25
45
  configDef: remoteInfo.configDeclared,
46
+ actionsMap,
47
+ playbooksMap,
26
48
  };
27
49
  }
28
50
  /**
@@ -116,6 +138,9 @@ export function registerInfoCommand(program, context) {
116
138
  }
117
139
  // 扫描本地候选包(当前工程根目录 + 全局已链接包)
118
140
  const { currentRoot, aggregated } = scanLocalAggregatedPackages(context);
141
+ const infoHints = [
142
+ "Tip: Run 'ad info <package-id>' to view detailed package configuration and schema.",
143
+ ];
119
144
  // 无关键字过滤场景
120
145
  if (!effectiveIntent) {
121
146
  if (currentRoot) {
@@ -130,7 +155,7 @@ export function registerInfoCommand(program, context) {
130
155
  });
131
156
  return;
132
157
  }
133
- renderResult({ linkedPackages: aggregated }, {
158
+ renderResult({ linkedPackages: aggregated, hints: infoHints }, {
134
159
  ...outOpts,
135
160
  humanFormatter: () => renderAggregatedPackages(aggregated),
136
161
  });
@@ -147,7 +172,7 @@ export function registerInfoCommand(program, context) {
147
172
  }
148
173
  if (aggregated.length === 0) {
149
174
  if (isMachine) {
150
- renderResult({ linkedPackages: [], matchedCount: 0, isFallback: false }, outOpts);
175
+ renderResult({ linkedPackages: [], matchedCount: 0, isFallback: false, hints: infoHints }, outOpts);
151
176
  return;
152
177
  }
153
178
  throw new ExecutionError(`No ActionDock project or linked packages available to match '${effectiveIntent}'`);
@@ -164,14 +189,14 @@ export function registerInfoCommand(program, context) {
164
189
  if (filterRes.matchedCount === 0) {
165
190
  if (!filterRes.isFallback) {
166
191
  if (isMachine) {
167
- renderResult({ linkedPackages: [], matchedCount: 0, isFallback: false }, outOpts);
192
+ renderResult({ linkedPackages: [], matchedCount: 0, isFallback: false, hints: infoHints }, outOpts);
168
193
  return;
169
194
  }
170
195
  throw new ExecutionError(`No packages matched intent '${effectiveIntent}'`);
171
196
  }
172
197
  // 回退模式:展示全部包并附带提示
173
198
  if (isMachine) {
174
- renderResult({ linkedPackages: filterRes.items, isFallback: true, matchedCount: 0 }, outOpts);
199
+ renderResult({ linkedPackages: filterRes.items, isFallback: true, matchedCount: 0, hints: infoHints }, outOpts);
175
200
  return;
176
201
  }
177
202
  renderResult({ linkedPackages: filterRes.items, isFallback: true, matchedCount: 0 }, {
@@ -183,7 +208,7 @@ export function registerInfoCommand(program, context) {
183
208
  }
184
209
  // 机器模式下搜索:稳定返回列表结构,不因为 matchedCount === 1 突变为详情
185
210
  if (isMachine) {
186
- renderResult({ linkedPackages: filterRes.items, matchedCount: filterRes.matchedCount, isFallback: false }, outOpts);
211
+ renderResult({ linkedPackages: filterRes.items, matchedCount: filterRes.matchedCount, isFallback: false, hints: infoHints }, outOpts);
187
212
  return;
188
213
  }
189
214
  // 人类终端交互:精确匹配单个包展开详情
@@ -63,8 +63,19 @@ export function attachListCommand(parent, context) {
63
63
  };
64
64
  });
65
65
  const filterRes = filterWithFallbackInfo(rawList, effectiveIntent, [(a) => a.id, (a) => a.description], shouldFallback);
66
- if (filterRes.isFallback && isMachine) {
67
- renderResult({ items: filterRes.items, isFallback: true, matchedCount: 0 }, { json: options.json, context });
66
+ const hints = [
67
+ "Tip: For composite or multi-step tasks, check 'ad playbook list' for standard operating procedures.",
68
+ ];
69
+ if (isMachine) {
70
+ if (filterRes.isFallback) {
71
+ renderResult({ items: filterRes.items, isFallback: true, matchedCount: 0, hints }, { json: options.json, context });
72
+ return;
73
+ }
74
+ renderResult({
75
+ items: filterRes.items,
76
+ ...(filterRes.matchedCount !== undefined ? { matchedCount: filterRes.matchedCount } : {}),
77
+ hints,
78
+ }, { json: options.json, context });
68
79
  return;
69
80
  }
70
81
  const title = resolved.type === "remote"
@@ -116,7 +116,6 @@ export function registerMcpCommands(program, context) {
116
116
  const platform = createNodePlatform({
117
117
  customHome: context?.customHome,
118
118
  dataDir: options.dataDir || context?.dataDir,
119
- rootDir: projectRoots?.[0],
120
119
  });
121
120
  const { startMcpHttpServer } = await import("@actiondock/mcp");
122
121
  const server = await startMcpHttpServer({