aline-ai 0.7.4__py3-none-any.whl → 0.7.5__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aline-ai
3
- Version: 0.7.4
3
+ Version: 0.7.5
4
4
  Summary: Shared AI memory; everyone knows everything in teams
5
5
  Author: Sharemind
6
6
  License: MIT
@@ -1,5 +1,5 @@
1
- aline_ai-0.7.4.dist-info/licenses/LICENSE,sha256=H8wTqV5IF1oHw_HbBtS1PSDU8G_q81yblEIL_JfV8Vo,1077
2
- realign/__init__.py,sha256=4YDOjZrTvdzz5ddO6-cwuKux4QKiTgmSFG5zrMzRo58,1623
1
+ aline_ai-0.7.5.dist-info/licenses/LICENSE,sha256=H8wTqV5IF1oHw_HbBtS1PSDU8G_q81yblEIL_JfV8Vo,1077
2
+ realign/__init__.py,sha256=IirhpRnTbBI_00iPya-W-qJQt2x8bWszi8GFYBZH8GM,1623
3
3
  realign/agent_names.py,sha256=H4oVJMkqg1ZYCk58vD_Jh9apaAHSFJRswa-C9SPdJxc,1171
4
4
  realign/auth.py,sha256=d_1yvCwluN5iIrdgjtuSKpOYAksDzrzNgntKacLVJrw,16583
5
5
  realign/claude_detector.py,sha256=ZLSJacMo6zzQclXByABKA70UNpstxqIv3fPGqdpA934,2792
@@ -51,7 +51,7 @@ realign/commands/init.py,sha256=y9GUJcOyLm9ZBl5XPdqaG6-HmN0q3oWXzMKohXzMcwA,3602
51
51
  realign/commands/restore.py,sha256=s2BxQZHxQw9r12NzRVsK20KlGafy5AIoSjWMo5PcnHY,11173
52
52
  realign/commands/search.py,sha256=QlUDzRDD6ebq21LTtLe5-OZM62iwDrDqfbnXbuxfklU,27516
53
53
  realign/commands/sync_agent.py,sha256=-ze0bXwABmO77d1EenObBPg0coAHgAX1rVEeJGXJjrg,24799
54
- realign/commands/upgrade.py,sha256=L3PLOUIN5qAQTbkfoVtSsIbbzEezA_xjjk9F1GMVfjw,12781
54
+ realign/commands/upgrade.py,sha256=e_U15s-3S2W2lCqGXuvcBhYCPxFJ4cGzGF7MNCWdG30,17050
55
55
  realign/commands/watcher.py,sha256=4WTThIgr-Z5guKh_JqGDcPmerr97XiHrVaaijmckHsA,134350
56
56
  realign/commands/worker.py,sha256=jTu7Pj60nTnn7SsH3oNCNnO6zl4TIFCJVNSC1OoQ_0o,23363
57
57
  realign/dashboard/__init__.py,sha256=QZkHTsGityH8UkF8rmvA3xW7dMXNe0swEWr443qfgCM,128
@@ -62,7 +62,7 @@ realign/dashboard/layout.py,sha256=sZxmFj6QTbkois9MHTvBEMMcnaRVehCDqugdbiFx10k,9
62
62
  realign/dashboard/local_api.py,sha256=Roq74etTJR0uOiHE3uIe7sqVITjS5JGQEF4g0nmUm5Q,4332
63
63
  realign/dashboard/state.py,sha256=V7zBKvyDgqdXv68XHxV4T8xf3IhYbI5W33UmYW3_hyM,1139
64
64
  realign/dashboard/terminal_backend.py,sha256=MlDfwtqhftyQK6jDNizQGFjAWIo5Bx2TDpSnP3MCZVM,3375
65
- realign/dashboard/tmux_manager.py,sha256=jTEU_0lqPfSJ7Q1fG77miY9fEctbQB5-qaDOW-prDKI,45767
65
+ realign/dashboard/tmux_manager.py,sha256=aDL2wpRvEKnTG1QBkh4i__0_6xRa-n9412cfQcMzM18,46440
66
66
  realign/dashboard/backends/__init__.py,sha256=POROX7YKtukYZcLB1pi_kO0sSEpuO3y-hwmF3WIN1Kk,163
67
67
  realign/dashboard/backends/iterm2.py,sha256=XYYJT5lrrp4pW_MyEqPZYkRI0qyKUwJlezwMidgnsHc,21390
68
68
  realign/dashboard/backends/kitty.py,sha256=5jdkR1f2PwB8a4SnS3EG6uOQ2XU-PB7-cpKBfIJq3hU,12066
@@ -77,8 +77,8 @@ realign/dashboard/screens/session_detail.py,sha256=TBkHqSHyMxsLB2QdZq9m1EoiH8oRV
77
77
  realign/dashboard/screens/share_import.py,sha256=hl2x0yGVycsoUI76AmdZTAV-br3Q6191g5xHHrZ8hOA,6318
78
78
  realign/dashboard/styles/dashboard.tcss,sha256=9W5Tx0lgyGb4HU-z-Kn7gBdexIK0aPe0bkVn2k_AseM,3288
79
79
  realign/dashboard/widgets/__init__.py,sha256=dXsOnbeu_8XhP-6Bu6-R_0LNGqsSM6x7dG7FCDumpa8,460
80
- realign/dashboard/widgets/agents_panel.py,sha256=Qi76wfiSSK_Qn3TpQEVW8g2cltPQcG0RuzWi8F0HCXo,70815
81
- realign/dashboard/widgets/config_panel.py,sha256=lQqAO2OEUzZIMn9qGiRJsv32D1sRb3XnjjiyYvXyS5E,15324
80
+ realign/dashboard/widgets/agents_panel.py,sha256=XuywG4LR4LrS4WSdVHAREqtHFjV54C045jZvtWiN8rs,71027
81
+ realign/dashboard/widgets/config_panel.py,sha256=yZPymOZ5gZ4RCJE9iZ3AYUHpH9CSpSBIa3tQslUi39Q,16006
82
82
  realign/dashboard/widgets/events_table.py,sha256=0cMvE0KdZFBZyvywv7vlt005qsR0aLQnQiMf3ZzK7RY,30218
83
83
  realign/dashboard/widgets/header.py,sha256=o6lQwNXvaxKwJQz0lep20ymoaT34gSb71zT7IkZe5D0,1562
84
84
  realign/dashboard/widgets/openable_table.py,sha256=GeJPDEYp0kRHShqvmPMzAePpYXRZHUNqcWNnxqsqxjA,1963
@@ -111,8 +111,8 @@ realign/triggers/next_turn_trigger.py,sha256=-x80_I-WmIjXXzQHEPBykgx_GQW6oKaLDQx
111
111
  realign/triggers/registry.py,sha256=dkIjSd8Bg-hF0nxaO2Fi2K-0Zipqv6vVjc-HYSrA_fY,3656
112
112
  realign/triggers/turn_status.py,sha256=wAZEhXDAmDoX5F-ohWfSnZZ0eA6DAJ9svSPiSv_f6sg,6041
113
113
  realign/triggers/turn_summary.py,sha256=f3hEUshgv9skJ9AbfWpoYs417lsv_HK2A_vpPjgryO4,4467
114
- aline_ai-0.7.4.dist-info/METADATA,sha256=roEHsnZg0iMKmmkXAL0hykWcZtI3wV-Qk2vtBIf2feo,1597
115
- aline_ai-0.7.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
116
- aline_ai-0.7.4.dist-info/entry_points.txt,sha256=TvYELpMoWsUTcQdMV8tBHxCbEf_LbK4sESqK3r8PM6Y,78
117
- aline_ai-0.7.4.dist-info/top_level.txt,sha256=yIL3s2xv9nf1GwD5n71Aq_JEIV4AfzCIDNKBzewuRm4,8
118
- aline_ai-0.7.4.dist-info/RECORD,,
114
+ aline_ai-0.7.5.dist-info/METADATA,sha256=-WmXf4ezxtC-BD-zb-NoywchU0NF89UzsoxAwNOZ6-g,1597
115
+ aline_ai-0.7.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
116
+ aline_ai-0.7.5.dist-info/entry_points.txt,sha256=TvYELpMoWsUTcQdMV8tBHxCbEf_LbK4sESqK3r8PM6Y,78
117
+ aline_ai-0.7.5.dist-info/top_level.txt,sha256=yIL3s2xv9nf1GwD5n71Aq_JEIV4AfzCIDNKBzewuRm4,8
118
+ aline_ai-0.7.5.dist-info/RECORD,,
realign/__init__.py CHANGED
@@ -3,7 +3,7 @@
3
3
  import hashlib
4
4
  from pathlib import Path
5
5
 
6
- __version__ = "0.7.4"
6
+ __version__ = "0.7.5"
7
7
 
8
8
 
9
9
  def get_realign_dir(project_root: Path) -> Path:
@@ -1,6 +1,7 @@
1
1
  """ReAlign upgrade command - Upgrade database schema to latest version."""
2
2
 
3
3
  from pathlib import Path
4
+ import os
4
5
  import subprocess
5
6
  import typer
