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.
- {agentbundle-0.5.0 → agentbundle-0.7.0}/PKG-INFO +32 -18
- {agentbundle-0.5.0 → agentbundle-0.7.0}/README.md +31 -17
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/adapter.schema.json +1 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/adapter.toml +29 -1
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/pack.schema.json +19 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/kiro.py +10 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/kiro_ide.py +24 -7
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_cursor.py +4 -2
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_gemini.py +3 -3
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_kiro_cli.py +39 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_kiro_ide.py +42 -4
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_contract.py +6 -7
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_pack_schema.py +79 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/cli.py +57 -14
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/_common.py +48 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/install.py +290 -8
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/reconcile.py +5 -12
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/uninstall.py +80 -13
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/upgrade.py +95 -6
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/version.py +1 -1
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle.egg-info/PKG-INFO +32 -18
- {agentbundle-0.5.0 → agentbundle-0.7.0}/pyproject.toml +1 -1
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/__init__.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/__main__.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/install-marker.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/plugin-manifest.derived.schema.json +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/plugin-manifest.schema.json +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/_data/profile.schema.json +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/__init__.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/__main__.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapter_root_bins.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/__init__.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/claude_code.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/codex.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/copilot.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/cursor.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/gemini.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/adapters/kiro_cli.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/contract.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/lint_packs.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/main.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/phase_order.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/__init__.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/codex_agent_toml.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/copilot_agent_md.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/copilot_hooks_json.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/direct_directory.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/gemini_command_toml.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/hook_id.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/kiro_ide_hook.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/merge_into_agent_json.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/merge_json.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/projections/user_merge_json.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/scope_rails.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/self_host.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/shared_libs.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/target_resolver.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/__init__.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_claude_code.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_codex.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_copilot.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_kiro.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_kiro_alias.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_adapter_root_bins_projection.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_architect_design_reviewer_projection.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_architect_design_reviewer_rubric_parity.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_build_ships_seeds.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_contract_scope.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_contract_v07.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_contract_v08.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_direct_directory_cleanup.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_end_to_end_build.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_lint_agents_md_diataxis_block.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_lint_agents_md_legacy_block.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_lint_agents_md_risk_block.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_lint_packs.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_load_pack_hook_wiring_safely.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_pack_schema_allowed_adapters.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_pack_schema_install.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_pipeline.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_plugin_manifest_schema.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_projectable_subset.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_projections_merge_json.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_scope_rails.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_security.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_self_host_check.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_shared_libs_projection.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_shipped_packs_v07_declarations.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_shipped_packs_v08_declarations.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_user_libs_projection.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/tests/test_validate.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/user_libs.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/build/validate.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/catalogue.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/categories.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/__init__.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/_drop_warning.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/adapt.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/config.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/diff.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/init_state.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/list_packs.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/list_profiles.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/list_targets.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/profile.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/render.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/scaffold.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/commands/validate.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/config.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/render.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/safety.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/scope.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle/user_config.py +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle.egg-info/SOURCES.txt +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle.egg-info/dependency_links.txt +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle.egg-info/entry_points.txt +0 -0
- {agentbundle-0.5.0 → agentbundle-0.7.0}/agentbundle.egg-info/top_level.txt +0 -0
- {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.
|
|
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
|
[](https://pypi.org/project/agentbundle/)
|
|
23
23
|
[](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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
+
**Install for yourself, everywhere** — so a pack follows you across every project, with no per-repo setup:
|
|
37
42
|
|
|
38
43
|
```bash
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
|
58
|
-
agentbundle upgrade --pack core
|
|
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
|
|
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
|
[](https://pypi.org/project/agentbundle/)
|
|
5
5
|
[](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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
+
**Install for yourself, everywhere** — so a pack follows you across every project, with no per-repo setup:
|
|
19
24
|
|
|
20
25
|
```bash
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
|
40
|
-
agentbundle upgrade --pack core
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
155
|
-
from
|
|
156
|
-
self.assertEqual(self.contract["contract"]["version"], "0.
|
|
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.
|
|
138
|
-
|
|
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.
|
|
143
|
-
"adapter.toml [contract] version must be '0.
|
|
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.
|
|
456
|
-
(bumped from
|
|
457
|
-
docs/specs/
|
|
458
|
-
|
|
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.
|
|
465
|
-
"adapter.toml [contract] version must be '0.
|
|
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
|
|