agentbundle 0.29.5__tar.gz → 0.31.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 (203) hide show
  1. {agentbundle-0.29.5 → agentbundle-0.31.0}/PKG-INFO +30 -6
  2. {agentbundle-0.29.5 → agentbundle-0.31.0}/README-pypi.md +29 -5
  3. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/adapter.toml +3 -0
  4. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/catalogue-scaffold/guides/_shared/reference/catalogue-authoring-standards.md +56 -0
  5. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/catalogue-scaffold/manifest.json +4 -4
  6. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/catalogue-scaffold/packs/AGENTS.md +38 -38
  7. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/catalogue-scaffold/packs/_example/pack.toml +1 -1
  8. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/install-marker.py +95 -21
  9. agentbundle-0.31.0/agentbundle/_data/marketplace-entry.schema.json +110 -0
  10. agentbundle-0.31.0/agentbundle/_data/plugin-manifest.derived.schema.json +147 -0
  11. agentbundle-0.31.0/agentbundle/_data/plugin-manifest.schema.json +110 -0
  12. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/adapter_root_bins.py +146 -30
  13. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/adapters/claude_code.py +27 -2
  14. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/main.py +353 -26
  15. agentbundle-0.31.0/agentbundle/build/projection_io.py +226 -0
  16. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/recipes/composite-marketplace.toml +1 -1
  17. agentbundle-0.31.0/agentbundle/build/recipes/marketplace.toml +9 -0
  18. agentbundle-0.31.0/agentbundle/build/recipes/per-pack-claude-plugin.toml +9 -0
  19. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/recipes/self-host.toml +1 -1
  20. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/self_host.py +258 -96
  21. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/user_libs.py +209 -26
  22. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/initialise_self_hosted.py +66 -1
  23. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/self_host.py +31 -2
  24. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/self_host_windows.py +19 -6
  25. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/verify.py +84 -25
  26. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/cli.py +4 -3
  27. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/install.py +11 -4
  28. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/upgrade.py +9 -6
  29. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/render.py +6 -2
  30. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/version.py +1 -1
  31. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle.egg-info/PKG-INFO +30 -6
  32. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle.egg-info/SOURCES.txt +2 -45
  33. {agentbundle-0.29.5 → agentbundle-0.31.0}/pyproject.toml +2 -2
  34. agentbundle-0.29.5/agentbundle/_data/plugin-manifest.derived.schema.json +0 -73
  35. agentbundle-0.29.5/agentbundle/_data/plugin-manifest.schema.json +0 -45
  36. agentbundle-0.29.5/agentbundle/build/recipes/marketplace.toml +0 -8
  37. agentbundle-0.29.5/agentbundle/build/recipes/per-pack-claude-plugin.toml +0 -7
  38. agentbundle-0.29.5/agentbundle/build/tests/__init__.py +0 -0
  39. agentbundle-0.29.5/agentbundle/build/tests/test_adapter_claude_code.py +0 -281
  40. agentbundle-0.29.5/agentbundle/build/tests/test_adapter_codex.py +0 -743
  41. agentbundle-0.29.5/agentbundle/build/tests/test_adapter_copilot.py +0 -120
  42. agentbundle-0.29.5/agentbundle/build/tests/test_adapter_cursor.py +0 -418
  43. agentbundle-0.29.5/agentbundle/build/tests/test_adapter_gemini.py +0 -636
  44. agentbundle-0.29.5/agentbundle/build/tests/test_adapter_kiro.py +0 -464
  45. agentbundle-0.29.5/agentbundle/build/tests/test_adapter_kiro_alias.py +0 -106
  46. agentbundle-0.29.5/agentbundle/build/tests/test_adapter_kiro_cli.py +0 -143
  47. agentbundle-0.29.5/agentbundle/build/tests/test_adapter_kiro_ide.py +0 -214
  48. agentbundle-0.29.5/agentbundle/build/tests/test_adapter_root_bins_projection.py +0 -491
  49. agentbundle-0.29.5/agentbundle/build/tests/test_architect_design_reviewer_projection.py +0 -88
  50. agentbundle-0.29.5/agentbundle/build/tests/test_architect_design_reviewer_rubric_parity.py +0 -74
  51. agentbundle-0.29.5/agentbundle/build/tests/test_build_ships_seeds.py +0 -81
  52. agentbundle-0.29.5/agentbundle/build/tests/test_contract.py +0 -617
  53. agentbundle-0.29.5/agentbundle/build/tests/test_contract_scope.py +0 -224
  54. agentbundle-0.29.5/agentbundle/build/tests/test_contract_v07.py +0 -196
  55. agentbundle-0.29.5/agentbundle/build/tests/test_contract_v08.py +0 -266
  56. agentbundle-0.29.5/agentbundle/build/tests/test_direct_directory_cleanup.py +0 -65
  57. agentbundle-0.29.5/agentbundle/build/tests/test_end_to_end_build.py +0 -225
  58. agentbundle-0.29.5/agentbundle/build/tests/test_lint_agents_md_diataxis_block.py +0 -95
  59. agentbundle-0.29.5/agentbundle/build/tests/test_lint_agents_md_legacy_block.py +0 -136
  60. agentbundle-0.29.5/agentbundle/build/tests/test_lint_agents_md_risk_block.py +0 -119
  61. agentbundle-0.29.5/agentbundle/build/tests/test_lint_packs.py +0 -716
  62. agentbundle-0.29.5/agentbundle/build/tests/test_load_pack_hook_wiring_safely.py +0 -176
  63. agentbundle-0.29.5/agentbundle/build/tests/test_pack_schema.py +0 -494
  64. agentbundle-0.29.5/agentbundle/build/tests/test_pack_schema_allowed_adapters.py +0 -258
  65. agentbundle-0.29.5/agentbundle/build/tests/test_pack_schema_install.py +0 -305
  66. agentbundle-0.29.5/agentbundle/build/tests/test_pipeline.py +0 -275
  67. agentbundle-0.29.5/agentbundle/build/tests/test_plugin_manifest_schema.py +0 -422
  68. agentbundle-0.29.5/agentbundle/build/tests/test_projectable_subset.py +0 -202
  69. agentbundle-0.29.5/agentbundle/build/tests/test_projections_merge_json.py +0 -151
  70. agentbundle-0.29.5/agentbundle/build/tests/test_scope_rails.py +0 -401
  71. agentbundle-0.29.5/agentbundle/build/tests/test_security.py +0 -102
  72. agentbundle-0.29.5/agentbundle/build/tests/test_self_host_check.py +0 -2278
  73. agentbundle-0.29.5/agentbundle/build/tests/test_self_host_fixture_guard.py +0 -88
  74. agentbundle-0.29.5/agentbundle/build/tests/test_self_host_recipe_config.py +0 -113
  75. agentbundle-0.29.5/agentbundle/build/tests/test_shared_libs_projection.py +0 -218
  76. agentbundle-0.29.5/agentbundle/build/tests/test_shared_prefix_contract.py +0 -114
  77. agentbundle-0.29.5/agentbundle/build/tests/test_shipped_packs_v07_declarations.py +0 -109
  78. agentbundle-0.29.5/agentbundle/build/tests/test_shipped_packs_v08_declarations.py +0 -92
  79. agentbundle-0.29.5/agentbundle/build/tests/test_user_libs_projection.py +0 -285
  80. agentbundle-0.29.5/agentbundle/build/tests/test_validate.py +0 -250
  81. agentbundle-0.29.5/agentbundle/build/tests/test_workspace_status_projection.py +0 -273
  82. agentbundle-0.29.5/agentbundle/build/tests/test_writers_emit_lf.py +0 -171
  83. {agentbundle-0.29.5 → agentbundle-0.31.0}/README.md +0 -0
  84. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/__init__.py +0 -0
  85. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/__main__.py +0 -0
  86. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/adapter.schema.json +0 -0
  87. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/catalogue-scaffold/guides/_shared/reference/catalogue-ci-contract.md +0 -0
  88. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/catalogue-scaffold/packs/README.md +0 -0
  89. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/catalogue-scaffold/packs/_example/README.md +0 -0
  90. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/catalogue-scaffold/profiles/AGENTS.md +0 -0
  91. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/catalogue-scaffold/profiles/README.md +0 -0
  92. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/catalogue-scaffold/profiles/_example/README.md +0 -0
  93. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/catalogue-scaffold/profiles/_example/profile.toml +0 -0
  94. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/catalogue.schema.json +0 -0
  95. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/guide.schema.json +0 -0
  96. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/install-defaults.toml +0 -0
  97. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/pack.schema.json +0 -0
  98. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/profile.schema.json +0 -0
  99. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/skill-manifest.schema.json +0 -0
  100. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/skill.schema.json +0 -0
  101. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/_data/target-vocab.toml +0 -0
  102. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/__init__.py +0 -0
  103. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/__main__.py +0 -0
  104. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/adapters/__init__.py +0 -0
  105. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/adapters/codex.py +0 -0
  106. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/adapters/copilot.py +0 -0
  107. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/adapters/cursor.py +0 -0
  108. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/adapters/gemini.py +0 -0
  109. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/adapters/kiro.py +0 -0
  110. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/adapters/kiro_cli.py +0 -0
  111. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/adapters/kiro_ide.py +0 -0
  112. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/contract.py +0 -0
  113. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/lint_packs.py +0 -0
  114. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/phase_order.py +0 -0
  115. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/projections/__init__.py +0 -0
  116. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/projections/codex_agent_toml.py +0 -0
  117. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/projections/copilot_agent_md.py +0 -0
  118. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/projections/copilot_hooks_json.py +0 -0
  119. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/projections/direct_directory.py +0 -0
  120. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/projections/gemini_command_toml.py +0 -0
  121. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/projections/hook_id.py +0 -0
  122. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/projections/kiro_ide_hook.py +0 -0
  123. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/projections/merge_into_agent_json.py +0 -0
  124. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/projections/merge_json.py +0 -0
  125. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/projections/user_merge_json.py +0 -0
  126. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/recipes/composite-agents-md.toml +0 -0
  127. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/recipes/per-pack-apm-package.toml +0 -0
  128. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/recipes/per-pack-overlay.toml +0 -0
  129. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/scope_rails.py +0 -0
  130. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/shared_libs.py +0 -0
  131. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/target_resolver.py +0 -0
  132. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/build/validate.py +0 -0
  133. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue.py +0 -0
  134. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/__init__.py +0 -0
  135. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/archive.py +0 -0
  136. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/build.py +0 -0
  137. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/config.py +0 -0
  138. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/defaults.py +0 -0
  139. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/diagnostics.py +0 -0
  140. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/identity.py +0 -0
  141. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/initialise.py +0 -0
  142. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/lint.py +0 -0
  143. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/package.py +0 -0
  144. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/results.py +0 -0
  145. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/skill_spec_lint.py +0 -0
  146. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/catalogue_tooling/toml_emit.py +0 -0
  147. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/categories.py +0 -0
  148. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/__init__.py +0 -0
  149. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/_common.py +0 -0
  150. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/_drop_warning.py +0 -0
  151. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/adapt.py +0 -0
  152. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/catalogue_build.py +0 -0
  153. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/catalogue_init.py +0 -0
  154. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/catalogue_lint.py +0 -0
  155. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/catalogue_package.py +0 -0
  156. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/catalogue_self_host.py +0 -0
  157. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/catalogue_sync_defaults.py +0 -0
  158. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/catalogue_tooling_stub.py +0 -0
  159. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/catalogue_verify.py +0 -0
  160. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/config.py +0 -0
  161. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/diff.py +0 -0
  162. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/docs.py +0 -0
  163. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/init_state.py +0 -0
  164. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/list_installed.py +0 -0
  165. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/list_packs.py +0 -0
  166. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/list_profiles.py +0 -0
  167. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/list_targets.py +0 -0
  168. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/oplog_cmd.py +0 -0
  169. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/pack_config_cmd.py +0 -0
  170. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/pack_evals.py +0 -0
  171. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/package_catalogue.py +0 -0
  172. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/profile.py +0 -0
  173. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/reconcile.py +0 -0
  174. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/render.py +0 -0
  175. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/scaffold.py +0 -0
  176. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/show.py +0 -0
  177. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/uninstall.py +0 -0
  178. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/commands/validate.py +0 -0
  179. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/config.py +0 -0
  180. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/https_catalogue.py +0 -0
  181. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/local_exclude.py +0 -0
  182. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/oplog.py +0 -0
  183. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/pack_inventory.py +0 -0
  184. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/safety.py +0 -0
  185. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/scaffold.py +0 -0
  186. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/scope.py +0 -0
  187. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/source_defaults.py +0 -0
  188. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/statelock.py +0 -0
  189. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/user_config.py +0 -0
  190. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle/workspace_mcp.py +0 -0
  191. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle.egg-info/dependency_links.txt +0 -0
  192. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle.egg-info/entry_points.txt +0 -0
  193. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle.egg-info/requires.txt +0 -0
  194. {agentbundle-0.29.5 → agentbundle-0.31.0}/agentbundle.egg-info/top_level.txt +0 -0
  195. {agentbundle-0.29.5 → agentbundle-0.31.0}/setup.cfg +0 -0
  196. {agentbundle-0.29.5 → agentbundle-0.31.0}/tests/test_adapter_permissions_projection.py +0 -0
  197. {agentbundle-0.29.5 → agentbundle-0.31.0}/tests/test_linear_primitive.py +0 -0
  198. {agentbundle-0.29.5 → agentbundle-0.31.0}/tests/test_workspace_mcp_elicit.py +0 -0
  199. {agentbundle-0.29.5 → agentbundle-0.31.0}/tests/test_workspace_mcp_event_bridge.py +0 -0
  200. {agentbundle-0.29.5 → agentbundle-0.31.0}/tests/test_workspace_mcp_git.py +0 -0
  201. {agentbundle-0.29.5 → agentbundle-0.31.0}/tests/test_workspace_mcp_lifecycle.py +0 -0
  202. {agentbundle-0.29.5 → agentbundle-0.31.0}/tests/test_workspace_mcp_stdin.py +0 -0
  203. {agentbundle-0.29.5 → agentbundle-0.31.0}/tests/test_workspace_mcp_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentbundle
3
- Version: 0.29.5
3
+ Version: 0.31.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
@@ -269,7 +269,13 @@ ignore them there; the separation is structural, not incidental.
269
269
  `keywords` once; the build projects the cleanly-mappable subset — plus the
270
270
  pack's `README.md` — into each distribution route's manifest (the `plugin.json`
271
271
  / `marketplace.json` entry), so the catalogue describes each pack richly rather
272
- than with a single sentence. Extra fields stay in `pack.toml`; the projection
272
+ than with a single sentence. The Claude-plugin route carries only packs whose
273
+ `[pack.install] allowed-scopes` admits `user`: a plugin's code lands in the
274
+ adopter's global cache, so a repo-only pack gets no `marketplace.json` entry
275
+ and is reached with `agentbundle install` instead. A marketplace entry's `source` is a `git-subdir`
276
+ object (`url`, `path`, and one of `ref`/`sha`) pointing at the pack's directory
277
+ on the published distribution branch, and every entry is schema-validated at
278
+ build time against `marketplace-entry.schema.json`. Extra fields stay in `pack.toml`; the projection
273
279
  is deliberately lossy per tool.
274
280
 
275
281
  Point a catalogue URI (a git URL or a local path) at the repo that holds your packs. Then `validate` a pack against the adapter contract, `render` it to preview the projection, and `install` it into a target repo. `scaffold` drops a pack's seeds into a fresh directory to start from. The build pipeline (`agentbundle.build`) is the same engine `make build` runs.