6
7
  from rich.console import Console
@@ -13,6 +14,97 @@ from ..db.schema import SCHEMA_VERSION, get_migration_scripts
13
14
  console = Console()
14
15
 
15
16
 
17
+ def _env_truthy(name: str) -> bool:
18
+ value = os.environ.get(name, "").strip().lower()
19
+ return value in {"1", "true", "yes", "y", "on"}
20
+
21
+
22
+ def _stop_daemons_best_effort_for_package_upgrade() -> None:
23
+ """Stop watcher/worker daemons (best-effort) before upgrading the package.
24
+
25
+ Upgrading the installed package while old daemons are running can leave the system
26
+ in a mixed-version state (old processes continue running old code).
27
+ """
28
+ try:
29
+ from . import watcher as watcher_cmd
30
+
31
+ try:
32
+ watcher_running = bool(watcher_cmd._is_watcher_running())
33
+ except Exception:
34
+ watcher_running = False
35
+ if watcher_running:
36
+ console.print("[dim]Stopping watcher for upgrade...[/dim]")
37
+ try:
38
+ watcher_cmd.watcher_stop_command()
39
+ except Exception:
40
+ pass
41
+ except Exception:
42
+ pass
43
+
44
+ try:
45
+ from . import worker as worker_cmd
46
+
47
+ try:
48
+ worker_running, _pid, _mode = worker_cmd.detect_worker_process()
49
+ except Exception:
50
+ worker_running = False
51
+ if worker_running:
52
+ console.print("[dim]Stopping worker for upgrade...[/dim]")
53
+ try:
54
+ worker_cmd.worker_stop_command()
55
+ except Exception:
56
+ pass
57
+ except Exception:
58
+ pass
59
+
60
+
61
+ def _detect_running_tmux_dashboard() -> tuple[bool, str, str]:
62
+ """Detect an Aline-managed tmux dashboard session (best-effort)."""
63
+ try:
64
+ from ..dashboard.tmux_manager import OUTER_SESSION, OUTER_SOCKET # type: ignore
65
+
66
+ outer_session = str(OUTER_SESSION)
67
+ outer_socket = str(OUTER_SOCKET)
68
+ except Exception:
69
+ # Keep defaults stable for older versions / import failures.
70
+ outer_session = "aline"
71
+ outer_socket = "aline_dash"
72
+
73
+ try:
74
+ proc = subprocess.run(
75
+ ["tmux", "-L", outer_socket, "has-session", "-t", outer_session],
76
+ capture_output=True,
77
+ text=True,
78
+ timeout=1.5,
79
+ check=False,
80
+ )
81
+ return proc.returncode == 0, outer_socket, outer_session
82
+ except Exception:
83
+ return False, outer_socket, outer_session
84
+
85
+
86
+ def _run_doctor_after_upgrade() -> None:
87
+ if _env_truthy("ALINE_SKIP_DOCTOR_AFTER_UPGRADE"):
88
+ console.print("[dim]Skipping 'aline doctor' (ALINE_SKIP_DOCTOR_AFTER_UPGRADE=1).[/dim]\n")
89
+ return
90
+
91
+ console.print("\n[dim]Running 'aline doctor' to finalize the update...[/dim]\n")
92
+ try:
93
+ result = subprocess.run(["aline", "doctor"], capture_output=False)
94
+ if result.returncode == 0:
95
+ console.print("\n[green]✓ 'aline doctor' completed.[/green]\n")
96
+ else:
97
+ console.print(
98
+ f"\n[yellow]⚠ 'aline doctor' exited with code {result.returncode}.[/yellow]\n"
99
+ )
100
+ except FileNotFoundError:
101
+ console.print(
102
+ "\n[yellow]⚠ Could not find 'aline' on PATH to run doctor. Please run: aline doctor[/yellow]\n"
103
+ )
104
+ except Exception as e:
105
+ console.print(f"\n[yellow]⚠ Failed to run 'aline doctor': {e}[/yellow]\n")
106
+
107
+
16
108
  def get_latest_pypi_version() -> Optional[str]:
