dotagents-cli 0.3.2__tar.gz → 0.3.4__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.
Files changed (71) hide show
  1. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/CHANGELOG.md +81 -0
  2. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/PKG-INFO +5 -5
  3. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/README.md +1 -1
  4. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/build.py +3 -3
  5. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/api/reference.md +0 -1
  6. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/guide/commands.md +14 -2
  7. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/install.py +2 -2
  8. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/mkdocs.yml +0 -1
  9. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/pyproject.toml +3 -3
  10. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/AGENTS.md +33 -11
  11. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/__init__.py +1 -1
  12. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_agents.py +20 -8
  13. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_merge.py +6 -2
  14. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_scope.py +1 -1
  15. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/cli/__init__.py +7 -15
  16. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/cli/_common.py +32 -0
  17. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/cli/build_pyz.py +15 -17
  18. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/cli/context.py +27 -6
  19. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/cli/env.py +31 -7
  20. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tests/test_agents.py +18 -0
  21. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tests/test_build_pyz.py +19 -1
  22. dotagents_cli-0.3.4/tests/test_env_cli_scope.py +263 -0
  23. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tests/test_env_format.py +12 -12
  24. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tools/audit.py +7 -5
  25. dotagents_cli-0.3.2/docs/api/sync.md +0 -3
  26. dotagents_cli-0.3.2/src/dotagents/_sync.py +0 -72
  27. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/.gitignore +0 -0
  28. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/LICENSE +0 -0
  29. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/api/agents.md +0 -0
  30. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/api/cli.md +0 -0
  31. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/api/context.md +0 -0
  32. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/api/env.md +0 -0
  33. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/api/hooks.md +0 -0
  34. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/api/merge.md +0 -0
  35. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/api/overlays.md +0 -0
  36. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/api/resolve.md +0 -0
  37. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/api/scope.md +0 -0
  38. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/api/skills.md +0 -0
  39. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/changelog.md +0 -0
  40. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/guide/authoring.md +0 -0
  41. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/guide/install.md +0 -0
  42. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/guide/overlays.md +0 -0
  43. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/guide/private-sync.md +0 -0
  44. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/docs/index.md +0 -0
  45. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/__main__.py +0 -0
  46. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_context.py +0 -0
  47. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_env.py +0 -0
  48. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_hooks.py +0 -0
  49. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_overlay/AGENTS.md +0 -0
  50. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_overlay/CLAUDE.md +0 -0
  51. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_overlay/README.md +0 -0
  52. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_overlay/dotagents/DECISIONS.md +0 -0
  53. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_overlay/dotagents/cmds/README.md +0 -0
  54. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_overlay/dotagents/hooks/preinvocation_antigravity_context.py +0 -0
  55. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_overlay/dotagents/hooks/pretooluse_codex_env.py +0 -0
  56. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_overlays.py +0 -0
  57. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_resolve.py +0 -0
  58. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_skills.py +0 -0
  59. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/_wrappers.py +0 -0
  60. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/cli/init.py +0 -0
  61. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/src/dotagents/cli/overlays.py +0 -0
  62. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tests/test_audit_leak.py +0 -0
  63. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tests/test_cmds_discovery.py +0 -0
  64. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tests/test_context.py +0 -0
  65. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tests/test_env.py +0 -0
  66. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tests/test_hooks.py +0 -0
  67. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tests/test_overlays_command.py +0 -0
  68. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tests/test_scope.py +0 -0
  69. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tests/test_wire_hooks.py +0 -0
  70. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tests/test_wrappers.py +0 -0
  71. {dotagents_cli-0.3.2 → dotagents_cli-0.3.4}/tools/cloud-setup.sh +0 -0
