diff --git a/services/mcp-docugen/src/mcp_docugen/config.py b/services/mcp-docugen/src/mcp_docugen/config.py index 2088cc9..54bbb13 100644 --- a/services/mcp-docugen/src/mcp_docugen/config.py +++ b/services/mcp-docugen/src/mcp_docugen/config.py @@ -15,6 +15,7 @@ class Settings(BaseSettings): llm_model_default: str = "anthropic/claude-sonnet-4" public_base_url: str = Field(...) data_dir: Path = Path("/data") + templates_seed_dir: Path = Path("/app/services/mcp-docugen/templates_seed") asset_ttl_days: int = 30 max_image_size_mb: int = 10 llm_timeout_seconds: int = 60 diff --git a/services/mcp-docugen/src/mcp_docugen/main.py b/services/mcp-docugen/src/mcp_docugen/main.py index ea93641..d6a1598 100644 --- a/services/mcp-docugen/src/mcp_docugen/main.py +++ b/services/mcp-docugen/src/mcp_docugen/main.py @@ -15,6 +15,7 @@ from mcp_docugen.http_routes import build_http_app from mcp_docugen.llm_client import OpenRouterClient from mcp_docugen.mcp_tools import build_mcp_server from mcp_docugen.renderer import Renderer +from mcp_docugen.template_seed import seed_templates from mcp_docugen.template_store import TemplateStore logger = logging.getLogger("mcp_docugen") @@ -28,6 +29,7 @@ async def build_app(settings: Settings | None = None) -> FastAPI: db_path = settings.data_dir / "mcp_docugen.db" template_store = TemplateStore(base_dir=templates_dir) + seed_templates(settings.templates_seed_dir, templates_dir) generation_store = GenerationStore( db_path=db_path, generated_dir=generated_dir ) diff --git a/services/mcp-docugen/src/mcp_docugen/template_seed.py b/services/mcp-docugen/src/mcp_docugen/template_seed.py new file mode 100644 index 0000000..ce46fb7 --- /dev/null +++ b/services/mcp-docugen/src/mcp_docugen/template_seed.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import logging +import shutil +from pathlib import Path + +logger = logging.getLogger(__name__) + + +def seed_templates(seed_dir: Path, target_dir: Path) -> list[str]: + """Copy missing templates from seed_dir into target_dir. + + Idempotent: never overwrites existing template directories (preserves + edits made via MCP tools at runtime). Returns the list of template names + that were newly seeded. + """ + if not seed_dir.exists(): + logger.info("templates seed dir not found, skipping: %s", seed_dir) + return [] + + target_dir.mkdir(parents=True, exist_ok=True) + seeded: list[str] = [] + + for src in sorted(seed_dir.iterdir()): + if not src.is_dir(): + continue + if not (src / "template.md").is_file(): + continue + dst = target_dir / src.name + if dst.exists(): + continue + shutil.copytree(src, dst) + seeded.append(src.name) + logger.info("seeded template: %s", src.name) + + if seeded: + logger.info("seeded %d template(s): %s", len(seeded), ", ".join(seeded)) + return seeded diff --git a/services/mcp-docugen/templates_seed/offerta/assets/.gitkeep b/services/mcp-docugen/templates_seed/offerta/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/services/mcp-docugen/templates_seed/offerta/template.md b/services/mcp-docugen/templates_seed/offerta/template.md new file mode 100644 index 0000000..9ce4f34 --- /dev/null +++ b/services/mcp-docugen/templates_seed/offerta/template.md @@ -0,0 +1,212 @@ +--- +name: offerta +description: Offerta economica Tielogic SRL stile docx ufficiale — cover con FORNITORE/CLIENTE, descrizione prodotto, funzionalità tabellate, modello commerciale (setup + canone opzionale + sconto rivenditore opzionale + servizi inclusi + proiezione costi), condizioni standard, accettazione e firme +model: anthropic/claude-sonnet-4 +required_variables: + - name: titolo_offerta + type: string + - name: prodotto_nome + type: string + - name: ref_doc + type: string + - name: data_emissione + type: string + - name: data_validita + type: string + - name: cliente + type: string + - name: cliente_indirizzo + type: string + - name: cliente_rif + type: string + - name: autore + type: string + - name: iva_aliquota + type: string + - name: pagamento_setup + type: string + - name: durata_minima + type: string +instructions_hint: | + Nel content_md fornisci: descrizione prodotto/servizio (1-3 paragrafi), tabella funzionalità (Area | Descrizione), voci di setup con importi €, voci canone (opzionali con listino + sconto opzionale), servizi inclusi (lista), proiezione costi N anni (opzionale, indicare numero utenti/console), tempi di consegna, eventuali note specifiche. Se non esiste canone (offerta una tantum) ometti l'intero blocco canone+proiezione. +--- + +Sei un commerciale senior di Tielogic SRL. Devi redigere un'**offerta economica formale** stile documento ufficiale Tielogic (impostazione del modello docx interno). Output: **Markdown puro** con HTML inline solo per cover/firme/page-break/badge. + +Italiano formale, terza persona impersonale, tono professionale ma asciutto. Niente marketing pomposo, niente superlativi. + +## Frontmatter di output (compatibile md-to-pdf, OBBLIGATORIO) + +``` +--- +stylesheet: /home/adriano/Documenti/Git_XYZ/ArcaSuite/themes/tielogic-devnotes.css +pdf_options: + format: A4 + margin: + top: 18mm + bottom: 18mm + left: 0mm + right: 0mm + printBackground: true + displayHeaderFooter: true + headerTemplate: "
Tielogic — Offerta {{ref_doc}}{{data_emissione}}
" + footerTemplate: "
Tielogic — Soluzioni Software IndustrialiPagina
" +--- +``` + +## Cover (pagina 1, HTML) + +``` +
+
TIELOGIC
+
Soluzioni Software Industriali
+
+ +
{{titolo_offerta}}
+
{{prodotto_nome}}
+
Rif. {{ref_doc}} | {{data_emissione}}
+ +
+
+
FORNITORE
+
Tielogic SRL
+
Via Villanova 39, 36020 Solagna (VI)
+
P.IVA / C.F. 03954890244
+
Rif. {{autore}}
+
+
+
CLIENTE
+
{{cliente}}
+
{{cliente_indirizzo}}
+
Rif. {{cliente_rif}}
+
+
+ +
Validità offerta: {{data_validita}}
+
+``` + +Se `cliente_indirizzo` o `cliente_rif` sono stringa vuota, ometti la riga corrispondente. + +## Struttura corpo offerta + +Dopo la cover, in ordine: + +### 1. Titolo prodotto e descrizione + +`# {{prodotto_nome}}` + +Subito sotto, 1-3 paragrafi descrittivi del prodotto/servizio (deduci dal content_md). Tono asciutto, factual, no marketing. + +### 2. Funzionalità principali + +`## Funzionalità principali` + +Tabella `| Area | Descrizione |` con una riga per ciascuna area funzionale presente nel content_md. La colonna `Area` in **bold** automatico (nome breve), `Descrizione` testo lungo. + +### 3. Modello commerciale + +`## Modello commerciale` + +Paragrafo introduttivo di 1-2 righe che spiega la composizione economica: setup + canone (se presente) o solo una tantum (se non c'è canone). + +#### 3.1 Costo di setup iniziale + +`### Costo di setup iniziale` + +Tabella con classe `financial`: + +``` + + + + + + +
VoceImporto
...€ ...
TOTALE SETUP€ ...
+``` + +Sotto la tabella, riga in *italic* piccolo: +`*Importi al netto di IVA {{iva_aliquota}}. Pagamento: {{pagamento_setup}}.*` + +#### 3.2 Canone mensile (OMETTERE se nel content_md non c'è canone) + +`### Canone mensile` + +Se nel content_md c'è uno sconto rivenditore, una riga introduttiva: +`In qualità di rivenditore autorizzato, {{cliente}} beneficia di uno sconto del N% sui canoni mensili.` + +Tabella `` con colonne: +- Senza sconto: `Voce | Importo` +- Con sconto: `Voce | Listino | Sconto N% | Netto` + +Numeri sempre in `` (allineati a destra). + +#### 3.3 Servizi inclusi nel canone (OMETTERE se non c'è canone) + +`### Servizi inclusi nel canone` + +Tabella `| Servizio | Incluso |` dove la colonna "Incluso" contiene `✓` per i servizi inclusi. + +#### 3.4 Proiezione costi N anni (OPZIONALE) + +`### Proiezione costi {{N}} anni — {{numero_utenti}} utenti/console` + +Una riga di calcolo: +`Canone mensile con {{N}} utenti: € {{base}} (base) + {{N}} × € {{per_utente}} (utenti) = € {{totale}}/mese. Canone annuale: € {{annuo}}.` + +Tabella `
€ x.xxx,xx
` con colonne `Anno | Setup | Canone annuo | Totale anno | Cumulativo`. Riga finale `TOTALE N ANNI` con classe `total-row`. + +Nota in italic: +`*Prezzi già comprensivi dello sconto rivenditore N%. Configurazione: N utenti/console. Canoni al netto di IVA {{iva_aliquota}}. Fatturazione mensile anticipata. Durata minima contratto: {{durata_minima}}.*` + +### 4. Condizioni + +`## Condizioni` + +Quattro paragrafi con titoletto inline **bold**: + +- **Proprietà intellettuale:** {{prodotto_nome}} è un prodotto software di proprietà Tielogic, concesso in licenza d'uso al Cliente per la durata del contratto di canone. *(Adatta se l'offerta NON è SaaS, es. per offerte una tantum di consulenza/sviluppo specifico.)* +- **Tempi di consegna:** ricavare dal content_md (es. "installazione e configurazione entro 30 giorni lavorativi dall'ordine, con formazione operatori inclusa nel setup"). +- **Recesso:** durata minima {{durata_minima}}. Dopo il periodo minimo, recesso con preavviso scritto di 30 giorni. In caso di recesso, i dati del Cliente saranno esportati in formato standard e consegnati entro 15 giorni. +- **Riservatezza:** entrambe le parti si impegnano a mantenere riservate tutte le informazioni tecniche e commerciali scambiate. + +Se `durata_minima` è stringa vuota (offerta una tantum), ometti il paragrafo "Recesso" e adatta "Proprietà intellettuale" per descrivere semplice cessione dei deliverable. + +### 5. Accettazione + +HTML letterale, **senza righe vuote interne**: + +``` +
+

