dotagents-cli 0.9.2__tar.gz → 0.9.3__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.
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/CHANGELOG.md +23 -1
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/PKG-INFO +1 -1
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/guide/commands.md +1 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/guide/overlay-hooks.md +32 -10
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/guide/private-sync.md +0 -2
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/AGENTS.md +11 -4
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/__init__.py +1 -1
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_overlay_hooks.py +87 -20
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/cli/mcp.py +11 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/cli/overlays.py +41 -9
- dotagents_cli-0.9.3/tests/test_install_allows_hooks.py +238 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_overlay_hooks_cli.py +0 -15
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tools/cloud-setup.sh +9 -13
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/.gitignore +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/LICENSE +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/README.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/agents.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/cli.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/commands.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/context.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/env.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/fs.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/hooks.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/merge.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/overlays.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/reference.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/resources.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/scope.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/skills.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/sources.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/api/wrappers.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/changelog.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/guide/authoring.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/guide/install.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/guide/overlays.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/docs/index.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/install.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/mkdocs.yml +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/pyproject.toml +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/__main__.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_activate.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_agents.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_aos.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_context.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_deps.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_env.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_fs.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_hook_dispatch.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_hook_main.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_hooks.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_launchers.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_mcp_gateway.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_mcp_gateway_clients.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_mcp_gateway_config.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_merge.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_overlay/dotagents/cmds/findings.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_overlay/dotagents/cmds/launch.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_overlay/dotagents/hooks/overlay_hook.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_overlay/dotagents/hooks/preinvocation_antigravity_context.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_overlay/dotagents/hooks/pretooluse_codex_env.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_overlay/dotagents/hooks/sessionstart_codex_context.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_overlay/dotagents/templates/AGENTS.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_overlay/dotagents/templates/PROJECT.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_overlays.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_pipwheel.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_policy.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_pyvenv.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_resources.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_run.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_scope.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_skills.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_sources.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/_wrappers.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/cli/__init__.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/cli/_common.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/cli/about.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/cli/build_pyz.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/cli/context.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/cli/env.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/cli/hook.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/cli/init.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/cli/path.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/src/dotagents/py.typed +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/_helpers.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/_hook_helpers.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/_shell.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/aos_vectors/README.md +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/aos_vectors/REVISION +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/aos_vectors/compose.json +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/aos_vectors/digest.json +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/aos_vectors/effective.json +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/aos_vectors/hooks.json +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/aos_vectors/hooks_compose.json +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/aos_vectors/hooks_validation.json +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/aos_vectors/names.json +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/aos_vectors/reading.json +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/aos_vectors/source.json +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/aos_vectors/validation.json +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/conftest.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_about.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_agents.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_aos_consumer.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_aos_hook_vectors.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_aos_vectors.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_api_header.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_audit_leak.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_build_pyz.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_cli_main.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_cloud_setup.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_cmds_discovery.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_context.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_deps.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_env.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_env_cli_scope.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_env_format.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_env_into.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_env_regressions.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_env_safety.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_findings_cmd.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_fix_adapters.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_fix_cli.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_fix_env.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_fix_hooks.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_fix_init.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_fix_overlays.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_fix_packaging.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_fix_scope_context.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_fix_skills_adapters.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_git_ssl_revoke.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_hooks.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_init_base.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_last_mile.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_launch_cmd.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_launchers.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_local_additions.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_mcp.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_no_internal_labels.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_overlay_hook_forwarder.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_overlay_hook_wiring.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_overlay_hooks.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_overlay_hooks_probes.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_overlay_hooks_security.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_overlay_hooks_snapshot.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_overlay_libs.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_overlays_cli.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_overlays_command.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_path_timeout_frontmatter.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_pipwheel.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_powershell_env_hook.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_pre_env_hook.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_pyvenv_core.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_quoting_injection_safety.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_run_hook.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_scope.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_sources.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_sources_uri.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_wire_hooks.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tests/test_wrappers.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tools/audit.py +0 -0
- {dotagents_cli-0.9.2 → dotagents_cli-0.9.3}/tools/pyz_smoke.sh +0 -0
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.3] - 2026-11-11
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Installing an overlay allows its hooks: `overlays add` and `overlays sync` allow the overlay's hooks
|
|
15
|
+
at the digest they install, for that store, and log which are on (event and script). Before, hooks
|
|
16
|
+
stayed off until `overlays allow-hooks` at a terminal. `--no-hooks` on both installs without
|
|
17
|
+
allowing, and a sync then leaves them off. A sync that changes the digest allows the new one, so
|
|
18
|
+
an overlay installed by 0.9.2 with its hooks off is allowed by its next `overlays sync`. A
|
|
19
|
+
`revoke-hooks` is remembered: syncs keep the hooks off until `allow-hooks` or a fresh `add`. An
|
|
20
|
+
install through the default `dotagents mcp` server allows like the command line. `allow-hooks` stays
|
|
21
|
+
(not an MCP tool, asks at the terminal) for overlays installed with `--no-hooks` or revoked.
|
|
22
|
+
`tools/cloud-setup.sh` no longer has a separate allow step.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `dotagents mcp --strict`, and the user-store setting `overlays/.dotagents-state/mcp.json`
|
|
27
|
+
`{"strict": true}` (read only from the user store, for a server started by `DOTAGENTS_MCP`): an
|
|
28
|
+
`overlays add` or `sync` through a strict MCP server installs without allowing hooks, says they are
|
|
29
|
+
off and that `allow-hooks` turns them on, and writes no allowance.
|
|
30
|
+
|
|
10
31
|
## [0.9.2] - 2026-11-11
|
|
11
32
|
|
|
12
33
|
### Added
|
|
@@ -1793,7 +1814,8 @@ Patch release: the PATH/POSIX-conversion fix above (the only change since 0.3.0)
|
|
|
1793
1814
|
affected module sources (`dotagents.cli`, `duho.presets`) to extracted temp files
|
|
1794
1815
|
before dispatch; a no-op for a plain install.
|
|
1795
1816
|
|
|
1796
|
-
[Unreleased]: https://github.com/jose-pr/dotagents/compare/v0.9.
|
|
1817
|
+
[Unreleased]: https://github.com/jose-pr/dotagents/compare/v0.9.3...HEAD
|
|
1818
|
+
[0.9.3]: https://github.com/jose-pr/dotagents/compare/v0.9.2...v0.9.3
|
|
1797
1819
|
[0.9.2]: https://github.com/jose-pr/dotagents/compare/v0.9.1...v0.9.2
|
|
1798
1820
|
[0.9.1]: https://github.com/jose-pr/dotagents/compare/v0.9.0...v0.9.1
|
|
1799
1821
|
[0.9.0]: https://github.com/jose-pr/dotagents/compare/v0.8.3...v0.9.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: dotagents-cli
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.3
|
|
4
4
|
Summary: The dotagents CLI: install and manage a portable ~/.agents config for AI coding agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/jose-pr/dotagents/
|
|
6
6
|
Project-URL: Repository, https://github.com/jose-pr/dotagents
|
|
@@ -217,6 +217,7 @@ dotagents overlays list # installed (discovered) + available
|
|
|
217
217
|
dotagents overlays sync 'py*' # refresh installed overlays matching a glob
|
|
218
218
|
dotagents overlays remove python # delete the overlay dir, unpublish its skills, un-merge its rules
|
|
219
219
|
dotagents overlays show python # describe one: declarations, digest, pin, weakenings, source (--json)
|
|
220
|
+
dotagents mcp --strict # serve MCP tools; an add/sync through it allows no hooks
|
|
220
221
|
dotagents overlays hooks private-sync # its hooks: what runs where, and whether they are allowed
|
|
221
222
|
dotagents overlays allow-hooks private-sync --digest sha256:... # allow them at that digest (see overlay-hooks)
|
|
222
223
|
```
|
|
@@ -27,25 +27,47 @@ event takes, and answers the harness itself, in the harness's words. A `decision
|
|
|
27
27
|
fails (`grant`) and nothing of it applies. dotagents never states an approval and never rewrites
|
|
28
28
|
a tool's input.
|
|
29
29
|
|
|
30
|
-
##
|
|
30
|
+
## Allowed by installing
|
|
31
31
|
|
|
32
32
|
A hook is the overlay's code, running at every session with your access, outside the harness's
|
|
33
|
-
sandbox.
|
|
33
|
+
sandbox. Installing an overlay is choosing to trust it, hooks included: `overlays add` and
|
|
34
|
+
`overlays sync` allow the overlay's hooks at the digest they install and log which are on (event and
|
|
35
|
+
script). A sync that changes the digest allows the new one, since you chose the source.
|
|
34
36
|
|
|
35
37
|
```
|
|
38
|
+
dotagents overlays add NAME [--no-hooks] # installs, and allows the hooks unless --no-hooks
|
|
39
|
+
dotagents overlays sync [NAME] [--no-hooks]
|
|
36
40
|
dotagents overlays hooks NAME # what runs, where, and whether it is allowed
|
|
37
|
-
dotagents overlays
|
|
41
|
+
dotagents overlays revoke-hooks NAME # off, and a sync keeps them off
|
|
42
|
+
dotagents overlays allow-hooks NAME # shows the same and the digest, then asks at the terminal
|
|
38
43
|
dotagents overlays allow-hooks NAME --digest sha256:...
|
|
39
|
-
dotagents overlays revoke-hooks NAME
|
|
40
44
|
```
|
|
41
45
|
|
|
46
|
+
`--no-hooks` installs without allowing them, and a later `sync` leaves them off (it is remembered
|
|
47
|
+
like a revoke). `revoke-hooks` turns them off and a `sync` keeps them off until `allow-hooks` or a
|
|
48
|
+
fresh `overlays add`. An overlay installed by 0.9.2, whose hooks were off, is allowed by its next
|
|
49
|
+
`overlays sync`, unless it was revoked.
|
|
50
|
+
|
|
51
|
+
Hooks run **outside the agent's sandbox**, with your access. An agent that can write the project's
|
|
52
|
+
`.claude/` (its own `settings.local.json`) or your user store can run code outside its sandbox
|
|
53
|
+
anyway, so a hook is nothing it could not already add: installing an overlay is the trust decision,
|
|
54
|
+
and `dotagents overlays add` and `sync` make it once. The default `dotagents mcp` server behaves as
|
|
55
|
+
the command line does, so an add or sync an agent asks for through it allows hooks too. Start it
|
|
56
|
+
**`dotagents mcp --strict`** (or put `{"strict": true}` in `overlays/.dotagents-state/mcp.json` of
|
|
57
|
+
your user store, which a server started by `DOTAGENTS_MCP` reads) for a stricter server: an add or sync
|
|
58
|
+
through it installs without allowing hooks, says the hooks are off and that `allow-hooks` at a terminal
|
|
59
|
+
turns them on, and records nothing (what you allowed before stays allowed at its digest). The setting
|
|
60
|
+
is read from the user store only; a project's files cannot make a server strict or lax.
|
|
61
|
+
|
|
42
62
|
The allowance is kept in your user store (`overlays/.dotagents-state/hooks-allowed.json`), never
|
|
43
|
-
in an overlay or a project, per overlay and store. There is one digest per overlay:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
in an overlay or a project, per overlay and store. There is one digest per overlay: a change to the
|
|
64
|
+
overlay that did not come through `overlays sync` or `add` (a hand edit, an update by other means)
|
|
65
|
+
turns its hooks off until you allow the new digest, and going back to an earlier digest needs a new
|
|
66
|
+
yes. `allow-hooks` asks at the terminal itself (`/dev/tty`, `CONIN$`), refuses to run inside an
|
|
67
|
+
agent harness's session and is not an MCP tool; `--no-prompt` is for your own setup script. The
|
|
68
|
+
allowance is only as strong as the agent's lack of write access to your user store: an agent that
|
|
69
|
+
can write there can write an allowance, and one that can run `dotagents overlays add` through a shell
|
|
70
|
+
tool installs and allows as you would.
|
|
49
71
|
|
|
50
72
|
An overlay with a hook that has `on_failure = "refuse"` (a guard) is **not activated** while its
|
|
51
73
|
hooks are off, and its guards **fail** meanwhile: a prompt is blocked, a tool call denied, as if
|
|
@@ -85,8 +85,6 @@ them:
|
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
87
|
dotagents overlays hooks private-sync # what runs, on which harness, and whether it is allowed
|
|
88
|
-
dotagents overlays allow-hooks private-sync # shows the same and the digest to allow
|
|
89
|
-
dotagents overlays allow-hooks private-sync --digest sha256:<the digest shown>
|
|
90
88
|
dotagents init -g
|
|
91
89
|
```
|
|
92
90
|
|
|
@@ -53,6 +53,7 @@ and an error saying a command is required, and exits 2.
|
|
|
53
53
|
| `overlays list [-g] [--repo SPEC]... [--json]` | Installed overlays per store (shadowed copies marked, unmet `requires` and refused overlays flagged) and what the repos offer. |
|
|
54
54
|
| `overlays show NAME [-g] [--repo SPEC]... [--json]` | One overlay: the copy a session would use, else the source's; its declarations, a refused manifest's problems, tree digest, pin, what this machine lacks for it, its weakenings, skills, member count, recorded source and commit. No name: exit 2. |
|
|
55
55
|
| `overlays hooks [NAME] [-g]` | An installed overlay's hooks (every overlay with some, without a name): per event the script and its settings, whether the hooks are allowed at the overlay's current digest, and per harness whether the event fires natively, is fired by dotagents itself (`pre-env`) or cannot fire. |
|
|
56
|
+
| `overlays add` / `overlays sync` `[--no-hooks]` | Besides installing, allow the overlay's hooks at the digest installed and log which are on (event, script); `--no-hooks` installs without allowing and remembers it. A sync that changes the digest allows the new one; hooks `revoke-hooks` turned off stay off across syncs until `allow-hooks` or a fresh `add`; an install made through the MCP tool allows nothing and says so. |
|
|
56
57
|
| `overlays allow-hooks NAME [--digest D] [--no-prompt] [-g]` | Show the same, then allow the overlay's hooks at digest `D`, which must be the current one: without `--digest` it prints the digest to pass and allows nothing (exit 0); a wrong digest exits 2. Then it asks at the terminal itself (`/dev/tty`, `CONIN$`): `allow NAME` to confirm; `--no-prompt` is for the user's own setup script. It refuses (exit 2) inside an agent harness's session and is not an MCP tool. The allowance is kept in the user store (`overlays/.dotagents-state/hooks-allowed.json`), one digest per overlay and store, and is only as strong as an agent's lack of write access to the user store. |
|
|
57
58
|
| `overlays revoke-hooks NAME [-g]` | Take back the allowance. |
|
|
58
59
|
| `hook EVENT --harness claude-code\|codex [--level user\|project] [--guard]` | Not for typing: what a harness's own hook entry runs (through `overlay_hook.py`). Reads the harness's payload on standard input, fires the overlays' hooks for the event (`user`: the user and system stores' overlays, `project`: the project store's), and answers in the harness's words; a hook's output never reaches the harness and the answer is never an approval. `--guard`: a failure of this program answers with a refusal where the event can be refused. Always exits 0 (2 for a bad argument). Not an MCP tool. |
|
|
@@ -63,7 +64,7 @@ and an error saying a command is required, and exits 2.
|
|
|
63
64
|
| `findings add\|list\|show\|done\|reopen\|remove\|index\|path [-g] [--dir D]` | The findings queue at `<store>/findings/` (`--dir` overrides): one markdown file per finding, `done` moves it to `processed/` with a required resolution, `INDEX.md` regenerated on every change. The frontmatter is valid YAML (a value is double-quoted when it must be); `index --requote` fixes values written bare in existing files, touching nothing else. |
|
|
64
65
|
| `about [--json]` | `dotagents-cli <version>`, then `<distribution> <version>` per bundled (`.pyz`) or installed package. |
|
|
65
66
|
| `build-pyz [--out P] [--python SHEBANG] [--duho-version V] [--pathapi-version V] [--extras a,b] [--with-pip]` | Build a zipapp from a source checkout; errors elsewhere. `--with-pip` vendors the pinned pip wheel (`_pipwheel`) as `dotagents/_vendor/`. |
|
|
66
|
-
| `mcp [--prefix TEMPLATE]` | Serve command and active overlay MCP tools over stdio; `{name}` in the template is the skill or server name. |
|
|
67
|
+
| `mcp [--prefix TEMPLATE] [--strict]` | Serve command and active overlay MCP tools over stdio (`--strict`, or the user store's `mcp.json` `{"strict": true}`: an `overlays add`/`sync` through it installs without allowing hooks); `{name}` in the template is the skill or server name. |
|
|
67
68
|
|
|
68
69
|
**Every command but `env` and `mcp` is also an MCP tool.** `dotagents mcp` (or
|
|
69
70
|
`DOTAGENTS_MCP=stdio dotagents`, the same server) serves the command set — built-in,
|
|
@@ -419,7 +420,7 @@ returns the exit code.
|
|
|
419
420
|
`bundle_manifest() -> Optional[dict]`, `installed_packages() -> dict[str, str]`,
|
|
420
421
|
`packages() -> tuple[dict[str, str], Optional[dict]]` (the bundle's packages, else
|
|
421
422
|
the installed ones; the CLI itself in neither).
|
|
422
|
-
- `mcp.Mcp` — `prefix: Optional[str]
|
|
423
|
+
- `mcp.Mcp` — `prefix: Optional[str]`, `strict: bool` (installs through it allow no hooks); exits 2 when the template uses a placeholder
|
|
423
424
|
other than `{name}` or does not format to a tool-name segment.
|
|
424
425
|
- `build_pyz`: `EXTRA_REQUIREMENTS` — `{extra: (requirement, ...)}`, what each
|
|
425
426
|
dotagents-cli extra installs (`uri`, `http`, `mcp-http`, `sftp`, `s3`, `repo`),
|
|
@@ -976,8 +977,14 @@ its directory, bounded by `LIMITS`.
|
|
|
976
977
|
- Allowances, in the user store only, keyed by the overlay's store and name:
|
|
977
978
|
`allowances_path(user_store)`, `read_allowances`, `allowed_digest(user_store, identity,
|
|
978
979
|
store=None)`, `allow(user_store, identity, digest, store=None)`, `revoke(...) -> bool`.
|
|
979
|
-
- `
|
|
980
|
-
|
|
980
|
+
- Installing allows: `record_install(user_store, overlay, *, fresh, no_hooks, dry_run=False) ->
|
|
981
|
+
(outcome, entries)` is what `overlays add` (`fresh=True`) and `overlays sync` do to the allowance
|
|
982
|
+
of the installed copy: `on` (allowed at the digest installed; a fresh add also forgets a revoke),
|
|
983
|
+
`declined` (`--no-hooks`, remembered), `strict` (`strict_mcp(user_store)`: a server started `dotagents mcp --strict`, or by `DOTAGENTS_MCP`
|
|
984
|
+
with the user's `mcp.json` `{"strict": true}`, installs without allowing and writes nothing; the
|
|
985
|
+
default server behaves as the command line; `mcp_settings_path`, `mcp_strict_setting` read the user
|
|
986
|
+
store only; `_MCP_STRICT` is the process flag), `revoked` (kept off across syncs), `none`; `read_revoked`,
|
|
987
|
+
`is_revoked`; `revoke` also records the revoke (the allowance file holds `allowed` and `revoked`).
|
|
981
988
|
- `read_tree(overlay, keep=False) -> (digest, files)` (the bytes the digest covers when `keep`),
|
|
982
989
|
`snapshot(user_store, overlay, digest, files) -> Path` — the copy a hook runs from, one directory
|
|
983
990
|
per digest under `<user store>/.cache/hook-snapshots/` made from exactly the bytes hashed (what
|
|
@@ -195,12 +195,32 @@ def allowed_digest(user_store: "str | os.PathLike[str]", identity: str,
|
|
|
195
195
|
return read_allowances(user_store).get(_key(user_store, identity, store))
|
|
196
196
|
|
|
197
197
|
|
|
198
|
-
def
|
|
198
|
+
def read_revoked(user_store: "str | os.PathLike[str]") -> "set[str]":
|
|
199
|
+
"""The overlays (by store and name) whose hooks the user turned off or declined at install: a
|
|
200
|
+
later sync does not allow them again; `allow-hooks` or a fresh `overlays add` does."""
|
|
201
|
+
try:
|
|
202
|
+
data = json.loads(allowances_path(user_store).read_text(encoding="utf-8"))
|
|
203
|
+
except (OSError, ValueError):
|
|
204
|
+
return set()
|
|
205
|
+
revoked = data.get("revoked") if isinstance(data, dict) else None
|
|
206
|
+
return {str(item) for item in revoked} if isinstance(revoked, list) else set()
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _write_allowances(user_store: "str | os.PathLike[str]", allowed: "dict[str, str]",
|
|
210
|
+
revoked: "Optional[set[str]]" = None) -> None:
|
|
199
211
|
from dotagents._fs import write_text_lf
|
|
200
212
|
|
|
201
213
|
path = allowances_path(user_store)
|
|
202
214
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
203
|
-
|
|
215
|
+
if revoked is None:
|
|
216
|
+
revoked = read_revoked(user_store)
|
|
217
|
+
write_text_lf(path, json.dumps({"allowed": dict(sorted(allowed.items())), "revoked": sorted(revoked)},
|
|
218
|
+
indent=2) + "\n", atomic=True)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def is_revoked(user_store: "str | os.PathLike[str]", identity: str,
|
|
222
|
+
store: "Optional[str | os.PathLike[str]]" = None) -> bool:
|
|
223
|
+
return _key(user_store, identity, store) in read_revoked(user_store)
|
|
204
224
|
|
|
205
225
|
|
|
206
226
|
def allow(user_store: "str | os.PathLike[str]", identity: str, digest: str,
|
|
@@ -208,19 +228,80 @@ def allow(user_store: "str | os.PathLike[str]", identity: str, digest: str,
|
|
|
208
228
|
"""Allow ``identity``'s hooks (of the overlay installed in ``store``, default the user store) at
|
|
209
229
|
``digest``, replacing the digest allowed before: there is one."""
|
|
210
230
|
allowed = read_allowances(user_store)
|
|
211
|
-
|
|
212
|
-
|
|
231
|
+
key = _key(user_store, identity, store)
|
|
232
|
+
allowed[key] = digest.lower()
|
|
233
|
+
_write_allowances(user_store, allowed, read_revoked(user_store) - {key})
|
|
213
234
|
|
|
214
235
|
|
|
215
236
|
def revoke(user_store: "str | os.PathLike[str]", identity: str,
|
|
216
237
|
store: "Optional[str | os.PathLike[str]]" = None) -> bool:
|
|
238
|
+
"""Turn the overlay's hooks off and keep them off across syncs. True when that changed anything."""
|
|
217
239
|
allowed = read_allowances(user_store)
|
|
218
|
-
|
|
240
|
+
key = _key(user_store, identity, store)
|
|
241
|
+
revoked = read_revoked(user_store)
|
|
242
|
+
if allowed.pop(key, None) is None and key in revoked:
|
|
219
243
|
return False
|
|
220
|
-
_write_allowances(user_store, allowed)
|
|
244
|
+
_write_allowances(user_store, allowed, revoked | {key})
|
|
221
245
|
return True
|
|
222
246
|
|
|
223
247
|
|
|
248
|
+
def mcp_settings_path(user_store: "str | os.PathLike[str]") -> Path:
|
|
249
|
+
return Path(user_store) / "overlays" / ".dotagents-state" / "mcp.json"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def mcp_strict_setting(user_store: "str | os.PathLike[str]") -> bool:
|
|
253
|
+
"""The user's own setting ``{"strict": true}`` for the MCP server, read from the user store only
|
|
254
|
+
(never from a project, whose MCP configuration an agent can write)."""
|
|
255
|
+
try:
|
|
256
|
+
data = json.loads(mcp_settings_path(user_store).read_text(encoding="utf-8"))
|
|
257
|
+
except (OSError, ValueError):
|
|
258
|
+
return False
|
|
259
|
+
return isinstance(data, dict) and data.get("strict") is True
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
#: Set by `dotagents mcp --strict` (or the user's setting): installs through this server allow no hooks.
|
|
263
|
+
_MCP_STRICT: "list[bool]" = []
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def strict_mcp(user_store: "str | os.PathLike[str]") -> bool:
|
|
267
|
+
"""Whether this process serves MCP tools in strict mode, chosen when the server was started: an
|
|
268
|
+
add or sync through it installs without allowing hooks, as ``--no-hooks`` does. The default server
|
|
269
|
+
behaves as the command line does."""
|
|
270
|
+
return bool(_MCP_STRICT) or (bool(os.environ.get("DOTAGENTS_MCP")) and mcp_strict_setting(user_store))
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def record_install(user_store: "str | os.PathLike[str]", overlay: Any, *, fresh: bool, no_hooks: bool,
|
|
274
|
+
dry_run: bool = False) -> "tuple[str, list[dict[str, Any]]]":
|
|
275
|
+
"""What installing or syncing ``overlay`` (the installed copy) does to its hooks' allowance.
|
|
276
|
+
Returns ``(outcome, entries)``: ``none`` (it has no hooks), or ``on`` (allowed at the digest it
|
|
277
|
+
holds, which is the digest installed), ``declined`` (``--no-hooks``: recorded so a later sync
|
|
278
|
+
does not allow them), ``strict`` (installed through a strict MCP server: nothing is allowed or recorded),
|
|
279
|
+
``revoked`` (turned off earlier and kept off by a sync). A fresh add allows, and forgets an
|
|
280
|
+
earlier revoke. With ``dry_run`` nothing is written."""
|
|
281
|
+
plan = overlay_plan(overlay, ANYWHERE, user_store)
|
|
282
|
+
entries = plan["hooks"]
|
|
283
|
+
if not entries:
|
|
284
|
+
return "none", []
|
|
285
|
+
key = _key(user_store, overlay.name, overlay.store)
|
|
286
|
+
if strict_mcp(user_store) and not no_hooks:
|
|
287
|
+
# Installs without allowing and writes nothing: what was allowed stays allowed at its own digest.
|
|
288
|
+
return "strict", entries
|
|
289
|
+
if no_hooks:
|
|
290
|
+
if not dry_run and plan["digest"] and key not in read_revoked(user_store):
|
|
291
|
+
allowed = read_allowances(user_store)
|
|
292
|
+
allowed.pop(key, None)
|
|
293
|
+
_write_allowances(user_store, allowed, read_revoked(user_store) | {key})
|
|
294
|
+
return "declined", entries
|
|
295
|
+
if not fresh and key in read_revoked(user_store):
|
|
296
|
+
return "revoked", entries
|
|
297
|
+
if plan["digest"] is None:
|
|
298
|
+
return "declined", entries
|
|
299
|
+
if not dry_run:
|
|
300
|
+
allow(user_store, overlay.name, plan["digest"], overlay.store)
|
|
301
|
+
return "on", entries
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
|
|
224
305
|
# --------------------------------------------------------------------------- #
|
|
225
306
|
# What an overlay's hooks are on this machine, for a harness.
|
|
226
307
|
# --------------------------------------------------------------------------- #
|
|
@@ -357,20 +438,6 @@ def _token() -> str:
|
|
|
357
438
|
return secrets.token_hex(4)
|
|
358
439
|
|
|
359
440
|
|
|
360
|
-
def install_notice(overlay: Any, user_store: "str | os.PathLike[str]") -> "Optional[str]":
|
|
361
|
-
"""A line for an install or sync: the overlay has hooks and they are off until the user allows
|
|
362
|
-
them. None when it has none, or they are allowed at this digest."""
|
|
363
|
-
from dotagents import _activate
|
|
364
|
-
|
|
365
|
-
entries, _ = _aos.hooks_of(overlay.document(), _activate.files(overlay), _aos.current_os(),
|
|
366
|
-
sorted(_activate.interpreters()))
|
|
367
|
-
if not entries or overlay_plan(overlay, ANYWHERE, user_store)["on"]:
|
|
368
|
-
return None
|
|
369
|
-
return ("overlay %s has hooks (%s); they are off until you allow them at its digest: "
|
|
370
|
-
"dotagents overlays hooks %s" % (overlay.name, ", ".join(entry["event"] for entry in entries),
|
|
371
|
-
overlay.name))
|
|
372
|
-
|
|
373
|
-
|
|
374
441
|
def declares_guard(overlay: Any) -> bool:
|
|
375
442
|
"""Whether the overlay has a hook with ``on_failure = "refuse"``."""
|
|
376
443
|
from dotagents import _activate
|
|
@@ -19,7 +19,18 @@ class Mcp(LoggingArgs, Cmd):
|
|
|
19
19
|
"Name prefix template for forwarded tools; `{name}` is the skill or server name."
|
|
20
20
|
("--prefix",)
|
|
21
21
|
|
|
22
|
+
strict: bool = False
|
|
23
|
+
"Strict mode: an add or sync through this server installs without allowing the overlay's hooks."
|
|
24
|
+
("--strict",)
|
|
25
|
+
|
|
22
26
|
def __call__(self) -> int:
|
|
27
|
+
from dotagents import _overlay_hooks
|
|
28
|
+
from dotagents._scope import resolve_user_store
|
|
29
|
+
|
|
30
|
+
# Chosen when the server starts: by the flag, or by the user's own setting in the user store
|
|
31
|
+
# (never a project's: an agent can write a project's MCP configuration).
|
|
32
|
+
if self.strict or _overlay_hooks.mcp_strict_setting(resolve_user_store()):
|
|
33
|
+
_overlay_hooks._MCP_STRICT.append(True)
|
|
23
34
|
if self.prefix is not None:
|
|
24
35
|
try:
|
|
25
36
|
sample = self.prefix.format(name="server")
|
|
@@ -241,12 +241,6 @@ def _prepare(scope, dest_dir: Path, name: str, *, change: str, no_setup=False, d
|
|
|
241
241
|
except _aos.Refused as refused:
|
|
242
242
|
logger.error("%s: %s %s (hash every requirement or none)", name, refused.code, refused.subject)
|
|
243
243
|
return 1
|
|
244
|
-
if change != "remove":
|
|
245
|
-
from dotagents import _overlay_hooks
|
|
246
|
-
|
|
247
|
-
note = _overlay_hooks.install_notice(overlay, scope.user_root)
|
|
248
|
-
if note:
|
|
249
|
-
logger.warning("%s", note)
|
|
250
244
|
if variant is None and change != "remove":
|
|
251
245
|
# A default setup.<ext> present that this machine cannot run.
|
|
252
246
|
plan = _aos.plan(overlay.document(), _activate.context(overlay), pinned=True)
|
|
@@ -543,9 +537,34 @@ def _log_install(logger, verb: str, name: str, result, dry_run: bool) -> None:
|
|
|
543
537
|
logger.info("overlay %s: backed up %s", name, ", ".join(result.backed_up))
|
|
544
538
|
|
|
545
539
|
|
|
540
|
+
def _record_hooks(scope, dest: Path, name: str, *, fresh: bool, no_hooks: bool, dry_run: bool, logger) -> None:
|
|
541
|
+
"""Installing or syncing an overlay allows its hooks at the digest installed (the person chose
|
|
542
|
+
this source, and the overlay declares them), and says which are on. ``--no-hooks`` installs
|
|
543
|
+
without allowing and remembers it; an overlay whose hooks were revoked stays off across syncs;
|
|
544
|
+
a request through the MCP tool allows like any other, unless the server was started `--strict`
|
|
545
|
+
(or the user's own setting says so), when it installs without allowing."""
|
|
546
|
+
from dotagents import _overlay_hooks
|
|
547
|
+
from dotagents._overlays import Overlay
|
|
548
|
+
|
|
549
|
+
overlay = Overlay(dest if dest.is_dir() else dest, scope.agents_root)
|
|
550
|
+
outcome, entries = _overlay_hooks.record_install(scope.user_root, overlay, fresh=fresh, no_hooks=no_hooks,
|
|
551
|
+
dry_run=dry_run)
|
|
552
|
+
if outcome == "none":
|
|
553
|
+
return
|
|
554
|
+
listed = ", ".join("%s (%s)" % (e["event"], e["path"] or "no hook.<ext> can run here") for e in entries)
|
|
555
|
+
if outcome == "on":
|
|
556
|
+
logger.info("overlay %s: %s its hooks, now on: %s (dotagents overlays hooks %s; revoke-hooks turns them off)",
|
|
557
|
+
name, "would allow" if dry_run else "allowed", listed, name)
|
|
558
|
+
else:
|
|
559
|
+
why = {"declined": "--no-hooks", "revoked": "they were revoked and a sync keeps them off",
|
|
560
|
+
"strict": "installed through `dotagents mcp --strict`, which allows no hooks"}[outcome]
|
|
561
|
+
logger.warning("overlay %s: its hooks are off (%s): %s -- allow them with `dotagents overlays "
|
|
562
|
+
"allow-hooks %s`", name, why, listed, name)
|
|
563
|
+
|
|
564
|
+
|
|
546
565
|
def _install_one(scope, name: str, src: Path, origin, *, copy: bool, no_setup: bool,
|
|
547
566
|
dry_run: bool, logger, setup_timeout: "Optional[int]" = None,
|
|
548
|
-
no_venv: bool = False, cache=None) -> "list[str]":
|
|
567
|
+
no_venv: bool = False, cache=None, no_hooks: bool = False) -> "list[str]":
|
|
549
568
|
"""Install (or re-install) overlay ``name`` from ``src`` into the scope,
|
|
550
569
|
publish its skills, install its Python dependencies into the scope's venv
|
|
551
570
|
and run its setup (``install`` the first time, then ``sync``). Nothing is written for a source
|
|
@@ -634,6 +653,8 @@ def _install_one(scope, name: str, src: Path, origin, *, copy: bool, no_setup: b
|
|
|
634
653
|
"was last prepared for (fix it and run `overlays sync %s`)", what, name, name,
|
|
635
654
|
)
|
|
636
655
|
raise SystemExit("error: %s for overlay %r failed (exit %d)" % (what, name, rc))
|
|
656
|
+
_record_hooks(scope, dest if dest.is_dir() else src, name, fresh=True, no_hooks=no_hooks, dry_run=dry_run,
|
|
657
|
+
logger=logger)
|
|
637
658
|
return skills
|
|
638
659
|
|
|
639
660
|
|
|
@@ -737,6 +758,10 @@ class OverlayAdd(_RepoArgs):
|
|
|
737
758
|
"Skip running an overlay's declared `setup` after install."
|
|
738
759
|
("--no-setup",)
|
|
739
760
|
|
|
761
|
+
no_hooks: bool = False
|
|
762
|
+
"Do not allow the overlay's hooks (installing allows them at the digest installed; `allow-hooks` does it later)."
|
|
763
|
+
("--no-hooks",)
|
|
764
|
+
|
|
740
765
|
setup_timeout: Optional[int] = None
|
|
741
766
|
"Seconds setup may run (default: the setup variant's timeout, else 300; 0 = no limit)."
|
|
742
767
|
("--setup-timeout",)
|
|
@@ -805,7 +830,7 @@ class OverlayAdd(_RepoArgs):
|
|
|
805
830
|
scope, name, src, origin, copy=self.copy, no_setup=self.no_setup,
|
|
806
831
|
dry_run=self.dry_run, logger=self._logger_,
|
|
807
832
|
setup_timeout=self.setup_timeout, no_venv=self.no_venv,
|
|
808
|
-
cache=source.cache,
|
|
833
|
+
cache=source.cache, no_hooks=self.no_hooks,
|
|
809
834
|
)
|
|
810
835
|
except (SystemExit, OSError) as exc:
|
|
811
836
|
why = str(exc.code if isinstance(exc, SystemExit) else exc)
|
|
@@ -1273,6 +1298,10 @@ class OverlaySync(_RepoArgs):
|
|
|
1273
1298
|
"Skip running each overlay's declared `setup` after sync."
|
|
1274
1299
|
("--no-setup",)
|
|
1275
1300
|
|
|
1301
|
+
no_hooks: bool = False
|
|
1302
|
+
"Do not allow the overlay's hooks (installing allows them at the digest installed; `allow-hooks` does it later)."
|
|
1303
|
+
("--no-hooks",)
|
|
1304
|
+
|
|
1276
1305
|
no_venv: bool = False
|
|
1277
1306
|
"Do not create or update the scope's Python venv (overlay requirements, duho, the .pth layering)."
|
|
1278
1307
|
("--no-venv",)
|
|
@@ -1401,6 +1430,9 @@ class OverlaySync(_RepoArgs):
|
|
|
1401
1430
|
self._logger_.error("preparing overlay %s failed (exit %d); it is activated only for the "
|
|
1402
1431
|
"digest it was last prepared for", name, rc)
|
|
1403
1432
|
failures.append(name)
|
|
1433
|
+
else:
|
|
1434
|
+
_record_hooks(scope, dest_dir, name, fresh=False, no_hooks=self.no_hooks,
|
|
1435
|
+
dry_run=self.dry_run, logger=self._logger_)
|
|
1404
1436
|
|
|
1405
1437
|
# A `requires` added upstream: install what no store provides yet.
|
|
1406
1438
|
have = frozenset(o.normalized_name for o in scope.overlays)
|
|
@@ -1425,7 +1457,7 @@ class OverlaySync(_RepoArgs):
|
|
|
1425
1457
|
skills += _install_one(
|
|
1426
1458
|
scope, dep, src, origin, copy=self.copy, no_setup=self.no_setup,
|
|
1427
1459
|
dry_run=self.dry_run, logger=self._logger_, setup_timeout=self.setup_timeout,
|
|
1428
|
-
no_venv=self.no_venv, cache=chain.cache,
|
|
1460
|
+
no_venv=self.no_venv, cache=chain.cache, no_hooks=self.no_hooks,
|
|
1429
1461
|
)
|
|
1430
1462
|
finally:
|
|
1431
1463
|
# Recompose over ALL installed overlays in (priority, name) order
|