Compare commits

..

7 Commits

Author SHA1 Message Date
HAL9000 f5d187086c docs: add getting started tutorial 2026-04-19 11:43:46 +00:00
HAL9000 9a5ccc6b01 Merge pull request 'Update timeline: milestone status for 2026-04-19' (#10684) from timeline-update-2026-04-19-final into master
CI / benchmark-regression (push) Failing after 0s
CI / benchmark-publish (push) Failing after 0s
CI / helm (push) Successful in 40s
CI / push-validation (push) Successful in 22s
CI / build (push) Successful in 3m57s
CI / lint (push) Successful in 4m13s
CI / quality (push) Successful in 4m34s
CI / typecheck (push) Successful in 4m56s
CI / security (push) Successful in 5m4s
CI / e2e_tests (push) Successful in 8m2s
CI / integration_tests (push) Successful in 8m8s
CI / unit_tests (push) Successful in 9m27s
CI / coverage (push) Successful in 14m53s
CI / docker (push) Successful in 1m49s
CI / status-check (push) Successful in 3s
2026-04-19 06:18:05 +00:00
HAL9000 f167098541 Merge branch 'master' into timeline-update-2026-04-19-final
CI / helm (pull_request) Successful in 42s
CI / lint (pull_request) Successful in 3m58s
CI / build (pull_request) Successful in 4m1s
CI / typecheck (pull_request) Successful in 4m40s
CI / quality (pull_request) Successful in 4m42s
CI / security (pull_request) Successful in 5m22s
CI / push-validation (pull_request) Successful in 23s
CI / integration_tests (pull_request) Successful in 8m7s
CI / e2e_tests (pull_request) Successful in 8m31s
CI / unit_tests (pull_request) Successful in 9m44s
CI / coverage (pull_request) Successful in 14m52s
CI / docker (pull_request) Successful in 2m3s
CI / status-check (pull_request) Successful in 3s
2026-04-19 05:27:54 +00:00
HAL9000 072f470212 fix(agents): make bug-hunt-pool-supervisor tracking non-blocking to prevent initialization hangs
CI / helm (pull_request) Successful in 40s
CI / push-validation (pull_request) Successful in 23s
CI / lint (pull_request) Successful in 4m0s
CI / typecheck (pull_request) Successful in 4m23s
CI / build (pull_request) Successful in 3m36s
CI / quality (pull_request) Successful in 4m15s
CI / security (pull_request) Successful in 4m37s
CI / e2e_tests (pull_request) Successful in 7m43s
CI / integration_tests (pull_request) Successful in 8m44s
CI / unit_tests (pull_request) Successful in 9m18s
CI / docker (pull_request) Successful in 1m37s
CI / coverage (pull_request) Successful in 14m53s
CI / benchmark-regression (push) Failing after 0s
CI / benchmark-publish (push) Failing after 0s
CI / status-check (pull_request) Successful in 5s
CI / helm (push) Successful in 32s
CI / build (push) Successful in 3m48s
CI / lint (push) Successful in 3m55s
CI / quality (push) Successful in 4m23s
CI / typecheck (push) Successful in 4m48s
CI / security (push) Successful in 4m51s
CI / push-validation (push) Successful in 23s
CI / e2e_tests (push) Successful in 7m30s
CI / integration_tests (push) Successful in 7m44s
CI / unit_tests (push) Successful in 9m11s
CI / docker (push) Successful in 1m38s
CI / coverage (push) Successful in 14m50s
CI / status-check (push) Successful in 13s
The automation-tracking-manager call in step 5 was blocking the main loop
indefinitely, causing 3+ consecutive initialization failures. This commit
documents the fix in CHANGELOG.md with the proper issue reference.

ISSUES CLOSED: #8835
2026-04-19 04:02:17 +00:00
HAL9000 1f95ea0c2a Update timeline: milestone status for 2026-04-19
CI / push-validation (pull_request) Successful in 35s
CI / helm (pull_request) Successful in 38s
CI / lint (pull_request) Successful in 4m2s
CI / quality (pull_request) Successful in 4m18s
CI / typecheck (pull_request) Successful in 4m43s
CI / security (pull_request) Successful in 4m48s
CI / build (pull_request) Successful in 3m48s
CI / e2e_tests (pull_request) Successful in 6m57s
CI / integration_tests (pull_request) Successful in 7m48s
CI / unit_tests (pull_request) Successful in 10m11s
CI / docker (pull_request) Successful in 1m37s
CI / coverage (pull_request) Successful in 14m56s
CI / status-check (pull_request) Successful in 5s
2026-04-19 04:00:21 +00:00
HAL9000 832d0b26ae Update timeline: milestone status for 2026-04-19 2026-04-19 03:59:57 +00:00
HAL9000 89baa0a525 Update timeline: milestone status for 2026-04-19 2026-04-19 03:59:35 +00:00
13 changed files with 460 additions and 5815 deletions
+8
View File
@@ -28,6 +28,14 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
correctly in all deployment modes: Docker containers, local pip installs
(wheel or editable), and development environments.
- **TDD Non-AssertionError Guard Visibility** (#8294): `apply_tdd_inversion` in
- **bug-hunt-pool-supervisor Non-Blocking Tracking** (#8835): The automation-tracking-manager
call in step 5 was blocking the main loop indefinitely, causing 3+ consecutive initialization
failures. Step 5 now explicitly marks tracking as best-effort -- if the call does not complete
within a reasonable time or fails, it is skipped and the supervisor continues to the next
cycle. A new Rule 9 reinforces that tracking must never block the main loop; core
functionality (module mapping, worker dispatch, monitoring) takes priority over status
reporting.
`features/environment.py` now emits its non-assertion exception guard warning to
both the structured logger and `stderr` via a new `_warning_with_stderr` helper.
This makes the guard firing visible in standard Behave console output and CI log
+411
View File
@@ -0,0 +1,411 @@
# Getting Started with CleverAgents
Welcome to CleverAgents! This guide will help you get up and running in about 5 minutes, walk you through your first project, and point you toward deeper learning resources.
## What is CleverAgents?
CleverAgents is a Python-first AI agent orchestration platform that lets you build, configure, and run intelligent automation workflows. It provides:
- **Unified CLI** (`agents` command) for all interactions
- **Interactive TUI** (Terminal User Interface) for hands-on agent management
- **Actor System** — composable AI agents with tools and skills
- **Plan Lifecycle** — structured workflow from strategy to execution to application
- **Resource Management** — handle files, databases, containers, and more
- **Multi-Provider Support** — OpenAI, Anthropic, Google, Azure, and others
## Quick Start (5 Minutes)
### 1. Clone the Repository
```bash
git clone https://git.cleverthis.com/cleveragents/cleveragents-core.git
cd cleveragents-core
```
### 2. Set Up Your Environment
```bash
# Create a virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install CleverAgents with development dependencies
pip install -e ".[dev,tests,docs]"
# Set up pre-commit hooks and verify tooling
bash scripts/setup-dev.sh
```
### 3. Verify Installation
```bash
# Check the CLI is working
agents --version
agents --help
# Run diagnostics to check LLM provider configuration
agents diagnostics
```
### 4. Configure an LLM Provider
CleverAgents works with multiple LLM providers. Set up at least one:
**OpenAI:**
```bash
export OPENAI_API_KEY="sk-..."
```
**Anthropic:**
```bash
export ANTHROPIC_API_KEY="sk-ant-..."
```
**Google:**
```bash
export GOOGLE_API_KEY="..."
```
See [LLM Provider Configuration](#llm-provider-configuration) below for all supported providers.
### 5. Launch the Interactive TUI
```bash
# Install the TUI extra if not already installed
pip install -e ".[tui]"
# Launch the interactive terminal UI
agents tui
```
Inside the TUI, you can:
- Type messages and press `Enter` to chat with the active actor
- Press `/` to open the slash command overlay
- Press `@` to insert file/resource references
- Press `!` to enter shell mode
- Press `F1` for context-sensitive help
- Press `Ctrl+Q` to quit
## Your First Project: Hello World Agent
Let's create a simple agent that greets you and answers questions.
### Step 1: Create a Basic Actor Configuration
Create a file `my-first-actor.yaml`:
```yaml
# Simple greeting actor
name: local/hello-world
entry_node: greeter
nodes:
greeter:
model: gpt-4o # or your preferred model
tool_sources: [builtin]
system_prompt: |
You are a friendly greeting agent.
Respond warmly and helpfully to user messages.
Keep responses concise and friendly.
```
### Step 2: Use the Actor in the CLI
```bash
# Tell the agent to do something
agents tell --actor local/hello-world "Say hello and tell me what you can do"
# Or use the v3 plan workflow
agents plan use local/hello-world my-project
agents plan execute <PLAN_ID>
agents plan apply <PLAN_ID>
```
### Step 3: Use the Actor in the TUI
```bash
# Launch the TUI
agents tui
# Inside the TUI:
# 1. Press Ctrl+T to cycle through available actors
# 2. Select "local/hello-world"
# 3. Type a message and press Enter
```
## Basic Concepts
### Actors
**Actors** are the execution units of CleverAgents. Each actor:
- Is defined in YAML with a name, entry node, and node graph
- Binds an LLM, tools, and optional integrations (LSP, MCP)
- Can be built-in (e.g., `openai/gpt-4o`) or custom (e.g., `local/my-actor`)
Example actor structure:
```yaml
name: local/my-actor
entry_node: main
nodes:
main:
model: gpt-4o
tool_sources: [builtin, mcp://bash-tools]
```
See [Actor System](../architecture.md#actor-system) for details.
### Tools
**Tools** are atomic capabilities available to actors. They include:
- Built-in tools (file operations, shell commands)
- MCP (Model Context Protocol) tools from external servers
- LSP (Language Server Protocol) tools for code intelligence
- Custom tools defined in your project
Tools are registered in the `ToolRegistry` and invoked by actors during execution.
### Skills
**Skills** are composable capability bundles that expose one or more tools. They:
- Load from YAML or AgentSkills.io-compatible directories
- Support progressive disclosure (discover → activate → deactivate)
- Are tracked by the `SkillRegistry`
### Resources
**Resources** are managed external entities like files, databases, and containers. They:
- Organize into a DAG (Directed Acyclic Graph) with dependency tracking
- Support multiple types: `file`, `directory`, `sqlite`, `postgresql`, `container.docker`, etc.
- Each type has a handler implementing CRUD, checkpoint, and rollback
### Plan Lifecycle
The **Plan Lifecycle** is the central workflow abstraction:
```
Action → Strategize → Execute → Apply
↑ ↓
└──────────────────────┘
(correction/rollback)
```
| Phase | Description |
|-------|-------------|
| **Action** | User intent captured as a plan request |
| **Strategize** | LLM generates a structured plan with operations |
| **Execute** | Operations executed against resources via tools |
| **Apply** | Validated changes committed; diff reviewed and approved |
See [Plan Lifecycle](../architecture.md#plan-lifecycle) for details.
### Personas
**Personas** are named identities that bind:
- An actor (which LLM and tools to use)
- Argument presets (default parameters)
- Scope references (which resources are available)
Personas are persisted in `~/.config/cleveragents/personas/` and can be switched in the TUI with `Ctrl+T`.
### Sessions
**Sessions** are conversation histories. You can:
- Create new sessions: `agents session create --actor openai/gpt-4o`
- List sessions: `agents session list`
- Export sessions: `agents session export --session-id <ID> --output session.json`
- Import sessions: `agents session import --input session.json`
## LLM Provider Configuration
CleverAgents automatically discovers and uses configured LLM providers. Set environment variables for the providers you want to use:
| Provider | Environment Variable | Example |
|----------|----------------------|---------|
| OpenAI | `OPENAI_API_KEY` | `sk-...` |
| Anthropic | `ANTHROPIC_API_KEY` | `sk-ant-...` |
| Google | `GOOGLE_API_KEY` or `GOOGLE_GENAI_API_KEY` | `AIza...` |
| Azure OpenAI | `AZURE_OPENAI_API_KEY`, `AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_DEPLOYMENT` | See Azure docs |
| OpenRouter | `OPENROUTER_API_KEY` | `sk-or-...` |
| Groq | `GROQ_API_KEY` | `gsk_...` |
| Together | `TOGETHER_API_KEY` | `...` |
| Cohere | `COHERE_API_KEY` | `...` |
### Setting a Default Provider
```bash
# Pin the global provider
export CLEVERAGENTS_DEFAULT_PROVIDER=openai
# Pin a specific model
export CLEVERAGENTS_DEFAULT_MODEL=gpt-4o
```
### Checking Your Configuration
```bash
# See which providers are configured and which actor is selected
agents diagnostics
```
## Common First-Time Issues and Solutions
### Issue: "No LLM provider configured"
**Symptom:** Error message says no API keys found.
**Solution:**
1. Verify you've set an environment variable: `echo $OPENAI_API_KEY`
2. If empty, set it: `export OPENAI_API_KEY="sk-..."`
3. Run `agents diagnostics` to verify the provider is detected
4. Restart your terminal or shell session if you just set the variable
### Issue: "Actor not found"
**Symptom:** Error says `local/my-actor` doesn't exist.
**Solution:**
1. Check the actor file exists: `ls my-first-actor.yaml`
2. Verify the file is valid YAML (check indentation)
3. Use the full path if the file is not in the current directory
4. Built-in actors use the format `<provider>/<model>` (e.g., `openai/gpt-4o`)
### Issue: "TUI won't start"
**Symptom:** `agents tui` fails or shows a blank screen.
**Solution:**
1. Ensure the TUI extra is installed: `pip install -e ".[tui]"`
2. Check your terminal supports 256 colors: `echo $TERM`
3. Try running with explicit terminal: `TERM=xterm-256color agents tui`
4. Check for conflicting environment variables: `env | grep -i textual`
### Issue: "Tool execution fails"
**Symptom:** Actor tries to use a tool but gets an error.
**Solution:**
1. Check the tool is available: `agents tools list` (if implemented)
2. Verify tool permissions are granted (TUI shows permission overlay)
3. Check tool configuration in the actor YAML
4. Review tool documentation: see [Tool System](../architecture.md#tool-system)
### Issue: "Session not found"
**Symptom:** Error when trying to export or import a session.
**Solution:**
1. List available sessions: `agents session list`
2. Use the correct session ID from the list
3. Check the session file exists (for import): `ls session.json`
4. Verify the JSON is valid: `python -m json.tool session.json`
### Issue: "Permission denied" errors
**Symptom:** Actor can't read/write files or access resources.
**Solution:**
1. Check file permissions: `ls -la <file>`
2. Ensure the file is readable/writable by your user
3. In the TUI, approve permission requests when prompted (press `y`)
4. Check resource configuration in your project
## Next Learning Steps
Now that you're up and running, here's what to explore next:
### 1. **Understand the Architecture** (30 minutes)
- Read [Architecture Overview](../architecture.md)
- Learn about the layered design and key components
- Understand the Plan Lifecycle in detail
### 2. **Build Your First Custom Actor** (1 hour)
- Create a YAML actor configuration
- Add tools and integrations
- Test in the TUI and CLI
- See [Actor System](../architecture.md#actor-system) for details
### 3. **Work with Resources** (1 hour)
- Create file and database resources
- Use resources in your actor
- Understand the Resource DAG
- See [Resource System](../architecture.md#resource-system)
### 4. **Explore Tools and Skills** (1 hour)
- Discover available tools
- Create custom tools
- Load and manage skills
- See [Tool System](../architecture.md#tool-system) and [Skill System](../architecture.md#skill-system)
### 5. **Master the Plan Lifecycle** (2 hours)
- Create and execute plans
- Understand phase transitions
- Use correction and rollback
- See [Plan Lifecycle](../architecture.md#plan-lifecycle)
### 6. **Integrate with External Services** (2 hours)
- Set up MCP (Model Context Protocol) servers
- Configure LSP (Language Server Protocol) integration
- Use ACMS (Advanced Context Management System)
- See [MCP Integration](../architecture.md#mcp-integration) and [LSP Integration](../architecture.md#lsp-integration)
### 7. **Deploy to Production** (2 hours)
- Use server mode: `agents server connect`
- Deploy with Kubernetes (see `k8s/`)
- Configure observability and logging
- See [Server Architecture](../development/agent-system-specification.md)
## Key Documentation References
| Topic | Document |
|-------|----------|
| **Architecture** | [Architecture Overview](../architecture.md) |
| **Specification** | [Full Specification](../specification.md) |
| **API Reference** | [API Docs](../api/index.md) |
| **Development** | [Development Guide](../development/agent-system-specification.md) |
| **Testing** | [Testing Guide](../development/testing.md) |
| **FAQ** | [Frequently Asked Questions](../faq.md) |
| **Design Decisions** | [Architecture Decision Records (ADRs)](../adr/index.md) |
## Tips for Success
1. **Start Small** — Create simple actors before complex ones
2. **Use the TUI** — The interactive interface is great for learning and debugging
3. **Read the Specification**`docs/specification.md` is the authoritative source
4. **Check the Examples** — See `examples/` for real-world configurations
5. **Run Tests** — Use `nox -s unit_tests` to verify your changes
6. **Ask for Help** — Check the FAQ and ADRs for common questions
## Troubleshooting Commands
```bash
# Check your setup
agents diagnostics
# List available actors
agents actor list
# List available tools
agents tools list # if implemented
# List sessions
agents session list
# View help for any command
agents <command> --help
# Run tests to verify everything works
nox -s unit_tests
# Check code quality
nox -s lint
nox -s typecheck
```
## What's Next?
- **Build an actor** — Create a custom actor for your use case
- **Explore the TUI** — Spend time in the interactive interface
- **Read the architecture** — Understand the design decisions
- **Join the community** — Check out discussions and issues
- **Contribute** — See [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines
Happy automating! 🚀
+26 -5502
View File
File diff suppressed because one or more lines are too long
-64
View File
@@ -1,64 +0,0 @@
"""Behave steps for TUI persona cycling."""
from __future__ import annotations
import shutil
import tempfile
from pathlib import Path
from behave import given, then, when
from behave.runner import Context
from cleveragents.tui.persona.registry import PersonaRegistry
from cleveragents.tui.persona.schema import Persona
from cleveragents.tui.persona.state import PersonaState
def _registry_for_temp_dir(path: Path) -> PersonaRegistry:
return PersonaRegistry(config_dir=path)
@given("a temporary TUI persona registry")
def step_temp_registry(context: Context) -> None:
temp_dir = Path(tempfile.mkdtemp())
context.tui_persona_dir = temp_dir
context.tui_registry = _registry_for_temp_dir(temp_dir)
context.add_cleanup(lambda: shutil.rmtree(str(temp_dir), ignore_errors=True))
@given(
'I save TUI persona "{name}" with actor "{actor}" and cycle order {cycle:d}'
)
def step_save_persona_cycle(
context: Context, name: str, actor: str, cycle: int
) -> None:
persona = Persona(name=name, actor=actor, cycle_order=cycle)
context.tui_registry.save(persona)
@when('I set active persona to "{persona_name}" for session "{session_id}"')
def step_set_active_persona(
context: Context, persona_name: str, session_id: str
) -> None:
if not hasattr(context, "tui_state"):
context.tui_state = PersonaState(registry=context.tui_registry)
context.tui_state.set_active_persona(session_id, persona_name)
@when('I cycle persona for session "{session_id}"')
def step_cycle_persona(context: Context, session_id: str) -> None:
if not hasattr(context, "tui_state"):
context.tui_state = PersonaState(registry=context.tui_registry)
context.tui_state.cycle_persona(session_id)
@then('active persona for session "{session_id}" should be "{persona_name}"')
def step_active_persona(context: Context, session_id: str, persona_name: str) -> None:
persona = context.tui_state.active_persona(session_id)
assert persona.name == persona_name
@then("the registry last persona should be set to {persona_name}")
def step_registry_last_persona(context: Context, persona_name: str) -> None:
last = context.tui_registry.get_last_persona()
assert last == persona_name
@@ -236,7 +236,7 @@ def step_verify_session_active_persona(context, session_id, expected):
assert context.state.active_by_session[session_id] == expected
@then('the mock registry last persona should be set to "{expected}"')
@then('the registry last persona should be set to "{expected}"')
def step_verify_last_persona_set(context, expected):
context.mock_registry.set_last_persona.assert_called_with(expected)
-51
View File
@@ -1,51 +0,0 @@
Feature: TUI Persona Cycling
Personas can be cycled through in order using cycle_order field.
Scenario: cycle_persona cycles through personas with cycle_order > 0
Given a temporary TUI persona registry
And I save TUI persona "first" with actor "local/mock-default" and cycle order 1
And I save TUI persona "second" with actor "local/mock-default" and cycle order 2
And I save TUI persona "third" with actor "local/mock-default" and cycle order 3
When I set active persona to "first" for session "s1"
And I cycle persona for session "s1"
Then active persona for session "s1" should be "second"
When I cycle persona for session "s1"
Then active persona for session "s1" should be "third"
When I cycle persona for session "s1"
Then active persona for session "s1" should be "first"
Scenario: cycle_persona returns current persona when no cyclic personas exist
Given a temporary TUI persona registry
And I save TUI persona "noncyclic" with actor "local/mock-default" and cycle order 0
When I set active persona to "noncyclic" for session "s1"
And I cycle persona for session "s1"
Then active persona for session "s1" should be "noncyclic"
Scenario: cycle_persona starts from first when current is not in cycle
Given a temporary TUI persona registry
And I save TUI persona "cyclic1" with actor "local/mock-default" and cycle order 1
And I save TUI persona "noncyclic" with actor "local/mock-default" and cycle order 0
When I set active persona to "noncyclic" for session "s1"
And I cycle persona for session "s1"
Then active persona for session "s1" should be "cyclic1"
Scenario: cycle_persona respects cycle_order field ordering
Given a temporary TUI persona registry
And I save TUI persona "alpha" with actor "local/mock-default" and cycle order 3
And I save TUI persona "beta" with actor "local/mock-default" and cycle order 1
And I save TUI persona "gamma" with actor "local/mock-default" and cycle order 2
When I set active persona to "beta" for session "s1"
And I cycle persona for session "s1"
Then active persona for session "s1" should be "gamma"
When I cycle persona for session "s1"
Then active persona for session "s1" should be "alpha"
When I cycle persona for session "s1"
Then active persona for session "s1" should be "beta"
Scenario: cycle_persona updates last persona in registry
Given a temporary TUI persona registry
And I save TUI persona "p1" with actor "local/mock-default" and cycle order 1
And I save TUI persona "p2" with actor "local/mock-default" and cycle order 2
When I set active persona to "p1" for session "s1"
And I cycle persona for session "s1"
Then the registry last persona should be set to "p2"
+1 -1
View File
@@ -33,7 +33,7 @@ Feature: TUI Persona State Coverage
When I set persona "coder" for session "sess-6"
Then the returned persona name should be "coder"
And session "sess-6" should have active persona "coder"
And the mock registry last persona should be set to "coder"
And the registry last persona should be set to "coder"
Scenario: set_active_persona skips preset init when session already has one
Given the preset for session "sess-6b" is already set to "turbo"
+2
View File
@@ -11,6 +11,8 @@ site_dir: build/site
nav:
- Specification: specification.md
- Architecture: architecture.md
- Guides:
- Getting Started: guides/getting-started.md
- API Reference:
- Overview: api/index.md
- Core Utilities: api/core.md
+1 -15
View File
@@ -18,23 +18,9 @@ def tui_callback(
help="Run a one-shot headless startup check instead of full UI loop.",
),
] = False,
web: Annotated[
bool,
typer.Option(
"--web",
help="Launch TUI in web mode accessible via browser.",
),
] = False,
web_port: Annotated[
int,
typer.Option(
"--web-port",
help="Port for web server (default: 8000).",
),
] = 8000,
) -> None:
"""Launch the CleverAgents TUI."""
# Import lazily so non-TUI commands avoid Textual startup cost.
from cleveragents.tui.commands import run_tui
raise typer.Exit(run_tui(headless=headless, web=web, web_port=web_port))
raise typer.Exit(run_tui(headless=headless))
+2 -143
View File
@@ -2,7 +2,6 @@
from __future__ import annotations
import contextlib
import json
from collections import defaultdict
from collections.abc import Callable
@@ -224,144 +223,8 @@ class TuiCommandRouter:
return f"Import failed: {exc}"
def _get_tui_web_html(port: int) -> str:
"""Generate HTML for TUI web mode.
Parameters
----------
port:
Port number for the web server.
Returns
-------
HTML content as string.
"""
return """<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CleverAgents TUI</title>
<style>
body {
margin: 0;
padding: 0;
font-family: monospace;
background-color: #1e1e1e;
color: #f8f8f2;
}
#tui-container {
width: 100%;
height: 100vh;
overflow: hidden;
}
.loading {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
font-size: 18px;
}
</style>
</head>
<body>
<div id="tui-container">
<div class="loading">Loading CleverAgents TUI...</div>
</div>
<script>
// WebSocket connection to TUI app
// Note: This is a placeholder. Full implementation would require
// a WebSocket server in the TUI app to handle real-time rendering.
console.log("TUI Web mode loaded. WebSocket support coming soon.");
</script>
</body>
</html>"""
def _run_tui_web(app: CleverAgentsTuiApp, *, port: int = 8000) -> int:
"""Run the TUI app in web mode via HTTP server.
Parameters
----------
app:
The Textual TUI app instance.
port:
Port for the web server.
Returns
-------
Exit code (0 for success, non-zero for failure).
"""
try:
# Import web server dependencies
import threading
import webbrowser
from http.server import BaseHTTPRequestHandler, HTTPServer
class TuiWebHandler(BaseHTTPRequestHandler):
"""HTTP request handler for TUI web mode."""
def do_GET(self) -> None:
"""Handle GET requests."""
if self.path == "/" or self.path == "/index.html":
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
html = _get_tui_web_html(port)
self.wfile.write(html.encode("utf-8"))
else:
self.send_response(404)
self.end_headers()
def log_message(self, format: str, *args: Any) -> None:
"""Suppress default logging."""
pass
# Create and start HTTP server
server = HTTPServer(("127.0.0.1", port), TuiWebHandler)
server_thread = threading.Thread(target=server.serve_forever, daemon=True)
server_thread.start()
# Print startup message
url = f"http://127.0.0.1:{port}"
print(f"TUI Web mode started at {url}")
print("Press Ctrl+C to stop")
# Try to open browser
with contextlib.suppress(Exception):
webbrowser.open(url)
# Run the app in headless mode (web driver will handle rendering)
try:
app.run(headless=True)
except KeyboardInterrupt:
pass
finally:
server.shutdown()
return 0
except Exception as exc:
print(f"Error starting web mode: {exc}")
return 1
def run_tui(*, headless: bool = False, web: bool = False, web_port: int = 8000) -> int:
"""Run the Textual TUI app, headless check, or web mode.
Parameters
----------
headless:
Run a one-shot headless startup check instead of full UI loop.
web:
Launch TUI in web mode accessible via browser.
web_port:
Port for web server (default: 8000).
Returns
-------
Exit code (0 for success, non-zero for failure).
"""
def run_tui(*, headless: bool = False) -> int:
"""Run the Textual TUI app or a headless startup check."""
container = get_container()
registry = container.persona_registry()
state = container.persona_state(registry=registry)
@@ -380,9 +243,5 @@ def run_tui(*, headless: bool = False, web: bool = False, web_port: int = 8000)
return 0
app = CleverAgentsTuiApp(command_router=router, persona_state=state)
if web:
return _run_tui_web(app, port=web_port)
app.run()
return 0
+8 -10
View File
@@ -79,25 +79,23 @@ class PersonaRegistry:
return result
def resolve_export_path(self, output_path: Path) -> Path:
"""Resolve export path, accepting both absolute and relative paths."""
resolved = output_path.resolve()
# Allow absolute paths directly
if output_path.is_absolute():
return resolved
# For relative paths, ensure they stay within working directory
raise ValueError(
"Export path must be relative to current working directory"
)
base = Path.cwd().resolve()
resolved = (base / output_path).resolve()
if not resolved.is_relative_to(base):
raise ValueError("Export path must stay within working directory")
return resolved
def resolve_import_path(self, input_path: Path) -> Path:
"""Resolve import path, accepting both absolute and relative paths."""
resolved = input_path.resolve()
# Allow absolute paths directly
if input_path.is_absolute():
return resolved
# For relative paths, ensure they stay within working directory
raise ValueError(
"Import path must be relative to current working directory"
)
base = Path.cwd().resolve()
resolved = (base / input_path).resolve()
if not resolved.is_relative_to(base):
raise ValueError("Import path must stay within working directory")
return resolved
-27
View File
@@ -63,33 +63,6 @@ class PersonaState:
self.preset_by_session[session_id] = next_name
return next_name
def cycle_persona(self, session_id: str) -> Persona:
"""Cycle to the next persona in cycle_order sequence.
Only personas with cycle_order > 0 are included in the cycle.
If no cyclic personas exist, returns the current active persona.
"""
personas = self.registry.list_personas()
cyclic = sorted(
[p for p in personas if p.cycle_order > 0],
key=lambda p: p.cycle_order
)
if not cyclic:
return self.active_persona(session_id)
current = self.active_name(session_id)
current_names = [p.name for p in cyclic]
if current not in current_names:
# Current persona is not in cycle, start from first
next_persona = cyclic[0]
else:
idx = current_names.index(current)
next_persona = cyclic[(idx + 1) % len(cyclic)]
return self.set_active_persona(session_id, next_persona.name)
def effective_arguments(self, session_id: str) -> dict[str, object]:
persona = self.active_persona(session_id)
preset = self.current_preset(session_id)
Submodule work/repo deleted from 435e409df9