Puras — local skill runner
Run AI skills end to end on your own machine.
Why Puras? · Docs · Getting started · Open-source vs Cloud · Community · License
Puras turns a prompt into a typed, testable, deployable skill — and runs it on your own machine.
Why Puras?
You can already fire a prompt at an LLM SDK — or an agentic one that wires up tool calls for you — and get an answer back. The hard part is everything around it: persisting memory across runs, orchestrating long-running, multi-step pipelines, and surviving the expensive failure — the run that burns through a dozen costly steps and then dies on the last one, throwing away everything before it. Add schema-validating the output and testing the whole thing so it doesn't silently regress, and you're maintaining a pile of glue code and infrastructure per prompt. A skill is that prompt promoted to a real unit:
- a typed input/output contract — schema-validated in, schema-valid JSON out, every time;
- memory that persists across runs — a local SQLite store on your laptop, semantic recall on the platform;
- evals that test the prompt like code, with a CI gate;
- one loop, two environments — build against a local API and ship the identical bundle with
puras deploy; in production, long runs are checkpointed, so a step that fails near the end resumes instead of re-running everything before it.
If all you need is a single completion, reach for the SDK — Puras earns its place the moment a prompt becomes something you run repeatedly, test, and ship.
Getting started
Install the CLI and the offline runner:
pip install puras
A skill is a folder — a prompt and an input/output contract. Create two files.
skill.yaml declares the contract as plain JSON Schema — the inputs it takes and
the fields it must return:
# triage/skill.yaml
title: Ticket Triage
description: Sort an inbound support message into a category, priority, and summary.
entrypoint: SKILL.md # markdown entrypoint = agentic; the file is the system prompt
input_schema:
type: object
required: [message]
properties:
message: { type: string }
output_schema:
type: object
properties:
category: { type: string, enum: [bug, billing, feature_request, other] }
priority: { type: string, enum: [low, medium, high, urgent] }
summary: { type: text } # `text` = multi-line string
SKILL.md is the system prompt the agent runs with:
<!-- triage/SKILL.md -->
You triage inbound support messages. Read the `message` and respond with its
`category`, `priority`, and a one-line `summary`. Judge priority by user impact,
not by how loud the message is.
Serve it. puras serve exposes the same job API your app will hit in
production, backed by the local runner on your own LLM key (if no key is set,
the CLI asks for one):
puras serve # serves ./ → http://127.0.0.1:8787
Now call the skill from your app — point any Puras SDK at the local base URL:
import puras
client = puras.Client(api_key="local", api_base="http://127.0.0.1:8787", skillpack="local")
out = client.run("triage", {"message": "I was charged twice this month and want a refund!"})
print(out["category"], out["priority"]) # → billing high
import { Puras } from "puras";
const puras = new Puras({ apiKey: "local", apiBase: "http://127.0.0.1:8787", skillpack: "local" });
const { category, priority } = await puras.run("triage", { message: "I was charged twice this month and want a refund!" });
console.log(category, priority); // → billing high
That's the whole loop. When you ship, change the base URL to
https://api.puras.co, run puras deploy, and the same app code runs
against the managed platform — nothing else changes.
Want to iterate faster?
puras run --local triage -i message="…"runs a skill straight from the CLI,puras eval --localgates it on evals, and theexamples/folder has ready-to-run skillpacks.
Open-source vs Cloud
Puras is open-core: the runner — the agent loop and the local tool surface — is MIT-licensed and runs fully offline, forever. The hosted platform at puras.co is how the project is sustainably funded, and it adds the managed surface that can't exist on a single laptop. Premium isn't a crippled core — it's the capabilities that need real infrastructure.
| Local runner (this repo, MIT) | Puras Cloud (hosted) | |
|---|---|---|
| Setup & maintenance | pip install, you run it |
Fully managed, nothing to install |
| LLM key & billing | Bring your own key, you pay the provider | Managed, usage-based, transparent pricing |
| Agent loop & local tools | ✓ text, bash, file tools, your Python tools, in-process subagents |
✓ same loop |
| Job API for your app | ✓ puras serve — the job API on localhost |
✓ api.puras.co — managed, scaled, durable |
Evals (check/schema/rubric) |
✓ per run + offline suites | ✓ + suites at scale, CI gating, version diffs |
| Guardrails (input/tool/output/retrieval rails, fail-closed) | — | ✓ enforce safety rails at runtime — PII/injection/policy, block/redact/approve |
| Media (image/video/audio) | ✓ generate_* direct to the provider |
✓ generation + persistence (bucket-backed) |
| Web search / fetch / browser | ✓ search + fetch | ✓ search / fetch / browser screenshots |
| Shared memory | ✓ persistent, local SQLite | ✓ persistent, workspace-scoped + semantic (pgvector) |
| Persistent storage | — | ✓ bucket-backed drive |
| Durable resume | — | ✓ checkpointed, survives worker restarts |
| Hindsight (retrospectives) | — | ✓ mines stored run traces for recurring inefficiencies, surfaces fixes |
| Budgets, tracing, dashboard | console events + a token tally | ✓ spend budgets, OTel spans, run timelines |
| Marketplace & sharing | — | ✓ |
| Support | Issues & Discussions | priority / SLA |
The local runner gives you the free, offline core; the hosted platform adds the managed surface — persistence, scale, durable resume, retrospectives — for when you ship the same bundle unchanged.
Community
Questions, bugs, and skill ideas are welcome in Issues and Discussions. PRs that improve the runner, the docs, or the examples are appreciated.
Working from a checkout instead of PyPI:
pip install -e . # puras-runner (the runtime)
pip install -e worker/sdk # the puras CLI + SDK
License
MIT. The hosted platform's server-side code is separate and commercial — this runner, the SDK, and the examples are MIT and yours to use, modify, and self-run.
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 puras_runner-0.8.0.tar.gz.
File metadata
- Download URL: puras_runner-0.8.0.tar.gz
- Upload date:
- Size: 213.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0580dae1d8fb272aaabe6ec38b8c10698da9d6fa6d0a8b39f32e55bb10f24e2f
|
|
| MD5 |
f0c61ab86017aa6bef1c31bf138359ef
|
|
| BLAKE2b-256 |
5bbb9be8c2c5200d694efc5db27abcf690f505e7b7719b3534ed5459aada7634
|
Provenance
The following attestation bundles were made for puras_runner-0.8.0.tar.gz:
Publisher:
publish.yml on PurasAI/puras
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
puras_runner-0.8.0.tar.gz -
Subject digest:
0580dae1d8fb272aaabe6ec38b8c10698da9d6fa6d0a8b39f32e55bb10f24e2f - Sigstore transparency entry: 1884983175
- Sigstore integration time:
-
Permalink:
PurasAI/puras@5f3fb553b07b2ae84e5754756516cec78241a3fa -
Branch / Tag:
refs/heads/main - Owner: https://github.com/PurasAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5f3fb553b07b2ae84e5754756516cec78241a3fa -
Trigger Event:
push
-
Statement type:
File details
Details for the file puras_runner-0.8.0-py3-none-any.whl.
File metadata
- Download URL: puras_runner-0.8.0-py3-none-any.whl
- Upload date:
- Size: 241.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c8253d38315cbc506ef61fab0d446371feecaaa4798d32a5f49a144d4ffbe879
|
|
| MD5 |
5f3cb0b7504407776a736073119d30b8
|
|
| BLAKE2b-256 |
d21fa016a5d690787d2cc4788248f3c3361327a88c034170ed58185890e52f85
|
Provenance
The following attestation bundles were made for puras_runner-0.8.0-py3-none-any.whl:
Publisher:
publish.yml on PurasAI/puras
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
puras_runner-0.8.0-py3-none-any.whl -
Subject digest:
c8253d38315cbc506ef61fab0d446371feecaaa4798d32a5f49a144d4ffbe879 - Sigstore transparency entry: 1884983351
- Sigstore integration time:
-
Permalink:
PurasAI/puras@5f3fb553b07b2ae84e5754756516cec78241a3fa -
Branch / Tag:
refs/heads/main - Owner: https://github.com/PurasAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5f3fb553b07b2ae84e5754756516cec78241a3fa -
Trigger Event:
push
-
Statement type: