agentbundle 0.11.1__tar.gz → 0.12.1__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.11.1 → agentbundle-0.12.1}/PKG-INFO +2 -2
- {agentbundle-0.11.1 → agentbundle-0.12.1}/README.md +1 -1
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/_data/pack.schema.json +20 -0
- agentbundle-0.12.1/agentbundle/_data/plugin-manifest.derived.schema.json +73 -0
- agentbundle-0.11.1/agentbundle/_data/plugin-manifest.derived.schema.json → agentbundle-0.12.1/agentbundle/_data/plugin-manifest.schema.json +14 -12
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/adapters/claude_code.py +15 -4
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/adapters/codex.py +16 -5
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/adapters/copilot.py +19 -8
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/adapters/cursor.py +2 -2
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/adapters/kiro.py +14 -1
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/main.py +100 -8
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/projections/gemini_command_toml.py +2 -2
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/self_host.py +18 -12
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_plugin_manifest_schema.py +30 -4
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_projectable_subset.py +18 -4
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_self_host_check.py +39 -60
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_shipped_packs_v08_declarations.py +2 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/_common.py +49 -33
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/diff.py +3 -3
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/install.py +58 -24
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/uninstall.py +3 -2
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/upgrade.py +25 -21
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/safety.py +38 -22
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/version.py +1 -1
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle.egg-info/PKG-INFO +2 -2
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle.egg-info/SOURCES.txt +2 -1
- {agentbundle-0.11.1 → agentbundle-0.12.1}/pyproject.toml +1 -1
- agentbundle-0.12.1/tests/test_linear_primitive.py +133 -0
- agentbundle-0.11.1/agentbundle/_data/plugin-manifest.schema.json +0 -28
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/__init__.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/__main__.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/_data/adapter.schema.json +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/_data/adapter.toml +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/_data/install-defaults.toml +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/_data/install-marker.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/_data/profile.schema.json +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/__init__.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/__main__.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/adapter_root_bins.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/adapters/__init__.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/adapters/gemini.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/adapters/kiro_cli.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/adapters/kiro_ide.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/contract.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/lint_packs.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/phase_order.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/projections/__init__.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/projections/codex_agent_toml.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/projections/copilot_agent_md.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/projections/copilot_hooks_json.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/projections/direct_directory.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/projections/hook_id.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/projections/kiro_ide_hook.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/projections/merge_into_agent_json.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/projections/merge_json.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/projections/user_merge_json.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/scope_rails.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/shared_libs.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/target_resolver.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/__init__.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_adapter_claude_code.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_adapter_codex.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_adapter_copilot.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_adapter_cursor.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_adapter_gemini.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_adapter_kiro.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_adapter_kiro_alias.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_adapter_kiro_cli.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_adapter_kiro_ide.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_adapter_root_bins_projection.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_architect_design_reviewer_projection.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_architect_design_reviewer_rubric_parity.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_build_ships_seeds.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_contract.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_contract_scope.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_contract_v07.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_contract_v08.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_direct_directory_cleanup.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_end_to_end_build.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_lint_agents_md_diataxis_block.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_lint_agents_md_legacy_block.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_lint_agents_md_risk_block.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_lint_packs.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_load_pack_hook_wiring_safely.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_pack_schema.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_pack_schema_allowed_adapters.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_pack_schema_install.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_pipeline.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_projections_merge_json.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_scope_rails.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_security.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_self_host_fixture_guard.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_self_host_recipe_config.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_shared_libs_projection.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_shared_prefix_contract.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_shipped_packs_v07_declarations.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_user_libs_projection.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_validate.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/tests/test_writers_emit_lf.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/user_libs.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/validate.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/catalogue.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/categories.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/cli.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/__init__.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/_drop_warning.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/adapt.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/config.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/init_state.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/list_installed.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/list_packs.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/list_profiles.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/list_targets.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/profile.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/reconcile.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/render.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/scaffold.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/show.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/commands/validate.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/config.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/pack_inventory.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/render.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/scope.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/source_defaults.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/statelock.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/user_config.py +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle.egg-info/dependency_links.txt +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle.egg-info/entry_points.txt +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle.egg-info/top_level.txt +0 -0
- {agentbundle-0.11.1 → agentbundle-0.12.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentbundle
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.1
|
|
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
|
|
@@ -41,7 +41,7 @@ No catalogue argument needed: it defaults to the agent-ready-repo catalogue. It
|
|
|
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 --scope user
|
|
44
|
+
agentbundle install --pack desk-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.
|
|
@@ -23,7 +23,7 @@ No catalogue argument needed: it defaults to the agent-ready-repo catalogue. It
|
|
|
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 --scope user
|
|
26
|
+
agentbundle install --pack desk-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.
|
|
@@ -150,6 +150,26 @@
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
|
+
},
|
|
154
|
+
"first-value": {
|
|
155
|
+
"type": "object",
|
|
156
|
+
"description": "Pack first-value contract (RFC-0064 Amendment #4). Level A required for all packs; Level B required when level-b = true.",
|
|
157
|
+
"properties": {
|
|
158
|
+
"audience-posture": {"type": "string", "enum": ["non-technical", "mixed", "technical"]},
|
|
159
|
+
"surfaces": {"type": "array", "items": {"type": "string"}},
|
|
160
|
+
"prerequisites": {"type": "array", "items": {"type": "string"}},
|
|
161
|
+
"verification": {"type": "string"},
|
|
162
|
+
"recovery": {"type": "string"},
|
|
163
|
+
"level-b": {"type": "boolean"},
|
|
164
|
+
"starter-task": {"type": "string"},
|
|
165
|
+
"starter-prompt": {"type": "string"},
|
|
166
|
+
"expected-result": {"type": "string"},
|
|
167
|
+
"next-action": {"type": "string"},
|
|
168
|
+
"tutorial": {"type": "string"},
|
|
169
|
+
"writes-to-repo": {"type": "boolean"},
|
|
170
|
+
"safety-gate": {"type": "string"}
|
|
171
|
+
},
|
|
172
|
+
"additionalProperties": false
|
|
153
173
|
}
|
|
154
174
|
},
|
|
155
175
|
"if": {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"required": ["name", "version", "description"],
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"properties": {
|
|
6
|
+
"name": {"type": "string"},
|
|
7
|
+
"version": {"type": "string"},
|
|
8
|
+
"description": {"type": "string"},
|
|
9
|
+
"skills": {
|
|
10
|
+
"type": "array",
|
|
11
|
+
"items": {"type": "string"}
|
|
12
|
+
},
|
|
13
|
+
"agents": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": {"type": "string"}
|
|
16
|
+
},
|
|
17
|
+
"author": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": ["name"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"name": {"type": "string"},
|
|
22
|
+
"email": {"type": "string"}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"license": {"type": "string"},
|
|
26
|
+
"homepage": {"type": "string"},
|
|
27
|
+
"repository": {"type": "string"},
|
|
28
|
+
"keywords": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"items": {"type": "string"}
|
|
31
|
+
},
|
|
32
|
+
"displayName": {"type": "string"},
|
|
33
|
+
"source": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"required": ["source", "repo", "branch", "directory"],
|
|
36
|
+
"additionalProperties": false,
|
|
37
|
+
"properties": {
|
|
38
|
+
"source": {"type": "string", "enum": ["github"]},
|
|
39
|
+
"repo": {"type": "string"},
|
|
40
|
+
"branch": {"type": "string"},
|
|
41
|
+
"directory": {"type": "string"}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"hooks": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"additionalProperties": false,
|
|
47
|
+
"properties": {
|
|
48
|
+
"SessionStart": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"items": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"additionalProperties": false,
|
|
53
|
+
"properties": {
|
|
54
|
+
"hooks": {
|
|
55
|
+
"type": "array",
|
|
56
|
+
"items": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"required": ["type", "command"],
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"properties": {
|
|
61
|
+
"type": {"type": "string", "enum": ["command"]},
|
|
62
|
+
"command": {"type": "string"},
|
|
63
|
+
"timeout": {"type": "integer"}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -14,7 +14,14 @@
|
|
|
14
14
|
"type": "array",
|
|
15
15
|
"items": {"type": "string"}
|
|
16
16
|
},
|
|
17
|
-
"author": {
|
|
17
|
+
"author": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": ["name"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"name": {"type": "string"},
|
|
22
|
+
"email": {"type": "string"}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
18
25
|
"license": {"type": "string"},
|
|
19
26
|
"homepage": {"type": "string"},
|
|
20
27
|
"repository": {"type": "string"},
|
|
@@ -24,19 +31,14 @@
|
|
|
24
31
|
},
|
|
25
32
|
"category": {"type": "string"},
|
|
26
33
|
"displayName": {"type": "string"},
|
|
27
|
-
"
|
|
34
|
+
"source": {
|
|
28
35
|
"type": "object",
|
|
36
|
+
"additionalProperties": false,
|
|
29
37
|
"properties": {
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"required": ["command"],
|
|
35
|
-
"properties": {
|
|
36
|
-
"command": {"type": "string"}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
38
|
+
"source": {"type": "string"},
|
|
39
|
+
"repo": {"type": "string"},
|
|
40
|
+
"branch": {"type": "string"},
|
|
41
|
+
"directory": {"type": "string"}
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
}
|
|
@@ -107,6 +107,17 @@ def _project_single(pack_path: Path, contract: dict, output_root: Path) -> None:
|
|
|
107
107
|
raise ValueError(f"claude-code: unhandled mode {mode!r} for {primitive_name}")
|
|
108
108
|
|
|
109
109
|
|
|
110
|
+
def _ignore_symlinks(directory: str, names: list[str]) -> set[str]:
|
|
111
|
+
"""`shutil.copytree` ignore callback: skip every symlink member.
|
|
112
|
+
|
|
113
|
+
Drops nested symlinks so they are never reproduced in the output
|
|
114
|
+
tree. The top-level `is_symlink()` skip in `_project_direct_directory`
|
|
115
|
+
covers the skill root; this covers the subtree.
|
|
116
|
+
"""
|
|
117
|
+
base = Path(directory)
|
|
118
|
+
return {name for name in names if (base / name).is_symlink()}
|
|
119
|
+
|
|
120
|
+
|
|
110
121
|
def _project_direct_directory(source_dir: Path, target_dir: Path) -> None:
|
|
111
122
|
for entry in sorted(source_dir.iterdir()):
|
|
112
123
|
# Defense-in-depth — `lint-packs` rejects packs that ship
|
|
@@ -125,10 +136,10 @@ def _project_direct_directory(source_dir: Path, target_dir: Path) -> None:
|
|
|
125
136
|
destination.unlink()
|
|
126
137
|
elif destination.exists():
|
|
127
138
|
shutil.rmtree(destination)
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
# cannot exfiltrate the target
|
|
131
|
-
shutil.copytree(entry, destination,
|
|
139
|
+
# `ignore=_ignore_symlinks` drops nested symlinks so they are
|
|
140
|
+
# never reproduced in the output tree. A malicious pack with a
|
|
141
|
+
# symlink to /etc/passwd cannot exfiltrate the target.
|
|
142
|
+
shutil.copytree(entry, destination, ignore=_ignore_symlinks)
|
|
132
143
|
|
|
133
144
|
|
|
134
145
|
def _project_direct_file(source_dir: Path, output_root: Path, target_prefix: str) -> None:
|
|
@@ -29,6 +29,17 @@ from agentbundle.build.projections.codex_agent_toml import (
|
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
def _ignore_symlinks(directory: str, names: list[str]) -> set[str]:
|
|
33
|
+
"""`shutil.copytree` ignore callback: skip every symlink member.
|
|
34
|
+
|
|
35
|
+
Drops nested symlinks so they are never reproduced in the output
|
|
36
|
+
tree. The top-level `is_symlink()` skip in `project_packs` covers
|
|
37
|
+
the skill root; this covers the subtree.
|
|
38
|
+
"""
|
|
39
|
+
base = Path(directory)
|
|
40
|
+
return {name for name in names if (base / name).is_symlink()}
|
|
41
|
+
|
|
42
|
+
|
|
32
43
|
def _iter_primitives(contract: dict) -> Iterator[str]:
|
|
33
44
|
"""Yield Codex's projected primitive names in phase order."""
|
|
34
45
|
adapter_block = contract["adapter"]["codex"]
|
|
@@ -131,11 +142,11 @@ def project_packs(pack_paths: list[Path], contract: dict, output_root: Path) ->
|
|
|
131
142
|
destination.unlink()
|
|
132
143
|
elif destination.exists():
|
|
133
144
|
shutil.rmtree(destination)
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
# pack with a symlink to /etc/passwd
|
|
137
|
-
# exfiltrate.
|
|
138
|
-
shutil.copytree(entry, destination,
|
|
145
|
+
# `ignore=_ignore_symlinks` drops nested symlinks
|
|
146
|
+
# entirely — never reproduced in the output tree.
|
|
147
|
+
# A malicious pack with a symlink to /etc/passwd
|
|
148
|
+
# cannot exfiltrate.
|
|
149
|
+
shutil.copytree(entry, destination, ignore=_ignore_symlinks)
|
|
139
150
|
# Bound to `skill` only per spec § Never do. Other
|
|
140
151
|
# direct-directory primitives opt in explicitly.
|
|
141
152
|
if primitive_name == "skill":
|
|
@@ -34,6 +34,17 @@ from agentbundle.build.projections.copilot_hooks_json import (
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
def _ignore_symlinks(directory: str, names: list[str]) -> set[str]:
|
|
38
|
+
"""`shutil.copytree` ignore callback: skip every symlink member.
|
|
39
|
+
|
|
40
|
+
Drops nested symlinks so they are never reproduced in the output
|
|
41
|
+
tree. The top-level `is_symlink()` skip in `_project_direct_directory`
|
|
42
|
+
covers the skill root; this covers the subtree.
|
|
43
|
+
"""
|
|
44
|
+
base = Path(directory)
|
|
45
|
+
return {name for name in names if (base / name).is_symlink()}
|
|
46
|
+
|
|
47
|
+
|
|
37
48
|
def _iter_primitives(contract: dict) -> Iterator[str]:
|
|
38
49
|
"""Yield Copilot's projected primitive names in phase order."""
|
|
39
50
|
adapter_block = contract["adapter"]["copilot"]
|
|
@@ -86,13 +97,13 @@ def _project_direct_directory(
|
|
|
86
97
|
"""Copy each `<name>/` source tree to the target directory verbatim, then
|
|
87
98
|
sweep orphaned target dirs no longer backed by a source.
|
|
88
99
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
100
|
+
A symlink at the entry root is skipped (defense-in-depth — `lint-packs`
|
|
101
|
+
already refuses symlinked packs, but a direct `project()` caller bypasses
|
|
102
|
+
that gate). `ignore=_ignore_symlinks` drops nested symlinks so they are
|
|
103
|
+
never reproduced in the output tree. A destination symlink is `unlink`ed
|
|
104
|
+
(never `rmtree`d) before the copy. The orphan sweep is **bounded to the
|
|
105
|
+
`skill` primitive's** expected source names, so it can never delete sibling
|
|
106
|
+
`.github/agents/` or `.github/hooks/` content.
|
|
96
107
|
"""
|
|
97
108
|
target_dir = output_root / rule["target-path"].rstrip("/")
|
|
98
109
|
target_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -107,6 +118,6 @@ def _project_direct_directory(
|
|
|
107
118
|
destination.unlink()
|
|
108
119
|
elif destination.exists():
|
|
109
120
|
shutil.rmtree(destination)
|
|
110
|
-
shutil.copytree(entry, destination,
|
|
121
|
+
shutil.copytree(entry, destination, ignore=_ignore_symlinks)
|
|
111
122
|
if primitive_name == "skill":
|
|
112
123
|
sweep_orphans(target_dir, expected_names)
|
|
@@ -143,8 +143,8 @@ def _ignore_symlinks(directory: str, names: list[str]) -> set[str]:
|
|
|
143
143
|
secret the symlink points at) into the projection. The top-level
|
|
144
144
|
`is_symlink()` skip in `_project_direct_directory` only covers the skill
|
|
145
145
|
root; this covers the subtree. (Build runs on trusted `packs/`; this is
|
|
146
|
-
the install-from-untrusted-catalogue defense —
|
|
147
|
-
|
|
146
|
+
the install-from-untrusted-catalogue defense — mirrored in all five
|
|
147
|
+
direct-directory adapters.)
|
|
148
148
|
"""
|
|
149
149
|
base = Path(directory)
|
|
150
150
|
return {name for name in names if (base / name).is_symlink()}
|
|
@@ -369,6 +369,17 @@ def _project_hook_wiring_to_agent_json(
|
|
|
369
369
|
# ---------------------------------------------------------------------------
|
|
370
370
|
|
|
371
371
|
|
|
372
|
+
def _ignore_symlinks(directory: str, names: list[str]) -> set[str]:
|
|
373
|
+
"""`shutil.copytree` ignore callback: skip every symlink member.
|
|
374
|
+
|
|
375
|
+
Drops nested symlinks so they are never reproduced in the output
|
|
376
|
+
tree. The top-level `is_symlink()` skip in `_project_direct_directory`
|
|
377
|
+
covers the skill root; this covers the subtree.
|
|
378
|
+
"""
|
|
379
|
+
base = Path(directory)
|
|
380
|
+
return {name for name in names if (base / name).is_symlink()}
|
|
381
|
+
|
|
382
|
+
|
|
372
383
|
def _project_direct_directory(source_dir: Path, target_dir: Path) -> None:
|
|
373
384
|
for entry in sorted(source_dir.iterdir()):
|
|
374
385
|
# Defense-in-depth — `lint-packs` rejects packs that ship
|
|
@@ -386,7 +397,9 @@ def _project_direct_directory(source_dir: Path, target_dir: Path) -> None:
|
|
|
386
397
|
destination.unlink()
|
|
387
398
|
elif destination.exists():
|
|
388
399
|
shutil.rmtree(destination)
|
|
389
|
-
|
|
400
|
+
# `ignore=_ignore_symlinks` drops nested symlinks so they are
|
|
401
|
+
# never reproduced in the output tree.
|
|
402
|
+
shutil.copytree(entry, destination, ignore=_ignore_symlinks)
|
|
390
403
|
|
|
391
404
|
|
|
392
405
|
def _project_direct_file(source_dir: Path, output_root: Path, target_prefix: str) -> None:
|
|
@@ -18,6 +18,7 @@ paths.
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
import json
|
|
21
|
+
import re
|
|
21
22
|
import shutil
|
|
22
23
|
import sys
|
|
23
24
|
import tomllib
|
|
@@ -33,6 +34,22 @@ PACKAGE_ROOT = Path(__file__).resolve().parent
|
|
|
33
34
|
RECIPES_DIR = PACKAGE_ROOT / "recipes"
|
|
34
35
|
REPO_ROOT = PACKAGE_ROOT.parent.parent.parent.parent
|
|
35
36
|
|
|
37
|
+
# Canonical branch name for the published Claude-plugins distribution.
|
|
38
|
+
# All marketplace entries' source.branch must match this value so `claude plugin
|
|
39
|
+
# install` fetches from the right branch.
|
|
40
|
+
_DIST_BRANCH = "claude-plugins-dist"
|
|
41
|
+
|
|
42
|
+
# Marketplace description included in both the dist and self-hosted marketplace.json.
|
|
43
|
+
# The description field is required for `claude plugin validate --strict` to pass.
|
|
44
|
+
_MARKETPLACE_DESCRIPTION = (
|
|
45
|
+
"Agent skills, subagents, and hooks for Claude Code and other coding agents."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Match https://github.com/owner/repo (optional trailing .git and slash).
|
|
49
|
+
_GITHUB_URL_RE = re.compile(
|
|
50
|
+
r"^https?://github\.com/([^/]+/[^/]+?)(?:\.git)?/?$"
|
|
51
|
+
)
|
|
52
|
+
|
|
36
53
|
|
|
37
54
|
def _bundled_or_repo(name: str) -> Path:
|
|
38
55
|
"""Locate a data file shipped under both `agentbundle/_data/` and
|
|
@@ -187,8 +204,11 @@ def derive_projectable_subset(pack_toml: dict) -> dict:
|
|
|
187
204
|
subset into the claude-plugins + apm routes (the ``plugin.json`` /
|
|
188
205
|
``marketplace.json`` entry). Fixed mapping:
|
|
189
206
|
|
|
190
|
-
- ``author`` ← first ``[[pack.maintainers]]``,
|
|
191
|
-
``"
|
|
207
|
+
- ``author`` ← first ``[[pack.maintainers]]``, as object
|
|
208
|
+
``{"name": ..., "email": ...}`` (email omitted when absent).
|
|
209
|
+
- ``source`` ← derived from ``[pack.links].repository`` when it is a
|
|
210
|
+
GitHub URL: ``{"source": "github", "repo": "owner/name",
|
|
211
|
+
"branch": _DIST_BRANCH, "directory": pack.name}``.
|
|
192
212
|
- ``license`` ← ``[pack].license`` (verbatim).
|
|
193
213
|
- ``homepage`` ← ``[pack.links].homepage`` (verbatim).
|
|
194
214
|
- ``repository`` ← ``[pack.links].repository`` (verbatim).
|
|
@@ -214,10 +234,10 @@ def derive_projectable_subset(pack_toml: dict) -> dict:
|
|
|
214
234
|
name = first.get("name")
|
|
215
235
|
email = first.get("email")
|
|
216
236
|
if isinstance(name, str) and name:
|
|
237
|
+
author_obj: dict = {"name": name}
|
|
217
238
|
if isinstance(email, str) and email:
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
out["author"] = name
|
|
239
|
+
author_obj["email"] = email
|
|
240
|
+
out["author"] = author_obj
|
|
221
241
|
|
|
222
242
|
license_ = pack.get("license")
|
|
223
243
|
if isinstance(license_, str) and license_:
|
|
@@ -231,6 +251,18 @@ def derive_projectable_subset(pack_toml: dict) -> dict:
|
|
|
231
251
|
repository = links.get("repository")
|
|
232
252
|
if isinstance(repository, str) and repository:
|
|
233
253
|
out["repository"] = repository
|
|
254
|
+
# Derive source field: tells Claude Code where to fetch the plugin
|
|
255
|
+
# from the canonical distribution branch.
|
|
256
|
+
pack_name = pack.get("name", "")
|
|
257
|
+
if isinstance(pack_name, str) and pack_name:
|
|
258
|
+
m = _GITHUB_URL_RE.match(repository)
|
|
259
|
+
if m:
|
|
260
|
+
out["source"] = {
|
|
261
|
+
"source": "github",
|
|
262
|
+
"repo": m.group(1),
|
|
263
|
+
"branch": _DIST_BRANCH,
|
|
264
|
+
"directory": pack_name,
|
|
265
|
+
}
|
|
234
266
|
|
|
235
267
|
keywords = pack.get("keywords")
|
|
236
268
|
if isinstance(keywords, list):
|
|
@@ -478,8 +510,22 @@ def _run_per_pack_single(
|
|
|
478
510
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
479
511
|
# Load, splice in synthesised SessionStart hook, re-serialise.
|
|
480
512
|
derived = json.loads(plugin_manifest.read_text(encoding="utf-8"))
|
|
513
|
+
# Claude Code 2.1.209+ hook contract: each event entry is an object
|
|
514
|
+
# with a nested "hooks" array of typed hook objects, not a flat
|
|
515
|
+
# {command} object. The old flat shape ({command}) is rejected with
|
|
516
|
+
# "hooks: Invalid input" by the plugin validator.
|
|
481
517
|
derived["hooks"] = {
|
|
482
|
-
"SessionStart": [
|
|
518
|
+
"SessionStart": [
|
|
519
|
+
{
|
|
520
|
+
"hooks": [
|
|
521
|
+
{
|
|
522
|
+
"type": "command",
|
|
523
|
+
"command": _SESSION_START_COMMAND,
|
|
524
|
+
"timeout": 10,
|
|
525
|
+
}
|
|
526
|
+
]
|
|
527
|
+
}
|
|
528
|
+
]
|
|
483
529
|
}
|
|
484
530
|
# enriched-pack-manifest: merge the projectable metadata subset derived
|
|
485
531
|
# from this pack's pack.toml (emit-only-when-present, so a legacy pack
|
|
@@ -490,6 +536,12 @@ def _run_per_pack_single(
|
|
|
490
536
|
pack_toml_for_subset.read_text(encoding="utf-8")
|
|
491
537
|
)
|
|
492
538
|
derived.update(derive_projectable_subset(pack_meta))
|
|
539
|
+
# Strip marketplace-only fields from the per-pack plugin.json.
|
|
540
|
+
# "source" and "category" belong only in marketplace.json entries;
|
|
541
|
+
# the Claude plugin validator warns they are ignored in plugin.json.
|
|
542
|
+
# _run_aggregate re-adds them from pack.toml when building marketplace.json.
|
|
543
|
+
for _marketplace_key in ("source", "category"):
|
|
544
|
+
derived.pop(_marketplace_key, None)
|
|
493
545
|
# Validate the derived manifest IN MEMORY before writing to disk
|
|
494
546
|
# (Blocker-3: pre-write validation so a synthesis bug never lands
|
|
495
547
|
# a malformed plugin.json in dist/).
|
|
@@ -624,12 +676,52 @@ def _run_aggregate(recipe: Recipe, output_dir: Path) -> dict:
|
|
|
624
676
|
for plugin_dir in sorted(input_dir.iterdir()):
|
|
625
677
|
manifest = plugin_dir / ".claude-plugin" / "plugin.json"
|
|
626
678
|
if manifest.exists():
|
|
627
|
-
|
|
679
|
+
entry = json.loads(manifest.read_text(encoding="utf-8"))
|
|
680
|
+
# hooks are per-plugin installation artifacts, not marketplace
|
|
681
|
+
# metadata; strip them from marketplace entries.
|
|
682
|
+
entry.pop("hooks", None)
|
|
683
|
+
# Re-add marketplace-only fields (source, category) from the
|
|
684
|
+
# projected pack.toml (also present in the dist directory).
|
|
685
|
+
# These were stripped from plugin.json to keep it clean of
|
|
686
|
+
# fields the Claude plugin loader ignores at runtime.
|
|
687
|
+
pack_toml_path = plugin_dir / "pack.toml"
|
|
688
|
+
if pack_toml_path.exists():
|
|
689
|
+
pack_meta = tomllib.loads(
|
|
690
|
+
pack_toml_path.read_text(encoding="utf-8")
|
|
691
|
+
)
|
|
692
|
+
subset = derive_projectable_subset(pack_meta)
|
|
693
|
+
for _mk in ("source", "category"):
|
|
694
|
+
if _mk in subset:
|
|
695
|
+
entry[_mk] = subset[_mk]
|
|
696
|
+
entries.append(entry)
|
|
628
697
|
output_path = output_dir / recipe.output_file
|
|
629
698
|
_assert_under(output_path, output_dir)
|
|
630
699
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
700
|
+
|
|
701
|
+
# Derive marketplace name and owner from the first entry that carries a
|
|
702
|
+
# source.repo field (populated above from pack.toml via derive_projectable_subset).
|
|
703
|
+
marketplace_name: str | None = None
|
|
704
|
+
marketplace_owner: dict | None = None
|
|
705
|
+
for entry in entries:
|
|
706
|
+
src = entry.get("source")
|
|
707
|
+
if isinstance(src, dict):
|
|
708
|
+
repo = src.get("repo", "")
|
|
709
|
+
if isinstance(repo, str) and "/" in repo:
|
|
710
|
+
owner_part, name_part = repo.split("/", 1)
|
|
711
|
+
marketplace_name = name_part
|
|
712
|
+
marketplace_owner = {"name": owner_part}
|
|
713
|
+
break
|
|
714
|
+
|
|
715
|
+
payload: dict = {}
|
|
716
|
+
if marketplace_name:
|
|
717
|
+
payload["name"] = marketplace_name
|
|
718
|
+
payload["description"] = _MARKETPLACE_DESCRIPTION
|
|
719
|
+
if marketplace_owner:
|
|
720
|
+
payload["owner"] = marketplace_owner
|
|
721
|
+
payload["plugins"] = entries
|
|
722
|
+
|
|
631
723
|
output_path.write_text(
|
|
632
|
-
json.dumps(
|
|
724
|
+
json.dumps(payload, indent=2, sort_keys=True) + "\n",
|
|
633
725
|
encoding="utf-8", newline="\n",
|
|
634
726
|
)
|
|
635
727
|
return {"recipe": recipe.name, "type": recipe.type, "entries": len(entries)}
|
{agentbundle-0.11.1 → agentbundle-0.12.1}/agentbundle/build/projections/gemini_command_toml.py
RENAMED
|
@@ -28,8 +28,8 @@ dollar-amount in prose or a fenced shell example (``$10/month``, or a ``$1``
|
|
|
28
28
|
inside a fenced code block) trips it too. This is **fail-closed** (a loud build
|
|
29
29
|
error an author can act on, never a silent bad emit), and no shipped command
|
|
30
30
|
contains a ``$<digit>``. An injection-context-aware parser (skip fenced/inline code, require
|
|
31
|
-
an injection-shaped token) is a documented follow-on — see ``
|
|
32
|
-
|
|
31
|
+
an injection-shaped token) is a documented follow-on — see ``workspace.toml [backlog]``
|
|
32
|
+
slug ``gemini-full-parity``. Until then: keep literal ``$<digit>`` out of command
|
|
33
33
|
bodies, or the build will refuse the command.
|
|
34
34
|
|
|
35
35
|
The one-source-file → one-output-file shape and the frontmatter helpers mirror
|
|
@@ -362,14 +362,13 @@ EXCLUDED_PATTERNS: tuple[str, ...] = (
|
|
|
362
362
|
"docs/product/*.md",
|
|
363
363
|
"docs/knowledge/*.md",
|
|
364
364
|
"docs/guides/**/*.md",
|
|
365
|
+
# Seeded-once / adopter-curated files (RFC-0002 Manual semantics).
|
|
366
|
+
"workspace.toml", # RFC-0069: seeded once; adopter-curated thereafter
|
|
365
367
|
# Manual seed-projected paths (RFC-0002 amendment 2026-05-25). The
|
|
366
368
|
# `docs/<area>/*.md` patterns above cover 11 of the 19 reclassified
|
|
367
|
-
# paths; the following
|
|
369
|
+
# paths; the following 7 are not matched by any pattern and need
|
|
368
370
|
# explicit listing. See `docs/specs/self-hosting/spec.md` AC20.
|
|
369
371
|
"docs/CHARTER.md",
|
|
370
|
-
"docs/backlog.md", # per-instance work/deferral register; seed is
|
|
371
|
-
# placeholder, on-disk file is the curated instance
|
|
372
|
-
# (RFC-0016 mechanism 5). Preserved like docs/product/*.
|
|
373
372
|
"docs/knowledge/patterns.jsonl",
|
|
374
373
|
"docs/rfc/README.md",
|
|
375
374
|
"docs/adr/README.md",
|
|
@@ -557,12 +556,14 @@ def _aggregate_marketplace(
|
|
|
557
556
|
packs_dir: Path,
|
|
558
557
|
output_root: Path,
|
|
559
558
|
owner: str = "eugenelim",
|
|
559
|
+
name: str = "agent-ready-repo",
|
|
560
|
+
description: str = "Agent skills, subagents, and hooks for Claude Code and other coding agents.",
|
|
560
561
|
) -> Path:
|
|
561
562
|
"""Aggregate `packs/*/.claude-plugin/plugin.json` into
|
|
562
563
|
`output_root/.claude-plugin/marketplace.json` so this repo is itself
|
|
563
|
-
a usable marketplace at HEAD. `owner`
|
|
564
|
-
concrete
|
|
565
|
-
`.adapt-discovery.toml
|
|
564
|
+
a usable marketplace at HEAD. `owner` and `name` default to this repo's
|
|
565
|
+
concrete values but `run_self_host` overrides them from
|
|
566
|
+
`.adapt-discovery.toml` so adopters get their own."""
|
|
566
567
|
entries: list[dict] = []
|
|
567
568
|
for pack_path in sorted(packs_dir.iterdir()):
|
|
568
569
|
if not pack_path.is_dir() or not (pack_path / "pack.toml").exists():
|
|
@@ -570,10 +571,12 @@ def _aggregate_marketplace(
|
|
|
570
571
|
manifest = pack_path / ".claude-plugin" / "plugin.json"
|
|
571
572
|
if manifest.exists():
|
|
572
573
|
entry = json.loads(manifest.read_text(encoding="utf-8"))
|
|
574
|
+
# hooks are per-plugin artifacts; strip them from marketplace entries.
|
|
575
|
+
entry.pop("hooks", None)
|
|
573
576
|
# enriched-pack-manifest: surface the projectable metadata subset
|
|
574
|
-
# (author / license / links / keywords / category / displayName
|
|
575
|
-
# derived from pack.toml
|
|
576
|
-
#
|
|
577
|
+
# (author / license / links / keywords / category / displayName /
|
|
578
|
+
# source) derived from pack.toml. Emit-only-when-present keeps
|
|
579
|
+
# legacy entries byte-identical.
|
|
577
580
|
pack_meta = tomllib.loads(
|
|
578
581
|
(pack_path / "pack.toml").read_text(encoding="utf-8")
|
|
579
582
|
)
|
|
@@ -582,6 +585,8 @@ def _aggregate_marketplace(
|
|
|
582
585
|
target = output_root / ".claude-plugin" / "marketplace.json"
|
|
583
586
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
584
587
|
payload = {
|
|
588
|
+
"name": name,
|
|
589
|
+
"description": description,
|
|
585
590
|
"owner": {"name": owner},
|
|
586
591
|
"plugins": entries,
|
|
587
592
|
}
|
|
@@ -1079,6 +1084,7 @@ def run_self_host(
|
|
|
1079
1084
|
return 3
|
|
1080
1085
|
discovery_flat = dict(discovery.markers)
|
|
1081
1086
|
owner = discovery_flat.get("owner", "eugenelim")
|
|
1087
|
+
marketplace_name = discovery_flat.get("project-name", "agent-ready-repo")
|
|
1082
1088
|
|
|
1083
1089
|
if dry_run:
|
|
1084
1090
|
with tempfile.TemporaryDirectory(prefix="agentbundle-shadow-") as shadow_str:
|
|
@@ -1096,7 +1102,7 @@ def run_self_host(
|
|
|
1096
1102
|
except ValueError as exc:
|
|
1097
1103
|
print(f"self-host: {exc}", file=sys.stderr)
|
|
1098
1104
|
return 4
|
|
1099
|
-
_aggregate_marketplace(packs_dir, shadow, owner=owner)
|
|
1105
|
+
_aggregate_marketplace(packs_dir, shadow, owner=owner, name=marketplace_name)
|
|
1100
1106
|
_recreate_claude_symlink(shadow, force_copy=no_symlink)
|
|
1101
1107
|
extra_marker_paths = list(seed_map.keys()) + [
|
|
1102
1108
|
Path(".claude-plugin") / "marketplace.json",
|
|
@@ -1152,7 +1158,7 @@ def run_self_host(
|
|
|
1152
1158
|
except ValueError as exc:
|
|
1153
1159
|
print(f"self-host: {exc}", file=sys.stderr)
|
|
1154
1160
|
return 4
|
|
1155
|
-
_aggregate_marketplace(packs_dir, working_tree, owner=owner)
|
|
1161
|
+
_aggregate_marketplace(packs_dir, working_tree, owner=owner, name=marketplace_name)
|
|
1156
1162
|
_recreate_claude_symlink(working_tree, force_copy=no_symlink)
|
|
1157
1163
|
extra_marker_paths = list(seed_map.keys()) + [
|
|
1158
1164
|
Path(".claude-plugin") / "marketplace.json",
|