std::slop is a persistent, SQLite-driven C++ CLI agent built around a JavaScript control plane based on QuickJS-ng. Its primary way to inspect repositories, compose tool calls, validate results, and make changes is run_js: a synchronous JavaScript execution environment with safe tools.* helpers for file edits, shell commands, database queries, scratchpad updates, and higher-level workflow orchestration. That programmable control plane is what makes std::slop different from agents that only issue one tool call at a time: repeated operations can become deterministic scripts, scripts can be validated, and successful patterns can be promoted into reusable helpers.
The agent can also learn from its own workflows. The self_improvement_learner skill can be invoked directly with requests such as "hey self_improvement_learner learn from session history"; it identifies repeated run_js orchestration patterns and promotes them into persisted JavaScript functions through tools.persist_function(args). Persisted helpers are validated before storage, discovered through tools.help() and /tools js_help, and loaded automatically in later run_js invocations. This lets std::slop simplify future tool calling: a verbose sequence of reads, edits, shell validation, or JSON reshaping can become a single reusable function such as runCommandSummary(...).
- π§
run_jscontrol plane: std::slop primarily operates throughrun_js, a programmable JavaScript layer that batches local tool calls, loops over structured data, validates intermediate state, and returns compact JSON results. - π Personas & Skills: Define global agent instructions via
AGENTS.mdand extend capabilities using modular, on-demandSKILL.mdfiles. - π± Self-improving helpers: The
self_improvement_learnerskill can turn repeatedrun_jsworkflows into persisted functions viatools.persist_function(args), reducing complex tool orchestration to small reusable helpers. - π§ Dynamic workflow harnesses: The
dynamic_workflow_harnessskill helps agents choose boundedrun_jspatterns for repository surveys, fan-out analysis, evaluator loops, proposal tournaments, external content review, and validation guards. - π Ledger-Driven: All interactions and tool calls are stored in SQLite for persistence and auditability.
- π Session Scratchpad: Maintain a per-session planning buffer with
/scratchpad edit,/scratchpad save, andread_scratchpad/write_scratchpadtools. - ποΈ Context Control: Granular control over conversation history via SQL-backed retrieval and rolling windows. As the context is built per-session, you can create multiple sessions and even clone existing ones to go down different paths.
- π¬ Mail workflows: Use docs/mail_mode.md for the manual patch-based workflow or docs/mail-loop/README.md for the automated mail-loop orchestrator.
- π€ Multi-Model: Supports Google Gemini and OpenAI-compatible APIs (OpenRouter, etc.) and OpenAI Responses API (with chatgpt plus/pro oauth).
- π£ Hotwords: Quick, single-turn skill activation using
hey <skill> <query>syntax. Eg: "hey code_reviewer review these patches".
The project ships Linux x86-64 and macOS binaries every release. You can directly use them.
- C++17 compiler (Clang/GCC)
- Bazel (Bazelisk recommended)
- Git: Targets must be valid git repositories. Usually, a git add and an initial commit is sufficient to trigger all the git enabled features.
# Build the binary
bazel build //:std_slop
# Optional: Add to your PATH
cp ./bazel-bin/app/std_slop /usr/local/bin/std::slop works best when it can track a specific project. Initialize a git repository and run it from the root:
mkdir my-project && cd my-project
git init
std_slopFor quick one-off tasks, you can use Batch Mode:
std_slop --prompt "Refactor main.cpp to remove all unused includes" Batch mode accepts exactly one instruction source, and optional piped stdin is prepended as context:
std_slop --prompt-file task.md
ls *.cc | std_slop --prompt "sort these files in alphabetical order"
cat errors.log | std_slop --prompt-file diagnose.mdUse exactly one instruction source: --prompt or --prompt-file. Empty instructions are rejected. Empty or whitespace-only piped stdin is ignored.
For scripts, batch mode can emit one structured JSON object to stdout:
std_slop --prompt-file task.md --output json | jq -r .assistant_messageThe JSON object contains ok, session, model, active_skills, assistant_message, error, and duration_ms.
Batch mode also takes in --model which is useful to specify the model to use and --session which is useful to indicate the session the prompt should be executed under. Batch mode works off an in memory sqlite db. If you want the db persisted you can point it to a DB with the --prompt-db argument.
/commands are also supported.
Read the Walkthrough first for the recommended getting-started flow, authentication setup paths, config.ini setup, docs-folder navigation, and llm_query subquery/persona configuration. Then use docs/README.md as the docs index for deeper reference material.
- Gemini: set
GOOGLE_API_KEYor put it in~/.config/slop/config.ini - OpenAI-compatible API key: set
OPENAI_API_KEY, optionally combine with--openai_base_url, or put both inconfig.ini - OpenAI OAuth (Responses API): run
std_slop --fetch_openai_oauth_tokenorstd_slop --fetch_openai_oauth_device_token, then start with--openai_oauth
You can configure std::slop using environment variables or a configuration file.
The agent looks for a configuration file at ~/.config/slop/config.ini. You can also specify a custom path using the --config flag.
It is STRONGLY RECOMMENDED that slop.db lies in a central directory or outside the codebase. It generates 2 other artifact files, at least ensure that
your .gitignore contains this. The context ledger is completely stored in the database, and it can inadvertently capture information from your environment if you are not careful. Eg Environment Variables.
For a getting-started walkthrough that covers config methods end-to-end, see docs/WALKTHROUGH.md.
[slop]
model = gemini-3-flash-preview
# OR
openai_api_key = sk-...
openai_base_url = https://api.openai.com/v1
# use_responses = true # optional: use OpenAI Responses API with API key mode
# openai_oauth = true # optional: use OpenAI OAuth token + Responses API
# openai_oauth_token_path = /custom/path/chatgpt_plus_token.jsonSee docs/example_config.ini for a full list of options.
You can define config-based llm_query specializations as first-class tools. This
is useful for role-focused delegation (for example: code review, repo exploration)
without rewriting prompts each time.
Add one INI section per specialization using the llm_tool_ prefix:
[llm_tool_code_review_llm]
system_prompt_patch = You are a strict code reviewer focused on correctness and regressions.
session_id = code_review
skill = code_reviewer
context_window = 8After startup, call the specialized tool directly by name (for example
llm_tool_code_review_llm) with a query argument.
For a complete multi-specialization example, see docs/example_subqueries.ini. Detailed behavior and policy constraints are documented in docs/impl/subqueries.md.
Agents can use the top-level run_js tool to execute a synchronous QuickJS
snippet when a task is easier to express as local control flow: batching file
reads, reshaping JSON, looping over small file edits, or running a focused shell
validation. Pass a JavaScript body in the code field and end with
return <json-serializable value>; when a result is needed. Optional JSON
input is exposed as globalThis.input; use it for source-code or edit payload
strings instead of embedding those strings in JavaScript literals.
Inside the snippet, tools.* helper methods validate arguments before they
call the host tool. Use tools.help() to discover the current helper/tool
catalog, and use tools.dispatch(name, args) for run-js-callable host tools
that do not have a dedicated helper method. Host calls still pass through the
normal tool executor, permission checks, and subquery policy. run_js cannot
recursively invoke itself.
Minimal example:
return tools.read_file({ path: 'README.md', start_line: 1, end_line: 20 });Batching example:
const status = tools.execute_bash({
cwd: '.',
command: 'git status --short',
allow_nonzero_exit: false,
timeout_seconds: 60
});
const help = tools.help();
return { status: status.stdout, run_js_callable: help.run_js_callable };Keep snippets bounded and deterministic, summarize large outputs before
returning them, and prefer exact edit_tool edits for source changes. For reusable
JavaScript, use tools.persist_function(args) after checking the current helper
catalog with tools.help(); persisted functions are stored in js_functions and
shown by /tools js_help. For adaptive multi-step tasks, activate the
dynamic_workflow_harness skill to select a small, budgeted run_js template
instead of hand-rolling an unbounded workflow.
SLOP_DEBUG_HTTP=1: Enable full verbose logging of all HTTP traffic (headers & bodies).
- C++ Standard: C++17.
- Style: Google C++ Style Guide.
- Exceptions: Disabled (-fno-exceptions).
- Memory: RAII and std::unique_ptr exclusively.
- Error Handling: absl::Status and absl::StatusOr.
- Asan and Tsan clean at all times.
- Personas & Skills: Understanding global context injection and modular skills.
- Documentation Guide: Entry point and reading order for the documentation set.
- Architecture & Schema: Understanding the database-driven engine.
- Sessions: How context isolation and management work.
- Context Management: The history and strategy for managing model memory.
- Walkthrough: A step-by-step example of using the agent.
- Subquery Implementation Notes: Design and policy notes for INI-configured
llm_queryspecializations. - run_js JavaScript Control Plane: Helper contract, discovery, examples, and operational guidance for agent-side JavaScript snippets.
- Fuzzing: FuzzTest targets, invariants, and how to run/extend the fuzz suite.
- Contributing: Code style, formatting, and linting guidelines.
The core logic is divided into modules:
database.h: Manages the SQLite-backed ledger. Handles persistence for messages, memos, tools, and skills.tool_dispatcher.h: Implements a thread-safe execution engine. It dispatches multiple tool calls concurrently while ensuring results are returned in the proper order for the LLM.cancellation.h: Provides a mechanism for interrupting tasks. It supports registering callbacks to kill shell processes or abort HTTP requests.orchestrator.h: high-level interface for model interaction. Implementations for Gemini and OpenAI manage history windowing and response parsing.shell_util.h: Executes shell commands in a separate process group, with support for live output polling and termination on cancellation.http_client.h: A minimalist, cancellation-aware HTTP client used for all model API calls.
interface/: Implements the terminal UI. The UI is minimal but clean, uses readline for user input, color codes and ASCII Codes.markdown/: Usestree-sitter-markdownto provide syntax highlighting (C++, Python, Go, JS, Rust, Bash) and structured rendering for agent responses. This is a stand alone Markdown parser / renderer library in C++.app/main.cpp: The primary event loop. Coordinates between the Orchestrator, ToolDispatcher, and UI.
