scan — Claude Code plugin marketplace
A marketplace repo containing the sleuth plugin: discovery & archaeology for
Claude Code systems. Install it once and run /sleuth:sleuth in any project.
Publishing
This repo is configured for twofoldtech-dakota/sleuth: marketplace name
is scan (what users type after @), owner.name is
twofoldtech-dakota, and the plugin repository is
https://github.com/twofoldtech-dakota/sleuth (a string URL — an object
form fails validation). The only step left is to push the repo to GitHub under
that owner/repo.
Forking this into your own marketplace? Re-set those three values, and don't
use a reserved marketplace name (claude-code-plugins, anthropic-plugins,
etc.).
Install (any project)
After the repo is on GitHub:
/plugin marketplace add twofoldtech-dakota/sleuth
/plugin install sleuth@scan
Then, in any project:
/sleuth:sleuth /path/to/some/harness
(Plugin commands are namespaced by plugin name, so the command is
/sleuth:sleuth. Rename plugins/sleuth/commands/sleuth.md to e.g. audit.md
if you prefer /sleuth:audit.)
It runs read-only against the target and writes
sleuth-dossier-<date>.md to your working directory. Re-run monthly and diff
the dossiers to track drift.
Other install sources
- Local path (fastest for iteration):
/plugin marketplace add /abs/path/to/sleuth - Git URL:
/plugin marketplace add https://gitlab.com/you/sleuth.git - Hot-test without installing:
claude --plugin-dir ./plugins/sleuth, then/reload-plugins. A--plugin-dirplugin shadows an installed one of the same name for that session — handy for hot-fixing before cutting a release.
Relative source paths in marketplace.json resolve only when the marketplace
is added via git (GitHub/GitLab/git URL) or a local path. If you ever serve
marketplace.json from a bare URL, switch plugin source to a
{ "source": "github", "repo": "..." } object.
What this plugin is
"Discover everything" is the wrong objective — a complete inventory is a
manifest: cheap, mechanical, table stakes. The valuable output is inference
with proof: what is load-bearing, dangerous, dead, and worth preserving, with
a path:line citation behind every claim. Success is measured in surprise,
not coverage.
Two stages, and the split is load-bearing:
- Harvest (deterministic) — bundled zero-dependency Python at
plugins/sleuth/scripts/harvest/. Walks the system, emits one structuredevidence.json. The swappable layer (Claude-Code-aware today). - Synthesis (frontier reasoning) — the
sleuthskill + three subagents (depth → synthesis → adversary). The constant, reusable core.
The harvester detects: dead/orphan capabilities (with an entry-point caveat so it never calls a user-typed command or event-fired hook "dead"), divergent duplicates (with the diff), shadow dependencies (undeclared env/MCP/paths), undocumented enforcement-hook guardrails, trigger collisions, and git incident fossils (rationale recovered and tagged as inference; degrades gracefully with no git).
Proven vs. prompt artifact (honest status)
| Component | Status |
|---|---|
plugins/sleuth/scripts/harvest/ | Tested. Green against a planted fixture with six defects and five discrimination controls — eval/run_eval.py → 20/20. |
eval/ fixture + binary eval | Tested. Reproducible; rebuilds git history; asserts each plant is found and each control is not tripped. |
plugins/sleuth/{skills,commands,agents} | Written, not unit-tested. Runs against a live model in your Claude Code; not executable in a sandbox. Validate on a system you already understand before trusting it on one you don't. |
Run the eval
The eval is dev-time only (it is not part of the installed plugin, so it never loads as plugin content):
cd eval && python3 run_eval.py # builds fixture, runs harvester, expects 20/20
Needs Python + git. On Windows it also needs Git for Windows installed and runs
the same from PowerShell or Git Bash — run_eval.py auto-detects Git Bash (set
SLEUTH_BASH to a bash.exe to override).
Layout
sleuth/
├── .claude-plugin/
│ └── marketplace.json # catalog (marketplace name + owner)
├── plugins/
│ └── sleuth/ # THE PLUGIN — only this dir is copied on install
│ ├── .claude-plugin/plugin.json # manifest (repository + author)
│ ├── commands/sleuth.md # /sleuth:sleuth entry point
│ ├── agents/sleuth-{depth,synthesis,adversary}.md
│ ├── skills/sleuth/SKILL.md # methodology core
│ ├── scripts/harvest/*.py # bundled deterministic harvester (tested)
│ └── README.md
├── eval/ # proof + regression harness (NOT installed)
│ ├── run_eval.py
│ ├── build_fixture.sh
│ └── fixture_src/ # planted .claude tree (6 defects + controls)
└── README.md
Extending the fixture (the compounding part)
When a real run misses something or fires a false positive, encode that case as
a new planted defect (or control) in eval/fixture_src/ and a new assertion in
eval/run_eval.py. The harvester then can never silently regress on it. Same
discipline as trigger-accuracy gating on a skills repo: every miss becomes a
permanent test.
Known blind spots
Static analysis only: textual reference matching undercounts runtime-constructed names (over-reporting "dead"); auto-delegation is inferred from description quality, not observed; duplicate detection is textual; description scoring is a heuristic prefilter, not a classifier. Every run emits its own blind-spot section — read it. The dossier is a ranked set of strong, evidence-backed hypotheses, not an oracle.