CommunityProgramación y desarrollogithub.com

GreenWich-art/agent-skills

Reformatea documentos Markdown o docx existentes según normas de publicaciones académicas. Permite espaciado interlineal de 1.5, tablas de tres líneas, tamaños y fuentes específicos para títulos y cuerpo.

¿Qué es agent-skills?

Esta habilidad para agentes de IA (compatible con Claude Code, Cursor, Codex) permite reformatear documentos Markdown o archivos docx existentes para cumplir con estándares de formato académico chino: títulos de nivel 1 en SimHei tamaño 15pt, nivel 2 en SimHei tamaño 14pt, cuerpo en Song Ti tamaño 12pt con interlineado 1.5, tablas de tres líneas, y mezcla chino-occidental con Times New Roman. No utiliza inserción por párrafos, sino que genera directamente docx con python-docx. Incluye scripts para bordes de tabla, itálicas en estadísticos (P valores, α, χ²), espaciado de signos, y reemplazo de comillas. Sigue reglas estrictas: no usar párrafos vacíos para espacios (solo spacing de párrafo), no negritas a menos que el usuario lo pida, y nunca regenerar el documento completo si el usuario ya hizo ediciones manuales.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/GreenWich-art/agent-skills/tree/HEAD/skills/docx-academic-format

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Reformateo de docx para artículos académicos

Cuándo usarlo

  • Cuando necesites convertir un informe o tabla de Markdown a un docx que cumpla con normas de publicación.
  • Cuando necesites aplicar configuración masiva de fuentes mixtas chino-occidental, tablas de tres líneas y espaciado entre títulos de varios niveles.
  • Cuando las API de inserción por párrafos no puedan controlar bordes de tablas de tres líneas, rFonts a nivel de run o división de itálicas, usa esta habilidad.

Tamaños de fuente china → puntos

TamañoPuntos
Sanhao16
Xiaosan15
Sihao14
Xiaosi12
Wuhao10.5

Norma por defecto (se puede sobrescribir según la revista o universidad de destino): títulos de nivel 1 en Xiaosan SimHei, nivel 2 en Sihao SimHei, cuerpo en Xiaosi Song Ti con interlineado 1.5, todos los textos occidentales en Times New Roman, tablas en Wuhao Song Ti.

Puntos clave de implementación (problemas encontrados)

1. Bordes de tabla de tres líneas

La unidad de w:sz en w:tblBorders es 1/8 de punto:

  • Superior/inferior 1.5 puntos → sz=12
  • Línea media (debajo del encabezado) 1.0 puntos → sz=8
  • Las líneas verticales deben establecerse explícitamente como val="none", de lo contrario Word dibujará líneas verticales por defecto.

La línea debajo del encabezado se debe configurar con w:tcBorders/w:bottom en cada row.cells[i]; solo configurar insideH en tblBorders no es fiable en algunos renderizadores.

2. cell.text = '' dejará un run vacío sin formato

Para vaciar una celda, usa:

p = cell.paragraphs[0]
for r in list(p.runs):
    r._element.getparent().remove(r._element)

De lo contrario, runs[0] será un run vacío sin rPr, que luego causará errores al verificar fuentes.

3. Para mezcla chino-occidental, se deben establecer 4 posiciones de fuente simultáneamente

rFonts.set(qn('w:ascii'), 'Times New Roman')
rFonts.set(qn('w:hAnsi'), 'Times New Roman')
rFonts.set(qn('w:eastAsia'), '宋体')   # Fuente china
rFonts.set(qn('w:cs'), 'Times New Roman')

Si se omite eastAsia, el texto chino volverá a la fuente predeterminada.

4. Las itálicas deben dividir los runs

Los estadísticos (P < 0.001, OR = 2.17, CI, β, valor P) deben dividirse en runs independientes para poder establecer itálica por separado. El orden de las expresiones regulares es importante: P值 debe coincidir con \bP(\s*值)\b; primero coincide P\s*[<>=]\s*[\d.]+ y luego P aislado; de lo contrario, P=0.06 se dividirá en P + =0.06.

