methodproof 0.7.18__tar.gz → 0.7.20__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 (88) hide show
  1. {methodproof-0.7.18 → methodproof-0.7.20}/CHANGELOG.md +8 -0
  2. {methodproof-0.7.18 → methodproof-0.7.20}/PKG-INFO +1 -1
  3. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/__init__.py +1 -1
  4. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/cli.py +20 -13
  5. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/tui/consent.py +1 -1
  6. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/tui/init.py +8 -5
  7. methodproof-0.7.20/methodproof/tui/login_success.py +41 -0
  8. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/tui/status.py +7 -5
  9. {methodproof-0.7.18 → methodproof-0.7.20}/pyproject.toml +1 -1
  10. {methodproof-0.7.18 → methodproof-0.7.20}/tests/conftest.py +2 -1
  11. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_cli_auth.py +1 -1
  12. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_cli_share.py +1 -1
  13. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_cli_start.py +7 -17
  14. methodproof-0.7.18/.code-review-graph/.gitignore +0 -3
  15. methodproof-0.7.18/.code-review-graph/graph.db +0 -0
  16. {methodproof-0.7.18 → methodproof-0.7.20}/.github/workflows/ci.yml +0 -0
  17. {methodproof-0.7.18 → methodproof-0.7.20}/.gitignore +0 -0
  18. {methodproof-0.7.18 → methodproof-0.7.20}/LICENSE +0 -0
  19. {methodproof-0.7.18 → methodproof-0.7.20}/README.md +0 -0
  20. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/__main__.py +0 -0
  21. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/_daemon.py +0 -0
  22. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/agents/__init__.py +0 -0
  23. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/agents/base.py +0 -0
  24. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/agents/music.py +0 -0
  25. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/agents/terminal.py +0 -0
  26. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/agents/watcher.py +0 -0
  27. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/analysis.py +0 -0
  28. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/binding.py +0 -0
  29. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/bip39.py +0 -0
  30. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/bridge.py +0 -0
  31. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/config.py +0 -0
  32. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/crypto.py +0 -0
  33. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/e2e.py +0 -0
  34. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/graph.py +0 -0
  35. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hook.py +0 -0
  36. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/__init__.py +0 -0
  37. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/claude_code.py +0 -0
  38. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/claude_code.sh +0 -0
  39. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/cline_hook.sh +0 -0
  40. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/codex_hook.sh +0 -0
  41. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/gemini_hook.sh +0 -0
  42. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/install.py +0 -0
  43. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/kiro_hook.sh +0 -0
  44. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/mcp_register.py +0 -0
  45. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/openclaw/HOOK.md +0 -0
  46. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/openclaw/handler.ts +0 -0
  47. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/openclaw_install.py +0 -0
  48. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/opencode_plugin.js +0 -0
  49. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/hooks/wrappers.py +0 -0
  50. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/integrity.py +0 -0
  51. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/kdf.py +0 -0
  52. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/keychain.py +0 -0
  53. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/live.py +0 -0
  54. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/lock.py +0 -0
  55. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/mcp.py +0 -0
  56. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/migrate_db.py +0 -0
  57. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/proxy.py +0 -0
  58. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/proxy_daemon.py +0 -0
  59. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/repos.py +0 -0
  60. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/skills/methodproof/SKILL.md +0 -0
  61. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/store.py +0 -0
  62. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/sync.py +0 -0
  63. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/tui/__init__.py +0 -0
  64. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/tui/log.py +0 -0
  65. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/tui/review.py +0 -0
  66. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/tui/start.py +0 -0
  67. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/tui/theme.py +0 -0
  68. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/viewer.py +0 -0
  69. {methodproof-0.7.18 → methodproof-0.7.20}/methodproof/wordlist.py +0 -0
  70. {methodproof-0.7.18 → methodproof-0.7.20}/test_windows_compat.py +0 -0
  71. {methodproof-0.7.18 → methodproof-0.7.20}/tests/__init__.py +0 -0
  72. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_analysis.py +0 -0
  73. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_cli_config.py +0 -0
  74. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_cli_helpers.py +0 -0
  75. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_cli_session.py +0 -0
  76. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_cli_update.py +0 -0
  77. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_e2e_integration.py +0 -0
  78. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_graph.py +0 -0
  79. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_hooks.py +0 -0
  80. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_live.py +0 -0
  81. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_openclaw_hooks.py +0 -0
  82. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_profiles.py +0 -0
  83. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_security.py +0 -0
  84. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_store.py +0 -0
  85. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_sync.py +0 -0
  86. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_viewer.py +0 -0
  87. {methodproof-0.7.18 → methodproof-0.7.20}/tests/test_wrappers.py +0 -0
  88. {methodproof-0.7.18 → methodproof-0.7.20}/uv.lock +0 -0
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.20] — 2026-04-11
4
+
5
+ ### Fixed
6
+ - **TUI init layout** — toggle rows now use `height: auto` so long capture descriptions don't clip; `width: 1fr` on `.row-label` overrides the 24-char truncation from base CSS; Switch widgets widened from 4→6 for correct rendering; Full Spectrum status no longer cut off at 1 line
7
+ - **TUI consent layout** — Switch widgets widened from 4→6 (same fix as init)
8
+ - **TUI status markup** — `_token_expiry()` was returning Rich markup strings into `Text.append()`, which treats input as plain text; tags now rendered literally (e.g. `[#d93326]expires soon[/#d93326]`). Fixed to return `(text, style)` tuple and apply styles at call site
9
+ - **Session complete timeout** — `mp push` was using 15s for the final `/complete` call, which times out on large sessions while the server drains the ingest queue and materializes stats. Raised to 90s. Timeout is now a configurable parameter on internal request helpers
10
+
3
11
  ## [0.7.6] — 2026-04-08
