@agentikos/omega-os 0.19.29 → 0.19.31

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.29"
191
+ __version__ = "0.19.31"
192
192
 
193
193
  __all__ = [
194
194
  "__version__",
@@ -2856,19 +2856,22 @@ 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` — slash-command REPL (default) or Textual TUI.
2859
+ """`omega menu-tui` — fzf arrow-key menu (default) / REPL / Textual.
2860
2860
 
2861
- v0.19.29 default = plain REPL. Textual was unreliable under
2862
- tmux + send-keys (focus didn't reach the input). The REPL uses
2863
- synchronous input() which works EVERYWHERE, plus readline for
2864
- history + tab completion of slash commands.
2861
+ v0.19.30 default = fzf arrow-key menu. Arrow keys + search-as-you-type
2862
+ + mouse + Enter, with clean grouped layout (CHAT / PROJECTS / AUDITS
2863
+ / INFRASTRUCTURE / HEALTH / SCRAPE / EXIT). Color-themed to Claude
2864
+ (orange #D97757 accents on dark backdrop).
2865
2865
 
2866
- Opt into Textual: `omega menu-tui --textual` (useful when running
2867
- omega directly in iTerm/Wezterm/Kitty without tmux send-keys).
2866
+ Flags:
2867
+ --repl → plain slash REPL (input() loop, type /commands)
2868
+ --textual → Textual app (opt-in, may break under tmux send-keys)
2868
2869
  """
2869
2870
  from omega_engine.tui import run_tui
2870
- prefer_textual = getattr(args, "textual", False)
2871
- return run_tui(prefer_textual=prefer_textual)
2871
+ return run_tui(
2872
+ prefer_textual=getattr(args, "textual", False),
2873
+ force_repl=getattr(args, "repl", False),
2874
+ )
2872
2875
 
2873
2876
 
2874
2877
  def _legacy_fzf_menu(_args: argparse.Namespace) -> int:
@@ -4345,10 +4348,12 @@ def _build_parser() -> argparse.ArgumentParser:
4345
4348
  help="open the interactive whiptail menu (legacy)"
4346
4349
  ).set_defaults(fn=cmd_menu_whiptail)
4347
4350
  p_mt = sub.add_parser("menu-tui",
4348
- help="slash-command REPL (default) — use --textual for the "
4349
- "fancier (but tmux-fragile) Textual TUI")
4351
+ help="fzf arrow-key menu (default), --repl for slash REPL, "
4352
+ "--textual for the Textual app")
4350
4353
  p_mt.add_argument("--textual", action="store_true",
4351
- help="try the Textual app instead of the REPL")
4354
+ help="try the Textual app (fragile under tmux)")
4355
+ p_mt.add_argument("--repl", action="store_true",
4356
+ help="force slash REPL instead of the arrow menu")
4352
4357
  p_mt.set_defaults(fn=cmd_menu_tui)
4353
4358
  sub.add_parser("menu-fzf",
4354
4359
  help="legacy v0.19.26 fzf menu fallback"
@@ -325,6 +325,48 @@ def _spawn_with_shell_then_run(
325
325
  return name
326
326
 
327
327
 
328
+ def spawn_chat_in_omega(
329
+ window_name: str,
330
+ *,
331
+ ctx_dir: Path,
332
+ run_command: str,
333
+ force_replace: bool = False,
334
+ ) -> bool:
335
+ """v0.19.31 — spawn a chat as a WINDOW inside the Omega master session.
336
+
337
+ Returns True when the window exists after the call. The user can
338
+ then `tmux select-window -t Omega:<name>` (or pick from the menu).
339
+ Detach (Ctrl-b d) detaches the whole Omega session — user comes
340
+ back to their shell with Omega still alive, and `omega` re-attaches
341
+ them straight back to the menu.
342
+
343
+ Why windows instead of separate sessions: the user wants `omega`
344
+ to STAY OMEGA. With separate AISB-chat / Hermes-chat sessions,
345
+ detaching from one of those landed them in the shell, not back
346
+ in the Omega menu. Windows under Omega solve that — everything
347
+ lives under the same session lifecycle.
348
+ """
349
+ if not is_alive("Omega"):
350
+ # Omega master must already be alive — bare `omega` ensures this.
351
+ spawn_omega_master(ctx_dir.parent.parent.parent)
352
+ # Probe existing windows.
353
+ _, list_out = _tmux("list-windows", "-t", "Omega", "-F", "#W")
354
+ existing = list_out.splitlines() if list_out else []
355
+ if window_name in existing:
356
+ if force_replace:
357
+ _tmux("kill-window", "-t", f"Omega:{window_name}")
358
+ else:
359
+ return True
360
+ # Create the window with the user's interactive shell at ctx_dir, then
361
+ # send the launch command. Shell stays alive even if the command exits.
362
+ rc, _ = _tmux("new-window", "-t", "Omega", "-n", window_name,
363
+ "-c", str(ctx_dir))
364
+ if rc != 0:
365
+ return False
366
+ _tmux("send-keys", "-t", f"Omega:{window_name}", run_command, "Enter")
367
+ return True
368
+
369
+
328
370
  def spawn_aisb_chat(omega_home: str | Path | None = None,
329
371
  force_replace: bool = False) -> str:
330
372
  """Spawn the AISB master chat tmux session — REAL Claude Code TUI.
@@ -530,12 +572,20 @@ bind-key Z display-popup -E -w 80% -h 80% "omega tmux menu"
530
572
  # Prefix+S — native session list (tmux's built-in choose-tree)
531
573
  bind-key S choose-tree -Zs
532
574
 
575
+ # Open the Omega menu from anywhere in tmux (Option+/ or Option+z).
576
+ # macOS Option key → enable "Use Option as Meta" in your terminal.
577
+ bind-key -n M-/ run-shell -b "tmux has-session -t Omega 2>/dev/null || tmux new-session -d -s Omega -n menu -c $HOME 'omega menu-tui'; tmux switch-client -t Omega"
578
+ bind-key -n M-z run-shell -b "tmux has-session -t Omega 2>/dev/null || tmux new-session -d -s Omega -n menu -c $HOME 'omega menu-tui'; tmux switch-client -t Omega"
579
+
533
580
  # ════════════════════════════════════════════════════════════════════
534
- # Visual cues per session category
581
+ # Claude Code LIGHT theme (white-paper)
535
582
  # ════════════════════════════════════════════════════════════════════
536
- set -g status-style "bg=#1a1a2e,fg=#eeeeee"
537
- set -g status-left "#[fg=#7aa2f7,bold]#S #[fg=default]│ "
538
- set -g status-right "#[fg=#888888]%H:%M │ #(omega tmux count 2>/dev/null) sessions"
583
+ set -g status-style "bg=#FAFAF7,fg=#3D3929"
584
+ set -g status-left "#[fg=#D97757,bold]Ω #S #[fg=#A8A29E]│ "
585
+ set -g status-right "#[fg=#88837A]%H:%M │ #(omega tmux count 2>/dev/null) sessions"
586
+ set -g pane-border-style "fg=#A8A29E"
587
+ set -g pane-active-border-style "fg=#D97757"
588
+ set -g message-style "bg=#E5E2DD,fg=#3D3929"
539
589
  """
540
590
 
541
591
 
@@ -623,18 +673,42 @@ bind-key S choose-tree -Zs
623
673
  bind-key r source-file ~/.tmux.conf \\; display-message "tmux.conf reloaded"
624
674
 
625
675
  # ════════════════════════════════════════════════════════════════════
626
- # Visual cues per session category — colour the status bar by name.
676
+ # Global Omega menu keybindings (v0.19.31)
677
+ # ════════════════════════════════════════════════════════════════════
678
+ # Open the Omega menu from ANYWHERE in tmux — no prefix needed.
679
+ #
680
+ # Option+/ or Option+z → opens the Omega menu
681
+ #
682
+ # How: spawn the Omega session if missing, then switch the current
683
+ # client to it. Detach (Ctrl-b d) brings you back to your shell with
684
+ # Omega still alive.
685
+ #
686
+ # IMPORTANT — for Option key to work on macOS terminals, enable
687
+ # "Use Option as Meta key" (iTerm2: Profile → Keys → Left/Right Option
688
+ # Key → Esc+). Otherwise the binding stays inactive.
689
+ bind-key -n M-/ run-shell -b "tmux has-session -t Omega 2>/dev/null || tmux new-session -d -s Omega -n menu -c $HOME 'omega menu-tui'; tmux switch-client -t Omega"
690
+ bind-key -n M-z run-shell -b "tmux has-session -t Omega 2>/dev/null || tmux new-session -d -s Omega -n menu -c $HOME 'omega menu-tui'; tmux switch-client -t Omega"
691
+
692
+ # ════════════════════════════════════════════════════════════════════
693
+ # Claude Code LIGHT theme (white-paper) — status bar + borders
627
694
  # ════════════════════════════════════════════════════════════════════
628
- set -g status-style "bg=#1a1a2e,fg=#eeeeee"
629
- set -g status-left "#[fg=#7aa2f7,bold]#S #[fg=default]│ "
630
- set -g status-right "#[fg=#888888]%H:%M │ #(omega tmux count 2>/dev/null) sessions"
695
+ # Colours derived from tweakcn.com/r/themes/claude.json:
696
+ # bg #FAFAF7 (cream) fg #3D3929 (slate) accent #D97757 (orange)
697
+ # muted #88837A (warm gray) border #A8A29E (soft)
698
+ set -g status-style "bg=#FAFAF7,fg=#3D3929"
699
+ set -g status-left "#[fg=#D97757,bold]Ω #S #[fg=#A8A29E]│ "
700
+ set -g status-right "#[fg=#88837A]%H:%M │ #(omega tmux count 2>/dev/null) sessions"
631
701
  set -g status-interval 5
632
702
  set -g status-left-length 40
633
703
  set -g status-right-length 60
634
704
 
635
- # Pane border colour softer than default red
636
- set -g pane-border-style "fg=#333333"
637
- set -g pane-active-border-style "fg=#7aa2f7"
705
+ # Pane borderssoft warm gray, accent orange on the active pane.
706
+ set -g pane-border-style "fg=#A8A29E"
707
+ set -g pane-active-border-style "fg=#D97757"
708
+
709
+ # Message + copy-mode prompts — same palette.
710
+ set -g message-style "bg=#E5E2DD,fg=#3D3929"
711
+ set -g mode-style "bg=#D97757,fg=#FAFAF7"
638
712
  """
639
713
 
640
714
 
@@ -409,23 +409,269 @@ if TEXTUAL_AVAILABLE:
409
409
  _switch_client(res.session_jump)
410
410
 
411
411
 
412
- def run_tui(prefer_textual: bool = False) -> int:
413
- """Entry point `omega menu-tui` calls this.
412
+ def _arrow_menu() -> int:
413
+ """v0.19.30fzf-driven arrow-key menu (the default `omega` UX).
414
+
415
+ Clean grouped layout, arrow-key navigation, search-as-you-type,
416
+ Enter to pick, Esc to refresh. After each action runs INLINE in
417
+ the same pane, "press Enter to return" then re-render the menu.
418
+
419
+ Why fzf and not Textual? Textual's focus model is fragile under
420
+ tmux + send-keys (v0.19.27 bug). fzf has been arrow-key + mouse
421
+ capable since 2014; it's lightweight, single-binary, and behaves
422
+ consistently in every terminal.
423
+
424
+ Why fzf and not whiptail? whiptail is fine but visually 1995. fzf
425
+ gives us a Linear-style search filter and clean ANSI color groups.
426
+ """
427
+ import os
428
+ import shlex
429
+ import shutil
430
+ import subprocess
431
+ from pathlib import Path
432
+ from omega_engine import __version__
433
+ from omega_engine import tmux
434
+
435
+ if not shutil.which("fzf"):
436
+ print(" fzf not on PATH — falling back to slash REPL.")
437
+ print(" install: brew install fzf (or apt install fzf)")
438
+ return _plain_repl()
439
+
440
+ HOME = Path(os.environ.get("OMEGA_HOME", str(Path.home() / "Omega")))
441
+ OMEGA_BIN = str(HOME / "Agentik_Tools" / "bin" / "omega")
442
+
443
+ # ANSI escapes — used via fzf --ansi.
444
+ ORANGE = "\033[38;2;217;119;87m"
445
+ MUTED = "\033[38;2;136;131;122m"
446
+ BOLD = "\033[1m"
447
+ DIM = "\033[2m"
448
+ RST = "\033[0m"
449
+
450
+ def _label(name: str, hint: str = "") -> str:
451
+ """Two-column label: name on the left, hint dimmed on the right."""
452
+ if hint:
453
+ return f" {name:<36}{DIM}{hint}{RST}"
454
+ return f" {name}"
455
+
456
+ def _section(name: str) -> str:
457
+ return f"{ORANGE}{BOLD}── {name} ──{RST}"
458
+
459
+ def _build_items() -> list[tuple[str, str]]:
460
+ """Return (display, action_key) — section headers have key '__sep__'."""
461
+ provider = _active_provider()
462
+ sessions = tmux.list_sessions(HOME)
463
+ return [
464
+ (_section("CHAT"), "__sep__"),
465
+ (_label("AISB master chat", "→ claude (Max OAuth)"), "open:aisb"),
466
+ (_label("Hermès companion", "→ claude (Anthropic API)"), "open:hermes"),
467
+ (_label("Switch LLM", f"current: {provider}"), "switch:provider"),
468
+ ("", "__sep__"),
469
+ (_section("PROJECTS"), "__sep__"),
470
+ (_label("New project", "Genesis pipeline"), "genesis:new"),
471
+ (_label("Open project shell"), "project:open"),
472
+ ("", "__sep__"),
473
+ (_section("AUDITS & MISSIONS"), "__sep__"),
474
+ (_label("Run a mission", "verified completion"), "run:mission"),
475
+ (_label("Quality Arsenal", "17 forensic audits"), "audit:menu"),
476
+ ("", "__sep__"),
477
+ (_section("INFRASTRUCTURE"), "__sep__"),
478
+ (_label("Sessions", f"{len(sessions)} active"), "sessions:list"),
479
+ (_label("Accounts", "Claude Max pool"), "accounts:menu"),
480
+ (_label("Vault", "encrypted secrets"), "vault:menu"),
481
+ ("", "__sep__"),
482
+ (_section("HEALTH"), "__sep__"),
483
+ (_label("omega doctor", "full health check"), "cmd:doctor"),
484
+ (_label("omega status", "task state"), "cmd:status"),
485
+ ("", "__sep__"),
486
+ (_section("SCRAPE"), "__sep__"),
487
+ (_label("Stealth scrape", "CloakBrowser"), "scrape:cloak"),
488
+ (_label("Fast scrape", "Scrapling"), "scrape:scrapling"),
489
+ ("", "__sep__"),
490
+ (_section("EXIT"), "__sep__"),
491
+ (_label("Detach", "session keeps running"), "detach"),
492
+ (_label("Quit Omega", "kills the tmux session"), "quit:kill"),
493
+ ]
494
+
495
+ def _run_inline(cmd_argv, *, shell: bool = False) -> None:
496
+ os.system("clear")
497
+ header = " ".join(cmd_argv) if isinstance(cmd_argv, list) else cmd_argv
498
+ print(f" {ORANGE}{BOLD}Ω ›{RST} {header}")
499
+ print(f" {MUTED}{'─' * 60}{RST}")
500
+ if shell:
501
+ subprocess.run(cmd_argv, shell=True)
502
+ else:
503
+ subprocess.run(cmd_argv)
504
+ print()
505
+ print(f" {MUTED}{'─' * 60}{RST}")
506
+ try:
507
+ input(f" {DIM}press Enter to return to Omega menu… {RST}")
508
+ except (EOFError, KeyboardInterrupt):
509
+ pass
510
+
511
+ def _prompt(label: str) -> str:
512
+ try:
513
+ return input(f" {ORANGE}{label}:{RST} ").strip()
514
+ except (EOFError, KeyboardInterrupt):
515
+ return ""
516
+
517
+ while True:
518
+ items = _build_items()
519
+ lines = [disp for disp, _ in items]
520
+ header = (
521
+ f"{ORANGE}{BOLD}Ω Omega OS v{__version__}{RST} "
522
+ f"{MUTED}• ↑↓ navigate • ↵ pick • / search • Esc refresh{RST}"
523
+ )
524
+ try:
525
+ # Claude Code LIGHT theme (white-paper) — bg cream #FAFAF7,
526
+ # fg dark slate #3D3929, accent orange #D97757, muted warm
527
+ # gray #88837A, soft border #A8A29E. Reads cleanly under
528
+ # both light and dark terminal backgrounds (we override
529
+ # fzf's bg explicitly).
530
+ proc = subprocess.run(
531
+ ["fzf",
532
+ "--ansi",
533
+ "--prompt=Ω › ",
534
+ f"--header={header}",
535
+ "--header-first",
536
+ "--layout=reverse",
537
+ "--height=100%",
538
+ "--border=rounded",
539
+ "--padding=1,2",
540
+ "--no-multi",
541
+ "--info=hidden",
542
+ "--pointer=▶",
543
+ "--marker=●",
544
+ "--color="
545
+ "bg:#FAFAF7,"
546
+ "fg:#3D3929,"
547
+ "bg+:#E5E2DD,"
548
+ "fg+:#D97757,"
549
+ "hl:#D97757,"
550
+ "hl+:#D97757,"
551
+ "prompt:#D97757,"
552
+ "pointer:#D97757,"
553
+ "marker:#D97757,"
554
+ "header:#88837A,"
555
+ "border:#A8A29E,"
556
+ "info:#88837A,"
557
+ "spinner:#D97757,"
558
+ "gutter:#FAFAF7"],
559
+ input="\n".join(lines), capture_output=True, text=True,
560
+ )
561
+ except (KeyboardInterrupt, subprocess.SubprocessError):
562
+ return 0
563
+ if proc.returncode != 0:
564
+ # Esc → refresh.
565
+ continue
566
+ pick = proc.stdout.rstrip("\n")
567
+ action = None
568
+ for disp, key in items:
569
+ if disp == pick and key != "__sep__":
570
+ action = key
571
+ break
572
+ if action is None:
573
+ continue
414
574
 
415
- v0.19.29 switched the DEFAULT to the plain slash REPL because the
416
- Textual app, while pretty, has unreliable focus/keyboard delivery
417
- inside tmux panes launched via send-keys. The user's bug report
418
- ("je peux pas taper dans le keyboard ni sélectionner") was reproducible
419
- on the VPS too — Textual's Input widget doesn't always claim focus
420
- when the app starts under tmux + send-keys.
575
+ # === Dispatch ===
576
+ if action == "detach":
577
+ subprocess.run(["tmux", "detach-client"])
578
+ return 0
579
+ if action == "quit:kill":
580
+ subprocess.run(["tmux", "kill-session", "-t", "Omega"])
581
+ return 0
582
+ if action == "open:aisb":
583
+ # v0.19.31 — open as a WINDOW in Omega, not a separate session.
584
+ # User stays in Omega; Ctrl-b 0 returns to menu, Ctrl-b d
585
+ # detaches the whole Omega (and `omega` brings them back).
586
+ persona = HOME / "Agentik_SSOT" / "agents" / "aisb" / "CLAUDE.md"
587
+ ctx_dir = tmux._ensure_chat_context_dir(HOME, "aisb-master", persona)
588
+ tmux.spawn_chat_in_omega(
589
+ "aisb", ctx_dir=ctx_dir, run_command="claude",
590
+ force_replace=False,
591
+ )
592
+ subprocess.run(["tmux", "select-window", "-t", "Omega:aisb"])
593
+ continue
594
+ if action == "open:hermes":
595
+ persona = HOME / "Agentik_SSOT" / "docs" / "LAYERS.md"
596
+ ctx_dir = tmux._ensure_chat_context_dir(HOME, "hermes", persona)
597
+ try:
598
+ from omega_engine.vault import vault_read
599
+ api_key = (vault_read(HOME, "ANTHROPIC_API_KEY_HERMES") or "").strip()
600
+ except Exception: # noqa: BLE001
601
+ api_key = ""
602
+ if not api_key:
603
+ api_key = (os.environ.get("ANTHROPIC_API_KEY") or "").strip()
604
+ run_cmd = (f"ANTHROPIC_API_KEY={api_key} claude"
605
+ if api_key else "claude")
606
+ tmux.spawn_chat_in_omega(
607
+ "hermes", ctx_dir=ctx_dir, run_command=run_cmd,
608
+ force_replace=False,
609
+ )
610
+ subprocess.run(["tmux", "select-window", "-t", "Omega:hermes"])
611
+ continue
612
+ if action == "switch:provider":
613
+ _run_inline([OMEGA_BIN, "switch"]); continue
614
+ if action == "genesis:new":
615
+ slug = _prompt("project slug (a-z0-9_-)")
616
+ if slug:
617
+ _run_inline([OMEGA_BIN, "genesis", "new", slug])
618
+ continue
619
+ if action == "project:open":
620
+ _run_inline("ls -la ~/Omega/Agentik_Coding/projects/ 2>/dev/null "
621
+ "&& echo && echo '(cd into one to start working)'",
622
+ shell=True)
623
+ continue
624
+ if action == "run:mission":
625
+ intent = _prompt("mission intent")
626
+ if intent:
627
+ _run_inline([OMEGA_BIN, "run", intent])
628
+ continue
629
+ if action == "audit:menu":
630
+ _run_inline(
631
+ "echo ' Quality Arsenal — 17 forensic audits:' && "
632
+ "echo ' /codeaudit /uiuxaudit /flowaudit /debugaudit' && "
633
+ "echo ' /featureaudit /perfaudit /secaudit /a11yaudit' && "
634
+ "echo ' /seoaudit /dataaudit /apiaudit /copyaudit' && "
635
+ "echo ' /dxaudit /motionaudit /automationaudit' && "
636
+ "echo ' /logicaudit /retentionaudit /refontaudit' && "
637
+ "echo && echo ' invoke from any claude session, or:' && "
638
+ f"echo ' {OMEGA_BIN} audit <id>'",
639
+ shell=True)
640
+ continue
641
+ if action == "sessions:list":
642
+ _run_inline("tmux ls", shell=True); continue
643
+ if action == "accounts:menu":
644
+ _run_inline([OMEGA_BIN, "account", "list"]); continue
645
+ if action == "vault:menu":
646
+ _run_inline([OMEGA_BIN, "vault", "status"]); continue
647
+ if action == "cmd:doctor":
648
+ _run_inline([OMEGA_BIN, "doctor"]); continue
649
+ if action == "cmd:status":
650
+ _run_inline([OMEGA_BIN, "status"]); continue
651
+ if action == "scrape:cloak":
652
+ url = _prompt("URL")
653
+ if url:
654
+ _run_inline([OMEGA_BIN, "scrape", url])
655
+ continue
656
+ if action == "scrape:scrapling":
657
+ url = _prompt("URL")
658
+ if url:
659
+ _run_inline([OMEGA_BIN, "scrape", url, "--engine", "scrapling"])
660
+ continue
661
+
662
+
663
+ def run_tui(prefer_textual: bool = False,
664
+ force_repl: bool = False) -> int:
665
+ """Entry point — `omega menu-tui` calls this.
421
666
 
422
- The plain REPL uses synchronous `input()` which is rock-solid in any
423
- terminal, any tmux config, any TERM value. It accepts the SAME slash
424
- commands (/aisb /hermes /switch /audit /mission /scrape /doctor …).
667
+ v0.19.30 default is the arrow-key fzf menu (clean grouped layout,
668
+ arrow keys + search-as-you-type + mouse, color theme matched to
669
+ Claude). Falls back to plain slash REPL if fzf is missing.
425
670
 
426
- Opt back into Textual via `omega menu-tui --textual` (or pass
427
- prefer_textual=True from code) useful when iTerm/Wezterm/Kitty
428
- runs the menu directly without tmux send-keys interference.
671
+ Modes:
672
+ - default → `_arrow_menu()` (fzf, arrow keys + search)
673
+ - `--repl` → `_plain_repl()` (input() loop, slash commands)
674
+ - `--textual` → Textual TUI (opt-in, may break under tmux)
429
675
  """
430
676
  if prefer_textual and TEXTUAL_AVAILABLE:
431
677
  try:
@@ -434,9 +680,10 @@ def run_tui(prefer_textual: bool = False) -> int:
434
680
  return 0
435
681
  except Exception as exc: # noqa: BLE001
436
682
  print(f" Textual TUI failed: {exc}")
437
- print(" Falling back to plain REPL.")
438
- return _plain_repl()
439
- return _plain_repl()
683
+ print(" Falling back to arrow-key menu.")
684
+ if force_repl:
685
+ return _plain_repl()
686
+ return _arrow_menu()
440
687
 
441
688
 
442
689
  # ---------------------------------------------------------------------------
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "omega-engine"
3
- version = "0.19.29"
3
+ version = "0.19.31"
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"
@@ -1 +1 @@
1
- 0.19.29
1
+ 0.19.31
@@ -74,6 +74,19 @@ native:
74
74
  secrets: []
75
75
  recommended: true
76
76
 
77
+ - id: paperclipai
78
+ name: Paperclip (orchestration UI — agent teams, org charts, governance)
79
+ # Node.js + React app from paperclipai/paperclip. Coordinates multiple
80
+ # agents (Claude Code, Codex, Cursor, OpenClaw) into teams with org
81
+ # charts, budgets, governance. Launches a web dashboard + CLI tools.
82
+ # We use the `onboard` npx flow which handles install + initial config.
83
+ # Requires Node 20+, pnpm 9.15+ (both checked by step_system_deps).
84
+ install: { npm_global: "paperclipai" }
85
+ binary: paperclip
86
+ secrets: []
87
+ recommended: false
88
+ post_install: ["paperclip", "--version"]
89
+
77
90
  - id: scrapling
78
91
  name: Scrapling (optional fast scraper — HTTP-first, adaptive elements)
79
92
  # Complementary to CloakBrowser. Scrapling shines for non-protected
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentikos/omega-os",
3
- "version": "0.19.29",
3
+ "version": "0.19.31",
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"