@nomad-e/bluma-cli 0.1.18 → 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 (39) hide show
  1. package/dist/config/skills/git-commit/LICENSE.txt +18 -0
  2. package/dist/config/skills/git-commit/SKILL.md +258 -0
  3. package/dist/config/skills/git-commit/references/REFERENCE.md +249 -0
  4. package/dist/config/skills/git-commit/scripts/validate_commit_msg.py +163 -0
  5. package/dist/config/skills/git-pr/LICENSE.txt +18 -0
  6. package/dist/config/skills/git-pr/SKILL.md +293 -0
  7. package/dist/config/skills/git-pr/references/REFERENCE.md +256 -0
  8. package/dist/config/skills/git-pr/scripts/validate_commits.py +112 -0
  9. package/dist/config/skills/pdf/LICENSE.txt +26 -0
  10. package/dist/config/skills/pdf/SKILL.md +327 -0
  11. package/dist/config/skills/pdf/references/FORMS.md +69 -0
  12. package/dist/config/skills/pdf/references/REFERENCE.md +52 -0
  13. package/dist/config/skills/pdf/scripts/create_report.py +59 -0
  14. package/dist/config/skills/pdf/scripts/merge_pdfs.py +39 -0
  15. package/dist/config/skills/skill-creator/LICENSE.txt +26 -0
  16. package/dist/config/skills/skill-creator/SKILL.md +229 -0
  17. package/dist/config/skills/xlsx/LICENSE.txt +18 -0
  18. package/dist/config/skills/xlsx/SKILL.md +298 -0
  19. package/dist/config/skills/xlsx/references/REFERENCE.md +337 -0
  20. package/dist/config/skills/xlsx/scripts/office/__init__.py +2 -0
  21. package/dist/config/skills/xlsx/scripts/office/__pycache__/__init__.cpython-312.pyc +0 -0
  22. package/dist/config/skills/xlsx/scripts/office/__pycache__/pack.cpython-312.pyc +0 -0
  23. package/dist/config/skills/xlsx/scripts/office/__pycache__/soffice.cpython-312.pyc +0 -0
  24. package/dist/config/skills/xlsx/scripts/office/__pycache__/unpack.cpython-312.pyc +0 -0
  25. package/dist/config/skills/xlsx/scripts/office/__pycache__/validate.cpython-312.pyc +0 -0
  26. package/dist/config/skills/xlsx/scripts/office/pack.py +58 -0
  27. package/dist/config/skills/xlsx/scripts/office/soffice.py +180 -0
  28. package/dist/config/skills/xlsx/scripts/office/unpack.py +63 -0
  29. package/dist/config/skills/xlsx/scripts/office/validate.py +122 -0
  30. package/dist/config/skills/xlsx/scripts/recalc.py +143 -0
  31. package/dist/main.js +187 -46
  32. package/package.json +1 -1
  33. package/dist/config/example.bluma-mcp.json.txt +0 -14
  34. package/dist/config/models_config.json +0 -78
  35. package/dist/skills/git-conventional/LICENSE.txt +0 -3
  36. package/dist/skills/git-conventional/SKILL.md +0 -83
  37. package/dist/skills/skill-creator/SKILL.md +0 -495
  38. package/dist/skills/testing/LICENSE.txt +0 -3
  39. package/dist/skills/testing/SKILL.md +0 -114
@@ -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.
@@ -0,0 +1,298 @@
1
+ ---
2
+ name: xlsx
3
+ description: >
4
+ Use this skill any time a spreadsheet file is the primary input or output.
5
+ Triggers: open, read, edit, create, fix, or convert .xlsx, .xlsm, .csv,
6
+ or .tsv files — including adding columns, computing formulas, formatting,
7
+ charting, cleaning messy data, restructuring tabular data, or building
8
+ financial models. Also trigger when the user references a spreadsheet by
9
+ name or path and wants something done to it. The deliverable must be a
10
+ spreadsheet file. Do NOT trigger when the primary deliverable is a Word
11
+ document, HTML report, or standalone script.
12
+ license: Proprietary. LICENSE.txt has complete terms
13
+ ---
14
+
15
+ # XLSX — Spreadsheet Creation, Editing & Analysis
16
+
17
+ ## Core Principle
18
+
19
+ > **Formulas, not hardcodes.** Every calculated value MUST be an Excel
20
+ > formula so the spreadsheet stays dynamic. Never compute in Python and
21
+ > paste the result into a cell.
22
+
23
+ ## Output Quality Standards
24
+
25
+ ### Professional Appearance
26
+ - Consistent font throughout (Arial or Calibri, 10-11pt)
27
+ - Headers bold with subtle background fill
28
+ - Column widths auto-fitted to content
29
+ - Number formatting applied to all numeric cells
30
+ - Borders and alignment used consistently
31
+
32
+ ### Zero Formula Errors
33
+ Every deliverable MUST have ZERO formula errors. After writing formulas,
34
+ always run `scripts/recalc.py` and fix any errors before delivering.
35
+
36
+ ### Preserve Existing Templates
37
+ When modifying an existing file, study and EXACTLY match its format,
38
+ style, and conventions. Existing patterns override these guidelines.
39
+
40
+ ## Reading & Analyzing Data
41
+
42
+ ### Quick Analysis with pandas
43
+
44
+ ```python
45
+ import pandas as pd
46
+
47
+ df = pd.read_excel('file.xlsx')
48
+ all_sheets = pd.read_excel('file.xlsx', sheet_name=None)
49
+
50
+ df.head()
51
+ df.info()
52
+ df.describe()
53
+
54
+ df.to_excel('output.xlsx', index=False)
55
+ ```
56
+
57
+ ### Reading with openpyxl (preserves formulas)
58
+
59
+ ```python
60
+ from openpyxl import load_workbook
61
+
62
+ wb = load_workbook('file.xlsx')
63
+ ws = wb.active
64
+
65
+ for row in ws.iter_rows(min_row=1, max_row=5, values_only=True):
66
+ print(row)
67
+
68
+ wb_data = load_workbook('file.xlsx', data_only=True)
69
+ ```
70
+
71
+ **Warning**: Never open with `data_only=True` and save — formulas are
72
+ permanently replaced with cached values.
73
+
74
+ ## Creating New Excel Files
75
+
76
+ ```python
77
+ from openpyxl import Workbook
78
+ from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
79
+ from openpyxl.utils import get_column_letter
80
+
81
+ wb = Workbook()
82
+ ws = wb.active
83
+ ws.title = "Data"
84
+
85
+ header_font = Font(bold=True, size=11, name='Arial')
86
+ header_fill = PatternFill('solid', fgColor='D9E1F2')
87
+ header_align = Alignment(horizontal='center', vertical='center')
88
+
89
+ headers = ['Item', 'Qty', 'Unit Price', 'Total']
90
+ for col, h in enumerate(headers, 1):
91
+ cell = ws.cell(row=1, column=col, value=h)
92
+ cell.font = header_font
93
+ cell.fill = header_fill
94
+ cell.alignment = header_align
95
+
96
+ ws['D2'] = '=B2*C2'
97
+
98
+ for col in range(1, len(headers) + 1):
99
+ ws.column_dimensions[get_column_letter(col)].width = 15
100
+
101
+ wb.save('output.xlsx')
102
+ ```
103
+
104
+ ## Editing Existing Files
105
+
106
+ ```python
107
+ from openpyxl import load_workbook
108
+
109
+ wb = load_workbook('existing.xlsx')
110
+ ws = wb['SheetName']
111
+
112
+ ws['A1'] = 'New Value'
113
+ ws.insert_rows(2)
114
+ ws.delete_cols(3)
115
+
116
+ new_ws = wb.create_sheet('Summary')
117
+ new_ws['A1'] = '=SUM(Data!B2:B100)'
118
+
119
+ wb.save('modified.xlsx')
120
+ ```
121
+
122
+ ## Formula Rules
123
+
124
+ ### Always Use Excel Formulas
125
+
126
+ ```python
127
+ # WRONG — hardcoded calculation
128
+ total = sum(values)
129
+ ws['B10'] = total
130
+
131
+ # CORRECT — Excel formula
132
+ ws['B10'] = '=SUM(B2:B9)'
133
+ ```
134
+
135
+ This applies to ALL calculations: totals, averages, percentages,
136
+ growth rates, ratios, conditional aggregations.
137
+
138
+ ### Common Formula Patterns
139
+
140
+ | Operation | Formula |
141
+ |-----------|---------|
142
+ | Sum | `=SUM(B2:B9)` |
143
+ | Average | `=AVERAGE(B2:B9)` |
144
+ | Count non-empty | `=COUNTA(B2:B9)` |
145
+ | Percentage | `=B2/B$10` |
146
+ | Year-over-year growth | `=(C2-B2)/B2` |
147
+ | Conditional sum | `=SUMIF(A:A,"Category",B:B)` |
148
+ | Lookup | `=VLOOKUP(A2,Data!A:C,3,FALSE)` |
149
+ | If/then | `=IF(B2>0,B2*C2,0)` |
150
+ | Error handling | `=IFERROR(B2/C2,0)` |
151
+
152
+ ### Cross-Sheet References
153
+
154
+ ```python
155
+ ws['A1'] = "=Summary!B5"
156
+ ws['A2'] = "=VLOOKUP(A1,Data!A:C,3,FALSE)"
157
+ ```
158
+
159
+ ## Recalculating Formulas (MANDATORY)
160
+
161
+ After creating or editing formulas with openpyxl, values are NOT
162
+ calculated. You MUST run recalculation:
163
+
164
+ ```bash
165
+ python scripts/recalc.py output.xlsx
166
+ ```
167
+
168
+ The script returns JSON:
169
+
170
+ ```json
171
+ {
172
+ "status": "success",
173
+ "total_errors": 0,
174
+ "total_formulas": 42,
175
+ "sheets_processed": ["Sheet1", "Summary"]
176
+ }
177
+ ```
178
+
179
+ If errors are found:
180
+
181
+ ```json
182
+ {
183
+ "status": "errors_found",
184
+ "total_errors": 3,
185
+ "total_formulas": 42,
186
+ "error_summary": {
187
+ "#REF!": {"count": 2, "locations": ["Sheet1!B5", "Sheet1!C10"]},
188
+ "#DIV/0!": {"count": 1, "locations": ["Summary!D4"]}
189
+ }
190
+ }
191
+ ```
192
+
193
+ **Fix all errors and recalculate again until status is "success".**
194
+
195
+ Common error fixes:
196
+ - `#REF!` — Invalid cell reference; check if rows/columns were deleted
197
+ - `#DIV/0!` — Wrap with `=IFERROR(formula, 0)`
198
+ - `#VALUE!` — Wrong data type; check cell contents
199
+ - `#NAME?` — Typo in function name or missing quotes on strings
200
+ - `#N/A` — VLOOKUP/MATCH found no result; use `=IFERROR()`
201
+
202
+ ## Financial Model Standards
203
+
204
+ ### Color Coding (Industry Standard)
205
+
206
+ | Color | RGB | Usage |
207
+ |-------|-----|-------|
208
+ | Blue text | `0000FF` | Hardcoded inputs and assumptions |
209
+ | Black text | `000000` | All formulas and calculations |
210
+ | Green text | `008000` | Links to other sheets in same workbook |
211
+ | Red text | `FF0000` | External links to other files |
212
+ | Yellow background | `FFFF00` | Key assumptions needing attention |
213
+
214
+ ```python
215
+ from openpyxl.styles import Font, PatternFill
216
+
217
+ input_font = Font(color='0000FF', name='Arial', size=11)
218
+ formula_font = Font(color='000000', name='Arial', size=11)
219
+ crossref_font = Font(color='008000', name='Arial', size=11)
220
+ attention_fill = PatternFill('solid', fgColor='FFFF00')
221
+ ```
222
+
223
+ ### Number Formatting
224
+
225
+ | Data Type | Format Code | Example |
226
+ |-----------|-------------|---------|
227
+ | Currency | `$#,##0;($#,##0);"-"` | $1,500 / ($200) / - |
228
+ | Percentage | `0.0%` | 15.3% |
229
+ | Multiples | `0.0x` | 8.5x |
230
+ | Years | `@` (text) | 2024 (not 2,024) |
231
+ | Large numbers | `$#,##0.0,,"mm"` | $1.5mm |
232
+ | Integers | `#,##0;(#,##0);"-"` | 1,500 / (200) / - |
233
+
234
+ ```python
235
+ ws['B2'].number_format = '$#,##0;($#,##0);"-"'
236
+ ws['C2'].number_format = '0.0%'
237
+ ws['D2'].number_format = '0.0x'
238
+ ```
239
+
240
+ ### Assumptions Placement
241
+
242
+ Place ALL assumptions in dedicated cells with blue text. Never
243
+ hardcode values inside formulas:
244
+
245
+ ```python
246
+ ws['B3'] = 0.05
247
+ ws['B3'].font = input_font
248
+ ws['B3'].number_format = '0.0%'
249
+ ws['B3'].comment = openpyxl.comments.Comment(
250
+ 'Revenue growth assumption — Source: Management guidance FY2025',
251
+ 'BluMa'
252
+ )
253
+
254
+ ws['C5'] = '=C4*(1+$B$3)'
255
+ ws['C5'].font = formula_font
256
+ ```
257
+
258
+ ### Documentation for Hardcoded Values
259
+
260
+ Every hardcoded number needs a source comment:
261
+ - `Source: Company 10-K, FY2024, Page 45, Revenue Note`
262
+ - `Source: Bloomberg Terminal, 2025-08-15, AAPL US Equity`
263
+ - `Source: FactSet, Consensus Estimates, 2025-08-20`
264
+
265
+ ## Verification Checklist
266
+
267
+ Before delivering any xlsx file:
268
+
269
+ 1. Run `scripts/recalc.py` — status must be "success"
270
+ 2. Spot-check 3-5 formulas against expected values
271
+ 3. Verify column mappings (Excel columns are 1-indexed)
272
+ 4. Check row offsets (DataFrame row 5 = Excel row 6 with header)
273
+ 5. Confirm no NaN or None values leaked into cells
274
+ 6. Validate number formatting on all numeric columns
275
+ 7. Ensure all hardcodes have source comments (financial models)
276
+ 8. Test edge cases: zero values, empty cells, negative numbers
277
+
278
+ ## Library Selection Guide
279
+
280
+ | Task | Use |
281
+ |------|-----|
282
+ | Data analysis, filtering, grouping | pandas |
283
+ | Formulas, formatting, styles | openpyxl |
284
+ | Complex formatting + data analysis | Both (pandas to process, openpyxl to format) |
285
+ | Read calculated values (no edit) | openpyxl with `data_only=True` |
286
+ | Very large files (read-only) | openpyxl with `read_only=True` |
287
+
288
+ ## Available References
289
+
290
+ - REFERENCE.md: Advanced financial modeling patterns, chart creation, pivot tables, conditional formatting, data validation, print layout
291
+
292
+ ## Available Scripts
293
+
294
+ - recalc.py: Recalculate all formulas via LibreOffice and scan for errors (MANDATORY after formula changes)
295
+ - office/soffice.py: LibreOffice process management for sandboxed environments (internal)
296
+ - office/pack.py: Compress Office XML files into .xlsx (internal)
297
+ - office/unpack.py: Decompress .xlsx into Office XML structure (internal)
298
+ - office/validate.py: Validate Office XML structure against schemas (internal)