agentwheel 0.10.0 → 0.14.3

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,13 +3,15 @@ import {
3
3
  atomicCopy,
4
4
  hashPath,
5
5
  inferSourceDriverName,
6
+ isIgnoredGeneratedEntry,
6
7
  pathExists,
7
8
  writeJsonAtomic
8
- } from "./chunk-N2LZY7LO.js";
9
+ } from "./chunk-B3FMBTWC.js";
9
10
 
10
11
  // src/cli/index.ts
11
- import { mkdir as mkdir14, rm as rm9, writeFile as writeFile12 } from "fs/promises";
12
- import { dirname as dirname21, join as join28, resolve as resolve18 } from "path";
12
+ import { mkdir as mkdir16, rm as rm9, writeFile as writeFile14 } from "fs/promises";
13
+ import { homedir as homedir9 } from "os";
14
+ import { dirname as dirname23, join as join31, resolve as resolve18 } from "path";
13
15
  import { fileURLToPath as fileURLToPath3 } from "url";
14
16
  import { Command } from "commander";
15
17
 
@@ -18,6 +20,7 @@ import { resolve as resolve2 } from "path";
18
20
 
19
21
  // src/model/adapter.ts
20
22
  import { readFile } from "fs/promises";
23
+ import { homedir } from "os";
21
24
  import { parse, printParseErrorCode } from "jsonc-parser";
22
25
  import { z as z2 } from "zod";
23
26
 
@@ -36,6 +39,7 @@ var artifactTypeSchema = z.enum([
36
39
  "fragments"
37
40
  ]);
38
41
  var fileKindSchema = z.enum(["file", "dir"]);
42
+ var artifactFormatSchema = z.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i, "artifact format must be a stable identifier");
39
43
  var packageAssetSchema = z.object({
40
44
  from: z.string().min(1),
41
45
  into: z.string().min(1),
@@ -68,6 +72,7 @@ var artifactSchema = z.object({
68
72
  relativePath: z.string().min(1),
69
73
  kind: fileKindSchema,
70
74
  hash: z.string().min(16),
75
+ format: artifactFormatSchema.optional(),
71
76
  packageName: z.string().min(1).optional(),
72
77
  channel: z.enum(["managed", "overlay", "addition", "override", "ejected"]).default("managed"),
73
78
  assets: z.array(packageAssetSchema).optional(),
@@ -79,20 +84,105 @@ var artifactSchema = z.object({
79
84
  });
80
85
 
81
86
  // src/model/adapter.ts
87
+ var defaultInstallationType = "local";
88
+ var installationTypeSchema = z2.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i, "installation type must be a stable path-safe identifier");
82
89
  var targetMappingSchema = z2.object({
83
90
  dest: z2.string().min(1),
84
91
  enabled: z2.boolean().default(true),
85
- semantic: z2.enum(["openclaw-plugin"]).optional(),
92
+ root: z2.enum(["target", "home"]).optional(),
93
+ formats: z2.array(z2.string().min(1)).optional(),
94
+ semantic: z2.enum(["openclaw-plugin", "codex-subagent", "copilot-instruction", "copilot-prompt", "copilot-agent"]).optional(),
86
95
  merge: z2.enum(["json-deep", "codex-toml-mcp"]).optional()
87
96
  });
97
+ var targetRegistrySchema = z2.record(installationTypeSchema, targetMappingSchema);
98
+ var targetRegistryInputSchema = z2.union([
99
+ targetMappingSchema.transform((mapping) => ({ [defaultInstallationType]: mapping })),
100
+ targetRegistrySchema
101
+ ]);
88
102
  var adapterSchema = z2.object({
89
103
  name: z2.string().min(1),
90
104
  displayName: z2.string().min(1).optional(),
91
105
  targets: z2.partialRecord(
92
106
  artifactTypeSchema,
93
- targetMappingSchema
107
+ targetRegistryInputSchema
94
108
  ).default({})
95
109
  });
110
+ function supportedInstallationTypes(adapter, artifactType) {
111
+ const registries = artifactType ? [adapter.targets[artifactType]] : Object.entries(adapter.targets).filter(([type]) => type !== "fragments").map(([, registry]) => registry);
112
+ const types = /* @__PURE__ */ new Set();
113
+ for (const registry of registries) {
114
+ for (const [installationType, target] of Object.entries(registry ?? {})) {
115
+ if (target.enabled) types.add(installationType);
116
+ }
117
+ }
118
+ return [...types].sort((a, b) => a.localeCompare(b));
119
+ }
120
+ function resolveInstallationTypeForArtifacts(adapter, artifactTypes, requested) {
121
+ const installableTypes = [...new Set(artifactTypes.filter((type) => type !== "fragments"))];
122
+ if (installableTypes.length === 0) {
123
+ return requested ?? resolveInstallationTypeForAdapter(adapter, requested);
124
+ }
125
+ for (const type of installableTypes) {
126
+ const supported = supportedInstallationTypes(adapter, type);
127
+ if (supported.length === 0) {
128
+ throw new Error(`Adapter ${adapter.name} does not support ${type} artifacts for any installation type.`);
129
+ }
130
+ if (requested && !supported.includes(requested)) {
131
+ throw new Error(`Adapter ${adapter.name} does not support ${type} artifacts for installation type '${requested}'. Supported: ${supported.join(", ")}`);
132
+ }
133
+ }
134
+ if (requested) return requested;
135
+ const [firstType, ...restTypes] = installableTypes;
136
+ let candidates = new Set(supportedInstallationTypes(adapter, firstType));
137
+ for (const type of restTypes) {
138
+ const supported = new Set(supportedInstallationTypes(adapter, type));
139
+ candidates = new Set([...candidates].filter((candidate) => supported.has(candidate)));
140
+ }
141
+ const available = [...candidates].sort((a, b) => a.localeCompare(b));
142
+ if (available.length === 1) return available[0];
143
+ if (available.length === 0) {
144
+ throw new Error(`Adapter ${adapter.name} has no common installation type for: ${installableTypes.join(", ")}`);
145
+ }
146
+ throw new Error(`Installation type required for ${adapter.name}; supported for selected artifacts: ${available.join(", ")}. Pass --installation-type <type>.`);
147
+ }
148
+ function resolveInstallationTypeForAdapter(adapter, requested) {
149
+ const supported = supportedInstallationTypes(adapter);
150
+ if (requested) {
151
+ if (!supported.includes(requested)) {
152
+ throw new Error(`Adapter ${adapter.name} does not support installation type '${requested}'. Supported: ${supported.join(", ") || "<none>"}`);
153
+ }
154
+ return requested;
155
+ }
156
+ if (supported.length === 1) return supported[0];
157
+ if (supported.length === 0) return defaultInstallationType;
158
+ throw new Error(`Installation type required for ${adapter.name}; supported: ${supported.join(", ")}. Pass --installation-type <type>.`);
159
+ }
160
+ function targetMappingForArtifact(adapter, artifactType, installationType) {
161
+ return adapter.targets[artifactType]?.[installationType];
162
+ }
163
+ function installRootForArtifacts(adapter, targetRoot, installationType, artifactTypes, isSsh = false) {
164
+ const roots = new Set(
165
+ [...new Set(artifactTypes.filter((type) => type !== "fragments"))].map((type) => targetMappingForArtifact(adapter, type, installationType)?.root ?? "target")
166
+ );
167
+ if (roots.has("home") && roots.has("target")) {
168
+ throw new Error(`Installation type '${installationType}' for ${adapter.name} mixes home-rooted and target-rooted artifacts.`);
169
+ }
170
+ return roots.has("home") && !isSsh ? userHomeRoot() : targetRoot;
171
+ }
172
+ function installRootForAdapterInstallationType(adapter, targetRoot, installationType, isSsh = false) {
173
+ const roots = /* @__PURE__ */ new Set();
174
+ for (const registry of Object.values(adapter.targets)) {
175
+ const target = registry?.[installationType];
176
+ if (target?.enabled) roots.add(target.root ?? "target");
177
+ }
178
+ if (roots.has("home") && roots.has("target")) {
179
+ throw new Error(`Installation type '${installationType}' for ${adapter.name} mixes home-rooted and target-rooted artifacts.`);
180
+ }
181
+ return roots.has("home") && !isSsh ? userHomeRoot() : targetRoot;
182
+ }
183
+ function userHomeRoot() {
184
+ return process.env.AGENTWHEEL_TEST_HOME || process.env.HOME || homedir();
185
+ }
96
186
  async function loadAdapterConfig(path) {
97
187
  const content = await readFile(path, "utf8");
98
188
  const errors = [];
@@ -112,28 +202,72 @@ var claudeAdapter = {
112
202
  name: "claude",
113
203
  displayName: "Claude Code",
114
204
  targets: {
115
- instructions: { enabled: true, dest: ".claude/CLAUDE.md" },
116
- rules: { enabled: true, dest: ".claude/rules" },
117
- skills: { enabled: true, dest: ".claude/skills" },
118
- commands: { enabled: true, dest: ".claude/commands" },
119
- subagents: { enabled: true, dest: ".claude/agents" },
120
- mcp: { enabled: true, dest: ".claude/.mcp.json", merge: "json-deep" },
121
- hooks: { enabled: true, dest: ".claude/settings.json", merge: "json-deep" },
122
- settings: { enabled: true, dest: ".claude/settings.json", merge: "json-deep" }
205
+ instructions: {
206
+ local: { enabled: true, dest: "CLAUDE.md" },
207
+ user: { enabled: true, root: "home", dest: ".claude/CLAUDE.md" }
208
+ },
209
+ rules: {
210
+ local: { enabled: true, dest: ".claude/rules", formats: ["markdown-rule", "claude-markdown-rule"] },
211
+ user: { enabled: true, root: "home", dest: ".claude/rules", formats: ["markdown-rule", "claude-markdown-rule"] }
212
+ },
213
+ skills: {
214
+ local: { enabled: true, dest: ".claude/skills" },
215
+ user: { enabled: true, root: "home", dest: ".claude/skills" }
216
+ },
217
+ commands: {
218
+ local: { enabled: true, dest: ".claude/commands" },
219
+ user: { enabled: true, root: "home", dest: ".claude/commands" }
220
+ },
221
+ subagents: {
222
+ local: { enabled: true, dest: ".claude/agents" },
223
+ user: { enabled: true, root: "home", dest: ".claude/agents" }
224
+ },
225
+ mcp: {
226
+ local: { enabled: true, dest: ".mcp.json", merge: "json-deep" }
227
+ },
228
+ hooks: {
229
+ local: { enabled: true, dest: ".claude/settings.json", merge: "json-deep" },
230
+ user: { enabled: true, root: "home", dest: ".claude/settings.json", merge: "json-deep" }
231
+ },
232
+ settings: {
233
+ local: { enabled: true, dest: ".claude/settings.json", merge: "json-deep" },
234
+ user: { enabled: true, root: "home", dest: ".claude/settings.json", merge: "json-deep" }
235
+ }
123
236
  }
124
237
  };
125
238
 
126
239
  // src/adapters/copilot.ts
127
240
  var copilotAdapter = {
128
241
  name: "copilot",
129
- displayName: "GitHub Copilot",
242
+ displayName: "GitHub Copilot CLI",
130
243
  targets: {
131
- instructions: { enabled: true, dest: ".github/copilot-instructions.md" },
132
- rules: { enabled: true, dest: ".github/instructions" },
133
- commands: { enabled: true, dest: ".github/prompts" },
134
- skills: { enabled: true, dest: ".github/skills" },
135
- subagents: { enabled: true, dest: ".github/agents" },
136
- mcp: { enabled: true, dest: ".vscode/mcp.json", merge: "json-deep" }
244
+ instructions: {
245
+ local: { enabled: true, dest: ".github/copilot-instructions.md" },
246
+ user: { enabled: true, root: "home", dest: ".copilot/copilot-instructions.md" }
247
+ },
248
+ rules: {
249
+ local: { enabled: true, dest: ".github/instructions", formats: ["markdown-rule", "copilot-instruction-rule"], semantic: "copilot-instruction" },
250
+ user: { enabled: true, root: "home", dest: ".copilot/instructions", formats: ["markdown-rule", "copilot-instruction-rule"], semantic: "copilot-instruction" }
251
+ },
252
+ commands: {
253
+ local: { enabled: true, dest: ".github/prompts", semantic: "copilot-prompt" }
254
+ },
255
+ skills: {
256
+ local: { enabled: true, dest: ".github/skills" },
257
+ user: { enabled: true, root: "home", dest: ".copilot/skills" }
258
+ },
259
+ subagents: {
260
+ local: { enabled: true, dest: ".github/agents", semantic: "copilot-agent" },
261
+ user: { enabled: true, root: "home", dest: ".copilot/agents", semantic: "copilot-agent" }
262
+ },
263
+ mcp: {
264
+ local: { enabled: true, dest: ".github/mcp.json", merge: "json-deep" },
265
+ user: { enabled: true, root: "home", dest: ".copilot/mcp-config.json", merge: "json-deep" }
266
+ },
267
+ hooks: {
268
+ local: { enabled: true, dest: ".github/hooks" },
269
+ user: { enabled: true, root: "home", dest: ".copilot/hooks" }
270
+ }
137
271
  }
138
272
  };
139
273
 
@@ -142,14 +276,30 @@ var codexAdapter = {
142
276
  name: "codex",
143
277
  displayName: "Codex CLI",
144
278
  targets: {
145
- instructions: { enabled: true, dest: ".codex/AGENTS.md" },
146
- rules: { enabled: true, dest: ".codex/rules" },
147
- skills: { enabled: true, dest: ".codex/skills" },
148
- commands: { enabled: true, dest: ".codex/commands" },
149
- subagents: { enabled: true, dest: ".codex/agents" },
150
- mcp: { enabled: true, dest: ".codex/config.toml", merge: "codex-toml-mcp" },
151
- hooks: { enabled: true, dest: ".codex/hooks.json", merge: "json-deep" },
152
- settings: { enabled: true, dest: ".codex/settings.json", merge: "json-deep" }
279
+ instructions: {
280
+ local: { enabled: true, dest: "AGENTS.md" },
281
+ user: { enabled: true, root: "home", dest: ".codex/AGENTS.md" }
282
+ },
283
+ rules: {
284
+ local: { enabled: true, dest: ".codex/rules", formats: ["codex-command-policy"] },
285
+ user: { enabled: true, root: "home", dest: ".codex/rules", formats: ["codex-command-policy"] }
286
+ },
287
+ skills: {
288
+ local: { enabled: true, dest: ".agents/skills" },
289
+ user: { enabled: true, root: "home", dest: ".agents/skills" }
290
+ },
291
+ subagents: {
292
+ local: { enabled: true, dest: ".codex/agents", semantic: "codex-subagent" },
293
+ user: { enabled: true, root: "home", dest: ".codex/agents", semantic: "codex-subagent" }
294
+ },
295
+ mcp: {
296
+ local: { enabled: true, dest: ".codex/config.toml", merge: "codex-toml-mcp" },
297
+ user: { enabled: true, root: "home", dest: ".codex/config.toml", merge: "codex-toml-mcp" }
298
+ },
299
+ hooks: {
300
+ local: { enabled: true, dest: ".codex/hooks.json", merge: "json-deep" },
301
+ user: { enabled: true, root: "home", dest: ".codex/hooks.json", merge: "json-deep" }
302
+ }
153
303
  }
154
304
  };
155
305
 
@@ -158,14 +308,12 @@ var hermesAdapter = {
158
308
  name: "hermes",
159
309
  displayName: "Hermes",
160
310
  targets: {
161
- instructions: { enabled: true, dest: ".hermes/AGENTS.md" },
162
- rules: { enabled: true, dest: ".hermes/rules" },
163
- skills: { enabled: true, dest: ".hermes/skills" },
164
- commands: { enabled: true, dest: ".hermes/commands" },
165
- subagents: { enabled: true, dest: ".hermes/agents" },
166
- mcp: { enabled: true, dest: ".hermes/mcp", merge: "json-deep" },
167
- hooks: { enabled: true, dest: ".hermes/hooks", merge: "json-deep" },
168
- settings: { enabled: true, dest: ".hermes/settings.json", merge: "json-deep" }
311
+ instructions: {
312
+ local: { enabled: true, dest: "AGENTS.md" }
313
+ },
314
+ skills: {
315
+ user: { enabled: true, root: "home", dest: ".hermes/skills" }
316
+ }
169
317
  }
170
318
  };
171
319
 
@@ -174,15 +322,13 @@ var openClawAdapter = {
174
322
  name: "openclaw",
175
323
  displayName: "OpenClaw",
176
324
  targets: {
177
- instructions: { enabled: true, dest: ".openclaw/AGENTS.md" },
178
- rules: { enabled: true, dest: ".openclaw/rules" },
179
- skills: { enabled: true, dest: ".openclaw/skills" },
180
- commands: { enabled: true, dest: ".openclaw/commands" },
181
- subagents: { enabled: true, dest: ".openclaw/agents" },
182
- mcp: { enabled: true, dest: ".openclaw/mcp", merge: "json-deep" },
183
- hooks: { enabled: true, dest: ".openclaw/hooks", merge: "json-deep" },
184
- settings: { enabled: true, dest: ".openclaw/settings.json", merge: "json-deep" },
185
- plugins: { enabled: true, dest: ".openclaw/plugins", semantic: "openclaw-plugin" }
325
+ skills: {
326
+ local: { enabled: true, dest: "skills" },
327
+ user: { enabled: true, root: "home", dest: ".openclaw/skills" }
328
+ },
329
+ plugins: {
330
+ local: { enabled: true, dest: ".openclaw/plugins", formats: ["openclaw-plugin"], semantic: "openclaw-plugin" }
331
+ }
186
332
  }
187
333
  };
188
334
 
@@ -264,11 +410,11 @@ async function resolveAdapter(options) {
264
410
  }
265
411
 
266
412
  // src/install/apply.ts
267
- import { execFile as execFile2 } from "child_process";
413
+ import { execFile as execFile3 } from "child_process";
268
414
  import { mkdtemp, rm as rm3, writeFile as writeFile6 } from "fs/promises";
269
415
  import { tmpdir as tmpdir2 } from "os";
270
416
  import { basename as basename2, join as join4 } from "path";
271
- import { promisify as promisify2 } from "util";
417
+ import { promisify as promisify3 } from "util";
272
418
 
273
419
  // src/model/graph-lock.ts
274
420
  import { createHash } from "crypto";
@@ -297,7 +443,8 @@ var graphLockRootSchema = z3.object({
297
443
  graphNodeId: z3.string().min(1),
298
444
  mode: z3.enum(["pinned", "tracking"]),
299
445
  selected: z3.array(z3.string().min(1)),
300
- aliases: z3.record(z3.string(), z3.string().min(1)).optional()
446
+ aliases: z3.record(z3.string(), z3.string().min(1)).optional(),
447
+ overrides: z3.array(z3.string().min(1)).optional()
301
448
  });
302
449
  var graphLockEdgeSchema = z3.object({
303
450
  from: z3.string().min(1),
@@ -346,6 +493,15 @@ var graphLockNamespacingSchema = z3.object({
346
493
  installName: z3.string().min(1),
347
494
  reason: z3.enum(["alias", "transitive-collision"])
348
495
  });
496
+ var graphLockOverrideSchema = z3.object({
497
+ rootId: z3.string().min(1),
498
+ selector: z3.string().min(1),
499
+ graphNodeId: z3.string().min(1),
500
+ overriddenGraphNodeId: z3.string().min(1),
501
+ type: artifactTypeSchema,
502
+ name: z3.string().min(1),
503
+ installName: z3.string().min(1)
504
+ });
349
505
  var graphLockCanonicalSchema = z3.object({
350
506
  targetFingerprint: z3.string().min(1).optional(),
351
507
  roots: z3.array(graphLockRootSchema),
@@ -354,6 +510,7 @@ var graphLockCanonicalSchema = z3.object({
354
510
  includeEdges: z3.array(graphLockIncludeEdgeSchema).default([]),
355
511
  artifacts: z3.array(graphLockArtifactSchema).default([]),
356
512
  namespacing: z3.array(graphLockNamespacingSchema).default([]),
513
+ overrides: z3.array(graphLockOverrideSchema).default([]),
357
514
  plainNameIncumbents: z3.array(graphLockPlainNameIncumbentSchema).default([])
358
515
  });
359
516
  var graphLockSchema = z3.object({
@@ -384,7 +541,11 @@ function canonicalizeGraphLock(lock) {
384
541
  version: 1,
385
542
  canonical: {
386
543
  targetFingerprint: parsed.canonical.targetFingerprint,
387
- roots: [...parsed.canonical.roots].map((root) => ({ ...root, selected: sortedUnique(root.selected) })).sort((a, b) => `${a.rootId}:${a.graphNodeId}`.localeCompare(`${b.rootId}:${b.graphNodeId}`)),
544
+ roots: [...parsed.canonical.roots].map((root) => ({
545
+ ...root,
546
+ selected: sortedUnique(root.selected),
547
+ overrides: root.overrides ? sortedUnique(root.overrides) : void 0
548
+ })).sort((a, b) => `${a.rootId}:${a.graphNodeId}`.localeCompare(`${b.rootId}:${b.graphNodeId}`)),
388
549
  nodes: [...parsed.canonical.nodes].map((node) => ({
389
550
  ...node,
390
551
  requiredBy: sortedUnique(node.requiredBy),
@@ -395,6 +556,7 @@ function canonicalizeGraphLock(lock) {
395
556
  includeEdges: [...parsed.canonical.includeEdges].sort((a, b) => `${a.fromNodeId}:${a.alias}:${a.toNodeId}:${a.selector}`.localeCompare(`${b.fromNodeId}:${b.alias}:${b.toNodeId}:${b.selector}`)),
396
557
  artifacts: [...parsed.canonical.artifacts].map((artifact) => ({ ...artifact, owners: sortedUnique(artifact.owners) })).sort((a, b) => a.logicalSelector.localeCompare(b.logicalSelector)),
397
558
  namespacing: [...parsed.canonical.namespacing].sort((a, b) => `${a.type}:${a.installName}:${a.graphNodeId}:${a.name}`.localeCompare(`${b.type}:${b.installName}:${b.graphNodeId}:${b.name}`)),
559
+ overrides: [...parsed.canonical.overrides].sort((a, b) => `${a.type}:${a.installName}:${a.graphNodeId}:${a.overriddenGraphNodeId}`.localeCompare(`${b.type}:${b.installName}:${b.graphNodeId}:${b.overriddenGraphNodeId}`)),
398
560
  plainNameIncumbents: [...parsed.canonical.plainNameIncumbents].sort((a, b) => `${a.adapter}:${a.targetFingerprint}:${a.type}:${a.name}`.localeCompare(`${b.adapter}:${b.targetFingerprint}:${b.type}:${b.name}`))
399
561
  }
400
562
  };
@@ -428,8 +590,11 @@ function stableValue(value) {
428
590
  }
429
591
 
430
592
  // src/transport/local.ts
593
+ import { execFile } from "child_process";
431
594
  import { mkdir as mkdir2, readFile as readFile4, rename as rename2, rm, writeFile as writeFile3 } from "fs/promises";
432
595
  import { dirname as dirname2 } from "path";
596
+ import { promisify } from "util";
597
+ var execFileAsync = promisify(execFile);
433
598
  var localTransport = {
434
599
  kind: "local",
435
600
  description: "local filesystem",
@@ -448,20 +613,23 @@ var localTransport = {
448
613
  },
449
614
  writeJsonAtomic,
450
615
  atomicCopy,
451
- rm: (path) => rm(path, { recursive: true, force: true })
616
+ rm: (path) => rm(path, { recursive: true, force: true }),
617
+ async execFile(command, args, options = {}) {
618
+ await execFileAsync(command, args, { cwd: options.cwd });
619
+ }
452
620
  };
453
621
 
454
622
  // src/transport/ssh.ts
455
- import { execFile, spawn } from "child_process";
623
+ import { execFile as execFile2, spawn } from "child_process";
456
624
  import { basename, dirname as dirname3 } from "path";
457
625
  import { dirname as posixDirname } from "path/posix";
458
- import { promisify } from "util";
459
- var execFileAsync = promisify(execFile);
626
+ import { promisify as promisify2 } from "util";
627
+ var execFileAsync2 = promisify2(execFile2);
460
628
  function createSshTransport(config) {
461
629
  const endpoint = config.user ? `${config.user}@${config.host}` : config.host;
462
630
  const args = baseSshArgs(config, endpoint);
463
631
  async function run(command) {
464
- const { stdout } = await execFileAsync("ssh", [...args, command], { maxBuffer: 20 * 1024 * 1024 });
632
+ const { stdout } = await execFileAsync2("ssh", [...args, command], { maxBuffer: 20 * 1024 * 1024 });
465
633
  return stdout;
466
634
  }
467
635
  async function runWithInput(command, input) {
@@ -507,6 +675,11 @@ function createSshTransport(config) {
507
675
  },
508
676
  rm(path) {
509
677
  return run(`rm -rf -- ${quoteSh(path)}`).then(() => void 0);
678
+ },
679
+ execFile(command, commandArgs, options = {}) {
680
+ const quoted = [command, ...commandArgs].map(quoteSh).join(" ");
681
+ const remoteCommand = options.cwd ? `cd ${quoteSh(options.cwd)} && ${quoted}` : quoted;
682
+ return run(remoteCommand).then(() => void 0);
510
683
  }
511
684
  };
512
685
  }
@@ -573,6 +746,11 @@ const { createHash } = require("node:crypto");
573
746
  const { readdirSync, readFileSync, statSync } = require("node:fs");
574
747
  const { join, relative } = require("node:path");
575
748
  const target = process.argv[1];
749
+ const ignoredNames = new Set([".git", "node_modules", "__pycache__", ".DS_Store"]);
750
+ const ignoredSuffixes = [".pyc", ".pyo"];
751
+ function isIgnoredGeneratedEntry(name) {
752
+ return ignoredNames.has(name) || ignoredSuffixes.some((suffix) => name.endsWith(suffix));
753
+ }
576
754
  function hashPath(path) {
577
755
  const stats = statSync(path);
578
756
  if (stats.isFile()) {
@@ -590,7 +768,7 @@ function listFiles(root) {
590
768
  const out = [];
591
769
  function walk(dir) {
592
770
  for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
593
- if (entry.name === ".git" || entry.name === "node_modules") continue;
771
+ if (isIgnoredGeneratedEntry(entry.name)) continue;
594
772
  const full = join(dir, entry.name);
595
773
  if (entry.isDirectory()) walk(full);
596
774
  else if (entry.isFile()) out.push(full);
@@ -706,6 +884,8 @@ var installManifestV1Schema = z4.object({
706
884
  var installManifestV2Schema = z4.object({
707
885
  version: z4.literal(2),
708
886
  adapter: z4.string().min(1),
887
+ installationType: installationTypeSchema.default(defaultInstallationType),
888
+ stateKey: z4.string().min(1).optional(),
709
889
  targetRoot: z4.string().min(1),
710
890
  generatedAt: z4.string().datetime(),
711
891
  revision: z4.string().min(16),
@@ -738,6 +918,7 @@ var sourceLockSchema = z4.object({
738
918
  relativePath: z4.string().min(1),
739
919
  kind: fileKindSchema,
740
920
  hash: z4.string().min(16),
921
+ format: artifactFormatSchema.optional(),
741
922
  composedFrom: z4.array(composedFromEntrySchema).optional()
742
923
  })
743
924
  )
@@ -748,16 +929,25 @@ import { join as join2 } from "path";
748
929
  function metadataDir(targetRoot) {
749
930
  return join2(targetRoot, ".agentwheel");
750
931
  }
751
- function installManifestPath(targetRoot, adapter) {
752
- return join2(metadataDir(targetRoot), `${adapter}.install-manifest.json`);
932
+ function stateKeyFor(adapter, scope = {}) {
933
+ if (scope.stateKey) return sanitizeStateKey(scope.stateKey);
934
+ const installationType = scope.installationType ?? defaultInstallationType;
935
+ const fingerprint = scope.targetFingerprint ? `.${scope.targetFingerprint}` : "";
936
+ return sanitizeStateKey(`${adapter}.${installationType}${fingerprint}`);
937
+ }
938
+ function installManifestPath(targetRoot, adapter, scope = {}) {
939
+ return join2(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.install-manifest.json`);
940
+ }
941
+ function sourceLockPath(targetRoot, adapter, scope = {}) {
942
+ return join2(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.source-lock.json`);
753
943
  }
754
- function sourceLockPath(targetRoot, adapter) {
755
- return join2(metadataDir(targetRoot), `${adapter}.source-lock.json`);
944
+ function sanitizeStateKey(value) {
945
+ return value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "default";
756
946
  }
757
947
 
758
948
  // src/install/manifest.ts
759
- async function readInstallManifest(targetRoot, adapter, transport = localTransport) {
760
- const path = installManifestPath(targetRoot, adapter);
949
+ async function readInstallManifest(targetRoot, adapter, transport = localTransport, scope = {}) {
950
+ const path = installManifestPath(targetRoot, adapter, scope);
761
951
  if (!await transport.pathExists(path)) return void 0;
762
952
  const raw = JSON.parse(await transport.readFile(path));
763
953
  const parsed = installManifestSchema.parse(raw);
@@ -768,14 +958,17 @@ async function readInstallManifest(targetRoot, adapter, transport = localTranspo
768
958
  }
769
959
  async function writeInstallManifest(manifest, transport = localTransport) {
770
960
  const next = withManifestRevision(manifest);
771
- await transport.writeJsonAtomic(installManifestPath(next.targetRoot, next.adapter), stripReadOnlyManifestFields(next));
961
+ await transport.writeJsonAtomic(installManifestPath(next.targetRoot, next.adapter, {
962
+ installationType: next.installationType,
963
+ stateKey: next.stateKey
964
+ }), stripReadOnlyManifestFields(next));
772
965
  }
773
- async function writeSourceLock(targetRoot, adapter, lock, transport = localTransport) {
774
- await transport.writeJsonAtomic(sourceLockPath(targetRoot, adapter), lock);
966
+ async function writeSourceLock(targetRoot, adapter, lock, transport = localTransport, scope = {}) {
967
+ await transport.writeJsonAtomic(sourceLockPath(targetRoot, adapter, scope), lock);
775
968
  }
776
- async function removeStateFiles(targetRoot, adapter, transport = localTransport) {
777
- await transport.rm(installManifestPath(targetRoot, adapter));
778
- await transport.rm(sourceLockPath(targetRoot, adapter));
969
+ async function removeStateFiles(targetRoot, adapter, transport = localTransport, scope = {}) {
970
+ await transport.rm(installManifestPath(targetRoot, adapter, scope));
971
+ await transport.rm(sourceLockPath(targetRoot, adapter, scope));
779
972
  }
780
973
  function normalizeTargetRoot(path) {
781
974
  return resolve3(path);
@@ -784,7 +977,11 @@ function withManifestRevision(manifest) {
784
977
  if (manifest.version !== 2) {
785
978
  throw new Error("Install manifest writes must use version 2");
786
979
  }
787
- const normalized = installManifestV2Schema.parse(stripReadOnlyManifestFields(manifest));
980
+ const raw = stripReadOnlyManifestFields(manifest);
981
+ const normalized = installManifestV2Schema.parse({
982
+ installationType: defaultInstallationType,
983
+ ...raw
984
+ });
788
985
  const withoutRevision = stripRuntimeManifestFields(normalized);
789
986
  return {
790
987
  ...normalized,
@@ -843,21 +1040,23 @@ function assertOperationContained(operation, targetRoot) {
843
1040
  // src/install/transaction.ts
844
1041
  import { cp, mkdir as mkdir4, rm as rm2, stat as stat2 } from "fs/promises";
845
1042
  import { dirname as dirname5, join as join3 } from "path";
846
- function applyLockPath(targetRoot, adapter) {
847
- return join3(metadataDir(targetRoot), `${adapter}.apply-lock`);
1043
+ function applyLockPath(targetRoot, adapter, scope = {}) {
1044
+ return join3(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-lock`);
848
1045
  }
849
- function applyJournalPath(targetRoot, adapter) {
850
- return join3(metadataDir(targetRoot), `${adapter}.apply-journal.json`);
1046
+ function applyJournalPath(targetRoot, adapter, scope = {}) {
1047
+ return join3(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-journal.json`);
851
1048
  }
852
- function applyBackupDir(targetRoot, adapter) {
853
- return join3(metadataDir(targetRoot), `${adapter}.apply-backups`);
1049
+ function applyBackupDir(targetRoot, adapter, scope = {}) {
1050
+ return join3(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-backups`);
854
1051
  }
855
- async function acquireApplyLock(targetRoot, adapter, transport = localTransport, options = {}) {
856
- const lockPath = applyLockPath(targetRoot, adapter);
1052
+ async function acquireApplyLock(targetRoot, adapter, transport = localTransport, options = {}, scope = {}) {
1053
+ const lockPath = applyLockPath(targetRoot, adapter, scope);
857
1054
  const ownerPath = join3(lockPath, "owner.json");
858
1055
  const metadata = {
859
1056
  pid: process.pid,
860
1057
  adapter,
1058
+ installationType: scope.installationType,
1059
+ stateKey: scope.stateKey,
861
1060
  targetRoot,
862
1061
  transport: transport.description,
863
1062
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -876,21 +1075,24 @@ async function acquireApplyLock(targetRoot, adapter, transport = localTransport,
876
1075
  };
877
1076
  }
878
1077
  async function writeApplyJournal(journal, transport = localTransport) {
879
- await transport.writeJsonAtomic(applyJournalPath(journal.targetRoot, journal.adapter), {
1078
+ await transport.writeJsonAtomic(applyJournalPath(journal.targetRoot, journal.adapter, {
1079
+ installationType: journal.installationType,
1080
+ stateKey: journal.stateKey
1081
+ }), {
880
1082
  ...journal,
881
1083
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
882
1084
  });
883
1085
  }
884
- async function readApplyJournal(targetRoot, adapter, transport = localTransport) {
885
- const path = applyJournalPath(targetRoot, adapter);
1086
+ async function readApplyJournal(targetRoot, adapter, transport = localTransport, scope = {}) {
1087
+ const path = applyJournalPath(targetRoot, adapter, scope);
886
1088
  if (!await transport.pathExists(path)) return void 0;
887
1089
  return JSON.parse(await transport.readFile(path));
888
1090
  }
889
- async function removeApplyJournal(targetRoot, adapter, transport = localTransport) {
890
- await transport.rm(applyJournalPath(targetRoot, adapter));
891
- await transport.rm(applyBackupDir(targetRoot, adapter));
1091
+ async function removeApplyJournal(targetRoot, adapter, transport = localTransport, scope = {}) {
1092
+ await transport.rm(applyJournalPath(targetRoot, adapter, scope));
1093
+ await transport.rm(applyBackupDir(targetRoot, adapter, scope));
892
1094
  }
893
- async function recordBackup(operation, index, targetRoot, adapter, transport = localTransport) {
1095
+ async function recordBackup(operation, index, targetRoot, adapter, transport = localTransport, scope = {}) {
894
1096
  const hadExisting = await transport.pathExists(operation.destPath);
895
1097
  if (!hadExisting || transport.kind !== "local" || operation.action !== "update" && operation.action !== "remove" && operation.action !== "create") {
896
1098
  return {
@@ -900,7 +1102,7 @@ async function recordBackup(operation, index, targetRoot, adapter, transport = l
900
1102
  hadExisting
901
1103
  };
902
1104
  }
903
- const backupPath = join3(applyBackupDir(targetRoot, adapter), String(index));
1105
+ const backupPath = join3(applyBackupDir(targetRoot, adapter, scope), String(index));
904
1106
  await rm2(backupPath, { recursive: true, force: true });
905
1107
  await mkdir4(dirname5(backupPath), { recursive: true });
906
1108
  await cp(operation.destPath, backupPath, { recursive: operation.kind === "dir", dereference: true });
@@ -1053,14 +1255,14 @@ function normalizeOwners(owners) {
1053
1255
  }
1054
1256
 
1055
1257
  // src/install/apply.ts
1056
- var execFileAsync2 = promisify2(execFile2);
1258
+ var execFileAsync3 = promisify3(execFile3);
1057
1259
  async function applyCombinedInstallPlan(plan, options = {}) {
1058
1260
  return applyPlanTransactionally(plan, options);
1059
1261
  }
1060
- async function recoverPendingApply(targetRoot, adapter, transport = localTransport) {
1061
- const lock = await acquireApplyLock(targetRoot, adapter, transport);
1262
+ async function recoverPendingApply(targetRoot, adapter, transport = localTransport, scope = {}) {
1263
+ const lock = await acquireApplyLock(targetRoot, adapter, transport, {}, scope);
1062
1264
  try {
1063
- const journal = await readApplyJournal(targetRoot, adapter, transport);
1265
+ const journal = await readApplyJournal(targetRoot, adapter, transport, scope);
1064
1266
  if (!journal) return void 0;
1065
1267
  if (journal.operations.some((operation) => operation.action === "plugin" || operation.action === "program")) {
1066
1268
  throw new Error("Cannot automatically recover a journal containing semantic plugin or programmatic operations");
@@ -1080,10 +1282,10 @@ async function recoverPendingApply(targetRoot, adapter, transport = localTranspo
1080
1282
  }
1081
1283
  if (operationNeedsSource(operation) && operation.sourcePath && !await localPathExists(operation.sourcePath)) {
1082
1284
  await rollbackStartedOperations(journal, transport);
1083
- await removeApplyJournal(targetRoot, adapter, transport);
1285
+ await removeApplyJournal(targetRoot, adapter, transport, scope);
1084
1286
  return void 0;
1085
1287
  }
1086
- const backup = started ?? await recordBackup(operation, index, targetRoot, adapter, transport);
1288
+ const backup = started ?? await recordBackup(operation, index, targetRoot, adapter, transport, scope);
1087
1289
  if (!started && isJournaledMutation(operation)) {
1088
1290
  journal.completed.push(backup);
1089
1291
  startedByIndex.set(index, backup);
@@ -1103,11 +1305,12 @@ async function recoverPendingApply(targetRoot, adapter, transport = localTranspo
1103
1305
  }
1104
1306
  async function applyPlanTransactionally(plan, options = {}) {
1105
1307
  const transport = options.transport ?? localTransport;
1308
+ const scope = { installationType: plan.installationType, stateKey: plan.stateKey };
1106
1309
  if (plan.hasBlockingChanges) {
1107
1310
  const blockers = plan.operations.filter((operation) => operation.action === "drift" || operation.action === "conflict");
1108
1311
  throw new Error(`Refusing to apply with blocking changes: ${blockers.map((item) => item.relativeDestPath).join(", ")}`);
1109
1312
  }
1110
- const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, options.lock);
1313
+ const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, options.lock, scope);
1111
1314
  try {
1112
1315
  await assertBaseRevision(plan, transport);
1113
1316
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1115,6 +1318,8 @@ async function applyPlanTransactionally(plan, options = {}) {
1115
1318
  const journal = {
1116
1319
  version: 1,
1117
1320
  adapter: plan.adapter,
1321
+ installationType: plan.installationType,
1322
+ stateKey: plan.stateKey,
1118
1323
  targetRoot: plan.targetRoot,
1119
1324
  baseRevision: plan.baseRevision,
1120
1325
  graphLockDigest,
@@ -1125,6 +1330,8 @@ async function applyPlanTransactionally(plan, options = {}) {
1125
1330
  manifest: {
1126
1331
  version: 2,
1127
1332
  adapter: plan.adapter,
1333
+ installationType: plan.installationType,
1334
+ stateKey: plan.stateKey,
1128
1335
  targetRoot: plan.targetRoot,
1129
1336
  generatedAt: now,
1130
1337
  revision: "pending-apply-0000",
@@ -1140,7 +1347,7 @@ async function applyPlanTransactionally(plan, options = {}) {
1140
1347
  const entries = [];
1141
1348
  for (const [index, operation] of plan.operations.entries()) {
1142
1349
  assertOperationContained(operation, plan.targetRoot);
1143
- const backup = isJournaledMutation(operation) ? await recordBackup(operation, index, plan.targetRoot, plan.adapter, transport) : void 0;
1350
+ const backup = isJournaledMutation(operation) ? await recordBackup(operation, index, plan.targetRoot, plan.adapter, transport, scope) : void 0;
1144
1351
  if (backup) {
1145
1352
  journal.completed.push(backup);
1146
1353
  await writeApplyJournal(journal, transport);
@@ -1166,6 +1373,7 @@ async function applyPlanTransactionally(plan, options = {}) {
1166
1373
  async function uninstall(plan, options = {}) {
1167
1374
  const resolvedOptions = typeof options === "boolean" ? { dryRun: options } : options;
1168
1375
  const transport = resolvedOptions.transport ?? localTransport;
1376
+ const scope = { installationType: plan.installationType, stateKey: plan.stateKey };
1169
1377
  if (resolvedOptions.keepFiles && resolvedOptions.force) {
1170
1378
  throw new Error("--keep-files cannot be combined with --force.");
1171
1379
  }
@@ -1185,6 +1393,8 @@ async function uninstall(plan, options = {}) {
1185
1393
  const finalManifest = withManifestRevision({
1186
1394
  version: 2,
1187
1395
  adapter: plan.adapter,
1396
+ installationType: plan.installationType,
1397
+ stateKey: plan.stateKey,
1188
1398
  targetRoot: plan.targetRoot,
1189
1399
  generatedAt: now,
1190
1400
  revision: "pending-uninstall-0",
@@ -1202,13 +1412,15 @@ async function uninstall(plan, options = {}) {
1202
1412
  });
1203
1413
  }).sort((a, b) => a.path.localeCompare(b.path))
1204
1414
  });
1205
- const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, resolvedOptions.lock);
1415
+ const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, resolvedOptions.lock, scope);
1206
1416
  try {
1207
1417
  await assertBaseRevision(plan, transport);
1208
1418
  const journal = {
1209
1419
  version: 1,
1210
1420
  mode: "uninstall",
1211
1421
  adapter: plan.adapter,
1422
+ installationType: plan.installationType,
1423
+ stateKey: plan.stateKey,
1212
1424
  targetRoot: plan.targetRoot,
1213
1425
  baseRevision: plan.baseRevision,
1214
1426
  graphLockDigest: plan.graphLockDigest,
@@ -1225,7 +1437,7 @@ async function uninstall(plan, options = {}) {
1225
1437
  };
1226
1438
  await writeApplyJournal(journal, transport);
1227
1439
  for (const [index, operation] of (resolvedOptions.keepFiles ? [] : removable).entries()) {
1228
- const backup = await recordBackup(operation, index, plan.targetRoot, plan.adapter, transport);
1440
+ const backup = await recordBackup(operation, index, plan.targetRoot, plan.adapter, transport, scope);
1229
1441
  journal.completed.push(backup);
1230
1442
  await writeApplyJournal(journal, transport);
1231
1443
  await applyOperation(operation, { transport, now, graphLockDigest: plan.graphLockDigest });
@@ -1251,15 +1463,24 @@ async function commitJournalState(journal, transport, entries, now) {
1251
1463
  entries: entries ? entries.sort((a, b) => a.path.localeCompare(b.path)) : journal.manifest.entries
1252
1464
  });
1253
1465
  if (journal.mode === "uninstall" && manifest.entries.length === 0) {
1254
- await removeStateFiles(journal.targetRoot, journal.adapter, transport);
1466
+ await removeStateFiles(journal.targetRoot, journal.adapter, transport, {
1467
+ installationType: journal.installationType,
1468
+ stateKey: journal.stateKey
1469
+ });
1255
1470
  } else {
1256
- if (journal.sourceLock) await writeSourceLock(journal.targetRoot, journal.adapter, journal.sourceLock, transport);
1471
+ if (journal.sourceLock) await writeSourceLock(journal.targetRoot, journal.adapter, journal.sourceLock, transport, {
1472
+ installationType: journal.installationType,
1473
+ stateKey: journal.stateKey
1474
+ });
1257
1475
  await writeInstallManifest(manifest, transport);
1258
1476
  }
1259
1477
  if (journal.graphLockPath && journal.graphLock) await writeGraphLock(journal.graphLockPath, journal.graphLock);
1260
1478
  if (journal.graphLockRemovePath) await rm3(journal.graphLockRemovePath, { force: true });
1261
1479
  if (journal.workspaceConfigPath && journal.workspaceConfig) await writeJsonAtomic(journal.workspaceConfigPath, journal.workspaceConfig);
1262
- await removeApplyJournal(journal.targetRoot, journal.adapter, transport);
1480
+ await removeApplyJournal(journal.targetRoot, journal.adapter, transport, {
1481
+ installationType: journal.installationType,
1482
+ stateKey: journal.stateKey
1483
+ });
1263
1484
  return manifest;
1264
1485
  }
1265
1486
  async function applyOperation(operation, context) {
@@ -1269,18 +1490,10 @@ async function applyOperation(operation, context) {
1269
1490
  throw new Error(`Invalid plugin operation missing hash: ${operation.relativeDestPath}`);
1270
1491
  }
1271
1492
  if (context.executePlugins) {
1272
- if (transport.kind !== "local") {
1273
- throw new Error(`Cannot execute semantic plugin install over ${transport.description}. Run plugin installation on the remote host.`);
1274
- }
1275
1493
  if (!operation.semanticCommand || operation.semanticCommand.length === 0) {
1276
1494
  throw new Error(`Invalid plugin operation missing command: ${operation.relativeDestPath}`);
1277
1495
  }
1278
- const command = operation.semanticCommand[0];
1279
- const args = operation.semanticCommand.slice(1);
1280
- if (!command) {
1281
- throw new Error(`Invalid plugin operation missing command: ${operation.relativeDestPath}`);
1282
- }
1283
- await execFileAsync2(command, args);
1496
+ await executePluginInstall(operation, transport);
1284
1497
  }
1285
1498
  return manifestEntryForOperation(operation, {
1286
1499
  now,
@@ -1360,6 +1573,32 @@ async function applyOperation(operation, context) {
1360
1573
  }
1361
1574
  return void 0;
1362
1575
  }
1576
+ async function executePluginInstall(operation, transport) {
1577
+ const command = operation.semanticCommand?.[0];
1578
+ const args = operation.semanticCommand?.slice(1) ?? [];
1579
+ if (!command) {
1580
+ throw new Error(`Invalid plugin operation missing command: ${operation.relativeDestPath}`);
1581
+ }
1582
+ if (!operation.sourcePath) {
1583
+ throw new Error(`Invalid plugin operation missing source path: ${operation.relativeDestPath}`);
1584
+ }
1585
+ if (transport.kind === "local") {
1586
+ await execFileAsync3(command, args);
1587
+ return;
1588
+ }
1589
+ if (!transport.execFile) {
1590
+ throw new Error(`Cannot execute semantic plugin install over ${transport.description}: transport does not support remote commands.`);
1591
+ }
1592
+ const stagingRoot = join4(operation.destPath, ".agentwheel", "plugin-staging", `${process.pid}-${Date.now()}`);
1593
+ const remoteSourcePath = join4(stagingRoot, basename2(operation.sourcePath));
1594
+ try {
1595
+ await transport.atomicCopy(operation.sourcePath, remoteSourcePath, operation.kind);
1596
+ const remoteArgs = args.map((arg) => arg === operation.sourcePath ? remoteSourcePath : arg);
1597
+ await transport.execFile(command, remoteArgs, { cwd: operation.destPath });
1598
+ } finally {
1599
+ await transport.rm(stagingRoot);
1600
+ }
1601
+ }
1363
1602
  async function entryForCompletedOperation(operation, transport, now, graphLockDigest) {
1364
1603
  if (operation.action === "remove") return void 0;
1365
1604
  if (operation.action === "create" || operation.action === "update") {
@@ -1407,7 +1646,10 @@ function manifestEntryForOperation(operation, values) {
1407
1646
  };
1408
1647
  }
1409
1648
  async function assertBaseRevision(plan, transport) {
1410
- const current = await readInstallManifest(plan.targetRoot, plan.adapter, transport);
1649
+ const current = await readInstallManifest(plan.targetRoot, plan.adapter, transport, {
1650
+ installationType: plan.installationType,
1651
+ stateKey: plan.stateKey
1652
+ });
1411
1653
  const currentRevision = current?.revision ?? null;
1412
1654
  if (currentRevision !== plan.baseRevision) {
1413
1655
  throw new Error(`Install manifest changed since planning for ${plan.adapter}; replan needed`);
@@ -1456,15 +1698,476 @@ async function mergeWithTransport(sourcePath, destPath, transport, merge) {
1456
1698
  }
1457
1699
 
1458
1700
  // src/install/plan.ts
1459
- import { join as join5, relative as relative2 } from "path";
1701
+ import { join as join8, relative as relative2 } from "path";
1702
+
1703
+ // src/staging/codex-subagents.ts
1704
+ import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
1705
+ import { basename as basename3, dirname as dirname7, join as join5 } from "path";
1706
+ var requiredCodexAgentFields = ["name", "description", "developer_instructions"];
1707
+ async function renderCodexSubagents(artifacts, stageRoot, adapter) {
1708
+ if (adapter?.name !== "codex") return artifacts;
1709
+ const names = /* @__PURE__ */ new Set();
1710
+ const rendered = [];
1711
+ for (const artifact of artifacts) {
1712
+ if (artifact.type !== "subagents") {
1713
+ rendered.push(artifact);
1714
+ continue;
1715
+ }
1716
+ const next = await renderCodexSubagent(artifact, stageRoot);
1717
+ if (names.has(next.name)) {
1718
+ throw new Error(`Codex subagents produce duplicate custom agent name '${next.name}'.`);
1719
+ }
1720
+ names.add(next.name);
1721
+ rendered.push(next);
1722
+ }
1723
+ return rendered;
1724
+ }
1725
+ async function renderCodexSubagent(artifact, stageRoot) {
1726
+ const sourcePath = artifact.stagedPath ?? artifact.sourcePath;
1727
+ const agentName = codexAgentName(artifact);
1728
+ const renderedPath = join5(stageRoot, ".agentwheel-rendered", "codex-subagents", `${agentName}.toml`);
1729
+ const lowerSourcePath = sourcePath.toLowerCase();
1730
+ if (artifact.kind === "file" && (artifact.name.toLowerCase().endsWith(".toml") || lowerSourcePath.endsWith(".toml"))) {
1731
+ const content = await readFile7(sourcePath, "utf8");
1732
+ validateCodexAgentToml(content, sourcePath);
1733
+ return {
1734
+ ...artifact,
1735
+ name: agentName,
1736
+ relativePath: join5("subagents", `${agentName}.toml`),
1737
+ kind: "file",
1738
+ hash: await hashPath(sourcePath)
1739
+ };
1740
+ }
1741
+ const markdownPath = artifact.kind === "dir" ? join5(sourcePath, "AGENTS.md") : sourcePath;
1742
+ if (artifact.kind === "dir" && !await pathExists(markdownPath)) {
1743
+ throw new Error(`Codex subagent directory ${artifact.relativePath} must contain AGENTS.md.`);
1744
+ }
1745
+ if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md") && !lowerSourcePath.endsWith(".md")) {
1746
+ throw new Error(`Codex subagent ${artifact.relativePath} must be a .toml file, .md file, or directory containing AGENTS.md.`);
1747
+ }
1748
+ const markdown = await readFile7(markdownPath, "utf8");
1749
+ const toml = markdownToCodexAgentToml(agentName, markdown);
1750
+ await mkdir6(dirname7(renderedPath), { recursive: true });
1751
+ await writeFile7(renderedPath, toml, "utf8");
1752
+ return {
1753
+ ...artifact,
1754
+ name: agentName,
1755
+ sourcePath: renderedPath,
1756
+ stagedPath: renderedPath,
1757
+ relativePath: join5("subagents", `${agentName}.toml`),
1758
+ kind: "file",
1759
+ hash: await hashPath(renderedPath)
1760
+ };
1761
+ }
1762
+ function codexAgentName(artifact) {
1763
+ const raw = artifact.kind === "dir" ? artifact.name : basename3(artifact.name);
1764
+ return raw.replace(/\.toml$/i, "").replace(/\.md$/i, "");
1765
+ }
1766
+ function validateCodexAgentToml(content, path) {
1767
+ for (const field of requiredCodexAgentFields) {
1768
+ const pattern = new RegExp(`(^|\\n)\\s*${escapeRegExp(field)}\\s*=`, "m");
1769
+ if (!pattern.test(content)) {
1770
+ throw new Error(`Codex custom agent TOML ${path} is missing required field '${field}'.`);
1771
+ }
1772
+ }
1773
+ }
1774
+ function markdownToCodexAgentToml(agentName, markdown) {
1775
+ const parsed = splitFrontmatter(markdown);
1776
+ const description = parsed.description ?? firstMeaningfulMarkdownLine(parsed.body) ?? `Custom Codex subagent ${agentName}.`;
1777
+ const developerInstructions = parsed.body.trim().length > 0 ? parsed.body.trimEnd() : description;
1778
+ return [
1779
+ `name = ${tomlString(agentName)}`,
1780
+ `description = ${tomlString(description)}`,
1781
+ `developer_instructions = ${tomlMultilineString(developerInstructions)}`,
1782
+ ""
1783
+ ].join("\n");
1784
+ }
1785
+ function splitFrontmatter(markdown) {
1786
+ const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(markdown);
1787
+ if (!match) return { body: markdown };
1788
+ const frontmatter = match[1] ?? "";
1789
+ const body = markdown.slice(match[0].length);
1790
+ const description = frontmatter.split(/\r?\n/).map((line) => /^description:\s*(?:"([^"]*)"|'([^']*)'|(.+))\s*$/.exec(line.trim())).find((item) => item !== null);
1791
+ return {
1792
+ body,
1793
+ description: description ? (description[1] ?? description[2] ?? description[3] ?? "").trim() : void 0
1794
+ };
1795
+ }
1796
+ function firstMeaningfulMarkdownLine(markdown) {
1797
+ for (const rawLine of markdown.split(/\r?\n/)) {
1798
+ const line = rawLine.trim();
1799
+ if (!line) continue;
1800
+ const heading = /^#{1,6}\s+(.+)$/.exec(line);
1801
+ return (heading?.[1] ?? line).trim();
1802
+ }
1803
+ return void 0;
1804
+ }
1805
+ function tomlString(value) {
1806
+ return JSON.stringify(value);
1807
+ }
1808
+ function tomlMultilineString(value) {
1809
+ const escaped = value.replaceAll("\\", "\\\\").replaceAll('"""', '\\"\\"\\"').replace(/\r\n?/g, "\n");
1810
+ return `"""
1811
+ ${escaped.endsWith("\n") ? escaped : `${escaped}
1812
+ `}"""`;
1813
+ }
1814
+ function escapeRegExp(value) {
1815
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1816
+ }
1817
+
1818
+ // src/staging/copilot-artifacts.ts
1819
+ import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
1820
+ import { basename as basename4, dirname as dirname8, join as join6 } from "path";
1821
+ async function renderCopilotArtifacts(artifacts, stageRoot, adapter) {
1822
+ if (adapter?.name !== "copilot") return artifacts;
1823
+ const names = /* @__PURE__ */ new Set();
1824
+ const rendered = [];
1825
+ for (const artifact of artifacts) {
1826
+ if (artifact.type !== "subagents") {
1827
+ rendered.push(artifact);
1828
+ continue;
1829
+ }
1830
+ const next = await renderCopilotSubagent(artifact, stageRoot);
1831
+ if (names.has(next.name)) {
1832
+ throw new Error(`Copilot subagents produce duplicate custom agent name '${next.name}'.`);
1833
+ }
1834
+ names.add(next.name);
1835
+ rendered.push(next);
1836
+ }
1837
+ return rendered;
1838
+ }
1839
+ async function renderCopilotSubagent(artifact, stageRoot) {
1840
+ const sourcePath = artifact.stagedPath ?? artifact.sourcePath;
1841
+ const agentName = copilotAgentName(artifact);
1842
+ const renderedPath = join6(stageRoot, ".agentwheel-rendered", "copilot-subagents", `${agentName}.agent.md`);
1843
+ const markdownPath = artifact.kind === "dir" ? join6(sourcePath, "AGENTS.md") : sourcePath;
1844
+ if (artifact.kind === "dir" && !await pathExists(markdownPath)) {
1845
+ throw new Error(`Copilot subagent directory ${artifact.relativePath} must contain AGENTS.md.`);
1846
+ }
1847
+ if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md")) {
1848
+ throw new Error(`Copilot subagent ${artifact.relativePath} must be a .md file or directory containing AGENTS.md.`);
1849
+ }
1850
+ const markdown = await readFile8(markdownPath, "utf8");
1851
+ await mkdir7(dirname8(renderedPath), { recursive: true });
1852
+ await writeFile8(renderedPath, ensureCopilotAgentDescription(agentName, markdown), "utf8");
1853
+ return {
1854
+ ...artifact,
1855
+ name: `${agentName}.agent.md`,
1856
+ sourcePath: renderedPath,
1857
+ stagedPath: renderedPath,
1858
+ relativePath: join6("subagents", `${agentName}.agent.md`),
1859
+ kind: "file",
1860
+ hash: await hashPath(renderedPath)
1861
+ };
1862
+ }
1863
+ function copilotAgentName(artifact) {
1864
+ const raw = artifact.kind === "dir" ? artifact.name : basename4(artifact.name);
1865
+ return raw.replace(/\.agent\.md$/i, "").replace(/\.md$/i, "");
1866
+ }
1867
+ function ensureCopilotAgentDescription(agentName, markdown) {
1868
+ const parsed = splitFrontmatter2(markdown);
1869
+ if (parsed.frontmatter !== void 0 && /^description\s*:/im.test(parsed.frontmatter)) {
1870
+ return markdown;
1871
+ }
1872
+ const description = firstMeaningfulMarkdownLine2(parsed.body) ?? `Custom Copilot agent ${agentName}.`;
1873
+ const frontmatter = parsed.frontmatter === void 0 ? `description: ${yamlString(description)}` : `${parsed.frontmatter.trimEnd()}
1874
+ description: ${yamlString(description)}`;
1875
+ return [
1876
+ "---",
1877
+ frontmatter,
1878
+ "---",
1879
+ "",
1880
+ parsed.body.trimStart()
1881
+ ].join("\n");
1882
+ }
1883
+ function splitFrontmatter2(markdown) {
1884
+ const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(markdown);
1885
+ if (!match) return { body: markdown };
1886
+ return {
1887
+ body: markdown.slice(match[0].length),
1888
+ frontmatter: match[1] ?? ""
1889
+ };
1890
+ }
1891
+ function firstMeaningfulMarkdownLine2(markdown) {
1892
+ for (const rawLine of markdown.split(/\r?\n/)) {
1893
+ const line = rawLine.trim();
1894
+ if (!line) continue;
1895
+ const heading = /^#{1,6}\s+(.+)$/.exec(line);
1896
+ return (heading?.[1] ?? line).trim();
1897
+ }
1898
+ return void 0;
1899
+ }
1900
+ function yamlString(value) {
1901
+ return JSON.stringify(value);
1902
+ }
1460
1903
 
1461
1904
  // src/targets/plugins/openclaw.ts
1462
1905
  function openClawPluginInstallCommand(request) {
1463
- return ["openclaw", "plugins", "install", "--link", request.path];
1906
+ return ["openclaw", "plugins", "install", "--force", request.path];
1907
+ }
1908
+
1909
+ // src/validation/artifacts.ts
1910
+ import { readFile as readFile9 } from "fs/promises";
1911
+ import { basename as basename5, join as join7 } from "path";
1912
+ async function validateArtifactsForInstall(artifacts, adapter, installationType) {
1913
+ const issues = [];
1914
+ for (const artifact of artifacts) {
1915
+ if (artifact.type === "fragments") continue;
1916
+ const target = targetMappingForArtifact(adapter, artifact.type, installationType);
1917
+ if (!target?.enabled) {
1918
+ issues.push({ artifact, message: `adapter ${adapter.name} does not support this artifact for installation type '${installationType}'` });
1919
+ continue;
1920
+ }
1921
+ issues.push(...await validateArtifact(artifact, target));
1922
+ }
1923
+ if (issues.length === 0) return;
1924
+ throw new Error([
1925
+ `Package artifacts are not installable for ${adapter.name}/${installationType}:`,
1926
+ ...issues.map((issue) => `- ${artifactLabel(issue.artifact)}: ${issue.message}`)
1927
+ ].join("\n"));
1928
+ }
1929
+ async function validateArtifact(artifact, target) {
1930
+ const issues = [];
1931
+ const format = artifact.format ?? await inferArtifactFormat(artifact, target) ?? semanticDefaultFormat(target);
1932
+ if (target.formats?.length) {
1933
+ if (!format) {
1934
+ issues.push({
1935
+ artifact,
1936
+ message: `format is unknown; expected one of: ${target.formats.join(", ")}`
1937
+ });
1938
+ } else if (!target.formats.includes(format)) {
1939
+ issues.push({
1940
+ artifact,
1941
+ message: `format '${format}' is not compatible; expected one of: ${target.formats.join(", ")}`
1942
+ });
1943
+ }
1944
+ }
1945
+ issues.push(...await validateKnownFormat(artifact, format, target));
1946
+ issues.push(...await validateGenericStructure(artifact, target));
1947
+ return issues;
1948
+ }
1949
+ async function inferArtifactFormat(artifact, target) {
1950
+ if (artifact.type === "rules") {
1951
+ if (hasExtension(artifact, ".rules")) return "codex-command-policy";
1952
+ if (hasExtension(artifact, ".md") || hasExtension(artifact, ".markdown")) return "markdown-rule";
1953
+ return void 0;
1954
+ }
1955
+ if (artifact.type === "plugins" && target.semantic === "openclaw-plugin") {
1956
+ if (artifact.kind === "dir" && (await openClawPluginManifestPaths(artifact)).length > 0) return "openclaw-plugin";
1957
+ }
1958
+ return void 0;
1959
+ }
1960
+ function semanticDefaultFormat(target) {
1961
+ if (target.semantic === "openclaw-plugin") return "openclaw-plugin";
1962
+ return void 0;
1963
+ }
1964
+ async function validateKnownFormat(artifact, format, target) {
1965
+ if (!format) return [];
1966
+ if (format === "codex-command-policy") return validateCodexCommandPolicyRule(artifact);
1967
+ if (format === "markdown-rule" || format === "claude-markdown-rule" || format === "copilot-instruction-rule") {
1968
+ return validateMarkdownRule(artifact, format);
1969
+ }
1970
+ if (format === "openclaw-plugin" || target.semantic === "openclaw-plugin") {
1971
+ return validateOpenClawPlugin(artifact);
1972
+ }
1973
+ return [];
1974
+ }
1975
+ async function validateGenericStructure(artifact, target) {
1976
+ const issues = [];
1977
+ if (artifact.type === "skills") {
1978
+ if (artifact.kind === "dir") {
1979
+ const skillMd = join7(artifactPath(artifact), "SKILL.md");
1980
+ if (!await pathExists(skillMd)) {
1981
+ issues.push({ artifact, message: "skill directory must contain SKILL.md" });
1982
+ } else {
1983
+ issues.push(...await validateSkillFrontmatter(artifact, skillMd));
1984
+ }
1985
+ } else if (!hasExtension(artifact, ".md")) {
1986
+ issues.push({ artifact, message: "file skill artifacts must be Markdown files" });
1987
+ } else {
1988
+ issues.push(...await validateSkillFrontmatter(artifact, artifactPath(artifact)));
1989
+ }
1990
+ }
1991
+ if (target.merge === "json-deep") {
1992
+ const parsed = await parseJsonObjectArtifact(artifact);
1993
+ if (!parsed.ok) issues.push({ artifact, message: parsed.message });
1994
+ }
1995
+ if (target.merge === "codex-toml-mcp") {
1996
+ const parsed = await parseJsonObjectArtifact(artifact);
1997
+ if (!parsed.ok) {
1998
+ issues.push({ artifact, message: parsed.message });
1999
+ } else if (Object.keys(extractMcpServers2(parsed.value)).length === 0) {
2000
+ issues.push({ artifact, message: "Codex MCP artifact must contain at least one server object, either under mcpServers or as top-level server entries" });
2001
+ }
2002
+ }
2003
+ return issues;
2004
+ }
2005
+ async function validateSkillFrontmatter(artifact, skillMdPath) {
2006
+ let content;
2007
+ try {
2008
+ content = await readFile9(skillMdPath, "utf8");
2009
+ } catch (error) {
2010
+ return [{ artifact, message: `could not read SKILL.md: ${errorMessage(error)}` }];
2011
+ }
2012
+ if (content.charCodeAt(0) === 65279) {
2013
+ return [{ artifact, message: "SKILL.md must not start with a UTF-8 BOM; YAML frontmatter must be the first bytes" }];
2014
+ }
2015
+ const isDelimiter = (line) => line.trimEnd() === "---";
2016
+ const lines = content.split(/\r?\n/);
2017
+ if (!isDelimiter(lines[0] ?? "")) {
2018
+ return [{ artifact, message: "SKILL.md must begin with YAML frontmatter delimited by '---' on the first line (no content before it)" }];
2019
+ }
2020
+ const closing = lines.findIndex((line, index) => index > 0 && isDelimiter(line));
2021
+ if (closing === -1) {
2022
+ return [{ artifact, message: "SKILL.md frontmatter is not closed with a '---' delimiter" }];
2023
+ }
2024
+ const issues = [];
2025
+ const frontmatter = lines.slice(1, closing);
2026
+ if (!frontmatter.some((line) => /^name\s*:/.test(line))) {
2027
+ issues.push({ artifact, message: "SKILL.md frontmatter must define 'name'" });
2028
+ }
2029
+ if (!frontmatter.some((line) => /^description\s*:/.test(line))) {
2030
+ issues.push({ artifact, message: "SKILL.md frontmatter must define 'description'" });
2031
+ }
2032
+ return issues;
2033
+ }
2034
+ async function validateCodexCommandPolicyRule(artifact) {
2035
+ const issues = [];
2036
+ if (artifact.type !== "rules") {
2037
+ return [{ artifact, message: "codex-command-policy format is only valid for rules artifacts" }];
2038
+ }
2039
+ if (artifact.kind !== "file") {
2040
+ issues.push({ artifact, message: "Codex command-policy rules must be files" });
2041
+ }
2042
+ if (!hasExtension(artifact, ".rules")) {
2043
+ issues.push({ artifact, message: "Codex command-policy rules must use the .rules extension" });
2044
+ }
2045
+ const content = await readUtf8Artifact(artifact, issues);
2046
+ if (content === void 0) return issues;
2047
+ if (!/\bprefix_rule\s*\(/.test(content)) {
2048
+ issues.push({ artifact, message: "Codex command-policy rules must contain at least one prefix_rule(...)" });
2049
+ }
2050
+ if (/\bprefix_rule\s*\(/.test(content) && !/\bpattern\s*=/.test(content)) {
2051
+ issues.push({ artifact, message: "Codex command-policy rules must define a pattern field" });
2052
+ }
2053
+ for (const match of content.matchAll(/\bdecision\s*=\s*["']([^"']+)["']/g)) {
2054
+ const decision = match[1];
2055
+ if (decision !== "allow" && decision !== "prompt" && decision !== "forbidden") {
2056
+ issues.push({ artifact, message: `Codex command-policy decision '${decision}' must be allow, prompt, or forbidden` });
2057
+ }
2058
+ }
2059
+ return issues;
2060
+ }
2061
+ function validateMarkdownRule(artifact, format) {
2062
+ const label = format === "copilot-instruction-rule" ? "Copilot instruction rules" : format === "claude-markdown-rule" ? "Claude markdown rules" : "Markdown rules";
2063
+ const issues = [];
2064
+ if (artifact.type !== "rules") {
2065
+ issues.push({ artifact, message: `${format} format is only valid for rules artifacts` });
2066
+ }
2067
+ if (artifact.kind !== "file") {
2068
+ issues.push({ artifact, message: `${label} must be files` });
2069
+ }
2070
+ if (!hasExtension(artifact, ".md") && !hasExtension(artifact, ".markdown")) {
2071
+ issues.push({ artifact, message: `${label} must use a Markdown extension` });
2072
+ }
2073
+ return issues;
2074
+ }
2075
+ async function validateOpenClawPlugin(artifact) {
2076
+ const issues = [];
2077
+ if (artifact.type !== "plugins") {
2078
+ return [{ artifact, message: "openclaw-plugin format is only valid for plugins artifacts" }];
2079
+ }
2080
+ if (artifact.kind !== "dir") {
2081
+ return [{ artifact, message: "OpenClaw plugins must be directory artifacts" }];
2082
+ }
2083
+ const manifestPaths = await openClawPluginManifestPaths(artifact);
2084
+ if (manifestPaths.length === 0) {
2085
+ return [{ artifact, message: "OpenClaw plugins must contain plugin.json or openclaw.plugin.json" }];
2086
+ }
2087
+ const parsed = await Promise.all(manifestPaths.map(async (manifestPath) => {
2088
+ const result = await parseOpenClawPluginManifest(manifestPath);
2089
+ if (!result.ok) issues.push({ artifact, message: result.message });
2090
+ return result;
2091
+ }));
2092
+ const names = new Set(parsed.filter((result) => result.ok).map((result) => result.name));
2093
+ if (names.size > 1) {
2094
+ issues.push({ artifact, message: "OpenClaw plugin descriptors must declare the same name" });
2095
+ }
2096
+ return issues;
2097
+ }
2098
+ async function openClawPluginManifestPaths(artifact) {
2099
+ const root = artifactPath(artifact);
2100
+ const candidates = [join7(root, "plugin.json"), join7(root, "openclaw.plugin.json")];
2101
+ const existing = await Promise.all(candidates.map(async (candidate) => await pathExists(candidate) ? candidate : void 0));
2102
+ return existing.filter((candidate) => candidate !== void 0);
2103
+ }
2104
+ async function parseOpenClawPluginManifest(manifestPath) {
2105
+ const manifestName = basename5(manifestPath);
2106
+ try {
2107
+ const parsed = JSON.parse(await readFile9(manifestPath, "utf8"));
2108
+ if (!isRecord3(parsed)) {
2109
+ return { ok: false, path: manifestPath, message: `OpenClaw ${manifestName} must be a JSON object` };
2110
+ }
2111
+ if (typeof parsed.name !== "string" || parsed.name.trim().length === 0) {
2112
+ return { ok: false, path: manifestPath, message: `OpenClaw ${manifestName} must declare a non-empty name` };
2113
+ }
2114
+ return { ok: true, path: manifestPath, name: parsed.name.trim() };
2115
+ } catch (error) {
2116
+ return { ok: false, path: manifestPath, message: `OpenClaw ${manifestName} must be valid JSON: ${errorMessage(error)}` };
2117
+ }
2118
+ }
2119
+ async function readUtf8Artifact(artifact, issues) {
2120
+ if (artifact.kind !== "file") return void 0;
2121
+ try {
2122
+ return await readFile9(artifactPath(artifact), "utf8");
2123
+ } catch (error) {
2124
+ issues.push({ artifact, message: `could not read artifact: ${errorMessage(error)}` });
2125
+ return void 0;
2126
+ }
2127
+ }
2128
+ async function parseJsonObjectArtifact(artifact) {
2129
+ if (artifact.kind !== "file") {
2130
+ return { ok: false, message: "merge artifacts must be JSON files" };
2131
+ }
2132
+ try {
2133
+ const parsed = JSON.parse(await readFile9(artifactPath(artifact), "utf8"));
2134
+ if (!isRecord3(parsed)) return { ok: false, message: "merge artifacts must contain a JSON object" };
2135
+ return { ok: true, value: parsed };
2136
+ } catch (error) {
2137
+ return { ok: false, message: `merge artifact must be valid JSON: ${errorMessage(error)}` };
2138
+ }
2139
+ }
2140
+ function extractMcpServers2(source) {
2141
+ const raw = isRecord3(source.mcpServers) ? source.mcpServers : source;
2142
+ const servers = {};
2143
+ for (const [name, value] of Object.entries(raw)) {
2144
+ if (isRecord3(value)) servers[name] = value;
2145
+ }
2146
+ return servers;
2147
+ }
2148
+ function artifactPath(artifact) {
2149
+ return artifact.stagedPath ?? artifact.sourcePath;
2150
+ }
2151
+ function artifactLabel(artifact) {
2152
+ const owner = artifact.packageName ? `${artifact.packageName}:` : "";
2153
+ return `${owner}${artifact.type}/${artifact.name}`;
2154
+ }
2155
+ function hasExtension(artifact, extension) {
2156
+ return basename5(artifact.name).toLowerCase().endsWith(extension) || basename5(artifactPath(artifact)).toLowerCase().endsWith(extension);
2157
+ }
2158
+ function isRecord3(value) {
2159
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2160
+ }
2161
+ function errorMessage(error) {
2162
+ return error instanceof Error ? error.message : String(error);
1464
2163
  }
1465
2164
 
1466
2165
  // src/install/plan.ts
1467
2166
  async function createCombinedInstallPlan(desiredArtifacts, adapter, targetRoot, manifest, transport = localTransport, options = {}) {
2167
+ const requestedInstallationType = options.installationType ?? defaultInstallationType;
2168
+ const installationType = resolveInstallationTypeForArtifacts(adapter, desiredArtifacts.map((artifact) => artifact.type), requestedInstallationType);
2169
+ const installRoot = installRootForArtifacts(adapter, targetRoot, installationType, desiredArtifacts.map((artifact) => artifact.type), transport.kind === "ssh");
2170
+ await validateArtifactsForInstall(desiredArtifacts, adapter, installationType);
1468
2171
  for (const artifact of desiredArtifacts) {
1469
2172
  if (artifact.meta.dependencyRole !== "root" && isGuardedMergeTarget(artifact.type)) {
1470
2173
  throw new Error(`Dependency-provided ${artifact.type} artifacts cannot be installed until per-subentry ownership exists: ${artifact.type}/${artifact.name}`);
@@ -1472,13 +2175,13 @@ async function createCombinedInstallPlan(desiredArtifacts, adapter, targetRoot,
1472
2175
  }
1473
2176
  const desired = [];
1474
2177
  for (const artifact of desiredArtifacts) {
1475
- const op = operationForArtifact(artifact, adapter, targetRoot, artifact.meta);
2178
+ const op = operationForArtifact(artifact, adapter, installRoot, installationType, artifact.meta);
1476
2179
  if (op) {
1477
2180
  desired.push(op);
1478
2181
  }
1479
2182
  }
1480
- await addProgrammaticOperations(desired, adapter, targetRoot);
1481
- return createPlanFromOperations(desired, adapter, targetRoot, manifest, transport, options);
2183
+ await addProgrammaticOperations(desired, adapter, installRoot);
2184
+ return createPlanFromOperations(desired, adapter, installRoot, manifest, transport, { ...options, installationType });
1482
2185
  }
1483
2186
  async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifest, transport, options) {
1484
2187
  const workspaceOwner = options.workspaceOwner;
@@ -1540,11 +2243,38 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
1540
2243
  }
1541
2244
  const currentHash = await transport.hashPath(op.destPath);
1542
2245
  if (!existing) {
1543
- operations.push({ ...op, action: "conflict", currentHash, reason: "destination exists but is not managed" });
2246
+ if (currentHash === op.desiredHash && options.forceConflict) {
2247
+ operations.push({
2248
+ ...op,
2249
+ action: "skip",
2250
+ currentHash,
2251
+ reason: "force adopting unmanaged destination with matching hash"
2252
+ });
2253
+ } else if (options.replaceConflict) {
2254
+ operations.push({
2255
+ ...op,
2256
+ action: "update",
2257
+ currentHash,
2258
+ reason: "force replacing unmanaged destination"
2259
+ });
2260
+ } else {
2261
+ operations.push({ ...op, action: "conflict", currentHash, reason: "destination exists but is not managed" });
2262
+ }
1544
2263
  continue;
1545
2264
  }
1546
2265
  if (currentHash !== existing.hash) {
1547
2266
  const composedFromDiff = changedComposedSelectors(op.composedFrom, existing.composedFrom);
2267
+ if (options.forceDrift) {
2268
+ operations.push({
2269
+ ...op,
2270
+ action: "update",
2271
+ currentHash,
2272
+ manifestHash: existing.hash,
2273
+ reason: reasonWithComposedDiff("force replacing drifted managed destination", op.composedFrom, existing.composedFrom),
2274
+ composedFromDiff
2275
+ });
2276
+ continue;
2277
+ }
1548
2278
  operations.push({
1549
2279
  ...op,
1550
2280
  action: "drift",
@@ -1571,7 +2301,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
1571
2301
  }
1572
2302
  for (const entry of effectiveEntries) {
1573
2303
  if (desired.has(entry.path)) continue;
1574
- const destPath = join5(targetRoot, entry.path);
2304
+ const destPath = join8(targetRoot, entry.path);
1575
2305
  if (!await transport.pathExists(destPath)) continue;
1576
2306
  const currentHash = await transport.hashPath(destPath);
1577
2307
  if (workspaceOwner && !entryOwnedByWorkspace(entry, workspaceOwner)) {
@@ -1592,7 +2322,8 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
1592
2322
  channel: entry.channel,
1593
2323
  packageName: entry.packageName,
1594
2324
  composedFrom: entry.composedFrom,
1595
- ...operationMetadataFromEntry(entry)
2325
+ ...operationMetadataFromEntry(entry),
2326
+ ...options.forceDrift ? { action: "remove", reason: "force removing drifted stale managed destination" } : {}
1596
2327
  });
1597
2328
  } else {
1598
2329
  operations.push({
@@ -1616,6 +2347,8 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
1616
2347
  operations.sort((a, b) => a.relativeDestPath.localeCompare(b.relativeDestPath));
1617
2348
  return {
1618
2349
  adapter: adapter.name,
2350
+ installationType: options.installationType ?? defaultInstallationType,
2351
+ stateKey: options.stateKey,
1619
2352
  targetRoot,
1620
2353
  operations,
1621
2354
  hasBlockingChanges: operations.some((op) => op.action === "drift" || op.action === "conflict"),
@@ -1678,7 +2411,7 @@ async function canStrictlyAdoptLegacyEntry(entry, op, targetRoot, transport) {
1678
2411
  if (entry.artifactType !== op.artifactType || entry.artifactName !== op.artifactName) return false;
1679
2412
  if (!op.desiredHash || entry.sourceHash !== op.desiredHash) return false;
1680
2413
  if (!packageIdentityMatches(entry, op)) return false;
1681
- const destPath = join5(targetRoot, entry.path);
2414
+ const destPath = join8(targetRoot, entry.path);
1682
2415
  if (!await transport.pathExists(destPath)) return false;
1683
2416
  return await transport.hashPath(destPath) === entry.hash;
1684
2417
  }
@@ -1808,7 +2541,7 @@ function keepForeignManifestEntryOperation(entry, targetRoot, workspaceOwner, op
1808
2541
  artifactType: entry.artifactType,
1809
2542
  artifactName: entry.artifactName,
1810
2543
  kind: entry.kind,
1811
- destPath: operation?.destPath ?? join5(targetRoot, entry.path),
2544
+ destPath: operation?.destPath ?? join8(targetRoot, entry.path),
1812
2545
  relativeDestPath: entry.path,
1813
2546
  desiredHash: entry.sourceHash,
1814
2547
  currentHash: currentHash ?? operation?.currentHash ?? entry.hash,
@@ -1830,11 +2563,18 @@ function normalizeOperationOwners(op) {
1830
2563
  function isGuardedMergeTarget(type) {
1831
2564
  return type === "mcp" || type === "hooks" || type === "settings" || type === "plugins";
1832
2565
  }
1833
- function operationForArtifact(artifact, adapter, targetRoot, meta) {
1834
- const target = adapter.targets[artifact.type];
1835
- if (!target?.enabled) return void 0;
2566
+ function operationForArtifact(artifact, adapter, targetRoot, installationType, meta) {
2567
+ if (artifact.type === "fragments") return void 0;
2568
+ const target = targetMappingForArtifact(adapter, artifact.type, installationType);
2569
+ if (!target?.enabled) {
2570
+ const supported = Object.keys(adapter.targets[artifact.type] ?? {});
2571
+ const suffix = supported.length > 0 ? ` Supported installation types: ${supported.join(", ")}` : "";
2572
+ throw new Error(`Adapter ${adapter.name} does not support ${artifact.type}/${artifact.name} for installation type '${installationType}'.${suffix}`);
2573
+ }
1836
2574
  const metadata = operationMetadataFromDesired(artifact, meta);
1837
- const installName = metadata.installName ?? artifact.name;
2575
+ const rawInstallName = metadata.installName ?? artifact.name;
2576
+ assertSafeInstallName(rawInstallName, `${artifact.type}/${artifact.name}`);
2577
+ const installName = semanticInstallName(artifact, target.semantic, rawInstallName);
1838
2578
  assertSafeInstallName(installName, `${artifact.type}/${artifact.name}`);
1839
2579
  if (artifact.type === "plugins" && target.semantic === "openclaw-plugin") {
1840
2580
  const sourcePath = artifact.stagedPath ?? artifact.sourcePath;
@@ -1855,7 +2595,26 @@ function operationForArtifact(artifact, adapter, targetRoot, meta) {
1855
2595
  ...metadata
1856
2596
  };
1857
2597
  }
1858
- const destPath = artifact.type === "instructions" || artifact.type === "settings" || isFileTarget(target.dest) ? join5(targetRoot, target.dest) : join5(targetRoot, target.dest, installName);
2598
+ if (artifact.type === "subagents" && target.semantic === "codex-subagent") {
2599
+ const destPath2 = join8(targetRoot, target.dest, `${installName.replace(/\.toml$/i, "")}.toml`);
2600
+ return {
2601
+ action: "create",
2602
+ artifactType: artifact.type,
2603
+ artifactName: artifact.name,
2604
+ kind: "file",
2605
+ sourcePath: artifact.stagedPath ?? artifact.sourcePath,
2606
+ destPath: destPath2,
2607
+ relativeDestPath: relative2(targetRoot, destPath2).replaceAll("\\", "/"),
2608
+ desiredHash: artifact.hash,
2609
+ reason: "destination missing",
2610
+ channel: artifact.channel ?? "managed",
2611
+ packageName: artifact.packageName,
2612
+ composedFrom: metadata.composedFrom,
2613
+ ...metadata,
2614
+ installName: installName.replace(/\.toml$/i, "")
2615
+ };
2616
+ }
2617
+ const destPath = artifact.type === "instructions" || artifact.type === "settings" || isFileTarget(target.dest) ? join8(targetRoot, target.dest) : join8(targetRoot, target.dest, installName);
1859
2618
  return {
1860
2619
  action: "create",
1861
2620
  artifactType: artifact.type,
@@ -1870,11 +2629,29 @@ function operationForArtifact(artifact, adapter, targetRoot, meta) {
1870
2629
  packageName: artifact.packageName,
1871
2630
  mergeStrategy: target.merge,
1872
2631
  composedFrom: metadata.composedFrom,
1873
- ...metadata
2632
+ ...metadata,
2633
+ installName
1874
2634
  };
1875
2635
  }
1876
- function isFileTarget(dest) {
1877
- return /\.(json|jsonc|toml|md)$/i.test(dest);
2636
+ function semanticInstallName(artifact, semantic, installName) {
2637
+ if (artifact.type === "rules" && semantic === "copilot-instruction") {
2638
+ return withExtension(installName, ".instructions.md", [".instructions.md", ".md"]);
2639
+ }
2640
+ if (artifact.type === "commands" && semantic === "copilot-prompt") {
2641
+ return withExtension(installName, ".prompt.md", [".prompt.md", ".md"]);
2642
+ }
2643
+ if (artifact.type === "subagents" && semantic === "copilot-agent") {
2644
+ return withExtension(installName, ".agent.md", [".agent.md", ".md"]);
2645
+ }
2646
+ return installName;
2647
+ }
2648
+ function withExtension(name, targetExtension, knownExtensions) {
2649
+ const match = knownExtensions.find((extension) => name.toLowerCase().endsWith(extension.toLowerCase()));
2650
+ const base = match ? name.slice(0, -match.length) : name;
2651
+ return `${base}${targetExtension}`;
2652
+ }
2653
+ function isFileTarget(dest) {
2654
+ return /\.(json|jsonc|toml|md)$/i.test(dest);
1878
2655
  }
1879
2656
  function reasonWithComposedDiff(reason, desired, current) {
1880
2657
  const changed = changedComposedSelectors(desired, current);
@@ -1907,13 +2684,19 @@ function summarizePlan(plan) {
1907
2684
  }
1908
2685
  return summary;
1909
2686
  }
2687
+ function isPendingInstallAction(action) {
2688
+ return action !== "skip" && action !== "keep";
2689
+ }
2690
+ function isPendingInstallOperation(operation) {
2691
+ return isPendingInstallAction(operation.action);
2692
+ }
1910
2693
 
1911
2694
  // src/install/uninstall.ts
1912
- import { join as join6 } from "path";
2695
+ import { join as join9 } from "path";
1913
2696
  async function createUninstallPlan(manifest, transport = localTransport) {
1914
2697
  const operations = [];
1915
2698
  for (const entry of manifest.entries) {
1916
- const destPath = join6(manifest.targetRoot, entry.path);
2699
+ const destPath = join9(manifest.targetRoot, entry.path);
1917
2700
  if (!await transport.pathExists(destPath)) continue;
1918
2701
  const currentHash = await transport.hashPath(destPath);
1919
2702
  if (currentHash !== entry.hash) {
@@ -1959,6 +2742,8 @@ async function createUninstallPlan(manifest, transport = localTransport) {
1959
2742
  operations.sort((a, b) => a.relativeDestPath.localeCompare(b.relativeDestPath));
1960
2743
  return {
1961
2744
  adapter: manifest.adapter,
2745
+ installationType: "installationType" in manifest ? manifest.installationType : defaultInstallationType,
2746
+ stateKey: "stateKey" in manifest ? manifest.stateKey : void 0,
1962
2747
  targetRoot: manifest.targetRoot,
1963
2748
  operations,
1964
2749
  hasBlockingChanges: operations.some((operation) => operation.action === "conflict"),
@@ -1967,13 +2752,15 @@ async function createUninstallPlan(manifest, transport = localTransport) {
1967
2752
  };
1968
2753
  }
1969
2754
  async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter, transport = localTransport, options = {}) {
1970
- const desiredPlan = await createCombinedInstallPlan(remainingDesired, adapter, manifest.targetRoot, void 0, transport);
2755
+ const installationType = "installationType" in manifest ? manifest.installationType : defaultInstallationType;
2756
+ const stateKey = "stateKey" in manifest ? manifest.stateKey : void 0;
2757
+ const desiredPlan = await createCombinedInstallPlan(remainingDesired, adapter, manifest.targetRoot, void 0, transport, { installationType, stateKey });
1971
2758
  const ownersByPath = new Map(
1972
2759
  desiredPlan.operations.filter((operation) => operation.owners?.length).map((operation) => [operation.relativeDestPath, operation.owners ?? []])
1973
2760
  );
1974
2761
  const operations = [];
1975
2762
  for (const entry of manifest.entries) {
1976
- const destPath = join6(manifest.targetRoot, entry.path);
2763
+ const destPath = join9(manifest.targetRoot, entry.path);
1977
2764
  if (!await transport.pathExists(destPath)) continue;
1978
2765
  const currentHash = await transport.hashPath(destPath);
1979
2766
  const remainingOwners = ownersByPath.get(entry.path) ?? [];
@@ -2044,6 +2831,8 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
2044
2831
  operations.sort((a, b) => a.relativeDestPath.localeCompare(b.relativeDestPath));
2045
2832
  return {
2046
2833
  adapter: manifest.adapter,
2834
+ installationType,
2835
+ stateKey,
2047
2836
  targetRoot: manifest.targetRoot,
2048
2837
  operations,
2049
2838
  hasBlockingChanges: operations.some((operation) => operation.action === "conflict"),
@@ -2091,7 +2880,7 @@ var channelLabels = {
2091
2880
  ejected: "EJECTED"
2092
2881
  };
2093
2882
  function formatPlan(plan) {
2094
- const lines = [`Plan for ${plan.adapter} at ${plan.targetRoot}`];
2883
+ const lines = [`Plan for ${plan.adapter}/${plan.installationType} at ${plan.targetRoot}`];
2095
2884
  if (plan.migrationReport) {
2096
2885
  const dropped = plan.migrationReport.dropped.length > 0 ? `; dropped unmanaged ${plan.migrationReport.dropped.join(", ")}` : "";
2097
2886
  lines.push(`MIGRATE adopted ${plan.migrationReport.adopted} legacy entries${dropped}`);
@@ -2138,6 +2927,9 @@ function formatGraphPlan(result) {
2138
2927
  for (const decision of result.bundle.graphLock.canonical.namespacing) {
2139
2928
  lines.push(`NAMESPACE ${decision.graphNodeId}:${decision.type}/${decision.name} -> ${decision.type}/${decision.installName} (${decision.reason})`);
2140
2929
  }
2930
+ for (const decision of result.bundle.graphLock.canonical.overrides) {
2931
+ lines.push(`OVERRIDE ${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`);
2932
+ }
2141
2933
  if (result.graphDiff.length > 0) {
2142
2934
  lines.push("Graph diff:");
2143
2935
  lines.push(...result.graphDiff);
@@ -2175,6 +2967,9 @@ function formatLockDependencyTree(lock) {
2175
2967
  for (const decision of lock.canonical.namespacing) {
2176
2968
  lines.push(`NAMESPACE ${decision.graphNodeId}:${decision.type}/${decision.name} -> ${decision.type}/${decision.installName} (${decision.reason})`);
2177
2969
  }
2970
+ for (const decision of lock.canonical.overrides) {
2971
+ lines.push(`OVERRIDE ${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`);
2972
+ }
2178
2973
  return lines.join("\n");
2179
2974
  }
2180
2975
  function formatDepsWhy(lock, manifest, query) {
@@ -2197,6 +2992,9 @@ function formatDepsWhy(lock, manifest, query) {
2197
2992
  }
2198
2993
  const namespace = lock.canonical.namespacing.find((decision) => decision.graphNodeId === match.graphNodeId && decision.type === match.type && decision.name === match.name);
2199
2994
  lines.push(namespace ? `NAME ${namespace.reason}: ${namespace.type}/${namespace.name} -> ${namespace.type}/${namespace.installName}` : `NAME plain: ${match.type}/${match.name}`);
2995
+ for (const override of lock.canonical.overrides.filter((decision) => decision.graphNodeId === match.graphNodeId && decision.type === match.type && decision.name === match.name)) {
2996
+ lines.push(`OVERRIDE replaces ${override.overriddenGraphNodeId}:${override.type}/${override.name} via ${override.rootId}`);
2997
+ }
2200
2998
  return lines.join("\n");
2201
2999
  }
2202
3000
  function formatSelected(selected, reasons) {
@@ -2254,15 +3052,15 @@ function ownerChains(lock, nodeId) {
2254
3052
  }
2255
3053
 
2256
3054
  // src/source/git.ts
2257
- import { execFile as execFile3 } from "child_process";
2258
- import { cp as cp2, mkdir as mkdir6, rename as rename3, rm as rm4, writeFile as writeFile7 } from "fs/promises";
2259
- import { homedir } from "os";
2260
- import { basename as basename4, dirname as dirname7, join as join9, resolve as resolve6 } from "path";
2261
- import { promisify as promisify3 } from "util";
3055
+ import { execFile as execFile4 } from "child_process";
3056
+ import { cp as cp2, mkdir as mkdir8, rename as rename3, rm as rm4, writeFile as writeFile9 } from "fs/promises";
3057
+ import { homedir as homedir2 } from "os";
3058
+ import { basename as basename7, dirname as dirname9, join as join12, resolve as resolve6 } from "path";
3059
+ import { promisify as promisify4 } from "util";
2262
3060
 
2263
3061
  // src/model/package.ts
2264
- import { readFile as readFile7 } from "fs/promises";
2265
- import { join as join7 } from "path";
3062
+ import { readFile as readFile10 } from "fs/promises";
3063
+ import { join as join10 } from "path";
2266
3064
  import { parse as parse2, printParseErrorCode as printParseErrorCode2 } from "jsonc-parser";
2267
3065
  import { z as z5 } from "zod";
2268
3066
  var legacyArtifactTypeSchema = z5.enum([
@@ -2279,10 +3077,12 @@ var legacyArtifactTypeSchema = z5.enum([
2279
3077
  var runtimeListSchema = z5.array(z5.string().min(1));
2280
3078
  var packageProvideBaseSchema = z5.object({
2281
3079
  path: z5.string().min(1),
3080
+ format: artifactFormatSchema.optional(),
2282
3081
  assets: z5.array(packageAssetSchema).optional(),
2283
3082
  required: z5.boolean().optional()
2284
3083
  });
2285
3084
  var packageItemSchema = z5.object({
3085
+ format: artifactFormatSchema.optional(),
2286
3086
  requires: z5.array(packageItemRequireSchema).optional(),
2287
3087
  compose: z5.array(packageComposeEntrySchema).optional(),
2288
3088
  runtimes: runtimeListSchema.optional()
@@ -2337,7 +3137,7 @@ var packageManifestNames = [...openPackManifestNames, ...legacyPackageManifestNa
2337
3137
  var warnedLegacyManifestPaths = /* @__PURE__ */ new Set();
2338
3138
  async function findPackageManifestPath(root, options = {}) {
2339
3139
  for (const name of packageManifestNames) {
2340
- const candidate = join7(root, name);
3140
+ const candidate = join10(root, name);
2341
3141
  if (!await pathExists(candidate)) continue;
2342
3142
  if (isLegacyPackageManifestName(name) && options.warnLegacy !== false && !warnedLegacyManifestPaths.has(candidate)) {
2343
3143
  warnedLegacyManifestPaths.add(candidate);
@@ -2350,7 +3150,7 @@ async function findPackageManifestPath(root, options = {}) {
2350
3150
  async function readPackageManifest(root) {
2351
3151
  const path = await findPackageManifestPath(root);
2352
3152
  if (!path) return void 0;
2353
- const content = await readFile7(path, "utf8");
3153
+ const content = await readFile10(path, "utf8");
2354
3154
  const errors = [];
2355
3155
  const parsed = parse2(content, errors, { allowTrailingComma: true, disallowComments: false });
2356
3156
  if (errors.length > 0) {
@@ -2360,7 +3160,7 @@ async function readPackageManifest(root) {
2360
3160
  return parsePackageManifest(parsed, path);
2361
3161
  }
2362
3162
  function parsePackageManifest(parsed, path = "package manifest") {
2363
- if (!isRecord3(parsed)) {
3163
+ if (!isRecord4(parsed)) {
2364
3164
  throw new Error(`Invalid package manifest ${path}: expected an object`);
2365
3165
  }
2366
3166
  if (parsed.schemaVersion === 1) {
@@ -2388,9 +3188,9 @@ function v1OpenPackViolations(manifest) {
2388
3188
  }
2389
3189
  const provides = Array.isArray(manifest.provides) ? manifest.provides : [];
2390
3190
  for (const [index, provide] of provides.entries()) {
2391
- if (!isRecord3(provide)) continue;
3191
+ if (!isRecord4(provide)) continue;
2392
3192
  if (provide.type === "fragments") violations.push(`provides[${index}].type=fragments`);
2393
- for (const key of ["items", "compose", "runtimes"]) {
3193
+ for (const key of ["format", "items", "compose", "runtimes"]) {
2394
3194
  if (Object.prototype.hasOwnProperty.call(provide, key)) violations.push(`provides[${index}].${key}`);
2395
3195
  }
2396
3196
  }
@@ -2399,13 +3199,14 @@ function v1OpenPackViolations(manifest) {
2399
3199
  function isLegacyPackageManifestName(name) {
2400
3200
  return legacyPackageManifestNames.includes(name);
2401
3201
  }
2402
- function isRecord3(value) {
3202
+ function isRecord4(value) {
2403
3203
  return typeof value === "object" && value !== null && !Array.isArray(value);
2404
3204
  }
2405
3205
 
2406
3206
  // src/source/local.ts
3207
+ import { createHash as createHash3 } from "crypto";
2407
3208
  import { readdir, stat as stat3 } from "fs/promises";
2408
- import { basename as basename3, join as join8, resolve as resolve5 } from "path";
3209
+ import { basename as basename6, join as join11, relative as relative3, resolve as resolve5 } from "path";
2409
3210
  var LocalSourceDriver = class {
2410
3211
  name = "local";
2411
3212
  async resolve(source) {
@@ -2425,7 +3226,7 @@ var LocalSourceDriver = class {
2425
3226
  packageName: manifest?.name,
2426
3227
  packageVersion: manifest?.version,
2427
3228
  mode: "pinned",
2428
- sourceHash: await hashPath(resolvedPath)
3229
+ sourceHash: await hashLocalSource(resolvedPath, manifest)
2429
3230
  };
2430
3231
  }
2431
3232
  async list(resolved) {
@@ -2435,29 +3236,29 @@ var LocalSourceDriver = class {
2435
3236
  }
2436
3237
  const artifacts = [];
2437
3238
  const root = resolved.resolvedPath;
2438
- const instructions = await firstExisting([join8(root, "instructions.md"), join8(root, "AGENTS.md")]);
3239
+ const instructions = await firstExisting([join11(root, "instructions.md"), join11(root, "AGENTS.md")]);
2439
3240
  if (instructions) {
2440
3241
  artifacts.push({
2441
3242
  type: "instructions",
2442
- name: basename3(instructions),
3243
+ name: basename6(instructions),
2443
3244
  sourcePath: instructions,
2444
- relativePath: basename3(instructions),
3245
+ relativePath: basename6(instructions),
2445
3246
  kind: "file",
2446
3247
  hash: await hashPath(instructions),
2447
3248
  packageName: resolved.packageName,
2448
3249
  channel: "managed"
2449
3250
  });
2450
3251
  }
2451
- const rulesDir = join8(root, "rules");
3252
+ const rulesDir = join11(root, "rules");
2452
3253
  if (await pathExists(rulesDir)) {
2453
3254
  for (const entry of await sortedDirEntries(rulesDir)) {
2454
- const full = join8(rulesDir, entry.name);
3255
+ const full = join11(rulesDir, entry.name);
2455
3256
  if (entry.isFile()) {
2456
3257
  artifacts.push({
2457
3258
  type: "rules",
2458
3259
  name: entry.name,
2459
3260
  sourcePath: full,
2460
- relativePath: join8("rules", entry.name),
3261
+ relativePath: join11("rules", entry.name),
2461
3262
  kind: "file",
2462
3263
  hash: await hashPath(full),
2463
3264
  packageName: resolved.packageName,
@@ -2466,16 +3267,16 @@ var LocalSourceDriver = class {
2466
3267
  }
2467
3268
  }
2468
3269
  }
2469
- const fragmentsDir = join8(root, "fragments");
3270
+ const fragmentsDir = join11(root, "fragments");
2470
3271
  if (await pathExists(fragmentsDir)) {
2471
3272
  for (const entry of await sortedDirEntries(fragmentsDir)) {
2472
- const full = join8(fragmentsDir, entry.name);
3273
+ const full = join11(fragmentsDir, entry.name);
2473
3274
  if (entry.isFile()) {
2474
3275
  artifacts.push({
2475
3276
  type: "fragments",
2476
3277
  name: entry.name,
2477
3278
  sourcePath: full,
2478
- relativePath: join8("fragments", entry.name),
3279
+ relativePath: join11("fragments", entry.name),
2479
3280
  kind: "file",
2480
3281
  hash: await hashPath(full),
2481
3282
  packageName: resolved.packageName,
@@ -2484,16 +3285,16 @@ var LocalSourceDriver = class {
2484
3285
  }
2485
3286
  }
2486
3287
  }
2487
- const skillsDir = join8(root, "skills");
3288
+ const skillsDir = join11(root, "skills");
2488
3289
  if (await pathExists(skillsDir)) {
2489
3290
  for (const entry of await sortedDirEntries(skillsDir)) {
2490
- const full = join8(skillsDir, entry.name);
3291
+ const full = join11(skillsDir, entry.name);
2491
3292
  if (entry.isDirectory()) {
2492
3293
  artifacts.push({
2493
3294
  type: "skills",
2494
3295
  name: entry.name,
2495
3296
  sourcePath: full,
2496
- relativePath: join8("skills", entry.name),
3297
+ relativePath: join11("skills", entry.name),
2497
3298
  kind: "dir",
2498
3299
  hash: await hashPath(full),
2499
3300
  packageName: resolved.packageName,
@@ -2504,7 +3305,7 @@ var LocalSourceDriver = class {
2504
3305
  type: "skills",
2505
3306
  name: entry.name.replace(/\.md$/, ""),
2506
3307
  sourcePath: full,
2507
- relativePath: join8("skills", entry.name),
3308
+ relativePath: join11("skills", entry.name),
2508
3309
  kind: "file",
2509
3310
  hash: await hashPath(full),
2510
3311
  packageName: resolved.packageName,
@@ -2514,7 +3315,7 @@ var LocalSourceDriver = class {
2514
3315
  }
2515
3316
  }
2516
3317
  for (const type of ["commands", "subagents", "mcp", "hooks", "settings", "plugins"]) {
2517
- const dir = join8(root, type);
3318
+ const dir = join11(root, type);
2518
3319
  if (!await pathExists(dir)) continue;
2519
3320
  artifacts.push(...await listGenericArtifacts(type, dir, type, resolved.packageName));
2520
3321
  }
@@ -2530,7 +3331,7 @@ var LocalSourceDriver = class {
2530
3331
  findings.push({ level: "warning", message: "No instructions.md or AGENTS.md found", path: resolved.resolvedPath });
2531
3332
  }
2532
3333
  for (const artifact of artifacts.filter((item) => item.type === "skills" && item.kind === "dir")) {
2533
- if (!await pathExists(join8(artifact.sourcePath, "SKILL.md"))) {
3334
+ if (!await pathExists(join11(artifact.sourcePath, "SKILL.md"))) {
2534
3335
  findings.push({ level: "warning", message: `Skill directory has no SKILL.md: ${artifact.name}`, path: artifact.sourcePath });
2535
3336
  }
2536
3337
  }
@@ -2543,6 +3344,26 @@ var LocalSourceDriver = class {
2543
3344
  return resolved;
2544
3345
  }
2545
3346
  };
3347
+ async function hashLocalSource(root, manifest) {
3348
+ if (!manifest) return hashPath(root);
3349
+ const hash = createHash3("sha256").update("local-openpack\0");
3350
+ const manifestPath = await findPackageManifestPath(root, { warnLegacy: false });
3351
+ if (manifestPath) {
3352
+ hash.update("manifest\0");
3353
+ hash.update(relative3(root, manifestPath).replaceAll("\\", "/")).update("\0");
3354
+ hash.update(await hashPath(manifestPath)).update("\0");
3355
+ }
3356
+ const provides = [...manifest.provides].sort((a, b) => `${a.type}\0${a.path}`.localeCompare(`${b.type}\0${b.path}`));
3357
+ for (const provide of provides) {
3358
+ const full = join11(root, provide.path);
3359
+ if (!await pathExists(full)) continue;
3360
+ hash.update("provide\0");
3361
+ hash.update(provide.type).update("\0");
3362
+ hash.update(provide.path.replaceAll("\\", "/")).update("\0");
3363
+ hash.update(await hashPath(full)).update("\0");
3364
+ }
3365
+ return hash.digest("hex");
3366
+ }
2546
3367
  async function firstExisting(paths) {
2547
3368
  for (const path of paths) {
2548
3369
  if (await pathExists(path)) return path;
@@ -2557,27 +3378,27 @@ async function listFromManifest(root, packageName) {
2557
3378
  if (!manifest) return [];
2558
3379
  const artifacts = [];
2559
3380
  for (const provide of manifest.provides) {
2560
- const full = join8(root, provide.path);
3381
+ const full = join11(root, provide.path);
2561
3382
  if (!await pathExists(full)) continue;
2562
3383
  const stats = await stat3(full);
2563
3384
  if (provide.type === "instructions") {
2564
3385
  if (stats.isFile()) {
2565
- artifacts.push(await artifactForFile(provide.type, basename3(full), full, provide.path, packageName, provide, manifest, basename3(full)));
3386
+ artifacts.push(await artifactForFile(provide.type, basename6(full), full, provide.path, packageName, provide, manifest, basename6(full)));
2566
3387
  }
2567
3388
  continue;
2568
3389
  }
2569
3390
  if (stats.isDirectory()) {
2570
3391
  for (const entry of await sortedDirEntries(full)) {
2571
- const child = join8(full, entry.name);
3392
+ const child = join11(full, entry.name);
2572
3393
  if ((provide.type === "skills" || provide.type === "plugins" || provide.type === "subagents") && entry.isDirectory()) {
2573
- artifacts.push(await artifactForDir(provide.type, entry.name, child, join8(provide.path, entry.name), packageName, provide, manifest, entry.name));
3394
+ artifacts.push(await artifactForDir(provide.type, entry.name, child, join11(provide.path, entry.name), packageName, provide, manifest, entry.name));
2574
3395
  } else if (entry.isFile()) {
2575
3396
  const name = provide.type === "rules" && entry.name.endsWith(".md") ? entry.name : entry.name;
2576
- artifacts.push(await artifactForFile(provide.type, name, child, join8(provide.path, entry.name), packageName, provide, manifest, name));
3397
+ artifacts.push(await artifactForFile(provide.type, name, child, join11(provide.path, entry.name), packageName, provide, manifest, name));
2577
3398
  }
2578
3399
  }
2579
3400
  } else if (stats.isFile()) {
2580
- artifacts.push(await artifactForFile(provide.type, basename3(full), full, provide.path, packageName, provide, manifest, basename3(full)));
3401
+ artifacts.push(await artifactForFile(provide.type, basename6(full), full, provide.path, packageName, provide, manifest, basename6(full)));
2581
3402
  }
2582
3403
  }
2583
3404
  return artifacts;
@@ -2585,11 +3406,11 @@ async function listFromManifest(root, packageName) {
2585
3406
  async function listGenericArtifacts(type, dir, relativeRoot, packageName) {
2586
3407
  const artifacts = [];
2587
3408
  for (const entry of await sortedDirEntries(dir)) {
2588
- const full = join8(dir, entry.name);
3409
+ const full = join11(dir, entry.name);
2589
3410
  if (entry.isDirectory()) {
2590
- artifacts.push(await artifactForDir(type, entry.name, full, join8(relativeRoot, entry.name), packageName));
3411
+ artifacts.push(await artifactForDir(type, entry.name, full, join11(relativeRoot, entry.name), packageName));
2591
3412
  } else if (entry.isFile()) {
2592
- artifacts.push(await artifactForFile(type, entry.name, full, join8(relativeRoot, entry.name), packageName));
3413
+ artifacts.push(await artifactForFile(type, entry.name, full, join11(relativeRoot, entry.name), packageName));
2593
3414
  }
2594
3415
  }
2595
3416
  return artifacts;
@@ -2603,6 +3424,7 @@ async function artifactForFile(type, name, sourcePath, relativePath, packageName
2603
3424
  relativePath,
2604
3425
  kind: "file",
2605
3426
  hash: await hashPath(sourcePath),
3427
+ format: item.format ?? provide?.format,
2606
3428
  packageName,
2607
3429
  channel: "managed",
2608
3430
  assets: provide?.assets,
@@ -2621,6 +3443,7 @@ async function artifactForDir(type, name, sourcePath, relativePath, packageName,
2621
3443
  relativePath,
2622
3444
  kind: "dir",
2623
3445
  hash: await hashPath(sourcePath),
3446
+ format: item.format ?? provide?.format,
2624
3447
  packageName,
2625
3448
  channel: "managed",
2626
3449
  assets: provide?.assets,
@@ -2634,7 +3457,7 @@ function itemMetadata(provide, itemName) {
2634
3457
  if (!provide || !("items" in provide) || !provide.items || !itemName) return {};
2635
3458
  const item = provide.items[itemName];
2636
3459
  if (!item) return {};
2637
- return { requires: item.requires, compose: item.compose, runtimes: item.runtimes };
3460
+ return { format: item.format, requires: item.requires, compose: item.compose, runtimes: item.runtimes };
2638
3461
  }
2639
3462
  function provideRuntimes(provide) {
2640
3463
  return provide && "runtimes" in provide ? provide.runtimes : void 0;
@@ -2644,7 +3467,7 @@ function manifestRuntimes(manifest) {
2644
3467
  }
2645
3468
 
2646
3469
  // src/source/git.ts
2647
- var execFileAsync3 = promisify3(execFile3);
3470
+ var execFileAsync4 = promisify4(execFile4);
2648
3471
  var GitSourceDriver = class {
2649
3472
  name = "git";
2650
3473
  local = new LocalSourceDriver();
@@ -2665,8 +3488,8 @@ var GitSourceDriver = class {
2665
3488
  async fetch(resolved) {
2666
3489
  return withFilesystemLock(`${resolved.resolvedPath}.lock`, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
2667
3490
  const parsed = parseGitSource(resolved.source);
2668
- await mkdir6(resolve6(resolved.resolvedPath, ".."), { recursive: true });
2669
- if (!await pathExists(join9(resolved.resolvedPath, ".git"))) {
3491
+ await mkdir8(resolve6(resolved.resolvedPath, ".."), { recursive: true });
3492
+ if (!await pathExists(join12(resolved.resolvedPath, ".git"))) {
2670
3493
  if (resolved.frozenLock) {
2671
3494
  throw new Error(`Frozen lock requires cached git checkout at ${resolved.resolvedPath}`);
2672
3495
  }
@@ -2733,20 +3556,20 @@ function parseGitSource(source) {
2733
3556
  throw new Error(`Invalid git source: ${source}`);
2734
3557
  }
2735
3558
  function cachePathFor(url, cacheRoot) {
2736
- const root = cacheRoot ? resolve6(cacheRoot) : join9(homedir(), ".agentwheel", "cache");
3559
+ const root = cacheRoot ? resolve6(cacheRoot) : join12(homedir2(), ".agentwheel", "cache");
2737
3560
  const slug2 = url.replace(/^[a-z]+:\/\//i, "").replace(/\.git$/i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
2738
- return join9(root, slug2 || basename4(url));
3561
+ return join12(root, slug2 || basename7(url));
2739
3562
  }
2740
3563
  async function git(args) {
2741
- return execFileAsync3("git", args, { maxBuffer: 1024 * 1024 * 10 });
3564
+ return execFileAsync4("git", args, { maxBuffer: 1024 * 1024 * 10 });
2742
3565
  }
2743
3566
  async function snapshotCheckout(checkoutPath, commit) {
2744
- const snapshotPath = join9(dirname7(checkoutPath), `${basename4(checkoutPath)}-${commit.slice(0, 12)}`);
3567
+ const snapshotPath = join12(dirname9(checkoutPath), `${basename7(checkoutPath)}-${commit.slice(0, 12)}`);
2745
3568
  if (await pathExists(snapshotPath)) return snapshotPath;
2746
- const tempPath = join9(dirname7(checkoutPath), `${basename4(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
3569
+ const tempPath = join12(dirname9(checkoutPath), `${basename7(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
2747
3570
  await rm4(tempPath, { recursive: true, force: true });
2748
3571
  await cp2(checkoutPath, tempPath, { recursive: true, dereference: true });
2749
- await rm4(join9(tempPath, ".git"), { recursive: true, force: true });
3572
+ await rm4(join12(tempPath, ".git"), { recursive: true, force: true });
2750
3573
  try {
2751
3574
  await rename3(tempPath, snapshotPath);
2752
3575
  } catch (error) {
@@ -2757,12 +3580,12 @@ async function snapshotCheckout(checkoutPath, commit) {
2757
3580
  return snapshotPath;
2758
3581
  }
2759
3582
  async function withFilesystemLock(lockPath, timeoutMs, fn) {
2760
- await mkdir6(dirname7(lockPath), { recursive: true });
3583
+ await mkdir8(dirname9(lockPath), { recursive: true });
2761
3584
  const started = Date.now();
2762
3585
  while (true) {
2763
3586
  try {
2764
- await mkdir6(lockPath);
2765
- await writeFile7(join9(lockPath, "owner.json"), JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
3587
+ await mkdir8(lockPath);
3588
+ await writeFile9(join12(lockPath, "owner.json"), JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
2766
3589
  break;
2767
3590
  } catch (error) {
2768
3591
  if (!isAlreadyExists2(error)) throw error;
@@ -2783,14 +3606,14 @@ function isAlreadyExists2(error) {
2783
3606
  }
2784
3607
 
2785
3608
  // src/source/skillkit.ts
2786
- import { cp as cp3, mkdir as mkdir7, readFile as readFile8, rm as rm5 } from "fs/promises";
2787
- import { homedir as homedir2 } from "os";
2788
- import { basename as basename6, dirname as dirname9, join as join11, resolve as resolve7 } from "path";
3609
+ import { cp as cp3, mkdir as mkdir9, readFile as readFile11, rm as rm5 } from "fs/promises";
3610
+ import { homedir as homedir3 } from "os";
3611
+ import { basename as basename9, dirname as dirname11, join as join14, resolve as resolve7 } from "path";
2789
3612
  import * as defaultSkillKit from "@skillkit/core";
2790
3613
 
2791
3614
  // src/source/skill-artifacts.ts
2792
3615
  import { readdir as readdir2, stat as stat4 } from "fs/promises";
2793
- import { basename as basename5, dirname as dirname8, extname as extname2, join as join10 } from "path";
3616
+ import { basename as basename8, dirname as dirname10, extname as extname2, join as join13 } from "path";
2794
3617
  async function artifactsFromSkillPaths(paths, packageName) {
2795
3618
  const artifacts = [];
2796
3619
  const seen = /* @__PURE__ */ new Set();
@@ -2812,28 +3635,28 @@ async function discoverSkillPaths(root) {
2812
3635
  async function artifactFromSkillPath(item, packageName) {
2813
3636
  const stats = await stat4(item.path);
2814
3637
  if (stats.isDirectory()) {
2815
- const skillMd = join10(item.path, "SKILL.md");
3638
+ const skillMd = join13(item.path, "SKILL.md");
2816
3639
  if (!await pathExists(skillMd)) return void 0;
2817
- const name = sanitizeSkillName(item.name ?? basename5(item.path));
3640
+ const name = sanitizeSkillName(item.name ?? basename8(item.path));
2818
3641
  return {
2819
3642
  type: "skills",
2820
3643
  name,
2821
3644
  sourcePath: item.path,
2822
- relativePath: join10("skills", name),
3645
+ relativePath: join13("skills", name),
2823
3646
  kind: "dir",
2824
3647
  hash: await hashPath(item.path),
2825
3648
  packageName,
2826
3649
  channel: "managed"
2827
3650
  };
2828
3651
  }
2829
- if (stats.isFile() && basename5(item.path).toLowerCase() === "skill.md") {
2830
- const dir = dirname8(item.path);
2831
- const name = sanitizeSkillName(item.name ?? basename5(dir));
3652
+ if (stats.isFile() && basename8(item.path).toLowerCase() === "skill.md") {
3653
+ const dir = dirname10(item.path);
3654
+ const name = sanitizeSkillName(item.name ?? basename8(dir));
2832
3655
  return {
2833
3656
  type: "skills",
2834
3657
  name,
2835
3658
  sourcePath: dir,
2836
- relativePath: join10("skills", name),
3659
+ relativePath: join13("skills", name),
2837
3660
  kind: "dir",
2838
3661
  hash: await hashPath(dir),
2839
3662
  packageName,
@@ -2841,12 +3664,12 @@ async function artifactFromSkillPath(item, packageName) {
2841
3664
  };
2842
3665
  }
2843
3666
  if (stats.isFile() && extname2(item.path).toLowerCase() === ".md") {
2844
- const name = sanitizeSkillName(item.name ?? basename5(item.path, ".md"));
3667
+ const name = sanitizeSkillName(item.name ?? basename8(item.path, ".md"));
2845
3668
  return {
2846
3669
  type: "skills",
2847
3670
  name,
2848
3671
  sourcePath: item.path,
2849
- relativePath: join10("skills", `${name}.md`),
3672
+ relativePath: join13("skills", `${name}.md`),
2850
3673
  kind: "file",
2851
3674
  hash: await hashPath(item.path),
2852
3675
  packageName,
@@ -2864,7 +3687,7 @@ async function walk(dir, paths) {
2864
3687
  }
2865
3688
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
2866
3689
  if (!entry.isDirectory() || entry.name === ".git" || entry.name === "node_modules") continue;
2867
- await walk(join10(dir, entry.name), paths);
3690
+ await walk(join13(dir, entry.name), paths);
2868
3691
  }
2869
3692
  }
2870
3693
  function sanitizeSkillName(name) {
@@ -2886,7 +3709,7 @@ var SkillKitSourceDriver = class {
2886
3709
  driver: this.name,
2887
3710
  source,
2888
3711
  resolvedPath,
2889
- packageName: `skillkit/${basename6(resolvedPath)}`,
3712
+ packageName: `skillkit/${basename9(resolvedPath)}`,
2890
3713
  mode: options.mode ?? "pinned",
2891
3714
  sourceHash: await hashPath(resolvedPath)
2892
3715
  };
@@ -2920,7 +3743,7 @@ var SkillKitSourceDriver = class {
2920
3743
  if (!provider?.clone) {
2921
3744
  throw new Error("SkillKit provider API unavailable or cannot resolve source. Expected @skillkit/core detectProvider().clone().");
2922
3745
  }
2923
- await mkdir7(dirname9(resolved.resolvedPath), { recursive: true });
3746
+ await mkdir9(dirname11(resolved.resolvedPath), { recursive: true });
2924
3747
  const result = await provider.clone(providerSpec, resolved.resolvedPath, {});
2925
3748
  if (!result.success || !result.path) {
2926
3749
  throw new Error(`SkillKit provider failed to fetch ${spec}: ${result.error ?? "unknown error"}`);
@@ -2964,9 +3787,9 @@ var SkillKitSourceDriver = class {
2964
3787
  throw new Error("SkillKit translateSkill API unavailable");
2965
3788
  }
2966
3789
  for (const skill of this.discover(resolved.resolvedPath)) {
2967
- const skillMd = join11(skill.path, "SKILL.md");
3790
+ const skillMd = join14(skill.path, "SKILL.md");
2968
3791
  if (await pathExists(skillMd)) {
2969
- this.core.translateSkill(await readFile8(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
3792
+ this.core.translateSkill(await readFile11(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
2970
3793
  }
2971
3794
  }
2972
3795
  return resolved;
@@ -2995,8 +3818,8 @@ function normalizeProviderSource(spec) {
2995
3818
  return spec;
2996
3819
  }
2997
3820
  function cachePathFor2(spec, cacheRoot) {
2998
- const root = cacheRoot ? resolve7(cacheRoot) : join11(homedir2(), ".agentwheel", "cache");
2999
- return join11(root, "skillkit", packageSlug(spec));
3821
+ const root = cacheRoot ? resolve7(cacheRoot) : join14(homedir3(), ".agentwheel", "cache");
3822
+ return join14(root, "skillkit", packageSlug(spec));
3000
3823
  }
3001
3824
  function packageSlug(spec) {
3002
3825
  return spec.replace(/^[a-z]+:\/\//i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "source";
@@ -3009,7 +3832,7 @@ function mapSeverity(severity) {
3009
3832
 
3010
3833
  // src/source/vercel-skills.ts
3011
3834
  import { stat as stat5 } from "fs/promises";
3012
- import { basename as basename7, join as join12, resolve as resolve8 } from "path";
3835
+ import { basename as basename10, join as join15, resolve as resolve8 } from "path";
3013
3836
  var VercelSkillsSourceDriver = class {
3014
3837
  name = "vercel-skills";
3015
3838
  git = new GitSourceDriver();
@@ -3024,7 +3847,7 @@ var VercelSkillsSourceDriver = class {
3024
3847
  driver: this.name,
3025
3848
  source,
3026
3849
  resolvedPath,
3027
- packageName: `vercel/${basename7(resolvedPath)}`,
3850
+ packageName: `vercel/${basename10(resolvedPath)}`,
3028
3851
  mode: options.mode ?? "pinned",
3029
3852
  sourceHash: await hashPath(resolvedPath)
3030
3853
  };
@@ -3045,7 +3868,7 @@ var VercelSkillsSourceDriver = class {
3045
3868
  driver: "git",
3046
3869
  source: parsed.gitSource
3047
3870
  });
3048
- const resolvedPath = parsed.subpath ? join12(fetched.resolvedPath, parsed.subpath) : fetched.resolvedPath;
3871
+ const resolvedPath = parsed.subpath ? join15(fetched.resolvedPath, parsed.subpath) : fetched.resolvedPath;
3049
3872
  if (!await pathExists(resolvedPath)) {
3050
3873
  throw new Error(`Vercel skills subpath not found: ${parsed.subpath}`);
3051
3874
  }
@@ -3134,14 +3957,14 @@ function getSourceDriver(name = "local") {
3134
3957
  }
3135
3958
 
3136
3959
  // src/staging/staging.ts
3137
- import { chmod, cp as cp5, mkdir as mkdir9, mkdtemp as mkdtemp2, readdir as readdir5, stat as stat8 } from "fs/promises";
3138
- import { basename as basename10, dirname as dirname12, join as join15, relative as relative4, resolve as resolve10, sep as sep2 } from "path";
3960
+ import { chmod, cp as cp5, mkdir as mkdir11, mkdtemp as mkdtemp2, readdir as readdir5, stat as stat8 } from "fs/promises";
3961
+ import { basename as basename13, dirname as dirname14, join as join18, relative as relative5, resolve as resolve10, sep as sep2 } from "path";
3139
3962
  import { tmpdir as tmpdir3 } from "os";
3140
3963
 
3141
3964
  // src/compose/markdown.ts
3142
- import { createHash as createHash3 } from "crypto";
3143
- import { readdir as readdir3, readFile as readFile9, stat as stat6, writeFile as writeFile8 } from "fs/promises";
3144
- import { basename as basename8, dirname as dirname10, extname as extname3, join as join13, relative as relative3, resolve as resolve9, sep } from "path";
3965
+ import { createHash as createHash4 } from "crypto";
3966
+ import { readdir as readdir3, readFile as readFile12, stat as stat6, writeFile as writeFile10 } from "fs/promises";
3967
+ import { basename as basename11, dirname as dirname12, extname as extname3, join as join16, relative as relative4, resolve as resolve9, sep } from "path";
3145
3968
  var includePattern = /<!--\s*openpack:include(\?)?\s+([^>]+?)\s*-->/g;
3146
3969
  var escapedIncludePattern = /<!--\s*openpack\\:include(\?)?\s+([^>]+?)\s*-->/g;
3147
3970
  var generatedPattern = /<!--\s*(?:BEGIN|END)\s+openpack:include\b/;
@@ -3156,7 +3979,7 @@ async function expandMarkdownIncludes(artifacts, packageRoot, options = {}) {
3156
3979
  const composedFrom = [];
3157
3980
  for (const file of files) {
3158
3981
  const result = await expandFile(file, packageRoot, composeEntriesForFile(artifact, file), artifactPaths, options);
3159
- if (result.changed) await writeFile8(file, result.content, "utf8");
3982
+ if (result.changed) await writeFile10(file, result.content, "utf8");
3160
3983
  composedFrom.push(...result.composedFrom);
3161
3984
  }
3162
3985
  const stagedPath = artifact.stagedPath ?? artifact.sourcePath;
@@ -3178,7 +4001,7 @@ async function validateMarkdownIncludes(artifacts, packageRoot, options = {}) {
3178
4001
  }
3179
4002
  }
3180
4003
  async function expandFile(file, packageRoot, appendEntries, artifactPaths, options) {
3181
- const raw = await readFile9(file, "utf8");
4004
+ const raw = await readFile12(file, "utf8");
3182
4005
  const owner = ownerSelector(packageRoot, file, options.nodeId);
3183
4006
  const expanded = await expandContent(raw, packageRoot, [owner], artifactPaths, options);
3184
4007
  let content = expanded.content;
@@ -3271,7 +4094,7 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
3271
4094
  if (!stats.isFile()) {
3272
4095
  throw new Error(`OpenPack include is not a file: ${displaySelector}`);
3273
4096
  }
3274
- const raw = sourceContent ?? await readFile9(sourcePath, "utf8");
4097
+ const raw = sourceContent ?? await readFile12(sourcePath, "utf8");
3275
4098
  const { optional: _optional, markers: _markers, chain: _chain, ...childOptions } = options;
3276
4099
  const expanded = await expandContent(raw, includePackageRoot, [...options.chain, displaySelector], includeArtifactPaths, {
3277
4100
  ...childOptions,
@@ -3347,7 +4170,7 @@ async function listMarkdownFiles(root) {
3347
4170
  const out = [];
3348
4171
  async function walk2(dir) {
3349
4172
  for (const entry of (await readdir3(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
3350
- const full = join13(dir, entry.name);
4173
+ const full = join16(dir, entry.name);
3351
4174
  if (entry.isDirectory()) {
3352
4175
  await walk2(full);
3353
4176
  } else if (entry.isFile() && extname3(entry.name).toLowerCase() === ".md") {
@@ -3361,7 +4184,7 @@ async function listMarkdownFiles(root) {
3361
4184
  function composeEntriesForFile(artifact, file) {
3362
4185
  if (!artifact.compose?.length) return [];
3363
4186
  if (artifact.kind === "file") return [resolve9(artifact.stagedPath ?? artifact.sourcePath), resolve9(file)].every(Boolean) && resolve9(artifact.stagedPath ?? artifact.sourcePath) === resolve9(file) ? artifact.compose : [];
3364
- return basename8(file) === "SKILL.md" && dirname10(file) === resolve9(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
4187
+ return basename11(file) === "SKILL.md" && dirname12(file) === resolve9(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
3365
4188
  }
3366
4189
  function orderedForExpansion(artifacts) {
3367
4190
  return [...artifacts].sort((a, b) => Number(a.type === "fragments") - Number(b.type === "fragments"));
@@ -3378,7 +4201,7 @@ function applyReplacements(content, replacements) {
3378
4201
  return out + content.slice(cursor);
3379
4202
  }
3380
4203
  function relativeSelector(root, file) {
3381
- return relative3(root, file).replaceAll("\\", "/");
4204
+ return relative4(root, file).replaceAll("\\", "/");
3382
4205
  }
3383
4206
  function ownerSelector(root, file, nodeId) {
3384
4207
  const selector = relativeSelector(root, file);
@@ -3392,7 +4215,7 @@ function cleanSelector(value) {
3392
4215
  return value.trim().replace(/\s+/g, " ");
3393
4216
  }
3394
4217
  function sha256(content) {
3395
- return createHash3("sha256").update(content).digest("hex");
4218
+ return createHash4("sha256").update(content).digest("hex");
3396
4219
  }
3397
4220
  function uniqueComposedFrom(entries) {
3398
4221
  if (entries.length === 0) return [];
@@ -3407,8 +4230,8 @@ function artifactPathMap(artifacts) {
3407
4230
  }
3408
4231
 
3409
4232
  // src/staging/customize.ts
3410
- import { cp as cp4, mkdir as mkdir8, readdir as readdir4, readFile as readFile10, writeFile as writeFile9 } from "fs/promises";
3411
- import { dirname as dirname11, join as join14 } from "path";
4233
+ import { cp as cp4, mkdir as mkdir10, readdir as readdir4, readFile as readFile13, writeFile as writeFile11 } from "fs/promises";
4234
+ import { dirname as dirname13, join as join17 } from "path";
3412
4235
  async function applyCustomizations(artifacts, options) {
3413
4236
  let next = [...artifacts];
3414
4237
  next = await applyReplacements2(next, options, "override", installableArtifactTypes());
@@ -3424,16 +4247,16 @@ async function applyFragmentCustomizations(artifacts, options) {
3424
4247
  return next.sort((a, b) => `${a.type}:${a.name}:${a.channel}`.localeCompare(`${b.type}:${b.name}:${b.channel}`));
3425
4248
  }
3426
4249
  async function applyInstructionOverlay(artifacts, options) {
3427
- const overlayPath = join14(options.workspaceRoot, ".agentwheel", "overlays", options.adapter.name, "instructions.local.md");
4250
+ const overlayPath = join17(options.workspaceRoot, ".agentwheel", "overlays", options.adapter.name, "instructions.local.md");
3428
4251
  if (!await pathExists(overlayPath)) return artifacts;
3429
4252
  const index = artifacts.findIndex((artifact2) => artifact2.type === "instructions");
3430
4253
  if (index < 0) return artifacts;
3431
4254
  const artifact = artifacts[index];
3432
- const managed = await readFile10(artifact.stagedPath ?? artifact.sourcePath, "utf8");
3433
- const local = await readFile10(overlayPath, "utf8");
3434
- const composedPath = join14(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
3435
- await mkdir8(dirname11(composedPath), { recursive: true });
3436
- await writeFile9(
4255
+ const managed = await readFile13(artifact.stagedPath ?? artifact.sourcePath, "utf8");
4256
+ const local = await readFile13(overlayPath, "utf8");
4257
+ const composedPath = join17(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
4258
+ await mkdir10(dirname13(composedPath), { recursive: true });
4259
+ await writeFile11(
3437
4260
  composedPath,
3438
4261
  [
3439
4262
  "<!-- BEGIN agentwheel managed: upstream -->",
@@ -3459,19 +4282,19 @@ async function applyInstructionOverlay(artifacts, options) {
3459
4282
  return [...artifacts.slice(0, index), updated, ...artifacts.slice(index + 1)];
3460
4283
  }
3461
4284
  async function applyAdditions(artifacts, options) {
3462
- const additionsRoot = join14(options.workspaceRoot, ".agentwheel", "additions");
3463
- const rulesRoot = join14(additionsRoot, "rules");
4285
+ const additionsRoot = join17(options.workspaceRoot, ".agentwheel", "additions");
4286
+ const rulesRoot = join17(additionsRoot, "rules");
3464
4287
  if (!await pathExists(rulesRoot)) return artifacts;
3465
4288
  const additions = [];
3466
4289
  for (const entry of await sortedDirEntries2(rulesRoot)) {
3467
- const full = join14(rulesRoot, entry.name);
4290
+ const full = join17(rulesRoot, entry.name);
3468
4291
  if (!entry.isFile()) continue;
3469
4292
  additions.push({
3470
4293
  type: "rules",
3471
4294
  name: entry.name,
3472
4295
  sourcePath: full,
3473
4296
  stagedPath: full,
3474
- relativePath: join14("additions", "rules", entry.name),
4297
+ relativePath: join17("additions", "rules", entry.name),
3475
4298
  kind: "file",
3476
4299
  hash: await hashPath(full),
3477
4300
  packageName: options.packageName,
@@ -3495,17 +4318,17 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
3495
4318
  );
3496
4319
  }
3497
4320
  for (const type of artifactTypes) {
3498
- const typeRoot = join14(root, type);
4321
+ const typeRoot = join17(root, type);
3499
4322
  if (!await pathExists(typeRoot)) continue;
3500
4323
  for (const entry of await sortedDirEntries2(typeRoot)) {
3501
4324
  const artifactMapKey = `${type}:${entry.name}`;
3502
4325
  if (seen.has(artifactMapKey)) continue;
3503
4326
  seen.add(artifactMapKey);
3504
- const full = join14(typeRoot, entry.name);
4327
+ const full = join17(typeRoot, entry.name);
3505
4328
  const artifactKind = entry.isDirectory() ? "dir" : "file";
3506
4329
  const existing = byKey.get(artifactMapKey);
3507
- const stagedPath = join14(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
3508
- await mkdir8(dirname11(stagedPath), { recursive: true });
4330
+ const stagedPath = join17(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
4331
+ await mkdir10(dirname13(stagedPath), { recursive: true });
3509
4332
  await cp4(full, stagedPath, { recursive: artifactKind === "dir", dereference: true });
3510
4333
  byKey.set(artifactMapKey, {
3511
4334
  ...existing,
@@ -3513,7 +4336,7 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
3513
4336
  name: entry.name,
3514
4337
  sourcePath: full,
3515
4338
  stagedPath,
3516
- relativePath: existing?.relativePath ?? join14(type, entry.name),
4339
+ relativePath: existing?.relativePath ?? join17(type, entry.name),
3517
4340
  kind: artifactKind,
3518
4341
  hash: await hashPath(stagedPath),
3519
4342
  packageName,
@@ -3528,13 +4351,13 @@ function replacementRoots(options, channel) {
3528
4351
  const stateDir = channel === "override" ? "overrides" : "ejected";
3529
4352
  const roots = [];
3530
4353
  if (options.graphNodeId) {
3531
- roots.push({ root: join14(options.workspaceRoot, ".agentwheel", stateDir, ...options.graphNodeId.split("/")), kind: "node" });
4354
+ roots.push({ root: join17(options.workspaceRoot, ".agentwheel", stateDir, ...options.graphNodeId.split("/")), kind: "node" });
3532
4355
  }
3533
4356
  if (options.packageName && options.packageVersion) {
3534
- roots.push({ root: join14(options.workspaceRoot, ".agentwheel", stateDir, ...`${options.packageName}@${options.packageVersion}`.split("/")), kind: "version" });
4357
+ roots.push({ root: join17(options.workspaceRoot, ".agentwheel", stateDir, ...`${options.packageName}@${options.packageVersion}`.split("/")), kind: "version" });
3535
4358
  }
3536
4359
  if (options.packageName) {
3537
- roots.push({ root: join14(options.workspaceRoot, ".agentwheel", stateDir, ...options.packageName.split("/")), kind: "package" });
4360
+ roots.push({ root: join17(options.workspaceRoot, ".agentwheel", stateDir, ...options.packageName.split("/")), kind: "package" });
3538
4361
  }
3539
4362
  return roots;
3540
4363
  }
@@ -3552,6 +4375,13 @@ async function sortedDirEntries2(path) {
3552
4375
  function artifactSelectorKey(artifact) {
3553
4376
  return `${artifact.type}/${artifact.name}`;
3554
4377
  }
4378
+ function artifactSelectorAliases(artifact) {
4379
+ const primary = artifactSelectorKey(artifact);
4380
+ if (artifact.type !== "subagents") return [primary];
4381
+ const baseName = subagentBaseName(artifact.name);
4382
+ if (baseName === artifact.name) return [primary];
4383
+ return [primary, `subagents/${baseName}`];
4384
+ }
3555
4385
  function normalizeArtifactSelectors(select, legacySkills) {
3556
4386
  const selected = [
3557
4387
  ...select ?? [],
@@ -3564,12 +4394,12 @@ function filterArtifactsBySelection(artifacts, selectors, legacySkills) {
3564
4394
  const selected = normalizeArtifactSelectors(selectors, legacySkills);
3565
4395
  if (!selected?.length) return artifacts;
3566
4396
  const selectedSet = new Set(selected);
3567
- const available = new Set(artifacts.map(artifactSelectorKey));
4397
+ const available = new Set(artifacts.flatMap(artifactSelectorAliases));
3568
4398
  const missing = selected.filter((selector) => !available.has(selector));
3569
4399
  if (missing.length > 0) {
3570
4400
  throw new Error(`Selected artifact not found in package: ${missing.join(", ")}`);
3571
4401
  }
3572
- return artifacts.filter((artifact) => artifact.required || selectedSet.has(artifactSelectorKey(artifact)));
4402
+ return artifacts.filter((artifact) => artifact.required || artifactSelectorAliases(artifact).some((selector) => selectedSet.has(selector)));
3573
4403
  }
3574
4404
  function splitSelectorList(value) {
3575
4405
  return value.split(",").map((item) => item.trim()).filter(Boolean);
@@ -3587,6 +4417,9 @@ function parseArtifactSelector(value) {
3587
4417
  }
3588
4418
  return `${parsedType.data}/${name}`;
3589
4419
  }
4420
+ function subagentBaseName(name) {
4421
+ return name.replace(/\.agent\.md$/i, "").replace(/\.toml$/i, "").replace(/\.md$/i, "");
4422
+ }
3590
4423
 
3591
4424
  // src/staging/staging.ts
3592
4425
  async function stageSource(driver, source, options = {}) {
@@ -3601,12 +4434,16 @@ async function stageResolvedSourceRaw(driver, resolved) {
3601
4434
  return stageResolvedArtifactsRaw(resolved, artifacts);
3602
4435
  }
3603
4436
  async function stageResolvedArtifactsRaw(resolved, artifacts) {
3604
- const root = await mkdtemp2(join15(tmpdir3(), "agentwheel-stage-"));
4437
+ const root = await mkdtemp2(join18(tmpdir3(), "agentwheel-stage-"));
3605
4438
  const stagedArtifacts = [];
3606
4439
  for (const artifact of artifacts) {
3607
- const stagedPath = join15(root, artifact.relativePath);
3608
- await mkdir9(dirname12(stagedPath), { recursive: true });
3609
- await cp5(artifact.sourcePath, stagedPath, { recursive: artifact.kind === "dir", dereference: true });
4440
+ const stagedPath = join18(root, artifact.relativePath);
4441
+ await mkdir11(dirname14(stagedPath), { recursive: true });
4442
+ await cp5(artifact.sourcePath, stagedPath, {
4443
+ recursive: artifact.kind === "dir",
4444
+ dereference: true,
4445
+ filter: (path) => !isIgnoredGeneratedEntry(basename13(path))
4446
+ });
3610
4447
  await composeAssets(artifact, resolved.resolvedPath, stagedPath);
3611
4448
  stagedArtifacts.push({
3612
4449
  ...artifact,
@@ -3633,12 +4470,14 @@ async function renderStagedBundle(bundle, options = {}) {
3633
4470
  const selectedArtifacts = filterArtifactsBySelection(expandedArtifacts, options.select, options.skills);
3634
4471
  const runtimeSelectedSet = new Set(normalizeArtifactSelectors(options.select, options.skills) ?? []);
3635
4472
  const runtimeArtifacts = options.adapter ? filterArtifactsByRuntime(selectedArtifacts, options.adapter.name, runtimeSelectedSet) : selectedArtifacts;
3636
- const finalArtifacts = options.workspaceRoot && options.adapter ? await applyCustomizations(runtimeArtifacts, {
4473
+ const codexRenderedArtifacts = await renderCodexSubagents(runtimeArtifacts, root, options.adapter);
4474
+ const renderedArtifacts = await renderCopilotArtifacts(codexRenderedArtifacts, root, options.adapter);
4475
+ const finalArtifacts = options.workspaceRoot && options.adapter ? await applyCustomizations(renderedArtifacts, {
3637
4476
  workspaceRoot: options.workspaceRoot,
3638
4477
  adapter: options.adapter,
3639
4478
  stageRoot: root,
3640
4479
  packageName: resolved.packageName
3641
- }) : runtimeArtifacts;
4480
+ }) : renderedArtifacts;
3642
4481
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
3643
4482
  return {
3644
4483
  root,
@@ -3662,6 +4501,7 @@ async function renderStagedBundle(bundle, options = {}) {
3662
4501
  relativePath: artifact.relativePath,
3663
4502
  kind: artifact.kind,
3664
4503
  hash: artifact.hash,
4504
+ format: artifact.format,
3665
4505
  composedFrom: artifact.composedFrom
3666
4506
  }))
3667
4507
  }
@@ -3683,16 +4523,16 @@ async function composeAssets(artifact, packageRoot, stagedPath) {
3683
4523
  }
3684
4524
  for (const asset of artifact.assets) {
3685
4525
  const source = resolvePackagePath(packageRoot, asset.from);
3686
- const dest = join15(stagedPath, asset.into);
4526
+ const dest = join18(stagedPath, asset.into);
3687
4527
  await copyAsset(asset, source, dest);
3688
4528
  }
3689
4529
  }
3690
4530
  async function copyAsset(asset, source, dest) {
3691
4531
  const sourceStats = await stat8(source);
3692
4532
  if (sourceStats.isFile()) {
3693
- if (matchesAny(basename10(source), asset.include)) {
3694
- await mkdir9(dest, { recursive: true });
3695
- await copyAssetFile(source, join15(dest, basename10(source)), asset);
4533
+ if (matchesAny(basename13(source), asset.include)) {
4534
+ await mkdir11(dest, { recursive: true });
4535
+ await copyAssetFile(source, join18(dest, basename13(source)), asset);
3696
4536
  }
3697
4537
  return;
3698
4538
  }
@@ -3700,19 +4540,19 @@ async function copyAsset(asset, source, dest) {
3700
4540
  throw new Error(`Asset include source is not a file or directory: ${source}`);
3701
4541
  }
3702
4542
  if (!asset.include?.length) {
3703
- await mkdir9(dirname12(dest), { recursive: true });
4543
+ await mkdir11(dirname14(dest), { recursive: true });
3704
4544
  await cp5(source, dest, { recursive: true, dereference: true });
3705
4545
  if (asset.mode === "copy") await normalizeCopiedModes(dest);
3706
4546
  return;
3707
4547
  }
3708
4548
  for (const file of await listFiles(source)) {
3709
- const rel = relative4(source, file).replaceAll("\\", "/");
3710
- if (!matchesAny(rel, asset.include) && !matchesAny(basename10(file), asset.include)) continue;
3711
- await copyAssetFile(file, join15(dest, rel), asset);
4549
+ const rel = relative5(source, file).replaceAll("\\", "/");
4550
+ if (!matchesAny(rel, asset.include) && !matchesAny(basename13(file), asset.include)) continue;
4551
+ await copyAssetFile(file, join18(dest, rel), asset);
3712
4552
  }
3713
4553
  }
3714
4554
  async function copyAssetFile(source, dest, asset) {
3715
- await mkdir9(dirname12(dest), { recursive: true });
4555
+ await mkdir11(dirname14(dest), { recursive: true });
3716
4556
  await cp5(source, dest, { dereference: true });
3717
4557
  if (asset.mode === "copy") await chmod(dest, 420);
3718
4558
  }
@@ -3728,7 +4568,7 @@ async function listFiles(root) {
3728
4568
  const out = [];
3729
4569
  async function walk2(dir) {
3730
4570
  for (const entry of (await readdir5(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
3731
- const full = join15(dir, entry.name);
4571
+ const full = join18(dir, entry.name);
3732
4572
  if (entry.isDirectory()) {
3733
4573
  await walk2(full);
3734
4574
  } else if (entry.isFile()) {
@@ -3747,7 +4587,7 @@ async function normalizeCopiedModes(path) {
3747
4587
  }
3748
4588
  if (!stats.isDirectory()) return;
3749
4589
  for (const entry of await readdir5(path, { withFileTypes: true })) {
3750
- await normalizeCopiedModes(join15(path, entry.name));
4590
+ await normalizeCopiedModes(join18(path, entry.name));
3751
4591
  }
3752
4592
  }
3753
4593
  function matchesAny(path, patterns) {
@@ -3760,9 +4600,9 @@ function matchesGlob(path, pattern) {
3760
4600
  }
3761
4601
 
3762
4602
  // src/model/workspace.ts
3763
- import { readFile as readFile11 } from "fs/promises";
3764
- import { homedir as homedir3 } from "os";
3765
- import { dirname as dirname13, join as join16, resolve as resolve11 } from "path";
4603
+ import { readFile as readFile14 } from "fs/promises";
4604
+ import { homedir as homedir4 } from "os";
4605
+ import { dirname as dirname15, join as join19, resolve as resolve11 } from "path";
3766
4606
  import { z as z6 } from "zod";
3767
4607
  var workspacePackageSchema = z6.object({
3768
4608
  name: z6.string().min(1),
@@ -3772,17 +4612,20 @@ var workspacePackageSchema = z6.object({
3772
4612
  adapterConfig: z6.string().min(1).optional(),
3773
4613
  adapterModule: z6.string().min(1).optional(),
3774
4614
  adapterCodeHash: z6.string().min(16).optional(),
4615
+ installationType: installationTypeSchema.optional(),
3775
4616
  mode: z6.enum(["pinned", "tracking"]).default("pinned"),
3776
4617
  requestedRef: z6.string().min(1).optional(),
3777
4618
  select: z6.array(z6.string().min(1)).optional(),
3778
4619
  skills: z6.array(z6.string().min(1)).optional(),
3779
- aliases: z6.record(z6.string(), z6.string().min(1)).optional()
4620
+ aliases: z6.record(z6.string(), z6.string().min(1)).optional(),
4621
+ overrides: z6.array(z6.string().min(1)).optional()
3780
4622
  });
3781
4623
  var workspaceProfileRuntimeSchema = z6.object({
3782
4624
  agent: z6.string().min(1).optional(),
3783
4625
  adapter: z6.string().min(1).default("openclaw"),
3784
4626
  adapterConfig: z6.string().min(1).optional(),
3785
4627
  adapterModule: z6.string().min(1).optional(),
4628
+ installationType: installationTypeSchema.optional(),
3786
4629
  targetRoot: z6.string().min(1).optional(),
3787
4630
  executePlugins: z6.boolean().optional()
3788
4631
  });
@@ -3802,6 +4645,7 @@ var workspaceTrustSchema = z6.object({
3802
4645
  var workspaceAgentSchema = z6.object({
3803
4646
  adapter: z6.string().min(1),
3804
4647
  root: z6.string().min(1),
4648
+ installationType: installationTypeSchema.optional(),
3805
4649
  transport: z6.enum(["local", "ssh"]).default("local"),
3806
4650
  host: z6.string().min(1).optional(),
3807
4651
  user: z6.string().min(1).optional(),
@@ -3827,12 +4671,12 @@ var workspaceConfigSchema = z6.object({
3827
4671
  agents: z6.record(z6.string(), workspaceAgentSchema).default({})
3828
4672
  });
3829
4673
  function workspaceConfigPath(workspaceRoot) {
3830
- return join16(workspaceRoot, ".agentwheel", "config.json");
4674
+ return join19(workspaceRoot, ".agentwheel", "config.json");
3831
4675
  }
3832
4676
  async function readWorkspaceConfig(workspaceRoot) {
3833
4677
  const path = workspaceConfigPath(workspaceRoot);
3834
4678
  if (!await pathExists(path)) return emptyWorkspaceConfig();
3835
- return workspaceConfigSchema.parse(JSON.parse(await readFile11(path, "utf8")));
4679
+ return workspaceConfigSchema.parse(JSON.parse(await readFile14(path, "utf8")));
3836
4680
  }
3837
4681
  async function writeWorkspaceConfig(workspaceRoot, config) {
3838
4682
  await writeJsonAtomic(workspaceConfigPath(workspaceRoot), workspaceConfigSchema.parse(config));
@@ -3844,14 +4688,14 @@ function upsertPackage(config, entry) {
3844
4688
  packages.sort((a, b) => a.name.localeCompare(b.name));
3845
4689
  return { schemaVersion: 1, packages, bootstrapSkills: parsed.bootstrapSkills, registry: parsed.registry ?? {}, trust: parsed.trust ?? {}, profiles: parsed.profiles ?? {}, agents: parsed.agents ?? {} };
3846
4690
  }
3847
- function globalWorkspaceConfigPath(globalRoot = homedir3()) {
3848
- return join16(globalRoot, ".agentwheel", "config.json");
4691
+ function globalWorkspaceConfigPath(globalRoot = homedir4()) {
4692
+ return join19(globalRoot, ".agentwheel", "config.json");
3849
4693
  }
3850
4694
  async function findWorkspaceRoot(start = process.cwd()) {
3851
4695
  let current = resolve11(start);
3852
4696
  while (true) {
3853
4697
  if (await pathExists(workspaceConfigPath(current))) return current;
3854
- const parent = dirname13(current);
4698
+ const parent = dirname15(current);
3855
4699
  if (parent === current) return resolve11(start);
3856
4700
  current = parent;
3857
4701
  }
@@ -3878,8 +4722,8 @@ function mergeWorkspaceConfig(global, project) {
3878
4722
  });
3879
4723
  }
3880
4724
  function resolveConfigPath(path, baseRoot) {
3881
- if (path.startsWith("~/")) return resolve11(homedir3(), path.slice(2));
3882
- if (path === "~") return homedir3();
4725
+ if (path.startsWith("~/")) return resolve11(homedir4(), path.slice(2));
4726
+ if (path === "~") return homedir4();
3883
4727
  return path.startsWith("/") ? resolve11(path) : resolve11(baseRoot, path);
3884
4728
  }
3885
4729
  function emptyWorkspaceConfig() {
@@ -3887,7 +4731,7 @@ function emptyWorkspaceConfig() {
3887
4731
  }
3888
4732
  async function readConfigPath(path) {
3889
4733
  if (!await pathExists(path)) return emptyWorkspaceConfig();
3890
- return workspaceConfigSchema.parse(JSON.parse(await readFile11(path, "utf8")));
4734
+ return workspaceConfigSchema.parse(JSON.parse(await readFile14(path, "utf8")));
3891
4735
  }
3892
4736
  function mergeWorkspaceTrust(global, project) {
3893
4737
  return {
@@ -3902,23 +4746,23 @@ function sortedUnique2(values) {
3902
4746
  }
3903
4747
 
3904
4748
  // src/lifecycle/customization.ts
3905
- import { appendFile, cp as cp6, mkdir as mkdir10, rm as rm7 } from "fs/promises";
3906
- import { dirname as dirname15, join as join19 } from "path";
4749
+ import { appendFile, cp as cp6, mkdir as mkdir12, rm as rm7 } from "fs/promises";
4750
+ import { dirname as dirname17, join as join22 } from "path";
3907
4751
 
3908
4752
  // src/resolve/graph.ts
3909
- import { createHash as createHash4 } from "crypto";
3910
- import { mkdtemp as mkdtemp3, readdir as readdir6, readFile as readFile13, stat as stat10 } from "fs/promises";
4753
+ import { createHash as createHash5 } from "crypto";
4754
+ import { mkdtemp as mkdtemp3, readdir as readdir6, readFile as readFile16, stat as stat10 } from "fs/promises";
3911
4755
  import { tmpdir as tmpdir4 } from "os";
3912
- import { basename as basename11, extname as extname4, join as join18 } from "path";
4756
+ import { basename as basename14, extname as extname4, join as join21 } from "path";
3913
4757
 
3914
4758
  // src/resolve/identity.ts
3915
- import { homedir as homedir5 } from "os";
4759
+ import { homedir as homedir6 } from "os";
3916
4760
  import { resolve as resolve13 } from "path";
3917
4761
 
3918
4762
  // src/registry/client.ts
3919
- import { readFile as readFile12, rm as rm6, stat as stat9 } from "fs/promises";
3920
- import { homedir as homedir4 } from "os";
3921
- import { dirname as dirname14, join as join17, resolve as resolve12 } from "path";
4763
+ import { readFile as readFile15, rm as rm6, stat as stat9 } from "fs/promises";
4764
+ import { homedir as homedir5 } from "os";
4765
+ import { dirname as dirname16, join as join20, resolve as resolve12 } from "path";
3922
4766
  import { fileURLToPath } from "url";
3923
4767
 
3924
4768
  // src/model/registry.ts
@@ -4016,7 +4860,7 @@ var RegistryClient = class {
4016
4860
  }
4017
4861
  async readCache() {
4018
4862
  if (!await pathExists(this.cachePath)) return void 0;
4019
- return registryCacheSchema.parse(JSON.parse(await readFile12(this.cachePath, "utf8")));
4863
+ return registryCacheSchema.parse(JSON.parse(await readFile15(this.cachePath, "utf8")));
4020
4864
  }
4021
4865
  isExpired(cache, ttlMs) {
4022
4866
  return this.now().getTime() - new Date(cache.fetchedAt).getTime() > ttlMs;
@@ -4035,10 +4879,10 @@ var RegistryClient = class {
4035
4879
  if (await pathExists(filePath)) {
4036
4880
  const fullPath = resolve12(filePath);
4037
4881
  const stats = await stat9(fullPath);
4038
- return readFile12(stats.isDirectory() ? join17(fullPath, "index.json") : fullPath, "utf8");
4882
+ return readFile15(stats.isDirectory() ? join20(fullPath, "index.json") : fullPath, "utf8");
4039
4883
  }
4040
- const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join17(dirname14(this.cachePath), "registry-repos") }));
4041
- return readFile12(join17(resolved.resolvedPath, "index.json"), "utf8");
4884
+ const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join20(dirname16(this.cachePath), "registry-repos") }));
4885
+ return readFile15(join20(resolved.resolvedPath, "index.json"), "utf8");
4042
4886
  }
4043
4887
  warnCompatibility(entries) {
4044
4888
  for (const entry of entries) {
@@ -4052,10 +4896,13 @@ var RegistryClient = class {
4052
4896
  }
4053
4897
  };
4054
4898
  async function resolvePackageSource(source, workspaceRoot, options = {}) {
4055
- const { isExplicitSource } = await import("./identify-7SEBWCNQ.js");
4899
+ const { isExplicitSource } = await import("./identify-TXIDGMNL.js");
4056
4900
  if (await isExplicitSource(source)) return { source };
4057
4901
  const entry = await new RegistryClient({ workspaceRoot, offline: options.offline, warn: options.warn }).resolve(source);
4058
4902
  if (!entry) {
4903
+ if (options.offline) {
4904
+ throw new Error(`Offline cannot refresh registry indexes (entry not found in cache: ${source}). Run without --offline first.`);
4905
+ }
4059
4906
  throw new Error(`Registry entry not found: ${source}. Use an explicit path/git/skillkit/vercel source to bypass the registry.`);
4060
4907
  }
4061
4908
  return { source: entry.source, registryEntry: entry };
@@ -4070,7 +4917,7 @@ function mergeIndexes(indexes) {
4070
4917
  return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
4071
4918
  }
4072
4919
  function defaultRegistryCachePath() {
4073
- return join17(homedir4(), ".agentwheel", "registry-cache.json");
4920
+ return join20(homedir5(), ".agentwheel", "registry-cache.json");
4074
4921
  }
4075
4922
  function sameSources(a, b) {
4076
4923
  return a.length === b.length && a.every((source, index) => source === b[index]);
@@ -4139,8 +4986,8 @@ function localSourcePath(source) {
4139
4986
  return source.startsWith("local:") ? source.slice("local:".length) : source;
4140
4987
  }
4141
4988
  function resolveLocalPath(path, declaringPackageRoot) {
4142
- if (path === "~") return homedir5();
4143
- if (path.startsWith("~/")) return resolve13(homedir5(), path.slice(2));
4989
+ if (path === "~") return homedir6();
4990
+ if (path.startsWith("~/")) return resolve13(homedir6(), path.slice(2));
4144
4991
  if (path.startsWith("/")) return resolve13(path);
4145
4992
  return resolve13(declaringPackageRoot, path);
4146
4993
  }
@@ -4299,7 +5146,7 @@ function compareSemver(a, b) {
4299
5146
  var cacheLocks = /* @__PURE__ */ new Map();
4300
5147
  async function resolveDependencyGraph(roots, options) {
4301
5148
  if (roots.length === 0) throw new Error("At least one graph root is required.");
4302
- const graphRoot = await mkdtemp3(join18(tmpdir4(), "agentwheel-graph-"));
5149
+ const graphRoot = await mkdtemp3(join21(tmpdir4(), "agentwheel-graph-"));
4303
5150
  const fetchCache = /* @__PURE__ */ new Map();
4304
5151
  const nodesByKey = /* @__PURE__ */ new Map();
4305
5152
  const rootResults = [];
@@ -4315,6 +5162,7 @@ async function resolveDependencyGraph(roots, options) {
4315
5162
  requiredBy: `workspace:${rootId}`,
4316
5163
  rootId,
4317
5164
  aliases: root.aliases,
5165
+ overrides: root.overrides,
4318
5166
  useLock: root.useLock ?? options.lockedResolution,
4319
5167
  depth: 0,
4320
5168
  optional: false,
@@ -4346,7 +5194,7 @@ async function resolveDependencyGraph(roots, options) {
4346
5194
  generatedAt: (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
4347
5195
  };
4348
5196
  }
4349
- function createGraphLock(graph, artifacts = [], targetFingerprint, includeEdges = [], namespacing = []) {
5197
+ function createGraphLock(graph, artifacts = [], targetFingerprint, includeEdges = [], namespacing = [], overrides = []) {
4350
5198
  const roots = graph.roots.map((root) => ({
4351
5199
  rootId: root.rootId,
4352
5200
  source: root.source,
@@ -4354,7 +5202,8 @@ function createGraphLock(graph, artifacts = [], targetFingerprint, includeEdges
4354
5202
  graphNodeId: root.graphNodeId,
4355
5203
  mode: root.mode,
4356
5204
  selected: root.selected,
4357
- aliases: root.aliases
5205
+ aliases: root.aliases,
5206
+ overrides: root.overrides
4358
5207
  }));
4359
5208
  return {
4360
5209
  version: 1,
@@ -4366,6 +5215,7 @@ function createGraphLock(graph, artifacts = [], targetFingerprint, includeEdges
4366
5215
  includeEdges,
4367
5216
  artifacts,
4368
5217
  namespacing,
5218
+ overrides,
4369
5219
  plainNameIncumbents: []
4370
5220
  }
4371
5221
  };
@@ -4462,7 +5312,8 @@ Run without ${lockLabel === "Offline" ? "--offline" : "--frozen-lock"} first.`
4462
5312
  graphNodeId: state.node.id,
4463
5313
  mode: state.node.mode,
4464
5314
  selected: state.node.selected,
4465
- aliases: requirement.aliases
5315
+ aliases: requirement.aliases,
5316
+ overrides: requirement.overrides
4466
5317
  });
4467
5318
  }
4468
5319
  if (requirement.parentId && requirement.alias) {
@@ -4657,7 +5508,7 @@ async function collectIncludeNeeds(artifact, artifactsByRelativePath) {
4657
5508
  const file = stack.shift();
4658
5509
  if (scanned.has(file)) continue;
4659
5510
  scanned.add(file);
4660
- const content = await readFile13(file, "utf8");
5511
+ const content = await readFile16(file, "utf8");
4661
5512
  for (const include of extractOpenPackIncludeSelectors(content)) {
4662
5513
  await collectIncludeSelector(include.raw, include.optional, artifactsByRelativePath, scanned, stack, needs);
4663
5514
  }
@@ -4700,7 +5551,7 @@ async function listMarkdownFiles2(root) {
4700
5551
  const out = [];
4701
5552
  async function walk2(dir) {
4702
5553
  for (const entry of (await readdir6(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
4703
- const full = join18(dir, entry.name);
5554
+ const full = join21(dir, entry.name);
4704
5555
  if (entry.isDirectory()) {
4705
5556
  await walk2(full);
4706
5557
  } else if (entry.isFile() && extname4(entry.name).toLowerCase() === ".md") {
@@ -4735,7 +5586,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
4735
5586
  const promise = (async () => {
4736
5587
  const driver = getSourceDriver(normalized.driver);
4737
5588
  const resolved = await driver.resolve(normalized.source, {
4738
- cacheRoot: options.cacheRoot ?? join18(options.workspaceRoot, ".agentwheel", "cache"),
5589
+ cacheRoot: options.cacheRoot ?? join21(options.workspaceRoot, ".agentwheel", "cache"),
4739
5590
  mode,
4740
5591
  ref: refOverride ?? normalized.requestedRef,
4741
5592
  frozenLock: hardLockedCheckout
@@ -4745,7 +5596,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
4745
5596
  const exported = await driver.export(translated);
4746
5597
  const manifest = await readPackageManifest(exported.resolvedPath);
4747
5598
  const artifacts = await driver.list(exported);
4748
- const name = manifest?.name ?? exported.packageName ?? basename11(exported.resolvedPath);
5599
+ const name = manifest?.name ?? exported.packageName ?? basename14(exported.resolvedPath);
4749
5600
  const version = manifest?.version ?? exported.packageVersion ?? "0.0.0";
4750
5601
  const sourceHash = exported.sourceHash ?? await hashPath(exported.resolvedPath);
4751
5602
  return {
@@ -4921,7 +5772,7 @@ function detectDirectCollisions(nodes) {
4921
5772
  }
4922
5773
  }
4923
5774
  function graphNodeId(name, version, normalizedSource, resolvedCommit, sourceHash) {
4924
- const digest = createHash4("sha256").update(normalizedSource).update("\0").update(resolvedCommit ?? sourceHash).digest("hex").slice(0, 12);
5775
+ const digest = createHash5("sha256").update(normalizedSource).update("\0").update(resolvedCommit ?? sourceHash).digest("hex").slice(0, 12);
4925
5776
  return `${name}@${version}+${digest}`;
4926
5777
  }
4927
5778
  function sortedUnique3(values) {
@@ -4942,8 +5793,8 @@ async function mapLimit(items, limit, fn) {
4942
5793
 
4943
5794
  // src/lifecycle/customization.ts
4944
5795
  async function remember(workspaceRoot, runtime, text) {
4945
- const overlayPath = join19(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
4946
- await mkdir10(dirname15(overlayPath), { recursive: true });
5796
+ const overlayPath = join22(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
5797
+ await mkdir12(dirname17(overlayPath), { recursive: true });
4947
5798
  await appendFile(overlayPath, `${text.trim()}
4948
5799
  `, "utf8");
4949
5800
  return { overlayPath };
@@ -4966,8 +5817,8 @@ async function ejectArtifact(workspaceRoot, item) {
4966
5817
  throw new Error(`Artifact not found: ${item}`);
4967
5818
  }
4968
5819
  const ejectedIdentity = parsed.packageIdentity === parsed.packageName ? parsed.packageIdentity : candidate.nodeId === parsed.packageIdentity ? candidate.nodeId : `${candidate.packageName}@${candidate.packageVersion}`;
4969
- const ejectedPath = join19(workspaceRoot, ".agentwheel", "ejected", ...ejectedIdentity.split("/"), parsed.type, parsed.name);
4970
- await mkdir10(dirname15(ejectedPath), { recursive: true });
5820
+ const ejectedPath = join22(workspaceRoot, ".agentwheel", "ejected", ...ejectedIdentity.split("/"), parsed.type, parsed.name);
5821
+ await mkdir12(dirname17(ejectedPath), { recursive: true });
4971
5822
  await rm7(ejectedPath, { recursive: true, force: true });
4972
5823
  await cp6(artifact.stagedPath ?? artifact.sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
4973
5824
  return {
@@ -5009,7 +5860,7 @@ async function stageEjectCandidate(workspaceRoot, pkg) {
5009
5860
  const adapter = pkg.adapterConfig ? await loadAdapterConfig(pkg.adapterConfig) : getAdapter(pkg.adapter);
5010
5861
  const bundle = await stageSource(driver, normalized.source, {
5011
5862
  adapter,
5012
- cacheRoot: join19(workspaceRoot, ".agentwheel", "cache"),
5863
+ cacheRoot: join22(workspaceRoot, ".agentwheel", "cache"),
5013
5864
  mode: pkg.mode,
5014
5865
  ref: normalized.requestedRef ?? pkg.requestedRef
5015
5866
  });
@@ -5059,9 +5910,9 @@ function ejectCommands(candidates, parsed) {
5059
5910
  import { rm as rm8 } from "fs/promises";
5060
5911
 
5061
5912
  // src/lifecycle/source-plan.ts
5062
- import { createHash as createHash6 } from "crypto";
5063
- import { mkdir as mkdir12 } from "fs/promises";
5064
- import { dirname as dirname17, join as join22, resolve as resolve14 } from "path";
5913
+ import { createHash as createHash7 } from "crypto";
5914
+ import { mkdir as mkdir14 } from "fs/promises";
5915
+ import { dirname as dirname19, join as join25, resolve as resolve14 } from "path";
5065
5916
 
5066
5917
  // src/resolve/graph-diff.ts
5067
5918
  function diffGraphLocks(previous, next) {
@@ -5069,7 +5920,8 @@ function diffGraphLocks(previous, next) {
5069
5920
  return [
5070
5921
  ...diffNodes(previous.canonical.nodes, next.canonical.nodes),
5071
5922
  ...diffIncludeEdges(previous.canonical.includeEdges, next.canonical.includeEdges),
5072
- ...diffNamespacing(previous.canonical.namespacing, next.canonical.namespacing)
5923
+ ...diffNamespacing(previous.canonical.namespacing, next.canonical.namespacing),
5924
+ ...diffOverrides(previous.canonical.overrides, next.canonical.overrides)
5073
5925
  ];
5074
5926
  }
5075
5927
  function diffNodes(previous, next) {
@@ -5131,6 +5983,23 @@ function diffNamespacing(previous, next) {
5131
5983
  }
5132
5984
  return lines.sort((a, b) => a.localeCompare(b));
5133
5985
  }
5986
+ function diffOverrides(previous, next) {
5987
+ const previousByKey = new Map(previous.map((decision) => [overrideKey(decision), decision]));
5988
+ const nextByKey = new Map(next.map((decision) => [overrideKey(decision), decision]));
5989
+ const lines = [];
5990
+ for (const [key, decision] of nextByKey) {
5991
+ const old = previousByKey.get(key);
5992
+ if (!old) {
5993
+ lines.push(`ADDED override ${formatOverride(decision)}`);
5994
+ } else if (old.graphNodeId !== decision.graphNodeId || old.installName !== decision.installName) {
5995
+ lines.push(`CHANGED override ${decision.selector} ${old.graphNodeId}:${old.type}/${old.name} -> ${decision.graphNodeId}:${decision.type}/${decision.name}`);
5996
+ }
5997
+ }
5998
+ for (const [key, decision] of previousByKey) {
5999
+ if (!nextByKey.has(key)) lines.push(`REMOVED override ${formatOverride(decision)}`);
6000
+ }
6001
+ return lines.sort((a, b) => a.localeCompare(b));
6002
+ }
5134
6003
  function stableNodeKey(node) {
5135
6004
  return `${node.normalizedSource}\0${node.name}`;
5136
6005
  }
@@ -5150,20 +6019,26 @@ function formatIncludeEdge(edge) {
5150
6019
  function namespaceKey(decision) {
5151
6020
  return `${decision.graphNodeId}\0${decision.type}\0${decision.name}`;
5152
6021
  }
6022
+ function overrideKey(decision) {
6023
+ return `${decision.rootId}\0${decision.selector}\0${decision.overriddenGraphNodeId}\0${decision.type}\0${decision.name}`;
6024
+ }
5153
6025
  function formatNamespace(decision) {
5154
6026
  return `${decision.graphNodeId}:${decision.type}/${decision.name} -> ${decision.type}/${decision.installName} (${decision.reason})`;
5155
6027
  }
6028
+ function formatOverride(decision) {
6029
+ return `${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`;
6030
+ }
5156
6031
  function short(hash) {
5157
6032
  return hash.slice(0, 12);
5158
6033
  }
5159
6034
 
5160
6035
  // src/resolve/render.ts
5161
- import { createHash as createHash5 } from "crypto";
5162
- import { readFile as readFile14, mkdtemp as mkdtemp4 } from "fs/promises";
6036
+ import { createHash as createHash6 } from "crypto";
6037
+ import { readFile as readFile17, mkdtemp as mkdtemp4 } from "fs/promises";
5163
6038
  import { tmpdir as tmpdir5 } from "os";
5164
- import { join as join20 } from "path";
6039
+ import { join as join23 } from "path";
5165
6040
  async function renderGraphForTarget(graph, targetContext = {}) {
5166
- const root = await mkdtemp4(join20(tmpdir5(), "agentwheel-render-"));
6041
+ const root = await mkdtemp4(join23(tmpdir5(), "agentwheel-render-"));
5167
6042
  const artifacts = [];
5168
6043
  const stagedNodes = /* @__PURE__ */ new Map();
5169
6044
  const includeEdges = /* @__PURE__ */ new Map();
@@ -5235,7 +6110,9 @@ async function renderGraphForTarget(graph, targetContext = {}) {
5235
6110
  const selectedArtifacts = filterArtifactsBySelection(expandedArtifacts, rawNode.node.selected);
5236
6111
  const runtimeSelectedSet = new Set(normalizeArtifactSelectors(rawNode.node.selected) ?? []);
5237
6112
  const runtimeArtifacts = targetContext.adapter ? filterArtifactsByRuntime2(selectedArtifacts, targetContext.adapter.name, runtimeSelectedSet) : selectedArtifacts;
5238
- const renderedArtifacts = targetContext.workspaceRoot && targetContext.adapter ? await applyCustomizations(runtimeArtifacts, {
6113
+ const codexRenderedArtifacts = await renderCodexSubagents(runtimeArtifacts, staged.root, targetContext.adapter);
6114
+ const runtimeRenderedArtifacts = await renderCopilotArtifacts(codexRenderedArtifacts, staged.root, targetContext.adapter);
6115
+ const renderedArtifacts = targetContext.workspaceRoot && targetContext.adapter ? await applyCustomizations(runtimeRenderedArtifacts, {
5239
6116
  workspaceRoot: targetContext.workspaceRoot,
5240
6117
  adapter: targetContext.adapter,
5241
6118
  stageRoot: staged.root,
@@ -5243,7 +6120,7 @@ async function renderGraphForTarget(graph, targetContext = {}) {
5243
6120
  packageVersion: rawNode.resolved.packageVersion,
5244
6121
  graphNodeId: rawNode.node.id,
5245
6122
  packageNameAmbiguous: ambiguousPackageNames.has(rawNode.node.name)
5246
- }) : runtimeArtifacts;
6123
+ }) : runtimeRenderedArtifacts;
5247
6124
  const installableArtifacts = renderedArtifacts.filter((artifact) => rawNode.depth === 0 || artifact.type !== "fragments");
5248
6125
  artifacts.push(...installableArtifacts.map((artifact) => ({
5249
6126
  ...artifact,
@@ -5254,13 +6131,13 @@ async function renderGraphForTarget(graph, targetContext = {}) {
5254
6131
  owners: [...rawNode.node.requiredBy].sort((a, b) => a.localeCompare(b))
5255
6132
  })));
5256
6133
  }
5257
- const { artifacts: namedArtifacts, namespacing } = assignInstallNames(graph, artifacts);
6134
+ const { artifacts: namedArtifacts, namespacing, overrides } = assignInstallNames(graph, artifacts);
5258
6135
  const sortedArtifacts = namedArtifacts.sort((a, b) => a.logicalSelector.localeCompare(b.logicalSelector));
5259
6136
  return {
5260
6137
  root,
5261
6138
  nodes: graph.nodes,
5262
6139
  artifacts: sortedArtifacts,
5263
- graphLock: createGraphLock(graph, sortedArtifacts.map(lockArtifactFor), targetContext.targetFingerprint, [...includeEdges.values()], namespacing)
6140
+ graphLock: createGraphLock(graph, sortedArtifacts.map(lockArtifactFor), targetContext.targetFingerprint, [...includeEdges.values()], namespacing, overrides)
5264
6141
  };
5265
6142
  }
5266
6143
  function aliasEdgeMap(graph) {
@@ -5277,7 +6154,7 @@ async function artifactContentMap(artifacts) {
5277
6154
  const out = /* @__PURE__ */ new Map();
5278
6155
  for (const artifact of artifacts) {
5279
6156
  if (artifact.kind !== "file") continue;
5280
- out.set(artifact.relativePath.replaceAll("\\", "/"), await readFile14(artifact.stagedPath ?? artifact.sourcePath, "utf8"));
6157
+ out.set(artifact.relativePath.replaceAll("\\", "/"), await readFile17(artifact.stagedPath ?? artifact.sourcePath, "utf8"));
5281
6158
  }
5282
6159
  return out;
5283
6160
  }
@@ -5291,7 +6168,7 @@ function filterArtifactsByRuntime2(artifacts, adapterName, selectedSet) {
5291
6168
  });
5292
6169
  }
5293
6170
  function sha2562(content) {
5294
- return createHash5("sha256").update(content).digest("hex");
6171
+ return createHash6("sha256").update(content).digest("hex");
5295
6172
  }
5296
6173
  function assignInstallNames(graph, artifacts) {
5297
6174
  const aliases = workspaceAliases(graph);
@@ -5304,7 +6181,8 @@ function assignInstallNames(graph, artifacts) {
5304
6181
  decisions.set(decisionKey(updated), namespaceDecision(updated, "alias"));
5305
6182
  return updated;
5306
6183
  });
5307
- const collisionGroups = [...groupBy(withAliases, (artifact) => `${artifact.type}\0${artifact.installName}`).values()].filter((group) => group.length > 1);
6184
+ const { artifacts: withOverrides, overrides } = applyWorkspaceOverrides(graph, withAliases);
6185
+ const collisionGroups = [...groupBy(withOverrides, (artifact) => `${artifact.type}\0${artifact.installName}`).values()].filter((group) => group.length > 1);
5308
6186
  const toRename = /* @__PURE__ */ new Set();
5309
6187
  for (const group of collisionGroups) {
5310
6188
  if (group.some((artifact) => decisions.has(decisionKey(artifact)))) throw installNameCollisionError(group);
@@ -5315,11 +6193,11 @@ function assignInstallNames(graph, artifacts) {
5315
6193
  }
5316
6194
  }
5317
6195
  const used = /* @__PURE__ */ new Map();
5318
- for (const artifact of withAliases) {
6196
+ for (const artifact of withOverrides) {
5319
6197
  if (toRename.has(artifact)) continue;
5320
6198
  used.set(`${artifact.type}\0${artifact.installName}`, artifact);
5321
6199
  }
5322
- const out = withAliases.filter((artifact) => !toRename.has(artifact));
6200
+ const out = withOverrides.filter((artifact) => !toRename.has(artifact));
5323
6201
  for (const group of groupBy([...toRename], (artifact) => `${artifact.type}\0${artifact.name}`).values()) {
5324
6202
  const renamed = namespaceTransitiveGroup(group, used);
5325
6203
  for (const artifact of renamed) {
@@ -5328,7 +6206,52 @@ function assignInstallNames(graph, artifacts) {
5328
6206
  out.push(artifact);
5329
6207
  }
5330
6208
  }
5331
- return { artifacts: out, namespacing: [...decisions.values()] };
6209
+ const finalInstallNames = new Map(out.map((artifact) => [decisionKey(artifact), artifact.installName]));
6210
+ const finalOverrides = overrides.map((override) => ({
6211
+ ...override,
6212
+ installName: finalInstallNames.get(`${override.graphNodeId}\0${override.type}\0${override.name}`) ?? override.installName
6213
+ }));
6214
+ return { artifacts: out, namespacing: [...decisions.values()], overrides: finalOverrides };
6215
+ }
6216
+ function applyWorkspaceOverrides(graph, artifacts) {
6217
+ const directives = workspaceOverrides(graph);
6218
+ if (directives.length === 0) return { artifacts, overrides: [] };
6219
+ const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
6220
+ let remaining = [...artifacts];
6221
+ const decisions = [];
6222
+ for (const directive of directives) {
6223
+ const matched = remaining.filter((artifact) => overrideMatchesArtifact(directive.selector, artifact, nodeById.get(artifact.graphNodeId)));
6224
+ const losers = matched.filter((artifact) => !directive.reachable.has(artifact.graphNodeId));
6225
+ if (matched.length === 0) {
6226
+ throw new Error(`Workspace override ${directive.selector} from ${directive.rootId} did not match any rendered artifact.`);
6227
+ }
6228
+ if (losers.length === 0) {
6229
+ throw new Error(`Workspace override ${directive.selector} from ${directive.rootId} matched only artifacts inside the replacing root.`);
6230
+ }
6231
+ if (losers.length > 1) {
6232
+ throw new Error(`Workspace override ${directive.selector} from ${directive.rootId} matched multiple artifacts: ${losers.map((artifact) => artifact.logicalSelector).sort().join(", ")}`);
6233
+ }
6234
+ const loser = losers[0];
6235
+ const winners = remaining.filter((artifact) => artifact !== loser && directive.reachable.has(artifact.graphNodeId) && artifact.type === loser.type && artifact.name === loser.name);
6236
+ if (winners.length === 0) {
6237
+ throw new Error(`Workspace override ${directive.selector} from ${directive.rootId} has no selected replacement for ${loser.type}/${loser.name}.`);
6238
+ }
6239
+ if (winners.length > 1) {
6240
+ throw new Error(`Workspace override ${directive.selector} from ${directive.rootId} has multiple selected replacements for ${loser.type}/${loser.name}: ${winners.map((artifact) => artifact.logicalSelector).sort().join(", ")}`);
6241
+ }
6242
+ const winner = winners[0];
6243
+ remaining = remaining.filter((artifact) => artifact !== loser);
6244
+ decisions.push({
6245
+ rootId: directive.rootId,
6246
+ selector: directive.selector,
6247
+ graphNodeId: winner.graphNodeId,
6248
+ overriddenGraphNodeId: loser.graphNodeId,
6249
+ type: winner.type,
6250
+ name: winner.name,
6251
+ installName: winner.installName
6252
+ });
6253
+ }
6254
+ return { artifacts: remaining, overrides: decisions };
5332
6255
  }
5333
6256
  function namespaceTransitiveGroup(group, used) {
5334
6257
  const sorted = [...group].sort((a, b) => a.logicalSelector.localeCompare(b.logicalSelector));
@@ -5366,6 +6289,16 @@ function workspaceAliases(graph) {
5366
6289
  }
5367
6290
  return aliases;
5368
6291
  }
6292
+ function workspaceOverrides(graph) {
6293
+ const overrides = [];
6294
+ for (const root of graph.roots) {
6295
+ const reachable = reachableNodeIds(graph, root.graphNodeId);
6296
+ for (const selector of root.overrides ?? []) {
6297
+ overrides.push({ rootId: root.rootId, selector, reachable });
6298
+ }
6299
+ }
6300
+ return overrides;
6301
+ }
5369
6302
  function validateAliasScopes(graph, artifacts, aliases) {
5370
6303
  for (const alias of aliases) {
5371
6304
  const matching = artifacts.filter((artifact) => {
@@ -5382,6 +6315,35 @@ function aliasMatchesArtifact(selector, artifact, node) {
5382
6315
  const artifactSelector = `${artifact.type}/${artifact.name}`;
5383
6316
  return selector === `${artifact.graphNodeId}:${artifactSelector}` || node !== void 0 && selector === `${node.name}@${node.version}:${artifactSelector}` || node !== void 0 && selector === `${node.name}:${artifactSelector}`;
5384
6317
  }
6318
+ function overrideMatchesArtifact(selector, artifact, node) {
6319
+ const sourceSeparator = selector.lastIndexOf("::");
6320
+ if (sourceSeparator >= 0) {
6321
+ const sourceSelector = selector.slice(0, sourceSeparator).trim();
6322
+ const artifactSelector = selector.slice(sourceSeparator + 2).trim();
6323
+ return artifactSelector === `${artifact.type}/${artifact.name}` && sourceMatchesArtifact(sourceSelector, node);
6324
+ }
6325
+ return aliasMatchesArtifact(selector, artifact, node);
6326
+ }
6327
+ function sourceMatchesArtifact(selector, node) {
6328
+ if (!node || selector.length === 0) return false;
6329
+ if (selector === node.source || selector === node.normalizedSource || selector === node.name || selector === node.id) return true;
6330
+ const normalized = node.normalizedSource.toLowerCase();
6331
+ const source = node.source.toLowerCase();
6332
+ const value = selector.toLowerCase();
6333
+ if (value === source || value === normalized || value === node.name.toLowerCase() || value === node.id.toLowerCase()) return true;
6334
+ const github = /^github:([^#]+?)(?:#(.+))?$/.exec(value);
6335
+ if (github) {
6336
+ const repo = github[1].replace(/\.git$/i, "");
6337
+ const ref = github[2];
6338
+ const prefix = `git:https://github.com/${repo}.git#`;
6339
+ return ref ? normalized.includes(`${prefix}${ref}`) : normalized.includes(prefix);
6340
+ }
6341
+ if (!value.includes(":") && value.includes("/")) {
6342
+ const repo = value.replace(/\.git$/i, "");
6343
+ return normalized.includes(`github.com/${repo}.git#`) || source.includes(`github.com/${repo}.git`);
6344
+ }
6345
+ return false;
6346
+ }
5385
6347
  function reachableNodeIds(graph, rootNodeId) {
5386
6348
  const reachable = /* @__PURE__ */ new Set();
5387
6349
  const queue = [rootNodeId];
@@ -5455,9 +6417,9 @@ function lockArtifactFor(artifact) {
5455
6417
  }
5456
6418
 
5457
6419
  // src/lifecycle/trust.ts
5458
- import { mkdir as mkdir11, readFile as readFile15 } from "fs/promises";
5459
- import { homedir as homedir6 } from "os";
5460
- import { dirname as dirname16, join as join21 } from "path";
6420
+ import { mkdir as mkdir13, readFile as readFile18 } from "fs/promises";
6421
+ import { homedir as homedir7 } from "os";
6422
+ import { dirname as dirname18, join as join24 } from "path";
5461
6423
  import { z as z8 } from "zod";
5462
6424
  var trustStoreSchema = z8.object({
5463
6425
  version: z8.literal(1),
@@ -5531,14 +6493,14 @@ function sortedUnique4(values) {
5531
6493
  }
5532
6494
  async function readTrustStore(path) {
5533
6495
  if (!await pathExists(path)) return { version: 1, acceptedSources: [] };
5534
- return trustStoreSchema.parse(JSON.parse(await readFile15(path, "utf8")));
6496
+ return trustStoreSchema.parse(JSON.parse(await readFile18(path, "utf8")));
5535
6497
  }
5536
6498
  async function writeTrustStore(path, store) {
5537
- await mkdir11(dirname16(path), { recursive: true });
6499
+ await mkdir13(dirname18(path), { recursive: true });
5538
6500
  await writeJsonAtomic(path, trustStoreSchema.parse(store));
5539
6501
  }
5540
6502
  function defaultTrustStorePath() {
5541
- return process.env.AGENTWHEEL_TRUST_STORE ?? join21(homedir6(), ".agentwheel", "trust.json");
6503
+ return process.env.AGENTWHEEL_TRUST_STORE ?? join24(homedir7(), ".agentwheel", "trust.json");
5542
6504
  }
5543
6505
 
5544
6506
  // src/lifecycle/source-plan.ts
@@ -5553,7 +6515,17 @@ async function createGraphSourcePlan(options) {
5553
6515
  warnings.push(message);
5554
6516
  options.warn?.(message);
5555
6517
  };
5556
- const recoveredPendingApply = options.readOnly === true ? false : await recoverPendingApplyIfSafe(options.targetRoot, options.adapter.name, transport);
6518
+ const installationType = options.installationType ?? resolveInstallationTypeForAdapterTarget(options.adapter);
6519
+ const targetFingerprint = computeTargetFingerprint(options.targetFingerprintParts ?? {
6520
+ adapter: options.adapter.name,
6521
+ installationType,
6522
+ targetRoot: options.targetRoot,
6523
+ transport: transport.kind,
6524
+ transportDescription: transport.description
6525
+ });
6526
+ const stateKey = options.stateKey ?? stateKeyFor(options.adapter.name, { installationType, targetFingerprint });
6527
+ const installRoot = installRootForAdapterInstallationType(options.adapter, options.targetRoot, installationType, transport.kind === "ssh");
6528
+ const recoveredPendingApply = options.readOnly === true ? false : await recoverPendingApplyIfSafe(installRoot, options.adapter.name, transport, { installationType, stateKey });
5557
6529
  const workspaceConfig = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: options.globalRoot });
5558
6530
  const trustPolicy = {
5559
6531
  ...normalizeTrustPolicy(workspaceConfig.trust),
@@ -5561,18 +6533,12 @@ async function createGraphSourcePlan(options) {
5561
6533
  };
5562
6534
  const lockMode = options.frozenLock === true || options.offline === true;
5563
6535
  const lockLabel = options.offline === true ? "Offline" : options.frozenLock === true ? "Frozen lock" : options.lockedResolution === true ? "Locked install" : "Fresh resolve";
5564
- const targetFingerprint = computeTargetFingerprint(options.targetFingerprintParts ?? {
5565
- adapter: options.adapter.name,
5566
- targetRoot: options.targetRoot,
5567
- transport: transport.kind,
5568
- transportDescription: transport.description
5569
- });
5570
6536
  const graphLockPath = pathForGraphLock(workspaceRoot, options.targetKey ?? "default", options.adapter.name, targetFingerprint);
5571
6537
  const previousLock = await readExistingGraphLock(graphLockPath);
5572
6538
  const registryClient = new RegistryClient({ workspaceRoot, offline: lockMode, offlineLabel: lockLabel, warn });
5573
6539
  const graph = await resolveDependencyGraph(options.roots, {
5574
6540
  workspaceRoot,
5575
- cacheRoot: join22(workspaceRoot, ".agentwheel", "cache"),
6541
+ cacheRoot: join25(workspaceRoot, ".agentwheel", "cache"),
5576
6542
  registryClient,
5577
6543
  noDeps: options.noDeps,
5578
6544
  lockedResolution: options.lockedResolution,
@@ -5594,13 +6560,20 @@ async function createGraphSourcePlan(options) {
5594
6560
  targetFingerprint
5595
6561
  });
5596
6562
  const desiredArtifacts = desiredArtifactsFromGraphBundle(bundle);
6563
+ const resolvedInstallationType = resolveInstallationTypeForArtifacts(options.adapter, desiredArtifacts.map((artifact) => artifact.type), installationType);
6564
+ const resolvedInstallRoot = installRootForArtifacts(options.adapter, options.targetRoot, resolvedInstallationType, desiredArtifacts.map((artifact) => artifact.type), transport.kind === "ssh");
5597
6565
  const graphLockDigest = digestGraphLock(bundle.graphLock);
5598
6566
  const graphDiff = diffGraphLocks(previousLock, bundle.graphLock);
5599
- const manifest = await readInstallManifest(options.targetRoot, options.adapter.name, transport);
6567
+ const manifest = await readInstallManifest(resolvedInstallRoot, options.adapter.name, transport, { installationType: resolvedInstallationType, stateKey });
5600
6568
  const plan = await createCombinedInstallPlan(desiredArtifacts, options.adapter, options.targetRoot, manifest, transport, {
5601
6569
  baseRevision: manifest?.revision ?? null,
5602
6570
  graphLockDigest,
5603
- workspaceOwner: workspaceOwnerId(workspaceRoot)
6571
+ workspaceOwner: workspaceOwnerId(workspaceRoot),
6572
+ installationType: resolvedInstallationType,
6573
+ stateKey,
6574
+ forceDrift: options.forceDrift,
6575
+ forceConflict: options.forceConflict,
6576
+ replaceConflict: options.replaceConflict
5604
6577
  });
5605
6578
  return {
5606
6579
  plan,
@@ -5635,28 +6608,38 @@ function desiredArtifactFromResolved(artifact) {
5635
6608
  }
5636
6609
  };
5637
6610
  }
5638
- async function recoverPendingApplyIfSafe(targetRoot, adapter, transport) {
5639
- if (!await readApplyJournal(targetRoot, adapter, transport)) return false;
6611
+ async function recoverPendingApplyIfSafe(targetRoot, adapter, transport, scope) {
6612
+ if (!await readApplyJournal(targetRoot, adapter, transport, scope)) return false;
5640
6613
  try {
5641
- await recoverPendingApply(targetRoot, adapter, transport);
6614
+ await recoverPendingApply(targetRoot, adapter, transport, scope);
5642
6615
  return true;
5643
6616
  } catch (error) {
5644
6617
  const message = error instanceof Error ? error.message : String(error);
5645
6618
  throw new Error(`Pending apply journal for ${adapter} at ${targetRoot} could not be recovered automatically: ${message}`);
5646
6619
  }
5647
6620
  }
6621
+ function resolveInstallationTypeForAdapterTarget(adapter) {
6622
+ const supported = /* @__PURE__ */ new Set();
6623
+ for (const registry of Object.values(adapter.targets)) {
6624
+ for (const [installationType, target] of Object.entries(registry ?? {})) {
6625
+ if (target.enabled) supported.add(installationType);
6626
+ }
6627
+ }
6628
+ if (supported.size === 1) return [...supported][0];
6629
+ return "local";
6630
+ }
5648
6631
  async function readExistingGraphLock(path) {
5649
6632
  if (!await pathExists(path)) return void 0;
5650
6633
  return readGraphLock(path);
5651
6634
  }
5652
6635
  function pathForGraphLock(workspaceRoot, targetKey, adapter, targetFingerprint) {
5653
- return join22(workspaceRoot, ".agentwheel", "locks", sanitizePathSegment(targetKey), sanitizePathSegment(adapter), `${targetFingerprint}.graph-lock.json`);
6636
+ return join25(workspaceRoot, ".agentwheel", "locks", sanitizePathSegment(targetKey), sanitizePathSegment(adapter), `${targetFingerprint}.graph-lock.json`);
5654
6637
  }
5655
6638
  function sanitizePathSegment(value) {
5656
6639
  return value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "default";
5657
6640
  }
5658
6641
  function digestGraphLock(lock) {
5659
- return createHash6("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
6642
+ return createHash7("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
5660
6643
  }
5661
6644
  function workspaceOwnerId(workspaceRoot) {
5662
6645
  return `workspace-root:${resolve14(workspaceRoot)}`;
@@ -5713,128 +6696,8 @@ Type yes to continue: `);
5713
6696
  ${sources.map((source) => `- ${source}`).join("\n")}`);
5714
6697
  }
5715
6698
 
5716
- // src/lifecycle/profile.ts
5717
- async function syncProfile(options) {
5718
- const config = await readMergedWorkspaceConfig(options.workspaceRoot);
5719
- const profile = config.profiles[options.profile];
5720
- if (!profile) {
5721
- throw new Error(`Unknown profile: ${options.profile}`);
5722
- }
5723
- const packages = options.source ? [await packageFromSource(options.source, options)] : config.packages;
5724
- if (packages.length === 0) {
5725
- throw new Error("Profile sync needs a source argument or configured packages.");
5726
- }
5727
- const results = [];
5728
- for (const runtime of profile.runtimes) {
5729
- const target = resolveProfileRuntime(runtime, config, options.workspaceRoot);
5730
- const adapter = await resolveAdapter({
5731
- adapter: target.adapter,
5732
- adapterConfig: runtime.adapterConfig,
5733
- adapterModule: runtime.adapterModule,
5734
- allowAdapterCode: options.allowAdapterCode,
5735
- baseDir: options.workspaceRoot,
5736
- warn: options.warn
5737
- });
5738
- const selected = normalizeArtifactSelectors(options.select, options.skills);
5739
- const graphPlan = await createGraphSourcePlan({
5740
- roots: packages.map((pkg) => ({
5741
- rootId: pkg.name,
5742
- source: pkg.source,
5743
- mode: options.mode ?? pkg.mode,
5744
- ref: pkg.requestedRef,
5745
- select: selected ?? normalizeArtifactSelectors(pkg.select, pkg.skills),
5746
- aliases: pkg.aliases
5747
- })),
5748
- targetRoot: target.targetRoot,
5749
- workspaceRoot: options.workspaceRoot,
5750
- adapter,
5751
- transport: target.transport,
5752
- targetKey: runtime.agent ?? adapter.name,
5753
- targetFingerprintParts: {
5754
- adapter: adapter.name,
5755
- adapterConfig: runtime.adapterConfig,
5756
- adapterModule: runtime.adapterModule,
5757
- adapterCodeHash: adapter.programmatic?.hash,
5758
- targetRoot: target.targetRoot,
5759
- transport: target.transport.kind
5760
- },
5761
- noDeps: options.noDeps,
5762
- lockedResolution: options.lockedResolution,
5763
- frozenLock: options.frozenLock,
5764
- offline: options.offline,
5765
- yes: options.yes,
5766
- trustPatterns: options.trustPatterns ?? [],
5767
- readOnly: options.readOnly,
5768
- isTTY: options.isTTY,
5769
- warn: options.warn
5770
- });
5771
- try {
5772
- results.push({
5773
- runtime: adapter.name,
5774
- targetRoot: target.targetRoot,
5775
- transport: target.transport.kind,
5776
- packageName: packages.map((pkg) => pkg.name).join(","),
5777
- plan: graphPlan.plan
5778
- });
5779
- if (!options.dryRun) {
5780
- await applyCombinedInstallPlan(graphPlan.plan, {
5781
- executePlugins: runtime.executePlugins ?? options.executePlugins,
5782
- transport: target.transport,
5783
- graphLockDigest: graphPlan.graphLockDigest,
5784
- graphLock: { path: graphPlan.graphLockPath, lock: graphPlan.bundle.graphLock }
5785
- });
5786
- }
5787
- } finally {
5788
- await rm8(graphPlan.bundle.root, { recursive: true, force: true });
5789
- }
5790
- }
5791
- return results;
5792
- }
5793
- function resolveProfileRuntime(runtime, config, workspaceRoot) {
5794
- if (runtime.agent) {
5795
- const agent = config.agents[runtime.agent];
5796
- if (!agent) throw new Error(`Unknown agent in profile: ${runtime.agent}`);
5797
- const target = {
5798
- agentName: runtime.agent,
5799
- adapter: agent.adapter,
5800
- targetRoot: agent.transport === "ssh" ? agent.root : resolveConfigPath(agent.root, workspaceRoot),
5801
- workspaceRoot,
5802
- transport: agent.transport,
5803
- ssh: agent.transport === "ssh" ? {
5804
- host: agent.host ?? "",
5805
- user: agent.user,
5806
- port: agent.port,
5807
- identityFile: agent.identityFile ? resolveConfigPath(agent.identityFile, workspaceRoot) : void 0
5808
- } : void 0,
5809
- source: "agent"
5810
- };
5811
- return { adapter: target.adapter, targetRoot: target.targetRoot, transport: transportForTarget(target) };
5812
- }
5813
- return {
5814
- adapter: runtime.adapter,
5815
- targetRoot: runtime.targetRoot ? resolveConfigPath(runtime.targetRoot, workspaceRoot) : workspaceRoot,
5816
- transport: localTransport
5817
- };
5818
- }
5819
- async function packageFromSource(source, options) {
5820
- const resolved = await resolvePackageSource(source, options.workspaceRoot, {
5821
- offline: options.frozenLock === true || options.offline === true,
5822
- warn: options.warn
5823
- });
5824
- const driver = options.driver ?? inferSourceDriverName(resolved.source);
5825
- return {
5826
- name: resolved.registryEntry?.name ?? source,
5827
- source: resolved.source,
5828
- driver,
5829
- adapter: "openclaw",
5830
- mode: options.mode ?? "pinned",
5831
- select: options.select,
5832
- skills: options.skills
5833
- };
5834
- }
5835
-
5836
6699
  // src/runtime/target.ts
5837
- import { basename as basename12, dirname as dirname18, join as join23, resolve as resolve15 } from "path";
6700
+ import { basename as basename15, dirname as dirname20, join as join26, resolve as resolve15 } from "path";
5838
6701
  var runtimeMarkers = [
5839
6702
  { adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
5840
6703
  { adapter: "claude", dirs: [".claude"] },
@@ -5848,6 +6711,7 @@ async function resolveRuntimeTarget(request = {}) {
5848
6711
  const targetRoot = resolve15(request.targetRoot);
5849
6712
  return {
5850
6713
  adapter: request.adapter ?? "openclaw",
6714
+ installationType: request.installationType,
5851
6715
  targetRoot,
5852
6716
  workspaceRoot: targetRoot,
5853
6717
  transport: "local",
@@ -5857,14 +6721,15 @@ async function resolveRuntimeTarget(request = {}) {
5857
6721
  const workspaceRoot = await findWorkspaceRoot(cwd);
5858
6722
  const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
5859
6723
  if (request.agent) {
5860
- return targetFromAgent(request.agent, config, workspaceRoot);
6724
+ return targetFromAgent(request.agent, config, workspaceRoot, request.installationType);
5861
6725
  }
5862
6726
  const detected = await detectRuntimeTarget(cwd, request.adapter);
5863
6727
  if (detected) {
5864
- return { ...detected, workspaceRoot: await findWorkspaceRoot(detected.targetRoot), transport: "local", source: "auto-detect" };
6728
+ return { ...detected, installationType: request.installationType, workspaceRoot: await findWorkspaceRoot(detected.targetRoot), transport: "local", source: "auto-detect" };
5865
6729
  }
5866
6730
  return {
5867
6731
  adapter: request.adapter ?? "openclaw",
6732
+ installationType: request.installationType,
5868
6733
  targetRoot: cwd,
5869
6734
  workspaceRoot,
5870
6735
  transport: "local",
@@ -5877,12 +6742,44 @@ async function resolveAllRuntimeTargets(request = {}) {
5877
6742
  const cwd = resolve15(request.cwd ?? process.cwd());
5878
6743
  const workspaceRoot = await findWorkspaceRoot(cwd);
5879
6744
  const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
5880
- const targets = Object.entries(config.agents).map(([name]) => targetFromAgent(name, config, workspaceRoot));
6745
+ const targets = Object.entries(config.agents).map(([name]) => targetFromAgent(name, config, workspaceRoot, request.installationType));
5881
6746
  if (targets.length === 0) {
5882
6747
  throw new Error("No agents configured. Add agents to .agentwheel/config.json or pass --target-root.");
5883
6748
  }
5884
6749
  return targets;
5885
6750
  }
6751
+ async function resolveProfileRuntimeTargets(request) {
6752
+ const cwd = resolve15(request.cwd ?? process.cwd());
6753
+ const workspaceRoot = request.targetRoot ? resolve15(request.targetRoot) : await findWorkspaceRoot(cwd);
6754
+ const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
6755
+ const profile = config.profiles[request.profile];
6756
+ if (!profile) {
6757
+ throw new Error(`Unknown profile: ${request.profile}`);
6758
+ }
6759
+ return profile.runtimes.map((runtime) => resolveProfileRuntimeTarget(runtime, config, workspaceRoot, request.installationType));
6760
+ }
6761
+ function resolveProfileRuntimeTarget(runtime, config, workspaceRoot, installationType) {
6762
+ if (runtime.agent) {
6763
+ const target = targetFromAgent(runtime.agent, config, workspaceRoot, installationType ?? runtime.installationType);
6764
+ return {
6765
+ ...target,
6766
+ adapterConfig: runtime.adapterConfig,
6767
+ adapterModule: runtime.adapterModule,
6768
+ targetKey: runtime.agent,
6769
+ source: "profile"
6770
+ };
6771
+ }
6772
+ return {
6773
+ adapter: runtime.adapter,
6774
+ adapterConfig: runtime.adapterConfig,
6775
+ adapterModule: runtime.adapterModule,
6776
+ installationType: installationType ?? runtime.installationType,
6777
+ targetRoot: runtime.targetRoot ? resolveConfigPath(runtime.targetRoot, workspaceRoot) : workspaceRoot,
6778
+ workspaceRoot,
6779
+ transport: "local",
6780
+ source: "profile"
6781
+ };
6782
+ }
5886
6783
  async function resolveAllDetectedRuntimeTargets(request = {}) {
5887
6784
  if (request.agent) return [await resolveRuntimeTarget(request)];
5888
6785
  const scanRoot = runtimeScanRoot(request);
@@ -5892,6 +6789,7 @@ async function resolveAllDetectedRuntimeTargets(request = {}) {
5892
6789
  }
5893
6790
  return Promise.all(matches.map(async (match) => ({
5894
6791
  ...match,
6792
+ installationType: request.installationType,
5895
6793
  workspaceRoot: await findWorkspaceRoot(match.targetRoot),
5896
6794
  transport: "local",
5897
6795
  source: "auto-detect"
@@ -5910,23 +6808,25 @@ async function detectRuntimeTargets(cwd = process.cwd(), adapterFilter) {
5910
6808
  for (const marker of runtimeMarkers) {
5911
6809
  if (adapterFilter && marker.adapter !== adapterFilter) continue;
5912
6810
  for (const dir of marker.dirs) {
5913
- if (basename12(root) === dir) {
5914
- matches.push({ adapter: marker.adapter, targetRoot: dirname18(root) });
5915
- } else if (await pathExists(join23(root, dir))) {
6811
+ if (basename15(root) === dir) {
6812
+ matches.push({ adapter: marker.adapter, targetRoot: dirname20(root) });
6813
+ } else if (await pathExists(join26(root, dir))) {
5916
6814
  matches.push({ adapter: marker.adapter, targetRoot: root });
5917
6815
  }
5918
6816
  }
5919
6817
  }
5920
6818
  return dedupeTargets(matches);
5921
6819
  }
5922
- function targetFromAgent(name, config, workspaceRoot) {
6820
+ function targetFromAgent(name, config, workspaceRoot, installationType) {
5923
6821
  const agent = config.agents[name];
5924
6822
  if (!agent) {
5925
6823
  throw new Error(`Unknown agent: ${name}`);
5926
6824
  }
5927
6825
  return {
5928
6826
  agentName: name,
6827
+ targetKey: name,
5929
6828
  adapter: agent.adapter,
6829
+ installationType: installationType ?? agent.installationType,
5930
6830
  targetRoot: agent.transport === "ssh" ? agent.root : resolveConfigPath(agent.root, workspaceRoot),
5931
6831
  workspaceRoot,
5932
6832
  transport: agent.transport,
@@ -5949,13 +6849,119 @@ function dedupeTargets(matches) {
5949
6849
  function runtimeScanRoot(request) {
5950
6850
  const root = resolve15(request.targetRoot ?? request.cwd ?? process.cwd());
5951
6851
  if (request.targetRoot) return root;
5952
- return runtimeMarkers.some((marker) => marker.dirs.includes(basename12(root))) ? dirname18(root) : root;
6852
+ return runtimeMarkers.some((marker) => marker.dirs.includes(basename15(root))) ? dirname20(root) : root;
6853
+ }
6854
+
6855
+ // src/lifecycle/profile.ts
6856
+ async function syncProfile(options) {
6857
+ const config = await readMergedWorkspaceConfig(options.workspaceRoot);
6858
+ const profile = config.profiles[options.profile];
6859
+ if (!profile) {
6860
+ throw new Error(`Unknown profile: ${options.profile}`);
6861
+ }
6862
+ const packages = options.source ? [await packageFromSource(options.source, options)] : config.packages;
6863
+ if (packages.length === 0) {
6864
+ throw new Error("Profile sync needs a source argument or configured packages.");
6865
+ }
6866
+ const results = [];
6867
+ for (const runtime of profile.runtimes) {
6868
+ const target = resolveProfileRuntimeTarget(runtime, config, options.workspaceRoot, options.installationType);
6869
+ const transport = transportForTarget(target);
6870
+ const adapter = await resolveAdapter({
6871
+ adapter: target.adapter,
6872
+ adapterConfig: target.adapterConfig,
6873
+ adapterModule: target.adapterModule,
6874
+ allowAdapterCode: options.allowAdapterCode,
6875
+ baseDir: options.workspaceRoot,
6876
+ warn: options.warn
6877
+ });
6878
+ const installationType = target.installationType ?? defaultInstallationType;
6879
+ resolveInstallationTypeForAdapter(adapter, installationType);
6880
+ const selected = normalizeArtifactSelectors(options.select, options.skills);
6881
+ const graphPlan = await createGraphSourcePlan({
6882
+ roots: packages.map((pkg) => ({
6883
+ rootId: pkg.name,
6884
+ source: pkg.source,
6885
+ mode: options.mode ?? pkg.mode,
6886
+ ref: pkg.requestedRef,
6887
+ select: selected ?? normalizeArtifactSelectors(pkg.select, pkg.skills),
6888
+ aliases: pkg.aliases,
6889
+ overrides: pkg.overrides
6890
+ })),
6891
+ targetRoot: target.targetRoot,
6892
+ workspaceRoot: options.workspaceRoot,
6893
+ adapter,
6894
+ transport,
6895
+ targetKey: target.targetKey ?? target.agentName ?? adapter.name,
6896
+ targetFingerprintParts: {
6897
+ adapter: adapter.name,
6898
+ installationType,
6899
+ adapterConfig: target.adapterConfig,
6900
+ adapterModule: target.adapterModule,
6901
+ adapterCodeHash: adapter.programmatic?.hash,
6902
+ agentName: target.agentName,
6903
+ targetRoot: target.targetRoot,
6904
+ transport: transport.kind,
6905
+ ssh: target.ssh
6906
+ },
6907
+ installationType,
6908
+ noDeps: options.noDeps,
6909
+ lockedResolution: options.lockedResolution,
6910
+ frozenLock: options.frozenLock,
6911
+ offline: options.offline,
6912
+ yes: options.yes,
6913
+ trustPatterns: options.trustPatterns ?? [],
6914
+ readOnly: options.readOnly,
6915
+ isTTY: options.isTTY,
6916
+ warn: options.warn,
6917
+ forceDrift: options.forceDrift,
6918
+ forceConflict: options.forceConflict,
6919
+ replaceConflict: options.replaceConflict
6920
+ });
6921
+ try {
6922
+ results.push({
6923
+ runtime: adapter.name,
6924
+ targetRoot: installRootForAdapterInstallationType(adapter, target.targetRoot, installationType, transport.kind === "ssh"),
6925
+ transport: transport.kind,
6926
+ packageName: packages.map((pkg) => pkg.name).join(","),
6927
+ plan: graphPlan.plan
6928
+ });
6929
+ if (!options.dryRun) {
6930
+ await applyCombinedInstallPlan(graphPlan.plan, {
6931
+ executePlugins: runtime.executePlugins ?? options.executePlugins,
6932
+ transport,
6933
+ graphLockDigest: graphPlan.graphLockDigest,
6934
+ graphLock: { path: graphPlan.graphLockPath, lock: graphPlan.bundle.graphLock }
6935
+ });
6936
+ }
6937
+ } finally {
6938
+ await rm8(graphPlan.bundle.root, { recursive: true, force: true });
6939
+ }
6940
+ }
6941
+ return results;
6942
+ }
6943
+ async function packageFromSource(source, options) {
6944
+ const resolved = await resolvePackageSource(source, options.workspaceRoot, {
6945
+ offline: options.frozenLock === true || options.offline === true,
6946
+ warn: options.warn
6947
+ });
6948
+ const driver = options.driver ?? inferSourceDriverName(resolved.source);
6949
+ return {
6950
+ name: resolved.registryEntry?.name ?? source,
6951
+ source: resolved.source,
6952
+ driver,
6953
+ adapter: "openclaw",
6954
+ installationType: options.installationType,
6955
+ mode: options.mode ?? "pinned",
6956
+ select: options.select,
6957
+ skills: options.skills
6958
+ };
5953
6959
  }
5954
6960
 
5955
6961
  // src/cli/update-check.ts
5956
- import { mkdir as mkdir13, readFile as readFile16, writeFile as writeFile10 } from "fs/promises";
5957
- import { homedir as homedir7 } from "os";
5958
- import { dirname as dirname19, join as join24 } from "path";
6962
+ import { mkdir as mkdir15, readFile as readFile19, writeFile as writeFile12 } from "fs/promises";
6963
+ import { homedir as homedir8 } from "os";
6964
+ import { dirname as dirname21, join as join27 } from "path";
5959
6965
  var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
5960
6966
  var DEFAULT_TIMEOUT_MS = 300;
5961
6967
  var REGISTRY_URL = "https://registry.npmjs.org/agentwheel";
@@ -5963,7 +6969,7 @@ async function maybeCheckForUpdate(options) {
5963
6969
  if (isDisabled(options)) return;
5964
6970
  const now = options.now?.() ?? /* @__PURE__ */ new Date();
5965
6971
  const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
5966
- const cachePath = options.cachePath ?? join24(homedir7(), ".agentwheel", "update-check.json");
6972
+ const cachePath = options.cachePath ?? join27(homedir8(), ".agentwheel", "update-check.json");
5967
6973
  try {
5968
6974
  const cached = await readCache(cachePath);
5969
6975
  if (cached && now.getTime() - Date.parse(cached.checkedAt) < ttlMs) {
@@ -6000,7 +7006,7 @@ async function fetchLatestVersion(fetchImpl, timeoutMs) {
6000
7006
  }
6001
7007
  async function readCache(path) {
6002
7008
  try {
6003
- const parsed = JSON.parse(await readFile16(path, "utf8"));
7009
+ const parsed = JSON.parse(await readFile19(path, "utf8"));
6004
7010
  if (typeof parsed.checkedAt !== "string" || typeof parsed.latest !== "string") return void 0;
6005
7011
  return { checkedAt: parsed.checkedAt, latest: parsed.latest };
6006
7012
  } catch {
@@ -6008,8 +7014,8 @@ async function readCache(path) {
6008
7014
  }
6009
7015
  }
6010
7016
  async function writeCache(path, cache) {
6011
- await mkdir13(dirname19(path), { recursive: true });
6012
- await writeFile10(path, `${JSON.stringify(cache, null, 2)}
7017
+ await mkdir15(dirname21(path), { recursive: true });
7018
+ await writeFile12(path, `${JSON.stringify(cache, null, 2)}
6013
7019
  `, "utf8");
6014
7020
  }
6015
7021
  function warnIfNewer(latest, current, stderr = process.stderr) {
@@ -6155,13 +7161,13 @@ function isCrossPackageSelector(value) {
6155
7161
  }
6156
7162
 
6157
7163
  // src/model/package-migrate.ts
6158
- import { readFile as readFile17, rename as rename4, writeFile as writeFile11 } from "fs/promises";
6159
- import { join as join26, resolve as resolve17 } from "path";
7164
+ import { readFile as readFile20, rename as rename4, writeFile as writeFile13 } from "fs/promises";
7165
+ import { join as join29, resolve as resolve17 } from "path";
6160
7166
  import { applyEdits, modify, parse as parse3 } from "jsonc-parser";
6161
7167
  async function migratePackageManifest(root) {
6162
7168
  const packageRoot = resolve17(root);
6163
7169
  for (const name of openPackManifestNames) {
6164
- const path = join26(packageRoot, name);
7170
+ const path = join29(packageRoot, name);
6165
7171
  if (await pathExists(path)) {
6166
7172
  return { changed: false, to: path, message: `Package already uses ${name}.` };
6167
7173
  }
@@ -6170,18 +7176,18 @@ async function migratePackageManifest(root) {
6170
7176
  if (!legacyName) {
6171
7177
  throw new Error(`No legacy package manifest found at ${packageRoot}`);
6172
7178
  }
6173
- const from = join26(packageRoot, legacyName);
7179
+ const from = join29(packageRoot, legacyName);
6174
7180
  const toName = legacyName.endsWith(".jsonc") ? "openpack.jsonc" : "openpack.json";
6175
- const to = join26(packageRoot, toName);
6176
- const content = await readFile17(from, "utf8");
7181
+ const to = join29(packageRoot, toName);
7182
+ const content = await readFile20(from, "utf8");
6177
7183
  const updated = updateSchemaVersion(content);
6178
7184
  await rename4(from, to);
6179
- await writeFile11(to, updated, "utf8");
7185
+ await writeFile13(to, updated, "utf8");
6180
7186
  return { changed: true, from, to, message: `Migrated ${legacyName} to ${toName}.` };
6181
7187
  }
6182
7188
  async function firstExistingLegacyManifest(root) {
6183
7189
  for (const name of legacyPackageManifestNames) {
6184
- if (await pathExists(join26(root, name))) return name;
7190
+ if (await pathExists(join29(root, name))) return name;
6185
7191
  }
6186
7192
  return void 0;
6187
7193
  }
@@ -6199,20 +7205,20 @@ function updateSchemaVersion(content) {
6199
7205
 
6200
7206
  // src/cli/version.ts
6201
7207
  import { readFileSync } from "fs";
6202
- import { dirname as dirname20, join as join27 } from "path";
7208
+ import { dirname as dirname22, join as join30 } from "path";
6203
7209
  import { fileURLToPath as fileURLToPath2 } from "url";
6204
7210
  var FALLBACK_VERSION = "0.0.0";
6205
7211
  function resolveCliVersion() {
6206
- let dir = dirname20(fileURLToPath2(import.meta.url));
7212
+ let dir = dirname22(fileURLToPath2(import.meta.url));
6207
7213
  while (true) {
6208
7214
  try {
6209
- const pkg = JSON.parse(readFileSync(join27(dir, "package.json"), "utf8"));
7215
+ const pkg = JSON.parse(readFileSync(join30(dir, "package.json"), "utf8"));
6210
7216
  if (pkg.name === "agentwheel" && typeof pkg.version === "string") {
6211
7217
  return pkg.version;
6212
7218
  }
6213
7219
  } catch {
6214
7220
  }
6215
- const parent = dirname20(dir);
7221
+ const parent = dirname22(dir);
6216
7222
  if (parent === dir) return FALLBACK_VERSION;
6217
7223
  dir = parent;
6218
7224
  }
@@ -6228,7 +7234,7 @@ Core flow:
6228
7234
  $ agentwheel plan
6229
7235
  $ agentwheel install
6230
7236
  `);
6231
- program.command("init").description("initialize an agentwheel workspace or package").argument("[kind]", "workspace or package", "workspace").option("--target-root <path>", "workspace root", process.cwd()).option("--fleet-example", "scaffold example agents and profiles in workspace config", false).action(async (kind, options) => {
7237
+ program.command("init").description("initialize an agentwheel workspace or package").argument("[kind]", "workspace or package", "workspace").option("-t, --target-root <path>", "workspace root", process.cwd()).option("--fleet-example", "scaffold example agents and profiles in workspace config", false).action(async (kind, options) => {
6232
7238
  const root = normalizeTargetRoot(options.targetRoot);
6233
7239
  if (kind === "package") {
6234
7240
  await initPackage(root);
@@ -6246,28 +7252,29 @@ program.command("init").description("initialize an agentwheel workspace or packa
6246
7252
  if (bootstrapPackage) console.log("Auto-added the agentwheel bootstrap skill for openclaw.");
6247
7253
  console.log(nextInstallNudge());
6248
7254
  });
6249
- program.command("add").description("add a package to .agentwheel/config.json without touching runtimes").argument("<source>", "package source").option("--driver <driver>", "source driver (local, git, skillkit, or vercel-skills)").option("--adapter <adapter>", "built-in adapter", "openclaw").option("--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("--target-root <path>", "workspace root", process.cwd()).option("--mode <mode>", "pinned or tracking", "pinned").option("--name <name>", "package alias").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).action(async (source, options) => {
6250
- const targetRoot = normalizeTargetRoot(options.targetRoot);
6251
- const entry = await packageEntryFromSource(source, targetRoot, options);
7255
+ program.command("add").description("add a package to .agentwheel/config.json without touching runtimes").argument("<source>", "package source").option("--driver <driver>", "source driver (local, git, skillkit, or vercel-skills)").option("--adapter <adapter>", "built-in adapter", "openclaw").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "workspace root").option("--mode <mode>", "pinned or tracking", "pinned").option("--name <name>", "package alias").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "allow this package to replace a colliding artifact (repeatable)", collectOverrideOption, []).action(async (source, options) => {
7256
+ const normalizedOptions = normalizeRuntimeScopeOptions(options);
7257
+ const targetRoot = normalizeTargetRoot(normalizedOptions.targetRoot ?? process.cwd());
7258
+ const entry = await packageEntryFromSource(source, targetRoot, normalizedOptions);
6252
7259
  await writeWorkspaceConfig(targetRoot, upsertPackage(await readWorkspaceConfig(targetRoot), entry));
6253
7260
  console.log(`Added ${entry.name}. Preview: agentwheel plan - Apply: agentwheel install`);
6254
7261
  });
6255
- program.command("list").description("list artifacts exposed by a package source").argument("<source>", "package source").option("--driver <driver>", "source driver").option("--target-root <path>", "workspace root", process.cwd()).option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).action(async (source, options) => {
7262
+ program.command("list").description("list artifacts exposed by a package source").argument("<source>", "package source").option("--driver <driver>", "source driver").option("-t, --target-root <path>", "workspace root", process.cwd()).option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).action(async (source, options) => {
6256
7263
  const targetRoot = normalizeTargetRoot(options.targetRoot);
6257
7264
  const selectedArtifacts = selectedArtifactsFromOptions(options);
6258
7265
  const resolvedInput = await resolvePackageSource(source, targetRoot);
6259
7266
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
6260
- const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join28(targetRoot, ".agentwheel", "cache") }))));
7267
+ const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join31(targetRoot, ".agentwheel", "cache") }))));
6261
7268
  const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
6262
7269
  for (const artifact of artifacts) {
6263
7270
  console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
6264
7271
  }
6265
7272
  });
6266
- program.command("scan").description("scan a package source for validation findings").argument("<source>", "package source").option("--driver <driver>", "source driver").option("--target-root <path>", "workspace root", process.cwd()).action(async (source, options) => {
7273
+ 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) => {
6267
7274
  const targetRoot = normalizeTargetRoot(options.targetRoot);
6268
7275
  const resolvedInput = await resolvePackageSource(source, targetRoot);
6269
7276
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
6270
- const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join28(targetRoot, ".agentwheel", "cache") }))));
7277
+ const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join31(targetRoot, ".agentwheel", "cache") }))));
6271
7278
  const result = await driver.scan(resolved);
6272
7279
  if (result.findings.length === 0) {
6273
7280
  console.log("Scan ok: no findings");
@@ -6278,69 +7285,77 @@ program.command("scan").description("scan a package source for validation findin
6278
7285
  }
6279
7286
  if (!result.ok) process.exitCode = 1;
6280
7287
  });
6281
- 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").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("--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("--dry-run", "accepted for symmetry; plan never writes", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
7288
+ program.command("plan").description("preview what install would reconcile without writing").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--dry-run", "accepted for symmetry; plan never writes", false).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
6282
7289
  await runInstallCommand(source, { ...options, dryRun: true }, { apply: false });
6283
7290
  });
6284
- 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").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("--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("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).addHelpText("after", "\nScoped install never removes files owned only by other configured packages; run a full install to reconcile those removals.\n").action(async (source, options) => {
7291
+ program.command("install").description("install configured packages into runtime targets").argument("[name-or-source]", "configured package name/source or package source to add and install").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).addHelpText("after", "\nScoped install never removes files owned only by other configured packages; run a full install to reconcile those removals.\n").action(async (source, options) => {
6285
7292
  await runInstallCommand(source, options, { apply: !options.dryRun });
6286
7293
  });
6287
- 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").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("--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("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
7294
+ program.command("sync", { hidden: true }).argument("[name-or-source]", "configured package name/source or package source").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
6288
7295
  console.error("warning: 'agentwheel sync' is deprecated and will be removed in 0.10. Use 'agentwheel install'.");
6289
7296
  await runInstallCommand(source, options, { apply: !options.dryRun });
6290
7297
  });
6291
- 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").option("--target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--dry-run", "show plans without writing", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--allow-adapter-code", "allow loading local adapter code from configured packages", false).option("--select <type/name>", "temporarily select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "temporarily select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (name, options) => {
6292
- const targets = await resolveCliTargets(options);
7298
+ program.command("update").description("re-resolve tracking packages, then apply the result").argument("[name]", "configured package name or source to update").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plans without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--allow-adapter-code", "allow loading local adapter code from configured packages", false).option("--select <type/name>", "temporarily select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "temporarily select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (name, options) => {
7299
+ const normalizedOptions = normalizeRuntimeScopeOptions(options);
7300
+ const targets = await resolveCliTargets(normalizedOptions, { preferAllProfile: true });
6293
7301
  for (const target of targets) {
6294
- await runConfiguredGraphPackages(target, { ...options, scope: name }, { mode: "update" });
7302
+ await runConfiguredGraphPackages(target, { ...normalizedOptions, scope: name }, { mode: "update" });
6295
7303
  }
6296
7304
  });
6297
7305
  program.command("deps").description("inspect the OpenPack dependency graph").addCommand(
6298
- new Command("tree").description("print the OpenPack dependency graph").argument("[source]", "optional package source to resolve").option("--adapter <adapter>", "built-in adapter").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("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
6299
- const targets = await resolveCliTargets(options);
7306
+ new Command("tree").description("print the OpenPack dependency graph").argument("[source]", "optional package source to resolve").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
7307
+ const normalizedOptions = normalizeRuntimeScopeOptions(options, { defaultUser: shouldDefaultUserInstall(source, options) });
7308
+ const targets = await resolveCliTargets(normalizedOptions);
6300
7309
  for (const target of targets) {
6301
7310
  if (source) {
6302
- for (const result of await buildGraphPlansForTarget(target, source, options, { mode: "install" })) {
7311
+ for (const result of await buildGraphPlansForTarget(target, source, normalizedOptions, { mode: "install" })) {
6303
7312
  console.log(formatDependencyTree(result.graph).join("\n"));
6304
7313
  for (const decision of result.bundle.graphLock.canonical.namespacing) {
6305
7314
  console.log(`NAMESPACE ${decision.graphNodeId}:${decision.type}/${decision.name} -> ${decision.type}/${decision.installName} (${decision.reason})`);
6306
7315
  }
7316
+ for (const decision of result.bundle.graphLock.canonical.overrides) {
7317
+ console.log(`OVERRIDE ${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`);
7318
+ }
6307
7319
  await rm9(result.bundle.root, { recursive: true, force: true });
6308
7320
  }
6309
7321
  continue;
6310
7322
  }
6311
- const { lock } = await readTargetGraphLock(target, options);
7323
+ const { lock } = await readTargetGraphLock(target, normalizedOptions);
6312
7324
  console.log(formatLockDependencyTree(lock));
6313
7325
  }
6314
7326
  })
6315
7327
  ).addCommand(
6316
- new Command("why").description("explain why an artifact is installed").argument("<selector>", "installed path, type/installName, or graphNodeId:type/name").option("--adapter <adapter>", "built-in adapter").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("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).action(async (selector, options) => {
6317
- const targets = await resolveCliTargets(options);
7328
+ new Command("why").description("explain why an artifact is installed").argument("<selector>", "installed path, type/installName, or graphNodeId:type/name").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).action(async (selector, options) => {
7329
+ const normalizedOptions = normalizeRuntimeScopeOptions(options);
7330
+ const targets = await resolveCliTargets(normalizedOptions);
6318
7331
  for (const target of targets) {
6319
- const { lock, adapter } = await readTargetGraphLock(target, options);
6320
- const manifest = await readInstallManifest(target.targetRoot, adapter.name, transportForTarget(target));
7332
+ const { lock, adapter } = await readTargetGraphLock(target, normalizedOptions);
7333
+ const installationType = normalizedOptions.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter);
7334
+ const state = installStateForTarget(target, adapter, normalizedOptions, installationType);
7335
+ const manifest = await readInstallManifest(state.installRoot, adapter.name, transportForTarget(target), state);
6321
7336
  console.log(formatDepsWhy(lock, manifest, selector));
6322
7337
  }
6323
7338
  })
6324
7339
  );
6325
7340
  program.command("registry").description("manage optional registry indexes").addCommand(
6326
- new Command("update").description("refresh the local registry cache").option("--target-root <path>", "workspace root", process.cwd()).action(async (options) => {
7341
+ new Command("update").description("refresh the local registry cache").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (options) => {
6327
7342
  const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
6328
7343
  const index = await client.getIndex({ refresh: true });
6329
7344
  console.log(`Registry refreshed: ${index.entries.length} entries from ${index.sources.join(", ")}`);
6330
7345
  })
6331
7346
  ).addCommand(
6332
- new Command("list").description("list available registry entries").option("--target-root <path>", "workspace root", process.cwd()).action(async (options) => {
7347
+ new Command("list").description("list available registry entries").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (options) => {
6333
7348
  const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
6334
7349
  printRegistryEntries((await client.getIndex()).entries);
6335
7350
  })
6336
7351
  ).addCommand(
6337
- new Command("search").description("search registry entries").argument("<query>", "search query").option("--target-root <path>", "workspace root", process.cwd()).action(async (query, options) => {
7352
+ new Command("search").description("search registry entries").argument("<query>", "search query").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (query, options) => {
6338
7353
  const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
6339
7354
  printRegistryEntries(await client.search(query));
6340
7355
  })
6341
7356
  );
6342
7357
  program.command("trust").description("manage persisted source trust decisions").addCommand(
6343
- new Command("forget").description("forget a persisted trusted source pattern").argument("<pattern>", "trusted source glob to revoke").option("--target-root <path>", "workspace root", process.cwd()).action(async (pattern, options) => {
7358
+ new Command("forget").description("forget a persisted trusted source pattern").argument("<pattern>", "trusted source glob to revoke").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (pattern, options) => {
6344
7359
  const removed = await forgetTrustedSources(normalizeTargetRoot(options.targetRoot), pattern);
6345
7360
  if (removed.length === 0) {
6346
7361
  console.log(`No persisted trust matched ${pattern}.`);
@@ -6367,114 +7382,128 @@ program.command("package").description("validate and migrate OpenPack packages")
6367
7382
  console.log(result.message);
6368
7383
  })
6369
7384
  );
6370
- program.command("remember").description("append text to the local instructions overlay").requiredOption("--runtime <runtime>", "runtime/adapter name").option("--target-root <path>", "workspace root", process.cwd()).argument("<text>", "text to append to the local instructions overlay").action(async (text, options) => {
7385
+ program.command("remember").description("append text to the local instructions overlay").requiredOption("--runtime <runtime>", "runtime/adapter name").option("-t, --target-root <path>", "workspace root", process.cwd()).argument("<text>", "text to append to the local instructions overlay").action(async (text, options) => {
6371
7386
  const targetRoot = normalizeTargetRoot(options.targetRoot);
6372
7387
  const result = await remember(targetRoot, options.runtime, text);
6373
7388
  console.log(`Remembered in ${result.overlayPath}.`);
6374
7389
  console.log(nextInstallNudge());
6375
7390
  });
6376
- program.command("eject").description("copy a managed artifact into local ownership").argument("<item>", "package/type/name").option("--target-root <path>", "workspace root", process.cwd()).action(async (item, options) => {
7391
+ 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) => {
6377
7392
  const targetRoot = normalizeTargetRoot(options.targetRoot);
6378
7393
  const result = await ejectArtifact(targetRoot, item);
6379
7394
  console.log(`Ejected ${item} to ${result.ejectedPath}.`);
6380
7395
  console.log(nextInstallNudge());
6381
7396
  });
6382
- program.command("uninstall").description("remove configured packages or managed runtime files").argument("[package]", "configured package name or source to remove from the ownership graph").option("--adapter <adapter>", "adapter").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--dry-run", "show removals without writing", false).option("--force", "remove drifted managed files too", false).option("--keep-files", "remove from config and manifest but leave runtime files unmanaged", false).option("--select <type/name>", "uninstall only selected artifact type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "uninstall only selected skill name (repeatable or comma-separated)", collectSkillOption, []).option("--frozen-lock", "resolve remaining packages strictly from the existing graph lock and cached sources", false).option("--offline", "resolve remaining packages strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources while resolving remaining packages", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (packageName, options) => {
7397
+ program.command("uninstall").description("remove configured packages or managed runtime files").argument("[package]", "configured package name or source to remove from the ownership graph").option("--adapter <adapter>", "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-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("--dry-run", "show removals without writing", false).option("--force", "remove drifted managed files too", false).option("--keep-files", "remove from config and manifest but leave runtime files unmanaged", false).option("--select <type/name>", "uninstall only selected artifact type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "uninstall only selected skill name (repeatable or comma-separated)", collectSkillOption, []).option("--frozen-lock", "resolve remaining packages strictly from the existing graph lock and cached sources", false).option("--offline", "resolve remaining packages strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources while resolving remaining packages", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (packageName, options) => {
6383
7398
  if (options.keepFiles && options.force) {
6384
7399
  throw new Error("--keep-files cannot be combined with --force.");
6385
7400
  }
6386
- const targets = await resolveCliTargets(options);
7401
+ const normalizedOptions = normalizeRuntimeScopeOptions(options);
7402
+ const targets = await resolveCliTargets(normalizedOptions);
6387
7403
  for (const target of targets) {
6388
7404
  if (packageName) {
6389
- await uninstallConfiguredPackage(target, packageName, options);
7405
+ await uninstallConfiguredPackage(target, packageName, normalizedOptions);
6390
7406
  continue;
6391
7407
  }
6392
- if (options.keepFiles) {
7408
+ if (normalizedOptions.keepFiles) {
6393
7409
  throw new Error("--keep-files requires a configured package name or source.");
6394
7410
  }
6395
- const adapter = await resolveAdapterForTarget(target, options);
7411
+ const adapter = await resolveAdapterForTarget(target, normalizedOptions);
6396
7412
  const transport = transportForTarget(target);
6397
- const manifest = await readInstallManifest(target.targetRoot, adapter.name, transport);
7413
+ const installationType = normalizedOptions.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter);
7414
+ const state = installStateForTarget(target, adapter, normalizedOptions, installationType);
7415
+ const manifest = await readInstallManifest(state.installRoot, adapter.name, transport, state);
6398
7416
  if (!manifest) {
6399
- console.log(`No install manifest for ${adapter.name} at ${target.targetRoot}`);
7417
+ console.log(`No install manifest for ${adapter.name}/${installationType} at ${state.installRoot}`);
6400
7418
  continue;
6401
7419
  }
6402
7420
  const plan = filterUninstallPlanBySelection(await createUninstallPlan(manifest), selectedArtifactsFromOptions(options));
6403
7421
  console.log(formatPlan(plan));
6404
- const result = await uninstall(plan, { dryRun: options.dryRun, force: options.force, transport });
6405
- if (!options.dryRun) {
7422
+ const result = await uninstall(plan, { dryRun: normalizedOptions.dryRun, force: normalizedOptions.force, transport });
7423
+ if (!normalizedOptions.dryRun) {
6406
7424
  if (transport.kind !== "local" && adapter.programmatic?.uninstall) {
6407
7425
  throw new Error(`Cannot execute programmatic adapter uninstall over ${transport.description}.`);
6408
7426
  }
6409
- await adapter.programmatic?.uninstall?.({ targetRoot: target.targetRoot, adapterName: adapter.name });
7427
+ await adapter.programmatic?.uninstall?.({ targetRoot: state.installRoot, adapterName: adapter.name });
6410
7428
  console.log(formatUninstallResult(result));
6411
7429
  }
6412
7430
  if (plan.hasBlockingChanges) process.exitCode = 1;
6413
7431
  }
6414
7432
  });
6415
- program.command("status").description("show configured packages and runtime install state").option("--adapter <adapter>", "built-in adapter").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("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).action(async (options) => {
6416
- const targets = await resolveCliTargets(options);
7433
+ program.command("status").description("show configured packages and runtime install state").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("--profile <name>", "workspace runtime profile").action(async (options) => {
7434
+ const normalizedOptions = normalizeRuntimeScopeOptions(options);
7435
+ const targets = await resolveCliTargets(normalizedOptions, { preferAllProfile: true });
6417
7436
  for (const target of targets) {
6418
- await printStatus(target, options);
7437
+ await printStatus(target, normalizedOptions);
6419
7438
  }
6420
7439
  });
6421
7440
  async function runInstallCommand(nameOrSource, options, behavior) {
7441
+ const normalizedOptions = normalizeRuntimeScopeOptions(options, { defaultUser: shouldDefaultUserInstall(nameOrSource, options) });
6422
7442
  if (options.profile) {
6423
- const target = await resolveRuntimeTarget({ targetRoot: options.targetRoot, adapter: options.adapter, agent: options.agent });
7443
+ const target = await resolveRuntimeTarget({
7444
+ targetRoot: normalizedOptions.targetRoot,
7445
+ adapter: normalizedOptions.adapter,
7446
+ installationType: normalizedOptions.installationType,
7447
+ agent: normalizedOptions.agent
7448
+ });
6424
7449
  const results = await syncProfile({
6425
7450
  workspaceRoot: target.workspaceRoot,
6426
7451
  profile: options.profile,
6427
7452
  source: nameOrSource,
6428
- driver: options.driver,
6429
- mode: options.mode,
6430
- select: selectedArtifactsFromOptions(options),
7453
+ driver: normalizedOptions.driver,
7454
+ mode: normalizedOptions.mode,
7455
+ select: selectedArtifactsFromOptions(normalizedOptions),
7456
+ installationType: normalizedOptions.installationType,
6431
7457
  dryRun: !behavior.apply,
6432
- executePlugins: options.executePlugins,
6433
- allowAdapterCode: options.allowAdapterCode,
6434
- noDeps: noDepsFromOptions(options),
7458
+ executePlugins: normalizedOptions.executePlugins,
7459
+ allowAdapterCode: normalizedOptions.allowAdapterCode,
7460
+ forceDrift: normalizedOptions.forceDrift,
7461
+ forceConflict: normalizedOptions.forceConflict,
7462
+ replaceConflict: normalizedOptions.replaceConflict,
7463
+ noDeps: noDepsFromOptions(normalizedOptions),
6435
7464
  lockedResolution: true,
6436
- frozenLock: options.frozenLock,
6437
- offline: options.offline,
6438
- yes: options.yes,
6439
- trustPatterns: options.trust ?? [],
7465
+ frozenLock: normalizedOptions.frozenLock,
7466
+ offline: normalizedOptions.offline,
7467
+ yes: normalizedOptions.yes,
7468
+ trustPatterns: normalizedOptions.trust ?? [],
6440
7469
  readOnly: !behavior.apply,
6441
7470
  isTTY: process.stdin.isTTY === true,
6442
7471
  warn: (message) => console.warn(message)
6443
7472
  });
6444
7473
  for (const result of results) {
6445
- console.log(`Profile ${options.profile} / ${result.runtime} / ${result.packageName} at ${result.targetRoot} (${result.transport}):`);
7474
+ console.log(`Profile ${normalizedOptions.profile} / ${result.runtime} / ${result.packageName} at ${result.targetRoot} (${result.transport}):`);
6446
7475
  console.log(formatPlan(result.plan));
6447
7476
  if (result.plan.hasBlockingChanges) process.exitCode = 1;
6448
7477
  }
6449
7478
  if (behavior.apply) console.log("Applied.");
6450
7479
  return;
6451
7480
  }
6452
- const targets = await resolveCliTargets(options);
7481
+ const targets = await resolveCliTargets(normalizedOptions);
6453
7482
  for (const target of targets) {
7483
+ const targetOptions = optionsForResolvedTarget(normalizedOptions, target);
6454
7484
  const config = await readMergedWorkspaceConfig(target.workspaceRoot);
6455
- const configured = nameOrSource ? findConfiguredPackage(config.packages, nameOrSource) : void 0;
7485
+ const configured = nameOrSource ? findConfiguredPackageForTarget(config.packages, nameOrSource, targetOptions, target) : void 0;
6456
7486
  let source;
6457
7487
  let scope = configured?.name;
6458
7488
  let extraPackage;
6459
7489
  if (nameOrSource && !configured) {
6460
7490
  try {
6461
- const entry = await packageEntryFromSource(nameOrSource, target.workspaceRoot, { ...options, adapter: options.adapter ?? target.adapter });
6462
- scope = entry.name;
6463
- if (behavior.apply) {
6464
- await writeWorkspaceConfig(target.workspaceRoot, upsertPackage(await readWorkspaceConfig(target.workspaceRoot), entry));
6465
- } else {
6466
- source = nameOrSource;
6467
- extraPackage = entry;
7491
+ let entry = await packageEntryFromSource(nameOrSource, target.workspaceRoot, { ...targetOptions, adapter: targetOptions.adapter ?? target.adapter });
7492
+ if (targetOptions.multiAdapterSource) {
7493
+ entry = packageEntryWithAdapterSuffix(entry);
6468
7494
  }
7495
+ scope = entry.name;
7496
+ source = nameOrSource;
7497
+ extraPackage = entry;
6469
7498
  } catch (error) {
6470
7499
  throw teachingInstallError(nameOrSource, error);
6471
7500
  }
6472
7501
  }
6473
- for (const result of await buildGraphPlansForTarget(target, source, { ...options, scope, extraPackage }, { mode: "install" })) {
7502
+ for (const result of await buildGraphPlansForTarget(target, source, { ...targetOptions, scope, extraPackage }, { mode: "install" })) {
6474
7503
  console.log(formatGraphPlan(result));
6475
7504
  if (behavior.apply) {
6476
7505
  await applyCombinedInstallPlan(result.plan, {
6477
- executePlugins: options.executePlugins,
7506
+ executePlugins: targetOptions.executePlugins,
6478
7507
  transport: transportForTarget(target),
6479
7508
  graphLockDigest: result.graphLockDigest,
6480
7509
  graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
@@ -6484,6 +7513,9 @@ async function runInstallCommand(nameOrSource, options, behavior) {
6484
7513
  await rm9(result.bundle.root, { recursive: true, force: true });
6485
7514
  if (result.plan.hasBlockingChanges) process.exitCode = 1;
6486
7515
  }
7516
+ if (behavior.apply && extraPackage && !targetOptions.onlySource) {
7517
+ await writeWorkspaceConfig(target.workspaceRoot, upsertPackage(await readWorkspaceConfig(target.workspaceRoot), extraPackage));
7518
+ }
6487
7519
  }
6488
7520
  }
6489
7521
  async function packageEntryFromSource(source, targetRoot, options) {
@@ -6504,23 +7536,26 @@ async function packageEntryFromSource(source, targetRoot, options) {
6504
7536
  const bundle = await stageSource(driver, resolvedSource, {
6505
7537
  workspaceRoot: targetRoot,
6506
7538
  adapter,
6507
- cacheRoot: join28(targetRoot, ".agentwheel", "cache"),
7539
+ cacheRoot: join31(targetRoot, ".agentwheel", "cache"),
6508
7540
  mode: options.mode,
6509
7541
  frozenLock: lockMode,
6510
7542
  select: selectedArtifacts
6511
7543
  });
7544
+ const installationType = resolveInstallationTypeForArtifacts(adapter, bundle.artifacts.map((artifact) => artifact.type), options.installationType);
6512
7545
  try {
6513
7546
  return {
6514
7547
  name: options.name ?? resolvedInput.registryEntry?.name ?? bundle.source.packageName ?? source,
6515
7548
  source: resolvedSource,
6516
7549
  driver: driverName,
6517
7550
  adapter: adapter.name,
7551
+ installationType,
6518
7552
  adapterConfig: options.adapterConfig,
6519
7553
  adapterModule: options.adapterModule,
6520
7554
  adapterCodeHash: adapter.programmatic?.hash,
6521
7555
  mode: options.mode ?? "pinned",
6522
7556
  requestedRef: bundle.source.requestedRef,
6523
- select: selectedArtifacts
7557
+ select: selectedArtifacts,
7558
+ overrides: overrideArtifactsFromOptions(options)
6524
7559
  };
6525
7560
  } finally {
6526
7561
  await rm9(bundle.root, { recursive: true, force: true });
@@ -6529,9 +7564,17 @@ async function packageEntryFromSource(source, targetRoot, options) {
6529
7564
  function findConfiguredPackage(packages, value) {
6530
7565
  return packages.find((pkg) => pkg.name === value || pkg.source === value);
6531
7566
  }
7567
+ function findConfiguredPackageForTarget(packages, value, options, target) {
7568
+ const matches = packages.filter((pkg) => pkg.name === value || pkg.source === value);
7569
+ return options.multiAdapterSource ? matches.find((pkg) => pkg.adapter === target.adapter) : matches[0];
7570
+ }
6532
7571
  function noDepsFromOptions(options) {
6533
7572
  return options.noDeps === true || options.deps === false;
6534
7573
  }
7574
+ function packageEntryWithAdapterSuffix(entry) {
7575
+ const suffix = `-${entry.adapter}`;
7576
+ return entry.name.endsWith(suffix) ? entry : { ...entry, name: `${entry.name}${suffix}` };
7577
+ }
6535
7578
  function teachingInstallError(input, cause) {
6536
7579
  const message = cause instanceof Error ? cause.message : String(cause);
6537
7580
  return new Error(
@@ -6545,28 +7588,103 @@ Resolver error: ${message}`
6545
7588
  function nextInstallNudge() {
6546
7589
  return "Preview: agentwheel plan - Apply: agentwheel install";
6547
7590
  }
6548
- async function resolveCliTargets(options) {
6549
- if (options.all && options.allDetected) {
7591
+ async function resolveCliTargets(options, behavior = {}) {
7592
+ const normalizedOptions = normalizeRuntimeScopeOptions(options);
7593
+ if (normalizedOptions.all && normalizedOptions.allDetected) {
6550
7594
  throw new Error("Choose either --all for configured agents or --all-detected for detected runtime directories.");
6551
7595
  }
6552
- if (options.all) {
6553
- return resolveAllRuntimeTargets({ targetRoot: options.targetRoot, adapter: options.adapter, agent: options.agent, all: options.all });
7596
+ if (normalizedOptions.profile && (normalizedOptions.all || normalizedOptions.allDetected || normalizedOptions.agent)) {
7597
+ throw new Error("--profile cannot be combined with --all, --all-detected, or --agent.");
7598
+ }
7599
+ const adapters2 = adapterListFromOption(normalizedOptions.adapter);
7600
+ if (adapters2.length > 1) {
7601
+ if (normalizedOptions.all || normalizedOptions.allDetected || normalizedOptions.agent || normalizedOptions.profile) {
7602
+ throw new Error("--adapter <a,b> cannot be combined with --all, --all-detected, --agent, or --profile. Use a profile for mixed configured targets.");
7603
+ }
7604
+ const targets = [];
7605
+ for (const adapter of adapters2) {
7606
+ targets.push(await resolveRuntimeTarget({
7607
+ targetRoot: normalizedOptions.targetRoot,
7608
+ adapter,
7609
+ installationType: normalizedOptions.installationType
7610
+ }));
7611
+ }
7612
+ return targets;
7613
+ }
7614
+ if (normalizedOptions.profile) {
7615
+ return resolveProfileRuntimeTargets({
7616
+ cwd: process.cwd(),
7617
+ targetRoot: normalizedOptions.targetRoot,
7618
+ installationType: normalizedOptions.installationType,
7619
+ profile: normalizedOptions.profile
7620
+ });
7621
+ }
7622
+ if (normalizedOptions.all) {
7623
+ if (behavior.preferAllProfile && !normalizedOptions.agent) {
7624
+ const profileTargets = await tryResolveProfileAllRuntimeTargets(normalizedOptions);
7625
+ if (profileTargets) return profileTargets;
7626
+ }
7627
+ return resolveAllRuntimeTargets({
7628
+ targetRoot: normalizedOptions.targetRoot,
7629
+ adapter: normalizedOptions.adapter,
7630
+ installationType: normalizedOptions.installationType,
7631
+ agent: normalizedOptions.agent,
7632
+ all: normalizedOptions.all
7633
+ });
6554
7634
  }
6555
- if (options.allDetected) {
6556
- return resolveAllDetectedRuntimeTargets({ targetRoot: options.targetRoot, adapter: options.adapter, agent: options.agent, allDetected: options.allDetected });
7635
+ if (normalizedOptions.allDetected) {
7636
+ return resolveAllDetectedRuntimeTargets({
7637
+ targetRoot: normalizedOptions.targetRoot,
7638
+ adapter: normalizedOptions.adapter,
7639
+ installationType: normalizedOptions.installationType,
7640
+ agent: normalizedOptions.agent,
7641
+ allDetected: normalizedOptions.allDetected
7642
+ });
6557
7643
  }
6558
- return [await resolveRuntimeTarget({ targetRoot: options.targetRoot, adapter: options.adapter, agent: options.agent })];
7644
+ return [await resolveRuntimeTarget({
7645
+ targetRoot: normalizedOptions.targetRoot,
7646
+ adapter: normalizedOptions.adapter,
7647
+ installationType: normalizedOptions.installationType,
7648
+ agent: normalizedOptions.agent
7649
+ })];
7650
+ }
7651
+ async function tryResolveProfileAllRuntimeTargets(options) {
7652
+ try {
7653
+ return await resolveProfileRuntimeTargets({
7654
+ cwd: process.cwd(),
7655
+ targetRoot: options.targetRoot,
7656
+ installationType: options.installationType,
7657
+ profile: "all"
7658
+ });
7659
+ } catch (error) {
7660
+ if (error instanceof Error && error.message === "Unknown profile: all") return void 0;
7661
+ throw error;
7662
+ }
7663
+ }
7664
+ function optionsForResolvedTarget(options, target) {
7665
+ return adapterListFromOption(options.adapter).length > 1 ? { ...options, adapter: target.adapter, multiAdapterSource: true } : options;
6559
7666
  }
6560
7667
  async function resolveAdapterForTarget(target, options) {
7668
+ const adapterOptions = adapterOptionsForTarget(target, options);
6561
7669
  return resolveAdapter({
6562
7670
  adapter: target.adapter,
6563
- adapterConfig: options.adapterConfig,
6564
- adapterModule: options.adapterModule,
6565
- allowAdapterCode: options.allowAdapterCode,
7671
+ adapterConfig: adapterOptions.adapterConfig,
7672
+ adapterModule: adapterOptions.adapterModule,
7673
+ allowAdapterCode: adapterOptions.allowAdapterCode,
6566
7674
  baseDir: target.workspaceRoot,
6567
7675
  warn: (message) => console.warn(message)
6568
7676
  });
6569
7677
  }
7678
+ function adapterOptionsForTarget(target, options) {
7679
+ return {
7680
+ adapterConfig: options.adapterConfig ?? target.adapterConfig,
7681
+ adapterModule: options.adapterModule ?? target.adapterModule,
7682
+ allowAdapterCode: options.allowAdapterCode
7683
+ };
7684
+ }
7685
+ function targetKeyForTarget(target, adapterName) {
7686
+ return target.targetKey ?? target.agentName ?? adapterName ?? target.source;
7687
+ }
6570
7688
  async function runConfiguredGraphPackages(target, options, behavior) {
6571
7689
  const results = await buildGraphPlansForTarget(target, void 0, options, behavior);
6572
7690
  for (const result of results) {
@@ -6586,37 +7704,39 @@ async function runConfiguredGraphPackages(target, options, behavior) {
6586
7704
  }
6587
7705
  }
6588
7706
  async function buildGraphPlansForTarget(target, source, options, behavior) {
7707
+ const targetOptions = optionsForResolvedTarget(options, target);
6589
7708
  const config = await readMergedWorkspaceConfig(target.workspaceRoot);
6590
7709
  const groups = /* @__PURE__ */ new Map();
6591
- const selectedArtifacts = selectedArtifactsFromOptions(options);
6592
- const scopedPackage = options.scope ? findConfiguredPackage(config.packages, options.scope) : void 0;
6593
- const scopedRootId = scopedPackage?.name ?? (source ? options.scope : void 0);
6594
- if (options.scope && !scopedPackage && !source) throw new Error(`Configured package not found: ${options.scope}`);
6595
- if (!source || !options.onlySource) {
7710
+ const selectedArtifacts = selectedArtifactsFromOptions(targetOptions);
7711
+ const scopedPackage = targetOptions.scope ? findConfiguredPackage(config.packages, targetOptions.scope) : void 0;
7712
+ const scopedRootId = scopedPackage?.name ?? (source ? targetOptions.scope : void 0);
7713
+ if (targetOptions.scope && !scopedPackage && !source) throw new Error(`Configured package not found: ${targetOptions.scope}`);
7714
+ if (!source || !targetOptions.onlySource) {
6596
7715
  for (const pkg of config.packages) {
6597
- const group = graphGroupForPackage(groups, target, pkg, options);
7716
+ const group = graphGroupForPackage(groups, target, pkg, targetOptions);
6598
7717
  group.packages.push(pkg);
6599
7718
  }
6600
7719
  }
6601
7720
  if (source) {
7721
+ let entry = targetOptions.extraPackage ?? await packageEntryFromSource(source, target.workspaceRoot, targetOptions);
7722
+ if (targetOptions.multiAdapterSource) {
7723
+ entry = packageEntryWithAdapterSuffix(entry);
7724
+ }
6602
7725
  const sourceTarget = target;
7726
+ const sourceInstallationType = targetOptions.installationType ?? entry.installationType ?? sourceTarget.installationType ?? "local";
7727
+ const sourceAdapterOptions = adapterOptionsForTarget(sourceTarget, targetOptions);
6603
7728
  const key = graphGroupKey(sourceTarget, {
6604
- adapterConfig: options.adapterConfig,
6605
- adapterModule: options.adapterModule,
6606
- allowAdapterCode: options.allowAdapterCode
7729
+ installationType: sourceInstallationType,
7730
+ ...sourceAdapterOptions
6607
7731
  });
6608
7732
  const group = groups.get(key) ?? {
6609
7733
  target: sourceTarget,
6610
- adapterOptions: {
6611
- adapterConfig: options.adapterConfig,
6612
- adapterModule: options.adapterModule,
6613
- allowAdapterCode: options.allowAdapterCode
6614
- },
7734
+ installationType: sourceInstallationType,
7735
+ adapterOptions: sourceAdapterOptions,
6615
7736
  packages: [],
6616
7737
  extraRoots: [],
6617
7738
  extraPackages: []
6618
7739
  };
6619
- const entry = options.extraPackage ?? await packageEntryFromSource(source, target.workspaceRoot, options);
6620
7740
  group.extraPackages.push(entry);
6621
7741
  groups.set(key, group);
6622
7742
  }
@@ -6629,19 +7749,21 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
6629
7749
  const adapter = await resolveAdapterForTarget(group.target, group.adapterOptions);
6630
7750
  const transport = transportForTarget(group.target);
6631
7751
  const allPackages = [...group.packages, ...group.extraPackages];
6632
- const groupHasScope = !scopedRootId || allPackages.some((pkg) => pkg.name === scopedRootId || pkg.source === options.scope);
7752
+ const groupHasScope = !scopedRootId || allPackages.some((pkg) => pkg.name === scopedRootId || pkg.source === targetOptions.scope);
6633
7753
  if (behavior.mode === "install" && scopedRootId && !groupHasScope) continue;
6634
7754
  const updateScope = behavior.mode === "update" ? scopedPackage ? /* @__PURE__ */ new Set([scopedPackage.name]) : void 0 : void 0;
6635
7755
  const roots = [
6636
7756
  ...allPackages.map((pkg) => {
6637
7757
  const updateThisPackage = behavior.mode === "update" && pkg.mode === "tracking" && (!updateScope || updateScope.has(pkg.name) || updateScope.has(pkg.source));
7758
+ const packageIsScoped = scopedRootId ? pkg.name === scopedRootId || pkg.source === targetOptions.scope : true;
6638
7759
  return {
6639
7760
  rootId: pkg.name,
6640
7761
  source: pkg.source,
6641
7762
  mode: pkg.mode,
6642
7763
  ref: pkg.requestedRef,
6643
- select: selectedArtifacts ?? normalizeArtifactSelectors(pkg.select, pkg.skills),
7764
+ select: selectedArtifacts && packageIsScoped ? selectedArtifacts : normalizeArtifactSelectors(pkg.select, pkg.skills),
6644
7765
  aliases: pkg.aliases,
7766
+ overrides: pkg.overrides,
6645
7767
  useLock: behavior.mode === "install" ? true : !updateThisPackage
6646
7768
  };
6647
7769
  }),
@@ -6650,7 +7772,7 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
6650
7772
  if (behavior.mode === "update") {
6651
7773
  const changed = roots.filter((root) => root.useLock === false);
6652
7774
  if (changed.length === 0) {
6653
- const label = options.scope ? ` ${options.scope}` : "";
7775
+ const label = targetOptions.scope ? ` ${targetOptions.scope}` : "";
6654
7776
  console.log(`No tracking packages to update${label}.`);
6655
7777
  continue;
6656
7778
  }
@@ -6662,19 +7784,24 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
6662
7784
  workspaceRoot: group.target.workspaceRoot,
6663
7785
  adapter,
6664
7786
  transport,
6665
- targetKey: group.target.agentName ?? group.target.source,
6666
- targetFingerprintParts: targetFingerprintParts(group.target, adapter, group.adapterOptions),
6667
- noDeps: noDepsFromOptions(options),
7787
+ targetKey: targetKeyForTarget(group.target, adapter.name),
7788
+ targetFingerprintParts: targetFingerprintParts(group.target, adapter, group.adapterOptions, group.installationType),
7789
+ installationType: group.installationType,
7790
+ noDeps: noDepsFromOptions(targetOptions),
6668
7791
  lockedResolution: behavior.mode === "install",
6669
- frozenLock: options.frozenLock,
6670
- offline: options.offline,
6671
- yes: options.yes,
6672
- trustPatterns: options.trust ?? [],
6673
- readOnly: options.dryRun === true,
6674
- isTTY: process.stdin.isTTY === true
7792
+ frozenLock: targetOptions.frozenLock,
7793
+ offline: targetOptions.offline,
7794
+ yes: targetOptions.yes,
7795
+ trustPatterns: targetOptions.trust ?? [],
7796
+ readOnly: targetOptions.dryRun === true,
7797
+ isTTY: process.stdin.isTTY === true,
7798
+ forceDrift: targetOptions.forceDrift,
7799
+ forceConflict: targetOptions.forceConflict,
7800
+ replaceConflict: targetOptions.replaceConflict
6675
7801
  });
6676
7802
  if (behavior.mode === "install" && scopedRootId) {
6677
- const manifest = await readInstallManifest(group.target.targetRoot, adapter.name, transport);
7803
+ const state = installStateForTarget(group.target, adapter, group.adapterOptions, group.installationType);
7804
+ const manifest = await readInstallManifest(state.installRoot, adapter.name, transport, state);
6678
7805
  results.push(scopeInstallPlanToRoot(result, scopedRootId, manifest));
6679
7806
  } else {
6680
7807
  results.push(result);
@@ -6761,7 +7888,7 @@ function keepManifestEntryOperation(entry, targetRoot, rootId, operation, option
6761
7888
  artifactType: entry.artifactType,
6762
7889
  artifactName: entry.artifactName,
6763
7890
  kind: entry.kind,
6764
- destPath: operation?.destPath ?? join28(targetRoot, entry.path),
7891
+ destPath: operation?.destPath ?? join31(targetRoot, entry.path),
6765
7892
  relativeDestPath: entry.path,
6766
7893
  desiredHash: entry.sourceHash,
6767
7894
  currentHash: operation?.currentHash ?? entry.hash,
@@ -6797,13 +7924,15 @@ async function uninstallConfiguredPackage(target, packageName, options) {
6797
7924
  for (const pkg of removed) {
6798
7925
  const removedTarget = targetForPackage(target, pkg, options);
6799
7926
  const removedAdapterOptions = {
6800
- adapterConfig: options.adapterConfig ?? pkg.adapterConfig,
6801
- adapterModule: options.adapterModule ?? pkg.adapterModule,
7927
+ adapterConfig: options.adapterConfig ?? removedTarget.adapterConfig ?? pkg.adapterConfig,
7928
+ adapterModule: options.adapterModule ?? removedTarget.adapterModule ?? pkg.adapterModule,
6802
7929
  allowAdapterCode: options.allowAdapterCode
6803
7930
  };
7931
+ const removedInstallationType = options.installationType ?? pkg.installationType ?? removedTarget.installationType ?? "local";
6804
7932
  const adapter = await resolveAdapterForTarget(removedTarget, removedAdapterOptions);
6805
7933
  const transport = transportForTarget(removedTarget);
6806
- const manifest = await readInstallManifest(removedTarget.targetRoot, adapter.name, transport);
7934
+ const removedState = installStateForTarget(removedTarget, adapter, removedAdapterOptions, removedInstallationType);
7935
+ const manifest = await readInstallManifest(removedState.installRoot, adapter.name, transport, removedState);
6807
7936
  if (!manifest) {
6808
7937
  console.log(`No install manifest for ${adapter.name} at ${removedTarget.targetRoot}`);
6809
7938
  continue;
@@ -6822,14 +7951,16 @@ async function uninstallConfiguredPackage(target, packageName, options) {
6822
7951
  mode: pkg2.mode,
6823
7952
  ref: pkg2.requestedRef,
6824
7953
  select: normalizeArtifactSelectors(pkg2.select, pkg2.skills),
6825
- aliases: pkg2.aliases
7954
+ aliases: pkg2.aliases,
7955
+ overrides: pkg2.overrides
6826
7956
  })),
6827
7957
  targetRoot: remainingGroup.target.targetRoot,
6828
7958
  workspaceRoot: remainingGroup.target.workspaceRoot,
6829
7959
  adapter: remainingAdapter,
6830
7960
  transport,
6831
- targetKey: remainingGroup.target.agentName ?? remainingGroup.target.source,
6832
- targetFingerprintParts: targetFingerprintParts(remainingGroup.target, remainingAdapter, remainingGroup.adapterOptions),
7961
+ targetKey: targetKeyForTarget(remainingGroup.target, remainingAdapter.name),
7962
+ targetFingerprintParts: targetFingerprintParts(remainingGroup.target, remainingAdapter, remainingGroup.adapterOptions, remainingGroup.installationType),
7963
+ installationType: remainingGroup.installationType,
6833
7964
  lockedResolution: true,
6834
7965
  frozenLock: options.frozenLock,
6835
7966
  offline: options.offline,
@@ -6848,9 +7979,9 @@ async function uninstallConfiguredPackage(target, packageName, options) {
6848
7979
  const graphLockFinalState = remainingGraphPlan ? { graphLock: { path: remainingGraphPlan.graphLockPath, lock: remainingGraphPlan.bundle.graphLock } } : {
6849
7980
  removeGraphLockPath: graphLockPathForTarget(
6850
7981
  removedTarget.workspaceRoot,
6851
- removedTarget.agentName ?? removedTarget.source,
7982
+ targetKeyForTarget(removedTarget, adapter.name),
6852
7983
  adapter.name,
6853
- targetFingerprintParts(removedTarget, adapter, removedAdapterOptions)
7984
+ targetFingerprintParts(removedTarget, adapter, removedAdapterOptions, removedInstallationType)
6854
7985
  )
6855
7986
  };
6856
7987
  const result = await uninstall(plan, {
@@ -6873,16 +8004,18 @@ async function uninstallConfiguredPackage(target, packageName, options) {
6873
8004
  }
6874
8005
  function graphGroupForPackage(groups, target, pkg, options) {
6875
8006
  const packageTarget = targetForPackage(target, pkg, options);
8007
+ const installationType = options.installationType ?? pkg.installationType ?? packageTarget.installationType ?? "local";
6876
8008
  const adapterOptions = {
6877
- adapterConfig: options.adapterConfig ?? pkg.adapterConfig,
6878
- adapterModule: options.adapterModule ?? pkg.adapterModule,
8009
+ adapterConfig: options.adapterConfig ?? packageTarget.adapterConfig ?? pkg.adapterConfig,
8010
+ adapterModule: options.adapterModule ?? packageTarget.adapterModule ?? pkg.adapterModule,
6879
8011
  allowAdapterCode: options.allowAdapterCode
6880
8012
  };
6881
- const key = graphGroupKey(packageTarget, adapterOptions);
8013
+ const key = graphGroupKey(packageTarget, { ...adapterOptions, installationType });
6882
8014
  const existing = groups.get(key);
6883
8015
  if (existing) return existing;
6884
8016
  const created = {
6885
8017
  target: packageTarget,
8018
+ installationType,
6886
8019
  adapterOptions,
6887
8020
  packages: [],
6888
8021
  extraRoots: [],
@@ -6892,11 +8025,13 @@ function graphGroupForPackage(groups, target, pkg, options) {
6892
8025
  return created;
6893
8026
  }
6894
8027
  function targetForPackage(target, pkg, options) {
6895
- return options.adapter || target.source !== "cwd" ? target : { ...target, adapter: pkg.adapter };
8028
+ const installationType = options.installationType ?? pkg.installationType ?? target.installationType;
8029
+ return options.adapter || target.source !== "cwd" ? { ...target, installationType } : { ...target, adapter: pkg.adapter, installationType };
6896
8030
  }
6897
8031
  function graphGroupKey(target, options) {
6898
8032
  return JSON.stringify({
6899
8033
  adapter: target.adapter,
8034
+ installationType: options.installationType ?? target.installationType ?? "local",
6900
8035
  targetRoot: target.targetRoot,
6901
8036
  transport: target.transport,
6902
8037
  agentName: target.agentName,
@@ -6904,9 +8039,10 @@ function graphGroupKey(target, options) {
6904
8039
  adapterModule: options.adapterModule
6905
8040
  });
6906
8041
  }
6907
- function targetFingerprintParts(target, adapter, options) {
8042
+ function targetFingerprintParts(target, adapter, options, installationType) {
6908
8043
  return {
6909
8044
  adapter: adapter.name,
8045
+ installationType: installationType ?? target.installationType ?? "local",
6910
8046
  adapterConfig: options.adapterConfig,
6911
8047
  adapterModule: options.adapterModule,
6912
8048
  adapterCodeHash: adapter.programmatic?.hash,
@@ -6916,13 +8052,27 @@ function targetFingerprintParts(target, adapter, options) {
6916
8052
  ssh: target.ssh
6917
8053
  };
6918
8054
  }
8055
+ function installStateForTarget(target, adapter, options, installationType) {
8056
+ const targetFingerprint = targetFingerprintDigest(target, adapter, options, installationType);
8057
+ return {
8058
+ installationType,
8059
+ stateKey: stateKeyFor(adapter.name, { installationType, targetFingerprint }),
8060
+ installRoot: installRootForAdapterInstallationType(adapter, target.targetRoot, installationType, target.transport === "ssh")
8061
+ };
8062
+ }
8063
+ function targetFingerprintDigest(target, adapter, options, installationType) {
8064
+ const fingerprintInput = targetFingerprintParts(target, adapter, options, installationType);
8065
+ return computeTargetFingerprint(fingerprintInput);
8066
+ }
6919
8067
  async function readTargetGraphLock(target, options) {
6920
- const adapter = await resolveAdapterForTarget(target, options);
8068
+ const adapterOptions = adapterOptionsForTarget(target, options);
8069
+ const adapter = await resolveAdapterForTarget(target, adapterOptions);
8070
+ const installationType = options.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter, void 0);
6921
8071
  const path = graphLockPathForTarget(
6922
8072
  target.workspaceRoot,
6923
- target.agentName ?? target.source,
8073
+ targetKeyForTarget(target, adapter.name),
6924
8074
  adapter.name,
6925
- targetFingerprintParts(target, adapter, options)
8075
+ targetFingerprintParts(target, adapter, adapterOptions, installationType)
6926
8076
  );
6927
8077
  if (!await pathExists(path)) {
6928
8078
  throw new Error(`No graph lock for ${adapter.name} at ${target.targetRoot}: ${path}`);
@@ -6931,9 +8081,12 @@ async function readTargetGraphLock(target, options) {
6931
8081
  }
6932
8082
  async function printStatus(target, options) {
6933
8083
  const config = await readMergedWorkspaceConfig(target.workspaceRoot);
6934
- const adapter = await resolveAdapterForTarget(target, options);
8084
+ const adapterOptions = adapterOptionsForTarget(target, options);
8085
+ const adapter = await resolveAdapterForTarget(target, adapterOptions);
6935
8086
  const transport = transportForTarget(target);
6936
- console.log(`Status for ${adapter.name} at ${target.targetRoot}`);
8087
+ const installationType = options.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter);
8088
+ const state = installStateForTarget(target, adapter, adapterOptions, installationType);
8089
+ console.log(`Status for ${adapter.name}/${installationType} at ${state.installRoot}`);
6937
8090
  if (config.packages.length === 0) {
6938
8091
  console.log(`Configured packages: none at ${target.workspaceRoot}`);
6939
8092
  return;
@@ -6942,10 +8095,10 @@ async function printStatus(target, options) {
6942
8095
  for (const pkg of config.packages) {
6943
8096
  console.log(`- ${pkg.name} (${pkg.mode}) ${pkg.source}`);
6944
8097
  }
6945
- const manifest = await readInstallManifest(target.targetRoot, adapter.name, transport);
8098
+ const manifest = await readInstallManifest(state.installRoot, adapter.name, transport, state);
6946
8099
  console.log(manifest ? `Install manifest: ${manifest.entries.length} entries, revision ${manifest.revision}` : "Install manifest: missing");
6947
8100
  try {
6948
- const { path, lock } = await readTargetGraphLock(target, options);
8101
+ const { path, lock } = await readTargetGraphLock(target, adapterOptions);
6949
8102
  console.log(`Graph lock: ${path}`);
6950
8103
  console.log(`Locked graph: ${lock.canonical.roots.length} roots, ${lock.canonical.nodes.length} nodes, ${lock.canonical.artifacts.length} artifacts`);
6951
8104
  } catch {
@@ -6958,7 +8111,7 @@ async function printPendingInstallWork(target, options) {
6958
8111
  try {
6959
8112
  results = await buildGraphPlansForTarget(target, void 0, { ...options, dryRun: true }, { mode: "install" });
6960
8113
  const operations = results.flatMap((result) => result.plan.operations);
6961
- const pending = operations.filter((operation) => operation.action !== "skip");
8114
+ const pending = operations.filter(isPendingInstallOperation);
6962
8115
  if (pending.length === 0) {
6963
8116
  console.log("Pending install work: none");
6964
8117
  return;
@@ -6985,9 +8138,67 @@ function collectSkillOption(value, previous) {
6985
8138
  function collectTrustOption(value, previous) {
6986
8139
  return [...previous, value];
6987
8140
  }
8141
+ function collectOverrideOption(value, previous) {
8142
+ return [...previous, ...splitSelectorList(value)];
8143
+ }
8144
+ function normalizeRuntimeScopeOptions(options, behavior = {}) {
8145
+ if (options.user && options.local) {
8146
+ throw new Error("Choose either --user or --local.");
8147
+ }
8148
+ const shortcutType = options.user ? "user" : options.local ? "local" : void 0;
8149
+ if (shortcutType && options.installationType && options.installationType !== shortcutType) {
8150
+ throw new Error(`--${shortcutType} conflicts with --installation-type ${options.installationType}.`);
8151
+ }
8152
+ let targetRoot = options.targetRoot ? normalizeCliPath(options.targetRoot) : void 0;
8153
+ let installationType = options.installationType ?? shortcutType;
8154
+ if (!installationType && targetRoot) {
8155
+ installationType = isHomePath(targetRoot) ? "user" : "local";
8156
+ }
8157
+ const canDefaultTargetRoot = !options.agent && !options.all && !options.allDetected && !options.profile;
8158
+ if (!targetRoot && canDefaultTargetRoot && (options.user || installationType === "user" || behavior.defaultUser)) {
8159
+ targetRoot = homedir9();
8160
+ }
8161
+ if (!installationType && behavior.defaultUser) {
8162
+ installationType = "user";
8163
+ }
8164
+ return {
8165
+ ...options,
8166
+ installationType,
8167
+ targetRoot
8168
+ };
8169
+ }
8170
+ function shouldDefaultUserInstall(nameOrSource, options) {
8171
+ return Boolean(
8172
+ nameOrSource && options.adapter && !options.installationType && !options.user && !options.local && !options.targetRoot && !options.agent && !options.all && !options.allDetected && !options.profile && looksLikeSourceSpecifier(nameOrSource)
8173
+ );
8174
+ }
8175
+ function looksLikeSourceSpecifier(value) {
8176
+ return value.includes(":") || value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value === "~" || value.startsWith("~/");
8177
+ }
8178
+ function normalizeCliPath(value) {
8179
+ if (value === "~") return homedir9();
8180
+ if (value.startsWith("~/")) return resolve18(homedir9(), value.slice(2));
8181
+ return resolve18(value);
8182
+ }
8183
+ function isHomePath(path) {
8184
+ return resolve18(path) === resolve18(homedir9());
8185
+ }
8186
+ function adapterListFromOption(adapter) {
8187
+ if (!adapter) return [];
8188
+ const adapters2 = splitSelectorList(adapter);
8189
+ const unique = [...new Set(adapters2)];
8190
+ if (unique.length !== adapters2.length) {
8191
+ throw new Error(`Duplicate adapter in --adapter: ${adapter}`);
8192
+ }
8193
+ return unique;
8194
+ }
6988
8195
  function selectedArtifactsFromOptions(options) {
6989
8196
  return normalizeArtifactSelectors(options.select, options.skills ?? options.skill);
6990
8197
  }
8198
+ function overrideArtifactsFromOptions(options) {
8199
+ const values = options.overrides ?? options.override;
8200
+ return values && values.length > 0 ? values : void 0;
8201
+ }
6991
8202
  function filterUninstallPlanBySelection(plan, selected) {
6992
8203
  if (!selected?.length) return plan;
6993
8204
  const requested = normalizeArtifactSelectors(selected) ?? [];
@@ -7013,10 +8224,10 @@ function filterUninstallPlanBySelection(plan, selected) {
7013
8224
  };
7014
8225
  }
7015
8226
  async function initPackage(root) {
7016
- await mkdir14(join28(root, "instructions"), { recursive: true });
7017
- await mkdir14(join28(root, "rules"), { recursive: true });
7018
- await mkdir14(join28(root, "skills"), { recursive: true });
7019
- const manifestPath = join28(root, "openpack.json");
8227
+ await mkdir16(join31(root, "instructions"), { recursive: true });
8228
+ await mkdir16(join31(root, "rules"), { recursive: true });
8229
+ await mkdir16(join31(root, "skills"), { recursive: true });
8230
+ const manifestPath = join31(root, "openpack.json");
7020
8231
  const manifest = {
7021
8232
  schemaVersion: 2,
7022
8233
  name: "example/agentwheel-package",
@@ -7027,18 +8238,19 @@ async function initPackage(root) {
7027
8238
  { type: "skills", path: "skills" }
7028
8239
  ]
7029
8240
  };
7030
- await writeFile12(manifestPath, `${JSON.stringify(manifest, null, 2)}
8241
+ await writeFile14(manifestPath, `${JSON.stringify(manifest, null, 2)}
7031
8242
  `, "utf8");
7032
- await writeFile12(join28(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
8243
+ await writeFile14(join31(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
7033
8244
  }
7034
8245
  async function defaultBootstrapPackage(_root) {
7035
- const packageRoot = await findAgentwheelPackageRoot(dirname21(fileURLToPath3(import.meta.url)));
8246
+ const packageRoot = await findAgentwheelPackageRoot(dirname23(fileURLToPath3(import.meta.url)));
7036
8247
  if (!packageRoot) return void 0;
7037
8248
  return {
7038
8249
  name: "agentwheel",
7039
8250
  source: packageRoot,
7040
8251
  driver: "local",
7041
8252
  adapter: "openclaw",
8253
+ installationType: "local",
7042
8254
  mode: "tracking",
7043
8255
  select: ["skills/agentwheel"]
7044
8256
  };
@@ -7055,7 +8267,7 @@ function withFleetExample(config) {
7055
8267
  },
7056
8268
  "remote-codex": config.agents["remote-codex"] ?? {
7057
8269
  adapter: "codex",
7058
- root: "/home/administrator/agent-runtime",
8270
+ root: "/workspace/agent-runtime",
7059
8271
  transport: "ssh",
7060
8272
  host: "remote-host.example",
7061
8273
  user: "administrator",
@@ -7078,7 +8290,7 @@ async function findAgentwheelPackageRoot(start) {
7078
8290
  let current = resolve18(start);
7079
8291
  while (true) {
7080
8292
  if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
7081
- const parent = dirname21(current);
8293
+ const parent = dirname23(current);
7082
8294
  if (parent === current) return void 0;
7083
8295
  current = parent;
7084
8296
  }
@@ -7108,7 +8320,9 @@ async function main() {
7108
8320
  });
7109
8321
  await program.parseAsync();
7110
8322
  }
7111
- main().catch((error) => {
8323
+ try {
8324
+ await main();
8325
+ } catch (error) {
7112
8326
  console.error(error instanceof Error ? error.message : String(error));
7113
8327
  process.exitCode = 1;
7114
- });
8328
+ }