Registro público
Informe de salud del softwareesquema 0.27.0 · métricas 1.13.0 · 2026-07-28 01:41 UTC

nichengfuben / echotools

通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链 (Python 3.8-3.14)

PythonSin licencia detectada★ 2 estrellas⑂ 0 forksdesde jun 2026Ver en GitHub ↗

nichengfuben/echotools tiene un índice de salud de 49 sobre 100, lo que lo sitúa en la banda En riesgo. Su puntuación más alta es Engineering Quality (85/100) y la más baja, Community & Adoption (25/100). Se actualizó por última vez hoy. Una sola persona concentra la mayor parte del trabajo reciente.

49
global / 100
En riesgo

Índice de salud del software

Las métricas se agrupan en categorías ponderadas sobre una escala de 1 a 100. El resultado global parte de su media; cuando la evidencia pública activa la Política de Jurisdicciones de Alto Riesgo, la calificación se ajusta y recibe el límite 49 (En riesgo). Preparación para IA queda fuera.

49
Excelente85-100Ejemplar; cumple prácticamente todos los criterios evaluados
Bueno70-84Saludable; carencias menores
Moderado50-69Aceptable con carencias notables; se recomienda revisión
En riesgo30-49Debilidades significativas; su adopción exige cautela
Crítico1-29Problemas 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.

Titularidad

NightPoemCuenta personal
1 seguidor29 repositorios públicosdesde dic 2024

Este repositorio pertenece a una cuenta personal. Un proyecto con un único propietario conlleva más riesgo de continuidad que uno respaldado por una organización.

Ecosistemas de paquetes

RegistroPaqueteVersiónDescargas / mesVersionesÚltima publicación
PyPIechotools2.3.61566781hace 0 días

Métricas por categoría

Vitalidad

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

71Bueno · 22% del índice global
Cómo se puntúa
36/36Recencia de push — último push hace 0 días
4.2/36Cadencia de commits — 6/52 semanas con commits
18/18Volumen de commits — 111 commits en el último año
0/10OpenSSF Scorecard: Maintained — project was created within the last 90 days. Please review its contents carefully
Datos de entrada utilizados
commits_last_year111
human_commit_share1
days_since_last_push0
active_weeks_last_year6
Cómo se puntúa
27/27Publica versiones — 4 versiones publicadas
36/36Recencia de las versiones — última versión hace 1 días
27/27Cadencia de publicación — una versión cada ~5,9 días
0/10OpenSSF Scorecard: Signed-Releases — Project has not signed or included provenance with any releases.
Datos de entrada utilizados
releases_count4
latest_release_tagv2.3.25
releases_from_tagsno
days_since_latest_release1
mean_days_between_releases5,9

Comunidad y Adopción

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

25Crítico · 18% del índice global
Cómo se puntúa
0/60Estrellas — 2 estrellas
0/25Forks — 0 forks
0/15Observadores — 0 observadores
Datos de entrada utilizados
forks0
stars2
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
0/6.3Plantilla de PR
Datos de entrada utilizados
has_readme
has_licenseno
has_contributingno
has_issue_templateno
has_code_of_conductno
has_pull_request_templateno
Cómo se puntúa
50/80Descargas mensuales — 5667 descargas/mes en pypi
0/20Dependientes en el registro — no lo informa este ecosistema
Datos de entrada utilizados
packagesechotools
dependents
ecosystemspypi
total_downloads
monthly_downloads5667
Excluidos de la puntuación (sin datos o no aplicable): Dependientes en el registro. Los pesos restantes se han renormalizado.

Sostenibilidad y Gobernanza

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

32En riesgo · 24% del índice global
Cómo se puntúa
9/54Factor bus — la mitad de los commits recae en 1 contribuyente(s)
0/22.5Distribución de commits — el principal contribuyente firma el 100% de los commits
1.4/13.5Amplitud de contribuyentes — 1 contribuyentes
0/10OpenSSF Scorecard: Contributors — project has 0 contributing companies or organizations -- score normalized to 0
Datos de entrada utilizados
bus_factor1
contributors_sampled1
top_contributor_share1
Cómo se puntúa
0/46.8Resolución de issues — sin issues o sin datos
0/38.3Aceptación de PR — sin PR decididos o sin datos
0/15OpenSSF Scorecard: Code-Review — Found 0/30 approved changesets -- score normalized to 0
Datos de entrada utilizados
merged_prs0
open_issues0
closed_issues0
issue_closed_ratio
closed_unmerged_prs0
Excluidos de la puntuación (sin datos o no aplicable): Resolución de issues, Aceptación de PR. Los pesos restantes se han renormalizado.
Cómo se puntúa
10/30Respaldo de la propiedad — cuenta personal (usuario)
0/20Dominio verificado — no aplicable a cuentas de usuario
2.2/25Alcance del propietario — 1 seguidores de nichengfuben
14/25Trayectoria — 29 repos públicos, cuenta de ~1 años
Datos de entrada utilizados
followers1
owner_typeUser
is_verified
owner_loginnichengfuben
public_repos29
account_age_days597
Excluidos de la puntuación (sin datos o no aplicable): Dominio verificado. Los pesos restantes se han renormalizado.
Cómo se puntúa
25/25Publicado y resoluble — 1 paquete(s) en pypi
35/35Recencia de publicación — última publicación hace 0 días
20/20Historial de versiones — 81 versiones en el registro
20/20No obsoleto — activo, ni obsoleto ni retirado
Datos de entrada utilizados
packagesechotools
ecosystemspypi
any_deprecatedno
min_days_since_publish0

Calidad de Ingeniería

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

85Excelente · 20% del índice global
Cómo se puntúa
24/24Flujos de trabajo de CI — 2 flujo(s) de trabajo
24/24Pruebas presentes
16/16Configuración de linter
9.6/9.6Hooks de pre-commit
0/6.4.editorconfig
0/20OpenSSF Scorecard: CI-Tests — sin datos
Datos de entrada utilizados
has_ci
has_tests
has_editorconfigno
has_linter_config
has_precommit_config
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: CI-Tests. Los pesos restantes se han renormalizado.
Cómo se puntúa
30/30README
25/25Directorio de documentación
0/15Sitio de documentación / página del proyecto
10/10Descripción del repositorio
0/10Topics
10/10Wiki
Datos de entrada utilizados
topics
has_wiki
homepage
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?

