diff --git a/.github/scripts/check-frontmatter.sh b/.github/scripts/check-frontmatter.sh
new file mode 100755
index 0000000..473314a
--- /dev/null
+++ b/.github/scripts/check-frontmatter.sh
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+# Frontmatter portability guard.
+#
+# Covers the two silent-skip gaps validate-skill@v1 leaves open:
+# 1. A UTF-8 BOM before the opening `---` passes validate-skill but makes
+# Gemini CLI (and any parser anchoring /^---/) skip the file silently.
+# Checked for every skills/**/SKILL.md and agents/**/*.md.
+# 2. agents/**/*.md frontmatter has no validator at all (validate-skill
+# hard-requires the filename SKILL.md; `claude plugin validate` exits 0
+# on a broken agent block). Checked here: frontmatter first, closed,
+# non-empty name + description, name matches the filename stem.
+#
+# Skill frontmatter content is deliberately NOT re-validated here —
+# Flash-Brew-Digital/validate-skill owns that in the validate-skills CI job.
+#
+# Parsing is plain-text matching, not YAML-aware: keys are expected as bare
+# `name: value` / `description: value` lines (the only forms used in this
+# repo). Quoted values would be flagged as mismatches — keep values unquoted.
+
+set -euo pipefail
+
+cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
+
+errors=0
+
+fail() {
+ echo "error: $1" >&2
+ errors=$((errors + 1))
+}
+
+# Strip trailing CR (CRLF files) and trailing whitespace.
+trim() {
+ sed 's/[[:space:]]*$//'
+}
+
+# --- 1. BOM guard (skills + agents) ---------------------------------------
+# find -L so symlinked SKILL.md files are checked too.
+while IFS= read -r file; do
+ if [ "$(head -c 3 "$file" | od -An -tx1 | tr -d ' \n')" = "efbbbf" ]; then
+ fail "$file starts with a UTF-8 BOM — Gemini CLI silently skips it"
+ fi
+done < <(find -L skills -name SKILL.md -type f; find -L agents -name '*.md' -type f)
+
+# --- 2. Agent frontmatter (agents only) -----------------------------------
+while IFS= read -r file; do
+ if [ "$(head -n 1 "$file" | trim)" != "---" ]; then
+ fail "$file: frontmatter must start on line 1 with ---"
+ continue
+ fi
+ close_line=$(awk 'NR > 1 && $0 ~ /^---[[:space:]]*$/ { print NR; exit }' "$file")
+ if [ -z "$close_line" ]; then
+ fail "$file: frontmatter block is never closed with ---"
+ continue
+ fi
+ block=$(sed -n "2,$((close_line - 1))p" "$file" | trim)
+ name=$(printf '%s\n' "$block" | sed -n 's/^name:[[:space:]]*//p' | head -n 1)
+ if [ -z "$name" ]; then
+ fail "$file: frontmatter has no non-empty name"
+ elif [ "$name" != "$(basename "$file" .md)" ]; then
+ fail "$file: name '$name' does not match filename stem"
+ fi
+ if ! printf '%s\n' "$block" | grep -q '^description:'; then
+ fail "$file: frontmatter has no description"
+ else
+ desc=$(printf '%s\n' "$block" | sed -n 's/^description:[[:space:]]*//p' | head -n 1)
+ case "$desc" in
+ "" | "|" | "|-" | "|+" | ">" | ">-" | ">+")
+ # Empty or block-scalar-only value: require at least one non-empty
+ # indented continuation line, else the description is effectively
+ # empty — the exact silent-skip case on Gemini/Codex/OpenCode.
+ if ! printf '%s\n' "$block" | awk '
+ /^description:/ { in_block = 1; next }
+ in_block && /^[^[:space:]]/ { in_block = 0 }
+ in_block && /[^[:space:]]/ { found = 1 }
+ END { exit !found }
+ '; then
+ fail "$file: description is empty"
+ fi
+ ;;
+ esac
+ fi
+done < <(find -L agents -name '*.md' -type f)
+
+if [ "$errors" -gt 0 ]; then
+ echo "check-frontmatter: $errors problem(s) found" >&2
+ exit 1
+fi
+
+echo "check-frontmatter: all skills and agents pass"
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index d07d78e..77c97fc 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -30,36 +30,31 @@ jobs:
**/*.md
!CHANGELOG.md
config: 'config/cspell.json'
- detect-changed-skills:
- name: 🔎 Detect Changed Skills
+ list-skills:
+ name: 🔎 List Skills
runs-on: ubuntu-latest
outputs:
- skills: ${{ steps.changed-skills.outputs.skills }}
+ skills: ${{ steps.list-skills.outputs.skills }}
steps:
- uses: actions/checkout@v7
- with:
- fetch-depth: 0
- - name: Get changed skills
- id: changed-skills
+ - name: List all skills
+ id: list-skills
run: |
- BASE=${{ github.event.pull_request.base.sha }}
- HEAD=${{ github.event.pull_request.head.sha }}
- SKILLS=$(git diff --name-only $BASE $HEAD | \
- { grep 'SKILL.md' || true; } | \
+ SKILLS=$(find -L skills -name SKILL.md -type f | \
xargs -I {} dirname {} | \
sort -u | \
jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "skills=$SKILLS" >> "$GITHUB_OUTPUT"
- echo "Changed skills: $SKILLS"
+ echo "Skills: $SKILLS"
validate-skills:
name: 🔍 Validate Skills
- needs: detect-changed-skills
- if: needs.detect-changed-skills.outputs.skills != '[]'
+ needs: list-skills
+ if: needs.list-skills.outputs.skills != '[]'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
- skill: ${{ fromJson(needs.detect-changed-skills.outputs.skills) }}
+ skill: ${{ fromJson(needs.list-skills.outputs.skills) }}
steps:
- uses: actions/checkout@v7
- name: Validate ${{ matrix.skill }}
@@ -76,6 +71,16 @@ jobs:
echo "Valid: ${{ steps.validate.outputs.valid }}"
echo "Errors: ${{ steps.validate.outputs.errors }}"
echo "Warnings: ${{ steps.validate.outputs.warnings }}"
+ frontmatter-guard:
+ name: 🛡️ Frontmatter Guard
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+ # Covers what validate-skills cannot: UTF-8 BOM detection (Gemini-fatal,
+ # passes validate-skill) and agents/**/*.md frontmatter (validate-skill
+ # only accepts files named SKILL.md).
+ - name: Check frontmatter portability
+ run: bash .github/scripts/check-frontmatter.sh
plugin-validate:
name: 📦 Plugin Validation
runs-on: ubuntu-latest
diff --git a/CLAUDE.md b/CLAUDE.md
index 995793f..fd208d6 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -38,6 +38,7 @@ skills/
bloc/SKILL.md
bloc/references/
create-project/SKILL.md
+ create-project/references/interaction-fallbacks.md # symlink → skills/shared/references/
dart-flutter-sdk-upgrade/SKILL.md
green-gate/SKILL.md
green-gate/references/
@@ -48,6 +49,9 @@ skills/
license-compliance/SKILL.md
material-theming/SKILL.md
navigation/SKILL.md
+ shared/
+ references/
+ interaction-fallbacks.md # AskUserQuestion + allowed-tools fallbacks; symlinked into consuming skills
static-security/SKILL.md
static-security/references/
testing/SKILL.md
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a070aa1..9755156 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -34,7 +34,7 @@ argument-hint: "[file-or-directory]" # optional
| Field | Required | Rules |
| ----- | -------- | ----- |
-| `name` | Yes | Must match the skill's folder name exactly; lowercase letters, numbers, and hyphens only |
+| `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 `validate-skill`) |
| `description` | Yes | Describes when the skill should be triggered |
| `allowed-tools` | Yes | Comma-separated list of tools the skill may use |
| `argument-hint` | No | Placeholder hint shown to the user |
@@ -69,6 +69,78 @@ Add the new skill directory and files to the repository structure tree in `CLAUD
- **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.
+## Cross-harness portability
+
+Skills are authored for Claude Code but target the [Agent Skills open
+standard](https://agentskills.io/specification) (the `npx skills` format, supported by
+many agents), so they should degrade gracefully on non-Claude harnesses such as Codex,
+Gemini CLI, and OpenCode without changing Claude Code behavior. Under that standard a skill
+is a **static instruction set**: the agent loads it by matching its `description`, then reads
+the body — there is no argument or template substitution. `$ARGUMENTS` and
+`${CLAUDE_SKILL_DIR}` are Claude Code conveniences, not spec features, so a body that uses
+them must still work when they arrive unsubstituted.
+
+**`$ARGUMENTS`** — not a spec concept; on a plain Agent Skill it is never substituted and
+stays literal. Always pair it with a fallback that fires when it is empty *or still shows
+the literal text* `$ARGUMENTS`:
+
+```markdown
+$ARGUMENTS
+
+**If the feature description above is empty or still shows the literal text
+`$ARGUMENTS` (the host did not substitute it), ask the user** for it (or read it
+from the conversation).
+```
+
+**`${CLAUDE_SKILL_DIR}`** — no skill here uses it today (the hooks use
+`${CLAUDE_PLUGIN_ROOT}`, resolved by Claude Code, not by skill bodies). If a future skill
+references a bundled file, prefer the spec form — a **relative path from the skill root**
+(`scripts/x.sh`) — and add a fallback for hosts that do not substitute the absolute form.
+
+**Frontmatter** — an agent silently skips a skill whose frontmatter is malformed. Keep the
+opening `---` on line 1, close the block with `---`, and include a non-empty `name:`
+(kebab-case, **matching the directory name**) and `description:`. The spec also allows
+`license`, `compatibility`, `metadata`, and `allowed-tools`. This plugin's Claude Code
+extras (`when_to_use`, `argument-hint`, `effort`, `model`) are not spec fields, but
+`npx skills` and other agents ignore unknown frontmatter keys — keep them top-level so
+Claude Code reads them and nothing else breaks. (The spec's optional `skills-ref` linter is
+stricter, rejecting any top-level field outside the six it allows; `npx skills` does not run
+it, and nesting these extras under `metadata:` is the escape hatch if strict conformance is
+ever needed.) The `Skill validation` CI job (`Flash-Brew-Digital/validate-skill@v1`) enforces
+the spec (including name-matches-directory) across every skill on each pull request, and
+`.github/scripts/check-frontmatter.sh` guards the gaps it leaves — a UTF-8 BOM (Gemini-fatal but
+passes `validate-skill`) and `agents/**/*.md` frontmatter, which no other check covers.
+
+**MCP references** — this plugin registers two MCP servers in `.mcp.json`: `dart` (Dart and
+Flutter actions) and `very-good-cli` (scaffolding, tests, license checks). On Claude Code
+they are the primary execution path, and the `check-vgv-cli.sh` / `block-cli-workarounds.sh`
+hooks deliberately steer the quality gates through the MCP tools instead of the raw CLI — do
+not weaken that on Claude Code. Those hooks do not run on other hosts and the MCP servers may
+not be connected there, so every skill that drives an MCP tool must name the equivalent
+`very_good` / `dart` / `flutter` CLI command as a fallback and never block when the server is
+absent. The `dart-flutter-sdk-upgrade` and `very-good-analysis-upgrade` skills already phrase
+this as "use the MCP tool if available; otherwise Bash" — match that.
+
+**Subagents** — subagents are not part of the Agent Skills standard, and no skill in this
+plugin dispatches one. The `flutter-reviewer` agent (`agents/flutter-reviewer.md`) is a
+Claude Code construct; on a host without a subagent mechanism its four preloaded standards
+(`bloc`, `testing`, `static-security`, `accessibility`) still apply — run the review inline
+against those skills instead of dispatching the agent.
+
+**`AskUserQuestion` and `allowed-tools`** — both are Claude Code conveniences. A skill that
+asks the user a structured question or declares a narrow `allowed-tools` list carries the
+cross-harness fallbacks in [`skills/shared/references/interaction-fallbacks.md`](skills/shared/references/interaction-fallbacks.md):
+ask the same question as plain numbered text where `AskUserQuestion` is absent, and treat
+`allowed-tools` as a permission hint rather than a hard cap.
+
+**Shared content via symlinks** — references shared across skills live once under
+`skills/shared/references/` and are symlinked into each consuming skill's `references/`
+directory (for example `interaction-fallbacks.md`). `npx skills` dereferences symlinks when
+it copies a cloned or local skill, so those install paths work as-is. Before relying on a
+server-side snapshot install, verify with a real `npx skills add` that the shared files
+arrive intact; if they do not, materialize them (dereference the symlinks into real files at
+publish time, or vendor real copies).
+
## Testing Locally
Editing a skill or hook and pushing straight to a PR only tells you the files
@@ -144,15 +216,20 @@ Then, inside a session:
### Validate before you push
-Run the same check CI runs, from the repository root:
+Run the same checks CI runs, from the repository root:
```bash
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.
+```bash
+bash .github/scripts/check-frontmatter.sh
+```
+
+The first validates the manifest, skill frontmatter, hook JSON, MCP config, and file
+references. The second is the frontmatter guard (UTF-8 BOM detection plus `agents/**/*.md`
+frontmatter). Both are static, so they confirm structure but do not replace the live checks
+above.
### Troubleshooting
@@ -170,10 +247,12 @@ Every pull request runs the following checks automatically:
| Check | What it does | Config |
| ----- | ------------ | ------ |
+| Markdown lint | Lints all `*.md` files (except `CHANGELOG.md`) | `config/custom.markdownlint.jsonc` |
| Spelling | Runs cspell on all `*.md` files | `config/cspell.json` |
-| File size | Ensures no file exceeds 50 KB | `scripts/check_large_files.sh` |
-| Skill validation | Validates `SKILL.md` frontmatter and structure | `scripts/validate_skills.sh` |
-| Plugin validation | Validates and test-installs the plugin | `claude plugin validate .` |
+| Skill validation | Validates **every** `SKILL.md`'s frontmatter and structure against the Agent Skills spec, so a malformed skill fails the build instead of silently vanishing on another host | `Flash-Brew-Digital/validate-skill@v1` |
+| Frontmatter guard | Fails on a UTF-8 BOM in any `SKILL.md` or agent file (Gemini-fatal, passes validate-skill) and validates `agents/**/*.md` frontmatter, which no other check covers | `.github/scripts/check-frontmatter.sh` |
+| Plugin validation | Validates the plugin manifests via the Claude Code CLI | `claude plugin validate .` |
+| Script tests | Runs the hook script unit tests | `hooks/scripts/*_test.sh` |
If the spelling check flags a legitimate word, add it to `config/cspell.json` in the `words` array.
diff --git a/agents/flutter-reviewer.md b/agents/flutter-reviewer.md
index 760c7ee..61e3c06 100644
--- a/agents/flutter-reviewer.md
+++ b/agents/flutter-reviewer.md
@@ -64,8 +64,9 @@ Determine the change set adaptively, from the repository root:
per affected package.
Read the changed files with `Read`/`Grep` to review their full context, not just the diff hunks.
-You may use `mcp__dart__analyze_files` to corroborate a skill-based judgment, but analyzer output is
-not itself a findings source (see "What not to report").
+When the Dart MCP server is connected, you may use `mcp__dart__analyze_files` to corroborate a
+skill-based judgment, but analyzer output is not itself a findings source (see "What not to
+report"). If it is unavailable, rely on the four preloaded standards alone.
### When scoping fails
diff --git a/config/cspell.json b/config/cspell.json
index 2ebb3a6..9c1313e 100644
--- a/config/cspell.json
+++ b/config/cspell.json
@@ -3,11 +3,13 @@
"words": [
"activatable",
"adversarial",
+ "agentskills",
"antipattern",
"Automator",
"Bidirectionality",
"Bienvenido",
"bypassable",
+ "Codex",
"dartdoc",
"CSPRNG",
"Cupertino",
@@ -37,6 +39,7 @@
"mocktail",
"monorepo",
"Mundo",
+ "opencode",
"pasteable",
"prefs",
"pubspec",
diff --git a/skills/accessibility/SKILL.md b/skills/accessibility/SKILL.md
index 67cd184..30d4cb5 100644
--- a/skills/accessibility/SKILL.md
+++ b/skills/accessibility/SKILL.md
@@ -61,6 +61,8 @@ Apply these standards to all accessibility work:
Every accessibility engagement follows four phases in sequence. Do not skip Phase 1 or Phase 2.
+> **Cross-harness note.** Phases 1, 2, and 4 below use `AskUserQuestion`. On a host without it, ask the same question as plain numbered text and wait for the reply before proceeding. See [interaction fallbacks](references/interaction-fallbacks.md).
+
### Phase 1: Conformance Level Selection
Use `AskUserQuestion` to ask:
diff --git a/skills/accessibility/references/interaction-fallbacks.md b/skills/accessibility/references/interaction-fallbacks.md
new file mode 120000
index 0000000..9492dce
--- /dev/null
+++ b/skills/accessibility/references/interaction-fallbacks.md
@@ -0,0 +1 @@
+../../shared/references/interaction-fallbacks.md
\ No newline at end of file
diff --git a/skills/create-project/SKILL.md b/skills/create-project/SKILL.md
index cc1361f..a071b72 100644
--- a/skills/create-project/SKILL.md
+++ b/skills/create-project/SKILL.md
@@ -11,6 +11,8 @@ model: haiku
Scaffold a new Dart or Flutter project using Very Good CLI templates.
+> **Cross-harness fallbacks.** This skill drives the Very Good CLI MCP server and asks the user structured questions. On a host without that MCP server connected, run the equivalent `very_good create …` and `very_good packages get` commands directly. On a host without `AskUserQuestion`, ask the same questions as plain numbered text. See [interaction fallbacks](references/interaction-fallbacks.md).
+
---
## Core Standards
diff --git a/skills/create-project/references/interaction-fallbacks.md b/skills/create-project/references/interaction-fallbacks.md
new file mode 120000
index 0000000..9492dce
--- /dev/null
+++ b/skills/create-project/references/interaction-fallbacks.md
@@ -0,0 +1 @@
+../../shared/references/interaction-fallbacks.md
\ No newline at end of file
diff --git a/skills/dart-flutter-sdk-upgrade/SKILL.md b/skills/dart-flutter-sdk-upgrade/SKILL.md
index 8f41d8b..627eaaf 100644
--- a/skills/dart-flutter-sdk-upgrade/SKILL.md
+++ b/skills/dart-flutter-sdk-upgrade/SKILL.md
@@ -48,9 +48,10 @@ for the target Flutter release before editing any files.
3. Note the Dart version listed alongside it
The target version comes from `$ARGUMENTS` (e.g. `3.41.0`) when the user supplied one. If
-`$ARGUMENTS` is empty, look up the latest Flutter stable release from that same page. For pure
-Dart packages (no Flutter dependency), the Dart version is whatever `$ARGUMENTS` specifies or
-the latest stable — no Flutter mapping needed.
+`$ARGUMENTS` is empty or still shows the literal text `$ARGUMENTS` (the host did not substitute
+it), look up the latest Flutter stable release from that same page. For pure Dart packages (no
+Flutter dependency), the Dart version is whatever `$ARGUMENTS` specifies or the latest stable —
+no Flutter mapping needed.
Confirm both resolved versions with the user before editing files.
diff --git a/skills/green-gate/SKILL.md b/skills/green-gate/SKILL.md
index 61fe2a2..b1d9511 100644
--- a/skills/green-gate/SKILL.md
+++ b/skills/green-gate/SKILL.md
@@ -42,7 +42,11 @@ Apply these to ALL green-gate work:
coverage via `mcp__very-good-cli__test`. The Bash test path
(`very_good test`, `flutter test`, `dart test`) is hook-blocked by
`block-cli-workarounds.sh`; `dart analyze` via Bash is redundant with the MCP
- tool. **Bash is reserved for parsing `coverage/lcov.info` only.**
+ tool. **Bash is reserved for parsing `coverage/lcov.info` only.** This MCP-only
+ rule is a Claude Code constraint enforced by that hook; it does not run on other
+ hosts. **Cross-harness fallback:** on a host without the hook and without the MCP
+ servers connected, run the equivalent `dart analyze`, `dart format`, and
+ `very_good test` CLI commands instead — never block on a missing MCP server.
- **Never cache green** — re-evaluate every gate every round. Fixing analyze or
test failures and writing new test files shifts both formatting and the
coverage denominator, so a previously green gate can regress.
diff --git a/skills/layered-architecture/SKILL.md b/skills/layered-architecture/SKILL.md
index 3a57f01..0a5b185 100644
--- a/skills/layered-architecture/SKILL.md
+++ b/skills/layered-architecture/SKILL.md
@@ -29,6 +29,8 @@ Apply these standards to ALL layered architecture work:
- **Repositories accept data layer dependencies via constructor injection** — never instantiate clients internally
- **App bootstrap wires all layers** — `main_.dart` creates clients and repositories, provides them via `RepositoryProvider`
+> **Cross-harness fallback.** This skill scaffolds and tests packages via the Very Good CLI MCP server. On a host without that MCP server connected, run the equivalent `very_good create dart_package …`, `very_good packages get`, and `very_good test` commands directly.
+
## Architecture Overview
| Layer | Responsibility | Location | Depends On | Example |
diff --git a/skills/license-compliance/SKILL.md b/skills/license-compliance/SKILL.md
index bb1296a..d60fc2a 100644
--- a/skills/license-compliance/SKILL.md
+++ b/skills/license-compliance/SKILL.md
@@ -17,6 +17,8 @@ effort: medium
Dependency license auditor for Dart and Flutter projects — verifies that all package dependencies use licenses compatible with the project's requirements using the Very Good CLI MCP tools.
+> **Cross-harness fallback.** On Claude Code the `packages_check_licenses` MCP tool is the execution path. On a host without the Very Good CLI MCP server connected, run `very_good packages check licenses --licenses` directly (add `--directory ` for a project in a subdirectory) and read its output the same way — never block on a missing MCP server.
+
---
## Core Standards
diff --git a/skills/shared/references/interaction-fallbacks.md b/skills/shared/references/interaction-fallbacks.md
new file mode 100644
index 0000000..f3bd61a
--- /dev/null
+++ b/skills/shared/references/interaction-fallbacks.md
@@ -0,0 +1,26 @@
+# Interaction fallbacks
+
+Cross-harness fallbacks for the Claude-specific interaction constructs. A strong host
+(Claude Code) uses the primary construct and ignores these notes; a weaker host follows
+them. Each is a diff against the primary path, not a replacement.
+
+## No structured-question tool (`AskUserQuestion`)
+
+`AskUserQuestion` is Claude-specific. On a host without it, ask the same question as
+**plain numbered text** and treat the user's next reply as the choice. Preserve the same
+options and the same default; a multi-select becomes "reply with all that apply".
+
+**Before an operation that writes or is hard to undo** — scaffolding a project into an
+existing directory, applying a batch of code fixes, or anything that overwrites files —
+never auto-proceed on a default. Stop, state what the operation will do, ask in plain
+text, and wait for an explicit reply. A headless or autonomous host must halt here rather
+than pick the default option.
+
+## Narrow `allowed-tools`
+
+`allowed-tools` is an experimental spec field; some hosts treat the list as an
+exhaustive allow-list rather than a permission hint. A skill whose body needs to read,
+write, edit, or run commands beyond its declared list must use whatever tools the task
+requires — the list is a permission hint for Claude Code, not a cap on the skill's work.
+If a host enforces the list and blocks a needed tool, that is the signal to widen the
+frontmatter list to the full set the skill uses.
diff --git a/skills/testing/SKILL.md b/skills/testing/SKILL.md
index 78c5ed4..b2e68ea 100644
--- a/skills/testing/SKILL.md
+++ b/skills/testing/SKILL.md
@@ -28,6 +28,7 @@ Apply these standards to ALL test work:
- **Tag all golden tests** — annotate with `TestTag.golden` so goldens can run/update independently
- **Pass `directory` to the `test` MCP tool when the project is not at the workspace root** — monorepos with the Flutter project in a subdirectory (e.g. `mobile/`) require `directory: 'mobile'`; omit it only when `pubspec.yaml` is at the workspace root
- **Pass `timeout_seconds` to the `test` MCP tool** — Flutter tests can hang indefinitely when `pumpAndSettle()` is called without a timeout; set a cap (e.g. `timeout_seconds: 120`) so the run is killed instead of stalling
+- **Cross-harness fallback for the `test` MCP tool** — on Claude Code use `mcp__very-good-cli__test`; on a host without that MCP server connected, run `very_good test` (or `flutter test` / `dart test`) directly with the same coverage and timeout options — never block on a missing MCP server
## Test Structure
diff --git a/skills/ui-package/SKILL.md b/skills/ui-package/SKILL.md
index d842516..a90cfdf 100644
--- a/skills/ui-package/SKILL.md
+++ b/skills/ui-package/SKILL.md
@@ -83,3 +83,5 @@ my_ui/
## Creating the Package
Use the Very Good CLI MCP tool to scaffold the `app_ui_package`.
+
+> **Cross-harness fallback.** On a host without the Very Good CLI MCP server connected, run the equivalent `very_good create` command for the UI package template directly.
diff --git a/skills/very-good-analysis-upgrade/SKILL.md b/skills/very-good-analysis-upgrade/SKILL.md
index fe95178..0cc45c2 100644
--- a/skills/very-good-analysis-upgrade/SKILL.md
+++ b/skills/very-good-analysis-upgrade/SKILL.md
@@ -31,7 +31,8 @@ These standards apply to every `very_good_analysis` upgrade.
Confirm two things before proceeding:
1. **Target version** — use `$ARGUMENTS` as the target version when the user supplied one
- (e.g. `10.0.0`). If `$ARGUMENTS` is empty, fetch the latest from the pub.dev API and use
+ (e.g. `10.0.0`). If `$ARGUMENTS` is empty or still shows the literal text `$ARGUMENTS`
+ (the host did not substitute it), fetch the latest from the pub.dev API and use
that. Don't ask — just look it up and proceed:
```bash