agentwheel 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +53 -2
  2. package/dist/index.js +343 -63
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -33,9 +33,10 @@ No lock-in. No central gatekeeper. Your packages live in plain git repos, your c
33
33
 
34
34
  ---
35
35
 
36
- > **Status: early (v0.4).** The lifecycle core is real and tested — local/git/skillkit/vercel
36
+ > **Status: early (v0.6).** The lifecycle core is real and tested — local/git/skillkit/vercel
37
37
  > sources, optional registry discovery, plan/sync/update/drift/uninstall, overlays, eject/remember,
38
- > profiles, runtime auto-detection, fleet targeting, rich JSON merge, and pluggable adapters.
38
+ > profiles, runtime auto-detection, fleet targeting, asset-includes, selective installs,
39
+ > update notifications, rich JSON merge, and pluggable adapters.
39
40
  > Expect sharp edges.
40
41
 
41
42
  ## What it does
@@ -75,6 +76,10 @@ pnpm link --global
75
76
  `uninstall` removes clean managed files by default and keeps drifted files in place with a warning.
76
77
  Use `agentwheel uninstall --force` only when you also want to remove drifted managed files.
77
78
 
79
+ agentwheel checks npm for newer versions at most once every 24 hours and prints a non-blocking
80
+ stderr warning when an update is available. Disable it with `--no-update-check` or
81
+ `AGENTWHEEL_NO_UPDATE_CHECK=1`.
82
+
78
83
  ## Runtime targeting
79
84
 
80
85
  Normal use no longer needs `--target-root`. Run agentwheel inside a runtime folder and it detects
@@ -147,6 +152,50 @@ A package is a git repo (or folder) with a JSON manifest and a canonical layout:
147
152
  }
148
153
  ```
149
154
 
155
+ Install only part of a package with `--select <type>/<name>`. `--skill <name>` is a shortcut for
156
+ `--select skills/<name>`, and selections saved during `add` are reused by later `sync` and `update`
157
+ runs.
158
+
159
+ ```bash
160
+ agentwheel add github:NestDevLab/agent-mesh --skill codex-tmux --adapter openclaw
161
+ agentwheel sync --dry-run
162
+
163
+ agentwheel sync github:your-org/agent-pack --select rules/safe-actions.md --select commands/build.md
164
+ ```
165
+
166
+ Package authors can mark dependencies as required. Required artifacts are always installed and
167
+ cannot be deselected:
168
+
169
+ ```jsonc
170
+ {
171
+ "type": "rules",
172
+ "path": "rules/core-safety.md",
173
+ "required": true
174
+ }
175
+ ```
176
+
177
+ Packages can compose shared files into each directory artifact at staging time. This keeps one
178
+ canonical copy in the package repo while installing self-contained skills:
179
+
180
+ ```jsonc
181
+ {
182
+ "type": "skills",
183
+ "path": "skills",
184
+ "assets": [
185
+ {
186
+ "from": "packages/tmux-bridge/bin",
187
+ "into": "bin",
188
+ "include": ["*.sh"],
189
+ "mode": "preserve"
190
+ }
191
+ ]
192
+ }
193
+ ```
194
+
195
+ `mode: "preserve"` keeps executable bits on copied scripts. The composed files are included in
196
+ the skill directory hash, so idempotency and drift detection work as if the assets had always
197
+ belonged to the skill.
198
+
150
199
  Publish by pushing to any git host. A registry exists only for short names and discovery — it's
151
200
  optional, and `agentwheel add <url|path>` always works without it.
152
201
 
@@ -221,6 +270,8 @@ clear conversion format.
221
270
  - [x] **v0.2** — git source driver; `update` (pinned & tracking); overlays/additive/override/eject; `init`; hermes + copilot adapters; commands/mcp/hooks artifacts; OpenClaw semantic plugin planning.
222
271
  - [x] **v0.3** — skillkit/vercel source drivers; optional registry & federation; programmatic adapters behind `--allow-adapter-code`; rich JSON merge for mcp/hooks/settings; profiles.
223
272
  - [x] **v0.4** — runtime auto-detection; no `--target-root` needed for normal use; fleet config with named agents; global + project config merge; `--agent` and `--all`.
273
+ - [x] **v0.5** — asset-includes compose shared files into skills at install time; executable bits preserved; hashes include composed assets.
274
+ - [x] **v0.6** — selective installs with `--select`/`--skill`; required artifacts; cached npm update notifier.
224
275
 
225
276
  ## Design docs
226
277
 
package/dist/index.js CHANGED
@@ -8,8 +8,8 @@ import {
8
8
  } from "./chunk-N2LZY7LO.js";
9
9
 
10
10
  // src/cli/index.ts
11
- import { mkdir as mkdir7, rm as rm8, writeFile as writeFile4 } from "fs/promises";
12
- import { join as join19 } from "path";
11
+ import { mkdir as mkdir8, rm as rm8, writeFile as writeFile5 } from "fs/promises";
12
+ import { join as join20 } from "path";
13
13
  import { Command } from "commander";
14
14
 
15
15
  // src/adapters/resolve.ts
@@ -34,6 +34,12 @@ var artifactTypeSchema = z.enum([
34
34
  "plugins"
35
35
  ]);
36
36
  var fileKindSchema = z.enum(["file", "dir"]);
37
+ var packageAssetSchema = z.object({
38
+ from: z.string().min(1),
39
+ into: z.string().min(1),
40
+ include: z.array(z.string().min(1)).optional(),
41
+ mode: z.enum(["preserve", "copy"]).default("preserve")
42
+ });
37
43
  var artifactSchema = z.object({
38
44
  type: artifactTypeSchema,
39
45
  name: z.string().min(1),
@@ -43,7 +49,9 @@ var artifactSchema = z.object({
43
49
  kind: fileKindSchema,
44
50
  hash: z.string().min(16),
45
51
  packageName: z.string().min(1).optional(),
46
- channel: z.enum(["managed", "overlay", "addition", "override", "ejected"]).default("managed")
52
+ channel: z.enum(["managed", "overlay", "addition", "override", "ejected"]).default("managed"),
53
+ assets: z.array(packageAssetSchema).optional(),
54
+ required: z.boolean().optional()
47
55
  });
48
56
 
49
57
  // src/model/adapter.ts
@@ -469,6 +477,7 @@ async function uninstall(plan, options = {}) {
469
477
  }
470
478
  const removable = plan.operations.filter((operation) => operation.action === "remove" || resolvedOptions.force && operation.action === "keep");
471
479
  const kept = resolvedOptions.force ? [] : plan.operations.filter((operation) => operation.action === "keep");
480
+ const skipped = plan.operations.filter((operation) => operation.action === "skip");
472
481
  const removedDrifted = resolvedOptions.force ? plan.operations.filter((operation) => operation.action === "keep").length : 0;
473
482
  if (resolvedOptions.dryRun) return { removed: removable.length, kept: kept.length, removedDrifted };
474
483
  for (const operation of plan.operations) {
@@ -476,7 +485,8 @@ async function uninstall(plan, options = {}) {
476
485
  await rm2(operation.destPath, { recursive: true, force: true });
477
486
  }
478
487
  }
479
- if (kept.length > 0) {
488
+ const preserved = [...kept, ...skipped];
489
+ if (preserved.length > 0) {
480
490
  const now = (/* @__PURE__ */ new Date()).toISOString();
481
491
  await writeInstallManifest({
482
492
  version: 1,
@@ -484,9 +494,9 @@ async function uninstall(plan, options = {}) {
484
494
  targetRoot: plan.targetRoot,
485
495
  generatedAt: now,
486
496
  adapterCode: plan.adapterCode,
487
- entries: kept.map((operation) => {
497
+ entries: preserved.map((operation) => {
488
498
  if (!operation.manifestHash || !operation.desiredHash) {
489
- throw new Error(`Invalid kept operation missing manifest/source hash: ${operation.relativeDestPath}`);
499
+ throw new Error(`Invalid preserved operation missing manifest/source hash: ${operation.relativeDestPath}`);
490
500
  }
491
501
  return {
492
502
  path: operation.relativeDestPath,
@@ -739,11 +749,14 @@ async function createUninstallPlan(manifest) {
739
749
  kind: entry.kind,
740
750
  destPath,
741
751
  relativeDestPath: entry.path,
752
+ desiredHash: entry.sourceHash,
742
753
  currentHash,
743
754
  manifestHash: entry.hash,
744
755
  reason: "uninstall managed artifact",
745
756
  channel: entry.channel,
746
- packageName: entry.packageName
757
+ packageName: entry.packageName,
758
+ semanticCommand: entry.semanticCommand,
759
+ mergeStrategy: entry.mergeStrategy
747
760
  });
748
761
  }
749
762
  }
@@ -804,7 +817,9 @@ import { parse as parse2, printParseErrorCode as printParseErrorCode2 } from "js
804
817
  import { z as z4 } from "zod";
805
818
  var packageProvideSchema = z4.object({
806
819
  type: artifactTypeSchema,
807
- path: z4.string().min(1)
820
+ path: z4.string().min(1),
821
+ assets: z4.array(packageAssetSchema).optional(),
822
+ required: z4.boolean().optional()
808
823
  });
809
824
  var packageManifestSchema = z4.object({
810
825
  schemaVersion: z4.literal(1),
@@ -973,7 +988,7 @@ async function listFromManifest(root, packageName) {
973
988
  const stats = await stat2(full);
974
989
  if (provide.type === "instructions") {
975
990
  if (stats.isFile()) {
976
- artifacts.push(await artifactForFile(provide.type, basename(full), full, provide.path, packageName));
991
+ artifacts.push(await artifactForFile(provide.type, basename(full), full, provide.path, packageName, provide.assets, provide.required));
977
992
  }
978
993
  continue;
979
994
  }
@@ -981,16 +996,16 @@ async function listFromManifest(root, packageName) {
981
996
  for (const entry of await sortedDirEntries(full)) {
982
997
  const child = join6(full, entry.name);
983
998
  if (provide.type === "skills" && entry.isDirectory()) {
984
- artifacts.push(await artifactForDir(provide.type, entry.name, child, join6(provide.path, entry.name), packageName));
999
+ artifacts.push(await artifactForDir(provide.type, entry.name, child, join6(provide.path, entry.name), packageName, provide.assets, provide.required));
985
1000
  } else if (provide.type === "plugins" && entry.isDirectory()) {
986
- artifacts.push(await artifactForDir(provide.type, entry.name, child, join6(provide.path, entry.name), packageName));
1001
+ artifacts.push(await artifactForDir(provide.type, entry.name, child, join6(provide.path, entry.name), packageName, provide.assets, provide.required));
987
1002
  } else if (entry.isFile()) {
988
1003
  const name = provide.type === "rules" && entry.name.endsWith(".md") ? entry.name : entry.name;
989
- artifacts.push(await artifactForFile(provide.type, name, child, join6(provide.path, entry.name), packageName));
1004
+ artifacts.push(await artifactForFile(provide.type, name, child, join6(provide.path, entry.name), packageName, provide.assets, provide.required));
990
1005
  }
991
1006
  }
992
1007
  } else if (stats.isFile()) {
993
- artifacts.push(await artifactForFile(provide.type, basename(full), full, provide.path, packageName));
1008
+ artifacts.push(await artifactForFile(provide.type, basename(full), full, provide.path, packageName, provide.assets, provide.required));
994
1009
  }
995
1010
  }
996
1011
  return artifacts;
@@ -1007,7 +1022,7 @@ async function listGenericArtifacts(type, dir, relativeRoot, packageName) {
1007
1022
  }
1008
1023
  return artifacts;
1009
1024
  }
1010
- async function artifactForFile(type, name, sourcePath, relativePath, packageName) {
1025
+ async function artifactForFile(type, name, sourcePath, relativePath, packageName, assets, required) {
1011
1026
  return {
1012
1027
  type,
1013
1028
  name,
@@ -1016,10 +1031,12 @@ async function artifactForFile(type, name, sourcePath, relativePath, packageName
1016
1031
  kind: "file",
1017
1032
  hash: await hashPath(sourcePath),
1018
1033
  packageName,
1019
- channel: "managed"
1034
+ channel: "managed",
1035
+ assets,
1036
+ required
1020
1037
  };
1021
1038
  }
1022
- async function artifactForDir(type, name, sourcePath, relativePath, packageName) {
1039
+ async function artifactForDir(type, name, sourcePath, relativePath, packageName, assets, required) {
1023
1040
  return {
1024
1041
  type,
1025
1042
  name,
@@ -1028,7 +1045,9 @@ async function artifactForDir(type, name, sourcePath, relativePath, packageName)
1028
1045
  kind: "dir",
1029
1046
  hash: await hashPath(sourcePath),
1030
1047
  packageName,
1031
- channel: "managed"
1048
+ channel: "managed",
1049
+ assets,
1050
+ required
1032
1051
  };
1033
1052
  }
1034
1053
 
@@ -1463,8 +1482,8 @@ function getSourceDriver(name = "local") {
1463
1482
  }
1464
1483
 
1465
1484
  // src/staging/staging.ts
1466
- import { cp as cp3, mkdir as mkdir5, mkdtemp } from "fs/promises";
1467
- import { dirname as dirname5, join as join12 } from "path";
1485
+ import { chmod, cp as cp3, mkdir as mkdir5, mkdtemp, readdir as readdir4, stat as stat6 } from "fs/promises";
1486
+ import { basename as basename7, dirname as dirname5, join as join12, relative as relative2, resolve as resolve8, sep } from "path";
1468
1487
  import { tmpdir as tmpdir2 } from "os";
1469
1488
 
1470
1489
  // src/staging/customize.ts
@@ -1573,6 +1592,46 @@ async function sortedDirEntries2(path) {
1573
1592
  return (await readdir3(path, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
1574
1593
  }
1575
1594
 
1595
+ // src/model/selection.ts
1596
+ function artifactSelectorKey(artifact) {
1597
+ return `${artifact.type}/${artifact.name}`;
1598
+ }
1599
+ function normalizeArtifactSelectors(select, legacySkills) {
1600
+ const selected = [
1601
+ ...select ?? [],
1602
+ ...(legacySkills ?? []).map((name) => `skills/${name}`)
1603
+ ].flatMap(splitSelectorList);
1604
+ if (selected.length === 0) return void 0;
1605
+ return [...new Set(selected.map(parseArtifactSelector))];
1606
+ }
1607
+ function filterArtifactsBySelection(artifacts, selectors, legacySkills) {
1608
+ const selected = normalizeArtifactSelectors(selectors, legacySkills);
1609
+ if (!selected?.length) return artifacts;
1610
+ const selectedSet = new Set(selected);
1611
+ const available = new Set(artifacts.map(artifactSelectorKey));
1612
+ const missing = selected.filter((selector) => !available.has(selector));
1613
+ if (missing.length > 0) {
1614
+ throw new Error(`Selected artifact not found in package: ${missing.join(", ")}`);
1615
+ }
1616
+ return artifacts.filter((artifact) => artifact.required || selectedSet.has(artifactSelectorKey(artifact)));
1617
+ }
1618
+ function splitSelectorList(value) {
1619
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
1620
+ }
1621
+ function parseArtifactSelector(value) {
1622
+ const slash = value.indexOf("/");
1623
+ if (slash <= 0 || slash === value.length - 1) {
1624
+ throw new Error(`Invalid artifact selector: ${value}. Expected <type>/<name>.`);
1625
+ }
1626
+ const type = value.slice(0, slash);
1627
+ const name = value.slice(slash + 1);
1628
+ const parsedType = artifactTypeSchema.safeParse(type);
1629
+ if (!parsedType.success) {
1630
+ throw new Error(`Invalid artifact selector type: ${type}`);
1631
+ }
1632
+ return `${parsedType.data}/${name}`;
1633
+ }
1634
+
1576
1635
  // src/staging/staging.ts
1577
1636
  async function stageSource(driver, source, options = {}) {
1578
1637
  const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(source, options))));
@@ -1583,6 +1642,7 @@ async function stageSource(driver, source, options = {}) {
1583
1642
  const stagedPath = join12(root, artifact.relativePath);
1584
1643
  await mkdir5(dirname5(stagedPath), { recursive: true });
1585
1644
  await cp3(artifact.sourcePath, stagedPath, { recursive: artifact.kind === "dir", dereference: true });
1645
+ await composeAssets(artifact, resolved.resolvedPath, stagedPath);
1586
1646
  stagedArtifacts.push({
1587
1647
  ...artifact,
1588
1648
  stagedPath,
@@ -1590,12 +1650,13 @@ async function stageSource(driver, source, options = {}) {
1590
1650
  channel: artifact.channel ?? "managed"
1591
1651
  });
1592
1652
  }
1593
- const finalArtifacts = options.workspaceRoot && options.adapter ? await applyCustomizations(stagedArtifacts, {
1653
+ const selectedArtifacts = filterArtifactsBySelection(stagedArtifacts, options.select, options.skills);
1654
+ const finalArtifacts = options.workspaceRoot && options.adapter ? await applyCustomizations(selectedArtifacts, {
1594
1655
  workspaceRoot: options.workspaceRoot,
1595
1656
  adapter: options.adapter,
1596
1657
  stageRoot: root,
1597
1658
  packageName: resolved.packageName
1598
- }) : stagedArtifacts;
1659
+ }) : selectedArtifacts;
1599
1660
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
1600
1661
  return {
1601
1662
  root,
@@ -1623,11 +1684,93 @@ async function stageSource(driver, source, options = {}) {
1623
1684
  }
1624
1685
  };
1625
1686
  }
1687
+ async function composeAssets(artifact, packageRoot, stagedPath) {
1688
+ if (!artifact.assets?.length) return;
1689
+ if (artifact.kind !== "dir") {
1690
+ throw new Error(`Asset includes require a directory artifact: ${artifact.type}/${artifact.name}`);
1691
+ }
1692
+ for (const asset of artifact.assets) {
1693
+ const source = resolvePackagePath(packageRoot, asset.from);
1694
+ const dest = join12(stagedPath, asset.into);
1695
+ await copyAsset(asset, source, dest);
1696
+ }
1697
+ }
1698
+ async function copyAsset(asset, source, dest) {
1699
+ const sourceStats = await stat6(source);
1700
+ if (sourceStats.isFile()) {
1701
+ if (matchesAny(basename7(source), asset.include)) {
1702
+ await mkdir5(dest, { recursive: true });
1703
+ await copyAssetFile(source, join12(dest, basename7(source)), asset);
1704
+ }
1705
+ return;
1706
+ }
1707
+ if (!sourceStats.isDirectory()) {
1708
+ throw new Error(`Asset include source is not a file or directory: ${source}`);
1709
+ }
1710
+ if (!asset.include?.length) {
1711
+ await mkdir5(dirname5(dest), { recursive: true });
1712
+ await cp3(source, dest, { recursive: true, dereference: true });
1713
+ if (asset.mode === "copy") await normalizeCopiedModes(dest);
1714
+ return;
1715
+ }
1716
+ for (const file of await listFiles(source)) {
1717
+ const rel = relative2(source, file).replaceAll("\\", "/");
1718
+ if (!matchesAny(rel, asset.include) && !matchesAny(basename7(file), asset.include)) continue;
1719
+ await copyAssetFile(file, join12(dest, rel), asset);
1720
+ }
1721
+ }
1722
+ async function copyAssetFile(source, dest, asset) {
1723
+ await mkdir5(dirname5(dest), { recursive: true });
1724
+ await cp3(source, dest, { dereference: true });
1725
+ if (asset.mode === "copy") await chmod(dest, 420);
1726
+ }
1727
+ function resolvePackagePath(packageRoot, path) {
1728
+ const resolved = resolve8(packageRoot, path);
1729
+ const root = resolve8(packageRoot);
1730
+ if (resolved !== root && !resolved.startsWith(`${root}${sep}`)) {
1731
+ throw new Error(`Asset include escapes package root: ${path}`);
1732
+ }
1733
+ return resolved;
1734
+ }
1735
+ async function listFiles(root) {
1736
+ const out = [];
1737
+ async function walk2(dir) {
1738
+ for (const entry of (await readdir4(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
1739
+ const full = join12(dir, entry.name);
1740
+ if (entry.isDirectory()) {
1741
+ await walk2(full);
1742
+ } else if (entry.isFile()) {
1743
+ out.push(full);
1744
+ }
1745
+ }
1746
+ }
1747
+ await walk2(root);
1748
+ return out;
1749
+ }
1750
+ async function normalizeCopiedModes(path) {
1751
+ const stats = await stat6(path);
1752
+ if (stats.isFile()) {
1753
+ await chmod(path, 420);
1754
+ return;
1755
+ }
1756
+ if (!stats.isDirectory()) return;
1757
+ for (const entry of await readdir4(path, { withFileTypes: true })) {
1758
+ await normalizeCopiedModes(join12(path, entry.name));
1759
+ }
1760
+ }
1761
+ function matchesAny(path, patterns) {
1762
+ if (!patterns?.length) return true;
1763
+ return patterns.some((pattern) => matchesGlob(path, pattern));
1764
+ }
1765
+ function matchesGlob(path, pattern) {
1766
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1767
+ return new RegExp(`^${escaped}$`).test(path);
1768
+ }
1626
1769
 
1627
1770
  // src/model/workspace.ts
1628
1771
  import { readFile as readFile8 } from "fs/promises";
1629
1772
  import { homedir as homedir3 } from "os";
1630
- import { dirname as dirname6, join as join13, resolve as resolve8 } from "path";
1773
+ import { dirname as dirname6, join as join13, resolve as resolve9 } from "path";
1631
1774
  import { z as z5 } from "zod";
1632
1775
  var workspacePackageSchema = z5.object({
1633
1776
  name: z5.string().min(1),
@@ -1638,7 +1781,9 @@ var workspacePackageSchema = z5.object({
1638
1781
  adapterModule: z5.string().min(1).optional(),
1639
1782
  adapterCodeHash: z5.string().min(16).optional(),
1640
1783
  mode: z5.enum(["pinned", "tracking"]).default("pinned"),
1641
- requestedRef: z5.string().min(1).optional()
1784
+ requestedRef: z5.string().min(1).optional(),
1785
+ select: z5.array(z5.string().min(1)).optional(),
1786
+ skills: z5.array(z5.string().min(1)).optional()
1642
1787
  });
1643
1788
  var workspaceProfileRuntimeSchema = z5.object({
1644
1789
  agent: z5.string().min(1).optional(),
@@ -1687,11 +1832,11 @@ function globalWorkspaceConfigPath(globalRoot = homedir3()) {
1687
1832
  return join13(globalRoot, ".agentwheel", "config.json");
1688
1833
  }
1689
1834
  async function findWorkspaceRoot(start = process.cwd()) {
1690
- let current = resolve8(start);
1835
+ let current = resolve9(start);
1691
1836
  while (true) {
1692
1837
  if (await pathExists(workspaceConfigPath(current))) return current;
1693
1838
  const parent = dirname6(current);
1694
- if (parent === current) return resolve8(start);
1839
+ if (parent === current) return resolve9(start);
1695
1840
  current = parent;
1696
1841
  }
1697
1842
  }
@@ -1715,9 +1860,9 @@ function mergeWorkspaceConfig(global, project) {
1715
1860
  });
1716
1861
  }
1717
1862
  function resolveConfigPath(path, baseRoot) {
1718
- if (path.startsWith("~/")) return resolve8(homedir3(), path.slice(2));
1863
+ if (path.startsWith("~/")) return resolve9(homedir3(), path.slice(2));
1719
1864
  if (path === "~") return homedir3();
1720
- return path.startsWith("/") ? resolve8(path) : resolve8(baseRoot, path);
1865
+ return path.startsWith("/") ? resolve9(path) : resolve9(baseRoot, path);
1721
1866
  }
1722
1867
  function emptyWorkspaceConfig() {
1723
1868
  return { schemaVersion: 1, packages: [], registry: {}, profiles: {}, agents: {} };
@@ -1781,9 +1926,9 @@ import { rm as rm7 } from "fs/promises";
1781
1926
  import { join as join16 } from "path";
1782
1927
 
1783
1928
  // src/registry/client.ts
1784
- import { readFile as readFile9, rm as rm6, stat as stat6 } from "fs/promises";
1929
+ import { readFile as readFile9, rm as rm6, stat as stat7 } from "fs/promises";
1785
1930
  import { homedir as homedir4 } from "os";
1786
- import { dirname as dirname8, join as join15, resolve as resolve9 } from "path";
1931
+ import { dirname as dirname8, join as join15, resolve as resolve10 } from "path";
1787
1932
  import { fileURLToPath } from "url";
1788
1933
 
1789
1934
  // src/model/registry.ts
@@ -1886,8 +2031,8 @@ var RegistryClient = class {
1886
2031
  }
1887
2032
  const filePath = source.startsWith("file:") ? fileURLToPath(source) : source;
1888
2033
  if (await pathExists(filePath)) {
1889
- const fullPath = resolve9(filePath);
1890
- const stats = await stat6(fullPath);
2034
+ const fullPath = resolve10(filePath);
2035
+ const stats = await stat7(fullPath);
1891
2036
  return readFile9(stats.isDirectory() ? join15(fullPath, "index.json") : fullPath, "utf8");
1892
2037
  }
1893
2038
  const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join15(dirname8(this.cachePath), "registry-repos") }));
@@ -1947,7 +2092,9 @@ async function syncProfile(options) {
1947
2092
  workspaceRoot: options.workspaceRoot,
1948
2093
  adapter,
1949
2094
  cacheRoot: join16(options.workspaceRoot, ".agentwheel", "cache"),
1950
- mode: options.mode ?? pkg.mode
2095
+ mode: options.mode ?? pkg.mode,
2096
+ select: options.select ?? pkg.select,
2097
+ skills: options.select ? void 0 : pkg.skills
1951
2098
  });
1952
2099
  try {
1953
2100
  const plan = await createInstallPlan(bundle, adapter, target.targetRoot, await readInstallManifest(target.targetRoot, adapter.name));
@@ -1981,7 +2128,9 @@ async function packageFromSource(source, options) {
1981
2128
  source: resolved.source,
1982
2129
  driver,
1983
2130
  adapter: "openclaw",
1984
- mode: options.mode ?? "pinned"
2131
+ mode: options.mode ?? "pinned",
2132
+ select: options.select,
2133
+ skills: options.skills
1985
2134
  };
1986
2135
  }
1987
2136
 
@@ -1996,7 +2145,9 @@ async function createSourcePlan(options) {
1996
2145
  workspaceRoot,
1997
2146
  adapter: options.adapter,
1998
2147
  cacheRoot: join17(workspaceRoot, ".agentwheel", "cache"),
1999
- mode: options.mode
2148
+ mode: options.mode,
2149
+ select: options.select,
2150
+ skills: options.skills
2000
2151
  });
2001
2152
  const manifest = await readInstallManifest(options.targetRoot, options.adapter.name);
2002
2153
  const plan = await createInstallPlan(bundle, options.adapter, options.targetRoot, manifest);
@@ -2021,7 +2172,7 @@ function shouldUpdatePackage(pkg, lock) {
2021
2172
  }
2022
2173
 
2023
2174
  // src/runtime/target.ts
2024
- import { basename as basename7, dirname as dirname9, join as join18, resolve as resolve10 } from "path";
2175
+ import { basename as basename8, dirname as dirname9, join as join18, resolve as resolve11 } from "path";
2025
2176
  var runtimeMarkers = [
2026
2177
  { adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
2027
2178
  { adapter: "claude", dirs: [".claude"] },
@@ -2030,9 +2181,9 @@ var runtimeMarkers = [
2030
2181
  { adapter: "copilot", dirs: [".github"] }
2031
2182
  ];
2032
2183
  async function resolveRuntimeTarget(request = {}) {
2033
- const cwd = resolve10(request.cwd ?? process.cwd());
2184
+ const cwd = resolve11(request.cwd ?? process.cwd());
2034
2185
  if (request.targetRoot) {
2035
- const targetRoot = resolve10(request.targetRoot);
2186
+ const targetRoot = resolve11(request.targetRoot);
2036
2187
  return {
2037
2188
  adapter: request.adapter ?? "openclaw",
2038
2189
  targetRoot,
@@ -2059,7 +2210,7 @@ async function resolveRuntimeTarget(request = {}) {
2059
2210
  async function resolveAllRuntimeTargets(request = {}) {
2060
2211
  if (request.targetRoot) return [await resolveRuntimeTarget(request)];
2061
2212
  if (request.agent) return [await resolveRuntimeTarget(request)];
2062
- const cwd = resolve10(request.cwd ?? process.cwd());
2213
+ const cwd = resolve11(request.cwd ?? process.cwd());
2063
2214
  const workspaceRoot = await findWorkspaceRoot(cwd);
2064
2215
  const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
2065
2216
  const targets = Object.entries(config.agents).map(([name]) => targetFromAgent(name, config, workspaceRoot));
@@ -2069,12 +2220,12 @@ async function resolveAllRuntimeTargets(request = {}) {
2069
2220
  return targets;
2070
2221
  }
2071
2222
  async function detectRuntimeTarget(cwd = process.cwd(), adapterFilter) {
2072
- const root = resolve10(cwd);
2223
+ const root = resolve11(cwd);
2073
2224
  const matches = [];
2074
2225
  for (const marker of runtimeMarkers) {
2075
2226
  if (adapterFilter && marker.adapter !== adapterFilter) continue;
2076
2227
  for (const dir of marker.dirs) {
2077
- if (basename7(root) === dir) {
2228
+ if (basename8(root) === dir) {
2078
2229
  matches.push({ adapter: marker.adapter, targetRoot: dirname9(root) });
2079
2230
  } else if (await pathExists(join18(root, dir))) {
2080
2231
  matches.push({ adapter: marker.adapter, targetRoot: root });
@@ -2108,9 +2259,86 @@ function dedupeTargets(matches) {
2108
2259
  return [...byKey.values()];
2109
2260
  }
2110
2261
 
2262
+ // src/cli/update-check.ts
2263
+ import { mkdir as mkdir7, readFile as readFile10, writeFile as writeFile4 } from "fs/promises";
2264
+ import { homedir as homedir5 } from "os";
2265
+ import { dirname as dirname10, join as join19 } from "path";
2266
+ var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
2267
+ var DEFAULT_TIMEOUT_MS = 300;
2268
+ var REGISTRY_URL = "https://registry.npmjs.org/agentwheel";
2269
+ async function maybeCheckForUpdate(options) {
2270
+ if (isDisabled(options)) return;
2271
+ const now = options.now?.() ?? /* @__PURE__ */ new Date();
2272
+ const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
2273
+ const cachePath = options.cachePath ?? join19(homedir5(), ".agentwheel", "update-check.json");
2274
+ try {
2275
+ const cached = await readCache(cachePath);
2276
+ if (cached && now.getTime() - Date.parse(cached.checkedAt) < ttlMs) {
2277
+ warnIfNewer(cached.latest, options.currentVersion, options.stderr);
2278
+ return;
2279
+ }
2280
+ const latest = await fetchLatestVersion(options.fetchImpl ?? fetch, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
2281
+ if (!latest) return;
2282
+ await writeCache(cachePath, { checkedAt: now.toISOString(), latest });
2283
+ warnIfNewer(latest, options.currentVersion, options.stderr);
2284
+ } catch {
2285
+ }
2286
+ }
2287
+ function isDisabled(options) {
2288
+ const env = options.env ?? process.env;
2289
+ if (env.AGENTWHEEL_NO_UPDATE_CHECK === "1" || env.AGENTWHEEL_NO_UPDATE_CHECK === "true") return true;
2290
+ if (env.CI) return true;
2291
+ if (options.argv?.includes("--no-update-check")) return true;
2292
+ const isTTY = options.isTTY ?? process.stderr.isTTY === true;
2293
+ return !isTTY;
2294
+ }
2295
+ async function fetchLatestVersion(fetchImpl, timeoutMs) {
2296
+ const controller = new AbortController();
2297
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
2298
+ try {
2299
+ const response = await fetchImpl(REGISTRY_URL, { signal: controller.signal });
2300
+ if (!response.ok) return void 0;
2301
+ const body = await response.json();
2302
+ return typeof body["dist-tags"]?.latest === "string" ? body["dist-tags"].latest : void 0;
2303
+ } finally {
2304
+ clearTimeout(timeout);
2305
+ }
2306
+ }
2307
+ async function readCache(path) {
2308
+ try {
2309
+ const parsed = JSON.parse(await readFile10(path, "utf8"));
2310
+ if (typeof parsed.checkedAt !== "string" || typeof parsed.latest !== "string") return void 0;
2311
+ return { checkedAt: parsed.checkedAt, latest: parsed.latest };
2312
+ } catch {
2313
+ return void 0;
2314
+ }
2315
+ }
2316
+ async function writeCache(path, cache) {
2317
+ await mkdir7(dirname10(path), { recursive: true });
2318
+ await writeFile4(path, `${JSON.stringify(cache, null, 2)}
2319
+ `, "utf8");
2320
+ }
2321
+ function warnIfNewer(latest, current, stderr = process.stderr) {
2322
+ if (compareVersions(latest, current) <= 0) return;
2323
+ stderr.write(`agentwheel ${latest} is available (you have ${current}). Update: npm i -g agentwheel
2324
+ `);
2325
+ }
2326
+ function compareVersions(a, b) {
2327
+ const left = normalizeVersion(a);
2328
+ const right = normalizeVersion(b);
2329
+ for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
2330
+ const diff = (left[index] ?? 0) - (right[index] ?? 0);
2331
+ if (diff !== 0) return diff > 0 ? 1 : -1;
2332
+ }
2333
+ return 0;
2334
+ }
2335
+ function normalizeVersion(version) {
2336
+ return version.replace(/^v/, "").split("-", 1)[0].split(".").map((part) => Number.parseInt(part, 10)).map((part) => Number.isFinite(part) ? part : 0);
2337
+ }
2338
+
2111
2339
  // src/cli/index.ts
2112
2340
  var program = new Command();
2113
- program.name("agentwheel").description("Multi-runtime agent artifact orchestrator").version("0.4.1");
2341
+ program.name("agentwheel").description("Multi-runtime agent artifact orchestrator").version("0.6.0").option("--no-update-check", "disable npm version update check", false);
2114
2342
  program.command("init").argument("[kind]", "workspace or package", "workspace").option("--target-root <path>", "workspace root", process.cwd()).action(async (kind, options) => {
2115
2343
  const root = normalizeTargetRoot(options.targetRoot);
2116
2344
  if (kind === "package") {
@@ -2124,8 +2352,9 @@ program.command("init").argument("[kind]", "workspace or package", "workspace").
2124
2352
  await writeWorkspaceConfig(root, await readWorkspaceConfig(root));
2125
2353
  console.log("Initialized .agentwheel/config.json.");
2126
2354
  });
2127
- program.command("add").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").action(async (source, options) => {
2355
+ program.command("add").argument("<source>", "package source").option("--driver <driver>", "source driver (local, git, skillkit, or vercel-skills)").option("--adapter <adapter>", "built-in adapter", "openclaw").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--target-root <path>", "workspace root", process.cwd()).option("--mode <mode>", "pinned or tracking", "pinned").option("--name <name>", "package alias").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).action(async (source, options) => {
2128
2356
  const targetRoot = normalizeTargetRoot(options.targetRoot);
2357
+ const selectedArtifacts = selectedArtifactsFromOptions(options);
2129
2358
  const resolvedInput = await resolvePackageSource(source, targetRoot);
2130
2359
  const resolvedSource = resolvedInput.source;
2131
2360
  const driverName = options.driver ?? inferSourceDriverName(resolvedSource);
@@ -2141,8 +2370,9 @@ program.command("add").argument("<source>", "package source").option("--driver <
2141
2370
  const bundle = await stageSource(driver, resolvedSource, {
2142
2371
  workspaceRoot: targetRoot,
2143
2372
  adapter,
2144
- cacheRoot: join19(targetRoot, ".agentwheel", "cache"),
2145
- mode: options.mode
2373
+ cacheRoot: join20(targetRoot, ".agentwheel", "cache"),
2374
+ mode: options.mode,
2375
+ select: selectedArtifacts
2146
2376
  });
2147
2377
  const name = options.name ?? resolvedInput.registryEntry?.name ?? bundle.source.packageName ?? source;
2148
2378
  const entry = {
@@ -2154,18 +2384,20 @@ program.command("add").argument("<source>", "package source").option("--driver <
2154
2384
  adapterModule: options.adapterModule,
2155
2385
  adapterCodeHash: adapter.programmatic?.hash,
2156
2386
  mode: options.mode,
2157
- requestedRef: bundle.source.requestedRef
2387
+ requestedRef: bundle.source.requestedRef,
2388
+ select: selectedArtifacts
2158
2389
  };
2159
2390
  await writeWorkspaceConfig(targetRoot, upsertPackage(await readWorkspaceConfig(targetRoot), entry));
2160
2391
  await rm8(bundle.root, { recursive: true, force: true });
2161
2392
  console.log(`Added ${name}.`);
2162
2393
  });
2163
- program.command("list").argument("<source>", "package source").option("--driver <driver>", "source driver").option("--target-root <path>", "workspace root", process.cwd()).action(async (source, options) => {
2394
+ program.command("list").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) => {
2164
2395
  const targetRoot = normalizeTargetRoot(options.targetRoot);
2396
+ const selectedArtifacts = selectedArtifactsFromOptions(options);
2165
2397
  const resolvedInput = await resolvePackageSource(source, targetRoot);
2166
2398
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
2167
- const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join19(targetRoot, ".agentwheel", "cache") }))));
2168
- const artifacts = await driver.list(resolved);
2399
+ const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join20(targetRoot, ".agentwheel", "cache") }))));
2400
+ const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
2169
2401
  for (const artifact of artifacts) {
2170
2402
  console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
2171
2403
  }
@@ -2174,7 +2406,7 @@ program.command("scan").argument("<source>", "package source").option("--driver
2174
2406
  const targetRoot = normalizeTargetRoot(options.targetRoot);
2175
2407
  const resolvedInput = await resolvePackageSource(source, targetRoot);
2176
2408
  const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
2177
- const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join19(targetRoot, ".agentwheel", "cache") }))));
2409
+ const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join20(targetRoot, ".agentwheel", "cache") }))));
2178
2410
  const result = await driver.scan(resolved);
2179
2411
  if (result.findings.length === 0) {
2180
2412
  console.log("Scan ok: no findings");
@@ -2185,7 +2417,7 @@ program.command("scan").argument("<source>", "package source").option("--driver
2185
2417
  }
2186
2418
  if (!result.ok) process.exitCode = 1;
2187
2419
  });
2188
- program.command("plan").argument("<source>", "source directory").option("--driver <driver>", "source driver").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>", "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("--dry-run", "accepted for symmetry; plan never writes", false).action(async (source, options) => {
2420
+ program.command("plan").argument("<source>", "source directory").option("--driver <driver>", "source driver").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>", "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("--dry-run", "accepted for symmetry; plan never writes", false).action(async (source, options) => {
2189
2421
  const targets = await resolveCliTargets(options);
2190
2422
  for (const target of targets) {
2191
2423
  const { plan, bundle } = await buildPlan(source, target, options);
@@ -2194,7 +2426,7 @@ program.command("plan").argument("<source>", "source directory").option("--drive
2194
2426
  if (plan.hasBlockingChanges) process.exitCode = 1;
2195
2427
  }
2196
2428
  });
2197
- program.command("sync").argument("[source]", "source directory").option("--driver <driver>", "source driver").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>", "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("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--execute-plugins", "execute semantic plugin installs", false).action(async (source, options) => {
2429
+ program.command("sync").argument("[source]", "source directory").option("--driver <driver>", "source driver").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>", "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("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--execute-plugins", "execute semantic plugin installs", false).action(async (source, options) => {
2198
2430
  if (options.profile) {
2199
2431
  const target = await resolveRuntimeTarget({ targetRoot: options.targetRoot, adapter: options.adapter, agent: options.agent });
2200
2432
  const results = await syncProfile({
@@ -2203,6 +2435,7 @@ program.command("sync").argument("[source]", "source directory").option("--drive
2203
2435
  source,
2204
2436
  driver: options.driver,
2205
2437
  mode: options.mode,
2438
+ select: selectedArtifactsFromOptions(options),
2206
2439
  dryRun: options.dryRun,
2207
2440
  executePlugins: options.executePlugins,
2208
2441
  allowAdapterCode: options.allowAdapterCode,
@@ -2234,7 +2467,7 @@ program.command("sync").argument("[source]", "source directory").option("--drive
2234
2467
  if (plan.hasBlockingChanges) process.exitCode = 1;
2235
2468
  }
2236
2469
  });
2237
- program.command("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).action(async (options) => {
2470
+ program.command("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, []).action(async (options) => {
2238
2471
  const targets = await resolveCliTargets(options);
2239
2472
  for (const target of targets) {
2240
2473
  await runConfiguredPackages(target, options, { useUpdateDecision: true });
@@ -2267,7 +2500,7 @@ program.command("eject").argument("<item>", "package/type/name").option("--targe
2267
2500
  const result = await ejectArtifact(targetRoot, item);
2268
2501
  console.log(`Ejected ${item} to ${result.ejectedPath}.`);
2269
2502
  });
2270
- program.command("uninstall").option("--adapter <adapter>", "adapter", "openclaw").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).action(async (options) => {
2503
+ program.command("uninstall").option("--adapter <adapter>", "adapter", "openclaw").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("--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, []).action(async (options) => {
2271
2504
  const targets = await resolveCliTargets(options);
2272
2505
  for (const target of targets) {
2273
2506
  const adapter = await resolveAdapterForTarget(target, options);
@@ -2276,7 +2509,7 @@ program.command("uninstall").option("--adapter <adapter>", "adapter", "openclaw"
2276
2509
  console.log(`No install manifest for ${adapter.name} at ${target.targetRoot}`);
2277
2510
  continue;
2278
2511
  }
2279
- const plan = await createUninstallPlan(manifest);
2512
+ const plan = filterUninstallPlanBySelection(await createUninstallPlan(manifest), selectedArtifactsFromOptions(options));
2280
2513
  console.log(formatPlan(plan));
2281
2514
  const result = await uninstall(plan, { dryRun: options.dryRun, force: options.force });
2282
2515
  if (!options.dryRun) {
@@ -2294,7 +2527,9 @@ async function buildPlan(source, target, options) {
2294
2527
  workspaceRoot: target.workspaceRoot,
2295
2528
  adapter,
2296
2529
  driver: options.driver,
2297
- mode: options.mode
2530
+ mode: options.mode,
2531
+ select: options.select ?? selectedArtifactsFromOptions(options),
2532
+ skills: options.skills
2298
2533
  });
2299
2534
  return { plan: result.plan, bundle: result.bundle };
2300
2535
  }
@@ -2320,6 +2555,7 @@ async function runConfiguredPackages(target, options, behavior) {
2320
2555
  console.log(`No packages configured at ${target.workspaceRoot}.`);
2321
2556
  return;
2322
2557
  }
2558
+ const selectedArtifacts = selectedArtifactsFromOptions(options);
2323
2559
  for (const pkg of config.packages) {
2324
2560
  const targetForPackage = options.adapter || target.source !== "cwd" ? target : { ...target, adapter: pkg.adapter };
2325
2561
  const adapter = await resolveAdapterForTarget(targetForPackage, {
@@ -2340,7 +2576,9 @@ async function runConfiguredPackages(target, options, behavior) {
2340
2576
  adapterConfig: options.adapterConfig ?? pkg.adapterConfig,
2341
2577
  adapterModule: options.adapterModule ?? pkg.adapterModule,
2342
2578
  allowAdapterCode: options.allowAdapterCode,
2343
- mode: pkg.mode
2579
+ mode: pkg.mode,
2580
+ select: selectedArtifacts ?? pkg.select,
2581
+ skills: selectedArtifacts ? void 0 : pkg.skills
2344
2582
  });
2345
2583
  console.log(`${behavior.useUpdateDecision ? "Update" : "Sync"} ${pkg.name} (${adapter.name} at ${targetForPackage.targetRoot}):`);
2346
2584
  console.log(formatPlan(plan));
@@ -2352,11 +2590,44 @@ async function runConfiguredPackages(target, options, behavior) {
2352
2590
  if (plan.hasBlockingChanges) process.exitCode = 1;
2353
2591
  }
2354
2592
  }
2593
+ function collectSelectOption(value, previous) {
2594
+ return [...previous, ...splitSelectorList(value)];
2595
+ }
2596
+ function collectSkillOption(value, previous) {
2597
+ return [...previous, ...splitSelectorList(value)];
2598
+ }
2599
+ function selectedArtifactsFromOptions(options) {
2600
+ return normalizeArtifactSelectors(options.select, options.skills ?? options.skill);
2601
+ }
2602
+ function filterUninstallPlanBySelection(plan, selected) {
2603
+ if (!selected?.length) return plan;
2604
+ const requested = normalizeArtifactSelectors(selected) ?? [];
2605
+ const available = new Set(plan.operations.map((operation) => `${operation.artifactType}/${operation.artifactName}`));
2606
+ const missing = requested.filter((selector) => !available.has(selector));
2607
+ if (missing.length > 0) {
2608
+ throw new Error(`Selected artifact not found in install manifest: ${missing.join(", ")}`);
2609
+ }
2610
+ const selectedSet = new Set(requested);
2611
+ const operations = plan.operations.map((operation) => {
2612
+ if (selectedSet.has(`${operation.artifactType}/${operation.artifactName}`)) return operation;
2613
+ return {
2614
+ ...operation,
2615
+ action: "skip",
2616
+ desiredHash: operation.desiredHash ?? operation.manifestHash,
2617
+ reason: "not selected for uninstall"
2618
+ };
2619
+ });
2620
+ return {
2621
+ ...plan,
2622
+ operations,
2623
+ hasBlockingChanges: operations.some((operation) => operation.action === "conflict")
2624
+ };
2625
+ }
2355
2626
  async function initPackage(root) {
2356
- await mkdir7(join19(root, "instructions"), { recursive: true });
2357
- await mkdir7(join19(root, "rules"), { recursive: true });
2358
- await mkdir7(join19(root, "skills"), { recursive: true });
2359
- const manifestPath = join19(root, "agentwheel.json");
2627
+ await mkdir8(join20(root, "instructions"), { recursive: true });
2628
+ await mkdir8(join20(root, "rules"), { recursive: true });
2629
+ await mkdir8(join20(root, "skills"), { recursive: true });
2630
+ const manifestPath = join20(root, "agentwheel.json");
2360
2631
  const manifest = {
2361
2632
  schemaVersion: 1,
2362
2633
  name: "example/agentwheel-package",
@@ -2367,9 +2638,9 @@ async function initPackage(root) {
2367
2638
  { type: "skills", path: "skills" }
2368
2639
  ]
2369
2640
  };
2370
- await writeFile4(manifestPath, `${JSON.stringify(manifest, null, 2)}
2641
+ await writeFile5(manifestPath, `${JSON.stringify(manifest, null, 2)}
2371
2642
  `, "utf8");
2372
- await writeFile4(join19(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
2643
+ await writeFile5(join20(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
2373
2644
  }
2374
2645
  function formatUninstallResult(result) {
2375
2646
  const removedLabel = result.removed === 1 ? "managed file" : "managed files";
@@ -2387,7 +2658,16 @@ function printRegistryEntries(entries) {
2387
2658
  console.log(`${entry.name} ${entry.type} ${entry.source} ${entry.description}${tags}`);
2388
2659
  }
2389
2660
  }
2390
- program.parseAsync().catch((error) => {
2661
+ async function main() {
2662
+ await maybeCheckForUpdate({
2663
+ currentVersion: "0.6.0",
2664
+ argv: process.argv,
2665
+ env: process.env,
2666
+ isTTY: process.stderr.isTTY === true
2667
+ });
2668
+ await program.parseAsync();
2669
+ }
2670
+ main().catch((error) => {
2391
2671
  console.error(error instanceof Error ? error.message : String(error));
2392
2672
  process.exitCode = 1;
2393
2673
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentwheel",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "Weave skills, rules, and instructions across every AI agent.",
5
5
  "type": "module",
6
6
  "license": "MIT",