@nomad-e/bluma-cli 0.1.17 → 0.1.19

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.
Files changed (41) hide show
  1. package/README.md +62 -12
  2. package/dist/config/native_tools.json +7 -0
  3. package/dist/config/skills/git-commit/LICENSE.txt +18 -0
  4. package/dist/config/skills/git-commit/SKILL.md +258 -0
  5. package/dist/config/skills/git-commit/references/REFERENCE.md +249 -0
  6. package/dist/config/skills/git-commit/scripts/validate_commit_msg.py +163 -0
  7. package/dist/config/skills/git-pr/LICENSE.txt +18 -0
  8. package/dist/config/skills/git-pr/SKILL.md +293 -0
  9. package/dist/config/skills/git-pr/references/REFERENCE.md +256 -0
  10. package/dist/config/skills/git-pr/scripts/validate_commits.py +112 -0
  11. package/dist/config/skills/pdf/LICENSE.txt +26 -0
  12. package/dist/config/skills/pdf/SKILL.md +327 -0
  13. package/dist/config/skills/pdf/references/FORMS.md +69 -0
  14. package/dist/config/skills/pdf/references/REFERENCE.md +52 -0
  15. package/dist/config/skills/pdf/scripts/create_report.py +59 -0
  16. package/dist/config/skills/pdf/scripts/merge_pdfs.py +39 -0
  17. package/dist/config/skills/skill-creator/LICENSE.txt +26 -0
  18. package/dist/config/skills/skill-creator/SKILL.md +229 -0
  19. package/dist/config/skills/xlsx/LICENSE.txt +18 -0
  20. package/dist/config/skills/xlsx/SKILL.md +298 -0
  21. package/dist/config/skills/xlsx/references/REFERENCE.md +337 -0
  22. package/dist/config/skills/xlsx/scripts/office/__init__.py +2 -0
  23. package/dist/config/skills/xlsx/scripts/office/__pycache__/__init__.cpython-312.pyc +0 -0
  24. package/dist/config/skills/xlsx/scripts/office/__pycache__/pack.cpython-312.pyc +0 -0
  25. package/dist/config/skills/xlsx/scripts/office/__pycache__/soffice.cpython-312.pyc +0 -0
  26. package/dist/config/skills/xlsx/scripts/office/__pycache__/unpack.cpython-312.pyc +0 -0
  27. package/dist/config/skills/xlsx/scripts/office/__pycache__/validate.cpython-312.pyc +0 -0
  28. package/dist/config/skills/xlsx/scripts/office/pack.py +58 -0
  29. package/dist/config/skills/xlsx/scripts/office/soffice.py +180 -0
  30. package/dist/config/skills/xlsx/scripts/office/unpack.py +63 -0
  31. package/dist/config/skills/xlsx/scripts/office/validate.py +122 -0
  32. package/dist/config/skills/xlsx/scripts/recalc.py +143 -0
  33. package/dist/main.js +275 -89
  34. package/package.json +1 -1
  35. package/dist/config/example.bluma-mcp.json.txt +0 -14
  36. package/dist/config/models_config.json +0 -78
  37. package/dist/skills/git-conventional/LICENSE.txt +0 -3
  38. package/dist/skills/git-conventional/SKILL.md +0 -83
  39. package/dist/skills/skill-creator/SKILL.md +0 -495
  40. package/dist/skills/testing/LICENSE.txt +0 -3
  41. package/dist/skills/testing/SKILL.md +0 -114
