Repository navigation
Releases: draz26648/flutter_craft_skills
Release list
2.2.0
2.2.0 (Codex support) is on main but hasn't been tagged. A release-readiness review turned up three blockers and a set of smaller fixes, all in this PR. Once it merges, main can be tagged 2.2.0.
Blockers
./install.sh --helpwas broken — a regression in 2.2.0. The usage heredoc was unquoted, so bash ran`.claude/skills`and the other backticked paths as commands. The output had four errors and read "Claude Code uses and .". CI missed it because--severity=warninghides SC2006.- 2.1.0 users had no upgrade path. The README now has an "Upgrading from 2.1.0" section, and the CHANGELOG entry is rewritten. Both cover three things:
- the marketplace rename from
draz-fluttertoflutter-craft-skills, which installs everything twice if both marketplaces are added - the
.claude/commands/flutter-adapt.mda vendored 2.1.0 install left behind .agents/winning over.claude/when a project has both profiles
- the marketplace rename from
flutter-adaptcould start without being asked. As a command it only ran when typed. As a skill withWritein its tools, an agent could run it on its own. It is now explicit-only on both hosts:disable-model-invocation: truefor Claude Code, andagents/openai.yamlwithallow_implicit_invocation: falsefor Codex.
Fixes
install.sh--project,--personaland--all-personalare mutually exclusive. Combining them used to silently drop one.--projectwith no path gives a clean error instead ofunbound variable.- Paths containing spaces work.
__pycache__folders are no longer copied into installed skills.- It prints the right invocation for a vendored install (
/flutter-adapt). - It warns about the stale 2.1.0 command, and
--forceremoves it. Only files containing that command's own text are touched.
check.sh--profilewith a path that doesn't exist exits2. It used to apply the defaults and could exit0.- When both
.agents/and.claude/profiles exist, the report notes that the.claude/one was ignored.
compare.pyexits2instead of1when Pillow or numpy is missing. A1means "the screen differs", so an agent would start changing widget code.- Skill text
- state-management told
providerusers to checkmountedon aChangeNotifier, which has no such property. - It claimed every stack deduplicates equal states;
setStateandChangeNotifierdon't. - Its Riverpod
refresh()example setstateright after anawait, breaking its own rule 2. - review-gate listed a check the script doesn't run.
- The architecture description still said "Cubit or Bloc" and "feature-first".
.claude/skillswas called "legacy" even though it is the installer's default.- The Claude Code invocation for vendored installs is now shown as
/flutter-adapt.
- state-management told
validate.pynow fails the build when:- the Claude and Codex manifests, or the marketplace entries, disagree on description, keywords, homepage, license or author (flutter-code-quality's already disagreed)
- the top CHANGELOG entry isn't the version in the manifests
- a skill links to another skill's file that
install.shcan't rewrite for vendored installs - a plugin ships a
commands/file
- CI
actions/checkoutandactions/setup-pythonmove to v7.- Flutter is pinned to 3.41.9 and ruff to 0.16.5.
- shellcheck runs at default severity over all four shell scripts, now including
test_check_sh.sh. .ruff_cache/is added to.gitignore.
Verification (local)
| Check | Result |
|---|---|
python3 scripts/validate.py |
12 skills, 0 warnings |
bash scripts/test_check_sh.sh |
64/64 (4 new) |
bash scripts/test_install_sh.sh |
112/112 (26 fail against the old installer) |
python3 scripts/test_compare.py |
21/21 (1 new) |
| shellcheck, default severity, all four scripts | clean |
ruff check scripts plugins (0.16.5) |
clean |
claude plugin validate on the repo and both plugins |
passes |
The v7 action versions and the pinned Flutter version are only tested by this PR's CI run. A Codex install has not been smoke-tested; the Codex CLI isn't installed on the machine this was prepared on.
Deliberately not included
These change the gate's result on projects that pass today, so under this repo's versioning rule they wait for 3.0.0:
- region-coded locales (
ar_EG) quietly turning the RTL check into a warning - untracked files and apps in a repo subdirectory not getting diff-scoped
- case-sensitive credential matching
compare.pycounting ignored pixels in its denominator- blocking on the asset check
The review-gate doc now describes the two scope limits in the meantime.
2.1.0
2.1.0
Additive. Nothing's verdict changes: a project with no .claude/flutter-profile.yaml gets
byte-for-byte the behaviour it got under 2.0.1, and there is a test asserting it.
Until now the skills described exactly one stack — freezed, Cubit, an AppTokens
extension, ARB — and the README's answer for anyone else was to fork the repo, which
trades the convention problem for never receiving an update again. That was the real
ceiling on who could use these.
Added
.claude/flutter-profile.yaml, a project profile. Nine fields —state,models,
structure,di,routing,tokens,l10n,locales,strictness— that say what
the project actually does. Skills read it before applying any rule that names a package,
a folder, or a severity. Every field defaults to what the skills already assumed, so the
file is only worth writing where a project differs. Spec:
skills/architecture/references/flutter-profile.md, duplicated into
flutter-design-fidelityso each plugin stands alone./flutter-code-quality:flutter-adaptgenerates the profile by inspecting
pubspec.yamlandlib/. It records the evidence for each field it writes, marks what
it could not determine rather than guessing, and refuses to pick a winner on its own when
a project has two state stacks mid-migration.- Riverpod support.
state-managementis now stack-agnostic: four rules that hold
everywhere — exhaustive state shape, a liveness guard after everyawait, narrow
rebuild subscriptions, noBuildContextin the state holder — with the code for them in
references/bloc.mdandreferences/riverpod.md, selected by the profile.provider,
signals, andsetstateget a translation table rather than a full reference. check.sh --profile PATH, and aProfile:line in the report naming what it
applied. A skipped check prints as skipped with the setting that caused it — a check
that vanishes silently is indistinguishable from one that passed.validate.pynow resolves${CLAUDE_PLUGIN_ROOT}/skills/<skill>/references/<file>
links, checkscommands/frontmatter, and fails the build if the two copies of the
profile spec drift apart. The duplicate top-levelskills/tree removed in 2.0.0 drifted
because nothing compared the copies; this duplication is checked.codebase-conventions, an eleventh skill. Makes generated code look like the code
already in the repository: search for an existing component by role before building a
second one, resolve typography through the project's own source, reference only assets
that exist and are declared, and match the file, class, and import naming already in
use. It triggers on every code-writing task, because the failure it prevents is
invisible — the code compiles, reads well in isolation, and quietly duplicates something
that already existed under a different name..claude/flutter-conventions.md, written by/flutter-adaptalongside the profile.
It records where this project keeps its shared widgets, typography, and assets, and
what its naming rules are — never a list of components. An enumerated inventory is stale
within a week, and a stale list is worse than none because it gets trusted, so the search
that would have found the real answer never runs.- Three checks in the gate, all warnings. Inline
TextStyle(in widget code; an asset
path that does not resolve to a file on disk; a widget class defined in more than one
file when the current change is part of the duplication. They warn rather than block
because a new blocking check would fail projects that pass today, which this repo counts
as a breaking change. The asset check is the one worth promoting in 3.0.0 — a path that
does not resolve is a runtime crash, not a matter of style, and nothing else in the
toolchain looks at it. - 33 new assertions in
test_check_sh.shcovering every profile-driven severity change
and each new check, including thatstrictness: warnstill blocks committed credentials
and that a duplicate widget outside the current change is not reported.
Changed
review-gatehonours the profile.tokens: noneskips the colour,EdgeInsets,
and radius checks;tokens: theme_onlyorconstantsdrops them to warnings;l10n: noneskips the hardcoded-string check; a declared LTR-onlylocalesdrops the
directional-inset check to a warning;strictness: warndrops every convention finding.
Formatting, static analysis, failing tests, and committed credentials block under every
profile.- An unrecognised profile value exits
2rather than falling back to the default.
Readingriverpoodasblocwould enforce the opposite of what the project asked for
with nothing in the output to say so. localeshas no default. Leaving it out keeps the RTL check blocking, because the
gate cannot tell whether the project ships an RTL language and the safe assumption is
that it might. Declaringlocales: [en]is what takes the downgrade.install.shcopies the profile spec into each vendored skill that references it and
rewrites the link, since${CLAUDE_PLUGIN_ROOT}is unset outside a plugin install — the
same failure mode$SKILL_DIRhad in 2.0.0. It also installs/flutter-adapt.
Fixed
review-gate's description advertised a coverage check that does not exist. The
script has never measured coverage. Removed from the description rather than added to the
script, which is a separate decision.- One of the two canary injections in
test_check_sh.shhad no anchor assertion. The
malformed-pattern canary asserted its anchor was present; the missing-sample one did not,
so a rename anywhere near it would have turned that test into a no-op that still reported
a pass. Both assert now.
2.0.1
2.0.1
Fixed
-
The "a broken check fails loudly" guarantee only held on macOS. 2.0.0 detected a
malformed pattern by testing for a grep exit code of 2 or higher. grep implementations
disagree: given a PCRE lookahead, ugrep exits 2, but GNU grep prints a warning and exits
1, which is indistinguishable from "no violations found". Since GNU grep is what Linux
CI runs, the case the guarantee existed for was still silently green. Every check now
declares a line its pattern is required to match, and a check that cannot find its own
known violation reports itself broken rather thanok. This is behaviour-based, so it
holds whatever grep is installed.No check shipped in 2.0.0 was actually broken; this closes the hole that would have let
a future one hide. Caught by the canary test added in 2.0.0, running on Linux CI.
2.0.0
2.0.0
Breaking, because upgrading changes the verdict you get on code you have already shipped.
Nothing here is a new rule; it is the existing rules finally being enforced, plus two
scripts that used to fail open now failing closed.
Breaking
review-gatewill fail projects that passed under 1.0.0. The TODO/FIXME check never
ran (see below), the force-unwrap,debugPrint, commented-out-code and hardcoded-string
checks were documented but never implemented, and all of them are live now. Run the gate
once before upgrading anything else so the new findings arrive on their own.compare.pyerrors instead of rescaling on a size mismatch. A pipeline that quietly
compared a 2x export against a 3x capture now exits2and explains that the export
scale is wrong. Pass--allow-resizeto keep the old behaviour, though the diff it
produces was always noise.compare.pyfails localized defects that used to pass. The new--max-cluster
threshold (default 0.25) grades the worst cell of a 12×12 grid alongside the global
share. Screens that scraped past the 2% global threshold on a single wrong-colored
element will now fail — correctly.- Exit code
2is new on both scripts and means the check could not run, not the
code is fine. Any CI step branching on!= 0is unaffected; one branching on== 1
needs updating. - The top-level
skills/directory is gone. Only reachable by copying out of a git
clone by hand, which the README has called the pre-v1.0.0 layout since 1.0.0. Marketplace
andinstall.shusers are unaffected.
Fixed
review-gatereported a passing check that could never fail. The TODO/FIXME rule
usedgrep -E 'TODO(?!\()|FIXME'— a PCRE negative lookahead handed to POSIX ERE. It
exited 2 on every run, the call site sent stderr to/dev/nulland ended in|| true,
and the gate printedok: TODOs carry a ticket referencefor its entire life. Rewritten
as an ERE match minus the ticket-carrying hits, andcheck_patternnow distinguishes
grep's three exit states so a malformed pattern fails loudly instead of reporting clean.review-gatefailed on correct code.Color(0x...)and numericEdgeInsetswere
blocking with no path exclusions, so a project's own token layer — the one place those
literals belong — failed by construction. Generated files (*.g.dart,*.freezed.dart,
and similar), tests, andtheme/,tokens/,design_system/are now carved out.review-gatedocumented checks it did not run. SKILL.md listed eight forbidden
patterns; the script implemented five, one of them broken, plus one the doc never
mentioned. The two lists are now identical, with severities stated.review-gatecould not name a failing test.flutter testoutput went to
/dev/null. Failures are captured and reported.review-gatereported clean when it had not run. No preconditions — pointed at a
non-Flutter directory it produced an empty, passing report. It now exits2.visual-verificationwrote a diff image that measured 98% pure black.
ImageChops.differenceis a near-zero delta wherever the images agree, so the artifact
the skill told you to open carried no information. It is now the capture, faded, with
differing pixels burned in as red.visual-verificationcrashed with a raw traceback on a missing or unreadable file.
Clean message, exit2.$SKILL_DIRis not a real variable. Both skills instructed the model to resolve it
by hand. Replaced with${CLAUDE_PLUGIN_ROOT}, which Claude Code sets and the shell
expands on its own.validate.pyrejects any reappearance of$SKILL_DIR.install.shcould never update a vendored skill. It skipped anything already
present with no override, so a user who ran it once was pinned forever. Added--force.
Added
--ignore-region X,Y,W,Honcompare.py(repeatable) for inherently dynamic content,
so a status bar clock no longer forces the threshold up for the whole screen.--skip-testsand--alloncheck.sh.- A "judgement the script cannot make" section in
review-gate, naming the findings that
need the diff read rather than grepped — whether a golden was reviewed or just
regenerated, whether a force-unwrap's justification is true, whether a new dependency
was necessary. - Documentation of the exclusion carve-outs in
review-gate, so the agent knows why some
literals are not flagged rather than assuming the check is broken. scripts/validate.py— frontmatter, name/directory agreement, description limits,
manifest/marketplace version agreement, duplicate skill names, danglingscripts/and
references/links, and a guard against the duplicate tree returning.scripts/test_check_sh.shandscripts/test_compare.py— 39 behavioural assertions,
including a canary that injects a malformed pattern and asserts the gate refuses to
reportok..github/workflows/validate.ymlrunning all three plusshellcheckandruff.
Removed
- The duplicate top-level
skills/tree. It mirroredplugins/*/skills/, nothing
synced them, and two of ten files had already drifted within a handful of commits.
install.shread fromplugins/, so the mirror was dead weight that could only go
stale.plugins/is now the only source of truth.