@nomad-e/bluma-cli 0.1.57 → 0.1.59
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.
- package/README.md +2 -2
- package/dist/config/native_tools.json +3 -3
- package/dist/config/skills/git-pr/SKILL.md +1 -1
- package/dist/config/skills/pdf/SKILL.md +153 -22
- package/dist/config/skills/pdf/scripts/__pycache__/create_report.cpython-312.pyc +0 -0
- package/dist/config/skills/pdf/scripts/create_report.py +607 -209
- package/dist/config/skills/pdf/scripts/merge_pdfs.py +1 -1
- package/dist/config/skills/skill-creator/SKILL.md +1 -1
- package/dist/main.js +117 -126
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -295,8 +295,8 @@ BluMa includes **40+ built-in tools** organized by category:
|
|
|
295
295
|
| `task_get` | Get one task by id |
|
|
296
296
|
| `task_update` | Update task fields |
|
|
297
297
|
| `task_stop` | Cancel a task |
|
|
298
|
-
| `create_artifact` | Save documents
|
|
299
|
-
| `read_artifact` |
|
|
298
|
+
| `create_artifact` | Save documents under `<workspace>/.bluma/artifacts/<session>/` (see `task_boundary` → `artifacts_dir`) |
|
|
299
|
+
| `read_artifact` | Read artifacts from the current session directory under `.bluma/artifacts/` |
|
|
300
300
|
|
|
301
301
|
### Knowledge & Research
|
|
302
302
|
| Tool | Description |
|
|
@@ -252,7 +252,7 @@
|
|
|
252
252
|
"items": {
|
|
253
253
|
"type": "string"
|
|
254
254
|
},
|
|
255
|
-
"description": "Optional file paths (absolute) to deliver as
|
|
255
|
+
"description": "Optional file paths (absolute) to deliver as attachments. Put generated files under ./.bluma/artifacts/ (or paths returned by task_boundary / create_artifact); never a top-level ./artifacts/ folder — tools remap that to .bluma/artifacts/."
|
|
256
256
|
}
|
|
257
257
|
},
|
|
258
258
|
"required": [
|
|
@@ -701,7 +701,7 @@
|
|
|
701
701
|
"type": "function",
|
|
702
702
|
"function": {
|
|
703
703
|
"name": "create_artifact",
|
|
704
|
-
"description": "Create or update an artifact
|
|
704
|
+
"description": "Create or update an artifact under the workspace .bluma/artifacts/<session>/ directory (see task_boundary artifacts_dir). Use for plans, walkthroughs, notes, or deliverables. Not the same as ~/.bluma.",
|
|
705
705
|
"parameters": {
|
|
706
706
|
"type": "object",
|
|
707
707
|
"properties": {
|
|
@@ -725,7 +725,7 @@
|
|
|
725
725
|
"type": "function",
|
|
726
726
|
"function": {
|
|
727
727
|
"name": "read_artifact",
|
|
728
|
-
"description": "Read an
|
|
728
|
+
"description": "Read an artifact from the current session artifacts directory (.bluma/artifacts/<session>/). Use to retrieve plans or notes saved with create_artifact in this task.",
|
|
729
729
|
"parameters": {
|
|
730
730
|
"type": "object",
|
|
731
731
|
"properties": {
|
|
@@ -104,7 +104,7 @@ feat(pdf): add CSV-to-PDF report generation
|
|
|
104
104
|
|
|
105
105
|
Implement create_report.py script that reads CSV data and produces
|
|
106
106
|
a formatted PDF report using reportlab. Supports custom titles and
|
|
107
|
-
outputs to the
|
|
107
|
+
outputs to `.bluma/artifacts/` in the workspace.
|
|
108
108
|
|
|
109
109
|
- Added scripts/create_report.py with argparse CLI
|
|
110
110
|
- Table styling with header highlighting and grid borders
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pdf
|
|
3
3
|
description: >
|
|
4
|
-
Use
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
Use for any PDF task. For **new business/executive reports**, prefer
|
|
5
|
+
`create_report.py --from-json`: invest effort in **rich, well-structured JSON**
|
|
6
|
+
(clear sections, tight copy, sparse callouts, scannable tables) — the template
|
|
7
|
+
already supplies a **modern, restrained** layout; shallow JSON wastes that
|
|
8
|
+
design. Ad-hoc ReportLab snippets produce amateur PDFs. Also: extract/merge/
|
|
9
|
+
OCR/forms. Triggers: .pdf, relatório, report, generate document, merge PDF.
|
|
10
10
|
license: Proprietary. LICENSE.txt has complete terms
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -18,6 +18,114 @@ license: Proprietary. LICENSE.txt has complete terms
|
|
|
18
18
|
> design agency, not a script. Typography, spacing, color, and hierarchy are
|
|
19
19
|
> not optional — they are the foundation of a credible document.
|
|
20
20
|
|
|
21
|
+
## MANDATORY workflow — new multi-page reports (read first)
|
|
22
|
+
|
|
23
|
+
Ad-hoc 30-line ReportLab scripts are the #1 cause of “cheap PDF” output (no
|
|
24
|
+
cover hierarchy, ugly tables, random margins). **Do not do that** for anything
|
|
25
|
+
the user will share externally.
|
|
26
|
+
|
|
27
|
+
**Preferred (deliverable quality locked in):**
|
|
28
|
+
|
|
29
|
+
1. Build a UTF-8 JSON file with `title`, optional `subtitle` / `author`, and
|
|
30
|
+
`sections[]` (`num`, `title`, `blocks[]`). Follow the **JSON quality**
|
|
31
|
+
section below — not only the schema (see **Available Scripts** for types).
|
|
32
|
+
2. Run: `python /absolute/path/to/scripts/create_report.py --from-json
|
|
33
|
+
your.json --output .bluma/artifacts/your.pdf` (or the path in `artifacts_dir` from `task_boundary`)
|
|
34
|
+
3. Attach that **absolute** path as the deliverable. Do **not** paste huge
|
|
35
|
+
ReportLab source into the chat unless the user explicitly wants code.
|
|
36
|
+
|
|
37
|
+
**Acceptable alternative:** copy `create_report.py` and edit only data / story
|
|
38
|
+
assembly — keep the same colors, margins, `cover_story`, `section`, `pro_table`,
|
|
39
|
+
`callout`, and `onFirstPage` / `onLaterPages` callbacks.
|
|
40
|
+
|
|
41
|
+
**Hard no (reads as unprofessional):** default ReportLab styles; wall of text
|
|
42
|
+
with no cover; tables without a styled header row; margins under 2cm; emoji in
|
|
43
|
+
corporate PDFs (font/rendering inconsistency); “nota rodapé” as raw
|
|
44
|
+
`canvas.drawString` for body paragraphs instead of `Paragraph` styles.
|
|
45
|
+
|
|
46
|
+
## JSON quality — make the PDF look “designed”, not “filled in”
|
|
47
|
+
|
|
48
|
+
The `--from-json` pipeline already applies a **clean, modern, understated**
|
|
49
|
+
visual system (slate palette, one accent, generous margins, one chapter per
|
|
50
|
+
page by default). **Exaggerated styling is not available and not desired** —
|
|
51
|
+
quality comes from **editorial structure and copy**, not from shouting.
|
|
52
|
+
|
|
53
|
+
When you author JSON, treat it like briefing a human designer:
|
|
54
|
+
|
|
55
|
+
### Cover metadata (`title`, `subtitle`, `author`)
|
|
56
|
+
|
|
57
|
+
- **`title`**: specific and scannable (e.g. “Relatório de atividade — Agiweb”),
|
|
58
|
+
not a generic “Relatório”. Length is OK; the script balances line breaks.
|
|
59
|
+
- **`subtitle`**: one line that states **scope or period** (what this PDF is
|
|
60
|
+
for). Avoid marketing fluff.
|
|
61
|
+
- **`author`**: organisation or product name the reader recognises; avoid
|
|
62
|
+
“Generated by AI” unless the user asked for it.
|
|
63
|
+
|
|
64
|
+
### Sections (`num`, `title`, `blocks`)
|
|
65
|
+
|
|
66
|
+
- Aim for **roughly 4–8 sections** for a typical executive note; merge thin
|
|
67
|
+
sections instead of many one-paragraph chapters.
|
|
68
|
+
- **Section titles**: concrete noun phrases (“Módulos ativos”, “Estado
|
|
69
|
+
operacional”) — not “Parte 2” or “Informações”.
|
|
70
|
+
- **Order inside a section** (good rhythm):
|
|
71
|
+
1. One or two **`paragraph`** blocks that set context (2–4 short sentences,
|
|
72
|
+
one main idea each; use `\n` only for a deliberate second paragraph).
|
|
73
|
+
2. Optionally an **`h2`** for a sub-topic if the section has two distinct
|
|
74
|
+
ideas.
|
|
75
|
+
3. **`bullets`** or **`table`** — not both back-to-back unless the second is
|
|
76
|
+
clearly “detail” after the first.
|
|
77
|
+
4. At most **one `callout`** per section, only for a decision, risk, or
|
|
78
|
+
single takeaway — not for every bullet.
|
|
79
|
+
|
|
80
|
+
### `paragraph`
|
|
81
|
+
|
|
82
|
+
- Prefer **facts, numbers, names** over vague adjectives (“significativo”,
|
|
83
|
+
“muito importante”). Write in the **same language** as the user’s request.
|
|
84
|
+
- Do not paste huge URLs or stack traces; summarise.
|
|
85
|
+
|
|
86
|
+
### `bullets`
|
|
87
|
+
|
|
88
|
+
- **Parallel wording** (all start with a verb, or all are noun phrases).
|
|
89
|
+
- **~3–8 items** per list in the body; move long enumerations to a **table** or
|
|
90
|
+
an appendix section.
|
|
91
|
+
- One line per item when possible; trim redundant words.
|
|
92
|
+
|
|
93
|
+
### `table`
|
|
94
|
+
|
|
95
|
+
- Prefer **2–5 columns**; headers are **short labels** (“Módulo”, “Estado”, not
|
|
96
|
+
sentences).
|
|
97
|
+
- Rows must be **readable at a glance**; paraphrase long statuses instead of
|
|
98
|
+
dumping raw log lines.
|
|
99
|
+
- Omit `col_widths` unless you need to weight columns; the generator normalises
|
|
100
|
+
widths.
|
|
101
|
+
|
|
102
|
+
### `callout` (`variant`: `info` | `warning`)
|
|
103
|
+
|
|
104
|
+
- **Info**: synthesis, definition, or “how to read this document”.
|
|
105
|
+
- **Warning**: real risk, compliance, or “requires action” — not routine stats.
|
|
106
|
+
- **Title**: 2–5 words; **text**: 1–3 sentences. **Never** stack two callouts in
|
|
107
|
+
a row.
|
|
108
|
+
|
|
109
|
+
### `h2`
|
|
110
|
+
|
|
111
|
+
- Use sparingly inside long sections; keeps hierarchy without visual noise.
|
|
112
|
+
|
|
113
|
+
### `page_break_per_section`
|
|
114
|
+
|
|
115
|
+
- Default **`true`** for multi-topic reports (calm, one chapter per page).
|
|
116
|
+
- Set **`false`** only for a short, single-thread document where breaks would
|
|
117
|
+
feel wasteful.
|
|
118
|
+
|
|
119
|
+
### Restraint checklist (before you run the script)
|
|
120
|
+
|
|
121
|
+
- [ ] Every section has at least one **substantive** `paragraph`, not only
|
|
122
|
+
bullets.
|
|
123
|
+
- [ ] No more than **one callout** per section unless the user explicitly wants
|
|
124
|
+
more.
|
|
125
|
+
- [ ] Tables are **small and meaningful**; no “spreadsheet dump”.
|
|
126
|
+
- [ ] No duplicated content between sumário-level titles and body.
|
|
127
|
+
- [ ] Tone is **professional and calm** — the layout already carries authority.
|
|
128
|
+
|
|
21
129
|
## MANDATORY: Professional Style System
|
|
22
130
|
|
|
23
131
|
Every PDF you create MUST use a consistent design system. Define these at the
|
|
@@ -29,17 +137,19 @@ top of every script BEFORE building any content.
|
|
|
29
137
|
from reportlab.lib.colors import HexColor
|
|
30
138
|
|
|
31
139
|
COLORS = {
|
|
32
|
-
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
140
|
+
# Keep in sync with scripts/create_report.py when editing custom code
|
|
141
|
+
'primary': HexColor('#0F172A'), # Slate — titles, table headers
|
|
142
|
+
'secondary': HexColor('#334155'), # Slate — subtitles, rules
|
|
143
|
+
'accent': HexColor('#C2410C'), # Burnt orange — rules, emphasis
|
|
144
|
+
'muted': HexColor('#64748B'),
|
|
145
|
+
'text': HexColor('#1E293B'),
|
|
146
|
+
'text_light': HexColor('#64748B'),
|
|
147
|
+
'bg_light': HexColor('#F1F5F9'),
|
|
148
|
+
'bg_accent': HexColor('#E0F2FE'),
|
|
149
|
+
'border': HexColor('#CBD5E1'),
|
|
40
150
|
'white': HexColor('#FFFFFF'),
|
|
41
|
-
'success': HexColor('#
|
|
42
|
-
'danger': HexColor('#
|
|
151
|
+
'success': HexColor('#15803D'),
|
|
152
|
+
'danger': HexColor('#B91C1C'),
|
|
43
153
|
}
|
|
44
154
|
```
|
|
45
155
|
|
|
@@ -272,11 +382,11 @@ def add_section(story, number, title):
|
|
|
272
382
|
def add_callout(story, title, text, box_type='info'):
|
|
273
383
|
bg = COLORS['bg_accent'] if box_type == 'info' else COLORS['bg_light']
|
|
274
384
|
border = COLORS['secondary'] if box_type == 'info' else COLORS['accent']
|
|
275
|
-
|
|
385
|
+
label = 'Information' if box_type == 'info' else 'Warning'
|
|
276
386
|
|
|
277
387
|
content = [
|
|
278
388
|
[Paragraph(
|
|
279
|
-
f"<b>{
|
|
389
|
+
f"<b>{label} — {title}</b>",
|
|
280
390
|
ParagraphStyle('CalloutTitle', fontName=FONT_BOLD,
|
|
281
391
|
fontSize=10, textColor=COLORS['primary'],
|
|
282
392
|
spaceAfter=4)
|
|
@@ -384,8 +494,12 @@ add_table(story,
|
|
|
384
494
|
[['Revenue', '$1.2M', '+15%'], ['Users', '50K', '+22%']],
|
|
385
495
|
)
|
|
386
496
|
|
|
387
|
-
|
|
388
|
-
|
|
497
|
+
def cover_canvas(canvas_obj, doc):
|
|
498
|
+
"""Page 1 only: brand bar / rules. See create_report.py draw_cover_canvas."""
|
|
499
|
+
pass
|
|
500
|
+
|
|
501
|
+
# First page = cover art; later pages = header + footer
|
|
502
|
+
doc.build(story, onFirstPage=cover_canvas, onLaterPages=header_footer)
|
|
389
503
|
```
|
|
390
504
|
|
|
391
505
|
## Design Rules (MANDATORY)
|
|
@@ -399,7 +513,9 @@ doc.build(story, onFirstPage=lambda c, d: None,
|
|
|
399
513
|
7. **Spacing**: Generous whitespace. Use `Spacer` between sections
|
|
400
514
|
8. **Cover page**: Every document with 3+ pages MUST have a cover page
|
|
401
515
|
9. **Headers/Footers**: Every multi-page document MUST have page numbers
|
|
402
|
-
10. **
|
|
516
|
+
10. **Body text**: use `Paragraph` + styles, never raw `drawString` for
|
|
517
|
+
paragraphs. **Exception:** minimal `drawString` in `onFirstPage` /
|
|
518
|
+
`onLaterPages` canvas callbacks for running title and page numbers only.
|
|
403
519
|
11. **Alignment**: Body text justified, titles centered, code left-aligned
|
|
404
520
|
12. **Callout boxes**: Use for key insights, warnings, important notes
|
|
405
521
|
13. **Section dividers**: Use `HRFlowable` or colored bars between major sections
|
|
@@ -498,5 +614,20 @@ for i, img in enumerate(images):
|
|
|
498
614
|
|
|
499
615
|
## Available Scripts
|
|
500
616
|
|
|
501
|
-
- create_report.py
|
|
617
|
+
- **create_report.py** — default: demo PDF; **production:** `--from-json FILE`
|
|
618
|
+
- Before writing JSON, follow **“JSON quality”** (this file) so content matches
|
|
619
|
+
the clean template — schema alone is not enough.
|
|
620
|
+
- Schema (top-level keys): `title` (str), `subtitle` (optional str),
|
|
621
|
+
`author` (optional str), `page_break_per_section` (optional bool, default
|
|
622
|
+
`true` — cada capítulo começa em página nova), `sections` (array).
|
|
623
|
+
- Each section: `num` (int), `title` (str), `blocks` (array).
|
|
624
|
+
- Each block: `type` is one of:
|
|
625
|
+
- `paragraph` — `{ "type": "paragraph", "text": "..." }` (use `\n` for line breaks)
|
|
626
|
+
- `h2` — `{ "type": "h2", "text": "..." }`
|
|
627
|
+
- `callout` — `{ "type": "callout", "title": "...", "text": "...", "variant": "info"|"warning" }`
|
|
628
|
+
- `bullets` — `{ "type": "bullets", "items": ["...", "..."] }`
|
|
629
|
+
- `table` — `{ "type": "table", "headers": [...], "rows": [[...],...], "col_widths": null or [pt,...] }`
|
|
630
|
+
- `spacer` — `{ "type": "spacer", "pt": 12 }`
|
|
631
|
+
- Plain text in JSON is XML-escaped automatically; do not embed ReportLab HTML
|
|
632
|
+
unless you generate JSON from trusted code that already escapes markup.
|
|
502
633
|
- merge_pdfs.py: Merge multiple PDFs into one
|