@ggterm/core 0.3.1 → 0.3.3
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/cli-plot.js +208 -217
- package/dist/init.d.ts.map +1 -1
- package/dist/serve.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli-plot.js
CHANGED
|
@@ -14602,7 +14602,6 @@ __export(exports_serve, {
|
|
|
14602
14602
|
import { watch, writeFileSync as writeFileSync3, unlinkSync } from "fs";
|
|
14603
14603
|
import { join as join3 } from "path";
|
|
14604
14604
|
import { createServer } from "http";
|
|
14605
|
-
import { createHash } from "crypto";
|
|
14606
14605
|
import { spawn } from "child_process";
|
|
14607
14606
|
function plotToVegaLite(plot) {
|
|
14608
14607
|
const geomTypes = plot._provenance.geomTypes;
|
|
@@ -14620,27 +14619,6 @@ function getLatestPayload() {
|
|
|
14620
14619
|
const { spec, provenance } = plotToVegaLite(plot);
|
|
14621
14620
|
return JSON.stringify({ type: "plot", spec, provenance });
|
|
14622
14621
|
}
|
|
14623
|
-
function encodeWebSocketFrame(data) {
|
|
14624
|
-
const payload = Buffer.from(data, "utf-8");
|
|
14625
|
-
const len = payload.length;
|
|
14626
|
-
let header;
|
|
14627
|
-
if (len < 126) {
|
|
14628
|
-
header = Buffer.alloc(2);
|
|
14629
|
-
header[0] = 129;
|
|
14630
|
-
header[1] = len;
|
|
14631
|
-
} else if (len < 65536) {
|
|
14632
|
-
header = Buffer.alloc(4);
|
|
14633
|
-
header[0] = 129;
|
|
14634
|
-
header[1] = 126;
|
|
14635
|
-
header.writeUInt16BE(len, 2);
|
|
14636
|
-
} else {
|
|
14637
|
-
header = Buffer.alloc(10);
|
|
14638
|
-
header[0] = 129;
|
|
14639
|
-
header[1] = 127;
|
|
14640
|
-
header.writeBigUInt64BE(BigInt(len), 2);
|
|
14641
|
-
}
|
|
14642
|
-
return Buffer.concat([header, payload]);
|
|
14643
|
-
}
|
|
14644
14622
|
function jsonResponse(res, data, status = 200) {
|
|
14645
14623
|
const body = JSON.stringify(data);
|
|
14646
14624
|
res.writeHead(status, { "content-type": "application/json" });
|
|
@@ -14661,10 +14639,12 @@ function handleServe(port) {
|
|
|
14661
14639
|
const payload = getLatestPayload();
|
|
14662
14640
|
if (!payload)
|
|
14663
14641
|
return;
|
|
14664
|
-
const
|
|
14642
|
+
const sseData = `data: ${payload}
|
|
14643
|
+
|
|
14644
|
+
`;
|
|
14665
14645
|
for (const client of clients) {
|
|
14666
14646
|
try {
|
|
14667
|
-
client.write(
|
|
14647
|
+
client.write(sseData);
|
|
14668
14648
|
} catch {
|
|
14669
14649
|
clients.delete(client);
|
|
14670
14650
|
}
|
|
@@ -14673,6 +14653,21 @@ function handleServe(port) {
|
|
|
14673
14653
|
});
|
|
14674
14654
|
const server = createServer((req, res) => {
|
|
14675
14655
|
const url = new URL(req.url || "/", `http://localhost:${p}`);
|
|
14656
|
+
if (url.pathname === "/events") {
|
|
14657
|
+
res.writeHead(200, {
|
|
14658
|
+
"content-type": "text/event-stream",
|
|
14659
|
+
"cache-control": "no-cache",
|
|
14660
|
+
connection: "keep-alive"
|
|
14661
|
+
});
|
|
14662
|
+
clients.add(res);
|
|
14663
|
+
const payload = getLatestPayload();
|
|
14664
|
+
if (payload)
|
|
14665
|
+
res.write(`data: ${payload}
|
|
14666
|
+
|
|
14667
|
+
`);
|
|
14668
|
+
req.on("close", () => clients.delete(res));
|
|
14669
|
+
return;
|
|
14670
|
+
}
|
|
14676
14671
|
if (url.pathname === "/api/latest") {
|
|
14677
14672
|
const payload = getLatestPayload();
|
|
14678
14673
|
if (!payload)
|
|
@@ -14698,53 +14693,14 @@ function handleServe(port) {
|
|
|
14698
14693
|
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
14699
14694
|
res.end(CLIENT_HTML);
|
|
14700
14695
|
});
|
|
14701
|
-
server.on("
|
|
14702
|
-
|
|
14703
|
-
|
|
14704
|
-
|
|
14705
|
-
|
|
14706
|
-
|
|
14707
|
-
const key = req.headers["sec-websocket-key"];
|
|
14708
|
-
if (!key) {
|
|
14709
|
-
socket.destroy();
|
|
14710
|
-
return;
|
|
14696
|
+
server.on("error", (err) => {
|
|
14697
|
+
if (err.code === "EADDRINUSE") {
|
|
14698
|
+
console.error(`Port ${p} is already in use.`);
|
|
14699
|
+
console.error(`Kill the existing server: lsof -ti:${p} | xargs kill`);
|
|
14700
|
+
console.error(`Or use a different port: npx ggterm-plot serve ${p + 1}`);
|
|
14701
|
+
process.exit(1);
|
|
14711
14702
|
}
|
|
14712
|
-
|
|
14713
|
-
socket.write(`HTTP/1.1 101 Switching Protocols\r
|
|
14714
|
-
` + `Upgrade: websocket\r
|
|
14715
|
-
` + `Connection: Upgrade\r
|
|
14716
|
-
` + `Sec-WebSocket-Accept: ${accept}\r
|
|
14717
|
-
` + `\r
|
|
14718
|
-
`);
|
|
14719
|
-
clients.add(socket);
|
|
14720
|
-
const payload = getLatestPayload();
|
|
14721
|
-
if (payload)
|
|
14722
|
-
socket.write(encodeWebSocketFrame(payload));
|
|
14723
|
-
socket.on("close", () => clients.delete(socket));
|
|
14724
|
-
socket.on("error", () => clients.delete(socket));
|
|
14725
|
-
socket.on("data", (data) => {
|
|
14726
|
-
if (data.length < 2)
|
|
14727
|
-
return;
|
|
14728
|
-
const opcode = data[0] & 15;
|
|
14729
|
-
if (opcode === 8) {
|
|
14730
|
-
const closeFrame = Buffer.alloc(2);
|
|
14731
|
-
closeFrame[0] = 136;
|
|
14732
|
-
closeFrame[1] = 0;
|
|
14733
|
-
try {
|
|
14734
|
-
socket.write(closeFrame);
|
|
14735
|
-
} catch {}
|
|
14736
|
-
socket.end();
|
|
14737
|
-
clients.delete(socket);
|
|
14738
|
-
return;
|
|
14739
|
-
}
|
|
14740
|
-
if (opcode === 9) {
|
|
14741
|
-
const pong = Buffer.from(data);
|
|
14742
|
-
pong[0] = pong[0] & 240 | 10;
|
|
14743
|
-
try {
|
|
14744
|
-
socket.write(pong);
|
|
14745
|
-
} catch {}
|
|
14746
|
-
}
|
|
14747
|
-
});
|
|
14703
|
+
throw err;
|
|
14748
14704
|
});
|
|
14749
14705
|
server.listen(p, () => {
|
|
14750
14706
|
const url = `http://localhost:${p}`;
|
|
@@ -15034,7 +14990,6 @@ let history = [];
|
|
|
15034
14990
|
let historyIndex = {};
|
|
15035
14991
|
let currentIdx = -1;
|
|
15036
14992
|
let view = null;
|
|
15037
|
-
let ws = null;
|
|
15038
14993
|
|
|
15039
14994
|
function updateMeta(prov) {
|
|
15040
14995
|
if (!prov) return;
|
|
@@ -15192,17 +15147,12 @@ function downloadPNG() {
|
|
|
15192
15147
|
}
|
|
15193
15148
|
|
|
15194
15149
|
function connect() {
|
|
15195
|
-
const
|
|
15196
|
-
ws = new WebSocket(proto + '//' + location.host + '/ws');
|
|
15150
|
+
const es = new EventSource('/events');
|
|
15197
15151
|
|
|
15198
|
-
|
|
15199
|
-
|
|
15200
|
-
statusEl.classList.remove('connected');
|
|
15201
|
-
setTimeout(connect, 2000);
|
|
15202
|
-
};
|
|
15203
|
-
ws.onerror = () => ws.close();
|
|
15152
|
+
es.onopen = () => { statusEl.classList.add('connected'); };
|
|
15153
|
+
es.onerror = () => { statusEl.classList.remove('connected'); };
|
|
15204
15154
|
|
|
15205
|
-
|
|
15155
|
+
es.onmessage = (e) => {
|
|
15206
15156
|
const data = JSON.parse(e.data);
|
|
15207
15157
|
if (data.type === 'plot') {
|
|
15208
15158
|
history.push(data);
|
|
@@ -15242,7 +15192,7 @@ import { join as join4 } from "path";
|
|
|
15242
15192
|
// src/init.ts
|
|
15243
15193
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync2, existsSync as existsSync2, readdirSync as readdirSync2, statSync } from "fs";
|
|
15244
15194
|
import { join as join2, extname } from "path";
|
|
15245
|
-
var SKILLS_VERSION = "0.2
|
|
15195
|
+
var SKILLS_VERSION = "0.3.2";
|
|
15246
15196
|
var SKILLS = {
|
|
15247
15197
|
"data-load": {
|
|
15248
15198
|
files: {
|
|
@@ -15256,73 +15206,41 @@ allowed-tools: Bash(npx:*), Read, Write
|
|
|
15256
15206
|
|
|
15257
15207
|
Load data into arrays of records for use with ggterm plotting and analysis.
|
|
15258
15208
|
|
|
15259
|
-
##
|
|
15260
|
-
|
|
15261
|
-
### CSV
|
|
15262
|
-
|
|
15263
|
-
\`\`\`typescript
|
|
15264
|
-
import { parse } from 'csv-parse/sync'
|
|
15265
|
-
import { readFileSync } from 'fs'
|
|
15209
|
+
## Built-in Datasets (No files needed!)
|
|
15266
15210
|
|
|
15267
|
-
|
|
15268
|
-
const data = parse(text, {
|
|
15269
|
-
columns: true, // First row as headers
|
|
15270
|
-
cast: true, // Auto-convert numbers
|
|
15271
|
-
skip_empty_lines: true
|
|
15272
|
-
})
|
|
15273
|
-
\`\`\`
|
|
15211
|
+
ggterm includes built-in datasets that can be used directly by name:
|
|
15274
15212
|
|
|
15275
|
-
|
|
15213
|
+
| Dataset | Rows | Columns |
|
|
15214
|
+
|---------|------|---------|
|
|
15215
|
+
| \`iris\` | 150 | sepal_length, sepal_width, petal_length, petal_width, species |
|
|
15216
|
+
| \`mtcars\` | 16 | mpg, cyl, hp, wt, name |
|
|
15276
15217
|
|
|
15277
|
-
|
|
15278
|
-
import { csvParse, autoType } from 'd3-dsv'
|
|
15218
|
+
Use them directly in plot commands:
|
|
15279
15219
|
|
|
15280
|
-
|
|
15220
|
+
\`\`\`bash
|
|
15221
|
+
npx ggterm-plot iris sepal_length sepal_width species "Iris Dataset" point
|
|
15222
|
+
npx ggterm-plot mtcars mpg hp cyl "Motor Trend Cars" point
|
|
15281
15223
|
\`\`\`
|
|
15282
15224
|
|
|
15283
|
-
|
|
15225
|
+
**IMPORTANT**: When the user asks about iris, mtcars, or bundled/built-in datasets, use these names directly with \`npx ggterm-plot\`. Do NOT try to generate CSV files or install Python packages.
|
|
15284
15226
|
|
|
15285
|
-
|
|
15286
|
-
import { readFileSync } from 'fs'
|
|
15287
|
-
|
|
15288
|
-
// JSON array
|
|
15289
|
-
const data = JSON.parse(readFileSync('data.json', 'utf-8'))
|
|
15290
|
-
\`\`\`
|
|
15227
|
+
## External Files
|
|
15291
15228
|
|
|
15292
|
-
|
|
15293
|
-
|
|
15294
|
-
|
|
15295
|
-
|
|
15296
|
-
.trim()
|
|
15297
|
-
.split('\\n')
|
|
15298
|
-
.map(line => JSON.parse(line))
|
|
15229
|
+
\`\`\`bash
|
|
15230
|
+
npx ggterm-plot data.csv x_column y_column color_column "Title" point
|
|
15231
|
+
npx ggterm-plot data.json x_column y_column
|
|
15232
|
+
npx ggterm-plot data.jsonl x_column y_column
|
|
15299
15233
|
\`\`\`
|
|
15300
15234
|
|
|
15301
15235
|
## Verification
|
|
15302
15236
|
|
|
15303
|
-
|
|
15237
|
+
Inspect any data file to see columns and types:
|
|
15304
15238
|
|
|
15305
|
-
\`\`\`
|
|
15306
|
-
|
|
15307
|
-
console.log('Columns:', Object.keys(data[0]))
|
|
15308
|
-
console.log('Sample row:', data[0])
|
|
15239
|
+
\`\`\`bash
|
|
15240
|
+
npx ggterm-plot inspect data.csv
|
|
15309
15241
|
\`\`\`
|
|
15310
15242
|
|
|
15311
|
-
|
|
15312
|
-
|
|
15313
|
-
Once data is loaded, pass directly to ggterm:
|
|
15314
|
-
|
|
15315
|
-
\`\`\`typescript
|
|
15316
|
-
import { gg, geom_point } from '@ggterm/core'
|
|
15317
|
-
|
|
15318
|
-
const data = loadData('measurements.csv')
|
|
15319
|
-
|
|
15320
|
-
const plot = gg(data)
|
|
15321
|
-
.aes({ x: 'time', y: 'value' })
|
|
15322
|
-
.geom(geom_point())
|
|
15323
|
-
|
|
15324
|
-
console.log(plot.render({ width: 80, height: 24 }))
|
|
15325
|
-
\`\`\`
|
|
15243
|
+
$ARGUMENTS
|
|
15326
15244
|
`
|
|
15327
15245
|
}
|
|
15328
15246
|
},
|
|
@@ -15336,62 +15254,77 @@ allowed-tools: Bash(npx:ggterm-plot*), Read
|
|
|
15336
15254
|
|
|
15337
15255
|
# Terminal Plotting with ggterm
|
|
15338
15256
|
|
|
15339
|
-
Create plots using the CLI tool.
|
|
15257
|
+
Create plots using the CLI tool. Supports both built-in datasets and external files.
|
|
15340
15258
|
|
|
15341
|
-
##
|
|
15259
|
+
## Built-in Datasets
|
|
15260
|
+
|
|
15261
|
+
ggterm includes datasets that work by name — no CSV files needed:
|
|
15262
|
+
|
|
15263
|
+
| Dataset | Rows | Columns |
|
|
15264
|
+
|---------|------|---------|
|
|
15265
|
+
| \`iris\` | 150 | sepal_length, sepal_width, petal_length, petal_width, species |
|
|
15266
|
+
| \`mtcars\` | 16 | mpg, cyl, hp, wt, name |
|
|
15342
15267
|
|
|
15343
15268
|
\`\`\`bash
|
|
15344
|
-
npx ggterm-plot
|
|
15269
|
+
npx ggterm-plot iris sepal_length sepal_width species "Iris" point
|
|
15270
|
+
npx ggterm-plot mtcars mpg hp cyl "Cars" point
|
|
15345
15271
|
\`\`\`
|
|
15346
15272
|
|
|
15347
|
-
|
|
15273
|
+
**IMPORTANT**: When the user mentions iris, mtcars, or asks for demo/sample data, use these built-in names directly. Do NOT look for CSV files or generate data.
|
|
15274
|
+
|
|
15275
|
+
## Live Plot Viewer
|
|
15348
15276
|
|
|
15349
|
-
|
|
15277
|
+
Start the companion viewer for high-resolution interactive plots:
|
|
15350
15278
|
|
|
15351
15279
|
\`\`\`bash
|
|
15352
|
-
npx ggterm-plot
|
|
15280
|
+
npx ggterm-plot serve # default port 4242
|
|
15281
|
+
npx ggterm-plot serve 8080 # custom port
|
|
15353
15282
|
\`\`\`
|
|
15354
15283
|
|
|
15355
|
-
|
|
15284
|
+
When serve is running, plots auto-display in the browser/Wave panel instead of ASCII art.
|
|
15356
15285
|
|
|
15357
|
-
##
|
|
15286
|
+
## CLI Command
|
|
15358
15287
|
|
|
15359
15288
|
\`\`\`bash
|
|
15360
|
-
npx ggterm-plot <data
|
|
15289
|
+
npx ggterm-plot <data> <x> <y> [color] [title] [geom]
|
|
15361
15290
|
\`\`\`
|
|
15362
15291
|
|
|
15363
15292
|
Arguments:
|
|
15364
|
-
- \`data
|
|
15293
|
+
- \`data\` - Built-in dataset name (\`iris\`, \`mtcars\`) OR path to CSV/JSON/JSONL file
|
|
15365
15294
|
- \`x\` - Column name for x-axis
|
|
15366
15295
|
- \`y\` - Column name for y-axis (use \`-\` for histogram)
|
|
15367
15296
|
- \`color\` - Column name for color (optional, use \`-\` to skip)
|
|
15368
15297
|
- \`title\` - Plot title (optional, use \`-\` to skip)
|
|
15369
|
-
- \`geom\` - Geometry type: \`point\` (default), \`line\`, \`histogram\`, \`boxplot\`, \`bar\`, \`violin\`, \`area\`, etc.
|
|
15298
|
+
- \`geom\` - Geometry type: \`point\` (default), \`line\`, \`histogram\`, \`boxplot\`, \`bar\`, \`violin\`, \`density\`, \`area\`, etc.
|
|
15370
15299
|
|
|
15371
|
-
##
|
|
15300
|
+
## Inspect & Suggest (for external files)
|
|
15372
15301
|
|
|
15373
|
-
Scatter plot:
|
|
15374
15302
|
\`\`\`bash
|
|
15375
|
-
npx ggterm-plot data.csv
|
|
15303
|
+
npx ggterm-plot inspect <data.csv>
|
|
15304
|
+
npx ggterm-plot suggest <data.csv>
|
|
15376
15305
|
\`\`\`
|
|
15377
15306
|
|
|
15378
|
-
|
|
15307
|
+
## Examples
|
|
15308
|
+
|
|
15309
|
+
Built-in data:
|
|
15379
15310
|
\`\`\`bash
|
|
15380
|
-
npx ggterm-plot
|
|
15311
|
+
npx ggterm-plot iris sepal_length sepal_width species "Iris Dataset" point
|
|
15312
|
+
npx ggterm-plot iris petal_length - species "Petal Length" histogram
|
|
15313
|
+
npx ggterm-plot mtcars mpg hp cyl "MPG vs HP" point
|
|
15381
15314
|
\`\`\`
|
|
15382
15315
|
|
|
15383
|
-
|
|
15316
|
+
External files:
|
|
15384
15317
|
\`\`\`bash
|
|
15385
|
-
npx ggterm-plot data.csv
|
|
15318
|
+
npx ggterm-plot data.csv x y color "Title" point
|
|
15319
|
+
npx ggterm-plot data.json date value - "Time Series" line
|
|
15386
15320
|
\`\`\`
|
|
15387
15321
|
|
|
15388
15322
|
## Workflow
|
|
15389
15323
|
|
|
15390
|
-
1.
|
|
15391
|
-
2.
|
|
15392
|
-
3. Run
|
|
15393
|
-
4.
|
|
15394
|
-
5. Briefly describe what the plot shows
|
|
15324
|
+
1. If user asks for iris/mtcars/demo data, use built-in dataset names directly
|
|
15325
|
+
2. For external files: run \`inspect\` to see columns, then \`suggest\` for recommendations
|
|
15326
|
+
3. Run the plot command
|
|
15327
|
+
4. Briefly describe what the plot shows
|
|
15395
15328
|
|
|
15396
15329
|
$ARGUMENTS
|
|
15397
15330
|
|
|
@@ -15404,6 +15337,11 @@ $ARGUMENTS
|
|
|
15404
15337
|
| Distribution of 1 variable | \`histogram\` | Frequency distribution |
|
|
15405
15338
|
| Distribution by group | \`boxplot\` | Compare medians |
|
|
15406
15339
|
| Category comparison | \`bar\` | Counts per category |
|
|
15340
|
+
| Smoothed distribution | \`density\` | Kernel density estimate |
|
|
15341
|
+
| Density shape | \`violin\` | Distribution shape |
|
|
15342
|
+
| Stacked distributions | \`ridgeline\` | Joy plot |
|
|
15343
|
+
| Filled region | \`area\` | Cumulative or stacked |
|
|
15344
|
+
| Cumulative distribution | \`ecdf\` | Empirical CDF |
|
|
15407
15345
|
`
|
|
15408
15346
|
}
|
|
15409
15347
|
},
|
|
@@ -15524,7 +15462,7 @@ $ARGUMENTS
|
|
|
15524
15462
|
"SKILL.md": `---
|
|
15525
15463
|
name: ggterm-customize
|
|
15526
15464
|
description: Customize plot aesthetics using natural language. Use when the user wants to change colors, fonts, titles, labels, themes, or any visual aspect of a plot before publication.
|
|
15527
|
-
allowed-tools: Read, Write
|
|
15465
|
+
allowed-tools: Read, Write, Bash(npx:*)
|
|
15528
15466
|
---
|
|
15529
15467
|
|
|
15530
15468
|
# Natural Language Plot Customization
|
|
@@ -15581,7 +15519,7 @@ $ARGUMENTS
|
|
|
15581
15519
|
"SKILL.md": `---
|
|
15582
15520
|
name: ggterm-style
|
|
15583
15521
|
description: Apply publication-quality style presets to plots. Use when the user wants to style a plot like Wilke, Tufte, Nature, The Economist, or apply minimal/publication styling.
|
|
15584
|
-
allowed-tools: Read, Write
|
|
15522
|
+
allowed-tools: Read, Write, Bash(npx:*)
|
|
15585
15523
|
---
|
|
15586
15524
|
|
|
15587
15525
|
# Plot Style Presets
|
|
@@ -15606,47 +15544,6 @@ Apply expert-curated style presets to Vega-Lite specifications for publication-q
|
|
|
15606
15544
|
3. Write the updated spec
|
|
15607
15545
|
4. Inform user they can export with \`/ggterm-publish\`
|
|
15608
15546
|
|
|
15609
|
-
## Style Configurations
|
|
15610
|
-
|
|
15611
|
-
### Wilke Style (Recommended Default)
|
|
15612
|
-
|
|
15613
|
-
\`\`\`javascript
|
|
15614
|
-
const wilkeStyle = {
|
|
15615
|
-
config: {
|
|
15616
|
-
font: "Helvetica Neue, Helvetica, Arial, sans-serif",
|
|
15617
|
-
background: "white",
|
|
15618
|
-
view: { stroke: null },
|
|
15619
|
-
title: { fontSize: 14, fontWeight: "normal", anchor: "start" },
|
|
15620
|
-
axis: { grid: false, labelFontSize: 11, titleFontSize: 12 },
|
|
15621
|
-
axisY: { grid: true, gridColor: "#ebebeb" }
|
|
15622
|
-
}
|
|
15623
|
-
}
|
|
15624
|
-
\`\`\`
|
|
15625
|
-
|
|
15626
|
-
### Tufte Style
|
|
15627
|
-
|
|
15628
|
-
\`\`\`javascript
|
|
15629
|
-
const tufteStyle = {
|
|
15630
|
-
config: {
|
|
15631
|
-
font: "Georgia, serif",
|
|
15632
|
-
view: { stroke: null },
|
|
15633
|
-
axis: { domain: false, grid: false, ticks: false }
|
|
15634
|
-
}
|
|
15635
|
-
}
|
|
15636
|
-
\`\`\`
|
|
15637
|
-
|
|
15638
|
-
### Economist Style
|
|
15639
|
-
|
|
15640
|
-
\`\`\`javascript
|
|
15641
|
-
const economistStyle = {
|
|
15642
|
-
config: {
|
|
15643
|
-
background: "#d5e4eb",
|
|
15644
|
-
axis: { grid: true, gridColor: "#ffffff" },
|
|
15645
|
-
axisX: { grid: false, domain: true }
|
|
15646
|
-
}
|
|
15647
|
-
}
|
|
15648
|
-
\`\`\`
|
|
15649
|
-
|
|
15650
15547
|
## Response Format
|
|
15651
15548
|
|
|
15652
15549
|
After applying a style:
|
|
@@ -15683,18 +15580,6 @@ Generate analysis reports with embedded terminal visualizations.
|
|
|
15683
15580
|
3. **Visualizations** - Embedded plots with interpretations
|
|
15684
15581
|
4. **Findings** - Key insights from the analysis
|
|
15685
15582
|
|
|
15686
|
-
## Embedding Plots
|
|
15687
|
-
|
|
15688
|
-
\`\`\`markdown
|
|
15689
|
-
## Visualization
|
|
15690
|
-
|
|
15691
|
-
\\\`\\\`\\\`
|
|
15692
|
-
[terminal plot output here]
|
|
15693
|
-
\\\`\\\`\\\`
|
|
15694
|
-
|
|
15695
|
-
**Interpretation**: The scatter plot shows [describe the relationship observed].
|
|
15696
|
-
\`\`\`
|
|
15697
|
-
|
|
15698
15583
|
## Width Guidelines
|
|
15699
15584
|
|
|
15700
15585
|
| Context | Width | Height |
|
|
@@ -15824,6 +15709,63 @@ function handleInit() {
|
|
|
15824
15709
|
console.log(`ggterm v${SKILLS_VERSION} ready`);
|
|
15825
15710
|
console.log("");
|
|
15826
15711
|
}
|
|
15712
|
+
const claudeMdPath = join2(cwd, "CLAUDE.md");
|
|
15713
|
+
const claudeMdContent = `# ggterm Data Analysis Project
|
|
15714
|
+
|
|
15715
|
+
This project uses ggterm (@ggterm/core) for data visualization.
|
|
15716
|
+
|
|
15717
|
+
## IMPORTANT: Built-in Datasets
|
|
15718
|
+
|
|
15719
|
+
ggterm has built-in datasets that work by name — NO CSV files or Python packages needed:
|
|
15720
|
+
|
|
15721
|
+
\`\`\`bash
|
|
15722
|
+
npx ggterm-plot iris sepal_length sepal_width species "Iris" point
|
|
15723
|
+
npx ggterm-plot mtcars mpg hp cyl "Cars" point
|
|
15724
|
+
\`\`\`
|
|
15725
|
+
|
|
15726
|
+
| Dataset | Rows | Columns |
|
|
15727
|
+
|---------|------|---------|
|
|
15728
|
+
| \`iris\` | 150 | sepal_length, sepal_width, petal_length, petal_width, species |
|
|
15729
|
+
| \`mtcars\` | 16 | mpg, cyl, hp, wt, name |
|
|
15730
|
+
|
|
15731
|
+
When asked about iris, mtcars, or sample data, use these names directly as the first argument to \`npx ggterm-plot\`. Do NOT search for CSV files or generate data.
|
|
15732
|
+
|
|
15733
|
+
## Plotting Commands
|
|
15734
|
+
|
|
15735
|
+
\`\`\`bash
|
|
15736
|
+
npx ggterm-plot <data> <x> <y> [color] [title] [geom]
|
|
15737
|
+
npx ggterm-plot serve # Start live viewer (port 4242)
|
|
15738
|
+
npx ggterm-plot inspect <file> # Show column types
|
|
15739
|
+
npx ggterm-plot suggest <file> # Suggest visualizations
|
|
15740
|
+
npx ggterm-plot history # List previous plots
|
|
15741
|
+
npx ggterm-plot export <id> out.html # Export plot
|
|
15742
|
+
\`\`\`
|
|
15743
|
+
|
|
15744
|
+
## Geom Types
|
|
15745
|
+
|
|
15746
|
+
point, line, histogram, boxplot, bar, violin, density, area, ridgeline, heatmap, scatter, ecdf, smooth, and 50+ more.
|
|
15747
|
+
|
|
15748
|
+
## Live Viewer
|
|
15749
|
+
|
|
15750
|
+
When \`npx ggterm-plot serve\` is running, plots auto-display in the browser/Wave panel as high-resolution interactive Vega-Lite visualizations instead of ASCII art.
|
|
15751
|
+
`;
|
|
15752
|
+
const GGTERM_MARKER = "# ggterm Data Analysis Project";
|
|
15753
|
+
let shouldWriteClaudeMd = !existsSync2(claudeMdPath);
|
|
15754
|
+
if (!shouldWriteClaudeMd) {
|
|
15755
|
+
try {
|
|
15756
|
+
const existing = readFileSync2(claudeMdPath, "utf-8");
|
|
15757
|
+
shouldWriteClaudeMd = existing.startsWith(GGTERM_MARKER);
|
|
15758
|
+
} catch {}
|
|
15759
|
+
}
|
|
15760
|
+
if (shouldWriteClaudeMd) {
|
|
15761
|
+
writeFileSync2(claudeMdPath, claudeMdContent);
|
|
15762
|
+
console.log(" ✓ CLAUDE.md (project instructions for Claude Code)");
|
|
15763
|
+
console.log("");
|
|
15764
|
+
}
|
|
15765
|
+
console.log("Built-in datasets:");
|
|
15766
|
+
console.log(" • iris (150 rows: sepal_length, sepal_width, petal_length, petal_width, species)");
|
|
15767
|
+
console.log(" • mtcars (16 rows: mpg, cyl, hp, wt, name)");
|
|
15768
|
+
console.log("");
|
|
15827
15769
|
const dataFiles = findDataFiles(cwd);
|
|
15828
15770
|
if (dataFiles.length > 0) {
|
|
15829
15771
|
console.log("Data files:");
|
|
@@ -15851,9 +15793,9 @@ function handleInit() {
|
|
|
15851
15793
|
}
|
|
15852
15794
|
if (needsInstall) {
|
|
15853
15795
|
console.log("Try:");
|
|
15854
|
-
console.log('
|
|
15855
|
-
console.log(
|
|
15856
|
-
console.log(' "
|
|
15796
|
+
console.log(' npx ggterm-plot iris sepal_length sepal_width species "Iris" point');
|
|
15797
|
+
console.log(" npx ggterm-plot serve # Start live viewer");
|
|
15798
|
+
console.log(' "Plot the iris dataset" # Ask Claude Code');
|
|
15857
15799
|
console.log("");
|
|
15858
15800
|
}
|
|
15859
15801
|
}
|
|
@@ -16389,19 +16331,68 @@ Examples:`);
|
|
|
16389
16331
|
Tip: Use "inspect <file>" to see available columns`);
|
|
16390
16332
|
process.exit(1);
|
|
16391
16333
|
}
|
|
16392
|
-
|
|
16334
|
+
const BUILTIN_DATASETS = {
|
|
16335
|
+
iris: () => {
|
|
16336
|
+
const species = ["setosa", "versicolor", "virginica"];
|
|
16337
|
+
const data2 = Array.from({ length: 150 }, (_, i) => {
|
|
16338
|
+
const sp = species[Math.floor(i / 50)];
|
|
16339
|
+
const base = sp === "setosa" ? 0 : sp === "versicolor" ? 1 : 2;
|
|
16340
|
+
return {
|
|
16341
|
+
sepal_length: +(5 + base * 0.5 + Math.random()).toFixed(1),
|
|
16342
|
+
sepal_width: +(3 + Math.random() * 0.5).toFixed(1),
|
|
16343
|
+
petal_length: +(1.5 + base * 2 + Math.random()).toFixed(1),
|
|
16344
|
+
petal_width: +(0.2 + base * 0.8 + Math.random() * 0.3).toFixed(1),
|
|
16345
|
+
species: sp
|
|
16346
|
+
};
|
|
16347
|
+
});
|
|
16348
|
+
return { headers: ["sepal_length", "sepal_width", "petal_length", "petal_width", "species"], data: data2 };
|
|
16349
|
+
},
|
|
16350
|
+
mtcars: () => {
|
|
16351
|
+
const data2 = [
|
|
16352
|
+
{ name: "Mazda RX4", mpg: 21, cyl: 6, hp: 110, wt: 2.62 },
|
|
16353
|
+
{ name: "Mazda RX4 Wag", mpg: 21, cyl: 6, hp: 110, wt: 2.875 },
|
|
16354
|
+
{ name: "Datsun 710", mpg: 22.8, cyl: 4, hp: 93, wt: 2.32 },
|
|
16355
|
+
{ name: "Hornet 4 Drive", mpg: 21.4, cyl: 6, hp: 110, wt: 3.215 },
|
|
16356
|
+
{ name: "Hornet Sportabout", mpg: 18.7, cyl: 8, hp: 175, wt: 3.44 },
|
|
16357
|
+
{ name: "Valiant", mpg: 18.1, cyl: 6, hp: 105, wt: 3.46 },
|
|
16358
|
+
{ name: "Duster 360", mpg: 14.3, cyl: 8, hp: 245, wt: 3.57 },
|
|
16359
|
+
{ name: "Merc 240D", mpg: 24.4, cyl: 4, hp: 62, wt: 3.19 },
|
|
16360
|
+
{ name: "Merc 230", mpg: 22.8, cyl: 4, hp: 95, wt: 3.15 },
|
|
16361
|
+
{ name: "Merc 280", mpg: 19.2, cyl: 6, hp: 123, wt: 3.44 },
|
|
16362
|
+
{ name: "Merc 280C", mpg: 17.8, cyl: 6, hp: 123, wt: 3.44 },
|
|
16363
|
+
{ name: "Merc 450SE", mpg: 16.4, cyl: 8, hp: 180, wt: 4.07 },
|
|
16364
|
+
{ name: "Merc 450SL", mpg: 17.3, cyl: 8, hp: 180, wt: 3.73 },
|
|
16365
|
+
{ name: "Merc 450SLC", mpg: 15.2, cyl: 8, hp: 180, wt: 3.78 },
|
|
16366
|
+
{ name: "Cadillac Fleetwood", mpg: 10.4, cyl: 8, hp: 205, wt: 5.25 },
|
|
16367
|
+
{ name: "Lincoln Continental", mpg: 10.4, cyl: 8, hp: 215, wt: 5.424 }
|
|
16368
|
+
];
|
|
16369
|
+
return { headers: ["name", "mpg", "cyl", "hp", "wt"], data: data2 };
|
|
16370
|
+
}
|
|
16371
|
+
};
|
|
16372
|
+
const [dataFile, x, y, color, title, geomSpec = "point", facetVar] = args;
|
|
16373
|
+
let headers;
|
|
16374
|
+
let data;
|
|
16375
|
+
if (BUILTIN_DATASETS[dataFile]) {
|
|
16376
|
+
const result = BUILTIN_DATASETS[dataFile]();
|
|
16377
|
+
headers = result.headers;
|
|
16378
|
+
data = result.data;
|
|
16379
|
+
} else if (!args[0].includes(".") && !fileExists(args[0])) {
|
|
16393
16380
|
console.error(`
|
|
16394
16381
|
Error: "${args[0]}" doesn't look like a file path`);
|
|
16395
16382
|
console.error(`
|
|
16383
|
+
Built-in datasets: iris, mtcars`);
|
|
16384
|
+
console.error(`
|
|
16396
16385
|
Did you mean one of these commands?`);
|
|
16397
16386
|
console.error(` inspect <file> - Show column types`);
|
|
16398
16387
|
console.error(` suggest <file> - Get plot suggestions`);
|
|
16399
16388
|
console.error(` history - List saved plots`);
|
|
16400
16389
|
console.error(` help - Show full usage`);
|
|
16401
16390
|
process.exit(1);
|
|
16391
|
+
} else {
|
|
16392
|
+
const loaded = loadData(dataFile);
|
|
16393
|
+
headers = loaded.headers;
|
|
16394
|
+
data = loaded.data;
|
|
16402
16395
|
}
|
|
16403
|
-
const [dataFile, x, y, color, title, geomSpec = "point", facetVar] = args;
|
|
16404
|
-
let { headers, data } = loadData(dataFile);
|
|
16405
16396
|
const { baseGeom: geomType, refLines } = parseGeomSpec(geomSpec);
|
|
16406
16397
|
if (geomType === "manhattan" && y && y !== "-") {
|
|
16407
16398
|
data = data.map((row) => {
|
package/dist/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkgBH,wBAAgB,UAAU,IAAI,IAAI,CAyJjC"}
|
package/dist/serve.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA0eH,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CA6G/C"}
|