craft-analyze
Post-cycle analysis — QA, UX, Creative, and Style audits using MCP browser tools.
Transforms an annotate record into a story. The annotate run IS the extraction - no Explore agent, no gap-fill interview. The user pointed at a live page, watched changes land on it, and approved what they saw. Scope and data shape still go through the normal story machinery
> /plugin marketplace add drobins25/craft > /plugin install craft@craft
How it fires
How this command gets triggered: by you, by Claude, or both.
/story-from-annotateContext preview
What this command does when you run it.
Transforms an annotate record into a story. The annotate run IS the extraction - no Explore agent, no gap-fill interview. The user pointed at a live page, watched changes land on it, and approved what they saw. Scope and data shape still go through the normal story machinery
Transforms an annotate record into a story. The annotate run IS the extraction - no Explore agent, no gap-fill interview. The user pointed at a live page, watched changes land on it, and approved what they saw. Scope and data shape still go through the normal story machinery (content-spark and alignment run on the created story like any other).
**The record's `session.css` holds every change shown live, one keyed block per pin, and it is exactly what the user saw on the page.** Rule: session.css values port verbatim for how things look - implementers never reinterpret a look from appearance. A block that stands in for content (text swapped through `content:`, a new piece drawn in CSS) is not a look: it graduates as a Scope requirement quoting the pin, with what was shown live, never as a binding row or ported CSS. Read the CSS to tell which blocks those are; there is no list of kinds. Pin selectors are anchors for finding the element, never ported as structural selectors: a selector like `div.toolbar > button.save` breaks the moment the markup shifts, while the rule's values stay true.
This flow asks no questions through a widget - there is no AskUserQuestion anywhere in it, because the annotate story exit reads it inline from a flow that forbids widgets. Where a choice is needed, ask in plain conversation.
If the caller handed a specific record path (the usual case - this flow was entered from the annotate story exit), use it. Otherwise list open records that were not cut short (the same list `story-from-craft-designs.md` searches). The project is `$CRAFT_PROJECT_ROOT` when set, else the folder the before-init lookup prints:
ROOT="${CRAFT_PROJECT_ROOT:-}"
[ -n "$ROOT" ] || ROOT=$(bash -c 'source "$1" >/dev/null 2>&1; craft_before_init_root' _ "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/find-workshop.sh")
ROOT="${ROOT%/}"
for r in "$ROOT"/.craft/annotations/*/record.md; do
[ -f "$r" ] || continue
if grep -q '^status: open' "$r" && ! grep -q '^outcome: error' "$r"; then echo "$r"; fi
doneOne record: use it, confirm conversationally ("Building the story from [name]"). Several: ask conversationally which one - no AskUserQuestion; the fork already spent the flow's question budget.
Read the full record: frontmatter, `## Pins` (the reading list), `## Pin Data` (the json fence, the source of truth for selector, component, box, page and `resolved`, the location the run found for the pin or null), and `session.css` beside it.
If the record's `created` is more than a few days old, re-verify the surface still exists as annotated BEFORE any pre-fill: for each pin's component name, confirm the component still exists in source; for each selector's classes, confirm they still appear; for each pin with a `resolved` key, confirm each resolved file still exists and the rule text still appears in it. Drift is surfaced to the user first ("the pin names `SaveButton`, which no longer exists - it looks like `PrimaryButton` now") - they decide whether the record still stands or the story should absorb the drift. Never silently pre-fill a story against a surface that no longer matches.
A record filed in this session skips this phase.
Target: `.craft/backlog/[story-name].md`, standard frontmatter (see craft-story-new Step 10) plus the backlink:
annotate: [record name, e.g. <date>-hero-polish]
When you were entered from the annotate story exit rather than from story-new, Read the Step 10 frontmatter block from `${CLAUDE_PLUGIN_ROOT}/commands/craft-story-new.md` first - this reference does not carry it.
Type is almost always `ui`: `type: ui`. Set `alignment: pending` - the alignment check fires at implement time as usual.
**Pre-filled sections:**
| Part | Role/State | Token | Value/Source |
|------|------------|-------|--------------|
| Save button | default | - | session.css `annotate-key 1728000000000` - `.toolbar .save { padding: 12px 20px }`; anchors: selector `div.toolbar > button.save`, component `SaveButton` |The Value/Source cell still names the session.css rule; the resolved location goes in Reference Materials, never in this cell. The source is the rule, never the selector. Selector and component are anchors that help the implementer find the element in source; a null component is simply omitted. Planning validation requires every non-TBD row bound by a `[visual-source:]` contract, so partial adoption of the sheet becomes a blocked plan, not a silent omission.
**Annotation files:** - [absolute path]/r
Stop Vibing. Start Crafting. A Claude Code plugin that acts as an intelligent harness for your development workflow: your codebase is read-only by default, every change passes through a Write Gate as planned and approved work, and craft tracks your project's
Post-cycle analysis — QA, UX, Creative, and Style audits using MCP browser tools.
Point at anything in a live browser tab - every pin gets a live attempt on the page the moment you SEND it, and the session keeps…
Consult a craft agent. Routes your question to the best mind in the workshop - not a menu, a recommendation.
Agent crystallization command. Studies a tool, role, or person and produces a portable 9-section agent that inhabits the domain -…
File a bug without fixing it, from a person mid-conversation or an agent mid-run. One door, a complete record on disk, nobody…