csv-processor
Read a CSV file from disk, compute per-column min/mean/max for every numeric column, emit the result as JSON. Stdlib-only Python; no pandas, no numpy. Demonstrates the simplest possible "give me a file path, get back structured analysis" skill — a deliberate baseline for any skil
Install
npx skills add https://github.com/ChronoAIProject/Ornn/tree/develop/examples/csv-processor
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install chronoaiproject-ornn@llmmart
git clone https://github.com/ChronoAIProject/Ornn.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole chronoaiproject/ornn collection as a plugin from our marketplace. Git is the plain clone.
README
csv-processor
Parse a CSV file, compute per-column min/mean/max for every numeric column.
Run: python src/main.py sample.csv
Adapt: add more aggregations (median, p95, stddev), or stream large files with running-mean updates. The in/out shape (path → { rowCount, columns: { ... } }) is intentionally fixed so the skill stays composable.
Stdlib only — no pandas, no numpy. See SKILL.md for the full contract.
Skill manifest
csv-processor
A deterministic, network-free skill — the easiest case. Useful as a control when debugging the agent ↔ skill plumbing: if this fails, the failure is in the runner, not the skill.
Contract
Input (single CLI argument):
python src/main.py /path/to/data.csv
The script reads argv[1] as a filesystem path. CSV must have a header row.
Output (stdout, JSON):
{
"rowCount": 1234,
"columns": {
"price": { "min": 1.23, "mean": 42.0, "max": 999.99, "count": 1234 },
"quantity": { "min": 0, "mean": 7.5, "max": 100, "count": 1230 }
}
}
Only numeric columns appear under columns. count is the number of cells that parsed successfully (numeric); non-numeric / blank cells are skipped.
Errors — written to stderr as {"error": "..."} and exit code 1.
Run locally
cd examples/csv-processor
python src/main.py sample.csv
A sample.csv is bundled so the example runs out of the box.
Adapt this
- Different aggregations — add median, p95, stddev; same shape, more keys per column.
- Streaming — for huge files, replace the in-memory accumulation with a running-mean update; one extra variable per column, same output shape.
- Source other than disk — accept a URL or stdin instead of
argv[1]. The aggregation core doesn't care.
Files (ornn)
-
src
-
main.py 2.6 KB
"""CSV processor example skill. Reads a CSV file path from `argv[1]`, computes per-column min/mean/max for every column where at least one cell parses as a number, writes a JSON summary to stdout. On any failure: `{ "error": "..." }` on stderr + exit code 1. Stdlib only; deterministic; offline. The control case for debugging agent ↔ skill plumbing. """ from __future__ import annotations import csv import json import sys from typing import Optional def parse_number(raw: str) -> Optional[float]: """Best-effort numeric parse — empty / non-numeric returns None.""" s = raw.strip() if not s: return None try: return float(s) except ValueError: return None def summarise(path: str) -> dict: with open(path, newline="", encoding="utf-8") as fh: reader = csv.DictReader(fh) fieldnames = reader.fieldnames or [] sums: dict[str, float] = {name: 0.0 for name in fieldnames} counts: dict[str, int] = {name: 0 for name in fieldnames} mins: dict[str, float] = {} maxs: dict[str, float] = {} row_count = 0 for row in reader: row_count += 1 for name in fieldnames: value = parse_number(row.get(name, "")) if value is None: continue sums[name] += value counts[name] += 1 if name not in mins or value < mins[name]: mins[name] = value if name not in maxs or value > maxs[name]: maxs[name] = value columns: dict[str, dict[str, float | int]] = {} for name in fieldnames: if counts[name] == 0: # Skip columns where no cell parsed as a number. continue columns[name] = { "min": mins[name], "mean": sums[name] / counts[name], "max": maxs[name], "count": counts[name], } return {"rowCount": row_count, "columns": columns} def main(argv: list[str]) -> int: if len(argv) < 2: sys.stderr.write(json.dumps({"error": "usage: main.py <path>"}) + "\n") return 1 try: result = summarise(argv[1]) except FileNotFoundError as e: sys.stderr.write(json.dumps({"error": f"file not found: {e.filename}"}) + "\n") return 1 except OSError as e: sys.stderr.write(json.dumps({"error": f"could not read CSV: {e}"}) + "\n") return 1 sys.stdout.write(json.dumps(result) + "\n") return 0 if __name__ == "__main__": sys.exit(main(sys.argv))
-
-
README.md 432 B
# csv-processor Parse a CSV file, compute per-column min/mean/max for every numeric column. **Run:** `python src/main.py sample.csv` **Adapt:** add more aggregations (median, p95, stddev), or stream large files with running-mean updates. The in/out shape (`path → { rowCount, columns: { ... } }`) is intentionally fixed so the skill stays composable. Stdlib only — no pandas, no numpy. See `SKILL.md` for the full contract. -
sample.csv 134 B · in bundle
-
SKILL.md 1.8 KB
--- name: csv-processor description: Read a CSV file from disk, compute per-column min/mean/max for every numeric column, emit the result as JSON. Stdlib-only Python; no pandas, no numpy. Demonstrates the simplest possible "give me a file path, get back structured analysis" skill — a deliberate baseline for any skill that processes tabular data locally without an LLM in the loop. version: "1.0" license: MIT metadata: category: data tag: - example - csv - statistics - python --- # csv-processor A deterministic, network-free skill — the easiest case. Useful as a control when debugging the agent ↔ skill plumbing: if this fails, the failure is in the runner, not the skill. ## Contract **Input** (single CLI argument): ``` python src/main.py /path/to/data.csv ``` The script reads `argv[1]` as a filesystem path. CSV must have a header row. **Output** (stdout, JSON): ```json { "rowCount": 1234, "columns": { "price": { "min": 1.23, "mean": 42.0, "max": 999.99, "count": 1234 }, "quantity": { "min": 0, "mean": 7.5, "max": 100, "count": 1230 } } } ``` Only numeric columns appear under `columns`. `count` is the number of cells that parsed successfully (numeric); non-numeric / blank cells are skipped. **Errors** — written to stderr as `{"error": "..."}` and exit code `1`. ## Run locally ```bash cd examples/csv-processor python src/main.py sample.csv ``` A `sample.csv` is bundled so the example runs out of the box. ## Adapt this - **Different aggregations** — add median, p95, stddev; same shape, more keys per column. - **Streaming** — for huge files, replace the in-memory accumulation with a running-mean update; one extra variable per column, same output shape. - **Source other than disk** — accept a URL or stdin instead of `argv[1]`. The aggregation core doesn't care.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.