Skip to main content
Glama
README.md
# prompt-library

A local workbench for reusable prompts: Markdown files, typed variables, immutable
revisions, local regression checks, an offline browser UI, and MCP tools **and native
prompts**. No model API, subscription, account or cloud service is required.

Your files are authoritative. SQLite is a searchable cache plus a local usage log.
Prompts remain readable and reviewable in Git without a running application.

## Quick start

From a clone, with a Python environment active:

```sh
pip install -e .
plib --config examples/config.toml sync --json
plib --config examples/config.toml check --json
```

The bundled library contains **20 useful prompts and 20 passing regression cases**:
weekly reports, email editing, meeting minutes, technical translation, study plans,
short social posts, code review, decision briefs, project updates, bug reports,
document outlines, research summaries, interview practice, release notes, risk
registers, customer replies, daily priorities, dataset review, requirement
clarification and structured task cards. All example facts and names are fictional.
Templates and documentation are English; intentional multilingual input fixtures
exercise Chinese text handling and translation use cases.

First sync:

```json
{"ok":true,"data":{"prompts":20,"changed":20,"removed":0},"warnings":[]}
```

Render a report or open the interface:

```sh
plib --config examples/config.toml render weekly-report --var-file examples/variables.json
plib --config examples/config.toml serve
```

Visit `http://127.0.0.1:8879`. Search, inspect a template, fill its variables, render,
and copy the result. Every asset is local; there is no CDN or browser-side model call.

![Offline search, typed variables, preview and rendered prompt](docs/web-desktop.png)

## Installation

Python 3.11 or 3.12, Linux or macOS, SQLite with FTS5:

```sh
git clone https://github.com/GuoHaoyu1688/prompt-library.git
cd prompt-library
python3 -m venv .venv
. .venv/bin/activate
pip install -e .
```

For development: `pip install -e '.[dev]'`. `requirements.txt` installs the same
base application. Secure descriptor-relative file access requires POSIX; Windows
is outside this version's supported platforms.

## File format

Each prompt is `prompts/<category>/<name>.md` under the configured library directory:

```yaml
---
name: technical-summary
title: Technical summary
description: Summarize supplied evidence without adding claims.
tags: [writing, research]
version: "0.1.0"
variables:
  source:
    type: string
    required: true
    description: Source text to summarize
  bullet_count:
    type: integer
    default: 5
    required: false
    description: Target number of bullets
model_hints: Use any suitable model manually; no provider is required.
---
Summarize this text in {{ bullet_count }} bullets:
{{ source }}
{% include 'common/style_en.md' %}
```

Supported variable types: `string`, `integer`, `number`, `boolean`, `array`, `object`.
Values are strict JSON types: `"2"` is not an integer, and `true` is not a number.
Defaults are validated. Required values without defaults must be supplied. Optional
values without defaults remain undefined and need a Jinja `default` filter or
`is defined` guard. Unknown input variables and undeclared template variables fail
with distinct error codes. Duplicate YAML keys are rejected.

`--var topic=123` remains the literal string `123` for a string declaration.
For other types, values use JSON: `--var count=3`, `--var urgent=true`,
`--var 'items=["first","second"]'`. A JSON `--var-file` can supply all fields;
explicit `--var` values override it. Duplicate CLI assignments are rejected.

Plain Markdown fragments live under `prompts/common/`. Includes use constant paths
relative to `prompts/`, such as `{% include 'common/style_zh.md' %}`. Absolute paths,
`..`, symlinks, dynamic filenames and include cycles are rejected. Included content
is part of the version SHA, so changing a fragment creates a new observed revision
for each affected prompt.

### Template safety

Rendering uses Jinja's `ImmutableSandboxedEnvironment` with `StrictUndefined`.
Python attribute access, function calls, macros, imports and inheritance are disabled.
Use `record['field']` for JSON object keys and `for item in items` for lists.
Sequence multiplication and exponentiation are disabled. Filters are limited to
`upper`, `lower`, `trim`, `replace`, `join`, `length`, `default`, `tojson`, `indent`,
`escape`, `sort`, `unique`, `first`, `last`, `round`.

Each render runs in a separate worker with a wall-clock timeout and output limit.
Only supplied JSON values and validated template fragments are passed in. This is
an application boundary, not a general-purpose sandbox for arbitrary executable
code. The optional external runner is a separate, explicit trust decision.

## Configuration and paths