4
12
 
5
13
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: methodproof
3
- Version: 0.7.18
3
+ Version: 0.7.20
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.18"
3
+ __version__ = "0.7.19"
@@ -1398,7 +1398,6 @@ def cmd_log(args: argparse.Namespace) -> None:
1398
1398
  return
1399
1399
  print(f"Found {len(empty)} empty session{'s' if len(empty) != 1 else ''}:")
1400
1400
  for s in empty:
1401
- from datetime import datetime, UTC
1402
1401
  dt = datetime.fromtimestamp(s["created_at"], tz=UTC).strftime("%Y-%m-%d %H:%M")
1403
1402
  print(f" {s['id'][:8]} {dt} {s.get('watch_dir', '?')}")
1404
1403
  answer = input(f"\nDelete {len(empty)} empty session{'s' if len(empty) != 1 else ''}? [y/N]: ").strip().lower()
@@ -1646,13 +1645,15 @@ def cmd_login(args: argparse.Namespace) -> None:
1646
1645
  api = args.api_url or cfg["api_url"]
1647
1646
 
1648
1647
  if cfg.get("token") and not getattr(args, "force", False):
1649
- current = cfg.get("email") or cfg.get("account_id", "")[:8] or "an account"
1650
- print(f"Already logged in as {current}.")
1651
- answer = input(" Switch accounts? [y/N]: ").strip().lower()
1652
- if answer not in ("y", "yes"):
1653
- return
1654
- # Stash current profile before switching
1655
- config.save_active_profile(cfg)
1648
+ if getattr(args, "add", False):
1649
+ config.save_active_profile(cfg)
1650
+ else:
1651
+ current = cfg.get("email") or cfg.get("account_id", "")[:8] or "an account"
1652
+ print(f"Already logged in as {current}.")
1653
+ answer = input(" Switch accounts? [y/N]: ").strip().lower()
1654
+ if answer not in ("y", "yes"):
1655
+ return
1656
+ config.save_active_profile(cfg)
1656
1657
 
