Skip to content
Back to skills

061 Architecture 37005839

ASecurity

Tunacode is a TUI (Terminal User Interface) code agent built with a **layered, component-based architecture**. The system is designed with clear separation of concerns between the presentation layer (UI), the core agent logic, tool capabilities, and configuration management.

  • 9 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 11, 2026
testingpythongoshellbashreactnodetestingdebugginggitapi

Works with

  • terminal
  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 061-architecture_37005839 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 061 Architecture 37005839?

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

Security grade badge for 061 Architecture 37005839
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-061-architecture-37005839/badge)](https://www.skillsdirectory.com/skills/tools-only-061-architecture-37005839)

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

SKILL.md
# Tunacode Architecture Documentation

## Overview

Tunacode is a TUI (Terminal User Interface) code agent built with a **layered, component-based architecture**. The system is designed with clear separation of concerns between the presentation layer (UI), the core agent logic, tool capabilities, and configuration management.

**File**: `<repo_root>/src`

**Version**: 0.1.20

**Python Version**: 3.11-3.13

---

## 1. Architectural Pattern

The project follows a **pragmatic layered architecture** with strong component-based design principles:

```
┌─────────────────────────────────────────────────────────────┐
│                     Presentation Layer                       │
│                    (src/tunacode/ui/)                        │
│  Built with Textual framework - handles user interaction     │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│                      Application Layer                       │
│                   (src/tunacode/core/)                       │
│  Agent orchestration, request processing, state management   │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│                       Domain Layer                           │
│                    (src/tunacode/tools/)                     │
│  Tool functions - bash, grep, read_file, etc.               │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│                   Infrastructure Layer                       │
│  (src/tunacode/configuration/, indexing/, lsp/, utils/)      │
│  Configuration, indexing, LSP integration, utilities        │
└─────────────────────────────────────────────────────────────┘
```

### Key Architectural Principles

1. **UI/Core Decoupling**: The core agent logic is completely independent of the Textual UI, enabling headless execution and testing
2. **Callback-based Communication**: Layers communicate through callbacks rather than direct dependencies
3. **Centralized State Management**: A `StateManager` holds all session state, avoiding scattered state management
4. **Modular Tool System**: Tools are self-contained functions with clear interfaces
5. **Composable System Prompts**: Prompts are composed from smaller sections for maintainability

---

## 2. Component Relationships

### Major Components

```
┌──────────────────────────────────────────────────────────────────┐
│                         UI Layer                                 │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │  TextualApp  │  │   Screens    │  │  Renderers   │          │
│  │  (app.py)    │  │ (setup,      │  │  (tools,     │          │
│  │              │  │  picker)     │  │   panels)    │          │
│  └──────────────┘  └──────────────┘  └──────────────┘          │
└──────────────────────────────────────────────────────────────────┘
         │                      │                      │
         │ requests             │ renders             │ callbacks
         ▼                      ▼                      ▼
┌──────────────────────────────────────────────────────────────────┐
│                        Core Layer                                │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │   Agent      │  │ Request      │  │  State       │          │
│  │  Config      │  │Orchestrator  │  │  Manager     │          │
│  └──────────────┘  └──────────────┘  └──────────────┘          │
└──────────────────────────────────────────────────────────────────┘
         │                      │                      │
         │ configures           │ orchestrates        │ manages
         ▼                      ▼                      ▼
┌──────────────────────────────────────────────────────────────────┐
│                       Tools Layer                                │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │    Bash      │  │    Grep      │  │  Read File   │          │
│  │   Tool       │  │    Tool      │  │    Tool      │          │
│  └──────────────┘  └──────────────┘  └──────────────┘          │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │    Glob      │  │    Tool      │  │   Decorators │          │
│  │   Tool       │  │  Utilities   │  │              │          │
│  └──────────────┘  └──────────────┘  └──────────────┘          │
└──────────────────────────────────────────────────────────────────┘
         │                      │                      │
         │ reads                │ uses                │ provides
         ▼                      ▼                      ▼
┌──────────────────────────────────────────────────────────────────┐
│                   Configuration Layer                            │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │   Models     │  │   Defaults   │  │   Settings   │          │
│  │  Registry    │  │              │  │              │          │
│  └──────────────┘  └──────────────┘  └──────────────┘          │
└──────────────────────────────────────────────────────────────────┘
```

### Dependency Graph

```
                    ┌─────────────┐
                    │    UI       │
                    └──────┬──────┘
                           │
          ┌────────────────┼────────────────┐
          ▼                ▼                ▼
    ┌───────────┐    ┌───────────┐    ┌───────────┐
    │   Core    │───▶│   Tools   │    │    LSP    │
    └─────┬─────┘    └─────┬─────┘    └───────────┘
          │                │
          └────────┬───────┘
                   ▼
            ┌──────────────┐
            │Configuration │
            └──────────────┘
```

**Note**: Dependency direction is one-way here:
- `core` imports tool functions for orchestration
- tools do not import from `core`

---

## 3. Data Flow

### Complete Request Lifecycle

```
USER INPUT
    │
    ▼
┌───────────────────────────────────────────────────────────────┐
│ 1. INPUT CAPTURE (UI Layer)                                   │
│    - User types in Editor widget (ui/app.py)                 │
│    - EditorSubmitRequested message posted                     │
│    - Message placed on request_queue                          │
└───────────────────────────────────────────────────────────────┘
    │
    ▼
┌───────────────────────────────────────────────────────────────┐
│ 2. REQUEST PROCESSING (Core Layer)                            │
│    - _request_worker dequeues from request_queue              │
│    - Calls process_request() (core/agents/main.py)           │
│    - RequestOrchestrator instantiated                          │
│    - Agent created/retrieved from cache                       │
└───────────────────────────────────────────────────────────────┘
    │
    ▼
┌───────────────────────────────────────────────────────────────┐
│ 3. AGENT EXECUTION (Core Layer)                               │
│    - System prompt composed from sections                     │
│    - Agent.iter() loop begins                                 │
│    - Model request sent to LLM API                            │
│    - Response streamed back                                   │
└───────────────────────────────────────────────────────────────┘
    │
    ▼
┌───────────────────────────────────────────────────────────────┐
│ 4. TOOL SELECTION (Core Layer)                                │
│    - Node processor inspects response                         │
│    - Tool calls extracted from structured output              │
│    - tool_callback invoked (ui/repl_support.py)              │
└───────────────────────────────────────────────────────────────┘
    │
    ▼
┌───────────────────────────────────────────────────────────────┐
│ 5. TOOL DISPATCH (Core Layer)                                 │
│    - Tool calls registered and batched                        │
│    - Parallel execution scheduled                             │
└───────────────────────────────────────────────────────────────┘
    │
    ▼
┌───────────────────────────────────────────────────────────────┐
│ 6. TOOL EXECUTION (Tools Layer)                               │
│    - Tool function executed (e.g., bash, grep)                │
│    - Return value captured                                    │
│    - Results recorded in the run message history              │
└───────────────────────────────────────────────────────────────┘
    │
    ▼
┌───────────────────────────────────────────────────────────────┐
│ 7. RESPONSE RENDERING (UI Layer)                              │
│    - streaming_callback updates streaming_output              │
│    - tool_result_callback creates Panel for results           │
│    - Final response written to RichLog                        │
│    - Token usage and cost calculated                          │
└───────────────────────────────────────────────────────────────┘
    │
    ▼
STATE UPDATE
    │
    ▼
┌───────────────────────────────────────────────────────────────┐
│ 8. STATE MANAGEMENT (Core Layer)                              │
│    - SessionState messages synced from agent_run history       │
│    - Token usage updated                                      │
│    - Context window managed via compaction                    │
└───────────────────────────────────────────────────────────────┘
```

### Callback Communication Flow

```
┌─────────────────┐                    ┌─────────────────┐
│      UI         │                    │      Core       │
│  (TextualApp)   │                    │ (Orchestrator)  │
└────────┬────────┘                    └────────┬────────┘
         │                                     │
         │  streaming_callback(text)           │
         │  tool_callback(tool_name)           │
         │  tool_result_callback(result)       │
         │  tool_start_callback()              │
         │------------------------------------>│
         │                                     │
         │                                     │  Execute tool
         │                                     │  Stream response
         │                                     │
         │  Update UI widgets                  │
         │  Show confirmation dialog           │
         │  Render tool output                 │
         │<-------------------------------------│
         │                                     │
```

---

## 4. Design Patterns

### 1. Factory & Singleton Pattern

**Location**: `src/tunacode/core/agents/agent_components/agent_config.py`

```python
def get_or_create_agent(model: ModelName, state_manager: StateManager) -> PydanticAgent:
    # Factory: Creates agent instances
    # Singleton: Caches agents per model
    if model not in _agent_cache:
        agent = Agent(
            model=model_instance,
            system_prompt=system_prompt,
            tools=tools_list,
        )
        _agent_cache[model] = agent
    return _agent_cache[model]
```

**Purpose**: Avoid expensive re-initialization of agents while allowing dynamic creation.

### 2. Strategy Pattern

**Location**: `src/tunacode/tools/grep.py`

```python
def grep(..., search_type: Literal["smart", "ripgrep", "python", "hybrid"] = "smart"):
    # Dynamically selects search algorithm based on context
    if search_type == "smart":
        # Choose best algorithm automatically
    elif search_type == "ripgrep":
        # Use ripgrep
    elif search_type == "python":
        # Use pure Python
```

**Purpose**: Allow runtime selection of search algorithms based on file count and context.

### 3. Decorator Pattern

**Location**: `src/tunacode/tools/decorators.py`

```python
@file_tool
def read_file(file_path: str) -> str:
    # Decorator adds error handling and LSP diagnostics
    ...

@base_tool
def bash(command: str) -> str:
    # Decorator adds error handling and retry logic
    ...
```

**Purpose**: Add cross-cutting concerns (error handling, retries) without cluttering tool logic.

### 4. Observer Pattern

**Location**: Throughout UI/Core communication

```python
# Core notifies UI of state changes
async def process_request(
    streaming_callback: Callable[[str], None],
    tool_callback: Callable[[ToolCall], None],
    tool_result_callback: Callable[[ToolResult], None],
    ...
):
    # Core (Subject) notifies UI (Observer) via callbacks
    await streaming_callback(token)
    await tool_callback(tool_call)
    await tool_result_callback(result)
```

**Purpose**: Enable UI to react to core events without tight coupling.

### 5. Composite Pattern

**Location**: `src/tunacode/core/agents/delegation_tools.py`

```python
# A tool that is itself an agent
def research_codebase(query: str) -> str:
    # Main agent delegates to specialized research agent
    research_agent = get_or_create_agent(model, ...)
    return await research_agent.run(query)
```

**Purpose**: Enable hierarchical agent organization where agents can delegate to specialized sub-agents.

### 6. State Management Pattern

**Location**: `src/tunacode/core/state.py`

```python
class StateManager:
    def __init__(self):
        self.session = SessionState(
            messages=[],
            model=None,
            token_usage=TokenUsage(),
            ...
        )

# Centralized state store passed to all components
state_manager = StateManager()
app = TextualReplApp(state_manager=state_manager)
```

**Purpose**: Single source of truth for application state, avoiding scattered state management.

---

## 5. Module Dependencies

### Dependency Hierarchy

```
Level 0 (Foundation):
├── constants
├── types
└── exceptions

Level 1 (Utilities):
├── configuration
│   ├── models (model registry, pricing)
│   ├── defaults (default config)
│   └── settings (API keys, user config)
├── utils
│   ├── config
│   ├── messaging
│   ├── parsing
│   └── system
├── lsp
├── prompting
│   └── sections
└── indexing

Level 2 (Core Logic):
├── core
│   ├── agents (agent orchestration)
│   ├── background (async task management)
│   ├── logging
│   ├── setup
│   └── token_usage
└── tools
    ├── grep_components
    ├── prompts
    └── utils

Level 3 (Application):
├── ui
│   ├── screens (setup, picker, confirm)
│   ├── renderers (tools, panels)
│   ├── components
│   ├── widgets
│   └── styles
└── services (currently empty)
```

### Import Flow Analysis

**Tightly Coupled**:
- `core` -> `tools` (one-way orchestration dependency)
  - `core` imports tool functions for agent configuration and dispatch

**Loosely Coupled**:
- `configuration` - Standalone, only depends on foundation types
- `constants`, `types`, `exceptions` - No internal dependencies
- `indexing`, `lsp`, `prompting` - Limited, specific dependencies

**Highly Coupled to UI**:
- `ui` depends on nearly all modules (core, tools, configuration, indexing, lsp)

### External Dependencies

| Package | Version | Purpose |
|---------|---------|---------|
| `textual` | ^4.0.0 | TUI framework |
| `pydantic-ai` | ^1.18.0 | AI agent framework |
| `pydantic` | ^2.12.4 | Data validation |
| `typer` | ^0.15.0 | CLI framework |
| `rich` | ^14.2.0 | Terminal formatting |
| `pathspec` | ^0.12.1 | Gitignore pattern matching |
| `html2text` | ^2024.2.26 | HTML to Markdown conversion |
| `python-Levenshtein` | ^0.21.0 | String similarity |
| `ruff` | ^0.14.0 | Linting and formatting |
| `textual-autocomplete` | ^4.0.6 | Autocomplete widget |
| `defusedxml` | * | Secure XML parsing |
| `prompt_toolkit` | ^3.0.52 | REPL prompt handling |
| `click` | ^8.1.0 | CLI utilities |

---

## 6. Key Architectural Decisions

### Decision 1: UI/Core Decoupling

**Rationale**: Enable headless execution, simplify testing, support future UI implementations

**Implementation**:
- Callback-based communication
- `StateManager` as shared state object
- Core logic has no Textual dependencies

**Files**:
- `<repo_root>/src/tunacode/ui/app.py`
- `<repo_root>/src/tunacode/core/agents/main.py`

### Decision 2: Modular System Prompts

**Rationale**: Make prompts maintainable, composable, and customizable per agent type

**Implementation**:
- Prompts composed from sections in `src/tunacode/core/prompting/sections/`
- `SectionLoader` and `compose_prompt` for assembly
- Different agents use different prompt combinations

**Files**:
- `<repo_root>/src/tunacode/core/prompting/`
- `<repo_root>/src/tunacode/prompts/sections/`

### Decision 3: Agent as Iterator

**Rationale**: Provide clean, pull-based model for consuming agent execution steps

**Implementation**:
- Agent execution exposed as async iterator
- UI consumes steps as they arrive
- Avoids callback hell

**Files**:
- `<repo_root>/src/tunacode/core/agents/main.py`

### Decision 4: Centralized State Management

**Rationale**: Single source of truth, explicit state changes, easier debugging

**Implementation**:
- `SessionState` dataclass holds all session data
- `StateManager` provides access and updates
- State passed to key components

**Files**:
- `<repo_root>/src/tunacode/core/state.py`

---

## 7. Separation of Concerns

### UI Layer (`src/tunacode/ui/`)

**Responsibility**: Presentation and user interaction

**Key Files**:
- `app.py` - Main Textual application
- `screens/` - Setup, picker, and confirmation screens
- `renderers/` - Tool output rendering
- `components/` - Reusable UI components
- `widgets/` - Custom widgets

**Does NOT**:
- Execute tool logic
- Make agent decisions
- Manage business logic

### Core Layer (`src/tunacode/core/`)

**Responsibility**: Agent orchestration and business logic

**Key Files**:
- `agents/main.py` - Request processing entry point
- `agents/agent_config.py` - Agent factory
- `agents/agent_components/node_processor.py` - Response processing
- `state.py` - State management
- `prompting/` - Prompt composition

**Does NOT**:
- Render UI
- Execute file operations directly
- Handle Textual framework details

### Tools Layer (`src/tunacode/tools/`)

**Responsibility**: Agent capabilities and system interactions

**Key Files**:
- `bash.py` - Shell command execution
- `grep.py` - Code search
- `read_file.py` - File reading

**Does NOT**:
- Orchestrate agent flow
- Render output
- Manage UI state

### Configuration Layer (`src/tunacode/configuration/`)

**Responsibility**: Settings and model data

**Key Files**:
- `models.py` - Model registry
- `defaults.py` - Default configuration
- `settings.py` - User settings

**Does NOT**:
- Execute logic
- Manage state
- Interact with UI

---

## 8. Anti-Patterns and Technical Debt

### Circular Dependency: Core ↔ Tools

**Issue**:
- `core` imports tool functions
- Some tools import from `core`

**Impact**:
- Difficult to test in isolation
- Complex initialization order
- Harder to reason about

**Potential Solutions**:
1. Extract shared interfaces to a separate module
2. Use dependency injection
3. Move shared functionality to a utilities module

### Current Locations

**Core → Tools**:
- `<repo_root>/src/tunacode/core/agents/agent_components/agent_config.py`

**Tools → Core**:
- `<repo_root>/src/tunacode/tools/todo.py`

---

## 9. Extension Points

### Adding a New Tool

1. Create tool function in `<repo_root>/src/tunacode/tools/`
2. Decorate with `@file_tool` or `@base_tool`
3. Add to tools list in `agent_config.py`
4. Optionally create custom renderer in `ui/renderers/tools/`

### Adding a New Agent Type

1. Create prompt sections in `<repo_root>/src/tunacode/prompts/sections/`
2. Compose prompt using `compose_prompt()`
3. Configure tools for agent type
4. Add agent factory logic

### Adding a New UI Screen

1. Create screen class in `<repo_root>/src/tunacode/ui/screens/`
2. Integrate with `TextualReplApp`
3. Add navigation logic

---

## 10. Testing Strategy

### Current Test Coverage

Located in `<repo_root>/tests/`:
- Tool decorator tests
- Tool conformance tests
- Compaction tests
- Tool retry logic tests

### Testing Challenges

1. **Circular Dependency**: Makes unit testing difficult
2. **Async Code**: Requires pytest-asyncio
3. **UI Code**: Textual framework requires specialized testing

### Recommended Testing Approach

1. **Unit Tests**: Test tools in isolation
2. **Integration Tests**: Test core orchestration with mock tools
3. **E2E Tests**: Test full request flow with real tools
4. **UI Tests**: Use textual-dev for screen testing

---

## 11. Performance Considerations

### Token Management

- Context window managed via `prune_old_tool_outputs()`
- Token usage tracked per request and session
- Cost calculation based on model pricing

### Caching

- Agent instances cached per model
- Code index cached
- Model registry lazy-loaded

### Async Operations

- All I/O operations are async
- Background tasks for long-running operations
- Non-blocking UI updates

---

## 13. Security Considerations

### Tool Safety

- Tool definitions enforce validation and limits
- Bash commands validated before execution
- File operations enforce size and path checks

### Input Validation

- Pydantic models for all inputs
- Path sanitization for file operations
- Shell command validation

### Dependency Security

- `defusedxml` for secure XML parsing
- Bandit security scanning configured
- Regular dependency updates

---

## Conclusion

Tunacode demonstrates a well-structured, modular architecture with clear separation of concerns. The primary architectural strength is the UI/Core decoupling, enabling multiple UI implementations and headless operation. Core-to-tools dependencies are now one-way, keeping the tool layer isolated from core internals.

The callback-based communication pattern, centralized state management, and composable system prompts all contribute to a maintainable and extensible codebase.

---

**Generated**: 2026-01-04
**Analysis Tool**: Gemini MCP (gemini-2.5-pro)
**Codebase Version**: 0.1.20

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.