@agentikos/omega-os 0.19.40 → 0.19.42
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.
- package/omega/Agentik_Engine/omega_engine/__init__.py +1 -1
- package/omega/Agentik_Engine/omega_engine/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/cli.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/tmux.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/cli.py +35 -6
- package/omega/Agentik_Engine/omega_engine/tmux.py +151 -21
- package/omega/Agentik_Engine/pyproject.toml +1 -1
- package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tmux_palette.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tmux_palette.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/test_tmux_and_aisb_chat.py +94 -0
- package/omega/Agentik_Engine/tests/test_tmux_palette.py +107 -0
- package/omega/Agentik_SSOT/VERSION +1 -1
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -2989,14 +2989,43 @@ def cmd_menu(_args: argparse.Namespace) -> int:
|
|
|
2989
2989
|
return 2
|
|
2990
2990
|
|
|
2991
2991
|
if os.environ.get("TMUX"):
|
|
2992
|
-
# We're already
|
|
2993
|
-
#
|
|
2992
|
+
# We're already inside a tmux client. Nested `tmux attach` is
|
|
2993
|
+
# rejected by tmux, but `tmux switch-client` works fine — and is
|
|
2994
|
+
# what the user wants when they type `omega` from inside tmux.
|
|
2995
|
+
# v0.19.41 — actually DO the switch instead of just printing
|
|
2996
|
+
# instructions. Print is the v0.19.40 bug ("ça lance rien du tout").
|
|
2997
|
+
import subprocess
|
|
2994
2998
|
if not tmux.is_alive(OMEGA_SESSION):
|
|
2995
2999
|
tmux.spawn_omega_master(_omega_home())
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
+
# Are we already INSIDE the Omega session?
|
|
3001
|
+
proc = subprocess.run(
|
|
3002
|
+
["tmux", "display-message", "-p", "#S"],
|
|
3003
|
+
capture_output=True, text=True,
|
|
3004
|
+
)
|
|
3005
|
+
current = (proc.stdout or "").strip()
|
|
3006
|
+
if current == OMEGA_SESSION:
|
|
3007
|
+
# Already in Omega — just jump to the menu window. If the
|
|
3008
|
+
# menu window was killed, recreate it.
|
|
3009
|
+
wins = subprocess.run(
|
|
3010
|
+
["tmux", "list-windows", "-t", OMEGA_SESSION, "-F", "#W"],
|
|
3011
|
+
capture_output=True, text=True,
|
|
3012
|
+
)
|
|
3013
|
+
if "menu" not in (wins.stdout or "").split():
|
|
3014
|
+
subprocess.run(
|
|
3015
|
+
["tmux", "new-window", "-t", OMEGA_SESSION, "-n",
|
|
3016
|
+
"menu", "omega menu-tui"],
|
|
3017
|
+
capture_output=True,
|
|
3018
|
+
)
|
|
3019
|
+
subprocess.run(
|
|
3020
|
+
["tmux", "select-window", "-t", f"{OMEGA_SESSION}:menu"],
|
|
3021
|
+
capture_output=True,
|
|
3022
|
+
)
|
|
3023
|
+
else:
|
|
3024
|
+
# Different session — switch the client to Omega.
|
|
3025
|
+
subprocess.run(
|
|
3026
|
+
["tmux", "switch-client", "-t", OMEGA_SESSION],
|
|
3027
|
+
capture_output=True,
|
|
3028
|
+
)
|
|
3000
3029
|
return 0
|
|
3001
3030
|
|
|
3002
3031
|
if not tmux.is_alive(OMEGA_SESSION):
|
|
@@ -589,10 +589,15 @@ bind-key Z display-popup -E -w 100% -h 100% "omega tmux menu"
|
|
|
589
589
|
bind-key S choose-tree -Zs
|
|
590
590
|
|
|
591
591
|
# Option+/ → session switcher (fzf popup, pick any live tmux session)
|
|
592
|
-
# Option+z → Omega action menu (
|
|
592
|
+
# Option+z → Omega action menu (popup direct like tmux-claude)
|
|
593
593
|
# macOS Option key → enable "Use Option as Meta" in your terminal.
|
|
594
|
-
|
|
595
|
-
|
|
594
|
+
#
|
|
595
|
+
# v0.19.42 — both binds use absolute paths (substituted at write-time)
|
|
596
|
+
# because tmux's popup spawns a non-interactive shell that doesn't source
|
|
597
|
+
# .bashrc/.zshrc, so bare `omega` is not in PATH. The placeholder
|
|
598
|
+
# __OMEGA_BIN__ is replaced by write_default_config().
|
|
599
|
+
bind-key -n M-/ display-popup -E -w 100% -h 100% "__OMEGA_BIN__ tmux switcher"
|
|
600
|
+
bind-key -n M-z display-popup -E -w 100% -h 100% "__OMEGA_BIN__ menu-tui"
|
|
596
601
|
|
|
597
602
|
# ════════════════════════════════════════════════════════════════════
|
|
598
603
|
# Pixel/CRT amber theme (tmux-claude inspired)
|
|
@@ -720,8 +725,16 @@ bind-key r source-file ~/.tmux.conf \\; display-message "tmux.conf reloaded"
|
|
|
720
725
|
# popup -E runs the command in a tmux popup overlay (modal). The popup
|
|
721
726
|
# closes when the command exits, returning the user to their previous
|
|
722
727
|
# pane. -w 100% -h 100% gives a full-screen popup (tmux-claude style).
|
|
723
|
-
|
|
724
|
-
|
|
728
|
+
#
|
|
729
|
+
# v0.19.42 — both binds use the ABSOLUTE PATH (substituted at write-time
|
|
730
|
+
# from __OMEGA_BIN__) because tmux display-popup spawns a non-interactive
|
|
731
|
+
# shell that doesn't source .bashrc/.zshrc — bare `omega` is not in PATH.
|
|
732
|
+
# Previously M-z used `run-shell -b` which executed in the background, so
|
|
733
|
+
# `tmux switch-client` ran in a forked process and never affected the
|
|
734
|
+
# foreground client. Result: "ça lance rien du tout". Fixed by going
|
|
735
|
+
# direct: display-popup the menu just like tmux-claude does.
|
|
736
|
+
bind-key -n M-/ display-popup -E -w 100% -h 100% "__OMEGA_BIN__ tmux switcher"
|
|
737
|
+
bind-key -n M-z display-popup -E -w 100% -h 100% "__OMEGA_BIN__ menu-tui"
|
|
725
738
|
|
|
726
739
|
# ════════════════════════════════════════════════════════════════════
|
|
727
740
|
# Pixel/CRT amber theme (tmux-claude inspired)
|
|
@@ -755,10 +768,23 @@ set -g mode-style "fg=default,bg=colour137"
|
|
|
755
768
|
"""
|
|
756
769
|
|
|
757
770
|
|
|
771
|
+
_OMEGA_BIN_PLACEHOLDER = "__OMEGA_BIN__"
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
def _resolve_omega_bin(omega_home: Path) -> str:
|
|
775
|
+
"""Return the absolute path to the omega CLI binary for the given
|
|
776
|
+
OMEGA_HOME. Used to substitute the __OMEGA_BIN__ placeholder in the
|
|
777
|
+
bundled tmux configs so tmux's display-popup (which spawns a
|
|
778
|
+
non-interactive shell) doesn't have to rely on PATH propagation.
|
|
779
|
+
"""
|
|
780
|
+
return str(omega_home / "Agentik_Tools" / "bin" / "omega")
|
|
781
|
+
|
|
782
|
+
|
|
758
783
|
def write_default_config(
|
|
759
784
|
target: str | Path | None = None,
|
|
760
785
|
*,
|
|
761
786
|
profile: str = "minimal",
|
|
787
|
+
omega_home: str | Path | None = None,
|
|
762
788
|
) -> Path:
|
|
763
789
|
"""Write a tmux config to ``target``.
|
|
764
790
|
|
|
@@ -773,52 +799,156 @@ def write_default_config(
|
|
|
773
799
|
or replace theirs entirely. We never silently overwrite ``~/.tmux.conf``.
|
|
774
800
|
Use :func:`install_into_home_tmux_conf` to do that explicitly + with
|
|
775
801
|
backup.
|
|
802
|
+
|
|
803
|
+
v0.19.42 — substitutes ``__OMEGA_BIN__`` placeholder in the M-z /
|
|
804
|
+
M-/ keybinds with the absolute path of the omega CLI binary, so the
|
|
805
|
+
binds work even when tmux's display-popup shell doesn't have
|
|
806
|
+
``$OMEGA_HOME/Agentik_Tools/bin`` in PATH.
|
|
776
807
|
"""
|
|
808
|
+
home = Path(omega_home or os.environ.get(
|
|
809
|
+
"OMEGA_HOME", str(Path.home() / "Omega")))
|
|
777
810
|
if target is None:
|
|
778
|
-
home = Path(os.environ.get("OMEGA_HOME",
|
|
779
|
-
str(Path.home() / "Omega")))
|
|
780
811
|
target = home / "Agentik_Tools" / "tmux.conf"
|
|
781
812
|
target = Path(target)
|
|
782
813
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
783
|
-
|
|
814
|
+
template = _PRO_CONFIG if profile == "pro" else _DEFAULT_CONFIG
|
|
815
|
+
content = template.replace(_OMEGA_BIN_PLACEHOLDER,
|
|
816
|
+
_resolve_omega_bin(home))
|
|
784
817
|
target.write_text(content)
|
|
785
818
|
return target
|
|
786
819
|
|
|
787
820
|
|
|
821
|
+
def _is_tmux_claude_config(text: str) -> bool:
|
|
822
|
+
"""Detect whether a ``~/.tmux.conf`` already hosts a tmux-claude
|
|
823
|
+
install (github.com/agentik-os/tmux-claude). If so, we MUST NOT
|
|
824
|
+
overwrite it — tmux-claude's session-manager-v2.sh provides the
|
|
825
|
+
user's Alt+Z / Alt+/ session navigator already, and clobbering it
|
|
826
|
+
breaks their setup.
|
|
827
|
+
|
|
828
|
+
Markers checked:
|
|
829
|
+
* literal ``tmux-claude`` mention (their banner / source-file
|
|
830
|
+
comment)
|
|
831
|
+
* ``session-manager-v2.sh`` (their flagship menu script)
|
|
832
|
+
"""
|
|
833
|
+
return ("tmux-claude" in text
|
|
834
|
+
or "session-manager-v2.sh" in text)
|
|
835
|
+
|
|
836
|
+
|
|
788
837
|
def install_into_home_tmux_conf(
|
|
789
838
|
*,
|
|
790
839
|
profile: str = "pro",
|
|
791
840
|
backup: bool = True,
|
|
792
841
|
omega_home: str | Path | None = None,
|
|
842
|
+
force: bool = False,
|
|
793
843
|
) -> dict[str, str]:
|
|
794
844
|
"""Write the chosen config to ``~/.tmux.conf`` with a timestamped backup.
|
|
795
845
|
|
|
796
|
-
|
|
797
|
-
|
|
846
|
+
v0.19.42 — RESPECTS tmux-claude. If ``~/.tmux.conf`` already hosts a
|
|
847
|
+
tmux-claude install, we DO NOT overwrite it. Instead we drop the
|
|
848
|
+
OmegaOS-specific keybinds at ``$OMEGA_HOME/Agentik_Tools/omega-tmux-add.conf``
|
|
849
|
+
and append one ``source-file`` line to ``~/.tmux.conf`` so both
|
|
850
|
+
coexist. Override with ``force=True``.
|
|
851
|
+
|
|
852
|
+
Returns:
|
|
853
|
+
* ``written`` — path of the file we wrote (~/.tmux.conf or the
|
|
854
|
+
add-on file)
|
|
855
|
+
* ``backup_path`` — backup of ~/.tmux.conf, empty if none
|
|
856
|
+
* ``bundled_copy`` — always written (the $OMEGA_HOME/Agentik_Tools/tmux.conf)
|
|
857
|
+
* ``profile`` — selected profile
|
|
858
|
+
* ``mode`` — "fresh" (we wrote a full conf) |
|
|
859
|
+
"preserved-tmux-claude" (we ADDED to the
|
|
860
|
+
existing tmux-claude conf without overwriting)
|
|
798
861
|
"""
|
|
799
862
|
import shutil as _sh
|
|
800
863
|
import time as _t
|
|
801
864
|
home_conf = Path.home() / ".tmux.conf"
|
|
802
|
-
backup_path = ""
|
|
803
|
-
if home_conf.exists() and backup:
|
|
804
|
-
ts = _t.strftime("%Y%m%d-%H%M%S")
|
|
805
|
-
backup_path = str(home_conf.with_suffix(f".conf.bak.{ts}"))
|
|
806
|
-
_sh.copy2(home_conf, backup_path)
|
|
807
|
-
content = _PRO_CONFIG if profile == "pro" else _DEFAULT_CONFIG
|
|
808
|
-
home_conf.write_text(content)
|
|
809
|
-
# Also drop it under $OMEGA_HOME so `omega tmux install` is the
|
|
810
|
-
# single source of truth that the engine sync mechanism can refresh.
|
|
811
|
-
_ = write_default_config(omega_home=None) if False else None
|
|
812
865
|
home = Path(omega_home or os.environ.get(
|
|
813
866
|
"OMEGA_HOME", str(Path.home() / "Omega")
|
|
814
867
|
))
|
|
868
|
+
template = _PRO_CONFIG if profile == "pro" else _DEFAULT_CONFIG
|
|
869
|
+
content = template.replace(_OMEGA_BIN_PLACEHOLDER,
|
|
870
|
+
_resolve_omega_bin(home))
|
|
871
|
+
|
|
872
|
+
# Always update the bundled copy under $OMEGA_HOME.
|
|
815
873
|
bundled = home / "Agentik_Tools" / "tmux.conf"
|
|
816
874
|
bundled.parent.mkdir(parents=True, exist_ok=True)
|
|
817
875
|
bundled.write_text(content)
|
|
876
|
+
|
|
877
|
+
backup_path = ""
|
|
878
|
+
if home_conf.exists() and backup:
|
|
879
|
+
ts = _t.strftime("%Y%m%d-%H%M%S")
|
|
880
|
+
backup_path = str(home_conf.with_suffix(f".conf.bak.{ts}"))
|
|
881
|
+
_sh.copy2(home_conf, backup_path)
|
|
882
|
+
|
|
883
|
+
# v0.19.42 — tmux-claude detection. If the user's ~/.tmux.conf is a
|
|
884
|
+
# tmux-claude install, leave it alone and provide an ADD-ON config.
|
|
885
|
+
if home_conf.exists() and not force:
|
|
886
|
+
existing = home_conf.read_text()
|
|
887
|
+
if _is_tmux_claude_config(existing):
|
|
888
|
+
addon_path = home / "Agentik_Tools" / "omega-tmux-add.conf"
|
|
889
|
+
addon_path.parent.mkdir(parents=True, exist_ok=True)
|
|
890
|
+
# Write a minimal OmegaOS-specific add-on: just the Ω bindings
|
|
891
|
+
# that don't collide with tmux-claude's M-z / M-/ (which we
|
|
892
|
+
# respect). Operator gets Omega via Ctrl-b z (prefix) or M-o.
|
|
893
|
+
addon = _omega_addon_config(home)
|
|
894
|
+
addon_path.write_text(addon)
|
|
895
|
+
# Append one source-file line if not already there.
|
|
896
|
+
source_line = f"source-file -q {addon_path}"
|
|
897
|
+
if source_line not in existing:
|
|
898
|
+
home_conf.write_text(existing.rstrip() + "\n\n"
|
|
899
|
+
+ "# OmegaOS — Ω bindings (added "
|
|
900
|
+
+ _t.strftime("%Y-%m-%d") + ")\n"
|
|
901
|
+
+ source_line + "\n")
|
|
902
|
+
return {
|
|
903
|
+
"written": str(addon_path),
|
|
904
|
+
"backup_path": backup_path,
|
|
905
|
+
"bundled_copy": str(bundled),
|
|
906
|
+
"profile": profile,
|
|
907
|
+
"mode": "preserved-tmux-claude",
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
# Fresh install (or --force): write the full OmegaOS conf.
|
|
911
|
+
home_conf.write_text(content)
|
|
818
912
|
return {
|
|
819
913
|
"written": str(home_conf),
|
|
820
914
|
"backup_path": backup_path,
|
|
821
915
|
"bundled_copy": str(bundled),
|
|
822
916
|
"profile": profile,
|
|
917
|
+
"mode": "fresh",
|
|
823
918
|
}
|
|
824
|
-
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
def _omega_addon_config(omega_home: Path) -> str:
|
|
922
|
+
"""The minimal OmegaOS keybinds + status-bar Ω indicator that we
|
|
923
|
+
can source from a tmux-claude config without conflict.
|
|
924
|
+
|
|
925
|
+
Conflicts with tmux-claude:
|
|
926
|
+
* M-z, M-/ → kept by tmux-claude (their session-manager-v2.sh)
|
|
927
|
+
* M-a → kept by tmux-claude (their AISB Command Center)
|
|
928
|
+
|
|
929
|
+
Free for OmegaOS:
|
|
930
|
+
* prefix+Ω → not bound by tmux-claude
|
|
931
|
+
* M-o → Alt+O for Omega menu (memorable)
|
|
932
|
+
* Ctrl-b Ω → prefix+Ω as an alternate
|
|
933
|
+
"""
|
|
934
|
+
omega_bin = _resolve_omega_bin(omega_home)
|
|
935
|
+
return f"""# ════════════════════════════════════════════════════════════════
|
|
936
|
+
# OmegaOS — additive bindings (sourced from ~/.tmux.conf)
|
|
937
|
+
# This file is generated by omega_engine.tmux. Safe to source alongside
|
|
938
|
+
# any existing tmux-claude install; we only bind keys tmux-claude leaves
|
|
939
|
+
# free.
|
|
940
|
+
#
|
|
941
|
+
# Free keys we use:
|
|
942
|
+
# * Alt+O → Omega menu (popup, full-screen)
|
|
943
|
+
# * Ctrl-b o → same, via prefix
|
|
944
|
+
# * Ctrl-b Ω → status-bar indicator only
|
|
945
|
+
#
|
|
946
|
+
# Reserved by tmux-claude (we DO NOT touch):
|
|
947
|
+
# * Alt+Z, Alt+/, Alt+A
|
|
948
|
+
# ════════════════════════════════════════════════════════════════
|
|
949
|
+
|
|
950
|
+
# Alt+O → Omega menu (popup, full-screen)
|
|
951
|
+
bind-key -n M-o display-popup -E -w 100% -h 100% "{omega_bin} menu-tui"
|
|
952
|
+
# Ctrl-b o → same via prefix
|
|
953
|
+
bind-key o display-popup -E -w 100% -h 100% "{omega_bin} menu-tui"
|
|
954
|
+
"""
|
package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313-pytest-8.4.2.pyc
CHANGED
|
Binary file
|
|
Binary file
|
package/omega/Agentik_Engine/tests/__pycache__/test_tmux_palette.cpython-313-pytest-8.4.2.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -219,5 +219,99 @@ class TestAisbChat(unittest.TestCase):
|
|
|
219
219
|
self.assertEqual(history[1].text, "ok, will look into it")
|
|
220
220
|
|
|
221
221
|
|
|
222
|
+
class TestBareOmegaAttachesNotPrints(unittest.TestCase):
|
|
223
|
+
"""v0.19.41 — bug fix. `omega` (no args) used to PRINT instructions
|
|
224
|
+
instead of doing the switch when invoked from inside tmux. Lock in
|
|
225
|
+
that cmd_menu calls tmux switch-client / select-window directly."""
|
|
226
|
+
|
|
227
|
+
def test_cmd_menu_when_in_tmux_calls_switch_client(self):
|
|
228
|
+
"""When $TMUX is set AND we're in a DIFFERENT session than Omega,
|
|
229
|
+
cmd_menu must run `tmux switch-client -t Omega` itself — not
|
|
230
|
+
print a hint and exit."""
|
|
231
|
+
import argparse
|
|
232
|
+
with mock.patch.dict("os.environ", {"TMUX": "/tmp/tmux-fake,1,0"}):
|
|
233
|
+
calls = []
|
|
234
|
+
class _FakeProc:
|
|
235
|
+
stdout = "some-other-session\n"
|
|
236
|
+
returncode = 0
|
|
237
|
+
def _fake_run(cmd, **kw):
|
|
238
|
+
calls.append(cmd)
|
|
239
|
+
return _FakeProc()
|
|
240
|
+
with mock.patch("subprocess.run", side_effect=_fake_run), \
|
|
241
|
+
mock.patch("shutil.which", return_value="/usr/bin/tmux"), \
|
|
242
|
+
mock.patch("omega_engine.tmux.is_alive", return_value=True):
|
|
243
|
+
from omega_engine.cli import cmd_menu
|
|
244
|
+
rc = cmd_menu(argparse.Namespace())
|
|
245
|
+
self.assertEqual(rc, 0)
|
|
246
|
+
switch_calls = [c for c in calls
|
|
247
|
+
if isinstance(c, list)
|
|
248
|
+
and len(c) >= 4
|
|
249
|
+
and c[:2] == ["tmux", "switch-client"]]
|
|
250
|
+
self.assertTrue(switch_calls,
|
|
251
|
+
f"cmd_menu must call `tmux switch-client -t Omega` when "
|
|
252
|
+
f"invoked from inside a different tmux session. "
|
|
253
|
+
f"Captured calls: {calls}")
|
|
254
|
+
|
|
255
|
+
def test_cmd_menu_when_already_in_omega_selects_menu_window(self):
|
|
256
|
+
"""If the user is ALREADY in the Omega session, cmd_menu must
|
|
257
|
+
select the :menu window (not switch-client — that'd be a no-op)."""
|
|
258
|
+
import argparse
|
|
259
|
+
with mock.patch.dict("os.environ", {"TMUX": "/tmp/tmux-fake,1,0"}):
|
|
260
|
+
calls = []
|
|
261
|
+
class _ProcS:
|
|
262
|
+
stdout = "Omega\n"; returncode = 0
|
|
263
|
+
class _ProcWins:
|
|
264
|
+
stdout = "menu\naisb\nhermes\n"; returncode = 0
|
|
265
|
+
def _fake_run(cmd, **kw):
|
|
266
|
+
calls.append(cmd)
|
|
267
|
+
if cmd[:3] == ["tmux", "display-message", "-p"]:
|
|
268
|
+
return _ProcS()
|
|
269
|
+
if cmd[:3] == ["tmux", "list-windows", "-t"]:
|
|
270
|
+
return _ProcWins()
|
|
271
|
+
class R: returncode = 0; stdout = ""
|
|
272
|
+
return R()
|
|
273
|
+
with mock.patch("subprocess.run", side_effect=_fake_run), \
|
|
274
|
+
mock.patch("shutil.which", return_value="/usr/bin/tmux"), \
|
|
275
|
+
mock.patch("omega_engine.tmux.is_alive", return_value=True):
|
|
276
|
+
from omega_engine.cli import cmd_menu
|
|
277
|
+
rc = cmd_menu(argparse.Namespace())
|
|
278
|
+
self.assertEqual(rc, 0)
|
|
279
|
+
select_calls = [c for c in calls
|
|
280
|
+
if isinstance(c, list)
|
|
281
|
+
and len(c) >= 4
|
|
282
|
+
and c[:2] == ["tmux", "select-window"]
|
|
283
|
+
and "Omega:menu" in c]
|
|
284
|
+
self.assertTrue(select_calls,
|
|
285
|
+
f"cmd_menu must call `tmux select-window -t Omega:menu` "
|
|
286
|
+
f"when already inside Omega. Captured: {calls}")
|
|
287
|
+
switch_calls = [c for c in calls
|
|
288
|
+
if isinstance(c, list)
|
|
289
|
+
and len(c) >= 2
|
|
290
|
+
and c[:2] == ["tmux", "switch-client"]]
|
|
291
|
+
self.assertEqual(switch_calls, [],
|
|
292
|
+
f"cmd_menu must NOT switch-client when already in Omega.")
|
|
293
|
+
|
|
294
|
+
def test_cmd_menu_does_not_print_hint_when_in_tmux(self):
|
|
295
|
+
"""REGRESSION — the v0.19.40 bug: cmd_menu printed
|
|
296
|
+
'Omega session is running. Switch to it with: ...' instead of
|
|
297
|
+
actually switching."""
|
|
298
|
+
import argparse, io, sys as _sys
|
|
299
|
+
with mock.patch.dict("os.environ", {"TMUX": "/tmp/tmux-fake,1,0"}):
|
|
300
|
+
class _Proc:
|
|
301
|
+
stdout = "Other\n"; returncode = 0
|
|
302
|
+
with mock.patch("subprocess.run", return_value=_Proc()), \
|
|
303
|
+
mock.patch("shutil.which", return_value="/usr/bin/tmux"), \
|
|
304
|
+
mock.patch("omega_engine.tmux.is_alive", return_value=True), \
|
|
305
|
+
mock.patch.object(_sys, "stdout", new_callable=io.StringIO) as captured:
|
|
306
|
+
from omega_engine.cli import cmd_menu
|
|
307
|
+
cmd_menu(argparse.Namespace())
|
|
308
|
+
out = captured.getvalue()
|
|
309
|
+
self.assertNotIn("Switch to it with", out,
|
|
310
|
+
f"v0.19.40 regression: cmd_menu printed switch instructions "
|
|
311
|
+
f"instead of switching. Output: {out!r}")
|
|
312
|
+
self.assertNotIn("tmux switch-client -t Omega", out,
|
|
313
|
+
"cmd_menu must DO the switch, not print the command")
|
|
314
|
+
|
|
315
|
+
|
|
222
316
|
if __name__ == "__main__":
|
|
223
317
|
unittest.main(verbosity=2)
|
|
@@ -90,5 +90,112 @@ class TestTmuxClaudePalette(unittest.TestCase):
|
|
|
90
90
|
)
|
|
91
91
|
|
|
92
92
|
|
|
93
|
+
class TestTmuxBindsFixedAndPathsAbsolute(unittest.TestCase):
|
|
94
|
+
"""v0.19.42 — regression locks for the broken M-z bind that left the
|
|
95
|
+
user staring at a black screen ("ça lance rien du tout")."""
|
|
96
|
+
|
|
97
|
+
def test_m_z_uses_display_popup_not_run_shell_bg(self):
|
|
98
|
+
from omega_engine.tmux import _PRO_CONFIG, _DEFAULT_CONFIG
|
|
99
|
+
for name, cfg in (("_PRO_CONFIG", _PRO_CONFIG),
|
|
100
|
+
("_DEFAULT_CONFIG", _DEFAULT_CONFIG)):
|
|
101
|
+
m_z_lines = [ln for ln in cfg.splitlines()
|
|
102
|
+
if ln.startswith("bind-key -n M-z")]
|
|
103
|
+
self.assertTrue(m_z_lines,
|
|
104
|
+
f"{name} must have bind-key -n M-z")
|
|
105
|
+
for ln in m_z_lines:
|
|
106
|
+
self.assertIn("display-popup", ln,
|
|
107
|
+
f"{name} M-z must use display-popup. Got: {ln}")
|
|
108
|
+
self.assertNotIn("run-shell -b", ln,
|
|
109
|
+
f"{name} M-z must NOT use `run-shell -b` (was the "
|
|
110
|
+
f"v0.19.40 bug — background switch-client breaks). "
|
|
111
|
+
f"Got: {ln}")
|
|
112
|
+
|
|
113
|
+
def test_omega_bin_placeholder_in_both_configs(self):
|
|
114
|
+
from omega_engine.tmux import _PRO_CONFIG, _DEFAULT_CONFIG
|
|
115
|
+
for name, cfg in (("_PRO_CONFIG", _PRO_CONFIG),
|
|
116
|
+
("_DEFAULT_CONFIG", _DEFAULT_CONFIG)):
|
|
117
|
+
self.assertIn("__OMEGA_BIN__", cfg,
|
|
118
|
+
f"{name} must use __OMEGA_BIN__ placeholder for the "
|
|
119
|
+
f"absolute omega path substitution at write-time")
|
|
120
|
+
|
|
121
|
+
def test_write_default_config_substitutes_omega_bin(self):
|
|
122
|
+
import tempfile
|
|
123
|
+
from pathlib import Path
|
|
124
|
+
from omega_engine.tmux import write_default_config
|
|
125
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
126
|
+
home = Path(tmp) / "Omega"
|
|
127
|
+
home.mkdir()
|
|
128
|
+
written = write_default_config(profile="pro", omega_home=home)
|
|
129
|
+
content = written.read_text()
|
|
130
|
+
self.assertNotIn("__OMEGA_BIN__", content,
|
|
131
|
+
"placeholder must be substituted away")
|
|
132
|
+
self.assertIn(str(home / "Agentik_Tools" / "bin" / "omega"),
|
|
133
|
+
content,
|
|
134
|
+
"the absolute omega-bin path must appear in the conf")
|
|
135
|
+
|
|
136
|
+
def test_install_preserves_tmux_claude(self):
|
|
137
|
+
import tempfile
|
|
138
|
+
from pathlib import Path
|
|
139
|
+
from unittest import mock
|
|
140
|
+
from omega_engine.tmux import install_into_home_tmux_conf
|
|
141
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
142
|
+
tmp = Path(tmp)
|
|
143
|
+
fake_home = tmp / "fake-home"; fake_home.mkdir()
|
|
144
|
+
omega_home = tmp / "Omega"; omega_home.mkdir()
|
|
145
|
+
home_conf = fake_home / ".tmux.conf"
|
|
146
|
+
home_conf.write_text(
|
|
147
|
+
"# tmux-claude config\nset -g mouse on\n"
|
|
148
|
+
"bind -n M-z display-popup -E "
|
|
149
|
+
"/home/user/.tmux/scripts/session-manager-v2.sh\n"
|
|
150
|
+
)
|
|
151
|
+
with mock.patch.object(Path, "home", lambda: fake_home):
|
|
152
|
+
result = install_into_home_tmux_conf(
|
|
153
|
+
profile="pro", omega_home=omega_home,
|
|
154
|
+
)
|
|
155
|
+
self.assertEqual(result["mode"], "preserved-tmux-claude",
|
|
156
|
+
"install must detect tmux-claude and not overwrite")
|
|
157
|
+
content = home_conf.read_text()
|
|
158
|
+
self.assertIn("session-manager-v2.sh", content,
|
|
159
|
+
"tmux-claude bind must survive — we did NOT clobber")
|
|
160
|
+
self.assertIn("omega-tmux-add.conf", content,
|
|
161
|
+
"install must add a `source-file` line for OmegaOS add-on")
|
|
162
|
+
addon = omega_home / "Agentik_Tools" / "omega-tmux-add.conf"
|
|
163
|
+
self.assertTrue(addon.exists(),
|
|
164
|
+
"add-on conf must be written at "
|
|
165
|
+
"$OMEGA_HOME/Agentik_Tools/omega-tmux-add.conf")
|
|
166
|
+
addon_content = addon.read_text()
|
|
167
|
+
self.assertIn("M-o", addon_content,
|
|
168
|
+
"Omega add-on must bind M-o (Alt+O — a key tmux-claude "
|
|
169
|
+
"leaves free)")
|
|
170
|
+
# Must NOT rebind M-z (reserved by tmux-claude). The string
|
|
171
|
+
# 'M-z' may appear in COMMENTS, so check there's no actual
|
|
172
|
+
# bind-key directive on M-z.
|
|
173
|
+
bind_m_z_lines = [ln for ln in addon_content.splitlines()
|
|
174
|
+
if ln.lstrip().startswith("bind-key")
|
|
175
|
+
and "M-z" in ln]
|
|
176
|
+
self.assertEqual(bind_m_z_lines, [],
|
|
177
|
+
f"Omega add-on must NOT rebind M-z — found: {bind_m_z_lines}")
|
|
178
|
+
|
|
179
|
+
def test_force_overwrites_tmux_claude(self):
|
|
180
|
+
import tempfile
|
|
181
|
+
from pathlib import Path
|
|
182
|
+
from unittest import mock
|
|
183
|
+
from omega_engine.tmux import install_into_home_tmux_conf
|
|
184
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
185
|
+
tmp = Path(tmp)
|
|
186
|
+
fake_home = tmp / "fake-home"; fake_home.mkdir()
|
|
187
|
+
omega_home = tmp / "Omega"; omega_home.mkdir()
|
|
188
|
+
home_conf = fake_home / ".tmux.conf"
|
|
189
|
+
home_conf.write_text("# tmux-claude\nsession-manager-v2.sh\n")
|
|
190
|
+
with mock.patch.object(Path, "home", lambda: fake_home):
|
|
191
|
+
result = install_into_home_tmux_conf(
|
|
192
|
+
profile="pro", omega_home=omega_home, force=True,
|
|
193
|
+
)
|
|
194
|
+
self.assertEqual(result["mode"], "fresh",
|
|
195
|
+
"force=True must do a fresh overwrite")
|
|
196
|
+
self.assertNotIn("session-manager-v2.sh", home_conf.read_text(),
|
|
197
|
+
"force=True must clobber tmux-claude")
|
|
198
|
+
|
|
199
|
+
|
|
93
200
|
if __name__ == "__main__":
|
|
94
201
|
unittest.main(verbosity=2)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.19.
|
|
1
|
+
0.19.42
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentikos/omega-os",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.42",
|
|
4
4
|
"description": "Omega OS — installable agentic operating system with verified-completion orchestration. Event-sourced engine, 8-block rack, autonomous agents, MCP.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"omega-os": "bin/omega-os.js"
|