@@ -7,6 +7,87 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.4] - 2026-08-16
11
+
12
+ ### Changed
13
+
14
+ - chore: raise the dependency floors and scope them to a minor series —
15
+ `duho>=0.5.0,<0.6` (was `>=0.4.0`) and `pathlib_next>=0.9.0,<0.10` (was
16
+ `>=0.8.0`). Both are pre-1.0, where a minor bump is the signal that the
17
+ documented API broke, so the ceiling is what keeps the next one from arriving
18
+ unannounced. Neither floor sits above its `.0` patch: the suite and a CLI
19
+ smoke pass at exactly duho 0.5.0 and pathlib_next 0.9.0, so nothing here needs
20
+ an API added later in either series. The one 0.5.0 behavior change that
21
+ reaches this CLI's surface is list-typed *option* fields taking one value per
22
+ occurrence instead of `nargs="*"` — `--agents` and `--cmdspath` are unaffected,
23
+ since the documented forms are the comma list (`--agents a,b`, split by the
24
+ command itself) and the repeated flag, both of which behave the same either
25
+ way. Exercised end to end against duho 0.5.4 / pathlib_next 0.9.2 on Python
26
+ 3.9 and 3.14. The `[uri]`/`[http]`/`[sftp]`/`[s3]` extras stay unversioned
27
+ passthroughs to `pathlib_next`'s own extras.
28
+ - chore: `build-pyz`'s vendored pins moved with those floors — the `.pyz` now
29
+ bundles duho 0.5.0 and pathlib_next 0.9.0 (was 0.4.0 / 0.8.0), the minimum the
30
+ package claims to support rather than the latest patch. These pins are a
31
+ second copy of the dependency versions and had drifted a full minor series
32
+ behind, so the shipped zipapp bundled versions `pip install dotagents-cli`
33
+ would have refused. The rebuilt `.pyz` keeps full flag/help/positional
34
+ fidelity through the zipapp shim.
35
+
36
+ ## [0.3.3] - 2026-08-16
37
+
38
+ ### Added
39
+
40
+ - feat: `env` and `context` gained **`--agents-dir`** (from the shared
41
+ `DotAgentsArgs` base) to override the store for one run. Their `-g/--global`
42
+ keeps its existing, narrower meaning here — *skip the project-level files* —
43
+ and now says so in `--help`.
44
+
45
+ ### Removed
46
+
47
+ - **`build-pyz` no longer bundles the repo's `tools/`** as `dotagents/_tools`
48
+ inside the built `.pyz`, and the `--tools-dir` flag is gone with it. Nothing
49
+ read `_tools`: the compiled `audit`/`leak-check` wrappers that shelled out to
50
+ it no longer exist. Every shipped artifact carried the dead weight while
51
+ `tools/audit.py` claimed it was "not shipped in the `.pyz`" — now true.
52
+ - **`dotagents._sync`** — a `pathlib_next.PathSyncer` wrapper that existed only
53
+ to back the `install` subcommand's backup/copy report. `install` was removed
54
+ in 0.3.x; the module has had no callers since, no tests, and a return
55
+ annotation that disagreed with what it returned. `pathlib_next` remains a
56
+ dependency (duho, and `--from` URI support). Its API-reference page went with
57
+ it.
58
+
59
+ ### Fixed
60
+
61
+ - docs: a cluster of "docs say X, tree does Y" corrections — `tools/audit.py`
62
+ no longer claims to ship as a bundled `audit` command module (it is repo CI
63
+ tooling and there is no `dotagents audit`) and its `--root` help names the
64
+ real default; `install.py`'s usage line drops the removed `install` and the
65
+ never-existing `audit`; the README's `tools/` row says where `leak-check`
66
+ actually lives; the API header names the real `_overlays` exports
67
+ (`install_overlay_dir` / `apply_overlay` / `run_overlay_setup`) and the real
68
+ package-data dirs (`_overlay` / `_overlays_src`, never a `skeleton/`); and
69
+ `_merge._extract_block`'s docstring now says it returns the block *including*
70
+ its marker lines, which is what it has always done and what callers rely on.
71
+ - fix: **`stamp_identity` no longer pretends to emit `AGENTS_AGENT`.** The line
72
+ sourced the value from `$AGENTS_AGENT` and only assigned when that same key
73
+ was unset, so it could never emit anything — while the docstring and the
74
+ shipped API header both advertised the var. Line removed; the API header's
75
+ "emitted by the identity/env layer" list now drops both `AGENTS_AGENT` and
76
+ `AGENTS_CODE_SESSION_ID` (the latter was deliberately never emitted) and says
77
+ so explicitly, so nothing branches on a var that never arrives.
78
+ - fix: **`dotagents env` and `dotagents context` now resolve their roots instead
79
+ of hardcoding them.** Both took `Path.cwd()` as the project root and
80
+ `~/.agents` as the user store, so `$AGENTS_PROJECT_ROOT` (or the agent-native
81
+ `$CLAUDE_PROJECT_DIR`) and `$AGENTS_HOME` were ignored by the two commands
82
+ that most needed them — a `SessionStart` hook runs `dotagents context` from
83
+ wherever the session happens to start, so a pinned project root was silently
84
+ dropped and a relocated store was never read. They now use
85
+ `_scope.project_root_default()` and the new
86
+ `dotagents.cli.resolve_user_store()` (`--agents-dir` → `$AGENTS_HOME` →
87
+ legacy `$DOTAGENTS_AGENTS_DIR` → `~/.agents`), matching what the package's own
88
+ `resolve_scope` docstring and command discovery already promised. Behavior is
89
+ unchanged when none of the vars are set.
90
+
10
91
  ## [0.3.2] - 2026-07-25
11
92
 
12
93
  ### Added
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: dotagents-cli
3
- Version: 0.3.2
3
+ Version: 0.3.4
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
@@ -41,8 +41,8 @@ Classifier: Programming Language :: Python :: 3.12
41
41
  Classifier: Programming Language :: Python :: 3.13
42
42
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
43
43
  Requires-Python: >=3.9
44
- Requires-Dist: duho>=0.4.0
45
- Requires-Dist: pathlib-next>=0.8.0
44
+ Requires-Dist: duho<0.6,>=0.5.0
45
+ Requires-Dist: pathlib-next<0.10,>=0.9.0
46
46
  Provides-Extra: dev
47
47
  Requires-Dist: pytest; extra == 'dev'
48
48
  Provides-Extra: docs
@@ -102,7 +102,7 @@ them. Everything else is repo infrastructure.
102
102
  | --- | --- |
103
103
  | `src/dotagents/` | The installable `dotagents` CLI (`init`/`overlays`/`context`/`env`/`build-pyz`) — that is the whole shipped surface; commands beyond it come from overlays or your own `cmds/` modules |
104
104
  | `src/dotagents/_overlay/` | The **base overlay** `init` writes: `AGENTS.md` scaffolding, `CLAUDE.md`, `dotagents/DECISIONS.md` (empty design-log index), and an empty `dotagents/cmds/` dir — your drop-in point for your own command modules. Neutral — imposes no flows, ships no command |
