@pixerate/editor 0.223.0 → 0.223.2

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 (28) hide show
  1. package/dist/agent/index.cjs +2 -2
  2. package/dist/agent/index.js +1 -1
  3. package/dist/{chunk-SVHXD2VH.js → chunk-H73MSHYZ.js} +5 -1
  4. package/dist/{chunk-BDT2IYXK.cjs → chunk-VXLYEYP4.cjs} +6 -2
  5. package/dist/tools/index.cjs +2 -2
  6. package/dist/tools/index.js +1 -1
  7. package/dist/tools/skill-evals.d.cts +6 -1
  8. package/dist/tools/skill-evals.d.ts +6 -1
  9. package/package.json +1 -1
  10. package/skills/README.md +6 -1
  11. package/skills/pixerate-chart/SKILL.md +8 -0
  12. package/skills/pixerate-chart-annotate/SKILL.md +96 -0
  13. package/skills/pixerate-chart-annotate/evals/highlight-the-point.json +78 -0
  14. package/skills/pixerate-chart-annotate/evals/source-and-units.json +76 -0
  15. package/skills/pixerate-chart-annotate/evals/target-line.json +85 -0
  16. package/skills/pixerate-chart-choose-and-build/SKILL.md +100 -0
  17. package/skills/pixerate-chart-choose-and-build/evals/colour-blind-safe.json +113 -0
  18. package/skills/pixerate-chart-choose-and-build/evals/honest-baseline.json +81 -0
  19. package/skills/pixerate-chart-choose-and-build/evals/trend-over-time.json +175 -0
  20. package/skills/pixerate-spreadsheet/SKILL.md +2 -0
  21. package/skills/pixerate-spreadsheet-analysis/SKILL.md +97 -0
  22. package/skills/pixerate-spreadsheet-analysis/evals/count-and-average.json +148 -0
  23. package/skills/pixerate-spreadsheet-analysis/evals/month-over-month.json +128 -0
  24. package/skills/pixerate-spreadsheet-analysis/evals/summary-by-region.json +178 -0
  25. package/skills/pixerate-spreadsheet-financial-model/SKILL.md +98 -0
  26. package/skills/pixerate-spreadsheet-financial-model/evals/find-hardcoded.json +113 -0
  27. package/skills/pixerate-spreadsheet-financial-model/evals/projection-with-inputs.json +118 -0
  28. package/skills/pixerate-spreadsheet-financial-model/evals/scenarios.json +136 -0
