È un file solo. Apri Claude Code e incolla questo. Claude crea la skill per te.
Incolla in Claude Codecrea la skill /iq nella tua cartella delle skill
Create the file ~/.claude/skills/iq/SKILL.md with exactly this content, then confirm /iq works:
---
name: iq
description: >-
Estimates your IQ as one number from your own past chats, as a fun estimate and not a test: two readers (pattern speed and planning depth) each score you alone, a small calculator turns that into an IQ estimate and where it sits among everyone else, and the report quotes the exact chats that raised your score and the ones that dragged it down. Use when the user types /iq, optionally followed by my-chats (the default) or a path to a file or folder of chat exports.
argument-hint: "[my-chats | folder or file of chat exports]"
license: MIT
metadata:
author: "Alex Chen (@nocodealex)"
homepage: "https://chen.media"
version: "1.0.0"
---
# /iq
The user types `/iq`. You will estimate their IQ from what they wrote in their past chats: two readers, each alone, then a calculator. This is a fun estimate made by language models from a slice of how someone writes. Say that plainly at the top and at the bottom of the result. Never flatter: a normal result is a middle result, and you say so.
## 1. Say what you will read, and ask once
- `my-chats` (or nothing) = the user's own Claude Code chats in `~/.claude/projects`. A path = a file or folder of exports: Claude Code `.jsonl`, a ChatGPT export (`conversations.json`), or `.txt` / `.md` files.
- Tell the user in ONE message: you will read only what THEY wrote (never Claude's replies), up to 320 of their messages, with tokens, emails and phone numbers masked; it is saved only in `.iq/` in this folder; you will then start two sub-agents that read it. Ask: "Go ahead? (yes/no)". Wait for a yes. If it is a no, stop.
## 2. Collect the evidence
Save the collector script at the end of this file as `.iq/collect.py` and the calculator as `.iq/iq_calc.py`, then run `python3 .iq/collect.py <the argument>` (nothing for `my-chats`). It prints how many messages and chats it found and writes `.iq/<date>/evidence.md`: the user's messages, numbered `[#12 2026-10-03 project]`. If it kept fewer than 30 messages, say there is not enough evidence for a fair estimate, say what to point it at (`/iq <folder of exports>`), and stop.
## 3. Two readers, alone
Start two sub-agents at once (one message, two calls). Give each ONLY the path to `evidence.md` and ONE reader below. Neither sees the other's answer. Each reads for one thing, nothing else.
1. **Pattern speed.** How fast the user spots patterns: do they notice the same problem coming back in a new costume, generalise from one example to a rule, name the structure behind a mess, fix the whole class of bug instead of the one instance, or spot that two requests are really the same request?
2. **Planning depth.** How many steps ahead the user plans: do they put the work in an order that avoids rework, name what could go wrong before it does, set up a check before building, think about what happens after the first version works, write the plan down, or only react to the last result?
Rules for both readers, and tell them all of it:
- 50 is the average adult on this scale, and most people land between 35 and 65. Start at 50 and move only on evidence in the file. 70 or more needs three different quotes that show it. 85 or more needs the same strength in different months. Below 50 is normal and must be said plainly. Never round up to be kind.
- Every claim quotes the file word for word with its number, for example `[#41] "..."`. No quote, no claim. Never invent anything about the user.
- This is only what the user wrote to an AI. Say what the evidence cannot show.
Each reader answers in exactly this form, under 170 words: `SCORE: <0-100>`, `CONFIDENCE: low, medium or high`, `RAISED BY:` up to three quotes, each with its number and one line on why it counts, `DRAGGED DOWN BY:` up to three quotes, each with its number and one line on why, `ONE LINE:` the verdict in plain words.
## 4. The number
Run `python3 .iq/iq_calc.py <pattern score> <planning score> <the LOWER of the two confidences>`. It prints `IQ`, `RANGE`, `PERCENTILE` and one plain sentence. Use its numbers exactly; do not adjust them yourself.
## 5. Write the result
Create `.iq/<date>/REPORT.md` with the sections below, then show the user the same in chat and tell them where the file is. Touch no other files.
1. **Your estimated IQ: N** (about `RANGE`), higher than `PERCENTILE` of 100 people if the estimate is right. One line under it: "A fun estimate from your chats, not an IQ test. 100 is the average adult."
2. A small table: reader, score, confidence, one line. Pattern speed and planning depth. Under it: "50 is a fixed reference for a typical adult, not a measured number."
3. **The chats that raised your score:** the best three quotes from both readers, each with its number and why it counts.
4. **The chats that dragged it down:** the three most telling quotes from both readers, each with its number and why, and one sentence on what to do differently.
5. **What this cannot show:** it only knows what the user wrote to an AI, a slice of how they think; short messages, one project or one mood can move it; a different set of chats can move the number by ten points or more.
6. End with this line: `Made with /iq by Alex Chen (@nocodealex) · chen.media`
## Rules
- Be honest. If the user is strong at something, say so with the quote that proves it. If the chats show little, say little. If the number is average, say it is average.
- This is a structured opinion made by language models from a slice of how someone writes. It is not an IQ test, not a psychological assessment and not advice about work, school or health. Do not let the user take it as one.
- Keep it private: nothing is uploaded or posted by this skill, and the evidence stays in `.iq/`.
## The collector script
Save exactly this as `.iq/collect.py`:
```python
#!/usr/bin/env python3
"""iq: collect YOUR OWN messages from past chats into one evidence file (the first step of /iq).
python3 collect.py [my-chats | path ...] [--out DIR]
my-chats (the default) reads your Claude Code chats in ~/.claude/projects. A path is a file or folder of exports: Claude Code
.jsonl, a ChatGPT export (conversations.json) or plain .txt / .md files. Only what YOU wrote is kept, never the replies; tokens,
emails and phone numbers are masked. Nothing leaves this computer: the script only writes DIR/evidence.md.
"""
import datetime
import glob
import json
import os
import re
import sys
LIMIT = 320
MAXLEN = 650
MINLEN = 25
SKIP = ("<", "Caveat:", "[Request interrupted", "This session is being continued", "/")
MASK = [(re.compile(p), r) for p, r in (
(r"\b(?:sk|pk|rk|ghp|gho|ghs|github_pat|xox[abprs]|AKIA|AIza|ya29)[-_A-Za-z0-9]{16,}", "[token]"),
(r"(?i)\b(?:password|passwd|secret|token|api[_ -]?key)\b\s*[:=]\s*\S+", "[secret]"),
(r"(?i)bearer\s+[A-Za-z0-9._~+/=-]{20,}", "[token]"),
(r"[\w.+-]+@[\w-]+\.[\w.-]+", "[email]"),
(r"(?<!\d)(?:\+?\d[\s().-]{0,2}){10,16}(?!\d)", "[phone]"))]
def clean(text):
text = re.sub(r"\s+", " ", text).strip()
for rx, rep in MASK:
text = rx.sub(rep, text)
return text[:MAXLEN]
def keep(text):
t = text.strip()
return len(t) >= MINLEN and not t.startswith(SKIP)
def blocks_text(content):
if isinstance(content, str):
return content
if isinstance(content, list):
return "\n".join(b.get("text", "") for b in content if isinstance(b, dict) and b.get("type") == "text")
return ""
def from_jsonl(path):
out, label = [], os.path.basename(os.path.dirname(path)).strip("-").split("-")[-1][:24] or "chat"
try:
with open(path, encoding="utf-8", errors="replace") as f:
for line in f:
try:
d = json.loads(line)
except ValueError:
continue
if d.get("type") != "user" or d.get("isSidechain") or d.get("isMeta"):
continue
text = blocks_text((d.get("message") or {}).get("content"))
if keep(text):
out.append((str(d.get("timestamp", ""))[:10], label, os.path.basename(path)[:8], text))
except OSError:
pass
return out
def from_chatgpt(path):
out = []
try:
data = json.load(open(path, encoding="utf-8", errors="replace"))
except (OSError, ValueError):
return out
for conv in data if isinstance(data, list) else []:
day = ""
if isinstance(conv, dict) and conv.get("create_time"):
day = datetime.datetime.fromtimestamp(conv["create_time"]).strftime("%Y-%m-%d")
for node in ((conv or {}).get("mapping") or {}).values():
m = (node or {}).get("message") or {}
if (m.get("author") or {}).get("role") != "user":
continue
parts = (m.get("content") or {}).get("parts") or []
text = "\n".join(p for p in parts if isinstance(p, str))
if keep(text):
out.append((day, "chatgpt", str(conv.get("id", ""))[:8], text))
return out
def from_text(path):
try:
raw = open(path, encoding="utf-8", errors="replace").read()
except OSError:
return []
return [("", os.path.basename(path)[:24], "text", p) for p in re.split(r"\n\s*\n", raw) if keep(p)]
def gather(args):
out = []
targets = [os.path.expanduser(a) for a in args if not a.startswith("--")]
if not targets or targets == ["my-chats"]:
targets = [os.path.expanduser("~/.claude/projects")]
for t in targets:
if t == os.path.expanduser("~/.claude/projects"):
files = sorted(glob.glob(os.path.join(t, "*", "*.jsonl")))
else:
files = [t] if os.path.isfile(t) else sorted(glob.glob(os.path.join(t, "**", "*"), recursive=True))
for p in files:
if not os.path.isfile(p):
continue
if p.endswith(".jsonl"):
out += from_jsonl(p)
elif os.path.basename(p) == "conversations.json" or (p.endswith(".json") and "conversation" in p.lower()):
out += from_chatgpt(p)
elif p.endswith((".txt", ".md")):
out += from_text(p)
return out
def pick(msgs):
seen, uniq = set(), []
for m in msgs:
k = re.sub(r"\W+", "", m[3].lower())[:80]
if k not in seen:
seen.add(k)
uniq.append(m)
uniq.sort(key=lambda m: m[0])
if len(uniq) <= LIMIT:
return uniq, len(uniq)
longest = {id(m) for m in sorted(uniq, key=lambda m: -len(m[3]))[:LIMIT // 2]}
rest = [m for m in uniq if id(m) not in longest]
step = len(rest) / float(LIMIT - len(longest))
even = {id(rest[int(i * step)]) for i in range(LIMIT - len(longest))}
return [m for m in uniq if id(m) in longest or id(m) in even], len(uniq)
def main(argv):
out_dir = ".iq/" + datetime.date.today().isoformat()
if "--out" in argv:
out_dir = argv[argv.index("--out") + 1]
argv = [a for i, a in enumerate(argv) if a != "--out" and (i == 0 or argv[i - 1] != "--out")]
msgs = gather(argv)
kept, total = pick(msgs)
os.makedirs(out_dir, exist_ok=True)
sessions = len({(m[1], m[2]) for m in msgs})
with open(os.path.join(out_dir, "evidence.md"), "w", encoding="utf-8") as f:
f.write(f"# Evidence: {len(kept)} of your {total} messages, from {sessions} chats\n\nOnly what you wrote. Masked: tokens, emails, phone numbers.\n\n")
for i, (day, label, sid, text) in enumerate(kept, 1):
f.write(f"[#{i} {day} {label}] {clean(text)}\n\n")
print(f"messages found: {total} chats: {sessions} kept: {len(kept)} file: {os.path.join(out_dir, 'evidence.md')}")
if __name__ == "__main__":
main(sys.argv[1:])
```
## The calculator script
Save exactly this as `.iq/iq_calc.py`:
```python
#!/usr/bin/env python3
"""iq: turn the two readers' scores into ONE IQ estimate and say where it sits among everyone.
python3 iq_calc.py <pattern 0-100> <planning 0-100> <low | medium | high>
"""
import math
import sys
SHRINK = {"low": 0.5, "medium": 0.75, "high": 1.0}
SPREAD = {"low": 12, "medium": 8, "high": 5}
LOWEST, HIGHEST = 70, 145
def percentile(iq):
return 100.0 * 0.5 * (1.0 + math.erf(((iq - 100.0) / 15.0) / math.sqrt(2.0)))
def estimate(pattern, planning, confidence="medium"):
conf = str(confidence).strip().lower()
if conf not in SHRINK:
raise ValueError("confidence must be low, medium or high")
for name, v in (("pattern", pattern), ("planning", planning)):
if not 0 <= float(v) <= 100:
raise ValueError(f"{name} must be between 0 and 100")
mean = (float(pattern) + float(planning)) / 2.0
z = (mean - 50.0) / 20.0 * SHRINK[conf]
iq = int(round(100 + 15 * z))
iq = max(LOWEST, min(HIGHEST, iq))
return {"iq": iq, "low": max(55, iq - SPREAD[conf]), "high": min(160, iq + SPREAD[conf]),
"percentile": int(round(percentile(iq))), "mean": mean, "confidence": conf}
def main(argv):
if len(argv) != 3:
print("usage: python3 iq_calc.py <pattern 0-100> <planning 0-100> <low|medium|high>")
return 2
try:
r = estimate(float(argv[0]), float(argv[1]), argv[2])
except ValueError as e:
print("error:", e)
return 2
print(f"IQ: {r['iq']}")
print(f"RANGE: {r['low']} to {r['high']}")
print(f"PERCENTILE: {r['percentile']}")
print(f"MEAN SCORE: {r['mean']:.1f} (50 = the average adult)")
print(f"CONFIDENCE: {r['confidence']}")
print(f"HIGHER THAN: about {r['percentile']} of 100 people, if this estimate were right (it is a fun estimate, not a test)")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
```
Open a new Claude Code session afterwards so it picks the skill up.
La skill finisce in , quindi funziona in tutti i tuoi progetti. Preferisci il terminale? Crea la cartella a mano () e salva lo stesso testo come dentro. Per toglierla: .