105
- | `tools/` | Required tooling (not an overlay): `cloud-setup.sh` (`leak-check` moved to the opt-in `leak-check` overlay, D84) |
105
+ | `tools/` | Repo tooling, not shipped: `audit.py` (CI structure check) and `cloud-setup.sh`. `leak-check` is not here either — it is a personal command module you keep in your own private `.agents/dotagents/cmds/` |
106
106
  | `install.py` | Thin shim over `dotagents.cli.main()`, kept at this filename for muscle memory |
107
107
 
108
108
  The **example overlays** — the `flows` workflow set, per-language `kb/` + templates,
@@ -41,7 +41,7 @@ them. Everything else is repo infrastructure.
41
41
  | --- | --- |
42
42
  | `src/dotagents/` | The installable `dotagents` CLI (`init`/`overlays`/`context`/`env`/`build-pyz`) — that is the whole shipped surface; commands beyond it come from overlays or your own `cmds/` modules |
43
43
  | `src/dotagents/_overlay/` | The **base overlay** `init` writes: `AGENTS.md` scaffolding, `CLAUDE.md`, `dotagents/DECISIONS.md` (empty design-log index), and an empty `dotagents/cmds/` dir — your drop-in point for your own command modules. Neutral — imposes no flows, ships no command |
44
- | `tools/` | Required tooling (not an overlay): `cloud-setup.sh` (`leak-check` moved to the opt-in `leak-check` overlay, D84) |
44
+ | `tools/` | Repo tooling, not shipped: `audit.py` (CI structure check) and `cloud-setup.sh`. `leak-check` is not here either — it is a personal command module you keep in your own private `.agents/dotagents/cmds/` |
45
45
  | `install.py` | Thin shim over `dotagents.cli.main()`, kept at this filename for muscle memory |
46
46
 
47
47
  The **example overlays** — the `flows` workflow set, per-language `kb/` + templates,
@@ -3,9 +3,9 @@
3
3
 
4
4
  Thin root-level wrapper over ``dotagents build-pyz`` (``dotagents.cli.build_pyz``):
5
5
  it vendors ``duho`` + ``pathlib_next`` into the archive (via ``pip install --target``,
6
- using **the Python that runs this script**), copies the ``dotagents`` package and the
7
- required ``tools/``, strips caches/metadata, and packages a runnable zipapp. The result
8
- needs no ``pip install`` on the target machine.
6
+ using **the Python that runs this script**), copies the ``dotagents`` package, strips
7
+ caches/metadata, and packages a runnable zipapp. The result needs no ``pip install``
8
+ on the target machine. The repo's ``tools/`` is CI tooling and is NOT bundled.
9
9
 
10
10
  Usage:
11
11
  python build.py # -> dist/dotagents.pyz
@@ -21,4 +21,3 @@ form its API. Generated from docstrings, organized by module:
21
21
  - **[Hooks](hooks.md)** — additive, idempotent merge of dotagents' hooks into an
22
22
  agent's own settings/config file.
23
23
  - **[Skills](skills.md)** — publishing an overlay's skills into a scope's shared dir.
24
- - **[Sync](sync.md)** — the `PathSyncer` backup/copy wrapper for `install`.
@@ -155,7 +155,14 @@ dotagents context --write-agent # write each agent's native confi
155
155
  - `--write-agent` — write each agent's native config file instead of `[output]`.
156
156
  - `--agents <a,b>` — which agents to generate for (default: the active agent).
157
157
  - `--format markdown|system-reminder|json` — output shape.
158
- - `-g` / `--global` — user scope.
158
+ - `-g` / `--global` — skip the project-level context files (the store is unaffected).
159
+ - `--agents-dir <dir>` — user store override for this run.
160
+
161
+ Roots: the store is `--agents-dir` → `$AGENTS_HOME` → `~/.agents`, and the project
162
+ root is `$AGENTS_PROJECT_ROOT` → `$CLAUDE_PROJECT_DIR` → the cwd. A `SessionStart`
163
+ hook runs this from wherever the session started, so pinning
164
+ `AGENTS_PROJECT_ROOT` is what keeps the assembled context the same in every
165
+ subdirectory.
159
166
 
160
167
  ## env
161
168
 
@@ -174,7 +181,12 @@ python -m dotagents env --diff --format json # only vars that differ from the
174
181
  (`bat`/`batch`, `set "KEY=value"`), `fish` (`set -gx KEY value`). Data forms:
175
182
  `json`, `ini`, `yaml`. An explicit `--format` always wins.
176
183
  - `--diff` — emit only the change set vs. the caller's environment.
177
- - `-g` / `--global` — user scope.
184
+ - `-g` / `--global` — skip the project-level env files (the store is unaffected).
185
+ - `--agents-dir <dir>` — user store override for this run.
186
+
187
+ Roots resolve exactly as for `context` above (`$AGENTS_HOME` /
188
+ `$AGENTS_PROJECT_ROOT`, both of which `env` also emits — so a subprocess reading
189
+ them agrees with the parent that wrote them).
178
190
 
179
191
  !!! warning