5. "Dejar una línea en blanco" = espaciado de párrafo, no párrafo vacío

Nunca uses saltos de línea (párrafos vacíos) para lograr "dejar una línea en blanco". Los párrafos vacíos en Word causan:

  • Líneas en blanco extra al editar, difíciles de eliminar limpiamente
  • Espacios en blanco impredecibles al imprimir/convertir a PDF
  • Espaciado descontrolado al superponerse con espacios antes/después

Método correcto: usar space_before / space_after (unidad: puntos):

SituaciónImplementación
Título nivel 1 "dejar una línea arriba y abajo"space_before = Pt(12×1.5), space_after = mismo valor
Título nivel 2 "dejar una línea arriba"space_before = Pt(12×1.5), space_after = Pt(0)
Títulos nivel 3 e inferioresSin espaciado
Cuerpospace_before = Pt(0), space_after = Pt(0)
Entre figura/tabla y textoEstablecer space_before/after en el párrafo de la figura/tabla, no insertar párrafos vacíos extra

Valores de referencia: Xiaosi (12pt) una línea en blanco ≈ 18 puntos; Wuhao (10.5pt) ≈ 15.75 puntos.

Método de verificación: sum(1 for p in doc.paragraphs if not p.text.strip()) == 0——un documento normal no debe tener ningún párrafo vacío.

6. Prohibido negrita automática

Nunca pongas texto en negrita a menos que el usuario lo solicite explícitamente.

Este es un error fácil de cometer: **texto** en Markdown es una marca de énfasis del archivo fuente, no una instrucción de formato. Renderizarlo como negrita en Word equivale a añadir énfasis no solicitado al texto original.

Método correcto:

  • **texto** → tomar solo el contenido del texto, sin negrita
  • Cuando necesites subtítulos, usa párrafos independientes + fuente/tamaño objetivo, no uses negrita para jerarquía
  • La sensación de jerarquía solo debe venir de: fuente (SimHei/KaiTi), tamaño, espaciado antes/después de párrafo, numeración

Igualmente prohibido: itálica excepto para estadísticos/símbolos especificados; subrayado, puntos de énfasis, texto de color, escala de caracteres/espaciado entre caracteres——si el usuario no lo pide, no se toca.

Después de la conversión, se debe auto-verificar:

# No debe haber negrita no solicitada
negritas = [r.text for p in doc.paragraphs for r in p.runs if r.font.bold]

Si el encabezado debe ir en negrita depende de la solicitud del usuario; esta habilidad por defecto no pone negrita en el encabezado (se distingue por tamaño + tabla de tres líneas).

7. Títulos nivel 3 e inferiores: sin línea en blanco

Solo los títulos de nivel 1 y 2 llevan espaciado antes/después; nivel 3 e inferiores: sin espaciado, sin negrita.

Scripts

ScriptPropósito
scripts/tri_line_table.pyBordes de tabla de tres líneas (incluye verify_table_borders() auto-verificación)
scripts/fmt_lib.pyBiblioteca de funciones útiles: fuentes, espaciado, análisis de segmentos en negrita, estilos de celda
scripts/fix_italic.pyDivisión de itálicas en estadísticos (modifica in situ, no recrea runs)
scripts/fix_spacing2.pyCorrección de espaciado en mezcla chino-inglés y signos (modifica in situ, no recrea runs)

Dependencias: pip install python-docx

fmt_lib.parse_bold_segments(text, allow_bold=False): por defecto descarta pero no renderiza las marcas **; solo cuando el usuario pide explícitamente negrita se pasa allow_bold=True.

Orden de ejecución para corregir formato en un docx existente (no invertir el orden):

python3 scripts/fix_italic.py   objetivo.docx   # Primero dividir itálicas
python3 scripts/fix_spacing2.py objetivo.docx  # Luego corregir espaciado

Invertir el orden hará que las reglas de espaciado recombinen incorrectamente los símbolos ya separados.

Lista de verificación (obligatorio después de generar)

  1. Número de párrafos vacíos == 0 — Prioridad máxima, sum(1 for p in doc.paragraphs if not p.text.strip())
  2. Runs en negrita == 0 (a menos que el usuario lo solicite) — [r.text for p in doc.paragraphs for r in p.runs if r.font.bold] debe estar vacío; también dentro de tablas
  3. Títulos nivel 1: sz==15 y eastAsia==SimHei, space_before==space_after==18pt
  4. Títulos nivel 2: sz==14, space_before==18pt
  5. Cuerpo: sz==12, eastAsia==Song Ti, line_spacing==1.5
  6. Todos los runs: ascii/hAnsi==Times New Roman
  7. Número de runs en itálica >0 y su contenido son estadísticos (este es el único formato especial permitido)
  8. Tabla de tres líneas: usar verify_table_borders(table) para verificar cada tabla, todas deben devolver (True, [])
  9. Título de tabla: Wuhao SimHei centrado, sin puntuación al final
  10. Todos los runs dentro de tabla: Song Ti + 10.5pt
  11. Párrafos de cuerpo: alineación == JUSTIFY, títulos y títulos de tabla mantienen su alineación original
  12. Espacio antes del signo de porcentaje: 33 % en lugar de 33%
  13. Sin espacio entre signo y carácter chino: 30余次; espacio entre signo y número: ≥ 10 小时, P < 0.05

Script de auto-verificación único:

import sys; sys.path.insert(0, '<skill>/scripts')
from tri_line_table import verify_table_borders
from docx import Document
d = Document('out.docx'); ps = d.paragraphs
assert sum(1 for x in ps if not x.text.strip()) == 0, 'Existen párrafos vacíos'
assert not [r for x in ps for r in x.runs if r.font.bold], 'Existen negritas'
assert not [r for t in d.tables for row in t.rows for c in row.cells
            for r in c.paragraphs[0].runs if r.font.bold], 'Existen negritas en tablas'
for i, t in enumerate(d.tables, 1):
    good, prob = verify_table_borders(t)
    assert good, f'Tabla {i} no cumple formato de tres líneas: {prob}'
print('Todo correcto')

Cuando no hay entorno de verificación visual

Si no tienes LibreOffice / Word para convertir a PDF y verificar visualmente las tablas de tres líneas, usa verificación a nivel XML (elementos 1–9 de la lista), que cubre de forma fiable tamaño de fuente, fuente, bordes y espaciado.

Suplemento: Especificación exacta de tabla de tres líneas

Solo 3 líneas:

PosiciónPuntosw:sz
Fila de encabezado (borde superior)1.512
Fila de encabezado (borde inferior)1.08
Última fila (borde inferior)1.512
Todas las demás líneas (incluidas filas intermedias, verticales)Vacíoval="none"

Clave: No basta con establecer insideH en tblBorders. Los bordes a nivel de tabla son anulados por tcBorders a nivel de celda; una vez que una celda tiene bottom, se toma el nivel de celda. Se debe establecer explícitamente celda por celda:

  1. Todos los seis elementos de tblBorders en none
  2. Todos los lados de tcBorders de todas las filas primero en none
  3. Celdas de la fila de encabezado: top=('single',12) + bottom=('single',8)
  4. Celdas de la última fila: bottom=('single',12)

clear_row_borders() debe llamarse para cada fila, no solo para el encabezado y la última fila; de lo contrario, las filas intermedias dejarán líneas residuales.

Suplemento: Reemplazo de comillas chinas

Las " en Markdown son comillas rectas; al convertir a docx deben reemplazarse por pares “ / ”:

dq_index = 0   # Debe contar continuamente a través de runs, no reiniciar por cada run
for r in p.runs:
    for ch in r.text:
        if ch == '"':
            out.append('“' if dq_index % 2 == 0 else '”')
            dq_index += 1

Solo modificar r.text, no tocar rPr → se conservan fuente, tamaño e itálica. Después del reemplazo, verificar: recuento total de " == 0, recuento de “ == recuento de ”, y por párrafo el emparejamiento izquierda-derecha (el equilibrio global no garantiza el equilibrio por párrafo).

Suplemento: La edición en Word genera runs fragmentados

