Skip to content

Commit 5b47e96

Browse files
authored
Merge pull request #8 from leancodepl/task/align-patrol-references
Source flutter-patrol from leancodepl/patrol
2 parents b864e8c + de7faa9 commit 5b47e96

11 files changed

Lines changed: 64 additions & 487 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,15 @@
108108
},
109109
{
110110
"name": "flutter-patrol",
111-
"source": "./plugins/flutter-patrol",
112-
"description": "LeanCode Patrol E2E testing guidance (Modules/System/ApiClients, keys conventions) plus Patrol MCP setup for AI-assisted test development.",
111+
"source": {
112+
"source": "github",
113+
"repo": "leancodepl/patrol"
114+
},
115+
"strict": false,
116+
"description": "Patrol E2E testing skills (write-test workflow and Modules/System/ApiClients architecture with keys conventions), sourced directly from the Patrol repository.",
113117
"skills": [
114-
"./skills/flutter-patrol-usage"
118+
"./skills/patrol-write-test",
119+
"./skills/patrol-test-architecture"
115120
]
116121
},
117122
{

AGENTS.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,23 @@ LeanCode's shared AI plugins for Claude Code, focused on Flutter development.
1313

1414
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.
1515

16+
## Externally sourced plugins
17+
18+
A plugin whose content is owned by another repository is registered in `.claude-plugin/marketplace.json` with an object source and `strict: false`:
19+
20+
```json
21+
{
22+
"name": "flutter-patrol",
23+
"source": { "source": "github", "repo": "leancodepl/patrol" },
24+
"strict": false,
25+
"skills": ["./skills/patrol-write-test", "./skills/patrol-test-architecture"]
26+
}
27+
```
28+
29+
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.
30+
31+
Currently external: `flutter-patrol` (from [leancodepl/patrol](https://github.com/leancodepl/patrol), which owns all Patrol AI support).
32+
1633
## Working conventions
1734

1835
- Keep plugins self-contained and independently installable.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ To have Claude Code prompt collaborators to install the marketplace automaticall
5353

5454
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`:
5555

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

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

8282
- [`flutter-ui`](plugins/flutter-ui/) - design-system-driven UI, loading/error patterns, localized presentation text, and UI implementation checklists
83-
- [`flutter-patrol`](plugins/flutter-patrol/) - Patrol test architecture, key conventions, and Patrol MCP workflow for AI-assisted E2E work
83+
- [`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
8484
- [`flutter-marionette`](plugins/flutter-marionette/) - runtime interaction with a live debug app through Marionette MCP for exploration, smoke checks, and UI debugging
8585
- [`flutter-read-logs`](plugins/flutter-read-logs/) - read the running app's latest `flutter run` logs as on-demand context via `/read-logs`
8686

internal/pluginvalidation/marketplace.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,17 @@ func (v *validator) validateMarketplaceEntries(where string, entries []marketpla
6565
seen[entry.Name] = i
6666
}
6767
}
68-
v.requireNonEmpty(where, fmt.Sprintf("plugins[%d].source", i), entry.Source)
68+
if !entry.External {
69+
v.requireNonEmpty(where, fmt.Sprintf("plugins[%d].source", i), entry.Source)
70+
}
6971
}
7072
}
7173

7274
func (v *validator) registeredPlugins(entries []marketplacePlugin) map[string]struct{} {
7375
registered := map[string]struct{}{}
7476
for _, entry := range entries {
75-
if entry.Name != "" {
77+
// External plugins live in another repository: nothing local to validate.
78+
if entry.Name != "" && !entry.External {
7679
registered[entry.Name] = struct{}{}
7780
}
7881
}

internal/pluginvalidation/types.go

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,41 @@
11
package pluginvalidation
22

3+
import "encoding/json"
4+
35
type marketplace struct {
46
Metadata map[string]any `json:"metadata"`
57
Plugins []marketplacePlugin `json:"plugins"`
68
}
79

810
type marketplacePlugin struct {
9-
Name string `json:"name"`
10-
Source string `json:"source"`
11-
Description string `json:"description"`
11+
Name string
12+
Source string
13+
External bool
14+
Description string
15+
}
16+
17+
// UnmarshalJSON accepts both source forms: a local path string and an object
18+
// pointing at an external repository ({"source": "github", ...}). External
19+
// plugins have no local directory, so the validator skips them.
20+
func (p *marketplacePlugin) UnmarshalJSON(data []byte) error {
21+
var raw struct {
22+
Name string `json:"name"`
23+
Source json.RawMessage `json:"source"`
24+
Description string `json:"description"`
25+
}
26+
if err := json.Unmarshal(data, &raw); err != nil {
27+
return err
28+
}
29+
p.Name = raw.Name
30+
p.Description = raw.Description
31+
if len(raw.Source) == 0 {
32+
return nil
33+
}
34+
if raw.Source[0] == '{' {
35+
p.External = true
36+
return nil
37+
}
38+
return json.Unmarshal(raw.Source, &p.Source)
1239
}
1340

1441
type pluginManifest struct {

plugins/flutter-marionette/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ LeanCode Flutter plugin for [Marionette MCP](https://github.com/leancodepl/mario
1111

1212
## Marionette vs Patrol
1313

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

125125
## Related plugins
126126

127-
- [`flutter-patrol`](../flutter-patrol/) — deterministic E2E testing with Patrol MCP
127+
- [`flutter-patrol`](https://github.com/leancodepl/patrol) — deterministic E2E testing with Patrol MCP
128128
- [`flutter-ui`](../flutter-ui/) — design-system guidance (relevant when configuring `isInteractiveWidget` for custom widgets)

plugins/flutter-patrol/.claude-plugin/plugin.json

Lines changed: 0 additions & 20 deletions
This file was deleted.

plugins/flutter-patrol/README.md

Lines changed: 0 additions & 22 deletions
This file was deleted.

plugins/flutter-patrol/skills/flutter-patrol-usage/SKILL.md

Lines changed: 0 additions & 40 deletions
This file was deleted.

0 commit comments

Comments
 (0)