1657
1658
  # Start device auth flow
1658
1659
  result = _request("POST", "/auth/cli/start", api, "")
@@ -1661,6 +1662,9 @@ def cmd_login(args: argparse.Namespace) -> None:
1661
1662
  user_code = result.get("user_code", "")
1662
1663
  verification_url = result.get("verification_url", "")
1663
1664
 
1665
+ if getattr(args, "add", False):
1666
+ auth_url += ("&" if "?" in auth_url else "?") + "mode=add"
1667
+
1664
1668
  print(f"\nOpening browser to sign in...\n")
1665
1669
  print(f" {auth_url}\n")
1666
1670
  if user_code and verification_url:
@@ -1692,11 +1696,13 @@ def cmd_login(args: argparse.Namespace) -> None:
1692
1696
  _setup_master_key(cfg)
1693
1697
  from methodproof.sync import sync_research_consent
1694
1698
  sync_research_consent(cfg["token"], cfg["api_url"])
1695
- label = cfg.get("email") or cfg.get("account_id", "")[:8]
1696
- profiles = cfg.get("profiles", {})
1697
- n = len(profiles)
1698
- print(f"Logged in as {label}. {n} account{'s' if n != 1 else ''} on this device.")
1699
- print(" Quick-swap: `mp switch`")
1699
+ from methodproof.tui.login_success import run as show_success
1700
+ show_success(
1701
+ display_name=cfg.get("email") or cfg.get("account_id", "")[:8],
1702
+ email=cfg.get("email", ""),
1703
+ account_count=len(cfg.get("profiles", {})),
1704
+ added=getattr(args, "add", False),
1705
+ )
1700
1706
  return
1701
1707
  except Exception:
1702
1708
  pass
@@ -2134,6 +2140,7 @@ def main() -> None:
2134
2140
  _add_ui_flags(s_status)
2135
2141
  l = sub.add_parser("login", help="Connect to platform")
2136
2142
  l.add_argument("--api-url")
2143
+ l.add_argument("--add", action="store_true", help="Add another account to this device")
2137
2144
  l.add_argument("--force", "-f", action="store_true", help="Skip switch-account prompt")
2138
2145
  l.add_argument("--no-key", action="store_true", help="Skip master key generation (test accounts)")
2139
2146
  sub.add_parser("logout", help="Clear login credentials (keeps consent and sessions)")
@@ -53,7 +53,7 @@ _CSS = BASE_CSS + f"""
53
53
  }}
54
54
  .toggle-row Switch {{
55
55
  margin: 0 1 0 0;
56
- width: 4;
56
+ width: 6;
57
57
  }}
58
58
  .pro-row .row-label {{
59
59
  color: {PURPLE};
@@ -34,14 +34,16 @@ PrefsScreen {{
34
34
  margin: 0 0 1 0;
35
35
  }}
36
36
  .toggle-row {{
37
- height: 2;
37
+ height: auto;
38
38
  align: left middle;
39
+ margin: 0 0 1 0;
39
40
  }}
40
41
  .toggle-row Switch {{
41
42
  margin: 0 1 0 0;
42
- width: 4;
43
+ width: 6;
43
44
  }}
44
45
  .row-label {{
46
+ width: 1fr;
45
47
  color: {TEXT};
46
48
  }}
47
49
  .pro-row .row-label {{
@@ -55,16 +57,17 @@ PrefsScreen {{
55
57
  width: 5;
56
58
  }}
57
59
  .opt-row {{
58
- height: 2;
60
+ height: auto;
59
61
  align: left middle;
62
+ margin: 0 0 1 0;
60
63
  }}
61
64
  .opt-row Switch {{
62
65
  margin: 0 1 0 0;
63
- width: 4;
66
+ width: 6;
64
67
  }}
65
68
  #fs-status {{
66
69
  color: {DIM};
67
- height: 1;
70
+ height: auto;
68
71
  margin: 1 0 0 0;
69
72
  }}
70
73
  #fs-status.full {{
@@ -0,0 +1,41 @@
1
+ """Rich success panel shown after mp login / mp login --add."""
2
+ from __future__ import annotations
3
+
4
+ from rich.console import Console
5
+ from rich.panel import Panel
6
+ from rich.text import Text
7
+
8
+ from methodproof.tui.theme import BORDER, DIM, GOLD, GREEN, TEXT
9
+
10
+ _CONSOLE = Console()
11
+
12
+
13
+ def run(display_name: str, email: str, account_count: int, added: bool) -> None:
14
+ """Render post-login success panel. Always shown regardless of ui_mode."""
15
+ body = Text()
16
+ body.append("✓ ", style=f"bold {GREEN}")
17
+
18
+ if added:
19
+ body.append("You have successfully added ", style=TEXT)
20
+ body.append(display_name, style=f"bold {TEXT}")
21
+ body.append(" authentication to your CLI.", style=TEXT)
22
+ else:
23
+ body.append("Signed in as ", style=TEXT)
24
+ body.append(display_name, style=f"bold {TEXT}")
25
+ body.append(".", style=TEXT)
26
+
27
+ if email and email != display_name:
28
+ body.append(f"\n {email}", style=DIM)
29
+
30
+ n = account_count
31
+ body.append(
32
+ f"\n\n {n} account{'s' if n != 1 else ''} on this device · ",
33
+ style=DIM,
34
+ )
35
+ body.append("mp switch", style=f"bold {GOLD}")
36
+ body.append(" to swap", style=DIM)
37
+
38
+ title = f"[{GOLD}] {'Added to CLI' if added else 'Logged In'} [/{GOLD}]"
39
+ _CONSOLE.print()
40
+ _CONSOLE.print(Panel(body, title=title, border_style=BORDER, padding=(0, 1)))
41
+ _CONSOLE.print()
@@ -19,15 +19,16 @@ def _tier_color(tier: str) -> str:
19
19
  return {"free": DIM, "basic": TEXT, "pro": GOLD, "team": PURPLE}.get(tier.lower(), TEXT)
20
20
 
21
21
 
22
- def _token_expiry(cfg: dict) -> str:
22
+ def _token_expiry(cfg: dict) -> tuple[str, str]:
23
+ """Return (text, style) for token expiry display."""
23
24
  last_auth = cfg.get("last_auth_at", 0)
24
25
  if not last_auth:
25
- return "unknown"
26
+ return "unknown", DIM
26
27
  age_h = (time.time() - last_auth) / 3600
27
28
  remaining_h = max(0, 24 - age_h)
28
29
  if remaining_h < 1:
29
- return f"[{RED}]expires soon[/{RED}]"
30
- return f"[{DIM}]expires in {int(remaining_h)}h[/{DIM}]"
30
+ return "expires soon", RED
31
+ return f"expires in {int(remaining_h)}h", DIM
31
32
 
32
33
 
33
34
  def run(cfg: dict) -> None:
@@ -52,7 +53,8 @@ def run(cfg: dict) -> None:
52
53
  acct_lines.append(" · ", style=DIM)
53
54
  acct_lines.append(tier, style=_tier_color(tier))
54
55
  acct_lines.append("\n")
55
- acct_lines.append(_token_expiry(cfg), style=DIM)
56
+ expiry_text, expiry_style = _token_expiry(cfg)
57
+ acct_lines.append(expiry_text, style=expiry_style)
56
58
 
57
59
  console.print(Panel(
58
60
  acct_lines,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "methodproof"
3
- version = "0.7.18"
3
+ version = "0.7.20"
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"]
@@ -27,7 +27,8 @@ def isolate_fs(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
27
27
  monkeypatch.setattr(config, "CONFIG", mp_dir / "config.json")
28
28
  monkeypatch.setattr(config, "DB_PATH", mp_dir / "methodproof.db")
29
29
  monkeypatch.setattr(config, "CMD_LOG", mp_dir / "commands.jsonl")
30
- (mp_dir / "config.json").write_text(json.dumps(dict(config._DEFAULTS), indent=2))
30
+ defaults = {**config._DEFAULTS, "ui_mode": False}
31
+ (mp_dir / "config.json").write_text(json.dumps(defaults, indent=2))
31
32
  monkeypatch.setattr(store, "_conn", None)
32
33
  store.init_db()
33
34
  return tmp_path
@@ -215,7 +215,7 @@ def test_login_happy_path(mock_req, mock_sleep, mock_browser, mock_key, mock_con
215
215
  assert cfg["account_id"] == "new-user"
216
216
  mock_browser.assert_called_once()
217
217
  out = capsys.readouterr().out
218
- assert "Logged in" in out
218
+ assert "Logged In" in out
219
219
 
220
220
 
221
221
  @patch("methodproof.sync._request")
@@ -98,7 +98,7 @@ def test_review_shows_breakdown(make_session, cli_args, capsys):
98
98
  out = capsys.readouterr().out
99
99
  assert "5" in out
100
100
  assert "events" in out
101
- assert "fields:" in out
101
+ assert "fields" in out
102
102
 
103
103
 
104
104
  # ── cmd_view ──
@@ -427,21 +427,11 @@ def test_consent_detailed_redaction_toggle():
427
427
  # ── cmd_init ──
428
428
 
429
429
 
430
- @patch("methodproof.integrity.has_keypair", return_value=True)
431
- @patch("methodproof.sync.sync_research_consent")
432
- @patch("methodproof.hooks.wrappers.install", return_value=[])
433
- @patch("methodproof.mcp.register_with_claude", return_value="registered")
434
- @patch("methodproof.hooks.install.install", return_value="installed")
435
- @patch("methodproof.hooks.openclaw_install.install", return_value="installed")
436
- @patch("methodproof.hook.install", return_value="hook installed")
437
- def test_init_first_run(mock_hook, mock_oc, mock_claude, mock_mcp, mock_wrap, mock_consent,
438
- mock_keypair, cli_args, capsys):
439
- # input() calls: auto-update, alias, local AI ports
440
- with patch("methodproof.cli._run_consent", return_value=config.load()) as mock_run_consent, \
441
- patch("builtins.input", side_effect=["N", "N", "N"]):
442
- cli.cmd_init(cli_args())
443
- mock_run_consent.assert_called_once()
444
- mock_hook.assert_called_once()
430
+ @patch("methodproof.tui.init.run")
431
+ def test_init_first_run(mock_tui_init, cli_args):
432
+ # First run: consent_acknowledged=False forces TUI regardless of ui_mode
433
+ cli.cmd_init(cli_args())
434
+ mock_tui_init.assert_called_once()
445
435
 
446
436
 
447
437
  @patch("methodproof.integrity.has_keypair", return_value=True)
@@ -450,7 +440,7 @@ def test_init_already_configured(mock_hook, mock_keypair, cli_args, capsys):
450
440
  cfg = config.load()
451
441
  cfg["consent_acknowledged"] = True
452
442
  config.save(cfg)
453
- # input() calls: auto-update, alias, local AI ports
454
- with patch("builtins.input", side_effect=["N", "N", "N"]):
443
+ # input() calls: auto-update, alias, ui-mode, local AI ports
444
+ with patch("builtins.input", side_effect=["N", "N", "N", "N"]):
455
445
  cli.cmd_init(cli_args())
456
446
  mock_hook.assert_called_once()
@@ -1,3 +0,0 @@
1
- # Auto-generated by code-review-graph — do not commit database files.
2
- # The graph.db contains absolute paths and code structure metadata.
3
- *
File without changes
File without changes
File without changes
File without changes