Después de que el usuario edite manualmente en Word, un párrafo se divide en varios runs, algunos de los cuales (como -, caracteres individuales extraídos) no llevan rFonts y vuelven a la fuente predeterminada. Después de corregir comillas/bordes, se debe revisar:

rf = r._element.rPr.rFonts if r._element.rPr is not None else None
if rf is None or rf.get(qn('w:eastAsia')) is None:  # → necesita completarse

Forma de completar: tomar como referencia el primer run del párrafo que ya tenga fuente establecida, heredar su rFonts/sz/b.

Recordatorio importante del flujo de trabajo

Si el usuario ha modificado manualmente el documento en Word, nunca regeneres el documento completo; eso sobrescribiría los cambios del usuario. Método correcto: leer el docx existente → modificar XML in situ → guardar. Antes de modificar, haz una copia de seguridad: cp xxx.docx /tmp/backup.docx.

Norma de itálica

Único criterio de juicio: si el carácter es una variable o símbolo en matemáticas/estadística

Categoría¿Itálica?Ejemplo
Significancia estadística PItálicaP = 0.04, P < 0.001
Nivel de significación αItálicaα = 0.05
Estadísticos de prueba F, Z, H, χ², t, rItálicaF = 4.52, χ² = 8.31
Letras especiales (símbolos de genes, valor β de metilación, etc.)Itálicaβ
OR, CI, RR, HR, SD, SE, AUCNo itálicaOR = 3.14, 95% CI
Abreviaturas de varias letras (BMI, OGTT, FPG…)No itálicaBMI, OGTT
Texto chinoNo itálica值, 为

⚠️ Error más común: en "P值" solo P va en itálica

✗ Itálica 'P值'         ← "值" también va en itálica incorrectamente
✓ Itálica 'P' + redonda '值'
✓ Itálica 'P' + redonda '值为' + ...

P 值, P值, P 值为 0.07 — siempre solo la P va en itálica, los caracteres chinos que la siguen van en redonda.

Las tres reglas de oro de la división de itálicas (lecciones aprendidas)

  1. Nunca recrees runs. Usar "recomposición de runs por flujo de caracteres" para corregir formato hará que se pierdan rFonts/sz/b, se borren los bordes de tablas de tres líneas y las marcas de itálica se fragmenten. Solo se puede modificar run.text, nunca eliminar/crear elementos run.
  2. La división debe usar copy.deepcopy(run._element) para conservar todos los atributos de formato; la posición de inserción parent.insert(index+1, el) (justo al lado del run original). Usar append lo colocará al final del párrafo, desordenando el orden, dando lugar a PP. Texto de la copia = lead + rest + trail (sin stat), texto del run original = lead + stat.
  3. run.text = x no vacía los nodos <w:t> existentes; en la copia deepcopy se añadirá. Primero se debe el.remove(child) eliminar todos los w:t/w:br/w:tab/w:cr; y antes de establecer el texto, se debe eliminar w:i/w:iCs; si el orden es incorrecto, también fallará.

Ver implementación de referencia en scripts/fix_italic.py.

Espaciado en mezcla chino-inglés y signos

Reglas básicas

SituaciónReglaEjemplo
Chino ↔ número/letra/puntuaciónSin espacio在30余次未校正检验的背景下, 但Table 1显示
Carácter chino ↔ carácter chinoSin espacio研究目的
Entre palabras en inglésCon espacioTable 1
Dentro de expresiones matemáticasEspacio entre letra/número y operadorP = 0.04, BMI < 18.5
Expresión matemática ↔ chinoCon espacioP < 0.05 为阈值

Espaciado entre signos y números/letras/chino (fácil de equivocarse)

CombinaciónReglaEjemplo
Signo ↔ númeroCon espacio33 %, 87.3 %, P = 0.04
Signo ↔ palabra/letra en inglésCon espacioTable 1; α = 0.05
Signo ↔ carácter chinoSin espacio30余次, ≥10小时 (signo pegado al carácter chino)

Punto fácil de equivocarse: 30 余次 (incorrecto, espacio extra entre signo y carácter chino) vs 30余次 (correcto). Signos de operador (≥ ≤ = < >) deben tener espacio con los números, pero el signo y el carácter chino que le sigue inmediatamente no llevan espacio.

Signo de porcentaje

33 % con espacio entre el número y %. Esto es un requisito de las normas de publicaciones científicas; no escribas 33%.

Ver implementación de referencia en scripts/fix_spacing2.py (incluye fix_spacing_in_run).

Alineación de párrafos de cuerpo

Los párrafos de cuerpo deben tener alineación justificada — es decir, JUSTIFY en Word (Cmd+J / Ctrl+J), no DISTRIBUTE.

from docx.enum.text import WD_ALIGN_PARAGRAPH
p.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY

Solo aplicar a párrafos de cuerpo; los siguientes mantienen su alineación original, no modificar:

  • Todos los niveles de título — normalmente alineación izquierda
  • Título de tabla — centrado
  • Celdas dentro de tabla — según necesidad (izquierda o centrado)

⚠️ Tres reglas de oro (orden de ejecución y verificación)

Orden de ejecución

python3 scripts/fix_italic.py   objetivo.docx   # Primero dividir itálicas
python3 scripts/fix_spacing2.py objetivo.docx  # Luego corregir espaciado

Verificación (obligatorio, no faltar ninguna)

import re
norm = lambda s: re.sub(r'\s', '', s)
# ① Cero pérdida de texto — evitar caracteres omitidos/duplicados
assert all(norm(a.text) == norm(b.text)
           for a, b in zip(doc_original.paragraphs, doc_nuevo.paragraphs))
# ② Los runs en itálica solo pueden ser el símbolo estadístico en sí (P/α/β/F/Z/H/χ/t/r), no pueden contener:
#    - operadores o números: [=<>≤≥0-9]
#    - caracteres chinos ('值' en 'P值' debe ser redonda)
#    - abreviaturas como OR/CI/RR
for p in doc_nuevo.paragraphs:
    for r in p.runs:
        if r.font.italic and r.text.strip():
            t = r.text.strip()
            assert not re.search(r'[=<>≤≥0-9\u4e00-\u9fff]', t), f'Itálica contiene operador/número/chino: {t!r}'
            assert t.split(' ')[0] not in ('OR','CI','RR','HR','SD','SE','AUC'), f'Abreviatura no debe ir en itálica: {t!r}'
# ③ Párrafos de cuerpo deben estar justificados
from docx.enum.text import WD_ALIGN_PARAGRAPH
assert any(p.alignment == WD_ALIGN_PARAGRAPH.JUSTIFY for p in doc_nuevo.paragraphs)
# ④ Comillas chinas emparejadas
assert sum(p.text.count('“') for p in doc_nuevo.paragraphs) == \
       sum(p.text.count('”') for p in doc_nuevo.paragraphs)

⚠️ Reglas de máxima prioridad (infringir = rehacer)

  1. "Dejar una línea en blanco" solo se puede hacer con espaciado de párrafo (space_before/space_after), nunca con saltos de línea / párrafos vacíos.
  2. Nunca pongas texto en negrita a menos que el usuario lo solicite explícitamente en el requerimiento. El **texto** de Markdown es una marca de énfasis del archivo fuente, no una instrucción de formato — solo elimina la marca, no pongas negrita.
  3. Los formatos no solicitados por el usuario (subrayado, puntos de énfasis, texto de color, espaciado entre caracteres, escala de caracteres) nunca se tocan.
  4. Si el usuario ha modificado manualmente el documento, prohibido regenerar el documento completo; solo se puede leer el archivo existente y modificar XML in situ.

La sensación de jerarquía solo puede venir de la fuente (SimHei/KaiTi), el tamaño, el espaciado antes/después de párrafo y la numeración — no de la negrita.

Precauciones al reducir texto en cuerpo/tablas

Al reducir contenido textual en un docx modificado por el usuario:

Regla de oro: escribir fusionado en el primer run, vaciar el resto

def set_paragraph_text(p, text):
    runs = p.runs
    if not runs: return False
    runs[0].text = text          # El formato se conserva en el primer run
    for r in runs[1:]:
        r.text = ''              # ⚠️ Debe vaciarse, de lo contrario queda texto antiguo causando duplicación
    return True

