methodproof 0.7.28__tar.gz → 0.7.29__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 (86) hide show
  1. {methodproof-0.7.28 → methodproof-0.7.29}/CHANGELOG.md +5 -0
  2. {methodproof-0.7.28 → methodproof-0.7.29}/PKG-INFO +1 -1
  3. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/cli.py +61 -25
  4. {methodproof-0.7.28 → methodproof-0.7.29}/pyproject.toml +1 -1
  5. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_cli_start.py +3 -2
  6. {methodproof-0.7.28 → methodproof-0.7.29}/.github/workflows/ci.yml +0 -0
  7. {methodproof-0.7.28 → methodproof-0.7.29}/.gitignore +0 -0
  8. {methodproof-0.7.28 → methodproof-0.7.29}/LICENSE +0 -0
  9. {methodproof-0.7.28 → methodproof-0.7.29}/README.md +0 -0
  10. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/__init__.py +0 -0
  11. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/__main__.py +0 -0
  12. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/_daemon.py +0 -0
  13. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/agents/__init__.py +0 -0
  14. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/agents/base.py +0 -0
  15. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/agents/music.py +0 -0
  16. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/agents/terminal.py +0 -0
  17. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/agents/watcher.py +0 -0
  18. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/analysis.py +0 -0
  19. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/binding.py +0 -0
  20. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/bip39.py +0 -0
  21. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/bridge.py +0 -0
  22. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/config.py +0 -0
  23. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/crypto.py +0 -0
  24. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/e2e.py +0 -0
  25. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/graph.py +0 -0
  26. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hook.py +0 -0
  27. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/__init__.py +0 -0
  28. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/claude_code.py +0 -0
  29. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/claude_code.sh +0 -0
  30. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/cline_hook.sh +0 -0
  31. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/codex_hook.sh +0 -0
  32. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/gemini_hook.sh +0 -0
  33. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/install.py +0 -0
  34. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/kiro_hook.sh +0 -0
  35. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/mcp_register.py +0 -0
  36. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/openclaw/HOOK.md +0 -0
  37. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/openclaw/handler.ts +0 -0
  38. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/openclaw_install.py +0 -0
  39. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/opencode_plugin.js +0 -0
  40. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/hooks/wrappers.py +0 -0
  41. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/integrity.py +0 -0
  42. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/kdf.py +0 -0
  43. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/keychain.py +0 -0
  44. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/live.py +0 -0
  45. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/lock.py +0 -0
  46. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/mcp.py +0 -0
  47. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/migrate_db.py +0 -0
  48. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/proxy.py +0 -0
  49. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/proxy_daemon.py +0 -0
  50. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/repos.py +0 -0
  51. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/skills/methodproof/SKILL.md +0 -0
  52. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/store.py +0 -0
  53. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/sync.py +0 -0
  54. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/tui/__init__.py +0 -0
  55. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/tui/consent.py +0 -0
  56. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/tui/init.py +0 -0
  57. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/tui/log.py +0 -0
  58. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/tui/login_success.py +0 -0
  59. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/tui/review.py +0 -0
  60. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/tui/start.py +0 -0
  61. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/tui/status.py +0 -0
  62. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/tui/theme.py +0 -0
  63. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/viewer.py +0 -0
  64. {methodproof-0.7.28 → methodproof-0.7.29}/methodproof/wordlist.py +0 -0
  65. {methodproof-0.7.28 → methodproof-0.7.29}/test_windows_compat.py +0 -0
  66. {methodproof-0.7.28 → methodproof-0.7.29}/tests/__init__.py +0 -0
  67. {methodproof-0.7.28 → methodproof-0.7.29}/tests/conftest.py +0 -0
  68. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_analysis.py +0 -0
  69. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_cli_auth.py +0 -0
  70. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_cli_config.py +0 -0
  71. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_cli_helpers.py +0 -0
  72. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_cli_session.py +0 -0
  73. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_cli_share.py +0 -0
  74. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_cli_update.py +0 -0
  75. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_e2e_integration.py +0 -0
  76. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_graph.py +0 -0
  77. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_hooks.py +0 -0
  78. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_live.py +0 -0
  79. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_openclaw_hooks.py +0 -0
  80. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_profiles.py +0 -0
  81. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_security.py +0 -0
  82. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_store.py +0 -0
  83. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_sync.py +0 -0
  84. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_viewer.py +0 -0
  85. {methodproof-0.7.28 → methodproof-0.7.29}/tests/test_wrappers.py +0 -0
  86. {methodproof-0.7.28 → methodproof-0.7.29}/uv.lock +0 -0
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.29] — 2026-04-12
4
+
5
+ ### Changed
6
+ - **Journal mode output is now tier-aware** — Pro/Team/Admin/Superadmin users see "Unlimited journal entries (Pro plan)" everywhere journal credits are displayed (`mp journal on`, `mp journal status`, `mp start --journal`, session mode line, consent flow). Basic users get a live credit count fetched from `GET /auth/me` (falls back to local cache if offline). Free users see their local credit count as before. Credit deduction on `--journal` is skipped for unlimited tiers.
7
+
3
8
  ## [0.7.28] — 2026-04-12
