plotagents 0.0.0-stage → 0.5.0

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 (60) hide show
  1. package/AGENTS.md +78 -0
  2. package/LICENSE +21 -0
  3. package/README.md +205 -2
  4. package/bin/plotagents.js +49 -0
  5. package/dist/plotagents-geo.js +79 -0
  6. package/dist/plotagents.js +7954 -0
  7. package/docs/api.md +154 -0
  8. package/docs/charts.md +218 -0
  9. package/docs/getting-started.md +138 -0
  10. package/docs/rendering.md +47 -0
  11. package/docs/themes.md +86 -0
  12. package/index.d.ts +213 -0
  13. package/package.json +74 -4
  14. package/schema.json +895 -0
  15. package/src/axes.js +144 -0
  16. package/src/charts/bar.js +339 -0
  17. package/src/charts/categorical.js +471 -0
  18. package/src/charts/chord.js +218 -0
  19. package/src/charts/choropleth.js +559 -0
  20. package/src/charts/dist.js +513 -0
  21. package/src/charts/donut.js +232 -0
  22. package/src/charts/flowchart.js +407 -0
  23. package/src/charts/heatmap.js +76 -0
  24. package/src/charts/kpi.js +97 -0
  25. package/src/charts/line.js +419 -0
  26. package/src/charts/network.js +344 -0
  27. package/src/charts/pyramid.js +177 -0
  28. package/src/charts/radar.js +152 -0
  29. package/src/charts/regplot.js +364 -0
  30. package/src/charts/sankey.js +133 -0
  31. package/src/charts/scatter.js +136 -0
  32. package/src/charts/sunburst.js +135 -0
  33. package/src/charts/treemap.js +194 -0
  34. package/src/context.js +10 -0
  35. package/src/dashed.js +90 -0
  36. package/src/data.js +137 -0
  37. package/src/facet.js +84 -0
  38. package/src/format.js +171 -0
  39. package/src/frame.js +157 -0
  40. package/src/geo/chile.js +7 -0
  41. package/src/geo/data/chile-regiones.js +12 -0
  42. package/src/geo/data/usa-estados.js +12 -0
  43. package/src/geo/data/world.js +12 -0
  44. package/src/geo/projection.js +152 -0
  45. package/src/geo/registry.js +31 -0
  46. package/src/geo/usa.js +8 -0
  47. package/src/geo/world.js +7 -0
  48. package/src/hierarchy.js +139 -0
  49. package/src/index.js +271 -0
  50. package/src/infer.js +49 -0
  51. package/src/interact.js +362 -0
  52. package/src/lint.js +137 -0
  53. package/src/palette.js +59 -0
  54. package/src/runtime.js +304 -0
  55. package/src/scale.js +171 -0
  56. package/src/schema.js +181 -0
  57. package/src/stats.js +246 -0
  58. package/src/svg.js +85 -0
  59. package/src/text.js +61 -0
  60. package/src/theme.js +224 -0