@@ -0,0 +1,148 @@
1
+ {
2
+ "prompt": "For each product, show how many orders there were and the average order amount, in a small table starting at G1 (Product, Orders, Average), with formulas.",
3
+ "fixture": {
4
+ "editor": "spreadsheet",
5
+ "rows": [
6
+ [
7
+ "Date",
8
+ "Region",
9
+ "Product",
10
+ "Amount"
11
+ ],
12
+ [
13
+ "2026-01-05",
14
+ "North",
15
+ "Widget",
16
+ 120
17
+ ],
18
+ [
19
+ "2026-01-07",
20
+ "South",
21
+ "Gadget",
22
+ 80
23
+ ],
24
+ [
25
+ "2026-01-12",
26
+ "North",
27
+ "Gadget",
28
+ 95
29
+ ],
30
+ [
31
+ "2026-01-15",
32
+ "East",
33
+ "Widget",
34
+ 60
35
+ ],
36
+ [
37
+ "2026-01-20",
38
+ "West",
39
+ "Widget",
40
+ 150
41
+ ],
42
+ [
43
+ "2026-02-02",
44
+ "South",
45
+ "Widget",
46
+ 110
47
+ ],
48
+ [
49
+ "2026-02-08",
50
+ "East",
51
+ "Gadget",
52
+ 70
53
+ ],
54
+ [
55
+ "2026-02-11",
56
+ "North",
57
+ "Widget",
58
+ 130
59
+ ],
60
+ [
61
+ "2026-02-19",
62
+ "West",
63
+ "Gadget",
64
+ 90
65
+ ],
66
+ [
67
+ "2026-02-25",
68
+ "South",
69
+ "Gadget",
70
+ 85
71
+ ]
72
+ ]
73
+ },
74
+ "checks": [
75
+ {
76
+ "why": "Widget: 5 orders averaging 114; Gadget: 5 orders averaging 84",
77
+ "tool": "get_range",
78
+ "args": {
79
+ "range": "G2:I10"
80
+ },
81
+ "expect": {
82
+ "values": {
83
+ "$nonEmpty": {
84
+ "$length": 2,
85
+ "$expr": "v.some((r) => r[0] === 'Widget' && r[1] === 5 && Math.abs(r[2] - 114) < 0.01) && v.some((r) => r[0] === 'Gadget' && r[1] === 5 && Math.abs(r[2] - 84) < 0.01)"
86
+ }
87
+ }
88
+ }
89
+ },
90
+ {
91
+ "why": "formulas for the counts and averages",
92
+ "tool": "get_range",
93
+ "args": {
94
+ "range": "H2:I3"
95
+ },
96
+ "expect": {
97
+ "raw": {
98
+ "$every": {
99
+ "$every": {
100
+ "$match": "^="
101
+ }
102
+ }
103
+ }
104
+ }
105
+ },
106
+ {
107
+ "why": "headers",
108
+ "tool": "get_range",
109
+ "args": {
110
+ "range": "G1:I1"
111
+ },
112
+ "expect": {
113
+ "values": [
114
+ {
115
+ "$every": {
116
+ "$match": "\\S"
117
+ }
118
+ }
119
+ ]
120
+ }
121
+ }
122
+ ],
123
+ "solution": [
124
+ {
125
+ "tool": "set_range",
126
+ "args": {
127
+ "start": "G1",
128
+ "values": [
129
+ [
130
+ "Product",
131
+ "Orders",
132
+ "Average"
133
+ ],
134
+ [
135
+ "Widget",
136
+ "=COUNTIFS($C$2:$C$11,G2)",
137
+ "=AVERAGEIFS($D$2:$D$11,$C$2:$C$11,G2)"
138
+ ],
139
+ [
140
+ "Gadget",
141
+ "=COUNTIFS($C$2:$C$11,G3)",
142
+ "=AVERAGEIFS($D$2:$D$11,$C$2:$C$11,G3)"
143
+ ]
144
+ ]
145
+ }
146
+ }
147
+ ]
148
+ }
@@ -0,0 +1,128 @@
1
+ {
2
+ "prompt": "Add month-over-month growth in column C (header in C1) as a fraction (0.1 for 10 %), with formulas. January has no previous month.",
3
+ "fixture": {
4
+ "editor": "spreadsheet",
5
+ "rows": [
6
+ [
7
+ "Month",
8
+ "Revenue"
9
+ ],
10
+ [
11
+ "Jan",
12
+ 100
13
+ ],
14
+ [
15
+ "Feb",
16
+ 110
17
+ ],
18
+ [
19
+ "Mar",
20
+ 99
21
+ ],
22
+ [
23
+ "Apr",
24
+ 121
25
+ ],
26
+ [
27
+ "May",
28
+ 121
29
+ ],
30
+ [
31
+ "Jun",
32
+ 133
33
+ ]
34
+ ]
35
+ },
36
+ "checks": [
37
+ {
38
+ "why": "growth for February to June",
39
+ "tool": "get_range",
40
+ "args": {
41
+ "range": "C3:C7"
42
+ },
43
+ "expect": {
44
+ "values": {
45
+ "$expr": "[0.1, -0.1, 0.2222, 0, 0.0992].every((g, i) => Math.abs(v[i][0] - g) < 0.001)"
46
+ }
47
+ }
48
+ },
49
+ {
50
+ "why": "formulas, not typed numbers",
51
+ "tool": "get_range",
52
+ "args": {
53
+ "range": "C3:C7"
54
+ },
55
+ "expect": {
56
+ "raw": {
57
+ "$every": {
58
+ "$every": {
59
+ "$match": "^="
60
+ }
61
+ }
62
+ }
63
+ }
64
+ },
65
+ {
66
+ "why": "January is left empty, not 0 or an error",
67
+ "tool": "get_range",
68
+ "args": {
69
+ "range": "C2"
70
+ },
71
+ "expect": {
72
+ "values": [
73
+ [
74
+ ""
75
+ ]
76
+ ],
77
+ "errors": []
78
+ }
79
+ },
80
+ {
81
+ "why": "a header",
82
+ "tool": "get_range",
83
+ "args": {
84
+ "range": "C1"
85
+ },
86
+ "expect": {
87
+ "values": [
88
+ [
89
+ {
90
+ "$match": "\\S"
91
+ }
92
+ ]
93
+ ]
94
+ }
95
+ }
96
+ ],
97
+ "solution": [
98
+ {
99
+ "tool": "set_range",
100
+ "args": {
101
+ "start": "C1",
102
+ "values": [
103
+ [
104
+ "Growth"
105
+ ],
106
+ [
107
+ null
108
+ ],
109
+ [
110
+ "=B3/B2-1"
111
+ ],
112
+ [
113
+ "=B4/B3-1"
114
+ ],
115
+ [
116
+ "=B5/B4-1"
117
+ ],
118
+ [
119
+ "=B6/B5-1"
120
+ ],
121
+ [
122
+ "=B7/B6-1"
123
+ ]
124
+ ]
125
+ }
126
+ }
127
+ ]
128
+ }
@@ -0,0 +1,178 @@
1
+ {
2
+ "prompt": "Make a summary of total sales by region next to the data, starting at G1 (a header row, then one row per region), using formulas so it updates when the data changes.",
3
+ "fixture": {
4
+ "editor": "spreadsheet",
5
+ "rows": [
6
+ [
7
+ "Date",
8
+ "Region",
9
+ "Product",
10
+ "Amount"
11
+ ],
12
+ [
13
+ "2026-01-05",
14
+ "North",
15
+ "Widget",
16
+ 120
17
+ ],
18
+ [
19
+ "2026-01-07",
20
+ "South",
21
+ "Gadget",
22
+ 80
23
+ ],
24
+ [
25
+ "2026-01-12",
26
+ "North",
27
+ "Gadget",
28
+ 95
29
+ ],
30
+ [
31
+ "2026-01-15",
32
+ "East",
33
+ "Widget",
34
+ 60
35
+ ],
36
+ [
37
+ "2026-01-20",
38
+ "West",
39
+ "Widget",
40
+ 150
41
+ ],
42
+ [
43
+ "2026-02-02",
44
+ "South",
45
+ "Widget",
46
+ 110
47
+ ],
48
+ [
49
+ "2026-02-08",
50
+ "East",
51
+ "Gadget",
52
+ 70
53
+ ],
54
+ [
55
+ "2026-02-11",
56
+ "North",
57
+ "Widget",
58
+ 130
59
+ ],
60
+ [
61
+ "2026-02-19",
62
+ "West",
63
+ "Gadget",
64
+ 90
65
+ ],
66
+ [
67
+ "2026-02-25",
68
+ "South",
69
+ "Gadget",
70
+ 85
71
+ ]
72
+ ]
73
+ },
74
+ "checks": [
75
+ {
76
+ "why": "one row per region with the right totals",
77
+ "tool": "get_range",
78
+ "args": {
79
+ "range": "G2:H10"
80
+ },
81
+ "expect": {
82
+ "values": {
83
+ "$nonEmpty": {
84
+ "$length": 4,
85
+ "$expr": "[['North',345],['South',275],['East',130],['West',240]].every(([r, t]) => v.some((row) => row[0] === r && row[1] === t))"
86
+ }
87
+ }
88
+ }
89
+ },
90
+ {
91
+ "why": "the totals are formulas over the data, not typed numbers",
92
+ "tool": "get_range",
93
+ "args": {
94
+ "range": "H2:H5"
95
+ },
96
+ "expect": {
97
+ "raw": {
98
+ "$every": {
99
+ "$every": {
100
+ "$match": [
101
+ "^=.*(SUMIFS?|SUMPRODUCT|FILTER)",
102
+ "i"
103
+ ]
104
+ }
105
+ }
106
+ }
107
+ }
108
+ },
109
+ {
110
+ "why": "a header row",
111
+ "tool": "get_range",
112
+ "args": {
113
+ "range": "G1:H1"
114
+ },
115
+ "expect": {
116
+ "values": [
117
+ {
118
+ "$every": {
119
+ "$match": "\\S"
120
+ }
121
+ }
122
+ ]
123
+ }
124
+ },
125
+ {
126
+ "why": "no formula errors",
127
+ "tool": "get_range",
128
+ "args": {
129
+ "range": "G1:H10"
130
+ },
131
+ "expect": {
132
+ "errors": []
133
+ }
134
+ }
135
+ ],
136
+ "solution": [
137
+ {
138
+ "tool": "get_range",
139
+ "args": {
140
+ "range": "A1:D20"
141
+ }
142
+ },
143
+ {
144
+ "tool": "set_range",
145
+ "args": {
146
+ "start": "G1",
147
+ "values": [
148
+ [
149
+ "Region",
150
+ "Total sales"
151
+ ],
152
+ [
153
+ "North",
154
+ "=SUMIFS($D$2:$D$11,$B$2:$B$11,G2)"
155
+ ],
156
+ [
157
+ "South",
158
+ "=SUMIFS($D$2:$D$11,$B$2:$B$11,G3)"
159
+ ],
160
+ [
161
+ "East",
162
+ "=SUMIFS($D$2:$D$11,$B$2:$B$11,G4)"
163
+ ],
164
+ [
165
+ "West",
166
+ "=SUMIFS($D$2:$D$11,$B$2:$B$11,G5)"
167
+ ]
168
+ ]
169
+ }
170
+ },
171
+ {
172
+ "tool": "get_range",
173
+ "args": {
174
+ "range": "G1:H5"
175
+ }
176
+ }
177
+ ]
178
+ }
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: pixerate-spreadsheet-financial-model
3
+ description: The financial model workflow in a Pixerate spreadsheet - inputs (assumptions) in labelled cells kept apart from calculations and outputs, one consistent formula across each row, absolute references to inputs, no hard-coded numbers in formulas, projections that compound, scenario and sensitivity tables, checks that totals tie, and finding the hard-coded or broken cell in an existing model. Use when asked to build or check a financial model, forecast, projection, budget, business case, revenue or cost model, break-even, loan or growth calculation, scenario or what-if table, or to find why a model does not update, or when get_range and set_range are available. Load with pixerate-spreadsheet, which lists every tool.
4
+ metadata:
5
+ kind: workflow
6
+ reference: pixerate-spreadsheet
7
+ ---
8
+
9
+ # Building a financial model
10
+
11
+ A model is inputs, calculations and outputs. Inputs are the assumptions a person changes
12
+ (starting revenue, growth, prices); calculations turn them into numbers period by period;
13
+ outputs are what the reader looks at (totals, year-5 revenue, profit). Every calculation
14
+ refers to inputs by absolute reference and never contains a typed number, so changing one
15
+ assumption flows through everything.
16
+
17
+ This skill is the method. `pixerate-spreadsheet` has the full tool reference and functions.
18
+
19
+ ## Goal and quality bar
20
+
21
+ - Inputs live in their own labelled cells at the top (or on their own block), one assumption per
22
+ cell, with units in the label.
23
+ - Each row of the projection uses one formula pattern across its periods, referring to inputs
24
+ absolutely (`$B$2`) and to the previous period relatively (`B5`).
25
+ - No hard-coded numbers in calculation formulas (no `*1.08`, no `+5000`); constants that matter
26
+ are inputs.
27
+ - Outputs are formulas; checks tie (subtotals add to totals, a balance closes).
28
+ - No formula errors; the model gives the expected value for a case you can work out by hand.
29
+
30
+ ## Inputs to confirm
31
+
32
+ - **Assumptions** and their values and units (growth as 0.08 or 8 %, currency).
33
+ - **Periods** (years or months) and how many.
34
+ - **Layout** the user expects (where inputs, the projection and outputs go).
35
+ - **Scenarios** to compare (low, base, high).
36
+
37
+ ## Working method
38
+
39
+ 1. **Read** what exists: `get_range` the whole model area, `raw` as well as `values`.
40
+ **Check:** you can list the inputs, the calculation rows and the outputs.
41
+ 2. **Inputs block.** Labels in column A, values in B (`set_range` in one call). Rates as
42
+ fractions (0.08), money as plain numbers with the currency in the label.
43
+ 3. **Timeline.** A header row of periods (years 1-5 in B4:F4).
44
+ 4. **Calculation rows.** One `set_range` per row: the first period from an input (`=$B$1`),
45
+ later periods from the previous one and the input (`=B5*(1+$B$2)`), the same pattern across.
46
+ **Check:** `get_range` values match a hand calculation (100,000 at 8 % is 108,000, then
47
+ 116,640); `errors` empty.
48
+ 5. **Outputs and checks.** Totals (`=SUM(B5:F5)`), year-N values, and a check cell that should be
49
+ 0 or TRUE (`=SUM(B7:F7)-B9`).
50
+ 6. **Scenarios.** A small table with the scenario inputs in one column and the output formula
51
+ next to each, referring to the starting input absolutely and its own scenario cell
52
+ relatively (`=$B$1*(1+A9)^4` for year 5).
53
+ **Check:** the base scenario equals the main model's output.
54
+ 7. **Audit.** Read `raw` across every calculation row: each should match its neighbours' pattern;
55
+ a formula with a typed number or a different reference is the bug.
56
+
57
+ ## Decision table
58
+
59
+ | Need | Formula pattern |
60
+ | --- | --- |
61
+ | Compound growth period on period | `=prev*(1+$rate$)` |
62
+ | Value at period n directly | `=$start$*(1+$rate$)^(n-1)` |
63
+ | Cost as a share of revenue | `=revenue_cell*$cost_share$` |
64
+ | Fixed cost each period | `=$fixed_cost$` |
65
+ | Running total | `=prev_total+this_period` |
66
+ | Profit | `=revenue-costs` |
67
+ | Scenario output | Input in its own cell per scenario, output formula next to it |
68
+ | Something does not update | Find the formula with a typed number or a missing `$` |
69
+
70
+ ## Recipes
71
+
72
+ **Five-year projection.** `set_range` from A1: `[["Starting revenue", 100000], ["Growth", 0.08],
73
+ [], ["Year", 1, 2, 3, 4, 5], ["Revenue", "=$B$1", "=B5*(1+$B$2)", "=C5*(1+$B$2)",
74
+ "=D5*(1+$B$2)", "=E5*(1+$B$2)"]]`.
75
+
76
+ **Find a hard-coded number.** `get_range` the calculation rows; compare each `raw` with its
77
+ neighbours; replace the odd one with the pattern (`set_cell`).
78
+
79
+ **Scenarios.** From A8: `[["Growth", "Year 5 revenue"], [0.05, "=$B$1*(1+A9)^4"], [0.08,
80
+ "=$B$1*(1+A10)^4"], [0.12, "=$B$1*(1+A11)^4"]]`.
81
+
82
+ ## Pitfalls
83
+
84
+ - **Typed numbers in formulas** (`=C5*1.08`) do not follow the input; the classic broken model.
85
+ - **Relative input references** shift as formulas copy across; inputs need `$`.
86
+ - **Mixed patterns in a row** hide errors; one formula pattern per row.
87
+ - **Percent confusion**: 8 typed as a rate is 800 %; write 0.08 or `8%`.
88
+ - **Circular references** (`#CYCLE!`) from a total that includes itself.
89
+ - **`set_range` does not shift references** the way fill does; write each cell's own.
90
+ - **Outputs typed as values** break when an input changes.
91
+
92
+ ## Tools used
93
+
94
+ - `get_range`: read `raw` and `values`, check results and errors
95
+ - `set_range`: write the inputs, timeline, rows and scenario tables, one undo step each
96
+ - `set_cell`: fix one formula
97
+ - `insert_rows`, `insert_columns`: add periods or lines
98
+ - `undo_spreadsheet`: take back a step
@@ -0,0 +1,113 @@
1
+ {
2
+ "prompt": "Something in this revenue model doesn't update when I change the growth rate in B2. Find and fix it.",
3
+ "fixture": {
4
+ "editor": "spreadsheet",
5
+ "rows": [
6
+ [
7
+ "Starting revenue",
8
+ 100000,
9
+ null,
10
+ null,
11
+ null,
12
+ null
13
+ ],
14
+ [
15
+ "Growth",
16
+ 0.08,
17
+ null,
18
+ null,
19
+ null,
20
+ null
21
+ ],
22
+ [
23
+ null,
24
+ null,
25
+ null,
26
+ null,
27
+ null,
28
+ null
29
+ ],
30
+ [
31
+ "Year",
32
+ 1,
33
+ 2,
34
+ 3,
35
+ 4,
36
+ 5
37
+ ],
38
+ [
39
+ "Revenue",
40
+ "=$B$1",
41
+ "=B5*(1+$B$2)",
42
+ "=C5*1.08",
43
+ "=D5*(1+$B$2)",
44
+ "=E5*(1+$B$2)"
45
+ ]
46
+ ]
47
+ },
48
+ "checks": [
49
+ {
50
+ "why": "no typed growth rate left in the projection",
51
+ "tool": "get_range",
52
+ "args": {
53
+ "range": "C5:F5"
54
+ },
55
+ "expect": {
56
+ "raw": [
57
+ {
58
+ "$every": {
59
+ "$match": "^=.*\\$?B\\$?2",
60
+ "$not": {
61
+ "$match": "1\\.08"
62
+ }
63
+ }
64
+ }
65
+ ]
66
+ }
67
+ },
68
+ {
69
+ "why": "the values are unchanged at 8 %",
70
+ "tool": "get_range",
71
+ "args": {
72
+ "range": "F5"
73
+ },
74
+ "expect": {
75
+ "values": {
76
+ "$expr": "Math.abs(v[0][0] - 136048.896) < 0.01"
77
+ }
78
+ }
79
+ },
80
+ {
81
+ "why": "the inputs are untouched",
82
+ "tool": "get_range",
83
+ "args": {
84
+ "range": "B1:B2"
85
+ },
86
+ "expect": {
87
+ "values": [
88
+ [
89
+ 100000
90
+ ],
91
+ [
92
+ 0.08
93
+ ]
94
+ ]
95
+ }
96
+ }
97
+ ],
98
+ "solution": [
99
+ {
100
+ "tool": "get_range",
101
+ "args": {
102
+ "range": "A1:F5"
103
+ }
104
+ },
105
+ {
106
+ "tool": "set_cell",
107
+ "args": {
108
+ "cell": "D5",
109
+ "value": "=C5*(1+$B$2)"
110
+ }
111
+ }
112
+ ]
113
+ }