methodproof 0.7.15__tar.gz → 0.7.17__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.15 → methodproof-0.7.17}/PKG-INFO +3 -4
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/__init__.py +1 -1
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/cli.py +13 -8
- methodproof-0.7.17/methodproof/tui/init.py +387 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/pyproject.toml +2 -3
- {methodproof-0.7.15 → methodproof-0.7.17}/.code-review-graph/.gitignore +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/.code-review-graph/graph.db +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/.github/workflows/ci.yml +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/.gitignore +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/CHANGELOG.md +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/LICENSE +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/README.md +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/__main__.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/_daemon.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/agents/__init__.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/agents/base.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/agents/music.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/agents/terminal.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/agents/watcher.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/analysis.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/binding.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/bip39.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/bridge.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/config.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/crypto.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/e2e.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/graph.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hook.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/__init__.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/claude_code.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/claude_code.sh +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/cline_hook.sh +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/codex_hook.sh +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/gemini_hook.sh +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/install.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/kiro_hook.sh +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/mcp_register.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/openclaw/HOOK.md +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/openclaw/handler.ts +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/openclaw_install.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/opencode_plugin.js +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/hooks/wrappers.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/integrity.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/kdf.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/keychain.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/live.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/lock.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/mcp.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/migrate_db.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/proxy.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/proxy_daemon.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/repos.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/skills/methodproof/SKILL.md +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/store.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/sync.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/tui/__init__.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/tui/consent.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/tui/log.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/tui/review.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/tui/start.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/tui/status.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/tui/theme.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/viewer.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/methodproof/wordlist.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/test_windows_compat.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/__init__.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/conftest.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_analysis.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_cli_auth.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_cli_config.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_cli_helpers.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_cli_session.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_cli_share.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_cli_start.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_cli_update.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_e2e_integration.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_graph.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_hooks.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_live.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_openclaw_hooks.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_profiles.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_security.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_store.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_sync.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_viewer.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/tests/test_wrappers.py +0 -0
- {methodproof-0.7.15 → methodproof-0.7.17}/uv.lock +0 -0
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: methodproof
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.17
|
|
4
4
|
Summary: See how you code. Capture and visualize your engineering process.
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Requires-Python: >=3.11
|
|
8
8
|
Requires-Dist: cryptography>=43.0
|
|
9
9
|
Requires-Dist: keyring>=25.0
|
|
10
|
+
Requires-Dist: rich>=13.7
|
|
11
|
+
Requires-Dist: textual>=0.59
|
|
10
12
|
Requires-Dist: watchdog>=4.0
|
|
11
13
|
Requires-Dist: websocket-client>=1.7
|
|
12
14
|
Provides-Extra: proxy
|
|
13
15
|
Requires-Dist: mitmproxy>=10.0; extra == 'proxy'
|
|
14
|
-
Provides-Extra: ui
|
|
15
|
-
Requires-Dist: rich>=13.7; extra == 'ui'
|
|
16
|
-
Requires-Dist: textual>=0.59; extra == 'ui'
|
|
17
16
|
Description-Content-Type: text/markdown
|
|
18
17
|
|
|
19
18
|
<p align="center">
|
|
@@ -339,6 +339,14 @@ def cmd_init(args: argparse.Namespace) -> None:
|
|
|
339
339
|
cfg.pop(key, None)
|
|
340
340
|
config.save(cfg)
|
|
341
341
|
|
|
342
|
+
# TUI wizard — runs when nothing is set up yet, or on --force, or when ui_mode is on
|
|
343
|
+
needs_setup = not cfg.get("consent_acknowledged")
|
|
344
|
+
use_ui = needs_setup or _resolve_ui(args, cfg)
|
|
345
|
+
if use_ui:
|
|
346
|
+
from methodproof.tui.init import run as tui_init
|
|
347
|
+
tui_init(cfg)
|
|
348
|
+
return
|
|
349
|
+
|
|
342
350
|
if not cfg.get("consent_acknowledged"):
|
|
343
351
|
cfg = _run_consent(cfg)
|
|
344
352
|
config.save(cfg)
|
|
@@ -1231,14 +1239,10 @@ def cmd_start(args: argparse.Namespace) -> None:
|
|
|
1231
1239
|
except Exception as exc:
|
|
1232
1240
|
print(f"Extension: not detected ({exc})")
|
|
1233
1241
|
if _resolve_ui(args, cfg):
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
tui_start(sid, session)
|
|
1239
|
-
return
|
|
1240
|
-
except SystemExit:
|
|
1241
|
-
pass # textual not installed — fall through to plain message
|
|
1242
|
+
session = store.get_session(sid)
|
|
1243
|
+
from methodproof.tui.start import run as tui_start
|
|
1244
|
+
tui_start(sid, session)
|
|
1245
|
+
return
|
|
1242
1246
|
print("Run `mp stop` to finish.")
|
|
1243
1247
|
return
|
|
1244
1248
|
|
|
@@ -2086,6 +2090,7 @@ def main() -> None:
|
|
|
2086
2090
|
|
|
2087
2091
|
s = sub.add_parser("init", help="Install shell hook")
|
|
2088
2092
|
s.add_argument("--force", action="store_true", help="Re-run all setup prompts from scratch")
|
|
2093
|
+
_add_ui_flags(s)
|
|
2089
2094
|
sub.add_parser("shell-hook", help="Print shell hook for eval (activates without restart)")
|
|
2090
2095
|
s = sub.add_parser("start", help="Start recording")
|
|
2091
2096
|
s.add_argument("--dir", help="Directory to watch")
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"""Textual TUI for mp init — two-screen setup wizard."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from textual.app import App, ComposeResult
|
|
5
|
+
from textual.binding import Binding
|
|
6
|
+
from textual.containers import Horizontal, ScrollableContainer, Vertical
|
|
7
|
+
from textual.screen import Screen
|
|
8
|
+
from textual.widgets import Button, Footer, Header, Label, RichLog, Rule, Static, Switch
|
|
9
|
+
|
|
10
|
+
from methodproof import config as cfg_mod
|
|
11
|
+
from methodproof.tui.theme import BASE_CSS, BORDER, DIM, GOLD, GREEN, PURPLE, RED, TEXT
|
|
12
|
+
|
|
13
|
+
_REDACTABLE = [
|
|
14
|
+
("command_output", "Terminal output"),
|
|
15
|
+
("ai_prompts", "AI prompt text"),
|
|
16
|
+
("ai_responses", "AI response text"),
|
|
17
|
+
("code_capture", "File diffs"),
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
_CSS = BASE_CSS + f"""
|
|
21
|
+
PrefsScreen {{
|
|
22
|
+
padding: 0;
|
|
23
|
+
}}
|
|
24
|
+
#prefs-scroll {{
|
|
25
|
+
padding: 1 3;
|
|
26
|
+
}}
|
|
27
|
+
.section-heading {{
|
|
28
|
+
color: {GOLD};
|
|
29
|
+
text-style: bold;
|
|
30
|
+
margin: 1 0 0 0;
|
|
31
|
+
}}
|
|
32
|
+
.section-sub {{
|
|
33
|
+
color: {DIM};
|
|
34
|
+
margin: 0 0 1 0;
|
|
35
|
+
}}
|
|
36
|
+
.toggle-row {{
|
|
37
|
+
height: 2;
|
|
38
|
+
align: left middle;
|
|
39
|
+
}}
|
|
40
|
+
.toggle-row Switch {{
|
|
41
|
+
margin: 0 1 0 0;
|
|
42
|
+
width: 4;
|
|
43
|
+
}}
|
|
44
|
+
.row-label {{
|
|
45
|
+
color: {TEXT};
|
|
46
|
+
}}
|
|
47
|
+
.pro-row .row-label {{
|
|
48
|
+
color: {PURPLE};
|
|
49
|
+
}}
|
|
50
|
+
.pro-badge {{
|
|
51
|
+
background: #200a26;
|
|
52
|
+
color: {PURPLE};
|
|
53
|
+
padding: 0 1;
|
|
54
|
+
margin: 0 0 0 1;
|
|
55
|
+
width: 5;
|
|
56
|
+
}}
|
|
57
|
+
.opt-row {{
|
|
58
|
+
height: 2;
|
|
59
|
+
align: left middle;
|
|
60
|
+
}}
|
|
61
|
+
.opt-row Switch {{
|
|
62
|
+
margin: 0 1 0 0;
|
|
63
|
+
width: 4;
|
|
64
|
+
}}
|
|
65
|
+
#fs-status {{
|
|
66
|
+
color: {DIM};
|
|
67
|
+
height: 1;
|
|
68
|
+
margin: 1 0 0 0;
|
|
69
|
+
}}
|
|
70
|
+
#fs-status.full {{
|
|
71
|
+
color: {GOLD};
|
|
72
|
+
}}
|
|
73
|
+
#begin-btn {{
|
|
74
|
+
margin: 2 0 1 0;
|
|
75
|
+
background: {GOLD};
|
|
76
|
+
color: #12110f;
|
|
77
|
+
border: none;
|
|
78
|
+
width: 20;
|
|
79
|
+
}}
|
|
80
|
+
|
|
81
|
+
ResultsScreen {{
|
|
82
|
+
padding: 0;
|
|
83
|
+
}}
|
|
84
|
+
#results-log {{
|
|
85
|
+
padding: 1 2;
|
|
86
|
+
border: none;
|
|
87
|
+
}}
|
|
88
|
+
#eval-box {{
|
|
89
|
+
background: #050403;
|
|
90
|
+
border: solid {BORDER};
|
|
91
|
+
margin: 1 2;
|
|
92
|
+
padding: 1 2;
|
|
93
|
+
height: 5;
|
|
94
|
+
}}
|
|
95
|
+
#eval-label {{
|
|
96
|
+
color: {DIM};
|
|
97
|
+
margin: 0 0 1 0;
|
|
98
|
+
}}
|
|
99
|
+
#eval-cmd {{
|
|
100
|
+
color: {GOLD};
|
|
101
|
+
text-style: bold;
|
|
102
|
+
}}
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class PrefsScreen(Screen[dict | None]):
|
|
107
|
+
"""Step 1: collect all preferences before any side effects run."""
|
|
108
|
+
|
|
109
|
+
BINDINGS = [
|
|
110
|
+
Binding("enter", "begin", "begin setup"),
|
|
111
|
+
Binding("escape", "cancel", "cancel"),
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
def __init__(self, cfg: dict) -> None:
|
|
115
|
+
super().__init__()
|
|
116
|
+
self._capture = dict(cfg.get("capture", {}))
|
|
117
|
+
self._redact = dict(cfg.get("publish_redact", {}))
|
|
118
|
+
self._auto_update = cfg.get("auto_update", True)
|
|
119
|
+
self._alias = cfg.get("alias_installed", True)
|
|
120
|
+
|
|
121
|
+
def compose(self) -> ComposeResult:
|
|
122
|
+
yield Header(show_clock=False)
|
|
123
|
+
with ScrollableContainer(id="prefs-scroll"):
|
|
124
|
+
yield Static("Capture settings", classes="section-heading")
|
|
125
|
+
yield Static("Choose what MethodProof records during your sessions.", classes="section-sub")
|
|
126
|
+
|
|
127
|
+
for cat in cfg_mod.STANDARD_CATEGORIES:
|
|
128
|
+
desc = cfg_mod.CAPTURE_DESCRIPTIONS.get(cat, cat)
|
|
129
|
+
enabled = self._capture.get(cat, True)
|
|
130
|
+
with Horizontal(classes="toggle-row", id=f"row-cap-{cat}"):
|
|
131
|
+
yield Switch(value=enabled, id=f"cap-{cat}", animate=False)
|
|
132
|
+
yield Label(desc, classes="row-label")
|
|
133
|
+
|
|
134
|
+
with Horizontal(classes="toggle-row pro-row", id="row-cap-code_capture"):
|
|
135
|
+
yield Switch(value=self._capture.get("code_capture", False),
|
|
136
|
+
id="cap-code_capture", animate=False)
|
|
137
|
+
yield Label("Full code diffs and git patches", classes="row-label")
|
|
138
|
+
yield Static("Pro", classes="pro-badge")
|
|
139
|
+
|
|
140
|
+
yield Static("", id="fs-status")
|
|
141
|
+
|
|
142
|
+
yield Rule()
|
|
143
|
+
yield Static("Publish redaction", classes="section-heading")
|
|
144
|
+
yield Static(
|
|
145
|
+
"Fields stripped when you make a session public via mp publish.",
|
|
146
|
+
classes="section-sub",
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
for key, desc in _REDACTABLE:
|
|
150
|
+
redacted = self._redact.get(key, True)
|
|
151
|
+
with Horizontal(classes="toggle-row", id=f"row-red-{key}"):
|
|
152
|
+
yield Switch(value=redacted, id=f"red-{key}", animate=False)
|
|
153
|
+
yield Label(f"Redact: {desc}", classes="row-label")
|
|
154
|
+
|
|
155
|
+
yield Rule()
|
|
156
|
+
yield Static("Options", classes="section-heading")
|
|
157
|
+
|
|
158
|
+
with Horizontal(classes="opt-row"):
|
|
159
|
+
yield Switch(value=self._auto_update, id="opt-auto-update", animate=False)
|
|
160
|
+
yield Label("Auto-update before each session", classes="row-label")
|
|
161
|
+
|
|
162
|
+
with Horizontal(classes="opt-row"):
|
|
163
|
+
yield Switch(value=self._alias, id="opt-alias", animate=False)
|
|
164
|
+
yield Label("Install `mp` shorthand alias", classes="row-label")
|
|
165
|
+
|
|
166
|
+
yield Button("Begin Setup →", id="begin-btn", variant="primary")
|
|
167
|
+
|
|
168
|
+
yield Footer()
|
|
169
|
+
|
|
170
|
+
def on_mount(self) -> None:
|
|
171
|
+
self._refresh_fs()
|
|
172
|
+
|
|
173
|
+
def on_switch_changed(self, event: Switch.Changed) -> None:
|
|
174
|
+
sid = event.switch.id or ""
|
|
175
|
+
if sid.startswith("cap-"):
|
|
176
|
+
cat = sid[4:]
|
|
177
|
+
self._capture[cat] = event.value
|
|
178
|
+
self._refresh_fs()
|
|
179
|
+
elif sid.startswith("red-"):
|
|
180
|
+
self._redact[sid[4:]] = event.value
|
|
181
|
+
elif sid == "opt-auto-update":
|
|
182
|
+
self._auto_update = event.value
|
|
183
|
+
elif sid == "opt-alias":
|
|
184
|
+
self._alias = event.value
|
|
185
|
+
|
|
186
|
+
def _refresh_fs(self) -> None:
|
|
187
|
+
all_on = all(self._capture.get(c, True) for c in cfg_mod.STANDARD_CATEGORIES)
|
|
188
|
+
widget = self.query_one("#fs-status", Static)
|
|
189
|
+
if all_on:
|
|
190
|
+
widget.update(f"[{GOLD}]★ Full Spectrum — live streaming unlocked[/{GOLD}]")
|
|
191
|
+
widget.add_class("full")
|
|
192
|
+
else:
|
|
193
|
+
widget.update(f"[{DIM}]Enable all 10 standard categories for Full Spectrum[/{DIM}]")
|
|
194
|
+
widget.remove_class("full")
|
|
195
|
+
|
|
196
|
+
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
197
|
+
if event.button.id == "begin-btn":
|
|
198
|
+
self.action_begin()
|
|
199
|
+
|
|
200
|
+
def action_begin(self) -> None:
|
|
201
|
+
self.dismiss({
|
|
202
|
+
"capture": self._capture,
|
|
203
|
+
"publish_redact": self._redact,
|
|
204
|
+
"auto_update": self._auto_update,
|
|
205
|
+
"install_alias": self._alias,
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
def action_cancel(self) -> None:
|
|
209
|
+
self.dismiss(None)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class ResultsScreen(Screen[None]):
|
|
213
|
+
"""Step 2: run side effects and display progress."""
|
|
214
|
+
|
|
215
|
+
BINDINGS = [Binding("escape", "done", "done")]
|
|
216
|
+
|
|
217
|
+
def __init__(self, cfg: dict, prefs: dict) -> None:
|
|
218
|
+
super().__init__()
|
|
219
|
+
self._cfg = cfg
|
|
220
|
+
self._prefs = prefs
|
|
221
|
+
|
|
222
|
+
def compose(self) -> ComposeResult:
|
|
223
|
+
yield Header(show_clock=False)
|
|
224
|
+
yield RichLog(id="results-log", highlight=True, markup=True)
|
|
225
|
+
with Vertical(id="eval-box"):
|
|
226
|
+
yield Static("Activate now (or restart your shell):", id="eval-label")
|
|
227
|
+
yield Static('eval "$(methodproof shell-hook)"', id="eval-cmd", markup=False)
|
|
228
|
+
yield Footer()
|
|
229
|
+
|
|
230
|
+
def on_mount(self) -> None:
|
|
231
|
+
self.run_worker(self._run_setup, exclusive=True, thread=True)
|
|
232
|
+
|
|
233
|
+
def _log(self, msg: str) -> None:
|
|
234
|
+
self.query_one(RichLog).write(msg)
|
|
235
|
+
|
|
236
|
+
def _run_setup(self) -> None:
|
|
237
|
+
from methodproof import config, store
|
|
238
|
+
from methodproof.hooks import hook
|
|
239
|
+
|
|
240
|
+
cfg = self._cfg
|
|
241
|
+
prefs = self._prefs
|
|
242
|
+
|
|
243
|
+
# Apply preferences
|
|
244
|
+
cfg["capture"] = prefs["capture"]
|
|
245
|
+
cfg["publish_redact"] = prefs["publish_redact"]
|
|
246
|
+
cfg["auto_update"] = prefs["auto_update"]
|
|
247
|
+
cfg["auto_update_offered"] = True
|
|
248
|
+
cfg["alias_offered"] = True
|
|
249
|
+
cfg["ui_mode_offered"] = True
|
|
250
|
+
cfg["ui_mode"] = True
|
|
251
|
+
cfg["consent_acknowledged"] = True
|
|
252
|
+
|
|
253
|
+
if prefs["install_alias"]:
|
|
254
|
+
try:
|
|
255
|
+
from methodproof.cli import _install_alias
|
|
256
|
+
_install_alias()
|
|
257
|
+
cfg["alias_installed"] = True
|
|
258
|
+
self._log(f"[{GREEN}]✓[/{GREEN}] Alias: mp → methodproof")
|
|
259
|
+
except Exception as exc:
|
|
260
|
+
self._log(f"[{DIM}] Alias: skipped ({exc})[/{DIM}]")
|
|
261
|
+
else:
|
|
262
|
+
self._log(f"[{DIM}] Alias: skipped[/{DIM}]")
|
|
263
|
+
|
|
264
|
+
config.save(cfg)
|
|
265
|
+
store.init_db()
|
|
266
|
+
|
|
267
|
+
# Shell hook
|
|
268
|
+
capture = cfg["capture"]
|
|
269
|
+
if capture.get("terminal_commands", True):
|
|
270
|
+
try:
|
|
271
|
+
rc = hook.install()
|
|
272
|
+
self._log(f"[{GREEN}]✓[/{GREEN}] Shell hook: {rc}")
|
|
273
|
+
except Exception as exc:
|
|
274
|
+
self._log(f"[{RED}]✗[/{RED}] Shell hook: {exc}")
|
|
275
|
+
else:
|
|
276
|
+
self._log(f"[{DIM}] Shell hook: skipped (terminal_commands off)[/{DIM}]")
|
|
277
|
+
|
|
278
|
+
# AI hooks
|
|
279
|
+
ai_enabled = capture.get("ai_prompts", True) or capture.get("ai_responses", True)
|
|
280
|
+
if ai_enabled:
|
|
281
|
+
try:
|
|
282
|
+
from methodproof.hooks.install import install as install_claude_hooks
|
|
283
|
+
result = install_claude_hooks()
|
|
284
|
+
if result is None:
|
|
285
|
+
self._log(f"[{DIM}] Claude Code: not found[/{DIM}]")
|
|
286
|
+
else:
|
|
287
|
+
self._log(f"[{GREEN}]✓[/{GREEN}] Claude Code hooks: {result}")
|
|
288
|
+
except Exception as exc:
|
|
289
|
+
self._log(f"[{DIM}] Claude Code: {exc}[/{DIM}]")
|
|
290
|
+
|
|
291
|
+
try:
|
|
292
|
+
from methodproof.mcp import register_with_claude
|
|
293
|
+
mcp = register_with_claude()
|
|
294
|
+
if mcp is None:
|
|
295
|
+
self._log(f"[{DIM}] Claude Code MCP: skipped[/{DIM}]")
|
|
296
|
+
elif mcp == "already registered":
|
|
297
|
+
self._log(f"[{GREEN}]✓[/{GREEN}] Claude Code MCP: already registered")
|
|
298
|
+
else:
|
|
299
|
+
self._log(f"[{GREEN}]✓[/{GREEN}] Claude Code MCP: registered in {mcp}")
|
|
300
|
+
except Exception as exc:
|
|
301
|
+
self._log(f"[{DIM}] MCP: {exc}[/{DIM}]")
|
|
302
|
+
|
|
303
|
+
try:
|
|
304
|
+
from methodproof.hooks.wrappers import install as install_wrappers
|
|
305
|
+
wrapped = install_wrappers()
|
|
306
|
+
if wrapped:
|
|
307
|
+
self._log(f"[{GREEN}]✓[/{GREEN}] AI CLI wrappers: {', '.join(wrapped)}")
|
|
308
|
+
else:
|
|
309
|
+
self._log(f"[{DIM}] AI CLI wrappers: no tools found[/{DIM}]")
|
|
310
|
+
except Exception as exc:
|
|
311
|
+
self._log(f"[{DIM}] AI CLI wrappers: {exc}[/{DIM}]")
|
|
312
|
+
|
|
313
|
+
try:
|
|
314
|
+
from methodproof.hooks.openclaw_install import install as install_oc, install_skill
|
|
315
|
+
oc = install_oc()
|
|
316
|
+
if oc is None:
|
|
317
|
+
self._log(f"[{DIM}] OpenClaw: not found[/{DIM}]")
|
|
318
|
+
else:
|
|
319
|
+
self._log(f"[{GREEN}]✓[/{GREEN}] OpenClaw hooks: {oc}")
|
|
320
|
+
skill = install_skill()
|
|
321
|
+
if skill:
|
|
322
|
+
self._log(f"[{GREEN}]✓[/{GREEN}] OpenClaw skill: {skill}")
|
|
323
|
+
except Exception as exc:
|
|
324
|
+
self._log(f"[{DIM}] OpenClaw: {exc}[/{DIM}]")
|
|
325
|
+
else:
|
|
326
|
+
self._log(f"[{DIM}] AI hooks: skipped[/{DIM}]")
|
|
327
|
+
|
|
328
|
+
# Local AI ports — skip interactive prompt, use defaults
|
|
329
|
+
if ai_enabled and not cfg.get("local_ai_ports_offered"):
|
|
330
|
+
cfg["local_ai_ports_offered"] = True
|
|
331
|
+
cfg["local_ai_ports"] = []
|
|
332
|
+
config.save(cfg)
|
|
333
|
+
self._log(f"[{DIM}] Local AI ports: using defaults (Ollama 11434, Jan 1234)[/{DIM}]")
|
|
334
|
+
|
|
335
|
+
# Signing key
|
|
336
|
+
try:
|
|
337
|
+
from methodproof.integrity import has_keypair
|
|
338
|
+
if not has_keypair():
|
|
339
|
+
from methodproof.integrity import generate_keypair
|
|
340
|
+
pub = generate_keypair()
|
|
341
|
+
self._log(f"[{GREEN}]✓[/{GREEN}] Signing key: generated ({len(pub)} bytes)")
|
|
342
|
+
else:
|
|
343
|
+
self._log(f"[{GREEN}]✓[/{GREEN}] Signing key: exists")
|
|
344
|
+
except ImportError:
|
|
345
|
+
self._log(f"[{DIM}] Signing key: skipped (pip install methodproof[signing])[/{DIM}]")
|
|
346
|
+
|
|
347
|
+
# Research consent sync
|
|
348
|
+
if cfg.get("token"):
|
|
349
|
+
try:
|
|
350
|
+
cfg["_pending_research_sync"] = True
|
|
351
|
+
config.save(cfg)
|
|
352
|
+
from methodproof.sync import sync_research_consent
|
|
353
|
+
sync_research_consent(cfg["token"], cfg["api_url"])
|
|
354
|
+
self._log(f"[{GREEN}]✓[/{GREEN}] Research consent synced")
|
|
355
|
+
except Exception:
|
|
356
|
+
pass
|
|
357
|
+
|
|
358
|
+
self._log("")
|
|
359
|
+
self._log(f"[{GOLD}]Setup complete.[/{GOLD}] Press Esc to exit.")
|
|
360
|
+
|
|
361
|
+
def action_done(self) -> None:
|
|
362
|
+
self.dismiss(None)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
class InitApp(App[None]):
|
|
366
|
+
"""mp init setup wizard — preferences then install."""
|
|
367
|
+
|
|
368
|
+
TITLE = "methodproof — mp init"
|
|
369
|
+
CSS = _CSS
|
|
370
|
+
|
|
371
|
+
def __init__(self, cfg: dict) -> None:
|
|
372
|
+
super().__init__()
|
|
373
|
+
self._cfg = cfg
|
|
374
|
+
|
|
375
|
+
def on_mount(self) -> None:
|
|
376
|
+
self.push_screen(PrefsScreen(self._cfg), self._on_prefs_done)
|
|
377
|
+
|
|
378
|
+
def _on_prefs_done(self, prefs: dict | None) -> None:
|
|
379
|
+
if prefs is None:
|
|
380
|
+
self.exit(None)
|
|
381
|
+
return
|
|
382
|
+
self.push_screen(ResultsScreen(self._cfg, prefs))
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def run(cfg: dict) -> None:
|
|
386
|
+
"""Launch the init wizard."""
|
|
387
|
+
InitApp(cfg).run()
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "methodproof"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.17"
|
|
4
4
|
description = "See how you code. Capture and visualize your engineering process."
|
|
5
5
|
requires-python = ">=3.11"
|
|
6
|
-
dependencies = ["watchdog>=4.0", "websocket-client>=1.7", "cryptography>=43.0", "keyring>=25.0"]
|
|
6
|
+
dependencies = ["watchdog>=4.0", "websocket-client>=1.7", "cryptography>=43.0", "keyring>=25.0", "textual>=0.59", "rich>=13.7"]
|
|
7
7
|
license = "Apache-2.0"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
|
|
10
10
|
[project.optional-dependencies]
|
|
11
11
|
proxy = ["mitmproxy>=10.0"]
|
|
12
|
-
ui = ["textual>=0.59", "rich>=13.7"]
|
|
13
12
|
|
|
14
13
|
[dependency-groups]
|
|
15
14
|
dev = ["pytest>=8.0", "pytest-cov>=5.0"]
|
|
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
|
|
File without changes
|
|
File without changes
|