@agentikos/omega-os 0.19.26 → 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.
@@ -188,7 +188,7 @@ from omega_engine.genesis import (
188
188
  )
189
189
  from omega_engine import plan as plan_v7
190
190
 
191
- __version__ = "0.19.26"
191
+ __version__ = "0.19.27"
192
192
 
193
193
  __all__ = [
194
194
  "__version__",
@@ -2856,16 +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 fzf action picker for the Omega session.
2859
+ """`omega menu-tui` — the OmegaOS Textual TUI (v0.19.27+).
2860
2860
 
2861
- v0.19.26 actions now run INLINE in the menu pane (not in dead
2862
- background tmux windows). After every action, "press Enter to
2863
- return to menu". Sessions (AISB/Hermes) still spawn as separate
2864
- tmux sessions because they're meant to be talked to long-running.
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
2865
 
2866
- Bug fix: previous version used fzf with leading-space labels,
2867
- then `pick.strip()` killed the spaces, so display matching failed
2868
- and every pick became a no-op. Both sides stripped now.
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.
2869
2880
  """
2870
2881
  import os
2871
2882
  import shlex
@@ -4333,9 +4344,12 @@ def _build_parser() -> argparse.ArgumentParser:
4333
4344
  help="open the interactive whiptail menu (legacy)"
4334
4345
  ).set_defaults(fn=cmd_menu_whiptail)
4335
4346
  sub.add_parser("menu-tui",
4336
- help="the fzf action picker that runs INSIDE the Omega tmux "
4337
- "session (don't call directly — bare `omega` lands you here)"
4347
+ help="the Textual TUI inside the Omega tmux session "
4348
+ "(don't call directly — bare `omega` lands you here)"
4338
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)
4339
4353
  sub.add_parser("version", help="print the engine version").set_defaults(fn=cmd_version)
4340
4354
 
4341
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.26"
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.26
1
+ 0.19.27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentikos/omega-os",
3
- "version": "0.19.26",
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"