Guide
Getting a plugin through the directory scan
The scan cannot be run locally, so this plugin runs an approximation of it, reads the report compactly, and bisects what the approximation cannot explain.
Install
/plugin marketplace add gagoar/plugin-directory-preflight /plugin install plugin-directory-preflight@plugin-directory-preflight /reload-plugins
Then ask Claude to run the plugin directory preflight on the repository you are about to upload.
The workflow
- Find the plugin root: the directory with
.claude-plugin/plugin.json, at the repo root or inplugin/. - Check the gate is current:
node scripts/install-gate.ts <repo> --check. Install or update it if not. - Run the gate over the whole repository:
node scripts/check-plugin-safety.ts .. Comments, documents and generated files count. - Validate:
claude plugin validate <plugin-root> --strict. - Fix every hit in one pass, then run steps 3 and 4 again. Each extra round is an upload.
- Bump the version, tag if the plugin ships release assets, and upload.
- Read the report with the report reader. Paste only the
diagnosticsarray, never the whole report.
The scripts
Plain Node 22.6 or later, no dependencies, run as node scripts/<name>.ts.
check-plugin-safety.ts
The gate. Takes one or more roots (default .); prints one line per problem and exits 1 if there are any.
install-gate.ts
node scripts/install-gate.ts <repo> install or update the gate; add the CI workflow if missing node scripts/install-gate.ts <repo> --check report drift only; exit 1 if anything is missing or old node scripts/install-gate.ts <repo> --force also overwrite a workflow that differs from the template
read-report.ts
node scripts/read-report.ts report.json
Prints the phases, each hold or blocking finding, the file the scan named, and the readable files that sort before it. Exits 1 on a hold.
make-probe.ts
node scripts/make-probe.ts round1 <file> <outDir> [slices] node scripts/make-probe.ts narrow <outDir> <slice> [slices] node scripts/make-probe.ts windows <outDir> <slice> node scripts/make-probe.ts explain <outDir> <reported-file>
How the scan behaves
- It names only the first match in path order. A match in an earlier-sorting file hides every later one, and fixing it can make the next one appear.
- Two levels: a file-level finding (a remote host and a credential-looking reference in one file) and a plugin-level one (any credential-looking reference in one script plus any
https://string in any script). - Comments, documents and generated bundles are read like code. Prose can be read as shell.
- Only the default branch is scanned, so stage experiments in a scratch repository. A Markdown file with a tester link and an environment word passed, so links can live in a document.
- A hold sends the version to a human reviewer. It is not a rejection, but it comes back on every version.
What held a real plugin
| What matches | What to do |
|---|---|
| A plugin name within two edits of a directory name, or the same words reordered | Choose a long, descriptive name and compare it with the public catalog names. The display name does not count. |
| A script or document of 256 KiB or more | Split vendored code into readable files under the limit. |
| One word longer than about 4 KiB | Split the string literal with concatenation. |
| A shell command that prints the environment, alone in a call or a condition | Rewrite the call or the variable. Never spell it in prose, comments or documents. |
| A template placeholder whose expression names a credential | Rename it, or build the string by concatenation. |
| Prose that reads as a shell line | Reword. Strip comments from vendored bundles. |
| A glob in a document that covers images or fonts | Use a path with no images under it. |
| A remote host and an environment reference in one file | Move one of them. |
| A native executable in the repository | Ship source and fetch a pinned build. |
| A list of plain English words joined with pipes, when one word is a command word | Build the list from an array and join it. |
| The whole environment used as a value, in a test file or a comment | Read named variables. Describe the shape in words. |
| A script that names an image or a font that exists in the repository | Use a name that no file in the repository has. A document naming it is fine. |
Pinning a hold with the probe
- Run
round1on the file the finding names. It writes a small plugin whoseprobe/holds 16 slices. - Push that folder to a throwaway public repository and upload it.
- The finding names a slice. Run
narrowon it, push, upload. - At a few lines, run
windows. The smallest window that triggers shows how many lines the trigger needs.explainprints the original lines. - Fix every instance of that shape in the real plugin, upload it, and delete the probe repository.
Bundling third-party code
Keep each bundled file under 256 KiB and readable. Strip comments from vendored bundles, split long string literals, rewrite calls and variables that read as lone command words, and neutralize credential-named placeholders. Make each build-time transform fail the build if its target text is not found, so a dependency upgrade cannot silently undo it.
FAQ
Is the gate the real scan? No. It is built from observed behavior. A clean run does not guarantee a clean report, and a hit may not hold in the real scan.
What if only false positives remain? A hold goes to a human reviewer. Use the portal's help action with a short note: what the plugin does and which file matches.
Does it send anything anywhere? No. The scripts read the directory you point them at and write only where you tell them to.