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.
Files changed (76) hide show
  1. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/.github/workflows/ci.yml +2 -2
  2. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/CHANGELOG.md +51 -0
  3. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/PKG-INFO +28 -5
  4. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/README.es.md +28 -1
  5. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/README.md +27 -4
  6. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/TUTORIAL.es.md +45 -7
  7. context_guard_cli-2.2.0/context_guard/_data/hosts/antigravity/skills/context-guard/SKILL.md +59 -0
  8. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/setup.py +113 -26
  9. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/docs/adapters/VERIFY.md +12 -11
  10. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/pyproject.toml +1 -1
  11. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_adapters.py +5 -4
  12. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_assets.py +71 -0
  13. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_packaging.py +17 -4
  14. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_readme.py +20 -0
  15. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_release.py +53 -6
  16. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_setup.py +292 -0
  17. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/.agents/rules/context-guard.md +0 -0
  18. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/.githooks/pre-commit +0 -0
  19. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/.github/workflows/publish.yml +0 -0
  20. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/.gitignore +0 -0
  21. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/AGENTS.md +0 -0
  22. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/LICENSE +0 -0
  23. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/__init__.py +0 -0
  24. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/antigravity/hooks.snippet.json +0 -0
  25. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/antigravity/rules/context-guard.md +0 -0
  26. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/claude-code/commands/cg-continue.md +0 -0
  27. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/claude-code/commands/cg-new.md +0 -0
  28. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/claude-code/mcp.snippet.json +0 -0
  29. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/claude-code/settings.snippet.json +0 -0
  30. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/opencode/agent.snippet.json +0 -0
  31. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/opencode/commands/cg-continue.md +0 -0
  32. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/opencode/commands/cg-new.md +0 -0
  33. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/opencode/mcp.snippet.json +0 -0
  34. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/hosts/opencode/permissions.snippet.json +0 -0
  35. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/phases/execute.md +0 -0
  36. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/phases/plan.md +0 -0
  37. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/_data/phases/verify.md +0 -0
  38. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/__init__.py +0 -0
  39. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/assets.py +0 -0
  40. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/cli.py +0 -0
  41. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/commands.py +0 -0
  42. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/errors.py +0 -0
  43. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/locking.py +0 -0
  44. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/manifest.py +0 -0
  45. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/migrate.py +0 -0
  46. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/paths.py +0 -0
  47. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/guard/transaction.py +0 -0
  48. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/context_guard/mcp_server.py +0 -0
  49. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/docs/adapters/antigravity/PERMISSIONS.md +0 -0
  50. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/docs/adapters/claude-code/PERMISSIONS.md +0 -0
  51. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/docs/adapters/opencode/PERMISSIONS.md +0 -0
  52. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/__init__.py +0 -0
  53. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_adversarial.py +0 -0
  54. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_adversarial_approve.py +0 -0
  55. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_adversarial_migrate.py +0 -0
  56. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_adversarial_multichange.py +0 -0
  57. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_agents_md.py +0 -0
  58. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_archive.py +0 -0
  59. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_check_completion.py +0 -0
  60. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_commands.py +0 -0
  61. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_doctor.py +0 -0
  62. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_ergonomics.py +0 -0
  63. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_json_format.py +0 -0
  64. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_license.py +0 -0
  65. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_locking.py +0 -0
  66. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_manifest.py +0 -0
  67. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_mcp_server.py +0 -0
  68. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_next_task_status.py +0 -0
  69. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_phase_docs.py +0 -0
  70. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_phases.py +0 -0
  71. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_pre_commit_hook.py +0 -0
  72. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_publish.py +0 -0
  73. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_pyproject_pin.py +0 -0
  74. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_readme_es.py +0 -0
  75. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_transaction.py +0 -0
  76. {context_guard_cli-2.1.0 → context_guard_cli-2.2.0}/tests/test_validate.py +0 -0
@@ -2,9 +2,9 @@ name: CI
2
2
 
3
3
  on:
4
4
  push:
5
- branches: [main, v2, v2.1]
5
+ branches: [main, v2, v2.1, v2.2]
6
6
  pull_request:
7
- branches: [main, v2, v2.1]
7
+ branches: [main, v2, v2.1, v2.2]
8
8
 
9
9
  jobs:
10
10
  test:
@@ -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.1.0
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
- pip install context-guard-cli
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. The OpenCode and Antigravity
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
- pip install context-guard-cli
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
- pip install context-guard-cli
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. The OpenCode and Antigravity
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
- pip install context-guard-cli
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
- *(Si usás `uv`, que es más rápido: `uv tool install context-guard-cli` y
52
- después `cg setup`.)*
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
- > **Si algo falla:** el error más común es que `pip` instaló pero la terminal
68
- > no encuentra `cg`. Cerrá y reabrí la terminal. Si persiste, probá
69
- > `python3 -m pip install --user context-guard-cli` y de nuevo cerrar/reabrir.
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): `pip install context-guard-cli` y luego `cg setup` una vez por máquina.
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 _install_antigravity(root, global_scope, no_hooks=False):
252
- """Global scope installs the deny hook; project scope installs the rule.
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
- The split follows what each artifact is for. The hook lives in user
255
- config by definition, so it has no meaning in a project install; the
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 `(touched, failure)`. Unlike the other hosts this one merges into
260
- a file it did not create and cannot fully predict, so it needs a way to
261
- decline without taking the whole run down with it.
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
- for relpath, text in iter_host_files("antigravity"):
265
- if relpath != "rules/context-guard.md":
266
- continue
267
- _write_text(os.path.join(root, ".agents", "rules", "context-guard.md"), text)
268
- return [".agents/rules/context-guard.md"], None
269
- return [], None
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 [], None
333
+ return touched, None, skips
273
334
 
274
- hooks_rel = os.path.join(".gemini", "config", "hooks.json")
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 [], f"FAIL|HOOKS_UNPARSEABLE|{hooks_path}|{HOOKS_MANUAL_FIX}"
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 [], f"FAIL|HOOKS_UNRECOGNISED|{hooks_path}|{HOOKS_MANUAL_FIX} ({problem})"
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
- return [f"{hooks_rel.replace(os.sep, '/')} {HOOKS_NOTE}"], None
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
- return os.path.isdir(os.path.join(home, ".gemini")) or _on_path("antigravity")
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(root, global_scope, no_hooks)
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 skipped (--no-hooks)")
463
+ lines.append(" -> Antigravity: skill installed, deny hook "
464
+ "skipped (--no-hooks)")
387
465
  else:
388
- lines.append(" -> Antigravity: deny hook merged into "
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 command / rule is visible to the host
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**: `/skills` (or the equivalent surface) shows the rule
33
- loaded, confirmed with `agy inspect` if available.
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**: either the `--with-antigravity-hook` deny fires, or (if
58
- not installed) the CLI's default `request-review` mode prompts before the
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
- - [ ] Claude Code — verified by: __________ on: __________
77
- - [ ] OpenCode — verified by: __________ on: __________
78
- - [ ] Antigravity — verified by: __________ on: __________ (may remain
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.1.0"
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
- self.assertIn("- [ ]", self.text)
414
-
415
- def test_allows_antigravity_to_remain_pending(self):
416
- self.assertIn("pending", self.text.lower())
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