fix: rename Python package from pocketclaw to pocketpaw

Complete the package rename: src/pocketclaw/ → src/pocketpaw/,
all imports, pyproject.toml entry point, docs code examples,
installer references, and test patch targets updated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Rohit Kushwaha
2026-02-14 23:54:14 +05:30
parent 43c09b94b3
commit 5542941ece
302 changed files with 1701 additions and 1661 deletions

View File

@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
PocketPaw is a self-hosted AI agent that runs locally and is controlled via Telegram, Discord, Slack, WhatsApp, or a web dashboard. The Python package is named `pocketclaw` (the internal/legacy name), while the public-facing name is `pocketpaw`. Python 3.11+ required.
PocketPaw is a self-hosted AI agent that runs locally and is controlled via Telegram, Discord, Slack, WhatsApp, or a web dashboard. The Python package is named `pocketpaw` (the internal/legacy name), while the public-facing name is `pocketpaw`. Python 3.11+ required.
## Commands
@@ -56,7 +56,7 @@ python -m build
### Message Bus Pattern
The core architecture is an event-driven message bus (`src/pocketclaw/bus/`). All communication flows through three event types defined in `bus/events.py`:
The core architecture is an event-driven message bus (`src/pocketpaw/bus/`). All communication flows through three event types defined in `bus/events.py`:
- **InboundMessage** — user input from any channel (Telegram, WebSocket, CLI)
- **OutboundMessage** — agent responses back to channels (supports streaming via `is_stream_chunk`/`is_stream_end`)
@@ -104,5 +104,5 @@ The web dashboard (`frontend/`) is vanilla JS/CSS/HTML served via FastAPI+Jinja2
- **Protocol-oriented**: Core interfaces (`AgentProtocol`, `ToolProtocol`, `MemoryStoreProtocol`, `BaseChannelAdapter`) are Python `Protocol` classes for swappable implementations
- **Env vars**: All settings use `POCKETPAW_` prefix (e.g., `POCKETPAW_ANTHROPIC_API_KEY`)
- **Ruff config**: line-length 100, target Python 3.11, lint rules E/F/I/UP
- **Entry point**: `pocketclaw.__main__:main`
- **Entry point**: `pocketpaw.__main__:main`
- **Lazy imports**: Agent backends are imported inside `AgentRouter._initialize_agent()` to avoid loading unused dependencies

View File

@@ -79,7 +79,7 @@ test: add coverage for injection scanner
## Project Structure
```
src/pocketclaw/ # Core package (internal name: pocketclaw)
src/pocketpaw/ # Core package (internal name: pocketpaw)
agents/ # Agent backends + routing
bus/adapters/ # Channel adapters (Discord, Slack, etc.)
tools/builtin/ # Built-in tools

View File

@@ -68,33 +68,37 @@ title: Get Channel Status
description: ...
api: GET /api/channels/status
baseUrl: http://localhost:8000
layout: '@/layouts/APIEndpointLayout.astro'
layout: "@/layouts/APIEndpointLayout.astro"
auth: bearer
---
## Overview
...
## Response
<ResponseField name="..." type="...">...</ResponseField>
<ResponseField name="..." type="...">
...
</ResponseField>
<RequestExample>
<Tabs items={["cURL", "JavaScript", "Python"]}>
<Tab title="cURL">...</Tab>
...
</Tabs>
<Tabs items={["cURL", "JavaScript", "Python"]}>
<Tab title="cURL">...</Tab>
...
</Tabs>
</RequestExample>
<ResponseExample>
<Tabs items={["200"]}>
<Tab title="200">...</Tab>
</Tabs>
<Tabs items={["200"]}>
<Tab title="200">...</Tab>
</Tabs>
</ResponseExample>
```
## Key Conventions
- **Package name duality**: Internal Python package is `pocketclaw`, public-facing name in docs is `PocketPaw`. Import paths use `pocketclaw` (e.g., `from pocketclaw.tools.registry import ...`).
- **Package name duality**: Internal Python package is `pocketpaw`, public-facing name in docs is `PocketPaw`. Import paths use `pocketpaw` (e.g., `from pocketpaw.tools.registry import ...`).
- **Channel count**: "9+" channels (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Teams, Google Chat, Web Dashboard).
- **Tool count**: "50+" built-in tools across search, media, integrations, sessions, desktop, and coding categories.
- **Backend count**: 3 backends (Claude Agent SDK, PocketPaw Native, Open Interpreter).

View File

