methodproof 0.7.19__tar.gz → 0.7.21__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.19 → methodproof-0.7.21}/CHANGELOG.md +8 -0
  2. {methodproof-0.7.19 → methodproof-0.7.21}/PKG-INFO +1 -1
  3. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/__init__.py +1 -1
  4. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/cli.py +57 -41
  5. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/tui/consent.py +1 -1
  6. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/tui/init.py +25 -12
  7. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/tui/status.py +7 -5
  8. {methodproof-0.7.19 → methodproof-0.7.21}/pyproject.toml +1 -1
  9. {methodproof-0.7.19 → methodproof-0.7.21}/tests/conftest.py +2 -1
  10. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_cli_auth.py +1 -1
  11. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_cli_share.py +1 -1
  12. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_cli_start.py +7 -17
  13. {methodproof-0.7.19 → methodproof-0.7.21}/.code-review-graph/.gitignore +0 -0
  14. {methodproof-0.7.19 → methodproof-0.7.21}/.code-review-graph/graph.db +0 -0
  15. {methodproof-0.7.19 → methodproof-0.7.21}/.github/workflows/ci.yml +0 -0
  16. {methodproof-0.7.19 → methodproof-0.7.21}/.gitignore +0 -0
  17. {methodproof-0.7.19 → methodproof-0.7.21}/LICENSE +0 -0
  18. {methodproof-0.7.19 → methodproof-0.7.21}/README.md +0 -0
  19. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/__main__.py +0 -0
  20. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/_daemon.py +0 -0
  21. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/agents/__init__.py +0 -0
  22. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/agents/base.py +0 -0
  23. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/agents/music.py +0 -0
  24. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/agents/terminal.py +0 -0
  25. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/agents/watcher.py +0 -0
  26. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/analysis.py +0 -0
  27. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/binding.py +0 -0
  28. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/bip39.py +0 -0
  29. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/bridge.py +0 -0
  30. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/config.py +0 -0
  31. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/crypto.py +0 -0
  32. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/e2e.py +0 -0
  33. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/graph.py +0 -0
  34. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hook.py +0 -0
  35. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/__init__.py +0 -0
  36. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/claude_code.py +0 -0
  37. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/claude_code.sh +0 -0
  38. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/cline_hook.sh +0 -0
  39. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/codex_hook.sh +0 -0
  40. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/gemini_hook.sh +0 -0
  41. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/install.py +0 -0
  42. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/kiro_hook.sh +0 -0
  43. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/mcp_register.py +0 -0
  44. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/openclaw/HOOK.md +0 -0
  45. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/openclaw/handler.ts +0 -0
  46. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/openclaw_install.py +0 -0
  47. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/opencode_plugin.js +0 -0
  48. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/hooks/wrappers.py +0 -0
  49. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/integrity.py +0 -0
  50. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/kdf.py +0 -0
  51. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/keychain.py +0 -0
  52. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/live.py +0 -0
  53. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/lock.py +0 -0
  54. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/mcp.py +0 -0
  55. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/migrate_db.py +0 -0
  56. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/proxy.py +0 -0
  57. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/proxy_daemon.py +0 -0
  58. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/repos.py +0 -0
  59. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/skills/methodproof/SKILL.md +0 -0
  60. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/store.py +0 -0
  61. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/sync.py +0 -0
  62. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/tui/__init__.py +0 -0
  63. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/tui/log.py +0 -0
  64. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/tui/login_success.py +0 -0
  65. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/tui/review.py +0 -0
  66. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/tui/start.py +0 -0
  67. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/tui/theme.py +0 -0
  68. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/viewer.py +0 -0
  69. {methodproof-0.7.19 → methodproof-0.7.21}/methodproof/wordlist.py +0 -0
  70. {methodproof-0.7.19 → methodproof-0.7.21}/test_windows_compat.py +0 -0
  71. {methodproof-0.7.19 → methodproof-0.7.21}/tests/__init__.py +0 -0
  72. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_analysis.py +0 -0
  73. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_cli_config.py +0 -0
  74. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_cli_helpers.py +0 -0
  75. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_cli_session.py +0 -0
  76. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_cli_update.py +0 -0
  77. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_e2e_integration.py +0 -0
  78. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_graph.py +0 -0
  79. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_hooks.py +0 -0
  80. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_live.py +0 -0
  81. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_openclaw_hooks.py +0 -0
  82. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_profiles.py +0 -0
  83. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_security.py +0 -0
  84. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_store.py +0 -0
  85. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_sync.py +0 -0
  86. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_viewer.py +0 -0
  87. {methodproof-0.7.19 → methodproof-0.7.21}/tests/test_wrappers.py +0 -0
  88. {methodproof-0.7.19 → methodproof-0.7.21}/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.19