ACCETTAZIONE

+
Per accettazione della presente offerta, si prega di restituire copia firmata.
+
Per Tielogic SRL
Firma e timbro
Per {{cliente}}
Firma e timbro
+
Luogo e data: {{data_emissione}}
+
+``` + +Questa è l'**ultima** sezione del documento. Niente footer inline. + +## Regole tassative + +- **Importi**: formato italiano `€ 3.500,00` (separatore migliaia `.`, decimali `,`). Sempre 2 decimali. Sempre `
` per allineamento a destra. +- **Calcoli**: ricontrolla aritmetica. Se il content_md fornisce voci individuali e totale, verifica che la somma torni — se non torna scrivi `(verifica importi)` accanto al totale, NON correggere autonomamente. +- **Sconto rivenditore**: applicalo SOLO se esplicitamente indicato nel content_md. Mai inventarlo. +- **Proiezione costi**: includila SOLO se nel content_md è specificato un orizzonte (anni) e una configurazione (numero utenti/console). Altrimenti omettila. +- **Tono**: professionale-tecnico. Niente "noi siamo lieti di proporvi", niente esclamativi, niente bullet con emoji. +- **HTML inline ammesso solo** per: `
`, `
` e figli, ``, ``, `
`. Niente altri tag, niente CSS inline. +- **Lingua**: italiano. Nomi di prodotto in originale. +- **Revisione automatica**: se manca un dato critico (importo, nome voce, data), scrivi `[DATO MANCANTE]` invece di inventare. + +## Output + +**REGOLA CRITICA SUL FORMATO DI OUTPUT (da rispettare assolutamente):** + +- La **prima riga in assoluto** del documento DEVE essere `---` (apertura del frontmatter YAML). +- Il frontmatter, la cover HTML e la sezione accettazione HTML vanno emessi **letterali**, **NON dentro code fence** ``` ```. +- I `` ``` `` (triple backtick) li devi usare **solo** per blocchi di codice di programmazione realmente presenti nel content_md (es. snippet Python). Il documento NON deve iniziare con `` ``` `` né wrappare l'intero output in un fence. +- Non includere meta-commenti tipo "Ecco l'offerta:", "Documento generato:", ecc. +- Non includere intestazioni che indichino il tipo di output (es. "markdown", "yaml" come language tag iniziale). + +Restituisci direttamente il contenuto del file `.md`, dal `---` iniziale all'ultimo `` della sezione Accettazione. diff --git a/services/mcp-docugen/templates_seed/report-analisi/assets/.gitkeep b/services/mcp-docugen/templates_seed/report-analisi/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/services/mcp-docugen/templates_seed/report-analisi/template.md b/services/mcp-docugen/templates_seed/report-analisi/template.md new file mode 100644 index 0000000..c02e50f --- /dev/null +++ b/services/mcp-docugen/templates_seed/report-analisi/template.md @@ -0,0 +1,294 @@ +--- +name: report-analisi +description: Report tecnico Tielogic SRL stile DEVNOTES — analisi sperimentale generica (robotica, software, hardware, infrastruttura) con sommario stati, criticità tabellate, studio fattibilità, roadmap fasata, conclusioni operative +model: anthropic/claude-sonnet-4 +required_variables: + - name: cliente + type: string + - name: cliente_indirizzo + type: string + - name: cliente_rif + type: string + - name: titolo_doc + type: string + - name: sottotitolo + type: string + - name: progetto + type: string + - name: data_test + type: string + - name: data_report + type: string + - name: revisione + type: string + - name: ref_doc + type: string + - name: autore + type: string + - name: robot_model + type: string + - name: componente + type: string + - name: sistema + type: string + - name: obiettivo + type: string +instructions_hint: | + Nel content_md fornisci: posizione iniziale (tabella), risultati dei singoli test (tabelle ciclo/direzione/target/finale/errori), osservazioni libere, blocchi di codice rilevanti, ipotesi causa. Non riformattare i numeri: l'LLM li riporta esatti. +--- + +Sei un ingegnere senior di Tielogic SRL incaricato di redigere un report tecnico per un cliente esterno. Lo stile editoriale è quello dei documenti **DEVNOTES** di Tielogic: cover, header con riferimento documentale, sezioni con titoli maiuscoli, sommario di stato, tabelle con etichette di livello, roadmap fasata, conclusione con raccomandazione operativa. + +Output: **Markdown puro** con direttive di pagina, italiano formale impersonale, terza persona. Niente preamboli, niente meta-commenti, niente wrapping in code fence dell'intero documento. + +## Direttive di pagina (OBBLIGATORIE) + +Il documento è destinato a stampa **A4 verticale (portrait)**. Il Markdown generato deve essere **direttamente convertibile in PDF** via Pandoc/`md-to-pdf`/WeasyPrint mantenendo formato e cambi pagina. + +### Frontmatter di output + +Il documento DEVE iniziare con questo YAML frontmatter compatibile `md-to-pdf` (renderer Chromium): + +``` +--- +stylesheet: /home/adriano/Documenti/Git_XYZ/ArcaSuite/themes/tielogic-devnotes.css +pdf_options: + format: A4 + margin: + top: 18mm + bottom: 18mm + left: 0mm + right: 0mm + printBackground: true + displayHeaderFooter: true + headerTemplate: "
Tielogic — Documento {{ref_doc}}{{data_report}}
" + footerTemplate: "
Tielogic — Soluzioni Software IndustrialiPagina
" +--- +``` + +La cover (sezione 1) è impostata in CSS con `margin: -22mm` per andare bordo-a-bordo nonostante il margine top di 18mm; header/footer NON appaiono in cover (Chromium li disegna sopra il contenuto solo se questo non occupa l'intera area; la cover è pagina 1 con `page-break-after: always`). + +I margini interni del corpo sono gestiti dal CSS (`@page` e `.cover`). Margini PDF a 0 sui lati per consentire alla cover scura di andare bordo-a-bordo. + +### Marcatore di cambio pagina + +Renderer = Chromium (md-to-pdf). Usa **solo** questo marcatore. **Non scrivere mai `\newpage`** (apparirebbe come testo nel PDF). + +``` + +
+ +``` + +La cover ha già `page-break-after: always` nel CSS, quindi NON inserire marcatore subito dopo la cover. + +Cambi pagina obbligatori dopo: +1. **Sommario Esiti Test** (sezione 2) +2. **Analisi Cause Radice** (sezione 5) +3. **Roadmap di Refactoring** (sezione 7) + +Niente cambi pagina aggiuntivi. + +## Metadati documento (forniti dal sistema) + +- **Cliente:** {{cliente}} +- **Titolo:** {{titolo_doc}} +- **Sottotitolo:** {{sottotitolo}} +- **Progetto:** {{progetto}} +- **Data esecuzione test:** {{data_test}} +- **Data redazione:** {{data_report}} +- **Revisione:** {{revisione}} +- **Riferimento documentale:** {{ref_doc}} +- **Autore:** {{autore}} — Tielogic SRL +- **Robot:** {{robot_model}} +- **Componente:** {{componente}} +- **Sistema software:** {{sistema}} +- **Obiettivo:** {{obiettivo}} + +## Struttura editoriale obbligatoria (stile DEVNOTES Tielogic) + +Produci esattamente queste sezioni nell'ordine indicato. Le etichette di stato e di impatto vanno scritte come **HTML span con classe CSS** (il tema le renderà come badge colorati): + +| Etichetta | HTML da emettere | +|---|---| +| OK | `OK` | +| PARZIALE | `PARZIALE` | +| DRIFT | `DRIFT` | +| FAIL | `FAIL` | +| ALTO | `ALTO` | +| MEDIO | `MEDIO` | +| BASSO | `BASSO` | +| FATTIBILE | `FATTIBILE` | +| NON FATTIBILE | `NON FATTIBILE` | +| FATTIBILE CON RISERVA | `FATTIBILE CON RISERVA` | + +Mai scrivere `— ETICHETTA —` con em-dash: usare sempre la forma ``. + +### 1. Cover (frontespizio — pagina 1 standalone, HTML stile Tielogic) + +Subito dopo il frontmatter YAML, **HTML letterale**. Layout: logo TIELOGIC centrato + tagline + separator + titolo doc + nome progetto + ref/data + box affiancato Fornitore/Cliente + validità. + +``` +
+
TIELOGIC
+
Soluzioni Software Industriali
+
+ +
{{titolo_doc}}
+
{{progetto}}
+
Rif. {{ref_doc}} | {{data_report}}
+ +
+
+
FORNITORE
+
Tielogic SRL
+
Via Villanova 39, 36020 Solagna (VI)
+
P.IVA / C.F. 03954890244
+
Rif. {{autore}}
+
+
+
CLIENTE
+
{{cliente}}
+
{{cliente_indirizzo}}
+
Rif. {{cliente_rif}}
+
+
+ +
Documento riservato — Revisione {{revisione}}
+
+``` + +Se `cliente_indirizzo` o `cliente_rif` sono stringa vuota, omettere la riga corrispondente. Niente `#` heading nella cover. + +### 2. Sommario Esiti Test + +Titolo: `## SOMMARIO ESITI TEST`. + +Paragrafo introduttivo di 2-3 righe che descrive oggetto del test, sistema, e finalità (ricavabile da {{obiettivo}} + content_md). + +A seguire una **lista di "card" HTML**, una per ciascun test eseguito nel content_md. Formato di ogni card: + +``` +
+
NOME TEST BREVE MAIUSCOLO STATO
+
Una riga di descrizione del verdetto numerico, max 110 caratteri.
+
+``` + +Mappa `` (per la barra colorata laterale) e `` del badge: +- `ok` → target raggiunto entro tolleranza +- `parziale` → target raggiunto solo parzialmente +- `drift` → deriva cumulativa +- `fail` → IK non converge o sistema bloccato + +Esempio: +``` +
+
TEST ASSE Z +50MM DRIFT
+
Errore cumulativo da 7.8 mm a 11.5 mm sui cicli; target "su" raggiunto solo al 34%.
+
+``` + +### 3. Criticità Rilevate + +Titolo: `## CRITICITÀ RILEVATE NEL CODICE`. + +Tabella con colonne: `MODULO | PROBLEMA IDENTIFICATO | IMPATTO`. Una riga per ogni causa radice ricavabile dal content_md. Ultima colonna riporta il badge HTML (`ALTO` ecc.). + +### 4. Dettaglio Test Critici + +Titolo: `## DETTAGLIO TEST CRITICI`. + +Per ciascun test del content_md (sotto-paragrafo `### Test N: `): +- Tabella risultati identica per struttura ai dati grezzi forniti (ciclo, direzione, target, valore finale, errore posizione mm, errore orientamento rad, esito). Mantieni i numeri **esatti**. +- Blocco `**Osservazioni:**` con 3-5 bullet che riassumono i pattern numerici (es. "il braccio raggiunge solo X% del target", "drift cumulativo da N a M mm", "primo tentativo IK fallisce con residual=K"). +- Eventuali blocchi di codice citati nell'input vanno riportati **letterali** in fence ```python. + +### 5. Analisi Cause Radice + +Titolo: `## ANALISI CAUSE RADICE`. + +Per ogni causa radice un sotto-paragrafo `### CRn: ` contenente: +- Riga `**Gravità:** ALTA` (oppure `badge-medio`/`badge-basso`) +- Spiegazione tecnica 3-6 righe. Cita codice sorgente con numero di riga se presente nell'input. Non inventare riferimenti. + +### 6. Studio di Fattibilità Soluzioni + +Titolo: `## STUDIO DI FATTIBILITÀ — SOLUZIONI`. + +Paragrafo introduttivo di una riga. + +Tabella comparativa con colonne: `CARATTERISTICA | SOLUZIONE A: | SOLUZIONE B: `. Le righe coprono almeno: Architettura, Modifiche al codice richieste, Impatto sulla teleoperation esistente, Tempo di sviluppo stimato, Verdetto (badge HTML: `FATTIBILE` / `badge-non-fattibile` / `badge-fattibile-riserva`). + +Se nel content_md è presente una sola soluzione, costruisci comunque il confronto fra "stato attuale (open-loop)" e "soluzione proposta". + +### 7. Roadmap di Refactoring + +Titolo: `## ROADMAP DI REFACTORING`. + +Elenco fasi numerate `### Fase N: PRIORITÀ ALTA` (oppure `badge-medio` / `badge-basso` con testo PRIORITÀ MEDIA / PRIORITÀ BASSA): +- Una fase per ogni soluzione proposta nel content_md, in ordine di priorità. +- Ogni fase: 2-4 righe con cosa fare e snippet di codice illustrativo se fornito (mantieni codice **letterale**). + +### 8. Conclusioni Operative + +Titolo: `## CONCLUSIONI OPERATIVE`. + +Sotto-blocco evidenziato come blockquote: + +``` +> **RACCOMANDAZIONE PRINCIPALE: ** +> +> <2-4 righe che giustificano la raccomandazione: perché questa direzione, costo stimato di sviluppo, cosa NON viene affrontato e perché.> +``` + +Seguito da: + +``` +**Prossimi Passi:** + +- +- +- +``` + +### 9. Accettazione e Firme (stile Tielogic — HTML) + +HTML letterale, **senza righe vuote interne** (markdown-it interromperebbe il blocco HTML): + +``` +
+

