@actiondock/builder 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.
@@ -1,5 +1,6 @@
1
1
  import type { ActionSpec, ProjectConfig } from "@actiondock/core";
2
2
  import type { PlaybookDefinition } from "@actiondock/core/project";
3
+ import type { CompositeCustomSection } from "./custom-parser.js";
3
4
  export type SkillActionItem = ActionSpec | {
4
5
  id: string;
5
6
  description?: string;
@@ -7,8 +8,33 @@ export type SkillActionItem = ActionSpec | {
7
8
  outputSchema?: any;
8
9
  annotations?: any;
9
10
  };
10
- export declare function generateSourceSkillMd(config: ProjectConfig, actions: SkillActionItem[], playbooks: PlaybookDefinition[]): string;
11
- export declare function generateStandaloneSkillMd(config: ProjectConfig, actions: SkillActionItem[], playbooks: PlaybookDefinition[], binaryRelPath?: string): string;
11
+ export type SkillPlaybookItem = PlaybookDefinition | {
12
+ id: string;
13
+ filePath: string;
14
+ name?: string;
15
+ description?: string;
16
+ actions?: string[];
17
+ };
18
+ /**
19
+ * 渲染完整「运行时环境参考」正文:写入导出目录 references/actiondock-runtime.md,
20
+ * 由主说明按需链接调阅,不在每个技能正文重复展开整套安装与框架管理教程。
21
+ */
22
+ export declare function renderRuntimeReferenceContent(options: {
23
+ /** 依赖安装步骤标题:source 为安装技能源码依赖,composite 为安装复合技能聚合依赖 */
24
+ dependencyStepLabel: string;
25
+ /** 重新链接步骤标题 */
26
+ relinkStepLabel: string;
27
+ /** 是否在重新链接步骤末尾附加复合链接语义说明 */
28
+ compositeLinkNote?: boolean;
29
+ /** 调用方式说明:源码型与 Node 目录型分化(如是否需要全局 ad) */
30
+ invocationStyle: "global-ad" | "node-entry";
31
+ /** Node 目录型的入口相对路径(invocationStyle 为 node-entry 时使用) */
32
+ entryRelPath?: string;
33
+ }): string;
34
+ /** 运行时参考文件的导出目录内相对路径(单一事实源) */
35
+ export declare const RUNTIME_REFERENCE_REL_PATH = "references/actiondock-runtime.md";
36
+ export declare function generateSourceSkillMd(config: ProjectConfig, actions: SkillActionItem[], playbooks: SkillPlaybookItem[]): string;
37
+ export declare function generateStandaloneSkillMd(config: ProjectConfig, actions: SkillActionItem[], playbooks: SkillPlaybookItem[], binaryRelPath?: string): string;
12
38
  export interface CompositeSkillPackageInfo {
13
39
  config: ProjectConfig;
14
40
  actions: Array<{
@@ -23,33 +49,6 @@ export interface CompositeSkillPackageInfo {
23
49
  }>;
24
50
  packageDir: string;
25
51
  }
26
- /**
27
- * 复合技能自定义说明书(SKILL.custom.md)的槽位,
28
- * 决定自定义段落插入到官方模板生成结果中的位置。
29
- */
30
- export type CompositeCustomSlot = "intro" | "after-init" | "after-describe" | "after-actions" | "after-playbooks" | "after-invoke" | "append";
31
- export declare const COMPOSITE_CUSTOM_SLOTS: readonly CompositeCustomSlot[];
32
- /** 工作区内约定俗成的复合技能自定义说明书文件名。 */
33
- export declare const COMPOSITE_CUSTOM_DECLARATION_FILE = "SKILL.custom.md";
34
- export interface CompositeCustomSection {
35
- slot: CompositeCustomSlot;
36
- content: string;
37
- }
38
- export interface CompositeCustomDeclaration {
39
- /** 自定义说明书 frontmatter 中声明的 description 覆盖值 */
40
- description?: string;
41
- sections: CompositeCustomSection[];
42
- }
43
- /**
44
- * 解析自定义说明书正文:以 `<!-- actiondock:slot <name> -->` 标记行分段。
45
- * 首个标记之前的内容归入 `append` 槽位;未知槽位名直接抛错,避免拼写错误被静默吞掉。
46
- */
47
- export declare function parseCustomSections(source: string): CompositeCustomSection[];
48
- /**
49
- * 解析复合技能自定义说明书文件:支持 YAML frontmatter 中的 `description` 覆盖,
50
- * 正文按槽位标记解析为自定义段落。
51
- */
52
- export declare function parseCustomSkillDeclaration(source: string): CompositeCustomDeclaration;
53
52
  /**
54
53
  * 生成多包聚合的复合模式 SKILL.md 文档。
55
54
  */
@@ -4,6 +4,10 @@ function getCleanSkillMetadata(config) {
4
4
  const desc = config.description || `AI Agent skill for ${config.name} (${config.id})`;
5
5
  return { cleanName, desc };
6
6
  }
7
+ /**
8
+ * 动作摘要列表渲染:只承载能力路由信息(标识、原始业务描述、只读或破坏性标注),
9
+ * 不展开完整输入输出 Schema;完整契约由 describe 命令按需调阅。
10
+ */
7
11
  function renderActionListMarkdown(actions, options = {}) {
8
12
  return actions
9
13
  .map((a) => {
@@ -12,7 +16,7 @@ function renderActionListMarkdown(actions, options = {}) {
12
16
  ? `\`${options.packageId}/${a.id}\` (或 \`${a.id}\`)`
13
17
  : `\`${a.id}\``;
14
18
  const lines = [`- ${idLabel}${aDesc}`];
15
- // 标注元数据解析
19
+ // 标注元数据解析(安全标注不得从主说明移走)
16
20
  if (a.annotations && typeof a.annotations === "object") {
17
21
  const annoList = [];
18
22
  if (a.annotations.readOnly === true) {
@@ -25,49 +29,6 @@ function renderActionListMarkdown(actions, options = {}) {
25
29
  lines.push(` - 属性标注: ${annoList.join(", ")}`);
26
30
  }
27
31
  }
28
- // 输入参数模式解析
29
- if (a.inputSchema &&
30
- typeof a.inputSchema === "object" &&
31
- a.inputSchema.properties) {
32
- const props = a.inputSchema.properties;
33
- const req = (a.inputSchema.required || []);
34
- const propKeys = Object.keys(props);
35
- if (propKeys.length > 0) {
36
- lines.push(" - 输入参数:");
37
- for (const k of propKeys) {
38
- const p = props[k] || {};
39
- const typeStr = p.type ? `\`${p.type}\`` : "`any`";
40
- const reqStr = req.includes(k) ? ", 必填" : "";
41
- const descStr = p.description ? `: ${p.description}` : "";
42
- const defStr = p.default !== undefined
43
- ? ` (默认值: \`${JSON.stringify(p.default)}\`)`
44
- : "";
45
- lines.push(` - \`${k}\` (${typeStr}${reqStr})${descStr}${defStr}`);
46
- }
47
- }
48
- else {
49
- lines.push(" - 输入参数: 无");
50
- }
51
- }
52
- else {
53
- lines.push(" - 输入参数: 无");
54
- }
55
- // 输出字段模式解析
56
- if (a.outputSchema &&
57
- typeof a.outputSchema === "object" &&
58
- a.outputSchema.properties) {
59
- const outProps = a.outputSchema.properties;
60
- const outKeys = Object.keys(outProps);
61
- if (outKeys.length > 0) {
62
- lines.push(" - 输出字段:");
63
- for (const k of outKeys) {
64
- const p = outProps[k] || {};
65
- const typeStr = p.type ? `\`${p.type}\`` : "`any`";
66
- const descStr = p.description ? `: ${p.description}` : "";
67
- lines.push(` - \`${k}\` (${typeStr})${descStr}`);
68
- }
69
- }
70
- }
71
32
  return lines.join("\n");
72
33
  })
73
34
  .join("\n\n");
@@ -130,39 +91,63 @@ function renderJsonEnvelopeSection(wording, options) {
130
91
  \`\`\`${descriptionBlock}`;
131
92
  }
132
93
  /**
133
- * 渲染「故障排查与环境安装指引」整节:source 与 composite 共用完整安装指引,
134
- * 差异点(依赖安装步骤标题、重新链接步骤标题、复合链接语义说明)通过参数分化。
94
+ * 渲染完整「运行时环境参考」正文:写入导出目录 references/actiondock-runtime.md,
95
+ * 由主说明按需链接调阅,不在每个技能正文重复展开整套安装与框架管理教程。
135
96
  */
136
- function renderTroubleshootingSection(options) {
97
+ export function renderRuntimeReferenceContent(options) {
137
98
  const relinkNote = options.compositeLinkNote
138
- ? `\n \`ad link\` 会自动识别并注册工作区下的所有子包,使其中的 Action 随时可以通过完全限定标识调用。`
99
+ ? `\`ad link\` 会自动识别并注册工作区下的所有子包,使其中的 Action 随时可以通过完全限定标识调用。`
139
100
  : "";
140
- return `---
141
-
142
- ## 故障排查与环境安装指引(按需查阅)
143
-
144
- > [!NOTE]
145
- > **按需排查原则**:默认宿主环境中已预置 \`ad\` 命令行工具与 Node.js 运行环境。正常执行流程直接调用上述 Action 即可,**严禁在任务启动前盲目进行前置环境检查或体检**;仅在终端明确报错提示命令不存在(如 \`ad: command not found\`)或提示依赖缺失时,方可按本节指引安装初始化。
146
-
147
- ### 命令行工具与环境依赖未就绪时的安装指引
101
+ const invocationSection = options.invocationStyle === "node-entry"
102
+ ? `本技能为 Node 目录型交付,可直接使用自身入口执行,不要求全局安装 \`ad\` 命令行工具:
148
103
 
149
- 若宿主环境未安装 \`ad\` 命令行工具或依赖缺失,请依次按如下步骤完成安装:
104
+ \`\`\`bash
105
+ ${options.entryRelPath || "node ./entry.mjs"} list
106
+ ${options.entryRelPath || "node ./entry.mjs"} describe <action-id>
107
+ ${options.entryRelPath || "node ./entry.mjs"} run <action-id> --json --input-file input.json
108
+ \`\`\`
150
109
 
151
- - **环境要求**:Node.js 版本大于等于 24.12.0(执行 \`node -v\` 确认)。
110
+ 若宿主环境已全局安装 \`ad\`,亦可将其链接后统一调度。`
111
+ : `本技能为源码型交付,通过全局安装的 \`ad\` 命令行工具执行。`;
112
+ const isNodeEntry = options.invocationStyle === "node-entry";
113
+ const probePrinciple = isNodeEntry
114
+ ? `默认宿主环境中已预置 Node.js 运行环境。本目录型交付直接使用自身入口执行,不依赖全局 \`ad\`;正常执行流程直接调用上述 Action 即可,严禁在任务启动前盲目进行前置环境检查或体检;仅在入口报错提示模块缺失(如 \`Cannot find package\`)或 Node.js 版本不满足时,方可按本文指引修复。`
115
+ : `默认宿主环境中已预置 \`ad\` 命令行工具与 Node.js 运行环境。正常执行流程直接调用上述 Action 即可,严禁在任务启动前盲目进行前置环境检查或体检;仅在终端明确报错提示命令不存在(如 \`ad: command not found\`)或提示依赖缺失时,方可按本文指引安装初始化。`;
116
+ const cliInstallSection = isNodeEntry
117
+ ? `- **Node.js 环境要求**:Node.js 版本大于等于 24.12.0(执行 \`node -v\` 确认)。目录型交付不需要全局安装 \`ad\`;若需要接入全局调度,可另行安装:
118
+ \`\`\`bash
119
+ npm install -g @actiondock/cli
120
+ \`\`\``
121
+ : `- **环境要求**:Node.js 版本大于等于 24.12.0(执行 \`node -v\` 确认)。
152
122
  - **全局安装 ActionDock 命令行工具**:
153
123
  \`\`\`bash
154
124
  npm install -g @actiondock/cli
155
- \`\`\`
125
+ \`\`\``;
126
+ return `# ActionDock 运行时环境参考
127
+
128
+ > 本文件为技能主说明的按需参考,仅在环境未就绪或报错时查阅;正常执行流程无需阅读本文。
129
+
130
+ ## 调用方式
131
+
132
+ ${invocationSection}
133
+
134
+ ## 按需排查原则
135
+
136
+ ${probePrinciple}
137
+
138
+ ## 环境依赖未就绪时的安装指引
139
+
140
+ 若宿主环境依赖缺失,请依次按如下步骤完成安装:
141
+
142
+ ${cliInstallSection}
156
143
  - **${options.dependencyStepLabel}**:
157
144
  若在技能目录内调用时提示模块缺失,在 \`<skill_root>\` 目录下安装生产依赖:
158
145
  \`\`\`bash
159
146
  cd "<skill_root>" && npm install --omit=dev
160
147
  \`\`\`
161
- - **验证工具就绪**:
162
- \`\`\`bash
163
- ad --version
164
- \`\`\`
165
- - **环境诊断与体检**:
148
+ - **验证就绪**:
149
+ ${isNodeEntry ? `${options.entryRelPath || "node ./entry.mjs"} list` : "ad --version"}
150
+ - **环境诊断与体检**(仅全局接入 \`ad\` 后适用):
166
151
  安装完成后若仍遇到异常,执行体检命令排查:
167
152
  \`\`\`bash
168
153
  ad doctor
@@ -170,8 +155,24 @@ function renderTroubleshootingSection(options) {
170
155
  - **${options.relinkStepLabel}**:
171
156
  \`\`\`bash
172
157
  ad link "<skill_root>"
173
- \`\`\`${relinkNote}`;
158
+ \`\`\`
159
+ ${relinkNote ? ` ${relinkNote}\n` : ""}`;
174
160
  }
161
+ /**
162
+ * 渲染主说明中的「故障排查」精简入口:仅保留按需排查原则与参考文件链接。
163
+ */
164
+ function renderTroubleshootingSection(referenceRelPath) {
165
+ return `---
166
+
167
+ ## 故障排查与环境安装指引(按需查阅)
168
+
169
+ > [!NOTE]
170
+ > **按需排查原则**:默认宿主环境中已预置运行环境与依赖,正常执行流程直接调用上述 Action 即可,严禁在任务启动前盲目进行前置环境检查或体检。
171
+
172
+ 仅在报错提示命令不存在或依赖缺失时,按需查阅完整安装与自愈指引:[${referenceRelPath}](${referenceRelPath})`;
173
+ }
174
+ /** 运行时参考文件的导出目录内相对路径(单一事实源) */
175
+ export const RUNTIME_REFERENCE_REL_PATH = "references/actiondock-runtime.md";
175
176
  export function generateSourceSkillMd(config, actions, playbooks) {
176
177
  const { cleanName, desc } = getCleanSkillMetadata(config);
177
178
  const pkgId = config.id;
@@ -191,9 +192,9 @@ ${desc}
191
192
 
192
193
  本技能为 **ActionDock 源码型技能包**。智能体可直接通过宿主环境中已安装的 ActionDock 命令行工具 \`ad\` 执行其中的 Action。
193
194
 
194
- ### 注册与链接
195
+ ### 注册与链接(全局调用)
195
196
 
196
- 在初次调用或初始化时,将包含本 \`SKILL.md\` 的目录解析为 \`<skill_root>\` 并完成注册:
197
+ 本技能面向全局挂载使用:在初次调用或初始化时,将包含本 \`SKILL.md\` 的目录解析为 \`<skill_root>\` 并完成注册链接,之后可在任意工作目录通过完全限定 ID 全局调用:
197
198
 
198
199
  \`\`\`bash
199
200
  ad link "<skill_root>"
@@ -229,13 +230,6 @@ ad run ${pkgId}/${firstAction} --json -- ASSIGNMENT...
229
230
  ad run ${pkgId}/${firstAction} --json --input-file input.json
230
231
  \`\`\`
231
232
 
232
- > 免注册本地执行:
233
- > 若工作目录已位于本技能根目录,亦可直接免 link 执行:
234
- > \`\`\`bash
235
- > cd <skill_root>
236
- > ad run ${firstAction} --json
237
- > \`\`\`
238
-
239
233
  ${renderJsonEnvelopeSection("envelope")}
240
234
  ${playbookSection}
241
235
  ---
@@ -260,10 +254,7 @@ ad state list --package ${pkgId}
260
254
  ad state get KEY --package ${pkgId}
261
255
  \`\`\`
262
256
 
263
- ${renderTroubleshootingSection({
264
- dependencyStepLabel: "安装技能源码依赖",
265
- relinkStepLabel: "完成安装后重新链接本技能",
266
- })}
257
+ ${renderTroubleshootingSection(RUNTIME_REFERENCE_REL_PATH)}
267
258
  `;
268
259
  }
269
260
  export function generateStandaloneSkillMd(config, actions, playbooks, binaryRelPath = "./bin/action-bin") {
@@ -335,73 +326,16 @@ ${binaryRelPath} state get KEY
335
326
 
336
327
  ## 故障排查与依赖自愈指引(按需查阅)
337
328
 
338
- 若在宿主环境中执行时提示模块缺失,在技能根目录下安装生产依赖:
329
+ 本目录为 Node 目录型交付,直接使用自身入口执行,不要求全局安装 \`ad\` 命令行工具。
330
+ 若提示模块缺失,在技能根目录下安装生产依赖:
339
331
 
340
332
  \`\`\`bash
341
333
  npm install --omit=dev
342
334
  \`\`\`
335
+
336
+ 完整环境安装与自愈指引按需查阅:[${RUNTIME_REFERENCE_REL_PATH}](${RUNTIME_REFERENCE_REL_PATH})
343
337
  `;
344
338
  }
345
- export const COMPOSITE_CUSTOM_SLOTS = [
346
- "intro",
347
- "after-init",
348
- "after-describe",
349
- "after-actions",
350
- "after-playbooks",
351
- "after-invoke",
352
- "append",
353
- ];
354
- /** 工作区内约定俗成的复合技能自定义说明书文件名。 */
355
- export const COMPOSITE_CUSTOM_DECLARATION_FILE = "SKILL.custom.md";
356
- const CUSTOM_SLOT_MARKER_PATTERN = /^\s*<!--\s*actiondock:slot\s+([a-zA-Z][a-zA-Z0-9-]*)\s*-->\s*$/;
357
- /**
358
- * 解析自定义说明书正文:以 `<!-- actiondock:slot <name> -->` 标记行分段。
359
- * 首个标记之前的内容归入 `append` 槽位;未知槽位名直接抛错,避免拼写错误被静默吞掉。
360
- */
361
- export function parseCustomSections(source) {
362
- const sections = [];
363
- let currentSlot = "append";
364
- let buffer = [];
365
- const flush = () => {
366
- const content = buffer.join("\n").trim();
367
- buffer = [];
368
- if (content) {
369
- sections.push({ slot: currentSlot, content });
370
- }
371
- };
372
- for (const line of source.split(/\r?\n/)) {
373
- const match = line.match(CUSTOM_SLOT_MARKER_PATTERN);
374
- if (match) {
375
- flush();
376
- const slot = match[1];
377
- if (!COMPOSITE_CUSTOM_SLOTS.includes(slot)) {
378
- throw new Error(`Unknown ActionDock custom slot '${match[1]}'. Valid slots: ${COMPOSITE_CUSTOM_SLOTS.join(", ")}`);
379
- }
380
- currentSlot = slot;
381
- continue;
382
- }
383
- buffer.push(line);
384
- }
385
- flush();
386
- return sections;
387
- }
388
- /**
389
- * 解析复合技能自定义说明书文件:支持 YAML frontmatter 中的 `description` 覆盖,
390
- * 正文按槽位标记解析为自定义段落。
391
- */
392
- export function parseCustomSkillDeclaration(source) {
393
- let body = source;
394
- let description;
395
- const frontmatter = source.match(/^\uFEFF?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/);
396
- if (frontmatter) {
397
- body = source.slice(frontmatter[0].length);
398
- const descLine = frontmatter[1].match(/^description:[ \t]*(.+?)[ \t]*$/m);
399
- if (descLine) {
400
- description = descLine[1].replace(/^["']|["']$/g, "");
401
- }
402
- }
403
- return { description, sections: parseCustomSections(body) };
404
- }
405
339
  /**
406
340
  * 生成多包聚合的复合模式 SKILL.md 文档。
407
341
  */
@@ -480,11 +414,7 @@ ad run ${sampleActionId} --json --input-file input.json
480
414
  \`\`\`
481
415
 
482
416
  ${renderJsonEnvelopeSection("envelope", { withDescriptions: false })}`;
483
- const sTroubleshooting = renderTroubleshootingSection({
484
- dependencyStepLabel: "安装复合技能聚合依赖",
485
- relinkStepLabel: "完成安装后重新链接复合技能",
486
- compositeLinkNote: true,
487
- });
417
+ const sTroubleshooting = renderTroubleshootingSection(RUNTIME_REFERENCE_REL_PATH);
488
418
  const parts = [
489
419
  { slot: "intro", text: sIntro },
490
420
  { slot: "after-describe", text: sDescribe },
@@ -1,5 +1,5 @@
1
- import { type PlaybookDefinition, type ProjectConfig } from "@actiondock/core/project";
2
- import { type CompositeCustomDeclaration, type SkillActionItem } from "./skill/index.js";
1
+ import { type ProjectConfig } from "@actiondock/core/project";
2
+ import { type CompositeCustomDeclaration } from "./skill/index.js";
3
3
  import type { CompositeSkillExportOptions, SelectionPlan } from "./types.js";
4
4
  /**
5
5
  * SKILL.md 发现、复用与模板视图适配层。
@@ -7,16 +7,6 @@ import type { CompositeSkillExportOptions, SelectionPlan } from "./types.js";
7
7
  * 职责单一聚焦:定位项目中已有的 SKILL.md(单包与复合套件两种检索策略)、
8
8
  * 解析自定义说明书声明,并将规划产物适配为模板生成所需视图。
9
9
  */
10
- /** 供模板生成使用的 SkillActionItem 兼容视图 */
11
- type PlanActionView = SkillActionItem;
12
- /**
13
- * 将规划产物中的 Action 列表适配为模板所需的 SkillActionItem 视图。
14
- */
15
- export declare function toSkillActionItems(actions: SelectionPlan["actions"]): PlanActionView[];
16
- /**
17
- * 将规划产物中的 Playbook 列表适配为模板所需的 PlaybookDefinition 视图。
18
- */
19
- export declare function toPlaybookDefinitions(playbooks: SelectionPlan["playbooks"]): PlaybookDefinition[];
20
10
  /**
21
11
  * 检索单个 Action 项目目录中已存在的 SKILL.md 文件。
22
12
  */
@@ -36,7 +26,7 @@ export declare function resolveCustomSkillDeclaration(options: CompositeSkillExp
36
26
  * 复制或生成 SKILL.md 到目标目录。
37
27
  * 返回实际复用的已有 SKILL.md 路径(若发生复用)。
38
28
  */
39
- export declare function writeSkillMd(root: string, destDir: string, plan: SelectionPlan, options: {
29
+ export declare function writeSkillMd(root: string, destDir: string, options: {
40
30
  skipSkillMd?: boolean;
41
31
  skillMdPath?: string;
42
32
  pkgSlug: string;
@@ -45,4 +35,3 @@ export declare function writeSkillMd(root: string, destDir: string, plan: Select
45
35
  * 构造模板生成所需的 ProjectConfig 视图。
46
36
  */
47
37
  export declare function buildConfigForTemplates(plan: SelectionPlan, actionsDir: string, playbooksDir: string): ProjectConfig;
48
- export {};
package/dist/skill-md.js CHANGED
@@ -4,17 +4,11 @@ import { getPackageSlug, } from "@actiondock/core/project";
4
4
  import { COMPOSITE_CUSTOM_DECLARATION_FILE, parseCustomSkillDeclaration, } from "./skill/index.js";
5
5
  import { BuilderError } from "./errors.js";
6
6
  /**
7
- * 将规划产物中的 Action 列表适配为模板所需的 SkillActionItem 视图。
7
+ * SKILL.md 发现、复用与模板视图适配层。
8
+ *
9
+ * 职责单一聚焦:定位项目中已有的 SKILL.md(单包与复合套件两种检索策略)、
10
+ * 解析自定义说明书声明,并将规划产物适配为模板生成所需视图。
8
11
  */
9
- export function toSkillActionItems(actions) {
10
- return actions;
11
- }
12
- /**
13
- * 将规划产物中的 Playbook 列表适配为模板所需的 PlaybookDefinition 视图。
14
- */
15
- export function toPlaybookDefinitions(playbooks) {
16
- return playbooks;
17
- }
18
12
  /**
19
13
  * 检索单个 Action 项目目录中已存在的 SKILL.md 文件。
20
14
  */
@@ -134,7 +128,7 @@ export function resolveCustomSkillDeclaration(options) {
134
128
  * 复制或生成 SKILL.md 到目标目录。
135
129
  * 返回实际复用的已有 SKILL.md 路径(若发生复用)。
136
130
  */
137
- export function writeSkillMd(root, destDir, plan, options, generate) {
131
+ export function writeSkillMd(root, destDir, options, generate) {
138
132
  const existingSkill = options.skipSkillMd
139
133
  ? undefined
140
134
  : findExistingSingleSkillMd(root, options.skillMdPath, options.pkgSlug);
package/dist/types.d.ts CHANGED
@@ -206,6 +206,25 @@ export interface BuildOptions {
206
206
  /** 是否跳过本地相对依赖完整性校验(默认 false) */
207
207
  skipDependencyValidation?: boolean;
208
208
  }
209
+ /**
210
+ * 基于已规划依赖图的目录型交付产物生成选项。
211
+ */
212
+ export interface BuildProjectWithPlanOptions {
213
+ /** 目标项目根目录绝对路径(可选,缺省时使用规划内 projectRoot) */
214
+ projectRoot?: string;
215
+ /** 产物输出目录路径(默认输出至 dist 目录) */
216
+ outDir?: string;
217
+ /** 输出路径别名 */
218
+ outfile?: string;
219
+ /** 是否生成标准 zip 压缩归档交付产物 */
220
+ archive?: boolean;
221
+ /** 是否在干净暂存目录中物化锁定的生产依赖 */
222
+ vendorDeps?: boolean;
223
+ /** 是否允许执行依赖安装生命周期脚本(默认 false) */
224
+ allowInstallScripts?: boolean;
225
+ /** 是否强制要求构建可复现(默认 false) */
226
+ requireReproducible?: boolean;
227
+ }
209
228
  /**
210
229
  * 目录型构建产物结果描述。
211
230
  */
@@ -357,26 +376,6 @@ export interface SkillExportResult {
357
376
  * 导出 Skill 结果契约(对齐标准接口命名)。
358
377
  */
359
378
  export type ExportSkillResult = SkillExportResult;
360
- /**
361
- * 批量 Skill 导出选项。
362
- */
363
- export interface BatchSkillExportOptions extends Omit<SkillExporterOptions, "projectRoot"> {
364
- /** 待导出的项目根目录绝对路径列表 */
365
- projectRoots: string[];
366
- }
367
- /**
368
- * 批量 Skill 导出结果。
369
- */
370
- export interface BatchSkillExportResult {
371
- /** 各包独立导出结果列表 */
372
- results: SkillExportResult[];
373
- /** 批量导出根目录 */
374
- outDir: string;
375
- /** 导出的总 Action 数量 */
376
- totalActions: number;
377
- /** 导出的总 Playbook 数量 */
378
- totalPlaybooks: number;
379
- }
380
379
  /**
381
380
  * 复合 Skill 套件导出选项。
382
381
  */
@@ -0,0 +1,5 @@
1
+ import type { SelectionPlan } from "./types.js";
2
+ /**
3
+ * 物化锁定生产依赖到产物 node_modules 目录(vendorDeps 开启时)。
4
+ */
5
+ export declare function vendorDependencies(root: string, stagingDir: string, plan: SelectionPlan): void;
package/dist/vendor.js ADDED
@@ -0,0 +1,76 @@
1
+ import { copyFileSync, existsSync, lstatSync, mkdirSync, readdirSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ /**
4
+ * 物化锁定生产依赖到产物 node_modules 目录(vendorDeps 开启时)。
5
+ */
6
+ export function vendorDependencies(root, stagingDir, plan) {
7
+ const destNodeModules = join(stagingDir, "node_modules");
8
+ mkdirSync(destNodeModules, { recursive: true });
9
+ // 尝试复制本地已解析的生产依赖
10
+ const sourceCandidates = [
11
+ join(root, "node_modules"),
12
+ resolve(import.meta.dirname, "../../../node_modules"),
13
+ ];
14
+ // 嵌套 node_modules 不再跳过:多版本共存时传递依赖必须完整物化,
15
+ // 否则产物运行时 Cannot find module 且构建阶段无任何警告;
16
+ // 保留 .git、.bin 等目录排除(.bin 为平台相关的命令行 shim,不参与运行时模块解析)
17
+ const copyVendorPackage = (srcDir, destDir) => {
18
+ if (!existsSync(srcDir))
19
+ return;
20
+ mkdirSync(destDir, { recursive: true });
21
+ const entries = readdirSync(srcDir);
22
+ for (const entry of entries) {
23
+ if (entry === ".git" ||
24
+ entry === ".actiondock" ||
25
+ entry === ".bin" ||
26
+ entry === "test" ||
27
+ entry === "tests") {
28
+ continue;
29
+ }
30
+ const srcPath = join(srcDir, entry);
31
+ const destPath = join(destDir, entry);
32
+ try {
33
+ const stat = lstatSync(srcPath);
34
+ if (stat.isSymbolicLink()) {
35
+ // 软链接不进行递归物理穿越,避免链接环路死锁
36
+ continue;
37
+ }
38
+ if (stat.isDirectory()) {
39
+ copyVendorPackage(srcPath, destPath);
40
+ }
41
+ else if (stat.isFile()) {
42
+ copyFileSync(srcPath, destPath);
43
+ }
44
+ }
45
+ catch (err) {
46
+ // 防御与透明:记录系统读取异常日志
47
+ console.warn(`[actiondock] Warning: Failed to vendor file ${srcPath}: ${err instanceof Error ? err.message : String(err)}`);
48
+ }
49
+ }
50
+ };
51
+ for (const dep of plan.dependencies.external) {
52
+ if (dep.isDev)
53
+ continue;
54
+ for (const baseModules of sourceCandidates) {
55
+ const srcDep = join(baseModules, dep.name);
56
+ if (existsSync(srcDep)) {
57
+ const targetDep = join(destNodeModules, dep.name);
58
+ mkdirSync(dirname(targetDep), { recursive: true });
59
+ copyVendorPackage(srcDep, targetDep);
60
+ break;
61
+ }
62
+ }
63
+ }
64
+ // 复制 ActionDock 内部依赖
65
+ for (const internalPkg of ["@actiondock/core", "@actiondock/sdk"]) {
66
+ for (const baseModules of sourceCandidates) {
67
+ const srcDep = join(baseModules, internalPkg);
68
+ if (existsSync(srcDep)) {
69
+ const targetDep = join(destNodeModules, internalPkg);
70
+ mkdirSync(dirname(targetDep), { recursive: true });
71
+ copyVendorPackage(srcDep, targetDep);
72
+ break;
73
+ }
74
+ }
75
+ }
76
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actiondock/builder",
3
- "version": "2.9.1",
3
+ "version": "2.11.0",
4
4
  "description": "ActionDock Build Planner, distribution packaging, and Skill exporter",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -29,8 +29,8 @@
29
29
  "test": "node ../../scripts/run-tests.ts"
30
30
  },
31
31
  "dependencies": {
32
- "@actiondock/core": "^2.9.1",
33
- "@actiondock/sdk": "^2.9.1"
32
+ "@actiondock/core": "^2.11.0",
33
+ "@actiondock/sdk": "^2.11.0"
34
34
  },
35
35
  "keywords": [
36
36
  "actiondock",
@@ -1,13 +0,0 @@
1
- import type { BatchSkillExportOptions, BatchSkillExportResult, SkillExportResult, SkillExporterOptions } from "./types.js";
2
- /**
3
- * 批量 Skill 导出装配层。
4
- *
5
- * 职责:按项目根目录列表逐一执行单包导出并聚合统计结果,
6
- * 目录名冲突时回退为原始 Package ID 的安全形式。
7
- */
8
- /** 单包导出回调:由 SkillExporter 注入自身导出能力,避免模块间循环依赖 */
9
- export type SingleSkillExporter = (options: SkillExporterOptions) => Promise<SkillExportResult>;
10
- /**
11
- * 执行批量导出多个 Skill。
12
- */
13
- export declare function exportBatchImpl(options: BatchSkillExportOptions, exportSingle: SingleSkillExporter): Promise<BatchSkillExportResult>;
@@ -1,37 +0,0 @@
1
- import { mkdirSync } from "node:fs";
2
- import { join, resolve } from "node:path";
3
- import { loadProjectConfig } from "@actiondock/core";
4
- import { BuilderError } from "./errors.js";
5
- import { allocatePackageDirName } from "./manifest.js";
6
- /**
7
- * 执行批量导出多个 Skill。
8
- */
9
- export async function exportBatchImpl(options, exportSingle) {
10
- if (!options.projectRoots || options.projectRoots.length === 0) {
11
- throw new BuilderError("No project roots provided for batch skill export.");
12
- }
13
- const results = [];
14
- const baseOutDir = resolve(options.outDir || join(process.cwd(), "dist", "skills"));
15
- mkdirSync(baseOutDir, { recursive: true });
16
- const usedDirNames = new Set();
17
- for (const projectRoot of options.projectRoots) {
18
- const config = loadProjectConfig(projectRoot);
19
- // 目录名分配统一复用 manifest 的 allocatePackageDirName,回退后仍冲突会追加数字后缀
20
- const pkgSlug = allocatePackageDirName(usedDirNames, config.id);
21
- const pkgOutDir = join(baseOutDir, `${pkgSlug}-skill`);
22
- const res = await exportSingle({
23
- ...options,
24
- projectRoot,
25
- outDir: pkgOutDir,
26
- });
27
- results.push(res);
28
- }
29
- const totalActions = results.reduce((sum, r) => sum + r.actionsCount, 0);
30
- const totalPlaybooks = results.reduce((sum, r) => sum + r.playbooksCount, 0);
31
- return {
32
- results,
33
- outDir: baseOutDir,
34
- totalActions,
35
- totalPlaybooks,
36
- };
37
- }
@@ -1,11 +0,0 @@
1
- import type { SelectionPlan, SkillExporterOptions, SkillExportResult } from "./types.js";
2
- /**
3
- * Node 目录型 Skill 导出装配层。
4
- *
5
- * 职责:复用 Node 目录型构建产出可执行交付目录,
6
- * 并在目录内生成调用该入口的 SKILL.md 说明书。
7
- */
8
- /**
9
- * 执行 Node 目录型 Skill 导出。
10
- */
11
- export declare function exportNodeSkill(root: string, targetSkillDir: string, plan: SelectionPlan, options: SkillExporterOptions, pkgSlug: string): Promise<SkillExportResult>;