3
+ Version: 0.7.21
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.19"
3
+ __version__ = "0.7.21"
@@ -334,62 +334,74 @@ def _run_consent_detailed(cfg: dict) -> dict:
334
334
  def cmd_init(args: argparse.Namespace) -> None:
335
335
  config.ensure_dirs()
336
336
  cfg = config.load()
337
+ yes = getattr(args, "yes", False)
337
338
  if getattr(args, "force", False):
338
339
  for key in ("consent_acknowledged", "auto_update_offered", "alias_offered", "local_ai_ports_offered", "ui_mode_offered"):
339
340
  cfg.pop(key, None)
340
341
  config.save(cfg)
341
342
 
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
-
350
343
  if not cfg.get("consent_acknowledged"):
351
- cfg = _run_consent(cfg)
352
- config.save(cfg)
353
- if cfg.get("token"):
354
- cfg["_pending_research_sync"] = True
344
+ if yes:
345
+ from methodproof import config as cfg_mod
346
+ cfg["capture"] = dict(cfg_mod._DEFAULTS["capture"])
347
+ cfg["publish_redact"] = dict(cfg_mod._DEFAULTS["publish_redact"])
348
+ cfg["consent_acknowledged"] = True
355
349
  config.save(cfg)
356
- from methodproof.sync import sync_research_consent
357
- sync_research_consent(cfg["token"], cfg["api_url"])
358
- print()
350
+ print("Capture: all defaults accepted")
351
+ else:
352
+ cfg = _run_consent(cfg)
353
+ config.save(cfg)
354
+ if cfg.get("token"):
355
+ cfg["_pending_research_sync"] = True
356
+ config.save(cfg)
357
+ from methodproof.sync import sync_research_consent
358
+ sync_research_consent(cfg["token"], cfg["api_url"])
359
+ print()
359
360
 
360
361
  capture = cfg.get("capture", {})
361
362
  store.init_db()
362
363
 
363
364
  # Offer auto-update (recommended)
364
365
  if not cfg.get("auto_update_offered"):
365
- answer = input("Enable auto-update (recommended)? [Y/n]: ").strip().lower()
366
- cfg["auto_update_offered"] = True
367
- cfg["auto_update"] = answer != "n"
368
- print(f"Auto-update: {'ON — updates install before each session' if cfg['auto_update'] else 'OFF — toggle with: mp update --auto'}")
366
+ if yes:
367
+ cfg["auto_update_offered"] = True
368
+ cfg["auto_update"] = True
369
+ print("Auto-update: ON")
370
+ else:
371
+ answer = input("Enable auto-update (recommended)? [Y/n]: ").strip().lower()
372
+ cfg["auto_update_offered"] = True
373
+ cfg["auto_update"] = answer != "n"
374
+ print(f"Auto-update: {'ON — updates install before each session' if cfg['auto_update'] else 'OFF — toggle with: mp update --auto'}")
369
375
  config.save(cfg)
370
376
 
371
377
  # Offer mp alias
372
378
  if not cfg.get("alias_offered"):