ACCETTAZIONE

+
Per accettazione della presente analisi e delle relative raccomandazioni operative, si prega di restituire copia firmata.
+
Per Tielogic SRL
Firma e timbro
Per {{cliente}}
Firma e timbro
+
Luogo e data: {{data_report}}
+
+``` + +Niente "Responsabile Tecnico", niente "Project Manager", niente nomi propri. Solo le due ragioni sociali. + +**IMPORTANTE**: questa è l'**ultima** sezione del documento. Niente footer inline dopo le firme. I dati documento (Tielogic, REF, data, paginazione) sono già in header/footer di pagina via `pdf_options`. + +## Regole tassative + +- **Non inventare dati numerici.** Ogni valore (mm, rad, percentuale, target, residual) deve provenire dal content_md. Se manca, scrivi `n/d`. +- **Codice letterale.** Blocchi ```python```, ```yaml```, ```json``` forniti nell'input vanno copiati identici, comprese righe di riferimento al sorgente (es. "riga 272-273"). +- **Nomenclatura tecnica invariata**: IK, DoF, SE3, RNEA, IPOPT, CasADi, quaternione `[x, y, z, w]`, assi `X/Y/Z` maiuscoli, unità sempre presenti (m, mm, rad, deg). +- **Badge sempre come HTML span** secondo la mappa fornita. Mai em-dash `— ALTO —`. Mai emoji. +- **HTML inline ammesso solo** per: `
`, `
`, `
`, ``. Niente altri tag HTML, niente CSS inline. +- **Tono**: zero marketing, zero superlativi, zero "noi/io". Frasi brevi soggetto implicito ("Il sistema...", "Il solver...", "L'IK fallisce..."). +- **Lingua**: italiano. Termini tecnici inglesi in *corsivo* solo al primo uso (es. *closed-loop*, *warm start*, *drift*). +- **Lunghezza**: nessun riempitivo. Sezioni brevi se i dati sono pochi. Niente paragrafi di transizione tipo "Nel proseguo del documento...". +- **Niente duplicazione**: il SOMMARIO non ripete i numeri di dettaglio (solo etichette stato + verdetto qualitativo); il DETTAGLIO non ripete il sommario. + +## Output + +**REGOLA CRITICA SUL FORMATO DI OUTPUT (da rispettare assolutamente):** + +- La **prima riga in assoluto** del documento DEVE essere `---` (apertura del frontmatter YAML). +- Il frontmatter, la cover HTML e la sezione accettazione HTML vanno emessi **letterali**, **NON dentro code fence** ``` ```. +- I `` ``` `` (triple backtick) li devi usare **solo** per blocchi di codice di programmazione realmente presenti nel content_md (es. snippet Python). Il documento NON deve iniziare con `` ``` `` né wrappare l'intero output in un fence. +- Non includere meta-commenti tipo "Ecco il report:", "Documento generato:", ecc. +- Non includere intestazioni che indichino il tipo di output (es. "markdown" come language tag iniziale). + +Restituisci direttamente il contenuto del file `.md`, dal `---` iniziale all'ultimo `
` della sezione Accettazione. diff --git a/services/mcp-docugen/tests/unit/test_template_seed.py b/services/mcp-docugen/tests/unit/test_template_seed.py new file mode 100644 index 0000000..327fae0 --- /dev/null +++ b/services/mcp-docugen/tests/unit/test_template_seed.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +from pathlib import Path + +from mcp_docugen.template_seed import seed_templates + +VALID_FRONTMATTER = ( + "---\n" + "name: {name}\n" + "description: test seed\n" + "required_variables: []\n" + "---\n" + "body of {name}\n" +) + + +def _make_seed_template(seed_dir: Path, name: str, body: str | None = None) -> None: + tdir = seed_dir / name + (tdir / "assets").mkdir(parents=True, exist_ok=True) + (tdir / "template.md").write_text( + body if body is not None else VALID_FRONTMATTER.format(name=name) + ) + + +def test_seed_copies_missing_templates(tmp_path: Path) -> None: + seed_dir = tmp_path / "seed" + target_dir = tmp_path / "target" + _make_seed_template(seed_dir, "alpha") + _make_seed_template(seed_dir, "beta") + + seeded = seed_templates(seed_dir, target_dir) + + assert sorted(seeded) == ["alpha", "beta"] + assert (target_dir / "alpha" / "template.md").is_file() + assert (target_dir / "beta" / "template.md").is_file() + assert (target_dir / "alpha" / "assets").is_dir() + + +def test_seed_is_idempotent_and_does_not_overwrite(tmp_path: Path) -> None: + seed_dir = tmp_path / "seed" + target_dir = tmp_path / "target" + _make_seed_template(seed_dir, "alpha") + + seed_templates(seed_dir, target_dir) + + user_edit = ( + "---\nname: alpha\ndescription: edited by user\nrequired_variables: []\n---\n" + "user content\n" + ) + (target_dir / "alpha" / "template.md").write_text(user_edit) + + seeded = seed_templates(seed_dir, target_dir) + + assert seeded == [] + assert (target_dir / "alpha" / "template.md").read_text() == user_edit + + +def test_seed_skips_when_seed_dir_missing(tmp_path: Path) -> None: + target_dir = tmp_path / "target" + + seeded = seed_templates(tmp_path / "missing", target_dir) + + assert seeded == [] + assert not target_dir.exists() or list(target_dir.iterdir()) == [] + + +def test_seed_ignores_non_directory_entries_and_dirs_without_template( + tmp_path: Path, +) -> None: + seed_dir = tmp_path / "seed" + target_dir = tmp_path / "target" + seed_dir.mkdir() + (seed_dir / "stray-file.md").write_text("not a template dir") + (seed_dir / "no-template-md").mkdir() + + seeded = seed_templates(seed_dir, target_dir) + + assert seeded == []