agentbundle 0.5.0__tar.gz → 0.7.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 (118) hide show
  1. {agentbundle-0.5.0 → agentbundle-0.7.0}/PKG-INFO +32 -18
  2. {agentbundle-0.5.0 → agentbundle-0.7.0}/README.md +31 -17
  3. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/adapter.schema.json +1 -0
  4. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/adapter.toml +29 -1
  5. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/pack.schema.json +19 -0
  6. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/kiro.py +10 -0
  7. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/kiro_ide.py +24 -7
  8. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_cursor.py +4 -2
  9. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_gemini.py +3 -3
  10. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_kiro_cli.py +39 -0
  11. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_kiro_ide.py +42 -4
  12. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_contract.py +6 -7
  13. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_pack_schema.py +79 -0
  14. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/cli.py +57 -14
  15. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/_common.py +48 -0
  16. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/install.py +290 -8
  17. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/reconcile.py +5 -12
  18. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/uninstall.py +80 -13
  19. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/upgrade.py +95 -6
  20. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/version.py +1 -1
  21. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle.egg-info/PKG-INFO +32 -18
  22. {agentbundle-0.5.0 → agentbundle-0.7.0}/pyproject.toml +1 -1
  23. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/__init__.py +0 -0
  24. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/__main__.py +0 -0
  25. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/install-marker.py +0 -0
  26. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/plugin-manifest.derived.schema.json +0 -0
  27. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/plugin-manifest.schema.json +0 -0
  28. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/profile.schema.json +0 -0
  29. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/__init__.py +0 -0
  30. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/__main__.py +0 -0
  31. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapter_root_bins.py +0 -0
  32. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/__init__.py +0 -0
  33. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/claude_code.py +0 -0
  34. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/codex.py +0 -0
  35. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/copilot.py +0 -0
  36. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/cursor.py +0 -0
  37. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/gemini.py +0 -0
  38. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/kiro_cli.py +0 -0
  39. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/contract.py +0 -0
  40. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/lint_packs.py +0 -0
  41. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/main.py +0 -0
  42. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/phase_order.py +0 -0
  43. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/__init__.py +0 -0
  44. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/codex_agent_toml.py +0 -0
  45. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/copilot_agent_md.py +0 -0
  46. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/copilot_hooks_json.py +0 -0
  47. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/direct_directory.py +0 -0
  48. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/gemini_command_toml.py +0 -0
  49. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/hook_id.py +0 -0
  50. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/kiro_ide_hook.py +0 -0
  51. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/merge_into_agent_json.py +0 -0
  52. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/merge_json.py +0 -0
  53. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/user_merge_json.py +0 -0
  54. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/scope_rails.py +0 -0
  55. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/self_host.py +0 -0
  56. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/shared_libs.py +0 -0
  57. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/target_resolver.py +0 -0
  58. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/__init__.py +0 -0
  59. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_claude_code.py +0 -0
  60. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_codex.py +0 -0
  61. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_copilot.py +0 -0
  62. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_kiro.py +0 -0
  63. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_kiro_alias.py +0 -0
  64. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_root_bins_projection.py +0 -0
  65. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_architect_design_reviewer_projection.py +0 -0
  66. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_architect_design_reviewer_rubric_parity.py +0 -0
  67. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_build_ships_seeds.py +0 -0
  68. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_contract_scope.py +0 -0
  69. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_contract_v07.py +0 -0
  70. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_contract_v08.py +0 -0
  71. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_direct_directory_cleanup.py +0 -0
  72. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_end_to_end_build.py +0 -0
  73. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_lint_agents_md_diataxis_block.py +0 -0
  74. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_lint_agents_md_legacy_block.py +0 -0
  75. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_lint_agents_md_risk_block.py +0 -0
  76. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_lint_packs.py +0 -0
  77. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_load_pack_hook_wiring_safely.py +0 -0
  78. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_pack_schema_allowed_adapters.py +0 -0
  79. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_pack_schema_install.py +0 -0
  80. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_pipeline.py +0 -0
  81. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_plugin_manifest_schema.py +0 -0
  82. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_projectable_subset.py +0 -0
  83. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_projections_merge_json.py +0 -0
  84. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_scope_rails.py +0 -0
  85. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_security.py +0 -0
  86. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_self_host_check.py +0 -0
  87. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_shared_libs_projection.py +0 -0
  88. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_shipped_packs_v07_declarations.py +0 -0
  89. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_shipped_packs_v08_declarations.py +0 -0
  90. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_user_libs_projection.py +0 -0
  91. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_validate.py +0 -0
  92. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/user_libs.py +0 -0
  93. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/validate.py +0 -0
  94. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/catalogue.py +0 -0
  95. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/categories.py +0 -0
  96. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/__init__.py +0 -0
  97. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/_drop_warning.py +0 -0
  98. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/adapt.py +0 -0
  99. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/config.py +0 -0
  100. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/diff.py +0 -0
  101. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/init_state.py +0 -0
  102. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/list_packs.py +0 -0
  103. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/list_profiles.py +0 -0
  104. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/list_targets.py +0 -0
  105. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/profile.py +0 -0
  106. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/render.py +0 -0
  107. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/scaffold.py +0 -0
  108. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/validate.py +0 -0
  109. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/config.py +0 -0
  110. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/render.py +0 -0
  111. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/safety.py +0 -0
  112. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/scope.py +0 -0
  113. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/user_config.py +0 -0
  114. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle.egg-info/SOURCES.txt +0 -0
  115. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle.egg-info/dependency_links.txt +0 -0
  116. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle.egg-info/entry_points.txt +0 -0
  117. {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle.egg-info/top_level.txt +0 -0
  118. {agentbundle-0.5.0 → agentbundle-0.7.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentbundle
3
- Version: 0.5.0
3
+ Version: 0.7.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
@@ -22,43 +22,57 @@ Description-Content-Type: text/markdown
22
22
  [![Python](https://img.shields.io/pypi/pyversions/agentbundle)](https://pypi.org/project/agentbundle/)
23
23
  [![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue)](https://github.com/eugenelim/agent-ready-repo#license)
24
24
 
25
- **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.
25
+ **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).
26
26
 
27
- `agentbundle` installs packs of agent primitives into your repo or your home directory. It projects each primitive into the layout every agent expects. One pack. One command. Every major agent.
27
+ ## Quick start
28
28
 
29
29
  ```bash
30
30
  pip install agentbundle
31
+ ```
32
+
33
+ **Install into a repo** — so everyone who clones it gets the pack. `core` is the flagship pack, the loop itself:
34
+
35
+ ```bash
31
36
  agentbundle install --pack core git+https://github.com/eugenelim/agent-ready-repo
32
37
  ```
33
38
 
34
- That lands `core`, the flagship pack and the loop itself, in your repo. Claude Code, Codex, Cursor, Copilot, Gemini, and Kiro all read it.
39
+ 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.
35
40
 
36
- ## Common commands
41
+ **Install for yourself, everywhere** — so a pack follows you across every project, with no per-repo setup:
37
42
 
38
43
  ```bash
39
- # See what's in a catalogue
40
- agentbundle list-packs git+https://github.com/eugenelim/agent-ready-repo
44
+ agentbundle install --pack research git+https://github.com/eugenelim/agent-ready-repo --scope user
45
+ ```
46
+
47
+ 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.
48
+
49
+ A catalogue is a git URL or a local path, and the install auto-detects your agent (`--adapter` overrides).
41
50
 
42
- # See the catalogue's curated install profiles
51
+ ## More commands
52
+
53
+ ```bash
54
+ # See what a catalogue offers
55
+ agentbundle list-packs git+https://github.com/eugenelim/agent-ready-repo
43
56
  agentbundle list-profiles git+https://github.com/eugenelim/agent-ready-repo
44
57
 
45
58
  # Install a whole curated profile — a single-scope set of packs — in one command
46
59
  agentbundle install --profile inception git+https://github.com/eugenelim/agent-ready-repo
47
60
 
48
- # Install the flagship loop into this repo
49
- agentbundle install --pack core git+https://github.com/eugenelim/agent-ready-repo
50
-
51
- # Install a pack at user scope, so it follows you across every project
52
- agentbundle install --pack research git+https://github.com/eugenelim/agent-ready-repo --scope user
53
-
54
- # Preview an install without writing a file
61
+ # Preview any install without writing a file
55
62
  agentbundle install --pack core git+https://github.com/eugenelim/agent-ready-repo --dry-run
56
63
 
57
- # Upgrade a pack; it reports the .upstream files you need to reconcile
58
- agentbundle upgrade --pack core --to 0.4.0 git+https://github.com/eugenelim/agent-ready-repo
64
+ # Upgrade to the version the catalogue ships shows installed → target, asks first
65
+ agentbundle upgrade --pack core git+https://github.com/eugenelim/agent-ready-repo
66
+ agentbundle upgrade --pack core git+https://github.com/eugenelim/agent-ready-repo --yes # skip the prompt (CI)
67
+
68
+ # Uninstall — previews remove (Tier-1) vs keep (your edits), asks first
69
+ agentbundle uninstall --pack core --dry-run
70
+ agentbundle uninstall --pack core --yes
59
71
  ```
60
72
 
61
- A catalogue is a git URL or a local path. Installs auto-detect your agent; pass `--adapter` to override. 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.
73
+ 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).
74
+
75
+ **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).
62
76
 
63
77
  ## Build your own catalogue
64
78
 
@@ -4,43 +4,57 @@
4
4
  [![Python](https://img.shields.io/pypi/pyversions/agentbundle)](https://pypi.org/project/agentbundle/)
5
5
  [![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue)](https://github.com/eugenelim/agent-ready-repo#license)
6
6
 
7
- **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.
7
+ **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).
8
8
 
9
- `agentbundle` installs packs of agent primitives into your repo or your home directory. It projects each primitive into the layout every agent expects. One pack. One command. Every major agent.
9
+ ## Quick start
10
10
 
11
11
  ```bash
12
12
  pip install agentbundle
13
+ ```
14
+
15
+ **Install into a repo** — so everyone who clones it gets the pack. `core` is the flagship pack, the loop itself:
16
+
17
+ ```bash
13
18
  agentbundle install --pack core git+https://github.com/eugenelim/agent-ready-repo
14
19
  ```
15
20
 
16
- That lands `core`, the flagship pack and the loop itself, in your repo. Claude Code, Codex, Cursor, Copilot, Gemini, and Kiro all read it.
21
+ 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.
17
22
 
18
- ## Common commands
23
+ **Install for yourself, everywhere** — so a pack follows you across every project, with no per-repo setup:
19
24
 
20
25
  ```bash
21
- # See what's in a catalogue
22
- agentbundle list-packs git+https://github.com/eugenelim/agent-ready-repo
26
+ agentbundle install --pack research git+https://github.com/eugenelim/agent-ready-repo --scope user
27
+ ```
28
+
29
+ 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.
30
+
31
+ A catalogue is a git URL or a local path, and the install auto-detects your agent (`--adapter` overrides).
23
32
 
24
- # See the catalogue's curated install profiles
33
+ ## More commands
34
+
35
+ ```bash
36
+ # See what a catalogue offers
37
+ agentbundle list-packs git+https://github.com/eugenelim/agent-ready-repo
25
38
  agentbundle list-profiles git+https://github.com/eugenelim/agent-ready-repo
26
39
 
27
40
  # Install a whole curated profile — a single-scope set of packs — in one command
28
41
  agentbundle install --profile inception git+https://github.com/eugenelim/agent-ready-repo
29
42
 
30
- # Install the flagship loop into this repo
31
- agentbundle install --pack core git+https://github.com/eugenelim/agent-ready-repo
32
-
33
- # Install a pack at user scope, so it follows you across every project
34
- agentbundle install --pack research git+https://github.com/eugenelim/agent-ready-repo --scope user
35
-
36
- # Preview an install without writing a file
43
+ # Preview any install without writing a file
37
44
  agentbundle install --pack core git+https://github.com/eugenelim/agent-ready-repo --dry-run
38
45
 
39
- # Upgrade a pack; it reports the .upstream files you need to reconcile
40
- agentbundle upgrade --pack core --to 0.4.0 git+https://github.com/eugenelim/agent-ready-repo
46
+ # Upgrade to the version the catalogue ships shows installed → target, asks first
47
+ agentbundle upgrade --pack core git+https://github.com/eugenelim/agent-ready-repo
48
+ agentbundle upgrade --pack core git+https://github.com/eugenelim/agent-ready-repo --yes # skip the prompt (CI)
49
+
50
+ # Uninstall — previews remove (Tier-1) vs keep (your edits), asks first
51
+ agentbundle uninstall --pack core --dry-run
52
+ agentbundle uninstall --pack core --yes
41
53
  ```
42
54
 
43
- A catalogue is a git URL or a local path. Installs auto-detect your agent; pass `--adapter` to override. 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.
55
+ 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).
56
+
57
+ **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).
44
58
 
45
59
  ## Build your own catalogue
46
60
 
@@ -69,6 +69,7 @@
69
69
  "managed-block-delimiter-end": {"type": "string"},
70
70
  "frontmatter-mapping": {"type": "string"},
71
71
  "frontmatter-default": {"type": "string"},
72
+ "inject-resources": {"type": "array", "items": {"type": "string"}},
72
73
  "info-message": {"type": "string"}
73
74
  }
74
75
  }
@@ -71,7 +71,20 @@
71
71
  # each pack's README. All new fields are optional — packs pinned below v0.14
72
72
  # build and validate unchanged. Declaration + projection only (no @catalogue/pack
73
73
  # resolution, no search, no persisted index).
74
- version = "0.14"
74
+ # docs/specs/kiro-cli-agent-skill-resources (v0.15): kiro-cli agent projection
75
+ # injects a fixed skill-resources glob (both install scopes) so headless
76
+ # `--agent` runs reach the bundle's skills — Kiro custom agents don't inherit
77
+ # the default agent's `.kiro/skills` auto-discovery (RFC-0022 E4; kiro
78
+ # #6887/#6888/#4993). Additive `inject-resources` on BOTH the kiro-cli and
79
+ # kiro-ide agent projections (CLI → agent JSON, IDE → quoted .md frontmatter).
80
+ # RFC-0040 / ADR-0030 / docs/specs/consolidated-pack-layout (v0.16): pack.toml
81
+ # gains an optional scope-keyed [pack.layout] table (optional .repo / .user
82
+ # sub-tables, each a `parent` base and/or a within-pack template-path) naming
83
+ # where a consuming pack's durable output lands. Consumed by the new installer
84
+ # `_append_layout_section` step (append-if-exists / never-create / never-
85
+ # overwrite) into an adopter-owned `agentbundle-layout.toml`. Additive +
86
+ # optional — packs pinned below v0.16 build and validate unchanged.
87
+ version = "0.16"
75
88
 
