Skip to main content

Slife

Terminal-based AI agent — a function-calling loop with minimum harness. Chat with an LLM that calls tools, remembers every turn, and orchestrates other agents.

You: "Find all TODO comments and create GitHub issues"
  → LLM calls search_content("TODO")
  → LLM calls github__create_issue(...) for each one
  → LLM: "Created 7 issues. All linked above."

One TUI window around an LLM tool loop: up to 50 native tools in 14 categories (plus 2 harness tools), six built-in plugin services, always-on memory with hybrid search, vision image attachments (@path/@url), runtime model switching across three API backends, and an agent-to-agent mesh — everything presented to the LLM as uniform OpenAI-style function definitions.

Requires Python 3.13+. Runs on Windows (native & WSL), macOS, and Linux.

Install

Zero prerequisites. The install script auto-installs uv, Node.js, and bun if needed. On WSL, Linux-native versions are installed (Windows executables cannot receive custom env vars via WSL interop). Mosquitto (only needed for the A2A MQTT mesh) is offered interactively.

macOS / Linux / WSL

# Global
curl -fsSL https://raw.githubusercontent.com/juzcn/slife/main/install.sh | bash
# China mainland
curl -fsSL https://gitee.com/juzcn/slife/raw/main/install.sh | bash

Windows PowerShell

# Global
powershell -ExecutionPolicy Bypass -Command "irm https://raw.githubusercontent.com/juzcn/slife/main/install.ps1 | iex"
# China mainland
powershell -ExecutionPolicy Bypass -Command "irm https://gitee.com/juzcn/slife/raw/main/install.ps1 | iex"

Try without installing

uvx --from git+https://github.com/juzcn/slife.git slife

Update

Re-run the install script — it auto-preserves optional packages (llama-cpp-python, sentence-transformers) by diffing the previous venv and re-adding them.

Uninstall

# macOS / Linux / WSL
curl -fsSL https://raw.githubusercontent.com/juzcn/slife/main/uninstall.sh | bash
# China mainland
curl -fsSL https://gitee.com/juzcn/slife/raw/main/uninstall.sh | bash

# Windows PowerShell
powershell -ExecutionPolicy Bypass -Command "irm https://raw.githubusercontent.com/juzcn/slife/main/uninstall.ps1 | iex"
# China mainland
powershell -ExecutionPolicy Bypass -Command "irm https://gitee.com/juzcn/slife/raw/main/uninstall.ps1 | iex"

User data (~/.slife/, ~/.credstore/) is not removed — delete manually for a full reset.

Related tools

The repo also ships two standalone PyPI packages — install each independently (neither pulls the others in):

Package One-click install Purpose
slife uv tool install slife The agent (this README)
credstore uv tool install credstore Cross-platform credential storage
cc-switch uv tool install cc-switch Generate ~/.claude/settings.json

Installing slife depends on credstore only — it does not install cc-switch. See the cc-switch and credstore READMEs for details.

Quick Start

credstore set-password              # first time — encrypted backup
credstore set DEEPSEEK_API_KEY      # store API key (masked input)
slife

To share the same API key across multiple providers:

credstore copy DEEPSEEK_API_KEY BAILIAN_API_KEY

Configuration

Secrets in the OS keyring, config in JSON5:

Layer Storage Contents
Secrets OS keyring (credstore) API keys — encrypted at OS level, plus an encrypted cryptfile backup
Config ~/.slife/slife.json5 ${VAR} references + non-secret values
env: {
  DEEPSEEK_API_KEY: "${DEEPSEEK_API_KEY}",   // → resolved from keyring at runtime
}

models: {
  providers: {
    deepseek: {
      base_url: "https://api.deepseek.com",
      api_key: "${DEEPSEEK_API_KEY}",
      api: "openai-completions",
      models: [{ model: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true }],
    },
  },
},
active_model: "deepseek/deepseek-v4-pro",

${VAR:-default} fallback syntax is supported. Secrets can also be referenced as keyring:service/key URIs.

Three first-class API backends:

api field Backend Providers
openai-completions OpenAI / DeepSeek / Ollama / MiniMax Chat Completions
anthropic-messages Claude / Bailian (Qwen) Messages
openai-responses OpenAI Responses

Per-model compat overrides (configured in the model entry, or via model_set):

models: {
  providers: {
    bailian: {
      api: "anthropic-messages",
      models: [{
        model: "qwen3.8-max", name: "Qwen3.8 Max",
        reasoning: true,
        compat: { thinkingFormat: "openai" },  // anthropic backend: model always thinks, no thinking param
      }],
    },
    scnet: {
      api: "openai-completions",
      models: [{
        model: "MiniMax-M3", name: "MiniMax M3",
        reasoning: true,
        compat: { thinking: "omit" },          // openai backend: send NO thinking field (gateway 400s on enabled)
      }],
    },
  },
},

compat.thinking on the OpenAI backend: "omit" sends no thinking field (for gateways that reject the {"type": "enabled"} shape but reason natively), "disabled" forces explicit off, "enabled" matches the default.

Switch at runtime: model_listmodel_switch(ref="bailian/qwen3.8-max").

Secrets never reach the LLM. User input, tool-call arguments, and every tool result pass through a pattern-based sanitizer before entering the conversation — API key shapes (sk-*, ghp_*, Bearer tokens, …) are auto-masked.

Features

Tools

All unified as OpenAI function definitions. The LLM sees no difference between native, plugin, and external MCP tools.

52 native tools in 14 categories — auto-discovered from slife/tools/ (up to 50 LLM-visible + 2 harness; include_image is dropped when the active model has no vision, and install_python_package is disabled by default in the shipped config):

Category Tools
System system_health, check_memdb, check_wechat, check_memfiles, check_mcp, check_a2a, check_watchdog
Execution execute_shell, run_python_script, install_python_package
Skills skill_list, skill_use, skill_set, skill_remove, skill_set_enabled
CLI cli_list, cli_set, cli_remove, cli_set_enabled
REST API rest_api_list, rest_api_set, rest_api_remove, rest_api_set_enabled
A2A a2a_send_task, a2a_send_task_async, a2a_get_task_result, a2a_cancel_task, a2a_list_agents, a2a_list_tasks, a2a_agent_card, a2a_broadcast
Subagent spawn_subagent, list_subagents, stop_subagent, subagent_send_task, subagent_send_task_async, subagent_get_task_result, subagent_list_tasks, subagent_cancel_task
Config config_env_set, config_env_get, config_env_remove, native_tool_set
Models model_list, model_set, model_remove, model_switch
Credentials credential_check, credential_inject, credential_uninject
Vision include_image (injects a local image or URL into the conversation)
Display notify_user
Harness _sys_note (context status) — auto-invoked, not for LLM use
Meta list_tools, check_async, cancel_async, clear_context, set_max_iterations

Every tool additionally accepts three harness meta-parameters: _timeout (per-call override), _async (run in background, poll with check_async), and _approve (inline approval prompt in the chat — Y approve / N deny / Esc deny).

Harness tools come in two tiers. _-prefixed native tools (_sys_note) are LLM-visible but reserved: the agent loop auto-invokes the note each turn to report context status (usage %, time range); it is schema-declared (so the Anthropic / OpenAI-Responses backends accept its call pair) but the system prompt forbids the LLM from calling it. Context trimming no longer happens through a tool — it runs internally after each turn is persisted, marking the cut with a runtime-only [TrimContext: N] note on the last assistant message (see Memory — Always On). __-prefixed plugin tools (__memory_save_turn, __mcp_call_tool, …) are LLM-invisible — filtered out of the schema entirely and called programmatically via client.call_tool().

