Metadata-Version: 2.5
Name: prompt-crafter
Version: 0.2.0
Summary: Say what the picture must contain, check that it does, and refuse when it does not. A typed contract of depictable claims drives both the prompt and the gate that verifies the pixels.
Project-URL: Repository, https://github.com/mcp-tool-shop-org/prompt-craft
Author-email: mcp-tool-shop <64996768+mcp-tool-shop@users.noreply.github.com>
License: MIT License
        
        Copyright (c) 2026 mcp-tool-shop
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: contract,diffusion,dspy,generative-assets,sprite,verifier
Requires-Python: >=3.11
Requires-Dist: pydantic>=2.6
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Provides-Extra: image
Requires-Dist: accelerate>=0.33; extra == 'image'
Requires-Dist: diffusers>=0.30; extra == 'image'
Requires-Dist: numpy>=1.26; extra == 'image'
Requires-Dist: pillow>=10.0; extra == 'image'
Requires-Dist: torch>=2.4; extra == 'image'
Requires-Dist: transformers>=4.44; extra == 'image'
Provides-Extra: synth
Requires-Dist: dspy-ai>=2.5; extra == 'synth'
Requires-Dist: httpx>=0.27; extra == 'synth'
Description-Content-Type: text/markdown

<p align="center">
  <a href="README.ja.md">日本語</a> | <a href="README.zh.md">中文</a> | <a href="README.es.md">Español</a> | <a href="README.fr.md">Français</a> | <a href="README.hi.md">हिन्दी</a> | <a href="README.it.md">Italiano</a> | <a href="README.pt-BR.md">Português (BR)</a>
</p>

<p align="center">
  <img src="docs/assets/logo.png" alt="prompt-craft" width="820">
</p>

<p align="center">
  <a href="https://github.com/mcp-tool-shop-org/prompt-craft/actions/workflows/ci.yml"><img src="https://github.com/mcp-tool-shop-org/prompt-craft/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License"></a>
  <img src="https://img.shields.io/badge/python-3.11%2B-blue" alt="Python 3.11+">
</p>

#

**Say what the picture must contain. Check that it does. Refuse when it doesn't.**

A generative image pipeline will happily hand you a hero with the wrong face, the wrong palette
and none of the faction's markings — and report success, because nothing looked. prompt-craft
replaces the opaque prose prompt with a **typed contract of depictable claims**, uses that same
list twice — once to write the prompt, once to check the pixels — and **blocks the asset when a
required claim is not there**.

```
   CONTRACT  ──atoms──▶  SYNTHESIZE  ──prompt──▶  GENERATE
   typed, depictable     every token traces      diffusion + control
        │                to an atom                     │
        │ the same atoms                                │ pixels
        └──────────────────────▶  GATE  ◀───────────────┘
                    a DIFFERENT model family checks the
                    contract against the image, cheapest
                    tier deciding first
                         │                    │
                       PASS                FAIL / UNCERTAIN
                         ▼                    ▼
                    BIND to canon      REPAIR ladder, or a
                  (only when every     human checkpoint when
                   required atom       the gate is unsure
                   actually passed)
```

**The one idea:** the contract's atom list is *the same list used twice.* Writing the prompt and
checking the result read from one source, so the thing you asked for is the thing that gets
verified. That is what closes the loop an opaque prompt leaves open.

## Install

```bash
pip install prompt-crafter
pcraft --help
```

```bash
npm install -g @mcptoolshop/prompt-crafter   # the same command, as a launcher
```

The distribution is **`prompt-crafter`** because `pcraft` and `prompt-craft` are both taken on
PyPI; the import package and the command stay `pcraft`. The npm package is a **launcher, not a
port** — reimplementing a threshold in a second language is how a threshold drifts, so it forwards
to the Python that holds the truth and inherits its exit code.

For development:

```bash
pip install -e ".[dev]"
```

The core is **GPU-free and runs anywhere** — the whole test suite executes against a mock
generator and verifier, which is what proves the plugin boundary actually holds. The `[image]`
extra (torch/diffusers) and `[synth]` extra (DSPy + a hosted LM) wire the real generator,
verifiers and synthesizer. **Neither is needed to run, test, or evaluate the core.**

```bash
pcraft demo              # the whole loop end-to-end, no GPU, deterministic stubs
pcraft gate <image>      # check an image against a contract
pcraft replay <record>   # re-read a bound asset's provenance receipt
```

## What a contract looks like

Not a prose prompt. A list of **atomic, depictable, individually checkable** claims:

- **`must_have`** — a garment, a palette, a silhouette, a sigil. Each carries a `check_type`
  (which gate tier verifies it), a `severity`, and optionally a `depends_on` edge so a claim is
  only meaningful when its parent passed. There is no point verifying the colour of an axe that
  is not there.
- **`must_not`** — anti-constraints, verified as **absence on the pixels**. Not a negative
  prompt: negative prompts leave residual features and fall to paraphrase.
- **`identity_ref`** — a reference plate. **Identity is conditioning, not tokens.** Anatomical
  text makes a diffusion model render a specimen; a reference image binds the specific face.

Contracts inherit — a character extends a faction — and inheritance is **fail-closed**: a child
may *raise* a requirement, never relax or silently drop one it inherited.

## The gate

Three tiers, cheapest deciding first, escalating only when a cheap answer is unclear. A
dependency-ordered pass means a failed parent marks its children N/A rather than scoring
nonsense.

**The verifier is always a different model family from the generator**, enforced by a guard that
refuses to run otherwise. A model is a poor judge of its own output, and that is the least
speculative part of this design.

**Exit codes distinguish four different things**, because a caller reading one number needs to
tell them apart:

| exit | meaning |
|---|---|
| `0` | the gate ran and every required atom passed |
| `1` | bad arguments or a malformed contract |
| `2` | it ran, and a required atom **failed** |
| `3` | it ran, and the result is **unconfirmed** — the human band |
| `4` | it **could not run** — no readable input, or no required tier available |

That last row is the one that matters. "I could not check" and "I checked and it is bad" are
different facts, and collapsing them is a documented source of real harm — it is why browsers
soft-fail certificate revocation, and why monitoring standards have carried a distinct *unknown*
verdict since the 1990s. Every gate transcript also reports **how many required tiers actually
executed**, independently of the verdict, so a gate that quietly stopped checking cannot read as
a pass.

**CLIPScore is not used as the gate metric.** It behaves as a bag of concepts — blind to which
attribute belongs to which object, to counts, and to relations. It is documented as known-broken
in the verifier interface so nobody reintroduces it.

## Honest status

**v0.2.0 — the core is real; the GPU path has never run here.**

| | |
|---|---|
| Core | **105 tests passing**, GPU-free, deterministic. `verify` runs the suite, the suite again under `-O`, and a package build |
| Predicates | the eleven compound decision points in `core/` are **mutation-tested** — 20 of 21 mutants killed, and [the survivor is named](scripts/mutate_predicates.py) rather than hidden |
| Coverage | 81% overall; GPU-bound generator and verifier adapters are the untested remainder |
| The `[image]` path | **never executed on this machine.** `bind --no-mock` refuses with a missing-dependency error. Everything below the plugin boundary is unproven by measurement |
| Thresholds | the sprite sub-gate's floor and variance limits are **hardcoded defaults with no recorded calibration** — no holdout, no citation. Treat them as placeholders |
| Real canon | the shipped example contract is a **generic invention**, not any real project's canon. Binding real canon is a deliberate, human decision |

Two claims that earlier versions of this document made and that measurement did not support,
corrected here rather than quietly dropped:

- The three-zone thresholds were described as *calibrated against a human-labelled holdout*. They
  are not. They are defaults.
- The rule that a generative model is never its own gate was stated as though a study had
  established it. The supporting evidence is **convergent rather than direct** — discriminative
  yes/no polling is measurably more stable than open-ended captioning, models cannot reliably
  self-correct without external feedback, and self-recognition tracks self-preference bias. No
  single study runs the head-to-head. The rule is sound; the certainty was overstated.

## Requirements

| | |
|---|---|
| Python | **3.11+** (CI runs 3.13) |
| Platforms | pure Python, no compiled extensions in the core — developed on Windows 11, CI on `ubuntu-latest` |
| Dependencies | the core needs only `pydantic`. GPU work lives behind optional extras |

## Trust and threat model

- **Data touched** — contract JSON you point it at, the images you pass it, and provenance
  records written under the directory you name. Nothing else is read.
- **Data NOT touched** — no credentials of any kind are read, stored or transmitted. **No
  telemetry, analytics or usage counting**: there is no opt-out because there is nothing to opt
  out of. The core imports no networking library at all.
- **Network egress** — none from the core. The optional `[image]` and `[synth]` extras reach a
  model host by their nature; that is the only network path, and installing them is a choice.
- **Permissions** — ordinary user permissions. No elevation, no service installation, no registry
  or system-settings writes.
- **The sharp edge, disclosed rather than claimed away** — **file operations are not sandboxed.**
  `--records-dir` and `--db` write wherever you point them, deliberately, because this is a
  local-first tool. Point them somewhere you intend.
- **Errors** — deliberate refusals carry a code, a message and a hint, and **raise rather than
  `assert`**, so `-O` cannot delete them; the suite runs a second time under `-O` to prove it.
  Unexpected failures print a traceback only under `--debug`.

## Support status

`main` is the only supported state. No release channel, no backport policy, no SLA. This is
studio infrastructure published in the open, not a product with a support contract.

## How the pieces are arranged

`core/` is domain-agnostic and imports zero diffusion or torch symbols — a domain plugin exports
exactly three things: a generator, a list of verifiers, and an encoder ruleset. Adding a new
domain is a new sibling under `domains/`; nothing in `core/` changes. The GPU-free suite is what
keeps that claim honest.

```
src/pcraft/
  core/          contract · loop · gate · synth · optimize · receipt   (GPU-free)
  cli/           pcraft: synth | gate | bind | demo | replay | compile | sync-rules
  domains/       ── PLUGIN BOUNDARY ──
    image/       generators, the three verifier tiers, encoder rules, sprite subdomain
```

Encoder rules under `domains/image/rules/` are **generated** from a verified recipe database, not
hand-written, and carry a generation header. Every bound asset writes a **replayable provenance
receipt** pinning the contract hash, the synthesizer artifact, the generator and seed, the
verifier version, and the full per-atom gate transcript.

Design rationale, the standards this repo scores itself against, and the named undo for every
irreversible action live in [`STANDARDS.md`](STANDARDS.md) and
[`COMPENSATORS.md`](COMPENSATORS.md).

## License

MIT — see [LICENSE](LICENSE). The licence of any *model* used through this tool is a separate
question and is not covered by it.