29Crítico · 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
0/2.5CI-Tests — sin datos
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/30 approved changesets -- score normalized to 0
0/2.5Contributors — project has 0 contributing companies or organizations -- score normalized to 0
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
0/7.5Maintained — project was created within the last 90 days. Please review its contents carefully
5/5Packaging — packaging workflow detected
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — no SAST tool detected
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — Project has not signed or included provenance with any releases.
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_evaluated17
scorecard_versionv5.5.0
checks_inconclusive1
scorecard_aggregate2,9
Excluidos de la puntuación (sin datos o no aplicable): ci_tests. 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? Es una insignia independiente y experimental — peso 0,0, de modo que se presenta por separado y no afecta a la puntuación de salud global.

62Moderado · 0% del índice global
Cómo se puntúa
0/45Instrucciones para agentes — sin CLAUDE.md / AGENTS.md / reglas de editor
0/15Documentación legible por máquinas (llms.txt)
40/40Historial de commits legible — 92 de 100 commits humanos declaran su intención (asunto estructurado o cuerpo explicativo)
Datos de entrada utilizados
has_llms_txtno
legible_history_share0,92
agent_instruction_files
agent_instruction_max_bytes
Cómo se puntúa
18/18Arranque con un solo comando — docs/Makefile
22/22Pruebas automatizadas
11/11Configuración de lint / formato
11/11Verificación estática de tipos — src/py.typed
0/10Entorno reproducible
10/10Práctica demostrada con agentes — 68 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_filesdocs/Makefile
has_devcontainerno
has_linter_config
typecheck_configssrc/py.typed
agent_commit_share0,68
toolchain_manifests
dependency_bot_commit_share0
Cómo se puntúa
27/45Código verificable por tipos — Python con configuración de verificación de tipos (src/py.typed)
54.5/55Tamaños de archivo manejables — 2/212 archivos fuente de más de 60 KB
Datos de entrada utilizados
primary_languagePython
largest_source_bytes63.845
source_files_sampled212
oversized_source_files2

Datos clave

2estrellas de GitHub
1contribuidores
111commits en los últimos 12 meses
0días desde el último push
4versiones publicadas
1factor bus
0issues abiertas
PyPIecosistemas 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
  • deps.dev does not index pypi:echotools@2.3.61; advisories assessed against the repository dependency graph instead

Más detalle

