agentwheel 0.11.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";
@@ -444,8 +590,11 @@ function stableValue(value) {
444
590
  }
445
591
 
446
592
  // src/transport/local.ts
593
+ import { execFile } from "child_process";
447
594
  import { mkdir as mkdir2, readFile as readFile4, rename as rename2, rm, writeFile as writeFile3 } from "fs/promises";
448
595
  import { dirname as dirname2 } from "path";
596
+ import { promisify } from "util";
597
+ var execFileAsync = promisify(execFile);
449
598
  var localTransport = {
450
599
  kind: "local",
451
600
  description: "local filesystem",
@@ -464,20 +613,23 @@ var localTransport = {
464
613
  },
465
614
  writeJsonAtomic,
466
615
  atomicCopy,
467
- 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
+ }
468
620
  };
469
621
 
470
622
  // src/transport/ssh.ts
471
- import { execFile, spawn } from "child_process";
623
+ import { execFile as execFile2, spawn } from "child_process";
472
624
  import { basename, dirname as dirname3 } from "path";
473
625
  import { dirname as posixDirname } from "path/posix";
474
- import { promisify } from "util";
475
- var execFileAsync = promisify(execFile);
626
+ import { promisify as promisify2 } from "util";
627
+ var execFileAsync2 = promisify2(execFile2);
476
628
  function createSshTransport(config) {
477
629
  const endpoint = config.user ? `${config.user}@${config.host}` : config.host;
478
630
  const args = baseSshArgs(config, endpoint);
479
631
  async function run(command) {
480
- const { stdout } = await execFileAsync("ssh", [...args, command], { maxBuffer: 20 * 1024 * 1024 });
632
+ const { stdout } = await execFileAsync2("ssh", [...args, command], { maxBuffer: 20 * 1024 * 1024 });
481
633
  return stdout;
482
634
  }
483
635
  async function runWithInput(command, input) {
@@ -523,6 +675,11 @@ function createSshTransport(config) {
523
675
  },
524
676
  rm(path) {
525
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);
526
683
  }
527
684
  };
528
685
  }
@@ -589,6 +746,11 @@ const { createHash } = require("node:crypto");
589
746
  const { readdirSync, readFileSync, statSync } = require("node:fs");
590
747
  const { join, relative } = require("node:path");
591
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
+ }
592
754
  function hashPath(path) {
593
755
  const stats = statSync(path);
594
756
  if (stats.isFile()) {
@@ -606,7 +768,7 @@ function listFiles(root) {
606
768
  const out = [];
607
769
  function walk(dir) {
608
770
  for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
609
- if (entry.name === ".git" || entry.name === "node_modules") continue;
771
+ if (isIgnoredGeneratedEntry(entry.name)) continue;
610
772
  const full = join(dir, entry.name);
611
773
  if (entry.isDirectory()) walk(full);
612
774
  else if (entry.isFile()) out.push(full);
@@ -722,6 +884,8 @@ var installManifestV1Schema = z4.object({
722
884
  var installManifestV2Schema = z4.object({
723
885
  version: z4.literal(2),
724
886
  adapter: z4.string().min(1),
887
+ installationType: installationTypeSchema.default(defaultInstallationType),
888
+ stateKey: z4.string().min(1).optional(),
725
889
  targetRoot: z4.string().min(1),
726
890
  generatedAt: z4.string().datetime(),
727
891
  revision: z4.string().min(16),
@@ -754,6 +918,7 @@ var sourceLockSchema = z4.object({
754
918
  relativePath: z4.string().min(1),
755
919
  kind: fileKindSchema,
756
920
  hash: z4.string().min(16),
921
+ format: artifactFormatSchema.optional(),
757
922
  composedFrom: z4.array(composedFromEntrySchema).optional()
758
923
  })
759
924
  )
@@ -764,16 +929,25 @@ import { join as join2 } from "path";
764
929
  function metadataDir(targetRoot) {
765
930
  return join2(targetRoot, ".agentwheel");
766
931
  }
767
- function installManifestPath(targetRoot, adapter) {
768
- 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}`);
769
937
  }
770
- function sourceLockPath(targetRoot, adapter) {
771
- return join2(metadataDir(targetRoot), `${adapter}.source-lock.json`);
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`);
943
+ }
944
+ function sanitizeStateKey(value) {
945
+ return value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "default";
772
946
  }
773
947
 
774
948
  // src/install/manifest.ts
775
- async function readInstallManifest(targetRoot, adapter, transport = localTransport) {
776
- const path = installManifestPath(targetRoot, adapter);
949
+ async function readInstallManifest(targetRoot, adapter, transport = localTransport, scope = {}) {
950
+ const path = installManifestPath(targetRoot, adapter, scope);
777
951
  if (!await transport.pathExists(path)) return void 0;
778
952
  const raw = JSON.parse(await transport.readFile(path));
779
953
  const parsed = installManifestSchema.parse(raw);
@@ -784,14 +958,17 @@ async function readInstallManifest(targetRoot, adapter, transport = localTranspo
784
958
  }
785
959
  async function writeInstallManifest(manifest, transport = localTransport) {
786
960
  const next = withManifestRevision(manifest);
787
- 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));
788
965
  }
789
- async function writeSourceLock(targetRoot, adapter, lock, transport = localTransport) {
790
- 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);
791
968
  }
792
- async function removeStateFiles(targetRoot, adapter, transport = localTransport) {
793
- await transport.rm(installManifestPath(targetRoot, adapter));
794
- 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));
795
972
  }
796
973
  function normalizeTargetRoot(path) {
797
974
  return resolve3(path);
@@ -800,7 +977,11 @@ function withManifestRevision(manifest) {
800
977
  if (manifest.version !== 2) {
801
978
  throw new Error("Install manifest writes must use version 2");
802
979
  }
803
- const normalized = installManifestV2Schema.parse(stripReadOnlyManifestFields(manifest));
980
+ const raw = stripReadOnlyManifestFields(manifest);
981
+ const normalized = installManifestV2Schema.parse({
982
+ installationType: defaultInstallationType,
983
+ ...raw
984
+ });
804
985
  const withoutRevision = stripRuntimeManifestFields(normalized);
805
986
  return {
806
987
  ...normalized,
@@ -859,21 +1040,23 @@ function assertOperationContained(operation, targetRoot) {
859
1040
  // src/install/transaction.ts
860
1041
  import { cp, mkdir as mkdir4, rm as rm2, stat as stat2 } from "fs/promises";
861
1042
  import { dirname as dirname5, join as join3 } from "path";
862
- function applyLockPath(targetRoot, adapter) {
863
- return join3(metadataDir(targetRoot), `${adapter}.apply-lock`);
1043
+ function applyLockPath(targetRoot, adapter, scope = {}) {
1044
+ return join3(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-lock`);
864
1045
  }
865
- function applyJournalPath(targetRoot, adapter) {
866
- 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`);
867
1048
  }
868
- function applyBackupDir(targetRoot, adapter) {
869
- return join3(metadataDir(targetRoot), `${adapter}.apply-backups`);
1049
+ function applyBackupDir(targetRoot, adapter, scope = {}) {
1050
+ return join3(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-backups`);
870
1051
  }
871
- async function acquireApplyLock(targetRoot, adapter, transport = localTransport, options = {}) {
872
- const lockPath = applyLockPath(targetRoot, adapter);
1052
+ async function acquireApplyLock(targetRoot, adapter, transport = localTransport, options = {}, scope = {}) {
1053
+ const lockPath = applyLockPath(targetRoot, adapter, scope);
873
1054
  const ownerPath = join3(lockPath, "owner.json");
874
1055
  const metadata = {
875
1056
  pid: process.pid,
876
1057
  adapter,
1058
+ installationType: scope.installationType,
1059
+ stateKey: scope.stateKey,
877
1060
  targetRoot,
878
1061
  transport: transport.description,
879
1062
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -892,21 +1075,24 @@ async function acquireApplyLock(targetRoot, adapter, transport = localTransport,
892
1075
  };
893
1076
  }
894
1077
  async function writeApplyJournal(journal, transport = localTransport) {
895
- 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
+ }), {
896
1082
  ...journal,
897
1083
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
898
1084
  });
899
1085
  }
900
- async function readApplyJournal(targetRoot, adapter, transport = localTransport) {
901
- const path = applyJournalPath(targetRoot, adapter);
1086
+ async function readApplyJournal(targetRoot, adapter, transport = localTransport, scope = {}) {
1087
+ const path = applyJournalPath(targetRoot, adapter, scope);
902
1088
  if (!await transport.pathExists(path)) return void 0;
903
1089
  return JSON.parse(await transport.readFile(path));
904
1090
  }
905
- async function removeApplyJournal(targetRoot, adapter, transport = localTransport) {
906
- await transport.rm(applyJournalPath(targetRoot, adapter));
907
- 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));
908
1094
  }
909
- async function recordBackup(operation, index, targetRoot, adapter, transport = localTransport) {
1095
+ async function recordBackup(operation, index, targetRoot, adapter, transport = localTransport, scope = {}) {
910
1096
  const hadExisting = await transport.pathExists(operation.destPath);
911
1097
  if (!hadExisting || transport.kind !== "local" || operation.action !== "update" && operation.action !== "remove" && operation.action !== "create") {
912
1098
  return {
@@ -916,7 +1102,7 @@ async function recordBackup(operation, index, targetRoot, adapter, transport = l
916
1102
  hadExisting
917
1103
  };
918
1104
  }
919
- const backupPath = join3(applyBackupDir(targetRoot, adapter), String(index));
1105
+ const backupPath = join3(applyBackupDir(targetRoot, adapter, scope), String(index));
920
1106
  await rm2(backupPath, { recursive: true, force: true });
921
1107
  await mkdir4(dirname5(backupPath), { recursive: true });
922
1108
  await cp(operation.destPath, backupPath, { recursive: operation.kind === "dir", dereference: true });
@@ -1069,14 +1255,14 @@ function normalizeOwners(owners) {
1069
1255
  }
1070
1256
 
1071
1257
  // src/install/apply.ts
1072
- var execFileAsync2 = promisify2(execFile2);
1258
+ var execFileAsync3 = promisify3(execFile3);
1073
1259
  async function applyCombinedInstallPlan(plan, options = {}) {
1074
1260
  return applyPlanTransactionally(plan, options);
1075
1261
  }
1076
- async function recoverPendingApply(targetRoot, adapter, transport = localTransport) {
1077
- 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);
1078
1264
  try {
1079
- const journal = await readApplyJournal(targetRoot, adapter, transport);
1265
+ const journal = await readApplyJournal(targetRoot, adapter, transport, scope);
1080
1266
  if (!journal) return void 0;
1081
1267
  if (journal.operations.some((operation) => operation.action === "plugin" || operation.action === "program")) {
1082
1268
  throw new Error("Cannot automatically recover a journal containing semantic plugin or programmatic operations");
@@ -1096,10 +1282,10 @@ async function recoverPendingApply(targetRoot, adapter, transport = localTranspo
1096
1282
  }
1097
1283
  if (operationNeedsSource(operation) && operation.sourcePath && !await localPathExists(operation.sourcePath)) {
1098
1284
  await rollbackStartedOperations(journal, transport);
1099
- await removeApplyJournal(targetRoot, adapter, transport);
1285
+ await removeApplyJournal(targetRoot, adapter, transport, scope);
1100
1286
  return void 0;
1101
1287
  }
1102
- const backup = started ?? await recordBackup(operation, index, targetRoot, adapter, transport);
1288
+ const backup = started ?? await recordBackup(operation, index, targetRoot, adapter, transport, scope);
1103
1289
  if (!started && isJournaledMutation(operation)) {
1104
1290
  journal.completed.push(backup);
1105
1291
  startedByIndex.set(index, backup);
@@ -1119,11 +1305,12 @@ async function recoverPendingApply(targetRoot, adapter, transport = localTranspo
1119
1305
  }
1120
1306
  async function applyPlanTransactionally(plan, options = {}) {
1121
1307
  const transport = options.transport ?? localTransport;
1308
+ const scope = { installationType: plan.installationType, stateKey: plan.stateKey };
1122
1309
  if (plan.hasBlockingChanges) {
1123
1310
  const blockers = plan.operations.filter((operation) => operation.action === "drift" || operation.action === "conflict");
1124
1311
  throw new Error(`Refusing to apply with blocking changes: ${blockers.map((item) => item.relativeDestPath).join(", ")}`);
1125
1312
  }
1126
- const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, options.lock);
1313
+ const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, options.lock, scope);
1127
1314
  try {
1128
1315
  await assertBaseRevision(plan, transport);
1129
1316
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1131,6 +1318,8 @@ async function applyPlanTransactionally(plan, options = {}) {
1131
1318
  const journal = {
1132
1319
  version: 1,
1133
1320
  adapter: plan.adapter,
1321
+ installationType: plan.installationType,
1322
+ stateKey: plan.stateKey,
1134
1323
  targetRoot: plan.targetRoot,
1135
1324
  baseRevision: plan.baseRevision,
1136
1325
  graphLockDigest,
@@ -1141,6 +1330,8 @@ async function applyPlanTransactionally(plan, options = {}) {
1141
1330
  manifest: {
1142
1331
  version: 2,
1143
1332
  adapter: plan.adapter,
1333
+ installationType: plan.installationType,
1334
+ stateKey: plan.stateKey,
1144
1335
  targetRoot: plan.targetRoot,
1145
1336
  generatedAt: now,
1146
1337
  revision: "pending-apply-0000",
@@ -1156,7 +1347,7 @@ async function applyPlanTransactionally(plan, options = {}) {
1156
1347
  const entries = [];
1157
1348
  for (const [index, operation] of plan.operations.entries()) {
1158
1349
  assertOperationContained(operation, plan.targetRoot);
1159
- 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;
1160
1351
  if (backup) {
1161
1352
  journal.completed.push(backup);
1162
1353
  await writeApplyJournal(journal, transport);
@@ -1182,6 +1373,7 @@ async function applyPlanTransactionally(plan, options = {}) {
1182
1373
  async function uninstall(plan, options = {}) {
1183
1374
  const resolvedOptions = typeof options === "boolean" ? { dryRun: options } : options;
1184
1375
  const transport = resolvedOptions.transport ?? localTransport;
1376
+ const scope = { installationType: plan.installationType, stateKey: plan.stateKey };
1185
1377
  if (resolvedOptions.keepFiles && resolvedOptions.force) {
1186
1378
  throw new Error("--keep-files cannot be combined with --force.");
1187
1379
  }
@@ -1201,6 +1393,8 @@ async function uninstall(plan, options = {}) {
1201
1393
  const finalManifest = withManifestRevision({
1202
1394
  version: 2,
1203
1395
  adapter: plan.adapter,
1396
+ installationType: plan.installationType,
1397
+ stateKey: plan.stateKey,
1204
1398
  targetRoot: plan.targetRoot,
1205
1399
  generatedAt: now,
1206
1400
  revision: "pending-uninstall-0",
@@ -1218,13 +1412,15 @@ async function uninstall(plan, options = {}) {
1218
1412
  });
1219
1413
  }).sort((a, b) => a.path.localeCompare(b.path))
1220
1414
  });
1221
- const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, resolvedOptions.lock);
1415
+ const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, resolvedOptions.lock, scope);
1222
1416
  try {
1223
1417
  await assertBaseRevision(plan, transport);
1224
1418
  const journal = {
1225
1419
  version: 1,
1226
1420
  mode: "uninstall",
1227
1421
  adapter: plan.adapter,
1422
+ installationType: plan.installationType,
1423
+ stateKey: plan.stateKey,
1228
1424
  targetRoot: plan.targetRoot,
1229
1425
  baseRevision: plan.baseRevision,
1230
1426
  graphLockDigest: plan.graphLockDigest,
@@ -1241,7 +1437,7 @@ async function uninstall(plan, options = {}) {
1241
1437
  };
1242
1438
  await writeApplyJournal(journal, transport);
1243
1439
  for (const [index, operation] of (resolvedOptions.keepFiles ? [] : removable).entries()) {
1244
- const backup = await recordBackup(operation, index, plan.targetRoot, plan.adapter, transport);
1440
+ const backup = await recordBackup(operation, index, plan.targetRoot, plan.adapter, transport, scope);
1245
1441
  journal.completed.push(backup);
1246
1442
  await writeApplyJournal(journal, transport);
1247
1443
  await applyOperation(operation, { transport, now, graphLockDigest: plan.graphLockDigest });
@@ -1267,15 +1463,24 @@ async function commitJournalState(journal, transport, entries, now) {
1267
1463
  entries: entries ? entries.sort((a, b) => a.path.localeCompare(b.path)) : journal.manifest.entries
1268
1464
  });
1269
1465
  if (journal.mode === "uninstall" && manifest.entries.length === 0) {
1270
- await removeStateFiles(journal.targetRoot, journal.adapter, transport);
1466
+ await removeStateFiles(journal.targetRoot, journal.adapter, transport, {
1467
+ installationType: journal.installationType,
1468
+ stateKey: journal.stateKey
1469
+ });
1271
1470
  } else {
1272
- 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
+ });
1273
1475
  await writeInstallManifest(manifest, transport);
1274
1476
  }
1275
1477
  if (journal.graphLockPath && journal.graphLock) await writeGraphLock(journal.graphLockPath, journal.graphLock);
1276
1478
  if (journal.graphLockRemovePath) await rm3(journal.graphLockRemovePath, { force: true });
1277
1479
  if (journal.workspaceConfigPath && journal.workspaceConfig) await writeJsonAtomic(journal.workspaceConfigPath, journal.workspaceConfig);
1278
- await removeApplyJournal(journal.targetRoot, journal.adapter, transport);
1480
+ await removeApplyJournal(journal.targetRoot, journal.adapter, transport, {
1481
+ installationType: journal.installationType,
1482
+ stateKey: journal.stateKey
1483
+ });
1279
1484
  return manifest;
1280
1485
  }
1281
1486
  async function applyOperation(operation, context) {
@@ -1285,18 +1490,10 @@ async function applyOperation(operation, context) {
1285
1490
  throw new Error(`Invalid plugin operation missing hash: ${operation.relativeDestPath}`);
1286
1491
  }
1287
1492
  if (context.executePlugins) {
1288
- if (transport.kind !== "local") {
1289
- throw new Error(`Cannot execute semantic plugin install over ${transport.description}. Run plugin installation on the remote host.`);
1290
- }
1291
1493
  if (!operation.semanticCommand || operation.semanticCommand.length === 0) {
1292
1494
  throw new Error(`Invalid plugin operation missing command: ${operation.relativeDestPath}`);
1293
1495
  }
1294
- const command = operation.semanticCommand[0];
1295
- const args = operation.semanticCommand.slice(1);
1296
- if (!command) {
1297
- throw new Error(`Invalid plugin operation missing command: ${operation.relativeDestPath}`);
1298
- }
1299
- await execFileAsync2(command, args);
1496
+ await executePluginInstall(operation, transport);
1300
1497
  }
1301
1498
  return manifestEntryForOperation(operation, {
1302
1499
  now,
@@ -1376,6 +1573,32 @@ async function applyOperation(operation, context) {
1376
1573
  }
1377
1574
  return void 0;
1378
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
+ }
1379
1602
  async function entryForCompletedOperation(operation, transport, now, graphLockDigest) {
1380
1603
  if (operation.action === "remove") return void 0;
1381
1604
  if (operation.action === "create" || operation.action === "update") {
@@ -1423,7 +1646,10 @@ function manifestEntryForOperation(operation, values) {
1423
1646
  };
1424
1647
  }
1425
1648
  async function assertBaseRevision(plan, transport) {
1426
- 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
+ });
1427
1653
  const currentRevision = current?.revision ?? null;
1428
1654
  if (currentRevision !== plan.baseRevision) {
1429
1655
  throw new Error(`Install manifest changed since planning for ${plan.adapter}; replan needed`);
@@ -1472,15 +1698,476 @@ async function mergeWithTransport(sourcePath, destPath, transport, merge) {
1472
1698
  }
1473
1699
 
1474
1700
  // src/install/plan.ts
1475
- 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
+ }
1476
1903
 
1477
1904
  // src/targets/plugins/openclaw.ts
1478
1905
  function openClawPluginInstallCommand(request) {
1479
- 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);
1480
2163
  }
1481
2164
 
1482
2165
  // src/install/plan.ts
1483
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);
1484
2171
  for (const artifact of desiredArtifacts) {
1485
2172
  if (artifact.meta.dependencyRole !== "root" && isGuardedMergeTarget(artifact.type)) {
1486
2173
  throw new Error(`Dependency-provided ${artifact.type} artifacts cannot be installed until per-subentry ownership exists: ${artifact.type}/${artifact.name}`);
@@ -1488,13 +2175,13 @@ async function createCombinedInstallPlan(desiredArtifacts, adapter, targetRoot,
1488
2175
  }
1489
2176
  const desired = [];
1490
2177
  for (const artifact of desiredArtifacts) {
1491
- const op = operationForArtifact(artifact, adapter, targetRoot, artifact.meta);
2178
+ const op = operationForArtifact(artifact, adapter, installRoot, installationType, artifact.meta);
1492
2179
  if (op) {
1493
2180
  desired.push(op);
1494
2181
  }
1495
2182
  }
1496
- await addProgrammaticOperations(desired, adapter, targetRoot);
1497
- return createPlanFromOperations(desired, adapter, targetRoot, manifest, transport, options);
2183
+ await addProgrammaticOperations(desired, adapter, installRoot);
2184
+ return createPlanFromOperations(desired, adapter, installRoot, manifest, transport, { ...options, installationType });
1498
2185
  }
1499
2186
  async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifest, transport, options) {
1500
2187
  const workspaceOwner = options.workspaceOwner;
@@ -1556,11 +2243,38 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
1556
2243
  }
1557
2244
  const currentHash = await transport.hashPath(op.destPath);
1558
2245
  if (!existing) {
1559
- 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
+ }
1560
2263
  continue;
1561
2264
  }
1562
2265
  if (currentHash !== existing.hash) {
1563
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
+ }
1564
2278
  operations.push({
1565
2279
  ...op,
1566
2280
  action: "drift",
@@ -1587,7 +2301,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
1587
2301
  }
1588
2302
  for (const entry of effectiveEntries) {
1589
2303
  if (desired.has(entry.path)) continue;
1590
- const destPath = join5(targetRoot, entry.path);
2304
+ const destPath = join8(targetRoot, entry.path);
1591
2305
  if (!await transport.pathExists(destPath)) continue;
1592
2306
  const currentHash = await transport.hashPath(destPath);
1593
2307
  if (workspaceOwner && !entryOwnedByWorkspace(entry, workspaceOwner)) {
@@ -1608,7 +2322,8 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
1608
2322
  channel: entry.channel,
1609
2323
  packageName: entry.packageName,
1610
2324
  composedFrom: entry.composedFrom,
1611
- ...operationMetadataFromEntry(entry)
2325
+ ...operationMetadataFromEntry(entry),
2326
+ ...options.forceDrift ? { action: "remove", reason: "force removing drifted stale managed destination" } : {}
1612
2327
  });
1613
2328
  } else {
1614
2329
  operations.push({
@@ -1632,6 +2347,8 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
1632
2347
  operations.sort((a, b) => a.relativeDestPath.localeCompare(b.relativeDestPath));
1633
2348
  return {
1634
2349
  adapter: adapter.name,
2350
+ installationType: options.installationType ?? defaultInstallationType,
2351
+ stateKey: options.stateKey,
1635
2352
  targetRoot,
1636
2353
  operations,
1637
2354
  hasBlockingChanges: operations.some((op) => op.action === "drift" || op.action === "conflict"),
@@ -1694,7 +2411,7 @@ async function canStrictlyAdoptLegacyEntry(entry, op, targetRoot, transport) {
1694
2411
  if (entry.artifactType !== op.artifactType || entry.artifactName !== op.artifactName) return false;
1695
2412
  if (!op.desiredHash || entry.sourceHash !== op.desiredHash) return false;
1696
2413
  if (!packageIdentityMatches(entry, op)) return false;
1697
- const destPath = join5(targetRoot, entry.path);
2414
+ const destPath = join8(targetRoot, entry.path);
1698
2415
  if (!await transport.pathExists(destPath)) return false;
1699
2416
  return await transport.hashPath(destPath) === entry.hash;
1700
2417
  }
@@ -1824,7 +2541,7 @@ function keepForeignManifestEntryOperation(entry, targetRoot, workspaceOwner, op
1824
2541
  artifactType: entry.artifactType,
1825
2542
  artifactName: entry.artifactName,
1826
2543
  kind: entry.kind,
1827
- destPath: operation?.destPath ?? join5(targetRoot, entry.path),
2544
+ destPath: operation?.destPath ?? join8(targetRoot, entry.path),
1828
2545
  relativeDestPath: entry.path,
1829
2546
  desiredHash: entry.sourceHash,
1830
2547
  currentHash: currentHash ?? operation?.currentHash ?? entry.hash,
@@ -1846,11 +2563,18 @@ function normalizeOperationOwners(op) {
1846
2563
  function isGuardedMergeTarget(type) {
1847
2564
  return type === "mcp" || type === "hooks" || type === "settings" || type === "plugins";
1848
2565
  }
1849
- function operationForArtifact(artifact, adapter, targetRoot, meta) {
1850
- const target = adapter.targets[artifact.type];
1851
- 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
+ }
1852
2574
  const metadata = operationMetadataFromDesired(artifact, meta);
1853
- 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);
1854
2578
  assertSafeInstallName(installName, `${artifact.type}/${artifact.name}`);
1855
2579
  if (artifact.type === "plugins" && target.semantic === "openclaw-plugin") {
1856
2580
  const sourcePath = artifact.stagedPath ?? artifact.sourcePath;
@@ -1871,7 +2595,26 @@ function operationForArtifact(artifact, adapter, targetRoot, meta) {
1871
2595
  ...metadata
1872
2596
  };
1873
2597
  }
1874
- 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);
1875
2618
  return {
1876
2619
  action: "create",
1877
2620
  artifactType: artifact.type,
@@ -1886,9 +2629,27 @@ function operationForArtifact(artifact, adapter, targetRoot, meta) {
1886
2629
  packageName: artifact.packageName,
1887
2630
  mergeStrategy: target.merge,
1888
2631
  composedFrom: metadata.composedFrom,
1889
- ...metadata
2632
+ ...metadata,
2633
+ installName
1890
2634
  };
1891
2635
  }
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
+ }
1892
2653
  function isFileTarget(dest) {
1893
2654
  return /\.(json|jsonc|toml|md)$/i.test(dest);
1894
2655
  }
@@ -1923,13 +2684,19 @@ function summarizePlan(plan) {
1923
2684
  }
1924
2685
  return summary;
1925
2686
  }
2687
+ function isPendingInstallAction(action) {
2688
+ return action !== "skip" && action !== "keep";
2689
+ }
2690
+ function isPendingInstallOperation(operation) {
2691
+ return isPendingInstallAction(operation.action);
2692
+ }
1926
2693
 
1927
2694
  // src/install/uninstall.ts
1928
- import { join as join6 } from "path";
2695
+ import { join as join9 } from "path";
1929
2696
  async function createUninstallPlan(manifest, transport = localTransport) {
1930
2697
  const operations = [];
1931
2698
  for (const entry of manifest.entries) {
1932
- const destPath = join6(manifest.targetRoot, entry.path);
2699
+ const destPath = join9(manifest.targetRoot, entry.path);
1933
2700
  if (!await transport.pathExists(destPath)) continue;
1934
2701
  const currentHash = await transport.hashPath(destPath);
1935
2702
  if (currentHash !== entry.hash) {
@@ -1975,6 +2742,8 @@ async function createUninstallPlan(manifest, transport = localTransport) {
1975
2742
  operations.sort((a, b) => a.relativeDestPath.localeCompare(b.relativeDestPath));
1976
2743
  return {
1977
2744
  adapter: manifest.adapter,
2745
+ installationType: "installationType" in manifest ? manifest.installationType : defaultInstallationType,
2746
+ stateKey: "stateKey" in manifest ? manifest.stateKey : void 0,
1978
2747
  targetRoot: manifest.targetRoot,
1979
2748
  operations,
1980
2749
  hasBlockingChanges: operations.some((operation) => operation.action === "conflict"),
@@ -1983,13 +2752,15 @@ async function createUninstallPlan(manifest, transport = localTransport) {
1983
2752
  };
1984
2753
  }
1985
2754
  async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter, transport = localTransport, options = {}) {
1986
- 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 });
1987
2758
  const ownersByPath = new Map(
1988
2759
  desiredPlan.operations.filter((operation) => operation.owners?.length).map((operation) => [operation.relativeDestPath, operation.owners ?? []])
1989
2760
  );
1990
2761
  const operations = [];
1991
2762
  for (const entry of manifest.entries) {
1992
- const destPath = join6(manifest.targetRoot, entry.path);
2763
+ const destPath = join9(manifest.targetRoot, entry.path);
1993
2764
  if (!await transport.pathExists(destPath)) continue;
1994
2765
  const currentHash = await transport.hashPath(destPath);
1995
2766
  const remainingOwners = ownersByPath.get(entry.path) ?? [];
@@ -2060,6 +2831,8 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
2060
2831
  operations.sort((a, b) => a.relativeDestPath.localeCompare(b.relativeDestPath));
2061
2832
  return {
2062
2833
  adapter: manifest.adapter,
2834
+ installationType,
2835
+ stateKey,
2063
2836
  targetRoot: manifest.targetRoot,
2064
2837
  operations,
2065
2838
  hasBlockingChanges: operations.some((operation) => operation.action === "conflict"),
@@ -2107,7 +2880,7 @@ var channelLabels = {
2107
2880
  ejected: "EJECTED"
2108
2881
  };
2109
2882
  function formatPlan(plan) {
2110
- const lines = [`Plan for ${plan.adapter} at ${plan.targetRoot}`];
2883
+ const lines = [`Plan for ${plan.adapter}/${plan.installationType} at ${plan.targetRoot}`];
2111
2884
  if (plan.migrationReport) {
2112
2885
  const dropped = plan.migrationReport.dropped.length > 0 ? `; dropped unmanaged ${plan.migrationReport.dropped.join(", ")}` : "";
2113
2886
  lines.push(`MIGRATE adopted ${plan.migrationReport.adopted} legacy entries${dropped}`);
@@ -2279,15 +3052,15 @@ function ownerChains(lock, nodeId) {
2279
3052
  }
2280
3053
 
2281
3054
  // src/source/git.ts
2282
- import { execFile as execFile3 } from "child_process";
2283
- import { cp as cp2, mkdir as mkdir6, rename as rename3, rm as rm4, writeFile as writeFile7 } from "fs/promises";
2284
- import { homedir } from "os";
2285
- import { basename as basename4, dirname as dirname7, join as join9, resolve as resolve6 } from "path";
2286
- 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";
2287
3060
 
2288
3061
  // src/model/package.ts
2289
- import { readFile as readFile7 } from "fs/promises";
2290
- import { join as join7 } from "path";
3062
+ import { readFile as readFile10 } from "fs/promises";
3063
+ import { join as join10 } from "path";
2291
3064
  import { parse as parse2, printParseErrorCode as printParseErrorCode2 } from "jsonc-parser";
2292
3065
  import { z as z5 } from "zod";
2293
3066
  var legacyArtifactTypeSchema = z5.enum([
@@ -2304,10 +3077,12 @@ var legacyArtifactTypeSchema = z5.enum([
2304
3077
  var runtimeListSchema = z5.array(z5.string().min(1));
2305
3078
  var packageProvideBaseSchema = z5.object({
2306
3079
  path: z5.string().min(1),
3080
+ format: artifactFormatSchema.optional(),
2307
3081
  assets: z5.array(packageAssetSchema).optional(),
2308
3082
  required: z5.boolean().optional()
2309
3083
  });
2310
3084
  var packageItemSchema = z5.object({
3085
+ format: artifactFormatSchema.optional(),
2311
3086
  requires: z5.array(packageItemRequireSchema).optional(),
2312
3087
  compose: z5.array(packageComposeEntrySchema).optional(),
2313
3088
  runtimes: runtimeListSchema.optional()
@@ -2362,7 +3137,7 @@ var packageManifestNames = [...openPackManifestNames, ...legacyPackageManifestNa
2362
3137
  var warnedLegacyManifestPaths = /* @__PURE__ */ new Set();
2363
3138
  async function findPackageManifestPath(root, options = {}) {
2364
3139
  for (const name of packageManifestNames) {
2365
- const candidate = join7(root, name);
3140
+ const candidate = join10(root, name);
2366
3141
  if (!await pathExists(candidate)) continue;
2367
3142
  if (isLegacyPackageManifestName(name) && options.warnLegacy !== false && !warnedLegacyManifestPaths.has(candidate)) {
2368
3143
  warnedLegacyManifestPaths.add(candidate);
@@ -2375,7 +3150,7 @@ async function findPackageManifestPath(root, options = {}) {
2375
3150
  async function readPackageManifest(root) {
2376
3151
  const path = await findPackageManifestPath(root);
2377
3152
  if (!path) return void 0;
2378
- const content = await readFile7(path, "utf8");
3153
+ const content = await readFile10(path, "utf8");
2379
3154
  const errors = [];
2380
3155
  const parsed = parse2(content, errors, { allowTrailingComma: true, disallowComments: false });
2381
3156
  if (errors.length > 0) {
@@ -2385,7 +3160,7 @@ async function readPackageManifest(root) {
2385
3160
  return parsePackageManifest(parsed, path);
2386
3161
  }
2387
3162
  function parsePackageManifest(parsed, path = "package manifest") {
2388
- if (!isRecord3(parsed)) {
3163
+ if (!isRecord4(parsed)) {
2389
3164
  throw new Error(`Invalid package manifest ${path}: expected an object`);
2390
3165
  }
2391
3166
  if (parsed.schemaVersion === 1) {
@@ -2413,9 +3188,9 @@ function v1OpenPackViolations(manifest) {
2413
3188
  }
2414
3189
  const provides = Array.isArray(manifest.provides) ? manifest.provides : [];
2415
3190
  for (const [index, provide] of provides.entries()) {
2416
- if (!isRecord3(provide)) continue;
3191
+ if (!isRecord4(provide)) continue;
2417
3192
  if (provide.type === "fragments") violations.push(`provides[${index}].type=fragments`);
2418
- for (const key of ["items", "compose", "runtimes"]) {
3193
+ for (const key of ["format", "items", "compose", "runtimes"]) {
2419
3194
  if (Object.prototype.hasOwnProperty.call(provide, key)) violations.push(`provides[${index}].${key}`);
2420
3195
  }
2421
3196
  }
@@ -2424,13 +3199,14 @@ function v1OpenPackViolations(manifest) {
2424
3199
  function isLegacyPackageManifestName(name) {
2425
3200
  return legacyPackageManifestNames.includes(name);
2426
3201
  }
2427
- function isRecord3(value) {
3202
+ function isRecord4(value) {
2428
3203
  return typeof value === "object" && value !== null && !Array.isArray(value);
2429
3204
  }
2430
3205
 
2431
3206
  // src/source/local.ts
3207
+ import { createHash as createHash3 } from "crypto";
2432
3208
  import { readdir, stat as stat3 } from "fs/promises";
2433
- 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";
2434
3210
  var LocalSourceDriver = class {
2435
3211
  name = "local";
2436
3212
  async resolve(source) {
@@ -2450,7 +3226,7 @@ var LocalSourceDriver = class {
2450
3226
  packageName: manifest?.name,
2451
3227
  packageVersion: manifest?.version,
2452
3228
  mode: "pinned",
2453
- sourceHash: await hashPath(resolvedPath)
3229
+ sourceHash: await hashLocalSource(resolvedPath, manifest)
2454
3230
  };
2455
3231
  }
2456
3232
  async list(resolved) {
@@ -2460,29 +3236,29 @@ var LocalSourceDriver = class {
2460
3236
  }
2461
3237
  const artifacts = [];
2462
3238
  const root = resolved.resolvedPath;
2463
- 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")]);
2464
3240
  if (instructions) {
2465
3241
  artifacts.push({
2466
3242
  type: "instructions",
2467
- name: basename3(instructions),
3243
+ name: basename6(instructions),
2468
3244
  sourcePath: instructions,
2469
- relativePath: basename3(instructions),
3245
+ relativePath: basename6(instructions),
2470
3246
  kind: "file",
2471
3247
  hash: await hashPath(instructions),
2472
3248
  packageName: resolved.packageName,
2473
3249
  channel: "managed"
2474
3250
  });
2475
3251
  }
2476
- const rulesDir = join8(root, "rules");
3252
+ const rulesDir = join11(root, "rules");
2477
3253
  if (await pathExists(rulesDir)) {
2478
3254
  for (const entry of await sortedDirEntries(rulesDir)) {
2479
- const full = join8(rulesDir, entry.name);
3255
+ const full = join11(rulesDir, entry.name);
2480
3256
  if (entry.isFile()) {
2481
3257
  artifacts.push({
2482
3258
  type: "rules",
2483
3259
  name: entry.name,
2484
3260
  sourcePath: full,
2485
- relativePath: join8("rules", entry.name),
3261
+ relativePath: join11("rules", entry.name),
2486
3262
  kind: "file",
2487
3263
  hash: await hashPath(full),
2488
3264
  packageName: resolved.packageName,
@@ -2491,16 +3267,16 @@ var LocalSourceDriver = class {
2491
3267
  }
2492
3268
  }
2493
3269
  }
2494
- const fragmentsDir = join8(root, "fragments");
3270
+ const fragmentsDir = join11(root, "fragments");
2495
3271
  if (await pathExists(fragmentsDir)) {
2496
3272
  for (const entry of await sortedDirEntries(fragmentsDir)) {
2497
- const full = join8(fragmentsDir, entry.name);
3273
+ const full = join11(fragmentsDir, entry.name);
2498
3274
  if (entry.isFile()) {
2499
3275
  artifacts.push({
2500
3276
  type: "fragments",
2501
3277
  name: entry.name,
2502
3278
  sourcePath: full,
2503
- relativePath: join8("fragments", entry.name),
3279
+ relativePath: join11("fragments", entry.name),
2504
3280
  kind: "file",
2505
3281
  hash: await hashPath(full),
2506
3282
  packageName: resolved.packageName,
@@ -2509,16 +3285,16 @@ var LocalSourceDriver = class {
2509
3285
  }
2510
3286
  }
2511
3287
  }
2512
- const skillsDir = join8(root, "skills");
3288
+ const skillsDir = join11(root, "skills");
2513
3289
  if (await pathExists(skillsDir)) {
2514
3290
  for (const entry of await sortedDirEntries(skillsDir)) {
2515
- const full = join8(skillsDir, entry.name);
3291
+ const full = join11(skillsDir, entry.name);
2516
3292
  if (entry.isDirectory()) {
2517
3293
  artifacts.push({
2518
3294
  type: "skills",
2519
3295
  name: entry.name,
2520
3296
  sourcePath: full,
2521
- relativePath: join8("skills", entry.name),
3297
+ relativePath: join11("skills", entry.name),
2522
3298
  kind: "dir",
2523
3299
  hash: await hashPath(full),
2524
3300
  packageName: resolved.packageName,
@@ -2529,7 +3305,7 @@ var LocalSourceDriver = class {
2529
3305
  type: "skills",
2530
3306
  name: entry.name.replace(/\.md$/, ""),
2531
3307
  sourcePath: full,
2532
- relativePath: join8("skills", entry.name),
3308
+ relativePath: join11("skills", entry.name),
2533
3309
  kind: "file",
2534
3310
  hash: await hashPath(full),
2535
3311
  packageName: resolved.packageName,
@@ -2539,7 +3315,7 @@ var LocalSourceDriver = class {
2539
3315
  }
2540
3316
  }
2541
3317
  for (const type of ["commands", "subagents", "mcp", "hooks", "settings", "plugins"]) {
2542
- const dir = join8(root, type);
3318
+ const dir = join11(root, type);
2543
3319
  if (!await pathExists(dir)) continue;
2544
3320
  artifacts.push(...await listGenericArtifacts(type, dir, type, resolved.packageName));
2545
3321
  }
@@ -2555,7 +3331,7 @@ var LocalSourceDriver = class {
2555
3331
  findings.push({ level: "warning", message: "No instructions.md or AGENTS.md found", path: resolved.resolvedPath });
2556
3332
  }
2557
3333
  for (const artifact of artifacts.filter((item) => item.type === "skills" && item.kind === "dir")) {
2558
- if (!await pathExists(join8(artifact.sourcePath, "SKILL.md"))) {
3334
+ if (!await pathExists(join11(artifact.sourcePath, "SKILL.md"))) {
2559
3335
  findings.push({ level: "warning", message: `Skill directory has no SKILL.md: ${artifact.name}`, path: artifact.sourcePath });
2560
3336
  }
2561
3337
  }
@@ -2568,6 +3344,26 @@ var LocalSourceDriver = class {
2568
3344
  return resolved;
2569
3345
  }
2570
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
+ }
2571
3367
  async function firstExisting(paths) {
2572
3368
  for (const path of paths) {
2573
3369
  if (await pathExists(path)) return path;
@@ -2582,27 +3378,27 @@ async function listFromManifest(root, packageName) {
2582
3378
  if (!manifest) return [];
2583
3379
  const artifacts = [];
2584
3380
  for (const provide of manifest.provides) {
2585
- const full = join8(root, provide.path);
3381
+ const full = join11(root, provide.path);
2586
3382
  if (!await pathExists(full)) continue;
2587
3383
  const stats = await stat3(full);
2588
3384
  if (provide.type === "instructions") {
2589
3385
  if (stats.isFile()) {
2590
- 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)));
2591
3387
  }
2592
3388
  continue;
2593
3389
  }
2594
3390
  if (stats.isDirectory()) {
2595
3391
  for (const entry of await sortedDirEntries(full)) {
2596
- const child = join8(full, entry.name);
3392
+ const child = join11(full, entry.name);
2597
3393
  if ((provide.type === "skills" || provide.type === "plugins" || provide.type === "subagents") && entry.isDirectory()) {
2598
- 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));
2599
3395
  } else if (entry.isFile()) {
2600
3396
  const name = provide.type === "rules" && entry.name.endsWith(".md") ? entry.name : entry.name;
2601
- 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));
2602
3398
  }
2603
3399
  }
2604
3400
  } else if (stats.isFile()) {
2605
- 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)));
2606
3402
  }
2607
3403
  }
2608
3404
  return artifacts;
@@ -2610,11 +3406,11 @@ async function listFromManifest(root, packageName) {
2610
3406
  async function listGenericArtifacts(type, dir, relativeRoot, packageName) {
2611
3407
  const artifacts = [];
2612
3408
  for (const entry of await sortedDirEntries(dir)) {
2613
- const full = join8(dir, entry.name);
3409
+ const full = join11(dir, entry.name);
2614
3410
  if (entry.isDirectory()) {
2615
- 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));
2616
3412
  } else if (entry.isFile()) {
2617
- 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));
2618
3414
  }
2619
3415
  }
2620
3416
  return artifacts;
@@ -2628,6 +3424,7 @@ async function artifactForFile(type, name, sourcePath, relativePath, packageName
2628
3424
  relativePath,
2629
3425
  kind: "file",
2630
3426
  hash: await hashPath(sourcePath),
3427
+ format: item.format ?? provide?.format,
2631
3428
  packageName,
2632
3429
  channel: "managed",
2633
3430
  assets: provide?.assets,
@@ -2646,6 +3443,7 @@ async function artifactForDir(type, name, sourcePath, relativePath, packageName,
2646
3443
  relativePath,
2647
3444
  kind: "dir",
2648
3445
  hash: await hashPath(sourcePath),
3446
+ format: item.format ?? provide?.format,
2649
3447
  packageName,
2650
3448
  channel: "managed",
2651
3449
  assets: provide?.assets,
@@ -2659,7 +3457,7 @@ function itemMetadata(provide, itemName) {
2659
3457
  if (!provide || !("items" in provide) || !provide.items || !itemName) return {};
2660
3458
  const item = provide.items[itemName];
2661
3459
  if (!item) return {};
2662
- return { requires: item.requires, compose: item.compose, runtimes: item.runtimes };
3460
+ return { format: item.format, requires: item.requires, compose: item.compose, runtimes: item.runtimes };
2663
3461
  }
2664
3462
  function provideRuntimes(provide) {
2665
3463
  return provide && "runtimes" in provide ? provide.runtimes : void 0;
@@ -2669,7 +3467,7 @@ function manifestRuntimes(manifest) {
2669
3467
  }
2670
3468
 
2671
3469
  // src/source/git.ts
2672
- var execFileAsync3 = promisify3(execFile3);
3470
+ var execFileAsync4 = promisify4(execFile4);
2673
3471
  var GitSourceDriver = class {
2674
3472
  name = "git";
2675
3473
  local = new LocalSourceDriver();
@@ -2690,8 +3488,8 @@ var GitSourceDriver = class {
2690
3488
  async fetch(resolved) {
2691
3489
  return withFilesystemLock(`${resolved.resolvedPath}.lock`, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
2692
3490
  const parsed = parseGitSource(resolved.source);
2693
- await mkdir6(resolve6(resolved.resolvedPath, ".."), { recursive: true });
2694
- if (!await pathExists(join9(resolved.resolvedPath, ".git"))) {
3491
+ await mkdir8(resolve6(resolved.resolvedPath, ".."), { recursive: true });
3492
+ if (!await pathExists(join12(resolved.resolvedPath, ".git"))) {
2695
3493
  if (resolved.frozenLock) {
2696
3494
  throw new Error(`Frozen lock requires cached git checkout at ${resolved.resolvedPath}`);
2697
3495
  }
@@ -2758,20 +3556,20 @@ function parseGitSource(source) {
2758
3556
  throw new Error(`Invalid git source: ${source}`);
2759
3557
  }
2760
3558
  function cachePathFor(url, cacheRoot) {
2761
- const root = cacheRoot ? resolve6(cacheRoot) : join9(homedir(), ".agentwheel", "cache");
3559
+ const root = cacheRoot ? resolve6(cacheRoot) : join12(homedir2(), ".agentwheel", "cache");
2762
3560
  const slug2 = url.replace(/^[a-z]+:\/\//i, "").replace(/\.git$/i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
2763
- return join9(root, slug2 || basename4(url));
3561
+ return join12(root, slug2 || basename7(url));
2764
3562
  }
2765
3563
  async function git(args) {
2766
- return execFileAsync3("git", args, { maxBuffer: 1024 * 1024 * 10 });
3564
+ return execFileAsync4("git", args, { maxBuffer: 1024 * 1024 * 10 });
2767
3565
  }
2768
3566
  async function snapshotCheckout(checkoutPath, commit) {
2769
- const snapshotPath = join9(dirname7(checkoutPath), `${basename4(checkoutPath)}-${commit.slice(0, 12)}`);
3567
+ const snapshotPath = join12(dirname9(checkoutPath), `${basename7(checkoutPath)}-${commit.slice(0, 12)}`);
2770
3568
  if (await pathExists(snapshotPath)) return snapshotPath;
2771
- 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()}`);
2772
3570
  await rm4(tempPath, { recursive: true, force: true });
2773
3571
  await cp2(checkoutPath, tempPath, { recursive: true, dereference: true });
2774
- await rm4(join9(tempPath, ".git"), { recursive: true, force: true });
3572
+ await rm4(join12(tempPath, ".git"), { recursive: true, force: true });
2775
3573
  try {
2776
3574
  await rename3(tempPath, snapshotPath);
2777
3575
  } catch (error) {
@@ -2782,12 +3580,12 @@ async function snapshotCheckout(checkoutPath, commit) {
2782
3580
  return snapshotPath;
2783
3581
  }
2784
3582
  async function withFilesystemLock(lockPath, timeoutMs, fn) {
2785
- await mkdir6(dirname7(lockPath), { recursive: true });
3583
+ await mkdir8(dirname9(lockPath), { recursive: true });
2786
3584
  const started = Date.now();
2787
3585
  while (true) {
2788
3586
  try {
2789
- await mkdir6(lockPath);
2790
- 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");
2791
3589
  break;
2792
3590
  } catch (error) {
2793
3591
  if (!isAlreadyExists2(error)) throw error;
@@ -2808,14 +3606,14 @@ function isAlreadyExists2(error) {
2808
3606
  }
2809
3607
 
2810
3608
  // src/source/skillkit.ts
2811
- import { cp as cp3, mkdir as mkdir7, readFile as readFile8, rm as rm5 } from "fs/promises";
2812
- import { homedir as homedir2 } from "os";
2813
- 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";
2814
3612
  import * as defaultSkillKit from "@skillkit/core";
2815
3613
 
2816
3614
  // src/source/skill-artifacts.ts
2817
3615
  import { readdir as readdir2, stat as stat4 } from "fs/promises";
2818
- 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";
2819
3617
  async function artifactsFromSkillPaths(paths, packageName) {
2820
3618
  const artifacts = [];
2821
3619
  const seen = /* @__PURE__ */ new Set();
@@ -2837,28 +3635,28 @@ async function discoverSkillPaths(root) {
2837
3635
  async function artifactFromSkillPath(item, packageName) {
2838
3636
  const stats = await stat4(item.path);
2839
3637
  if (stats.isDirectory()) {
2840
- const skillMd = join10(item.path, "SKILL.md");
3638
+ const skillMd = join13(item.path, "SKILL.md");
2841
3639
  if (!await pathExists(skillMd)) return void 0;
2842
- const name = sanitizeSkillName(item.name ?? basename5(item.path));
3640
+ const name = sanitizeSkillName(item.name ?? basename8(item.path));
2843
3641
  return {
2844
3642
  type: "skills",
2845
3643
  name,
2846
3644
  sourcePath: item.path,
2847
- relativePath: join10("skills", name),
3645
+ relativePath: join13("skills", name),
2848
3646
  kind: "dir",
2849
3647
  hash: await hashPath(item.path),
2850
3648
  packageName,
2851
3649
  channel: "managed"
2852
3650
  };
2853
3651
  }
2854
- if (stats.isFile() && basename5(item.path).toLowerCase() === "skill.md") {
2855
- const dir = dirname8(item.path);
2856
- 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));
2857
3655
  return {
2858
3656
  type: "skills",
2859
3657
  name,
2860
3658
  sourcePath: dir,
2861
- relativePath: join10("skills", name),
3659
+ relativePath: join13("skills", name),
2862
3660
  kind: "dir",
2863
3661
  hash: await hashPath(dir),
2864
3662
  packageName,
@@ -2866,12 +3664,12 @@ async function artifactFromSkillPath(item, packageName) {
2866
3664
  };
2867
3665
  }
2868
3666
  if (stats.isFile() && extname2(item.path).toLowerCase() === ".md") {
2869
- const name = sanitizeSkillName(item.name ?? basename5(item.path, ".md"));
3667
+ const name = sanitizeSkillName(item.name ?? basename8(item.path, ".md"));
2870
3668
  return {
2871
3669
  type: "skills",
2872
3670
  name,
2873
3671
  sourcePath: item.path,
2874
- relativePath: join10("skills", `${name}.md`),
3672
+ relativePath: join13("skills", `${name}.md`),
2875
3673
  kind: "file",
2876
3674
  hash: await hashPath(item.path),
2877
3675
  packageName,
@@ -2889,7 +3687,7 @@ async function walk(dir, paths) {
2889
3687
  }
2890
3688
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
2891
3689
  if (!entry.isDirectory() || entry.name === ".git" || entry.name === "node_modules") continue;
2892
- await walk(join10(dir, entry.name), paths);
3690
+ await walk(join13(dir, entry.name), paths);
2893
3691
  }
2894
3692
  }
2895
3693
  function sanitizeSkillName(name) {
@@ -2911,7 +3709,7 @@ var SkillKitSourceDriver = class {
2911
3709
  driver: this.name,
2912
3710
  source,
2913
3711
  resolvedPath,
2914
- packageName: `skillkit/${basename6(resolvedPath)}`,
3712
+ packageName: `skillkit/${basename9(resolvedPath)}`,
2915
3713
  mode: options.mode ?? "pinned",
2916
3714
  sourceHash: await hashPath(resolvedPath)
2917
3715
  };
@@ -2945,7 +3743,7 @@ var SkillKitSourceDriver = class {
2945
3743
  if (!provider?.clone) {
2946
3744
  throw new Error("SkillKit provider API unavailable or cannot resolve source. Expected @skillkit/core detectProvider().clone().");
2947
3745
  }
2948
- await mkdir7(dirname9(resolved.resolvedPath), { recursive: true });
3746
+ await mkdir9(dirname11(resolved.resolvedPath), { recursive: true });
2949
3747
  const result = await provider.clone(providerSpec, resolved.resolvedPath, {});
2950
3748
  if (!result.success || !result.path) {
2951
3749
  throw new Error(`SkillKit provider failed to fetch ${spec}: ${result.error ?? "unknown error"}`);
@@ -2989,9 +3787,9 @@ var SkillKitSourceDriver = class {
2989
3787
  throw new Error("SkillKit translateSkill API unavailable");
2990
3788
  }
2991
3789
  for (const skill of this.discover(resolved.resolvedPath)) {
2992
- const skillMd = join11(skill.path, "SKILL.md");
3790
+ const skillMd = join14(skill.path, "SKILL.md");
2993
3791
  if (await pathExists(skillMd)) {
2994
- this.core.translateSkill(await readFile8(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
3792
+ this.core.translateSkill(await readFile11(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
2995
3793
  }
2996
3794
  }
2997
3795
  return resolved;
@@ -3020,8 +3818,8 @@ function normalizeProviderSource(spec) {
3020
3818
  return spec;
3021
3819
  }
3022
3820
  function cachePathFor2(spec, cacheRoot) {
3023
- const root = cacheRoot ? resolve7(cacheRoot) : join11(homedir2(), ".agentwheel", "cache");
3024
- return join11(root, "skillkit", packageSlug(spec));
3821
+ const root = cacheRoot ? resolve7(cacheRoot) : join14(homedir3(), ".agentwheel", "cache");
3822
+ return join14(root, "skillkit", packageSlug(spec));
3025
3823
  }
3026
3824
  function packageSlug(spec) {
3027
3825
  return spec.replace(/^[a-z]+:\/\//i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "source";
@@ -3034,7 +3832,7 @@ function mapSeverity(severity) {
3034
3832
 
3035
3833
  // src/source/vercel-skills.ts
3036
3834
  import { stat as stat5 } from "fs/promises";
3037
- 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";
3038
3836
  var VercelSkillsSourceDriver = class {
3039
3837
  name = "vercel-skills";
3040
3838
  git = new GitSourceDriver();
@@ -3049,7 +3847,7 @@ var VercelSkillsSourceDriver = class {
3049
3847
  driver: this.name,
3050
3848
  source,
3051
3849
  resolvedPath,
3052
- packageName: `vercel/${basename7(resolvedPath)}`,
3850
+ packageName: `vercel/${basename10(resolvedPath)}`,
3053
3851
  mode: options.mode ?? "pinned",
3054
3852
  sourceHash: await hashPath(resolvedPath)
3055
3853
  };
@@ -3070,7 +3868,7 @@ var VercelSkillsSourceDriver = class {
3070
3868
  driver: "git",
3071
3869
  source: parsed.gitSource
3072
3870
  });
3073
- const resolvedPath = parsed.subpath ? join12(fetched.resolvedPath, parsed.subpath) : fetched.resolvedPath;
3871
+ const resolvedPath = parsed.subpath ? join15(fetched.resolvedPath, parsed.subpath) : fetched.resolvedPath;
3074
3872
  if (!await pathExists(resolvedPath)) {
3075
3873
  throw new Error(`Vercel skills subpath not found: ${parsed.subpath}`);
3076
3874
  }
@@ -3159,14 +3957,14 @@ function getSourceDriver(name = "local") {
3159
3957
  }
3160
3958
 
3161
3959
  // src/staging/staging.ts
3162
- import { chmod, cp as cp5, mkdir as mkdir9, mkdtemp as mkdtemp2, readdir as readdir5, stat as stat8 } from "fs/promises";
3163
- 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";
3164
3962
  import { tmpdir as tmpdir3 } from "os";
3165
3963
 
3166
3964
  // src/compose/markdown.ts
3167
- import { createHash as createHash3 } from "crypto";
3168
- import { readdir as readdir3, readFile as readFile9, stat as stat6, writeFile as writeFile8 } from "fs/promises";
3169
- 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";
3170
3968
  var includePattern = /<!--\s*openpack:include(\?)?\s+([^>]+?)\s*-->/g;
3171
3969
  var escapedIncludePattern = /<!--\s*openpack\\:include(\?)?\s+([^>]+?)\s*-->/g;
3172
3970
  var generatedPattern = /<!--\s*(?:BEGIN|END)\s+openpack:include\b/;
@@ -3181,7 +3979,7 @@ async function expandMarkdownIncludes(artifacts, packageRoot, options = {}) {
3181
3979
  const composedFrom = [];
3182
3980
  for (const file of files) {
3183
3981
  const result = await expandFile(file, packageRoot, composeEntriesForFile(artifact, file), artifactPaths, options);
3184
- if (result.changed) await writeFile8(file, result.content, "utf8");
3982
+ if (result.changed) await writeFile10(file, result.content, "utf8");
3185
3983
  composedFrom.push(...result.composedFrom);
3186
3984
  }
3187
3985
  const stagedPath = artifact.stagedPath ?? artifact.sourcePath;
@@ -3203,7 +4001,7 @@ async function validateMarkdownIncludes(artifacts, packageRoot, options = {}) {
3203
4001
  }
3204
4002
  }
3205
4003
  async function expandFile(file, packageRoot, appendEntries, artifactPaths, options) {
3206
- const raw = await readFile9(file, "utf8");
4004
+ const raw = await readFile12(file, "utf8");
3207
4005
  const owner = ownerSelector(packageRoot, file, options.nodeId);
3208
4006
  const expanded = await expandContent(raw, packageRoot, [owner], artifactPaths, options);
3209
4007
  let content = expanded.content;
@@ -3296,7 +4094,7 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
3296
4094
  if (!stats.isFile()) {
3297
4095
  throw new Error(`OpenPack include is not a file: ${displaySelector}`);
3298
4096
  }
3299
- const raw = sourceContent ?? await readFile9(sourcePath, "utf8");
4097
+ const raw = sourceContent ?? await readFile12(sourcePath, "utf8");
3300
4098
  const { optional: _optional, markers: _markers, chain: _chain, ...childOptions } = options;
3301
4099
  const expanded = await expandContent(raw, includePackageRoot, [...options.chain, displaySelector], includeArtifactPaths, {
3302
4100
  ...childOptions,
@@ -3372,7 +4170,7 @@ async function listMarkdownFiles(root) {
3372
4170
  const out = [];
3373
4171
  async function walk2(dir) {
3374
4172
  for (const entry of (await readdir3(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
3375
- const full = join13(dir, entry.name);
4173
+ const full = join16(dir, entry.name);
3376
4174
  if (entry.isDirectory()) {
3377
4175
  await walk2(full);
3378
4176
  } else if (entry.isFile() && extname3(entry.name).toLowerCase() === ".md") {
@@ -3386,7 +4184,7 @@ async function listMarkdownFiles(root) {
3386
4184
  function composeEntriesForFile(artifact, file) {
3387
4185
  if (!artifact.compose?.length) return [];
3388
4186
  if (artifact.kind === "file") return [resolve9(artifact.stagedPath ?? artifact.sourcePath), resolve9(file)].every(Boolean) && resolve9(artifact.stagedPath ?? artifact.sourcePath) === resolve9(file) ? artifact.compose : [];
3389
- 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 : [];
3390
4188
  }
3391
4189
  function orderedForExpansion(artifacts) {
3392
4190
  return [...artifacts].sort((a, b) => Number(a.type === "fragments") - Number(b.type === "fragments"));
@@ -3403,7 +4201,7 @@ function applyReplacements(content, replacements) {
3403
4201
  return out + content.slice(cursor);
3404
4202
  }
3405
4203
  function relativeSelector(root, file) {
3406
- return relative3(root, file).replaceAll("\\", "/");
4204
+ return relative4(root, file).replaceAll("\\", "/");
3407
4205
  }
3408
4206
  function ownerSelector(root, file, nodeId) {
3409
4207
  const selector = relativeSelector(root, file);
@@ -3417,7 +4215,7 @@ function cleanSelector(value) {
3417
4215
  return value.trim().replace(/\s+/g, " ");
3418
4216
  }
3419
4217
  function sha256(content) {
3420
- return createHash3("sha256").update(content).digest("hex");
4218
+ return createHash4("sha256").update(content).digest("hex");
3421
4219
  }
3422
4220
  function uniqueComposedFrom(entries) {
3423
4221
  if (entries.length === 0) return [];
@@ -3432,8 +4230,8 @@ function artifactPathMap(artifacts) {
3432
4230
  }
3433
4231
 
3434
4232
  // src/staging/customize.ts
3435
- import { cp as cp4, mkdir as mkdir8, readdir as readdir4, readFile as readFile10, writeFile as writeFile9 } from "fs/promises";
3436
- 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";
3437
4235
  async function applyCustomizations(artifacts, options) {
3438
4236
  let next = [...artifacts];
3439
4237
  next = await applyReplacements2(next, options, "override", installableArtifactTypes());
@@ -3449,16 +4247,16 @@ async function applyFragmentCustomizations(artifacts, options) {
3449
4247
  return next.sort((a, b) => `${a.type}:${a.name}:${a.channel}`.localeCompare(`${b.type}:${b.name}:${b.channel}`));
3450
4248
  }
3451
4249
  async function applyInstructionOverlay(artifacts, options) {
3452
- 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");
3453
4251
  if (!await pathExists(overlayPath)) return artifacts;
3454
4252
  const index = artifacts.findIndex((artifact2) => artifact2.type === "instructions");
3455
4253
  if (index < 0) return artifacts;
3456
4254
  const artifact = artifacts[index];
3457
- const managed = await readFile10(artifact.stagedPath ?? artifact.sourcePath, "utf8");
3458
- const local = await readFile10(overlayPath, "utf8");
3459
- const composedPath = join14(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
3460
- await mkdir8(dirname11(composedPath), { recursive: true });
3461
- 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(
3462
4260
  composedPath,
3463
4261
  [
3464
4262
  "<!-- BEGIN agentwheel managed: upstream -->",
@@ -3484,19 +4282,19 @@ async function applyInstructionOverlay(artifacts, options) {
3484
4282
  return [...artifacts.slice(0, index), updated, ...artifacts.slice(index + 1)];
3485
4283
  }
3486
4284
  async function applyAdditions(artifacts, options) {
3487
- const additionsRoot = join14(options.workspaceRoot, ".agentwheel", "additions");
3488
- const rulesRoot = join14(additionsRoot, "rules");
4285
+ const additionsRoot = join17(options.workspaceRoot, ".agentwheel", "additions");
4286
+ const rulesRoot = join17(additionsRoot, "rules");
3489
4287
  if (!await pathExists(rulesRoot)) return artifacts;
3490
4288
  const additions = [];
3491
4289
  for (const entry of await sortedDirEntries2(rulesRoot)) {
3492
- const full = join14(rulesRoot, entry.name);
4290
+ const full = join17(rulesRoot, entry.name);
3493
4291
  if (!entry.isFile()) continue;
3494
4292
  additions.push({
3495
4293
  type: "rules",
3496
4294
  name: entry.name,
3497
4295
  sourcePath: full,
3498
4296
  stagedPath: full,
3499
- relativePath: join14("additions", "rules", entry.name),
4297
+ relativePath: join17("additions", "rules", entry.name),
3500
4298
  kind: "file",
3501
4299
  hash: await hashPath(full),
3502
4300
  packageName: options.packageName,
@@ -3520,17 +4318,17 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
3520
4318
  );
3521
4319
  }
3522
4320
  for (const type of artifactTypes) {
3523
- const typeRoot = join14(root, type);
4321
+ const typeRoot = join17(root, type);
3524
4322
  if (!await pathExists(typeRoot)) continue;
3525
4323
  for (const entry of await sortedDirEntries2(typeRoot)) {
3526
4324
  const artifactMapKey = `${type}:${entry.name}`;
3527
4325
  if (seen.has(artifactMapKey)) continue;
3528
4326
  seen.add(artifactMapKey);
3529
- const full = join14(typeRoot, entry.name);
4327
+ const full = join17(typeRoot, entry.name);
3530
4328
  const artifactKind = entry.isDirectory() ? "dir" : "file";
3531
4329
  const existing = byKey.get(artifactMapKey);
3532
- const stagedPath = join14(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
3533
- 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 });
3534
4332
  await cp4(full, stagedPath, { recursive: artifactKind === "dir", dereference: true });
3535
4333
  byKey.set(artifactMapKey, {
3536
4334
  ...existing,
@@ -3538,7 +4336,7 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
3538
4336
  name: entry.name,
3539
4337
  sourcePath: full,
3540
4338
  stagedPath,
3541
- relativePath: existing?.relativePath ?? join14(type, entry.name),
4339
+ relativePath: existing?.relativePath ?? join17(type, entry.name),
3542
4340
  kind: artifactKind,
3543
4341
  hash: await hashPath(stagedPath),
3544
4342
  packageName,
@@ -3553,13 +4351,13 @@ function replacementRoots(options, channel) {
3553
4351
  const stateDir = channel === "override" ? "overrides" : "ejected";
3554
4352
  const roots = [];
3555
4353
  if (options.graphNodeId) {
3556
- 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" });
3557
4355
  }
3558
4356
  if (options.packageName && options.packageVersion) {
3559
- 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" });
3560
4358
  }
3561
4359
  if (options.packageName) {
3562
- 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" });
3563
4361
  }
3564
4362
  return roots;
3565
4363
  }
@@ -3577,6 +4375,13 @@ async function sortedDirEntries2(path) {
3577
4375
  function artifactSelectorKey(artifact) {
3578
4376
  return `${artifact.type}/${artifact.name}`;
3579
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
+ }
3580
4385
  function normalizeArtifactSelectors(select, legacySkills) {
3581
4386
  const selected = [
3582
4387
  ...select ?? [],
@@ -3589,12 +4394,12 @@ function filterArtifactsBySelection(artifacts, selectors, legacySkills) {
3589
4394
  const selected = normalizeArtifactSelectors(selectors, legacySkills);
3590
4395
  if (!selected?.length) return artifacts;
3591
4396
  const selectedSet = new Set(selected);
3592
- const available = new Set(artifacts.map(artifactSelectorKey));
4397
+ const available = new Set(artifacts.flatMap(artifactSelectorAliases));
3593
4398
  const missing = selected.filter((selector) => !available.has(selector));
3594
4399
  if (missing.length > 0) {
3595
4400
  throw new Error(`Selected artifact not found in package: ${missing.join(", ")}`);
3596
4401
  }
3597
- return artifacts.filter((artifact) => artifact.required || selectedSet.has(artifactSelectorKey(artifact)));
4402
+ return artifacts.filter((artifact) => artifact.required || artifactSelectorAliases(artifact).some((selector) => selectedSet.has(selector)));
3598
4403
  }
3599
4404
  function splitSelectorList(value) {
3600
4405
  return value.split(",").map((item) => item.trim()).filter(Boolean);
@@ -3612,6 +4417,9 @@ function parseArtifactSelector(value) {
3612
4417
  }
3613
4418
  return `${parsedType.data}/${name}`;
3614
4419
  }
4420
+ function subagentBaseName(name) {
4421
+ return name.replace(/\.agent\.md$/i, "").replace(/\.toml$/i, "").replace(/\.md$/i, "");
4422
+ }
3615
4423
 
3616
4424
  // src/staging/staging.ts
3617
4425
  async function stageSource(driver, source, options = {}) {
@@ -3626,12 +4434,16 @@ async function stageResolvedSourceRaw(driver, resolved) {
3626
4434
  return stageResolvedArtifactsRaw(resolved, artifacts);
3627
4435
  }
3628
4436
  async function stageResolvedArtifactsRaw(resolved, artifacts) {
3629
- const root = await mkdtemp2(join15(tmpdir3(), "agentwheel-stage-"));
4437
+ const root = await mkdtemp2(join18(tmpdir3(), "agentwheel-stage-"));
3630
4438
  const stagedArtifacts = [];
3631
4439
  for (const artifact of artifacts) {
3632
- const stagedPath = join15(root, artifact.relativePath);
3633
- await mkdir9(dirname12(stagedPath), { recursive: true });
3634
- 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
+ });
3635
4447
  await composeAssets(artifact, resolved.resolvedPath, stagedPath);
3636
4448
  stagedArtifacts.push({
3637
4449
  ...artifact,
@@ -3658,12 +4470,14 @@ async function renderStagedBundle(bundle, options = {}) {
3658
4470
  const selectedArtifacts = filterArtifactsBySelection(expandedArtifacts, options.select, options.skills);
3659
4471
  const runtimeSelectedSet = new Set(normalizeArtifactSelectors(options.select, options.skills) ?? []);
3660
4472
  const runtimeArtifacts = options.adapter ? filterArtifactsByRuntime(selectedArtifacts, options.adapter.name, runtimeSelectedSet) : selectedArtifacts;
3661
- 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, {
3662
4476
  workspaceRoot: options.workspaceRoot,
3663
4477
  adapter: options.adapter,
3664
4478
  stageRoot: root,
3665
4479
  packageName: resolved.packageName
3666
- }) : runtimeArtifacts;
4480
+ }) : renderedArtifacts;
3667
4481
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
3668
4482
  return {
3669
4483
  root,
@@ -3687,6 +4501,7 @@ async function renderStagedBundle(bundle, options = {}) {
3687
4501
  relativePath: artifact.relativePath,
3688
4502
  kind: artifact.kind,
3689
4503
  hash: artifact.hash,
4504
+ format: artifact.format,
3690
4505
  composedFrom: artifact.composedFrom
3691
4506
  }))
3692
4507
  }
@@ -3708,16 +4523,16 @@ async function composeAssets(artifact, packageRoot, stagedPath) {
3708
4523
  }
3709
4524
  for (const asset of artifact.assets) {
3710
4525
  const source = resolvePackagePath(packageRoot, asset.from);
3711
- const dest = join15(stagedPath, asset.into);
4526
+ const dest = join18(stagedPath, asset.into);
3712
4527
  await copyAsset(asset, source, dest);
3713
4528
  }
3714
4529
  }
3715
4530
  async function copyAsset(asset, source, dest) {
3716
4531
  const sourceStats = await stat8(source);
3717
4532
  if (sourceStats.isFile()) {
3718
- if (matchesAny(basename10(source), asset.include)) {
3719
- await mkdir9(dest, { recursive: true });
3720
- 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);
3721
4536
  }
3722
4537
  return;
3723
4538
  }
@@ -3725,19 +4540,19 @@ async function copyAsset(asset, source, dest) {
3725
4540
  throw new Error(`Asset include source is not a file or directory: ${source}`);
3726
4541
  }
3727
4542
  if (!asset.include?.length) {
3728
- await mkdir9(dirname12(dest), { recursive: true });
4543
+ await mkdir11(dirname14(dest), { recursive: true });
3729
4544
  await cp5(source, dest, { recursive: true, dereference: true });
3730
4545
  if (asset.mode === "copy") await normalizeCopiedModes(dest);
3731
4546
  return;
3732
4547
  }
3733
4548
  for (const file of await listFiles(source)) {
3734
- const rel = relative4(source, file).replaceAll("\\", "/");
3735
- if (!matchesAny(rel, asset.include) && !matchesAny(basename10(file), asset.include)) continue;
3736
- 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);
3737
4552
  }
3738
4553
  }
3739
4554
  async function copyAssetFile(source, dest, asset) {
3740
- await mkdir9(dirname12(dest), { recursive: true });
4555
+ await mkdir11(dirname14(dest), { recursive: true });
3741
4556
  await cp5(source, dest, { dereference: true });
3742
4557
  if (asset.mode === "copy") await chmod(dest, 420);
3743
4558
  }
@@ -3753,7 +4568,7 @@ async function listFiles(root) {
3753
4568
  const out = [];
3754
4569
  async function walk2(dir) {
3755
4570
  for (const entry of (await readdir5(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
3756
- const full = join15(dir, entry.name);
4571
+ const full = join18(dir, entry.name);
3757
4572
  if (entry.isDirectory()) {
3758
4573
  await walk2(full);
3759
4574
  } else if (entry.isFile()) {
@@ -3772,7 +4587,7 @@ async function normalizeCopiedModes(path) {
3772
4587
  }
3773
4588
  if (!stats.isDirectory()) return;
3774
4589
  for (const entry of await readdir5(path, { withFileTypes: true })) {
3775
- await normalizeCopiedModes(join15(path, entry.name));
4590
+ await normalizeCopiedModes(join18(path, entry.name));
3776
4591
  }
3777
4592
  }
3778
4593
  function matchesAny(path, patterns) {
@@ -3785,9 +4600,9 @@ function matchesGlob(path, pattern) {
3785
4600
  }
3786
4601
 
3787
4602
  // src/model/workspace.ts
3788
- import { readFile as readFile11 } from "fs/promises";
3789
- import { homedir as homedir3 } from "os";
3790
- 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";
3791
4606
  import { z as z6 } from "zod";
3792
4607
  var workspacePackageSchema = z6.object({
3793
4608
  name: z6.string().min(1),
@@ -3797,6 +4612,7 @@ var workspacePackageSchema = z6.object({
3797
4612
  adapterConfig: z6.string().min(1).optional(),
3798
4613
  adapterModule: z6.string().min(1).optional(),
3799
4614
  adapterCodeHash: z6.string().min(16).optional(),
4615
+ installationType: installationTypeSchema.optional(),
3800
4616
  mode: z6.enum(["pinned", "tracking"]).default("pinned"),
3801
4617
  requestedRef: z6.string().min(1).optional(),
3802
4618
  select: z6.array(z6.string().min(1)).optional(),
@@ -3809,6 +4625,7 @@ var workspaceProfileRuntimeSchema = z6.object({
3809
4625
  adapter: z6.string().min(1).default("openclaw"),
3810
4626
  adapterConfig: z6.string().min(1).optional(),
3811
4627
  adapterModule: z6.string().min(1).optional(),
4628
+ installationType: installationTypeSchema.optional(),
3812
4629
  targetRoot: z6.string().min(1).optional(),
3813
4630
  executePlugins: z6.boolean().optional()
3814
4631
  });
@@ -3828,6 +4645,7 @@ var workspaceTrustSchema = z6.object({
3828
4645
  var workspaceAgentSchema = z6.object({
3829
4646
  adapter: z6.string().min(1),
3830
4647
  root: z6.string().min(1),
4648
+ installationType: installationTypeSchema.optional(),
3831
4649
  transport: z6.enum(["local", "ssh"]).default("local"),
3832
4650
  host: z6.string().min(1).optional(),
3833
4651
  user: z6.string().min(1).optional(),
@@ -3853,12 +4671,12 @@ var workspaceConfigSchema = z6.object({
3853
4671
  agents: z6.record(z6.string(), workspaceAgentSchema).default({})
3854
4672
  });
3855
4673
  function workspaceConfigPath(workspaceRoot) {
3856
- return join16(workspaceRoot, ".agentwheel", "config.json");
4674
+ return join19(workspaceRoot, ".agentwheel", "config.json");
3857
4675
  }
3858
4676
  async function readWorkspaceConfig(workspaceRoot) {
3859
4677
  const path = workspaceConfigPath(workspaceRoot);
3860
4678
  if (!await pathExists(path)) return emptyWorkspaceConfig();
3861
- return workspaceConfigSchema.parse(JSON.parse(await readFile11(path, "utf8")));
4679
+ return workspaceConfigSchema.parse(JSON.parse(await readFile14(path, "utf8")));
3862
4680
  }
3863
4681
  async function writeWorkspaceConfig(workspaceRoot, config) {
3864
4682
  await writeJsonAtomic(workspaceConfigPath(workspaceRoot), workspaceConfigSchema.parse(config));
@@ -3870,14 +4688,14 @@ function upsertPackage(config, entry) {
3870
4688
  packages.sort((a, b) => a.name.localeCompare(b.name));
3871
4689
  return { schemaVersion: 1, packages, bootstrapSkills: parsed.bootstrapSkills, registry: parsed.registry ?? {}, trust: parsed.trust ?? {}, profiles: parsed.profiles ?? {}, agents: parsed.agents ?? {} };
3872
4690
  }
3873
- function globalWorkspaceConfigPath(globalRoot = homedir3()) {
3874
- return join16(globalRoot, ".agentwheel", "config.json");
4691
+ function globalWorkspaceConfigPath(globalRoot = homedir4()) {
4692
+ return join19(globalRoot, ".agentwheel", "config.json");
3875
4693
  }
3876
4694
  async function findWorkspaceRoot(start = process.cwd()) {
3877
4695
  let current = resolve11(start);
3878
4696
  while (true) {
3879
4697
  if (await pathExists(workspaceConfigPath(current))) return current;
3880
- const parent = dirname13(current);
4698
+ const parent = dirname15(current);
3881
4699
  if (parent === current) return resolve11(start);
3882
4700
  current = parent;
3883
4701
  }
@@ -3904,8 +4722,8 @@ function mergeWorkspaceConfig(global, project) {
3904
4722
  });
3905
4723
  }
3906
4724
  function resolveConfigPath(path, baseRoot) {
3907
- if (path.startsWith("~/")) return resolve11(homedir3(), path.slice(2));
3908
- if (path === "~") return homedir3();
4725
+ if (path.startsWith("~/")) return resolve11(homedir4(), path.slice(2));
4726
+ if (path === "~") return homedir4();
3909
4727
  return path.startsWith("/") ? resolve11(path) : resolve11(baseRoot, path);
3910
4728
  }
3911
4729
  function emptyWorkspaceConfig() {
@@ -3913,7 +4731,7 @@ function emptyWorkspaceConfig() {
3913
4731
  }
3914
4732
  async function readConfigPath(path) {
3915
4733
  if (!await pathExists(path)) return emptyWorkspaceConfig();
3916
- return workspaceConfigSchema.parse(JSON.parse(await readFile11(path, "utf8")));
4734
+ return workspaceConfigSchema.parse(JSON.parse(await readFile14(path, "utf8")));
3917
4735
  }
3918
4736
  function mergeWorkspaceTrust(global, project) {
3919
4737
  return {
@@ -3928,23 +4746,23 @@ function sortedUnique2(values) {
3928
4746
  }
3929
4747
 
3930
4748
  // src/lifecycle/customization.ts
3931
- import { appendFile, cp as cp6, mkdir as mkdir10, rm as rm7 } from "fs/promises";
3932
- 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";
3933
4751
 
3934
4752
  // src/resolve/graph.ts
3935
- import { createHash as createHash4 } from "crypto";
3936
- 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";
3937
4755
  import { tmpdir as tmpdir4 } from "os";
3938
- 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";
3939
4757
 
3940
4758
  // src/resolve/identity.ts
3941
- import { homedir as homedir5 } from "os";
4759
+ import { homedir as homedir6 } from "os";
3942
4760
  import { resolve as resolve13 } from "path";
3943
4761
 
3944
4762
  // src/registry/client.ts
3945
- import { readFile as readFile12, rm as rm6, stat as stat9 } from "fs/promises";
3946
- import { homedir as homedir4 } from "os";
3947
- 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";
3948
4766
  import { fileURLToPath } from "url";
3949
4767
 
3950
4768
  // src/model/registry.ts
@@ -4042,7 +4860,7 @@ var RegistryClient = class {
4042
4860
  }
4043
4861
  async readCache() {
4044
4862
  if (!await pathExists(this.cachePath)) return void 0;
4045
- return registryCacheSchema.parse(JSON.parse(await readFile12(this.cachePath, "utf8")));
4863
+ return registryCacheSchema.parse(JSON.parse(await readFile15(this.cachePath, "utf8")));
4046
4864
  }
4047
4865
  isExpired(cache, ttlMs) {
4048
4866
  return this.now().getTime() - new Date(cache.fetchedAt).getTime() > ttlMs;
@@ -4061,10 +4879,10 @@ var RegistryClient = class {
4061
4879
  if (await pathExists(filePath)) {
4062
4880
  const fullPath = resolve12(filePath);
4063
4881
  const stats = await stat9(fullPath);
4064
- return readFile12(stats.isDirectory() ? join17(fullPath, "index.json") : fullPath, "utf8");
4882
+ return readFile15(stats.isDirectory() ? join20(fullPath, "index.json") : fullPath, "utf8");
4065
4883
  }
4066
- const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join17(dirname14(this.cachePath), "registry-repos") }));
4067
- 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");
4068
4886
  }
4069
4887
  warnCompatibility(entries) {
4070
4888
  for (const entry of entries) {
@@ -4078,10 +4896,13 @@ var RegistryClient = class {
4078
4896
  }
4079
4897
  };
4080
4898
  async function resolvePackageSource(source, workspaceRoot, options = {}) {
4081
- const { isExplicitSource } = await import("./identify-7SEBWCNQ.js");
4899
+ const { isExplicitSource } = await import("./identify-TXIDGMNL.js");
4082
4900
  if (await isExplicitSource(source)) return { source };
4083
4901
  const entry = await new RegistryClient({ workspaceRoot, offline: options.offline, warn: options.warn }).resolve(source);
4084
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
+ }
4085
4906
  throw new Error(`Registry entry not found: ${source}. Use an explicit path/git/skillkit/vercel source to bypass the registry.`);
4086
4907
  }
4087
4908
  return { source: entry.source, registryEntry: entry };
@@ -4096,7 +4917,7 @@ function mergeIndexes(indexes) {
4096
4917
  return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
4097
4918
  }
4098
4919
  function defaultRegistryCachePath() {
4099
- return join17(homedir4(), ".agentwheel", "registry-cache.json");
4920
+ return join20(homedir5(), ".agentwheel", "registry-cache.json");
4100
4921
  }
4101
4922
  function sameSources(a, b) {
4102
4923
  return a.length === b.length && a.every((source, index) => source === b[index]);
@@ -4165,8 +4986,8 @@ function localSourcePath(source) {
4165
4986
  return source.startsWith("local:") ? source.slice("local:".length) : source;
4166
4987
  }
4167
4988
  function resolveLocalPath(path, declaringPackageRoot) {
4168
- if (path === "~") return homedir5();
4169
- if (path.startsWith("~/")) return resolve13(homedir5(), path.slice(2));
4989
+ if (path === "~") return homedir6();
4990
+ if (path.startsWith("~/")) return resolve13(homedir6(), path.slice(2));
4170
4991
  if (path.startsWith("/")) return resolve13(path);
4171
4992
  return resolve13(declaringPackageRoot, path);
4172
4993
  }
@@ -4325,7 +5146,7 @@ function compareSemver(a, b) {
4325
5146
  var cacheLocks = /* @__PURE__ */ new Map();
4326
5147
  async function resolveDependencyGraph(roots, options) {
4327
5148
  if (roots.length === 0) throw new Error("At least one graph root is required.");
4328
- const graphRoot = await mkdtemp3(join18(tmpdir4(), "agentwheel-graph-"));
5149
+ const graphRoot = await mkdtemp3(join21(tmpdir4(), "agentwheel-graph-"));
4329
5150
  const fetchCache = /* @__PURE__ */ new Map();
4330
5151
  const nodesByKey = /* @__PURE__ */ new Map();
4331
5152
  const rootResults = [];
@@ -4687,7 +5508,7 @@ async function collectIncludeNeeds(artifact, artifactsByRelativePath) {
4687
5508
  const file = stack.shift();
4688
5509
  if (scanned.has(file)) continue;
4689
5510
  scanned.add(file);
4690
- const content = await readFile13(file, "utf8");
5511
+ const content = await readFile16(file, "utf8");
4691
5512
  for (const include of extractOpenPackIncludeSelectors(content)) {
4692
5513
  await collectIncludeSelector(include.raw, include.optional, artifactsByRelativePath, scanned, stack, needs);
4693
5514
  }
@@ -4730,7 +5551,7 @@ async function listMarkdownFiles2(root) {
4730
5551
  const out = [];
4731
5552
  async function walk2(dir) {
4732
5553
  for (const entry of (await readdir6(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
4733
- const full = join18(dir, entry.name);
5554
+ const full = join21(dir, entry.name);
4734
5555
  if (entry.isDirectory()) {
4735
5556
  await walk2(full);
4736
5557
  } else if (entry.isFile() && extname4(entry.name).toLowerCase() === ".md") {
@@ -4765,7 +5586,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
4765
5586
  const promise = (async () => {
4766
5587
  const driver = getSourceDriver(normalized.driver);
4767
5588
  const resolved = await driver.resolve(normalized.source, {
4768
- cacheRoot: options.cacheRoot ?? join18(options.workspaceRoot, ".agentwheel", "cache"),
5589
+ cacheRoot: options.cacheRoot ?? join21(options.workspaceRoot, ".agentwheel", "cache"),
4769
5590
  mode,
4770
5591
  ref: refOverride ?? normalized.requestedRef,
4771
5592
  frozenLock: hardLockedCheckout
@@ -4775,7 +5596,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
4775
5596
  const exported = await driver.export(translated);
4776
5597
  const manifest = await readPackageManifest(exported.resolvedPath);
4777
5598
  const artifacts = await driver.list(exported);
4778
- const name = manifest?.name ?? exported.packageName ?? basename11(exported.resolvedPath);
5599
+ const name = manifest?.name ?? exported.packageName ?? basename14(exported.resolvedPath);
4779
5600
  const version = manifest?.version ?? exported.packageVersion ?? "0.0.0";
4780
5601
  const sourceHash = exported.sourceHash ?? await hashPath(exported.resolvedPath);
4781
5602
  return {
@@ -4951,7 +5772,7 @@ function detectDirectCollisions(nodes) {
4951
5772
  }
4952
5773
  }
4953
5774
  function graphNodeId(name, version, normalizedSource, resolvedCommit, sourceHash) {
4954
- 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);
4955
5776
  return `${name}@${version}+${digest}`;
4956
5777
  }
4957
5778
  function sortedUnique3(values) {
@@ -4972,8 +5793,8 @@ async function mapLimit(items, limit, fn) {
4972
5793
 
4973
5794
  // src/lifecycle/customization.ts
4974
5795
  async function remember(workspaceRoot, runtime, text) {
4975
- const overlayPath = join19(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
4976
- await mkdir10(dirname15(overlayPath), { recursive: true });
5796
+ const overlayPath = join22(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
5797
+ await mkdir12(dirname17(overlayPath), { recursive: true });
4977
5798
  await appendFile(overlayPath, `${text.trim()}
4978
5799
  `, "utf8");
4979
5800
  return { overlayPath };
@@ -4996,8 +5817,8 @@ async function ejectArtifact(workspaceRoot, item) {
4996
5817
  throw new Error(`Artifact not found: ${item}`);
4997
5818
  }
4998
5819
  const ejectedIdentity = parsed.packageIdentity === parsed.packageName ? parsed.packageIdentity : candidate.nodeId === parsed.packageIdentity ? candidate.nodeId : `${candidate.packageName}@${candidate.packageVersion}`;
4999
- const ejectedPath = join19(workspaceRoot, ".agentwheel", "ejected", ...ejectedIdentity.split("/"), parsed.type, parsed.name);
5000
- 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 });
5001
5822
  await rm7(ejectedPath, { recursive: true, force: true });
5002
5823
  await cp6(artifact.stagedPath ?? artifact.sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
5003
5824
  return {
@@ -5039,7 +5860,7 @@ async function stageEjectCandidate(workspaceRoot, pkg) {
5039
5860
  const adapter = pkg.adapterConfig ? await loadAdapterConfig(pkg.adapterConfig) : getAdapter(pkg.adapter);
5040
5861
  const bundle = await stageSource(driver, normalized.source, {
5041
5862
  adapter,
5042
- cacheRoot: join19(workspaceRoot, ".agentwheel", "cache"),
5863
+ cacheRoot: join22(workspaceRoot, ".agentwheel", "cache"),
5043
5864
  mode: pkg.mode,
5044
5865
  ref: normalized.requestedRef ?? pkg.requestedRef
5045
5866
  });
@@ -5089,9 +5910,9 @@ function ejectCommands(candidates, parsed) {
5089
5910
  import { rm as rm8 } from "fs/promises";
5090
5911
 
5091
5912
  // src/lifecycle/source-plan.ts
5092
- import { createHash as createHash6 } from "crypto";
5093
- import { mkdir as mkdir12 } from "fs/promises";
5094
- 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";
5095
5916
 
5096
5917
  // src/resolve/graph-diff.ts
5097
5918
  function diffGraphLocks(previous, next) {
@@ -5212,12 +6033,12 @@ function short(hash) {
5212
6033
  }
5213
6034
 
5214
6035
  // src/resolve/render.ts
5215
- import { createHash as createHash5 } from "crypto";
5216
- 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";
5217
6038
  import { tmpdir as tmpdir5 } from "os";
5218
- import { join as join20 } from "path";
6039
+ import { join as join23 } from "path";
5219
6040
  async function renderGraphForTarget(graph, targetContext = {}) {
5220
- const root = await mkdtemp4(join20(tmpdir5(), "agentwheel-render-"));
6041
+ const root = await mkdtemp4(join23(tmpdir5(), "agentwheel-render-"));
5221
6042
  const artifacts = [];
5222
6043
  const stagedNodes = /* @__PURE__ */ new Map();
5223
6044
  const includeEdges = /* @__PURE__ */ new Map();
@@ -5289,7 +6110,9 @@ async function renderGraphForTarget(graph, targetContext = {}) {
5289
6110
  const selectedArtifacts = filterArtifactsBySelection(expandedArtifacts, rawNode.node.selected);
5290
6111
  const runtimeSelectedSet = new Set(normalizeArtifactSelectors(rawNode.node.selected) ?? []);
5291
6112
  const runtimeArtifacts = targetContext.adapter ? filterArtifactsByRuntime2(selectedArtifacts, targetContext.adapter.name, runtimeSelectedSet) : selectedArtifacts;
5292
- 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, {
5293
6116
  workspaceRoot: targetContext.workspaceRoot,
5294
6117
  adapter: targetContext.adapter,
5295
6118
  stageRoot: staged.root,
@@ -5297,7 +6120,7 @@ async function renderGraphForTarget(graph, targetContext = {}) {
5297
6120
  packageVersion: rawNode.resolved.packageVersion,
5298
6121
  graphNodeId: rawNode.node.id,
5299
6122
  packageNameAmbiguous: ambiguousPackageNames.has(rawNode.node.name)
5300
- }) : runtimeArtifacts;
6123
+ }) : runtimeRenderedArtifacts;
5301
6124
  const installableArtifacts = renderedArtifacts.filter((artifact) => rawNode.depth === 0 || artifact.type !== "fragments");
5302
6125
  artifacts.push(...installableArtifacts.map((artifact) => ({
5303
6126
  ...artifact,
@@ -5331,7 +6154,7 @@ async function artifactContentMap(artifacts) {
5331
6154
  const out = /* @__PURE__ */ new Map();
5332
6155
  for (const artifact of artifacts) {
5333
6156
  if (artifact.kind !== "file") continue;
5334
- 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"));
5335
6158
  }
5336
6159
  return out;
5337
6160
  }
@@ -5345,7 +6168,7 @@ function filterArtifactsByRuntime2(artifacts, adapterName, selectedSet) {
5345
6168
  });
5346
6169
  }
5347
6170
  function sha2562(content) {
5348
- return createHash5("sha256").update(content).digest("hex");
6171
+ return createHash6("sha256").update(content).digest("hex");
5349
6172
  }
5350
6173
  function assignInstallNames(graph, artifacts) {
5351
6174
  const aliases = workspaceAliases(graph);
@@ -5594,9 +6417,9 @@ function lockArtifactFor(artifact) {
5594
6417
  }
5595
6418
 
5596
6419
  // src/lifecycle/trust.ts
5597
- import { mkdir as mkdir11, readFile as readFile15 } from "fs/promises";
5598
- import { homedir as homedir6 } from "os";
5599
- 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";
5600
6423
  import { z as z8 } from "zod";
5601
6424
  var trustStoreSchema = z8.object({
5602
6425
  version: z8.literal(1),
@@ -5670,14 +6493,14 @@ function sortedUnique4(values) {
5670
6493
  }
5671
6494
  async function readTrustStore(path) {
5672
6495
  if (!await pathExists(path)) return { version: 1, acceptedSources: [] };
5673
- return trustStoreSchema.parse(JSON.parse(await readFile15(path, "utf8")));
6496
+ return trustStoreSchema.parse(JSON.parse(await readFile18(path, "utf8")));
5674
6497
  }
5675
6498
  async function writeTrustStore(path, store) {
5676
- await mkdir11(dirname16(path), { recursive: true });
6499
+ await mkdir13(dirname18(path), { recursive: true });
5677
6500
  await writeJsonAtomic(path, trustStoreSchema.parse(store));
5678
6501
  }
5679
6502
  function defaultTrustStorePath() {
5680
- return process.env.AGENTWHEEL_TRUST_STORE ?? join21(homedir6(), ".agentwheel", "trust.json");
6503
+ return process.env.AGENTWHEEL_TRUST_STORE ?? join24(homedir7(), ".agentwheel", "trust.json");
5681
6504
  }
5682
6505
 
5683
6506
  // src/lifecycle/source-plan.ts
@@ -5692,7 +6515,17 @@ async function createGraphSourcePlan(options) {
5692
6515
  warnings.push(message);
5693
6516
  options.warn?.(message);
5694
6517
  };
5695
- 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 });
5696
6529
  const workspaceConfig = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: options.globalRoot });
5697
6530
  const trustPolicy = {
5698
6531
  ...normalizeTrustPolicy(workspaceConfig.trust),
@@ -5700,18 +6533,12 @@ async function createGraphSourcePlan(options) {
5700
6533
  };
5701
6534
  const lockMode = options.frozenLock === true || options.offline === true;
5702
6535
  const lockLabel = options.offline === true ? "Offline" : options.frozenLock === true ? "Frozen lock" : options.lockedResolution === true ? "Locked install" : "Fresh resolve";
5703
- const targetFingerprint = computeTargetFingerprint(options.targetFingerprintParts ?? {
5704
- adapter: options.adapter.name,
5705
- targetRoot: options.targetRoot,
5706
- transport: transport.kind,
5707
- transportDescription: transport.description
5708
- });
5709
6536
  const graphLockPath = pathForGraphLock(workspaceRoot, options.targetKey ?? "default", options.adapter.name, targetFingerprint);
5710
6537
  const previousLock = await readExistingGraphLock(graphLockPath);
5711
6538
  const registryClient = new RegistryClient({ workspaceRoot, offline: lockMode, offlineLabel: lockLabel, warn });
5712
6539
  const graph = await resolveDependencyGraph(options.roots, {
5713
6540
  workspaceRoot,
5714
- cacheRoot: join22(workspaceRoot, ".agentwheel", "cache"),
6541
+ cacheRoot: join25(workspaceRoot, ".agentwheel", "cache"),
5715
6542
  registryClient,
5716
6543
  noDeps: options.noDeps,
5717
6544
  lockedResolution: options.lockedResolution,
@@ -5733,13 +6560,20 @@ async function createGraphSourcePlan(options) {
5733
6560
  targetFingerprint
5734
6561
  });
5735
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");
5736
6565
  const graphLockDigest = digestGraphLock(bundle.graphLock);
5737
6566
  const graphDiff = diffGraphLocks(previousLock, bundle.graphLock);
5738
- const manifest = await readInstallManifest(options.targetRoot, options.adapter.name, transport);
6567
+ const manifest = await readInstallManifest(resolvedInstallRoot, options.adapter.name, transport, { installationType: resolvedInstallationType, stateKey });
5739
6568
  const plan = await createCombinedInstallPlan(desiredArtifacts, options.adapter, options.targetRoot, manifest, transport, {
5740
6569
  baseRevision: manifest?.revision ?? null,
5741
6570
  graphLockDigest,
5742
- workspaceOwner: workspaceOwnerId(workspaceRoot)
6571
+ workspaceOwner: workspaceOwnerId(workspaceRoot),
6572
+ installationType: resolvedInstallationType,
6573
+ stateKey,
6574
+ forceDrift: options.forceDrift,
6575
+ forceConflict: options.forceConflict,
6576
+ replaceConflict: options.replaceConflict
5743
6577
  });
5744
6578
  return {
5745
6579
  plan,
@@ -5774,28 +6608,38 @@ function desiredArtifactFromResolved(artifact) {
5774
6608
  }
5775
6609
  };
5776
6610
  }
5777
- async function recoverPendingApplyIfSafe(targetRoot, adapter, transport) {
5778
- 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;
5779
6613
  try {
5780
- await recoverPendingApply(targetRoot, adapter, transport);
6614
+ await recoverPendingApply(targetRoot, adapter, transport, scope);
5781
6615
  return true;
5782
6616
  } catch (error) {
5783
6617
  const message = error instanceof Error ? error.message : String(error);
5784
6618
  throw new Error(`Pending apply journal for ${adapter} at ${targetRoot} could not be recovered automatically: ${message}`);
5785
6619
  }
5786
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
+ }
5787
6631
  async function readExistingGraphLock(path) {
5788
6632
  if (!await pathExists(path)) return void 0;
5789
6633
  return readGraphLock(path);
5790
6634
  }
5791
6635
  function pathForGraphLock(workspaceRoot, targetKey, adapter, targetFingerprint) {
5792
- 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`);
5793
6637
  }
5794
6638
  function sanitizePathSegment(value) {
5795
6639
  return value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "default";
5796
6640
  }
5797
6641
  function digestGraphLock(lock) {
5798
- return createHash6("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
6642
+ return createHash7("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
5799
6643
  }
5800
6644
  function workspaceOwnerId(workspaceRoot) {
5801
6645
  return `workspace-root:${resolve14(workspaceRoot)}`;
@@ -5852,129 +6696,8 @@ Type yes to continue: `);
5852
6696
  ${sources.map((source) => `- ${source}`).join("\n")}`);
5853
6697
  }
5854
6698
 
5855
- // src/lifecycle/profile.ts
5856
- async function syncProfile(options) {
5857
- const config = await readMergedWorkspaceConfig(options.workspaceRoot);
5858
- const profile = config.profiles[options.profile];
5859
- if (!profile) {
5860
- throw new Error(`Unknown profile: ${options.profile}`);
5861
- }
5862
- const packages = options.source ? [await packageFromSource(options.source, options)] : config.packages;
5863
- if (packages.length === 0) {
5864
- throw new Error("Profile sync needs a source argument or configured packages.");
5865
- }
5866
- const results = [];
5867
- for (const runtime of profile.runtimes) {
5868
- const target = resolveProfileRuntime(runtime, config, options.workspaceRoot);
5869
- const adapter = await resolveAdapter({
5870
- adapter: target.adapter,
5871
- adapterConfig: runtime.adapterConfig,
5872
- adapterModule: runtime.adapterModule,
5873
- allowAdapterCode: options.allowAdapterCode,
5874
- baseDir: options.workspaceRoot,
5875
- warn: options.warn
5876
- });
5877
- const selected = normalizeArtifactSelectors(options.select, options.skills);
5878
- const graphPlan = await createGraphSourcePlan({
5879
- roots: packages.map((pkg) => ({
5880
- rootId: pkg.name,
5881
- source: pkg.source,
5882
- mode: options.mode ?? pkg.mode,
5883
- ref: pkg.requestedRef,
5884
- select: selected ?? normalizeArtifactSelectors(pkg.select, pkg.skills),
5885
- aliases: pkg.aliases,
5886
- overrides: pkg.overrides
5887
- })),
5888
- targetRoot: target.targetRoot,
5889
- workspaceRoot: options.workspaceRoot,
5890
- adapter,
5891
- transport: target.transport,
5892
- targetKey: runtime.agent ?? adapter.name,
5893
- targetFingerprintParts: {
5894
- adapter: adapter.name,
5895
- adapterConfig: runtime.adapterConfig,
5896
- adapterModule: runtime.adapterModule,
5897
- adapterCodeHash: adapter.programmatic?.hash,
5898
- targetRoot: target.targetRoot,
5899
- transport: target.transport.kind
5900
- },
5901
- noDeps: options.noDeps,
5902
- lockedResolution: options.lockedResolution,
5903
- frozenLock: options.frozenLock,
5904
- offline: options.offline,
5905
- yes: options.yes,
5906
- trustPatterns: options.trustPatterns ?? [],
5907
- readOnly: options.readOnly,
5908
- isTTY: options.isTTY,
5909
- warn: options.warn
5910
- });
5911
- try {
5912
- results.push({
5913
- runtime: adapter.name,
5914
- targetRoot: target.targetRoot,
5915
- transport: target.transport.kind,
5916
- packageName: packages.map((pkg) => pkg.name).join(","),
5917
- plan: graphPlan.plan
5918
- });
5919
- if (!options.dryRun) {
5920
- await applyCombinedInstallPlan(graphPlan.plan, {
5921
- executePlugins: runtime.executePlugins ?? options.executePlugins,
5922
- transport: target.transport,
5923
- graphLockDigest: graphPlan.graphLockDigest,
5924
- graphLock: { path: graphPlan.graphLockPath, lock: graphPlan.bundle.graphLock }
5925
- });
5926
- }
5927
- } finally {
5928
- await rm8(graphPlan.bundle.root, { recursive: true, force: true });
5929
- }
5930
- }
5931
- return results;
5932
- }
5933
- function resolveProfileRuntime(runtime, config, workspaceRoot) {
5934
- if (runtime.agent) {
5935
- const agent = config.agents[runtime.agent];
5936
- if (!agent) throw new Error(`Unknown agent in profile: ${runtime.agent}`);
5937
- const target = {
5938
- agentName: runtime.agent,
5939
- adapter: agent.adapter,
5940
- targetRoot: agent.transport === "ssh" ? agent.root : resolveConfigPath(agent.root, workspaceRoot),
5941
- workspaceRoot,
5942
- transport: agent.transport,
5943
- ssh: agent.transport === "ssh" ? {
5944
- host: agent.host ?? "",
5945
- user: agent.user,
5946
- port: agent.port,
5947
- identityFile: agent.identityFile ? resolveConfigPath(agent.identityFile, workspaceRoot) : void 0
5948
- } : void 0,
5949
- source: "agent"
5950
- };
5951
- return { adapter: target.adapter, targetRoot: target.targetRoot, transport: transportForTarget(target) };
5952
- }
5953
- return {
5954
- adapter: runtime.adapter,
5955
- targetRoot: runtime.targetRoot ? resolveConfigPath(runtime.targetRoot, workspaceRoot) : workspaceRoot,
5956
- transport: localTransport
5957
- };
5958
- }
5959
- async function packageFromSource(source, options) {
5960
- const resolved = await resolvePackageSource(source, options.workspaceRoot, {
5961
- offline: options.frozenLock === true || options.offline === true,
5962
- warn: options.warn
5963
- });
5964
- const driver = options.driver ?? inferSourceDriverName(resolved.source);
5965
- return {
5966
- name: resolved.registryEntry?.name ?? source,
5967
- source: resolved.source,
5968
- driver,
5969
- adapter: "openclaw",
5970
- mode: options.mode ?? "pinned",
5971
- select: options.select,
5972
- skills: options.skills
5973
- };
5974
- }
5975
-
5976
6699
  // src/runtime/target.ts
5977
- 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";
5978
6701
  var runtimeMarkers = [
5979
6702
  { adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
5980
6703
  { adapter: "claude", dirs: [".claude"] },
@@ -5988,6 +6711,7 @@ async function resolveRuntimeTarget(request = {}) {
5988
6711
  const targetRoot = resolve15(request.targetRoot);
5989
6712
  return {
5990
6713
  adapter: request.adapter ?? "openclaw",
6714
+ installationType: request.installationType,
5991
6715
  targetRoot,
5992
6716
  workspaceRoot: targetRoot,
5993
6717
  transport: "local",
@@ -5997,14 +6721,15 @@ async function resolveRuntimeTarget(request = {}) {
5997
6721
  const workspaceRoot = await findWorkspaceRoot(cwd);
5998
6722
  const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
5999
6723
  if (request.agent) {
6000
- return targetFromAgent(request.agent, config, workspaceRoot);
6724
+ return targetFromAgent(request.agent, config, workspaceRoot, request.installationType);
6001
6725
  }
6002
6726
  const detected = await detectRuntimeTarget(cwd, request.adapter);
6003
6727
  if (detected) {
6004
- 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" };
6005
6729
  }
6006
6730
  return {
6007
6731
  adapter: request.adapter ?? "openclaw",
6732
+ installationType: request.installationType,
6008
6733
  targetRoot: cwd,
6009
6734
  workspaceRoot,
6010
6735
  transport: "local",
@@ -6017,12 +6742,44 @@ async function resolveAllRuntimeTargets(request = {}) {
6017
6742
  const cwd = resolve15(request.cwd ?? process.cwd());
6018
6743
  const workspaceRoot = await findWorkspaceRoot(cwd);
6019
6744
  const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
6020
- 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));
6021
6746
  if (targets.length === 0) {
6022
6747
  throw new Error("No agents configured. Add agents to .agentwheel/config.json or pass --target-root.");
6023
6748
  }
6024
6749
  return targets;
6025
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
+ }
6026
6783
  async function resolveAllDetectedRuntimeTargets(request = {}) {
6027
6784
  if (request.agent) return [await resolveRuntimeTarget(request)];
6028
6785
  const scanRoot = runtimeScanRoot(request);
@@ -6032,6 +6789,7 @@ async function resolveAllDetectedRuntimeTargets(request = {}) {
6032
6789
  }
6033
6790
  return Promise.all(matches.map(async (match) => ({
6034
6791
  ...match,
6792
+ installationType: request.installationType,
6035
6793
  workspaceRoot: await findWorkspaceRoot(match.targetRoot),
6036
6794
  transport: "local",
6037
6795
  source: "auto-detect"
@@ -6050,23 +6808,25 @@ async function detectRuntimeTargets(cwd = process.cwd(), adapterFilter) {
6050
6808
  for (const marker of runtimeMarkers) {
6051
6809
  if (adapterFilter && marker.adapter !== adapterFilter) continue;
6052
6810
  for (const dir of marker.dirs) {
6053
- if (basename12(root) === dir) {
6054
- matches.push({ adapter: marker.adapter, targetRoot: dirname18(root) });
6055
- } 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))) {
6056
6814
  matches.push({ adapter: marker.adapter, targetRoot: root });
6057
6815
  }
6058
6816
  }
6059
6817
  }
6060
6818
  return dedupeTargets(matches);
6061
6819
  }
6062
- function targetFromAgent(name, config, workspaceRoot) {
6820
+ function targetFromAgent(name, config, workspaceRoot, installationType) {
6063
6821
  const agent = config.agents[name];
6064
6822
  if (!agent) {
6065
6823
  throw new Error(`Unknown agent: ${name}`);
6066
6824
  }
6067
6825
  return {
6068
6826
  agentName: name,
6827
+ targetKey: name,
6069
6828
  adapter: agent.adapter,
6829
+ installationType: installationType ?? agent.installationType,
6070
6830
  targetRoot: agent.transport === "ssh" ? agent.root : resolveConfigPath(agent.root, workspaceRoot),
6071
6831
  workspaceRoot,
6072
6832
  transport: agent.transport,
@@ -6089,13 +6849,119 @@ function dedupeTargets(matches) {
6089
6849
  function runtimeScanRoot(request) {
6090
6850
  const root = resolve15(request.targetRoot ?? request.cwd ?? process.cwd());
6091
6851
  if (request.targetRoot) return root;
6092
- 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
+ };
6093
6959
  }
6094
6960
 
6095
6961
  // src/cli/update-check.ts
6096
- import { mkdir as mkdir13, readFile as readFile16, writeFile as writeFile10 } from "fs/promises";
6097
- import { homedir as homedir7 } from "os";
6098
- 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";
6099
6965
  var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
6100
6966
  var DEFAULT_TIMEOUT_MS = 300;
6101
6967
  var REGISTRY_URL = "https://registry.npmjs.org/agentwheel";
@@ -6103,7 +6969,7 @@ async function maybeCheckForUpdate(options) {
6103
6969
  if (isDisabled(options)) return;
6104
6970
  const now = options.now?.() ?? /* @__PURE__ */ new Date();
6105
6971
  const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
6106
- const cachePath = options.cachePath ?? join24(homedir7(), ".agentwheel", "update-check.json");
6972
+ const cachePath = options.cachePath ?? join27(homedir8(), ".agentwheel", "update-check.json");
6107
6973
  try {
6108
6974
  const cached = await readCache(cachePath);
6109
6975
  if (cached && now.getTime() - Date.parse(cached.checkedAt) < ttlMs) {
@@ -6140,7 +7006,7 @@ async function fetchLatestVersion(fetchImpl, timeoutMs) {
6140
7006
  }
6141
7007
  async function readCache(path) {
6142
7008
  try {
6143
- const parsed = JSON.parse(await readFile16(path, "utf8"));
7009
+ const parsed = JSON.parse(await readFile19(path, "utf8"));
6144
7010
  if (typeof parsed.checkedAt !== "string" || typeof parsed.latest !== "string") return void 0;
6145
7011
  return { checkedAt: parsed.checkedAt, latest: parsed.latest };
6146
7012
  } catch {
@@ -6148,8 +7014,8 @@ async function readCache(path) {
6148
7014
  }
6149
7015
  }
6150
7016
  async function writeCache(path, cache) {
6151
- await mkdir13(dirname19(path), { recursive: true });
6152
- await writeFile10(path, `${JSON.stringify(cache, null, 2)}
7017
+ await mkdir15(dirname21(path), { recursive: true });
7018
+ await writeFile12(path, `${JSON.stringify(cache, null, 2)}
6153
7019
  `, "utf8");
6154
7020
  }
6155
7021
  function warnIfNewer(latest, current, stderr = process.stderr) {
@@ -6295,13 +7161,13 @@ function isCrossPackageSelector(value) {
6295
7161
  }
6296
7162
 
6297
7163
  // src/model/package-migrate.ts
6298
- import { readFile as readFile17, rename as rename4, writeFile as writeFile11 } from "fs/promises";
6299
- 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";
6300
7166
  import { applyEdits, modify, parse as parse3 } from "jsonc-parser";
6301
7167
  async function migratePackageManifest(root) {
6302
7168
  const packageRoot = resolve17(root);
6303
7169
  for (const name of openPackManifestNames) {
6304
- const path = join26(packageRoot, name);
7170
+ const path = join29(packageRoot, name);
6305
7171
  if (await pathExists(path)) {
6306
7172
  return { changed: false, to: path, message: `Package already uses ${name}.` };
6307
7173
  }
@@ -6310,18 +7176,18 @@ async function migratePackageManifest(root) {
6310
7176
  if (!legacyName) {
6311
7177
  throw new Error(`No legacy package manifest found at ${packageRoot}`);
6312
7178
  }
6313
- const from = join26(packageRoot, legacyName);
7179
+ const from = join29(packageRoot, legacyName);
6314
7180
  const toName = legacyName.endsWith(".jsonc") ? "openpack.jsonc" : "openpack.json";
6315
- const to = join26(packageRoot, toName);
6316
- const content = await readFile17(from, "utf8");
7181
+ const to = join29(packageRoot, toName);
7182
+ const content = await readFile20(from, "utf8");
6317
7183
  const updated = updateSchemaVersion(content);
6318
7184
  await rename4(from, to);
6319
- await writeFile11(to, updated, "utf8");
7185
+ await writeFile13(to, updated, "utf8");
6320
7186
  return { changed: true, from, to, message: `Migrated ${legacyName} to ${toName}.` };
6321
7187
  }
6322
7188
  async function firstExistingLegacyManifest(root) {
6323
7189
  for (const name of legacyPackageManifestNames) {
6324
- if (await pathExists(join26(root, name))) return name;
7190
+ if (await pathExists(join29(root, name))) return name;
6325
7191
  }
6326
7192
  return void 0;
6327
7193
  }
@@ -6339,20 +7205,20 @@ function updateSchemaVersion(content) {
6339
7205
 
6340
7206
  // src/cli/version.ts
6341
7207
  import { readFileSync } from "fs";
6342
- import { dirname as dirname20, join as join27 } from "path";
7208
+ import { dirname as dirname22, join as join30 } from "path";
6343
7209
  import { fileURLToPath as fileURLToPath2 } from "url";
6344
7210
  var FALLBACK_VERSION = "0.0.0";
6345
7211
  function resolveCliVersion() {
6346
- let dir = dirname20(fileURLToPath2(import.meta.url));
7212
+ let dir = dirname22(fileURLToPath2(import.meta.url));
6347
7213
  while (true) {
6348
7214
  try {
6349
- const pkg = JSON.parse(readFileSync(join27(dir, "package.json"), "utf8"));
7215
+ const pkg = JSON.parse(readFileSync(join30(dir, "package.json"), "utf8"));
6350
7216
  if (pkg.name === "agentwheel" && typeof pkg.version === "string") {
6351
7217
  return pkg.version;
6352
7218
  }
6353
7219
  } catch {
6354
7220
  }
6355
- const parent = dirname20(dir);
7221
+ const parent = dirname22(dir);
6356
7222
  if (parent === dir) return FALLBACK_VERSION;
6357
7223
  dir = parent;
6358
7224
  }
@@ -6368,7 +7234,7 @@ Core flow:
6368
7234
  $ agentwheel plan
6369
7235
  $ agentwheel install
6370
7236
  `);
6371
- 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) => {
6372
7238
  const root = normalizeTargetRoot(options.targetRoot);
6373
7239
  if (kind === "package") {
6374
7240
  await initPackage(root);
@@ -6386,28 +7252,29 @@ program.command("init").description("initialize an agentwheel workspace or packa
6386
7252
  if (bootstrapPackage) console.log("Auto-added the agentwheel bootstrap skill for openclaw.");
6387
7253
  console.log(nextInstallNudge());
6388
7254
  });
6389
- 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, []).option("--override <source-or-package::type/name>", "allow this package to replace a colliding artifact (repeatable)", collectOverrideOption, []).action(async (source, options) => {
6390
- const targetRoot = normalizeTargetRoot(options.targetRoot);
6391
- 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);
6392
7259
  await writeWorkspaceConfig(targetRoot, upsertPackage(await readWorkspaceConfig(targetRoot), entry));
6393
7260
  console.log(`Added ${entry.name}. Preview: agentwheel plan - Apply: agentwheel install`);
6394
7261
  });
6395
- 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) => {
6396
7263
  const targetRoot = normalizeTargetRoot(options.targetRoot);
6397
7264
  const selectedArtifacts = selectedArtifactsFromOptions(options);
6398
7265
  const resolvedInput = await resolvePackageSource(source, targetRoot);
6399
7266
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
6400
- 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") }))));
6401
7268
  const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
6402
7269
  for (const artifact of artifacts) {
6403
7270
  console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
6404
7271
  }
6405
7272
  });
6406
- 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) => {
6407
7274
  const targetRoot = normalizeTargetRoot(options.targetRoot);
6408
7275
  const resolvedInput = await resolvePackageSource(source, targetRoot);
6409
7276
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
6410
- 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") }))));
6411
7278
  const result = await driver.scan(resolved);
6412
7279
  if (result.findings.length === 0) {
6413
7280
  console.log("Scan ok: no findings");
@@ -6418,28 +7285,30 @@ program.command("scan").description("scan a package source for validation findin
6418
7285
  }
6419
7286
  if (!result.ok) process.exitCode = 1;
6420
7287
  });
6421
- 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("--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("--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) => {
6422
7289
  await runInstallCommand(source, { ...options, dryRun: true }, { apply: false });
6423
7290
  });
6424
- 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("--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("--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) => {
6425
7292
  await runInstallCommand(source, options, { apply: !options.dryRun });
6426
7293
  });
6427
- 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("--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("--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) => {
6428
7295
  console.error("warning: 'agentwheel sync' is deprecated and will be removed in 0.10. Use 'agentwheel install'.");
6429
7296
  await runInstallCommand(source, options, { apply: !options.dryRun });
6430
7297
  });
6431
- 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) => {
6432
- 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 });
6433
7301
  for (const target of targets) {
6434
- await runConfiguredGraphPackages(target, { ...options, scope: name }, { mode: "update" });
7302
+ await runConfiguredGraphPackages(target, { ...normalizedOptions, scope: name }, { mode: "update" });
6435
7303
  }
6436
7304
  });
6437
7305
  program.command("deps").description("inspect the OpenPack dependency graph").addCommand(
6438
- 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) => {
6439
- 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);
6440
7309
  for (const target of targets) {
6441
7310
  if (source) {
6442
- for (const result of await buildGraphPlansForTarget(target, source, options, { mode: "install" })) {
7311
+ for (const result of await buildGraphPlansForTarget(target, source, normalizedOptions, { mode: "install" })) {
6443
7312
  console.log(formatDependencyTree(result.graph).join("\n"));
6444
7313
  for (const decision of result.bundle.graphLock.canonical.namespacing) {
6445
7314
  console.log(`NAMESPACE ${decision.graphNodeId}:${decision.type}/${decision.name} -> ${decision.type}/${decision.installName} (${decision.reason})`);
@@ -6451,39 +7320,42 @@ program.command("deps").description("inspect the OpenPack dependency graph").add
6451
7320
  }
6452
7321
  continue;
6453
7322
  }
6454
- const { lock } = await readTargetGraphLock(target, options);
7323
+ const { lock } = await readTargetGraphLock(target, normalizedOptions);
6455
7324
  console.log(formatLockDependencyTree(lock));
6456
7325
  }
6457
7326
  })
6458
7327
  ).addCommand(
6459
- 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) => {
6460
- 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);
6461
7331
  for (const target of targets) {
6462
- const { lock, adapter } = await readTargetGraphLock(target, options);
6463
- 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);
6464
7336
  console.log(formatDepsWhy(lock, manifest, selector));
6465
7337
  }
6466
7338
  })
6467
7339
  );
6468
7340
  program.command("registry").description("manage optional registry indexes").addCommand(
6469
- 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) => {
6470
7342
  const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
6471
7343
  const index = await client.getIndex({ refresh: true });
6472
7344
  console.log(`Registry refreshed: ${index.entries.length} entries from ${index.sources.join(", ")}`);
6473
7345
  })
6474
7346
  ).addCommand(
6475
- 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) => {
6476
7348
  const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
6477
7349
  printRegistryEntries((await client.getIndex()).entries);
6478
7350
  })
6479
7351
  ).addCommand(
6480
- 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) => {
6481
7353
  const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
6482
7354
  printRegistryEntries(await client.search(query));
6483
7355
  })
6484
7356
  );
6485
7357
  program.command("trust").description("manage persisted source trust decisions").addCommand(
6486
- 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) => {
6487
7359
  const removed = await forgetTrustedSources(normalizeTargetRoot(options.targetRoot), pattern);
6488
7360
  if (removed.length === 0) {
6489
7361
  console.log(`No persisted trust matched ${pattern}.`);
@@ -6510,114 +7382,128 @@ program.command("package").description("validate and migrate OpenPack packages")
6510
7382
  console.log(result.message);
6511
7383
  })
6512
7384
  );
6513
- 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) => {
6514
7386
  const targetRoot = normalizeTargetRoot(options.targetRoot);
6515
7387
  const result = await remember(targetRoot, options.runtime, text);
6516
7388
  console.log(`Remembered in ${result.overlayPath}.`);
6517
7389
  console.log(nextInstallNudge());
6518
7390
  });
6519
- 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) => {
6520
7392
  const targetRoot = normalizeTargetRoot(options.targetRoot);
6521
7393
  const result = await ejectArtifact(targetRoot, item);
6522
7394
  console.log(`Ejected ${item} to ${result.ejectedPath}.`);
6523
7395
  console.log(nextInstallNudge());
6524
7396
  });
6525
- 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) => {
6526
7398
  if (options.keepFiles && options.force) {
6527
7399
  throw new Error("--keep-files cannot be combined with --force.");
6528
7400
  }
6529
- const targets = await resolveCliTargets(options);
7401
+ const normalizedOptions = normalizeRuntimeScopeOptions(options);
7402
+ const targets = await resolveCliTargets(normalizedOptions);
6530
7403
  for (const target of targets) {
6531
7404
  if (packageName) {
6532
- await uninstallConfiguredPackage(target, packageName, options);
7405
+ await uninstallConfiguredPackage(target, packageName, normalizedOptions);
6533
7406
  continue;
6534
7407
  }
6535
- if (options.keepFiles) {
7408
+ if (normalizedOptions.keepFiles) {
6536
7409
  throw new Error("--keep-files requires a configured package name or source.");
6537
7410
  }
6538
- const adapter = await resolveAdapterForTarget(target, options);
7411
+ const adapter = await resolveAdapterForTarget(target, normalizedOptions);
6539
7412
  const transport = transportForTarget(target);
6540
- 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);
6541
7416
  if (!manifest) {
6542
- console.log(`No install manifest for ${adapter.name} at ${target.targetRoot}`);
7417
+ console.log(`No install manifest for ${adapter.name}/${installationType} at ${state.installRoot}`);
6543
7418
  continue;
6544
7419
  }
6545
7420
  const plan = filterUninstallPlanBySelection(await createUninstallPlan(manifest), selectedArtifactsFromOptions(options));
6546
7421
  console.log(formatPlan(plan));
6547
- const result = await uninstall(plan, { dryRun: options.dryRun, force: options.force, transport });
6548
- if (!options.dryRun) {
7422
+ const result = await uninstall(plan, { dryRun: normalizedOptions.dryRun, force: normalizedOptions.force, transport });
7423
+ if (!normalizedOptions.dryRun) {
6549
7424
  if (transport.kind !== "local" && adapter.programmatic?.uninstall) {
6550
7425
  throw new Error(`Cannot execute programmatic adapter uninstall over ${transport.description}.`);
6551
7426
  }
6552
- await adapter.programmatic?.uninstall?.({ targetRoot: target.targetRoot, adapterName: adapter.name });
7427
+ await adapter.programmatic?.uninstall?.({ targetRoot: state.installRoot, adapterName: adapter.name });
6553
7428
  console.log(formatUninstallResult(result));
6554
7429
  }
6555
7430
  if (plan.hasBlockingChanges) process.exitCode = 1;
6556
7431
  }
6557
7432
  });
6558
- 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) => {
6559
- 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 });
6560
7436
  for (const target of targets) {
6561
- await printStatus(target, options);
7437
+ await printStatus(target, normalizedOptions);
6562
7438
  }
6563
7439
  });
6564
7440
  async function runInstallCommand(nameOrSource, options, behavior) {
7441
+ const normalizedOptions = normalizeRuntimeScopeOptions(options, { defaultUser: shouldDefaultUserInstall(nameOrSource, options) });
6565
7442
  if (options.profile) {
6566
- 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
+ });
6567
7449
  const results = await syncProfile({
6568
7450
  workspaceRoot: target.workspaceRoot,
6569
7451
  profile: options.profile,
6570
7452
  source: nameOrSource,
6571
- driver: options.driver,
6572
- mode: options.mode,
6573
- select: selectedArtifactsFromOptions(options),
7453
+ driver: normalizedOptions.driver,
7454
+ mode: normalizedOptions.mode,
7455
+ select: selectedArtifactsFromOptions(normalizedOptions),
7456
+ installationType: normalizedOptions.installationType,
6574
7457
  dryRun: !behavior.apply,
6575
- executePlugins: options.executePlugins,
6576
- allowAdapterCode: options.allowAdapterCode,
6577
- 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),
6578
7464
  lockedResolution: true,
6579
- frozenLock: options.frozenLock,
6580
- offline: options.offline,
6581
- yes: options.yes,
6582
- trustPatterns: options.trust ?? [],
7465
+ frozenLock: normalizedOptions.frozenLock,
7466
+ offline: normalizedOptions.offline,
7467
+ yes: normalizedOptions.yes,
7468
+ trustPatterns: normalizedOptions.trust ?? [],
6583
7469
  readOnly: !behavior.apply,
6584
7470
  isTTY: process.stdin.isTTY === true,
6585
7471
  warn: (message) => console.warn(message)
6586
7472
  });
6587
7473
  for (const result of results) {
6588
- 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}):`);
6589
7475
  console.log(formatPlan(result.plan));
6590
7476
  if (result.plan.hasBlockingChanges) process.exitCode = 1;
6591
7477
  }
6592
7478
  if (behavior.apply) console.log("Applied.");
6593
7479
  return;
6594
7480
  }
6595
- const targets = await resolveCliTargets(options);
7481
+ const targets = await resolveCliTargets(normalizedOptions);
6596
7482
  for (const target of targets) {
7483
+ const targetOptions = optionsForResolvedTarget(normalizedOptions, target);
6597
7484
  const config = await readMergedWorkspaceConfig(target.workspaceRoot);
6598
- const configured = nameOrSource ? findConfiguredPackage(config.packages, nameOrSource) : void 0;
7485
+ const configured = nameOrSource ? findConfiguredPackageForTarget(config.packages, nameOrSource, targetOptions, target) : void 0;
6599
7486
  let source;
6600
7487
  let scope = configured?.name;
6601
7488
  let extraPackage;
6602
7489
  if (nameOrSource && !configured) {
6603
7490
  try {
6604
- const entry = await packageEntryFromSource(nameOrSource, target.workspaceRoot, { ...options, adapter: options.adapter ?? target.adapter });
6605
- scope = entry.name;
6606
- if (behavior.apply) {
6607
- await writeWorkspaceConfig(target.workspaceRoot, upsertPackage(await readWorkspaceConfig(target.workspaceRoot), entry));
6608
- } else {
6609
- source = nameOrSource;
6610
- extraPackage = entry;
7491
+ let entry = await packageEntryFromSource(nameOrSource, target.workspaceRoot, { ...targetOptions, adapter: targetOptions.adapter ?? target.adapter });
7492
+ if (targetOptions.multiAdapterSource) {
7493
+ entry = packageEntryWithAdapterSuffix(entry);
6611
7494
  }
7495
+ scope = entry.name;
7496
+ source = nameOrSource;
7497
+ extraPackage = entry;
6612
7498
  } catch (error) {
6613
7499
  throw teachingInstallError(nameOrSource, error);
6614
7500
  }
6615
7501
  }
6616
- 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" })) {
6617
7503
  console.log(formatGraphPlan(result));
6618
7504
  if (behavior.apply) {
6619
7505
  await applyCombinedInstallPlan(result.plan, {
6620
- executePlugins: options.executePlugins,
7506
+ executePlugins: targetOptions.executePlugins,
6621
7507
  transport: transportForTarget(target),
6622
7508
  graphLockDigest: result.graphLockDigest,
6623
7509
  graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
@@ -6627,6 +7513,9 @@ async function runInstallCommand(nameOrSource, options, behavior) {
6627
7513
  await rm9(result.bundle.root, { recursive: true, force: true });
6628
7514
  if (result.plan.hasBlockingChanges) process.exitCode = 1;
6629
7515
  }
7516
+ if (behavior.apply && extraPackage && !targetOptions.onlySource) {
7517
+ await writeWorkspaceConfig(target.workspaceRoot, upsertPackage(await readWorkspaceConfig(target.workspaceRoot), extraPackage));
7518
+ }
6630
7519
  }
6631
7520
  }
6632
7521
  async function packageEntryFromSource(source, targetRoot, options) {
@@ -6647,17 +7536,19 @@ async function packageEntryFromSource(source, targetRoot, options) {
6647
7536
  const bundle = await stageSource(driver, resolvedSource, {
6648
7537
  workspaceRoot: targetRoot,
6649
7538
  adapter,
6650
- cacheRoot: join28(targetRoot, ".agentwheel", "cache"),
7539
+ cacheRoot: join31(targetRoot, ".agentwheel", "cache"),
6651
7540
  mode: options.mode,
6652
7541
  frozenLock: lockMode,
6653
7542
  select: selectedArtifacts
6654
7543
  });
7544
+ const installationType = resolveInstallationTypeForArtifacts(adapter, bundle.artifacts.map((artifact) => artifact.type), options.installationType);
6655
7545
  try {
6656
7546
  return {
6657
7547
  name: options.name ?? resolvedInput.registryEntry?.name ?? bundle.source.packageName ?? source,
6658
7548
  source: resolvedSource,
6659
7549
  driver: driverName,
6660
7550
  adapter: adapter.name,
7551
+ installationType,
6661
7552
  adapterConfig: options.adapterConfig,
6662
7553
  adapterModule: options.adapterModule,
6663
7554
  adapterCodeHash: adapter.programmatic?.hash,
@@ -6673,9 +7564,17 @@ async function packageEntryFromSource(source, targetRoot, options) {
6673
7564
  function findConfiguredPackage(packages, value) {
6674
7565
  return packages.find((pkg) => pkg.name === value || pkg.source === value);
6675
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
+ }
6676
7571
  function noDepsFromOptions(options) {
6677
7572
  return options.noDeps === true || options.deps === false;
6678
7573
  }
7574
+ function packageEntryWithAdapterSuffix(entry) {
7575
+ const suffix = `-${entry.adapter}`;
7576
+ return entry.name.endsWith(suffix) ? entry : { ...entry, name: `${entry.name}${suffix}` };
7577
+ }
6679
7578
  function teachingInstallError(input, cause) {
6680
7579
  const message = cause instanceof Error ? cause.message : String(cause);
6681
7580
  return new Error(
@@ -6689,28 +7588,103 @@ Resolver error: ${message}`
6689
7588
  function nextInstallNudge() {
6690
7589
  return "Preview: agentwheel plan - Apply: agentwheel install";
6691
7590
  }
6692
- async function resolveCliTargets(options) {
6693
- if (options.all && options.allDetected) {
7591
+ async function resolveCliTargets(options, behavior = {}) {
7592
+ const normalizedOptions = normalizeRuntimeScopeOptions(options);
7593
+ if (normalizedOptions.all && normalizedOptions.allDetected) {
6694
7594
  throw new Error("Choose either --all for configured agents or --all-detected for detected runtime directories.");
6695
7595
  }
6696
- if (options.all) {
6697
- 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
+ });
6698
7621
  }
6699
- if (options.allDetected) {
6700
- return resolveAllDetectedRuntimeTargets({ targetRoot: options.targetRoot, adapter: options.adapter, agent: options.agent, allDetected: options.allDetected });
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
+ });
7634
+ }
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
+ });
6701
7643
  }
6702
- 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;
6703
7666
  }
6704
7667
  async function resolveAdapterForTarget(target, options) {
7668
+ const adapterOptions = adapterOptionsForTarget(target, options);
6705
7669
  return resolveAdapter({
6706
7670
  adapter: target.adapter,
6707
- adapterConfig: options.adapterConfig,
6708
- adapterModule: options.adapterModule,
6709
- allowAdapterCode: options.allowAdapterCode,
7671
+ adapterConfig: adapterOptions.adapterConfig,
7672
+ adapterModule: adapterOptions.adapterModule,
7673
+ allowAdapterCode: adapterOptions.allowAdapterCode,
6710
7674
  baseDir: target.workspaceRoot,
6711
7675
  warn: (message) => console.warn(message)
6712
7676
  });
6713
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
+ }
6714
7688
  async function runConfiguredGraphPackages(target, options, behavior) {
6715
7689
  const results = await buildGraphPlansForTarget(target, void 0, options, behavior);
6716
7690
  for (const result of results) {
@@ -6730,37 +7704,39 @@ async function runConfiguredGraphPackages(target, options, behavior) {
6730
7704
  }
6731
7705
  }
6732
7706
  async function buildGraphPlansForTarget(target, source, options, behavior) {
7707
+ const targetOptions = optionsForResolvedTarget(options, target);
6733
7708
  const config = await readMergedWorkspaceConfig(target.workspaceRoot);
6734
7709
  const groups = /* @__PURE__ */ new Map();
6735
- const selectedArtifacts = selectedArtifactsFromOptions(options);
6736
- const scopedPackage = options.scope ? findConfiguredPackage(config.packages, options.scope) : void 0;
6737
- const scopedRootId = scopedPackage?.name ?? (source ? options.scope : void 0);
6738
- if (options.scope && !scopedPackage && !source) throw new Error(`Configured package not found: ${options.scope}`);
6739
- 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) {
6740
7715
  for (const pkg of config.packages) {
6741
- const group = graphGroupForPackage(groups, target, pkg, options);
7716
+ const group = graphGroupForPackage(groups, target, pkg, targetOptions);
6742
7717
  group.packages.push(pkg);
6743
7718
  }
6744
7719
  }
6745
7720
  if (source) {
7721
+ let entry = targetOptions.extraPackage ?? await packageEntryFromSource(source, target.workspaceRoot, targetOptions);
7722
+ if (targetOptions.multiAdapterSource) {
7723
+ entry = packageEntryWithAdapterSuffix(entry);
7724
+ }
6746
7725
  const sourceTarget = target;
7726
+ const sourceInstallationType = targetOptions.installationType ?? entry.installationType ?? sourceTarget.installationType ?? "local";
7727
+ const sourceAdapterOptions = adapterOptionsForTarget(sourceTarget, targetOptions);
6747
7728
  const key = graphGroupKey(sourceTarget, {
6748
- adapterConfig: options.adapterConfig,
6749
- adapterModule: options.adapterModule,
6750
- allowAdapterCode: options.allowAdapterCode
7729
+ installationType: sourceInstallationType,
7730
+ ...sourceAdapterOptions
6751
7731
  });
6752
7732
  const group = groups.get(key) ?? {
6753
7733
  target: sourceTarget,
6754
- adapterOptions: {
6755
- adapterConfig: options.adapterConfig,
6756
- adapterModule: options.adapterModule,
6757
- allowAdapterCode: options.allowAdapterCode
6758
- },
7734
+ installationType: sourceInstallationType,
7735
+ adapterOptions: sourceAdapterOptions,
6759
7736
  packages: [],
6760
7737
  extraRoots: [],
6761
7738
  extraPackages: []
6762
7739
  };
6763
- const entry = options.extraPackage ?? await packageEntryFromSource(source, target.workspaceRoot, options);
6764
7740
  group.extraPackages.push(entry);
6765
7741
  groups.set(key, group);
6766
7742
  }
@@ -6773,18 +7749,19 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
6773
7749
  const adapter = await resolveAdapterForTarget(group.target, group.adapterOptions);
6774
7750
  const transport = transportForTarget(group.target);
6775
7751
  const allPackages = [...group.packages, ...group.extraPackages];
6776
- 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);
6777
7753
  if (behavior.mode === "install" && scopedRootId && !groupHasScope) continue;
6778
7754
  const updateScope = behavior.mode === "update" ? scopedPackage ? /* @__PURE__ */ new Set([scopedPackage.name]) : void 0 : void 0;
6779
7755
  const roots = [
6780
7756
  ...allPackages.map((pkg) => {
6781
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;
6782
7759
  return {
6783
7760
  rootId: pkg.name,
6784
7761
  source: pkg.source,
6785
7762
  mode: pkg.mode,
6786
7763
  ref: pkg.requestedRef,
6787
- select: selectedArtifacts ?? normalizeArtifactSelectors(pkg.select, pkg.skills),
7764
+ select: selectedArtifacts && packageIsScoped ? selectedArtifacts : normalizeArtifactSelectors(pkg.select, pkg.skills),
6788
7765
  aliases: pkg.aliases,
6789
7766
  overrides: pkg.overrides,
6790
7767
  useLock: behavior.mode === "install" ? true : !updateThisPackage
@@ -6795,7 +7772,7 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
6795
7772
  if (behavior.mode === "update") {
6796
7773
  const changed = roots.filter((root) => root.useLock === false);
6797
7774
  if (changed.length === 0) {
6798
- const label = options.scope ? ` ${options.scope}` : "";
7775
+ const label = targetOptions.scope ? ` ${targetOptions.scope}` : "";
6799
7776
  console.log(`No tracking packages to update${label}.`);
6800
7777
  continue;
6801
7778
  }
@@ -6807,19 +7784,24 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
6807
7784
  workspaceRoot: group.target.workspaceRoot,
6808
7785
  adapter,
6809
7786
  transport,
6810
- targetKey: group.target.agentName ?? group.target.source,
6811
- targetFingerprintParts: targetFingerprintParts(group.target, adapter, group.adapterOptions),
6812
- 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),
6813
7791
  lockedResolution: behavior.mode === "install",
6814
- frozenLock: options.frozenLock,
6815
- offline: options.offline,
6816
- yes: options.yes,
6817
- trustPatterns: options.trust ?? [],
6818
- readOnly: options.dryRun === true,
6819
- 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
6820
7801
  });
6821
7802
  if (behavior.mode === "install" && scopedRootId) {
6822
- 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);
6823
7805
  results.push(scopeInstallPlanToRoot(result, scopedRootId, manifest));
6824
7806
  } else {
6825
7807
  results.push(result);
@@ -6906,7 +7888,7 @@ function keepManifestEntryOperation(entry, targetRoot, rootId, operation, option
6906
7888
  artifactType: entry.artifactType,
6907
7889
  artifactName: entry.artifactName,
6908
7890
  kind: entry.kind,
6909
- destPath: operation?.destPath ?? join28(targetRoot, entry.path),
7891
+ destPath: operation?.destPath ?? join31(targetRoot, entry.path),
6910
7892
  relativeDestPath: entry.path,
6911
7893
  desiredHash: entry.sourceHash,
6912
7894
  currentHash: operation?.currentHash ?? entry.hash,
@@ -6942,13 +7924,15 @@ async function uninstallConfiguredPackage(target, packageName, options) {
6942
7924
  for (const pkg of removed) {
6943
7925
  const removedTarget = targetForPackage(target, pkg, options);
6944
7926
  const removedAdapterOptions = {
6945
- adapterConfig: options.adapterConfig ?? pkg.adapterConfig,
6946
- adapterModule: options.adapterModule ?? pkg.adapterModule,
7927
+ adapterConfig: options.adapterConfig ?? removedTarget.adapterConfig ?? pkg.adapterConfig,
7928
+ adapterModule: options.adapterModule ?? removedTarget.adapterModule ?? pkg.adapterModule,
6947
7929
  allowAdapterCode: options.allowAdapterCode
6948
7930
  };
7931
+ const removedInstallationType = options.installationType ?? pkg.installationType ?? removedTarget.installationType ?? "local";
6949
7932
  const adapter = await resolveAdapterForTarget(removedTarget, removedAdapterOptions);
6950
7933
  const transport = transportForTarget(removedTarget);
6951
- 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);
6952
7936
  if (!manifest) {
6953
7937
  console.log(`No install manifest for ${adapter.name} at ${removedTarget.targetRoot}`);
6954
7938
  continue;
@@ -6974,8 +7958,9 @@ async function uninstallConfiguredPackage(target, packageName, options) {
6974
7958
  workspaceRoot: remainingGroup.target.workspaceRoot,
6975
7959
  adapter: remainingAdapter,
6976
7960
  transport,
6977
- targetKey: remainingGroup.target.agentName ?? remainingGroup.target.source,
6978
- 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,
6979
7964
  lockedResolution: true,
6980
7965
  frozenLock: options.frozenLock,
6981
7966
  offline: options.offline,
@@ -6994,9 +7979,9 @@ async function uninstallConfiguredPackage(target, packageName, options) {
6994
7979
  const graphLockFinalState = remainingGraphPlan ? { graphLock: { path: remainingGraphPlan.graphLockPath, lock: remainingGraphPlan.bundle.graphLock } } : {
6995
7980
  removeGraphLockPath: graphLockPathForTarget(
6996
7981
  removedTarget.workspaceRoot,
6997
- removedTarget.agentName ?? removedTarget.source,
7982
+ targetKeyForTarget(removedTarget, adapter.name),
6998
7983
  adapter.name,
6999
- targetFingerprintParts(removedTarget, adapter, removedAdapterOptions)
7984
+ targetFingerprintParts(removedTarget, adapter, removedAdapterOptions, removedInstallationType)
7000
7985
  )
7001
7986
  };
7002
7987
  const result = await uninstall(plan, {
@@ -7019,16 +8004,18 @@ async function uninstallConfiguredPackage(target, packageName, options) {
7019
8004
  }
7020
8005
  function graphGroupForPackage(groups, target, pkg, options) {
7021
8006
  const packageTarget = targetForPackage(target, pkg, options);
8007
+ const installationType = options.installationType ?? pkg.installationType ?? packageTarget.installationType ?? "local";
7022
8008
  const adapterOptions = {
7023
- adapterConfig: options.adapterConfig ?? pkg.adapterConfig,
7024
- adapterModule: options.adapterModule ?? pkg.adapterModule,
8009
+ adapterConfig: options.adapterConfig ?? packageTarget.adapterConfig ?? pkg.adapterConfig,
8010
+ adapterModule: options.adapterModule ?? packageTarget.adapterModule ?? pkg.adapterModule,
7025
8011
  allowAdapterCode: options.allowAdapterCode
7026
8012
  };
7027
- const key = graphGroupKey(packageTarget, adapterOptions);
8013
+ const key = graphGroupKey(packageTarget, { ...adapterOptions, installationType });
7028
8014
  const existing = groups.get(key);
7029
8015
  if (existing) return existing;
7030
8016
  const created = {
7031
8017
  target: packageTarget,
8018
+ installationType,
7032
8019
  adapterOptions,
7033
8020
  packages: [],
7034
8021
  extraRoots: [],
@@ -7038,11 +8025,13 @@ function graphGroupForPackage(groups, target, pkg, options) {
7038
8025
  return created;
7039
8026
  }
7040
8027
  function targetForPackage(target, pkg, options) {
7041
- 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 };
7042
8030
  }
7043
8031
  function graphGroupKey(target, options) {
7044
8032
  return JSON.stringify({
7045
8033
  adapter: target.adapter,
8034
+ installationType: options.installationType ?? target.installationType ?? "local",
7046
8035
  targetRoot: target.targetRoot,
7047
8036
  transport: target.transport,
7048
8037
  agentName: target.agentName,
@@ -7050,9 +8039,10 @@ function graphGroupKey(target, options) {
7050
8039
  adapterModule: options.adapterModule
7051
8040
  });
7052
8041
  }
7053
- function targetFingerprintParts(target, adapter, options) {
8042
+ function targetFingerprintParts(target, adapter, options, installationType) {
7054
8043
  return {
7055
8044
  adapter: adapter.name,
8045
+ installationType: installationType ?? target.installationType ?? "local",
7056
8046
  adapterConfig: options.adapterConfig,
7057
8047
  adapterModule: options.adapterModule,
7058
8048
  adapterCodeHash: adapter.programmatic?.hash,
@@ -7062,13 +8052,27 @@ function targetFingerprintParts(target, adapter, options) {
7062
8052
  ssh: target.ssh
7063
8053
  };
7064
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
+ }
7065
8067
  async function readTargetGraphLock(target, options) {
7066
- 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);
7067
8071
  const path = graphLockPathForTarget(
7068
8072
  target.workspaceRoot,
7069
- target.agentName ?? target.source,
8073
+ targetKeyForTarget(target, adapter.name),
7070
8074
  adapter.name,
7071
- targetFingerprintParts(target, adapter, options)
8075
+ targetFingerprintParts(target, adapter, adapterOptions, installationType)
7072
8076
  );
7073
8077
  if (!await pathExists(path)) {
7074
8078
  throw new Error(`No graph lock for ${adapter.name} at ${target.targetRoot}: ${path}`);
@@ -7077,9 +8081,12 @@ async function readTargetGraphLock(target, options) {
7077
8081
  }
7078
8082
  async function printStatus(target, options) {
7079
8083
  const config = await readMergedWorkspaceConfig(target.workspaceRoot);
7080
- const adapter = await resolveAdapterForTarget(target, options);
8084
+ const adapterOptions = adapterOptionsForTarget(target, options);
8085
+ const adapter = await resolveAdapterForTarget(target, adapterOptions);
7081
8086
  const transport = transportForTarget(target);
7082
- 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}`);
7083
8090
  if (config.packages.length === 0) {
7084
8091
  console.log(`Configured packages: none at ${target.workspaceRoot}`);
7085
8092
  return;
@@ -7088,10 +8095,10 @@ async function printStatus(target, options) {
7088
8095
  for (const pkg of config.packages) {
7089
8096
  console.log(`- ${pkg.name} (${pkg.mode}) ${pkg.source}`);
7090
8097
  }
7091
- const manifest = await readInstallManifest(target.targetRoot, adapter.name, transport);
8098
+ const manifest = await readInstallManifest(state.installRoot, adapter.name, transport, state);
7092
8099
  console.log(manifest ? `Install manifest: ${manifest.entries.length} entries, revision ${manifest.revision}` : "Install manifest: missing");
7093
8100
  try {
7094
- const { path, lock } = await readTargetGraphLock(target, options);
8101
+ const { path, lock } = await readTargetGraphLock(target, adapterOptions);
7095
8102
  console.log(`Graph lock: ${path}`);
7096
8103
  console.log(`Locked graph: ${lock.canonical.roots.length} roots, ${lock.canonical.nodes.length} nodes, ${lock.canonical.artifacts.length} artifacts`);
7097
8104
  } catch {
@@ -7104,7 +8111,7 @@ async function printPendingInstallWork(target, options) {
7104
8111
  try {
7105
8112
  results = await buildGraphPlansForTarget(target, void 0, { ...options, dryRun: true }, { mode: "install" });
7106
8113
  const operations = results.flatMap((result) => result.plan.operations);
7107
- const pending = operations.filter((operation) => operation.action !== "skip");
8114
+ const pending = operations.filter(isPendingInstallOperation);
7108
8115
  if (pending.length === 0) {
7109
8116
  console.log("Pending install work: none");
7110
8117
  return;
@@ -7134,6 +8141,57 @@ function collectTrustOption(value, previous) {
7134
8141
  function collectOverrideOption(value, previous) {
7135
8142
  return [...previous, ...splitSelectorList(value)];
7136
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
+ }
7137
8195
  function selectedArtifactsFromOptions(options) {
7138
8196
  return normalizeArtifactSelectors(options.select, options.skills ?? options.skill);
7139
8197
  }
@@ -7166,10 +8224,10 @@ function filterUninstallPlanBySelection(plan, selected) {
7166
8224
  };
7167
8225
  }
7168
8226
  async function initPackage(root) {
7169
- await mkdir14(join28(root, "instructions"), { recursive: true });
7170
- await mkdir14(join28(root, "rules"), { recursive: true });
7171
- await mkdir14(join28(root, "skills"), { recursive: true });
7172
- 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");
7173
8231
  const manifest = {
7174
8232
  schemaVersion: 2,
7175
8233
  name: "example/agentwheel-package",
@@ -7180,18 +8238,19 @@ async function initPackage(root) {
7180
8238
  { type: "skills", path: "skills" }
7181
8239
  ]
7182
8240
  };
7183
- await writeFile12(manifestPath, `${JSON.stringify(manifest, null, 2)}
8241
+ await writeFile14(manifestPath, `${JSON.stringify(manifest, null, 2)}
7184
8242
  `, "utf8");
7185
- await writeFile12(join28(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
8243
+ await writeFile14(join31(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
7186
8244
  }
7187
8245
  async function defaultBootstrapPackage(_root) {
7188
- const packageRoot = await findAgentwheelPackageRoot(dirname21(fileURLToPath3(import.meta.url)));
8246
+ const packageRoot = await findAgentwheelPackageRoot(dirname23(fileURLToPath3(import.meta.url)));
7189
8247
  if (!packageRoot) return void 0;
7190
8248
  return {
7191
8249
  name: "agentwheel",
7192
8250
  source: packageRoot,
7193
8251
  driver: "local",
7194
8252
  adapter: "openclaw",
8253
+ installationType: "local",
7195
8254
  mode: "tracking",
7196
8255
  select: ["skills/agentwheel"]
7197
8256
  };
@@ -7208,7 +8267,7 @@ function withFleetExample(config) {
7208
8267
  },
7209
8268
  "remote-codex": config.agents["remote-codex"] ?? {
7210
8269
  adapter: "codex",
7211
- root: "/home/administrator/agent-runtime",
8270
+ root: "/workspace/agent-runtime",
7212
8271
  transport: "ssh",
7213
8272
  host: "remote-host.example",
7214
8273
  user: "administrator",
@@ -7231,7 +8290,7 @@ async function findAgentwheelPackageRoot(start) {
7231
8290
  let current = resolve18(start);
7232
8291
  while (true) {
7233
8292
  if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
7234
- const parent = dirname21(current);
8293
+ const parent = dirname23(current);
7235
8294
  if (parent === current) return void 0;
7236
8295
  current = parent;
7237
8296
  }