# Implementation Plan: REQ-013 -- ievo config TUI Config Tab + ievo secret Rename

## Domain Research

- Problem type: Integration (CLI command re-wiring + TUI widget composition)
- Approaches evaluated:
  - (A) Create a new `secret_cmd.py` that re-implements the four subcommands -- rejected: unnecessary duplication, `config_cmd.py` already works
  - (B) Re-register the existing `config_cmd.app` Typer sub-app under the name `"secret"` in `cli.py`, and register a new plain `@app.command(name="config")` -- chosen: zero code duplication, minimal change surface
- Recommended approach: (B) -- rename-by-re-registration. The existing `config_cmd.app` Typer sub-app is registered under `"secret"` instead of `"config"`. A new `@app.command("config")` calls `run_tui(initial_tab="config")`. The TUI gets a fifth tab.
- Libraries/tools: No new dependencies. Uses existing Textual widgets (`Static`, `VerticalScroll`, `TabPane`), existing core modules (`global_config`, `credentials`, `project`).
- Known pitfalls:
  - Typer `add_typer` name collision: the old `name="config"` registration must be removed before adding the new `@app.command(name="config")`, otherwise Typer raises a conflict.
  - `IevoApp.__init__` currently takes no parameters. Must accept `initial_tab` kwarg and pass to `TabbedContent(initial=...)`.
  - `run_tui()` currently has no parameters. Signature changes to `run_tui(initial_tab: str = "agents")`.
  - Existing `test_config_cmd.py` imports from `ievo.commands.config_cmd` -- these tests remain valid since the module is unchanged; only its CLI registration name changes.
  - `action_refresh_data` must also reload Config tab content; the Config tab widget needs a reload method.

## Architecture Assessment

- Fits existing architecture: yes -- follows existing tab pattern (Agents, Pipeline, Evolution, Files)
- New patterns introduced: parameterized `run_tui(initial_tab=...)` for CLI-to-TUI deep linking
- Modules affected: `src/ievo/cli.py`, `src/ievo/tui/app.py`, `tests/` (new test files + minor test updates)
- Risk level: low
- Estimated complexity: M

## Pre-conditions

- [x] REQ-000 must be implemented (dependency) -- confirmed implemented
- [x] Git workflow confirmed in DECISIONS.md (D-012: trunk-based, `feat/REQ-013-config-tui-secret`)

## Sub-tasks

### TASK-013-1: Rename `ievo config` to `ievo secret` + new `ievo config` command

Scope: CLI wiring only -- re-register `config_cmd.app` as `"secret"`, add new `@app.command("config")` that calls `run_tui(initial_tab="config")`. Update `run_tui()` signature to accept `initial_tab`. No TUI widget changes yet (Config tab does not exist yet, so `initial_tab="config"` will fall back to default Textual behavior).

Tests: ~10
Files: `src/ievo/cli.py`, `src/ievo/tui/app.py`, `tests/test_secret_cmd.py` (new), `tests/test_cli_app.py` (update)

### TASK-013-2: Config tab widget -- Global Settings + Credentials + Project Manifest

Scope: Add `TabPane("Config", id="config")` to `IevoApp.compose()`, key binding `c`, populate three sections. Wire refresh (`action_refresh_data`) to reload Config tab.

Tests: ~11
Files: `src/ievo/tui/app.py`, `tests/test_config_tab.py` (new)

## Micro-Steps

---

### TASK-013-1: Rename `ievo config` to `ievo secret` + new `ievo config` command

---

### Step 1: Create `tests/test_secret_cmd.py` -- red tests for `ievo secret` subcommands

Write 8 tests mirroring `tests/test_config_cmd.py` but importing from `secret_cmd` (the module that will expose the same four subcommands under the new name). Since `secret_cmd.py` does not exist yet, all tests will fail (red).

- Tests to write:
  - `test_secret_set_plaintext` -- `ievo secret set default_model opus` saves config key
  - `test_secret_set_sensitive` -- `ievo secret set ANTHROPIC_API_KEY sk-xxx` encrypts credential
  - `test_secret_get_plaintext` -- retrieves plain config value
  - `test_secret_get_sensitive_masked` -- retrieves and masks sensitive value
  - `test_secret_list_shows_keys` -- lists config keys and credential key names
  - `test_secret_delete_plaintext` -- deletes plain config key
  - `test_secret_delete_sensitive` -- deletes credential
  - `test_secret_no_args_shows_help` -- `ievo secret` with no subcommand shows help
- Criteria covered: AC-1, AC-7
- Files: create `tests/test_secret_cmd.py`
- Commit: `REQ-013: step 1 - red tests for ievo secret subcommands`

### Step 2: Create `src/ievo/commands/secret_cmd.py` + re-wire `cli.py` -- green

Create `src/ievo/commands/secret_cmd.py` that re-exports the same Typer app from `config_cmd` (or creates a thin wrapper that delegates to the same functions). The simplest approach: `secret_cmd.py` creates its own `app = typer.Typer(no_args_is_help=True)` and registers the same four command functions (`config_set`, `config_get`, `config_list`, `config_delete`) from `config_cmd`.

In `src/ievo/cli.py`:
- Remove: `app.add_typer(config_cmd.app, name="config", ...)`
- Add: `app.add_typer(secret_cmd.app, name="secret", help="Manage settings and credentials.")`
- Add import: `from ievo.commands import secret_cmd`

This step does NOT yet add the new `ievo config` command (that depends on `run_tui` signature change).

- Tests to write: none (step 1 tests go green)
- Criteria covered: AC-1, AC-2, AC-7
- Files: create `src/ievo/commands/secret_cmd.py`, modify `src/ievo/cli.py`
- Commit: `REQ-013: step 2 - create secret_cmd and re-wire cli.py`

### Step 3: Parameterize `run_tui()` and add `ievo config` command -- tests + green

Write tests:
- `test_run_tui_accepts_initial_tab` -- verify `run_tui` signature accepts `initial_tab` kwarg (unit test, mock `IevoApp.run`)
- `test_config_command_calls_run_tui_with_config_tab` -- verify `ievo config` command calls `run_tui(initial_tab="config")` (mock `run_tui`, invoke command)

Implementation:
- In `src/ievo/tui/app.py`:
  - `IevoApp.__init__(self, initial_tab: str = "agents")` -- stores `self._initial_tab = initial_tab`
  - `compose()`: change `TabbedContent(initial="agents")` to `TabbedContent(initial=self._initial_tab)`
  - `run_tui(initial_tab: str = "agents")`: pass to `IevoApp(initial_tab=initial_tab)`
- In `src/ievo/cli.py`:
  - Add new command:
    ```
    @app.command(name="config", help="Open TUI Config tab.")
    def config_command() -> None:
        from ievo.tui.app import run_tui
        run_tui(initial_tab="config")
    ```
  - Update `main()` callback: `run_tui()` call already uses default, no change needed

- Criteria covered: AC-3 (partial -- `run_tui` accepts `initial_tab`), AC-2
- Files: modify `src/ievo/tui/app.py`, modify `src/ievo/cli.py`, add tests to `tests/test_cli_app.py` or a new `tests/test_config_tab.py`
- Commit: `REQ-013: step 3 - parameterize run_tui and add ievo config command`

---

### TASK-013-2: Config tab widget

---

### Step 4: Red tests for Config tab -- key binding + sections

Write tests in `tests/test_config_tab.py` (Pilot-based async tests following pattern from `test_tui_integration.py`):

- `test_config_tab_key_binding` -- press `c` from Agents tab, verify `TabbedContent.active == "config"`
- `test_config_tab_shows_global_settings` -- mock `ensure_home` to return `tmp_path` with a populated `config.json`, verify "Global Settings" heading and key-value rows rendered in Config tab
- `test_config_tab_no_config_file` -- `tmp_path` with no `config.json`, verify "No global settings found." text
- `test_config_tab_shows_credential_keys` -- mock `list_credentials` to return `["ANTHROPIC_API_KEY", "GITHUB_TOKEN"]`, verify key names appear with masked values
- `test_config_tab_no_credentials` -- mock `list_credentials` to return `[]`, verify "No credentials stored." text
- `test_config_tab_shows_manifest_info` -- project with manifest, verify project name, version, agents rendered
- `test_config_tab_no_project` -- no project root, verify "No project found." text
- `test_run_tui_initial_tab_config` -- create app with `initial_tab="config"`, verify active tab is `"config"` on mount

All tests red -- Config tab `TabPane` does not exist yet.

- Tests to write: 8 tests listed above
- Criteria covered: AC-3, AC-4, AC-5, AC-6
- Files: create/extend `tests/test_config_tab.py`
- Commit: `REQ-013: step 4 - red tests for config tab widget`

### Step 5: Add Config TabPane with Global Settings section -- partial green

In `src/ievo/tui/app.py`:

- Add import: `from ievo.core.global_config import load_global_config` (already available in project)
- Add import: `from ievo.core.credentials import list_credentials`
- Add import: `from ievo.core.config import ensure_home`
- Add `Binding("c", "tab('config')", "Config")` to `BINDINGS` (insert before `r`)
- In `compose()`, add after the Files `TabPane`:
  ```
  with TabPane("Config", id="config"):
      yield VerticalScroll(Static("", id="config-view", markup=True))
  ```
- Add CSS for `#config-view`:
  ```
  #config-view {
      padding: 1 2;
      background: #0c0c0c;
  }
  ```
- Add method `_load_config_tab(self) -> None`:
  - Reads `ensure_home()` for global config and credentials
  - Reads `self._manifest` for project info (may be None)
  - Builds Rich markup string with three sections
  - Updates `#config-view` Static widget
- Call `_load_config_tab()` at the end of `watch_project_root()` (both branches: with and without project)
- Call `_load_config_tab()` inside `action_refresh_data()` (after re-setting `project_root`)

Section 1 -- Global Settings:
```
[bold #4ade80]Global Settings[/bold #4ade80]
  default_model        sonnet
  marketplace_url      https://github.com/ievo-ai/marketplace
  ...
```
If `config.json` does not exist (or `ensure_home` fails gracefully): show "No global settings found."

Section 2 -- Credentials:
```
[bold #4ade80]Credentials[/bold #4ade80]
  ANTHROPIC_API_KEY    ***
  GITHUB_TOKEN         ***
```
If no credentials: "No credentials stored."

Section 3 -- Project:
```
[bold #4ade80]Project[/bold #4ade80]
  project       my-project
  version       0.1.0
  created       2026-03-05
  cli_version   0.9.0
  agents        spec-writer, coder, architect
```
If no project: "No project found."

- Tests to write: none new (step 4 tests go green)
- Criteria covered: AC-3, AC-4, AC-5, AC-6, AC-8
- Files: modify `src/ievo/tui/app.py`
- Commit: `REQ-013: step 5 - add config tab with three sections`

### Step 6: Test for refresh updates Config tab + final coverage sweep

- Tests to write:
  - `test_refresh_updates_config_tab` -- modify `config.json` on disk, press `r`, verify updated content in Config tab
  - `test_config_command_opens_tui_on_config_tab` -- Pilot test: create `IevoApp(initial_tab="config")`, verify Config tab is active on mount
  - Any edge-case tests needed for 100% coverage on changed lines

- Criteria covered: AC-8, AC-3
- Files: extend `tests/test_config_tab.py`
- Commit: `REQ-013: step 6 - refresh test and final coverage`

## Traceability

| Acceptance Criterion | Covered in Step |
|---------------------|-----------------|
| AC-1 (secret subcommands) | Step 1, 2 |
| AC-2 (old config no longer routes to creds) | Step 2, 3 |
| AC-3 (ievo config opens TUI Config tab) | Step 3, 4, 5, 6 |
| AC-4 (Global Settings section) | Step 4, 5 |
| AC-5 (Credentials section) | Step 4, 5 |
| AC-6 (Project Manifest section) | Step 4, 5 |
| AC-7 (secret is active, not deprecated) | Step 1, 2 |
| AC-8 (refresh reloads Config tab) | Step 5, 6 |

## Scope Guard

- Considered but excluded: editable config values in TUI (Config tab is read-only display only per REQ)
- Considered but excluded: deprecation warning on old `ievo config set/get/list/delete` -- per REQ, the old subcommands simply stop existing under `config`; they are not deprecated, they are moved to `secret`
- Considered but excluded: renaming `config_cmd.py` file to `secret_cmd.py` -- keeping original file reduces diff; new `secret_cmd.py` delegates to it

## Architecture Notes for Coder

- `secret_cmd.py` should NOT copy-paste the four functions from `config_cmd.py`. Instead, create a new `app = typer.Typer(no_args_is_help=True)` and register the same functions: `app.command("set")(config_cmd.config_set)`, etc. This is zero-duplication re-registration.
- The `_load_config_tab` method in `IevoApp` must handle the case where `ensure_home()` creates `~/.ievo/` with defaults -- in tests, mock `ensure_home` to return `tmp_path` and pre-populate (or not) `config.json` there.
- For Pilot tests, follow the exact pattern in `tests/test_tui_integration.py`: `_make_project()` helper, `patch("ievo.tui.app.find_project_root", ...)`, `async with app.run_test(size=(120, 40)) as pilot`.
- `list_credentials` may raise if RSA keys don't exist. In `_load_config_tab`, catch exceptions from `list_credentials` and treat as empty list. Alternatively, mock `list_credentials` in TUI tests (it's an external boundary -- crypto).
- The `#config-view` Static widget content is built as a single Rich markup string, same pattern as `#pipeline-view`.
- `IevoApp.__init__` must call `super().__init__()` -- Textual `App.__init__` does not take an `initial_tab` kwarg; store it as `self._initial_tab`.
- For `test_secret_no_args_shows_help`, use Typer's `CliRunner` to invoke `secret_cmd.app` with no args and assert help text appears.
- The `_load_config_tab` method should also be called when `project_root` is `None` (in the `watch_project_root` None branch) -- Global Settings and Credentials are independent of project state.