OpenSSF Scorecard 2.9 / 10
2.9agregado

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-28 01:40 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
n/dCI-Testsno pull request found
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/30 approved changesets -- score normalized to 0
0Contributorsproject has 0 contributing companies or organizations -- score normalized to 0
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
0Licenselicense file not detected
0Maintainedproject was created within the last 90 days. Please review its contents carefully
10Packagingpackaging workflow detected
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTno SAST tool detected
0Security-Policysecurity policy file not detected
0Signed-ReleasesProject has not signed or included provenance with any releases.
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
10Vulnerabilities0 existing vulnerabilities detected
Dependencias directas 1
RegistroPaqueteRestricción de versiónManifiesto
PyPItyping-extensions>=4.7.0pyproject.toml
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": [],
      "is_fork": false,
      "size_kb": 3796,
      "has_wiki": true,
      "homepage": null,
      "languages": {
        "CSS": 13400,
        "Python": 975518,
        "JavaScript": 49485
      },
      "pushed_at": "2026-07-28T01:39:35Z",
      "created_at": "2026-06-13T07:46:59Z",
      "owner_type": "User",
      "updated_at": "2026-07-28T01:39:36Z",
      "description": "通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链 (Python 3.8-3.14)",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": null,
      "default_branch": "main",
      "license_spdx_raw": null,
      "primary_language": "Python",
      "significant_languages": [
        "Python"
      ]
    },
    "owner": {
      "blog": null,
      "name": "NightPoem",
      "type": "User",
      "login": "nichengfuben",
      "company": null,
      "location": null,
      "followers": 1,
      "avatar_url": "https://avatars.githubusercontent.com/u/190981802?v=4",
      "created_at": "2024-12-08T00:35:45Z",
      "is_verified": null,
      "public_repos": 29,
      "account_age_days": 597
    },
    "license": {
      "state": "absent",
      "spdx_id": null,
      "raw_spdx": null,
      "file_present": false,
      "scorecard_found": false,
      "profile_has_license": false
    },
    "activity": {
      "releases": [
        {
          "tag": "v2.3.25",
          "kind": "patch",
          "published_at": "2026-07-26T03:57:51Z"
        },
        {
          "tag": "v2.3.24",
          "kind": "patch",
          "published_at": "2026-07-26T03:23:04Z"
        },
        {
          "tag": "v2.3.7",
          "kind": "patch",
          "published_at": "2026-07-21T03:17:38Z"
        },
        {
          "tag": "v1.0.35",
          "kind": "patch",
          "published_at": "2026-07-08T14:33:37Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "e673cc3e8e4cde29606fe3de249c7f042f5934b3",
          "body": "….61)\n\nSupport <thinking> as alternate open tag; </thinking> closes entml-open blocks when invoke follows; both close tags work for plain-open blocks.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: thinking tag fault tolerance for plain open and fault close (2.3…",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-28T01:39:20Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "53196e8115e4f15e0a75059c037c0ebc1333cdb6",
          "body": "…eadability (2.3.54–2.3.60)\n\nAlign invoke FIFO queue and array/object partial_json with schema; split current user message only when last role is user; humanize tool description fields in parameters JSON.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: stream partial_json parity, prompt split, and tool description r…",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-28T01:24:11Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "14746d164e3f215a8e8ee70624e712f6727b22e3",
          "body": "When the model omits <entml:thinking> but emits </entml:thinking>, move pre-close text from visible to thinking so planning is not streamed as the answer.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: reclassify orphan thinking close without open tag",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T23:58:52Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7c7ac854323a2b6bc2fffe97f058e424793a0d1c",
          "body": "Do not hold or strip bare <entml:invoke> in visible text; hold incomplete invoke open tags until > closes; restore parameter hold inside thinking so char-by-char tool XML is not emitted as thinking.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: preserve prose entml invoke mentions and thinking stream hold",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T18:03:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ee071adfc9967c340cedb96a7c025e59e7a91e91",
          "body": "Sync JSON stream encoder snapshot when tool call completes so the next chunk does not re-emit full arguments JSON, which broke Anthropic clients with Extra data parse errors on Bash and similar tools.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: prevent duplicate partial_json after invoke ready in stream",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T17:52:50Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d025764db69fcc312745b7841e2732c16768af3d",
          "body": "Extract actionable invoke blocks from thinking (including after </thinking>),\nclear fault_watch when tool parsing starts, and fix stream routing so parallel\ninvokes and split-chunk parameter bodies no longer lose calls or emit bad JSON.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: parse invoke inside thinking and fault-close handoff for 2.3.51",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T17:41:47Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f8ec4a3a2fb66177249bd5d3a8c916d7735b47d1",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: ruff unused import in comprehensive parser tests",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T17:19:08Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bf8a50a45b3e20e893eb046062a763704e99e852",
          "body": "Stop parsing invoke inside unclosed thinking on large first chunks; merge thinking holdback into fncall after fault-close splits; remove assistant prompt dedup; add comprehensive parser tests.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: stream thinking invoke boundaries and holdback drain for 2.3.50",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T17:15:49Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "84209f0da28fb187be8db2b5cf45d8b40e440f94",
          "body": "… fault\n\nOnly parse entml tool calls after </entml:thinking>; treat </thinking> as close only when invoke follows, else plain thinking text.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: strict thinking close before tool parse; conditional </thinking>…",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T16:46:10Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3a7854cca95a8bd2de3516ed21a0c01a39d1bca5",
          "body": "Defer closing brace until invoke ends; add exhaustive stream parse tests.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: keep multi-parameter input_json_delta as one open JSON object",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T16:43:41Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5dee55cbeb7d6bc5e912b8ecf2f7fafa07ae4efd",
          "body": "…king_behavior\n\nFix CI ruff unused import; bump version for PyPI publish.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "release: 2.3.47 — entml stream invoke delta, user_system_prompt, thin…",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T16:22:13Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f94140ba31550ee6f429a9ff1abb59a6b56eb5d6",
          "body": "This reverts commit 8ac999ad6c2b63ef98f2f6d3662b0bedbe3139c2.",
          "is_bot": false,
          "headline": "Reapply \"fix: omit invoke wording in thinking_behavior when no tools\"",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T16:18:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8ac999ad6c2b63ef98f2f6d3662b0bedbe3139c2",
          "body": "This reverts commit 58adfac462b3c430428bb4dc03652f8137ac3174.",
          "is_bot": false,
          "headline": "Revert \"fix: omit invoke wording in thinking_behavior when no tools\"",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T16:18:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "58adfac462b3c430428bb4dc03652f8137ac3174",
          "body": "Split ON/AUTO thinking instructions by has_tools so no-tools inject paths do not mention entml:invoke.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: omit invoke wording in thinking_behavior when no tools",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T16:16:52Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "b99e6009e50fcfbaf480aad914564312d9099e36",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: include user_system_prompt in entml no-tools inject path",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T16:05:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "370a13a272e247a38656a761abb2e63ee85043ac",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: stream tool input after invoke open tag for Anthropic partial_json",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T15:21:28Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "e07c383bcd912e923d1d752fd31d0fe3387c7003",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "chore: restore achecker.py",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T13:55:20Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0b67b31e6535cb9e34795ccae05cdf0bba9230e0",
          "body": "Delete split_console_ui helpers, achecker, and unused mixins/_typing.py. Split is complete; 740 tests pass.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "chore: remove one-off migration scripts and unused dev tools",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T13:54:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d102a01d48c96db74e2d4409996875c4f4b7fb13",
          "body": "Ignore .env, keys, and local checker output. No credentials were found in git history (full scan).\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "chore: gitignore local secrets and add .env.example",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T13:47:08Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "52f301c3736ce398b193518a3c496d624af6eec5",
          "body": "Bump version; fix ruff/mypy after console module split; CI-ready.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "release: 2.3.46 — entml bare invoke + console UI split fixes",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T13:40:24Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "14435b42f4201eb19a5787b2848f04e31e77dc8f",
          "body": "Extract uicore/uilayout/uiwidgets from monolithic ui.py; refresh input_box JS components; add split helper scripts.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "refactor: split console UI modules and update web input box",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T13:36:24Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "69fe1fbbba9e4efb71a6e7bd21883c00e72ddbe3",
          "body": "Drop function_calls from stream triggers and detect/hold paths; strip legacy wrapper on stream normalize. Add comprehensive bare-invoke test suite.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: align entml stream with bare invoke prompt format",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T13:35:24Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "38fc78969ccd830db5b023b32fca96cec7b21df7",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "style: fix ruff import order in new entml parse tests",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T12:47:16Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f3534084f31428cb5065ae0e8a4de1c3c71d3f13",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "chore: add setup.cfg Home-page so PyPI accepts uploads",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T12:42:46Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "b6c45b78043ba1aaa07c93c38fdc93d8c6398c14",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "release: 2.3.45 — complete entml tool parse/stream harden",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T12:37:42Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3f637f46a709d7a76bd953757f5e27c2359ac001",
          "body": "Tighten invoke/parameter parsing, strip tool residue, keep string coercion literal, and make FncallStreamParser emit completed tool calls incrementally without missing finals.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(entml): harden tool parse/stream — no tag leak, realtime ready calls",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T12:36:51Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ec8351f277bb4580b2f5cc8520dcd63b2c5f5fb3",
          "body": null,
          "is_bot": false,
          "headline": "style: fix ruff import formatting for CI",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T10:56:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6f268e2a5c22d8d0a6484754f88ea03d3f2110a7",
          "body": "…gths\n\nPass low-medium-high-xhigh-max as thinking_mode values with new default max_thinking_length mapping. Legacy thinking_mode on unchanged. Release 2.3.43.",
          "is_bot": false,
          "headline": "feat(entml): inject thinking_mode by level and update default max len…",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T10:52:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a7a619e62378693228d0fe6800b4d2da02f996db",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "release: 2.3.42 entml_think reorg and thinking blocks at prompt end",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T10:36:16Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "b45f5365c9eadf76a773d8130fce96e5e91c5923",
          "body": "While </entml:thinking> is missing, stream content stays plain text; invoke inside thinking is not parsed. Add comprehensive stream tests.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(entml): hold fncall parse until thinking block closes",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T09:05:04Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9077a4ddc414bcec81b22d6efab8a104db71872e",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "chore: release 2.3.39 — fix prompt_helpers f-string on Python 3.11",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T08:56:20Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9a0b970cac672d574d4896d3f433afee51f4486b",
          "body": "Fix Python 3.8 f-string syntax, import order, and console ui mypy override.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(ci): pass ruff and mypy on main",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T08:47:46Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8ceaa4c5d63b45c17a69777fb2d35c8d707d1173",
          "body": "Add none/low/medium/high/xhigh/max/auto levels with default max lengths, wrap guidance in thinking_behavior, and inject off/auto/on modes for the model.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "feat(entml): thinking levels map to thinking_behavior injection",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T08:29:23Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "803fc8bb9d57a7c9e6a02468489f8a9d872886b0",
          "body": "Wire clean_tags to generic entml:* sanitizer, strip every user message before history split, and remove duplicate per-tag cleaners from history.py.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(entml): strip entml tags from all user messages on inject",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T08:08:09Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "1f6f34349660925c1814d3778281eaa65de978c3",
          "body": "Strip all entml:* tags generically from user messages only; remove per-tag regex rules for function_calls/invoke/etc.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "refactor(entml): simplify user-message entml tag stripping",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T08:07:53Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "169b509cf53efa54c9c296c7f880d19d53271371",
          "body": "Use [tool: value] for single args and [tool: v1 | v2] with spaced pipes for multiple args.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(entml): normalize tool call line spacing in history",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T08:04:39Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bb96defe21f9d13d1ba0ca97ee5aafb3bcaef4d8",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(entml): restore invoke block wording in tool instruction",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T07:57:50Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9d297cf7f7d0634a02a3c7991dfd948631edcbff",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(entml): show bare invoke example without code fence",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T07:56:02Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c4b4ee0ca17cfc1550e14f62415912789fc0f755",
          "body": "Assistant history now contains text only; each tool turn renders one sibling tool block, merging parallel calls from the same turn.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(entml): emit tool blocks as siblings outside assistant",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T07:50:44Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0dcd24f2c04eaaa2230d4be16bde38dcd90b4b41",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "test(entml): cover multi-tool history blocks in one assistant turn",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T07:47:46Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "478ffcfd88f69d0e15e50b34b9544536c120e986",
          "body": "Replace ambiguous Reminder pseudocode guidance with a hard constraint, and render completed tool turns as compact tool blocks instead of fn(...)/Result lines.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "feat(entml): adopt tool history blocks and IMPORTANT invoke reminder",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-27T07:46:04Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "135c26fbb0712cfb0a849cc73d3691d4323c2d08",
          "body": "When conversation history contains tool calls and tools are available, append a reminder after conversation_history that only entml:invoke blocks execute tools.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "feat(entml): remind model that history tool pseudocode is not invocable",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-26T15:37:58Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ed9e7403a50d11112ff19f545bf516d24b60d97c",
          "body": "…tions\n\nAdd include_thinking_in_history to inject_fncall protocol_options so callers\n(rogator/provider-core) no longer hand-build entml:thinking blocks in history.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "feat: v2.3.30 — render assistant reasoning in history via protocol_op…",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-26T13:58:45Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "de6e0a2c6229219ba244875f88f860e47a0d73a5",
          "body": "Align tool prompt format (### heading, Description outside JSON, ```text invoke sample, parameters-only schema). Stream parser emits complete bare invokes mid-stream and incremental thinking; empty end tags keep no_auto_close.",
          "is_bot": false,
          "headline": "feat: v2.3.29 — entml prompt redesign, incremental thinking/tool stream",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-26T06:56:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cdaef67dee28f8876acddeb4bb3c268a80c70666",
          "body": "…/streaming\n\n- History format: assistant turns now inline call+result as [name(k=\"v\")]\\n→ Result: ...\n  instead of separate <function_results> XML blocks\n- normalization.py: _normalize_value respects schema type=string; no longer\n  JSON-parses string params that happen to look like JSON objects\n- st\n[…]\nper, format without wrapper\n- entml_patterns.py: BLOCK_RE matches bare entml:invoke\n- base.py: get_stream_end_tags() default in ToolProtocol\n- _no_auto_close=True when get_stream_end_tags() returns []",
          "is_bot": false,
          "headline": "feat: v2.3.27 — inline tool call history, normalization fix, thinking…",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-26T04:54:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f4f021db0646ebf40a74128a1d9bfaffd4170166",
          "body": "…empty end tags",
          "is_bot": false,
          "headline": "fix: FncallStreamParser — no_auto_close flag for protocols declaring …",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-26T03:56:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f822a7d77a8093e2b5bb8c573782d48148559937",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "feat(console): migrate ConsoleUI charmap and spinner",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-22T07:48:35Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "962d0f205bd45268731c45920ab0f47835eff8f5",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "feat(entml): add interleaved history policy for thinking blocks",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-22T02:59:14Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c51b2f41b5eb33c140a14784d9c9c65dea6375d5",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(entml): skip thinking injection when off or max length unset",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-22T02:25:03Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "09e8b64003f7a7e4daeedbff92db69feb0987763",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(entml): keep only off/on/auto thinking modes",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-22T01:45:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9b413d7e2c1cfd2919d81d4a8f137284535801a0",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "feat(entml): off/on/auto/interleaved thinking prompts (2.3.19)",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-22T01:43:29Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c1dd2ae31c9a88def771fc0616b6577ea1744ed8",
          "body": "Bump to 2.3.16.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "feat(entml): add thinking parse and restore At the very start copy",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-22T01:05:44Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "1907b6331c157d64e5434a4669063bc2b77c25c1",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "chore: stop tracking abnormis.txt",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-22T00:53:13Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a5caff189720adad246b0e3e6d1351a360e08f14",
          "body": "Keep only <current_user_message> tag update; restore entml:thinking section; bump to 2.3.15.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(entml): revert mistaken thinking tag changes in 2.3.14",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-22T00:52:33Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ea1e0256b47be076a4d602be1fedc2f5f34a9dd6",
          "body": "Use <current_user_message> and <thinking> without entml prefix; update thinking instruction copy; bump to 2.3.14.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(entml): unify current_user_message and thinking tags",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-22T00:49:21Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0d120d1dcb20e2711cf8dd3a6ea9a7b09b73a2fa",
          "body": "Support type= on parameter tags, default scalar to str without it, improve JSON Schema tool formatting, and use </current_user_message> closing tag; bump to 2.3.13.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(entml): parameter type attr and default str coercion",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-22T00:35:36Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3e816d18254f67ad3036f2d3c8c2edd0a0591855",
          "body": "Match antml-style instruction, JSONSchema tool blocks, and thinking section placement; bump to 2.3.12.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "feat(entml): align prompt with antml spec format",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-22T00:14:11Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "82ccea81ae24cd62619d93d71132b7ec0fc20c74",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: auto-install compat aliases bump 2.3.11",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-21T17:38:16Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9270802ced5240cd1857d4a7d4ef45112fb05c0d",
          "body": "EOF\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: lower coverage fail_under to 89 for py3.8 CI",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-21T16:40:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "fb328cd958299d04802425724eb4ad8a449ec7a8",
          "body": "EOF\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: omit compat.py from coverage gate (CI 3.8)",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-21T16:38:55Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a4f0b9bbec58cfb69ee6d99efaf7c9fbfbbcbfd2",
          "body": "EOF\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: make CI ruff/mypy clean (spinner imports, LocalTerminal refs)",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-21T16:32:37Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ba3a15f5c674f38495cc25664c9f88e5b02b41e4",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: export terminal session buffer size constants",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-21T16:10:36Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0554cb61aa7206a91fa791d3e6e1531aea82a3c5",
          "body": "Replace top-level re-export packages with meta-path aliases; default\ndispatch persist to ~/.echotools; remove duplicate py.typed.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: drop filesystem shims, use import aliases for 2.3.9",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-21T11:36:15Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "afac3e469a7ace790a206eac5c489a06f4764eaf",
          "body": "Drop obsolete fncall/logger/protocol re-export paths after meta-package\nreorg; achecker clean.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "chore: remove legacy shims and release 2.3.9",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-21T11:26:05Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a9be067ddb7d18030e73974a2e8fc5afc8924188",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: add backward-compat shims for fncall/protocol/logger paths",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-21T11:12:45Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "6f52a74a0cdd2f772d2cc9d577adf051abc70a8a",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "feat(entml): align tags and package layout v2.3.8",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-21T11:11:21Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "71dc3e01ca6e8708da526d766773c9bca3c3a87f",
          "body": "Add normalize_tool_call(s) and apply at parse exits. Bump to 2.3.7.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "feat(fncall): normalize tool call Python literal arguments",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-21T03:14:25Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "044d8de321fb0b839dd26a71f7d2e3bc68c1bd34",
          "body": "Bump version to 2.3.6 so setuptools ships web/input_box static assets reliably.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(web): rename input_box subdirs to snake_case for package-data",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-20T13:12:21Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "288c6ce3624da708583cd248282f10bcad3d2bd9",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(ci): omit spinner module from coverage gate",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-19T10:31:40Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8d5d978a4faeb2c2363231ce23566a6a384abdbd",
          "body": null,
          "is_bot": false,
          "headline": "merge: fix CI (setuptools compat, flaky test assertions)",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-16T05:01:40Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7d1703908959bbedfbece355fe6c9f2a35d6fa2c",
          "body": "…sertions",
          "is_bot": false,
          "headline": "fix: revert license to table form for py3.8 compat, fix flaky test as…",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-16T04:55:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "36d970420bbf893bb3244edbac7510c0e3a7f918",
          "body": "…dle'",
          "is_bot": false,
          "headline": "Merge remote-tracking branch 'origin/worktree-shimmying-wibbling-spin…",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-16T04:27:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "991e14daada8e8b932b42b6398d33dd80d8ed150",
          "body": "…se schema)",
          "is_bot": false,
          "headline": "fix: resolve CI failures (ruff E741, unused imports, setuptools licen…",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-16T03:21:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "768ae0eb51ea6ad2f16982243d47182d1f291777",
          "body": null,
          "is_bot": false,
          "headline": "merge: spinner module + version 2.3.5",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-16T03:10:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d8e03d7e20a8a6119a4b227b33c1b05aadb9a570",
          "body": null,
          "is_bot": false,
          "headline": "chore: bump version to 2.3.5",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-16T03:01:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "93c97b39434390feb640d9fe846416cdf6aa2035",
          "body": null,
          "is_bot": false,
          "headline": "chore: bump version to 2.3.5",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-16T02:18:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4bd518c929c884313735a2c6f9a8f7f99f422cef",
          "body": null,
          "is_bot": false,
          "headline": "feat: add spinner module with pause/theme/message/status support",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-16T02:03:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ebb0c93ea70eb58bb01f771823e0535f8d09a70f",
          "body": null,
          "is_bot": false,
          "headline": "fix: remove License classifier conflicting with SPDX license expression",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-15T16:16:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "08617747d0700398b0a4e3bfdf0b029aaefab7d7",
          "body": null,
          "is_bot": false,
          "headline": "清理历史消息中的entml标签,优化清理逻辑",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-12T03:16:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d9a46e5df65f6c13c2f33075125c78146e30709c",
          "body": null,
          "is_bot": false,
          "headline": "增强entml协议解析子标签,优化无工具时历史注入",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-12T02:34:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "582a7286985168ba02a48b45d0b34dba5123de9d",
          "body": null,
          "is_bot": false,
          "headline": "版本号",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-12T00:46:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "751cab543da772d1a0e4b32dadd173d4f41ce384",
          "body": null,
          "is_bot": false,
          "headline": "更新entml协议",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-12T00:46:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "943bba8dea2e2ec0cd6601f1c62e29c173db7fd1",
          "body": null,
          "is_bot": false,
          "headline": "release: v2.3.1 - add entml protocol",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-11T07:07:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4cccb79d8b2a9edfbea617f529aad8911a62c32b",
          "body": "Move antml/xml/bracket/nous/dsml/original to Provider-Fncall-Util plugin. Core ships EntmlProtocol only; custom protocol requires plugin factory.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "release: v2.1.0 entml-only fncall protocols",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-09T06:37:46Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "b0606dcd508b9334e50ef5abbadb07a77e16a0fa",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: publish workflow verifies PyPI when credentials missing",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-08T16:34:06Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ec7cec6ee0b1c69d1f7f1be016b27676016049c5",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: skip-existing on PyPI upload for idempotent publish",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-08T16:32:20Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a2ab5874ef032a05bb737b366d08cac2b17b7f26",
          "body": "Co-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: publish workflow secrets conditional for GHA validation",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-08T16:29:09Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d7eab017ecd5c66b1852480b4f1780949847da70",
          "body": "Remove numpy from ProxySelector, expand tests to 137 cases with 90% coverage gate, and add PyPI publish fallback via twine token.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix: v1.0.36 numpy-free proxy selector and 91% core coverage",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-08T15:09:49Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ef39476e6ed7a04e95837a4cec297851cfe184fb",
          "body": "Lazy optional-deps imports, event-driven dispatch race, debounced selector persistence, EchoTools facade enhancements, GitHub CI/publish workflows, and expanded test suite (92 tests, 66% core coverage).\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "feat: v1.0.35 portability, CI, tests, and performance improvements",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-08T14:22:15Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "cf274a690f78179eb46a4f21d8b53c03c2d0bfe3",
          "body": null,
          "is_bot": false,
          "headline": "fix(terminal): guard default cmdline with cmd.exe check",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-04T21:47:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "93f860ef18691f744030e0fa56a2554d229763bb",
          "body": "- Added shell fallback chains for Windows (pwsh.exe → PowerShell → cmd.exe)\n- Added shell fallback chains for POSIX ($SHELL → zsh → bash → sh)\n- Added output sanitization module for clean history management\n- Added subprocess monitoring with metadata events\n- Added history management with 5000-line limit\n- Added on_metadata callback to TerminalCallback\n- Version bumped from 1.0.32 to 1.0.33",
          "is_bot": false,
          "headline": "feat(terminal): add shell fallback chain and subprocess monitoring",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-07-03T22:04:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "84b8aa35996f5a50516cd51080a50fa25ba28915",
          "body": null,
          "is_bot": false,
          "headline": "v1.0.30: update",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-06-21T02:15:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "352a9ab8d279b953118421739b3a8d9241ca01bb",
          "body": "- Run Unix PTY allocation in executor to avoid blocking the event loop\n- Add select-based timeout in _read_pty_chunk for clean shutdown\n- Implement shell candidate discovery for robust Unix shell selection\n- Downgrade session lifecycle log messages from info to debug\n- Bump version to 1.0.29",
          "is_bot": false,
          "headline": "perf(terminal): non-blocking PTY operations and reduced log verbosity",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-06-20T08:00:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "979d2bf4b1eb52f56f946f3a6a40bca177a002fc",
          "body": "…ycle\n\nSupport process keep-alive across client disconnections:\n- TerminalSession: multi-client callbacks, offline output buffering,\n  detach()/attach()/detach_callback() for lifecycle management\n- LocalTerminal: kill() for explicit termination, pid/is_alive properties,\n  save_state()/recover_sessions() for crash recovery\n- Version bump: 1.0.26 -> 1.0.27",
          "is_bot": false,
          "headline": "feat(terminal): add session persistence with detach/attach/kill lifec…",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-06-20T02:47:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7eb6dfa9f107c9df2b9389741d1ee1597fb130f6",
          "body": "Replace the pipe-based cmd.exe spawn with ConPTY (via pywinpty) on\nWindows, enabling ANSI escape sequences, cls/color/title builtins,\nterminal resize propagation, and interactive features like Tab\ncompletion and arrow-key history.\n\n- New module: terminal/conpty.py (ConPTYHandle wrapping winpty.PTY)\n\n[…]\ndated to handle ConPTY path\n- Add pywinpty>=2.0.0 as optional 'terminal' dependency\n- Bump version 1.0.25 -> 1.0.26, sync stale __version__\n- Public API unchanged: provider-self needs no modifications",
          "is_bot": false,
          "headline": "feat(terminal): add ConPTY support via pywinpty for real Windows console",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-06-20T00:21:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0a722451cc1084368f6109db7e85942899cdada1",
          "body": "- TerminalSession 抽象基类 + TerminalCallback 回调协议\n- LocalTerminal: Windows asyncio subprocess / Unix pty\n- SSHTerminal: paramiko SSH 客户端封装\n- 版本 1.0.24",
          "is_bot": false,
          "headline": "feat(terminal): 新增终端会话管理模块",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-06-19T05:25:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7bb2571089fe8d25de70aa3ec281d698b8e00396",
          "body": "新增 echotools.web 模块通用组件:\n- stats.py: RequestStats 请求统计收集器(环形缓冲、延迟分布、Token 用量)\n- broker.py: RequestBroker 请求事件广播器 + WebSocket 广播\n- middleware.py: create_stats_middleware 工厂(依赖注入)\n\n支持流式响应 chunk 捕获和广播,可用于任意 aiohttp 项目。\n\nVersion 1.0.16 -> 1.0.17",
          "is_bot": false,
          "headline": "feat(web): 新增 RequestStats + RequestBroker + stats_middleware",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-06-14T03:39:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3f0f4f8d39a87fa84c1d3d8d45748f47c38e35d5",
          "body": "- 移除 nous.py 中 \"classical branch qwen_util.py\" 引用\n- 移除 xml.py/original.py 中 \"Chat2API\" 引用\n- 重命名 parse_fncall_provider → parse_fncall_managed_xml\n- 重命名 _parse_provider_params → _parse_managed_xml_params\n\nVersion 1.0.15 -> 1.0.16",
          "is_bot": false,
          "headline": "refactor(fncall): 移除项目特定引用,提升通用性",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-06-14T02:49:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9f6dbeb3f451443336c863c0e4584c48837e81e9",
          "body": "当 agent-cli 未传回 tool result 时,_normalize_messages 现在会:\n1. 收集所有 assistant tool_call IDs\n2. 收集所有 tool role 的 tool_call_id\n3. 对孤立的 tool_call 注入合成 result: \"[tool {name} was called but no result was provided]\"\n\n这确保所有协议、所有上下文中模型都能看到完整的工具调用历史,\n防止因缺少 tool result 而重复调用同一工具。\n\nVersion 1.0.14 -> 1.0.15",
          "is_bot": false,
          "headline": "fix(fncall): _normalize_messages 检测孤立 tool_calls 并注入合成 result",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-06-14T02:41:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5694e44490c92a7a7ef15b8ff930be0cb990bf41",
          "body": "- Trigger tag: function= prefix (not function_calls)\n- Tool defs: tools/function/name/description/parameters blocks\n- Tool calls: function=name with JSON args\n- Tool results: tool_response tags\n- Based on proven classical branch qwen_util.py\n- Version 1.0.13 -> 1.0.14",
          "is_bot": false,
          "headline": "fix(fncall): rewrite NousProtocol to real Nous/Hermes format",
          "author_name": "nichengfuben",
          "author_login": "nichengfuben",
          "committed_at": "2026-06-13T18:05:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        }
      ],
      "releases_count": 4,
      "commits_last_year": 111,
      "latest_release_at": "2026-07-26T03:57:51Z",
      "latest_release_tag": "v2.3.25",
      "releases_from_tags": false,
      "days_since_last_push": 0,
      "active_weeks_last_year": 6,
      "days_since_latest_release": 1,
      "mean_days_between_releases": 5.9
    },
    "community": {
      "has_readme": true,
      "has_license": false,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 28,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "echotools",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "Development Status :: 4 - Beta",
            "Intended Audience :: Developers",
            "Programming Language :: Python :: 3",
            "Programming Language :: Python :: 3.10",
            "Programming Language :: Python :: 3.11",
            "Programming Language :: Python :: 3.12",
            "Programming Language :: Python :: 3.13",
            "Programming Language :: Python :: 3.8",
            "Programming Language :: Python :: 3.9",
            "Topic :: Software Development :: Libraries"
          ],
          "ecosystem": "pypi",
          "matches_repo": true,
          "registry_url": "https://pypi.org/project/echotools/",
          "is_deprecated": false,
          "latest_version": "2.3.61",
          "repository_url": "https://github.com/nichengfuben/echotools",
          "versions_count": 81,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": 5667,
          "first_published_at": "2026-06-13T08:28:15.598947Z",
          "latest_published_at": "2026-07-28T01:39:57.791493Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 0
        }
      ]
    },
    "popularity": {
      "forks": 0,
      "stars": 2,
      "watchers": 0,
      "fork_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_forks": 0
      },
      "star_history": null,
      "open_issues_and_prs": 0
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [
        "docs/Makefile"
      ],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [
        "src/py.typed"
      ],
      "toolchain_manifests": [],
      "largest_source_bytes": 63845,
      "source_files_sampled": 212,
      "oversized_source_files": 2,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "pyproject.toml",
        "setup.cfg"
      ],
      "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": [
        "pypi"
      ],
      "dependencies": [
        {
          "name": "typing-extensions",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=4.7.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": 0,
        "merged_prs": 0,
        "open_issues": 0,
        "closed_ratio": null,
        "closed_issues": 0,
        "closed_unmerged_prs": 0
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "nichengfuben",
          "commits": 111,
          "avatar_url": "https://avatars.githubusercontent.com/u/190981802?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml",
        "publish.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": true
    },
    "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": null,
            "reason": "no pull request found",
            "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 0/30 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 0,
            "reason": "project has 0 contributing companies or organizations -- score normalized to 0",
            "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": 0,
            "reason": "project was created within the last 90 days. Please review its contents carefully",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": 10,
            "reason": "packaging workflow 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": "no SAST tool detected",
            "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": 0,
            "reason": "Project has not signed or included provenance with any releases.",
            "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": "e673cc3e8e4cde29606fe3de249c7f042f5934b3",
        "ran_at": "2026-07-28T01:40:55Z",
        "aggregate_score": 2.9,
        "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-28T01:40:02Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": null,
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/nichengfuben/echotools",
    "host": "github.com",
    "name": "echotools",
    "owner": "nichengfuben"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "at_risk",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 49,
      "inputs": {
        "security": 29,
        "vitality": 71,
        "community": 25,
        "governance": 32,
        "engineering": 85
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 71,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "moderate",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 58,
            "inputs": {
              "commits_last_year": 111,
              "human_commit_share": 1,
              "days_since_last_push": 0,
              "active_weeks_last_year": 6
            },
            "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": "6/52 weeks with commits",
                "points": 4.2,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 6
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "111 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 111
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "project was created within the last 90 days. Please review its contents carefully",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "excellent",
            "name": "Release discipline",
            "note": null,
            "notes": [],
            "value": 90,
            "inputs": {
              "releases_count": 4,
              "latest_release_tag": "v2.3.25",
              "releases_from_tags": false,
              "days_since_latest_release": 1,
              "mean_days_between_releases": 5.9
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "4 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 4
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 1 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 1
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~5.9 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 5.9
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "Project has not signed or included provenance with any releases.",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "excellent",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "unverified",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": "repository_too_young",
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": null,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "maintenance record not established from the collected data",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_unverified",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "critical",
        "name": "Community & Adoption",
        "value": 25,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 1,
            "inputs": {
              "forks": 0,
              "stars": 2,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "2 stars",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "0 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "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": "critical",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 25,
            "inputs": {
              "has_readme": true,
              "has_license": false,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "has_pull_request_template": false
            },
            "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": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.3
              }
            ]
          },
          {
            "key": "ecosystem_adoption",
            "band": "moderate",
            "name": "Ecosystem adoption (downloads)",
            "note": "Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "registry_dependents"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 62,
            "inputs": {
              "packages": [
                "echotools"
              ],
              "dependents": null,
              "ecosystems": "pypi",
              "total_downloads": null,
              "monthly_downloads": 5667
            },
            "components": [
              {
                "key": "monthly_downloads",
                "name": "Monthly downloads",
                "detail": "5,667 downloads/month across pypi",
                "points": 50,
                "status": "partial",
                "details": [
                  {
                    "code": "downloads_monthly",
                    "params": {
                      "count": 5667,
                      "ecosystems": "pypi"
                    }
                  }
                ],
                "max_points": 80
              },
              {
                "key": "registry_dependents",
                "name": "Registry dependents",
                "detail": "not reported by this ecosystem",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "not_reported_by_this_ecosystem",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "at_risk",
        "name": "Sustainability & Governance",
        "value": 32,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 10,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 1,
              "top_contributor_share": 1
            },
            "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 100% of commits",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "1 contributors",
                "points": 1.4,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "critical",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): Issue resolution, PR acceptance. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "issue_resolution",
                    "pr_acceptance"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 1,
            "inputs": {
              "merged_prs": 0,
              "open_issues": 0,
              "closed_issues": 0,
              "issue_closed_ratio": null,
              "closed_unmerged_prs": 0
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "no issues or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_issues_or_data",
                    "params": {}
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "no decided pull requests or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_decided_prs_or_data",
                    "params": {}
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "at_risk",
            "name": "Ownership & stewardship",
            "note": "Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "verified_domain"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 33,
            "inputs": {
              "followers": 1,
              "owner_type": "User",
              "is_verified": null,
              "owner_login": "nichengfuben",
              "public_repos": 29,
              "account_age_days": 597
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "personal (user) account",
                "points": 10,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_personal",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": "not applicable to user accounts",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "not_applicable_to_user_accounts",
                    "params": {}
                  }
                ],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "1 followers of nichengfuben",
                "points": 2.2,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 1,
                      "login": "nichengfuben"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "29 public repos, account ~1 yr old",
                "points": 14,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 29
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 1
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "echotools"
              ],
              "ecosystems": "pypi",
              "any_deprecated": false,
              "min_days_since_publish": 0
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on pypi",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "pypi"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 0 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "81 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 81
                    }
                  }
                ],
                "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": 85,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "excellent",
            "name": "Engineering practices",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: CI-Tests. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_ci_tests"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 92,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": true
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "2 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "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": null,
                "points": 16,
                "status": "met",
                "details": [],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 9.6,
                "status": "met",
                "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": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "good",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 75,
            "inputs": {
              "topics": [],
              "has_wiki": true,
              "homepage": null,
              "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": null,
                "points": 0,
                "status": "missed",
                "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": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "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": "critical",
        "name": "Security",
        "value": 29,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "critical",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): CI-Tests. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "ci_tests"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 29,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 17,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 1,
              "scorecard_aggregate": 2.9
            },
            "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": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "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 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "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": "project was created within the last 90 days. Please review its contents carefully",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow detected",
                "points": 5,
                "status": "met",
                "details": [],
                "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": "no SAST tool detected",
                "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": "Project has not signed or included provenance with any releases.",
                "points": 0,
                "status": "missed",
                "details": [],
                "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
              }
            ]
          }
        ],
        "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": 62,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "at_risk",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.92,
              "agent_instruction_files": [],
              "agent_instruction_max_bytes": null
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "no CLAUDE.md / AGENTS.md / editor rules",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_instructions",
                    "params": {}
                  }
                ],
                "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": "92 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 92,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "good",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 72,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [],
              "has_dockerfile": false,
              "typed_language": false,
              "bootstrap_files": [
                "docs/Makefile"
              ],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [
                "src/py.typed"
              ],
              "agent_commit_share": 0.68,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": "docs/Makefile",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "docs/Makefile"
                    }
                  }
                ],
                "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": null,
                "points": 11,
                "status": "met",
                "details": [],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "src/py.typed",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "src/py.typed"
                    }
                  }
                ],
                "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": "68 of the last 100 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 68,
                      "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": "good",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 82,
            "inputs": {
              "primary_language": "Python",
              "largest_source_bytes": 63845,
              "source_files_sampled": 212,
              "oversized_source_files": 2
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Python with type-check config (src/py.typed)",
                "points": 27,
                "status": "partial",
                "details": [
                  {
                    "code": "typecheck_config_language",
                    "params": {
                      "files": "src/py.typed",
                      "language": "Python"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "2/212 source files over 60KB",
                "points": 54.5,
                "status": "partial",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 212,
                      "oversized": 2
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
      }
    ],
    "metrics_version": "1.13.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",
    "deps.dev does not index pypi:echotools@2.3.61; advisories assessed against the repository dependency graph instead"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-28T01:41:00.976063Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/n/nichengfuben/echotools.svg",
  "full_name": "nichengfuben/echotools",
  "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 v1.13.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 agregadasPyPI.