agentbundle 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. agentbundle/__init__.py +14 -0
  2. agentbundle/__main__.py +5 -0
  3. agentbundle/_data/adapter.schema.json +270 -0
  4. agentbundle/_data/adapter.toml +584 -0
  5. agentbundle/_data/install-marker.py +1099 -0
  6. agentbundle/_data/pack.schema.json +152 -0
  7. agentbundle/_data/plugin-manifest.derived.schema.json +33 -0
  8. agentbundle/_data/plugin-manifest.schema.json +18 -0
  9. agentbundle/build/__init__.py +206 -0
  10. agentbundle/build/__main__.py +8 -0
  11. agentbundle/build/adapter_root_bins.py +336 -0
  12. agentbundle/build/adapters/__init__.py +46 -0
  13. agentbundle/build/adapters/claude_code.py +142 -0
  14. agentbundle/build/adapters/codex.py +227 -0
  15. agentbundle/build/adapters/copilot.py +149 -0
  16. agentbundle/build/adapters/kiro.py +608 -0
  17. agentbundle/build/adapters/kiro_cli.py +53 -0
  18. agentbundle/build/adapters/kiro_ide.py +275 -0
  19. agentbundle/build/contract.py +20 -0
  20. agentbundle/build/lint_packs.py +555 -0
  21. agentbundle/build/main.py +596 -0
  22. agentbundle/build/phase_order.py +40 -0
  23. agentbundle/build/projections/__init__.py +13 -0
  24. agentbundle/build/projections/codex_agent_toml.py +232 -0
  25. agentbundle/build/projections/copilot_agent_md.py +206 -0
  26. agentbundle/build/projections/copilot_hooks_json.py +142 -0
  27. agentbundle/build/projections/direct_directory.py +41 -0
  28. agentbundle/build/projections/hook_id.py +27 -0
  29. agentbundle/build/projections/kiro_ide_hook.py +256 -0
  30. agentbundle/build/projections/merge_into_agent_json.py +264 -0
  31. agentbundle/build/projections/merge_json.py +58 -0
  32. agentbundle/build/projections/user_merge_json.py +324 -0
  33. agentbundle/build/scope_rails.py +728 -0
  34. agentbundle/build/self_host.py +1486 -0
  35. agentbundle/build/shared_libs.py +309 -0
  36. agentbundle/build/target_resolver.py +85 -0
  37. agentbundle/build/tests/__init__.py +0 -0
  38. agentbundle/build/tests/test_adapter_claude_code.py +275 -0
  39. agentbundle/build/tests/test_adapter_codex.py +699 -0
  40. agentbundle/build/tests/test_adapter_copilot.py +91 -0
  41. agentbundle/build/tests/test_adapter_kiro.py +449 -0
  42. agentbundle/build/tests/test_adapter_kiro_alias.py +105 -0
  43. agentbundle/build/tests/test_adapter_kiro_cli.py +102 -0
  44. agentbundle/build/tests/test_adapter_kiro_ide.py +173 -0
  45. agentbundle/build/tests/test_adapter_root_bins_projection.py +429 -0
  46. agentbundle/build/tests/test_build_ships_seeds.py +78 -0
  47. agentbundle/build/tests/test_contract.py +582 -0
  48. agentbundle/build/tests/test_contract_scope.py +224 -0
  49. agentbundle/build/tests/test_contract_v07.py +191 -0
  50. agentbundle/build/tests/test_contract_v08.py +230 -0
  51. agentbundle/build/tests/test_direct_directory_cleanup.py +65 -0
  52. agentbundle/build/tests/test_end_to_end_build.py +227 -0
  53. agentbundle/build/tests/test_lint_agents_md_legacy_block.py +135 -0
  54. agentbundle/build/tests/test_lint_agents_md_risk_block.py +116 -0
  55. agentbundle/build/tests/test_lint_packs.py +703 -0
  56. agentbundle/build/tests/test_load_pack_hook_wiring_safely.py +176 -0
  57. agentbundle/build/tests/test_pack_schema.py +265 -0
  58. agentbundle/build/tests/test_pack_schema_allowed_adapters.py +258 -0
  59. agentbundle/build/tests/test_pack_schema_install.py +305 -0
  60. agentbundle/build/tests/test_pipeline.py +272 -0
  61. agentbundle/build/tests/test_plugin_manifest_schema.py +327 -0
  62. agentbundle/build/tests/test_projections_merge_json.py +148 -0
  63. agentbundle/build/tests/test_scope_rails.py +398 -0
  64. agentbundle/build/tests/test_security.py +97 -0
  65. agentbundle/build/tests/test_self_host_check.py +2100 -0
  66. agentbundle/build/tests/test_shared_libs_projection.py +415 -0
  67. agentbundle/build/tests/test_shipped_packs_v07_declarations.py +100 -0
  68. agentbundle/build/tests/test_shipped_packs_v08_declarations.py +80 -0
  69. agentbundle/build/tests/test_validate.py +250 -0
  70. agentbundle/build/validate.py +141 -0
  71. agentbundle/catalogue.py +164 -0
  72. agentbundle/cli.py +486 -0
  73. agentbundle/commands/__init__.py +5 -0
  74. agentbundle/commands/_common.py +174 -0
  75. agentbundle/commands/_drop_warning.py +329 -0
  76. agentbundle/commands/adapt.py +343 -0
  77. agentbundle/commands/config.py +125 -0
  78. agentbundle/commands/diff.py +211 -0
  79. agentbundle/commands/init_state.py +279 -0
  80. agentbundle/commands/install.py +3026 -0
  81. agentbundle/commands/list_packs.py +170 -0
  82. agentbundle/commands/list_targets.py +23 -0
  83. agentbundle/commands/reconcile.py +161 -0
  84. agentbundle/commands/render.py +165 -0
  85. agentbundle/commands/scaffold.py +69 -0
  86. agentbundle/commands/uninstall.py +294 -0
  87. agentbundle/commands/upgrade.py +699 -0
  88. agentbundle/commands/validate.py +688 -0
  89. agentbundle/config.py +747 -0
  90. agentbundle/render.py +123 -0
  91. agentbundle/safety.py +633 -0
  92. agentbundle/scope.py +319 -0
  93. agentbundle/user_config.py +284 -0
  94. agentbundle/version.py +49 -0
  95. agentbundle-0.2.0.dist-info/METADATA +37 -0
  96. agentbundle-0.2.0.dist-info/RECORD +99 -0
  97. agentbundle-0.2.0.dist-info/WHEEL +5 -0
  98. agentbundle-0.2.0.dist-info/entry_points.txt +2 -0
  99. agentbundle-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,102 @@
1
+ """Tests for the kiro-cli adapter (T3 — RFC-0022).
2
+
3
+ kiro-cli targets the `kiro` terminal binary. It projects agents as
4
+ `.json` with CLI short-name tool tokens (read, grep, glob, write,
5
+ shell, web_fetch, web_search) and retains hook-wiring via
6
+ merge-into-agent-json. kiro-ide-hook is dropped.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import tempfile
13
+ import unittest
14
+ from pathlib import Path
15
+
16
+ from agentbundle.build.adapters.kiro_cli import project
17
+ from agentbundle.build.contract import load as load_contract
18
+
19
+ REPO_ROOT = Path(__file__).resolve().parents[5]
20
+ CONTRACT_PATH = REPO_ROOT / "docs" / "contracts" / "adapter.toml"
21
+
22
+
23
+ def _seed_agent_pack(root: Path, tools: str = "Read, Grep, Glob, Bash") -> Path:
24
+ pack = root / "pack"
25
+ (pack / ".apm" / "agents").mkdir(parents=True)
26
+ (pack / ".apm" / "agents" / "bar.md").write_text(
27
+ f"---\nname: bar\ntools: {tools}\n---\nagent body\n",
28
+ encoding="utf-8",
29
+ )
30
+ return pack
31
+
32
+
33
+ class KiroCliAdapterTests(unittest.TestCase):
34
+ @classmethod
35
+ def setUpClass(cls) -> None:
36
+ cls.contract = load_contract(CONTRACT_PATH)
37
+
38
+ def test_cli_agent_is_json(self) -> None:
39
+ """kiro-cli projects agents as .json, not .md."""
40
+ with tempfile.TemporaryDirectory() as tmp:
41
+ tmp_path = Path(tmp)
42
+ pack = _seed_agent_pack(tmp_path)
43
+ out = tmp_path / "out"
44
+ project(pack, self.contract, out)
45
+ self.assertTrue(
46
+ (out / ".kiro" / "agents" / "bar.json").exists(),
47
+ "kiro-cli must project agent as .json",
48
+ )
49
+ self.assertFalse(
50
+ (out / ".kiro" / "agents" / "bar.md").exists(),
51
+ "kiro-cli must not project agent as .md",
52
+ )
53
+
54
+ def test_cli_tool_short_names(self) -> None:
55
+ """kiro-cli uses CLI short-name tool tokens per the
56
+ kiro-cli-agent-frontmatter-v1.0 mapping table:
57
+ Read→read, Grep→grep, Glob→glob, Bash→shell,
58
+ WebFetch→web_fetch, WebSearch→web_search."""
59
+ with tempfile.TemporaryDirectory() as tmp:
60
+ tmp_path = Path(tmp)
61
+ pack = _seed_agent_pack(
62
+ tmp_path,
63
+ tools="Read, Grep, Glob, Bash, WebFetch, WebSearch",
64
+ )
65
+ out = tmp_path / "out"
66
+ project(pack, self.contract, out)
67
+ data = json.loads(
68
+ (out / ".kiro" / "agents" / "bar.json").read_text(encoding="utf-8")
69
+ )
70
+ tools = data.get("tools", [])
71
+ self.assertIn("read", tools)
72
+ self.assertIn("grep", tools)
73
+ self.assertIn("glob", tools)
74
+ self.assertIn("shell", tools)
75
+ self.assertIn("web_fetch", tools)
76
+ self.assertIn("web_search", tools)
77
+ # Verify these are short-names, not the IDE ids
78
+ self.assertNotIn("read_file", tools)
79
+ self.assertNotIn("grep_search", tools)
80
+ self.assertNotIn("execute_bash", tools)
81
+
82
+ def test_cli_no_ide_hook_field(self) -> None:
83
+ """kiro-cli projected agent JSON must not contain ide-event-vocabulary
84
+ or kiro-ide-hook sections — those are IDE-only fields that cause the
85
+ IDE loader to silently drop agents."""
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
+ raw = (out / ".kiro" / "agents" / "bar.json").read_text(encoding="utf-8")
92
+ self.assertNotIn("ide-event-vocabulary", raw)
93
+ self.assertNotIn("kiro-ide-hook", raw)
94
+ data = json.loads(raw)
95
+ self.assertNotIn("hooks", data, "kiro-cli agent JSON must not carry hooks key")
96
+ self.assertNotIn("allowedTools", data)
97
+ self.assertNotIn("toolsSettings", data)
98
+ self.assertNotIn("mcpServers", data)
99
+
100
+
101
+ if __name__ == "__main__":
102
+ unittest.main()
@@ -0,0 +1,173 @@
1
+ """Tests for the kiro-ide adapter (T1 — RFC-0022).
2
+
3
+ kiro-ide targets the Kiro VS Code-fork IDE. Agents project as .md with YAML
4
+ frontmatter (read by gray-matter), using IDE tool ids. kiro-ide-hook is
5
+ activated. No CLI-only keys in agent output.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import tempfile
12
+ import unittest
13
+ from pathlib import Path
14
+
15
+ from agentbundle.build.adapters.kiro_ide import project
16
+ from agentbundle.build.contract import load as load_contract
17
+
18
+ REPO_ROOT = Path(__file__).resolve().parents[5]
19
+ CONTRACT_PATH = REPO_ROOT / "docs" / "contracts" / "adapter.toml"
20
+
21
+
22
+ def _seed_agent_pack(root: Path, tools: str = "Read, Grep", model: str | None = None) -> Path:
23
+ pack = root / "pack"
24
+ (pack / ".apm" / "agents").mkdir(parents=True)
25
+ model_line = f"\nmodel: {model}" if model else ""
26
+ (pack / ".apm" / "agents" / "bar.md").write_text(
27
+ f"---\nname: bar\ntools: {tools}{model_line}\n---\nagent body\n",
28
+ encoding="utf-8",
29
+ )
30
+ return pack
31
+
32
+
33
+ class KiroIdeAdapterTests(unittest.TestCase):
34
+ @classmethod
35
+ def setUpClass(cls) -> None:
36
+ cls.contract = load_contract(CONTRACT_PATH)
37
+
38
+ def test_kiro_ide_agent_is_md(self) -> None:
39
+ """kiro-ide projects agents as .md, not .json."""
40
+ with tempfile.TemporaryDirectory() as tmp:
41
+ tmp_path = Path(tmp)
42
+ pack = _seed_agent_pack(tmp_path)
43
+ out = tmp_path / "out"
44
+ project(pack, self.contract, out)
45
+ self.assertTrue(
46
+ (out / ".kiro" / "agents" / "bar.md").exists(),
47
+ "kiro-ide must project agent as .md",
48
+ )
49
+ self.assertFalse(
50
+ (out / ".kiro" / "agents" / "bar.json").exists(),
51
+ "kiro-ide must not project agent as .json",
52
+ )
53
+
54
+ def test_kiro_ide_no_cli_only_keys(self) -> None:
55
+ """kiro-ide .md frontmatter must not carry CLI-only keys that would
56
+ cause the IDE loader to silently drop the agent."""
57
+ with tempfile.TemporaryDirectory() as tmp:
58
+ tmp_path = Path(tmp)
59
+ pack = _seed_agent_pack(tmp_path)
60
+ out = tmp_path / "out"
61
+ project(pack, self.contract, out)
62
+ raw = (out / ".kiro" / "agents" / "bar.md").read_text(encoding="utf-8")
63
+ for cli_only in ("hooks", "allowedTools", "toolsSettings", "mcpServers"):
64
+ self.assertNotIn(cli_only, raw, f"kiro-ide agent .md must not contain {cli_only!r}")
65
+
66
+ def test_kiro_ide_tools_use_ide_ids(self) -> None:
67
+ """kiro-ide uses IDE tool ids (read_file, grep_search) not CLI short-names."""
68
+ with tempfile.TemporaryDirectory() as tmp:
69
+ tmp_path = Path(tmp)
70
+ pack = _seed_agent_pack(tmp_path, tools="Read, Grep, Glob, Bash")
71
+ out = tmp_path / "out"
72
+ project(pack, self.contract, out)
73
+ raw = (out / ".kiro" / "agents" / "bar.md").read_text(encoding="utf-8")
74
+ self.assertIn("read_file", raw)
75
+ self.assertIn("grep_search", raw)
76
+ self.assertIn("file_search", raw)
77
+ self.assertIn("execute_bash", raw)
78
+ # Not CLI short-names
79
+ self.assertNotIn("shell", raw.split("---")[1]) # only check frontmatter
80
+
81
+ def test_kiro_ide_md_has_frontmatter_and_body(self) -> None:
82
+ """Output .md file has --- fenced frontmatter and the original body."""
83
+ with tempfile.TemporaryDirectory() as tmp:
84
+ tmp_path = Path(tmp)
85
+ pack = _seed_agent_pack(tmp_path)
86
+ out = tmp_path / "out"
87
+ project(pack, self.contract, out)
88
+ text = (out / ".kiro" / "agents" / "bar.md").read_text(encoding="utf-8")
89
+ self.assertTrue(text.startswith("---\n"), "must start with --- frontmatter fence")
90
+ self.assertIn("\n---\n", text, "must have closing --- fence")
91
+ self.assertIn("agent body", text, "original body must be preserved")
92
+
93
+ def test_kiro_ide_model_translates_to_kiro_id(self) -> None:
94
+ """model: opus translates to claude-opus-4.6 (same mapping as kiro-cli)."""
95
+ with tempfile.TemporaryDirectory() as tmp:
96
+ tmp_path = Path(tmp)
97
+ pack = _seed_agent_pack(tmp_path, model="opus")
98
+ out = tmp_path / "out"
99
+ project(pack, self.contract, out)
100
+ text = (out / ".kiro" / "agents" / "bar.md").read_text(encoding="utf-8")
101
+ self.assertIn("claude-opus-4.6", text)
102
+ self.assertNotIn(": opus", text)
103
+
104
+ def test_frontmatter_table_renamed(self) -> None:
105
+ """The mapping table is kiro-ide-agent-frontmatter-v0.9 (renamed from
106
+ kiro-agent-frontmatter-v0.9 in T1). Old name must not exist."""
107
+ mapping = self.contract.get("frontmatter-mapping", {})
108
+ self.assertIn(
109
+ "kiro-ide-agent-frontmatter-v0.9",
110
+ mapping,
111
+ "kiro-ide-agent-frontmatter-v0.9 must be in contract",
112
+ )
113
+ self.assertNotIn(
114
+ "kiro-agent-frontmatter-v0.9",
115
+ mapping,
116
+ "old name kiro-agent-frontmatter-v0.9 must not exist after T1 rename",
117
+ )
118
+
119
+ def test_kiro_ide_hook_declared_in_contract(self) -> None:
120
+ """[adapter.kiro-ide.projections.kiro-ide-hook] is declared in contract."""
121
+ kiro_ide_block = self.contract["adapter"]["kiro-ide"]
122
+ projections = kiro_ide_block.get("projections", {})
123
+ self.assertIn(
124
+ "kiro-ide-hook",
125
+ projections,
126
+ "kiro-ide adapter must declare kiro-ide-hook in projections",
127
+ )
128
+ rule = projections["kiro-ide-hook"]
129
+ self.assertEqual(rule.get("mode"), "direct-file")
130
+ target = rule.get("target", {})
131
+ target_repo = target.get("repo") if isinstance(target, dict) else target
132
+ self.assertIsNotNone(target_repo)
133
+ self.assertIn("<pack>--<name>", target_repo, "flat-with-prefix path must use -- separator")
134
+ self.assertIn(".kiro.hook", target_repo)
135
+
136
+ def test_contract_version_is_0_9(self) -> None:
137
+ """Contract version is 0.10 (RFC-0024 / copilot-full-parity, atop the
138
+ 0.9 kiro-adapter-split bump). Name preserved to keep the diff small."""
139
+ self.assertEqual(
140
+ self.contract["contract"]["version"],
141
+ "0.10",
142
+ "adapter.toml [contract] version must be '0.10' after copilot-full-parity",
143
+ )
144
+
145
+ def test_kiro_ide_hook_projects_with_flat_prefix_path(self) -> None:
146
+ """kiro-ide-hook files project to .kiro/hooks/<pack>--<name>.kiro.hook
147
+ (flat-with-prefix, confirmed by Q6 probe no×yes 2026-06-01)."""
148
+ with tempfile.TemporaryDirectory() as tmp:
149
+ tmp_path = Path(tmp)
150
+ pack = tmp_path / "my-pack"
151
+ hooks_dir = pack / ".apm" / "kiro-ide-hooks"
152
+ hooks_dir.mkdir(parents=True)
153
+ hook_body = {
154
+ "name": "on-save",
155
+ "version": "1",
156
+ "when": {"type": "fileEdited", "patterns": ["**/*.py"]},
157
+ "then": {"type": "askAgent", "prompt": "Run lint."},
158
+ }
159
+ (hooks_dir / "on-save.kiro.hook").write_text(
160
+ json.dumps(hook_body), encoding="utf-8"
161
+ )
162
+ out = tmp_path / "out"
163
+ project(pack, self.contract, out)
164
+ # Flat path: .kiro/hooks/my-pack--on-save.kiro.hook
165
+ expected = out / ".kiro" / "hooks" / "my-pack--on-save.kiro.hook"
166
+ self.assertTrue(expected.exists(), f"expected hook at flat path {expected}")
167
+ # No subdirectory
168
+ subdir = out / ".kiro" / "hooks" / "my-pack"
169
+ self.assertFalse(subdir.exists(), "must NOT create a subdirectory for the pack")
170
+
171
+
172
+ if __name__ == "__main__":
173
+ unittest.main()