agentbundle 0.21.0__tar.gz → 0.22.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 (165) hide show
  1. {agentbundle-0.21.0 → agentbundle-0.22.0}/PKG-INFO +6 -1
  2. agentbundle-0.21.0/README.md → agentbundle-0.22.0/README-pypi.md +5 -0
  3. agentbundle-0.22.0/README.md +257 -0
  4. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/_data/catalogue.schema.json +4 -0
  5. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_contract.py +4 -4
  6. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_lint_agents_md_diataxis_block.py +3 -1
  7. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_lint_agents_md_legacy_block.py +2 -1
  8. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_lint_agents_md_risk_block.py +3 -1
  9. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_self_host_check.py +5 -0
  10. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_shared_prefix_contract.py +5 -2
  11. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue_tooling/build.py +3 -2
  12. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue_tooling/config.py +13 -0
  13. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue_tooling/defaults.py +1 -2
  14. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/install.py +2 -2
  15. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/scope.py +1 -1
  16. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/source_defaults.py +15 -13
  17. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/version.py +1 -1
  18. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle.egg-info/PKG-INFO +6 -1
  19. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle.egg-info/SOURCES.txt +1 -0
  20. {agentbundle-0.21.0 → agentbundle-0.22.0}/pyproject.toml +2 -2
  21. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/__init__.py +0 -0
  22. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/__main__.py +0 -0
  23. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/_data/adapter.schema.json +0 -0
  24. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/_data/adapter.toml +0 -0
  25. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/_data/install-defaults.toml +0 -0
  26. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/_data/install-marker.py +0 -0
  27. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/_data/pack.schema.json +0 -0
  28. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/_data/plugin-manifest.derived.schema.json +0 -0
  29. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/_data/plugin-manifest.schema.json +0 -0
  30. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/_data/profile.schema.json +0 -0
  31. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/__init__.py +0 -0
  32. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/__main__.py +0 -0
  33. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/adapter_root_bins.py +0 -0
  34. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/adapters/__init__.py +0 -0
  35. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/adapters/claude_code.py +0 -0
  36. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/adapters/codex.py +0 -0
  37. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/adapters/copilot.py +0 -0
  38. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/adapters/cursor.py +0 -0
  39. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/adapters/gemini.py +0 -0
  40. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/adapters/kiro.py +0 -0
  41. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/adapters/kiro_cli.py +0 -0
  42. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/adapters/kiro_ide.py +0 -0
  43. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/contract.py +0 -0
  44. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/lint_packs.py +0 -0
  45. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/main.py +0 -0
  46. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/phase_order.py +0 -0
  47. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/projections/__init__.py +0 -0
  48. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/projections/codex_agent_toml.py +0 -0
  49. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/projections/copilot_agent_md.py +0 -0
  50. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/projections/copilot_hooks_json.py +0 -0
  51. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/projections/direct_directory.py +0 -0
  52. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/projections/gemini_command_toml.py +0 -0
  53. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/projections/hook_id.py +0 -0
  54. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/projections/kiro_ide_hook.py +0 -0
  55. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/projections/merge_into_agent_json.py +0 -0
  56. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/projections/merge_json.py +0 -0
  57. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/projections/user_merge_json.py +0 -0
  58. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/recipes/composite-agents-md.toml +0 -0
  59. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/recipes/composite-marketplace.toml +0 -0
  60. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/recipes/marketplace.toml +0 -0
  61. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/recipes/per-pack-apm-package.toml +0 -0
  62. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/recipes/per-pack-claude-plugin.toml +0 -0
  63. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/recipes/per-pack-overlay.toml +0 -0
  64. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/recipes/self-host.toml +0 -0
  65. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/scope_rails.py +0 -0
  66. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/self_host.py +0 -0
  67. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/shared_libs.py +0 -0
  68. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/target_resolver.py +0 -0
  69. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/__init__.py +0 -0
  70. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_adapter_claude_code.py +0 -0
  71. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_adapter_codex.py +0 -0
  72. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_adapter_copilot.py +0 -0
  73. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_adapter_cursor.py +0 -0
  74. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_adapter_gemini.py +0 -0
  75. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_adapter_kiro.py +0 -0
  76. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_adapter_kiro_alias.py +0 -0
  77. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_adapter_kiro_cli.py +0 -0
  78. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_adapter_kiro_ide.py +0 -0
  79. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_adapter_root_bins_projection.py +0 -0
  80. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_architect_design_reviewer_projection.py +0 -0
  81. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_architect_design_reviewer_rubric_parity.py +0 -0
  82. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_build_ships_seeds.py +0 -0
  83. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_contract_scope.py +0 -0
  84. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_contract_v07.py +0 -0
  85. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_contract_v08.py +0 -0
  86. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_direct_directory_cleanup.py +0 -0
  87. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_end_to_end_build.py +0 -0
  88. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_lint_packs.py +0 -0
  89. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_load_pack_hook_wiring_safely.py +0 -0
  90. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_pack_schema.py +0 -0
  91. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_pack_schema_allowed_adapters.py +0 -0
  92. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_pack_schema_install.py +0 -0
  93. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_pipeline.py +0 -0
  94. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_plugin_manifest_schema.py +0 -0
  95. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_projectable_subset.py +0 -0
  96. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_projections_merge_json.py +0 -0
  97. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_scope_rails.py +0 -0
  98. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_security.py +0 -0
  99. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_self_host_fixture_guard.py +0 -0
  100. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_self_host_recipe_config.py +0 -0
  101. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_shared_libs_projection.py +0 -0
  102. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_shipped_packs_v07_declarations.py +0 -0
  103. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_shipped_packs_v08_declarations.py +0 -0
  104. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_user_libs_projection.py +0 -0
  105. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_validate.py +0 -0
  106. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/tests/test_writers_emit_lf.py +0 -0
  107. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/user_libs.py +0 -0
  108. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/build/validate.py +0 -0
  109. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue.py +0 -0
  110. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue_tooling/__init__.py +0 -0
  111. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue_tooling/archive.py +0 -0
  112. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue_tooling/diagnostics.py +0 -0
  113. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue_tooling/lint.py +0 -0
  114. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue_tooling/package.py +0 -0
  115. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue_tooling/results.py +0 -0
  116. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue_tooling/self_host.py +0 -0
  117. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue_tooling/skill_spec_lint.py +0 -0
  118. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/catalogue_tooling/verify.py +0 -0
  119. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/categories.py +0 -0
  120. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/cli.py +0 -0
  121. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/__init__.py +0 -0
  122. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/_common.py +0 -0
  123. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/_drop_warning.py +0 -0
  124. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/adapt.py +0 -0
  125. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/catalogue_build.py +0 -0
  126. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/catalogue_lint.py +0 -0
  127. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/catalogue_package.py +0 -0
  128. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/catalogue_self_host.py +0 -0
  129. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/catalogue_sync_defaults.py +0 -0
  130. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/catalogue_tooling_stub.py +0 -0
  131. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/catalogue_verify.py +0 -0
  132. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/config.py +0 -0
  133. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/diff.py +0 -0
  134. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/docs.py +0 -0
  135. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/init_state.py +0 -0
  136. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/list_installed.py +0 -0
  137. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/list_packs.py +0 -0
  138. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/list_profiles.py +0 -0
  139. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/list_targets.py +0 -0
  140. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/oplog_cmd.py +0 -0
  141. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/pack_config_cmd.py +0 -0
  142. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/pack_evals.py +0 -0
  143. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/package_catalogue.py +0 -0
  144. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/profile.py +0 -0
  145. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/reconcile.py +0 -0
  146. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/render.py +0 -0
  147. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/scaffold.py +0 -0
  148. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/show.py +0 -0
  149. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/uninstall.py +0 -0
  150. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/upgrade.py +0 -0
  151. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/commands/validate.py +0 -0
  152. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/config.py +0 -0
  153. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/https_catalogue.py +0 -0
  154. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/oplog.py +0 -0
  155. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/pack_inventory.py +0 -0
  156. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/render.py +0 -0
  157. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/safety.py +0 -0
  158. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/statelock.py +0 -0
  159. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle/user_config.py +0 -0
  160. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle.egg-info/dependency_links.txt +0 -0
  161. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle.egg-info/entry_points.txt +0 -0
  162. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle.egg-info/requires.txt +0 -0
  163. {agentbundle-0.21.0 → agentbundle-0.22.0}/agentbundle.egg-info/top_level.txt +0 -0
  164. {agentbundle-0.21.0 → agentbundle-0.22.0}/setup.cfg +0 -0
  165. {agentbundle-0.21.0 → agentbundle-0.22.0}/tests/test_linear_primitive.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentbundle
3
- Version: 0.21.0
3
+ Version: 0.22.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
@@ -175,6 +175,11 @@ Developers installing from your fork get the internal channel without a manual
175
175
  A malformed `enabled = true` config fails closed — no silent fallback to the public
176
176
  source.
177
177
 
178
+ **Offline and air-gapped hosts:** set `AGENTBUNDLE_NO_REMOTE=1` to skip the org
179
+ Artifactory bootstrap and editable-install detection entirely. `agentbundle` falls
180
+ straight through to the packaged default, so hosts without network access to Artifactory
181
+ still resolve a source without errors.
182
+
178
183
  See the full enterprise adoption guide at
179
184
  `docs/guides/_shared/how-to/use-an-artifactory-catalogue.md` for all six flows
180
185
  (org bootstrap, repo-scope CI upgrade, user-scope MDM, source-conflict remediation,
@@ -153,6 +153,11 @@ Developers installing from your fork get the internal channel without a manual
153
153
  A malformed `enabled = true` config fails closed — no silent fallback to the public
154
154
  source.
155
155
 
156
+ **Offline and air-gapped hosts:** set `AGENTBUNDLE_NO_REMOTE=1` to skip the org
157
+ Artifactory bootstrap and editable-install detection entirely. `agentbundle` falls
158
+ straight through to the packaged default, so hosts without network access to Artifactory
159
+ still resolve a source without errors.
160
+
156
161
  See the full enterprise adoption guide at
157
162
  `docs/guides/_shared/how-to/use-an-artifactory-catalogue.md` for all six flows
158
163
  (org bootstrap, repo-scope CI upgrade, user-scope MDM, source-conflict remediation,
@@ -0,0 +1,257 @@
1
+ # agentbundle
2
+
3
+ **The installer for [agent-ready-repo](https://github.com/eugenelim/agent-ready-repo).** Think npm, but for the skills, subagents, and hooks your coding agent runs on. One pack, one command, every major agent — Claude Code, Codex, Cursor, Copilot, Gemini, and Kiro (both the CLI and the IDE).
4
+
5
+ ## Quick start
6
+
7
+ ```bash
8
+ pip install -e packages/agentbundle
9
+ ```
10
+
11
+ Requires Python 3.11+. Runs on macOS, Linux, and Windows.
12
+
13
+ **Install into a repo** — so everyone who clones it gets the pack. `core` is the flagship pack, the loop itself:
14
+
15
+ ```bash
16
+ agentbundle install --pack core
17
+ ```
18
+
19
+ No catalogue argument needed: it defaults to the agent-ready-repo catalogue. It lands in the repo's agent config — subagents and skills included — and you commit it like any other project file. This is the default scope: the pack belongs to the project and the whole team.
20
+
21
+ **Install for yourself, everywhere** — so a pack follows you across every project, with no per-repo setup:
22
+
23
+ ```bash
24
+ agentbundle install --pack desk-research --scope user
25
+ ```
26
+
27
+ User-scope packs land in your home directory, not the repo — they're yours, not the team's, and they're there in every project you open.
28
+
29
+ The install auto-detects your agent (`--adapter` overrides). Multi-IDE? Install the same pack for each agent at the same scope — they coexist, and the agents that read `.agents/skills/` (codex, cursor, gemini, copilot) share one skill copy instead of fighting over it. To install from a **different** catalogue, pass it as a trailing argument — a git URL or a local path (`agentbundle install --pack core <catalogue>`); a `config set source <catalogue>` makes that the default, and an editable clone (`pip install -e`) defaults to itself.
30
+
31
+ ## More commands
32
+
33
+ ```bash
34
+ # See what the catalogue offers (bare uses the default; or name one explicitly)
35
+ agentbundle list-packs
36
+ agentbundle list-profiles
37
+
38
+ # See what a single pack contains — skills and agents, derived live from its tree
39
+ agentbundle show core
40
+ agentbundle show core --format json # stable object for scripts/agents
41
+
42
+ # See what YOU have installed — pack, adapter, scope, version, and whether
43
+ # an upgrade is available (both scopes by default)
44
+ agentbundle list-installed
45
+ agentbundle list-installed --no-check # skip the catalogue check (offline, fast)
46
+ agentbundle list-installed --check-drift # also count locally edited files
47
+ agentbundle list-installed --format json # machine-readable JSON (schema_version 1)
48
+ agentbundle list-installed --updates-only # show only rows needing attention
49
+
50
+ # Install a whole curated profile — a single-scope set of packs — in one command
51
+ agentbundle install --profile inception
52
+
53
+ # Preview any install without writing a file
54
+ agentbundle install --pack core --dry-run
55
+
56
+ # Upgrade to the version the catalogue ships — shows installed → target, asks first
57
+ agentbundle upgrade --pack core
58
+ agentbundle upgrade --pack core --yes # skip the prompt (CI)
59
+
60
+ # Uninstall — previews remove (Tier-1) vs keep (your edits), asks first
61
+ agentbundle uninstall --pack core --dry-run
62
+ agentbundle uninstall --pack core --yes
63
+ ```
64
+
65
+ **`list-installed`** reads your state files (not the catalogue) and reports every installed `(pack, adapter)` at each scope with its version and a four-value status — `up-to-date`, `upgrade-available`, `ahead` (installed version is newer than catalogue), or `unknown`; it degrades to `unknown` (never an error) when the catalogue can't be resolved, and `--no-check` skips the check entirely. `--format json` emits a stable JSON contract (`schema_version: 1`) to stdout — useful for CI automation of upgrade decisions. `--updates-only` hides `up-to-date` rows.
66
+
67
+ **`show <pack>`** answers "what skills and agents does this pack contain?" by walking the pack's source tree live on each call — so the answer can't drift, and nothing is persisted. `--format json` emits a stable object (`name`, `version`, `description`, `skills`, `agents`, `source`) for scripts and agents. When the catalogue can't be resolved, an *installed* pack still reports its inventory from your state files (marked `source: installed-state`); a not-installed pack errors.
68
+
69
+ A **profile** is a catalogue-curated, single-scope set of packs you install in one command — it declares its own scope, so `--scope` doesn't apply. **Upgrade takes no version** — the target is whatever the catalogue you point at declares; to pin a past version, point the catalogue at that git ref. Install a pack that's **already there** and `agentbundle` offers to `upgrade` it instead (`--yes` runs it straight away).
70
+
71
+ **Mutating commands ask first.** `uninstall`, the `--force` cleanup, and the upgrade offer all preview what they'll do and confirm before touching anything; `--dry-run` previews without writing, and `--yes` skips the prompt for non-interactive / CI use (where, without it, they refuse rather than hang).
72
+
73
+ ## Enterprise distribution
74
+
75
+ For organizations running an internal Artifactory mirror or any static HTTPS server,
76
+ agentbundle's enterprise distribution capabilities handle the full adoption loop —
77
+ from org-wide channel configuration to CI-driven bulk upgrades.
78
+
79
+ **Install from an internal Artifactory channel:**
80
+
81
+ ```bash
82
+ # Point agentbundle at your org's channel descriptor (one-time per machine,
83
+ # or pre-configured in your org fork — see Org bootstrap below)
84
+ agentbundle config set source catalogue+https://artifactory.example.test/agentbundle/catalogues/core/channels/stable.json
85
+
86
+ agentbundle install --pack core
87
+ ```
88
+
89
+ The channel descriptor points to an immutable versioned archive; agentbundle
90
+ fetches, verifies its SHA-256 digest, and installs. Pass a bearer token via
91
+ `AGENTBUNDLE_HTTP_BEARER_TOKEN` — it is never stored in state, never printed, and
92
+ never forwarded to a different host.
93
+
94
+ **JSON output for CI pipelines:**
95
+
96
+ ```bash
97
+ # See what's installed and what needs upgrading — machine-readable
98
+ agentbundle list-installed --format json
99
+ agentbundle list-installed --format json --updates-only
100
+ ```
101
+
102
+ Returns a stable JSON contract (`schema_version` 1) with per-row status
103
+ (`up-to-date` / `upgrade-available` / `ahead` / `unknown`) and machine-readable
104
+ reason codes for unknown rows. Pipe into `jq` or your CI annotation step.
105
+
106
+ **Bulk upgrade in one scoped command:**
107
+
108
+ ```bash
109
+ # Upgrade all installed packs in a scope — preflights before any write
110
+ agentbundle upgrade --all --scope repo --yes
111
+ agentbundle upgrade --all --scope user --format json --yes
112
+ ```
113
+
114
+ Preflights all rows before writing anything; a blocked row stops the run before the
115
+ filesystem is touched. Partial failure is reported honestly — not described as a
116
+ rollback. Never silently downgrades an `ahead` row.
117
+
118
+ **Package your catalogue for Artifactory:**
119
+
120
+ ```bash
121
+ agentbundle package-catalogue \
122
+ --root /path/to/catalogue \
123
+ --bundle my-packs \
124
+ --release 1.0.0 \
125
+ --channel stable \
126
+ --output dist/
127
+ ```
128
+
129
+ Produces a deterministic, reproducible gzip archive (versioned) and a mutable channel
130
+ descriptor JSON (`stable.json`), ready to upload to Artifactory. Identical inputs
131
+ produce byte-identical archives (honors `SOURCE_DATE_EPOCH`).
132
+
133
+ **Org bootstrap — ship the default channel in your fork:**
134
+
135
+ Add an `[organization.artifactory]` block to
136
+ `agentbundle/_data/install-defaults.toml` in your org's agentbundle fork:
137
+
138
+ ```toml
139
+ [organization.artifactory]
140
+ enabled = true
141
+ base-url = "https://artifactory.example.test"
142
+ repository = "agentbundle"
143
+ bundle = "core"
144
+ channel = "stable"
145
+ ```
146
+
147
+ Developers installing from your fork get the internal channel without a manual
148
+ `config set source` step. The block ships `enabled = false` in the public package.
149
+ A malformed `enabled = true` config fails closed — no silent fallback to the public
150
+ source.
151
+
152
+ See the full enterprise adoption guide at
153
+ `docs/guides/_shared/how-to/use-an-artifactory-catalogue.md` for all six flows
154
+ (org bootstrap, repo-scope CI upgrade, user-scope MDM, source-conflict remediation,
155
+ disconnected hosts, and security controls).
156
+
157
+ ## Build your own catalogue
158
+
159
+ `agentbundle` isn't tied to the agent-ready-repo catalogue. Any repo that lays its packs out the same way can use it. A pack is a directory:
160
+
161
+ ```text
162
+ my-pack/
163
+ pack.toml # name, version, adapter-contract, install scope,
164
+ # plus rich metadata (license, maintainers, links,
165
+ # categories, keywords) and a README pointer
166
+ .claude-plugin/
167
+ plugin.json # Claude Code plugin manifest (hand-authored)
168
+ README.md # the pack's portable doc — projected with the pack
169
+ .apm/ # primitives — projected by the build pipeline
170
+ skills/<name>/
171
+ SKILL.md # the skill body; one folder per skill
172
+ references/ # progressive-disclosure docs, loaded on demand
173
+ assets/ # templates the skill copies into the repo
174
+ agents/<name>.md # subagents
175
+ hooks/<name>.py # lifecycle hooks
176
+ seeds/ # files scaffolded into the adopter repo
177
+ ```
178
+
179
+ `pack.toml` is the **single source of truth** for a pack's metadata. Declare
180
+ `license`, `[[pack.maintainers]]`, `[pack.links]`, `categories`, and
181
+ `keywords` once; the build projects the cleanly-mappable subset — plus the
182
+ pack's `README.md` — into each distribution route's manifest (the `plugin.json`
183
+ / `marketplace.json` entry), so the catalogue describes each pack richly rather
184
+ than with a single sentence. Extra fields stay in `pack.toml`; the projection
185
+ is deliberately lossy per tool.
186
+
187
+ 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.
188
+
189
+ **Org adapter default:** If your org ships a private `agentbundle` wheel (or a fork pinned to your internal catalogue), you can set a default adapter for all developers without requiring them to run `agentbundle config set` or pass `--adapter` on every install. Add an `[organization]` table to `_data/install-defaults.toml` in your fork:
190
+
191
+ ```toml
192
+ [organization]
193
+ preferred_adapter = "cursor"
194
+ ```
195
+
196
+ 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.
197
+
198
+ **Lint your catalogue** — shallow structural checks run without extra dependencies:
199
+
200
+ ```bash
201
+ agentbundle catalogue lint --root .
202
+ ```
203
+
204
+ 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`:
205
+
206
+ ```bash
207
+ pip install -e 'packages/agentbundle[lint]'
208
+ agentbundle catalogue lint --root . --deep
209
+ ```
210
+
211
+ **Run Tier-A activation evals** to measure whether each covered skill fires on the prompts it should:
212
+
213
+ ```bash
214
+ agentbundle pack evals run --pack <pack-name> --catalogue-root .
215
+ ```
216
+
217
+ 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).
218
+
219
+ ## Catalogue defaults and pack config API
220
+
221
+ **Catalogue defaults** let operators ship sensible starting values for every pack they distribute. Add a `[pack-defaults.<pack-name>]` table to `catalogue.toml` and the defaults are baked in at publish time. They slot into the three-layer cascade: pack-source defaults → operator defaults → user config. A custom install root is also supported via `[catalogue] user-dir = "~/custom/path"`.
222
+
223
+ **Pack scripts** can resolve their user-scope directory and read the merged config at runtime using the `agentbundle.config` API:
224
+
225
+ ```python
226
+ from agentbundle.config import pack_dir, load_pack_config
227
+
228
+ directory = pack_dir("my-pack") # ~/.agentbundle/my-pack/
229
+ config = load_pack_config("my-pack") # merged dict: pack defaults + operator + user
230
+ ```
231
+
232
+ **Operation log** — scripts can append structured JSONL records to `<pack_dir>/ops.jsonl` for lightweight audit trails:
233
+
234
+ ```python
235
+ from agentbundle.oplog import write_entry
236
+
237
+ write_entry("my-pack", "install", src="git+https://example.com/my-pack")
238
+ ```
239
+
240
+ **CLI commands** to read, write, and inspect pack config and operation logs:
241
+
242
+ ```bash
243
+ agentbundle pack-config show my-pack # all config values for a pack
244
+ agentbundle pack-config get my-pack api-key # single value
245
+ agentbundle pack-config set my-pack api-key v # write to user config.toml
246
+
247
+ agentbundle oplog show my-pack # JSONL operation history
248
+ agentbundle oplog clear my-pack # wipe history (asks first)
249
+ ```
250
+
251
+ ## Credentials
252
+
253
+ `agentbundle` doesn't resolve secrets. Credentialed skills use [`credbroker`](../credbroker/README.md), a standalone resolver that keeps cleartext out of the model's reach.
254
+
255
+ ## Learn more
256
+
257
+ The full story — the loop, the reviewers, the pack catalogue — is in the [agent-ready-repo README](https://github.com/eugenelim/agent-ready-repo#readme).
@@ -171,6 +171,10 @@
171
171
  "bundle": {
172
172
  "type": "string",
173
173
  "description": "Catalogue bundle name (required when enabled)."
174
+ },
175
+ "channel": {
176
+ "type": "string",
177
+ "description": "Artifactory channel name (required when enabled = true)."
174
178
  }
175
179
  }
176
180
  }
@@ -602,10 +602,10 @@ class TestCodexSkillDirectDirectory(unittest.TestCase):
602
602
  self.assertNotIn("managed-block-delimiter-end", entry)
603
603
 
604
604
  def test_contract_files_byte_identical(self) -> None:
605
- self.assertEqual(
606
- CONTRACT_PATH.read_bytes(),
607
- DATA_CONTRACT_PATH.read_bytes(),
608
- )
605
+ def _norm(p: Path) -> bytes:
606
+ return p.read_bytes().replace(b"\r\n", b"\n")
607
+
608
+ self.assertEqual(_norm(CONTRACT_PATH), _norm(DATA_CONTRACT_PATH))
609
609
 
610
610
  def test_seed_agents_md_has_no_legacy_delimiters(self) -> None:
611
611
  text = SEED_AGENTS_MD_PATH.read_text(encoding="utf-8")
@@ -47,12 +47,14 @@ def _make_quadrants(base: Path) -> None:
47
47
 
48
48
 
49
49
  def _run_linter(cwd: Path) -> subprocess.CompletedProcess[str]:
50
+ env = {**os.environ, "PYTHONIOENCODING": "utf-8", "PYTHONUTF8": "1"}
50
51
  return subprocess.run(
51
52
  [sys.executable, str(LINTER)],
52
53
  cwd=cwd,
53
- env=dict(os.environ),
54
+ env=env,
54
55
  capture_output=True,
55
56
  text=True,
57
+ encoding="utf-8",
56
58
  check=False,
57
59
  )
58
60
 
@@ -63,13 +63,14 @@ def _seed_tree(
63
63
 
64
64
 
65
65
  def _run_linter(cwd: Path) -> subprocess.CompletedProcess[str]:
66
- env = dict(os.environ)
66
+ env = {**os.environ, "PYTHONIOENCODING": "utf-8", "PYTHONUTF8": "1"}
67
67
  return subprocess.run(
68
68
  [sys.executable, str(LINTER)],
69
69
  cwd=cwd,
70
70
  env=env,
71
71
  capture_output=True,
72
72
  text=True,
73
+ encoding="utf-8",
73
74
  check=False,
74
75
  )
75
76
 
@@ -52,12 +52,14 @@ def _seed(root: Path, canonical_block: str, agents_block: str) -> None:
52
52
 
53
53
 
54
54
  def _run_linter(cwd: Path) -> subprocess.CompletedProcess[str]:
55
+ env = {**os.environ, "PYTHONIOENCODING": "utf-8", "PYTHONUTF8": "1"}
55
56
  return subprocess.run(
56
57
  [sys.executable, str(LINTER)],
57
58
  cwd=cwd,
58
- env=dict(os.environ),
59
+ env=env,
59
60
  capture_output=True,
60
61
  text=True,
62
+ encoding="utf-8",
61
63
  check=False,
62
64
  )
63
65
 
@@ -21,6 +21,7 @@ import json
21
21
  import os
22
22
  import shutil
23
23
  import subprocess
24
+ import sys
24
25
  import tempfile
25
26
  import unittest
26
27
  from pathlib import Path
@@ -1206,6 +1207,7 @@ class MarketplaceAggregationTests(unittest.TestCase):
1206
1207
  )
1207
1208
 
1208
1209
 
1210
+ @unittest.skipIf(sys.platform == "win32", "symlinks require Developer Mode on Windows")
1209
1211
  class ClaudeSymlinkTests(unittest.TestCase):
1210
1212
  """Unit tests for `_recreate_claude_symlink`."""
1211
1213
 
@@ -1349,6 +1351,7 @@ class ClaudeSymlinkFallbackTests(unittest.TestCase):
1349
1351
  self.assertIn("CLAUDE.md", buf.getvalue())
1350
1352
  self.assertIn("copy", buf.getvalue().lower())
1351
1353
 
1354
+ @unittest.skipIf(sys.platform == "win32", "symlinks require Developer Mode on Windows")
1352
1355
  def test_default_path_unchanged_on_posix(self) -> None:
1353
1356
  """Sanity: with no force_copy and sys.platform unmonkeypatched,
1354
1357
  the existing symlink behaviour is unchanged."""
@@ -1698,6 +1701,7 @@ class CrlfNormalisationTests(unittest.TestCase):
1698
1701
  class FileModeBitsTests(unittest.TestCase):
1699
1702
  """Phase-2 comparison rule (b): mode bits drift for regular files."""
1700
1703
 
1704
+ @unittest.skipIf(sys.platform == "win32", "execute bits not supported on Windows")
1701
1705
  def test_mode_bits_drift_for_regular_files(self) -> None:
1702
1706
  with tempfile.TemporaryDirectory() as tmp:
1703
1707
  shadow = Path(tmp) / "shadow"
@@ -1827,6 +1831,7 @@ class StrengthenedDiffRegressionIntegrationTests(unittest.TestCase):
1827
1831
  def setUpClass(cls) -> None:
1828
1832
  cls.contract = load_contract(CONTRACT_PATH)
1829
1833
 
1834
+ @unittest.skipIf(sys.platform == "win32", "symlinks/execute bits not supported on Windows")
1830
1835
  def test_each_rule_catches_its_regression(self) -> None:
1831
1836
  with tempfile.TemporaryDirectory() as tmp:
1832
1837
  shadow = Path(tmp) / "shadow"
@@ -100,9 +100,12 @@ class CohortSkillRoutingTests(unittest.TestCase):
100
100
 
101
101
  class ContractMirrorTests(unittest.TestCase):
102
102
  def test_data_and_docs_agree_byte_for_byte(self) -> None:
103
+ def _norm(p: Path) -> bytes:
104
+ return p.read_bytes().replace(b"\r\n", b"\n")
105
+
103
106
  self.assertEqual(
104
- _DATA_COPY.read_bytes(),
105
- _DOCS_COPY.read_bytes(),
107
+ _norm(_DATA_COPY),
108
+ _norm(_DOCS_COPY),
106
109
  "the _data/ and contracts/ adapter.toml copies must be identical",
107
110
  )
108
111
 
@@ -35,8 +35,9 @@ def _validate_recipe_path(root: Path, recipe: str | None) -> None:
35
35
  if not recipe:
36
36
  raise ValueError("recipe path must not be empty")
37
37
  rp = Path(recipe)
38
- # Reject absolute paths (including Windows drive-absolute)
39
- if rp.is_absolute() or (len(recipe) > 1 and recipe[1] == ":"):
38
+ # Reject absolute paths (Windows drive-absolute, POSIX absolute, or Unix-
39
+ # style root on Windows where Path.is_absolute() returns False for "/foo").
40
+ if rp.is_absolute() or (len(recipe) > 1 and recipe[1] == ":") or recipe.startswith("/"):
40
41
  raise ValueError(f"recipe path must be relative, not absolute: {recipe!r}")
41
42
  # Reject traversal
42
43
  if ".." in rp.parts:
@@ -78,6 +78,7 @@ class ArtifactoryConfig:
78
78
  base_url: str | None = None
79
79
  repository: str | None = None
80
80
  bundle: str | None = None
81
+ channel: str | None = None
81
82
 
82
83
 
83
84
  @dataclass
@@ -446,9 +447,21 @@ def load_catalogue_config(root: Path) -> CatalogueConfig | None:
446
447
  f"catalogue.toml: distribution.agentbundle.artifactory.{seg_name} "
447
448
  f"must match [A-Za-z0-9._-]+, got {seg_val!r}"
448
449
  )
450
+ channel = art_raw.get("channel")
451
+ if not channel or not isinstance(channel, str) or not channel.strip():
452
+ raise CatalogueConfigError(
453
+ "catalogue.toml: distribution.agentbundle.artifactory.channel "
454
+ "is required when enabled = true (e.g. channel = \"stable\")"
455
+ )
456
+ if not _SAFE_SEGMENT_RE.match(channel):
457
+ raise CatalogueConfigError(
458
+ f"catalogue.toml: distribution.agentbundle.artifactory.channel "
459
+ f"must match [A-Za-z0-9._-]+, got {channel!r}"
460
+ )
449
461
  art.base_url = base_url
450
462
  art.repository = repository
451
463
  art.bundle = bundle
464
+ art.channel = channel
452
465
 
453
466
  agentbundle_dist = AgentbundleDistribution(
454
467
  install_defaults_output=install_defaults_output,
@@ -84,8 +84,7 @@ def compile_defaults(config: CatalogueConfig) -> str:
84
84
  art_rows.append(("base-url", _emit_str(art.base_url or "")))
85
85
  art_rows.append(("repository", _emit_str(art.repository or "")))
86
86
  art_rows.append(("bundle", _emit_str(art.bundle or "")))
87
- # channel is not in ArtifactoryConfig dataclass — emit empty when enabled
88
- art_rows.append(("channel", _emit_str("")))
87
+ art_rows.append(("channel", _emit_str(art.channel or "")))
89
88
  parts.append(_emit_section("organization.artifactory", art_rows))
90
89
 
91
90
  # [defaults]
@@ -114,10 +114,10 @@ def _check_source_conflict(
114
114
  )
115
115
  lines = [
116
116
  f"{pack_name}: source conflict at {scope} scope \u2014 "
117
- f"incoming source {incoming_display!r} differs from existing installation(s):",
117
+ f"incoming source '{incoming_display}' differs from existing installation(s):",
118
118
  ]
119
119
  for adapter, display in conflicts:
120
- lines.append(f" {adapter}: {display!r}")
120
+ lines.append(f" {adapter}: '{display}'")
121
121
  lines.append("--force does not override source conflicts.")
122
122
  lines.append(
123
123
  f"Recovery: run 'agentbundle upgrade --pack {pack_name}' from a concrete source "
@@ -213,7 +213,7 @@ def resolve_user_root(home: Path | None = None) -> Path:
213
213
  raise UserScopeUnresolvable(
214
214
  f"could not resolve $HOME path {expanded}: {exc}"
215
215
  ) from exc
216
- if str(normalised) == "/":
216
+ if normalised == normalised.parent:
217
217
  raise UserScopeUnresolvable("expanduser resolved to '/' ($HOME=/)")
218
218
  return normalised
219
219
 
@@ -20,6 +20,7 @@ consults a repo-committed source. See
20
20
  from __future__ import annotations
21
21
 
22
22
  import json
23
+ import os
23
24
  import re
24
25
  import re as _re
25
26
  import sys
@@ -572,19 +573,20 @@ def resolve_default_source(
572
573
  file=stream,
573
574
  )
574
575
 
575
- # Layer 3 — org Artifactory bootstrap (RFC-0072 D2).
576
- if read_org is None:
577
- read_org = read_org_bootstrap
578
- org = read_org() # None when disabled; raises CatalogueError on fail-closed
579
- if org is not None:
580
- return org
581
-
582
- # Layer 4 — editable detection.
583
- if dist is _UNSET:
584
- dist = _load_distribution()
585
- editable = _detect_editable_source(dist, stream=stream)
586
- if editable is not None:
587
- return editable
576
+ if not os.environ.get("AGENTBUNDLE_NO_REMOTE"):
577
+ # Layer 3 — org Artifactory bootstrap (RFC-0072 D2).
578
+ if read_org is None:
579
+ read_org = read_org_bootstrap
580
+ org = read_org() # None when disabled; raises CatalogueError on fail-closed
581
+ if org is not None:
582
+ return org
583
+
584
+ # Layer 4 — editable detection.
585
+ if dist is _UNSET:
586
+ dist = _load_distribution()
587
+ editable = _detect_editable_source(dist, stream=stream)
588
+ if editable is not None:
589
+ return editable
588
590
 
589
591
  # Layer 5 — packaged default, validated.
590
592
  if read_packaged is None:
@@ -14,7 +14,7 @@ import tomllib
14
14
  from importlib.resources import files
15
15
  from pathlib import Path
16
16
 
17
- CLI_VERSION = "0.21.0"
17
+ CLI_VERSION = "0.22.0"
18
18
 
19
19
  _HERE = Path(__file__).resolve().parent
20
20
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentbundle
3
- Version: 0.21.0
3
+ Version: 0.22.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
@@ -175,6 +175,11 @@ Developers installing from your fork get the internal channel without a manual
175
175
  A malformed `enabled = true` config fails closed — no silent fallback to the public
176
176
  source.
177
177
 
178
+ **Offline and air-gapped hosts:** set `AGENTBUNDLE_NO_REMOTE=1` to skip the org
179
+ Artifactory bootstrap and editable-install detection entirely. `agentbundle` falls
180
+ straight through to the packaged default, so hosts without network access to Artifactory
181
+ still resolve a source without errors.
182
+
178
183
  See the full enterprise adoption guide at
179
184
  `docs/guides/_shared/how-to/use-an-artifactory-catalogue.md` for all six flows
180
185
  (org bootstrap, repo-scope CI upgrade, user-scope MDM, source-conflict remediation,
@@ -1,3 +1,4 @@
1
+ README-pypi.md
1
2
  README.md
2
3
  pyproject.toml
3
4
  agentbundle/__init__.py
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentbundle"
7
- version = "0.21.0"
7
+ version = "0.22.0"
8
8
  description = "npm for your coding agent. Install packs of skills, subagents, and hooks into any repo, for every major agent."
9
9
  requires-python = ">=3.11"
10
10
  dependencies = []
11
11
  authors = [{ name = "eugenelim", email = "eugenelim@users.noreply.github.com" }]
12
12
  license = { text = "Apache-2.0 OR MIT" }
13
- readme = "README.md"
13
+ readme = "README-pypi.md"
14
14
  classifiers = [
15
15
  "Development Status :: 4 - Beta",
16
16
  "License :: OSI Approved :: Apache Software License",