methodproof 0.7.37__tar.gz → 0.7.38__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.
- {methodproof-0.7.37 → methodproof-0.7.38}/CHANGELOG.md +10 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/PKG-INFO +1 -1
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/agents/base.py +7 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/agents/watcher.py +101 -0
- methodproof-0.7.38/methodproof/hooks/gemini_hook.sh +123 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/install.py +5 -1
- {methodproof-0.7.37 → methodproof-0.7.38}/pyproject.toml +1 -1
- methodproof-0.7.37/methodproof/hooks/gemini_hook.sh +0 -58
- {methodproof-0.7.37 → methodproof-0.7.38}/.github/workflows/ci.yml +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/.gitignore +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/LICENSE +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/README.md +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/__init__.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/__main__.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/_daemon.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/agents/__init__.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/agents/music.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/agents/terminal.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/analysis.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/binding.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/bip39.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/bridge.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/cli.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/config.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/crypto.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/e2e.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/graph.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hook.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/__init__.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/claude_code.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/claude_code.sh +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/cline_hook.sh +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/codex_hook.sh +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/kiro_hook.sh +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/mcp_register.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/openclaw/HOOK.md +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/openclaw/handler.ts +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/openclaw_install.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/opencode_plugin.js +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/hooks/wrappers.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/integrity.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/kdf.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/keychain.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/live.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/lock.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/mcp.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/migrate_db.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/proxy.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/proxy_daemon.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/repos.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/skills/methodproof/SKILL.md +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/store.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/sync.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/tui/__init__.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/tui/consent.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/tui/init.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/tui/log.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/tui/login_success.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/tui/review.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/tui/start.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/tui/status.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/tui/theme.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/viewer.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/methodproof/wordlist.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/test_windows_compat.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/__init__.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/conftest.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_analysis.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_cli_auth.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_cli_config.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_cli_helpers.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_cli_session.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_cli_share.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_cli_start.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_cli_update.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_e2e_integration.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_graph.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_hooks.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_live.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_openclaw_hooks.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_profiles.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_security.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_store.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_sync.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_viewer.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/tests/test_wrappers.py +0 -0
- {methodproof-0.7.37 → methodproof-0.7.38}/uv.lock +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.38] — 2026-04-12
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Structural hunk capture on `file_edit` and `git_commit`** — watcher parses `@@ -old,count +new,count @@` headers from `git diff --unified=0` and `git show --format= --unified=0 <sha>` and emits them as `hunks: [{old_start, old_count, new_start, new_count}, ...]` on `file_edit` metadata and `file_hunks: {path: [hunks, ...]}` on `git_commit` metadata. Pure range data — no code content, no identifiers. Stays inside the structural-capture boundary alongside `lines_added` / `lines_removed`.
|
|
7
|
+
- **Journal-mode hunk content** — when `code_capture` consent is on (Pro tier, journal mode), each hunk additionally carries a `lines: ["+foo", "-bar", ...]` field with the actual diff lines for that hunk, capped at 2000 lines per event. Gated by the same consent flag that gates the raw `diff` field.
|
|
8
|
+
- **`base.is_content_captured()`** — public helper in `methodproof/agents/base.py` exposing the `code_capture` capture-flag check so agents can decide what content to include without reaching into private module state.
|
|
9
|
+
|
|
10
|
+
### Why
|
|
11
|
+
Enables the platform's post-processing pipeline to distinguish `SUPERSEDES` (later commit replaces 100% of an earlier commit's added lines on each shared path) from `EXTENSION_TO` (anything less — the default) without ever needing the raw diff content. See `methodproof` changelog v0.6.30.
|
|
12
|
+
|
|
3
13
|
## [0.7.37] — 2026-04-12
|
|
4
14
|
|
|
5
15
|
### Changed
|
|
@@ -125,6 +125,13 @@ def log(level: str, event: str, **kw: object) -> None:
|
|
|
125
125
|
sys.stderr.write(json.dumps(entry, default=str) + "\n")
|
|
126
126
|
|
|
127
127
|
|
|
128
|
+
def is_content_captured() -> bool:
|
|
129
|
+
"""True when code_capture consent is on (journal/Pro mode). Agents use this
|
|
130
|
+
to decide whether to include line-level diff content alongside structural
|
|
131
|
+
metadata."""
|
|
132
|
+
return bool(_capture.get("code_capture", False))
|
|
133
|
+
|
|
134
|
+
|
|
128
135
|
def emit(event_type: str, metadata: dict[str, Any]) -> None:
|
|
129
136
|
if not _initialized:
|
|
130
137
|
log("warning", "emit.before_init", type=event_type)
|
|
@@ -46,6 +46,99 @@ IGNORE_PATTERNS = re.compile(
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
_MAX_DIFF_BYTES = 50_000
|
|
49
|
+
_MAX_HUNK_LINES = 2_000
|
|
50
|
+
|
|
51
|
+
_HUNK_HEADER_RE = re.compile(r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@")
|
|
52
|
+
_DIFF_FILE_RE = re.compile(r"^diff --git a/(.+?) b/(.+?)$")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _parse_hunks(diff_text: str, include_lines: bool) -> list[dict[str, object]]:
|
|
56
|
+
"""Parse unified diff text into structured hunks.
|
|
57
|
+
|
|
58
|
+
Each hunk: {old_start, old_count, new_start, new_count, [lines]}.
|
|
59
|
+
Lines are only included when `include_lines` is True (journal / code_capture).
|
|
60
|
+
"""
|
|
61
|
+
hunks: list[dict[str, object]] = []
|
|
62
|
+
current: dict[str, object] | None = None
|
|
63
|
+
total_lines = 0
|
|
64
|
+
for line in diff_text.splitlines():
|
|
65
|
+
header = _HUNK_HEADER_RE.match(line)
|
|
66
|
+
if header:
|
|
67
|
+
if current is not None:
|
|
68
|
+
hunks.append(current)
|
|
69
|
+
current = {
|
|
70
|
+
"old_start": int(header.group(1)),
|
|
71
|
+
"old_count": int(header.group(2) or 1),
|
|
72
|
+
"new_start": int(header.group(3)),
|
|
73
|
+
"new_count": int(header.group(4) or 1),
|
|
74
|
+
}
|
|
75
|
+
if include_lines:
|
|
76
|
+
current["lines"] = []
|
|
77
|
+
continue
|
|
78
|
+
if current is None or not include_lines:
|
|
79
|
+
continue
|
|
80
|
+
if not (line.startswith("+") or line.startswith("-")):
|
|
81
|
+
continue
|
|
82
|
+
if line.startswith("+++") or line.startswith("---"):
|
|
83
|
+
continue
|
|
84
|
+
if total_lines >= _MAX_HUNK_LINES:
|
|
85
|
+
continue
|
|
86
|
+
current["lines"].append(line) # type: ignore[union-attr]
|
|
87
|
+
total_lines += 1
|
|
88
|
+
if current is not None:
|
|
89
|
+
hunks.append(current)
|
|
90
|
+
return hunks
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _parse_show_hunks(
|
|
94
|
+
show_text: str, include_lines: bool,
|
|
95
|
+
) -> dict[str, list[dict[str, object]]]:
|
|
96
|
+
"""Split `git show --unified=0` output by file and parse hunks per file."""
|
|
97
|
+
file_hunks: dict[str, list[dict[str, object]]] = {}
|
|
98
|
+
current_path: str | None = None
|
|
99
|
+
buffer: list[str] = []
|
|
100
|
+
|
|
101
|
+
def _flush() -> None:
|
|
102
|
+
if current_path and buffer:
|
|
103
|
+
file_hunks[current_path] = _parse_hunks("\n".join(buffer), include_lines)
|
|
104
|
+
|
|
105
|
+
for line in show_text.splitlines():
|
|
106
|
+
m = _DIFF_FILE_RE.match(line)
|
|
107
|
+
if m:
|
|
108
|
+
_flush()
|
|
109
|
+
current_path = m.group(2)
|
|
110
|
+
buffer = []
|
|
111
|
+
continue
|
|
112
|
+
if current_path is not None:
|
|
113
|
+
buffer.append(line)
|
|
114
|
+
_flush()
|
|
115
|
+
return file_hunks
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _git_diff_hunks(repo: str, path: str, include_lines: bool) -> list[dict[str, object]]:
|
|
119
|
+
"""Structured hunks for a pending file_edit. Ranges always; line content when allowed."""
|
|
120
|
+
try:
|
|
121
|
+
result = subprocess.run(
|
|
122
|
+
["git", "-C", repo, "diff", "--unified=0", "--", path],
|
|
123
|
+
capture_output=True, text=True, timeout=5,
|
|
124
|
+
)
|
|
125
|
+
return _parse_hunks(result.stdout[:_MAX_DIFF_BYTES], include_lines)
|
|
126
|
+
except Exception:
|
|
127
|
+
return []
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _git_show_file_hunks(
|
|
131
|
+
repo: str, sha: str, include_lines: bool,
|
|
132
|
+
) -> dict[str, list[dict[str, object]]]:
|
|
133
|
+
"""Per-file structured hunks for a commit."""
|
|
134
|
+
try:
|
|
135
|
+
result = subprocess.run(
|
|
136
|
+
["git", "-C", repo, "show", "--format=", "--unified=0", sha],
|
|
137
|
+
capture_output=True, text=True, timeout=10,
|
|
138
|
+
)
|
|
139
|
+
return _parse_show_hunks(result.stdout[:_MAX_DIFF_BYTES * 4], include_lines)
|
|
140
|
+
except Exception:
|
|
141
|
+
return {}
|
|
49
142
|
|
|
50
143
|
|
|
51
144
|
def _git_diff_stats(repo: str, path: str) -> tuple[int, int]:
|
|
@@ -125,10 +218,14 @@ class _Handler(FileSystemEventHandler):
|
|
|
125
218
|
|
|
126
219
|
added, removed = _git_diff_stats(self._root, path)
|
|
127
220
|
lang = Path(event.src_path).suffix.lstrip(".")
|
|
221
|
+
include_lines = base.is_content_captured()
|
|
222
|
+
hunks = _git_diff_hunks(self._root, path, include_lines)
|
|
128
223
|
meta: dict[str, object] = {
|
|
129
224
|
"path": path, "language": lang,
|
|
130
225
|
"lines_added": added, "lines_removed": removed,
|
|
131
226
|
}
|
|
227
|
+
if hunks:
|
|
228
|
+
meta["hunks"] = hunks
|
|
132
229
|
diff = _git_diff_content(self._root, path)
|
|
133
230
|
if diff:
|
|
134
231
|
meta["diff"] = diff
|
|
@@ -192,6 +289,10 @@ def _log_commit(watch_dir: str, sha: str) -> None:
|
|
|
192
289
|
meta["parent_hash"] = parent_hash
|
|
193
290
|
if body and body != subject:
|
|
194
291
|
meta["body"] = body[:2000]
|
|
292
|
+
include_lines = base.is_content_captured()
|
|
293
|
+
file_hunks = _git_show_file_hunks(watch_dir, sha, include_lines)
|
|
294
|
+
if file_hunks:
|
|
295
|
+
meta["file_hunks"] = file_hunks
|
|
195
296
|
diff = _git_show_diff(watch_dir, sha)
|
|
196
297
|
if diff:
|
|
197
298
|
meta["diff"] = diff
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# MethodProof hook for Gemini CLI — captures prompts, tools, model calls, sessions.
|
|
3
|
+
# Receives JSON on stdin. Posts to local bridge. Fails silently.
|
|
4
|
+
# Must complete in <1s to avoid blocking Gemini.
|
|
5
|
+
|
|
6
|
+
# Skip if no session is running (no pidfile = no daemon = no bridge)
|
|
7
|
+
[ -f "${HOME}/.methodproof/methodproof.pid" ] || exit 0
|
|
8
|
+
|
|
9
|
+
INPUT=$(cat)
|
|
10
|
+
|
|
11
|
+
if command -v jq >/dev/null 2>&1; then
|
|
12
|
+
EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // "unknown"' 2>/dev/null || echo "unknown")
|
|
13
|
+
else
|
|
14
|
+
EVENT=$(echo "$INPUT" | grep -o '"hook_event_name":"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
15
|
+
EVENT=${EVENT:-unknown}
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
if date +%s.%N >/dev/null 2>&1 && [ "$(date +%N)" != "%N" ]; then
|
|
19
|
+
TS=$(date +%s.%N)
|
|
20
|
+
else
|
|
21
|
+
TS=$(date +%s).000
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
if command -v jq >/dev/null 2>&1; then
|
|
25
|
+
case "$EVENT" in
|
|
26
|
+
BeforeAgent)
|
|
27
|
+
TYPE="user_prompt"
|
|
28
|
+
META=$(echo "$INPUT" | jq -c '{
|
|
29
|
+
tool: "gemini",
|
|
30
|
+
prompt_preview: (.prompt // "" | .[0:200]),
|
|
31
|
+
prompt_length: (.prompt // "" | length)
|
|
32
|
+
}' 2>/dev/null || echo '{"tool":"gemini"}')
|
|
33
|
+
;;
|
|
34
|
+
AfterAgent)
|
|
35
|
+
TYPE="agent_completion"
|
|
36
|
+
META=$(echo "$INPUT" | jq -c '{
|
|
37
|
+
tool: "gemini",
|
|
38
|
+
prompt_preview: (.prompt // "" | .[0:200]),
|
|
39
|
+
response_preview: (.prompt_response // "" | .[0:200]),
|
|
40
|
+
response_length: (.prompt_response // "" | length)
|
|
41
|
+
}' 2>/dev/null || echo '{"tool":"gemini"}')
|
|
42
|
+
;;
|
|
43
|
+
BeforeTool)
|
|
44
|
+
TYPE="tool_call"
|
|
45
|
+
META=$(echo "$INPUT" | jq -c '{
|
|
46
|
+
tool: "gemini",
|
|
47
|
+
tool_name: (.tool_name // "unknown"),
|
|
48
|
+
tool_input_preview: (.tool_input // {} | tostring | .[0:200])
|
|
49
|
+
}' 2>/dev/null || echo '{"tool":"gemini"}')
|
|
50
|
+
;;
|
|
51
|
+
AfterTool)
|
|
52
|
+
TYPE="tool_result"
|
|
53
|
+
META=$(echo "$INPUT" | jq -c '{
|
|
54
|
+
tool: "gemini",
|
|
55
|
+
tool_name: (.tool_name // "unknown"),
|
|
56
|
+
tool_input_preview: (.tool_input // {} | tostring | .[0:200]),
|
|
57
|
+
result_preview: (.tool_response // {} | tostring | .[0:200])
|
|
58
|
+
}' 2>/dev/null || echo '{"tool":"gemini"}')
|
|
59
|
+
;;
|
|
60
|
+
BeforeModel)
|
|
61
|
+
TYPE="llm_prompt"
|
|
62
|
+
META=$(echo "$INPUT" | jq -c '{
|
|
63
|
+
tool: "gemini",
|
|
64
|
+
model: "gemini"
|
|
65
|
+
}' 2>/dev/null || echo '{"tool":"gemini","model":"gemini"}')
|
|
66
|
+
;;
|
|
67
|
+
AfterModel)
|
|
68
|
+
TYPE="llm_completion"
|
|
69
|
+
META=$(echo "$INPUT" | jq -c '{
|
|
70
|
+
tool: "gemini",
|
|
71
|
+
model: "gemini"
|
|
72
|
+
}' 2>/dev/null || echo '{"tool":"gemini","model":"gemini"}')
|
|
73
|
+
;;
|
|
74
|
+
SessionStart)
|
|
75
|
+
TYPE="gemini_session_start"
|
|
76
|
+
META=$(echo "$INPUT" | jq -c '{
|
|
77
|
+
tool: "gemini",
|
|
78
|
+
gemini_session_id: (.session_id // ""),
|
|
79
|
+
cwd: (.cwd // ""),
|
|
80
|
+
source: (.source // "")
|
|
81
|
+
}' 2>/dev/null || echo '{"tool":"gemini"}')
|
|
82
|
+
;;
|
|
83
|
+
SessionEnd)
|
|
84
|
+
TYPE="gemini_session_end"
|
|
85
|
+
META=$(echo "$INPUT" | jq -c '{
|
|
86
|
+
tool: "gemini",
|
|
87
|
+
gemini_session_id: (.session_id // ""),
|
|
88
|
+
reason: (.reason // "")
|
|
89
|
+
}' 2>/dev/null || echo '{"tool":"gemini"}')
|
|
90
|
+
;;
|
|
91
|
+
PreCompress)
|
|
92
|
+
TYPE="context_compact_start"
|
|
93
|
+
META='{"tool":"gemini"}'
|
|
94
|
+
;;
|
|
95
|
+
Notification)
|
|
96
|
+
TYPE="gemini_event"
|
|
97
|
+
META=$(echo "$INPUT" | jq -c '{
|
|
98
|
+
tool: "gemini",
|
|
99
|
+
event: "notification",
|
|
100
|
+
notification_type: (.notification_type // ""),
|
|
101
|
+
message: (.message // "" | .[0:200])
|
|
102
|
+
}' 2>/dev/null || echo '{"tool":"gemini","event":"notification"}')
|
|
103
|
+
;;
|
|
104
|
+
*)
|
|
105
|
+
TYPE="gemini_event"
|
|
106
|
+
META="{\"tool\":\"gemini\",\"event\":\"$EVENT\"}"
|
|
107
|
+
;;
|
|
108
|
+
esac
|
|
109
|
+
else
|
|
110
|
+
TYPE="gemini_event"
|
|
111
|
+
META="{\"tool\":\"gemini\",\"event\":\"$EVENT\"}"
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
PAYLOAD="{\"events\":[{\"type\":\"$TYPE\",\"timestamp\":$TS,\"metadata\":$META}]}"
|
|
115
|
+
RESPONSE=$(curl -s -w "\n%{http_code}" --max-time 1 --connect-timeout 0.5 \
|
|
116
|
+
-X POST http://localhost:9877/events \
|
|
117
|
+
-H "Content-Type: application/json" \
|
|
118
|
+
-d "$PAYLOAD" 2>/dev/null) || true
|
|
119
|
+
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
|
|
120
|
+
if [ -n "$HTTP_CODE" ] && [ "$HTTP_CODE" != "200" ]; then
|
|
121
|
+
echo "{\"ts\":$TS,\"level\":\"warning\",\"event\":\"hook.post_failed\",\"http_code\":\"$HTTP_CODE\",\"type\":\"$TYPE\",\"payload\":$PAYLOAD}" \
|
|
122
|
+
>> "${HOME}/.methodproof/hook_errors.log" 2>/dev/null || true
|
|
123
|
+
fi
|
|
@@ -48,7 +48,11 @@ _CODEX_EVENTS = ["PreToolUse", "PostToolUse", "UserPromptSubmit", "SessionStart"
|
|
|
48
48
|
# --- Gemini CLI ---
|
|
49
49
|
_GEMINI_DIR = Path.home() / ".gemini"
|
|
50
50
|
_GEMINI_HOOK = _HOOKS_DIR / "gemini_hook.sh"
|
|
51
|
-
_GEMINI_EVENTS = [
|
|
51
|
+
_GEMINI_EVENTS = [
|
|
52
|
+
"BeforeTool", "AfterTool", "BeforeAgent", "AfterAgent",
|
|
53
|
+
"BeforeModel", "AfterModel", "SessionStart", "SessionEnd",
|
|
54
|
+
"PreCompress", "Notification",
|
|
55
|
+
]
|
|
52
56
|
|
|
53
57
|
# --- Kiro ---
|
|
54
58
|
_KIRO_DIR = Path.home() / ".kiro"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "methodproof"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.38"
|
|
4
4
|
description = "See how you code. Capture and visualize your engineering process."
|
|
5
5
|
requires-python = ">=3.11"
|
|
6
6
|
dependencies = ["watchdog>=4.0", "websocket-client>=1.7", "cryptography>=43.0", "keyring>=25.0", "textual>=0.59", "rich>=13.7"]
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# MethodProof hook for Gemini CLI — captures tool calls and sessions.
|
|
3
|
-
# Receives JSON on stdin. Posts to local bridge. Fails silently.
|
|
4
|
-
# Must complete in <1s to avoid blocking Gemini.
|
|
5
|
-
|
|
6
|
-
INPUT=$(cat)
|
|
7
|
-
|
|
8
|
-
if command -v jq >/dev/null 2>&1; then
|
|
9
|
-
EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // "unknown"' 2>/dev/null || echo "unknown")
|
|
10
|
-
else
|
|
11
|
-
EVENT=$(echo "$INPUT" | grep -o '"hook_event_name":"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
12
|
-
EVENT=${EVENT:-unknown}
|
|
13
|
-
fi
|
|
14
|
-
|
|
15
|
-
if date +%s.%N >/dev/null 2>&1 && [ "$(date +%N)" != "%N" ]; then
|
|
16
|
-
TS=$(date +%s.%N)
|
|
17
|
-
else
|
|
18
|
-
TS=$(date +%s).000
|
|
19
|
-
fi
|
|
20
|
-
|
|
21
|
-
if command -v jq >/dev/null 2>&1; then
|
|
22
|
-
case "$EVENT" in
|
|
23
|
-
BeforeTool)
|
|
24
|
-
TYPE="tool_call"
|
|
25
|
-
META=$(echo "$INPUT" | jq -c '{tool: "gemini", tool_name: (.tool_name // "unknown")}' 2>/dev/null || echo '{"tool":"gemini"}')
|
|
26
|
-
;;
|
|
27
|
-
AfterTool)
|
|
28
|
-
TYPE="tool_result"
|
|
29
|
-
META=$(echo "$INPUT" | jq -c '{tool: "gemini", tool_name: (.tool_name // "unknown")}' 2>/dev/null || echo '{"tool":"gemini"}')
|
|
30
|
-
;;
|
|
31
|
-
SessionStart)
|
|
32
|
-
TYPE="gemini_session_start"
|
|
33
|
-
META=$(echo "$INPUT" | jq -c '{tool: "gemini", cwd: (.cwd // "")}' 2>/dev/null || echo '{"tool":"gemini"}')
|
|
34
|
-
;;
|
|
35
|
-
SessionEnd)
|
|
36
|
-
TYPE="gemini_session_end"
|
|
37
|
-
META=$(echo "$INPUT" | jq -c '{tool: "gemini", reason: (.reason // "")}' 2>/dev/null || echo '{"tool":"gemini"}')
|
|
38
|
-
;;
|
|
39
|
-
*)
|
|
40
|
-
TYPE="gemini_event"
|
|
41
|
-
META="{\"tool\":\"gemini\",\"event\":\"$EVENT\"}"
|
|
42
|
-
;;
|
|
43
|
-
esac
|
|
44
|
-
else
|
|
45
|
-
TYPE="gemini_event"
|
|
46
|
-
META="{\"tool\":\"gemini\",\"event\":\"$EVENT\"}"
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
PAYLOAD="{\"events\":[{\"type\":\"$TYPE\",\"timestamp\":$TS,\"metadata\":$META}]}"
|
|
50
|
-
RESPONSE=$(curl -s -w "\n%{http_code}" --max-time 1 --connect-timeout 0.5 \
|
|
51
|
-
-X POST http://localhost:9877/events \
|
|
52
|
-
-H "Content-Type: application/json" \
|
|
53
|
-
-d "$PAYLOAD" 2>/dev/null) || true
|
|
54
|
-
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
|
|
55
|
-
if [ -n "$HTTP_CODE" ] && [ "$HTTP_CODE" != "200" ]; then
|
|
56
|
-
echo "{\"ts\":$TS,\"level\":\"warning\",\"event\":\"hook.post_failed\",\"http_code\":\"$HTTP_CODE\",\"type\":\"$TYPE\",\"payload\":$PAYLOAD}" \
|
|
57
|
-
>> "${HOME}/.methodproof/hook_errors.log" 2>/dev/null || true
|
|
58
|
-
fi
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|