@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.
- package/dist/config/skills/git-commit/LICENSE.txt +18 -0
- package/dist/config/skills/git-commit/SKILL.md +258 -0
- package/dist/config/skills/git-commit/references/REFERENCE.md +249 -0
- package/dist/config/skills/git-commit/scripts/validate_commit_msg.py +163 -0
- package/dist/config/skills/git-pr/LICENSE.txt +18 -0
- package/dist/config/skills/git-pr/SKILL.md +293 -0
- package/dist/config/skills/git-pr/references/REFERENCE.md +256 -0
- package/dist/config/skills/git-pr/scripts/validate_commits.py +112 -0
- package/dist/config/skills/pdf/LICENSE.txt +26 -0
- package/dist/config/skills/pdf/SKILL.md +327 -0
- package/dist/config/skills/pdf/references/FORMS.md +69 -0
- package/dist/config/skills/pdf/references/REFERENCE.md +52 -0
- package/dist/config/skills/pdf/scripts/create_report.py +59 -0
- package/dist/config/skills/pdf/scripts/merge_pdfs.py +39 -0
- package/dist/config/skills/skill-creator/LICENSE.txt +26 -0
- package/dist/config/skills/skill-creator/SKILL.md +229 -0
- package/dist/config/skills/xlsx/LICENSE.txt +18 -0
- package/dist/config/skills/xlsx/SKILL.md +298 -0
- package/dist/config/skills/xlsx/references/REFERENCE.md +337 -0
- package/dist/config/skills/xlsx/scripts/office/__init__.py +2 -0
- package/dist/config/skills/xlsx/scripts/office/__pycache__/__init__.cpython-312.pyc +0 -0
- package/dist/config/skills/xlsx/scripts/office/__pycache__/pack.cpython-312.pyc +0 -0
- package/dist/config/skills/xlsx/scripts/office/__pycache__/soffice.cpython-312.pyc +0 -0
- package/dist/config/skills/xlsx/scripts/office/__pycache__/unpack.cpython-312.pyc +0 -0
- package/dist/config/skills/xlsx/scripts/office/__pycache__/validate.cpython-312.pyc +0 -0
- package/dist/config/skills/xlsx/scripts/office/pack.py +58 -0
- package/dist/config/skills/xlsx/scripts/office/soffice.py +180 -0
- package/dist/config/skills/xlsx/scripts/office/unpack.py +63 -0
- package/dist/config/skills/xlsx/scripts/office/validate.py +122 -0
- package/dist/config/skills/xlsx/scripts/recalc.py +143 -0
- package/dist/main.js +187 -46
- package/package.json +1 -1
- package/dist/config/example.bluma-mcp.json.txt +0 -14
- package/dist/config/models_config.json +0 -78
- package/dist/skills/git-conventional/LICENSE.txt +0 -3
- package/dist/skills/git-conventional/SKILL.md +0 -83
- package/dist/skills/skill-creator/SKILL.md +0 -495
- package/dist/skills/testing/LICENSE.txt +0 -3
- 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)
|