guard-cli 0.1.1__tar.gz → 0.1.2__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.
- {guard_cli-0.1.1 → guard_cli-0.1.2}/PKG-INFO +15 -5
- {guard_cli-0.1.1 → guard_cli-0.1.2}/README.md +10 -4
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/__init__.py +1 -1
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/api.py +43 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/cli.py +35 -12
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/doctor_cmd.py +8 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/hook.py +328 -137
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/ingest/coverage.py +13 -2
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/init_cmd.py +10 -8
- guard_cli-0.1.2/guard_cli/integrations/antigravity.py +258 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/integrations/claude.py +70 -34
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/state.py +55 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli.egg-info/PKG-INFO +15 -5
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli.egg-info/SOURCES.txt +2 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/pyproject.toml +7 -1
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_antigravity_integration.py +2 -2
- guard_cli-0.1.2/tests/test_antigravity_pre_recording_fixes.py +345 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_api_and_hook.py +91 -13
- guard_cli-0.1.2/tests/test_commit_bypass_fresh_session.py +177 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_integration_init_doctor.py +5 -1
- guard_cli-0.1.1/guard_cli/integrations/antigravity.py +0 -154
- {guard_cli-0.1.1 → guard_cli-0.1.2}/LICENSE +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/agent_integration.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/backend.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/db.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/decision.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/events.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/feedback.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/ingest/__init__.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/ingest/static.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/integrations/__init__.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli/thin_slice.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli.egg-info/dependency_links.txt +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli.egg-info/entry_points.txt +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli.egg-info/requires.txt +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/guard_cli.egg-info/top_level.txt +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/setup.cfg +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_backend.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_commit_bypass.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_coverage.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_db.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_decision.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_doctor_cmd.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_embedded_platform.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_event_reasons.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_feedback.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_fingerprint_invalidation.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_hook_timeout.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_init_cmd.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_static.py +0 -0
- {guard_cli-0.1.1 → guard_cli-0.1.2}/tests/test_thin_slice.py +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: guard-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Graph-powered safety layer for AI coding agents built on FalkorDB
|
|
5
5
|
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/thisisvaishnav/guard
|
|
7
|
+
Project-URL: Repository, https://github.com/thisisvaishnav/guard
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/thisisvaishnav/guard/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/thisisvaishnav/guard/blob/main/CHANGELOG.md
|
|
6
10
|
Requires-Python: >=3.10
|
|
7
11
|
Description-Content-Type: text/markdown
|
|
8
12
|
License-File: LICENSE
|
|
@@ -176,7 +180,13 @@ $$\text{Trust} = 0.5 \cdot \text{EvidenceRatio} + 0.3 \cdot \text{Freshness} + 0
|
|
|
176
180
|
|
|
177
181
|
## Claude Code & Agent Integration
|
|
178
182
|
|
|
179
|
-
|
|
183
|
+
> **Experimental**: Both the Claude Code Stop hook and the Antigravity integration are
|
|
184
|
+
> labeled **experimental**. The Antigravity `hooks.json` schema was verified against
|
|
185
|
+
> [Antigravity's official hook documentation](https://antigravity.google/docs/hooks), but
|
|
186
|
+
> both integrations have only been validated with simulated stdin/stdout input. A real
|
|
187
|
+
> live session (Claude Code, Antigravity) has not yet confirmed end-to-end behavior.
|
|
188
|
+
|
|
189
|
+
Guard integrates into autonomous coding workflows:
|
|
180
190
|
|
|
181
191
|
- **Claude Code Stop Hook**: Installed via `guard hook install` into `.claude/settings.local.json`. Whenever Claude attempts to conclude a task, the Stop hook evaluates the diff. If tests fail, it blocks completion (Exit 2) and injects a concise $\le$ 30 line failure report.
|
|
182
192
|
- **Idle-Turn Optimization**: Caches working-tree SHA-256 fingerprints and skips unchanged turns and non-Python diffs.
|
|
@@ -228,11 +238,11 @@ guard check [--repo /path/to/repo] [--json]
|
|
|
228
238
|
```
|
|
229
239
|
|
|
230
240
|
### `guard hook`
|
|
231
|
-
Manages Claude Code and agent hook configurations:
|
|
241
|
+
Manages Claude Code and agent hook configurations (experimental — see note above):
|
|
232
242
|
```bash
|
|
233
|
-
guard hook install [--shared]
|
|
243
|
+
guard hook install [--shared] [--antigravity]
|
|
234
244
|
guard hook status
|
|
235
|
-
guard hook uninstall
|
|
245
|
+
guard hook uninstall [--antigravity]
|
|
236
246
|
```
|
|
237
247
|
|
|
238
248
|
### `guard feedback`
|
|
@@ -155,7 +155,13 @@ $$\text{Trust} = 0.5 \cdot \text{EvidenceRatio} + 0.3 \cdot \text{Freshness} + 0
|
|
|
155
155
|
|
|
156
156
|
## Claude Code & Agent Integration
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
> **Experimental**: Both the Claude Code Stop hook and the Antigravity integration are
|
|
159
|
+
> labeled **experimental**. The Antigravity `hooks.json` schema was verified against
|
|
160
|
+
> [Antigravity's official hook documentation](https://antigravity.google/docs/hooks), but
|
|
161
|
+
> both integrations have only been validated with simulated stdin/stdout input. A real
|
|
162
|
+
> live session (Claude Code, Antigravity) has not yet confirmed end-to-end behavior.
|
|
163
|
+
|
|
164
|
+
Guard integrates into autonomous coding workflows:
|
|
159
165
|
|
|
160
166
|
- **Claude Code Stop Hook**: Installed via `guard hook install` into `.claude/settings.local.json`. Whenever Claude attempts to conclude a task, the Stop hook evaluates the diff. If tests fail, it blocks completion (Exit 2) and injects a concise $\le$ 30 line failure report.
|
|
161
167
|
- **Idle-Turn Optimization**: Caches working-tree SHA-256 fingerprints and skips unchanged turns and non-Python diffs.
|
|
@@ -207,11 +213,11 @@ guard check [--repo /path/to/repo] [--json]
|
|
|
207
213
|
```
|
|
208
214
|
|
|
209
215
|
### `guard hook`
|
|
210
|
-
Manages Claude Code and agent hook configurations:
|
|
216
|
+
Manages Claude Code and agent hook configurations (experimental — see note above):
|
|
211
217
|
```bash
|
|
212
|
-
guard hook install [--shared]
|
|
218
|
+
guard hook install [--shared] [--antigravity]
|
|
213
219
|
guard hook status
|
|
214
|
-
guard hook uninstall
|
|
220
|
+
guard hook uninstall [--antigravity]
|
|
215
221
|
```
|
|
216
222
|
|
|
217
223
|
### `guard feedback`
|
|
@@ -227,6 +227,49 @@ def run(
|
|
|
227
227
|
"fail_open": True,
|
|
228
228
|
}, 0
|
|
229
229
|
|
|
230
|
+
# Fail-open short-circuit: if check() could not evaluate the diff (database/graph
|
|
231
|
+
# unavailable), do not attempt the full-suite fallback. Running the whole suite on an
|
|
232
|
+
# unknown diff is itself unreliable (e.g. Guard's own tests require FalkorDB), so we
|
|
233
|
+
# allow the stop with a loud warning and record a fail_open event, matching Guard's
|
|
234
|
+
# documented fail-open behavior instead of blocking the agent.
|
|
235
|
+
if check_dict.get("fail_open") or check_dict.get("policy") == "fail_open":
|
|
236
|
+
reason = (
|
|
237
|
+
check_dict.get("fallback_reason")
|
|
238
|
+
or check_dict.get("error")
|
|
239
|
+
or "Guard database/graph unavailable"
|
|
240
|
+
)
|
|
241
|
+
if strict:
|
|
242
|
+
raise RuntimeError(f"Guard fail-open suppressed by strict mode: {reason}")
|
|
243
|
+
sys.stderr.write(
|
|
244
|
+
f"Guard warning (fail-open): {reason}. "
|
|
245
|
+
"Skipping safety check and allowing stop without running tests.\n"
|
|
246
|
+
)
|
|
247
|
+
log_event(
|
|
248
|
+
event="fail_open",
|
|
249
|
+
session_id=session_id,
|
|
250
|
+
repo_path=repo,
|
|
251
|
+
details={"op": "run", "error": reason},
|
|
252
|
+
)
|
|
253
|
+
return {
|
|
254
|
+
"version": SCHEMA_VERSION,
|
|
255
|
+
"status": "passed",
|
|
256
|
+
"policy": "fail_open",
|
|
257
|
+
"trust": 0.0,
|
|
258
|
+
"overrides_triggered": check_dict.get("overrides_triggered", ["fail_open_error"]),
|
|
259
|
+
"selected_tests": [],
|
|
260
|
+
"failures": [],
|
|
261
|
+
"reasons": check_dict.get("reasons", {}),
|
|
262
|
+
"tests_run": [],
|
|
263
|
+
"tests_run_count": 0,
|
|
264
|
+
"tests_passed": True,
|
|
265
|
+
"forced_full": False,
|
|
266
|
+
"fallback_reason": reason,
|
|
267
|
+
"duration_s": 0.0,
|
|
268
|
+
"exit_code": 0,
|
|
269
|
+
"output_summary": f"Guard bypassed fail-open: {reason}",
|
|
270
|
+
"fail_open": True,
|
|
271
|
+
}, 0
|
|
272
|
+
|
|
230
273
|
forced_full = check_dict.get("is_fallback", False) or (check_dict.get("status") == "run-all")
|
|
231
274
|
if forced_full:
|
|
232
275
|
tests_to_run = ["tests"]
|
|
@@ -171,23 +171,42 @@ def cmd_hook_stop(args) -> int:
|
|
|
171
171
|
)
|
|
172
172
|
|
|
173
173
|
|
|
174
|
+
def cmd_hook_session_start(args) -> int:
|
|
175
|
+
from guard_cli.hook import run_session_start_hook
|
|
176
|
+
return run_session_start_hook(repo_override=args.repo)
|
|
177
|
+
|
|
178
|
+
|
|
174
179
|
def cmd_hook_install(args) -> int:
|
|
175
|
-
|
|
176
|
-
|
|
180
|
+
if getattr(args, "antigravity", False):
|
|
181
|
+
from guard_cli.integrations import antigravity
|
|
182
|
+
ok, msg = antigravity.install(repo_path=args.repo)
|
|
183
|
+
else:
|
|
184
|
+
from guard_cli.integrations import claude
|
|
185
|
+
ok, msg = claude.install(repo_path=args.repo, shared=args.shared)
|
|
177
186
|
print(msg)
|
|
178
187
|
return 0 if ok else 1
|
|
179
188
|
|
|
180
189
|
|
|
181
190
|
def cmd_hook_uninstall(args) -> int:
|
|
182
|
-
|
|
183
|
-
|
|
191
|
+
if getattr(args, "antigravity", False):
|
|
192
|
+
from guard_cli.integrations import antigravity
|
|
193
|
+
ok, msg = antigravity.uninstall(repo_path=args.repo)
|
|
194
|
+
else:
|
|
195
|
+
from guard_cli.integrations import claude
|
|
196
|
+
ok, msg = claude.uninstall(repo_path=args.repo, shared=args.shared)
|
|
184
197
|
print(msg)
|
|
185
198
|
return 0 if ok else 1
|
|
186
199
|
|
|
187
200
|
|
|
188
201
|
def cmd_hook_status(args) -> int:
|
|
189
|
-
from guard_cli.integrations import claude
|
|
190
|
-
|
|
202
|
+
from guard_cli.integrations import claude, antigravity
|
|
203
|
+
if getattr(args, "antigravity", False):
|
|
204
|
+
stat = {"antigravity": antigravity.status(repo_path=args.repo)}
|
|
205
|
+
else:
|
|
206
|
+
stat = {
|
|
207
|
+
"claude": claude.status(repo_path=args.repo, shared=getattr(args, "shared", False)),
|
|
208
|
+
"antigravity": antigravity.status(repo_path=args.repo),
|
|
209
|
+
}
|
|
191
210
|
print(json.dumps(stat, indent=2))
|
|
192
211
|
return 0
|
|
193
212
|
|
|
@@ -260,20 +279,24 @@ def main(argv=None) -> int:
|
|
|
260
279
|
hk_stop.add_argument("--strict", action="store_true", help="fail closed on error")
|
|
261
280
|
hk_stop.set_defaults(fn=cmd_hook_stop)
|
|
262
281
|
|
|
263
|
-
|
|
282
|
+
hk_start = hk_sub.add_parser("session-start", help="Record baseline SHA at session start (Claude Code SessionStart / Antigravity PreInvocation)")
|
|
283
|
+
hk_start.add_argument("--repo", default=None, help="path to repo root")
|
|
284
|
+
hk_start.set_defaults(fn=cmd_hook_session_start)
|
|
285
|
+
|
|
286
|
+
hk_inst = hk_sub.add_parser("install", help="Install Stop hook into agent settings (Claude Code by default, Antigravity with --antigravity)")
|
|
264
287
|
hk_inst.add_argument("--repo", default=None, help="path to repo root")
|
|
265
288
|
hk_inst.add_argument("--shared", action="store_true", help="install to settings.json instead of settings.local.json")
|
|
266
|
-
hk_inst.
|
|
289
|
+
hk_inst.add_argument("--antigravity", action="store_true", help="install to Antigravity .agents/hooks.json instead of Claude Code settings")
|
|
267
290
|
|
|
268
|
-
hk_uninst = hk_sub.add_parser("uninstall", help="Uninstall Stop hook from Claude Code
|
|
291
|
+
hk_uninst = hk_sub.add_parser("uninstall", help="Uninstall Stop hook from agent settings (Claude Code by default, Antigravity with --antigravity)")
|
|
269
292
|
hk_uninst.add_argument("--repo", default=None, help="path to repo root")
|
|
270
293
|
hk_uninst.add_argument("--shared", action="store_true", help="uninstall from settings.json instead of settings.local.json")
|
|
271
|
-
hk_uninst.
|
|
294
|
+
hk_uninst.add_argument("--antigravity", action="store_true", help="uninstall from Antigravity .agents/hooks.json instead of Claude Code settings")
|
|
272
295
|
|
|
273
|
-
hk_stat = hk_sub.add_parser("status", help="Check Claude Code
|
|
296
|
+
hk_stat = hk_sub.add_parser("status", help="Check hook installation status (Claude Code by default, Antigravity with --antigravity)")
|
|
274
297
|
hk_stat.add_argument("--repo", default=None, help="path to repo root")
|
|
275
298
|
hk_stat.add_argument("--shared", action="store_true", help="check settings.json instead of settings.local.json")
|
|
276
|
-
hk_stat.
|
|
299
|
+
hk_stat.add_argument("--antigravity", action="store_true", help="check Antigravity .agents/hooks.json instead of Claude Code settings")
|
|
277
300
|
|
|
278
301
|
fb = sub.add_parser("feedback")
|
|
279
302
|
fb.add_argument("--function", required=True, help="Function ID where fault was missed")
|
|
@@ -316,6 +316,14 @@ def check_integrations(repo_path: Path) -> CheckResult:
|
|
|
316
316
|
agy_info = antigravity.status(repo_path)
|
|
317
317
|
if not claude_info["installed"] and not agy_info["installed"]:
|
|
318
318
|
missing.append("hook in .agents/hooks.json or .claude/settings.json")
|
|
319
|
+
elif agy_info.get("installed") and not agy_info.get("executable_exists", True):
|
|
320
|
+
exe = agy_info.get("executable_path")
|
|
321
|
+
return CheckResult(
|
|
322
|
+
name="integrations",
|
|
323
|
+
status="fail",
|
|
324
|
+
message=f"Guard executable configured in .agents/hooks.json does not exist: {exe}.",
|
|
325
|
+
fix="Run 'guard init' or 'guard hook install --antigravity' to update executable path.",
|
|
326
|
+
)
|
|
319
327
|
|
|
320
328
|
# 3. Check AGENTS.md or CLAUDE.md marked block
|
|
321
329
|
agents_doc = repo_path / "AGENTS.md"
|