Registro público
Informe de salud del softwareesquema 0.27.0 · métricas 2.5.0 · 2026-07-29 20:11 UTC

cboxdk / statamic-mcp

MCP (Model Context Protocol) server for Statamic CMS v6 — gives AI assistants structured access to content, blueprints, assets, and more.

PHPSin licencia detectada★ 32 estrellas⑂ 10 forksdesde ago 2025Ver en GitHub ↗
TipoBibliotecacómo se determina

cboxdk/statamic-mcp tiene un índice de salud de 71 sobre 100, lo que lo sitúa en la banda Bueno. Su puntuación más alta es Engineering Quality (87/100) y la más baja, Community & Adoption (36/100). Se actualizó por última vez hoy. Una sola persona concentra la mayor parte del trabajo reciente.

71
global / 100
Bueno

Índice de salud del software

Las métricas se agrupan en categorías ponderadas sobre una escala estandarizada de 1 a 100. El resultado global parte de su media ponderada, calibrada contra la distribución del registro público para que las bandas tengan significado percentil; cuando la evidencia pública activa la Política de Jurisdicciones de Alto Riesgo, la calificación se ajusta y recibe un límite «En riesgo» de 34.

71
Excepcional93-100El nivel más alto del registro (≈ el 5% superior); cumple prácticamente todos los criterios evaluados
Excelente80-92Sólido en todos los frentes; carencias menores
Bueno65-79Saludable; carencias limitadas y manejables
Moderado50-64Aceptable con carencias notables; se recomienda revisión
Débil35-49Debilidades sustanciales en varias áreas
En riesgo20-34Debilidades significativas; su adopción exige cautela
Crítico1-19Problemas graves (proyecto abandonado, un solo mantenedor, sin higiene)
VitalidadComunidad yAdopciónSostenibilidady GobernanzaCalidad deIngenieríaSeguridadPreparaciónpara IA

Perfil de puntuación

Cada eje es una categoría. La forma importa más que la media: un proyecto sano llena toda la figura, mientras que un perfil de picos y cráteres indica que la fortaleza en una dimensión enmascara el riesgo en otra.

El resultado global ponderado 63 se calibra a 71 en la escala publicada del índice (calibración del registro 2026-08-02).

Titularidad

CboxOrganización
6 seguidores66 repositorios públicosdesde jun 2016

Este repositorio está respaldado por una organización: una custodia compartida y responsable que puede sobrevivir a cualquier mantenedor individual.

Ecosistemas de paquetes

RegistroPaqueteVersiónDescargas / mesVersionesÚltima publicaciónEtiquetas
Packagistcboxdk/statamic-mcpv2.7.0532231hace 24 díastoolsdevelopmentlaravelcursoraistatamicmcpclaude

Métricas por categoría

Vitalidad

¿Está vivo el proyecto: se escribe código y se publican versiones?

84Excelente · 21% del índice global
Cómo se puntúa
36/36Recencia de push — último push hace 0 días
11.8/36Cadencia de commits — 17/52 semanas con commits
18/18Volumen de commits — 150 commits en el último año
8/10OpenSSF Scorecard: Maintained — 10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8
Datos de entrada utilizados
commits_last_year150
human_commit_share1
days_since_last_push0
active_weeks_last_year17
Cómo se puntúa
27/27Publica versiones — 31 versiones publicadas
36/36Recencia de las versiones — última versión hace 24 días
27/27Cadencia de publicación — una versión cada ~9,1 días
0/10OpenSSF Scorecard: Signed-Releases — sin datos
Datos de entrada utilizados
releases_count31
latest_release_tagv2.7.0
releases_from_tagsno
days_since_latest_release24
mean_days_between_releases9,1
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: Signed-Releases. Los pesos restantes se han renormalizado.

Comunidad y Adopción

¿Tiene el proyecto usuarios, descargas, atención y unas condiciones acogedoras para quienes contribuyen?

36Débil · 17% del índice global
Cómo se puntúa
24.2/60Estrellas — 32 estrellas
8/25Forks — 10 forks
0/15Observadores — 0 observadores
Datos de entrada utilizados
forks10
stars32
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Cómo se puntúa
22.5/22.5README
0/22.5Licencia — no se detectó ningún archivo de licencia
0/18Guía CONTRIBUTING
0/13.5Código de conducta
0/7.2Plantilla de issues
6.3/6.3Plantilla de PR
Datos de entrada utilizados
has_readme
has_licenseno
readme_badges
has_contributingno
has_issue_templateno
has_code_of_conductno
readme_badge_services
has_pull_request_template
Cómo se puntúa
49.7/80Descargas mensuales — 5322 descargas/mes en packagist
0/20Dependientes en el registro — 0 paquetes dependen de él
Datos de entrada utilizados
packagescboxdk/statamic-mcp
dependents0
ecosystemspackagist
total_downloads19.659
monthly_downloads5322

Sostenibilidad y Gobernanza

¿Sobrevivirá el proyecto a sus personas: factor bus, capacidad de respuesta, quién lo respalda y mantenimiento del paquete?

62Moderado · 23% del índice global
Cómo se puntúa
9/54Factor bus — la mitad de los commits recae en 1 contribuyente(s)
0.9/22.5Distribución de commits — el principal contribuyente firma el 96% de los commits
5.4/13.5Amplitud de contribuyentes — 4 contribuyentes
6/10OpenSSF Scorecard: Contributors — project has 2 contributing companies or organizations -- score normalized to 6
Datos de entrada utilizados
bus_factor1
contributors_sampled4
top_contributor_share0,96
Cómo se puntúa
42/42Resolución de issues — 100% de issues cerradas
28.8/30Aceptación de PR — 25/26 PR decididos fusionados
0/13Newcomer PR acceptance — ningún PR de un contribuyente primerizo decidido en 30 d
0/15OpenSSF Scorecard: Code-Review — Found 1/18 approved changesets -- score normalized to 0
Datos de entrada utilizados
merged_prs25
open_issues0
closed_issues9
prs_merged_7d
prs_decided_7d
prs_merged_30d
prs_decided_30d
issue_closed_ratio1
closed_unmerged_prs1
first_time_authors_30d
first_time_prs_merged_30d
first_time_prs_decided_30d
Excluidos de la puntuación (sin datos o no aplicable): newcomer_pr_acceptance. Los pesos restantes se han renormalizado.
Cómo se puntúa
30/30Respaldo de la propiedad — propiedad de una organización
0/20Dominio verificado
6.1/25Alcance del propietario — 6 seguidores de cboxdk
25/25Trayectoria — 66 repos públicos, cuenta de ~10 años
Datos de entrada utilizados
followers6
owner_typeOrganization
is_verified
owner_logincboxdk
public_repos66
account_age_days3697
Cómo se puntúa
25/25Publicado y resoluble — 1 paquete(s) en packagist
35/35Recencia de publicación — última publicación hace 24 días
20/20Historial de versiones — 31 versiones en el registro
20/20No obsoleto — activo, ni obsoleto ni retirado
Datos de entrada utilizados
packagescboxdk/statamic-mcp
ecosystemspackagist
any_deprecatedno
min_days_since_publish24

Calidad de Ingeniería

¿Existen unas prácticas mínimas de ingeniería y documentación?

87Excelente · 19% del índice global
Cómo se puntúa
24/24Flujos de trabajo de CI — 3 flujo(s) de trabajo
24/24Pruebas presentes
16/16Configuración de linter — phpstan.neon
0/9.6Hooks de pre-commit
0/6.4.editorconfig
14/20OpenSSF Scorecard: CI-Tests — 7 out of 9 merged PRs checked by a CI test -- score normalized to 7
Datos de entrada utilizados
has_ci
has_tests
has_editorconfigno
has_linter_config
has_precommit_configno

Documentación

100Excepcional
Cómo se puntúa
30/30README
25/25Directorio de documentación
15/15Sitio de documentación / página del proyecto — https://cbox.dk/packages/statamic-mcp
10/10Descripción del repositorio
10/10Topics — 8 topics
10/10Wiki
Datos de entrada utilizados
topicsai, claude, development, mcp, statamic, cursor, laravel, tools
has_wiki
homepagehttps://cbox.dk/packages/statamic-mcp
has_readme
has_docs_dir
has_description

Seguridad

¿Son sólidas las prácticas visibles de seguridad y de cadena de suministro, sin exposición jurisdiccional de alto riesgo sin resolver?

37Débil · 16% del índice global
Cómo se puntúa
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — branch protection not enabled on development/release branches
1.8/2.5CI-Tests — 7 out of 9 merged PRs checked by a CI test -- score normalized to 7
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 1/18 approved changesets -- score normalized to 0
1.5/2.5Contributors — project has 2 contributing companies or organizations -- score normalized to 6
10/10Dangerous-Workflow — no dangerous workflow patterns detected
0/7.5Dependency-Update-Tool — no update tool detected
0/5Fuzzing — project is not fuzzed
0/2.5Licencia — license file not detected
6/7.5Maintained — 10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8
0/5Packaging — sin datos
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — SAST tool is not run on all commits -- score normalized to 0
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — sin datos
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
7.5/7.5Vulnerabilities — 0 existing vulnerabilities detected
Datos de entrada utilizados
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate3,7
Excluidos de la puntuación (sin datos o no aplicable): packaging, signed_releases. Los pesos restantes se han renormalizado.

Preparación para IA

¿Hasta qué punto está el repositorio preparado para desarrollarse y mantenerse con agentes de codificación de IA? Tiene un peso deliberadamente pequeño (4%): las herramientas para agentes son una señal real de mantenimiento, pero un repositorio sin ninguna puede alcanzar igualmente 100/100.

