diff --git a/src/apm_cli/integration/hook_integrator.py b/src/apm_cli/integration/hook_integrator.py index 412a60f74..5b0d96657 100644 --- a/src/apm_cli/integration/hook_integrator.py +++ b/src/apm_cli/integration/hook_integrator.py @@ -1283,6 +1283,8 @@ def integrate_package_hooks( renamed_hooks[event_name] = entries rewritten["hooks"] = renamed_hooks + rewritten.setdefault("version", 1) + # Write rewritten JSON with open(target_path, "w", encoding="utf-8") as f: json.dump(rewritten, f, indent=2) diff --git a/tests/integration/test_hook_integrator_copilot_casing_e2e.py b/tests/integration/test_hook_integrator_copilot_casing_e2e.py index 8fcf85fba..0f995f741 100644 --- a/tests/integration/test_hook_integrator_copilot_casing_e2e.py +++ b/tests/integration/test_hook_integrator_copilot_casing_e2e.py @@ -80,7 +80,9 @@ def _read_copilot_hooks_config(project_root: Path, package_name: str) -> dict[st """Read the actual Copilot hook JSON written under .github/hooks.""" config_path = project_root / ".github" / "hooks" / f"{package_name}-hooks.json" assert config_path.exists() - return json.loads(config_path.read_text(encoding="utf-8")) + config = json.loads(config_path.read_text(encoding="utf-8")) + assert config["version"] == 1 + return config def test_copilot_install_writes_only_camel_case_hook_events(tmp_path: Path) -> None: diff --git a/tests/unit/integration/test_hook_integrator.py b/tests/unit/integration/test_hook_integrator.py index 558496358..023bdfb50 100644 --- a/tests/unit/integration/test_hook_integrator.py +++ b/tests/unit/integration/test_hook_integrator.py @@ -281,6 +281,32 @@ def test_integrate_hookify_vscode(self, temp_project): assert (scripts_dir / "stop.py").exists() assert (scripts_dir / "userpromptsubmit.py").exists() + def test_copilot_version_emitted_on_fresh_install(self, temp_project): + """Fresh Copilot hook JSON must contain top-level "version": 1.""" + pkg_info = self._setup_hookify_package(temp_project) + integrator = HookIntegrator() + + integrator.integrate_package_hooks(pkg_info, temp_project) + + hooks_path = temp_project / ".github" / "hooks" / "hookify-hooks.json" + config = json.loads(hooks_path.read_text()) + assert config["version"] == 1 + + def test_copilot_existing_source_version_preserved(self, temp_project): + """An explicit source version must not be overwritten.""" + pkg_info = self._setup_hookify_package(temp_project) + source_path = pkg_info.install_path / "hooks" / "hooks.json" + source_config = json.loads(source_path.read_text()) + source_config["version"] = 3 + source_path.write_text(json.dumps(source_config)) + + integrator = HookIntegrator() + integrator.integrate_package_hooks(pkg_info, temp_project) + + hooks_path = temp_project / ".github" / "hooks" / "hookify-hooks.json" + config = json.loads(hooks_path.read_text()) + assert config["version"] == 3 + def test_integrate_learning_output_style_vscode(self, temp_project): """Test VSCode integration of learning-output-style plugin (different script dir).""" pkg_dir = temp_project / "apm_modules" / "anthropics" / "learning-output-style"