@event4u/agent-config 2.4.1 → 2.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.
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Shared agent configuration \u2014 skills for AI coding tools (Claude Code, Augment, Cursor, Cline, Windsurf, Gemini CLI).",
9
- "version": "2.4.1",
9
+ "version": "2.6.0",
10
10
  "keywords": [
11
11
  "agent-config",
12
12
  "skills",
package/CHANGELOG.md CHANGED
@@ -343,6 +343,26 @@ our recommendation order, not its support status.
343
343
  users" tension without removing any path that an existing user
344
344
  might rely on.
345
345
 
346
+ ## [2.6.0](https://github.com/event4u-app/agent-config/compare/2.4.1...2.6.0) (2026-05-13)
347
+
348
+ ### Features
349
+
350
+ * **claude-desktop:** bundle commands as desktop skills ([50cd319](https://github.com/event4u-app/agent-config/commit/50cd31988ac571ecdf883c5d2c6d62c209820ebc))
351
+
352
+ ### Other
353
+
354
+ * 2.5.0 ([1da0e10](https://github.com/event4u-app/agent-config/commit/1da0e1014b9cf24cf41d820795f96f2668ce738c))
355
+
356
+ Tests: 3530 (+9 since 2.4.1)
357
+
358
+ ## [2.5.0](https://github.com/event4u-app/agent-config/compare/2.4.1...2.5.0) (2026-05-13)
359
+
360
+ ### Features
361
+
362
+ * **claude-desktop:** bundle commands as desktop skills ([50cd319](https://github.com/event4u-app/agent-config/commit/50cd31988ac571ecdf883c5d2c6d62c209820ebc))
363
+
364
+ Tests: 3530 (+9 since 2.4.1)
365
+
346
366
  ## [2.4.1](https://github.com/event4u-app/agent-config/compare/2.4.0...2.4.1) (2026-05-13)
347
367
 
348
368
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@event4u/agent-config",
3
- "version": "2.4.1",
3
+ "version": "2.6.0",
4
4
  "description": "Shared agent configuration \u2014 skills, rules, commands, guidelines, and templates for AI coding tools",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -3,20 +3,32 @@
3
3
  Claude Desktop has no filesystem convention for skills; the Customize →
4
4
  Skills UI accepts a ZIP per skill via the Upload button. This module
5
5
  walks ``<package_root>/.agent-src/skills/*`` and produces one
6
- ``<skill-name>.zip`` per directory into ``dest_dir``.
6
+ ``<skill-name>.zip`` per directory into ``dest_dir``. It additionally
7
+ walks ``<package_root>/.agent-src/commands/`` and produces one
8
+ ``<command-slug>.zip`` per command ``.md`` file so Claude Desktop sees
9
+ the same surface that Claude Code exposes via the ``.claude/skills/``
10
+ symlink wrapper layer.
7
11
 
8
12
  Contract:
9
13
 
10
14
  - Each ZIP contains ``SKILL.md`` plus every sibling file under the same
11
15
  directory (recursive). Symlinks are dereferenced so the ZIP is
12
16
  self-contained.
17
+ - Command bundles wrap a single ``.agent-src/commands/<path>.md`` file
18
+ as ``SKILL.md`` inside the ZIP. Nested commands flatten to
19
+ ``<cluster>-<leaf>`` slugs (e.g. ``council/default.md`` →
20
+ ``council-default.zip``) to mirror ``compress.py``.
13
21
  - Exclusions: ``.git*``, ``__pycache__``, ``*.pyc`` — matched on the
14
22
  basename of any path component.
15
23
  - A skill folder without a ``SKILL.md`` is skipped (defensive: avoids
16
24
  shipping Claude-Code orchestrator stubs that don't follow the
17
25
  Anthropic skill schema).
26
+ - Command files named ``AGENTS.md`` are skipped (cluster authoring docs,
27
+ not invocable commands).
28
+ - A command slug that collides with an existing skill name is skipped —
29
+ the real skill bundle wins, matching ``compress.generate_claude_commands``.
18
30
  - Writes are atomic via tempfile → ``os.replace``.
19
- - Idempotent: each ZIP gets a sibling ``<skill-name>.sha256`` recording
31
+ - Idempotent: each ZIP gets a sibling ``<slug>.sha256`` recording
20
32
  the manifest digest. If the recomputed digest matches the recorded
21
33
  one, the existing ZIP is left untouched (unless ``force=True``).
22
34
  """
@@ -148,3 +160,79 @@ def build_skill_bundles(
148
160
  digest_path.write_text(digest + "\n", encoding="utf-8")
149
161
  written.append(zip_path)
150
162
  return written
163
+
164
+
165
+ def _command_slug(source_file: Path, commands_root: Path) -> str:
166
+ """Return the flat slug for a command source file.
167
+
168
+ Mirrors ``scripts/compress.py::_command_slug``: top-level commands
169
+ keep their stem (``commit.md`` → ``commit``); nested commands flatten
170
+ the relative path with ``-`` (``council/default.md`` →
171
+ ``council-default``).
172
+ """
173
+ rel = source_file.relative_to(commands_root)
174
+ return "-".join(rel.with_suffix("").parts)
175
+
176
+
177
+ def _iter_command_files(commands_root: Path) -> Iterable[Path]:
178
+ """Yield every command ``.md`` file under ``commands_root`` (recursive).
179
+
180
+ Skips ``AGENTS.md`` cluster authoring docs, matching
181
+ ``scripts/compress.py::_iter_commands``.
182
+ """
183
+ for source_file in sorted(commands_root.rglob("*.md")):
184
+ if source_file.name == "AGENTS.md":
185
+ continue
186
+ yield source_file
187
+
188
+
189
+ def build_command_bundles(
190
+ package_root: Path,
191
+ dest_dir: Path,
192
+ force: bool = False,
193
+ curation: Optional[list[str]] = None,
194
+ ) -> list[Path]:
195
+ """Build per-command ZIPs under ``dest_dir``.
196
+
197
+ Each ZIP contains a single ``SKILL.md`` whose bytes are the source
198
+ command ``.md`` file — same wrapping pattern that
199
+ ``compress.generate_claude_commands`` uses for Claude Code via
200
+ ``.claude/skills/<slug>/SKILL.md`` symlinks.
201
+
202
+ Slugs that collide with an existing skill folder under
203
+ ``<package_root>/.agent-src/skills/`` are skipped so the real skill
204
+ bundle wins.
205
+
206
+ Returns the list of ZIP paths that were (re-)written this call. ZIPs
207
+ skipped because their content digest matched the existing sidecar
208
+ are not in the returned list (but remain on disk).
209
+
210
+ ``curation`` optionally restricts the build to the given command
211
+ slugs; ``None`` bundles every command file.
212
+ """
213
+ commands_root = package_root / ".agent-src" / "commands"
214
+ if not commands_root.is_dir():
215
+ return []
216
+ skills_root = package_root / ".agent-src" / "skills"
217
+ skill_names: set[str] = set()
218
+ if skills_root.is_dir():
219
+ skill_names = {entry.name for entry in skills_root.iterdir() if entry.is_dir()}
220
+ dest_dir.mkdir(parents=True, exist_ok=True)
221
+ written: list[Path] = []
222
+ for source_file in _iter_command_files(commands_root):
223
+ slug = _command_slug(source_file, commands_root)
224
+ if slug in skill_names:
225
+ continue
226
+ if curation is not None and slug not in curation:
227
+ continue
228
+ files = [(source_file.resolve(), ("SKILL.md",))]
229
+ digest = _manifest_digest(files)
230
+ zip_path = dest_dir / f"{slug}.zip"
231
+ digest_path = dest_dir / f"{slug}.sha256"
232
+ recorded = digest_path.read_text(encoding="utf-8").strip() if digest_path.exists() else ""
233
+ if not force and recorded == digest and zip_path.exists():
234
+ continue
235
+ _atomic_write_zip(zip_path, files)
236
+ digest_path.write_text(digest + "\n", encoding="utf-8")
237
+ written.append(zip_path)
238
+ return written
@@ -2888,6 +2888,7 @@ def _deploy_claude_desktop(
2888
2888
  bundler = _load_claude_desktop_bundler_module()
2889
2889
  bundles_dir = _claude_desktop_bundles_dir()
2890
2890
  bundler.build_skill_bundles(package_root, bundles_dir, force=force)
2891
+ bundler.build_command_bundles(package_root, bundles_dir, force=force)
2891
2892
  # Count total existing ZIPs (idempotent runs may not rewrite any).
2892
2893
  bundle_count = sum(1 for _ in bundles_dir.glob("*.zip")) if bundles_dir.is_dir() else 0
2893
2894
  _, _, marker_paths = _write_claude_desktop_marker(