Igualmente se sigue el principio de "no recrear runs" — solo modificar run.text, nunca eliminar/crear <w:r>.

Localizar tabla: por orden del flujo del documento, no se puede usar índice de doc.paragraphs

Cuando hay tablas incrustadas en párrafos, el índice doc.paragraphs[i] no se corresponde con la tabla. Se debe recorrer el flujo del body:

from docx.table import Table
from docx.text.paragraph import Paragraph
W = '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}'
seq = []
for c in doc.element.body.iterchildren():
    if c.tag == W+'p':   seq.append(('p', Paragraph(c, doc)))
    elif c.tag == W+'tbl': seq.append(('t', Table(c, doc)))

Dos problemas al añadir/eliminar filas

  1. table.rows se vuelve a analizar XML cada vez que se accede — después de eliminar una fila, usar rows[ri] dará error de índice. Primero tomar una instantánea con rows = list(table.rows), luego eliminar desde atrás hacia adelante.
  2. Las filas clonadas perderán el borde inferior de la última fila — después de clonar la última fila para completar, la línea inferior de la tabla de tres líneas se convertirá en nil. Al final, se debe volver a establecer el borde bottom de la última fila (sz=12).

⚠️ Al detectar bordes, no solo reconozcas none

En las tablas de tres líneas propias de Word, las filas intermedias escriben w:val="nil" en lugar de "none". Para juzgar "sin línea", se deben aceptar ambos:

assert tv in ('nil','none') and bv in ('nil','none')

Solo reconocer none dará falsos positivos de "borde dañado", cuando en realidad es la lógica de detección la que está mal.

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

GreenWich-art/agent-skills

桌面 GUI 操作的统一编排入口。当用户要求操作一个只有图形界面的应用(改设置、点按钮、填表单、操作无 CLI/连接器的软件)时使用此 skill。提供四级能力:①路由决策——判断任务该走连接器、浏览器控制、Office 技能、命令行还是 computer use 桌面接管;②授权引导——检测并引导用户完成 macOS 辅助功能/屏幕录制权限授权,处理授权不生效等常见坑;③任务派发与验证——按目标/边界/失败处理三段式描述派发 GUI 任务,并以最终截图为准确认结果;④AX 兜底层——内置 axctl 工具做元素级无障碍读写(AXPress 语义点击,不碰鼠标不抢焦点),适用于 computer use 不可用或轻量场景。台前调度全场景实测(macOS 15 + 27.0.1 复检)。触发词:操控电脑、操作某个软件、点某个按钮、GUI 操作、电脑设置。

GreenWich-art/agent-skills

参考文献的 GB/T 7714-2015 著录规范化,以及 URL / DOI / PMID / PMC 链接的 可访问性批量核验与"假失效"识别。 触发场景: - 论文 / 综述 / 作业要交,需要核对参考文献格式 - "帮我按国标改一下参考文献" - 要交付一份文献清单,需要确认链接没死 - 已经有一堆 links,要批量验证哪些还能打开 含一个可复用脚本 `scripts/check_refs.py`,能自动判别 URL / DOI / PMID / PMC 并并发核验,且能区分「真死」与「被反爬」——后者不该判为失效。 不适用:从零生成一份带引文的正文(那是写作任务); Zotero 样式定制(见正文第六节,按需手工改 CSL)。

GreenWich-art/agent-skills

Local OCR using the macOS Vision framework. Triggers: OCR, 识别图片文字, 提取截图内容, 扫描件转文字, 图片转文字, 扫描版PDF转文字, read text from image/screenshot/photo/scanned PDF. Local-first and offline; prefer it over cloud OCR whenever data is sensitive or the machine is offline.

GreenWich-art/agent-skills

Download open-access biomedical article PDFs from PMC with offline Proof-of-Work solving. Triggers: 下载文献PDF, PMC PDF, 403/404 on PDF link, Preparing to download, reCAPTCHA page, OA full text, supplementary files, download paper PDF.

Skills relacionados