@@ -3,7 +3,14 @@ title: Cron Scheduler
description: "PocketPaw's built-in cron scheduler handles recurring tasks and one-time reminders with APScheduler. Tasks persist across restarts, support natural language time parsing, and auto-reschedule after execution."
section: Advanced
ogType: article
keywords: ["cron scheduler", "recurring tasks", "reminders", "apscheduler", "task scheduling"]
keywords:
[
"cron scheduler",
"recurring tasks",
"reminders",
"apscheduler",
"task scheduling",
]
tags: ["advanced", "automation"]
---
@@ -43,7 +50,7 @@ The scheduler provides two main functions:
### add_recurring
```python
from pocketclaw.scheduler import scheduler
from pocketpaw.scheduler import scheduler
scheduler.add_recurring(
task_id="weekly-summary",
@@ -67,9 +74,9 @@ Recurring tasks are persisted to the config and re-loaded on startup. One-time r
Standard cron format: `minute hour day_of_month month day_of_week`
| Expression | Description |
|------------|-------------|
| `0 9 * * 1` | Every Monday at 9:00 AM |
| `*/30 * * * *` | Every 30 minutes |
| `0 8,17 * * 1-5` | 8 AM and 5 PM on weekdays |
| `0 0 1 * *` | First day of every month at midnight |
| Expression | Description |
| ---------------- | ------------------------------------ |
| `0 9 * * 1` | Every Monday at 9:00 AM |
| `*/30 * * * *` | Every 30 minutes |
| `0 8,17 * * 1-5` | 8 AM and 5 PM on weekdays |
| `0 0 1 * *` | First day of every month at midnight |

View File

@@ -3,7 +3,14 @@ title: Message Bus
description: "The message bus is PocketPaw's communication backbone: async pub/sub for InboundMessage, OutboundMessage, and SystemEvent types flowing between channels and the agent loop."
section: Core Concepts
ogType: article
keywords: ["pub/sub", "async messaging", "event types", "inbound message", "outbound message"]
keywords:
[
"pub/sub",
"async messaging",
"event types",
"inbound message",
"outbound message",
]
tags: ["architecture", "messaging"]
---
@@ -16,8 +23,8 @@ The message bus is the backbone of PocketPaw's architecture. All communication b
The message bus implements a simple **publish/subscribe** pattern. Publishers emit events, and subscribers receive them asynchronously. This decouples components so they don't need to know about each other.
```python
from pocketclaw.bus.message_bus import MessageBus
from pocketclaw.bus.events import InboundMessage, OutboundMessage
from pocketpaw.bus.message_bus import MessageBus
from pocketpaw.bus.events import InboundMessage, OutboundMessage
bus = MessageBus()
@@ -39,35 +46,35 @@ await bus.publish(InboundMessage(
Represents user input from any channel:
| Field | Type | Description |
|-------|------|-------------|
| `content` | `str` | The message text |
| `channel` | `str` | Source channel (telegram, discord, slack, etc.) |
| `session_id` | `str` | Unique session identifier |
| `metadata` | `dict` | Channel-specific metadata (chat_id, thread_ts, etc.) |
| Field | Type | Description |
| ------------ | ------ | ---------------------------------------------------- |
| `content` | `str` | The message text |
| `channel` | `str` | Source channel (telegram, discord, slack, etc.) |
| `session_id` | `str` | Unique session identifier |
| `metadata` | `dict` | Channel-specific metadata (chat_id, thread_ts, etc.) |
### OutboundMessage
Agent responses sent back to channels:
| Field | Type | Description |
|-------|------|-------------|
| `content` | `str` | Response text |
| `channel` | `str` | Target channel |
| `session_id` | `str` | Session identifier |
| Field | Type | Description |
| ----------------- | ------ | --------------------------------- |
| `content` | `str` | Response text |
| `channel` | `str` | Target channel |
| `session_id` | `str` | Session identifier |
| `is_stream_chunk` | `bool` | Whether this is a streaming chunk |
| `is_stream_end` | `bool` | Whether this is the final chunk |
| `metadata` | `dict` | Channel-specific metadata |
| `is_stream_end` | `bool` | Whether this is the final chunk |
| `metadata` | `dict` | Channel-specific metadata |
### SystemEvent
Internal events consumed by the web dashboard:
| Field | Type | Description |
|-------|------|-------------|
| `event_type` | `str` | Event type (tool_start, tool_result, thinking, error, inbox_update) |
| `data` | `dict` | Event-specific data |
| `session_id` | `str` | Session identifier |
| Field | Type | Description |
| ------------ | ------ | ------------------------------------------------------------------- |
| `event_type` | `str` | Event type (tool_start, tool_result, thinking, error, inbox_update) |
| `data` | `dict` | Event-specific data |
| `session_id` | `str` | Session identifier |
## Streaming Protocol

View File

@@ -3,7 +3,14 @@ title: Tool System
description: "PocketPaw's extensible tool system uses ToolProtocol interfaces with dual-schema export (Anthropic and OpenAI), a policy engine with deny-wins precedence, and 50+ built-in tools."
section: Core Concepts
ogType: article
keywords: ["tool protocol", "tool registry", "policy engine", "anthropic schema", "openai schema"]
keywords:
[
"tool protocol",
"tool registry",
"policy engine",
"anthropic schema",
"openai schema",
]
tags: ["tools", "architecture"]
---
@@ -11,7 +18,10 @@ tags: ["tools", "architecture"]
PocketPaw's tool system provides 30+ built-in tools and supports custom tool creation. Tools are governed by a policy system that controls which tools are available.
<img src="/Tool-system-architecture-and-policy-system.webp" alt="Tool system architecture: 21 built-in tools across eight categories, ToolProtocol interface with dual-schema export, and three-tier policy engine (profiles, groups, allow/deny lists) with strict deny-wins precedence." />
<img
src="/Tool-system-architecture-and-policy-system.webp"
alt="Tool system architecture: 21 built-in tools across eight categories, ToolProtocol interface with dual-schema export, and three-tier policy engine (profiles, groups, allow/deny lists) with strict deny-wins precedence."
/>
## Tool Protocol
@@ -38,7 +48,7 @@ The `ToolDefinition` provides schema export for both Anthropic and OpenAI format
The `ToolRegistry` is the central registry for all tools:
```python
from pocketclaw.tools.registry import ToolRegistry
from pocketpaw.tools.registry import ToolRegistry
registry = ToolRegistry()
registry.register(MyCustomTool())
@@ -57,60 +67,67 @@ The registry automatically filters tools based on the active [tool policy](/tool
PocketPaw ships with these built-in tools:
### Core Tools (Claude Agent SDK)
- **Bash** — Execute shell commands
- **Read** — Read files from the filesystem
- **Write** — Write files to the filesystem
- **Edit** — Edit existing files
### Search & Research
- **web_search** — Search the web via Tavily or Brave
- **research** — Multi-step web research chains
### Media
- **image_gen** — Generate images with Google Gemini
- **voice** — Text-to-speech (OpenAI, ElevenLabs)
- **stt** — Speech-to-text (OpenAI Whisper)
- **ocr** — Extract text from images (GPT-4o Vision + pytesseract)
### Productivity
- **gmail** — Search, read, and send emails
- **calendar** — List, create, and search calendar events
- **gdrive** — List, download, upload, and share files
- **gdocs** — Read, create, and search documents
### Entertainment
- **spotify** — Search, playback, playlists, now playing
- **reddit** — Search, read threads, trending content
### Agent Tools
- **delegate** — Spawn sub-agents for parallel work
- **skill_gen** — Generate custom skill definitions
### Browser
- **browser** — Playwright-based web automation using accessibility tree snapshots
## Tool Groups
Tools are organized into groups for policy management:
| Group | Tools |
|-------|-------|
| `group:filesystem` | read_file, write_file, list_dir |
| `group:shell` | shell |
| `group:memory` | save_memory, recall_memory |
| `group:search` | web_search |
| `group:media` | image_gen, voice, stt, ocr |
| `group:gmail` | gmail_search, gmail_read, gmail_send |
| `group:calendar` | calendar_list, calendar_create, calendar_search |
| `group:drive` | gdrive_list, gdrive_download, gdrive_upload, gdrive_share |
| `group:docs` | gdocs_read, gdocs_create, gdocs_search |
| `group:spotify` | spotify_search, spotify_now_playing, spotify_playback, spotify_playlist |
| `group:reddit` | reddit_search, reddit_read, reddit_trending |
| `group:voice` | voice, stt |
| `group:research` | research |
| `group:delegation` | delegate |
| `group:skills` | skill_gen |
| `group:mcp` | All MCP server tools |
| Group | Tools |
| ------------------ | ----------------------------------------------------------------------- |
| `group:filesystem` | read_file, write_file, list_dir |
| `group:shell` | shell |
| `group:memory` | save_memory, recall_memory |
| `group:search` | web_search |
| `group:media` | image_gen, voice, stt, ocr |
| `group:gmail` | gmail_search, gmail_read, gmail_send |
| `group:calendar` | calendar_list, calendar_create, calendar_search |
| `group:drive` | gdrive_list, gdrive_download, gdrive_upload, gdrive_share |
| `group:docs` | gdocs_read, gdocs_create, gdocs_search |
| `group:spotify` | spotify_search, spotify_now_playing, spotify_playback, spotify_playlist |
| `group:reddit` | reddit_search, reddit_read, reddit_trending |
| `group:voice` | voice, stt |
| `group:research` | research |
| `group:delegation` | delegate |
| `group:skills` | skill_gen |
| `group:mcp` | All MCP server tools |
## Schema Export

View File

@@ -1,6 +1,6 @@
---
title: Project Structure
description: "Explore the PocketPaw codebase layout — src/pocketclaw package structure, bus adapters, agent backends, tool registry, memory stores, and frontend dashboard files."
description: "Explore the PocketPaw codebase layout — src/pocketpaw package structure, bus adapters, agent backends, tool registry, memory stores, and frontend dashboard files."
section: Getting Started
ogType: article
keywords: ["codebase", "directory layout", "source code", "package structure"]
@@ -15,7 +15,7 @@ PocketPaw's codebase follows a modular architecture. Here's how it's organized.
```text
pocketpaw/
├── src/pocketclaw/ # Main Python package
├── src/pocketpaw/ # Main Python package
│ ├── __main__.py # CLI entry point
│ ├── config.py # Pydantic Settings configuration
│ ├── scheduler.py # Cron/recurring task scheduler

View File

@@ -3,7 +3,8 @@ title: Custom Tools
description: "Build custom tools for PocketPaw using the ToolProtocol interface. Define tool schemas compatible with both Anthropic and OpenAI APIs, register them in the ToolRegistry, and use them immediately."
section: Tools
ogType: article
keywords: ["custom tools", "tool protocol", "tool development", "plugin", "extensible"]
keywords:
["custom tools", "tool protocol", "tool development", "plugin", "extensible"]
tags: ["tools", "development"]
---
@@ -14,7 +15,7 @@ PocketPaw's tool system is extensible. You can create custom tools by implementi
## ToolProtocol Interface
```python
from pocketclaw.tools.registry import ToolProtocol, ToolDefinition
from pocketpaw.tools.registry import ToolProtocol, ToolDefinition
class MyCustomTool:
@property
@@ -53,7 +54,7 @@ class MyCustomTool:
Register your tool with the `ToolRegistry`:
```python
from pocketclaw.tools.registry import ToolRegistry
from pocketpaw.tools.registry import ToolRegistry
registry = ToolRegistry()
registry.register(MyCustomTool())

View File

@@ -1122,7 +1122,7 @@ class PocketPawInstaller:
except FileNotFoundError:
# Might not be on PATH yet, try python -m
try:
os.execvp(sys.executable, [sys.executable, "-m", "pocketclaw"])
os.execvp(sys.executable, [sys.executable, "-m", "pocketpaw"])
except Exception as exc:
print(f" Could not launch: {exc}")
print(" Try running 'pocketpaw' manually.\n")

View File

@@ -26,16 +26,16 @@ A lightweight desktop app (~15MB) that lets non-developers run PocketPaw with ze
## Modules
| File | Purpose |
|------|---------|
| `__main__.py` | Entry point — orchestrates bootstrap, server, tray |
| `bootstrap.py` | Python detection, venv creation, pip install |
| `server.py` | Server process lifecycle, PID management, health checks |
| `tray.py` | System tray icon with dynamic menu (via pystray) |
| `splash.py` | First-run tkinter progress window |
| `updater.py` | PyPI version check and one-click upgrade |
| `build/build.py` | PyInstaller build script |
| `build/launcher.spec` | PyInstaller spec file (folder mode, platform-specific) |
| File | Purpose |
| --------------------- | ------------------------------------------------------- |
| `__main__.py` | Entry point — orchestrates bootstrap, server, tray |
| `bootstrap.py` | Python detection, venv creation, pip install |
| `server.py` | Server process lifecycle, PID management, health checks |
| `tray.py` | System tray icon with dynamic menu (via pystray) |
| `splash.py` | First-run tkinter progress window |
| `updater.py` | PyPI version check and one-click upgrade |
| `build/build.py` | PyInstaller build script |
| `build/launcher.spec` | PyInstaller spec file (folder mode, platform-specific) |
## Running from source
@@ -54,16 +54,16 @@ PYTHONPATH=. python -m installer.launcher --no-browser --no-tray --port 9999
### CLI Options
| Flag | Description |
|------|-------------|
| `--no-browser` | Don't auto-open the browser |
| `--no-tray` | Run headless (no system tray icon, Ctrl+C to stop) |
| `--port PORT` | Override the dashboard port (default: 8888) |
| Flag | Description |
| --------------- | ---------------------------------------------------------------------------- |
| `--no-browser` | Don't auto-open the browser |
| `--no-tray` | Run headless (no system tray icon, Ctrl+C to stop) |
| `--port PORT` | Override the dashboard port (default: 8888) |
| `--extras LIST` | Comma-separated pip extras, e.g. `telegram,discord` (default: `recommended`) |
| `--reset` | Delete the venv and reinstall from scratch |
| `--dev` | Install from the `dev` branch instead of PyPI (shortcut for `--branch dev`) |
| `--branch NAME` | Install from a specific git branch (e.g. `--branch feat/new-thing`) |
| `--local PATH` | Install from a local directory in editable mode |
| `--reset` | Delete the venv and reinstall from scratch |
| `--dev` | Install from the `dev` branch instead of PyPI (shortcut for `--branch dev`) |
| `--branch NAME` | Install from a specific git branch (e.g. `--branch feat/new-thing`) |
| `--local PATH` | Install from a local directory in editable mode |
## Dev / Branch Testing
@@ -143,6 +143,7 @@ hdiutil create -volname PocketPaw \
Option A: Use [Inno Setup](https://jrsoftware.org/isinfo.php) (free) — point it at `dist\launcher\PocketPaw\`.
Option B: Zip the folder for a portable build:
```powershell
Compress-Archive -Path dist\launcher\PocketPaw -DestinationPath dist\launcher\PocketPaw-portable.zip
```
@@ -150,11 +151,13 @@ Compress-Archive -Path dist\launcher\PocketPaw -DestinationPath dist\launcher\Po
### Automated builds (CI)
The GitHub Actions workflow `.github/workflows/build-launcher.yml` builds for:
- **macOS (Apple Silicon)** — `.dmg`
- **macOS (Intel)** — `.dmg`
- **Windows** — `.exe` installer (via Inno Setup)
Triggered on:
- **Release published** — artifacts are attached to the GitHub release
- **Manual dispatch** — artifacts are uploaded as workflow artifacts
@@ -169,7 +172,7 @@ Triggered on:
### Server management (`server.py`)
- Starts PocketPaw as a subprocess: `{venv}/bin/python -m pocketclaw --port {port}`
- Starts PocketPaw as a subprocess: `{venv}/bin/python -m pocketpaw --port {port}`
- Writes PID to `~/.pocketpaw/launcher.pid`
- Health check via HTTP GET to `http://127.0.0.1:{port}/`
- Graceful shutdown: SIGTERM → wait → SIGKILL
@@ -189,14 +192,14 @@ Triggered on:
## File locations
| Path | Purpose |
|------|---------|
| `~/.pocketpaw/venv/` | Virtual environment with pocketpaw installed |
| `~/.pocketpaw/config.json` | PocketPaw configuration |
| `~/.pocketpaw/launcher.pid` | Server process PID |
| `~/.pocketpaw/logs/launcher.log` | Launcher log file |
| `~/.pocketpaw/.dev-mode` | Dev mode marker (present when `--dev`/`--branch`/`--local` was used) |
| `~/.pocketpaw/python/` | Embedded Python (Windows only) |
| Path | Purpose |
| -------------------------------- | -------------------------------------------------------------------- |
| `~/.pocketpaw/venv/` | Virtual environment with pocketpaw installed |
| `~/.pocketpaw/config.json` | PocketPaw configuration |
| `~/.pocketpaw/launcher.pid` | Server process PID |
| `~/.pocketpaw/logs/launcher.log` | Launcher log file |
| `~/.pocketpaw/.dev-mode` | Dev mode marker (present when `--dev`/`--branch`/`--local` was used) |
| `~/.pocketpaw/python/` | Embedded Python (Windows only) |
## Tests

View File

@@ -313,7 +313,7 @@ cat ~/.pocketpaw/.dev-mode
# Should show: branch=dev
# Verify pocketpaw is installed
~/.pocketpaw/venv/bin/python -c "import pocketclaw; print(pocketclaw.__version__)"
~/.pocketpaw/venv/bin/python -c "import pocketpaw; print(pocketpaw.__version__)"
```
### Windows (PowerShell)
@@ -329,7 +329,7 @@ Get-Content $env:USERPROFILE\.pocketpaw\.dev-mode
# Should show: branch=dev
# Verify pocketpaw is installed
& "$env:USERPROFILE\.pocketpaw\venv\Scripts\python.exe" -c "import pocketclaw; print(pocketclaw.__version__)"
& "$env:USERPROFILE\.pocketpaw\venv\Scripts\python.exe" -c "import pocketpaw; print(pocketpaw.__version__)"
```
### Test local editable install
@@ -355,6 +355,7 @@ ls ~/.pocketpaw/.dev-mode 2>&1
```
**What to verify:**
- [ ] `--dev` installs from git, not PyPI
- [ ] `~/.pocketpaw/.dev-mode` marker file is created
- [ ] `--reset` without `--dev` removes the marker and installs from PyPI
@@ -437,28 +438,28 @@ gh workflow run "Build Desktop Launcher" -f version=test-0.0.1
## Quick Checklist
| # | Test | macOS | Windows | Linux |
|---|------|:-----:|:-------:|:-----:|
| 1 | `make_icons.py` produces `.ico` + `.icns` | [ ] | [ ] | [ ] |
| 2 | `build.py --version X` succeeds | [ ] | [ ] | N/A |
| 3 | DMG mounts, shows app + Applications alias | [ ] | N/A | N/A |
| 4 | Installer wizard works, shortcuts created | N/A | [ ] | N/A |
| 5 | Launcher starts from source (`--no-browser`) | [ ] | [ ] | [ ] |
| 6 | Tray menu shows all items with version | [ ] | [ ] | [ ] |
| 7 | Tooltip updates dynamically | [ ] | [ ] | [ ] |
| 8 | `--autostart` creates platform entry | [ ] | [ ] | [ ] |
| 9 | `--no-autostart` removes it | [ ] | [ ] | [ ] |
| 10 | Tray "Start on Login" toggles correctly | [ ] | [ ] | [ ] |
| 11 | `--uninstall` interactive mode works | [ ] | [ ] | [ ] |
| 12 | Tray "Uninstall..." removes safe components | [ ] | [ ] | [ ] |
| 13 | "View Logs..." opens log file | [ ] | [ ] | [ ] |
| 14 | `--dev` installs from git branch | [ ] | [ ] | [ ] |
| 15 | `--branch X` installs from custom branch | [ ] | [ ] | [ ] |
| 16 | `--local /path` does editable install | [ ] | [ ] | [ ] |
| 17 | `--reset` without `--dev` switches back to PyPI | [ ] | [ ] | [ ] |
| 18 | Dev mode updater re-pulls from branch | [ ] | [ ] | [ ] |
| 19 | `install.ps1` bootstraps correctly | N/A | [ ] | N/A |
| 20 | `install.sh` redirects Windows users to PS1 | N/A | [ ] | N/A |
| 21 | CI workflow produces all 3 artifacts | [ ] | [ ] | N/A |
| 22 | Checksums are generated alongside artifacts | [ ] | [ ] | N/A |
| 23 | Automated tests pass (62 tests) | [ ] | [ ] | [ ] |
| # | Test | macOS | Windows | Linux |
| --- | ----------------------------------------------- | :---: | :-----: | :---: |
| 1 | `make_icons.py` produces `.ico` + `.icns` | [ ] | [ ] | [ ] |
| 2 | `build.py --version X` succeeds | [ ] | [ ] | N/A |
| 3 | DMG mounts, shows app + Applications alias | [ ] | N/A | N/A |
| 4 | Installer wizard works, shortcuts created | N/A | [ ] | N/A |
| 5 | Launcher starts from source (`--no-browser`) | [ ] | [ ] | [ ] |
| 6 | Tray menu shows all items with version | [ ] | [ ] | [ ] |
| 7 | Tooltip updates dynamically | [ ] | [ ] | [ ] |
| 8 | `--autostart` creates platform entry | [ ] | [ ] | [ ] |
| 9 | `--no-autostart` removes it | [ ] | [ ] | [ ] |
| 10 | Tray "Start on Login" toggles correctly | [ ] | [ ] | [ ] |
| 11 | `--uninstall` interactive mode works | [ ] | [ ] | [ ] |
| 12 | Tray "Uninstall..." removes safe components | [ ] | [ ] | [ ] |
| 13 | "View Logs..." opens log file | [ ] | [ ] | [ ] |
| 14 | `--dev` installs from git branch | [ ] | [ ] | [ ] |
| 15 | `--branch X` installs from custom branch | [ ] | [ ] | [ ] |
| 16 | `--local /path` does editable install | [ ] | [ ] | [ ] |
| 17 | `--reset` without `--dev` switches back to PyPI | [ ] | [ ] | [ ] |
| 18 | Dev mode updater re-pulls from branch | [ ] | [ ] | [ ] |
| 19 | `install.ps1` bootstraps correctly | N/A | [ ] | N/A |
| 20 | `install.sh` redirects Windows users to PS1 | N/A | [ ] | N/A |
| 21 | CI workflow produces all 3 artifacts | [ ] | [ ] | N/A |
| 22 | Checksums are generated alongside artifacts | [ ] | [ ] | N/A |
| 23 | Automated tests pass (62 tests) | [ ] | [ ] | [ ] |

View File

@@ -63,7 +63,7 @@ class ServerManager:
logger.info("Default port busy, using port %d", self.port)
self.on_status(f"Starting PocketPaw on port {self.port}...")
logger.info("Starting server: %s -m pocketclaw --port %d", python, self.port)
logger.info("Starting server: %s -m pocketpaw --port %d", python, self.port)
try:
# Start the server process — redirect output to a log file
@@ -72,7 +72,7 @@ class ServerManager:
self._log_fh = open(log_file, "a", encoding="utf-8") # noqa: SIM115
env = self._build_env()
self._process = subprocess.Popen(
[str(python), "-m", "pocketclaw", "--port", str(self.port)],
[str(python), "-m", "pocketpaw", "--port", str(self.port)],
env=env,
stdout=self._log_fh,
stderr=self._log_fh,
@@ -84,7 +84,7 @@ class ServerManager:
PID_FILE.write_text(str(self._process.pid))
# Wait for the server to become healthy
# pocketclaw needs ~25s for startup + internal setup
# pocketpaw needs ~25s for startup + internal setup
if self._wait_for_healthy(timeout=60):
self.on_status(f"PocketPaw running on port {self.port}")
return True
@@ -180,17 +180,17 @@ class ServerManager:
else:
venv_bin = str(VENV_DIR / "bin")
# Also add the uv directory so pocketclaw's auto_install can find uv
# Also add the uv directory so pocketpaw's auto_install can find uv
uv_dir = str(POCKETPAW_HOME / "uv")
env["PATH"] = venv_bin + os.pathsep + uv_dir + os.pathsep + env.get("PATH", "")
env["VIRTUAL_ENV"] = str(VENV_DIR)
# Force UTF-8 so emoji/unicode in pocketclaw output doesn't crash
# Force UTF-8 so emoji/unicode in pocketpaw output doesn't crash
# on Windows (default cp1252 can't encode them)
env["PYTHONIOENCODING"] = "utf-8"
env["PYTHONUTF8"] = "1"
# Set UV_OVERRIDE so any uv invocation (including pocketclaw's
# Set UV_OVERRIDE so any uv invocation (including pocketpaw's
# internal auto_install) uses our tiktoken override
overrides_file = POCKETPAW_HOME / "uv-overrides.txt"
if not overrides_file.exists():

View File

@@ -153,7 +153,7 @@ dev = [
]
[project.scripts]
pocketpaw = "pocketclaw.__main__:main"
pocketpaw = "pocketpaw.__main__:main"
[project.urls]
Homepage = "https://github.com/pocketpaw/pocketpaw"
@@ -167,7 +167,7 @@ requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/pocketclaw"]
packages = ["src/pocketpaw"]
[tool.ruff]
line-length = 100

View File

@@ -1,13 +0,0 @@
# Bootstrap package.
# Created: 2026-02-02
from pocketclaw.bootstrap.protocol import BootstrapProviderProtocol, BootstrapContext
from pocketclaw.bootstrap.default_provider import DefaultBootstrapProvider
from pocketclaw.bootstrap.context_builder import AgentContextBuilder
__all__ = [
"BootstrapProviderProtocol",
"BootstrapContext",
"DefaultBootstrapProvider",
"AgentContextBuilder",
]

View File

@@ -1,6 +0,0 @@
"""LLM package for PocketPaw."""
from pocketclaw.llm.client import LLMClient, resolve_llm_client
from pocketclaw.llm.router import LLMRouter
__all__ = ["LLMClient", "LLMRouter", "resolve_llm_client"]

View File

@@ -1,11 +0,0 @@
from pocketclaw.security.audit import AuditLogger, AuditEvent, AuditSeverity, get_audit_logger
from pocketclaw.security.guardian import GuardianAgent, get_guardian
__all__ = [
"AuditLogger",
"AuditEvent",
"AuditSeverity",
"get_audit_logger",
"GuardianAgent",
"get_guardian",
]

View File

@@ -1,15 +0,0 @@
# Tools package.
from pocketclaw.tools.policy import TOOL_GROUPS, TOOL_PROFILES, ToolPolicy
from pocketclaw.tools.protocol import BaseTool, ToolDefinition, ToolProtocol
from pocketclaw.tools.registry import ToolRegistry
__all__ = [
"ToolProtocol",
"BaseTool",
"ToolDefinition",
"ToolRegistry",
"ToolPolicy",
"TOOL_GROUPS",
"TOOL_PROFILES",
]

View File

@@ -18,8 +18,8 @@ import sys
import webbrowser
from importlib.metadata import version as get_version
from pocketclaw.config import Settings, get_settings
from pocketclaw.logging_setup import setup_logging
from pocketpaw.config import Settings, get_settings
from pocketpaw.logging_setup import setup_logging
# Setup beautiful logging with Rich
setup_logging(level="INFO")
@@ -28,8 +28,8 @@ logger = logging.getLogger(__name__)
async def run_telegram_mode(settings: Settings) -> None:
"""Run in Telegram bot mode."""
from pocketclaw.bot_gateway import run_bot
from pocketclaw.web_server import find_available_port, run_pairing_server
from pocketpaw.bot_gateway import run_bot
from pocketpaw.web_server import find_available_port, run_pairing_server
# Check if we need to run pairing flow
if not settings.telegram_bot_token or not settings.allowed_user_id:
@@ -68,8 +68,8 @@ async def run_telegram_mode(settings: Settings) -> None:
async def run_multi_channel_mode(settings: Settings, args: argparse.Namespace) -> None:
"""Run one or more channel adapters sharing a single bus and AgentLoop."""
from pocketclaw.agents.loop import AgentLoop
from pocketclaw.bus import get_message_bus
from pocketpaw.agents.loop import AgentLoop
from pocketpaw.bus import get_message_bus
bus = get_message_bus()
adapters = []
@@ -78,7 +78,7 @@ async def run_multi_channel_mode(settings: Settings, args: argparse.Namespace) -
if not settings.discord_bot_token:
logger.error("Discord bot token not configured. Set POCKETPAW_DISCORD_BOT_TOKEN.")
else:
from pocketclaw.bus.adapters.discord_adapter import DiscordAdapter
from pocketpaw.bus.adapters.discord_adapter import DiscordAdapter
adapters.append(
DiscordAdapter(
@@ -95,7 +95,7 @@ async def run_multi_channel_mode(settings: Settings, args: argparse.Namespace) -
"and POCKETPAW_SLACK_APP_TOKEN."
)
else:
from pocketclaw.bus.adapters.slack_adapter import SlackAdapter
from pocketpaw.bus.adapters.slack_adapter import SlackAdapter
adapters.append(
SlackAdapter(
@@ -112,7 +112,7 @@ async def run_multi_channel_mode(settings: Settings, args: argparse.Namespace) -
"and POCKETPAW_WHATSAPP_PHONE_NUMBER_ID."
)
else:
from pocketclaw.bus.adapters.whatsapp_adapter import WhatsAppAdapter
from pocketpaw.bus.adapters.whatsapp_adapter import WhatsAppAdapter
adapters.append(
WhatsAppAdapter(
@@ -127,7 +127,7 @@ async def run_multi_channel_mode(settings: Settings, args: argparse.Namespace) -
if not settings.signal_phone_number:
logger.error("Signal not configured. Set POCKETPAW_SIGNAL_PHONE_NUMBER.")
else:
from pocketclaw.bus.adapters.signal_adapter import SignalAdapter
from pocketpaw.bus.adapters.signal_adapter import SignalAdapter
adapters.append(
SignalAdapter(
@@ -144,7 +144,7 @@ async def run_multi_channel_mode(settings: Settings, args: argparse.Namespace) -
"and POCKETPAW_MATRIX_USER_ID."
)
else:
from pocketclaw.bus.adapters.matrix_adapter import MatrixAdapter
from pocketpaw.bus.adapters.matrix_adapter import MatrixAdapter
adapters.append(
MatrixAdapter(
@@ -164,7 +164,7 @@ async def run_multi_channel_mode(settings: Settings, args: argparse.Namespace) -
"and POCKETPAW_TEAMS_APP_PASSWORD."
)
else:
from pocketclaw.bus.adapters.teams_adapter import TeamsAdapter
from pocketpaw.bus.adapters.teams_adapter import TeamsAdapter
adapters.append(
TeamsAdapter(
@@ -179,7 +179,7 @@ async def run_multi_channel_mode(settings: Settings, args: argparse.Namespace) -
if not settings.gchat_service_account_key:
logger.error("Google Chat not configured. Set POCKETPAW_GCHAT_SERVICE_ACCOUNT_KEY.")
else:
from pocketclaw.bus.adapters.gchat_adapter import GoogleChatAdapter
from pocketpaw.bus.adapters.gchat_adapter import GoogleChatAdapter
adapters.append(
GoogleChatAdapter(
@@ -208,8 +208,8 @@ async def run_multi_channel_mode(settings: Settings, args: argparse.Namespace) -
if args.whatsapp:
import uvicorn
import pocketclaw.whatsapp_gateway as wa_gw
from pocketclaw.whatsapp_gateway import create_whatsapp_app
import pocketpaw.whatsapp_gateway as wa_gw
from pocketpaw.whatsapp_gateway import create_whatsapp_app
# Point the gateway module at our adapter
for a in adapters:
@@ -245,7 +245,7 @@ def _is_headless() -> bool:
def run_dashboard_mode(settings: Settings, host: str, port: int) -> None:
"""Run in web dashboard mode."""
from pocketclaw.dashboard import run_dashboard
from pocketpaw.dashboard import run_dashboard
run_dashboard(host=host, port=port, open_browser=not _is_headless())
@@ -258,7 +258,7 @@ async def check_ollama(settings: Settings) -> int:
import httpx
from rich.console import Console
from pocketclaw.llm.client import resolve_llm_client
from pocketpaw.llm.client import resolve_llm_client
console = Console()
llm = resolve_llm_client(settings, force_provider="ollama")
@@ -489,7 +489,7 @@ Examples:
exit_code = asyncio.run(check_ollama(settings))
raise SystemExit(exit_code)
elif args.security_audit:
from pocketclaw.security.audit_cli import run_security_audit
from pocketpaw.security.audit_cli import run_security_audit
exit_code = asyncio.run(run_security_audit(fix=args.fix))
raise SystemExit(exit_code)
@@ -504,7 +504,7 @@ Examples:
logger.info("👋 PocketPaw stopped.")
finally:
# Coordinated singleton shutdown
from pocketclaw.lifecycle import shutdown_all
from pocketpaw.lifecycle import shutdown_all
try:
asyncio.run(shutdown_all())

View File

@@ -1,5 +1,5 @@
"""Agents package for PocketPaw."""
from pocketclaw.agents.router import AgentRouter
from pocketpaw.agents.router import AgentRouter
__all__ = ["AgentRouter"]

View File

@@ -4,8 +4,8 @@ import asyncio
import logging
from typing import AsyncIterator, Optional
from pocketclaw.config import Settings
from pocketclaw.tools.screenshot import take_screenshot
from pocketpaw.config import Settings
from pocketpaw.tools.screenshot import take_screenshot
logger = logging.getLogger(__name__)
@@ -21,7 +21,7 @@ class ClaudeCodeAgent:
def _initialize(self) -> None:
"""Initialize the Anthropic client."""
from pocketclaw.llm.client import resolve_llm_client
from pocketpaw.llm.client import resolve_llm_client
try:
llm = resolve_llm_client(self.settings, force_provider="anthropic")

View File

@@ -25,9 +25,9 @@ from collections.abc import AsyncIterator
from pathlib import Path
from typing import Any
from pocketclaw.agents.protocol import AgentEvent, ExecutorProtocol
from pocketclaw.config import Settings
from pocketclaw.tools.policy import ToolPolicy
from pocketpaw.agents.protocol import AgentEvent, ExecutorProtocol
from pocketpaw.config import Settings
from pocketpaw.tools.policy import ToolPolicy
logger = logging.getLogger(__name__)
@@ -69,7 +69,7 @@ _TOOL_INSTRUCTIONS = """
You have extra tools installed. Call them with:
```bash
python -m pocketclaw.tools.cli <tool_name> '<json_args>'
python -m pocketpaw.tools.cli <tool_name> '<json_args>'
```
### Memory
@@ -155,7 +155,7 @@ directly — never use a tool to look up what you already know.
## Guidelines
1. **Be AGENTIC** execute tasks using tools, don't just describe how.
2. **Use PocketPaw tools** always prefer `python -m pocketclaw.tools.cli` over platform-specific commands (AppleScript, PowerShell, etc.). These tools work on all operating systems.
2. **Use PocketPaw tools** always prefer `python -m pocketpaw.tools.cli` over platform-specific commands (AppleScript, PowerShell, etc.). These tools work on all operating systems.
3. **Be concise** give clear, helpful responses.
4. **Be safe** don't run destructive commands. Ask for confirmation if unsure.
5. If Gmail/Calendar/Drive/Docs returns "not authenticated", tell the user to visit:
@@ -429,7 +429,7 @@ class ClaudeAgentSDK:
McpHttpServerConfig).
"""
try:
from pocketclaw.mcp.config import load_mcp_config
from pocketpaw.mcp.config import load_mcp_config
except ImportError:
return {}
@@ -570,7 +570,7 @@ class ClaudeAgentSDK:
}
# Configure LLM provider for the Claude CLI subprocess.
from pocketclaw.llm.client import resolve_llm_client
from pocketpaw.llm.client import resolve_llm_client
llm = resolve_llm_client(self.settings)
sdk_env = llm.to_sdk_env()
@@ -603,7 +603,7 @@ class ClaudeAgentSDK:
# Smart model routing (opt-in, skip for Ollama — model already set)
if self.settings.smart_routing_enabled and not llm.is_ollama:
from pocketclaw.agents.model_router import ModelRouter
from pocketpaw.agents.model_router import ModelRouter
model_router = ModelRouter(self.settings)
selection = model_router.classify(message)

View File

@@ -12,7 +12,7 @@ import asyncio
import logging
from pathlib import Path
from pocketclaw.config import Settings
from pocketpaw.config import Settings
logger = logging.getLogger(__name__)
@@ -38,7 +38,7 @@ class OpenInterpreterExecutor:
try:
from interpreter import interpreter
from pocketclaw.llm.client import resolve_llm_client
from pocketpaw.llm.client import resolve_llm_client
# Configure for execution mode (minimal LLM usage)
interpreter.auto_run = True

View File

@@ -17,14 +17,14 @@ It replaces the old highly-coupled bot loops.
import asyncio
import logging
from pocketclaw.agents.router import AgentRouter
from pocketclaw.bootstrap import AgentContextBuilder
from pocketclaw.bus import InboundMessage, OutboundMessage, SystemEvent, get_message_bus
from pocketclaw.bus.commands import get_command_handler
from pocketclaw.bus.events import Channel
from pocketclaw.config import Settings, get_settings
from pocketclaw.memory import get_memory_manager
from pocketclaw.security.injection_scanner import ThreatLevel, get_injection_scanner
from pocketpaw.agents.router import AgentRouter
from pocketpaw.bootstrap import AgentContextBuilder
from pocketpaw.bus import InboundMessage, OutboundMessage, SystemEvent, get_message_bus
from pocketpaw.bus.commands import get_command_handler
from pocketpaw.bus.events import Channel
from pocketpaw.config import Settings, get_settings
from pocketpaw.memory import get_memory_manager
from pocketpaw.security.injection_scanner import ThreatLevel, get_injection_scanner
logger = logging.getLogger(__name__)

View File

@@ -9,7 +9,7 @@ import re
from dataclasses import dataclass
from enum import Enum
from pocketclaw.config import Settings
from pocketpaw.config import Settings
logger = logging.getLogger(__name__)

View File

@@ -10,7 +10,7 @@ import asyncio
import logging
from collections.abc import AsyncIterator
from pocketclaw.config import Settings
from pocketpaw.config import Settings
logger = logging.getLogger(__name__)
@@ -36,7 +36,7 @@ class OpenInterpreterAgent:
try:
from interpreter import interpreter
from pocketclaw.llm.client import resolve_llm_client
from pocketpaw.llm.client import resolve_llm_client
# Configure interpreter
interpreter.auto_run = True # Don't ask for confirmation

View File

@@ -24,10 +24,10 @@ import re
from pathlib import Path
from typing import AsyncIterator, Optional
from pocketclaw.agents.protocol import AgentEvent
from pocketclaw.config import Settings
from pocketclaw.llm.client import LLMClient, resolve_llm_client
from pocketclaw.tools.policy import ToolPolicy
from pocketpaw.agents.protocol import AgentEvent
from pocketpaw.config import Settings
from pocketpaw.llm.client import LLMClient, resolve_llm_client
from pocketpaw.tools.policy import ToolPolicy
logger = logging.getLogger(__name__)
@@ -359,7 +359,7 @@ class PocketPawOrchestrator:
# Initialize executor (Open Interpreter)
try:
from pocketclaw.agents.executor import OpenInterpreterExecutor
from pocketpaw.agents.executor import OpenInterpreterExecutor
self._executor = OpenInterpreterExecutor(self.settings)
except Exception as e:
@@ -391,7 +391,7 @@ class PocketPawOrchestrator:
def _get_mcp_tools(self) -> list[dict]:
"""Convert MCP tools to Anthropic tool format, filtered by policy."""
try:
from pocketclaw.mcp.manager import get_mcp_manager
from pocketpaw.mcp.manager import get_mcp_manager
except ImportError:
return []
@@ -622,7 +622,7 @@ class PocketPawOrchestrator:
return "\n".join(os.listdir(Path(path).expanduser()))
elif tool_name == "remember":
from pocketclaw.memory.manager import get_memory_manager
from pocketpaw.memory.manager import get_memory_manager
content = tool_input.get("content", "")
tags = tool_input.get("tags", [])
@@ -639,7 +639,7 @@ class PocketPawOrchestrator:
)
elif tool_name == "recall":
from pocketclaw.memory.manager import get_memory_manager
from pocketpaw.memory.manager import get_memory_manager
query = tool_input.get("query", "")
limit = tool_input.get("limit", 5)
@@ -661,7 +661,7 @@ class PocketPawOrchestrator:
return "\n".join(lines)
elif tool_name == "forget":
from pocketclaw.memory.manager import get_memory_manager
from pocketpaw.memory.manager import get_memory_manager
query = tool_input.get("query", "")
if not query:
@@ -684,7 +684,7 @@ class PocketPawOrchestrator:
mcp_parsed = self._parse_mcp_tool_name(tool_name)
if mcp_parsed:
server_name, original_tool = mcp_parsed
from pocketclaw.mcp.manager import get_mcp_manager
from pocketpaw.mcp.manager import get_mcp_manager
mgr = get_mcp_manager()
result = await mgr.call_tool(server_name, original_tool, tool_input)
@@ -752,7 +752,7 @@ class PocketPawOrchestrator:
# Smart model routing (opt-in, skip for Ollama — single model)
if self.settings.smart_routing_enabled and not self._llm.is_ollama:
from pocketclaw.agents.model_router import ModelRouter
from pocketpaw.agents.model_router import ModelRouter
model_router = ModelRouter(self.settings)
selection = model_router.classify(message)

View File

@@ -12,7 +12,7 @@ Changes:
import logging
from collections.abc import AsyncIterator
from pocketclaw.config import Settings
from pocketpaw.config import Settings
logger = logging.getLogger(__name__)
@@ -39,7 +39,7 @@ class AgentRouter:
def _initialize_agent(self) -> None:
"""Initialize the selected agent backend."""
from pocketclaw.llm.client import resolve_llm_client
from pocketpaw.llm.client import resolve_llm_client
backend = self.settings.agent_backend
@@ -59,7 +59,7 @@ class AgentRouter:
try:
if backend == "claude_agent_sdk":
from pocketclaw.agents.claude_sdk import ClaudeAgentSDKWrapper
from pocketpaw.agents.claude_sdk import ClaudeAgentSDKWrapper
self._agent = ClaudeAgentSDKWrapper(self.settings)
logger.info(
@@ -67,13 +67,13 @@ class AgentRouter:
)
elif backend == "pocketpaw_native":
from pocketclaw.agents.pocketpaw_native import PocketPawOrchestrator
from pocketpaw.agents.pocketpaw_native import PocketPawOrchestrator
self._agent = PocketPawOrchestrator(self.settings)
logger.info("🧠 [bold blue]PocketPaw Native[/] ─ Anthropic + Open Interpreter")
elif backend == "open_interpreter":
from pocketclaw.agents.open_interpreter import OpenInterpreterAgent
from pocketpaw.agents.open_interpreter import OpenInterpreterAgent
self._agent = OpenInterpreterAgent(self.settings)
logger.info(
@@ -82,7 +82,7 @@ class AgentRouter:
else:
logger.warning(f"Unknown backend: {backend} → using claude_agent_sdk")
from pocketclaw.agents.claude_sdk import ClaudeAgentSDKWrapper
from pocketpaw.agents.claude_sdk import ClaudeAgentSDKWrapper
self._agent = ClaudeAgentSDKWrapper(self.settings)
except ImportError as exc:

View File

@@ -0,0 +1,13 @@
# Bootstrap package.
# Created: 2026-02-02
from pocketpaw.bootstrap.protocol import BootstrapProviderProtocol, BootstrapContext
from pocketpaw.bootstrap.default_provider import DefaultBootstrapProvider
from pocketpaw.bootstrap.context_builder import AgentContextBuilder
__all__ = [
"BootstrapProviderProtocol",
"BootstrapContext",
"DefaultBootstrapProvider",
"AgentContextBuilder",
]

View File

@@ -7,11 +7,11 @@ Updated: 2026-02-10 - Channel-aware format hints
from __future__ import annotations
from pocketclaw.bootstrap.default_provider import DefaultBootstrapProvider
from pocketclaw.bootstrap.protocol import BootstrapProviderProtocol
from pocketclaw.bus.events import Channel
from pocketclaw.bus.format import CHANNEL_FORMAT_HINTS
from pocketclaw.memory.manager import MemoryManager, get_memory_manager
from pocketpaw.bootstrap.default_provider import DefaultBootstrapProvider
from pocketpaw.bootstrap.protocol import BootstrapProviderProtocol
from pocketpaw.bus.events import Channel
from pocketpaw.bus.format import CHANNEL_FORMAT_HINTS
from pocketpaw.memory.manager import MemoryManager, get_memory_manager
class AgentContextBuilder:
@@ -70,7 +70,7 @@ class AgentContextBuilder:
# 3. Inject sender identity block
if sender_id:
from pocketclaw.config import get_settings
from pocketpaw.config import get_settings
settings = get_settings()
if settings.owner_id:

View File

@@ -5,8 +5,8 @@ Created: 2026-02-02
from pathlib import Path
from pocketclaw.bootstrap.protocol import BootstrapContext, BootstrapProviderProtocol
from pocketclaw.config import get_config_dir
from pocketpaw.bootstrap.protocol import BootstrapContext, BootstrapProviderProtocol
from pocketpaw.config import get_config_dir
class DefaultBootstrapProvider(BootstrapProviderProtocol):

View File

@@ -3,10 +3,10 @@
import logging
import asyncio
from pocketclaw.config import Settings
from pocketclaw.bus import get_message_bus
from pocketclaw.bus.adapters.telegram_adapter import TelegramAdapter
from pocketclaw.agents.loop import AgentLoop
from pocketpaw.config import Settings
from pocketpaw.bus import get_message_bus
from pocketpaw.bus.adapters.telegram_adapter import TelegramAdapter
from pocketpaw.agents.loop import AgentLoop
logger = logging.getLogger(__name__)

View File

@@ -68,7 +68,7 @@ class BrowserDriver:
try:
import playwright # noqa: F401
except ImportError:
from pocketclaw._compat import require_extra
from pocketpaw._compat import require_extra
require_extra("playwright", "browser")

View File

@@ -171,7 +171,7 @@ def get_browser_session_manager() -> BrowserSessionManager:
if _manager_instance is None:
_manager_instance = BrowserSessionManager()
from pocketclaw.lifecycle import register
from pocketpaw.lifecycle import register
def _reset():
global _manager_instance

View File

@@ -1,9 +1,9 @@
# Message bus package.
# Created: 2026-02-02
from pocketclaw.bus.events import InboundMessage, OutboundMessage, SystemEvent, Channel
from pocketclaw.bus.queue import MessageBus, get_message_bus
from pocketclaw.bus.adapters import ChannelAdapter, BaseChannelAdapter
from pocketpaw.bus.events import InboundMessage, OutboundMessage, SystemEvent, Channel
from pocketpaw.bus.queue import MessageBus, get_message_bus
from pocketpaw.bus.adapters import ChannelAdapter, BaseChannelAdapter
__all__ = [
"InboundMessage",

View File

@@ -10,8 +10,8 @@ import subprocess
from abc import ABC, abstractmethod
from typing import Protocol
from pocketclaw.bus.events import Channel, InboundMessage, OutboundMessage
from pocketclaw.bus.queue import MessageBus
from pocketpaw.bus.events import Channel, InboundMessage, OutboundMessage
from pocketpaw.bus.queue import MessageBus
_log = logging.getLogger(__name__)

View File

@@ -7,7 +7,7 @@ import asyncio
import logging
from typing import Any
from pocketclaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketpaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
logger = logging.getLogger(__name__)
@@ -45,7 +45,7 @@ class DiscordAdapter(BaseChannelAdapter):
try:
import discord
except ImportError:
from pocketclaw.bus.adapters import auto_install
from pocketpaw.bus.adapters import auto_install
auto_install("discord", "discord")
import discord
@@ -172,7 +172,7 @@ class DiscordAdapter(BaseChannelAdapter):
media_paths: list[str] = []
if message.attachments:
try:
from pocketclaw.bus.media import build_media_hint, get_media_downloader
from pocketpaw.bus.media import build_media_hint, get_media_downloader
downloader = get_media_downloader()
names = []

View File

@@ -12,8 +12,8 @@ Created: 2026-02-07
import asyncio
import logging
from pocketclaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketclaw.bus.format import convert_markdown
from pocketpaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketpaw.bus.format import convert_markdown
logger = logging.getLogger(__name__)
@@ -72,7 +72,7 @@ class GoogleChatAdapter(BaseChannelAdapter):
from google.oauth2 import service_account
from googleapiclient.discovery import build
except ImportError:
from pocketclaw.bus.adapters import auto_install
from pocketpaw.bus.adapters import auto_install
auto_install("gchat", "googleapiclient")
from google.oauth2 import service_account
@@ -111,7 +111,7 @@ class GoogleChatAdapter(BaseChannelAdapter):
if attachment:
attachments = attachment if isinstance(attachment, list) else [attachment]
try:
from pocketclaw.bus.media import build_media_hint, get_media_downloader
from pocketpaw.bus.media import build_media_hint, get_media_downloader
downloader = get_media_downloader()
names = []

View File

@@ -12,7 +12,7 @@ import asyncio
import logging
import time
from pocketclaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketpaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
logger = logging.getLogger(__name__)
@@ -57,7 +57,7 @@ class MatrixAdapter(BaseChannelAdapter):
try:
from nio import AsyncClient, RoomMessageText
except ImportError:
from pocketclaw.bus.adapters import auto_install
from pocketpaw.bus.adapters import auto_install
auto_install("matrix", "nio")
from nio import AsyncClient, RoomMessageText
@@ -159,7 +159,7 @@ class MatrixAdapter(BaseChannelAdapter):
mxc_url = getattr(event, "url", None)
if mxc_url and mxc_url.startswith("mxc://"):
try:
from pocketclaw.bus.media import build_media_hint, get_media_downloader
from pocketpaw.bus.media import build_media_hint, get_media_downloader
# Convert mxc://server/media_id to HTTPS download URL
parts = mxc_url[len("mxc://") :]

View File

@@ -13,8 +13,8 @@ import threading
from pathlib import Path
from typing import Any
from pocketclaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketclaw.bus.format import convert_markdown
from pocketpaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketpaw.bus.format import convert_markdown
logger = logging.getLogger(__name__)
@@ -74,7 +74,7 @@ class NeonizeAdapter(BaseChannelAdapter):
from neonize.aioze.events import ConnectedEv, MessageEv
from neonize.utils.jid import Jid2String
except ImportError:
from pocketclaw.bus.adapters import auto_install
from pocketpaw.bus.adapters import auto_install
auto_install("whatsapp-personal", "neonize")
from neonize.aioze.client import NewAClient
@@ -141,7 +141,7 @@ class NeonizeAdapter(BaseChannelAdapter):
)
if media_msg:
try:
from pocketclaw.bus.media import build_media_hint, get_media_downloader
from pocketpaw.bus.media import build_media_hint, get_media_downloader
data = await client.download_any(message)
mime = getattr(media_msg, "mimetype", None)

View File

@@ -14,8 +14,8 @@ import logging
import httpx
from pocketclaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketclaw.bus.format import convert_markdown
from pocketpaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketpaw.bus.format import convert_markdown
logger = logging.getLogger(__name__)
@@ -96,7 +96,7 @@ class SignalAdapter(BaseChannelAdapter):
attachments = data_msg.get("attachments", [])
if attachments and self._http:
try:
from pocketclaw.bus.media import build_media_hint, get_media_downloader
from pocketpaw.bus.media import build_media_hint, get_media_downloader
downloader = get_media_downloader()
names = []

View File

@@ -7,8 +7,8 @@ import asyncio
import logging
from typing import Any
from pocketclaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketclaw.bus.format import convert_markdown
from pocketpaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketpaw.bus.format import convert_markdown
logger = logging.getLogger(__name__)
@@ -44,7 +44,7 @@ class SlackAdapter(BaseChannelAdapter):
from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler
from slack_bolt.async_app import AsyncApp
except ImportError:
from pocketclaw.bus.adapters import auto_install
from pocketpaw.bus.adapters import auto_install
auto_install("slack", "slack_bolt")
from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler
@@ -146,7 +146,7 @@ class SlackAdapter(BaseChannelAdapter):
files = event.get("files", [])
if files:
try:
from pocketclaw.bus.media import build_media_hint, get_media_downloader
from pocketpaw.bus.media import build_media_hint, get_media_downloader
downloader = get_media_downloader()
names = []

View File

@@ -12,8 +12,8 @@ import asyncio
import logging
from typing import Any
from pocketclaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketclaw.bus.format import convert_markdown
from pocketpaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketpaw.bus.format import convert_markdown
logger = logging.getLogger(__name__)
@@ -57,7 +57,7 @@ class TeamsAdapter(BaseChannelAdapter):
BotFrameworkAdapterSettings,
)
except ImportError:
from pocketclaw.bus.adapters import auto_install
from pocketpaw.bus.adapters import auto_install
auto_install("teams", "botbuilder")
from botbuilder.core import (
@@ -160,7 +160,7 @@ class TeamsAdapter(BaseChannelAdapter):
attachments = getattr(activity, "attachments", None) or []
if attachments:
try:
from pocketclaw.bus.media import build_media_hint, get_media_downloader
from pocketpaw.bus.media import build_media_hint, get_media_downloader
downloader = get_media_downloader()
names = []

View File

@@ -22,13 +22,13 @@ except ImportError as _exc:
"Install it with: pip install 'pocketpaw[telegram]'"
) from _exc
from pocketclaw.bus import (
from pocketpaw.bus import (
BaseChannelAdapter,
Channel,
InboundMessage,
OutboundMessage,
)
from pocketclaw.bus.format import convert_markdown
from pocketpaw.bus.format import convert_markdown
logger = logging.getLogger(__name__)
@@ -348,7 +348,7 @@ class TelegramAdapter(BaseChannelAdapter):
if file_obj:
try:
from pocketclaw.bus.media import build_media_hint, get_media_downloader
from pocketpaw.bus.media import build_media_hint, get_media_downloader
data = await file_obj.download_as_bytearray()
downloader = get_media_downloader()

View File

@@ -14,8 +14,8 @@ import json
import logging
from dataclasses import dataclass
from pocketclaw.bus.adapters import BaseChannelAdapter
from pocketclaw.bus.events import Channel, InboundMessage, OutboundMessage
from pocketpaw.bus.adapters import BaseChannelAdapter
from pocketpaw.bus.events import Channel, InboundMessage, OutboundMessage
_log = logging.getLogger(__name__)
@@ -115,7 +115,7 @@ class WebhookAdapter(BaseChannelAdapter):
media_urls = body.get("media_urls", [])
if media_urls and isinstance(media_urls, list):
try:
from pocketclaw.bus.media import build_media_hint, get_media_downloader
from pocketpaw.bus.media import build_media_hint, get_media_downloader
downloader = get_media_downloader()
names = []

View File

@@ -10,9 +10,9 @@ from typing import Any
from fastapi import WebSocket
from pocketclaw.bus.adapters import BaseChannelAdapter
from pocketclaw.bus.events import Channel, InboundMessage, OutboundMessage, SystemEvent
from pocketclaw.bus.queue import MessageBus
from pocketpaw.bus.adapters import BaseChannelAdapter
from pocketpaw.bus.events import Channel, InboundMessage, OutboundMessage, SystemEvent
from pocketpaw.bus.queue import MessageBus
logger = logging.getLogger(__name__)
@@ -77,7 +77,7 @@ class WebSocketAdapter(BaseChannelAdapter):
try:
import base64
from pocketclaw.bus.media import build_media_hint, get_media_downloader
from pocketpaw.bus.media import build_media_hint, get_media_downloader
downloader = get_media_downloader()
names = []

View File

@@ -7,8 +7,8 @@ import logging
import httpx
from pocketclaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketclaw.bus.format import convert_markdown
from pocketpaw.bus import BaseChannelAdapter, Channel, InboundMessage, OutboundMessage
from pocketpaw.bus.format import convert_markdown
logger = logging.getLogger(__name__)
@@ -143,7 +143,7 @@ class WhatsAppAdapter(BaseChannelAdapter):
path = await self._download_whatsapp_media(media_id, filename, mime)
if path:
media_paths.append(path)
from pocketclaw.bus.media import build_media_hint
from pocketpaw.bus.media import build_media_hint
caption += build_media_hint([filename])
except Exception as e:
@@ -171,7 +171,7 @@ class WhatsAppAdapter(BaseChannelAdapter):
return None
# Step 2: Download the actual file (reuse existing auth headers)
from pocketclaw.bus.media import get_media_downloader
from pocketpaw.bus.media import get_media_downloader
downloader = get_media_downloader()
return await downloader.download_url_with_auth(

View File

@@ -10,8 +10,8 @@ import logging
import re
import uuid
from pocketclaw.bus.events import InboundMessage, OutboundMessage
from pocketclaw.memory import get_memory_manager
from pocketpaw.bus.events import InboundMessage, OutboundMessage
from pocketpaw.memory import get_memory_manager
logger = logging.getLogger(__name__)
@@ -274,7 +274,7 @@ class CommandHandler:
async def _cmd_status(self, message: InboundMessage, session_key: str) -> OutboundMessage:
"""Show current session info."""
from pocketclaw.config import get_settings
from pocketpaw.config import get_settings
memory = get_memory_manager()
settings = get_settings()

View File

@@ -10,7 +10,7 @@ from __future__ import annotations
import re
from pocketclaw.bus.events import Channel
from pocketpaw.bus.events import Channel
# ---------------------------------------------------------------------------
# LLM system-prompt hints (one sentence each)

View File

@@ -13,7 +13,7 @@ from pathlib import Path
import httpx
from pocketclaw.config import get_config_dir, get_settings
from pocketpaw.config import get_config_dir, get_settings
logger = logging.getLogger(__name__)

View File

@@ -6,7 +6,7 @@ and publishes OutboundMessage events to the message bus.
import logging
from pocketclaw.bus.events import Channel, OutboundMessage
from pocketpaw.bus.events import Channel, OutboundMessage
logger = logging.getLogger(__name__)
@@ -26,14 +26,14 @@ async def notify(content: str, targets: list[str] | None = None) -> int:
Number of messages successfully published.
"""
if targets is None:
from pocketclaw.config import get_settings
from pocketpaw.config import get_settings
targets = get_settings().notification_channels
if not targets:
return 0
from pocketclaw.bus import get_message_bus
from pocketpaw.bus import get_message_bus
bus = get_message_bus()
count = 0

View File

@@ -7,7 +7,7 @@ import asyncio
import logging
from collections.abc import Awaitable, Callable
from pocketclaw.bus.events import Channel, InboundMessage, OutboundMessage, SystemEvent
from pocketpaw.bus.events import Channel, InboundMessage, OutboundMessage, SystemEvent
logger = logging.getLogger(__name__)
@@ -166,7 +166,7 @@ def get_message_bus() -> MessageBus:
if _bus is None:
_bus = MessageBus()
from pocketclaw.lifecycle import register
from pocketpaw.lifecycle import register
def _reset():
global _bus

View File

@@ -379,7 +379,7 @@ class Settings(BaseSettings):
Non-secret fields go to config.json. Secret fields (API keys, tokens)
go to the encrypted credential store.
"""
from pocketclaw.credentials import SECRET_FIELDS, get_credential_store
from pocketpaw.credentials import SECRET_FIELDS, get_credential_store
config_path = get_config_path()
@@ -543,7 +543,7 @@ class Settings(BaseSettings):
@classmethod
def load(cls) -> "Settings":
"""Load settings from config file + encrypted credential store."""
from pocketclaw.credentials import SECRET_FIELDS, get_credential_store
from pocketpaw.credentials import SECRET_FIELDS, get_credential_store
# Run one-time migration from plaintext config
_migrate_plaintext_keys()
@@ -614,7 +614,7 @@ _MIGRATION_DONE_PATH: Path | None = None
def _migrate_plaintext_keys() -> None:
"""One-time migration: move plaintext API keys from config.json to encrypted store."""
from pocketclaw.credentials import SECRET_FIELDS, get_credential_store
from pocketpaw.credentials import SECRET_FIELDS, get_credential_store
global _MIGRATION_DONE_PATH # noqa: PLW0603
if _MIGRATION_DONE_PATH is None:

View File

@@ -9,7 +9,7 @@ import logging
from datetime import UTC, datetime, timedelta
from pathlib import Path
from pocketclaw.config import get_config_dir, get_settings
from pocketpaw.config import get_config_dir, get_settings
logger = logging.getLogger(__name__)
@@ -127,7 +127,7 @@ async def run_self_audit() -> dict:
# Run audit_cli checks (the 7 original ones)
try:
from pocketclaw.security.audit_cli import (
from pocketpaw.security.audit_cli import (
_check_audit_log,
_check_bypass_permissions,
_check_config_permissions,

View File

@@ -41,21 +41,21 @@ except ImportError as _exc:
"but not installed. Reinstall with: pip install --upgrade pocketpaw"
) from _exc
from pocketclaw.agents.loop import AgentLoop
from pocketclaw.bootstrap import DefaultBootstrapProvider
from pocketclaw.bus import get_message_bus
from pocketclaw.bus.adapters.websocket_adapter import WebSocketAdapter
from pocketclaw.config import Settings, get_access_token, get_config_path, regenerate_token
from pocketclaw.daemon import get_daemon
from pocketclaw.deep_work.api import router as deep_work_router
from pocketclaw.memory import MemoryType, get_memory_manager
from pocketclaw.mission_control.api import router as mission_control_router
from pocketclaw.scheduler import get_scheduler
from pocketclaw.security import get_audit_logger
from pocketclaw.security.rate_limiter import api_limiter, auth_limiter, cleanup_all, ws_limiter
from pocketclaw.security.session_tokens import create_session_token, verify_session_token
from pocketclaw.skills import SkillExecutor, get_skill_loader
from pocketclaw.tunnel import get_tunnel_manager
from pocketpaw.agents.loop import AgentLoop
from pocketpaw.bootstrap import DefaultBootstrapProvider
from pocketpaw.bus import get_message_bus
from pocketpaw.bus.adapters.websocket_adapter import WebSocketAdapter
from pocketpaw.config import Settings, get_access_token, get_config_path, regenerate_token
from pocketpaw.daemon import get_daemon
from pocketpaw.deep_work.api import router as deep_work_router
from pocketpaw.memory import MemoryType, get_memory_manager
from pocketpaw.mission_control.api import router as mission_control_router
from pocketpaw.scheduler import get_scheduler
from pocketpaw.security import get_audit_logger
from pocketpaw.security.rate_limiter import api_limiter, auth_limiter, cleanup_all, ws_limiter
from pocketpaw.security.session_tokens import create_session_token, verify_session_token
from pocketpaw.skills import SkillExecutor, get_skill_loader
from pocketpaw.tunnel import get_tunnel_manager
logger = logging.getLogger(__name__)
@@ -148,7 +148,7 @@ async def broadcast_reminder(reminder: dict):
# Push to notification channels
try:
from pocketclaw.bus.notifier import notify
from pocketpaw.bus.notifier import notify
await notify(f"Reminder: {reminder.get('text', '')}")
except Exception:
@@ -168,7 +168,7 @@ async def broadcast_intention(intention_id: str, chunk: dict):
# Push message-type intention chunks to notification channels
if chunk.get("type") == "message":
try:
from pocketclaw.bus.notifier import notify
from pocketpaw.bus.notifier import notify
await notify(chunk.get("content", ""))
except Exception:
@@ -195,7 +195,7 @@ async def _start_channel_adapter(channel: str, settings: Settings | None = None)
if channel == "discord":
if not settings.discord_bot_token:
return False
from pocketclaw.bus.adapters.discord_adapter import DiscordAdapter
from pocketpaw.bus.adapters.discord_adapter import DiscordAdapter
adapter = DiscordAdapter(
token=settings.discord_bot_token,
@@ -209,7 +209,7 @@ async def _start_channel_adapter(channel: str, settings: Settings | None = None)
if channel == "slack":
if not settings.slack_bot_token or not settings.slack_app_token:
return False
from pocketclaw.bus.adapters.slack_adapter import SlackAdapter
from pocketpaw.bus.adapters.slack_adapter import SlackAdapter
adapter = SlackAdapter(
bot_token=settings.slack_bot_token,
@@ -224,7 +224,7 @@ async def _start_channel_adapter(channel: str, settings: Settings | None = None)
mode = settings.whatsapp_mode
if mode == "personal":
from pocketclaw.bus.adapters.neonize_adapter import NeonizeAdapter
from pocketpaw.bus.adapters.neonize_adapter import NeonizeAdapter
db_path = settings.whatsapp_neonize_db or None
adapter = NeonizeAdapter(db_path=db_path)
@@ -235,7 +235,7 @@ async def _start_channel_adapter(channel: str, settings: Settings | None = None)
# Business mode (Cloud API)
if not settings.whatsapp_access_token or not settings.whatsapp_phone_number_id:
return False
from pocketclaw.bus.adapters.whatsapp_adapter import WhatsAppAdapter
from pocketpaw.bus.adapters.whatsapp_adapter import WhatsAppAdapter
adapter = WhatsAppAdapter(
access_token=settings.whatsapp_access_token,
@@ -250,7 +250,7 @@ async def _start_channel_adapter(channel: str, settings: Settings | None = None)
if channel == "telegram":
if not settings.telegram_bot_token:
return False
from pocketclaw.bus.adapters.telegram_adapter import TelegramAdapter
from pocketpaw.bus.adapters.telegram_adapter import TelegramAdapter
adapter = TelegramAdapter(
token=settings.telegram_bot_token,
@@ -263,7 +263,7 @@ async def _start_channel_adapter(channel: str, settings: Settings | None = None)
if channel == "signal":
if not settings.signal_phone_number:
return False
from pocketclaw.bus.adapters.signal_adapter import SignalAdapter
from pocketpaw.bus.adapters.signal_adapter import SignalAdapter
adapter = SignalAdapter(
api_url=settings.signal_api_url,
@@ -277,7 +277,7 @@ async def _start_channel_adapter(channel: str, settings: Settings | None = None)
if channel == "matrix":
if not settings.matrix_homeserver or not settings.matrix_user_id:
return False
from pocketclaw.bus.adapters.matrix_adapter import MatrixAdapter
from pocketpaw.bus.adapters.matrix_adapter import MatrixAdapter
adapter = MatrixAdapter(
homeserver=settings.matrix_homeserver,
@@ -294,7 +294,7 @@ async def _start_channel_adapter(channel: str, settings: Settings | None = None)
if channel == "teams":
if not settings.teams_app_id or not settings.teams_app_password:
return False
from pocketclaw.bus.adapters.teams_adapter import TeamsAdapter
from pocketpaw.bus.adapters.teams_adapter import TeamsAdapter
adapter = TeamsAdapter(
app_id=settings.teams_app_id,
@@ -309,7 +309,7 @@ async def _start_channel_adapter(channel: str, settings: Settings | None = None)
if channel == "google_chat":
if not settings.gchat_service_account_key:
return False
from pocketclaw.bus.adapters.gchat_adapter import GoogleChatAdapter
from pocketpaw.bus.adapters.gchat_adapter import GoogleChatAdapter
adapter = GoogleChatAdapter(
mode=settings.gchat_mode,
@@ -323,7 +323,7 @@ async def _start_channel_adapter(channel: str, settings: Settings | None = None)
return True
if channel == "webhook":
from pocketclaw.bus.adapters.webhook_adapter import WebhookAdapter
from pocketpaw.bus.adapters.webhook_adapter import WebhookAdapter
adapter = WebhookAdapter()
await adapter.start(bus)
@@ -382,7 +382,7 @@ async def startup_event():
# Ensure project directories exist for all Deep Work projects
try:
from pocketclaw.mission_control.manager import get_mission_control_manager
from pocketpaw.mission_control.manager import get_mission_control_manager
mc_manager = get_mission_control_manager()
await mc_manager.ensure_project_directories()
@@ -391,7 +391,7 @@ async def startup_event():
# Recover Deep Work projects interrupted by previous shutdown
try:
from pocketclaw.deep_work import recover_interrupted_projects
from pocketpaw.deep_work import recover_interrupted_projects
recovered = await recover_interrupted_projects()
if recovered:
@@ -401,7 +401,7 @@ async def startup_event():
# Auto-start enabled MCP servers
try:
from pocketclaw.mcp.manager import get_mcp_manager
from pocketpaw.mcp.manager import get_mcp_manager
mcp = get_mcp_manager()
await mcp.start_enabled_servers()
@@ -461,7 +461,7 @@ async def shutdown_event():
# Stop MCP servers
try:
from pocketclaw.mcp.manager import get_mcp_manager
from pocketpaw.mcp.manager import get_mcp_manager
mcp = get_mcp_manager()
await mcp.stop_all()
@@ -475,7 +475,7 @@ async def shutdown_event():
@app.get("/api/mcp/status")
async def get_mcp_status():
"""Get status of all configured MCP servers."""
from pocketclaw.mcp.manager import get_mcp_manager
from pocketpaw.mcp.manager import get_mcp_manager
mgr = get_mcp_manager()
return mgr.get_server_status()
@@ -484,8 +484,8 @@ async def get_mcp_status():
@app.post("/api/mcp/add")
async def add_mcp_server(request: Request):
"""Add a new MCP server configuration and optionally start it."""
from pocketclaw.mcp.config import MCPServerConfig
from pocketclaw.mcp.manager import get_mcp_manager
from pocketpaw.mcp.config import MCPServerConfig
from pocketpaw.mcp.manager import get_mcp_manager
data = await request.json()
config = MCPServerConfig(
@@ -516,7 +516,7 @@ async def add_mcp_server(request: Request):
@app.post("/api/mcp/remove")
async def remove_mcp_server(request: Request):
"""Remove an MCP server config and stop it if running."""
from pocketclaw.mcp.manager import get_mcp_manager
from pocketpaw.mcp.manager import get_mcp_manager
data = await request.json()
name = data.get("name", "")
@@ -532,8 +532,8 @@ async def remove_mcp_server(request: Request):
@app.post("/api/mcp/toggle")
async def toggle_mcp_server(request: Request):
"""Toggle an MCP server: start if stopped/disconnected, stop if running."""
from pocketclaw.mcp.config import load_mcp_config
from pocketclaw.mcp.manager import get_mcp_manager
from pocketpaw.mcp.config import load_mcp_config
from pocketpaw.mcp.manager import get_mcp_manager
data = await request.json()
name = data.get("name", "")
@@ -565,8 +565,8 @@ async def toggle_mcp_server(request: Request):
@app.post("/api/mcp/test")
async def test_mcp_server(request: Request):
"""Test an MCP server connection and return discovered tools."""
from pocketclaw.mcp.config import MCPServerConfig
from pocketclaw.mcp.manager import get_mcp_manager
from pocketpaw.mcp.config import MCPServerConfig
from pocketpaw.mcp.manager import get_mcp_manager
data = await request.json()
config = MCPServerConfig(
@@ -599,8 +599,8 @@ async def test_mcp_server(request: Request):
@app.get("/api/mcp/presets")
async def list_mcp_presets():
"""Return all MCP presets with installed flag."""
from pocketclaw.mcp.config import load_mcp_config
from pocketclaw.mcp.presets import get_all_presets
from pocketpaw.mcp.config import load_mcp_config
from pocketpaw.mcp.presets import get_all_presets
installed_names = {c.name for c in load_mcp_config()}
presets = get_all_presets()
@@ -637,8 +637,8 @@ async def install_mcp_preset(request: Request):
"""Install an MCP preset by ID with user-supplied env vars."""
from fastapi.responses import JSONResponse
from pocketclaw.mcp.manager import get_mcp_manager
from pocketclaw.mcp.presets import get_preset, preset_to_config
from pocketpaw.mcp.manager import get_mcp_manager
from pocketpaw.mcp.presets import get_preset, preset_to_config
data = await request.json()
preset_id = data.get("preset_id", "")
@@ -775,8 +775,8 @@ async def install_from_registry(request: Request):
"""
from fastapi.responses import JSONResponse
from pocketclaw.mcp.config import MCPServerConfig
from pocketclaw.mcp.manager import get_mcp_manager
from pocketpaw.mcp.config import MCPServerConfig
from pocketpaw.mcp.manager import get_mcp_manager
data = await request.json()
server = data.get("server", {})
@@ -1120,7 +1120,7 @@ async def webhook_inbound(
if slot_dict is None:
raise HTTPException(status_code=404, detail=f"Webhook '{webhook_name}' not found")
from pocketclaw.bus.adapters.webhook_adapter import WebhookSlotConfig
from pocketpaw.bus.adapters.webhook_adapter import WebhookSlotConfig
slot = WebhookSlotConfig(
name=slot_dict["name"],
@@ -1501,7 +1501,7 @@ async def oauth_authorize(service: str = Query("google_gmail")):
detail="Google OAuth Client ID not configured. Set it in Settings first.",
)
from pocketclaw.integrations.oauth import OAuthManager
from pocketpaw.integrations.oauth import OAuthManager
manager = OAuthManager()
redirect_uri = f"http://localhost:{settings.web_port}/oauth/callback"
@@ -1533,8 +1533,8 @@ async def oauth_callback(
return HTMLResponse("<h2>Missing authorization code</h2>")
try:
from pocketclaw.integrations.oauth import OAuthManager
from pocketclaw.integrations.token_store import TokenStore
from pocketpaw.integrations.oauth import OAuthManager
from pocketpaw.integrations.token_store import TokenStore
settings = Settings.load()
manager = OAuthManager(TokenStore())
@@ -2299,7 +2299,7 @@ async def websocket_endpoint(
agent_loop.reset_router()
# Clear settings cache so memory manager picks up new values
from pocketclaw.config import get_settings as _get_settings
from pocketpaw.config import get_settings as _get_settings
_get_settings.cache_clear()
@@ -2565,7 +2565,7 @@ async def websocket_endpoint(
# ==================== Plan Mode API ====================
elif action == "approve_plan":
from pocketclaw.agents.plan_mode import get_plan_manager
from pocketpaw.agents.plan_mode import get_plan_manager
pm = get_plan_manager()
session_key = data.get("session_key", "")
@@ -2578,7 +2578,7 @@ async def websocket_endpoint(
)
elif action == "reject_plan":
from pocketclaw.agents.plan_mode import get_plan_manager
from pocketpaw.agents.plan_mode import get_plan_manager
pm = get_plan_manager()
session_key = data.get("session_key", "")
@@ -2969,7 +2969,7 @@ async def clear_audit_log():
@app.post("/api/security-audit")
async def run_security_audit_endpoint():
"""Run security audit checks and return results."""
from pocketclaw.security.audit_cli import (
from pocketpaw.security.audit_cli import (
_check_audit_log,
_check_bypass_permissions,
_check_config_permissions,
@@ -3022,7 +3022,7 @@ async def run_security_audit_endpoint():
@app.get("/api/self-audit/reports")
async def get_self_audit_reports():
"""List recent self-audit reports."""
from pocketclaw.config import get_config_dir
from pocketpaw.config import get_config_dir
reports_dir = get_config_dir() / "audit_reports"
if not reports_dir.exists():
@@ -3052,7 +3052,7 @@ async def get_self_audit_report(date: str):
"""Get a specific self-audit report by date."""
import json
from pocketclaw.config import get_config_dir
from pocketpaw.config import get_config_dir
report_path = get_config_dir() / "audit_reports" / f"{date}.json"
if not report_path.exists():
@@ -3063,7 +3063,7 @@ async def get_self_audit_report(date: str):
@app.post("/api/self-audit/run")
async def run_self_audit_endpoint():
"""Trigger a self-audit run and return the report."""
from pocketclaw.daemon.self_audit import run_self_audit
from pocketpaw.daemon.self_audit import run_self_audit
report = await run_self_audit()
return report
@@ -3077,7 +3077,7 @@ async def handle_tool(websocket: WebSocket, tool: str, settings: Settings, data:
import asyncio
from concurrent.futures import ThreadPoolExecutor
from pocketclaw.tools.status import get_system_status
from pocketpaw.tools.status import get_system_status
loop = asyncio.get_event_loop()
with ThreadPoolExecutor() as pool:
@@ -3085,7 +3085,7 @@ async def handle_tool(websocket: WebSocket, tool: str, settings: Settings, data:
await websocket.send_json({"type": "status", "content": status})
elif tool == "screenshot":
from pocketclaw.tools.screenshot import take_screenshot
from pocketpaw.tools.screenshot import take_screenshot
result = take_screenshot() # sync function
@@ -3097,7 +3097,7 @@ async def handle_tool(websocket: WebSocket, tool: str, settings: Settings, data:
await websocket.send_json({"type": "error", "content": result})
elif tool == "fetch":
from pocketclaw.tools.fetch import list_directory
from pocketpaw.tools.fetch import list_directory
path = data.get("path") or str(Path.home())
result = list_directory(path, settings.file_jail_path) # sync function
@@ -3115,7 +3115,7 @@ async def handle_tool(websocket: WebSocket, tool: str, settings: Settings, data:
async def handle_file_navigation(websocket: WebSocket, path: str, settings: Settings):
"""Handle file browser navigation."""
from pocketclaw.tools.fetch import list_directory
from pocketpaw.tools.fetch import list_directory
result = list_directory(path, settings.file_jail_path) # sync function
await websocket.send_json({"type": "message", "content": result})
@@ -3129,7 +3129,7 @@ async def handle_file_browse(
If an optional ``context`` string is provided it is echoed back in the
response so the frontend can route sidebar vs modal file responses.
"""
from pocketclaw.tools.fetch import is_safe_path
from pocketpaw.tools.fetch import is_safe_path
def _resp(payload: dict) -> dict:
"""Attach context to every response so frontend can route sidebar vs modal."""
@@ -3251,12 +3251,12 @@ async def save_memory_settings(request: Request):
settings.save()
# Clear settings cache so memory manager picks up new values
from pocketclaw.config import get_settings as _get_settings
from pocketpaw.config import get_settings as _get_settings
_get_settings.cache_clear()
# Force reload the memory manager with fresh settings
from pocketclaw.memory import get_memory_manager
from pocketpaw.memory import get_memory_manager
manager = get_memory_manager(force_reload=True)
agent_loop.memory = manager

View File

@@ -16,7 +16,7 @@
import logging
from pocketclaw.deep_work.models import (
from pocketpaw.deep_work.models import (
AgentSpec,
PlannerResult,
Project,
@@ -55,9 +55,9 @@ def get_deep_work_session():
if _session_instance is not None:
return _session_instance
from pocketclaw.deep_work.session import DeepWorkSession
from pocketclaw.mission_control.executor import get_mc_task_executor
from pocketclaw.mission_control.manager import get_mission_control_manager
from pocketpaw.deep_work.session import DeepWorkSession
from pocketpaw.mission_control.executor import get_mc_task_executor
from pocketpaw.mission_control.manager import get_mission_control_manager
manager = get_mission_control_manager()
executor = get_mc_task_executor()

View File

@@ -47,9 +47,9 @@ async def start_deep_work(request: StartDeepWorkRequest) -> dict[str, Any]:
planner in the background. Frontend tracks progress via WebSocket
events (dw_planning_phase, dw_planning_complete).
"""
from pocketclaw.deep_work import get_deep_work_session
from pocketclaw.deep_work.models import ProjectStatus
from pocketclaw.mission_control.manager import get_mission_control_manager
from pocketpaw.deep_work import get_deep_work_session
from pocketpaw.deep_work.models import ProjectStatus
from pocketpaw.mission_control.manager import get_mission_control_manager
manager = get_mission_control_manager()
@@ -86,8 +86,8 @@ async def get_plan(project_id: str) -> dict[str, Any]:
Returns project details, tasks, progress, PRD document, and execution_levels
(task IDs grouped by dependency level for parallel execution).
"""
from pocketclaw.deep_work.scheduler import DependencyScheduler
from pocketclaw.mission_control.manager import get_mission_control_manager
from pocketpaw.deep_work.scheduler import DependencyScheduler
from pocketpaw.mission_control.manager import get_mission_control_manager
manager = get_mission_control_manager()
project = await manager.get_project(project_id)
@@ -124,7 +124,7 @@ async def get_plan(project_id: str) -> dict[str, Any]:
@router.post("/projects/{project_id}/approve")
async def approve_project(project_id: str) -> dict[str, Any]:
"""Approve a project plan and start execution."""
from pocketclaw.deep_work import approve_project as _approve
from pocketpaw.deep_work import approve_project as _approve
try:
project = await _approve(project_id)
@@ -139,7 +139,7 @@ async def approve_project(project_id: str) -> dict[str, Any]:
@router.post("/projects/{project_id}/pause")
async def pause_project(project_id: str) -> dict[str, Any]:
"""Pause project execution."""
from pocketclaw.deep_work import pause_project as _pause
from pocketpaw.deep_work import pause_project as _pause
try:
project = await _pause(project_id)
@@ -154,7 +154,7 @@ async def pause_project(project_id: str) -> dict[str, Any]:
@router.post("/projects/{project_id}/resume")
async def resume_project(project_id: str) -> dict[str, Any]:
"""Resume a paused project."""
from pocketclaw.deep_work import resume_project as _resume
from pocketpaw.deep_work import resume_project as _resume
try:
project = await _resume(project_id)
@@ -173,9 +173,9 @@ async def skip_task(project_id: str, task_id: str) -> dict[str, Any]:
Sets task status to SKIPPED with completed_at timestamp, then
cascades unblocking via the scheduler.
"""
from pocketclaw.deep_work import get_deep_work_session
from pocketclaw.mission_control.manager import get_mission_control_manager
from pocketclaw.mission_control.models import TaskStatus, now_iso
from pocketpaw.deep_work import get_deep_work_session
from pocketpaw.mission_control.manager import get_mission_control_manager
from pocketpaw.mission_control.models import TaskStatus, now_iso
manager = get_mission_control_manager()

View File

@@ -7,7 +7,7 @@
import logging
from pocketclaw.mission_control.models import Task
from pocketpaw.mission_control.models import Task
logger = logging.getLogger(__name__)
@@ -115,8 +115,8 @@ class HumanTaskRouter:
async def _publish_outbound(self, content: str, metadata: dict) -> None:
"""Broadcast OutboundMessage to all active channel adapters."""
try:
from pocketclaw.bus import get_message_bus
from pocketclaw.bus.events import Channel, OutboundMessage
from pocketpaw.bus import get_message_bus
from pocketpaw.bus.events import Channel, OutboundMessage
bus = get_message_bus()
msg = OutboundMessage(

View File

@@ -11,7 +11,7 @@ from dataclasses import dataclass, field
from enum import Enum
from typing import Any
from pocketclaw.mission_control.models import generate_id, now_iso
from pocketpaw.mission_control.models import generate_id, now_iso
# ============================================================================
# Enums

View File

@@ -11,8 +11,8 @@ import json
import logging
import re
from pocketclaw.deep_work.models import AgentSpec, PlannerResult, TaskSpec
from pocketclaw.deep_work.prompts import (
from pocketpaw.deep_work.models import AgentSpec, PlannerResult, TaskSpec
from pocketpaw.deep_work.prompts import (
PRD_PROMPT,
RESEARCH_PROMPT,
RESEARCH_PROMPT_DEEP,
@@ -20,8 +20,8 @@ from pocketclaw.deep_work.prompts import (
TASK_BREAKDOWN_PROMPT,
TEAM_ASSEMBLY_PROMPT,
)
from pocketclaw.mission_control.manager import MissionControlManager
from pocketclaw.mission_control.models import AgentProfile
from pocketpaw.mission_control.manager import MissionControlManager
from pocketpaw.mission_control.models import AgentProfile
logger = logging.getLogger(__name__)
@@ -80,8 +80,8 @@ class PlannerAgent:
progress (e.g. spinner text).
"""
# Create a single AgentRouter for all phases (avoids 4x SDK init)
from pocketclaw.agents.router import AgentRouter
from pocketclaw.config import get_settings
from pocketpaw.agents.router import AgentRouter
from pocketpaw.config import get_settings
router = AgentRouter(get_settings())
@@ -180,8 +180,8 @@ class PlannerAgent:
router: Optional pre-created AgentRouter (avoids re-initialization).
"""
if router is None:
from pocketclaw.agents.router import AgentRouter
from pocketclaw.config import get_settings
from pocketpaw.agents.router import AgentRouter
from pocketpaw.config import get_settings
router = AgentRouter(get_settings())
@@ -259,8 +259,8 @@ class PlannerAgent:
message = phase_messages.get(phase, f"Planning phase: {phase}")
try:
from pocketclaw.bus import get_message_bus
from pocketclaw.bus.events import SystemEvent
from pocketpaw.bus import get_message_bus
from pocketpaw.bus.events import SystemEvent
bus = get_message_bus()
import asyncio

View File

@@ -14,7 +14,7 @@ import asyncio
import logging
from collections import deque
from pocketclaw.mission_control.models import Task, TaskStatus, now_iso
from pocketpaw.mission_control.models import Task, TaskStatus, now_iso
logger = logging.getLogger(__name__)
@@ -156,7 +156,7 @@ class DependencyScheduler:
if all_done:
project = await self.manager.get_project(project_id)
if project:
from pocketclaw.deep_work.models import ProjectStatus
from pocketpaw.deep_work.models import ProjectStatus
project.status = ProjectStatus.COMPLETED
project.completed_at = now_iso()

View File

@@ -18,12 +18,12 @@
import logging
from typing import Any
from pocketclaw.deep_work.human_tasks import HumanTaskRouter
from pocketclaw.deep_work.models import Project, ProjectStatus
from pocketclaw.deep_work.planner import PlannerAgent
from pocketclaw.deep_work.scheduler import DependencyScheduler
from pocketclaw.mission_control.manager import MissionControlManager
from pocketclaw.mission_control.models import (
from pocketpaw.deep_work.human_tasks import HumanTaskRouter
from pocketpaw.deep_work.models import Project, ProjectStatus
from pocketpaw.deep_work.planner import PlannerAgent
from pocketpaw.deep_work.scheduler import DependencyScheduler
from pocketpaw.mission_control.manager import MissionControlManager
from pocketpaw.mission_control.models import (
DocumentType,
TaskPriority,
TaskStatus,
@@ -77,7 +77,7 @@ class DeepWorkSession:
if self._subscribed:
return
try:
from pocketclaw.bus import get_message_bus
from pocketpaw.bus import get_message_bus
bus = get_message_bus()
bus.subscribe_system(self._on_system_event)
@@ -404,8 +404,8 @@ class DeepWorkSession:
try:
import asyncio
from pocketclaw.bus import get_message_bus
from pocketclaw.bus.events import SystemEvent
from pocketpaw.bus import get_message_bus
from pocketpaw.bus.events import SystemEvent
bus = get_message_bus()
loop = asyncio.get_running_loop()

View File

@@ -3,10 +3,10 @@
import asyncio
import logging
from pocketclaw.agents.loop import AgentLoop
from pocketclaw.bus import get_message_bus
from pocketclaw.bus.adapters.discord_adapter import DiscordAdapter
from pocketclaw.config import Settings
from pocketpaw.agents.loop import AgentLoop
from pocketpaw.bus import get_message_bus
from pocketpaw.bus.adapters.discord_adapter import DiscordAdapter
from pocketpaw.config import Settings
logger = logging.getLogger(__name__)

Some files were not shown because too many files have changed in this diff Show More