180
192
  `env` output is sensitive by design — it prints resolved values. Treat the
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Entry point for the dotagents CLI (init / install / overlays / build-pyz / ...).
2
+ """Entry point for the dotagents CLI (init / overlays / context / env / build-pyz).
3
3
 
4
4
  Thin front over ``dotagents.cli.main()``, kept at this filename so existing
5
5
  muscle-memory/docs pointing at ``python install.py`` still work.
@@ -14,7 +14,7 @@ bare ``pip`` off PATH) guarantees the install lands in *this* Python, so the
14
14
  import that follows actually sees it. When everything is already importable it
15
15
  just dispatches -- no pip is ever run.
16
16
 
17
- Usage: python install.py <init|install|overlays|audit|build-pyz|...> [options]
17
+ Usage: python install.py <init|overlays|context|env|build-pyz|...> [options]
18
18
  Run `python install.py --help` for the full subcommand/flag reference.
19
19
  """
20
20
  import os
@@ -46,7 +46,6 @@ nav:
46
46
  - Merge: api/merge.md
47
47
  - Hooks: api/hooks.md
48
48
  - Skills: api/skills.md
49
- - Sync: api/sync.md
50
49
  - Changelog: changelog.md
51
50
 
52
51
  plugins:
@@ -9,7 +9,7 @@ build-backend = "hatchling.build"
9
9
  # redirect/placeholder before someone else does, but the CLI ships under -cli.
10
10
  [project]
11
11
  name = "dotagents-cli"
12
- version = "0.3.2"
12
+ version = "0.3.4"
13
13
  authors = [{ name = "Jose A." }]
14
14
  description = "The dotagents CLI: install and manage a portable ~/.agents config for AI coding agents"
15
15
  readme = "README.md"
@@ -30,8 +30,8 @@ classifiers = [
30
30
  "Topic :: Software Development :: Libraries :: Python Modules",
31
31
  ]
32
32
  dependencies = [
33
- "duho>=0.4.0",
34
- "pathlib_next>=0.8.0",
33
+ "duho>=0.5.0,<0.6",
34
+ "pathlib_next>=0.9.0,<0.10",
35
35
  ]
36
36
 
37
37
  [project.optional-dependencies]
@@ -11,8 +11,17 @@ can read it without the source. Full docs: https://jose-pr.github.io/dotagents/
11
11
  `duho.app` with the discovered command set. Also the `dotagents` console script
12
12
  (`[project.scripts]`).
13
13
  - `dotagents.cli.Dotagents(LoggingArgs, Cli)` — the umbrella CLI class.
14
+ - `dotagents.cli.DotAgentsArgs` — mix-in base carrying the shared `-g/--global` +
15
+ `--agents-dir` pair and `resolve_scope()`; an overlay-shipped command module
16
+ should inherit it (as the FIRST base) rather than redeclare the flags.
17
+ - `dotagents.cli.resolve_user_store(agents_dir=None) -> Path` — the USER store
18
+ root: `agents_dir` (`--agents-dir`) → `$AGENTS_HOME` → legacy
19
+ `$DOTAGENTS_AGENTS_DIR` → `~/.agents`. Use it (not `resolve_scope`) when the
20
+ store is always the user store and the project scope only adds/removes tiers,
21
+ as in `env` / `context`.
14
22
  - Compiled command classes live in `dotagents.cli.<name>` (`init`, `overlays`,
15
- `context`, `env`, `build_pyz`); each is a `class X(LoggingArgs, Cmd)` with a
23
+ `context`, `env`, `build_pyz`); each is a `class X(LoggingArgs, Cmd)` or
24
+ `class X(DotAgentsArgs)`, which is that pair transitively — with a
16
25
  `__call__`. That is the WHOLE shipped surface — dotagents bundles no command
17
26
  module of its own. `link` / `sync` left the package with their logic (D85): the
18
27
  opt-in **private-sync** overlay ships them, renamed `link-project` /
@@ -30,8 +39,9 @@ can read it without the source. Full docs: https://jose-pr.github.io/dotagents/
30
39
 
31
40
  - `_agents` — `Agent` base type + per-agent adapters; `stamp_identity(...)` emits the
32
41
  standardized `AGENTS_*` / `AGENT` identity vars.
33
- - `_overlays` — `install_overlay` / `read_manifest` / `find_setup_script` /
34
- `run_setup_script`; installs an overlay's files and collects its `routing` / `rules`
42
+ - `_overlays` — `install_overlay_dir` (copy an overlay into a scope, self-describing)
43
+ / `apply_overlay` (lay its files down) / `read_manifest` / `find_setup_script` /
44
+ `run_overlay_setup`; installs an overlay's files and collects its `routing` / `rules`
35
45
  contributions to the managed `AGENTS.md` block. `DEFAULT_PRIORITY = 500`.
36
46
  - `_scope` — `resolve_scope(global_scope, agents_dir=None)` and `resolve_source(...)`;
