agentbundle 0.7.0__tar.gz → 0.9.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.7.0 → agentbundle-0.9.0}/PKG-INFO +12 -12
- {agentbundle-0.7.0 → agentbundle-0.9.0}/README.md +11 -11
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/_data/adapter.toml +45 -14
- agentbundle-0.9.0/agentbundle/_data/install-defaults.toml +10 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/adapters/codex.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/adapters/cursor.py +2 -2
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/adapters/gemini.py +2 -2
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/adapters/kiro.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/adapters/kiro_ide.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/main.py +4 -4
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/codex_agent_toml.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/copilot_agent_md.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/copilot_hooks_json.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/gemini_command_toml.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/kiro_ide_hook.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/merge_into_agent_json.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/merge_json.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/user_merge_json.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/self_host.py +106 -22
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_adapter_copilot.py +8 -9
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_adapter_cursor.py +7 -7
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_adapter_gemini.py +7 -7
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_adapter_kiro_ide.py +3 -3
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_contract.py +4 -4
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_contract_scope.py +8 -7
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_contract_v07.py +7 -4
- agentbundle-0.9.0/agentbundle/build/tests/test_self_host_fixture_guard.py +88 -0
- agentbundle-0.9.0/agentbundle/build/tests/test_self_host_recipe_config.py +113 -0
- agentbundle-0.9.0/agentbundle/build/tests/test_shared_prefix_contract.py +111 -0
- agentbundle-0.9.0/agentbundle/build/tests/test_writers_emit_lf.py +169 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/cli.py +69 -4
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/_common.py +119 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/config.py +6 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/diff.py +34 -8
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/init_state.py +33 -64
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/install.py +314 -56
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/list_packs.py +20 -30
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/list_profiles.py +12 -16
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/reconcile.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/uninstall.py +87 -32
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/upgrade.py +68 -14
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/config.py +268 -155
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/safety.py +16 -12
- agentbundle-0.9.0/agentbundle/source_defaults.py +342 -0
- agentbundle-0.9.0/agentbundle/statelock.py +148 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/user_config.py +53 -5
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/version.py +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle.egg-info/PKG-INFO +12 -12
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle.egg-info/SOURCES.txt +7 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/pyproject.toml +1 -1
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/__init__.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/__main__.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/_data/adapter.schema.json +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/_data/install-marker.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/_data/pack.schema.json +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/_data/plugin-manifest.derived.schema.json +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/_data/plugin-manifest.schema.json +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/_data/profile.schema.json +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/__init__.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/__main__.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/adapter_root_bins.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/adapters/__init__.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/adapters/claude_code.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/adapters/copilot.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/adapters/kiro_cli.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/contract.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/lint_packs.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/phase_order.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/__init__.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/direct_directory.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/hook_id.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/scope_rails.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/shared_libs.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/target_resolver.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/__init__.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_adapter_claude_code.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_adapter_codex.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_adapter_kiro.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_adapter_kiro_alias.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_adapter_kiro_cli.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_adapter_root_bins_projection.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_architect_design_reviewer_projection.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_architect_design_reviewer_rubric_parity.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_build_ships_seeds.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_contract_v08.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_direct_directory_cleanup.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_end_to_end_build.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_lint_agents_md_diataxis_block.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_lint_agents_md_legacy_block.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_lint_agents_md_risk_block.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_lint_packs.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_load_pack_hook_wiring_safely.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_pack_schema.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_pack_schema_allowed_adapters.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_pack_schema_install.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_pipeline.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_plugin_manifest_schema.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_projectable_subset.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_projections_merge_json.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_scope_rails.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_security.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_self_host_check.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_shared_libs_projection.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_shipped_packs_v07_declarations.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_shipped_packs_v08_declarations.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_user_libs_projection.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/tests/test_validate.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/user_libs.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/validate.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/catalogue.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/categories.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/__init__.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/_drop_warning.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/adapt.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/list_targets.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/profile.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/render.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/scaffold.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/commands/validate.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/render.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/scope.py +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle.egg-info/dependency_links.txt +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle.egg-info/entry_points.txt +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle.egg-info/top_level.txt +0 -0
- {agentbundle-0.7.0 → agentbundle-0.9.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.9.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
|
|
@@ -33,37 +33,37 @@ pip install agentbundle
|
|
|
33
33
|
**Install into a repo** — so everyone who clones it gets the pack. `core` is the flagship pack, the loop itself:
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
agentbundle install --pack core
|
|
36
|
+
agentbundle install --pack core
|
|
37
37
|
```
|
|
38
38
|
|
|
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.
|
|
39
|
+
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.
|
|
40
40
|
|
|
41
41
|
**Install for yourself, everywhere** — so a pack follows you across every project, with no per-repo setup:
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
agentbundle install --pack research
|
|
44
|
+
agentbundle install --pack research --scope user
|
|
45
45
|
```
|
|
46
46
|
|
|
47
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
48
|
|
|
49
|
-
|
|
49
|
+
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.
|
|
50
50
|
|
|
51
51
|
## More commands
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
# See what
|
|
55
|
-
agentbundle list-packs
|
|
56
|
-
agentbundle list-profiles
|
|
54
|
+
# See what the catalogue offers (bare uses the default; or name one explicitly)
|
|
55
|
+
agentbundle list-packs
|
|
56
|
+
agentbundle list-profiles
|
|
57
57
|
|
|
58
58
|
# Install a whole curated profile — a single-scope set of packs — in one command
|
|
59
|
-
agentbundle install --profile inception
|
|
59
|
+
agentbundle install --profile inception
|
|
60
60
|
|
|
61
61
|
# Preview any install without writing a file
|
|
62
|
-
agentbundle install --pack core
|
|
62
|
+
agentbundle install --pack core --dry-run
|
|
63
63
|
|
|
64
64
|
# Upgrade to the version the catalogue ships — shows installed → target, asks first
|
|
65
|
-
agentbundle upgrade --pack core
|
|
66
|
-
agentbundle upgrade --pack core
|
|
65
|
+
agentbundle upgrade --pack core
|
|
66
|
+
agentbundle upgrade --pack core --yes # skip the prompt (CI)
|
|
67
67
|
|
|
68
68
|
# Uninstall — previews remove (Tier-1) vs keep (your edits), asks first
|
|
69
69
|
agentbundle uninstall --pack core --dry-run
|
|
@@ -15,37 +15,37 @@ pip install agentbundle
|
|
|
15
15
|
**Install into a repo** — so everyone who clones it gets the pack. `core` is the flagship pack, the loop itself:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
agentbundle install --pack core
|
|
18
|
+
agentbundle install --pack core
|
|
19
19
|
```
|
|
20
20
|
|
|
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.
|
|
21
|
+
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.
|
|
22
22
|
|
|
23
23
|
**Install for yourself, everywhere** — so a pack follows you across every project, with no per-repo setup:
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
agentbundle install --pack research
|
|
26
|
+
agentbundle install --pack research --scope user
|
|
27
27
|
```
|
|
28
28
|
|
|
29
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
30
|
|
|
31
|
-
|
|
31
|
+
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.
|
|
32
32
|
|
|
33
33
|
## More commands
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
# See what
|
|
37
|
-
agentbundle list-packs
|
|
38
|
-
agentbundle list-profiles
|
|
36
|
+
# See what the catalogue offers (bare uses the default; or name one explicitly)
|
|
37
|
+
agentbundle list-packs
|
|
38
|
+
agentbundle list-profiles
|
|
39
39
|
|
|
40
40
|
# Install a whole curated profile — a single-scope set of packs — in one command
|
|
41
|
-
agentbundle install --profile inception
|
|
41
|
+
agentbundle install --profile inception
|
|
42
42
|
|
|
43
43
|
# Preview any install without writing a file
|
|
44
|
-
agentbundle install --pack core
|
|
44
|
+
agentbundle install --pack core --dry-run
|
|
45
45
|
|
|
46
46
|
# Upgrade to the version the catalogue ships — shows installed → target, asks first
|
|
47
|
-
agentbundle upgrade --pack core
|
|
48
|
-
agentbundle upgrade --pack core
|
|
47
|
+
agentbundle upgrade --pack core
|
|
48
|
+
agentbundle upgrade --pack core --yes # skip the prompt (CI)
|
|
49
49
|
|
|
50
50
|
# Uninstall — previews remove (Tier-1) vs keep (your edits), asks first
|
|
51
51
|
agentbundle uninstall --pack core --dry-run
|
|
@@ -84,7 +84,13 @@
|
|
|
84
84
|
# `_append_layout_section` step (append-if-exists / never-create / never-
|
|
85
85
|
# overwrite) into an adopter-owned `agentbundle-layout.toml`. Additive +
|
|
86
86
|
# optional — packs pinned below v0.16 build and validate unchanged.
|
|
87
|
-
|
|
87
|
+
# RFC-0052 / ADR-0039+0040 (v0.17): the `[contract.shared-prefixes]` registry
|
|
88
|
+
# (below) classifies each allowed-prefix as `shared` (a path more than one
|
|
89
|
+
# adapter reads) or — by absence — `private`, and a shared prefix declares its
|
|
90
|
+
# reader cohort. cursor/gemini/copilot route the `skill` primitive to the shared
|
|
91
|
+
# `.agents/skills/` home (joining codex) and gain it in `allowed-prefixes` at
|
|
92
|
+
# both scopes; their agent/hook/command projection stays native.
|
|
93
|
+
version = "0.17"
|
|
88
94
|
|
|
89
95
|
# Sibling schemas this contract references — pack metadata and the
|
|
90
96
|
# per-pack Claude-plugin manifest. Defined by spec AC #3 + #4.
|
|
@@ -92,6 +98,16 @@ version = "0.16"
|
|
|
92
98
|
pack = "pack.schema.json"
|
|
93
99
|
plugin-manifest = "plugin-manifest.schema.json"
|
|
94
100
|
|
|
101
|
+
# Shared-prefix registry (RFC-0052 / ADR-0039). Each key is a `shared` prefix —
|
|
102
|
+
# a path more than one shipped adapter reads — mapped to its reader cohort (the
|
|
103
|
+
# shipped adapters that read it). Every prefix NOT listed here is `private`
|
|
104
|
+
# (adapter-exclusive). The install-time cross-adapter disclosure names a shared
|
|
105
|
+
# prefix's *other* shipped cohort members; the footprint gate co-owns a shared
|
|
106
|
+
# path across same-pack rows. A future adapter joins a cohort by one line here.
|
|
107
|
+
[contract.shared-prefixes]
|
|
108
|
+
".agents/skills/" = ["codex", "cursor", "gemini", "copilot"]
|
|
109
|
+
".kiro/skills/" = ["kiro-ide", "kiro-cli"]
|
|
110
|
+
|
|
95
111
|
# ---------------------------------------------------------------------------
|
|
96
112
|
# Primitive types — source paths within a pack's .apm/ directory
|
|
97
113
|
# ---------------------------------------------------------------------------
|
|
@@ -477,7 +493,10 @@ allowed-prefixes.user = [".kiro/", ".agentbundle/"]
|
|
|
477
493
|
[[adapter.copilot.projection]]
|
|
478
494
|
primitive = "skill"
|
|
479
495
|
mode = "direct-directory"
|
|
480
|
-
|
|
496
|
+
# RFC-0052 / ADR-0040: cohort skill home. Copilot reads `.agents/skills/`
|
|
497
|
+
# (project) and `~/.agents/skills/` (personal); routing skills there lets the
|
|
498
|
+
# whole cohort (codex, cursor, gemini, copilot) share one co-owned copy.
|
|
499
|
+
target-path = ".agents/skills/"
|
|
481
500
|
on-conflict = "prompt-then-preserve"
|
|
482
501
|
|
|
483
502
|
[[adapter.copilot.projection]]
|
|
@@ -517,12 +536,14 @@ repo = "."
|
|
|
517
536
|
user = "~"
|
|
518
537
|
# RFC-0024 + copilot-skills-and-web: repo-scope skills at `.github/skills/`,
|
|
519
538
|
# agents at `.github/agents/`, hook wiring + bodies at `.github/hooks/`.
|
|
520
|
-
|
|
521
|
-
#
|
|
522
|
-
|
|
523
|
-
#
|
|
524
|
-
#
|
|
525
|
-
|
|
539
|
+
# RFC-0052: skills route to the shared `.agents/skills/` home; agents + hooks
|
|
540
|
+
# stay native under `.github/` (ADR-0040 moves only the skill primitive).
|
|
541
|
+
allowed-prefixes.repo = [".agents/skills/", ".github/skills/", ".github/agents/", ".github/hooks/"]
|
|
542
|
+
# User-scope home is `~/.copilot/` for agents/hooks (the prefix the install
|
|
543
|
+
# rewrite produces) and `~/.agents/skills/` for skills (scope-agnostic, no
|
|
544
|
+
# rewrite). `.agentbundle/` carries the install state file
|
|
545
|
+
# (`~/.agentbundle/state.toml`), same as every other user-scope-capable adapter.
|
|
546
|
+
allowed-prefixes.user = [".agents/skills/", ".copilot/skills/", ".copilot/agents/", ".copilot/hooks/", ".agentbundle/"]
|
|
526
547
|
|
|
527
548
|
# ---------------------------------------------------------------------------
|
|
528
549
|
# Codex adapter — 5 projections (one per primitive)
|
|
@@ -596,7 +617,10 @@ allowed-prefixes.user = [".agents/skills/", ".codex/", ".agentbundle/"]
|
|
|
596
617
|
[[adapter.cursor.projection]]
|
|
597
618
|
primitive = "skill"
|
|
598
619
|
mode = "direct-directory"
|
|
599
|
-
|
|
620
|
+
# RFC-0052 / ADR-0040: cohort skill home. Cursor reads `.agents/skills/` and
|
|
621
|
+
# `~/.agents/skills/` alongside `.cursor/skills/`; route skills there to share
|
|
622
|
+
# one co-owned copy across the cohort.
|
|
623
|
+
target-path = ".agents/skills/"
|
|
600
624
|
on-conflict = "prompt-then-preserve"
|
|
601
625
|
|
|
602
626
|
[[adapter.cursor.projection]]
|
|
@@ -650,8 +674,10 @@ mode = "dropped"
|
|
|
650
674
|
[adapter.cursor.scope]
|
|
651
675
|
repo = "."
|
|
652
676
|
user = "~"
|
|
653
|
-
|
|
654
|
-
|
|
677
|
+
# RFC-0052: skills route to the shared `.agents/skills/` home (both scopes);
|
|
678
|
+
# agents/hooks stay under `.cursor/` (ADR-0040 moves only the skill primitive).
|
|
679
|
+
allowed-prefixes.repo = [".agents/skills/", ".cursor/", ".agentbundle/"]
|
|
680
|
+
allowed-prefixes.user = [".agents/skills/", ".cursor/", ".agentbundle/"]
|
|
655
681
|
|
|
656
682
|
# ---------------------------------------------------------------------------
|
|
657
683
|
# Gemini adapter — full-parity native adapter (RFC-0027 / ADR-0016 /
|
|
@@ -685,7 +711,10 @@ allowed-prefixes.user = [".cursor/", ".agentbundle/"]
|
|
|
685
711
|
[[adapter.gemini.projection]]
|
|
686
712
|
primitive = "skill"
|
|
687
713
|
mode = "direct-directory"
|
|
688
|
-
|
|
714
|
+
# RFC-0052 / ADR-0040: cohort skill home. Gemini CLI reads `.agents/skills/`
|
|
715
|
+
# and `~/.agents/skills/`, and its docs now make the alias precedence-winning
|
|
716
|
+
# over `.gemini/skills/`; route skills there to share one co-owned copy.
|
|
717
|
+
target-path = ".agents/skills/"
|
|
689
718
|
on-conflict = "prompt-then-preserve"
|
|
690
719
|
|
|
691
720
|
[[adapter.gemini.projection]]
|
|
@@ -744,8 +773,10 @@ mode = "dropped"
|
|
|
744
773
|
[adapter.gemini.scope]
|
|
745
774
|
repo = "."
|
|
746
775
|
user = "~"
|
|
747
|
-
|
|
748
|
-
|
|
776
|
+
# RFC-0052: skills route to the shared `.agents/skills/` home (both scopes);
|
|
777
|
+
# agents/hooks stay under `.gemini/` (ADR-0040 moves only the skill primitive).
|
|
778
|
+
allowed-prefixes.repo = [".agents/skills/", ".gemini/", ".agentbundle/"]
|
|
779
|
+
allowed-prefixes.user = [".agents/skills/", ".gemini/", ".agentbundle/"]
|
|
749
780
|
|
|
750
781
|
# ---------------------------------------------------------------------------
|
|
751
782
|
# Frontmatter mappings — rewrite rules applied when projecting a primitive
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Packaged install default — layer 4 of the catalogue-source resolution chain
|
|
2
|
+
# (RFC-0046 / ADR-0036). When `agentbundle install`/`upgrade` is run with no
|
|
3
|
+
# `catalogue` positional and layers 1–3 (explicit arg, user config, editable
|
|
4
|
+
# detection) yield nothing, this source is used.
|
|
5
|
+
#
|
|
6
|
+
# Private-fork pattern: blank the `source` value below (or delete this file
|
|
7
|
+
# entirely) to disable the packaged GitHub default — layers 1–3 still apply, so
|
|
8
|
+
# an editable clone or a `config set source` still resolves.
|
|
9
|
+
[defaults]
|
|
10
|
+
source = "git+https://github.com/eugenelim/agent-ready-repo"
|
|
@@ -72,7 +72,7 @@ def project_packs(pack_paths: list[Path], contract: dict, output_root: Path) ->
|
|
|
72
72
|
dir=str(agents_md.parent),
|
|
73
73
|
)
|
|
74
74
|
try:
|
|
75
|
-
with os.fdopen(fd, "w", encoding="utf-8") as handle:
|
|
75
|
+
with os.fdopen(fd, "w", encoding="utf-8", newline="\n") as handle:
|
|
76
76
|
handle.write(stripped)
|
|
77
77
|
os.replace(tmp_path, agents_md)
|
|
78
78
|
except BaseException:
|
|
@@ -251,7 +251,7 @@ def _project_agent_as_md(
|
|
|
251
251
|
out_fm["readonly"] = readonly
|
|
252
252
|
|
|
253
253
|
output_text = _serialize_frontmatter_md(out_fm) + body
|
|
254
|
-
(target_dir / entry.name).write_text(output_text, encoding="utf-8")
|
|
254
|
+
(target_dir / entry.name).write_text(output_text, encoding="utf-8", newline="\n")
|
|
255
255
|
|
|
256
256
|
|
|
257
257
|
def _derive_readonly(frontmatter: dict[str, Any]) -> bool | None:
|
|
@@ -357,7 +357,7 @@ def _project_hooks_json(source_dir: Path, output_root: Path, rule: dict) -> None
|
|
|
357
357
|
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
358
358
|
target_path.write_text(
|
|
359
359
|
json.dumps(existing, indent=2, sort_keys=True) + "\n",
|
|
360
|
-
encoding="utf-8",
|
|
360
|
+
encoding="utf-8", newline="\n",
|
|
361
361
|
)
|
|
362
362
|
|
|
363
363
|
|
|
@@ -220,7 +220,7 @@ def _project_agent_as_md(
|
|
|
220
220
|
frontmatter, body = _split_frontmatter(entry.read_text(encoding="utf-8"))
|
|
221
221
|
out_fm = _apply_mapping(frontmatter, mapping, entry.stem)
|
|
222
222
|
output_text = _serialize_frontmatter_md(out_fm) + body
|
|
223
|
-
(target_dir / entry.name).write_text(output_text, encoding="utf-8")
|
|
223
|
+
(target_dir / entry.name).write_text(output_text, encoding="utf-8", newline="\n")
|
|
224
224
|
|
|
225
225
|
|
|
226
226
|
def _apply_mapping(
|
|
@@ -386,7 +386,7 @@ def _project_settings_json(
|
|
|
386
386
|
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
387
387
|
target_path.write_text(
|
|
388
388
|
json.dumps(existing, indent=2, sort_keys=True) + "\n",
|
|
389
|
-
encoding="utf-8",
|
|
389
|
+
encoding="utf-8", newline="\n",
|
|
390
390
|
)
|
|
391
391
|
|
|
392
392
|
|
|
@@ -255,7 +255,7 @@ def _project_agent_as_md(
|
|
|
255
255
|
|
|
256
256
|
output_text = _serialize_frontmatter_md(rewritten) + body
|
|
257
257
|
destination = target_dir / entry.name # preserves .md extension
|
|
258
|
-
destination.write_text(output_text, encoding="utf-8")
|
|
258
|
+
destination.write_text(output_text, encoding="utf-8", newline="\n")
|
|
259
259
|
|
|
260
260
|
|
|
261
261
|
def _serialize_frontmatter_md(fields: dict[str, Any]) -> str:
|
|
@@ -498,7 +498,7 @@ def _run_per_pack_single(
|
|
|
498
498
|
)
|
|
499
499
|
destination.write_text(
|
|
500
500
|
json.dumps(derived, indent=2, sort_keys=False) + "\n",
|
|
501
|
-
encoding="utf-8",
|
|
501
|
+
encoding="utf-8", newline="\n",
|
|
502
502
|
)
|
|
503
503
|
# Defence-in-depth: re-validate the written file against the schema
|
|
504
504
|
# to catch any serialise/parse divergence introduced by json.dumps.
|
|
@@ -547,7 +547,7 @@ def _run_per_pack_apm(recipe: Recipe, packs: list[Pack], output_dir: Path) -> di
|
|
|
547
547
|
pack_metadata = tomllib.loads((pack.path / "pack.toml").read_text(encoding="utf-8"))
|
|
548
548
|
(per_pack_output / "apm.yml").write_text(
|
|
549
549
|
_render_apm_yml(pack_metadata.get("pack", {})),
|
|
550
|
-
encoding="utf-8",
|
|
550
|
+
encoding="utf-8", newline="\n",
|
|
551
551
|
)
|
|
552
552
|
apm_source = pack.path / ".apm"
|
|
553
553
|
if apm_source.exists():
|
|
@@ -568,7 +568,7 @@ def _run_per_pack_apm(recipe: Recipe, packs: list[Pack], output_dir: Path) -> di
|
|
|
568
568
|
(hooks_dir / "install-marker.py").write_bytes(writer_bytes)
|
|
569
569
|
(hooks_dir / "install-marker.json").write_text(
|
|
570
570
|
json.dumps(_APM_INSTALL_MARKER_HOOK_JSON, indent=2) + "\n",
|
|
571
|
-
encoding="utf-8",
|
|
571
|
+
encoding="utf-8", newline="\n",
|
|
572
572
|
)
|
|
573
573
|
|
|
574
574
|
# Project pack.toml verbatim. The writer reads it for
|
|
@@ -630,7 +630,7 @@ def _run_aggregate(recipe: Recipe, output_dir: Path) -> dict:
|
|
|
630
630
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
631
631
|
output_path.write_text(
|
|
632
632
|
json.dumps({"plugins": entries}, indent=2, sort_keys=True) + "\n",
|
|
633
|
-
encoding="utf-8",
|
|
633
|
+
encoding="utf-8", newline="\n",
|
|
634
634
|
)
|
|
635
635
|
return {"recipe": recipe.name, "type": recipe.type, "entries": len(entries)}
|
|
636
636
|
|
|
@@ -331,4 +331,4 @@ def project_codex_agent_toml(
|
|
|
331
331
|
)
|
|
332
332
|
toml_lines = _emit_fields(rewritten, body)
|
|
333
333
|
destination = target_dir / (entry.stem + ".toml")
|
|
334
|
-
destination.write_text("\n".join(toml_lines) + "\n", encoding="utf-8")
|
|
334
|
+
destination.write_text("\n".join(toml_lines) + "\n", encoding="utf-8", newline="\n")
|
|
@@ -208,4 +208,4 @@ def project_copilot_agent_md(
|
|
|
208
208
|
_validate_tools(frontmatter["tools"])
|
|
209
209
|
emitted["tools"] = frontmatter["tools"]
|
|
210
210
|
destination = target_dir / (entry.stem + ".agent.md")
|
|
211
|
-
destination.write_text(_emit(emitted, body), encoding="utf-8")
|
|
211
|
+
destination.write_text(_emit(emitted, body), encoding="utf-8", newline="\n")
|
{agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/gemini_command_toml.py
RENAMED
|
@@ -227,4 +227,4 @@ def project_gemini_command_toml(
|
|
|
227
227
|
|
|
228
228
|
destination = target_root / relative.with_suffix(".toml")
|
|
229
229
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
230
|
-
destination.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
230
|
+
destination.write_text("\n".join(lines) + "\n", encoding="utf-8", newline="\n")
|
{agentbundle-0.7.0 → agentbundle-0.9.0}/agentbundle/build/projections/merge_into_agent_json.py
RENAMED
|
@@ -243,7 +243,7 @@ def _atomic_write(target_path: Path, data: dict) -> None:
|
|
|
243
243
|
serialised = json.dumps(data, indent=2, sort_keys=False) + "\n"
|
|
244
244
|
with tempfile.NamedTemporaryFile(
|
|
245
245
|
mode="w",
|
|
246
|
-
encoding="utf-8",
|
|
246
|
+
encoding="utf-8", newline="\n",
|
|
247
247
|
dir=str(target_path.parent),
|
|
248
248
|
prefix=target_path.name + ".",
|
|
249
249
|
suffix=".tmp",
|
|
@@ -54,5 +54,5 @@ def project_merge_json(source_dir: Path, output_root: Path, rule: dict) -> None:
|
|
|
54
54
|
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
55
55
|
target_path.write_text(
|
|
56
56
|
json.dumps(existing, indent=2, sort_keys=True) + "\n",
|
|
57
|
-
encoding="utf-8",
|
|
57
|
+
encoding="utf-8", newline="\n",
|
|
58
58
|
)
|
|
@@ -296,7 +296,7 @@ def _atomic_write(target_path: Path, data: dict) -> None:
|
|
|
296
296
|
serialised = json.dumps(data, indent=2, sort_keys=False) + "\n"
|
|
297
297
|
with tempfile.NamedTemporaryFile(
|
|
298
298
|
mode="w",
|
|
299
|
-
encoding="utf-8",
|
|
299
|
+
encoding="utf-8", newline="\n",
|
|
300
300
|
dir=str(target_path.parent),
|
|
301
301
|
prefix=target_path.name + ".",
|
|
302
302
|
suffix=".tmp",
|
|
@@ -24,7 +24,9 @@ consumer.
|
|
|
24
24
|
Self-host scope (see docs/specs/self-hosting/spec.md § Phased rollout):
|
|
25
25
|
the `SELF_HOST_ADAPTERS` allow-list runs `claude-code` and `codex`.
|
|
26
26
|
Kiro and Copilot stay distribution-only so self-host does not project
|
|
27
|
-
`.kiro/` or `.github/instructions/`.
|
|
27
|
+
`.kiro/` or `.github/instructions/`. Both `SELF_HOST_ADAPTERS` and
|
|
28
|
+
`SELF_HOST_PACKS` are sourced from `recipes/self-host.toml` (see the
|
|
29
|
+
`_DEFAULT_*` block below); the values named here are the current defaults.
|
|
28
30
|
"""
|
|
29
31
|
|
|
30
32
|
from __future__ import annotations
|
|
@@ -79,27 +81,76 @@ TARGET_PATHS = (
|
|
|
79
81
|
Path("AGENTS.md"),
|
|
80
82
|
)
|
|
81
83
|
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
# are
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
# `monorepo-extras` is repo-only by metadata but the bundle is not a
|
|
94
|
-
# canonical monorepo consumer of `new-package`, so it's left out too.
|
|
95
|
-
# `_aggregate_marketplace` intentionally ignores this filter — the
|
|
96
|
-
# catalogue advertises every pack.
|
|
97
|
-
SELF_HOST_PACKS: tuple[str, ...] = (
|
|
84
|
+
# Built-in fallbacks for the self-host pack / adapter allow-lists. The
|
|
85
|
+
# authoritative values live in `recipes/self-host.toml` (read at import below);
|
|
86
|
+
# these are used only when that recipe is missing a key or can't be read, so
|
|
87
|
+
# module import stays total. Kiro and Copilot remain in the contract for
|
|
88
|
+
# distribution builds but are excluded from the self-host runner. This repo is
|
|
89
|
+
# the catalogue's home, not an adopter, so `make build-self` only projects the
|
|
90
|
+
# in-house packs; `_aggregate_marketplace` intentionally ignores the pack
|
|
91
|
+
# filter — the catalogue advertises every pack. See the recipe for the full
|
|
92
|
+
# rationale behind the pack selection.
|
|
93
|
+
_DEFAULT_SELF_HOST_ADAPTERS: tuple[str, ...] = ("claude-code", "codex")
|
|
94
|
+
_DEFAULT_SELF_HOST_PACKS: tuple[str, ...] = (
|
|
98
95
|
"core",
|
|
99
96
|
"governance-extras",
|
|
100
97
|
"user-guide-diataxis",
|
|
101
98
|
)
|
|
102
99
|
|
|
100
|
+
_SELF_HOST_RECIPE = "self-host"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _read_recipe_text(name: str) -> str | None:
|
|
104
|
+
"""Return the text of `recipes/<name>.toml`, or None if unreadable.
|
|
105
|
+
|
|
106
|
+
Mirrors `build.main`'s recipe resolution: filesystem first (dev / editable
|
|
107
|
+
install), then `importlib.resources` (zipapp, where the package lives inside
|
|
108
|
+
a `.pyz` archive that `Path.exists()` cannot traverse).
|
|
109
|
+
"""
|
|
110
|
+
recipe_path = Path(__file__).resolve().parent / "recipes" / f"{name}.toml"
|
|
111
|
+
if recipe_path.exists():
|
|
112
|
+
return recipe_path.read_text(encoding="utf-8")
|
|
113
|
+
try:
|
|
114
|
+
from importlib.resources import files
|
|
115
|
+
|
|
116
|
+
resource = files("agentbundle.build").joinpath(f"recipes/{name}.toml")
|
|
117
|
+
if resource.is_file():
|
|
118
|
+
return resource.read_text(encoding="utf-8")
|
|
119
|
+
except (FileNotFoundError, ModuleNotFoundError):
|
|
120
|
+
pass
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _extract_self_host_lists(
|
|
125
|
+
recipe: dict,
|
|
126
|
+
) -> tuple[tuple[str, ...], tuple[str, ...]]:
|
|
127
|
+
"""Pull `(packs, adapters)` from a parsed self-host recipe, substituting the
|
|
128
|
+
built-in defaults for any list that is absent or empty."""
|
|
129
|
+
body = recipe.get("recipe", {})
|
|
130
|
+
packs = tuple(body.get("packs", {}).get("include", ())) or _DEFAULT_SELF_HOST_PACKS
|
|
131
|
+
adapters = (
|
|
132
|
+
tuple(body.get("adapters", {}).get("targets", ()))
|
|
133
|
+
or _DEFAULT_SELF_HOST_ADAPTERS
|
|
134
|
+
)
|
|
135
|
+
return packs, adapters
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _load_self_host_lists() -> tuple[tuple[str, ...], tuple[str, ...]]:
|
|
139
|
+
"""Read the self-host allow-lists from the recipe. Total: any read or parse
|
|
140
|
+
failure falls back to the built-in defaults so module import never raises."""
|
|
141
|
+
try:
|
|
142
|
+
text = _read_recipe_text(_SELF_HOST_RECIPE)
|
|
143
|
+
if text is None:
|
|
144
|
+
return _DEFAULT_SELF_HOST_PACKS, _DEFAULT_SELF_HOST_ADAPTERS
|
|
145
|
+
return _extract_self_host_lists(tomllib.loads(text))
|
|
146
|
+
except Exception:
|
|
147
|
+
# Unreadable (non-UTF-8 / permission / IO) or malformed recipe — fall
|
|
148
|
+
# back so module import is total (AC3).
|
|
149
|
+
return _DEFAULT_SELF_HOST_PACKS, _DEFAULT_SELF_HOST_ADAPTERS
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
SELF_HOST_PACKS, SELF_HOST_ADAPTERS = _load_self_host_lists()
|
|
153
|
+
|
|
103
154
|
|
|
104
155
|
def _filter_self_host_packs(pack_paths: list[Path]) -> list[Path]:
|
|
105
156
|
"""Return the subset of *pack_paths* whose directory name is in
|
|
@@ -195,7 +246,7 @@ def resolve_markers(
|
|
|
195
246
|
text,
|
|
196
247
|
)
|
|
197
248
|
if replaced != text:
|
|
198
|
-
path.write_text(replaced, encoding="utf-8")
|
|
249
|
+
path.write_text(replaced, encoding="utf-8", newline="\n")
|
|
199
250
|
modified += 1
|
|
200
251
|
return modified
|
|
201
252
|
|
|
@@ -270,7 +321,7 @@ def _compose_agents_md(
|
|
|
270
321
|
body = body_path.read_text(encoding="utf-8").replace("\r\n", "\n")
|
|
271
322
|
if body and not body.endswith("\n"):
|
|
272
323
|
body += "\n"
|
|
273
|
-
target_path.write_text(body, encoding="utf-8")
|
|
324
|
+
target_path.write_text(body, encoding="utf-8", newline="\n")
|
|
274
325
|
|
|
275
326
|
if footer_path.exists():
|
|
276
327
|
text = target_path.read_text(encoding="utf-8")
|
|
@@ -279,7 +330,7 @@ def _compose_agents_md(
|
|
|
279
330
|
text += "\n"
|
|
280
331
|
if footer and not footer.endswith("\n"):
|
|
281
332
|
footer += "\n"
|
|
282
|
-
target_path.write_text(text + footer, encoding="utf-8")
|
|
333
|
+
target_path.write_text(text + footer, encoding="utf-8", newline="\n")
|
|
283
334
|
return target_path
|
|
284
335
|
|
|
285
336
|
|
|
@@ -329,6 +380,7 @@ EXCLUDED_PATTERNS: tuple[str, ...] = (
|
|
|
329
380
|
"README.md", # root-level; nested README.md not excluded
|
|
330
381
|
"LICENSE-*",
|
|
331
382
|
".gitignore",
|
|
383
|
+
".gitattributes",
|
|
332
384
|
".github/**",
|
|
333
385
|
"AGENTS.local.md",
|
|
334
386
|
"AGENTS.md", # root-level; nested AGENTS.md not excluded
|
|
@@ -535,7 +587,7 @@ def _aggregate_marketplace(
|
|
|
535
587
|
}
|
|
536
588
|
target.write_text(
|
|
537
589
|
json.dumps(payload, indent=2, sort_keys=True) + "\n",
|
|
538
|
-
encoding="utf-8",
|
|
590
|
+
encoding="utf-8", newline="\n",
|
|
539
591
|
)
|
|
540
592
|
return target
|
|
541
593
|
|
|
@@ -1463,10 +1515,42 @@ def run_build_check_drift_gates(
|
|
|
1463
1515
|
return 0
|
|
1464
1516
|
|
|
1465
1517
|
|
|
1518
|
+
def _refuse_fixture_packs_dir(packs_dir: Path, *, dry_run: bool) -> int | None:
|
|
1519
|
+
"""Refuse a real-write self-host whose `packs_dir` points into
|
|
1520
|
+
`tests/fixtures/` (which would overwrite the working tree with fixture
|
|
1521
|
+
data), unless `ALLOW_FIXTURE_PACKS` is set.
|
|
1522
|
+
|
|
1523
|
+
This is the cross-platform home of the guard that used to live only in the
|
|
1524
|
+
Makefile `build-self` recipe — so the make-free entry
|
|
1525
|
+
`python -m agentbundle.build self` (the only way to run build-self on
|
|
1526
|
+
Windows) is protected too. Returns a non-zero exit code to refuse, or
|
|
1527
|
+
`None` to proceed. Dry-run writes to a shadow temp dir, so it is never
|
|
1528
|
+
guarded (matching the `run_self_host` dirty-tree check). `as_posix()`
|
|
1529
|
+
normalises separators so the match is Windows-safe.
|
|
1530
|
+
"""
|
|
1531
|
+
if dry_run or os.environ.get("ALLOW_FIXTURE_PACKS"):
|
|
1532
|
+
return None
|
|
1533
|
+
# Trailing slash mirrors the historical Makefile glob `*tests/fixtures/*`
|
|
1534
|
+
# exactly — so a sibling like `my-tests/fixtures-backup/` doesn't over-match.
|
|
1535
|
+
if "tests/fixtures/" in packs_dir.as_posix():
|
|
1536
|
+
print(
|
|
1537
|
+
"self-host: refusing — --packs-dir points into tests/fixtures/; "
|
|
1538
|
+
"this would overwrite your working tree with fixture data. Set "
|
|
1539
|
+
"ALLOW_FIXTURE_PACKS=1 to override, or use --packs-dir packs.",
|
|
1540
|
+
file=sys.stderr,
|
|
1541
|
+
)
|
|
1542
|
+
return 2
|
|
1543
|
+
return None
|
|
1544
|
+
|
|
1545
|
+
|
|
1466
1546
|
def cmd_self(args) -> int:
|
|
1547
|
+
packs_dir = Path(args.packs_dir).resolve()
|
|
1548
|
+
refusal = _refuse_fixture_packs_dir(packs_dir, dry_run=args.dry_run)
|
|
1549
|
+
if refusal is not None:
|
|
1550
|
+
return refusal
|
|
1467
1551
|
return run_self_host(
|
|
1468
1552
|
working_tree=Path(args.output_dir).resolve(),
|
|
1469
|
-
packs_dir=
|
|
1553
|
+
packs_dir=packs_dir,
|
|
1470
1554
|
dry_run=args.dry_run,
|
|
1471
1555
|
force=args.force,
|
|
1472
1556
|
no_symlink=getattr(args, "no_symlink", False),
|