Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
---
name: feature-data-flow
description: Build a full feature in this Flutter repository that includes backend or storage data flow: read API schema, create DTOs, map DTOs to entities, add Riverpod use cases, connect feature state, and render UI data. Use when a task goes beyond a screen and needs real data integration.
description: >
Build a full feature in this Flutter repository that includes backend or storage data flow:
read API schema, create DTOs, map DTOs to entities, add Riverpod use cases, connect feature
state, and render UI data. Use when a task goes beyond a screen and needs real data integration.
allowed-tools: Bash, Read, Grep, Glob, Edit, Write
model: claude-sonnet-4-6
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ In standalone mode, verify before considering the task complete.

Use the smallest sufficient verification:
- Run `make gen` when annotations, routes, DTOs, localization, or asset inputs changed.
- Run `ai/skills/lint-format/scripts/lint-format.sh` for normal Dart/Flutter code changes.
- Run `.agents/skills/lint-format/scripts/lint-format.sh` for normal Dart/Flutter code changes.
- Run `fvm flutter test` when behavior, state, use cases, widgets, or tests changed.
- Run `ai/skills/build-verify/scripts/verify.sh` for broad, cross-platform, dependency, startup, routing, generated-code, or PR-ready tasks.
- Run `.agents/skills/build-verify/scripts/verify.sh` for broad, cross-platform, dependency, startup, routing, generated-code, or PR-ready tasks.
- Run `make integration_test` only when Patrol integration flows changed or the user asks for it.

If any step fails, fix the underlying issue and re-run the failed verification. Do not disable checks, delete tests, add broad `// ignore:` comments, or hand-edit generated files to make verification pass.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Run this skill after completing Flutter/Dart code changes unless a broader verif
Run the wrapper script from the repo root:

```bash
ai/skills/lint-format/scripts/lint-format.sh
.agents/skills/lint-format/scripts/lint-format.sh
```

The script auto-detects whether Flutter-relevant files changed. When relevant files changed, it formats changed Dart files and runs a project-wide analyzer pass.
Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions ai/skills/prd/SKILL.md → .agents/skills/prd/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ You are a PRD creation specialist focused on producing clear, actionable product
4. Generate a PRD using the standardized template

## Template Reference
- Template: `ai/templates/prd.md` (also reachable as `.claude/templates/prd.md`)
- Template: `.agents/templates/prd.md` (also reachable as `.claude/templates/prd.md`)
- Output: `.claude/tasks/[feature-name]/prd.md`

## Read First
- `AGENTS.md`
- `docs/PROJECT_OVERVIEW.md`
- `docs/PROJECT_GUIDELINES.md`
- `ai/templates/prd.md`
- `.agents/templates/prd.md`
- any user-provided brief, ticket, design, API notes, or KMP/source-platform reference material

## Scope Boundary
Expand Down Expand Up @@ -73,7 +73,7 @@ Get explicit confirmation before drafting.

### Step 3: Draft the PRD (Mandatory)

Read the template at `ai/templates/prd.md` and use it to draft the PRD.
Read the template at `.agents/templates/prd.md` and use it to draft the PRD.
- Focus on **WHAT** and **WHY**, not implementation details.
- Keep requirements testable and unambiguous.
- Reference domain entities from existing project docs, user input, or migration material where relevant.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,20 @@ Use this when setting up a new clone, preparing AI workflows, or debugging skill
- Run `gh auth login --hostname github.com --git-protocol ssh --web`.
- Run `gh auth status` and fix any invalid `GITHUB_TOKEN` / `GH_TOKEN` environment variables if they override the stored login.
3. Verify repo-local script permissions and syntax:
- `test -x ai/skills/build-verify/scripts/verify.sh`
- `test -x ai/skills/lint-format/scripts/lint-format.sh`
- `test -x ai/skills/release-builds/scripts/archive_ios_ipa.sh`
- `bash -n ai/skills/build-verify/scripts/verify.sh`
- `bash -n ai/skills/lint-format/scripts/lint-format.sh`
- `sh -n ai/skills/release-builds/scripts/archive_ios_ipa.sh`
- `test -x .agents/skills/build-verify/scripts/verify.sh`
- `test -x .agents/skills/lint-format/scripts/lint-format.sh`
- `test -x .agents/skills/release-builds/scripts/archive_ios_ipa.sh`
- `bash -n .agents/skills/build-verify/scripts/verify.sh`
- `bash -n .agents/skills/lint-format/scripts/lint-format.sh`
- `sh -n .agents/skills/release-builds/scripts/archive_ios_ipa.sh`
4. Verify Claude discovery wiring when Claude Code support is needed:
- every `ai/skills/<name>/SKILL.md` has a matching `.claude/skills/<name>` symlink
- every `.agents/skills/<name>/SKILL.md` has a matching `.claude/skills/<name>` symlink
- every skill has a matching `.claude/commands/<name>.md`
- each symlink resolves to a `SKILL.md`
5. Run lightweight help checks:
- `ai/skills/build-verify/scripts/verify.sh --help`
- `ai/skills/lint-format/scripts/lint-format.sh --help`
- `ai/skills/release-builds/scripts/archive_ios_ipa.sh` should print usage and exit non-zero when no flavor is supplied.
- `.agents/skills/build-verify/scripts/verify.sh --help`
- `.agents/skills/lint-format/scripts/lint-format.sh --help`
- `.agents/skills/release-builds/scripts/archive_ios_ipa.sh` should print usage and exit non-zero when no flavor is supplied.

## Platform Cleanup Workflow
Use this workflow to automate README First steps > Automated steps item 1.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Use this skill only after the release PR has been merged.
- `pubspec.yaml`
- `makefile`
- `.github/workflows/`
- `ai/skills/release-builds/scripts/archive_ios_ipa.sh`
- `.agents/skills/release-builds/scripts/archive_ios_ipa.sh`

## Goal
This skill is for the post-merge release-build phase.
Expand Down Expand Up @@ -64,13 +64,13 @@ Immediately after each IPA build, archive the IPA out of `build/ios/ipa/` and Fl
Use:

```bash
ai/skills/release-builds/scripts/archive_ios_ipa.sh <flavor>
.agents/skills/release-builds/scripts/archive_ios_ipa.sh <flavor>
```

Examples:
- `ai/skills/release-builds/scripts/archive_ios_ipa.sh develop`
- `ai/skills/release-builds/scripts/archive_ios_ipa.sh staging`
- `ai/skills/release-builds/scripts/archive_ios_ipa.sh production`
- `.agents/skills/release-builds/scripts/archive_ios_ipa.sh develop`
- `.agents/skills/release-builds/scripts/archive_ios_ipa.sh staging`
- `.agents/skills/release-builds/scripts/archive_ios_ipa.sh production`

The script copies the generated IPA into:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
set -eu

if [ "$#" -ne 1 ]; then
echo "Usage: ai/skills/release-builds/scripts/archive_ios_ipa.sh <develop|staging|production>" >&2
echo "Usage: .agents/skills/release-builds/scripts/archive_ios_ipa.sh <develop|staging|production>" >&2
exit 1
fi

Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions ai/skills/tasks/SKILL.md → .agents/skills/tasks/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Read both documents and identify:

### Step 2: Generate Task Structure

Order tasks following this Flutter-stack progression (mirrors the `ai/templates/task-list.md`
Order tasks following this Flutter-stack progression (mirrors the `.agents/templates/task-list.md`
phases):

1. **Foundation (data layer)** — DTOs in `lib/common/data/dto/` (`@freezed` with
Expand All @@ -67,13 +67,13 @@ Keep the total number of tasks reasonable — prefer fewer, well-scoped tasks ov

### Step 3: Create Task Summary

Use the template at `ai/templates/task-list.md` (also reachable as
Use the template at `.agents/templates/task-list.md` (also reachable as
`.claude/templates/task-list.md` via symlink) to generate:
`.claude/tasks/[feature-name]/tasks.md`

### Step 4: Generate Individual Task Files

Use the template at `ai/templates/task.md` (also reachable as `.claude/templates/task.md`
Use the template at `.agents/templates/task.md` (also reachable as `.claude/templates/task.md`
via symlink) to create:
`.claude/tasks/[feature-name]/[num]_task.md`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Do not proceed until clarifications are resolved.

### Step 4: Generate Tech Spec

Read the template at `ai/templates/techspec.md` (also reachable at
Read the template at `.agents/templates/techspec.md` (also reachable at
`.claude/templates/techspec.md` via symlink) and draft the spec.

- Reference concrete files and modules from the codebase
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
165 changes: 165 additions & 0 deletions .agents/validate_skills.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#!/usr/bin/env python3
"""Validate .agents/skills/* against the Agent Skills spec (agentskills.io/specification)
and this repo's skill-exposure convention (see AGENTS.md "Creating a new skill").

Checks per skill:
- SKILL.md exists with parseable YAML frontmatter (strict — lenient parsers in
some clients mask errors that make other clients silently skip the skill)
- name: required, 1-64 chars, lowercase alphanumerics and single hyphens,
no leading/trailing hyphen, matches the directory name
- description: required, 1-1024 chars
- unknown top-level frontmatter fields (spec fields + known Claude Code
extensions are accepted; anything else is a warning)
- SKILL.md body under 500 lines (spec recommendation; warning)
- Claude Code exposure exists:
.claude/skills/<name> symlink and .claude/commands/<name>.md slash command

Exits 1 on errors, 0 if only warnings.
"""

import os
import re
import sys

REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SKILLS_DIR = os.path.join(REPO_ROOT, ".agents", "skills")

SPEC_FIELDS = {"name", "description", "license", "compatibility", "metadata", "allowed-tools"}
CLAUDE_EXTENSIONS = {"model", "user-invocable", "disable-model-invocation"}
NAME_RE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
MAX_BODY_LINES = 500

try:
import yaml
except ImportError:
yaml = None


def parse_frontmatter(text: str) -> dict[str, object]:
if yaml is not None:
fm = yaml.safe_load(text)
if not isinstance(fm, dict):
raise ValueError("frontmatter is not a YAML mapping")
return fm

data: dict[str, object] = {}
lines = text.splitlines()
index = 0
while index < len(lines):
line = lines[index]
index += 1
if not line.strip():
continue
if line.startswith((" ", "\t")) or ":" not in line:
raise ValueError("frontmatter uses YAML syntax that requires PyYAML")

key, value = line.split(":", 1)
key = key.strip()
value = value.strip()
if not key:
raise ValueError("frontmatter contains an empty key")

if value in {">", "|"}:
block_lines: list[str] = []
while index < len(lines) and (lines[index].startswith((" ", "\t")) or not lines[index].strip()):
block_lines.append(lines[index].strip())
index += 1
data[key] = "\n".join(block_lines).strip() if value == "|" else " ".join(block_lines).strip()
continue

if value.lower() == "true":
data[key] = True
elif value.lower() == "false":
data[key] = False
else:
data[key] = value.strip("\"'")

return data


def check_skill(skill_dir: str) -> tuple[list[str], list[str]]:
errors: list[str] = []
warnings: list[str] = []
dirname = os.path.basename(skill_dir)
skill_md = os.path.join(skill_dir, "SKILL.md")

if not os.path.isfile(skill_md):
return [f"missing SKILL.md"], warnings

text = open(skill_md, encoding="utf-8").read()
match = re.match(r"^---\n(.*?)\n---\n?(.*)$", text, re.S)
if not match:
return ["SKILL.md has no YAML frontmatter block"], warnings

try:
fm = parse_frontmatter(match.group(1))
except Exception as exc:
msg = str(exc).splitlines()[0]
return [f"frontmatter is not valid YAML ({msg}) — "
"quote values containing colons or use a '>' block scalar"], warnings

name = fm.get("name")
if not name:
errors.append("missing required field: name")
else:
if name != dirname:
errors.append(f"name '{name}' does not match directory '{dirname}'")
if not NAME_RE.fullmatch(str(name)):
errors.append(f"name '{name}' violates spec charset rules")
if len(str(name)) > 64:
errors.append("name exceeds 64 characters")

description = fm.get("description")
if not description:
errors.append("missing required field: description")
elif len(str(description)) > 1024:
errors.append(f"description is {len(str(description))} chars (max 1024)")
Comment thread
michalurbanek marked this conversation as resolved.
Outdated

Comment thread
michalurbanek marked this conversation as resolved.
for key in fm:
if key not in SPEC_FIELDS | CLAUDE_EXTENSIONS:
warnings.append(f"unknown frontmatter field '{key}' "
"(spec suggests nesting extras under 'metadata:')")

body_lines = match.group(2).count("\n") + 1
if body_lines > MAX_BODY_LINES:
warnings.append(f"body is {body_lines} lines (spec recommends < {MAX_BODY_LINES}; "
"consider moving detail into references/)")

for rel, kind in [
(os.path.join(".claude", "skills", dirname), "Claude Code symlink"),
(os.path.join(".claude", "commands", f"{dirname}.md"), "slash command"),
]:
path = os.path.join(REPO_ROOT, rel)
if not os.path.exists(path):
errors.append(f"missing {kind}: {rel}")
Comment thread
michalurbanek marked this conversation as resolved.
Outdated

return errors, warnings


def main() -> int:
if not os.path.isdir(SKILLS_DIR):
print(f"error: skills directory not found: {SKILLS_DIR}")
return 1

total_errors = 0
total_warnings = 0
for entry in sorted(os.listdir(SKILLS_DIR)):
skill_dir = os.path.join(SKILLS_DIR, entry)
if not os.path.isdir(skill_dir):
continue
errors, warnings = check_skill(skill_dir)
total_errors += len(errors)
total_warnings += len(warnings)
status = "FAIL" if errors else ("WARN" if warnings else "OK")
print(f"{status:4} {entry}")
for issue in errors:
print(f" error: {issue}")
for issue in warnings:
print(f" warning: {issue}")

print(f"\n{total_errors} error(s), {total_warnings} warning(s)")
return 1 if total_errors else 0


if __name__ == "__main__":
sys.exit(main())
18 changes: 10 additions & 8 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
@../AGENTS.md

Project-specific reusable AI workflows live in `../ai/skills/` and are surfaced
to Claude Code two ways:
Project-specific reusable AI workflows live in `../.agents/skills/` — the
standard cross-client Agent Skills location — and are surfaced to Claude Code
two ways:
- **auto-discovery**: `./skills/<name>` is a symlink with target
`../../ai/skills/<name>` (resolved from `.claude/skills/`, this points
at `<repo>/ai/skills/<name>`), so Claude reads the SKILL.md frontmatter
`../../.agents/skills/<name>` (resolved from `.claude/skills/`, this points
at `<repo>/.agents/skills/<name>`), so Claude reads the SKILL.md frontmatter
and triggers them based on context.
- **slash commands**: `./commands/<name>.md` exposes each skill as an explicit
command. Current commands mirror the skills listed in `AGENTS.md`, including
`/project-setup`, `/feature-screen`, `/feature-data-flow`, `/prd`,
`/techspec`, `/tasks`, `/implement`, `/implement-tasks-sequence`,
`/start-job`, `/build-verify`, `/lint-format`, `/create-pr`,
`/review-pr-comments`, `/upgrade`, `/release-prepare`, `/release-builds`,
`/secrets-bootstrap`, and `/pr-review`.
`/start-job`, `/build-verify`, `/lint-format`, `/widget-test`,
`/layout-debug`, `/create-pr`, `/review-pr-comments`, `/upgrade`,
`/release-prepare`, `/release-builds`, `/secrets-bootstrap`, and
`/pr-review`.

See the "Reusable Workflows" section of `AGENTS.md` for the canonical list of
skills and the four-step convention for adding a new one.
skills and the five-step convention for adding a new one.
2 changes: 1 addition & 1 deletion .claude/skills/build-verify
2 changes: 1 addition & 1 deletion .claude/skills/create-pr
2 changes: 1 addition & 1 deletion .claude/skills/feature-data-flow
2 changes: 1 addition & 1 deletion .claude/skills/feature-screen
2 changes: 1 addition & 1 deletion .claude/skills/implement
2 changes: 1 addition & 1 deletion .claude/skills/implement-tasks-sequence
2 changes: 1 addition & 1 deletion .claude/skills/layout-debug
2 changes: 1 addition & 1 deletion .claude/skills/lint-format
2 changes: 1 addition & 1 deletion .claude/skills/pr-review
Loading
Loading