Five managed categories (Skills / CLI / REST API / Models / MCP) support X_list / X_set / X_remove (+ X_set_enabled where a toggle applies) — all X_set tools are idempotent upserts. model_set upserts merge into the existing entry (a partial update preserves reasoning / input / compat), and accepts a compat dict for per-model provider overrides.

Plugin tools — registered at runtime as {server}__{tool} proxies:

Server LLM-visible tools
mcp mcp_set, mcp_set_enabled, mcp_remove, mcp_list, mcp_list_tools
memdb memdb__memory_list_turns, memdb__memory_search, memdb__memory_open, memdb__memory_turn_summarize, memdb__memory_count, memdb__memory_token_usage, memdb__memory_check_embedding, memdb__memory_set_embedding, memdb__memory_set_enabled
wechat wechat_login, wechat_send_message, wechat_send_typing, wechat_check_messages, wechat_check_status, wechat_logout
memfiles memfiles__note_save, memfiles__diary_write, memfiles__file_save, memfiles__url_save, memfiles__note_list, memfiles__diary_list, memfiles__note_read, memfiles__diary_read, memfiles__list_files, memfiles__search, memfiles__read, memfiles__embedding_check, memfiles__expose_file

Built-in plugin tools that already carry their server as a name prefix (mcp_set, wechat_login) are registered as-is; the rest are namespaced {server}__{tool}. External MCP servers configured in slife.json5mcp.servers always appear as {server}__{tool} (e.g. filesystem__read_file).

