cable8mm / prompt-weaver
Prompt Weaver is a simple library for creating and managing prompts in PHP.
Requires
- php: ^8.3
- ext-gd: *
- bacon/bacon-qr-code: ^3.1
- cable8mm/enum-getter: ^2.2
- laravel/ai: ^0.11.0
- laravel/prompts: ^0.3.22
- symfony/console: ^7.4|^8.1
Requires (Dev)
- laravel/framework: ^13.22
- laravel/pint: ^1.0
- orchestra/testbench: ^11.1
- pestphp/pest: ^4.0
Suggests
- ext-gd: Required for generating QR codes.
README
Prompt Weaver is a small PHP library for generating structured prompts for a Wi-Fi signage design workflow.
It helps you build three pieces of text:
- A short creative brief
- A JSON config prompt
- A final image-generation prompt
The package is centered around the WiFi Note signage flow, where a design brief is turned into a printable, high-contrast sign layout.
Requirements
- PHP 8.3 or newer
- Composer 2.x
- GD extension (required by preview, calibration, and QR rendering)
uv(optional; required for OpenCV-based QR calibration)
Installation
composer require cable8mm/prompt-weaver
Laravel application setup
Laravel's package discovery registers the service provider automatically. If the
application uses Vite, include Prompt Weaver's stylesheet in the application's CSS
entry point, usually resources/css/app.css:
@import "../../vendor/cable8mm/prompt-weaver/resources/css/prompt-weaver.css";
The stylesheet contains the Atkinson Hyperlegible font used by the browser preview.
Because its font URLs are relative, Vite includes and versions the font files in the application build. Build the frontend as usual:
npm run build
Apply the prompt-weaver-font class to dynamic SSID and password text rendered by the service:
<span class="prompt-weaver-font">{{ $ssid }}</span> <span class="prompt-weaver-font">{{ $password }}</span>
No manual service-provider registration, Nova dependency, or public/vendor font
copy is required. The package's PHP/GD preview renderer uses its bundled TTF font
automatically.
For OpenCV-based QR calibration, install uv first. On macOS with Homebrew:
brew install uv
Or use the official installer on macOS/Linux:
curl -LsSf https://astral.sh/uv/install.sh | sh
After Composer installation, initialize the Python environment from the package directory:
uv sync --project vendor/cable8mm/prompt-weaver
This installs the locked Python dependencies from the package's pyproject.toml and uv.lock (currently opencv-python-headless). The package does not ask Composer to run network-dependent Python installation commands. requirements.txt is provided as a compatibility file for users who prefer a requirements-based workflow; the package's uv runner uses the project files above.
If uv is not installed, preview rendering still works, but calibration for fixtures with a QR placeholder requires uv and OpenCV.
Service server setup
QR calibration runs a Python process from PHP. The server therefore needs uv, a writable cache directory, and permission for the PHP process to execute proc_open(). The first run downloads opencv-python-headless; later runs reuse the uv cache.
For a Linux server, run the following during deployment as the same user that runs the application (or PHP worker):
export UV_CACHE_DIR=/var/cache/prompt-weaver/uv mkdir -p "$UV_CACHE_DIR" cd /path/to/application composer install --no-dev --prefer-dist --optimize-autoloader uv sync --locked --project vendor/cable8mm/prompt-weaver
Give the PHP-FPM or queue-worker user read/write access to UV_CACHE_DIR. If the environment is managed by PHP-FPM or systemd, configure UV_CACHE_DIR there; setting it only in an interactive shell does not make it available to PHP.
The package does not install a service-specific CLI command. Call the package's calibration service from your application's command or job, and make that command fail when calibration fails. For example, if your service wraps calibration and preview in shell commands, use:
php artisan wifi:calibrate cafe-restaurant && php artisan wifi:preview cafe-restaurant
calibrate returns a non-zero exit code when uv, OpenCV, the image, or QR-frame detection fails. It does not write a new config.json on failure. Keep uv.lock in the deployed package; the PHP runner uses uv run --locked so a server cannot silently rewrite the lockfile.
For the package repository's GitHub Actions, install and cache uv, then install from the lockfile before running both Python and PHP tests:
- uses: astral-sh/setup-uv@v10 with: enable-cache: true - run: uv sync --locked - run: uv run --locked python scripts/test_calibrate_qr.py - run: composer install --prefer-dist --no-progress - run: composer test
The repository's .github/workflows/run-tests.yml follows this order.
To work on this repository locally, install the development dependencies instead:
git clone https://github.com/cable8mm/prompt-weaver.git
cd prompt-weaver
composer install
What It Does
The library contains three prompt builders:
Cable8mm\PromptWeaver\DesignBriefPromptCable8mm\PromptWeaver\ConfigPromptCable8mm\PromptWeaver\ImagePrompt
They work together like this:
DesignBriefPrompttakes a category and format in the constructor, thenbuild()generates a Wi-Fi signage design brief prompt andprompt()returns it.ConfigPrompttakes the template description, color direction, and font mood in the constructor, thenbuild()generates the prompt andprompt()returns it.ImagePrompttakes the parsed JSON config in the constructor, thenbuild()generates the prompt andprompt()returns it.- The prompt classes implement
PromptInterfaceand only build prompt text. AI execution is handled byPipethrough Laravel AI.
The final prompt text is also stored in the fixture example at
tests/Fixtures/cafe-restaurant/image.prompt.
Usage
Step 1: Build a design brief prompt
Input:
use Cable8mm\PromptWeaver\DesignBriefPrompt; use Cable8mm\PromptWeaver\Enums\Category; use Cable8mm\PromptWeaver\Enums\Format; use Cable8mm\PromptWeaver\Enums\ColorMode; $briefPrompt = new DesignBriefPrompt( category: Category::CAFE_RESTAURANT, format: Format::A45_POSTER, ); $briefPrompt->build(); $promptText = $briefPrompt->prompt();
Output:
[Role]
You are a creative director for a Wi-Fi signage template. Your job is to write ONE short design brief...
...
[Inputs]
- Category: Cafe/Restaurant
- Format: A4/A5 Poster
...
That returned text is not the final design brief yet. It is the prompt you send to a model.
Step 2: Turn the design brief into a config prompt
use Cable8mm\PromptWeaver\ConfigPrompt; // This would usually be the model's response to Step 1. $description = 'A cozy cafe-style Wi-Fi sign with warm cream and coffee-brown tones.'; $configPrompt = new ConfigPrompt( description: $description, colorDirection: 'warm brown and cream tones with soft gold accents', fontMood: 'rounded handwritten-style Korean font', format: Format::A45_POSTER, colorMode: ColorMode::MONO, name: '카페 시그니처', // optional ); $configPrompt->build(); $promptText = $configPrompt->prompt();
Output:
[Role]
You are a design-template config generator for a Wi-Fi signage print system called WiFi Note.
Your ONLY job is to output a single valid JSON object matching the schema below.
...
Step 3: Turn the config JSON into the final image prompt
ImagePrompt expects a structured config array shaped like the JSON schema produced by ConfigPrompt.
use Cable8mm\PromptWeaver\ImagePrompt; // This would usually be the parsed JSON response from Step 2. $config = [ 'canvas' => [ 'aspect_ratio' => Format::A45_POSTER->ratio(), ], 'style' => [ 'theme' => 'Warm cafe vibe with a soft analog feel', 'background' => 'cream paper texture with subtle grain', 'print_target' => 'black-and-white laser printer safe', ], 'content' => [ 'title' => [ 'text' => '와이파이 연결', 'x_pc' => 50, 'y_pc' => 10, 'style' => 'bold and friendly', ], 'wifi_icon' => [ 'x_pc' => 50, 'y_pc' => 20, 'width_pc' => 15, 'style' => 'simple line icon', ], 'message' => [ 'text' => '스캔하여 연결하세요.', 'x_pc' => 50, 'y_pc' => 62, ], 'footer' => [ 'text' => '제작: WIFI NOTE', 'x_pc' => 50, 'y_pc' => 96, ], ], 'placeholders' => [ 'ssid' => [ 'box_x_pc' => 50, 'box_y_pc' => 40, 'box_width_pc' => 70, 'box_height_pc' => 8, 'label' => 'SSID:', 'label_position' => 'outside_above', 'box_fill' => '#FFFFFF', 'box_fill_note' => 'solid flat white cutout, no background pattern bleeding through', ], 'password' => [ 'box_x_pc' => 50, 'box_y_pc' => 52, 'box_width_pc' => 70, 'box_height_pc' => 8, 'label' => 'PASSWORD:', 'label_position' => 'outside_above', 'box_fill' => '#FFFFFF', 'box_fill_note' => 'solid flat white cutout, no background pattern bleeding through', ], 'qr' => [ 'x_pc' => 50, 'y_pc' => 80, 'width_pc' => 28, 'style' => 'QR frame style with clean edges', ], ], ]; $imagePrompt = new ImagePrompt($config); $imagePrompt->build(); $promptText = $imagePrompt->prompt();
Iterative prompt-template workflow
The repository also includes editable prompt templates for working directly with a
chat-based AI. The shared image-preview instructions live in
prompts/preview.prompt, while layout-specific config
instructions live in stubs/.
To create or revise a layout config template:
- Open
prompts/config.promptin a chat-based AI. - Include
stubs/config.centered.promptas the canonical reference and ask the AI to create or revise exactly onestubs/config.<layout>.promptfile. - Save the result under
stubs/, keeping the existing schema and changing only the layout-specific composition values.
For example, save an editorial layout as:
stubs/config.editorial.prompt
To test that layout with an image-capable chat AI, run:
./weaver config-stub editorial
This inserts the complete stubs/config.editorial.prompt into the
CONFIG PROMPT section of prompts/preview.prompt and
copies the assembled image prompt to the macOS clipboard. Paste it into the
interactive AI, inspect the generated image, then revise the stub and run the
command again.
To inspect or pipe the assembled prompt without using the clipboard, run:
./weaver config-stub editorial --print
The currently registered layout names are centered, editorial, split,
qr-focus, and mini-square. A new layout name must also be registered in the CLI before it can be
used with ./weaver config-stub <layout>.
CLI Workflow
The repository includes the ./weaver wrapper, which invokes bin/prompt-weaver:
./weaver --help ./weaver --version
You can also invoke the PHP entry point directly:
php bin/prompt-weaver --help
The CLI uses one template code to identify a fixture. The checked-in example uses cafe-restaurant. The provider and model are runtime options used by pipe, not part of the template code.
All commands below operate on the same working folder under .weaver:
./weaver init cafe-restaurant
You can also specify the category and format when creating a fixture:
./weaver init cafe-restaurant --category="Office/Coworking" --format="A4/A5 Poster"
When run from a terminal, init interactively prompts you to select a category, format, and layout if you omit the corresponding options. Available layouts are centered, editorial, split, qr-focus, and mini-square. In non-interactive environments (tests, CI, pipes), the defaults are used automatically. You can select one explicitly with --layout=editorial.
The fixture reference is positional for commands such as brief, config, image, preview, and calibrate. config-stub accepts a registered layout name and assembles an image prompt from prompts/preview.prompt and stubs/config.<layout>.prompt. preview and calibrate also accept a direct fixture directory with --fixture=/path/to/fixture.
This creates:
.weaver/cafe-restaurant/manifest.json
tests/Fixtures contains checked-in reference data for the test suite. The CLI does not use it by default, so normal runs do not modify test fixtures.
The commands use the files created or saved in that folder:
./weaver brief cafe-restaurant ./weaver config cafe-restaurant ./weaver image cafe-restaurant ./weaver imagegen cafe-restaurant ./weaver calibrate cafe-restaurant ./weaver preview cafe-restaurant
brief reads manifest.json and saves the generated prompt as brief.prompt. config reads design-brief.json, takes its description, and saves the generated prompt as config.prompt. Save the model's response as raw.config.json. image reads raw.config.json and saves the generated prompt as image.prompt. calibrate detects the actual white text boxes and QR frame in image.png, then writes the calibrated final configuration to config.json without changing raw.config.json. preview uses config.json when it exists, otherwise it uses raw.config.json; its output format is selected by the output filename extension. config-stub assembles the interactive image prompt described above.
After the design has been generated, assign its final code from config.json's style.theme:
./weaver code 202608202113
The command converts the theme to kebab-case, keeps at most the first four words and 48 characters, and removes a partial trailing word when the length limit is reached. For example, Wabi-Sabi Minimalist becomes wabi-sabi-minimalist. It renames the matching .weaver/<code> folder, updates manifest.json, and renames and updates dist/<code> when an export already exists. If the target code is already in use, the command stops without renaming anything.
What each command outputs:
briefsaves the design-brief prompt you send to a model.configsaves the JSON-generation prompt you send after you have a template description.imagesaves the final image-generation prompt you can paste into your image model.imagegenreadsimage.promptand saves the generated image asimage.png.calibratewrites the finalconfig.jsonto match the actual text-box and QR-frame positions inimage.png.previewrenders a human-checkablepreview.pngor browser-basedpreview.htmlfor a fixture.chainprints all three prompts in one run for quick inspection.initcreates a new fixture manifest folder with the templatecodeand default values forcategory,format, andcolor_mode. Use--color-mode=Colorfor color output or--color-mode=Monofor monochrome output.coderenames a fixture from its current code to a kebab-case code derived fromconfig.json'sstyle.theme. It updates the fixture folder,manifest.json, and any matchingdist/<code>export.piperuns the full three-step pipeline end-to-end throughlaravel/aiand saves the prompts and intermediate JSON responses. Use--show-outputto print them. The default provider isopenrouterwith thegoogle/gemma-4-26b-a4b-it:freemodel; use--provider=openaito switch to OpenAI.exportpackages a manually generated PNG and the working config into a Laravel-readydist/<code>directory.config-stubassembles a registered layout stub into the image-generation prompt and copies it to the clipboard for interactive AI testing. Use--printto print it instead.config:validatevalidates a config JSON file against the required config structure and canvas aspect-ratio format.
For the complete command and option list, run ./weaver --help or ./weaver list.
Output Flow
This package is intended to be used as part of a multi-step generation pipeline:
Manual workflow
- Create a
DesignBriefPromptwith category and format, callbuild(), then retrieve the prompt viaprompt(). - Send that prompt to a model and capture the description.
- Create a
ConfigPromptwith the description, color direction, font mood, and optional template name, callbuild(), then retrieve the prompt viaprompt(). - Send that prompt to a model and parse the returned JSON.
- Create an
ImagePromptwith the parsed config, callbuild(), then retrieve the prompt viaprompt(). - Send the final text to your image model or image generator.
- Use the Laravel AI integration or
Pipeto send generated prompts to an AI model.
Automated workflow with pipe
The Pipe class automates the text-prompt portion of the pipeline by sending the design-brief and config prompts to an AI model via laravel/ai. It returns the final image-generation prompt. The imagegen command sends the saved image.prompt to a Laravel AI image provider:
use Cable8mm\PromptWeaver\Enums\Category; use Cable8mm\PromptWeaver\Enums\Format; use Cable8mm\PromptWeaver\Enums\ColorMode; use Cable8mm\PromptWeaver\Pipe; use Cable8mm\PromptWeaver\Laravel\LaravelAiClient; $pipe = new Pipe(new LaravelAiClient); $result = $pipe->run( category: Category::CAFE_RESTAURANT, format: Format::A45_POSTER, color: 'warm brown and cream', // optional colorMode: ColorMode::MONO, ); // Access all prompts and responses echo $result->briefPrompt; // Design brief prompt echo json_encode($result->briefJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); // Parsed design brief JSON echo $result->configPrompt; // Config generation prompt echo json_encode($result->config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); // Parsed config JSON echo $result->imagePrompt; // Final image generation prompt
The PipeResult object contains all three prompts plus the parsed intermediate JSON responses, making it easy to inspect or log each step of the pipeline.
Notes
DesignBriefPromptintentionally adds a small amount of randomness so the generated briefs feel less repetitive.ConfigPromptis strict about JSON structure so the next step can parse the output reliably.ImagePromptfocuses on layout, contrast, and print-safe composition.- The
tests/Fixtures/cafe-restaurant/folder shows one complete example of the chain, including the final image prompt.
Testing
The easiest way to test this package is to copy a checked-in fixture into a working directory, generate files there, then run Pest. The repository's reference fixtures remain unchanged.
1) Create a fixture folder
./weaver init cafe-restaurant
This creates .weaver/cafe-restaurant/manifest.json with the template code and default values for category, format, and layout. You can also pass --category, --format, and --layout to customize the manifest:
./weaver init cafe-restaurant --category="Office/Coworking" --format="A4/A5 Poster"
For example, select the editorial config layout with:
./weaver init cafe-restaurant --layout=editorial
When run from a terminal, init interactively prompts you to select a category and format if you omit --category / --format. In non-interactive environments (tests, CI, pipes), the defaults are used automatically. Run ./weaver --help to see the available categories and formats.
2) Generate the design-brief prompt
./weaver brief cafe-restaurant
The prompt is also saved automatically as .weaver/cafe-restaurant/brief.prompt. Send it to a model and save its JSON response as .weaver/cafe-restaurant/design-brief.json.
3) Generate the config prompt
./weaver config cafe-restaurant
The command reads design-brief.json, takes its description value, and saves the JSON-generation prompt as .weaver/cafe-restaurant/config.prompt. Send that prompt to a model and save its JSON response as .weaver/cafe-restaurant/raw.config.json.
4) Generate the final image prompt
./weaver image cafe-restaurant
The command reads raw.config.json and saves the final image-generation prompt as .weaver/cafe-restaurant/image.prompt.
5) Calibrate the config to the generated image
./weaver calibrate cafe-restaurant
This detects the actual white text boxes and QR frame in image.png. It writes the calibrated SSID/password box_y_pc values and QR x_pc, y_pc, and width_pc values to config.json, leaving the original raw.config.json unchanged.
When available, QR frame calibration uses the optional Python/OpenCV detector for contour-based square detection. The detector is managed with uv:
uv run --project . scripts/calibrate_qr.py --help
The first uv run creates the cached environment from pyproject.toml; subsequent runs reuse it. QR calibration uses the Python detector exclusively. If uv or OpenCV is unavailable, calibrate reports an installation error instead of using a less accurate PHP detector. Set PROMPT_WEAVER_UV to select a different uv executable, UV_CACHE_DIR to a writable persistent cache directory on a server, or PROMPT_WEAVER_PYTHON to bypass uv and use a Python interpreter directly.
6) Generate a preview image
./weaver preview cafe-restaurant
This creates .weaver/cafe-restaurant/preview.png using the final config.json when present, so you can inspect the SSID, password, and QR placement by eye. If raw.config.json or image.png changes, run calibrate again to regenerate the final config.
7) Generate a browser preview
./weaver preview cafe-restaurant --output=html
This creates .weaver/cafe-restaurant/preview.html using image.png, config.json, and fonts/AtkinsonHyperlegible-Regular.woff2 as external files. The HTML reads the SSID and password values from the JSON and renders the QR code in the calibrated position. The QR image is embedded in the HTML, so no additional JavaScript QR library is required. Keep the generated HTML in its working directory so its relative asset paths remain valid.
Because browsers commonly block fetch() from local file:// pages, serve the fixture directory through a local web server before opening the HTML:
php -S localhost:8000 -t .weaver/cafe-restaurant
Then open http://localhost:8000/preview.html. If raw.config.json, config.json, or image.png changes, run calibrate and regenerate preview.html so the calibrated coordinates and QR payload are refreshed.
8) Generate the image
Use the saved image prompt to generate image.png through the configured Laravel AI image provider:
./weaver imagegen cafe-restaurant
You can override the provider, model, or output path:
./weaver imagegen cafe-restaurant \ --provider=openai \ --model=gpt-image-1 \ --output=/path/to/generated-image.png
9) Export the generated image
./weaver export cafe-restaurant \
--image=/path/to/generated-image.png \
--output-dir=dist/cafe-restaurant
If --image is omitted, the command uses .weaver/cafe-restaurant/image.png. The command uses the final .weaver/cafe-restaurant/config.json; run calibrate first if it does not exist.
The export command validates that the manifest, design brief, and config exist, the image is a PNG, and its aspect ratio matches canvas.aspect_ratio. The resulting directory contains:
Typography in new configs is physical: use placeholders.ssid.font_size_pt and placeholders.password.font_size_pt (6–96 pt), with canvas.width_mm, canvas.height_mm, and canvas.dpi describing the print canvas. HTML previews use CSS points; PNG previews convert points using the configured physical width and the loaded raster width: pixels = points * image_width_px * 25.4 / (canvas.width_mm * 72). Existing configs using font_size_px remain supported as a legacy fallback and do not require migration, though converting them to points is recommended when the physical format is known.
dist/cafe-restaurant/
├── config.json
├── image.png
└── preview.png
If the fixture has a preview.png, it is copied as a thumbnail alongside the exported image.
To export every fixture under .weaver, use export-all. Each fixture's own
image.png is used and the output is written to dist/<code>:
./weaver export-all
Use --fixtures-root and --output-dir to change the input and output roots:
./weaver export-all --fixtures-root=.weaver --output-dir=dist
The exported config.json contains a metadata object with flattened manifest and design-brief fields. When the source config includes localized style metadata, it also includes an optional nested metadata.style object. Display labels for enum-backed values such as style.print_target should be resolved by the consuming service through the corresponding enum and translation files.
{
"schema_version": 1,
"metadata": {
"code": "cafe-restaurant",
"category": "Cafe/Restaurant",
"format": "A4/A5 Poster",
"color_mode": "Mono",
"name": "...",
"description": "...",
"color_direction": "...",
"font_mood": "...",
"style": {
"theme": "...",
"background": "..."
}
}
}
To validate an exported or generated config file independently, run:
./weaver config:validate path/to/config.json
Export config contract
The machine-readable contract for exported config.json files is available at
schemas/config.schema.json. It defines the required
top-level fields, metadata fields, layout structures, and value types.
The current contract uses schema_version: 1. Consumers should inspect this value
before deserializing the file. Incompatible structural changes increment the schema
version; optional fields may be added without changing the version.
Laravel applications can validate the JSON against this schema and then map it to a
consumer-owned DTO, such as a spatie/laravel-data class. The exported JSON remains
the integration boundary so consumers are not coupled to this package's PHP types.
These two files are intended to be imported by the Laravel service. The command does not delete existing files in the output directory, but it overwrites config.json and image.png.
9) Run the automated text pipeline
If you have an OpenRouter API key, you can run the automated brief/config/image-prompt pipeline. The default provider is openrouter with the google/gemma-4-26b-a4b-it:free model. You can change both defaults in .env:
The standalone weaver command automatically loads .env from the project root. Copy .env.example to .env and add your key once:
cp .env.example .env # edit .env and set OPENROUTER_API_KEY # PROMPT_WEAVER_PROVIDER=openrouter # PROMPT_WEAVER_MODEL=@preset/openrouter-free-presets ./weaver pipe cafe-restaurant
The --provider and --model options override the corresponding .env values for a single run. OpenRouter model IDs and presets are passed through unchanged.
The .env file is ignored by Git. Existing shell environment variables take precedence over values in .env. Laravel applications can continue using Laravel's own .env loading; the package does not load .env from its service provider.
While pipe is running, it displays progress for the design brief, config JSON, and image prompt stages. Use --no-progress when running it from a script or when you only want the completion message:
./weaver pipe cafe-restaurant --no-progress
./weaver pipe cafe-restaurant --api-key=sk-or-v1-...
Or with explicit options:
./weaver pipe \ --category="Cafe/Restaurant" \ --format="A4/A5 Poster" \ --color-mode=Mono \ --provider=openrouter \ --api-key=sk-or-v1-... \ --model=google/gemma-4-26b-a4b-it:free \ --color="warm brown and cream"
To use OpenAI instead, pass --provider=openai and an OpenAI API key:
./weaver pipe cafe-restaurant --provider=openai --api-key=sk-... --model=gpt-4o-mini
By default, the command displays progress and saves the generated files into the fixture directory without printing the full prompts or JSON responses. Use --show-output to print them as well:
./weaver pipe cafe-restaurant --show-output
The generated files are:
brief.promptdesign-brief.jsonconfig.promptraw.config.jsonimage.prompt
This command:
- Generates the design-brief prompt and sends it to the configured Laravel AI provider
- Receives the design-brief response as structured output
- Generates the config prompt and sends it to the model
- Parses the config JSON response
- Generates the final image prompt
- Saves the generated prompts and intermediate JSON responses to the fixture directory
9) Compare against fixtures
The repo already includes one complete example:
tests/Fixtures/cafe-restaurant/manifest.jsontests/Fixtures/cafe-restaurant/design-brief.jsontests/Fixtures/cafe-restaurant/raw.config.jsontests/Fixtures/cafe-restaurant/config.jsontests/Fixtures/cafe-restaurant/image.prompt
The integration test reads those files and checks that:
- The design-brief prompt is generated correctly.
- The config prompt includes the generated brief.
- The image prompt matches the saved
image.promptfixture. - The preview image can be generated from the fixture background without errors, with credential text and the QR code rendered in the calibrated area.
9.5) E2E test output
The E2E test writes files generated from real OpenRouter API calls to the ignored .weaver working directory:
.weaver/google-gemma-4-26b-a4b-it-free/
These fixtures are generated automatically when you run the E2E test:
composer test:e2e
The E2E test uses the OpenRouter API with the google/gemma-4-26b-a4b-it:free model and saves all prompts and responses to the working directory for inspection and debugging.
10) Run the tests
composer test
If you change prompt wording, update the checked-in reference fixtures first, then run the tests again.
Development
composer install
composer test
composer lint
License
MIT