76
89
  # Sibling schemas this contract references — pack metadata and the
77
90
  # per-pack Claude-plugin manifest. Defined by spec AC #3 + #4.
@@ -319,6 +332,13 @@ primitive = "agent"
319
332
  mode = "direct-file"
320
333
  target-path = ".kiro/agents/"
321
334
  frontmatter-mapping = "kiro-ide-agent-frontmatter-v0.9"
335
+ # Skill discovery (RFC-0022 E4): IDE custom agents / subagents do NOT inherit
336
+ # the default agent's `.kiro/skills` auto-discovery either (kiro #6887/#6888,
337
+ # #4993). Inject the same fixed skill-resources glob as kiro-cli; the IDE
338
+ # agent `resources` field is documented and `skill://`-aware, so it does not
339
+ # trip the silent-drop bug (that's for non-schema keys like `hooks`). Emitted
340
+ # into the `.md` YAML frontmatter, quoted. Author override wins.
341
+ inject-resources = ["skill://.kiro/skills/**/SKILL.md", "skill://~/.kiro/skills/**/SKILL.md"]
322
342
  on-conflict = "prompt-then-preserve"
323
343
 
324
344
  [[adapter.kiro-ide.projection]]
@@ -388,6 +408,14 @@ primitive = "agent"
388
408
  mode = "direct-file"