@@ -285,8 +291,9 @@ The org hint fires after the user-config but before the on-disk IDE probe — so
285
291
 
286
292
  **Bundled contracts** — the wheel ships the machine contracts used for offline validation:
287
293
  `pack.schema.json`, `skill.schema.json`, `guide.schema.json`, `skill-manifest.schema.json`,
288
- `profile.schema.json`, `catalogue.schema.json`, `target-vocab.toml`, and the adapter
289
- contract files. All are available without network access via `importlib.resources`.
294
+ `profile.schema.json`, `catalogue.schema.json`, `plugin-manifest.schema.json`,
295
+ `plugin-manifest.derived.schema.json`, `marketplace-entry.schema.json`,
296
+ `target-vocab.toml`, and the adapter contract files. All are available without network access via `importlib.resources`.
290
297
 
291
298
  **Lint your catalogue** — shallow structural checks run without extra dependencies:
292
299
 
@@ -301,13 +308,30 @@ pip install 'agentbundle[lint]'
301
308
  agentbundle catalogue lint --root . --deep
302
309
  ```
303
310
 
304
- **Check self-host projection drift** — verifies that adapter-projected files (skills, agents, hooks) match their `.apm/` sources without writing anything:
311
+ **Verify a catalogue before you ship it** — runs the full read-only contract and
312
+ self-host checks in one command:
313
+
314
+ ```bash
315
+ agentbundle catalogue verify --root .
316
+ ```
317
+
318
+ For a self-host-enabled catalogue with `.adapt-discovery.toml`, that includes the
319
+ self-host classifier. It treats generated targets as projected and known
320
+ repository-owned paths as excluded from projection. A genuinely unknown Git-visible
321
+ path remains an informational `unclassified` notice; it does not fail an otherwise
322
+ clean catalogue. Missing, modified, or orphaned projections do fail verification,
323
+ including generated executables under `.agentbundle/bin/` and vendored user libraries
324
+ under `.agentbundle/lib/`. Git filenames are read losslessly, and a failed Git listing
325
+ is reported as a warning rather than mistaken for a fully classified inventory.
326
+
327
+ To check only self-host projection drift, or to regenerate projected files locally:
305
328
 
306
329
  ```bash
307
330
  agentbundle catalogue self-host --check --root .
