@mizchi/vlmkit-plan
v0.7.0
Published
Planner contract for Playwright test generation: turns a user story, seed test, PRD, and UI observations into a Markdown test plan.
Readme
@mizchi/vlmkit-plan
Planner contract for Playwright test generation. It turns a user story, seed
test, PRD, and observed UI facts into a Markdown test plan under specs/.
This is not a full replacement for Playwright's official planner agent. It is a library layer for projects that want the same planner-shaped artifact without depending on a specific agent runtime.
import { createPlan } from "@mizchi/vlmkit-plan";
async function main() {
const plan = await createPlan({
title: "Guest Checkout",
request: "Plan coverage for guest checkout.",
seed: { path: "tests/seed.spec.ts" },
observations: [{ roles: ['button "Pay now"', 'textbox "Email"'] }],
}, { provider: "anthropic" });
if (plan.diagnostics.length) {
throw new Error(`Invalid plan:\n${plan.diagnostics.join("\n")}`);
}
console.log(plan.markdown);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});diagnostics is part of the contract. Treat non-empty diagnostics as a failed
planner run and ask the model to regenerate before passing the plan to a
generator.
Use createPlanWithRetry when you want that regeneration loop built in:
import { createPlanWithRetry } from "@mizchi/vlmkit-plan";
const plan = await createPlanWithRetry(input, { provider: "anthropic" }, undefined, {
maxAttempts: 2,
});For stricter downstream validation, use the structured JSON contract and render it to the same Markdown format:
import {
createStructuredPlan,
structuredPlanToLocatorInventory,
} from "@mizchi/vlmkit-plan";
const planned = await createStructuredPlan(input, { provider: "anthropic" });
const locatorInventory = planned.plan
? structuredPlanToLocatorInventory(planned.plan, input.observations)
: undefined;locatorInventory can be passed to @mizchi/vlmkit-generate to detect locator
hallucinations in generated tests. The structured planner treats this inventory
as observed data only: if no observations are supplied, inventory entries are
diagnostics rather than trusted facts. If observations are supplied and the model
omits locatorInventory, vlmkit-plan fills it deterministically from the
observed roles, labels, test IDs, and text entries.
Role inventory entries are canonicalized before rendering/export. Equivalent
forms such as button "Pay now", button 'Pay now', button: Pay now, and
role=button[name='Pay now'] are treated as the same observed role.
Planner scope defaults to smoke, which keeps output to one primary scenario.
Use focused for at most two scenarios, or full when the request explicitly
needs broader coverage.
When provider is omitted, vlmkit-plan uses VRT_LLM_PROVIDER first, then
available API keys in this order: Anthropic, OpenRouter, Gemini. With no key
signals it falls back to OpenRouter.
CLI
vlmkit-plan \
--title "Guest Checkout" \
--request-file specs/checkout.request.md \
--observations specs/checkout.observations.json \
--out specs/checkout.plan.md \
--structured-out specs/checkout.plan.json \
--locator-inventory-out specs/checkout.locators.json \
--scope smoke \
--provider anthropic \
--max-attempts 2The CLI exits 2 and does not write --out when diagnostics remain after the
retry budget. Passing --structured-out or --locator-inventory-out makes the
CLI use the structured planner contract; the locator inventory file can be passed
directly to vlmkit-generate --locator-inventory.
