docs(humanizer): v5.1.0 release notes across plugin + marketplace docs
- Plugin README: add "What's New in v5.1.0" section with humanizer overview, before/after example, plain-language vocabulary table, --raw flag docs. Bump version badge 5.0.0 → 5.1.0. Add Version History row. - Plugin CLAUDE.md: add humanizer.mjs + humanizer-data.mjs to Scanner Lib table. Add "Plain-Language Output (v5.1.0)" section documenting output modes, vocabularies, and Wave 5 lessons. Bump test count 635 → 792 across 52 test files. - Marketplace root README: bump config-audit entry 5.0.0 → 5.1.0, update one-line description to mention plain-language UX, add bullet for the v5.1.0 humanizer, bump test count 635+ → 792+. Test-normalizer hardening (consequence of growing CLAUDE.md): walkClaudeMdCascade walks upward from the marketplace-medium fixture into this plugin's own CLAUDE.md, so any docs edit ripples into `scanners[*].activeConfig.claudeMdEstimatedTokens`. The v5.0.0 byte-stability contract is about scanner internals being unchanged, not ancestor input content being frozen. Normalizers in json-backcompat, raw-backcompat, posture-humanizer, scan-orchestrator-humanizer, and snapshot-default-output now strip claudeMdEstimatedTokens to <ANCESTOR_DERIVED>. The default-output snapshot for scan-orchestrator was re-seeded via UPDATE_SNAPSHOT=1 (intent: Wave 6 docs additions; humanizer prose unchanged). Verify: - grep -E "5\.1\.0|v5\.1\.0" README.md CLAUDE.md ../../README.md | wc -l = 12 - node --test 'tests/**/*.test.mjs' = 792/792 pass - self-audit configGrade A (97), pluginGrade A (100), readmeCheck.passed true
This commit is contained in:
parent
819fd47ce0
commit
fc8808d6e4
9 changed files with 170 additions and 10 deletions
|
|
@ -51,9 +51,9 @@ Key commands: `/security posture`, `/security audit`, `/security scan`, `/securi
|
|||
|
||||
---
|
||||
|
||||
### [Config-Audit](plugins/config-audit/) `v5.0.0`
|
||||
### [Config-Audit](plugins/config-audit/) `v5.1.0`
|
||||
|
||||
Configuration intelligence for Claude Code — health checks, feature discovery, auto-fix, active-config inventory, and reality-based Opus-4.7 token analysis.
|
||||
Configuration intelligence for Claude Code — health checks, feature discovery, auto-fix, active-config inventory, reality-based Opus-4.7 token analysis, and plain-language UX that leads with prose ("Fix soon: The same automation is set up more than once") instead of technical IDs.
|
||||
|
||||
Claude Code reads instructions from 7+ file types across multiple scopes. This plugin tells you what's wrong, what's missing, what's silently conflicting, what's actually loaded, and where you're burning tokens unnecessarily:
|
||||
|
||||
|
|
@ -63,10 +63,11 @@ Claude Code reads instructions from 7+ file types across multiple scopes. This p
|
|||
- **What's active** — read-only inventory of plugins, skills, MCP servers, hooks, and CLAUDE.md cascade for a repo, with token estimates
|
||||
- **Token hotspots** — `/config-audit tokens` ranks files by estimated waste across 6 Opus-4.7 patterns (cache-breaking volatile content, redundant tool permissions, deep import chains, oversized cascades, bloated SKILL.md descriptions, MCP tool-schema budget). Optional `--accurate-tokens` calibrates against Anthropic's `count_tokens` API.
|
||||
- **System-prompt manifest** — `/config-audit manifest` ranks every token source (CLAUDE.md cascade, plugins, skills, MCP servers, hooks) by estimated tokens
|
||||
- **Plain-language UX (v5.1.0)** — default output of all 18 commands leads with prose; findings group by user-impact category (Configuration mistake, Conflict, Wasted tokens, Missed opportunity, Dead config) and urgency phrase (Fix this now → FYI). Pass `--raw` for v5.0.0 verbatim output; `--json` is unchanged and byte-stable.
|
||||
|
||||
Key commands: `/config-audit posture`, `/config-audit feature-gap`, `/config-audit fix`, `/config-audit whats-active`, `/config-audit tokens`, `/config-audit manifest`
|
||||
|
||||
6 agents · 12 scanners · 18 commands · 635+ tests
|
||||
6 agents · 12 scanners · 18 commands · 792+ tests
|
||||
|
||||
→ [Full documentation](plugins/config-audit/README.md)
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ Scanner CLI: `node scanners/scan-orchestrator.mjs <path> [--global] [--full-mach
|
|||
| `suppression.mjs` | .config-audit-ignore parsing, finding suppression, audit trail |
|
||||
| `active-config-reader.mjs` | Read-only inventory: readActiveConfig(), detectGitRoot(), walkClaudeMdCascade(), readClaudeJsonProjectSlice() (longest-prefix match), enumeratePlugins(), enumerateSkills(), readActiveHooks(), readActiveMcpServers() (with cache → package.json tool-count fallback), estimateTokens() (v5: `'mcp'` kind = 500 + toolCount × 200) |
|
||||
| `tokenizer-api.mjs` | Anthropic `count_tokens` wrapper for `--accurate-tokens` (v5 N5); 5s AbortController timeout, exponential 429 backoff, key masking |
|
||||
| `humanizer.mjs` | Plain-language output translator (v5.1.0): `humanizeFinding`, `humanizeFindings`, `humanizeEnvelope`, `computeRelevanceContext`. Pure functions; never mutate inputs. Adds `userImpactCategory`, `userActionLanguage`, `relevanceContext` fields and replaces title/description/recommendation when a translation exists. Bypassed by `--raw` and `--json` paths. |
|
||||
| `humanizer-data.mjs` | TRANSLATIONS table for 13 scanner prefixes (CML/SET/HKV/RUL/MCP/IMP/CNF/COL/TOK/CPS/DIS/GAP/PLH). Three-step lookup: exact title → regex pattern → `_default` → fall through to original |
|
||||
|
||||
### Action Engines (`scanners/`)
|
||||
|
||||
|
|
@ -130,6 +132,57 @@ Scanner CLI: `node scanners/scan-orchestrator.mjs <path> [--global] [--full-mach
|
|||
| SessionStart | `session-start.mjs` | Checks for active (unfinished) sessions |
|
||||
| Stop | `stop-session-reminder.mjs` | Reminds about current session phase |
|
||||
|
||||
## Plain-Language Output (v5.1.0)
|
||||
|
||||
Default output of all 18 commands routes through `humanizeEnvelope` from `lib/humanizer.mjs`. Findings are decorated with three additive fields and may have title/description/recommendation replaced when a translation exists.
|
||||
|
||||
### Output modes
|
||||
|
||||
| Flag | Behavior |
|
||||
|------|----------|
|
||||
| (default, no flag) | Plain-language: humanizer applied, findings group by user-impact, titles lead with prose. Self-audit terminal render also humanized. |
|
||||
| `--raw` | Byte-stable v5.0.0 verbatim — humanizer bypassed, technical IDs and severity-only labels. For tooling that scrapes stderr from v5.0.0. |
|
||||
| `--json` | Unchanged from v5.0.0 — humanizer bypassed, byte-stable JSON envelope. Always preferred for programmatic consumption over `--raw`. |
|
||||
| `--output-file <path>` | Writes raw v5.0.0-shape JSON (humanizer bypassed). Posture-specific. |
|
||||
|
||||
`--raw` is threaded through every CLI: `posture.mjs`, `scan-orchestrator.mjs`, `token-hotspots-cli.mjs`, `manifest.mjs`, `whats-active.mjs`, `fix-cli.mjs`, `drift-cli.mjs`, `self-audit.mjs`.
|
||||
|
||||
### Vocabularies
|
||||
|
||||
User-impact category (added to each finding as `userImpactCategory`, derived from scanner prefix):
|
||||
|
||||
| Label | Scanners |
|
||||
|-------|----------|
|
||||
| Configuration mistake | CML, SET, HKV, RUL, MCP, IMP, PLH |
|
||||
| Conflict | CNF, COL |
|
||||
| Wasted tokens | TOK, CPS |
|
||||
| Dead config | DIS |
|
||||
| Missed opportunity | GAP |
|
||||
|
||||
Action language (added to each finding as `userActionLanguage`, derived from severity):
|
||||
|
||||
| Severity | Phrase |
|
||||
|----------|--------|
|
||||
| critical | Fix this now |
|
||||
| high | Fix soon |
|
||||
| medium | Fix when convenient |
|
||||
| low | Optional cleanup |
|
||||
| info | FYI |
|
||||
|
||||
Relevance context (added to each finding as `relevanceContext`, computed from finding's file path):
|
||||
|
||||
| Value | When |
|
||||
|-------|------|
|
||||
| `test-fixture-no-impact` | Path contains `/tests/fixtures/` or `/test/fixtures/` |
|
||||
| `affects-this-machine-only` | Basename matches `*.local.*` (e.g., `settings.local.json`) |
|
||||
| `affects-everyone` | Default — assumed shared/committed config |
|
||||
|
||||
### Wave 5 lessons
|
||||
|
||||
- Posture's stderr scorecard is rendered prose-side and is not part of the JSON envelope; `humanized.areas[].titleHumanized` referenced by command templates lives only in the prose render.
|
||||
- Posture's `--output-file` writes raw v5.0.0-shape JSON because `posture.mjs` does not call `humanizeEnvelope`. If session-files should later be humanized, posture needs its own humanize pass — out of v5.1.0 scope.
|
||||
- The default-output snapshot at `tests/snapshots/default-output/posture.json` is frozen — change requires `UPDATE_SNAPSHOT=1` plus intent confirmation.
|
||||
|
||||
## Suppressions
|
||||
|
||||
Create `.config-audit-ignore` at project root to suppress known findings:
|
||||
|
|
@ -165,7 +218,7 @@ Default: auto-detects scope from git context. Override with `/config-audit full|
|
|||
node --test 'tests/**/*.test.mjs'
|
||||
```
|
||||
|
||||
635 tests across 36 test files (12 lib + 23 scanner + 1 hook). Test fixtures in `tests/fixtures/`.
|
||||
792 tests across 52 test files (15 lib + 28 scanner + 1 hook + 1 agent + 3 commands + 4 top-level). Test fixtures in `tests/fixtures/`. Top-level humanizer tests: `json-backcompat.test.mjs`, `raw-backcompat.test.mjs`, `scenario-read-test.test.mjs`, `snapshot-default-output.test.mjs`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
*AI-generated: all code produced by Claude Code through dialog-driven development. [Full disclosure →](../../README.md#ai-generated-code-disclosure)*
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
|
@ -21,6 +21,7 @@ A Claude Code plugin that checks configuration health, suggests context-aware im
|
|||
|
||||
## Table of Contents
|
||||
|
||||
- [What's New in v5.1.0](#whats-new-in-v510)
|
||||
- [What Is This?](#what-is-this)
|
||||
- [The Configuration Problem](#the-configuration-problem)
|
||||
- [Quick Start](#quick-start)
|
||||
|
|
@ -44,6 +45,59 @@ A Claude Code plugin that checks configuration health, suggests context-aware im
|
|||
|
||||
---
|
||||
|
||||
## What's New in v5.1.0
|
||||
|
||||
**Plain-language UX humanizer** — every command's default output now leads with prose. Findings are grouped by what they mean for the user (Configuration mistake, Conflict, Wasted tokens, Missed opportunity, Dead config) and led with an urgency phrase (Fix this now, Fix soon, Fix when convenient, Optional cleanup, FYI). Technical IDs (`CA-CML-001`, `CA-TOK-005`, …) still appear, but at end-of-line where they belong as references rather than headlines.
|
||||
|
||||
### Before / after
|
||||
|
||||
```
|
||||
v5.0.0 default
|
||||
- [low] CA-CNF-001: Hook duplicate event registration
|
||||
|
||||
v5.1.0 default
|
||||
- [low] The same automation is set up more than once
|
||||
|
||||
v5.1.0 with --json (machine-readable, byte-stable)
|
||||
{ "id": "CA-CNF-001", "title": "...", "userImpactCategory": "Conflict",
|
||||
"userActionLanguage": "Optional cleanup", "relevanceContext": "affects-everyone" }
|
||||
```
|
||||
|
||||
### Plain-language vocabulary
|
||||
|
||||
The toolchain uses these terms when describing findings:
|
||||
|
||||
| User-facing label | What it means |
|
||||
|-------------------|---------------|
|
||||
| Fix this now | Something is broken or risky and should be addressed immediately |
|
||||
| Fix soon | High-priority issue worth scheduling this week |
|
||||
| Fix when convenient | Real issue but not urgent |
|
||||
| Optional cleanup | Tidy-up that improves polish but isn't required |
|
||||
| FYI | Informational; no action expected |
|
||||
| Configuration mistake | A configuration file has an error or omission |
|
||||
| Conflict | Two configuration sources disagree |
|
||||
| Wasted tokens | Configuration is loading content that costs tokens without payback |
|
||||
| Missed opportunity | A Claude Code feature you aren't using that could help your project |
|
||||
| Dead config | Configuration that has no effect (e.g., a permission that's also denied) |
|
||||
|
||||
### Backwards compatibility — the `--raw` flag
|
||||
|
||||
Every CLI accepts `--raw` for byte-stable v5.0.0 verbatim output (technical IDs, raw severity, no prose translation). `--json` is unchanged from v5.0.0 — already byte-stable for programmatic consumption. Use `--raw` only if you've built tooling against v5.0.0 stderr scrapes; for new automation, prefer `--json`.
|
||||
|
||||
```bash
|
||||
node scanners/posture.mjs . # v5.1.0 plain-language default
|
||||
node scanners/posture.mjs . --raw # v5.0.0 verbatim (byte-stable)
|
||||
node scanners/posture.mjs . --json # unchanged JSON envelope
|
||||
```
|
||||
|
||||
### What's not changed
|
||||
|
||||
- All scanner internals (12 scanners + standalone PLH) emit the same finding IDs and structural data — humanization happens at output-formatting time only
|
||||
- `--json` envelope shape is byte-stable with v5.0.0 (humanizer fields are additive on findings only in default mode; the `--json` path bypasses humanization entirely)
|
||||
- 635 tests grew to 792 (+157 covering humanizer module, scenario read-tests, forbidden-words lint, JSON / `--raw` backwards-compat, default-output snapshots, and command-template / agent-prompt shape)
|
||||
|
||||
---
|
||||
|
||||
## What Is This?
|
||||
|
||||
Claude Code reads instructions from at least 7 different file types across multiple scopes: `CLAUDE.md`, `settings.json`, `.claude/rules/`, `hooks.json`, `.mcp.json`, `.claudeignore`, and `settings.local.json`. Each can exist at project level, user level, or both. Plugins add more. The system is powerful — but nobody tells you what you're using wrong, what you're missing, or what's silently conflicting.
|
||||
|
|
@ -544,6 +598,7 @@ This plugin is cautious by design — configuration files are important, and a b
|
|||
|
||||
| Version | Date | Highlights |
|
||||
|---------|------|-----------|
|
||||
| **5.1.0** | 2026-05-01 | Plain-language UX humanizer. Default output of all 18 commands now leads with prose; findings grouped by user-impact category (Configuration mistake, Conflict, Wasted tokens, Missed opportunity, Dead config) and led by urgency phrase (Fix this now → FYI). New `--raw` flag preserves v5.0.0 verbatim output for tooling that scrapes stderr; `--json` is unchanged and byte-stable. New scanner-lib modules: `humanizer.mjs`, `humanizer-data.mjs` with TRANSLATIONS for 13 scanner prefixes. Self-audit terminal output also humanized. 792 tests (+157 humanizer-tester) |
|
||||
| **5.0.0** | 2026-05-01 | Reality-based token-optimization. 3 new scanners (CPS cache-prefix, DIS dead tools, COL plugin collisions) → 12 deterministic scanners. New `/config-audit manifest` and `--accurate-tokens` API calibration. Severity-weighted scoring (`scoringVersion: 'v5'`). MCP token estimates 15 → 500+. Plugin Hygiene as 10th quality area. Knowledge: cache-stability replaces 200-line rule, cache-telemetry recipe. **Breaking:** F2 token magnitude jump, F3 severity weighting, F5 Pattern D removed, N1 `CA-TOK-*` glob now matches CA-TOK-005. 635 tests |
|
||||
| **4.0.0** | 2026-04-19 | Opus 4.7 era: new TOK scanner (cache-breaking volatile content, redundant tool permissions, deep import chains, sonnet-era setups), `/config-audit tokens` command, Token Efficiency 8th quality area, scanner-agent + verifier-agent migrated haiku → sonnet. 543 tests |
|
||||
| **3.1.0** | 2026-04-14 | New `/config-audit whats-active` — read-only inventory of active plugins, skills, MCP, hooks, CLAUDE.md for a repo, with token estimates. 522 tests |
|
||||
|
|
|
|||
|
|
@ -67,9 +67,24 @@ async function ensureDriftBaseline() {
|
|||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Normalizers — strip time / path fields that vary between runs.
|
||||
// Normalizers — strip time / path / ancestor-derived fields that vary
|
||||
// independently of scanner internals. `claudeMdEstimatedTokens` is computed
|
||||
// by walking the FS cascade upward from the fixture; any edit to this
|
||||
// plugin's own CLAUDE.md ripples into it, even though scanner behavior is
|
||||
// unchanged. The byte-stability contract covers scanner output shape, not
|
||||
// the size of ancestor input docs.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function stripAncestorDerived(envOrEnvelope) {
|
||||
if (Array.isArray(envOrEnvelope?.scanners)) {
|
||||
for (const s of envOrEnvelope.scanners) {
|
||||
if (s?.activeConfig && 'claudeMdEstimatedTokens' in s.activeConfig) {
|
||||
s.activeConfig.claudeMdEstimatedTokens = '<ANCESTOR_DERIVED>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeScanOrchestrator(env) {
|
||||
const out = JSON.parse(JSON.stringify(env));
|
||||
if (out.meta) {
|
||||
|
|
@ -81,6 +96,7 @@ function normalizeScanOrchestrator(env) {
|
|||
s.duration_ms = 0;
|
||||
}
|
||||
}
|
||||
stripAncestorDerived(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +112,7 @@ function normalizePosture(p) {
|
|||
s.duration_ms = 0;
|
||||
}
|
||||
}
|
||||
stripAncestorDerived(out.scannerEnvelope);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,8 +66,21 @@ async function ensureDriftBaseline() {
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Normalizers — same as json-backcompat to keep the contracts aligned.
|
||||
// `claudeMdEstimatedTokens` is stripped because walkClaudeMdCascade walks
|
||||
// upward from the fixture into this plugin's own CLAUDE.md; any docs edit
|
||||
// here ripples into it even when scanner internals are unchanged.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function stripAncestorDerived(envOrEnvelope) {
|
||||
if (Array.isArray(envOrEnvelope?.scanners)) {
|
||||
for (const s of envOrEnvelope.scanners) {
|
||||
if (s?.activeConfig && 'claudeMdEstimatedTokens' in s.activeConfig) {
|
||||
s.activeConfig.claudeMdEstimatedTokens = '<ANCESTOR_DERIVED>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeScanOrchestrator(env) {
|
||||
const out = JSON.parse(JSON.stringify(env));
|
||||
if (out.meta) {
|
||||
|
|
@ -79,6 +92,7 @@ function normalizeScanOrchestrator(env) {
|
|||
s.duration_ms = 0;
|
||||
}
|
||||
}
|
||||
stripAncestorDerived(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +108,7 @@ function normalizePosture(p) {
|
|||
s.duration_ms = 0;
|
||||
}
|
||||
}
|
||||
stripAncestorDerived(out.scannerEnvelope);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ const POSTURE_STDERR_SNAPSHOT = resolve(REPO, 'tests/snapshots/v5.0.0-stderr/pos
|
|||
|
||||
/**
|
||||
* Normalize a runPosture result for snapshot comparison by zeroing out
|
||||
* time-varying fields and machine-specific paths.
|
||||
* time-varying fields, machine-specific paths, and ancestor-cascade-derived
|
||||
* counts. `claudeMdEstimatedTokens` reflects walkClaudeMdCascade walking
|
||||
* upward from the fixture; any docs edit to this plugin's own CLAUDE.md
|
||||
* ripples into it even though scanner behavior is unchanged.
|
||||
*/
|
||||
function normalizePosture(p) {
|
||||
const out = JSON.parse(JSON.stringify(p));
|
||||
|
|
@ -28,6 +31,9 @@ function normalizePosture(p) {
|
|||
if (Array.isArray(out.scannerEnvelope.scanners)) {
|
||||
for (const s of out.scannerEnvelope.scanners) {
|
||||
s.duration_ms = 0;
|
||||
if (s.activeConfig && 'claudeMdEstimatedTokens' in s.activeConfig) {
|
||||
s.activeConfig.claudeMdEstimatedTokens = '<ANCESTOR_DERIVED>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,11 @@ const SNAPSHOT_PATH = resolve(REPO, 'tests/snapshots/v5.0.0/scan-orchestrator.js
|
|||
|
||||
/**
|
||||
* Normalize a scan-orchestrator envelope for snapshot comparison by
|
||||
* blanking out time-varying fields (timestamp, durations, target path).
|
||||
* Returns a NEW object — does not mutate input.
|
||||
* blanking out time-varying fields (timestamp, durations, target path)
|
||||
* and ancestor-cascade-derived counts. `claudeMdEstimatedTokens` reflects
|
||||
* walkClaudeMdCascade walking upward from the fixture; any docs edit to
|
||||
* this plugin's own CLAUDE.md ripples into it even when scanner behavior
|
||||
* is unchanged. Returns a NEW object — does not mutate input.
|
||||
*/
|
||||
function normalizeEnvelope(env) {
|
||||
const out = JSON.parse(JSON.stringify(env));
|
||||
|
|
@ -27,6 +30,9 @@ function normalizeEnvelope(env) {
|
|||
if (Array.isArray(out.scanners)) {
|
||||
for (const s of out.scanners) {
|
||||
s.duration_ms = 0;
|
||||
if (s.activeConfig && 'claudeMdEstimatedTokens' in s.activeConfig) {
|
||||
s.activeConfig.claudeMdEstimatedTokens = '<ANCESTOR_DERIVED>';
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
|
|
|
|||
|
|
@ -58,6 +58,13 @@ function normalizeScanOrchestrator(env) {
|
|||
if (Array.isArray(out.scanners)) {
|
||||
for (const s of out.scanners) {
|
||||
s.duration_ms = 0;
|
||||
// claudeMdEstimatedTokens reflects walkClaudeMdCascade walking up from
|
||||
// the fixture into this plugin's own CLAUDE.md; any docs edit ripples
|
||||
// into it independently of scanner internals. Strip to keep the
|
||||
// default-output snapshot focused on humanizer prose stability.
|
||||
if (s.activeConfig && 'claudeMdEstimatedTokens' in s.activeConfig) {
|
||||
s.activeConfig.claudeMdEstimatedTokens = '<ANCESTOR_DERIVED>';
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
|
|
|
|||
|
|
@ -491,7 +491,7 @@
|
|||
],
|
||||
"total_estimated_tokens": 1809,
|
||||
"activeConfig": {
|
||||
"claudeMdEstimatedTokens": 5716,
|
||||
"claudeMdEstimatedTokens": "<ANCESTOR_DERIVED>",
|
||||
"mcpServerCount": 3,
|
||||
"pluginCount": 41,
|
||||
"skillCount": 65
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue