longrun/SKILL.md
Async concurrent scripts that stream results to disk and run in tmux. Use for any batch/bulk work: API calls, scraping, file processing, data transforms — anything that processes a list of items or takes >10s.
npx skillsauth add snqb/my-skills longrunInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Write scripts that are concurrent inside, streaming out, and async from conversation (tmux).
Trigger: About to write a script that loops over items, calls APIs, processes files, or will take >10s.
#!/usr/bin/env -S deno run --allow-all
/** One-line description of what this does. */
import $ from "jsr:@david/[email protected]";
import { pooledMap } from "jsr:@std/async";
const CONCURRENCY = 10;
const OUTPUT = "/tmp/TASKNAME.jsonl";
type Result = { item: string; status: "ok" | "error"; data?: unknown; error?: string };
async function process(item: string): Promise<Result> {
try {
const resp = await fetch(`https://api.example.com/${item}`);
const data = await resp.json();
return { item, status: "ok", data };
} catch (e) {
return { item, status: "error", error: String(e) };
}
}
// Resume: skip already-processed items
const done = new Set<string>();
try {
for (const line of (await Deno.readTextFile(OUTPUT)).split("\n").filter(Boolean)) {
done.add(JSON.parse(line).item);
}
} catch { /* file doesn't exist yet */ }
const items = ALL_ITEMS.filter((i) => !done.has(i));
if (!items.length) { console.error("All done."); Deno.exit(0); }
console.error(`Processing ${items.length} items (${done.size} already done)`);
// Concurrent pool → stream to disk
const file = await Deno.open(OUTPUT, { append: true, create: true });
const enc = new TextEncoder();
for await (const result of pooledMap(CONCURRENCY, items, process)) {
const line = JSON.stringify(result);
file.writeSync(enc.encode(line + "\n"));
console.log(line);
}
file.close();
tmux has-session -t pi 2>/dev/null || tmux new-session -d -s pi
tmux new-window -d -t pi -n TASKNAME 'deno run --allow-all /tmp/TASKNAME.ts'
# Progress count
wc -l /tmp/TASKNAME.jsonl
# Last few results
tail -3 /tmp/TASKNAME.jsonl | jq .
# Error count
grep -c '"status":"error"' /tmp/TASKNAME.jsonl
# Sanity check first result
head -1 /tmp/TASKNAME.jsonl | jq .
# Live follow
tmux capture-pane -t pi:TASKNAME -p -S -10
tmux kill-window -t pi:TASKNAME 2>/dev/null
.jsonl — one JSON object per line. Parseable, appendable, streamable.writeSync — results hit disk immediately. No buffering.try/catch per item — one failure must not kill the batch.pooledMap for concurrency — don't blast APIs. Start with 10, adjust."ok" or "error", makes filtering trivial./tmp/ — disposable. Move to .git/reports/ if worth keeping..ts — all scripts are TypeScript, run with deno run --allow-all.#!/usr/bin/env -S deno run --allow-all
for (const item of items) {
const result = await process(item);
console.log(JSON.stringify(result));
}
#!/usr/bin/env -S deno run --allow-all
import $ from "jsr:@david/[email protected]";
import { pooledMap } from "jsr:@std/async";
for await (const result of pooledMap(5, files, async (file) => {
const output = await $`ffprobe -v quiet -print_format json -show_format ${file}`.json();
return { file, status: "ok", data: output };
})) {
console.log(JSON.stringify(result));
}
#!/usr/bin/env -S deno run --allow-all
import { pooledMap, retry } from "jsr:@std/async";
for await (const result of pooledMap(5, urls, async (url) => {
const data = await retry(async () => {
const r = await fetch(url);
if (!r.ok) throw new Error(`${r.status}`);
return r.json();
}, { maxAttempts: 3, minTimeout: 1000 });
return { url, status: "ok", data };
})) {
console.log(JSON.stringify(result));
}
Before writing fuzzy matching, scoring functions, or multi-iteration string comparison — STOP.
These tasks are judgment, not computation. Use ask():
Tokens are unlimited (Max subscription). Don't optimize for cost — optimize for result quality. Writing 10 iterations of fuzzy matching = hours wasted + bugs + false matches. ask() gets it right first time.
Rule of thumb: if you're on iteration 2+ of fixing edge cases in string matching, you already wasted time — should have used ask() from the start.
ask() adds 200-500ms per callimport { ask, run } from "~/.pi/agent/lib/pi-llm.ts";
// Judgment per item — cheap and accurate
for (const item of remaining) {
const match = await ask(
`Is "${item.nameA}" the same entity as "${item.nameB}"? ` +
`Context: ${item.city}, ${item.country}. Answer: yes/no`,
{ model: "claude-haiku-4-5" } // $0.001/call
);
// ...
}
// Research per item — when agent needs tools
for (const item of remaining) {
const result = await run(`Research ${item.name}`, { tools: "full", maxTurns: 8 });
file.writeSync(enc.encode(JSON.stringify({ id: item.id, text: result.text, cost: result.cost }) + "\n"));
}
ask() = text→text, no tools, instant. run() = bounded agent with bash/search/read. Tokens are unlimited — use freely.
pooledMapask() for judgmentdocumentation
Enrich Markdown articles with inline Wikipedia links. First mention of each notable entity gets a hyperlink. Use when asked to add wiki links, enrich, or add references to .md files.
development
Structured visual QA: screenshot → batch issues → fix all → verify. Replaces the 300-cycle screenshot→edit death spiral. Optional bishkek review as exit gate. Use when building/polishing UI with browser testing, or when user asks for N iterations/reviews.
development
Find complex code, analyze intent, recommend battle-tested library replacements. Uses radon/eslint for detection, GitHub quality search for alternatives.
research
Research real-world UI patterns from curated galleries (Collect UI, Component Gallery, Mobbin). Use when exploring what exists: dropdowns, accordions, inputs, navigation, cards, modals, etc.