@@ -0,0 +1,69 @@
1
+ # PDF Forms — Filling and Processing Guide
2
+
3
+ This reference covers PDF form filling using Python and JavaScript libraries.
4
+
5
+ ## Detecting Form Fields (Python)
6
+
7
+ ```python
8
+ from pypdf import PdfReader
9
+
10
+ reader = PdfReader("form.pdf")
11
+ fields = reader.get_fields()
12
+ for name, field in fields.items():
13
+ print(f"Field: {name}, Type: {field.get('/FT')}, Value: {field.get('/V')}")
14
+ ```
15
+
16
+ ## Filling Forms with pypdf
17
+
18
+ ```python
19
+ from pypdf import PdfReader, PdfWriter
20
+
21
+ reader = PdfReader("form.pdf")
22
+ writer = PdfWriter()
23
+ writer.append(reader)
24
+
25
+ writer.update_page_form_field_values(
26
+ writer.pages[0],
27
+ {
28
+ "name_field": "John Doe",
29
+ "email_field": "john@example.com",
30
+ "date_field": "2026-01-15",
31
+ }
32
+ )
33
+
34
+ with open("filled_form.pdf", "wb") as output:
35
+ writer.write(output)
36
+ ```
37
+
38
+ ## Filling Forms with pdf-lib (JavaScript)
39
+
40
+ ```javascript
41
+ import { PDFDocument } from 'pdf-lib';
42
+ import fs from 'fs';
43
+
44
+ const pdfBytes = fs.readFileSync('form.pdf');
45
+ const pdfDoc = await PDFDocument.load(pdfBytes);
46
+ const form = pdfDoc.getForm();
47
+
48
+ form.getTextField('name').setText('John Doe');
49
+ form.getTextField('email').setText('john@example.com');
50
+ form.getCheckBox('agree').check();
51
+
52
+ const filledBytes = await pdfDoc.save();
53
+ fs.writeFileSync('filled_form.pdf', filledBytes);
54
+ ```
55
+
56
+ ## Common Form Field Types
57
+
58
+ | Type | pypdf Code | pdf-lib Code |
59
+ |------|-----------|-------------|
60
+ | Text | `update_page_form_field_values` | `getTextField().setText()` |
61
+ | Checkbox | Set to `/Yes` or `/Off` | `getCheckBox().check()` |
62
+ | Radio | Set to option value | `getRadioGroup().select()` |
63
+ | Dropdown | Set to option value | `getDropdown().select()` |
64
+
65
+ ## Tips
66
+
67
+ - Always flatten forms after filling if the PDF will be shared (prevents editing).
68
+ - Some PDFs have XFA forms (XML-based) which are harder to fill programmatically.
69
+ - For XFA forms, consider using pdftk or a commercial library.
@@ -0,0 +1,52 @@
1
+ # PDF Processing — Advanced Reference
2
+
3
+ This document covers advanced PDF processing techniques beyond the basics in SKILL.md.
4
+
5
+ ## pypdfium2 — High-Performance Rendering
6
+
7
+ ```python
8
+ import pypdfium2 as pdfium
9
+
10
+ pdf = pdfium.PdfDocument("document.pdf")
11
+ for i, page in enumerate(pdf):
12
+ bitmap = page.render(scale=2)
13
+ image = bitmap.to_pil()
14
+ image.save(f"page_{i+1}.png")
15
+ ```
16
+
17
+ ## JavaScript Libraries (pdf-lib)
18
+
19
+ For browser-based or Node.js PDF manipulation:
20
+
21
+ ```javascript
22
+ import { PDFDocument } from 'pdf-lib';
23
+
24
+ const pdfDoc = await PDFDocument.create();
25
+ const page = pdfDoc.addPage([600, 400]);
26
+ page.drawText('Hello, PDF!', { x: 50, y: 350, size: 30 });
27
+ const pdfBytes = await pdfDoc.save();
28
+ ```
29
+
30
+ ## pdf-lib — Modify Existing PDFs
31
+
32
+ ```javascript
33
+ import { PDFDocument } from 'pdf-lib';
34
+ import fs from 'fs';
35
+
36
+ const existingPdfBytes = fs.readFileSync('input.pdf');
37
+ const pdfDoc = await PDFDocument.load(existingPdfBytes);
38
+ const pages = pdfDoc.getPages();
39
+ const firstPage = pages[0];
40
+ firstPage.drawText('WATERMARK', { x: 200, y: 400, size: 50, opacity: 0.3 });
41
+ const pdfBytes = await pdfDoc.save();
42
+ fs.writeFileSync('output.pdf', pdfBytes);
43
+ ```
44
+
45
+ ## Troubleshooting
46
+
47
+ | Issue | Solution |
48
+ |-------|----------|
49
+ | "PdfReadError: EOF marker not found" | File is corrupted or not a valid PDF |
50
+ | Empty text extraction | PDF might be image-based; use OCR (pytesseract) |
51
+ | Tables not extracted correctly | Adjust pdfplumber table settings or try camelot |
52
+ | Large PDF processing slow | Process pages in chunks or use pypdfium2 |
@@ -0,0 +1,59 @@
1
+ """
2
+ Create a simple PDF report from CSV data using reportlab.
3
+
4
+ Usage:
5
+ python create_report.py --input data.csv --output ./artifacts/report.pdf
6
+ python create_report.py --input data.csv --output report.pdf --title "Sales Report Q1 2026"
7
+ """
8
+ import argparse
9
+ import csv
10
+ import sys
11
+
12
+ def create_report(input_csv: str, output_pdf: str, title: str = "Report") -> None:
13
+ try:
14
+ from reportlab.lib.pagesizes import A4
15
+ from reportlab.lib import colors
16
+ from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
17
+ from reportlab.lib.styles import getSampleStyleSheet
18
+ except ImportError:
19
+ print("Error: reportlab is required. Install with: pip install reportlab", file=sys.stderr)
20
+ sys.exit(1)
21
+
22
+ with open(input_csv, "r", encoding="utf-8") as f:
23
+ reader = csv.reader(f)
24
+ data = list(reader)
25
+
26
+ if not data:
27
+ print("Error: CSV file is empty", file=sys.stderr)
28
+ sys.exit(1)
29
+
30
+ doc = SimpleDocTemplate(output_pdf, pagesize=A4)
31
+ styles = getSampleStyleSheet()
32
+ elements = []
33
+
34
+ elements.append(Paragraph(title, styles["Title"]))
35
+ elements.append(Spacer(1, 20))
36
+
37
+ table = Table(data)
38
+ table.setStyle(TableStyle([
39
+ ("BACKGROUND", (0, 0), (-1, 0), colors.grey),
40
+ ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke),
41
+ ("ALIGN", (0, 0), (-1, -1), "CENTER"),
42
+ ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
43
+ ("FONTSIZE", (0, 0), (-1, 0), 12),
44
+ ("BOTTOMPADDING", (0, 0), (-1, 0), 12),
45
+ ("BACKGROUND", (0, 1), (-1, -1), colors.beige),
46
+ ("GRID", (0, 0), (-1, -1), 1, colors.black),
47
+ ]))
48
+ elements.append(table)
49
+
50
+ doc.build(elements)
51
+ print(f"Report created: {output_pdf} ({len(data)-1} data rows)")
52
+
53
+ if __name__ == "__main__":
54
+ parser = argparse.ArgumentParser(description="Create PDF report from CSV")
55
+ parser.add_argument("--input", "-i", required=True, help="Input CSV file")
56
+ parser.add_argument("--output", "-o", required=True, help="Output PDF path")
57
+ parser.add_argument("--title", "-t", default="Report", help="Report title")
58
+ args = parser.parse_args()
59
+ create_report(args.input, args.output, args.title)
@@ -0,0 +1,39 @@
1
+ """
2
+ Merge multiple PDF files into a single output PDF.
3
+
4
+ Usage:
5
+ python merge_pdfs.py --output merged.pdf file1.pdf file2.pdf file3.pdf
6
+ python merge_pdfs.py --output ./artifacts/combined.pdf *.pdf
7
+ """
8
+ import argparse
9
+ import sys
10
+
11
+ def merge_pdfs(input_files: list[str], output_path: str) -> None:
12
+ try:
13
+ from pypdf import PdfWriter, PdfReader
14
+ except ImportError:
15
+ print("Error: pypdf is required. Install with: pip install pypdf", file=sys.stderr)
16
+ sys.exit(1)
17
+
18
+ writer = PdfWriter()
19
+ for pdf_file in input_files:
20
+ try:
21
+ reader = PdfReader(pdf_file)
22
+ for page in reader.pages:
23
+ writer.add_page(page)
24
+ print(f"Added: {pdf_file} ({len(reader.pages)} pages)")
25
+ except Exception as e:
26
+ print(f"Error reading {pdf_file}: {e}", file=sys.stderr)
27
+ sys.exit(1)
28
+
29
+ with open(output_path, "wb") as f:
30
+ writer.write(f)
31
+
32
+ print(f"Merged {len(input_files)} files -> {output_path}")
33
+
34
+ if __name__ == "__main__":
35
+ parser = argparse.ArgumentParser(description="Merge PDF files")
36
+ parser.add_argument("files", nargs="+", help="PDF files to merge")
37
+ parser.add_argument("--output", "-o", required=True, help="Output file path")
38
+ args = parser.parse_args()
39
+ merge_pdfs(args.files, args.output)
@@ -0,0 +1,26 @@
1
+ © 2026 NomadEngenuity LDA. All rights reserved.
2
+
3
+ LICENSE: Use of these materials (including all code, prompts, assets, files,
4
+ and other components of this Skill) is governed by your agreement with
5
+ NomadEngenuity LDA regarding use of NomadEngenuity LDA's services. If no
6
+ separate agreement exists, use is governed by NomadEngenuity LDA's applicable
7
+ Terms of Service.
8
+
9
+ ADDITIONAL RESTRICTIONS: Notwithstanding anything in the Agreement to the
10
+ contrary, users may not:
11
+
12
+ - Extract these materials from the Services or retain copies of these
13
+ materials outside the Services
14
+ - Reproduce or copy these materials, except for temporary copies created
15
+ automatically during authorized use of the Services
16
+ - Create derivative works based on these materials
17
+ - Distribute, sublicense, or transfer these materials to any third party
18
+ - Make, offer to sell, sell, or import any inventions embodied in these
19
+ materials
20
+ - Reverse engineer, decompile, or disassemble these materials
21
+
22
+ The receipt, viewing, or possession of these materials does not convey or
23
+ imply any license or right beyond those expressly granted above.
24
+
25
+ NomadEngenuity LDA retains all right, title, and interest in these materials,
26
+ including all copyrights, patents, and other intellectual property rights.
@@ -0,0 +1,229 @@
1
+ ---
2
+ name: skill-creator
3
+ description: >
4
+ Use this skill when the user wants to create, write, build, or author a new
5
+ BluMa skill. Triggers include: "create a skill", "make a new skill",
6
+ "build a skill for X", "I need a skill that does Y", "skill template",
7
+ or any request to package knowledge/workflows as a reusable skill module.
8
+ license: Proprietary. LICENSE.txt has complete terms
9
+ ---
10
+
11
+ # Instructions
12
+
13
+ ## Context
14
+ You are a Skill Builder. You create new BluMa skills with the full Progressive Disclosure structure. Every skill you create MUST follow the standard layout.
15
+
16
+ ## Standard Skill Layout
17
+
18
+ Every skill is a directory with this structure:
19
+
20
+ ```
21
+ skill-name/
22
+ SKILL.md # Core instructions (Level 2)
23
+ LICENSE.txt # License terms
24
+ references/ # Additional docs loaded on-demand (Level 3a)
25
+ REFERENCE.md # Advanced guide (if applicable)
26
+ scripts/ # Ready-made scripts (Level 3b)
27
+ (domain-relevant scripts)
28
+ ```
29
+
30
+ The 3 levels of Progressive Disclosure:
31
+ - **Level 1**: `description` in frontmatter — always visible in the skills list, acts as a trigger
32
+ - **Level 2**: Body of SKILL.md — loaded when the skill is activated via `load_skill`
33
+ - **Level 3**: `references/` and `scripts/` — accessed only when the task requires them
34
+
35
+ ## Procedure
36
+
37
+ ### Step 1: Ask Location (REQUIRED)
38
+
39
+ Before anything else, ask the user where to create the skill:
40
+
41
+ ```
42
+ message({
43
+ content: "Where should I create this skill?\n\n**1. Project** (`.bluma/skills/`) - Only available in THIS project\n**2. Global** (`~/.bluma/skills/`) - Available in ALL projects\n\nChoose 1 or 2:",
44
+ message_type: "result"
45
+ })
46
+ ```
47
+
48
+ Wait for user response before continuing.
49
+
50
+ ### Step 2: Analyze User Input
51
+
52
+ Extract from user's content:
53
+ - Purpose/domain
54
+ - Responsibilities and workflows
55
+ - Rules/constraints
56
+ - Error scenarios
57
+ - Whether the content is short (<100 lines) or extensive (>100 lines)
58
+
59
+ ### Step 3: Generate Skill Name
60
+
61
+ Create kebab-case name from domain:
62
+ - "git workflow" -> `git-workflow`
63
+ - "api testing" -> `api-testing`
64
+ - "pdf processing" -> `pdf`
65
+
66
+ ### Step 4: Determine Path
67
+
68
+ Based on user's choice in Step 1:
69
+ - **Project (1)**: `.bluma/skills/{name}/`
70
+ - **Global (2)**: `~/.bluma/skills/{name}/`
71
+
72
+ ### Step 5: Check Directory
73
+
74
+ ```
75
+ ls_tool({ path: "{chosen-path}/{name}" })
76
+ ```
77
+
78
+ Verify directory doesn't exist (or confirm overwrite with user).
79
+
80
+ ### Step 6: Plan Content Split
81
+
82
+ Decide what goes where based on content size:
83
+
84
+ **If content is SHORT (<100 lines):**
85
+ - Everything goes in SKILL.md body
86
+ - references/ gets a minimal REFERENCE.md placeholder
87
+ - scripts/ stays empty (or gets a single utility script if applicable)
88
+
89
+ **If content is EXTENSIVE (>100 lines):**
90
+ - SKILL.md body gets: overview, quick start, core procedure (the essentials)
91
+ - references/ gets: advanced docs, detailed API guides, specialized topics
92
+ - scripts/ gets: reusable scripts the agent can execute
93
+
94
+ ### Step 7: Create the Skill Structure
95
+
96
+ Create the following files in order:
97
+
98
+ **7a. Create SKILL.md**
99
+
100
+ Use `edit_tool` with this template:
101
+
102
+ ```markdown
103
+ ---
104
+ name: {kebab-case-name}
105
+ description: >
106
+ Use this skill for any task involving {domain}. Triggers include: {list
107
+ of trigger phrases and keywords that should activate this skill}. Use
108
+ this skill whenever the user mentions {key terms} or any {domain}-related
109
+ task.
110
+ license: Proprietary. LICENSE.txt has complete terms
111
+ ---
112
+
113
+ # {Domain} Guide
114
+
115
+ ## Overview
116
+
117
+ {Brief description of what this skill covers and when to use it.}
118
+ {If extensive content exists in references/, mention it here:}
119
+ {For advanced features, see references/REFERENCE.md.}
120
+
121
+ ## Quick Start
122
+
123
+ {Minimal example to get started — 5-10 lines max.}
124
+
125
+ ## Procedure
126
+
127
+ ### Step 1: {First Action}
128
+ {Instructions}
129
+
130
+ ### Step 2: {Second Action}
131
+ {Instructions}
132
+
133
+ ## Error Reference
134
+
135
+ | Error | Cause | Fix |
136
+ |-------|-------|-----|
137
+ | {error} | {cause} | {fix} |
138
+
139
+ ## Available References
140
+
141
+ {List each reference file with when to use it:}
142
+ - REFERENCE.md: For advanced features and detailed API docs
143
+ {Add more as needed}
144
+
145
+ ## Available Scripts
146
+
147
+ {List each script with what it does:}
148
+ - {script_name.py}: {one-line description}
149
+ {Or "No scripts available for this skill." if none}
150
+
151
+ ## Next Steps
152
+
153
+ {Point to references/ for advanced topics.}
154
+ {Point to scripts/ for automation.}
155
+ ```
156
+
157
+ **7b. Create LICENSE.txt**
158
+
159
+ ```
160
+ edit_tool({
161
+ file_path: "{path}/{name}/LICENSE.txt",
162
+ old_string: "",
163
+ new_string: "© 2026 NomadEngenuity LDA. All rights reserved.\n\nLICENSE: Use of these materials..."
164
+ })
165
+ ```
166
+
167
+ Use the standard NomadEngenuity proprietary license (same as other native skills).
168
+
169
+ **7c. Create references/REFERENCE.md**
170
+
171
+ If extensive content exists, put advanced documentation here.
172
+ If content is short, create a placeholder:
173
+
174
+ ```markdown
175
+ # {Domain} — Advanced Reference
176
+
177
+ {Advanced documentation, detailed examples, specialized topics.}
178
+ {This file is loaded on-demand when the task requires deeper knowledge.}
179
+ ```
180
+
181
+ **7d. Create scripts/ (if applicable)**
182
+
183
+ If the domain benefits from ready-made scripts, create them.
184
+ Each script should:
185
+ - Be self-contained and executable with `python script.py [args]`
186
+ - Include a docstring explaining what it does
187
+ - Accept arguments via argparse or sys.argv
188
+ - Print results to stdout or write to a specified output path
189
+
190
+ Example script header:
191
+ ```python
192
+ """
193
+ {description of what this script does}
194
+
195
+ Usage:
196
+ python {script_name}.py --input data.csv --output ./artifacts/result.pdf
197
+ """
198
+ import argparse
199
+ ```
200
+
201
+ ### Step 8: Report Result
202
+
203
+ ```
204
+ message({
205
+ content: "Skill **{name}** created at `{path}/{name}/`\n\nStructure:\n- SKILL.md (core instructions)\n- LICENSE.txt\n- references/REFERENCE.md\n- scripts/ {list or empty}\n\nLocation: {Project|Global}\nThe skill is now available and will appear in the skills list.",
206
+ message_type: "result"
207
+ })
208
+ ```
209
+
210
+ ## Location Reference
211
+
212
+ | Location | Path | Scope | Use When |
213
+ |----------|------|-------|----------|
214
+ | Project | `.bluma/skills/` | This project only | Skill is specific to this codebase |
215
+ | Global | `~/.bluma/skills/` | All projects | Skill is generic and reusable |
216
+
217
+ ## Guidelines
218
+
219
+ - ALWAYS ask location first with `message_type: "result"`
220
+ - ALWAYS create the full structure: SKILL.md + LICENSE.txt + references/ + scripts/
221
+ - The `description` field is CRITICAL — it is the only text always visible and determines when the skill is activated. Include trigger words and phrases.
222
+ - SKILL.md body should contain only the essentials (quick start + procedure). Advanced docs go in references/.
223
+ - Scripts should be self-contained, executable, and well-documented.
224
+ - Use lists not tables where possible (saves tokens)
225
+ - No emojis in skill content
226
+ - English only for skill content (translate user input if needed)
227
+ - Professional, specific language
228
+ - Extract ALL information from user input
229
+ - NEVER create a skill with the same name as a native BluMa skill (check available_skills list first)
@@ -0,0 +1,18 @@
1
+ BluMa — Base Language Unit · Model Agent
2
+ Proprietary License
3
+
4
+ Copyright (c) 2024-2026 BluMa Contributors
5
+
6
+ This skill is part of the BluMa CLI distribution.
7
+ It is provided as a bundled native skill and may not be
8
+ redistributed, modified, or used outside of the BluMa agent
9
+ framework without prior written permission.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
13
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
14
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
15
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
16
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
18
+ OTHER DEALINGS IN THE SOFTWARE.