agentwheel 0.14.3 → 0.14.4

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
@@ -9,9 +9,9 @@ import {
9
9
  } from "./chunk-B3FMBTWC.js";
10
10
 
11
11
  // src/cli/index.ts
12
- import { mkdir as mkdir16, rm as rm9, writeFile as writeFile14 } from "fs/promises";
12
+ import { mkdir as mkdir17, rm as rm10, writeFile as writeFile16 } from "fs/promises";
13
13
  import { homedir as homedir9 } from "os";
14
- import { dirname as dirname23, join as join31, resolve as resolve18 } from "path";
14
+ import { dirname as dirname25, join as join38, resolve as resolve18 } from "path";
15
15
  import { fileURLToPath as fileURLToPath3 } from "url";
16
16
  import { Command } from "commander";
17
17
 
@@ -91,8 +91,19 @@ var targetMappingSchema = z2.object({
91
91
  enabled: z2.boolean().default(true),
92
92
  root: z2.enum(["target", "home"]).optional(),
93
93
  formats: z2.array(z2.string().min(1)).optional(),
94
- semantic: z2.enum(["openclaw-plugin", "codex-subagent", "copilot-instruction", "copilot-prompt", "copilot-agent"]).optional(),
95
- merge: z2.enum(["json-deep", "codex-toml-mcp"]).optional()
94
+ semantic: z2.enum([
95
+ "openclaw-plugin",
96
+ "claude-plugin",
97
+ "codex-plugin",
98
+ "hermes-plugin",
99
+ "copilot-plugin",
100
+ "codex-subagent",
101
+ "copilot-instruction",
102
+ "copilot-prompt",
103
+ "copilot-agent"
104
+ ]).optional(),
105
+ merge: z2.enum(["json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
106
+ mode: z2.enum(["managed-block"]).optional()
96
107
  });
97
108
  var targetRegistrySchema = z2.record(installationTypeSchema, targetMappingSchema);
98
109
  var targetRegistryInputSchema = z2.union([
@@ -203,8 +214,8 @@ var claudeAdapter = {
203
214
  displayName: "Claude Code",
204
215
  targets: {
205
216
  instructions: {
206
- local: { enabled: true, dest: "CLAUDE.md" },
207
- user: { enabled: true, root: "home", dest: ".claude/CLAUDE.md" }
217
+ local: { enabled: true, dest: "CLAUDE.md", mode: "managed-block" },
218
+ user: { enabled: true, root: "home", dest: ".claude/CLAUDE.md", mode: "managed-block" }
208
219
  },
209
220
  rules: {
210
221
  local: { enabled: true, dest: ".claude/rules", formats: ["markdown-rule", "claude-markdown-rule"] },
@@ -222,6 +233,10 @@ var claudeAdapter = {
222
233
  local: { enabled: true, dest: ".claude/agents" },
223
234
  user: { enabled: true, root: "home", dest: ".claude/agents" }
224
235
  },
236
+ plugins: {
237
+ local: { enabled: true, dest: ".agentwheel/plugins/claude", semantic: "claude-plugin" },
238
+ user: { enabled: true, root: "home", dest: ".agentwheel/plugins/claude", semantic: "claude-plugin" }
239
+ },
225
240
  mcp: {
226
241
  local: { enabled: true, dest: ".mcp.json", merge: "json-deep" }
227
242
  },
@@ -242,8 +257,8 @@ var copilotAdapter = {
242
257
  displayName: "GitHub Copilot CLI",
243
258
  targets: {
244
259
  instructions: {
245
- local: { enabled: true, dest: ".github/copilot-instructions.md" },
246
- user: { enabled: true, root: "home", dest: ".copilot/copilot-instructions.md" }
260
+ local: { enabled: true, dest: ".github/copilot-instructions.md", mode: "managed-block" },
261
+ user: { enabled: true, root: "home", dest: ".copilot/copilot-instructions.md", mode: "managed-block" }
247
262
  },
248
263
  rules: {
249
264
  local: { enabled: true, dest: ".github/instructions", formats: ["markdown-rule", "copilot-instruction-rule"], semantic: "copilot-instruction" },
@@ -256,6 +271,9 @@ var copilotAdapter = {
256
271
  local: { enabled: true, dest: ".github/skills" },
257
272
  user: { enabled: true, root: "home", dest: ".copilot/skills" }
258
273
  },
274
+ plugins: {
275
+ user: { enabled: true, root: "home", dest: ".agentwheel/plugins/copilot", semantic: "copilot-plugin" }
276
+ },
259
277
  subagents: {
260
278
  local: { enabled: true, dest: ".github/agents", semantic: "copilot-agent" },
261
279
  user: { enabled: true, root: "home", dest: ".copilot/agents", semantic: "copilot-agent" }
@@ -267,6 +285,10 @@ var copilotAdapter = {
267
285
  hooks: {
268
286
  local: { enabled: true, dest: ".github/hooks" },
269
287
  user: { enabled: true, root: "home", dest: ".copilot/hooks" }
288
+ },
289
+ settings: {
290
+ local: { enabled: true, dest: ".github/settings.json", merge: "json-deep" },
291
+ user: { enabled: true, root: "home", dest: ".copilot/settings.json", merge: "json-deep" }
270
292
  }
271
293
  }
272
294
  };
@@ -277,17 +299,17 @@ var codexAdapter = {
277
299
  displayName: "Codex CLI",
278
300
  targets: {
279
301
  instructions: {
280
- local: { enabled: true, dest: "AGENTS.md" },
281
- user: { enabled: true, root: "home", dest: ".codex/AGENTS.md" }
282
- },
283
- rules: {
284
- local: { enabled: true, dest: ".codex/rules", formats: ["codex-command-policy"] },
285
- user: { enabled: true, root: "home", dest: ".codex/rules", formats: ["codex-command-policy"] }
302
+ local: { enabled: true, dest: "AGENTS.md", mode: "managed-block" },
303
+ user: { enabled: true, root: "home", dest: ".codex/AGENTS.md", mode: "managed-block" }
286
304
  },
287
305
  skills: {
288
306
  local: { enabled: true, dest: ".agents/skills" },
289
307
  user: { enabled: true, root: "home", dest: ".agents/skills" }
290
308
  },
309
+ plugins: {
310
+ local: { enabled: true, dest: ".agentwheel/plugins/codex", semantic: "codex-plugin" },
311
+ user: { enabled: true, root: "home", dest: ".agentwheel/plugins/codex", semantic: "codex-plugin" }
312
+ },
291
313
  subagents: {
292
314
  local: { enabled: true, dest: ".codex/agents", semantic: "codex-subagent" },
293
315
  user: { enabled: true, root: "home", dest: ".codex/agents", semantic: "codex-subagent" }
@@ -309,10 +331,21 @@ var hermesAdapter = {
309
331
  displayName: "Hermes",
310
332
  targets: {
311
333
  instructions: {
312
- local: { enabled: true, dest: "AGENTS.md" }
334
+ local: { enabled: true, dest: "AGENTS.md", mode: "managed-block" },
335
+ // Hermes documents SOUL.md as the user-level instruction surface.
336
+ user: { enabled: true, root: "home", dest: ".hermes/SOUL.md", mode: "managed-block" }
313
337
  },
314
338
  skills: {
315
339
  user: { enabled: true, root: "home", dest: ".hermes/skills" }
340
+ },
341
+ mcp: {
342
+ user: { enabled: true, root: "home", dest: ".hermes/config.yaml", merge: "yaml-deep" }
343
+ },
344
+ settings: {
345
+ user: { enabled: true, root: "home", dest: ".hermes/config.yaml", merge: "yaml-deep" }
346
+ },
347
+ plugins: {
348
+ user: { enabled: true, root: "home", dest: ".agentwheel/plugins/hermes", semantic: "hermes-plugin" }
316
349
  }
317
350
  }
318
351
  };
@@ -322,10 +355,19 @@ var openClawAdapter = {
322
355
  name: "openclaw",
323
356
  displayName: "OpenClaw",
324
357
  targets: {
358
+ instructions: {
359
+ user: { enabled: true, root: "home", dest: ".openclaw/workspace/AGENTS.md", mode: "managed-block" }
360
+ },
325
361
  skills: {
326
362
  local: { enabled: true, dest: "skills" },
327
363
  user: { enabled: true, root: "home", dest: ".openclaw/skills" }
328
364
  },
365
+ mcp: {
366
+ user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "json-deep" }
367
+ },
368
+ settings: {
369
+ user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "json-deep" }
370
+ },
329
371
  plugins: {
330
372
  local: { enabled: true, dest: ".openclaw/plugins", formats: ["openclaw-plugin"], semantic: "openclaw-plugin" }
331
373
  }
@@ -411,9 +453,9 @@ async function resolveAdapter(options) {
411
453
 
412
454
  // src/install/apply.ts
413
455
  import { execFile as execFile3 } from "child_process";
414
- import { mkdtemp, rm as rm3, writeFile as writeFile6 } from "fs/promises";
415
- import { tmpdir as tmpdir2 } from "os";
416
- import { basename as basename2, join as join4 } from "path";
456
+ import { mkdtemp as mkdtemp2, rm as rm4, writeFile as writeFile8 } from "fs/promises";
457
+ import { tmpdir as tmpdir3 } from "os";
458
+ import { basename as basename3, join as join5 } from "path";
417
459
  import { promisify as promisify3 } from "util";
418
460
 
419
461
  // src/model/graph-lock.ts
@@ -835,6 +877,16 @@ import { resolve as resolve3 } from "path";
835
877
  import { z as z4 } from "zod";
836
878
  var dependencyRoleSchema = z4.enum(["root", "direct", "transitive", "fragment"]);
837
879
  var legacyUnownedWorkspaceOwner = "legacy:unowned";
880
+ var semanticPluginSpecSchema = z4.object({
881
+ runtime: z4.enum(["openclaw", "claude", "codex", "copilot", "hermes"]),
882
+ pluginName: z4.string().min(1),
883
+ marketplaceName: z4.string().min(1).optional(),
884
+ stateRoot: z4.string().min(1).optional(),
885
+ installCommands: z4.array(z4.array(z4.string()).min(1)).min(1),
886
+ uninstallCommands: z4.array(z4.array(z4.string()).min(1)).min(1),
887
+ enableCommands: z4.array(z4.array(z4.string()).min(1)).optional(),
888
+ disableCommands: z4.array(z4.array(z4.string()).min(1)).optional()
889
+ });
838
890
  var manifestEntryV1Schema = z4.object({
839
891
  path: z4.string().min(1),
840
892
  artifactType: artifactTypeSchema,
@@ -846,8 +898,10 @@ var manifestEntryV1Schema = z4.object({
846
898
  channel: z4.enum(["managed", "overlay", "addition", "override", "ejected"]).default("managed"),
847
899
  packageName: z4.string().min(1).optional(),
848
900
  semanticCommand: z4.array(z4.string()).optional(),
901
+ semanticPlugin: semanticPluginSpecSchema.optional(),
849
902
  executed: z4.boolean().optional(),
850
- mergeStrategy: z4.enum(["json-deep", "codex-toml-mcp"]).optional(),
903
+ mergeStrategy: z4.enum(["json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
904
+ mode: z4.enum(["managed-block"]).optional(),
851
905
  composedFrom: z4.array(composedFromEntrySchema).optional()
852
906
  });
853
907
  var manifestEntrySchema = manifestEntryV1Schema.extend({
@@ -1245,6 +1299,65 @@ function isRecord2(value) {
1245
1299
  return typeof value === "object" && value !== null && !Array.isArray(value);
1246
1300
  }
1247
1301
 
1302
+ // 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";
1305
+ import { parse as parse2, stringify } from "yaml";
1306
+ async function mergeYamlFile(sourcePath, destPath) {
1307
+ const source = parseYamlValue(await readFile7(sourcePath, "utf8"));
1308
+ const current = await pathExists(destPath) ? parseYamlValue(await readFile7(destPath, "utf8")) : {};
1309
+ const merged = deepMerge2(current, source);
1310
+ await mkdir6(dirname7(destPath), { recursive: true });
1311
+ await writeFile6(destPath, stringify(merged), "utf8");
1312
+ }
1313
+ function parseYamlValue(content) {
1314
+ return normalizeYamlValue(parse2(content));
1315
+ }
1316
+ function normalizeYamlValue(value) {
1317
+ if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
1318
+ return value;
1319
+ }
1320
+ if (Array.isArray(value)) return value.map(normalizeYamlValue);
1321
+ if (isPlainObject(value)) {
1322
+ const out = {};
1323
+ for (const [key, child] of Object.entries(value)) {
1324
+ out[key] = normalizeYamlValue(child);
1325
+ }
1326
+ return out;
1327
+ }
1328
+ return value === void 0 ? null : String(value);
1329
+ }
1330
+ function deepMerge2(base, incoming) {
1331
+ if (Array.isArray(base) && Array.isArray(incoming)) {
1332
+ return dedupeArray2([...base, ...incoming]);
1333
+ }
1334
+ if (isRecord3(base) && isRecord3(incoming)) {
1335
+ const out = { ...base };
1336
+ for (const [key, value] of Object.entries(incoming)) {
1337
+ out[key] = key in out ? deepMerge2(out[key], value) : value;
1338
+ }
1339
+ return out;
1340
+ }
1341
+ return incoming;
1342
+ }
1343
+ function isPlainObject(value) {
1344
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1345
+ }
1346
+ function isRecord3(value) {
1347
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1348
+ }
1349
+ function dedupeArray2(values) {
1350
+ const seen = /* @__PURE__ */ new Set();
1351
+ const out = [];
1352
+ for (const value of values) {
1353
+ const key = JSON.stringify(value);
1354
+ if (seen.has(key)) continue;
1355
+ seen.add(key);
1356
+ out.push(value);
1357
+ }
1358
+ return out;
1359
+ }
1360
+
1248
1361
  // src/install/desired.ts
1249
1362
  function normalizeOwners(owners) {
1250
1363
  const normalized = [...new Set(owners.map((owner) => owner.trim()).filter(Boolean))].sort();
@@ -1254,6 +1367,189 @@ function normalizeOwners(owners) {
1254
1367
  return normalized;
1255
1368
  }
1256
1369
 
1370
+ // src/install/instructions-block.ts
1371
+ import { createHash as createHash3 } from "crypto";
1372
+ import { mkdtemp, readFile as readFile8, realpath, rm as rm3, writeFile as writeFile7 } from "fs/promises";
1373
+ import { tmpdir as tmpdir2 } from "os";
1374
+ import { basename as basename2, dirname as dirname8, join as join4, relative as relative2 } from "path";
1375
+ var managedInstructionBlockMode = "managed-block";
1376
+ var managedInstructionBanner = "<!-- agentwheel-managed: edit fragments, not this block -->";
1377
+ async function desiredManagedInstructionBlockHash(sourcePath) {
1378
+ const source = await readFile8(sourcePath, "utf8");
1379
+ return hashText(managedBlockBody(source));
1380
+ }
1381
+ async function readManagedInstructionBlockState(destPath, selector, transport) {
1382
+ if (!await transport.pathExists(destPath)) {
1383
+ return { exists: false, hasBlock: false, drifted: false };
1384
+ }
1385
+ const content = await transport.readFile(destPath);
1386
+ const block = findManagedInstructionBlock(content, selector);
1387
+ if (!block) return { exists: true, hasBlock: false, drifted: false };
1388
+ const hash = hashText(block.body);
1389
+ return {
1390
+ exists: true,
1391
+ hasBlock: true,
1392
+ hash,
1393
+ markerHash: block.markerHash,
1394
+ drifted: hash !== block.markerHash
1395
+ };
1396
+ }
1397
+ async function writeManagedInstructionBlock(sourcePath, destPath, selector, transport, expectedHash) {
1398
+ const source = await readFile8(sourcePath, "utf8");
1399
+ const desired = renderManagedInstructionBlock(selector, source);
1400
+ const existing = await readOptionalText(destPath, transport);
1401
+ const merged = upsertManagedInstructionBlock(existing ?? "", selector, desired.block, expectedHash);
1402
+ await writeTextWithTransport(destPath, merged, transport);
1403
+ return desired.hash;
1404
+ }
1405
+ async function removeManagedInstructionBlock(destPath, selector, transport, expectedHash) {
1406
+ if (!await transport.pathExists(destPath)) return;
1407
+ const existing = await transport.readFile(destPath);
1408
+ const updated = removeManagedBlockFromContent(existing, selector, expectedHash);
1409
+ await writeTextWithTransport(destPath, updated, transport);
1410
+ }
1411
+ async function managedInstructionBlockLanded(destPath, selector, expectedHash, transport) {
1412
+ if (!expectedHash) return false;
1413
+ const state = await readManagedInstructionBlockState(destPath, selector, transport);
1414
+ return state.exists && state.hasBlock && !state.drifted && state.hash === expectedHash;
1415
+ }
1416
+ async function managedInstructionPhysicalKey(destPath, transport) {
1417
+ if (transport.kind !== "local" || !await transport.pathExists(destPath)) return destPath;
1418
+ return realpath(destPath);
1419
+ }
1420
+ async function claudeInstructionBridgesAgents(claudePath, agentsPath, transport) {
1421
+ if (!await transport.pathExists(claudePath)) return false;
1422
+ if (await samePhysicalPath(claudePath, agentsPath, transport)) return true;
1423
+ const claudeContent = await transport.readFile(claudePath);
1424
+ return referencesAgentsMd(claudeContent, claudePath, agentsPath);
1425
+ }
1426
+ function managedInstructionSelector(selector, artifactType, artifactName) {
1427
+ if (!selector) return `${artifactType}/${artifactName}`;
1428
+ const unscoped = selector.split(":").at(-1);
1429
+ return unscoped?.includes("/") ? unscoped : selector;
1430
+ }
1431
+ function renderManagedInstructionBlock(selector, source) {
1432
+ const body = managedBlockBody(source);
1433
+ const hash = hashText(body);
1434
+ return {
1435
+ block: `<!-- BEGIN openpack:include ${selector} sha256:${hash} -->
1436
+ ${body}<!-- END openpack:include ${selector} -->
1437
+ `,
1438
+ hash
1439
+ };
1440
+ }
1441
+ function upsertManagedInstructionBlock(content, selector, block, expectedHash) {
1442
+ const existing = findManagedInstructionBlock(content, selector);
1443
+ if (!existing) {
1444
+ if (expectedHash) throw new Error(`Managed instruction block missing for ${selector}`);
1445
+ return appendManagedInstructionBlock(content, block);
1446
+ }
1447
+ assertCleanBlock(existing, selector);
1448
+ if (expectedHash && hashText(existing.body) !== expectedHash) {
1449
+ throw new Error(`Managed instruction block drift detected for ${selector}`);
1450
+ }
1451
+ return `${content.slice(0, existing.start)}${block}${content.slice(existing.end)}`;
1452
+ }
1453
+ function removeManagedBlockFromContent(content, selector, expectedHash) {
1454
+ const existing = findManagedInstructionBlock(content, selector);
1455
+ 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}`);
1459
+ }
1460
+ return `${content.slice(0, existing.start)}${content.slice(existing.end)}`;
1461
+ }
1462
+ function findManagedInstructionBlock(content, selector) {
1463
+ const beginPattern = new RegExp(`<!-- BEGIN openpack:include ${escapeRegex(selector)} sha256:([a-f0-9]+) -->\\r?\\n?`);
1464
+ const begin = beginPattern.exec(content);
1465
+ if (!begin || begin.index === void 0) return void 0;
1466
+ const markerHash = begin[1];
1467
+ const bodyStart = begin.index + begin[0].length;
1468
+ const endMarker = `<!-- END openpack:include ${selector} -->`;
1469
+ const endIndex = content.indexOf(endMarker, bodyStart);
1470
+ if (endIndex < 0) {
1471
+ return {
1472
+ start: begin.index,
1473
+ end: content.length,
1474
+ body: content.slice(bodyStart),
1475
+ markerHash
1476
+ };
1477
+ }
1478
+ let blockEnd = endIndex + endMarker.length;
1479
+ if (content[blockEnd] === "\r" && content[blockEnd + 1] === "\n") blockEnd += 2;
1480
+ else if (content[blockEnd] === "\n") blockEnd += 1;
1481
+ return {
1482
+ start: begin.index,
1483
+ end: blockEnd,
1484
+ body: content.slice(bodyStart, endIndex),
1485
+ markerHash
1486
+ };
1487
+ }
1488
+ function managedBlockBody(source) {
1489
+ return `${managedInstructionBanner}
1490
+ ${ensureTrailingNewline(source)}`;
1491
+ }
1492
+ function appendManagedInstructionBlock(content, block) {
1493
+ if (content.length === 0) return block;
1494
+ const separator = content.endsWith("\n\n") ? "" : content.endsWith("\n") ? "\n" : "\n\n";
1495
+ return `${content}${separator}${block}`;
1496
+ }
1497
+ function assertCleanBlock(block, selector) {
1498
+ const actual = hashText(block.body);
1499
+ if (actual !== block.markerHash) {
1500
+ throw new Error(`Managed instruction block drift detected for ${selector}`);
1501
+ }
1502
+ }
1503
+ function referencesAgentsMd(content, claudePath, agentsPath) {
1504
+ const claudeDir = dirname8(claudePath);
1505
+ for (const rawLine of content.split(/\r?\n/)) {
1506
+ const line = rawLine.trim();
1507
+ const atImport = /^@import\s+(.+)$/i.exec(line);
1508
+ const atPath = /^@(.+AGENTS\.md)$/i.exec(line);
1509
+ const referenced = atImport?.[1] ?? atPath?.[1];
1510
+ if (!referenced) continue;
1511
+ const cleaned = referenced.trim().replace(/^["']|["']$/g, "");
1512
+ if (!/AGENTS\.md$/i.test(cleaned)) continue;
1513
+ const candidate = cleaned.startsWith("/") ? cleaned : join4(claudeDir, cleaned);
1514
+ if (relative2(dirname8(agentsPath), candidate).replaceAll("\\", "/") === "AGENTS.md") return true;
1515
+ if (candidate === agentsPath) return true;
1516
+ }
1517
+ return false;
1518
+ }
1519
+ async function samePhysicalPath(left, right, transport) {
1520
+ if (transport.kind !== "local") return false;
1521
+ if (!await transport.pathExists(left) || !await transport.pathExists(right)) return false;
1522
+ return await realpath(left) === await realpath(right);
1523
+ }
1524
+ async function readOptionalText(path, transport) {
1525
+ if (!await transport.pathExists(path)) return void 0;
1526
+ return transport.readFile(path);
1527
+ }
1528
+ async function writeTextWithTransport(path, content, transport) {
1529
+ if (transport.kind === "local") {
1530
+ await transport.writeFileAtomic(path, content);
1531
+ return;
1532
+ }
1533
+ const tempRoot = await mkdtemp(join4(tmpdir2(), "agentwheel-instructions-"));
1534
+ const localPath = join4(tempRoot, basename2(path) || "instructions.md");
1535
+ try {
1536
+ await writeFile7(localPath, content, "utf8");
1537
+ await transport.atomicCopy(localPath, path, "file");
1538
+ } finally {
1539
+ await rm3(tempRoot, { recursive: true, force: true });
1540
+ }
1541
+ }
1542
+ function ensureTrailingNewline(content) {
1543
+ return content.endsWith("\n") ? content : `${content}
1544
+ `;
1545
+ }
1546
+ function hashText(content) {
1547
+ return createHash3("sha256").update(content).digest("hex");
1548
+ }
1549
+ function escapeRegex(value) {
1550
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1551
+ }
1552
+
1257
1553
  // src/install/apply.ts
1258
1554
  var execFileAsync3 = promisify3(execFile3);
1259
1555
  async function applyCombinedInstallPlan(plan, options = {}) {
@@ -1264,7 +1560,7 @@ async function recoverPendingApply(targetRoot, adapter, transport = localTranspo
1264
1560
  try {
1265
1561
  const journal = await readApplyJournal(targetRoot, adapter, transport, scope);
1266
1562
  if (!journal) return void 0;
1267
- if (journal.operations.some((operation) => operation.action === "plugin" || operation.action === "program")) {
1563
+ if (journal.operations.some((operation) => operation.action === "plugin" || operation.action === "program" || operation.semanticPlugin)) {
1268
1564
  throw new Error("Cannot automatically recover a journal containing semantic plugin or programmatic operations");
1269
1565
  }
1270
1566
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1475,7 +1771,7 @@ async function commitJournalState(journal, transport, entries, now) {
1475
1771
  await writeInstallManifest(manifest, transport);
1476
1772
  }
1477
1773
  if (journal.graphLockPath && journal.graphLock) await writeGraphLock(journal.graphLockPath, journal.graphLock);
1478
- if (journal.graphLockRemovePath) await rm3(journal.graphLockRemovePath, { force: true });
1774
+ if (journal.graphLockRemovePath) await rm4(journal.graphLockRemovePath, { force: true });
1479
1775
  if (journal.workspaceConfigPath && journal.workspaceConfig) await writeJsonAtomic(journal.workspaceConfigPath, journal.workspaceConfig);
1480
1776
  await removeApplyJournal(journal.targetRoot, journal.adapter, transport, {
1481
1777
  installationType: journal.installationType,
@@ -1490,9 +1786,6 @@ async function applyOperation(operation, context) {
1490
1786
  throw new Error(`Invalid plugin operation missing hash: ${operation.relativeDestPath}`);
1491
1787
  }
1492
1788
  if (context.executePlugins) {
1493
- if (!operation.semanticCommand || operation.semanticCommand.length === 0) {
1494
- throw new Error(`Invalid plugin operation missing command: ${operation.relativeDestPath}`);
1495
- }
1496
1789
  await executePluginInstall(operation, transport);
1497
1790
  }
1498
1791
  return manifestEntryForOperation(operation, {
@@ -1527,10 +1820,27 @@ async function applyOperation(operation, context) {
1527
1820
  if (!operation.sourcePath || !operation.desiredHash) {
1528
1821
  throw new Error(`Invalid operation missing source/hash: ${operation.relativeDestPath}`);
1529
1822
  }
1823
+ if (operation.mode === managedInstructionBlockMode) {
1824
+ const selector = managedInstructionSelector(operation.logicalSelector, operation.artifactType, operation.artifactName);
1825
+ const hash2 = await writeManagedInstructionBlock(operation.sourcePath, operation.destPath, selector, transport, operation.manifestHash);
1826
+ if (hash2 !== operation.desiredHash) {
1827
+ throw new Error(`Managed block hash verification failed for ${operation.relativeDestPath}: expected ${operation.desiredHash}, got ${hash2}`);
1828
+ }
1829
+ return manifestEntryForOperation(operation, {
1830
+ now,
1831
+ hash: hash2,
1832
+ sourceHash: operation.desiredHash,
1833
+ graphLockDigest: context.graphLockDigest
1834
+ });
1835
+ }
1530
1836
  if (operation.mergeStrategy === "json-deep") {
1531
1837
  await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeJsonFile);
1838
+ } else if (operation.mergeStrategy === "yaml-deep") {
1839
+ await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeYamlFile);
1532
1840
  } else if (operation.mergeStrategy === "codex-toml-mcp") {
1533
1841
  await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeCodexTomlMcp);
1842
+ } else if (operation.mergeStrategy) {
1843
+ throw new Error(`Merge strategy '${operation.mergeStrategy}' is not implemented by apply.`);
1534
1844
  } else {
1535
1845
  await transport.atomicCopy(operation.sourcePath, operation.destPath, operation.kind);
1536
1846
  }
@@ -1551,7 +1861,7 @@ async function applyOperation(operation, context) {
1551
1861
  }
1552
1862
  return manifestEntryForOperation(operation, {
1553
1863
  now,
1554
- hash: operation.mergeStrategy && operation.currentHash ? operation.currentHash : operation.desiredHash,
1864
+ hash: (operation.mergeStrategy || operation.mode === managedInstructionBlockMode) && operation.currentHash ? operation.currentHash : operation.desiredHash,
1555
1865
  sourceHash: operation.desiredHash,
1556
1866
  graphLockDigest: context.graphLockDigest
1557
1867
  });
@@ -1568,40 +1878,198 @@ async function applyOperation(operation, context) {
1568
1878
  });
1569
1879
  }
1570
1880
  if (operation.action === "remove") {
1571
- await transport.rm(operation.destPath);
1881
+ if (operation.semanticPlugin) {
1882
+ if (operation.execute !== false) await executePluginUninstall(operation, transport);
1883
+ return void 0;
1884
+ }
1885
+ if (operation.mode === managedInstructionBlockMode) {
1886
+ const selector = managedInstructionSelector(operation.logicalSelector, operation.artifactType, operation.artifactName);
1887
+ await removeManagedInstructionBlock(operation.destPath, selector, transport, operation.manifestHash);
1888
+ } else {
1889
+ await transport.rm(operation.destPath);
1890
+ }
1572
1891
  return void 0;
1573
1892
  }
1574
1893
  return void 0;
1575
1894
  }
1576
1895
  async function executePluginInstall(operation, transport) {
1577
- const command = operation.semanticCommand?.[0];
1578
- const args = operation.semanticCommand?.slice(1) ?? [];
1579
- if (!command) {
1580
- throw new Error(`Invalid plugin operation missing command: ${operation.relativeDestPath}`);
1581
- }
1896
+ const commands = semanticInstallCommands(operation);
1897
+ if (commands.length === 0) throw new Error(`Invalid plugin operation missing command: ${operation.relativeDestPath}`);
1582
1898
  if (!operation.sourcePath) {
1583
1899
  throw new Error(`Invalid plugin operation missing source path: ${operation.relativeDestPath}`);
1584
1900
  }
1901
+ if (operation.semanticPlugin?.stateRoot) {
1902
+ await prepareSemanticPluginState(operation, transport);
1903
+ }
1904
+ await executeSemanticCommands(operation, commands, transport, { stageSource: operation.semanticPlugin?.stateRoot ? false : true });
1905
+ }
1906
+ async function executePluginUninstall(operation, transport) {
1907
+ const commands = operation.semanticPlugin?.uninstallCommands ?? [];
1908
+ if (commands.length === 0) throw new Error(`Invalid semantic plugin operation missing uninstall command: ${operation.relativeDestPath}`);
1909
+ await executeSemanticCommands(operation, commands, transport);
1910
+ if (operation.semanticPlugin?.stateRoot) {
1911
+ await transport.rm(operation.semanticPlugin.stateRoot);
1912
+ }
1913
+ }
1914
+ async function prepareSemanticPluginState(operation, transport) {
1915
+ const spec = operation.semanticPlugin;
1916
+ if (!spec?.stateRoot) return;
1917
+ if (!operation.sourcePath) {
1918
+ throw new Error(`Invalid semantic plugin operation missing source path: ${operation.relativeDestPath}`);
1919
+ }
1920
+ if (spec.runtime === "claude") {
1921
+ await prepareClaudeMarketplace(operation, transport);
1922
+ return;
1923
+ }
1924
+ if (spec.runtime === "codex") {
1925
+ await prepareCodexMarketplace(operation, transport);
1926
+ return;
1927
+ }
1928
+ if (spec.runtime === "copilot") {
1929
+ await prepareCopilotPlugin(operation, transport);
1930
+ return;
1931
+ }
1932
+ if (spec.runtime === "hermes") {
1933
+ await prepareHermesGitShim(operation, transport);
1934
+ }
1935
+ }
1936
+ async function prepareClaudeMarketplace(operation, transport) {
1937
+ const spec = requireSemanticPluginState(operation);
1938
+ const marketplaceName = requireMarketplaceName(operation);
1939
+ const marketplaceRoot = join5(spec.stateRoot, "marketplace");
1940
+ await transport.rm(spec.stateRoot);
1941
+ await transport.atomicCopy(requireSourcePath(operation), join5(marketplaceRoot, "plugins", spec.pluginName), operation.kind);
1942
+ await transport.writeJsonAtomic(join5(marketplaceRoot, ".claude-plugin", "marketplace.json"), {
1943
+ name: marketplaceName,
1944
+ owner: { name: "Agentwheel" },
1945
+ plugins: [
1946
+ {
1947
+ name: spec.pluginName,
1948
+ source: `./plugins/${spec.pluginName}`,
1949
+ description: `Installed by Agentwheel${operation.packageName ? ` from ${operation.packageName}` : ""}`
1950
+ }
1951
+ ]
1952
+ });
1953
+ }
1954
+ async function prepareCodexMarketplace(operation, transport) {
1955
+ const spec = requireSemanticPluginState(operation);
1956
+ const marketplaceName = requireMarketplaceName(operation);
1957
+ const marketplaceRoot = join5(spec.stateRoot, "marketplace");
1958
+ await transport.rm(spec.stateRoot);
1959
+ await transport.atomicCopy(requireSourcePath(operation), join5(marketplaceRoot, "plugins", spec.pluginName), operation.kind);
1960
+ await transport.writeJsonAtomic(join5(marketplaceRoot, ".agents", "plugins", "marketplace.json"), {
1961
+ name: marketplaceName,
1962
+ interface: {
1963
+ displayName: "Agentwheel"
1964
+ },
1965
+ plugins: [
1966
+ {
1967
+ name: spec.pluginName,
1968
+ source: {
1969
+ source: "local",
1970
+ path: `./plugins/${spec.pluginName}`
1971
+ },
1972
+ policy: {
1973
+ installation: "AVAILABLE",
1974
+ authentication: "ON_INSTALL"
1975
+ },
1976
+ category: "Agentwheel"
1977
+ }
1978
+ ]
1979
+ });
1980
+ }
1981
+ async function prepareCopilotPlugin(operation, transport) {
1982
+ const spec = requireSemanticPluginState(operation);
1983
+ await transport.rm(spec.stateRoot);
1984
+ await transport.atomicCopy(requireSourcePath(operation), join5(spec.stateRoot, "plugin"), operation.kind);
1985
+ }
1986
+ async function prepareHermesGitShim(operation, transport) {
1987
+ const spec = requireSemanticPluginState(operation);
1988
+ const repoRoot = join5(spec.stateRoot, "repo");
1989
+ await transport.rm(spec.stateRoot);
1990
+ await transport.atomicCopy(requireSourcePath(operation), repoRoot, operation.kind);
1991
+ if (!transport.execFile) {
1992
+ throw new Error(`Cannot prepare Hermes plugin git shim over ${transport.description}: transport does not support remote commands.`);
1993
+ }
1994
+ await transport.execFile("git", ["init", repoRoot], { cwd: operation.destPath });
1995
+ await transport.execFile("git", ["-C", repoRoot, "add", "-A"], { cwd: operation.destPath });
1996
+ await transport.execFile("git", [
1997
+ "-C",
1998
+ repoRoot,
1999
+ "-c",
2000
+ "user.name=agentwheel",
2001
+ "-c",
2002
+ "user.email=agentwheel@example.invalid",
2003
+ "commit",
2004
+ "-m",
2005
+ `agentwheel plugin ${operation.desiredHash ?? "unknown"}`
2006
+ ], { cwd: operation.destPath });
2007
+ }
2008
+ function requireSemanticPluginState(operation) {
2009
+ const spec = operation.semanticPlugin;
2010
+ if (!spec?.stateRoot) throw new Error(`Invalid semantic plugin operation missing state root: ${operation.relativeDestPath}`);
2011
+ return spec;
2012
+ }
2013
+ function requireSourcePath(operation) {
2014
+ if (!operation.sourcePath) throw new Error(`Invalid semantic plugin operation missing source path: ${operation.relativeDestPath}`);
2015
+ return operation.sourcePath;
2016
+ }
2017
+ function requireMarketplaceName(operation) {
2018
+ const marketplaceName = operation.semanticPlugin?.marketplaceName;
2019
+ if (!marketplaceName) throw new Error(`Invalid semantic plugin operation missing marketplace name: ${operation.relativeDestPath}`);
2020
+ return marketplaceName;
2021
+ }
2022
+ async function executeSemanticCommands(operation, commands, transport, options = {}) {
1585
2023
  if (transport.kind === "local") {
1586
- await execFileAsync3(command, args);
2024
+ for (const [command, ...args] of commands) {
2025
+ if (!command) throw new Error(`Invalid semantic plugin command for ${operation.relativeDestPath}`);
2026
+ await execFileAsync3(command, args);
2027
+ }
1587
2028
  return;
1588
2029
  }
1589
2030
  if (!transport.execFile) {
1590
- throw new Error(`Cannot execute semantic plugin install over ${transport.description}: transport does not support remote commands.`);
2031
+ throw new Error(`Cannot execute semantic plugin command over ${transport.description}: transport does not support remote commands.`);
2032
+ }
2033
+ if (!options.stageSource) {
2034
+ for (const [command, ...args] of commands) {
2035
+ if (!command) throw new Error(`Invalid semantic plugin command for ${operation.relativeDestPath}`);
2036
+ await transport.execFile(command, args, { cwd: operation.destPath });
2037
+ }
2038
+ return;
2039
+ }
2040
+ if (!operation.sourcePath) {
2041
+ throw new Error(`Invalid semantic plugin operation missing source path: ${operation.relativeDestPath}`);
1591
2042
  }
1592
- const stagingRoot = join4(operation.destPath, ".agentwheel", "plugin-staging", `${process.pid}-${Date.now()}`);
1593
- const remoteSourcePath = join4(stagingRoot, basename2(operation.sourcePath));
2043
+ const stagingRoot = join5(operation.destPath, ".agentwheel", "plugin-staging", `${process.pid}-${Date.now()}`);
2044
+ const remoteSourcePath = join5(stagingRoot, basename3(operation.sourcePath));
1594
2045
  try {
1595
2046
  await transport.atomicCopy(operation.sourcePath, remoteSourcePath, operation.kind);
1596
- const remoteArgs = args.map((arg) => arg === operation.sourcePath ? remoteSourcePath : arg);
1597
- await transport.execFile(command, remoteArgs, { cwd: operation.destPath });
2047
+ for (const [command, ...args] of commands) {
2048
+ if (!command) throw new Error(`Invalid semantic plugin command for ${operation.relativeDestPath}`);
2049
+ const remoteArgs = args.map((arg) => arg === operation.sourcePath ? remoteSourcePath : arg);
2050
+ await transport.execFile(command, remoteArgs, { cwd: operation.destPath });
2051
+ }
1598
2052
  } finally {
1599
2053
  await transport.rm(stagingRoot);
1600
2054
  }
1601
2055
  }
2056
+ function semanticInstallCommands(operation) {
2057
+ if (operation.semanticPlugin) return operation.semanticPlugin.installCommands;
2058
+ return operation.semanticCommand ? [operation.semanticCommand] : [];
2059
+ }
1602
2060
  async function entryForCompletedOperation(operation, transport, now, graphLockDigest) {
1603
2061
  if (operation.action === "remove") return void 0;
1604
2062
  if (operation.action === "create" || operation.action === "update") {
2063
+ if (operation.mode === managedInstructionBlockMode) {
2064
+ const selector = managedInstructionSelector(operation.logicalSelector, operation.artifactType, operation.artifactName);
2065
+ const state = await readManagedInstructionBlockState(operation.destPath, selector, transport);
2066
+ return manifestEntryForOperation(operation, {
2067
+ now,
2068
+ hash: state.hash ?? requireDesiredHash(operation),
2069
+ sourceHash: requireDesiredHash(operation),
2070
+ graphLockDigest
2071
+ });
2072
+ }
1605
2073
  return manifestEntryForOperation(operation, {
1606
2074
  now,
1607
2075
  hash: await transport.hashPath(operation.destPath),
@@ -1612,7 +2080,7 @@ async function entryForCompletedOperation(operation, transport, now, graphLockDi
1612
2080
  if (operation.action === "skip") {
1613
2081
  return manifestEntryForOperation(operation, {
1614
2082
  now,
1615
- hash: operation.mergeStrategy && operation.currentHash ? operation.currentHash : requireDesiredHash(operation),
2083
+ hash: (operation.mergeStrategy || operation.mode === managedInstructionBlockMode) && operation.currentHash ? operation.currentHash : requireDesiredHash(operation),
1616
2084
  sourceHash: requireDesiredHash(operation),
1617
2085
  graphLockDigest
1618
2086
  });
@@ -1639,8 +2107,10 @@ function manifestEntryForOperation(operation, values) {
1639
2107
  channel: operation.channel,
1640
2108
  packageName: operation.packageName,
1641
2109
  semanticCommand: operation.semanticCommand,
2110
+ semanticPlugin: operation.semanticPlugin,
1642
2111
  executed: values.executed ?? operation.execute,
1643
2112
  mergeStrategy: operation.mergeStrategy,
2113
+ mode: operation.mode,
1644
2114
  composedFrom: operation.composedFrom,
1645
2115
  graphLockDigest: operation.graphLockDigest ?? values.graphLockDigest
1646
2116
  };
@@ -1656,15 +2126,27 @@ async function assertBaseRevision(plan, transport) {
1656
2126
  }
1657
2127
  }
1658
2128
  function isJournaledMutation(operation) {
2129
+ if (operation.semanticPlugin && (operation.action === "plugin" || operation.action === "remove")) return false;
1659
2130
  return operation.action === "create" || operation.action === "update" || operation.action === "remove";
1660
2131
  }
1661
2132
  function operationNeedsSource(operation) {
1662
2133
  return operation.action === "create" || operation.action === "update";
1663
2134
  }
1664
2135
  async function operationLanded(operation, transport) {
1665
- if (operation.action === "remove") return !await transport.pathExists(operation.destPath);
2136
+ if (operation.action === "remove") {
2137
+ if (operation.mode === managedInstructionBlockMode) {
2138
+ const selector = managedInstructionSelector(operation.logicalSelector, operation.artifactType, operation.artifactName);
2139
+ const state = await readManagedInstructionBlockState(operation.destPath, selector, transport);
2140
+ return !state.exists || !state.hasBlock;
2141
+ }
2142
+ return !await transport.pathExists(operation.destPath);
2143
+ }
1666
2144
  if (operation.action !== "create" && operation.action !== "update") return false;
1667
2145
  if (!await transport.pathExists(operation.destPath)) return false;
2146
+ if (operation.mode === managedInstructionBlockMode) {
2147
+ const selector = managedInstructionSelector(operation.logicalSelector, operation.artifactType, operation.artifactName);
2148
+ return managedInstructionBlockLanded(operation.destPath, selector, operation.desiredHash, transport);
2149
+ }
1668
2150
  if (operation.mergeStrategy) return true;
1669
2151
  if (!operation.desiredHash) return false;
1670
2152
  return await transport.hashPath(operation.destPath) === operation.desiredHash;
@@ -1684,25 +2166,25 @@ async function mergeWithTransport(sourcePath, destPath, transport, merge) {
1684
2166
  await merge(sourcePath, destPath);
1685
2167
  return;
1686
2168
  }
1687
- const tempRoot = await mkdtemp(join4(tmpdir2(), "agentwheel-merge-"));
1688
- const localDest = join4(tempRoot, basename2(destPath) || "merged");
2169
+ const tempRoot = await mkdtemp2(join5(tmpdir3(), "agentwheel-merge-"));
2170
+ const localDest = join5(tempRoot, basename3(destPath) || "merged");
1689
2171
  try {
1690
2172
  if (await transport.pathExists(destPath)) {
1691
- await writeFile6(localDest, await transport.readFile(destPath), "utf8");
2173
+ await writeFile8(localDest, await transport.readFile(destPath), "utf8");
1692
2174
  }
1693
2175
  await merge(sourcePath, localDest);
1694
2176
  await transport.atomicCopy(localDest, destPath, "file");
1695
2177
  } finally {
1696
- await rm3(tempRoot, { recursive: true, force: true });
2178
+ await rm4(tempRoot, { recursive: true, force: true });
1697
2179
  }
1698
2180
  }
1699
2181
 
1700
2182
  // src/install/plan.ts
1701
- import { join as join8, relative as relative2 } from "path";
2183
+ import { basename as basename7, join as join15, relative as relative3 } from "path";
1702
2184
 
1703
2185
  // src/staging/codex-subagents.ts
1704
- import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
1705
- import { basename as basename3, dirname as dirname7, join as join5 } from "path";
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";
1706
2188
  var requiredCodexAgentFields = ["name", "description", "developer_instructions"];
1707
2189
  async function renderCodexSubagents(artifacts, stageRoot, adapter) {
1708
2190
  if (adapter?.name !== "codex") return artifacts;
@@ -1725,42 +2207,42 @@ async function renderCodexSubagents(artifacts, stageRoot, adapter) {
1725
2207
  async function renderCodexSubagent(artifact, stageRoot) {
1726
2208
  const sourcePath = artifact.stagedPath ?? artifact.sourcePath;
1727
2209
  const agentName = codexAgentName(artifact);
1728
- const renderedPath = join5(stageRoot, ".agentwheel-rendered", "codex-subagents", `${agentName}.toml`);
2210
+ const renderedPath = join6(stageRoot, ".agentwheel-rendered", "codex-subagents", `${agentName}.toml`);
1729
2211
  const lowerSourcePath = sourcePath.toLowerCase();
1730
2212
  if (artifact.kind === "file" && (artifact.name.toLowerCase().endsWith(".toml") || lowerSourcePath.endsWith(".toml"))) {
1731
- const content = await readFile7(sourcePath, "utf8");
2213
+ const content = await readFile9(sourcePath, "utf8");
1732
2214
  validateCodexAgentToml(content, sourcePath);
1733
2215
  return {
1734
2216
  ...artifact,
1735
2217
  name: agentName,
1736
- relativePath: join5("subagents", `${agentName}.toml`),
2218
+ relativePath: join6("subagents", `${agentName}.toml`),
1737
2219
  kind: "file",
1738
2220
  hash: await hashPath(sourcePath)
1739
2221
  };
1740
2222
  }
1741
- const markdownPath = artifact.kind === "dir" ? join5(sourcePath, "AGENTS.md") : sourcePath;
2223
+ const markdownPath = artifact.kind === "dir" ? join6(sourcePath, "AGENTS.md") : sourcePath;
1742
2224
  if (artifact.kind === "dir" && !await pathExists(markdownPath)) {
1743
2225
  throw new Error(`Codex subagent directory ${artifact.relativePath} must contain AGENTS.md.`);
1744
2226
  }
1745
2227
  if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md") && !lowerSourcePath.endsWith(".md")) {
1746
2228
  throw new Error(`Codex subagent ${artifact.relativePath} must be a .toml file, .md file, or directory containing AGENTS.md.`);
1747
2229
  }
1748
- const markdown = await readFile7(markdownPath, "utf8");
2230
+ const markdown = await readFile9(markdownPath, "utf8");
1749
2231
  const toml = markdownToCodexAgentToml(agentName, markdown);
1750
- await mkdir6(dirname7(renderedPath), { recursive: true });
1751
- await writeFile7(renderedPath, toml, "utf8");
2232
+ await mkdir7(dirname9(renderedPath), { recursive: true });
2233
+ await writeFile9(renderedPath, toml, "utf8");
1752
2234
  return {
1753
2235
  ...artifact,
1754
2236
  name: agentName,
1755
2237
  sourcePath: renderedPath,
1756
2238
  stagedPath: renderedPath,
1757
- relativePath: join5("subagents", `${agentName}.toml`),
2239
+ relativePath: join6("subagents", `${agentName}.toml`),
1758
2240
  kind: "file",
1759
2241
  hash: await hashPath(renderedPath)
1760
2242
  };
1761
2243
  }
1762
2244
  function codexAgentName(artifact) {
1763
- const raw = artifact.kind === "dir" ? artifact.name : basename3(artifact.name);
2245
+ const raw = artifact.kind === "dir" ? artifact.name : basename4(artifact.name);
1764
2246
  return raw.replace(/\.toml$/i, "").replace(/\.md$/i, "");
1765
2247
  }
1766
2248
  function validateCodexAgentToml(content, path) {
@@ -1816,8 +2298,8 @@ function escapeRegExp(value) {
1816
2298
  }
1817
2299
 
1818
2300
  // src/staging/copilot-artifacts.ts
1819
- import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
1820
- import { basename as basename4, dirname as dirname8, join as join6 } from "path";
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";
1821
2303
  async function renderCopilotArtifacts(artifacts, stageRoot, adapter) {
1822
2304
  if (adapter?.name !== "copilot") return artifacts;
1823
2305
  const names = /* @__PURE__ */ new Set();
@@ -1839,29 +2321,29 @@ async function renderCopilotArtifacts(artifacts, stageRoot, adapter) {
1839
2321
  async function renderCopilotSubagent(artifact, stageRoot) {
1840
2322
  const sourcePath = artifact.stagedPath ?? artifact.sourcePath;
1841
2323
  const agentName = copilotAgentName(artifact);
1842
- const renderedPath = join6(stageRoot, ".agentwheel-rendered", "copilot-subagents", `${agentName}.agent.md`);
1843
- const markdownPath = artifact.kind === "dir" ? join6(sourcePath, "AGENTS.md") : sourcePath;
2324
+ const renderedPath = join7(stageRoot, ".agentwheel-rendered", "copilot-subagents", `${agentName}.agent.md`);
2325
+ const markdownPath = artifact.kind === "dir" ? join7(sourcePath, "AGENTS.md") : sourcePath;
1844
2326
  if (artifact.kind === "dir" && !await pathExists(markdownPath)) {
1845
2327
  throw new Error(`Copilot subagent directory ${artifact.relativePath} must contain AGENTS.md.`);
1846
2328
  }
1847
2329
  if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md")) {
1848
2330
  throw new Error(`Copilot subagent ${artifact.relativePath} must be a .md file or directory containing AGENTS.md.`);
1849
2331
  }
1850
- const markdown = await readFile8(markdownPath, "utf8");
1851
- await mkdir7(dirname8(renderedPath), { recursive: true });
1852
- await writeFile8(renderedPath, ensureCopilotAgentDescription(agentName, markdown), "utf8");
2332
+ const markdown = await readFile10(markdownPath, "utf8");
2333
+ await mkdir8(dirname10(renderedPath), { recursive: true });
2334
+ await writeFile10(renderedPath, ensureCopilotAgentDescription(agentName, markdown), "utf8");
1853
2335
  return {
1854
2336
  ...artifact,
1855
2337
  name: `${agentName}.agent.md`,
1856
2338
  sourcePath: renderedPath,
1857
2339
  stagedPath: renderedPath,
1858
- relativePath: join6("subagents", `${agentName}.agent.md`),
2340
+ relativePath: join7("subagents", `${agentName}.agent.md`),
1859
2341
  kind: "file",
1860
2342
  hash: await hashPath(renderedPath)
1861
2343
  };
1862
2344
  }
1863
2345
  function copilotAgentName(artifact) {
1864
- const raw = artifact.kind === "dir" ? artifact.name : basename4(artifact.name);
2346
+ const raw = artifact.kind === "dir" ? artifact.name : basename5(artifact.name);
1865
2347
  return raw.replace(/\.agent\.md$/i, "").replace(/\.md$/i, "");
1866
2348
  }
1867
2349
  function ensureCopilotAgentDescription(agentName, markdown) {
@@ -1901,14 +2383,322 @@ function yamlString(value) {
1901
2383
  return JSON.stringify(value);
1902
2384
  }
1903
2385
 
2386
+ // src/targets/plugins/claude.ts
2387
+ import { join as join9 } from "path";
2388
+
2389
+ // src/targets/plugins/common.ts
2390
+ import { readFile as readFile11 } from "fs/promises";
2391
+ import { join as join8 } from "path";
2392
+ import { parseDocument } from "yaml";
2393
+ function pluginStateRoot(request) {
2394
+ return join8(
2395
+ request.targetRoot,
2396
+ ".agentwheel",
2397
+ "plugins",
2398
+ request.runtime,
2399
+ request.installationType,
2400
+ safeNameSegment(request.packageName ?? "package"),
2401
+ safeNameSegment(request.installName)
2402
+ );
2403
+ }
2404
+ function agentwheelMarketplaceName(packageName, pluginName) {
2405
+ return `agentwheel-${safeNameSegment(packageName ?? "package")}-${safeNameSegment(pluginName)}`;
2406
+ }
2407
+ function safeNameSegment(value) {
2408
+ const normalized = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
2409
+ return normalized.length > 0 ? normalized : "unnamed";
2410
+ }
2411
+ async function jsonPluginName(root, relativeManifestPath, fallback) {
2412
+ const manifestPath = join8(root, relativeManifestPath);
2413
+ if (!await pathExists(manifestPath)) return fallback;
2414
+ const parsed = JSON.parse(await readFile11(manifestPath, "utf8"));
2415
+ return typeof parsed.name === "string" && parsed.name.trim().length > 0 ? parsed.name.trim() : fallback;
2416
+ }
2417
+ async function yamlPluginName(root, relativeManifestPaths, fallback) {
2418
+ for (const relativeManifestPath of relativeManifestPaths) {
2419
+ const manifestPath = join8(root, relativeManifestPath);
2420
+ if (!await pathExists(manifestPath)) continue;
2421
+ const document = parseDocument(await readFile11(manifestPath, "utf8"));
2422
+ const parsed = document.toJSON();
2423
+ if (!isRecord4(parsed)) continue;
2424
+ for (const key of ["name", "module", "package"]) {
2425
+ const value = parsed[key];
2426
+ if (typeof value === "string" && value.trim().length > 0) return value.trim();
2427
+ }
2428
+ const packageValue = parsed.package;
2429
+ if (isRecord4(packageValue) && typeof packageValue.name === "string" && packageValue.name.trim().length > 0) {
2430
+ return packageValue.name.trim();
2431
+ }
2432
+ }
2433
+ return fallback;
2434
+ }
2435
+ function isRecord4(value) {
2436
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2437
+ }
2438
+
2439
+ // src/targets/plugins/claude.ts
2440
+ async function claudePluginSpec(request) {
2441
+ const pluginName = await jsonPluginName(request.sourcePath, ".claude-plugin/plugin.json", request.installName);
2442
+ const marketplaceName = agentwheelMarketplaceName(request.artifact.packageName, pluginName);
2443
+ const stateRoot = pluginStateRoot({
2444
+ targetRoot: request.targetRoot,
2445
+ runtime: "claude",
2446
+ installationType: request.installationType,
2447
+ packageName: request.artifact.packageName,
2448
+ installName: request.installName
2449
+ });
2450
+ const marketplaceRoot = join9(stateRoot, "marketplace");
2451
+ const scope = claudeScope(request.installationType);
2452
+ const selector = `${pluginName}@${marketplaceName}`;
2453
+ return {
2454
+ runtime: "claude",
2455
+ pluginName,
2456
+ marketplaceName,
2457
+ stateRoot,
2458
+ installCommands: [
2459
+ ["claude", "plugin", "marketplace", "add", marketplaceRoot, "--scope", scope],
2460
+ ["claude", "plugin", "install", selector, "--scope", scope]
2461
+ ],
2462
+ uninstallCommands: [
2463
+ ["claude", "plugin", "uninstall", selector, "--scope", scope],
2464
+ ["claude", "plugin", "marketplace", "remove", marketplaceName, "--scope", scope]
2465
+ ]
2466
+ };
2467
+ }
2468
+ function claudeScope(installationType) {
2469
+ if (installationType === "user" || installationType === "local") return installationType;
2470
+ if (installationType === "project") return "project";
2471
+ throw new Error(`Claude plugins support installation types user, local, or project; got '${installationType}'.`);
2472
+ }
2473
+
2474
+ // src/targets/plugins/codex.ts
2475
+ import { join as join10 } from "path";
2476
+ async function codexPluginSpec(request) {
2477
+ const pluginName = await jsonPluginName(request.sourcePath, ".codex-plugin/plugin.json", request.installName);
2478
+ const marketplaceName = agentwheelMarketplaceName(request.artifact.packageName, pluginName);
2479
+ const stateRoot = pluginStateRoot({
2480
+ targetRoot: request.targetRoot,
2481
+ runtime: "codex",
2482
+ installationType: request.installationType,
2483
+ packageName: request.artifact.packageName,
2484
+ installName: request.installName
2485
+ });
2486
+ const marketplaceRoot = join10(stateRoot, "marketplace");
2487
+ const selector = `${pluginName}@${marketplaceName}`;
2488
+ return {
2489
+ runtime: "codex",
2490
+ pluginName,
2491
+ marketplaceName,
2492
+ stateRoot,
2493
+ installCommands: [
2494
+ ["codex", "plugin", "marketplace", "add", marketplaceRoot, "--json"],
2495
+ ["codex", "plugin", "add", selector, "--json"]
2496
+ ],
2497
+ uninstallCommands: [
2498
+ ["codex", "plugin", "remove", selector, "--json"],
2499
+ ["codex", "plugin", "marketplace", "remove", marketplaceName, "--json"]
2500
+ ]
2501
+ };
2502
+ }
2503
+
2504
+ // src/targets/plugins/copilot.ts
2505
+ import { join as join11 } from "path";
2506
+ async function copilotPluginSpec(request) {
2507
+ if (request.installationType !== "user") {
2508
+ throw new Error("Copilot plugins are persistent user-level installs only; pass --installation-type user.");
2509
+ }
2510
+ const pluginName = await jsonPluginName(request.sourcePath, "plugin.json", request.installName);
2511
+ const stateRoot = pluginStateRoot({
2512
+ targetRoot: request.targetRoot,
2513
+ runtime: "copilot",
2514
+ installationType: request.installationType,
2515
+ packageName: request.artifact.packageName,
2516
+ installName: request.installName
2517
+ });
2518
+ const pluginRoot = join11(stateRoot, "plugin");
2519
+ return {
2520
+ runtime: "copilot",
2521
+ pluginName,
2522
+ stateRoot,
2523
+ installCommands: [["copilot", "plugin", "install", pluginRoot]],
2524
+ uninstallCommands: [["copilot", "plugin", "uninstall", pluginName]]
2525
+ };
2526
+ }
2527
+
2528
+ // src/targets/plugins/hermes.ts
2529
+ import { join as join12 } from "path";
2530
+ async function hermesPluginSpec(request) {
2531
+ if (request.installationType !== "user") {
2532
+ throw new Error("Hermes plugins are user-level installs only; pass --installation-type user.");
2533
+ }
2534
+ const pluginName = await yamlPluginName(request.sourcePath, ["plugin.yaml", "plugin.yml"], request.installName);
2535
+ const stateRoot = pluginStateRoot({
2536
+ targetRoot: request.targetRoot,
2537
+ runtime: "hermes",
2538
+ installationType: request.installationType,
2539
+ packageName: request.artifact.packageName,
2540
+ installName: request.installName
2541
+ });
2542
+ const repoRoot = join12(stateRoot, "repo");
2543
+ return {
2544
+ runtime: "hermes",
2545
+ pluginName,
2546
+ stateRoot,
2547
+ installCommands: [["hermes", "plugins", "install", "--force", "--enable", `file://${repoRoot}`]],
2548
+ uninstallCommands: [["hermes", "plugins", "remove", pluginName]]
2549
+ };
2550
+ }
2551
+
1904
2552
  // src/targets/plugins/openclaw.ts
2553
+ import { readFile as readFile12 } from "fs/promises";
2554
+ import { join as join13 } from "path";
1905
2555
  function openClawPluginInstallCommand(request) {
1906
2556
  return ["openclaw", "plugins", "install", "--force", request.path];
1907
2557
  }
2558
+ function openClawPluginUninstallCommand(pluginName) {
2559
+ return ["openclaw", "plugins", "uninstall", pluginName, "--force"];
2560
+ }
2561
+ async function openClawPluginSpec(request) {
2562
+ const pluginName = await openClawPluginName(request.path, request.fallbackPluginName);
2563
+ return {
2564
+ runtime: "openclaw",
2565
+ pluginName,
2566
+ installCommands: [openClawPluginInstallCommand({ path: request.path, dryRun: true })],
2567
+ uninstallCommands: [openClawPluginUninstallCommand(pluginName)]
2568
+ };
2569
+ }
2570
+ async function openClawPluginName(root, fallback) {
2571
+ for (const manifestName of ["plugin.json", "openclaw.plugin.json"]) {
2572
+ const manifestPath = join13(root, manifestName);
2573
+ if (!await pathExists(manifestPath)) continue;
2574
+ const parsed = JSON.parse(await readFile12(manifestPath, "utf8"));
2575
+ if (typeof parsed.name === "string" && parsed.name.trim().length > 0) return parsed.name.trim();
2576
+ }
2577
+ return fallback;
2578
+ }
2579
+
2580
+ // src/targets/plugins/index.ts
2581
+ async function semanticPluginSpecForArtifact(request) {
2582
+ if (request.semantic === "openclaw-plugin") {
2583
+ return openClawPluginSpec({
2584
+ path: request.sourcePath,
2585
+ fallbackPluginName: request.installName
2586
+ });
2587
+ }
2588
+ if (request.semantic === "claude-plugin") {
2589
+ return claudePluginSpec(request);
2590
+ }
2591
+ if (request.semantic === "codex-plugin") {
2592
+ return codexPluginSpec(request);
2593
+ }
2594
+ if (request.semantic === "copilot-plugin") {
2595
+ return copilotPluginSpec(request);
2596
+ }
2597
+ if (request.semantic === "hermes-plugin") {
2598
+ return hermesPluginSpec(request);
2599
+ }
2600
+ return void 0;
2601
+ }
2602
+
2603
+ // src/validation/artifacts.ts
2604
+ import { readFile as readFile13 } from "fs/promises";
2605
+ import { basename as basename6, join as join14 } from "path";
2606
+ import { parseDocument as parseDocument2 } from "yaml";
2607
+
2608
+ // src/model/selection.ts
2609
+ function artifactSelectorKey(artifact) {
2610
+ return `${artifact.type}/${artifact.name}`;
2611
+ }
2612
+ function artifactSelectorAliases(artifact) {
2613
+ const primary = artifactSelectorKey(artifact);
2614
+ if (artifact.type !== "subagents") return [primary];
2615
+ const baseName = subagentBaseName(artifact.name);
2616
+ if (baseName === artifact.name) return [primary];
2617
+ return [primary, `subagents/${baseName}`];
2618
+ }
2619
+ function normalizeArtifactSelectors(select, legacySkills) {
2620
+ const selected = [
2621
+ ...select ?? [],
2622
+ ...(legacySkills ?? []).map((name) => `skills/${name}`)
2623
+ ].flatMap(splitSelectorList);
2624
+ if (selected.length === 0) return void 0;
2625
+ return [...new Set(selected.map(parseArtifactSelector))];
2626
+ }
2627
+ function filterArtifactsBySelection(artifacts, selectors, legacySkills) {
2628
+ const selected = normalizeArtifactSelectors(selectors, legacySkills);
2629
+ if (!selected?.length) return artifacts;
2630
+ const selectedSet = new Set(selected);
2631
+ const available = new Set(artifacts.flatMap(artifactSelectorAliases));
2632
+ const missing = selected.filter((selector) => !available.has(selector));
2633
+ if (missing.length > 0) {
2634
+ throw new Error(`Selected artifact not found in package: ${missing.join(", ")}`);
2635
+ }
2636
+ return artifacts.filter((artifact) => artifact.required || artifactSelectorAliases(artifact).some((selector) => selectedSet.has(selector)));
2637
+ }
2638
+ function splitSelectorList(value) {
2639
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
2640
+ }
2641
+ function parseArtifactSelector(value) {
2642
+ const slash = value.indexOf("/");
2643
+ if (slash <= 0 || slash === value.length - 1) {
2644
+ throw new Error(`Invalid artifact selector: ${value}. Expected <type>/<name>.`);
2645
+ }
2646
+ const type = value.slice(0, slash);
2647
+ const name = value.slice(slash + 1);
2648
+ const parsedType = artifactTypeSchema.safeParse(type);
2649
+ if (!parsedType.success) {
2650
+ throw new Error(`Invalid artifact selector type: ${type}`);
2651
+ }
2652
+ return `${parsedType.data}/${name}`;
2653
+ }
2654
+ function subagentBaseName(name) {
2655
+ return name.replace(/\.agent\.md$/i, "").replace(/\.toml$/i, "").replace(/\.md$/i, "");
2656
+ }
1908
2657
 
1909
2658
  // src/validation/artifacts.ts
1910
- import { readFile as readFile9 } from "fs/promises";
1911
- import { basename as basename5, join as join7 } from "path";
2659
+ var behavioralRuleFormats = ["markdown-rule", "claude-markdown-rule", "copilot-instruction-rule"];
2660
+ var pluginFormats = ["claude-plugin", "codex-plugin", "hermes-plugin", "copilot-plugin", "openclaw-plugin"];
2661
+ async function filterArtifactsByInstallFormat(artifacts, adapter, installationType, options = {}) {
2662
+ const selectedSet = new Set(normalizeArtifactSelectors(options.selected ?? []) ?? []);
2663
+ const kept = [];
2664
+ const skipped = [];
2665
+ let installableCount = 0;
2666
+ for (const artifact of artifacts) {
2667
+ if (artifact.type === "fragments") {
2668
+ kept.push(artifact);
2669
+ continue;
2670
+ }
2671
+ const target = targetMappingForArtifact(adapter, artifact.type, installationType);
2672
+ if (!target?.enabled) {
2673
+ if (artifact.type === "rules") {
2674
+ skipped.push({
2675
+ artifact,
2676
+ compatibility: {
2677
+ compatible: false,
2678
+ knownIncompatible: true,
2679
+ expected: behavioralRuleFormats,
2680
+ message: "target does not support behavioral Markdown rules"
2681
+ }
2682
+ });
2683
+ continue;
2684
+ }
2685
+ kept.push(artifact);
2686
+ continue;
2687
+ }
2688
+ const compatibility = await artifactFormatCompatibility(artifact, target);
2689
+ if (compatibility.knownIncompatible) {
2690
+ skipped.push({ artifact, compatibility });
2691
+ continue;
2692
+ }
2693
+ kept.push(artifact);
2694
+ installableCount += 1;
2695
+ }
2696
+ if (installableCount === 0 && skipped.length > 0) return artifacts;
2697
+ for (const item of skipped) {
2698
+ options.warn?.(formatSkipWarning(item.artifact, item.compatibility, adapter, installationType, selectedSet));
2699
+ }
2700
+ return kept;
2701
+ }
1912
2702
  async function validateArtifactsForInstall(artifacts, adapter, installationType) {
1913
2703
  const issues = [];
1914
2704
  for (const artifact of artifacts) {
@@ -1928,17 +2718,18 @@ async function validateArtifactsForInstall(artifacts, adapter, installationType)
1928
2718
  }
1929
2719
  async function validateArtifact(artifact, target) {
1930
2720
  const issues = [];
1931
- const format = artifact.format ?? await inferArtifactFormat(artifact, target) ?? semanticDefaultFormat(target);
1932
- if (target.formats?.length) {
2721
+ const compatibility = await artifactFormatCompatibility(artifact, target);
2722
+ const format = compatibility.format;
2723
+ if (compatibility.expected?.length) {
1933
2724
  if (!format) {
1934
2725
  issues.push({
1935
2726
  artifact,
1936
- message: `format is unknown; expected one of: ${target.formats.join(", ")}`
2727
+ message: `format is unknown; expected one of: ${compatibility.expected.join(", ")}`
1937
2728
  });
1938
- } else if (!target.formats.includes(format)) {
2729
+ } else if (!compatibility.compatible) {
1939
2730
  issues.push({
1940
2731
  artifact,
1941
- message: `format '${format}' is not compatible; expected one of: ${target.formats.join(", ")}`
2732
+ message: compatibility.message ?? `format '${format}' is not compatible; expected one of: ${compatibility.expected.join(", ")}`
1942
2733
  });
1943
2734
  }
1944
2735
  }
@@ -1946,9 +2737,31 @@ async function validateArtifact(artifact, target) {
1946
2737
  issues.push(...await validateGenericStructure(artifact, target));
1947
2738
  return issues;
1948
2739
  }
2740
+ async function artifactFormatCompatibility(artifact, target) {
2741
+ const format = artifact.format ?? await inferArtifactFormat(artifact, target) ?? semanticDefaultFormat(artifact, target);
2742
+ const expected = expectedFormats(artifact, target);
2743
+ if (!expected?.length) return { compatible: true, knownIncompatible: false, format };
2744
+ if (!format) return { compatible: false, knownIncompatible: false, expected };
2745
+ if (artifact.type === "rules" && target.formats?.length && target.formats.every((item) => !behavioralRuleFormats.includes(item))) {
2746
+ return {
2747
+ compatible: false,
2748
+ knownIncompatible: true,
2749
+ format,
2750
+ expected,
2751
+ message: `target does not support behavioral Markdown rules; expected one of: ${expected.join(", ")}`
2752
+ };
2753
+ }
2754
+ const compatible = expected.includes(format);
2755
+ return {
2756
+ compatible,
2757
+ knownIncompatible: !compatible,
2758
+ format,
2759
+ expected
2760
+ };
2761
+ }
1949
2762
  async function inferArtifactFormat(artifact, target) {
1950
2763
  if (artifact.type === "rules") {
1951
- if (hasExtension(artifact, ".rules")) return "codex-command-policy";
2764
+ if (artifact.format === "codex-command-policy" || hasExtension(artifact, ".rules")) return "codex-command-policy";
1952
2765
  if (hasExtension(artifact, ".md") || hasExtension(artifact, ".markdown")) return "markdown-rule";
1953
2766
  return void 0;
1954
2767
  }
@@ -1957,26 +2770,51 @@ async function inferArtifactFormat(artifact, target) {
1957
2770
  }
1958
2771
  return void 0;
1959
2772
  }
1960
- function semanticDefaultFormat(target) {
1961
- if (target.semantic === "openclaw-plugin") return "openclaw-plugin";
2773
+ function semanticDefaultFormat(artifact, target) {
2774
+ if (artifact.type === "plugins" && isPluginFormat(target.semantic)) return target.semantic;
1962
2775
  return void 0;
1963
2776
  }
2777
+ function expectedFormats(artifact, target) {
2778
+ if (artifact.type === "rules") {
2779
+ const declared = target.formats?.filter((format) => behavioralRuleFormats.includes(format));
2780
+ if (target.formats?.length) return declared?.length ? declared : behavioralRuleFormats;
2781
+ return behavioralRuleFormats;
2782
+ }
2783
+ if (artifact.type === "plugins") {
2784
+ if (isPluginFormat(target.semantic)) return [target.semantic];
2785
+ const declared = target.formats?.filter((format) => pluginFormats.includes(format));
2786
+ if (target.formats?.length) return declared?.length ? declared : pluginFormats;
2787
+ }
2788
+ return target.formats;
2789
+ }
1964
2790
  async function validateKnownFormat(artifact, format, target) {
1965
2791
  if (!format) return [];
1966
- if (format === "codex-command-policy") return validateCodexCommandPolicyRule(artifact);
1967
2792
  if (format === "markdown-rule" || format === "claude-markdown-rule" || format === "copilot-instruction-rule") {
1968
2793
  return validateMarkdownRule(artifact, format);
1969
2794
  }
1970
2795
  if (format === "openclaw-plugin" || target.semantic === "openclaw-plugin") {
1971
2796
  return validateOpenClawPlugin(artifact);
1972
2797
  }
2798
+ if (format === "claude-plugin" || target.semantic === "claude-plugin") {
2799
+ return validateJsonPluginDescriptor(artifact, ".claude-plugin/plugin.json", "Claude");
2800
+ }
2801
+ if (format === "codex-plugin" || target.semantic === "codex-plugin") {
2802
+ return validateJsonPluginDescriptor(artifact, ".codex-plugin/plugin.json", "Codex");
2803
+ }
2804
+ if (format === "copilot-plugin" || target.semantic === "copilot-plugin") {
2805
+ return validateJsonPluginDescriptor(artifact, "plugin.json", "Copilot");
2806
+ }
2807
+ if (format === "hermes-plugin" || target.semantic === "hermes-plugin") {
2808
+ return validateHermesPlugin(artifact);
2809
+ }
2810
+ if (pluginFormats.includes(format)) return validatePluginArtifact(artifact, format);
1973
2811
  return [];
1974
2812
  }
1975
2813
  async function validateGenericStructure(artifact, target) {
1976
2814
  const issues = [];
1977
2815
  if (artifact.type === "skills") {
1978
2816
  if (artifact.kind === "dir") {
1979
- const skillMd = join7(artifactPath(artifact), "SKILL.md");
2817
+ const skillMd = join14(artifactPath(artifact), "SKILL.md");
1980
2818
  if (!await pathExists(skillMd)) {
1981
2819
  issues.push({ artifact, message: "skill directory must contain SKILL.md" });
1982
2820
  } else {
@@ -1992,6 +2830,10 @@ async function validateGenericStructure(artifact, target) {
1992
2830
  const parsed = await parseJsonObjectArtifact(artifact);
1993
2831
  if (!parsed.ok) issues.push({ artifact, message: parsed.message });
1994
2832
  }
2833
+ if (target.merge === "yaml-deep") {
2834
+ const parsed = await parseYamlObjectArtifact(artifact);
2835
+ if (!parsed.ok) issues.push({ artifact, message: parsed.message });
2836
+ }
1995
2837
  if (target.merge === "codex-toml-mcp") {
1996
2838
  const parsed = await parseJsonObjectArtifact(artifact);
1997
2839
  if (!parsed.ok) {
@@ -2005,7 +2847,7 @@ async function validateGenericStructure(artifact, target) {
2005
2847
  async function validateSkillFrontmatter(artifact, skillMdPath) {
2006
2848
  let content;
2007
2849
  try {
2008
- content = await readFile9(skillMdPath, "utf8");
2850
+ content = await readFile13(skillMdPath, "utf8");
2009
2851
  } catch (error) {
2010
2852
  return [{ artifact, message: `could not read SKILL.md: ${errorMessage(error)}` }];
2011
2853
  }
@@ -2031,33 +2873,6 @@ async function validateSkillFrontmatter(artifact, skillMdPath) {
2031
2873
  }
2032
2874
  return issues;
2033
2875
  }
2034
- async function validateCodexCommandPolicyRule(artifact) {
2035
- const issues = [];
2036
- if (artifact.type !== "rules") {
2037
- return [{ artifact, message: "codex-command-policy format is only valid for rules artifacts" }];
2038
- }
2039
- if (artifact.kind !== "file") {
2040
- issues.push({ artifact, message: "Codex command-policy rules must be files" });
2041
- }
2042
- if (!hasExtension(artifact, ".rules")) {
2043
- issues.push({ artifact, message: "Codex command-policy rules must use the .rules extension" });
2044
- }
2045
- const content = await readUtf8Artifact(artifact, issues);
2046
- if (content === void 0) return issues;
2047
- if (!/\bprefix_rule\s*\(/.test(content)) {
2048
- issues.push({ artifact, message: "Codex command-policy rules must contain at least one prefix_rule(...)" });
2049
- }
2050
- if (/\bprefix_rule\s*\(/.test(content) && !/\bpattern\s*=/.test(content)) {
2051
- issues.push({ artifact, message: "Codex command-policy rules must define a pattern field" });
2052
- }
2053
- for (const match of content.matchAll(/\bdecision\s*=\s*["']([^"']+)["']/g)) {
2054
- const decision = match[1];
2055
- if (decision !== "allow" && decision !== "prompt" && decision !== "forbidden") {
2056
- issues.push({ artifact, message: `Codex command-policy decision '${decision}' must be allow, prompt, or forbidden` });
2057
- }
2058
- }
2059
- return issues;
2060
- }
2061
2876
  function validateMarkdownRule(artifact, format) {
2062
2877
  const label = format === "copilot-instruction-rule" ? "Copilot instruction rules" : format === "claude-markdown-rule" ? "Claude markdown rules" : "Markdown rules";
2063
2878
  const issues = [];
@@ -2072,6 +2887,72 @@ function validateMarkdownRule(artifact, format) {
2072
2887
  }
2073
2888
  return issues;
2074
2889
  }
2890
+ function validatePluginArtifact(artifact, format) {
2891
+ const issues = [];
2892
+ if (artifact.type !== "plugins") {
2893
+ issues.push({ artifact, message: `${format} format is only valid for plugins artifacts` });
2894
+ }
2895
+ if (artifact.kind !== "dir") {
2896
+ issues.push({ artifact, message: `${format} plugins must be directory artifacts` });
2897
+ }
2898
+ return issues;
2899
+ }
2900
+ async function validateJsonPluginDescriptor(artifact, relativeManifestPath, label) {
2901
+ const generic = validatePluginArtifact(artifact, `${label.toLowerCase()}-plugin`);
2902
+ if (generic.length > 0) return generic;
2903
+ const manifestPath = join14(artifactPath(artifact), relativeManifestPath);
2904
+ if (!await pathExists(manifestPath)) {
2905
+ return [{ artifact, message: `${label} plugins must contain ${relativeManifestPath}` }];
2906
+ }
2907
+ const parsed = await parseJsonPluginManifest(manifestPath, label);
2908
+ return parsed.ok ? [] : [{ artifact, message: parsed.message }];
2909
+ }
2910
+ async function validateHermesPlugin(artifact) {
2911
+ const generic = validatePluginArtifact(artifact, "hermes-plugin");
2912
+ if (generic.length > 0) return generic;
2913
+ const root = artifactPath(artifact);
2914
+ const manifestPaths = [join14(root, "plugin.yaml"), join14(root, "plugin.yml")];
2915
+ const manifestPath = await firstExistingPath(manifestPaths);
2916
+ if (!manifestPath) {
2917
+ return [{ artifact, message: "Hermes plugins must contain plugin.yaml or plugin.yml" }];
2918
+ }
2919
+ try {
2920
+ const document = parseDocument2(await readFile13(manifestPath, "utf8"));
2921
+ if (document.errors.length > 0) {
2922
+ return [{ artifact, message: `Hermes ${basename6(manifestPath)} must be valid YAML: ${document.errors[0]?.message ?? "parse error"}` }];
2923
+ }
2924
+ const parsed = document.toJSON();
2925
+ if (!isUnknownRecord(parsed)) {
2926
+ return [{ artifact, message: `Hermes ${basename6(manifestPath)} must contain a YAML object` }];
2927
+ }
2928
+ if (!hasStringField(parsed, "name") && !hasStringField(parsed, "module") && !hasStringField(parsed, "package") && !hasNestedPackageName(parsed)) {
2929
+ return [{ artifact, message: `Hermes ${basename6(manifestPath)} must declare a non-empty name, module, or package name` }];
2930
+ }
2931
+ return [];
2932
+ } catch (error) {
2933
+ return [{ artifact, message: `Hermes ${basename6(manifestPath)} must be valid YAML: ${errorMessage(error)}` }];
2934
+ }
2935
+ }
2936
+ async function firstExistingPath(paths) {
2937
+ for (const path of paths) {
2938
+ if (await pathExists(path)) return path;
2939
+ }
2940
+ return void 0;
2941
+ }
2942
+ async function parseJsonPluginManifest(manifestPath, label) {
2943
+ try {
2944
+ const parsed = JSON.parse(await readFile13(manifestPath, "utf8"));
2945
+ if (!isRecord5(parsed)) {
2946
+ return { ok: false, message: `${label} ${basename6(manifestPath)} must be a JSON object` };
2947
+ }
2948
+ if (typeof parsed.name !== "string" || parsed.name.trim().length === 0) {
2949
+ return { ok: false, message: `${label} ${basename6(manifestPath)} must declare a non-empty name` };
2950
+ }
2951
+ return { ok: true, name: parsed.name.trim() };
2952
+ } catch (error) {
2953
+ return { ok: false, message: `${label} ${basename6(manifestPath)} must be valid JSON: ${errorMessage(error)}` };
2954
+ }
2955
+ }
2075
2956
  async function validateOpenClawPlugin(artifact) {
2076
2957
  const issues = [];
2077
2958
  if (artifact.type !== "plugins") {
@@ -2097,15 +2978,15 @@ async function validateOpenClawPlugin(artifact) {
2097
2978
  }
2098
2979
  async function openClawPluginManifestPaths(artifact) {
2099
2980
  const root = artifactPath(artifact);
2100
- const candidates = [join7(root, "plugin.json"), join7(root, "openclaw.plugin.json")];
2981
+ const candidates = [join14(root, "plugin.json"), join14(root, "openclaw.plugin.json")];
2101
2982
  const existing = await Promise.all(candidates.map(async (candidate) => await pathExists(candidate) ? candidate : void 0));
2102
2983
  return existing.filter((candidate) => candidate !== void 0);
2103
2984
  }
2104
2985
  async function parseOpenClawPluginManifest(manifestPath) {
2105
- const manifestName = basename5(manifestPath);
2986
+ const manifestName = basename6(manifestPath);
2106
2987
  try {
2107
- const parsed = JSON.parse(await readFile9(manifestPath, "utf8"));
2108
- if (!isRecord3(parsed)) {
2988
+ const parsed = JSON.parse(await readFile13(manifestPath, "utf8"));
2989
+ if (!isRecord5(parsed)) {
2109
2990
  return { ok: false, path: manifestPath, message: `OpenClaw ${manifestName} must be a JSON object` };
2110
2991
  }
2111
2992
  if (typeof parsed.name !== "string" || parsed.name.trim().length === 0) {
@@ -2116,32 +2997,39 @@ async function parseOpenClawPluginManifest(manifestPath) {
2116
2997
  return { ok: false, path: manifestPath, message: `OpenClaw ${manifestName} must be valid JSON: ${errorMessage(error)}` };
2117
2998
  }
2118
2999
  }
2119
- async function readUtf8Artifact(artifact, issues) {
2120
- if (artifact.kind !== "file") return void 0;
3000
+ async function parseJsonObjectArtifact(artifact) {
3001
+ if (artifact.kind !== "file") {
3002
+ return { ok: false, message: "merge artifacts must be JSON files" };
3003
+ }
2121
3004
  try {
2122
- return await readFile9(artifactPath(artifact), "utf8");
3005
+ const parsed = JSON.parse(await readFile13(artifactPath(artifact), "utf8"));
3006
+ if (!isRecord5(parsed)) return { ok: false, message: "merge artifacts must contain a JSON object" };
3007
+ return { ok: true, value: parsed };
2123
3008
  } catch (error) {
2124
- issues.push({ artifact, message: `could not read artifact: ${errorMessage(error)}` });
2125
- return void 0;
3009
+ return { ok: false, message: `merge artifact must be valid JSON: ${errorMessage(error)}` };
2126
3010
  }
2127
3011
  }
2128
- async function parseJsonObjectArtifact(artifact) {
3012
+ async function parseYamlObjectArtifact(artifact) {
2129
3013
  if (artifact.kind !== "file") {
2130
- return { ok: false, message: "merge artifacts must be JSON files" };
3014
+ return { ok: false, message: "merge artifacts must be YAML files" };
2131
3015
  }
2132
3016
  try {
2133
- const parsed = JSON.parse(await readFile9(artifactPath(artifact), "utf8"));
2134
- if (!isRecord3(parsed)) return { ok: false, message: "merge artifacts must contain a JSON object" };
3017
+ const document = parseDocument2(await readFile13(artifactPath(artifact), "utf8"));
3018
+ if (document.errors.length > 0) {
3019
+ return { ok: false, message: `merge artifact must be valid YAML: ${document.errors[0]?.message ?? "parse error"}` };
3020
+ }
3021
+ const parsed = document.toJSON();
3022
+ if (!isUnknownRecord(parsed)) return { ok: false, message: "merge artifacts must contain a YAML object" };
2135
3023
  return { ok: true, value: parsed };
2136
3024
  } catch (error) {
2137
- return { ok: false, message: `merge artifact must be valid JSON: ${errorMessage(error)}` };
3025
+ return { ok: false, message: `merge artifact must be valid YAML: ${errorMessage(error)}` };
2138
3026
  }
2139
3027
  }
2140
3028
  function extractMcpServers2(source) {
2141
- const raw = isRecord3(source.mcpServers) ? source.mcpServers : source;
3029
+ const raw = isRecord5(source.mcpServers) ? source.mcpServers : source;
2142
3030
  const servers = {};
2143
3031
  for (const [name, value] of Object.entries(raw)) {
2144
- if (isRecord3(value)) servers[name] = value;
3032
+ if (isRecord5(value)) servers[name] = value;
2145
3033
  }
2146
3034
  return servers;
2147
3035
  }
@@ -2153,11 +3041,33 @@ function artifactLabel(artifact) {
2153
3041
  return `${owner}${artifact.type}/${artifact.name}`;
2154
3042
  }
2155
3043
  function hasExtension(artifact, extension) {
2156
- return basename5(artifact.name).toLowerCase().endsWith(extension) || basename5(artifactPath(artifact)).toLowerCase().endsWith(extension);
3044
+ return basename6(artifact.name).toLowerCase().endsWith(extension) || basename6(artifactPath(artifact)).toLowerCase().endsWith(extension);
2157
3045
  }
2158
- function isRecord3(value) {
3046
+ function isPluginFormat(value) {
3047
+ return value !== void 0 && pluginFormats.includes(value);
3048
+ }
3049
+ function isRecord5(value) {
3050
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3051
+ }
3052
+ function isUnknownRecord(value) {
2159
3053
  return typeof value === "object" && value !== null && !Array.isArray(value);
2160
3054
  }
3055
+ function hasStringField(value, field) {
3056
+ const raw = value[field];
3057
+ return typeof raw === "string" && raw.trim().length > 0;
3058
+ }
3059
+ function hasNestedPackageName(value) {
3060
+ const raw = value.package;
3061
+ return isUnknownRecord(raw) && hasStringField(raw, "name");
3062
+ }
3063
+ function formatSkipWarning(artifact, compatibility, adapter, installationType, selectedSet) {
3064
+ const selector = artifactSelectorKey(artifact);
3065
+ const reason = selectedSet.has(selector) ? "selected but format-incompatible" : "format-incompatible";
3066
+ const expected = compatibility.expected?.join(", ") ?? "supported format";
3067
+ const format = compatibility.format ?? "unknown";
3068
+ const details = compatibility.message ?? `${adapter.name}/${installationType} expects ${expected}, got ${format}`;
3069
+ return `skip ${selector} (${reason}: ${details})`;
3070
+ }
2161
3071
  function errorMessage(error) {
2162
3072
  return error instanceof Error ? error.message : String(error);
2163
3073
  }
@@ -2165,17 +3075,18 @@ function errorMessage(error) {
2165
3075
  // src/install/plan.ts
2166
3076
  async function createCombinedInstallPlan(desiredArtifacts, adapter, targetRoot, manifest, transport = localTransport, options = {}) {
2167
3077
  const requestedInstallationType = options.installationType ?? defaultInstallationType;
2168
- const installationType = resolveInstallationTypeForArtifacts(adapter, desiredArtifacts.map((artifact) => artifact.type), requestedInstallationType);
2169
- const installRoot = installRootForArtifacts(adapter, targetRoot, installationType, desiredArtifacts.map((artifact) => artifact.type), transport.kind === "ssh");
2170
- await validateArtifactsForInstall(desiredArtifacts, adapter, installationType);
2171
- for (const artifact of desiredArtifacts) {
3078
+ const installableArtifacts = await filterArtifactsByInstallFormat(desiredArtifacts, adapter, requestedInstallationType, { warn: options.warn });
3079
+ const installationType = resolveInstallationTypeForArtifacts(adapter, installableArtifacts.map((artifact) => artifact.type), requestedInstallationType);
3080
+ const installRoot = installRootForArtifacts(adapter, targetRoot, installationType, installableArtifacts.map((artifact) => artifact.type), transport.kind === "ssh");
3081
+ await validateArtifactsForInstall(installableArtifacts, adapter, installationType);
3082
+ for (const artifact of installableArtifacts) {
2172
3083
  if (artifact.meta.dependencyRole !== "root" && isGuardedMergeTarget(artifact.type)) {
2173
3084
  throw new Error(`Dependency-provided ${artifact.type} artifacts cannot be installed until per-subentry ownership exists: ${artifact.type}/${artifact.name}`);
2174
3085
  }
2175
3086
  }
2176
3087
  const desired = [];
2177
- for (const artifact of desiredArtifacts) {
2178
- const op = operationForArtifact(artifact, adapter, installRoot, installationType, artifact.meta);
3088
+ for (const artifact of installableArtifacts) {
3089
+ const op = await operationForArtifact(artifact, adapter, installRoot, installationType, artifact.meta);
2179
3090
  if (op) {
2180
3091
  desired.push(op);
2181
3092
  }
@@ -2188,20 +3099,21 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
2188
3099
  if (workspaceOwner) {
2189
3100
  for (const op of desiredOps) op.workspaceOwner = workspaceOwner;
2190
3101
  }
2191
- for (const op of desiredOps) assertOperationContained(op, targetRoot);
2192
- const migration = await migrateManifestForPlan(manifest, desiredOps, targetRoot, transport);
3102
+ const preparedOps = await prepareManagedBlockOperations(desiredOps, adapter, targetRoot, transport, options);
3103
+ for (const op of preparedOps) assertOperationContained(op, targetRoot);
3104
+ const migration = await migrateManifestForPlan(manifest, preparedOps, targetRoot, transport);
2193
3105
  const effectiveEntries = migration.entries;
2194
3106
  const manifestByPath = new Map(effectiveEntries.map((entry) => [entry.path, entry]));
2195
- const { desired, collisions } = await splitCollisionOperations(desiredOps, manifestByPath, transport);
3107
+ const { desired, collisions } = await splitCollisionOperations(preparedOps, manifestByPath, transport);
2196
3108
  const operations = [];
2197
3109
  operations.push(...collisions);
2198
3110
  for (const op of desired.values()) {
2199
3111
  if (op.action === "plugin") {
2200
3112
  const existing2 = manifestByPath.get(op.relativeDestPath);
2201
- if (existing2 && existing2.hash === op.desiredHash) {
2202
- operations.push({ ...op, action: "skip", manifestHash: existing2.hash, reason: "plugin already planned" });
3113
+ if (existing2 && existing2.sourceHash === op.desiredHash && existing2.executed === true) {
3114
+ operations.push({ ...op, action: "skip", manifestHash: existing2.hash, reason: "semantic plugin already executed" });
2203
3115
  } else {
2204
- operations.push(op);
3116
+ operations.push(existing2 && existing2.sourceHash === op.desiredHash ? { ...op, manifestHash: existing2.hash, reason: "semantic plugin pending execution" } : op);
2205
3117
  }
2206
3118
  continue;
2207
3119
  }
@@ -2214,6 +3126,10 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
2214
3126
  }
2215
3127
  continue;
2216
3128
  }
3129
+ if (op.mode === managedInstructionBlockMode) {
3130
+ operations.push(...await planManagedBlockOperation(op, manifestByPath, transport, options, workspaceOwner, targetRoot));
3131
+ continue;
3132
+ }
2217
3133
  if (op.mergeStrategy) {
2218
3134
  const existing2 = manifestByPath.get(op.relativeDestPath);
2219
3135
  const exists2 = await transport.pathExists(op.destPath);
@@ -2225,7 +3141,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
2225
3141
  if (existing2 && existing2.sourceHash === op.desiredHash) {
2226
3142
  operations.push({ ...op, action: "skip", currentHash: currentHash2, manifestHash: existing2.hash, reason: "merged source already up to date" });
2227
3143
  } else {
2228
- operations.push({ ...op, action: "update", currentHash: currentHash2, manifestHash: existing2?.hash, reason: existing2 ? "merge source changed" : "merge into existing JSON" });
3144
+ operations.push({ ...op, action: "update", currentHash: currentHash2, manifestHash: existing2?.hash, reason: existing2 ? "merge source changed" : "merge into existing destination" });
2229
3145
  }
2230
3146
  continue;
2231
3147
  }
@@ -2301,9 +3217,10 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
2301
3217
  }
2302
3218
  for (const entry of effectiveEntries) {
2303
3219
  if (desired.has(entry.path)) continue;
2304
- const destPath = join8(targetRoot, entry.path);
2305
- if (!await transport.pathExists(destPath)) continue;
2306
- const currentHash = await transport.hashPath(destPath);
3220
+ const semanticPlugin = entry.semanticPlugin;
3221
+ const destPath = semanticPlugin ? targetRoot : join15(targetRoot, entry.path);
3222
+ if (!semanticPlugin && !await transport.pathExists(destPath)) continue;
3223
+ const currentHash = semanticPlugin ? entry.hash : await currentEntryHash(entry, destPath, transport);
2307
3224
  if (workspaceOwner && !entryOwnedByWorkspace(entry, workspaceOwner)) {
2308
3225
  operations.push(keepForeignManifestEntryOperation(entry, targetRoot, workspaceOwner, void 0, currentHash));
2309
3226
  continue;
@@ -2321,6 +3238,11 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
2321
3238
  reason: "managed stale destination changed outside agentwheel",
2322
3239
  channel: entry.channel,
2323
3240
  packageName: entry.packageName,
3241
+ semanticCommand: entry.semanticCommand,
3242
+ semanticPlugin,
3243
+ execute: entry.executed,
3244
+ mergeStrategy: entry.mergeStrategy,
3245
+ mode: entry.mode,
2324
3246
  composedFrom: entry.composedFrom,
2325
3247
  ...operationMetadataFromEntry(entry),
2326
3248
  ...options.forceDrift ? { action: "remove", reason: "force removing drifted stale managed destination" } : {}
@@ -2338,6 +3260,11 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
2338
3260
  reason: "artifact removed from source",
2339
3261
  channel: entry.channel,
2340
3262
  packageName: entry.packageName,
3263
+ semanticCommand: semanticPlugin?.uninstallCommands[0] ?? entry.semanticCommand,
3264
+ semanticPlugin,
3265
+ execute: entry.executed,
3266
+ mergeStrategy: entry.mergeStrategy,
3267
+ mode: entry.mode,
2341
3268
  composedFrom: entry.composedFrom,
2342
3269
  ...operationMetadataFromEntry(entry)
2343
3270
  });
@@ -2407,11 +3334,131 @@ async function migrateManifestForPlan(manifest, desired, targetRoot, transport)
2407
3334
  }
2408
3335
  };
2409
3336
  }
3337
+ async function prepareManagedBlockOperations(desiredOps, adapter, targetRoot, transport, options) {
3338
+ const prepared = [];
3339
+ const physicalInstructionPaths = /* @__PURE__ */ new Map();
3340
+ for (const op of desiredOps) {
3341
+ if (op.mode !== managedInstructionBlockMode) {
3342
+ prepared.push(op);
3343
+ continue;
3344
+ }
3345
+ if (!op.sourcePath) {
3346
+ throw new Error(`Managed block instruction operation missing source: ${op.relativeDestPath}`);
3347
+ }
3348
+ const managedOp = {
3349
+ ...op,
3350
+ desiredHash: await desiredManagedInstructionBlockHash(op.sourcePath)
3351
+ };
3352
+ if (await shouldSkipClaudeBridge(adapter, managedOp, targetRoot, transport)) {
3353
+ continue;
3354
+ }
3355
+ await warnOnCopilotDoubleRead(adapter, managedOp, targetRoot, transport, options);
3356
+ const physicalKey = await managedInstructionPhysicalKey(managedOp.destPath, transport);
3357
+ const incumbent = physicalInstructionPaths.get(physicalKey);
3358
+ if (incumbent) {
3359
+ if (incumbent.desiredHash !== managedOp.desiredHash) {
3360
+ options.warn?.(`Multiple instruction targets resolve to ${managedOp.relativeDestPath}; using ${incumbent.relativeDestPath} and skipping duplicate with different content.`);
3361
+ }
3362
+ continue;
3363
+ }
3364
+ physicalInstructionPaths.set(physicalKey, managedOp);
3365
+ prepared.push(managedOp);
3366
+ }
3367
+ return prepared;
3368
+ }
3369
+ async function shouldSkipClaudeBridge(adapter, op, targetRoot, transport) {
3370
+ if (adapter.name !== "claude" || op.artifactType !== "instructions" || basename7(op.destPath).toLowerCase() !== "claude.md") {
3371
+ return false;
3372
+ }
3373
+ const agentsPath = join15(targetRoot, "AGENTS.md");
3374
+ return claudeInstructionBridgesAgents(op.destPath, agentsPath, transport);
3375
+ }
3376
+ async function warnOnCopilotDoubleRead(adapter, op, targetRoot, transport, options) {
3377
+ if (adapter.name !== "claude" || op.artifactType !== "instructions" || basename7(op.destPath).toLowerCase() !== "claude.md") return;
3378
+ const agentsPath = join15(targetRoot, "AGENTS.md");
3379
+ if (!await transport.pathExists(agentsPath)) return;
3380
+ if (await claudeInstructionBridgesAgents(op.destPath, agentsPath, transport)) return;
3381
+ options.warn?.("CLAUDE.md and AGENTS.md are separate instruction files; if Copilot is active it may read the managed instructions twice.");
3382
+ }
3383
+ async function planManagedBlockOperation(op, manifestByPath, transport, options, workspaceOwner, targetRoot) {
3384
+ const existing = manifestByPath.get(op.relativeDestPath);
3385
+ if (existing && workspaceOwner && !entryOwnedByWorkspace(existing, workspaceOwner)) {
3386
+ return [keepForeignManifestEntryOperation(existing, targetRoot, workspaceOwner, op)];
3387
+ }
3388
+ const selector = managedInstructionSelector(op.logicalSelector, op.artifactType, op.artifactName);
3389
+ const state = await readManagedInstructionBlockState(op.destPath, selector, transport);
3390
+ if (!state.exists) {
3391
+ return [{ ...op, action: "create", reason: "managed instruction destination missing" }];
3392
+ }
3393
+ if (!existing) {
3394
+ if (state.hasBlock && !state.drifted && state.hash === op.desiredHash && options.forceConflict) {
3395
+ return [{
3396
+ ...op,
3397
+ action: "skip",
3398
+ currentHash: state.hash,
3399
+ reason: "force adopting unmanaged managed-block destination with matching hash"
3400
+ }];
3401
+ }
3402
+ if (options.replaceConflict) {
3403
+ return [{
3404
+ ...op,
3405
+ action: "update",
3406
+ currentHash: state.hash,
3407
+ reason: "force adopting managed-block destination"
3408
+ }];
3409
+ }
3410
+ return [{
3411
+ ...op,
3412
+ action: "conflict",
3413
+ currentHash: state.hash,
3414
+ reason: "destination exists but is not managed"
3415
+ }];
3416
+ }
3417
+ if (!state.hasBlock || state.drifted || state.hash !== existing.hash) {
3418
+ const composedFromDiff = changedComposedSelectors(op.composedFrom, existing.composedFrom);
3419
+ if (options.forceDrift) {
3420
+ return [{
3421
+ ...op,
3422
+ action: "update",
3423
+ currentHash: state.hash,
3424
+ manifestHash: existing.hash,
3425
+ reason: reasonWithComposedDiff("force replacing drifted managed instruction block", op.composedFrom, existing.composedFrom),
3426
+ composedFromDiff
3427
+ }];
3428
+ }
3429
+ return [{
3430
+ ...op,
3431
+ action: "drift",
3432
+ currentHash: state.hash,
3433
+ manifestHash: existing.hash,
3434
+ reason: state.hasBlock ? "managed instruction block changed outside agentwheel" : "managed instruction block missing from destination",
3435
+ blockedDesiredHash: op.desiredHash,
3436
+ blockedReason: blockedDriftReason(op.composedFrom, existing.composedFrom),
3437
+ composedFromDiff
3438
+ }];
3439
+ }
3440
+ if (existing.sourceHash === op.desiredHash) {
3441
+ return [{ ...op, action: "skip", currentHash: state.hash, manifestHash: existing.hash, reason: "managed instruction block already up to date" }];
3442
+ }
3443
+ return [{
3444
+ ...op,
3445
+ action: "update",
3446
+ currentHash: state.hash,
3447
+ manifestHash: existing.hash,
3448
+ reason: reasonWithComposedDiff("managed instruction source changed", op.composedFrom, existing.composedFrom)
3449
+ }];
3450
+ }
3451
+ async function currentEntryHash(entry, destPath, transport) {
3452
+ if (entry.mode !== managedInstructionBlockMode) return transport.hashPath(destPath);
3453
+ const selector = managedInstructionSelector("logicalSelector" in entry ? entry.logicalSelector : void 0, entry.artifactType, entry.artifactName);
3454
+ const state = await readManagedInstructionBlockState(destPath, selector, transport);
3455
+ return state.drifted ? state.markerHash : state.hash;
3456
+ }
2410
3457
  async function canStrictlyAdoptLegacyEntry(entry, op, targetRoot, transport) {
2411
3458
  if (entry.artifactType !== op.artifactType || entry.artifactName !== op.artifactName) return false;
2412
3459
  if (!op.desiredHash || entry.sourceHash !== op.desiredHash) return false;
2413
3460
  if (!packageIdentityMatches(entry, op)) return false;
2414
- const destPath = join8(targetRoot, entry.path);
3461
+ const destPath = join15(targetRoot, entry.path);
2415
3462
  if (!await transport.pathExists(destPath)) return false;
2416
3463
  return await transport.hashPath(destPath) === entry.hash;
2417
3464
  }
@@ -2501,7 +3548,8 @@ function adoptLegacyEntry(entry, op) {
2501
3548
  refCount: owners.length,
2502
3549
  workspaceOwner: op.workspaceOwner ?? legacyUnownedWorkspaceOwner,
2503
3550
  graphLockDigest: op.graphLockDigest,
2504
- composedFrom: op.composedFrom ?? entry.composedFrom
3551
+ composedFrom: op.composedFrom ?? entry.composedFrom,
3552
+ mode: op.mode
2505
3553
  };
2506
3554
  }
2507
3555
  function operationMetadataFromDesired(artifact, meta) {
@@ -2541,7 +3589,7 @@ function keepForeignManifestEntryOperation(entry, targetRoot, workspaceOwner, op
2541
3589
  artifactType: entry.artifactType,
2542
3590
  artifactName: entry.artifactName,
2543
3591
  kind: entry.kind,
2544
- destPath: operation?.destPath ?? join8(targetRoot, entry.path),
3592
+ destPath: operation?.destPath ?? join15(targetRoot, entry.path),
2545
3593
  relativeDestPath: entry.path,
2546
3594
  desiredHash: entry.sourceHash,
2547
3595
  currentHash: currentHash ?? operation?.currentHash ?? entry.hash,
@@ -2550,8 +3598,10 @@ function keepForeignManifestEntryOperation(entry, targetRoot, workspaceOwner, op
2550
3598
  channel: entry.channel,
2551
3599
  packageName: entry.packageName,
2552
3600
  semanticCommand: entry.semanticCommand,
3601
+ semanticPlugin: entry.semanticPlugin,
2553
3602
  execute: entry.executed,
2554
3603
  mergeStrategy: entry.mergeStrategy,
3604
+ mode: entry.mode,
2555
3605
  composedFrom: entry.composedFrom,
2556
3606
  preserveInManifest: true,
2557
3607
  ...operationMetadataFromEntry(entry)
@@ -2563,7 +3613,7 @@ function normalizeOperationOwners(op) {
2563
3613
  function isGuardedMergeTarget(type) {
2564
3614
  return type === "mcp" || type === "hooks" || type === "settings" || type === "plugins";
2565
3615
  }
2566
- function operationForArtifact(artifact, adapter, targetRoot, installationType, meta) {
3616
+ async function operationForArtifact(artifact, adapter, targetRoot, installationType, meta) {
2567
3617
  if (artifact.type === "fragments") return void 0;
2568
3618
  const target = targetMappingForArtifact(adapter, artifact.type, installationType);
2569
3619
  if (!target?.enabled) {
@@ -2576,27 +3626,38 @@ function operationForArtifact(artifact, adapter, targetRoot, installationType, m
2576
3626
  assertSafeInstallName(rawInstallName, `${artifact.type}/${artifact.name}`);
2577
3627
  const installName = semanticInstallName(artifact, target.semantic, rawInstallName);
2578
3628
  assertSafeInstallName(installName, `${artifact.type}/${artifact.name}`);
2579
- if (artifact.type === "plugins" && target.semantic === "openclaw-plugin") {
3629
+ if (artifact.type === "plugins") {
2580
3630
  const sourcePath = artifact.stagedPath ?? artifact.sourcePath;
2581
- return {
2582
- action: "plugin",
2583
- artifactType: artifact.type,
2584
- artifactName: artifact.name,
2585
- kind: artifact.kind,
3631
+ const semanticPlugin = await semanticPluginSpecForArtifact({
3632
+ semantic: target.semantic,
3633
+ artifact,
3634
+ installName,
2586
3635
  sourcePath,
2587
- destPath: targetRoot,
2588
- relativeDestPath: `plugins/${installName}`,
2589
- desiredHash: artifact.hash,
2590
- reason: "semantic plugin install planned",
2591
- channel: artifact.channel ?? "managed",
2592
- packageName: artifact.packageName,
2593
- semanticCommand: openClawPluginInstallCommand({ path: sourcePath, dryRun: true }),
2594
- composedFrom: metadata.composedFrom,
2595
- ...metadata
2596
- };
3636
+ targetRoot,
3637
+ installationType
3638
+ });
3639
+ if (semanticPlugin) {
3640
+ return {
3641
+ action: "plugin",
3642
+ artifactType: artifact.type,
3643
+ artifactName: artifact.name,
3644
+ kind: artifact.kind,
3645
+ sourcePath,
3646
+ destPath: targetRoot,
3647
+ relativeDestPath: `plugins/${installName}`,
3648
+ desiredHash: artifact.hash,
3649
+ reason: "semantic plugin install planned",
3650
+ channel: artifact.channel ?? "managed",
3651
+ packageName: artifact.packageName,
3652
+ semanticCommand: semanticPlugin.installCommands[0],
3653
+ semanticPlugin,
3654
+ composedFrom: metadata.composedFrom,
3655
+ ...metadata
3656
+ };
3657
+ }
2597
3658
  }
2598
3659
  if (artifact.type === "subagents" && target.semantic === "codex-subagent") {
2599
- const destPath2 = join8(targetRoot, target.dest, `${installName.replace(/\.toml$/i, "")}.toml`);
3660
+ const destPath2 = join15(targetRoot, target.dest, `${installName.replace(/\.toml$/i, "")}.toml`);
2600
3661
  return {
2601
3662
  action: "create",
2602
3663
  artifactType: artifact.type,
@@ -2604,7 +3665,7 @@ function operationForArtifact(artifact, adapter, targetRoot, installationType, m
2604
3665
  kind: "file",
2605
3666
  sourcePath: artifact.stagedPath ?? artifact.sourcePath,
2606
3667
  destPath: destPath2,
2607
- relativeDestPath: relative2(targetRoot, destPath2).replaceAll("\\", "/"),
3668
+ relativeDestPath: relative3(targetRoot, destPath2).replaceAll("\\", "/"),
2608
3669
  desiredHash: artifact.hash,
2609
3670
  reason: "destination missing",
2610
3671
  channel: artifact.channel ?? "managed",
@@ -2614,7 +3675,7 @@ function operationForArtifact(artifact, adapter, targetRoot, installationType, m
2614
3675
  installName: installName.replace(/\.toml$/i, "")
2615
3676
  };
2616
3677
  }
2617
- const destPath = artifact.type === "instructions" || artifact.type === "settings" || isFileTarget(target.dest) ? join8(targetRoot, target.dest) : join8(targetRoot, target.dest, installName);
3678
+ const destPath = artifact.type === "instructions" || artifact.type === "settings" || isFileTarget(target.dest) ? join15(targetRoot, target.dest) : join15(targetRoot, target.dest, installName);
2618
3679
  return {
2619
3680
  action: "create",
2620
3681
  artifactType: artifact.type,
@@ -2622,12 +3683,13 @@ function operationForArtifact(artifact, adapter, targetRoot, installationType, m
2622
3683
  kind: artifact.kind,
2623
3684
  sourcePath: artifact.stagedPath ?? artifact.sourcePath,
2624
3685
  destPath,
2625
- relativeDestPath: relative2(targetRoot, destPath).replaceAll("\\", "/"),
3686
+ relativeDestPath: relative3(targetRoot, destPath).replaceAll("\\", "/"),
2626
3687
  desiredHash: artifact.hash,
2627
3688
  reason: "destination missing",
2628
3689
  channel: artifact.channel ?? "managed",
2629
3690
  packageName: artifact.packageName,
2630
3691
  mergeStrategy: target.merge,
3692
+ mode: target.mode,
2631
3693
  composedFrom: metadata.composedFrom,
2632
3694
  ...metadata,
2633
3695
  installName
@@ -2651,7 +3713,7 @@ function withExtension(name, targetExtension, knownExtensions) {
2651
3713
  return `${base}${targetExtension}`;
2652
3714
  }
2653
3715
  function isFileTarget(dest) {
2654
- return /\.(json|jsonc|toml|md)$/i.test(dest);
3716
+ return /\.(json|jsonc|ya?ml|toml|md)$/i.test(dest);
2655
3717
  }
2656
3718
  function reasonWithComposedDiff(reason, desired, current) {
2657
3719
  const changed = changedComposedSelectors(desired, current);
@@ -2692,13 +3754,14 @@ function isPendingInstallOperation(operation) {
2692
3754
  }
2693
3755
 
2694
3756
  // src/install/uninstall.ts
2695
- import { join as join9 } from "path";
3757
+ import { join as join16 } from "path";
2696
3758
  async function createUninstallPlan(manifest, transport = localTransport) {
2697
3759
  const operations = [];
2698
3760
  for (const entry of manifest.entries) {
2699
- const destPath = join9(manifest.targetRoot, entry.path);
2700
- if (!await transport.pathExists(destPath)) continue;
2701
- const currentHash = await transport.hashPath(destPath);
3761
+ const semanticPlugin = "semanticPlugin" in entry ? entry.semanticPlugin : void 0;
3762
+ const destPath = semanticPlugin ? manifest.targetRoot : join16(manifest.targetRoot, entry.path);
3763
+ if (!semanticPlugin && !await transport.pathExists(destPath)) continue;
3764
+ const currentHash = semanticPlugin ? entry.hash : await currentEntryHash2(entry, destPath, transport);
2702
3765
  if (currentHash !== entry.hash) {
2703
3766
  operations.push({
2704
3767
  action: "keep",
@@ -2714,7 +3777,10 @@ async function createUninstallPlan(manifest, transport = localTransport) {
2714
3777
  channel: entry.channel,
2715
3778
  packageName: entry.packageName,
2716
3779
  semanticCommand: entry.semanticCommand,
3780
+ semanticPlugin,
3781
+ execute: entry.executed,
2717
3782
  mergeStrategy: entry.mergeStrategy,
3783
+ mode: entry.mode,
2718
3784
  composedFrom: entry.composedFrom,
2719
3785
  ...operationMetadataFromEntry2(entry)
2720
3786
  });
@@ -2732,8 +3798,11 @@ async function createUninstallPlan(manifest, transport = localTransport) {
2732
3798
  reason: "uninstall managed artifact",
2733
3799
  channel: entry.channel,
2734
3800
  packageName: entry.packageName,
2735
- semanticCommand: entry.semanticCommand,
3801
+ semanticCommand: semanticPlugin?.uninstallCommands[0] ?? entry.semanticCommand,
3802
+ semanticPlugin,
3803
+ execute: entry.executed,
2736
3804
  mergeStrategy: entry.mergeStrategy,
3805
+ mode: entry.mode,
2737
3806
  composedFrom: entry.composedFrom,
2738
3807
  ...operationMetadataFromEntry2(entry)
2739
3808
  });
@@ -2760,9 +3829,10 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
2760
3829
  );
2761
3830
  const operations = [];
2762
3831
  for (const entry of manifest.entries) {
2763
- const destPath = join9(manifest.targetRoot, entry.path);
2764
- if (!await transport.pathExists(destPath)) continue;
2765
- const currentHash = await transport.hashPath(destPath);
3832
+ const semanticPlugin = "semanticPlugin" in entry ? entry.semanticPlugin : void 0;
3833
+ const destPath = semanticPlugin ? manifest.targetRoot : join16(manifest.targetRoot, entry.path);
3834
+ if (!semanticPlugin && !await transport.pathExists(destPath)) continue;
3835
+ const currentHash = semanticPlugin ? entry.hash : await currentEntryHash2(entry, destPath, transport);
2766
3836
  const remainingOwners = ownersByPath.get(entry.path) ?? [];
2767
3837
  if (remainingOwners.length > 0) {
2768
3838
  operations.push({
@@ -2779,7 +3849,10 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
2779
3849
  channel: entry.channel,
2780
3850
  packageName: entry.packageName,
2781
3851
  semanticCommand: entry.semanticCommand,
3852
+ semanticPlugin,
3853
+ execute: entry.executed,
2782
3854
  mergeStrategy: entry.mergeStrategy,
3855
+ mode: entry.mode,
2783
3856
  composedFrom: entry.composedFrom,
2784
3857
  ...operationMetadataFromEntry2(entry, remainingOwners),
2785
3858
  graphLockDigest: options.graphLockDigest,
@@ -2802,7 +3875,10 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
2802
3875
  channel: entry.channel,
2803
3876
  packageName: entry.packageName,
2804
3877
  semanticCommand: entry.semanticCommand,
3878
+ semanticPlugin,
3879
+ execute: entry.executed,
2805
3880
  mergeStrategy: entry.mergeStrategy,
3881
+ mode: entry.mode,
2806
3882
  composedFrom: entry.composedFrom,
2807
3883
  ...operationMetadataFromEntry2(entry),
2808
3884
  graphLockDigest: options.graphLockDigest
@@ -2821,8 +3897,11 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
2821
3897
  reason: "owner set became empty",
2822
3898
  channel: entry.channel,
2823
3899
  packageName: entry.packageName,
2824
- semanticCommand: entry.semanticCommand,
3900
+ semanticCommand: semanticPlugin?.uninstallCommands[0] ?? entry.semanticCommand,
3901
+ semanticPlugin,
3902
+ execute: entry.executed,
2825
3903
  mergeStrategy: entry.mergeStrategy,
3904
+ mode: entry.mode,
2826
3905
  composedFrom: entry.composedFrom,
2827
3906
  ...operationMetadataFromEntry2(entry, [])
2828
3907
  });
@@ -2841,6 +3920,12 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
2841
3920
  graphLockDigest: options.graphLockDigest
2842
3921
  };
2843
3922
  }
3923
+ async function currentEntryHash2(entry, destPath, transport) {
3924
+ if (entry.mode !== managedInstructionBlockMode) return transport.hashPath(destPath);
3925
+ const selector = managedInstructionSelector("logicalSelector" in entry ? entry.logicalSelector : void 0, entry.artifactType, entry.artifactName);
3926
+ const state = await readManagedInstructionBlockState(destPath, selector, transport);
3927
+ return state.drifted ? state.markerHash : state.hash;
3928
+ }
2844
3929
  function operationMetadataFromEntry2(entry, ownersOverride) {
2845
3930
  if ("owners" in entry) {
2846
3931
  return {
@@ -2887,7 +3972,7 @@ function formatPlan(plan) {
2887
3972
  }
2888
3973
  for (const operation of sortedPlanOperations(plan.operations)) {
2889
3974
  const source = operation.sourcePath ? `${operation.sourcePath} -> ` : "";
2890
- const command = operation.semanticCommand ? ` :: ${operation.semanticCommand.join(" ")}` : "";
3975
+ const command = formatSemanticCommandSuffix(operation);
2891
3976
  const blocked = operation.blockedReason ? `; ${operation.blockedReason}` : "";
2892
3977
  lines.push(`${labels[operation.action].padEnd(8)} ${channelLabels[operation.channel].padEnd(8)} ${operation.artifactType}/${operation.artifactName} ${source}${operation.relativeDestPath} (${operation.reason}${blocked})${command}`);
2893
3978
  }
@@ -2897,6 +3982,18 @@ function formatPlan(plan) {
2897
3982
  );
2898
3983
  return lines.join("\n");
2899
3984
  }
3985
+ function formatSemanticCommandSuffix(operation) {
3986
+ const commands = semanticCommandsForOperation(operation);
3987
+ return commands.length > 0 ? ` :: ${commands.map((command) => command.join(" ")).join(" && ")}` : "";
3988
+ }
3989
+ function semanticCommandsForOperation(operation) {
3990
+ if (operation.semanticPlugin) {
3991
+ if (operation.action === "remove" && operation.execute === false) return [];
3992
+ if (operation.action === "remove") return operation.semanticPlugin.uninstallCommands;
3993
+ if (operation.action === "plugin") return operation.semanticPlugin.installCommands;
3994
+ }
3995
+ return operation.semanticCommand ? [operation.semanticCommand] : [];
3996
+ }
2900
3997
  function sortedPlanOperations(operations) {
2901
3998
  return [...operations].sort((a, b) => {
2902
3999
  const destructiveA = a.action === "remove" || a.action === "drift" || a.action === "conflict";
@@ -3053,15 +4150,15 @@ function ownerChains(lock, nodeId) {
3053
4150
 
3054
4151
  // src/source/git.ts
3055
4152
  import { execFile as execFile4 } from "child_process";
3056
- import { cp as cp2, mkdir as mkdir8, rename as rename3, rm as rm4, writeFile as writeFile9 } from "fs/promises";
4153
+ import { cp as cp2, mkdir as mkdir9, rename as rename3, rm as rm5, writeFile as writeFile11 } from "fs/promises";
3057
4154
  import { homedir as homedir2 } from "os";
3058
- import { basename as basename7, dirname as dirname9, join as join12, resolve as resolve6 } from "path";
4155
+ import { basename as basename9, dirname as dirname11, join as join19, resolve as resolve6 } from "path";
3059
4156
  import { promisify as promisify4 } from "util";
3060
4157
 
3061
4158
  // src/model/package.ts
3062
- import { readFile as readFile10 } from "fs/promises";
3063
- import { join as join10 } from "path";
3064
- import { parse as parse2, printParseErrorCode as printParseErrorCode2 } from "jsonc-parser";
4159
+ import { readFile as readFile14 } from "fs/promises";
4160
+ import { join as join17 } from "path";
4161
+ import { parse as parse3, printParseErrorCode as printParseErrorCode2 } from "jsonc-parser";
3065
4162
  import { z as z5 } from "zod";
3066
4163
  var legacyArtifactTypeSchema = z5.enum([
3067
4164
  "instructions",
@@ -3137,7 +4234,7 @@ var packageManifestNames = [...openPackManifestNames, ...legacyPackageManifestNa
3137
4234
  var warnedLegacyManifestPaths = /* @__PURE__ */ new Set();
3138
4235
  async function findPackageManifestPath(root, options = {}) {
3139
4236
  for (const name of packageManifestNames) {
3140
- const candidate = join10(root, name);
4237
+ const candidate = join17(root, name);
3141
4238
  if (!await pathExists(candidate)) continue;
3142
4239
  if (isLegacyPackageManifestName(name) && options.warnLegacy !== false && !warnedLegacyManifestPaths.has(candidate)) {
3143
4240
  warnedLegacyManifestPaths.add(candidate);
@@ -3150,9 +4247,9 @@ async function findPackageManifestPath(root, options = {}) {
3150
4247
  async function readPackageManifest(root) {
3151
4248
  const path = await findPackageManifestPath(root);
3152
4249
  if (!path) return void 0;
3153
- const content = await readFile10(path, "utf8");
4250
+ const content = await readFile14(path, "utf8");
3154
4251
  const errors = [];
3155
- const parsed = parse2(content, errors, { allowTrailingComma: true, disallowComments: false });
4252
+ const parsed = parse3(content, errors, { allowTrailingComma: true, disallowComments: false });
3156
4253
  if (errors.length > 0) {
3157
4254
  const details = errors.map((error) => `${printParseErrorCode2(error.error)} at offset ${error.offset}`).join(", ");
3158
4255
  throw new Error(`Invalid package manifest ${path}: ${details}`);
@@ -3160,7 +4257,7 @@ async function readPackageManifest(root) {
3160
4257
  return parsePackageManifest(parsed, path);
3161
4258
  }
3162
4259
  function parsePackageManifest(parsed, path = "package manifest") {
3163
- if (!isRecord4(parsed)) {
4260
+ if (!isRecord6(parsed)) {
3164
4261
  throw new Error(`Invalid package manifest ${path}: expected an object`);
3165
4262
  }
3166
4263
  if (parsed.schemaVersion === 1) {
@@ -3188,7 +4285,7 @@ function v1OpenPackViolations(manifest) {
3188
4285
  }
3189
4286
  const provides = Array.isArray(manifest.provides) ? manifest.provides : [];
3190
4287
  for (const [index, provide] of provides.entries()) {
3191
- if (!isRecord4(provide)) continue;
4288
+ if (!isRecord6(provide)) continue;
3192
4289
  if (provide.type === "fragments") violations.push(`provides[${index}].type=fragments`);
3193
4290
  for (const key of ["format", "items", "compose", "runtimes"]) {
3194
4291
  if (Object.prototype.hasOwnProperty.call(provide, key)) violations.push(`provides[${index}].${key}`);
@@ -3199,14 +4296,14 @@ function v1OpenPackViolations(manifest) {
3199
4296
  function isLegacyPackageManifestName(name) {
3200
4297
  return legacyPackageManifestNames.includes(name);
3201
4298
  }
3202
- function isRecord4(value) {
4299
+ function isRecord6(value) {
3203
4300
  return typeof value === "object" && value !== null && !Array.isArray(value);
3204
4301
  }
3205
4302
 
3206
4303
  // src/source/local.ts
3207
- import { createHash as createHash3 } from "crypto";
4304
+ import { createHash as createHash4 } from "crypto";
3208
4305
  import { readdir, stat as stat3 } from "fs/promises";
3209
- import { basename as basename6, join as join11, relative as relative3, resolve as resolve5 } from "path";
4306
+ import { basename as basename8, join as join18, relative as relative4, resolve as resolve5 } from "path";
3210
4307
  var LocalSourceDriver = class {
3211
4308
  name = "local";
3212
4309
  async resolve(source) {
@@ -3236,29 +4333,29 @@ var LocalSourceDriver = class {
3236
4333
  }
3237
4334
  const artifacts = [];
3238
4335
  const root = resolved.resolvedPath;
3239
- const instructions = await firstExisting([join11(root, "instructions.md"), join11(root, "AGENTS.md")]);
4336
+ const instructions = await firstExisting([join18(root, "instructions.md"), join18(root, "AGENTS.md")]);
3240
4337
  if (instructions) {
3241
4338
  artifacts.push({
3242
4339
  type: "instructions",
3243
- name: basename6(instructions),
4340
+ name: basename8(instructions),
3244
4341
  sourcePath: instructions,
3245
- relativePath: basename6(instructions),
4342
+ relativePath: basename8(instructions),
3246
4343
  kind: "file",
3247
4344
  hash: await hashPath(instructions),
3248
4345
  packageName: resolved.packageName,
3249
4346
  channel: "managed"
3250
4347
  });
3251
4348
  }
3252
- const rulesDir = join11(root, "rules");
4349
+ const rulesDir = join18(root, "rules");
3253
4350
  if (await pathExists(rulesDir)) {
3254
4351
  for (const entry of await sortedDirEntries(rulesDir)) {
3255
- const full = join11(rulesDir, entry.name);
4352
+ const full = join18(rulesDir, entry.name);
3256
4353
  if (entry.isFile()) {
3257
4354
  artifacts.push({
3258
4355
  type: "rules",
3259
4356
  name: entry.name,
3260
4357
  sourcePath: full,
3261
- relativePath: join11("rules", entry.name),
4358
+ relativePath: join18("rules", entry.name),
3262
4359
  kind: "file",
3263
4360
  hash: await hashPath(full),
3264
4361
  packageName: resolved.packageName,
@@ -3267,16 +4364,16 @@ var LocalSourceDriver = class {
3267
4364
  }
3268
4365
  }
3269
4366
  }
3270
- const fragmentsDir = join11(root, "fragments");
4367
+ const fragmentsDir = join18(root, "fragments");
3271
4368
  if (await pathExists(fragmentsDir)) {
3272
4369
  for (const entry of await sortedDirEntries(fragmentsDir)) {
3273
- const full = join11(fragmentsDir, entry.name);
4370
+ const full = join18(fragmentsDir, entry.name);
3274
4371
  if (entry.isFile()) {
3275
4372
  artifacts.push({
3276
4373
  type: "fragments",
3277
4374
  name: entry.name,
3278
4375
  sourcePath: full,
3279
- relativePath: join11("fragments", entry.name),
4376
+ relativePath: join18("fragments", entry.name),
3280
4377
  kind: "file",
3281
4378
  hash: await hashPath(full),
3282
4379
  packageName: resolved.packageName,
@@ -3285,16 +4382,16 @@ var LocalSourceDriver = class {
3285
4382
  }
3286
4383
  }
3287
4384
  }
3288
- const skillsDir = join11(root, "skills");
4385
+ const skillsDir = join18(root, "skills");
3289
4386
  if (await pathExists(skillsDir)) {
3290
4387
  for (const entry of await sortedDirEntries(skillsDir)) {
3291
- const full = join11(skillsDir, entry.name);
4388
+ const full = join18(skillsDir, entry.name);
3292
4389
  if (entry.isDirectory()) {
3293
4390
  artifacts.push({
3294
4391
  type: "skills",
3295
4392
  name: entry.name,
3296
4393
  sourcePath: full,
3297
- relativePath: join11("skills", entry.name),
4394
+ relativePath: join18("skills", entry.name),
3298
4395
  kind: "dir",
3299
4396
  hash: await hashPath(full),
3300
4397
  packageName: resolved.packageName,
@@ -3305,7 +4402,7 @@ var LocalSourceDriver = class {
3305
4402
  type: "skills",
3306
4403
  name: entry.name.replace(/\.md$/, ""),
3307
4404
  sourcePath: full,
3308
- relativePath: join11("skills", entry.name),
4405
+ relativePath: join18("skills", entry.name),
3309
4406
  kind: "file",
3310
4407
  hash: await hashPath(full),
3311
4408
  packageName: resolved.packageName,
@@ -3315,7 +4412,7 @@ var LocalSourceDriver = class {
3315
4412
  }
3316
4413
  }
3317
4414
  for (const type of ["commands", "subagents", "mcp", "hooks", "settings", "plugins"]) {
3318
- const dir = join11(root, type);
4415
+ const dir = join18(root, type);
3319
4416
  if (!await pathExists(dir)) continue;
3320
4417
  artifacts.push(...await listGenericArtifacts(type, dir, type, resolved.packageName));
3321
4418
  }
@@ -3331,7 +4428,7 @@ var LocalSourceDriver = class {
3331
4428
  findings.push({ level: "warning", message: "No instructions.md or AGENTS.md found", path: resolved.resolvedPath });
3332
4429
  }
3333
4430
  for (const artifact of artifacts.filter((item) => item.type === "skills" && item.kind === "dir")) {
3334
- if (!await pathExists(join11(artifact.sourcePath, "SKILL.md"))) {
4431
+ if (!await pathExists(join18(artifact.sourcePath, "SKILL.md"))) {
3335
4432
  findings.push({ level: "warning", message: `Skill directory has no SKILL.md: ${artifact.name}`, path: artifact.sourcePath });
3336
4433
  }
3337
4434
  }
@@ -3346,16 +4443,16 @@ var LocalSourceDriver = class {
3346
4443
  };
3347
4444
  async function hashLocalSource(root, manifest) {
3348
4445
  if (!manifest) return hashPath(root);
3349
- const hash = createHash3("sha256").update("local-openpack\0");
4446
+ const hash = createHash4("sha256").update("local-openpack\0");
3350
4447
  const manifestPath = await findPackageManifestPath(root, { warnLegacy: false });
3351
4448
  if (manifestPath) {
3352
4449
  hash.update("manifest\0");
3353
- hash.update(relative3(root, manifestPath).replaceAll("\\", "/")).update("\0");
4450
+ hash.update(relative4(root, manifestPath).replaceAll("\\", "/")).update("\0");
3354
4451
  hash.update(await hashPath(manifestPath)).update("\0");
3355
4452
  }
3356
4453
  const provides = [...manifest.provides].sort((a, b) => `${a.type}\0${a.path}`.localeCompare(`${b.type}\0${b.path}`));
3357
4454
  for (const provide of provides) {
3358
- const full = join11(root, provide.path);
4455
+ const full = join18(root, provide.path);
3359
4456
  if (!await pathExists(full)) continue;
3360
4457
  hash.update("provide\0");
3361
4458
  hash.update(provide.type).update("\0");
@@ -3378,27 +4475,27 @@ async function listFromManifest(root, packageName) {
3378
4475
  if (!manifest) return [];
3379
4476
  const artifacts = [];
3380
4477
  for (const provide of manifest.provides) {
3381
- const full = join11(root, provide.path);
4478
+ const full = join18(root, provide.path);
3382
4479
  if (!await pathExists(full)) continue;
3383
4480
  const stats = await stat3(full);
3384
4481
  if (provide.type === "instructions") {
3385
4482
  if (stats.isFile()) {
3386
- artifacts.push(await artifactForFile(provide.type, basename6(full), full, provide.path, packageName, provide, manifest, basename6(full)));
4483
+ artifacts.push(await artifactForFile(provide.type, basename8(full), full, provide.path, packageName, provide, manifest, basename8(full)));
3387
4484
  }
3388
4485
  continue;
3389
4486
  }
3390
4487
  if (stats.isDirectory()) {
3391
4488
  for (const entry of await sortedDirEntries(full)) {
3392
- const child = join11(full, entry.name);
4489
+ const child = join18(full, entry.name);
3393
4490
  if ((provide.type === "skills" || provide.type === "plugins" || provide.type === "subagents") && entry.isDirectory()) {
3394
- artifacts.push(await artifactForDir(provide.type, entry.name, child, join11(provide.path, entry.name), packageName, provide, manifest, entry.name));
4491
+ artifacts.push(await artifactForDir(provide.type, entry.name, child, join18(provide.path, entry.name), packageName, provide, manifest, entry.name));
3395
4492
  } else if (entry.isFile()) {
3396
4493
  const name = provide.type === "rules" && entry.name.endsWith(".md") ? entry.name : entry.name;
3397
- artifacts.push(await artifactForFile(provide.type, name, child, join11(provide.path, entry.name), packageName, provide, manifest, name));
4494
+ artifacts.push(await artifactForFile(provide.type, name, child, join18(provide.path, entry.name), packageName, provide, manifest, name));
3398
4495
  }
3399
4496
  }
3400
4497
  } else if (stats.isFile()) {
3401
- artifacts.push(await artifactForFile(provide.type, basename6(full), full, provide.path, packageName, provide, manifest, basename6(full)));
4498
+ artifacts.push(await artifactForFile(provide.type, basename8(full), full, provide.path, packageName, provide, manifest, basename8(full)));
3402
4499
  }
3403
4500
  }
3404
4501
  return artifacts;
@@ -3406,11 +4503,11 @@ async function listFromManifest(root, packageName) {
3406
4503
  async function listGenericArtifacts(type, dir, relativeRoot, packageName) {
3407
4504
  const artifacts = [];
3408
4505
  for (const entry of await sortedDirEntries(dir)) {
3409
- const full = join11(dir, entry.name);
4506
+ const full = join18(dir, entry.name);
3410
4507
  if (entry.isDirectory()) {
3411
- artifacts.push(await artifactForDir(type, entry.name, full, join11(relativeRoot, entry.name), packageName));
4508
+ artifacts.push(await artifactForDir(type, entry.name, full, join18(relativeRoot, entry.name), packageName));
3412
4509
  } else if (entry.isFile()) {
3413
- artifacts.push(await artifactForFile(type, entry.name, full, join11(relativeRoot, entry.name), packageName));
4510
+ artifacts.push(await artifactForFile(type, entry.name, full, join18(relativeRoot, entry.name), packageName));
3414
4511
  }
3415
4512
  }
3416
4513
  return artifacts;
@@ -3488,12 +4585,12 @@ var GitSourceDriver = class {
3488
4585
  async fetch(resolved) {
3489
4586
  return withFilesystemLock(`${resolved.resolvedPath}.lock`, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
3490
4587
  const parsed = parseGitSource(resolved.source);
3491
- await mkdir8(resolve6(resolved.resolvedPath, ".."), { recursive: true });
3492
- if (!await pathExists(join12(resolved.resolvedPath, ".git"))) {
4588
+ await mkdir9(resolve6(resolved.resolvedPath, ".."), { recursive: true });
4589
+ if (!await pathExists(join19(resolved.resolvedPath, ".git"))) {
3493
4590
  if (resolved.frozenLock) {
3494
4591
  throw new Error(`Frozen lock requires cached git checkout at ${resolved.resolvedPath}`);
3495
4592
  }
3496
- await rm4(resolved.resolvedPath, { recursive: true, force: true });
4593
+ await rm5(resolved.resolvedPath, { recursive: true, force: true });
3497
4594
  await git(["clone", "--no-tags", parsed.url, resolved.resolvedPath]);
3498
4595
  } else if (!resolved.frozenLock) {
3499
4596
  await git(["-C", resolved.resolvedPath, "fetch", "--prune", "origin"]);
@@ -3556,36 +4653,36 @@ function parseGitSource(source) {
3556
4653
  throw new Error(`Invalid git source: ${source}`);
3557
4654
  }
3558
4655
  function cachePathFor(url, cacheRoot) {
3559
- const root = cacheRoot ? resolve6(cacheRoot) : join12(homedir2(), ".agentwheel", "cache");
4656
+ const root = cacheRoot ? resolve6(cacheRoot) : join19(homedir2(), ".agentwheel", "cache");
3560
4657
  const slug2 = url.replace(/^[a-z]+:\/\//i, "").replace(/\.git$/i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
3561
- return join12(root, slug2 || basename7(url));
4658
+ return join19(root, slug2 || basename9(url));
3562
4659
  }
3563
4660
  async function git(args) {
3564
4661
  return execFileAsync4("git", args, { maxBuffer: 1024 * 1024 * 10 });
3565
4662
  }
3566
4663
  async function snapshotCheckout(checkoutPath, commit) {
3567
- const snapshotPath = join12(dirname9(checkoutPath), `${basename7(checkoutPath)}-${commit.slice(0, 12)}`);
4664
+ const snapshotPath = join19(dirname11(checkoutPath), `${basename9(checkoutPath)}-${commit.slice(0, 12)}`);
3568
4665
  if (await pathExists(snapshotPath)) return snapshotPath;
3569
- const tempPath = join12(dirname9(checkoutPath), `${basename7(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
3570
- await rm4(tempPath, { recursive: true, force: true });
4666
+ const tempPath = join19(dirname11(checkoutPath), `${basename9(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
4667
+ await rm5(tempPath, { recursive: true, force: true });
3571
4668
  await cp2(checkoutPath, tempPath, { recursive: true, dereference: true });
3572
- await rm4(join12(tempPath, ".git"), { recursive: true, force: true });
4669
+ await rm5(join19(tempPath, ".git"), { recursive: true, force: true });
3573
4670
  try {
3574
4671
  await rename3(tempPath, snapshotPath);
3575
4672
  } catch (error) {
3576
4673
  if (!isAlreadyExists2(error)) throw error;
3577
- await rm4(tempPath, { recursive: true, force: true });
4674
+ await rm5(tempPath, { recursive: true, force: true });
3578
4675
  return snapshotPath;
3579
4676
  }
3580
4677
  return snapshotPath;
3581
4678
  }
3582
4679
  async function withFilesystemLock(lockPath, timeoutMs, fn) {
3583
- await mkdir8(dirname9(lockPath), { recursive: true });
4680
+ await mkdir9(dirname11(lockPath), { recursive: true });
3584
4681
  const started = Date.now();
3585
4682
  while (true) {
3586
4683
  try {
3587
- await mkdir8(lockPath);
3588
- await writeFile9(join12(lockPath, "owner.json"), JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
4684
+ await mkdir9(lockPath);
4685
+ await writeFile11(join19(lockPath, "owner.json"), JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
3589
4686
  break;
3590
4687
  } catch (error) {
3591
4688
  if (!isAlreadyExists2(error)) throw error;
@@ -3598,7 +4695,7 @@ async function withFilesystemLock(lockPath, timeoutMs, fn) {
3598
4695
  try {
3599
4696
  return await fn();
3600
4697
  } finally {
3601
- await rm4(lockPath, { recursive: true, force: true });
4698
+ await rm5(lockPath, { recursive: true, force: true });
3602
4699
  }
3603
4700
  }
3604
4701
  function isAlreadyExists2(error) {
@@ -3606,14 +4703,14 @@ function isAlreadyExists2(error) {
3606
4703
  }
3607
4704
 
3608
4705
  // src/source/skillkit.ts
3609
- import { cp as cp3, mkdir as mkdir9, readFile as readFile11, rm as rm5 } from "fs/promises";
4706
+ import { cp as cp3, mkdir as mkdir10, readFile as readFile15, rm as rm6 } from "fs/promises";
3610
4707
  import { homedir as homedir3 } from "os";
3611
- import { basename as basename9, dirname as dirname11, join as join14, resolve as resolve7 } from "path";
4708
+ import { basename as basename11, dirname as dirname13, join as join21, resolve as resolve7 } from "path";
3612
4709
  import * as defaultSkillKit from "@skillkit/core";
3613
4710
 
3614
4711
  // src/source/skill-artifacts.ts
3615
4712
  import { readdir as readdir2, stat as stat4 } from "fs/promises";
3616
- import { basename as basename8, dirname as dirname10, extname as extname2, join as join13 } from "path";
4713
+ import { basename as basename10, dirname as dirname12, extname as extname2, join as join20 } from "path";
3617
4714
  async function artifactsFromSkillPaths(paths, packageName) {
3618
4715
  const artifacts = [];
3619
4716
  const seen = /* @__PURE__ */ new Set();
@@ -3635,28 +4732,28 @@ async function discoverSkillPaths(root) {
3635
4732
  async function artifactFromSkillPath(item, packageName) {
3636
4733
  const stats = await stat4(item.path);
3637
4734
  if (stats.isDirectory()) {
3638
- const skillMd = join13(item.path, "SKILL.md");
4735
+ const skillMd = join20(item.path, "SKILL.md");
3639
4736
  if (!await pathExists(skillMd)) return void 0;
3640
- const name = sanitizeSkillName(item.name ?? basename8(item.path));
4737
+ const name = sanitizeSkillName(item.name ?? basename10(item.path));
3641
4738
  return {
3642
4739
  type: "skills",
3643
4740
  name,
3644
4741
  sourcePath: item.path,
3645
- relativePath: join13("skills", name),
4742
+ relativePath: join20("skills", name),
3646
4743
  kind: "dir",
3647
4744
  hash: await hashPath(item.path),
3648
4745
  packageName,
3649
4746
  channel: "managed"
3650
4747
  };
3651
4748
  }
3652
- if (stats.isFile() && basename8(item.path).toLowerCase() === "skill.md") {
3653
- const dir = dirname10(item.path);
3654
- const name = sanitizeSkillName(item.name ?? basename8(dir));
4749
+ if (stats.isFile() && basename10(item.path).toLowerCase() === "skill.md") {
4750
+ const dir = dirname12(item.path);
4751
+ const name = sanitizeSkillName(item.name ?? basename10(dir));
3655
4752
  return {
3656
4753
  type: "skills",
3657
4754
  name,
3658
4755
  sourcePath: dir,
3659
- relativePath: join13("skills", name),
4756
+ relativePath: join20("skills", name),
3660
4757
  kind: "dir",
3661
4758
  hash: await hashPath(dir),
3662
4759
  packageName,
@@ -3664,12 +4761,12 @@ async function artifactFromSkillPath(item, packageName) {
3664
4761
  };
3665
4762
  }
3666
4763
  if (stats.isFile() && extname2(item.path).toLowerCase() === ".md") {
3667
- const name = sanitizeSkillName(item.name ?? basename8(item.path, ".md"));
4764
+ const name = sanitizeSkillName(item.name ?? basename10(item.path, ".md"));
3668
4765
  return {
3669
4766
  type: "skills",
3670
4767
  name,
3671
4768
  sourcePath: item.path,
3672
- relativePath: join13("skills", `${name}.md`),
4769
+ relativePath: join20("skills", `${name}.md`),
3673
4770
  kind: "file",
3674
4771
  hash: await hashPath(item.path),
3675
4772
  packageName,
@@ -3687,7 +4784,7 @@ async function walk(dir, paths) {
3687
4784
  }
3688
4785
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
3689
4786
  if (!entry.isDirectory() || entry.name === ".git" || entry.name === "node_modules") continue;
3690
- await walk(join13(dir, entry.name), paths);
4787
+ await walk(join20(dir, entry.name), paths);
3691
4788
  }
3692
4789
  }
3693
4790
  function sanitizeSkillName(name) {
@@ -3709,7 +4806,7 @@ var SkillKitSourceDriver = class {
3709
4806
  driver: this.name,
3710
4807
  source,
3711
4808
  resolvedPath,
3712
- packageName: `skillkit/${basename9(resolvedPath)}`,
4809
+ packageName: `skillkit/${basename11(resolvedPath)}`,
3713
4810
  mode: options.mode ?? "pinned",
3714
4811
  sourceHash: await hashPath(resolvedPath)
3715
4812
  };
@@ -3743,17 +4840,17 @@ var SkillKitSourceDriver = class {
3743
4840
  if (!provider?.clone) {
3744
4841
  throw new Error("SkillKit provider API unavailable or cannot resolve source. Expected @skillkit/core detectProvider().clone().");
3745
4842
  }
3746
- await mkdir9(dirname11(resolved.resolvedPath), { recursive: true });
4843
+ await mkdir10(dirname13(resolved.resolvedPath), { recursive: true });
3747
4844
  const result = await provider.clone(providerSpec, resolved.resolvedPath, {});
3748
4845
  if (!result.success || !result.path) {
3749
4846
  throw new Error(`SkillKit provider failed to fetch ${spec}: ${result.error ?? "unknown error"}`);
3750
4847
  }
3751
4848
  if (resolve7(result.path) !== resolve7(resolved.resolvedPath)) {
3752
- await rm5(resolved.resolvedPath, { recursive: true, force: true });
4849
+ await rm6(resolved.resolvedPath, { recursive: true, force: true });
3753
4850
  await cp3(result.path, resolved.resolvedPath, { recursive: true, dereference: true });
3754
4851
  }
3755
4852
  if (result.tempRoot) {
3756
- await rm5(result.tempRoot, { recursive: true, force: true });
4853
+ await rm6(result.tempRoot, { recursive: true, force: true });
3757
4854
  }
3758
4855
  return {
3759
4856
  ...resolved,
@@ -3787,9 +4884,9 @@ var SkillKitSourceDriver = class {
3787
4884
  throw new Error("SkillKit translateSkill API unavailable");
3788
4885
  }
3789
4886
  for (const skill of this.discover(resolved.resolvedPath)) {
3790
- const skillMd = join14(skill.path, "SKILL.md");
4887
+ const skillMd = join21(skill.path, "SKILL.md");
3791
4888
  if (await pathExists(skillMd)) {
3792
- this.core.translateSkill(await readFile11(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
4889
+ this.core.translateSkill(await readFile15(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
3793
4890
  }
3794
4891
  }
3795
4892
  return resolved;
@@ -3818,8 +4915,8 @@ function normalizeProviderSource(spec) {
3818
4915
  return spec;
3819
4916
  }
3820
4917
  function cachePathFor2(spec, cacheRoot) {
3821
- const root = cacheRoot ? resolve7(cacheRoot) : join14(homedir3(), ".agentwheel", "cache");
3822
- return join14(root, "skillkit", packageSlug(spec));
4918
+ const root = cacheRoot ? resolve7(cacheRoot) : join21(homedir3(), ".agentwheel", "cache");
4919
+ return join21(root, "skillkit", packageSlug(spec));
3823
4920
  }
3824
4921
  function packageSlug(spec) {
3825
4922
  return spec.replace(/^[a-z]+:\/\//i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "source";
@@ -3832,7 +4929,7 @@ function mapSeverity(severity) {
3832
4929
 
3833
4930
  // src/source/vercel-skills.ts
3834
4931
  import { stat as stat5 } from "fs/promises";
3835
- import { basename as basename10, join as join15, resolve as resolve8 } from "path";
4932
+ import { basename as basename12, join as join22, resolve as resolve8 } from "path";
3836
4933
  var VercelSkillsSourceDriver = class {
3837
4934
  name = "vercel-skills";
3838
4935
  git = new GitSourceDriver();
@@ -3847,7 +4944,7 @@ var VercelSkillsSourceDriver = class {
3847
4944
  driver: this.name,
3848
4945
  source,
3849
4946
  resolvedPath,
3850
- packageName: `vercel/${basename10(resolvedPath)}`,
4947
+ packageName: `vercel/${basename12(resolvedPath)}`,
3851
4948
  mode: options.mode ?? "pinned",
3852
4949
  sourceHash: await hashPath(resolvedPath)
3853
4950
  };
@@ -3868,7 +4965,7 @@ var VercelSkillsSourceDriver = class {
3868
4965
  driver: "git",
3869
4966
  source: parsed.gitSource
3870
4967
  });
3871
- const resolvedPath = parsed.subpath ? join15(fetched.resolvedPath, parsed.subpath) : fetched.resolvedPath;
4968
+ const resolvedPath = parsed.subpath ? join22(fetched.resolvedPath, parsed.subpath) : fetched.resolvedPath;
3872
4969
  if (!await pathExists(resolvedPath)) {
3873
4970
  throw new Error(`Vercel skills subpath not found: ${parsed.subpath}`);
3874
4971
  }
@@ -3957,14 +5054,14 @@ function getSourceDriver(name = "local") {
3957
5054
  }
3958
5055
 
3959
5056
  // src/staging/staging.ts
3960
- import { chmod, cp as cp5, mkdir as mkdir11, mkdtemp as mkdtemp2, readdir as readdir5, stat as stat8 } from "fs/promises";
3961
- import { basename as basename13, dirname as dirname14, join as join18, relative as relative5, resolve as resolve10, sep as sep2 } from "path";
3962
- import { tmpdir as tmpdir3 } from "os";
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";
5059
+ import { tmpdir as tmpdir4 } from "os";
3963
5060
 
3964
5061
  // src/compose/markdown.ts
3965
- import { createHash as createHash4 } from "crypto";
3966
- import { readdir as readdir3, readFile as readFile12, stat as stat6, writeFile as writeFile10 } from "fs/promises";
3967
- import { basename as basename11, dirname as dirname12, extname as extname3, join as join16, relative as relative4, resolve as resolve9, sep } from "path";
5062
+ 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";
3968
5065
  var includePattern = /<!--\s*openpack:include(\?)?\s+([^>]+?)\s*-->/g;
3969
5066
  var escapedIncludePattern = /<!--\s*openpack\\:include(\?)?\s+([^>]+?)\s*-->/g;
3970
5067
  var generatedPattern = /<!--\s*(?:BEGIN|END)\s+openpack:include\b/;
@@ -3979,7 +5076,7 @@ async function expandMarkdownIncludes(artifacts, packageRoot, options = {}) {
3979
5076
  const composedFrom = [];
3980
5077
  for (const file of files) {
3981
5078
  const result = await expandFile(file, packageRoot, composeEntriesForFile(artifact, file), artifactPaths, options);
3982
- if (result.changed) await writeFile10(file, result.content, "utf8");
5079
+ if (result.changed) await writeFile12(file, result.content, "utf8");
3983
5080
  composedFrom.push(...result.composedFrom);
3984
5081
  }
3985
5082
  const stagedPath = artifact.stagedPath ?? artifact.sourcePath;
@@ -4001,7 +5098,7 @@ async function validateMarkdownIncludes(artifacts, packageRoot, options = {}) {
4001
5098
  }
4002
5099
  }
4003
5100
  async function expandFile(file, packageRoot, appendEntries, artifactPaths, options) {
4004
- const raw = await readFile12(file, "utf8");
5101
+ const raw = await readFile16(file, "utf8");
4005
5102
  const owner = ownerSelector(packageRoot, file, options.nodeId);
4006
5103
  const expanded = await expandContent(raw, packageRoot, [owner], artifactPaths, options);
4007
5104
  let content = expanded.content;
@@ -4094,7 +5191,7 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
4094
5191
  if (!stats.isFile()) {
4095
5192
  throw new Error(`OpenPack include is not a file: ${displaySelector}`);
4096
5193
  }
4097
- const raw = sourceContent ?? await readFile12(sourcePath, "utf8");
5194
+ const raw = sourceContent ?? await readFile16(sourcePath, "utf8");
4098
5195
  const { optional: _optional, markers: _markers, chain: _chain, ...childOptions } = options;
4099
5196
  const expanded = await expandContent(raw, includePackageRoot, [...options.chain, displaySelector], includeArtifactPaths, {
4100
5197
  ...childOptions,
@@ -4170,7 +5267,7 @@ async function listMarkdownFiles(root) {
4170
5267
  const out = [];
4171
5268
  async function walk2(dir) {
4172
5269
  for (const entry of (await readdir3(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
4173
- const full = join16(dir, entry.name);
5270
+ const full = join23(dir, entry.name);
4174
5271
  if (entry.isDirectory()) {
4175
5272
  await walk2(full);
4176
5273
  } else if (entry.isFile() && extname3(entry.name).toLowerCase() === ".md") {
@@ -4184,7 +5281,7 @@ async function listMarkdownFiles(root) {
4184
5281
  function composeEntriesForFile(artifact, file) {
4185
5282
  if (!artifact.compose?.length) return [];
4186
5283
  if (artifact.kind === "file") return [resolve9(artifact.stagedPath ?? artifact.sourcePath), resolve9(file)].every(Boolean) && resolve9(artifact.stagedPath ?? artifact.sourcePath) === resolve9(file) ? artifact.compose : [];
4187
- return basename11(file) === "SKILL.md" && dirname12(file) === resolve9(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
5284
+ return basename13(file) === "SKILL.md" && dirname14(file) === resolve9(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
4188
5285
  }
4189
5286
  function orderedForExpansion(artifacts) {
4190
5287
  return [...artifacts].sort((a, b) => Number(a.type === "fragments") - Number(b.type === "fragments"));
@@ -4201,7 +5298,7 @@ function applyReplacements(content, replacements) {
4201
5298
  return out + content.slice(cursor);
4202
5299
  }
4203
5300
  function relativeSelector(root, file) {
4204
- return relative4(root, file).replaceAll("\\", "/");
5301
+ return relative5(root, file).replaceAll("\\", "/");
4205
5302
  }
4206
5303
  function ownerSelector(root, file, nodeId) {
4207
5304
  const selector = relativeSelector(root, file);
@@ -4215,7 +5312,7 @@ function cleanSelector(value) {
4215
5312
  return value.trim().replace(/\s+/g, " ");
4216
5313
  }
4217
5314
  function sha256(content) {
4218
- return createHash4("sha256").update(content).digest("hex");
5315
+ return createHash5("sha256").update(content).digest("hex");
4219
5316
  }
4220
5317
  function uniqueComposedFrom(entries) {
4221
5318
  if (entries.length === 0) return [];
@@ -4230,8 +5327,8 @@ function artifactPathMap(artifacts) {
4230
5327
  }
4231
5328
 
4232
5329
  // src/staging/customize.ts
4233
- import { cp as cp4, mkdir as mkdir10, readdir as readdir4, readFile as readFile13, writeFile as writeFile11 } from "fs/promises";
4234
- import { dirname as dirname13, join as join17 } from "path";
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";
4235
5332
  async function applyCustomizations(artifacts, options) {
4236
5333
  let next = [...artifacts];
4237
5334
  next = await applyReplacements2(next, options, "override", installableArtifactTypes());
@@ -4247,16 +5344,16 @@ async function applyFragmentCustomizations(artifacts, options) {
4247
5344
  return next.sort((a, b) => `${a.type}:${a.name}:${a.channel}`.localeCompare(`${b.type}:${b.name}:${b.channel}`));
4248
5345
  }
4249
5346
  async function applyInstructionOverlay(artifacts, options) {
4250
- const overlayPath = join17(options.workspaceRoot, ".agentwheel", "overlays", options.adapter.name, "instructions.local.md");
5347
+ const overlayPath = join24(options.workspaceRoot, ".agentwheel", "overlays", options.adapter.name, "instructions.local.md");
4251
5348
  if (!await pathExists(overlayPath)) return artifacts;
4252
5349
  const index = artifacts.findIndex((artifact2) => artifact2.type === "instructions");
4253
5350
  if (index < 0) return artifacts;
4254
5351
  const artifact = artifacts[index];
4255
- const managed = await readFile13(artifact.stagedPath ?? artifact.sourcePath, "utf8");
4256
- const local = await readFile13(overlayPath, "utf8");
4257
- const composedPath = join17(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
4258
- await mkdir10(dirname13(composedPath), { recursive: true });
4259
- await writeFile11(
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(
4260
5357
  composedPath,
4261
5358
  [
4262
5359
  "<!-- BEGIN agentwheel managed: upstream -->",
@@ -4282,19 +5379,19 @@ async function applyInstructionOverlay(artifacts, options) {
4282
5379
  return [...artifacts.slice(0, index), updated, ...artifacts.slice(index + 1)];
4283
5380
  }
4284
5381
  async function applyAdditions(artifacts, options) {
4285
- const additionsRoot = join17(options.workspaceRoot, ".agentwheel", "additions");
4286
- const rulesRoot = join17(additionsRoot, "rules");
5382
+ const additionsRoot = join24(options.workspaceRoot, ".agentwheel", "additions");
5383
+ const rulesRoot = join24(additionsRoot, "rules");
4287
5384
  if (!await pathExists(rulesRoot)) return artifacts;
4288
5385
  const additions = [];
4289
5386
  for (const entry of await sortedDirEntries2(rulesRoot)) {
4290
- const full = join17(rulesRoot, entry.name);
5387
+ const full = join24(rulesRoot, entry.name);
4291
5388
  if (!entry.isFile()) continue;
4292
5389
  additions.push({
4293
5390
  type: "rules",
4294
5391
  name: entry.name,
4295
5392
  sourcePath: full,
4296
5393
  stagedPath: full,
4297
- relativePath: join17("additions", "rules", entry.name),
5394
+ relativePath: join24("additions", "rules", entry.name),
4298
5395
  kind: "file",
4299
5396
  hash: await hashPath(full),
4300
5397
  packageName: options.packageName,
@@ -4318,17 +5415,17 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
4318
5415
  );
4319
5416
  }
4320
5417
  for (const type of artifactTypes) {
4321
- const typeRoot = join17(root, type);
5418
+ const typeRoot = join24(root, type);
4322
5419
  if (!await pathExists(typeRoot)) continue;
4323
5420
  for (const entry of await sortedDirEntries2(typeRoot)) {
4324
5421
  const artifactMapKey = `${type}:${entry.name}`;
4325
5422
  if (seen.has(artifactMapKey)) continue;
4326
5423
  seen.add(artifactMapKey);
4327
- const full = join17(typeRoot, entry.name);
5424
+ const full = join24(typeRoot, entry.name);
4328
5425
  const artifactKind = entry.isDirectory() ? "dir" : "file";
4329
5426
  const existing = byKey.get(artifactMapKey);
4330
- const stagedPath = join17(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
4331
- await mkdir10(dirname13(stagedPath), { recursive: true });
5427
+ const stagedPath = join24(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
5428
+ await mkdir11(dirname15(stagedPath), { recursive: true });
4332
5429
  await cp4(full, stagedPath, { recursive: artifactKind === "dir", dereference: true });
4333
5430
  byKey.set(artifactMapKey, {
4334
5431
  ...existing,
@@ -4336,7 +5433,7 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
4336
5433
  name: entry.name,
4337
5434
  sourcePath: full,
4338
5435
  stagedPath,
4339
- relativePath: existing?.relativePath ?? join17(type, entry.name),
5436
+ relativePath: existing?.relativePath ?? join24(type, entry.name),
4340
5437
  kind: artifactKind,
4341
5438
  hash: await hashPath(stagedPath),
4342
5439
  packageName,
@@ -4351,13 +5448,13 @@ function replacementRoots(options, channel) {
4351
5448
  const stateDir = channel === "override" ? "overrides" : "ejected";
4352
5449
  const roots = [];
4353
5450
  if (options.graphNodeId) {
4354
- roots.push({ root: join17(options.workspaceRoot, ".agentwheel", stateDir, ...options.graphNodeId.split("/")), kind: "node" });
5451
+ roots.push({ root: join24(options.workspaceRoot, ".agentwheel", stateDir, ...options.graphNodeId.split("/")), kind: "node" });
4355
5452
  }
4356
5453
  if (options.packageName && options.packageVersion) {
4357
- roots.push({ root: join17(options.workspaceRoot, ".agentwheel", stateDir, ...`${options.packageName}@${options.packageVersion}`.split("/")), kind: "version" });
5454
+ roots.push({ root: join24(options.workspaceRoot, ".agentwheel", stateDir, ...`${options.packageName}@${options.packageVersion}`.split("/")), kind: "version" });
4358
5455
  }
4359
5456
  if (options.packageName) {
4360
- roots.push({ root: join17(options.workspaceRoot, ".agentwheel", stateDir, ...options.packageName.split("/")), kind: "package" });
5457
+ roots.push({ root: join24(options.workspaceRoot, ".agentwheel", stateDir, ...options.packageName.split("/")), kind: "package" });
4361
5458
  }
4362
5459
  return roots;
4363
5460
  }
@@ -4371,56 +5468,6 @@ async function sortedDirEntries2(path) {
4371
5468
  return (await readdir4(path, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
4372
5469
  }
4373
5470
 
4374
- // src/model/selection.ts
4375
- function artifactSelectorKey(artifact) {
4376
- return `${artifact.type}/${artifact.name}`;
4377
- }
4378
- function artifactSelectorAliases(artifact) {
4379
- const primary = artifactSelectorKey(artifact);
4380
- if (artifact.type !== "subagents") return [primary];
4381
- const baseName = subagentBaseName(artifact.name);
4382
- if (baseName === artifact.name) return [primary];
4383
- return [primary, `subagents/${baseName}`];
4384
- }
4385
- function normalizeArtifactSelectors(select, legacySkills) {
4386
- const selected = [
4387
- ...select ?? [],
4388
- ...(legacySkills ?? []).map((name) => `skills/${name}`)
4389
- ].flatMap(splitSelectorList);
4390
- if (selected.length === 0) return void 0;
4391
- return [...new Set(selected.map(parseArtifactSelector))];
4392
- }
4393
- function filterArtifactsBySelection(artifacts, selectors, legacySkills) {
4394
- const selected = normalizeArtifactSelectors(selectors, legacySkills);
4395
- if (!selected?.length) return artifacts;
4396
- const selectedSet = new Set(selected);
4397
- const available = new Set(artifacts.flatMap(artifactSelectorAliases));
4398
- const missing = selected.filter((selector) => !available.has(selector));
4399
- if (missing.length > 0) {
4400
- throw new Error(`Selected artifact not found in package: ${missing.join(", ")}`);
4401
- }
4402
- return artifacts.filter((artifact) => artifact.required || artifactSelectorAliases(artifact).some((selector) => selectedSet.has(selector)));
4403
- }
4404
- function splitSelectorList(value) {
4405
- return value.split(",").map((item) => item.trim()).filter(Boolean);
4406
- }
4407
- function parseArtifactSelector(value) {
4408
- const slash = value.indexOf("/");
4409
- if (slash <= 0 || slash === value.length - 1) {
4410
- throw new Error(`Invalid artifact selector: ${value}. Expected <type>/<name>.`);
4411
- }
4412
- const type = value.slice(0, slash);
4413
- const name = value.slice(slash + 1);
4414
- const parsedType = artifactTypeSchema.safeParse(type);
4415
- if (!parsedType.success) {
4416
- throw new Error(`Invalid artifact selector type: ${type}`);
4417
- }
4418
- return `${parsedType.data}/${name}`;
4419
- }
4420
- function subagentBaseName(name) {
4421
- return name.replace(/\.agent\.md$/i, "").replace(/\.toml$/i, "").replace(/\.md$/i, "");
4422
- }
4423
-
4424
5471
  // src/staging/staging.ts
4425
5472
  async function stageSource(driver, source, options = {}) {
4426
5473
  return renderStagedBundle(await stageSourceRaw(driver, source, options), options);
@@ -4434,15 +5481,15 @@ async function stageResolvedSourceRaw(driver, resolved) {
4434
5481
  return stageResolvedArtifactsRaw(resolved, artifacts);
4435
5482
  }
4436
5483
  async function stageResolvedArtifactsRaw(resolved, artifacts) {
4437
- const root = await mkdtemp2(join18(tmpdir3(), "agentwheel-stage-"));
5484
+ const root = await mkdtemp3(join25(tmpdir4(), "agentwheel-stage-"));
4438
5485
  const stagedArtifacts = [];
4439
5486
  for (const artifact of artifacts) {
4440
- const stagedPath = join18(root, artifact.relativePath);
4441
- await mkdir11(dirname14(stagedPath), { recursive: true });
5487
+ const stagedPath = join25(root, artifact.relativePath);
5488
+ await mkdir12(dirname16(stagedPath), { recursive: true });
4442
5489
  await cp5(artifact.sourcePath, stagedPath, {
4443
5490
  recursive: artifact.kind === "dir",
4444
5491
  dereference: true,
4445
- filter: (path) => !isIgnoredGeneratedEntry(basename13(path))
5492
+ filter: (path) => !isIgnoredGeneratedEntry(basename15(path))
4446
5493
  });
4447
5494
  await composeAssets(artifact, resolved.resolvedPath, stagedPath);
4448
5495
  stagedArtifacts.push({
@@ -4523,16 +5570,16 @@ async function composeAssets(artifact, packageRoot, stagedPath) {
4523
5570
  }
4524
5571
  for (const asset of artifact.assets) {
4525
5572
  const source = resolvePackagePath(packageRoot, asset.from);
4526
- const dest = join18(stagedPath, asset.into);
5573
+ const dest = join25(stagedPath, asset.into);
4527
5574
  await copyAsset(asset, source, dest);
4528
5575
  }
4529
5576
  }
4530
5577
  async function copyAsset(asset, source, dest) {
4531
5578
  const sourceStats = await stat8(source);
4532
5579
  if (sourceStats.isFile()) {
4533
- if (matchesAny(basename13(source), asset.include)) {
4534
- await mkdir11(dest, { recursive: true });
4535
- await copyAssetFile(source, join18(dest, basename13(source)), asset);
5580
+ if (matchesAny(basename15(source), asset.include)) {
5581
+ await mkdir12(dest, { recursive: true });
5582
+ await copyAssetFile(source, join25(dest, basename15(source)), asset);
4536
5583
  }
4537
5584
  return;
4538
5585
  }
@@ -4540,19 +5587,19 @@ async function copyAsset(asset, source, dest) {
4540
5587
  throw new Error(`Asset include source is not a file or directory: ${source}`);
4541
5588
  }
4542
5589
  if (!asset.include?.length) {
4543
- await mkdir11(dirname14(dest), { recursive: true });
5590
+ await mkdir12(dirname16(dest), { recursive: true });
4544
5591
  await cp5(source, dest, { recursive: true, dereference: true });
4545
5592
  if (asset.mode === "copy") await normalizeCopiedModes(dest);
4546
5593
  return;
4547
5594
  }
4548
5595
  for (const file of await listFiles(source)) {
4549
- const rel = relative5(source, file).replaceAll("\\", "/");
4550
- if (!matchesAny(rel, asset.include) && !matchesAny(basename13(file), asset.include)) continue;
4551
- await copyAssetFile(file, join18(dest, rel), asset);
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);
4552
5599
  }
4553
5600
  }
4554
5601
  async function copyAssetFile(source, dest, asset) {
4555
- await mkdir11(dirname14(dest), { recursive: true });
5602
+ await mkdir12(dirname16(dest), { recursive: true });
4556
5603
  await cp5(source, dest, { dereference: true });
4557
5604
  if (asset.mode === "copy") await chmod(dest, 420);
4558
5605
  }
@@ -4568,7 +5615,7 @@ async function listFiles(root) {
4568
5615
  const out = [];
4569
5616
  async function walk2(dir) {
4570
5617
  for (const entry of (await readdir5(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
4571
- const full = join18(dir, entry.name);
5618
+ const full = join25(dir, entry.name);
4572
5619
  if (entry.isDirectory()) {
4573
5620
  await walk2(full);
4574
5621
  } else if (entry.isFile()) {
@@ -4587,7 +5634,7 @@ async function normalizeCopiedModes(path) {
4587
5634
  }
4588
5635
  if (!stats.isDirectory()) return;
4589
5636
  for (const entry of await readdir5(path, { withFileTypes: true })) {
4590
- await normalizeCopiedModes(join18(path, entry.name));
5637
+ await normalizeCopiedModes(join25(path, entry.name));
4591
5638
  }
4592
5639
  }
4593
5640
  function matchesAny(path, patterns) {
@@ -4600,9 +5647,9 @@ function matchesGlob(path, pattern) {
4600
5647
  }
4601
5648
 
4602
5649
  // src/model/workspace.ts
4603
- import { readFile as readFile14 } from "fs/promises";
5650
+ import { readFile as readFile18 } from "fs/promises";
4604
5651
  import { homedir as homedir4 } from "os";
4605
- import { dirname as dirname15, join as join19, resolve as resolve11 } from "path";
5652
+ import { dirname as dirname17, join as join26, resolve as resolve11 } from "path";
4606
5653
  import { z as z6 } from "zod";
4607
5654
  var workspacePackageSchema = z6.object({
4608
5655
  name: z6.string().min(1),
@@ -4671,12 +5718,12 @@ var workspaceConfigSchema = z6.object({
4671
5718
  agents: z6.record(z6.string(), workspaceAgentSchema).default({})
4672
5719
  });
4673
5720
  function workspaceConfigPath(workspaceRoot) {
4674
- return join19(workspaceRoot, ".agentwheel", "config.json");
5721
+ return join26(workspaceRoot, ".agentwheel", "config.json");
4675
5722
  }
4676
5723
  async function readWorkspaceConfig(workspaceRoot) {
4677
5724
  const path = workspaceConfigPath(workspaceRoot);
4678
5725
  if (!await pathExists(path)) return emptyWorkspaceConfig();
4679
- return workspaceConfigSchema.parse(JSON.parse(await readFile14(path, "utf8")));
5726
+ return workspaceConfigSchema.parse(JSON.parse(await readFile18(path, "utf8")));
4680
5727
  }
4681
5728
  async function writeWorkspaceConfig(workspaceRoot, config) {
4682
5729
  await writeJsonAtomic(workspaceConfigPath(workspaceRoot), workspaceConfigSchema.parse(config));
@@ -4689,13 +5736,13 @@ function upsertPackage(config, entry) {
4689
5736
  return { schemaVersion: 1, packages, bootstrapSkills: parsed.bootstrapSkills, registry: parsed.registry ?? {}, trust: parsed.trust ?? {}, profiles: parsed.profiles ?? {}, agents: parsed.agents ?? {} };
4690
5737
  }
4691
5738
  function globalWorkspaceConfigPath(globalRoot = homedir4()) {
4692
- return join19(globalRoot, ".agentwheel", "config.json");
5739
+ return join26(globalRoot, ".agentwheel", "config.json");
4693
5740
  }
4694
5741
  async function findWorkspaceRoot(start = process.cwd()) {
4695
5742
  let current = resolve11(start);
4696
5743
  while (true) {
4697
5744
  if (await pathExists(workspaceConfigPath(current))) return current;
4698
- const parent = dirname15(current);
5745
+ const parent = dirname17(current);
4699
5746
  if (parent === current) return resolve11(start);
4700
5747
  current = parent;
4701
5748
  }
@@ -4731,7 +5778,7 @@ function emptyWorkspaceConfig() {
4731
5778
  }
4732
5779
  async function readConfigPath(path) {
4733
5780
  if (!await pathExists(path)) return emptyWorkspaceConfig();
4734
- return workspaceConfigSchema.parse(JSON.parse(await readFile14(path, "utf8")));
5781
+ return workspaceConfigSchema.parse(JSON.parse(await readFile18(path, "utf8")));
4735
5782
  }
4736
5783
  function mergeWorkspaceTrust(global, project) {
4737
5784
  return {
@@ -4746,23 +5793,23 @@ function sortedUnique2(values) {
4746
5793
  }
4747
5794
 
4748
5795
  // src/lifecycle/customization.ts
4749
- import { appendFile, cp as cp6, mkdir as mkdir12, rm as rm7 } from "fs/promises";
4750
- import { dirname as dirname17, join as join22 } from "path";
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";
4751
5798
 
4752
5799
  // src/resolve/graph.ts
4753
- import { createHash as createHash5 } from "crypto";
4754
- import { mkdtemp as mkdtemp3, readdir as readdir6, readFile as readFile16, stat as stat10 } from "fs/promises";
4755
- import { tmpdir as tmpdir4 } from "os";
4756
- import { basename as basename14, extname as extname4, join as join21 } from "path";
5800
+ import { createHash as createHash6 } from "crypto";
5801
+ import { mkdtemp as mkdtemp4, readdir as readdir6, readFile as readFile20, stat as stat10 } from "fs/promises";
5802
+ import { tmpdir as tmpdir5 } from "os";
5803
+ import { basename as basename16, extname as extname4, join as join28 } from "path";
4757
5804
 
4758
5805
  // src/resolve/identity.ts
4759
5806
  import { homedir as homedir6 } from "os";
4760
5807
  import { resolve as resolve13 } from "path";
4761
5808
 
4762
5809
  // src/registry/client.ts
4763
- import { readFile as readFile15, rm as rm6, stat as stat9 } from "fs/promises";
5810
+ import { readFile as readFile19, rm as rm7, stat as stat9 } from "fs/promises";
4764
5811
  import { homedir as homedir5 } from "os";
4765
- import { dirname as dirname16, join as join20, resolve as resolve12 } from "path";
5812
+ import { dirname as dirname18, join as join27, resolve as resolve12 } from "path";
4766
5813
  import { fileURLToPath } from "url";
4767
5814
 
4768
5815
  // src/model/registry.ts
@@ -4837,7 +5884,7 @@ var RegistryClient = class {
4837
5884
  );
4838
5885
  }
4839
5886
  async clearCache() {
4840
- await rm6(this.cachePath, { force: true });
5887
+ await rm7(this.cachePath, { force: true });
4841
5888
  }
4842
5889
  async getSources() {
4843
5890
  if (this.options.sources?.length) return this.options.sources;
@@ -4860,7 +5907,7 @@ var RegistryClient = class {
4860
5907
  }
4861
5908
  async readCache() {
4862
5909
  if (!await pathExists(this.cachePath)) return void 0;
4863
- return registryCacheSchema.parse(JSON.parse(await readFile15(this.cachePath, "utf8")));
5910
+ return registryCacheSchema.parse(JSON.parse(await readFile19(this.cachePath, "utf8")));
4864
5911
  }
4865
5912
  isExpired(cache, ttlMs) {
4866
5913
  return this.now().getTime() - new Date(cache.fetchedAt).getTime() > ttlMs;
@@ -4879,10 +5926,10 @@ var RegistryClient = class {
4879
5926
  if (await pathExists(filePath)) {
4880
5927
  const fullPath = resolve12(filePath);
4881
5928
  const stats = await stat9(fullPath);
4882
- return readFile15(stats.isDirectory() ? join20(fullPath, "index.json") : fullPath, "utf8");
5929
+ return readFile19(stats.isDirectory() ? join27(fullPath, "index.json") : fullPath, "utf8");
4883
5930
  }
4884
- const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join20(dirname16(this.cachePath), "registry-repos") }));
4885
- return readFile15(join20(resolved.resolvedPath, "index.json"), "utf8");
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");
4886
5933
  }
4887
5934
  warnCompatibility(entries) {
4888
5935
  for (const entry of entries) {
@@ -4917,7 +5964,7 @@ function mergeIndexes(indexes) {
4917
5964
  return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
4918
5965
  }
4919
5966
  function defaultRegistryCachePath() {
4920
- return join20(homedir5(), ".agentwheel", "registry-cache.json");
5967
+ return join27(homedir5(), ".agentwheel", "registry-cache.json");
4921
5968
  }
4922
5969
  function sameSources(a, b) {
4923
5970
  return a.length === b.length && a.every((source, index) => source === b[index]);
@@ -5146,7 +6193,7 @@ function compareSemver(a, b) {
5146
6193
  var cacheLocks = /* @__PURE__ */ new Map();
5147
6194
  async function resolveDependencyGraph(roots, options) {
5148
6195
  if (roots.length === 0) throw new Error("At least one graph root is required.");
5149
- const graphRoot = await mkdtemp3(join21(tmpdir4(), "agentwheel-graph-"));
6196
+ const graphRoot = await mkdtemp4(join28(tmpdir5(), "agentwheel-graph-"));
5150
6197
  const fetchCache = /* @__PURE__ */ new Map();
5151
6198
  const nodesByKey = /* @__PURE__ */ new Map();
5152
6199
  const rootResults = [];
@@ -5508,7 +6555,7 @@ async function collectIncludeNeeds(artifact, artifactsByRelativePath) {
5508
6555
  const file = stack.shift();
5509
6556
  if (scanned.has(file)) continue;
5510
6557
  scanned.add(file);
5511
- const content = await readFile16(file, "utf8");
6558
+ const content = await readFile20(file, "utf8");
5512
6559
  for (const include of extractOpenPackIncludeSelectors(content)) {
5513
6560
  await collectIncludeSelector(include.raw, include.optional, artifactsByRelativePath, scanned, stack, needs);
5514
6561
  }
@@ -5551,7 +6598,7 @@ async function listMarkdownFiles2(root) {
5551
6598
  const out = [];
5552
6599
  async function walk2(dir) {
5553
6600
  for (const entry of (await readdir6(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
5554
- const full = join21(dir, entry.name);
6601
+ const full = join28(dir, entry.name);
5555
6602
  if (entry.isDirectory()) {
5556
6603
  await walk2(full);
5557
6604
  } else if (entry.isFile() && extname4(entry.name).toLowerCase() === ".md") {
@@ -5586,7 +6633,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
5586
6633
  const promise = (async () => {
5587
6634
  const driver = getSourceDriver(normalized.driver);
5588
6635
  const resolved = await driver.resolve(normalized.source, {
5589
- cacheRoot: options.cacheRoot ?? join21(options.workspaceRoot, ".agentwheel", "cache"),
6636
+ cacheRoot: options.cacheRoot ?? join28(options.workspaceRoot, ".agentwheel", "cache"),
5590
6637
  mode,
5591
6638
  ref: refOverride ?? normalized.requestedRef,
5592
6639
  frozenLock: hardLockedCheckout
@@ -5596,7 +6643,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
5596
6643
  const exported = await driver.export(translated);
5597
6644
  const manifest = await readPackageManifest(exported.resolvedPath);
5598
6645
  const artifacts = await driver.list(exported);
5599
- const name = manifest?.name ?? exported.packageName ?? basename14(exported.resolvedPath);
6646
+ const name = manifest?.name ?? exported.packageName ?? basename16(exported.resolvedPath);
5600
6647
  const version = manifest?.version ?? exported.packageVersion ?? "0.0.0";
5601
6648
  const sourceHash = exported.sourceHash ?? await hashPath(exported.resolvedPath);
5602
6649
  return {
@@ -5772,7 +6819,7 @@ function detectDirectCollisions(nodes) {
5772
6819
  }
5773
6820
  }
5774
6821
  function graphNodeId(name, version, normalizedSource, resolvedCommit, sourceHash) {
5775
- const digest = createHash5("sha256").update(normalizedSource).update("\0").update(resolvedCommit ?? sourceHash).digest("hex").slice(0, 12);
6822
+ const digest = createHash6("sha256").update(normalizedSource).update("\0").update(resolvedCommit ?? sourceHash).digest("hex").slice(0, 12);
5776
6823
  return `${name}@${version}+${digest}`;
5777
6824
  }
5778
6825
  function sortedUnique3(values) {
@@ -5793,8 +6840,8 @@ async function mapLimit(items, limit, fn) {
5793
6840
 
5794
6841
  // src/lifecycle/customization.ts
5795
6842
  async function remember(workspaceRoot, runtime, text) {
5796
- const overlayPath = join22(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
5797
- await mkdir12(dirname17(overlayPath), { recursive: true });
6843
+ const overlayPath = join29(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
6844
+ await mkdir13(dirname19(overlayPath), { recursive: true });
5798
6845
  await appendFile(overlayPath, `${text.trim()}
5799
6846
  `, "utf8");
5800
6847
  return { overlayPath };
@@ -5817,9 +6864,9 @@ async function ejectArtifact(workspaceRoot, item) {
5817
6864
  throw new Error(`Artifact not found: ${item}`);
5818
6865
  }
5819
6866
  const ejectedIdentity = parsed.packageIdentity === parsed.packageName ? parsed.packageIdentity : candidate.nodeId === parsed.packageIdentity ? candidate.nodeId : `${candidate.packageName}@${candidate.packageVersion}`;
5820
- const ejectedPath = join22(workspaceRoot, ".agentwheel", "ejected", ...ejectedIdentity.split("/"), parsed.type, parsed.name);
5821
- await mkdir12(dirname17(ejectedPath), { recursive: true });
5822
- await rm7(ejectedPath, { recursive: true, force: true });
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 });
5823
6870
  await cp6(artifact.stagedPath ?? artifact.sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
5824
6871
  return {
5825
6872
  ...parsed,
@@ -5830,7 +6877,7 @@ async function ejectArtifact(workspaceRoot, item) {
5830
6877
  ejectedPath
5831
6878
  };
5832
6879
  } finally {
5833
- await Promise.all(candidates.map((candidate) => rm7(candidate.bundle.root, { recursive: true, force: true })));
6880
+ await Promise.all(candidates.map((candidate) => rm8(candidate.bundle.root, { recursive: true, force: true })));
5834
6881
  }
5835
6882
  }
5836
6883
  function parseEjectItem(item) {
@@ -5860,7 +6907,7 @@ async function stageEjectCandidate(workspaceRoot, pkg) {
5860
6907
  const adapter = pkg.adapterConfig ? await loadAdapterConfig(pkg.adapterConfig) : getAdapter(pkg.adapter);
5861
6908
  const bundle = await stageSource(driver, normalized.source, {
5862
6909
  adapter,
5863
- cacheRoot: join22(workspaceRoot, ".agentwheel", "cache"),
6910
+ cacheRoot: join29(workspaceRoot, ".agentwheel", "cache"),
5864
6911
  mode: pkg.mode,
5865
6912
  ref: normalized.requestedRef ?? pkg.requestedRef
5866
6913
  });
@@ -5907,12 +6954,12 @@ function ejectCommands(candidates, parsed) {
5907
6954
  }
5908
6955
 
5909
6956
  // src/lifecycle/profile.ts
5910
- import { rm as rm8 } from "fs/promises";
6957
+ import { rm as rm9 } from "fs/promises";
5911
6958
 
5912
6959
  // src/lifecycle/source-plan.ts
5913
- import { createHash as createHash7 } from "crypto";
5914
- import { mkdir as mkdir14 } from "fs/promises";
5915
- import { dirname as dirname19, join as join25, resolve as resolve14 } from "path";
6960
+ 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";
5916
6963
 
5917
6964
  // src/resolve/graph-diff.ts
5918
6965
  function diffGraphLocks(previous, next) {
@@ -6033,12 +7080,12 @@ function short(hash) {
6033
7080
  }
6034
7081
 
6035
7082
  // src/resolve/render.ts
6036
- import { createHash as createHash6 } from "crypto";
6037
- import { readFile as readFile17, mkdtemp as mkdtemp4 } from "fs/promises";
6038
- import { tmpdir as tmpdir5 } from "os";
6039
- import { join as join23 } from "path";
7083
+ import { createHash as createHash7 } from "crypto";
7084
+ import { readFile as readFile21, mkdtemp as mkdtemp5 } from "fs/promises";
7085
+ import { tmpdir as tmpdir6 } from "os";
7086
+ import { join as join30 } from "path";
6040
7087
  async function renderGraphForTarget(graph, targetContext = {}) {
6041
- const root = await mkdtemp4(join23(tmpdir5(), "agentwheel-render-"));
7088
+ const root = await mkdtemp5(join30(tmpdir6(), "agentwheel-render-"));
6042
7089
  const artifacts = [];
6043
7090
  const stagedNodes = /* @__PURE__ */ new Map();
6044
7091
  const includeEdges = /* @__PURE__ */ new Map();
@@ -6121,7 +7168,11 @@ async function renderGraphForTarget(graph, targetContext = {}) {
6121
7168
  graphNodeId: rawNode.node.id,
6122
7169
  packageNameAmbiguous: ambiguousPackageNames.has(rawNode.node.name)
6123
7170
  }) : runtimeRenderedArtifacts;
6124
- const installableArtifacts = renderedArtifacts.filter((artifact) => rawNode.depth === 0 || artifact.type !== "fragments");
7171
+ const compatibleArtifacts = targetContext.adapter && targetContext.installationType ? await filterArtifactsByInstallFormat(renderedArtifacts, targetContext.adapter, targetContext.installationType, {
7172
+ selected: rawNode.node.selected,
7173
+ warn: targetContext.warn
7174
+ }) : renderedArtifacts;
7175
+ const installableArtifacts = compatibleArtifacts.filter((artifact) => rawNode.depth === 0 || artifact.type !== "fragments");
6125
7176
  artifacts.push(...installableArtifacts.map((artifact) => ({
6126
7177
  ...artifact,
6127
7178
  graphNodeId: rawNode.node.id,
@@ -6154,7 +7205,7 @@ async function artifactContentMap(artifacts) {
6154
7205
  const out = /* @__PURE__ */ new Map();
6155
7206
  for (const artifact of artifacts) {
6156
7207
  if (artifact.kind !== "file") continue;
6157
- out.set(artifact.relativePath.replaceAll("\\", "/"), await readFile17(artifact.stagedPath ?? artifact.sourcePath, "utf8"));
7208
+ out.set(artifact.relativePath.replaceAll("\\", "/"), await readFile21(artifact.stagedPath ?? artifact.sourcePath, "utf8"));
6158
7209
  }
6159
7210
  return out;
6160
7211
  }
@@ -6168,7 +7219,7 @@ function filterArtifactsByRuntime2(artifacts, adapterName, selectedSet) {
6168
7219
  });
6169
7220
  }
6170
7221
  function sha2562(content) {
6171
- return createHash6("sha256").update(content).digest("hex");
7222
+ return createHash7("sha256").update(content).digest("hex");
6172
7223
  }
6173
7224
  function assignInstallNames(graph, artifacts) {
6174
7225
  const aliases = workspaceAliases(graph);
@@ -6417,9 +7468,9 @@ function lockArtifactFor(artifact) {
6417
7468
  }
6418
7469
 
6419
7470
  // src/lifecycle/trust.ts
6420
- import { mkdir as mkdir13, readFile as readFile18 } from "fs/promises";
7471
+ import { mkdir as mkdir14, readFile as readFile22 } from "fs/promises";
6421
7472
  import { homedir as homedir7 } from "os";
6422
- import { dirname as dirname18, join as join24 } from "path";
7473
+ import { dirname as dirname20, join as join31 } from "path";
6423
7474
  import { z as z8 } from "zod";
6424
7475
  var trustStoreSchema = z8.object({
6425
7476
  version: z8.literal(1),
@@ -6493,14 +7544,14 @@ function sortedUnique4(values) {
6493
7544
  }
6494
7545
  async function readTrustStore(path) {
6495
7546
  if (!await pathExists(path)) return { version: 1, acceptedSources: [] };
6496
- return trustStoreSchema.parse(JSON.parse(await readFile18(path, "utf8")));
7547
+ return trustStoreSchema.parse(JSON.parse(await readFile22(path, "utf8")));
6497
7548
  }
6498
7549
  async function writeTrustStore(path, store) {
6499
- await mkdir13(dirname18(path), { recursive: true });
7550
+ await mkdir14(dirname20(path), { recursive: true });
6500
7551
  await writeJsonAtomic(path, trustStoreSchema.parse(store));
6501
7552
  }
6502
7553
  function defaultTrustStorePath() {
6503
- return process.env.AGENTWHEEL_TRUST_STORE ?? join24(homedir7(), ".agentwheel", "trust.json");
7554
+ return process.env.AGENTWHEEL_TRUST_STORE ?? join31(homedir7(), ".agentwheel", "trust.json");
6504
7555
  }
6505
7556
 
6506
7557
  // src/lifecycle/source-plan.ts
@@ -6538,7 +7589,7 @@ async function createGraphSourcePlan(options) {
6538
7589
  const registryClient = new RegistryClient({ workspaceRoot, offline: lockMode, offlineLabel: lockLabel, warn });
6539
7590
  const graph = await resolveDependencyGraph(options.roots, {
6540
7591
  workspaceRoot,
6541
- cacheRoot: join25(workspaceRoot, ".agentwheel", "cache"),
7592
+ cacheRoot: join32(workspaceRoot, ".agentwheel", "cache"),
6542
7593
  registryClient,
6543
7594
  noDeps: options.noDeps,
6544
7595
  lockedResolution: options.lockedResolution,
@@ -6557,7 +7608,9 @@ async function createGraphSourcePlan(options) {
6557
7608
  const bundle = await renderGraphForTarget(graph, {
6558
7609
  workspaceRoot,
6559
7610
  adapter: options.adapter,
6560
- targetFingerprint
7611
+ installationType,
7612
+ targetFingerprint,
7613
+ warn
6561
7614
  });
6562
7615
  const desiredArtifacts = desiredArtifactsFromGraphBundle(bundle);
6563
7616
  const resolvedInstallationType = resolveInstallationTypeForArtifacts(options.adapter, desiredArtifacts.map((artifact) => artifact.type), installationType);
@@ -6573,7 +7626,8 @@ async function createGraphSourcePlan(options) {
6573
7626
  stateKey,
6574
7627
  forceDrift: options.forceDrift,
6575
7628
  forceConflict: options.forceConflict,
6576
- replaceConflict: options.replaceConflict
7629
+ replaceConflict: options.replaceConflict,
7630
+ warn
6577
7631
  });
6578
7632
  return {
6579
7633
  plan,
@@ -6633,13 +7687,13 @@ async function readExistingGraphLock(path) {
6633
7687
  return readGraphLock(path);
6634
7688
  }
6635
7689
  function pathForGraphLock(workspaceRoot, targetKey, adapter, targetFingerprint) {
6636
- return join25(workspaceRoot, ".agentwheel", "locks", sanitizePathSegment(targetKey), sanitizePathSegment(adapter), `${targetFingerprint}.graph-lock.json`);
7690
+ return join32(workspaceRoot, ".agentwheel", "locks", sanitizePathSegment(targetKey), sanitizePathSegment(adapter), `${targetFingerprint}.graph-lock.json`);
6637
7691
  }
6638
7692
  function sanitizePathSegment(value) {
6639
7693
  return value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "default";
6640
7694
  }
6641
7695
  function digestGraphLock(lock) {
6642
- return createHash7("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
7696
+ return createHash8("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
6643
7697
  }
6644
7698
  function workspaceOwnerId(workspaceRoot) {
6645
7699
  return `workspace-root:${resolve14(workspaceRoot)}`;
@@ -6697,7 +7751,7 @@ ${sources.map((source) => `- ${source}`).join("\n")}`);
6697
7751
  }
6698
7752
 
6699
7753
  // src/runtime/target.ts
6700
- import { basename as basename15, dirname as dirname20, join as join26, resolve as resolve15 } from "path";
7754
+ import { basename as basename17, dirname as dirname22, join as join33, resolve as resolve15 } from "path";
6701
7755
  var runtimeMarkers = [
6702
7756
  { adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
6703
7757
  { adapter: "claude", dirs: [".claude"] },
@@ -6808,9 +7862,9 @@ async function detectRuntimeTargets(cwd = process.cwd(), adapterFilter) {
6808
7862
  for (const marker of runtimeMarkers) {
6809
7863
  if (adapterFilter && marker.adapter !== adapterFilter) continue;
6810
7864
  for (const dir of marker.dirs) {
6811
- if (basename15(root) === dir) {
6812
- matches.push({ adapter: marker.adapter, targetRoot: dirname20(root) });
6813
- } else if (await pathExists(join26(root, dir))) {
7865
+ if (basename17(root) === dir) {
7866
+ matches.push({ adapter: marker.adapter, targetRoot: dirname22(root) });
7867
+ } else if (await pathExists(join33(root, dir))) {
6814
7868
  matches.push({ adapter: marker.adapter, targetRoot: root });
6815
7869
  }
6816
7870
  }
@@ -6849,7 +7903,7 @@ function dedupeTargets(matches) {
6849
7903
  function runtimeScanRoot(request) {
6850
7904
  const root = resolve15(request.targetRoot ?? request.cwd ?? process.cwd());
6851
7905
  if (request.targetRoot) return root;
6852
- return runtimeMarkers.some((marker) => marker.dirs.includes(basename15(root))) ? dirname20(root) : root;
7906
+ return runtimeMarkers.some((marker) => marker.dirs.includes(basename17(root))) ? dirname22(root) : root;
6853
7907
  }
6854
7908
 
6855
7909
  // src/lifecycle/profile.ts
@@ -6935,7 +7989,7 @@ async function syncProfile(options) {
6935
7989
  });
6936
7990
  }
6937
7991
  } finally {
6938
- await rm8(graphPlan.bundle.root, { recursive: true, force: true });
7992
+ await rm9(graphPlan.bundle.root, { recursive: true, force: true });
6939
7993
  }
6940
7994
  }
6941
7995
  return results;
@@ -6959,9 +8013,9 @@ async function packageFromSource(source, options) {
6959
8013
  }
6960
8014
 
6961
8015
  // src/cli/update-check.ts
6962
- import { mkdir as mkdir15, readFile as readFile19, writeFile as writeFile12 } from "fs/promises";
8016
+ import { mkdir as mkdir16, readFile as readFile23, writeFile as writeFile14 } from "fs/promises";
6963
8017
  import { homedir as homedir8 } from "os";
6964
- import { dirname as dirname21, join as join27 } from "path";
8018
+ import { dirname as dirname23, join as join34 } from "path";
6965
8019
  var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
6966
8020
  var DEFAULT_TIMEOUT_MS = 300;
6967
8021
  var REGISTRY_URL = "https://registry.npmjs.org/agentwheel";
@@ -6969,7 +8023,7 @@ async function maybeCheckForUpdate(options) {
6969
8023
  if (isDisabled(options)) return;
6970
8024
  const now = options.now?.() ?? /* @__PURE__ */ new Date();
6971
8025
  const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
6972
- const cachePath = options.cachePath ?? join27(homedir8(), ".agentwheel", "update-check.json");
8026
+ const cachePath = options.cachePath ?? join34(homedir8(), ".agentwheel", "update-check.json");
6973
8027
  try {
6974
8028
  const cached = await readCache(cachePath);
6975
8029
  if (cached && now.getTime() - Date.parse(cached.checkedAt) < ttlMs) {
@@ -7006,7 +8060,7 @@ async function fetchLatestVersion(fetchImpl, timeoutMs) {
7006
8060
  }
7007
8061
  async function readCache(path) {
7008
8062
  try {
7009
- const parsed = JSON.parse(await readFile19(path, "utf8"));
8063
+ const parsed = JSON.parse(await readFile23(path, "utf8"));
7010
8064
  if (typeof parsed.checkedAt !== "string" || typeof parsed.latest !== "string") return void 0;
7011
8065
  return { checkedAt: parsed.checkedAt, latest: parsed.latest };
7012
8066
  } catch {
@@ -7014,8 +8068,8 @@ async function readCache(path) {
7014
8068
  }
7015
8069
  }
7016
8070
  async function writeCache(path, cache) {
7017
- await mkdir15(dirname21(path), { recursive: true });
7018
- await writeFile12(path, `${JSON.stringify(cache, null, 2)}
8071
+ await mkdir16(dirname23(path), { recursive: true });
8072
+ await writeFile14(path, `${JSON.stringify(cache, null, 2)}
7019
8073
  `, "utf8");
7020
8074
  }
7021
8075
  function warnIfNewer(latest, current, stderr = process.stderr) {
@@ -7161,13 +8215,13 @@ function isCrossPackageSelector(value) {
7161
8215
  }
7162
8216
 
7163
8217
  // src/model/package-migrate.ts
7164
- import { readFile as readFile20, rename as rename4, writeFile as writeFile13 } from "fs/promises";
7165
- import { join as join29, resolve as resolve17 } from "path";
7166
- import { applyEdits, modify, parse as parse3 } from "jsonc-parser";
8218
+ import { readFile as readFile24, rename as rename4, writeFile as writeFile15 } from "fs/promises";
8219
+ import { join as join36, resolve as resolve17 } from "path";
8220
+ import { applyEdits, modify, parse as parse4 } from "jsonc-parser";
7167
8221
  async function migratePackageManifest(root) {
7168
8222
  const packageRoot = resolve17(root);
7169
8223
  for (const name of openPackManifestNames) {
7170
- const path = join29(packageRoot, name);
8224
+ const path = join36(packageRoot, name);
7171
8225
  if (await pathExists(path)) {
7172
8226
  return { changed: false, to: path, message: `Package already uses ${name}.` };
7173
8227
  }
@@ -7176,24 +8230,24 @@ async function migratePackageManifest(root) {
7176
8230
  if (!legacyName) {
7177
8231
  throw new Error(`No legacy package manifest found at ${packageRoot}`);
7178
8232
  }
7179
- const from = join29(packageRoot, legacyName);
8233
+ const from = join36(packageRoot, legacyName);
7180
8234
  const toName = legacyName.endsWith(".jsonc") ? "openpack.jsonc" : "openpack.json";
7181
- const to = join29(packageRoot, toName);
7182
- const content = await readFile20(from, "utf8");
8235
+ const to = join36(packageRoot, toName);
8236
+ const content = await readFile24(from, "utf8");
7183
8237
  const updated = updateSchemaVersion(content);
7184
8238
  await rename4(from, to);
7185
- await writeFile13(to, updated, "utf8");
8239
+ await writeFile15(to, updated, "utf8");
7186
8240
  return { changed: true, from, to, message: `Migrated ${legacyName} to ${toName}.` };
7187
8241
  }
7188
8242
  async function firstExistingLegacyManifest(root) {
7189
8243
  for (const name of legacyPackageManifestNames) {
7190
- if (await pathExists(join29(root, name))) return name;
8244
+ if (await pathExists(join36(root, name))) return name;
7191
8245
  }
7192
8246
  return void 0;
7193
8247
  }
7194
8248
  function updateSchemaVersion(content) {
7195
8249
  const errors = [];
7196
- parse3(content, errors, { allowTrailingComma: true, disallowComments: false });
8250
+ parse4(content, errors, { allowTrailingComma: true, disallowComments: false });
7197
8251
  if (errors.length > 0) {
7198
8252
  throw new Error("Cannot migrate invalid JSON/JSONC package manifest.");
7199
8253
  }
@@ -7205,20 +8259,20 @@ function updateSchemaVersion(content) {
7205
8259
 
7206
8260
  // src/cli/version.ts
7207
8261
  import { readFileSync } from "fs";
7208
- import { dirname as dirname22, join as join30 } from "path";
8262
+ import { dirname as dirname24, join as join37 } from "path";
7209
8263
  import { fileURLToPath as fileURLToPath2 } from "url";
7210
8264
  var FALLBACK_VERSION = "0.0.0";
7211
8265
  function resolveCliVersion() {
7212
- let dir = dirname22(fileURLToPath2(import.meta.url));
8266
+ let dir = dirname24(fileURLToPath2(import.meta.url));
7213
8267
  while (true) {
7214
8268
  try {
7215
- const pkg = JSON.parse(readFileSync(join30(dir, "package.json"), "utf8"));
8269
+ const pkg = JSON.parse(readFileSync(join37(dir, "package.json"), "utf8"));
7216
8270
  if (pkg.name === "agentwheel" && typeof pkg.version === "string") {
7217
8271
  return pkg.version;
7218
8272
  }
7219
8273
  } catch {
7220
8274
  }
7221
- const parent = dirname22(dir);
8275
+ const parent = dirname24(dir);
7222
8276
  if (parent === dir) return FALLBACK_VERSION;
7223
8277
  dir = parent;
7224
8278
  }
@@ -7226,6 +8280,8 @@ function resolveCliVersion() {
7226
8280
 
7227
8281
  // src/cli/index.ts
7228
8282
  var CLI_VERSION = resolveCliVersion();
8283
+ var COMPANION_SKILL_SOURCE = "github:NestDevLab/agentwheel";
8284
+ var COMPANION_SKILL_NAME = "agentwheel";
7229
8285
  var program = new Command();
7230
8286
  program.name("agentwheel").description("Multi-runtime agent artifact orchestrator").version(CLI_VERSION).showSuggestionAfterError(false).option("--no-update-check", "disable npm version update check", false).addHelpText("after", `
7231
8287
 
@@ -7264,7 +8320,7 @@ program.command("list").description("list artifacts exposed by a package source"
7264
8320
  const selectedArtifacts = selectedArtifactsFromOptions(options);
7265
8321
  const resolvedInput = await resolvePackageSource(source, targetRoot);
7266
8322
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
7267
- const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join31(targetRoot, ".agentwheel", "cache") }))));
8323
+ const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join38(targetRoot, ".agentwheel", "cache") }))));
7268
8324
  const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
7269
8325
  for (const artifact of artifacts) {
7270
8326
  console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
@@ -7274,7 +8330,7 @@ program.command("scan").description("scan a package source for validation findin
7274
8330
  const targetRoot = normalizeTargetRoot(options.targetRoot);
7275
8331
  const resolvedInput = await resolvePackageSource(source, targetRoot);
7276
8332
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
7277
- const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join31(targetRoot, ".agentwheel", "cache") }))));
8333
+ const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join38(targetRoot, ".agentwheel", "cache") }))));
7278
8334
  const result = await driver.scan(resolved);
7279
8335
  if (result.findings.length === 0) {
7280
8336
  console.log("Scan ok: no findings");
@@ -7316,7 +8372,7 @@ program.command("deps").description("inspect the OpenPack dependency graph").add
7316
8372
  for (const decision of result.bundle.graphLock.canonical.overrides) {
7317
8373
  console.log(`OVERRIDE ${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`);
7318
8374
  }
7319
- await rm9(result.bundle.root, { recursive: true, force: true });
8375
+ await rm10(result.bundle.root, { recursive: true, force: true });
7320
8376
  }
7321
8377
  continue;
7322
8378
  }
@@ -7437,6 +8493,16 @@ program.command("status").description("show configured packages and runtime inst
7437
8493
  await printStatus(target, normalizedOptions);
7438
8494
  }
7439
8495
  });
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) => {
8497
+ const normalizedOptions = normalizeRuntimeScopeOptions(options);
8498
+ const target = await resolveRuntimeTarget({
8499
+ targetRoot: normalizedOptions.targetRoot,
8500
+ adapter: normalizedOptions.adapter,
8501
+ installationType: normalizedOptions.installationType,
8502
+ agent: normalizedOptions.agent
8503
+ });
8504
+ await printDoctor(target, normalizedOptions);
8505
+ });
7440
8506
  async function runInstallCommand(nameOrSource, options, behavior) {
7441
8507
  const normalizedOptions = normalizeRuntimeScopeOptions(options, { defaultUser: shouldDefaultUserInstall(nameOrSource, options) });
7442
8508
  if (options.profile) {
@@ -7510,7 +8576,7 @@ async function runInstallCommand(nameOrSource, options, behavior) {
7510
8576
  });
7511
8577
  console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
7512
8578
  }
7513
- await rm9(result.bundle.root, { recursive: true, force: true });
8579
+ await rm10(result.bundle.root, { recursive: true, force: true });
7514
8580
  if (result.plan.hasBlockingChanges) process.exitCode = 1;
7515
8581
  }
7516
8582
  if (behavior.apply && extraPackage && !targetOptions.onlySource) {
@@ -7536,7 +8602,7 @@ async function packageEntryFromSource(source, targetRoot, options) {
7536
8602
  const bundle = await stageSource(driver, resolvedSource, {
7537
8603
  workspaceRoot: targetRoot,
7538
8604
  adapter,
7539
- cacheRoot: join31(targetRoot, ".agentwheel", "cache"),
8605
+ cacheRoot: join38(targetRoot, ".agentwheel", "cache"),
7540
8606
  mode: options.mode,
7541
8607
  frozenLock: lockMode,
7542
8608
  select: selectedArtifacts
@@ -7558,7 +8624,7 @@ async function packageEntryFromSource(source, targetRoot, options) {
7558
8624
  overrides: overrideArtifactsFromOptions(options)
7559
8625
  };
7560
8626
  } finally {
7561
- await rm9(bundle.root, { recursive: true, force: true });
8627
+ await rm10(bundle.root, { recursive: true, force: true });
7562
8628
  }
7563
8629
  }
7564
8630
  function findConfiguredPackage(packages, value) {
@@ -7699,7 +8765,7 @@ async function runConfiguredGraphPackages(target, options, behavior) {
7699
8765
  });
7700
8766
  console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
7701
8767
  }
7702
- await rm9(result.bundle.root, { recursive: true, force: true });
8768
+ await rm10(result.bundle.root, { recursive: true, force: true });
7703
8769
  if (result.plan.hasBlockingChanges) process.exitCode = 1;
7704
8770
  }
7705
8771
  }
@@ -7888,7 +8954,7 @@ function keepManifestEntryOperation(entry, targetRoot, rootId, operation, option
7888
8954
  artifactType: entry.artifactType,
7889
8955
  artifactName: entry.artifactName,
7890
8956
  kind: entry.kind,
7891
- destPath: operation?.destPath ?? join31(targetRoot, entry.path),
8957
+ destPath: operation?.destPath ?? join38(targetRoot, entry.path),
7892
8958
  relativeDestPath: entry.path,
7893
8959
  desiredHash: entry.sourceHash,
7894
8960
  currentHash: operation?.currentHash ?? entry.hash,
@@ -7897,6 +8963,7 @@ function keepManifestEntryOperation(entry, targetRoot, rootId, operation, option
7897
8963
  channel: entry.channel,
7898
8964
  packageName: entry.packageName,
7899
8965
  semanticCommand: entry.semanticCommand,
8966
+ semanticPlugin: entry.semanticPlugin,
7900
8967
  execute: entry.executed,
7901
8968
  mergeStrategy: entry.mergeStrategy,
7902
8969
  composedFrom: entry.composedFrom,
@@ -7998,7 +9065,7 @@ async function uninstallConfiguredPackage(target, packageName, options) {
7998
9065
  if (!options.dryRun) {
7999
9066
  console.log(formatUninstallResult(result));
8000
9067
  }
8001
- if (renderedRoot) await rm9(renderedRoot, { recursive: true, force: true });
9068
+ if (renderedRoot) await rm10(renderedRoot, { recursive: true, force: true });
8002
9069
  if (plan.hasBlockingChanges) process.exitCode = 1;
8003
9070
  }
8004
9071
  }
@@ -8126,9 +9193,57 @@ async function printPendingInstallWork(target, options) {
8126
9193
  const message = error instanceof Error ? error.message : String(error);
8127
9194
  console.log(`Pending install work: unavailable (${message})`);
8128
9195
  } finally {
8129
- await Promise.all(results.map((result) => rm9(result.bundle.root, { recursive: true, force: true })));
9196
+ await Promise.all(results.map((result) => rm10(result.bundle.root, { recursive: true, force: true })));
8130
9197
  }
8131
9198
  }
9199
+ async function printDoctor(target, options) {
9200
+ const adapterOptions = adapterOptionsForTarget(target, options);
9201
+ const adapter = await resolveAdapterForTarget(target, adapterOptions);
9202
+ const installationType = options.installationType ?? target.installationType ?? resolveInstallationTypeForArtifacts(adapter, ["skills"]);
9203
+ const targetMapping = targetMappingForArtifact(adapter, "skills", installationType);
9204
+ if (!targetMapping?.enabled) {
9205
+ throw new Error(`Adapter ${adapter.name} does not support skills for installation type '${installationType}'.`);
9206
+ }
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}`);
9213
+ return;
9214
+ }
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)}`);
9219
+ }
9220
+ function companionSkillInstallCommand(adapter, installationType, options, behavior = {}) {
9221
+ const args = [
9222
+ "agentwheel",
9223
+ "install",
9224
+ COMPANION_SKILL_SOURCE,
9225
+ "--adapter",
9226
+ adapter,
9227
+ ...installationTypeCommandArgs(installationType),
9228
+ ...targetRootCommandArgs(options),
9229
+ "--skill",
9230
+ COMPANION_SKILL_NAME
9231
+ ];
9232
+ if (behavior.dryRun) args.push("--dry-run");
9233
+ return args.map(shellQuoteArg).join(" ");
9234
+ }
9235
+ function installationTypeCommandArgs(installationType) {
9236
+ if (installationType === "user") return ["--user"];
9237
+ if (installationType === "local") return ["--local"];
9238
+ return ["--installation-type", installationType];
9239
+ }
9240
+ function targetRootCommandArgs(options) {
9241
+ return options.targetRoot && options.installationType !== "user" ? ["--target-root", options.targetRoot] : [];
9242
+ }
9243
+ function shellQuoteArg(value) {
9244
+ if (/^[A-Za-z0-9_./:@=-]+$/.test(value)) return value;
9245
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
9246
+ }
8132
9247
  function collectSelectOption(value, previous) {
8133
9248
  return [...previous, ...splitSelectorList(value)];
8134
9249
  }
@@ -8224,10 +9339,10 @@ function filterUninstallPlanBySelection(plan, selected) {
8224
9339
  };
8225
9340
  }
8226
9341
  async function initPackage(root) {
8227
- await mkdir16(join31(root, "instructions"), { recursive: true });
8228
- await mkdir16(join31(root, "rules"), { recursive: true });
8229
- await mkdir16(join31(root, "skills"), { recursive: true });
8230
- const manifestPath = join31(root, "openpack.json");
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");
8231
9346
  const manifest = {
8232
9347
  schemaVersion: 2,
8233
9348
  name: "example/agentwheel-package",
@@ -8238,12 +9353,12 @@ async function initPackage(root) {
8238
9353
  { type: "skills", path: "skills" }
8239
9354
  ]
8240
9355
  };
8241
- await writeFile14(manifestPath, `${JSON.stringify(manifest, null, 2)}
9356
+ await writeFile16(manifestPath, `${JSON.stringify(manifest, null, 2)}
8242
9357
  `, "utf8");
8243
- await writeFile14(join31(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
9358
+ await writeFile16(join38(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
8244
9359
  }
8245
9360
  async function defaultBootstrapPackage(_root) {
8246
- const packageRoot = await findAgentwheelPackageRoot(dirname23(fileURLToPath3(import.meta.url)));
9361
+ const packageRoot = await findAgentwheelPackageRoot(dirname25(fileURLToPath3(import.meta.url)));
8247
9362
  if (!packageRoot) return void 0;
8248
9363
  return {
8249
9364
  name: "agentwheel",
@@ -8290,7 +9405,7 @@ async function findAgentwheelPackageRoot(start) {
8290
9405
  let current = resolve18(start);
8291
9406
  while (true) {
8292
9407
  if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
8293
- const parent = dirname23(current);
9408
+ const parent = dirname25(current);
8294
9409
  if (parent === current) return void 0;
8295
9410
  current = parent;
8296
9411
  }