37
47
  scope = *where installed overlays live* (user = the configurable store, project =
@@ -139,8 +149,6 @@ can read it without the source. Full docs: https://jose-pr.github.io/dotagents/
139
149
  that `python3` resolves to the Microsoft Store app-execution-alias stub and
140
150
  fails outright on this dev machine (exit 49), the same trap noted elsewhere
141
151
  for `py`/venv creation.
142
- - `_sync` — `PathSyncer` wrapper reproducing `install`'s backup/copy/report; requires
143
- `pathlib_next.Path` instances (not plain `pathlib.Path`) and a pre-created parent dir.
144
152
 
145
153
  ## Environment variables
146
154
 
@@ -174,13 +182,25 @@ Tool-internal / secret vars (`DOTAGENTS_*` — kept; read, **never printed**):
174
182
  - `DOTAGENTS_AUDIT_PATTERNS` — path to the machine-local audit-pattern file (tooling).
175
183
 
176
184
  Emitted by the identity/env layer (safe to branch on in env files):
177
- `AGENTS_HARNESS`, `AGENTS_VENDOR`, `AGENTS_MODEL`, `AGENTS_AGENT` / `AGENT`,
178
- `AGENTS_CODE_SESSION_ID`, `AGENTS_PROXY`, `AGENTS_WEBFETCH_PROXY_URL`, plus the two
185
+ `AGENTS_HARNESS`, `AGENTS_VENDOR`, `AGENTS_MODEL`, `AGENT`, `AGENTS_PROXY`,
186
+ `AGENTS_WEBFETCH_PROXY_URL`, plus the two
179
187
  scope roots — `AGENTS_HOME` (the user store, `agents_dir`/`~/.agents`) and
180
188
  `AGENTS_PROJECT_ROOT` (this project's root). Both are seeded only if unset, so a
181
- harness/env can pin them. `resolve_scope` READS `AGENTS_PROJECT_ROOT` (then the
189
+ harness/env can pin them. Deliberately NOT emitted, despite looking like they
190
+ would be: `AGENTS_AGENT` (a named persona — nothing derives one, so there is
191
+ nothing to emit) and `AGENTS_CODE_SESSION_ID` (dropped with the precursor's
192
+ blanket `CLAUDE_*`→`AGENTS_*` rewrite). Do not branch on either. `resolve_scope` READS `AGENTS_PROJECT_ROOT` (then the
182
193
  agent-native `CLAUDE_PROJECT_DIR`, then cwd) for the project scope's root.
183
194
 
195
+ Every command READS both back, so the pin actually holds: `env` and `context`
196
+ resolve their user store through `cli.resolve_user_store()` (`--agents-dir` →
197
+ `$AGENTS_HOME` → legacy `$DOTAGENTS_AGENTS_DIR` → `~/.agents`) and their project
198
+ root through `_scope.project_root_default()` — neither is taken from the cwd or a
199
+ hardcoded home. This matters for hook-invoked runs: a `SessionStart` hook runs
200
+ `dotagents context` / `dotagents env` from wherever the session started, and only
201
+ the pinned root makes that cwd-independent. `-g/--global` on these two means
202
+ *skip the project tier*, not *use a different store*.
203
+
184
204
  ## Gotchas
185
205
 
186
206
  - **Python 3.9 floor.** Files using bare `X | Y` unions in runtime-evaluated positions
@@ -191,8 +211,10 @@ agent-native `CLAUDE_PROJECT_DIR`, then cwd) for the project scope's root.
191
211
  `cli.main()` calls `_repoint_zipapp_sources()` first, extracting the built-in command
192
212
  modules to real temp files. Discovered `cmds` modules are extracted by
193
213
  `_package_data_dir` before import, so they need no repoint.
194
- - **Package data in a `.pyz`.** `_package_data_dir()` resolves `skeleton/` and the
195
- bundled payload via `importlib.resources` (a zip-backed `Traversable` is extracted to
196
- a temp dir once), never `Path(__file__).exists()` (always False in a zipapp).
214
+ - **Package data in a `.pyz`.** `_package_data_dir(name)` resolves a package-data dir
215
+ by name `_overlay` (the base overlay `init` writes) and `_overlays_src` (bundled
216
+ example overlays, when a build includes them) via `importlib.resources` (a
217
+ zip-backed `Traversable` is extracted to a temp dir once), never
218
+ `Path(__file__).exists()` (always False in a zipapp).
197
219
  - **`pathlib_next` needs `typing_extensions` on Python < 3.10** (an upstream gap); a
198
220
  3.9 environment must `pip install typing_extensions`.
@@ -1,3 +1,3 @@
1
1
  """dotagents: installable CLI for the dotagents agent-config payload."""
2
2
 
3
- __version__ = "0.3.2"
3
+ __version__ = "0.3.4"
@@ -490,10 +490,18 @@ class AntigravityAgent(Agent):
490
490
  # PreToolUse hooks do.
491
491
  #
492
492
  # No `shell`/`commandWindows`-equivalent field is documented for Antigravity
493
- # hook commands at all (checked directly, absent) -- the emitted command
494
- # must therefore work via a bare interpreter invocation with no shell
495
- # assumptions, `python "<path>"` exactly as Codex's does, no dual-platform
496
- # variant possible/needed here since nothing platform-specific is offered.
493
+ # hook commands at all (checked directly, absent) -- the emitted command must
494
+ # therefore work via a bare interpreter invocation with no shell assumptions,
495
+ # and there is exactly ONE string to get right for every platform.
496
+ #
497
+ # It emits `python "<path>"`. That is NOT what Codex does, despite the
498
+ # similar shape: Codex emits `python3` and overrides it to `python` through
499
+ # its `commandWindows` field, precisely because `python3` is the POSIX name
500
+ # and `python3` on Windows hits the Store app-execution-alias stub. With no
501
+ # second field available here, one name has to cover both, and `python` is
502
+ # the one that works on Windows -- but a POSIX box that ships only `python3`
503
+ # (still common) gets a hook that cannot start. Known gap, no fix available
504
+ # inside the hook entry itself; see `.agents/plans/` for the follow-up.
497
505
  PRETOOLUSE_HOOK_SCRIPT = "preinvocation_antigravity_context.py"
498
506
 
499
507
  def wire_hooks(
@@ -964,13 +972,19 @@ def stamp_identity(
964
972
  Emits ``AGENTS_HARNESS`` (the harness_id, e.g. ``claude-code`` -- NOT the
965
973
  short name), ``AGENTS_VENDOR``, ``AGENT`` (= AGENTS_HARNESS, aligning with
966
974
  the emerging ecosystem marker used by Goose/Amp; agentsmd/agents.md#136),
967
- and, when derivable, ``AGENTS_MODEL`` (from the adapter's vendor model var)
968
- and ``AGENTS_AGENT`` (a named persona, from $AGENTS_AGENT if already set).
975
+ and, when derivable, ``AGENTS_MODEL`` (from the adapter's vendor model var).
969
976
 
970
977
  Never emits a var it cannot source (no empty ``AGENTS_MODEL=``). Does NOT
971
978
  clobber a value already set in ``environ`` -- an explicit user/harness value
972
979
  wins. The deliberate curated mapping replaces the precursor's blanket
973
980
  ``CLAUDE_*``->``AGENTS_*`` rewrite (no ``AGENTS_CODE_SESSION_ID`` junk).
981
+
982
+ ``AGENTS_AGENT`` (a named persona) is NOT emitted and never was: the line
983
+ that claimed to "surface an already-selected persona" read the value from
984
+ the same key its own not-already-set guard required to be absent, so it was
985
+ unreachable in both directions. A persona would have to be DERIVED from
986
+ something (e.g. a ``<store>/<agent>.md``), which was never ported -- until
987
+ it is, nothing may branch on this var.
974
988
  """
975
989
  active = resolve_active_agent(environ, explicit=explicit, root=root)
976
990
 
@@ -984,7 +998,5 @@ def stamp_identity(
984
998
  _set("AGENTS_VENDOR", active.vendor)
985
999
  _set("AGENT", active.harness_id or active.name)
986
1000
  _set("AGENTS_MODEL", active.resolve_model(environ))
987
- # A named persona (~/.agents/<agent>.md); only surface one already selected.
988
- _set("AGENTS_AGENT", environ.get("AGENTS_AGENT"))
989
1001
 
990
1002
  return identity
@@ -18,8 +18,12 @@ END_MARKER = "<!-- dotagents:end -->"
18
18
  def _extract_block(
19
19
  text: str, begin_marker: str = BEGIN_MARKER, end_marker: str = END_MARKER
20
20
  ) -> str:
21
- """Return the managed block's inner text (without the marker lines) from
22
- a skeleton/template file that is itself fully marker-wrapped."""
21
+ """Return the managed block from a base-overlay/template file that is itself
22
+ fully marker-wrapped -- the inner text INCLUDING both marker lines.
23
+
24
+ The markers are part of the returned string on purpose: callers write this
25
+ straight into a target file, and the block has to stay detectable there (the
26
+ merge finds an existing block by marker presence alone)."""
23
27
  start = text.index(begin_marker)
24
28
  end = text.index(end_marker)
25
29
  return text[start : end + len(end_marker)]
@@ -1,7 +1,7 @@
1
1
  """Scope and overlay-source resolution for ``dotagents overlays``.
2
2
 
3
3
  Two orthogonal axes the ``overlays`` command needs, kept out of ``cli.py`` (which
4
- only wires args) to match ``_overlays.py`` / ``_skills.py`` / ``_sync.py``:
4
+ only wires args) to match ``_overlays.py`` / ``_skills.py``:
5
5
 
6
6
  * **Scope** -- *where installed overlays live*. ``user`` is ``<agents_dir>/`` (the
7
7
  configurable store, default ``~/.agents``); ``project`` is ``<project>/.agents/``.
@@ -51,6 +51,8 @@ from dotagents import __version__
51
51
  # Re-export shared helpers so `dotagents.cli.<name>` keeps resolving for both the
52
52
  # command modules and external importers (`dotagents._overlays`, `dotagents._scope`).
53
53
  from dotagents.cli._common import ( # noqa: F401
54
+ AGENTS_DIR_ENV,
55
+ AGENTS_DIR_ENV_LEGACY,
54
56
  BASE_PLAIN_FILES,
55
57
  BASE_ROOT,
56
58
  DotAgentsArgs,
@@ -60,6 +62,7 @@ from dotagents.cli._common import ( # noqa: F401
60
62
  _package_data_dir,
61
63
  _resolve_from,
62
64
  _run_overlay_setup,
65
+ resolve_user_store,
63
66
  )
64
67
 
65
68
  # Import each built-in command class to register it as a compiled subcommand.
@@ -128,13 +131,6 @@ CMDS_PATH_ENV = "AGENTS_CMDS_PATH"
128
131
  #: back-compat: DOTAGENTS_CMDS_PATH is deprecated, removable next release.
129
132
  CMDS_PATH_ENV_LEGACY = "DOTAGENTS_CMDS_PATH"
130
133
 
131
- #: The configurable user-scope store (D58). Discovery resolves the user scope's
132
- #: cmds dir through this, not a hardcoded ~/.agents, when set. This is the same
133
- #: var `dotagents env` emits (D79).
134
- AGENTS_DIR_ENV = "AGENTS_HOME"
135
- #: back-compat: DOTAGENTS_AGENTS_DIR is deprecated, removable next release.
136
- AGENTS_DIR_ENV_LEGACY = "DOTAGENTS_AGENTS_DIR"
137
-
138
134
 
139
135
  class Dotagents(LoggingArgs, Cli):
140
136
  """Umbrella CLI for installing and building the dotagents config."""
@@ -225,20 +221,16 @@ def _cmds_dirs() -> "list[Path]":
225
221
  (an overlay may SHIP a command; a user/project can still override it).
226
222
 
227
223
  The store location is configurable (D58/D79): the user scope resolves through
228
- `$AGENTS_HOME` (default `~/.agents`); the project scope is `<cwd>/.agents`.
224
+ `resolve_user_store()` (`$AGENTS_HOME`, legacy `$DOTAGENTS_AGENTS_DIR`, default
225
+ `~/.agents`) -- the same resolver `env`/`context` use, so every user-store
226
+ reader agrees; the project scope is `<cwd>/.agents`.
229
227
  `include_missing=True` (precursor semantics): every level's cmds dir is offered
230
228
  and the caller's `_discover_dir` skips the ones that don't exist."""
231
229
  from dotagents import _resolve, _scope
232
230
 
233
- agents_dir = (
234
- os.environ.get(AGENTS_DIR_ENV)
235
- or os.environ.get(AGENTS_DIR_ENV_LEGACY)
236
- or None
237
- )
238
- user = _scope.resolve_scope(global_scope=True, agents_dir=agents_dir)
239
231
  resolved = _resolve.get_file_paths(
240
232
  {"default": "dotagents/cmds", "overlay": "cmds"},
241
- agents_dir=user.agents_root,
233
+ agents_dir=resolve_user_store(),
242
234
  project_root=_scope.project_root_default(),
243
235
  global_scope=False,
244
236
  include_missing=True,
@@ -21,6 +21,38 @@ from duho import Cmd, LoggingArgs
21
21
 
22
22
  _extracted_dirs_cache: "dict[str, Path]" = {}
23
23
 
24
+ #: The configurable user-scope store (D58). Every reader of the user store
25
+ #: resolves it through this var (default `~/.agents`) rather than hardcoding the
26
+ #: home path -- this is the same var `dotagents env` emits (D79). Defined HERE
27
+ #: rather than in `cli/__init__` so command modules can use it without importing
28
+ #: the umbrella (import cycle); `cli/__init__` re-exports both names.
29
+ AGENTS_DIR_ENV = "AGENTS_HOME"
30
+ #: back-compat: DOTAGENTS_AGENTS_DIR is deprecated, removable next release.
31
+ AGENTS_DIR_ENV_LEGACY = "DOTAGENTS_AGENTS_DIR"
32
+
33
+
34
+ def resolve_user_store(agents_dir: "Optional[Path]" = None) -> Path:
35
+ """The USER store root, in precedence order: an explicit ``agents_dir``
36
+ (``--agents-dir``) -> ``$AGENTS_HOME`` -> the legacy
37
+ ``$DOTAGENTS_AGENTS_DIR`` -> ``~/.agents`` (D58/D79/D80).
38
+
39
+ Distinct from :meth:`DotAgentsArgs.resolve_scope`, which answers "which scope
40
+ do I install INTO" and returns ``<project>/.agents`` for the project scope.
41
+ Commands that always walk from the user store and merely *include or skip*
42
+ project-level files -- ``env`` and ``context``, whose Contract-A walk takes
43
+ the user store as ``agents_dir`` and the project root separately -- want this
44
+ instead: the store never becomes the project dir, whatever ``-g`` says.
45
+
46
+ Never logs or prints the raw env value (Leakage rule); only the resolved path
47
+ is ever reported.
48
+ """
49
+ if agents_dir:
50
+ return Path(agents_dir).expanduser()
51
+ value = os.environ.get(AGENTS_DIR_ENV) or os.environ.get(AGENTS_DIR_ENV_LEGACY)
52
+ if value:
53
+ return Path(value).expanduser()
54
+ return Path.home() / ".agents"
55
+
24
56
 
25
57
  class DotAgentsArgs(LoggingArgs, Cmd):
26
58
  """Shared ``-g/--global`` + ``--agents-dir`` fields for any command whose scope
@@ -6,7 +6,6 @@ import subprocess
6
6
  import sys
7
7
  import tempfile
8
8
  from pathlib import Path
9
- from typing import Optional
10
9
 
11
10
  from duho import Cmd, LoggingArgs
12
11
 
@@ -29,28 +28,34 @@ class BuildPyz(LoggingArgs, Cmd):
29
28
  "Shebang line to embed in the pyz."
30
29
  ("--python",)
31
30
 
32
- duho_version: str = "0.4.0"
31
+ # These two are a SECOND copy of the dependency versions declared in
32
+ # `pyproject.toml`'s `[project] dependencies`, and the two must move
33
+ # together: the zipapp bundles what the package claims to support, so a
34
+ # stale pin here ships an artifact `pip install dotagents-cli` would refuse.
35
+ # Pin the FLOOR of each declared range, not the latest patch -- the .pyz then
36
+ # exercises the minimum the metadata promises.
37
+ duho_version: str = "0.5.0"
33
38
  "Pinned duho version to vendor."
34
39
  ("--duho-version",)
35
40
 
36
- pathlib_next_version: str = "0.8.0"
41
+ pathlib_next_version: str = "0.9.0"
37
42
  "Pinned pathlib_next version to vendor."
38
43
  ("--pathlib-next-version",)
39
44
 
40
- tools_dir: Optional[Path] = None
41
- "Repo tools/ dir (required tooling) to bundle as _tools (default: autodetected)."
42
- ("--tools-dir",)
43
-
44
45
  def __call__(self) -> int:
45
46
  import zipapp
46
47
 
47
48
  # This module lives at src/dotagents/cli/build_pyz.py, so the repo root
48
49
  # is parents[3] (cli -> dotagents -> src -> repo) and the dotagents
49
50
  # package dir is parents[1].
51
+ #
52
+ # The repo's `tools/` is NOT bundled. It used to ride along as
53
+ # `dotagents/_tools` for compiled `audit`/`leak-check` wrappers that
54
+ # shelled out to it; both wrappers are gone (audit is repo CI tooling,
55
+ # leak-check is a personal command module), nothing reads `_tools`, and
56
+ # `tools/audit.py`'s own docstring says it is not shipped in the .pyz --
57
+ # which is only true now that this stopped copying it.
50
58
  repo_root = Path(__file__).resolve().parents[3]
51
- tools_src = Path(self.tools_dir) if self.tools_dir else (repo_root / "tools")
52
- if not tools_src.exists():
53
- raise SystemExit("error: repo tools/ not found at %s (pass --tools-dir)" % tools_src)
54
59
 
55
60
  with tempfile.TemporaryDirectory(prefix="dotagents-pyz-") as tmp:
56
61
  stage = Path(tmp) / "stage"
@@ -109,13 +114,6 @@ class BuildPyz(LoggingArgs, Cmd):
109
114
  )
110
115
  self._logger_.info("stamped __version__ = %s (from pyproject.toml)", version)
111
116
 
112
- tools_dest = dotagents_pkg_dest / "_tools"
113
- shutil.copytree(
114
- tools_src,
115
- tools_dest,
116
- ignore=shutil.ignore_patterns("__pycache__", "*.pyc"),
117
- )
118
-
119
117
  for path in stage.rglob("*.dist-info"):
120
118
  shutil.rmtree(path, ignore_errors=True)
121
119
  for path in stage.rglob("__pycache__"):
@@ -4,7 +4,7 @@ import sys
4
4
  from pathlib import Path
5
5
  from typing import Optional
6
6
 
7
- from duho import Cmd, LoggingArgs
7
+ from dotagents.cli._common import DotAgentsArgs, resolve_user_store
8
8
 
9
9
 
10
10
  def _write_stdout(text: str) -> None:
@@ -27,8 +27,21 @@ def _write_stdout(text: str) -> None:
27
27
  buffer.flush()
28
28
 
29
29
 
30
- class Context(LoggingArgs, Cmd):
31
- """Assemble the effective context for agents (Plan 04)."""
30
+ class Context(DotAgentsArgs):
31
+ """Assemble the effective context for agents (Plan 04).
32
+
33
+ Roots (both configurable, never hardcoded -- D58/D79/D80): the user store is
34
+ ``--agents-dir`` -> ``$AGENTS_HOME`` -> legacy ``$DOTAGENTS_AGENTS_DIR`` ->
35
+ ``~/.agents`` (:func:`~dotagents.cli._common.resolve_user_store`), and the
36
+ project root is ``$AGENTS_PROJECT_ROOT`` -> ``$CLAUDE_PROJECT_DIR`` -> the cwd
37
+ (:func:`~dotagents._scope.project_root_default`). This matters most here: the
38
+ SessionStart hook runs ``dotagents context`` from wherever the session happens
39
+ to start, so a pinned project root is the only thing that keeps the assembled
40
+ context stable across subdirectories.
41
+
42
+ ``-g/--global`` means **skip the project-level context files**, NOT "resolve a
43
+ different store" -- same narrowed meaning as ``dotagents env``'s (and unlike
44
+ ``DotAgentsArgs.resolve_scope``, which is deliberately not used here)."""
32
45
 
33
46
  _parsername_ = "context"
34
47
 
@@ -36,10 +49,17 @@ class Context(LoggingArgs, Cmd):
36
49
  "Output format: markdown, system-reminder, or json."
37
50
  ("--format",)
38
51
 
52
+ # Inherited from `DotAgentsArgs`; help restated because this command's `-g` is
53
+ # narrower than the base's and its store is always the user store (see the
54
+ # matching comment in `cli/env.py`).
39
55
  global_scope: bool = False
40
- "Use global scope."
56
+ "Skip project-level context files (the store root is unaffected)."
41
57
  ("--global", "-g")
42
58
 
59
+ agents_dir: "Optional[Path]" = None
60
+ "User store root override (default: $AGENTS_HOME, else ~/.agents)."
61
+ ("--agents-dir",)
62
+
43
63
  agents: "list[str]" = []
44
64
  "List of agents to generate context for (e.g. claude,gemini). Default: active agent."
45
65
  ("--agents",)
@@ -56,11 +76,12 @@ class Context(LoggingArgs, Cmd):
56
76
  def __call__(self) -> int:
57
77
  from dotagents import _agents
58
78
  from dotagents import _context
79
+ from dotagents import _scope
59
80
  import json
60
81
  import os
61
82
 
62
- project_root = Path.cwd()
63
- agents_dir = Path.home() / ".agents"
83
+ project_root = _scope.project_root_default()
84
+ agents_dir = resolve_user_store(self.agents_dir)
64
85
 
65
86
  agent_names = []
66
87
  if self.agents: