dotagents-cli 0.7.2__tar.gz → 0.7.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/CHANGELOG.md +36 -1
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/PKG-INFO +1 -1
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/guide/authoring.md +19 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/guide/commands.md +44 -6
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/guide/overlays.md +1 -1
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/guide/private-sync.md +8 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/AGENTS.md +11 -1
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/__init__.py +1 -1
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_overlay/dotagents/cmds/findings.py +104 -5
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_findings_cmd.py +81 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/.gitignore +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/LICENSE +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/README.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/agents.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/cli.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/commands.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/context.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/env.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/fs.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/hooks.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/merge.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/overlays.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/reference.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/resources.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/scope.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/skills.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/sources.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/api/wrappers.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/changelog.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/guide/install.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/docs/index.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/install.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/mkdocs.yml +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/pyproject.toml +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/__main__.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_agents.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_context.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_env.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_fs.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_hooks.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_ignore.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_merge.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_overlay/dotagents/cmds/launch.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_overlay/dotagents/hooks/preinvocation_antigravity_context.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_overlay/dotagents/hooks/pretooluse_codex_env.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_overlay/dotagents/hooks/sessionstart_codex_context.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_overlay/dotagents/templates/AGENTS.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_overlay/dotagents/templates/PROJECT.md +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_overlays.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_resources.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_scope.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_skills.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_sources.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_wrappers.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/cli/__init__.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/cli/_common.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/cli/about.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/cli/build_pyz.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/cli/context.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/cli/env.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/cli/init.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/cli/overlays.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/cli/path.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/py.typed +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/_helpers.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/_shell.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/conftest.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_about.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_agents.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_api_header.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_audit_leak.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_build_pyz.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_cli_main.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_cloud_setup.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_cmds_discovery.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_context.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_env.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_env_cli_scope.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_env_format.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_env_into.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_env_regressions.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_env_safety.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_fix_adapters.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_fix_cli.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_fix_env.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_fix_hooks.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_fix_init.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_fix_overlays.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_fix_packaging.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_fix_scope_context.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_fix_skills_adapters.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_git_ssl_revoke.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_hooks.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_ignore.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_init_base.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_last_mile.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_launch_cmd.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_local_additions.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_mcp.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_no_internal_labels.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_overlay_libs.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_overlays_cli.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_overlays_command.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_path_timeout_frontmatter.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_powershell_env_hook.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_quoting_injection_safety.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_scope.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_sources.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_sources_uri.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_wire_hooks.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tests/test_wrappers.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tools/audit.py +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tools/cloud-setup.sh +0 -0
- {dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/tools/pyz_smoke.sh +0 -0
|
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.4] - 2026-10-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `findings index --requote` quotes, in the findings already on disk, the frontmatter
|
|
15
|
+
values YAML cannot read as they stand. Only those lines change: bodies, the other
|
|
16
|
+
lines and line endings are kept.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- The private-sync guide describes the default ignore rules the overlay's
|
|
21
|
+
`sync-project` now writes, once, into the `.gitignore` of a store kept in git:
|
|
22
|
+
`scratch/`, `tmp/`, `__pycache__/`, hidden files and the machine-local overrides
|
|
23
|
+
stay out of the private repository.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- `findings` writes frontmatter any YAML parser reads. A description that opens with
|
|
28
|
+
`[` or a backtick, or holds `: `, was written bare, and a markdown viewer or any
|
|
29
|
+
other tool reading the frontmatter rejected the file. Such a value is now a
|
|
30
|
+
double-quoted string; a plain one is still written bare, and a finding written
|
|
31
|
+
before reads as it did.
|
|
32
|
+
|
|
33
|
+
## [0.7.3] - 2026-10-04
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- The command guide and the shipped API header now say that every command except
|
|
38
|
+
`env`, built-in or discovered, is served as an MCP tool when `dotagents` starts with
|
|
39
|
+
`DOTAGENTS_MCP=stdio`, and that `AGENT_HELP=1` turns `--help` into a JSON document.
|
|
40
|
+
The authoring guide asks overlays to ship command modules rather than loose
|
|
41
|
+
scripts, and skills to name the tools they use.
|
|
42
|
+
|
|
10
43
|
## [0.7.2] - 2026-10-03
|
|
11
44
|
|
|
12
45
|
### Added
|
|
@@ -1384,7 +1417,9 @@ Patch release: the PATH/POSIX-conversion fix above (the only change since 0.3.0)
|
|
|
1384
1417
|
affected module sources (`dotagents.cli`, `duho.presets`) to extracted temp files
|
|
1385
1418
|
before dispatch; a no-op for a plain install.
|
|
1386
1419
|
|
|
1387
|
-
[Unreleased]: https://github.com/jose-pr/dotagents/compare/v0.7.
|
|
1420
|
+
[Unreleased]: https://github.com/jose-pr/dotagents/compare/v0.7.4...HEAD
|
|
1421
|
+
[0.7.4]: https://github.com/jose-pr/dotagents/compare/v0.7.3...v0.7.4
|
|
1422
|
+
[0.7.3]: https://github.com/jose-pr/dotagents/compare/v0.7.2...v0.7.3
|
|
1388
1423
|
[0.7.2]: https://github.com/jose-pr/dotagents/compare/v0.7.1...v0.7.2
|
|
1389
1424
|
[0.7.1]: https://github.com/jose-pr/dotagents/compare/v0.7.0...v0.7.1
|
|
1390
1425
|
[0.7.0]: https://github.com/jose-pr/dotagents/compare/v0.6.3...v0.7.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: dotagents-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.4
|
|
4
4
|
Summary: The dotagents CLI: install and manage a portable ~/.agents config for AI coding agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/jose-pr/dotagents/
|
|
6
6
|
Project-URL: Repository, https://github.com/jose-pr/dotagents
|
|
@@ -149,7 +149,26 @@ parser cannot be built. The bundled `findings` and `launch` are ordinary command
|
|
|
149
149
|
modules discovered from the package, and the private-sync overlay ships
|
|
150
150
|
`link-project` / `sync-project` from its own `cmds/` the same way.
|
|
151
151
|
|
|
152
|
+
**Prefer a command module to a loose script.** A script under `tools/` is something
|
|
153
|
+
an agent has to find, quote and spawn. A command module is discovered, shows up in
|
|
154
|
+
`dotagents --help`, and is served as an MCP tool with a typed schema when `dotagents`
|
|
155
|
+
runs with `DOTAGENTS_MCP=stdio` (see
|
|
156
|
+
[Commands → As tools for an agent](commands.md#as-tools-for-an-agent)). Write it to be
|
|
157
|
+
a good tool:
|
|
158
|
+
|
|
159
|
+
- the logic lives in the command class — `__call__` and its methods — not in one
|
|
160
|
+
function that `__call__` forwards every field to;
|
|
161
|
+
- each field's help says what the value is, since it becomes the schema's description;
|
|
162
|
+
- the output is short: one line for a change, the smallest useful part for a read,
|
|
163
|
+
`--json` for a program;
|
|
164
|
+
- it never prompts, and a command that deletes or publishes needs an explicit flag.
|
|
165
|
+
|
|
152
166
|
## Skills
|
|
153
167
|
|
|
154
168
|
Put `skills/<skill-name>/` directories in your overlay to publish shared skills into
|
|
155
169
|
the scope on install. See [Overlays → Skills](overlays.md#skills).
|
|
170
|
+
|
|
171
|
+
A skill that relies on commands says so, in its own text: which `dotagents` commands
|
|
172
|
+
it uses, that they are available as MCP tools (`dotagents.<command>`) when the
|
|
173
|
+
server is registered, and the command-line form to fall back to when it is not. An
|
|
174
|
+
agent reading the skill should not have to discover either.
|
|
@@ -59,6 +59,38 @@ into `<store>/dotagents/cmds/` (create the dir — `init` does not) and it becom
|
|
|
59
59
|
subcommand, no registration needed; see
|
|
60
60
|
[Authoring → Custom commands](authoring.md#custom-commands).
|
|
61
61
|
|
|
62
|
+
## As tools for an agent
|
|
63
|
+
|
|
64
|
+
Every command above except `env`, and every discovered one, is also an **MCP tool**.
|
|
65
|
+
Start `dotagents` with `DOTAGENTS_MCP=stdio` in its environment and it serves that
|
|
66
|
+
command set over stdio instead of running a command:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
DOTAGENTS_MCP=stdio dotagents # register this as a stdio MCP server in your agent
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- One tool per leaf command, named with dots: `dotagents.findings.add`,
|
|
73
|
+
`dotagents.overlays.list`, and for an overlay's commands `dotagents.<command>`.
|
|
74
|
+
- A tool's input schema is generated from the command's fields; its result is what
|
|
75
|
+
the command prints.
|
|
76
|
+
- The command set is discovered once, when the server starts, for the directory it
|
|
77
|
+
starts in. Restart it after installing an overlay or changing project.
|
|
78
|
+
|
|
79
|
+
Use it for a command an agent calls many times in a session — a findings queue, a
|
|
80
|
+
plan's progress, a lookup. A tool call skips the interpreter start-up and the
|
|
81
|
+
command discovery that every `dotagents <command>` process pays, and its arguments
|
|
82
|
+
are typed values, not strings quoted through a shell.
|
|
83
|
+
|
|
84
|
+
`env` is not served: it prints resolved values, which can be secrets. Everything
|
|
85
|
+
else is, including the commands that change the store (`init`, `overlays add`,
|
|
86
|
+
`overlays remove`). Register the server only where the agent is meant to be able to
|
|
87
|
+
run them.
|
|
88
|
+
|
|
89
|
+
For a machine-readable description without a server, set `AGENT_HELP=1` and ask for
|
|
90
|
+
`--help`: `dotagents findings --help` then prints one JSON document for that command
|
|
91
|
+
tree. It is complete and long; a single subcommand's plain `--help` is shorter when
|
|
92
|
+
that is all that is needed.
|
|
93
|
+
|
|
62
94
|
## init
|
|
63
95
|
|
|
64
96
|
See [Install](install.md) for the full walkthrough. In brief:
|
|
@@ -410,6 +442,7 @@ dotagents findings done <name> -r "fixed in _context; test added"
|
|
|
410
442
|
dotagents findings reopen <name>
|
|
411
443
|
dotagents findings remove <name> # an active finding recorded by mistake
|
|
412
444
|
dotagents findings index # regenerate INDEX.md after hand edits
|
|
445
|
+
dotagents findings index --requote # ...and quote frontmatter values YAML cannot read
|
|
413
446
|
dotagents findings path # where this scope's queue lives
|
|
414
447
|
```
|
|
415
448
|
|
|
@@ -431,6 +464,11 @@ dotagents findings path # where this scope's queue lives
|
|
|
431
464
|
without a frontmatter is still listed (name = file stem, description = its
|
|
432
465
|
first heading or line) and gains one the first time the command rewrites it;
|
|
433
466
|
a note that is not UTF-8 is skipped with a warning and never rewritten.
|
|
467
|
+
- The frontmatter is YAML a viewer or another tool can parse: a value is written
|
|
468
|
+
bare when YAML reads it back as the same text, and double-quoted otherwise (a
|
|
469
|
+
description that opens with `[` or a backtick, or holds `: `). `index --requote`
|
|
470
|
+
does the same for findings an earlier version wrote, and for hand-written ones,
|
|
471
|
+
changing only the lines that need it.
|
|
434
472
|
- `add` refuses the names `index` and `readme` (on a case-insensitive
|
|
435
473
|
filesystem they are `INDEX.md` / `README.md`). `done` and `reopen` refuse to
|
|
436
474
|
move onto an existing file, and `remove` deletes active findings only: reopen
|
|
@@ -501,13 +539,13 @@ There is no `dotagents audit`. The dotagents source repo has its own
|
|
|
501
539
|
it is not a validator for an installed `~/.agents` and is deliberately not shipped
|
|
502
540
|
in the package or the `.pyz`.
|
|
503
541
|
|
|
504
|
-
###
|
|
542
|
+
### Pre-push scanning (an overlay's command)
|
|
505
543
|
|
|
506
|
-
Scanning a repo for
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
544
|
+
Scanning a repo for leaks before publishing it — machine paths, private plan names,
|
|
545
|
+
`.agents/` refs, agent-session trailers in commit messages — enforces a convention
|
|
546
|
+
rather than dotagents' own mechanism, so the package ships no such command. The
|
|
547
|
+
opt-in `engineering` overlay does: `dotagents leak-check <repo>`. Its patterns for
|
|
548
|
+
what is personal to you stay in a machine-local file, never in a repository.
|
|
511
549
|
|
|
512
550
|
## link-project / sync-project
|
|
513
551
|
|
|
@@ -44,7 +44,7 @@ or `--repo <checkout>/overlays` for a local checkout of the branch.
|
|
|
44
44
|
|
|
45
45
|
| Overlay | What it carries |
|
|
46
46
|
| --- | --- |
|
|
47
|
-
| `engineering` | One opinionated engineering process. `rules/ENGINEERING.md` — always-on discipline merged into `AGENTS.md` (commit shape, release-tag consent, benchmark-as-evidence, token discipline, draft follow-ups). `flows/` — `PLAN.md` (a strong model writes precise, autonomous plans), `EXEC.md` (a cheaper model executes them without re-deriving context), `REVIEW.md` (file-threaded multi-agent plan review), `REPO.md` (the repo standard); `kb/MODELS.md` (executor/model selection). `references/` — language-neutral repo-file templates (README, CHANGELOG, LICENSE, `.gitignore`, docs-index, the plan template). `
|
|
47
|
+
| `engineering` | One opinionated engineering process. `rules/ENGINEERING.md` — always-on discipline merged into `AGENTS.md` (commit shape, release-tag consent, benchmark-as-evidence, token discipline, draft follow-ups). `flows/` — `PLAN.md` (a strong model writes precise, autonomous plans), `EXEC.md` (a cheaper model executes them without re-deriving context), `REVIEW.md` (file-threaded multi-agent plan review), `REPO.md` (the repo standard); `kb/MODELS.md` (executor/model selection). `references/` — language-neutral repo-file templates (README, CHANGELOG, LICENSE, `.gitignore`, docs-index, the plan template). `cmds/` — `plans`, `leak-check`, `eol`, `summarize-run` (command → verdict) and `compare-bench` (diff benchmark JSONs), each a `dotagents` command. The language overlays require it. |
|
|
48
48
|
| `python`, `node`, `rust` | Per-language `kb/` conventions + manifest templates + CI workflow templates. |
|
|
49
49
|
| `release` | A host-agnostic release helper: an agent-driven commit-plan loop plus tag/CI monitoring across GitHub (`gh`) and GitLab. |
|
|
50
50
|
| `private-sync` | The one-private-repo, per-project `.agents` model (`kb/PRIVATE_SYNC.md` + cloud hooks) — see [Private sync](private-sync.md). |
|
|
@@ -49,6 +49,14 @@ leaves the machine is a perfectly valid setup.
|
|
|
49
49
|
links the store instead.
|
|
50
50
|
- **Copy mode** (`--copy`) makes `.agents` a real directory rather than a symlink;
|
|
51
51
|
`sync-project` copies edits back into the store.
|
|
52
|
+
- **Default ignore rules.** A project's `.agents/scratch/` lives inside the store, so
|
|
53
|
+
the built-in git path would commit it. The first `sync-project` on that path
|
|
54
|
+
appends a block to the store's `.gitignore` that leaves out `scratch/`, `tmp/`,
|
|
55
|
+
`__pycache__/`, hidden files (`.*`, keeping `.gitignore`, `.gitattributes`,
|
|
56
|
+
`.gitkeep`, `.ignore` and `.dotagents-install.json`) and the machine-local
|
|
57
|
+
overrides (`*.local.*`, `local.env`). It is written once, under a marker line:
|
|
58
|
+
edit the block freely and keep that line. The overlay's `kb/PRIVATE_SYNC.md` has
|
|
59
|
+
the block in full.
|
|
52
60
|
|
|
53
61
|
## Cloud sessions
|
|
54
62
|
|
|
@@ -40,10 +40,20 @@ and an error saying a command is required, and exits 2.
|
|
|
40
40
|
| `path [-g] [--lib] [--format list\|native\|posix\|json]` | Print the bin dirs `env` puts on `PATH` (`--lib`: the existing lib dirs it puts on `PYTHONPATH`), highest precedence first; runs no env file. `posix` is `:`-joined, MSYS2 form on Windows. |
|
|
41
41
|
| `env [-g] [--format F] [--diff] [--cache] [--into FILE]` | Print the assembled environment (`--diff`: only what differs from the caller's; `--cache`: replay the previous output while `env_cache_key` holds, up to `ENV_CACHE_TTL`; `--into FILE`: write it into FILE as the one block this command owns there) in format `F`: `auto` (default: the calling shell), `export`/`posix`/`sh`/`bash`, `dotenv`/`env`, `powershell`/`pwsh`/`ps`, `cmd`/`bat`/`batch`, `fish`, `json`, `ini`, `yaml`. |
|
|
42
42
|
| `launch [AGENT] [-g] [--command PROG] [--no-context] [--inline] [--write-agent] [--dry-run] [-- ARGS...]` | Run the agent's CLI with the env applied and the context handed over; the exit code is the harness's (`128+N` on signal N). |
|
|
43
|
-
| `findings add\|list\|show\|done\|reopen\|remove\|index\|path [-g] [--dir D]` | The findings queue at `<store>/findings/` (`--dir` overrides): one markdown file per finding, `done` moves it to `processed/` with a required resolution, `INDEX.md` regenerated on every change. |
|
|
43
|
+
| `findings add\|list\|show\|done\|reopen\|remove\|index\|path [-g] [--dir D]` | The findings queue at `<store>/findings/` (`--dir` overrides): one markdown file per finding, `done` moves it to `processed/` with a required resolution, `INDEX.md` regenerated on every change. The frontmatter is valid YAML (a value is double-quoted when it must be); `index --requote` fixes values written bare in existing files, touching nothing else. |
|
|
44
44
|
| `about [--json]` | `dotagents-cli <version>`, then `<distribution> <version>` per bundled (`.pyz`) or installed package. |
|
|
45
45
|
| `build-pyz [--out P] [--python SHEBANG] [--duho-version V] [--pathlib-next-version V] [--extras a,b]` | Build a zipapp from a source checkout; errors elsewhere. |
|
|
46
46
|
|
|
47
|
+
**Every command but `env` is also an MCP tool.** With `DOTAGENTS_MCP=stdio` in its
|
|
48
|
+
environment, `dotagents` serves the command set — built-in, bundled and discovered —
|
|
49
|
+
over stdio instead of running one: a tool per leaf command, named with dots
|
|
50
|
+
(`dotagents.findings.add`, `dotagents.overlays.list`), its input schema generated from
|
|
51
|
+
the command's fields, its result the command's output. `env` is not served: it prints
|
|
52
|
+
resolved, possibly secret values. The set is fixed when the server starts, for the
|
|
53
|
+
scope it starts in. With `AGENT_HELP=1`, `--help` prints one
|
|
54
|
+
JSON document (`duho/agent-help@1`) for the command tree it is asked about, in place
|
|
55
|
+
of the human text.
|
|
56
|
+
|
|
47
57
|
Scope flags: `-g` / `--global` selects the user store; `--agents-dir D` overrides the
|
|
48
58
|
store the command resolves to (the project store for `init` / `overlays` / `findings`
|
|
49
59
|
without `-g`, the user store otherwise). For `env`, `context` and `launch`, `-g` means
|
{dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_overlay/dotagents/cmds/findings.py
RENAMED
|
@@ -37,6 +37,10 @@ line, the details in the body::
|
|
|
37
37
|
|
|
38
38
|
<the decision, evidence, or why no action was needed>
|
|
39
39
|
|
|
40
|
+
The frontmatter is YAML a viewer or another tool can parse: a value is written
|
|
41
|
+
bare when YAML reads it back as the same text, and as a double-quoted string
|
|
42
|
+
otherwise (a description that opens with ``[`` or a backtick, or holds ``: ``).
|
|
43
|
+
|
|
40
44
|
``INDEX.md`` is one line per finding, active first, then processed -- the cheap
|
|
41
45
|
thing to read at session start. Hand-written notes without frontmatter are
|
|
42
46
|
still listed (name = file stem, description = first heading or line), and gain
|
|
@@ -90,6 +94,69 @@ def slugify(text: str) -> str:
|
|
|
90
94
|
return slug
|
|
91
95
|
|
|
92
96
|
|
|
97
|
+
#: A value cannot be written bare when it opens with one of these: YAML reads
|
|
98
|
+
#: ``[`` as a list, a quote as a quoted string, a backtick as reserved.
|
|
99
|
+
_YAML_INDICATORS = frozenset("-?:,[]{}#&*!|>'\"%@`")
|
|
100
|
+
#: Bare words YAML reads as a boolean or as nothing rather than as text.
|
|
101
|
+
_YAML_WORDS = frozenset(("true", "false", "yes", "no", "on", "off", "null", "~"))
|
|
102
|
+
#: Line breaks to YAML that JSON leaves as they are, escaped inside a quoted value.
|
|
103
|
+
_YAML_BREAKS = {"\x85": "\\u0085", "
": "\\u2028", "
": "\\u2029"}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _yaml_scalar(value: str) -> str:
|
|
107
|
+
"""``value`` as a one-line YAML scalar: bare when YAML reads it back as the
|
|
108
|
+
same text, double-quoted when it would read something else (a list, a
|
|
109
|
+
mapping, a comment) or nothing at all."""
|
|
110
|
+
plain = (
|
|
111
|
+
value == value.strip()
|
|
112
|
+
and value[:1] not in _YAML_INDICATORS
|
|
113
|
+
and ": " not in value
|
|
114
|
+
and " #" not in value
|
|
115
|
+
and not value.endswith(":")
|
|
116
|
+
and value.lower() not in _YAML_WORDS
|
|
117
|
+
and not any(ord(c) < 32 or c == "\x7f" or c in _YAML_BREAKS for c in value)
|
|
118
|
+
)
|
|
119
|
+
if plain and value:
|
|
120
|
+
return value
|
|
121
|
+
quoted = json.dumps(value, ensure_ascii=False) # a JSON string is a YAML one
|
|
122
|
+
for char, escape in _YAML_BREAKS.items():
|
|
123
|
+
quoted = quoted.replace(char, escape)
|
|
124
|
+
return quoted
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _yaml_text(scalar: str) -> str:
|
|
128
|
+
"""The text of a one-line scalar as :func:`_yaml_scalar` writes it. Anything
|
|
129
|
+
that is not one whole quoted string is its own text, which is how a value
|
|
130
|
+
this command wrote bare, before it quoted, still reads as it did."""
|
|
131
|
+
if len(scalar) >= 2 and scalar[0] == scalar[-1] == '"':
|
|
132
|
+
try:
|
|
133
|
+
value = json.loads(scalar)
|
|
134
|
+
except ValueError:
|
|
135
|
+
return scalar
|
|
136
|
+
return value if isinstance(value, str) else scalar
|
|
137
|
+
if len(scalar) >= 2 and scalar[0] == scalar[-1] == "'":
|
|
138
|
+
inner = scalar[1:-1]
|
|
139
|
+
if "'" not in inner.replace("''", ""):
|
|
140
|
+
return inner.replace("''", "'")
|
|
141
|
+
return scalar
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _requoted(block: str) -> str:
|
|
145
|
+
"""A frontmatter block with every bare value YAML would misread quoted.
|
|
146
|
+
A value that is already one quoted string, or reads back as itself, keeps
|
|
147
|
+
its line as it is; so do keys, order, line endings and any line that is
|
|
148
|
+
not ``key: value``."""
|
|
149
|
+
out = []
|
|
150
|
+
for line in block.splitlines(True):
|
|
151
|
+
text = line.rstrip("\r\n")
|
|
152
|
+
key, sep, value = text.partition(":")
|
|
153
|
+
bare = value.strip()
|
|
154
|
+
if sep and key.strip() and bare and _yaml_text(bare) == bare and _yaml_scalar(bare) != bare:
|
|
155
|
+
text = "%s: %s" % (key, _yaml_scalar(bare))
|
|
156
|
+
out.append(text + line[len(line.rstrip("\r\n")):])
|
|
157
|
+
return "".join(out)
|
|
158
|
+
|
|
159
|
+
|
|
93
160
|
def _today() -> str:
|
|
94
161
|
return datetime.date.today().isoformat()
|
|
95
162
|
|
|
@@ -153,7 +220,7 @@ class Finding:
|
|
|
153
220
|
for line in m.group(1).splitlines():
|
|
154
221
|
key, sep, value = line.partition(":")
|
|
155
222
|
if sep and key.strip():
|
|
156
|
-
meta[key.strip()] = value.strip()
|
|
223
|
+
meta[key.strip()] = _yaml_text(value.strip())
|
|
157
224
|
body = text[m.end():]
|
|
158
225
|
else:
|
|
159
226
|
body = text
|
|
@@ -178,10 +245,10 @@ class Finding:
|
|
|
178
245
|
lines = ["---"]
|
|
179
246
|
for key in _META_KEYS:
|
|
180
247
|
if self.meta.get(key):
|
|
181
|
-
lines.append("%s: %s" % (key, self.meta[key]))
|
|
248
|
+
lines.append("%s: %s" % (key, _yaml_scalar(self.meta[key])))
|
|
182
249
|
for key, value in self.meta.items():
|
|
183
250
|
if key not in _META_KEYS and value:
|
|
184
|
-
lines.append("%s: %s" % (key, value))
|
|
251
|
+
lines.append("%s: %s" % (key, _yaml_scalar(value)))
|
|
185
252
|
lines.append("---")
|
|
186
253
|
body = self.body.rstrip("\r\n")
|
|
187
254
|
return "\n".join(lines) + "\n\n" + (body + "\n" if body else "")
|
|
@@ -372,6 +439,26 @@ class FindingsStore:
|
|
|
372
439
|
self.write_index()
|
|
373
440
|
return path
|
|
374
441
|
|
|
442
|
+
def requote(self) -> "list[Path]":
|
|
443
|
+
"""Write again every frontmatter whose values are not in the form this
|
|
444
|
+
command writes now: a description an earlier version left bare where
|
|
445
|
+
YAML needs it quoted. Only those lines change; the body, the other
|
|
446
|
+
lines and the line endings stay byte for byte. Returns the files
|
|
447
|
+
changed."""
|
|
448
|
+
changed = []
|
|
449
|
+
for finding in self.all():
|
|
450
|
+
text = finding.path.read_bytes().decode("utf-8")
|
|
451
|
+
m = _FRONTMATTER_RE.match(text[1:] if text.startswith("") else text)
|
|
452
|
+
if not m:
|
|
453
|
+
continue
|
|
454
|
+
shift = 1 if text.startswith("") else 0
|
|
455
|
+
start, end = m.start(1) + shift, m.end(1) + shift
|
|
456
|
+
block = _requoted(text[start:end])
|
|
457
|
+
if block != text[start:end]:
|
|
458
|
+
write_text_lf(finding.path, text[:start] + block + text[end:])
|
|
459
|
+
changed.append(finding.path)
|
|
460
|
+
return changed
|
|
461
|
+
|
|
375
462
|
# -- index ---------------------------------------------------------------
|
|
376
463
|
|
|
377
464
|
def render_index(self) -> str:
|
|
@@ -605,12 +692,24 @@ class Findings(LoggingArgs, Cli):
|
|
|
605
692
|
return 0
|
|
606
693
|
|
|
607
694
|
class Index(_Base):
|
|
608
|
-
"""Regenerate INDEX.md from the files (after hand edits).
|
|
695
|
+
"""Regenerate INDEX.md from the files (after hand edits).
|
|
696
|
+
|
|
697
|
+
`--requote` first quotes any frontmatter value YAML cannot read as it
|
|
698
|
+
stands: what an earlier version wrote for a description opening with
|
|
699
|
+
`[` or a backtick, or holding `: `."""
|
|
609
700
|
|
|
610
701
|
_parsername_ = "index"
|
|
611
702
|
|
|
703
|
+
requote: bool = False
|
|
704
|
+
"Also quote frontmatter values YAML cannot read bare (bodies are not touched)."
|
|
705
|
+
("--requote",)
|
|
706
|
+
|
|
612
707
|
def __call__(self) -> int:
|
|
613
|
-
|
|
708
|
+
store = self.store()
|
|
709
|
+
if self.requote:
|
|
710
|
+
changed = store.requote()
|
|
711
|
+
self._logger_.info("requoted the frontmatter of %d finding(s)", len(changed))
|
|
712
|
+
_write_stdout("%s\n" % (store.write_index(),))
|
|
614
713
|
return 0
|
|
615
714
|
|
|
616
715
|
class PathCmd(_Base):
|
|
@@ -336,3 +336,84 @@ def test_a_finding_named_like_the_index_is_refused(findings_mod, tmp_path):
|
|
|
336
336
|
with pytest.raises(SystemExit, match="reserved"):
|
|
337
337
|
store.add(title, body="important details")
|
|
338
338
|
assert not root.exists() or not [p for p in root.iterdir() if p.suffix == ".md"]
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# --------------------------------------------------------------------------- #
|
|
342
|
+
# The frontmatter is YAML another tool can read
|
|
343
|
+
# --------------------------------------------------------------------------- #
|
|
344
|
+
|
|
345
|
+
AWKWARD = [
|
|
346
|
+
"[low][correctness] off Linux, sniff with an interface name raises",
|
|
347
|
+
"`dotagents pyvenv` broken on the new machine",
|
|
348
|
+
"perf rule: two hosted-runner runs are not a before/after",
|
|
349
|
+
'"It compiles" is not evidence',
|
|
350
|
+
"'first', 'second' and a third",
|
|
351
|
+
'"quoted"',
|
|
352
|
+
"ends with a colon:",
|
|
353
|
+
"a marker # that starts a comment",
|
|
354
|
+
"*starred* and &anchored",
|
|
355
|
+
'a back\\slash and a "quote": both',
|
|
356
|
+
"- a leading dash",
|
|
357
|
+
"{braces} and a percent %",
|
|
358
|
+
"em dash — and an accent: é",
|
|
359
|
+
"true",
|
|
360
|
+
]
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
@pytest.mark.parametrize("text", AWKWARD)
|
|
364
|
+
def test_any_description_is_frontmatter_yaml_can_read(findings_mod, tmp_path, text):
|
|
365
|
+
yaml = pytest.importorskip("yaml")
|
|
366
|
+
store = findings_mod.FindingsStore(tmp_path / "findings")
|
|
367
|
+
finding = store.add(text, name="x")
|
|
368
|
+
front = yaml.safe_load(finding.path.read_text(encoding="utf-8").split("---\n")[1])
|
|
369
|
+
assert front["name"] == "x" and str(front["description"]) == text
|
|
370
|
+
assert findings_mod.Finding.load(finding.path).description == text
|
|
371
|
+
assert "— %s\n" % text in store.index_path.read_text(encoding="utf-8")
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def test_a_plain_description_is_written_bare(findings_mod, tmp_path):
|
|
375
|
+
store = findings_mod.FindingsStore(tmp_path / "findings")
|
|
376
|
+
text = "bare kb/X.md refs were never inlined (3 files, 2026-09-09)"
|
|
377
|
+
raw = store.add(text).path.read_text(encoding="utf-8")
|
|
378
|
+
assert "\ndescription: %s\n" % text in raw
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
@pytest.mark.parametrize("text", [
|
|
382
|
+
"[low][api] a consumer: rewrites `x`",
|
|
383
|
+
'"It compiles" is not evidence',
|
|
384
|
+
"'one', 'two'",
|
|
385
|
+
'"a" and "b"',
|
|
386
|
+
])
|
|
387
|
+
def test_a_finding_written_bare_still_reads_as_it_did(findings_mod, tmp_path, text):
|
|
388
|
+
"""What an earlier version wrote: the description as typed, whatever it held."""
|
|
389
|
+
path = tmp_path / "findings" / "old.md"
|
|
390
|
+
path.parent.mkdir(parents=True)
|
|
391
|
+
path.write_bytes(("---\nname: old\ndescription: %s\nstatus: active\n---\n\nbody\n" % text).encode("utf-8"))
|
|
392
|
+
assert findings_mod.Finding.load(path).description == text
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def test_index_requote_changes_only_the_values_yaml_cannot_read(findings_mod, tmp_path, capsys):
|
|
396
|
+
yaml = pytest.importorskip("yaml")
|
|
397
|
+
root = tmp_path / "findings"
|
|
398
|
+
(root / "processed").mkdir(parents=True)
|
|
399
|
+
bad = root / "processed" / "bad.md" # CRLF, odd spacing, blank lines: all kept
|
|
400
|
+
bad.write_bytes(b"---\r\nname: bad\r\ndescription: [low][api] a: b\r\nstatus: processed\r\n---\r\n\r\n\r\nbody kept \r\n\r\n\r\n")
|
|
401
|
+
kept = {
|
|
402
|
+
root / "quoted.md": b'---\nname: quoted\ndescription: "already: quoted"\n---\nbody\n',
|
|
403
|
+
root / "plain.md": b"---\nname: plain\ndescription: nothing to do\nextra: 1\n---\n\nbody\n",
|
|
404
|
+
root / "note.md": b"# a hand note\n\n---\nnot: [frontmatter\n---\n",
|
|
405
|
+
}
|
|
406
|
+
for path, data in kept.items():
|
|
407
|
+
path.write_bytes(data)
|
|
408
|
+
|
|
409
|
+
assert _run(findings_mod.Findings.Index, dir=root, requote=True) == 0
|
|
410
|
+
assert capsys.readouterr().out.strip() == str(root / "INDEX.md")
|
|
411
|
+
|
|
412
|
+
assert bad.read_bytes() == (
|
|
413
|
+
b'---\r\nname: bad\r\ndescription: "[low][api] a: b"\r\nstatus: processed\r\n---\r\n\r\n\r\nbody kept \r\n\r\n\r\n')
|
|
414
|
+
assert yaml.safe_load(bad.read_bytes().decode().split("---\r\n")[1])["description"] == "[low][api] a: b"
|
|
415
|
+
for path, data in kept.items():
|
|
416
|
+
assert path.read_bytes() == data, path.name
|
|
417
|
+
assert "— [low][api] a: b" in (root / "INDEX.md").read_text(encoding="utf-8")
|
|
418
|
+
# A second pass has nothing left to do.
|
|
419
|
+
assert findings_mod.FindingsStore(root).requote() == []
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_overlay/dotagents/templates/AGENTS.md
RENAMED
|
File without changes
|
{dotagents_cli-0.7.2 → dotagents_cli-0.7.4}/src/dotagents/_overlay/dotagents/templates/PROJECT.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|