Standalone CLI and daemon for NapCat QQ bot management with skills-fs integration
Project description
napcat-cli
Standalone CLI and daemon for NapCat QQ bot management with skills-fs integration.
介绍博文: napcat-cli 群配置与管理速查 — 从零配置、群消息收发、Agent Wake 的完整链路。
napcat-cli provides a CLI for all NapCat API operations, a WebSocket daemon that bridges to a skills-fs HTTP provider, and an agent wake-up mechanism for integrating with Hermes or other AI agents.
napcat send group 123456 -m "Hello"
napcat send private 987654 -m "Hi"
napcat recall 1001
napcat events --limit 10
napcat daemon start
napcat wake --reason NEW_MESSAGE
Commands
| Command | Description |
|---|---|
napcat api <endpoint> |
Raw API access (like gh api) |
napcat send group <id> -m "msg" |
Send group message |
napcat send private <id> -m "msg" |
Send private message |
napcat reply <id> -m "msg" |
Reply to a message |
napcat recall <msg_id> |
Recall a message |
napcat group <sub> |
Group list, members, settings |
napcat friend <sub> |
Friend list, info |
napcat file <sub> |
File upload, download, list |
napcat events |
Read events from SQLite |
napcat alerts [--clear] |
Pending alerts |
napcat status |
Bot online status |
napcat config get/set |
Configuration management |
napcat daemon start/stop/status/restart |
Watch daemon |
napcat fs tree |
Skills-fs directory tree |
napcat wake [--reason R] [--prompt P] [--transport T] [--dry-run] |
Wake the configured agent (HTTP/CLI, auto-fallback) |
napcat setup |
Interactive setup wizard |
napcat phone |
Textual phone-style TUI |
Setup
uv tool install napcat-cli # recommended — isolated install via uv (alternative: pip install napcat-cli)
napcat setup # interactive — guides token, data dir, skills-fs, wake
napcat daemon start
The setup wizard writes two config files:
<data_dir>/config.json— API URL, token, self_id, ports, triggers<data_dir>/daemon.json— All fields consumed bywatch.pyincludingskills_fs_*settings
Non-interactive mode uses defaults:
napcat setup --non-interactive # no prompts, validates token
napcat setup --yes # skip token validation
napcat setup --force # overwrite existing config
Agent Wake
When a notable QQ event arrives, the daemon wakes an external agent (Hermes by default) carrying a contextual prompt so it can read the inbox and reply. The wake mechanism is generic and pluggable — Hermes is just the default preset; any HTTP endpoint or shell command works.
How it works
QQ Event (WS) --> EventProcessor --> WakeOrchestrator (debounce/cooldown/queue)
|
Waker (auto-fallback)
HTTP API (RECOMMENDED) CLI (LEGACY — 不推荐)
POST /chat hermes -z "..."
fixed session_id --continue <session>
- Debounce (
wake_debounce_seconds, default 3): coalesces a burst of same-reason events into one wake. - Cooldown (
wake_cooldown_seconds, default 30): suppresses repeat wakes for non-urgent reasons. - Serial queue:
WakeOrchestratorhas a single worker thread — wakes to the same session are processed one at a time, preventing concurrent "split personality" responses. - Timeout:
wake_timeout(default 300s) — how long to wait for the agent to respond.
Every wake is logged to daemon.log:
[WAKE] trigger reason=AT_ME who=Alice where=group123 text='hello'
[WAKE] queued reason=AT_ME pending=1 debounce=1.0s primary=auto
[WAKE] delivered reason=AT_ME transport=http detail=POST /api/sessions/.../chat -> 200
Event routing
| Trigger | Behavior |
|---|---|
AT_ME, REPLY_TO_ME, DM_ME |
Near-immediate wake (cooldown bypassed). Prompt includes who/where/text, image metadata, reply chain, and skill hints. |
GROUP_TRIGGER |
Debounced wake (group trigger-word match) |
NEW_MESSAGE (not @) |
Tracked, not woken; if unread longer than wake_new_message_idle_seconds -> NEW_MESSAGE_BACKLOG wake |
NEW_FRIEND, NEW_REQUEST, BOT_BANNED, NEW_POKE, GROUP_ADMIN_CHANGE, NEW_GROUP_MEMBER, BOT_OFFLINE, ... |
Debounced + cooldown-bounded wake |
AT_ME detection supports all NapCat message formats: CQ code ([CQ:at,qq=...]), display name (@name (qq)), and message segments ({"type":"at","data":{"qq":"..."}}).
Two transports — HTTP recommended, CLI legacy
⚠️ Wake transport recommendation. The HTTP API server is the recommended transport for agent wake. The CLI one-shot backend is kept as a legacy fallback for environments where running the API server isn't viable — it is known to be less reliable (process-spawn latency, quoting hazards, new-session drift, no idempotency). When
wake_primary=autofalls back to CLI because HTTP is unreachable, the wake may appear to succeed but never deliver the prompt (only HTTP returns a realdeliveredindaemon.log). Enable the HTTP API server first — only keep CLI for diagnostic--dry-runor as last-resort fallback. SeeAGENTS.md"Critical Lessons —hermes -z -" for the most common CLI failure mode.
| Transport | Status | When it picks | Needs |
|---|---|---|---|
| HTTP API server | RECOMMENDED | primary=http or primary=auto when configured + reachable |
wake_http_url + wake_http_key (bearer) |
| CLI one-shot | LEGACY / 不推荐 | primary=cli (forced) or primary=auto only if HTTP is not configured/unreachable as a last-resort fallback |
Agent CLI on PATH |
wake_primary=auto tries HTTP first (if configured + reachable), else falls back to CLI.
Configuring for Hermes Agent
Option A: HTTP API Server (recommended)
The HTTP API server is the recommended wake transport. It provides:
- Session continuity: all wakes go to the same session (no "split personality")
- Idempotency:
Idempotency-Keyheader prevents duplicate processing on retry - Persistence: session history is stored in
~/.hermes/state.db - Lower latency: no CLI process spawn overhead
Step 1: Enable Hermes API Server
Add to ~/.hermes/.env:
API_SERVER_ENABLED=true
API_SERVER_KEY=$(openssl rand -hex 32) # generate a random key
Restart the gateway:
sudo systemctl restart hermes-gateway.service
# Verify API server is listening:
curl http://127.0.0.1:8642/health
Step 2: Create a dedicated QQ session
curl -X POST http://127.0.0.1:8642/api/sessions \
-H "Authorization: Bearer <API_SERVER_KEY>" \
-H "Content-Type: application/json" \
-d '{"title": "napcat-qq"}'
# Response: {"session": {"id": "api_1784824379_59dd9495", ...}}
Step 3: Configure napcat-cli
napcat config set wake_enabled true
napcat config set wake_preset hermes
napcat config set wake_primary auto # try HTTP first, CLI fallback
napcat config set wake_http_url http://127.0.0.1:8642
napcat config set wake_http_key <API_SERVER_KEY>
napcat config set wake_http_session_id <SESSION_ID> # from Step 2
napcat config set wake_session napcat-qq # CLI fallback session name
napcat config set wake_timeout 300 # seconds
Or edit ~/.napcat-data/daemon.json directly:
{
"wake_enabled": true,
"wake_preset": "hermes",
"wake_primary": "auto",
"wake_http_url": "http://127.0.0.1:8642",
"wake_http_key": "<API_SERVER_KEY>",
"wake_http_session_id": "<SESSION_ID>",
"wake_timeout": 300.0,
"wake_cli_command": "hermes --continue {session} -z \"$(cat {prompt_file})\" --yolo --pass-session-id"
}
Key env vars (also checked by Hermes preset, in priority order):
NAPCAT_WAKE_HTTP_KEY— HTTP API bearer tokenHERMES_API_KEY— alias for the above
Option B: CLI one-shot — LEGACY / 不推荐
⚠️ Not recommended. Prefer Option A (HTTP). The CLI backend is kept for environments where running the API server isn't viable, but it has multiple known failure modes (process-spawn latency, quoting hazards, no idempotency, new-session drift). When
wake_primary=autofalls back to CLI after an HTTP probe fails, the wake often appears to succeed but never delivers the prompt to the agent. Diagnose by runninggrep '\[WAKE\]' ~/.napcat-data/daemon.log— only the HTTP backend writes adelivered ... transport=http -> 200line. For agents other than HTTP, configure Option A first; only fall back to CLI when there is no other choice.
If you don't want to run the API server, napcat-cli can invoke the Hermes CLI
directly. The CLI backend writes the prompt to a temp file and passes it via
$(cat {prompt_file}) to avoid shell quoting issues.
napcat config set wake_primary cli
napcat config set wake_session napcat-qq
napcat config set wake_cli_command 'hermes --continue {session} -z "$(cat {prompt_file})" --yolo --pass-session-id'
Limitations of CLI mode:
- Each invocation may create a new session if the name doesn't match an existing one
- No idempotency — retries may cause duplicate processing
- ~30-60s per invocation (CLI process startup + model inference)
- Large prompts with special characters require the
$(cat {prompt_file})pattern (not-z -, which Hermes treats as literal text "-")
Configuring for other agents
napcat-cli's wake system is agent-agnostic. Any HTTP endpoint or shell command works.
Custom HTTP endpoint
napcat config set wake_preset custom
napcat config set wake_primary http
napcat config set wake_http_url http://127.0.0.1:9000
napcat config set wake_http_key my-secret-key
# The HTTP backend POSTs to: {wake_http_url}/api/sessions/{session}/chat
# Body: {"input": "<prompt>"}
# Headers: Authorization: Bearer <key>, Idempotency-Key: <unique-id>
# If your endpoint uses a different path/body format, see wake_backend.py HttpWakeBackend
Custom CLI command — LEGACY / 不推荐
⚠️ Not recommended. The shell-command backend is kept for legacy / experimental setups only. Prefer Custom HTTP endpoint above — see the "Two transports — HTTP recommended, CLI legacy" note at the top of this section for why.
napcat config set wake_preset custom
napcat config set wake_primary cli
napcat config set wake_cli_command 'my-agent --prompt "$(cat {prompt_file})"'
# Placeholders: {prompt_file} (temp file path), {session}, {prompt} (inline, shell-quoted)
Disable wake entirely
napcat config set wake_preset none
Manual / debug
napcat wake # reason: manual, contextual default prompt
napcat wake --reason AT_ME --prompt "hello"
napcat wake --transport http # recommended; --transport cli is legacy
napcat wake --dry-run # render the HTTP request + CLI command without executing
napcat wake test # per-transport configured + reachable probe
napcat wake sessions # list Hermes sessions (HTTP backend)
grep '\[WAKE\]' ~/.napcat-data/daemon.log # see when/why/how wakes fired
The agent replies via napcat send / napcat reply (or the skills-fs write
path) — see napcat_cli/data/SKILL.md.
Troubleshooting & verification
Verify the full wake chain end-to-end
-
Check daemon is running:
ps aux | grep watch.py | grep -v grep tail -5 ~/.napcat-data/daemon.log # should show recent events
-
Check Hermes API server is listening:
curl -s http://127.0.0.1:8642/health # Expected: {"status": "ok", ...}
-
Test HTTP wake manually:
curl -X POST http://127.0.0.1:8642/api/sessions/<SESSION_ID>/chat \ -H "Authorization: Bearer <KEY>" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: test-001" \ -d '{"input": "ping"}' # Expected: {"message": {"content": "..."}} with a response
-
Test CLI wake manually (legacy — prefer HTTP test above):
⚠️ CLI is legacy / not recommended. This reference is kept for diagnosing environments where the HTTP API server is genuinely unavailable. Only test CLI as a last resort — the HTTP test (step 3) is the one that matters.
echo "hello" > /tmp/prompt.txt hermes --continue napcat-qq -z "$(cat /tmp/prompt.txt)" --yolo --pass-session-id # Expected: agent responds within 30-60s # NOTE: -z - does NOT read stdin. Always use -z "$(cat file)" or -z "literal".
-
Send a test @mention to the bot in a group, then check logs:
grep '\[WAKE\]' ~/.napcat-data/daemon.log | tail -10 # Should show: trigger -> queued -> delivered (or failed)
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Bot doesn't respond to @mentions | Daemon not running, or crash killed event loop | ps aux | grep watch.py; check daemon.log for errors; restart daemon |
[WAKE] delivered but no reply |
Agent received prompt but didn't act | Check Hermes session via hermes sessions list; verify agent has napcat skills/tools |
[WAKE] failed ... timeout |
Agent took > wake_timeout seconds |
Increase wake_timeout in daemon.json |
[WAKE] failed ... session resolution failed |
HTTP session ID not found | Run napcat wake sessions to list; recreate if needed |
| Multiple replies to one event ("split personality") | Concurrent wakes to same session | Set wake_primary=http (or ensure HTTP probe passes); the worker thread serializes wakes through HTTP's fixed session, but CLI one-shot can still create drift |
hermes -z - sends literal "-" |
-z takes literal string, not stdin |
Use -z "$(cat {prompt_file})" template |
| Events stop after a bad message | Unhandled exception in event handler | Check daemon.log for process() ERROR; the handler now has try/except |
Static analysis (prevents regressions)
The test suite includes tests/test_lint.py which runs pyflakes and
py_compile on every file in napcat_cli/. This catches undefined names,
syntax errors, and other issues that would crash the daemon at runtime.
python -m pytest tests/test_lint.py -v
# Should pass: test_no_undefined_names, test_all_modules_compile
Image & OCR
The wake prompt automatically includes image metadata when an image message is received:
file_id— image file IDurl— download URLsub_type— image type (0=normal, 1=sticker, 7=like)file_size— file size in bytessummary— PaddleOCR auto-recognized text (if available)reply_id— reply message ID (for tracking reply chains)
PaddleOCR is integrated. Image text is auto-recognized and included in the
summary field. The agent can also use multimodal vision to read the image URL
directly. NapCat's built-in OCR is unavailable — do not use /napcat/ocr.
| Operation | CLI | skills-fs |
|---|---|---|
| Download image | napcat get_image <url> |
/napcat/get_image |
| View message detail | napcat group <gid> get_message <mid> |
/napcat/groups/:gid/:range/:mid/:content |
| Send image | napcat send group <gid> -f <path> |
/napcat/groups/:gid/send/image |
Environment Variables
| Variable | Default | Description |
|---|---|---|
NAPCAT_API_URL |
http://127.0.0.1:18801 |
NapCat HTTP endpoint |
NAPCAT_TOKEN |
— | API auth token |
NAPCAT_DATA_DIR |
~/.napcat-data |
Data directory |
skills-fs Integration
The daemon runs an HTTP provider server (port 18820/18821) that skills-fs calls to read/write NapCat API endpoints through the virtual filesystem.
When skills_fs_enabled is true in daemon.json, the daemon spawns skills-fs
automatically with the configured mountpoint and config file.
Distribution note:
skills-fsis a compiled Go binary (git submodule pinned atcb13f37). It is not bundled in the pip/uv install ofnapcat-cli. If you need the FUSE mount (optional), build it separately:git clone https://github.com/yandu-app/skills-fs cd skills-fs && make build # produces ./skills-fs napcat config set skills_fs_binary /path/to/skills-fs napcat daemon stop && napcat daemon startOr place
skills-fson yourPATHand the daemon will find it. This feature is optional — the CLI, daemon, and wake system work without it.
Manual start:
skills-fs fuse --config ~/.napcat-data/skills-fs.json \
--mountpoint ~/.napcat-data/skills/napcat-cli --allow-other
Development
napcat-cli/
├── napcat_cli/ # Installable package
│ ├── cli.py # CLI entry point
│ ├── wake.py # Wake command-template renderer
│ ├── wake_backend.py # Generic HTTP/CLI wake transports + Waker (auto-fallback)
│ ├── wake_presets.py # Hermes/custom/none presets -> Waker
│ ├── wake_orchestrator.py # Debounce, cooldown, backlog sweep, contextual prompts
│ ├── setup_wizard.py # Setup wizard
│ ├── daemon/ # Watch daemon, schemas
│ ├── lib/ # API, config, events
│ ├── tui/ # Textual TUI
│ └── data/ # SKILL.md, persona.md
├── pyproject.toml
├── tests/
├── skills-fs/ # Go submodule
└── tools/ # Dev utilities
python -m pytest tests/ -v
python -m build --wheel
uv build && uv publish
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file napcat_cli-2.2.0.tar.gz.
File metadata
- Download URL: napcat_cli-2.2.0.tar.gz
- Upload date:
- Size: 144.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45d21d7ab171687655f4088c7c03cb1724ab94f7dfb5ff44f9fd2d1e502106ec
|
|
| MD5 |
61a43aa0439cb3661b18286147aaba33
|
|
| BLAKE2b-256 |
7113bf533ad78f3de754fa45c9bd5b417588a6dc443e49e50bd647012afe3852
|
File details
Details for the file napcat_cli-2.2.0-py3-none-any.whl.
File metadata
- Download URL: napcat_cli-2.2.0-py3-none-any.whl
- Upload date:
- Size: 123.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a0bffa21fc6c06c0c3aa60503e5258dd265af58adc7295e84d37fac37e59b32
|
|
| MD5 |
cb3462d88927a2d8ce26b56ed3a28d0e
|
|
| BLAKE2b-256 |
fcbf1ccb496ad20bb6e31434ed6fc10fc5ce60bd1a43809372c2d42a9ce12aad
|