dotagents-cli 0.3.4__tar.gz → 0.4.0__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 (83) hide show
  1. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/CHANGELOG.md +339 -6
  2. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/PKG-INFO +6 -6
  3. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/README.md +5 -5
  4. dotagents_cli-0.4.0/docs/api/fs.md +3 -0
  5. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/api/reference.md +4 -3
  6. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/guide/commands.md +93 -24
  7. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/guide/install.md +1 -1
  8. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/guide/overlays.md +28 -11
  9. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/index.md +1 -1
  10. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/mkdocs.yml +1 -1
  11. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/pyproject.toml +1 -1
  12. dotagents_cli-0.4.0/src/dotagents/AGENTS.md +364 -0
  13. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/__init__.py +1 -1
  14. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/_agents.py +251 -94
  15. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/_context.py +133 -133
  16. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/_env.py +212 -74
  17. dotagents_cli-0.4.0/src/dotagents/_fs.py +43 -0
  18. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/_hooks.py +58 -24
  19. dotagents_cli-0.4.0/src/dotagents/_merge.py +222 -0
  20. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/_overlay/AGENTS.md +5 -3
  21. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/_overlay/README.md +16 -9
  22. dotagents_cli-0.4.0/src/dotagents/_overlay/dotagents/DECISIONS.md +24 -0
  23. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/_overlay/dotagents/cmds/README.md +12 -9
  24. dotagents_cli-0.4.0/src/dotagents/_overlay/dotagents/cmds/findings.py +579 -0
  25. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/_overlay/dotagents/hooks/preinvocation_antigravity_context.py +34 -5
  26. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/_overlay/dotagents/hooks/pretooluse_codex_env.py +8 -2
  27. dotagents_cli-0.4.0/src/dotagents/_overlays.py +707 -0
  28. dotagents_cli-0.4.0/src/dotagents/_scope.py +445 -0
  29. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/_skills.py +37 -16
  30. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/cli/__init__.py +84 -44
  31. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/cli/_common.py +139 -75
  32. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/cli/build_pyz.py +13 -5
  33. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/cli/context.py +43 -33
  34. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/cli/env.py +109 -31
  35. dotagents_cli-0.4.0/src/dotagents/cli/overlays.py +531 -0
  36. dotagents_cli-0.4.0/tests/_shell.py +50 -0
  37. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_agents.py +3 -1
  38. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_audit_leak.py +13 -6
  39. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_cmds_discovery.py +50 -16
  40. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_context.py +107 -31
  41. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_env.py +190 -18
  42. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_env_format.py +140 -10
  43. dotagents_cli-0.4.0/tests/test_env_safety.py +203 -0
  44. dotagents_cli-0.4.0/tests/test_findings_cmd.py +280 -0
  45. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_hooks.py +27 -0
  46. dotagents_cli-0.4.0/tests/test_last_mile.py +256 -0
  47. dotagents_cli-0.4.0/tests/test_overlays_cli.py +358 -0
  48. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_overlays_command.py +157 -16
  49. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_scope.py +7 -25
  50. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_wire_hooks.py +116 -11
  51. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tools/audit.py +34 -23
  52. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tools/cloud-setup.sh +10 -6
  53. dotagents_cli-0.3.4/docs/api/resolve.md +0 -3
  54. dotagents_cli-0.3.4/src/dotagents/AGENTS.md +0 -220
  55. dotagents_cli-0.3.4/src/dotagents/_merge.py +0 -126
  56. dotagents_cli-0.3.4/src/dotagents/_overlay/dotagents/DECISIONS.md +0 -20
  57. dotagents_cli-0.3.4/src/dotagents/_overlays.py +0 -412
  58. dotagents_cli-0.3.4/src/dotagents/_resolve.py +0 -81
  59. dotagents_cli-0.3.4/src/dotagents/_scope.py +0 -265
  60. dotagents_cli-0.3.4/src/dotagents/cli/overlays.py +0 -334
  61. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/.gitignore +0 -0
  62. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/LICENSE +0 -0
  63. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/build.py +0 -0
  64. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/api/agents.md +0 -0
  65. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/api/cli.md +0 -0
  66. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/api/context.md +0 -0
  67. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/api/env.md +0 -0
  68. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/api/hooks.md +0 -0
  69. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/api/merge.md +0 -0
  70. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/api/overlays.md +0 -0
  71. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/api/scope.md +0 -0
  72. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/api/skills.md +0 -0
  73. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/changelog.md +0 -0
  74. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/guide/authoring.md +0 -0
  75. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/docs/guide/private-sync.md +0 -0
  76. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/install.py +0 -0
  77. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/__main__.py +0 -0
  78. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/_overlay/CLAUDE.md +0 -0
  79. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/_wrappers.py +0 -0
  80. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/src/dotagents/cli/init.py +0 -0
  81. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_build_pyz.py +0 -0
  82. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_env_cli_scope.py +0 -0
  83. {dotagents_cli-0.3.4 → dotagents_cli-0.4.0}/tests/test_wrappers.py +0 -0