4
9
 
5
10
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: methodproof
3
- Version: 0.7.28
3
+ Version: 0.7.29
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
@@ -156,13 +156,50 @@ def _install_alias() -> None:
156
156
  f.write(alias)
157
157
 
158
158
 
159
- def _print_journal_intro(credits: int) -> None:
160
- """Show journal mode introduction with remaining credits."""
159
+ _PRO_TIERS = {"pro", "team", "admin", "superadmin"}
160
+
161
+
162
+ def _journal_entitlement(cfg: dict) -> str | int:
163
+ """Return 'unlimited' for Pro+ users, or an int credit count for others.
164
+
165
+ For Basic users, fetches live credit count from the platform; falls back
166
+ to the locally cached value if the request fails (e.g. offline).
167
+ """
168
+ token = cfg.get("token", "")
169
+ claims = _decode_jwt_claims(token) if token else {}
170
+ account_type = claims.get("account_type", "free").lower()
171
+
172
+ if account_type in _PRO_TIERS:
173
+ return "unlimited"
174
+
175
+ if account_type == "basic":
176
+ try:
177
+ profile = _request("GET", "/auth/me", cfg.get("api_url", config.LOCAL_API_URL), token)
178
+ server_credits = profile.get("journal_credits")
179
+ if server_credits is not None:
180
+ cfg["journal_credits"] = int(server_credits)
181
+ config.save(cfg)
182
+ except Exception:
183
+ pass # offline — use local cache
184
+
185
+ return int(cfg.get("journal_credits", 0))
186
+
187
+
188
+ def _journal_credits_line(entitlement: str | int) -> str:
189
+ if entitlement == "unlimited":
190
+ return "Unlimited journal entries (Pro plan)"
191
+ n = int(entitlement)
192
+ label = f"{n} free journal credit{'s' if n != 1 else ''}"
193
+ return f"{label} (up to {config.FREE_JOURNAL_MAX_HOURS}h per session)"
194
+
195
+
196
+ def _print_journal_intro(cfg: dict) -> None:
197
+ """Show journal mode introduction with tier-aware credit status."""
198
+ entitlement = _journal_entitlement(cfg)
161
199
  print(" ┌─────────────────────────────────────────────────────┐")
162
200
  print(" │ Journal Mode — full content capture │")
163
201
  print(" └─────────────────────────────────────────────────────┘")
164
- print(f" You have {credits} free journal credit{'s' if credits != 1 else ''} "
165
- f"(sessions up to {config.FREE_JOURNAL_MAX_HOURS}h each).")
202
+ print(f" {_journal_credits_line(entitlement)}")
166
203
  print()
167
204
  print(" By default, MethodProof captures structural metadata only —")
168
205
  print(" file paths, line counts, timing, tool names. Journal mode")
@@ -199,9 +236,8 @@ def _run_consent(cfg: dict) -> dict:
199
236
  cfg["research_consent"] = False
200
237
  cfg["publish_redact"] = dict(config._DEFAULTS["publish_redact"])
201
238
  cfg["consent_acknowledged"] = True
202
- credits = cfg.get("journal_credits", config._DEFAULTS["journal_credits"])
203
239
  print("\n Minimal capture enabled (file changes + git commits).\n")
204
- _print_journal_intro(credits)
240
+ _print_journal_intro(cfg)
205
241
  print(" Customize anytime: `methodproof consent`\n")
206
242
  return cfg
207
243
 
@@ -210,9 +246,8 @@ def _run_consent(cfg: dict) -> dict:
210
246
  cfg["research_consent"] = False
211
247
  cfg["publish_redact"] = dict(config._DEFAULTS["publish_redact"])
212
248
  cfg["consent_acknowledged"] = True
213
- credits = cfg.get("journal_credits", config._DEFAULTS["journal_credits"])
214
249
  print(f"\n {_rainbow('Full Spectrum')} enabled — free live streaming unlocked.\n")
215
- _print_journal_intro(credits)
250
+ _print_journal_intro(cfg)
216
251
  print(" Customize anytime: `methodproof consent`\n")
217
252
  return cfg
218
253
 
@@ -873,7 +908,8 @@ def cmd_journal(args: argparse.Namespace) -> None:
873
908
  """Journal mode — full content capture."""
874
909
  subcmd = getattr(args, "journal_cmd", None)
875
910
  cfg = config.load()
876
- credits = cfg.get("journal_credits", 0)
911
+ entitlement = _journal_entitlement(cfg)
912
+ is_unlimited = entitlement == "unlimited"
877
913
 
878
914
  if subcmd == "on":
879
915
  print("Journal Mode — Full Content Capture\n")
@@ -884,12 +920,9 @@ def cmd_journal(args: argparse.Namespace) -> None:
884
920
  print(" • Terminal output (not just commands)")
885
921
  print(" • Tool call parameters and results\n")
886
922
  print("All content is encrypted (AES-256-GCM) and subject to your consent settings.\n")
887
- if credits > 0:
888
- print(f"You have {credits} free journal credit{'s' if credits != 1 else ''} "
889
- f"(sessions up to {config.FREE_JOURNAL_MAX_HOURS}h each).")
890
- print("After credits are used, journal mode requires a Pro plan.\n")
891
- else:
892
- print("Journal mode requires a Pro plan (or free credits if available).\n")
923
+ print(f" {_journal_credits_line(entitlement)}\n")
924
+ if not is_unlimited and int(entitlement) == 0:
925
+ print("You have no credits remaining. Upgrade to Pro for unlimited journal entries.\n")
893
926
  answer = input("Enable journal mode? [y/N] ").strip().lower()
894
927
  if answer != "y":
895
928
  print("Journal mode not enabled.")
@@ -913,9 +946,7 @@ def cmd_journal(args: argparse.Namespace) -> None:
913
946
  print("Journal mode: OFF (structural only)")
914
947
  print(" Only metadata captured: lengths, types, timing, file paths.")
915
948
  print(" Enable with: methodproof journal on")
916
- if credits > 0:
917
- print(f" Free journal credits: {credits} "
918
- f"(up to {config.FREE_JOURNAL_MAX_HOURS}h per session)")
949
+ print(f" {_journal_credits_line(entitlement)}")
919
950
 
920
951
  else:
921
952
  print("Usage: methodproof journal [on|off|status]")
@@ -1196,12 +1227,17 @@ def cmd_start(args: argparse.Namespace) -> None:
1196
1227
  # Journal mode
1197
1228
  if getattr(args, "journal", False):
1198
1229
  cfg["journal_mode"] = True
1199
- credits = cfg.get("journal_credits", 0)
1200
- if credits > 0:
1201
- cfg["journal_credits"] = credits - 1
1202
- print(f"Journal mode ON (free credit used — {credits - 1} remaining, {config.FREE_JOURNAL_MAX_HOURS}h cap).")
1230
+ entitlement = _journal_entitlement(cfg)
1231
+ if entitlement == "unlimited":
1232
+ print("Journal mode ON (full content capture).")
1203
1233
  else:
1204
- print("Journal mode ON for this session (full content capture).")
1234
+ credits = int(entitlement)
1235
+ if credits > 0:
1236
+ cfg["journal_credits"] = credits - 1
1237
+ remaining = credits - 1
1238
+ print(f"Journal mode ON (free credit used — {remaining} remaining, {config.FREE_JOURNAL_MAX_HOURS}h cap).")
1239
+ else:
1240
+ print("Journal mode ON for this session (full content capture).")
1205
1241
  config.save(cfg)
1206
1242
 
1207
1243
  # E2E mode
@@ -1558,8 +1594,8 @@ def cmd_status(args: argparse.Namespace) -> None:
1558
1594
  # Modes
1559
1595
  modes = []
1560
1596
  if cfg.get("journal_mode"):
1561
- credits = cfg.get("journal_credits", 0)
1562
- modes.append(f"journal ({credits} credits)")
1597
+ ent = _journal_entitlement(cfg)
1598
+ modes.append("journal (unlimited)" if ent == "unlimited" else f"journal ({ent} credits)")
1563
1599
  if cfg.get("e2e_mode"):
1564
1600
  fp = cfg.get("e2e_fingerprint", "")
1565
1601
  modes.append(f"e2e ({fp[:8]})" if fp else "e2e")
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "methodproof"
3
- version = "0.7.28"
3
+ version = "0.7.29"
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"]
@@ -118,9 +118,10 @@ def test_start_creates_session_and_spawns_daemon(mock_sleep, mock_popen, mock_re
118
118
  @patch("subprocess.Popen")
119
119
  @patch("time.sleep")
120
120
  def test_start_journal_decrements_credits(mock_sleep, mock_popen, mock_req, mock_repo, mock_update, mock_auth,
121
- mock_hook, mock_alive, logged_in_cfg, cli_args,
121
+ mock_hook, mock_alive, logged_in_cfg, fake_jwt, cli_args,
122
122
  monkeypatch, capsys):
123
- cfg = logged_in_cfg(account_id="acct-1")
123
+ # Use free-tier JWT — pro/team skips credit deduction (unlimited)
124
+ cfg = logged_in_cfg(account_id="acct-1", token=fake_jwt(user_id="acct-1", account_type="free"))
124
125
  cfg["journal_credits"] = 2
125
126
  config.save(cfg)
126
127
  pidfile = config.DIR / "methodproof.pid"
File without changes
File without changes
File without changes
File without changes