agentwheel 0.16.5 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -106,11 +106,14 @@ Fragments are Agentwheel composition inputs, not runtime file-drop targets.
106
106
  | `agentwheel install` | Reconcile configured packages into the current target or selected fleet. Uses the graph lock as input by default. |
107
107
  | `agentwheel install <name-or-source>` | Ensure semantics: configured name/source scopes the install; a new source is added and installed. |
108
108
  | `agentwheel update [name]` | Re-resolve tracking packages, then apply. A configured name preserves artifacts owned by other roots; `--dependency <name-or-source>` moves one tracking dependency while unrelated graph nodes stay locked. |
109
+ | `agentwheel skill update <name>` | Resolve the owning configured package and reconcile only its closure; pinned owners install current source, tracking owners re-resolve. |
109
110
  | `agentwheel uninstall <name-or-source>` | Remove a configured package from runtimes and config. |
110
111
  | `agentwheel uninstall <name> --keep-files` | Remove from config/manifest while leaving runtime files unmanaged. |
111
112
  | `agentwheel status` | Show configured packages, manifest/lock presence, and install state. Use `--profile <name>` for profile-managed fleets; `status --all` uses profile `all` when present. |
112
113
  | `agentwheel ownership handoff <type/name>` | Transfer one manifest entry to a different workspace root after exact owner, hash, and revision checks; runtime content is not rewritten. |
114
+ | `agentwheel mcp retire <package>` | Preview removal of one exact legacy MCP contribution under an explicitly selected install-state key; add `--apply` only after review. |
113
115
  | `agentwheel doctor` | Check runtime setup and suggest explicit companion/selected skill install commands when they are missing. |
116
+ | `agentwheel cache prune` | Preview old Git source snapshots; add `--apply` to remove them while retaining locked commits. |
114
117
 
115
118
  Mental model: **`install` = make what is declared true. `update` = move tracking declarations forward,
116
119
  then make them true.**
@@ -133,6 +136,22 @@ Apply the same command with the reviewed values added as `--expected-hash <sha25
133
136
  journal, rechecks the old owner and the live artifact hash, then atomically updates only the install
134
137
  manifest. Run one target at a time; named SSH agents use the same contract through `--agent`.
135
138
 
139
+ For a one-time MCP rename, keep the legacy artifact in a dedicated cutover workspace rather than
140
+ the canonical desired-state profile. Set the legacy manifest `stateKey` on its named agent, then
141
+ preview one runtime at a time:
142
+
143
+ ```bash
144
+ agentwheel mcp retire legacy-mcp --agent legacy-codex \
145
+ --from-workspace-root /old/cutover/workspace --dry-run
146
+ agentwheel mcp retire legacy-mcp --agent legacy-claude --dry-run
147
+ ```
148
+
149
+ The package must render exactly one MCP artifact. The command accepts only an exact JSON MCP server
150
+ object or exact Codex TOML MCP sections, rejects unexpected manifest entries or owners, and
151
+ revalidates the exact contribution under the apply lock. It removes only the selected legacy
152
+ server; sibling servers and user configuration remain. The command previews by default. A later
153
+ runtime approval must use the same command with `--apply` instead of `--dry-run`.
154
+
136
155
  For a surgical dependency update, start with a dry-run:
137
156
 
138
157
  ```bash
@@ -84,6 +84,31 @@ async function writeJsonAtomic(path, data) {
84
84
  `, "utf8");
85
85
  await rename(temp, path);
86
86
  }
87
+ async function withFilesystemLock(lockPath, timeoutMs, fn, description = "cache") {
88
+ await mkdir(dirname(lockPath), { recursive: true });
89
+ const started = Date.now();
90
+ while (true) {
91
+ try {
92
+ await mkdir(lockPath);
93
+ await writeFile(join(lockPath, "owner.json"), JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
94
+ break;
95
+ } catch (error) {
96
+ if (!isAlreadyExists(error)) throw error;
97
+ if (Date.now() - started > timeoutMs) {
98
+ throw new Error(`Timed out waiting for ${description} lock at ${lockPath}`);
99
+ }
100
+ await new Promise((resolve2) => setTimeout(resolve2, 50));
101
+ }
102
+ }
103
+ try {
104
+ return await fn();
105
+ } finally {
106
+ await rm(lockPath, { recursive: true, force: true });
107
+ }
108
+ }
109
+ function isAlreadyExists(error) {
110
+ return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
111
+ }
87
112
 
88
113
  // src/source/identify.ts
89
114
  function inferSourceDriverName(source) {
@@ -114,6 +139,8 @@ export {
114
139
  isIgnoredGeneratedEntry,
115
140
  atomicCopy,
116
141
  writeJsonAtomic,
142
+ withFilesystemLock,
143
+ isAlreadyExists,
117
144
  inferSourceDriverName,
118
145
  isExplicitSource
119
146
  };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  inferSourceDriverName,
4
4
  isExplicitSource
5
- } from "./chunk-PKAPR55N.js";
5
+ } from "./chunk-24IUHC3D.js";
6
6
  export {
7
7
  inferSourceDriverName,
8
8
  isExplicitSource