Changelog
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Fixed
- fix:
dotagents env --format export/dotenv/fishnow emit a POSIX PATH on Windows, instead of the OS-nativeC:\...;C:\...form. This is the exact command the ClaudeSessionStarthook appends into$CLAUDE_ENV_FILE, which Claude sources before every subsequent Bash tool call in the session — an unconverted PATH broke command lookup (git,grep,head,python, ...) for the rest of the session once poisoned, not just once. PATH-shaped values are now converted per segment: backslash to forward-slash,;to:, and a drive letter to its MSYS mount point (C:/...->/c/...— required for PATH lookups specifically; slash direction alone does not work in MSYS2/Cygwin bash).PATHEXTis dropped (no POSIX meaning). A handful of real Windows env vars with parentheses in their names (ProgramFiles(x86)) are also dropped for these formats —export FOO(X86)=...is a bash syntax error, not a bad value, and aborts sourcing the rest of the file.powershell/cmd/json/ini/yamlare unaffected. - fix: a UNC PATH segment (
\\server\share\...) no longer collapses to a single leading slash (/server/share/...) during the POSIX conversion above — MSYS requires the double-slash UNC root (//server/share/...) to resolve it.
[0.3.1] - 2026-07-24
Patch release: the PATH/POSIX-conversion fix above (the only change since 0.3.0).
[0.3.0] - 2026-07-24
Changed
-
BREAKING —
dotagents link/dotagents syncare gone from the CLI. They are the private-sync workflow's commands, not dotagents' core, so they moved — together with the logic behind them (src/dotagents/_link.py) — into the opt-inprivate-syncoverlay, and were renamed to say what they act on:dotagents link-project . # was: dotagents link . dotagents sync-project -m "msg" # was: dotagents sync -m "msg"
Install the overlay to get them back: dotagents overlays add private-sync --source
<overlays-checkout>. A plain dotagents now ships no private-sync workflow at all;
its whole command surface is init / build-pyz / context / env / overlays,
and everything else is discovered from an overlay or from your own cmds/ modules.
tools/cloud-setup.sh installs the overlay before linking, so the cloud bootstrap
is unaffected.
- The bundled dotagents/cmds/ directory now ships no command module of its own, but
init still creates it: it is the documented drop-in point for your own commands
(a README.md beside it explains the shape and the precedence rules).
Added
- feat:
initwires agent hooks and links the shared skills dir, for each active agent with a published hook schema (today: Claude and Codex).--no-hooksskips it.
Claude (~/.claude/settings.json) gets SessionStart, which appends
dotagents env --diff --format export to $CLAUDE_ENV_FILE and then runs
dotagents context — Claude sources that file before each Bash command and injects
the hook's stdout into the session context, so both the env layers and the assembled
context reach the session automatically. It also gets CwdChanged, surfacing a
directory's AGENTS.md. The env redirect appends and is guarded against an
unset variable, both as the hooks docs require.
Codex (~/.codex/hooks.json, or $CODEX_HOME) gets SessionStart running
dotagents context; its hook JSON is structurally identical to Claude's. We write
hooks.json rather than touching your config.toml for hooks.
Codex's env arrives differently: it has no $CLAUDE_ENV_FILE equivalent, reads no
.env files, and has no event that fires before config load, so
dotagents init --agents codex writes a # dotagents:begin/end managed block
containing [shell_environment_policy].set into config.toml. Only on an
explicit --agents — this edits your main config with values that go stale, so
auto-detection never triggers it. The block is appended and refreshed in place
(everything outside the markers is untouched) and set merges rather than
replaces. The values are a static snapshot: re-run init after changing your env
layers. Identity vars describe the target agent, so initializing from Claude still
writes AGENT = "codex"; PATH is excluded, since set overrides per subprocess
and a baked-in PATH would replace the inherited one.
The merge is additive and idempotent: unrelated keys and hooks you wrote yourself survive verbatim, malformed entries are dropped rather than raising, and re-running writes nothing.
init also links <scope>/skills/ into the agent's config dir, closing the last
mile for overlay-published skills — publishing only helps if the agent reads that
directory. Symlink where the OS permits, copy otherwise (a copy is a snapshot;
re-run init to refresh).
- feat:
tools/leak_check.pynow also scans commit messages (current branch history) for agent-session trailers/URLs — aClaude-Session:trailer orclaude.ai/code/sessionlink — and exits 1 on any hit. The trailer is auto-added by the agent harness and exposes a session id in public history if it slips through; the pre-existing tracked-file scan didn't cover commit messages.flows/REPO.mdrelease discipline documents the check and thegit filter-branch --msg-filterremediation for one that already landed. - feat:
tools/cloud-setup.shstep 5 wireshooks/settings.snippet.jsoninto the user-level~/.claude/settings.json(idempotent JSON merge, preserves existing settings/hooks). A fresh cloud container has no settings file and nothing else created one, so the SessionStart pull/link and Stop sync-back hooks never ran — the private repo went stale and session changes were silently never pushed back.kb/PRIVATE_SYNC.mddocuments the auto-wiring (manual merge still applies on local machines).
Fixed
-
fix:
dotagents link/syncnever adopt or copy back a<project>/.agentsthat is itself a git checkout (.gitpresent — dir, or file for worktrees). A hosted-runner session that lists the agents repo as a source gets it cloned to<project>/.agentsby the harness; first-link adoption then moved that entire checkout —.git, foreign proxy remote, session branch — into~/.agents/projects/<name>/, nesting a repo inside the private repo, which a later sync'sgit add -Awould push as a bare gitlink (andsync's copy-back had the same swallow, withoverwrite=True). Both paths now log a skip and leave the checkout in place;link --forcekeeps an escape hatch that backs the checkout up to.agents.bak*(git state intact) and links the store. -
fix:
dotagents syncnow authenticates the private repo directly against github.com whenDOTAGENTS_AGENTS_TOKENis set — and on a hosted runner that rewrites github traffic to a scoped in-session proxy, bypasses the rewrite — so a standalonedotagents syncno longer 403s. Previously only the private-sync Stop hook worked (it sources_agents-git-auth.sh); a direct CLI run had no bypass, so its pull failed (could not read Password) and its push returned HTTP 403 through the proxy. The CLI now ports that logic: a per-command-ccredential helper in a normal environment, or an isolatedGIT_CONFIG_GLOBAL(identity + CA bundle preserved) that skips the rewrite when one is active. The token is still read from the environment at auth time and never written to.git/config. - fix:
tools/cloud-setup.shno longer lets a single container-start clone failure permanently disable the environment. The clone often loses a race with egress/proxy readiness; previously itexit 0'd on the first failure, skipping the hook-wiring step — so the SessionStart hook (which can itself re-clone) was never registered and nothing ever recovered. Now the clone retries with backoff (5 attempts), and if it still fails the script persists a copy of itself and wires a SessionStart recovery hook that re-runs the bootstrap next session (egress is up by then); the first successful run merges the private-sync hooks and removes the recovery hook. - fix:
tools/cloud-setup.shalso wires that recovery hook whenDOTAGENTS_AGENTS_REMOTEis unset at setup time, not only on clone failure. Hosted runners often expose the remote/token secrets to session processes but not to the setup-script phase, so the first bootstrap had no remote to clone and its no-remote branch justexit 0'd, leaving nothing to retry — the environment stayed dead every session (observed: setup ran the correct one-liner and emitted only the banner +skippingline, ~154 bytes, no clone). The branch now persists the recovery hook like the exhausted-clone path, so the next session — where the secret is present — clones and self-removes the hook. A genuinely remote-less environment just re-skips each session (idempotent; the hook never duplicates). (Durable fix is still to expose the secrets to the Setup Script phase so the first container succeeds.) - fix:
.gitignoretemplates anddotagents linknow use a slashless.agentsinstead of.agents/.linkcreates.agentsas a symlink, which git treats as a file, so the directory-only.agents/pattern never actually ignored it — the link showed up as untracked in every project._gitignore_excludes_agentsis now symlink-aware (a bare.agents/no longer counts as excluding a symlinked link, so the WARN fires), and the reference template, REPO.md guidance, and the starter_overlay/AGENTS.mdLeakage rule all recommend.agents. - docs: recommend
curl … -o file && sh fileovercurl … | shfor the setup-script field (README,kb/PRIVATE_SYNC.md,tools/cloud-setup.shheader). With a pipe the field's exit code issh's (0 on empty stdin), so a failed fetch at container start is silently reported as success;&&propagates the fetch failure to the setup log.
Changed
- refactor: move the cloud bootstrap from
overlays/private-sync/hooks/cloud-setup.shto top-leveltools/cloud-setup.sh(public, required tooling) so a fresh cloud container can fetch-and-run it from the public repo instead of pasting its contents — the web environment setup-script field becomes a one-liner (curl -fsSL …/tools/cloud-setup.sh | sh) that stays current on every container start. Docs (README,kb/PRIVATE_SYNC.md) updated to the download bootstrap. - fix:
tools/cloud-setup.shprintsstarting/donebanners (so a setup-script log proves whether it executed — a blank log means the field never invoked it, a config issue) andmkdir -p "$HOME"beforegit config --global(which fails if HOME isn't created yet in some setup contexts).
[0.2.0] - 2026-07-19
Changed
- chore: migrate the CLI to
duho>=0.3.3(was>=0.1.1). duho's Plan-13Args/Cmdsplit means commands are nowclass X(LoggingArgs, Cmd)with a__call__entrypoint (was a bareLoggingArgswith__run__) and the umbrella root isclass Dotagents(LoggingArgs, Cli). Field declarations (annotation + help string + flags tuple) are unchanged. Bumped thebuild-pyzvendoredduhodefault to 0.3.3. - fix: restore full flag/help fidelity in the built
dotagents.pyzunder duho 0.3.3. duho discovers each field's flags + help by AST-parsing its module source, and inside a zipapp the zip-internal__file__isn't readable — degrading--fromto--from-, thelinkpositional to--path, and dropping help text.cli.mainnow repoints the affected module sources (dotagents.cli,duho.presets) to extracted temp files before dispatch; a no-op for a plain install.
Added
- feat: private-agents git sync —
dotagents linksymlinks a project's.agentsto a per-project store under the global~/.agents/projects/<name>(basename-keyed, so a local and a cloud checkout converge on the same store), adopting an existing real.agents/into an empty store on the first link;--copymirrors it as a real dir for no-symlink environments (with automatic fallback),--forcehandles conflicts.dotagents syncrunsgit pull --rebase/commit/push on the private repo, copies a copy-mode project's.agentsback into its store first (--project), and bootstraps a fresh repo in one command (--remote). Logic insrc/dotagents/_link.py; the model keeps per-user config and every project's private.agentsin one private repo while the public project repos track none of it (the Leakage rule already.gitignores.agents/). - feat:
overlays/private-sync/overlay —kb/PRIVATE_SYNC.md(the model, commands, first-time + cloud setup, auth, gotchas) plushooks/private-sync-{start,stop}.sh(SessionStart clone/pull + link, Stop sync-back) and asettings.snippet.jsonfor~/.claude/settings.json, so cloud sessions link and sync automatically. Cloud auth is a fine-grained PAT viaDOTAGENTS_AGENTS_TOKEN, wired through a git credential helper that reads it from the environment (never persisted to.git/config);hooks/_agents-git-auth.shauto-detects a hosted-runnergithub.com→in-session-proxyinsteadOfrewrite and bypasses it (isolated git config) so token auth reaches the real github.com for a private repo outside the session's scope.hooks/cloud-setup.shis a self-contained container-start bootstrap (inlines auth + bypass, so it runs before~/.agentsexists) that clones/pulls the repo, installs the CLI, and links the project — for the web environment's setup-script field, solving the first-clone chicken-and-egg the SessionStart hook can't. - feat: installable
dotagentsCLI package (src/dotagents/, built onduhofor the argument surface andpathlib_nextfor copy/URI handling) exposinginit(lay down the neutral base overlay),install(base plus opt-in overlays via repeatable--overlays <path>, copied additively),audit(wrapstools/audit_config.py), andbuild-pyz(vendors pinnedduho/pathlib_nextviapip install --target+zipappinto a self-contained, downloadabledotagents.pyz).init'sAGENTS.md/CLAUDE.mdare merged as a marker-delimited managed block so re-running never clobbers customizations outside the block.install --bin-dirwritesdotagents/dotagents.cmdwrappers.install.pyis a thin shim overdotagents.cli.main(). - Config content is a neutral base overlay (
src/dotagents/_overlay/— theAGENTS.mdscaffolding + design-log conventioninitwrites) plus opt-in overlays (overlays/<name>/):flows(PLAN/EXEC/REVIEW/REPO + MODELS),recovery,references,python/node/rust,agents,tools. Each carries anoverlay.tomlmanifest for a futuredotagents overlayssubcommand. - Repo layout: the CLI in
src/dotagents/, config overlays inoverlays/, required tooling in top-leveltools/(audit_config.py,leak_check.py); repo root holds the installer, CI, repo-development directives, and the tracked, sanitized.agents/design log (index + per-decision files) + plans.audit_config.pyhas--repo-hygiene(scans tracked files for personal/machine-specific leftovers).