agentbundle 0.13.0__tar.gz → 0.17.0__tar.gz

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 (162) hide show
  1. {agentbundle-0.13.0 → agentbundle-0.17.0}/PKG-INFO +22 -1
  2. {agentbundle-0.13.0 → agentbundle-0.17.0}/README.md +19 -0
  3. agentbundle-0.17.0/agentbundle/_data/catalogue.schema.json +170 -0
  4. agentbundle-0.17.0/agentbundle/_data/install-defaults.toml +9 -0
  5. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/__init__.py +43 -4
  6. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/adapters/claude_code.py +16 -10
  7. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/adapters/codex.py +20 -15
  8. agentbundle-0.17.0/agentbundle/build/recipes/composite-agents-md.toml +8 -0
  9. agentbundle-0.17.0/agentbundle/build/recipes/composite-marketplace.toml +9 -0
  10. agentbundle-0.17.0/agentbundle/build/recipes/marketplace.toml +8 -0
  11. agentbundle-0.17.0/agentbundle/build/recipes/per-pack-apm-package.toml +7 -0
  12. agentbundle-0.17.0/agentbundle/build/recipes/per-pack-claude-plugin.toml +7 -0
  13. agentbundle-0.17.0/agentbundle/build/recipes/per-pack-overlay.toml +7 -0
  14. agentbundle-0.17.0/agentbundle/build/recipes/self-host.toml +54 -0
  15. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_security.py +10 -7
  16. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_shipped_packs_v08_declarations.py +1 -0
  17. agentbundle-0.17.0/agentbundle/catalogue_tooling/__init__.py +12 -0
  18. agentbundle-0.17.0/agentbundle/catalogue_tooling/archive.py +357 -0
  19. agentbundle-0.17.0/agentbundle/catalogue_tooling/build.py +125 -0
  20. agentbundle-0.17.0/agentbundle/catalogue_tooling/config.py +459 -0
  21. agentbundle-0.17.0/agentbundle/catalogue_tooling/defaults.py +199 -0
  22. agentbundle-0.17.0/agentbundle/catalogue_tooling/diagnostics.py +38 -0
  23. agentbundle-0.17.0/agentbundle/catalogue_tooling/lint.py +648 -0
  24. agentbundle-0.17.0/agentbundle/catalogue_tooling/package.py +623 -0
  25. agentbundle-0.17.0/agentbundle/catalogue_tooling/results.py +69 -0
  26. agentbundle-0.17.0/agentbundle/catalogue_tooling/self_host.py +73 -0
  27. agentbundle-0.17.0/agentbundle/catalogue_tooling/skill_spec_lint.py +665 -0
  28. agentbundle-0.17.0/agentbundle/catalogue_tooling/verify.py +440 -0
  29. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/cli.py +156 -0
  30. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/adapt.py +1 -1
  31. agentbundle-0.17.0/agentbundle/commands/catalogue_build.py +46 -0
  32. agentbundle-0.17.0/agentbundle/commands/catalogue_lint.py +34 -0
  33. agentbundle-0.17.0/agentbundle/commands/catalogue_package.py +47 -0
  34. agentbundle-0.17.0/agentbundle/commands/catalogue_self_host.py +48 -0
  35. agentbundle-0.17.0/agentbundle/commands/catalogue_sync_defaults.py +43 -0
  36. agentbundle-0.17.0/agentbundle/commands/catalogue_tooling_stub.py +22 -0
  37. agentbundle-0.17.0/agentbundle/commands/catalogue_verify.py +47 -0
  38. agentbundle-0.17.0/agentbundle/commands/pack_evals.py +1242 -0
  39. agentbundle-0.17.0/agentbundle/commands/package_catalogue.py +66 -0
  40. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/upgrade.py +21 -2
  41. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/scope.py +10 -0
  42. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/version.py +1 -1
  43. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle.egg-info/PKG-INFO +22 -1
  44. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle.egg-info/SOURCES.txt +29 -0
  45. agentbundle-0.17.0/agentbundle.egg-info/requires.txt +3 -0
  46. {agentbundle-0.13.0 → agentbundle-0.17.0}/pyproject.toml +5 -2
  47. {agentbundle-0.13.0 → agentbundle-0.17.0}/tests/test_linear_primitive.py +2 -1
  48. agentbundle-0.13.0/agentbundle/_data/install-defaults.toml +0 -19
  49. agentbundle-0.13.0/agentbundle/commands/package_catalogue.py +0 -461
  50. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/__init__.py +0 -0
  51. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/__main__.py +0 -0
  52. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/_data/adapter.schema.json +0 -0
  53. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/_data/adapter.toml +0 -0
  54. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/_data/install-marker.py +0 -0
  55. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/_data/pack.schema.json +0 -0
  56. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/_data/plugin-manifest.derived.schema.json +0 -0
  57. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/_data/plugin-manifest.schema.json +0 -0
  58. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/_data/profile.schema.json +0 -0
  59. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/__main__.py +0 -0
  60. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/adapter_root_bins.py +0 -0
  61. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/adapters/__init__.py +0 -0
  62. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/adapters/copilot.py +0 -0
  63. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/adapters/cursor.py +0 -0
  64. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/adapters/gemini.py +0 -0
  65. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/adapters/kiro.py +0 -0
  66. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/adapters/kiro_cli.py +0 -0
  67. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/adapters/kiro_ide.py +0 -0
  68. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/contract.py +0 -0
  69. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/lint_packs.py +0 -0
  70. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/main.py +0 -0
  71. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/phase_order.py +0 -0
  72. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/projections/__init__.py +0 -0
  73. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/projections/codex_agent_toml.py +0 -0
  74. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/projections/copilot_agent_md.py +0 -0
  75. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/projections/copilot_hooks_json.py +0 -0
  76. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/projections/direct_directory.py +0 -0
  77. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/projections/gemini_command_toml.py +0 -0
  78. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/projections/hook_id.py +0 -0
  79. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/projections/kiro_ide_hook.py +0 -0
  80. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/projections/merge_into_agent_json.py +0 -0
  81. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/projections/merge_json.py +0 -0
  82. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/projections/user_merge_json.py +0 -0
  83. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/scope_rails.py +0 -0
  84. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/self_host.py +0 -0
  85. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/shared_libs.py +0 -0
  86. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/target_resolver.py +0 -0
  87. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/__init__.py +0 -0
  88. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_adapter_claude_code.py +0 -0
  89. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_adapter_codex.py +0 -0
  90. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_adapter_copilot.py +0 -0
  91. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_adapter_cursor.py +0 -0
  92. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_adapter_gemini.py +0 -0
  93. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_adapter_kiro.py +0 -0
  94. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_adapter_kiro_alias.py +0 -0
  95. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_adapter_kiro_cli.py +0 -0
  96. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_adapter_kiro_ide.py +0 -0
  97. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_adapter_root_bins_projection.py +0 -0
  98. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_architect_design_reviewer_projection.py +0 -0
  99. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_architect_design_reviewer_rubric_parity.py +0 -0
  100. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_build_ships_seeds.py +0 -0
  101. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_contract.py +0 -0
  102. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_contract_scope.py +0 -0
  103. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_contract_v07.py +0 -0
  104. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_contract_v08.py +0 -0
  105. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_direct_directory_cleanup.py +0 -0
  106. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_end_to_end_build.py +0 -0
  107. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_lint_agents_md_diataxis_block.py +0 -0
  108. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_lint_agents_md_legacy_block.py +0 -0
  109. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_lint_agents_md_risk_block.py +0 -0
  110. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_lint_packs.py +0 -0
  111. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_load_pack_hook_wiring_safely.py +0 -0
  112. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_pack_schema.py +0 -0
  113. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_pack_schema_allowed_adapters.py +0 -0
  114. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_pack_schema_install.py +0 -0
  115. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_pipeline.py +0 -0
  116. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_plugin_manifest_schema.py +0 -0
  117. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_projectable_subset.py +0 -0
  118. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_projections_merge_json.py +0 -0
  119. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_scope_rails.py +0 -0
  120. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_self_host_check.py +0 -0
  121. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_self_host_fixture_guard.py +0 -0
  122. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_self_host_recipe_config.py +0 -0
  123. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_shared_libs_projection.py +0 -0
  124. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_shared_prefix_contract.py +0 -0
  125. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_shipped_packs_v07_declarations.py +0 -0
  126. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_user_libs_projection.py +0 -0
  127. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_validate.py +0 -0
  128. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/tests/test_writers_emit_lf.py +0 -0
  129. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/user_libs.py +0 -0
  130. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/build/validate.py +0 -0
  131. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/catalogue.py +0 -0
  132. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/categories.py +0 -0
  133. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/__init__.py +0 -0
  134. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/_common.py +0 -0
  135. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/_drop_warning.py +0 -0
  136. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/config.py +0 -0
  137. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/diff.py +0 -0
  138. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/init_state.py +0 -0
  139. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/install.py +0 -0
  140. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/list_installed.py +0 -0
  141. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/list_packs.py +0 -0
  142. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/list_profiles.py +0 -0
  143. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/list_targets.py +0 -0
  144. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/profile.py +0 -0
  145. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/reconcile.py +0 -0
  146. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/render.py +0 -0
  147. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/scaffold.py +0 -0
  148. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/show.py +0 -0
  149. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/uninstall.py +0 -0
  150. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/commands/validate.py +0 -0
  151. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/config.py +0 -0
  152. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/https_catalogue.py +0 -0
  153. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/pack_inventory.py +0 -0
  154. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/render.py +0 -0
  155. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/safety.py +0 -0
  156. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/source_defaults.py +0 -0
  157. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/statelock.py +0 -0
  158. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle/user_config.py +0 -0
  159. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle.egg-info/dependency_links.txt +0 -0
  160. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle.egg-info/entry_points.txt +0 -0
  161. {agentbundle-0.13.0 → agentbundle-0.17.0}/agentbundle.egg-info/top_level.txt +0 -0
  162. {agentbundle-0.13.0 → agentbundle-0.17.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentbundle
3
- Version: 0.13.0
3
+ Version: 0.17.0
4
4
  Summary: npm for your coding agent. Install packs of skills, subagents, and hooks into any repo, for every major agent.
5
5
  Author-email: eugenelim <eugenelim@users.noreply.github.com>
6
6
  License: Apache-2.0 OR MIT
@@ -15,6 +15,8 @@ Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
16
  Requires-Python: >=3.11
17
17
  Description-Content-Type: text/markdown
18
+ Provides-Extra: lint
19
+ Requires-Dist: pyyaml>=6.0; extra == "lint"
18
20
 
19
21
  # agentbundle
20
22
 
@@ -215,6 +217,25 @@ preferred_adapter = "cursor"
215
217
 
216
218
  The org hint fires after the user-config but before the on-disk IDE probe — so `--adapter`, user-config, and upgrade state-hints all take priority. An invalid value exits 1 before writing anything. See the [`agentbundle` reference](https://github.com/eugenelim/agent-ready-repo/blob/main/docs/guides/_shared/reference/agentbundle.md#org-adapter-default) for the full cascade.
217
219
 
220
+ **Lint your catalogue** — shallow structural checks run without extra dependencies:
221
+
222
+ ```bash
223
+ agentbundle catalogue lint --root .
224
+ ```
225
+
226
+ For full [agentskills.io spec](https://agentskills.io/specification) compliance (frontmatter key set, description policy, encoding, evals schema), install the `lint` extra and run with `--deep`:
227
+
228
+ ```bash
229
+ pip install 'agentbundle[lint]'
230
+ agentbundle catalogue lint --root . --deep
231
+ ```
232
+
233
+ **Run Tier-A activation evals** to measure whether each covered skill fires on the prompts it should:
234
+
235
+ ```bash
236
+ agentbundle pack evals run --pack <pack-name> --catalogue-root .
237
+ ```
238
+
218
239
  See the [pack layout reference](https://github.com/eugenelim/agent-ready-repo/blob/main/docs/architecture/pack-layout.md) and [authoring a skill](https://github.com/eugenelim/agent-ready-repo/blob/main/docs/guides/_shared/how-to/author-a-skill.md).
219
240
 
220
241
  ## Credentials
@@ -197,6 +197,25 @@ preferred_adapter = "cursor"
197
197
 
198
198
  The org hint fires after the user-config but before the on-disk IDE probe — so `--adapter`, user-config, and upgrade state-hints all take priority. An invalid value exits 1 before writing anything. See the [`agentbundle` reference](https://github.com/eugenelim/agent-ready-repo/blob/main/docs/guides/_shared/reference/agentbundle.md#org-adapter-default) for the full cascade.
199
199
 
200
+ **Lint your catalogue** — shallow structural checks run without extra dependencies:
201
+
202
+ ```bash
203
+ agentbundle catalogue lint --root .
204
+ ```
205
+
206
+ For full [agentskills.io spec](https://agentskills.io/specification) compliance (frontmatter key set, description policy, encoding, evals schema), install the `lint` extra and run with `--deep`:
207
+
208
+ ```bash
209
+ pip install 'agentbundle[lint]'
210
+ agentbundle catalogue lint --root . --deep
211
+ ```
212
+
213
+ **Run Tier-A activation evals** to measure whether each covered skill fires on the prompts it should:
214
+
215
+ ```bash
216
+ agentbundle pack evals run --pack <pack-name> --catalogue-root .
217
+ ```
218
+
200
219
  See the [pack layout reference](https://github.com/eugenelim/agent-ready-repo/blob/main/docs/architecture/pack-layout.md) and [authoring a skill](https://github.com/eugenelim/agent-ready-repo/blob/main/docs/guides/_shared/how-to/author-a-skill.md).
201
220
 
202
221
  ## Credentials
@@ -0,0 +1,170 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "catalogue.schema.json",
4
+ "title": "CatalogueConfig",
5
+ "description": "JSON Schema for catalogue.toml — ini-005 Bucket 3 configuration layer.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "catalogue", "distribution"],
9
+ "properties": {
10
+ "schema": {
11
+ "type": "integer",
12
+ "description": "Schema version — must be 1.",
13
+ "enum": [1]
14
+ },
15
+ "catalogue": {
16
+ "type": "object",
17
+ "additionalProperties": false,
18
+ "required": [
19
+ "name",
20
+ "display-name",
21
+ "description",
22
+ "minimum-agentbundle-version",
23
+ "paths",
24
+ "build",
25
+ "package"
26
+ ],
27
+ "properties": {
28
+ "name": {
29
+ "type": "string",
30
+ "description": "Safe identifier — [A-Za-z0-9][A-Za-z0-9_-]*.",
31
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_\\-]*$"
32
+ },
33
+ "display-name": {
34
+ "type": "string",
35
+ "description": "Human-readable catalogue name."
36
+ },
37
+ "description": {
38
+ "type": "string",
39
+ "description": "One-line description of the catalogue."
40
+ },
41
+ "minimum-agentbundle-version": {
42
+ "type": "string",
43
+ "description": "Minimum agentbundle version required to use this catalogue."
44
+ },
45
+ "paths": {
46
+ "type": "object",
47
+ "additionalProperties": false,
48
+ "required": ["packs", "profiles", "contracts", "marketplace", "build-output"],
49
+ "properties": {
50
+ "packs": {
51
+ "type": "string",
52
+ "description": "Relative path to the packs directory."
53
+ },
54
+ "profiles": {
55
+ "type": "string",
56
+ "description": "Relative path to the profiles directory."
57
+ },
58
+ "contracts": {
59
+ "type": "string",
60
+ "description": "Relative path to the contracts directory."
61
+ },
62
+ "marketplace": {
63
+ "type": "string",
64
+ "description": "Relative path to the marketplace.json file."
65
+ },
66
+ "build-output": {
67
+ "type": "string",
68
+ "description": "Relative path for build output."
69
+ }
70
+ }
71
+ },
72
+ "build": {
73
+ "type": "object",
74
+ "additionalProperties": false,
75
+ "required": ["recipes", "self-host", "claude-plugin-branch", "marketplace-description"],
76
+ "properties": {
77
+ "recipes": {
78
+ "type": "array",
79
+ "items": { "type": "string" },
80
+ "description": "Build recipe IDs or relative .toml paths."
81
+ },
82
+ "self-host": {
83
+ "type": "boolean",
84
+ "description": "Whether to generate self-host install-defaults.toml."
85
+ },
86
+ "claude-plugin-branch": {
87
+ "type": "string",
88
+ "description": "Branch name for claude-plugin publishing."
89
+ },
90
+ "marketplace-description": {
91
+ "type": "string",
92
+ "description": "Description for marketplace.json."
93
+ }
94
+ }
95
+ },
96
+ "package": {
97
+ "type": "object",
98
+ "additionalProperties": false,
99
+ "required": ["include", "required"],
100
+ "properties": {
101
+ "include": {
102
+ "type": "array",
103
+ "items": { "type": "string" },
104
+ "description": "Pack paths to include in a packaged archive."
105
+ },
106
+ "required": {
107
+ "type": "array",
108
+ "items": { "type": "string" },
109
+ "description": "Pack paths that must be present (subset of include)."
110
+ }
111
+ }
112
+ }
113
+ }
114
+ },
115
+ "distribution": {
116
+ "type": "object",
117
+ "additionalProperties": false,
118
+ "required": ["agentbundle"],
119
+ "properties": {
120
+ "agentbundle": {
121
+ "type": "object",
122
+ "additionalProperties": false,
123
+ "required": [
124
+ "install-defaults-output",
125
+ "preferred-adapter",
126
+ "default-source",
127
+ "artifactory"
128
+ ],
129
+ "properties": {
130
+ "install-defaults-output": {
131
+ "type": "string",
132
+ "description": "Relative path for generated install-defaults.toml."
133
+ },
134
+ "preferred-adapter": {
135
+ "type": "string",
136
+ "description": "Default adapter hint written to install-defaults.toml."
137
+ },
138
+ "default-source": {
139
+ "type": "string",
140
+ "description": "Default catalogue source URI (git+https:// or local path)."
141
+ },
142
+ "artifactory": {
143
+ "type": "object",
144
+ "additionalProperties": false,
145
+ "required": ["enabled"],
146
+ "properties": {
147
+ "enabled": {
148
+ "type": "boolean",
149
+ "description": "Whether the Artifactory bootstrap is active."
150
+ },
151
+ "base-url": {
152
+ "type": "string",
153
+ "description": "Artifactory base URL (https:// only, required when enabled)."
154
+ },
155
+ "repository": {
156
+ "type": "string",
157
+ "description": "Artifactory repository name (required when enabled)."
158
+ },
159
+ "bundle": {
160
+ "type": "string",
161
+ "description": "Catalogue bundle name (required when enabled)."
162
+ }
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+ }
170
+ }
@@ -0,0 +1,9 @@
1
+ # Generated by agentbundle catalogue sync-defaults — source of truth: catalogue.toml
2
+ [organization]
3
+ preferred_adapter = "claude-code"
4
+
5
+ [organization.artifactory]
6
+ enabled = false
7
+
8
+ [defaults]
9
+ source = "git+https://github.com/eugenelim/agent-ready-repo"
@@ -111,7 +111,7 @@ def _build_parser() -> argparse.ArgumentParser:
111
111
  default="dist",
112
112
  help="Where to write build artefacts (default: dist/).",
113
113
  )
114
- build_parser.set_defaults(func=cmd_build)
114
+ build_parser.set_defaults(func=_cmd_build_shim)
115
115
 
116
116
  self_parser = subparsers.add_parser(
117
117
  "self",
@@ -130,7 +130,7 @@ def _build_parser() -> argparse.ArgumentParser:
130
130
  "OS for filesystems or workflows that disallow symlinks."
131
131
  ),
132
132
  )
133
- self_parser.set_defaults(func=cmd_self)
133
+ self_parser.set_defaults(func=_cmd_self_shim)
134
134
 
135
135
  check_parser = subparsers.add_parser(
136
136
  "check",
@@ -146,7 +146,7 @@ def _build_parser() -> argparse.ArgumentParser:
146
146
  "regular-file CLAUDE.md copy rather than a symlink."
147
147
  ),
148
148
  )
149
- check_parser.set_defaults(func=cmd_check)
149
+ check_parser.set_defaults(func=_cmd_check_shim)
150
150
 
151
151
  scaffold_parser = subparsers.add_parser(
152
152
  "scaffold",
@@ -162,14 +162,53 @@ def _build_parser() -> argparse.ArgumentParser:
162
162
  help=(
163
163
  "Windows-portability lint: reject packs that ship symlinks "
164
164
  "or Windows-poisonous names under seeds/ or .apm/."
165
+ " [deprecated: use 'agentbundle catalogue lint']"
165
166
  ),
166
167
  )
167
168
  lint_packs_parser.add_argument("--packs-dir", default="packs")
168
- lint_packs_parser.set_defaults(func=cmd_lint_packs)
169
+ lint_packs_parser.set_defaults(func=_cmd_lint_packs_shim)
169
170
 
170
171
  return parser
171
172
 
172
173
 
174
+ def _cmd_build_shim(args) -> int:
175
+ print(
176
+ "agentbundle.build build is deprecated; use 'agentbundle catalogue build --root .' instead",
177
+ file=sys.stderr,
178
+ )
179
+ return int(cmd_build(args))
180
+
181
+
182
+ def _cmd_self_shim(args) -> int:
183
+ print(
184
+ "agentbundle.build self is deprecated; use 'agentbundle catalogue self-host --write --root .' instead",
185
+ file=sys.stderr,
186
+ )
187
+ return int(cmd_self(args))
188
+
189
+
190
+ def _cmd_check_shim(args) -> int:
191
+ print(
192
+ "agentbundle.build check is deprecated; use 'agentbundle catalogue self-host --check --root .' instead",
193
+ file=sys.stderr,
194
+ )
195
+ return int(cmd_check(args))
196
+
197
+
198
+ def _cmd_lint_packs_shim(args) -> int:
199
+ print(
200
+ "agentbundle.build lint-packs is deprecated; use 'agentbundle catalogue lint --root .' instead",
201
+ file=sys.stderr,
202
+ )
203
+ from agentbundle.catalogue_tooling.lint import lint_catalogue, render_table
204
+
205
+ packs_dir = Path(getattr(args, "packs_dir", "packs")).resolve()
206
+ root = packs_dir.parent
207
+ result = lint_catalogue(root)
208
+ print(render_table(result), file=sys.stderr)
209
+ return 0 if result.ok else 1
210
+
211
+
173
212
  def _cmd_scaffold(args) -> int:
174
213
  pack_seeds = Path(args.packs_dir) / args.pack / "seeds"
175
214
  output = Path(args.output)
@@ -14,6 +14,7 @@ deep-merging the incoming TOML payload.
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ import os
17
18
  import shutil
18
19
  from pathlib import Path
19
20
  from typing import Iterator
@@ -107,15 +108,17 @@ def _project_single(pack_path: Path, contract: dict, output_root: Path) -> None:
107
108
  raise ValueError(f"claude-code: unhandled mode {mode!r} for {primitive_name}")
108
109
 
109
110
 
110
- def _ignore_symlinks(directory: str, names: list[str]) -> set[str]:
111
- """`shutil.copytree` ignore callback: skip every symlink member.
111
+ def _ignore_absolute_symlinks(directory: str, names: list[str]) -> set[str]:
112
+ """`shutil.copytree` ignore callback: drop symlinks with absolute targets.
112
113
 
113
- Drops nested symlinks so they are never reproduced in the output
114
- tree. The top-level `is_symlink()` skip in `_project_direct_directory`
115
- covers the skill root; this covers the subtree.
114
+ Relative symlinks (intra-skill cross-references) are preserved as
115
+ symlinks. Absolute symlinks always escape the tree and are a path-escape vector.
116
116
  """
117
117
  base = Path(directory)
118
- return {name for name in names if (base / name).is_symlink()}
118
+ return {
119
+ name for name in names
120
+ if (base / name).is_symlink() and os.path.isabs(os.readlink(base / name))
121
+ }
119
122
 
120
123
 
121
124
  def _project_direct_directory(source_dir: Path, target_dir: Path) -> None:
@@ -136,10 +139,13 @@ def _project_direct_directory(source_dir: Path, target_dir: Path) -> None:
136
139
  destination.unlink()
137
140
  elif destination.exists():
138
141
  shutil.rmtree(destination)
139
- # `ignore=_ignore_symlinks` drops nested symlinks so they are
140
- # never reproduced in the output tree. A malicious pack with a
141
- # symlink to /etc/passwd cannot exfiltrate the target.
142
- shutil.copytree(entry, destination, ignore=_ignore_symlinks)
142
+ # symlinks=True preserves relative nested symlinks; absolute
143
+ # targets are filtered out by _ignore_absolute_symlinks.
144
+ shutil.copytree(
145
+ entry, destination,
146
+ symlinks=True,
147
+ ignore=_ignore_absolute_symlinks,
148
+ )
143
149
 
144
150
 
145
151
  def _project_direct_file(source_dir: Path, output_root: Path, target_prefix: str) -> None:
@@ -29,16 +29,6 @@ from agentbundle.build.projections.codex_agent_toml import (
29
29
  )
30
30
 
31
31
 
32
- def _ignore_symlinks(directory: str, names: list[str]) -> set[str]:
33
- """`shutil.copytree` ignore callback: skip every symlink member.
34
-
35
- Drops nested symlinks so they are never reproduced in the output
36
- tree. The top-level `is_symlink()` skip in `project_packs` covers
37
- the skill root; this covers the subtree.
38
- """
39
- base = Path(directory)
40
- return {name for name in names if (base / name).is_symlink()}
41
-
42
32
 
43
33
  def _iter_primitives(contract: dict) -> Iterator[str]:
44
34
  """Yield Codex's projected primitive names in phase order."""
@@ -142,11 +132,13 @@ def project_packs(pack_paths: list[Path], contract: dict, output_root: Path) ->
142
132
  destination.unlink()
143
133
  elif destination.exists():
144
134
  shutil.rmtree(destination)
145
- # `ignore=_ignore_symlinks` drops nested symlinks
146
- # entirely never reproduced in the output tree.
147
- # A malicious pack with a symlink to /etc/passwd
148
- # cannot exfiltrate.
149
- shutil.copytree(entry, destination, ignore=_ignore_symlinks)
135
+ # symlinks=True preserves relative nested symlinks;
136
+ # absolute targets are filtered by _ignore_absolute_symlinks.
137
+ shutil.copytree(
138
+ entry, destination,
139
+ symlinks=True,
140
+ ignore=_ignore_absolute_symlinks,
141
+ )
150
142
  # Bound to `skill` only per spec § Never do. Other
151
143
  # direct-directory primitives opt in explicitly.
152
144
  if primitive_name == "skill":
@@ -166,6 +158,19 @@ def project_packs(pack_paths: list[Path], contract: dict, output_root: Path) ->
166
158
  raise ValueError(f"codex: unhandled mode {mode!r} for {primitive_name}")
167
159
 
168
160
 
161
+ def _ignore_absolute_symlinks(directory: str, names: list[str]) -> set[str]:
162
+ """`shutil.copytree` ignore callback: drop symlinks with absolute targets.
163
+
164
+ Relative symlinks (intra-skill cross-references) are preserved (AC5).
165
+ Absolute symlinks always escape the tree and are a path-escape vector.
166
+ """
167
+ base = Path(directory)
168
+ return {
169
+ name for name in names
170
+ if (base / name).is_symlink() and os.path.isabs(os.readlink(base / name))
171
+ }
172
+
173
+
169
174
  def _project_direct_file(source_dir: Path, output_root: Path, target_prefix: str) -> None:
170
175
  target_dir = output_root / target_prefix.rstrip("/")
171
176
  target_dir.mkdir(parents=True, exist_ok=True)
@@ -0,0 +1,8 @@
1
+ # RFC-0002 recipe: compose a single AGENTS.md from each pack's
2
+ # seeds/AGENTS.fragment.md, concatenated in declared order.
3
+
4
+ [recipe]
5
+ name = "composite-agents-md"
6
+ type = "composite"
7
+ fragment-path = "seeds/AGENTS.fragment.md"
8
+ output-file = "AGENTS.md"
@@ -0,0 +1,9 @@
1
+ # RFC-0002 recipe: compose a self-host marketplace listing from each
2
+ # pack's .claude-plugin/plugin.json (distinct from the dist/-aggregating
3
+ # `marketplace` recipe).
4
+
5
+ [recipe]
6
+ name = "composite-marketplace"
7
+ type = "composite"
8
+ manifest-path = ".claude-plugin/plugin.json"
9
+ output-file = ".claude-plugin/marketplace.json"
@@ -0,0 +1,8 @@
1
+ # RFC-0001 recipe: aggregate every per-pack Claude plugin into one
2
+ # marketplace.json at dist/claude-plugins/marketplace.json.
3
+
4
+ [recipe]
5
+ name = "marketplace"
6
+ type = "aggregate"
7
+ input-subdir = "claude-plugins"
8
+ output-file = "claude-plugins/marketplace.json"
@@ -0,0 +1,7 @@
1
+ # RFC-0001 recipe: project each pack as an APM package into dist/apm/<pack>/.
2
+
3
+ [recipe]
4
+ name = "per-pack-apm-package"
5
+ type = "per-pack"
6
+ adapter = "apm"
7
+ output-subdir = "apm"
@@ -0,0 +1,7 @@
1
+ # RFC-0001 recipe: project each pack as a Claude Code plugin into dist/claude-plugins/<pack>/.
2
+
3
+ [recipe]
4
+ name = "per-pack-claude-plugin"
5
+ type = "per-pack"
6
+ adapter = "claude-code"
7
+ output-subdir = "claude-plugins"
@@ -0,0 +1,7 @@
1
+ # RFC-0002 recipe: self-host overlay — project each pack's .apm/ + seeds
2
+ # into the working tree (consumed by `make build-self`).
3
+
4
+ [recipe]
5
+ name = "per-pack-overlay"
6
+ type = "overlay"
7
+ units = [".apm/", "seeds/"]
@@ -0,0 +1,54 @@
1
+ # RFC-0002 self-host recipe — composes the four-pack overlay used by
2
+ # `make build-self`.
3
+ #
4
+ # The self-host runner reads this recipe-shape contract and executes the
5
+ # concrete projection in `agentbundle.build.self_host`.
6
+
7
+ [recipe]
8
+ name = "self-host"
9
+ type = "composite"
10
+ description = "Overlay every pack's Claude Code projection onto this repo; compose AGENTS.md from packs/core/seeds and aggregate .claude-plugin/marketplace.json across packs."
11
+
12
+ [recipe.adapters]
13
+ # Authoritative source for `SELF_HOST_ADAPTERS` — `self_host.py` reads this list.
14
+ # Kiro and Copilot stay in the contract for distribution builds but are excluded
15
+ # here so self-host does not project `.kiro/` or `.github/instructions/`.
16
+ targets = ["claude-code", "codex"]
17
+
18
+ [recipe.packs]
19
+ # Authoritative source for `SELF_HOST_PACKS` — `self_host.py` reads this list.
20
+ # This repo is the catalogue's home, not an adopter, so `make build-self` only
21
+ # projects the in-house packs into the working tree. User-scope-default packs
22
+ # (architect, atlassian, contracts, converters, credential-brokers, figma) are
23
+ # advertised via `marketplace.json` but their primitives don't belong in this
24
+ # repo's `.claude/skills/` tree; `monorepo-extras` is repo-only by metadata but
25
+ # the bundle is not a canonical `new-package` consumer, so it's left out too.
26
+ include = ["core", "governance-extras", "user-guide-diataxis", "catalogue-curation"]
27
+
28
+ [recipe.iterate]
29
+ over = "packs/*/"
30
+ read-pack-metadata = "pack.toml"
31
+
32
+ [recipe.per-pack-overlay]
33
+ root = "."
34
+ copy-from-pack-apm = [
35
+ { from = "{pack.path}/.apm/skills/", to = ".claude/skills/" },
36
+ { from = "{pack.path}/.apm/agents/", to = ".claude/agents/" },
37
+ { from = "{pack.path}/.apm/commands/", to = ".claude/commands/" },
38
+ { from = "{pack.path}/.apm/hooks/", to = "tools/hooks/" },
39
+ ]
40
+ copy-from-pack-seeds = [
41
+ { from = "{pack.path}/seeds/", to = "." },
42
+ ]
43
+
44
+ [recipe.composite-agents-md]
45
+ output-path = "AGENTS.md"
46
+ template-from = "packs/core/seeds/AGENTS.md"
47
+ managed-block-adapter = "codex"
48
+ managed-block-content-source = "all-installed-packs"
49
+ footer-from = "packs/core/seeds/_agents-footer.md"
50
+
51
+ [recipe.composite-marketplace]
52
+ output-path = ".claude-plugin/marketplace.json"
53
+ aggregate-from = "packs/*/.claude-plugin/plugin.json"
54
+ owner = { name = "eugenelim" }
@@ -45,10 +45,13 @@ class SymlinkProjectionTests(unittest.TestCase):
45
45
  def setUpClass(cls) -> None:
46
46
  cls.contract = load_contract(CONTRACT_PATH)
47
47
 
48
- def test_symlink_in_pack_skill_is_preserved_not_dereferenced(self) -> None:
49
- """A pack with a symlink to /etc/passwd should not exfiltrate
50
- the target into the projection — symlinks=True preserves them
51
- as symlinks rather than copying the target's contents.
48
+ def test_absolute_symlink_in_pack_skill_is_dropped(self) -> None:
49
+ """A pack skill with an absolute symlink must not reproduce it
50
+ in the projection — not as a symlink, not as copied content.
51
+
52
+ Absolute symlinks escape the skill tree and are a path-escape
53
+ vector. _ignore_absolute_symlinks drops them before copytree
54
+ writes them to the output.
52
55
 
53
56
  Test-only symlink creation: Windows-portability lint
54
57
  (`lint_packs.py`) rejects symlinks in shipped pack content, so
@@ -66,9 +69,9 @@ class SymlinkProjectionTests(unittest.TestCase):
66
69
  out = tmp_path / "out"
67
70
  project_claude_code(pack, self.contract, out)
68
71
  projected = out / ".claude" / "skills" / "foo" / "leak.txt"
69
- self.assertTrue(projected.is_symlink())
70
- # The link target is preserved as a symlink, not dereferenced.
71
- self.assertEqual(os.readlink(projected), "/etc/passwd")
72
+ # Absolute symlink is dropped entirely — not reproduced, not dereferenced.
73
+ self.assertFalse(projected.exists())
74
+ self.assertFalse(projected.is_symlink())
72
75
 
73
76
 
74
77
  class PluginManifestValidationTests(unittest.TestCase):
@@ -44,6 +44,7 @@ V08_PACKS = (
44
44
  "contracts",
45
45
  "converters",
46
46
  "figma",
47
+ "github",
47
48
  "governance-extras",
48
49
  "linear",
49
50
  "monorepo-extras",
@@ -0,0 +1,12 @@
1
+ """agentbundle.catalogue_tooling — portable catalogue engine.
2
+
3
+ This package provides the configuration schema, result types, and command
4
+ stubs for the agentbundle catalogue * and agentbundle lint packs CLI surface.
5
+
6
+ Wave 2-4 specs fill the command logic; this package establishes the stable
7
+ contract layer between all modules.
8
+ """
9
+
10
+ from agentbundle.catalogue_tooling.config import CatalogueConfigError, load_catalogue_config
11
+
12
+ __all__ = ["CatalogueConfigError", "load_catalogue_config"]