agentwheel 0.16.6 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,17 +3,19 @@ import {
3
3
  atomicCopy,
4
4
  hashPath,
5
5
  inferSourceDriverName,
6
+ isAlreadyExists,
6
7
  isIgnoredGeneratedEntry,
7
8
  pathExists,
9
+ withFilesystemLock,
8
10
  writeJsonAtomic
9
- } from "./chunk-PKAPR55N.js";
11
+ } from "./chunk-24IUHC3D.js";
10
12
 
11
13
  // src/cli/index.ts
12
- import { createHash as createHash14 } from "crypto";
14
+ import { createHash as createHash15 } from "crypto";
13
15
  import { existsSync } from "fs";
14
- import { mkdir as mkdir23, rm as rm12, writeFile as writeFile22 } from "fs/promises";
16
+ import { mkdir as mkdir24, rm as rm13, writeFile as writeFile22 } from "fs/promises";
15
17
  import { homedir as homedir12 } from "os";
16
- import { dirname as dirname32, join as join48, resolve as resolve22 } from "path";
18
+ import { dirname as dirname33, join as join49, resolve as resolve22 } from "path";
17
19
  import { fileURLToPath as fileURLToPath3 } from "url";
18
20
  import { Command } from "commander";
19
21
 
@@ -495,113 +497,126 @@ import { promisify as promisify3 } from "util";
495
497
  import { createHash } from "crypto";
496
498
  import { mkdir, readFile as readFile3, rename, writeFile as writeFile2 } from "fs/promises";
497
499
  import { dirname } from "path";
500
+ import { z as z4 } from "zod";
501
+
502
+ // src/model/cache-identity.ts
498
503
  import { z as z3 } from "zod";
499
- var graphLockNodeSchema = z3.object({
500
- id: z3.string().min(1),
501
- name: z3.string().min(1),
502
- version: z3.string().min(1),
503
- source: z3.string().min(1),
504
- normalizedSource: z3.string().min(1),
505
- driver: z3.string().min(1),
506
- requestedRef: z3.string().min(1).optional(),
507
- resolvedCommit: z3.string().min(1).optional(),
508
- sourceHash: z3.string().min(16),
509
- mode: z3.enum(["pinned", "tracking"]),
510
- requiredBy: z3.array(z3.string().min(1)),
511
- selected: z3.array(z3.string().min(1)),
512
- selectionReasons: z3.record(z3.string(), z3.array(z3.string().min(1))).optional()
504
+ var immutableCacheIdentitySchema = z3.string().regex(
505
+ /^(?:[0-9a-f]{40}|content-[0-9a-f]{64})$/i,
506
+ "Expected a Git commit or content-addressed SHA-256 cache identity"
507
+ );
508
+ function normalizeImmutableCacheIdentity(value) {
509
+ return value === void 0 ? void 0 : immutableCacheIdentitySchema.parse(value).toLowerCase();
510
+ }
511
+
512
+ // src/model/graph-lock.ts
513
+ var graphLockNodeSchema = z4.object({
514
+ id: z4.string().min(1),
515
+ name: z4.string().min(1),
516
+ version: z4.string().min(1),
517
+ source: z4.string().min(1),
518
+ normalizedSource: z4.string().min(1),
519
+ driver: z4.string().min(1),
520
+ requestedRef: z4.string().min(1).optional(),
521
+ resolvedCommit: z4.string().min(1).optional(),
522
+ cacheIdentity: immutableCacheIdentitySchema.optional(),
523
+ sourceHash: z4.string().min(16),
524
+ mode: z4.enum(["pinned", "tracking"]),
525
+ requiredBy: z4.array(z4.string().min(1)),
526
+ selected: z4.array(z4.string().min(1)),
527
+ selectionReasons: z4.record(z4.string(), z4.array(z4.string().min(1))).optional()
513
528
  });
514
- var graphLockRootSchema = z3.object({
515
- rootId: z3.string().min(1),
516
- source: z3.string().min(1),
517
- normalizedSource: z3.string().min(1),
518
- graphNodeId: z3.string().min(1),
519
- mode: z3.enum(["pinned", "tracking"]),
520
- selected: z3.array(z3.string().min(1)),
521
- aliases: z3.record(z3.string(), z3.string().min(1)).optional(),
522
- overrides: z3.array(z3.string().min(1)).optional(),
523
- selectionImport: z3.object({
524
- configPath: z3.string().min(1),
525
- configHash: z3.string().min(16),
526
- exportHash: z3.string().min(16),
527
- exportName: z3.string().min(1),
528
- extends: z3.array(z3.string().min(1)),
529
- inherited: z3.array(z3.string().min(1)),
530
- additions: z3.array(z3.string().min(1)),
531
- exclusions: z3.array(z3.string().min(1)),
532
- effective: z3.array(z3.string().min(1))
529
+ var graphLockRootSchema = z4.object({
530
+ rootId: z4.string().min(1),
531
+ source: z4.string().min(1),
532
+ normalizedSource: z4.string().min(1),
533
+ graphNodeId: z4.string().min(1),
534
+ mode: z4.enum(["pinned", "tracking"]),
535
+ selected: z4.array(z4.string().min(1)),
536
+ aliases: z4.record(z4.string(), z4.string().min(1)).optional(),
537
+ overrides: z4.array(z4.string().min(1)).optional(),
538
+ selectionImport: z4.object({
539
+ configPath: z4.string().min(1),
540
+ configHash: z4.string().min(16),
541
+ exportHash: z4.string().min(16),
542
+ exportName: z4.string().min(1),
543
+ extends: z4.array(z4.string().min(1)),
544
+ inherited: z4.array(z4.string().min(1)),
545
+ additions: z4.array(z4.string().min(1)),
546
+ exclusions: z4.array(z4.string().min(1)),
547
+ effective: z4.array(z4.string().min(1))
533
548
  }).optional()
534
549
  });
535
- var graphLockEdgeSchema = z3.object({
536
- from: z3.string().min(1),
537
- to: z3.string().min(1),
538
- alias: z3.string().min(1),
539
- source: z3.string().min(1),
540
- normalizedSource: z3.string().min(1),
541
- requestedRef: z3.string().min(1).optional(),
542
- version: z3.string().min(1).optional(),
543
- mode: z3.enum(["pinned", "tracking"]),
544
- optional: z3.boolean().default(false),
545
- selected: z3.array(z3.string().min(1))
550
+ var graphLockEdgeSchema = z4.object({
551
+ from: z4.string().min(1),
552
+ to: z4.string().min(1),
553
+ alias: z4.string().min(1),
554
+ source: z4.string().min(1),
555
+ normalizedSource: z4.string().min(1),
556
+ requestedRef: z4.string().min(1).optional(),
557
+ version: z4.string().min(1).optional(),
558
+ mode: z4.enum(["pinned", "tracking"]),
559
+ optional: z4.boolean().default(false),
560
+ selected: z4.array(z4.string().min(1))
546
561
  });
547
- var graphLockIncludeEdgeSchema = z3.object({
548
- fromNodeId: z3.string().min(1),
549
- alias: z3.string().min(1),
550
- toNodeId: z3.string().min(1),
551
- selector: z3.string().min(1),
552
- sourceHash: z3.string().min(16)
562
+ var graphLockIncludeEdgeSchema = z4.object({
563
+ fromNodeId: z4.string().min(1),
564
+ alias: z4.string().min(1),
565
+ toNodeId: z4.string().min(1),
566
+ selector: z4.string().min(1),
567
+ sourceHash: z4.string().min(16)
553
568
  });
554
- var graphLockArtifactSchema = z3.object({
555
- graphNodeId: z3.string().min(1),
556
- dependencyRole: z3.enum(["root", "direct", "transitive", "fragment"]),
569
+ var graphLockArtifactSchema = z4.object({
570
+ graphNodeId: z4.string().min(1),
571
+ dependencyRole: z4.enum(["root", "direct", "transitive", "fragment"]),
557
572
  type: artifactTypeSchema,
558
- name: z3.string().min(1),
559
- installName: z3.string().min(1),
560
- logicalSelector: z3.string().min(1),
561
- owners: z3.array(z3.string().min(1)),
562
- relativePath: z3.string().min(1),
573
+ name: z4.string().min(1),
574
+ installName: z4.string().min(1),
575
+ logicalSelector: z4.string().min(1),
576
+ owners: z4.array(z4.string().min(1)),
577
+ relativePath: z4.string().min(1),
563
578
  kind: fileKindSchema,
564
- hash: z3.string().min(16),
565
- channel: z3.enum(["managed", "overlay", "addition", "override", "ejected"]).default("managed"),
566
- composedFrom: z3.array(composedFromEntrySchema).optional()
579
+ hash: z4.string().min(16),
580
+ channel: z4.enum(["managed", "overlay", "addition", "override", "ejected"]).default("managed"),
581
+ composedFrom: z4.array(composedFromEntrySchema).optional()
567
582
  });
568
- var graphLockPlainNameIncumbentSchema = z3.object({
569
- adapter: z3.string().min(1),
570
- targetFingerprint: z3.string().min(1),
583
+ var graphLockPlainNameIncumbentSchema = z4.object({
584
+ adapter: z4.string().min(1),
585
+ targetFingerprint: z4.string().min(1),
571
586
  type: artifactTypeSchema,
572
- name: z3.string().min(1),
573
- graphNodeId: z3.string().min(1)
587
+ name: z4.string().min(1),
588
+ graphNodeId: z4.string().min(1)
574
589
  });
575
- var graphLockNamespacingSchema = z3.object({
576
- graphNodeId: z3.string().min(1),
590
+ var graphLockNamespacingSchema = z4.object({
591
+ graphNodeId: z4.string().min(1),
577
592
  type: artifactTypeSchema,
578
- name: z3.string().min(1),
579
- installName: z3.string().min(1),
580
- reason: z3.enum(["alias", "transitive-collision"])
593
+ name: z4.string().min(1),
594
+ installName: z4.string().min(1),
595
+ reason: z4.enum(["alias", "transitive-collision"])
581
596
  });
582
- var graphLockOverrideSchema = z3.object({
583
- rootId: z3.string().min(1),
584
- selector: z3.string().min(1),
585
- graphNodeId: z3.string().min(1),
586
- overriddenGraphNodeId: z3.string().min(1),
597
+ var graphLockOverrideSchema = z4.object({
598
+ rootId: z4.string().min(1),
599
+ selector: z4.string().min(1),
600
+ graphNodeId: z4.string().min(1),
601
+ overriddenGraphNodeId: z4.string().min(1),
587
602
  type: artifactTypeSchema,
588
- name: z3.string().min(1),
589
- installName: z3.string().min(1)
603
+ name: z4.string().min(1),
604
+ installName: z4.string().min(1)
590
605
  });
591
- var graphLockCanonicalSchema = z3.object({
592
- targetFingerprint: z3.string().min(1).optional(),
593
- roots: z3.array(graphLockRootSchema),
594
- nodes: z3.array(graphLockNodeSchema),
595
- edges: z3.array(graphLockEdgeSchema),
596
- includeEdges: z3.array(graphLockIncludeEdgeSchema).default([]),
597
- artifacts: z3.array(graphLockArtifactSchema).default([]),
598
- namespacing: z3.array(graphLockNamespacingSchema).default([]),
599
- overrides: z3.array(graphLockOverrideSchema).default([]),
600
- plainNameIncumbents: z3.array(graphLockPlainNameIncumbentSchema).default([])
606
+ var graphLockCanonicalSchema = z4.object({
607
+ targetFingerprint: z4.string().min(1).optional(),
608
+ roots: z4.array(graphLockRootSchema),
609
+ nodes: z4.array(graphLockNodeSchema),
610
+ edges: z4.array(graphLockEdgeSchema),
611
+ includeEdges: z4.array(graphLockIncludeEdgeSchema).default([]),
612
+ artifacts: z4.array(graphLockArtifactSchema).default([]),
613
+ namespacing: z4.array(graphLockNamespacingSchema).default([]),
614
+ overrides: z4.array(graphLockOverrideSchema).default([]),
615
+ plainNameIncumbents: z4.array(graphLockPlainNameIncumbentSchema).default([])
601
616
  });
602
- var graphLockSchema = z3.object({
603
- version: z3.literal(1),
604
- generatedAt: z3.string().datetime().optional(),
617
+ var graphLockSchema = z4.object({
618
+ version: z4.literal(1),
619
+ generatedAt: z4.string().datetime().optional(),
605
620
  canonical: graphLockCanonicalSchema
606
621
  });
607
622
  async function readGraphLock(path) {
@@ -1052,56 +1067,56 @@ import { createHash as createHash2 } from "crypto";
1052
1067
  import { resolve as resolve3 } from "path";
1053
1068
 
1054
1069
  // src/model/manifest.ts
1055
- import { z as z4 } from "zod";
1056
- var mergeValueSchema = z4.lazy(() => z4.union([
1057
- z4.null(),
1058
- z4.boolean(),
1059
- z4.number(),
1060
- z4.string(),
1061
- z4.array(mergeValueSchema),
1062
- z4.record(z4.string(), mergeValueSchema)
1070
+ import { z as z5 } from "zod";
1071
+ var mergeValueSchema = z5.lazy(() => z5.union([
1072
+ z5.null(),
1073
+ z5.boolean(),
1074
+ z5.number(),
1075
+ z5.string(),
1076
+ z5.array(mergeValueSchema),
1077
+ z5.record(z5.string(), mergeValueSchema)
1063
1078
  ]));
1064
- var mergeRemovalSchema = z4.record(z4.string(), mergeValueSchema);
1065
- var dependencyRoleSchema = z4.enum(["root", "direct", "transitive", "fragment"]);
1079
+ var mergeRemovalSchema = z5.record(z5.string(), mergeValueSchema);
1080
+ var dependencyRoleSchema = z5.enum(["root", "direct", "transitive", "fragment"]);
1066
1081
  var legacyUnownedWorkspaceOwner = "legacy:unowned";
1067
- var semanticPluginSpecSchema = z4.object({
1068
- runtime: z4.enum(["openclaw", "claude", "codex", "copilot", "hermes"]),
1069
- pluginName: z4.string().min(1),
1070
- marketplaceName: z4.string().min(1).optional(),
1071
- stateRoot: z4.string().min(1).optional(),
1072
- installCommands: z4.array(z4.array(z4.string()).min(1)).min(1),
1073
- uninstallCommands: z4.array(z4.array(z4.string()).min(1)).min(1),
1074
- enableCommands: z4.array(z4.array(z4.string()).min(1)).optional(),
1075
- disableCommands: z4.array(z4.array(z4.string()).min(1)).optional()
1082
+ var semanticPluginSpecSchema = z5.object({
1083
+ runtime: z5.enum(["openclaw", "claude", "codex", "copilot", "hermes"]),
1084
+ pluginName: z5.string().min(1),
1085
+ marketplaceName: z5.string().min(1).optional(),
1086
+ stateRoot: z5.string().min(1).optional(),
1087
+ installCommands: z5.array(z5.array(z5.string()).min(1)).min(1),
1088
+ uninstallCommands: z5.array(z5.array(z5.string()).min(1)).min(1),
1089
+ enableCommands: z5.array(z5.array(z5.string()).min(1)).optional(),
1090
+ disableCommands: z5.array(z5.array(z5.string()).min(1)).optional()
1076
1091
  });
1077
- var manifestEntryV1Schema = z4.object({
1078
- path: z4.string().min(1),
1092
+ var manifestEntryV1Schema = z5.object({
1093
+ path: z5.string().min(1),
1079
1094
  artifactType: artifactTypeSchema,
1080
- artifactName: z4.string().min(1),
1095
+ artifactName: z5.string().min(1),
1081
1096
  kind: fileKindSchema,
1082
- hash: z4.string().min(16),
1083
- sourceHash: z4.string().min(16),
1084
- updatedAt: z4.string().datetime(),
1085
- channel: z4.enum(["managed", "overlay", "addition", "override", "ejected"]).default("managed"),
1086
- packageName: z4.string().min(1).optional(),
1087
- semanticCommand: z4.array(z4.string()).optional(),
1097
+ hash: z5.string().min(16),
1098
+ sourceHash: z5.string().min(16),
1099
+ updatedAt: z5.string().datetime(),
1100
+ channel: z5.enum(["managed", "overlay", "addition", "override", "ejected"]).default("managed"),
1101
+ packageName: z5.string().min(1).optional(),
1102
+ semanticCommand: z5.array(z5.string()).optional(),
1088
1103
  semanticPlugin: semanticPluginSpecSchema.optional(),
1089
- executed: z4.boolean().optional(),
1090
- mergeStrategy: z4.enum(["json-deep", "openclaw-json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
1104
+ executed: z5.boolean().optional(),
1105
+ mergeStrategy: z5.enum(["json-deep", "openclaw-json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
1091
1106
  mergeRemoval: mergeRemovalSchema.optional(),
1092
- mergeCreatedDestination: z4.boolean().optional(),
1093
- mode: z4.enum(["managed-block"]).optional(),
1094
- composedFrom: z4.array(composedFromEntrySchema).optional()
1107
+ mergeCreatedDestination: z5.boolean().optional(),
1108
+ mode: z5.enum(["managed-block"]).optional(),
1109
+ composedFrom: z5.array(composedFromEntrySchema).optional()
1095
1110
  });
1096
1111
  var manifestEntrySchema = manifestEntryV1Schema.extend({
1097
- installName: z4.string().min(1),
1098
- logicalSelector: z4.string().min(1).optional(),
1099
- graphNodeId: z4.string().min(1).optional(),
1112
+ installName: z5.string().min(1),
1113
+ logicalSelector: z5.string().min(1).optional(),
1114
+ graphNodeId: z5.string().min(1).optional(),
1100
1115
  dependencyRole: dependencyRoleSchema.default("root"),
1101
- owners: z4.array(z4.string().min(1)).min(1),
1102
- refCount: z4.number().int().positive(),
1103
- workspaceOwner: z4.string().min(1).default(legacyUnownedWorkspaceOwner),
1104
- graphLockDigest: z4.string().min(1).optional()
1116
+ owners: z5.array(z5.string().min(1)).min(1),
1117
+ refCount: z5.number().int().positive(),
1118
+ workspaceOwner: z5.string().min(1).default(legacyUnownedWorkspaceOwner),
1119
+ graphLockDigest: z5.string().min(1).optional()
1105
1120
  }).transform((entry) => {
1106
1121
  const owners = [...new Set(entry.owners)].sort();
1107
1122
  return {
@@ -1110,59 +1125,60 @@ var manifestEntrySchema = manifestEntryV1Schema.extend({
1110
1125
  refCount: owners.length
1111
1126
  };
1112
1127
  });
1113
- var installManifestV1Schema = z4.object({
1114
- version: z4.literal(1),
1115
- adapter: z4.string().min(1),
1116
- targetRoot: z4.string().min(1),
1117
- generatedAt: z4.string().datetime(),
1118
- adapterCode: z4.object({
1119
- modulePath: z4.string().min(1),
1120
- hash: z4.string().min(16)
1128
+ var installManifestV1Schema = z5.object({
1129
+ version: z5.literal(1),
1130
+ adapter: z5.string().min(1),
1131
+ targetRoot: z5.string().min(1),
1132
+ generatedAt: z5.string().datetime(),
1133
+ adapterCode: z5.object({
1134
+ modulePath: z5.string().min(1),
1135
+ hash: z5.string().min(16)
1121
1136
  }).optional(),
1122
- entries: z4.array(manifestEntryV1Schema)
1137
+ entries: z5.array(manifestEntryV1Schema)
1123
1138
  }).transform((manifest) => ({
1124
1139
  ...manifest,
1125
1140
  legacy: true
1126
1141
  }));
1127
- var installManifestV2Schema = z4.object({
1128
- version: z4.literal(2),
1129
- adapter: z4.string().min(1),
1142
+ var installManifestV2Schema = z5.object({
1143
+ version: z5.literal(2),
1144
+ adapter: z5.string().min(1),
1130
1145
  installationType: installationTypeSchema.default(defaultInstallationType),
1131
- stateKey: z4.string().min(1).optional(),
1132
- targetRoot: z4.string().min(1),
1133
- generatedAt: z4.string().datetime(),
1134
- revision: z4.string().min(16),
1135
- adapterCode: z4.object({
1136
- modulePath: z4.string().min(1),
1137
- hash: z4.string().min(16)
1146
+ stateKey: z5.string().min(1).optional(),
1147
+ targetRoot: z5.string().min(1),
1148
+ generatedAt: z5.string().datetime(),
1149
+ revision: z5.string().min(16),
1150
+ adapterCode: z5.object({
1151
+ modulePath: z5.string().min(1),
1152
+ hash: z5.string().min(16)
1138
1153
  }).optional(),
1139
- entries: z4.array(manifestEntrySchema)
1154
+ entries: z5.array(manifestEntrySchema)
1140
1155
  }).transform((manifest) => ({
1141
1156
  ...manifest,
1142
1157
  legacy: false
1143
1158
  }));
1144
- var installManifestSchema = z4.union([installManifestV2Schema, installManifestV1Schema]);
1145
- var sourceLockSchema = z4.object({
1146
- version: z4.literal(1),
1147
- driver: z4.string().min(1),
1148
- source: z4.string().min(1),
1149
- resolvedPath: z4.string().min(1),
1150
- packageName: z4.string().min(1).optional(),
1151
- packageVersion: z4.string().min(1).optional(),
1152
- mode: z4.enum(["pinned", "tracking"]).default("pinned"),
1153
- requestedRef: z4.string().min(1).optional(),
1154
- resolvedCommit: z4.string().min(1).optional(),
1155
- sourceHash: z4.string().min(16).optional(),
1156
- generatedAt: z4.string().datetime(),
1157
- artifacts: z4.array(
1158
- z4.object({
1159
+ var installManifestSchema = z5.union([installManifestV2Schema, installManifestV1Schema]);
1160
+ var sourceLockSchema = z5.object({
1161
+ version: z5.literal(1),
1162
+ driver: z5.string().min(1),
1163
+ source: z5.string().min(1),
1164
+ resolvedPath: z5.string().min(1),
1165
+ packageName: z5.string().min(1).optional(),
1166
+ packageVersion: z5.string().min(1).optional(),
1167
+ mode: z5.enum(["pinned", "tracking"]).default("pinned"),
1168
+ requestedRef: z5.string().min(1).optional(),
1169
+ resolvedCommit: z5.string().min(1).optional(),
1170
+ cacheIdentity: immutableCacheIdentitySchema.optional(),
1171
+ sourceHash: z5.string().min(16).optional(),
1172
+ generatedAt: z5.string().datetime(),
1173
+ artifacts: z5.array(
1174
+ z5.object({
1159
1175
  type: artifactTypeSchema,
1160
- name: z4.string().min(1),
1161
- relativePath: z4.string().min(1),
1176
+ name: z5.string().min(1),
1177
+ relativePath: z5.string().min(1),
1162
1178
  kind: fileKindSchema,
1163
- hash: z4.string().min(16),
1179
+ hash: z5.string().min(16),
1164
1180
  format: artifactFormatSchema.optional(),
1165
- composedFrom: z4.array(composedFromEntrySchema).optional()
1181
+ composedFrom: z5.array(composedFromEntrySchema).optional()
1166
1182
  })
1167
1183
  )
1168
1184
  });
@@ -1307,7 +1323,7 @@ async function acquireApplyLock(targetRoot, adapter, transport = localTransport,
1307
1323
  try {
1308
1324
  await transport.mkdirExclusive(lockPath);
1309
1325
  } catch (error) {
1310
- if (!isAlreadyExists(error)) throw error;
1326
+ if (!isAlreadyExists2(error)) throw error;
1311
1327
  await handleExistingLock(lockPath, ownerPath, transport, options);
1312
1328
  await transport.mkdirExclusive(lockPath);
1313
1329
  }
@@ -1405,7 +1421,7 @@ async function readLockOwner(ownerPath, transport) {
1405
1421
  return void 0;
1406
1422
  }
1407
1423
  }
1408
- function isAlreadyExists(error) {
1424
+ function isAlreadyExists2(error) {
1409
1425
  return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
1410
1426
  }
1411
1427
  function journalTimestamp(date) {
@@ -1432,6 +1448,10 @@ async function mergeCodexTomlMcp(sourcePath, destPath) {
1432
1448
  await mkdir6(dirname7(destPath), { recursive: true });
1433
1449
  await writeFile6(destPath, merged, "utf8");
1434
1450
  }
1451
+ function mismatchedCodexTomlMcpServers(source, currentContent) {
1452
+ const servers = extractMcpServers(source);
1453
+ return Object.entries(servers).filter(([name, server]) => extractMcpServerBlock(currentContent, name) !== formatMcpServer(name, server)).map(([name]) => name);
1454
+ }
1435
1455
  function extractMcpServers(source) {
1436
1456
  const raw = isRecord2(source.mcpServers) ? source.mcpServers : source;
1437
1457
  const servers = {};
@@ -1483,6 +1503,32 @@ function formatMcpServer(name, server) {
1483
1503
  }
1484
1504
  return lines.join("\n");
1485
1505
  }
1506
+ function extractMcpServerBlock(content, serverName) {
1507
+ const lines = content.split(/\r?\n/);
1508
+ const blocks = [];
1509
+ let block;
1510
+ let collecting = false;
1511
+ for (const line of lines) {
1512
+ const section = line.match(/^\s*\[([^\]]+)]\s*$/)?.[1];
1513
+ if (section) {
1514
+ const match = section.match(/^mcp_servers\.([^\.\]]+)(?:\.|$)/);
1515
+ const name = match?.[1] ? unquoteTomlKey(match[1]) : void 0;
1516
+ if (collecting && block) {
1517
+ while (block.at(-1)?.trim() === "") block.pop();
1518
+ blocks.push(block);
1519
+ }
1520
+ collecting = name === serverName;
1521
+ block = collecting ? [] : void 0;
1522
+ }
1523
+ if (collecting) block?.push(line);
1524
+ }
1525
+ if (collecting && block) {
1526
+ while (block.at(-1)?.trim() === "") block.pop();
1527
+ blocks.push(block);
1528
+ }
1529
+ if (blocks.length === 0) return void 0;
1530
+ return blocks.map((lines2) => lines2.join("\n")).join("\n\n");
1531
+ }
1486
1532
  function formatTomlValue(value) {
1487
1533
  if (typeof value === "string") return JSON.stringify(value);
1488
1534
  if (typeof value === "number" || typeof value === "boolean") return String(value);
@@ -1571,9 +1617,53 @@ function dedupeArray2(values) {
1571
1617
  import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile8 } from "fs/promises";
1572
1618
  import { dirname as dirname9 } from "path";
1573
1619
  import { parse as parse3, stringify as stringify2 } from "yaml";
1574
- async function mergeRemovalForInstall(sourcePath, strategy, currentContent) {
1620
+ var MergeAdoptionMismatchError = class extends Error {
1621
+ };
1622
+ function assertExactMcpMergeContribution(removal, strategy, currentContent) {
1623
+ if (strategy === "codex-toml-mcp") {
1624
+ const mismatched = mismatchedCodexTomlMcpServers(removal, currentContent);
1625
+ if (mismatched.length > 0) {
1626
+ throw new MergeAdoptionMismatchError(`exact MCP retirement precondition failed: Codex MCP server content differs or is missing for ${mismatched.join(", ")}`);
1627
+ }
1628
+ return;
1629
+ }
1630
+ if (strategy !== "json-deep") {
1631
+ throw new MergeAdoptionMismatchError(`exact MCP retirement precondition failed: strategy ${strategy} is not supported`);
1632
+ }
1633
+ const mismatch = firstMcpContributionMismatch(parseMergeDestination(currentContent, strategy), removal);
1634
+ if (mismatch) {
1635
+ throw new MergeAdoptionMismatchError(`exact MCP retirement precondition failed: destination differs or is missing at ${mismatch}`);
1636
+ }
1637
+ }
1638
+ function combineMergeRemovals(existing, incoming) {
1639
+ return combineMergeValues(existing, incoming);
1640
+ }
1641
+ function hasMergeRemovalContent(removal) {
1642
+ if (!removal) return false;
1643
+ return Object.entries(removal).some(([key, value]) => {
1644
+ return !(key === "mcpServers" && isRecord4(value) && Object.keys(value).length === 0);
1645
+ });
1646
+ }
1647
+ async function mergeRemovalForInstall(sourcePath, strategy, currentContent, options = {}) {
1575
1648
  const source = await readMergeSource(sourcePath, strategy);
1576
1649
  if (currentContent === void 0) return source;
1650
+ if (options.adoptExistingMcp) {
1651
+ if (strategy === "codex-toml-mcp") {
1652
+ const mismatched = mismatchedCodexTomlMcpServers(source, currentContent);
1653
+ if (mismatched.length > 0) {
1654
+ throw new MergeAdoptionMismatchError(`cannot adopt merged contribution: Codex MCP server content differs or is missing for ${mismatched.join(", ")}`);
1655
+ }
1656
+ return source;
1657
+ }
1658
+ if (strategy !== "json-deep") {
1659
+ throw new MergeAdoptionMismatchError(`cannot adopt merged contribution: strategy ${strategy} is not supported for MCP adoption`);
1660
+ }
1661
+ const mismatch = firstMcpContributionMismatch(parseMergeDestination(currentContent, strategy), source);
1662
+ if (mismatch) {
1663
+ throw new MergeAdoptionMismatchError(`cannot adopt merged contribution: destination differs or is missing at ${mismatch}`);
1664
+ }
1665
+ return source;
1666
+ }
1577
1667
  if (strategy === "codex-toml-mcp") {
1578
1668
  const existingServers = codexTomlMcpServerNames(currentContent);
1579
1669
  const servers = isRecord4(source.mcpServers) ? source.mcpServers : source;
@@ -1621,6 +1711,33 @@ function introducedMergeContent(base, incoming) {
1621
1711
  }
1622
1712
  return introduced;
1623
1713
  }
1714
+ function firstMcpContributionMismatch(current, incoming) {
1715
+ if (!isRecord4(current) || !isRecord4(incoming)) return "$";
1716
+ if (!isRecord4(current.mcpServers) || !isRecord4(incoming.mcpServers)) return "$.mcpServers";
1717
+ for (const [name, incomingServer] of Object.entries(incoming.mcpServers)) {
1718
+ if (!(name in current.mcpServers) || !sameMcpValue(current.mcpServers[name], incomingServer)) {
1719
+ return `$.mcpServers.${name}`;
1720
+ }
1721
+ }
1722
+ for (const [key, incomingValue] of Object.entries(incoming)) {
1723
+ if (key === "mcpServers") continue;
1724
+ if (!(key in current) || !sameMcpValue(current[key], incomingValue)) return `$.${key}`;
1725
+ }
1726
+ return void 0;
1727
+ }
1728
+ function combineMergeValues(existing, incoming) {
1729
+ if (isRecord4(existing) && isRecord4(incoming)) {
1730
+ const combined = { ...existing };
1731
+ for (const [key, incomingValue] of Object.entries(incoming)) {
1732
+ combined[key] = key in combined ? combineMergeValues(combined[key], incomingValue) : incomingValue;
1733
+ }
1734
+ return combined;
1735
+ }
1736
+ if (Array.isArray(existing) && Array.isArray(incoming)) {
1737
+ return [...existing, ...incoming.filter((value) => !existing.some((current) => sameValue(current, value)))];
1738
+ }
1739
+ return incoming;
1740
+ }
1624
1741
  function removeIntroducedContent(current, removal) {
1625
1742
  for (const [key, removalValue] of Object.entries(removal)) {
1626
1743
  if (!(key in current)) continue;
@@ -1675,6 +1792,18 @@ function isRecord4(value) {
1675
1792
  function sameValue(left, right) {
1676
1793
  return JSON.stringify(left) === JSON.stringify(right);
1677
1794
  }
1795
+ function sameMcpValue(left, right) {
1796
+ if (Array.isArray(left) || Array.isArray(right)) {
1797
+ return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => sameMcpValue(value, right[index]));
1798
+ }
1799
+ if (isRecord4(left) || isRecord4(right)) {
1800
+ if (!isRecord4(left) || !isRecord4(right)) return false;
1801
+ const leftKeys = Object.keys(left).sort();
1802
+ const rightKeys = Object.keys(right).sort();
1803
+ return leftKeys.length === rightKeys.length && leftKeys.every((key, index) => key === rightKeys[index] && sameMcpValue(left[key], right[key]));
1804
+ }
1805
+ return left === right;
1806
+ }
1678
1807
  function unquoteTomlKey2(key) {
1679
1808
  if (!key.startsWith('"')) return key;
1680
1809
  try {
@@ -2043,6 +2172,7 @@ async function uninstall(plan, options = {}) {
2043
2172
  const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, resolvedOptions.lock, scope);
2044
2173
  try {
2045
2174
  await assertBaseRevision(plan, transport);
2175
+ await assertExactMergeRemovalPreconditions(removable, transport);
2046
2176
  const journal = {
2047
2177
  version: 1,
2048
2178
  mode: "uninstall",
@@ -2223,6 +2353,16 @@ async function applyOperation(operation, context) {
2223
2353
  if (operation.mergeCreatedDestination) {
2224
2354
  await transport.rm(operation.destPath);
2225
2355
  } else if (operation.mergeRemoval) {
2356
+ if (operation.exactMergeRemoval) {
2357
+ if (!await transport.pathExists(operation.destPath)) {
2358
+ throw new Error(`Exact MCP retirement destination is missing: ${operation.relativeDestPath}`);
2359
+ }
2360
+ assertExactMcpMergeContribution(
2361
+ operation.mergeRemoval,
2362
+ operation.mergeStrategy,
2363
+ await transport.readFile(operation.destPath)
2364
+ );
2365
+ }
2226
2366
  await removeMergeWithTransport(operation.destPath, operation.mergeStrategy, operation.mergeRemoval, transport);
2227
2367
  }
2228
2368
  } else {
@@ -2507,6 +2647,22 @@ async function assertBaseRevision(plan, transport) {
2507
2647
  throw new Error(`Install manifest changed since planning for ${plan.adapter}; replan needed`);
2508
2648
  }
2509
2649
  }
2650
+ async function assertExactMergeRemovalPreconditions(operations, transport) {
2651
+ for (const operation of operations) {
2652
+ if (!operation.exactMergeRemoval) continue;
2653
+ if (!operation.mergeStrategy || !operation.mergeRemoval) {
2654
+ throw new Error(`Invalid exact MCP retirement operation: ${operation.relativeDestPath}`);
2655
+ }
2656
+ if (!await transport.pathExists(operation.destPath)) {
2657
+ throw new Error(`Exact MCP retirement destination is missing: ${operation.relativeDestPath}`);
2658
+ }
2659
+ assertExactMcpMergeContribution(
2660
+ operation.mergeRemoval,
2661
+ operation.mergeStrategy,
2662
+ await transport.readFile(operation.destPath)
2663
+ );
2664
+ }
2665
+ }
2510
2666
  function isJournaledMutation(operation) {
2511
2667
  if (operation.semanticPlugin && (operation.action === "plugin" || operation.action === "remove")) return false;
2512
2668
  return operation.action === "create" || operation.action === "update" || operation.action === "remove";
@@ -3766,12 +3922,49 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
3766
3922
  if (op.mergeStrategy) {
3767
3923
  const existing2 = manifestByPath.get(op.relativeDestPath);
3768
3924
  const exists2 = await transport.pathExists(op.destPath);
3769
- const mergeRemoval = await mergeRemovalForInstall(op.sourcePath, op.mergeStrategy, exists2 ? await transport.readFile(op.destPath) : void 0);
3925
+ const currentContent = exists2 ? await transport.readFile(op.destPath) : void 0;
3926
+ const currentHash2 = exists2 ? await transport.hashPath(op.destPath) : void 0;
3927
+ if (existing2 && workspaceOwner && !entryOwnedByWorkspace(existing2, workspaceOwner)) {
3928
+ if (!await canAdoptLegacyUnownedEntry(existing2, op, transport)) {
3929
+ operations.push(keepForeignManifestEntryOperation(existing2, targetRoot, workspaceOwner, op, currentHash2));
3930
+ continue;
3931
+ }
3932
+ }
3933
+ const incompleteMergeOwnership = existing2 && existing2.mergeStrategy && !("mergeCreatedDestination" in existing2 && existing2.mergeCreatedDestination === true) && !("mergeRemoval" in existing2 && hasMergeRemovalContent(existing2.mergeRemoval));
3934
+ const adoptExisting = exists2 && (!existing2 || incompleteMergeOwnership) && options.forceConflict === true && op.artifactType === "mcp" && (op.mergeStrategy === "json-deep" || op.mergeStrategy === "codex-toml-mcp");
3935
+ let mergeRemoval;
3936
+ try {
3937
+ mergeRemoval = await mergeRemovalForInstall(op.sourcePath, op.mergeStrategy, currentContent, { adoptExistingMcp: adoptExisting });
3938
+ } catch (error) {
3939
+ if (!(error instanceof MergeAdoptionMismatchError)) throw error;
3940
+ operations.push({
3941
+ ...op,
3942
+ action: "conflict",
3943
+ currentHash: currentHash2,
3944
+ reason: error.message,
3945
+ blockedReason: error.message
3946
+ });
3947
+ continue;
3948
+ }
3949
+ if (existing2 && "mergeRemoval" in existing2 && existing2.mergeRemoval) {
3950
+ mergeRemoval = combineMergeRemovals(existing2.mergeRemoval, mergeRemoval);
3951
+ }
3770
3952
  if (!exists2) {
3771
3953
  operations.push({ ...op, action: "create", mergeRemoval, mergeCreatedDestination: true, reason: "merge destination missing" });
3772
3954
  continue;
3773
3955
  }
3774
- const currentHash2 = await transport.hashPath(op.destPath);
3956
+ if (adoptExisting) {
3957
+ operations.push({
3958
+ ...op,
3959
+ action: "skip",
3960
+ mergeRemoval,
3961
+ mergeCreatedDestination: existing2?.mergeCreatedDestination,
3962
+ currentHash: currentHash2,
3963
+ manifestHash: existing2?.hash,
3964
+ reason: existing2 ? "force repairing exact incomplete merge ownership" : "force adopting exact unmanaged merge contribution"
3965
+ });
3966
+ continue;
3967
+ }
3775
3968
  if (existing2 && existing2.sourceHash === op.desiredHash) {
3776
3969
  operations.push(options.forceDrift ? { ...op, action: "update", mergeRemoval, mergeCreatedDestination: existing2.mergeCreatedDestination, currentHash: currentHash2, manifestHash: existing2.hash, reason: "force refreshing managed merge destination" } : { ...op, action: "skip", mergeRemoval: existing2.mergeRemoval, mergeCreatedDestination: existing2.mergeCreatedDestination, currentHash: currentHash2, manifestHash: existing2.hash, reason: "merged source already up to date" });
3777
3970
  } else {
@@ -4426,7 +4619,7 @@ async function createUninstallPlan(manifest, transport = localTransport) {
4426
4619
  composedFrom: entry.composedFrom,
4427
4620
  ...operationMetadataFromEntry2(entry)
4428
4621
  });
4429
- } else if (entry.mergeStrategy && !("mergeRemoval" in entry && entry.mergeRemoval !== void 0) && !("mergeCreatedDestination" in entry && entry.mergeCreatedDestination === true)) {
4622
+ } else if (entry.mergeStrategy && !("mergeRemoval" in entry && hasMergeRemovalContent(entry.mergeRemoval)) && !("mergeCreatedDestination" in entry && entry.mergeCreatedDestination === true)) {
4430
4623
  operations.push(legacyMergeKeepOperation(entry, destPath));
4431
4624
  } else {
4432
4625
  operations.push({
@@ -4527,7 +4720,7 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
4527
4720
  ...operationMetadataFromEntry2(entry),
4528
4721
  graphLockDigest: options.graphLockDigest
4529
4722
  });
4530
- } else if (entry.mergeStrategy && !("mergeRemoval" in entry && entry.mergeRemoval !== void 0) && !("mergeCreatedDestination" in entry && entry.mergeCreatedDestination === true)) {
4723
+ } else if (entry.mergeStrategy && !("mergeRemoval" in entry && hasMergeRemovalContent(entry.mergeRemoval)) && !("mergeCreatedDestination" in entry && entry.mergeCreatedDestination === true)) {
4531
4724
  operations.push(legacyMergeKeepOperation(entry, destPath));
4532
4725
  } else {
4533
4726
  operations.push({
@@ -5396,8 +5589,8 @@ import { basename as basename9, join as join19, relative as relative4, resolve a
5396
5589
  import { readFile as readFile17 } from "fs/promises";
5397
5590
  import { join as join18 } from "path";
5398
5591
  import { parse as parse4, printParseErrorCode as printParseErrorCode2 } from "jsonc-parser";
5399
- import { z as z5 } from "zod";
5400
- var legacyArtifactTypeSchema = z5.enum([
5592
+ import { z as z6 } from "zod";
5593
+ var legacyArtifactTypeSchema = z6.enum([
5401
5594
  "instructions",
5402
5595
  "rules",
5403
5596
  "skills",
@@ -5408,33 +5601,33 @@ var legacyArtifactTypeSchema = z5.enum([
5408
5601
  "settings",
5409
5602
  "plugins"
5410
5603
  ]);
5411
- var runtimeListSchema = z5.array(z5.string().min(1));
5412
- var packageProvideBaseSchema = z5.object({
5413
- path: z5.string().min(1),
5604
+ var runtimeListSchema = z6.array(z6.string().min(1));
5605
+ var packageProvideBaseSchema = z6.object({
5606
+ path: z6.string().min(1),
5414
5607
  format: artifactFormatSchema.optional(),
5415
- assets: z5.array(packageAssetSchema).optional(),
5416
- required: z5.boolean().optional()
5608
+ assets: z6.array(packageAssetSchema).optional(),
5609
+ required: z6.boolean().optional()
5417
5610
  });
5418
- var packageItemSchema = z5.object({
5611
+ var packageItemSchema = z6.object({
5419
5612
  format: artifactFormatSchema.optional(),
5420
- requires: z5.array(packageItemRequireSchema).optional(),
5421
- suggests: z5.array(packageItemSuggestSchema).optional(),
5422
- compose: z5.array(packageComposeEntrySchema).optional(),
5613
+ requires: z6.array(packageItemRequireSchema).optional(),
5614
+ suggests: z6.array(packageItemSuggestSchema).optional(),
5615
+ compose: z6.array(packageComposeEntrySchema).optional(),
5423
5616
  runtimes: runtimeListSchema.optional()
5424
5617
  });
5425
- var packageDependencySchema = z5.object({
5426
- source: z5.string().min(1),
5427
- ref: z5.string().min(1).optional(),
5428
- version: z5.string().min(1).optional(),
5429
- select: z5.array(z5.string().min(1)).optional(),
5430
- mode: z5.enum(["pinned", "tracking"]).optional(),
5431
- optional: z5.boolean().optional(),
5432
- integrity: z5.string().min(1).optional(),
5618
+ var packageDependencySchema = z6.object({
5619
+ source: z6.string().min(1),
5620
+ ref: z6.string().min(1).optional(),
5621
+ version: z6.string().min(1).optional(),
5622
+ select: z6.array(z6.string().min(1)).optional(),
5623
+ mode: z6.enum(["pinned", "tracking"]).optional(),
5624
+ optional: z6.boolean().optional(),
5625
+ integrity: z6.string().min(1).optional(),
5433
5626
  runtimes: runtimeListSchema.optional()
5434
5627
  });
5435
5628
  var packageSuggestionSchema = packageDependencySchema.extend({
5436
- reason: z5.string().min(1).optional(),
5437
- when: z5.string().min(1).optional()
5629
+ reason: z6.string().min(1).optional(),
5630
+ when: z6.string().min(1).optional()
5438
5631
  });
5439
5632
  var packageProvideV1Schema = packageProvideBaseSchema.extend({
5440
5633
  type: legacyArtifactTypeSchema
@@ -5442,35 +5635,35 @@ var packageProvideV1Schema = packageProvideBaseSchema.extend({
5442
5635
  var packageProvideSchema = packageProvideBaseSchema.extend({
5443
5636
  type: artifactTypeSchema,
5444
5637
  runtimes: runtimeListSchema.optional(),
5445
- items: z5.record(z5.string().min(1), packageItemSchema).optional()
5638
+ items: z6.record(z6.string().min(1), packageItemSchema).optional()
5446
5639
  });
5447
- var packageManifestV1Schema = z5.object({
5448
- schemaVersion: z5.literal(1),
5449
- name: z5.string().min(1),
5450
- version: z5.string().min(1),
5451
- provides: z5.array(packageProvideV1Schema).min(1)
5640
+ var packageManifestV1Schema = z6.object({
5641
+ schemaVersion: z6.literal(1),
5642
+ name: z6.string().min(1),
5643
+ version: z6.string().min(1),
5644
+ provides: z6.array(packageProvideV1Schema).min(1)
5452
5645
  });
5453
- var packageManifestV2Schema = z5.object({
5454
- schemaVersion: z5.literal(2),
5455
- name: z5.string().min(1),
5456
- version: z5.string().min(1),
5646
+ var packageManifestV2Schema = z6.object({
5647
+ schemaVersion: z6.literal(2),
5648
+ name: z6.string().min(1),
5649
+ version: z6.string().min(1),
5457
5650
  runtimes: runtimeListSchema.optional(),
5458
- requires: z5.record(z5.string().min(1), packageDependencySchema).optional(),
5459
- suggests: z5.record(z5.string().min(1), packageSuggestionSchema).optional(),
5460
- compose: z5.array(packageComposeEntrySchema).optional(),
5461
- provides: z5.array(packageProvideSchema).default([])
5651
+ requires: z6.record(z6.string().min(1), packageDependencySchema).optional(),
5652
+ suggests: z6.record(z6.string().min(1), packageSuggestionSchema).optional(),
5653
+ compose: z6.array(packageComposeEntrySchema).optional(),
5654
+ provides: z6.array(packageProvideSchema).default([])
5462
5655
  }).superRefine((manifest, ctx) => {
5463
5656
  const hasProvides = manifest.provides.length > 0;
5464
5657
  const hasRequires = Object.keys(manifest.requires ?? {}).length > 0;
5465
5658
  if (!hasProvides && !hasRequires) {
5466
5659
  ctx.addIssue({
5467
- code: z5.ZodIssueCode.custom,
5660
+ code: z6.ZodIssueCode.custom,
5468
5661
  path: ["provides"],
5469
5662
  message: "OpenPack v2 manifest must declare at least one provides entry or one requires dependency"
5470
5663
  });
5471
5664
  }
5472
5665
  });
5473
- var packageManifestSchema = z5.union([packageManifestV1Schema, packageManifestV2Schema]);
5666
+ var packageManifestSchema = z6.union([packageManifestV1Schema, packageManifestV2Schema]);
5474
5667
  var openPackManifestNames = ["openpack.json", "openpack.jsonc"];
5475
5668
  var legacyPackageManifestNames = ["agentwheel.json", "agentwheel.jsonc"];
5476
5669
  var packageManifestNames = [...openPackManifestNames, ...legacyPackageManifestNames];
@@ -5664,7 +5857,7 @@ var LocalSourceDriver = class {
5664
5857
  async scan(resolved) {
5665
5858
  const artifacts = await this.list(resolved);
5666
5859
  const findings = [];
5667
- if (!artifacts.some((artifact) => artifact.type === "instructions")) {
5860
+ if (!resolved.packageName && !artifacts.some((artifact) => artifact.type === "instructions")) {
5668
5861
  findings.push({ level: "warning", message: "No instructions.md or AGENTS.md found", path: resolved.resolvedPath });
5669
5862
  }
5670
5863
  for (const artifact of artifacts.filter((item) => item.type === "skills" && item.kind === "dir")) {
@@ -5730,6 +5923,7 @@ async function listFromManifest(root, packageName) {
5730
5923
  if ((provide.type === "skills" || provide.type === "plugins" || provide.type === "subagents") && entry.isDirectory()) {
5731
5924
  artifacts.push(await artifactForDir(provide.type, entry.name, child, join19(provide.path, entry.name), packageName, provide, manifest, entry.name));
5732
5925
  } else if (entry.isFile()) {
5926
+ if (provide.type === "skills" && entry.name.toLowerCase() === "readme.md") continue;
5733
5927
  const name = provide.type === "rules" && entry.name.endsWith(".md") ? entry.name : entry.name;
5734
5928
  artifacts.push(await artifactForFile(provide.type, name, child, join19(provide.path, entry.name), packageName, provide, manifest, name));
5735
5929
  }
@@ -5949,9 +6143,9 @@ function cachePathFor(packageName, cacheRoot) {
5949
6143
 
5950
6144
  // src/source/git.ts
5951
6145
  import { execFile as execFile4 } from "child_process";
5952
- import { cp as cp2, mkdir as mkdir13, rename as rename3, rm as rm6, writeFile as writeFile15 } from "fs/promises";
6146
+ import { cp as cp2, mkdir as mkdir14, rename as rename3, rm as rm7 } from "fs/promises";
5953
6147
  import { homedir as homedir3 } from "os";
5954
- import { basename as basename11, dirname as dirname16, join as join22, resolve as resolve7 } from "path";
6148
+ import { basename as basename11, dirname as dirname17, join as join23, resolve as resolve7 } from "path";
5955
6149
  import { promisify as promisify4 } from "util";
5956
6150
 
5957
6151
  // src/source/auth.ts
@@ -6023,6 +6217,209 @@ function isMissingFile(error) {
6023
6217
  return isRecord8(error) && error.code === "ENOENT";
6024
6218
  }
6025
6219
 
6220
+ // src/source/cache.ts
6221
+ import { randomUUID } from "crypto";
6222
+ import { mkdir as mkdir13, readdir as readdir2, readFile as readFile19, rm as rm6, stat as stat4, writeFile as writeFile15 } from "fs/promises";
6223
+ import { dirname as dirname16, join as join22 } from "path";
6224
+ var snapshotNamePattern = /^(.*)-([0-9a-f]{12})$/i;
6225
+ var leaseMarker = ".agentwheel-lease-";
6226
+ async function pruneGitCache(cacheRoot, options = {}) {
6227
+ if (!await pathExists(cacheRoot)) return { removedPaths: [], retainedPaths: [] };
6228
+ if (options.maintenanceLockHeld) return pruneGitCacheUnlocked(cacheRoot, options);
6229
+ return withGitCacheMaintenanceLock(
6230
+ cacheRoot,
6231
+ options.cacheLockTimeoutMs ?? 3e4,
6232
+ () => pruneGitCacheUnlocked(cacheRoot, options)
6233
+ );
6234
+ }
6235
+ async function withGitCacheMaintenanceLock(cacheRoot, timeoutMs, fn) {
6236
+ const lockPath = join22(cacheRoot, ".maintenance.lock");
6237
+ await mkdir13(cacheRoot, { recursive: true });
6238
+ const started = Date.now();
6239
+ while (true) {
6240
+ try {
6241
+ await mkdir13(lockPath);
6242
+ await writeFile15(
6243
+ join22(lockPath, "owner.json"),
6244
+ JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }),
6245
+ "utf8"
6246
+ );
6247
+ break;
6248
+ } catch (error) {
6249
+ if (!isAlreadyExists3(error)) throw error;
6250
+ if (await removeStaleMaintenanceLock(lockPath)) continue;
6251
+ if (Date.now() - started > timeoutMs) {
6252
+ throw new Error(`Timed out waiting for git cache maintenance lock at ${lockPath}`);
6253
+ }
6254
+ await new Promise((resolve23) => setTimeout(resolve23, 50));
6255
+ }
6256
+ }
6257
+ try {
6258
+ return await fn();
6259
+ } finally {
6260
+ await rm6(lockPath, { recursive: true, force: true });
6261
+ }
6262
+ }
6263
+ async function createGitSnapshotLease(snapshotPath) {
6264
+ const leasePath = `${snapshotPath}${leaseMarker}${process.pid}-${randomUUID()}`;
6265
+ await writeFile15(leasePath, JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), {
6266
+ encoding: "utf8",
6267
+ flag: "wx"
6268
+ });
6269
+ return leasePath;
6270
+ }
6271
+ async function releaseGitSnapshotLease(leasePath) {
6272
+ if (leasePath) await rm6(leasePath, { force: true });
6273
+ }
6274
+ async function removeGeneratedEntries(root, preserveGit) {
6275
+ let entries;
6276
+ try {
6277
+ entries = await readdir2(root, { withFileTypes: true });
6278
+ } catch {
6279
+ return;
6280
+ }
6281
+ for (const entry of entries) {
6282
+ const path = join22(root, entry.name);
6283
+ if (isIgnoredGeneratedEntry(entry.name) && !(preserveGit && entry.name === ".git")) {
6284
+ await rm6(path, { recursive: true, force: true });
6285
+ } else if (entry.isDirectory()) {
6286
+ await removeGeneratedEntries(path, preserveGit);
6287
+ }
6288
+ }
6289
+ }
6290
+ async function pruneGitCacheUnlocked(cacheRoot, options) {
6291
+ const referencedCommits = await referencedGraphLockCommits(join22(dirname16(cacheRoot), "locks"));
6292
+ const entries = await readdir2(cacheRoot, { withFileTypes: true });
6293
+ const groups = /* @__PURE__ */ new Map();
6294
+ for (const entry of entries) {
6295
+ if (!entry.isDirectory()) continue;
6296
+ const match = snapshotNamePattern.exec(entry.name);
6297
+ if (!match) continue;
6298
+ const checkoutPath = join22(cacheRoot, match[1]);
6299
+ if (!await pathExists(join22(checkoutPath, ".git"))) continue;
6300
+ const snapshotPath = join22(cacheRoot, entry.name);
6301
+ const snapshotStats = await stat4(snapshotPath);
6302
+ const snapshots = groups.get(checkoutPath) ?? [];
6303
+ snapshots.push({ path: snapshotPath, commitPrefix: match[2].toLowerCase(), modifiedAt: snapshotStats.mtimeMs });
6304
+ groups.set(checkoutPath, snapshots);
6305
+ }
6306
+ const keepCount = Math.max(1, Math.floor(options.keepSnapshots ?? 3));
6307
+ const removedPaths = [];
6308
+ const retainedPaths = [];
6309
+ for (const [checkoutPath, snapshots] of groups) {
6310
+ if (!options.dryRun) await removeGeneratedEntries(checkoutPath, true);
6311
+ snapshots.sort((a, b) => b.modifiedAt - a.modifiedAt || a.path.localeCompare(b.path));
6312
+ const keep = /* @__PURE__ */ new Set();
6313
+ for (const snapshot of snapshots.slice(0, keepCount)) keep.add(snapshot.path);
6314
+ if (options.currentSnapshot) keep.add(options.currentSnapshot);
6315
+ for (const snapshot of snapshots) {
6316
+ if ([...referencedCommits].some((commit) => commit.startsWith(snapshot.commitPrefix))) keep.add(snapshot.path);
6317
+ if (await hasLiveSnapshotLease(snapshot.path, options.dryRun === true)) keep.add(snapshot.path);
6318
+ }
6319
+ for (const snapshot of snapshots) {
6320
+ if (keep.has(snapshot.path)) {
6321
+ retainedPaths.push(snapshot.path);
6322
+ if (!options.dryRun) await removeGeneratedEntries(snapshot.path, false);
6323
+ } else {
6324
+ removedPaths.push(snapshot.path);
6325
+ if (!options.dryRun) await rm6(snapshot.path, { recursive: true, force: true });
6326
+ }
6327
+ }
6328
+ }
6329
+ return { removedPaths, retainedPaths };
6330
+ }
6331
+ async function referencedGraphLockCommits(lockRoot) {
6332
+ const commits = /* @__PURE__ */ new Set();
6333
+ await walkGraphLocks(lockRoot, (value) => collectResolvedCommits(value, commits));
6334
+ return commits;
6335
+ }
6336
+ async function walkGraphLocks(root, visit) {
6337
+ let entries;
6338
+ try {
6339
+ entries = await readdir2(root, { withFileTypes: true });
6340
+ } catch (error) {
6341
+ if (isNotFound(error)) return;
6342
+ throw new Error(`Cannot inspect graph-lock directory ${root}; cache prune aborted: ${errorMessage3(error)}`);
6343
+ }
6344
+ for (const entry of entries) {
6345
+ const path = join22(root, entry.name);
6346
+ if (entry.isDirectory()) {
6347
+ await walkGraphLocks(path, visit);
6348
+ } else if (entry.isFile() && entry.name.endsWith(".graph-lock.json")) {
6349
+ try {
6350
+ visit(JSON.parse(await readFile19(path, "utf8")));
6351
+ } catch (error) {
6352
+ throw new Error(`Cannot read graph lock ${path}; cache prune aborted: ${errorMessage3(error)}`);
6353
+ }
6354
+ }
6355
+ }
6356
+ }
6357
+ async function hasLiveSnapshotLease(snapshotPath, dryRun) {
6358
+ const directory = dirname16(snapshotPath);
6359
+ const prefix = `${snapshotPath.slice(directory.length + 1)}${leaseMarker}`;
6360
+ const entries = await readdir2(directory, { withFileTypes: true });
6361
+ let live = false;
6362
+ for (const entry of entries) {
6363
+ if (!entry.isFile() || !entry.name.startsWith(prefix)) continue;
6364
+ const leasePath = join22(directory, entry.name);
6365
+ try {
6366
+ const value = JSON.parse(await readFile19(leasePath, "utf8"));
6367
+ if (typeof value.pid !== "number" || !Number.isSafeInteger(value.pid) || value.pid <= 0) {
6368
+ live = true;
6369
+ } else if (isProcessAlive(value.pid)) {
6370
+ live = true;
6371
+ } else if (!dryRun) {
6372
+ await rm6(leasePath, { force: true });
6373
+ }
6374
+ } catch {
6375
+ live = true;
6376
+ }
6377
+ }
6378
+ return live;
6379
+ }
6380
+ async function removeStaleMaintenanceLock(lockPath) {
6381
+ try {
6382
+ const value = JSON.parse(await readFile19(join22(lockPath, "owner.json"), "utf8"));
6383
+ if (typeof value.pid !== "number" || !Number.isSafeInteger(value.pid) || value.pid <= 0 || isProcessAlive(value.pid)) {
6384
+ return false;
6385
+ }
6386
+ await rm6(lockPath, { recursive: true, force: true });
6387
+ return true;
6388
+ } catch {
6389
+ return false;
6390
+ }
6391
+ }
6392
+ function isProcessAlive(pid) {
6393
+ try {
6394
+ process.kill(pid, 0);
6395
+ return true;
6396
+ } catch (error) {
6397
+ return typeof error === "object" && error !== null && "code" in error && error.code !== "ESRCH";
6398
+ }
6399
+ }
6400
+ function collectResolvedCommits(value, commits) {
6401
+ if (Array.isArray(value)) {
6402
+ for (const item of value) collectResolvedCommits(item, commits);
6403
+ return;
6404
+ }
6405
+ if (!value || typeof value !== "object") return;
6406
+ for (const [key, item] of Object.entries(value)) {
6407
+ if (key === "resolvedCommit" && typeof item === "string" && /^[0-9a-f]{12,40}$/i.test(item)) {
6408
+ commits.add(item.toLowerCase());
6409
+ }
6410
+ collectResolvedCommits(item, commits);
6411
+ }
6412
+ }
6413
+ function isAlreadyExists3(error) {
6414
+ return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
6415
+ }
6416
+ function isNotFound(error) {
6417
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
6418
+ }
6419
+ function errorMessage3(error) {
6420
+ return error instanceof Error ? error.message : String(error);
6421
+ }
6422
+
6026
6423
  // src/source/git.ts
6027
6424
  var execFileAsync4 = promisify4(execFile4);
6028
6425
  var GitSourceDriver = class {
@@ -6045,12 +6442,12 @@ var GitSourceDriver = class {
6045
6442
  async fetch(resolved) {
6046
6443
  return withFilesystemLock(`${resolved.resolvedPath}.lock`, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
6047
6444
  const parsed = parseGitSource(resolved.source);
6048
- await mkdir13(resolve7(resolved.resolvedPath, ".."), { recursive: true });
6049
- if (!await pathExists(join22(resolved.resolvedPath, ".git"))) {
6445
+ await mkdir14(resolve7(resolved.resolvedPath, ".."), { recursive: true });
6446
+ if (!await pathExists(join23(resolved.resolvedPath, ".git"))) {
6050
6447
  if (resolved.frozenLock) {
6051
6448
  throw new Error(`Frozen lock requires cached git checkout at ${resolved.resolvedPath}`);
6052
6449
  }
6053
- await rm6(resolved.resolvedPath, { recursive: true, force: true });
6450
+ await rm7(resolved.resolvedPath, { recursive: true, force: true });
6054
6451
  await git([...await gitAuthArguments(parsed.url), "clone", parsed.url, resolved.resolvedPath]);
6055
6452
  } else if (!resolved.frozenLock) {
6056
6453
  await git([
@@ -6076,9 +6473,24 @@ var GitSourceDriver = class {
6076
6473
  await git(["-C", resolved.resolvedPath, "checkout", "--detach", ref]);
6077
6474
  }
6078
6475
  }
6476
+ await removeGeneratedEntries(resolved.resolvedPath, true);
6079
6477
  const { stdout } = await git(["-C", resolved.resolvedPath, "rev-parse", "HEAD"]);
6080
6478
  const resolvedCommit = stdout.trim();
6081
- const snapshotPath = await snapshotCheckout(resolved.resolvedPath, resolvedCommit);
6479
+ const cacheRoot = dirname17(resolved.resolvedPath);
6480
+ const snapshot = await withGitCacheMaintenanceLock(
6481
+ cacheRoot,
6482
+ resolved.cacheLockTimeoutMs ?? 3e4,
6483
+ async () => {
6484
+ const path = await snapshotCheckout(resolved.resolvedPath, resolvedCommit);
6485
+ const leasePath = await createGitSnapshotLease(path);
6486
+ await pruneGitCache(cacheRoot, {
6487
+ currentSnapshot: path,
6488
+ maintenanceLockHeld: true
6489
+ });
6490
+ return { path, leasePath };
6491
+ }
6492
+ );
6493
+ const snapshotPath = snapshot.path;
6082
6494
  const manifest = await readPackageManifest(snapshotPath);
6083
6495
  return {
6084
6496
  ...resolved,
@@ -6086,9 +6498,10 @@ var GitSourceDriver = class {
6086
6498
  packageName: manifest?.name,
6087
6499
  packageVersion: manifest?.version,
6088
6500
  resolvedCommit,
6089
- sourceHash: await hashPath(snapshotPath)
6501
+ sourceHash: await hashPath(snapshotPath),
6502
+ cacheLeasePath: snapshot.leasePath
6090
6503
  };
6091
- });
6504
+ }, "git cache");
6092
6505
  }
6093
6506
  async list(resolved) {
6094
6507
  return this.local.list({ ...resolved, driver: "local" });
@@ -6121,58 +6534,37 @@ function parseGitSource(source) {
6121
6534
  throw new Error(`Invalid git source: ${source}`);
6122
6535
  }
6123
6536
  function cachePathFor2(url, cacheRoot) {
6124
- const root = cacheRoot ? resolve7(cacheRoot) : join22(homedir3(), ".agentwheel", "cache");
6537
+ const root = cacheRoot ? resolve7(cacheRoot) : join23(homedir3(), ".agentwheel", "cache");
6125
6538
  const slug2 = url.replace(/^[a-z]+:\/\//i, "").replace(/\.git$/i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
6126
- return join22(root, slug2 || basename11(url));
6539
+ return join23(root, slug2 || basename11(url));
6127
6540
  }
6128
6541
  async function git(args) {
6129
6542
  return execFileAsync4("git", args, { maxBuffer: 1024 * 1024 * 10 });
6130
6543
  }
6131
6544
  async function snapshotCheckout(checkoutPath, commit) {
6132
- const snapshotPath = join22(dirname16(checkoutPath), `${basename11(checkoutPath)}-${commit.slice(0, 12)}`);
6545
+ const snapshotPath = join23(dirname17(checkoutPath), `${basename11(checkoutPath)}-${commit.slice(0, 12)}`);
6133
6546
  if (await pathExists(snapshotPath)) return snapshotPath;
6134
- const tempPath = join22(dirname16(checkoutPath), `${basename11(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
6135
- await rm6(tempPath, { recursive: true, force: true });
6136
- await cp2(checkoutPath, tempPath, { recursive: true, dereference: true });
6137
- await rm6(join22(tempPath, ".git"), { recursive: true, force: true });
6547
+ const tempPath = join23(dirname17(checkoutPath), `${basename11(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
6548
+ await rm7(tempPath, { recursive: true, force: true });
6549
+ await cp2(checkoutPath, tempPath, {
6550
+ recursive: true,
6551
+ dereference: true,
6552
+ filter: (path) => !isIgnoredGeneratedEntry(basename11(path))
6553
+ });
6554
+ await rm7(join23(tempPath, ".git"), { recursive: true, force: true });
6138
6555
  try {
6139
6556
  await rename3(tempPath, snapshotPath);
6140
6557
  } catch (error) {
6141
- if (!isAlreadyExists2(error)) throw error;
6142
- await rm6(tempPath, { recursive: true, force: true });
6558
+ if (!isAlreadyExists(error)) throw error;
6559
+ await rm7(tempPath, { recursive: true, force: true });
6143
6560
  return snapshotPath;
6144
6561
  }
6145
6562
  return snapshotPath;
6146
6563
  }
6147
- async function withFilesystemLock(lockPath, timeoutMs, fn) {
6148
- await mkdir13(dirname16(lockPath), { recursive: true });
6149
- const started = Date.now();
6150
- while (true) {
6151
- try {
6152
- await mkdir13(lockPath);
6153
- await writeFile15(join22(lockPath, "owner.json"), JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
6154
- break;
6155
- } catch (error) {
6156
- if (!isAlreadyExists2(error)) throw error;
6157
- if (Date.now() - started > timeoutMs) {
6158
- throw new Error(`Timed out waiting for git cache lock at ${lockPath}`);
6159
- }
6160
- await new Promise((resolve23) => setTimeout(resolve23, 50));
6161
- }
6162
- }
6163
- try {
6164
- return await fn();
6165
- } finally {
6166
- await rm6(lockPath, { recursive: true, force: true });
6167
- }
6168
- }
6169
- function isAlreadyExists2(error) {
6170
- return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
6171
- }
6172
6564
 
6173
6565
  // src/source/mcp-registry.ts
6174
- import { mkdir as mkdir14, writeFile as writeFile16 } from "fs/promises";
6175
- import { basename as basename12, dirname as dirname17, join as join23, resolve as resolve8 } from "path";
6566
+ import { mkdir as mkdir15, writeFile as writeFile16 } from "fs/promises";
6567
+ import { basename as basename12, dirname as dirname18, join as join24, resolve as resolve8 } from "path";
6176
6568
  var registryBaseUrl = "https://registry.modelcontextprotocol.io/v0.1";
6177
6569
  var sourcePrefix2 = "mcp-registry:";
6178
6570
  var McpRegistrySourceDriver = class {
@@ -6238,7 +6630,7 @@ var McpRegistrySourceDriver = class {
6238
6630
  return this.local.list({ ...resolved, driver: "local" });
6239
6631
  }
6240
6632
  async scan(resolved) {
6241
- if (!await pathExists(join23(resolved.resolvedPath, "mcp"))) {
6633
+ if (!await pathExists(join24(resolved.resolvedPath, "mcp"))) {
6242
6634
  return { ok: false, findings: [{ level: "error", message: "MCP registry source has no generated mcp artifact" }] };
6243
6635
  }
6244
6636
  return { ok: true, findings: [] };
@@ -6278,9 +6670,9 @@ function isSafeHttpUrl(value) {
6278
6670
  }
6279
6671
  async function writeGeneratedPackage2(root, server) {
6280
6672
  const serverId = installNameFor3(server.serverName);
6281
- const mcpPath = join23(root, "mcp", `${serverId}.json`);
6282
- await mkdir14(dirname17(mcpPath), { recursive: true });
6283
- await writeFile16(join23(root, "openpack.json"), `${JSON.stringify({
6673
+ const mcpPath = join24(root, "mcp", `${serverId}.json`);
6674
+ await mkdir15(dirname18(mcpPath), { recursive: true });
6675
+ await writeFile16(join24(root, "openpack.json"), `${JSON.stringify({
6284
6676
  schemaVersion: 2,
6285
6677
  name: `mcp-registry/${server.serverName}`,
6286
6678
  version: server.version ?? "latest",
@@ -6301,20 +6693,23 @@ function installNameFor3(serverName) {
6301
6693
  return basename12(serverName).replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "mcp-server";
6302
6694
  }
6303
6695
  function cachePathFor3(serverName, cacheRoot) {
6304
- const root = cacheRoot ? resolve8(cacheRoot) : join23(process.env.HOME ?? ".", ".agentwheel", "cache");
6696
+ const root = cacheRoot ? resolve8(cacheRoot) : join24(process.env.HOME ?? ".", ".agentwheel", "cache");
6305
6697
  const slug2 = `mcp-registry-${serverName}`.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
6306
- return join23(root, slug2 || "mcp-registry-server");
6698
+ return join24(root, slug2 || "mcp-registry-server");
6307
6699
  }
6308
6700
 
6309
6701
  // src/source/skillkit.ts
6310
- import { cp as cp3, mkdir as mkdir15, readFile as readFile19, rm as rm7 } from "fs/promises";
6702
+ import { createHash as createHash6, randomUUID as randomUUID2 } from "crypto";
6703
+ import { execFile as execFile5 } from "child_process";
6704
+ import { cp as cp3, mkdir as mkdir16, readFile as readFile20, rename as rename4, rm as rm8 } from "fs/promises";
6311
6705
  import { homedir as homedir4 } from "os";
6312
- import { basename as basename14, dirname as dirname19, join as join25, resolve as resolve9 } from "path";
6706
+ import { basename as basename14, dirname as dirname20, join as join26, resolve as resolve9 } from "path";
6707
+ import { promisify as promisify5 } from "util";
6313
6708
  import * as defaultSkillKit from "@skillkit/core";
6314
6709
 
6315
6710
  // src/source/skill-artifacts.ts
6316
- import { readdir as readdir2, stat as stat4 } from "fs/promises";
6317
- import { basename as basename13, dirname as dirname18, extname as extname2, join as join24 } from "path";
6711
+ import { readdir as readdir3, stat as stat5 } from "fs/promises";
6712
+ import { basename as basename13, dirname as dirname19, extname as extname2, join as join25 } from "path";
6318
6713
  async function artifactsFromSkillPaths(paths, packageName) {
6319
6714
  const artifacts = [];
6320
6715
  const seen = /* @__PURE__ */ new Set();
@@ -6334,16 +6729,16 @@ async function discoverSkillPaths(root) {
6334
6729
  return paths;
6335
6730
  }
6336
6731
  async function artifactFromSkillPath(item, packageName) {
6337
- const stats = await stat4(item.path);
6732
+ const stats = await stat5(item.path);
6338
6733
  if (stats.isDirectory()) {
6339
- const skillMd = join24(item.path, "SKILL.md");
6734
+ const skillMd = join25(item.path, "SKILL.md");
6340
6735
  if (!await pathExists(skillMd)) return void 0;
6341
6736
  const name = sanitizeSkillName(item.name ?? basename13(item.path));
6342
6737
  return {
6343
6738
  type: "skills",
6344
6739
  name,
6345
6740
  sourcePath: item.path,
6346
- relativePath: join24("skills", name),
6741
+ relativePath: join25("skills", name),
6347
6742
  kind: "dir",
6348
6743
  hash: await hashPath(item.path),
6349
6744
  packageName,
@@ -6351,13 +6746,13 @@ async function artifactFromSkillPath(item, packageName) {
6351
6746
  };
6352
6747
  }
6353
6748
  if (stats.isFile() && basename13(item.path).toLowerCase() === "skill.md") {
6354
- const dir = dirname18(item.path);
6749
+ const dir = dirname19(item.path);
6355
6750
  const name = sanitizeSkillName(item.name ?? basename13(dir));
6356
6751
  return {
6357
6752
  type: "skills",
6358
6753
  name,
6359
6754
  sourcePath: dir,
6360
- relativePath: join24("skills", name),
6755
+ relativePath: join25("skills", name),
6361
6756
  kind: "dir",
6362
6757
  hash: await hashPath(dir),
6363
6758
  packageName,
@@ -6370,7 +6765,7 @@ async function artifactFromSkillPath(item, packageName) {
6370
6765
  type: "skills",
6371
6766
  name,
6372
6767
  sourcePath: item.path,
6373
- relativePath: join24("skills", `${name}.md`),
6768
+ relativePath: join25("skills", `${name}.md`),
6374
6769
  kind: "file",
6375
6770
  hash: await hashPath(item.path),
6376
6771
  packageName,
@@ -6381,14 +6776,14 @@ async function artifactFromSkillPath(item, packageName) {
6381
6776
  }
6382
6777
  async function walk(dir, paths) {
6383
6778
  if (!await pathExists(dir)) return;
6384
- const entries = await readdir2(dir, { withFileTypes: true });
6779
+ const entries = await readdir3(dir, { withFileTypes: true });
6385
6780
  if (entries.some((entry) => entry.isFile() && entry.name === "SKILL.md")) {
6386
6781
  paths.push({ path: dir });
6387
6782
  return;
6388
6783
  }
6389
6784
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
6390
6785
  if (!entry.isDirectory() || entry.name === ".git" || entry.name === "node_modules") continue;
6391
- await walk(join24(dir, entry.name), paths);
6786
+ await walk(join25(dir, entry.name), paths);
6392
6787
  }
6393
6788
  }
6394
6789
  function sanitizeSkillName(name) {
@@ -6396,12 +6791,14 @@ function sanitizeSkillName(name) {
6396
6791
  }
6397
6792
 
6398
6793
  // src/source/skillkit.ts
6794
+ var execFileAsync5 = promisify5(execFile5);
6399
6795
  var SkillKitSourceDriver = class {
6400
6796
  constructor(core = defaultSkillKit) {
6401
6797
  this.core = core;
6402
6798
  }
6403
6799
  core;
6404
6800
  name = "skillkit";
6801
+ inflightFetches = /* @__PURE__ */ new Map();
6405
6802
  async resolve(source, options = {}) {
6406
6803
  const spec = parseSkillKitSource(source);
6407
6804
  if (await pathExists(spec)) {
@@ -6415,14 +6812,17 @@ var SkillKitSourceDriver = class {
6415
6812
  sourceHash: await hashPath(resolvedPath)
6416
6813
  };
6417
6814
  }
6815
+ const cacheIdentity = normalizeImmutableCacheIdentity(options.cacheIdentity) ?? immutableCommit(options.ref);
6418
6816
  return {
6419
6817
  driver: this.name,
6420
6818
  source,
6421
- resolvedPath: cachePathFor4(spec, options.cacheRoot),
6819
+ resolvedPath: cachePathFor4(spec, options.cacheRoot, cacheIdentity),
6422
6820
  packageName: `skillkit/${packageSlug(spec)}`,
6423
6821
  mode: options.mode ?? "tracking",
6424
6822
  requestedRef: options.ref,
6425
- frozenLock: options.frozenLock
6823
+ cacheIdentity,
6824
+ frozenLock: options.frozenLock,
6825
+ cacheLockTimeoutMs: options.cacheLockTimeoutMs
6426
6826
  };
6427
6827
  }
6428
6828
  async fetch(resolved) {
@@ -6430,35 +6830,104 @@ var SkillKitSourceDriver = class {
6430
6830
  if (await pathExists(spec)) {
6431
6831
  return resolved;
6432
6832
  }
6833
+ const requestedCommit = immutableCommit(resolved.requestedRef);
6834
+ const immutableCacheIdentity = resolved.cacheIdentity ?? requestedCommit;
6433
6835
  if (resolved.frozenLock) {
6836
+ if (!immutableCacheIdentity) {
6837
+ throw new Error("Frozen lock requires cached SkillKit source identified by an immutable cache identity.");
6838
+ }
6434
6839
  if (!await pathExists(resolved.resolvedPath)) {
6435
6840
  throw new Error(`Frozen lock requires cached SkillKit source at ${resolved.resolvedPath}`);
6436
6841
  }
6437
6842
  return {
6438
6843
  ...resolved,
6844
+ resolvedCommit: requestedCommit,
6845
+ cacheIdentity: immutableCacheIdentity,
6439
6846
  sourceHash: await hashPath(resolved.resolvedPath)
6440
6847
  };
6441
6848
  }
6849
+ if (immutableCacheIdentity && await pathExists(resolved.resolvedPath)) {
6850
+ return {
6851
+ ...resolved,
6852
+ resolvedCommit: requestedCommit,
6853
+ cacheIdentity: immutableCacheIdentity,
6854
+ sourceHash: await hashPath(resolved.resolvedPath)
6855
+ };
6856
+ }
6857
+ const specCachePath = cachePathFor4(spec, dirname20(dirname20(resolved.resolvedPath)));
6858
+ const refIdentity = resolved.requestedRef ?? "default";
6859
+ const lockPath = `${specCachePath}.ref-${createHash6("sha256").update(refIdentity).digest("hex")}.lock`;
6860
+ const inflightKey = `${lockPath}\0${requestedCommit ?? "movable"}`;
6861
+ const inflight = this.inflightFetches.get(inflightKey);
6862
+ if (inflight) return inflight;
6863
+ const fetchPromise = this.materializeRemote(resolved, spec, specCachePath, lockPath, requestedCommit);
6864
+ this.inflightFetches.set(inflightKey, fetchPromise);
6865
+ try {
6866
+ return await fetchPromise;
6867
+ } finally {
6868
+ if (this.inflightFetches.get(inflightKey) === fetchPromise) this.inflightFetches.delete(inflightKey);
6869
+ }
6870
+ }
6871
+ async materializeRemote(resolved, spec, specCachePath, lockPath, requestedCommit) {
6442
6872
  const providerSpec = normalizeProviderSource(spec);
6443
6873
  const provider = this.core.detectProvider?.(providerSpec);
6444
6874
  if (!provider?.clone) {
6445
6875
  throw new Error("SkillKit provider API unavailable or cannot resolve source. Expected @skillkit/core detectProvider().clone().");
6446
6876
  }
6447
- await mkdir15(dirname19(resolved.resolvedPath), { recursive: true });
6448
- const result = await provider.clone(providerSpec, resolved.resolvedPath, {});
6449
- if (!result.success || !result.path) {
6450
- throw new Error(`SkillKit provider failed to fetch ${spec}: ${result.error ?? "unknown error"}`);
6451
- }
6452
- if (resolve9(result.path) !== resolve9(resolved.resolvedPath)) {
6453
- await rm7(resolved.resolvedPath, { recursive: true, force: true });
6454
- await cp3(result.path, resolved.resolvedPath, { recursive: true, dereference: true });
6455
- }
6456
- if (result.tempRoot) {
6457
- await rm7(result.tempRoot, { recursive: true, force: true });
6458
- }
6877
+ const materialized = await withFilesystemLock(lockPath, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
6878
+ const immutablePath = requestedCommit ? cachePathFor4(spec, dirname20(dirname20(specCachePath)), requestedCommit) : void 0;
6879
+ if (immutablePath && await pathExists(immutablePath)) {
6880
+ return { path: immutablePath, commit: requestedCommit, cacheIdentity: requestedCommit };
6881
+ }
6882
+ await mkdir16(dirname20(specCachePath), { recursive: true });
6883
+ const candidatePath = `${specCachePath}.agentwheel-tmp-${process.pid}-${randomUUID2()}`;
6884
+ const publishPath = `${candidatePath}.publish`;
6885
+ let result;
6886
+ try {
6887
+ const requestedRef = resolved.requestedRef;
6888
+ result = await provider.clone(providerSpec, candidatePath, requestedCommit || !requestedRef ? {} : { branch: requestedRef });
6889
+ if (!result.success || !result.path) {
6890
+ throw new Error(`SkillKit provider failed to fetch ${spec}: ${result.error ?? "unknown error"}`);
6891
+ }
6892
+ if (requestedCommit && requestedRef) {
6893
+ if (!result.tempRoot) {
6894
+ throw new Error(`SkillKit provider cannot materialize commit ${requestedRef}: clone result has no git checkout root`);
6895
+ }
6896
+ await checkoutCommit(result.tempRoot, requestedRef);
6897
+ }
6898
+ const resolvedIdentity = await resolveCloneIdentity(result);
6899
+ if (requestedCommit && !resolvedIdentity.commit?.startsWith(requestedCommit)) {
6900
+ throw new Error(
6901
+ `SkillKit provider resolved ${resolvedIdentity.commit ?? "non-Git content"} instead of requested commit ${requestedCommit}`
6902
+ );
6903
+ }
6904
+ const cachePath = cachePathFor4(spec, dirname20(dirname20(specCachePath)), resolvedIdentity.cacheKey);
6905
+ if (await pathExists(cachePath)) {
6906
+ return { path: cachePath, commit: resolvedIdentity.commit, cacheIdentity: resolvedIdentity.cacheKey };
6907
+ }
6908
+ const publishCandidate = resolve9(result.path) === resolve9(candidatePath) ? candidatePath : publishPath;
6909
+ if (publishCandidate === publishPath) await cp3(result.path, publishPath, { recursive: true, dereference: true });
6910
+ try {
6911
+ await rename4(publishCandidate, cachePath);
6912
+ } catch (error) {
6913
+ if (!isAlreadyExists(error) && !isDirectoryNotEmpty(error)) throw error;
6914
+ await rm8(publishCandidate, { recursive: true, force: true });
6915
+ }
6916
+ return { path: cachePath, commit: resolvedIdentity.commit, cacheIdentity: resolvedIdentity.cacheKey };
6917
+ } finally {
6918
+ await rm8(candidatePath, { recursive: true, force: true });
6919
+ await rm8(publishPath, { recursive: true, force: true });
6920
+ if (result?.tempRoot) {
6921
+ await rm8(result.tempRoot, { recursive: true, force: true });
6922
+ }
6923
+ }
6924
+ });
6459
6925
  return {
6460
6926
  ...resolved,
6461
- sourceHash: await hashPath(resolved.resolvedPath)
6927
+ resolvedPath: materialized.path,
6928
+ resolvedCommit: materialized.commit,
6929
+ cacheIdentity: materialized.cacheIdentity,
6930
+ sourceHash: await hashPath(materialized.path)
6462
6931
  };
6463
6932
  }
6464
6933
  async list(resolved) {
@@ -6488,9 +6957,9 @@ var SkillKitSourceDriver = class {
6488
6957
  throw new Error("SkillKit translateSkill API unavailable");
6489
6958
  }
6490
6959
  for (const skill of this.discover(resolved.resolvedPath)) {
6491
- const skillMd = join25(skill.path, "SKILL.md");
6960
+ const skillMd = join26(skill.path, "SKILL.md");
6492
6961
  if (await pathExists(skillMd)) {
6493
- this.core.translateSkill(await readFile19(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
6962
+ this.core.translateSkill(await readFile20(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
6494
6963
  }
6495
6964
  }
6496
6965
  return resolved;
@@ -6518,9 +6987,11 @@ function normalizeProviderSource(spec) {
6518
6987
  if (spec.startsWith("git:https://github.com/")) return spec.slice("git:".length);
6519
6988
  return spec;
6520
6989
  }
6521
- function cachePathFor4(spec, cacheRoot) {
6522
- const root = cacheRoot ? resolve9(cacheRoot) : join25(homedir4(), ".agentwheel", "cache");
6523
- return join25(root, "skillkit", packageSlug(spec));
6990
+ function cachePathFor4(spec, cacheRoot, immutableIdentity) {
6991
+ const root = cacheRoot ? resolve9(cacheRoot) : join26(homedir4(), ".agentwheel", "cache");
6992
+ const sourceIdentity = createHash6("sha256").update(spec).digest("hex");
6993
+ const snapshotIdentity = immutableIdentity ? `-${immutableIdentity.toLowerCase()}` : "";
6994
+ return join26(root, "skillkit", `${packageSlug(spec)}-${sourceIdentity}${snapshotIdentity}`);
6524
6995
  }
6525
6996
  function packageSlug(spec) {
6526
6997
  return spec.replace(/^[a-z]+:\/\//i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "source";
@@ -6530,10 +7001,40 @@ function mapSeverity(severity) {
6530
7001
  if (severity === "medium" || severity === "low") return "warning";
6531
7002
  return "info";
6532
7003
  }
7004
+ function isDirectoryNotEmpty(error) {
7005
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOTEMPTY";
7006
+ }
7007
+ function immutableCommit(ref) {
7008
+ return ref && /^[0-9a-f]{7,40}$/i.test(ref) ? ref.toLowerCase() : void 0;
7009
+ }
7010
+ async function resolveCloneIdentity(result) {
7011
+ if (result.resolvedCommit && /^[0-9a-f]{40}$/i.test(result.resolvedCommit)) {
7012
+ const commit = result.resolvedCommit.toLowerCase();
7013
+ return { cacheKey: commit, commit };
7014
+ }
7015
+ const checkoutRoot = result.tempRoot ?? result.path;
7016
+ if (!checkoutRoot) throw new Error("SkillKit provider clone has no checkout path");
7017
+ try {
7018
+ const { stdout } = await execFileAsync5("git", ["-C", checkoutRoot, "rev-parse", "HEAD"]);
7019
+ const commit = stdout.trim().toLowerCase();
7020
+ if (!/^[0-9a-f]{40}$/.test(commit)) throw new Error(`invalid commit '${commit}'`);
7021
+ return { cacheKey: commit, commit };
7022
+ } catch {
7023
+ return { cacheKey: `content-${await hashPath(result.path)}` };
7024
+ }
7025
+ }
7026
+ async function checkoutCommit(root, commit) {
7027
+ try {
7028
+ await execFileAsync5("git", ["-C", root, "checkout", "--detach", commit]);
7029
+ } catch {
7030
+ await execFileAsync5("git", ["-C", root, "fetch", "origin", commit]);
7031
+ await execFileAsync5("git", ["-C", root, "checkout", "--detach", commit]);
7032
+ }
7033
+ }
6533
7034
 
6534
7035
  // src/source/vercel-skills.ts
6535
- import { stat as stat5 } from "fs/promises";
6536
- import { basename as basename15, join as join26, relative as relative5, resolve as resolve10 } from "path";
7036
+ import { stat as stat6 } from "fs/promises";
7037
+ import { basename as basename15, join as join27, relative as relative5, resolve as resolve10 } from "path";
6537
7038
  var VercelSkillsSourceDriver = class {
6538
7039
  name = "vercel-skills";
6539
7040
  git = new GitSourceDriver();
@@ -6541,7 +7042,7 @@ var VercelSkillsSourceDriver = class {
6541
7042
  const parsed = parseVercelSource(source);
6542
7043
  if (parsed.kind === "local") {
6543
7044
  const resolvedPath = resolve10(parsed.path);
6544
- if (!await pathExists(resolvedPath) || !(await stat5(resolvedPath)).isDirectory()) {
7045
+ if (!await pathExists(resolvedPath) || !(await stat6(resolvedPath)).isDirectory()) {
6545
7046
  throw new Error(`Vercel skills local source not found: ${resolvedPath}`);
6546
7047
  }
6547
7048
  return {
@@ -6596,7 +7097,7 @@ var VercelSkillsSourceDriver = class {
6596
7097
  };
6597
7098
  async function resolveVercelSkillSubpath(root, subpath) {
6598
7099
  if (!subpath) return root;
6599
- const candidates = [join26(root, subpath), join26(root, "skills", subpath)];
7100
+ const candidates = [join27(root, subpath), join27(root, "skills", subpath)];
6600
7101
  for (const candidate of candidates) {
6601
7102
  if (await pathExists(candidate)) return candidate;
6602
7103
  }
@@ -6666,14 +7167,14 @@ function getSourceDriver(name = "local") {
6666
7167
  }
6667
7168
 
6668
7169
  // src/staging/staging.ts
6669
- import { chmod, cp as cp5, mkdir as mkdir18, mkdtemp as mkdtemp3, readdir as readdir5, stat as stat8 } from "fs/promises";
6670
- import { basename as basename19, dirname as dirname23, join as join30, relative as relative7, resolve as resolve12, sep as sep2 } from "path";
7170
+ import { chmod, cp as cp5, mkdir as mkdir19, mkdtemp as mkdtemp3, readdir as readdir6, stat as stat9 } from "fs/promises";
7171
+ import { basename as basename19, dirname as dirname24, join as join31, relative as relative7, resolve as resolve12, sep as sep2 } from "path";
6671
7172
  import { tmpdir as tmpdir4 } from "os";
6672
7173
 
6673
7174
  // src/compose/markdown.ts
6674
- import { createHash as createHash6 } from "crypto";
6675
- import { readdir as readdir3, readFile as readFile20, stat as stat6, writeFile as writeFile17 } from "fs/promises";
6676
- import { basename as basename16, dirname as dirname20, extname as extname3, join as join27, relative as relative6, resolve as resolve11, sep } from "path";
7175
+ import { createHash as createHash7 } from "crypto";
7176
+ import { readdir as readdir4, readFile as readFile21, stat as stat7, writeFile as writeFile17 } from "fs/promises";
7177
+ import { basename as basename16, dirname as dirname21, extname as extname3, join as join28, relative as relative6, resolve as resolve11, sep } from "path";
6677
7178
  var includePattern = /<!--\s*openpack:include(\?)?\s+([^>]+?)\s*-->/g;
6678
7179
  var escapedIncludePattern = /<!--\s*openpack\\:include(\?)?\s+([^>]+?)\s*-->/g;
6679
7180
  var generatedPattern = /<!--\s*(?:BEGIN|END)\s+openpack:include\b/;
@@ -6711,7 +7212,7 @@ async function validateMarkdownIncludes(artifacts, packageRoot, options = {}) {
6711
7212
  }
6712
7213
  }
6713
7214
  async function expandFile(file, packageRoot, appendEntries, artifactPaths, options) {
6714
- const raw = await readFile20(file, "utf8");
7215
+ const raw = await readFile21(file, "utf8");
6715
7216
  const owner = ownerSelector(packageRoot, file, options.nodeId);
6716
7217
  const expanded = await expandContent(raw, packageRoot, [owner], artifactPaths, options);
6717
7218
  let content = expanded.content;
@@ -6800,11 +7301,11 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
6800
7301
  if (options.optional) return void 0;
6801
7302
  throw new Error(`OpenPack include not found: ${displaySelector}`);
6802
7303
  }
6803
- const stats = await stat6(sourcePath);
7304
+ const stats = await stat7(sourcePath);
6804
7305
  if (!stats.isFile()) {
6805
7306
  throw new Error(`OpenPack include is not a file: ${displaySelector}`);
6806
7307
  }
6807
- const raw = sourceContent ?? await readFile20(sourcePath, "utf8");
7308
+ const raw = sourceContent ?? await readFile21(sourcePath, "utf8");
6808
7309
  const { optional: _optional, markers: _markers, chain: _chain, ...childOptions } = options;
6809
7310
  const expanded = await expandContent(raw, includePackageRoot, [...options.chain, displaySelector], includeArtifactPaths, {
6810
7311
  ...childOptions,
@@ -6871,7 +7372,7 @@ function resolvePackageSelector(packageRoot, selector) {
6871
7372
  }
6872
7373
  async function markdownFilesForArtifact(artifact) {
6873
7374
  const root = artifact.stagedPath ?? artifact.sourcePath;
6874
- const stats = await stat6(root);
7375
+ const stats = await stat7(root);
6875
7376
  if (stats.isFile()) return extname3(root).toLowerCase() === ".md" ? [root] : [];
6876
7377
  if (!stats.isDirectory()) return [];
6877
7378
  return listMarkdownFiles(root);
@@ -6879,8 +7380,8 @@ async function markdownFilesForArtifact(artifact) {
6879
7380
  async function listMarkdownFiles(root) {
6880
7381
  const out = [];
6881
7382
  async function walk2(dir) {
6882
- for (const entry of (await readdir3(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
6883
- const full = join27(dir, entry.name);
7383
+ for (const entry of (await readdir4(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
7384
+ const full = join28(dir, entry.name);
6884
7385
  if (entry.isDirectory()) {
6885
7386
  await walk2(full);
6886
7387
  } else if (entry.isFile() && extname3(entry.name).toLowerCase() === ".md") {
@@ -6894,7 +7395,7 @@ async function listMarkdownFiles(root) {
6894
7395
  function composeEntriesForFile(artifact, file) {
6895
7396
  if (!artifact.compose?.length) return [];
6896
7397
  if (artifact.kind === "file") return [resolve11(artifact.stagedPath ?? artifact.sourcePath), resolve11(file)].every(Boolean) && resolve11(artifact.stagedPath ?? artifact.sourcePath) === resolve11(file) ? artifact.compose : [];
6897
- return basename16(file) === "SKILL.md" && dirname20(file) === resolve11(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
7398
+ return basename16(file) === "SKILL.md" && dirname21(file) === resolve11(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
6898
7399
  }
6899
7400
  function orderedForExpansion(artifacts) {
6900
7401
  return [...artifacts].sort((a, b) => Number(a.type === "fragments") - Number(b.type === "fragments"));
@@ -6925,7 +7426,7 @@ function cleanSelector(value) {
6925
7426
  return value.trim().replace(/\s+/g, " ");
6926
7427
  }
6927
7428
  function sha256(content) {
6928
- return createHash6("sha256").update(content).digest("hex");
7429
+ return createHash7("sha256").update(content).digest("hex");
6929
7430
  }
6930
7431
  function uniqueComposedFrom(entries) {
6931
7432
  if (entries.length === 0) return [];
@@ -6940,8 +7441,8 @@ function artifactPathMap(artifacts) {
6940
7441
  }
6941
7442
 
6942
7443
  // src/staging/customize.ts
6943
- import { cp as cp4, mkdir as mkdir16, readdir as readdir4, readFile as readFile21, writeFile as writeFile18 } from "fs/promises";
6944
- import { dirname as dirname21, join as join28 } from "path";
7444
+ import { cp as cp4, mkdir as mkdir17, readdir as readdir5, readFile as readFile22, writeFile as writeFile18 } from "fs/promises";
7445
+ import { dirname as dirname22, join as join29 } from "path";
6945
7446
  async function applyCustomizations(artifacts, options) {
6946
7447
  let next = [...artifacts];
6947
7448
  next = await applyReplacements2(next, options, "override", installableArtifactTypes());
@@ -6957,15 +7458,15 @@ async function applyFragmentCustomizations(artifacts, options) {
6957
7458
  return next.sort((a, b) => `${a.type}:${a.name}:${a.channel}`.localeCompare(`${b.type}:${b.name}:${b.channel}`));
6958
7459
  }
6959
7460
  async function applyInstructionOverlay(artifacts, options) {
6960
- const overlayPath = join28(options.workspaceRoot, ".agentwheel", "overlays", options.adapter.name, "instructions.local.md");
7461
+ const overlayPath = join29(options.workspaceRoot, ".agentwheel", "overlays", options.adapter.name, "instructions.local.md");
6961
7462
  if (!await pathExists(overlayPath)) return artifacts;
6962
7463
  const index = artifacts.findIndex((artifact2) => artifact2.type === "instructions");
6963
7464
  if (index < 0) return artifacts;
6964
7465
  const artifact = artifacts[index];
6965
- const managed = await readFile21(artifact.stagedPath ?? artifact.sourcePath, "utf8");
6966
- const local = await readFile21(overlayPath, "utf8");
6967
- const composedPath = join28(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
6968
- await mkdir16(dirname21(composedPath), { recursive: true });
7466
+ const managed = await readFile22(artifact.stagedPath ?? artifact.sourcePath, "utf8");
7467
+ const local = await readFile22(overlayPath, "utf8");
7468
+ const composedPath = join29(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
7469
+ await mkdir17(dirname22(composedPath), { recursive: true });
6969
7470
  await writeFile18(
6970
7471
  composedPath,
6971
7472
  [
@@ -6992,19 +7493,19 @@ async function applyInstructionOverlay(artifacts, options) {
6992
7493
  return [...artifacts.slice(0, index), updated, ...artifacts.slice(index + 1)];
6993
7494
  }
6994
7495
  async function applyAdditions(artifacts, options) {
6995
- const additionsRoot = join28(options.workspaceRoot, ".agentwheel", "additions");
6996
- const rulesRoot = join28(additionsRoot, "rules");
7496
+ const additionsRoot = join29(options.workspaceRoot, ".agentwheel", "additions");
7497
+ const rulesRoot = join29(additionsRoot, "rules");
6997
7498
  if (!await pathExists(rulesRoot)) return artifacts;
6998
7499
  const additions = [];
6999
7500
  for (const entry of await sortedDirEntries2(rulesRoot)) {
7000
- const full = join28(rulesRoot, entry.name);
7501
+ const full = join29(rulesRoot, entry.name);
7001
7502
  if (!entry.isFile()) continue;
7002
7503
  additions.push({
7003
7504
  type: "rules",
7004
7505
  name: entry.name,
7005
7506
  sourcePath: full,
7006
7507
  stagedPath: full,
7007
- relativePath: join28("additions", "rules", entry.name),
7508
+ relativePath: join29("additions", "rules", entry.name),
7008
7509
  kind: "file",
7009
7510
  hash: await hashPath(full),
7010
7511
  packageName: options.packageName,
@@ -7028,17 +7529,17 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
7028
7529
  );
7029
7530
  }
7030
7531
  for (const type of artifactTypes) {
7031
- const typeRoot = join28(root, type);
7532
+ const typeRoot = join29(root, type);
7032
7533
  if (!await pathExists(typeRoot)) continue;
7033
7534
  for (const entry of await sortedDirEntries2(typeRoot)) {
7034
7535
  const artifactMapKey = `${type}:${entry.name}`;
7035
7536
  if (seen.has(artifactMapKey)) continue;
7036
7537
  seen.add(artifactMapKey);
7037
- const full = join28(typeRoot, entry.name);
7538
+ const full = join29(typeRoot, entry.name);
7038
7539
  const artifactKind = entry.isDirectory() ? "dir" : "file";
7039
7540
  const existing = byKey.get(artifactMapKey);
7040
- const stagedPath = join28(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
7041
- await mkdir16(dirname21(stagedPath), { recursive: true });
7541
+ const stagedPath = join29(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
7542
+ await mkdir17(dirname22(stagedPath), { recursive: true });
7042
7543
  await cp4(full, stagedPath, { recursive: artifactKind === "dir", dereference: true });
7043
7544
  byKey.set(artifactMapKey, {
7044
7545
  ...existing,
@@ -7046,7 +7547,7 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
7046
7547
  name: entry.name,
7047
7548
  sourcePath: full,
7048
7549
  stagedPath,
7049
- relativePath: existing?.relativePath ?? join28(type, entry.name),
7550
+ relativePath: existing?.relativePath ?? join29(type, entry.name),
7050
7551
  kind: artifactKind,
7051
7552
  hash: await hashPath(stagedPath),
7052
7553
  packageName,
@@ -7061,13 +7562,13 @@ function replacementRoots(options, channel) {
7061
7562
  const stateDir = channel === "override" ? "overrides" : "ejected";
7062
7563
  const roots = [];
7063
7564
  if (options.graphNodeId) {
7064
- roots.push({ root: join28(options.workspaceRoot, ".agentwheel", stateDir, ...options.graphNodeId.split("/")), kind: "node" });
7565
+ roots.push({ root: join29(options.workspaceRoot, ".agentwheel", stateDir, ...options.graphNodeId.split("/")), kind: "node" });
7065
7566
  }
7066
7567
  if (options.packageName && options.packageVersion) {
7067
- roots.push({ root: join28(options.workspaceRoot, ".agentwheel", stateDir, ...`${options.packageName}@${options.packageVersion}`.split("/")), kind: "version" });
7568
+ roots.push({ root: join29(options.workspaceRoot, ".agentwheel", stateDir, ...`${options.packageName}@${options.packageVersion}`.split("/")), kind: "version" });
7068
7569
  }
7069
7570
  if (options.packageName) {
7070
- roots.push({ root: join28(options.workspaceRoot, ".agentwheel", stateDir, ...options.packageName.split("/")), kind: "package" });
7571
+ roots.push({ root: join29(options.workspaceRoot, ".agentwheel", stateDir, ...options.packageName.split("/")), kind: "package" });
7071
7572
  }
7072
7573
  return roots;
7073
7574
  }
@@ -7078,12 +7579,12 @@ function installableArtifactTypes() {
7078
7579
  return ["instructions", "rules", "skills", "commands", "subagents", "mcp", "hooks", "settings", "plugins"];
7079
7580
  }
7080
7581
  async function sortedDirEntries2(path) {
7081
- return (await readdir4(path, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
7582
+ return (await readdir5(path, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
7082
7583
  }
7083
7584
 
7084
7585
  // src/staging/claude-subagents.ts
7085
- import { mkdir as mkdir17, readFile as readFile22, writeFile as writeFile19 } from "fs/promises";
7086
- import { basename as basename18, dirname as dirname22, join as join29 } from "path";
7586
+ import { mkdir as mkdir18, readFile as readFile23, writeFile as writeFile19 } from "fs/promises";
7587
+ import { basename as basename18, dirname as dirname23, join as join30 } from "path";
7087
7588
  async function renderClaudeSubagents(artifacts, stageRoot, adapter) {
7088
7589
  if (adapter?.name !== "claude") return artifacts;
7089
7590
  const names = /* @__PURE__ */ new Set();
@@ -7105,16 +7606,16 @@ async function renderClaudeSubagents(artifacts, stageRoot, adapter) {
7105
7606
  async function renderClaudeSubagent(artifact, stageRoot) {
7106
7607
  const sourcePath = artifact.stagedPath ?? artifact.sourcePath;
7107
7608
  const agentName = claudeAgentName(artifact);
7108
- const markdownPath = artifact.kind === "dir" ? join29(sourcePath, "AGENTS.md") : sourcePath;
7609
+ const markdownPath = artifact.kind === "dir" ? join30(sourcePath, "AGENTS.md") : sourcePath;
7109
7610
  if (artifact.kind === "dir" && !await pathExists(markdownPath)) {
7110
7611
  throw new Error(`Claude subagent directory ${artifact.relativePath} must contain AGENTS.md.`);
7111
7612
  }
7112
7613
  if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md") && !sourcePath.toLowerCase().endsWith(".md")) {
7113
7614
  throw new Error(`Claude subagent ${artifact.relativePath} must be a .md file or directory containing AGENTS.md.`);
7114
7615
  }
7115
- const content = await readFile22(markdownPath, "utf8");
7116
- const renderedPath = join29(stageRoot, ".agentwheel-rendered", "claude-subagents", `${agentName}.md`);
7117
- await mkdir17(dirname22(renderedPath), { recursive: true });
7616
+ const content = await readFile23(markdownPath, "utf8");
7617
+ const renderedPath = join30(stageRoot, ".agentwheel-rendered", "claude-subagents", `${agentName}.md`);
7618
+ await mkdir18(dirname23(renderedPath), { recursive: true });
7118
7619
  await writeFile19(renderedPath, content.endsWith("\n") ? content : `${content}
7119
7620
  `, "utf8");
7120
7621
  return {
@@ -7122,7 +7623,7 @@ async function renderClaudeSubagent(artifact, stageRoot) {
7122
7623
  name: `${agentName}.md`,
7123
7624
  sourcePath: renderedPath,
7124
7625
  stagedPath: renderedPath,
7125
- relativePath: join29("subagents", `${agentName}.md`),
7626
+ relativePath: join30("subagents", `${agentName}.md`),
7126
7627
  kind: "file",
7127
7628
  hash: await hashPath(renderedPath)
7128
7629
  };
@@ -7138,18 +7639,23 @@ async function stageSource(driver, source, options = {}) {
7138
7639
  }
7139
7640
  async function stageSourceRaw(driver, source, options = {}) {
7140
7641
  const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(source, options))));
7141
- return stageResolvedSourceRaw(driver, resolved);
7642
+ try {
7643
+ const staged = await stageResolvedSourceRaw(driver, resolved);
7644
+ return { ...staged, source: { ...staged.source, cacheLeasePath: void 0 } };
7645
+ } finally {
7646
+ await releaseGitSnapshotLease(resolved.cacheLeasePath);
7647
+ }
7142
7648
  }
7143
7649
  async function stageResolvedSourceRaw(driver, resolved) {
7144
7650
  const artifacts = await driver.list(resolved);
7145
7651
  return stageResolvedArtifactsRaw(resolved, artifacts);
7146
7652
  }
7147
7653
  async function stageResolvedArtifactsRaw(resolved, artifacts) {
7148
- const root = await mkdtemp3(join30(tmpdir4(), "agentwheel-stage-"));
7654
+ const root = await mkdtemp3(join31(tmpdir4(), "agentwheel-stage-"));
7149
7655
  const stagedArtifacts = [];
7150
7656
  for (const artifact of artifacts) {
7151
- const stagedPath = join30(root, artifact.relativePath);
7152
- await mkdir18(dirname23(stagedPath), { recursive: true });
7657
+ const stagedPath = join31(root, artifact.relativePath);
7658
+ await mkdir19(dirname24(stagedPath), { recursive: true });
7153
7659
  await cp5(artifact.sourcePath, stagedPath, {
7154
7660
  recursive: artifact.kind === "dir",
7155
7661
  dereference: true,
@@ -7206,6 +7712,7 @@ async function renderStagedBundle(bundle, options = {}) {
7206
7712
  mode: resolved.mode ?? "pinned",
7207
7713
  requestedRef: resolved.requestedRef,
7208
7714
  resolvedCommit: resolved.resolvedCommit,
7715
+ cacheIdentity: resolved.cacheIdentity,
7209
7716
  sourceHash: resolved.sourceHash,
7210
7717
  generatedAt,
7211
7718
  artifacts: finalArtifacts.map((artifact) => ({
@@ -7236,16 +7743,16 @@ async function composeAssets(artifact, packageRoot, stagedPath) {
7236
7743
  }
7237
7744
  for (const asset of artifact.assets) {
7238
7745
  const source = resolvePackagePath(packageRoot, asset.from);
7239
- const dest = join30(stagedPath, asset.into);
7746
+ const dest = join31(stagedPath, asset.into);
7240
7747
  await copyAsset(asset, source, dest);
7241
7748
  }
7242
7749
  }
7243
7750
  async function copyAsset(asset, source, dest) {
7244
- const sourceStats = await stat8(source);
7751
+ const sourceStats = await stat9(source);
7245
7752
  if (sourceStats.isFile()) {
7246
7753
  if (matchesAny(basename19(source), asset.include)) {
7247
- await mkdir18(dest, { recursive: true });
7248
- await copyAssetFile(source, join30(dest, basename19(source)), asset);
7754
+ await mkdir19(dest, { recursive: true });
7755
+ await copyAssetFile(source, join31(dest, basename19(source)), asset);
7249
7756
  }
7250
7757
  return;
7251
7758
  }
@@ -7253,7 +7760,7 @@ async function copyAsset(asset, source, dest) {
7253
7760
  throw new Error(`Asset include source is not a file or directory: ${source}`);
7254
7761
  }
7255
7762
  if (!asset.include?.length) {
7256
- await mkdir18(dirname23(dest), { recursive: true });
7763
+ await mkdir19(dirname24(dest), { recursive: true });
7257
7764
  await cp5(source, dest, { recursive: true, dereference: true });
7258
7765
  if (asset.mode === "copy") await normalizeCopiedModes(dest);
7259
7766
  return;
@@ -7261,11 +7768,11 @@ async function copyAsset(asset, source, dest) {
7261
7768
  for (const file of await listFiles(source)) {
7262
7769
  const rel = relative7(source, file).replaceAll("\\", "/");
7263
7770
  if (!matchesAny(rel, asset.include) && !matchesAny(basename19(file), asset.include)) continue;
7264
- await copyAssetFile(file, join30(dest, rel), asset);
7771
+ await copyAssetFile(file, join31(dest, rel), asset);
7265
7772
  }
7266
7773
  }
7267
7774
  async function copyAssetFile(source, dest, asset) {
7268
- await mkdir18(dirname23(dest), { recursive: true });
7775
+ await mkdir19(dirname24(dest), { recursive: true });
7269
7776
  await cp5(source, dest, { dereference: true });
7270
7777
  if (asset.mode === "copy") await chmod(dest, 420);
7271
7778
  }
@@ -7280,8 +7787,8 @@ function resolvePackagePath(packageRoot, path) {
7280
7787
  async function listFiles(root) {
7281
7788
  const out = [];
7282
7789
  async function walk2(dir) {
7283
- for (const entry of (await readdir5(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
7284
- const full = join30(dir, entry.name);
7790
+ for (const entry of (await readdir6(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
7791
+ const full = join31(dir, entry.name);
7285
7792
  if (entry.isDirectory()) {
7286
7793
  await walk2(full);
7287
7794
  } else if (entry.isFile()) {
@@ -7293,14 +7800,14 @@ async function listFiles(root) {
7293
7800
  return out;
7294
7801
  }
7295
7802
  async function normalizeCopiedModes(path) {
7296
- const stats = await stat8(path);
7803
+ const stats = await stat9(path);
7297
7804
  if (stats.isFile()) {
7298
7805
  await chmod(path, 420);
7299
7806
  return;
7300
7807
  }
7301
7808
  if (!stats.isDirectory()) return;
7302
- for (const entry of await readdir5(path, { withFileTypes: true })) {
7303
- await normalizeCopiedModes(join30(path, entry.name));
7809
+ for (const entry of await readdir6(path, { withFileTypes: true })) {
7810
+ await normalizeCopiedModes(join31(path, entry.name));
7304
7811
  }
7305
7812
  }
7306
7813
  function matchesAny(path, patterns) {
@@ -7313,10 +7820,10 @@ function matchesGlob(path, pattern) {
7313
7820
  }
7314
7821
 
7315
7822
  // src/model/workspace.ts
7316
- import { readFile as readFile23 } from "fs/promises";
7823
+ import { readFile as readFile24 } from "fs/promises";
7317
7824
  import { homedir as homedir5 } from "os";
7318
- import { dirname as dirname24, join as join31, resolve as resolve13 } from "path";
7319
- import { z as z6 } from "zod";
7825
+ import { dirname as dirname25, join as join32, resolve as resolve13 } from "path";
7826
+ import { z as z7 } from "zod";
7320
7827
 
7321
7828
  // src/resolve/semver.ts
7322
7829
  var semverPattern = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
@@ -7415,14 +7922,14 @@ function compareSemver(a, b) {
7415
7922
  }
7416
7923
 
7417
7924
  // src/model/workspace.ts
7418
- var artifactSelectorListSchema = z6.array(z6.string().min(1));
7419
- var workspaceSelectionImportSchema = z6.object({
7420
- export: z6.string().min(1),
7925
+ var artifactSelectorListSchema = z7.array(z7.string().min(1));
7926
+ var workspaceSelectionImportSchema = z7.object({
7927
+ export: z7.string().min(1),
7421
7928
  add: artifactSelectorListSchema.optional(),
7422
7929
  exclude: artifactSelectorListSchema.optional()
7423
7930
  }).strict();
7424
- var workspaceSelectionExportSchema = z6.object({
7425
- extends: z6.string().min(1).optional(),
7931
+ var workspaceSelectionExportSchema = z7.object({
7932
+ extends: z7.string().min(1).optional(),
7426
7933
  select: artifactSelectorListSchema.optional(),
7427
7934
  add: artifactSelectorListSchema.optional(),
7428
7935
  exclude: artifactSelectorListSchema.optional()
@@ -7442,29 +7949,29 @@ var workspaceSelectionExportSchema = z6.object({
7442
7949
  });
7443
7950
  }
7444
7951
  });
7445
- var workspaceExportsSchema = z6.object({
7446
- selections: z6.record(z6.string().min(1), workspaceSelectionExportSchema).default({})
7952
+ var workspaceExportsSchema = z7.object({
7953
+ selections: z7.record(z7.string().min(1), workspaceSelectionExportSchema).default({})
7447
7954
  }).strict();
7448
- var workspacePackageBaseSchema = z6.object({
7449
- name: z6.string().min(1),
7450
- source: z6.string().min(1),
7451
- driver: z6.enum(["local", "git", "skillkit", "vercel-skills", "mcp-registry", "clawhub"]).default("local"),
7452
- adapter: z6.string().min(1).default("openclaw"),
7453
- adapterConfig: z6.string().min(1).optional(),
7454
- adapterModule: z6.string().min(1).optional(),
7455
- adapterCodeHash: z6.string().min(16).optional(),
7955
+ var workspacePackageBaseSchema = z7.object({
7956
+ name: z7.string().min(1),
7957
+ source: z7.string().min(1),
7958
+ driver: z7.enum(["local", "git", "skillkit", "vercel-skills", "mcp-registry", "clawhub"]).default("local"),
7959
+ adapter: z7.string().min(1).default("openclaw"),
7960
+ adapterConfig: z7.string().min(1).optional(),
7961
+ adapterModule: z7.string().min(1).optional(),
7962
+ adapterCodeHash: z7.string().min(16).optional(),
7456
7963
  installationType: installationTypeSchema.optional(),
7457
- mode: z6.enum(["pinned", "tracking"]).default("pinned"),
7458
- version: z6.string().min(1).refine(isSupportedVersionRange, {
7964
+ mode: z7.enum(["pinned", "tracking"]).default("pinned"),
7965
+ version: z7.string().min(1).refine(isSupportedVersionRange, {
7459
7966
  message: "Version policy must be an exact semver, ~range, ^range, comparator range, or *"
7460
7967
  }).optional(),
7461
- requestedRef: z6.string().min(1).optional(),
7462
- select: z6.array(z6.string().min(1)).optional(),
7463
- skills: z6.array(z6.string().min(1)).optional(),
7464
- withSuggestions: z6.boolean().optional(),
7465
- suggestions: z6.array(z6.string().min(1)).optional(),
7466
- aliases: z6.record(z6.string(), z6.string().min(1)).optional(),
7467
- overrides: z6.array(z6.string().min(1)).optional()
7968
+ requestedRef: z7.string().min(1).optional(),
7969
+ select: z7.array(z7.string().min(1)).optional(),
7970
+ skills: z7.array(z7.string().min(1)).optional(),
7971
+ withSuggestions: z7.boolean().optional(),
7972
+ suggestions: z7.array(z7.string().min(1)).optional(),
7973
+ aliases: z7.record(z7.string(), z7.string().min(1)).optional(),
7974
+ overrides: z7.array(z7.string().min(1)).optional()
7468
7975
  });
7469
7976
  var workspacePackageSchema = workspacePackageBaseSchema.extend({
7470
7977
  selection: workspaceSelectionImportSchema.optional()
@@ -7478,31 +7985,33 @@ var workspacePackageSchema = workspacePackageBaseSchema.extend({
7478
7985
  }
7479
7986
  });
7480
7987
  var workspacePackageV1Schema = workspacePackageBaseSchema.extend({
7481
- selection: z6.never().optional()
7988
+ selection: z7.never().optional()
7482
7989
  });
7483
- var commandSchema = z6.array(z6.string().min(1)).min(1);
7484
- var commandListSchema = z6.array(commandSchema).min(1).optional();
7485
- var workspaceProfileRuntimeSchema = z6.object({
7486
- agent: z6.string().min(1).optional(),
7487
- adapter: z6.string().min(1).default("openclaw"),
7488
- adapterConfig: z6.string().min(1).optional(),
7489
- adapterModule: z6.string().min(1).optional(),
7990
+ var commandSchema = z7.array(z7.string().min(1)).min(1);
7991
+ var commandListSchema = z7.array(commandSchema).min(1).optional();
7992
+ var installStateKeySchema = z7.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i);
7993
+ var workspaceProfileRuntimeSchema = z7.object({
7994
+ agent: z7.string().min(1).optional(),
7995
+ adapter: z7.string().min(1).default("openclaw"),
7996
+ adapterConfig: z7.string().min(1).optional(),
7997
+ adapterModule: z7.string().min(1).optional(),
7490
7998
  installationType: installationTypeSchema.optional(),
7491
- targetRoot: z6.string().min(1).optional(),
7492
- executePlugins: z6.boolean().optional(),
7493
- reloadRuntimes: z6.boolean().optional(),
7999
+ stateKey: installStateKeySchema.optional(),
8000
+ targetRoot: z7.string().min(1).optional(),
8001
+ executePlugins: z7.boolean().optional(),
8002
+ reloadRuntimes: z7.boolean().optional(),
7494
8003
  reloadCommands: commandListSchema
7495
8004
  });
7496
- var workspaceProfileMemberSchema = z6.object({
7497
- id: z6.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i),
7498
- workspace: z6.string().min(1),
7499
- profile: z6.string().min(1),
7500
- transport: z6.enum(["local", "ssh"]).default("local"),
7501
- host: z6.string().min(1).optional(),
7502
- user: z6.string().min(1).optional(),
7503
- port: z6.number().int().positive().optional(),
7504
- identityFile: z6.string().min(1).optional(),
7505
- refreshTtlSeconds: z6.number().int().positive().optional()
8005
+ var workspaceProfileMemberSchema = z7.object({
8006
+ id: z7.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i),
8007
+ workspace: z7.string().min(1),
8008
+ profile: z7.string().min(1),
8009
+ transport: z7.enum(["local", "ssh"]).default("local"),
8010
+ host: z7.string().min(1).optional(),
8011
+ user: z7.string().min(1).optional(),
8012
+ port: z7.number().int().positive().optional(),
8013
+ identityFile: z7.string().min(1).optional(),
8014
+ refreshTtlSeconds: z7.number().int().positive().optional()
7506
8015
  }).strict().superRefine((member, ctx) => {
7507
8016
  if (member.transport === "ssh" && !member.host) {
7508
8017
  ctx.addIssue({ code: "custom", path: ["host"], message: "SSH profile members require host" });
@@ -7511,14 +8020,14 @@ var workspaceProfileMemberSchema = z6.object({
7511
8020
  ctx.addIssue({ code: "custom", path: ["workspace"], message: "SSH profile member workspaces must be absolute" });
7512
8021
  }
7513
8022
  });
7514
- var workspaceLeafProfileSchema = z6.object({
7515
- runtimes: z6.array(workspaceProfileRuntimeSchema).min(1),
7516
- members: z6.never().optional()
8023
+ var workspaceLeafProfileSchema = z7.object({
8024
+ runtimes: z7.array(workspaceProfileRuntimeSchema).min(1),
8025
+ members: z7.never().optional()
7517
8026
  }).strict();
7518
- var workspaceCompositeProfileSchema = z6.object({
7519
- members: z6.array(workspaceProfileMemberSchema).min(1),
7520
- runtimes: z6.never().optional(),
7521
- refreshTtlSeconds: z6.number().int().positive().default(86400)
8027
+ var workspaceCompositeProfileSchema = z7.object({
8028
+ members: z7.array(workspaceProfileMemberSchema).min(1),
8029
+ runtimes: z7.never().optional(),
8030
+ refreshTtlSeconds: z7.number().int().positive().default(86400)
7522
8031
  }).strict().superRefine((profile, ctx) => {
7523
8032
  const seen = /* @__PURE__ */ new Set();
7524
8033
  for (const [index, member] of profile.members.entries()) {
@@ -7528,31 +8037,32 @@ var workspaceCompositeProfileSchema = z6.object({
7528
8037
  seen.add(member.id);
7529
8038
  }
7530
8039
  });
7531
- var workspaceProfileSchema = z6.union([
8040
+ var workspaceProfileSchema = z7.union([
7532
8041
  workspaceLeafProfileSchema,
7533
8042
  workspaceCompositeProfileSchema
7534
8043
  ]);
7535
- var workspaceRegistrySchema = z6.object({
7536
- sources: z6.array(z6.string().min(1)).optional(),
7537
- ttlSeconds: z6.number().int().positive().optional()
8044
+ var workspaceRegistrySchema = z7.object({
8045
+ sources: z7.array(z7.string().min(1)).optional(),
8046
+ ttlSeconds: z7.number().int().positive().optional()
7538
8047
  }).default({});
7539
- var workspaceTrustSchema = z6.object({
7540
- allow: z6.array(z6.string().min(1)).optional(),
7541
- acceptedSources: z6.array(z6.string().min(1)).optional(),
7542
- denyArtifactTypes: z6.array(artifactTypeSchema).optional(),
7543
- requireReviewForTransitive: z6.boolean().optional()
8048
+ var workspaceTrustSchema = z7.object({
8049
+ allow: z7.array(z7.string().min(1)).optional(),
8050
+ acceptedSources: z7.array(z7.string().min(1)).optional(),
8051
+ denyArtifactTypes: z7.array(artifactTypeSchema).optional(),
8052
+ requireReviewForTransitive: z7.boolean().optional()
7544
8053
  }).default({});
7545
- var workspaceAgentSchema = z6.object({
7546
- adapter: z6.string().min(1),
7547
- adapterConfig: z6.string().min(1).optional(),
7548
- adapterModule: z6.string().min(1).optional(),
7549
- root: z6.string().min(1),
8054
+ var workspaceAgentSchema = z7.object({
8055
+ adapter: z7.string().min(1),
8056
+ adapterConfig: z7.string().min(1).optional(),
8057
+ adapterModule: z7.string().min(1).optional(),
8058
+ root: z7.string().min(1),
7550
8059
  installationType: installationTypeSchema.optional(),
7551
- transport: z6.enum(["local", "ssh"]).default("local"),
7552
- host: z6.string().min(1).optional(),
7553
- user: z6.string().min(1).optional(),
7554
- port: z6.number().int().positive().optional(),
7555
- identityFile: z6.string().min(1).optional(),
8060
+ stateKey: installStateKeySchema.optional(),
8061
+ transport: z7.enum(["local", "ssh"]).default("local"),
8062
+ host: z7.string().min(1).optional(),
8063
+ user: z7.string().min(1).optional(),
8064
+ port: z7.number().int().positive().optional(),
8065
+ identityFile: z7.string().min(1).optional(),
7556
8066
  reloadCommands: commandListSchema
7557
8067
  }).superRefine((agent, ctx) => {
7558
8068
  if (agent.transport !== "ssh") return;
@@ -7564,34 +8074,34 @@ var workspaceAgentSchema = z6.object({
7564
8074
  });
7565
8075
  }
7566
8076
  });
7567
- var workspaceConfigBaseSchema = z6.object({
7568
- bootstrapSkills: z6.boolean().optional(),
8077
+ var workspaceConfigBaseSchema = z7.object({
8078
+ bootstrapSkills: z7.boolean().optional(),
7569
8079
  registry: workspaceRegistrySchema,
7570
8080
  trust: workspaceTrustSchema,
7571
- profiles: z6.record(z6.string(), workspaceProfileSchema).default({}),
7572
- agents: z6.record(z6.string(), workspaceAgentSchema).default({})
8081
+ profiles: z7.record(z7.string(), workspaceProfileSchema).default({}),
8082
+ agents: z7.record(z7.string(), workspaceAgentSchema).default({})
7573
8083
  });
7574
8084
  var workspaceConfigV1Schema = workspaceConfigBaseSchema.extend({
7575
- schemaVersion: z6.literal(1),
7576
- packages: z6.array(workspacePackageV1Schema).default([]),
7577
- exports: z6.never().optional()
8085
+ schemaVersion: z7.literal(1),
8086
+ packages: z7.array(workspacePackageV1Schema).default([]),
8087
+ exports: z7.never().optional()
7578
8088
  });
7579
8089
  var workspaceConfigV2Schema = workspaceConfigBaseSchema.extend({
7580
- schemaVersion: z6.literal(2),
7581
- packages: z6.array(workspacePackageSchema).default([]),
8090
+ schemaVersion: z7.literal(2),
8091
+ packages: z7.array(workspacePackageSchema).default([]),
7582
8092
  exports: workspaceExportsSchema.default({ selections: {} })
7583
8093
  });
7584
- var workspaceConfigSchema = z6.discriminatedUnion("schemaVersion", [
8094
+ var workspaceConfigSchema = z7.discriminatedUnion("schemaVersion", [
7585
8095
  workspaceConfigV1Schema,
7586
8096
  workspaceConfigV2Schema
7587
8097
  ]);
7588
8098
  function workspaceConfigPath(workspaceRoot) {
7589
- return join31(workspaceRoot, ".agentwheel", "config.json");
8099
+ return join32(workspaceRoot, ".agentwheel", "config.json");
7590
8100
  }
7591
8101
  async function readWorkspaceConfig(workspaceRoot) {
7592
8102
  const path = workspaceConfigPath(workspaceRoot);
7593
8103
  if (!await pathExists(path)) return emptyWorkspaceConfig();
7594
- return workspaceConfigSchema.parse(JSON.parse(await readFile23(path, "utf8")));
8104
+ return workspaceConfigSchema.parse(JSON.parse(await readFile24(path, "utf8")));
7595
8105
  }
7596
8106
  async function writeWorkspaceConfig(workspaceRoot, config) {
7597
8107
  await writeJsonAtomic(workspaceConfigPath(workspaceRoot), workspaceConfigSchema.parse(config));
@@ -7604,13 +8114,13 @@ function upsertPackage(config, entry) {
7604
8114
  return workspaceConfigSchema.parse({ ...parsed, packages });
7605
8115
  }
7606
8116
  function globalWorkspaceConfigPath(globalRoot = homedir5()) {
7607
- return join31(globalRoot, ".agentwheel", "config.json");
8117
+ return join32(globalRoot, ".agentwheel", "config.json");
7608
8118
  }
7609
8119
  async function findWorkspaceRoot(start = process.cwd()) {
7610
8120
  let current = resolve13(start);
7611
8121
  while (true) {
7612
8122
  if (await pathExists(workspaceConfigPath(current))) return current;
7613
- const parent = dirname24(current);
8123
+ const parent = dirname25(current);
7614
8124
  if (parent === current) return resolve13(start);
7615
8125
  current = parent;
7616
8126
  }
@@ -7652,7 +8162,7 @@ function isCompositeWorkspaceProfile(profile) {
7652
8162
  }
7653
8163
  async function readConfigPath(path) {
7654
8164
  if (!await pathExists(path)) return emptyWorkspaceConfig();
7655
- return workspaceConfigSchema.parse(JSON.parse(await readFile23(path, "utf8")));
8165
+ return workspaceConfigSchema.parse(JSON.parse(await readFile24(path, "utf8")));
7656
8166
  }
7657
8167
  function mergeWorkspaceTrust(global, project) {
7658
8168
  return {
@@ -7667,21 +8177,21 @@ function sortedUnique2(values) {
7667
8177
  }
7668
8178
 
7669
8179
  // src/lifecycle/customization.ts
7670
- import { appendFile, cp as cp6, mkdir as mkdir19, rm as rm9 } from "fs/promises";
7671
- import { dirname as dirname26, join as join34 } from "path";
8180
+ import { appendFile, cp as cp6, mkdir as mkdir20, rm as rm10 } from "fs/promises";
8181
+ import { dirname as dirname27, join as join35 } from "path";
7672
8182
 
7673
8183
  // src/resolve/graph.ts
7674
- import { createHash as createHash8 } from "crypto";
7675
- import { mkdtemp as mkdtemp4, readdir as readdir6, readFile as readFile26, stat as stat10 } from "fs/promises";
8184
+ import { createHash as createHash9 } from "crypto";
8185
+ import { mkdtemp as mkdtemp4, readdir as readdir7, readFile as readFile27, stat as stat11 } from "fs/promises";
7676
8186
  import { tmpdir as tmpdir5 } from "os";
7677
- import { basename as basename20, extname as extname4, join as join33 } from "path";
8187
+ import { basename as basename20, extname as extname4, join as join34 } from "path";
7678
8188
 
7679
8189
  // src/model/workspace-composition.ts
7680
- import { createHash as createHash7 } from "crypto";
7681
- import { readFile as readFile24 } from "fs/promises";
7682
- import { z as z7 } from "zod";
7683
- var selectionSourceConfigSchema = z7.object({
7684
- schemaVersion: z7.literal(2),
8190
+ import { createHash as createHash8 } from "crypto";
8191
+ import { readFile as readFile25 } from "fs/promises";
8192
+ import { z as z8 } from "zod";
8193
+ var selectionSourceConfigSchema = z8.object({
8194
+ schemaVersion: z8.literal(2),
7685
8195
  exports: workspaceExportsSchema
7686
8196
  }).passthrough();
7687
8197
  async function resolveSelectionImport(sourceRoot, sourceDriver, selection) {
@@ -7697,7 +8207,7 @@ async function resolveSelectionImport(sourceRoot, sourceDriver, selection) {
7697
8207
  }
7698
8208
  let raw;
7699
8209
  try {
7700
- raw = JSON.parse(await readFile24(path, "utf8"));
8210
+ raw = JSON.parse(await readFile25(path, "utf8"));
7701
8211
  } catch (error) {
7702
8212
  const message = error instanceof Error ? error.message : String(error);
7703
8213
  throw new Error(`Selection import '${parsedSelection.export}' cannot parse ${path}: ${message}`);
@@ -7775,7 +8285,7 @@ function sortedUnique3(values) {
7775
8285
  return [...new Set(values)].sort((a, b) => a.localeCompare(b));
7776
8286
  }
7777
8287
  function sha2562(value) {
7778
- return createHash7("sha256").update(value).digest("hex");
8288
+ return createHash8("sha256").update(value).digest("hex");
7779
8289
  }
7780
8290
  function stableJson(value) {
7781
8291
  return JSON.stringify(stableValue2(value));
@@ -7799,42 +8309,42 @@ import { homedir as homedir7 } from "os";
7799
8309
  import { resolve as resolve15 } from "path";
7800
8310
 
7801
8311
  // src/registry/client.ts
7802
- import { readFile as readFile25, rm as rm8, stat as stat9 } from "fs/promises";
8312
+ import { readFile as readFile26, rm as rm9, stat as stat10 } from "fs/promises";
7803
8313
  import { homedir as homedir6 } from "os";
7804
- import { dirname as dirname25, join as join32, resolve as resolve14 } from "path";
8314
+ import { dirname as dirname26, join as join33, resolve as resolve14 } from "path";
7805
8315
  import { fileURLToPath } from "url";
7806
8316
 
7807
8317
  // src/model/registry.ts
7808
- import { z as z8 } from "zod";
7809
- var registryEntrySchema = z8.object({
7810
- name: z8.string().min(1),
7811
- source: z8.string().min(1),
7812
- type: z8.enum(["package", "skill", "plugin", "mcp", "adapter"]).default("package"),
7813
- description: z8.string().default(""),
7814
- tags: z8.array(z8.string().min(1)).default([]),
7815
- select: z8.array(z8.string().min(1)).optional(),
7816
- skills: z8.array(z8.string().min(1)).optional(),
7817
- homepageUrl: z8.string().min(1).optional(),
7818
- homepageLinkLabel: z8.string().min(1).optional(),
7819
- sourceUrl: z8.string().min(1).optional(),
7820
- sourceLinkLabel: z8.string().min(1).optional(),
7821
- openpack: z8.object({
7822
- schemaVersion: z8.number().int().positive().optional(),
7823
- specVersion: z8.string().min(1).optional()
8318
+ import { z as z9 } from "zod";
8319
+ var registryEntrySchema = z9.object({
8320
+ name: z9.string().min(1),
8321
+ source: z9.string().min(1),
8322
+ type: z9.enum(["package", "skill", "plugin", "mcp", "adapter"]).default("package"),
8323
+ description: z9.string().default(""),
8324
+ tags: z9.array(z9.string().min(1)).default([]),
8325
+ select: z9.array(z9.string().min(1)).optional(),
8326
+ skills: z9.array(z9.string().min(1)).optional(),
8327
+ homepageUrl: z9.string().min(1).optional(),
8328
+ homepageLinkLabel: z9.string().min(1).optional(),
8329
+ sourceUrl: z9.string().min(1).optional(),
8330
+ sourceLinkLabel: z9.string().min(1).optional(),
8331
+ openpack: z9.object({
8332
+ schemaVersion: z9.number().int().positive().optional(),
8333
+ specVersion: z9.string().min(1).optional()
7824
8334
  }).passthrough().optional()
7825
8335
  });
7826
- var registryIndexSchema = z8.union([
7827
- z8.array(registryEntrySchema),
7828
- z8.object({
7829
- schemaVersion: z8.literal(1).optional(),
7830
- entries: z8.array(registryEntrySchema)
8336
+ var registryIndexSchema = z9.union([
8337
+ z9.array(registryEntrySchema),
8338
+ z9.object({
8339
+ schemaVersion: z9.literal(1).optional(),
8340
+ entries: z9.array(registryEntrySchema)
7831
8341
  })
7832
8342
  ]).transform((value) => Array.isArray(value) ? value : value.entries);
7833
- var registryCacheSchema = z8.object({
7834
- version: z8.literal(1),
7835
- fetchedAt: z8.string().datetime(),
7836
- sources: z8.array(z8.string().min(1)),
7837
- entries: z8.array(registryEntrySchema)
8343
+ var registryCacheSchema = z9.object({
8344
+ version: z9.literal(1),
8345
+ fetchedAt: z9.string().datetime(),
8346
+ sources: z9.array(z9.string().min(1)),
8347
+ entries: z9.array(registryEntrySchema)
7838
8348
  });
7839
8349
 
7840
8350
  // src/registry/client.ts
@@ -7875,7 +8385,7 @@ var RegistryClient = class {
7875
8385
  return index.entries.find((entry) => entry.name === name);
7876
8386
  }
7877
8387
  async clearCache() {
7878
- await rm8(this.cachePath, { force: true });
8388
+ await rm9(this.cachePath, { force: true });
7879
8389
  }
7880
8390
  async getSources() {
7881
8391
  if (this.options.sources?.length) return this.options.sources;
@@ -7898,7 +8408,7 @@ var RegistryClient = class {
7898
8408
  }
7899
8409
  async readCache() {
7900
8410
  if (!await pathExists(this.cachePath)) return void 0;
7901
- return registryCacheSchema.parse(JSON.parse(await readFile25(this.cachePath, "utf8")));
8411
+ return registryCacheSchema.parse(JSON.parse(await readFile26(this.cachePath, "utf8")));
7902
8412
  }
7903
8413
  isExpired(cache, ttlMs) {
7904
8414
  return this.now().getTime() - new Date(cache.fetchedAt).getTime() > ttlMs;
@@ -7916,11 +8426,11 @@ var RegistryClient = class {
7916
8426
  const filePath = source.startsWith("file:") ? fileURLToPath(source) : source;
7917
8427
  if (await pathExists(filePath)) {
7918
8428
  const fullPath = resolve14(filePath);
7919
- const stats = await stat9(fullPath);
7920
- return readFile25(stats.isDirectory() ? join32(fullPath, "index.json") : fullPath, "utf8");
8429
+ const stats = await stat10(fullPath);
8430
+ return readFile26(stats.isDirectory() ? join33(fullPath, "index.json") : fullPath, "utf8");
7921
8431
  }
7922
- const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join32(dirname25(this.cachePath), "registry-repos") }));
7923
- return readFile25(join32(resolved.resolvedPath, "index.json"), "utf8");
8432
+ const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join33(dirname26(this.cachePath), "registry-repos") }));
8433
+ return readFile26(join33(resolved.resolvedPath, "index.json"), "utf8");
7924
8434
  }
7925
8435
  warnCompatibility(entries) {
7926
8436
  for (const entry of entries) {
@@ -7934,7 +8444,7 @@ var RegistryClient = class {
7934
8444
  }
7935
8445
  };
7936
8446
  async function resolvePackageSource(source, workspaceRoot, options = {}) {
7937
- const { isExplicitSource } = await import("./identify-VV7SXUIQ.js");
8447
+ const { isExplicitSource } = await import("./identify-G3TXOHNS.js");
7938
8448
  if (await isExplicitSource(source)) return { source };
7939
8449
  const entry = await new RegistryClient({ workspaceRoot, offline: options.offline, warn: options.warn }).resolve(source);
7940
8450
  if (!entry) {
@@ -7958,7 +8468,7 @@ function mergeIndexes(indexes) {
7958
8468
  return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
7959
8469
  }
7960
8470
  function defaultRegistryCachePath() {
7961
- return join32(homedir6(), ".agentwheel", "registry-cache.json");
8471
+ return join33(homedir6(), ".agentwheel", "registry-cache.json");
7962
8472
  }
7963
8473
  function sameSources(a, b) {
7964
8474
  return a.length === b.length && a.every((source, index) => source === b[index]);
@@ -8122,7 +8632,7 @@ function normalizeLiteralProviderSpec(source, prefix) {
8122
8632
  var cacheLocks = /* @__PURE__ */ new Map();
8123
8633
  async function resolveDependencyGraph(roots, options) {
8124
8634
  if (roots.length === 0) throw new Error("At least one graph root is required.");
8125
- const graphRoot = await mkdtemp4(join33(tmpdir5(), "agentwheel-graph-"));
8635
+ const graphRoot = await mkdtemp4(join34(tmpdir5(), "agentwheel-graph-"));
8126
8636
  const fetchCache = /* @__PURE__ */ new Map();
8127
8637
  const nodesByKey = /* @__PURE__ */ new Map();
8128
8638
  const rootResults = [];
@@ -8231,12 +8741,13 @@ Run without ${lockLabel === "Offline" ? "--offline" : "--frozen-lock"} first.`
8231
8741
  }
8232
8742
  lockedByReference = void 0;
8233
8743
  normalized = declared;
8744
+ requirement.updateClosure = true;
8234
8745
  }
8235
8746
  }
8236
8747
  const frozen = lockedByReference ?? lockedNodeForRequirement(normalized.normalizedSource, requirement, options, lockLabel);
8237
8748
  let fetched;
8238
8749
  try {
8239
- fetched = await fetchPackage(normalized, requirement.mode, options, fetchCache, frozen?.requestedRef);
8750
+ fetched = await fetchPackage(normalized, requirement.mode, options, fetchCache, frozen);
8240
8751
  } catch (error) {
8241
8752
  const usingLockedNode = frozen?.node !== void 0;
8242
8753
  if (!options.frozenLock && !options.offline && !usingLockedNode) throw error;
@@ -8271,6 +8782,7 @@ Run without ${lockLabel === "Offline" ? "--offline" : "--frozen-lock"} first.`
8271
8782
  driver: fetched.resolved.driver,
8272
8783
  requestedRef: fetched.resolved.requestedRef,
8273
8784
  resolvedCommit: fetched.resolved.resolvedCommit,
8785
+ cacheIdentity: fetched.resolved.cacheIdentity,
8274
8786
  sourceHash: fetched.sourceHash,
8275
8787
  mode: fetched.resolved.mode ?? requirement.mode,
8276
8788
  requiredBy: [],
@@ -8642,7 +9154,7 @@ async function collectIncludeNeeds(artifact, artifactsByRelativePath) {
8642
9154
  const file = stack.shift();
8643
9155
  if (scanned.has(file)) continue;
8644
9156
  scanned.add(file);
8645
- const content = await readFile26(file, "utf8");
9157
+ const content = await readFile27(file, "utf8");
8646
9158
  for (const include of extractOpenPackIncludeSelectors(content)) {
8647
9159
  await collectIncludeSelector(include.raw, include.optional, artifactsByRelativePath, scanned, stack, needs);
8648
9160
  }
@@ -8676,7 +9188,7 @@ function requirementTargetsRuntime(runtimes, runtime, label, warn) {
8676
9188
  }
8677
9189
  async function markdownFilesForArtifact2(artifact) {
8678
9190
  const root = artifact.sourcePath;
8679
- const stats = await stat10(root);
9191
+ const stats = await stat11(root);
8680
9192
  if (stats.isFile()) return extname4(root).toLowerCase() === ".md" ? [root] : [];
8681
9193
  if (!stats.isDirectory()) return [];
8682
9194
  return listMarkdownFiles2(root);
@@ -8684,8 +9196,8 @@ async function markdownFilesForArtifact2(artifact) {
8684
9196
  async function listMarkdownFiles2(root) {
8685
9197
  const out = [];
8686
9198
  async function walk2(dir) {
8687
- for (const entry of (await readdir6(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
8688
- const full = join33(dir, entry.name);
9199
+ for (const entry of (await readdir7(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
9200
+ const full = join34(dir, entry.name);
8689
9201
  if (entry.isDirectory()) {
8690
9202
  await walk2(full);
8691
9203
  } else if (entry.isFile() && extname4(entry.name).toLowerCase() === ".md") {
@@ -8712,17 +9224,19 @@ function dependencyTargetsRuntime(runtimes, runtime, nodeId, alias, warn) {
8712
9224
  warn?.(`skip dependency ${nodeId}:${alias} (not targeted: runtimes=[${runtimes.join(",")}])`);
8713
9225
  return false;
8714
9226
  }
8715
- async function fetchPackage(normalized, mode, options, fetchCache, refOverride) {
9227
+ async function fetchPackage(normalized, mode, options, fetchCache, frozen) {
8716
9228
  const hardLockedCheckout = options.frozenLock === true || options.offline === true;
8717
- const key = `${normalized.driver}\0${normalized.normalizedSource}\0${mode}\0${refOverride ?? ""}\0${hardLockedCheckout ? "hard-locked" : "mutable"}`;
9229
+ const refOverride = frozen?.requestedRef;
9230
+ const key = `${normalized.driver}\0${normalized.normalizedSource}\0${mode}\0${refOverride ?? ""}\0${frozen?.cacheIdentity ?? ""}\0${hardLockedCheckout ? "hard-locked" : "mutable"}`;
8718
9231
  const existing = fetchCache.get(key);
8719
9232
  if (existing) return existing;
8720
9233
  const promise = (async () => {
8721
9234
  const driver = getSourceDriver(normalized.driver);
8722
9235
  const resolved = await driver.resolve(normalized.source, {
8723
- cacheRoot: options.cacheRoot ?? join33(options.workspaceRoot, ".agentwheel", "cache"),
9236
+ cacheRoot: options.cacheRoot ?? join34(options.workspaceRoot, ".agentwheel", "cache"),
8724
9237
  mode,
8725
9238
  ref: refOverride ?? normalized.requestedRef,
9239
+ cacheIdentity: frozen?.cacheIdentity,
8726
9240
  frozenLock: hardLockedCheckout
8727
9241
  });
8728
9242
  const fetched = await withCachePathLock(resolved.resolvedPath, () => driver.fetch(resolved));
@@ -8771,7 +9285,8 @@ function lockedNodeForRequirement(normalizedSource, requirement, options, label)
8771
9285
  const node = matches[0];
8772
9286
  return {
8773
9287
  node,
8774
- requestedRef: node.driver === "git" ? node.resolvedCommit ?? node.requestedRef : node.requestedRef
9288
+ requestedRef: lockedSourceRef(node),
9289
+ cacheIdentity: node.cacheIdentity
8775
9290
  };
8776
9291
  }
8777
9292
  function lockedNodeForRequirementReference(requirement, options, label) {
@@ -8804,7 +9319,8 @@ function lockedNodeForRequirementReference(requirement, options, label) {
8804
9319
  }
8805
9320
  return {
8806
9321
  node,
8807
- requestedRef: node.driver === "git" ? node.resolvedCommit ?? node.requestedRef : node.requestedRef
9322
+ requestedRef: lockedSourceRef(node),
9323
+ cacheIdentity: node.cacheIdentity
8808
9324
  };
8809
9325
  }
8810
9326
  function shouldCheckLockedRootSource(requirement) {
@@ -8827,6 +9343,9 @@ function normalizedSourceFromLockedNode(node) {
8827
9343
  requestedRef: node.requestedRef
8828
9344
  };
8829
9345
  }
9346
+ function lockedSourceRef(node) {
9347
+ return node.driver === "git" || node.driver === "skillkit" ? node.resolvedCommit ?? node.requestedRef : node.requestedRef;
9348
+ }
8830
9349
  function verifyIntegrity(integrity, sourceHash, label) {
8831
9350
  if (!integrity) return;
8832
9351
  const expected = integrity.replace(/^sha256[-:]/i, "");
@@ -8906,7 +9425,7 @@ function detectDirectCollisions(nodes) {
8906
9425
  }
8907
9426
  }
8908
9427
  function graphNodeId(name, version, normalizedSource, resolvedCommit, sourceHash) {
8909
- const digest = createHash8("sha256").update(normalizedSource).update("\0").update(resolvedCommit ?? sourceHash).digest("hex").slice(0, 12);
9428
+ const digest = createHash9("sha256").update(normalizedSource).update("\0").update(resolvedCommit ?? sourceHash).digest("hex").slice(0, 12);
8910
9429
  return `${name}@${version}+${digest}`;
8911
9430
  }
8912
9431
  function sortedUnique4(values) {
@@ -8927,8 +9446,8 @@ async function mapLimit(items, limit, fn) {
8927
9446
 
8928
9447
  // src/lifecycle/customization.ts
8929
9448
  async function remember(workspaceRoot, runtime, text) {
8930
- const overlayPath = join34(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
8931
- await mkdir19(dirname26(overlayPath), { recursive: true });
9449
+ const overlayPath = join35(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
9450
+ await mkdir20(dirname27(overlayPath), { recursive: true });
8932
9451
  await appendFile(overlayPath, `${text.trim()}
8933
9452
  `, "utf8");
8934
9453
  return { overlayPath };
@@ -8951,9 +9470,9 @@ async function ejectArtifact(workspaceRoot, item) {
8951
9470
  throw new Error(`Artifact not found: ${item}`);
8952
9471
  }
8953
9472
  const ejectedIdentity = parsed.packageIdentity === parsed.packageName ? parsed.packageIdentity : candidate.nodeId === parsed.packageIdentity ? candidate.nodeId : `${candidate.packageName}@${candidate.packageVersion}`;
8954
- const ejectedPath = join34(workspaceRoot, ".agentwheel", "ejected", ...ejectedIdentity.split("/"), parsed.type, parsed.name);
8955
- await mkdir19(dirname26(ejectedPath), { recursive: true });
8956
- await rm9(ejectedPath, { recursive: true, force: true });
9473
+ const ejectedPath = join35(workspaceRoot, ".agentwheel", "ejected", ...ejectedIdentity.split("/"), parsed.type, parsed.name);
9474
+ await mkdir20(dirname27(ejectedPath), { recursive: true });
9475
+ await rm10(ejectedPath, { recursive: true, force: true });
8957
9476
  await cp6(artifact.stagedPath ?? artifact.sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
8958
9477
  return {
8959
9478
  ...parsed,
@@ -8964,7 +9483,7 @@ async function ejectArtifact(workspaceRoot, item) {
8964
9483
  ejectedPath
8965
9484
  };
8966
9485
  } finally {
8967
- await Promise.all(candidates.map((candidate) => rm9(candidate.bundle.root, { recursive: true, force: true })));
9486
+ await Promise.all(candidates.map((candidate) => rm10(candidate.bundle.root, { recursive: true, force: true })));
8968
9487
  }
8969
9488
  }
8970
9489
  function parseEjectItem(item) {
@@ -8994,7 +9513,7 @@ async function stageEjectCandidate(workspaceRoot, pkg) {
8994
9513
  const adapter = pkg.adapterConfig ? await loadAdapterConfig(pkg.adapterConfig) : getAdapter(pkg.adapter);
8995
9514
  const bundle = await stageSource(driver, normalized.source, {
8996
9515
  adapter,
8997
- cacheRoot: join34(workspaceRoot, ".agentwheel", "cache"),
9516
+ cacheRoot: join35(workspaceRoot, ".agentwheel", "cache"),
8998
9517
  mode: pkg.mode,
8999
9518
  ref: normalized.requestedRef ?? pkg.requestedRef
9000
9519
  });
@@ -9041,12 +9560,12 @@ function ejectCommands(candidates, parsed) {
9041
9560
  }
9042
9561
 
9043
9562
  // src/lifecycle/profile.ts
9044
- import { rm as rm10 } from "fs/promises";
9563
+ import { rm as rm11 } from "fs/promises";
9045
9564
 
9046
9565
  // src/lifecycle/source-plan.ts
9047
- import { createHash as createHash10 } from "crypto";
9048
- import { mkdir as mkdir21 } from "fs/promises";
9049
- import { dirname as dirname28, join as join37 } from "path";
9566
+ import { createHash as createHash11 } from "crypto";
9567
+ import { mkdir as mkdir22 } from "fs/promises";
9568
+ import { dirname as dirname29, join as join38 } from "path";
9050
9569
 
9051
9570
  // src/resolve/graph-diff.ts
9052
9571
  function diffGraphLocks(previous, next) {
@@ -9089,7 +9608,7 @@ function diffNodes(previous, next) {
9089
9608
  for (const [key, oldNode] of previousByStable) {
9090
9609
  const newNode = nextByStable.get(key);
9091
9610
  if (!newNode) continue;
9092
- if (oldNode.id === newNode.id && oldNode.version === newNode.version && oldNode.resolvedCommit === newNode.resolvedCommit && oldNode.sourceHash === newNode.sourceHash) {
9611
+ if (oldNode.id === newNode.id && oldNode.version === newNode.version && oldNode.resolvedCommit === newNode.resolvedCommit && oldNode.cacheIdentity === newNode.cacheIdentity && oldNode.sourceHash === newNode.sourceHash) {
9093
9612
  continue;
9094
9613
  }
9095
9614
  movedOldIds.add(oldNode.id);
@@ -9161,6 +9680,7 @@ function nodeChangeDetails(oldNode, newNode) {
9161
9680
  const details = [];
9162
9681
  if (oldNode.version !== newNode.version) details.push(`version ${oldNode.version} -> ${newNode.version}`);
9163
9682
  if (oldNode.resolvedCommit !== newNode.resolvedCommit) details.push(`commit ${oldNode.resolvedCommit ?? "<none>"} -> ${newNode.resolvedCommit ?? "<none>"}`);
9683
+ if (oldNode.cacheIdentity !== newNode.cacheIdentity) details.push(`cache ${short(oldNode.cacheIdentity ?? "none")} -> ${short(newNode.cacheIdentity ?? "none")}`);
9164
9684
  if (oldNode.sourceHash !== newNode.sourceHash) details.push(`sourceHash ${short(oldNode.sourceHash)} -> ${short(newNode.sourceHash)}`);
9165
9685
  return details.length > 0 ? ` (${details.join(", ")})` : "";
9166
9686
  }
@@ -9207,12 +9727,12 @@ function formatSelectionImport(root) {
9207
9727
  }
9208
9728
 
9209
9729
  // src/resolve/render.ts
9210
- import { createHash as createHash9 } from "crypto";
9211
- import { readFile as readFile27, mkdtemp as mkdtemp5 } from "fs/promises";
9730
+ import { createHash as createHash10 } from "crypto";
9731
+ import { readFile as readFile28, mkdtemp as mkdtemp5 } from "fs/promises";
9212
9732
  import { tmpdir as tmpdir6 } from "os";
9213
- import { join as join35 } from "path";
9733
+ import { join as join36 } from "path";
9214
9734
  async function renderGraphForTarget(graph, targetContext = {}) {
9215
- const root = await mkdtemp5(join35(tmpdir6(), "agentwheel-render-"));
9735
+ const root = await mkdtemp5(join36(tmpdir6(), "agentwheel-render-"));
9216
9736
  const artifacts = [];
9217
9737
  const stagedNodes = /* @__PURE__ */ new Map();
9218
9738
  const includeEdges = /* @__PURE__ */ new Map();
@@ -9334,7 +9854,7 @@ async function artifactContentMap(artifacts) {
9334
9854
  const out = /* @__PURE__ */ new Map();
9335
9855
  for (const artifact of artifacts) {
9336
9856
  if (artifact.kind !== "file") continue;
9337
- out.set(artifact.relativePath.replaceAll("\\", "/"), await readFile27(artifact.stagedPath ?? artifact.sourcePath, "utf8"));
9857
+ out.set(artifact.relativePath.replaceAll("\\", "/"), await readFile28(artifact.stagedPath ?? artifact.sourcePath, "utf8"));
9338
9858
  }
9339
9859
  return out;
9340
9860
  }
@@ -9348,7 +9868,7 @@ function filterArtifactsByRuntime2(artifacts, adapterName, selectedSet) {
9348
9868
  });
9349
9869
  }
9350
9870
  function sha2563(content) {
9351
- return createHash9("sha256").update(content).digest("hex");
9871
+ return createHash10("sha256").update(content).digest("hex");
9352
9872
  }
9353
9873
  function assignInstallNames(graph, artifacts) {
9354
9874
  const aliases = workspaceAliases(graph);
@@ -9597,13 +10117,13 @@ function lockArtifactFor(artifact) {
9597
10117
  }
9598
10118
 
9599
10119
  // src/lifecycle/trust.ts
9600
- import { mkdir as mkdir20, readFile as readFile28 } from "fs/promises";
10120
+ import { mkdir as mkdir21, readFile as readFile29 } from "fs/promises";
9601
10121
  import { homedir as homedir8 } from "os";
9602
- import { dirname as dirname27, join as join36 } from "path";
9603
- import { z as z9 } from "zod";
9604
- var trustStoreSchema = z9.object({
9605
- version: z9.literal(1),
9606
- acceptedSources: z9.array(z9.string().min(1)).default([])
10122
+ import { dirname as dirname28, join as join37 } from "path";
10123
+ import { z as z10 } from "zod";
10124
+ var trustStoreSchema = z10.object({
10125
+ version: z10.literal(1),
10126
+ acceptedSources: z10.array(z10.string().min(1)).default([])
9607
10127
  });
9608
10128
  function normalizeTrustPolicy(policy) {
9609
10129
  return {
@@ -9673,14 +10193,14 @@ function sortedUnique5(values) {
9673
10193
  }
9674
10194
  async function readTrustStore(path) {
9675
10195
  if (!await pathExists(path)) return { version: 1, acceptedSources: [] };
9676
- return trustStoreSchema.parse(JSON.parse(await readFile28(path, "utf8")));
10196
+ return trustStoreSchema.parse(JSON.parse(await readFile29(path, "utf8")));
9677
10197
  }
9678
10198
  async function writeTrustStore(path, store) {
9679
- await mkdir20(dirname27(path), { recursive: true });
10199
+ await mkdir21(dirname28(path), { recursive: true });
9680
10200
  await writeJsonAtomic(path, trustStoreSchema.parse(store));
9681
10201
  }
9682
10202
  function defaultTrustStorePath() {
9683
- return process.env.AGENTWHEEL_TRUST_STORE ?? join36(homedir8(), ".agentwheel", "trust.json");
10203
+ return process.env.AGENTWHEEL_TRUST_STORE ?? join37(homedir8(), ".agentwheel", "trust.json");
9684
10204
  }
9685
10205
 
9686
10206
  // src/lifecycle/ownership.ts
@@ -9817,6 +10337,82 @@ function assertManifestIdentity(manifest, request) {
9817
10337
  }
9818
10338
  }
9819
10339
 
10340
+ // src/lifecycle/mcp-retirement.ts
10341
+ async function createExactMcpRetirementPlan(desiredArtifacts, adapter, targetRoot, manifest, transport = localTransport, options) {
10342
+ if (desiredArtifacts.length !== 1 || desiredArtifacts[0]?.type !== "mcp") {
10343
+ throw new Error(`Exact MCP retirement requires exactly one MCP artifact; found ${desiredArtifacts.length}.`);
10344
+ }
10345
+ if (manifest && manifest.version !== 2) {
10346
+ throw new Error("Exact MCP retirement requires an Agentwheel v2 install manifest when managed state exists.");
10347
+ }
10348
+ if (manifest && manifest.entries.length !== 1) {
10349
+ throw new Error(`Exact MCP retirement requires exactly one manifest entry; found ${manifest.entries.length}.`);
10350
+ }
10351
+ if (options.expectedFromWorkspaceOwner && !manifest) {
10352
+ throw new Error(`Expected managed MCP ownership from ${options.expectedFromWorkspaceOwner}, but no manifest exists.`);
10353
+ }
10354
+ if (options.expectedFromWorkspaceOwner === options.workspaceOwner) {
10355
+ throw new Error("Exact MCP retirement ownership handoff requires different old and new workspace owners.");
10356
+ }
10357
+ const adoption = await createCombinedInstallPlan(
10358
+ desiredArtifacts,
10359
+ adapter,
10360
+ targetRoot,
10361
+ void 0,
10362
+ transport,
10363
+ {
10364
+ baseRevision: manifest?.revision ?? null,
10365
+ graphLockDigest: options.graphLockDigest,
10366
+ workspaceOwner: options.workspaceOwner,
10367
+ installationType: options.installationType,
10368
+ stateKey: options.stateKey,
10369
+ forceConflict: true
10370
+ }
10371
+ );
10372
+ if (adoption.operations.length !== 1) {
10373
+ throw new Error(`Exact MCP retirement expected one rendered operation; found ${adoption.operations.length}.`);
10374
+ }
10375
+ const operation = adoption.operations[0];
10376
+ if (adoption.hasBlockingChanges || operation.action !== "skip" || !operation.mergeStrategy || !operation.mergeRemoval) {
10377
+ return adoption;
10378
+ }
10379
+ if (operation.mergeStrategy !== "json-deep" && operation.mergeStrategy !== "codex-toml-mcp") {
10380
+ throw new Error(`Exact MCP retirement does not support merge strategy ${operation.mergeStrategy}.`);
10381
+ }
10382
+ const removalKeys = Object.keys(operation.mergeRemoval);
10383
+ const servers = operation.mergeRemoval.mcpServers;
10384
+ if (removalKeys.length !== 1 || removalKeys[0] !== "mcpServers" || !servers || typeof servers !== "object" || Array.isArray(servers) || Object.keys(servers).length !== 1) {
10385
+ throw new Error("Exact MCP retirement requires exactly one MCP server and no non-MCP configuration.");
10386
+ }
10387
+ const entry = manifest?.entries[0];
10388
+ if (entry) {
10389
+ const mismatches = [];
10390
+ if (entry.artifactType !== operation.artifactType) mismatches.push(`artifact type ${entry.artifactType}`);
10391
+ if (entry.artifactName !== operation.artifactName) mismatches.push(`artifact name ${entry.artifactName}`);
10392
+ if (entry.path !== operation.relativeDestPath) mismatches.push(`path ${entry.path}`);
10393
+ if (entry.sourceHash !== operation.desiredHash) mismatches.push("source hash");
10394
+ if (entry.mergeStrategy !== operation.mergeStrategy) mismatches.push(`merge strategy ${entry.mergeStrategy ?? "missing"}`);
10395
+ if (entry.mergeCreatedDestination === true) mismatches.push("manifest claims ownership of the whole destination");
10396
+ const expectedOwner = options.expectedFromWorkspaceOwner ?? options.workspaceOwner;
10397
+ if (entry.workspaceOwner !== expectedOwner) mismatches.push(`owner ${entry.workspaceOwner}`);
10398
+ if (mismatches.length > 0) {
10399
+ throw new Error(`Exact MCP retirement manifest precondition failed: ${mismatches.join(", ")}.`);
10400
+ }
10401
+ }
10402
+ return {
10403
+ ...adoption,
10404
+ baseRevision: manifest?.revision ?? null,
10405
+ operations: [{
10406
+ ...operation,
10407
+ action: "remove",
10408
+ exactMergeRemoval: true,
10409
+ manifestHash: entry?.hash,
10410
+ workspaceOwner: options.workspaceOwner,
10411
+ reason: entry ? `retire exact MCP contribution after explicit ownership handoff from ${entry.workspaceOwner}` : `retire exact unmanaged MCP contribution under ${options.workspaceOwner}`
10412
+ }]
10413
+ };
10414
+ }
10415
+
9820
10416
  // src/lifecycle/source-plan.ts
9821
10417
  async function createGraphSourcePlan(options) {
9822
10418
  if (options.roots.length === 0) {
@@ -9852,7 +10448,7 @@ async function createGraphSourcePlan(options) {
9852
10448
  const registryClient = new RegistryClient({ workspaceRoot, offline: lockMode, offlineLabel: lockLabel, warn });
9853
10449
  const graph = await resolveDependencyGraph(options.roots, {
9854
10450
  workspaceRoot,
9855
- cacheRoot: join37(workspaceRoot, ".agentwheel", "cache"),
10451
+ cacheRoot: join38(workspaceRoot, ".agentwheel", "cache"),
9856
10452
  registryClient,
9857
10453
  noDeps: options.noDeps,
9858
10454
  includeSuggestions: options.includeSuggestions,
@@ -9889,10 +10485,24 @@ async function createGraphSourcePlan(options) {
9889
10485
  const graphLockDigest = digestGraphLock(bundle.graphLock);
9890
10486
  const graphDiff = diffGraphLocks(previousLock, bundle.graphLock);
9891
10487
  const manifest = await readInstallManifest(resolvedInstallRoot, options.adapter.name, transport, { installationType: resolvedInstallationType, stateKey });
9892
- const plan = await createCombinedInstallPlan(desiredArtifacts, options.adapter, options.targetRoot, manifest, transport, {
10488
+ const workspaceOwner = workspaceOwnerForRoot(workspaceRoot);
10489
+ const plan = options.retireExactMcp ? await createExactMcpRetirementPlan(
10490
+ desiredArtifacts,
10491
+ options.adapter,
10492
+ options.targetRoot,
10493
+ manifest,
10494
+ transport,
10495
+ {
10496
+ installationType: resolvedInstallationType,
10497
+ stateKey,
10498
+ workspaceOwner,
10499
+ expectedFromWorkspaceOwner: options.expectedFromWorkspaceOwner,
10500
+ graphLockDigest
10501
+ }
10502
+ ) : await createCombinedInstallPlan(desiredArtifacts, options.adapter, options.targetRoot, manifest, transport, {
9893
10503
  baseRevision: manifest?.revision ?? null,
9894
10504
  graphLockDigest,
9895
- workspaceOwner: workspaceOwnerForRoot(workspaceRoot),
10505
+ workspaceOwner,
9896
10506
  installationType: resolvedInstallationType,
9897
10507
  stateKey,
9898
10508
  forceDrift: options.forceDrift,
@@ -9958,13 +10568,13 @@ async function readExistingGraphLock(path) {
9958
10568
  return readGraphLock(path);
9959
10569
  }
9960
10570
  function pathForGraphLock(workspaceRoot, targetKey2, adapter, targetFingerprint) {
9961
- return join37(workspaceRoot, ".agentwheel", "locks", sanitizePathSegment(targetKey2), sanitizePathSegment(adapter), `${targetFingerprint}.graph-lock.json`);
10571
+ return join38(workspaceRoot, ".agentwheel", "locks", sanitizePathSegment(targetKey2), sanitizePathSegment(adapter), `${targetFingerprint}.graph-lock.json`);
9962
10572
  }
9963
10573
  function sanitizePathSegment(value) {
9964
10574
  return value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "default";
9965
10575
  }
9966
10576
  function digestGraphLock(lock) {
9967
- return createHash10("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
10577
+ return createHash11("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
9968
10578
  }
9969
10579
  function assertFrozenGraph(previousLock, graph, frozen, label) {
9970
10580
  if (!frozen) return;
@@ -10090,7 +10700,7 @@ function targetLabel(target) {
10090
10700
  }
10091
10701
 
10092
10702
  // src/runtime/target.ts
10093
- import { basename as basename21, dirname as dirname29, join as join38, resolve as resolve17 } from "path";
10703
+ import { basename as basename21, dirname as dirname30, join as join39, resolve as resolve17 } from "path";
10094
10704
  var runtimeMarkers = [
10095
10705
  { adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
10096
10706
  { adapter: "claude", dirs: [".claude"] },
@@ -10161,6 +10771,7 @@ function resolveProfileRuntimeTarget(runtime, config, workspaceRoot, installatio
10161
10771
  ...target,
10162
10772
  adapterConfig: runtime.adapterConfig,
10163
10773
  adapterModule: runtime.adapterModule,
10774
+ stateKey: runtime.stateKey ?? target.stateKey,
10164
10775
  executePlugins: runtime.executePlugins,
10165
10776
  reloadRuntimes: runtime.reloadRuntimes,
10166
10777
  reloadCommands: runtime.reloadCommands ?? target.reloadCommands,
@@ -10173,6 +10784,7 @@ function resolveProfileRuntimeTarget(runtime, config, workspaceRoot, installatio
10173
10784
  adapterConfig: runtime.adapterConfig,
10174
10785
  adapterModule: runtime.adapterModule,
10175
10786
  installationType: installationType ?? runtime.installationType,
10787
+ stateKey: runtime.stateKey,
10176
10788
  targetRoot: runtime.targetRoot ? resolveConfigPath(runtime.targetRoot, workspaceRoot) : workspaceRoot,
10177
10789
  workspaceRoot,
10178
10790
  executePlugins: runtime.executePlugins,
@@ -10211,8 +10823,8 @@ async function detectRuntimeTargets(cwd = process.cwd(), adapterFilter) {
10211
10823
  if (adapterFilter && marker.adapter !== adapterFilter) continue;
10212
10824
  for (const dir of marker.dirs) {
10213
10825
  if (basename21(root) === dir) {
10214
- matches.push({ adapter: marker.adapter, targetRoot: dirname29(root) });
10215
- } else if (await pathExists(join38(root, dir))) {
10826
+ matches.push({ adapter: marker.adapter, targetRoot: dirname30(root) });
10827
+ } else if (await pathExists(join39(root, dir))) {
10216
10828
  matches.push({ adapter: marker.adapter, targetRoot: root });
10217
10829
  }
10218
10830
  }
@@ -10231,6 +10843,7 @@ function targetFromAgent(name, config, workspaceRoot, installationType) {
10231
10843
  adapterConfig: agent.adapterConfig,
10232
10844
  adapterModule: agent.adapterModule,
10233
10845
  installationType: installationType ?? agent.installationType,
10846
+ stateKey: agent.stateKey,
10234
10847
  targetRoot: agent.transport === "ssh" ? agent.root : resolveConfigPath(agent.root, workspaceRoot),
10235
10848
  workspaceRoot,
10236
10849
  reloadCommands: agent.reloadCommands,
@@ -10254,7 +10867,7 @@ function dedupeTargets(matches) {
10254
10867
  function runtimeScanRoot(request) {
10255
10868
  const root = resolve17(request.targetRoot ?? request.cwd ?? process.cwd());
10256
10869
  if (request.targetRoot) return root;
10257
- return runtimeMarkers.some((marker) => marker.dirs.includes(basename21(root))) ? dirname29(root) : root;
10870
+ return runtimeMarkers.some((marker) => marker.dirs.includes(basename21(root))) ? dirname30(root) : root;
10258
10871
  }
10259
10872
 
10260
10873
  // src/lifecycle/profile.ts
@@ -10317,9 +10930,11 @@ async function syncProfile(options) {
10317
10930
  agentName: target.agentName,
10318
10931
  targetRoot: target.targetRoot,
10319
10932
  transport: transport.kind,
10320
- ssh: target.ssh
10933
+ ssh: target.ssh,
10934
+ stateKey: target.stateKey
10321
10935
  },
10322
10936
  installationType,
10937
+ stateKey: target.stateKey,
10323
10938
  noDeps: options.noDeps,
10324
10939
  includeSuggestions: options.includeSuggestions,
10325
10940
  suggestionAliases: options.suggestionAliases,
@@ -10362,7 +10977,7 @@ async function syncProfile(options) {
10362
10977
  }
10363
10978
  results.push(result);
10364
10979
  } finally {
10365
- await rm10(graphPlan.bundle.root, { recursive: true, force: true });
10980
+ await rm11(graphPlan.bundle.root, { recursive: true, force: true });
10366
10981
  }
10367
10982
  }
10368
10983
  return results;
@@ -10556,9 +11171,9 @@ function shellQuoteArg(value) {
10556
11171
  }
10557
11172
 
10558
11173
  // src/cli/update-check.ts
10559
- import { mkdir as mkdir22, readFile as readFile29, writeFile as writeFile20 } from "fs/promises";
11174
+ import { mkdir as mkdir23, readFile as readFile30, writeFile as writeFile20 } from "fs/promises";
10560
11175
  import { homedir as homedir9 } from "os";
10561
- import { dirname as dirname30, join as join39 } from "path";
11176
+ import { dirname as dirname31, join as join40 } from "path";
10562
11177
  var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
10563
11178
  var DEFAULT_TIMEOUT_MS = 300;
10564
11179
  var REGISTRY_URL = "https://registry.npmjs.org/agentwheel";
@@ -10566,7 +11181,7 @@ async function maybeCheckForUpdate(options) {
10566
11181
  if (isDisabled(options)) return;
10567
11182
  const now = options.now?.() ?? /* @__PURE__ */ new Date();
10568
11183
  const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
10569
- const cachePath = options.cachePath ?? join39(homedir9(), ".agentwheel", "update-check.json");
11184
+ const cachePath = options.cachePath ?? join40(homedir9(), ".agentwheel", "update-check.json");
10570
11185
  try {
10571
11186
  const cached = await readCache(cachePath);
10572
11187
  if (cached && now.getTime() - Date.parse(cached.checkedAt) < ttlMs) {
@@ -10603,7 +11218,7 @@ async function fetchLatestVersion(fetchImpl, timeoutMs) {
10603
11218
  }
10604
11219
  async function readCache(path) {
10605
11220
  try {
10606
- const parsed = JSON.parse(await readFile29(path, "utf8"));
11221
+ const parsed = JSON.parse(await readFile30(path, "utf8"));
10607
11222
  if (typeof parsed.checkedAt !== "string" || typeof parsed.latest !== "string") return void 0;
10608
11223
  return { checkedAt: parsed.checkedAt, latest: parsed.latest };
10609
11224
  } catch {
@@ -10611,7 +11226,7 @@ async function readCache(path) {
10611
11226
  }
10612
11227
  }
10613
11228
  async function writeCache(path, cache) {
10614
- await mkdir22(dirname30(path), { recursive: true });
11229
+ await mkdir23(dirname31(path), { recursive: true });
10615
11230
  await writeFile20(path, `${JSON.stringify(cache, null, 2)}
10616
11231
  `, "utf8");
10617
11232
  }
@@ -10634,7 +11249,7 @@ function normalizeVersion(version) {
10634
11249
  }
10635
11250
 
10636
11251
  // src/model/package-validate.ts
10637
- import { stat as stat11 } from "fs/promises";
11252
+ import { stat as stat12 } from "fs/promises";
10638
11253
  import { resolve as resolve18 } from "path";
10639
11254
  async function validatePackage(root) {
10640
11255
  const packageRoot = resolve18(root);
@@ -10727,7 +11342,7 @@ async function validateManifestComposeInclude(packageRoot, selector, optional, f
10727
11342
  findings.push({ level: "error", message: `Compose include escapes package root: ${selector}`, path: manifestPath });
10728
11343
  return;
10729
11344
  }
10730
- if (!optional) await stat11(full);
11345
+ if (!optional) await stat12(full);
10731
11346
  } catch (error) {
10732
11347
  if (!optional) {
10733
11348
  findings.push({ level: "error", message: error instanceof Error ? error.message : String(error), path: manifestPath });
@@ -10775,13 +11390,13 @@ function isCrossPackageSelector(value) {
10775
11390
  }
10776
11391
 
10777
11392
  // src/model/package-migrate.ts
10778
- import { readFile as readFile30, rename as rename4, writeFile as writeFile21 } from "fs/promises";
10779
- import { join as join41, resolve as resolve19 } from "path";
11393
+ import { readFile as readFile31, rename as rename5, writeFile as writeFile21 } from "fs/promises";
11394
+ import { join as join42, resolve as resolve19 } from "path";
10780
11395
  import { applyEdits, modify, parse as parse5 } from "jsonc-parser";
10781
11396
  async function migratePackageManifest(root) {
10782
11397
  const packageRoot = resolve19(root);
10783
11398
  for (const name of openPackManifestNames) {
10784
- const path = join41(packageRoot, name);
11399
+ const path = join42(packageRoot, name);
10785
11400
  if (await pathExists(path)) {
10786
11401
  return { changed: false, to: path, message: `Package already uses ${name}.` };
10787
11402
  }
@@ -10790,18 +11405,18 @@ async function migratePackageManifest(root) {
10790
11405
  if (!legacyName) {
10791
11406
  throw new Error(`No legacy package manifest found at ${packageRoot}`);
10792
11407
  }
10793
- const from = join41(packageRoot, legacyName);
11408
+ const from = join42(packageRoot, legacyName);
10794
11409
  const toName = legacyName.endsWith(".jsonc") ? "openpack.jsonc" : "openpack.json";
10795
- const to = join41(packageRoot, toName);
10796
- const content = await readFile30(from, "utf8");
11410
+ const to = join42(packageRoot, toName);
11411
+ const content = await readFile31(from, "utf8");
10797
11412
  const updated = updateSchemaVersion(content);
10798
- await rename4(from, to);
11413
+ await rename5(from, to);
10799
11414
  await writeFile21(to, updated, "utf8");
10800
11415
  return { changed: true, from, to, message: `Migrated ${legacyName} to ${toName}.` };
10801
11416
  }
10802
11417
  async function firstExistingLegacyManifest(root) {
10803
11418
  for (const name of legacyPackageManifestNames) {
10804
- if (await pathExists(join41(root, name))) return name;
11419
+ if (await pathExists(join42(root, name))) return name;
10805
11420
  }
10806
11421
  return void 0;
10807
11422
  }
@@ -10819,45 +11434,45 @@ function updateSchemaVersion(content) {
10819
11434
 
10820
11435
  // src/cli/version.ts
10821
11436
  import { readFileSync } from "fs";
10822
- import { dirname as dirname31, join as join42 } from "path";
11437
+ import { dirname as dirname32, join as join43 } from "path";
10823
11438
  import { fileURLToPath as fileURLToPath2 } from "url";
10824
11439
  var FALLBACK_VERSION = "0.0.0";
10825
11440
  function resolveCliVersion() {
10826
- let dir = dirname31(fileURLToPath2(import.meta.url));
11441
+ let dir = dirname32(fileURLToPath2(import.meta.url));
10827
11442
  while (true) {
10828
11443
  try {
10829
- const pkg = JSON.parse(readFileSync(join42(dir, "package.json"), "utf8"));
11444
+ const pkg = JSON.parse(readFileSync(join43(dir, "package.json"), "utf8"));
10830
11445
  if (pkg.name === "agentwheel" && typeof pkg.version === "string") {
10831
11446
  return pkg.version;
10832
11447
  }
10833
11448
  } catch {
10834
11449
  }
10835
- const parent = dirname31(dir);
11450
+ const parent = dirname32(dir);
10836
11451
  if (parent === dir) return FALLBACK_VERSION;
10837
11452
  dir = parent;
10838
11453
  }
10839
11454
  }
10840
11455
 
10841
11456
  // src/version/policy.ts
10842
- import { execFile as execFile5 } from "child_process";
10843
- import { readFile as readFile31 } from "fs/promises";
10844
- import { join as join43, resolve as resolve20 } from "path";
10845
- import { promisify as promisify5 } from "util";
11457
+ import { execFile as execFile6 } from "child_process";
11458
+ import { readFile as readFile32 } from "fs/promises";
11459
+ import { join as join44, resolve as resolve20 } from "path";
11460
+ import { promisify as promisify6 } from "util";
10846
11461
  import { parse as parseJsonc } from "jsonc-parser";
10847
- import { z as z10 } from "zod";
10848
- var execFileAsync5 = promisify5(execFile5);
11462
+ import { z as z11 } from "zod";
11463
+ var execFileAsync6 = promisify6(execFile6);
10849
11464
  var DEFAULT_VERSION_REFRESH_TTL_SECONDS = 86400;
10850
- var cachedVersionSchema = z10.object({
10851
- version: z10.string().min(1),
10852
- ref: z10.string().min(1)
11465
+ var cachedVersionSchema = z11.object({
11466
+ version: z11.string().min(1),
11467
+ ref: z11.string().min(1)
10853
11468
  });
10854
- var versionCacheEntrySchema = z10.object({
10855
- checkedAt: z10.string().datetime(),
10856
- versions: z10.array(cachedVersionSchema)
11469
+ var versionCacheEntrySchema = z11.object({
11470
+ checkedAt: z11.string().datetime(),
11471
+ versions: z11.array(cachedVersionSchema)
10857
11472
  });
10858
- var versionCacheSchema = z10.object({
10859
- schemaVersion: z10.literal(1),
10860
- sources: z10.record(z10.string(), versionCacheEntrySchema)
11473
+ var versionCacheSchema = z11.object({
11474
+ schemaVersion: z11.literal(1),
11475
+ sources: z11.record(z11.string(), versionCacheEntrySchema)
10861
11476
  });
10862
11477
  async function discoverPackageVersions(pkg, workspaceRoot, options = {}) {
10863
11478
  const now = (options.now ?? (() => /* @__PURE__ */ new Date()))();
@@ -10939,7 +11554,7 @@ async function discoverVersionsFromSource(pkg, workspaceRoot) {
10939
11554
  const manifest2 = await readPackageManifest(root);
10940
11555
  const current = manifest2 ? [{ version: manifest2.version, ref: pkg.requestedRef ?? root }] : [];
10941
11556
  try {
10942
- const { stdout } = await execFileAsync5("git", ["-C", root, "remote", "get-url", "origin"]);
11557
+ const { stdout } = await execFileAsync6("git", ["-C", root, "remote", "get-url", "origin"]);
10943
11558
  return uniqueVersions([
10944
11559
  ...await discoverGitTagsFromUrl(stdout.trim(), pkg.version, root),
10945
11560
  ...current
@@ -10950,7 +11565,7 @@ async function discoverVersionsFromSource(pkg, workspaceRoot) {
10950
11565
  }
10951
11566
  const driver = getSourceDriver(driverName);
10952
11567
  const resolved = await driver.resolve(pkg.source, {
10953
- cacheRoot: join43(workspaceRoot, ".agentwheel", "cache"),
11568
+ cacheRoot: join44(workspaceRoot, ".agentwheel", "cache"),
10954
11569
  mode: "tracking",
10955
11570
  ref: pkg.requestedRef
10956
11571
  });
@@ -10965,7 +11580,7 @@ async function discoverGitTags(source, policy) {
10965
11580
  return discoverGitTagsFromUrl(url, policy, localRoot);
10966
11581
  }
10967
11582
  async function discoverGitTagsFromUrl(url, policy, localRoot) {
10968
- const { stdout } = await execFileAsync5("git", ["ls-remote", "--tags", "--refs", url], {
11583
+ const { stdout } = await execFileAsync6("git", ["ls-remote", "--tags", "--refs", url], {
10969
11584
  maxBuffer: 10 * 1024 * 1024
10970
11585
  });
10971
11586
  const byVersion = /* @__PURE__ */ new Map();
@@ -11003,7 +11618,7 @@ async function manifestVersionAtRef(url, ref, localRoot) {
11003
11618
  if (localRoot) {
11004
11619
  for (const name of ["openpack.json", "openpack.jsonc"]) {
11005
11620
  try {
11006
- const { stdout } = await execFileAsync5("git", ["-C", localRoot, "show", `${ref}:${name}`], {
11621
+ const { stdout } = await execFileAsync6("git", ["-C", localRoot, "show", `${ref}:${name}`], {
11007
11622
  maxBuffer: 1024 * 1024
11008
11623
  });
11009
11624
  const parsed = parseJsonc(stdout);
@@ -11044,27 +11659,27 @@ function gitUrlFromSource(source) {
11044
11659
  throw new Error(`Version discovery does not support Git source: ${source}`);
11045
11660
  }
11046
11661
  function versionCachePath(workspaceRoot) {
11047
- return join43(workspaceRoot, ".agentwheel", "cache", "version-index.json");
11662
+ return join44(workspaceRoot, ".agentwheel", "cache", "version-index.json");
11048
11663
  }
11049
11664
  async function readVersionCache(path) {
11050
11665
  if (!await pathExists(path)) return { schemaVersion: 1, sources: {} };
11051
11666
  try {
11052
- return versionCacheSchema.parse(JSON.parse(await readFile31(path, "utf8")));
11667
+ return versionCacheSchema.parse(JSON.parse(await readFile32(path, "utf8")));
11053
11668
  } catch {
11054
11669
  return { schemaVersion: 1, sources: {} };
11055
11670
  }
11056
11671
  }
11057
11672
 
11058
11673
  // src/profile/members.ts
11059
- import { execFile as execFile6 } from "child_process";
11060
- import { readFile as readFile32 } from "fs/promises";
11061
- import { join as join44, resolve as resolve21 } from "path";
11062
- import { promisify as promisify6 } from "util";
11063
- import { z as z12 } from "zod";
11674
+ import { execFile as execFile7 } from "child_process";
11675
+ import { readFile as readFile33 } from "fs/promises";
11676
+ import { join as join45, resolve as resolve21 } from "path";
11677
+ import { promisify as promisify7 } from "util";
11678
+ import { z as z13 } from "zod";
11064
11679
 
11065
11680
  // src/status/report.ts
11066
- import { z as z11 } from "zod";
11067
- var statusHealthSchema = z11.enum([
11681
+ import { z as z12 } from "zod";
11682
+ var statusHealthSchema = z12.enum([
11068
11683
  "PASS",
11069
11684
  "WARN",
11070
11685
  "FAIL",
@@ -11073,77 +11688,77 @@ var statusHealthSchema = z11.enum([
11073
11688
  "INCOMPATIBLE",
11074
11689
  "BUSY"
11075
11690
  ]);
11076
- var statusPackageSchema = z11.object({
11077
- name: z11.string().min(1),
11078
- source: z11.string().min(1),
11079
- mode: z11.enum(["pinned", "tracking"]),
11080
- policy: z11.string().min(1),
11081
- installed: z11.string().nullable(),
11082
- locked: z11.string().nullable(),
11083
- latestAllowed: z11.string().nullable(),
11084
- latestOverall: z11.string().nullable(),
11085
- availability: z11.enum(["FRESH", "STALE", "UNKNOWN"]),
11086
- checkedAt: z11.string().nullable(),
11087
- error: z11.string().optional(),
11088
- updateAvailableAllowed: z11.boolean(),
11089
- updateAvailableOverall: z11.boolean()
11691
+ var statusPackageSchema = z12.object({
11692
+ name: z12.string().min(1),
11693
+ source: z12.string().min(1),
11694
+ mode: z12.enum(["pinned", "tracking"]),
11695
+ policy: z12.string().min(1),
11696
+ installed: z12.string().nullable(),
11697
+ locked: z12.string().nullable(),
11698
+ latestAllowed: z12.string().nullable(),
11699
+ latestOverall: z12.string().nullable(),
11700
+ availability: z12.enum(["FRESH", "STALE", "UNKNOWN"]),
11701
+ checkedAt: z12.string().nullable(),
11702
+ error: z12.string().optional(),
11703
+ updateAvailableAllowed: z12.boolean(),
11704
+ updateAvailableOverall: z12.boolean()
11090
11705
  });
11091
- var statusArtifactSchema = z11.object({
11092
- selector: z11.string().min(1),
11093
- type: z11.string().min(1),
11094
- name: z11.string().min(1),
11095
- installName: z11.string().min(1),
11096
- packageName: z11.string().nullable(),
11097
- packageVersion: z11.string().nullable(),
11098
- hash: z11.string().min(16),
11099
- installed: z11.boolean()
11706
+ var statusArtifactSchema = z12.object({
11707
+ selector: z12.string().min(1),
11708
+ type: z12.string().min(1),
11709
+ name: z12.string().min(1),
11710
+ installName: z12.string().min(1),
11711
+ packageName: z12.string().nullable(),
11712
+ packageVersion: z12.string().nullable(),
11713
+ hash: z12.string().min(16),
11714
+ installed: z12.boolean()
11100
11715
  });
11101
- var statusTargetSchema = z11.object({
11102
- adapter: z11.string().min(1),
11103
- installationType: z11.string().min(1),
11104
- targetRoot: z11.string().min(1),
11716
+ var statusTargetSchema = z12.object({
11717
+ adapter: z12.string().min(1),
11718
+ installationType: z12.string().min(1),
11719
+ targetRoot: z12.string().min(1),
11105
11720
  health: statusHealthSchema,
11106
- manifestRevision: z11.string().nullable(),
11107
- manifestEntryCount: z11.number().int().nonnegative(),
11108
- graphLockPath: z11.string().nullable(),
11109
- packageCount: z11.number().int().nonnegative(),
11110
- artifactCount: z11.number().int().nonnegative(),
11111
- pendingCount: z11.number().int().nonnegative(),
11112
- driftCount: z11.number().int().nonnegative(),
11113
- conflictCount: z11.number().int().nonnegative(),
11114
- error: z11.string().optional(),
11115
- packages: z11.array(statusPackageSchema),
11116
- artifacts: z11.array(statusArtifactSchema)
11721
+ manifestRevision: z12.string().nullable(),
11722
+ manifestEntryCount: z12.number().int().nonnegative(),
11723
+ graphLockPath: z12.string().nullable(),
11724
+ packageCount: z12.number().int().nonnegative(),
11725
+ artifactCount: z12.number().int().nonnegative(),
11726
+ pendingCount: z12.number().int().nonnegative(),
11727
+ driftCount: z12.number().int().nonnegative(),
11728
+ conflictCount: z12.number().int().nonnegative(),
11729
+ error: z12.string().optional(),
11730
+ packages: z12.array(statusPackageSchema),
11731
+ artifacts: z12.array(statusArtifactSchema)
11117
11732
  });
11118
- var statusReportSchema = z11.lazy(() => z11.object({
11119
- schemaVersion: z11.literal(1),
11120
- command: z11.literal("status"),
11121
- agentwheelVersion: z11.string().min(1),
11122
- generatedAt: z11.string().datetime(),
11123
- workspace: z11.string().min(1),
11124
- profile: z11.string().nullable(),
11733
+ var statusReportSchema = z12.lazy(() => z12.object({
11734
+ schemaVersion: z12.literal(1),
11735
+ command: z12.literal("status"),
11736
+ agentwheelVersion: z12.string().min(1),
11737
+ generatedAt: z12.string().datetime(),
11738
+ workspace: z12.string().min(1),
11739
+ profile: z12.string().nullable(),
11125
11740
  health: statusHealthSchema,
11126
- repository: z11.object({
11127
- available: z11.boolean(),
11128
- branch: z11.string().nullable(),
11129
- head: z11.string().nullable(),
11130
- upstream: z11.string().nullable(),
11131
- ahead: z11.number().int().nonnegative(),
11132
- behind: z11.number().int().nonnegative(),
11133
- dirtyCount: z11.number().int().nonnegative(),
11134
- error: z11.string().optional()
11741
+ repository: z12.object({
11742
+ available: z12.boolean(),
11743
+ branch: z12.string().nullable(),
11744
+ head: z12.string().nullable(),
11745
+ upstream: z12.string().nullable(),
11746
+ ahead: z12.number().int().nonnegative(),
11747
+ behind: z12.number().int().nonnegative(),
11748
+ dirtyCount: z12.number().int().nonnegative(),
11749
+ error: z12.string().optional()
11135
11750
  }),
11136
- targets: z11.array(statusTargetSchema),
11137
- members: z11.array(z11.object({
11138
- id: z11.string().min(1),
11139
- transport: z11.enum(["local", "ssh"]),
11140
- workspace: z11.string().min(1),
11141
- profile: z11.string().min(1),
11751
+ targets: z12.array(statusTargetSchema),
11752
+ members: z12.array(z12.object({
11753
+ id: z12.string().min(1),
11754
+ transport: z12.enum(["local", "ssh"]),
11755
+ workspace: z12.string().min(1),
11756
+ profile: z12.string().min(1),
11142
11757
  health: statusHealthSchema,
11143
- agentwheelVersion: z11.string().nullable(),
11144
- checkedAt: z11.string().nullable(),
11145
- stale: z11.boolean(),
11146
- error: z11.string().optional(),
11758
+ agentwheelVersion: z12.string().nullable(),
11759
+ checkedAt: z12.string().nullable(),
11760
+ stale: z12.boolean(),
11761
+ error: z12.string().optional(),
11147
11762
  report: statusReportSchema.optional()
11148
11763
  }))
11149
11764
  }));
@@ -11167,10 +11782,10 @@ function blocksCompositeApply(health) {
11167
11782
  }
11168
11783
 
11169
11784
  // src/profile/members.ts
11170
- var execFileAsync6 = promisify6(execFile6);
11171
- var memberCacheSchema = z12.object({
11172
- schemaVersion: z12.literal(1),
11173
- checkedAt: z12.string().datetime(),
11785
+ var execFileAsync7 = promisify7(execFile7);
11786
+ var memberCacheSchema = z13.object({
11787
+ schemaVersion: z13.literal(1),
11788
+ checkedAt: z13.string().datetime(),
11174
11789
  report: statusReportSchema
11175
11790
  });
11176
11791
  async function collectCompositeMembers(options) {
@@ -11234,7 +11849,7 @@ async function invokeMemberStatus(member, parentWorkspace, chain, options, cliEn
11234
11849
  try {
11235
11850
  if (member.transport === "local") {
11236
11851
  const workspace = resolve21(parentWorkspace, member.workspace);
11237
- const result = await execFileAsync6(process.execPath, [cliEntry, ...args], {
11852
+ const result = await execFileAsync7(process.execPath, [cliEntry, ...args], {
11238
11853
  cwd: workspace,
11239
11854
  env: env2,
11240
11855
  maxBuffer: 20 * 1024 * 1024
@@ -11250,7 +11865,7 @@ async function invokeMemberStatus(member, parentWorkspace, chain, options, cliEn
11250
11865
  "agentwheel",
11251
11866
  ...args.map(shellQuote2)
11252
11867
  ];
11253
- const result = await execFileAsync6("ssh", [...sshArgs, remoteArgs.join(" ")], {
11868
+ const result = await execFileAsync7("ssh", [...sshArgs, remoteArgs.join(" ")], {
11254
11869
  env: env2,
11255
11870
  maxBuffer: 20 * 1024 * 1024
11256
11871
  });
@@ -11275,7 +11890,7 @@ async function runMemberAgentwheel(member, parentWorkspace, args, chain) {
11275
11890
  const env2 = { ...process.env, AGENTWHEEL_COMPOSITE_CHAIN: JSON.stringify(chain) };
11276
11891
  try {
11277
11892
  if (member.transport === "local") {
11278
- const result2 = await execFileAsync6(
11893
+ const result2 = await execFileAsync7(
11279
11894
  process.execPath,
11280
11895
  [process.argv[1], "--no-update-check", ...args],
11281
11896
  {
@@ -11294,7 +11909,7 @@ async function runMemberAgentwheel(member, parentWorkspace, args, chain) {
11294
11909
  "--no-update-check",
11295
11910
  ...args.map(shellQuote2)
11296
11911
  ];
11297
- const result = await execFileAsync6("ssh", [...sshArguments(member), remoteArgs.join(" ")], {
11912
+ const result = await execFileAsync7("ssh", [...sshArguments(member), remoteArgs.join(" ")], {
11298
11913
  env: env2,
11299
11914
  maxBuffer: 20 * 1024 * 1024
11300
11915
  });
@@ -11354,12 +11969,12 @@ function memberFailure(member, health, error) {
11354
11969
  };
11355
11970
  }
11356
11971
  function memberCachePath(workspaceRoot, profileName, memberId) {
11357
- return join44(workspaceRoot, ".agentwheel", "cache", "member-status", profileName, `${memberId}.json`);
11972
+ return join45(workspaceRoot, ".agentwheel", "cache", "member-status", profileName, `${memberId}.json`);
11358
11973
  }
11359
11974
  async function readMemberCache(path) {
11360
11975
  if (!await pathExists(path)) return void 0;
11361
11976
  try {
11362
- return memberCacheSchema.parse(JSON.parse(await readFile32(path, "utf8")));
11977
+ return memberCacheSchema.parse(JSON.parse(await readFile33(path, "utf8")));
11363
11978
  } catch {
11364
11979
  return void 0;
11365
11980
  }
@@ -11368,7 +11983,7 @@ function parseCompositeChain() {
11368
11983
  const value = process.env.AGENTWHEEL_COMPOSITE_CHAIN;
11369
11984
  if (!value) return [];
11370
11985
  try {
11371
- return z12.array(z12.string()).parse(JSON.parse(value));
11986
+ return z13.array(z13.string()).parse(JSON.parse(value));
11372
11987
  } catch {
11373
11988
  throw new Error("Invalid AGENTWHEEL_COMPOSITE_CHAIN protocol value.");
11374
11989
  }
@@ -11384,12 +11999,12 @@ function compositeKey(workspaceRoot, profileName) {
11384
11999
  }
11385
12000
 
11386
12001
  // src/status/repository.ts
11387
- import { execFile as execFile7 } from "child_process";
11388
- import { promisify as promisify7 } from "util";
11389
- var execFileAsync7 = promisify7(execFile7);
12002
+ import { execFile as execFile8 } from "child_process";
12003
+ import { promisify as promisify8 } from "util";
12004
+ var execFileAsync8 = promisify8(execFile8);
11390
12005
  async function collectRepositoryStatus(workspaceRoot) {
11391
12006
  try {
11392
- const { stdout } = await execFileAsync7(
12007
+ const { stdout } = await execFileAsync8(
11393
12008
  "git",
11394
12009
  ["-C", workspaceRoot, "status", "--porcelain=v2", "--branch"],
11395
12010
  { maxBuffer: 10 * 1024 * 1024 }
@@ -11427,16 +12042,16 @@ function valueAfter(lines, prefix) {
11427
12042
  }
11428
12043
 
11429
12044
  // src/catalogue/client.ts
11430
- import { createHash as createHash11 } from "crypto";
11431
- import { readFile as readFile33, rm as rm11 } from "fs/promises";
12045
+ import { createHash as createHash12 } from "crypto";
12046
+ import { readFile as readFile34, rm as rm12 } from "fs/promises";
11432
12047
  import { homedir as homedir10 } from "os";
11433
- import { join as join45 } from "path";
12048
+ import { join as join46 } from "path";
11434
12049
 
11435
12050
  // src/model/catalogue.ts
11436
- import { z as z13 } from "zod";
11437
- var searchScopeSchema = z13.enum(["all", "registry", "enriched", "vercel"]);
11438
- var searchTypeSchema = z13.enum(["package", "skill", "plugin", "mcp", "adapter"]);
11439
- var searchEcosystemSchema = z13.enum([
12051
+ import { z as z14 } from "zod";
12052
+ var searchScopeSchema = z14.enum(["all", "registry", "enriched", "vercel"]);
12053
+ var searchTypeSchema = z14.enum(["package", "skill", "plugin", "mcp", "adapter"]);
12054
+ var searchEcosystemSchema = z14.enum([
11440
12055
  "official",
11441
12056
  "openpack",
11442
12057
  "mcp-registry",
@@ -11444,13 +12059,13 @@ var searchEcosystemSchema = z13.enum([
11444
12059
  "skillkit",
11445
12060
  "vercel"
11446
12061
  ]);
11447
- var catalogueProvenanceSchema = z13.enum(["registry", "enriched", "vercel"]);
11448
- var installabilitySchema = z13.enum(["registry", "source", "informational"]);
11449
- var nullableString = z13.string().nullable();
11450
- var nullableStringArray = z13.array(z13.string()).nullable();
11451
- var enrichedCatalogueEntrySchema = z13.object({
11452
- id: z13.string().min(1),
11453
- name: z13.string().min(1),
12062
+ var catalogueProvenanceSchema = z14.enum(["registry", "enriched", "vercel"]);
12063
+ var installabilitySchema = z14.enum(["registry", "source", "informational"]);
12064
+ var nullableString = z14.string().nullable();
12065
+ var nullableStringArray = z14.array(z14.string()).nullable();
12066
+ var enrichedCatalogueEntrySchema = z14.object({
12067
+ id: z14.string().min(1),
12068
+ name: z14.string().min(1),
11454
12069
  ecosystem: searchEcosystemSchema.nullable(),
11455
12070
  type: searchTypeSchema.nullable(),
11456
12071
  description: nullableString,
@@ -11460,17 +12075,17 @@ var enrichedCatalogueEntrySchema = z13.object({
11460
12075
  repoUrl: nullableString,
11461
12076
  homepageUrl: nullableString.optional(),
11462
12077
  homepageLinkLabel: nullableString.optional(),
11463
- stars: z13.number().finite().nullable().optional(),
12078
+ stars: z14.number().finite().nullable().optional(),
11464
12079
  lastPush: nullableString.optional(),
11465
- archived: z13.boolean().nullable(),
12080
+ archived: z14.boolean().nullable(),
11466
12081
  provides: nullableStringArray,
11467
12082
  version: nullableString,
11468
- featured: z13.boolean().nullable().optional()
12083
+ featured: z14.boolean().nullable().optional()
11469
12084
  });
11470
- var enrichedCatalogueSchema = z13.object({
11471
- schemaVersion: z13.literal(1),
11472
- generatedAt: z13.string().datetime(),
11473
- entries: z13.array(enrichedCatalogueEntrySchema)
12085
+ var enrichedCatalogueSchema = z14.object({
12086
+ schemaVersion: z14.literal(1),
12087
+ generatedAt: z14.string().datetime(),
12088
+ entries: z14.array(enrichedCatalogueEntrySchema)
11474
12089
  }).superRefine((value, context) => {
11475
12090
  const seen = /* @__PURE__ */ new Set();
11476
12091
  value.entries.forEach((entry, index) => {
@@ -11484,17 +12099,17 @@ var enrichedCatalogueSchema = z13.object({
11484
12099
  seen.add(entry.id);
11485
12100
  });
11486
12101
  });
11487
- var vercelCatalogueEntrySchema = z13.object({
11488
- o: z13.string().min(1),
11489
- r: z13.string().min(1),
11490
- s: z13.string().min(1),
11491
- d: z13.string().nullable().optional()
12102
+ var vercelCatalogueEntrySchema = z14.object({
12103
+ o: z14.string().min(1),
12104
+ r: z14.string().min(1),
12105
+ s: z14.string().min(1),
12106
+ d: z14.string().nullable().optional()
11492
12107
  });
11493
- var vercelCatalogueSchema = z13.object({
11494
- schemaVersion: z13.literal(1),
11495
- generatedAt: z13.string().datetime(),
11496
- count: z13.number().int().nonnegative(),
11497
- entries: z13.array(vercelCatalogueEntrySchema)
12108
+ var vercelCatalogueSchema = z14.object({
12109
+ schemaVersion: z14.literal(1),
12110
+ generatedAt: z14.string().datetime(),
12111
+ count: z14.number().int().nonnegative(),
12112
+ entries: z14.array(vercelCatalogueEntrySchema)
11498
12113
  }).superRefine((value, context) => {
11499
12114
  if (value.count !== value.entries.length) {
11500
12115
  context.addIssue({
@@ -11516,53 +12131,53 @@ var vercelCatalogueSchema = z13.object({
11516
12131
  seen.add(id);
11517
12132
  });
11518
12133
  });
11519
- var catalogueCacheSchema = z13.object({
11520
- version: z13.literal(1),
11521
- fetchedAt: z13.string().datetime(),
11522
- sources: z13.tuple([z13.string().url(), z13.string().url()]),
12134
+ var catalogueCacheSchema = z14.object({
12135
+ version: z14.literal(1),
12136
+ fetchedAt: z14.string().datetime(),
12137
+ sources: z14.tuple([z14.string().url(), z14.string().url()]),
11523
12138
  enriched: enrichedCatalogueSchema,
11524
12139
  vercel: vercelCatalogueSchema,
11525
- sourceDigests: z13.object({
11526
- enriched: z13.string().regex(/^[a-f0-9]{64}$/),
11527
- vercel: z13.string().regex(/^[a-f0-9]{64}$/)
12140
+ sourceDigests: z14.object({
12141
+ enriched: z14.string().regex(/^[a-f0-9]{64}$/),
12142
+ vercel: z14.string().regex(/^[a-f0-9]{64}$/)
11528
12143
  }).optional()
11529
12144
  });
11530
- var catalogueCacheEnvelopeSchema = z13.object({
11531
- version: z13.literal(1),
11532
- fetchedAt: z13.string().datetime(),
11533
- sources: z13.tuple([z13.string().url(), z13.string().url()]),
11534
- contentHash: z13.string().regex(/^[a-f0-9]{64}$/).optional(),
11535
- sourceDigests: z13.object({
11536
- enriched: z13.string().regex(/^[a-f0-9]{64}$/),
11537
- vercel: z13.string().regex(/^[a-f0-9]{64}$/)
12145
+ var catalogueCacheEnvelopeSchema = z14.object({
12146
+ version: z14.literal(1),
12147
+ fetchedAt: z14.string().datetime(),
12148
+ sources: z14.tuple([z14.string().url(), z14.string().url()]),
12149
+ contentHash: z14.string().regex(/^[a-f0-9]{64}$/).optional(),
12150
+ sourceDigests: z14.object({
12151
+ enriched: z14.string().regex(/^[a-f0-9]{64}$/),
12152
+ vercel: z14.string().regex(/^[a-f0-9]{64}$/)
11538
12153
  }).optional(),
11539
- enriched: z13.unknown(),
11540
- vercel: z13.unknown()
12154
+ enriched: z14.unknown(),
12155
+ vercel: z14.unknown()
11541
12156
  });
11542
- var searchResultSchema = z13.object({
11543
- id: z13.string().min(1),
11544
- name: z13.string().min(1),
11545
- description: z13.string(),
12157
+ var searchResultSchema = z14.object({
12158
+ id: z14.string().min(1),
12159
+ name: z14.string().min(1),
12160
+ description: z14.string(),
11546
12161
  type: searchTypeSchema,
11547
12162
  ecosystem: searchEcosystemSchema.optional(),
11548
- tags: z13.array(z13.string()),
11549
- provides: z13.array(z13.string()),
11550
- source: z13.string().min(1).optional(),
11551
- repoUrl: z13.string().min(1).optional(),
11552
- installCommand: z13.string().min(1).optional(),
12163
+ tags: z14.array(z14.string()),
12164
+ provides: z14.array(z14.string()),
12165
+ source: z14.string().min(1).optional(),
12166
+ repoUrl: z14.string().min(1).optional(),
12167
+ installCommand: z14.string().min(1).optional(),
11553
12168
  installability: installabilitySchema,
11554
- provenances: z13.array(catalogueProvenanceSchema).min(1),
11555
- score: z13.number().int().nonnegative(),
11556
- matchedFields: z13.array(z13.string()),
11557
- semanticScore: z13.number().finite().optional()
12169
+ provenances: z14.array(catalogueProvenanceSchema).min(1),
12170
+ score: z14.number().int().nonnegative(),
12171
+ matchedFields: z14.array(z14.string()),
12172
+ semanticScore: z14.number().finite().optional()
11558
12173
  });
11559
- var searchResponseSchema = z13.object({
11560
- schemaVersion: z13.literal(1),
11561
- query: z13.string(),
12174
+ var searchResponseSchema = z14.object({
12175
+ schemaVersion: z14.literal(1),
12176
+ query: z14.string(),
11562
12177
  scope: searchScopeSchema,
11563
- fromCache: z13.boolean(),
11564
- searchMode: z13.enum(["lexical", "semantic"]).optional(),
11565
- results: z13.array(searchResultSchema)
12178
+ fromCache: z14.boolean(),
12179
+ searchMode: z14.enum(["lexical", "semantic"]).optional(),
12180
+ results: z14.array(searchResultSchema)
11566
12181
  });
11567
12182
 
11568
12183
  // src/catalogue/client.ts
@@ -11641,12 +12256,12 @@ var CatalogueClient = class {
11641
12256
  }
11642
12257
  }
11643
12258
  async clearCache() {
11644
- await rm11(this.cachePath, { force: true });
12259
+ await rm12(this.cachePath, { force: true });
11645
12260
  }
11646
12261
  async readCache() {
11647
12262
  if (!await pathExists(this.cachePath)) return void 0;
11648
12263
  try {
11649
- const value = JSON.parse(await readFile33(this.cachePath, "utf8"));
12264
+ const value = JSON.parse(await readFile34(this.cachePath, "utf8"));
11650
12265
  const envelope = catalogueCacheEnvelopeSchema.parse(value);
11651
12266
  if (envelope.contentHash) {
11652
12267
  const contentHash = catalogueContentHash(envelope.enriched, envelope.vercel);
@@ -11702,18 +12317,18 @@ var CatalogueClient = class {
11702
12317
  }
11703
12318
  return {
11704
12319
  value: schema.parse(value),
11705
- digest: createHash11("sha256").update(bytes).digest("hex")
12320
+ digest: createHash12("sha256").update(bytes).digest("hex")
11706
12321
  };
11707
12322
  }
11708
12323
  };
11709
12324
  function defaultCatalogueCachePath() {
11710
- return join45(homedir10(), ".agentwheel", "catalogue-cache.json");
12325
+ return join46(homedir10(), ".agentwheel", "catalogue-cache.json");
11711
12326
  }
11712
12327
  function sameSources2(a, b) {
11713
12328
  return a.length === b.length && a.every((source, index) => source === b[index]);
11714
12329
  }
11715
12330
  function catalogueContentHash(enriched, vercel) {
11716
- return createHash11("sha256").update(JSON.stringify({ enriched, vercel })).digest("hex");
12331
+ return createHash12("sha256").update(JSON.stringify({ enriched, vercel })).digest("hex");
11717
12332
  }
11718
12333
 
11719
12334
  // src/search/index.ts
@@ -12075,9 +12690,9 @@ function includesToken(normalizedText, token) {
12075
12690
  }
12076
12691
 
12077
12692
  // src/semantic/index.ts
12078
- import { createHash as createHash12 } from "crypto";
12693
+ import { createHash as createHash13 } from "crypto";
12079
12694
  import { homedir as homedir11 } from "os";
12080
- import { join as join46 } from "path";
12695
+ import { join as join47 } from "path";
12081
12696
  import { env, pipeline } from "@huggingface/transformers";
12082
12697
  var DEFAULT_SEMANTIC_INDEX_URL = "https://raw.githubusercontent.com/NestDevLab/agentwheel-registry/main/catalogue-semantic-index/gte-v1/";
12083
12698
  var CONTRACT = {
@@ -12165,13 +12780,13 @@ var SemanticSearchClient = class {
12165
12780
  if (!response.ok) throw new Error(`Semantic index file failed (${response.status}): ${descriptor.path}`);
12166
12781
  const bytes = new Uint8Array(await response.arrayBuffer());
12167
12782
  if (bytes.byteLength !== descriptor.bytes) throw new Error(`Semantic index file size does not match: ${descriptor.path}`);
12168
- const digest = createHash12("sha256").update(bytes).digest("hex");
12783
+ const digest = createHash13("sha256").update(bytes).digest("hex");
12169
12784
  if (digest !== descriptor.sha256) throw new Error(`Semantic index checksum does not match: ${descriptor.path}`);
12170
12785
  return bytes;
12171
12786
  }
12172
12787
  };
12173
12788
  async function embedQuery(query) {
12174
- env.cacheDir = join46(homedir11(), ".agentwheel", "semantic-models");
12789
+ env.cacheDir = join47(homedir11(), ".agentwheel", "semantic-models");
12175
12790
  const extractor = await pipeline("feature-extraction", CONTRACT.model.id, {
12176
12791
  revision: CONTRACT.model.revision,
12177
12792
  dtype: CONTRACT.model.dtype,
@@ -12255,9 +12870,9 @@ function ensureTrailingSlash(value) {
12255
12870
  }
12256
12871
 
12257
12872
  // src/trial/skill.ts
12258
- import { createHash as createHash13 } from "crypto";
12259
- import { readFile as readFile34, stat as stat12 } from "fs/promises";
12260
- import { join as join47 } from "path";
12873
+ import { createHash as createHash14 } from "crypto";
12874
+ import { readFile as readFile35, stat as stat13 } from "fs/promises";
12875
+ import { join as join48 } from "path";
12261
12876
  import { parse as parseYaml2 } from "yaml";
12262
12877
  var MAX_TRIAL_SKILL_BYTES = 512 * 1024;
12263
12878
  async function createSkillTrial(driver, resolved, selectors) {
@@ -12269,12 +12884,12 @@ async function createSkillTrial(driver, resolved, selectors) {
12269
12884
  throw new Error("Skill trial requires exactly one selected skill. Use --skill <name> or --select skills/<name>.");
12270
12885
  }
12271
12886
  const artifact = skills[0];
12272
- const path = artifact.kind === "dir" ? join47(artifact.sourcePath, "SKILL.md") : artifact.sourcePath;
12273
- const info = await stat12(path);
12887
+ const path = artifact.kind === "dir" ? join48(artifact.sourcePath, "SKILL.md") : artifact.sourcePath;
12888
+ const info = await stat13(path);
12274
12889
  if (info.size > MAX_TRIAL_SKILL_BYTES) {
12275
12890
  throw new Error(`Skill trial exceeds the ${MAX_TRIAL_SKILL_BYTES / 1024} KiB content limit.`);
12276
12891
  }
12277
- const content = await readFile34(path, "utf8");
12892
+ const content = await readFile35(path, "utf8");
12278
12893
  const frontmatter = readSkillFrontmatter(content, artifact);
12279
12894
  return {
12280
12895
  schemaVersion: 1,
@@ -12285,7 +12900,7 @@ async function createSkillTrial(driver, resolved, selectors) {
12285
12900
  skill: {
12286
12901
  name: artifact.name,
12287
12902
  relativePath: artifact.relativePath,
12288
- sha256: createHash13("sha256").update(content).digest("hex"),
12903
+ sha256: createHash14("sha256").update(content).digest("hex"),
12289
12904
  frontmatter,
12290
12905
  content
12291
12906
  }
@@ -12334,6 +12949,15 @@ program.command("init").description("initialize an agentwheel workspace or packa
12334
12949
  if (bootstrapPackage) console.log("Auto-added the agentwheel bootstrap skill for openclaw.");
12335
12950
  console.log(nextInstallNudge());
12336
12951
  });
12952
+ var cacheCommand = program.command("cache").description("inspect and maintain local source caches");
12953
+ cacheCommand.command("prune").description("remove old Git source snapshots while preserving locked commits").option("-t, --target-root <path>", "workspace root", process.cwd()).option("--keep <count>", "newest snapshots to retain per source", parsePositiveInteger, 3).option("--apply", "delete the selected snapshots; without this flag only preview", false).action(async (options) => {
12954
+ const targetRoot = normalizeTargetRoot(options.targetRoot);
12955
+ const cacheRoot = join49(targetRoot, ".agentwheel", "cache");
12956
+ const result = await pruneGitCache(cacheRoot, { keepSnapshots: options.keep, dryRun: !options.apply });
12957
+ const verb = options.apply ? "Removed" : "Would remove";
12958
+ for (const path of result.removedPaths) console.log(`${verb} ${path}`);
12959
+ console.log(`${options.apply ? "Pruned" : "Preview"}: ${result.removedPaths.length} snapshots; retained ${result.retainedPaths.length}.`);
12960
+ });
12337
12961
  program.command("add").description("add a package to .agentwheel/config.json without touching runtimes").argument("<source>", "package source").option("--driver <driver>", "source driver (local, git, skillkit, vercel-skills, mcp-registry, or clawhub)").option("--adapter <adapter>", "built-in adapter", "openclaw").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "workspace root").option("--mode <mode>", "pinned or tracking", "pinned").option("--version <range>", "root package version policy (exact, ~, ^, or *)").option("--name <name>", "package alias").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots on future installs", false).option("--suggestion <alias>", "include one suggested companion alias on future installs (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "allow this package to replace a colliding artifact (repeatable)", collectOverrideOption, []).action(async (source, options) => {
12338
12962
  const normalizedOptions = normalizeRuntimeScopeOptions(options);
12339
12963
  const targetRoot = normalizeTargetRoot(normalizedOptions.targetRoot ?? process.cwd());
@@ -12346,10 +12970,14 @@ program.command("list").description("list artifacts exposed by a package source"
12346
12970
  const resolvedInput = await resolvePackageSource(source, targetRoot);
12347
12971
  const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
12348
12972
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
12349
- const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join48(targetRoot, ".agentwheel", "cache") }))));
12350
- const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
12351
- for (const artifact of artifacts) {
12352
- console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
12973
+ const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join49(targetRoot, ".agentwheel", "cache") }))));
12974
+ try {
12975
+ const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
12976
+ for (const artifact of artifacts) {
12977
+ console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
12978
+ }
12979
+ } finally {
12980
+ await releaseGitSnapshotLease(resolved.cacheLeasePath);
12353
12981
  }
12354
12982
  });
12355
12983
  program.command("search").description("search registry and public catalogue artifacts").argument("<query>", "search query").option("--json", "print the versioned search response as JSON", false).option("--scope <scope>", "search scope: all, registry, enriched, or vercel", "all").option("--type <type>", "artifact type: package, skill, plugin, mcp, or adapter").option("--ecosystem <ecosystem>", "ecosystem: official, openpack, mcp-registry, clawhub, skillkit, or vercel").option("--limit <n>", "maximum number of results (1-100)", "20").option("--include-archived", "include archived catalogue entries", false).option("--refresh", "refresh registry and catalogue caches", false).option("--offline", "use compatible local caches without network access", false).option("--semantic", "rank published catalogue entries with the verified semantic index", false).option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (query, options) => {
@@ -12411,42 +13039,50 @@ program.command("try").description("read and validate one skill for the current
12411
13039
  const resolvedInput = await resolvePackageSource(source, targetRoot);
12412
13040
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
12413
13041
  const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, {
12414
- cacheRoot: join48(targetRoot, ".agentwheel", "cache")
13042
+ cacheRoot: join49(targetRoot, ".agentwheel", "cache")
12415
13043
  }))));
12416
- const trial = await createSkillTrial(driver, resolved, selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry));
12417
- if (options.json) {
12418
- console.log(JSON.stringify(trial, null, 2));
12419
- return;
13044
+ try {
13045
+ const trial = await createSkillTrial(driver, resolved, selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry));
13046
+ if (options.json) {
13047
+ console.log(JSON.stringify(trial, null, 2));
13048
+ return;
13049
+ }
13050
+ console.log(`Read-only skill trial: ${trial.skill.name}`);
13051
+ console.log(`Source: ${trial.source}`);
13052
+ console.log(`Description: ${trial.skill.frontmatter.description}`);
13053
+ console.log("No configuration or runtime files were changed.");
13054
+ console.log("\n--- SKILL.md ---\n");
13055
+ console.log(trial.skill.content);
13056
+ } finally {
13057
+ await releaseGitSnapshotLease(resolved.cacheLeasePath);
12420
13058
  }
12421
- console.log(`Read-only skill trial: ${trial.skill.name}`);
12422
- console.log(`Source: ${trial.source}`);
12423
- console.log(`Description: ${trial.skill.frontmatter.description}`);
12424
- console.log("No configuration or runtime files were changed.");
12425
- console.log("\n--- SKILL.md ---\n");
12426
- console.log(trial.skill.content);
12427
13059
  });
12428
13060
  program.command("scan").description("scan a package source for validation findings").argument("<source>", "package source").option("--driver <driver>", "source driver").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (source, options) => {
12429
13061
  const targetRoot = normalizeTargetRoot(options.targetRoot);
12430
13062
  const resolvedInput = await resolvePackageSource(source, targetRoot);
12431
13063
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
12432
- const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join48(targetRoot, ".agentwheel", "cache") }))));
12433
- const result = await driver.scan(resolved);
12434
- if (result.findings.length === 0) {
12435
- console.log("Scan ok: no findings");
12436
- } else {
12437
- for (const finding of result.findings) {
12438
- console.log(`${finding.level.toUpperCase()}: ${finding.message}${finding.path ? ` (${finding.path})` : ""}`);
13064
+ const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join49(targetRoot, ".agentwheel", "cache") }))));
13065
+ try {
13066
+ const result = await driver.scan(resolved);
13067
+ if (result.findings.length === 0) {
13068
+ console.log("Scan ok: no findings");
13069
+ } else {
13070
+ for (const finding of result.findings) {
13071
+ console.log(`${finding.level.toUpperCase()}: ${finding.message}${finding.path ? ` (${finding.path})` : ""}`);
13072
+ }
12439
13073
  }
13074
+ if (!result.ok) process.exitCode = 1;
13075
+ } finally {
13076
+ await releaseGitSnapshotLease(resolved.cacheLeasePath);
12440
13077
  }
12441
- if (!result.ok) process.exitCode = 1;
12442
13078
  });
12443
- program.command("plan").description("preview what install would reconcile without writing").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--profile <name>", "workspace runtime profile").option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--dry-run", "accepted for symmetry; plan never writes", false).option("--format <fmt>", "output format: human|json|mermaid|html", "human").option("--json", "print the resolved plan as JSON", false).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
13079
+ program.command("plan").description("preview what install would reconcile without writing").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--profile <name>", "workspace runtime profile").option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--dry-run", "accepted for symmetry; plan never writes", false).option("--format <fmt>", "output format: human|json|mermaid|html", "human").option("--json", "print the resolved plan as JSON", false).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "exclude unrelated configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
12444
13080
  await runInstallCommand(source, { ...options, dryRun: true }, { apply: false });
12445
13081
  });
12446
- program.command("install").description("install configured packages into runtime targets").argument("[name-or-source]", "configured package name/source or package source to add and install").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--format <fmt>", "output format: human|json|mermaid|html", "human").option("--json", "print the resolved plan as JSON", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--reload-runtimes", "run configured runtime reload commands after executed semantic plugin changes", false).option("--restart-runtimes", "alias for --reload-runtimes", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).addHelpText("after", "\nScoped install never removes files owned only by other configured packages; run a full install to reconcile those removals.\n").action(async (source, options) => {
13082
+ program.command("install").description("install configured packages into runtime targets").argument("[name-or-source]", "configured package name/source or package source to add and install").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--format <fmt>", "output format: human|json|mermaid|html", "human").option("--json", "print the resolved plan as JSON", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--reload-runtimes", "run configured runtime reload commands after executed semantic plugin changes", false).option("--restart-runtimes", "alias for --reload-runtimes", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "exclude unrelated configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).addHelpText("after", "\nScoped install never removes files owned only by other configured packages; run a full install to reconcile those removals.\n").action(async (source, options) => {
12447
13083
  await runInstallCommand(source, options, { apply: !options.dryRun });
12448
13084
  });
12449
- program.command("serve").description("serve a read-only live dashboard for the resolved install plan").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--profile <name>", "workspace runtime profile").option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).option("--bind <addr>", "interface to bind", "127.0.0.1").option("--port <n>", "TCP port (0 selects an ephemeral port)", "8765").option("--interval <seconds>", "background re-render cadence in seconds", "60").option("--once", "render once and skip the background re-render loop", false).action(async (source, options) => {
13085
+ program.command("serve").description("serve a read-only live dashboard for the resolved install plan").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--profile <name>", "workspace runtime profile").option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "exclude unrelated configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).option("--bind <addr>", "interface to bind", "127.0.0.1").option("--port <n>", "TCP port (0 selects an ephemeral port)", "8765").option("--interval <seconds>", "background re-render cadence in seconds", "60").option("--once", "render once and skip the background re-render loop", false).action(async (source, options) => {
12450
13086
  await servePlanDashboard({
12451
13087
  bind: options.bind,
12452
13088
  port: parseServePort(options.port),
@@ -12455,11 +13091,13 @@ program.command("serve").description("serve a read-only live dashboard for the r
12455
13091
  buildReport: () => buildPlanReport(source, options)
12456
13092
  });
12457
13093
  });
12458
- program.command("sync", { hidden: true }).argument("[name-or-source]", "configured package name/source or package source").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--reload-runtimes", "run configured runtime reload commands after executed semantic plugin changes", false).option("--restart-runtimes", "alias for --reload-runtimes", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
13094
+ program.command("sync", { hidden: true }).argument("[name-or-source]", "configured package name/source or package source").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--reload-runtimes", "run configured runtime reload commands after executed semantic plugin changes", false).option("--restart-runtimes", "alias for --reload-runtimes", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "exclude unrelated configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
12459
13095
  console.error("warning: 'agentwheel sync' is deprecated and will be removed in 0.10. Use 'agentwheel install'.");
12460
13096
  await runInstallCommand(source, options, { apply: !options.dryRun });
12461
13097
  });
12462
- program.command("update").description("re-resolve tracking packages, then apply the result").argument("[name]", "configured package name or source to update").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plans without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--reload-runtimes", "run configured runtime reload commands after executed semantic plugin changes", false).option("--restart-runtimes", "alias for --reload-runtimes", false).option("--allow-adapter-code", "allow loading local adapter code from configured packages", false).option("--select <type/name>", "temporarily select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "temporarily select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--dependency <name-or-source>", "update one tracking dependency while keeping unrelated graph nodes locked (repeatable)", collectDependencyOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (name, options) => {
13098
+ program.command("update").description("re-resolve tracking packages, then apply the result").argument("[name]", "configured package name or source to update").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plans without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--reload-runtimes", "run configured runtime reload commands after executed semantic plugin changes", false).option("--restart-runtimes", "alias for --reload-runtimes", false).option("--allow-adapter-code", "allow loading local adapter code from configured packages", false).option("--select <type/name>", "temporarily select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "temporarily select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--dependency <name-or-source>", "update one tracking dependency while keeping unrelated graph nodes locked (repeatable)", collectDependencyOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "exclude unrelated configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (name, options) => {
13099
+ if (options.onlySource && options.dependency.length > 0) throw new Error("--only-source cannot be combined with --dependency.");
13100
+ if (options.onlySource && !name) throw new Error("--only-source requires a configured package argument.");
12463
13101
  if (name && options.dependency.length > 0) throw new Error("A package argument cannot be combined with --dependency.");
12464
13102
  if (options.dependency.length > 0 && (options.select.length > 0 || options.skill.length > 0)) {
12465
13103
  throw new Error("--dependency cannot be combined with --select or --skill; package selections remain unchanged.");
@@ -12478,6 +13116,11 @@ program.command("update").description("re-resolve tracking packages, then apply
12478
13116
  await runConfiguredGraphPackages(target, { ...normalizedOptions, scope: name }, { mode: "update" });
12479
13117
  }
12480
13118
  });
13119
+ program.command("skill").description("operate on configured skills").addCommand(
13120
+ new Command("update").description("reconcile one skill through its configured owning package").argument("<name>", "configured skill name").option("--package <name>", "owning configured package when automatic ownership is ambiguous").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plans without writing", false).option("--adopt", "replace unmanaged destinations in the owning package closure", false).option("--force-drift", "replace drifted managed artifacts", false).option("--allow-adapter-code", "allow loading local adapter code from the owning package", false).option("--no-deps", "resolve only the owning package and ignore its requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (name, options) => {
13121
+ await runSkillUpdateCommand(name, options);
13122
+ })
13123
+ );
12481
13124
  program.command("deps").description("inspect the OpenPack dependency graph").addCommand(
12482
13125
  new Command("tree").description("print the OpenPack dependency graph").argument("[source]", "optional package source to resolve").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
12483
13126
  const normalizedOptions = normalizeRuntimeScopeOptions(options, { defaultUser: shouldDefaultUserInstall(source, options) });
@@ -12492,7 +13135,7 @@ program.command("deps").description("inspect the OpenPack dependency graph").add
12492
13135
  for (const decision of result.bundle.graphLock.canonical.overrides) {
12493
13136
  console.log(`OVERRIDE ${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`);
12494
13137
  }
12495
- await rm12(result.bundle.root, { recursive: true, force: true });
13138
+ await rm13(result.bundle.root, { recursive: true, force: true });
12496
13139
  }
12497
13140
  continue;
12498
13141
  }
@@ -12621,6 +13264,11 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
12621
13264
  console.log(`Manifest revision: ${result.manifestRevision}`);
12622
13265
  console.log(`Owner: ${result.fromOwner} -> ${result.toOwner}`);
12623
13266
  });
13267
+ var mcpCommand = program.command("mcp").description("operate on MCP runtime configuration");
13268
+ mcpCommand.command("retire").description("remove one exact legacy MCP contribution with explicit state ownership").argument("<package>", "configured package containing exactly one legacy MCP artifact").option("--agent <name>", "one named agent from merged config").option("--profile <name>", "workspace runtime profile resolving to one target").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--from-workspace-root <path>", "required previous owner when managed legacy state exists").option("--dry-run", "show the retirement plan without writing (default)", false).option("--apply", "apply the exact reviewed retirement plan", false).option("--json", "print the install plan as JSON", false).action(async (packageName, options) => {
13269
+ if (options.apply && options.dryRun) throw new Error("--apply cannot be combined with --dry-run.");
13270
+ await runExactMcpRetirement(packageName, { ...options, dryRun: !options.apply });
13271
+ });
12624
13272
  program.command("eject").description("copy a managed artifact into local ownership").argument("<item>", "package/type/name").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (item, options) => {
12625
13273
  const targetRoot = normalizeTargetRoot(options.targetRoot);
12626
13274
  const result = await ejectArtifact(targetRoot, item);
@@ -12697,7 +13345,7 @@ journalCommand.command("list").description("show pending apply journals for reso
12697
13345
  if (!journal) continue;
12698
13346
  pending += 1;
12699
13347
  console.log(`PENDING ${state.adapter.name}/${state.installationType} at ${state.installRoot}`);
12700
- console.log(` journal: ${join48(state.installRoot, ".agentwheel", `${state.state.stateKey}.apply-journal.json`)}`);
13348
+ console.log(` journal: ${join49(state.installRoot, ".agentwheel", `${state.state.stateKey}.apply-journal.json`)}`);
12701
13349
  console.log(` stateKey: ${state.state.stateKey}`);
12702
13350
  console.log(` createdAt: ${journal.createdAt}`);
12703
13351
  console.log(` updatedAt: ${journal.updatedAt}`);
@@ -12818,7 +13466,11 @@ async function runInstallCommand(nameOrSource, options, behavior) {
12818
13466
  let extraPackage;
12819
13467
  if (nameOrSource && !configured) {
12820
13468
  try {
12821
- let entry = await packageEntryFromSource(nameOrSource, target.workspaceRoot, { ...targetOptions, adapter: targetOptions.adapter ?? target.adapter });
13469
+ let entry = await packageEntryFromSource(nameOrSource, target.workspaceRoot, {
13470
+ ...targetOptions,
13471
+ adapter: targetOptions.adapter ?? target.adapter,
13472
+ deferGraphRendering: true
13473
+ });
12822
13474
  if (targetOptions.multiAdapterSource) {
12823
13475
  entry = packageEntryWithAdapterSuffix(entry);
12824
13476
  }
@@ -12849,7 +13501,7 @@ async function runInstallCommand(nameOrSource, options, behavior) {
12849
13501
  if (reloaded) console.log(`Reloaded runtime via ${formatReloadCommands(target.reloadCommands)}.`);
12850
13502
  }
12851
13503
  }
12852
- await rm12(result.bundle.root, { recursive: true, force: true });
13504
+ await rm13(result.bundle.root, { recursive: true, force: true });
12853
13505
  if (result.plan.hasBlockingChanges) process.exitCode = 1;
12854
13506
  }
12855
13507
  if (behavior.apply && extraPackage && !targetOptions.onlySource) {
@@ -12915,6 +13567,7 @@ async function buildPlanReport(nameOrSource, options) {
12915
13567
  let entry = await packageEntryFromSource(nameOrSource, target.workspaceRoot, {
12916
13568
  ...targetOptions,
12917
13569
  adapter: targetOptions.adapter ?? target.adapter,
13570
+ deferGraphRendering: true,
12918
13571
  warn: collectWarning
12919
13572
  });
12920
13573
  if (targetOptions.multiAdapterSource) {
@@ -12944,7 +13597,7 @@ async function buildPlanReport(nameOrSource, options) {
12944
13597
  for (const result of results) {
12945
13598
  reportTargets.push(installPlanReportTarget(result.plan, result.graphLockDigest));
12946
13599
  reportWarnings.push(...result.warnings);
12947
- await rm12(result.bundle.root, { recursive: true, force: true });
13600
+ await rm13(result.bundle.root, { recursive: true, force: true });
12948
13601
  }
12949
13602
  }
12950
13603
  return planReport(reportTargets, reportWarnings);
@@ -12997,16 +13650,26 @@ async function packageEntryFromSource(source, targetRoot, options) {
12997
13650
  `No available version of ${provisionalName} satisfies ${options.version}; latest overall is ${initialVersion.availability.latestOverall ?? "unknown"}.`
12998
13651
  );
12999
13652
  }
13000
- const bundle = await stageSource(driver, resolvedSource, {
13653
+ const bundle = options.deferGraphRendering ? await stageSourceRaw(driver, resolvedSource, {
13654
+ cacheRoot: join49(targetRoot, ".agentwheel", "cache"),
13655
+ mode: options.mode,
13656
+ ref: initialVersion?.ref,
13657
+ frozenLock: lockMode
13658
+ }) : await stageSource(driver, resolvedSource, {
13001
13659
  workspaceRoot: targetRoot,
13002
13660
  adapter,
13003
- cacheRoot: join48(targetRoot, ".agentwheel", "cache"),
13661
+ cacheRoot: join49(targetRoot, ".agentwheel", "cache"),
13004
13662
  mode: options.mode,
13005
13663
  ref: initialVersion?.ref,
13006
13664
  frozenLock: lockMode,
13007
13665
  select: selectedArtifacts
13008
13666
  });
13009
- const installationType = resolveInstallationTypeForArtifacts(adapter, bundle.artifacts.map((artifact) => artifact.type), options.installationType);
13667
+ const installationArtifacts = options.deferGraphRendering ? filterArtifactsBySelection(bundle.artifacts, selectedArtifacts).filter((artifact) => !artifact.runtimes?.length || artifact.runtimes.includes(adapter.name)) : bundle.artifacts;
13668
+ const installationType = resolveInstallationTypeForArtifacts(
13669
+ adapter,
13670
+ installationArtifacts.map((artifact) => artifact.type),
13671
+ options.installationType
13672
+ );
13010
13673
  try {
13011
13674
  return {
13012
13675
  name: options.name ?? resolvedInput.registryEntry?.name ?? bundle.source.packageName ?? source,
@@ -13026,7 +13689,7 @@ async function packageEntryFromSource(source, targetRoot, options) {
13026
13689
  overrides: overrideArtifactsFromOptions(options)
13027
13690
  };
13028
13691
  } finally {
13029
- await rm12(bundle.root, { recursive: true, force: true });
13692
+ await rm13(bundle.root, { recursive: true, force: true });
13030
13693
  }
13031
13694
  }
13032
13695
  function findConfiguredPackage(packages, value) {
@@ -13036,6 +13699,64 @@ function findConfiguredPackageForTarget(packages, value, options, target) {
13036
13699
  const matches = packages.filter((pkg) => pkg.name === value || pkg.source === value);
13037
13700
  return options.multiAdapterSource ? matches.find((pkg) => pkg.adapter === target.adapter) : matches[0];
13038
13701
  }
13702
+ async function configuredPackageForSkill(target, packages, skillName, options, explicitPackageName) {
13703
+ const selector = `skills/${skillName}`;
13704
+ if (explicitPackageName) {
13705
+ const pkg = findConfiguredPackage(packages, explicitPackageName);
13706
+ if (!pkg) throw new Error(`Configured package not found: ${explicitPackageName}`);
13707
+ if (!await packageSelectsSkillForTarget(target, pkg, selector, options)) {
13708
+ throw new Error(`Configured package '${pkg.name}' does not select ${selector}.`);
13709
+ }
13710
+ return pkg;
13711
+ }
13712
+ const adapterPackages = packages.filter((pkg) => pkg.adapter === target.adapter);
13713
+ const candidates = adapterPackages.length > 0 ? adapterPackages : packages;
13714
+ const matches = [];
13715
+ for (const pkg of candidates) {
13716
+ if (await packageSelectsSkillForTarget(target, pkg, selector, options)) matches.push(pkg);
13717
+ }
13718
+ if (matches.length === 1) return matches[0];
13719
+ if (matches.length > 1) {
13720
+ throw new Error(
13721
+ `Skill '${skillName}' has multiple configured owners: ${matches.map((pkg) => pkg.name).sort().join(", ")}. Pass --package <name>.`
13722
+ );
13723
+ }
13724
+ throw new Error(
13725
+ `No configured owner found for skill '${skillName}'. Add ${selector} to one package selection or pass --package <name>.`
13726
+ );
13727
+ }
13728
+ async function packageSelectsSkillForTarget(target, pkg, selector, options) {
13729
+ const explicitSelection = normalizeArtifactSelectors(pkg.select, pkg.skills);
13730
+ if (explicitSelection && !explicitSelection.some((candidate) => candidate === selector)) return false;
13731
+ const groups = /* @__PURE__ */ new Map();
13732
+ const group = graphGroupForPackage(groups, target, pkg, options);
13733
+ const adapter = await resolveAdapterForTarget(group.target, group.adapterOptions);
13734
+ const graphLockPath = graphLockPathForTarget(
13735
+ group.target.workspaceRoot,
13736
+ targetKeyForTarget(group.target, adapter.name),
13737
+ adapter.name,
13738
+ targetFingerprintParts(group.target, adapter, group.adapterOptions, group.installationType)
13739
+ );
13740
+ if (await pathExists(graphLockPath)) {
13741
+ const lock = await readGraphLock(graphLockPath);
13742
+ const root = lock.canonical.roots.find((candidate) => candidate.rootId === pkg.name);
13743
+ if (root?.selected.includes(selector)) return true;
13744
+ }
13745
+ const results = await buildGraphPlansForTarget(target, void 0, {
13746
+ ...options,
13747
+ scope: pkg.name,
13748
+ onlySource: true,
13749
+ dryRun: true,
13750
+ suppressEmptyMessage: true
13751
+ }, { mode: "install" });
13752
+ try {
13753
+ return results.some((result) => result.bundle.graphLock.canonical.roots.some(
13754
+ (root) => root.rootId === pkg.name && root.selected.includes(selector)
13755
+ ));
13756
+ } finally {
13757
+ await Promise.all(results.map((result) => rm13(result.bundle.root, { recursive: true, force: true })));
13758
+ }
13759
+ }
13039
13760
  function noDepsFromOptions(options) {
13040
13761
  return options.noDeps === true || options.deps === false;
13041
13762
  }
@@ -13156,6 +13877,69 @@ function adapterOptionsForTarget(target, options) {
13156
13877
  function targetKeyForTarget(target, adapterName) {
13157
13878
  return target.targetKey ?? target.agentName ?? adapterName ?? target.source;
13158
13879
  }
13880
+ async function runExactMcpRetirement(packageName, options) {
13881
+ const normalizedOptions = normalizeRuntimeScopeOptions(options);
13882
+ const targets = await resolveCliTargets(normalizedOptions);
13883
+ if (targets.length !== 1) {
13884
+ throw new Error(`Exact MCP retirement requires exactly one runtime target, found ${targets.length}.`);
13885
+ }
13886
+ const target = targets[0];
13887
+ const expectedFromWorkspaceOwner = options.fromWorkspaceRoot ? workspaceOwnerForRoot(normalizeCliPath(options.fromWorkspaceRoot)) : void 0;
13888
+ const results = await buildGraphPlansForTarget(target, void 0, {
13889
+ ...normalizedOptions,
13890
+ scope: packageName,
13891
+ onlySource: true,
13892
+ retireExactMcp: true,
13893
+ expectedFromWorkspaceOwner,
13894
+ dryRun: true
13895
+ }, { mode: "install" });
13896
+ if (results.length !== 1) {
13897
+ await Promise.all(results.map((result2) => rm13(result2.bundle.root, { recursive: true, force: true })));
13898
+ throw new Error(`Exact MCP retirement requires one package plan, found ${results.length}.`);
13899
+ }
13900
+ const result = results[0];
13901
+ try {
13902
+ console.log(options.json ? JSON.stringify(result.plan, null, 2) : formatGraphPlan(result));
13903
+ if (result.plan.hasBlockingChanges) {
13904
+ process.exitCode = 1;
13905
+ return;
13906
+ }
13907
+ if (!options.dryRun) {
13908
+ await uninstall(result.plan, { transport: transportForTarget(target) });
13909
+ console.log(`Retired exact MCP contribution for ${result.plan.adapter} at ${result.plan.targetRoot}.`);
13910
+ }
13911
+ } finally {
13912
+ await rm13(result.bundle.root, { recursive: true, force: true });
13913
+ }
13914
+ }
13915
+ async function runSkillUpdateCommand(skillName, options) {
13916
+ const normalizedOptions = normalizeRuntimeScopeOptions(options);
13917
+ const composite = await resolveSelectedCompositeProfile(normalizedOptions);
13918
+ if (composite) {
13919
+ await runCompositeSkillUpdate(
13920
+ composite.workspaceRoot,
13921
+ composite.name,
13922
+ composite.profile,
13923
+ skillName,
13924
+ options.package,
13925
+ normalizedOptions
13926
+ );
13927
+ return;
13928
+ }
13929
+ const targets = await resolveCliTargets(normalizedOptions, { preferAllProfile: true });
13930
+ for (const target of targets) {
13931
+ const config = await readMergedWorkspaceConfig(target.workspaceRoot);
13932
+ const owner = await configuredPackageForSkill(target, config.packages, skillName, normalizedOptions, options.package);
13933
+ const mode = owner.mode === "tracking" ? "update" : "install";
13934
+ console.log(`Skill ${skillName}: ${owner.name} (${mode}).`);
13935
+ await runConfiguredGraphPackages(target, {
13936
+ ...normalizedOptions,
13937
+ scope: owner.name,
13938
+ onlySource: true,
13939
+ replaceConflict: options.adopt === true
13940
+ }, { mode });
13941
+ }
13942
+ }
13159
13943
  async function runConfiguredGraphPackages(target, options, behavior) {
13160
13944
  const results = await buildGraphPlansForTarget(target, void 0, options, behavior);
13161
13945
  for (const result of results) {
@@ -13177,7 +13961,7 @@ async function runConfiguredGraphPackages(target, options, behavior) {
13177
13961
  console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
13178
13962
  if (reloaded) console.log(`Reloaded runtime via ${formatReloadCommands(target.reloadCommands)}.`);
13179
13963
  }
13180
- await rm12(result.bundle.root, { recursive: true, force: true });
13964
+ await rm13(result.bundle.root, { recursive: true, force: true });
13181
13965
  if (result.plan.hasBlockingChanges) process.exitCode = 1;
13182
13966
  }
13183
13967
  }
@@ -13191,14 +13975,20 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
13191
13975
  const scopedPackage = targetOptions.scope ? findConfiguredPackage(config.packages, targetOptions.scope) : void 0;
13192
13976
  const scopedRootId = scopedPackage?.name ?? (source ? targetOptions.scope : void 0);
13193
13977
  if (targetOptions.scope && !scopedPackage && !source) throw new Error(`Configured package not found: ${targetOptions.scope}`);
13194
- if (!source || !targetOptions.onlySource) {
13978
+ if (scopedPackage && targetOptions.onlySource) {
13979
+ const group = graphGroupForPackage(groups, target, scopedPackage, targetOptions);
13980
+ group.packages.push(scopedPackage);
13981
+ } else if (!source || !targetOptions.onlySource) {
13195
13982
  for (const pkg of config.packages) {
13196
13983
  const group = graphGroupForPackage(groups, target, pkg, targetOptions);
13197
13984
  group.packages.push(pkg);
13198
13985
  }
13199
13986
  }
13200
13987
  if (source) {
13201
- let entry = targetOptions.extraPackage ?? await packageEntryFromSource(source, target.workspaceRoot, targetOptions);
13988
+ let entry = targetOptions.extraPackage ?? await packageEntryFromSource(source, target.workspaceRoot, {
13989
+ ...targetOptions,
13990
+ deferGraphRendering: true
13991
+ });
13202
13992
  if (targetOptions.multiAdapterSource) {
13203
13993
  entry = packageEntryWithAdapterSuffix(entry);
13204
13994
  }
@@ -13329,6 +14119,7 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
13329
14119
  targetKey: targetKeyForTarget(group.target, adapter.name),
13330
14120
  targetFingerprintParts: targetFingerprintParts(group.target, adapter, group.adapterOptions, group.installationType),
13331
14121
  installationType: group.installationType,
14122
+ stateKey: group.target.stateKey,
13332
14123
  noDeps: noDepsFromOptions(targetOptions),
13333
14124
  includeSuggestions: targetOptions.withSuggestions,
13334
14125
  suggestionAliases: suggestionAliasesFromOptions(targetOptions),
@@ -13342,12 +14133,14 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
13342
14133
  isTTY: process.stdin.isTTY === true,
13343
14134
  forceDrift: targetOptions.forceDrift,
13344
14135
  forceConflict: targetOptions.forceConflict,
13345
- replaceConflict: targetOptions.replaceConflict
14136
+ replaceConflict: targetOptions.replaceConflict,
14137
+ retireExactMcp: targetOptions.retireExactMcp,
14138
+ expectedFromWorkspaceOwner: targetOptions.expectedFromWorkspaceOwner
13346
14139
  });
13347
14140
  if ((behavior.mode === "install" || behavior.mode === "update") && scopedRootId) {
13348
14141
  const state = installStateForTarget(group.target, adapter, group.adapterOptions, group.installationType);
13349
14142
  const manifest = await readInstallManifest(state.installRoot, adapter.name, transport, state);
13350
- results.push(behavior.mode === "update" && previousGroupLock ? scopeUpdatePlanToRoot(result, scopedRootId, previousGroupLock, manifest) : scopeInstallPlanToRoot(result, scopedRootId, manifest));
14143
+ results.push(previousGroupLock ? scopeUpdatePlanToRoot(result, scopedRootId, previousGroupLock, manifest) : scopeInstallPlanToRoot(result, scopedRootId, manifest));
13351
14144
  } else if (scopedDependencyUpdate) {
13352
14145
  const state = installStateForTarget(group.target, adapter, group.adapterOptions, group.installationType);
13353
14146
  const manifest = await readInstallManifest(state.installRoot, adapter.name, transport, state);
@@ -13394,7 +14187,7 @@ function scopeUpdatePlanToDependencies(result, selectors, previousLock, manifest
13394
14187
  selectedPreviousNodeIds,
13395
14188
  selectedRootIds
13396
14189
  );
13397
- const graphLockDigest = createHash14("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
14190
+ const graphLockDigest = createHash15("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
13398
14191
  return {
13399
14192
  ...result,
13400
14193
  bundle: { ...result.bundle, graphLock },
@@ -13562,7 +14355,7 @@ function scopeUpdatePlanToRoot(result, rootId, previousLock, manifest) {
13562
14355
  selectedPreviousNodeIds,
13563
14356
  /* @__PURE__ */ new Set([rootId])
13564
14357
  );
13565
- const graphLockDigest = createHash14("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
14358
+ const graphLockDigest = createHash15("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
13566
14359
  return {
13567
14360
  ...scoped,
13568
14361
  bundle: { ...scoped.bundle, graphLock },
@@ -13634,7 +14427,7 @@ function keepManifestEntryOperation(entry, targetRoot, scopeDescription, operati
13634
14427
  artifactType: entry.artifactType,
13635
14428
  artifactName: entry.artifactName,
13636
14429
  kind: entry.kind,
13637
- destPath: operation?.destPath ?? join48(targetRoot, entry.path),
14430
+ destPath: operation?.destPath ?? join49(targetRoot, entry.path),
13638
14431
  relativeDestPath: entry.path,
13639
14432
  desiredHash: entry.sourceHash,
13640
14433
  currentHash: operation?.currentHash ?? entry.hash,
@@ -13751,7 +14544,7 @@ async function uninstallConfiguredPackage(target, packageName, options) {
13751
14544
  if (!options.dryRun) {
13752
14545
  console.log(formatUninstallResult(result));
13753
14546
  }
13754
- if (renderedRoot) await rm12(renderedRoot, { recursive: true, force: true });
14547
+ if (renderedRoot) await rm13(renderedRoot, { recursive: true, force: true });
13755
14548
  if (plan.hasBlockingChanges) process.exitCode = 1;
13756
14549
  }
13757
14550
  }
@@ -13802,14 +14595,15 @@ function targetFingerprintParts(target, adapter, options, installationType) {
13802
14595
  agentName: target.agentName,
13803
14596
  targetRoot: target.targetRoot,
13804
14597
  transport: target.transport,
13805
- ssh: target.ssh
14598
+ ssh: target.ssh,
14599
+ stateKey: target.stateKey
13806
14600
  };
13807
14601
  }
13808
14602
  function installStateForTarget(target, adapter, options, installationType) {
13809
14603
  const targetFingerprint = targetFingerprintDigest(target, adapter, options, installationType);
13810
14604
  return {
13811
14605
  installationType,
13812
- stateKey: stateKeyFor(adapter.name, { installationType, targetFingerprint }),
14606
+ stateKey: target.stateKey ?? stateKeyFor(adapter.name, { installationType, targetFingerprint }),
13813
14607
  installRoot: installRootForAdapterInstallationType(adapter, target.targetRoot, installationType, target.transport === "ssh")
13814
14608
  };
13815
14609
  }
@@ -14005,6 +14799,44 @@ async function runCompositeUpdate(workspaceRoot, profileName, profile, packageNa
14005
14799
  if (result.stderr.trim()) console.error(result.stderr.trimEnd());
14006
14800
  }
14007
14801
  }
14802
+ async function runCompositeSkillUpdate(workspaceRoot, profileName, profile, skillName, packageName, options) {
14803
+ const preflight = await collectCompositeStatus(workspaceRoot, profileName, profile, options);
14804
+ printStatusReport(preflight);
14805
+ const blockers = preflight.members.filter((member) => blocksCompositeApply(member.health));
14806
+ if (blockers.length > 0) {
14807
+ throw new Error(
14808
+ "Composite skill update blocked before member execution: " + blockers.map((member) => `${member.id}=${member.health}`).join(", ")
14809
+ );
14810
+ }
14811
+ const incomingChain = parseCompositeChain();
14812
+ const memberChain = [...incomingChain, compositeKey(workspaceRoot, profileName)];
14813
+ for (const member of profile.members) {
14814
+ if (!options.dryRun) {
14815
+ const before = preflight.members.find((candidate) => candidate.id === member.id);
14816
+ const revalidated = await collectCompositeMembers({
14817
+ cliVersion: CLI_VERSION,
14818
+ workspaceRoot,
14819
+ profileName,
14820
+ profileTtlSeconds: profile.refreshTtlSeconds,
14821
+ members: [member],
14822
+ refresh: true,
14823
+ chain: incomingChain
14824
+ });
14825
+ const current = revalidated[0];
14826
+ if (blocksCompositeApply(current.health)) {
14827
+ throw new Error(`Composite skill update stopped before ${member.id}: revalidation is ${current.health}.`);
14828
+ }
14829
+ if (statusRevisionSignature(before?.report) !== statusRevisionSignature(current.report)) {
14830
+ throw new Error(`Composite skill update stopped before ${member.id}: member revision changed after preflight.`);
14831
+ }
14832
+ }
14833
+ const args = compositeSkillUpdateArguments(member.profile, skillName, packageName, options);
14834
+ console.log(`${options.dryRun ? "Plan" : "Update"} member ${member.id}:`);
14835
+ const result = await runMemberAgentwheel(member, workspaceRoot, args, memberChain);
14836
+ if (result.stdout.trim()) console.log(result.stdout.trimEnd());
14837
+ if (result.stderr.trim()) console.error(result.stderr.trimEnd());
14838
+ }
14839
+ }
14008
14840
  async function runCompositeInstall(workspaceRoot, profileName, profile, nameOrSource, options, behavior) {
14009
14841
  const preflight = await collectCompositeStatus(workspaceRoot, profileName, profile, options);
14010
14842
  printStatusReport(preflight);
@@ -14080,6 +14912,21 @@ function compositeUpdateArguments(profile, packageName, options) {
14080
14912
  if (options.yes) args.push("--yes");
14081
14913
  return args;
14082
14914
  }
14915
+ function compositeSkillUpdateArguments(profile, skillName, packageName, options) {
14916
+ const args = ["skill", "update", skillName, "--profile", profile];
14917
+ if (packageName) args.push("--package", packageName);
14918
+ if (options.dryRun) args.push("--dry-run");
14919
+ if (options.adopt) args.push("--adopt");
14920
+ if (options.refresh) args.push("--refresh");
14921
+ if (options.forceDrift) args.push("--force-drift");
14922
+ if (options.allowAdapterCode) args.push("--allow-adapter-code");
14923
+ if (options.noDeps) args.push("--no-deps");
14924
+ if (options.frozenLock) args.push("--frozen-lock");
14925
+ if (options.offline) args.push("--offline");
14926
+ for (const trust of options.trust ?? []) args.push("--trust", trust);
14927
+ if (options.yes) args.push("--yes");
14928
+ return args;
14929
+ }
14083
14930
  function statusRevisionSignature(report) {
14084
14931
  if (!report) return "missing";
14085
14932
  return JSON.stringify({
@@ -14202,7 +15049,7 @@ async function collectPendingInstallWork(target, options) {
14202
15049
  const message = error instanceof Error ? error.message : String(error);
14203
15050
  return { pendingCount: 0, driftCount: 0, conflictCount: 0, counts: {}, error: message };
14204
15051
  } finally {
14205
- await Promise.all(results.map((result) => rm12(result.bundle.root, { recursive: true, force: true })));
15052
+ await Promise.all(results.map((result) => rm13(result.bundle.root, { recursive: true, force: true })));
14206
15053
  }
14207
15054
  }
14208
15055
  async function printDoctor(target, options) {
@@ -14219,12 +15066,12 @@ async function printDoctor(target, options) {
14219
15066
  const requestedSkills = doctorSkillRequests(target, options);
14220
15067
  const skills = [];
14221
15068
  for (const request of requestedSkills) {
14222
- const skillPath = join48(state.installRoot, targetMapping.dest, request.name);
15069
+ const skillPath = join49(state.installRoot, targetMapping.dest, request.name);
14223
15070
  const exists = await pathExists(skillPath);
14224
15071
  const manifestEntry = manifest?.entries.find((entry) => {
14225
15072
  if (entry.artifactType !== "skills") return false;
14226
15073
  const legacyInstallName = "installName" in entry && typeof entry.installName === "string" ? entry.installName : void 0;
14227
- return entry.artifactName === request.name || legacyInstallName === request.name || entry.path === join48(targetMapping.dest, request.name);
15074
+ return entry.artifactName === request.name || legacyInstallName === request.name || entry.path === join49(targetMapping.dest, request.name);
14228
15075
  });
14229
15076
  const status = manifestEntry ? "managed" : exists ? "present-unmanaged" : "missing";
14230
15077
  skills.push({
@@ -14304,7 +15151,7 @@ function doctorSkillLabel(name) {
14304
15151
  return `${name} skill`;
14305
15152
  }
14306
15153
  function isSyncwheelWorkspace(targetRoot) {
14307
- return existsSync(join48(targetRoot, ".syncwheel", "manifest.json"));
15154
+ return existsSync(join49(targetRoot, ".syncwheel", "manifest.json"));
14308
15155
  }
14309
15156
  function skillInstallCommand(adapter, installationType, options, skill, behavior = {}) {
14310
15157
  const args = [
@@ -14336,6 +15183,11 @@ function shellQuoteArg2(value) {
14336
15183
  function collectSelectOption(value, previous) {
14337
15184
  return [...previous, ...splitSelectorList(value)];
14338
15185
  }
15186
+ function parsePositiveInteger(value) {
15187
+ const parsed = Number(value);
15188
+ if (!Number.isInteger(parsed) || parsed < 1) throw new Error(`Expected a positive integer, got: ${value}`);
15189
+ return parsed;
15190
+ }
14339
15191
  function collectSkillOption(value, previous) {
14340
15192
  return [...previous, ...splitSelectorList(value)];
14341
15193
  }
@@ -14452,10 +15304,10 @@ function filterUninstallPlanBySelection(plan, selected) {
14452
15304
  };
14453
15305
  }
14454
15306
  async function initPackage(root) {
14455
- await mkdir23(join48(root, "instructions"), { recursive: true });
14456
- await mkdir23(join48(root, "rules"), { recursive: true });
14457
- await mkdir23(join48(root, "skills"), { recursive: true });
14458
- const manifestPath = join48(root, "openpack.json");
15307
+ await mkdir24(join49(root, "instructions"), { recursive: true });
15308
+ await mkdir24(join49(root, "rules"), { recursive: true });
15309
+ await mkdir24(join49(root, "skills"), { recursive: true });
15310
+ const manifestPath = join49(root, "openpack.json");
14459
15311
  const manifest = {
14460
15312
  schemaVersion: 2,
14461
15313
  name: "example/agentwheel-package",
@@ -14468,10 +15320,10 @@ async function initPackage(root) {
14468
15320
  };
14469
15321
  await writeFile22(manifestPath, `${JSON.stringify(manifest, null, 2)}
14470
15322
  `, "utf8");
14471
- await writeFile22(join48(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
15323
+ await writeFile22(join49(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
14472
15324
  }
14473
15325
  async function defaultBootstrapPackage(_root) {
14474
- const packageRoot = await findAgentwheelPackageRoot(dirname32(fileURLToPath3(import.meta.url)));
15326
+ const packageRoot = await findAgentwheelPackageRoot(dirname33(fileURLToPath3(import.meta.url)));
14475
15327
  if (!packageRoot) return void 0;
14476
15328
  return {
14477
15329
  name: "agentwheel",
@@ -14518,7 +15370,7 @@ async function findAgentwheelPackageRoot(start) {
14518
15370
  let current = resolve22(start);
14519
15371
  while (true) {
14520
15372
  if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
14521
- const parent = dirname32(current);
15373
+ const parent = dirname33(current);
14522
15374
  if (parent === current) return void 0;
14523
15375
  current = parent;
14524
15376
  }