54Moderado · 4% del índice global
Cómo se puntúa
45/45Instrucciones para agentes — CLAUDE.md
0/15Documentación legible por máquinas (llms.txt)
40/40Historial de commits legible — 94 de 100 commits humanos declaran su intención (asunto estructurado o cuerpo explicativo)
Datos de entrada utilizados
has_llms_txtno
legible_history_share0,94
agent_instruction_filesCLAUDE.md
agent_instruction_max_bytes39.819
Cómo se puntúa
0/18Arranque con un solo comando
22/22Pruebas automatizadas
11/11Configuración de lint / formato — phpstan.neon
0/11Verificación estática de tipos
0/10Entorno reproducible
10/10Práctica demostrada con agentes — 13 de los últimos 100 commits con autoría o crédito de agente
0/8Mantenimiento automatizado — no se observan actualizaciones automáticas de dependencias
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Datos de entrada utilizados
has_nixno
has_tests
lockfiles
has_dockerfileno
typed_languageno
bootstrap_files
has_devcontainerno
has_linter_config
typecheck_configs
agent_commit_share0,13
toolchain_manifests
dependency_bot_commit_share0
Cómo se puntúa
0/45Código verificable por tipos — PHP sin configuración de verificación de tipos
55/55Tamaños de archivo manejables — 0/211 archivos fuente de más de 60 KB
Datos de entrada utilizados
primary_languagePHP
largest_source_bytes50.386
source_files_sampled211
oversized_source_files0
Cómo se puntúa
0/40Esquema de API (OpenAPI/GraphQL/proto)
20/20Servidor MCP
0/40Ejemplos ejecutables
Datos de entrada utilizados
example_dirs
has_mcp_signal
api_schema_files

Datos clave

32estrellas de GitHub
4contribuidores
150commits en los últimos 12 meses
0días desde el último push
31versiones publicadas
1factor bus
0issues abiertas
npm, Packagistecosistemas de paquetes

Advertencias de recopilación de datos

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Más detalle

Historial de estrellas y forks 0 ★ / 10 ⇿
0Estrellas
10Forks
19Versiones

Cuándo se añadió cada estrella y fork, recopilado de GitHub y agrupado por día. El crecimiento acumulado se sitúa justo encima de las adiciones diarias que lo componen, de modo que ambos se leen en conjunto: la acumulación orgánica sostenida no se parece en nada a un pico abrupto y efímero. Cuando esa diferencia es medible, se informa como autenticidad del crecimiento.

02468101012025-112026-022026-05
Mayor 1Menor 7Parche 8
OpenSSF Scorecard 3.7 / 10
3.7agregado

Evaluación de seguridad independiente y agnóstica en cuanto a herramientas, procedente del proyecto de código abierto OpenSSF Scorecard. Cada comprobación premia una práctica de seguridad, no la herramienta de un proveedor concreto. Las comprobaciones que Scorecard no pudo determinar se marcan como n/d y se excluyen de la puntuación de seguridad (nunca se cuentan como cero).Scorecard v5.5.0 · 2026-07-29 20:11 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
7CI-Tests7 out of 9 merged PRs checked by a CI test -- score normalized to 7
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 1/18 approved changesets -- score normalized to 0
6Contributorsproject has 2 contributing companies or organizations -- score normalized to 6
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
0Licenselicense file not detected
8Maintained10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8
n/dPackagingpackaging workflow not detected
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTSAST tool is not run on all commits -- score normalized to 0
0Security-Policysecurity policy file not detected
n/dSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
10Vulnerabilities0 existing vulnerabilities detected
Dependencias directas 3
RegistroPaqueteRestricción de versiónManifiesto
Packagiststatamic/cms^6.6composer.json
Packagistlaravel/mcp^0.6 || ^0.7 || ^0.8composer.json
Packagistsymfony/yaml^7.0 || ^8.0composer.json
Todas las dependencias no recopilado

No fue posible recopilar el conjunto de dependencias resuelto para este informe: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Informe JSON sin procesar legible por máquina
{
  "data": {
    "repo": {
      "topics": [
        "ai",
        "claude",
        "development",
        "mcp",
        "statamic",
        "cursor",
        "laravel",
        "tools"
      ],
      "is_fork": false,
      "size_kb": 1409,
      "has_wiki": true,
      "homepage": "https://cbox.dk/packages/statamic-mcp",
      "languages": {
        "PHP": 1447749,
        "Vue": 85343,
        "Blade": 6039,
        "JavaScript": 10640
      },
      "pushed_at": "2026-07-29T20:09:14Z",
      "created_at": "2025-08-29T20:52:25Z",
      "owner_type": "Organization",
      "updated_at": "2026-07-23T01:32:26Z",
      "description": "MCP (Model Context Protocol) server for Statamic CMS v6 — gives AI assistants structured access to content, blueprints, assets, and more.",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": null,
      "default_branch": "main",
      "license_spdx_raw": null,
      "primary_language": "PHP",
      "significant_languages": [
        "PHP"
      ]
    },
    "owner": {
      "blog": "www.cbox.dk",
      "name": "Cbox",
      "type": "Organization",
      "login": "cboxdk",
      "company": null,
      "location": "Denmark",
      "followers": 6,
      "avatar_url": "https://avatars.githubusercontent.com/u/19936443?v=4",
      "created_at": "2016-06-14T18:35:48Z",
      "is_verified": null,
      "public_repos": 66,
      "account_age_days": 3697
    },
    "license": {
      "state": "absent",
      "spdx_id": null,
      "raw_spdx": null,
      "file_present": false,
      "scorecard_found": false,
      "profile_has_license": false
    },
    "activity": {
      "releases": [
        {
          "tag": "v2.7.0",
          "kind": "minor",
          "published_at": "2026-07-05T10:36:38Z"
        },
        {
          "tag": "v2.6.1",
          "kind": "patch",
          "published_at": "2026-06-30T11:18:30Z"
        },
        {
          "tag": "v2.6.0",
          "kind": "minor",
          "published_at": "2026-06-02T21:27:17Z"
        },
        {
          "tag": "v2.5.0",
          "kind": "minor",
          "published_at": "2026-05-06T16:25:57Z"
        },
        {
          "tag": "v2.4.0",
          "kind": "minor",
          "published_at": "2026-05-05T13:54:48Z"
        },
        {
          "tag": "v2.3.0",
          "kind": "minor",
          "published_at": "2026-04-23T14:03:59Z"
        },
        {
          "tag": "v2.2.4",
          "kind": "patch",
          "published_at": "2026-04-14T13:38:36Z"
        },
        {
          "tag": "v2.2.3",
          "kind": "patch",
          "published_at": "2026-04-14T13:08:57Z"
        },
        {
          "tag": "v2.2.2",
          "kind": "patch",
          "published_at": "2026-04-14T11:44:31Z"
        },
        {
          "tag": "v2.2.1",
          "kind": "patch",
          "published_at": "2026-04-14T11:09:17Z"
        },
        {
          "tag": "v2.2.0",
          "kind": "minor",
          "published_at": "2026-04-14T10:42:45Z"
        },
        {
          "tag": "v2.1.0",
          "kind": "minor",
          "published_at": "2026-04-13T15:02:46Z"
        },
        {
          "tag": "v2.0.4",
          "kind": "patch",
          "published_at": "2026-04-10T11:10:12Z"
        },
        {
          "tag": "v2.0.3",
          "kind": "patch",
          "published_at": "2026-04-09T08:57:08Z"
        },
        {
          "tag": "v2.0.2",
          "kind": "patch",
          "published_at": "2026-03-19T09:02:29Z"
        },
        {
          "tag": "v2.0.1",
          "kind": "patch",
          "published_at": "2026-03-18T13:42:18Z"
        },
        {
          "tag": "v2.0.0",
          "kind": "major",
          "published_at": "2026-03-18T09:54:11Z"
        },
        {
          "tag": "v1.0.0-alpha.3",
          "kind": "prerelease",
          "published_at": "2026-01-23T11:22:17Z"
        },
        {
          "tag": "v1.0.0-alpha.2",
          "kind": "prerelease",
          "published_at": "2026-01-23T09:26:02Z"
        },
        {
          "tag": "v1.0.0-alpha.1",
          "kind": "prerelease",
          "published_at": "2025-12-13T06:40:53Z"
        },
        {
          "tag": "v0.11.0",
          "kind": "minor",
          "published_at": "2025-12-10T12:37:49Z"
        },
        {
          "tag": "v0.10.0",
          "kind": "minor",
          "published_at": "2025-11-02T23:51:13Z"
        },
        {
          "tag": "v0.9.0",
          "kind": "minor",
          "published_at": "2025-09-27T18:31:48Z"
        },
        {
          "tag": "v0.8.0",
          "kind": "minor",
          "published_at": "2025-09-08T10:34:53Z"
        },
        {
          "tag": "v0.7.0",
          "kind": "minor",
          "published_at": "2025-09-08T10:20:26Z"
        },
        {
          "tag": "v0.6.0",
          "kind": "minor",
          "published_at": "2025-09-08T09:45:16Z"
        },
        {
          "tag": "v0.5.0",
          "kind": "minor",
          "published_at": "2025-09-08T09:40:18Z"
        },
        {
          "tag": "v0.4.0",
          "kind": "minor",
          "published_at": "2025-09-08T09:28:35Z"
        },
        {
          "tag": "v0.3.0",
          "kind": "minor",
          "published_at": "2025-09-04T06:39:05Z"
        },
        {
          "tag": "v0.2.0",
          "kind": "minor",
          "published_at": "2025-09-03T20:06:56Z"
        },
        {
          "tag": "v0.1.0",
          "kind": "minor",
          "published_at": "2025-09-03T07:24:21Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "ec433cc971b7ba318c389da68cc4539fb01ac517",
          "body": "Allow laravel/mcp ^0.8 alongside ^0.6 and ^0.7. Fixes null blueprint\nhandle in types analysis and output buffer cleanup type safety flagged\nby stricter dependency types.",
          "is_bot": false,
          "headline": "Release v2.7.0",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-07-05T10:36:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c7114e2a9b84ccdde99aadc8c0f22db577980b42",
          "body": null,
          "is_bot": false,
          "headline": "Release v2.6.1",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-06-30T11:18:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "390d2e4e25172885087b4136b60a6acbf9845e11",
          "body": "Fixes #34.",
          "is_bot": false,
          "headline": "Fix confirmation token retry loop",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-06-30T11:15:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "38a6c2b87d79b8eeeae404e06ce7c77b39f4734b",
          "body": null,
          "is_bot": false,
          "headline": "Fix Eloquent user IDs for MCP dashboard (#33)",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-06-02T21:25:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e79a3fbf7b64741d6d9f0839dd717a5bab8b079d",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add v2.5.0 changelog",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-05-06T16:25:44Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bd4f895cf31ccb77f92d3df772cc777830b63adb",
          "body": "* feat(entries): add revision-aware behavior to EntriesRouter\n\nWhen a collection has revisions enabled, the MCP server now respects\nStatamic's editorial workflow instead of bypassing it with direct saves.\n\nChanges:\n- Update on published entry creates a working copy (published content unchanged)\n- Cr\n[…]\n Add revision_message type check in publishWorkingCopyAction\n- Add missing PHPDoc annotations and return types for PHPStan L8\n\n* fix: resolve PHPStan L8 error in filterOutputFields mixed offset access",
          "is_bot": false,
          "headline": "feat(entries): add revision-aware behavior to EntriesRouter (#30)",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-05-06T16:22:06Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "df632b0015080fd08b205201fbd306c734c1da6f",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add v2.4.0 changelog",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-05-05T13:54:32Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "90fdb1a56900927e6664997a4d3164bd7d15b746",
          "body": "Login was called per-test (5× per run), hitting Statamic's login\nthrottle on the last test. Switch to globalSetup + storageState so\nlogin happens once and all tests reuse the authenticated session.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(ci): use shared auth state to prevent Playwright login throttling",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-05-05T13:39:47Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "1765e715d8dc456bcf701963c9fb5f7a66ce2ce4",
          "body": "… (#29)\n\nFixes ENG-804.\n\nAdds a FieldFormatSpec service that derives a wire-format guidance object\nfrom each Statamic Field — bard inline vs full, replicator/grid/group item\nshape, allowed set types, recursive set definitions, markdown vs ProseMirror\ndistinction, relationship/asset/date input shapes\n[…]\n leaking internals.\n\nSchema descriptions on BlueprintsRouter were also corrected to reflect the\nactual list-vs-get scoping of include_fields, include_config,\ninclude_format_spec, and max_format_depth.",
          "is_bot": false,
          "headline": "feat(blueprints): emit per-field wire-format spec to guide MCP agents…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-05-05T13:01:05Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5bfe2d278ad0ea1c4c8e800e34bcad81ae6cbaf7",
          "body": "…n-27\n\nfix(entries): exclude current entry from slug uniqueness on update (#27)",
          "is_bot": false,
          "headline": "Merge pull request #28 from SAY-5/fix/update-entry-slug-self-collisio…",
          "author_name": "Sai Asish Y",
          "author_login": "SAY-5",
          "committed_at": "2026-05-05T13:00:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "eedbe53a2156f41f8d50ee4a9c2ae05ab94c9bcb",
          "body": "…ions\n\nfeat: make confirmation-token action list configurable per domain",
          "is_bot": false,
          "headline": "Merge pull request #26 from cboxdk/feat/configurable-confirmation-act…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-24T05:51:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "564795dc55fbc6e7991346165a80001e7d33af13",
          "body": "Previously the confirmation-token flow was hardcoded to gate only\n'delete' on every router plus 'create'/'update' on blueprints.\nOperators running STATAMIC_MCP_CONFIRMATION_ENABLED=true in production\nhad no way to require confirmation on other destructive actions\n(entries.update, globals.update, use\n[…]\nctionGate. Domains not listed\nfall back to 'default'; '*' gates every action; [] disables the gate\nfor a domain. Shipped defaults reproduce the original behaviour, so\nexisting consumers see no change.",
          "is_bot": false,
          "headline": "feat(confirmation): configurable per-domain action gate",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-23T14:43:48Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d1cef678a98aef02f6c8b0b9d3ba5ace60085987",
          "body": "fix(sanitizer): normalize table cells to scalars before persist",
          "is_bot": false,
          "headline": "Merge pull request #25 from cboxdk/fix/sanitize-table-cells",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-23T14:02:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6f4c3500c611f5c6bfe26201033aac00f667026a",
          "body": "…cate\n\nlaravel/mcp's AddWwwAuthenticateHeader resolves the RFC 9728\nresource_metadata URL through route-name lookup. When that route name\nisn't visible at request time (observed in the Playwright e2e env\nrunning under `php artisan serve`) it falls back to a generic\n`Bearer realm=\"mcp\", error=\"invali\n[…]\nlace the middleware in the container with a subclass that builds\nthe discovery URL directly via url(), so the pointer is emitted as\nlong as OAuth is configured — regardless of route-name availability.",
          "is_bot": false,
          "headline": "fix(oauth): always emit resource_metadata pointer in 401 WWW-Authenti…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-23T13:50:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "049d2d3d0bf639ea68d85840037296c8c32755f2",
          "body": "Table fieldtype stores each cell as a bare string or null, but LLM\noutput and template-layer augmentation commonly wrap cells as\n`['value' => scalar]`. That form rendered fine on the frontend while\nthe CP displayed `[object Object]` because the editor reads raw storage.\n\nAdd sanitizeTableValue() to the existing SanitizesFieldData trait so\ndirect table fields and tables nested inside Bard sets get normalized\n(unwrap `value` key, reject unknown shapes) before they reach disk.",
          "is_bot": false,
          "headline": "fix(sanitizer): normalize table cells to scalars before persist",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-23T13:28:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "55b285935ef25275bd4436651da0ebc21e7ddd0a",
          "body": "Resolves PHPStan Level 8 errors where array<mixed, mixed> was passed\nto ResourcePolicy::filterFields() which expects array<string, mixed>.",
          "is_bot": false,
          "headline": "fix: add PHPStan type assertions for filterFields() calls",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-20T07:47:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7a10b8c9c5ff6f3c411894eadd2837d4b4977df8",
          "body": "docs: add Node.js TLS certificate fix for Laravel Herd/Valet",
          "is_bot": false,
          "headline": "Merge pull request #24 from cboxdk/docs/node-tls-certificate-fix",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-20T07:15:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "541ce99ce763f70e0cdacda3c6e881e95ffcaa62",
          "body": "…th Laravel Herd/Valet",
          "is_bot": false,
          "headline": "docs: add troubleshooting guide for Node.js TLS certificate issues wi…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-20T07:14:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2c1036ee212ed95b4af4a0d037e84e446930506f",
          "body": "…de failure\n\n- BaseStatamicTool: return structured response for all standard envelopes\n  (not just success), so confirmation token data payloads are preserved\n- ConfirmationTokenManager: handle json_encode failure explicitly\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: preserve structured data in error responses and handle json_enco…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T21:06:19Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8f45724d267d355d83b97fd895610e9405fe990f",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add confirmation tokens and resource policy to CLAUDE.md",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:30:32Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "4d7d61c66a569650e6ed20df26411315d62dfdad",
          "body": "Old test expected 'Deletion requires explicit confirmation' from the\nremoved confirm parameter. Updated to test that deleting a nonexistent\nblueprint returns 'Blueprint not found' (CLI context bypasses confirmation).\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: fix blueprint delete test for confirmation token changes",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:29:52Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "12d864181b9536f52e024276abf1c11bd5f0eb0f",
          "body": "BaseRouter now calls checkResourceAccess(), handleConfirmation(),\nfilterInputFields(), and filterOutputFields() for all routers.\nRemoves old confirm parameter from BlueprintsRouter.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: wire confirmation tokens and resource policy into router flow",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:27:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d8ce65b8a237ebcc71e3dccee101b1baa59793bd",
          "body": "Trait provides resource access checks (glob-based) and field filtering\n(input + output) for routers. Not yet wired into BaseRouter.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add EnforcesResourcePolicy trait for resource authorization",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:25:55Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "55855e3f6818600464bb0461dc4e888736243a0d",
          "body": "Trait provides handleConfirmation() for routers to call before\ndestructive actions. Not yet wired into BaseRouter.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add RequiresConfirmation trait for destructive operations",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:25:55Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a4c18733f937f892dd37b975f3f032c5c4f37176",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: register ConfirmationTokenManager and ResourcePolicy singletons",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:24:51Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "e147e41bc56996dc8d1f8ad652f254ad976f3cc8",
          "body": "Adds confirmation section (enabled auto-detect, 300s TTL) and extends\neach tool domain with resources (read/write glob lists) and denied_fields.\nDefaults are fully backwards compatible (everything open).\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "config: add confirmation tokens and resource policy settings",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:24:47Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bfec36cf6cfe5b42856daf971db8da38e65da335",
          "body": "Glob-based resource allowlists (read/write per domain) and recursive\nfield deny list filtering. Unconfigured domains default to allow-all\nfor backwards compatibility.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(auth): add ResourcePolicy for granular resource authorization",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:23:30Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bad013b67b06efc4fd3920abfe146558cb88ac62",
          "body": "Adds ConfirmationTokenManager with generate/validate/isEnabled methods.\nTokens are cryptographically bound to tool + arguments, expire via TTL,\nand auto-detect production vs development environments.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(auth): add stateless HMAC confirmation token manager",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:23:23Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "dd70b44348fbf499ee9d6e293db8503300b1d3ab",
          "body": "8 additional discovery endpoint tests:\n- authorization_endpoint follows custom CP route config\n- authorization_endpoint handles CP route with leading/trailing slashes\n- revocation_endpoint is present in AS metadata\n- full client discovery flow (protected-resource → path-suffixed AS\n  metadata → CIMD\n[…]\nith default path, custom path, and CIMD off\n- root vs path-suffixed response field-by-field identity checks for\n  both authorization-server and protected-resource endpoints\n\nBumps CHANGELOG to v2.2.4.",
          "is_bot": false,
          "headline": "test(oauth): add full discovery flow, CP route, and revocation tests",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T13:38:22Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b35268abd2656f9b4e2f463e3f258044ec471351",
          "body": "…and path-suffixed routes\n\n9 new tests covering:\n- CIMD config missing entirely (shallow merge scenario)\n- CIMD config as string \"true\"/\"false\" (env var behavior)\n- CIMD config as zero (explicit disable)\n- Path-suffixed authorization server discovery (RFC 8414 §3.1)\n- Path-suffixed protected resource discovery\n- Custom web path with path-suffixed discovery\n- Deeply nested and single-segment path suffixes\n\nAlso updates CHANGELOG to consolidate v2.2.0–v2.2.3 fixes.",
          "is_bot": false,
          "headline": "test(oauth): add discovery endpoint tests for CIMD config edge cases …",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T13:18:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "db4195e1900e0fae19d8ca4b6140561a7324fb66",
          "body": "MCP clients following the 2025-11-25 spec use path insertion for\ndiscovery: a server at /mcp/statamic triggers a fetch to\n/.well-known/oauth-authorization-server/mcp/statamic first.\n\nWithout these routes the request returned 403, so ChatGPT never\nsaw client_id_metadata_document_supported and disabled CIMD.",
          "is_bot": false,
          "headline": "fix(oauth): register path-suffixed discovery endpoints (RFC 8414 §3.1)",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T13:08:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a7dca2754f471802b6777c5d6f966475274c86f8",
          "body": "mergeConfigFrom() only does shallow merge — published configs missing\nthe cimd_enabled key return null, disabling CIMD silently.",
          "is_bot": false,
          "headline": "fix(oauth): add default true to all cimd_enabled config lookups",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T11:44:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "87ceeb1330336840d2449779472020bc847d492e",
          "body": "config() returns env() strings, not booleans. The strict === true\ncheck always failed, so CIMD was never advertised in discovery\nmetadata and never resolved during authorization or token exchange.",
          "is_bot": false,
          "headline": "fix(oauth): use boolean cast for cimd_enabled config check",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T11:09:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fb1b0ec570ec79f6dee355c73e2a5fad014ba0fd",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.2.0 — 2026-04-14",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:42:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5d68cf96a5b447a3b095d9b3d00d9b25eec6a29c",
          "body": "Add Client ID Metadata Document (CIMD) support for OAuth 2.1",
          "is_bot": false,
          "headline": "Merge pull request #16 from cboxdk/add-cimd-support",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:40:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9a31d46c668840ab0e5454960f47fe40b1e66c12",
          "body": null,
          "is_bot": false,
          "headline": "Auto-fix code formatting [skip ci]",
          "author_name": "GitHub Action",
          "author_login": "actions-user",
          "committed_at": "2026-04-14T10:22:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a3e8482481da504fdc8d5b35e9f52e2a7b5429e8",
          "body": null,
          "is_bot": false,
          "headline": "feat: [US-006] - Token endpoint CIMD integration and E2E flow",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fd31f38fa240a277a5f050a007ea78e394c71868",
          "body": null,
          "is_bot": false,
          "headline": "feat: [US-005] - Authorization flow CIMD integration",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "18239964927c0d238a36421526bf3f1ac67c87a8",
          "body": null,
          "is_bot": false,
          "headline": "feat: [US-004] - OAuthClient CIMD fields and discovery endpoint",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "64290180fd84f394c0f1d13e7fa043f094e83507",
          "body": null,
          "is_bot": false,
          "headline": "feat: [US-003] - CIMD resolver service with SSRF protection",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "90d99de3441792e89b9d4fe770265ea15c052ff5",
          "body": null,
          "is_bot": false,
          "headline": "feat: [US-002] - CIMD metadata document parsing and validation",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "24e2a0b1795ef18d8216c1e329a69f5223b979b7",
          "body": null,
          "is_bot": false,
          "headline": "feat: [US-001] - CIMD config and client ID URL validator",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f232c3153d59b7c7ae11ab3ed1bc050b879e93b4",
          "body": "…allback\n\nfix(routers): fall back to incoming-only validation on TypeError in update",
          "is_bot": false,
          "headline": "Merge pull request #21 from cboxdk/fix/update-validation-type-error-f…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:20:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1ba67e5ca2b422f3a8924eba73e4efd0af9fc93a",
          "body": null,
          "is_bot": false,
          "headline": "chore: remove internal ticket and client references from comments",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T09:27:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "140b5a291d29f9013cd6bfaf28a725c82e2c7445",
          "body": "…pdate\n\nThe update action on entries, terms, and globals ran the FieldsValidator\non ALL merged data (existing + incoming). Third-party fieldtypes (e.g.,\nSEO Pro) whose preProcessValidatable or extraRules methods cannot handle\nstored data formats threw TypeError, breaking every update regardless of\np\n[…]\nalready valid when saved.\n\nAdds 10 tests covering deep nested replicator/bard/grid/group\nblueprints, round-trip create→update, partial updates with required\nfields, and a simulated crashing fieldtype.",
          "is_bot": false,
          "headline": "fix(routers): fall back to incoming-only validation on TypeError in u…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T09:25:47Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "062e90cfb55e46764b0b1a0192459c33591034e9",
          "body": "…-remote\n\nClaude Desktop's config file only supports stdio transport — the `url`-based\nformat we documented doesn't work there. Replace with two valid options:\nOAuth Connectors (recommended) and mcp-remote stdio bridge (fallback).\n\nFixes #20",
          "is_bot": false,
          "headline": "docs: fix Claude Desktop setup instructions to use Connectors and mcp…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T16:23:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e02c06aea75650e35abf04b037d39b6c8cccd9a9",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.1.0 — 2026-04-13",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T15:02:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8e6b3b794379edf6df6c3f7fb9069961b0c8fb78",
          "body": "Fix entry/term data pipeline, field persistence, and hardening",
          "is_bot": false,
          "headline": "Merge pull request #19 from cboxdk/fix-nested-field-offset",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T15:01:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "45527181e1d0d90bf4170bf340c1852c183ac388",
          "body": "…nforce HTTPS\n\n- Add fflush() before flock(LOCK_UN) in exchangeCode() and\n  exchangeRefreshToken() — without it, concurrent requests could both\n  read 'used: false' from the disk buffer and double-spend an auth code\n  or refresh token. Same bug we fixed in FileTokenStore.\n\n- Strip HTML tags from cli\n[…]\ner, token, revoke) in\n  EnsureSecureTransport middleware so tokens cannot be exchanged over\n  plain HTTP in production. Discovery endpoints remain unprotected\n  since they expose only public metadata.",
          "is_bot": false,
          "headline": "fix(oauth): flush writes before lock release, sanitize client_name, e…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T14:15:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b6190cc88331a82d882c3cc1c42e181c6a94965a",
          "body": "AuthorizeController had three config() calls for default_scopes with\ndifferent fallbacks: two used ['content:read'], one used ['*']. All\nthree now fall back to [] — the config file is the single source of\ntruth for default scopes.",
          "is_bot": false,
          "headline": "fix(oauth): remove inconsistent hardcoded scope fallbacks",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T13:43:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "be5494b4b89eafa55160bbc02c988e18f5ebd95d",
          "body": "…read only\n\nA client with only content:read cannot read blueprints, structures, or\nsystem info — making it effectively useless for MCP discovery. Default\nto all :read scopes so OAuth clients can explore the CMS out of the box\nwithout write access.",
          "is_bot": false,
          "headline": "fix(config): use all read scopes as OAuth default instead of content:…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T13:36:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bdc20669f7372776cc3c4cb1d835997df43fe27d",
          "body": null,
          "is_bot": false,
          "headline": "chore: remove leftover .context repro test file",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T13:34:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "37aca91b3da8626ea0356faeff1884ae30d6c53a",
          "body": "…fault\n\n- BlueprintsRouter: close file handle when flock() fails but fopen()\n  succeeded — previously leaked the descriptor under contention.\n\n- FileTokenStore: add fflush() before releasing lock in updateIndex()\n  and removeFromIndex() to prevent partial writes on crash. The other\n  write methods (\n[…]\nebuildIndex) already had this.\n\n- Config: change OAuth default_scopes from '*' (all permissions) to\n  'content:read'. Operators can still override via env var but the\n  default is no longer wide open.",
          "is_bot": false,
          "headline": "fix: file handle leak, missing fflush, and overly permissive OAuth de…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T12:57:22Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "125db1daf463598fe4885b5fad0d61a003bf0720",
          "body": "…ompat\n\nThe method exists because this addon stored data without the fieldtype\nprocess() step prior to v2.1 — not because of Statamic legacy formats.\nMark as deprecated with clear removal criteria.",
          "is_bot": false,
          "headline": "docs: clarify that sanitizeStoredFieldDataForValidation is backward c…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T12:38:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "73e2c32ec32acc92f960e4e8fbd0b71029f75c46",
          "body": "Match the Statamic CP pipeline by calling $fields->process()->values()\nafter validation and before saving. Previously the routers stored raw\nvalidated data, skipping fieldtype transformations:\n\n- Terms::process() wraps values via Arr::wrap and strips taxonomy\n  prefixes\n- Bard::process() normalizes \n[…]\n)\nwas needed as a bandage to re-normalize on subsequent updates.\n\nFor updates, only the incoming data is processed (not existing stored\nvalues), preventing double-processing of already-stored content.",
          "is_bot": false,
          "headline": "fix(routers): process field data through fieldtypes before storage",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T12:05:08Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d55c9fbc1d18691ea03b63699916164ccb1decfd",
          "body": "… update\n\nAudit of all write paths found the same silent-drop pattern that caused\nENG-697:\n\n- HandlesTaxonomies: create/update now handle preview_targets and\n  default_status (previously only in configureTaxonomy). Removed the\n  broken collections() call from configureTaxonomy — Taxonomy::collection\n[…]\n now handle collections (previously\n  only in configureNavigation).\n\n- SanitizesFieldData: add checkboxes to relationship-style normalization\n  so bare strings are wrapped to arrays before validation.",
          "is_bot": false,
          "headline": "fix(structures): add missing fields to taxonomy/navigation create and…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T11:47:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "37a271867a7527e323914594d2cde520cd2a02a8",
          "body": "…istence\n\nSanitize relationship field values (terms, entries, users, assets) by\nwrapping bare strings in arrays before validation — LLMs often send\n\"slug\" instead of [\"slug\"], which crashed Statamic's validator with\n\"Cannot access offset of type string on string\".\n\nAlso add taxonomies handling to collection create/update in the\nstructures router — the field was silently ignored by the match\nstatement, so taxonomies never persisted despite a success response.\n\nRefs: ENG-697",
          "is_bot": false,
          "headline": "fix(entries): handle terms field updates and collection taxonomy pers…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T11:37:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a6f4bc8c6d81c3ead2ee7a8c69863a4e3f3b97cc",
          "body": null,
          "is_bot": false,
          "headline": "Fix nested field offset sanitization",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-11T05:28:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a0dd632a3fd2248bfeefe80cb7685c95a7afb6ef",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.0.4 — 2026-04-10",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-10T11:10:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d4f19eae07096d89f365e844b022cd7b1c88db78",
          "body": "fix: date normalization and entry property extraction",
          "is_bot": false,
          "headline": "Merge pull request #18 from cboxdk/fix/date-normalization-and-validation",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-10T11:09:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "95625359a3f1c0a03e1585a86594aad002c4d80c",
          "body": "Three related bugs fixed:\n\n1. addValues() exceptions escaping try-catch — Statamic's\n   Fields::addValues() was called outside the try-catch block in all\n   routers, causing \"Cannot access offset of type string on string\"\n   errors to propagate uncaught.\n\n2. Entry-level date/published not extracted \n[…]\nd dates as Y-m-d, Y-m-d H:i, ISO 8601, or {date, time}\n   objects. Added NormalizesDateFields trait that inspects the blueprint,\n   finds all date-type fields, and normalizes values before validation.",
          "is_bot": false,
          "headline": "fix: handle date normalization and entry property extraction in routers",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-10T10:59:53Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "332cf17c14cf2642cb82186255182bf034f7a5e7",
          "body": null,
          "is_bot": false,
          "headline": "Auto-fix code formatting [skip ci]",
          "author_name": "GitHub Action",
          "author_login": "actions-user",
          "committed_at": "2026-04-09T08:58:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e6d657d9e7632a7a27cb670ddf1f31b4846ab9f2",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.0.3 — 2026-04-09",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-09T08:56:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a5d61b3b41fe5943d3cdbf82effe3173b11cdd2b",
          "body": "fix: blueprint update merges fields instead of replacing",
          "is_bot": false,
          "headline": "Merge pull request #17 from cboxdk/fix/blueprint-update-data-loss",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-09T08:56:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "85b1fbd970695a9ed2f0b8afabd7edc4b1da4a7b",
          "body": "…ting fields\n\nThe update action was silently destroying all existing blueprint fields when\nadding new ones. Three compounding issues:\n\n1. Fields were replaced (`$contents['fields'] = $new`) instead of merged\n2. Existing fields were read from `$contents['fields']` which is always empty\n   after save \n[…]\nall()` collapsed multi-tab\n   blueprints into a single \"main\" tab\n\nNow: update merges by default (preserving tab/section structure), with an\nexplicit `replace_fields=true` opt-in for full replacement.",
          "is_bot": false,
          "headline": "fix: blueprint update now merges fields instead of replacing all exis…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-09T08:46:52Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "92e7cdd6408944ded18452f84c8cc1932720247e",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.0.2 — 2026-03-19",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-19T09:02:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "17fd70e5b515851c316e2599858ade8a07fe53a6",
          "body": "fix: install command no longer crashes without a database",
          "is_bot": false,
          "headline": "Merge pull request #15 from cboxdk/fix/install-command-resilience",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-19T08:59:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8373fec40a93f263250ee57eed4ee5accf120b58",
          "body": "- Config publish: user confirming \"overwrite\" now actually forces the\n  publish (previously --force stayed false, so vendor:publish silently\n  skipped the file)\n- Migrations: skip automatically when all storage drivers are file-based\n  (the default); only run when DatabaseTokenStore, DatabaseAuditSt\n[…]\nhDriver is configured\n- Add --skip-migrations flag as an explicit escape hatch\n- Wrap migrate call in try/catch with actionable guidance on failure\n- Completion message reflects what actually happened",
          "is_bot": false,
          "headline": "fix: install command no longer crashes without a database",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-19T08:48:53Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3ab9e9dab029d61cd45d8be7b36503a4633424bc",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.0.1 — 2026-03-18",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T13:42:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "740f0cca97018e920107aa3e6c8eb0175eda4171",
          "body": "fix: token form UX improvements",
          "is_bot": false,
          "headline": "Merge pull request #14 from cboxdk/fix/token-form-ux",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T13:41:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "15723edc998a5a9b343250679a495077cf0756f9",
          "body": "… improve error feedback\n\n- Remove hard `before` validation on expires_at — max_token_lifetime_days is a default suggestion, not a block\n- Add scope presets (Read Only, Content Editor, Full Access) matching documented common combinations\n- Use Statamic toast notifications for all token CRUD operatio\n[…]\ntoken table (shows preset name instead of individual scopes)\n- Set stack size to half for better proportions\n- Remove internal docs/superpowers directory (development plans/specs, not for public repo)",
          "is_bot": false,
          "headline": "fix: token form UX — remove date validation block, add scope presets,…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T13:27:56Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7497d259ea52dc2b88ce6aedb98d608e966607f4",
          "body": "v2.0.0: Storage drivers, OAuth 2.1, audit overhaul, security hardening",
          "is_bot": false,
          "headline": "v2.0.0: Storage drivers, OAuth 2.1, audit overhaul, security hardening",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T09:53:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e2ca418e9ad1c0ba9502947aef3433fbf88f923b",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.0.0 — 2026-03-18",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T09:41:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "12ac0c0c6a371bad30a09ba94cf324ff19a89f80",
          "body": "Extracts findTokenWithPermission() to handle the admin vs regular user\ntoken lookup consistently across update, regenerate, and destroy methods.\n\nAdmins (super or with manage/revoke all mcp tokens) can operate on any\ntoken. Regular users can only operate on their own.",
          "is_bot": false,
          "headline": "fix: allow admins to edit and regenerate any token, not just their own",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T09:27:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b8f9314cd49d340c9f10629d03fb7aa6cadff12b",
          "body": "TokenController::destroy() only searched the current user's tokens,\nso admins on the All Tokens page couldn't delete tokens belonging to\nother users (e.g. OAuth tokens with unknown/different user IDs).\n\nNow checks 'revoke all mcp tokens' permission — if granted, searches\nall tokens. Falls back to own-tokens-only for regular users.",
          "is_bot": false,
          "headline": "fix: allow admins to delete any token, not just their own",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T09:19:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fe26135b64c1057acb6e899786d14a46127e23a1",
          "body": "All runtime dependencies already support Laravel 13:\n- statamic/cms ^6.6 supports Laravel 12 and 13\n- laravel/mcp ^0.6 supports Laravel 11, 12, and 13\n\nChanges:\n- orchestra/testbench: drop ^9.0 (Laravel 11), keep ^10.0 (L12) + ^11.0 (L13)\n- Update docs to reflect Laravel 12/13 support",
          "is_bot": false,
          "headline": "feat: add Laravel 13 support",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T09:02:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b4cc6689390711f4977b04e7bb64279ef98c241f",
          "body": "- UPGRADE.md: step-by-step migration guide from v1.x to v2.0\n- CHANGELOG.md: merge premature [2.0.0] section back into [Unreleased],\n  add all recent changes (security hardening, DRY cleanup, OAuth quotas),\n  fix inaccurate action lists and scope counts",
          "is_bot": false,
          "headline": "docs: add UPGRADE.md and consolidate CHANGELOG for v2.0 release",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T09:00:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9037af4a46ae03e1b1e55ae2a5e6dda4a8d587ba",
          "body": "- Scope count 17→21 (missing content-facade scopes)\n- Tool count 12→11\n- Fix incorrect action lists for globals, system, content-facade, structures\n- Remove references to non-existent config keys (decay_minutes, web.middleware,\n  STATAMIC_MCP_MAX_TOKENS, STATAMIC_MCP_TOKEN_EXPIRY)\n- Fix oauth.max_clients default 1000→50, add max_clients_per_ip docs\n- Clarify default storage is file-based YAML, not database",
          "is_bot": false,
          "headline": "docs: fix 26 factual errors across all documentation",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T08:57:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d0254eec817a4e357c4c84f949613cf302fdc580",
          "body": "Security:\n- OAuth client registration: per-IP quota (5), global max 50, stricter rate limit (3/hr)\n- Directory permissions 0700 for token/OAuth storage\n- Correlation ID validated (alphanumeric, max 128 chars) instead of blindly trusted\n- HTTPS error no longer leaks env variable name\n- DatabaseTokenS\n[…]\nruntime effect)\n\nOther:\n- parseBytes() replaced with PHP 8.3 ini_parse_quantity()\n- Fixed isWebContext/isCliContext inconsistency across routers\n- OAuth test isolation: clean storage between test runs",
          "is_bot": false,
          "headline": "refactor: security hardening, DRY cleanup, and dead code removal",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T08:28:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d801577527eac78c6e823dd5cbf3c549c3675de7",
          "body": "…ecture\n\nSecurity:\n- Delete OAuth refresh tokens after exchange (single-use rotation)\n- Add X-Frame-Options and X-Content-Type-Options to all responses\n- Reject CORS wildcard in production instead of just warning\n- Default OAuth scopes to config value instead of full access\n- Reject bearer tokens ov\n[…]\nis tests (20 tests)\n- Add PruneExpiredTokensCommand tests (3 tests)\n- Add ClientConfigGenerator tests (17 tests)\n\nFrontend:\n- Split McpPage.vue (747→80 lines) into ConnectPanel, TokenList, useTokenApi",
          "is_bot": false,
          "headline": "refactor: comprehensive codebase review fixes — security, DRY, archit…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T23:32:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "71af40d9d3340778fbce27883059490acc76d7d5",
          "body": "High:\n- Fix empty scopes when OAuth client omits scope parameter: fallback\n  to all TokenScope values so tokens are functional\n\nMedium:\n- RegistrationController: use $e->httpStatus instead of $e->getCode()\n- SchemaTool: correct ContentFacadeRouter catalog (content_audit,\n  cross_reference — not exec\n[…]\no OAuth discovery metadata (RFC 8414)\n- Remove spurious success key from DiscoveryTool response\n- Fix wrong scope names in InstallCommand docs (navigation→structures,\n  remove non-existent forms:read)",
          "is_bot": false,
          "headline": "fix: address all 6 fourth-pass review findings",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T21:59:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "73e8697d5c352f580b740516677654e6d107e73c",
          "body": "Critical:\n- Fix open redirect in OAuth authorize: validate client_id and\n  redirect_uri BEFORE any redirect-based error responses. Errors for\n  response_type, code_challenge, code_challenge_method now use the\n  validated redirect_uri.\n\nHigh:\n- Revoke existing OAuth tokens for same client+user before\n[…]\netAccessible(true).\n\nLow:\n- Remove orphaned PHPDoc block on GlobalsRouter\n- Simplify no-op ternary in AuthorizeController::approve()\n- Separate OAuthException::$httpStatus from RuntimeException::$code",
          "is_bot": false,
          "headline": "fix: address all 7 third-pass review findings",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T21:45:15Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a99232557b1b5aef87e6fae2c7b86892af0de06e",
          "body": "Make it explicit that binary files must use encoding=base64, clarify\nthe difference between create and upload actions, and explain when to\nuse content vs file_path based on client type (remote vs CLI).",
          "is_bot": false,
          "headline": "docs(assets): improve schema descriptions for upload and content params",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T21:21:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "76f469b1556f78a1e48ddeed5bcfb9ea6d45e8c6",
          "body": "Remote MCP clients like ChatGPT cannot access the server filesystem,\nso file_path-based uploads fail. The upload action now accepts content\nwith encoding (base64/raw) as an alternative to file_path.\n\n- Upload action supports: file_path (local) OR content+encoding+filename (remote)\n- Add filename, content, file_path parameters to asset schema\n- Same security checks as create action (size limit, path traversal, temp file cleanup)\n- Update action description to document both upload methods",
          "is_bot": false,
          "headline": "feat(assets): support base64 content upload for remote MCP clients",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T21:17:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c286b58616fbd07d0c4e75f7a8e7b1965e8fec44",
          "body": "High:\n- Fix rate limit decay: multiply by 60 (config is minutes, hit() expects seconds)\n\nMedium:\n- Remove dead OAuthConsent.vue and its addon.js registration\n- Remove dead web.middleware config key\n- Use app()->make() instead of reflection in DiscoveryTool\n\nLow:\n- Remove unused McpToken model method\n[…]\nion in ContentFacadeRouter::crossReference\n- Remove redundant expiry check from RequireMcpPermission\n- Standardize environment check to app()->environment()\n- Remove deprecated ToolLogger::toolStarted",
          "is_bot": false,
          "headline": "fix: address all 12 second-pass review findings",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T21:10:39Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "18114255051ac07df1137d95cea5487bdb657779",
          "body": "…rays\n\nStatamic v6 uses standard Tailwind gray scale for dark mode, not custom\ndark-* utility classes. Systematically replaced all dark:bg-dark-*,\ndark:text-dark-*, dark:border-dark-*, dark:hover:bg-dark-* classes\nacross both McpPage.vue and McpAdminPage.vue with their standard\nTailwind gray equivalents.",
          "is_bot": false,
          "headline": "fix(ui): replace all custom dark-* utilities with standard Tailwind g…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T20:25:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cf8ca2394381f25010f23dd8da4ba49ec4b72096",
          "body": "Use Statamic's dark mode color tokens (gray-850, gray-900) instead of\ncustom dark-* utilities that don't exist in Statamic's Tailwind config.",
          "is_bot": false,
          "headline": "fix(ui): correct dark mode colors for permissions cards",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T20:03:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bf9c4f73eb80f1c80ec1ef3d6759f93e5ca14235",
          "body": "Replace flat checkbox list with grouped cards — each domain gets its\nown card with a \"Check All\" toggle and descriptions per permission.\nMatches Statamic's native role permissions UI pattern.\n\nAlso:\n- Add description() method to TokenScope enum\n- Include descriptions in scope serialization\n- Prefill expiration with max_token_lifetime_days default\n- Show \"Never expire\" button to clear the date\n- Communicate default expiration in field description",
          "is_bot": false,
          "headline": "feat(ui): redesign permissions to match Statamic role permissions style",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T14:48:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c325dad4d7e6c0ddb0499719e4e4c2e808a9796e",
          "body": "Add revokeRefreshToken() to OAuthDriver contract and both drivers.\nRevocationController now tries refresh token revocation when access\ntoken lookup fails (RFC 7009 compliance).\n\nUpdate all documentation to match current codebase:\n- Statamic constraint ^6.6, symfony/yaml ^7.0 || ^8.0\n- 21 scopes (was\n[…]\ns architecture documented\n- Git automation events documented\n- Tool env toggles documented\n- Fix tool names, config references, and broken links\n- Remove references to non-existent auth config section",
          "is_bot": false,
          "headline": "feat(oauth): add refresh token revocation + update all documentation",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T14:30:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "43fcda43752cb93a7352c933ac682de3c0e04e2c",
          "body": "Register McpTokenSaved and McpTokenDeleted events with Statamic's Git\nautomation listener. When git automation is enabled, token changes will\ntrigger automatic commits with descriptive messages.\n\nEvents are dispatched from TokenService for all token operations:\ncreate, update, regenerate, and revoke.",
          "is_bot": false,
          "headline": "feat: dispatch git events for token create, update, and delete",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T12:58:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "97761855830336d79385878f5b6ac777f5aa148d",
          "body": "Critical:\n- Fix rate limit config key mismatch (security.rate_limit_max → rate_limit.max_attempts)\n- Fix expose_versions default to false (was leaking version info)\n- Add missing OAuth storage paths to published config\n\nHigh:\n- Extract validateRedirectUri into shared ValidatesRedirectUris trait\n- Ex\n[…]\nh traversal check on asset filenames\n- Simplify ContentFacadeRouter to route via action directly\n- Clarify StatsService::getToolCount documentation\n- Fix misleading hash_equals comment in TokenService",
          "is_bot": false,
          "headline": "fix: address all 17 code review findings",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T12:51:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "43a4b93d8c9d7ace671e7d165e02d20ce2996f0b",
          "body": "- Add STATAMIC_MCP_TOOL_{NAME}_ENABLED env vars for all 9 tool domains\n- Remove deprecated security.audit_channel and security.audit_path\n- Remove unused ToolLogger::getLogPath() method and its test\n- Remove empty auth config section\n- Remove duplicate \"Tool Configuration\" comment block",
          "is_bot": false,
          "headline": "chore(config): add env toggles to tools, remove deprecated audit keys",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T12:34:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2aa895d68352bcb0d349ae31896970b306eead9d",
          "body": "- CP login test: check we left /auth/ instead of expecting /cp/ URL\n  (Statamic may redirect to site root after login)\n- Claude Desktop guide: use .first() for \"Connectors\" text that\n  appears in multiple elements (strict mode violation)",
          "is_bot": false,
          "headline": "fix(ci): fix remaining Playwright test assertions",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T12:22:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c02a6ca65611c4934aa10f851de7d138c0318849",
          "body": "Use named input selectors instead of positional locators, wait for full\nnavigation away from /auth/ paths, and wait for networkidle to ensure\nthe session cookie is set before subsequent navigation.",
          "is_bot": false,
          "headline": "fix(ci): improve Playwright login helper for reliable session handling",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T12:19:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "879bf377a6478330680365cc1ccb37a49cbfee59",
          "body": "The addon's IIFE script could execute before Statamic's CP bundle\nfinished initializing, causing \"Cannot read properties of undefined\n(reading 'register')\" on Statamic.$inertia.\n\nWrap registration in Statamic.booting() callback which runs after\nStatamic is fully initialized.",
          "is_bot": false,
          "headline": "fix(frontend): defer Inertia page registration to Statamic.booting()",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T12:15:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1fb5680cdbe1fdfb58f1cf38fb363f33262cadd4",
          "body": "The cp -r command silently failed because the target directory didn't\nexist. Use vendor:publish instead, which creates the directory and\ncopies assets correctly.",
          "is_bot": false,
          "headline": "fix(ci): publish addon assets properly in browser tests",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T11:52:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "260c7cf84563a32ac27e74a8081c37fad3bf4b97",
          "body": "Playwright couldn't find playwright.config.js when running from the\naddon root directory. Explicitly pass --config to point at the correct\nconfig file in tests/Browser/.",
          "is_bot": false,
          "headline": "fix(ci): specify Playwright config path in browser tests workflow",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T11:48:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4916ad27ce963af6bb15e855e63ad0f5d3b9c23a",
          "body": "…MySQL\n\nMySQL tests failed with \"Table already exists\" because tests manually\nran migrations without cleanup between runs. SQLite (in-memory) was\nunaffected since each test gets a fresh database.\n\n- Prefix token migrations with 0001/0002/0003 for correct alphabetical order\n- Replace manual include+u\n[…]\nonsFrom()\n- Add RefreshDatabase trait to all 11 database test files\n- Fix browser-tests workflow: create user via YAML file instead of\n  statamic:make:user --email (which doesn't exist in Statamic v6)",
          "is_bot": false,
          "headline": "fix(tests): add RefreshDatabase trait and fix migration ordering for …",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T11:44:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "90b64cf30ebc91e0a94d00fcf2ac53742c52eacb",
          "body": "CI creates a fresh Statamic site that pulls symfony/yaml v8.\nOur ^7.0 constraint blocked the install.",
          "is_bot": false,
          "headline": "chore(deps): widen symfony/yaml constraint to include v8",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T11:23:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cb20ffd12f368ebeb17898af87452bd1d747fcdd",
          "body": "Add oauth_client_id and oauth_client_name fields to tokens created via\nOAuth flow. This enables the dashboard to show which integration created\na token and prevent regeneration (which would break the integration).\n\n- Add nullable oauth_client_id/oauth_client_name to McpTokenData DTO\n- Add database m\n[…]\nController through TokenService\n- Show green \"OAuth · ClientName\" badge in dashboard\n- Hide regenerate button for OAuth tokens (frontend + backend 403)\n- Scope editing remains available for all tokens",
          "is_bot": false,
          "headline": "feat(oauth): store client metadata on tokens, hide regenerate for OAuth",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T11:21:51Z",
          "body_truncated": true,
          "is_coding_agent": false
        }
      ],
      "releases_count": 31,
      "commits_last_year": 150,
      "latest_release_at": "2026-07-05T10:36:38Z",
      "latest_release_tag": "v2.7.0",
      "releases_from_tags": false,
      "days_since_last_push": 0,
      "active_weeks_last_year": 17,
      "days_since_latest_release": 24,
      "mean_days_between_releases": 9.1
    },
    "community": {
      "has_readme": true,
      "has_license": false,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 50,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": true
    },
    "ecosystem": {
      "packages": [
        {
          "name": "cboxdk/statamic-mcp",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "tools",
            "development",
            "laravel",
            "cursor",
            "ai",
            "statamic",
            "mcp",
            "claude"
          ],
          "ecosystem": "packagist",
          "matches_repo": true,
          "registry_url": "https://packagist.org/packages/cboxdk/statamic-mcp",
          "is_deprecated": false,
          "latest_version": "v2.7.0",
          "repository_url": "https://github.com/cboxdk/statamic-mcp",
          "versions_count": 31,
          "total_downloads": 19659,
          "dependents_count": 0,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": 5322,
          "first_published_at": null,
          "latest_published_at": "2026-07-05T10:36:05Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 24
        }
      ]
    },
    "popularity": {
      "forks": 10,
      "stars": 32,
      "watchers": 0,
      "fork_history": {
        "days": [
          {
            "date": "2025-11-01",
            "count": 1
          },
          {
            "date": "2025-12-23",
            "count": 1
          },
          {
            "date": "2025-12-28",
            "count": 1
          },
          {
            "date": "2026-01-13",
            "count": 1
          },
          {
            "date": "2026-01-22",
            "count": 1
          },
          {
            "date": "2026-02-10",
            "count": 1
          },
          {
            "date": "2026-03-05",
            "count": 1
          },
          {
            "date": "2026-04-28",
            "count": 1
          },
          {
            "date": "2026-05-11",
            "count": 1
          },
          {
            "date": "2026-05-13",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 10,
        "total_forks": 10
      },
      "star_history": null,
      "open_issues_and_prs": 1
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": true,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [],
      "largest_source_bytes": 50386,
      "source_files_sampled": 211,
      "oversized_source_files": 0,
      "agent_instruction_files": [
        "CLAUDE.md"
      ],
      "agent_instruction_max_bytes": 39819
    },
    "dependencies": {
      "manifests": [
        "composer.json",
        "package.json"
      ],
      "advisories": {
        "error": null,
        "scope": null,
        "source": null,
        "findings": [],
        "collected": false,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 0,
        "malicious_count": 0,
        "assessed_package": null,
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "npm",
        "packagist"
      ],
      "dependencies": [
        {
          "name": "statamic/cms",
          "manifest": "composer.json",
          "ecosystem": "packagist",
          "version_constraint": "^6.6"
        },
        {
          "name": "laravel/mcp",
          "manifest": "composer.json",
          "ecosystem": "packagist",
          "version_constraint": "^0.6 || ^0.7 || ^0.8"
        },
        {
          "name": "symfony/yaml",
          "manifest": "composer.json",
          "ecosystem": "packagist",
          "version_constraint": "^7.0 || ^8.0"
        }
      ],
      "all_dependencies": {
        "error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
        "source": null,
        "packages": [],
        "collected": false,
        "truncated": false,
        "total_count": null,
        "direct_count": null,
        "indirect_count": null
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 1,
        "merged_prs": 25,
        "open_issues": 0,
        "closed_ratio": 1,
        "closed_issues": 9,
        "closed_unmerged_prs": 1
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "sylvesterdamgaard",
          "commits": 144,
          "avatar_url": "https://avatars.githubusercontent.com/u/2431914?v=4"
        },
        {
          "type": "User",
          "login": "actions-user",
          "commits": 4,
          "avatar_url": "https://avatars.githubusercontent.com/u/65916846?v=4"
        },
        {
          "type": "User",
          "login": "SAY-5",
          "commits": 1,
          "avatar_url": "https://avatars.githubusercontent.com/u/240962040?v=4"
        },
        {
          "type": "User",
          "login": "ruttydm",
          "commits": 1,
          "avatar_url": "https://avatars.githubusercontent.com/u/5909558?v=4"
        }
      ],
      "contributors_sampled": 4,
      "top_contributor_share": 0.96
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "browser-tests.yml",
        "release.yml",
        "tests.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [
        "phpstan.neon"
      ],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 0,
            "reason": "branch protection not enabled on development/release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": 7,
            "reason": "7 out of 9 merged PRs checked by a CI test -- score normalized to 7",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 0,
            "reason": "Found 1/18 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 6,
            "reason": "project has 2 contributing companies or organizations -- score normalized to 6",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": 10,
            "reason": "no dangerous workflow patterns detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 0,
            "reason": "no update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 0,
            "reason": "license file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 8,
            "reason": "10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": null,
            "reason": "packaging workflow not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 0,
            "reason": "dependency not pinned by hash detected -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "SAST tool is not run on all commits -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 0,
            "reason": "security policy file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 10,
            "reason": "0 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "ec433cc971b7ba318c389da68cc4539fb01ac517",
        "ran_at": "2026-07-29T20:11:41Z",
        "aggregate_score": 3.7,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": false,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": "2026-07-05T10:38:57Z",
      "oldest_open_prs": [
        {
          "number": 36,
          "created_at": "2026-07-29T20:09:28Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ],
      "last_merged_pr_at": "2026-06-30T11:15:16Z",
      "ci_last_conclusion": "FAILURE",
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/cboxdk/statamic-mcp",
    "host": "github.com",
    "name": "statamic-mcp",
    "owner": "cboxdk"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "good",
      "name": "Overall health",
      "note": "The weighted overall 63 is calibrated to 71 on the published index scale (record calibration 2026-08-02).",
      "notes": [
        {
          "code": "overall_calibration",
          "params": {
            "raw": 63,
            "calibrated": 71,
            "calibration": "2026-08-02"
          }
        }
      ],
      "value": 71,
      "inputs": {
        "security": 37,
        "vitality": 84,
        "community": 36,
        "governance": 62,
        "calibration": "2026-08-02",
        "engineering": 87,
        "ai_readiness": 54,
        "weighted_overall_raw": 63
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "excellent",
        "name": "Vitality",
        "value": 84,
        "weight": 0.21,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 74,
            "inputs": {
              "commits_last_year": 150,
              "human_commit_share": 1,
              "days_since_last_push": 0,
              "active_weeks_last_year": 17
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 0 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "17/52 weeks with commits",
                "points": 11.8,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 17
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "150 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 150
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8",
                "points": 8,
                "status": "partial",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "exceptional",
            "name": "Release discipline",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "releases_count": 31,
              "latest_release_tag": "v2.7.0",
              "releases_from_tags": false,
              "days_since_latest_release": 24,
              "mean_days_between_releases": 9.1
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "31 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 31
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 24 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 24
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~9.1 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 9.1
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "exceptional",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "maintained",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": null,
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": 30,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "last human commit 30 days ago",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_maintained",
                    "params": {
                      "days": 30
                    }
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "weak",
        "name": "Community & Adoption",
        "value": 36,
        "weight": 0.17,
        "metrics": [
          {
            "key": "popularity",
            "band": "at_risk",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 32,
            "inputs": {
              "forks": 10,
              "stars": 32,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "32 stars",
                "points": 24.2,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 32
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "10 forks",
                "points": 8,
                "status": "partial",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 10
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "0 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "at_risk",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 32,
            "inputs": {
              "has_readme": true,
              "has_license": false,
              "readme_badges": null,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "readme_badge_services": [],
              "has_pull_request_template": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "no license file detected",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "license_absent",
                    "params": {}
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 6.3,
                "status": "met",
                "details": [],
                "max_points": 6.3
              }
            ]
          },
          {
            "key": "ecosystem_adoption",
            "band": "moderate",
            "name": "Ecosystem adoption (downloads)",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "packages": [
                "cboxdk/statamic-mcp"
              ],
              "dependents": 0,
              "ecosystems": "packagist",
              "total_downloads": 19659,
              "monthly_downloads": 5322
            },
            "components": [
              {
                "key": "monthly_downloads",
                "name": "Monthly downloads",
                "detail": "5,322 downloads/month across packagist",
                "points": 49.7,
                "status": "partial",
                "details": [
                  {
                    "code": "downloads_monthly",
                    "params": {
                      "count": 5322,
                      "ecosystems": "packagist"
                    }
                  }
                ],
                "max_points": 80
              },
              {
                "key": "registry_dependents",
                "name": "Registry dependents",
                "detail": "0 packages depend on it",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "registry_dependents",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "moderate",
        "name": "Sustainability & Governance",
        "value": 62,
        "weight": 0.23,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "at_risk",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 21,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 4,
              "top_contributor_share": 0.96
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 96% of commits",
                "points": 0.9,
                "status": "partial",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 96
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "4 contributors",
                "points": 5.4,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 4
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 2 contributing companies or organizations -- score normalized to 6",
                "points": 6,
                "status": "partial",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "excellent",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): Newcomer PR acceptance. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "newcomer_pr_acceptance"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 81,
            "inputs": {
              "merged_prs": 25,
              "open_issues": 0,
              "closed_issues": 9,
              "prs_merged_7d": null,
              "prs_decided_7d": null,
              "prs_merged_30d": null,
              "prs_decided_30d": null,
              "issue_closed_ratio": 1,
              "closed_unmerged_prs": 1,
              "first_time_authors_30d": null,
              "first_time_prs_merged_30d": null,
              "first_time_prs_decided_30d": null
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "100% of issues closed",
                "points": 42,
                "status": "met",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 42
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "25/26 decided PRs merged",
                "points": 28.8,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 25,
                      "decided": 26
                    }
                  }
                ],
                "max_points": 30
              },
              {
                "key": "newcomer_pr_acceptance",
                "name": "Newcomer PR acceptance",
                "detail": "no first-time contributor's PR decided in 30d",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_newcomer_prs",
                    "params": {
                      "days": 30
                    }
                  }
                ],
                "max_points": 13
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 1/18 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "moderate",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 61,
            "inputs": {
              "followers": 6,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "cboxdk",
              "public_repos": 66,
              "account_age_days": 3697
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "organization-owned",
                "points": 30,
                "status": "met",
                "details": [
                  {
                    "code": "owner_organization",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "6 followers of cboxdk",
                "points": 6.1,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 6,
                      "login": "cboxdk"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "66 public repos, account ~10 yr old",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 66
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 10
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "exceptional",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "cboxdk/statamic-mcp"
              ],
              "ecosystems": "packagist",
              "any_deprecated": false,
              "min_days_since_publish": 24
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on packagist",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "packagist"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 24 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 24
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "31 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 31
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "excellent",
        "name": "Engineering Quality",
        "value": 87,
        "weight": 0.19,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "good",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 78,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "3 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": "phpstan.neon",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "phpstan.neon"
                    }
                  }
                ],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "7 out of 9 merged PRs checked by a CI test -- score normalized to 7",
                "points": 14,
                "status": "partial",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "exceptional",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "topics": [
                "ai",
                "claude",
                "development",
                "mcp",
                "statamic",
                "cursor",
                "laravel",
                "tools"
              ],
              "has_wiki": true,
              "homepage": "https://cbox.dk/packages/statamic-mcp",
              "has_readme": true,
              "has_docs_dir": true,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 25,
                "status": "met",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": "https://cbox.dk/packages/statamic-mcp",
                "points": 15,
                "status": "met",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": "8 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 8
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "weak",
        "name": "Security",
        "value": 37,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "weak",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Packaging, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "packaging",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 37,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 3.7
            },
            "components": [
              {
                "key": "binary_artifacts",
                "name": "Binary-Artifacts",
                "detail": "no binaries found in the repo",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "branch_protection",
                "name": "Branch-Protection",
                "detail": "branch protection not enabled on development/release branches",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "7 out of 9 merged PRs checked by a CI test -- score normalized to 7",
                "points": 1.8,
                "status": "partial",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 1/18 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 2 contributing companies or organizations -- score normalized to 6",
                "points": 1.5,
                "status": "partial",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no dangerous workflow patterns detected",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "no update tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8",
                "points": 6,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "SAST tool is not run on all commits -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "0 existing vulnerabilities detected",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "high_risk_jurisdiction_exposure",
            "band": "exceptional",
            "name": "High-Risk Jurisdiction Exposure",
            "note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
            "notes": [
              {
                "code": "jurisdiction_evidence_limits",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "meaning": "self-published location evidence; not nationality or citizenship",
              "red_flag": false,
              "exposures": [],
              "policy_countries": [
                "Russia",
                "Iran",
                "North Korea"
              ],
              "commit_weight_rule": {
                "min_commits": 50,
                "min_commit_share": 0.1
              },
              "review_only_matches": 0,
              "below_threshold_exposures": [],
              "assessed_self_published_locations": 5
            },
            "components": [
              {
                "key": "policy_exposure_multiplier",
                "name": "Policy exposure multiplier",
                "detail": "no confirmed policy-scope location match",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "jurisdiction_no_match",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "moderate",
        "name": "AI Readiness",
        "value": 54,
        "weight": 0.04,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "excellent",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.94,
              "agent_instruction_files": [
                "CLAUDE.md"
              ],
              "agent_instruction_max_bytes": 39819
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "CLAUDE.md",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "CLAUDE.md"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "94 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 94,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "weak",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 43,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [],
              "has_dockerfile": false,
              "typed_language": false,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [],
              "agent_commit_share": 0.13,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": "phpstan.neon",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "phpstan.neon"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "13 of the last 100 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 13,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "no automated dependency updates observed",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_dependency_automation",
                    "params": {}
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "moderate",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 55,
            "inputs": {
              "primary_language": "PHP",
              "largest_source_bytes": 50386,
              "source_files_sampled": 211,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "PHP without a type-check config",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_typecheck_config_language",
                    "params": {
                      "language": "PHP"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/211 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 211,
                      "oversized": 0
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          },
          {
            "key": "ai_interfaces",
            "band": "at_risk",
            "name": "Machine-readable interfaces",
            "note": null,
            "notes": [],
            "value": 20,
            "inputs": {
              "example_dirs": [],
              "has_mcp_signal": true,
              "api_schema_files": []
            },
            "components": [
              {
                "key": "api_schema_openapi_graphql_proto",
                "name": "API schema (OpenAPI/GraphQL/proto)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              },
              {
                "key": "mcp_server",
                "name": "MCP server",
                "detail": null,
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              },
              {
                "key": "runnable_examples",
                "name": "Runnable examples",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? Carries a deliberately small weight: agent tooling is a real maintenance signal, but its absence must never gate the top of the scale (calibration saturates at raw 91, so 100/100 remains reachable with AI Readiness at zero)."
      }
    ],
    "classification": {
      "top": [
        "library"
      ],
      "labels": [
        "library"
      ],
      "scores": {
        "library": 6,
        "mcp-server": 3
      },
      "primary": "library",
      "evidence": [
        {
          "tier": "distribution",
          "label": "library",
          "source": "registry:packagist",
          "weight": 6
        },
        {
          "tier": "structure",
          "label": "mcp-server",
          "source": "mcp_signal",
          "weight": 3
        }
      ],
      "artifacts": [],
      "confidence": "medium",
      "host_extension": false,
      "runs_as_process": false,
      "consumed_by_code": true
    },
    "metrics_version": "2.5.0"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-29T20:11:55.698173Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/c/cboxdk/statamic-mcp.svg",
  "full_name": "cboxdk/statamic-mcp",
  "license_state": "absent",
  "license_spdx": null
}

Las puntuaciones son señales, no garantías. Reflejan prácticas públicamente visibles en GitHub; no son una auditoría de código ni una garantía de seguridad.

Los datos ausentes se excluyen y los pesos se renormalizan; nunca se puntúan como cero. La metodología es versionada y abierta: métricas v2.5.0, esquema v0.27.0 — metodología completa · wiki de métricas.

Cómo se sitúa un resultado dentro del registro general: estadísticas agregadasPackagist.