389
409
  target-path = ".kiro/agents/"
390
410
  frontmatter-mapping = "kiro-cli-agent-frontmatter-v1.0"
411
+ # Skill discovery (RFC-0022 E4): Kiro custom agents do NOT inherit the default
412
+ # agent's auto-discovery of `.kiro/skills` (kiro #6887/#6888), so a headless
413
+ # `--agent` run sees zero skills unless the agent declares them. Inject this
414
+ # fixed skill-resources glob (both install scopes) into every projected agent.
415
+ # Skipped when the agent source already declares its own `resources` (author
416
+ # override wins). kiro-ide carries the same field on its own agent projection
417
+ # entry (emitted into `.md` frontmatter rather than JSON).
418
+ inject-resources = ["skill://.kiro/skills/**/SKILL.md", "skill://~/.kiro/skills/**/SKILL.md"]
391
419
  on-conflict = "prompt-then-preserve"
392
420
 
393
421
  [[adapter.kiro-cli.projection]]
@@ -131,6 +131,25 @@
131
131
  "type": "string",
132
132
  "pattern": "^[^/].*"
133
133
  }
134
+ },
135
+ "layout": {
136
+ "type": "object",
137
+ "properties": {
138
+ "repo": {
139
+ "type": "object",
140
+ "properties": {
141
+ "parent": {"type": "string"},
142
+ "template": {"type": "string"}
143
+ }
144
+ },
145
+ "user": {
146
+ "type": "object",
147
+ "properties": {
148
+ "parent": {"type": "string"},
149
+ "template": {"type": "string"}
150
+ }
151
+ }
152
+ }
134
153
  }
