agentwheel 0.14.4 → 0.14.6

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
@@ -6,12 +6,13 @@ import {
6
6
  isIgnoredGeneratedEntry,
7
7
  pathExists,
8
8
  writeJsonAtomic
9
- } from "./chunk-B3FMBTWC.js";
9
+ } from "./chunk-QJTTISLY.js";
10
10
 
11
11
  // src/cli/index.ts
12
- import { mkdir as mkdir17, rm as rm10, writeFile as writeFile16 } from "fs/promises";
12
+ import { existsSync } from "fs";
13
+ import { mkdir as mkdir20, rm as rm11, writeFile as writeFile19 } from "fs/promises";
13
14
  import { homedir as homedir9 } from "os";
14
- import { dirname as dirname25, join as join38, resolve as resolve18 } from "path";
15
+ import { dirname as dirname28, join as join40, resolve as resolve20 } from "path";
15
16
  import { fileURLToPath as fileURLToPath3 } from "url";
16
17
  import { Command } from "commander";
17
18
 
@@ -60,6 +61,18 @@ var packageItemRequireSchema = z.union([
60
61
  z.string().min(1),
61
62
  packageItemRequireObjectSchema
62
63
  ]);
64
+ var packageItemSuggestObjectSchema = z.object({
65
+ alias: z.string().min(1),
66
+ select: z.array(z.string().min(1)).optional(),
67
+ optional: z.boolean().optional(),
68
+ runtimes: z.array(z.string().min(1)).optional(),
69
+ reason: z.string().min(1).optional(),
70
+ when: z.string().min(1).optional()
71
+ }).passthrough();
72
+ var packageItemSuggestSchema = z.union([
73
+ z.string().min(1),
74
+ packageItemSuggestObjectSchema
75
+ ]);
63
76
  var composedFromEntrySchema = z.object({
64
77
  selector: z.string().min(1),
65
78
  hash: z.string().min(16)
@@ -78,6 +91,7 @@ var artifactSchema = z.object({
78
91
  assets: z.array(packageAssetSchema).optional(),
79
92
  required: z.boolean().optional(),
80
93
  requires: z.array(packageItemRequireSchema).optional(),
94
+ suggests: z.array(packageItemSuggestSchema).optional(),
81
95
  compose: z.array(packageComposeEntrySchema).optional(),
82
96
  runtimes: z.array(z.string().min(1)).optional(),
83
97
  composedFrom: z.array(composedFromEntrySchema).optional()
@@ -102,7 +116,7 @@ var targetMappingSchema = z2.object({
102
116
  "copilot-prompt",
103
117
  "copilot-agent"
104
118
  ]).optional(),
105
- merge: z2.enum(["json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
119
+ merge: z2.enum(["json-deep", "openclaw-json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
106
120
  mode: z2.enum(["managed-block"]).optional()
107
121
  });
108
122
  var targetRegistrySchema = z2.record(installationTypeSchema, targetMappingSchema);
@@ -363,13 +377,13 @@ var openClawAdapter = {
363
377
  user: { enabled: true, root: "home", dest: ".openclaw/skills" }
364
378
  },
365
379
  mcp: {
366
- user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "json-deep" }
380
+ user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "openclaw-json-deep" }
367
381
  },
368
382
  settings: {
369
- user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "json-deep" }
383
+ user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "openclaw-json-deep" }
370
384
  },
371
385
  plugins: {
372
- local: { enabled: true, dest: ".openclaw/plugins", formats: ["openclaw-plugin"], semantic: "openclaw-plugin" }
386
+ local: { enabled: true, dest: ".openclaw/plugins", formats: ["openclaw-plugin", "openclaw-clawhub-plugin"], semantic: "openclaw-plugin" }
373
387
  }
374
388
  }
375
389
  };
@@ -453,7 +467,7 @@ async function resolveAdapter(options) {
453
467
 
454
468
  // src/install/apply.ts
455
469
  import { execFile as execFile3 } from "child_process";
456
- import { mkdtemp as mkdtemp2, rm as rm4, writeFile as writeFile8 } from "fs/promises";
470
+ import { mkdtemp as mkdtemp2, rm as rm4, writeFile as writeFile9 } from "fs/promises";
457
471
  import { tmpdir as tmpdir3 } from "os";
458
472
  import { basename as basename3, join as join5 } from "path";
459
473
  import { promisify as promisify3 } from "util";
@@ -759,13 +773,13 @@ async function spawnWithInput(command, args, input) {
759
773
  if (result.stderr) throw new Error(result.stderr);
760
774
  }
761
775
  function waitForProcess(child, label) {
762
- return new Promise((resolve19, reject) => {
776
+ return new Promise((resolve21, reject) => {
763
777
  const stderr = [];
764
778
  child.stderr?.on("data", (chunk) => stderr.push(chunk));
765
779
  child.on("error", reject);
766
780
  child.on("close", (code) => {
767
781
  const message = Buffer.concat(stderr).toString("utf8");
768
- if (code === 0) resolve19({ stderr: "" });
782
+ if (code === 0) resolve21({ stderr: "" });
769
783
  else reject(new Error(`${label} exited ${code}${message ? `: ${message}` : ""}`));
770
784
  });
771
785
  });
@@ -869,6 +883,58 @@ function dedupeArray(values) {
869
883
  return out;
870
884
  }
871
885
 
886
+ // src/install/openclaw-json-merge.ts
887
+ import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
888
+ import { dirname as dirname5 } from "path";
889
+ async function mergeOpenClawJsonFile(sourcePath, destPath) {
890
+ const source = expandEnvPlaceholders(
891
+ normalizeOpenClawConfig(JSON.parse(await readFile6(sourcePath, "utf8"))),
892
+ sourcePath
893
+ );
894
+ const current = await pathExists(destPath) ? JSON.parse(await readFile6(destPath, "utf8")) : {};
895
+ const merged = deepMerge(current, source);
896
+ await mkdir4(dirname5(destPath), { recursive: true });
897
+ await writeFile5(destPath, `${JSON.stringify(merged, null, 2)}
898
+ `, "utf8");
899
+ }
900
+ function expandEnvPlaceholders(value, sourcePath) {
901
+ if (typeof value === "string") {
902
+ return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, name) => {
903
+ const replacement = process.env[name];
904
+ if (replacement === void 0) {
905
+ throw new Error(`Missing environment variable ${name} while rendering OpenClaw JSON merge artifact ${sourcePath}`);
906
+ }
907
+ return replacement;
908
+ });
909
+ }
910
+ if (Array.isArray(value)) return value.map((item) => expandEnvPlaceholders(item, sourcePath));
911
+ if (!isRecord(value)) return value;
912
+ return Object.fromEntries(
913
+ Object.entries(value).map(([key, child]) => [key, expandEnvPlaceholders(child, sourcePath)])
914
+ );
915
+ }
916
+ function normalizeOpenClawConfig(value) {
917
+ if (!isRecord(value)) return value;
918
+ const rootMcpServers = isRecord(value.mcpServers) ? value.mcpServers : void 0;
919
+ if (!rootMcpServers) return value;
920
+ const out = { ...value };
921
+ const normalizedServers = {};
922
+ for (const [name, server] of Object.entries(rootMcpServers)) {
923
+ if (isRecord(server)) normalizedServers[name] = normalizeOpenClawMcpServer(server);
924
+ }
925
+ const mcp = isRecord(out.mcp) ? out.mcp : {};
926
+ out.mcp = deepMerge(mcp, { servers: normalizedServers });
927
+ delete out.mcpServers;
928
+ return out;
929
+ }
930
+ function normalizeOpenClawMcpServer(server) {
931
+ const out = { ...server };
932
+ const type = typeof out.type === "string" ? out.type : void 0;
933
+ if (type && typeof out.transport !== "string") out.transport = type;
934
+ delete out.type;
935
+ return out;
936
+ }
937
+
872
938
  // src/install/manifest.ts
873
939
  import { createHash as createHash2 } from "crypto";
874
940
  import { resolve as resolve3 } from "path";
@@ -900,7 +966,7 @@ var manifestEntryV1Schema = z4.object({
900
966
  semanticCommand: z4.array(z4.string()).optional(),
901
967
  semanticPlugin: semanticPluginSpecSchema.optional(),
902
968
  executed: z4.boolean().optional(),
903
- mergeStrategy: z4.enum(["json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
969
+ mergeStrategy: z4.enum(["json-deep", "openclaw-json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
904
970
  mode: z4.enum(["managed-block"]).optional(),
905
971
  composedFrom: z4.array(composedFromEntrySchema).optional()
906
972
  });
@@ -1092,8 +1158,8 @@ function assertOperationContained(operation, targetRoot) {
1092
1158
  }
1093
1159
 
1094
1160
  // src/install/transaction.ts
1095
- import { cp, mkdir as mkdir4, rm as rm2, stat as stat2 } from "fs/promises";
1096
- import { dirname as dirname5, join as join3 } from "path";
1161
+ import { cp, mkdir as mkdir5, rm as rm2, stat as stat2 } from "fs/promises";
1162
+ import { dirname as dirname6, join as join3 } from "path";
1097
1163
  function applyLockPath(targetRoot, adapter, scope = {}) {
1098
1164
  return join3(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-lock`);
1099
1165
  }
@@ -1158,7 +1224,7 @@ async function recordBackup(operation, index, targetRoot, adapter, transport = l
1158
1224
  }
1159
1225
  const backupPath = join3(applyBackupDir(targetRoot, adapter, scope), String(index));
1160
1226
  await rm2(backupPath, { recursive: true, force: true });
1161
- await mkdir4(dirname5(backupPath), { recursive: true });
1227
+ await mkdir5(dirname6(backupPath), { recursive: true });
1162
1228
  await cp(operation.destPath, backupPath, { recursive: operation.kind === "dir", dereference: true });
1163
1229
  return {
1164
1230
  index,
@@ -1212,16 +1278,16 @@ async function localPathExists(path) {
1212
1278
  }
1213
1279
 
1214
1280
  // src/install/toml-merge.ts
1215
- import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
1216
- import { dirname as dirname6 } from "path";
1281
+ import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
1282
+ import { dirname as dirname7 } from "path";
1217
1283
  async function mergeCodexTomlMcp(sourcePath, destPath) {
1218
- const source = JSON.parse(await readFile6(sourcePath, "utf8"));
1284
+ const source = JSON.parse(await readFile7(sourcePath, "utf8"));
1219
1285
  const servers = extractMcpServers(source);
1220
- const current = await pathExists(destPath) ? await readFile6(destPath, "utf8") : "";
1286
+ const current = await pathExists(destPath) ? await readFile7(destPath, "utf8") : "";
1221
1287
  const withoutManaged = removeManagedMcpSections(current, Object.keys(servers));
1222
1288
  const merged = appendMcpServers(withoutManaged, servers);
1223
- await mkdir5(dirname6(destPath), { recursive: true });
1224
- await writeFile5(destPath, merged, "utf8");
1289
+ await mkdir6(dirname7(destPath), { recursive: true });
1290
+ await writeFile6(destPath, merged, "utf8");
1225
1291
  }
1226
1292
  function extractMcpServers(source) {
1227
1293
  const raw = isRecord2(source.mcpServers) ? source.mcpServers : source;
@@ -1300,15 +1366,15 @@ function isRecord2(value) {
1300
1366
  }
1301
1367
 
1302
1368
  // src/install/yaml-merge.ts
1303
- import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
1304
- import { dirname as dirname7 } from "path";
1369
+ import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile7 } from "fs/promises";
1370
+ import { dirname as dirname8 } from "path";
1305
1371
  import { parse as parse2, stringify } from "yaml";
1306
1372
  async function mergeYamlFile(sourcePath, destPath) {
1307
- const source = parseYamlValue(await readFile7(sourcePath, "utf8"));
1308
- const current = await pathExists(destPath) ? parseYamlValue(await readFile7(destPath, "utf8")) : {};
1373
+ const source = parseYamlValue(await readFile8(sourcePath, "utf8"));
1374
+ const current = await pathExists(destPath) ? parseYamlValue(await readFile8(destPath, "utf8")) : {};
1309
1375
  const merged = deepMerge2(current, source);
1310
- await mkdir6(dirname7(destPath), { recursive: true });
1311
- await writeFile6(destPath, stringify(merged), "utf8");
1376
+ await mkdir7(dirname8(destPath), { recursive: true });
1377
+ await writeFile7(destPath, stringify(merged), "utf8");
1312
1378
  }
1313
1379
  function parseYamlValue(content) {
1314
1380
  return normalizeYamlValue(parse2(content));
@@ -1369,13 +1435,13 @@ function normalizeOwners(owners) {
1369
1435
 
1370
1436
  // src/install/instructions-block.ts
1371
1437
  import { createHash as createHash3 } from "crypto";
1372
- import { mkdtemp, readFile as readFile8, realpath, rm as rm3, writeFile as writeFile7 } from "fs/promises";
1438
+ import { mkdtemp, readFile as readFile9, realpath, rm as rm3, writeFile as writeFile8 } from "fs/promises";
1373
1439
  import { tmpdir as tmpdir2 } from "os";
1374
- import { basename as basename2, dirname as dirname8, join as join4, relative as relative2 } from "path";
1440
+ import { basename as basename2, dirname as dirname9, join as join4, relative as relative2 } from "path";
1375
1441
  var managedInstructionBlockMode = "managed-block";
1376
1442
  var managedInstructionBanner = "<!-- agentwheel-managed: edit fragments, not this block -->";
1377
1443
  async function desiredManagedInstructionBlockHash(sourcePath) {
1378
- const source = await readFile8(sourcePath, "utf8");
1444
+ const source = await readFile9(sourcePath, "utf8");
1379
1445
  return hashText(managedBlockBody(source));
1380
1446
  }
1381
1447
  async function readManagedInstructionBlockState(destPath, selector, transport) {
@@ -1394,18 +1460,18 @@ async function readManagedInstructionBlockState(destPath, selector, transport) {
1394
1460
  drifted: hash !== block.markerHash
1395
1461
  };
1396
1462
  }
1397
- async function writeManagedInstructionBlock(sourcePath, destPath, selector, transport, expectedHash) {
1398
- const source = await readFile8(sourcePath, "utf8");
1463
+ async function writeManagedInstructionBlock(sourcePath, destPath, selector, transport, options = {}) {
1464
+ const source = await readFile9(sourcePath, "utf8");
1399
1465
  const desired = renderManagedInstructionBlock(selector, source);
1400
1466
  const existing = await readOptionalText(destPath, transport);
1401
- const merged = upsertManagedInstructionBlock(existing ?? "", selector, desired.block, expectedHash);
1467
+ const merged = upsertManagedInstructionBlock(existing ?? "", selector, desired.block, options);
1402
1468
  await writeTextWithTransport(destPath, merged, transport);
1403
1469
  return desired.hash;
1404
1470
  }
1405
- async function removeManagedInstructionBlock(destPath, selector, transport, expectedHash) {
1471
+ async function removeManagedInstructionBlock(destPath, selector, transport, options = {}) {
1406
1472
  if (!await transport.pathExists(destPath)) return;
1407
1473
  const existing = await transport.readFile(destPath);
1408
- const updated = removeManagedBlockFromContent(existing, selector, expectedHash);
1474
+ const updated = removeManagedBlockFromContent(existing, selector, options);
1409
1475
  await writeTextWithTransport(destPath, updated, transport);
1410
1476
  }
1411
1477
  async function managedInstructionBlockLanded(destPath, selector, expectedHash, transport) {
@@ -1438,24 +1504,30 @@ ${body}<!-- END openpack:include ${selector} -->
1438
1504
  hash
1439
1505
  };
1440
1506
  }
1441
- function upsertManagedInstructionBlock(content, selector, block, expectedHash) {
1507
+ function upsertManagedInstructionBlock(content, selector, block, options) {
1508
+ const { expectedHash, allowDrift = false } = options;
1442
1509
  const existing = findManagedInstructionBlock(content, selector);
1443
1510
  if (!existing) {
1444
1511
  if (expectedHash) throw new Error(`Managed instruction block missing for ${selector}`);
1445
1512
  return appendManagedInstructionBlock(content, block);
1446
1513
  }
1447
- assertCleanBlock(existing, selector);
1448
- if (expectedHash && hashText(existing.body) !== expectedHash) {
1449
- throw new Error(`Managed instruction block drift detected for ${selector}`);
1514
+ if (!allowDrift) {
1515
+ assertCleanBlock(existing, selector);
1516
+ if (expectedHash && hashText(existing.body) !== expectedHash) {
1517
+ throw new Error(`Managed instruction block drift detected for ${selector}`);
1518
+ }
1450
1519
  }
1451
1520
  return `${content.slice(0, existing.start)}${block}${content.slice(existing.end)}`;
1452
1521
  }
1453
- function removeManagedBlockFromContent(content, selector, expectedHash) {
1522
+ function removeManagedBlockFromContent(content, selector, options) {
1523
+ const { expectedHash, allowDrift = false } = options;
1454
1524
  const existing = findManagedInstructionBlock(content, selector);
1455
1525
  if (!existing) return content;
1456
- assertCleanBlock(existing, selector);
1457
- if (expectedHash && hashText(existing.body) !== expectedHash) {
1458
- throw new Error(`Managed instruction block drift detected for ${selector}`);
1526
+ if (!allowDrift) {
1527
+ assertCleanBlock(existing, selector);
1528
+ if (expectedHash && hashText(existing.body) !== expectedHash) {
1529
+ throw new Error(`Managed instruction block drift detected for ${selector}`);
1530
+ }
1459
1531
  }
1460
1532
  return `${content.slice(0, existing.start)}${content.slice(existing.end)}`;
1461
1533
  }
@@ -1501,7 +1573,7 @@ function assertCleanBlock(block, selector) {
1501
1573
  }
1502
1574
  }
1503
1575
  function referencesAgentsMd(content, claudePath, agentsPath) {
1504
- const claudeDir = dirname8(claudePath);
1576
+ const claudeDir = dirname9(claudePath);
1505
1577
  for (const rawLine of content.split(/\r?\n/)) {
1506
1578
  const line = rawLine.trim();
1507
1579
  const atImport = /^@import\s+(.+)$/i.exec(line);
@@ -1511,7 +1583,7 @@ function referencesAgentsMd(content, claudePath, agentsPath) {
1511
1583
  const cleaned = referenced.trim().replace(/^["']|["']$/g, "");
1512
1584
  if (!/AGENTS\.md$/i.test(cleaned)) continue;
1513
1585
  const candidate = cleaned.startsWith("/") ? cleaned : join4(claudeDir, cleaned);
1514
- if (relative2(dirname8(agentsPath), candidate).replaceAll("\\", "/") === "AGENTS.md") return true;
1586
+ if (relative2(dirname9(agentsPath), candidate).replaceAll("\\", "/") === "AGENTS.md") return true;
1515
1587
  if (candidate === agentsPath) return true;
1516
1588
  }
1517
1589
  return false;
@@ -1533,7 +1605,7 @@ async function writeTextWithTransport(path, content, transport) {
1533
1605
  const tempRoot = await mkdtemp(join4(tmpdir2(), "agentwheel-instructions-"));
1534
1606
  const localPath = join4(tempRoot, basename2(path) || "instructions.md");
1535
1607
  try {
1536
- await writeFile7(localPath, content, "utf8");
1608
+ await writeFile8(localPath, content, "utf8");
1537
1609
  await transport.atomicCopy(localPath, path, "file");
1538
1610
  } finally {
1539
1611
  await rm3(tempRoot, { recursive: true, force: true });
@@ -1677,7 +1749,7 @@ async function uninstall(plan, options = {}) {
1677
1749
  const blockers = plan.operations.filter((operation) => operation.action === "conflict");
1678
1750
  throw new Error(`Refusing to uninstall with blocking changes: ${blockers.map((item) => item.relativeDestPath).join(", ")}`);
1679
1751
  }
1680
- const removable = plan.operations.filter((operation) => operation.action === "remove" || resolvedOptions.force && isForceRemovableKeep(operation)).map((operation) => operation.action === "keep" ? { ...operation, action: "remove", reason: `${operation.reason}; force removing drifted managed file` } : operation);
1752
+ const removable = plan.operations.filter((operation) => operation.action === "remove" || resolvedOptions.force && isForceRemovableKeep(operation)).map((operation) => operation.action === "keep" ? { ...operation, action: "remove", overrideDrift: true, reason: `${operation.reason}; force removing drifted managed file` } : operation);
1681
1753
  const kept = plan.operations.filter((operation) => operation.action === "keep" && (!resolvedOptions.force || !isForceRemovableKeep(operation)));
1682
1754
  const skipped = plan.operations.filter((operation) => operation.action === "skip");
1683
1755
  const removedDrifted = resolvedOptions.force ? plan.operations.filter((operation) => operation.action === "keep" && isForceRemovableKeep(operation)).length : 0;
@@ -1822,7 +1894,7 @@ async function applyOperation(operation, context) {
1822
1894
  }
1823
1895
  if (operation.mode === managedInstructionBlockMode) {
1824
1896
  const selector = managedInstructionSelector(operation.logicalSelector, operation.artifactType, operation.artifactName);
1825
- const hash2 = await writeManagedInstructionBlock(operation.sourcePath, operation.destPath, selector, transport, operation.manifestHash);
1897
+ const hash2 = await writeManagedInstructionBlock(operation.sourcePath, operation.destPath, selector, transport, managedBlockMutationOptions(operation));
1826
1898
  if (hash2 !== operation.desiredHash) {
1827
1899
  throw new Error(`Managed block hash verification failed for ${operation.relativeDestPath}: expected ${operation.desiredHash}, got ${hash2}`);
1828
1900
  }
@@ -1835,6 +1907,8 @@ async function applyOperation(operation, context) {
1835
1907
  }
1836
1908
  if (operation.mergeStrategy === "json-deep") {
1837
1909
  await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeJsonFile);
1910
+ } else if (operation.mergeStrategy === "openclaw-json-deep") {
1911
+ await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeOpenClawJsonFile);
1838
1912
  } else if (operation.mergeStrategy === "yaml-deep") {
1839
1913
  await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeYamlFile);
1840
1914
  } else if (operation.mergeStrategy === "codex-toml-mcp") {
@@ -1884,7 +1958,7 @@ async function applyOperation(operation, context) {
1884
1958
  }
1885
1959
  if (operation.mode === managedInstructionBlockMode) {
1886
1960
  const selector = managedInstructionSelector(operation.logicalSelector, operation.artifactType, operation.artifactName);
1887
- await removeManagedInstructionBlock(operation.destPath, selector, transport, operation.manifestHash);
1961
+ await removeManagedInstructionBlock(operation.destPath, selector, transport, managedBlockMutationOptions(operation));
1888
1962
  } else {
1889
1963
  await transport.rm(operation.destPath);
1890
1964
  }
@@ -2057,6 +2131,12 @@ function semanticInstallCommands(operation) {
2057
2131
  if (operation.semanticPlugin) return operation.semanticPlugin.installCommands;
2058
2132
  return operation.semanticCommand ? [operation.semanticCommand] : [];
2059
2133
  }
2134
+ function managedBlockMutationOptions(operation) {
2135
+ return {
2136
+ expectedHash: operation.overrideDrift ? void 0 : operation.manifestHash,
2137
+ allowDrift: operation.overrideDrift === true
2138
+ };
2139
+ }
2060
2140
  async function entryForCompletedOperation(operation, transport, now, graphLockDigest) {
2061
2141
  if (operation.action === "remove") return void 0;
2062
2142
  if (operation.action === "create" || operation.action === "update") {
@@ -2170,7 +2250,7 @@ async function mergeWithTransport(sourcePath, destPath, transport, merge) {
2170
2250
  const localDest = join5(tempRoot, basename3(destPath) || "merged");
2171
2251
  try {
2172
2252
  if (await transport.pathExists(destPath)) {
2173
- await writeFile8(localDest, await transport.readFile(destPath), "utf8");
2253
+ await writeFile9(localDest, await transport.readFile(destPath), "utf8");
2174
2254
  }
2175
2255
  await merge(sourcePath, localDest);
2176
2256
  await transport.atomicCopy(localDest, destPath, "file");
@@ -2183,8 +2263,8 @@ async function mergeWithTransport(sourcePath, destPath, transport, merge) {
2183
2263
  import { basename as basename7, join as join15, relative as relative3 } from "path";
2184
2264
 
2185
2265
  // src/staging/codex-subagents.ts
2186
- import { mkdir as mkdir7, readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
2187
- import { basename as basename4, dirname as dirname9, join as join6 } from "path";
2266
+ import { mkdir as mkdir8, readFile as readFile10, writeFile as writeFile10 } from "fs/promises";
2267
+ import { basename as basename4, dirname as dirname10, join as join6 } from "path";
2188
2268
  var requiredCodexAgentFields = ["name", "description", "developer_instructions"];
2189
2269
  async function renderCodexSubagents(artifacts, stageRoot, adapter) {
2190
2270
  if (adapter?.name !== "codex") return artifacts;
@@ -2210,7 +2290,7 @@ async function renderCodexSubagent(artifact, stageRoot) {
2210
2290
  const renderedPath = join6(stageRoot, ".agentwheel-rendered", "codex-subagents", `${agentName}.toml`);
2211
2291
  const lowerSourcePath = sourcePath.toLowerCase();
2212
2292
  if (artifact.kind === "file" && (artifact.name.toLowerCase().endsWith(".toml") || lowerSourcePath.endsWith(".toml"))) {
2213
- const content = await readFile9(sourcePath, "utf8");
2293
+ const content = await readFile10(sourcePath, "utf8");
2214
2294
  validateCodexAgentToml(content, sourcePath);
2215
2295
  return {
2216
2296
  ...artifact,
@@ -2227,10 +2307,10 @@ async function renderCodexSubagent(artifact, stageRoot) {
2227
2307
  if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md") && !lowerSourcePath.endsWith(".md")) {
2228
2308
  throw new Error(`Codex subagent ${artifact.relativePath} must be a .toml file, .md file, or directory containing AGENTS.md.`);
2229
2309
  }
2230
- const markdown = await readFile9(markdownPath, "utf8");
2310
+ const markdown = await readFile10(markdownPath, "utf8");
2231
2311
  const toml = markdownToCodexAgentToml(agentName, markdown);
2232
- await mkdir7(dirname9(renderedPath), { recursive: true });
2233
- await writeFile9(renderedPath, toml, "utf8");
2312
+ await mkdir8(dirname10(renderedPath), { recursive: true });
2313
+ await writeFile10(renderedPath, toml, "utf8");
2234
2314
  return {
2235
2315
  ...artifact,
2236
2316
  name: agentName,
@@ -2298,8 +2378,8 @@ function escapeRegExp(value) {
2298
2378
  }
2299
2379
 
2300
2380
  // src/staging/copilot-artifacts.ts
2301
- import { mkdir as mkdir8, readFile as readFile10, writeFile as writeFile10 } from "fs/promises";
2302
- import { basename as basename5, dirname as dirname10, join as join7 } from "path";
2381
+ import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile11 } from "fs/promises";
2382
+ import { basename as basename5, dirname as dirname11, join as join7 } from "path";
2303
2383
  async function renderCopilotArtifacts(artifacts, stageRoot, adapter) {
2304
2384
  if (adapter?.name !== "copilot") return artifacts;
2305
2385
  const names = /* @__PURE__ */ new Set();
@@ -2329,9 +2409,9 @@ async function renderCopilotSubagent(artifact, stageRoot) {
2329
2409
  if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md")) {
2330
2410
  throw new Error(`Copilot subagent ${artifact.relativePath} must be a .md file or directory containing AGENTS.md.`);
2331
2411
  }
2332
- const markdown = await readFile10(markdownPath, "utf8");
2333
- await mkdir8(dirname10(renderedPath), { recursive: true });
2334
- await writeFile10(renderedPath, ensureCopilotAgentDescription(agentName, markdown), "utf8");
2412
+ const markdown = await readFile11(markdownPath, "utf8");
2413
+ await mkdir9(dirname11(renderedPath), { recursive: true });
2414
+ await writeFile11(renderedPath, ensureCopilotAgentDescription(agentName, markdown), "utf8");
2335
2415
  return {
2336
2416
  ...artifact,
2337
2417
  name: `${agentName}.agent.md`,
@@ -2387,7 +2467,7 @@ function yamlString(value) {
2387
2467
  import { join as join9 } from "path";
2388
2468
 
2389
2469
  // src/targets/plugins/common.ts
2390
- import { readFile as readFile11 } from "fs/promises";
2470
+ import { readFile as readFile12 } from "fs/promises";
2391
2471
  import { join as join8 } from "path";
2392
2472
  import { parseDocument } from "yaml";
2393
2473
  function pluginStateRoot(request) {
@@ -2411,14 +2491,14 @@ function safeNameSegment(value) {
2411
2491
  async function jsonPluginName(root, relativeManifestPath, fallback) {
2412
2492
  const manifestPath = join8(root, relativeManifestPath);
2413
2493
  if (!await pathExists(manifestPath)) return fallback;
2414
- const parsed = JSON.parse(await readFile11(manifestPath, "utf8"));
2494
+ const parsed = JSON.parse(await readFile12(manifestPath, "utf8"));
2415
2495
  return typeof parsed.name === "string" && parsed.name.trim().length > 0 ? parsed.name.trim() : fallback;
2416
2496
  }
2417
2497
  async function yamlPluginName(root, relativeManifestPaths, fallback) {
2418
2498
  for (const relativeManifestPath of relativeManifestPaths) {
2419
2499
  const manifestPath = join8(root, relativeManifestPath);
2420
2500
  if (!await pathExists(manifestPath)) continue;
2421
- const document = parseDocument(await readFile11(manifestPath, "utf8"));
2501
+ const document = parseDocument(await readFile12(manifestPath, "utf8"));
2422
2502
  const parsed = document.toJSON();
2423
2503
  if (!isRecord4(parsed)) continue;
2424
2504
  for (const key of ["name", "module", "package"]) {
@@ -2550,7 +2630,7 @@ async function hermesPluginSpec(request) {
2550
2630
  }
2551
2631
 
2552
2632
  // src/targets/plugins/openclaw.ts
2553
- import { readFile as readFile12 } from "fs/promises";
2633
+ import { readFile as readFile13 } from "fs/promises";
2554
2634
  import { join as join13 } from "path";
2555
2635
  function openClawPluginInstallCommand(request) {
2556
2636
  return ["openclaw", "plugins", "install", "--force", request.path];
@@ -2559,6 +2639,15 @@ function openClawPluginUninstallCommand(pluginName) {
2559
2639
  return ["openclaw", "plugins", "uninstall", pluginName, "--force"];
2560
2640
  }
2561
2641
  async function openClawPluginSpec(request) {
2642
+ if (request.format === "openclaw-clawhub-plugin") {
2643
+ const metadata = await openClawClawHubPluginMetadata(request.path, request.fallbackPluginName);
2644
+ return {
2645
+ runtime: "openclaw",
2646
+ pluginName: metadata.pluginName,
2647
+ installCommands: [openClawPluginInstallCommand({ path: metadata.installSpec, dryRun: true })],
2648
+ uninstallCommands: [openClawPluginUninstallCommand(metadata.pluginName)]
2649
+ };
2650
+ }
2562
2651
  const pluginName = await openClawPluginName(request.path, request.fallbackPluginName);
2563
2652
  return {
2564
2653
  runtime: "openclaw",
@@ -2571,17 +2660,37 @@ async function openClawPluginName(root, fallback) {
2571
2660
  for (const manifestName of ["plugin.json", "openclaw.plugin.json"]) {
2572
2661
  const manifestPath = join13(root, manifestName);
2573
2662
  if (!await pathExists(manifestPath)) continue;
2574
- const parsed = JSON.parse(await readFile12(manifestPath, "utf8"));
2663
+ const parsed = JSON.parse(await readFile13(manifestPath, "utf8"));
2575
2664
  if (typeof parsed.name === "string" && parsed.name.trim().length > 0) return parsed.name.trim();
2576
2665
  }
2577
2666
  return fallback;
2578
2667
  }
2668
+ async function openClawClawHubPluginMetadata(root, fallback) {
2669
+ const metadataPath = join13(root, "clawhub.json");
2670
+ if (!await pathExists(metadataPath)) {
2671
+ throw new Error("OpenClaw ClawHub plugins must contain clawhub.json");
2672
+ }
2673
+ const parsed = JSON.parse(await readFile13(metadataPath, "utf8"));
2674
+ const installSpec = stringField(parsed.installSpec);
2675
+ if (!installSpec?.startsWith("clawhub:")) {
2676
+ throw new Error("OpenClaw ClawHub plugin metadata must declare installSpec starting with clawhub:");
2677
+ }
2678
+ const pluginName = stringField(parsed.runtimeId) ?? installNameFor(stringField(parsed.name) ?? fallback);
2679
+ return { installSpec, pluginName };
2680
+ }
2681
+ function stringField(value) {
2682
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
2683
+ }
2684
+ function installNameFor(value) {
2685
+ return value.split("/").at(-1).replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "clawhub-plugin";
2686
+ }
2579
2687
 
2580
2688
  // src/targets/plugins/index.ts
2581
2689
  async function semanticPluginSpecForArtifact(request) {
2582
2690
  if (request.semantic === "openclaw-plugin") {
2583
2691
  return openClawPluginSpec({
2584
2692
  path: request.sourcePath,
2693
+ format: request.artifact.format,
2585
2694
  fallbackPluginName: request.installName
2586
2695
  });
2587
2696
  }
@@ -2601,7 +2710,7 @@ async function semanticPluginSpecForArtifact(request) {
2601
2710
  }
2602
2711
 
2603
2712
  // src/validation/artifacts.ts
2604
- import { readFile as readFile13 } from "fs/promises";
2713
+ import { readFile as readFile14 } from "fs/promises";
2605
2714
  import { basename as basename6, join as join14 } from "path";
2606
2715
  import { parseDocument as parseDocument2 } from "yaml";
2607
2716
 
@@ -2657,7 +2766,7 @@ function subagentBaseName(name) {
2657
2766
 
2658
2767
  // src/validation/artifacts.ts
2659
2768
  var behavioralRuleFormats = ["markdown-rule", "claude-markdown-rule", "copilot-instruction-rule"];
2660
- var pluginFormats = ["claude-plugin", "codex-plugin", "hermes-plugin", "copilot-plugin", "openclaw-plugin"];
2769
+ var pluginFormats = ["claude-plugin", "codex-plugin", "hermes-plugin", "copilot-plugin", "openclaw-plugin", "openclaw-clawhub-plugin"];
2661
2770
  async function filterArtifactsByInstallFormat(artifacts, adapter, installationType, options = {}) {
2662
2771
  const selectedSet = new Set(normalizeArtifactSelectors(options.selected ?? []) ?? []);
2663
2772
  const kept = [];
@@ -2766,6 +2875,7 @@ async function inferArtifactFormat(artifact, target) {
2766
2875
  return void 0;
2767
2876
  }
2768
2877
  if (artifact.type === "plugins" && target.semantic === "openclaw-plugin") {
2878
+ if (artifact.kind === "dir" && await pathExists(join14(artifactPath(artifact), "clawhub.json"))) return "openclaw-clawhub-plugin";
2769
2879
  if (artifact.kind === "dir" && (await openClawPluginManifestPaths(artifact)).length > 0) return "openclaw-plugin";
2770
2880
  }
2771
2881
  return void 0;
@@ -2781,9 +2891,9 @@ function expectedFormats(artifact, target) {
2781
2891
  return behavioralRuleFormats;
2782
2892
  }
2783
2893
  if (artifact.type === "plugins") {
2784
- if (isPluginFormat(target.semantic)) return [target.semantic];
2785
2894
  const declared = target.formats?.filter((format) => pluginFormats.includes(format));
2786
2895
  if (target.formats?.length) return declared?.length ? declared : pluginFormats;
2896
+ if (isPluginFormat(target.semantic)) return [target.semantic];
2787
2897
  }
2788
2898
  return target.formats;
2789
2899
  }
@@ -2792,7 +2902,10 @@ async function validateKnownFormat(artifact, format, target) {
2792
2902
  if (format === "markdown-rule" || format === "claude-markdown-rule" || format === "copilot-instruction-rule") {
2793
2903
  return validateMarkdownRule(artifact, format);
2794
2904
  }
2795
- if (format === "openclaw-plugin" || target.semantic === "openclaw-plugin") {
2905
+ if (format === "openclaw-clawhub-plugin") {
2906
+ return validateOpenClawClawHubPlugin(artifact);
2907
+ }
2908
+ if (format === "openclaw-plugin") {
2796
2909
  return validateOpenClawPlugin(artifact);
2797
2910
  }
2798
2911
  if (format === "claude-plugin" || target.semantic === "claude-plugin") {
@@ -2847,7 +2960,7 @@ async function validateGenericStructure(artifact, target) {
2847
2960
  async function validateSkillFrontmatter(artifact, skillMdPath) {
2848
2961
  let content;
2849
2962
  try {
2850
- content = await readFile13(skillMdPath, "utf8");
2963
+ content = await readFile14(skillMdPath, "utf8");
2851
2964
  } catch (error) {
2852
2965
  return [{ artifact, message: `could not read SKILL.md: ${errorMessage(error)}` }];
2853
2966
  }
@@ -2917,7 +3030,7 @@ async function validateHermesPlugin(artifact) {
2917
3030
  return [{ artifact, message: "Hermes plugins must contain plugin.yaml or plugin.yml" }];
2918
3031
  }
2919
3032
  try {
2920
- const document = parseDocument2(await readFile13(manifestPath, "utf8"));
3033
+ const document = parseDocument2(await readFile14(manifestPath, "utf8"));
2921
3034
  if (document.errors.length > 0) {
2922
3035
  return [{ artifact, message: `Hermes ${basename6(manifestPath)} must be valid YAML: ${document.errors[0]?.message ?? "parse error"}` }];
2923
3036
  }
@@ -2941,7 +3054,7 @@ async function firstExistingPath(paths) {
2941
3054
  }
2942
3055
  async function parseJsonPluginManifest(manifestPath, label) {
2943
3056
  try {
2944
- const parsed = JSON.parse(await readFile13(manifestPath, "utf8"));
3057
+ const parsed = JSON.parse(await readFile14(manifestPath, "utf8"));
2945
3058
  if (!isRecord5(parsed)) {
2946
3059
  return { ok: false, message: `${label} ${basename6(manifestPath)} must be a JSON object` };
2947
3060
  }
@@ -2976,6 +3089,33 @@ async function validateOpenClawPlugin(artifact) {
2976
3089
  }
2977
3090
  return issues;
2978
3091
  }
3092
+ async function validateOpenClawClawHubPlugin(artifact) {
3093
+ if (artifact.type !== "plugins") {
3094
+ return [{ artifact, message: "openclaw-clawhub-plugin format is only valid for plugins artifacts" }];
3095
+ }
3096
+ if (artifact.kind !== "dir") {
3097
+ return [{ artifact, message: "OpenClaw ClawHub plugins must be directory artifacts" }];
3098
+ }
3099
+ const metadataPath = join14(artifactPath(artifact), "clawhub.json");
3100
+ if (!await pathExists(metadataPath)) {
3101
+ return [{ artifact, message: "OpenClaw ClawHub plugins must contain clawhub.json" }];
3102
+ }
3103
+ try {
3104
+ const parsed = JSON.parse(await readFile14(metadataPath, "utf8"));
3105
+ if (!isRecord5(parsed)) {
3106
+ return [{ artifact, message: "OpenClaw ClawHub clawhub.json must be a JSON object" }];
3107
+ }
3108
+ if (typeof parsed.installSpec !== "string" || !parsed.installSpec.trim().startsWith("clawhub:")) {
3109
+ return [{ artifact, message: "OpenClaw ClawHub clawhub.json must declare installSpec starting with clawhub:" }];
3110
+ }
3111
+ if (typeof parsed.name !== "string" || parsed.name.trim().length === 0) {
3112
+ return [{ artifact, message: "OpenClaw ClawHub clawhub.json must declare a non-empty name" }];
3113
+ }
3114
+ return [];
3115
+ } catch (error) {
3116
+ return [{ artifact, message: `OpenClaw ClawHub clawhub.json must be valid JSON: ${errorMessage(error)}` }];
3117
+ }
3118
+ }
2979
3119
  async function openClawPluginManifestPaths(artifact) {
2980
3120
  const root = artifactPath(artifact);
2981
3121
  const candidates = [join14(root, "plugin.json"), join14(root, "openclaw.plugin.json")];
@@ -2985,7 +3125,7 @@ async function openClawPluginManifestPaths(artifact) {
2985
3125
  async function parseOpenClawPluginManifest(manifestPath) {
2986
3126
  const manifestName = basename6(manifestPath);
2987
3127
  try {
2988
- const parsed = JSON.parse(await readFile13(manifestPath, "utf8"));
3128
+ const parsed = JSON.parse(await readFile14(manifestPath, "utf8"));
2989
3129
  if (!isRecord5(parsed)) {
2990
3130
  return { ok: false, path: manifestPath, message: `OpenClaw ${manifestName} must be a JSON object` };
2991
3131
  }
@@ -3002,7 +3142,7 @@ async function parseJsonObjectArtifact(artifact) {
3002
3142
  return { ok: false, message: "merge artifacts must be JSON files" };
3003
3143
  }
3004
3144
  try {
3005
- const parsed = JSON.parse(await readFile13(artifactPath(artifact), "utf8"));
3145
+ const parsed = JSON.parse(await readFile14(artifactPath(artifact), "utf8"));
3006
3146
  if (!isRecord5(parsed)) return { ok: false, message: "merge artifacts must contain a JSON object" };
3007
3147
  return { ok: true, value: parsed };
3008
3148
  } catch (error) {
@@ -3014,7 +3154,7 @@ async function parseYamlObjectArtifact(artifact) {
3014
3154
  return { ok: false, message: "merge artifacts must be YAML files" };
3015
3155
  }
3016
3156
  try {
3017
- const document = parseDocument2(await readFile13(artifactPath(artifact), "utf8"));
3157
+ const document = parseDocument2(await readFile14(artifactPath(artifact), "utf8"));
3018
3158
  if (document.errors.length > 0) {
3019
3159
  return { ok: false, message: `merge artifact must be valid YAML: ${document.errors[0]?.message ?? "parse error"}` };
3020
3160
  }
@@ -3139,7 +3279,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
3139
3279
  }
3140
3280
  const currentHash2 = await transport.hashPath(op.destPath);
3141
3281
  if (existing2 && existing2.sourceHash === op.desiredHash) {
3142
- operations.push({ ...op, action: "skip", currentHash: currentHash2, manifestHash: existing2.hash, reason: "merged source already up to date" });
3282
+ operations.push(options.forceDrift ? { ...op, action: "update", currentHash: currentHash2, manifestHash: existing2.hash, reason: "force refreshing managed merge destination" } : { ...op, action: "skip", currentHash: currentHash2, manifestHash: existing2.hash, reason: "merged source already up to date" });
3143
3283
  } else {
3144
3284
  operations.push({ ...op, action: "update", currentHash: currentHash2, manifestHash: existing2?.hash, reason: existing2 ? "merge source changed" : "merge into existing destination" });
3145
3285
  }
@@ -3186,6 +3326,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
3186
3326
  action: "update",
3187
3327
  currentHash,
3188
3328
  manifestHash: existing.hash,
3329
+ overrideDrift: true,
3189
3330
  reason: reasonWithComposedDiff("force replacing drifted managed destination", op.composedFrom, existing.composedFrom),
3190
3331
  composedFromDiff
3191
3332
  });
@@ -3245,7 +3386,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
3245
3386
  mode: entry.mode,
3246
3387
  composedFrom: entry.composedFrom,
3247
3388
  ...operationMetadataFromEntry(entry),
3248
- ...options.forceDrift ? { action: "remove", reason: "force removing drifted stale managed destination" } : {}
3389
+ ...options.forceDrift ? { action: "remove", reason: "force removing drifted stale managed destination", overrideDrift: true } : {}
3249
3390
  });
3250
3391
  } else {
3251
3392
  operations.push({
@@ -3422,6 +3563,7 @@ async function planManagedBlockOperation(op, manifestByPath, transport, options,
3422
3563
  action: "update",
3423
3564
  currentHash: state.hash,
3424
3565
  manifestHash: existing.hash,
3566
+ overrideDrift: true,
3425
3567
  reason: reasonWithComposedDiff("force replacing drifted managed instruction block", op.composedFrom, existing.composedFrom),
3426
3568
  composedFromDiff
3427
3569
  }];
@@ -3452,7 +3594,7 @@ async function currentEntryHash(entry, destPath, transport) {
3452
3594
  if (entry.mode !== managedInstructionBlockMode) return transport.hashPath(destPath);
3453
3595
  const selector = managedInstructionSelector("logicalSelector" in entry ? entry.logicalSelector : void 0, entry.artifactType, entry.artifactName);
3454
3596
  const state = await readManagedInstructionBlockState(destPath, selector, transport);
3455
- return state.drifted ? state.markerHash : state.hash;
3597
+ return state.hash;
3456
3598
  }
3457
3599
  async function canStrictlyAdoptLegacyEntry(entry, op, targetRoot, transport) {
3458
3600
  if (entry.artifactType !== op.artifactType || entry.artifactName !== op.artifactName) return false;
@@ -3924,7 +4066,7 @@ async function currentEntryHash2(entry, destPath, transport) {
3924
4066
  if (entry.mode !== managedInstructionBlockMode) return transport.hashPath(destPath);
3925
4067
  const selector = managedInstructionSelector("logicalSelector" in entry ? entry.logicalSelector : void 0, entry.artifactType, entry.artifactName);
3926
4068
  const state = await readManagedInstructionBlockState(destPath, selector, transport);
3927
- return state.drifted ? state.markerHash : state.hash;
4069
+ return state.hash;
3928
4070
  }
3929
4071
  function operationMetadataFromEntry2(entry, ownersOverride) {
3930
4072
  if ("owners" in entry) {
@@ -4148,15 +4290,17 @@ function ownerChains(lock, nodeId) {
4148
4290
  return incoming.flatMap((edge) => ownerChains(lock, edge.from).map((chain) => [...chain, `${edge.alias}:${nodeId}`]));
4149
4291
  }
4150
4292
 
4151
- // src/source/git.ts
4152
- import { execFile as execFile4 } from "child_process";
4153
- import { cp as cp2, mkdir as mkdir9, rename as rename3, rm as rm5, writeFile as writeFile11 } from "fs/promises";
4154
- import { homedir as homedir2 } from "os";
4155
- import { basename as basename9, dirname as dirname11, join as join19, resolve as resolve6 } from "path";
4156
- import { promisify as promisify4 } from "util";
4293
+ // src/source/clawhub.ts
4294
+ import { mkdir as mkdir10, rm as rm5, writeFile as writeFile12 } from "fs/promises";
4295
+ import { basename as basename9, dirname as dirname12, join as join19, resolve as resolve6 } from "path";
4296
+
4297
+ // src/source/local.ts
4298
+ import { createHash as createHash4 } from "crypto";
4299
+ import { readdir, stat as stat3 } from "fs/promises";
4300
+ import { basename as basename8, join as join18, relative as relative4, resolve as resolve5 } from "path";
4157
4301
 
4158
4302
  // src/model/package.ts
4159
- import { readFile as readFile14 } from "fs/promises";
4303
+ import { readFile as readFile15 } from "fs/promises";
4160
4304
  import { join as join17 } from "path";
4161
4305
  import { parse as parse3, printParseErrorCode as printParseErrorCode2 } from "jsonc-parser";
4162
4306
  import { z as z5 } from "zod";
@@ -4181,6 +4325,7 @@ var packageProvideBaseSchema = z5.object({
4181
4325
  var packageItemSchema = z5.object({
4182
4326
  format: artifactFormatSchema.optional(),
4183
4327
  requires: z5.array(packageItemRequireSchema).optional(),
4328
+ suggests: z5.array(packageItemSuggestSchema).optional(),
4184
4329
  compose: z5.array(packageComposeEntrySchema).optional(),
4185
4330
  runtimes: runtimeListSchema.optional()
4186
4331
  });
@@ -4194,6 +4339,10 @@ var packageDependencySchema = z5.object({
4194
4339
  integrity: z5.string().min(1).optional(),
4195
4340
  runtimes: runtimeListSchema.optional()
4196
4341
  });
4342
+ var packageSuggestionSchema = packageDependencySchema.extend({
4343
+ reason: z5.string().min(1).optional(),
4344
+ when: z5.string().min(1).optional()
4345
+ });
4197
4346
  var packageProvideV1Schema = packageProvideBaseSchema.extend({
4198
4347
  type: legacyArtifactTypeSchema
4199
4348
  });
@@ -4214,6 +4363,7 @@ var packageManifestV2Schema = z5.object({
4214
4363
  version: z5.string().min(1),
4215
4364
  runtimes: runtimeListSchema.optional(),
4216
4365
  requires: z5.record(z5.string().min(1), packageDependencySchema).optional(),
4366
+ suggests: z5.record(z5.string().min(1), packageSuggestionSchema).optional(),
4217
4367
  compose: z5.array(packageComposeEntrySchema).optional(),
4218
4368
  provides: z5.array(packageProvideSchema).default([])
4219
4369
  }).superRefine((manifest, ctx) => {
@@ -4247,7 +4397,7 @@ async function findPackageManifestPath(root, options = {}) {
4247
4397
  async function readPackageManifest(root) {
4248
4398
  const path = await findPackageManifestPath(root);
4249
4399
  if (!path) return void 0;
4250
- const content = await readFile14(path, "utf8");
4400
+ const content = await readFile15(path, "utf8");
4251
4401
  const errors = [];
4252
4402
  const parsed = parse3(content, errors, { allowTrailingComma: true, disallowComments: false });
4253
4403
  if (errors.length > 0) {
@@ -4301,9 +4451,6 @@ function isRecord6(value) {
4301
4451
  }
4302
4452
 
4303
4453
  // src/source/local.ts
4304
- import { createHash as createHash4 } from "crypto";
4305
- import { readdir, stat as stat3 } from "fs/promises";
4306
- import { basename as basename8, join as join18, relative as relative4, resolve as resolve5 } from "path";
4307
4454
  var LocalSourceDriver = class {
4308
4455
  name = "local";
4309
4456
  async resolve(source) {
@@ -4527,6 +4674,7 @@ async function artifactForFile(type, name, sourcePath, relativePath, packageName
4527
4674
  assets: provide?.assets,
4528
4675
  required: provide?.required,
4529
4676
  requires: item.requires,
4677
+ suggests: item.suggests,
4530
4678
  compose: item.compose,
4531
4679
  runtimes: item.runtimes ?? provideRuntimes(provide) ?? manifestRuntimes(manifest)
4532
4680
  };
@@ -4546,6 +4694,7 @@ async function artifactForDir(type, name, sourcePath, relativePath, packageName,
4546
4694
  assets: provide?.assets,
4547
4695
  required: provide?.required,
4548
4696
  requires: item.requires,
4697
+ suggests: item.suggests,
4549
4698
  compose: item.compose,
4550
4699
  runtimes: item.runtimes ?? provideRuntimes(provide) ?? manifestRuntimes(manifest)
4551
4700
  };
@@ -4554,7 +4703,7 @@ function itemMetadata(provide, itemName) {
4554
4703
  if (!provide || !("items" in provide) || !provide.items || !itemName) return {};
4555
4704
  const item = provide.items[itemName];
4556
4705
  if (!item) return {};
4557
- return { format: item.format, requires: item.requires, compose: item.compose, runtimes: item.runtimes };
4706
+ return { format: item.format, requires: item.requires, suggests: item.suggests, compose: item.compose, runtimes: item.runtimes };
4558
4707
  }
4559
4708
  function provideRuntimes(provide) {
4560
4709
  return provide && "runtimes" in provide ? provide.runtimes : void 0;
@@ -4563,7 +4712,154 @@ function manifestRuntimes(manifest) {
4563
4712
  return manifest && manifest.schemaVersion === 2 ? manifest.runtimes : void 0;
4564
4713
  }
4565
4714
 
4715
+ // src/source/clawhub.ts
4716
+ var clawHubBaseUrl = "https://clawhub.ai/api/v1";
4717
+ var sourcePrefix = "clawhub:";
4718
+ var transientStatuses = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
4719
+ var ClawHubSourceDriver = class {
4720
+ constructor(fetchImpl = fetch) {
4721
+ this.fetchImpl = fetchImpl;
4722
+ }
4723
+ fetchImpl;
4724
+ name = "clawhub";
4725
+ local = new LocalSourceDriver();
4726
+ async resolve(source, options = {}) {
4727
+ const packageName = parseClawHubSource(source);
4728
+ return {
4729
+ driver: this.name,
4730
+ source,
4731
+ resolvedPath: cachePathFor(packageName, options.cacheRoot),
4732
+ packageName: `clawhub/${packageName}`,
4733
+ mode: options.mode ?? "tracking",
4734
+ requestedRef: options.ref ?? "latest",
4735
+ frozenLock: options.frozenLock,
4736
+ cacheLockTimeoutMs: options.cacheLockTimeoutMs
4737
+ };
4738
+ }
4739
+ async fetch(resolved) {
4740
+ if (resolved.frozenLock) {
4741
+ if (!await pathExists(resolved.resolvedPath)) {
4742
+ throw new Error(`Frozen lock requires cached ClawHub source at ${resolved.resolvedPath}`);
4743
+ }
4744
+ return {
4745
+ ...resolved,
4746
+ sourceHash: await hashPath(resolved.resolvedPath)
4747
+ };
4748
+ }
4749
+ const requestedName = parseClawHubSource(resolved.source);
4750
+ const response = await fetchClawHubPackage(this.fetchImpl, requestedName);
4751
+ if (!response.ok) {
4752
+ throw new Error(`ClawHub lookup failed for ${requestedName}: HTTP ${response.status}`);
4753
+ }
4754
+ const payload = await response.json();
4755
+ const packageInfo = payload.package;
4756
+ if (!packageInfo?.name) {
4757
+ throw new Error(`ClawHub response missing package metadata for ${requestedName}`);
4758
+ }
4759
+ if (!isInstallableOpenClawPackage(packageInfo.family)) {
4760
+ throw new Error(`ClawHub package is not an OpenClaw plugin or hook package: ${packageInfo.name}`);
4761
+ }
4762
+ await writeGeneratedPackage(resolved.resolvedPath, packageInfo);
4763
+ return {
4764
+ ...resolved,
4765
+ packageName: `clawhub/${packageInfo.name}`,
4766
+ packageVersion: packageInfo.latestVersion ?? "latest",
4767
+ sourceHash: await hashPath(resolved.resolvedPath)
4768
+ };
4769
+ }
4770
+ async list(resolved) {
4771
+ return this.local.list({ ...resolved, driver: "local" });
4772
+ }
4773
+ async scan(resolved) {
4774
+ if (!await pathExists(join19(resolved.resolvedPath, "plugins"))) {
4775
+ return { ok: false, findings: [{ level: "error", message: "ClawHub source has no generated plugin artifact" }] };
4776
+ }
4777
+ return { ok: true, findings: [] };
4778
+ }
4779
+ async translate(resolved) {
4780
+ return resolved;
4781
+ }
4782
+ async export(resolved) {
4783
+ return resolved;
4784
+ }
4785
+ };
4786
+ async function fetchClawHubPackage(fetchImpl, packageName) {
4787
+ const url = `${clawHubBaseUrl}/packages/${encodeURIComponent(packageName)}`;
4788
+ for (let attempt = 0; attempt < 3; attempt++) {
4789
+ const response = await fetchImpl(url, { headers: { Accept: "application/json" } });
4790
+ if (!transientStatuses.has(response.status) || attempt === 2) return response;
4791
+ await delay((attempt + 1) * 250);
4792
+ }
4793
+ throw new Error(`ClawHub lookup failed for ${packageName}`);
4794
+ }
4795
+ async function delay(ms) {
4796
+ await new Promise((resolve21) => setTimeout(resolve21, ms));
4797
+ }
4798
+ function parseClawHubSource(source) {
4799
+ if (!source.startsWith(sourcePrefix)) {
4800
+ throw new Error(`Invalid ClawHub source: ${source}`);
4801
+ }
4802
+ const packageName = source.slice(sourcePrefix.length).trim();
4803
+ if (!packageName) throw new Error(`Invalid ClawHub source: ${source}`);
4804
+ return packageName;
4805
+ }
4806
+ function isInstallableOpenClawPackage(family) {
4807
+ if (!family) return true;
4808
+ return family.endsWith("-plugin") || family === "hook-pack";
4809
+ }
4810
+ async function writeGeneratedPackage(root, packageInfo) {
4811
+ const name = packageInfo.name?.trim();
4812
+ if (!name) throw new Error("ClawHub package metadata must include a name");
4813
+ const pluginId = installNameFor2(packageInfo.runtimeId ?? name);
4814
+ const plugin = {
4815
+ name,
4816
+ installSpec: `${sourcePrefix}${name}`,
4817
+ pluginId,
4818
+ displayName: packageInfo.displayName,
4819
+ runtimeId: packageInfo.runtimeId,
4820
+ latestVersion: packageInfo.latestVersion,
4821
+ family: packageInfo.family,
4822
+ summary: packageInfo.summary ?? packageInfo.description,
4823
+ artifact: packageInfo.artifact,
4824
+ verification: packageInfo.verification
4825
+ };
4826
+ const pluginPath = join19(root, "plugins", pluginId, "clawhub.json");
4827
+ await rm5(root, { recursive: true, force: true });
4828
+ await mkdir10(dirname12(pluginPath), { recursive: true });
4829
+ await writeFile12(join19(root, "openpack.json"), `${JSON.stringify({
4830
+ schemaVersion: 2,
4831
+ name: `clawhub/${name}`,
4832
+ version: packageInfo.latestVersion ?? "latest",
4833
+ runtimes: ["openclaw"],
4834
+ provides: [
4835
+ {
4836
+ type: "plugins",
4837
+ path: "plugins",
4838
+ format: "openclaw-clawhub-plugin",
4839
+ runtimes: ["openclaw"],
4840
+ required: true
4841
+ }
4842
+ ]
4843
+ }, null, 2)}
4844
+ `, "utf8");
4845
+ await writeFile12(pluginPath, `${JSON.stringify(plugin, null, 2)}
4846
+ `, "utf8");
4847
+ }
4848
+ function installNameFor2(value) {
4849
+ return basename9(value).replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "clawhub-plugin";
4850
+ }
4851
+ function cachePathFor(packageName, cacheRoot) {
4852
+ const root = cacheRoot ? resolve6(cacheRoot) : join19(process.env.HOME ?? ".", ".agentwheel", "cache");
4853
+ const slug2 = `clawhub-${packageName}`.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
4854
+ return join19(root, slug2 || "clawhub-package");
4855
+ }
4856
+
4566
4857
  // src/source/git.ts
4858
+ import { execFile as execFile4 } from "child_process";
4859
+ import { cp as cp2, mkdir as mkdir11, rename as rename3, rm as rm6, writeFile as writeFile13 } from "fs/promises";
4860
+ import { homedir as homedir2 } from "os";
4861
+ import { basename as basename10, dirname as dirname13, join as join20, resolve as resolve7 } from "path";
4862
+ import { promisify as promisify4 } from "util";
4567
4863
  var execFileAsync4 = promisify4(execFile4);
4568
4864
  var GitSourceDriver = class {
4569
4865
  name = "git";
@@ -4575,7 +4871,7 @@ var GitSourceDriver = class {
4575
4871
  return {
4576
4872
  driver: this.name,
4577
4873
  source,
4578
- resolvedPath: cachePathFor(parsed.url, options.cacheRoot),
4874
+ resolvedPath: cachePathFor2(parsed.url, options.cacheRoot),
4579
4875
  mode,
4580
4876
  requestedRef,
4581
4877
  frozenLock: options.frozenLock,
@@ -4585,12 +4881,12 @@ var GitSourceDriver = class {
4585
4881
  async fetch(resolved) {
4586
4882
  return withFilesystemLock(`${resolved.resolvedPath}.lock`, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
4587
4883
  const parsed = parseGitSource(resolved.source);
4588
- await mkdir9(resolve6(resolved.resolvedPath, ".."), { recursive: true });
4589
- if (!await pathExists(join19(resolved.resolvedPath, ".git"))) {
4884
+ await mkdir11(resolve7(resolved.resolvedPath, ".."), { recursive: true });
4885
+ if (!await pathExists(join20(resolved.resolvedPath, ".git"))) {
4590
4886
  if (resolved.frozenLock) {
4591
4887
  throw new Error(`Frozen lock requires cached git checkout at ${resolved.resolvedPath}`);
4592
4888
  }
4593
- await rm5(resolved.resolvedPath, { recursive: true, force: true });
4889
+ await rm6(resolved.resolvedPath, { recursive: true, force: true });
4594
4890
  await git(["clone", "--no-tags", parsed.url, resolved.resolvedPath]);
4595
4891
  } else if (!resolved.frozenLock) {
4596
4892
  await git(["-C", resolved.resolvedPath, "fetch", "--prune", "origin"]);
@@ -4652,65 +4948,201 @@ function parseGitSource(source) {
4652
4948
  }
4653
4949
  throw new Error(`Invalid git source: ${source}`);
4654
4950
  }
4655
- function cachePathFor(url, cacheRoot) {
4656
- const root = cacheRoot ? resolve6(cacheRoot) : join19(homedir2(), ".agentwheel", "cache");
4951
+ function cachePathFor2(url, cacheRoot) {
4952
+ const root = cacheRoot ? resolve7(cacheRoot) : join20(homedir2(), ".agentwheel", "cache");
4657
4953
  const slug2 = url.replace(/^[a-z]+:\/\//i, "").replace(/\.git$/i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
4658
- return join19(root, slug2 || basename9(url));
4954
+ return join20(root, slug2 || basename10(url));
4659
4955
  }
4660
4956
  async function git(args) {
4661
4957
  return execFileAsync4("git", args, { maxBuffer: 1024 * 1024 * 10 });
4662
4958
  }
4663
4959
  async function snapshotCheckout(checkoutPath, commit) {
4664
- const snapshotPath = join19(dirname11(checkoutPath), `${basename9(checkoutPath)}-${commit.slice(0, 12)}`);
4960
+ const snapshotPath = join20(dirname13(checkoutPath), `${basename10(checkoutPath)}-${commit.slice(0, 12)}`);
4665
4961
  if (await pathExists(snapshotPath)) return snapshotPath;
4666
- const tempPath = join19(dirname11(checkoutPath), `${basename9(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
4667
- await rm5(tempPath, { recursive: true, force: true });
4962
+ const tempPath = join20(dirname13(checkoutPath), `${basename10(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
4963
+ await rm6(tempPath, { recursive: true, force: true });
4668
4964
  await cp2(checkoutPath, tempPath, { recursive: true, dereference: true });
4669
- await rm5(join19(tempPath, ".git"), { recursive: true, force: true });
4965
+ await rm6(join20(tempPath, ".git"), { recursive: true, force: true });
4670
4966
  try {
4671
4967
  await rename3(tempPath, snapshotPath);
4672
4968
  } catch (error) {
4673
4969
  if (!isAlreadyExists2(error)) throw error;
4674
- await rm5(tempPath, { recursive: true, force: true });
4970
+ await rm6(tempPath, { recursive: true, force: true });
4675
4971
  return snapshotPath;
4676
4972
  }
4677
4973
  return snapshotPath;
4678
4974
  }
4679
4975
  async function withFilesystemLock(lockPath, timeoutMs, fn) {
4680
- await mkdir9(dirname11(lockPath), { recursive: true });
4976
+ await mkdir11(dirname13(lockPath), { recursive: true });
4681
4977
  const started = Date.now();
4682
4978
  while (true) {
4683
4979
  try {
4684
- await mkdir9(lockPath);
4685
- await writeFile11(join19(lockPath, "owner.json"), JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
4980
+ await mkdir11(lockPath);
4981
+ await writeFile13(join20(lockPath, "owner.json"), JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
4686
4982
  break;
4687
4983
  } catch (error) {
4688
4984
  if (!isAlreadyExists2(error)) throw error;
4689
4985
  if (Date.now() - started > timeoutMs) {
4690
4986
  throw new Error(`Timed out waiting for git cache lock at ${lockPath}`);
4691
4987
  }
4692
- await new Promise((resolve19) => setTimeout(resolve19, 50));
4988
+ await new Promise((resolve21) => setTimeout(resolve21, 50));
4693
4989
  }
4694
4990
  }
4695
4991
  try {
4696
4992
  return await fn();
4697
4993
  } finally {
4698
- await rm5(lockPath, { recursive: true, force: true });
4994
+ await rm6(lockPath, { recursive: true, force: true });
4699
4995
  }
4700
4996
  }
4701
4997
  function isAlreadyExists2(error) {
4702
4998
  return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
4703
4999
  }
4704
5000
 
5001
+ // src/source/mcp-registry.ts
5002
+ import { mkdir as mkdir12, writeFile as writeFile14 } from "fs/promises";
5003
+ import { basename as basename11, dirname as dirname14, join as join21, resolve as resolve8 } from "path";
5004
+ var registryBaseUrl = "https://registry.modelcontextprotocol.io/v0.1";
5005
+ var sourcePrefix2 = "mcp-registry:";
5006
+ var McpRegistrySourceDriver = class {
5007
+ constructor(fetchImpl = fetch) {
5008
+ this.fetchImpl = fetchImpl;
5009
+ }
5010
+ fetchImpl;
5011
+ name = "mcp-registry";
5012
+ local = new LocalSourceDriver();
5013
+ async resolve(source, options = {}) {
5014
+ const serverName = parseMcpRegistrySource(source);
5015
+ return {
5016
+ driver: this.name,
5017
+ source,
5018
+ resolvedPath: cachePathFor3(serverName, options.cacheRoot),
5019
+ packageName: `mcp-registry/${serverName}`,
5020
+ mode: options.mode ?? "tracking",
5021
+ requestedRef: options.ref ?? "latest",
5022
+ frozenLock: options.frozenLock
5023
+ };
5024
+ }
5025
+ async fetch(resolved) {
5026
+ if (resolved.frozenLock) {
5027
+ if (!await pathExists(resolved.resolvedPath)) {
5028
+ throw new Error(`Frozen lock requires cached MCP registry source at ${resolved.resolvedPath}`);
5029
+ }
5030
+ return {
5031
+ ...resolved,
5032
+ sourceHash: await hashPath(resolved.resolvedPath)
5033
+ };
5034
+ }
5035
+ const serverName = parseMcpRegistrySource(resolved.source);
5036
+ const response = await this.fetchImpl(`${registryBaseUrl}/servers/${encodeURIComponent(serverName)}/versions/latest`, {
5037
+ headers: { Accept: "application/json" }
5038
+ });
5039
+ if (!response.ok) {
5040
+ throw new Error(`MCP registry lookup failed for ${serverName}: HTTP ${response.status}`);
5041
+ }
5042
+ const payload = await response.json();
5043
+ const server = payload.server;
5044
+ if (!server?.name) {
5045
+ throw new Error(`MCP registry response missing server metadata for ${serverName}`);
5046
+ }
5047
+ const remote = supportedRemote(server.remotes ?? []);
5048
+ if (!remote) {
5049
+ throw new Error(`MCP registry server is discovery-only for Agentwheel: ${serverName}`);
5050
+ }
5051
+ await writeGeneratedPackage2(resolved.resolvedPath, {
5052
+ serverName: server.name,
5053
+ title: server.title,
5054
+ description: server.description,
5055
+ version: server.version,
5056
+ url: remote.url
5057
+ });
5058
+ return {
5059
+ ...resolved,
5060
+ packageName: `mcp-registry/${server.name}`,
5061
+ packageVersion: server.version,
5062
+ sourceHash: await hashPath(resolved.resolvedPath)
5063
+ };
5064
+ }
5065
+ async list(resolved) {
5066
+ return this.local.list({ ...resolved, driver: "local" });
5067
+ }
5068
+ async scan(resolved) {
5069
+ if (!await pathExists(join21(resolved.resolvedPath, "mcp"))) {
5070
+ return { ok: false, findings: [{ level: "error", message: "MCP registry source has no generated mcp artifact" }] };
5071
+ }
5072
+ return { ok: true, findings: [] };
5073
+ }
5074
+ async translate(resolved) {
5075
+ return resolved;
5076
+ }
5077
+ async export(resolved) {
5078
+ return resolved;
5079
+ }
5080
+ };
5081
+ function parseMcpRegistrySource(source) {
5082
+ if (!source.startsWith(sourcePrefix2)) {
5083
+ throw new Error(`Invalid MCP registry source: ${source}`);
5084
+ }
5085
+ const serverName = source.slice(sourcePrefix2.length).trim();
5086
+ if (!serverName) throw new Error(`Invalid MCP registry source: ${source}`);
5087
+ return serverName;
5088
+ }
5089
+ function supportedRemote(remotes) {
5090
+ for (const remote of remotes ?? []) {
5091
+ if (remote?.type !== "streamable-http") continue;
5092
+ if (!isSafeHttpUrl(remote.url)) continue;
5093
+ if ((remote.headers ?? []).some((header) => header.isRequired && header.isSecret)) continue;
5094
+ return { url: remote.url };
5095
+ }
5096
+ return void 0;
5097
+ }
5098
+ function isSafeHttpUrl(value) {
5099
+ if (typeof value !== "string") return false;
5100
+ try {
5101
+ const url = new URL(value);
5102
+ return url.protocol === "https:" || url.protocol === "http:";
5103
+ } catch {
5104
+ return false;
5105
+ }
5106
+ }
5107
+ async function writeGeneratedPackage2(root, server) {
5108
+ const serverId = installNameFor3(server.serverName);
5109
+ const mcpPath = join21(root, "mcp", `${serverId}.json`);
5110
+ await mkdir12(dirname14(mcpPath), { recursive: true });
5111
+ await writeFile14(join21(root, "openpack.json"), `${JSON.stringify({
5112
+ schemaVersion: 2,
5113
+ name: `mcp-registry/${server.serverName}`,
5114
+ version: server.version ?? "latest",
5115
+ provides: [{ type: "mcp", path: "mcp" }]
5116
+ }, null, 2)}
5117
+ `, "utf8");
5118
+ await writeFile14(mcpPath, `${JSON.stringify({
5119
+ mcpServers: {
5120
+ [serverId]: {
5121
+ type: "streamable-http",
5122
+ url: server.url
5123
+ }
5124
+ }
5125
+ }, null, 2)}
5126
+ `, "utf8");
5127
+ }
5128
+ function installNameFor3(serverName) {
5129
+ return basename11(serverName).replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "mcp-server";
5130
+ }
5131
+ function cachePathFor3(serverName, cacheRoot) {
5132
+ const root = cacheRoot ? resolve8(cacheRoot) : join21(process.env.HOME ?? ".", ".agentwheel", "cache");
5133
+ const slug2 = `mcp-registry-${serverName}`.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
5134
+ return join21(root, slug2 || "mcp-registry-server");
5135
+ }
5136
+
4705
5137
  // src/source/skillkit.ts
4706
- import { cp as cp3, mkdir as mkdir10, readFile as readFile15, rm as rm6 } from "fs/promises";
5138
+ import { cp as cp3, mkdir as mkdir13, readFile as readFile16, rm as rm7 } from "fs/promises";
4707
5139
  import { homedir as homedir3 } from "os";
4708
- import { basename as basename11, dirname as dirname13, join as join21, resolve as resolve7 } from "path";
5140
+ import { basename as basename13, dirname as dirname16, join as join23, resolve as resolve9 } from "path";
4709
5141
  import * as defaultSkillKit from "@skillkit/core";
4710
5142
 
4711
5143
  // src/source/skill-artifacts.ts
4712
5144
  import { readdir as readdir2, stat as stat4 } from "fs/promises";
4713
- import { basename as basename10, dirname as dirname12, extname as extname2, join as join20 } from "path";
5145
+ import { basename as basename12, dirname as dirname15, extname as extname2, join as join22 } from "path";
4714
5146
  async function artifactsFromSkillPaths(paths, packageName) {
4715
5147
  const artifacts = [];
4716
5148
  const seen = /* @__PURE__ */ new Set();
@@ -4732,28 +5164,28 @@ async function discoverSkillPaths(root) {
4732
5164
  async function artifactFromSkillPath(item, packageName) {
4733
5165
  const stats = await stat4(item.path);
4734
5166
  if (stats.isDirectory()) {
4735
- const skillMd = join20(item.path, "SKILL.md");
5167
+ const skillMd = join22(item.path, "SKILL.md");
4736
5168
  if (!await pathExists(skillMd)) return void 0;
4737
- const name = sanitizeSkillName(item.name ?? basename10(item.path));
5169
+ const name = sanitizeSkillName(item.name ?? basename12(item.path));
4738
5170
  return {
4739
5171
  type: "skills",
4740
5172
  name,
4741
5173
  sourcePath: item.path,
4742
- relativePath: join20("skills", name),
5174
+ relativePath: join22("skills", name),
4743
5175
  kind: "dir",
4744
5176
  hash: await hashPath(item.path),
4745
5177
  packageName,
4746
5178
  channel: "managed"
4747
5179
  };
4748
5180
  }
4749
- if (stats.isFile() && basename10(item.path).toLowerCase() === "skill.md") {
4750
- const dir = dirname12(item.path);
4751
- const name = sanitizeSkillName(item.name ?? basename10(dir));
5181
+ if (stats.isFile() && basename12(item.path).toLowerCase() === "skill.md") {
5182
+ const dir = dirname15(item.path);
5183
+ const name = sanitizeSkillName(item.name ?? basename12(dir));
4752
5184
  return {
4753
5185
  type: "skills",
4754
5186
  name,
4755
5187
  sourcePath: dir,
4756
- relativePath: join20("skills", name),
5188
+ relativePath: join22("skills", name),
4757
5189
  kind: "dir",
4758
5190
  hash: await hashPath(dir),
4759
5191
  packageName,
@@ -4761,12 +5193,12 @@ async function artifactFromSkillPath(item, packageName) {
4761
5193
  };
4762
5194
  }
4763
5195
  if (stats.isFile() && extname2(item.path).toLowerCase() === ".md") {
4764
- const name = sanitizeSkillName(item.name ?? basename10(item.path, ".md"));
5196
+ const name = sanitizeSkillName(item.name ?? basename12(item.path, ".md"));
4765
5197
  return {
4766
5198
  type: "skills",
4767
5199
  name,
4768
5200
  sourcePath: item.path,
4769
- relativePath: join20("skills", `${name}.md`),
5201
+ relativePath: join22("skills", `${name}.md`),
4770
5202
  kind: "file",
4771
5203
  hash: await hashPath(item.path),
4772
5204
  packageName,
@@ -4784,7 +5216,7 @@ async function walk(dir, paths) {
4784
5216
  }
4785
5217
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
4786
5218
  if (!entry.isDirectory() || entry.name === ".git" || entry.name === "node_modules") continue;
4787
- await walk(join20(dir, entry.name), paths);
5219
+ await walk(join22(dir, entry.name), paths);
4788
5220
  }
4789
5221
  }
4790
5222
  function sanitizeSkillName(name) {
@@ -4801,12 +5233,12 @@ var SkillKitSourceDriver = class {
4801
5233
  async resolve(source, options = {}) {
4802
5234
  const spec = parseSkillKitSource(source);
4803
5235
  if (await pathExists(spec)) {
4804
- const resolvedPath = resolve7(spec);
5236
+ const resolvedPath = resolve9(spec);
4805
5237
  return {
4806
5238
  driver: this.name,
4807
5239
  source,
4808
5240
  resolvedPath,
4809
- packageName: `skillkit/${basename11(resolvedPath)}`,
5241
+ packageName: `skillkit/${basename13(resolvedPath)}`,
4810
5242
  mode: options.mode ?? "pinned",
4811
5243
  sourceHash: await hashPath(resolvedPath)
4812
5244
  };
@@ -4814,7 +5246,7 @@ var SkillKitSourceDriver = class {
4814
5246
  return {
4815
5247
  driver: this.name,
4816
5248
  source,
4817
- resolvedPath: cachePathFor2(spec, options.cacheRoot),
5249
+ resolvedPath: cachePathFor4(spec, options.cacheRoot),
4818
5250
  packageName: `skillkit/${packageSlug(spec)}`,
4819
5251
  mode: options.mode ?? "tracking",
4820
5252
  requestedRef: options.ref,
@@ -4840,17 +5272,17 @@ var SkillKitSourceDriver = class {
4840
5272
  if (!provider?.clone) {
4841
5273
  throw new Error("SkillKit provider API unavailable or cannot resolve source. Expected @skillkit/core detectProvider().clone().");
4842
5274
  }
4843
- await mkdir10(dirname13(resolved.resolvedPath), { recursive: true });
5275
+ await mkdir13(dirname16(resolved.resolvedPath), { recursive: true });
4844
5276
  const result = await provider.clone(providerSpec, resolved.resolvedPath, {});
4845
5277
  if (!result.success || !result.path) {
4846
5278
  throw new Error(`SkillKit provider failed to fetch ${spec}: ${result.error ?? "unknown error"}`);
4847
5279
  }
4848
- if (resolve7(result.path) !== resolve7(resolved.resolvedPath)) {
4849
- await rm6(resolved.resolvedPath, { recursive: true, force: true });
5280
+ if (resolve9(result.path) !== resolve9(resolved.resolvedPath)) {
5281
+ await rm7(resolved.resolvedPath, { recursive: true, force: true });
4850
5282
  await cp3(result.path, resolved.resolvedPath, { recursive: true, dereference: true });
4851
5283
  }
4852
5284
  if (result.tempRoot) {
4853
- await rm6(result.tempRoot, { recursive: true, force: true });
5285
+ await rm7(result.tempRoot, { recursive: true, force: true });
4854
5286
  }
4855
5287
  return {
4856
5288
  ...resolved,
@@ -4884,9 +5316,9 @@ var SkillKitSourceDriver = class {
4884
5316
  throw new Error("SkillKit translateSkill API unavailable");
4885
5317
  }
4886
5318
  for (const skill of this.discover(resolved.resolvedPath)) {
4887
- const skillMd = join21(skill.path, "SKILL.md");
5319
+ const skillMd = join23(skill.path, "SKILL.md");
4888
5320
  if (await pathExists(skillMd)) {
4889
- this.core.translateSkill(await readFile15(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
5321
+ this.core.translateSkill(await readFile16(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
4890
5322
  }
4891
5323
  }
4892
5324
  return resolved;
@@ -4914,9 +5346,9 @@ function normalizeProviderSource(spec) {
4914
5346
  if (spec.startsWith("git:https://github.com/")) return spec.slice("git:".length);
4915
5347
  return spec;
4916
5348
  }
4917
- function cachePathFor2(spec, cacheRoot) {
4918
- const root = cacheRoot ? resolve7(cacheRoot) : join21(homedir3(), ".agentwheel", "cache");
4919
- return join21(root, "skillkit", packageSlug(spec));
5349
+ function cachePathFor4(spec, cacheRoot) {
5350
+ const root = cacheRoot ? resolve9(cacheRoot) : join23(homedir3(), ".agentwheel", "cache");
5351
+ return join23(root, "skillkit", packageSlug(spec));
4920
5352
  }
4921
5353
  function packageSlug(spec) {
4922
5354
  return spec.replace(/^[a-z]+:\/\//i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "source";
@@ -4929,14 +5361,14 @@ function mapSeverity(severity) {
4929
5361
 
4930
5362
  // src/source/vercel-skills.ts
4931
5363
  import { stat as stat5 } from "fs/promises";
4932
- import { basename as basename12, join as join22, resolve as resolve8 } from "path";
5364
+ import { basename as basename14, join as join24, relative as relative5, resolve as resolve10 } from "path";
4933
5365
  var VercelSkillsSourceDriver = class {
4934
5366
  name = "vercel-skills";
4935
5367
  git = new GitSourceDriver();
4936
5368
  async resolve(source, options = {}) {
4937
5369
  const parsed = parseVercelSource(source);
4938
5370
  if (parsed.kind === "local") {
4939
- const resolvedPath = resolve8(parsed.path);
5371
+ const resolvedPath = resolve10(parsed.path);
4940
5372
  if (!await pathExists(resolvedPath) || !(await stat5(resolvedPath)).isDirectory()) {
4941
5373
  throw new Error(`Vercel skills local source not found: ${resolvedPath}`);
4942
5374
  }
@@ -4944,7 +5376,7 @@ var VercelSkillsSourceDriver = class {
4944
5376
  driver: this.name,
4945
5377
  source,
4946
5378
  resolvedPath,
4947
- packageName: `vercel/${basename12(resolvedPath)}`,
5379
+ packageName: `vercel/${basename14(resolvedPath)}`,
4948
5380
  mode: options.mode ?? "pinned",
4949
5381
  sourceHash: await hashPath(resolvedPath)
4950
5382
  };
@@ -4965,10 +5397,7 @@ var VercelSkillsSourceDriver = class {
4965
5397
  driver: "git",
4966
5398
  source: parsed.gitSource
4967
5399
  });
4968
- const resolvedPath = parsed.subpath ? join22(fetched.resolvedPath, parsed.subpath) : fetched.resolvedPath;
4969
- if (!await pathExists(resolvedPath)) {
4970
- throw new Error(`Vercel skills subpath not found: ${parsed.subpath}`);
4971
- }
5400
+ const resolvedPath = await resolveVercelSkillSubpath(fetched.resolvedPath, parsed.subpath);
4972
5401
  return {
4973
5402
  ...resolved,
4974
5403
  resolvedPath,
@@ -4993,6 +5422,15 @@ var VercelSkillsSourceDriver = class {
4993
5422
  return resolved;
4994
5423
  }
4995
5424
  };
5425
+ async function resolveVercelSkillSubpath(root, subpath) {
5426
+ if (!subpath) return root;
5427
+ const candidates = [join24(root, subpath), join24(root, "skills", subpath)];
5428
+ for (const candidate of candidates) {
5429
+ if (await pathExists(candidate)) return candidate;
5430
+ }
5431
+ const tried = candidates.map((candidate) => relative5(root, candidate)).join(", ");
5432
+ throw new Error(`Vercel skills subpath not found: ${subpath} (tried ${tried})`);
5433
+ }
4996
5434
  function parseVercelSource(source) {
4997
5435
  if (!source.startsWith("vercel:")) {
4998
5436
  throw new Error(`Invalid Vercel skills source: ${source}`);
@@ -5043,7 +5481,9 @@ var drivers = [
5043
5481
  new LocalSourceDriver(),
5044
5482
  new GitSourceDriver(),
5045
5483
  new SkillKitSourceDriver(),
5046
- new VercelSkillsSourceDriver()
5484
+ new VercelSkillsSourceDriver(),
5485
+ new McpRegistrySourceDriver(),
5486
+ new ClawHubSourceDriver()
5047
5487
  ];
5048
5488
  function getSourceDriver(name = "local") {
5049
5489
  const driver = drivers.find((candidate) => candidate.name === name);
@@ -5054,14 +5494,14 @@ function getSourceDriver(name = "local") {
5054
5494
  }
5055
5495
 
5056
5496
  // src/staging/staging.ts
5057
- import { chmod, cp as cp5, mkdir as mkdir12, mkdtemp as mkdtemp3, readdir as readdir5, stat as stat8 } from "fs/promises";
5058
- import { basename as basename15, dirname as dirname16, join as join25, relative as relative6, resolve as resolve10, sep as sep2 } from "path";
5497
+ import { chmod, cp as cp5, mkdir as mkdir15, mkdtemp as mkdtemp3, readdir as readdir5, stat as stat8 } from "fs/promises";
5498
+ import { basename as basename17, dirname as dirname19, join as join27, relative as relative7, resolve as resolve12, sep as sep2 } from "path";
5059
5499
  import { tmpdir as tmpdir4 } from "os";
5060
5500
 
5061
5501
  // src/compose/markdown.ts
5062
5502
  import { createHash as createHash5 } from "crypto";
5063
- import { readdir as readdir3, readFile as readFile16, stat as stat6, writeFile as writeFile12 } from "fs/promises";
5064
- import { basename as basename13, dirname as dirname14, extname as extname3, join as join23, relative as relative5, resolve as resolve9, sep } from "path";
5503
+ import { readdir as readdir3, readFile as readFile17, stat as stat6, writeFile as writeFile15 } from "fs/promises";
5504
+ import { basename as basename15, dirname as dirname17, extname as extname3, join as join25, relative as relative6, resolve as resolve11, sep } from "path";
5065
5505
  var includePattern = /<!--\s*openpack:include(\?)?\s+([^>]+?)\s*-->/g;
5066
5506
  var escapedIncludePattern = /<!--\s*openpack\\:include(\?)?\s+([^>]+?)\s*-->/g;
5067
5507
  var generatedPattern = /<!--\s*(?:BEGIN|END)\s+openpack:include\b/;
@@ -5076,7 +5516,7 @@ async function expandMarkdownIncludes(artifacts, packageRoot, options = {}) {
5076
5516
  const composedFrom = [];
5077
5517
  for (const file of files) {
5078
5518
  const result = await expandFile(file, packageRoot, composeEntriesForFile(artifact, file), artifactPaths, options);
5079
- if (result.changed) await writeFile12(file, result.content, "utf8");
5519
+ if (result.changed) await writeFile15(file, result.content, "utf8");
5080
5520
  composedFrom.push(...result.composedFrom);
5081
5521
  }
5082
5522
  const stagedPath = artifact.stagedPath ?? artifact.sourcePath;
@@ -5098,7 +5538,7 @@ async function validateMarkdownIncludes(artifacts, packageRoot, options = {}) {
5098
5538
  }
5099
5539
  }
5100
5540
  async function expandFile(file, packageRoot, appendEntries, artifactPaths, options) {
5101
- const raw = await readFile16(file, "utf8");
5541
+ const raw = await readFile17(file, "utf8");
5102
5542
  const owner = ownerSelector(packageRoot, file, options.nodeId);
5103
5543
  const expanded = await expandContent(raw, packageRoot, [owner], artifactPaths, options);
5104
5544
  let content = expanded.content;
@@ -5191,7 +5631,7 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
5191
5631
  if (!stats.isFile()) {
5192
5632
  throw new Error(`OpenPack include is not a file: ${displaySelector}`);
5193
5633
  }
5194
- const raw = sourceContent ?? await readFile16(sourcePath, "utf8");
5634
+ const raw = sourceContent ?? await readFile17(sourcePath, "utf8");
5195
5635
  const { optional: _optional, markers: _markers, chain: _chain, ...childOptions } = options;
5196
5636
  const expanded = await expandContent(raw, includePackageRoot, [...options.chain, displaySelector], includeArtifactPaths, {
5197
5637
  ...childOptions,
@@ -5249,8 +5689,8 @@ function extractOpenPackIncludeSelectors(content) {
5249
5689
  return selectors;
5250
5690
  }
5251
5691
  function resolvePackageSelector(packageRoot, selector) {
5252
- const root = resolve9(packageRoot);
5253
- const resolved = resolve9(root, selector);
5692
+ const root = resolve11(packageRoot);
5693
+ const resolved = resolve11(root, selector);
5254
5694
  if (resolved !== root && !resolved.startsWith(`${root}${sep}`)) {
5255
5695
  throw new Error(`OpenPack include escapes package root: ${selector}`);
5256
5696
  }
@@ -5267,7 +5707,7 @@ async function listMarkdownFiles(root) {
5267
5707
  const out = [];
5268
5708
  async function walk2(dir) {
5269
5709
  for (const entry of (await readdir3(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
5270
- const full = join23(dir, entry.name);
5710
+ const full = join25(dir, entry.name);
5271
5711
  if (entry.isDirectory()) {
5272
5712
  await walk2(full);
5273
5713
  } else if (entry.isFile() && extname3(entry.name).toLowerCase() === ".md") {
@@ -5280,8 +5720,8 @@ async function listMarkdownFiles(root) {
5280
5720
  }
5281
5721
  function composeEntriesForFile(artifact, file) {
5282
5722
  if (!artifact.compose?.length) return [];
5283
- if (artifact.kind === "file") return [resolve9(artifact.stagedPath ?? artifact.sourcePath), resolve9(file)].every(Boolean) && resolve9(artifact.stagedPath ?? artifact.sourcePath) === resolve9(file) ? artifact.compose : [];
5284
- return basename13(file) === "SKILL.md" && dirname14(file) === resolve9(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
5723
+ if (artifact.kind === "file") return [resolve11(artifact.stagedPath ?? artifact.sourcePath), resolve11(file)].every(Boolean) && resolve11(artifact.stagedPath ?? artifact.sourcePath) === resolve11(file) ? artifact.compose : [];
5724
+ return basename15(file) === "SKILL.md" && dirname17(file) === resolve11(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
5285
5725
  }
5286
5726
  function orderedForExpansion(artifacts) {
5287
5727
  return [...artifacts].sort((a, b) => Number(a.type === "fragments") - Number(b.type === "fragments"));
@@ -5298,7 +5738,7 @@ function applyReplacements(content, replacements) {
5298
5738
  return out + content.slice(cursor);
5299
5739
  }
5300
5740
  function relativeSelector(root, file) {
5301
- return relative5(root, file).replaceAll("\\", "/");
5741
+ return relative6(root, file).replaceAll("\\", "/");
5302
5742
  }
5303
5743
  function ownerSelector(root, file, nodeId) {
5304
5744
  const selector = relativeSelector(root, file);
@@ -5327,8 +5767,8 @@ function artifactPathMap(artifacts) {
5327
5767
  }
5328
5768
 
5329
5769
  // src/staging/customize.ts
5330
- import { cp as cp4, mkdir as mkdir11, readdir as readdir4, readFile as readFile17, writeFile as writeFile13 } from "fs/promises";
5331
- import { dirname as dirname15, join as join24 } from "path";
5770
+ import { cp as cp4, mkdir as mkdir14, readdir as readdir4, readFile as readFile18, writeFile as writeFile16 } from "fs/promises";
5771
+ import { dirname as dirname18, join as join26 } from "path";
5332
5772
  async function applyCustomizations(artifacts, options) {
5333
5773
  let next = [...artifacts];
5334
5774
  next = await applyReplacements2(next, options, "override", installableArtifactTypes());
@@ -5344,16 +5784,16 @@ async function applyFragmentCustomizations(artifacts, options) {
5344
5784
  return next.sort((a, b) => `${a.type}:${a.name}:${a.channel}`.localeCompare(`${b.type}:${b.name}:${b.channel}`));
5345
5785
  }
5346
5786
  async function applyInstructionOverlay(artifacts, options) {
5347
- const overlayPath = join24(options.workspaceRoot, ".agentwheel", "overlays", options.adapter.name, "instructions.local.md");
5787
+ const overlayPath = join26(options.workspaceRoot, ".agentwheel", "overlays", options.adapter.name, "instructions.local.md");
5348
5788
  if (!await pathExists(overlayPath)) return artifacts;
5349
5789
  const index = artifacts.findIndex((artifact2) => artifact2.type === "instructions");
5350
5790
  if (index < 0) return artifacts;
5351
5791
  const artifact = artifacts[index];
5352
- const managed = await readFile17(artifact.stagedPath ?? artifact.sourcePath, "utf8");
5353
- const local = await readFile17(overlayPath, "utf8");
5354
- const composedPath = join24(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
5355
- await mkdir11(dirname15(composedPath), { recursive: true });
5356
- await writeFile13(
5792
+ const managed = await readFile18(artifact.stagedPath ?? artifact.sourcePath, "utf8");
5793
+ const local = await readFile18(overlayPath, "utf8");
5794
+ const composedPath = join26(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
5795
+ await mkdir14(dirname18(composedPath), { recursive: true });
5796
+ await writeFile16(
5357
5797
  composedPath,
5358
5798
  [
5359
5799
  "<!-- BEGIN agentwheel managed: upstream -->",
@@ -5379,19 +5819,19 @@ async function applyInstructionOverlay(artifacts, options) {
5379
5819
  return [...artifacts.slice(0, index), updated, ...artifacts.slice(index + 1)];
5380
5820
  }
5381
5821
  async function applyAdditions(artifacts, options) {
5382
- const additionsRoot = join24(options.workspaceRoot, ".agentwheel", "additions");
5383
- const rulesRoot = join24(additionsRoot, "rules");
5822
+ const additionsRoot = join26(options.workspaceRoot, ".agentwheel", "additions");
5823
+ const rulesRoot = join26(additionsRoot, "rules");
5384
5824
  if (!await pathExists(rulesRoot)) return artifacts;
5385
5825
  const additions = [];
5386
5826
  for (const entry of await sortedDirEntries2(rulesRoot)) {
5387
- const full = join24(rulesRoot, entry.name);
5827
+ const full = join26(rulesRoot, entry.name);
5388
5828
  if (!entry.isFile()) continue;
5389
5829
  additions.push({
5390
5830
  type: "rules",
5391
5831
  name: entry.name,
5392
5832
  sourcePath: full,
5393
5833
  stagedPath: full,
5394
- relativePath: join24("additions", "rules", entry.name),
5834
+ relativePath: join26("additions", "rules", entry.name),
5395
5835
  kind: "file",
5396
5836
  hash: await hashPath(full),
5397
5837
  packageName: options.packageName,
@@ -5415,17 +5855,17 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
5415
5855
  );
5416
5856
  }
5417
5857
  for (const type of artifactTypes) {
5418
- const typeRoot = join24(root, type);
5858
+ const typeRoot = join26(root, type);
5419
5859
  if (!await pathExists(typeRoot)) continue;
5420
5860
  for (const entry of await sortedDirEntries2(typeRoot)) {
5421
5861
  const artifactMapKey = `${type}:${entry.name}`;
5422
5862
  if (seen.has(artifactMapKey)) continue;
5423
5863
  seen.add(artifactMapKey);
5424
- const full = join24(typeRoot, entry.name);
5864
+ const full = join26(typeRoot, entry.name);
5425
5865
  const artifactKind = entry.isDirectory() ? "dir" : "file";
5426
5866
  const existing = byKey.get(artifactMapKey);
5427
- const stagedPath = join24(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
5428
- await mkdir11(dirname15(stagedPath), { recursive: true });
5867
+ const stagedPath = join26(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
5868
+ await mkdir14(dirname18(stagedPath), { recursive: true });
5429
5869
  await cp4(full, stagedPath, { recursive: artifactKind === "dir", dereference: true });
5430
5870
  byKey.set(artifactMapKey, {
5431
5871
  ...existing,
@@ -5433,7 +5873,7 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
5433
5873
  name: entry.name,
5434
5874
  sourcePath: full,
5435
5875
  stagedPath,
5436
- relativePath: existing?.relativePath ?? join24(type, entry.name),
5876
+ relativePath: existing?.relativePath ?? join26(type, entry.name),
5437
5877
  kind: artifactKind,
5438
5878
  hash: await hashPath(stagedPath),
5439
5879
  packageName,
@@ -5448,13 +5888,13 @@ function replacementRoots(options, channel) {
5448
5888
  const stateDir = channel === "override" ? "overrides" : "ejected";
5449
5889
  const roots = [];
5450
5890
  if (options.graphNodeId) {
5451
- roots.push({ root: join24(options.workspaceRoot, ".agentwheel", stateDir, ...options.graphNodeId.split("/")), kind: "node" });
5891
+ roots.push({ root: join26(options.workspaceRoot, ".agentwheel", stateDir, ...options.graphNodeId.split("/")), kind: "node" });
5452
5892
  }
5453
5893
  if (options.packageName && options.packageVersion) {
5454
- roots.push({ root: join24(options.workspaceRoot, ".agentwheel", stateDir, ...`${options.packageName}@${options.packageVersion}`.split("/")), kind: "version" });
5894
+ roots.push({ root: join26(options.workspaceRoot, ".agentwheel", stateDir, ...`${options.packageName}@${options.packageVersion}`.split("/")), kind: "version" });
5455
5895
  }
5456
5896
  if (options.packageName) {
5457
- roots.push({ root: join24(options.workspaceRoot, ".agentwheel", stateDir, ...options.packageName.split("/")), kind: "package" });
5897
+ roots.push({ root: join26(options.workspaceRoot, ".agentwheel", stateDir, ...options.packageName.split("/")), kind: "package" });
5458
5898
  }
5459
5899
  return roots;
5460
5900
  }
@@ -5481,15 +5921,15 @@ async function stageResolvedSourceRaw(driver, resolved) {
5481
5921
  return stageResolvedArtifactsRaw(resolved, artifacts);
5482
5922
  }
5483
5923
  async function stageResolvedArtifactsRaw(resolved, artifacts) {
5484
- const root = await mkdtemp3(join25(tmpdir4(), "agentwheel-stage-"));
5924
+ const root = await mkdtemp3(join27(tmpdir4(), "agentwheel-stage-"));
5485
5925
  const stagedArtifacts = [];
5486
5926
  for (const artifact of artifacts) {
5487
- const stagedPath = join25(root, artifact.relativePath);
5488
- await mkdir12(dirname16(stagedPath), { recursive: true });
5927
+ const stagedPath = join27(root, artifact.relativePath);
5928
+ await mkdir15(dirname19(stagedPath), { recursive: true });
5489
5929
  await cp5(artifact.sourcePath, stagedPath, {
5490
5930
  recursive: artifact.kind === "dir",
5491
5931
  dereference: true,
5492
- filter: (path) => !isIgnoredGeneratedEntry(basename15(path))
5932
+ filter: (path) => !isIgnoredGeneratedEntry(basename17(path))
5493
5933
  });
5494
5934
  await composeAssets(artifact, resolved.resolvedPath, stagedPath);
5495
5935
  stagedArtifacts.push({
@@ -5570,16 +6010,16 @@ async function composeAssets(artifact, packageRoot, stagedPath) {
5570
6010
  }
5571
6011
  for (const asset of artifact.assets) {
5572
6012
  const source = resolvePackagePath(packageRoot, asset.from);
5573
- const dest = join25(stagedPath, asset.into);
6013
+ const dest = join27(stagedPath, asset.into);
5574
6014
  await copyAsset(asset, source, dest);
5575
6015
  }
5576
6016
  }
5577
6017
  async function copyAsset(asset, source, dest) {
5578
6018
  const sourceStats = await stat8(source);
5579
6019
  if (sourceStats.isFile()) {
5580
- if (matchesAny(basename15(source), asset.include)) {
5581
- await mkdir12(dest, { recursive: true });
5582
- await copyAssetFile(source, join25(dest, basename15(source)), asset);
6020
+ if (matchesAny(basename17(source), asset.include)) {
6021
+ await mkdir15(dest, { recursive: true });
6022
+ await copyAssetFile(source, join27(dest, basename17(source)), asset);
5583
6023
  }
5584
6024
  return;
5585
6025
  }
@@ -5587,25 +6027,25 @@ async function copyAsset(asset, source, dest) {
5587
6027
  throw new Error(`Asset include source is not a file or directory: ${source}`);
5588
6028
  }
5589
6029
  if (!asset.include?.length) {
5590
- await mkdir12(dirname16(dest), { recursive: true });
6030
+ await mkdir15(dirname19(dest), { recursive: true });
5591
6031
  await cp5(source, dest, { recursive: true, dereference: true });
5592
6032
  if (asset.mode === "copy") await normalizeCopiedModes(dest);
5593
6033
  return;
5594
6034
  }
5595
6035
  for (const file of await listFiles(source)) {
5596
- const rel = relative6(source, file).replaceAll("\\", "/");
5597
- if (!matchesAny(rel, asset.include) && !matchesAny(basename15(file), asset.include)) continue;
5598
- await copyAssetFile(file, join25(dest, rel), asset);
6036
+ const rel = relative7(source, file).replaceAll("\\", "/");
6037
+ if (!matchesAny(rel, asset.include) && !matchesAny(basename17(file), asset.include)) continue;
6038
+ await copyAssetFile(file, join27(dest, rel), asset);
5599
6039
  }
5600
6040
  }
5601
6041
  async function copyAssetFile(source, dest, asset) {
5602
- await mkdir12(dirname16(dest), { recursive: true });
6042
+ await mkdir15(dirname19(dest), { recursive: true });
5603
6043
  await cp5(source, dest, { dereference: true });
5604
6044
  if (asset.mode === "copy") await chmod(dest, 420);
5605
6045
  }
5606
6046
  function resolvePackagePath(packageRoot, path) {
5607
- const resolved = resolve10(packageRoot, path);
5608
- const root = resolve10(packageRoot);
6047
+ const resolved = resolve12(packageRoot, path);
6048
+ const root = resolve12(packageRoot);
5609
6049
  if (resolved !== root && !resolved.startsWith(`${root}${sep2}`)) {
5610
6050
  throw new Error(`Asset include escapes package root: ${path}`);
5611
6051
  }
@@ -5615,7 +6055,7 @@ async function listFiles(root) {
5615
6055
  const out = [];
5616
6056
  async function walk2(dir) {
5617
6057
  for (const entry of (await readdir5(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
5618
- const full = join25(dir, entry.name);
6058
+ const full = join27(dir, entry.name);
5619
6059
  if (entry.isDirectory()) {
5620
6060
  await walk2(full);
5621
6061
  } else if (entry.isFile()) {
@@ -5634,7 +6074,7 @@ async function normalizeCopiedModes(path) {
5634
6074
  }
5635
6075
  if (!stats.isDirectory()) return;
5636
6076
  for (const entry of await readdir5(path, { withFileTypes: true })) {
5637
- await normalizeCopiedModes(join25(path, entry.name));
6077
+ await normalizeCopiedModes(join27(path, entry.name));
5638
6078
  }
5639
6079
  }
5640
6080
  function matchesAny(path, patterns) {
@@ -5647,14 +6087,14 @@ function matchesGlob(path, pattern) {
5647
6087
  }
5648
6088
 
5649
6089
  // src/model/workspace.ts
5650
- import { readFile as readFile18 } from "fs/promises";
6090
+ import { readFile as readFile19 } from "fs/promises";
5651
6091
  import { homedir as homedir4 } from "os";
5652
- import { dirname as dirname17, join as join26, resolve as resolve11 } from "path";
6092
+ import { dirname as dirname20, join as join28, resolve as resolve13 } from "path";
5653
6093
  import { z as z6 } from "zod";
5654
6094
  var workspacePackageSchema = z6.object({
5655
6095
  name: z6.string().min(1),
5656
6096
  source: z6.string().min(1),
5657
- driver: z6.enum(["local", "git", "skillkit", "vercel-skills"]).default("local"),
6097
+ driver: z6.enum(["local", "git", "skillkit", "vercel-skills", "mcp-registry", "clawhub"]).default("local"),
5658
6098
  adapter: z6.string().min(1).default("openclaw"),
5659
6099
  adapterConfig: z6.string().min(1).optional(),
5660
6100
  adapterModule: z6.string().min(1).optional(),
@@ -5664,6 +6104,8 @@ var workspacePackageSchema = z6.object({
5664
6104
  requestedRef: z6.string().min(1).optional(),
5665
6105
  select: z6.array(z6.string().min(1)).optional(),
5666
6106
  skills: z6.array(z6.string().min(1)).optional(),
6107
+ withSuggestions: z6.boolean().optional(),
6108
+ suggestions: z6.array(z6.string().min(1)).optional(),
5667
6109
  aliases: z6.record(z6.string(), z6.string().min(1)).optional(),
5668
6110
  overrides: z6.array(z6.string().min(1)).optional()
5669
6111
  });
@@ -5718,12 +6160,12 @@ var workspaceConfigSchema = z6.object({
5718
6160
  agents: z6.record(z6.string(), workspaceAgentSchema).default({})
5719
6161
  });
5720
6162
  function workspaceConfigPath(workspaceRoot) {
5721
- return join26(workspaceRoot, ".agentwheel", "config.json");
6163
+ return join28(workspaceRoot, ".agentwheel", "config.json");
5722
6164
  }
5723
6165
  async function readWorkspaceConfig(workspaceRoot) {
5724
6166
  const path = workspaceConfigPath(workspaceRoot);
5725
6167
  if (!await pathExists(path)) return emptyWorkspaceConfig();
5726
- return workspaceConfigSchema.parse(JSON.parse(await readFile18(path, "utf8")));
6168
+ return workspaceConfigSchema.parse(JSON.parse(await readFile19(path, "utf8")));
5727
6169
  }
5728
6170
  async function writeWorkspaceConfig(workspaceRoot, config) {
5729
6171
  await writeJsonAtomic(workspaceConfigPath(workspaceRoot), workspaceConfigSchema.parse(config));
@@ -5736,14 +6178,14 @@ function upsertPackage(config, entry) {
5736
6178
  return { schemaVersion: 1, packages, bootstrapSkills: parsed.bootstrapSkills, registry: parsed.registry ?? {}, trust: parsed.trust ?? {}, profiles: parsed.profiles ?? {}, agents: parsed.agents ?? {} };
5737
6179
  }
5738
6180
  function globalWorkspaceConfigPath(globalRoot = homedir4()) {
5739
- return join26(globalRoot, ".agentwheel", "config.json");
6181
+ return join28(globalRoot, ".agentwheel", "config.json");
5740
6182
  }
5741
6183
  async function findWorkspaceRoot(start = process.cwd()) {
5742
- let current = resolve11(start);
6184
+ let current = resolve13(start);
5743
6185
  while (true) {
5744
6186
  if (await pathExists(workspaceConfigPath(current))) return current;
5745
- const parent = dirname17(current);
5746
- if (parent === current) return resolve11(start);
6187
+ const parent = dirname20(current);
6188
+ if (parent === current) return resolve13(start);
5747
6189
  current = parent;
5748
6190
  }
5749
6191
  }
@@ -5769,16 +6211,16 @@ function mergeWorkspaceConfig(global, project) {
5769
6211
  });
5770
6212
  }
5771
6213
  function resolveConfigPath(path, baseRoot) {
5772
- if (path.startsWith("~/")) return resolve11(homedir4(), path.slice(2));
6214
+ if (path.startsWith("~/")) return resolve13(homedir4(), path.slice(2));
5773
6215
  if (path === "~") return homedir4();
5774
- return path.startsWith("/") ? resolve11(path) : resolve11(baseRoot, path);
6216
+ return path.startsWith("/") ? resolve13(path) : resolve13(baseRoot, path);
5775
6217
  }
5776
6218
  function emptyWorkspaceConfig() {
5777
6219
  return { schemaVersion: 1, packages: [], registry: {}, trust: {}, profiles: {}, agents: {} };
5778
6220
  }
5779
6221
  async function readConfigPath(path) {
5780
6222
  if (!await pathExists(path)) return emptyWorkspaceConfig();
5781
- return workspaceConfigSchema.parse(JSON.parse(await readFile18(path, "utf8")));
6223
+ return workspaceConfigSchema.parse(JSON.parse(await readFile19(path, "utf8")));
5782
6224
  }
5783
6225
  function mergeWorkspaceTrust(global, project) {
5784
6226
  return {
@@ -5793,23 +6235,23 @@ function sortedUnique2(values) {
5793
6235
  }
5794
6236
 
5795
6237
  // src/lifecycle/customization.ts
5796
- import { appendFile, cp as cp6, mkdir as mkdir13, rm as rm8 } from "fs/promises";
5797
- import { dirname as dirname19, join as join29 } from "path";
6238
+ import { appendFile, cp as cp6, mkdir as mkdir16, rm as rm9 } from "fs/promises";
6239
+ import { dirname as dirname22, join as join31 } from "path";
5798
6240
 
5799
6241
  // src/resolve/graph.ts
5800
6242
  import { createHash as createHash6 } from "crypto";
5801
- import { mkdtemp as mkdtemp4, readdir as readdir6, readFile as readFile20, stat as stat10 } from "fs/promises";
6243
+ import { mkdtemp as mkdtemp4, readdir as readdir6, readFile as readFile21, stat as stat10 } from "fs/promises";
5802
6244
  import { tmpdir as tmpdir5 } from "os";
5803
- import { basename as basename16, extname as extname4, join as join28 } from "path";
6245
+ import { basename as basename18, extname as extname4, join as join30 } from "path";
5804
6246
 
5805
6247
  // src/resolve/identity.ts
5806
6248
  import { homedir as homedir6 } from "os";
5807
- import { resolve as resolve13 } from "path";
6249
+ import { resolve as resolve15 } from "path";
5808
6250
 
5809
6251
  // src/registry/client.ts
5810
- import { readFile as readFile19, rm as rm7, stat as stat9 } from "fs/promises";
6252
+ import { readFile as readFile20, rm as rm8, stat as stat9 } from "fs/promises";
5811
6253
  import { homedir as homedir5 } from "os";
5812
- import { dirname as dirname18, join as join27, resolve as resolve12 } from "path";
6254
+ import { dirname as dirname21, join as join29, resolve as resolve14 } from "path";
5813
6255
  import { fileURLToPath } from "url";
5814
6256
 
5815
6257
  // src/model/registry.ts
@@ -5820,6 +6262,12 @@ var registryEntrySchema = z7.object({
5820
6262
  type: z7.enum(["package", "skill", "plugin", "mcp", "adapter"]).default("package"),
5821
6263
  description: z7.string().default(""),
5822
6264
  tags: z7.array(z7.string().min(1)).default([]),
6265
+ select: z7.array(z7.string().min(1)).optional(),
6266
+ skills: z7.array(z7.string().min(1)).optional(),
6267
+ homepageUrl: z7.string().min(1).optional(),
6268
+ homepageLinkLabel: z7.string().min(1).optional(),
6269
+ sourceUrl: z7.string().min(1).optional(),
6270
+ sourceLinkLabel: z7.string().min(1).optional(),
5823
6271
  openpack: z7.object({
5824
6272
  schemaVersion: z7.number().int().positive().optional(),
5825
6273
  specVersion: z7.string().min(1).optional()
@@ -5884,7 +6332,7 @@ var RegistryClient = class {
5884
6332
  );
5885
6333
  }
5886
6334
  async clearCache() {
5887
- await rm7(this.cachePath, { force: true });
6335
+ await rm8(this.cachePath, { force: true });
5888
6336
  }
5889
6337
  async getSources() {
5890
6338
  if (this.options.sources?.length) return this.options.sources;
@@ -5907,7 +6355,7 @@ var RegistryClient = class {
5907
6355
  }
5908
6356
  async readCache() {
5909
6357
  if (!await pathExists(this.cachePath)) return void 0;
5910
- return registryCacheSchema.parse(JSON.parse(await readFile19(this.cachePath, "utf8")));
6358
+ return registryCacheSchema.parse(JSON.parse(await readFile20(this.cachePath, "utf8")));
5911
6359
  }
5912
6360
  isExpired(cache, ttlMs) {
5913
6361
  return this.now().getTime() - new Date(cache.fetchedAt).getTime() > ttlMs;
@@ -5924,12 +6372,12 @@ var RegistryClient = class {
5924
6372
  }
5925
6373
  const filePath = source.startsWith("file:") ? fileURLToPath(source) : source;
5926
6374
  if (await pathExists(filePath)) {
5927
- const fullPath = resolve12(filePath);
6375
+ const fullPath = resolve14(filePath);
5928
6376
  const stats = await stat9(fullPath);
5929
- return readFile19(stats.isDirectory() ? join27(fullPath, "index.json") : fullPath, "utf8");
6377
+ return readFile20(stats.isDirectory() ? join29(fullPath, "index.json") : fullPath, "utf8");
5930
6378
  }
5931
- const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join27(dirname18(this.cachePath), "registry-repos") }));
5932
- return readFile19(join27(resolved.resolvedPath, "index.json"), "utf8");
6379
+ const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join29(dirname21(this.cachePath), "registry-repos") }));
6380
+ return readFile20(join29(resolved.resolvedPath, "index.json"), "utf8");
5933
6381
  }
5934
6382
  warnCompatibility(entries) {
5935
6383
  for (const entry of entries) {
@@ -5943,17 +6391,20 @@ var RegistryClient = class {
5943
6391
  }
5944
6392
  };
5945
6393
  async function resolvePackageSource(source, workspaceRoot, options = {}) {
5946
- const { isExplicitSource } = await import("./identify-TXIDGMNL.js");
6394
+ const { isExplicitSource } = await import("./identify-T4RE5RBD.js");
5947
6395
  if (await isExplicitSource(source)) return { source };
5948
6396
  const entry = await new RegistryClient({ workspaceRoot, offline: options.offline, warn: options.warn }).resolve(source);
5949
6397
  if (!entry) {
5950
6398
  if (options.offline) {
5951
6399
  throw new Error(`Offline cannot refresh registry indexes (entry not found in cache: ${source}). Run without --offline first.`);
5952
6400
  }
5953
- throw new Error(`Registry entry not found: ${source}. Use an explicit path/git/skillkit/vercel source to bypass the registry.`);
6401
+ throw new Error(`Registry entry not found: ${source}. Use an explicit path/git/skillkit/vercel/mcp-registry/clawhub source to bypass the registry.`);
5954
6402
  }
5955
6403
  return { source: entry.source, registryEntry: entry };
5956
6404
  }
6405
+ function selectorsFromRegistryEntry(entry) {
6406
+ return normalizeArtifactSelectors(entry?.select, entry?.skills);
6407
+ }
5957
6408
  function mergeIndexes(indexes) {
5958
6409
  const merged = /* @__PURE__ */ new Map();
5959
6410
  for (const index of indexes) {
@@ -5964,7 +6415,7 @@ function mergeIndexes(indexes) {
5964
6415
  return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
5965
6416
  }
5966
6417
  function defaultRegistryCachePath() {
5967
- return join27(homedir5(), ".agentwheel", "registry-cache.json");
6418
+ return join29(homedir5(), ".agentwheel", "registry-cache.json");
5968
6419
  }
5969
6420
  function sameSources(a, b) {
5970
6421
  return a.length === b.length && a.every((source, index) => source === b[index]);
@@ -6021,7 +6472,23 @@ async function normalizeDependencySource(source, options) {
6021
6472
  driver: "vercel-skills"
6022
6473
  };
6023
6474
  }
6024
- throw new Error(`Unsupported dependency source: ${source}. Use registry:<name>, ./, ../, local:, github:, git:, skillkit:, or vercel:.`);
6475
+ if (trimmed.startsWith("mcp-registry:")) {
6476
+ const spec = normalizeLiteralProviderSpec(trimmed, "mcp-registry:");
6477
+ return {
6478
+ source: spec,
6479
+ normalizedSource: spec,
6480
+ driver: "mcp-registry"
6481
+ };
6482
+ }
6483
+ if (trimmed.startsWith("clawhub:")) {
6484
+ const spec = normalizeLiteralProviderSpec(trimmed, "clawhub:");
6485
+ return {
6486
+ source: spec,
6487
+ normalizedSource: spec,
6488
+ driver: "clawhub"
6489
+ };
6490
+ }
6491
+ throw new Error(`Unsupported dependency source: ${source}. Use registry:<name>, ./, ../, local:, github:, git:, skillkit:, vercel:, mcp-registry:, or clawhub:.`);
6025
6492
  }
6026
6493
  function isBareRegistryName(source) {
6027
6494
  return !source.includes(":") && !isLocalSource(source);
@@ -6034,16 +6501,16 @@ function localSourcePath(source) {
6034
6501
  }
6035
6502
  function resolveLocalPath(path, declaringPackageRoot) {
6036
6503
  if (path === "~") return homedir6();
6037
- if (path.startsWith("~/")) return resolve13(homedir6(), path.slice(2));
6038
- if (path.startsWith("/")) return resolve13(path);
6039
- return resolve13(declaringPackageRoot, path);
6504
+ if (path.startsWith("~/")) return resolve15(homedir6(), path.slice(2));
6505
+ if (path.startsWith("/")) return resolve15(path);
6506
+ return resolve15(declaringPackageRoot, path);
6040
6507
  }
6041
6508
  async function normalizeRegistrySource(name, options) {
6042
6509
  const client = options.registryClient ?? new RegistryClient({ workspaceRoot: options.workspaceRoot });
6043
6510
  const entry = await client.resolve(name);
6044
6511
  if (!entry) {
6045
6512
  throw new Error(
6046
- `Registry entry not found: ${name}. Bare dependency names are registry-only inside package manifests; use ./, ../, local:, github:, git:, skillkit:, or vercel: for explicit sources.`
6513
+ `Registry entry not found: ${name}. Bare dependency names are registry-only inside package manifests; use ./, ../, local:, github:, git:, skillkit:, vercel:, mcp-registry:, or clawhub: for explicit sources.`
6047
6514
  );
6048
6515
  }
6049
6516
  const resolved = await normalizeDependencySource(entry.source, {
@@ -6102,6 +6569,11 @@ function normalizeProviderSpec(spec, declaringPackageRoot) {
6102
6569
  }
6103
6570
  return spec.trim();
6104
6571
  }
6572
+ function normalizeLiteralProviderSpec(source, prefix) {
6573
+ const spec = source.slice(prefix.length).trim();
6574
+ if (!spec) throw new Error(`Provider dependency source must include a spec: ${prefix}<name>`);
6575
+ return `${prefix}${spec}`;
6576
+ }
6105
6577
 
6106
6578
  // src/resolve/semver.ts
6107
6579
  var semverPattern = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
@@ -6193,7 +6665,7 @@ function compareSemver(a, b) {
6193
6665
  var cacheLocks = /* @__PURE__ */ new Map();
6194
6666
  async function resolveDependencyGraph(roots, options) {
6195
6667
  if (roots.length === 0) throw new Error("At least one graph root is required.");
6196
- const graphRoot = await mkdtemp4(join28(tmpdir5(), "agentwheel-graph-"));
6668
+ const graphRoot = await mkdtemp4(join30(tmpdir5(), "agentwheel-graph-"));
6197
6669
  const fetchCache = /* @__PURE__ */ new Map();
6198
6670
  const nodesByKey = /* @__PURE__ */ new Map();
6199
6671
  const rootResults = [];
@@ -6213,7 +6685,9 @@ async function resolveDependencyGraph(roots, options) {
6213
6685
  useLock: root.useLock ?? options.lockedResolution,
6214
6686
  depth: 0,
6215
6687
  optional: false,
6216
- chain: [`workspace:${rootId}`]
6688
+ chain: [`workspace:${rootId}`],
6689
+ includeSuggestions: root.includeSuggestions ?? options.includeSuggestions,
6690
+ suggestionAliases: sortedUnique3([...root.suggestionAliases ?? [], ...options.suggestionAliases ?? []])
6217
6691
  };
6218
6692
  });
6219
6693
  let iterations = 0;
@@ -6341,13 +6815,18 @@ Run without ${lockLabel === "Offline" ? "--offline" : "--frozen-lock"} first.`
6341
6815
  selectionReasons: /* @__PURE__ */ new Map(),
6342
6816
  processedNeeds: /* @__PURE__ */ new Set(),
6343
6817
  processedPackageAliases: /* @__PURE__ */ new Set(),
6818
+ processedSuggestions: /* @__PURE__ */ new Set(),
6344
6819
  depth: requirement.depth,
6345
- fullPackageSelected: false
6820
+ fullPackageSelected: false,
6821
+ includeSuggestions: false,
6822
+ suggestionAliases: /* @__PURE__ */ new Set()
6346
6823
  };
6347
6824
  nodesByKey.set(nodeKey, state);
6348
6825
  }
6349
6826
  state.depth = Math.min(state.depth, requirement.depth);
6350
6827
  state.fullPackageSelected = state.fullPackageSelected || requirement.select === void 0;
6828
+ state.includeSuggestions = state.includeSuggestions || requirement.includeSuggestions === true;
6829
+ for (const alias of requirement.suggestionAliases ?? []) state.suggestionAliases.add(alias);
6351
6830
  state.requiredBy.add(requirement.requiredBy);
6352
6831
  for (const selector of selected) addSelectedSelector(state, selector, requirement.selectionReason);
6353
6832
  refreshNode(state);
@@ -6394,10 +6873,13 @@ Dependency chain: ${requirement.chain.join(" -> ")}`);
6394
6873
  async function collectDependencyNeeds(state, fetched, options, chain) {
6395
6874
  if (fetched.manifest?.schemaVersion !== 2) return [];
6396
6875
  const dependencies = fetched.manifest.requires ?? {};
6876
+ const suggestions = fetched.manifest.suggests ?? {};
6397
6877
  const dependencyEntries = Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b));
6878
+ const suggestionEntries = Object.entries(suggestions).sort(([a], [b]) => a.localeCompare(b));
6879
+ const suggestionOptions = suggestionOptionsForState(state, options);
6398
6880
  const requirements = [];
6399
6881
  if (options.noDeps) {
6400
- warnNoDepsOnce(state, dependencyEntries.map(([alias]) => alias), options.warn);
6882
+ warnNoDepsOnce(state, [...dependencyEntries.map(([alias]) => alias), ...suggestionEntries.map(([alias]) => alias)], options.warn);
6401
6883
  } else {
6402
6884
  for (const [alias, dependency] of dependencyEntries) {
6403
6885
  if (state.processedPackageAliases.has(alias)) continue;
@@ -6406,6 +6888,14 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
6406
6888
  if (!dependencyTargetsRuntime(dependency.runtimes, options.runtime, state.node.id, alias, options.warn)) continue;
6407
6889
  requirements.push(dependencyRequirement(state, fetched, alias, dependency, dependency.select, chain, options.lockedResolution === true));
6408
6890
  }
6891
+ for (const [alias, suggestion] of suggestionEntries) {
6892
+ if (state.processedSuggestions.has(alias)) continue;
6893
+ if (!shouldIncludeSuggestionAlias(alias, suggestionOptions, state.fullPackageSelected)) continue;
6894
+ if (!suggestion.select?.length && !(state.fullPackageSelected && suggestion.select === void 0) && !explicitSuggestionAlias(alias, suggestionOptions)) continue;
6895
+ state.processedSuggestions.add(alias);
6896
+ if (!dependencyTargetsRuntime(suggestion.runtimes, options.runtime, state.node.id, alias, options.warn)) continue;
6897
+ requirements.push(suggestionRequirement(state, fetched, alias, suggestion, suggestion.select, chain, suggestionOptions));
6898
+ }
6409
6899
  }
6410
6900
  const artifactsBySelector = new Map(fetched.artifacts.map((artifact) => [artifactSelectorKey(artifact), artifact]));
6411
6901
  const artifactsByRelativePath = new Map(fetched.artifacts.map((artifact) => [artifact.relativePath.replaceAll("\\", "/"), artifact]));
@@ -6449,6 +6939,28 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
6449
6939
  }
6450
6940
  addSelectedSelector(state, parsed.selector, `required by ${parentSelector}`);
6451
6941
  }
6942
+ for (const suggestion of artifact.suggests ?? []) {
6943
+ const parsed = parseArtifactSuggestion(suggestion);
6944
+ if (!shouldIncludeSuggestionAlias(parsed.alias, suggestionOptions, true)) continue;
6945
+ if (!requirementTargetsRuntime(parsed.runtimes, options.runtime, `${state.node.id}:${parentSelector} -> ${parsed.raw}`, options.warn)) continue;
6946
+ if (options.noDeps) {
6947
+ warnNoDepsOnce(state, [parsed.alias], options.warn);
6948
+ continue;
6949
+ }
6950
+ const packageSuggestion = suggestionForAlias(suggestions, state.node.id, parsed.alias);
6951
+ if (!dependencyTargetsRuntime(packageSuggestion.runtimes, options.runtime, state.node.id, parsed.alias, options.warn)) continue;
6952
+ requirements.push(suggestionRequirement(
6953
+ state,
6954
+ fetched,
6955
+ parsed.alias,
6956
+ packageSuggestion,
6957
+ combinedSelectors(packageSuggestion.select, parsed.select),
6958
+ chain,
6959
+ suggestionOptions,
6960
+ parsed.optional || shouldTreatSuggestionAsOptional(parsed.alias, packageSuggestion, suggestionOptions),
6961
+ `suggested by ${parentSelector}`
6962
+ ));
6963
+ }
6452
6964
  for (const include of await collectIncludeNeeds(artifact, artifactsByRelativePath)) {
6453
6965
  if (!include.alias) continue;
6454
6966
  if (options.noDeps) {
@@ -6490,9 +7002,24 @@ function dependencyRequirement(state, fetched, alias, dependency, select, chain,
6490
7002
  chain: [...chain, `${state.node.id}:${alias}`],
6491
7003
  version: dependency.version,
6492
7004
  integrity: dependency.integrity,
6493
- selectionReason
7005
+ selectionReason,
7006
+ includeSuggestions: state.includeSuggestions,
7007
+ suggestionAliases: sortedUnique3([...state.suggestionAliases])
6494
7008
  };
6495
7009
  }
7010
+ function suggestionRequirement(state, fetched, alias, suggestion, select, chain, options, optional = shouldTreatSuggestionAsOptional(alias, suggestion, options), selectionReason) {
7011
+ return dependencyRequirement(
7012
+ state,
7013
+ fetched,
7014
+ alias,
7015
+ suggestion,
7016
+ select,
7017
+ chain,
7018
+ options.lockedResolution === true,
7019
+ optional,
7020
+ selectionReason
7021
+ );
7022
+ }
6496
7023
  function dependencyForAlias(dependencies, nodeId, alias) {
6497
7024
  const dependency = dependencies[alias];
6498
7025
  if (!dependency) {
@@ -6500,6 +7027,13 @@ function dependencyForAlias(dependencies, nodeId, alias) {
6500
7027
  }
6501
7028
  return dependency;
6502
7029
  }
7030
+ function suggestionForAlias(suggestions, nodeId, alias) {
7031
+ const suggestion = suggestions[alias];
7032
+ if (!suggestion) {
7033
+ throw new Error(`Suggestion alias not found in ${nodeId}: ${alias}`);
7034
+ }
7035
+ return suggestion;
7036
+ }
6503
7037
  function warnNoDepsOnce(state, aliases, warn) {
6504
7038
  const unique = sortedUnique3(aliases.filter(Boolean));
6505
7039
  if (unique.length === 0 || state.processedPackageAliases.has("__noDepsWarned")) return;
@@ -6517,6 +7051,45 @@ function parseArtifactRequirement(requirement) {
6517
7051
  runtimes: typeof requirement === "string" ? void 0 : requirement.runtimes
6518
7052
  };
6519
7053
  }
7054
+ function parseArtifactSuggestion(suggestion) {
7055
+ if (typeof suggestion === "string") {
7056
+ return {
7057
+ raw: suggestion,
7058
+ alias: suggestion,
7059
+ optional: false
7060
+ };
7061
+ }
7062
+ return {
7063
+ raw: suggestion.alias,
7064
+ alias: suggestion.alias,
7065
+ select: suggestion.select,
7066
+ optional: suggestion.optional === true,
7067
+ runtimes: suggestion.runtimes
7068
+ };
7069
+ }
7070
+ function shouldIncludeSuggestionAlias(alias, options, includeWhenAllSuggestions) {
7071
+ const aliases = new Set(options.suggestionAliases ?? []);
7072
+ if (aliases.has(alias)) return true;
7073
+ return options.includeSuggestions === true && includeWhenAllSuggestions;
7074
+ }
7075
+ function suggestionOptionsForState(state, options) {
7076
+ return {
7077
+ ...options,
7078
+ includeSuggestions: state.includeSuggestions || options.includeSuggestions === true,
7079
+ suggestionAliases: sortedUnique3([...options.suggestionAliases ?? [], ...state.suggestionAliases])
7080
+ };
7081
+ }
7082
+ function explicitSuggestionAlias(alias, options) {
7083
+ return (options.suggestionAliases ?? []).includes(alias);
7084
+ }
7085
+ function shouldTreatSuggestionAsOptional(alias, suggestion, options) {
7086
+ if (suggestion.optional === true) return true;
7087
+ return options.includeSuggestions === true && !(options.suggestionAliases ?? []).includes(alias);
7088
+ }
7089
+ function combinedSelectors(base, extra) {
7090
+ const values = [...base ?? [], ...extra ?? []];
7091
+ return values.length > 0 ? sortedUnique3(values) : void 0;
7092
+ }
6520
7093
  function parseDependencySelector(value) {
6521
7094
  const cleaned = value.trim();
6522
7095
  const slash = cleaned.indexOf("/");
@@ -6555,7 +7128,7 @@ async function collectIncludeNeeds(artifact, artifactsByRelativePath) {
6555
7128
  const file = stack.shift();
6556
7129
  if (scanned.has(file)) continue;
6557
7130
  scanned.add(file);
6558
- const content = await readFile20(file, "utf8");
7131
+ const content = await readFile21(file, "utf8");
6559
7132
  for (const include of extractOpenPackIncludeSelectors(content)) {
6560
7133
  await collectIncludeSelector(include.raw, include.optional, artifactsByRelativePath, scanned, stack, needs);
6561
7134
  }
@@ -6598,7 +7171,7 @@ async function listMarkdownFiles2(root) {
6598
7171
  const out = [];
6599
7172
  async function walk2(dir) {
6600
7173
  for (const entry of (await readdir6(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
6601
- const full = join28(dir, entry.name);
7174
+ const full = join30(dir, entry.name);
6602
7175
  if (entry.isDirectory()) {
6603
7176
  await walk2(full);
6604
7177
  } else if (entry.isFile() && extname4(entry.name).toLowerCase() === ".md") {
@@ -6633,7 +7206,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
6633
7206
  const promise = (async () => {
6634
7207
  const driver = getSourceDriver(normalized.driver);
6635
7208
  const resolved = await driver.resolve(normalized.source, {
6636
- cacheRoot: options.cacheRoot ?? join28(options.workspaceRoot, ".agentwheel", "cache"),
7209
+ cacheRoot: options.cacheRoot ?? join30(options.workspaceRoot, ".agentwheel", "cache"),
6637
7210
  mode,
6638
7211
  ref: refOverride ?? normalized.requestedRef,
6639
7212
  frozenLock: hardLockedCheckout
@@ -6643,7 +7216,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
6643
7216
  const exported = await driver.export(translated);
6644
7217
  const manifest = await readPackageManifest(exported.resolvedPath);
6645
7218
  const artifacts = await driver.list(exported);
6646
- const name = manifest?.name ?? exported.packageName ?? basename16(exported.resolvedPath);
7219
+ const name = manifest?.name ?? exported.packageName ?? basename18(exported.resolvedPath);
6647
7220
  const version = manifest?.version ?? exported.packageVersion ?? "0.0.0";
6648
7221
  const sourceHash = exported.sourceHash ?? await hashPath(exported.resolvedPath);
6649
7222
  return {
@@ -6727,7 +7300,7 @@ function shouldCheckLockedRootSource(requirement) {
6727
7300
  }
6728
7301
  function isExplicitNonRegistrySource(source) {
6729
7302
  const trimmed = source.trim();
6730
- return trimmed === "~" || trimmed.startsWith("~/") || trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("/") || trimmed.startsWith("local:") || trimmed.startsWith("github:") || trimmed.startsWith("git:") || trimmed.startsWith("skillkit:") || trimmed.startsWith("vercel:");
7303
+ return trimmed === "~" || trimmed.startsWith("~/") || trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("/") || trimmed.startsWith("local:") || trimmed.startsWith("github:") || trimmed.startsWith("git:") || trimmed.startsWith("skillkit:") || trimmed.startsWith("vercel:") || trimmed.startsWith("mcp-registry:") || trimmed.startsWith("clawhub:");
6731
7304
  }
6732
7305
  function lockedRootSourceDrifted(declared, locked) {
6733
7306
  return declared.normalizedSource !== locked.normalizedSource || declared.requestedRef !== locked.requestedRef;
@@ -6750,8 +7323,8 @@ function verifyIntegrity(integrity, sourceHash, label) {
6750
7323
  async function withCachePathLock(path, fn) {
6751
7324
  const previous = cacheLocks.get(path) ?? Promise.resolve();
6752
7325
  let release = () => void 0;
6753
- const current = previous.then(() => new Promise((resolve19) => {
6754
- release = resolve19;
7326
+ const current = previous.then(() => new Promise((resolve21) => {
7327
+ release = resolve21;
6755
7328
  }));
6756
7329
  cacheLocks.set(path, current);
6757
7330
  await previous;
@@ -6840,8 +7413,8 @@ async function mapLimit(items, limit, fn) {
6840
7413
 
6841
7414
  // src/lifecycle/customization.ts
6842
7415
  async function remember(workspaceRoot, runtime, text) {
6843
- const overlayPath = join29(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
6844
- await mkdir13(dirname19(overlayPath), { recursive: true });
7416
+ const overlayPath = join31(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
7417
+ await mkdir16(dirname22(overlayPath), { recursive: true });
6845
7418
  await appendFile(overlayPath, `${text.trim()}
6846
7419
  `, "utf8");
6847
7420
  return { overlayPath };
@@ -6864,9 +7437,9 @@ async function ejectArtifact(workspaceRoot, item) {
6864
7437
  throw new Error(`Artifact not found: ${item}`);
6865
7438
  }
6866
7439
  const ejectedIdentity = parsed.packageIdentity === parsed.packageName ? parsed.packageIdentity : candidate.nodeId === parsed.packageIdentity ? candidate.nodeId : `${candidate.packageName}@${candidate.packageVersion}`;
6867
- const ejectedPath = join29(workspaceRoot, ".agentwheel", "ejected", ...ejectedIdentity.split("/"), parsed.type, parsed.name);
6868
- await mkdir13(dirname19(ejectedPath), { recursive: true });
6869
- await rm8(ejectedPath, { recursive: true, force: true });
7440
+ const ejectedPath = join31(workspaceRoot, ".agentwheel", "ejected", ...ejectedIdentity.split("/"), parsed.type, parsed.name);
7441
+ await mkdir16(dirname22(ejectedPath), { recursive: true });
7442
+ await rm9(ejectedPath, { recursive: true, force: true });
6870
7443
  await cp6(artifact.stagedPath ?? artifact.sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
6871
7444
  return {
6872
7445
  ...parsed,
@@ -6877,7 +7450,7 @@ async function ejectArtifact(workspaceRoot, item) {
6877
7450
  ejectedPath
6878
7451
  };
6879
7452
  } finally {
6880
- await Promise.all(candidates.map((candidate) => rm8(candidate.bundle.root, { recursive: true, force: true })));
7453
+ await Promise.all(candidates.map((candidate) => rm9(candidate.bundle.root, { recursive: true, force: true })));
6881
7454
  }
6882
7455
  }
6883
7456
  function parseEjectItem(item) {
@@ -6907,7 +7480,7 @@ async function stageEjectCandidate(workspaceRoot, pkg) {
6907
7480
  const adapter = pkg.adapterConfig ? await loadAdapterConfig(pkg.adapterConfig) : getAdapter(pkg.adapter);
6908
7481
  const bundle = await stageSource(driver, normalized.source, {
6909
7482
  adapter,
6910
- cacheRoot: join29(workspaceRoot, ".agentwheel", "cache"),
7483
+ cacheRoot: join31(workspaceRoot, ".agentwheel", "cache"),
6911
7484
  mode: pkg.mode,
6912
7485
  ref: normalized.requestedRef ?? pkg.requestedRef
6913
7486
  });
@@ -6954,12 +7527,12 @@ function ejectCommands(candidates, parsed) {
6954
7527
  }
6955
7528
 
6956
7529
  // src/lifecycle/profile.ts
6957
- import { rm as rm9 } from "fs/promises";
7530
+ import { rm as rm10 } from "fs/promises";
6958
7531
 
6959
7532
  // src/lifecycle/source-plan.ts
6960
7533
  import { createHash as createHash8 } from "crypto";
6961
- import { mkdir as mkdir15 } from "fs/promises";
6962
- import { dirname as dirname21, join as join32, resolve as resolve14 } from "path";
7534
+ import { mkdir as mkdir18 } from "fs/promises";
7535
+ import { dirname as dirname24, join as join34, resolve as resolve16 } from "path";
6963
7536
 
6964
7537
  // src/resolve/graph-diff.ts
6965
7538
  function diffGraphLocks(previous, next) {
@@ -7081,11 +7654,11 @@ function short(hash) {
7081
7654
 
7082
7655
  // src/resolve/render.ts
7083
7656
  import { createHash as createHash7 } from "crypto";
7084
- import { readFile as readFile21, mkdtemp as mkdtemp5 } from "fs/promises";
7657
+ import { readFile as readFile22, mkdtemp as mkdtemp5 } from "fs/promises";
7085
7658
  import { tmpdir as tmpdir6 } from "os";
7086
- import { join as join30 } from "path";
7659
+ import { join as join32 } from "path";
7087
7660
  async function renderGraphForTarget(graph, targetContext = {}) {
7088
- const root = await mkdtemp5(join30(tmpdir6(), "agentwheel-render-"));
7661
+ const root = await mkdtemp5(join32(tmpdir6(), "agentwheel-render-"));
7089
7662
  const artifacts = [];
7090
7663
  const stagedNodes = /* @__PURE__ */ new Map();
7091
7664
  const includeEdges = /* @__PURE__ */ new Map();
@@ -7205,7 +7778,7 @@ async function artifactContentMap(artifacts) {
7205
7778
  const out = /* @__PURE__ */ new Map();
7206
7779
  for (const artifact of artifacts) {
7207
7780
  if (artifact.kind !== "file") continue;
7208
- out.set(artifact.relativePath.replaceAll("\\", "/"), await readFile21(artifact.stagedPath ?? artifact.sourcePath, "utf8"));
7781
+ out.set(artifact.relativePath.replaceAll("\\", "/"), await readFile22(artifact.stagedPath ?? artifact.sourcePath, "utf8"));
7209
7782
  }
7210
7783
  return out;
7211
7784
  }
@@ -7468,9 +8041,9 @@ function lockArtifactFor(artifact) {
7468
8041
  }
7469
8042
 
7470
8043
  // src/lifecycle/trust.ts
7471
- import { mkdir as mkdir14, readFile as readFile22 } from "fs/promises";
8044
+ import { mkdir as mkdir17, readFile as readFile23 } from "fs/promises";
7472
8045
  import { homedir as homedir7 } from "os";
7473
- import { dirname as dirname20, join as join31 } from "path";
8046
+ import { dirname as dirname23, join as join33 } from "path";
7474
8047
  import { z as z8 } from "zod";
7475
8048
  var trustStoreSchema = z8.object({
7476
8049
  version: z8.literal(1),
@@ -7544,14 +8117,14 @@ function sortedUnique4(values) {
7544
8117
  }
7545
8118
  async function readTrustStore(path) {
7546
8119
  if (!await pathExists(path)) return { version: 1, acceptedSources: [] };
7547
- return trustStoreSchema.parse(JSON.parse(await readFile22(path, "utf8")));
8120
+ return trustStoreSchema.parse(JSON.parse(await readFile23(path, "utf8")));
7548
8121
  }
7549
8122
  async function writeTrustStore(path, store) {
7550
- await mkdir14(dirname20(path), { recursive: true });
8123
+ await mkdir17(dirname23(path), { recursive: true });
7551
8124
  await writeJsonAtomic(path, trustStoreSchema.parse(store));
7552
8125
  }
7553
8126
  function defaultTrustStorePath() {
7554
- return process.env.AGENTWHEEL_TRUST_STORE ?? join31(homedir7(), ".agentwheel", "trust.json");
8127
+ return process.env.AGENTWHEEL_TRUST_STORE ?? join33(homedir7(), ".agentwheel", "trust.json");
7555
8128
  }
7556
8129
 
7557
8130
  // src/lifecycle/source-plan.ts
@@ -7589,9 +8162,11 @@ async function createGraphSourcePlan(options) {
7589
8162
  const registryClient = new RegistryClient({ workspaceRoot, offline: lockMode, offlineLabel: lockLabel, warn });
7590
8163
  const graph = await resolveDependencyGraph(options.roots, {
7591
8164
  workspaceRoot,
7592
- cacheRoot: join32(workspaceRoot, ".agentwheel", "cache"),
8165
+ cacheRoot: join34(workspaceRoot, ".agentwheel", "cache"),
7593
8166
  registryClient,
7594
8167
  noDeps: options.noDeps,
8168
+ includeSuggestions: options.includeSuggestions,
8169
+ suggestionAliases: options.suggestionAliases,
7595
8170
  lockedResolution: options.lockedResolution,
7596
8171
  frozenLock: lockMode,
7597
8172
  offline: options.offline,
@@ -7687,7 +8262,7 @@ async function readExistingGraphLock(path) {
7687
8262
  return readGraphLock(path);
7688
8263
  }
7689
8264
  function pathForGraphLock(workspaceRoot, targetKey, adapter, targetFingerprint) {
7690
- return join32(workspaceRoot, ".agentwheel", "locks", sanitizePathSegment(targetKey), sanitizePathSegment(adapter), `${targetFingerprint}.graph-lock.json`);
8265
+ return join34(workspaceRoot, ".agentwheel", "locks", sanitizePathSegment(targetKey), sanitizePathSegment(adapter), `${targetFingerprint}.graph-lock.json`);
7691
8266
  }
7692
8267
  function sanitizePathSegment(value) {
7693
8268
  return value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "default";
@@ -7696,7 +8271,7 @@ function digestGraphLock(lock) {
7696
8271
  return createHash8("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
7697
8272
  }
7698
8273
  function workspaceOwnerId(workspaceRoot) {
7699
- return `workspace-root:${resolve14(workspaceRoot)}`;
8274
+ return `workspace-root:${resolve16(workspaceRoot)}`;
7700
8275
  }
7701
8276
  function assertFrozenGraph(previousLock, graph, frozen, label) {
7702
8277
  if (!frozen) return;
@@ -7751,7 +8326,7 @@ ${sources.map((source) => `- ${source}`).join("\n")}`);
7751
8326
  }
7752
8327
 
7753
8328
  // src/runtime/target.ts
7754
- import { basename as basename17, dirname as dirname22, join as join33, resolve as resolve15 } from "path";
8329
+ import { basename as basename19, dirname as dirname25, join as join35, resolve as resolve17 } from "path";
7755
8330
  var runtimeMarkers = [
7756
8331
  { adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
7757
8332
  { adapter: "claude", dirs: [".claude"] },
@@ -7760,9 +8335,9 @@ var runtimeMarkers = [
7760
8335
  { adapter: "copilot", dirs: [".github"] }
7761
8336
  ];
7762
8337
  async function resolveRuntimeTarget(request = {}) {
7763
- const cwd = resolve15(request.cwd ?? process.cwd());
8338
+ const cwd = resolve17(request.cwd ?? process.cwd());
7764
8339
  if (request.targetRoot) {
7765
- const targetRoot = resolve15(request.targetRoot);
8340
+ const targetRoot = resolve17(request.targetRoot);
7766
8341
  return {
7767
8342
  adapter: request.adapter ?? "openclaw",
7768
8343
  installationType: request.installationType,
@@ -7793,7 +8368,7 @@ async function resolveRuntimeTarget(request = {}) {
7793
8368
  async function resolveAllRuntimeTargets(request = {}) {
7794
8369
  if (request.targetRoot) return [await resolveRuntimeTarget(request)];
7795
8370
  if (request.agent) return [await resolveRuntimeTarget(request)];
7796
- const cwd = resolve15(request.cwd ?? process.cwd());
8371
+ const cwd = resolve17(request.cwd ?? process.cwd());
7797
8372
  const workspaceRoot = await findWorkspaceRoot(cwd);
7798
8373
  const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
7799
8374
  const targets = Object.entries(config.agents).map(([name]) => targetFromAgent(name, config, workspaceRoot, request.installationType));
@@ -7803,8 +8378,8 @@ async function resolveAllRuntimeTargets(request = {}) {
7803
8378
  return targets;
7804
8379
  }
7805
8380
  async function resolveProfileRuntimeTargets(request) {
7806
- const cwd = resolve15(request.cwd ?? process.cwd());
7807
- const workspaceRoot = request.targetRoot ? resolve15(request.targetRoot) : await findWorkspaceRoot(cwd);
8381
+ const cwd = resolve17(request.cwd ?? process.cwd());
8382
+ const workspaceRoot = request.targetRoot ? resolve17(request.targetRoot) : await findWorkspaceRoot(cwd);
7808
8383
  const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
7809
8384
  const profile = config.profiles[request.profile];
7810
8385
  if (!profile) {
@@ -7857,14 +8432,14 @@ async function detectRuntimeTarget(cwd = process.cwd(), adapterFilter) {
7857
8432
  return unique[0];
7858
8433
  }
7859
8434
  async function detectRuntimeTargets(cwd = process.cwd(), adapterFilter) {
7860
- const root = resolve15(cwd);
8435
+ const root = resolve17(cwd);
7861
8436
  const matches = [];
7862
8437
  for (const marker of runtimeMarkers) {
7863
8438
  if (adapterFilter && marker.adapter !== adapterFilter) continue;
7864
8439
  for (const dir of marker.dirs) {
7865
- if (basename17(root) === dir) {
7866
- matches.push({ adapter: marker.adapter, targetRoot: dirname22(root) });
7867
- } else if (await pathExists(join33(root, dir))) {
8440
+ if (basename19(root) === dir) {
8441
+ matches.push({ adapter: marker.adapter, targetRoot: dirname25(root) });
8442
+ } else if (await pathExists(join35(root, dir))) {
7868
8443
  matches.push({ adapter: marker.adapter, targetRoot: root });
7869
8444
  }
7870
8445
  }
@@ -7901,9 +8476,9 @@ function dedupeTargets(matches) {
7901
8476
  return [...byKey.values()];
7902
8477
  }
7903
8478
  function runtimeScanRoot(request) {
7904
- const root = resolve15(request.targetRoot ?? request.cwd ?? process.cwd());
8479
+ const root = resolve17(request.targetRoot ?? request.cwd ?? process.cwd());
7905
8480
  if (request.targetRoot) return root;
7906
- return runtimeMarkers.some((marker) => marker.dirs.includes(basename17(root))) ? dirname22(root) : root;
8481
+ return runtimeMarkers.some((marker) => marker.dirs.includes(basename19(root))) ? dirname25(root) : root;
7907
8482
  }
7908
8483
 
7909
8484
  // src/lifecycle/profile.ts
@@ -7940,7 +8515,9 @@ async function syncProfile(options) {
7940
8515
  ref: pkg.requestedRef,
7941
8516
  select: selected ?? normalizeArtifactSelectors(pkg.select, pkg.skills),
7942
8517
  aliases: pkg.aliases,
7943
- overrides: pkg.overrides
8518
+ overrides: pkg.overrides,
8519
+ includeSuggestions: options.includeSuggestions === true || pkg.withSuggestions === true,
8520
+ suggestionAliases: combinedSuggestionAliases(pkg.suggestions, options.suggestionAliases)
7944
8521
  })),
7945
8522
  targetRoot: target.targetRoot,
7946
8523
  workspaceRoot: options.workspaceRoot,
@@ -7960,6 +8537,8 @@ async function syncProfile(options) {
7960
8537
  },
7961
8538
  installationType,
7962
8539
  noDeps: options.noDeps,
8540
+ includeSuggestions: options.includeSuggestions,
8541
+ suggestionAliases: options.suggestionAliases,
7963
8542
  lockedResolution: options.lockedResolution,
7964
8543
  frozenLock: options.frozenLock,
7965
8544
  offline: options.offline,
@@ -7989,7 +8568,7 @@ async function syncProfile(options) {
7989
8568
  });
7990
8569
  }
7991
8570
  } finally {
7992
- await rm9(graphPlan.bundle.root, { recursive: true, force: true });
8571
+ await rm10(graphPlan.bundle.root, { recursive: true, force: true });
7993
8572
  }
7994
8573
  }
7995
8574
  return results;
@@ -8000,6 +8579,7 @@ async function packageFromSource(source, options) {
8000
8579
  warn: options.warn
8001
8580
  });
8002
8581
  const driver = options.driver ?? inferSourceDriverName(resolved.source);
8582
+ const selectedArtifacts = normalizeArtifactSelectors(options.select, options.skills) ?? selectorsFromRegistryEntry(resolved.registryEntry);
8003
8583
  return {
8004
8584
  name: resolved.registryEntry?.name ?? source,
8005
8585
  source: resolved.source,
@@ -8007,15 +8587,184 @@ async function packageFromSource(source, options) {
8007
8587
  adapter: "openclaw",
8008
8588
  installationType: options.installationType,
8009
8589
  mode: options.mode ?? "pinned",
8010
- select: options.select,
8011
- skills: options.skills
8590
+ select: selectedArtifacts,
8591
+ withSuggestions: options.includeSuggestions === true ? true : void 0,
8592
+ suggestions: options.suggestionAliases
8012
8593
  };
8013
8594
  }
8595
+ function combinedSuggestionAliases(packageAliases, optionAliases) {
8596
+ const aliases = [...packageAliases ?? [], ...optionAliases ?? []].map((item) => item.trim()).filter(Boolean);
8597
+ return aliases.length > 0 ? [...new Set(aliases)].sort((a, b) => a.localeCompare(b)) : void 0;
8598
+ }
8599
+
8600
+ // src/registry/publish.ts
8601
+ var DEFAULT_REGISTRY_SUBMISSION_URL = "https://github.com/NestDevLab/agentwheel-registry/issues/new";
8602
+ var registryEntryTypes = ["package", "skill", "plugin", "mcp", "adapter"];
8603
+ var explicitSourcePrefixes = ["github:", "git:", "skillkit:", "vercel:", "mcp-registry:", "clawhub:"];
8604
+ function createRegistryPublishDraft(sourceInput, options = {}) {
8605
+ const source = normalizeCatalogueSource(sourceInput);
8606
+ const entry = {
8607
+ name: normalizeRegistryName(options.name ?? inferRegistryName(source)),
8608
+ source,
8609
+ type: options.type ? parseRegistryEntryType(options.type) : inferRegistryEntryType(source),
8610
+ description: options.description?.trim() ?? "",
8611
+ tags: normalizeTags(options.tags ?? [])
8612
+ };
8613
+ const selectors = normalizeArtifactSelectors(options.select, options.skills);
8614
+ if (selectors?.length) entry.select = selectors;
8615
+ if (options.skills?.length) entry.skills = normalizeSkillNames(options.skills);
8616
+ const installCommand = installCommandForEntry(entry);
8617
+ return {
8618
+ entry,
8619
+ installCommand,
8620
+ issueUrl: registrySubmissionUrl(entry, installCommand, options.submissionUrl ?? DEFAULT_REGISTRY_SUBMISSION_URL)
8621
+ };
8622
+ }
8623
+ function normalizeCatalogueSource(sourceInput) {
8624
+ const source = sourceInput.trim();
8625
+ if (!source) throw new Error("Catalogue source is required.");
8626
+ const unprefixedGitUrl = source.startsWith("git+http://") || source.startsWith("git+https://") ? source.slice("git+".length) : source;
8627
+ const githubSource = normalizeGitHubUrl(unprefixedGitUrl);
8628
+ if (githubSource) return githubSource;
8629
+ if (isHttpUrl(unprefixedGitUrl)) return `git:${unprefixedGitUrl}`;
8630
+ if (explicitSourcePrefixes.some((prefix) => source.startsWith(prefix))) return source;
8631
+ if (source.startsWith(".") || source.startsWith("/") || source.startsWith("local:")) {
8632
+ throw new Error("Catalogue submissions must use a public source, not a local path.");
8633
+ }
8634
+ const shorthand = normalizeOwnerRepoShorthand(source);
8635
+ if (shorthand) return shorthand;
8636
+ throw new Error(`Unsupported catalogue source: ${sourceInput}. Use a GitHub URL, github:owner/repo, git:https://..., skillkit:, vercel:, mcp-registry:, or clawhub:.`);
8637
+ }
8638
+ function inferRegistryEntryType(source) {
8639
+ if (source.startsWith("mcp-registry:")) return "mcp";
8640
+ if (source.startsWith("clawhub:")) return "plugin";
8641
+ if (source.startsWith("skillkit:") || source.startsWith("vercel:")) return "skill";
8642
+ return "package";
8643
+ }
8644
+ function parseRegistryEntryType(value) {
8645
+ const normalized = value.trim().toLowerCase();
8646
+ if (registryEntryTypes.includes(normalized)) return normalized;
8647
+ throw new Error(`Unsupported registry entry type: ${value}. Use ${registryEntryTypes.join(", ")}.`);
8648
+ }
8649
+ function installCommandForEntry(entry) {
8650
+ const base = ["agentwheel", "install", entry.source, ...selectorArgsForEntry(entry)];
8651
+ if (entry.type === "mcp") return [...base, "--adapter", "claude", "--local", "--dry-run"].map(shellQuoteArg).join(" ");
8652
+ if (entry.source.startsWith("clawhub:") || entry.type === "plugin") {
8653
+ return [...base, "--adapter", "openclaw", "--local", "--dry-run"].map(shellQuoteArg).join(" ");
8654
+ }
8655
+ return [...base, "--adapter", "codex", "--local", "--dry-run"].map(shellQuoteArg).join(" ");
8656
+ }
8657
+ function registrySubmissionUrl(entry, installCommand, baseUrl) {
8658
+ const url = new URL(baseUrl);
8659
+ url.searchParams.set("title", `Catalogue submission: ${entry.name}`);
8660
+ url.searchParams.set("body", registrySubmissionBody(entry, installCommand));
8661
+ return url.toString();
8662
+ }
8663
+ function registrySubmissionBody(entry, installCommand) {
8664
+ const descriptionNote = entry.description ? [] : ["", "Note: add a concise description before submitting."];
8665
+ return [
8666
+ "## Agentwheel catalogue submission",
8667
+ "",
8668
+ "Please review this generated registry entry:",
8669
+ "",
8670
+ "```json",
8671
+ JSON.stringify(entry, null, 2),
8672
+ "```",
8673
+ "",
8674
+ "## Verification",
8675
+ "",
8676
+ `Source: \`${entry.source}\``,
8677
+ `Suggested check: \`${installCommand}\``,
8678
+ "",
8679
+ "## Checklist",
8680
+ "",
8681
+ "- [ ] The source is public and installable.",
8682
+ "- [ ] The description is concise and factual.",
8683
+ "- [ ] Tags help discovery.",
8684
+ ...descriptionNote
8685
+ ].join("\n");
8686
+ }
8687
+ function normalizeGitHubUrl(value) {
8688
+ let url;
8689
+ try {
8690
+ url = new URL(value);
8691
+ } catch {
8692
+ return void 0;
8693
+ }
8694
+ if (url.hostname.toLowerCase() !== "github.com") return void 0;
8695
+ const segments = url.pathname.split("/").filter(Boolean).map((segment) => decodeURIComponent(segment));
8696
+ const [owner, repoSegment] = segments;
8697
+ if (!owner || !repoSegment) return void 0;
8698
+ const repo = repoSegment.replace(/\.git$/i, "");
8699
+ let ref = url.hash ? decodeURIComponent(url.hash.slice(1)) : "";
8700
+ if (segments[2] === "tree" && segments.length > 3) {
8701
+ ref = segments.slice(3).join("/");
8702
+ }
8703
+ return `github:${owner}/${repo}${ref ? `#${ref}` : ""}`;
8704
+ }
8705
+ function normalizeOwnerRepoShorthand(value) {
8706
+ const match = /^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)(#[^\s]+)?$/.exec(value);
8707
+ if (!match) return void 0;
8708
+ return `github:${match[1]}/${match[2]}${match[3] ?? ""}`;
8709
+ }
8710
+ function inferRegistryName(source) {
8711
+ const withoutRef = source.split("#", 1)[0];
8712
+ if (withoutRef.startsWith("github:")) return lastPathSegment(withoutRef.slice("github:".length));
8713
+ if (withoutRef.startsWith("git:")) return nameFromGitUrl(withoutRef.slice("git:".length));
8714
+ if (withoutRef.startsWith("skillkit:")) return lastPathSegment(withoutRef.slice("skillkit:".length));
8715
+ if (withoutRef.startsWith("vercel:")) return lastPathSegment(withoutRef.slice("vercel:".length));
8716
+ if (withoutRef.startsWith("mcp-registry:")) return lastPathSegment(withoutRef.slice("mcp-registry:".length));
8717
+ if (withoutRef.startsWith("clawhub:")) return lastPathSegment(withoutRef.slice("clawhub:".length));
8718
+ return withoutRef;
8719
+ }
8720
+ function nameFromGitUrl(value) {
8721
+ try {
8722
+ return lastPathSegment(new URL(value).pathname);
8723
+ } catch {
8724
+ return lastPathSegment(value);
8725
+ }
8726
+ }
8727
+ function lastPathSegment(value) {
8728
+ const trimmed = value.replace(/\.git$/i, "").replace(/\/+$/g, "");
8729
+ const segments = trimmed.split("/").filter(Boolean);
8730
+ return segments.at(-1) ?? trimmed;
8731
+ }
8732
+ function normalizeRegistryName(value) {
8733
+ const name = slugify(value);
8734
+ if (!name) throw new Error("Registry entry name could not be inferred. Pass --name <short-name>.");
8735
+ return name;
8736
+ }
8737
+ function normalizeTags(tags) {
8738
+ const normalized = tags.flatMap((tag) => tag.split(",")).map((tag) => slugify(tag)).filter(Boolean);
8739
+ return [...new Set(normalized)];
8740
+ }
8741
+ function normalizeSkillNames(skills) {
8742
+ return [...new Set(skills.flatMap((skill) => skill.split(",")).map((skill) => skill.trim()).filter(Boolean))];
8743
+ }
8744
+ function selectorArgsForEntry(entry) {
8745
+ if (entry.skills?.length) return entry.skills.flatMap((skill) => ["--skill", skill]);
8746
+ return (entry.select ?? []).flatMap((selector) => ["--select", selector]);
8747
+ }
8748
+ function slugify(value) {
8749
+ return value.trim().toLowerCase().replace(/\.git$/i, "").replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
8750
+ }
8751
+ function isHttpUrl(value) {
8752
+ try {
8753
+ const url = new URL(value);
8754
+ return url.protocol === "http:" || url.protocol === "https:";
8755
+ } catch {
8756
+ return false;
8757
+ }
8758
+ }
8759
+ function shellQuoteArg(value) {
8760
+ if (/^[A-Za-z0-9_./:@=-]+$/.test(value)) return value;
8761
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
8762
+ }
8014
8763
 
8015
8764
  // src/cli/update-check.ts
8016
- import { mkdir as mkdir16, readFile as readFile23, writeFile as writeFile14 } from "fs/promises";
8765
+ import { mkdir as mkdir19, readFile as readFile24, writeFile as writeFile17 } from "fs/promises";
8017
8766
  import { homedir as homedir8 } from "os";
8018
- import { dirname as dirname23, join as join34 } from "path";
8767
+ import { dirname as dirname26, join as join36 } from "path";
8019
8768
  var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
8020
8769
  var DEFAULT_TIMEOUT_MS = 300;
8021
8770
  var REGISTRY_URL = "https://registry.npmjs.org/agentwheel";
@@ -8023,7 +8772,7 @@ async function maybeCheckForUpdate(options) {
8023
8772
  if (isDisabled(options)) return;
8024
8773
  const now = options.now?.() ?? /* @__PURE__ */ new Date();
8025
8774
  const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
8026
- const cachePath = options.cachePath ?? join34(homedir8(), ".agentwheel", "update-check.json");
8775
+ const cachePath = options.cachePath ?? join36(homedir8(), ".agentwheel", "update-check.json");
8027
8776
  try {
8028
8777
  const cached = await readCache(cachePath);
8029
8778
  if (cached && now.getTime() - Date.parse(cached.checkedAt) < ttlMs) {
@@ -8060,7 +8809,7 @@ async function fetchLatestVersion(fetchImpl, timeoutMs) {
8060
8809
  }
8061
8810
  async function readCache(path) {
8062
8811
  try {
8063
- const parsed = JSON.parse(await readFile23(path, "utf8"));
8812
+ const parsed = JSON.parse(await readFile24(path, "utf8"));
8064
8813
  if (typeof parsed.checkedAt !== "string" || typeof parsed.latest !== "string") return void 0;
8065
8814
  return { checkedAt: parsed.checkedAt, latest: parsed.latest };
8066
8815
  } catch {
@@ -8068,8 +8817,8 @@ async function readCache(path) {
8068
8817
  }
8069
8818
  }
8070
8819
  async function writeCache(path, cache) {
8071
- await mkdir16(dirname23(path), { recursive: true });
8072
- await writeFile14(path, `${JSON.stringify(cache, null, 2)}
8820
+ await mkdir19(dirname26(path), { recursive: true });
8821
+ await writeFile17(path, `${JSON.stringify(cache, null, 2)}
8073
8822
  `, "utf8");
8074
8823
  }
8075
8824
  function warnIfNewer(latest, current, stderr = process.stderr) {
@@ -8092,9 +8841,9 @@ function normalizeVersion(version) {
8092
8841
 
8093
8842
  // src/model/package-validate.ts
8094
8843
  import { stat as stat11 } from "fs/promises";
8095
- import { resolve as resolve16 } from "path";
8844
+ import { resolve as resolve18 } from "path";
8096
8845
  async function validatePackage(root) {
8097
- const packageRoot = resolve16(root);
8846
+ const packageRoot = resolve18(root);
8098
8847
  const findings = [];
8099
8848
  const manifestPath = await findPackageManifestPath(packageRoot);
8100
8849
  if (!manifestPath) {
@@ -8141,6 +8890,14 @@ function validateDeclaredSelectors(manifest, findings, manifestPath) {
8141
8890
  validateSelector(selector, `requires.${alias}.select`, findings, manifestPath, { localOnly: true });
8142
8891
  }
8143
8892
  }
8893
+ for (const [alias, suggestion] of Object.entries(manifest.suggests ?? {})) {
8894
+ if (!alias.trim()) {
8895
+ findings.push({ level: "error", message: "Suggestion alias must be non-empty", path: manifestPath });
8896
+ }
8897
+ for (const selector of suggestion.select ?? []) {
8898
+ validateSelector(selector, `suggests.${alias}.select`, findings, manifestPath, { localOnly: true });
8899
+ }
8900
+ }
8144
8901
  }
8145
8902
  for (const [provideIndex, provide] of manifest.provides.entries()) {
8146
8903
  if (!("items" in provide) || !provide.items) continue;
@@ -8149,6 +8906,15 @@ function validateDeclaredSelectors(manifest, findings, manifestPath) {
8149
8906
  const selector = typeof requirement === "string" ? requirement : requirement.selector;
8150
8907
  validateSelector(selector, `provides[${provideIndex}].items.${itemName}.requires`, findings, manifestPath, { aliases: manifest.schemaVersion === 2 ? Object.keys(manifest.requires ?? {}) : [] });
8151
8908
  }
8909
+ for (const suggestion of item.suggests ?? []) {
8910
+ const alias = typeof suggestion === "string" ? suggestion : suggestion.alias;
8911
+ if (manifest.schemaVersion === 2 && !Object.keys(manifest.suggests ?? {}).includes(alias)) {
8912
+ findings.push({ level: "error", message: `provides[${provideIndex}].items.${itemName}.suggests: suggestion alias not declared: ${alias}`, path: manifestPath });
8913
+ }
8914
+ for (const selector of typeof suggestion === "string" ? [] : suggestion.select ?? []) {
8915
+ validateSelector(selector, `provides[${provideIndex}].items.${itemName}.suggests.${alias}.select`, findings, manifestPath, { localOnly: true });
8916
+ }
8917
+ }
8152
8918
  for (const entry of item.compose ?? []) {
8153
8919
  validateSelector(entry.include, `provides[${provideIndex}].items.${itemName}.compose.include`, findings, manifestPath, {
8154
8920
  aliases: manifest.schemaVersion === 2 ? Object.keys(manifest.requires ?? {}) : [],
@@ -8162,7 +8928,7 @@ async function validateManifestComposeInclude(packageRoot, selector, optional, f
8162
8928
  try {
8163
8929
  validateSelector(selector, "compose.include", findings, manifestPath, { fragmentsOnly: true, aliases });
8164
8930
  if (isCrossPackageSelector(selector)) return;
8165
- const full = resolve16(packageRoot, selector);
8931
+ const full = resolve18(packageRoot, selector);
8166
8932
  if (full !== packageRoot && !full.startsWith(`${packageRoot}/`)) {
8167
8933
  findings.push({ level: "error", message: `Compose include escapes package root: ${selector}`, path: manifestPath });
8168
8934
  return;
@@ -8215,13 +8981,13 @@ function isCrossPackageSelector(value) {
8215
8981
  }
8216
8982
 
8217
8983
  // src/model/package-migrate.ts
8218
- import { readFile as readFile24, rename as rename4, writeFile as writeFile15 } from "fs/promises";
8219
- import { join as join36, resolve as resolve17 } from "path";
8984
+ import { readFile as readFile25, rename as rename4, writeFile as writeFile18 } from "fs/promises";
8985
+ import { join as join38, resolve as resolve19 } from "path";
8220
8986
  import { applyEdits, modify, parse as parse4 } from "jsonc-parser";
8221
8987
  async function migratePackageManifest(root) {
8222
- const packageRoot = resolve17(root);
8988
+ const packageRoot = resolve19(root);
8223
8989
  for (const name of openPackManifestNames) {
8224
- const path = join36(packageRoot, name);
8990
+ const path = join38(packageRoot, name);
8225
8991
  if (await pathExists(path)) {
8226
8992
  return { changed: false, to: path, message: `Package already uses ${name}.` };
8227
8993
  }
@@ -8230,18 +8996,18 @@ async function migratePackageManifest(root) {
8230
8996
  if (!legacyName) {
8231
8997
  throw new Error(`No legacy package manifest found at ${packageRoot}`);
8232
8998
  }
8233
- const from = join36(packageRoot, legacyName);
8999
+ const from = join38(packageRoot, legacyName);
8234
9000
  const toName = legacyName.endsWith(".jsonc") ? "openpack.jsonc" : "openpack.json";
8235
- const to = join36(packageRoot, toName);
8236
- const content = await readFile24(from, "utf8");
9001
+ const to = join38(packageRoot, toName);
9002
+ const content = await readFile25(from, "utf8");
8237
9003
  const updated = updateSchemaVersion(content);
8238
9004
  await rename4(from, to);
8239
- await writeFile15(to, updated, "utf8");
9005
+ await writeFile18(to, updated, "utf8");
8240
9006
  return { changed: true, from, to, message: `Migrated ${legacyName} to ${toName}.` };
8241
9007
  }
8242
9008
  async function firstExistingLegacyManifest(root) {
8243
9009
  for (const name of legacyPackageManifestNames) {
8244
- if (await pathExists(join36(root, name))) return name;
9010
+ if (await pathExists(join38(root, name))) return name;
8245
9011
  }
8246
9012
  return void 0;
8247
9013
  }
@@ -8259,20 +9025,20 @@ function updateSchemaVersion(content) {
8259
9025
 
8260
9026
  // src/cli/version.ts
8261
9027
  import { readFileSync } from "fs";
8262
- import { dirname as dirname24, join as join37 } from "path";
9028
+ import { dirname as dirname27, join as join39 } from "path";
8263
9029
  import { fileURLToPath as fileURLToPath2 } from "url";
8264
9030
  var FALLBACK_VERSION = "0.0.0";
8265
9031
  function resolveCliVersion() {
8266
- let dir = dirname24(fileURLToPath2(import.meta.url));
9032
+ let dir = dirname27(fileURLToPath2(import.meta.url));
8267
9033
  while (true) {
8268
9034
  try {
8269
- const pkg = JSON.parse(readFileSync(join37(dir, "package.json"), "utf8"));
9035
+ const pkg = JSON.parse(readFileSync(join39(dir, "package.json"), "utf8"));
8270
9036
  if (pkg.name === "agentwheel" && typeof pkg.version === "string") {
8271
9037
  return pkg.version;
8272
9038
  }
8273
9039
  } catch {
8274
9040
  }
8275
- const parent = dirname24(dir);
9041
+ const parent = dirname27(dir);
8276
9042
  if (parent === dir) return FALLBACK_VERSION;
8277
9043
  dir = parent;
8278
9044
  }
@@ -8308,7 +9074,7 @@ program.command("init").description("initialize an agentwheel workspace or packa
8308
9074
  if (bootstrapPackage) console.log("Auto-added the agentwheel bootstrap skill for openclaw.");
8309
9075
  console.log(nextInstallNudge());
8310
9076
  });
8311
- program.command("add").description("add a package to .agentwheel/config.json without touching runtimes").argument("<source>", "package source").option("--driver <driver>", "source driver (local, git, skillkit, or vercel-skills)").option("--adapter <adapter>", "built-in adapter", "openclaw").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "workspace root").option("--mode <mode>", "pinned or tracking", "pinned").option("--name <name>", "package alias").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "allow this package to replace a colliding artifact (repeatable)", collectOverrideOption, []).action(async (source, options) => {
9077
+ program.command("add").description("add a package to .agentwheel/config.json without touching runtimes").argument("<source>", "package source").option("--driver <driver>", "source driver (local, git, skillkit, vercel-skills, mcp-registry, or clawhub)").option("--adapter <adapter>", "built-in adapter", "openclaw").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "workspace root").option("--mode <mode>", "pinned or tracking", "pinned").option("--name <name>", "package alias").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots on future installs", false).option("--suggestion <alias>", "include one suggested companion alias on future installs (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "allow this package to replace a colliding artifact (repeatable)", collectOverrideOption, []).action(async (source, options) => {
8312
9078
  const normalizedOptions = normalizeRuntimeScopeOptions(options);
8313
9079
  const targetRoot = normalizeTargetRoot(normalizedOptions.targetRoot ?? process.cwd());
8314
9080
  const entry = await packageEntryFromSource(source, targetRoot, normalizedOptions);
@@ -8317,10 +9083,10 @@ program.command("add").description("add a package to .agentwheel/config.json wit
8317
9083
  });
8318
9084
  program.command("list").description("list artifacts exposed by a package source").argument("<source>", "package source").option("--driver <driver>", "source driver").option("-t, --target-root <path>", "workspace root", process.cwd()).option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).action(async (source, options) => {
8319
9085
  const targetRoot = normalizeTargetRoot(options.targetRoot);
8320
- const selectedArtifacts = selectedArtifactsFromOptions(options);
8321
9086
  const resolvedInput = await resolvePackageSource(source, targetRoot);
9087
+ const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
8322
9088
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
8323
- const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join38(targetRoot, ".agentwheel", "cache") }))));
9089
+ const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join40(targetRoot, ".agentwheel", "cache") }))));
8324
9090
  const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
8325
9091
  for (const artifact of artifacts) {
8326
9092
  console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
@@ -8330,7 +9096,7 @@ program.command("scan").description("scan a package source for validation findin
8330
9096
  const targetRoot = normalizeTargetRoot(options.targetRoot);
8331
9097
  const resolvedInput = await resolvePackageSource(source, targetRoot);
8332
9098
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
8333
- const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join38(targetRoot, ".agentwheel", "cache") }))));
9099
+ const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join40(targetRoot, ".agentwheel", "cache") }))));
8334
9100
  const result = await driver.scan(resolved);
8335
9101
  if (result.findings.length === 0) {
8336
9102
  console.log("Scan ok: no findings");
@@ -8341,17 +9107,17 @@ program.command("scan").description("scan a package source for validation findin
8341
9107
  }
8342
9108
  if (!result.ok) process.exitCode = 1;
8343
9109
  });
8344
- program.command("plan").description("preview what install would reconcile without writing").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--dry-run", "accepted for symmetry; plan never writes", false).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
9110
+ program.command("plan").description("preview what install would reconcile without writing").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--dry-run", "accepted for symmetry; plan never writes", false).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
8345
9111
  await runInstallCommand(source, { ...options, dryRun: true }, { apply: false });
8346
9112
  });
8347
- program.command("install").description("install configured packages into runtime targets").argument("[name-or-source]", "configured package name/source or package source to add and install").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).addHelpText("after", "\nScoped install never removes files owned only by other configured packages; run a full install to reconcile those removals.\n").action(async (source, options) => {
9113
+ program.command("install").description("install configured packages into runtime targets").argument("[name-or-source]", "configured package name/source or package source to add and install").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).addHelpText("after", "\nScoped install never removes files owned only by other configured packages; run a full install to reconcile those removals.\n").action(async (source, options) => {
8348
9114
  await runInstallCommand(source, options, { apply: !options.dryRun });
8349
9115
  });
8350
- program.command("sync", { hidden: true }).argument("[name-or-source]", "configured package name/source or package source").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
9116
+ program.command("sync", { hidden: true }).argument("[name-or-source]", "configured package name/source or package source").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
8351
9117
  console.error("warning: 'agentwheel sync' is deprecated and will be removed in 0.10. Use 'agentwheel install'.");
8352
9118
  await runInstallCommand(source, options, { apply: !options.dryRun });
8353
9119
  });
8354
- program.command("update").description("re-resolve tracking packages, then apply the result").argument("[name]", "configured package name or source to update").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plans without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--allow-adapter-code", "allow loading local adapter code from configured packages", false).option("--select <type/name>", "temporarily select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "temporarily select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (name, options) => {
9120
+ program.command("update").description("re-resolve tracking packages, then apply the result").argument("[name]", "configured package name or source to update").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plans without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--allow-adapter-code", "allow loading local adapter code from configured packages", false).option("--select <type/name>", "temporarily select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "temporarily select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (name, options) => {
8355
9121
  const normalizedOptions = normalizeRuntimeScopeOptions(options);
8356
9122
  const targets = await resolveCliTargets(normalizedOptions, { preferAllProfile: true });
8357
9123
  for (const target of targets) {
@@ -8359,7 +9125,7 @@ program.command("update").description("re-resolve tracking packages, then apply
8359
9125
  }
8360
9126
  });
8361
9127
  program.command("deps").description("inspect the OpenPack dependency graph").addCommand(
8362
- new Command("tree").description("print the OpenPack dependency graph").argument("[source]", "optional package source to resolve").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
9128
+ new Command("tree").description("print the OpenPack dependency graph").argument("[source]", "optional package source to resolve").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
8363
9129
  const normalizedOptions = normalizeRuntimeScopeOptions(options, { defaultUser: shouldDefaultUserInstall(source, options) });
8364
9130
  const targets = await resolveCliTargets(normalizedOptions);
8365
9131
  for (const target of targets) {
@@ -8372,7 +9138,7 @@ program.command("deps").description("inspect the OpenPack dependency graph").add
8372
9138
  for (const decision of result.bundle.graphLock.canonical.overrides) {
8373
9139
  console.log(`OVERRIDE ${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`);
8374
9140
  }
8375
- await rm10(result.bundle.root, { recursive: true, force: true });
9141
+ await rm11(result.bundle.root, { recursive: true, force: true });
8376
9142
  }
8377
9143
  continue;
8378
9144
  }
@@ -8409,6 +9175,29 @@ program.command("registry").description("manage optional registry indexes").addC
8409
9175
  const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
8410
9176
  printRegistryEntries(await client.search(query));
8411
9177
  })
9178
+ ).addCommand(
9179
+ new Command("publish").description("draft a catalogue submission for a public source").argument("<source>", "public resource source or GitHub URL").option("--name <name>", "registry short name").option("--type <type>", "entry type (package, skill, plugin, mcp, or adapter)").option("--description <text>", "short catalogue description").option("--tag <tag>", "search tag (repeatable or comma-separated)", collectTagOption, []).option("--select <type/name>", "selected artifact inside a larger package (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "selected skill inside a larger package (repeatable or comma-separated)", collectSkillOption, []).option("--json", "print only the registry entry JSON", false).action(async (source, options) => {
9180
+ const draft = createRegistryPublishDraft(source, {
9181
+ name: options.name,
9182
+ type: options.type,
9183
+ description: options.description,
9184
+ tags: options.tag,
9185
+ select: options.select,
9186
+ skills: options.skill
9187
+ });
9188
+ if (options.json) {
9189
+ console.log(JSON.stringify(draft.entry, null, 2));
9190
+ return;
9191
+ }
9192
+ console.log("Draft registry entry:");
9193
+ console.log(JSON.stringify(draft.entry, null, 2));
9194
+ console.log("");
9195
+ console.log(`Verify: ${draft.installCommand}`);
9196
+ if (!draft.entry.description) console.log('Tip: add --description "..." or fill the description before submitting.');
9197
+ console.log("");
9198
+ console.log("Submit:");
9199
+ console.log(draft.issueUrl);
9200
+ })
8412
9201
  );
8413
9202
  program.command("trust").description("manage persisted source trust decisions").addCommand(
8414
9203
  new Command("forget").description("forget a persisted trusted source pattern").argument("<pattern>", "trusted source glob to revoke").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (pattern, options) => {
@@ -8493,7 +9282,7 @@ program.command("status").description("show configured packages and runtime inst
8493
9282
  await printStatus(target, normalizedOptions);
8494
9283
  }
8495
9284
  });
8496
- program.command("doctor").description("check agentwheel runtime setup and companion skill guidance").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).action(async (options) => {
9285
+ program.command("doctor").description("check agentwheel runtime setup and companion skill guidance").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--skill <name>", "check a specific skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--source <source>", "source to use in suggested install commands").option("--json", "print machine-readable doctor report", false).action(async (options) => {
8497
9286
  const normalizedOptions = normalizeRuntimeScopeOptions(options);
8498
9287
  const target = await resolveRuntimeTarget({
8499
9288
  targetRoot: normalizedOptions.targetRoot,
@@ -8527,6 +9316,8 @@ async function runInstallCommand(nameOrSource, options, behavior) {
8527
9316
  forceConflict: normalizedOptions.forceConflict,
8528
9317
  replaceConflict: normalizedOptions.replaceConflict,
8529
9318
  noDeps: noDepsFromOptions(normalizedOptions),
9319
+ includeSuggestions: normalizedOptions.withSuggestions,
9320
+ suggestionAliases: suggestionAliasesFromOptions(normalizedOptions),
8530
9321
  lockedResolution: true,
8531
9322
  frozenLock: normalizedOptions.frozenLock,
8532
9323
  offline: normalizedOptions.offline,
@@ -8576,7 +9367,7 @@ async function runInstallCommand(nameOrSource, options, behavior) {
8576
9367
  });
8577
9368
  console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
8578
9369
  }
8579
- await rm10(result.bundle.root, { recursive: true, force: true });
9370
+ await rm11(result.bundle.root, { recursive: true, force: true });
8580
9371
  if (result.plan.hasBlockingChanges) process.exitCode = 1;
8581
9372
  }
8582
9373
  if (behavior.apply && extraPackage && !targetOptions.onlySource) {
@@ -8585,10 +9376,10 @@ async function runInstallCommand(nameOrSource, options, behavior) {
8585
9376
  }
8586
9377
  }
8587
9378
  async function packageEntryFromSource(source, targetRoot, options) {
8588
- const selectedArtifacts = selectedArtifactsFromOptions(options);
8589
9379
  const lockMode = options.frozenLock === true || options.offline === true;
8590
9380
  const resolvedInput = await resolvePackageSource(source, targetRoot, { offline: lockMode });
8591
9381
  const resolvedSource = resolvedInput.source;
9382
+ const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
8592
9383
  const driverName = options.driver ?? inferSourceDriverName(resolvedSource);
8593
9384
  const driver = getSourceDriver(driverName);
8594
9385
  const adapter = await resolveAdapter({
@@ -8602,7 +9393,7 @@ async function packageEntryFromSource(source, targetRoot, options) {
8602
9393
  const bundle = await stageSource(driver, resolvedSource, {
8603
9394
  workspaceRoot: targetRoot,
8604
9395
  adapter,
8605
- cacheRoot: join38(targetRoot, ".agentwheel", "cache"),
9396
+ cacheRoot: join40(targetRoot, ".agentwheel", "cache"),
8606
9397
  mode: options.mode,
8607
9398
  frozenLock: lockMode,
8608
9399
  select: selectedArtifacts
@@ -8621,10 +9412,12 @@ async function packageEntryFromSource(source, targetRoot, options) {
8621
9412
  mode: options.mode ?? "pinned",
8622
9413
  requestedRef: bundle.source.requestedRef,
8623
9414
  select: selectedArtifacts,
9415
+ withSuggestions: options.withSuggestions === true ? true : void 0,
9416
+ suggestions: suggestionAliasesFromOptions(options),
8624
9417
  overrides: overrideArtifactsFromOptions(options)
8625
9418
  };
8626
9419
  } finally {
8627
- await rm10(bundle.root, { recursive: true, force: true });
9420
+ await rm11(bundle.root, { recursive: true, force: true });
8628
9421
  }
8629
9422
  }
8630
9423
  function findConfiguredPackage(packages, value) {
@@ -8765,7 +9558,7 @@ async function runConfiguredGraphPackages(target, options, behavior) {
8765
9558
  });
8766
9559
  console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
8767
9560
  }
8768
- await rm10(result.bundle.root, { recursive: true, force: true });
9561
+ await rm11(result.bundle.root, { recursive: true, force: true });
8769
9562
  if (result.plan.hasBlockingChanges) process.exitCode = 1;
8770
9563
  }
8771
9564
  }
@@ -8830,6 +9623,8 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
8830
9623
  select: selectedArtifacts && packageIsScoped ? selectedArtifacts : normalizeArtifactSelectors(pkg.select, pkg.skills),
8831
9624
  aliases: pkg.aliases,
8832
9625
  overrides: pkg.overrides,
9626
+ includeSuggestions: targetOptions.withSuggestions === true || pkg.withSuggestions === true,
9627
+ suggestionAliases: packageSuggestionAliases(pkg, targetOptions),
8833
9628
  useLock: behavior.mode === "install" ? true : !updateThisPackage
8834
9629
  };
8835
9630
  }),
@@ -8854,6 +9649,8 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
8854
9649
  targetFingerprintParts: targetFingerprintParts(group.target, adapter, group.adapterOptions, group.installationType),
8855
9650
  installationType: group.installationType,
8856
9651
  noDeps: noDepsFromOptions(targetOptions),
9652
+ includeSuggestions: targetOptions.withSuggestions,
9653
+ suggestionAliases: suggestionAliasesFromOptions(targetOptions),
8857
9654
  lockedResolution: behavior.mode === "install",
8858
9655
  frozenLock: targetOptions.frozenLock,
8859
9656
  offline: targetOptions.offline,
@@ -8954,7 +9751,7 @@ function keepManifestEntryOperation(entry, targetRoot, rootId, operation, option
8954
9751
  artifactType: entry.artifactType,
8955
9752
  artifactName: entry.artifactName,
8956
9753
  kind: entry.kind,
8957
- destPath: operation?.destPath ?? join38(targetRoot, entry.path),
9754
+ destPath: operation?.destPath ?? join40(targetRoot, entry.path),
8958
9755
  relativeDestPath: entry.path,
8959
9756
  desiredHash: entry.sourceHash,
8960
9757
  currentHash: operation?.currentHash ?? entry.hash,
@@ -9019,7 +9816,9 @@ async function uninstallConfiguredPackage(target, packageName, options) {
9019
9816
  ref: pkg2.requestedRef,
9020
9817
  select: normalizeArtifactSelectors(pkg2.select, pkg2.skills),
9021
9818
  aliases: pkg2.aliases,
9022
- overrides: pkg2.overrides
9819
+ overrides: pkg2.overrides,
9820
+ includeSuggestions: options.withSuggestions === true || pkg2.withSuggestions === true,
9821
+ suggestionAliases: packageSuggestionAliases(pkg2, options)
9023
9822
  })),
9024
9823
  targetRoot: remainingGroup.target.targetRoot,
9025
9824
  workspaceRoot: remainingGroup.target.workspaceRoot,
@@ -9028,6 +9827,8 @@ async function uninstallConfiguredPackage(target, packageName, options) {
9028
9827
  targetKey: targetKeyForTarget(remainingGroup.target, remainingAdapter.name),
9029
9828
  targetFingerprintParts: targetFingerprintParts(remainingGroup.target, remainingAdapter, remainingGroup.adapterOptions, remainingGroup.installationType),
9030
9829
  installationType: remainingGroup.installationType,
9830
+ includeSuggestions: options.withSuggestions,
9831
+ suggestionAliases: suggestionAliasesFromOptions(options),
9031
9832
  lockedResolution: true,
9032
9833
  frozenLock: options.frozenLock,
9033
9834
  offline: options.offline,
@@ -9065,7 +9866,7 @@ async function uninstallConfiguredPackage(target, packageName, options) {
9065
9866
  if (!options.dryRun) {
9066
9867
  console.log(formatUninstallResult(result));
9067
9868
  }
9068
- if (renderedRoot) await rm10(renderedRoot, { recursive: true, force: true });
9869
+ if (renderedRoot) await rm11(renderedRoot, { recursive: true, force: true });
9069
9870
  if (plan.hasBlockingChanges) process.exitCode = 1;
9070
9871
  }
9071
9872
  }
@@ -9193,7 +9994,7 @@ async function printPendingInstallWork(target, options) {
9193
9994
  const message = error instanceof Error ? error.message : String(error);
9194
9995
  console.log(`Pending install work: unavailable (${message})`);
9195
9996
  } finally {
9196
- await Promise.all(results.map((result) => rm10(result.bundle.root, { recursive: true, force: true })));
9997
+ await Promise.all(results.map((result) => rm11(result.bundle.root, { recursive: true, force: true })));
9197
9998
  }
9198
9999
  }
9199
10000
  async function printDoctor(target, options) {
@@ -9204,33 +10005,113 @@ async function printDoctor(target, options) {
9204
10005
  if (!targetMapping?.enabled) {
9205
10006
  throw new Error(`Adapter ${adapter.name} does not support skills for installation type '${installationType}'.`);
9206
10007
  }
9207
- const installRoot = installRootForAdapterInstallationType(adapter, target.targetRoot, installationType, target.transport === "ssh");
9208
- const companionSkillPath = join38(installRoot, targetMapping.dest, COMPANION_SKILL_NAME);
9209
- const installed = await pathExists(companionSkillPath);
9210
- console.log(`Doctor for ${adapter.name}/${installationType} at ${installRoot}`);
9211
- if (installed) {
9212
- console.log(`Agentwheel companion skill: installed at ${companionSkillPath}`);
10008
+ const transport = transportForTarget(target);
10009
+ const state = installStateForTarget(target, adapter, adapterOptions, installationType);
10010
+ const manifest = await readInstallManifest(state.installRoot, adapter.name, transport, state);
10011
+ const requestedSkills = doctorSkillRequests(target, options);
10012
+ const skills = [];
10013
+ for (const request of requestedSkills) {
10014
+ const skillPath = join40(state.installRoot, targetMapping.dest, request.name);
10015
+ const exists = await pathExists(skillPath);
10016
+ const manifestEntry = manifest?.entries.find((entry) => {
10017
+ if (entry.artifactType !== "skills") return false;
10018
+ const legacyInstallName = "installName" in entry && typeof entry.installName === "string" ? entry.installName : void 0;
10019
+ return entry.artifactName === request.name || legacyInstallName === request.name || entry.path === join40(targetMapping.dest, request.name);
10020
+ });
10021
+ const status = manifestEntry ? "managed" : exists ? "present-unmanaged" : "missing";
10022
+ skills.push({
10023
+ name: request.name,
10024
+ source: request.source,
10025
+ label: request.label,
10026
+ status,
10027
+ path: skillPath,
10028
+ managed: Boolean(manifestEntry),
10029
+ present: exists,
10030
+ suggestedCommands: status === "missing" ? {
10031
+ dryRun: skillInstallCommand(adapter.name, installationType, options, request, { dryRun: true }),
10032
+ apply: skillInstallCommand(adapter.name, installationType, options, request)
10033
+ } : void 0
10034
+ });
10035
+ }
10036
+ const report = {
10037
+ adapter: adapter.name,
10038
+ installationType,
10039
+ targetRoot: target.targetRoot,
10040
+ installRoot: state.installRoot,
10041
+ manifest: manifest ? { entries: manifest.entries.length, revision: manifest.revision } : null,
10042
+ skills
10043
+ };
10044
+ if (options.json) {
10045
+ console.log(JSON.stringify(report, null, 2));
9213
10046
  return;
9214
10047
  }
9215
- console.log(`Agentwheel companion skill: missing at ${companionSkillPath}`);
9216
- console.log("Suggested commands:");
9217
- console.log(` ${companionSkillInstallCommand(adapter.name, installationType, options, { dryRun: true })}`);
9218
- console.log(` ${companionSkillInstallCommand(adapter.name, installationType, options)}`);
10048
+ console.log(`Doctor for ${adapter.name}/${installationType} at ${state.installRoot}`);
10049
+ for (const skill of skills) {
10050
+ const statusLabel = skill.status === "managed" ? "installed" : skill.status === "present-unmanaged" ? "installed (unmanaged)" : "missing";
10051
+ console.log(`${skill.label}: ${statusLabel} at ${skill.path}`);
10052
+ }
10053
+ const missing = skills.filter((skill) => skill.status === "missing");
10054
+ if (missing.length > 0) {
10055
+ console.log("Suggested commands:");
10056
+ for (const skill of missing) {
10057
+ console.log(` ${skill.suggestedCommands?.dryRun}`);
10058
+ console.log(` ${skill.suggestedCommands?.apply}`);
10059
+ }
10060
+ }
10061
+ }
10062
+ function doctorSkillRequests(target, options) {
10063
+ const explicitSkills = normalizeDoctorSkillNames(options.skill ?? []);
10064
+ if (explicitSkills.length > 0) {
10065
+ return explicitSkills.map((name) => ({
10066
+ name,
10067
+ source: options.source ?? defaultSourceForSkill(name),
10068
+ label: doctorSkillLabel(name)
10069
+ }));
10070
+ }
10071
+ const requests = [{
10072
+ name: COMPANION_SKILL_NAME,
10073
+ source: COMPANION_SKILL_SOURCE,
10074
+ label: "Agentwheel companion skill"
10075
+ }];
10076
+ if (isSyncwheelWorkspace(target.targetRoot)) {
10077
+ requests.push({
10078
+ name: "syncwheel",
10079
+ source: "github:NestDevLab/syncwheel",
10080
+ label: "Syncwheel skill"
10081
+ });
10082
+ }
10083
+ return requests;
10084
+ }
10085
+ function normalizeDoctorSkillNames(skills) {
10086
+ return [...new Set(skills.flatMap(splitSelectorList).map((item) => item.trim()).filter(Boolean))];
10087
+ }
10088
+ function defaultSourceForSkill(name) {
10089
+ if (name === COMPANION_SKILL_NAME) return COMPANION_SKILL_SOURCE;
10090
+ if (name === "syncwheel") return "github:NestDevLab/syncwheel";
10091
+ return `github:NestDevLab/${name}`;
9219
10092
  }
9220
- function companionSkillInstallCommand(adapter, installationType, options, behavior = {}) {
10093
+ function doctorSkillLabel(name) {
10094
+ if (name === COMPANION_SKILL_NAME) return "Agentwheel companion skill";
10095
+ if (name === "syncwheel") return "Syncwheel skill";
10096
+ return `${name} skill`;
10097
+ }
10098
+ function isSyncwheelWorkspace(targetRoot) {
10099
+ return existsSync(join40(targetRoot, ".syncwheel", "manifest.json"));
10100
+ }
10101
+ function skillInstallCommand(adapter, installationType, options, skill, behavior = {}) {
9221
10102
  const args = [
9222
10103
  "agentwheel",
9223
10104
  "install",
9224
- COMPANION_SKILL_SOURCE,
10105
+ skill.source,
9225
10106
  "--adapter",
9226
10107
  adapter,
9227
10108
  ...installationTypeCommandArgs(installationType),
9228
10109
  ...targetRootCommandArgs(options),
9229
10110
  "--skill",
9230
- COMPANION_SKILL_NAME
10111
+ skill.name
9231
10112
  ];
9232
10113
  if (behavior.dryRun) args.push("--dry-run");
9233
- return args.map(shellQuoteArg).join(" ");
10114
+ return args.map(shellQuoteArg2).join(" ");
9234
10115
  }
9235
10116
  function installationTypeCommandArgs(installationType) {
9236
10117
  if (installationType === "user") return ["--user"];
@@ -9240,7 +10121,7 @@ function installationTypeCommandArgs(installationType) {
9240
10121
  function targetRootCommandArgs(options) {
9241
10122
  return options.targetRoot && options.installationType !== "user" ? ["--target-root", options.targetRoot] : [];
9242
10123
  }
9243
- function shellQuoteArg(value) {
10124
+ function shellQuoteArg2(value) {
9244
10125
  if (/^[A-Za-z0-9_./:@=-]+$/.test(value)) return value;
9245
10126
  return `'${value.replaceAll("'", `'"'"'`)}'`;
9246
10127
  }
@@ -9250,12 +10131,18 @@ function collectSelectOption(value, previous) {
9250
10131
  function collectSkillOption(value, previous) {
9251
10132
  return [...previous, ...splitSelectorList(value)];
9252
10133
  }
10134
+ function collectSuggestionOption(value, previous) {
10135
+ return [...previous, ...splitSelectorList(value)];
10136
+ }
9253
10137
  function collectTrustOption(value, previous) {
9254
10138
  return [...previous, value];
9255
10139
  }
9256
10140
  function collectOverrideOption(value, previous) {
9257
10141
  return [...previous, ...splitSelectorList(value)];
9258
10142
  }
10143
+ function collectTagOption(value, previous) {
10144
+ return [...previous, ...splitSelectorList(value)];
10145
+ }
9259
10146
  function normalizeRuntimeScopeOptions(options, behavior = {}) {
9260
10147
  if (options.user && options.local) {
9261
10148
  throw new Error("Choose either --user or --local.");
@@ -9292,11 +10179,11 @@ function looksLikeSourceSpecifier(value) {
9292
10179
  }
9293
10180
  function normalizeCliPath(value) {
9294
10181
  if (value === "~") return homedir9();
9295
- if (value.startsWith("~/")) return resolve18(homedir9(), value.slice(2));
9296
- return resolve18(value);
10182
+ if (value.startsWith("~/")) return resolve20(homedir9(), value.slice(2));
10183
+ return resolve20(value);
9297
10184
  }
9298
10185
  function isHomePath(path) {
9299
- return resolve18(path) === resolve18(homedir9());
10186
+ return resolve20(path) === resolve20(homedir9());
9300
10187
  }
9301
10188
  function adapterListFromOption(adapter) {
9302
10189
  if (!adapter) return [];
@@ -9310,6 +10197,18 @@ function adapterListFromOption(adapter) {
9310
10197
  function selectedArtifactsFromOptions(options) {
9311
10198
  return normalizeArtifactSelectors(options.select, options.skills ?? options.skill);
9312
10199
  }
10200
+ function selectedArtifactsFromOptionsOrRegistry(options, registryEntry) {
10201
+ return selectedArtifactsFromOptions(options) ?? selectorsFromRegistryEntry(registryEntry);
10202
+ }
10203
+ function suggestionAliasesFromOptions(options) {
10204
+ const values = options.suggestions ?? options.suggestion;
10205
+ if (!values?.length) return void 0;
10206
+ return [...new Set(values.flatMap(splitSelectorList).map((item) => item.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
10207
+ }
10208
+ function packageSuggestionAliases(pkg, options) {
10209
+ const aliases = [...pkg.suggestions ?? [], ...suggestionAliasesFromOptions(options) ?? []].map((item) => item.trim()).filter(Boolean);
10210
+ return aliases.length > 0 ? [...new Set(aliases)].sort((a, b) => a.localeCompare(b)) : void 0;
10211
+ }
9313
10212
  function overrideArtifactsFromOptions(options) {
9314
10213
  const values = options.overrides ?? options.override;
9315
10214
  return values && values.length > 0 ? values : void 0;
@@ -9339,10 +10238,10 @@ function filterUninstallPlanBySelection(plan, selected) {
9339
10238
  };
9340
10239
  }
9341
10240
  async function initPackage(root) {
9342
- await mkdir17(join38(root, "instructions"), { recursive: true });
9343
- await mkdir17(join38(root, "rules"), { recursive: true });
9344
- await mkdir17(join38(root, "skills"), { recursive: true });
9345
- const manifestPath = join38(root, "openpack.json");
10241
+ await mkdir20(join40(root, "instructions"), { recursive: true });
10242
+ await mkdir20(join40(root, "rules"), { recursive: true });
10243
+ await mkdir20(join40(root, "skills"), { recursive: true });
10244
+ const manifestPath = join40(root, "openpack.json");
9346
10245
  const manifest = {
9347
10246
  schemaVersion: 2,
9348
10247
  name: "example/agentwheel-package",
@@ -9353,12 +10252,12 @@ async function initPackage(root) {
9353
10252
  { type: "skills", path: "skills" }
9354
10253
  ]
9355
10254
  };
9356
- await writeFile16(manifestPath, `${JSON.stringify(manifest, null, 2)}
10255
+ await writeFile19(manifestPath, `${JSON.stringify(manifest, null, 2)}
9357
10256
  `, "utf8");
9358
- await writeFile16(join38(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
10257
+ await writeFile19(join40(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
9359
10258
  }
9360
10259
  async function defaultBootstrapPackage(_root) {
9361
- const packageRoot = await findAgentwheelPackageRoot(dirname25(fileURLToPath3(import.meta.url)));
10260
+ const packageRoot = await findAgentwheelPackageRoot(dirname28(fileURLToPath3(import.meta.url)));
9362
10261
  if (!packageRoot) return void 0;
9363
10262
  return {
9364
10263
  name: "agentwheel",
@@ -9402,10 +10301,10 @@ function withFleetExample(config) {
9402
10301
  };
9403
10302
  }
9404
10303
  async function findAgentwheelPackageRoot(start) {
9405
- let current = resolve18(start);
10304
+ let current = resolve20(start);
9406
10305
  while (true) {
9407
10306
  if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
9408
- const parent = dirname25(current);
10307
+ const parent = dirname28(current);
9409
10308
  if (parent === current) return void 0;
9410
10309
  current = parent;
9411
10310
  }