373
- answer = input("Install `mp` as a shorthand alias? [Y/n]: ").strip().lower()
374
- cfg["alias_offered"] = True
375
- if answer != "n":
379
+ if yes:
376
380
  _install_alias()
381
+ cfg["alias_offered"] = True
377
382
  cfg["alias_installed"] = True
378
383
  print("Alias: mp -> methodproof")
379
384
  else:
380
- print("Alias: skipped")
385
+ answer = input("Install `mp` as a shorthand alias? [Y/n]: ").strip().lower()
386
+ cfg["alias_offered"] = True
387
+ if answer != "n":
388
+ _install_alias()
389
+ cfg["alias_installed"] = True
390
+ print("Alias: mp -> methodproof")
391
+ else:
392
+ print("Alias: skipped")
381
393
  config.save(cfg)
382
394
 
383
395
  # Offer classic CLI mode (TUI is default)
384
396
  if not cfg.get("ui_mode_offered"):
385
397
  cfg["ui_mode_offered"] = True
386
- answer = input("Prefer classic terminal output instead of the rich TUI? [y/N]: ").strip().lower()
387
- if answer == "y":
388
- cfg["ui_mode"] = False
389
- print("Output mode: classic (toggle anytime with: mp ui on/off)")
390
- else:
398
+ if yes:
391
399
  cfg["ui_mode"] = True
392
- print("Output mode: rich TUI (toggle anytime with: mp ui off)")
400
+ print("Output mode: rich TUI")
401
+ else:
402
+ answer = input("Prefer classic terminal output instead of the rich TUI? [y/N]: ").strip().lower()
403
+ cfg["ui_mode"] = answer != "y"
404
+ print(f"Output mode: {'classic (toggle anytime with: mp ui on/off)' if not cfg['ui_mode'] else 'rich TUI (toggle anytime with: mp ui off)'}")
393
405
  config.save(cfg)
394
406
 
395
407
  # Shell hook — needed for terminal commands
@@ -440,19 +452,23 @@ def cmd_init(args: argparse.Namespace) -> None:
440
452
 
441
453
  # Local AI ports — capture traffic from local LLM servers
442
454
  if ai_enabled and not cfg.get("local_ai_ports_offered"):
443
- answer = input("Run any local AI models (Ollama, LM Studio, vLLM, etc.)? [y/N]: ").strip().lower()
444
455
  cfg["local_ai_ports_offered"] = True
445
- if answer == "y":
446
- raw = input("Enter ports (comma-separated, e.g. 8080,5000,7860): ").strip()
447
- ports = [int(p.strip()) for p in raw.split(",") if p.strip().isdigit()]
448
- cfg["local_ai_ports"] = ports
449
- if ports:
450
- print(f"Local AI ports: {', '.join(str(p) for p in ports)} (proxy will decode these)")
451
- else:
452
- print("Local AI ports: none added")
453
- else:
456
+ if yes:
454
457
  cfg["local_ai_ports"] = []
455
- print("Local AI ports: skipped (built-in: Ollama 11434, Jan 1234)")
458
+ print("Local AI ports: using defaults (Ollama 11434, Jan 1234)")
459
+ else:
460
+ answer = input("Run any local AI models (Ollama, LM Studio, vLLM, etc.)? [y/N]: ").strip().lower()
461
+ if answer == "y":
462
+ raw = input("Enter ports (comma-separated, e.g. 8080,5000,7860): ").strip()
463
+ ports = [int(p.strip()) for p in raw.split(",") if p.strip().isdigit()]
464
+ cfg["local_ai_ports"] = ports
465
+ if ports:
466
+ print(f"Local AI ports: {', '.join(str(p) for p in ports)} (proxy will decode these)")
467
+ else:
468
+ print("Local AI ports: none added")
469
+ else:
470
+ cfg["local_ai_ports"] = []
471
+ print("Local AI ports: skipped (built-in: Ollama 11434, Jan 1234)")
456
472
  config.save(cfg)
