aline-ai 0.5.4__py3-none-any.whl → 0.5.6__py3-none-any.whl

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 (82) hide show
  1. {aline_ai-0.5.4.dist-info → aline_ai-0.5.6.dist-info}/METADATA +1 -1
  2. aline_ai-0.5.6.dist-info/RECORD +95 -0
  3. realign/__init__.py +1 -1
  4. realign/adapters/antigravity.py +28 -20
  5. realign/adapters/base.py +46 -50
  6. realign/adapters/claude.py +14 -14
  7. realign/adapters/codex.py +7 -7
  8. realign/adapters/gemini.py +11 -11
  9. realign/adapters/registry.py +14 -10
  10. realign/claude_detector.py +2 -2
  11. realign/claude_hooks/__init__.py +3 -3
  12. realign/claude_hooks/permission_request_hook_installer.py +31 -32
  13. realign/claude_hooks/stop_hook.py +4 -1
  14. realign/claude_hooks/stop_hook_installer.py +30 -31
  15. realign/cli.py +23 -4
  16. realign/codex_detector.py +11 -11
  17. realign/commands/add.py +88 -65
  18. realign/commands/config.py +3 -12
  19. realign/commands/context.py +3 -1
  20. realign/commands/export_shares.py +86 -127
  21. realign/commands/import_shares.py +145 -155
  22. realign/commands/init.py +166 -30
  23. realign/commands/restore.py +18 -6
  24. realign/commands/search.py +14 -42
  25. realign/commands/upgrade.py +155 -11
  26. realign/commands/watcher.py +98 -219
  27. realign/commands/worker.py +29 -6
  28. realign/config.py +25 -20
  29. realign/context.py +1 -3
  30. realign/dashboard/app.py +34 -24
  31. realign/dashboard/screens/__init__.py +10 -1
  32. realign/dashboard/screens/create_agent.py +244 -0
  33. realign/dashboard/screens/create_event.py +3 -1
  34. realign/dashboard/screens/event_detail.py +14 -6
  35. realign/dashboard/screens/help_screen.py +114 -0
  36. realign/dashboard/screens/session_detail.py +3 -1
  37. realign/dashboard/screens/share_import.py +7 -3
  38. realign/dashboard/tmux_manager.py +54 -9
  39. realign/dashboard/widgets/config_panel.py +85 -1
  40. realign/dashboard/widgets/events_table.py +314 -70
  41. realign/dashboard/widgets/header.py +2 -1
  42. realign/dashboard/widgets/search_panel.py +37 -27
  43. realign/dashboard/widgets/sessions_table.py +404 -85
  44. realign/dashboard/widgets/terminal_panel.py +155 -175
  45. realign/dashboard/widgets/watcher_panel.py +6 -2
  46. realign/dashboard/widgets/worker_panel.py +10 -1
  47. realign/db/__init__.py +1 -1
  48. realign/db/base.py +5 -15
  49. realign/db/locks.py +0 -1
  50. realign/db/migration.py +82 -76
  51. realign/db/schema.py +2 -6
  52. realign/db/sqlite_db.py +23 -41
  53. realign/events/__init__.py +0 -1
  54. realign/events/event_summarizer.py +27 -15
  55. realign/events/session_summarizer.py +29 -15
  56. realign/file_lock.py +1 -0
  57. realign/hooks.py +150 -60
  58. realign/logging_config.py +12 -15
  59. realign/mcp_server.py +30 -51
  60. realign/mcp_watcher.py +0 -1
  61. realign/models/event.py +29 -20
  62. realign/prompts/__init__.py +7 -7
  63. realign/prompts/presets.py +15 -11
  64. realign/redactor.py +99 -59
  65. realign/triggers/__init__.py +9 -9
  66. realign/triggers/antigravity_trigger.py +30 -28
  67. realign/triggers/base.py +4 -3
  68. realign/triggers/claude_trigger.py +104 -85
  69. realign/triggers/codex_trigger.py +15 -5
  70. realign/triggers/gemini_trigger.py +57 -47
  71. realign/triggers/next_turn_trigger.py +3 -1
  72. realign/triggers/registry.py +6 -2
  73. realign/triggers/turn_status.py +3 -1
  74. realign/watcher_core.py +306 -131
  75. realign/watcher_daemon.py +8 -8
  76. realign/worker_core.py +3 -1
  77. realign/worker_daemon.py +3 -1
  78. aline_ai-0.5.4.dist-info/RECORD +0 -93
  79. {aline_ai-0.5.4.dist-info → aline_ai-0.5.6.dist-info}/WHEEL +0 -0
  80. {aline_ai-0.5.4.dist-info → aline_ai-0.5.6.dist-info}/entry_points.txt +0 -0
  81. {aline_ai-0.5.4.dist-info → aline_ai-0.5.6.dist-info}/licenses/LICENSE +0 -0
  82. {aline_ai-0.5.4.dist-info → aline_ai-0.5.6.dist-info}/top_level.txt +0 -0
