@clawhub-seitzbg-96dd349eaf
Generate a polished PDF report or summary from findings, data, or tables gathered earlier in the conversation. Use when the user asks to "make a PDF", "gener...
---
name: pdf-report
description: |
Generate a polished PDF report or summary from findings, data, or tables
gathered earlier in the conversation. Use when the user asks to "make a PDF",
"generate a report", "export findings", "summarize as a PDF", or similar.
Produces Typst source, compiles it with `typst`, and reports the output path.
metadata: {"openclaw":{"emoji":"📄","requires":{"bins":["typst"]}}}
---
# pdf-report
Turn gathered conversation content (findings, data, tables) into a clean PDF
via Typst. Two templates ship with this skill — pick one, fill it in, compile.
## When to invoke
Trigger on any of:
- "make a PDF" / "create a PDF" / "export to PDF"
- "generate a report" / "write up a report"
- "summarize this as a PDF" / "PDF summary"
- "export these findings/results"
If the user says "export" or "deliverable" without specifying format and the
context has tabular data or distinct findings, ask once whether they want PDF
before invoking.
## Inputs to gather
Before writing any Typst, confirm or infer:
1. **Title** — ask if not obvious. Keep short (≤ 60 chars).
2. **Subtitle / author** — optional; leave blank or omit if unknown.
3. **Content scope** — exactly which findings/tables/sections from the
conversation belong in the document. Do not invent data the user did not
provide.
4. **Output path** — default `./report-YYYY-MM-DD.pdf` in cwd. If a file with
that name exists, append `-2`, `-3`, etc.
## Pick a template
| Template | Use when |
|----------|----------|
| `templates/summary.typ` | 1–3 sections, executive summary, ≤ ~3 pages. **Default.** |
| `templates/report.typ` | 3+ distinct sections, title page + TOC desired, or user said "report". |
Both live next to this `SKILL.md`. Read the chosen one, then write a new
`.typ` file at `<output>.typ` (next to the target PDF) with placeholders
replaced and example tables swapped for real data.
## Authoring rules
- **Tables**: always use `#table()` with `table.header(...)`. Right-align
numeric columns. Preserve the precision the user gave you — do not round.
- **Long tables**: `table.header(repeat: true, ...)` repeats headers on page
breaks automatically (Typst 0.12+).
- **Special characters in cell text**: wrap in `[...]` content blocks; escape
`#`, `$`, `@`, `\` if they appear literally.
- **Adapt freely**: add or drop sections to match the content. Keep the page
setup, font sizing, and heading shows from the template — those define the
look.
- **No watermarks**. Do not add "Generated by AI", "Claude", "Qwen", or
similar. The user does not want it.
## Compile
```bash
typst compile <output>.typ <output>.pdf
```
`typst` is at `/home/linuxbrew/.linuxbrew/bin/typst` (linuxbrew). If it's not
on PATH in the current shell, use the full path.
## Error handling
If compile fails:
1. Read the error — Typst errors include file, line, and a usually-clear hint.
2. Fix the `.typ` source and retry **once**.
3. If it still fails, stop. Show the user:
- the error output,
- the path to the `.typ` source so they can edit directly.
Do not loop indefinitely on errors.
## Reporting back
On success, tell the user:
- output PDF path (absolute),
- page count (from `pdftotext -l 1` or just `typst query` — or simply state
"compiled successfully" if neither is convenient),
- that the `.typ` source is kept alongside for future tweaks.
Offer to open it (`xdg-open <path>`) but do not open without confirmation.
## Quick reference: minimal Typst patterns
```typst
// Two-column table with numeric right-align
#table(
columns: (auto, auto),
align: (left, right),
stroke: 0.5pt + gray,
inset: 7pt,
table.header([*Name*], [*Count*]),
[alpha], [42],
[beta], [108],
)
// Bullet list
- one
- two
- three
// Inline emphasis
This is *bold* and this is _italic_ and this is `mono`.
// Math (inline and block)
The energy is $E = m c^2$.
$ integral_0^infinity e^(-x^2) dif x = sqrt(pi) / 2 $
// Image
#figure(image("plot.png", width: 80%), caption: [A plot.])
```
Reads and reports hardware temperature sensor values from the DGX Spark system via SNMP for hardware health monitoring.
# dgx-spark-temperature
Read hardware temperature sensors on the DGX Spark via SNMP.
## When to use
- User asks for body temperature, DGX Spark temp, hardware temps, how hot things are running
- Any temperature/hardware health check request for the DGX Spark
## How to use
Run `exec` with:
```
bash <workspace>/skills/dgx-spark-temperature/check_temperature.sh
```
The script uses:
- `snmpwalk -v2c -c licpub dgx-spark1.fiber.house 1.3.6.1.4.1.2021.13.16.2.1`
- Parses LM-SENSORS MIB table: `lmTempSensorsIndex`, `lmTempSensorsDevice`, `lmTempSensorsValue`
- Values are in milliCelsius — divide by 1000 for °C
## Sensor mapping (16 sensors)
| IDX | Sensor Name | Notes |
|-----|-----------------------|---------------------------------|
| 1 | asic | GPU/GB10 ASIC |
| 2 | Module0 | GPU Module 0 |
| 3 | mlx5-pci-0100:asic | Mellanox NIC #1 ASIC |
| 4 | mlx5-pci-0100:Module0 | Mellanox NIC #1 module |
| 5 | temp1 | Generic thermal sensor |
| 6 | temp2 | Generic thermal sensor |
| 7 | temp3 | Generic thermal sensor |
| 8 | temp4 | Generic thermal sensor |
| 9 | temp5 | Generic thermal sensor |
| 10 | temp6 | Generic thermal sensor |
| 11 | temp7 | Generic thermal sensor |
| 12 | mlx5-pci-20101:asic | Mellanox NIC #3 ASIC |
| 13 | mlx5-pci-0101:asic | Mellanox NIC #2 ASIC |
| 14 | Composite | Overall/aggregate temp |
| 15 | Sensor 1 | Additional thermal probe |
| 16 | Sensor 2 | Additional thermal probe |
## File layout
```
skills/dgx-spark-temperature/
SKILL.md ← this file
check_temperature.sh ← the script
```
## Notes
- Community string is `licpub` (read-only)
- SNMPv2c, no auth/privacy
- DGX Spark runs Ubuntu 24.04 kernel 6.17, aarch64 (NVIDIA)
- Location: "Basement" (per SNMP sysLocation)
- Hostname: `bseitz-spark1`
FILE:check_temperature.sh
#!/usr/bin/env bash
# check_temperature — read DGX Spark temps via SNMP and format nicely
# Usage: bash check_temperature.sh
#
# Requires: snmpwalk (net-snmp-utils)
# SNMP target: dgx-spark1.fiber.house, v2c, community "licpub"
# MIB: LM-SENSORS (UCD-SNMP-MIB) OID 1.3.6.1.4.1.2021.13.16.2.1
SNMPDST="dgx-spark1.fiber.house"
COMMUNITY="licpub"
OID_BASE="1.3.6.1.4.1.2021.13.16.2.1"
# Fetch all name/value columns
WALK=$(snmpwalk -v2c -c "$COMMUNITY" "$SNMPDST" "$OID_BASE" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$WALK" ]; then
echo "ERROR: SNMP walk failed"
exit 1
fi
# Parse SNMP output using gawk
# OID format: iso.3.6.1.4.1.2021.13.16.2.1.COL.INDEX
# COL 1 = index (lmTempSensorsIndex), 2 = name (lmTempSensorsDevice), 3 = value (lmTempSensorsValue)
echo "$WALK" | gawk '
BEGIN {
count = 0
}
{
# Split the OID (first field) by dots to get column type and index
split($1, oid_parts, ".")
n = length(oid_parts)
idx = oid_parts[n] + 0
col = oid_parts[n-1] + 0
if (col == 1 && idx > 0) {
# lmTempSensorsIndex
if (!(idx in names)) {
count++
indices[count] = idx
}
}
else if (col == 2 && idx > 0) {
# lmTempSensorsDevice — extract quoted string from line
if (match($0, /"([^"]+)"/, s)) {
names[idx] = s[1]
}
}
else if (col == 3 && idx > 0) {
# lmTempSensorsValue — Gauge32
if (match($0, /Gauge32: ([0-9]+)/, v)) {
vals[idx] = v[1] + 0
}
}
}
END {
printf "\n=== DGX Spark Temperature Report ===\n\n"
printf "%-4s %-30s %10s\n", "IDX", "SENSOR", "TEMP (°C)"
printf "%-4s %-30s %10s\n", "---", "------------------------------", "----------"
max_c = -999
min_c = 999
max_name = ""
min_name = ""
sum = 0
cnt = 0
for (i = 1; i <= count; i++) {
idx = indices[i]
name = (idx in names) ? names[idx] : "unknown"
val = (idx in vals) ? vals[idx] : -1
if (val >= 0) {
temp = val / 1000.0
printf "%-4s %-30s %8.1f°C\n", idx, name, temp
sum += temp
cnt++
if (temp > max_c) { max_c = temp; max_name = name }
if (temp < min_c) { min_c = temp; min_name = name }
} else {
printf "%-4s %-30s %10s\n", idx, name, "N/A"
}
}
printf "\n=== Summary ===\n"
if (cnt > 0) {
printf " Avg: %.1f°C\n", sum / cnt
printf " Max: %.1f°C (%s)\n", max_c, max_name
printf " Min: %.1f°C (%s)\n", min_c, min_name
}
}
'