Skip to main content

A local-first CLI workflow engine for reliable personal automation.

Project description

Runspool

CI License: MIT Python 3.11+

Local-first CLI workflows for reliable personal automation.

Runspool turns scripts, files, and manual checklists into resumable, observable workflows — with SQLite state, retries, logs, pause/resume controls, step plugins, and JSON output for humans, scripts, and AI agents.

It runs entirely on your machine. No hosted service, no account, no data leaving your laptop by default.

简体中文 README · Docs · Examples


Why Runspool

Personal automation usually starts as a shell script and slowly turns into a mess: when it dies halfway through, you don't know what ran; re-running redoes work; there's no history; and pausing or retrying means editing the script.

Runspool gives that automation a backbone:

  • Resumable — every task is a row in SQLite; a crash or reboot loses nothing.
  • Observable — every state change is an event; every step run is timed.
  • Controllable — pause, resume, retry, terminate, reprioritize from the CLI.
  • Composable — workflows are ordered lists of steps; add your own as plugins.
  • Scriptable--json on every read command, built for shell and AI agents.

It is not an AI tool, and it is not a cloud workflow platform. It is a small, dependable engine for turning local scripts, files, and checklists into workflows you can trust.

Install

pip install runspool

Or from source:

git clone https://github.com/ethan-sun-dev/runspool
cd runspool
pip install -e ".[dev]"

Requires Python 3.11+. Core dependencies: Typer, Pydantic, PyYAML (SQLite is in the standard library).

Quickstart (about 3 minutes, no setup)

# 1. Create a config and database.
runspool init

# 2. Queue a task. The default `local_file` workflow uses only built-in steps.
echo "Invoice #42  Total amount due: 1320  Payment terms: net 30" > invoice.txt
runspool add ./invoice.txt

# 3. Advance every task to completion, once.
runspool run

# 4. Look at the result.
runspool status
runspool inspect 1

You'll see the task flow through five steps and land its artifacts under workspace/ready/1/ (normalized Markdown, a summary, a classification, and metadata). That's a complete workflow with persisted state, logs, and a step timeline — and it ran with zero external dependencies.

What it looks like

flowchart LR
    CLI[runspool CLI] -->|add / run / daemon| ENG
    AGENT[AI agent or script] -->|--json| CLI
    subgraph ENG[Engine]
        COORD[Coordinator] --> POOL[Worker pool]
        POOL --> RUN[Step runner]
        RUN --> STEPS[Step registry\nbuilt-in + plugins]
    end
    ENG <--> DB[(SQLite\ntasks · events · step_runs)]
    RUN --> FS[(Workspace\nartifacts)]

A task carries an input through a workflow — an ordered list of steps. The coordinator claims queued tasks (respecting per-step concurrency quotas), the worker pool runs each step, and the state machine records every transition. Long jobs run under the daemon; one-shot runs use run.

Task lifecycle

queued → running → (next step) queued → … → completed
                 ↘ failed ──(retry)──↗
                 ↘ manual_required          (retries exhausted; needs you)
   running → pause_pending → paused → (resume) queued
   non-terminal → terminated   (completed / terminated refuse further control)

CLI

runspool init                     # create config + database
runspool add <input> -w <wf>      # queue a task (default workflow: local_file)
runspool run                      # advance all runnable tasks once (great for demos)
runspool daemon                   # run a resident loop (long-running automation)
runspool status [<id>]            # list tasks, or show one in detail
runspool inspect <id>             # agent-friendly snapshot + suggested next action
runspool logs <id>                # event history for a task
runspool overview                 # counts by status
runspool pause|resume|retry|terminate <id>
runspool set-priority|set-retries|set-step <id> <value>
runspool workflows                # list workflows and their steps
runspool doctor                   # check the local environment

Every read command supports --json:

runspool status --json
runspool inspect 1 --json
runspool logs 1 --json
runspool overview --json
runspool workflows --json
runspool doctor --json

See docs/cli.md for the full reference.

Built for AI agents and scripts

runspool inspect <id> --json returns exactly what an automated caller needs to decide what to do next — current state, the last error, the artifacts produced, the actions that are valid right now, and a plain-language suggestion:

{
  "id": 1,
  "status": "manual_required",
  "workflow": "client_intel",
  "current_step": "collect_sources",
  "last_error": "FileNotFoundError: Missing required source(s): requirements.md",
  "retry_count": 1,
  "max_retries": 0,
  "recent_events": [],
  "artifacts": [],
  "available_actions": ["retry", "set-step", "set-retries", "terminate"],
  "suggested_next_action": "FileNotFoundError: Missing required source(s): requirements.md. Resolve the cause, then run `runspool retry 1`."
}

An agent can poll inspect --json, act on available_actions, fix the cause, and call runspool retry 1 — no screen-scraping required. See docs/agent-json-output.md.

Examples

Three runnable examples, each with its own README and sample data:

Example What it shows
local-file-pipeline The quickstart. Built-in steps only; runs offline in minutes.
client-intel-brief A real consulting workflow: sources → briefing package. Custom plugin steps; demonstrates manual_required recovery.
creator-publishing-pipeline A content pipeline that builds a multi-platform draft package (never auto-publishes).

Write a custom step

A step is a small class. It reads the task, does work, writes artifacts, and returns a result:

from runspool.engine.step import Step, StepContext, StepResult

class GreetStep(Step):
    name = "greet"

    def run(self, ctx: StepContext) -> StepResult:
        ctx.heartbeat("working")                 # optional progress
        name = ctx.task.get("name") or "world"
        return StepResult(message=f"hello, {name}")

Load it from config and use it in a workflow:

plugin_paths: [steps]            # directories added to sys.path (relative to this config)
steps:
  greet:
    import: "my_steps:GreetStep"
workflows:
  hello:
    steps: [greet, archive]

Steps can also raise StepDeferred to wait for a precondition (retry next tick without counting a failure), or raise any exception to fail and retry. See docs/writing-steps.md.

Concurrency

Runspool runs steps in a bounded thread pool. The daemon keeps the pool busy across many ticks, so long steps don't block the queue, and a per-step concurrency quota caps how many of a given step run at once. A crashed or silent worker's task is reclaimed by heartbeat timeout. See docs/concepts.md.

Privacy & safety

  • Local-first. All state lives under workspace_root on your machine. There is no hosted service and nothing is uploaded by default.
  • No secrets required. The engine and built-in steps need no API keys.
  • Drafts, not auto-publish. Content examples produce drafts and checklists; publishing is always a deliberate, manual step.

Non-goals

  • Not a hosted/cloud workflow platform.
  • Not a distributed scheduler or a replacement for heavyweight orchestrators.
  • Not an AI product (though it is deliberately AI-agent-friendly).
  • No web UI in scope for now — the CLI and JSON are the interface.

Roadmap

  • Atomic multi-coordinator claiming (single-process is solid today).
  • runspool watch to follow a task's events live.
  • Optional structured log export (JSONL).
  • A small library of community step plugins.
  • Opt-in publish adapters for the creator example (draft submission only).

Contributing

Contributions are welcome — see CONTRIBUTING.md and the Code of Conduct.

pip install -e ".[dev]"
ruff check .
pytest

License

MIT.

Project details


Download files

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

Source Distribution

runspool-0.1.0.tar.gz (66.8 kB view details)

Uploaded Source

Built Distribution

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

runspool-0.1.0-py3-none-any.whl (46.4 kB view details)

Uploaded Python 3

File details

Details for the file runspool-0.1.0.tar.gz.

File metadata

  • Download URL: runspool-0.1.0.tar.gz
  • Upload date:
  • Size: 66.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for runspool-0.1.0.tar.gz
Algorithm Hash digest
SHA256 46d827f528c30c631cdba1bf0423e7ffe788b3de85531f438bb0ef5d44f2b3a4
MD5 eda86ab17b73865cbc182a13c71828ba
BLAKE2b-256 dcef7daab7ebc968af9fb98cbca994a5c6630ac0cc46aef365d98c2a1ef06342

See more details on using hashes here.

Provenance

The following attestation bundles were made for runspool-0.1.0.tar.gz:

Publisher: publish.yml on ethan-sun-dev/runspool

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file runspool-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: runspool-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 46.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for runspool-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a2b5a7dc02a59297affacfe13b7b91902e7356a37834c0e4e4adc830f0ac47b6
MD5 e7edbf98b61ce223eca207fd034b8981
BLAKE2b-256 7ba8a275ecfea5230cca694bcbdd67f6ea6dcda47043d5c3cbc80a97cdafaca0

See more details on using hashes here.

Provenance

The following attestation bundles were made for runspool-0.1.0-py3-none-any.whl:

Publisher: publish.yml on ethan-sun-dev/runspool

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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