methodproof 0.7.15__tar.gz → 0.7.16__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.
Files changed (87) hide show
  1. {methodproof-0.7.15 → methodproof-0.7.16}/PKG-INFO +1 -1
  2. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/__init__.py +1 -1
  3. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/cli.py +13 -0
  4. methodproof-0.7.16/methodproof/tui/init.py +387 -0
  5. {methodproof-0.7.15 → methodproof-0.7.16}/pyproject.toml +1 -1
  6. {methodproof-0.7.15 → methodproof-0.7.16}/.code-review-graph/.gitignore +0 -0
  7. {methodproof-0.7.15 → methodproof-0.7.16}/.code-review-graph/graph.db +0 -0
  8. {methodproof-0.7.15 → methodproof-0.7.16}/.github/workflows/ci.yml +0 -0
  9. {methodproof-0.7.15 → methodproof-0.7.16}/.gitignore +0 -0
  10. {methodproof-0.7.15 → methodproof-0.7.16}/CHANGELOG.md +0 -0
  11. {methodproof-0.7.15 → methodproof-0.7.16}/LICENSE +0 -0
  12. {methodproof-0.7.15 → methodproof-0.7.16}/README.md +0 -0
  13. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/__main__.py +0 -0
  14. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/_daemon.py +0 -0
  15. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/agents/__init__.py +0 -0
  16. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/agents/base.py +0 -0
  17. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/agents/music.py +0 -0
  18. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/agents/terminal.py +0 -0
  19. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/agents/watcher.py +0 -0
  20. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/analysis.py +0 -0
  21. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/binding.py +0 -0
  22. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/bip39.py +0 -0
  23. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/bridge.py +0 -0
  24. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/config.py +0 -0
  25. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/crypto.py +0 -0
  26. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/e2e.py +0 -0
  27. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/graph.py +0 -0
  28. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hook.py +0 -0
  29. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/__init__.py +0 -0
  30. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/claude_code.py +0 -0
  31. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/claude_code.sh +0 -0
  32. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/cline_hook.sh +0 -0
  33. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/codex_hook.sh +0 -0
  34. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/gemini_hook.sh +0 -0
  35. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/install.py +0 -0
  36. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/kiro_hook.sh +0 -0
  37. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/mcp_register.py +0 -0
  38. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/openclaw/HOOK.md +0 -0
  39. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/openclaw/handler.ts +0 -0
  40. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/openclaw_install.py +0 -0
  41. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/opencode_plugin.js +0 -0
  42. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/hooks/wrappers.py +0 -0
  43. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/integrity.py +0 -0
  44. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/kdf.py +0 -0
  45. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/keychain.py +0 -0
  46. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/live.py +0 -0
  47. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/lock.py +0 -0
  48. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/mcp.py +0 -0
  49. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/migrate_db.py +0 -0
  50. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/proxy.py +0 -0
  51. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/proxy_daemon.py +0 -0
  52. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/repos.py +0 -0
  53. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/skills/methodproof/SKILL.md +0 -0
  54. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/store.py +0 -0
  55. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/sync.py +0 -0
  56. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/tui/__init__.py +0 -0
  57. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/tui/consent.py +0 -0
  58. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/tui/log.py +0 -0
  59. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/tui/review.py +0 -0
  60. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/tui/start.py +0 -0
  61. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/tui/status.py +0 -0
  62. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/tui/theme.py +0 -0
  63. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/viewer.py +0 -0
  64. {methodproof-0.7.15 → methodproof-0.7.16}/methodproof/wordlist.py +0 -0
  65. {methodproof-0.7.15 → methodproof-0.7.16}/test_windows_compat.py +0 -0
  66. {methodproof-0.7.15 → methodproof-0.7.16}/tests/__init__.py +0 -0
  67. {methodproof-0.7.15 → methodproof-0.7.16}/tests/conftest.py +0 -0
  68. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_analysis.py +0 -0
  69. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_cli_auth.py +0 -0
  70. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_cli_config.py +0 -0
  71. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_cli_helpers.py +0 -0
  72. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_cli_session.py +0 -0
  73. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_cli_share.py +0 -0
  74. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_cli_start.py +0 -0
  75. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_cli_update.py +0 -0
  76. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_e2e_integration.py +0 -0
  77. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_graph.py +0 -0
  78. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_hooks.py +0 -0
  79. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_live.py +0 -0
  80. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_openclaw_hooks.py +0 -0
  81. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_profiles.py +0 -0
  82. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_security.py +0 -0
  83. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_store.py +0 -0
  84. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_sync.py +0 -0
  85. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_viewer.py +0 -0
  86. {methodproof-0.7.15 → methodproof-0.7.16}/tests/test_wrappers.py +0 -0
  87. {methodproof-0.7.15 → methodproof-0.7.16}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: methodproof
3
- Version: 0.7.15
3
+ Version: 0.7.16
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
@@ -1,3 +1,3 @@
1
1
  """MethodProof — see how you code."""
2
2
 
3
- __version__ = "0.7.15"
3
+ __version__ = "0.7.16"
@@ -339,6 +339,18 @@ 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
+ try:
347
+ _tui_guard()
348
+ from methodproof.tui.init import run as tui_init
349
+ tui_init(cfg)
350
+ return
351
+ except SystemExit:
352
+ pass # textual not installed — fall through to classic
353
+
342
354
  if not cfg.get("consent_acknowledged"):
343
355
  cfg = _run_consent(cfg)
344
356
  config.save(cfg)
@@ -2086,6 +2098,7 @@ def main() -> None:
2086
2098
 
2087
2099
  s = sub.add_parser("init", help="Install shell hook")
2088
2100
  s.add_argument("--force", action="store_true", help="Re-run all setup prompts from scratch")
2101
+ _add_ui_flags(s)
2089
2102
  sub.add_parser("shell-hook", help="Print shell hook for eval (activates without restart)")
2090
2103
  s = sub.add_parser("start", help="Start recording")
2091
2104
  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,6 +1,6 @@
1
1
  [project]
2
2
  name = "methodproof"
3
- version = "0.7.15"
3
+ version = "0.7.16"
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"]
File without changes
File without changes
File without changes
File without changes
File without changes