Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
# Top-level modules from pyproject.toml py-modules
modules = [
'cheetahclaws', 'agent', 'agent_runner', 'bootstrap',
'circuit_breaker', 'cloudsave', 'compaction', 'cc_config',
'circuit_breaker', 'cloudsave', 'compaction', 'config',
'context', 'health', 'jobs', 'logging_utils', 'memory',
'providers', 'quota', 'runtime', 'skills', 'subagent',
'tmux_tools', 'tool_registry',
Expand All @@ -66,7 +66,7 @@ jobs:
'tools', 'tools.security', 'tools.fs', 'tools.shell',
'tools.web', 'tools.notebook', 'tools.diagnostics',
'tools.interaction',
'cc_mcp', 'monitor', 'multi_agent', 'plugin', 'skill', 'task',
'mcp_client', 'monitor', 'multi_agent', 'plugin', 'skill', 'task',
'voice', 'video', 'checkpoint', 'ui', 'bridges', 'commands',
'modular',
]
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ cheetahclaws/
├── bridges/ # Telegram, WeChat, Slack integrations
├── plugin/ # Plugin system (install, load, manifest parsing)
├── skill/ # Skill system (Markdown prompt templates)
├── cc_daemon/ # Daemon foundation (F-1..F-9 all landed) — `cheetahclaws serve` + RPC surface (agent/monitor/bridge/session/proactive/system); see docs/RFC/0002
├── cc_mcp/ # MCP (Model Context Protocol) client & tools
├── daemon/ # Daemon foundation (F-1..F-9 all landed) — `cheetahclaws serve` + RPC surface (agent/monitor/bridge/session/proactive/system); see docs/RFC/0002
├── mcp_client/ # MCP (Model Context Protocol) client & tools
├── research/lab/ # Autonomous multi-agent research engine (/lab — 9-stage state machine + sandboxed experiments + citation verifier)
├── memory/ # Persistent memory system
├── multi_agent/ # Sub-agent spawning & worktree isolation
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ Detailed guides live in [`docs/guides/`](docs/guides/) to keep this README focus
| [**FAQ**](docs/guides/faq.md) | The full FAQ (MCP, models/providers, CLI/scripting, voice) |
| [**Plugin Authoring**](docs/guides/plugin-authoring.md) · [Example](examples/example-plugin/) | Build a plugin: tools, commands, skills, MCP; starter template |
| [**Research Lab**](docs/guides/research-lab.md) | `/lab start <topic>` — autonomous multi-agent paper writing with sandboxed experiments |
| [**Agent OS**](docs/agent-os.md) · [RFC index](docs/RFC/) | The `cc_kernel/` layer + all design notes (RFC 0001-0032) |
| [**Agent OS**](docs/agent-os.md) · [RFC index](docs/RFC/) | The `kernel/` layer + all design notes (RFC 0001-0032) |
| [**Contributing**](CONTRIBUTING.md) | Project structure, architecture guide, PR checklist |

---
Expand Down
12 changes: 6 additions & 6 deletions agent_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def start_runner(
# send_fn is ignored in subprocess mode for the skeleton —
# ``notify`` IPC messages are dropped on the supervisor side
# until F-6/7/8 wires bridge delivery in.
from cc_daemon import runner_supervisor
from daemon import runner_supervisor
return runner_supervisor.start(
name=name,
template_name=template_name,
Expand Down Expand Up @@ -161,7 +161,7 @@ def stop_runner(name: str) -> bool:
return True
# Subprocess mode (F-4): the handle lives in the daemon supervisor.
try:
from cc_daemon import runner_supervisor
from daemon import runner_supervisor
except Exception:
return False
return runner_supervisor.stop(name)
Expand All @@ -175,7 +175,7 @@ def stop_all() -> int:
r.stop()
count = len(runners)
try:
from cc_daemon import runner_supervisor
from daemon import runner_supervisor
count += runner_supervisor.stop_all()
except Exception:
pass
Expand Down Expand Up @@ -624,9 +624,9 @@ def _persist_record(self, rec: _IterationRecord) -> None:
# When invoked as ``python -m agent_runner --pipe``, this module turns
# itself into a runner driven by a JSON-line IPC channel on stdin/stdout
# instead of the in-process thread + send_fn API. The supervisor side lives
# in ``cc_daemon/runner_supervisor.py``.
# in ``daemon/runner_supervisor.py``.
#
# Protocol (see cc_daemon/runner_ipc.py docstring for the full message
# Protocol (see daemon/runner_ipc.py docstring for the full message
# catalogue):
# 1. Supervisor writes {"op": "init", "payload": {...}} on stdin.
# 2. We reply {"op": "ready"} on stdout, then run the existing
Expand All @@ -642,7 +642,7 @@ def _pipe_main(name_arg: Optional[str] = None) -> int:
"""Subprocess entry point. Returns the process exit code."""
import argparse
import sys as _sys
from cc_daemon.runner_ipc import IpcReadTimeout, JsonLineChannel
from daemon.runner_ipc import IpcReadTimeout, JsonLineChannel

parser = argparse.ArgumentParser(prog="agent_runner")
parser.add_argument("--pipe", action="store_true",
Expand Down
2 changes: 1 addition & 1 deletion bridges/qq.py
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,7 @@ def cmd_qq(args: str, _state, config) -> bool:
"""
global _qq_thread, _qq_stop
import os as _os
from cc_config import save_config
from config import save_config
from bridges import resolve_bridge_token, scrub_token_from_history

parts = args.strip().split()
Expand Down
2 changes: 1 addition & 1 deletion bridges/slack.py
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@ def cmd_slack(args: str, _state, config) -> bool:
"""
global _slack_thread, _slack_stop
import os as _os
from cc_config import save_config
from config import save_config
from bridges import resolve_bridge_token, scrub_token_from_history

parts = args.strip().split()
Expand Down
2 changes: 1 addition & 1 deletion bridges/telegram.py
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,7 @@ def cmd_telegram(args: str, _state, config) -> bool:
/telegram status — show current status
"""
global _telegram_thread, _telegram_stop
from cc_config import save_config
from config import save_config
from bridges import resolve_bridge_token, scrub_token_from_history

parts = args.strip().split()
Expand Down
8 changes: 4 additions & 4 deletions bridges/wechat.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ def _wx_typing_loop(user_id: str, stop_event: threading.Event, config: dict) ->

def _wx_qr_login(config: dict, bot_type: str = _ILINK_DEFAULT_BOT_TYPE,
timeout_seconds: int = 480) -> bool:
from cc_config import save_config
from config import save_config
import time as _time

info("Fetching WeChat QR code from iLink...")
Expand Down Expand Up @@ -394,7 +394,7 @@ def _wx_poll_loop(token: str, base_url: str, config: dict) -> str:
print(clr("\n ⚠ WeChat: session expired — re-authenticate with /wechat login", "yellow"))
config.pop("wechat_token", None)
config.pop("wechat_base_url", None)
from cc_config import save_config
from config import save_config
save_config(config)
_log.warn("bridge_auth_error", bridge="wechat", ret=ret, errcode=errcode)
session_ctx.wx_send = None
Expand Down Expand Up @@ -425,7 +425,7 @@ def _wx_poll_loop(token: str, base_url: str, config: dict) -> str:
and not from_uid.endswith("@chatroom")):
config["wechat_self_uid"] = from_uid
try:
from cc_config import save_config
from config import save_config
save_config(config)
except Exception:
pass
Expand Down Expand Up @@ -1022,7 +1022,7 @@ def cmd_wechat(args: str, _state, config) -> bool:
/wechat logout — clear saved credentials
"""
global _wechat_thread, _wechat_stop
from cc_config import save_config
from config import save_config

sub = args.strip().split()[0].lower() if args.strip() else ""

Expand Down
18 changes: 9 additions & 9 deletions cheetahclaws.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,13 +390,13 @@ def ask_permission_interactive(desc: str, config: dict) -> bool:
def _proactive_foreign_daemon_running() -> bool:
"""True iff a daemon other than this process owns the discovery file.

F-5: when one is running, the daemon's :mod:`cc_daemon.proactive_scheduler`
F-5: when one is running, the daemon's :mod:`daemon.proactive_scheduler`
fires the tick events; the REPL's own watcher must step aside so we
don't double-fire or run on stale local state.
"""
try:
import os
from cc_daemon import discovery
from daemon import discovery
info_d = discovery.locate()
if info_d is None:
return False
Expand Down Expand Up @@ -896,7 +896,7 @@ def _headless_run_query(prompt: str, is_background: bool = False) -> None:
# ── Main REPL ──────────────────────────────────────────────────────────────

def repl(config: dict, initial_prompt: str = None):
from cc_config import HISTORY_FILE
from config import HISTORY_FILE
from context import build_system_prompt
from agent import AgentState, run, TextChunk, ThinkingChunk, ToolStart, ToolEnd, TurnDone, PermissionRequest, QuotaPause

Expand Down Expand Up @@ -1918,7 +1918,7 @@ def main():
# rotate-token). See docs/RFC/0001-daemon-design-note.md and
# docs/RFC/0002-daemon-foundation-roadmap.md.
if len(sys.argv) >= 2 and sys.argv[1] == "serve":
from cc_daemon.cli import serve_main as _serve_main
from daemon.cli import serve_main as _serve_main
sys.exit(_serve_main(sys.argv[2:]))
if len(sys.argv) >= 2 and sys.argv[1] == "daemon":
from commands.daemon_cmd import dispatch as _daemon_dispatch
Expand All @@ -1928,14 +1928,14 @@ def main():
# existing daemon RPC channel; gracefully reports "not running"
# when the daemon is absent.
if len(sys.argv) >= 2 and sys.argv[1] == "kernel":
from cc_kernel.cli import dispatch as _kernel_dispatch
from kernel.cli import dispatch as _kernel_dispatch
sys.exit(_kernel_dispatch(sys.argv[2:]))
# Backward-compat alias for the spike's `cheetahclaws spike-daemon ...`
# surface (referenced in docs/RFC/0001-spike-notes.md). Routes through
# the same paths as `serve` / `daemon <action>` so spike-notes commands
# keep working unchanged.
if len(sys.argv) >= 2 and sys.argv[1] == "spike-daemon":
from cc_daemon.cli import main as _legacy_main
from daemon.cli import main as _legacy_main
sys.exit(_legacy_main(sys.argv[2:]))

parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -1985,7 +1985,7 @@ def main():
sys.exit(0)

if args.web:
from cc_config import load_config as _load_cfg, save_config as _save_cfg
from config import load_config as _load_cfg, save_config as _save_cfg
_cfg = _load_cfg()
# --model needs to persist: web request handlers reload config from
# disk per request, so an in-memory override would be ignored.
Expand All @@ -2008,7 +2008,7 @@ def main():
start_web_server(port=args.port, host=args.host, no_auth=args.no_auth)
sys.exit(0)

from cc_config import load_config, save_config, has_api_key
from config import load_config, save_config, has_api_key
from providers import detect_provider, PROVIDERS

config = load_config()
Expand Down Expand Up @@ -2054,7 +2054,7 @@ def main():
warn(f"--budget: {_e} (e.g. --budget $5 or --budget 200k); ignoring.")

# ── Setup wizard: --setup flag or first-run auto-trigger ─────────────
from cc_config import CONFIG_FILE
from config import CONFIG_FILE
is_first_run = not CONFIG_FILE.exists() or os.path.getsize(CONFIG_FILE) < 5
if args.setup or (is_first_run and sys.stdin.isatty() and not args.print_mode):
run_setup_wizard(config)
Expand Down
6 changes: 3 additions & 3 deletions commands/advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -2240,10 +2240,10 @@ def cmd_skills(_args: str, _state, config) -> bool:

def cmd_mcp(args: str, _state, config) -> bool:
"""Show MCP server status, or manage servers."""
from cc_mcp.client import get_mcp_manager
from cc_mcp.config import (load_mcp_configs, add_server_to_user_config,
from mcp_client.client import get_mcp_manager
from mcp_client.config import (load_mcp_configs, add_server_to_user_config,
remove_server_from_user_config, list_config_files)
from cc_mcp.tools import initialize_mcp, reload_mcp, refresh_server
from mcp_client.tools import initialize_mcp, reload_mcp, refresh_server

parts = args.split() if args.strip() else []
subcmd = parts[0].lower() if parts else ""
Expand Down
14 changes: 7 additions & 7 deletions commands/config_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def cmd_model(args: str, _state, config) -> bool:
config["model"] = m
pname = detect_provider(m)
ok(f"Model set to {m} (provider: {pname})")
from cc_config import save_config
from config import save_config
save_config(config)
return True

Expand Down Expand Up @@ -89,7 +89,7 @@ def _interactive_ollama_picker(config: dict) -> bool:
if 0 <= idx < len(models):
new_model = f"ollama/{models[idx]}"
config["model"] = new_model
from cc_config import save_config
from config import save_config
save_config(config)
ok(f"Model updated to {new_model}")
return True
Expand All @@ -101,7 +101,7 @@ def _interactive_ollama_picker(config: dict) -> bool:


def cmd_config(args: str, _state, config) -> bool:
from cc_config import save_config
from config import save_config
if not args:
_SECRETS = {"api_key", "anthropic_api_key", "telegram_token", "wechat_token"}
display = {k: v for k, v in config.items()
Expand Down Expand Up @@ -148,7 +148,7 @@ def cmd_config(args: str, _state, config) -> bool:


def cmd_verbose(_args: str, _state, config) -> bool:
from cc_config import save_config
from config import save_config
config["verbose"] = not config.get("verbose", False)
state_str = "ON" if config["verbose"] else "OFF"
ok(f"Verbose mode: {state_str}")
Expand All @@ -157,7 +157,7 @@ def cmd_verbose(_args: str, _state, config) -> bool:


def cmd_quiet(_args: str, _state, config) -> bool:
from cc_config import save_config
from config import save_config
config["quiet"] = not config.get("quiet", True)
state_str = "ON" if config["quiet"] else "OFF"
ok(f"Quiet mode: {state_str} "
Expand All @@ -168,7 +168,7 @@ def cmd_quiet(_args: str, _state, config) -> bool:


def cmd_thinking(_args: str, _state, config) -> bool:
from cc_config import save_config
from config import save_config
config["thinking"] = not config.get("thinking", False)
state_str = "ON" if config["thinking"] else "OFF"
ok(f"Extended thinking: {state_str}")
Expand All @@ -177,7 +177,7 @@ def cmd_thinking(_args: str, _state, config) -> bool:


def cmd_permissions(args: str, _state, config) -> bool:
from cc_config import save_config
from config import save_config
from tools import ask_input_interactive
modes = ["auto", "accept-all", "manual"]
mode_desc = {
Expand Down
10 changes: 5 additions & 5 deletions commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def _est(text: str) -> int:


def cmd_cost(_args: str, state, config) -> bool:
from cc_config import calc_cost
from config import calc_cost
cost = calc_cost(config["model"],
state.total_input_tokens,
state.total_output_tokens)
Expand All @@ -237,7 +237,7 @@ def cmd_budget(args: str, state, config) -> bool:
/budget clear remove all caps (unlimited)
"""
import quota as _quota
from cc_config import save_config
from config import save_config

arg = args.strip()
sid = config.get("_session_id", "default")
Expand Down Expand Up @@ -668,7 +668,7 @@ def _fail(msg):

def run_setup_wizard(config: dict) -> None:
"""Interactive first-run setup: pick provider, set API key, verify."""
from cc_config import save_config
from config import save_config
from providers import PROVIDERS, detect_provider, get_api_key

print()
Expand Down Expand Up @@ -840,7 +840,7 @@ def _proactive_daemon_running() -> bool:
"""
try:
import os
from cc_daemon import discovery
from daemon import discovery
info_d = discovery.locate()
if info_d is None:
return False
Expand All @@ -858,7 +858,7 @@ def _proactive_rpc(method: str, params: dict | None = None) -> dict | None:
import http.client
import json
import os
from cc_daemon import API_VERSION, API_VERSION_HEADER, discovery
from daemon import API_VERSION, API_VERSION_HEADER, discovery

info_d = discovery.locate()
if info_d is None:
Expand Down
Loading
Loading