331
+ agentbundle catalogue self-host --write --root .
308
332
  ```
309
333
 
310
- Exits non-zero if any projected file is out of date. Safe to run in CI as a required check; use `--write` locally to regenerate.
334
+ Both check commands are safe to run in CI. The write command changes projected files.
311
335
 
312
336
  By default projects for `claude-code` and `codex`. Downstream repos that use a single adapter (e.g. `kiro-ide`) can declare it in `catalogue.toml` — only that adapter is then projected, and its output files participate in the drift check:
313
337
 
@@ -247,7 +247,13 @@ ignore them there; the separation is structural, not incidental.
247
247
  `keywords` once; the build projects the cleanly-mappable subset — plus the
248
248
  pack's `README.md` — into each distribution route's manifest (the `plugin.json`
249
249
  / `marketplace.json` entry), so the catalogue describes each pack richly rather
250
- than with a single sentence. Extra fields stay in `pack.toml`; the projection
250
+ than with a single sentence. The Claude-plugin route carries only packs whose
251
+ `[pack.install] allowed-scopes` admits `user`: a plugin's code lands in the
252
+ adopter's global cache, so a repo-only pack gets no `marketplace.json` entry
253
+ and is reached with `agentbundle install` instead. A marketplace entry's `source` is a `git-subdir`
254
+ object (`url`, `path`, and one of `ref`/`sha`) pointing at the pack's directory
255
+ on the published distribution branch, and every entry is schema-validated at
256
+ build time against `marketplace-entry.schema.json`. Extra fields stay in `pack.toml`; the projection
251
257
  is deliberately lossy per tool.
252
258
 
253
259
  Point a catalogue URI (a git URL or a local path) at the repo that holds your packs. Then `validate` a pack against the adapter contract, `render` it to preview the projection, and `install` it into a target repo. `scaffold` drops a pack's seeds into a fresh directory to start from. The build pipeline (`agentbundle.build`) is the same engine `make build` runs.
@@ -263,8 +269,9 @@ The org hint fires after the user-config but before the on-disk IDE probe — so
263
269
 
264
270
  **Bundled contracts** — the wheel ships the machine contracts used for offline validation:
265
271
  `pack.schema.json`, `skill.schema.json`, `guide.schema.json`, `skill-manifest.schema.json`,
266
- `profile.schema.json`, `catalogue.schema.json`, `target-vocab.toml`, and the adapter
267
- contract files. All are available without network access via `importlib.resources`.
272
+ `profile.schema.json`, `catalogue.schema.json`, `plugin-manifest.schema.json`,
273
+ `plugin-manifest.derived.schema.json`, `marketplace-entry.schema.json`,
274
+ `target-vocab.toml`, and the adapter contract files. All are available without network access via `importlib.resources`.
268
275
 
269
276
  **Lint your catalogue** — shallow structural checks run without extra dependencies:
270
277
 
@@ -279,13 +286,30 @@ pip install 'agentbundle[lint]'
279
286
  agentbundle catalogue lint --root . --deep
280
287
  ```
281
288
 
282
- **Check self-host projection drift** — verifies that adapter-projected files (skills, agents, hooks) match their `.apm/` sources without writing anything:
289
+ **Verify a catalogue before you ship it** — runs the full read-only contract and
290
+ self-host checks in one command:
291
+
292
+ ```bash
293
+ agentbundle catalogue verify --root .
294
+ ```
295
+
296
+ For a self-host-enabled catalogue with `.adapt-discovery.toml`, that includes the
297
+ self-host classifier. It treats generated targets as projected and known
298
+ repository-owned paths as excluded from projection. A genuinely unknown Git-visible
299
+ path remains an informational `unclassified` notice; it does not fail an otherwise
300
+ clean catalogue. Missing, modified, or orphaned projections do fail verification,
301
+ including generated executables under `.agentbundle/bin/` and vendored user libraries
302
+ under `.agentbundle/lib/`. Git filenames are read losslessly, and a failed Git listing
303
+ is reported as a warning rather than mistaken for a fully classified inventory.
304
+
305
+ To check only self-host projection drift, or to regenerate projected files locally:
283
306
 
284
307
  ```bash
285
308
  agentbundle catalogue self-host --check --root .
309
+ agentbundle catalogue self-host --write --root .
286
310
  ```
287
311
 
288
- Exits non-zero if any projected file is out of date. Safe to run in CI as a required check; use `--write` locally to regenerate.
312
+ Both check commands are safe to run in CI. The write command changes projected files.
289
313
 
290
314
  By default projects for `claude-code` and `codex`. Downstream repos that use a single adapter (e.g. `kiro-ide`) can declare it in `catalogue.toml` — only that adapter is then projected, and its output files participate in the drift check:
291
315
 
@@ -188,12 +188,14 @@ install-routes = ["cli", "claude-plugins", "apm"]
188
188
  primitive = "skill"
189
189
  mode = "direct-directory"
190
190
  target-path = ".claude/skills/"
191
+ plugin-target-path = "skills/"
191
192
  on-conflict = "prompt-then-preserve"
192
193
 
193
194
  [[adapter."claude-code".projection]]
194
195
  primitive = "agent"
195
196
  mode = "direct-file"
196
197
  target-path = ".claude/agents/"
198
+ plugin-target-path = "agents/"
197
199
  on-conflict = "prompt-then-preserve"
198
200
 
199
201
  [[adapter."claude-code".projection]]
@@ -213,6 +215,7 @@ on-conflict = "merge-managed-key-only"
213
215
  primitive = "command"
214
216
  mode = "direct-file"
215
217
  target-path = ".claude/commands/"
218
+ plugin-target-path = "commands/"
216
219
  on-conflict = "prompt-then-preserve"
217
220
 
218
221
  # hook-body — scope-conditional target.
@@ -36,6 +36,62 @@ and `version`; `[pack.adapter-contract]` with `version`.
36
36
  - [packs/README.md](../../../packs/README.md) — pack layout, versioning rules, lint commands.
37
37
  - [packs/AGENTS.md](../../../packs/AGENTS.md) — agent-facing schema map and primary workflow.
38
38
 
39
+ ### `[pack].description` — display copy
40
+
41
+ `[pack].description` is read by a **person** deciding whether to install: in a
42
+ marketplace browser, a catalogue listing, or CLI output. It is not read by the model.
43
+
44
+ That makes it a different artifact from a skill's or agent's `description`, which the
45
+ model reads to decide whether to **activate** the primitive. Length is load-bearing
46
+ there and shortening it degrades activation; per-target caps in
47
+ `contracts/target-vocab.toml` govern it. Do not carry habits from one to the other.
48
+
49
+ **Shape.** Open on the job the adopter accomplishes, in their words. Say what they
50
+ get. Then stop.
51
+
52
+ Beyond that, let the pack decide the shape. Some need one sentence:
53
+
54
+ > Write the OpenAPI or AsyncAPI contract before anyone writes a handler.
55
+
56
+ Others earn three:
57
+
58
+ > Ship it the way production will see it. Deploy to a throwaway environment, run
59
+ > the tests, read the telemetry, and go round again until it holds. Then it stops
60
+ > and asks, because the next step is the one you cannot undo.
61
+
62
+ **Do not run every description through one mould.** An earlier pass here applied a
63
+ single shape — verb-first sentence, then a comma-list of capabilities — to all
64
+ 22 packs. The result measured as 20 of 22 at exactly two sentences, 14 with a
65
+ comma-list second sentence, four opening on the identical "Turn X into Y" frame.
66
+ Every fact was right and the set still read as machine-written, because uniform
67
+ rhythm and symmetrical construction are themselves the tell. Vary sentence count
68
+ and length to match what each pack actually is.
69
+
70
+ Reach for the concrete. A real thing the user says (`"convert this PDF to
71
+ Markdown"`), a real artifact (`a Google-style design doc`), or a real check
72
+ (`the empty state and the focus ring you forgot`) all beat an abstract capability
73
+ noun.
74
+
75
+ **Anti-patterns**, each of which has shipped here before:
76
+
77
+ | Anti-pattern | Instead |
78
+ | --- | --- |
79
+ | Opening with a component inventory (`Core: work-loop, new-spec, bug-fix, …`) | Lead with the outcome; the inventory belongs in the README |
80
+ | Repo-insider vocabulary (`forked-context`, `the grown-up successor to X`) | Words a first-time reader resolves without this repo |
81
+ | Cross-pack references (`co-installs with X`, `feeds Y's queue`) | Nothing that assumes another pack is known |
82
+ | Provenance name-drops (`(STORM, PRISMA, ACH, GRADE)`) | Name a framework only when the reader is buying it |
83
+ | Negative space (`no stack, no values tables, no pixel comps`) | Say what it does; the README can scope what it doesn't |
84
+ | Internal paths (`~/.agentbundle/bin/`) | Nothing an adopter cannot act on from a listing |
85
+
86
+ **Discoverability is carried elsewhere** — `[pack].keywords` and `[pack].categories`
87
+ — so prose does not need to be keyword-dense to stay findable.
88
+
89
+ There is no target-imposed length limit. `tools/lint-pack-descriptions.py` fails the
90
+ build past a deliberately loose backstop, which exists only to stop runaway drift (the
91
+ field once reached 1122 characters); it is not the quality bar. **This section is.** A
92
+ description inside the backstop can still be bad, and a length check cannot tell —
93
+ review against the shape and the table above.
94
+
39
95
  ---
40
96
 
