@agentikos/omega-os 0.19.25 → 0.19.27
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__/tui.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/cli.py +119 -100
- package/omega/Agentik_Engine/omega_engine/tui.py +457 -0
- package/omega/Agentik_Engine/pyproject.toml +2 -1
- package/omega/Agentik_SSOT/VERSION +1 -1
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
@@ -2856,15 +2856,27 @@ def cmd_menu(_args: argparse.Namespace) -> int:
|
|
|
2856
2856
|
|
|
2857
2857
|
|
|
2858
2858
|
def cmd_menu_tui(_args: argparse.Namespace) -> int:
|
|
2859
|
-
"""`omega menu-tui` — the
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2859
|
+
"""`omega menu-tui` — the OmegaOS Textual TUI (v0.19.27+).
|
|
2860
|
+
|
|
2861
|
+
Modern boxed-layout TUI with slash commands (Hermès-style), sidebar
|
|
2862
|
+
categories, scrollable content log, footer keybindings. Falls back
|
|
2863
|
+
to a plain slash REPL if `textual` isn't installed (e.g. headless
|
|
2864
|
+
or before the engine venv finishes installing deps).
|
|
2865
|
+
|
|
2866
|
+
Delegates to `omega_engine.tui.run_tui()` which encapsulates both
|
|
2867
|
+
the Textual app and the plain REPL fallback.
|
|
2868
|
+
"""
|
|
2869
|
+
from omega_engine.tui import run_tui
|
|
2870
|
+
return run_tui()
|
|
2871
|
+
|
|
2872
|
+
|
|
2873
|
+
def _legacy_fzf_menu(_args: argparse.Namespace) -> int:
|
|
2874
|
+
"""v0.19.26 fzf menu — kept as fallback. Reachable via `omega menu-fzf`.
|
|
2875
|
+
|
|
2876
|
+
Actions run INLINE in the menu pane (not in dead background tmux
|
|
2877
|
+
windows). After every action, "press Enter to return to menu".
|
|
2878
|
+
Sessions (AISB/Hermes) still spawn as separate tmux sessions because
|
|
2879
|
+
they're meant to be talked to long-running.
|
|
2868
2880
|
"""
|
|
2869
2881
|
import os
|
|
2870
2882
|
import shlex
|
|
@@ -2881,102 +2893,101 @@ def cmd_menu_tui(_args: argparse.Namespace) -> int:
|
|
|
2881
2893
|
"(macOS) or `apt install fzf` (Linux)")
|
|
2882
2894
|
return 2
|
|
2883
2895
|
|
|
2884
|
-
# The menu actions. Each entry: (label, action_key).
|
|
2885
|
-
# Actions are dispatched in the if/elif block below.
|
|
2886
2896
|
def _build_menu() -> list[tuple[str, str]]:
|
|
2887
2897
|
active = _active_provider()
|
|
2888
2898
|
sessions = tmux.list_sessions(HOME)
|
|
2889
|
-
|
|
2890
|
-
("CHAT",
|
|
2891
|
-
(f"
|
|
2892
|
-
(f"
|
|
2893
|
-
(f"
|
|
2894
|
-
("PROJECTS",
|
|
2895
|
-
(f"
|
|
2896
|
-
(f"
|
|
2897
|
-
("AUDITS & MISSIONS",
|
|
2898
|
-
(f"
|
|
2899
|
-
(f"
|
|
2900
|
-
("INFRASTRUCTURE",
|
|
2901
|
-
(f"
|
|
2902
|
-
(f"
|
|
2903
|
-
(f"
|
|
2904
|
-
("HEALTH",
|
|
2905
|
-
(f"
|
|
2906
|
-
(f"
|
|
2907
|
-
("SCRAPE & SEARCH",
|
|
2908
|
-
(f"
|
|
2909
|
-
(f"
|
|
2910
|
-
("",
|
|
2911
|
-
(f"
|
|
2912
|
-
(f"
|
|
2899
|
+
return [
|
|
2900
|
+
("── CHAT ─────────────────────", "__sep__"),
|
|
2901
|
+
(f"AISB master chat (Claude Max OAuth)", "open:aisb"),
|
|
2902
|
+
(f"Hermès meta-companion (Anthropic API)", "open:hermes"),
|
|
2903
|
+
(f"Switch LLM provider [{active}]", "switch:provider"),
|
|
2904
|
+
("── PROJECTS ─────────────────", "__sep__"),
|
|
2905
|
+
(f"New project (Genesis pipeline)", "genesis:new"),
|
|
2906
|
+
(f"Open project shell", "project:open"),
|
|
2907
|
+
("── AUDITS & MISSIONS ────────", "__sep__"),
|
|
2908
|
+
(f"Run a mission", "run:mission"),
|
|
2909
|
+
(f"Quality Arsenal — list audits", "audit:menu"),
|
|
2910
|
+
("── INFRASTRUCTURE ───────────", "__sep__"),
|
|
2911
|
+
(f"Sessions ({len(sessions)} active) — pick to attach", "sessions:list"),
|
|
2912
|
+
(f"Accounts — Claude Max + per-provider auth", "accounts:menu"),
|
|
2913
|
+
(f"Vault — secrets", "vault:menu"),
|
|
2914
|
+
("── HEALTH ───────────────────", "__sep__"),
|
|
2915
|
+
(f"omega doctor", "cmd:doctor"),
|
|
2916
|
+
(f"omega status", "cmd:status"),
|
|
2917
|
+
("── SCRAPE & SEARCH ──────────", "__sep__"),
|
|
2918
|
+
(f"Stealth scrape a URL (CloakBrowser)", "scrape:cloak"),
|
|
2919
|
+
(f"Fast scrape a URL (Scrapling)", "scrape:scrapling"),
|
|
2920
|
+
("── EXIT ─────────────────────", "__sep__"),
|
|
2921
|
+
(f"Detach (Ctrl-b d also works)", "detach"),
|
|
2922
|
+
(f"Quit Omega session (kills the tmux session)", "quit:kill"),
|
|
2913
2923
|
]
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
tmux._tmux("send-keys", "-t", f"Omega:{window}", command, "Enter")
|
|
2924
|
+
|
|
2925
|
+
def _run_inline(cmd_argv: list[str] | str, *, shell: bool = False) -> None:
|
|
2926
|
+
"""Run a command in the current pane, stream stdout/stderr,
|
|
2927
|
+
then prompt the user to return to the menu."""
|
|
2928
|
+
os.system("clear")
|
|
2929
|
+
print(f" Ω › running: {cmd_argv if shell else ' '.join(cmd_argv)}")
|
|
2930
|
+
print(" " + "─" * 60)
|
|
2931
|
+
if shell:
|
|
2932
|
+
subprocess.run(cmd_argv, shell=True)
|
|
2924
2933
|
else:
|
|
2925
|
-
|
|
2926
|
-
|
|
2934
|
+
subprocess.run(cmd_argv)
|
|
2935
|
+
print()
|
|
2936
|
+
print(" " + "─" * 60)
|
|
2937
|
+
try:
|
|
2938
|
+
input(" press Enter to return to Omega menu… ")
|
|
2939
|
+
except (EOFError, KeyboardInterrupt):
|
|
2940
|
+
pass
|
|
2941
|
+
|
|
2942
|
+
def _prompt(label: str) -> str:
|
|
2943
|
+
try:
|
|
2944
|
+
return input(f" {label}: ").strip()
|
|
2945
|
+
except (EOFError, KeyboardInterrupt):
|
|
2946
|
+
return ""
|
|
2927
2947
|
|
|
2928
|
-
print(f"\n Omega v{__version__} — type a number to refresh, Ctrl-c to exit.\n")
|
|
2929
2948
|
while True:
|
|
2930
2949
|
items = _build_menu()
|
|
2931
|
-
|
|
2932
|
-
lines = []
|
|
2933
|
-
for label, key in items:
|
|
2934
|
-
if key == "__separator__":
|
|
2935
|
-
# Visual section header line.
|
|
2936
|
-
lines.append(f"── {label} " + "─" * max(0, 50 - len(label)))
|
|
2937
|
-
else:
|
|
2938
|
-
lines.append(label)
|
|
2950
|
+
lines = [label for label, _ in items]
|
|
2939
2951
|
fzf_input = "\n".join(lines)
|
|
2940
2952
|
try:
|
|
2941
2953
|
proc = subprocess.run(
|
|
2942
2954
|
["fzf",
|
|
2943
2955
|
"--prompt=Ω › ",
|
|
2944
|
-
"--header=Omega
|
|
2956
|
+
"--header=Omega — Enter to pick, Esc to refresh, "
|
|
2957
|
+
"Ctrl-c to exit",
|
|
2945
2958
|
"--layout=reverse",
|
|
2946
2959
|
"--height=100%",
|
|
2947
2960
|
"--border=rounded",
|
|
2948
2961
|
"--no-multi",
|
|
2949
|
-
"--info=inline"
|
|
2962
|
+
"--info=inline",
|
|
2963
|
+
"--ansi"],
|
|
2950
2964
|
input=fzf_input, capture_output=True, text=True,
|
|
2951
2965
|
)
|
|
2952
2966
|
except KeyboardInterrupt:
|
|
2953
2967
|
return 0
|
|
2954
2968
|
if proc.returncode != 0:
|
|
2955
|
-
# Esc
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
except (EOFError, KeyboardInterrupt):
|
|
2961
|
-
return 0
|
|
2962
|
-
pick = proc.stdout.strip()
|
|
2969
|
+
# Esc → refresh.
|
|
2970
|
+
continue
|
|
2971
|
+
# FIX (v0.19.26): match label EXACTLY, no strip — fzf returns the
|
|
2972
|
+
# full line including any chars we put in. Just trim trailing \n.
|
|
2973
|
+
pick = proc.stdout.rstrip("\n")
|
|
2963
2974
|
action = None
|
|
2964
2975
|
for label, key in items:
|
|
2965
|
-
|
|
2966
|
-
if key == "__separator__" else label)
|
|
2967
|
-
if display == pick and key != "__separator__":
|
|
2976
|
+
if label == pick and key != "__sep__":
|
|
2968
2977
|
action = key
|
|
2969
2978
|
break
|
|
2970
2979
|
if action is None:
|
|
2971
2980
|
continue
|
|
2972
2981
|
|
|
2973
|
-
# Dispatch
|
|
2982
|
+
# === Dispatch ===
|
|
2974
2983
|
if action == "detach":
|
|
2975
2984
|
subprocess.run(["tmux", "detach-client"])
|
|
2976
2985
|
return 0
|
|
2977
2986
|
if action == "quit:kill":
|
|
2978
2987
|
subprocess.run(["tmux", "kill-session", "-t", "Omega"])
|
|
2979
2988
|
return 0
|
|
2989
|
+
|
|
2990
|
+
# Chat sessions — switch tmux client to the chat session.
|
|
2980
2991
|
if action == "open:aisb":
|
|
2981
2992
|
tmux.spawn_aisb_chat(HOME, force_replace=False)
|
|
2982
2993
|
subprocess.run(["tmux", "switch-client", "-t", "AISB-chat"])
|
|
@@ -2985,57 +2996,62 @@ def cmd_menu_tui(_args: argparse.Namespace) -> int:
|
|
|
2985
2996
|
tmux.spawn_hermes_chat(HOME, force_replace=False)
|
|
2986
2997
|
subprocess.run(["tmux", "switch-client", "-t", "Hermes-chat"])
|
|
2987
2998
|
continue
|
|
2999
|
+
|
|
3000
|
+
# Inline actions — run in the menu pane, prompt to return.
|
|
2988
3001
|
if action == "switch:provider":
|
|
2989
|
-
|
|
3002
|
+
_run_inline([OMEGA_BIN, "switch"])
|
|
2990
3003
|
continue
|
|
2991
3004
|
if action == "genesis:new":
|
|
2992
|
-
|
|
2993
|
-
|
|
3005
|
+
slug = _prompt("project slug (alphanumeric, _-)")
|
|
3006
|
+
if slug:
|
|
3007
|
+
_run_inline([OMEGA_BIN, "genesis", "new", slug])
|
|
2994
3008
|
continue
|
|
2995
3009
|
if action == "project:open":
|
|
2996
|
-
|
|
3010
|
+
_run_inline("ls -la ~/Omega/Agentik_Coding/projects/ 2>/dev/null "
|
|
3011
|
+
"&& echo && echo '(cd into one to start working)'",
|
|
3012
|
+
shell=True)
|
|
2997
3013
|
continue
|
|
2998
3014
|
if action == "run:mission":
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3015
|
+
intent = _prompt("mission intent (free-form)")
|
|
3016
|
+
if intent:
|
|
3017
|
+
_run_inline([OMEGA_BIN, "run", intent])
|
|
3002
3018
|
continue
|
|
3003
3019
|
if action == "audit:menu":
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3020
|
+
_run_inline(
|
|
3021
|
+
"echo 'Quality Arsenal — 17 forensic audits:' && "
|
|
3022
|
+
"echo ' /codeaudit /uiuxaudit /flowaudit /debugaudit' && "
|
|
3023
|
+
"echo ' /featureaudit /perfaudit /secaudit /a11yaudit' && "
|
|
3024
|
+
"echo ' /seoaudit /dataaudit /apiaudit /copyaudit' && "
|
|
3025
|
+
"echo ' /dxaudit /motionaudit /automationaudit' && "
|
|
3026
|
+
"echo ' /logicaudit /retentionaudit /refontaudit' && "
|
|
3027
|
+
"echo && echo 'invoke from any claude session, or:' && "
|
|
3028
|
+
f"echo ' {OMEGA_BIN} audit <id>'",
|
|
3029
|
+
shell=True)
|
|
3012
3030
|
continue
|
|
3013
3031
|
if action == "sessions:list":
|
|
3014
|
-
|
|
3032
|
+
_run_inline("tmux ls", shell=True)
|
|
3015
3033
|
continue
|
|
3016
3034
|
if action == "accounts:menu":
|
|
3017
|
-
|
|
3018
|
-
f"{shlex.quote(OMEGA_BIN)} account list")
|
|
3035
|
+
_run_inline([OMEGA_BIN, "account", "list"])
|
|
3019
3036
|
continue
|
|
3020
3037
|
if action == "vault:menu":
|
|
3021
|
-
|
|
3022
|
-
f"{shlex.quote(OMEGA_BIN)} vault status")
|
|
3038
|
+
_run_inline([OMEGA_BIN, "vault", "status"])
|
|
3023
3039
|
continue
|
|
3024
3040
|
if action == "cmd:doctor":
|
|
3025
|
-
|
|
3041
|
+
_run_inline([OMEGA_BIN, "doctor"])
|
|
3026
3042
|
continue
|
|
3027
3043
|
if action == "cmd:status":
|
|
3028
|
-
|
|
3044
|
+
_run_inline([OMEGA_BIN, "status"])
|
|
3029
3045
|
continue
|
|
3030
3046
|
if action == "scrape:cloak":
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3047
|
+
url = _prompt("URL")
|
|
3048
|
+
if url:
|
|
3049
|
+
_run_inline([OMEGA_BIN, "scrape", url])
|
|
3034
3050
|
continue
|
|
3035
3051
|
if action == "scrape:scrapling":
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3052
|
+
url = _prompt("URL")
|
|
3053
|
+
if url:
|
|
3054
|
+
_run_inline([OMEGA_BIN, "scrape", url, "--engine", "scrapling"])
|
|
3039
3055
|
continue
|
|
3040
3056
|
|
|
3041
3057
|
|
|
@@ -4328,9 +4344,12 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
4328
4344
|
help="open the interactive whiptail menu (legacy)"
|
|
4329
4345
|
).set_defaults(fn=cmd_menu_whiptail)
|
|
4330
4346
|
sub.add_parser("menu-tui",
|
|
4331
|
-
help="the
|
|
4332
|
-
"
|
|
4347
|
+
help="the Textual TUI inside the Omega tmux session "
|
|
4348
|
+
"(don't call directly — bare `omega` lands you here)"
|
|
4333
4349
|
).set_defaults(fn=cmd_menu_tui)
|
|
4350
|
+
sub.add_parser("menu-fzf",
|
|
4351
|
+
help="legacy v0.19.26 fzf menu fallback"
|
|
4352
|
+
).set_defaults(fn=_legacy_fzf_menu)
|
|
4334
4353
|
sub.add_parser("version", help="print the engine version").set_defaults(fn=cmd_version)
|
|
4335
4354
|
|
|
4336
4355
|
# `omega switch <provider>` — hot-swap the LLM CLI used for new chats.
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
"""OmegaOS interactive Textual TUI — the v0.19.27 redesign.
|
|
2
|
+
|
|
3
|
+
User: "l'objectif c'est que ça fonctionne comme
|
|
4
|
+
https://www.agentik-os.com/services/agentik-coding-workflow ! avec notre
|
|
5
|
+
nouveau system omegaOS qui est noramlement une refont de tout ca ! et
|
|
6
|
+
aussi pour ta reponse - le TUI mets tout en place pour OmegaOS"
|
|
7
|
+
|
|
8
|
+
Architecture mirror of the Agentik Coding Workflow v2.5.1 doc:
|
|
9
|
+
|
|
10
|
+
- 4-level hierarchy visible (Human → AISB → Oracle → Worker)
|
|
11
|
+
- Telegram + terminal-native (no web dashboard)
|
|
12
|
+
- Terminal entry = this TUI
|
|
13
|
+
- Sessions exposed via tmux; TUI is the OmegaOS control surface
|
|
14
|
+
|
|
15
|
+
Layout (Textual, mouse + keyboard, slash-command driven like Hermès):
|
|
16
|
+
|
|
17
|
+
┌── Ω Omega OS v0.19.27 • provider: claude_code • sessions: 3 ──┐
|
|
18
|
+
│ CATEGORY │ CONTENT │
|
|
19
|
+
│ ──────── │ ─────── │
|
|
20
|
+
│ ▶ Chat │ (selected category's content here │
|
|
21
|
+
│ Projects │ — sessions list, audit menu, │
|
|
22
|
+
│ Audits │ doctor output, etc.) │
|
|
23
|
+
│ Sessions │ │
|
|
24
|
+
│ Accounts │ │
|
|
25
|
+
│ Vault │ │
|
|
26
|
+
│ Health │ │
|
|
27
|
+
│ Scrape │ │
|
|
28
|
+
│ │ │
|
|
29
|
+
├────────────────────────────┴─────────────────────────────────────┤
|
|
30
|
+
│ Ω › /<slash command> │
|
|
31
|
+
├──────────────────────────────────────────────────────────────────┤
|
|
32
|
+
│ q quit • d detach • ? help • / focus slash input │
|
|
33
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
34
|
+
|
|
35
|
+
Slash commands (Hermès-style):
|
|
36
|
+
/chat aisb /chat hermes /aisb /hermes
|
|
37
|
+
/switch <provider> /switch (show current)
|
|
38
|
+
/audit <id> /mission "<intent>" /scrape <url>
|
|
39
|
+
/genesis <slug> /doctor /status
|
|
40
|
+
/vault [<key>] /accounts /sessions
|
|
41
|
+
/quit /detach /help
|
|
42
|
+
"""
|
|
43
|
+
from __future__ import annotations
|
|
44
|
+
|
|
45
|
+
import os
|
|
46
|
+
import subprocess
|
|
47
|
+
import shlex
|
|
48
|
+
from pathlib import Path
|
|
49
|
+
from typing import Any
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
from textual.app import App, ComposeResult
|
|
53
|
+
from textual.widgets import (
|
|
54
|
+
Header, Footer, Input, RichLog, ListView, ListItem, Label,
|
|
55
|
+
)
|
|
56
|
+
from textual.containers import Horizontal, Vertical
|
|
57
|
+
from textual.binding import Binding
|
|
58
|
+
TEXTUAL_AVAILABLE = True
|
|
59
|
+
except ImportError:
|
|
60
|
+
TEXTUAL_AVAILABLE = False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
# Sidebar category map
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
CATEGORIES: list[tuple[str, str]] = [
|
|
68
|
+
("chat", "Chat — AISB & Hermès"),
|
|
69
|
+
("projects", "Projects — Genesis & shells"),
|
|
70
|
+
("audits", "Audits — Quality Arsenal (17 forensic)"),
|
|
71
|
+
("missions", "Missions — run with verified completion"),
|
|
72
|
+
("sessions", "Sessions — live tmux sessions"),
|
|
73
|
+
("accounts", "Accounts — Claude Max + provider auth"),
|
|
74
|
+
("vault", "Vault — encrypted secrets"),
|
|
75
|
+
("health", "Health — doctor + status"),
|
|
76
|
+
("scrape", "Scrape — CloakBrowser + Scrapling"),
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
# Slash command dispatcher
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class CommandResult:
|
|
86
|
+
"""A command's effect — text to display + optional side-effects."""
|
|
87
|
+
|
|
88
|
+
def __init__(self, *, text: str = "", session_jump: str = "",
|
|
89
|
+
exit_tui: bool = False, detach: bool = False) -> None:
|
|
90
|
+
self.text = text
|
|
91
|
+
self.session_jump = session_jump
|
|
92
|
+
self.exit_tui = exit_tui
|
|
93
|
+
self.detach = detach
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _omega_bin() -> str:
|
|
97
|
+
home = Path(os.environ.get("OMEGA_HOME", str(Path.home() / "Omega")))
|
|
98
|
+
return str(home / "Agentik_Tools" / "bin" / "omega")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _run_omega(*args: str) -> str:
|
|
102
|
+
"""Shell out to the omega CLI and capture its stdout/stderr."""
|
|
103
|
+
try:
|
|
104
|
+
proc = subprocess.run([_omega_bin(), *args],
|
|
105
|
+
capture_output=True, text=True, timeout=120)
|
|
106
|
+
return (proc.stdout + proc.stderr).rstrip()
|
|
107
|
+
except subprocess.SubprocessError as exc:
|
|
108
|
+
return f" error: {exc}"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _switch_client(target: str) -> None:
|
|
112
|
+
subprocess.run(["tmux", "switch-client", "-t", target],
|
|
113
|
+
capture_output=True)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def dispatch_slash(line: str) -> CommandResult:
|
|
117
|
+
"""Parse a slash command and return the effect to apply."""
|
|
118
|
+
line = line.strip()
|
|
119
|
+
if not line:
|
|
120
|
+
return CommandResult(text="(empty)")
|
|
121
|
+
# Allow both "/foo bar" and "foo bar" inputs.
|
|
122
|
+
if line.startswith("/"):
|
|
123
|
+
line = line[1:]
|
|
124
|
+
try:
|
|
125
|
+
argv = shlex.split(line)
|
|
126
|
+
except ValueError as exc:
|
|
127
|
+
return CommandResult(text=f" parse error: {exc}")
|
|
128
|
+
if not argv:
|
|
129
|
+
return CommandResult(text="(empty)")
|
|
130
|
+
cmd, *rest = argv
|
|
131
|
+
|
|
132
|
+
# Quit / detach
|
|
133
|
+
if cmd in ("quit", "q", "exit"):
|
|
134
|
+
return CommandResult(exit_tui=True)
|
|
135
|
+
if cmd in ("detach", "d"):
|
|
136
|
+
return CommandResult(detach=True)
|
|
137
|
+
|
|
138
|
+
# Chat sessions
|
|
139
|
+
if cmd == "aisb" or (cmd == "chat" and rest and rest[0] == "aisb"):
|
|
140
|
+
from omega_engine import tmux as _tx
|
|
141
|
+
_tx.spawn_aisb_chat(Path(os.environ.get(
|
|
142
|
+
"OMEGA_HOME", str(Path.home() / "Omega"))))
|
|
143
|
+
return CommandResult(
|
|
144
|
+
text=" AISB-chat spawned — switching client…",
|
|
145
|
+
session_jump="AISB-chat",
|
|
146
|
+
)
|
|
147
|
+
if cmd == "hermes" or (cmd == "chat" and rest and rest[0] == "hermes"):
|
|
148
|
+
from omega_engine import tmux as _tx
|
|
149
|
+
_tx.spawn_hermes_chat(Path(os.environ.get(
|
|
150
|
+
"OMEGA_HOME", str(Path.home() / "Omega"))))
|
|
151
|
+
return CommandResult(
|
|
152
|
+
text=" Hermes-chat spawned — switching client…",
|
|
153
|
+
session_jump="Hermes-chat",
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Provider hot-swap
|
|
157
|
+
if cmd == "switch":
|
|
158
|
+
if rest:
|
|
159
|
+
return CommandResult(text=_run_omega("switch", rest[0]))
|
|
160
|
+
return CommandResult(text=_run_omega("switch"))
|
|
161
|
+
|
|
162
|
+
# Audits + missions
|
|
163
|
+
if cmd == "audit":
|
|
164
|
+
if rest:
|
|
165
|
+
return CommandResult(text=_run_omega("audit", *rest))
|
|
166
|
+
return CommandResult(text=(
|
|
167
|
+
" Quality Arsenal — 17 forensic audits:\n"
|
|
168
|
+
" /codeaudit /uiuxaudit /flowaudit /debugaudit\n"
|
|
169
|
+
" /featureaudit /perfaudit /secaudit /a11yaudit\n"
|
|
170
|
+
" /seoaudit /dataaudit /apiaudit /copyaudit\n"
|
|
171
|
+
" /dxaudit /motionaudit /automationaudit\n"
|
|
172
|
+
" /logicaudit /retentionaudit /refontaudit\n"
|
|
173
|
+
" invoke from any claude session, or:\n"
|
|
174
|
+
f" omega audit <id>"
|
|
175
|
+
))
|
|
176
|
+
if cmd in ("mission", "run"):
|
|
177
|
+
if not rest:
|
|
178
|
+
return CommandResult(text=" usage: /mission \"<intent>\"")
|
|
179
|
+
return CommandResult(text=_run_omega("run", " ".join(rest)))
|
|
180
|
+
|
|
181
|
+
# Genesis (new project)
|
|
182
|
+
if cmd == "genesis":
|
|
183
|
+
if not rest:
|
|
184
|
+
return CommandResult(text=_run_omega("genesis", "questions"))
|
|
185
|
+
return CommandResult(text=_run_omega("genesis", "new", rest[0]))
|
|
186
|
+
|
|
187
|
+
# Vault
|
|
188
|
+
if cmd == "vault":
|
|
189
|
+
if not rest:
|
|
190
|
+
return CommandResult(text=_run_omega("vault", "status"))
|
|
191
|
+
return CommandResult(text=_run_omega("vault", *rest))
|
|
192
|
+
|
|
193
|
+
# Accounts + sessions
|
|
194
|
+
if cmd == "accounts":
|
|
195
|
+
return CommandResult(text=_run_omega("account", "list"))
|
|
196
|
+
if cmd == "sessions":
|
|
197
|
+
try:
|
|
198
|
+
proc = subprocess.run(["tmux", "ls"],
|
|
199
|
+
capture_output=True, text=True, timeout=10)
|
|
200
|
+
return CommandResult(text=proc.stdout + proc.stderr)
|
|
201
|
+
except subprocess.SubprocessError as exc:
|
|
202
|
+
return CommandResult(text=f" error: {exc}")
|
|
203
|
+
|
|
204
|
+
# Health
|
|
205
|
+
if cmd == "doctor":
|
|
206
|
+
return CommandResult(text=_run_omega("doctor"))
|
|
207
|
+
if cmd == "status":
|
|
208
|
+
return CommandResult(text=_run_omega("status"))
|
|
209
|
+
|
|
210
|
+
# Scrape
|
|
211
|
+
if cmd == "scrape":
|
|
212
|
+
if not rest:
|
|
213
|
+
return CommandResult(text=" usage: /scrape <url> [--engine scrapling]")
|
|
214
|
+
return CommandResult(text=_run_omega("scrape", *rest))
|
|
215
|
+
|
|
216
|
+
# Help
|
|
217
|
+
if cmd in ("help", "?"):
|
|
218
|
+
return CommandResult(text=(
|
|
219
|
+
" SLASH COMMANDS\n"
|
|
220
|
+
" ──────────────\n"
|
|
221
|
+
" /aisb switch to AISB master chat (Max OAuth)\n"
|
|
222
|
+
" /hermes switch to Hermès chat (Anthropic API)\n"
|
|
223
|
+
" /switch <provider> hot-swap LLM provider (Hermes-style)\n"
|
|
224
|
+
" /mission \"<intent>\" run a verified-completion mission\n"
|
|
225
|
+
" /audit <id> run a forensic audit (codeaudit, …)\n"
|
|
226
|
+
" /genesis <slug> start a new project via Genesis pipeline\n"
|
|
227
|
+
" /scrape <url> stealth scrape (CloakBrowser default)\n"
|
|
228
|
+
" /scrape <url> --engine scrapling fast scrape (Scrapling)\n"
|
|
229
|
+
" /sessions list active tmux sessions\n"
|
|
230
|
+
" /accounts list Claude Max account pool\n"
|
|
231
|
+
" /vault [key] vault status or read key\n"
|
|
232
|
+
" /doctor full health check (omega doctor)\n"
|
|
233
|
+
" /status task state (omega status)\n"
|
|
234
|
+
" /detach detach from Omega tmux session\n"
|
|
235
|
+
" /quit quit the TUI (kill Omega session)\n"
|
|
236
|
+
" /help show this help\n"
|
|
237
|
+
"\n"
|
|
238
|
+
" KEYBINDINGS\n"
|
|
239
|
+
" ───────────\n"
|
|
240
|
+
" / focus the slash input\n"
|
|
241
|
+
" ? this help\n"
|
|
242
|
+
" d detach\n"
|
|
243
|
+
" q quit\n"
|
|
244
|
+
" Tab navigate categories"
|
|
245
|
+
))
|
|
246
|
+
|
|
247
|
+
return CommandResult(text=(
|
|
248
|
+
f" unknown command: /{cmd}\n"
|
|
249
|
+
f" type /help for the list of commands"
|
|
250
|
+
))
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
# ---------------------------------------------------------------------------
|
|
254
|
+
# The Textual app
|
|
255
|
+
# ---------------------------------------------------------------------------
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
if TEXTUAL_AVAILABLE:
|
|
259
|
+
|
|
260
|
+
class OmegaTUI(App):
|
|
261
|
+
"""OmegaOS Textual TUI — the v0.19.27 control surface."""
|
|
262
|
+
|
|
263
|
+
CSS = """
|
|
264
|
+
Screen {
|
|
265
|
+
background: $background;
|
|
266
|
+
}
|
|
267
|
+
#sidebar {
|
|
268
|
+
width: 32;
|
|
269
|
+
border-right: tall $primary;
|
|
270
|
+
padding: 1 1;
|
|
271
|
+
}
|
|
272
|
+
#content {
|
|
273
|
+
padding: 1 2;
|
|
274
|
+
}
|
|
275
|
+
#slash {
|
|
276
|
+
border-top: tall $primary;
|
|
277
|
+
background: $surface;
|
|
278
|
+
}
|
|
279
|
+
Header {
|
|
280
|
+
background: $primary;
|
|
281
|
+
color: $text;
|
|
282
|
+
}
|
|
283
|
+
ListView > ListItem.--highlight {
|
|
284
|
+
background: $primary;
|
|
285
|
+
color: $text;
|
|
286
|
+
}
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
BINDINGS = [
|
|
290
|
+
Binding("q", "quit_omega", "Quit"),
|
|
291
|
+
Binding("d", "detach_omega", "Detach"),
|
|
292
|
+
Binding("question_mark", "help_omega", "Help"),
|
|
293
|
+
Binding("slash", "focus_slash", "Slash"),
|
|
294
|
+
]
|
|
295
|
+
|
|
296
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
297
|
+
super().__init__(*args, **kwargs)
|
|
298
|
+
self._content: RichLog | None = None
|
|
299
|
+
self._slash: Input | None = None
|
|
300
|
+
|
|
301
|
+
def compose(self) -> ComposeResult:
|
|
302
|
+
from omega_engine import __version__
|
|
303
|
+
yield Header(name=f"Ω Omega OS v{__version__}", show_clock=True)
|
|
304
|
+
with Horizontal():
|
|
305
|
+
with Vertical(id="sidebar"):
|
|
306
|
+
yield Label("Categories", id="sidebar-title")
|
|
307
|
+
items = [ListItem(Label(name)) for _id, name in CATEGORIES]
|
|
308
|
+
yield ListView(*items, id="cats")
|
|
309
|
+
with Vertical(id="content-wrap"):
|
|
310
|
+
log = RichLog(id="content", highlight=True, markup=True,
|
|
311
|
+
wrap=True)
|
|
312
|
+
self._content = log
|
|
313
|
+
yield log
|
|
314
|
+
self._slash = Input(placeholder="Ω › type a slash command "
|
|
315
|
+
"(/help for list, /quit to exit)",
|
|
316
|
+
id="slash")
|
|
317
|
+
yield self._slash
|
|
318
|
+
yield Footer()
|
|
319
|
+
|
|
320
|
+
def on_mount(self) -> None:
|
|
321
|
+
self._write_welcome()
|
|
322
|
+
if self._slash:
|
|
323
|
+
self._slash.focus()
|
|
324
|
+
|
|
325
|
+
def _write_welcome(self) -> None:
|
|
326
|
+
if not self._content:
|
|
327
|
+
return
|
|
328
|
+
from omega_engine import __version__
|
|
329
|
+
self._content.write(
|
|
330
|
+
f"[bold #D97757]Ω Omega OS v{__version__}[/]\n"
|
|
331
|
+
"Type /help to see all slash commands.\n"
|
|
332
|
+
"\n"
|
|
333
|
+
"Quick start:\n"
|
|
334
|
+
" [bold]/aisb[/] chat with the AISB master agent\n"
|
|
335
|
+
" [bold]/hermes[/] chat with Hermès (Anthropic API)\n"
|
|
336
|
+
" [bold]/switch[/] see + hot-swap LLM provider\n"
|
|
337
|
+
" [bold]/mission \"...\"[/] run a verified-completion mission\n"
|
|
338
|
+
" [bold]/scrape <url>[/] stealth scrape via CloakBrowser\n"
|
|
339
|
+
" [bold]/audit <id>[/] run a Quality Arsenal audit\n"
|
|
340
|
+
" [bold]/doctor[/] full health check\n"
|
|
341
|
+
"\n"
|
|
342
|
+
"Click a category in the sidebar, or type a / command.\n"
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
def action_quit_omega(self) -> None:
|
|
346
|
+
subprocess.run(["tmux", "kill-session", "-t", "Omega"],
|
|
347
|
+
capture_output=True)
|
|
348
|
+
self.exit()
|
|
349
|
+
|
|
350
|
+
def action_detach_omega(self) -> None:
|
|
351
|
+
subprocess.run(["tmux", "detach-client"], capture_output=True)
|
|
352
|
+
self.exit()
|
|
353
|
+
|
|
354
|
+
def action_help_omega(self) -> None:
|
|
355
|
+
res = dispatch_slash("/help")
|
|
356
|
+
if self._content:
|
|
357
|
+
self._content.write(res.text)
|
|
358
|
+
|
|
359
|
+
def action_focus_slash(self) -> None:
|
|
360
|
+
if self._slash:
|
|
361
|
+
self._slash.focus()
|
|
362
|
+
|
|
363
|
+
def on_list_view_selected(self, event: Any) -> None:
|
|
364
|
+
"""User clicked a sidebar category — render that category's content."""
|
|
365
|
+
idx = event.list_view.index or 0
|
|
366
|
+
if idx >= len(CATEGORIES):
|
|
367
|
+
return
|
|
368
|
+
cat_id = CATEGORIES[idx][0]
|
|
369
|
+
self._render_category(cat_id)
|
|
370
|
+
|
|
371
|
+
def _render_category(self, cat_id: str) -> None:
|
|
372
|
+
if not self._content:
|
|
373
|
+
return
|
|
374
|
+
mapping = {
|
|
375
|
+
"chat": "/help", # show overall help — chat actions in help
|
|
376
|
+
"projects": "/genesis",
|
|
377
|
+
"audits": "/audit",
|
|
378
|
+
"missions": "/help",
|
|
379
|
+
"sessions": "/sessions",
|
|
380
|
+
"accounts": "/accounts",
|
|
381
|
+
"vault": "/vault",
|
|
382
|
+
"health": "/doctor",
|
|
383
|
+
"scrape": "/help",
|
|
384
|
+
}
|
|
385
|
+
cmd = mapping.get(cat_id, "/help")
|
|
386
|
+
res = dispatch_slash(cmd)
|
|
387
|
+
self._content.write(f"\n[bold #D97757]── {cat_id} ──[/]\n")
|
|
388
|
+
self._content.write(res.text)
|
|
389
|
+
|
|
390
|
+
def on_input_submitted(self, event: Any) -> None:
|
|
391
|
+
"""User typed a slash command."""
|
|
392
|
+
value = event.value
|
|
393
|
+
if not value:
|
|
394
|
+
return
|
|
395
|
+
if self._content:
|
|
396
|
+
self._content.write(f"\n[bold #D97757]Ω ›[/] {value}\n")
|
|
397
|
+
res = dispatch_slash(value)
|
|
398
|
+
if event.input:
|
|
399
|
+
event.input.value = ""
|
|
400
|
+
if res.exit_tui:
|
|
401
|
+
self.action_quit_omega()
|
|
402
|
+
return
|
|
403
|
+
if res.detach:
|
|
404
|
+
self.action_detach_omega()
|
|
405
|
+
return
|
|
406
|
+
if res.text and self._content:
|
|
407
|
+
self._content.write(res.text)
|
|
408
|
+
if res.session_jump:
|
|
409
|
+
_switch_client(res.session_jump)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def run_tui() -> int:
|
|
413
|
+
"""Entry point — `omega menu-tui` calls this."""
|
|
414
|
+
if not TEXTUAL_AVAILABLE:
|
|
415
|
+
print(" textual not installed — `omega upgrade` or run:")
|
|
416
|
+
print(" pip install textual")
|
|
417
|
+
print()
|
|
418
|
+
print(" Falling back to plain prompt mode.")
|
|
419
|
+
return _plain_repl()
|
|
420
|
+
app = OmegaTUI()
|
|
421
|
+
app.run()
|
|
422
|
+
return 0
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
# ---------------------------------------------------------------------------
|
|
426
|
+
# Plain REPL fallback (when textual isn't installed)
|
|
427
|
+
# ---------------------------------------------------------------------------
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def _plain_repl() -> int:
|
|
431
|
+
"""Slash-command REPL with no textual TUI — same command set."""
|
|
432
|
+
from omega_engine import __version__
|
|
433
|
+
print(f"\n Ω Omega OS v{__version__} — plain REPL mode")
|
|
434
|
+
print(" Type /help for commands, /quit to exit.\n")
|
|
435
|
+
while True:
|
|
436
|
+
try:
|
|
437
|
+
line = input(" Ω › ").strip()
|
|
438
|
+
except (EOFError, KeyboardInterrupt):
|
|
439
|
+
print()
|
|
440
|
+
return 0
|
|
441
|
+
if not line:
|
|
442
|
+
continue
|
|
443
|
+
res = dispatch_slash(line)
|
|
444
|
+
if res.exit_tui:
|
|
445
|
+
subprocess.run(["tmux", "kill-session", "-t", "Omega"],
|
|
446
|
+
capture_output=True)
|
|
447
|
+
return 0
|
|
448
|
+
if res.detach:
|
|
449
|
+
subprocess.run(["tmux", "detach-client"], capture_output=True)
|
|
450
|
+
return 0
|
|
451
|
+
if res.text:
|
|
452
|
+
print()
|
|
453
|
+
for ln in res.text.splitlines():
|
|
454
|
+
print(f" {ln}")
|
|
455
|
+
print()
|
|
456
|
+
if res.session_jump:
|
|
457
|
+
_switch_client(res.session_jump)
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "omega-engine"
|
|
3
|
-
version = "0.19.
|
|
3
|
+
version = "0.19.27"
|
|
4
4
|
description = "The Omega OS orchestration engine — event-sourced, verified-completion agent graphs."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
license = { text = "MIT" }
|
|
8
8
|
dependencies = [
|
|
9
9
|
"pyyaml>=6.0",
|
|
10
|
+
"textual>=1.0",
|
|
10
11
|
]
|
|
11
12
|
|
|
12
13
|
[project.optional-dependencies]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.19.
|
|
1
|
+
0.19.27
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentikos/omega-os",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.27",
|
|
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"
|