replkit2


Namereplkit2 JSON
Version 0.7.2 PyPI version JSON
download
home_pageNone
SummaryA minimal Python framework for building stateful REPL applications with ASCII display
upload_time2025-08-10 21:08:33
maintainerNone
docs_urlNone
authorNone
requires_python>=3.12
licenseMIT
keywords cli framework mcp repl stateful terminal
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # ReplKit2

Flask-style framework for building stateful REPL applications with rich display, MCP integration, and multi-mode deployment.

## ✨ Features

- 🚀 **Multi-mode**: REPL, CLI, MCP server from one codebase
- 🎨 **Rich display**: Tables, trees, boxes, charts, markdown
- 🔌 **MCP ready**: Tools, resources, prompts for Claude/LLMs
- ⚡ **CLI support**: Traditional command-line interface via Typer
- 🎯 **Type safe**: Full type hints, MCP-compatible validation

## 📦 Installation

```bash
# With uv (recommended)
uv add replkit2                      # Core library only
uv add "replkit2[all]"               # MCP + CLI support
uv add "replkit2[mcp,cli]"           # Same as above
uv add "replkit2[examples]"          # For running examples

# Or with pip
pip install replkit2
pip install replkit2[all]            # MCP + CLI support
pip install replkit2[mcp,cli]        # Same as above
pip install replkit2[examples]       # For running examples
```

## 🚀 Quick Start

```python
from dataclasses import dataclass, field
from replkit2 import App

@dataclass
class State:
    tasks: list = field(default_factory=list)
    next_id: int = 1

app = App("todo", State)

@app.command(display="table", headers=["ID", "Task", "Done"])
def list_tasks(state):
    """List all tasks."""
    return [{"ID": t["id"], "Task": t["text"], "Done": "✓" if t["done"] else ""} 
            for t in state.tasks]

@app.command()
def add(state, text: str):
    """Add a task."""
    task = {"id": state.next_id, "text": text, "done": False}
    state.tasks.append(task)
    state.next_id += 1
    return f"Added: {text}"

@app.command()
def done(state, id: int):
    """Mark task as done."""
    for task in state.tasks:
        if task["id"] == id:
            task["done"] = True
            return f"Completed task {id}"
    return f"Task {id} not found"

if __name__ == "__main__":
    import sys
    if "--mcp" in sys.argv:
        app.mcp.run()     # MCP server for Claude/LLMs
    elif "--cli" in sys.argv:
        app.cli()         # Traditional CLI
    else:
        app.run(title="Todo Manager")  # Interactive REPL
```

**Run it:**
```bash
python todo.py                    # Interactive REPL
python todo.py --cli add "Buy milk"  # CLI mode
python todo.py --cli list_tasks      # CLI mode
python todo.py --mcp              # MCP server
```

## 🎨 Display Types

```python
@app.command(display="table", headers=["Name", "Status"])
def show_table(state):
    return [{"Name": "Item 1", "Status": "Active"}]

@app.command(display="box", title="Info")
def show_box(state):
    return "This is in a box!"

@app.command(display="tree")
def show_tree(state):
    return {"root": {"child1": "leaf", "child2": ["item1", "item2"]}}

@app.command(display="progress", show_percentage=True)
def show_progress(state):
    return {"value": 7, "total": 10}
```

## 🔌 MCP Integration

```python
# Tool (callable action)
@app.command(fastmcp={"type": "tool"})
def process(state, text: str, count: int = 1):
    return f"Processed '{text}' {count} times"

# Resource (readable data at app://get_task/123)
@app.command(fastmcp={"type": "resource"})
def get_task(state, id: int):
    return {"id": id, "data": state.tasks.get(id)}

# Prompt template
@app.command(fastmcp={"type": "prompt"})
def brainstorm(state, topic: str = ""):
    context = "\n".join(t["text"] for t in state.tasks[:5])
    return f"Based on these tasks:\n{context}\n\nBrainstorm about: {topic}"
```