135
154
  },
136
155
  "if": {
@@ -284,6 +284,16 @@ def _project_agent_as_json(
284
284
  prompt = body.rstrip("\n")
285
285
  if prompt:
286
286
  agent_json["prompt"] = prompt
287
+ # Skill-resources injection (RFC-0022 E4). The agent projection
288
+ # declares `inject-resources` so custom agents reach the bundle's
289
+ # skills — Kiro custom agents don't inherit the default agent's
290
+ # `.kiro/skills` auto-discovery (kiro #6887/#6888). Author-declared
291
+ # `resources` win: only inject when the agent set none. This function
292
+ # handles the CLI (JSON) path; kiro-ide injects the same field in its
293
+ # own `.md` projector (`_project_agent_as_md`).
294
+ inject_resources = rule.get("inject-resources")
295
+ if inject_resources and "resources" not in agent_json:
296
+ agent_json["resources"] = list(inject_resources)
287
297
  destination = target_dir / (entry.stem + ".json")
288
298
  destination.write_text(
289
299
  json.dumps(agent_json, indent=2, sort_keys=False) + "\n",
@@ -244,6 +244,15 @@ def _project_agent_as_md(
244
244
  agent_name = rewritten.get("name") or entry.stem
245
245
  rewritten["name"] = agent_name
246
246
 
247
+ # Skill-resources injection (RFC-0022 E4). Same gap as kiro-cli: IDE
248
+ # custom agents don't inherit the default agent's `.kiro/skills`
249
+ # auto-discovery, so inject the declared glob unless the agent set its
250
+ # own `resources` (author override wins). `resources` is a documented
251
+ # IDE agent field, so it does not trip the silent-drop bug.
252
+ inject_resources = rule.get("inject-resources")
253
+ if inject_resources and "resources" not in rewritten:
254
+ rewritten["resources"] = list(inject_resources)
255
+
247
256
  output_text = _serialize_frontmatter_md(rewritten) + body
248
257
  destination = target_dir / entry.name # preserves .md extension
249
258
  destination.write_text(output_text, encoding="utf-8")
@@ -260,16 +269,24 @@ def _serialize_frontmatter_md(fields: dict[str, Any]) -> str:
260
269
  lines = ["---"]
261
270
  for key, value in fields.items():
262
271
  if isinstance(value, list):
263
- items = ", ".join(str(v) for v in value)
272
+ # Flow sequence. Simple tokens (tool ids like `read_file`) stay
273
+ # unquoted; items with YAML-special chars (e.g. `skill://` URIs
274
+ # with `**` globs) are quoted so the IDE's parser can't misread them.
275
+ items = ", ".join(_yaml_scalar(str(v)) for v in value)
264
276
  lines.append(f"{key}: [{items}]")
265
277
  elif isinstance(value, str):
266
- # Quote strings that contain YAML-special characters.
267
- if any(c in value for c in ':#{}[]|>&*!,\'"'):
268
- escaped = value.replace("\\", "\\\\").replace('"', '\\"')
269
- lines.append(f'{key}: "{escaped}"')
270
- else:
271
- lines.append(f"{key}: {value}")
278
+ lines.append(f"{key}: {_yaml_scalar(value)}")
272
279
  else:
273
280
  lines.append(f"{key}: {value}")
274
281
  lines.append("---")
275
282
  return "\n".join(lines) + "\n"
283
+
284
+
285
+ def _yaml_scalar(value: str) -> str:
286
+ """Render a string as a YAML scalar, double-quoting when it contains a
287
+ YAML-special character (else a plain scalar). Used for both top-level
288
+ values and items inside a `[...]` flow sequence."""
289
+ if any(c in value for c in ':#{}[]|>&*!,\'"'):
290
+ escaped = value.replace("\\", "\\\\").replace('"', '\\"')
291
+ return f'"{escaped}"'
292
+ return value
@@ -53,9 +53,11 @@ class CursorContractTests(unittest.TestCase):
53
53
  def test_contract_version_is_0_11(self) -> None:
54
54
  """AC1 — contract bumped to 0.11 by cursor-full-parity; subsequently
55
55
  0.12 (copilot-skills-and-web), 0.13 (docs/specs/gemini-full-parity),
56
- then 0.14 (docs/specs/enriched-pack-manifest).
56
+ 0.14 (docs/specs/enriched-pack-manifest), 0.15
57
+ (docs/specs/kiro-cli-agent-skill-resources), then 0.16
58
+ (docs/specs/consolidated-pack-layout).
57
59
  Name preserved to keep the diff small."""
58
- self.assertEqual(self.contract["contract"]["version"], "0.14")
60
+ self.assertEqual(self.contract["contract"]["version"], "0.16")
59
61
 
60
62
  def test_cursor_block_projects_five_primitives(self) -> None:
61
63
  """AC2 — the five standard primitives are in the projection array."""
@@ -151,9 +151,9 @@ class GeminiContractTests(unittest.TestCase):
151
151
  cls.contract = load_contract(CONTRACT_PATH)
152
152
 
153
153
  def test_contract_version_is_0_14(self) -> None:
154
- """Contract version is 0.14 (docs/specs/enriched-pack-manifest bumped it
155
- from gemini-full-parity's 0.13). Renamed from the 0_13 method."""
156
- self.assertEqual(self.contract["contract"]["version"], "0.14")
154
+ """Contract version is 0.16 (docs/specs/consolidated-pack-layout
155
+ bumped it from kiro-cli-agent-skill-resources' 0.15). Name preserved."""
156
+ self.assertEqual(self.contract["contract"]["version"], "0.16")
157
157
 
158
158
  def test_gemini_block_projects_five_primitives(self) -> None:
159
159
  """AC2 — five standard primitives with their gemini targets."""
@@ -79,6 +79,45 @@ class KiroCliAdapterTests(unittest.TestCase):
79
79
  self.assertNotIn("grep_search", tools)
80
80
  self.assertNotIn("execute_bash", tools)
81
81
 
82
+ def test_cli_agent_gets_skill_resources(self) -> None:
83
+ """kiro-cli custom agents must declare the skill-resources glob so
84
+ they reach the bundle's skills — Kiro custom agents don't inherit the
85
+ default agent's auto-discovery (RFC-0022 E4; kiro #6887/#6888)."""
86
+ with tempfile.TemporaryDirectory() as tmp:
87
+ tmp_path = Path(tmp)
88
+ pack = _seed_agent_pack(tmp_path)
89
+ out = tmp_path / "out"
90
+ project(pack, self.contract, out)
91
+ data = json.loads(
92
+ (out / ".kiro" / "agents" / "bar.json").read_text(encoding="utf-8")
93
+ )
94
+ self.assertEqual(
95
+ data.get("resources"),
96
+ [
97
+ "skill://.kiro/skills/**/SKILL.md",
98
+ "skill://~/.kiro/skills/**/SKILL.md",
99
+ ],
100
+ "kiro-cli agent JSON must inject both repo- and user-scope skill globs",
101
+ )
102
+
103
+ def test_cli_agent_resources_author_override_wins(self) -> None:
104
+ """An agent that declares its own `resources` keeps it — the default
105
+ does not clobber an author-supplied value."""
106
+ with tempfile.TemporaryDirectory() as tmp:
107
+ tmp_path = Path(tmp)
108
+ pack = tmp_path / "pack"
109
+ (pack / ".apm" / "agents").mkdir(parents=True)
110
+ (pack / ".apm" / "agents" / "bar.md").write_text(
111
+ "---\nname: bar\nresources: [file://README.md]\n---\nbody\n",
112
+ encoding="utf-8",
113
+ )
114
+ out = tmp_path / "out"
115
+ project(pack, self.contract, out)
116
+ data = json.loads(
117
+ (out / ".kiro" / "agents" / "bar.json").read_text(encoding="utf-8")
118
+ )
119
+ self.assertEqual(data.get("resources"), ["file://README.md"])
120
+
82
121
  def test_cli_no_ide_hook_field(self) -> None:
83
122
  """kiro-cli projected agent JSON must not contain ide-event-vocabulary
84
123
  or kiro-ide-hook sections — those are IDE-only fields that cause the
@@ -63,6 +63,44 @@ class KiroIdeAdapterTests(unittest.TestCase):
63
63
  for cli_only in ("hooks", "allowedTools", "toolsSettings", "mcpServers"):
64
64
  self.assertNotIn(cli_only, raw, f"kiro-ide agent .md must not contain {cli_only!r}")
65
65
 
66
+ def test_kiro_ide_gets_skill_resources(self) -> None:
67
+ """kiro-ide custom agents (and IDE subagents) must declare the
68
+ skill-resources glob so they reach skills — IDE custom agents don't
69
+ inherit the default agent's auto-discovery either (RFC-0022 E4)."""
70
+ with tempfile.TemporaryDirectory() as tmp:
71
+ tmp_path = Path(tmp)
72
+ pack = _seed_agent_pack(tmp_path)
73
+ out = tmp_path / "out"
74
+ project(pack, self.contract, out)
75
+ raw = (out / ".kiro" / "agents" / "bar.md").read_text(encoding="utf-8")
76
+ # Assert the exact emitted line: a flow sequence of DOUBLE-QUOTED
77
+ # scalars. Quoting makes the `skill://` URIs / `**` globs
78
+ # unambiguous YAML, guarding the IDE's fail-silent frontmatter
79
+ # parser (kiro #8329). The bytes were confirmed to round-trip
80
+ # through PyYAML to the two-element list during verification; this
81
+ # gate is exact-string (the build tree is stdlib-only — no yaml).
82
+ self.assertIn(
83
+ 'resources: ["skill://.kiro/skills/**/SKILL.md", '
84
+ '"skill://~/.kiro/skills/**/SKILL.md"]',
85
+ raw,
86
+ )
87
+
88
+ def test_kiro_ide_resources_author_override_wins(self) -> None:
89
+ """An IDE agent that declares its own `resources` keeps it."""
90
+ with tempfile.TemporaryDirectory() as tmp:
91
+ tmp_path = Path(tmp)
92
+ pack = tmp_path / "pack"
93
+ (pack / ".apm" / "agents").mkdir(parents=True)
94
+ (pack / ".apm" / "agents" / "bar.md").write_text(
95
+ "---\nname: bar\nresources: [file://README.md]\n---\nbody\n",
96
+ encoding="utf-8",
97
+ )
98
+ out = tmp_path / "out"
99
+ project(pack, self.contract, out)
100
+ raw = (out / ".kiro" / "agents" / "bar.md").read_text(encoding="utf-8")
101
+ self.assertIn("file://README.md", raw)
102
+ self.assertNotIn("skill://", raw)
103
+
66
104
  def test_kiro_ide_tools_use_ide_ids(self) -> None:
67
105
  """kiro-ide uses IDE tool ids (read_file, grep_search) not CLI short-names."""
68
106
  with tempfile.TemporaryDirectory() as tmp:
@@ -134,13 +172,13 @@ class KiroIdeAdapterTests(unittest.TestCase):
134
172
  self.assertIn(".kiro.hook", target_repo)
135
173
 
136
174
  def test_contract_version_is_0_9(self) -> None:
137
- """Contract version is 0.14 (docs/specs/enriched-pack-manifest, atop
138
- gemini-full-parity's 0.13 and copilot-skills-and-web's 0.12).
175
+ """Contract version is 0.16 (docs/specs/consolidated-pack-layout,
176
+ atop kiro-cli-agent-skill-resources' 0.15 and enriched-pack-manifest's 0.14).
139
177
  Name preserved to keep the diff small."""
140
178
  self.assertEqual(
141
179
  self.contract["contract"]["version"],
142
- "0.14",
143
- "adapter.toml [contract] version must be '0.14' after enriched-pack-manifest",
180
+ "0.16",
181
+ "adapter.toml [contract] version must be '0.16' after consolidated-pack-layout",
144
182
  )
145
183
 
146
184
  def test_kiro_ide_hook_projects_with_flat_prefix_path(self) -> None:
@@ -452,17 +452,16 @@ class ContractV05Tests(unittest.TestCase):
452
452
  self.schema = _load_schema()
453
453
 
454
454
  def test_contract_version_is_v05(self) -> None:
455
- """tomllib.loads of adapter.toml returns contract.version == "0.14"
456
- (bumped from gemini-full-parity's "0.13" by
457
- docs/specs/enriched-pack-manifest: pack.toml becomes the rich metadata
458
- source of truth and the build projects the projectable subset + READMEs
459
- into the claude-plugins + apm routes). Class/method names preserved to
455
+ """tomllib.loads of adapter.toml returns contract.version == "0.16"
456
+ (bumped from kiro-cli-agent-skill-resources' "0.15" by
457
+ docs/specs/consolidated-pack-layout: pack.toml gains an optional
458
+ scope-keyed [pack.layout] table). Class/method names preserved to
460
459
  avoid churn.
461
460
  """
462
461
  self.assertEqual(
463
462
  self.contract["contract"]["version"],
464
- "0.14",
465
- "adapter.toml [contract] version must be '0.14' after enriched-pack-manifest",
463
+ "0.16",
464
+ "adapter.toml [contract] version must be '0.16' after consolidated-pack-layout",
466
465
  )
467
466
 
468
467
  def test_claude_code_install_routes_includes_apm(self) -> None:
@@ -399,6 +399,85 @@ description = "Core skills."
399
399
  self.assertEqual(errors, [], "\n".join(errors))
400
400
 
401
401
 
402
+ class PackSchemaLayoutTests(unittest.TestCase):
403
+ """consolidated-pack-layout T1: optional scope-keyed [pack.layout] table.
404
+
405
+ pack.schema.json gains an optional `[pack.layout]` table with optional
406
+ `.repo` / `.user` sub-tables, each carrying a `parent` base and/or a
407
+ within-pack `template` path. Every field is optional and the change is
408
+ additive — a manifest omitting it must still validate (legacy invariance),
409
+ and a pack may declare only `.repo` (the three current consumers do, since
410
+ their output is per-repo and they have no sensible absolute user default).
411
+ """
412
+
413
+ def test_accepts_layout_with_repo_and_user(self) -> None:
414
+ """A manifest carrying [pack.layout.repo] and [pack.layout.user] validates."""
415
+ from agentbundle.build.validate import validate
416
+
417
+ schema = _load_schema()
418
+ toml_text = """
419
+ [pack]
420
+ name = "research"
421
+ version = "0.5.0"
422
+
423
+ [pack.layout.repo]
424
+ parent = ".context/research"
425
+
426
+ [pack.layout.user]
427
+ parent = "~/research-projects"
428
+ template = "references/agentbundle-layout.toml"
429
+ """
430
+ instance = _parse_toml(toml_text)
431
+ errors = validate(instance, schema)
432
+ self.assertEqual(errors, [], "\n".join(errors))
433
+
434
+ def test_accepts_layout_repo_only(self) -> None:
435
+ """A pack may declare only [pack.layout.repo] — the consumer shape."""
436
+ from agentbundle.build.validate import validate
437
+
438
+ schema = _load_schema()
439
+ toml_text = """
440
+ [pack]
441
+ name = "product-engineering"
442
+ version = "0.5.0"
443
+
444
+ [pack.layout.repo]
445
+ parent = "docs/product"
446
+ """
447
+ instance = _parse_toml(toml_text)
448
+ errors = validate(instance, schema)
449
+ self.assertEqual(errors, [], "\n".join(errors))
450
+
451
+ def test_accepts_manifest_omitting_layout(self) -> None:
452
+ """Optionality: a manifest with no [pack.layout] validates (legacy invariance)."""
453
+ from agentbundle.build.validate import validate
454
+
455
+ schema = _load_schema()
456
+ toml_text = """
457
+ [pack]
458
+ name = "core"
459
+ version = "0.4.0"
460
+ """
461
+ instance = _parse_toml(toml_text)
462
+ errors = validate(instance, schema)
463
+ self.assertEqual(errors, [], "\n".join(errors))
464
+
465
+ def test_rejects_non_string_parent(self) -> None:
466
+ """[pack.layout.repo].parent must be a string; a non-string is rejected."""
467
+ from agentbundle.build.validate import validate
468
+
469
+ schema = _load_schema()
470
+ instance = {
471
+ "pack": {
472
+ "name": "core",
473
+ "version": "0.1.0",
474
+ "layout": {"repo": {"parent": 42}},
475
+ }
476
+ }
477
+ errors = validate(instance, schema)
478
+ self.assertTrue(errors, "schema accepted a non-string layout.repo.parent")
479
+
480
+
402
481
  class PackSchemaLoadsTests(unittest.TestCase):
403
482
  """Smoke test: the schema file loads and has the expected top-level shape."""
404
483