methodproof 0.8.1__tar.gz → 0.8.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.
- {methodproof-0.8.1 → methodproof-0.8.2}/PKG-INFO +1 -1
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/graph.py +88 -41
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/claude_code.py +8 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/claude_code.sh +4 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/install.py +1 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/pyproject.toml +1 -1
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_graph.py +5 -4
- methodproof-0.8.1/.code-review-graph/.gitignore +0 -3
- methodproof-0.8.1/.code-review-graph/graph.db +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/.github/workflows/ci.yml +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/.gitignore +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/CHANGELOG.md +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/LICENSE +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/README.md +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/__init__.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/__main__.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/_daemon.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/agents/__init__.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/agents/base.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/agents/music.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/agents/terminal.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/agents/watcher.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/analysis.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/binding.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/bip39.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/bridge.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/cli.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/config.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/crypto.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/e2e.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hook.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/__init__.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/cline_hook.sh +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/codex_hook.sh +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/gemini_hook.sh +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/kiro_hook.sh +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/mcp_register.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/openclaw/HOOK.md +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/openclaw/handler.ts +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/openclaw_install.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/opencode_plugin.js +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/hooks/wrappers.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/integrity.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/kdf.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/keychain.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/live.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/lock.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/mcp.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/migrate_db.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/proxy.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/proxy_daemon.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/repos.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/skills/methodproof/SKILL.md +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/store.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/sync.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/tui/__init__.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/tui/consent.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/tui/init.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/tui/log.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/tui/login_success.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/tui/review.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/tui/start.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/tui/status.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/tui/theme.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/viewer.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/methodproof/wordlist.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/test_windows_compat.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/__init__.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/conftest.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_analysis.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_cli_auth.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_cli_config.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_cli_helpers.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_cli_session.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_cli_share.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_cli_start.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_cli_update.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_e2e_integration.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_hooks.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_live.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_openclaw_hooks.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_profiles.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_repos.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_security.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_store.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_sync.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_viewer.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/tests/test_wrappers.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.2}/uv.lock +0 -0
|
@@ -36,20 +36,19 @@ def build(session_id: str) -> dict[str, int]:
|
|
|
36
36
|
db.execute(
|
|
37
37
|
"DELETE FROM causal_links WHERE source_id IN "
|
|
38
38
|
"(SELECT id FROM events WHERE session_id = ?)", (session_id,))
|
|
39
|
-
stats["causal"] +=
|
|
40
|
-
|
|
41
|
-
stats["causal"] +=
|
|
42
|
-
|
|
43
|
-
stats["causal"] +=
|
|
44
|
-
|
|
45
|
-
stats["causal"] +=
|
|
46
|
-
|
|
39
|
+
stats["causal"] += _link_nearest(db, session_id, "llm_prompt",
|
|
40
|
+
"llm_completion", "RECEIVED", "model")
|
|
41
|
+
stats["causal"] += _link_until_next(db, session_id, "llm_completion",
|
|
42
|
+
"file_edit", "INFORMED")
|
|
43
|
+
stats["causal"] += _link_until_next(db, session_id, "web_search",
|
|
44
|
+
"web_visit", "LED_TO")
|
|
45
|
+
stats["causal"] += _link_until_next(db, session_id, "browser_search",
|
|
46
|
+
"browser_visit", "LED_TO")
|
|
47
47
|
stats["causal"] += _link_pasted(db, session_id)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"INFORMED", 60)
|
|
48
|
+
stats["causal"] += _link_nearest(db, session_id, "agent_prompt",
|
|
49
|
+
"agent_completion", "RECEIVED", "model")
|
|
50
|
+
stats["causal"] += _link_until_next(db, session_id, "agent_completion",
|
|
51
|
+
"file_edit", "INFORMED")
|
|
53
52
|
|
|
54
53
|
# Resources
|
|
55
54
|
for e in events:
|
|
@@ -95,39 +94,87 @@ def build(session_id: str) -> dict[str, int]:
|
|
|
95
94
|
return stats
|
|
96
95
|
|
|
97
96
|
|
|
98
|
-
def
|
|
97
|
+
def _link_nearest(
|
|
99
98
|
db: object, sid: str, src_type: str, tgt_type: str,
|
|
100
|
-
rel: str,
|
|
99
|
+
rel: str, match_field: str | None = None,
|
|
101
100
|
) -> int:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
101
|
+
"""1:1 nearest-neighbor pairing. No time window."""
|
|
102
|
+
rows = db.execute(
|
|
103
|
+
"SELECT id, type, timestamp, metadata FROM events "
|
|
104
|
+
"WHERE session_id = ? AND type IN (?, ?) ORDER BY timestamp",
|
|
105
|
+
(sid, src_type, tgt_type),
|
|
106
|
+
).fetchall()
|
|
107
|
+
claimed: set[str] = set()
|
|
108
|
+
pairs: list[tuple[str, str, str]] = []
|
|
109
|
+
for src in rows:
|
|
110
|
+
if src["type"] != src_type:
|
|
111
|
+
continue
|
|
112
|
+
src_val = _decompress_meta(src["metadata"]).get(match_field) if match_field else None
|
|
113
|
+
for tgt in rows:
|
|
114
|
+
if tgt["type"] != tgt_type or tgt["id"] in claimed or tgt["timestamp"] <= src["timestamp"]:
|
|
115
|
+
continue
|
|
116
|
+
if match_field and _decompress_meta(tgt["metadata"]).get(match_field) != src_val:
|
|
117
|
+
continue
|
|
118
|
+
pairs.append((src["id"], tgt["id"], rel))
|
|
119
|
+
claimed.add(tgt["id"])
|
|
120
|
+
break
|
|
121
|
+
if pairs:
|
|
122
|
+
db.executemany(
|
|
123
|
+
"INSERT OR IGNORE INTO causal_links (source_id, target_id, type) "
|
|
124
|
+
"VALUES (?, ?, ?)", pairs)
|
|
125
|
+
return len(pairs)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _link_until_next(
|
|
129
|
+
db: object, sid: str, src_type: str, tgt_type: str, rel: str,
|
|
130
|
+
) -> int:
|
|
131
|
+
"""Link source to all targets until the next source event."""
|
|
132
|
+
rows = db.execute(
|
|
133
|
+
"SELECT id, type FROM events "
|
|
134
|
+
"WHERE session_id = ? AND type IN (?, ?) ORDER BY timestamp",
|
|
135
|
+
(sid, src_type, tgt_type),
|
|
136
|
+
).fetchall()
|
|
137
|
+
pairs: list[tuple[str, str, str]] = []
|
|
138
|
+
current_src: str | None = None
|
|
139
|
+
for ev in rows:
|
|
140
|
+
if ev["type"] == src_type:
|
|
141
|
+
current_src = ev["id"]
|
|
142
|
+
elif current_src:
|
|
143
|
+
pairs.append((current_src, ev["id"], rel))
|
|
144
|
+
if pairs:
|
|
145
|
+
db.executemany(
|
|
146
|
+
"INSERT OR IGNORE INTO causal_links (source_id, target_id, type) "
|
|
147
|
+
"VALUES (?, ?, ?)", pairs)
|
|
148
|
+
return len(pairs)
|
|
116
149
|
|
|
117
150
|
|
|
118
151
|
def _link_pasted(db: object, sid: str) -> int:
|
|
119
|
-
"""browser_copy → file_edit
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
152
|
+
"""browser_copy → nearest file_edit with content length within 20%."""
|
|
153
|
+
rows = db.execute(
|
|
154
|
+
"SELECT id, type, timestamp, metadata FROM events "
|
|
155
|
+
"WHERE session_id = ? AND type IN ('browser_copy', 'file_edit') "
|
|
156
|
+
"ORDER BY timestamp", (sid,),
|
|
157
|
+
).fetchall()
|
|
158
|
+
claimed: set[str] = set()
|
|
159
|
+
pairs: list[tuple[str, str]] = []
|
|
160
|
+
for src in rows:
|
|
161
|
+
if src["type"] != "browser_copy":
|
|
162
|
+
continue
|
|
163
|
+
src_len = _decompress_meta(src["metadata"]).get("text_length", 0)
|
|
164
|
+
if not src_len:
|
|
165
|
+
continue
|
|
166
|
+
for tgt in rows:
|
|
167
|
+
if tgt["type"] != "file_edit" or tgt["id"] in claimed or tgt["timestamp"] <= src["timestamp"]:
|
|
168
|
+
continue
|
|
169
|
+
if abs(_decompress_meta(tgt["metadata"]).get("lines_added", 0) * 40 - src_len) < src_len * 0.2:
|
|
170
|
+
pairs.append((src["id"], tgt["id"]))
|
|
171
|
+
claimed.add(tgt["id"])
|
|
172
|
+
break
|
|
173
|
+
if pairs:
|
|
174
|
+
db.executemany(
|
|
175
|
+
"INSERT OR IGNORE INTO causal_links (source_id, target_id, type, confidence) "
|
|
176
|
+
"VALUES (?, ?, 'PASTED_FROM', 0.7)", pairs)
|
|
177
|
+
return len(pairs)
|
|
131
178
|
|
|
132
179
|
|
|
133
180
|
def _link_action_resources(db: object, sid: str) -> None:
|
|
@@ -76,6 +76,8 @@ _TYPE_MAP = {
|
|
|
76
76
|
# Worktree
|
|
77
77
|
"WorktreeCreate": "worktree_create",
|
|
78
78
|
"WorktreeRemove": "worktree_remove",
|
|
79
|
+
# Notifications
|
|
80
|
+
"Notification": "notification",
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
_TOOL = "claude_code"
|
|
@@ -139,6 +141,12 @@ _META_EXTRACTORS = {
|
|
|
139
141
|
"ElicitationResult": lambda d: {"tool": _TOOL},
|
|
140
142
|
"WorktreeCreate": lambda d: {"tool": _TOOL, "worktree_path": d.get("worktree_path", "")},
|
|
141
143
|
"WorktreeRemove": lambda d: {"tool": _TOOL, "worktree_path": d.get("worktree_path", "")},
|
|
144
|
+
"Notification": lambda d: {
|
|
145
|
+
"tool": _TOOL,
|
|
146
|
+
"title": d.get("title", ""),
|
|
147
|
+
"message": d.get("message", d.get("text", ""))[:1000],
|
|
148
|
+
"notification_type": d.get("type", d.get("notification_type", "")),
|
|
149
|
+
},
|
|
142
150
|
}
|
|
143
151
|
|
|
144
152
|
|
|
@@ -162,6 +162,10 @@ if command -v jq >/dev/null 2>&1; then
|
|
|
162
162
|
TYPE=$(echo "$EVENT" | sed 's/Elicitation$/mcp_elicitation/;s/ElicitationResult/mcp_elicitation_result/')
|
|
163
163
|
META='{"tool":"claude_code"}'
|
|
164
164
|
;;
|
|
165
|
+
Notification)
|
|
166
|
+
TYPE="notification"
|
|
167
|
+
META=$(echo "$INPUT" | jq -c '{tool: "claude_code", title: (.title // ""), message: ((.message // .text // "")[:1000]), notification_type: (.type // .notification_type // "")}' 2>/dev/null || echo '{"tool":"claude_code"}')
|
|
168
|
+
;;
|
|
165
169
|
*)
|
|
166
170
|
TYPE="claude_code_event"
|
|
167
171
|
META="{\"event\":\"$EVENT\"}"
|
|
@@ -38,6 +38,7 @@ HOOK_EVENTS = [
|
|
|
38
38
|
"ElicitationResult", # user responded to MCP
|
|
39
39
|
"WorktreeCreate", # git worktree created (parallel agent)
|
|
40
40
|
"WorktreeRemove", # git worktree removed
|
|
41
|
+
"Notification", # system notifications (recap, compaction summary, etc.)
|
|
41
42
|
]
|
|
42
43
|
|
|
43
44
|
# --- Codex CLI ---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "methodproof"
|
|
3
|
-
version = "0.8.
|
|
3
|
+
version = "0.8.2"
|
|
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", "sqlcipher3>=0.6"]
|
|
@@ -93,15 +93,16 @@ def test_led_to_link() -> None:
|
|
|
93
93
|
assert db.execute("SELECT count(*) FROM causal_links WHERE type='LED_TO'").fetchone()[0] == 1
|
|
94
94
|
|
|
95
95
|
|
|
96
|
-
def
|
|
97
|
-
"""
|
|
96
|
+
def test_informed_bounded_by_next_completion() -> None:
|
|
97
|
+
"""Edit after a second completion links only to the second."""
|
|
98
98
|
sid = _session([
|
|
99
99
|
_event("llm_completion", 100, {"model": "claude"}),
|
|
100
|
-
_event("
|
|
100
|
+
_event("llm_completion", 200, {"model": "claude"}),
|
|
101
|
+
_event("file_edit", 250, {"path": "a.py"}),
|
|
101
102
|
])
|
|
102
103
|
graph.build(sid)
|
|
103
104
|
db = store._db()
|
|
104
|
-
assert db.execute("SELECT count(*) FROM causal_links WHERE type='INFORMED'").fetchone()[0] ==
|
|
105
|
+
assert db.execute("SELECT count(*) FROM causal_links WHERE type='INFORMED'").fetchone()[0] == 1
|
|
105
106
|
|
|
106
107
|
|
|
107
108
|
def test_resource_creation() -> None:
|
|
Binary file
|
|
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
|
|
File without changes
|