package/AGENTS.md ADDED
@@ -0,0 +1,78 @@
1
+ # plotagents · guide for AI agents
2
+
3
+ One function, one JSON object. No design decisions: the library makes them.
4
+
5
+ ```js
6
+ import { plot, safePlot } from 'plotagents';
7
+ const svg = plot({ type, data, x, y, series, title, subtitle, source });
8
+ ```
9
+
10
+ ## Rules for a good chart
11
+
12
+ 1. **`title` is the finding**, a full sentence with the key number: "Biobío accounts for 30% of sales", not "Sales by region".
13
+ 2. **`subtitle` says how to read it**: measure, unit and period.
14
+ 3. **Always set `source`.** If the data is made up, use `source: "illustrative data"`.
15
+ 4. `data` is a list of rows (`[{ region: "Biobío", sales: 1240 }]`). For a wide table, pass `y: ["revenue", "costs"]`.
16
+ 5. Call `safePlot(spec)` as your tool: it never throws and returns `errors`, `warnings` and `suggestions`, each with a `fix`. If `ok` is `false`, fix the spec and call again.
17
+ 6. Set `locale: "en"` for English number formats (default is Spanish: `1.234,5`).
18
+
19
+ ## Which type
20
+
21
+ | To show | `type` | Key fields |
22
+ |---|---|---|
23
+ | Compare categories | `bar` | `x`, `y`, `series?`, `stack?` (`true` / `"percent"`), `top?` |
24
+ | Count rows per category | `countplot` | `x` |
25
+ | Mean per category with a CI | `barplot` or `point` | `x`, `y`, `hue?` |
26
+ | Change over time | `line` | `x` date (`"2024-03"`) or year, `y`, `series?`; `ci: true` with several observations per x |
27
+ | Volume over time | `area` | like `line`; stacks with `series` |
28
+ | Measures on different scales | `line` + `index: true`, or `facet` | **there is no dual axis** |
29
+ | Exponential growth | `line` + `yScale: "log"` | |
30
+ | Relationship between two measures | `regplot` (fit + CI) / `scatter` / `bubble` | `x`, `y`, `hue?`, `size?`, `label?` |
31
+ | Relationship + marginals | `jointplot` | `x`, `y` |
32
+ | Distribution of one measure | `hist` (`kde: true`), `kde`, `ecdf` | `x`, `hue?` |
33
+ | Distribution by category | `box`, `violin`, `strip`, `swarm` | `x` category, `y` measure, `hue?` |
34
+ | Multi-dimensional profile | `radar` | `axis`, `value`, `series` (≤ 4 series) |
35
+ | Hierarchical part-to-whole | `treemap` or `sunburst` | `path: [level1, level2, …]`, `value` |
36
+ | Part-to-whole, ≤ 6 parts | `donut` / `pie` | `label`, `value`; `half: true` for a semicircle |
37
+ | Magnitude on a grid | `heatmap` | `x`, `y`, `value` |
38
+ | A value per region, state or country | `choropleth` (`symbolmap` for totals) | `geo: "chile" \| "usa" \| "world"`, `key`, `value` |
39
+ | Who is connected to whom | `network` | edges `from`, `to`, `value?`; `nodes?` with `group` |
40
+ | Exchanges between a few groups | `chord` | `from`, `to`, `value` |
41
+ | Age structure | `pyramid` | `age`, `sex`, `value` |
42
+ | A process with decisions | `flowchart` | `nodes` (`type`, `lane`), `edges` (`label`) |
43
+ | Flows between stages | `sankey` | `from`, `to`, `value` |
44
+ | One or more numbers | `kpi` | `items: [{ label, value, delta, period, trend, target, good }]` |
45
+
46
+ seaborn names work (`histplot`, `kdeplot`, `boxplot`, `violinplot`, `lmplot`, `stripplot`, `swarmplot`, `pointplot`), and `hue` is an alias of `series`. Leave `type` out to infer it from the data.
47
+
48
+ ## Most useful options
49
+
50
+ - `highlight: "Ñuble"`: emphasize one category or series and mute the rest.
51
+ - `dashed` for what is not a firm number (estimates, projections, provisional figures): a name, a list, or a data field (`dashed: "provisional"`). Shapes keep their color, with a hatched fill and a dashed outline. On a time axis, `dashedFrom: 2025` dashes lines, areas and columns from that year on. **Always set `dashedLabel`** ("Projection", "Preliminary") so readers know what the hatching means.
52
+ - `facet: "region"` (+ `columns`): one panel per value, on a shared scale.
53
+ - `time: "year"`: one period at a time (the latest) with % change vs the previous one; in the browser, a slider with play (`slider: false` / `play: false` to turn them off). Treemaps can be colored by change with `colorBy: "change"`.
54
+ - `refLines: [{ y: 600, label: "Target" }]` and `annotations: [{ x: "2024-01", label: "New branch" }]`.
55
+ - `format: "percent" | "currency" | "compact" | "integer"` or `{ prefix, suffix, decimals }`.
56
+ - `xScale` / `yScale: "log"`.
57
+ - `theme: "noche"` or a JSON object (`{ extends, categorical, type, marks, font }`); `palette: [...]` for your own colors.
58
+ - `font: "Poppins"` and `textScale: 1.5` (presentation mode); `configure({ font, textScale })` sets them for every chart.
59
+ - `network` + `focus: "node id"`: center a node and highlight its neighbors.
60
+ - `geo: "usa"`: states match by postal code ("TX"), English or Spanish name, or FIPS ("48"). Alaska and Hawaii are insets; Puerto Rico appears only when your data has it. In the browser, maps zoom on click, Ctrl/⌘ + scroll or pinch.
61
+
62
+ ## Mistakes the library already handles
63
+
64
+ - More than 8 series → the 9th onward turn gray; `lint` suggests `highlight` or `facet`.
65
+ - Colliding line labels → spread apart with leader lines.
66
+ - Donut with more than 6 parts → folds into "Other".
67
+ - Long category labels → switches to horizontal.
68
+ - Hard-to-tell custom palette → `lint` names the offending pair.
69
+ - Numbers as text ("1.234,5", "12%", "$ 3.000") → parsed automatically.
70
+ - Tens of thousands of points → drawn as batched paths, still fast.
71
+
72
+ ## Outputs
73
+
74
+ - `plot(spec)` → SVG string. Drop it into HTML, Markdown, an `.svg` file or a PDF.
75
+ - `toHTML([spec1, spec2])` → self-contained page with tooltips, sliders and drill-down.
76
+ - `describe(spec)` → alt text for the chart.
77
+ - `schema` / `toolDefinition()` → JSON Schema and tool definition (Anthropic or OpenAI).
78
+ - CLI: `npx plotagents spec.json -o chart.svg`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Carlos Navarrete
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,206 @@
1
- # Temporary Holding Version
1
+ # plotagents
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Beautiful charts from a single JSON spec, built for AI agents.**
4
+
5
+ **[Documentation, live examples and playground →](https://carlosnavarrete.cl/plotagents/)**
6
+
7
+ Data and field names go in; an accessible SVG comes out, with careful typography, colorblind-safe colors and tooltips. 32 chart types: the ones people reach for in seaborn, multi-level treemaps and sunbursts, choropleth maps of Chile, the United States and the world, networks, chord diagrams, population pyramids and flow charts, plus time sliders, small multiples and fully JSON-configurable themes. Zero dependencies. Runs in Node, Deno, Bun and the browser.
8
+
9
+ ```bash
10
+ npm install github:carlosnavarreteliz/plotagents
11
+ ```
12
+
13
+ plotagents is not on the npm registry yet, so install it from GitHub for now; `npm install plotagents` will work once it is published.
14
+
15
+ ```js
16
+ import { plot } from 'plotagents';
17
+
18
+ const svg = plot({
19
+ title: 'Biobío accounts for 30% of southern sales',
20
+ subtitle: 'Sales by region, CLP millions, 2025',
21
+ source: 'illustrative data',
22
+ data: [
23
+ { region: 'Biobío', sales: 1240 },
24
+ { region: 'Araucanía', sales: 880 },
25
+ { region: 'Ñuble', sales: 610 },
26
+ ],
27
+ x: 'region',
28
+ y: 'sales',
29
+ locale: 'en',
30
+ });
31
+ ```
32
+
33
+ ![Columns](examples/svg/barras.svg)
34
+
35
+ The [interactive gallery](https://carlosnavarrete.cl/plotagents/examples/), 61 charts with the code for each one, is also in [`examples/index.html`](examples/index.html). Try any spec live in the [playground](https://carlosnavarrete.cl/plotagents/playground/).
36
+
37
+ ## Why
38
+
39
+ Agents are good at data and bad at charts: dual axes, 12-slice pies, titles that say nothing. plotagents takes design decisions out of the agent's hands. The rules come from data-visualization teaching for management (Wilke's *Fundamentals of Data Visualization*, the Data Viz Catalogue, the Atlas of Economic Complexity) and are written into the code:
40
+
41
+ - **The title states the finding.** `lint` nudges toward it and `describe` writes the alt text.
42
+ - **One axis.** There is no dual axis; `index: true` rebases series to 100 and `facet` draws panels on a shared scale.
43
+ - **Labels at the end of every line**, always. When they collide they are spread apart and joined to their point by a thin leader line; a legend appears only when needed.
44
+ - **Every pixel for the data.** The y axis sits inside the plot on its gridline; margins and type scale down on phones; treemaps, sunbursts, horizontal bars, heatmaps and KPI tiles size their own height.
45
+ - **Color follows the entity.** Eight validated colors in a fixed order; "Copper" keeps its color when you filter, drill down or move the time slider. One solid color per category.
46
+ - **Legend and chart are connected.** Hover a legend key to light up its marks, click to pin; line labels and tooltip headers wear their series color.
47
+ - **Estimates look like estimates.** `dashed` hatches the shapes that are projections or provisional figures (same color, striped fill, dashed outline) and `dashedFrom: 2025` turns lines, areas and columns dashed from that point on, over a soft labeled band. The legend says what the hatching means; hovering it lights up just those shapes.
48
+ - **Maps you can explore.** Click a state or region to zoom to it, Ctrl/⌘ + scroll or pinch to zoom, drag to pan; +/−/reset buttons for the keyboard. Hovering outlines the shape instead of dimming the map, and each color class in the legend lights up its regions. With a time slider, colors glide from one year to the next.
49
+ - **Networks you can explore.** Click a node and it glides to the center with its connections highlighted; click again or press Esc to go back.
50
+ - **Thin marks, quiet grid.** 4 px rounded data ends, 2.25 px lines, 2 px surface gaps instead of borders.
51
+ - **Locale-aware numbers.** Spanish (Chile) by default: `1.234,5`, `56,1%`, `$18.500`. `locale: 'en'` gives `1,234.5`.
52
+
53
+ ## Chart types
54
+
55
+ | Purpose | `type` (aliases) |
56
+ |---|---|
57
+ | Compare categories | `bar` (`barplot` with confidence intervals), `count` (`countplot`) |
58
+ | Change over time | `line` (`lineplot`, `ci` for a confidence band), `area` |
59
+ | Relationships | `scatter`, `bubble`, `regplot` (`lmplot`), `residplot`, `jointplot` |
60
+ | Profiles | `radar` (`spider`) |
61
+ | Distributions | `hist` (`histplot`), `kde` (`kdeplot`), `ecdf`, `box` (`boxplot`), `violin`, `strip`, `swarm` (`beeswarm`), `point` (`pointplot`) |
62
+ | Part-to-whole | `treemap` (any depth, with % change over time), `sunburst`, `donut`, `pie` |
63
+ | Maps | `choropleth` (Chile by region, US by state, world by country, any GeoJSON), `symbolmap` |
64
+ | Networks and flows | `network`, `chord`, `sankey`, `flowchart` (with swimlanes) |
65
+ | Populations | `pyramid` |
66
+ | Grids | `heatmap` |
67
+ | Numbers | `kpi` |
68
+
69
+ Leave `type` out and it is inferred: dates → `line`, two measures → `scatter`, `source`/`target` → `sankey`, `path` → `treemap`, categories → `bar`. Every option is documented in [`docs/charts.md`](docs/charts.md).
70
+
71
+ | | | |
72
+ |---|---|---|
73
+ | ![](examples/svg/lineas.svg) | ![](examples/svg/treemap-niveles.svg) | ![](examples/svg/regresion.svg) |
74
+ | ![](examples/svg/violin.svg) | ![](examples/svg/sunburst.svg) | ![](examples/svg/histograma.svg) |
75
+ | ![](examples/svg/radar.svg) | ![](examples/svg/sankey.svg) | ![](examples/svg/conjunto.svg) |
76
+ | ![](examples/svg/mapa-chile.svg) | ![](examples/svg/cuerdas.svg) | ![](examples/svg/piramide.svg) |
77
+
78
+ ## Time, panels and drill-down
79
+
80
+ ```js
81
+ // One period at a time with a slider and a play button (d3plus-style `time`).
82
+ plot({ type: 'treemap', data, path: ['sector', 'product'], value: 'value', time: 'year' });
83
+
84
+ // Time series: in the browser, render() adds a range slider with an overview.
85
+ render('#chart', { type: 'line', data, x: 'month', y: 'sales', series: 'region' });
86
+
87
+ // Small multiples (seaborn's col / col_wrap) with shared scales and colors.
88
+ plot({ type: 'area', data, x: 'month', y: 'sales', facet: 'region', columns: 3 });
89
+
90
+ // Treemaps and sunbursts of any depth; click a group to drill down.
91
+ plot({ type: 'sunburst', data, path: ['area', 'unit', 'item'], value: 'amount', focus: ['Teaching'] });
92
+
93
+ // Log scales with 1-2-5 ticks and no empty decades.
94
+ plot({ type: 'line', data, x: 'month', y: 'users', series: 'platform', yScale: 'log' });
95
+ ```
96
+
97
+ ## One switch for fonts and text size
98
+
99
+ ```js
100
+ import { configure } from 'plotagents';
101
+
102
+ configure({ font: 'Poppins', textScale: 1.5 }); // every chart from now on
103
+ plot({ ...spec, textScale: 2 }); // or per chart: presentation mode
104
+ configure(null); // back to defaults
105
+ ```
106
+
107
+ `font` takes a Google Fonts name (it is loaded for you in the browser and in `toHTML()`), a CSS stack or `{ family, import }`. `textScale` multiplies every text size and the layout is recomputed with the real sizes, so labels at ×2 still never overlap. `configure()` also accepts `locale`, `theme`, `palette` or any other spec option.
108
+
109
+ ## Themes are JSON
110
+
111
+ Everything visual is a JSON object merged over a base theme, so an agent can apply a brand without touching code. See [`docs/themes.md`](docs/themes.md).
112
+
113
+ ```js
114
+ plot({
115
+ ...spec,
116
+ theme: {
117
+ extends: 'papel', // or 'noche' (dark)
118
+ categorical: 'okabe-ito', // palette name or a list of colors in fixed order
119
+ surface: '#FFFFFF',
120
+ font: { family: "'Poppins', sans-serif" },
121
+ type: { title: 22, titleWeight: 700 },
122
+ marks: { lineWidth: 3, barRadius: 8, barMax: 44 },
123
+ axes: { inside: false },
124
+ },
125
+ });
126
+ ```
127
+
128
+ `validatePalette(colors)` checks a custom palette with the same tests the built-in one passed (lightness band, chroma floor, colorblind separation, normal-vision separation), and `lint` runs it for you.
129
+
130
+ ## SVG, not Canvas
131
+
132
+ plotagents renders SVG. Dense charts (more than 1,500 points) are drawn automatically as one path per color, with a nearest-point tooltip layer, which paints faster than Canvas while keeping vector output, server-side rendering and accessible text. The benchmark and the reasoning are in [`docs/rendering.md`](docs/rendering.md).
133
+
134
+ | Points | SVG, one node per point | Canvas | plotagents (batched SVG) |
135
+ |---|---|---|---|
136
+ | 5,000 | 62 ms | 30 ms | 12 ms |
137
+ | 20,000 | 671 ms | 100 ms | 38 ms |
138
+ | 50,000 | 1,468 ms | 357 ms | 123 ms |
139
+
140
+ ## API
141
+
142
+ ```js
143
+ import { plot, safePlot, lint, describe, render, toHTML, schema, toolDefinition, validatePalette } from 'plotagents';
144
+ ```
145
+
146
+ | Function | Returns |
147
+ |---|---|
148
+ | `plot(spec)` | SVG string. Throws `PlotError` with a fix hint. |
149
+ | `safePlot(spec)` | `{ ok, svg, description, errors, warnings, suggestions }`. Never throws. |
150
+ | `lint(spec)` | Review of the spec without drawing: errors and design suggestions. |
151
+ | `describe(spec)` | Alt text ("Highest: Biobío with 1,240 (30%)…"). |
152
+ | `render(el, spec)` | Browser: tooltips, responsive width, time sliders and drill-down. |
153
+ | `toHTML(specs)` | A self-contained, interactive HTML page in one file. |
154
+ | `schema` / `toolDefinition({ flavor })` | JSON Schema of the spec and a tool definition for Claude (`anthropic`) or OpenAI (`openai`). |
155
+ | `validatePalette(colors, opts)` | `{ ok, checks }`. |
156
+
157
+ In the browser without a bundler, `<script src="dist/plotagents.js"></script>` defines `window.plotagents`. Full types are in [`index.d.ts`](index.d.ts) and every option in [`schema.json`](schema.json).
158
+
159
+ ## As an agent tool
160
+
161
+ ```js
162
+ import Anthropic from '@anthropic-ai/sdk';
163
+ import { safePlot, toolDefinition } from 'plotagents';
164
+
165
+ const client = new Anthropic();
166
+ const res = await client.messages.create({
167
+ model: process.env.ANTHROPIC_MODEL, // the model you use
168
+ max_tokens: 4096,
169
+ tools: [toolDefinition()],
170
+ messages: [{ role: 'user', content: 'Plot the distribution of service times per clinic: …' }],
171
+ });
172
+ const call = res.content.find((b) => b.type === 'tool_use');
173
+ const { ok, svg, errors, suggestions } = safePlot(call.input);
174
+ // If !ok, send `errors` back as the tool_result: every error carries a `fix`.
175
+ ```
176
+
177
+ [`AGENTS.md`](AGENTS.md) is a short guide meant to be pasted into an agent's system prompt.
178
+
179
+ ## CLI
180
+
181
+ ```bash
182
+ npx plotagents spec.json -o chart.svg # SVG
183
+ npx plotagents specs.json -o report.html # several interactive charts on one page
184
+ npx plotagents spec.json --lint # review and suggestions
185
+ npx plotagents --tool # tool definition
186
+ ```
187
+
188
+ ## Built-in palette
189
+
190
+ | Theme | Surface | Categorical (fixed order) |
191
+ |---|---|---|
192
+ | `papel` | `#FCFBF9` | `#3A47B8` `#D42A5E` `#E8A33D` `#1E9C89` `#7B5CD6` `#B5562E` `#5BA3E0` `#8E9A2F` |
193
+ | `noche` | `#0B0F2B` | `#6170E0` `#E63369` `#C4851C` `#22A892` `#9780E8` `#D06A3E` `#499BDA` `#909D2D` |
194
+
195
+ Both pass the lightness, chroma, colorblind (adjacent ΔE OKLab ≥ 8) and normal-vision (≥ 15) checks. The order is part of the validation. Typeface: Mozilla Text, falling back to `system-ui`.
196
+
197
+ ## Development
198
+
199
+ ```bash
200
+ npm test # tests (node:test)
201
+ npm run build # dist/plotagents.js for the browser
202
+ npm run examples # regenerates examples/svg and examples/index.html
203
+ npm run site # builds the GitHub Pages site into _site/ (docs, gallery, playground)
204
+ ```
205
+
206
+ MIT © Carlos Navarrete
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ // CLI: plotagents spec.json [-o salida.svg|.html] [--lint] [--schema] [--tool]
3
+ // cat spec.json | plotagents > grafico.svg
4
+ import { readFileSync, writeFileSync } from 'node:fs';
5
+ import { plot, toHTML, lint, schema, toolDefinition, describe } from '../src/index.js';
6
+
7
+ const args = process.argv.slice(2);
8
+ const flag = (f) => args.includes(f);
9
+ const opt = (f) => { const i = args.indexOf(f); return i >= 0 ? args[i + 1] : undefined; };
10
+ const input = args.find((a, i) => !a.startsWith('-') && args[i - 1] !== '-o' && args[i - 1] !== '--out');
11
+
12
+ if (flag('-h') || flag('--help')) {
13
+ console.log(`plotagents — gráficos bellos desde un spec JSON
14
+
15
+ Uso:
16
+ plotagents spec.json SVG a la salida estándar
17
+ plotagents spec.json -o grafico.svg guarda SVG
18
+ plotagents spec.json -o reporte.html página HTML interactiva (acepta un arreglo de specs)
19
+ plotagents spec.json --lint revisa el spec y sugiere mejoras (JSON)
20
+ plotagents spec.json --describe descripción en texto del gráfico
21
+ plotagents --schema JSON Schema del spec
22
+ plotagents --tool [openai] definición de herramienta para agentes
23
+ cat spec.json | plotagents también lee de stdin`);
24
+ process.exit(0);
25
+ }
26
+ if (flag('--schema')) { console.log(JSON.stringify(schema, null, 2)); process.exit(0); }
27
+ if (flag('--tool')) { console.log(JSON.stringify(toolDefinition({ flavor: opt('--tool') === 'openai' ? 'openai' : 'anthropic' }), null, 2)); process.exit(0); }
28
+
29
+ let raw;
30
+ try { raw = input ? readFileSync(input, 'utf8') : readFileSync(0, 'utf8'); } catch (e) { console.error('No pude leer el spec:', e.message); process.exit(2); }
31
+ let spec;
32
+ try { spec = JSON.parse(raw); } catch (e) { console.error('El spec no es JSON válido:', e.message); process.exit(2); }
33
+
34
+ if (flag('--lint')) {
35
+ const list = Array.isArray(spec) ? spec : [spec];
36
+ const out = list.map(lint);
37
+ console.log(JSON.stringify(Array.isArray(spec) ? out : out[0], null, 2));
38
+ process.exit(out.every((r) => r.ok) ? 0 : 1);
39
+ }
40
+ try {
41
+ if (flag('--describe')) { console.log((Array.isArray(spec) ? spec : [spec]).map(describe).join('\n')); process.exit(0); }
42
+ const outFile = opt('-o') ?? opt('--out');
43
+ const html = (outFile && outFile.endsWith('.html')) || flag('--html') || Array.isArray(spec);
44
+ const result = html ? toHTML(spec, { title: (Array.isArray(spec) ? spec[0] : spec).title || 'plotagents' }) : plot(spec);
45
+ if (outFile) { writeFileSync(outFile, result); console.error(`✓ ${outFile}`); } else process.stdout.write(result);
46
+ } catch (e) {
47
+ console.error(e.message);
48
+ process.exit(1);
49
+ }