`--data-dir` or `PLIB_DATA_DIR` selects runtime state; default `./data`.
`--config settings.toml` or `PLIB_CONFIG` selects TOML. Relative `library_dir` is
resolved against the configuration file; `PLIB_LIBRARY_DIR` can override it.
Without explicit configuration, saved settings in `<data-dir>/config.json` are
used, or the working directory is used for a new library.

```toml
library_dir = "/workspace/prompts"
timezone = "Asia/Shanghai"
render_timeout = 4.0
max_output_chars = 1000000
runner_enabled = false
runner_command = []
runner_timeout = 10.0
```

Runtime files: `index.db` (FTS and usage), `config.json`,
`snapshots/<name>/<sha>.json`, and dated ZIPs in `backups/`.
Keep user libraries and runtime state outside the public repository. The shipped
`examples/library/` is deliberately fictional and safe to publish.

## CLI reference

Global flags work before or after the command: `--data-dir`, `--config`, `--json`,
`--quiet`, `--progress`, `--yes`, `--version`. All commands are noninteractive.
Query results are JSON envelopes. `render` emits plain text unless `--json` is set;
JSON includes text, semantic version, content SHA and actor. Errors are always JSON.
Logs/progress go to stderr. `--quiet` leaves the machine-readable result intact.

| Command | Example |
|---|---|
| `new` | `plib new summary --category writing --title "Short summary"` |
| `new --body-file` | `plib new summary --body-file draft.txt --yes` |
| `list` | `plib list --tag research --json` |
| `search` | `plib search '"code review"' --json` |
| `show` | `plib show code-review --json` |
| `render` | `plib render polish-email --var 'draft=Hello Alice, please review the sample.' --actor assistant-a --json` |
| `history` | `plib history polish-email --json` |
| `diff` | `plib diff polish-email SHA1 SHA2 --json` |
| `check` | `plib check --progress --json`; optionally one prompt name |
| `sync` | `plib sync --progress --json` |
| `stats` | `plib stats --json` |
| `export` | `plib export --format json --out library.json --json` |
| `export` | `plib export --format csv --out library.csv --no-bom --json` |
| `import` | `plib import library.json --json`; CSV and large Markdown also supported |
| `backup` | `plib backup --json`; optionally `--out unique-backup.zip` |
| `serve` | `plib serve --host 127.0.0.1 --port 8879` |
| `mcp` | `plib mcp` |

`new` creates a simple starter with no variables; edit its YAML declarations and
body in your editor, then run `sync`. Use `import` for complete existing templates.
`--yes` authorizes overwriting changed source paths during `new` or import. Existing
exports/backups are always refused. Back up before source replacement.

FTS search supports AND-separated terms, quoted phrases and trailing `*` prefixes.
Chinese-only phrases use overlapping bigrams with single-character support. Mixed
Chinese/Latin phrases use ordinary SQLite Unicode tokenization. Tag listing uses
exact tag matching. All reading operations validate and sync current source files,
so invalid source edits cannot silently serve stale cached templates.

### Revisions and usage

The built-in snapshot system works with or without Git. A SHA-256 covers the raw
prompt and resolved include contents. `history` lists immutable observed snapshots;
`diff` accepts full SHAs, unique SHA prefixes of at least eight characters, or an
unambiguous `version` label. Reusing a semantic version is permitted, but choose
SHAs when several snapshots share it. Snapshots capture revisions observed by
`sync`/reads/rendering; intermediate edits never observed by the tool are not recovered.
Git history is independent and is not parsed.

Snapshot files preserve history even when the searchable cache is rebuilt.
Usage records track successful user-facing renders by time, actor and version SHA.
`stats` shows frequent and never-used prompts. Regression checks do not inflate
usage counts. CLI defaults to `cli`; the Web UI records `web`; MCP tools record
`mcp`, and native MCP prompts record `mcp-prompt`.

## Local regression checks

Each prompt has `tests/<name>.yaml` within its library:

```yaml
cases:
  - name: fictional-example
    variables:
      source: A fictional sample was inspected.
      bullet_count: 3
    expect:
      contains: ["3 bullets", "fictional sample"]
      not_contains: ["{{"]
      min_length: 30
      max_length: 2000
```

Assertions apply to the **rendered prompt itself**, not to an invented model answer.
`json_schema` can validate a rendered JSON prompt; only local `#` references are
allowed, so validation never retrieves a schema from the network. Missing/invalid
case files count as failures. `check` returns exit code 1 and `CHECK_FAILED` when
any case fails, with detailed per-case results.

### Optional local command runner

Disabled by default. To check output from your own local program, explicitly set:

```toml
runner_enabled = true
runner_command = ["/absolute/path/to/your-local-program", "--stdin"]
runner_timeout = 10.0
```

Then run `plib check --external --json`. The rendered prompt goes to stdin; stdout
is checked with the same expectations. Commands are argv lists with `shell=False`;
template content never becomes command-line code. Timeouts, nonzero exit statuses
and excessive output are failures. The command is user-supplied code and can have
its own side effects or network access. No runner is exposed through Web or MCP.

## Import, export and backups

JSON exports contain `schema_version: 1` and a `files` array of relative `path` and
UTF-8 `content`. CSV uses `path,content`, UTF-8 with BOM by default. Both include
prompt files, shared fragments and regression cases. Imports validate the entire
combined library in a temporary staging area before changing live files, reject
unsafe paths and duplicate names, and are idempotent for identical content. Changed
existing files require `--yes`; unexpected write failures roll back completed writes.

Large Markdown imports split at level-two (`##`) headings. Each section becomes a
prompt in `prompts/imported/`; template variables become required strings. The
preamble is ignored. Names are deterministic slugs with collision suffixes. Review
inferred types after importing. See `examples/bulk-import.md`.

`backup` creates a dated ZIP with the authoritative library, regression cases,
SQLite snapshot (including usage), configuration and immutable prompt revisions.
To restore, stop writers, preserve the current data, extract a trusted backup into
new directories, update `library_dir`, then run `sync`. Do not delete the database
as a routine rebuild: `sync` preserves usage; deleting `index.db` loses usage history.

## MCP tools and native prompts

```json
{
  "mcpServers": {
    "prompt-library": {
      "command": "/workspace/apps/prompt-library/.venv/bin/plib",
      "args": ["--config", "/workspace/config/prompt-library.toml", "--data-dir", "/workspace/data/prompt-library", "mcp"]
    }
  }
}
```

Tools: `list_prompts(tag?)`, `search_prompts(query)`, `get_prompt(name)`,
`render_prompt(name, variables)`. The render tool accepts native JSON variable types
and appends a usage record. It does not invoke a model or command.

Native **prompts/list** exposes each prompt and its variable arguments.
**prompts/get** returns the rendered user message. Native MCP arguments are strings:
use plain strings for string variables and JSON text for other types, for example
`{"topic":"Sample task","priority":"3","urgent":"true"}`. Required/default
status is preserved. Restart the server to register newly added prompt names or
changed native argument declarations; tool calls always read current source files.

Uses the official `mcp` SDK, constrained to `>=1.20,<2` for FastMCP. Schemas and an
example native prompt descriptor are in [docs/mcp-capabilities.json](docs/mcp-capabilities.json).

## Validation

```sh
pip install -e '.[dev]'
ruff check .
ruff format --check .
pytest -q --cov=prompt_library --cov-report=term-missing
```

The suite covers all 20 fixture checks; missing, unknown and incorrectly typed
variables; template syntax and sandbox escape attempts; include traversal/symlinks;
include-aware history and diff integrity; JSON/CSV/Markdown roundtrips; disabled
and timed-out runners; offline schema validation; HTTP origin guards; and an actual
SDK stdio client listing and fetching native prompts. The Web search, render and
copy workflow is also checked in a real browser. CI runs Python 3.11/3.12 on Ubuntu.

## FAQ and roadmap

- **Does rendering call a model?** No. It produces text for you or your assistant.
- **Can I edit files directly?** Yes. They are the source of truth; queries sync them.
- **Why is `loop.index` rejected?** All Python attribute access is disabled. Use
  simple bullet loops or provide numbering in the input data.
- **Why did a fragment change alter a prompt SHA?** Its rendered instructions changed,
  so reproducibility requires a new dependency-aware version.
- **Can I publish my library?** The examples are fictional. Your own templates,
  variables, backups and usage records remain local unless you share them.
- **macOS editable import failure?** If `python -v` reports hidden `.pth` files,
  clear that flag only inside the virtual environment using
  `chflags nohidden .venv/lib/python3.12/site-packages/*.pth`.

Planned improvements: richer structured variable forms, more search normalization,
and optional Git revision views. Paid-model integrations and online sharing are
outside this project.

MIT licensed. See [LICENSE](LICENSE), [AGENTS.md](AGENTS.md) and [CHANGELOG.md](CHANGELOG.md).
Implementation references: [Jinja sandbox](https://jinja.palletsprojects.com/en/stable/sandbox/),
[SQLite FTS5](https://www.sqlite.org/fts5.html), and the
[official MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk).