laiss-docs 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of laiss-docs might be problematic. Click here for more details.
- laiss_docs-1.1.0.dist-info/METADATA +594 -0
- laiss_docs-1.1.0.dist-info/RECORD +33 -0
- laiss_docs-1.1.0.dist-info/WHEEL +5 -0
- laiss_docs-1.1.0.dist-info/entry_points.txt +2 -0
- laiss_docs-1.1.0.dist-info/licenses/LICENSE +21 -0
- laiss_docs-1.1.0.dist-info/top_level.txt +1 -0
- laissdocs/__init__.py +7 -0
- laissdocs/__main__.py +8 -0
- laissdocs/ai.py +318 -0
- laissdocs/assets/fonts/Inter-Bold.otf +0 -0
- laissdocs/assets/fonts/Inter-BoldItalic.otf +0 -0
- laissdocs/assets/fonts/Inter-Italic.otf +0 -0
- laissdocs/assets/fonts/Inter-Regular.otf +0 -0
- laissdocs/assets/fonts/JetBrainsMonoNerdFont-Bold.ttf +0 -0
- laissdocs/assets/fonts/JetBrainsMonoNerdFont-Italic.ttf +0 -0
- laissdocs/assets/fonts/JetBrainsMonoNerdFont-Regular.ttf +0 -0
- laissdocs/assets/fonts/LICENSE.txt +11 -0
- laissdocs/assets/mark.pdf +0 -0
- laissdocs/build.py +520 -0
- laissdocs/cli.py +1092 -0
- laissdocs/config.py +453 -0
- laissdocs/content.py +796 -0
- laissdocs/diagrams.py +324 -0
- laissdocs/document.py +551 -0
- laissdocs/guide.py +196 -0
- laissdocs/install.py +524 -0
- laissdocs/locales.py +201 -0
- laissdocs/mcp.py +771 -0
- laissdocs/metrics.py +193 -0
- laissdocs/paths.py +119 -0
- laissdocs/py.typed +0 -0
- laissdocs/tex/cover.tex +78 -0
- laissdocs/tex/laissdoc.sty +517 -0
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: laiss-docs
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Documentos Laiss Labs em A4, a partir de JSON. Sistema de design, motor e CLI.
|
|
5
|
+
Author-email: Júlio Papel <julio@laisslabs.com>
|
|
6
|
+
Maintainer: Laiss Labs
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/JulioPapel/laiss-docs
|
|
9
|
+
Project-URL: Documentation, https://github.com/JulioPapel/laiss-docs/tree/main/docs
|
|
10
|
+
Project-URL: Issues, https://github.com/JulioPapel/laiss-docs/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/JulioPapel/laiss-docs/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: latex,lualatex,pdf,documentation,mcp,design-system
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Operating System :: MacOS
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Topic :: Documentation
|
|
27
|
+
Classifier: Topic :: Text Processing :: Markup :: LaTeX
|
|
28
|
+
Classifier: Typing :: Typed
|
|
29
|
+
Requires-Python: >=3.9
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
34
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# laiss-docs
|
|
38
|
+
|
|
39
|
+
Documentos da Laiss Labs em A4, a partir de JSON. O sistema de design do guia
|
|
40
|
+
de contribuição, extraído para um pacote que se instala em qualquer máquina e
|
|
41
|
+
que um agente AI pode usar por MCP.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install laiss-docs # instala o programa (5 MB)
|
|
45
|
+
laiss-docs build handbook # a primeira vez instala o que falta
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
laiss-docs doctor # o que falta nesta máquina
|
|
50
|
+
laiss-docs deps --dry-run # o que falta, e como se instala
|
|
51
|
+
laiss-docs new handbook --lang en-US # criar
|
|
52
|
+
laiss-docs build handbook # compilar e verificar
|
|
53
|
+
laiss-docs preview handbook # abrir
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Saída: `handbook/out/handbook.pdf`.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## O que isto é
|
|
61
|
+
|
|
62
|
+
Um conjunto de três peças que não se confundem:
|
|
63
|
+
|
|
64
|
+
| Peça | O que é | Onde |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| **Design** | `laissdoc.sty` + `cover.tex`. Tipografia, paleta, caixas, cabeçalhos. | `laissdocs/tex/` |
|
|
67
|
+
| **Motor** | `config.py`. Traduz `doc.json` em LaTeX. Escapa tudo. | `laissdocs/` |
|
|
68
|
+
| **Interface** | CLI, e um servidor MCP para agentes. | `laissdocs/cli.py`, `mcp.py` |
|
|
69
|
+
|
|
70
|
+
A regra: **o design não se edita num documento.** Se um documento precisa de um
|
|
71
|
+
aspecto diferente, ou é um parâmetro do `doc.json`, ou é uma alteração ao `.sty`
|
|
72
|
+
que beneficia todos. Um documento que foge ao sistema perde a razão de existir.
|
|
73
|
+
|
|
74
|
+
## Instalação
|
|
75
|
+
|
|
76
|
+
### Com pip
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install laiss-docs
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
A primeira vez que correres um comando que precisa de compilar, o programa
|
|
83
|
+
verifica o que falta na máquina e instala o que souber instalar. Pergunta
|
|
84
|
+
antes de mexer no sistema, e diz o que vai fazer:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
$ laiss-docs build relatorio
|
|
88
|
+
Faltam dependencias do sistema para compilar.
|
|
89
|
+
|
|
90
|
+
✗ LuaLaTeX em falta
|
|
91
|
+
e o motor de composicao; sem ele nao ha PDF
|
|
92
|
+
$ sudo apt-get install -y texlive-latex-extra texlive-fonts-extra
|
|
93
|
+
(pede a password de root)
|
|
94
|
+
✗ Poppler em falta
|
|
95
|
+
falta pdftotext, pdfinfo
|
|
96
|
+
$ sudo apt-get install -y poppler-utils
|
|
97
|
+
(pede a password de root)
|
|
98
|
+
|
|
99
|
+
A instalar automaticamente. Isto corre uma vez.
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Antes de instalar, vê o plano:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
laiss-docs deps --dry-run # o que falta e o que seria corrido
|
|
106
|
+
laiss-docs deps # instalar, com confirmação
|
|
107
|
+
laiss-docs deps -y # instalar sem perguntar (CI)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Em CI, ou num sítio sem terminal, **nada é instalado** sem
|
|
111
|
+
`LAISSDOCS_AUTO_INSTALL=1`. Um build não deve começar a escrever no sistema
|
|
112
|
+
porque lhe apeteceu.
|
|
113
|
+
|
|
114
|
+
O que o pip **não** instala, e porquê: o LuaLaTeX, o poppler, o Graphviz e o
|
|
115
|
+
mermaid-cli não são pacotes Python. Um pacote Python que tentasse instalá-los
|
|
116
|
+
seria um pacote que corre `sudo` às escondidas. O `deps` faz isso à vista.
|
|
117
|
+
|
|
118
|
+
### Requisitos
|
|
119
|
+
|
|
120
|
+
- **Python 3.9+** — sem dependências externas, só a biblioteca padrão.
|
|
121
|
+
- **LuaLaTeX** — TeX Live, MacTeX ou MiKTeX. *Não* é opcional, e *não* pode
|
|
122
|
+
ser substituído por Tectonic (ver [Porquê LuaLaTeX](docs/porque-lualatex.md)).
|
|
123
|
+
- **poppler-utils** (`pdftotext`, `pdfinfo`) — para as verificações.
|
|
124
|
+
|
|
125
|
+
O `laiss-docs deps` instala estes três em Debian, Ubuntu, Arch, Fedora,
|
|
126
|
+
openSUSE, Alpine e macOS. No NixOS diz o que pôr no `shell.nix`, porque
|
|
127
|
+
instalar imperativamente numa máquina Nix é a coisa errada a fazer.
|
|
128
|
+
|
|
129
|
+
### Sem pip
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
git clone <este-repo> && cd laiss-docs
|
|
133
|
+
./bin/laiss-docs doctor
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Funciona a partir do checkout, sem instalar nada.
|
|
137
|
+
|
|
138
|
+
### O que o `doctor` verifica
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
laiss-docs doctor
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
laiss-docs 1.1.0
|
|
146
|
+
instalado em /home/juliopapel/.local/share/laiss-docs
|
|
147
|
+
|
|
148
|
+
✓ python3 3.15.2
|
|
149
|
+
✓ lualatex This is LuaHBTeX, Version 1.25.7
|
|
150
|
+
✓ pdftotext
|
|
151
|
+
✓ pdfinfo
|
|
152
|
+
✓ Inter
|
|
153
|
+
✓ JetBrains Mono
|
|
154
|
+
✓ pacotes LaTeX todos os ficheiros do estilo
|
|
155
|
+
✓ idiomas LaTeX 5 idiomas
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
A verificação dos pacotes pergunta ao `kpsewhich`, que é o mesmo resolvedor
|
|
159
|
+
que o `lualatex` usa: se ele não encontra um ficheiro, a compilação também
|
|
160
|
+
não. Custa 0,1 s, em vez dos segundos de uma sonda compilada. A sonda
|
|
161
|
+
continua a existir para as máquinas sem `kpsewhich`.
|
|
162
|
+
|
|
163
|
+
Se `doctor` disser que faltam as fontes, corre `laiss-docs init`. As fontes
|
|
164
|
+
vão dentro do pacote (~10 MB), por isso funciona sem rede.
|
|
165
|
+
|
|
166
|
+
## Uso
|
|
167
|
+
|
|
168
|
+
### Criar um documento
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
laiss-docs new relatorio-tecnico \
|
|
172
|
+
--title "Relatório Técnico" \
|
|
173
|
+
--subtitle "Estado do motor de CMS em Novembro de 2026" \
|
|
174
|
+
--lang pt-PT
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Cria:
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
relatorio-tecnico/
|
|
181
|
+
├── doc.json metadados e estrutura
|
|
182
|
+
├── src/
|
|
183
|
+
│ ├── main.tex gerado
|
|
184
|
+
│ ├── cover.tex copiado da instalação
|
|
185
|
+
│ ├── laissdoc.sty symlink para a instalação
|
|
186
|
+
│ ├── laissconfig.tex gerado a partir do doc.json
|
|
187
|
+
│ ├── part01.tex ← escreves aqui
|
|
188
|
+
│ └── colophon.tex
|
|
189
|
+
└── out/ o PDF
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Escreves em `part*.tex` e em `doc.json`. Nunca em `main.tex`,
|
|
193
|
+
`laissconfig.tex` ou `laissdoc.sty`** — são gerados, e o `build` escreve por
|
|
194
|
+
cima.
|
|
195
|
+
|
|
196
|
+
### Compilar
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
laiss-docs build relatorio-tecnico
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
✓ pages 12 paginas
|
|
204
|
+
✓ a4 A4
|
|
205
|
+
✓ text-layer sem glifos colados
|
|
206
|
+
✓ refs todas as referencias resolvidas
|
|
207
|
+
✓ toc 47 entradas conferidas
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Se alguma verificação falhar, o comando sai com código 1. É isso que o torna
|
|
211
|
+
utilizável em CI.
|
|
212
|
+
|
|
213
|
+
### Verificações, e porque existem
|
|
214
|
+
|
|
215
|
+
| Verificação | O que apanha | Porque existe |
|
|
216
|
+
|---|---|---|
|
|
217
|
+
| `pages` | PDF vazio ou truncado | Um `\input` em falta produz um PDF de 1 página sem aviso |
|
|
218
|
+
| `a4` | Formato errado | Um `\documentclass` errado passa despercebido |
|
|
219
|
+
| `text-layer` | Glifos colados | As ligaduras do JetBrains Mono transformam `https://` em `https:^//` no texto copiado |
|
|
220
|
+
| `refs` | `??` no PDF | Uma referência por resolver compila sem erro |
|
|
221
|
+
| `toc` | Índice desalinhado | Entradas com número de página errado parecem bem até se tentar navegar |
|
|
222
|
+
|
|
223
|
+
A verificação `text-layer` é a mais importante e a menos óbvia. Um PDF pode
|
|
224
|
+
parecer perfeito no ecrã e ter comandos corrompidos quando alguém os copia. Num
|
|
225
|
+
documento técnico, isso é o defeito mais caro possível.
|
|
226
|
+
|
|
227
|
+
### Uma excepção declarada: `allowTextLayer`
|
|
228
|
+
|
|
229
|
+
Há um caso em que um circunflexo no texto extraído é intencional: um documento
|
|
230
|
+
que **documente o próprio bug das ligaduras**. Nesse caso, declara o trecho:
|
|
231
|
+
|
|
232
|
+
```json
|
|
233
|
+
{ "allowTextLayer": ["https:^//github.com"] }
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
A verificação continua a correr para tudo o resto. Sem esta opção, a única
|
|
237
|
+
alternativa seria desligar a verificação — e uma verificação que se desliga
|
|
238
|
+
quando incomoda não serve para nada.
|
|
239
|
+
|
|
240
|
+
### Títulos e o colofão
|
|
241
|
+
|
|
242
|
+
Os títulos **não são hifenizados**. O CLI mede a largura real de cada palavra
|
|
243
|
+
com a fonte que vai ser usada (Inter Bold a 40pt na capa) e parte o título só
|
|
244
|
+
onde cabe — nunca a meio de uma palavra. Contar caracteres não servia: um
|
|
245
|
+
título de 22 caracteres pode medir 489pt numa área de 461pt.
|
|
246
|
+
|
|
247
|
+
O colofão final está **desligado por omissão**: o documento termina na última
|
|
248
|
+
secção. Para o incluir, `"colophon": true` no `doc.json`.
|
|
249
|
+
|
|
250
|
+
## Resumo
|
|
251
|
+
|
|
252
|
+
Página entre o índice e o capítulo 1, sem numeração, com entrada no índice.
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{"abstract": [{"type": "p", "text": "O que este documento trata."}],
|
|
256
|
+
"abstractTitle": "Resumo"}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Ou por MCP: `laiss_set_abstract`, `laiss_get_abstract`, `laiss_clear_abstract`.
|
|
260
|
+
|
|
261
|
+
Acrescentar um resumo **não toca nas secções**: o texto já escrito fica
|
|
262
|
+
exactamente como estava. Um resumo é um campo do documento, não uma secção, e
|
|
263
|
+
não entra na numeração dos capítulos.
|
|
264
|
+
|
|
265
|
+
## Figuras, diagramas e anexos
|
|
266
|
+
|
|
267
|
+
Três formas de figura, todas com `caption` obrigatória:
|
|
268
|
+
|
|
269
|
+
```json
|
|
270
|
+
{"type": "mermaid", "caption": "Pipeline", "source": "flowchart LR\n A[Texto] --> B[IDs]"}
|
|
271
|
+
{"type": "graphviz", "caption": "Dependências", "rankdir": "LR", "source": "A -> B;"}
|
|
272
|
+
{"type": "chart", "caption": "Tokens por frase", "labels": ["PT","EN"], "values": [77,62]}
|
|
273
|
+
{"type": "figure", "path": "imagens/painel.png", "caption": "O painel"}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
- **`mermaid`** — fluxogramas, sequências, estados, gantt. Precisa de
|
|
277
|
+
`@mermaid-js/mermaid-cli` e de um browser headless.
|
|
278
|
+
- **`graphviz`** — grafos grandes, vectorial. Precisa de `dot`.
|
|
279
|
+
- **`chart`** — barras horizontais em TikZ, a partir de números. **Não precisa
|
|
280
|
+
de nada externo** e funciona sempre.
|
|
281
|
+
- **`figure`** — SVG, PNG, JPEG ou PDF que já exista.
|
|
282
|
+
|
|
283
|
+
`laiss-docs mcp` expõe `laiss_diagrams`, que diz o que está instalado antes de
|
|
284
|
+
o agente escrever um bloco que não vai compilar.
|
|
285
|
+
|
|
286
|
+
O índice de figuras é gerado automaticamente quando há figuras
|
|
287
|
+
(`"listOfFigures": false` desliga).
|
|
288
|
+
|
|
289
|
+
### Anexos
|
|
290
|
+
|
|
291
|
+
Numerados por letra: ANEXO A, B. **A lista de referências não é um anexo**:
|
|
292
|
+
sai como página "Referências", sem letra, e não gasta a letra A. Um anexo pode
|
|
293
|
+
conter qualquer bloco.
|
|
294
|
+
|
|
295
|
+
```json
|
|
296
|
+
{"title": "Referências", "blocks": [
|
|
297
|
+
{"type": "refs", "items": [
|
|
298
|
+
{"text": "Sennrich, R. (2016). Neural Machine Translation…",
|
|
299
|
+
"url": "https://arxiv.org/abs/1508.07909",
|
|
300
|
+
"note": "A origem do BPE."}
|
|
301
|
+
]}
|
|
302
|
+
]}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### O travessão
|
|
306
|
+
|
|
307
|
+
**Não uses o travessão longo entre frases.** Modelos de linguagem fazem-no por
|
|
308
|
+
hábito. O motor substitui por vírgula, mas o resultado automático é sempre pior
|
|
309
|
+
do que uma frase escrita de raiz. Ver [escrever.md](escrever.md).
|
|
310
|
+
|
|
311
|
+
## Idiomas
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
laiss-docs locales
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
| Código | Idioma | Aliases |
|
|
318
|
+
|---|---|---|
|
|
319
|
+
| `pt-PT` | Português (Portugal) | `pt`, `pt_BR`, `portuguese` |
|
|
320
|
+
| `en-US` | English (US) | `en`, `english` |
|
|
321
|
+
| `es-ES` | Español (España) | `es`, `spanish` |
|
|
322
|
+
| `fr-FR` | Français (France) | `fr`, `french` |
|
|
323
|
+
| `de-DE` | Deutsch (Deutschland) | `de`, `german` |
|
|
324
|
+
|
|
325
|
+
O idioma muda: a hifenização, a data por extenso (`5 de Novembro de 2026` /
|
|
326
|
+
`November 5, 2026` / `5 novembre 2026`), o título do índice, as etiquetas da
|
|
327
|
+
capa (`DE`/`PARA` → `FROM`/`TO` → `VON`/`AN`) e os títulos por omissão das
|
|
328
|
+
caixas (`Nota`/`Atenção` → `Hinweis`/`Achtung`).
|
|
329
|
+
|
|
330
|
+
**O que o idioma não muda:** o design. Todos os documentos têm a mesma
|
|
331
|
+
tipografia, a mesma paleta, a mesma grelha. É esse o ponto.
|
|
332
|
+
|
|
333
|
+
### Acrescentar um idioma
|
|
334
|
+
|
|
335
|
+
Em `laissdocs/locales.py`, copia um bloco de `LOCALES`, traduz os valores, e
|
|
336
|
+
acrescenta os acentos em `ACCENTS`. O teste `test_all_have_required_keys` diz-te
|
|
337
|
+
se faltou alguma chave.
|
|
338
|
+
|
|
339
|
+
## Escrever o corpo
|
|
340
|
+
|
|
341
|
+
Ver [escrever.md](escrever.md) para a referência completa.
|
|
342
|
+
|
|
343
|
+
```latex
|
|
344
|
+
\partlabel{PARTE 1}
|
|
345
|
+
\section{O ciclo de trabalho}
|
|
346
|
+
|
|
347
|
+
Texto corrido. Uma interface: \ui{Botões} \uiarrow\ \ui{Definições}.
|
|
348
|
+
Um caminho: \fpath{src/main.tex}. Código inline: \code{git push}.
|
|
349
|
+
|
|
350
|
+
\begin{nota}[O que isto significa]
|
|
351
|
+
Caixa azul, informação.
|
|
352
|
+
\end{nota}
|
|
353
|
+
|
|
354
|
+
\begin{atencao}[Isto parte coisas]
|
|
355
|
+
Caixa laranja, risco.
|
|
356
|
+
\end{atencao}
|
|
357
|
+
|
|
358
|
+
\begin{cmds}[terminal]
|
|
359
|
+
git status
|
|
360
|
+
git diff
|
|
361
|
+
\end{cmds}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Agentes AI, por MCP
|
|
365
|
+
|
|
366
|
+
O servidor expõe 22 ferramentas desenhadas para um agente que não conhece o
|
|
367
|
+
sistema. A primeira que deve chamar é `laiss_guide`: explica o ciclo, todos os
|
|
368
|
+
tipos de bloco, a marcação permitida e os erros comuns.
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
laiss-docs mcp # JSON-RPC sobre stdio
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
No Cursor (`.cursor/mcp.json`):
|
|
375
|
+
|
|
376
|
+
```json
|
|
377
|
+
{
|
|
378
|
+
"mcpServers": {
|
|
379
|
+
"laiss-docs": {
|
|
380
|
+
"command": "laiss-docs",
|
|
381
|
+
"args": ["mcp"]
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
| Ferramenta | Para quê |
|
|
388
|
+
|---|---|
|
|
389
|
+
| `laiss_guide` | **Chamar primeiro.** O processo inteiro, em texto |
|
|
390
|
+
| `laiss_create` | Cria o documento e devolve o caminho |
|
|
391
|
+
| `laiss_outline` | Índice: ids, títulos, contagem de blocos |
|
|
392
|
+
| `laiss_read_section` | Conteúdo de uma secção, bloco a bloco |
|
|
393
|
+
| `laiss_add_section` | Nova secção, opcionalmente já com blocos |
|
|
394
|
+
| `laiss_add_blocks` | Acrescenta blocos a uma secção |
|
|
395
|
+
| `laiss_update_block` | Substitui um bloco pelo número |
|
|
396
|
+
| `laiss_remove_block` | Remove um bloco |
|
|
397
|
+
| `laiss_remove_section` | Remove uma secção |
|
|
398
|
+
| `laiss_update_section` | Muda título ou etiqueta |
|
|
399
|
+
| `laiss_move_section` | Reordena |
|
|
400
|
+
| `laiss_set_abstract` | Escreve o resumo (não toca nas secções) |
|
|
401
|
+
| `laiss_get_abstract` | Lê o resumo |
|
|
402
|
+
| `laiss_clear_abstract` | Remove o resumo |
|
|
403
|
+
| `laiss_add_appendix` | Novo anexo, com etiqueta por letra |
|
|
404
|
+
| `laiss_add_appendix_blocks` | Acrescenta blocos a um anexo |
|
|
405
|
+
| `laiss_remove_appendix` | Remove um anexo |
|
|
406
|
+
| `laiss_build` | Compila e verifica |
|
|
407
|
+
| `laiss_check` | Verifica sem recompilar |
|
|
408
|
+
| `laiss_schema` | JSON Schema do `doc.json` |
|
|
409
|
+
| `laiss_locales` | Idiomas |
|
|
410
|
+
| `laiss_diagrams` | O que está instalado para diagramas |
|
|
411
|
+
|
|
412
|
+
### A decisão que faz isto funcionar
|
|
413
|
+
|
|
414
|
+
**O agente não escreve LaTeX. Escreve JSON.**
|
|
415
|
+
|
|
416
|
+
Um modelo de linguagem não sabe que um `_` dentro de `path{}` parte o
|
|
417
|
+
compilador. Num documento, três builds falharam por isso — e o erro aparecia
|
|
418
|
+
longe da causa. Com blocos JSON, o modelo escolhe a estrutura e o texto; o motor
|
|
419
|
+
compõe. O formato deixa de ser responsabilidade de quem escreve.
|
|
420
|
+
|
|
421
|
+
```json
|
|
422
|
+
{"type": "p", "text": "Um parágrafo com **forte** e `código`."}
|
|
423
|
+
{"type": "warn", "title": "Cuidado", "text": "Isto apaga trabalho."}
|
|
424
|
+
{"type": "code", "label": "terminal", "text": "git status"}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
Ver [escrever.md](escrever.md) para a referência, e `laiss_guide` para a versão
|
|
428
|
+
que o agente lê.
|
|
429
|
+
|
|
430
|
+
## IA remota por endpoint
|
|
431
|
+
|
|
432
|
+
Gera o conteúdo com um modelo, por endpoint compatível com a API de chat
|
|
433
|
+
completions (OpenAI, Ollama, LM Studio, vLLM, llama.cpp).
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
export LAISSDOCS_BASE_URL="http://localhost:1234/v1" # LM Studio
|
|
437
|
+
export LAISSDOCS_MODEL="laiss-tva-26-1-qat"
|
|
438
|
+
export LAISSDOCS_API_KEY="sk-lm-..." # se o servidor exigir
|
|
439
|
+
|
|
440
|
+
laiss-docs ai # testa a ligação
|
|
441
|
+
laiss-docs new estudo --brief "O que quero estudar" --sections 4
|
|
442
|
+
laiss-docs generate estudo --section introducao --instruction "acrescenta um exemplo"
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Precedência da configuração: **argumento > ambiente > `doc.json` > omissão**.
|
|
446
|
+
O ambiente ganha ao `doc.json` para poderes comparar modelos sem editar o
|
|
447
|
+
ficheiro.
|
|
448
|
+
|
|
449
|
+
O que o modelo devolve é validado antes de entrar no documento. Se um bloco vier
|
|
450
|
+
mal formado, o erro do validador volta para a conversa e o modelo corrige — até
|
|
451
|
+
três tentativas. Um erro que o validador não explica é um erro que o utilizador
|
|
452
|
+
tem de resolver à mão.
|
|
453
|
+
|
|
454
|
+
## Estrutura do repositório
|
|
455
|
+
|
|
456
|
+
As quinze peças, por camada. A regra é que cada uma responde a uma pergunta,
|
|
457
|
+
e nenhuma sabe da camada de cima.
|
|
458
|
+
|
|
459
|
+
```
|
|
460
|
+
laissdocs/
|
|
461
|
+
├── tex/ o DESIGN
|
|
462
|
+
│ ├── laissdoc.sty o estilo inteiro: tipografia, paleta, caixas
|
|
463
|
+
│ └── cover.tex a capa
|
|
464
|
+
├── assets/ os RECURSOS
|
|
465
|
+
│ ├── fonts/ Inter + JetBrains Mono (embutidas, ~10 MB)
|
|
466
|
+
│ └── mark.pdf a marca
|
|
467
|
+
│
|
|
468
|
+
├── content.py o CONTEÚDO
|
|
469
|
+
│ blocos JSON → LaTeX. Escapa tudo. Valida antes
|
|
470
|
+
│ de compor: um bloco mal formado falha aqui, com
|
|
471
|
+
│ o caminho até ele, e não a meio do LaTeX.
|
|
472
|
+
├── locales.py os IDIOMAS
|
|
473
|
+
│ nomes de meses, etiquetas da capa, título do
|
|
474
|
+
│ índice, pacote babel de cada língua.
|
|
475
|
+
├── metrics.py as MEDIDAS
|
|
476
|
+
│ largura real de um texto na fonte final. É o
|
|
477
|
+
│ que impede um título de partir a meio.
|
|
478
|
+
│
|
|
479
|
+
├── config.py a PONTE
|
|
480
|
+
│ doc.json → laissconfig.tex. É a fronteira entre
|
|
481
|
+
│ o que se escreve e o que se compõe.
|
|
482
|
+
├── document.py a ESTRUTURA
|
|
483
|
+
│ editar o documento por operações (acrescentar,
|
|
484
|
+
│ mover, remover) em vez de reescrever o ficheiro.
|
|
485
|
+
├── diagrams.py as FIGURAS
|
|
486
|
+
│ Mermaid, Graphviz e imagens. Cache por conteúdo.
|
|
487
|
+
│ Sabe o que está instalado antes de prometer.
|
|
488
|
+
│
|
|
489
|
+
├── build.py a COMPILAÇÃO
|
|
490
|
+
│ LuaLaTeX três vezes, e cinco verificações no PDF
|
|
491
|
+
│ já produzido, não só no código de saída.
|
|
492
|
+
├── install.py as DEPENDÊNCIAS
|
|
493
|
+
│ o que falta no sistema, como se instala nesta
|
|
494
|
+
│ distribuição, e a diferença entre saber e não
|
|
495
|
+
│ saber (devolve `None`, nunca uma lista vazia).
|
|
496
|
+
├── paths.py os CAMINHOS
|
|
497
|
+
│ onde vivem as fontes e os estilos. Uma correcção
|
|
498
|
+
│ ao design chega a todos os documentos.
|
|
499
|
+
│
|
|
500
|
+
├── cli.py a INTERFACE
|
|
501
|
+
│ quinze comandos. Todos aceitam --json.
|
|
502
|
+
├── mcp.py a INTERFACE PARA AGENTES
|
|
503
|
+
│ vinte e duas ferramentas MCP sobre stdio, escritas à
|
|
504
|
+
│ mão em JSON-RPC 2.0.
|
|
505
|
+
├── guide.py o GUIA
|
|
506
|
+
│ o texto que o agente lê primeiro: o processo
|
|
507
|
+
│ inteiro, sem assumir LaTeX.
|
|
508
|
+
├── ai.py a IA REMOTA
|
|
509
|
+
│ cliente de chat completions em urllib. Pede
|
|
510
|
+
│ blocos JSON, não prosa.
|
|
511
|
+
└── __main__.py `python -m laissdocs`
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### As camadas, e porque estão separadas
|
|
515
|
+
|
|
516
|
+
| Camada | Pergunta que responde | Módulos |
|
|
517
|
+
|---|---|---|
|
|
518
|
+
| **Design** | Como é que isto se parece? | `tex/`, `assets/` |
|
|
519
|
+
| **Conteúdo** | O que é que isto diz? | `content.py`, `locales.py`, `metrics.py` |
|
|
520
|
+
| **Estrutura** | Como é que isto se organiza? | `document.py`, `config.py`, `diagrams.py` |
|
|
521
|
+
| **Motor** | Como é que isto se compõe? | `build.py`, `install.py`, `paths.py` |
|
|
522
|
+
| **Interface** | Como é que alguém fala com isto? | `cli.py`, `mcp.py`, `guide.py`, `ai.py` |
|
|
523
|
+
|
|
524
|
+
A separação não é decorativa: é o que permite que o **conteúdo** (JSON) não
|
|
525
|
+
saiba que existe LaTeX, e que o **design** não saiba que documentos existem.
|
|
526
|
+
Um documento novo não toca em nenhuma das duas.
|
|
527
|
+
|
|
528
|
+
## Desenvolvimento
|
|
529
|
+
|
|
530
|
+
```bash
|
|
531
|
+
python3 -m unittest discover -s tests -v
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
163 testes, cerca de 16 segundos, quase todos em compilações reais. Os que
|
|
535
|
+
precisam de LuaLaTeX saltam se ele não estiver instalado.
|
|
536
|
+
|
|
537
|
+
| Ficheiro | Testes | O que protege |
|
|
538
|
+
|---|---|---|
|
|
539
|
+
| `test_content.py` | 135 | Blocos, escaping, legendas, figuras, instalação |
|
|
540
|
+
| `test_laissdocs.py` | 29 | Compilação, verificações, MCP, idiomas |
|
|
541
|
+
| `test_install.py` | 28 | Plano de dependências, comandos por distro, sudo, marcador |
|
|
542
|
+
|
|
543
|
+
Depois de mudar o `.sty` ou a capa:
|
|
544
|
+
|
|
545
|
+
```bash
|
|
546
|
+
laiss-docs init # instala a nova versão
|
|
547
|
+
laiss-docs build <doc> # ressincroniza o documento e compila
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### Publicar uma versão
|
|
551
|
+
|
|
552
|
+
A versão tem uma fonte só: `laissdocs/__init__.py`. O `pyproject.toml` lê-a
|
|
553
|
+
de lá. Duas cópias divergem sempre, e já divergiram: o pacote saiu como 1.1.0
|
|
554
|
+
e o CLI continuou a dizer 1.0.0.
|
|
555
|
+
|
|
556
|
+
```bash
|
|
557
|
+
# 1. subir a versão
|
|
558
|
+
$EDITOR laissdocs/__init__.py
|
|
559
|
+
|
|
560
|
+
# 2. verificar o que sai
|
|
561
|
+
python -m build
|
|
562
|
+
twine check dist/*
|
|
563
|
+
|
|
564
|
+
# 3. provar num ambiente limpo antes de publicar
|
|
565
|
+
python -m venv /tmp/prova
|
|
566
|
+
/tmp/prova/bin/pip install dist/*.whl
|
|
567
|
+
/tmp/prova/bin/laiss-docs doctor
|
|
568
|
+
|
|
569
|
+
# 4. publicar
|
|
570
|
+
twine upload dist/*
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
A prova em ambiente limpo não é opcional. É a única forma de saber que o
|
|
574
|
+
pacote está completo: um ficheiro de dados que fica fora do wheel não aparece
|
|
575
|
+
no `twine check`, e só se descobre quando alguém instala e o comando falha
|
|
576
|
+
com um ficheiro de estilo em falta.
|
|
577
|
+
|
|
578
|
+
## Limites conhecidos
|
|
579
|
+
|
|
580
|
+
- **Só A4 retrato.** Paisagem e formatos maiores não estão previstos. Um
|
|
581
|
+
documento que precise disso é outro sistema.
|
|
582
|
+
- **O corpo é LaTeX, não Markdown.** Deliberado: quem escreve precisa de caixas,
|
|
583
|
+
de diagramas e de blocos de código com etiqueta. Markdown não chega, e o
|
|
584
|
+
conversor para LaTeX é onde os sistemas destes perdem a tipografia.
|
|
585
|
+
- **Sem índice remissivo nem bibliografia.** Se um documento os pedir, é sinal
|
|
586
|
+
de que cresceu para lá do que isto serve.
|
|
587
|
+
- **As fontes estão embutidas.** ~10 MB no pacote. A alternativa era depender
|
|
588
|
+
da rede na primeira compilação, e um documento que não compila offline não é
|
|
589
|
+
um documento.
|
|
590
|
+
|
|
591
|
+
## Licença
|
|
592
|
+
|
|
593
|
+
MIT, Laiss Labs. As fontes mantêm as suas licenças: Inter (OFL 1.1),
|
|
594
|
+
JetBrains Mono (OFL 1.1).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
laiss_docs-1.1.0.dist-info/licenses/LICENSE,sha256=2_tCdrXKhSQAvKLqk9QyhtBL2Jkn_RicVzBzO69sQxQ,1067
|
|
2
|
+
laissdocs/__init__.py,sha256=mO_MCqPWHHXKyaH61xnyPNcVrxilQop5PO0tHk7zvu8,198
|
|
3
|
+
laissdocs/__main__.py,sha256=YXHccPCkqOzVD52GU7O7pjXKSTDpqJe7vQmr7rm-WLg,149
|
|
4
|
+
laissdocs/ai.py,sha256=3gG8oM2NOSVqeYsPCdL7gYG1304QWDu4iWma3CIqids,11622
|
|
5
|
+
laissdocs/build.py,sha256=yQLacrjfIiEFOvwXQKwKKdp30U9PW9cblHVlT7PkJDo,18214
|
|
6
|
+
laissdocs/cli.py,sha256=fM3lE1HAwC024vAydrGgUAz8jXtc4eI9E_x3Jv7D2Fk,42555
|
|
7
|
+
laissdocs/config.py,sha256=4O1G6NJDkMkS8bxO0VTyiqPbHydWYclt3N5LSSCGmAQ,16136
|
|
8
|
+
laissdocs/content.py,sha256=-Z3wAUSKEIqdGFAHi_kEqJ0ReO9PMkRAel0i97UB734,32095
|
|
9
|
+
laissdocs/diagrams.py,sha256=y0pCK4tFmA-E11lFd8Yb68Zp5oeoW5e5c7yLxcHnzV0,11474
|
|
10
|
+
laissdocs/document.py,sha256=KC5UqmvCVLC1bB2YHB9hOWY8_sooMKHQ3oxeaXbvusk,21363
|
|
11
|
+
laissdocs/guide.py,sha256=kC-3tWkJxl7vnRD128we6wkXXOzN2viTJBC5Cy1IHMg,7605
|
|
12
|
+
laissdocs/install.py,sha256=lcV2ZgnA8cbo3Oo7dFoPDyYGXIQ2HgpcGFPqRLn9gWg,17874
|
|
13
|
+
laissdocs/locales.py,sha256=CBWhIIx6S0dk2R6tdiA8OEkemk3MW2c5rwMybR5iRrs,6658
|
|
14
|
+
laissdocs/mcp.py,sha256=kpbex9QSh7ZSXTJhUNsfqwOiliTitmPsA6CSJm55b4Q,27241
|
|
15
|
+
laissdocs/metrics.py,sha256=yzLjR7o6p8eLknF-XWjDbLAngUfqQj1EYTsE1H79MX4,6666
|
|
16
|
+
laissdocs/paths.py,sha256=qsPcisPiZjd-jH9s_Dj1dftpxLmegKdYDXlZTATSYjY,3645
|
|
17
|
+
laissdocs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
laissdocs/assets/mark.pdf,sha256=Gl_jZg7D4Ma9o7UKQPcUtNVOG1dEN-5eH1WfMTe1eEU,10206
|
|
19
|
+
laissdocs/assets/fonts/Inter-Bold.otf,sha256=AP1vRpHuiISjO0a_RYUbNBknw3wwBk9VjhvvskKXHEk,626056
|
|
20
|
+
laissdocs/assets/fonts/Inter-BoldItalic.otf,sha256=DJ9zeuRMwZ3Slicz9uGgCqSlN2jvx92YzSnTYJ2zEd0,604316
|
|
21
|
+
laissdocs/assets/fonts/Inter-Italic.otf,sha256=8GXrFIQxonaZW6iLeekiXkqgXwWzv-uFfPBFfcwQZKk,586752
|
|
22
|
+
laissdocs/assets/fonts/Inter-Regular.otf,sha256=vm1wnctzDdrwUMva5v6JvVbV0U3GaIVjm79vBr0DUhs,605092
|
|
23
|
+
laissdocs/assets/fonts/JetBrainsMonoNerdFont-Bold.ttf,sha256=5JBmCtdeCxUsk7FgTC6hpOpnXxuKP-DQBbGZhWj5nx8,2575364
|
|
24
|
+
laissdocs/assets/fonts/JetBrainsMonoNerdFont-Italic.ttf,sha256=cdVGbMOrMb7ji04sXMmcK0GnScSXPtRXm4FOyZKwc44,2573684
|
|
25
|
+
laissdocs/assets/fonts/JetBrainsMonoNerdFont-Regular.ttf,sha256=HGgOjN6fz4uIpWBc6NH7lN0_sVhB98p79MVWZIVeVhE,2571596
|
|
26
|
+
laissdocs/assets/fonts/LICENSE.txt,sha256=eMID7QPrkTnVmnwc0rEul_p68mXxuEaivbI6fEd97rQ,470
|
|
27
|
+
laissdocs/tex/cover.tex,sha256=3XJ4hx9WXyiTU4K-QU8Z9d0EJBR403Otu3GC_0c2p9E,2990
|
|
28
|
+
laissdocs/tex/laissdoc.sty,sha256=rdnFOETdHPkl10ADZxg6EHFBC_sdEx6i_3EDBuWy13M,20633
|
|
29
|
+
laiss_docs-1.1.0.dist-info/METADATA,sha256=4SdoyNU-ySl2_ToFnu8Q69TmNeJm9Tr35f_a_YHeYVU,21918
|
|
30
|
+
laiss_docs-1.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
31
|
+
laiss_docs-1.1.0.dist-info/entry_points.txt,sha256=7q9NTTPDIxIlgkeQ6PhUfpLJJ9xOmLgJAqQy1UtqkfQ,50
|
|
32
|
+
laiss_docs-1.1.0.dist-info/top_level.txt,sha256=x4EH8afxWBwQfol-UK3ZHoEEWy_hpjo3tAZ92rbjmTY,10
|
|
33
|
+
laiss_docs-1.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Laiss Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
laissdocs
|
laissdocs/__init__.py
ADDED