457
473
 
458
474
  # Signing keypair for attestation
@@ -1398,7 +1414,6 @@ def cmd_log(args: argparse.Namespace) -> None:
1398
1414
  return
1399
1415
  print(f"Found {len(empty)} empty session{'s' if len(empty) != 1 else ''}:")
1400
1416
  for s in empty:
1401
- from datetime import datetime, UTC
1402
1417
  dt = datetime.fromtimestamp(s["created_at"], tz=UTC).strftime("%Y-%m-%d %H:%M")
1403
1418
  print(f" {s['id'][:8]} {dt} {s.get('watch_dir', '?')}")
1404
1419
  answer = input(f"\nDelete {len(empty)} empty session{'s' if len(empty) != 1 else ''}? [y/N]: ").strip().lower()
@@ -2116,6 +2131,7 @@ def main() -> None:
2116
2131
 
2117
2132
  s = sub.add_parser("init", help="Install shell hook")
2118
2133
  s.add_argument("--force", action="store_true", help="Re-run all setup prompts from scratch")
2134
+ s.add_argument("-y", "--yes", action="store_true", help="Accept all defaults, no prompts")
2119
2135
  _add_ui_flags(s)
2120
2136
  sub.add_parser("shell-hook", help="Print shell hook for eval (activates without restart)")
2121
2137
  s = sub.add_parser("start", help="Start recording")
@@ -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};
@@ -8,7 +8,7 @@ from textual.screen import Screen
8
8
  from textual.widgets import Button, Footer, Header, Label, RichLog, Rule, Static, Switch
9
9
 
10
10
  from methodproof import config as cfg_mod
11
- from methodproof.tui.theme import BASE_CSS, BORDER, DIM, GOLD, GREEN, PURPLE, RED, TEXT
11
+ from methodproof.tui.theme import BASE_CSS, BG, BORDER, DIM, GOLD, GREEN, PURPLE, RED, TEXT
12
12
 
13
13
  _REDACTABLE = [
14
14
  ("command_output", "Terminal output"),
@@ -33,15 +33,31 @@ PrefsScreen {{
33
33
  color: {DIM};
34
34
  margin: 0 0 1 0;
35
35
  }}
36
+ Switch {{
37
+ border: none;
38
+ height: 1;
39
+ width: 10;
40
+ background: {BORDER};
41
+ padding: 0 1;
42
+ }}
43
+ Switch.-on {{
44
+ background: {GREEN};
45
+ }}
46
+ Switch .switch--slider {{
47
+ color: {TEXT};
48
+ background: {DIM};
49
+ }}
50
+ Switch.-on .switch--slider {{
51
+ color: {BG};
52
+ background: {TEXT};
53
+ }}
36
54
  .toggle-row {{
37
- height: 2;
55
+ height: 1;
38
56
  align: left middle;
39
- }}
40
- .toggle-row Switch {{
41
- margin: 0 1 0 0;
42
- width: 4;
57
+ margin: 0 0 1 0;
43
58
  }}
44
59
  .row-label {{
60
+ width: 1fr;
45
61
  color: {TEXT};
46
62
  }}
47
63
  .pro-row .row-label {{
@@ -55,16 +71,13 @@ PrefsScreen {{
55
71
  width: 5;
56
72
  }}
57
73
  .opt-row {{
58
- height: 2;
74
+ height: 1;
59
75
  align: left middle;
60
- }}
61
- .opt-row Switch {{
62
- margin: 0 1 0 0;
63
- width: 4;
76
+ margin: 0 0 1 0;
64
77
  }}
65
78
  #fs-status {{
66
79
  color: {DIM};
67
- height: 1;
80
+ height: auto;
68
81
  margin: 1 0 0 0;
69
82
  }}
70
83
  #fs-status.full {{
@@ -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.19"
3
+ version = "0.7.21"
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()
File without changes
File without changes
File without changes
File without changes