@@ -7,6 +7,339 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] - 2026-09-09
11
+
12
+ ### Security
13
+
14
+ - `env --format export` (the form the SessionStart hook writes into
15
+ `$CLAUDE_ENV_FILE`) now single-quotes every value (`'` → `'\''`, control
16
+ characters via bash's `$'...'`). Values were JSON-quoted, i.e. inside DOUBLE
17
+ quotes, so `$(...)`, backticks and `$VAR` in any env value were executed or
18
+ expanded when the file was sourced, while `\n` and non-ASCII (`\u00e9`)
19
+ arrived as literal escape text.
20
+ - The env chain no longer executes or sources a project's own top-level
21
+ `env.py` / `env`: only `<project>/.agents/*` and the user-local
22
+ `local.env` / `pre.local.env` run at the project levels. Every session start
23
+ ran the chain, so opening a session in a cloned repository with a top-level
24
+ `env.py` was code execution from that checkout.
25
+
26
+ ### Added
27
+
28
+ - feat: **`dotagents findings`** — a per-scope findings queue, shipped as the
29
+ one bundled command module (`_overlay/dotagents/cmds/findings.py`). A
30
+ finding is one markdown file shaped like an agent memory (frontmatter
31
+ `name`/`description`/`status`/`created` for the index line, details in the
32
+ body) under `<scope-root>/findings/`: the project's `.agents/findings/` by
33
+ default, the user store's with `-g`, anywhere with `--dir`. `add` records
34
+ one (`--body`/`--body-file`, `-` = stdin), `list` prints the active ones
35
+ (`--all`, `--processed`, `--json`), `show` prints one (`--json`), `done`
36
+ appends a required `## Resolution` and MOVES the file to `processed/`
37
+ (never deletes), `reopen` moves it back, `remove` deletes a mistaken one,
38
+ `index` regenerates `INDEX.md` (active first, then processed — every
39
+ mutating command rewrites it), `path` prints the queue's location.
40
+ Hand-written notes without a frontmatter are listed too and gain one when
41
+ first rewritten. Files are written LF-only on every platform.
42
+ - chore: `init` no longer copies bundled command modules (`*.py`) from the
43
+ package's `dotagents/cmds/` into the store — only the README. The bundled
44
+ dir is always a discovery source, so a copy added nothing, and being
45
+ create-if-absent it would have pinned the first-installed version of
46
+ `findings` and shadowed every later one. A same-named module dropped into a
47
+ scope's `dotagents/cmds/` still overrides the bundled one.
48
+ - feat: `env` prepends every level's existing **`lib/`** dir to `PYTHONPATH`,
49
+ the way it already prepends every level's `bin/` to `PATH` — overlays first,
50
+ then the store, then the project's `.agents/`, never the project root — and
51
+ does so before the env-file chain, so an `env.py` (and every subprocess that
52
+ inherits the env) can `import` an overlay's `lib/` module. Only dirs that
53
+ exist are added, and `PYTHONPATH` is untouched when there are none. The
54
+ formatter already converts any `*PATH` variable between Windows and POSIX
55
+ forms, so `PYTHONPATH` gets the same treatment as `PATH`.
56
+ - feat: `env` emits one **`<NAME>_OVERLAY_ROOT`** per installed overlay (the
57
+ overlay's install dir), seeded before the env-file chain like the two scope
58
+ roots and, like them, only if unset. `NAME` is the overlay's directory name
59
+ upper-cased with every non-alphanumeric character turned into `_`
60
+ (`my-ov.v2` → `MY_OV_V2_OVERLAY_ROOT`). This is the same name `context`
61
+ already expanded as a `<NAME_OVERLAY_ROOT>` placeholder; the two now share
62
+ one naming function, so an env file and a context file name an overlay's
63
+ install dir identically. The variable is derived from the same normalized
64
+ name `overlays add` installs under (`Overlay.normalize_name`), so the var
65
+ for `overlays/<n>/` is always `Overlay.root_var_for(n)`.
66
+
67
+ ### Changed
68
+
69
+ - The overlay-root note `overlays add` writes above routing lines cites
70
+ `$ENGINEERING_OVERLAY_ROOT` as its example; the docs and CI install the
71
+ `engineering` overlay, which now carries the flows, references, and tools that
72
+ were separate overlays (each is a category dir under it, never `<name>/<name>/`).
73
+ - The contract-A walk is `Scope.paths(*names, include_missing=False)`; the
74
+ `_resolve` module (and its API docs page) is gone, its `LEVEL_NAMES` live in
75
+ `_scope`.
76
+ - The system store is a store like the others. `Scope.system_root`
77
+ (`/etc/agents`, or `$AGENTS_SYSTEM_ROOT`) heads `Scope.stores`, and the
78
+ contract-A walk goes store by store -- system, user, project -- each store's
79
+ overlays first, then the store itself, then the project root; the hardcoded
80
+ `/etc/agents` tier and its odd position (after the user store's overlays,
81
+ before the user store) are gone. A system overlay is shadowed by a same-named
82
+ user or project one. `overlays list` prints one `installed (<level>)` block
83
+ per store that has anything, most specific first.
84
+ - The library walk takes one object. `Scope` now carries the whole picture
85
+ (`user_root`, `project_root`, `stores`, `overlays`, `files(*names)`), and
86
+ `get_file_paths`, `get_environment`, `get_diff`, `resolve_env_files`,
87
+ `get_bin_paths`, `get_lib_paths`, `get_overlay_roots`, `assemble_context` and
88
+ `assemble_context_data` take a `Scope` instead of the
89
+ `(agents_dir, project_root, global_scope)` keyword triple ported from the
90
+ precursor. `Scope.of(agents_dir=, project_root=, global_scope=)` builds one
91
+ from resolved parts. No compatibility form: the CLI is the public surface.
92
+ - **One overlay name, two scopes = one overlay, the project's.** A project
93
+ session uses the user store's overlays AND the project's (`<project>/.agents/
94
+ overlays/`); an overlay installed in both under the same name is the
95
+ project's copy only, which shadows the store's for bin, lib, env files,
96
+ cmds, `CONTEXT.md` and the `<NAME>_OVERLAY_ROOT` var (a store root the
97
+ session had already pinned is re-pointed). `-g` sees the user store alone.
98
+ `Overlay.installed(*stores)` is the one discovery behind the contract-A
99
+ walk, `env`, `context` and `overlays list` / `show`; the `_scope`
100
+ `discover_overlays` pass-through is gone (`Overlay.discover(root)` is the
101
+ single-root form). `overlays list` in a project scope prints both
102
+ `installed (project)` and `installed (user)`, marking shadowed store copies;
103
+ `overlays show` falls back to the user store's copy before the source.
104
+ - Overlay routing lines refer to overlay files through `$<NAME>_OVERLAY_ROOT`
105
+ (the variable `env` exports per installed overlay), not a hard `~/.agents/`
106
+ path; when a merged block carries such lines, `AGENTS.md` gets one line
107
+ above them saying what the token is and how to resolve it. The example
108
+ overlays on the `overlays` branch follow this convention and ship no setup
109
+ scripts any more (PATH, PYTHONPATH and the root var are `env`'s job; an
110
+ overlay's own env belongs in its `env.py`). `tools/cloud-setup.sh` finds
111
+ the private-sync settings snippet inside the installed overlay.
112
+ - The base overlay's findings workflow now goes through the command. The
113
+ managed `AGENTS.md` rule ("Global-config misses"), `dotagents/DECISIONS.md`
114
+ ("How findings become config") and the skeleton README say
115
+ `dotagents findings add -g ...` to record a miss, `list -g` / `show -g` to
116
+ triage and `done -g <name> -r ...` to close one, instead of "drop a note in
117
+ `~/.agents/dotagents/findings/`". With that, the user store's queue is
118
+ **`~/.agents/findings/`** (the command's `-g` default), no longer
119
+ `~/.agents/dotagents/findings/`. An existing queue at the old path keeps
120
+ working with `--dir ~/.agents/dotagents/findings`, or move it once:
121
+ `mv ~/.agents/dotagents/findings ~/.agents/findings`. Re-run `dotagents init`
122
+ to refresh the managed block.
123
+ - refactor: `dotagents._overlays` is now built around an **`Overlay`** class —
124
+ one overlay is one directory, and everything that depends on a single
125
+ overlay is a method or property on it: `.path` / `.name` / `.normalized_name`
126
+ / `.root_var` / `.is_valid` / `.manifest_path`, `.read_manifest()` /
127
+ `.priority` / `.sort_key`, `.find_setup_script()` / `.run_setup(...)`,
128
+ `.files()` / `.rule_blocks(...)` / `.apply_to(...)` / `.install_to(...)` /
129
+ `.merge_rules_into(...)`. The name rules are static methods
130
+ (`Overlay.is_valid_name` / `.normalize_name` / `.root_var_for`), so a caller
131
+ holding only a name uses the same rule as one holding a directory;
132
+ `Overlay.discover(root)` is the single discovery routine that `_scope`,
133
+ the contract-A resolver and `env` all share, and `Overlay.sort_by_priority`
134
+ the single merge order. The module-level functions they replace
135
+ (`read_manifest`, `find_setup_script`, `run_overlay_setup`, `overlay_files`,
136
+ `rule_blocks`, `apply_overlay`, `install_overlay_dir`, `merge_overlay_rules`,
137
+ `overlay_sort_key`, `sort_overlays_by_priority`, `normalize_name`,
138
+ `overlay_root_var`) are gone, as are `_scope.is_valid_overlay_name` and
139
+ `_scope.normalize_overlay_name`. `recompose_overlay_block` stays a module
140
+ function (it works over a set of overlays) and now accepts `Overlay`
141
+ instances or directories. `_overlays` is a private module, so no public
142
+ version signal.
143
+
144
+ - fix: an `env.py` may now print its changes as one JSON object **per line**,
145
+ merged in order (a later line wins), as well as a single object. Each
146
+ overlay's `setup.py` appends its own managed block to the store's `env.py`
147
+ and each block prints its own object, so a store with two such overlays
148
+ (e.g. `net` + `private-sync`) emitted two lines — and the single-object
149
+ reader rejected the whole output, silently dropping both overlays' vars. A
150
+ line that is not a JSON object still voids the whole script's contribution,
151
+ so a half-applied change set is impossible.
152
+ - fix: `init --agents codex` run from a session whose environment already
153
+ carried dotagents' identity vars (which its own env-loader hook exports —
154
+ `AGENT=claude-code` and friends in every command a Claude session runs)
155
+ wrote a Codex env block with no identity at all: the identity stamp never
156
+ overrides a value already present, so every key counted as "already set",
157
+ to Claude's values. When the agent is named explicitly the identity is now
158
+ that agent's, overriding the base env; without an explicit agent a pinned
159
+ value is respected as before.
160
+ - fix: the built `.pyz` lost the help text of the umbrella's own `--cmdspath`
161
+ flag (every subcommand's help survived). The zipapp source-repoint shim
162
+ covered each `dotagents.cli.<x>` command module but not the `dotagents.cli`
163
+ package itself, and it resolved a package's source as `cli.py` instead of
164
+ `cli/__init__.py`, so the umbrella kept its zip-internal `__file__` and duho
165
+ fell back to a bare flag. Both fixed; the top-level help in a `.pyz` now
166
+ matches a plain install.
167
+ - fix: `context`'s `<NAME_OVERLAY_ROOT>` placeholder now also maps `.` (and any
168
+ other non-alphanumeric character) in an overlay name to `_`, not only `-` —
169
+ the name has to be a legal shell variable to be emitted by `env`, and the
170
+ placeholder follows the same rule. Overlays whose names contain only letters,
171
+ digits, `_` and `-` are unaffected.
172
+
173
+ ### Fixed
174
+
175
+ - **One broken command module no longer breaks every `dotagents` call.**
176
+ Discovery skips a source that fails to import for any reason (a
177
+ `SyntaxError`, an exception at import time) with a warning naming it. It
178
+ caught `ImportError` only, and duho propagates the rest on purpose, so a
179
+ typo in `~/.agents/dotagents/cmds/foo.py` made `env`, `context`, `init` and
180
+ even `--version` traceback, i.e. the hooks delivered nothing.
181
+ - **No more temp-directory litter from the `.pyz`.** Everything a zipapp run
182
+ extracts (package data, the repointed module sources) lives under one
183
+ per-process scratch directory removed at exit; a `mkdtemp` per item with no
184
+ cleanup had left 632 `dotagents-*` directories in one machine's `%TEMP%`.
185
+ - `overlays add` installs a source dir spelled `my_overlay` (it normalized to
186
+ `my-overlay` and looked only that up), validates and resolves EVERY name
187
+ against the source before touching anything (`add good bad` used to install
188
+ `good`, run its setup, then fail), installs each manifest's `requires`
189
+ first (transitively; `--no-requires` to skip; a missing requirement warns, a
190
+ cycle errors), publishes skills from the INSTALLED copy (a symlink into the
191
+ source dies with a temporary checkout and could never be matched by
192
+ `remove`), and reports the setup script on a dry run.
193
+ - `overlays remove` normalizes names (`add My_Ov` + `remove My_Ov` said "not
194
+ installed") and recomposes the managed block over the overlays that remain,
195
+ so an overlay's rules and routing leave `AGENTS.md` with it; the warning
196
+ that pointed at the removed `dotagents install` is gone.
197
+ - `overlays sync` honours `--copy` (declared and ignored) and gains
198
+ `--overwrite`, which replaces installed files whose content differs from the
199
+ source; without it a sync never updated an upstream change to an existing
200
+ file.
201
+ - New `overlays show <name>` describes an overlay (installed copy first, else
202
+ the source): description, priority, requires, routing, rules, setup script,
203
+ skills, file count, root var; `--json`.
204
+ - The manifest reader strips trailing `#` comments quote-aware and finds an
205
+ array's closing `]` by scanning, so `routing = ["a"] # note` no longer
206
+ swallows the NEXT array (a rules path became a routing line), an indented
207
+ `]` no longer yields `[]`, `'single-quoted'` strings parse, and
208
+ `priority = 5 # low` is 5, not 500. `description` and `requires` are read.
209
+ - `_compose_block` with a base that has no `## Load on demand` heading appends
210
+ the overlay rules at the end of the block, as its warning always claimed;
211
+ they were dropped.
212
+ - `build-pyz` outside a source checkout is a clear error instead of a
213
+ `FileNotFoundError`.
214
+ - `dotagents`, `overlays` and `findings` invoked with no subcommand print
215
+ their help and exit 2 instead of logging a hint and exiting 0.
216
+ - `--agents-dir X` on the command line is honoured by command discovery, so
217
+ the store the command is about to use is the one whose `cmds/` are found.
218
+ - `findings add` rejects a name already carried by another note's frontmatter
219
+ (`get` matches frontmatter names first, so `done` would have processed the
220
+ wrong file).
221
+ - Two overlay dirs with the same manifest `name` sort deterministically (dir
222
+ name is the final tiebreaker).
223
+ - **SessionStart context is no longer injected twice.** The PowerShell
224
+ variants of Claude's `SessionStart` / `CwdChanged` handlers run only when
225
+ `bash` is not on PATH. Both handlers fire on every session, and on a Windows
226
+ box that has both Git Bash and PowerShell both succeeded, so the same
227
+ payload landed twice at every session start (two identical 100 KB
228
+ payloads, measured).
229
+ - The Codex `PreToolUse` env-loader prefix is quoted correctly: inside
230
+ `"$(...)"` its `\"` were literal quote characters, so `PATH` became
231
+ `".agents/bin:...:<last>"` with the quotes, the project `.agents/bin` was
232
+ never found and the last original entry was broken. The rewritten command
233
+ is now executed in bash by a test.
234
+ - Hook merging keeps a user's hook that shares a matcher-object with an older
235
+ shape of ours (the whole object used to be dropped), and a revised `shell`
236
+ / `matcher` / `commandWindows` / status on an unchanged command text now
237
+ reaches existing users instead of being kept as-is.
238
+ - Claude's skills link is per skill into `<config>/skills/<name>`: linking the
239
+ whole directory failed with "conflict" for anyone who already had their own
240
+ `~/.claude/skills`, so overlay skills never reached them. A same-named skill
241
+ the user placed there stays.
242
+ - Every hook command resolves the store as `$AGENTS_HOME` when set (bash:
243
+ `${AGENTS_HOME:-$HOME/.agents}`), so a custom store gets hooks that can find
244
+ `dotagents`; the PowerShell SessionStart variant prefers the project's own
245
+ `.agents\bin` like the bash one. The PowerShell tool env-loader runs
246
+ `env --diff` instead of re-assigning the whole environment on every call.
247
+ - The bash `CwdChanged` handler re-pins `AGENTS_PROJECT_ROOT` into
248
+ `$CLAUDE_ENV_FILE` when the new directory carries a `.agents/`; the
249
+ SessionStart pin is only-if-unset, so a `cd` into another project used to
250
+ keep the first project's root for the rest of the session.
251
+ - The Antigravity hook pins the project root from `workspacePaths` (cwd and
252
+ `AGENTS_PROJECT_ROOT` of the `dotagents context` spawn), as its docstring
253
+ already claimed, honours `$AGENTS_HOME` when locating `dotagents`, and
254
+ injects nothing when the assembly exits non-zero. Its constant is
255
+ `PREINVOCATION_HOOK_SCRIPT` (it wires a `PreInvocation` hook).
256
+ - Unpublishing an overlay's skills compares file CONTENT, not just names: a
257
+ copy the user had edited (same file set, different bytes) was deleted as
258
+ the overlay's.
259
+ - **The store's rules now reach Claude on a fresh install.** `init` writes the
260
+ `@` include where Claude Code reads it -- `~/.claude/CLAUDE.md` for the user
261
+ store, `<project>/.claude/CLAUDE.md` for a project -- as an appended managed
262
+ block, skipped when the include line is already there by hand. Before, it
263
+ wrote `<store>/CLAUDE.md` (which Claude never reads) while `context`
264
+ subtracted `~/.agents/AGENTS.md` as "already loaded" on a static assumption,
265
+ so nothing delivered the base rules. `context` now subtracts exactly what
266
+ the harness's entry files actually `@`-include (`Agent.loaded_paths`),
267
+ recursively, on this machine.
268
+ - `context` no longer inlines every `.md` file the sources mention. Inlining is
269
+ opt-in (`--inline` / `inline=True`): the base rules say to read those files
270
+ only when a task needs them, and inlining every mention made a 100 KB
271
+ SessionStart payload, most of it a changelog and an API header that
272
+ happened to be named in prose. With `--inline`, sources and harness-loaded
273
+ files are never inlined a second time, and placeholders inside inlined
274
+ files expand.
275
+ - Overlay priority ordering and `<NAME_OVERLAY_ROOT>` placeholder expansion in
276
+ `context` never worked: the resolver labels an overlay entry with the
277
+ overlay's name and the code compared it to the literal `"overlay"`. Every
278
+ installed overlay now gets a placeholder (matching `env`), not only one that
279
+ ships a `CONTEXT.md`.
280
+ - `context --write-agent` merges the context into the harness's own
281
+ instruction file under the PROJECT root -- Claude `.claude/CLAUDE.md`, Codex
282
+ `AGENTS.md`, Gemini `GEMINI.md`, Cursor `.cursorrules`, Copilot
283
+ `.github/copilot-instructions.md`, Antigravity `.agents/rules/dotagents.md`
284
+ -- as a managed `dotagents:context` block that is refreshed in place. It
285
+ used to overwrite a file under the user STORE; for Codex that was
286
+ `<store>/AGENTS.md`, a context source, so every run re-inlined the previous
287
+ run's output. `--format` is validated, and `--write-agent` refuses `--format
288
+ json` or an output path instead of silently ignoring one.
289
+ - Project-scope overlays (`<project>/.agents/overlays/`, where `overlays add`
290
+ installs by default) are now part of the contract-A walk: their `bin`,
291
+ `lib`, env files, `cmds` and `CONTEXT.md` resolve, after the user store's,
292
+ and each gets a `<NAME>_OVERLAY_ROOT` (a project overlay wins a name clash).
293
+ Nothing consumed them before except the `AGENTS.md` recompose.
294
+ - Managed-block markers must be a line of their own: a prose MENTION of the
295
+ markers (the base `AGENTS.md` carries one) was matched as the block, so the
296
+ sentence around it was replaced and a second block appeared. A begin marker
297
+ with no end after it is refused with a clear error instead of gaining a
298
+ second block; a `--from` base without markers is a usage error, not a
299
+ traceback.
300
+ - Every managed file (`AGENTS.md`, the includes, `settings.json`, `hooks.json`,
301
+ context targets) is written LF-only on every platform; `settings.json` /
302
+ `hooks.json` are written atomically and keep non-ASCII values as-is.
303
+ - The user scope is recognized by the configurable store (`$AGENTS_HOME`), not
304
+ the literal `~/.agents`: `init -g` with a custom store used to wire Claude's
305
+ hooks into `<store-parent>/.claude/settings.local.json`, which nothing reads.
306
+ - `CODEX_HOME` no longer marks a running Codex session (it is the user's
307
+ persistent state-dir override, exported from a shell profile); the
308
+ `CODEX_SANDBOX*` vars still do, and `CODEX_HOME` still locates the config.
309
+ - An unknown `--agents` name no longer overrides a pinned identity in `env`.
310
+ - An overlay cannot be named like a contract-A level (`user`, `project`,
311
+ `system`, `project-root`, `default`, `overlay`); its dir name is its level
312
+ label in the walk and would collide with the per-level filename keys.
313
+ - A directory named `env` (a common virtualenv name) is no longer "sourced" as
314
+ an env file; only regular files resolve.
315
+ - Sourcing a plain env file that fails (missing, a directory, a syntax error)
316
+ now contributes nothing and warns, as documented. The bash command was a
317
+ `;` list, so `env -0` ran regardless and reported rc 0 with whatever had
318
+ been assigned before the error.
319
+ - Bash's own `PWD` (in `/c/...` form), `OLDPWD`, `SHLVL` and `MSYSTEM*` are no
320
+ longer reported as a sourced file's changes; a non-UTF-8 byte in a sourced
321
+ value no longer aborts the whole assembly.
322
+ - `env --format auto` returned `cmd` when invoked through the `dotagents.cmd`
323
+ wrapper from PowerShell (the wrapper cannot exec, so `cmd.exe` sits between
324
+ Python and the shell); a `cmd` whose own parent is a shell is now skipped.
325
+ - `env` formats: `fish` escapes backslashes; `cmd` doubles `%` and flattens
326
+ newlines; `yaml` quotes values a reader would type (`true`, `123`, `null`,
327
+ `1e3`, leading indicators); `powershell`/`cmd` no longer mangle a
328
+ forward-slash native path (`C:/a/tools` became `C;A:\tools`); output is
329
+ written as UTF-8 bytes so a non-Latin-1 value cannot crash on a cp1252
330
+ console.
331
+ - `init -g`, `overlays ... -g` and every other `resolve_scope` caller honour
332
+ `$AGENTS_HOME` (the var `env` itself emits) instead of the literal `~/.agents`,
333
+ and `--agents-dir` overrides the store in the project scope too (it was
334
+ silently ignored without `-g`). `resolve_user_store` moved to `_scope`
335
+ (still re-exported from `dotagents.cli`); the `findings` command's local
336
+ workaround is gone.
337
+ - `overlays list` / `overlays add` apply the one overlay-name rule when listing
338
+ a source (`__pycache__`, `2fast`, dotdirs are no longer "available"), and a
339
+ bad source path names whether it came from `--source` or the env var.
340
+ - `findings list` / `show` write UTF-8 bytes, so a description with a
341
+ non-Latin-1 character no longer raises on a cp1252 console.
342
+
10
343
  ## [0.3.4] - 2026-08-16
11
344
 
12
345
  ### Changed
@@ -46,8 +379,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
46
379
 
47
380
  - **`build-pyz` no longer bundles the repo's `tools/`** as `dotagents/_tools`
48
381
  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
382
+ read `_tools`: the compiled `audit` wrapper and a personal scanner's wrapper that
383
+ shelled out to it no longer exist. Every shipped artifact carried the dead weight while
51
384
  `tools/audit.py` claimed it was "not shipped in the `.pyz`" — now true.
52
385
  - **`dotagents._sync`** — a `pathlib_next.PathSyncer` wrapper that existed only
53
386
  to back the `install` subcommand's backup/copy report. `install` was removed
@@ -62,8 +395,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
62
395
  no longer claims to ship as a bundled `audit` command module (it is repo CI
63
396
  tooling and there is no `dotagents audit`) and its `--root` help names the
64
397
  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
398
+ never-existing `audit`; the README's `tools/` row says where a personal
399
+ command module actually lives; the API header names the real `_overlays` exports
67
400
  (`install_overlay_dir` / `apply_overlay` / `run_overlay_setup`) and the real
68
401
  package-data dirs (`_overlay` / `_overlays_src`, never a `skeleton/`); and
69
402
  `_merge._extract_block`'s docstring now says it returns the block *including*
@@ -248,7 +581,7 @@ Patch release: the PATH/POSIX-conversion fix above (the only change since 0.3.0)
248
581
  directory. Symlink where the OS permits, copy otherwise (a copy is a snapshot;
249
582
  re-run `init` to refresh).
250
583
 
251
- - feat: `tools/leak_check.py` now also scans commit messages (current branch history)
584
+ - feat: the personal leak scanner (then in `tools/`) now also scans commit messages (current branch history)
252
585
  for agent-session trailers/URLs — a `Claude-Session:` trailer or `claude.ai/code/session`
253
586
  link — and exits 1 on any hit. The trailer is auto-added by the agent harness and
254
587
  exposes a session id in public history if it slips through; the pre-existing tracked-file
@@ -387,7 +720,7 @@ Patch release: the PATH/POSIX-conversion fix above (the only change since 0.3.0)
387
720
  `recovery`, `references`, `python`/`node`/`rust`, `agents`, `tools`. Each carries
388
721
  an `overlay.toml` manifest for a future `dotagents overlays` subcommand.
389
722
  - Repo layout: the CLI in `src/dotagents/`, config overlays in `overlays/`, required
390
- tooling in top-level `tools/` (`audit_config.py`, `leak_check.py`); repo root holds
723
+ tooling in top-level `tools/` (`audit_config.py` and a personal leak scanner); repo root holds
391
724
  the installer, CI, repo-development directives, and the tracked, sanitized
392
725
  `.agents/` design log (index + per-decision files) + plans. `audit_config.py` has
393
726
  `--repo-hygiene` (scans tracked files for personal/machine-specific leftovers).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dotagents-cli
3
- Version: 0.3.4
3
+ Version: 0.4.0
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/` | 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/` |
105
+ | `tools/` | Repo tooling, not shipped: `audit.py` (CI structure check) and `cloud-setup.sh`. Personal scanning tools are not here either — keep them as command modules 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,
@@ -167,7 +167,7 @@ where the example overlays live. Installed overlays are *discovered* by their pr
167
167
  under `<scope>/.agents/overlays/` — there is no registry file.
168
168
 
169
169
  ```bash
170
- dotagents overlays add python flows # install into the scope, publish skills, merge D59 rules/routing
170
+ dotagents overlays add python engineering # install into the scope, publish skills, merge D59 rules/routing
171
171
  dotagents overlays list # installed (discovered) + available (from source)
172
172
  dotagents overlays sync 'py*' # refresh installed overlays matching a glob, resync their skills
173
173
  dotagents overlays remove python # delete the overlay dir + unpublish its skills
@@ -214,7 +214,7 @@ Then wire your runner to it — e.g. Claude Code: put `@AGENTS.md` in
214
214
  `~/.claude/CLAUDE.md`... which is exactly what the installed `CLAUDE.md` contains.
215
215
 
216
216
  **Or let your agent do it:** point it at this repo and say —
217
- > Read README.md, run `python install.py init && python install.py overlays add flows -g`,
217
+ > Read README.md, run `python install.py init && python install.py overlays add engineering -g`,
218
218
  > and confirm `~/.agents/overlays/flows/flows/PLAN.md` exists.
219
219
 
220
220
  ## Private sync (per-user + per-project, one private repo)
@@ -283,8 +283,8 @@ committed — so what's public here is only the CLI, the base overlay, and the o
283
283
  overlays. If you fork, keep the tracked surface free of personal paths and private
284
284
  project names. `dotagents audit` validates config *structure* only; personal-leak
285
285
  scanning (machine paths, private plan names, session trailers) is a separate,
286
- personal `leak-check` tool you run locally before a push — it lives in your private
287
- `.agents/`, not shipped in this repo.
286
+ personal tool you run locally before a push — it lives in your private `.agents/`,
287
+ not shipped in this repo.
288
288
 
289
289
  ## Documentation
290
290
 
@@ -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/` | 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/` |
44
+ | `tools/` | Repo tooling, not shipped: `audit.py` (CI structure check) and `cloud-setup.sh`. Personal scanning tools are not here either — keep them as command modules 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,
@@ -106,7 +106,7 @@ where the example overlays live. Installed overlays are *discovered* by their pr
106
106
  under `<scope>/.agents/overlays/` — there is no registry file.
107
107
 
108
108
  ```bash
109
- dotagents overlays add python flows # install into the scope, publish skills, merge D59 rules/routing
109
+ dotagents overlays add python engineering # install into the scope, publish skills, merge D59 rules/routing
110
110
  dotagents overlays list # installed (discovered) + available (from source)
111
111
  dotagents overlays sync 'py*' # refresh installed overlays matching a glob, resync their skills
112
112
  dotagents overlays remove python # delete the overlay dir + unpublish its skills
@@ -153,7 +153,7 @@ Then wire your runner to it — e.g. Claude Code: put `@AGENTS.md` in
153
153
  `~/.claude/CLAUDE.md`... which is exactly what the installed `CLAUDE.md` contains.
154
154
 
155
155
  **Or let your agent do it:** point it at this repo and say —
156
- > Read README.md, run `python install.py init && python install.py overlays add flows -g`,
156
+ > Read README.md, run `python install.py init && python install.py overlays add engineering -g`,
157
157
  > and confirm `~/.agents/overlays/flows/flows/PLAN.md` exists.
158
158
 
159
159
  ## Private sync (per-user + per-project, one private repo)
@@ -222,8 +222,8 @@ committed — so what's public here is only the CLI, the base overlay, and the o
222
222
  overlays. If you fork, keep the tracked surface free of personal paths and private
223
223
  project names. `dotagents audit` validates config *structure* only; personal-leak
224
224
  scanning (machine paths, private plan names, session trailers) is a separate,
225
- personal `leak-check` tool you run locally before a push — it lives in your private
226
- `.agents/`, not shipped in this repo.
225
+ personal tool you run locally before a push — it lives in your private `.agents/`,
226
+ not shipped in this repo.
227
227
 
228
228
  ## Documentation
229
229
 
@@ -0,0 +1,3 @@
1
+ # Filesystem
2
+
3
+ ::: dotagents._fs
@@ -13,11 +13,12 @@ form its API. Generated from docstrings, organized by module:
13
13
  command.
14
14
  - **[Context](context.md)** — assembling the effective context for agents.
15
15
  - **[Environment](env.md)** — chained env-file assembly and `env.py` execution.
16
- - **[Resolution](resolve.md)** — the precedence walk / filename resolution
17
- (Contract A).
18
16
  - **[Merge](merge.md)** — the managed-block merge for `init`'s `AGENTS.md` /
19
- `CLAUDE.md`, and for other comment syntaxes (e.g. TOML) via `begin_marker`/
17
+ `CLAUDE.md`, the harness-entry `@` include, the `context --write-agent`
18
+ block, and other comment syntaxes (e.g. TOML) via `begin_marker`/
20
19
  `end_marker`/`append`.
20
+ - **[Filesystem](fs.md)** — the one text-file writer every module uses:
21
+ LF-only on every platform, optionally atomic.
21
22
  - **[Hooks](hooks.md)** — additive, idempotent merge of dotagents' hooks into an
22
23
  agent's own settings/config file.
23
24
  - **[Skills](skills.md)** — publishing an overlay's skills into a scope's shared dir.