Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
11 changes: 8 additions & 3 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,15 @@
},
{
"name": "flutter-patrol",
"source": "./plugins/flutter-patrol",
"description": "LeanCode Patrol E2E testing guidance (Modules/System/ApiClients, keys conventions) plus Patrol MCP setup for AI-assisted test development.",
"source": {
"source": "github",
"repo": "leancodepl/patrol"
},
"strict": false,
"description": "Patrol E2E testing skills (write-test workflow and Modules/System/ApiClients architecture with keys conventions), sourced directly from the Patrol repository.",
"skills": [
"./skills/flutter-patrol-usage"
"./skills/patrol-write-test",
"./skills/patrol-test-architecture"
]
},
{
Expand Down
17 changes: 17 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,23 @@ LeanCode's shared AI plugins for Claude Code, focused on Flutter development.

Claude Code also documents support for per-plugin `agents/`, `commands/`, and `hooks/` directories. No plugin in this repo currently uses them — treat them as not-yet-exercised.

## Externally sourced plugins

A plugin whose content is owned by another repository is registered in `.claude-plugin/marketplace.json` with an object source and `strict: false`:

```json
{
"name": "flutter-patrol",
"source": { "source": "github", "repo": "leancodepl/patrol" },
"strict": false,
"skills": ["./skills/patrol-write-test", "./skills/patrol-test-architecture"]
}
```

The external repo is the single source of truth — there is no `plugins/<name>/` directory and no copy of its content here; do not vendor one. The validator skips external entries (no local directory to check); the `claude plugin validate` CI step covers the entry's schema.

Currently external: `flutter-patrol` (from [leancodepl/patrol](https://github.com/leancodepl/patrol), which owns all Patrol AI support).

## Working conventions

- Keep plugins self-contained and independently installable.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ To have Claude Code prompt collaborators to install the marketplace automaticall

Most plugins are pure rules and skills with no setup. A few need one-time tooling or MCP setup — finish it from the plugin's `README.md`:

- [`flutter-patrol`](plugins/flutter-patrol/) - Patrol CLI and Patrol MCP
- [`flutter-patrol`](https://github.com/leancodepl/patrol) - Patrol CLI and Patrol MCP
- [`flutter-marionette`](plugins/flutter-marionette/) - Marionette MCP and app-side binding

## Available plugins
Expand All @@ -80,7 +80,7 @@ Most plugins are pure rules and skills with no setup. A few need one-time toolin
### UI and verification

- [`flutter-ui`](plugins/flutter-ui/) - design-system-driven UI, loading/error patterns, localized presentation text, and UI implementation checklists
- [`flutter-patrol`](plugins/flutter-patrol/) - Patrol test architecture, key conventions, and Patrol MCP workflow for AI-assisted E2E work
- [`flutter-patrol`](https://github.com/leancodepl/patrol) - Patrol E2E test skills (write-test workflow, test architecture, key conventions, Patrol MCP), sourced directly from the Patrol repository's `skills/` — the single source of truth for Patrol AI support
- [`flutter-marionette`](plugins/flutter-marionette/) - runtime interaction with a live debug app through Marionette MCP for exploration, smoke checks, and UI debugging
- [`flutter-read-logs`](plugins/flutter-read-logs/) - read the running app's latest `flutter run` logs as on-demand context via `/read-logs`

Expand Down
7 changes: 5 additions & 2 deletions internal/pluginvalidation/marketplace.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,17 @@ func (v *validator) validateMarketplaceEntries(where string, entries []marketpla
seen[entry.Name] = i
}
}
v.requireNonEmpty(where, fmt.Sprintf("plugins[%d].source", i), entry.Source)
if !entry.External {
v.requireNonEmpty(where, fmt.Sprintf("plugins[%d].source", i), entry.Source)
}
}
}

func (v *validator) registeredPlugins(entries []marketplacePlugin) map[string]struct{} {
registered := map[string]struct{}{}
for _, entry := range entries {
if entry.Name != "" {
// External plugins live in another repository: nothing local to validate.
if entry.Name != "" && !entry.External {
registered[entry.Name] = struct{}{}
}
}
Expand Down
33 changes: 30 additions & 3 deletions internal/pluginvalidation/types.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,41 @@
package pluginvalidation

import "encoding/json"

type marketplace struct {
Metadata map[string]any `json:"metadata"`
Plugins []marketplacePlugin `json:"plugins"`
}

type marketplacePlugin struct {
Name string `json:"name"`
Source string `json:"source"`
Description string `json:"description"`
Name string
Source string
External bool
Description string
}

// UnmarshalJSON accepts both source forms: a local path string and an object
// pointing at an external repository ({"source": "github", ...}). External
// plugins have no local directory, so the validator skips them.
func (p *marketplacePlugin) UnmarshalJSON(data []byte) error {
var raw struct {
Name string `json:"name"`
Source json.RawMessage `json:"source"`
Description string `json:"description"`
}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
p.Name = raw.Name
p.Description = raw.Description
if len(raw.Source) == 0 {
return nil
}
if raw.Source[0] == '{' {
p.External = true
return nil
}
return json.Unmarshal(raw.Source, &p.Source)
}

type pluginManifest struct {
Expand Down
4 changes: 2 additions & 2 deletions plugins/flutter-marionette/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ LeanCode Flutter plugin for [Marionette MCP](https://github.com/leancodepl/mario

## Marionette vs Patrol

| | Marionette (this plugin) | Patrol ([`flutter-patrol`](../flutter-patrol/)) |
| | Marionette (this plugin) | Patrol ([`flutter-patrol`](https://github.com/leancodepl/patrol)) |
|---|---|---|
| Purpose | Runtime exploration, smoke verification | Deterministic E2E test suites |
| Runs against | Live `flutter run` debug session | `patrol develop` / `patrol test` |
Expand Down Expand Up @@ -124,5 +124,5 @@ Marionette relies on Flutter's VM Service and is intended for a live `flutter ru

## Related plugins

- [`flutter-patrol`](../flutter-patrol/) — deterministic E2E testing with Patrol MCP
- [`flutter-patrol`](https://github.com/leancodepl/patrol) — deterministic E2E testing with Patrol MCP
- [`flutter-ui`](../flutter-ui/) — design-system guidance (relevant when configuring `isInteractiveWidget` for custom widgets)
20 changes: 0 additions & 20 deletions plugins/flutter-patrol/.claude-plugin/plugin.json

This file was deleted.

22 changes: 0 additions & 22 deletions plugins/flutter-patrol/README.md

This file was deleted.

40 changes: 0 additions & 40 deletions plugins/flutter-patrol/skills/flutter-patrol-usage/SKILL.md

This file was deleted.

Loading
Loading