Windows execution. execute_shell runs in the detected shell — PowerShell or cmd (the same value the system prompt reports, so the LLM's syntax actually executes) — and its output is decoded with the system code page (GBK/cp936 on Chinese Windows). run_python_script forces the child Python to UTF-8 (-X utf8) so non-ASCII output can't crash the child.

Memory — Always On

Every conversation turn is permanently recorded in SQLite (~/.slife/<agent>.db). Hybrid search across four modes:

Memory is a core feature — the agent never runs silently without it. If the memory DB is broken (missing column, corruption, disk error), the agent fails loudly instead of pretending: a session that can't restore aborts at startup with the error; a turn that can't be saved freezes the inbox and shows a red banner — new turns stop until the DB is fixed and the agent is restarted. A memdb plugin that fails to load likewise aborts startup.

Mode Best for
grep Exact strings — error messages, file paths, code
fts5 Topic / keyword search with ranked snippets
hybrid Semantic recall (FTS5 + vector → RRF merge)
time Browse by date

Embedding backends: local GGUF (BGE-M3, offline), HuggingFace transformers, or OpenAI-compatible API. Keyword search works without any embedding backend. Semantic (hybrid) results are only served once the index is fully built for the current model — while a full reindex runs (new/changed model, restart mid-index), hybrid degrades to keyword-only and resumes automatically when indexing finishes. A single SemanticManager owns the lifecycle (search gate, embedder, background index drainer): memory_set_embedding / memory_set_enabled(true) block until the model loads and the index starts building, and memory_check_embedding reports the live state (disabled / loading / indexing / ready / stalled) alongside the gate.

Each turn also records two timestamps — the user's input time (created_at, the Enter-press moment) and the assistant's completion time (completed_at) — shown as dim [HH:MM] markers in the chat (user messages and assistant responses respectively). Databases created before completed_at are migrated once with python scripts/migrate_memdb_completed_at.py (no in-plugin ALTER); fresh databases get the column automatically. Image attachments (images) use the same standalone-script pattern — python scripts/migrate_memdb_images.py for pre-existing databases. Token usage is recorded in two columns — token_count (the turn's cumulative billed tokens) and prompt_tokens (the context size at the last API call, used at restore to prime the status footer with the real exit-time occupancy) — migrated with python scripts/migrate_memdb_prompt_tokens.py.

User messages carry a compact [Turn: N · start → end] footnote (the memory rowid plus when the turn happened), concatenated into the message text — so the LLM can tell turns apart and reference them by rowid (memdb__memory_open / memdb__memory_turn_summarize), and the human reads the same line in the TUI. Restored turns get it at session restore; a just-completed live turn gets it as soon as its save returns the rowid (so the next call can reference it precisely); the current in-flight turn has none. Machine annotations share one [Kind: …] shape — [Heartbeat] (a synthetic autonomous trigger, not a user query) and [TrimContext: N] on an assistant message (a context compaction to the floor: N oldest complete turns were cut after the latest turn was saved, so tool results / intermediate reasoning from that window may no longer be in context; the turns remain searchable in memory). The trim marker is runtime-only — it appears only in the current session and never in restored history (a restored session is already the trimmed state).

Autonomous Heartbeat

While idle, the agent gets a periodic autonomous window (every agent.heartbeat_interval seconds, default 60) to think or act on its own. It runs as a normal turn (own conversation, saved to memory); the reply contract is real content if it has something worth saying, otherwise a single .. A bare . reply is silence — never rendered in the chat or session restore, from any event (heartbeat, A2A async-completion notification, etc.); the [Heartbeat] trigger is filtered, and a real autonomous reply renders as ⚡ 自主. A precondition for emergent self-initiated behavior.

Image & Vision

Attach images with @path / @url syntax (quotes supported for paths with spaces) to feed them to a vision-capable model:

Check this screenshot @D:\Downloads\error.png

Vision-capable models receive local files as base64 data URIs and HTTP(S) URLs as-is; the include_image tool lets the agent attach images mid-conversation. Nothing is ever rendered in the terminal — files open with the OS default app, and memfiles__expose_file publishes any local file as a public HTTPS link via the ngrok tunnel (returns a graceful error while the tunnel is offline).

Plugins

Six built-in plugins as independent child processes:

Plugin Role
slife-mcp Gateway for external MCP servers (stdio / SSE / Streamable HTTP)
slife-memdb Diary database with hybrid search
slife-wechat Bidirectional WeChat messaging
slife-memfiles Notes / diary / files cabinet + public sharing (Streamable HTTP, /share route on the same port; ngrok tunnel owned by the plugin). Notes & diary dual-written to markdown + a SQLite hybrid index
slife-a2a A2A mesh channel over MQTT (only starts when the broker is reachable)
slife-media Non-chat AI generation (image, video, TTS, ASR) from any provider — owns the media: config section and a provider-agnostic adapter layer (dashscope-aigc, openai-images). Tools: generate_image, generate_video, text_to_speech, transcribe_audio (media__*)

External MCP servers configured in slife.json5mcp.servers — any stdio, SSE, or Streamable HTTP MCP server works, no Slife SDK required. For url-configured servers, SSE is auto-detected and Streamable HTTP is the fallback; a Streamable response may arrive as a single JSON body or an SSE stream (both handled).

All plugins — built-in and auto-discovered third-party alike — run with a watchdog that auto-restarts them on crash (exponential backoff 1s→30s, max 5 restarts). The MCP wrapper watchdog also reconnects external servers after restart. Runtime health checks — check_memdb, check_wechat, check_memfiles, check_mcp, check_a2a, check_watchdog — monitor application-level state and are surfaced via system_health; the watchdog is purely process-level.

A2A — Agent-to-Agent (mesh)

The A2A protocol (JSON-RPC operations and Message/Task/AgentCard data shapes mirroring the official a2a-python reference interface) runs over a pluggable transport binding — currently MQTT. The a2a plugin hosts the LLM-visible tools and the A2AClient, and only starts when the broker is reachable:

  • Mesh tools (one uniform a2a_ prefix): a2a_send_task, a2a_send_task_async, a2a_get_task_result, a2a_cancel_task, a2a_list_agents, a2a_list_tasks, a2a_agent_card, a2a_broadcast.
  • Local workers are NOT A2A: spawn_subagent, list_subagents, stop_subagent, subagent_send_task, subagent_send_task_async, subagent_get_task_result, subagent_list_tasks, subagent_cancel_task. A worker runs one task at a time; a sync send to a busy worker is auto-queued as async (task_id returned) and reported.

A2A's only implemented transport binding is MQTT — setting transport to any other value disables A2A with a warning instead of crashing startup. All messages — human, WeChat, MQTT, subagent results — flow through a single inbox queue and are processed one turn at a time.

Keyboard Shortcuts

Key Action
Ctrl+C Quit
Esc Cancel agent loop
Ctrl+S Switch model (inline picker — type a number, Esc cancels)
Home / End Scroll to top / bottom
Ctrl+Y Copy result (on a tool call)
Enter / Space Toggle thinking block (on an assistant message)

CLI

Flag Description
--agent <id> Agent identity — separate diary database + A2A mesh name (default: slife)

Optional Extras

Extra Enables
slife[gguf] Local GGUF embeddings via llama-cpp-python (offline, ~300 MB)
slife[transformer] HuggingFace transformer embeddings via sentence-transformers (~2 GB)
slife[embeddings] Both of the above

Linux / macOS — builds from source:

uv tool install "slife[gguf]" --reinstall

Windows — pre-built wheels (no C++ compiler needed); uv is configured to use the llama-cpp-python CPU wheel index. See install docs for wheel selection and first-use instructions.

Development

git clone https://github.com/juzcn/slife.git
cd slife
uv sync --all-extras

uv run credstore set-password
uv run credstore set DEEPSEEK_API_KEY
uv run slife

# Tests
uv run pytest
uv run pytest --cov --cov-report=term-missing

Dev mode auto-detects when you run from the source tree: data files stay in the project directory. Production installs (uv tool / pipx / pip) always use ~/.slife/ — even when launched from inside a checkout or from the home directory. CI runs the test suite on Ubuntu, macOS, and Windows with Python 3.13.

Architecture

See DESIGN.md — philosophy, agent loop, tool system, plugin contract, MCP gateway, memory database, A2A mesh, credential security model, and full project structure.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

slife-0.9.6.tar.gz (417.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

slife-0.9.6-py3-none-any.whl (448.1 kB view details)

Uploaded Python 3

File details

Details for the file slife-0.9.6.tar.gz.

File metadata

  • Download URL: slife-0.9.6.tar.gz
  • Upload date:
  • Size: 417.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.15

File hashes

Hashes for slife-0.9.6.tar.gz
Algorithm Hash digest
SHA256 b7508fc5d1fdb6db8059a1cfa99c89539309964f4dafcf8597e98721e8921940
MD5 1317b5ffab1cbb894f4873c67e812a50
BLAKE2b-256 1388f496a18cdd62d4883e53eb2efd3183c6367b46b9c9fe2bfec69b8ab9722f

See more details on using hashes here.

File details

Details for the file slife-0.9.6-py3-none-any.whl.

File metadata

  • Download URL: slife-0.9.6-py3-none-any.whl
  • Upload date:
  • Size: 448.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.15

File hashes

Hashes for slife-0.9.6-py3-none-any.whl
Algorithm Hash digest
SHA256 b6a0cfc29bf6143b1e5081d2f52223d1f6610a5b439cc8e896a1d451317c2f42
MD5 3eda8bb8b82bd4975491c84af0797285
BLAKE2b-256 98da50f2341038c3f3cc9c13fa971ab8fc44b7abaf50536f893a23bf16dec91b

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.9.6 This release

2 files

0.9.5

2 files

0.9.4

2 files

0.9.0

2 files

0.3.24

2 files

0.3.23

2 files

0.3.22

2 files

0.3.21

2 files

0.3.20

2 files

0.3.19

2 files

0.3.18

2 files

0.3.17

2 files

0.3.16

2 files

0.3.15

2 files

0.3.14

2 files

0.3.13

1 file

0.3.12

2 files

0.3.11

2 files

0.3.10

2 files

0.3.9

2 files

0.3.8

2 files

0.3.7

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page