methodproof 0.8.1__tar.gz → 0.8.3__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.3}/PKG-INFO +1 -1
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/graph.py +88 -41
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/claude_code.py +40 -1
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/claude_code.sh +14 -1
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/install.py +1 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/pyproject.toml +1 -1
- {methodproof-0.8.1 → methodproof-0.8.3}/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.3}/.github/workflows/ci.yml +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/.gitignore +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/CHANGELOG.md +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/LICENSE +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/README.md +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/__init__.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/__main__.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/_daemon.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/agents/__init__.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/agents/base.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/agents/music.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/agents/terminal.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/agents/watcher.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/analysis.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/binding.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/bip39.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/bridge.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/cli.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/config.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/crypto.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/e2e.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hook.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/__init__.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/cline_hook.sh +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/codex_hook.sh +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/gemini_hook.sh +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/kiro_hook.sh +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/mcp_register.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/openclaw/HOOK.md +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/openclaw/handler.ts +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/openclaw_install.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/opencode_plugin.js +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/hooks/wrappers.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/integrity.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/kdf.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/keychain.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/live.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/lock.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/mcp.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/migrate_db.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/proxy.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/proxy_daemon.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/repos.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/skills/methodproof/SKILL.md +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/store.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/sync.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/tui/__init__.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/tui/consent.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/tui/init.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/tui/log.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/tui/login_success.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/tui/review.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/tui/start.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/tui/status.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/tui/theme.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/viewer.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/methodproof/wordlist.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/test_windows_compat.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/__init__.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/conftest.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_analysis.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_cli_auth.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_cli_config.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_cli_helpers.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_cli_session.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_cli_share.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_cli_start.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_cli_update.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_e2e_integration.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_hooks.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_live.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_openclaw_hooks.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_profiles.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_repos.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_security.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_store.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_sync.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_viewer.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/tests/test_wrappers.py +0 -0
- {methodproof-0.8.1 → methodproof-0.8.3}/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,9 +141,33 @@ _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
|
|
|
153
|
+
def _extract_recap(transcript_path: str) -> str | None:
|
|
154
|
+
"""Grep transcript for the last ※ recap: line. Journal mode only."""
|
|
155
|
+
import pathlib
|
|
156
|
+
tp = pathlib.Path(transcript_path)
|
|
157
|
+
if not tp.exists():
|
|
158
|
+
return None
|
|
159
|
+
try:
|
|
160
|
+
text = tp.read_text(errors="replace")
|
|
161
|
+
except OSError:
|
|
162
|
+
return None
|
|
163
|
+
last_recap = None
|
|
164
|
+
for line in text.splitlines():
|
|
165
|
+
stripped = line.strip()
|
|
166
|
+
if stripped.startswith("※ recap:") or stripped.startswith("recap:"):
|
|
167
|
+
last_recap = stripped.removeprefix("※ ").removeprefix("recap:").strip()
|
|
168
|
+
return last_recap
|
|
169
|
+
|
|
170
|
+
|
|
145
171
|
def main() -> None:
|
|
146
172
|
try:
|
|
147
173
|
data = json.load(sys.stdin)
|
|
@@ -154,7 +180,20 @@ def main() -> None:
|
|
|
154
180
|
meta = extractor(data) if extractor else {"tool": _TOOL, "event": event}
|
|
155
181
|
ts = time.time()
|
|
156
182
|
|
|
157
|
-
|
|
183
|
+
events_out = [{"type": etype, "timestamp": ts, "metadata": meta}]
|
|
184
|
+
|
|
185
|
+
# On Stop, grep transcript for recap (journal mode only)
|
|
186
|
+
transcript_path = data.get("transcript_path", "")
|
|
187
|
+
if event == "Stop" and transcript_path:
|
|
188
|
+
recap = _extract_recap(transcript_path)
|
|
189
|
+
if recap:
|
|
190
|
+
events_out.append({
|
|
191
|
+
"type": "context_recap",
|
|
192
|
+
"timestamp": ts,
|
|
193
|
+
"metadata": {"tool": _TOOL, "recap": recap[:2000]},
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
payload = json.dumps({"events": events_out}).encode()
|
|
158
197
|
req = urllib.request.Request(
|
|
159
198
|
"http://localhost:9877/events", data=payload,
|
|
160
199
|
headers={"Content-Type": "application/json"}, method="POST",
|
|
@@ -125,6 +125,15 @@ if command -v jq >/dev/null 2>&1; then
|
|
|
125
125
|
Stop)
|
|
126
126
|
TYPE="agent_turn_end"
|
|
127
127
|
META='{"tool":"claude_code"}'
|
|
128
|
+
# Extract recap from transcript if available (journal mode)
|
|
129
|
+
TRANSCRIPT=$(echo "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
130
|
+
if [ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ]; then
|
|
131
|
+
RECAP=$(grep -E '※ recap:|^recap:' "$TRANSCRIPT" | tail -1 | sed 's/^.*※ recap: *//;s/^recap: *//' | head -c 2000)
|
|
132
|
+
if [ -n "$RECAP" ]; then
|
|
133
|
+
RECAP_ESCAPED=$(echo "$RECAP" | jq -Rs '.' 2>/dev/null || echo '""')
|
|
134
|
+
RECAP_EVENT=",{\"type\":\"context_recap\",\"timestamp\":$TS,\"metadata\":{\"tool\":\"claude_code\",\"recap\":$RECAP_ESCAPED}}"
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
128
137
|
;;
|
|
129
138
|
StopFailure)
|
|
130
139
|
TYPE="agent_turn_error"
|
|
@@ -162,6 +171,10 @@ if command -v jq >/dev/null 2>&1; then
|
|
|
162
171
|
TYPE=$(echo "$EVENT" | sed 's/Elicitation$/mcp_elicitation/;s/ElicitationResult/mcp_elicitation_result/')
|
|
163
172
|
META='{"tool":"claude_code"}'
|
|
164
173
|
;;
|
|
174
|
+
Notification)
|
|
175
|
+
TYPE="notification"
|
|
176
|
+
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"}')
|
|
177
|
+
;;
|
|
165
178
|
*)
|
|
166
179
|
TYPE="claude_code_event"
|
|
167
180
|
META="{\"event\":\"$EVENT\"}"
|
|
@@ -174,7 +187,7 @@ else
|
|
|
174
187
|
fi
|
|
175
188
|
|
|
176
189
|
# Post to bridge with strict 1-second timeout (never block Claude Code)
|
|
177
|
-
PAYLOAD="{\"events\":[{\"type\":\"$TYPE\",\"timestamp\":$TS,\"metadata\":$META}]}"
|
|
190
|
+
PAYLOAD="{\"events\":[{\"type\":\"$TYPE\",\"timestamp\":$TS,\"metadata\":$META}${RECAP_EVENT:-}]}"
|
|
178
191
|
RESPONSE=$(curl -s -w "\n%{http_code}" --max-time 1 --connect-timeout 0.5 \
|
|
179
192
|
-X POST http://localhost:9877/events \
|
|
180
193
|
-H "Content-Type: application/json" \
|
|
@@ -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.3"
|
|
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
|