context-guard-cli 2.1.0__tar.gz → 2.2.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.
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/.github/workflows/ci.yml +2 -2
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/CHANGELOG.md +51 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/PKG-INFO +28 -5
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/README.es.md +28 -1
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/README.md +27 -4
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/TUTORIAL.es.md +45 -7
- context_guard_cli-2.2.0/context_guard/_data/hosts/antigravity/skills/context-guard/SKILL.md +59 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/setup.py +113 -26
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/docs/adapters/VERIFY.md +12 -11
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/pyproject.toml +1 -1
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_adapters.py +5 -4
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_assets.py +71 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_packaging.py +17 -4
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_readme.py +20 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_release.py +53 -6
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_setup.py +292 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/.agents/rules/context-guard.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/.githooks/pre-commit +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/.github/workflows/publish.yml +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/.gitignore +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/AGENTS.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/LICENSE +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/__init__.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/antigravity/hooks.snippet.json +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/antigravity/rules/context-guard.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/claude-code/commands/cg-continue.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/claude-code/commands/cg-new.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/claude-code/mcp.snippet.json +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/claude-code/settings.snippet.json +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/opencode/agent.snippet.json +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/opencode/commands/cg-continue.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/opencode/commands/cg-new.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/opencode/mcp.snippet.json +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/opencode/permissions.snippet.json +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/phases/execute.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/phases/plan.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/phases/verify.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/__init__.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/assets.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/cli.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/commands.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/errors.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/locking.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/manifest.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/migrate.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/paths.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/transaction.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/mcp_server.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/docs/adapters/antigravity/PERMISSIONS.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/docs/adapters/claude-code/PERMISSIONS.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/docs/adapters/opencode/PERMISSIONS.md +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/__init__.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_adversarial.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_adversarial_approve.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_adversarial_migrate.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_adversarial_multichange.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_agents_md.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_archive.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_check_completion.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_commands.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_doctor.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_ergonomics.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_json_format.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_license.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_locking.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_manifest.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_mcp_server.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_next_task_status.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_phase_docs.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_phases.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_pre_commit_hook.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_publish.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_pyproject_pin.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_readme_es.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_transaction.py +0 -0
- {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_validate.py +0 -0
|
@@ -6,6 +6,57 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [2.2.0] - 2026-08-02
|
|
10
|
+
|
|
11
|
+
Triggered by real dogfooding: Antigravity never raised the context-guard
|
|
12
|
+
protocol in a fresh project, because `cg setup` only installed its
|
|
13
|
+
enforcement hook, nothing discoverable.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- A discovery skill for Antigravity, installed by `cg setup` at
|
|
18
|
+
`~/.gemini/antigravity-cli/skills/context-guard/SKILL.md` alongside the existing
|
|
19
|
+
deny hook. Antigravity loads skills by progressive disclosure — only the
|
|
20
|
+
name and description sit in context until the model picks it — which is
|
|
21
|
+
what makes this affordable without repeating 2.0's bug 6.0.4
|
|
22
|
+
(`~/.gemini/GEMINI.md` contamination of every project on the machine,
|
|
23
|
+
which stays permanently out of scope). The skill carries the three-phase
|
|
24
|
+
DAG, the operative `cg` commands, and marks `cg approve` human-only.
|
|
25
|
+
- `cg setup --project <dir>` now installs the Antigravity workspace rule
|
|
26
|
+
directly, instead of that artifact only ever being written by `cg new`.
|
|
27
|
+
- A shared ownership-marker check: a skill or rule file not written by
|
|
28
|
+
context-guard is never overwritten — reported as `SKIP|SKILL_EXISTS|<path>`
|
|
29
|
+
and left alone, whether hit through `cg setup` or `cg new`.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Antigravity detection now checks for `agy` — the CLI's actual binary name
|
|
34
|
+
— and `~/.gemini/antigravity-cli`, its state directory. The prior check
|
|
35
|
+
named `antigravity`, which resolves on PATH to an unrelated program.
|
|
36
|
+
- `cg setup --project` was overwriting an `.agents/rules/context-guard.md`
|
|
37
|
+
a team had edited; `cg new` never did. Both paths now honor the same
|
|
38
|
+
ownership marker.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- README.md, README.es.md and TUTORIAL.es.md Install sections lead with
|
|
43
|
+
`uv tool install context-guard-cli` / `pipx install`, isolated installs
|
|
44
|
+
that land `cg` on PATH globally — what a once-per-machine `cg setup`
|
|
45
|
+
assumes. `pip install` remains documented as a fallback. Explicit warning
|
|
46
|
+
added against `uv pip install context-guard-cli`, which installs into
|
|
47
|
+
whichever project venv is active rather than the machine, silently
|
|
48
|
+
defeating a global `cg setup`.
|
|
49
|
+
- `docs/adapters/VERIFY.md`: step 2 no longer checks Antigravity discovery
|
|
50
|
+
via `agy inspect`, which does not exist as a subcommand — replaced with
|
|
51
|
+
the actual verification, behavioral: a new session, a multi-step task,
|
|
52
|
+
and whether the agent invokes `cg` unprompted. Step 5 no longer names
|
|
53
|
+
`--with-antigravity-hook`, a flag removed in 2.1; the deny hook installs
|
|
54
|
+
by default now, declined with `--no-hooks`.
|
|
55
|
+
- VERIFY.md is complete for all three hosts — Claude Code, OpenCode and
|
|
56
|
+
Antigravity — closing the gap the README used to admit openly. The line
|
|
57
|
+
stating the OpenCode and Antigravity adapters had not been run against a
|
|
58
|
+
live host is removed; both have been.
|
|
59
|
+
|
|
9
60
|
## [2.1.0] - 2026-08-01
|
|
10
61
|
|
|
11
62
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: context-guard-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: The transactional memory layer for AI coding agents — your context survives crashes, compaction, and session loss
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -82,10 +82,19 @@ illustrative shorthand.
|
|
|
82
82
|
Two lines, once per machine:
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
|
-
|
|
85
|
+
uv tool install context-guard-cli
|
|
86
86
|
cg setup
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
No `uv`? `pipx install context-guard-cli` works the same way. Both install
|
|
90
|
+
into an isolated environment and put `cg` on PATH globally — the whole point,
|
|
91
|
+
since `cg setup` configures your machine once, not once per project.
|
|
92
|
+
`pip install context-guard-cli` also works, as a fallback for a plain Python
|
|
93
|
+
install with no `uv`/`pipx` available. **Do not use `uv pip install
|
|
94
|
+
context-guard-cli`**: that installs into whichever venv is currently active —
|
|
95
|
+
a single project's, if you happen to be inside one — not onto the machine,
|
|
96
|
+
which silently defeats the point of a global `cg setup`.
|
|
97
|
+
|
|
89
98
|
`cg setup` detects Claude Code, OpenCode and Antigravity, installs the slash
|
|
90
99
|
commands, and puts `cg approve` behind each one's permission prompt — see
|
|
91
100
|
[Adapters](#adapters-and-permission-configuration). It prints every file it
|
|
@@ -106,6 +115,22 @@ cd context-guard && uv venv && uv pip install -e ".[dev]"
|
|
|
106
115
|
git config core.hooksPath .githooks # activates the pre-commit gate below
|
|
107
116
|
```
|
|
108
117
|
|
|
118
|
+
## Upgrading
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
uv tool upgrade context-guard-cli && cg setup
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The second command is not optional. `cg setup` copies commands, skills and
|
|
125
|
+
permission snippets into your host configs; upgrading the package does not
|
|
126
|
+
touch those copies, so a new version's adapter fixes only reach a machine
|
|
127
|
+
once `cg setup` runs again. It is idempotent — safe to run any time.
|
|
128
|
+
|
|
129
|
+
Phase files already materialised in a project (`.context-guard/phases/`)
|
|
130
|
+
are never overwritten, by design: a project keeps the phases it was started
|
|
131
|
+
with, including your edits. Delete a phase file and run `cg new` to pull the
|
|
132
|
+
current version.
|
|
133
|
+
|
|
109
134
|
## How it works
|
|
110
135
|
|
|
111
136
|
```
|
|
@@ -256,9 +281,7 @@ at `phases/{plan,execute,verify}.md` rather than duplicating them. How to put
|
|
|
256
281
|
`cg approve` behind each harness's permission prompt is documented in
|
|
257
282
|
[docs/adapters/](docs/adapters/), one `PERMISSIONS.md` per host, alongside the
|
|
258
283
|
manual smoke-test checklist in [docs/adapters/VERIFY.md](docs/adapters/VERIFY.md).
|
|
259
|
-
`cg setup` installs the right one for each detected host.
|
|
260
|
-
adapters are ported from state-guard and covered by static tests only — they
|
|
261
|
-
have not been run against a live host of either.
|
|
284
|
+
`cg setup` installs the right one for each detected host.
|
|
262
285
|
|
|
263
286
|
## How this compares
|
|
264
287
|
|
|
@@ -66,10 +66,20 @@ nada acá es un resumen ilustrativo.
|
|
|
66
66
|
Dos líneas, una vez por máquina:
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
|
-
|
|
69
|
+
uv tool install context-guard-cli
|
|
70
70
|
cg setup
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
¿No tenés `uv`? `pipx install context-guard-cli` hace lo mismo. Ambos
|
|
74
|
+
instalan en un entorno aislado y dejan `cg` en el PATH global — que es el
|
|
75
|
+
punto, porque `cg setup` configura tu máquina una vez, no una vez por
|
|
76
|
+
proyecto. `pip install context-guard-cli` también funciona, como alternativa
|
|
77
|
+
para una instalación de Python simple sin `uv`/`pipx` disponible. **No uses
|
|
78
|
+
`uv pip install context-guard-cli`**: eso instala en el venv que esté activo
|
|
79
|
+
en ese momento — el de un proyecto puntual, si estás parado adentro de
|
|
80
|
+
uno — no en la máquina, lo que anula en silencio el sentido de un `cg setup`
|
|
81
|
+
global.
|
|
82
|
+
|
|
73
83
|
`cg setup` detecta Claude Code, OpenCode y Antigravity, instala los slash
|
|
74
84
|
commands, y deja `cg approve` detrás del permission prompt de cada uno — ver
|
|
75
85
|
[Adapters](#adapters-y-configuración-de-permisos). Imprime cada archivo que
|
|
@@ -91,6 +101,23 @@ cd context-guard && uv venv && uv pip install -e ".[dev]"
|
|
|
91
101
|
git config core.hooksPath .githooks # activa el gate de pre-commit de más abajo
|
|
92
102
|
```
|
|
93
103
|
|
|
104
|
+
## Actualizar
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
uv tool upgrade context-guard-cli && cg setup
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
El segundo comando no es opcional. `cg setup` copia los comandos, skills y
|
|
111
|
+
snippets de permisos dentro de la configuración de tus hosts; actualizar el
|
|
112
|
+
paquete no toca esas copias, así que las correcciones de adapters de una
|
|
113
|
+
versión nueva recién llegan a la máquina cuando se vuelve a correr
|
|
114
|
+
`cg setup`. Es idempotente — se puede correr las veces que quieras.
|
|
115
|
+
|
|
116
|
+
Las fases ya materializadas en un proyecto (`.context-guard/phases/`) nunca
|
|
117
|
+
se sobrescriben, por diseño: cada proyecto conserva las fases con las que
|
|
118
|
+
empezó, incluidas tus ediciones. Borrá el archivo de fase y corré `cg new`
|
|
119
|
+
para traer la versión actual.
|
|
120
|
+
|
|
94
121
|
## Cómo funciona
|
|
95
122
|
|
|
96
123
|
```
|
|
@@ -69,10 +69,19 @@ illustrative shorthand.
|
|
|
69
69
|
Two lines, once per machine:
|
|
70
70
|
|
|
71
71
|
```bash
|
|
72
|
-
|
|
72
|
+
uv tool install context-guard-cli
|
|
73
73
|
cg setup
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
No `uv`? `pipx install context-guard-cli` works the same way. Both install
|
|
77
|
+
into an isolated environment and put `cg` on PATH globally — the whole point,
|
|
78
|
+
since `cg setup` configures your machine once, not once per project.
|
|
79
|
+
`pip install context-guard-cli` also works, as a fallback for a plain Python
|
|
80
|
+
install with no `uv`/`pipx` available. **Do not use `uv pip install
|
|
81
|
+
context-guard-cli`**: that installs into whichever venv is currently active —
|
|
82
|
+
a single project's, if you happen to be inside one — not onto the machine,
|
|
83
|
+
which silently defeats the point of a global `cg setup`.
|
|
84
|
+
|
|
76
85
|
`cg setup` detects Claude Code, OpenCode and Antigravity, installs the slash
|
|
77
86
|
commands, and puts `cg approve` behind each one's permission prompt — see
|
|
78
87
|
[Adapters](#adapters-and-permission-configuration). It prints every file it
|
|
@@ -93,6 +102,22 @@ cd context-guard && uv venv && uv pip install -e ".[dev]"
|
|
|
93
102
|
git config core.hooksPath .githooks # activates the pre-commit gate below
|
|
94
103
|
```
|
|
95
104
|
|
|
105
|
+
## Upgrading
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
uv tool upgrade context-guard-cli && cg setup
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The second command is not optional. `cg setup` copies commands, skills and
|
|
112
|
+
permission snippets into your host configs; upgrading the package does not
|
|
113
|
+
touch those copies, so a new version's adapter fixes only reach a machine
|
|
114
|
+
once `cg setup` runs again. It is idempotent — safe to run any time.
|
|
115
|
+
|
|
116
|
+
Phase files already materialised in a project (`.context-guard/phases/`)
|
|
117
|
+
are never overwritten, by design: a project keeps the phases it was started
|
|
118
|
+
with, including your edits. Delete a phase file and run `cg new` to pull the
|
|
119
|
+
current version.
|
|
120
|
+
|
|
96
121
|
## How it works
|
|
97
122
|
|
|
98
123
|
```
|
|
@@ -243,9 +268,7 @@ at `phases/{plan,execute,verify}.md` rather than duplicating them. How to put
|
|
|
243
268
|
`cg approve` behind each harness's permission prompt is documented in
|
|
244
269
|
[docs/adapters/](docs/adapters/), one `PERMISSIONS.md` per host, alongside the
|
|
245
270
|
manual smoke-test checklist in [docs/adapters/VERIFY.md](docs/adapters/VERIFY.md).
|
|
246
|
-
`cg setup` installs the right one for each detected host.
|
|
247
|
-
adapters are ported from state-guard and covered by static tests only — they
|
|
248
|
-
have not been run against a live host of either.
|
|
271
|
+
`cg setup` installs the right one for each detected host.
|
|
249
272
|
|
|
250
273
|
## How this compares
|
|
251
274
|
|
|
@@ -37,7 +37,7 @@ Una aclaración honesta, porque este proyecto se toma en serio la honestidad: co
|
|
|
37
37
|
Abrí la terminal y corré:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
|
|
40
|
+
uv tool install context-guard-cli
|
|
41
41
|
cg setup
|
|
42
42
|
```
|
|
43
43
|
|
|
@@ -48,8 +48,14 @@ aprobación (paso 4). Al terminar imprime la lista exacta de archivos que
|
|
|
48
48
|
tocó — nada oculto. Podés correrlo las veces que quieras: si ya está
|
|
49
49
|
instalado, no duplica nada.
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
¿No tenés `uv`? `pipx install context-guard-cli` hace lo mismo. Si ninguno de
|
|
52
|
+
los dos está disponible, `pip install context-guard-cli` también funciona,
|
|
53
|
+
como alternativa.
|
|
54
|
+
|
|
55
|
+
> **Ojo con esta:** `uv pip install context-guard-cli` *no* es lo mismo que
|
|
56
|
+
> `uv tool install`. Instala la herramienta adentro del entorno virtual de
|
|
57
|
+
> tu proyecto actual, no en tu máquina — y `cg setup` necesita que `cg` esté
|
|
58
|
+
> disponible en cualquier terminal, no solo dentro de ese proyecto.
|
|
53
59
|
|
|
54
60
|
Verificá que quedó todo:
|
|
55
61
|
|
|
@@ -64,9 +70,40 @@ herramienta; todo lo que hagas con context-guard empieza con `cg`.
|
|
|
64
70
|
un cambio en un proyecto nuevo, `cg new` escribe ahí lo que falte. No
|
|
65
71
|
necesitás descargar este repositorio ni copiar archivos a mano.
|
|
66
72
|
|
|
67
|
-
>
|
|
68
|
-
>
|
|
69
|
-
>
|
|
73
|
+
> **¿Dónde quedó instalado?** `uv tool install` y `pipx` ponen `cg` en un
|
|
74
|
+
> directorio propio (`~/.local/bin` en Linux/macOS típicamente) y lo agregan
|
|
75
|
+
> al PATH por vos. `pip install` sin `--user` lo deja donde viva tu Python
|
|
76
|
+
> del sistema; con `--user`, en `~/.local/bin` también. Si el paso de
|
|
77
|
+
> verificación de arriba no encuentra `cg`, es casi siempre esto: el
|
|
78
|
+
> directorio existe pero tu terminal no lo tiene en el PATH todavía.
|
|
79
|
+
|
|
80
|
+
> **Si algo falla:** el error más común es que la instalación terminó bien
|
|
81
|
+
> pero la terminal no encuentra `cg`. Cerrá y reabrí la terminal. Si
|
|
82
|
+
> persiste, probá `python3 -m pip install --user context-guard-cli` y de
|
|
83
|
+
> nuevo cerrar/reabrir.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 3.1. Cómo actualizar
|
|
88
|
+
|
|
89
|
+
Cuando salga una versión nueva, dos comandos en uno:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
uv tool upgrade context-guard-cli && cg setup
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
*(Con pipx: `pipx upgrade context-guard-cli && cg setup`.)*
|
|
96
|
+
|
|
97
|
+
**Por qué son dos y no uno:** el primero actualiza el programa; el segundo
|
|
98
|
+
vuelve a copiar los comandos y la configuración dentro de tu asistente. Si
|
|
99
|
+
hacés solo el primero, tu asistente sigue usando las instrucciones viejas y
|
|
100
|
+
las mejoras de la versión nueva no aparecen. Correr `cg setup` de más nunca
|
|
101
|
+
rompe nada.
|
|
102
|
+
|
|
103
|
+
**Tus proyectos viejos siguen como estaban.** Las guías de fase que se
|
|
104
|
+
copiaron dentro de cada proyecto no se pisan al actualizar — a propósito,
|
|
105
|
+
para no borrar cambios que hayas hecho. Los proyectos nuevos arrancan con
|
|
106
|
+
las guías actualizadas.
|
|
70
107
|
|
|
71
108
|
---
|
|
72
109
|
|
|
@@ -172,7 +209,8 @@ Diagnostica y libera lo que quedó colgado de procesos muertos. Nunca resuelvas
|
|
|
172
209
|
| Ver el avance | `cg status` | — |
|
|
173
210
|
| Retomar tras un corte | `/cg-continue` | Continúa donde quedó |
|
|
174
211
|
| Destrabar | `cg doctor --fix` | — |
|
|
212
|
+
| Actualizar | `uv tool upgrade context-guard-cli && cg setup` | — |
|
|
175
213
|
|
|
176
|
-
Instalación (una vez): `
|
|
214
|
+
Instalación (una vez): `uv tool install context-guard-cli` y luego `cg setup` una vez por máquina.
|
|
177
215
|
|
|
178
216
|
Documentación completa, en inglés y español, en el [repositorio](https://github.com/fdomerlo/context-guard).
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: context-guard
|
|
3
|
+
description: >-
|
|
4
|
+
Use when the user asks for a multi-step coding task, when they ask to
|
|
5
|
+
resume work after a lost, crashed or compacted session, or in any project
|
|
6
|
+
that already contains a `.context-guard/` directory. Drives the `cg` CLI so
|
|
7
|
+
a phase-governed change keeps its plan, its progress and its verification
|
|
8
|
+
on disk instead of in the conversation.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<!-- context-guard:begin -->
|
|
12
|
+
# context-guard
|
|
13
|
+
|
|
14
|
+
`cg` (long form: `context-guard`) is a transactional state manager. A change
|
|
15
|
+
moves through fixed phases and nothing advances without passing that phase's
|
|
16
|
+
gate, so a session that dies mid-task resumes instead of restarting.
|
|
17
|
+
|
|
18
|
+
## The pipeline
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
PLAN --approve--> EXECUTE --tasks done--> VERIFY --approve--> ARCHIVE
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
| Phase | Produces | Gate to leave it |
|
|
25
|
+
|---------|----------------------------------------|-------------------------------|
|
|
26
|
+
| PLAN | `objective.md`, `tasks.md` | human approval, then `commit` |
|
|
27
|
+
| EXECUTE | code changes, `tasks.md` checked off | every task checked off |
|
|
28
|
+
| VERIFY | `review-report.md`, `verify-report.md` | human approval, then archive |
|
|
29
|
+
|
|
30
|
+
`cg commit` rejects any transition outside that order.
|
|
31
|
+
|
|
32
|
+
## On entering a phase, read that phase's file
|
|
33
|
+
|
|
34
|
+
The full instructions live in `.context-guard/phases/plan.md`,
|
|
35
|
+
`.context-guard/phases/execute.md` and `.context-guard/phases/verify.md` —
|
|
36
|
+
not in this skill. Load the file for the phase you are entering and follow
|
|
37
|
+
it exactly.
|
|
38
|
+
|
|
39
|
+
## Commands
|
|
40
|
+
|
|
41
|
+
- `cg new <name>` — scaffold a change, open PLAN, write the phase files.
|
|
42
|
+
- `cg status` — what phase the change is in and what is still pending. Run
|
|
43
|
+
this first when resuming, before reading any code.
|
|
44
|
+
- `cg next-task` — the next unchecked task, during EXECUTE.
|
|
45
|
+
- `cg commit --next-phase <PHASE>` — advance, once the gate passes.
|
|
46
|
+
- `cg list`, `cg validate`, `cg checkpoint`, `cg rollback` — inspect, check,
|
|
47
|
+
snapshot, undo.
|
|
48
|
+
|
|
49
|
+
Every command takes `--change <name>`. Pass it whenever more than one change
|
|
50
|
+
is active: `cg` errors rather than guessing which one you meant.
|
|
51
|
+
|
|
52
|
+
## `cg approve` is human-only
|
|
53
|
+
|
|
54
|
+
Never run `cg approve` yourself, and never run it "on behalf of" the user.
|
|
55
|
+
When `cg commit` exits 6 (`APPROVAL_REQUIRED`), stop, show the human the
|
|
56
|
+
artifacts the gate covers, and ask them to run the approval themselves.
|
|
57
|
+
Editing `.context-guard/**/manifest.json` by hand is the same violation by
|
|
58
|
+
another route: it forges the state the gate reads.
|
|
59
|
+
<!-- context-guard:end -->
|
|
@@ -214,6 +214,18 @@ def _install_opencode(root, with_mcp, global_scope):
|
|
|
214
214
|
return touched
|
|
215
215
|
|
|
216
216
|
|
|
217
|
+
# The path was confirmed against Antigravity CLI 1.1.9 via the /skills panel,
|
|
218
|
+
# which lists ~/.gemini/antigravity-cli/skills/ as the user's global location;
|
|
219
|
+
# builtin/ is the subtree with checksum, a sibling of this one, not its parent.
|
|
220
|
+
# ANTIGRAVITY_GLOBAL_ROOT remains as is: hooks.json does reside in .gemini/config/.
|
|
221
|
+
ANTIGRAVITY_GLOBAL_ROOT = (".gemini", "config")
|
|
222
|
+
ANTIGRAVITY_SKILL_REL = (".gemini", "antigravity-cli", "skills", "context-guard", "SKILL.md")
|
|
223
|
+
|
|
224
|
+
# Both artifacts we write into a user's tree as whole files carry this marker,
|
|
225
|
+
# so a later run can tell its own output from a file of the same name the user
|
|
226
|
+
# wrote themselves.
|
|
227
|
+
OWNERSHIP_MARKER = "<!-- context-guard:begin -->"
|
|
228
|
+
|
|
217
229
|
HOOKS_MANUAL_FIX = ("left untouched; add the deny hook manually "
|
|
218
230
|
"(see docs/adapters/antigravity/PERMISSIONS.md)")
|
|
219
231
|
|
|
@@ -248,36 +260,85 @@ def _hooks_shape_problem(cfg):
|
|
|
248
260
|
return None
|
|
249
261
|
|
|
250
262
|
|
|
251
|
-
def
|
|
252
|
-
|
|
263
|
+
def _embedded_antigravity_file(relpath):
|
|
264
|
+
for name, text in iter_host_files("antigravity"):
|
|
265
|
+
if name == relpath:
|
|
266
|
+
return text
|
|
267
|
+
return None
|
|
268
|
+
|
|
253
269
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
workspace rule travels with the repository, and for the global case
|
|
257
|
-
`cg new` materialises it per project instead.
|
|
270
|
+
def _write_owned(path, text):
|
|
271
|
+
"""Write one of our marked files, unless something else already owns it.
|
|
258
272
|
|
|
259
|
-
Returns `(
|
|
260
|
-
|
|
261
|
-
|
|
273
|
+
Returns `(written, skip)`. A file carrying OWNERSHIP_MARKER is one of ours
|
|
274
|
+
and is refreshed, which is what lets a `cg` upgrade actually reach the
|
|
275
|
+
artifacts an earlier version installed. Anything else is left exactly as
|
|
276
|
+
it is: `context-guard` is a plausible name for a skill or a rule the user
|
|
277
|
+
wrote themselves, and this code cannot reconstruct what it would destroy.
|
|
278
|
+
|
|
279
|
+
Not a failure. A name collision means the user has a file here on purpose;
|
|
280
|
+
it says so and moves on, rather than deciding the machine is broken.
|
|
281
|
+
"""
|
|
282
|
+
if os.path.exists(path):
|
|
283
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
284
|
+
if OWNERSHIP_MARKER not in f.read():
|
|
285
|
+
return False, f"SKIP|SKILL_EXISTS|{path}|not written by context-guard, left as is"
|
|
286
|
+
_write_text(path, text)
|
|
287
|
+
return True, None
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _install_antigravity(root, global_scope, no_hooks=False):
|
|
291
|
+
"""Global scope installs the discovery skill and the deny hook; project
|
|
292
|
+
scope installs the workspace rule.
|
|
293
|
+
|
|
294
|
+
The split follows what each artifact is for. The hook lives in user config
|
|
295
|
+
by definition, so it has no meaning in a project install; the workspace
|
|
296
|
+
rule travels with the repository. Until 2.2 the global case installed the
|
|
297
|
+
hook alone — enforcement with nothing to discover it by — and the only
|
|
298
|
+
bootstrap artifact was written by `cg new`, which nobody runs before they
|
|
299
|
+
know `cg` exists. The skill closes that loop: Antigravity loads it by
|
|
300
|
+
progressive disclosure, so it costs nothing until the model picks it.
|
|
301
|
+
|
|
302
|
+
Returns `(touched, failure, skips)`. Unlike the other hosts this one
|
|
303
|
+
writes into files it did not create and cannot fully predict, so it needs
|
|
304
|
+
both a way to fail without taking the whole run down and a way to decline
|
|
305
|
+
a single artifact without failing at all.
|
|
262
306
|
"""
|
|
263
307
|
if not global_scope:
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
return [],
|
|
308
|
+
text = _embedded_antigravity_file("rules/context-guard.md")
|
|
309
|
+
if text is None:
|
|
310
|
+
return [], None, []
|
|
311
|
+
rule_rel = os.path.join(".agents", "rules", "context-guard.md")
|
|
312
|
+
written, skip = _write_owned(os.path.join(root, rule_rel), text)
|
|
313
|
+
return ([rule_rel.replace(os.sep, "/")] if written else [],
|
|
314
|
+
None,
|
|
315
|
+
[skip] if skip else [])
|
|
270
316
|
|
|
317
|
+
touched = []
|
|
318
|
+
skips = []
|
|
319
|
+
|
|
320
|
+
skill_text = _embedded_antigravity_file("skills/context-guard/SKILL.md")
|
|
321
|
+
if skill_text is not None:
|
|
322
|
+
skill_rel = os.path.join(*ANTIGRAVITY_SKILL_REL)
|
|
323
|
+
written, skip = _write_owned(os.path.join(root, skill_rel), skill_text)
|
|
324
|
+
if written:
|
|
325
|
+
touched.append(skill_rel.replace(os.sep, "/"))
|
|
326
|
+
if skip:
|
|
327
|
+
skips.append(skip)
|
|
328
|
+
|
|
329
|
+
# `--no-hooks` declines the enforcement, not the discovery. A user who
|
|
330
|
+
# does not want the deny hook still wants their agent to know the tool
|
|
331
|
+
# is there.
|
|
271
332
|
if no_hooks:
|
|
272
|
-
return
|
|
333
|
+
return touched, None, skips
|
|
273
334
|
|
|
274
|
-
hooks_rel = os.path.join(
|
|
335
|
+
hooks_rel = os.path.join(*ANTIGRAVITY_GLOBAL_ROOT, "hooks.json")
|
|
275
336
|
hooks_path = os.path.join(root, hooks_rel)
|
|
276
337
|
|
|
277
338
|
try:
|
|
278
339
|
cfg = _read_json(hooks_path)
|
|
279
340
|
except ConfigCorruptError:
|
|
280
|
-
return
|
|
341
|
+
return touched, f"FAIL|HOOKS_UNPARSEABLE|{hooks_path}|{HOOKS_MANUAL_FIX}", skips
|
|
281
342
|
if cfg is None:
|
|
282
343
|
cfg = {}
|
|
283
344
|
|
|
@@ -286,7 +347,9 @@ def _install_antigravity(root, global_scope, no_hooks=False):
|
|
|
286
347
|
# Reported and left alone rather than normalised. Rewriting a config
|
|
287
348
|
# into the shape this code prefers would discard whatever the user's
|
|
288
349
|
# own tooling put there, and we cannot know what that was for.
|
|
289
|
-
return
|
|
350
|
+
return (touched,
|
|
351
|
+
f"FAIL|HOOKS_UNRECOGNISED|{hooks_path}|{HOOKS_MANUAL_FIX} ({problem})",
|
|
352
|
+
skips)
|
|
290
353
|
|
|
291
354
|
snippet = json.loads(read_snippet("antigravity", "hooks"))
|
|
292
355
|
new_hook = snippet["hooks"]["PreToolUse"][0]
|
|
@@ -301,7 +364,8 @@ def _install_antigravity(root, global_scope, no_hooks=False):
|
|
|
301
364
|
if not already:
|
|
302
365
|
pre_tool_use.append(new_hook)
|
|
303
366
|
_write_json(hooks_path, cfg)
|
|
304
|
-
|
|
367
|
+
touched.append(f"{hooks_rel.replace(os.sep, '/')} {HOOKS_NOTE}")
|
|
368
|
+
return touched, None, skips
|
|
305
369
|
|
|
306
370
|
|
|
307
371
|
# ---------------------------------------------------------------------------
|
|
@@ -323,7 +387,17 @@ def _detected(host, home):
|
|
|
323
387
|
return (os.path.isdir(os.path.join(home, ".config", "opencode"))
|
|
324
388
|
or _on_path("opencode"))
|
|
325
389
|
if host == "antigravity":
|
|
326
|
-
|
|
390
|
+
# `agy` is the real CLI binary — confirmed on a live install
|
|
391
|
+
# (PLAN-2.2 F2.1): `antigravity` also resolves on PATH there, but to
|
|
392
|
+
# an unrelated program, so it is kept rather than trusted alone.
|
|
393
|
+
# `~/.gemini/antigravity-cli` is the CLI's own state directory (log/,
|
|
394
|
+
# brain/, settings.json all live under it there), a stronger signal
|
|
395
|
+
# than the bare `~/.gemini` parent that anything gemini-related
|
|
396
|
+
# could have created — kept alongside it, not instead of it.
|
|
397
|
+
return (os.path.isdir(os.path.join(home, ".gemini"))
|
|
398
|
+
or os.path.isdir(os.path.join(home, ".gemini", "antigravity-cli"))
|
|
399
|
+
or _on_path("antigravity")
|
|
400
|
+
or _on_path("agy"))
|
|
327
401
|
return False
|
|
328
402
|
|
|
329
403
|
|
|
@@ -362,6 +436,7 @@ def run_setup(host="all", with_mcp=False, project=None, no_hooks=False):
|
|
|
362
436
|
lines = [f"Installing context-guard adapters into {root} ..."]
|
|
363
437
|
touched = []
|
|
364
438
|
failures = []
|
|
439
|
+
skips = []
|
|
365
440
|
|
|
366
441
|
for name in selected:
|
|
367
442
|
if name == "claude":
|
|
@@ -375,18 +450,21 @@ def run_setup(host="all", with_mcp=False, project=None, no_hooks=False):
|
|
|
375
450
|
# cannot be configured reports and steps aside: letting it abort
|
|
376
451
|
# the run would let one unrelated file on disk decide that the
|
|
377
452
|
# tool does not work on this machine.
|
|
378
|
-
host_touched, failure = _install_antigravity(
|
|
453
|
+
host_touched, failure, host_skips = _install_antigravity(
|
|
454
|
+
root, global_scope, no_hooks)
|
|
379
455
|
touched += host_touched
|
|
456
|
+
skips += host_skips
|
|
380
457
|
if failure:
|
|
381
458
|
failures.append(failure)
|
|
382
459
|
lines.append(" -> Antigravity: hooks.json left untouched, see below")
|
|
383
460
|
elif not global_scope:
|
|
384
|
-
lines.append(" -> Antigravity: rule installed")
|
|
461
|
+
lines.append(" -> Antigravity: workspace rule installed")
|
|
385
462
|
elif no_hooks:
|
|
386
|
-
lines.append(" -> Antigravity: deny hook
|
|
463
|
+
lines.append(" -> Antigravity: skill installed, deny hook "
|
|
464
|
+
"skipped (--no-hooks)")
|
|
387
465
|
else:
|
|
388
|
-
lines.append(" -> Antigravity: deny hook merged
|
|
389
|
-
"~/.gemini/config/hooks.json")
|
|
466
|
+
lines.append(" -> Antigravity: skill installed, deny hook merged "
|
|
467
|
+
"into ~/.gemini/config/hooks.json")
|
|
390
468
|
|
|
391
469
|
for name in HOST_DIRS:
|
|
392
470
|
if name in selected:
|
|
@@ -410,6 +488,15 @@ def run_setup(host="all", with_mcp=False, project=None, no_hooks=False):
|
|
|
410
488
|
else:
|
|
411
489
|
lines.append(" (none)")
|
|
412
490
|
|
|
491
|
+
# Listed apart from both the touched files and the failures: nothing was
|
|
492
|
+
# written, and nothing is wrong. Kept visible anyway, because the user is
|
|
493
|
+
# otherwise left with a host that installed "successfully" and still has
|
|
494
|
+
# no entry point.
|
|
495
|
+
if skips:
|
|
496
|
+
lines.append("")
|
|
497
|
+
lines.append("Skipped (a file of ours already exists, written by someone else):")
|
|
498
|
+
lines.extend(f" {skip}" for skip in skips)
|
|
499
|
+
|
|
413
500
|
if failures:
|
|
414
501
|
lines.append("")
|
|
415
502
|
lines.append("Failed:")
|
|
@@ -25,12 +25,15 @@ Confirm the files land where the tests say they should:
|
|
|
25
25
|
respectively, plus the merged permission config
|
|
26
26
|
(`.claude/settings.json`, `opencode.json`).
|
|
27
27
|
|
|
28
|
-
### 2. The
|
|
28
|
+
### 2. The entry point is discovered, not just installed
|
|
29
29
|
|
|
30
30
|
- **Claude Code**: `/` menu lists `cg-new` and `cg-continue`.
|
|
31
31
|
- **OpenCode**: `/help` lists `cg-new` and `cg-continue`.
|
|
32
|
-
- **Antigravity**:
|
|
33
|
-
|
|
32
|
+
- **Antigravity**: there is no `agy inspect` or equivalent command to check
|
|
33
|
+
instead. Open a **new** session — rules and skills load at session start,
|
|
34
|
+
so an already-open one will not see anything just written — give it a
|
|
35
|
+
multi-step coding task, and watch whether it invokes `cg` on its own,
|
|
36
|
+
unprompted. That behavior, not any command's output, is the verification.
|
|
34
37
|
|
|
35
38
|
### 3. `/cg-new demo` drives `cg`, not improvisation
|
|
36
39
|
|
|
@@ -54,9 +57,9 @@ intercepts it before it runs:
|
|
|
54
57
|
|
|
55
58
|
- **Claude Code**: the `ask` permission prompt appears.
|
|
56
59
|
- **OpenCode**: the `ask` permission prompt appears.
|
|
57
|
-
- **Antigravity**:
|
|
58
|
-
|
|
59
|
-
command runs.
|
|
60
|
+
- **Antigravity**: the deny hook `cg setup --host antigravity` installs by
|
|
61
|
+
default fires. If that machine was set up with `--no-hooks`, the CLI's
|
|
62
|
+
default `request-review` mode prompts before the command runs instead.
|
|
60
63
|
|
|
61
64
|
### 6. Headless, where the host has one
|
|
62
65
|
|
|
@@ -73,8 +76,6 @@ version, host version, and date — this checklist decays the moment either
|
|
|
73
76
|
side changes its config schema, so a pass from six months ago is not a pass
|
|
74
77
|
today.
|
|
75
78
|
|
|
76
|
-
- [
|
|
77
|
-
- [
|
|
78
|
-
- [
|
|
79
|
-
pending per PLAN.md F6 acceptance criterion 4; record it as such
|
|
80
|
-
rather than leaving it silently unchecked)
|
|
79
|
+
- [x] Claude Code — verified by: fdomerlo on: 2026-08-02
|
|
80
|
+
- [x] OpenCode — verified by: fdomerlo on: 2026-08-02
|
|
81
|
+
- [x] Antigravity — verified by: fdomerlo on: 2026-08-02
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "context-guard-cli"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.2.0"
|
|
8
8
|
description = "The transactional memory layer for AI coding agents — your context survives crashes, compaction, and session loss"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -410,8 +410,9 @@ class TestVerifyChecklist(unittest.TestCase):
|
|
|
410
410
|
self.assertIn("cg approve", self.text)
|
|
411
411
|
|
|
412
412
|
def test_has_a_recordable_checklist(self):
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
413
|
+
"""Markdown checkbox syntax, checked or not — this stopped pinning
|
|
414
|
+
"still unchecked" once PLAN-2.2 F3 actually completed all three
|
|
415
|
+
rows; the property worth keeping is that the format is recordable
|
|
416
|
+
at all."""
|
|
417
|
+
self.assertRegex(self.text, r"- \[[ x]\]")
|
|
417
418
|
|