Skip to content

Latest commit

 

History

History
345 lines (262 loc) · 16.2 KB

CONTRIBUTING.md

File metadata and controls

345 lines (262 loc) · 16.2 KB

🦄 Contributing to VGV AI Flutter Plugin

First of all, thank you for taking the time to contribute! 🎉👍 Before you do, please carefully read this guide.

Getting Started

  1. Fork the repository and clone your fork locally.
  2. Create a new branch from main for your work.
  3. Open the project in your editor of choice — any text editor works.

Types of Contributions

Contribution Where
New skill skills/<skill-name>/SKILL.md
Improve an existing skill Edit the relevant skills/*/SKILL.md or reference.md
Eval cases evals/<skill-name>/<case-name>/
Hooks hooks/ directory
Bug reports & feature requests GitHub Issues

Adding a New Skill

1. Create the skill file

Create skills/<skill-name>/SKILL.md. The file must begin with YAML frontmatter:

---
name: <skill-name>
description: When this skill should be triggered — be specific.
allowed-tools: Read Glob Grep
argument-hint: "[file-or-directory]"   # optional
---
Field Required Rules
name Yes Lowercase letters, numbers, and hyphens only; no leading, trailing, or consecutive hyphen; 1-64 chars; must match the skill's directory name (enforced in CI by skills_lint)
description Yes Describes when the skill should be triggered
allowed-tools No Space-separated list of tools the skill may use; a Claude Code permission hint, not a hard cap
argument-hint No Placeholder hint shown to the user

After the frontmatter, structure the file as:

  1. H1 title — human-readable skill name
  2. Core Standards — enforced constraints, always first
  3. Content sections — architecture, code examples, workflows, anti-patterns

Also create the Codex sidecar skills/<skill-name>/agents/openai.yaml with the skill-picker metadata (see Cross-harness portability → Codex sidecar):

interface:
  display_name: "Skill Name"
  short_description: "One short line for the picker"

2. Add eval cases

Create one case directory per prompt under evals/<skill-name>/ — prompts that prove the skill actually changes what Claude produces. Nothing has to be registered; cases are discovered from the directory tree. See Eval Cases below.

3. Update plugin.json tags

Add relevant keywords to the keywords array in .claude-plugin/plugin.json.

4. Update the README skills table

Add a row to the skills table in README.md. The skill name must link to the SKILL.md file:

| [**Skill Name**](skills/<skill-name>/SKILL.md) | Short description of what the skill covers |

5. Update AGENTS.md repository structure

Add the new skill directory and files to the repository structure tree in AGENTS.md.

Eval Cases

Evals ask one question: does Claude route to the skill, and does the output follow it? claude plugin eval runs every case twice, once with this plugin loaded and once with no plugin at all, so a grader that passes in both arms is measuring the model rather than the skill.

claude plugin eval . --scaffold

Adding a skill means adding a evals/<skill>/<case-name>/ directory per case, each with a prompt.md, a case.yaml, and one file per grader under graders/. There is no central registry to update — cases are discovered from the tree — and the new case's fixture.sh is a symlink to evals/_fixture/fixture.sh.

evals/README.md is the single source of truth — the case format, the grader reference, prerequisites, what makes a case worth having, and what these evals deliberately do not cover. Read it before writing a case, and add new eval documentation there rather than here.

Skill Writing Guidelines

  • Use clear directives — no soft language ("consider", "prefer"). Say "Use X" or "Do not use Y".
  • Fence all code blocks with language identifiers (e.g., ```dart).
  • Provide complete, copy-pasteable snippets — not fragments.
  • Reference packages by full name (e.g., package:mocktail, not just "mocktail").
  • Show anti-patterns alongside correct patterns when helpful, so readers understand both what to do and what to avoid.

What Belongs in a SKILL.md

A skill earns its tokens by carrying what the model cannot infer: the opinions this team holds. Reference material that merely restates the framework displaces those opinions and slows routing.

Every section you write gets one of three verdicts.

KEEP in SKILL.md — a VGV opinion the model cannot infer:

  • Choices among valid alternatives (mocktail over mockito; dart_package over flutter_package for data-layer packages; go() over push() by default)
  • VGV-invented conventions (pumpApp, AppMotion, AppSpacing, TestTag, the private _Mock rule, the four-layer dependency direction)
  • Judgement calls the model would get wrong by default (the animations decision tree; holding the implicit form when a controller is requested by name)
  • Harness facts (hook-enforced tool routing, MCP tool arguments, cross-harness fallbacks)
  • Safety and compliance rules

DEMOTE to references/ — correct and useful, but recallable or situational:

  • API glossaries and lookup tables for framework surface
  • Long code samples demonstrating framework mechanics rather than a VGV pattern
  • Deep-dive setup only one workflow in the skill needs

DELETE — no new information at any load time:

  • A ## Quick Reference table that restates ## Core Standards in other words
  • Anti-pattern rows whose "Correct Approach" column is verbatim a Core Standard
  • Reference links repeated in more than one place in the same file

Target shape: SKILL.md at or under ~150 lines — frontmatter, H1, ## Core Standards, the VGV-specific sections, ## Additional Resources. One link per reference file, in one place. Procedural skills that drive a tool loop (green-gate, dart-flutter-sdk-upgrade) may run longer; the steps are the content.

Two things this rubric does not apply to:

  • The frontmatter description. It is the router and loads before the body, so every trigger phrase is load-bearing — do not trim it for length (see Cross-harness portability → Description length).
  • Genuinely important areas. Hard constraints stay hard. Directive density is not the defect; generic reference material is.

Cross-harness portability

Skills are authored for Claude Code but target the Agent Skills open standard, so they should degrade gracefully on non-Claude harnesses (Codex, Gemini CLI, OpenCode) without changing Claude Code behavior. Under that standard a skill is a static instruction set: the agent matches its description, then reads the body — there is no argument or template substitution. The rules below keep a skill working on both.

  • $ARGUMENTS / ${CLAUDE_SKILL_DIR} — Claude Code conveniences, not spec features, so a body that uses them must still work unsubstituted. Always pair $ARGUMENTS with a fallback that fires when it is empty or still shows the literal text, and ask the user (or read the conversation) instead. For bundled files prefer a relative path from the skill root (scripts/x.sh).
  • Frontmatter — an agent silently skips a skill with malformed frontmatter. Keep the opening --- on line 1, close the block, and include a non-empty name: (kebab-case, matching the directory name) and description:. Plugin extras (argument-hint, effort, model) are not spec fields but are ignored as unknown keys — keep them top-level. The Skills Lint CI job enforces the spec on every PR (skills_lint.yaml).
  • Description length — description carries the whole trigger surface. The spec caps it at 1024 characters and skills_lint hard-fails an overrun. Keep it to trigger phrases and scope; leave teaching material to the body. Do not assume a sentence is redundant because the body repeats it — routing happens before the body is read. Re-run a skill's eval cases after trimming its description.
  • MCP references — this plugin registers dart and very-good-cli in .mcp.json, the primary execution path on Claude Code; do not weaken the hook-enforced routing there. Those hooks and servers may be absent on other hosts, so every skill that drives an MCP tool must name the equivalent very_good / dart / flutter CLI fallback and never block when the server is missing ("use the MCP tool if available; otherwise Bash").
  • Subagents — not part of the standard. On a host without a subagent mechanism, the flutter-reviewer agent's preloaded standards still apply — run the review inline.
  • AskUserQuestion / allowed-tools — Claude Code conveniences. Carry an inline fallback for structured questions (drop to plain numbered text where the host has none), and treat allowed-tools as a permission hint, not a hard cap.
  • Own your references — a skill's reference files live in its own references/ directory. Do not share a reference across skills by symlink or ../other-skill/… link; those do not survive every install path.
  • Codex sidecar (agents/openai.yaml) — every skill ships one beside its SKILL.md with the picker metadata (interface.display_name, interface.short_description). The SKILL.md body stays the source of truth. Add one for every new skill.
  • Invocation — every skill here is model-invoked; there is no when_to_use field (only Claude Code read it). To make a skill user-invoked, set disable-model-invocation: true in the frontmatter and policy.allow_implicit_invocation: false in its agents/openai.yaml — both harnesses or neither.

Testing Locally

Editing a skill or hook and pushing straight to a PR only tells you the files are valid, not that they work correctly. Load your working copy into a real Claude Code session and exercise it before you commit.

Prerequisites

  • Claude Code CLI installed (npm install -g @anthropic-ai/claude-code).
  • Dart SDK and jq on your PATH — the hooks need both.
  • Very Good CLI ≥ 1.5.0 (dart pub global activate very_good_cli) for the Very Good CLI MCP server tools.

See the README Hooks and MCP Integration sections for the full prerequisite details.

Load your local copy

From the repository root, launch Claude Code pointed at this directory:

claude --plugin-dir .

--plugin-dir loads the plugin for that session only, needs no install or marketplace, and overrides any marketplace-installed copy of the same plugin. ${CLAUDE_PLUGIN_ROOT} (used throughout hooks/hooks.json) resolves to the directory you pass, so the hook script paths resolve correctly.

Verify each component loaded

Component How to verify
Skills Run /help. Skills appear namespaced as /vgv-ai-flutter-plugin:<skill> (e.g. /vgv-ai-flutter-plugin:bloc). Invoke one to confirm it triggers.
MCP servers Run /mcp. Confirm dart and very-good-cli both show connected.
Hooks Have Claude Edit or Write a .dart file and confirm analyze.sh and format.sh run. Launch without Very Good CLI to see the SessionStart warning fire.

Iterate on changes

After editing a SKILL.md, a hook script, or .mcp.json, restart the claude --plugin-dir . session to guarantee the change is picked up. Changes to .claude-plugin/plugin.json always require a restart. Edits to the hook .sh scripts take effect on the next matching tool call with no restart, since each hook runs the script fresh.

Rehearse the real install (optional)

To mimic the marketplace install flow without pushing anything, register a throwaway local marketplace. Create .claude-plugin/marketplace.json in a temp directory with an absolute path to this repo:

// /tmp/vgv-test-marketplace/.claude-plugin/marketplace.json
{
  "plugins": [
    {
      "name": "vgv-ai-flutter-plugin",
      "source": {
        "type": "directory",
        "path": "/ABSOLUTE/path/to/vgv-ai-flutter-plugin"
      }
    }
  ]
}

Then, inside a session:

/plugin marketplace add /tmp/vgv-test-marketplace
/plugin install vgv-ai-flutter-plugin

Validate before you push

Run the same check CI runs, from the repository root:

claude plugin validate .

This validates the manifest, skill frontmatter, hook JSON, MCP config, and file references. It is static, so it confirms structure but does not replace the live checks above.

Troubleshooting

Symptom Likely cause Fix
Skill missing from /help Invalid frontmatter, or name doesn't match the folder Run claude plugin validate . and fix the reported error
MCP server "executable not found" dart or very_good not on PATH Install the SDK / activate the CLI, then verify with which
Hook never fires jq not installed, or script lacks +x / a shebang Install jq; chmod +x the script and add #!/bin/bash
${CLAUDE_PLUGIN_ROOT} not resolving Session not launched via --plugin-dir (or restart pending) Restart with claude --plugin-dir . from the repo root
Local marketplace won't install source.path is relative Use an absolute path in marketplace.json

CI Checks

Every pull request runs the following checks automatically:

Check What it does Config
Markdown quality Lints all *.md files with markdownlint-cli2 config/custom.markdownlint.jsonc
Spelling Runs cspell on all *.md files config/cspell.json
Skills Lint Validates every SKILL.md's frontmatter, structure, and linked paths against the Agent Skills spec, so a malformed skill fails the build instead of silently vanishing on another host skills_lint.yaml
Plugin validation Validates and test-installs the plugin claude plugin validate .
Script tests Runs the hook scripts' own test suites hooks/scripts/*_test.sh

Evals do not run on a pull request. They call real models, so they run after a merge to main instead, scoped to the skills that changed:

Check What it does Config
Evals (post-merge) Runs the eval cases for the changed skills, with-plugin arm only. Advisory, never blocking .github/workflows/evals.yaml

That means a regression is reported after the merge rather than before it, which is a deliberate trade: a single eval run is too noisy to gate on, and running the full suite on every push to a PR would cost more than it saves. Run the cases for the skill you touched locally before opening the PR — see Eval Cases.

If the spelling check flags a legitimate word, add it to config/cspell.json in the words array.

Commit Convention

Use Conventional Commits with the format:

type(scope): description
Type When to use Example
feat New skill or feature feat: add bloc skill
fix Fix an error or incorrect guidance fix: correct GoRouter redirect example
docs Documentation-only change docs: add logo to README
chore Maintenance, CI, tooling chore: update cspell config
refactor Restructure without changing behavior refactor: reorganize testing skill sections
ci CI pipeline changes ci: add manifest validation step

PRs are squash-merged with the PR title as the commit message, and release-please builds the changelog from those titles. The PR title must follow Conventional Commits.

Pull Requests

  • Branch from main.
  • Use a Conventional Commits PR title, for example feat: add navigation skill.
  • Keep PRs focused — one skill per PR for new skills.
  • Fill out the PR template completely.
  • Ensure all CI checks pass before requesting review.
  • Link any related issues in the PR description.