Configure Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
  "mcpServers": {
    "todo": {
      "command": "python",
      "args": ["/path/to/todo.py", "--mcp"]
    }
  }
}
```

## ⚡ CLI Support

```python
@app.command(
    typer={"name": "ls", "help": "List tasks with filters"}
)
def list_tasks(state, done: bool = False, limit: int = 10):
    tasks = [t for t in state.tasks if not done or t.get("done")]
    return tasks[:limit]

# Usage:
# python todo.py --cli ls --done --limit 5
# python todo.py --cli add "New task"
# python todo.py --cli done 1
```

## 🎯 Type Safety

```python
# ✅ Good - MCP compatible
def cmd(state, 
    required: str,                  # Required param
    optional: str = None,           # Optional with None
    items: List[str] = None,        # Typed list
    config: Dict[str, int] = None,  # Typed dict
):
    pass

# ❌ Bad - causes "unknown" in MCP
def cmd(state,
    untyped,                        # Missing annotation
    opt: Optional[str] = None,      # Don't use Optional
    either: Union[str, int] = "",   # Don't use Union
):
    pass
```

## 📁 Examples

- **[`todo.py`](examples/todo.py)** - Full task manager with persistence
- **[`notes_mcp.py`](examples/notes_mcp.py)** - MCP server with all types
- **[`monitor.py`](examples/monitor.py)** - System monitoring dashboard
- **[`typer_demo.py`](examples/typer_demo.py)** - CLI with JSON state
- **[`markdown_demo.py`](examples/markdown_demo.py)** - Markdown rendering

Run examples:
```bash
cd examples
python todo.py                  # REPL mode
python notes_mcp.py --mcp        # MCP server
python typer_demo.py --cli --help  # CLI help
```

## 📚 Documentation

- [CHANGELOG.md](CHANGELOG.md) - Version history
- [ROADMAP.md](ROADMAP.md) - Future plans  
- [CLAUDE.md](CLAUDE.md) - Development guide
- [src/replkit2/llms.txt](src/replkit2/llms.txt) - LLM quick reference

## 🛠️ Development

```bash
# Clone and install
git clone https://github.com/angelsen/replkit2
cd replkit2
uv sync --group dev

# Type check
uv run basedpyright src/replkit2

# Format & lint
uv run ruff format src/
uv run ruff check src/
```

## 📄 License

MIT - see [LICENSE](LICENSE) for details.
            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "replkit2",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.12",
    "maintainer_email": null,
    "keywords": "cli, framework, mcp, repl, stateful, terminal",
    "author": null,
    "author_email": "Fredrik Angelsen <fredrikangelsen@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/e9/e5/60854fb976235d18c844c49fc6e89a0e07970c136f0d8600f4b4db87a9cc/replkit2-0.7.2.tar.gz",
    "platform": null,
    "description": "# ReplKit2\n\nFlask-style framework for building stateful REPL applications with rich display, MCP integration, and multi-mode deployment.\n\n## \u2728 Features\n\n- \ud83d\ude80 **Multi-mode**: REPL, CLI, MCP server from one codebase\n- \ud83c\udfa8 **Rich display**: Tables, trees, boxes, charts, markdown\n- \ud83d\udd0c **MCP ready**: Tools, resources, prompts for Claude/LLMs\n- \u26a1 **CLI support**: Traditional command-line interface via Typer\n- \ud83c\udfaf **Type safe**: Full type hints, MCP-compatible validation\n\n## \ud83d\udce6 Installation\n\n```bash\n# With uv (recommended)\nuv add replkit2                      # Core library only\nuv add \"replkit2[all]\"               # MCP + CLI support\nuv add \"replkit2[mcp,cli]\"           # Same as above\nuv add \"replkit2[examples]\"          # For running examples\n\n# Or with pip\npip install replkit2\npip install replkit2[all]            # MCP + CLI support\npip install replkit2[mcp,cli]        # Same as above\npip install replkit2[examples]       # For running examples\n```\n\n## \ud83d\ude80 Quick Start\n\n```python\nfrom dataclasses import dataclass, field\nfrom replkit2 import App\n\n@dataclass\nclass State:\n    tasks: list = field(default_factory=list)\n    next_id: int = 1\n\napp = App(\"todo\", State)\n\n@app.command(display=\"table\", headers=[\"ID\", \"Task\", \"Done\"])\ndef list_tasks(state):\n    \"\"\"List all tasks.\"\"\"\n    return [{\"ID\": t[\"id\"], \"Task\": t[\"text\"], \"Done\": \"\u2713\" if t[\"done\"] else \"\"} \n            for t in state.tasks]\n\n@app.command()\ndef add(state, text: str):\n    \"\"\"Add a task.\"\"\"\n    task = {\"id\": state.next_id, \"text\": text, \"done\": False}\n    state.tasks.append(task)\n    state.next_id += 1\n    return f\"Added: {text}\"\n\n@app.command()\ndef done(state, id: int):\n    \"\"\"Mark task as done.\"\"\"\n    for task in state.tasks:\n        if task[\"id\"] == id:\n            task[\"done\"] = True\n            return f\"Completed task {id}\"\n    return f\"Task {id} not found\"\n\nif __name__ == \"__main__\":\n    import sys\n    if \"--mcp\" in sys.argv:\n        app.mcp.run()     # MCP server for Claude/LLMs\n    elif \"--cli\" in sys.argv:\n        app.cli()         # Traditional CLI\n    else:\n        app.run(title=\"Todo Manager\")  # Interactive REPL\n```\n\n**Run it:**\n```bash\npython todo.py                    # Interactive REPL\npython todo.py --cli add \"Buy milk\"  # CLI mode\npython todo.py --cli list_tasks      # CLI mode\npython todo.py --mcp              # MCP server\n```\n\n## \ud83c\udfa8 Display Types\n\n```python\n@app.command(display=\"table\", headers=[\"Name\", \"Status\"])\ndef show_table(state):\n    return [{\"Name\": \"Item 1\", \"Status\": \"Active\"}]\n\n@app.command(display=\"box\", title=\"Info\")\ndef show_box(state):\n    return \"This is in a box!\"\n\n@app.command(display=\"tree\")\ndef show_tree(state):\n    return {\"root\": {\"child1\": \"leaf\", \"child2\": [\"item1\", \"item2\"]}}\n\n@app.command(display=\"progress\", show_percentage=True)\ndef show_progress(state):\n    return {\"value\": 7, \"total\": 10}\n```\n\n## \ud83d\udd0c MCP Integration\n\n```python\n# Tool (callable action)\n@app.command(fastmcp={\"type\": \"tool\"})\ndef process(state, text: str, count: int = 1):\n    return f\"Processed '{text}' {count} times\"\n\n# Resource (readable data at app://get_task/123)\n@app.command(fastmcp={\"type\": \"resource\"})\ndef get_task(state, id: int):\n    return {\"id\": id, \"data\": state.tasks.get(id)}\n\n# Prompt template\n@app.command(fastmcp={\"type\": \"prompt\"})\ndef brainstorm(state, topic: str = \"\"):\n    context = \"\\n\".join(t[\"text\"] for t in state.tasks[:5])\n    return f\"Based on these tasks:\\n{context}\\n\\nBrainstorm about: {topic}\"\n```\n\nConfigure Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_config.json`):\n```json\n{\n  \"mcpServers\": {\n    \"todo\": {\n      \"command\": \"python\",\n      \"args\": [\"/path/to/todo.py\", \"--mcp\"]\n    }\n  }\n}\n```\n\n## \u26a1 CLI Support\n\n```python\n@app.command(\n    typer={\"name\": \"ls\", \"help\": \"List tasks with filters\"}\n)\ndef list_tasks(state, done: bool = False, limit: int = 10):\n    tasks = [t for t in state.tasks if not done or t.get(\"done\")]\n    return tasks[:limit]\n\n# Usage:\n# python todo.py --cli ls --done --limit 5\n# python todo.py --cli add \"New task\"\n# python todo.py --cli done 1\n```\n\n## \ud83c\udfaf Type Safety\n\n```python\n# \u2705 Good - MCP compatible\ndef cmd(state, \n    required: str,                  # Required param\n    optional: str = None,           # Optional with None\n    items: List[str] = None,        # Typed list\n    config: Dict[str, int] = None,  # Typed dict\n):\n    pass\n\n# \u274c Bad - causes \"unknown\" in MCP\ndef cmd(state,\n    untyped,                        # Missing annotation\n    opt: Optional[str] = None,      # Don't use Optional\n    either: Union[str, int] = \"\",   # Don't use Union\n):\n    pass\n```\n\n## \ud83d\udcc1 Examples\n\n- **[`todo.py`](examples/todo.py)** - Full task manager with persistence\n- **[`notes_mcp.py`](examples/notes_mcp.py)** - MCP server with all types\n- **[`monitor.py`](examples/monitor.py)** - System monitoring dashboard\n- **[`typer_demo.py`](examples/typer_demo.py)** - CLI with JSON state\n- **[`markdown_demo.py`](examples/markdown_demo.py)** - Markdown rendering\n\nRun examples:\n```bash\ncd examples\npython todo.py                  # REPL mode\npython notes_mcp.py --mcp        # MCP server\npython typer_demo.py --cli --help  # CLI help\n```\n\n## \ud83d\udcda Documentation\n\n- [CHANGELOG.md](CHANGELOG.md) - Version history\n- [ROADMAP.md](ROADMAP.md) - Future plans  \n- [CLAUDE.md](CLAUDE.md) - Development guide\n- [src/replkit2/llms.txt](src/replkit2/llms.txt) - LLM quick reference\n\n## \ud83d\udee0\ufe0f Development\n\n```bash\n# Clone and install\ngit clone https://github.com/angelsen/replkit2\ncd replkit2\nuv sync --group dev\n\n# Type check\nuv run basedpyright src/replkit2\n\n# Format & lint\nuv run ruff format src/\nuv run ruff check src/\n```\n\n## \ud83d\udcc4 License\n\nMIT - see [LICENSE](LICENSE) for details.",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A minimal Python framework for building stateful REPL applications with ASCII display",
    "version": "0.7.2",
    "project_urls": {
        "Issues": "https://github.com/angelsen/replkit2/issues",
        "Repository": "https://github.com/angelsen/replkit2"
    },
    "split_keywords": [
        "cli",
        " framework",
        " mcp",
        " repl",
        " stateful",
        " terminal"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "7201984669c164ec4b2fb496c218f1f54765e05f3041cc1addf33a435136742e",
                "md5": "615ec877fe29d01f8ac496072cfc3ef7",
                "sha256": "8b472c9de6f488016785d0fa275de51b9d95fe292db0d4ba3d6a404108572214"
            },
            "downloads": -1,
            "filename": "replkit2-0.7.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "615ec877fe29d01f8ac496072cfc3ef7",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.12",
            "size": 34721,
            "upload_time": "2025-08-10T21:08:31",
            "upload_time_iso_8601": "2025-08-10T21:08:31.858175Z",
            "url": "https://files.pythonhosted.org/packages/72/01/984669c164ec4b2fb496c218f1f54765e05f3041cc1addf33a435136742e/replkit2-0.7.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "e9e560854fb976235d18c844c49fc6e89a0e07970c136f0d8600f4b4db87a9cc",
                "md5": "597e17f9eadb33b95dc4d7199af8b996",
                "sha256": "971b58df19d97fb377e558c56c6a97f8088a78a1e7fdc2e03995b0159cf1d757"
            },
            "downloads": -1,
            "filename": "replkit2-0.7.2.tar.gz",
            "has_sig": false,
            "md5_digest": "597e17f9eadb33b95dc4d7199af8b996",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.12",
            "size": 94849,
            "upload_time": "2025-08-10T21:08:33",
            "upload_time_iso_8601": "2025-08-10T21:08:33.297633Z",
            "url": "https://files.pythonhosted.org/packages/e9/e5/60854fb976235d18c844c49fc6e89a0e07970c136f0d8600f4b4db87a9cc/replkit2-0.7.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-10 21:08:33",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "angelsen",
    "github_project": "replkit2",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "replkit2"
}
        
Elapsed time: 0.51329s