dotagents-cli 0.3.3__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.
- dotagents_cli-0.4.0/CHANGELOG.md +726 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/PKG-INFO +8 -8
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/README.md +5 -5
- dotagents_cli-0.4.0/docs/api/fs.md +3 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/api/reference.md +4 -3
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/guide/commands.md +93 -24
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/guide/install.md +1 -1
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/guide/overlays.md +28 -11
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/index.md +1 -1
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/mkdocs.yml +1 -1
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/pyproject.toml +3 -3
- dotagents_cli-0.4.0/src/dotagents/AGENTS.md +364 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/__init__.py +1 -1
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/_agents.py +251 -94
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/_context.py +133 -133
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/_env.py +212 -74
- dotagents_cli-0.4.0/src/dotagents/_fs.py +43 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/_hooks.py +58 -24
- dotagents_cli-0.4.0/src/dotagents/_merge.py +222 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/_overlay/AGENTS.md +5 -3
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/_overlay/README.md +16 -9
- dotagents_cli-0.4.0/src/dotagents/_overlay/dotagents/DECISIONS.md +24 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/_overlay/dotagents/cmds/README.md +12 -9
- dotagents_cli-0.4.0/src/dotagents/_overlay/dotagents/cmds/findings.py +579 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/_overlay/dotagents/hooks/preinvocation_antigravity_context.py +34 -5
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/_overlay/dotagents/hooks/pretooluse_codex_env.py +8 -2
- dotagents_cli-0.4.0/src/dotagents/_overlays.py +707 -0
- dotagents_cli-0.4.0/src/dotagents/_scope.py +445 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/_skills.py +37 -16
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/cli/__init__.py +84 -44
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/cli/_common.py +139 -75
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/cli/build_pyz.py +21 -7
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/cli/context.py +43 -33
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/cli/env.py +109 -31
- dotagents_cli-0.4.0/src/dotagents/cli/overlays.py +531 -0
- dotagents_cli-0.4.0/tests/_shell.py +50 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_agents.py +3 -1
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_audit_leak.py +13 -6
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_cmds_discovery.py +50 -16
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_context.py +107 -31
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_env.py +190 -18
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_env_format.py +140 -10
- dotagents_cli-0.4.0/tests/test_env_safety.py +203 -0
- dotagents_cli-0.4.0/tests/test_findings_cmd.py +280 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_hooks.py +27 -0
- dotagents_cli-0.4.0/tests/test_last_mile.py +256 -0
- dotagents_cli-0.4.0/tests/test_overlays_cli.py +358 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_overlays_command.py +157 -16
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_scope.py +7 -25
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_wire_hooks.py +116 -11
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tools/audit.py +34 -23
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tools/cloud-setup.sh +10 -6
- dotagents_cli-0.3.3/CHANGELOG.md +0 -367
- dotagents_cli-0.3.3/docs/api/resolve.md +0 -3
- dotagents_cli-0.3.3/src/dotagents/AGENTS.md +0 -220
- dotagents_cli-0.3.3/src/dotagents/_merge.py +0 -126
- dotagents_cli-0.3.3/src/dotagents/_overlay/dotagents/DECISIONS.md +0 -20
- dotagents_cli-0.3.3/src/dotagents/_overlays.py +0 -412
- dotagents_cli-0.3.3/src/dotagents/_resolve.py +0 -81
- dotagents_cli-0.3.3/src/dotagents/_scope.py +0 -265
- dotagents_cli-0.3.3/src/dotagents/cli/overlays.py +0 -334
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/.gitignore +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/LICENSE +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/build.py +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/api/agents.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/api/cli.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/api/context.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/api/env.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/api/hooks.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/api/merge.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/api/overlays.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/api/scope.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/api/skills.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/changelog.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/guide/authoring.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/docs/guide/private-sync.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/install.py +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/__main__.py +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/_overlay/CLAUDE.md +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/_wrappers.py +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/src/dotagents/cli/init.py +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_build_pyz.py +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_env_cli_scope.py +0 -0
- {dotagents_cli-0.3.3 → dotagents_cli-0.4.0}/tests/test_wrappers.py +0 -0
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
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
|
+
|
|
343
|
+
## [0.3.4] - 2026-08-16
|
|
344
|
+
|
|
345
|
+
### Changed
|
|
346
|
+
|
|
347
|
+
- chore: raise the dependency floors and scope them to a minor series —
|
|
348
|
+
`duho>=0.5.0,<0.6` (was `>=0.4.0`) and `pathlib_next>=0.9.0,<0.10` (was
|
|
349
|
+
`>=0.8.0`). Both are pre-1.0, where a minor bump is the signal that the
|
|
350
|
+
documented API broke, so the ceiling is what keeps the next one from arriving
|
|
351
|
+
unannounced. Neither floor sits above its `.0` patch: the suite and a CLI
|
|
352
|
+
smoke pass at exactly duho 0.5.0 and pathlib_next 0.9.0, so nothing here needs
|
|
353
|
+
an API added later in either series. The one 0.5.0 behavior change that
|
|
354
|
+
reaches this CLI's surface is list-typed *option* fields taking one value per
|
|
355
|
+
occurrence instead of `nargs="*"` — `--agents` and `--cmdspath` are unaffected,
|
|
356
|
+
since the documented forms are the comma list (`--agents a,b`, split by the
|
|
357
|
+
command itself) and the repeated flag, both of which behave the same either
|
|
358
|
+
way. Exercised end to end against duho 0.5.4 / pathlib_next 0.9.2 on Python
|
|
359
|
+
3.9 and 3.14. The `[uri]`/`[http]`/`[sftp]`/`[s3]` extras stay unversioned
|
|
360
|
+
passthroughs to `pathlib_next`'s own extras.
|
|
361
|
+
- chore: `build-pyz`'s vendored pins moved with those floors — the `.pyz` now
|
|
362
|
+
bundles duho 0.5.0 and pathlib_next 0.9.0 (was 0.4.0 / 0.8.0), the minimum the
|
|
363
|
+
package claims to support rather than the latest patch. These pins are a
|
|
364
|
+
second copy of the dependency versions and had drifted a full minor series
|
|
365
|
+
behind, so the shipped zipapp bundled versions `pip install dotagents-cli`
|
|
366
|
+
would have refused. The rebuilt `.pyz` keeps full flag/help/positional
|
|
367
|
+
fidelity through the zipapp shim.
|
|
368
|
+
|
|
369
|
+
## [0.3.3] - 2026-08-16
|
|
370
|
+
|
|
371
|
+
### Added
|
|
372
|
+
|
|
373
|
+
- feat: `env` and `context` gained **`--agents-dir`** (from the shared
|
|
374
|
+
`DotAgentsArgs` base) to override the store for one run. Their `-g/--global`
|
|
375
|
+
keeps its existing, narrower meaning here — *skip the project-level files* —
|
|
376
|
+
and now says so in `--help`.
|
|
377
|
+
|
|
378
|
+
### Removed
|
|
379
|
+
|
|
380
|
+
- **`build-pyz` no longer bundles the repo's `tools/`** as `dotagents/_tools`
|
|
381
|
+
inside the built `.pyz`, and the `--tools-dir` flag is gone with it. Nothing
|
|
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
|
|
384
|
+
`tools/audit.py` claimed it was "not shipped in the `.pyz`" — now true.
|
|
385
|
+
- **`dotagents._sync`** — a `pathlib_next.PathSyncer` wrapper that existed only
|
|
386
|
+
to back the `install` subcommand's backup/copy report. `install` was removed
|
|
387
|
+
in 0.3.x; the module has had no callers since, no tests, and a return
|
|
388
|
+
annotation that disagreed with what it returned. `pathlib_next` remains a
|
|
389
|
+
dependency (duho, and `--from` URI support). Its API-reference page went with
|
|
390
|
+
it.
|
|
391
|
+
|
|
392
|
+
### Fixed
|
|
393
|
+
|
|
394
|
+
- docs: a cluster of "docs say X, tree does Y" corrections — `tools/audit.py`
|
|
395
|
+
no longer claims to ship as a bundled `audit` command module (it is repo CI
|
|
396
|
+
tooling and there is no `dotagents audit`) and its `--root` help names the
|
|
397
|
+
real default; `install.py`'s usage line drops the removed `install` and the
|
|
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
|
|
400
|
+
(`install_overlay_dir` / `apply_overlay` / `run_overlay_setup`) and the real
|
|
401
|
+
package-data dirs (`_overlay` / `_overlays_src`, never a `skeleton/`); and
|
|
402
|
+
`_merge._extract_block`'s docstring now says it returns the block *including*
|
|
403
|
+
its marker lines, which is what it has always done and what callers rely on.
|
|
404
|
+
- fix: **`stamp_identity` no longer pretends to emit `AGENTS_AGENT`.** The line
|
|
405
|
+
sourced the value from `$AGENTS_AGENT` and only assigned when that same key
|
|
406
|
+
was unset, so it could never emit anything — while the docstring and the
|
|
407
|
+
shipped API header both advertised the var. Line removed; the API header's
|
|
408
|
+
"emitted by the identity/env layer" list now drops both `AGENTS_AGENT` and
|
|
409
|
+
`AGENTS_CODE_SESSION_ID` (the latter was deliberately never emitted) and says
|
|
410
|
+
so explicitly, so nothing branches on a var that never arrives.
|
|
411
|
+
- fix: **`dotagents env` and `dotagents context` now resolve their roots instead
|
|
412
|
+
of hardcoding them.** Both took `Path.cwd()` as the project root and
|
|
413
|
+
`~/.agents` as the user store, so `$AGENTS_PROJECT_ROOT` (or the agent-native
|
|
414
|
+
`$CLAUDE_PROJECT_DIR`) and `$AGENTS_HOME` were ignored by the two commands
|
|
415
|
+
that most needed them — a `SessionStart` hook runs `dotagents context` from
|
|
416
|
+
wherever the session happens to start, so a pinned project root was silently
|
|
417
|
+
dropped and a relocated store was never read. They now use
|
|
418
|
+
`_scope.project_root_default()` and the new
|
|
419
|
+
`dotagents.cli.resolve_user_store()` (`--agents-dir` → `$AGENTS_HOME` →
|
|
420
|
+
legacy `$DOTAGENTS_AGENTS_DIR` → `~/.agents`), matching what the package's own
|
|
421
|
+
`resolve_scope` docstring and command discovery already promised. Behavior is
|
|
422
|
+
unchanged when none of the vars are set.
|
|
423
|
+
|
|
424
|
+
## [0.3.2] - 2026-07-25
|
|
425
|
+
|
|
426
|
+
### Added
|
|
427
|
+
|
|
428
|
+
- feat: **`AntigravityAgent`** — context injection for Google's Antigravity
|
|
429
|
+
CLI/IDE (a separate product from Gemini CLI, despite sharing the `~/.gemini/`
|
|
430
|
+
namespace for some files). Antigravity's hooks have no `SessionStart`
|
|
431
|
+
equivalent — only `PreToolUse`/`PostToolUse`/`PreInvocation`/`PostInvocation`/
|
|
432
|
+
`Stop` — so a `PreInvocation` hook gated on `invocationNum == 0` behaves like a
|
|
433
|
+
one-shot context load instead of resending it every model turn. Wires into
|
|
434
|
+
`~/.gemini/config/hooks.json`. No detection marker exists for Antigravity, so
|
|
435
|
+
it's explicit-`--agents antigravity`-only, never auto-detected.
|
|
436
|
+
- feat: **Codex gets a `PreToolUse` env hook**, closing the one gap Codex had
|
|
437
|
+
versus Claude: Codex has no per-session env-persistence mechanism at any hook
|
|
438
|
+
event, so a deployed script prepends a guarded env-loader to every `Bash` tool
|
|
439
|
+
call via `updatedInput.command` — the same rewrite mechanism Claude's own
|
|
440
|
+
`PreToolUse` hook uses, confirmed directly against Codex's docs.
|
|
441
|
+
- feat: **`init` wires a PowerShell `PreToolUse` env hook on Windows**, closing
|
|
442
|
+
a real gap: `$CLAUDE_ENV_FILE` only reaches Claude's *Bash* tool
|
|
443
|
+
(`$env:CLAUDE_ENV_FILE` is empty inside a live PowerShell tool call, confirmed
|
|
444
|
+
directly) — a fresh PowerShell tool call gets none of the SessionStart env.
|
|
445
|
+
The hook prepends a guarded env-loader to a PowerShell tool call's own command,
|
|
446
|
+
shipped as an inline command string (never a `.ps1` file — a script file is
|
|
447
|
+
subject to PowerShell's execution policy, and dotagents has no code-signing
|
|
448
|
+
certificate; the inline form runs even under `Restricted`).
|
|
449
|
+
- feat: **`SessionStart`/`CwdChanged` now register two handlers each** — a
|
|
450
|
+
bash-syntax one and an explicit `shell: "powershell"` one — because Claude's
|
|
451
|
+
own hooks.md says the shell "defaults to bash, or to powershell on Windows
|
|
452
|
+
when Git Bash isn't installed": bash syntax fed to `powershell -Command` on
|
|
453
|
+
such a machine is a hard parse error, silently losing both env and context for
|
|
454
|
+
the whole session.
|
|
455
|
+
- feat: **`DotAgentsArgs`** (`dotagents.cli`, re-exported for overlay-shipped
|
|
456
|
+
commands) — one shared `-g/--global` + `--agents-dir` base class. `init` and
|
|
457
|
+
all four `overlays` subcommands now inherit it instead of independently
|
|
458
|
+
redeclaring the same fields, so scope resolution can't silently drift between
|
|
459
|
+
commands (one previously defaulted `agents_dir` eagerly to `Path.home() /
|
|
460
|
+
".agents"`; harmless in practice, but needless).
|
|
461
|
+
|
|
462
|
+
### Fixed
|
|
463
|
+
|
|
464
|
+
- fix: **`dotagents env --format powershell`/`cmd` left an already-POSIX `PATH`
|
|
465
|
+
unconverted**, the mirror of the export/dotenv/fish fix below going the other
|
|
466
|
+
direction. Found live: run from a genuine Windows PowerShell terminal whose
|
|
467
|
+
own inherited `PATH` already held WSL/MSYS-mount-style entries
|
|
468
|
+
(`/mnt/c/Program Files/...`), the emitted `${env:PATH} = '...'` was
|
|
469
|
+
syntactically valid PowerShell but a single opaque colon-joined string, not
|
|
470
|
+
the `;`-split list PowerShell's own PATH lookup needs — every subsequent
|
|
471
|
+
bare-command lookup broke for that session. Also handles the MIXED case
|
|
472
|
+
(dotagents' own native bin dirs prepended onto an already-POSIX inherited
|
|
473
|
+
PATH, one string with both separators at once), caught by sourcing real
|
|
474
|
+
output into a live PowerShell session and watching `git.exe` fail to
|
|
475
|
+
resolve before the second fix. A WSL-only segment with no Windows equivalent
|
|
476
|
+
(`/usr/bin`) is dropped rather than mangled into a broken relative path.
|
|
477
|
+
- fix: **the built `.pyz` degraded `-g` on any discovered command inheriting a
|
|
478
|
+
dotagents-defined base class** (first hit by `DotAgentsArgs` above) — duho's
|
|
479
|
+
AST introspection walks the full MRO for a command's flags, but the zipapp
|
|
480
|
+
source-repoint shim only ever covered built-in command modules, not a base
|
|
481
|
+
class's own module (`dotagents.cli._common`). `--global` degraded to the
|
|
482
|
+
name-derived `--global-scope` and `-g` vanished silently. Fixed, with new CI
|
|
483
|
+
coverage asserting the exact short flag survives a real built pyz.
|
|
484
|
+
- fix: **`harness_loads` relative entries matched by bare filename, not full
|
|
485
|
+
path** — a relative entry like Codex's `"AGENTS.md"` wrongly suppressed ANY
|
|
486
|
+
file sharing that basename anywhere on disk, including the unrelated
|
|
487
|
+
`~/.agents/AGENTS.md` user-store file Codex's harness never reads.
|
|
488
|
+
`dotagents context --agents codex` emitted an empty `sources: []` even with
|
|
489
|
+
real content present. Now resolved against `project_root` and compared by
|
|
490
|
+
full path, like the absolute (`~/`, `/`) forms already were.
|
|
491
|
+
- fix: `dotagents context --format json` crashed with `UnicodeEncodeError` on
|
|
492
|
+
any character outside Latin-1 (a bare `print()` encoding with the console's
|
|
493
|
+
codepage) — the same class of bug already fixed for the markdown path, just
|
|
494
|
+
never covered for JSON.
|
|
495
|
+
- fix: PowerShell format uses `${env:NAME}` (curly-brace form), not the bare
|
|
496
|
+
`$env:NAME` sigil — a handful of real Windows env vars have parens in their
|
|
497
|
+
names (`ProgramFiles(x86)`), and `$env:FOO(X86) = ...` is a PowerShell parse
|
|
498
|
+
error; the curly-brace form is valid for every name.
|
|
499
|
+
|
|
500
|
+
## [0.3.1] - 2026-07-24
|
|
501
|
+
|
|
502
|
+
### Fixed
|
|
503
|
+
|
|
504
|
+
- fix: **`dotagents env --format export`/`dotenv`/`fish` now emit a POSIX PATH on
|
|
505
|
+
Windows**, instead of the OS-native `C:\...;C:\...` form. This is the exact
|
|
506
|
+
command the Claude `SessionStart` hook appends into `$CLAUDE_ENV_FILE`, which
|
|
507
|
+
Claude sources before *every* subsequent Bash tool call in the session — an
|
|
508
|
+
unconverted PATH broke command lookup (`git`, `grep`, `head`, `python`, ...) for
|
|
509
|
+
the rest of the session once poisoned, not just once. PATH-shaped values are
|
|
510
|
+
now converted per segment: backslash to forward-slash, `;` to `:`, and a drive
|
|
511
|
+
letter to its MSYS mount point (`C:/...` -> `/c/...` — required for PATH
|
|
512
|
+
*lookups* specifically; slash direction alone does not work in MSYS2/Cygwin
|
|
513
|
+
bash). `PATHEXT` is dropped (no POSIX meaning). A handful of real Windows env
|
|
514
|
+
vars with parentheses in their names (`ProgramFiles(x86)`) are also dropped for
|
|
515
|
+
these formats — `export FOO(X86)=...` is a bash syntax error, not a bad value,
|
|
516
|
+
and aborts sourcing the rest of the file. `powershell`/`cmd`/`json`/`ini`/`yaml`
|
|
517
|
+
are unaffected.
|
|
518
|
+
- fix: a UNC PATH segment (`\\server\share\...`) no longer collapses to a single
|
|
519
|
+
leading slash (`/server/share/...`) during the POSIX conversion above — MSYS
|
|
520
|
+
requires the double-slash UNC root (`//server/share/...`) to resolve it.
|
|
521
|
+
|
|
522
|
+
Patch release: the PATH/POSIX-conversion fix above (the only change since 0.3.0).
|
|
523
|
+
|
|
524
|
+
## [0.3.0] - 2026-07-24
|
|
525
|
+
|
|
526
|
+
### Changed
|
|
527
|
+
|
|
528
|
+
- **BREAKING** — `dotagents link` / `dotagents sync` are gone from the CLI. They are
|
|
529
|
+
the private-sync workflow's commands, not dotagents' core, so they moved — together
|
|
530
|
+
with the logic behind them (`src/dotagents/_link.py`) — into the opt-in
|
|
531
|
+
**`private-sync` overlay**, and were renamed to say what they act on:
|
|
532
|
+
|
|
533
|
+
dotagents link-project . # was: dotagents link .
|
|
534
|
+
dotagents sync-project -m "msg" # was: dotagents sync -m "msg"
|
|
535
|
+
|
|
536
|
+
Install the overlay to get them back: `dotagents overlays add private-sync --source
|
|
537
|
+
<overlays-checkout>`. A plain dotagents now ships no private-sync workflow at all;
|
|
538
|
+
its whole command surface is `init` / `build-pyz` / `context` / `env` / `overlays`,
|
|
539
|
+
and everything else is discovered from an overlay or from your own `cmds/` modules.
|
|
540
|
+
`tools/cloud-setup.sh` installs the overlay before linking, so the cloud bootstrap
|
|
541
|
+
is unaffected.
|
|
542
|
+
- The bundled `dotagents/cmds/` directory now ships no command module of its own, but
|
|
543
|
+
`init` still creates it: it is the documented drop-in point for your own commands
|
|
544
|
+
(a `README.md` beside it explains the shape and the precedence rules).
|
|
545
|
+
|
|
546
|
+
### Added
|
|
547
|
+
|
|
548
|
+
- feat: `init` wires agent hooks and links the shared skills dir, for each active
|
|
549
|
+
agent with a published hook schema (today: Claude and Codex). `--no-hooks` skips it.
|
|
550
|
+
|
|
551
|
+
**Claude** (`~/.claude/settings.json`) gets `SessionStart`, which appends
|
|
552
|
+
`dotagents env --diff --format export` to `$CLAUDE_ENV_FILE` and then runs
|
|
553
|
+
`dotagents context` — Claude sources that file before each Bash command and injects
|
|
554
|
+
the hook's stdout into the session context, so both the env layers and the assembled
|
|
555
|
+
context reach the session automatically. It also gets `CwdChanged`, surfacing a
|
|
556
|
+
directory's `AGENTS.md`. The env redirect **appends** and is guarded against an
|
|
557
|
+
unset variable, both as the hooks docs require.
|
|
558
|
+
|
|
559
|
+
**Codex** (`~/.codex/hooks.json`, or `$CODEX_HOME`) gets `SessionStart` running
|
|
560
|
+
`dotagents context`; its hook JSON is structurally identical to Claude's. We write
|
|
561
|
+
`hooks.json` rather than touching your `config.toml` for hooks.
|
|
562
|
+
|
|
563
|
+
Codex's env arrives differently: it has no `$CLAUDE_ENV_FILE` equivalent, reads no
|
|
564
|
+
`.env` files, and has no event that fires before config load, so
|
|
565
|
+
`dotagents init --agents codex` writes a `# dotagents:begin/end` managed block
|
|
566
|
+
containing `[shell_environment_policy].set` into `config.toml`. **Only on an
|
|
567
|
+
explicit `--agents`** — this edits your main config with values that go stale, so
|
|
568
|
+
auto-detection never triggers it. The block is appended and refreshed in place
|
|
569
|
+
(everything outside the markers is untouched) and `set` merges rather than
|
|
570
|
+
replaces. **The values are a static snapshot: re-run `init` after changing your env
|
|
571
|
+
layers.** Identity vars describe the target agent, so initializing from Claude still
|
|
572
|
+
writes `AGENT = "codex"`; `PATH` is excluded, since `set` overrides per subprocess
|
|
573
|
+
and a baked-in `PATH` would replace the inherited one.
|
|
574
|
+
|
|
575
|
+
The merge is additive and idempotent: unrelated keys and hooks you wrote yourself
|
|
576
|
+
survive verbatim, malformed entries are dropped rather than raising, and re-running
|
|
577
|
+
writes nothing.
|
|
578
|
+
|
|
579
|
+
`init` also links `<scope>/skills/` into the agent's config dir, closing the last
|
|
580
|
+
mile for overlay-published skills — publishing only helps if the agent reads that
|
|
581
|
+
directory. Symlink where the OS permits, copy otherwise (a copy is a snapshot;
|
|
582
|
+
re-run `init` to refresh).
|
|
583
|
+
|
|
584
|
+
- feat: the personal leak scanner (then in `tools/`) now also scans commit messages (current branch history)
|
|
585
|
+
for agent-session trailers/URLs — a `Claude-Session:` trailer or `claude.ai/code/session`
|
|
586
|
+
link — and exits 1 on any hit. The trailer is auto-added by the agent harness and
|
|
587
|
+
exposes a session id in public history if it slips through; the pre-existing tracked-file
|
|
588
|
+
scan didn't cover commit messages. `flows/REPO.md` release discipline documents the check
|
|
589
|
+
and the `git filter-branch --msg-filter` remediation for one that already landed.
|
|
590
|
+
- feat: `tools/cloud-setup.sh` step 5 wires `hooks/settings.snippet.json` into the
|
|
591
|
+
user-level `~/.claude/settings.json` (idempotent JSON merge, preserves existing
|
|
592
|
+
settings/hooks). A fresh cloud container has no settings file and nothing else
|
|
593
|
+
created one, so the SessionStart pull/link and Stop sync-back hooks never ran —
|
|
594
|
+
the private repo went stale and session changes were silently never pushed back.
|
|
595
|
+
`kb/PRIVATE_SYNC.md` documents the auto-wiring (manual merge still applies on
|
|
596
|
+
local machines).
|
|
597
|
+
|
|
598
|
+
### Fixed
|
|
599
|
+
|
|
600
|
+
- fix: `dotagents link`/`sync` never adopt or copy back a `<project>/.agents` that is
|
|
601
|
+
itself a git checkout (`.git` present — dir, or file for worktrees). A hosted-runner
|
|
602
|
+
session that lists the agents repo as a *source* gets it cloned to
|
|
603
|
+
`<project>/.agents` by the harness; first-link adoption then moved that entire
|
|
604
|
+
checkout — `.git`, foreign proxy remote, session branch — into
|
|
605
|
+
`~/.agents/projects/<name>/`, nesting a repo inside the private repo, which a later
|
|
606
|
+
sync's `git add -A` would push as a bare gitlink (and `sync`'s copy-back had the same
|
|
607
|
+
swallow, with `overwrite=True`). Both paths now log a skip and leave the checkout in
|
|
608
|
+
place; `link --force` keeps an escape hatch that backs the checkout up to
|
|
609
|
+
`.agents.bak*` (git state intact) and links the store.
|
|
610
|
+
|
|
611
|
+
- fix: `dotagents sync` now authenticates the private repo directly against github.com
|
|
612
|
+
when `DOTAGENTS_AGENTS_TOKEN` is set — and on a hosted runner that rewrites github
|
|
613
|
+
traffic to a scoped in-session proxy, bypasses the rewrite — so a **standalone**
|
|
614
|
+
`dotagents sync` no longer 403s. Previously only the private-sync Stop hook worked
|
|
615
|
+
(it sources `_agents-git-auth.sh`); a direct CLI run had no bypass, so its pull failed
|
|
616
|
+
(`could not read Password`) and its push returned HTTP 403 through the proxy. The CLI
|
|
617
|
+
now ports that logic: a per-command `-c` credential helper in a normal environment, or
|
|
618
|
+
an isolated `GIT_CONFIG_GLOBAL` (identity + CA bundle preserved) that skips the rewrite
|
|
619
|
+
when one is active. The token is still read from the environment at auth time and never
|
|
620
|
+
written to `.git/config`.
|
|
621
|
+
- fix: `tools/cloud-setup.sh` no longer lets a single container-start clone failure
|
|
622
|
+
permanently disable the environment. The clone often loses a race with egress/proxy
|
|
623
|
+
readiness; previously it `exit 0`'d on the first failure, skipping the hook-wiring
|
|
624
|
+
step — so the SessionStart hook (which can itself re-clone) was never registered and
|
|
625
|
+
nothing ever recovered. Now the clone retries with backoff (5 attempts), and if it
|
|
626
|
+
still fails the script persists a copy of itself and wires a SessionStart **recovery
|
|
627
|
+
hook** that re-runs the bootstrap next session (egress is up by then); the first
|
|
628
|
+
successful run merges the private-sync hooks and removes the recovery hook.
|
|
629
|
+
- fix: `tools/cloud-setup.sh` also wires that recovery hook when
|
|
630
|
+
`DOTAGENTS_AGENTS_REMOTE` is **unset at setup time**, not only on clone failure.
|
|
631
|
+
Hosted runners often expose the remote/token secrets to session processes but not
|
|
632
|
+
to the setup-script phase, so the first bootstrap had no remote to clone and its
|
|
633
|
+
no-remote branch just `exit 0`'d, leaving nothing to retry — the environment stayed
|
|
634
|
+
dead every session (observed: setup ran the correct one-liner and emitted only the
|
|
635
|
+
banner + `skipping` line, ~154 bytes, no clone). The branch now persists the recovery
|
|
636
|
+
hook like the exhausted-clone path, so the next session — where the secret is present
|
|
637
|
+
— clones and self-removes the hook. A genuinely remote-less environment just re-skips
|
|
638
|
+
each session (idempotent; the hook never duplicates). (Durable fix is still
|
|
639
|
+
to expose the secrets to the Setup Script phase so the first container succeeds.)
|
|
640
|
+
- fix: `.gitignore` templates and `dotagents link` now use a slashless `.agents`
|
|
641
|
+
instead of `.agents/`. `link` creates `.agents` as a *symlink*, which git treats
|
|
642
|
+
as a file, so the directory-only `.agents/` pattern never actually ignored it —
|
|
643
|
+
the link showed up as untracked in every project. `_gitignore_excludes_agents`
|
|
644
|
+
is now symlink-aware (a bare `.agents/` no longer counts as excluding a symlinked
|
|
645
|
+
link, so the WARN fires), and the reference template, REPO.md guidance, and the
|
|
646
|
+
starter `_overlay/AGENTS.md` Leakage rule all recommend `.agents`.
|
|
647
|
+
- docs: recommend `curl … -o file && sh file` over `curl … | sh` for the setup-script
|
|
648
|
+
field (README, `kb/PRIVATE_SYNC.md`, `tools/cloud-setup.sh` header). With a pipe the
|
|
649
|
+
field's exit code is `sh`'s (0 on empty stdin), so a failed fetch at container start
|
|
650
|
+
is silently reported as success; `&&` propagates the fetch failure to the setup log.
|
|
651
|
+
|
|
652
|
+
### Changed
|
|
653
|
+
|
|
654
|
+
- refactor: move the cloud bootstrap from `overlays/private-sync/hooks/cloud-setup.sh` to
|
|
655
|
+
top-level `tools/cloud-setup.sh` (public, required tooling) so a fresh cloud container
|
|
656
|
+
can fetch-and-run it from the public repo instead of pasting its contents — the web
|
|
657
|
+
environment setup-script field becomes a one-liner
|
|
658
|
+
(`curl -fsSL …/tools/cloud-setup.sh | sh`) that stays current on every container start.
|
|
659
|
+
Docs (README, `kb/PRIVATE_SYNC.md`) updated to the download bootstrap.
|
|
660
|
+
- fix: `tools/cloud-setup.sh` prints `starting`/`done` banners (so a setup-script log
|
|
661
|
+
proves whether it executed — a blank log means the field never invoked it, a config
|
|
662
|
+
issue) and `mkdir -p "$HOME"` before `git config --global` (which fails if HOME isn't
|
|
663
|
+
created yet in some setup contexts).
|
|
664
|
+
|
|
665
|
+
## [0.2.0] - 2026-07-19
|
|
666
|
+
|
|
667
|
+
### Changed
|
|
668
|
+
|
|
669
|
+
- chore: migrate the CLI to `duho>=0.3.3` (was `>=0.1.1`). duho's Plan-13 `Args`/`Cmd`
|
|
670
|
+
split means commands are now `class X(LoggingArgs, Cmd)` with a `__call__` entrypoint
|
|
671
|
+
(was a bare `LoggingArgs` with `__run__`) and the umbrella root is
|
|
672
|
+
`class Dotagents(LoggingArgs, Cli)`. Field declarations (annotation + help string +
|
|
673
|
+
flags tuple) are unchanged. Bumped the `build-pyz` vendored `duho` default to 0.3.3.
|
|
674
|
+
- fix: restore full flag/help fidelity in the built `dotagents.pyz` under duho 0.3.3.
|
|
675
|
+
duho discovers each field's flags + help by AST-parsing its module source, and inside
|
|
676
|
+
a zipapp the zip-internal `__file__` isn't readable — degrading `--from` to `--from-`,
|
|
677
|
+
the `link` positional to `--path`, and dropping help text. `cli.main` now repoints the
|
|
678
|
+
affected module sources (`dotagents.cli`, `duho.presets`) to extracted temp files
|
|
679
|
+
before dispatch; a no-op for a plain install.
|
|
680
|
+
|
|
681
|
+
### Added
|
|
682
|
+
|
|
683
|
+
- feat: private-agents git sync — `dotagents link` symlinks a project's `.agents` to a
|
|
684
|
+
per-project store under the global `~/.agents/projects/<name>` (basename-keyed, so a
|
|
685
|
+
local and a cloud checkout converge on the same store), adopting an existing real
|
|
686
|
+
`.agents/` into an empty store on the first link; `--copy` mirrors it as a real dir
|
|
687
|
+
for no-symlink environments (with automatic fallback), `--force` handles conflicts.
|
|
688
|
+
`dotagents sync` runs `git pull --rebase`/commit/push on the private repo, copies a
|
|
689
|
+
copy-mode project's `.agents` back into its store first (`--project`), and bootstraps
|
|
690
|
+
a fresh repo in one command (`--remote`). Logic in `src/dotagents/_link.py`; the model
|
|
691
|
+
keeps per-user config and every project's private `.agents` in one private repo while
|
|
692
|
+
the public project repos track none of it (the Leakage rule already `.gitignore`s
|
|
693
|
+
`.agents/`).
|
|
694
|
+
- feat: `overlays/private-sync/` overlay — `kb/PRIVATE_SYNC.md` (the model, commands,
|
|
695
|
+
first-time + cloud setup, auth, gotchas) plus `hooks/private-sync-{start,stop}.sh`
|
|
696
|
+
(SessionStart clone/pull + link, Stop sync-back) and a `settings.snippet.json` for
|
|
697
|
+
`~/.claude/settings.json`, so cloud sessions link and sync automatically. Cloud auth is
|
|
698
|
+
a fine-grained PAT via `DOTAGENTS_AGENTS_TOKEN`, wired through a git credential helper
|
|
699
|
+
that reads it from the environment (never persisted to `.git/config`);
|
|
700
|
+
`hooks/_agents-git-auth.sh` auto-detects a hosted-runner `github.com`→in-session-proxy
|
|
701
|
+
`insteadOf` rewrite and bypasses it (isolated git config) so token auth reaches the
|
|
702
|
+
real github.com for a private repo outside the session's scope. `hooks/cloud-setup.sh`
|
|
703
|
+
is a self-contained container-start bootstrap (inlines auth + bypass, so it runs before
|
|
704
|
+
`~/.agents` exists) that clones/pulls the repo, installs the CLI, and links the project
|
|
705
|
+
— for the web environment's setup-script field, solving the first-clone chicken-and-egg
|
|
706
|
+
the SessionStart hook can't.
|
|
707
|
+
- feat: installable `dotagents` CLI package (`src/dotagents/`, built on `duho` for
|
|
708
|
+
the argument surface and `pathlib_next` for copy/URI handling) exposing `init`
|
|
709
|
+
(lay down the neutral base overlay), `install` (base plus opt-in overlays via
|
|
710
|
+
repeatable `--overlays <path>`, copied additively), `audit` (wraps
|
|
711
|
+
`tools/audit_config.py`), and `build-pyz` (vendors pinned `duho`/`pathlib_next`
|
|
712
|
+
via `pip install --target` + `zipapp` into a self-contained, downloadable
|
|
713
|
+
`dotagents.pyz`). `init`'s `AGENTS.md`/`CLAUDE.md` are merged as a
|
|
714
|
+
marker-delimited managed block so re-running never clobbers customizations
|
|
715
|
+
outside the block. `install --bin-dir` writes `dotagents`/`dotagents.cmd`
|
|
716
|
+
wrappers. `install.py` is a thin shim over `dotagents.cli.main()`.
|
|
717
|
+
- Config content is a **neutral base overlay** (`src/dotagents/_overlay/` — the
|
|
718
|
+
`AGENTS.md` scaffolding + design-log convention `init` writes) plus **opt-in
|
|
719
|
+
overlays** (`overlays/<name>/`): `flows` (PLAN/EXEC/REVIEW/REPO + MODELS),
|
|
720
|
+
`recovery`, `references`, `python`/`node`/`rust`, `agents`, `tools`. Each carries
|
|
721
|
+
an `overlay.toml` manifest for a future `dotagents overlays` subcommand.
|
|
722
|
+
- Repo layout: the CLI in `src/dotagents/`, config overlays in `overlays/`, required
|
|
723
|
+
tooling in top-level `tools/` (`audit_config.py` and a personal leak scanner); repo root holds
|
|
724
|
+
the installer, CI, repo-development directives, and the tracked, sanitized
|
|
725
|
+
`.agents/` design log (index + per-decision files) + plans. `audit_config.py` has
|
|
726
|
+
`--repo-hygiene` (scans tracked files for personal/machine-specific leftovers).
|