@@ -154,7 +154,7 @@ def _find_events_by_uuid(selector: str, events: List) -> List[int]:
154
154
 
155
155
  for i, event in enumerate(events, 1):
156
156
  # Support both 'id' and 'event_id' attributes
157
- eid = getattr(event, 'event_id', None) or getattr(event, 'id', None) or ""
157
+ eid = getattr(event, "event_id", None) or getattr(event, "id", None) or ""
158
158
  eid = eid.lower()
159
159
  if eid.startswith(part) or eid == part:
160
160
  if i not in indices:
@@ -452,9 +452,7 @@ def _compact_message_content(content: Any, config: ExportCompactionConfig) -> An
452
452
  cmd = input_obj.get("command")
453
453
  if isinstance(cmd, str):
454
454
  input_obj = dict(input_obj)
455
- input_obj["command"] = _truncate_string(
456
- cmd, config.max_tool_command_chars
457
- )
455
+ input_obj["command"] = _truncate_string(cmd, config.max_tool_command_chars)
458
456
  block = dict(block)
459
457
  block["input"] = input_obj
460
458
  compacted.append(block)
@@ -527,9 +525,7 @@ def _compact_jsonl_record(
527
525
  if not isinstance(item, dict):
528
526
  continue
529
527
  t = item.get("type")
530
- if t in ("input_text", "output_text") and isinstance(
531
- item.get("text"), str
532
- ):
528
+ if t in ("input_text", "output_text") and isinstance(item.get("text"), str):
533
529
  texts.append(item["text"])
534
530
  compact_message: Dict[str, Any] = {}
535
531
  if isinstance(role, str):
@@ -573,7 +569,11 @@ def _compact_jsonl_record(
573
569
  if isinstance(role, str):
574
570
  compact_message["role"] = role
575
571
  compacted_content = _compact_message_content(content, config)
576
- if isinstance(compacted_content, str) and config.omit_thinking_blocks and compacted_content.startswith("[Thinking]"):
572
+ if (
573
+ isinstance(compacted_content, str)
574
+ and config.omit_thinking_blocks
575
+ and compacted_content.startswith("[Thinking]")
576
+ ):
577
577
  return None
578
578
  compact_message["content"] = compacted_content
579
579
 
@@ -651,9 +651,7 @@ def _build_export_size_report(
651
651
  content = msg.get("content")
652
652
  if isinstance(content, list):
653
653
  for block in content:
654
- if isinstance(block, dict) and isinstance(
655
- block.get("type"), str
656
- ):
654
+ if isinstance(block, dict) and isinstance(block.get("type"), str):
657
655
  block_types.append(block["type"])
658
656
  if block.get("type") == "tool_use":
659
657
  tool_use_id = block.get("id")
@@ -669,7 +667,11 @@ def _build_export_size_report(
669
667
  command = input_obj["command"]
670
668
  if isinstance(input_obj, dict):
671
669
  input_keys = sorted(
672
- [k for k in input_obj.keys() if isinstance(k, str)]
670
+ [
671
+ k
672
+ for k in input_obj.keys()
673
+ if isinstance(k, str)
674
+ ]
673
675
  )
674
676
  for k, v in input_obj.items():
675
677
  if isinstance(k, str) and isinstance(v, str):
@@ -681,20 +683,24 @@ def _build_export_size_report(
681
683
  break
682
684
  tool_use_index[tool_use_id] = {
683
685
  "tool_name": block.get("name"),
684
- "description": input_obj.get("description")
685
- if isinstance(input_obj, dict)
686
- else None,
686
+ "description": (
687
+ input_obj.get("description")
688
+ if isinstance(input_obj, dict)
689
+ else None
690
+ ),
687
691
  "input_keys": input_keys,
688
692
  "input_string_lengths": input_string_lengths,
689
693
  "path_hint": input_path_hint,
690
- "command_snippet": _truncate_string(
691
- command, 200
692
- )
693
- if isinstance(command, str)
694
- else None,
695
- "command_chars": len(command)
696
- if isinstance(command, str)
697
- else None,
694
+ "command_snippet": (
695
+ _truncate_string(command, 200)
696
+ if isinstance(command, str)
697
+ else None
698
+ ),
699
+ "command_chars": (
700
+ len(command)
701
+ if isinstance(command, str)
702
+ else None
703
+ ),
698
704
  }
699
705
  if block.get("type") == "tool_result":
700
706
  tool_use_id = block.get("tool_use_id")
@@ -718,11 +724,7 @@ def _build_export_size_report(
718
724
  "session_id": session.session_id,
719
725
  "turn_id": turn.id,
720
726
  "turn_number": turn.turn_number,
721
- "timestamp": (
722
- turn.timestamp.isoformat()
723
- if turn.timestamp
724
- else None
725
- ),
727
+ "timestamp": (turn.timestamp.isoformat() if turn.timestamp else None),
726
728
  "record_type": kind,
727
729
  "role": role,
728
730
  "content_block_types": sorted(set(block_types))[:6],
@@ -780,9 +782,7 @@ def build_enhanced_conversation_data(
780
782
  Dictionary with v2.0 structure including event metadata, session metadata,
781
783
  and structured turns with messages
782
784
  """
783
- logger.info(
784
- f"Building enhanced conversation data (v2.0) for event: {selected_event.title}"
785
- )
785
+ logger.info(f"Building enhanced conversation data (v2.0) for event: {selected_event.title}")
786
786
 
787
787
  # Build event data
788
788
  event_data = {
@@ -792,9 +792,7 @@ def build_enhanced_conversation_data(
792
792
  "event_type": selected_event.event_type,
793
793
  "status": selected_event.status,
794
794
  "created_at": None, # Will be populated if available from EventRecord
795
- "updated_at": selected_event.updated_at.isoformat()
796
- if selected_event.updated_at
797
- else None,
795
+ "updated_at": selected_event.updated_at.isoformat() if selected_event.updated_at else None,
798
796
  "metadata": {},
799
797
  }
800
798
 
@@ -834,9 +832,7 @@ def build_enhanced_conversation_data(
834
832
  continue
835
833
  messages.append(msg)
836
834
  except json.JSONDecodeError:
837
- logger.warning(
838
- f"Failed to parse JSONL line in turn {turn.id}"
839
- )
835
+ logger.warning(f"Failed to parse JSONL line in turn {turn.id}")
840
836
  continue
841
837
 
842
838
  # Build turn data (V9: includes creator fields)
@@ -844,9 +840,9 @@ def build_enhanced_conversation_data(
844
840
  "turn_id": turn.id,
845
841
  "turn_number": turn.turn_number,
846
842
  "content_hash": turn.content_hash,
847
- "timestamp": turn.timestamp.isoformat()
848
- if turn.timestamp
849
- else datetime.now().isoformat(),
843
+ "timestamp": (
844
+ turn.timestamp.isoformat() if turn.timestamp else datetime.now().isoformat()
845
+ ),
850
846
  "llm_title": turn.llm_title or "",
851
847
  "llm_description": turn.llm_description,
852
848
  "user_message": (
@@ -862,7 +858,11 @@ def build_enhanced_conversation_data(
862
858
  _collapse_high_entropy_runs(turn.assistant_summary, compaction),
863
859
  compaction.max_turn_assistant_summary_chars,
864
860
  )
865
- if (compaction and compaction.enabled and isinstance(turn.assistant_summary, str))
861
+ if (
862
+ compaction
863
+ and compaction.enabled
864
+ and isinstance(turn.assistant_summary, str)
865
+ )
866
866
  else turn.assistant_summary
867
867
  ),
868
868
  "model_name": turn.model_name,
@@ -880,12 +880,10 @@ def build_enhanced_conversation_data(
880
880
  "workspace_path": session.workspace_path,
881
881
  "session_title": session.session_title,
882
882
  "session_summary": session.session_summary,
883
- "started_at": session.started_at.isoformat()
884
- if session.started_at
885
- else None,
886
- "last_activity_at": session.last_activity_at.isoformat()
887
- if session.last_activity_at
888
- else None,
883
+ "started_at": session.started_at.isoformat() if session.started_at else None,
884
+ "last_activity_at": (
885
+ session.last_activity_at.isoformat() if session.last_activity_at else None
886
+ ),
889
887
  "creator_name": session.creator_name,
890
888
  "creator_id": session.creator_id,
891
889
  "turns": turns_data,
@@ -938,9 +936,7 @@ def get_unpushed_commits(repo_root: Path) -> List[UnpushedCommit]:
938
936
  Returns empty list since git tracking is no longer used.
939
937
  Use get_sessions_for_export() instead.
940
938
  """
941
- logger.warning(
942
- "get_unpushed_commits is deprecated, use get_sessions_for_export instead"
943
- )
939
+ logger.warning("get_unpushed_commits is deprecated, use get_sessions_for_export instead")
944
940
  return []
945
941
 
946
942
 
@@ -1212,11 +1208,7 @@ def display_sessions_for_selection(sessions: List[ExportableSession]) -> None:
1212
1208
  if title:
1213
1209
  title = title[:80] + ("..." if len(title) > 80 else "")
1214
1210
 
1215
- last_ts = (
1216
- session.last_activity_at.isoformat()
1217
- if session.last_activity_at
1218
- else "unknown"
1219
- )
1211
+ last_ts = session.last_activity_at.isoformat() if session.last_activity_at else "unknown"
1220
1212
  turns = session.turn_count
1221
1213
  meta = f"{session.session_type} | turns={turns} | last={last_ts}"
1222
1214
  if title:
@@ -1254,11 +1246,7 @@ def display_events_for_selection(events: List[ExportableEvent]) -> None:
1254
1246
  # First line: [索引] event_id... | 会话数 | 更新时间
1255
1247
  event_id_short = event.event_id[:8]
1256
1248
  session_count = len(event.sessions)
1257
- updated = (
1258
- event.updated_at.strftime("%Y-%m-%d %H:%M")
1259
- if event.updated_at
1260
- else "unknown"
1261
- )
1249
+ updated = event.updated_at.strftime("%Y-%m-%d %H:%M") if event.updated_at else "unknown"
1262
1250
 
1263
1251
  first_line = f" [{event.index}] {event_id_short}... | {session_count} sessions | {updated}"
1264
1252
  if use_rich:
@@ -1451,7 +1439,10 @@ def export_shares_command(
1451
1439
  indices_list = parse_indices(indices_input)
1452
1440
  except ValueError as e:
1453
1441
  print(f"Error: Invalid selector format: {e}", file=sys.stderr)
1454
- print(f"Valid formats: 1-{len(events)}, abc123de (UUID prefix), or abc123de,def456gh (multiple UUIDs)", file=sys.stderr)
1442
+ print(
1443
+ f"Valid formats: 1-{len(events)}, abc123de (UUID prefix), or abc123de,def456gh (multiple UUIDs)",
1444
+ file=sys.stderr,
1445
+ )
1455
1446
  logger.error(f"Invalid selector format: {e}")
1456
1447
  return 1
1457
1448
 
@@ -1540,9 +1531,7 @@ def encrypt_conversation_data(data: dict, password: str) -> dict:
1540
1531
  包含加密数据的字典: {encrypted_data, salt, nonce, password_hash}
1541
1532
  """
1542
1533
  if not CRYPTO_AVAILABLE:
1543
- raise RuntimeError(
1544
- "cryptography package not installed. Run: pip install cryptography"
1545
- )
1534
+ raise RuntimeError("cryptography package not installed. Run: pip install cryptography")
1546
1535
 
1547
1536
  # 生成盐值和随机数
1548
1537
  salt = os.urandom(32)
@@ -1627,7 +1616,9 @@ def upload_to_backend(
1627
1616
  encrypted_data_size = len(encrypted_data.encode("utf-8"))
1628
1617
 
1629
1618
  # Always print payload size for debugging
1630
- print(f"📊 Payload size: {payload_size / 1024 / 1024:.2f}MB (threshold: {CHUNKED_UPLOAD_THRESHOLD / 1024 / 1024:.2f}MB)")
1619
+ print(
1620
+ f"📊 Payload size: {payload_size / 1024 / 1024:.2f}MB (threshold: {CHUNKED_UPLOAD_THRESHOLD / 1024 / 1024:.2f}MB)"
1621
+ )
1631
1622
 
1632
1623
  # Decide upload method based on size
1633
1624
  # Use chunked upload if either the full payload or encrypted_data exceeds threshold
@@ -1724,9 +1715,7 @@ def _upload_chunks_and_complete(
1724
1715
  # Upload each chunk
1725
1716
  for i, chunk in enumerate(chunks):
1726
1717
  if progress_callback:
1727
- progress_callback(
1728
- i + 1, total_chunks + 2, f"Uploading chunk {i + 1}/{total_chunks}..."
1729
- )
1718
+ progress_callback(i + 1, total_chunks + 2, f"Uploading chunk {i + 1}/{total_chunks}...")
1730
1719
 
1731
1720
  try:
1732
1721
  chunk_payload = {
@@ -1912,7 +1901,9 @@ def upload_to_backend_unencrypted(
1912
1901
  payload_size = len(payload_json.encode("utf-8"))
1913
1902
 
1914
1903
  # Always print payload size for debugging
1915
- print(f"📊 Payload size: {payload_size / 1024 / 1024:.2f}MB (threshold: {CHUNKED_UPLOAD_THRESHOLD / 1024 / 1024:.2f}MB)")
1904
+ print(
1905
+ f"📊 Payload size: {payload_size / 1024 / 1024:.2f}MB (threshold: {CHUNKED_UPLOAD_THRESHOLD / 1024 / 1024:.2f}MB)"
1906
+ )
1916
1907
 
1917
1908
  # Decide upload method based on size
1918
1909
  if payload_size > CHUNKED_UPLOAD_THRESHOLD:
@@ -2398,9 +2389,7 @@ Return the content in JSON format."""
2398
2389
 
2399
2390
  except Exception as e:
2400
2391
  logger.error(f"LLM UI metadata generation failed: {e}", exc_info=True)
2401
- print(
2402
- f" ⚠️ LLM generation failed: {e}, using default UI text", file=sys.stderr
2403
- )
2392
+ print(f" ⚠️ LLM generation failed: {e}, using default UI text", file=sys.stderr)
2404
2393
  return None, None
2405
2394
 
2406
2395
 
@@ -2629,9 +2618,7 @@ def _export_by_events_interactive(
2629
2618
 
2630
2619
  if not events:
2631
2620
  print("No events available yet.")
2632
- print(
2633
- "Tip: events are auto-generated by session_summarizer during watcher sessions"
2634
- )
2621
+ print("Tip: events are auto-generated by session_summarizer during watcher sessions")
2635
2622
  return 1
2636
2623
 
2637
2624
  # Default: show 3-7 most recent events, but allow showing all (tree view).
@@ -2642,15 +2629,11 @@ def _export_by_events_interactive(
2642
2629
  )
2643
2630
  default_limit = 7
2644
2631
  show_choice = (
2645
- input(
2646
- f"Show [r]ecent events ({min(default_limit, len(events_sorted))}) or [a]ll? [r]: "
2647
- )
2632
+ input(f"Show [r]ecent events ({min(default_limit, len(events_sorted))}) or [a]ll? [r]: ")
2648
2633
  .strip()
2649
2634
  .lower()
2650
2635
  )
2651
- events_to_show = (
2652
- events_sorted if show_choice.startswith("a") else events_sorted[:default_limit]
2653
- )
2636
+ events_to_show = events_sorted if show_choice.startswith("a") else events_sorted[:default_limit]
2654
2637
 
2655
2638
  def _event_path(ev) -> List[str]:
2656
2639
  ui = ev.ui_metadata or {}
@@ -2714,9 +2697,7 @@ def _export_by_events_interactive(
2714
2697
  "idx": idx,
2715
2698
  "label": key,
2716
2699
  "commit_count": commit_count,
2717
- "desc": desc
2718
- if (desc and not desc.startswith("Auto-added"))
2719
- else "",
2700
+ "desc": desc if (desc and not desc.startswith("Auto-added")) else "",
2720
2701
  }
2721
2702
  )
2722
2703
  else:
@@ -2773,15 +2754,9 @@ def _export_by_events_interactive(
2773
2754
  console.print(Padding(_render_markdown_lite(line), (0, 0, 0, 2 * indent)))
2774
2755
  desc = (item.get("desc") or "").strip()
2775
2756
  if desc:
2776
- console.print(
2777
- Padding(_render_markdown_lite(desc), (0, 0, 1, 2 * (indent + 1)))
2778
- )
2757
+ console.print(Padding(_render_markdown_lite(desc), (0, 0, 1, 2 * (indent + 1))))
2779
2758
  else:
2780
- console.print(
2781
- Padding(
2782
- _render_markdown_lite(str(item["label"])), (0, 0, 0, 2 * indent)
2783
- )
2784
- )
2759
+ console.print(Padding(_render_markdown_lite(str(item["label"])), (0, 0, 0, 2 * indent)))
2785
2760
  console.print()
2786
2761
 
2787
2762
  # Get user selection
@@ -2807,9 +2782,7 @@ def _export_by_events_interactive(
2807
2782
  else:
2808
2783
  # Fall back to numeric indices
2809
2784
  indices_list = parse_commit_indices(selection)
2810
- selected_events = [
2811
- index_to_event[i] for i in indices_list if i in index_to_event
2812
- ]
2785
+ selected_events = [index_to_event[i] for i in indices_list if i in index_to_event]
2813
2786
 
2814
2787
  if not selected_events:
2815
2788
  print("No valid events selected.")
@@ -2817,7 +2790,9 @@ def _export_by_events_interactive(
2817
2790
 
2818
2791
  except ValueError as e:
2819
2792
  print(f"Error: Invalid selector format: {e}", file=sys.stderr)
2820
- print("Valid formats: 1,3,5-7, all, abc123de (UUID prefix), or abc123de,def456gh (multiple UUIDs)")
2793
+ print(
2794
+ "Valid formats: 1,3,5-7, all, abc123de (UUID prefix), or abc123de,def456gh (multiple UUIDs)"
2795
+ )
2821
2796
  return 1
2822
2797
 
2823
2798
  all_commit_hashes = set()
@@ -2833,9 +2808,7 @@ def _export_by_events_interactive(
2833
2808
  loaded = get_commits_by_hashes(shadow_git, missing_hashes)
2834
2809
  selected_commits.extend(loaded)
2835
2810
  except Exception as e:
2836
- logger.error(
2837
- f"Failed to load commits by hashes for events: {e}", exc_info=True
2838
- )
2811
+ logger.error(f"Failed to load commits by hashes for events: {e}", exc_info=True)
2839
2812
  print(
2840
2813
  f"Error: Failed to load commits for selected events: {e}",
2841
2814
  file=sys.stderr,
@@ -2868,11 +2841,7 @@ def _export_by_events_interactive(
2868
2841
  selected_commits = sorted(selected_commits, key=lambda c: c.timestamp, reverse=True)
2869
2842
  print("\n📋 Commits included:\n")
2870
2843
  for i, c in enumerate(selected_commits, 1):
2871
- summary = (
2872
- c.llm_summary
2873
- if c.llm_summary and c.llm_summary != "(No summary)"
2874
- else c.message
2875
- )
2844
+ summary = c.llm_summary if c.llm_summary and c.llm_summary != "(No summary)" else c.message
2876
2845
  print(f" [{i}] {c.hash} - {summary}")
2877
2846
  print()
2878
2847
 
@@ -2888,9 +2857,7 @@ def _export_by_events_interactive(
2888
2857
  try:
2889
2858
  keep = parse_commit_indices(subset)
2890
2859
  selected_commits = [
2891
- selected_commits[i - 1]
2892
- for i in keep
2893
- if 0 < i <= len(selected_commits)
2860
+ selected_commits[i - 1] for i in keep if 0 < i <= len(selected_commits)
2894
2861
  ]
2895
2862
  except Exception as e:
2896
2863
  print(f"Error: Invalid indices format: {e}", file=sys.stderr)
@@ -3101,9 +3068,7 @@ def export_shares_interactive_command(
3101
3068
 
3102
3069
  from .. import get_realign_dir
3103
3070
 
3104
- shadow_git = get_realign_dir(
3105
- repo_root
3106
- ) # used for log/output placement (not a git repo)
3071
+ shadow_git = get_realign_dir(repo_root) # used for log/output placement (not a git repo)
3107
3072
 
3108
3073
  # Always show all events, don't filter by workspace
3109
3074
  events = get_events_for_export(workspace_path=None, limit=200)
@@ -3122,9 +3087,7 @@ def export_shares_interactive_command(
3122
3087
  from rich.console import Console
3123
3088
 
3124
3089
  console = Console()
3125
- console.print(
3126
- "[yellow]Enter event number to export (single event only):[/yellow]"
3127
- )
3090
+ console.print("[yellow]Enter event number to export (single event only):[/yellow]")
3128
3091
  except ImportError:
3129
3092
  print("Enter event number to export (single event only):")
3130
3093
  indices = input("Event #: ").strip()
@@ -3236,7 +3199,9 @@ def export_shares_interactive_command(
3236
3199
  and full_event.share_url
3237
3200
  ):
3238
3201
  reuse_share_url = full_event.share_url
3239
- reuse_share_id = getattr(full_event, "share_id", None) or _extract_share_id_from_url(reuse_share_url)
3202
+ reuse_share_id = getattr(full_event, "share_id", None) or _extract_share_id_from_url(
3203
+ reuse_share_url
3204
+ )
3240
3205
  reuse_admin_token = getattr(full_event, "share_admin_token", None)
3241
3206
 
3242
3207
  # Fast path: if we're not debugging, don't rebuild payload—just extend expiry.
@@ -3312,9 +3277,7 @@ def export_shares_interactive_command(
3312
3277
  compaction=None,
3313
3278
  )
3314
3279
  conversation_data_full_for_debug = conversation_data_full
3315
- full_bytes = len(
3316
- json.dumps(conversation_data_full, ensure_ascii=False).encode("utf-8")
3317
- )
3280
+ full_bytes = len(json.dumps(conversation_data_full, ensure_ascii=False).encode("utf-8"))
3318
3281
 
3319
3282
  conversation_data_compact = build_enhanced_conversation_data(
3320
3283
  selected_event=selected_events[0],
@@ -3334,9 +3297,7 @@ def export_shares_interactive_command(
3334
3297
 
3335
3298
  full_mb = full_bytes / 1024 / 1024
3336
3299
  compact_mb = compact_bytes / 1024 / 1024
3337
- reduction = (
3338
- (1 - (compact_bytes / full_bytes)) * 100 if full_bytes else 0.0
3339
- )
3300
+ reduction = (1 - (compact_bytes / full_bytes)) * 100 if full_bytes else 0.0
3340
3301
  print("\n=== Export Payload Size (pre-encryption) ===")
3341
3302
  print(f"No compact: {full_mb:.2f}MB")
3342
3303
  print(f"Compact: {compact_mb:.2f}MB ({reduction:.1f}% smaller)")
@@ -3351,7 +3312,9 @@ def export_shares_interactive_command(
3351
3312
  print("\nLargest lines (no content):")
3352
3313
  for item in report["top_lines"]:
3353
3314
  size_kb = item["size_bytes"] / 1024
3354
- blocks = ",".join(item["content_block_types"]) if item["content_block_types"] else "-"
3315
+ blocks = (
3316
+ ",".join(item["content_block_types"]) if item["content_block_types"] else "-"
3317
+ )
3355
3318
  tools = item.get("tools") or []
3356
3319
  tool_hint = ""
3357
3320
  if tools:
@@ -3663,9 +3626,7 @@ def export_shares_interactive_command(
3663
3626
  db = get_database()
3664
3627
  db.update_event_share_metadata(
3665
3628
  event_id=selected_event.event_id,
3666
- preset_questions=ui_metadata.get("preset_questions")
3667
- if ui_metadata
3668
- else None,
3629
+ preset_questions=ui_metadata.get("preset_questions") if ui_metadata else None,
3669
3630
  slack_message=ui_metadata.get("slack_message") if ui_metadata else None,
3670
3631
  share_url=result.get("share_url"),
3671
3632
  share_id=result.get("share_id")
@@ -3706,7 +3667,5 @@ def export_shares_interactive_command(
3706
3667
  )
3707
3668
 
3708
3669
  if not json_output:
3709
- logger.info(
3710
- f"======== Interactive export completed: {result['share_url']} ========"
3711
- )
3670
+ logger.info(f"======== Interactive export completed: {result['share_url']} ========")
3712
3671
  return 0