dotagents-cli 0.3.1__tar.gz → 0.3.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.
Files changed (73) hide show
  1. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/.gitignore +4 -0
  2. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/CHANGELOG.md +133 -2
  3. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/PKG-INFO +3 -3
  4. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/README.md +1 -1
  5. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/build.py +3 -3
  6. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/api/reference.md +0 -1
  7. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/guide/commands.md +14 -2
  8. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/install.py +2 -2
  9. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/mkdocs.yml +0 -1
  10. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/pyproject.toml +1 -1
  11. dotagents_cli-0.3.3/src/dotagents/AGENTS.md +220 -0
  12. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/__init__.py +1 -1
  13. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_agents.py +389 -14
  14. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_context.py +13 -6
  15. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_hooks.py +22 -4
  16. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_merge.py +6 -2
  17. dotagents_cli-0.3.3/src/dotagents/_overlay/dotagents/hooks/preinvocation_antigravity_context.py +123 -0
  18. dotagents_cli-0.3.3/src/dotagents/_overlay/dotagents/hooks/pretooluse_codex_env.py +90 -0
  19. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_scope.py +1 -1
  20. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/cli/__init__.py +26 -19
  21. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/cli/_common.py +74 -0
  22. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/cli/build_pyz.py +37 -14
  23. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/cli/context.py +33 -7
  24. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/cli/env.py +192 -8
  25. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/cli/init.py +3 -15
  26. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/cli/overlays.py +11 -43
  27. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/tests/test_agents.py +19 -1
  28. dotagents_cli-0.3.3/tests/test_build_pyz.py +69 -0
  29. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/tests/test_context.py +72 -4
  30. dotagents_cli-0.3.3/tests/test_env_cli_scope.py +263 -0
  31. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/tests/test_env_format.py +149 -11
  32. dotagents_cli-0.3.3/tests/test_wire_hooks.py +887 -0
  33. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/tools/audit.py +7 -5
  34. dotagents_cli-0.3.1/docs/api/sync.md +0 -3
  35. dotagents_cli-0.3.1/src/dotagents/AGENTS.md +0 -125
  36. dotagents_cli-0.3.1/src/dotagents/_sync.py +0 -72
  37. dotagents_cli-0.3.1/tests/test_wire_hooks.py +0 -439
  38. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/LICENSE +0 -0
  39. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/api/agents.md +0 -0
  40. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/api/cli.md +0 -0
  41. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/api/context.md +0 -0
  42. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/api/env.md +0 -0
  43. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/api/hooks.md +0 -0
  44. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/api/merge.md +0 -0
  45. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/api/overlays.md +0 -0
  46. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/api/resolve.md +0 -0
  47. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/api/scope.md +0 -0
  48. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/api/skills.md +0 -0
  49. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/changelog.md +0 -0
  50. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/guide/authoring.md +0 -0
  51. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/guide/install.md +0 -0
  52. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/guide/overlays.md +0 -0
  53. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/guide/private-sync.md +0 -0
  54. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/docs/index.md +0 -0
  55. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/__main__.py +0 -0
  56. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_env.py +0 -0
  57. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_overlay/AGENTS.md +0 -0
  58. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_overlay/CLAUDE.md +0 -0
  59. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_overlay/README.md +0 -0
  60. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_overlay/dotagents/DECISIONS.md +0 -0
  61. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_overlay/dotagents/cmds/README.md +0 -0
  62. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_overlays.py +0 -0
  63. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_resolve.py +0 -0
  64. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_skills.py +0 -0
  65. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/src/dotagents/_wrappers.py +0 -0
  66. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/tests/test_audit_leak.py +0 -0
  67. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/tests/test_cmds_discovery.py +0 -0
  68. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/tests/test_env.py +0 -0
  69. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/tests/test_hooks.py +0 -0
  70. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/tests/test_overlays_command.py +0 -0
  71. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/tests/test_scope.py +0 -0
  72. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/tests/test_wrappers.py +0 -0
  73. {dotagents_cli-0.3.1 → dotagents_cli-0.3.3}/tools/cloud-setup.sh +0 -0
@@ -34,3 +34,7 @@ Thumbs.db
34
34
  .idea/
35
35
  .vscode/
36
36
  _staging/
37
+
38
+ # `overlays` branch, checked out here as a worktree/clone for local editing
39
+ # (D77: example overlays split off this repo's main branch).
40
+ overlays/
@@ -7,6 +7,139 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.3] - 2026-08-16
11
+
12
+ ### Added
13
+
14
+ - feat: `env` and `context` gained **`--agents-dir`** (from the shared
15
+ `DotAgentsArgs` base) to override the store for one run. Their `-g/--global`
16
+ keeps its existing, narrower meaning here — *skip the project-level files* —
17
+ and now says so in `--help`.
18
+
19
+ ### Removed
20
+
21
+ - **`build-pyz` no longer bundles the repo's `tools/`** as `dotagents/_tools`
22
+ inside the built `.pyz`, and the `--tools-dir` flag is gone with it. Nothing
23
+ read `_tools`: the compiled `audit`/`leak-check` wrappers that shelled out to
24
+ it no longer exist. Every shipped artifact carried the dead weight while
25
+ `tools/audit.py` claimed it was "not shipped in the `.pyz`" — now true.
26
+ - **`dotagents._sync`** — a `pathlib_next.PathSyncer` wrapper that existed only
27
+ to back the `install` subcommand's backup/copy report. `install` was removed
28
+ in 0.3.x; the module has had no callers since, no tests, and a return
29
+ annotation that disagreed with what it returned. `pathlib_next` remains a
30
+ dependency (duho, and `--from` URI support). Its API-reference page went with
31
+ it.
32
+
33
+ ### Fixed
34
+
35
+ - docs: a cluster of "docs say X, tree does Y" corrections — `tools/audit.py`
36
+ no longer claims to ship as a bundled `audit` command module (it is repo CI
37
+ tooling and there is no `dotagents audit`) and its `--root` help names the
38
+ real default; `install.py`'s usage line drops the removed `install` and the
39
+ never-existing `audit`; the README's `tools/` row says where `leak-check`
40
+ actually lives; the API header names the real `_overlays` exports
41
+ (`install_overlay_dir` / `apply_overlay` / `run_overlay_setup`) and the real
42
+ package-data dirs (`_overlay` / `_overlays_src`, never a `skeleton/`); and
43
+ `_merge._extract_block`'s docstring now says it returns the block *including*
44
+ its marker lines, which is what it has always done and what callers rely on.
45
+ - fix: **`stamp_identity` no longer pretends to emit `AGENTS_AGENT`.** The line
46
+ sourced the value from `$AGENTS_AGENT` and only assigned when that same key
47
+ was unset, so it could never emit anything — while the docstring and the
48
+ shipped API header both advertised the var. Line removed; the API header's
49
+ "emitted by the identity/env layer" list now drops both `AGENTS_AGENT` and
50
+ `AGENTS_CODE_SESSION_ID` (the latter was deliberately never emitted) and says
51
+ so explicitly, so nothing branches on a var that never arrives.
52
+ - fix: **`dotagents env` and `dotagents context` now resolve their roots instead
53
+ of hardcoding them.** Both took `Path.cwd()` as the project root and
54
+ `~/.agents` as the user store, so `$AGENTS_PROJECT_ROOT` (or the agent-native
55
+ `$CLAUDE_PROJECT_DIR`) and `$AGENTS_HOME` were ignored by the two commands
56
+ that most needed them — a `SessionStart` hook runs `dotagents context` from
57
+ wherever the session happens to start, so a pinned project root was silently
58
+ dropped and a relocated store was never read. They now use
59
+ `_scope.project_root_default()` and the new
60
+ `dotagents.cli.resolve_user_store()` (`--agents-dir` → `$AGENTS_HOME` →
61
+ legacy `$DOTAGENTS_AGENTS_DIR` → `~/.agents`), matching what the package's own
62
+ `resolve_scope` docstring and command discovery already promised. Behavior is
63
+ unchanged when none of the vars are set.
64
+
65
+ ## [0.3.2] - 2026-07-25
66
+
67
+ ### Added
68
+
69
+ - feat: **`AntigravityAgent`** — context injection for Google's Antigravity
70
+ CLI/IDE (a separate product from Gemini CLI, despite sharing the `~/.gemini/`
71
+ namespace for some files). Antigravity's hooks have no `SessionStart`
72
+ equivalent — only `PreToolUse`/`PostToolUse`/`PreInvocation`/`PostInvocation`/
73
+ `Stop` — so a `PreInvocation` hook gated on `invocationNum == 0` behaves like a
74
+ one-shot context load instead of resending it every model turn. Wires into
75
+ `~/.gemini/config/hooks.json`. No detection marker exists for Antigravity, so
76
+ it's explicit-`--agents antigravity`-only, never auto-detected.
77
+ - feat: **Codex gets a `PreToolUse` env hook**, closing the one gap Codex had
78
+ versus Claude: Codex has no per-session env-persistence mechanism at any hook
79
+ event, so a deployed script prepends a guarded env-loader to every `Bash` tool
80
+ call via `updatedInput.command` — the same rewrite mechanism Claude's own
81
+ `PreToolUse` hook uses, confirmed directly against Codex's docs.
82
+ - feat: **`init` wires a PowerShell `PreToolUse` env hook on Windows**, closing
83
+ a real gap: `$CLAUDE_ENV_FILE` only reaches Claude's *Bash* tool
84
+ (`$env:CLAUDE_ENV_FILE` is empty inside a live PowerShell tool call, confirmed
85
+ directly) — a fresh PowerShell tool call gets none of the SessionStart env.
86
+ The hook prepends a guarded env-loader to a PowerShell tool call's own command,
87
+ shipped as an inline command string (never a `.ps1` file — a script file is
88
+ subject to PowerShell's execution policy, and dotagents has no code-signing
89
+ certificate; the inline form runs even under `Restricted`).
90
+ - feat: **`SessionStart`/`CwdChanged` now register two handlers each** — a
91
+ bash-syntax one and an explicit `shell: "powershell"` one — because Claude's
92
+ own hooks.md says the shell "defaults to bash, or to powershell on Windows
93
+ when Git Bash isn't installed": bash syntax fed to `powershell -Command` on
94
+ such a machine is a hard parse error, silently losing both env and context for
95
+ the whole session.
96
+ - feat: **`DotAgentsArgs`** (`dotagents.cli`, re-exported for overlay-shipped
97
+ commands) — one shared `-g/--global` + `--agents-dir` base class. `init` and
98
+ all four `overlays` subcommands now inherit it instead of independently
99
+ redeclaring the same fields, so scope resolution can't silently drift between
100
+ commands (one previously defaulted `agents_dir` eagerly to `Path.home() /
101
+ ".agents"`; harmless in practice, but needless).
102
+
103
+ ### Fixed
104
+
105
+ - fix: **`dotagents env --format powershell`/`cmd` left an already-POSIX `PATH`
106
+ unconverted**, the mirror of the export/dotenv/fish fix below going the other
107
+ direction. Found live: run from a genuine Windows PowerShell terminal whose
108
+ own inherited `PATH` already held WSL/MSYS-mount-style entries
109
+ (`/mnt/c/Program Files/...`), the emitted `${env:PATH} = '...'` was
110
+ syntactically valid PowerShell but a single opaque colon-joined string, not
111
+ the `;`-split list PowerShell's own PATH lookup needs — every subsequent
112
+ bare-command lookup broke for that session. Also handles the MIXED case
113
+ (dotagents' own native bin dirs prepended onto an already-POSIX inherited
114
+ PATH, one string with both separators at once), caught by sourcing real
115
+ output into a live PowerShell session and watching `git.exe` fail to
116
+ resolve before the second fix. A WSL-only segment with no Windows equivalent
117
+ (`/usr/bin`) is dropped rather than mangled into a broken relative path.
118
+ - fix: **the built `.pyz` degraded `-g` on any discovered command inheriting a
119
+ dotagents-defined base class** (first hit by `DotAgentsArgs` above) — duho's
120
+ AST introspection walks the full MRO for a command's flags, but the zipapp
121
+ source-repoint shim only ever covered built-in command modules, not a base
122
+ class's own module (`dotagents.cli._common`). `--global` degraded to the
123
+ name-derived `--global-scope` and `-g` vanished silently. Fixed, with new CI
124
+ coverage asserting the exact short flag survives a real built pyz.
125
+ - fix: **`harness_loads` relative entries matched by bare filename, not full
126
+ path** — a relative entry like Codex's `"AGENTS.md"` wrongly suppressed ANY
127
+ file sharing that basename anywhere on disk, including the unrelated
128
+ `~/.agents/AGENTS.md` user-store file Codex's harness never reads.
129
+ `dotagents context --agents codex` emitted an empty `sources: []` even with
130
+ real content present. Now resolved against `project_root` and compared by
131
+ full path, like the absolute (`~/`, `/`) forms already were.
132
+ - fix: `dotagents context --format json` crashed with `UnicodeEncodeError` on
133
+ any character outside Latin-1 (a bare `print()` encoding with the console's
134
+ codepage) — the same class of bug already fixed for the markdown path, just
135
+ never covered for JSON.
136
+ - fix: PowerShell format uses `${env:NAME}` (curly-brace form), not the bare
137
+ `$env:NAME` sigil — a handful of real Windows env vars have parens in their
138
+ names (`ProgramFiles(x86)`), and `$env:FOO(X86) = ...` is a PowerShell parse
139
+ error; the curly-brace form is valid for every name.
140
+
141
+ ## [0.3.1] - 2026-07-24
142
+
10
143
  ### Fixed
11
144
 
12
145
  - fix: **`dotagents env --format export`/`dotenv`/`fish` now emit a POSIX PATH on
@@ -27,8 +160,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
27
160
  leading slash (`/server/share/...`) during the POSIX conversion above — MSYS
28
161
  requires the double-slash UNC root (`//server/share/...`) to resolve it.
29
162
 
30
- ## [0.3.1] - 2026-07-24
31
-
32
163
  Patch release: the PATH/POSIX-conversion fix above (the only change since 0.3.0).
33
164
 
34
165
  ## [0.3.0] - 2026-07-24
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: dotagents-cli
3
- Version: 0.3.1
3
+ Version: 0.3.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
@@ -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.1"
12
+ version = "0.3.3"
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"
@@ -0,0 +1,220 @@
1
+ # dotagents — package API header
2
+
3
+ Public API of the installed `dotagents` package: the CLI umbrella plus the `_*.py`
4
+ helper modules that back it. This file ships inside the package so a consuming agent
5
+ can read it without the source. Full docs: https://jose-pr.github.io/dotagents/
6
+
7
+ ## Entry points
8
+
9
+ - `dotagents.cli.main(argv=None) -> int` — the `install.py` shim and
10
+ `python -m dotagents` entry point. Repoints zipapp sources, then dispatches through
11
+ `duho.app` with the discovered command set. Also the `dotagents` console script
12
+ (`[project.scripts]`).
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`.
22
+ - Compiled command classes live in `dotagents.cli.<name>` (`init`, `overlays`,
23
+ `context`, `env`, `build_pyz`); each is a `class X(LoggingArgs, Cmd)` — or
24
+ `class X(DotAgentsArgs)`, which is that pair transitively — with a
25
+ `__call__`. That is the WHOLE shipped surface — dotagents bundles no command
26
+ module of its own. `link` / `sync` left the package with their logic (D85): the
27
+ opt-in **private-sync** overlay ships them, renamed `link-project` /
28
+ `sync-project`, from its own `cmds/` + `lib/_link.py`. `leak-check` is likewise
29
+ not in the repo — a personal command module the user keeps in their private
30
+ `<scope>/dotagents/cmds/` (D84). `audit` is repo CI tooling (`tools/audit.py`),
31
+ not a command.
32
+ - Command discovery layers sources, later wins: built-ins < bundled `cmds` <
33
+ overlay `cmds` (`<overlay-root>/cmds`) < scope `cmds` dirs (user + project) <
34
+ `$AGENTS_CMDS_PATH` < `--cmdspath`. The overlay + scope tiers come from one
35
+ Contract-A `get_file_paths` walk (`cli._cmds_dirs`), the same resolver that
36
+ backs `bin`/PATH.
37
+
38
+ ## Helper modules (public surface)
39
+
40
+ - `_agents` — `Agent` base type + per-agent adapters; `stamp_identity(...)` emits the
41
+ standardized `AGENTS_*` / `AGENT` identity vars.
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`
45
+ contributions to the managed `AGENTS.md` block. `DEFAULT_PRIORITY = 500`.
46
+ - `_scope` — `resolve_scope(global_scope, agents_dir=None)` and `resolve_source(...)`;
47
+ scope = *where installed overlays live* (user = the configurable store, project =
48
+ `<project>/.agents`), source = *where an overlay comes from* (bundled by default).
49
+ Installed overlays are **discovered** by presence, not tracked in a registry.
50
+ - `_context` — assemble the effective per-agent context (Plan 04); reads overlay
51
+ `priority` from the manifest (lower sorts earlier).
52
+ - `_env` — chained env-file assembly + `env.py` execution (frozen contract B):
53
+ `get_environment` / `get_diff` / `resolve_env_files` / `get_env_from_py` /
54
+ `get_env_from_file`. Bins onto PATH first, then two tiers (`pre.env*` then `env*`),
55
+ later-overrides-earlier. Identity seeded before the chain; proxy vars applied after.
56
+ - `_resolve` — `get_file_paths(*names, agents_dir, project_root, global_scope=False,
57
+ include_missing=False)`: the Contract-A precedence walk / filename resolution.
58
+ - `_merge` — managed-block merge for `init`'s `AGENTS.md` / `CLAUDE.md`, delimited by
59
+ `<!-- dotagents:begin -->` / `<!-- dotagents:end -->`. Detection is by marker
60
+ presence only, so it survives user reformatting. `begin_marker`/`end_marker`
61
+ override the pair for other comment syntaxes (`#` for TOML), and `append=True`
62
+ puts a first-time block at the END of the file — required for TOML, where a
63
+ `[table]` header captures every key line after it and a prepended block would
64
+ swallow the user's top-level keys.
65
+ - `_skills` — publish an overlay's `skills/<name>/` into a scope's shared skills dir
66
+ (symlink-preferred, copy fallback); unpublish removes only what the overlay
67
+ published, then sweeps broken symlinks. Pure stdlib.
68
+ - `_hooks` — additive, idempotent merge of our hooks into an agent's `settings.json`.
69
+ `hooks.<Event>` is a **list of matcher-objects** each holding its own `hooks` list,
70
+ not a flat command list. Foreign hooks are preserved verbatim, malformed entries
71
+ are dropped rather than raising, and invalid JSON raises `SystemExit` instead of
72
+ silently overwriting the user's file. `shell` (Claude: `"bash"`/`"powershell"`,
73
+ picks the interpreter for the hook's own command) and `command_windows`
74
+ (Codex: emitted as `commandWindows`, a separate Windows-only command OVERRIDE,
75
+ not an interpreter choice) are both supported. Pure stdlib. Consumed by
76
+ `ClaudeAgent.wire_hooks` (`~/.claude/settings.json`: env via `$CLAUDE_ENV_FILE`
77
+ + context via stdout, plus `CwdChanged`, plus a `PreToolUse` env-loader for the
78
+ PowerShell tool) and `CodexAgent.wire_hooks` (`<CODEX_HOME|~/.codex>/hooks.json`,
79
+ never `config.toml`: `SessionStart` context-only, plus a `PreToolUse` env-loader
80
+ matched on `matcher: "Bash"`). Codex's hook JSON is structurally identical to
81
+ Claude's, including the SAME `updatedInput.command` rewrite mechanism on
82
+ `PreToolUse` — confirmed directly against Codex's own docs
83
+ (learn.chatgpt.com/docs/hooks), not assumed from Claude parity.
84
+ Gemini/Cursor/Copilot keep the base no-op — Gemini CLI proper has no hook
85
+ mechanism documented at all (checked directly). **Antigravity is a separate
86
+ product** from Gemini CLI (Antigravity CLI/IDE/SDK family, shares only the
87
+ `~/.gemini/` namespace for some files) and DOES wire a hook: see
88
+ `AntigravityAgent.wire_hooks` below. `PreToolUse` there is still allow/deny/ask
89
+ only — no `updatedInput`, so no command-rewrite/env-loader path exists to hang
90
+ on it, unlike Claude/Codex. Revisit both conclusions if either framework changes.
91
+ - **`AntigravityAgent.wire_hooks`** (`<AGENTS_HOME_ANTIGRAVITY|~/.gemini/config>/hooks.json`,
92
+ keyed under a `"dotagents"` name per the docs' own example shape — a named-entry
93
+ object, not Claude/Codex's flat `hooks.<Event>`): wires a single `PreInvocation`
94
+ entry, context-only, no env mechanism. Antigravity's hooks
95
+ (antigravity.google/docs/hooks) have exactly five events — `PreToolUse`,
96
+ `PostToolUse`, `PreInvocation`, `PostInvocation`, `Stop` — no SessionStart
97
+ equivalent. `PreInvocation` fires every model turn (`invocationNum`, 0-indexed),
98
+ so the deployed script (`preinvocation_antigravity_context.py`,
99
+ `_overlay/dotagents/hooks/`) gates on `invocationNum == 0` to behave like a
100
+ one-shot SessionStart rather than resending context every turn. Output shape is
101
+ a bare `{"injectSteps": [{"ephemeralMessage": "..."}]}`, no `hookSpecificOutput`
102
+ wrapper — confirmed against the primary docs after an earlier pass here wrongly
103
+ concluded no useful injection was possible; `ephemeralMessage` is the one of the
104
+ three step types meant for free text (`toolCall` executes a tool,
105
+ `userMessage` impersonates the user). No detection marker exists anywhere in
106
+ Antigravity's docs, so `detect_env_vars = []` — explicit `--agents antigravity`
107
+ only, same posture as Codex's env-block precedent (writes touching an agent's
108
+ own live config are opt-in, never inferred).
109
+ - **`SessionStart`/`CwdChanged` register TWO handlers each**, bash-syntax
110
+ (default shell) and a PowerShell-native equivalent (`shell: "powershell"`).
111
+ hooks.md: `shell` "Defaults to bash, or to powershell on Windows when Git Bash
112
+ isn't installed" — verified directly that bash syntax fed to `powershell
113
+ -Command` on such a machine is a hard parse error, not a soft failure, so
114
+ every session there would silently get neither env nor context. Every handler
115
+ in a matched group fires unconditionally (hooks.md), so both always run; the
116
+ one whose interpreter is absent fails harmlessly. The PowerShell
117
+ `SessionStart` variant is context-only (`dotagents context`), not
118
+ env+context: `$CLAUDE_ENV_FILE`'s documented effect is "subsequent BASH
119
+ commands" regardless of which shell wrote it, so writing to it from a
120
+ PowerShell-shelled hook would feed nothing.
121
+ - **Windows only**: `ClaudeAgent._wire_powershell_pretooluse` additionally wires
122
+ a no-matcher `PreToolUse` hook (fires on every tool call), `shell:
123
+ "powershell"`, running `PRETOOLUSE_POWERSHELL_COMMAND` INLINE — deliberately
124
+ not a `.ps1` file, since a script file is subject to PowerShell's execution
125
+ policy (RemoteSigned/AllSigned/Restricted) and dotagents has no code-signing
126
+ certificate; verified directly that the inline form runs successfully even
127
+ under `Restricted`, which blocks every `.ps1` file outright. Closes a real
128
+ gap: `$CLAUDE_ENV_FILE` is Bash-tool-only (confirmed empirically that
129
+ `$env:CLAUDE_ENV_FILE` is empty inside a live PowerShell tool call), so the
130
+ SessionStart env half never reaches the PowerShell tool. Uses `PreToolUse`'s
131
+ `updatedInput` to prepend a guarded env-loader (`AGENTS_RUNTIME_SET`,
132
+ matching the precursor's convention) to a `PowerShell` tool call's own
133
+ command — not by trying to persist state across hook invocations, which are
134
+ each their own fresh process and cannot. Whether the guard is visible across
135
+ separate PowerShell tool calls is UNVERIFIED. Every literal `\` in the
136
+ command constant must be a raw string — a bare `\b` in a normal Python string
137
+ literal silently becomes a backspace character, corrupting the emitted path;
138
+ caught once by testing a draft through a real PowerShell spawn.
139
+ - **`CodexAgent._deploy_pretooluse_script`** covers the same env gap for Codex,
140
+ which has NO env-persistence mechanism at any hook event (not Bash-only like
141
+ Claude — none). Ships `pretooluse_codex_env.py` (`_overlay/dotagents/hooks/`),
142
+ deployed to `<codex-home>/hooks/` (create-or-refresh), wired as `PreToolUse`
143
+ with `matcher: "Bash"` — Codex's one shell tool, so (unlike Claude's
144
+ no-matcher hook) filtering happens at the settings level, no runtime
145
+ `tool_name` check needed in the script. A FILE, not inlined like Claude's:
146
+ Codex's docs show every hook example as `python3 <path>`, and a `.py` file
147
+ has no execution-policy/signing concern (PowerShell-specific). Sets
148
+ `commandWindows` to `python "<path>"` (not `python3`) — verified directly
149
+ that `python3` resolves to the Microsoft Store app-execution-alias stub and
150
+ fails outright on this dev machine (exit 49), the same trap noted elsewhere
151
+ for `py`/venv creation.
152
+
153
+ ## Environment variables
154
+
155
+ The prefix split (D80): **`AGENTS_*`** names everything about the `.agents` / agent
156
+ world (paths, scope, overlays, sync) — non-secret, safe to emit; **`DOTAGENTS_*`** is
157
+ reserved for genuinely tool-internal config and secrets, so the "never print
158
+ `DOTAGENTS_*` values" leak guard (D48) stays a simple blanket ban over exactly the
159
+ sensitive set.
160
+
161
+ Config / path / sync vars (`AGENTS_*`, non-secret — read, and some emitted):
162
+
163
+ - `AGENTS_HOME` — the configurable user-scope store path (default `~/.agents`). Also
164
+ **emitted** by `dotagents env` (D79) and set for overlay setup scripts / sync hooks.
165
+ - `AGENTS_STORE_DIR` — per-project store location (absolute paths allowed).
166
+ - `AGENTS_OVERLAYS_SRC` — default overlay source dir for `overlays`.
167
+ - `AGENTS_CMDS_PATH` — extra command-module search paths (os.pathsep-split).
168
+ - `AGENTS_OVERLAY_DIR` — set for an overlay's setup script (its own installed dir).
169
+ - `AGENTS_REMOTE` / `AGENTS_SYNC_MESSAGE` — private-store sync (tokenless remote URL /
170
+ commit message).
171
+
172
+ Every reader above prefers the `AGENTS_*` name and falls back to the old
173
+ `DOTAGENTS_*` name (`DOTAGENTS_AGENTS_DIR`, `DOTAGENTS_STORE_DIR`,
174
+ `DOTAGENTS_OVERLAYS_SRC`, `DOTAGENTS_CMDS_PATH`, `DOTAGENTS_OVERLAY_DIR`,
175
+ `DOTAGENTS_AGENTS_REMOTE`, `DOTAGENTS_SYNC_MESSAGE`) for one release — deprecated,
176
+ removable next. Setters emit both names this release.
177
+
178
+ Tool-internal / secret vars (`DOTAGENTS_*` — kept; read, **never printed**):
179
+
180
+ - `DOTAGENTS_AGENTS_TOKEN` — **secret** (fine-grained PAT) for private-store auth.
181
+ - `DOTAGENTS_CLI_INSTALL` — pip spec to install the CLI itself (tool-specific).
182
+ - `DOTAGENTS_AUDIT_PATTERNS` — path to the machine-local audit-pattern file (tooling).
183
+
184
+ Emitted by the identity/env layer (safe to branch on in env files):
185
+ `AGENTS_HARNESS`, `AGENTS_VENDOR`, `AGENTS_MODEL`, `AGENT`, `AGENTS_PROXY`,
186
+ `AGENTS_WEBFETCH_PROXY_URL`, plus the two
187
+ scope roots — `AGENTS_HOME` (the user store, `agents_dir`/`~/.agents`) and
188
+ `AGENTS_PROJECT_ROOT` (this project's root). Both are seeded only if unset, so a
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
193
+ agent-native `CLAUDE_PROJECT_DIR`, then cwd) for the project scope's root.
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
+
204
+ ## Gotchas
205
+
206
+ - **Python 3.9 floor.** Files using bare `X | Y` unions in runtime-evaluated positions
207
+ need `from __future__ import annotations`. `Path.write_text(..., newline=...)` is
208
+ 3.10+, so wrapper-script writers use `open(path, "w", newline="")`.
209
+ - **Zipapp source shim.** Inside a `.pyz`, `Path(__file__)` is not a real file, so
210
+ duho's AST flag/help introspection degrades (`--from` → `--from-`, positionals lost).
211
+ `cli.main()` calls `_repoint_zipapp_sources()` first, extracting the built-in command
212
+ modules to real temp files. Discovered `cmds` modules are extracted by
213
+ `_package_data_dir` before import, so they need no repoint.
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).
219
+ - **`pathlib_next` needs `typing_extensions` on Python < 3.10** (an upstream gap); a
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.2.0"
3
+ __version__ = "0.3.3"