@actiondock/cli 2.0.10 → 2.0.11

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
@@ -24271,6 +24271,10 @@ app.run(process.argv.slice(2)).catch((err: unknown) => {
24271
24271
  });
24272
24272
  `;
24273
24273
  }
24274
+ // ../core/src/catalog/action-index.ts
24275
+ import { existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
24276
+ import { join as join3 } from "path";
24277
+
24274
24278
  // ../core/src/project/manifest.ts
24275
24279
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
24276
24280
  import { join as join2, relative } from "path";
@@ -24926,11 +24930,176 @@ async function syncManifest(projectRoot, options = {}) {
24926
24930
  unchanged
24927
24931
  };
24928
24932
  }
24933
+
24934
+ // ../core/src/catalog/action-index.ts
24935
+ class ActionIndex {
24936
+ actions = new Map;
24937
+ constructor(snapshot) {
24938
+ this.indexSnapshot(snapshot);
24939
+ }
24940
+ indexSnapshot(snapshot) {
24941
+ for (const [packageId, pkg] of snapshot.packages) {
24942
+ const manifest = loadManifest(pkg.projectRoot);
24943
+ if (manifest && manifest.actions) {
24944
+ for (const [actionId, item] of Object.entries(manifest.actions)) {
24945
+ const indexed = {
24946
+ packageId,
24947
+ actionId,
24948
+ contract: {
24949
+ id: actionId,
24950
+ description: item.description,
24951
+ inputSchema: item.inputSchema,
24952
+ outputSchema: item.outputSchema,
24953
+ uses: item.uses,
24954
+ tags: item.tags,
24955
+ annotations: item.annotations
24956
+ },
24957
+ entry: item.entry,
24958
+ projectRoot: pkg.projectRoot
24959
+ };
24960
+ this.add(indexed);
24961
+ }
24962
+ } else {
24963
+ const actionsDir = join3(pkg.projectRoot, pkg.config.actionsDir || "actions");
24964
+ if (existsSync3(actionsDir)) {
24965
+ try {
24966
+ const files = readdirSync2(actionsDir);
24967
+ for (const file of files) {
24968
+ if (file.endsWith(".ts") || file.endsWith(".js")) {
24969
+ const actionId = file.replace(/\.(ts|js)$/, "");
24970
+ const indexed = {
24971
+ packageId,
24972
+ actionId,
24973
+ contract: {
24974
+ id: actionId
24975
+ },
24976
+ entry: join3(pkg.config.actionsDir || "actions", file),
24977
+ projectRoot: pkg.projectRoot
24978
+ };
24979
+ this.add(indexed);
24980
+ }
24981
+ }
24982
+ } catch {}
24983
+ }
24984
+ }
24985
+ }
24986
+ }
24987
+ add(action) {
24988
+ let list = this.actions.get(action.actionId);
24989
+ if (!list) {
24990
+ list = [];
24991
+ this.actions.set(action.actionId, list);
24992
+ }
24993
+ list.push(action);
24994
+ }
24995
+ list(packageId) {
24996
+ const all = [];
24997
+ for (const [_, list] of this.actions) {
24998
+ for (const action of list) {
24999
+ if (!packageId || action.packageId === packageId) {
25000
+ all.push(action);
25001
+ }
25002
+ }
25003
+ }
25004
+ return all;
25005
+ }
25006
+ find(actionId, packageId) {
25007
+ const matches = this.actions.get(actionId) || [];
25008
+ if (packageId) {
25009
+ return matches.filter((a) => a.packageId === packageId);
25010
+ }
25011
+ return matches;
25012
+ }
25013
+ }
25014
+ // ../core/src/catalog/action-resolver.ts
25015
+ class ActionResolver {
25016
+ snapshot;
25017
+ index;
25018
+ constructor(snapshot, index) {
25019
+ this.snapshot = snapshot;
25020
+ this.index = index || new ActionIndex(snapshot);
25021
+ }
25022
+ static parseRef(refStringOrObj) {
25023
+ if (typeof refStringOrObj === "object") {
25024
+ return refStringOrObj;
25025
+ }
25026
+ const str = refStringOrObj.trim();
25027
+ if (str.includes("/")) {
25028
+ const lastSlashIndex = str.lastIndexOf("/");
25029
+ const packageId = str.slice(0, lastSlashIndex);
25030
+ const actionId = str.slice(lastSlashIndex + 1);
25031
+ if (actionId.includes(":") || actionId.includes("/") || actionId.includes("..")) {
25032
+ throw new Error(`Invalid action identifier: '${actionId}'`);
25033
+ }
25034
+ return { packageId, actionId };
25035
+ }
25036
+ if (str.includes(":")) {
25037
+ const parts = str.split(":");
25038
+ throw new Error(`Legacy syntax '${str}' is deprecated. Please use '${parts.join("/")}' instead.`);
25039
+ }
25040
+ return { actionId: str };
25041
+ }
25042
+ resolve(refStringOrObj, options = {}) {
25043
+ const ref = ActionResolver.parseRef(refStringOrObj);
25044
+ if (ref.packageId) {
25045
+ const matches = this.index.find(ref.actionId, ref.packageId);
25046
+ if (matches.length === 0) {
25047
+ throw new Error(`ACTION_NOT_FOUND: Action '${ref.actionId}' not found in package '${ref.packageId}'`);
25048
+ }
25049
+ const matched = matches[0];
25050
+ const pkg = this.snapshot.packages.get(matched.packageId);
25051
+ return {
25052
+ resolved: {
25053
+ packageId: matched.packageId,
25054
+ packageInstanceId: pkg?.packageInstanceId || matched.packageId,
25055
+ actionId: matched.actionId,
25056
+ generationId: this.snapshot.generationId
25057
+ },
25058
+ action: matched
25059
+ };
25060
+ }
25061
+ if (options.currentPackageId) {
25062
+ const inCurrent = this.index.find(ref.actionId, options.currentPackageId);
25063
+ if (inCurrent.length > 0) {
25064
+ const matched = inCurrent[0];
25065
+ const pkg = this.snapshot.packages.get(matched.packageId);
25066
+ return {
25067
+ resolved: {
25068
+ packageId: matched.packageId,
25069
+ packageInstanceId: pkg?.packageInstanceId || matched.packageId,
25070
+ actionId: matched.actionId,
25071
+ generationId: this.snapshot.generationId
25072
+ },
25073
+ action: matched
25074
+ };
25075
+ }
25076
+ }
25077
+ const allMatches = this.index.find(ref.actionId);
25078
+ if (allMatches.length === 0) {
25079
+ throw new Error(`ACTION_NOT_FOUND: Action '${ref.actionId}' not found in any linked package`);
25080
+ }
25081
+ if (allMatches.length > 1) {
25082
+ const candidates = allMatches.map((m) => `${m.packageId}/${m.actionId}`).join(", ");
25083
+ throw new Error(`AMBIGUOUS_ACTION_REF: Action '${ref.actionId}' is provided by multiple packages: ${candidates}. Please specify the package name.`);
25084
+ }
25085
+ const matched = allMatches[0];
25086
+ const pkg = this.snapshot.packages.get(matched.packageId);
25087
+ return {
25088
+ resolved: {
25089
+ packageId: matched.packageId,
25090
+ packageInstanceId: pkg?.packageInstanceId || matched.packageId,
25091
+ actionId: matched.actionId,
25092
+ generationId: this.snapshot.generationId
25093
+ },
25094
+ action: matched
25095
+ };
25096
+ }
25097
+ }
24929
25098
  // ../core/src/utils/index.ts
24930
25099
  import { homedir as homedir2 } from "os";
24931
25100
  // ../sdk/src/cli.ts
24932
- import { existsSync as existsSync3 } from "fs";
24933
- import { delimiter, join as join3 } from "path";
25101
+ import { existsSync as existsSync4 } from "fs";
25102
+ import { delimiter, join as join4 } from "path";
24934
25103
  function findExecutable(command) {
24935
25104
  if (typeof globalThis.Bun !== "undefined" && typeof globalThis.Bun.which === "function") {
24936
25105
  try {
@@ -24941,7 +25110,7 @@ function findExecutable(command) {
24941
25110
  }
24942
25111
  const hasPathSep = command.includes("/") || command.includes("\\");
24943
25112
  if (hasPathSep) {
24944
- return existsSync3(command) ? command : null;
25113
+ return existsSync4(command) ? command : null;
24945
25114
  }
24946
25115
  const pathEnv = process.env.PATH || "";
24947
25116
  const dirs = pathEnv.split(delimiter);
@@ -24951,8 +25120,8 @@ function findExecutable(command) {
24951
25120
  if (!dir)
24952
25121
  continue;
24953
25122
  for (const ext of pathext) {
24954
- const candidate = join3(dir, isWindows && !command.includes(".") ? command + ext : command);
24955
- if (existsSync3(candidate)) {
25123
+ const candidate = join4(dir, isWindows && !command.includes(".") ? command + ext : command);
25124
+ if (existsSync4(candidate)) {
24956
25125
  return candidate;
24957
25126
  }
24958
25127
  }
@@ -25002,8 +25171,8 @@ function getActionDockHome(customHome) {
25002
25171
  return customHome || process.env.ACTIONDOCK_HOME || homedir2();
25003
25172
  }
25004
25173
  // ../core/src/registry/registry.ts
25005
- import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
25006
- import { basename as basename2, dirname as dirname2, join as join4, resolve as resolve3 } from "path";
25174
+ import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync3, readdirSync as readdirSync3, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
25175
+ import { basename as basename2, dirname as dirname2, join as join5, resolve as resolve3 } from "path";
25007
25176
  var IGNORED_SCAN_DIRS = new Set([
25008
25177
  "node_modules",
25009
25178
  ".git",
@@ -25022,7 +25191,7 @@ function discoverProjects(dir, maxDepth = 3) {
25022
25191
  if (currentDepth > maxDepth)
25023
25192
  return;
25024
25193
  try {
25025
- const entries = readdirSync2(currentDir, { withFileTypes: true });
25194
+ const entries = readdirSync3(currentDir, { withFileTypes: true });
25026
25195
  const hasActiondock = entries.some((e) => e.isFile() && e.name === "actiondock.json");
25027
25196
  if (hasActiondock && currentDir !== resolvedDir) {
25028
25197
  results.push(currentDir);
@@ -25030,7 +25199,7 @@ function discoverProjects(dir, maxDepth = 3) {
25030
25199
  }
25031
25200
  for (const entry of entries) {
25032
25201
  if (entry.isDirectory() && !IGNORED_SCAN_DIRS.has(entry.name)) {
25033
- walk(join4(currentDir, entry.name), currentDepth + 1);
25202
+ walk(join5(currentDir, entry.name), currentDepth + 1);
25034
25203
  }
25035
25204
  }
25036
25205
  } catch {}
@@ -25040,11 +25209,11 @@ function discoverProjects(dir, maxDepth = 3) {
25040
25209
  }
25041
25210
  function getRegistryFilePath(customHome) {
25042
25211
  const baseDir = getActionDockHome(customHome);
25043
- return join4(baseDir, ".actiondock", "registry.json");
25212
+ return join5(baseDir, ".actiondock", "registry.json");
25044
25213
  }
25045
25214
  function loadRegistry(customHome) {
25046
25215
  const filePath = getRegistryFilePath(customHome);
25047
- if (!existsSync4(filePath)) {
25216
+ if (!existsSync5(filePath)) {
25048
25217
  return { version: "2.0.0", packages: {}, workspaces: {} };
25049
25218
  }
25050
25219
  try {
@@ -25070,7 +25239,7 @@ function saveRegistry(data, customHome) {
25070
25239
  }
25071
25240
  function linkPackage(targetPath = process.cwd(), customHome, options) {
25072
25241
  const absPath = resolve3(targetPath);
25073
- const directHasConfig = existsSync4(join4(absPath, "actiondock.json"));
25242
+ const directHasConfig = existsSync5(join5(absPath, "actiondock.json"));
25074
25243
  if (directHasConfig && !options?.recursive) {
25075
25244
  const config = loadProjectConfig(absPath);
25076
25245
  const registry = loadRegistry(customHome);
@@ -25231,7 +25400,7 @@ function listLinkedPackages(customHome) {
25231
25400
  const result = { ...registry.packages };
25232
25401
  if (registry.workspaces) {
25233
25402
  for (const ws of Object.values(registry.workspaces)) {
25234
- if (!existsSync4(ws.path))
25403
+ if (!existsSync5(ws.path))
25235
25404
  continue;
25236
25405
  const discovered = discoverProjects(ws.path);
25237
25406
  for (const root of discovered) {
@@ -25265,6 +25434,110 @@ async function projectHasAction(projectRoot, actionsDir, actionId) {
25265
25434
  return false;
25266
25435
  }
25267
25436
  }
25437
+ function projectHasActionSync(projectRoot, actionsDir, actionId) {
25438
+ const manifest = loadManifest(projectRoot);
25439
+ if (manifest?.actions && actionId in manifest.actions) {
25440
+ return true;
25441
+ }
25442
+ const dir = join5(projectRoot, actionsDir || "actions");
25443
+ if (!existsSync5(dir)) {
25444
+ return false;
25445
+ }
25446
+ if (actionId.includes("..") || actionId.startsWith("/") || actionId.startsWith("\\")) {
25447
+ return false;
25448
+ }
25449
+ if (existsSync5(join5(dir, `${actionId}.ts`)) || existsSync5(join5(dir, `${actionId}.js`))) {
25450
+ return true;
25451
+ }
25452
+ const relFile = actionId.replace(/\./g, "/");
25453
+ if (existsSync5(join5(dir, `${relFile}.ts`)) || existsSync5(join5(dir, `${relFile}.js`))) {
25454
+ return true;
25455
+ }
25456
+ return false;
25457
+ }
25458
+ function resolveActionProjectSync(actionIdentifier, cwd = process.cwd(), customHome) {
25459
+ const currentRoot = findProjectRoot(cwd);
25460
+ if (currentRoot) {
25461
+ try {
25462
+ const config = loadProjectConfig(currentRoot);
25463
+ if (projectHasActionSync(currentRoot, config.actionsDir, actionIdentifier)) {
25464
+ return {
25465
+ projectRoot: currentRoot,
25466
+ packageId: config.id,
25467
+ actionId: actionIdentifier
25468
+ };
25469
+ }
25470
+ } catch {}
25471
+ }
25472
+ let targetPackage;
25473
+ let pureActionId = actionIdentifier;
25474
+ if (actionIdentifier.includes("/")) {
25475
+ const slashIdx = actionIdentifier.lastIndexOf("/");
25476
+ targetPackage = actionIdentifier.slice(0, slashIdx);
25477
+ pureActionId = actionIdentifier.slice(slashIdx + 1);
25478
+ } else if (actionIdentifier.includes(":")) {
25479
+ const colonIdx = actionIdentifier.lastIndexOf(":");
25480
+ targetPackage = actionIdentifier.slice(0, colonIdx);
25481
+ pureActionId = actionIdentifier.slice(colonIdx + 1);
25482
+ }
25483
+ const linkedList = listLinkedPackages(customHome);
25484
+ if (targetPackage) {
25485
+ let targetRoot;
25486
+ let targetPkgId = targetPackage;
25487
+ const pkg = linkedList.find((p) => p.id === targetPackage || getPackageSlug(p.id) === targetPackage);
25488
+ if (pkg && existsSync5(pkg.path)) {
25489
+ targetRoot = pkg.path;
25490
+ targetPkgId = pkg.id;
25491
+ } else if (currentRoot) {
25492
+ try {
25493
+ const config = loadProjectConfig(currentRoot);
25494
+ if (config.id === targetPackage || getPackageSlug(config.id) === targetPackage) {
25495
+ targetRoot = currentRoot;
25496
+ targetPkgId = config.id;
25497
+ }
25498
+ } catch {}
25499
+ }
25500
+ if (!targetRoot || !existsSync5(targetRoot)) {
25501
+ throw new Error(`Linked package '${targetPackage}' not found or path no longer exists (${pkg?.path || "unregistered"}). Run 'ad link' in the package directory.`);
25502
+ }
25503
+ const config = loadProjectConfig(targetRoot);
25504
+ if (!projectHasActionSync(targetRoot, config.actionsDir, pureActionId)) {
25505
+ throw new Error(`Action '${pureActionId}' not found in package '${targetPkgId}' (${targetRoot})`);
25506
+ }
25507
+ return {
25508
+ projectRoot: targetRoot,
25509
+ packageId: targetPkgId,
25510
+ actionId: pureActionId
25511
+ };
25512
+ }
25513
+ const matches = [];
25514
+ for (const pkg of linkedList) {
25515
+ if (!existsSync5(pkg.path))
25516
+ continue;
25517
+ try {
25518
+ const config = loadProjectConfig(pkg.path);
25519
+ if (projectHasActionSync(pkg.path, config.actionsDir, actionIdentifier)) {
25520
+ matches.push({ entry: pkg, actionId: actionIdentifier });
25521
+ }
25522
+ } catch {}
25523
+ }
25524
+ if (matches.length === 1) {
25525
+ return {
25526
+ projectRoot: matches[0].entry.path,
25527
+ packageId: matches[0].entry.id,
25528
+ actionId: matches[0].actionId
25529
+ };
25530
+ }
25531
+ if (matches.length > 1) {
25532
+ const pkgList = matches.map((m) => `'${m.entry.id}'`).join(", ");
25533
+ throw new Error(`Action '${actionIdentifier}' is provided by multiple linked packages: ${pkgList}. Please specify using '<package-id>/${actionIdentifier}'.`);
25534
+ }
25535
+ if (currentRoot) {
25536
+ throw new Error(`Action '${actionIdentifier}' not found in current project or any linked packages`);
25537
+ } else {
25538
+ throw new Error(`Action '${actionIdentifier}' not found. You are not in an ActionDock project, and no linked package provides '${actionIdentifier}'. Use 'ad link' to register your package.`);
25539
+ }
25540
+ }
25268
25541
  async function resolveActionProject(actionIdentifier, cwd = process.cwd(), customHome) {
25269
25542
  const currentRoot = findProjectRoot(cwd);
25270
25543
  if (currentRoot) {
@@ -25282,11 +25555,11 @@ async function resolveActionProject(actionIdentifier, cwd = process.cwd(), custo
25282
25555
  let targetPackage;
25283
25556
  let pureActionId = actionIdentifier;
25284
25557
  if (actionIdentifier.includes("/")) {
25285
- const slashIdx = actionIdentifier.indexOf("/");
25558
+ const slashIdx = actionIdentifier.lastIndexOf("/");
25286
25559
  targetPackage = actionIdentifier.slice(0, slashIdx);
25287
25560
  pureActionId = actionIdentifier.slice(slashIdx + 1);
25288
25561
  } else if (actionIdentifier.includes(":")) {
25289
- const colonIdx = actionIdentifier.indexOf(":");
25562
+ const colonIdx = actionIdentifier.lastIndexOf(":");
25290
25563
  targetPackage = actionIdentifier.slice(0, colonIdx);
25291
25564
  pureActionId = actionIdentifier.slice(colonIdx + 1);
25292
25565
  }
@@ -25295,7 +25568,7 @@ async function resolveActionProject(actionIdentifier, cwd = process.cwd(), custo
25295
25568
  let targetRoot;
25296
25569
  let targetPkgId = targetPackage;
25297
25570
  const pkg = linkedList.find((p) => p.id === targetPackage || getPackageSlug(p.id) === targetPackage);
25298
- if (pkg && existsSync4(pkg.path)) {
25571
+ if (pkg && existsSync5(pkg.path)) {
25299
25572
  targetRoot = pkg.path;
25300
25573
  targetPkgId = pkg.id;
25301
25574
  } else if (currentRoot) {
@@ -25307,7 +25580,7 @@ async function resolveActionProject(actionIdentifier, cwd = process.cwd(), custo
25307
25580
  }
25308
25581
  } catch {}
25309
25582
  }
25310
- if (!targetRoot || !existsSync4(targetRoot)) {
25583
+ if (!targetRoot || !existsSync5(targetRoot)) {
25311
25584
  throw new Error(`Linked package '${targetPackage}' not found or path no longer exists (${pkg?.path || "unregistered"}). Run 'ad link' in the package directory.`);
25312
25585
  }
25313
25586
  const config = loadProjectConfig(targetRoot);
@@ -25322,7 +25595,7 @@ async function resolveActionProject(actionIdentifier, cwd = process.cwd(), custo
25322
25595
  }
25323
25596
  const matches = [];
25324
25597
  for (const pkg of linkedList) {
25325
- if (!existsSync4(pkg.path))
25598
+ if (!existsSync5(pkg.path))
25326
25599
  continue;
25327
25600
  try {
25328
25601
  const config = loadProjectConfig(pkg.path);
@@ -25354,11 +25627,11 @@ function resolvePackageRoot(packageIdOrPath, cwd, customHome) {
25354
25627
  }
25355
25628
  const baseDir = cwd || process.cwd();
25356
25629
  const resolvedPath = resolve3(baseDir, packageIdOrPath);
25357
- if (existsSync4(resolvedPath)) {
25630
+ if (existsSync5(resolvedPath)) {
25358
25631
  try {
25359
25632
  const stat = statSync2(resolvedPath);
25360
25633
  const targetDir = stat.isDirectory() ? resolvedPath : dirname2(resolvedPath);
25361
- if (existsSync4(join4(targetDir, "actiondock.json"))) {
25634
+ if (existsSync5(join5(targetDir, "actiondock.json"))) {
25362
25635
  return targetDir;
25363
25636
  }
25364
25637
  const parentRoot = findProjectRoot(targetDir);
@@ -25419,7 +25692,7 @@ function resolvePlaybookProject(playbookIdentifier, cwd = process.cwd(), customH
25419
25692
  let targetRoot;
25420
25693
  let targetPkgId = targetPackage;
25421
25694
  const pkg = linkedList.find((p) => p.id === targetPackage || getPackageSlug(p.id) === targetPackage);
25422
- if (pkg && existsSync4(pkg.path)) {
25695
+ if (pkg && existsSync5(pkg.path)) {
25423
25696
  targetRoot = pkg.path;
25424
25697
  targetPkgId = pkg.id;
25425
25698
  } else if (currentRoot) {
@@ -25431,7 +25704,7 @@ function resolvePlaybookProject(playbookIdentifier, cwd = process.cwd(), customH
25431
25704
  }
25432
25705
  } catch {}
25433
25706
  }
25434
- if (!targetRoot || !existsSync4(targetRoot)) {
25707
+ if (!targetRoot || !existsSync5(targetRoot)) {
25435
25708
  throw new Error(`Linked package '${targetPackage}' not found or path no longer exists (${pkg?.path || "unregistered"}). Run 'ad link' in the package directory.`);
25436
25709
  }
25437
25710
  const config = loadProjectConfig(targetRoot);
@@ -25449,7 +25722,7 @@ function resolvePlaybookProject(playbookIdentifier, cwd = process.cwd(), customH
25449
25722
  }
25450
25723
  const matches = [];
25451
25724
  for (const pkg of linkedList) {
25452
- if (!existsSync4(pkg.path))
25725
+ if (!existsSync5(pkg.path))
25453
25726
  continue;
25454
25727
  try {
25455
25728
  const config = loadProjectConfig(pkg.path);
@@ -25489,7 +25762,7 @@ function getRegistryStatus(customHome) {
25489
25762
  const seenPackageIds = new Set;
25490
25763
  if (registry.workspaces) {
25491
25764
  for (const [wsPath, wsEntry] of Object.entries(registry.workspaces)) {
25492
- const isWsActive = existsSync4(wsPath);
25765
+ const isWsActive = existsSync5(wsPath);
25493
25766
  if (!isWsActive) {
25494
25767
  staleCount++;
25495
25768
  workspaces.push({
@@ -25534,7 +25807,7 @@ function getRegistryStatus(customHome) {
25534
25807
  if (seenPackageIds.has(pkgId)) {
25535
25808
  continue;
25536
25809
  }
25537
- const isPkgActive = existsSync4(pkgEntry.path);
25810
+ const isPkgActive = existsSync5(pkgEntry.path);
25538
25811
  if (!isPkgActive) {
25539
25812
  staleCount++;
25540
25813
  packages.push({
@@ -25570,14 +25843,14 @@ function pruneRegistry(customHome) {
25570
25843
  const prunedPackages = [];
25571
25844
  if (registry.workspaces) {
25572
25845
  for (const [wsPath, wsEntry] of Object.entries(registry.workspaces)) {
25573
- if (!existsSync4(wsPath)) {
25846
+ if (!existsSync5(wsPath)) {
25574
25847
  prunedWorkspaces.push(wsEntry);
25575
25848
  delete registry.workspaces[wsPath];
25576
25849
  }
25577
25850
  }
25578
25851
  }
25579
25852
  for (const [pkgId, pkgEntry] of Object.entries(registry.packages)) {
25580
- if (!existsSync4(pkgEntry.path)) {
25853
+ if (!existsSync5(pkgEntry.path)) {
25581
25854
  prunedPackages.push(pkgEntry);
25582
25855
  delete registry.packages[pkgId];
25583
25856
  }
@@ -25591,14 +25864,14 @@ function pruneRegistry(customHome) {
25591
25864
  };
25592
25865
  }
25593
25866
  // ../core/src/doctor/doctor.ts
25594
- import { existsSync as existsSync6, statSync as statSync3 } from "fs";
25595
- import { join as join6, relative as relative2 } from "path";
25867
+ import { existsSync as existsSync7, statSync as statSync3 } from "fs";
25868
+ import { join as join7, relative as relative2 } from "path";
25596
25869
 
25597
25870
  // ../core/src/storage/index.ts
25598
- import { join as join5 } from "path";
25871
+ import { join as join6 } from "path";
25599
25872
 
25600
25873
  // ../core/src/storage/sqlite.ts
25601
- import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
25874
+ import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
25602
25875
  import { dirname as dirname3 } from "path";
25603
25876
 
25604
25877
  // ../core/src/runtime/clock.ts
@@ -25742,7 +26015,7 @@ class SqliteRuntimeStorage {
25742
26015
  const dbPath = options.dbPath || ":memory:";
25743
26016
  if (dbPath !== ":memory:") {
25744
26017
  const dir = dirname3(dbPath);
25745
- if (!existsSync5(dir)) {
26018
+ if (!existsSync6(dir)) {
25746
26019
  try {
25747
26020
  mkdirSync2(dir, { recursive: true, mode: 448 });
25748
26021
  chmodSync(dir, 448);
@@ -25750,7 +26023,7 @@ class SqliteRuntimeStorage {
25750
26023
  }
25751
26024
  }
25752
26025
  this.driver = options.driver ?? createDefaultSqliteDriver(dbPath);
25753
- if (dbPath !== ":memory:" && existsSync5(dbPath)) {
26026
+ if (dbPath !== ":memory:" && existsSync6(dbPath)) {
25754
26027
  try {
25755
26028
  chmodSync(dbPath, 384);
25756
26029
  } catch {}
@@ -26254,12 +26527,12 @@ function resolveDatabasePath(packageId, options = {}) {
26254
26527
  return ":memory:";
26255
26528
  }
26256
26529
  if (options.dataDir) {
26257
- return join5(options.dataDir, packageId, "runtime.db");
26530
+ return join6(options.dataDir, packageId, "runtime.db");
26258
26531
  }
26259
26532
  if (options.projectRoot) {
26260
- return join5(options.projectRoot, ".actiondock", "runtime.db");
26533
+ return join6(options.projectRoot, ".actiondock", "runtime.db");
26261
26534
  }
26262
- return join5(getActionDockHome(), ".actiondock", "data", packageId, "runtime.db");
26535
+ return join6(getActionDockHome(), ".actiondock", "data", packageId, "runtime.db");
26263
26536
  }
26264
26537
  function createStorage(packageId, options = {}) {
26265
26538
  const dbPath = resolveDatabasePath(packageId, options);
@@ -26275,7 +26548,7 @@ function createGlobalStorage(customHomeOrOptions, dataDirArg) {
26275
26548
  customHome = customHomeOrOptions;
26276
26549
  dataDir = dataDirArg;
26277
26550
  }
26278
- const dbPath = dataDir ? join5(dataDir, "global.db") : join5(getActionDockHome(customHome), ".actiondock", "global.db");
26551
+ const dbPath = dataDir ? join6(dataDir, "global.db") : join6(getActionDockHome(customHome), ".actiondock", "global.db");
26279
26552
  return new SqliteRuntimeStorage({ dbPath, packageId: "__global__" });
26280
26553
  }
26281
26554
 
@@ -26415,7 +26688,7 @@ async function runDoctorChecks(options) {
26415
26688
  status: "ok",
26416
26689
  message: `Valid (${config.id} v${config.version})`
26417
26690
  });
26418
- const hasSdkInNodeModules = existsSync6(join6(projectRoot, "node_modules", "@actiondock", "sdk")) || existsSync6(join6(projectRoot, "node_modules", "@actiondock", "sdk", "package.json"));
26691
+ const hasSdkInNodeModules = existsSync7(join7(projectRoot, "node_modules", "@actiondock", "sdk")) || existsSync7(join7(projectRoot, "node_modules", "@actiondock", "sdk", "package.json"));
26419
26692
  if (hasSdkInNodeModules) {
26420
26693
  checks.push({
26421
26694
  id: "project.sdk",
@@ -26444,7 +26717,7 @@ async function runDoctorChecks(options) {
26444
26717
  category: "project",
26445
26718
  name: "Project Database",
26446
26719
  status: "ok",
26447
- message: `Database writable at ${join6(projectRoot, ".actiondock", "runtime.db")}`
26720
+ message: `Database writable at ${join7(projectRoot, ".actiondock", "runtime.db")}`
26448
26721
  });
26449
26722
  } catch (err) {
26450
26723
  checks.push({
@@ -26453,7 +26726,7 @@ async function runDoctorChecks(options) {
26453
26726
  name: "Project Database",
26454
26727
  status: "error",
26455
26728
  message: `Failed to write project runtime database: ${err.message}`,
26456
- fix: `Check write permissions for '${join6(projectRoot, ".actiondock")}'`
26729
+ fix: `Check write permissions for '${join7(projectRoot, ".actiondock")}'`
26457
26730
  });
26458
26731
  }
26459
26732
  try {
@@ -26493,10 +26766,10 @@ async function runDoctorChecks(options) {
26493
26766
  });
26494
26767
  }
26495
26768
  try {
26496
- const manifestPath = join6(projectRoot, MANIFEST_FILE_NAME);
26769
+ const manifestPath = join7(projectRoot, MANIFEST_FILE_NAME);
26497
26770
  const manifest = loadManifest(projectRoot);
26498
26771
  const actionFiles = discoverActionFiles(projectRoot, config.actionsDir || "actions");
26499
- if (!existsSync6(manifestPath)) {
26772
+ if (!existsSync7(manifestPath)) {
26500
26773
  if (actionFiles.length > 0) {
26501
26774
  checks.push({
26502
26775
  id: "project.manifest",
@@ -26508,7 +26781,7 @@ async function runDoctorChecks(options) {
26508
26781
  });
26509
26782
  }
26510
26783
  } else if (manifest && manifest.actions) {
26511
- const missingFiles = Object.entries(manifest.actions).filter(([_, item]) => !existsSync6(join6(projectRoot, item.entry))).map(([id, item]) => `${id} (${item.entry})`);
26784
+ const missingFiles = Object.entries(manifest.actions).filter(([_, item]) => !existsSync7(join7(projectRoot, item.entry))).map(([id, item]) => `${id} (${item.entry})`);
26512
26785
  const manifestEntries = new Set(Object.values(manifest.actions).map((a) => a.entry.replace(/\\/g, "/")));
26513
26786
  const untracked = actionFiles.map((f) => relative2(projectRoot, f).replace(/\\/g, "/")).filter((rel) => !manifestEntries.has(rel));
26514
26787
  if (missingFiles.length > 0) {
@@ -26739,6 +27012,7 @@ class InMemoryEventSink {
26739
27012
  var defaultEventSink = new InMemoryEventSink;
26740
27013
 
26741
27014
  // ../core/src/runtime/runner.ts
27015
+ import { existsSync as existsSync8 } from "fs";
26742
27016
  import { randomUUID as randomUUID2 } from "crypto";
26743
27017
 
26744
27018
  // ../core/src/schema/validator.ts
@@ -27241,12 +27515,14 @@ class ActionRunner {
27241
27515
  projectConfig;
27242
27516
  configOverrides;
27243
27517
  actions;
27518
+ actionResolver;
27244
27519
  constructor(options) {
27245
27520
  this.packageId = options.packageId;
27246
27521
  this.storage = options.storage;
27247
27522
  this.projectConfig = options.projectConfig;
27248
27523
  this.configOverrides = options.configOverrides || {};
27249
27524
  this.actions = options.actions || new Map;
27525
+ this.actionResolver = options.actionResolver;
27250
27526
  }
27251
27527
  registerAction(action) {
27252
27528
  this.actions.set(action.id, action);
@@ -27254,6 +27530,60 @@ class ActionRunner {
27254
27530
  getAction(id) {
27255
27531
  return this.actions.get(id);
27256
27532
  }
27533
+ async resolveAction(actionOrRef) {
27534
+ if (typeof actionOrRef === "object" && "run" in actionOrRef && typeof actionOrRef.run === "function") {
27535
+ return actionOrRef;
27536
+ }
27537
+ const ref = actionOrRef;
27538
+ const parsed = ActionResolver.parseRef(ref);
27539
+ const targetActionId = parsed.actionId;
27540
+ const targetPackageId = parsed.packageId;
27541
+ if (targetPackageId && targetPackageId !== this.packageId) {
27542
+ if (this.actions.has(`${targetPackageId}/${targetActionId}`)) {
27543
+ return this.actions.get(`${targetPackageId}/${targetActionId}`);
27544
+ }
27545
+ } else {
27546
+ if (this.actions.has(targetActionId)) {
27547
+ return this.actions.get(targetActionId);
27548
+ }
27549
+ if (this.packageId && this.actions.has(`${this.packageId}/${targetActionId}`)) {
27550
+ return this.actions.get(`${this.packageId}/${targetActionId}`);
27551
+ }
27552
+ }
27553
+ if (this.actionResolver) {
27554
+ const customResolved = await this.actionResolver(ref, this.packageId);
27555
+ if (customResolved) {
27556
+ if (targetPackageId && targetPackageId !== this.packageId) {
27557
+ this.actions.set(`${targetPackageId}/${targetActionId}`, customResolved);
27558
+ } else {
27559
+ this.actions.set(targetActionId, customResolved);
27560
+ if (this.packageId) {
27561
+ this.actions.set(`${this.packageId}/${targetActionId}`, customResolved);
27562
+ }
27563
+ }
27564
+ return customResolved;
27565
+ }
27566
+ }
27567
+ try {
27568
+ const identifier = targetPackageId ? `${targetPackageId}/${targetActionId}` : targetActionId;
27569
+ const resolved = await resolveActionProject(identifier);
27570
+ if (resolved && existsSync8(resolved.projectRoot)) {
27571
+ const config = loadProjectConfig(resolved.projectRoot);
27572
+ const actionsMap = await loadActions(resolved.projectRoot, config.actionsDir, {
27573
+ autoInstall: false
27574
+ });
27575
+ const matched = actionsMap.get(resolved.actionId);
27576
+ if (matched) {
27577
+ this.actions.set(`${resolved.packageId}/${resolved.actionId}`, matched);
27578
+ if (!targetPackageId || resolved.packageId === this.packageId) {
27579
+ this.actions.set(resolved.actionId, matched);
27580
+ }
27581
+ return matched;
27582
+ }
27583
+ }
27584
+ } catch {}
27585
+ return;
27586
+ }
27257
27587
  listActions() {
27258
27588
  return Array.from(this.actions.values());
27259
27589
  }
@@ -27262,27 +27592,30 @@ class ActionRunner {
27262
27592
  const startedAt = new Date().toISOString();
27263
27593
  const callStack = [...options.callStack || []];
27264
27594
  let action;
27265
- if (typeof actionOrId === "string") {
27266
- const found = this.actions.get(actionOrId);
27267
- if (!found) {
27268
- const error = {
27269
- code: "ACTION_NOT_FOUND",
27270
- message: `Action '${actionOrId}' not found in registry`
27271
- };
27272
- return {
27273
- runId,
27274
- result: Promise.resolve({ ok: false, runId, error }),
27275
- cancel: () => false
27276
- };
27277
- }
27278
- action = found;
27279
- } else {
27595
+ let targetActionId;
27596
+ let targetPackageId = this.packageId;
27597
+ if (typeof actionOrId === "object" && "run" in actionOrId && typeof actionOrId.run === "function") {
27280
27598
  action = actionOrId;
27599
+ targetActionId = action.id;
27600
+ } else {
27601
+ const parsed = ActionResolver.parseRef(actionOrId);
27602
+ targetActionId = parsed.actionId;
27603
+ if (parsed.packageId) {
27604
+ targetPackageId = parsed.packageId;
27605
+ }
27606
+ if (parsed.packageId && parsed.packageId !== this.packageId) {
27607
+ action = this.actions.get(`${parsed.packageId}/${targetActionId}`);
27608
+ } else {
27609
+ action = this.actions.get(targetActionId) || (this.packageId ? this.actions.get(`${this.packageId}/${targetActionId}`) : undefined);
27610
+ }
27281
27611
  }
27282
- if (callStack.includes(action.id)) {
27612
+ const isExternal = Boolean(targetPackageId && targetPackageId !== this.packageId);
27613
+ const callKey = isExternal ? `${targetPackageId}/${targetActionId}` : targetActionId;
27614
+ const hasCycle = isExternal ? callStack.includes(callKey) : callStack.includes(callKey) || (this.packageId ? callStack.includes(`${this.packageId}/${targetActionId}`) : false);
27615
+ if (hasCycle) {
27283
27616
  const error = {
27284
27617
  code: "ACTION_CYCLE_DETECTED",
27285
- message: `Cycle detected in action invocation: ${callStack.join(" -> ")} -> ${action.id}`
27618
+ message: `Cycle detected in action invocation: ${callStack.join(" -> ")} -> ${callKey}`
27286
27619
  };
27287
27620
  return {
27288
27621
  runId,
@@ -27290,8 +27623,8 @@ class ActionRunner {
27290
27623
  cancel: () => false
27291
27624
  };
27292
27625
  }
27293
- callStack.push(action.id);
27294
- if (action.inputSchema) {
27626
+ callStack.push(callKey);
27627
+ if (action?.inputSchema) {
27295
27628
  const val = validateSchema(action.inputSchema, input);
27296
27629
  if (!val.valid) {
27297
27630
  const error = {
@@ -27310,9 +27643,9 @@ class ActionRunner {
27310
27643
  id: runId,
27311
27644
  rootRunId: options.rootRunId || options.parentRunId || runId,
27312
27645
  parentRunId: options.parentRunId,
27313
- packageId: this.packageId,
27314
- packageInstanceId: options.packageInstanceId || this.packageId,
27315
- actionId: action.id,
27646
+ packageId: targetPackageId,
27647
+ packageInstanceId: options.packageInstanceId || targetPackageId,
27648
+ actionId: targetActionId,
27316
27649
  generationId: options.generationId || "1",
27317
27650
  ownerId: options.ownerId || "local",
27318
27651
  status: "running",
@@ -27357,7 +27690,7 @@ class ActionRunner {
27357
27690
  signal: controller.signal,
27358
27691
  process: options.process,
27359
27692
  progress: options.progress,
27360
- logger: options.logger || new StderrLogger(action.id),
27693
+ logger: options.logger || new StderrLogger(action?.id || targetActionId),
27361
27694
  onActionInvoke: async (childAction, childInput, parentRunId) => {
27362
27695
  const childResult = await this.execute(childAction, childInput, {
27363
27696
  rootRunId: initialRun.rootRunId,
@@ -27386,16 +27719,40 @@ class ActionRunner {
27386
27719
  });
27387
27720
  const executionPromise = (async () => {
27388
27721
  try {
27722
+ let currentAction = action;
27723
+ if (!currentAction) {
27724
+ currentAction = await this.resolveAction(actionOrId);
27725
+ if (!currentAction) {
27726
+ const error = {
27727
+ code: "ACTION_NOT_FOUND",
27728
+ message: `Action '${targetActionId}' not found in registry or linked packages`
27729
+ };
27730
+ finalizeRun("failed", undefined, error);
27731
+ return { ok: false, runId, error };
27732
+ }
27733
+ if (currentAction.inputSchema) {
27734
+ const val = validateSchema(currentAction.inputSchema, input);
27735
+ if (!val.valid) {
27736
+ const error = {
27737
+ code: "INPUT_VALIDATION_FAILED",
27738
+ message: `Input schema validation failed for action '${currentAction.id}'`,
27739
+ details: val.errors
27740
+ };
27741
+ finalizeRun("failed", undefined, error);
27742
+ return { ok: false, runId, error };
27743
+ }
27744
+ }
27745
+ }
27389
27746
  const rawOutput = await Promise.race([
27390
- Promise.resolve().then(() => action.run(input, ctx)),
27747
+ Promise.resolve().then(() => currentAction.run(input, ctx)),
27391
27748
  abortPromise
27392
27749
  ]);
27393
- if (action.outputSchema) {
27394
- const outVal = validateSchema(action.outputSchema, rawOutput);
27750
+ if (currentAction.outputSchema) {
27751
+ const outVal = validateSchema(currentAction.outputSchema, rawOutput);
27395
27752
  if (!outVal.valid) {
27396
27753
  const error = {
27397
27754
  code: "OUTPUT_VALIDATION_FAILED",
27398
- message: `Output schema validation failed for action '${action.id}'`,
27755
+ message: `Output schema validation failed for action '${currentAction.id}'`,
27399
27756
  details: outVal.errors
27400
27757
  };
27401
27758
  finalizeRun("failed", undefined, error);
@@ -27568,6 +27925,14 @@ ad link "<skill_root>"
27568
27925
 
27569
27926
  > \`ad link\` \u5929\u7136\u5177\u5907\u5E42\u7B49\u6027\uFF0C\u540C\u4E00 Package \u591A\u6B21\u6267\u884C\u4F1A\u76F4\u63A5\u66F4\u65B0\u8DEF\u5F84\uFF0C\u53EF\u5B89\u5168\u91CD\u590D\u8C03\u7528\u3002
27570
27927
 
27928
+ ### \u52A8\u4F5C\u53C2\u6570\u5951\u7EA6\u6309\u9700\u8C03\u9605
27929
+
27930
+ \u5728\u8C03\u7528\u672A\u77E5\u53C2\u6570\u7684 Action \u524D\uFF0C\u53EF\u5728\u7EC8\u7AEF\u6267\u884C\u547D\u4EE4\u6309\u9700\u67E5\u9605\u8BE5 Action \u7684\u8F93\u5165\u8F93\u51FA\u6A21\u5F0F\u4E0E\u8BE6\u7EC6\u8BF4\u660E\uFF1A
27931
+
27932
+ \`\`\`bash
27933
+ ad action show ${pkgId}/${firstAction}
27934
+ \`\`\`
27935
+
27571
27936
  ### \u6267\u884C Action
27572
27937
 
27573
27938
  \u4E3A\u907F\u514D\u591A\u6280\u80FD\u4E4B\u95F4\u7684 Action ID \u547D\u540D\u51B2\u7A81\uFF0C\u5EFA\u8BAE\u7EDF\u4E00\u4F7F\u7528\u5E26\u6709 Package \u524D\u7F00\u7684\u5B8C\u5168\u9650\u5B9A ID\u3002
@@ -27776,70 +28141,106 @@ ${binaryRelPath} state get KEY
27776
28141
  \`\`\`
27777
28142
  `;
27778
28143
  }
27779
- function generateSkillJson(config, actions, binaryNameOrOptions, target = "host", playbooksList = []) {
27780
- let mode = "source";
27781
- let executable;
27782
- let targetPlatform = target;
27783
- let playbooks = playbooksList;
27784
- if (typeof binaryNameOrOptions === "string") {
27785
- mode = "standalone";
27786
- executable = `./bin/${binaryNameOrOptions}`;
27787
- } else if (binaryNameOrOptions && typeof binaryNameOrOptions === "object") {
27788
- mode = binaryNameOrOptions.mode || (binaryNameOrOptions.executable ? "standalone" : "source");
27789
- executable = binaryNameOrOptions.executable;
27790
- targetPlatform = binaryNameOrOptions.target || target;
27791
- if (binaryNameOrOptions.playbooks) {
27792
- playbooks = binaryNameOrOptions.playbooks;
27793
- }
27794
- }
27795
- const manifest = {
27796
- schemaVersion: "2.0.0",
27797
- packageId: config.id,
27798
- name: config.name,
27799
- version: config.version,
27800
- description: config.description,
27801
- mode
27802
- };
27803
- if (mode === "standalone" && executable) {
27804
- manifest.target = targetPlatform;
27805
- manifest.executable = executable;
27806
- }
27807
- manifest.actions = actions.map((a) => {
27808
- const item = {
27809
- id: a.id
27810
- };
27811
- if (a.entry) {
27812
- item.entry = a.entry;
27813
- }
27814
- if (a.description) {
27815
- item.description = a.description;
27816
- }
27817
- if (a.inputSchema !== undefined) {
27818
- item.inputSchema = a.inputSchema;
27819
- }
27820
- if (a.outputSchema !== undefined) {
27821
- item.outputSchema = a.outputSchema;
27822
- }
27823
- if (a.uses) {
27824
- item.uses = a.uses;
27825
- }
27826
- if (a.tags) {
27827
- item.tags = a.tags;
27828
- }
27829
- if (a.annotations) {
27830
- item.annotations = a.annotations;
28144
+ function generateCompositeSkillMd(bundleName, description, packages) {
28145
+ const cleanName = bundleName.replace(/[^a-zA-Z0-9-_]/g, "-").toLowerCase();
28146
+ const samplePkg = packages.find((p) => p.actions && p.actions.length > 0);
28147
+ const sampleActionId = samplePkg ? `${samplePkg.config.id}/${samplePkg.actions[0].id}` : "sample.greet";
28148
+ const actionSections = packages.map((pkg) => {
28149
+ const actList = pkg.actions.map((a) => {
28150
+ const desc = a.description ? `: ${a.description}` : "";
28151
+ return `- \`${pkg.config.id}/${a.id}\`${desc}`;
28152
+ }).join(`
28153
+ `);
28154
+ return `### ${pkg.config.name} (${pkg.config.id})
28155
+ ${actList || "- \u65E0\u53EF\u7528 Action"}`;
28156
+ }).join(`
28157
+
28158
+ `);
28159
+ const playbookEntries = [];
28160
+ for (const pkg of packages) {
28161
+ for (const pb of pkg.playbooks) {
28162
+ const relPath = `packages/${pkg.packageDir}/playbooks/${basename3(pb.filePath)}`;
28163
+ playbookEntries.push(`- [${pb.name || pb.id}](${relPath}): ${pb.description || "\u6807\u51C6\u64CD\u4F5C\u89C4\u7A0B"}`);
27831
28164
  }
27832
- return item;
27833
- });
27834
- if (playbooks && playbooks.length > 0) {
27835
- manifest.playbooks = playbooks.map((p) => ({
27836
- id: p.id,
27837
- description: p.description,
27838
- entry: `playbooks/${basename3(p.filePath)}`
27839
- }));
27840
28165
  }
27841
- manifest.exportedAt = new Date().toISOString();
27842
- return JSON.stringify(manifest, null, 2) + `
28166
+ const playbookSection = playbookEntries.length > 0 ? `## \u63A8\u8350\u64CD\u4F5C\u89C4\u7A0B
28167
+
28168
+ \u6D89\u53CA\u591A\u6B65\u9AA4\u6216\u4E1A\u52A1\u6D41\u7A0B\u65F6\uFF0C\u4F18\u5148\u9075\u5FAA\u4EE5\u4E0B\u539F\u4F4D\u89C4\u7A0B\uFF1A
28169
+
28170
+ ${playbookEntries.join(`
28171
+ `)}
28172
+
28173
+ ---
28174
+ ` : "";
28175
+ return `---
28176
+ name: ${cleanName}
28177
+ description: ${description}
28178
+ ---
28179
+
28180
+ # ${bundleName} \u590D\u5408\u6280\u80FD\u5957\u4EF6
28181
+
28182
+ ${description}
28183
+
28184
+ ## ActionDock \u8FD0\u884C\u65F6\u521D\u59CB\u5316
28185
+
28186
+ \u672C\u6280\u80FD\u4E3A **ActionDock \u590D\u5408\u5DE5\u4F5C\u533A\u6280\u80FD\u5305**\uFF0C\u805A\u5408\u4E86\u591A\u4E2A\u529F\u80FD\u5305\u3002\u667A\u80FD\u4F53\u5728\u521D\u6B21\u8C03\u7528\u6216\u521D\u59CB\u5316\u65F6\uFF0C\u5728\u5F53\u524D\u6280\u80FD\u6839\u76EE\u5F55\u6267\u884C\u6CE8\u518C\u547D\u4EE4\uFF1A
28187
+
28188
+ \`\`\`bash
28189
+ ad link "<skill_root>"
28190
+ \`\`\`
28191
+
28192
+ > \`ad link\` \u4F1A\u81EA\u52A8\u8BC6\u522B\u5E76\u6CE8\u518C\u5DE5\u4F5C\u533A\u4E0B\u7684\u6240\u6709\u5B50\u5305\uFF0C\u4F7F\u5176\u4E2D\u7684 Action \u968F\u65F6\u53EF\u4EE5\u901A\u8FC7\u5B8C\u5168\u9650\u5B9A\u6807\u8BC6\u8C03\u7528\u3002
28193
+
28194
+ ## \u52A8\u4F5C\u53C2\u6570\u5951\u7EA6\u6309\u9700\u8C03\u9605
28195
+
28196
+ \u4E3A\u8282\u7701\u4E0A\u4E0B\u6587\u5F00\u9500\uFF0C\u5404 Action \u7684\u8BE6\u7EC6\u53C2\u6570\u7ED3\u6784\u4E0D\u9759\u6001\u5185\u5D4C\u5728\u8BF4\u660E\u4E66\u4E2D\u3002\u5728\u8C03\u7528\u672A\u77E5\u53C2\u6570\u7684 Action \u524D\uFF0C\u53EF\u5728\u7EC8\u7AEF\u6267\u884C\u547D\u4EE4\u67E5\u9605\u8F93\u5165\u8F93\u51FA\u7EA6\u675F\uFF1A
28197
+
28198
+ \`\`\`bash
28199
+ ad action show ${sampleActionId}
28200
+ \`\`\`
28201
+
28202
+ ## \u53EF\u7528 Action \u5DE5\u5177\u6E05\u5355
28203
+
28204
+ ${actionSections}
28205
+
28206
+ ---
28207
+
28208
+ ${playbookSection}
28209
+ ## \u6807\u51C6\u8C03\u7528\u547D\u4EE4
28210
+
28211
+ \u63A8\u8350\u4F7F\u7528\u53C2\u6570\u6587\u4EF6\u4F20\u9012\u5185\u5BB9\uFF0C\u675C\u7EDD\u7EC8\u7AEF\u5F15\u53F7\u8F6C\u4E49\u95EE\u9898\uFF1A
28212
+
28213
+ \`\`\`bash
28214
+ cat << 'EOF' > /tmp/input.json
28215
+ {
28216
+ "param": "value"
28217
+ }
28218
+ EOF
28219
+ ad run ${sampleActionId} --input-file /tmp/input.json
28220
+ \`\`\`
28221
+
28222
+ ### \u7ED3\u6784\u5316\u54CD\u5E94\u89E3\u6790
28223
+
28224
+ \u6240\u6709 Action \u6267\u884C\u7ED3\u679C\u5747\u5728 \`stdout\` \u8F93\u51FA\u6807\u51C6\u683C\u5F0F\u7684 JSON \u4FE1\u5C01\uFF1A
28225
+
28226
+ \`\`\`json
28227
+ // \u6267\u884C\u6210\u529F\u54CD\u5E94 (ok \u4E3A true)
28228
+ {
28229
+ "ok": true,
28230
+ "runId": "01J...",
28231
+ "data": { ... }
28232
+ }
28233
+
28234
+ // \u6267\u884C\u5931\u8D25\u54CD\u5E94 (ok \u4E3A false)
28235
+ {
28236
+ "ok": false,
28237
+ "runId": "01J...",
28238
+ "error": {
28239
+ "code": "ACTION_EXECUTION_FAILED",
28240
+ "message": "\u9519\u8BEF\u8BE6\u7EC6\u63CF\u8FF0\u4FE1\u606F"
28241
+ }
28242
+ }
28243
+ \`\`\`
27843
28244
  `;
27844
28245
  }
27845
28246
  // ../core/src/filter/intent.ts
@@ -27939,8 +28340,8 @@ function filterByIntent(items, intent, extractors, fallback = true) {
27939
28340
  return filterWithFallbackInfo(items, intent, extractors, fallback).items;
27940
28341
  }
27941
28342
  // ../core/src/profile/manager.ts
27942
- import { chmodSync as chmodSync2, existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
27943
- import { dirname as dirname4, join as join7 } from "path";
28343
+ import { chmodSync as chmodSync2, existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
28344
+ import { dirname as dirname4, join as join8 } from "path";
27944
28345
  var PROFILE_NAME_REGEX = /^[a-zA-Z0-9_\-\.]+$/;
27945
28346
  var DEFAULT_PROFILES_CONFIG = {
27946
28347
  currentProfile: "local",
@@ -27960,11 +28361,11 @@ function normalizeServerUrl(url) {
27960
28361
  }
27961
28362
  function getProfilesFilePath(customHome) {
27962
28363
  const baseDir = getActionDockHome(customHome);
27963
- return join7(baseDir, ".actiondock", "profiles.json");
28364
+ return join8(baseDir, ".actiondock", "profiles.json");
27964
28365
  }
27965
28366
  function loadProfiles(customHome) {
27966
28367
  const filePath = getProfilesFilePath(customHome);
27967
- if (!existsSync7(filePath)) {
28368
+ if (!existsSync9(filePath)) {
27968
28369
  return structuredClone(DEFAULT_PROFILES_CONFIG);
27969
28370
  }
27970
28371
  try {
@@ -28457,11 +28858,11 @@ async function deleteRemoteConfig(serverUrl, key, token, packageId) {
28457
28858
  });
28458
28859
  }
28459
28860
  // ../core/src/project/init.ts
28460
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
28461
- import { basename as basename4, join as join8, resolve as resolve4 } from "path";
28861
+ import { existsSync as existsSync10, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
28862
+ import { basename as basename4, join as join9, resolve as resolve4 } from "path";
28462
28863
  function initProject(targetDir, options = {}) {
28463
28864
  const root = resolve4(targetDir);
28464
- if (!existsSync8(root)) {
28865
+ if (!existsSync10(root)) {
28465
28866
  mkdirSync4(root, { recursive: true });
28466
28867
  }
28467
28868
  const dirName = basename4(root);
@@ -28482,7 +28883,7 @@ function initProject(targetDir, options = {}) {
28482
28883
  }
28483
28884
  }
28484
28885
  };
28485
- writeFileSync4(join8(root, "actiondock.json"), JSON.stringify(actiondockJson, null, 2) + `
28886
+ writeFileSync4(join9(root, "actiondock.json"), JSON.stringify(actiondockJson, null, 2) + `
28486
28887
  `);
28487
28888
  const manifestJson = {
28488
28889
  schemaVersion: 1,
@@ -28514,7 +28915,7 @@ function initProject(targetDir, options = {}) {
28514
28915
  },
28515
28916
  assets: []
28516
28917
  };
28517
- writeFileSync4(join8(root, "actiondock.manifest.json"), JSON.stringify(manifestJson, null, 2) + `
28918
+ writeFileSync4(join9(root, "actiondock.manifest.json"), JSON.stringify(manifestJson, null, 2) + `
28518
28919
  `);
28519
28920
  const packageJson = {
28520
28921
  name: id,
@@ -28528,16 +28929,16 @@ function initProject(targetDir, options = {}) {
28528
28929
  node: ">=22.12.0"
28529
28930
  },
28530
28931
  dependencies: {
28531
- "@actiondock/sdk": "^2.0.10"
28932
+ "@actiondock/sdk": "^2.0.11"
28532
28933
  },
28533
28934
  devDependencies: {
28534
- "@actiondock/testing": "^2.0.10",
28935
+ "@actiondock/testing": "^2.0.11",
28535
28936
  "@types/node": "^22.12.0",
28536
28937
  tsx: "^4.19.0",
28537
28938
  typescript: "^5.7.0"
28538
28939
  }
28539
28940
  };
28540
- writeFileSync4(join8(root, "package.json"), JSON.stringify(packageJson, null, 2) + `
28941
+ writeFileSync4(join9(root, "package.json"), JSON.stringify(packageJson, null, 2) + `
28541
28942
  `);
28542
28943
  const tsconfigJson = {
28543
28944
  compilerOptions: {
@@ -28549,7 +28950,7 @@ function initProject(targetDir, options = {}) {
28549
28950
  types: ["node"]
28550
28951
  }
28551
28952
  };
28552
- writeFileSync4(join8(root, "tsconfig.json"), JSON.stringify(tsconfigJson, null, 2) + `
28953
+ writeFileSync4(join9(root, "tsconfig.json"), JSON.stringify(tsconfigJson, null, 2) + `
28553
28954
  `);
28554
28955
  const gitignore = `.actiondock/
28555
28956
  node_modules/
@@ -28558,8 +28959,8 @@ build/
28558
28959
  *.log
28559
28960
  .env
28560
28961
  `;
28561
- writeFileSync4(join8(root, ".gitignore"), gitignore);
28562
- const actionsDir = join8(root, "actions");
28962
+ writeFileSync4(join9(root, ".gitignore"), gitignore);
28963
+ const actionsDir = join9(root, "actions");
28563
28964
  mkdirSync4(actionsDir, { recursive: true });
28564
28965
  const sampleAction = `import { defineAction } from "@actiondock/sdk";
28565
28966
 
@@ -28602,8 +29003,8 @@ export default defineAction({
28602
29003
  },
28603
29004
  });
28604
29005
  `;
28605
- writeFileSync4(join8(actionsDir, "greet.ts"), sampleAction);
28606
- const playbooksDir = join8(root, "playbooks");
29006
+ writeFileSync4(join9(actionsDir, "greet.ts"), sampleAction);
29007
+ const playbooksDir = join9(root, "playbooks");
28607
29008
  mkdirSync4(playbooksDir, { recursive: true });
28608
29009
  const samplePlaybook = `---
28609
29010
  id: greet-user
@@ -28617,8 +29018,8 @@ actions:
28617
29018
  - Call \`sample.greet\` with the user's name.
28618
29019
  - Confirm the returned greeting message.
28619
29020
  `;
28620
- writeFileSync4(join8(playbooksDir, "greet-user.md"), samplePlaybook);
28621
- const testsDir = join8(root, "tests");
29021
+ writeFileSync4(join9(playbooksDir, "greet-user.md"), samplePlaybook);
29022
+ const testsDir = join9(root, "tests");
28622
29023
  mkdirSync4(testsDir, { recursive: true });
28623
29024
  const sampleTest = `import { describe, it } from "node:test";
28624
29025
  import assert from "node:assert/strict";
@@ -28641,7 +29042,7 @@ describe("greet action", () => {
28641
29042
  });
28642
29043
  });
28643
29044
  `;
28644
- writeFileSync4(join8(testsDir, "greet.test.ts"), sampleTest);
29045
+ writeFileSync4(join9(testsDir, "greet.test.ts"), sampleTest);
28645
29046
  }
28646
29047
  // ../core/src/runtime/execution-manager.ts
28647
29048
  class ExecutionManager {
@@ -28865,7 +29266,7 @@ class ServerRuntimeRegistry {
28865
29266
  }
28866
29267
  }
28867
29268
  // ../core/src/server/routes/common.ts
28868
- import { existsSync as existsSync9 } from "fs";
29269
+ import { existsSync as existsSync11 } from "fs";
28869
29270
  function jsonResponse(data, status = 200, corsHeaders = {}) {
28870
29271
  return new Response(JSON.stringify(data, null, 2), {
28871
29272
  status,
@@ -28929,7 +29330,7 @@ function findRunAcrossStorages(runId, runtimeRegistry, projectRoot, customHome)
28929
29330
  try {
28930
29331
  const linked = listLinkedPackages(customHome);
28931
29332
  for (const pkg of linked) {
28932
- if (!existsSync9(pkg.path))
29333
+ if (!existsSync11(pkg.path))
28933
29334
  continue;
28934
29335
  const storage = runtimeRegistry.getStorage(pkg.id, pkg.path);
28935
29336
  const run = storage.getRun(runId);
@@ -28966,7 +29367,7 @@ async function handleHealthRoute(ctx) {
28966
29367
  return jsonResponse(healthData, 200, corsHeaders);
28967
29368
  }
28968
29369
  // ../core/src/server/routes/info.ts
28969
- import { existsSync as existsSync10 } from "fs";
29370
+ import { existsSync as existsSync12 } from "fs";
28970
29371
  async function handleInfoRoute(ctx) {
28971
29372
  const { req, url, pathname, corsHeaders, projectRoot, customHome, options } = ctx;
28972
29373
  if (pathname !== "/api/v1/info" || req.method !== "GET") {
@@ -29011,7 +29412,7 @@ async function handleInfoRoute(ctx) {
29011
29412
  } else {
29012
29413
  const linked = listLinkedPackages(customHome);
29013
29414
  for (const pkg of linked) {
29014
- if (!existsSync10(pkg.path))
29415
+ if (!existsSync12(pkg.path))
29015
29416
  continue;
29016
29417
  try {
29017
29418
  const config = loadProjectConfig(pkg.path);
@@ -29140,7 +29541,7 @@ async function handleDoctorRoute(ctx) {
29140
29541
  }
29141
29542
  // ../core/src/server/routes/actions.ts
29142
29543
  import { randomUUID as randomUUID4 } from "crypto";
29143
- import { existsSync as existsSync11 } from "fs";
29544
+ import { existsSync as existsSync13 } from "fs";
29144
29545
  async function handleActionsRoutes(ctx) {
29145
29546
  const { req, url, pathname, corsHeaders, projectRoot, customHome, runtimeRegistry, options } = ctx;
29146
29547
  if (pathname === "/api/v1/actions" && req.method === "GET") {
@@ -29161,7 +29562,7 @@ async function handleActionsRoutes(ctx) {
29161
29562
  for (const pkg of linked) {
29162
29563
  if (projectRoot && pkg.path === projectRoot)
29163
29564
  continue;
29164
- if (!existsSync11(pkg.path))
29565
+ if (!existsSync13(pkg.path))
29165
29566
  continue;
29166
29567
  try {
29167
29568
  const config = loadProjectConfig(pkg.path);
@@ -29308,7 +29709,7 @@ async function handleActionsRoutes(ctx) {
29308
29709
  return null;
29309
29710
  }
29310
29711
  // ../core/src/server/routes/playbooks.ts
29311
- import { existsSync as existsSync12 } from "fs";
29712
+ import { existsSync as existsSync14 } from "fs";
29312
29713
  async function handlePlaybooksRoutes(ctx) {
29313
29714
  const { req, url, pathname, corsHeaders, projectRoot, customHome } = ctx;
29314
29715
  if (pathname === "/api/v1/playbooks" && req.method === "GET") {
@@ -29325,7 +29726,7 @@ async function handlePlaybooksRoutes(ctx) {
29325
29726
  for (const pkg of linked) {
29326
29727
  if (projectRoot && pkg.path === projectRoot)
29327
29728
  continue;
29328
- if (!existsSync12(pkg.path))
29729
+ if (!existsSync14(pkg.path))
29329
29730
  continue;
29330
29731
  roots.push({ root: pkg.path, packageId: pkg.id });
29331
29732
  }
@@ -29374,7 +29775,7 @@ async function handlePlaybooksRoutes(ctx) {
29374
29775
  return null;
29375
29776
  }
29376
29777
  // ../core/src/server/routes/runs.ts
29377
- import { existsSync as existsSync13 } from "fs";
29778
+ import { existsSync as existsSync15 } from "fs";
29378
29779
  async function handleRunsRoutes(ctx) {
29379
29780
  const { req, url, pathname, corsHeaders, projectRoot, customHome, runtimeRegistry, options } = ctx;
29380
29781
  if (pathname === "/api/v1/runs" && req.method === "GET") {
@@ -29400,7 +29801,7 @@ async function handleRunsRoutes(ctx) {
29400
29801
  for (const pkg of linked) {
29401
29802
  if (projectRoot && pkg.path === projectRoot)
29402
29803
  continue;
29403
- if (!existsSync13(pkg.path))
29804
+ if (!existsSync15(pkg.path))
29404
29805
  continue;
29405
29806
  candidateStorages.push({
29406
29807
  packageId: pkg.id,
@@ -29451,7 +29852,7 @@ async function handleRunsRoutes(ctx) {
29451
29852
  for (const pkg of linked) {
29452
29853
  if (projectRoot && pkg.path === projectRoot)
29453
29854
  continue;
29454
- if (!existsSync13(pkg.path))
29855
+ if (!existsSync15(pkg.path))
29455
29856
  continue;
29456
29857
  if (packageId && pkg.id !== packageId)
29457
29858
  continue;
@@ -52305,7 +52706,7 @@ function toMcpSchema(schema) {
52305
52706
  return fromJsonSchema2(schema);
52306
52707
  }
52307
52708
  // ../mcp/src/adapter.ts
52308
- import { existsSync as existsSync14 } from "fs";
52709
+ import { existsSync as existsSync16 } from "fs";
52309
52710
  import { resolve as resolve6 } from "path";
52310
52711
  function isPlainObject3(value) {
52311
52712
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
@@ -52389,7 +52790,7 @@ async function createActionDockMcpServer(options = {}) {
52389
52790
  if (options.all) {
52390
52791
  const linked = listLinkedPackages(options.customHome);
52391
52792
  for (const pkg of linked) {
52392
- if (existsSync14(pkg.path)) {
52793
+ if (existsSync16(pkg.path)) {
52393
52794
  resolvedRoots.add(resolve6(pkg.path));
52394
52795
  }
52395
52796
  }
@@ -52400,7 +52801,7 @@ async function createActionDockMcpServer(options = {}) {
52400
52801
  }
52401
52802
  for (const pkgId of targetPackages) {
52402
52803
  const root = resolvePackageRoot(pkgId, undefined, options.customHome);
52403
- if (!root || !existsSync14(root)) {
52804
+ if (!root || !existsSync16(root)) {
52404
52805
  throw new Error(`Package '${pkgId}' not found in registry`);
52405
52806
  }
52406
52807
  resolvedRoots.add(resolve6(root));
@@ -53488,9 +53889,9 @@ class NodeHttpServer {
53488
53889
  }
53489
53890
 
53490
53891
  // ../runtime-node/src/module-loader.ts
53491
- import { existsSync as existsSync15, statSync as statSync4 } from "fs";
53892
+ import { existsSync as existsSync17, statSync as statSync4 } from "fs";
53492
53893
  import { createRequire as createRequire2 } from "module";
53493
- import { dirname as dirname5, isAbsolute, join as join9, resolve as resolve7 } from "path";
53894
+ import { dirname as dirname5, isAbsolute, join as join10, resolve as resolve7 } from "path";
53494
53895
  import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "url";
53495
53896
  var cjsRequire2 = createRequire2(import.meta.url);
53496
53897
  var CANDIDATE_EXTENSIONS = [
@@ -53533,7 +53934,7 @@ class TsxModuleLoader {
53533
53934
  if (parentPath) {
53534
53935
  try {
53535
53936
  const p = parentPath.startsWith("file://") ? fileURLToPath(parentPath) : parentPath;
53536
- if (existsSync15(p)) {
53937
+ if (existsSync17(p)) {
53537
53938
  const stat = statSync4(p);
53538
53939
  baseDir = stat.isDirectory() ? resolve7(p) : dirname5(resolve7(p));
53539
53940
  } else {
@@ -53546,7 +53947,7 @@ class TsxModuleLoader {
53546
53947
  const candidateBasePath = isAbsolute(target) ? target : resolve7(baseDir, target);
53547
53948
  for (const ext of CANDIDATE_EXTENSIONS) {
53548
53949
  const fullPath = ext ? `${candidateBasePath}${ext}` : candidateBasePath;
53549
- if (existsSync15(fullPath)) {
53950
+ if (existsSync17(fullPath)) {
53550
53951
  try {
53551
53952
  if (statSync4(fullPath).isFile()) {
53552
53953
  return fullPath;
@@ -53554,12 +53955,12 @@ class TsxModuleLoader {
53554
53955
  } catch {}
53555
53956
  }
53556
53957
  }
53557
- if (existsSync15(candidateBasePath)) {
53958
+ if (existsSync17(candidateBasePath)) {
53558
53959
  try {
53559
53960
  if (statSync4(candidateBasePath).isDirectory()) {
53560
53961
  for (const indexName of INDEX_CANDIDATES) {
53561
- const indexFilePath = join9(candidateBasePath, indexName);
53562
- if (existsSync15(indexFilePath) && statSync4(indexFilePath).isFile()) {
53962
+ const indexFilePath = join10(candidateBasePath, indexName);
53963
+ if (existsSync17(indexFilePath) && statSync4(indexFilePath).isFile()) {
53563
53964
  return indexFilePath;
53564
53965
  }
53565
53966
  }
@@ -53577,7 +53978,7 @@ class TsxModuleLoader {
53577
53978
  }
53578
53979
  async load(specifier, parentPath) {
53579
53980
  const resolvedPath = this.resolve(specifier, parentPath);
53580
- const parentUrl = parentPath ? parentPath.startsWith("file://") ? parentPath : pathToFileURL2(resolve7(parentPath)).href : pathToFileURL2(join9(process.cwd(), "index.js")).href;
53981
+ const parentUrl = parentPath ? parentPath.startsWith("file://") ? parentPath : pathToFileURL2(resolve7(parentPath)).href : pathToFileURL2(join10(process.cwd(), "index.js")).href;
53581
53982
  const isBun = typeof globalThis.Bun !== "undefined";
53582
53983
  if (isBun) {
53583
53984
  try {
@@ -60617,8 +61018,8 @@ function setupNodeRuntime() {
60617
61018
  }
60618
61019
 
60619
61020
  // src/commands/action/create.ts
60620
- import { existsSync as existsSync16, mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
60621
- import { join as join10, resolve as resolve8 } from "path";
61021
+ import { existsSync as existsSync18, mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
61022
+ import { join as join11, resolve as resolve8 } from "path";
60622
61023
  function registerActionCreateCommand(actionCmd) {
60623
61024
  actionCmd.command("create <id>").alias("new").description("Scaffold a new Action definition file").option("-d, --desc <description>", "Action description").option("-f, --file <filePath>", "Target file path relative to actions dir").action(async (id, options) => {
60624
61025
  const root = findProjectRoot();
@@ -60628,13 +61029,13 @@ function registerActionCreateCommand(actionCmd) {
60628
61029
  try {
60629
61030
  const config = loadProjectConfig(root);
60630
61031
  const actionsDir = resolve8(root, config.actionsDir || "actions");
60631
- if (!existsSync16(actionsDir)) {
61032
+ if (!existsSync18(actionsDir)) {
60632
61033
  mkdirSync5(actionsDir, { recursive: true });
60633
61034
  }
60634
61035
  const cleanName = getPackageSlug(id);
60635
61036
  const targetRelFile = options.file || `${cleanName}.ts`;
60636
61037
  const targetFullFile = resolve8(actionsDir, targetRelFile);
60637
- if (existsSync16(targetFullFile)) {
61038
+ if (existsSync18(targetFullFile)) {
60638
61039
  throw new ExecutionError(`Target action file already exists at ${targetFullFile}`);
60639
61040
  }
60640
61041
  const desc = options.desc || `Action ${id}`;
@@ -60691,7 +61092,7 @@ export default defineAction<Input, Output>({
60691
61092
  const manifest = loadManifest(root) || { schemaVersion: 1, actions: {}, assets: [] };
60692
61093
  manifest.actions = manifest.actions || {};
60693
61094
  manifest.actions[id] = {
60694
- entry: join10(config.actionsDir || "actions", targetRelFile),
61095
+ entry: join11(config.actionsDir || "actions", targetRelFile),
60695
61096
  description: desc,
60696
61097
  inputSchema: {
60697
61098
  type: "object",
@@ -60728,7 +61129,7 @@ To run this action:`);
60728
61129
  }
60729
61130
 
60730
61131
  // src/commands/action/list.ts
60731
- import { existsSync as existsSync17 } from "fs";
61132
+ import { existsSync as existsSync19 } from "fs";
60732
61133
  function registerActionListCommand(actionCmd) {
60733
61134
  actionCmd.command("list [patterns...]").description("List actions in current project, linked packages, or remote profile").option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match").option("-P, --package <id>", "Target package ID or path").option("-p, --profile <name>", "Execute or query against a specific profile").option("-s, --server <url>", "Remote server URL").option("-t, --token <token>", "Auth token for remote server").option("--fallback", "Enable fallback to full list when no items match intent").option("--no-fallback", "Disable fallback to full list when no items match intent").option("--json", "Output as JSON").option("--envelope", "Wrap JSON output in standard envelope").action(async (patterns, rawOptions, cmd) => {
60734
61135
  try {
@@ -60827,7 +61228,7 @@ Run 'ad link' inside an Action package to register it.`
60827
61228
  }
60828
61229
  const aggregated = [];
60829
61230
  for (const pkg of linkedList) {
60830
- if (!existsSync17(pkg.path))
61231
+ if (!existsSync19(pkg.path))
60831
61232
  continue;
60832
61233
  try {
60833
61234
  const config = loadProjectConfig(pkg.path);
@@ -61329,13 +61730,13 @@ class CompilerError extends BuilderError {
61329
61730
  }
61330
61731
  }
61331
61732
  // ../builder/src/build.ts
61332
- import { existsSync as existsSync20, mkdirSync as mkdirSync7, rmSync, writeFileSync as writeFileSync8 } from "fs";
61333
- import { join as join12, resolve as resolve11 } from "path";
61733
+ import { existsSync as existsSync22, mkdirSync as mkdirSync7, rmSync, writeFileSync as writeFileSync8 } from "fs";
61734
+ import { join as join13, resolve as resolve11 } from "path";
61334
61735
 
61335
61736
  // ../builder/src/compiler.ts
61336
61737
  import { spawnSync as spawnSync3 } from "child_process";
61337
61738
  import { createHash } from "crypto";
61338
- import { existsSync as existsSync18, mkdirSync as mkdirSync6, readFileSync as readFileSync8, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
61739
+ import { existsSync as existsSync20, mkdirSync as mkdirSync6, readFileSync as readFileSync8, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
61339
61740
  import { basename as basename5, dirname as dirname6, resolve as resolve9 } from "path";
61340
61741
  var SUPPORTED_TARGETS = new Set([
61341
61742
  "bun",
@@ -61400,7 +61801,7 @@ class BunCompiler {
61400
61801
  });
61401
61802
  }
61402
61803
  const resolvedEntry = resolve9(options.entrypoint);
61403
- if (!existsSync18(resolvedEntry)) {
61804
+ if (!existsSync20(resolvedEntry)) {
61404
61805
  throw new CompilerValidationError(`Entrypoint file not found: ${options.entrypoint}`, { code: "ENTRYPOINT_NOT_FOUND" });
61405
61806
  }
61406
61807
  if (!options.outfile) {
@@ -61473,13 +61874,13 @@ class BunCompiler {
61473
61874
  throw normalizeCompilerError(rawStderr, rawStdout, proc.status ?? 1, options.target);
61474
61875
  }
61475
61876
  let actualExecutablePath = resolvedOutfile;
61476
- if (!existsSync18(actualExecutablePath) && isWindowsTarget) {
61877
+ if (!existsSync20(actualExecutablePath) && isWindowsTarget) {
61477
61878
  const withExe = `${resolvedOutfile}.exe`;
61478
- if (existsSync18(withExe)) {
61879
+ if (existsSync20(withExe)) {
61479
61880
  actualExecutablePath = withExe;
61480
61881
  }
61481
61882
  }
61482
- if (!existsSync18(actualExecutablePath)) {
61883
+ if (!existsSync20(actualExecutablePath)) {
61483
61884
  throw new CompilerError(`Compiler completed with exit code 0 but output executable not found at ${resolvedOutfile}`, {
61484
61885
  code: "OUTPUT_NOT_FOUND",
61485
61886
  exitCode: 0,
@@ -61541,15 +61942,15 @@ class BunCompiler {
61541
61942
  }
61542
61943
 
61543
61944
  // ../builder/src/planner.ts
61544
- import { existsSync as existsSync19, readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
61545
- import { basename as basename6, join as join11, relative as relative3, resolve as resolve10 } from "path";
61945
+ import { existsSync as existsSync21, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
61946
+ import { basename as basename6, dirname as dirname7, isAbsolute as isAbsolute2, join as join12, relative as relative3, resolve as resolve10 } from "path";
61546
61947
  function walkDirectory(dir) {
61547
- if (!existsSync19(dir))
61948
+ if (!existsSync21(dir))
61548
61949
  return [];
61549
61950
  const results = [];
61550
- const entries = readdirSync3(dir);
61951
+ const entries = readdirSync4(dir);
61551
61952
  for (const entry of entries) {
61552
- const fullPath = join11(dir, entry);
61953
+ const fullPath = join12(dir, entry);
61553
61954
  const stat = statSync7(fullPath);
61554
61955
  if (stat.isDirectory()) {
61555
61956
  results.push(...walkDirectory(fullPath));
@@ -61559,6 +61960,136 @@ function walkDirectory(dir) {
61559
61960
  }
61560
61961
  return results;
61561
61962
  }
61963
+ function isIgnoredModulePath(relPath) {
61964
+ const normalized = relPath.replace(/\\/g, "/");
61965
+ return normalized.startsWith("node_modules/") || normalized.includes("/node_modules/") || normalized.startsWith(".git/") || normalized.startsWith("dist/") || normalized.startsWith(".actiondock/") || normalized.endsWith(".test.ts") || normalized.endsWith(".test.js") || normalized.endsWith(".test.tsx") || normalized.endsWith(".test.jsx") || normalized.endsWith(".spec.ts") || normalized.endsWith(".spec.js") || normalized.endsWith(".spec.tsx") || normalized.endsWith(".spec.jsx") || normalized.endsWith(".d.ts");
61966
+ }
61967
+ function extractRelativeImports(source) {
61968
+ const stripped = source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\n]*/g, "");
61969
+ const specifiers = new Set;
61970
+ const fromRegex = /\bfrom\s*["'`]([^"'`]+)["'`]/g;
61971
+ let match;
61972
+ while ((match = fromRegex.exec(stripped)) !== null) {
61973
+ const spec = match[1].trim();
61974
+ if (spec.startsWith("./") || spec.startsWith("../")) {
61975
+ specifiers.add(spec);
61976
+ }
61977
+ }
61978
+ const importRegex = /\bimport\s*(?:\(\s*)?["'`]([^"'`]+)["'`]/g;
61979
+ while ((match = importRegex.exec(stripped)) !== null) {
61980
+ const spec = match[1].trim();
61981
+ if (spec.startsWith("./") || spec.startsWith("../")) {
61982
+ specifiers.add(spec);
61983
+ }
61984
+ }
61985
+ const requireRegex = /\brequire\s*\(\s*["'`]([^"'`]+)["'`]/g;
61986
+ while ((match = requireRegex.exec(stripped)) !== null) {
61987
+ const spec = match[1].trim();
61988
+ if (spec.startsWith("./") || spec.startsWith("../")) {
61989
+ specifiers.add(spec);
61990
+ }
61991
+ }
61992
+ return Array.from(specifiers);
61993
+ }
61994
+ var CANDIDATE_EXTENSIONS2 = [
61995
+ "",
61996
+ ".ts",
61997
+ ".tsx",
61998
+ ".js",
61999
+ ".jsx",
62000
+ ".mjs",
62001
+ ".cjs",
62002
+ ".mts",
62003
+ ".cts",
62004
+ ".json"
62005
+ ];
62006
+ function resolveLocalModulePath(baseDir, specifier) {
62007
+ const candidate = resolve10(baseDir, specifier);
62008
+ if (existsSync21(candidate)) {
62009
+ try {
62010
+ if (statSync7(candidate).isFile()) {
62011
+ return candidate;
62012
+ }
62013
+ } catch {}
62014
+ }
62015
+ if (candidate.endsWith(".js")) {
62016
+ const tsPath = candidate.slice(0, -3) + ".ts";
62017
+ if (existsSync21(tsPath))
62018
+ return tsPath;
62019
+ const tsxPath = candidate.slice(0, -3) + ".tsx";
62020
+ if (existsSync21(tsxPath))
62021
+ return tsxPath;
62022
+ } else if (candidate.endsWith(".mjs")) {
62023
+ const mtsPath = candidate.slice(0, -4) + ".mts";
62024
+ if (existsSync21(mtsPath))
62025
+ return mtsPath;
62026
+ } else if (candidate.endsWith(".cjs")) {
62027
+ const ctsPath = candidate.slice(0, -4) + ".cts";
62028
+ if (existsSync21(ctsPath))
62029
+ return ctsPath;
62030
+ }
62031
+ for (const ext of CANDIDATE_EXTENSIONS2) {
62032
+ if (!ext)
62033
+ continue;
62034
+ const withExt = candidate + ext;
62035
+ if (existsSync21(withExt)) {
62036
+ try {
62037
+ if (statSync7(withExt).isFile()) {
62038
+ return withExt;
62039
+ }
62040
+ } catch {}
62041
+ }
62042
+ }
62043
+ if (existsSync21(candidate)) {
62044
+ try {
62045
+ if (statSync7(candidate).isDirectory()) {
62046
+ for (const ext of [".ts", ".tsx", ".js", ".mjs", ".json"]) {
62047
+ const indexFile = join12(candidate, `index${ext}`);
62048
+ if (existsSync21(indexFile)) {
62049
+ return indexFile;
62050
+ }
62051
+ }
62052
+ }
62053
+ } catch {}
62054
+ }
62055
+ return null;
62056
+ }
62057
+ function traceLocalModuleDependencies(entryFiles, projectRoot) {
62058
+ const visited = new Set;
62059
+ const modules = new Set;
62060
+ const queue = [...entryFiles];
62061
+ while (queue.length > 0) {
62062
+ const current = queue.shift();
62063
+ if (visited.has(current))
62064
+ continue;
62065
+ visited.add(current);
62066
+ const relToRoot = relative3(projectRoot, current);
62067
+ if (relToRoot.startsWith("..") || isAbsolute2(relToRoot)) {
62068
+ continue;
62069
+ }
62070
+ let content = "";
62071
+ try {
62072
+ content = readFileSync9(current, "utf-8");
62073
+ } catch {
62074
+ continue;
62075
+ }
62076
+ const specifiers = extractRelativeImports(content);
62077
+ const dir = dirname7(current);
62078
+ for (const spec of specifiers) {
62079
+ const resolved = resolveLocalModulePath(dir, spec);
62080
+ if (resolved) {
62081
+ const rel = relative3(projectRoot, resolved);
62082
+ if (!rel.startsWith("..") && !isAbsolute2(rel) && !isIgnoredModulePath(rel)) {
62083
+ modules.add(resolved);
62084
+ if (!visited.has(resolved)) {
62085
+ queue.push(resolved);
62086
+ }
62087
+ }
62088
+ }
62089
+ }
62090
+ }
62091
+ return Array.from(modules);
62092
+ }
61562
62093
  function parsePlaybookFile(filePath) {
61563
62094
  try {
61564
62095
  const content = readFileSync9(filePath, "utf-8");
@@ -61584,8 +62115,8 @@ function parsePlaybookFile(filePath) {
61584
62115
  }
61585
62116
  function loadProjectPlaybooks(projectRoot, playbooksDir = "playbooks") {
61586
62117
  const map = new Map;
61587
- const dir = join11(projectRoot, playbooksDir);
61588
- if (!existsSync19(dir))
62118
+ const dir = join12(projectRoot, playbooksDir);
62119
+ if (!existsSync21(dir))
61589
62120
  return map;
61590
62121
  const files = walkDirectory(dir).filter((f) => f.endsWith(".md"));
61591
62122
  for (const file of files) {
@@ -61597,9 +62128,9 @@ function loadProjectPlaybooks(projectRoot, playbooksDir = "playbooks") {
61597
62128
  return map;
61598
62129
  }
61599
62130
  function generateStaticManifest(projectRoot, actionsDir = "actions") {
61600
- const dir = join11(projectRoot, actionsDir);
62131
+ const dir = join12(projectRoot, actionsDir);
61601
62132
  const actions = {};
61602
- if (existsSync19(dir)) {
62133
+ if (existsSync21(dir)) {
61603
62134
  const files = walkDirectory(dir).filter((f) => (f.endsWith(".ts") || f.endsWith(".js")) && !f.endsWith(".d.ts") && !f.endsWith(".test.ts") && !f.endsWith(".spec.ts"));
61604
62135
  for (const file of files) {
61605
62136
  const relPath = relative3(projectRoot, file);
@@ -61637,8 +62168,8 @@ function generateStaticManifest(projectRoot, actionsDir = "actions") {
61637
62168
  };
61638
62169
  }
61639
62170
  function extractExternalDependencies(projectRoot) {
61640
- const pkgPath = join11(projectRoot, "package.json");
61641
- if (!existsSync19(pkgPath))
62171
+ const pkgPath = join12(projectRoot, "package.json");
62172
+ if (!existsSync21(pkgPath))
61642
62173
  return [];
61643
62174
  try {
61644
62175
  const raw = readFileSync9(pkgPath, "utf-8");
@@ -61730,14 +62261,34 @@ class BuildPlanner {
61730
62261
  }
61731
62262
  const resolvedActionIds = new Set;
61732
62263
  const queue = Array.from(initialActionIds);
62264
+ const externalActionRoots = new Map;
62265
+ const externalActionEntries = new Map;
61733
62266
  while (queue.length > 0) {
61734
62267
  const currentId = queue.shift();
61735
62268
  if (resolvedActionIds.has(currentId)) {
61736
62269
  continue;
61737
62270
  }
61738
- const entry = manifest.actions[currentId];
62271
+ let entry = manifest.actions[currentId];
62272
+ let entryRoot = root;
61739
62273
  if (!entry) {
61740
- throw new PlannerError(`Action '${currentId}' referenced in dependency closure (uses) was not found in manifest`, "MISSING_DEPENDENCY");
62274
+ try {
62275
+ const resolvedExternal = resolveActionProjectSync(currentId, root);
62276
+ if (resolvedExternal && existsSync21(resolvedExternal.projectRoot)) {
62277
+ const externalManifest = loadManifest(resolvedExternal.projectRoot);
62278
+ const externalEntry = externalManifest?.actions?.[resolvedExternal.actionId];
62279
+ if (externalEntry) {
62280
+ entry = externalEntry;
62281
+ entryRoot = resolvedExternal.projectRoot;
62282
+ }
62283
+ }
62284
+ } catch {}
62285
+ }
62286
+ if (!entry) {
62287
+ throw new PlannerError(`Action '${currentId}' referenced in dependency closure (uses) was not found in manifest or linked packages`, "MISSING_DEPENDENCY");
62288
+ }
62289
+ if (entryRoot !== root) {
62290
+ externalActionRoots.set(currentId, entryRoot);
62291
+ externalActionEntries.set(currentId, entry);
61741
62292
  }
61742
62293
  resolvedActionIds.add(currentId);
61743
62294
  if (entry.uses && Array.isArray(entry.uses)) {
@@ -61757,9 +62308,13 @@ class BuildPlanner {
61757
62308
  }
61758
62309
  const actionDependencies = [];
61759
62310
  for (const actId of resolvedActionIds) {
61760
- const entry = manifest.actions[actId];
61761
- const resolvedPath = resolve10(root, entry.entry);
61762
- if (!existsSync19(resolvedPath)) {
62311
+ const entry = manifest.actions[actId] || externalActionEntries.get(actId);
62312
+ const entryRoot = externalActionRoots.get(actId) || root;
62313
+ if (!entry) {
62314
+ throw new PlannerError(`Action '${actId}' entry not found`, "ENTRY_FILE_NOT_FOUND");
62315
+ }
62316
+ const resolvedPath = resolve10(entryRoot, entry.entry);
62317
+ if (!existsSync21(resolvedPath)) {
61763
62318
  throw new PlannerError(`Action '${actId}' entry file not found on disk: ${entry.entry}`, "ENTRY_FILE_NOT_FOUND");
61764
62319
  }
61765
62320
  actionDependencies.push({
@@ -61776,6 +62331,50 @@ class BuildPlanner {
61776
62331
  }
61777
62332
  const modulesAndAssets = [];
61778
62333
  const assetPathSet = new Set;
62334
+ const modulePathSet = new Set;
62335
+ const actionPathSet = new Set(actionDependencies.map((a) => a.resolvedPath));
62336
+ const rootActionsMap = new Map;
62337
+ for (const act of actionDependencies) {
62338
+ const actRoot = externalActionRoots.get(act.id) || root;
62339
+ if (!rootActionsMap.has(actRoot)) {
62340
+ rootActionsMap.set(actRoot, []);
62341
+ }
62342
+ rootActionsMap.get(actRoot).push(act.resolvedPath);
62343
+ }
62344
+ for (const [actRoot, files] of rootActionsMap) {
62345
+ const tracedModulePaths = traceLocalModuleDependencies(files, actRoot);
62346
+ for (const modPath of tracedModulePaths) {
62347
+ if (!actionPathSet.has(modPath)) {
62348
+ const rel = relative3(actRoot, modPath).replace(/\\/g, "/");
62349
+ if (!modulePathSet.has(rel) && !isIgnoredModulePath(rel)) {
62350
+ modulePathSet.add(rel);
62351
+ modulesAndAssets.push({
62352
+ path: rel,
62353
+ resolvedPath: modPath,
62354
+ type: "module"
62355
+ });
62356
+ }
62357
+ }
62358
+ }
62359
+ }
62360
+ const isSelective = Boolean(options?.actions && options.actions.length > 0) || Boolean(options?.playbooks && options.playbooks.length > 0);
62361
+ if (!isSelective) {
62362
+ const defaultLibDir = join12(root, "lib");
62363
+ if (existsSync21(defaultLibDir)) {
62364
+ const libFiles = walkDirectory(defaultLibDir);
62365
+ for (const file of libFiles) {
62366
+ const rel = relative3(root, file).replace(/\\/g, "/");
62367
+ if (!isIgnoredModulePath(rel) && !actionPathSet.has(file) && !modulePathSet.has(rel)) {
62368
+ modulePathSet.add(rel);
62369
+ modulesAndAssets.push({
62370
+ path: rel,
62371
+ resolvedPath: file,
62372
+ type: "module"
62373
+ });
62374
+ }
62375
+ }
62376
+ }
62377
+ }
61779
62378
  if (manifest.assets && Array.isArray(manifest.assets)) {
61780
62379
  for (const assetRel of manifest.assets) {
61781
62380
  if (!assetPathSet.has(assetRel)) {
@@ -61788,8 +62387,8 @@ class BuildPlanner {
61788
62387
  }
61789
62388
  }
61790
62389
  }
61791
- const defaultAssetsDir = join11(root, "assets");
61792
- if (existsSync19(defaultAssetsDir)) {
62390
+ const defaultAssetsDir = join12(root, "assets");
62391
+ if (existsSync21(defaultAssetsDir)) {
61793
62392
  const assetFiles = walkDirectory(defaultAssetsDir);
61794
62393
  for (const file of assetFiles) {
61795
62394
  const rel = relative3(root, file);
@@ -61811,16 +62410,16 @@ class BuildPlanner {
61811
62410
  type: "playbook"
61812
62411
  });
61813
62412
  }
61814
- const configPath = join11(root, "actiondock.json");
61815
- if (existsSync19(configPath)) {
62413
+ const configPath = join12(root, "actiondock.json");
62414
+ if (existsSync21(configPath)) {
61816
62415
  modulesAndAssets.push({
61817
62416
  path: "actiondock.json",
61818
62417
  resolvedPath: configPath,
61819
62418
  type: "config"
61820
62419
  });
61821
62420
  }
61822
- const manifestPath = join11(root, "actiondock.manifest.json");
61823
- if (existsSync19(manifestPath)) {
62421
+ const manifestPath = join12(root, "actiondock.manifest.json");
62422
+ if (existsSync21(manifestPath)) {
61824
62423
  modulesAndAssets.push({
61825
62424
  path: "actiondock.manifest.json",
61826
62425
  resolvedPath: manifestPath,
@@ -61868,14 +62467,14 @@ async function buildProject(options) {
61868
62467
  if (plan.actions.length === 0) {
61869
62468
  throw new BuilderError("No actions resolved for standalone compilation");
61870
62469
  }
61871
- const buildDir = join12(root, ".actiondock", ".build");
62470
+ const buildDir = join13(root, ".actiondock", ".build");
61872
62471
  mkdirSync7(buildDir, { recursive: true });
61873
62472
  const entryCode = generateStandaloneEntrypoint(plan.packageId, plan.version, plan.description, plan.actions.map((a) => ({ id: a.id, filePath: a.resolvedPath })), plan.configDefs);
61874
62473
  const entryFileName = `entry-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.ts`;
61875
- const entryPath = join12(buildDir, entryFileName);
62474
+ const entryPath = join13(buildDir, entryFileName);
61876
62475
  writeFileSync8(entryPath, entryCode, "utf-8");
61877
62476
  const binaryName = getPackageSlug(plan.packageId);
61878
- const defaultOutfile = join12(root, "dist", binaryName);
62477
+ const defaultOutfile = join13(root, "dist", binaryName);
61879
62478
  const outfile = resolve11(options.outfile || defaultOutfile);
61880
62479
  try {
61881
62480
  const result = await BunCompiler.compile({
@@ -61898,23 +62497,23 @@ async function buildProject(options) {
61898
62497
  actions: plan.actions.map((a) => a.id)
61899
62498
  };
61900
62499
  } finally {
61901
- if (existsSync20(entryPath)) {
62500
+ if (existsSync22(entryPath)) {
61902
62501
  rmSync(entryPath, { force: true });
61903
62502
  }
61904
62503
  }
61905
62504
  }
61906
62505
  // ../builder/src/exporter.ts
61907
- import { copyFileSync, existsSync as existsSync21, mkdirSync as mkdirSync8, readFileSync as readFileSync11, readdirSync as readdirSync5, rmSync as rmSync2, statSync as statSync9, writeFileSync as writeFileSync10 } from "fs";
61908
- import { basename as basename8, dirname as dirname9, join as join14, relative as relative5, resolve as resolve12 } from "path";
62506
+ import { copyFileSync, existsSync as existsSync23, mkdirSync as mkdirSync8, readFileSync as readFileSync11, readdirSync as readdirSync6, rmSync as rmSync2, statSync as statSync9, writeFileSync as writeFileSync10 } from "fs";
62507
+ import { basename as basename8, dirname as dirname9, join as join15, relative as relative5, resolve as resolve12 } from "path";
61909
62508
 
61910
62509
  // ../builder/src/archive.ts
61911
- import { readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync8, writeFileSync as writeFileSync9 } from "fs";
62510
+ import { readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync8, writeFileSync as writeFileSync9 } from "fs";
61912
62511
  import { deflateRawSync, gzipSync } from "zlib";
61913
- import { basename as basename7, join as join13, relative as relative4, sep } from "path";
62512
+ import { basename as basename7, join as join14, relative as relative4, sep } from "path";
61914
62513
  function collectEntries(dir, baseDir = dir) {
61915
62514
  const entries = [];
61916
- for (const name of readdirSync4(dir).sort()) {
61917
- const fullPath = join13(dir, name);
62515
+ for (const name of readdirSync5(dir).sort()) {
62516
+ const fullPath = join14(dir, name);
61918
62517
  const stat = statSync8(fullPath);
61919
62518
  if (stat.isDirectory()) {
61920
62519
  entries.push({ relPath: relative4(baseDir, fullPath).split(sep).join("/"), isDir: true });
@@ -61959,9 +62558,9 @@ function createZipArchive(dir, outPath) {
61959
62558
  for (const entry of entries) {
61960
62559
  const name = `${rootName}/${entry.relPath}${entry.isDir ? "/" : ""}`;
61961
62560
  const nameBuf = Buffer.from(name, "utf8");
61962
- const stat = statSync8(join13(dir, entry.relPath));
62561
+ const stat = statSync8(join14(dir, entry.relPath));
61963
62562
  const { date, time } = dosDateTime(stat.mtimeMs);
61964
- const content = entry.isDir ? Buffer.alloc(0) : readFileSync10(join13(dir, entry.relPath));
62563
+ const content = entry.isDir ? Buffer.alloc(0) : readFileSync10(join14(dir, entry.relPath));
61965
62564
  let method = 0;
61966
62565
  let payload = content;
61967
62566
  if (!entry.isDir && content.length > 0) {
@@ -62067,7 +62666,7 @@ function createTarGzArchive(dir, outPath) {
62067
62666
  const chunks = [];
62068
62667
  for (const entry of entries) {
62069
62668
  const path = `${rootName}/${entry.relPath}`;
62070
- const fullPath = join13(dir, entry.relPath);
62669
+ const fullPath = join14(dir, entry.relPath);
62071
62670
  const stat = statSync8(fullPath);
62072
62671
  const content = entry.isDir ? Buffer.alloc(0) : readFileSync10(fullPath);
62073
62672
  chunks.push(tarHeader(path, content.length, Math.floor(stat.mtimeMs / 1000), entry.isDir));
@@ -62081,12 +62680,12 @@ function createTarGzArchive(dir, outPath) {
62081
62680
 
62082
62681
  // ../builder/src/exporter.ts
62083
62682
  function scanRelativeFiles(dir, baseDir = dir) {
62084
- if (!existsSync21(dir))
62683
+ if (!existsSync23(dir))
62085
62684
  return [];
62086
62685
  const results = [];
62087
- const entries = readdirSync5(dir);
62686
+ const entries = readdirSync6(dir);
62088
62687
  for (const entry of entries) {
62089
- const fullPath = join14(dir, entry);
62688
+ const fullPath = join15(dir, entry);
62090
62689
  const stat = statSync9(fullPath);
62091
62690
  if (stat.isDirectory()) {
62092
62691
  results.push(...scanRelativeFiles(fullPath, baseDir));
@@ -62100,8 +62699,8 @@ function createArchive(skillDir, format) {
62100
62699
  const parentDir = dirname9(skillDir);
62101
62700
  const folderName = basename8(skillDir);
62102
62701
  const archiveName = `${folderName}.${format === "tar.gz" ? "tar.gz" : "zip"}`;
62103
- const archivePath = join14(parentDir, archiveName);
62104
- if (existsSync21(archivePath)) {
62702
+ const archivePath = join15(parentDir, archiveName);
62703
+ if (existsSync23(archivePath)) {
62105
62704
  rmSync2(archivePath, { force: true });
62106
62705
  }
62107
62706
  try {
@@ -62115,6 +62714,74 @@ function createArchive(skillDir, format) {
62115
62714
  }
62116
62715
  return archivePath;
62117
62716
  }
62717
+ function findExistingSingleSkillMd(projectRoot, explicitPath, pkgSlug) {
62718
+ if (explicitPath && existsSync23(explicitPath)) {
62719
+ try {
62720
+ if (statSync9(explicitPath).isFile())
62721
+ return resolve12(explicitPath);
62722
+ } catch {}
62723
+ }
62724
+ const candidates = [
62725
+ join15(projectRoot, "SKILL.md"),
62726
+ join15(projectRoot, "skill.md"),
62727
+ join15(projectRoot, "skills", "SKILL.md"),
62728
+ join15(projectRoot, "skills", "skill.md")
62729
+ ];
62730
+ if (pkgSlug) {
62731
+ candidates.push(join15(projectRoot, "skills", pkgSlug, "SKILL.md"), join15(projectRoot, "skills", pkgSlug, "skill.md"));
62732
+ }
62733
+ for (const candidate of candidates) {
62734
+ if (existsSync23(candidate)) {
62735
+ try {
62736
+ if (statSync9(candidate).isFile())
62737
+ return resolve12(candidate);
62738
+ } catch {}
62739
+ }
62740
+ }
62741
+ return;
62742
+ }
62743
+ function findExistingCompositeSkillMd(options) {
62744
+ if (options.skillMdPath && existsSync23(options.skillMdPath)) {
62745
+ try {
62746
+ if (statSync9(options.skillMdPath).isFile())
62747
+ return resolve12(options.skillMdPath);
62748
+ } catch {}
62749
+ }
62750
+ const bundleSlug = getPackageSlug(options.bundleName);
62751
+ const searchDirs = new Set;
62752
+ if (options.workspaceRoot && existsSync23(options.workspaceRoot)) {
62753
+ searchDirs.add(resolve12(options.workspaceRoot));
62754
+ }
62755
+ searchDirs.add(process.cwd());
62756
+ for (const root of options.projectRoots) {
62757
+ const absRoot = resolve12(root);
62758
+ const parentDir = dirname9(absRoot);
62759
+ searchDirs.add(parentDir);
62760
+ searchDirs.add(dirname9(parentDir));
62761
+ }
62762
+ const candidateRelativePaths = [
62763
+ join15("skills", options.bundleName, "SKILL.md"),
62764
+ join15("skills", bundleSlug, "SKILL.md"),
62765
+ join15("skills", "SKILL.md"),
62766
+ "SKILL.md",
62767
+ join15("skills", options.bundleName, "skill.md"),
62768
+ join15("skills", bundleSlug, "skill.md"),
62769
+ join15("skills", "skill.md"),
62770
+ "skill.md"
62771
+ ];
62772
+ for (const dir of searchDirs) {
62773
+ for (const rel of candidateRelativePaths) {
62774
+ const fullPath = join15(dir, rel);
62775
+ if (existsSync23(fullPath)) {
62776
+ try {
62777
+ if (statSync9(fullPath).isFile())
62778
+ return fullPath;
62779
+ } catch {}
62780
+ }
62781
+ }
62782
+ }
62783
+ return;
62784
+ }
62118
62785
 
62119
62786
  class SkillExporter {
62120
62787
  async export(options) {
@@ -62132,10 +62799,10 @@ class SkillExporter {
62132
62799
  const pkgSlug = getPackageSlug(plan.packageId);
62133
62800
  const targetSuffix = mode === "standalone" && target !== "host" && target !== "bun" ? `-${target}` : "";
62134
62801
  const defaultFolderName = `${pkgSlug}-skill${targetSuffix}`;
62135
- const defaultSkillDir = join14(root, "dist", defaultFolderName);
62802
+ const defaultSkillDir = join15(root, "dist", defaultFolderName);
62136
62803
  const skillDir = resolve12(options.outDir || defaultSkillDir);
62137
62804
  mkdirSync8(skillDir, { recursive: true });
62138
- const playbooksDestDir = join14(skillDir, "playbooks");
62805
+ const playbooksDestDir = join15(skillDir, "playbooks");
62139
62806
  if (plan.playbooks.length > 0) {
62140
62807
  mkdirSync8(playbooksDestDir, { recursive: true });
62141
62808
  }
@@ -62148,14 +62815,19 @@ class SkillExporter {
62148
62815
  playbooksDir: "playbooks",
62149
62816
  config: plan.configDefs
62150
62817
  };
62818
+ let usedExistingSkillMd;
62151
62819
  if (mode === "source") {
62152
- const skillMd = generateSourceSkillMd(configForTemplates, plan.actions, plan.playbooks);
62153
- writeFileSync10(join14(skillDir, "SKILL.md"), skillMd, "utf-8");
62154
- const skillJsonContent = generateSkillJson(configForTemplates, plan.actions, {
62155
- mode: "source",
62156
- playbooks: plan.playbooks
62157
- });
62158
- writeFileSync10(join14(skillDir, "actiondock.skill.json"), skillJsonContent, "utf-8");
62820
+ const existingSkill = !options.skipSkillMd ? findExistingSingleSkillMd(root, options.skillMdPath, pkgSlug) : undefined;
62821
+ if (existingSkill) {
62822
+ usedExistingSkillMd = existingSkill;
62823
+ const destSkillMd = join15(skillDir, "SKILL.md");
62824
+ if (resolve12(existingSkill) !== resolve12(destSkillMd)) {
62825
+ copyFileSync(existingSkill, destSkillMd);
62826
+ }
62827
+ } else if (!options.skipSkillMd) {
62828
+ const skillMd = generateSourceSkillMd(configForTemplates, plan.actions, plan.playbooks);
62829
+ writeFileSync10(join15(skillDir, "SKILL.md"), skillMd, "utf-8");
62830
+ }
62159
62831
  const manifestActions = {};
62160
62832
  for (const a of plan.actions) {
62161
62833
  manifestActions[a.id] = {
@@ -62167,13 +62839,19 @@ class SkillExporter {
62167
62839
  tags: a.tags,
62168
62840
  annotations: a.annotations
62169
62841
  };
62842
+ if (a.id.includes("/")) {
62843
+ const shortId = a.id.slice(a.id.lastIndexOf("/") + 1);
62844
+ if (!manifestActions[shortId]) {
62845
+ manifestActions[shortId] = manifestActions[a.id];
62846
+ }
62847
+ }
62170
62848
  }
62171
62849
  const exportedManifest = {
62172
62850
  schemaVersion: 1,
62173
62851
  actions: manifestActions,
62174
62852
  assets: plan.assets
62175
62853
  };
62176
- writeFileSync10(join14(skillDir, "actiondock.manifest.json"), JSON.stringify(exportedManifest, null, 2) + `
62854
+ writeFileSync10(join15(skillDir, "actiondock.manifest.json"), JSON.stringify(exportedManifest, null, 2) + `
62177
62855
  `, "utf-8");
62178
62856
  const exportedConfig = {
62179
62857
  id: plan.packageId,
@@ -62184,11 +62862,11 @@ class SkillExporter {
62184
62862
  playbooksDir: "playbooks",
62185
62863
  config: plan.configDefs
62186
62864
  };
62187
- writeFileSync10(join14(skillDir, "actiondock.json"), JSON.stringify(exportedConfig, null, 2) + `
62865
+ writeFileSync10(join15(skillDir, "actiondock.json"), JSON.stringify(exportedConfig, null, 2) + `
62188
62866
  `, "utf-8");
62189
- const projectPkgPath = join14(root, "package.json");
62867
+ const projectPkgPath = join15(root, "package.json");
62190
62868
  let exportedPkg;
62191
- if (existsSync21(projectPkgPath)) {
62869
+ if (existsSync23(projectPkgPath)) {
62192
62870
  try {
62193
62871
  const raw = readFileSync11(projectPkgPath, "utf-8");
62194
62872
  const parsed = JSON.parse(raw);
@@ -62220,42 +62898,42 @@ class SkillExporter {
62220
62898
  dependencies: { "@actiondock/sdk": "^2.0.0" }
62221
62899
  };
62222
62900
  }
62223
- writeFileSync10(join14(skillDir, "package.json"), JSON.stringify(exportedPkg, null, 2) + `
62901
+ writeFileSync10(join15(skillDir, "package.json"), JSON.stringify(exportedPkg, null, 2) + `
62224
62902
  `, "utf-8");
62225
- const tsconfigPath = join14(root, "tsconfig.json");
62226
- if (existsSync21(tsconfigPath)) {
62227
- copyFileSync(tsconfigPath, join14(skillDir, "tsconfig.json"));
62903
+ const tsconfigPath = join15(root, "tsconfig.json");
62904
+ if (existsSync23(tsconfigPath)) {
62905
+ copyFileSync(tsconfigPath, join15(skillDir, "tsconfig.json"));
62228
62906
  }
62229
62907
  for (const act of plan.actions) {
62230
- if (existsSync21(act.resolvedPath)) {
62231
- const destFile = join14(skillDir, act.entry);
62908
+ if (existsSync23(act.resolvedPath)) {
62909
+ const destFile = join15(skillDir, act.entry);
62232
62910
  mkdirSync8(dirname9(destFile), { recursive: true });
62233
62911
  copyFileSync(act.resolvedPath, destFile);
62234
62912
  }
62235
62913
  }
62236
62914
  for (const dep of plan.dependencies.modulesAndAssets) {
62237
- if (dep.type === "asset" && existsSync21(dep.resolvedPath)) {
62238
- const destAsset = join14(skillDir, dep.path);
62915
+ if ((dep.type === "asset" || dep.type === "module") && existsSync23(dep.resolvedPath)) {
62916
+ const destAsset = join15(skillDir, dep.path);
62239
62917
  mkdirSync8(dirname9(destAsset), { recursive: true });
62240
62918
  copyFileSync(dep.resolvedPath, destAsset);
62241
62919
  }
62242
62920
  }
62243
62921
  for (const pb of plan.playbooks) {
62244
- if (existsSync21(pb.filePath)) {
62245
- const destPb = join14(playbooksDestDir, basename8(pb.filePath));
62922
+ if (existsSync23(pb.filePath)) {
62923
+ const destPb = join15(playbooksDestDir, basename8(pb.filePath));
62246
62924
  copyFileSync(pb.filePath, destPb);
62247
62925
  }
62248
62926
  }
62249
62927
  } else {
62250
- const binDir = join14(skillDir, "bin");
62928
+ const binDir = join15(skillDir, "bin");
62251
62929
  mkdirSync8(binDir, { recursive: true });
62252
62930
  const binaryName = pkgSlug;
62253
- const binaryPath = join14(binDir, binaryName);
62254
- const buildDir = join14(root, ".actiondock", ".build");
62931
+ const binaryPath = join15(binDir, binaryName);
62932
+ const buildDir = join15(root, ".actiondock", ".build");
62255
62933
  mkdirSync8(buildDir, { recursive: true });
62256
62934
  const entryCode = generateStandaloneEntrypoint(plan.packageId, plan.version, plan.description, plan.actions.map((a) => ({ id: a.id, filePath: a.resolvedPath })), plan.configDefs);
62257
62935
  const entryFileName = `entry-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.ts`;
62258
- const entryPath = join14(buildDir, entryFileName);
62936
+ const entryPath = join15(buildDir, entryFileName);
62259
62937
  writeFileSync10(entryPath, entryCode, "utf-8");
62260
62938
  let compileRes;
62261
62939
  try {
@@ -62271,29 +62949,31 @@ class SkillExporter {
62271
62949
  actions: plan.actions.map((a) => a.id)
62272
62950
  });
62273
62951
  } finally {
62274
- if (existsSync21(entryPath)) {
62952
+ if (existsSync23(entryPath)) {
62275
62953
  rmSync2(entryPath, { force: true });
62276
62954
  }
62277
62955
  }
62278
62956
  const actualBinaryName = basename8(compileRes.executablePath);
62279
- const skillMd = generateStandaloneSkillMd(configForTemplates, plan.actions, plan.playbooks, `./bin/${actualBinaryName}`);
62280
- writeFileSync10(join14(skillDir, "SKILL.md"), skillMd, "utf-8");
62281
- const skillJsonContent = generateSkillJson(configForTemplates, plan.actions, {
62282
- mode: "standalone",
62283
- executable: `./bin/${actualBinaryName}`,
62284
- target,
62285
- playbooks: plan.playbooks
62286
- });
62287
- writeFileSync10(join14(skillDir, "actiondock.skill.json"), skillJsonContent, "utf-8");
62957
+ const existingSkill = !options.skipSkillMd ? findExistingSingleSkillMd(root, options.skillMdPath, pkgSlug) : undefined;
62958
+ if (existingSkill) {
62959
+ usedExistingSkillMd = existingSkill;
62960
+ const destSkillMd = join15(skillDir, "SKILL.md");
62961
+ if (resolve12(existingSkill) !== resolve12(destSkillMd)) {
62962
+ copyFileSync(existingSkill, destSkillMd);
62963
+ }
62964
+ } else if (!options.skipSkillMd) {
62965
+ const skillMd = generateStandaloneSkillMd(configForTemplates, plan.actions, plan.playbooks, `./bin/${actualBinaryName}`);
62966
+ writeFileSync10(join15(skillDir, "SKILL.md"), skillMd, "utf-8");
62967
+ }
62288
62968
  for (const pb of plan.playbooks) {
62289
- if (existsSync21(pb.filePath)) {
62290
- const destPb = join14(playbooksDestDir, basename8(pb.filePath));
62969
+ if (existsSync23(pb.filePath)) {
62970
+ const destPb = join15(playbooksDestDir, basename8(pb.filePath));
62291
62971
  copyFileSync(pb.filePath, destPb);
62292
62972
  }
62293
62973
  }
62294
62974
  for (const dep of plan.dependencies.modulesAndAssets) {
62295
- if (dep.type === "asset" && existsSync21(dep.resolvedPath)) {
62296
- const destAsset = join14(skillDir, dep.path);
62975
+ if (dep.type === "asset" && existsSync23(dep.resolvedPath)) {
62976
+ const destAsset = join15(skillDir, dep.path);
62297
62977
  mkdirSync8(dirname9(destAsset), { recursive: true });
62298
62978
  copyFileSync(dep.resolvedPath, destAsset);
62299
62979
  }
@@ -62321,9 +63001,134 @@ class SkillExporter {
62321
63001
  playbooksCount: plan.playbooks.length,
62322
63002
  actions: plan.actions.map((a) => a.id),
62323
63003
  playbooks: plan.playbooks.map((p) => p.id),
62324
- files
63004
+ files,
63005
+ usedExistingSkillMd
63006
+ };
63007
+ }
63008
+ async exportBatch(options) {
63009
+ if (!options.projectRoots || options.projectRoots.length === 0) {
63010
+ throw new BuilderError("No project roots provided for batch skill export.");
63011
+ }
63012
+ const results = [];
63013
+ const baseOutDir = resolve12(options.outDir || join15(process.cwd(), "dist", "skills"));
63014
+ mkdirSync8(baseOutDir, { recursive: true });
63015
+ const usedDirNames = new Set;
63016
+ for (const projectRoot of options.projectRoots) {
63017
+ const config = loadProjectConfig(projectRoot);
63018
+ let pkgSlug = getPackageSlug(config.id);
63019
+ if (usedDirNames.has(pkgSlug)) {
63020
+ pkgSlug = config.id.replace(/[^a-zA-Z0-9-_]/g, "-").replace(/^-+|-+$/g, "");
63021
+ }
63022
+ usedDirNames.add(pkgSlug);
63023
+ const pkgOutDir = join15(baseOutDir, `${pkgSlug}-skill`);
63024
+ const res = await this.export({
63025
+ ...options,
63026
+ projectRoot,
63027
+ outDir: pkgOutDir
63028
+ });
63029
+ results.push(res);
63030
+ }
63031
+ const totalActions = results.reduce((sum, r) => sum + r.actionsCount, 0);
63032
+ const totalPlaybooks = results.reduce((sum, r) => sum + r.playbooksCount, 0);
63033
+ return {
63034
+ results,
63035
+ outDir: baseOutDir,
63036
+ totalActions,
63037
+ totalPlaybooks
63038
+ };
63039
+ }
63040
+ static async exportBatch(options) {
63041
+ const exporter = new SkillExporter;
63042
+ return exporter.exportBatch(options);
63043
+ }
63044
+ async exportComposite(options) {
63045
+ if (!options.projectRoots || options.projectRoots.length === 0) {
63046
+ throw new BuilderError("No project roots provided for composite skill export.");
63047
+ }
63048
+ if (!options.bundleName || !options.bundleName.trim()) {
63049
+ throw new BuilderError("bundleName is required for composite skill export.");
63050
+ }
63051
+ const bundleSlug = getPackageSlug(options.bundleName);
63052
+ const defaultSkillDir = join15(process.cwd(), "dist", `${bundleSlug}-skill`);
63053
+ const skillDir = resolve12(options.outDir || defaultSkillDir);
63054
+ mkdirSync8(skillDir, { recursive: true });
63055
+ const packagesDestDir = join15(skillDir, "packages");
63056
+ mkdirSync8(packagesDestDir, { recursive: true });
63057
+ const packageInfos = [];
63058
+ const packageSummaries = [];
63059
+ const usedDirNames = new Set;
63060
+ for (const projectRoot of options.projectRoots) {
63061
+ const config = loadProjectConfig(projectRoot);
63062
+ let pkgSlug = getPackageSlug(config.id);
63063
+ if (usedDirNames.has(pkgSlug)) {
63064
+ pkgSlug = config.id.replace(/[^a-zA-Z0-9-_]/g, "-").replace(/^-+|-+$/g, "");
63065
+ }
63066
+ usedDirNames.add(pkgSlug);
63067
+ const destPkgDir = join15(packagesDestDir, pkgSlug);
63068
+ const singleExport = await this.export({
63069
+ projectRoot,
63070
+ outDir: destPkgDir,
63071
+ archive: false,
63072
+ skipSkillMd: true
63073
+ });
63074
+ const manifest = loadManifest(destPkgDir) || loadManifest(projectRoot);
63075
+ const playbooks = loadPlaybooks(projectRoot, config.playbooksDir);
63076
+ const actionEntries = singleExport.actions.map((actId) => ({
63077
+ id: actId,
63078
+ description: manifest?.actions?.[actId]?.description
63079
+ }));
63080
+ packageInfos.push({
63081
+ config,
63082
+ actions: actionEntries,
63083
+ playbooks: Array.from(playbooks.values()),
63084
+ packageDir: pkgSlug
63085
+ });
63086
+ packageSummaries.push({
63087
+ packageId: config.id,
63088
+ actions: singleExport.actions,
63089
+ playbooks: singleExport.playbooks
63090
+ });
63091
+ }
63092
+ const existingSkillPath = findExistingCompositeSkillMd(options);
63093
+ if (existingSkillPath) {
63094
+ const destSkillMdPath = join15(skillDir, "SKILL.md");
63095
+ if (resolve12(existingSkillPath) !== resolve12(destSkillMdPath)) {
63096
+ copyFileSync(existingSkillPath, destSkillMdPath);
63097
+ }
63098
+ } else {
63099
+ const description = options.description || `ActionDock \u590D\u5408\u6280\u80FD\u5957\u4EF6\uFF0C\u805A\u5408 ${packageInfos.map((p) => p.config.name).join("\u3001")}`;
63100
+ const compositeSkillMd = generateCompositeSkillMd(options.bundleName, description, packageInfos);
63101
+ writeFileSync10(join15(skillDir, "SKILL.md"), compositeSkillMd, "utf-8");
63102
+ }
63103
+ let archivePath;
63104
+ if (options.archive) {
63105
+ let format = "zip";
63106
+ if (options.archiveFormat === "tar.gz" || options.archive === "tar.gz") {
63107
+ format = "tar.gz";
63108
+ } else if (options.archiveFormat === "zip" || options.archive === "zip") {
63109
+ format = "zip";
63110
+ }
63111
+ archivePath = createArchive(skillDir, format);
63112
+ }
63113
+ const files = scanRelativeFiles(skillDir);
63114
+ const totalActions = packageSummaries.reduce((sum, p) => sum + p.actions.length, 0);
63115
+ const totalPlaybooks = packageSummaries.reduce((sum, p) => sum + p.playbooks.length, 0);
63116
+ return {
63117
+ bundleName: options.bundleName,
63118
+ skillDir,
63119
+ archivePath,
63120
+ packagesCount: packageInfos.length,
63121
+ actionsCount: totalActions,
63122
+ playbooksCount: totalPlaybooks,
63123
+ packages: packageSummaries,
63124
+ files,
63125
+ usedExistingSkillMd: existingSkillPath
62325
63126
  };
62326
63127
  }
63128
+ static async exportComposite(options) {
63129
+ const exporter = new SkillExporter;
63130
+ return exporter.exportComposite(options);
63131
+ }
62327
63132
  static async export(options) {
62328
63133
  const exporter = new SkillExporter;
62329
63134
  return exporter.export(options);
@@ -62332,6 +63137,12 @@ class SkillExporter {
62332
63137
  async function exportSkill(options) {
62333
63138
  return SkillExporter.export(options);
62334
63139
  }
63140
+ async function exportSkillBatch(options) {
63141
+ return SkillExporter.exportBatch(options);
63142
+ }
63143
+ async function exportCompositeSkill(options) {
63144
+ return SkillExporter.exportComposite(options);
63145
+ }
62335
63146
  // src/commands/build.ts
62336
63147
  function registerBuildCommand(program) {
62337
63148
  program.command("build").description("Build project actions into a single standalone executable").option("-P, --package <id>", "Target package ID or path").option("-t, --target <target>", "Target compilation platform (e.g. bun, linux-x64, darwin-arm64, windows-x64)").option("-o, --out <path>", "Output executable path").option("-a, --actions <actions...>", "Only build specific action(s) into the standalone binary").option("-m, --minify", "Minify bundled JavaScript (default: true)", true).option("--no-minify", "Disable JavaScript minification").option("--bytecode", "Compile JavaScript to bytecode for faster startup (default: true)", true).option("--no-bytecode", "Disable bytecode compilation").action(async (options) => {
@@ -62847,19 +63658,109 @@ function registerDoctorCommand(program) {
62847
63658
  }
62848
63659
 
62849
63660
  // src/commands/export.ts
63661
+ import { existsSync as existsSync24 } from "fs";
63662
+ import { basename as basename9 } from "path";
63663
+ function parseListOption2(val, prev = []) {
63664
+ const parts = val.split(",").map((s) => s.trim()).filter(Boolean);
63665
+ return [...prev, ...parts];
63666
+ }
62850
63667
  function registerExportCommand(program) {
62851
63668
  const exportCmd = program.command("export").description("Export project artifacts");
62852
- exportCmd.command("skill").description("Export Skill directory for AI Agents (default: source skill; use -s/--standalone for pre-built standalone binary)").option("-P, --package <id>", "Target package ID or path").option("-s, --standalone", "Export pre-compiled standalone binary skill (for environments without ActionDock runtime)").option("-t, --target <target>", "Target compilation platform for standalone mode (e.g. host, linux-x64, darwin-arm64, windows-x64)").option("-o, --out <path>", "Output skill directory").option("-p, --playbook <playbooks...>", "Only export specific playbook(s) and their dependent actions (Playbook-driven minimal export)").option("-a, --actions <actions...>", "Only export specific action(s)").option("-m, --minify", "Minify bundled JavaScript in standalone mode (default: true)", true).option("--no-minify", "Disable JavaScript minification in standalone mode").option("--bytecode", "Compile JavaScript to bytecode in standalone mode (default: true)", true).option("--no-bytecode", "Disable bytecode compilation in standalone mode").option("-z, --archive", "Create a .zip archive of the exported skill").action(async (options) => {
62853
- const root = options.package ? resolvePackageRoot(options.package) : findProjectRoot();
62854
- if (!root) {
62855
- if (options.package) {
62856
- throw new ExecutionError(`Package '${options.package}' not found in linked packages or path`);
63669
+ exportCmd.command("skill").description("Export Skill directory for AI Agents (default: source skill; use -s/--standalone for pre-built standalone binary)").option("-P, --package <id...>", "Target package ID(s) or path(s) (can be specified multiple times or comma-separated)", parseListOption2, []).option("--workspace", "Export all packages discovered in current workspace").option("--all", "Export all linked packages from global registry").option("--bundle [name]", "Export multiple packages as a unified composite Skill bundle (defaults to workspace directory name if omitted)").option("-s, --standalone", "Export pre-compiled standalone binary skill (for environments without ActionDock runtime)").option("-t, --target <target>", "Target compilation platform for standalone mode (e.g. host, linux-x64, darwin-arm64, windows-x64)").option("-o, --out <path>", "Output skill directory").option("-p, --playbook <playbooks...>", "Only export specific playbook(s) and their dependent actions (Playbook-driven minimal export)").option("-a, --actions <actions...>", "Only export specific action(s)").option("-m, --minify", "Minify bundled JavaScript in standalone mode (default: true)", true).option("--no-minify", "Disable JavaScript minification in standalone mode").option("--bytecode", "Compile JavaScript to bytecode in standalone mode (default: true)", true).option("--no-bytecode", "Disable bytecode compilation in standalone mode").option("-z, --archive", "Create a .zip archive of the exported skill").option("--skill-md <path>", "Use specified existing SKILL.md file instead of auto-generating").action(async (options) => {
63670
+ const isStandalone = Boolean(options.standalone);
63671
+ if (options.bundle && isStandalone) {
63672
+ throw new ExecutionError("Composite Skill export (--bundle) currently only supports source mode. Please remove --standalone to export as a composite workspace Skill.");
63673
+ }
63674
+ const sourceCount = (options.all ? 1 : 0) + (options.workspace ? 1 : 0) + (options.package && options.package.length > 0 ? 1 : 0);
63675
+ if (sourceCount > 1) {
63676
+ throw new ExecutionError("Options -P/--package, --workspace, and --all are mutually exclusive. Please specify only one package selection mode.");
63677
+ }
63678
+ let roots = [];
63679
+ if (options.all) {
63680
+ const linked = listLinkedPackages();
63681
+ roots = linked.map((p) => p.path).filter((p) => existsSync24(p));
63682
+ if (roots.length === 0) {
63683
+ throw new ExecutionError("No linked packages found in global registry.");
63684
+ }
63685
+ } else if (options.workspace) {
63686
+ const cwd = process.cwd();
63687
+ const discovered = discoverProjects(cwd);
63688
+ roots = discovered.length > 0 ? discovered : findProjectRoot(cwd) ? [findProjectRoot(cwd)] : [];
63689
+ if (roots.length === 0) {
63690
+ throw new ExecutionError(`No ActionDock packages found in workspace '${cwd}'.
63691
+ Make sure child directories contain actiondock.json.`);
63692
+ }
63693
+ } else if (options.package && options.package.length > 0) {
63694
+ for (const pkgIdOrPath of options.package) {
63695
+ const root = resolvePackageRoot(pkgIdOrPath);
63696
+ if (!root) {
63697
+ throw new ExecutionError(`Package '${pkgIdOrPath}' not found in linked packages or path`);
63698
+ }
63699
+ if (!roots.includes(root)) {
63700
+ roots.push(root);
63701
+ }
63702
+ }
63703
+ } else {
63704
+ const root = findProjectRoot();
63705
+ if (root) {
63706
+ roots.push(root);
63707
+ } else {
63708
+ throw new ExecutionError(`Not in an ActionDock project (actiondock.json not found).
63709
+ Please specify -P, --package <id>, --workspace, --all, or cd into a project directory.`);
62857
63710
  }
62858
- throw new ExecutionError(`Not in an ActionDock project (actiondock.json not found).
62859
- Please specify -P, --package <id> or cd into a project directory.`);
62860
63711
  }
62861
- const isStandalone = Boolean(options.standalone);
63712
+ if (roots.length > 1 && (options.actions || options.playbook)) {
63713
+ throw new ExecutionError("Filtering options (--actions, --playbook) can only be used when exporting a single package.");
63714
+ }
62862
63715
  try {
63716
+ if (options.bundle !== undefined) {
63717
+ const bundleName = typeof options.bundle === "string" && options.bundle.trim() ? options.bundle.trim() : basename9(process.cwd());
63718
+ console.log(`Exporting composite Skill bundle '${bundleName}' (${roots.length} package${roots.length > 1 ? "s" : ""})...`);
63719
+ const result = await exportCompositeSkill({
63720
+ bundleName,
63721
+ projectRoots: roots,
63722
+ outDir: options.out,
63723
+ archive: options.archive,
63724
+ workspaceRoot: options.workspace ? process.cwd() : undefined,
63725
+ skillMdPath: options.skillMd
63726
+ });
63727
+ console.log(`[OK] Successfully exported Composite Skill: ${result.bundleName}`);
63728
+ console.log(` Packages: ${result.packagesCount}`);
63729
+ console.log(` Actions: ${result.actionsCount}`);
63730
+ console.log(` Playbooks: ${result.playbooksCount}`);
63731
+ console.log(` Skill Dir: ${result.skillDir}`);
63732
+ if (result.usedExistingSkillMd) {
63733
+ console.log(` SKILL.md: Reused existing file from ${result.usedExistingSkillMd}`);
63734
+ }
63735
+ if (result.archivePath) {
63736
+ console.log(` Archive: ${result.archivePath}`);
63737
+ }
63738
+ return;
63739
+ }
63740
+ if (roots.length > 1) {
63741
+ console.log(`Batch exporting ${roots.length} Skill packages...`);
63742
+ const batchRes = await exportSkillBatch({
63743
+ projectRoots: roots,
63744
+ mode: isStandalone ? "standalone" : "source",
63745
+ standalone: isStandalone,
63746
+ target: options.target,
63747
+ outDir: options.out,
63748
+ archive: options.archive,
63749
+ playbooks: options.playbook,
63750
+ actions: options.actions,
63751
+ minify: options.minify,
63752
+ bytecode: options.bytecode,
63753
+ skillMdPath: options.skillMd
63754
+ });
63755
+ console.log(`[OK] Successfully batch exported ${batchRes.results.length} Skill packages to: ${batchRes.outDir}`);
63756
+ for (const res of batchRes.results) {
63757
+ console.log(` - ${res.packageId} (v${res.version}): ${res.actionsCount} actions, ${res.playbooksCount} playbooks -> ${res.skillDir}`);
63758
+ }
63759
+ console.log(` Total Actions: ${batchRes.totalActions}`);
63760
+ console.log(` Total Playbooks: ${batchRes.totalPlaybooks}`);
63761
+ return;
63762
+ }
63763
+ const root = roots[0];
62863
63764
  console.log(`Exporting ${isStandalone ? "standalone binary" : "source"} Skill artifact...`);
62864
63765
  const result = await exportSkill({
62865
63766
  projectRoot: root,
@@ -62871,13 +63772,17 @@ Please specify -P, --package <id> or cd into a project directory.`);
62871
63772
  playbooks: options.playbook,
62872
63773
  actions: options.actions,
62873
63774
  minify: options.minify,
62874
- bytecode: options.bytecode
63775
+ bytecode: options.bytecode,
63776
+ skillMdPath: options.skillMd
62875
63777
  });
62876
63778
  console.log(`[OK] Successfully exported ${result.mode === "standalone" ? "Standalone" : "Source"} Skill: ${result.packageId} (v${result.version})`);
62877
63779
  console.log(` Mode: ${result.mode}${result.mode === "standalone" ? ` (target: ${result.target})` : ""}`);
62878
63780
  console.log(` Actions: ${result.actionsCount}`);
62879
63781
  console.log(` Playbooks: ${result.playbooksCount}`);
62880
63782
  console.log(` Skill Dir: ${result.skillDir}`);
63783
+ if (result.usedExistingSkillMd) {
63784
+ console.log(` SKILL.md: Reused existing file from ${result.usedExistingSkillMd}`);
63785
+ }
62881
63786
  if (result.archivePath) {
62882
63787
  console.log(` Archive: ${result.archivePath}`);
62883
63788
  }
@@ -62888,7 +63793,7 @@ Please specify -P, --package <id> or cd into a project directory.`);
62888
63793
  }
62889
63794
 
62890
63795
  // src/commands/info.ts
62891
- import { existsSync as existsSync22 } from "fs";
63796
+ import { existsSync as existsSync25 } from "fs";
62892
63797
  async function getProjectDetailInfo(root) {
62893
63798
  const config = loadProjectConfig(root);
62894
63799
  const manifest = loadManifest(root);
@@ -63164,7 +64069,7 @@ Declared Config Keys:`);
63164
64069
  } catch {}
63165
64070
  }
63166
64071
  for (const pkg of linkedList) {
63167
- if (!existsSync22(pkg.path))
64072
+ if (!existsSync25(pkg.path))
63168
64073
  continue;
63169
64074
  if (seenPaths.has(pkg.path))
63170
64075
  continue;
@@ -63354,12 +64259,12 @@ function registerLinkCommands(program) {
63354
64259
 
63355
64260
  // src/commands/mcp.ts
63356
64261
  import { resolve as resolve13 } from "path";
63357
- function parseListOption2(val, prev = []) {
64262
+ function parseListOption3(val, prev = []) {
63358
64263
  const parts = val.split(",").map((s) => s.trim()).filter(Boolean);
63359
64264
  return [...prev, ...parts];
63360
64265
  }
63361
64266
  function registerMcpCommands2(program) {
63362
- const mcpCommand = program.command("mcp").description("Model Context Protocol (MCP) server for ActionDock Actions (STDIO default)").option("-d, --dir <path>", "Project root directory or directories (can be specified multiple times or comma-separated)", parseListOption2, []).option("--package <package-id>", "Specific linked package ID(s) to serve (can be specified multiple times or comma-separated)", parseListOption2, []).option("--all", "Serve all linked packages from global registry").option("--timeout <duration>", "Execution timeout (e.g. 30s, 5m, 500ms)").action(async (options) => {
64267
+ const mcpCommand = program.command("mcp").description("Model Context Protocol (MCP) server for ActionDock Actions (STDIO default)").option("-d, --dir <path>", "Project root directory or directories (can be specified multiple times or comma-separated)", parseListOption3, []).option("--package <package-id>", "Specific linked package ID(s) to serve (can be specified multiple times or comma-separated)", parseListOption3, []).option("--all", "Serve all linked packages from global registry").option("--timeout <duration>", "Execution timeout (e.g. 30s, 5m, 500ms)").action(async (options) => {
63363
64268
  let timeoutMs;
63364
64269
  if (options.timeout) {
63365
64270
  try {
@@ -63382,7 +64287,7 @@ function registerMcpCommands2(program) {
63382
64287
  throw new ExecutionError(`Failed to start MCP STDIO server: ${err.message}`);
63383
64288
  }
63384
64289
  });
63385
- mcpCommand.command("serve").description("Start the ActionDock MCP server over HTTP transport").option("-p, --port <port>", "Port to listen on (default: 5178)", "5178").option("-H, --host <host>", "Host address to bind to (default: 127.0.0.1)", "127.0.0.1").option("-t, --token <token>", "Authentication token for securing the endpoint (or set ACTIONDOCK_TOKEN)").option("--token-env <env>", "Environment variable name containing the authentication token").option("--allow-insecure-no-auth", "Allow non-loopback host binding without authentication token (INSECURE)").option("--cors-origin <origin>", "Allowed CORS origin (can be specified multiple times)", (val, prev = []) => [...prev, val], []).option("--max-body <size>", "Maximum allowed JSON request body size (e.g. 1mb, 500kb)", "1mb").option("-d, --dir <path>", "Project root directory or directories (can be specified multiple times or comma-separated)", parseListOption2, []).option("--package <package-id>", "Specific linked package ID(s) to serve (can be specified multiple times or comma-separated)", parseListOption2, []).option("--all", "Serve all linked packages from global registry").option("--timeout <duration>", "Execution timeout (e.g. 30s, 5m, 500ms)").action(async (options) => {
64290
+ mcpCommand.command("serve").description("Start the ActionDock MCP server over HTTP transport").option("-p, --port <port>", "Port to listen on (default: 5178)", "5178").option("-H, --host <host>", "Host address to bind to (default: 127.0.0.1)", "127.0.0.1").option("-t, --token <token>", "Authentication token for securing the endpoint (or set ACTIONDOCK_TOKEN)").option("--token-env <env>", "Environment variable name containing the authentication token").option("--allow-insecure-no-auth", "Allow non-loopback host binding without authentication token (INSECURE)").option("--cors-origin <origin>", "Allowed CORS origin (can be specified multiple times)", (val, prev = []) => [...prev, val], []).option("--max-body <size>", "Maximum allowed JSON request body size (e.g. 1mb, 500kb)", "1mb").option("-d, --dir <path>", "Project root directory or directories (can be specified multiple times or comma-separated)", parseListOption3, []).option("--package <package-id>", "Specific linked package ID(s) to serve (can be specified multiple times or comma-separated)", parseListOption3, []).option("--all", "Serve all linked packages from global registry").option("--timeout <duration>", "Execution timeout (e.g. 30s, 5m, 500ms)").action(async (options) => {
63386
64291
  const port = parseInt(options.port, 10) || 5178;
63387
64292
  const host = options.host || "127.0.0.1";
63388
64293
  const token = options.token || (options.tokenEnv ? process.env[options.tokenEnv] : undefined) || process.env.ACTIONDOCK_TOKEN;
@@ -63477,7 +64382,7 @@ Stopping MCP HTTP server...`);
63477
64382
  }
63478
64383
 
63479
64384
  // src/commands/playbook.ts
63480
- import { existsSync as existsSync23, mkdirSync as mkdirSync9, writeFileSync as writeFileSync11 } from "fs";
64385
+ import { existsSync as existsSync26, mkdirSync as mkdirSync9, writeFileSync as writeFileSync11 } from "fs";
63481
64386
  import { dirname as dirname10, resolve as resolve14 } from "path";
63482
64387
  function registerPlaybookCommands2(program) {
63483
64388
  const pbCmd = program.command("playbook").description("Manage task Playbooks (Task SOPs for AI Agents)");
@@ -63589,7 +64494,7 @@ Run 'ad link' inside an Action package to register it.`
63589
64494
  }
63590
64495
  const aggregated = [];
63591
64496
  for (const pkg of linkedList) {
63592
- if (!existsSync23(pkg.path))
64497
+ if (!existsSync26(pkg.path))
63593
64498
  continue;
63594
64499
  try {
63595
64500
  const config = loadProjectConfig(pkg.path);
@@ -63670,13 +64575,13 @@ Run 'ad link' inside an Action package to register it.`
63670
64575
  try {
63671
64576
  const config = loadProjectConfig(root);
63672
64577
  const pbDir = resolve14(root, config.playbooksDir || "playbooks");
63673
- if (!existsSync23(pbDir)) {
64578
+ if (!existsSync26(pbDir)) {
63674
64579
  mkdirSync9(pbDir, { recursive: true });
63675
64580
  }
63676
64581
  const cleanName = id.replace(/[^a-zA-Z0-9-_]/g, "-");
63677
64582
  const targetRelFile = options.file || `${cleanName}.md`;
63678
64583
  const targetFullFile = resolve14(pbDir, targetRelFile);
63679
- if (existsSync23(targetFullFile)) {
64584
+ if (existsSync26(targetFullFile)) {
63680
64585
  throw new ExecutionError(`File '${targetFullFile}' already exists`);
63681
64586
  }
63682
64587
  mkdirSync9(dirname10(targetFullFile), { recursive: true });
@@ -63824,7 +64729,7 @@ This playbook provides task execution guidance for AI Agents.
63824
64729
  throw new ArgumentError("Not in an ActionDock project, and no packages linked.");
63825
64730
  }
63826
64731
  for (const pkg of linkedList) {
63827
- if (!existsSync23(pkg.path))
64732
+ if (!existsSync26(pkg.path))
63828
64733
  continue;
63829
64734
  try {
63830
64735
  const config = loadProjectConfig(pkg.path);
@@ -64113,7 +65018,7 @@ Use 'ad profile use <name>' to switch or 'ad run <action> --profile <name>' to e
64113
65018
  }
64114
65019
 
64115
65020
  // src/commands/runs.ts
64116
- import { existsSync as existsSync24 } from "fs";
65021
+ import { existsSync as existsSync27 } from "fs";
64117
65022
  function printRunRecord(run) {
64118
65023
  console.log(`Run: ${run.id}`);
64119
65024
  console.log(`Action: ${run.actionId}`);
@@ -64220,7 +65125,7 @@ function registerRunsCommands2(program) {
64220
65125
  }
64221
65126
  let allRecords = [];
64222
65127
  for (const pkg of linkedList) {
64223
- if (!existsSync24(pkg.path))
65128
+ if (!existsSync27(pkg.path))
64224
65129
  continue;
64225
65130
  try {
64226
65131
  const projConfig = loadProjectConfig(pkg.path);
@@ -64326,7 +65231,7 @@ function registerRunsCommands2(program) {
64326
65231
  if (!foundRun) {
64327
65232
  const linkedList = listLinkedPackages();
64328
65233
  for (const pkg of linkedList) {
64329
- if (!existsSync24(pkg.path))
65234
+ if (!existsSync27(pkg.path))
64330
65235
  continue;
64331
65236
  try {
64332
65237
  const projConfig = loadProjectConfig(pkg.path);
@@ -64533,7 +65438,7 @@ Stopping ActionDock server...`);
64533
65438
  }
64534
65439
 
64535
65440
  // src/commands/state.ts
64536
- import { existsSync as existsSync25 } from "fs";
65441
+ import { existsSync as existsSync28 } from "fs";
64537
65442
  function getTargetRoot2(packageOption, keyHint) {
64538
65443
  let targetPackage = packageOption;
64539
65444
  let effectiveKey = keyHint || "";
@@ -64641,7 +65546,7 @@ function registerStateCommands2(program) {
64641
65546
  }
64642
65547
  const aggregated = [];
64643
65548
  for (const pkg of linkedList) {
64644
- if (!existsSync25(pkg.path))
65549
+ if (!existsSync28(pkg.path))
64645
65550
  continue;
64646
65551
  try {
64647
65552
  const projConfig = loadProjectConfig(pkg.path);
@@ -64940,16 +65845,16 @@ function registerStateCommands2(program) {
64940
65845
 
64941
65846
  // src/commands/test.ts
64942
65847
  import { spawnSync as spawnSync4 } from "child_process";
64943
- import { existsSync as existsSync26, readFileSync as readFileSync12 } from "fs";
64944
- import { join as join15 } from "path";
65848
+ import { existsSync as existsSync29, readFileSync as readFileSync12 } from "fs";
65849
+ import { join as join16 } from "path";
64945
65850
  function registerTestCommand(program) {
64946
65851
  program.command("test [pattern]").description("Run project tests using configured test runner (node:test or bun test)").action((pattern) => {
64947
65852
  const root = findProjectRoot();
64948
65853
  const cwd = root || process.cwd();
64949
- const pkgJsonPath = join15(cwd, "package.json");
65854
+ const pkgJsonPath = join16(cwd, "package.json");
64950
65855
  let testCmd = "npm";
64951
65856
  let testArgs = ["test"];
64952
- if (existsSync26(pkgJsonPath)) {
65857
+ if (existsSync29(pkgJsonPath)) {
64953
65858
  try {
64954
65859
  const pkg = JSON.parse(readFileSync12(pkgJsonPath, "utf-8"));
64955
65860
  if (pkg.scripts?.test?.includes("bun test") && typeof globalThis.Bun !== "undefined") {
@@ -64993,7 +65898,7 @@ function applyCommonOptions(cmd) {
64993
65898
  }
64994
65899
  function createCliProgram() {
64995
65900
  const program = new Command2;
64996
- program.name("ad").description("ActionDock (ad) 2.0 - Toolchain for building and shipping standalone AI Agent Actions & Skills").version("2.0.10", "-v, --version");
65901
+ program.name("ad").description("ActionDock (ad) 2.0 - Toolchain for building and shipping standalone AI Agent Actions & Skills").version("2.0.11", "-v, --version");
64997
65902
  program.option("-V", "output the version number");
64998
65903
  program.on("option:V", () => {
64999
65904
  program._outputConfiguration.writeOut(`${program.version()}