41
97
  ## 3. Pack README standard
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "files": {
3
- "guides/_shared/reference/catalogue-authoring-standards.md": "cec0f7afd7a0bf61d7bedd9facd096b083cb7413679a6fbcd0af28d40bece58c",
3
+ "guides/_shared/reference/catalogue-authoring-standards.md": "ff4bdc203affda0a2f41641c1155b3f4e90aade022e122999a82e7093e94d2ee",
4
4
  "guides/_shared/reference/catalogue-ci-contract.md": "fae793ab47989b6dd3c731afcb0b4cda21c7d08bbe7fa40fca6d3abb3d158141",
5
- "packs/AGENTS.md": "861a029007741cd7645cdfcc018e5ac85cf218d332f6688115156c9c676381b8",
5
+ "packs/AGENTS.md": "9bfeda4069826e518a4f4fdae45bd40f4393c1c5aaf146149d3eb4aece30be83",
6
6
  "packs/README.md": "d2de5f9556f4c8e521103474bde74636e02cc805f9bf5312a3a61a1f3eb45d8f",
7
7
  "packs/_example/.apm/skills/example-skill/SKILL.md": "33be8cbf4b2c014a5a63e788b7d0b350ad8061c6c4b2579369a3e984135bf9f1",
8
8
  "packs/_example/.apm/skills/example-skill/evals/eval_queries.json": "f41ecda0834a22f3b4823c81ab02e58b0e4beba610e3b40c185579abff9cfeee",
9
- "packs/_example/.claude-plugin/plugin.json": "d38c8456640d5e2f9296361f7e3e675e368ddd3eb22a6245267560f460155f41",
9
+ "packs/_example/.claude-plugin/plugin.json": "7f94084a56260eed225ec47ac82042fe9029c594557cca760a459885d03ccc26",
10
10
  "packs/_example/README.md": "ef130c9c1f97a75a07cc59fc55fc5b3bf71846dd2f06c3cddf5a1154023b6ef6",
11
- "packs/_example/pack.toml": "94e2c7fd25d4d115858728c7ecb8f739adfd0f594b29596c106daba0791a4b94",
11
+ "packs/_example/pack.toml": "f19a0be9fca99a6a0e3ce2d5cd188bf98000f0a4f186954fb42a0c99c6859701",
12
12
  "profiles/AGENTS.md": "8950438d9e0f57c13613104c88c90b598b53196c91bfdced6d63e4e01b5e9254",
13
13
  "profiles/README.md": "a0b0268edc57fc575f7fea08f3363ba7da9f6e67dc4a0a6f8bd38c9668c97922",
14
14
  "profiles/_example/README.md": "164494df049ddce3e57cb44ed604cb3d39be0a6ec2072e609ea3fa4bbfc74c72",
@@ -6,9 +6,11 @@ Read `packs/AGENTS.local.md` when present — it carries host-specific overrides
6
6
  ## Pack layout
7
7
 
8
8
  The pack is the ownership and test-execution boundary; `.apm/` is the runtime export boundary; a skill is the
9
- evaluation-fixture boundary. Tests go in `packs/<pack>/tests/`, never under `.apm/`; evals stay skill-local at
10
- `.apm/skills/<skill>/evals/` and project with the skill. `tools/lint-pack-test-boundary.py` enforces both across
11
- every pack, plus one pytest process per skill test dir. Shape: `docs/architecture/pack-layout.md`.
9
+ evaluation-fixture boundary. Skill tests go in `packs/<pack>/tests/skills/<skill>/` and hook tests in
10
+ `packs/<pack>/tests/hooks/` — never under `.apm/`, which adapters project verbatim into an installed tree.
11
+ Evals stay skill-local at `.apm/skills/<skill>/evals/` and project with the skill. Run **one pytest process
12
+ per skill test directory**: two skills may ship the same test basename, and a shared `sys.path` binds one
13
+ skill's module for both suites and passes green.
12
14
 
13
15
  | Path | Purpose |
14
16
  |------|---------|
@@ -28,14 +30,13 @@ every pack, plus one pytest process per skill test dir. Shape: `docs/architectur
28
30
 
29
31
  ## Reserved authoring assets
30
32
 
31
- Any immediate child of the packs root whose name begins with `_` is a reserved authoring asset.
32
- Reserved directories are not catalogue payload — they do not appear in `list-packs`, are not
33
- installed, and are not included in packaged archives. See `packs/README.md`.
33
+ Any immediate child of the packs root whose name begins with `_` is a reserved authoring asset: not
34
+ catalogue payload, absent from `list-packs`, never installed or packaged. See `packs/README.md`.
34
35
 
35
36
  ## pack.toml schema map
36
37
 
37
- > The machine source of truth for pack.toml format is `contracts/pack.schema.json`.
38
- > The table below is a navigational summary; the JSON Schema is normative.
38
+ > The normative source for pack.toml format is the `pack` JSON Schema that ships inside `agentbundle`;
39
+ > `agentbundle catalogue lint --root .` validates against it. The table below is a navigational summary.
39
40
 
40
41
  | Table | Required fields | Notable optional fields |
41
42
  |-------|----------------|------------------------|
@@ -64,14 +65,14 @@ For CI pipeline orchestration — publication ordering, exit codes, JSON output
64
65
 
65
66
  ## Version bump rule
66
67
 
67
- Every **non-cosmetic** change to pack content requires a version bump in both:
68
- 1. `pack.toml` → `[pack] version`
69
- 2. `.claude-plugin/plugin.json` → `"version"`
68
+ Every **non-cosmetic** change to pack content requires a version bump in **both** `pack.toml` →
69
+ `[pack] version` and `.claude-plugin/plugin.json` → `"version"`.
70
70
 
71
71
  Which increment: **patch** for changed bodies/directives/conventions; **minor** for new primitives;
72
72
  **major** for removals. Never ride an unreleased version from another in-flight PR.
73
73
 
74
- Host-specific post-bump steps (changelog, marketplace regeneration) are in `packs/AGENTS.local.md`.
74
+ Further post-bump steps (changelog, marketplace regeneration) are host-specific — see `AGENTS.local.md` if
75
+ your catalogue has one.
75
76
 
76
77
  ## Self-hosting projection
77
78
 
@@ -80,52 +81,50 @@ projects them to every shipped adapter's layout. Never edit a projected output d
80
81
 
81
82
  On a dirty working tree: `agentbundle catalogue self-host --root . --write --force`.
82
83
 
83
- **Critical ordering:** when a session edits both seeds and non-seed pack sources (`.apm/**`,
84
- `pack.toml`), run self-host AFTER all edits — not between them.
84
+ **Critical ordering:** when a session edits both seeds and non-seed pack sources (`.apm/**`, `pack.toml`),
85
+ run self-host AFTER all edits — not between them.
85
86
 
86
87
  ## Claude plugin JSON format
87
88
 
88
- Each pack's `.claude-plugin/plugin.json` is validated against `contracts/plugin-manifest.schema.json`.
89
- Non-compliant manifests block publishing.
89
+ Each pack's `.claude-plugin/plugin.json` is validated against the plugin-manifest JSON Schema that ships
90
+ inside `agentbundle`; `agentbundle catalogue lint --root .` reports violations. Non-compliant manifests
91
+ block publishing.
90
92
 
91
93
  **Required:** `name` (string), `version` (string matching `pack.toml`), `description` (string).
92
94
 
93
- **Allowed optional fields** — `skills`, `agents` (arrays of strings); `author` (`{name, email?}`);
94
- `license`, `homepage`, `repository`, `category`, `displayName` (strings); `keywords` (array);
95
- `source` (`{source, repo, branch, directory}`).
95
+ **Allowed optional fields** — `skills`, `agents` (arrays of strings); `author` (`{name, email?}`); `license`,
96
+ `homepage`, `repository`, `category`, `displayName` (strings); `keywords` (array); `source`
97
+ (`{source, repo, branch, directory}`).
96
98
 
97
99
  `additionalProperties: false` — any unknown key fails validation.
98
100
 
99
101
  ## Authoring or editing a skill
100
102
 
101
- `README.md` states pack intent and the user journey it serves — not a contributor capability list.
102
- Edit `.apm/skills/<name>/SKILL.md`. Run self-host to project. Run
103
- `agentbundle catalogue lint --root . --deep` to confirm spec compliance.
103
+ `README.md` states pack intent and the user journey it serves — not a contributor capability list. Edit
104
+ `.apm/skills/<name>/SKILL.md`, run self-host to project, then `agentbundle catalogue lint --root . --deep`.
104
105
 
105
- Full authoring standards — frontmatter key whitelist, body structure, naming, three-tier dependency
106
- policy, and evals — live in [`guides/_shared/how-to/author-a-skill.md`](../guides/_shared/how-to/author-a-skill.md).
106
+ Full authoring standards — frontmatter key whitelist, body structure, naming, three-tier dependency policy,
107
+ evals — live in [`catalogue-authoring-standards.md`](../guides/_shared/reference/catalogue-authoring-standards.md).
107
108
 
108
109
  ## Eval coverage
109
110
 
110
111
  A non-cosmetic pack update must also update the pack's eval harness:
111
- - **Tier-A activation** — `evals/eval_queries.json` (~8–10 should-trigger + ~8–10 near-miss) and
112
- a `[pack.evals]` block in `pack.toml` listing every user-triggered skill.
112
+ - **Tier-A activation** — `evals/eval_queries.json` (~8–10 should-trigger + ~8–10 near-miss) and a
113
+ `[pack.evals]` block in `pack.toml` listing every user-triggered skill.
113
114
  - **Tier-4 LLM-judge rubric** — `evals/evals.json` for judgment/authoring skills.
114
115
  - **Tier-B-lite behavior check** — add an `expect` block to an `evals/evals.json` entry (non-destructive,
115
- non-credentialed skills only). Four things that must be explicit to avoid format churn:
116
- - Field name is **`files`** (not `fixture`): paths are relative to the **skill root**
117
- (e.g. `"evals/files/sample.md"`), not relative to `evals/`. The runner seeds a temp workspace
118
- with these files before invoking the skill.
116
+ non-credentialed skills only). Four things must be explicit, or the format churns:
117
+ - The field is **`files`**, not `fixture`, and its paths are relative to the **skill root**
118
+ (`"evals/files/sample.md"`) — not to `evals/`. The runner seeds a temp workspace with them first.
119
119
  - `expect.produces`: filenames the run must create in the workspace.
120
120
  - `expect.output_contains` / `expect.output_excludes`: substrings in captured output.
121
- - **Your skill script must accept the workspace path** — via a `--fixture`/`--root` flag or by
122
- treating CWD as the workspace — so the runner can confine writes and verify `produces`.
123
- Full procedure: [`guides/_shared/how-to/author-a-skill.md`](../guides/_shared/how-to/author-a-skill.md).
121
+ - **Your skill script must accept the workspace path** — a `--fixture`/`--root` flag, or treat CWD as
122
+ the workspace — so the runner can confine writes and verify `produces`.
123
+ Full procedure: [`guides/_shared/reference/catalogue-authoring-standards.md`](../guides/_shared/reference/catalogue-authoring-standards.md).
124
124
 
125
125
  ## Windows-safe Python scripts
126
126
 
127
- Any script under `.apm/` that prints to stdout or stderr must include the UTF-8 reconfigure guard
128
- immediately after `import sys`, before any `print()` call:
127
+ Any script under `.apm/` that prints to stdout or stderr must include the UTF-8 reconfigure guard immediately after `import sys`, before any `print()` call:
129
128
 
130
129
  ```python
131
130
  sys.stdout.reconfigure(encoding="utf-8", errors="strict")
@@ -145,6 +144,7 @@ Stubs in `plan.md` tasks must be `raise NotImplementedError # STUB: ACn` — no
145
144
 
146
145
  ## Shipped pack content carries no internal-governance citations
147
146
 
148
- Anywhere under `packs/` — `.apm/**`, `pack.toml`, `README`/`JOURNEY`/`DESIGN`, `seeds/**` —
149
- never cite this catalogue's own governance: `RFC-0NNN`, `ADR-0NNN`, spec/plan or AC citations,
150
- internal doc paths. Keep the rule, drop the citation. Detail: `packs/AGENTS.local.md`.
147
+ Anywhere under `packs/` — `.apm/**`, `pack.toml`, `README`/`JOURNEY`/`DESIGN`, `seeds/**` — never cite your
148
+ catalogue's own governance: decision-record or proposal numbers, spec/plan or acceptance-criterion citations,
149
+ or repository-only paths. Shipped content lands in someone else's tree, where each is a dead end. **Keep the
150
+ rule, drop the citation** — state what the reader must do, in full.
@@ -1,7 +1,7 @@
1
1
  [pack]
2
2
  name = "example-pack"
3
3
  version = "0.1.0"
4
- description = "A minimal example pack demonstrating the standard authoring layout."
4
+ description = "The skeleton to copy when you start a new pack."
5
5
  display_name = "Example Pack"
6
6
  categories = ["productivity"]
7
7
  keywords = ["example", "template"]
@@ -1,9 +1,13 @@
1
1
  """Canonical stdlib-only install-marker writer for the Claude-plugins and APM install routes.
2
2
 
3
3
  This script is invoked by a ``SessionStart`` hook derived by ``agentbundle build``
4
- into every pack's projected output:
5
- - ``dist/claude-plugins/<pack>/.claude-plugin/plugin.json`` — claude-plugins route
6
- - ``dist/apm/<pack>/.apm/hooks/install-marker.json`` — APM route
4
+ into each pack's projected output:
5
+ - ``dist/claude-plugins/<pack>/.claude-plugin/plugin.json`` — claude-plugins
6
+ route, for user-capable packs only (a plugin's code lands in the adopter's
7
+ global cache, so a pack whose ``allowed-scopes`` omits ``user`` is not
8
+ projected onto this route at all)
9
+ - ``dist/apm/<pack>/.apm/hooks/install-marker.json`` — APM route,
10
+ every pack
7
11
 
8
12
  It detects first-install-or-update and writes a ``[[packs-installed]]`` entry
9
13
  to the scope-correct ``.adapt-install-marker.toml`` file so the existing core
@@ -213,11 +217,66 @@ def _assert_portable_name(component: str) -> None:
213
217
  # ---------------------------------------------------------------------------
214
218
 
215
219
 
220
+ # Settings files are read under an adopter's ``${CLAUDE_PROJECT_DIR}``, whose
221
+ # contents a cloned repo controls. Bound the read itself — measuring after
222
+ # `read_text()` has already pulled the whole file in is no bound at all.
223
+ _SETTINGS_READ_CAP = 1_048_576 # 1 MiB
224
+
225
+
226
+ def _plugin_marketplace(
227
+ plugin_root: pathlib.Path,
228
+ plugin_name: str,
229
+ ) -> "str | None":
230
+ """Return the marketplace this pack was installed from, or ``None``.
231
+
232
+ The Claude-plugins client installs into
233
+ ``<config>/plugins/cache/<marketplace>/<plugin>/<version>``, so the pack
234
+ can read its own marketplace identity back out of ``${CLAUDE_PLUGIN_ROOT}``.
235
+ Verified against Claude Code 2.1.223.
236
+
237
+ Returns ``None`` when ``plugin_root`` does not have that shape. The caller
238
+ then matches on bare pack name alone — a layout we do not recognise must
239
+ degrade to the historical behaviour, not to "never opted in" (that is the
240
+ very failure this function's caller exists to fix).
241
+ """
242
+ parts = plugin_root.parts
243
+ if len(parts) < 4:
244
+ return None
245
+ if parts[-4] != "cache" or parts[-2] != plugin_name:
246
+ return None
247
+ return parts[-3] or None
248
+
249
+
250
+ def _entry_matches(
251
+ entry: str,
252
+ plugin_name: str,
253
+ marketplace: "str | None",
254
+ ) -> bool:
255
+ """Does one ``enabledPlugins`` identifier name *this* pack?
256
+
257
+ Current clients write a qualified ``name@marketplace``; older ones wrote a
258
+ bare ``name``. A qualified identifier must agree on *both* halves whenever
259
+ we know our own marketplace, so another vendor's ``core@other-marketplace``
260
+ does not opt in a ``core`` that came from somewhere else.
261
+ """
262
+ name, sep, entry_marketplace = entry.strip().partition("@")
263
+ if name.strip() != plugin_name:
264
+ return False
265
+ if not sep:
266
+ # Bare name — the identifier makes no marketplace claim to check.
267
+ return True
268
+ if marketplace is None:
269
+ # Our own identity is unknown; the name is all there is to compare.
270
+ return True
271
+ return entry_marketplace.strip() == marketplace
272
+
273
+
216
274
  def _detect_origin(
217
275
  *,
218
276
  plugin_name: str,
219
277
  home: pathlib.Path,
220
278
  project_dir: pathlib.Path | None,
279
+ marketplace: "str | None",
221
280
  ) -> str | None:
222
281
  """Walk the three ``enabledPlugins`` settings files in precedence order.
223
282
 
@@ -226,9 +285,17 @@ def _detect_origin(
226
285
  (pack is not enabled at any scope).
227
286
 
228
287
  Precedence: local → project → user (most-specific wins).
229
- Missing file, malformed JSON, absent ``enabledPlugins`` key, or
230
- ``enabledPlugins`` present but not a JSON array are each treated as
231
- "not opted in at that scope" and fall through.
288
+
289
+ ``enabledPlugins`` carries two shapes. Current clients write an **object**
290
+ keyed by plugin identifier whose boolean value is the enabled state — only
291
+ ``true`` counts as opted in, since ``false`` is an adopter who deliberately
292
+ disabled the pack. Older clients wrote a **JSON array** of identifiers;
293
+ both are accepted.
294
+
295
+ Every other outcome is "not opted in at that scope" and falls through: a
296
+ missing file, a non-regular path, a file past ``_SETTINGS_READ_CAP``,
297
+ undecodable bytes, malformed JSON, an absent ``enabledPlugins`` key, or a
298
+ value of any other type. A hostile settings file must never raise.
232
299
  """
233
300
  candidates: list[tuple[str, pathlib.Path]] = []
234
301
 
@@ -239,32 +306,38 @@ def _detect_origin(
239
306
  candidates.append(("user", home / ".claude" / "settings.json"))
240
307
 
241
308
  for origin, settings_path in candidates:
242
- if not settings_path.exists():
309
+ # `.is_file()` where the old code used `.exists()`: a FIFO exists, and
310
+ # opening one for reading blocks until a writer appears. A character
311
+ # device such as /dev/zero exists too, and reads forever.
312
+ if not settings_path.is_file():
243
313
  continue
244
314
  try:
245
- read_text = settings_path.read_text(encoding="utf-8")
315
+ with settings_path.open("rb") as fh:
316
+ blob = fh.read(_SETTINGS_READ_CAP + 1)
246
317
  except OSError:
247
318
  continue
248
- # Cap read size to 1 MiB to guard against DoS via giant file.
249
- if len(read_text) > 1_048_576:
319
+ if len(blob) > _SETTINGS_READ_CAP:
250
320
  continue
251
321
  try:
252
- raw = json.loads(read_text)
253
- except (json.JSONDecodeError, OSError):
322
+ # `json.loads` decodes UTF-8 itself; undecodable bytes raise
323
+ # UnicodeDecodeError and malformed JSON raises JSONDecodeError,
324
+ # both subclasses of ValueError.
325
+ raw = json.loads(blob)
326
+ except ValueError:
254
327
  continue
255
328
  if not isinstance(raw, dict):
256
329
  continue
257
330
  enabled = raw.get("enabledPlugins")
258
- if not isinstance(enabled, list):
331
+ if isinstance(enabled, dict):
332
+ entries: list = [key for key, value in enabled.items() if value is True]
333
+ elif isinstance(enabled, list):
334
+ entries = enabled
335
+ else:
259
336
  continue
260
- # Check whether this pack is in the list; compare by name prefix
261
- # since installed plugins may appear as "name@marketplace-url".
262
- for entry in enabled:
263
- if not isinstance(entry, str):
264
- continue
265
- # Match by pack name as a prefix component (before '@').
266
- entry_name = entry.split("@")[0].strip()
267
- if entry_name == plugin_name:
337
+ for entry in entries:
338
+ if isinstance(entry, str) and _entry_matches(
339
+ entry, plugin_name, marketplace
340
+ ):
268
341
  return origin
269
342
 
270
343
  return None
@@ -837,6 +910,7 @@ def _main_claude_plugins(args: argparse.Namespace) -> int:
837
910
  plugin_name=pack_name,
838
911
  home=home,
839
912
  project_dir=project_dir,
913
+ marketplace=_plugin_marketplace(plugin_root, pack_name),
840
914
  )
841
915
 
842
916
  if origin is None: