context-guard-cli 2.1.0__py3-none-any.whl
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/__init__.py +1 -0
- context_guard/_data/hosts/antigravity/hooks.snippet.json +16 -0
- context_guard/_data/hosts/antigravity/rules/context-guard.md +15 -0
- context_guard/_data/hosts/claude-code/commands/cg-continue.md +13 -0
- context_guard/_data/hosts/claude-code/commands/cg-new.md +11 -0
- context_guard/_data/hosts/claude-code/mcp.snippet.json +7 -0
- context_guard/_data/hosts/claude-code/settings.snippet.json +12 -0
- context_guard/_data/hosts/opencode/agent.snippet.json +7 -0
- context_guard/_data/hosts/opencode/commands/cg-continue.md +16 -0
- context_guard/_data/hosts/opencode/commands/cg-new.md +12 -0
- context_guard/_data/hosts/opencode/mcp.snippet.json +9 -0
- context_guard/_data/hosts/opencode/permissions.snippet.json +12 -0
- context_guard/_data/phases/execute.md +99 -0
- context_guard/_data/phases/plan.md +136 -0
- context_guard/_data/phases/verify.md +129 -0
- context_guard/guard/__init__.py +1 -0
- context_guard/guard/assets.py +94 -0
- context_guard/guard/cli.py +307 -0
- context_guard/guard/commands.py +811 -0
- context_guard/guard/errors.py +71 -0
- context_guard/guard/locking.py +181 -0
- context_guard/guard/manifest.py +69 -0
- context_guard/guard/migrate.py +288 -0
- context_guard/guard/paths.py +199 -0
- context_guard/guard/setup.py +476 -0
- context_guard/guard/transaction.py +403 -0
- context_guard/mcp_server.py +280 -0
- context_guard_cli-2.1.0.dist-info/METADATA +296 -0
- context_guard_cli-2.1.0.dist-info/RECORD +32 -0
- context_guard_cli-2.1.0.dist-info/WHEEL +4 -0
- context_guard_cli-2.1.0.dist-info/entry_points.txt +4 -0
- context_guard_cli-2.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""context-guard package."""
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": {
|
|
6
|
+
"tool": "run_command",
|
|
7
|
+
"commandPattern": "^\\s*(cg|context-guard)\\s+approve\\b"
|
|
8
|
+
},
|
|
9
|
+
"action": {
|
|
10
|
+
"decision": "deny",
|
|
11
|
+
"message": "human-only command — ask the user to run it"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!-- context-guard:begin -->
|
|
2
|
+
## ACTIVE PERSISTENCE CONTRACT: context-guard
|
|
3
|
+
MANDATORY BOOTSTRAP — read before responding to anything in a project that
|
|
4
|
+
has a `.context-guard/` directory:
|
|
5
|
+
1. Read `AGENTS.md` at the project root and follow it as your state contract.
|
|
6
|
+
2. State manager binary: `cg` (or `context-guard`). Operative subcommands:
|
|
7
|
+
begin | commit | rollback | checkpoint | status | next-task | validate
|
|
8
|
+
Human-only subcommand (never run by the agent): `cg approve`.
|
|
9
|
+
If `commit` returns EXIT_APPROVAL_REQUIRED (6): stop and ask the user to
|
|
10
|
+
run `cg approve --change <name>` themselves.
|
|
11
|
+
3. Check `.context-guard/changes/*/manifest.json` for an active change and
|
|
12
|
+
act accordingly (cold start, resume via `cg status`, or report a stuck
|
|
13
|
+
lock — never fix a stuck lock by editing the manifest by hand).
|
|
14
|
+
4. Phase instructions live in `.context-guard/phases/{plan,execute,verify}.md`.
|
|
15
|
+
<!-- context-guard:end -->
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Resume the active context-guard change from where it left off
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Change (optional): $ARGUMENTS
|
|
6
|
+
|
|
7
|
+
1. Run `cg status [--change $ARGUMENTS]`.
|
|
8
|
+
2. Load and follow, exactly, the file matching the reported `lock_phase`:
|
|
9
|
+
- PLAN -> `.context-guard/phases/plan.md`
|
|
10
|
+
- EXECUTE -> `.context-guard/phases/execute.md`
|
|
11
|
+
- VERIFY -> `.context-guard/phases/verify.md`
|
|
12
|
+
3. If the lock is held by another agent, stop and report the conflict —
|
|
13
|
+
do not retry automatically.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Start a new context-guard change and run its PLAN phase
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Change name: $ARGUMENTS (ask the user for one if empty).
|
|
6
|
+
|
|
7
|
+
1. Run `cg new $ARGUMENTS`
|
|
8
|
+
2. Load `.context-guard/phases/plan.md` and follow it exactly for this
|
|
9
|
+
change, through its human review gate.
|
|
10
|
+
3. Never run `cg commit --next-phase EXECUTE` without an explicit
|
|
11
|
+
go-ahead from the user in this chat.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Merge into .claude/settings.json. PLAN.md 0.6: cg approve is cooperative by itself; putting it on the ask list is the hard control that pairs with it.",
|
|
3
|
+
"permissions": {
|
|
4
|
+
"ask": [
|
|
5
|
+
"Bash(cg approve*)",
|
|
6
|
+
"Bash(context-guard approve*)"
|
|
7
|
+
],
|
|
8
|
+
"deny": [
|
|
9
|
+
"Edit(.context-guard/**/manifest.json)"
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"context-guard": {
|
|
3
|
+
"mode": "all",
|
|
4
|
+
"description": "Context Guard — transactional memory for AI coding agents",
|
|
5
|
+
"prompt": "Read AGENTS.md at the project root and follow it as your state contract. Phase instructions live in .context-guard/phases/{plan,execute,verify}.md. Use the cg CLI (begin/commit/rollback/checkpoint/status/next-task/validate); cg approve is a human-only command. If commit returns exit code 6 (APPROVAL_REQUIRED), stop and ask the user to run cg approve themselves."
|
|
6
|
+
}
|
|
7
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Resume the active context-guard change from where it left off
|
|
3
|
+
agent: context-guard
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
!`cg status --format json`
|
|
7
|
+
|
|
8
|
+
Change (optional): $ARGUMENTS. The status above is already current — do not
|
|
9
|
+
re-run `cg status` yourself.
|
|
10
|
+
|
|
11
|
+
1. Load and follow, exactly, the file matching the reported `lock_phase`:
|
|
12
|
+
- PLAN -> `.context-guard/phases/plan.md`
|
|
13
|
+
- EXECUTE -> `.context-guard/phases/execute.md`
|
|
14
|
+
- VERIFY -> `.context-guard/phases/verify.md`
|
|
15
|
+
2. If the lock is held by another agent, stop and report the conflict —
|
|
16
|
+
do not retry automatically.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Start a new context-guard change and run its PLAN phase
|
|
3
|
+
agent: context-guard
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Change name: $ARGUMENTS (ask the user for one if empty).
|
|
7
|
+
|
|
8
|
+
1. Run `cg new $ARGUMENTS`
|
|
9
|
+
2. Load `.context-guard/phases/plan.md` and follow it exactly for this
|
|
10
|
+
change, through its human review gate.
|
|
11
|
+
3. Never run `cg commit --next-phase EXECUTE` without an explicit
|
|
12
|
+
go-ahead from the user in this chat.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# EXECUTE Phase
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
EXECUTE is the only phase that changes the project's source code. It works
|
|
6
|
+
through `tasks.md` task by task until the change is implemented.
|
|
7
|
+
|
|
8
|
+
**Prerequisite:** `lock_phase == EXECUTE` — this only happens after the human
|
|
9
|
+
approved the PLAN commit.
|
|
10
|
+
|
|
11
|
+
## What to do
|
|
12
|
+
|
|
13
|
+
### Step 1: Begin the transaction
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cg begin --context <path> --phase EXECUTE --change <change-name>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Step 2: Read the context
|
|
20
|
+
|
|
21
|
+
Before writing any code:
|
|
22
|
+
|
|
23
|
+
1. **Plan** — read `objective.md` for intent and scope.
|
|
24
|
+
2. **Tasks** — read `tasks.md` for the full breakdown.
|
|
25
|
+
3. **Existing code** — read the files each task actually touches.
|
|
26
|
+
|
|
27
|
+
### Step 3: Detect TDD mode
|
|
28
|
+
|
|
29
|
+
Check, in order: a project config declaring `tdd: true`, existing test
|
|
30
|
+
patterns in the codebase (a `tests/` directory with a consistent style is a
|
|
31
|
+
strong signal), otherwise assume standard mode (code first).
|
|
32
|
+
|
|
33
|
+
### Step 4: Implement tasks
|
|
34
|
+
|
|
35
|
+
**TDD mode (RED → GREEN → REFACTOR)**, for each task:
|
|
36
|
+
1. Read the task description and any related acceptance criteria.
|
|
37
|
+
2. **RED**: write a test describing the expected behavior → confirm it FAILS.
|
|
38
|
+
3. **GREEN**: implement the minimum code to make it pass → confirm it PASSES.
|
|
39
|
+
4. **REFACTOR**: clean up without changing behavior → confirm it still PASSES.
|
|
40
|
+
5. Check the task off in `tasks.md` (`- [x]`).
|
|
41
|
+
|
|
42
|
+
> CRITICAL: run tests in a real terminal. Simulating or inferring results is
|
|
43
|
+
> not allowed.
|
|
44
|
+
|
|
45
|
+
**Standard mode**, for each task: read the description, read existing code
|
|
46
|
+
patterns, write the code, check the task off.
|
|
47
|
+
|
|
48
|
+
Default batch size: 3 tasks per invocation, adjusted to available context.
|
|
49
|
+
For task lists that support parallel work, `cg next-task --context <path>
|
|
50
|
+
--change <name>` and `cg claim-task` let multiple agents work the same
|
|
51
|
+
change without stepping on each other — each claim carries a lease, so a
|
|
52
|
+
crashed agent's tasks become available again automatically; see `AGENTS.md`.
|
|
53
|
+
|
|
54
|
+
### Step 5: Verify progress with the middleware
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
cg check-completion --context <path> --change <change-name>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Reports `total`, `completed`, and whether every task is done — the agent
|
|
61
|
+
never counts checkboxes by hand.
|
|
62
|
+
|
|
63
|
+
### Step 6: Report
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
## Implementation progress
|
|
67
|
+
|
|
68
|
+
**Change**: {change-name}
|
|
69
|
+
**Mode**: {TDD | Standard}
|
|
70
|
+
|
|
71
|
+
### Completed tasks
|
|
72
|
+
- [x] [T001] {description}
|
|
73
|
+
|
|
74
|
+
### Files changed
|
|
75
|
+
| File | Action | What happened |
|
|
76
|
+
|------|--------|----------------|
|
|
77
|
+
|
|
78
|
+
### Deviations from the plan
|
|
79
|
+
{List, or "None — implementation matches the plan."}
|
|
80
|
+
|
|
81
|
+
### Issues found
|
|
82
|
+
{List, or "None."}
|
|
83
|
+
|
|
84
|
+
### Status
|
|
85
|
+
{N}/{total} tasks complete. {Ready for VERIFY / next batch pending}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Rules
|
|
89
|
+
|
|
90
|
+
- Always follow the plan's decisions — do not improvise architecture.
|
|
91
|
+
- Always match existing code patterns and conventions.
|
|
92
|
+
- Check tasks off in `tasks.md` as you complete them, never in bulk at the
|
|
93
|
+
end.
|
|
94
|
+
- If the plan turns out to be wrong or incomplete, write it down as a
|
|
95
|
+
deviation — never diverge silently.
|
|
96
|
+
- If a task is blocked, stop and report it instead of skipping ahead.
|
|
97
|
+
|
|
98
|
+
Once every task is checked off, run `cg commit --context <path> --change
|
|
99
|
+
<change-name> --next-phase VERIFY` and move on to `phases/verify.md`.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# PLAN Phase
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
PLAN absorbs exploration, proposal, and scoping into a single block of work.
|
|
6
|
+
It produces `objective.md` and `tasks.md` and submits them to a **mandatory
|
|
7
|
+
human review** before the change is allowed into EXECUTE.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
draft → human review → commit
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The commit into EXECUTE is the event that closes PLAN. **The agent must
|
|
14
|
+
never run that commit without an explicit go-ahead from the human.**
|
|
15
|
+
|
|
16
|
+
## What to do
|
|
17
|
+
|
|
18
|
+
### Step 1: Begin the transaction
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cg begin --context <path> --phase PLAN --change <change-name>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If the change is new, `cg new <change-name> --context <path>` does this for
|
|
25
|
+
you and scaffolds `objective.md`, `snapshot.md`, `tasks.md`,
|
|
26
|
+
`review-report.md`, `verify-report.md` in
|
|
27
|
+
`.context-guard/changes/<change-name>/`, all starting as `[PENDING]`.
|
|
28
|
+
|
|
29
|
+
### Step 2: Discover the stack
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
ls package.json pyproject.toml composer.json go.mod Cargo.toml docker-compose.yml 2>/dev/null
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Read whatever manifests exist to identify the stack, framework, and tooling
|
|
36
|
+
before analyzing anything.
|
|
37
|
+
|
|
38
|
+
### Step 3: Explore and analyze
|
|
39
|
+
|
|
40
|
+
- Is this new functionality, a bug fix, or a refactor?
|
|
41
|
+
- Read the relevant code: entry points, affected modules, existing tests.
|
|
42
|
+
- Compare approaches if there are real alternatives.
|
|
43
|
+
|
|
44
|
+
### Step 4: Draft the artifacts
|
|
45
|
+
|
|
46
|
+
Write directly to disk, replacing the `[PENDING]` scaffolds:
|
|
47
|
+
|
|
48
|
+
**`objective.md`**
|
|
49
|
+
```markdown
|
|
50
|
+
# Objective: {Change Title}
|
|
51
|
+
|
|
52
|
+
## Intent
|
|
53
|
+
{What problem this solves and why}
|
|
54
|
+
|
|
55
|
+
## Scope
|
|
56
|
+
### In scope
|
|
57
|
+
- {deliverable}
|
|
58
|
+
### Out of scope
|
|
59
|
+
- {deferred}
|
|
60
|
+
|
|
61
|
+
## Success criteria
|
|
62
|
+
- [ ] {measurable outcome}
|
|
63
|
+
|
|
64
|
+
## Open questions
|
|
65
|
+
- [ ] {unresolved question — mark blocking ones with [!]}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**`tasks.md`**
|
|
69
|
+
```markdown
|
|
70
|
+
# Tasks: {Change Title}
|
|
71
|
+
|
|
72
|
+
## Phase 1: {e.g. Infrastructure}
|
|
73
|
+
- [ ] [T001] {atomic task with a concrete file path}
|
|
74
|
+
|
|
75
|
+
## Phase 2: {e.g. Core implementation}
|
|
76
|
+
- [ ] [T002] {atomic task}
|
|
77
|
+
|
|
78
|
+
## Phase 3: {e.g. Testing}
|
|
79
|
+
- [ ] [T003] {atomic task}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Task rules: one task = one file or logical module (no "monster tasks"); IDs
|
|
83
|
+
follow `[Txxx]` — `check-completion` and `next-task` parse them; group by
|
|
84
|
+
infrastructure → implementation → testing; reference success criteria as
|
|
85
|
+
acceptance checks.
|
|
86
|
+
|
|
87
|
+
Optionally update `snapshot.md` with a short state-of-the-world summary if
|
|
88
|
+
the change spans multiple sessions — `cg validate` requires the file to
|
|
89
|
+
exist (alongside `objective.md`) and checks size and language, though it
|
|
90
|
+
does not check for a leftover `[PENDING]`; that is `commit`'s job.
|
|
91
|
+
|
|
92
|
+
### Step 5: Human review gate
|
|
93
|
+
|
|
94
|
+
**The agent cannot proceed on its own — this is the one barrier only a human
|
|
95
|
+
can cross.**
|
|
96
|
+
|
|
97
|
+
1. Present `objective.md` and `tasks.md` with an executive summary.
|
|
98
|
+
2. List architecture decisions and open questions explicitly.
|
|
99
|
+
3. Run `cg validate --context <path> --change <change-name>` and fix
|
|
100
|
+
anything it flags (missing file, oversized artifact, non-English text) —
|
|
101
|
+
it does not check for `[PENDING]`; confirm by eye that both artifacts
|
|
102
|
+
are actually filled in before asking for approval.
|
|
103
|
+
4. Ask the human, in this conversation, to review and confirm.
|
|
104
|
+
5. Ask them to record it — **never run this yourself**:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
cg approve --context <path> --change <change-name> --by <who>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Without it, Step 6 fails with `APPROVAL_REQUIRED` (exit 6). The approval is
|
|
111
|
+
spent by the commit it authorizes, so if the plan is revised afterwards the
|
|
112
|
+
human has to approve again. The harness's permission prompt on `cg approve`
|
|
113
|
+
(configured per `adapters/*/PERMISSIONS.md`) is what makes that confirmation
|
|
114
|
+
hard to skip rather than merely polite.
|
|
115
|
+
|
|
116
|
+
### Step 6: Commit and close PLAN
|
|
117
|
+
|
|
118
|
+
Only after the human confirms:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
cg commit --context <path> --change <change-name> --next-phase EXECUTE
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This advances `lock_phase` to `EXECUTE`, releases the phase lock, and
|
|
125
|
+
auto-generates a checkpoint of the DAG state. Report to the user that PLAN is
|
|
126
|
+
locked and EXECUTE is open, with the suggested next command: `/cg-continue`.
|
|
127
|
+
|
|
128
|
+
## Rules
|
|
129
|
+
|
|
130
|
+
- Never commit into EXECUTE without explicit human approval.
|
|
131
|
+
- If the human requests changes, regenerate only the sections asked for, not
|
|
132
|
+
the whole artifact.
|
|
133
|
+
- Blocking open questions (`[!]`) must be resolved before requesting review.
|
|
134
|
+
- Always read the real code — never assume about the codebase.
|
|
135
|
+
- `objective.md` and `tasks.md` are separate artifacts with separate
|
|
136
|
+
purposes; do not merge them into one file.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# VERIFY Phase
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
VERIFY is the final quality gate. It proves — with evidence from real
|
|
6
|
+
execution, not static reasoning alone — that the implementation is complete
|
|
7
|
+
and correct. If the verdict is APPROVED, the **archive step** runs
|
|
8
|
+
immediately as part of this same phase.
|
|
9
|
+
|
|
10
|
+
## What to do
|
|
11
|
+
|
|
12
|
+
### Step 1: Begin the transaction
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
cg begin --context <path> --phase VERIFY --change <change-name>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Step 2: Read the context
|
|
19
|
+
|
|
20
|
+
1. **Plan** — `objective.md` for intent and success criteria.
|
|
21
|
+
2. **Tasks** — `tasks.md` for what was supposed to happen.
|
|
22
|
+
|
|
23
|
+
### Step 3: Check completeness
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cg check-completion --context <path> --change <change-name>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
List any incomplete tasks. Mark CRITICAL if core tasks are unfinished,
|
|
30
|
+
WARNING if only cleanup tasks remain.
|
|
31
|
+
|
|
32
|
+
### Step 4: Check correctness against the objective
|
|
33
|
+
|
|
34
|
+
For each success criterion in `objective.md`: is there real evidence in the
|
|
35
|
+
codebase that it's met? Mark CRITICAL if a criterion has no implementation,
|
|
36
|
+
WARNING if only partially covered.
|
|
37
|
+
|
|
38
|
+
### Step 5: Run tests and build for real
|
|
39
|
+
|
|
40
|
+
> CRITICAL: use a real terminal. Simulating or inferring results is not
|
|
41
|
+
> allowed.
|
|
42
|
+
|
|
43
|
+
Detect and run the project's test command (`package.json` scripts, `pytest`/
|
|
44
|
+
`pyproject.toml`, a `Makefile` target, or whatever `objective.md` calls out)
|
|
45
|
+
and its build/typecheck command if one exists. A missing build step is a
|
|
46
|
+
WARNING, not a CRITICAL — some projects have none.
|
|
47
|
+
|
|
48
|
+
### Step 6: Write `review-report.md` and `verify-report.md`
|
|
49
|
+
|
|
50
|
+
`review-report.md` covers static findings (code quality, plan adherence,
|
|
51
|
+
deviations). `verify-report.md` covers dynamic evidence:
|
|
52
|
+
|
|
53
|
+
```markdown
|
|
54
|
+
## Verification report
|
|
55
|
+
|
|
56
|
+
**Change**: {change-name}
|
|
57
|
+
|
|
58
|
+
### Completeness
|
|
59
|
+
| Metric | Value |
|
|
60
|
+
|--------|-------|
|
|
61
|
+
| Total tasks | {N} |
|
|
62
|
+
| Completed | {N} |
|
|
63
|
+
| Incomplete | {N} |
|
|
64
|
+
|
|
65
|
+
### Build and test run
|
|
66
|
+
**Build**: pass / fail
|
|
67
|
+
**Tests**: {N} passed / {N} failed / {N} skipped
|
|
68
|
+
|
|
69
|
+
### Issues found
|
|
70
|
+
**CRITICAL**: {list, or "None"}
|
|
71
|
+
**WARNING**: {list, or "None"}
|
|
72
|
+
|
|
73
|
+
### Verdict
|
|
74
|
+
{APPROVED / APPROVED WITH WARNINGS / REJECTED}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Step 7: Decide
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
If there are CRITICAL issues:
|
|
81
|
+
→ run `cg rollback --context <path> --change <change-name>`
|
|
82
|
+
→ report the issues to the user; the change returns to EXECUTE
|
|
83
|
+
|
|
84
|
+
If the verdict is APPROVED or APPROVED WITH WARNINGS:
|
|
85
|
+
→ run `cg commit --context <path> --change <change-name> --next-phase ARCHIVE`
|
|
86
|
+
→ continue immediately to Step 8 (archive), in the same invocation
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### Step 8: ARCHIVE — close out the change
|
|
92
|
+
|
|
93
|
+
This runs automatically after an APPROVED verdict, in the same `/cg-continue`
|
|
94
|
+
invocation that ran VERIFY. There is no separate manual archive command for
|
|
95
|
+
this path.
|
|
96
|
+
|
|
97
|
+
1. Confirm `verify-report.md` and `review-report.md` contain no `[PENDING]`
|
|
98
|
+
and no unresolved CRITICAL issue. If they do, abort.
|
|
99
|
+
2. Check `git status --porcelain`: a dirty tree blocks archiving — ask the
|
|
100
|
+
user to commit first.
|
|
101
|
+
3. Run:
|
|
102
|
+
```bash
|
|
103
|
+
cg archive --context <path> --change <change-name>
|
|
104
|
+
```
|
|
105
|
+
This copies `.context-guard/changes/<change-name>/` to
|
|
106
|
+
`.context-guard/changes/archive/<change-name>/` and removes the live
|
|
107
|
+
change directory. The archive is an audit trail — never edit or delete an
|
|
108
|
+
archived change.
|
|
109
|
+
4. Report to the user:
|
|
110
|
+
```markdown
|
|
111
|
+
## Change archived
|
|
112
|
+
|
|
113
|
+
**Change**: {change-name}
|
|
114
|
+
**Archived at**: .context-guard/changes/archive/{change-name}/
|
|
115
|
+
|
|
116
|
+
The change has been planned, implemented, verified, and archived.
|
|
117
|
+
Ready for the next `/cg-new`.
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Rules
|
|
121
|
+
|
|
122
|
+
- Always read real source and run real tests — static analysis alone is not
|
|
123
|
+
verification.
|
|
124
|
+
- CRITICAL issues must be resolved before archiving.
|
|
125
|
+
- WARNING issues should be resolved but do not block.
|
|
126
|
+
- Do not fix problems found during VERIFY — only report them; fixes happen
|
|
127
|
+
back in EXECUTE.
|
|
128
|
+
- Never archive while `verify-report.md` has an unresolved CRITICAL issue.
|
|
129
|
+
- Never modify or delete an already-archived change.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# context-guard middleware package
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Accessors for the artifacts shipped as package data (PLAN-2.1 F1).
|
|
2
|
+
|
|
3
|
+
Everything a host needs — the three phase documents, each host's slash
|
|
4
|
+
commands, rule files and config snippets — lives under `context_guard/_data/`
|
|
5
|
+
and is reached through `importlib.resources`. Never through paths relative to
|
|
6
|
+
this file or to the working directory: the package has to work installed from
|
|
7
|
+
a wheel, with no clone of the repo anywhere on the machine.
|
|
8
|
+
|
|
9
|
+
Names are validated against a fixed allowlist rather than joined onto the data
|
|
10
|
+
directory. These accessors are called with values that come from CLI flags, so
|
|
11
|
+
a joined path would turn `cg` into a reader for arbitrary files.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from importlib import resources
|
|
15
|
+
|
|
16
|
+
from .errors import EXIT_GENERIC, GuardError
|
|
17
|
+
|
|
18
|
+
PHASES = ("plan", "execute", "verify")
|
|
19
|
+
|
|
20
|
+
HOSTS = ("claude-code", "opencode", "antigravity")
|
|
21
|
+
|
|
22
|
+
# Which snippets each host ships, keyed by the short name callers pass. The
|
|
23
|
+
# file is always "<name>.snippet.json"; the mapping exists to be an allowlist,
|
|
24
|
+
# not to spell out a naming convention.
|
|
25
|
+
SNIPPETS = {
|
|
26
|
+
"claude-code": ("settings", "mcp"),
|
|
27
|
+
"opencode": ("agent", "permissions", "mcp"),
|
|
28
|
+
"antigravity": ("hooks",),
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AssetNotFoundError(GuardError):
|
|
33
|
+
"""A requested phase, host or snippet is not part of the packaged data."""
|
|
34
|
+
|
|
35
|
+
def __init__(self, message):
|
|
36
|
+
super().__init__(f"FAIL|ASSET_NOT_FOUND|{message}", EXIT_GENERIC)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _data_root():
|
|
40
|
+
"""The `_data` directory as a Traversable.
|
|
41
|
+
|
|
42
|
+
`files()` targets the package and joins from there, so this resolves the
|
|
43
|
+
same whether the package sits in a source checkout or in site-packages.
|
|
44
|
+
"""
|
|
45
|
+
return resources.files("context_guard").joinpath("_data")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_phase(name):
|
|
49
|
+
"""Return the text of `_data/phases/<name>.md`.
|
|
50
|
+
|
|
51
|
+
`name` must be one of PHASES — anything else raises, including a value that
|
|
52
|
+
happens to name a real file elsewhere in the tree.
|
|
53
|
+
"""
|
|
54
|
+
if name not in PHASES:
|
|
55
|
+
raise AssetNotFoundError(f"unknown phase '{name}' (expected one of {', '.join(PHASES)})")
|
|
56
|
+
return _data_root().joinpath("phases", f"{name}.md").read_text(encoding="utf-8")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def iter_host_files(host):
|
|
60
|
+
"""Yield `(relpath, text)` for every file this host installs.
|
|
61
|
+
|
|
62
|
+
`relpath` is relative to the host's directory and always uses forward
|
|
63
|
+
slashes, because callers join it onto a target directory to write the file
|
|
64
|
+
out. Raises for an unknown host instead of yielding nothing: a `cg setup`
|
|
65
|
+
that installs zero files and exits 0 looks exactly like success.
|
|
66
|
+
"""
|
|
67
|
+
if host not in HOSTS:
|
|
68
|
+
raise AssetNotFoundError(f"unknown host '{host}' (expected one of {', '.join(HOSTS)})")
|
|
69
|
+
|
|
70
|
+
root = _data_root().joinpath("hosts", host)
|
|
71
|
+
|
|
72
|
+
def walk(node, prefix):
|
|
73
|
+
# Sorted so the file order — and therefore any output listing what was
|
|
74
|
+
# installed — is stable across platforms and filesystems.
|
|
75
|
+
for child in sorted(node.iterdir(), key=lambda c: c.name):
|
|
76
|
+
relpath = f"{prefix}{child.name}"
|
|
77
|
+
if child.is_dir():
|
|
78
|
+
yield from walk(child, f"{relpath}/")
|
|
79
|
+
else:
|
|
80
|
+
yield relpath, child.read_text(encoding="utf-8")
|
|
81
|
+
|
|
82
|
+
yield from walk(root, "")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def read_snippet(host, name):
|
|
86
|
+
"""Return the text of `_data/hosts/<host>/<name>.snippet.json`."""
|
|
87
|
+
if host not in HOSTS:
|
|
88
|
+
raise AssetNotFoundError(f"unknown host '{host}' (expected one of {', '.join(HOSTS)})")
|
|
89
|
+
if name not in SNIPPETS[host]:
|
|
90
|
+
raise AssetNotFoundError(
|
|
91
|
+
f"host '{host}' has no '{name}' snippet "
|
|
92
|
+
f"(expected one of {', '.join(SNIPPETS[host])})")
|
|
93
|
+
return _data_root().joinpath(
|
|
94
|
+
"hosts", host, f"{name}.snippet.json").read_text(encoding="utf-8")
|