Skip to content

Latest commit

 

History

History
37 lines (19 loc) · 14.2 KB

SCANNING.md

File metadata and controls

37 lines (19 loc) · 14.2 KB

How discovery and validation work

The catalogue lists discovered plugins whose hooks/hooks.json names a module, excluding test fixtures, mirrors of Anthropic's own mods and copies inside a catalogue. Repos in data/catalogs.txt are named once with their count, so their stars never rank a copy above the author's own repo. A mod its author has moved to another repo counts once, on the copy named in data/duplicates.txt; the scanner flags same-owner same-name pairs for review and never collapses them on its own. Entries are sorted by hosting repository stars, which do not measure installs or popularity of an individual mod.

Reach is the widest thing the mod's $ calls can touch. L0 draws and remembers, L1 reads, L2 writes files, runs processes or drives Claude, and L3 reaches the network. It is a footprint, not a verdict; a PR tracker has to run gh. Sees lists what the mod observes without a matcher.

Discovery searches GitHub code for CLAUDE_CODE_ENABLE_FUNCTION_HOOKS and for a hooks/hooks.json with a modules key, and adds the repos in data/seeds.txt. Repository search adds repositories pushed since the last complete search that have a mod topic or "claude mod" in the name or description, and keeps one only when its file tree has a hooks/hooks.json that lists modules. Each query covers its time window in slices small enough for one page of results, so no slice reaches GitHub's 1,000-result cap. Progress is in data/discovery.json, which records when the last complete search started, the repositories already found not to be mods, those left for the next run and those found but not yet merged. Scheduled runs pass it to the next run through the Actions cache, so progress does not wait for a merge. Code search runs once a day. Scans every three hours and after a seed is merged use the known candidates, the seeds and repository search. The scanner clones each repo shallow, finds every plugin with a hooks module, and runs claude plugin validate on its plugin.json. The validator inventories the source and prints the events hooked and the $ calls made; no mod code runs. The grader turns the calls into a reach level and the hooks into a visibility list. tools/grade.mjs grades every member of the mods API that Claude Code 2.1.302 declares in the type file it writes for a mod (listed in tools/api-members.json), each by what its declaration says it does. A call it does not know is unclassified: either a method in a Claude Code namespace that the table does not list, or a call in a namespace that is not in the table at all, which could be an API another mod adds, a different Claude Code version or a typo; pages list those separately. Unclassified calls count as level 1 in the stored level and are named, not scored, on mod pages. $.state.get is graded as a read because any mod can read another mod's state; the scan does not yet record whose state a call reads. Levels are this catalogue's way of ordering what a mod's own code references, not Anthropic security ratings and not limits on what a started program or a rewritten event can do. Every scan, publication and render grades all records again from their recorded calls, so a grading change applies to retained records too. The renderer writes this catalogue, the README count, one badge pair per mod under badges/, the searchable collection under docs/, which GitHub Pages serves as mods.aidojo.si, one page per mod under docs/mods/, and docs/access/, which answers what a mod can access with numbers computed from the same records. The homepage carries the 100 most-starred rows and loads the rest of the table and the access strip from parts under docs/directory/, because Googlebot reads only the first 2 MB of a page or of any file it fetches. Every mod page is also linked from plain HTML A to Z pages under docs/browse/, and the build stops if any of these pages passes 1 MB.

Topic pages under docs/topics/ answer a category query, such as prompt cache mods, with a plain explanation and a link to every member's page. A mod joins a topic by the rule in data/topics.json: patterns matched against its name and description after the renderer's own normalisation, plus ids to include or exclude, applied by tools/topics.mjs at render time and never by a model. Only counted mods with a scan result and a description are listed, whether or not search engines are asked to index their pages. The list sits in three groups by repository stars (100 or more, 10 to 99, under 10) in name order, and prints no star counts, so a rescan that only moves stars and dates changes no topic page while every repository stays inside its group. Each entry carries the author's description, the access line built from the same branches as the mod page's scan summary, and the install hint. node tools/topics.mjs --list <slug> prints every match with the pattern that hit, the excluded and stale ids and the listed count; run it before changing a rule. The page prose lives in tools/topics.mjs keyed by slug, so a new topic is a source-and-data change and a membership edit is a data-only change.

Discovery retains previously found repos and splits searches above GitHub's 1,000-result limit by file size. Incomplete responses are retried; missing pages or changing counts trigger smaller searches. A partition that remains capped or incomplete fails the code search instead of publishing partial results. Each partition must be complete, but GitHub does not provide an atomic snapshot across searches. This covers the configured searches, not every repository on GitHub: indexing and search-pattern limits still apply.

Search requests run sequentially with a ten-second pause. Rate-limit retries honor GitHub's cooldown and quota-reset headers and use exponential backoff with jitter. Each request gets at most seven attempts, with a shared 30-minute retry-wait budget across both searches. If the required wait exceeds the budget, or the code search fails another way, the daily scan discards that search, keeps the known candidates and seeds, and says so in the scan pull request. Discovery never removes a candidate. Logs include rate-limit headers and request IDs for diagnosis.

Scheduled scans retain missing plugins with their last known footprint and mark them “not verified”. A separate weekly retirement workflow proposes removals only when a fresh, successfully inspected checkout no longer contains the hook module. Failed clones, malformed hook files and validation failures do not justify removal. The proposal records the checked revisions in data/retirements.json and requires a maintainer merge. Exception seeds remain candidates until reviewed separately; curated descriptions are maintained by hand.

Each mod page lives at mods.aidojo.si/mods/<owner>--<repo>--<name>/. A page leads with the author's description, then a scan summary built only from the record: the level it states counts the calls the grader classifies, and calls it cannot classify are named rather than scored. When the scan saw a passing root marketplace or an Anthropic directory entry, the page adds an install hint, labelled as a hint because the scan reads the manifest's plugin name rather than the marketplace entry's. data/pages.json records which mod owns each path, and the renderer only adds to it, so a path never passes to a different mod. A same-named plugin that appears later gets a numbered path. A change in repository letter case keeps the same path. When a scan finds a mod under a renamed repository, or data/duplicates.txt maps a copy to its keeper, the old path becomes a redirect to the keeper's page and keeps redirecting after the old record is retired. For a move the scanner cannot see, such as a mod that moved to another repository after the old one was deleted, add "to": "<new path>" to the old path's entry in data/pages.json. The old path redirects even while scans still retain the old record, and serves its own page again if a fresh scan finds it. A retired mod with no keeper loses its page; GitHub Pages cannot answer with a redirect or a 410 status, so that path returns the site's 404 page with a search box. Every mod keeps its page, its badge and its place in the catalogue, the A to Z pages and site search. Search engines are asked to index a page only when its repository has two or more stars, is listed in Anthropic's directory or is built into Claude Code, and its description runs to at least 40 characters. Most catalogued mods are new repositories with no stars yet, and search engines were declining thousands of near-identical pages; stars are the only signal in the scan that someone other than the author has looked. When several new mods that qualify carry the same description, the one in the oldest repository is the indexed page, and a later mod with a description an indexed page already has stays out. Every other page carries noindex and stays out of the sitemap, as do pages for retained records that were not rescanned and for mods without a description. data/pages.json marks a page "indexed": true once it qualifies, and from then on only that mod's own record can take the page out: a description that falls under 40 characters, or a scan that only retains the record. Losing stars, leaving the directory or another mod arriving at the same description does not, so a rescan that changes nothing in a mod's own record never turns its page back. Failed mods stay indexed, because the failure is what someone searching for that mod needs to know. Once Pages serves a change, .github/workflows/indexnow.yml sends the added, changed and removed page URLs to IndexNow, which Bing and several other search engines read; the key file is docs/70e762a8c306a4cfd45110e471061e09.txt.

Pull-request checks combine committed candidates with submitted seeds without running global discovery. Newly added seeds must clone successfully and contain mod plugins that pass validation, with warnings allowed.

Validation is a static check, not a runtime compatibility test. Reserved plugin names fail validation; the raw data and each mod’s page on the website retain the reason. The scanner also validates ancestor marketplaces that list the plugin through a local source path. A marketplace failure appears separately because loading the plugin directly is a different install route. Incomplete or unrecognized results stay “not verified”.

“Review UI rewrite” means that a mod hooks ui.render and its module source or local imports contain control-character strings. The scanner parses JavaScript and TypeScript without executing them, and links each finding to the scanned commit. It excludes comments, regular expressions, tab and newline characters. This is a source-review warning: the strings can have other uses, and computed strings, external packages or unresolved imports can escape detection. No warning does not prove runtime compatibility. See the 2.1.287 compatibility report for the reported restriction on rewritten text.

Benchmark directories and plugins that identify themselves as fixtures or non-installable harnesses are recorded but excluded from the mod count, tables and badges. Confirmed course exercises, field experiments, qualification harnesses and development probes are excluded by repository-specific rules; finished mods elsewhere in the same repository still count, including guards shipped alongside teaching experiments. A prototype folder or a testing tool's name alone does not exclude it. Confirmed local copies use individual duplicate mappings so original mods in a mixed repository remain listed. The workflow tests classification and renders sample lists of up to 1000 mods in Chromium at desktop and mobile widths before scanning.

UI source review uses iterative traversal, with limits of 8 MiB per file, 32 MiB of source, 4,096 files and 1,000,000 syntax nodes per plugin. A read or parser failure, or reaching a limit, adds an explicit ui-review-incomplete warning. Other source modules continue where the remaining budget permits. The repository is not marked fully inspected or cached, so a later scan retries it; required seeds stay blocked and retirement cannot remove earlier records from that repository. Validator results and any control-character findings remain visible separately. These limits bound this source check, not the whole clone or validator process.

An unexpected checkout inspection error is isolated to that repository and named in the scan log. Its earlier records remain with unknown validation and their last known footprint; it cannot count as a successful required-seed check or retirement inspection. Healthy repositories finish and their cache entries are saved. Process termination or a failure outside checkout inspection can still stop a run before the cache is saved.

Each fresh record has a footprint.status of reported only when every declared module has both hooks and calls output. An explicit nothing on $ can establish L0; missing output cannot. Incomplete coverage sets footprint.status to unavailable and reach.level to null, while preserving the source revision, validation result and any reported calls. reach.minimumLevel is the lower bound from those calls, not the mod's total access.

The validator can shorten long lists or individual matcher values with a character-count marker. Either makes the footprint unavailable, even when validation passes. The scanner keeps only complete entries before the first marker; it does not interpret a shortened matcher, the marker or a partial name as source information. These results can be cached for the same source and validator version. A changed validator or checkout reader invalidates the cache.

Older failed records do not establish complete coverage. Until a fresh scan, the website, badges and finder show their access as unknown, including when some calls were recorded. Retained records use their last-known successful validation when available. Rendering normalizes the public JSON to the same unknown representation without rewriting the raw inventory. Old completed scans cannot be reused after a scanner change; regenerate them with a fresh scan.

The public scan result records each entry. See Contributing to reproduce the scan or add badges to your own mod.