Skip to content
Back to skills

1638 Plan 8882b9fb

ASecurity

The `src/tunacode/ui/lifecycle.py` module contains free functions:

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
testingpythongoapi

Works with

  • api

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 1638-plan_8882b9fb --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 1638 Plan 8882b9fb?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 1638 Plan 8882b9fb
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-1638-plan-8882b9fb/badge)](https://www.skillsdirectory.com/skills/tools-only-1638-plan-8882b9fb)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Plan: Refactor `lifecycle.py` to `AppLifecycle`

## Current State

The `src/tunacode/ui/lifecycle.py` module contains free functions:

| Function | Responsibility |
|----------|----------------|
| `on_mount(app)` | Entry point — theme, session metadata, setup-vs-repl branch |
| `_on_setup_complete(app, completed)` | Setup dismiss callback |
| `_start_repl(app)` | Logger wiring, request worker, timer, welcome |
| `on_unmount(app)` | Stop timer + save session |

### Problems

- Lifecycle state is implicit and spread across function scope + `app` fields.
- `app` is threaded through every call.
- Setup callback indirection (`_on_setup_callback`) adds noise.
- `_start_repl` can be reached from two paths with no explicit idempotency guard.
- Mount/unmount logic is not represented as one cohesive object.

## Proposed Design

Create an `AppLifecycle` class that owns lifecycle sequencing and mount/unmount flow.

```python
class AppLifecycle:
    """Manage TunaCode app lifecycle stages."""

    def __init__(self, app: TextualReplApp) -> None:
        self._app = app
        self._state_manager = app.state_manager
        self._repl_started = False

    def mount(self) -> None:
        self._init_theme()
        self._init_session_metadata()
        if self._app._show_setup:
            self._push_setup_screen()
            return
        self._start_repl()

    async def unmount(self) -> None:
        self._stop_slopgotchi_timer()
        await self._state_manager.save_session()

    def _push_setup_screen(self) -> None:
        self._app.push_screen(SetupScreen(self._state_manager), self._on_setup_complete)

    def _on_setup_complete(self, completed: bool | None) -> None:
        if completed:
            self._app._update_resource_bar()
        self._start_repl()

    def _start_repl(self) -> None:
        if self._repl_started:
            return
        self._repl_started = True
        ...
```

## App Integration (Critical)

`TextualReplApp` must persist one lifecycle instance:

```python
self._lifecycle = AppLifecycle(self)
self._lifecycle.mount()
...
await self._lifecycle.unmount()
```

Do **not** instantiate a fresh `AppLifecycle` in `on_unmount`; that would drop mount-time state like `_repl_started`.

## Timer Ownership

Keep timer ownership on `TextualReplApp._slopgotchi_timer` (existing field) to avoid split state and minimize migration risk.

`AppLifecycle` starts/stops that timer via dedicated helper methods.

## Benefits

1. Single lifecycle controller with explicit sequencing.
2. Cleaner app boundary: `mount()` / `unmount()` as the public lifecycle API.
3. Fewer cross-function parameters (`self._app` replaces repeated `app`).
4. Idempotent REPL startup via `_repl_started` guard.
5. Easier to unit test by mocking app/state manager boundaries.

## Implementation Steps

1. Replace free functions in `lifecycle.py` with `AppLifecycle` methods.
2. Update `TextualReplApp` to hold `self._lifecycle: AppLifecycle | None`.
3. In `on_mount`, create + store lifecycle, then call `mount()`.
4. In `on_unmount`, assert lifecycle exists, then call `await unmount()`.
5. Remove old function imports/call sites from `app.py`.
6. Run lint + tests.

## Validation Checklist

- Mount with setup disabled starts worker, logger callback, timer, welcome exactly once.
- Mount with setup enabled starts REPL after setup dismiss callback.
- `_start_repl` remains idempotent.
- Unmount stops timer and saves session.
- No remaining imports of `on_mount`/`on_unmount` from `tunacode.ui.lifecycle`.

## Notes

- Keep lazy import of `SetupScreen` and `show_welcome` to avoid circular dependencies.
- Preserve current behavior where setup dismissal still enters REPL flow (regardless of completion value).

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.