Skip to content
This repository was archived by the owner on Apr 7, 2026. It is now read-only.

Commit d93e5ad

Browse files
authored
feat: support multi-plugin Codex marketplace repos (#43)
* feat: support multi-plugin marketplace repos Signed-off-by: Michael Kantor <6068672+kantorcodes@users.noreply.github.com> * fix: handle remote marketplace entries cleanly Signed-off-by: Michael Kantor <6068672+kantorcodes@users.noreply.github.com> --------- Signed-off-by: Michael Kantor <6068672+kantorcodes@users.noreply.github.com>
1 parent 6e62149 commit d93e5ad

27 files changed

Lines changed: 849 additions & 97 deletions

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ pipx run codex-plugin-scanner verify .
3131
min_score: 80
3232
```
3333
34+
If your repository uses a Codex marketplace root like `.agents/plugins/marketplace.json`, keep `plugin_dir: "."`. The scanner will discover local `./plugins/...` entries automatically, scan each local plugin manifest, and skip remote marketplace entries instead of treating the repo root as a single plugin.
35+
3436
## Use After `$plugin-creator`
3537

3638
`codex-plugin-scanner` is designed as the quality gate between plugin creation and distribution:
@@ -120,13 +122,17 @@ codex-plugin-scanner ./my-plugin --cisco-skill-scan on --cisco-policy strict
120122
# Summary scan (legacy form still works)
121123
codex-plugin-scanner scan ./my-plugin --format json --profile public-marketplace
122124
125+
# Scan a multi-plugin repo from the marketplace root
126+
codex-plugin-scanner scan . --format json
127+
123128
# Rule-oriented lint (with optional mechanical fixes)
124129
codex-plugin-scanner lint ./my-plugin --list-rules
125130
codex-plugin-scanner lint ./my-plugin --explain README_MISSING
126131
codex-plugin-scanner lint ./my-plugin --fix --profile strict-security
127132
128133
# Runtime readiness verification
129134
codex-plugin-scanner verify ./my-plugin --format json
135+
codex-plugin-scanner verify . --format json
130136
codex-plugin-scanner verify ./my-plugin --online --format text
131137
132138
# Artifact-backed submission gate
@@ -148,6 +154,8 @@ The scanner follows the current Codex plugin packaging conventions more closely:
148154

149155
`lint --fix` preserves or adds the documented `./` prefixes instead of stripping them away.
150156

157+
For repo-scoped marketplaces, `scan`, `lint`, `verify`, and `doctor` can target the repository root directly. `submit` remains intentionally single-plugin so the emitted artifact points at one concrete plugin package.
158+
151159
## Config + Baseline Example
152160

153161
```toml
@@ -252,6 +260,8 @@ jobs:
252260
upload_sarif: true
253261
```
254262

263+
For a multi-plugin repo, the same workflow can stay pointed at `plugin_dir: "."` as long as the repository has `.agents/plugins/marketplace.json` with local `./plugins/...` entries.
264+
255265
Local pre-commit style hook:
256266

257267
```yaml

action/README.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@ This README is intentionally root-ready for a dedicated GitHub Marketplace actio
88

99
```yaml
1010
- name: Scan Codex Plugin
11-
uses: your-org/hol-codex-plugin-scanner-action@v1
11+
uses: hashgraph-online/hol-codex-plugin-scanner-action@v1
1212
with:
1313
plugin_dir: "./my-plugin"
1414
min_score: 70
1515
fail_on_severity: high
1616
```
1717
18+
If your repository exposes multiple plugins from `.agents/plugins/marketplace.json`, keep `plugin_dir: "."`. The action will discover local `./plugins/...` entries automatically, scan each local plugin, and skip remote marketplace entries.
19+
1820
## Inputs
1921

2022
| Input | Description | Default |
2123
|-------|-------------|---------|
22-
| `plugin_dir` | Path to the plugin directory to scan | `.` |
24+
| `plugin_dir` | Path to a single plugin directory or a repo marketplace root | `.` |
2325
| `mode` | Execution mode: `scan`, `lint`, `verify`, or `submit` | `scan` |
2426
| `format` | Output format: `text`, `json`, `markdown`, `sarif` | `text` |
2527
| `output` | Write report to this file path | `""` |
@@ -78,7 +80,7 @@ Mode notes:
7880
### Basic scan with minimum score gate
7981

8082
```yaml
81-
- uses: your-org/hol-codex-plugin-scanner-action@v1
83+
- uses: hashgraph-online/hol-codex-plugin-scanner-action@v1
8284
with:
8385
plugin_dir: "."
8486
min_score: 70
@@ -96,7 +98,7 @@ jobs:
9698
runs-on: ubuntu-latest
9799
steps:
98100
- uses: actions/checkout@v6
99-
- uses: your-org/hol-codex-plugin-scanner-action@v1
101+
- uses: hashgraph-online/hol-codex-plugin-scanner-action@v1
100102
with:
101103
plugin_dir: "."
102104
mode: scan
@@ -105,10 +107,12 @@ jobs:
105107
upload_sarif: true
106108
```
107109

110+
This `plugin_dir: "."` pattern is correct for both single-plugin repositories and multi-plugin marketplace repositories. When `.agents/plugins/marketplace.json` exists, the action switches into repository mode and scans each local plugin entry declared under `./plugins/...`.
111+
108112
### With Cisco skill scanning
109113

110114
```yaml
111-
- uses: your-org/hol-codex-plugin-scanner-action@v1
115+
- uses: hashgraph-online/hol-codex-plugin-scanner-action@v1
112116
with:
113117
plugin_dir: "."
114118
cisco_skill_scan: on
@@ -120,7 +124,7 @@ The action installs the scanner with its published `cisco` extra enabled, so the
120124
### Export registry payload for Codex ecosystem automation
121125

122126
```yaml
123-
- uses: your-org/hol-codex-plugin-scanner-action@v1
127+
- uses: hashgraph-online/hol-codex-plugin-scanner-action@v1
124128
id: scan
125129
with:
126130
plugin_dir: "."
@@ -153,7 +157,7 @@ jobs:
153157
154158
- name: Scan plugin and submit if eligible
155159
id: scan
156-
uses: your-org/hol-codex-plugin-scanner-action@v1
160+
uses: hashgraph-online/hol-codex-plugin-scanner-action@v1
157161
with:
158162
plugin_dir: "."
159163
min_score: 80
@@ -172,7 +176,7 @@ Use a fine-grained token with `issues:write` on `hashgraph-online/awesome-codex-
172176
### Markdown report as PR comment
173177

174178
```yaml
175-
- uses: your-org/hol-codex-plugin-scanner-action@v1
179+
- uses: hashgraph-online/hol-codex-plugin-scanner-action@v1
176180
id: scan
177181
with:
178182
plugin_dir: "."
@@ -214,12 +218,12 @@ The source bundle for this action lives in the main scanner repository under `ac
214218
Set `mode` to one of `scan`, `lint`, `verify`, or `submit`.
215219

216220
```yaml
217-
- uses: your-org/hol-codex-plugin-scanner-action@v1
221+
- uses: hashgraph-online/hol-codex-plugin-scanner-action@v1
218222
with:
219223
mode: verify
220224
plugin_dir: "."
221225
```
222226

223-
For `submit` mode, use `registry_payload_output` to control artifact path.
227+
For `submit` mode, point `plugin_dir` at one concrete plugin directory. Repository-mode discovery is supported for `scan`, `lint`, and `verify`, but `submit` intentionally remains single-plugin.
224228

225229
For `scan` mode, set `upload_sarif: true` to emit and upload SARIF automatically instead of wiring a separate upload step by hand.

action/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ inputs:
88
required: false
99
default: "scan"
1010
plugin_dir:
11-
description: "Path to the plugin directory to scan (default: repository root)"
11+
description: "Path to a single plugin directory or a repo marketplace root (default: repository root)"
1212
required: false
1313
default: "."
1414
format:

schemas/scan-result.v1.json

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"profile",
1010
"policy_pass",
1111
"verify_pass",
12+
"scope",
1213
"score",
1314
"raw_score",
1415
"effective_score",
@@ -37,6 +38,10 @@
3738
"verify_pass": {
3839
"type": "boolean"
3940
},
41+
"scope": {
42+
"type": "string",
43+
"enum": ["plugin", "repository"]
44+
},
4045
"score": {
4146
"type": "integer",
4247
"minimum": 0,
@@ -95,6 +100,32 @@
95100
"pluginDir": {
96101
"type": "string",
97102
"minLength": 1
103+
},
104+
"repository": {
105+
"type": "object",
106+
"additionalProperties": false,
107+
"required": ["marketplaceFile", "localPluginCount"],
108+
"properties": {
109+
"marketplaceFile": {
110+
"type": ["string", "null"]
111+
},
112+
"localPluginCount": {
113+
"type": "integer",
114+
"minimum": 0
115+
}
116+
}
117+
},
118+
"plugins": {
119+
"type": "array",
120+
"items": {
121+
"$ref": "#/$defs/pluginSummary"
122+
}
123+
},
124+
"skippedTargets": {
125+
"type": "array",
126+
"items": {
127+
"$ref": "#/$defs/skippedTarget"
128+
}
98129
}
99130
},
100131
"$defs": {
@@ -145,16 +176,20 @@
145176
}
146177
},
147178
"finding": {
148-
"allOf": [
149-
{"$ref": "#/$defs/findingRef"},
150-
{
151-
"type": "object",
152-
"required": ["category"],
153-
"properties": {
154-
"category": {"type": "string", "minLength": 1}
155-
}
156-
}
157-
]
179+
"type": "object",
180+
"additionalProperties": false,
181+
"required": ["ruleId", "severity", "title", "description", "source", "category"],
182+
"properties": {
183+
"ruleId": {"type": "string", "minLength": 1},
184+
"severity": {"$ref": "#/$defs/severity"},
185+
"title": {"type": "string", "minLength": 1},
186+
"description": {"type": "string", "minLength": 1},
187+
"remediation": {"type": ["string", "null"]},
188+
"filePath": {"type": ["string", "null"]},
189+
"lineNumber": {"type": ["integer", "null"], "minimum": 1},
190+
"source": {"type": "string", "minLength": 1},
191+
"category": {"type": "string", "minLength": 1}
192+
}
158193
},
159194
"check": {
160195
"type": "object",
@@ -185,6 +220,39 @@
185220
"items": {"$ref": "#/$defs/check"}
186221
}
187222
}
223+
},
224+
"pluginSummary": {
225+
"type": "object",
226+
"additionalProperties": false,
227+
"required": ["name", "pluginDir", "score", "grade", "summary"],
228+
"properties": {
229+
"name": {"type": "string", "minLength": 1},
230+
"pluginDir": {"type": "string", "minLength": 1},
231+
"score": {"type": "integer", "minimum": 0, "maximum": 100},
232+
"grade": {"type": "string", "enum": ["A", "B", "C", "D", "F"]},
233+
"summary": {
234+
"type": "object",
235+
"additionalProperties": false,
236+
"required": ["findings", "integrations"],
237+
"properties": {
238+
"findings": {"$ref": "#/$defs/severityCounts"},
239+
"integrations": {
240+
"type": "array",
241+
"items": {"$ref": "#/$defs/integration"}
242+
}
243+
}
244+
}
245+
}
246+
},
247+
"skippedTarget": {
248+
"type": "object",
249+
"additionalProperties": false,
250+
"required": ["name", "reason", "sourcePath"],
251+
"properties": {
252+
"name": {"type": "string", "minLength": 1},
253+
"reason": {"type": "string", "minLength": 1},
254+
"sourcePath": {"type": ["string", "null"]}
255+
}
188256
}
189257
}
190258
}

schemas/verify-result.v1.json

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"title": "VerifyResultV1",
44
"type": "object",
55
"additionalProperties": false,
6-
"required": ["verify_pass", "workspace", "cases"],
6+
"required": ["verify_pass", "workspace", "scope", "cases"],
77
"properties": {
88
"verify_pass": {
99
"type": "boolean"
@@ -12,19 +12,69 @@
1212
"type": "string",
1313
"minLength": 1
1414
},
15+
"scope": {
16+
"type": "string",
17+
"enum": ["plugin", "repository"]
18+
},
19+
"repository": {
20+
"type": "object",
21+
"additionalProperties": false,
22+
"required": ["marketplaceFile", "localPluginCount"],
23+
"properties": {
24+
"marketplaceFile": {"type": ["string", "null"]},
25+
"localPluginCount": {"type": "integer", "minimum": 0}
26+
}
27+
},
28+
"plugins": {
29+
"type": "array",
30+
"items": {
31+
"$ref": "#/$defs/pluginVerifySummary"
32+
}
33+
},
34+
"skippedTargets": {
35+
"type": "array",
36+
"items": {
37+
"$ref": "#/$defs/skippedTarget"
38+
}
39+
},
1540
"cases": {
1641
"type": "array",
1742
"items": {
18-
"type": "object",
19-
"additionalProperties": false,
20-
"required": ["component", "name", "passed", "message", "classification"],
21-
"properties": {
22-
"component": {"type": "string", "minLength": 1},
23-
"name": {"type": "string", "minLength": 1},
24-
"passed": {"type": "boolean"},
25-
"message": {"type": "string"},
26-
"classification": {"type": "string", "minLength": 1}
27-
}
43+
"$ref": "#/$defs/case"
44+
}
45+
}
46+
},
47+
"$defs": {
48+
"case": {
49+
"type": "object",
50+
"additionalProperties": false,
51+
"required": ["component", "name", "passed", "message", "classification"],
52+
"properties": {
53+
"component": {"type": "string", "minLength": 1},
54+
"name": {"type": "string", "minLength": 1},
55+
"passed": {"type": "boolean"},
56+
"message": {"type": "string"},
57+
"classification": {"type": "string", "minLength": 1}
58+
}
59+
},
60+
"pluginVerifySummary": {
61+
"type": "object",
62+
"additionalProperties": false,
63+
"required": ["name", "workspace", "verify_pass"],
64+
"properties": {
65+
"name": {"type": ["string", "null"]},
66+
"workspace": {"type": "string", "minLength": 1},
67+
"verify_pass": {"type": "boolean"}
68+
}
69+
},
70+
"skippedTarget": {
71+
"type": "object",
72+
"additionalProperties": false,
73+
"required": ["name", "reason", "sourcePath"],
74+
"properties": {
75+
"name": {"type": "string", "minLength": 1},
76+
"reason": {"type": "string", "minLength": 1},
77+
"sourcePath": {"type": ["string", "null"]}
2878
}
2979
}
3080
}

0 commit comments

Comments
 (0)