17
109
  """Fetch the latest version of aline-ai from PyPI.
18
110
 
@@ -110,6 +202,30 @@ def check_and_prompt_update() -> bool:
110
202
  console.print("[dim]Update skipped.[/dim]\n")
111
203
  return False
112
204
 
205
+ # Best practice: stop daemons and close dashboards before upgrading.
206
+ # Otherwise users can end up with old processes still running old code.
207
+ dash_running, dash_socket, dash_session = _detect_running_tmux_dashboard()
208
+ if dash_running:
209
+ console.print(
210
+ "\n[bold yellow]⚠ Detected a running Aline dashboard tmux session.[/bold yellow]"
211
+ )
212
+ console.print(
213
+ f"[dim]Close it before upgrading to avoid mixed versions. "
214
+ f"(Command: tmux -L {dash_socket} kill-session -t {dash_session})[/dim]\n"
215
+ )
216
+ try:
217
+ proceed = console.input(
218
+ "[dim]Continue upgrade anyway? ([/dim][green]y[/green][dim]/[/dim][yellow]n[/yellow][dim]):[/dim] "
219
+ ).strip().lower()
220
+ except (EOFError, KeyboardInterrupt):
221
+ console.print("\n[dim]Update skipped.[/dim]\n")
222
+ return False
223
+ if proceed not in ("y", "yes"):
224
+ console.print("[dim]Update skipped.[/dim]\n")
225
+ return False
226
+
227
+ _stop_daemons_best_effort_for_package_upgrade()
228
+
113
229
  # Perform update using pipx
114
230
  console.print("\n[bold]Updating aline-ai via pipx...[/bold]\n")
115
231
 
@@ -123,6 +239,7 @@ def check_and_prompt_update() -> bool:
123
239
  console.print(
124
240
  f"\n[bold green]✓ Successfully updated to {latest_version}![/bold green]"
125
241
  )
242
+ _run_doctor_after_upgrade()
126
243
  console.print("[dim]Please restart 'aline' to use the new version.[/dim]\n")
127
244
  return True
128
245
  else:
@@ -51,6 +51,7 @@ OPT_NO_TRACK = "@aline_no_track"
51
51
  # Default outer layout preferences (tmux mode).
52
52
  # Note: tmux "pixels" are terminal cell columns; Textual has no notion of pixels.
53
53
  DEFAULT_DASHBOARD_PANE_WIDTH_COLS = 45
54
+ _STATE_BORDER_RESIZE_ENABLED_KEY = "tmux_border_resize_enabled"
54
55
 
55
56
 
56
57
  @dataclass(frozen=True)
@@ -502,6 +503,12 @@ def bootstrap_dashboard_into_tmux() -> None:
502
503
  # Enable mouse for the managed session only.
503
504
  _run_outer_tmux(["set-option", "-t", OUTER_SESSION, "mouse", "on"])
504
505
  _disable_outer_border_resize()
506
+ try:
507
+ from .state import set_dashboard_state_value
508
+
509
+ set_dashboard_state_value(_STATE_BORDER_RESIZE_ENABLED_KEY, False)
510
+ except Exception:
511
+ pass
505
512
 
506
513
  # Disable status bar for cleaner UI (Aline sessions only).
507
514
  _run_outer_tmux(["set-option", "-t", OUTER_SESSION, "status", "off"])
@@ -538,7 +545,7 @@ def bootstrap_dashboard_into_tmux() -> None:
538
545
  _run_outer_tmux(["select-window", "-t", f"{OUTER_SESSION}:{OUTER_WINDOW}"])
539
546
 
540
547
  # Best-effort: enforce a stable dashboard pane width if the terminal pane already exists.
541
- _set_outer_dashboard_pane_width(DEFAULT_DASHBOARD_PANE_WIDTH_COLS)
548
+ enforce_outer_dashboard_pane_width(DEFAULT_DASHBOARD_PANE_WIDTH_COLS)
542
549
 
543
550
  # Sanity-check before exec'ing into tmux attach. If this fails, fall back to non-tmux mode.
544
551
  ready = _run_outer_tmux(["has-session", "-t", OUTER_SESSION], capture=True)
@@ -725,7 +732,7 @@ def ensure_right_pane(width_percent: int = 50) -> bool:
725
732
  )
726
733
  panes = _parse_lines(panes_out)
727
734
  if len(panes) >= 2:
728
- _set_outer_dashboard_pane_width(DEFAULT_DASHBOARD_PANE_WIDTH_COLS)
735
+ enforce_outer_dashboard_pane_width(DEFAULT_DASHBOARD_PANE_WIDTH_COLS)
729
736
  return True
730
737
 
731
738
  # Split from the dashboard pane to keep it on the left.
@@ -748,7 +755,7 @@ def ensure_right_pane(width_percent: int = 50) -> bool:
748
755
  if detail:
749
756
  logger.warning(f"ensure_right_pane split-window failed: {detail}")
750
757
  if split.returncode == 0:
751
- _set_outer_dashboard_pane_width(DEFAULT_DASHBOARD_PANE_WIDTH_COLS)
758
+ enforce_outer_dashboard_pane_width(DEFAULT_DASHBOARD_PANE_WIDTH_COLS)
752
759
  return True
753
760
  return False
754
761
 
@@ -763,8 +770,9 @@ def ensure_right_pane_ready(width_percent: int = 50) -> bool:
763
770
  if not ok:
764
771
  return False
765
772
 
766
- # Best-effort: enforce a stable dashboard pane width whenever we touch the outer layout.
767
- _set_outer_dashboard_pane_width(DEFAULT_DASHBOARD_PANE_WIDTH_COLS)
773
+ # Best-effort: enforce a stable dashboard pane width whenever we touch the outer layout
774
+ # (unless user enabled drag-to-resize).
775
+ enforce_outer_dashboard_pane_width(DEFAULT_DASHBOARD_PANE_WIDTH_COLS)
768
776
 
769
777
  attach_cmd = shlex.join(["tmux", "-L", INNER_SOCKET, "attach", "-t", INNER_SESSION])
770
778
 
@@ -815,6 +823,16 @@ def _disable_outer_border_resize() -> None:
815
823
  return
816
824
 
817
825
 
826
+ def outer_border_resize_enabled() -> bool:
827
+ """Return True when user has enabled tmux border drag-to-resize in the dashboard."""
828
+ try:
829
+ from .state import get_dashboard_state_value
830
+
831
+ return bool(get_dashboard_state_value(_STATE_BORDER_RESIZE_ENABLED_KEY, False))
832
+ except Exception:
833
+ return False
834
+
835
+
818
836
  def _set_outer_dashboard_pane_width(width_cols: int) -> None:
819
837
  """Best-effort: size the left dashboard pane to a fixed width (in terminal columns)."""
820
838
  try:
@@ -877,6 +895,8 @@ def _set_outer_dashboard_pane_width(width_cols: int) -> None:
877
895
 
878
896
  def enforce_outer_dashboard_pane_width(width_cols: int = DEFAULT_DASHBOARD_PANE_WIDTH_COLS) -> None:
879
897
  """Best-effort: enforce the fixed dashboard pane width for the outer tmux layout."""
898
+ if outer_border_resize_enabled():
899
+ return
880
900
  _set_outer_dashboard_pane_width(width_cols)
881
901
 
882
902
 
@@ -105,7 +105,12 @@ class _TerminalRowWidget(Horizontal):
105
105
 
106
106
  switch_classes = "terminal-switch active-terminal" if is_active else "terminal-switch"
107
107
 
108
- if title:
108
+ pane_cmd = (pane_current_command or "").strip().lower()
109
+ title_is_relevant = bool(title) and (
110
+ bool(running_agent) or pane_cmd in {"claude", "codex", "opencode"}
111
+ )
112
+
113
+ if title_is_relevant:
109
114
  label = f"{prefix}{title}"
110
115
  switch_btn: Button = TruncButton(
111
116
  label,
@@ -332,6 +332,10 @@ class ConfigPanel(Static):
332
332
  # If MouseDrag1Border is in the output, resize is enabled
333
333
  is_enabled = "MouseDrag1Border" in output
334
334
  self._border_resize_enabled = is_enabled
335
+ try:
336
+ set_dashboard_state_value("tmux_border_resize_enabled", bool(is_enabled))
337
+ except Exception:
338
+ pass
335
339
 
336
340
  # Update radio buttons without triggering the toggle action
337
341
  self._syncing_radio = True
@@ -356,6 +360,10 @@ class ConfigPanel(Static):
356
360
  "bind", "-n", "MouseDrag1Border", "resize-pane", "-M"
357
361
  ], timeout_s=0.5)
358
362
  self._border_resize_enabled = True
363
+ try:
364
+ set_dashboard_state_value("tmux_border_resize_enabled", True)
365
+ except Exception:
366
+ pass
359
367
  self.app.notify("Border resize enabled", title="Tmux")
360
368
  else:
361
369
  # Disable border resize by unbinding MouseDrag1Border
@@ -363,6 +371,16 @@ class ConfigPanel(Static):
363
371
  "unbind", "-n", "MouseDrag1Border"
364
372
  ], timeout_s=0.5)
365
373
  self._border_resize_enabled = False
374
+ try:
375
+ set_dashboard_state_value("tmux_border_resize_enabled", False)
376
+ except Exception:
377
+ pass
378
+ try:
379
+ from .. import tmux_manager
380
+
381
+ tmux_manager.enforce_outer_dashboard_pane_width()
382
+ except Exception:
383
+ pass
366
384
  self.app.notify("Border resize disabled", title="Tmux")
367
385
  except Exception as e:
368
386
  self.app.notify(f"Error toggling border resize: {e}", title="Tmux", severity="error")