graph-visualizer/SKILL.md
Builds a fully 3D interactive goal-graph visualization as a self-contained HTML file using Three.js. Use this skill whenever the user wants to visualize a goal, system, workflow, or project as an animated directed graph — with a 3D space background, glowing floating nodes, an animated pulse traveling the loop, and a collapsible constraints sidebar. Trigger when the user says things like "create a graph for my goal", "visualize my system as a graph", "build a 3D graph app", "show my workflow as a graph", or "make an interactive graph visualization". Parses natural language description of nodes, edges, weights, and constraints, and produces THREE distinct production-grade Three.js HTML files with different aesthetics.
npx skillsauth add shenron0101/opencode-skill-shenron graph-visualizerInstall 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.
Produces THREE separate self-contained HTML files, each with a distinct aesthetic direction. All JS inline. No build tools. Open in any browser.
Read ALL of the following skills before writing a single line:
threejs-fundamentals — scene, camera, renderer, clock, resizethreejs-geometry — TubeGeometry for edges, Points for starsthreejs-materials — MeshStandardMaterial, emissive glowthreejs-shaders — ShaderMaterial for custom effectsthreejs-postprocessing — UnrealBloomPassthreejs-animation — clock-driven pulse traversalthreejs-lighting — point lights for node illuminationfrontend-design — aesthetic direction for each of the 3 variantsThe frontend-design skill defines how to choose bold, distinctive, non-generic visual directions. Apply it to choose THREE meaningfully different themes (e.g. one dark/cyberpunk, one light/minimal, one warm/organic). Each variant must be genuinely distinct — different fonts, different color palettes, different node shapes, different edge styles.
Extract from the user's description:
GOAL_NODE — final destination node (label + metric if given)
NODES[] — { id, label, layer: 0..N, slot: 0..M, type: 'start'|'parallel'|'sequential'|'goal', weight: 'large'|'medium'|'small' }
EDGES[] — { from, to, dashed?: boolean }
LOOP_PATH[] — ordered node IDs the pulse travels (last entry loops back to first)
CONSTRAINTS[] — { id, label, type: 'time'|'energy'|'personal'|'market'|'economic'|'other', detail }
Nodes are laid out in layers along Y. Layer 0 = top (start), last layer = bottom (goal). Within a layer, nodes spread along X by slot.
frontend-design)Before any code, commit to 3 fully distinct aesthetic directions. Name them. Example sets (don't copy these — invent your own each time):
Each variant gets its own HTML file. The graph data (nodes, edges, constraints) is identical — only the visual treatment changes.
threejs-fundamentals)const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, W / H, 0.1, 200);
camera.position.set(0, 0, 18);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(W, H);
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.2;
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.enablePan = false;
controls.minDistance = 8;
controls.maxDistance = 40;
threejs-geometry)~800 THREE.Points placed in a sphere of radius 60–80. Slowly rotate each frame.
Vary the starfield per variant:
const LAYER_SPACING = 3.2;
const SLOT_SPACING = 3.8;
function nodePosition(node) {
const count = layerNodeCounts[node.layer];
const x = (node.slot - (count - 1) / 2) * SLOT_SPACING;
const y = -node.layer * LAYER_SPACING + (totalLayers - 1) * LAYER_SPACING / 2;
return new THREE.Vector3(x, y, 0);
}
threejs-materials)The #1 visibility problem in past versions: nodes were near-black with low emissive — impossible to read.
Fix with these rules:
0x1a2a3a not 0x020408.BoxGeometry behind each node with an emissive outline material, scaled by 1.05. This creates a visible glowing edge.// CORRECT — high visibility node
{ color: 0x0d2235, emissive: 0x4a9eff, emissiveIntensity: 1.8, roughness: 0.4, metalness: 0.3 }
// WRONG — near-invisible
{ color: 0x080820, emissive: 0x4466ff, emissiveIntensity: 0.3, roughness: 0.6, metalness: 0.1 }
Node sizes by weight:
large: 2.2 × 0.6 × 0.4medium: 1.8 × 0.55 × 0.4small: 1.4 × 0.5 × 0.4Outline glow mesh (add behind each node):
const outlineGeo = new THREE.BoxGeometry(dim.w * 1.08, dim.h * 1.2, dim.d * 0.5);
const outlineMat = new THREE.MeshBasicMaterial({ color: emissiveColor, transparent: true, opacity: 0.2 });
const outline = new THREE.Mesh(outlineGeo, outlineMat);
outline.position.copy(nodePos);
outline.position.z -= 0.05;
scene.add(outline);
Vary node shapes per variant:
CylinderGeometry(w/2, w/2, h, 32) (rounded pill, laid flat)threejs-geometry)The #2 visibility problem: edges at opacity 0.45 on dark background are near-invisible.
Fix:
// CORRECT
new THREE.MeshBasicMaterial({ color: 0x1a5a8a, transparent: true, opacity: 0.75 })
// WRONG — invisible
new THREE.MeshBasicMaterial({ color: 0x1a3a4a, transparent: true, opacity: 0.45 })
Tube radius: 0.035 (not 0.025 — thicker is more visible).
For the feedback loop edge (goal → start): arc far left (X offset -10), opacity 0.4, dashed feel via alternating thin/thick segments if possible, otherwise just lower opacity.
threejs-animation)Build the full closed CatmullRomCurve3 from LOOP_PATH bezier segments (30 points each).
Pulse sphere: SphereGeometry(0.14, 16, 16) — slightly larger than before (0.1 was too small).
// emissiveIntensity must be high — this is the brightest element in the scene
new THREE.MeshStandardMaterial({ color: accentColor, emissive: accentColor, emissiveIntensity: 4.0 })
4–5 trail ghosts, decreasing size and emissiveIntensity. Loop duration: 8 seconds.
Vary pulse color per variant:
0x6ee7f70x39ff140xfbbf24threejs-postprocessing)const bloom = new UnrealBloomPass(
new THREE.Vector2(W, H),
1.2, // strength — higher than before
0.6, // radius
0.1 // threshold — very low so emissive materials bloom
);
threejs-lighting)scene.add(new THREE.AmbientLight(variantAmbientColor, 3.0)); // brighter ambient than before
const key = new THREE.PointLight(variantKeyColor, 3.5, 40);
key.position.set(0, 6, 12);
scene.add(key);
Vary lighting per variant — different ambient and key light colors to set mood.
Same data for all variants, but style it to match each variant's aesthetic.
.constraints-panel {
position: fixed; right: 0; top: 0;
width: 280px; height: 100vh;
background: variantPanelBg; /* dark semi-transparent, themed to variant */
backdrop-filter: blur(18px);
border-left: 1px solid variantBorderColor;
overflow-y: auto; z-index: 10;
padding: 20px 12px;
font-family: variantFont;
}
Accordion behavior: max-height: 0 → 200px CSS transition on data-open toggle.
Type badge colors (consistent across variants):
time → amber #f5c842energy → cyan #6ee7f7personal → violet #a78bfamarket → blue #60a5faeconomic → red #f87171other → graywindow.addEventListener('resize', () => {
const w = window.innerWidth - 280;
const h = window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
composer.setSize(w, h);
});
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
const elapsed = clock.getElapsedTime();
controls.update();
stars.rotation.y += delta * 0.008;
const t = (elapsed % LOOP_DURATION) / LOOP_DURATION;
pulse.position.copy(fullPath.getPointAt(t));
updateTrails(t);
// Subtle node float
nodeMeshes.forEach((item, i) => {
item.mesh.position.y = item.mesh.userData.baseY + Math.sin(elapsed * 0.5 + i * 1.2) * 0.05;
item.sprite.position.y = item.mesh.position.y;
item.outline.position.y = item.mesh.position.y;
});
composer.render();
}
animate();
Save THREE files to ./:
graph-[slug]-A.html — Variant Agraph-[slug]-B.html — Variant Bgraph-[slug]-C.html — Variant CCall present_files with all three. Tell the user: "Here are 3 aesthetic variants — open each in any browser. Drag to orbit, scroll to zoom. Pick your favourite or ask for adjustments."
"Create a 3D graph for my quant finance goal — nodes are wake up/run, quant research, interview prep, exam prep, daily validation, social capital. Constraints: ADHD, time pressure, Singapore market."
"Build a goal graph: morning routine → fitness / writing / networking → launch startup. Constraints: solo founder, 3-month runway."
<script type="importmap"> for Three.js CDN.localStorage — state in memory only.CatmullRomCurve3 with getPointAt(t).tools
This skill should be used when the user asks to "generate interview questions", "make interview prep", "quiz me on" a topic, "practice questions for" a topic, "create Q&A" from vault notes, or asks for flashcards or Obsidian interview-prep output.
content-media
This skill should be used when the user asks to "make a cheatsheet", "generate a cheat sheet", "compress my notes", "create a study sheet", "make a printable reference", or asks for a cheatsheet for a topic from vault notes or PDFs.
testing
# PDF Study QA Skill Study and ask questions about PDF documents using AI-powered analysis. ## Description This skill enables you to upload PDF documents and ask questions about their content. It extracts text from PDFs and uses AI to provide answers based on the document's content. ## Usage ```bash # Load the skill /skill pdf-study-qa # Ask questions about a PDF /study-pdf <path-to-pdf> "What is the main topic of this document?" ``` ## Features - **Text Extraction**: Automatically extra
tools
# Claude2OpenCode Skill Convert Claude Code project configurations to OpenCode format. ## Description This skill provides a migration toolkit for converting Claude Code project configurations (.claude directory, CLAUDE.md, MCP settings) to OpenCode format. ## Usage ```bash # Load the skill /skill claude2opencode # Run the migration python3 ~/.claude/tools/migrate_claude_project.py --target opencode --project-root . ``` ## What It Migrates - **CLAUDE.md / .claude/CLAUDE.md** → AGENTS.md (