From 8d3d2a39938a6d55e368bdcc7aea9c1613ca1714 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sun, 1 Mar 2026 16:03:04 -0500 Subject: [PATCH] Add TUI v2 scaffold, plan docs, and discovery command hints --- README.md | 1 + docs/README.md | 1 + docs/src/generator/index.js | 4 +- docs/tui_v2_plan.md | 96 ++++++++++++++++++++++++++++++++ landing/docs.html | 1 + memory-update.md | 14 +++++ src/seedpass/cli/__init__.py | 41 ++++++++++++++ src/seedpass/cli/capabilities.py | 2 + src/seedpass/tui_v2/__init__.py | 2 + src/seedpass/tui_v2/app.py | 62 +++++++++++++++++++++ 10 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 docs/tui_v2_plan.md create mode 100644 src/seedpass/tui_v2/__init__.py create mode 100644 src/seedpass/tui_v2/app.py diff --git a/README.md b/README.md index 4a1f8ee0..85e2d391 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ For machine-readable discovery, run: ```bash seedpass capabilities --format json seedpass agent bootstrap-context +seedpass tui2 --check ``` ## Architecture Overview diff --git a/docs/README.md b/docs/README.md index 927930f8..c22c0ec2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,6 +23,7 @@ This directory contains SeedPass project documentation. - `SPEC.md` - `entry_types.md` - `entry_graph.md` +- `tui_v2_plan.md` - `security.md` - `nostr_setup.md` - `nostr_namespace_reset.md` diff --git a/docs/src/generator/index.js b/docs/src/generator/index.js index 8fbd4dab..1075b936 100644 --- a/docs/src/generator/index.js +++ b/docs/src/generator/index.js @@ -1,4 +1,4 @@ -// Generator entry point for Archivox +// Generator entry point for SeedPass docs const fs = require('fs'); const path = require('path'); const matter = require('gray-matter'); @@ -213,7 +213,7 @@ async function generate({ contentDir = 'content', outputDir = '_site', configPat } // Copy the main assets directory (theme, js, etc.) - // Always resolve assets relative to the Archivox package so it works + // Always resolve assets relative to the docs package so it works // regardless of the current working directory or config location. const mainAssetsSrc = path.resolve(__dirname, '../../assets'); const mainAssetsDest = path.join(outputDir, 'assets'); diff --git a/docs/tui_v2_plan.md b/docs/tui_v2_plan.md new file mode 100644 index 00000000..731b1e78 --- /dev/null +++ b/docs/tui_v2_plan.md @@ -0,0 +1,96 @@ +# TUI v2 Plan (Textual) + +Status: Draft implementation plan on `beta` branch. + +Goal: Introduce a modern, modular TUI (`seedpass tui2`) without rewriting core logic or breaking existing CLI/API behavior. + +## Constraints + +- Keep `seedpass.core` as the source of truth for all business logic. +- Keep current TUI (`seedpass` default path) operational during migration. +- Maintain interface modularity: + - CLI/TUI + - API + - future GUI + +## Proposed Stack + +- Primary: `Textual` for TUI v2 shell and interaction model. +- Keep dependency optional initially; runtime check from `seedpass tui2 --check`. + +## Architecture + +1. `seedpass.core` +- Existing logic/services (`EntryManager`, `EntryService`, sync, vault, policy). + +2. `seedpass.app` (future thin orchestration layer) +- UI-agnostic use-cases and view-model mappers. +- Shared by Textual TUI and future GUI. + +3. Interface adapters +- `seedpass.cli` (Typer commands) +- `seedpass.tui_v2` (Textual app) +- `seedpass.api` (FastAPI) + +## Phased Rollout + +### Phase 0: Scaffold (this PR) +- [x] Add plan doc. +- [x] Add `seedpass tui2` command. +- [x] Add runtime check utility and placeholder launcher. +- [ ] Add CI smoke command for `seedpass tui2 --check`. + +### Phase 1: Read-only shell +- [ ] Textual app skeleton with 3-pane layout: + - filters panel + - entry list panel + - details panel +- [ ] Unlock/profile context hookup. +- [ ] Entry list/search/detail read paths. +- [ ] Status line for sync/profile/lock state. + +### Phase 2: Core editing workflows +- [ ] Add/modify/archive flows for common entry kinds. +- [ ] Document editor panel with dirty-state and save/cancel. +- [ ] Keybindings + command palette. + +### Phase 3: Graph workflows +- [ ] Entry links panel (list, add, remove). +- [ ] Relation-aware search controls. +- [ ] Neighbor navigation and quick traversal. + +### Phase 4: Cutover readiness +- [ ] Feature parity checklist against legacy TUI. +- [ ] UX/perf pass for large vaults. +- [ ] Error messaging and recovery flows. +- [ ] Decide default switch timing (`seedpass` -> `tui2`). + +## Test Strategy + +- Unit tests: + - runtime checks and command wiring. + - view-model transformations (when `seedpass.app` exists). +- Integration tests: + - command-level smoke (`seedpass tui2 --check`). + - later: Textual interaction tests for list/search/edit/link. +- Regression: + - full suite must remain green during phased migration. + +## Risks and Mitigations + +- Risk: UI code bypasses core invariants. + - Mitigation: enforce service-layer-only mutation from TUI. +- Risk: dependency churn (`textual` changes). + - Mitigation: pin versions when enabling default path. +- Risk: branch drift while both TUIs exist. + - Mitigation: parity checklist and shared orchestration layer. + +## Deliverables + +- New command: + - `seedpass tui2` + - `seedpass tui2 --check` +- New package: + - `seedpass/tui_v2/` +- Docs updates: + - this file listed in docs index and discoverability nav. diff --git a/landing/docs.html b/landing/docs.html index be20c7ec..d7b1e6e4 100644 --- a/landing/docs.html +++ b/landing/docs.html @@ -56,6 +56,7 @@
  • > Specification
  • > Entry Types
  • > Entry Graph
  • +
  • > TUI v2 Plan
  • > Security
  • > Security Readiness
  • > Agent Security Plan
  • diff --git a/memory-update.md b/memory-update.md index cbad1a23..9871e363 100644 --- a/memory-update.md +++ b/memory-update.md @@ -162,3 +162,17 @@ - Verification: - `.venv/bin/pytest -q src/tests/test_typer_cli.py -k "capabilities_text or capabilities_json" src/tests/test_cli_agent_mode.py -k "bootstrap_context"` - Result: `2 passed`. + +## TUI v2 scaffold kickoff on beta +- Added `docs/tui_v2_plan.md` with phased migration plan, risk controls, and testing strategy for Textual-based TUI v2. +- Added experimental CLI command `seedpass tui2` and diagnostics mode `seedpass tui2 --check`. +- Added new runtime-check/launcher scaffold package at `src/seedpass/tui_v2/`. +- Updated discoverability/docs surfaces: + - `docs/README.md` includes `tui_v2_plan.md` + - `landing/docs.html` includes `TUI v2 Plan` + - `README.md` discovery snippet includes `seedpass tui2 --check` + - `capabilities` root commands/help hints include `tui2` +- Validation: + - `py_compile` for updated modules + - `pytest` capabilities tests (`2 passed`) + - `.venv` smoke run confirms `seedpass tui2 --check` emits runtime diagnostics. diff --git a/src/seedpass/cli/__init__.py b/src/seedpass/cli/__init__.py index f684b69c..35ae8558 100644 --- a/src/seedpass/cli/__init__.py +++ b/src/seedpass/cli/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations import importlib import importlib.util +import json import subprocess import sys from typing import Optional @@ -11,6 +12,7 @@ import typer from .common import _get_services from seedpass.core.errors import SeedPassError from constants import GUI_BACKEND_CONFIG +from seedpass.tui_v2.app import check_tui2_runtime, launch_tui2 app = typer.Typer( help=( @@ -105,6 +107,45 @@ def root_lock(ctx: typer.Context) -> None: typer.echo("locked") +@app.command("tui2") +def tui2( + ctx: typer.Context, + check: bool = typer.Option( + False, + "--check", + help="Check whether TUI v2 runtime dependencies are available", + ), + fallback_legacy: bool = typer.Option( + True, + "--fallback-legacy/--no-fallback-legacy", + help="Fall back to legacy TUI when TUI v2 runtime is unavailable", + ), +) -> None: + """Launch experimental TUI v2 scaffold.""" + if check: + typer.echo(json.dumps(check_tui2_runtime(), indent=2, sort_keys=True)) + return + + fingerprint = (ctx.obj or {}).get("fingerprint") + launched = launch_tui2(fingerprint=fingerprint) + if launched: + return + + if fallback_legacy: + typer.echo( + "TUI v2 runtime unavailable; falling back to legacy TUI. " + "Run `seedpass tui2 --check` for diagnostics." + ) + tui = importlib.import_module("main") + raise typer.Exit(tui.main(fingerprint=fingerprint)) + + typer.echo( + "TUI v2 runtime unavailable. Install `textual` and retry.", + err=True, + ) + raise typer.Exit(1) + + @app.command() def gui( install: bool = typer.Option( diff --git a/src/seedpass/cli/capabilities.py b/src/seedpass/cli/capabilities.py index 29638bbd..9f46c337 100644 --- a/src/seedpass/cli/capabilities.py +++ b/src/seedpass/cli/capabilities.py @@ -17,6 +17,7 @@ def build_capabilities() -> dict[str, Any]: "root_commands": [ "lock", "gui", + "tui2", "capabilities", "entry", "vault", @@ -239,6 +240,7 @@ def build_capabilities() -> dict[str, Any]: "Run `seedpass --help` for top-level commands.", "Run `seedpass --help` for detailed options.", "Use `seedpass capabilities --format json` for machine-readable discovery.", + "Run `seedpass tui2 --check` before adopting Textual-based TUI v2.", "For graph workflows, start with `seedpass entry link-add --help` and `seedpass entry links --help`.", "For document workflows, run `seedpass entry import-document --help` and `seedpass agent document-import --help`.", "After unlock/login, run `seedpass capabilities` before autonomous runs.", diff --git a/src/seedpass/tui_v2/__init__.py b/src/seedpass/tui_v2/__init__.py new file mode 100644 index 00000000..95883444 --- /dev/null +++ b/src/seedpass/tui_v2/__init__.py @@ -0,0 +1,2 @@ +"""TUI v2 package scaffold (Textual-backed).""" + diff --git a/src/seedpass/tui_v2/app.py b/src/seedpass/tui_v2/app.py new file mode 100644 index 00000000..4349586f --- /dev/null +++ b/src/seedpass/tui_v2/app.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import importlib.util +from typing import Any + + +def check_tui2_runtime() -> dict[str, Any]: + """Return runtime capability diagnostics for TUI v2.""" + textual_available = importlib.util.find_spec("textual") is not None + return { + "status": "ok" if textual_available else "unavailable", + "backend": "textual", + "textual_available": textual_available, + "message": ( + "Textual runtime available." + if textual_available + else "Textual is not installed. Install `textual` to run tui2." + ), + } + + +def launch_tui2(*, fingerprint: str | None = None) -> bool: + """Launch TUI v2 when runtime dependencies are available. + + Returns ``True`` when launch succeeds, ``False`` when runtime is unavailable. + """ + runtime = check_tui2_runtime() + if not runtime["textual_available"]: + return False + + # Placeholder shell for incremental migration. + from textual.app import App, ComposeResult + from textual.containers import Container + from textual.widgets import Footer, Header, Static + + class SeedPassTuiV2(App[None]): + BINDINGS = [("q", "quit", "Quit")] + + def compose(self) -> ComposeResult: + yield Header(show_clock=True) + with Container(): + fp_line = f"Fingerprint: {fingerprint}" if fingerprint else "No fingerprint selected" + yield Static( + "\n".join( + [ + "SeedPass TUI v2 scaffold", + fp_line, + "", + "Next steps:", + "- Implement list/search/details panes", + "- Add document editor panel", + "- Add graph links panel", + "", + "Press 'q' to quit.", + ] + ) + ) + yield Footer() + + SeedPassTuiV2().run() + return True +