engineer-design-diagram

"Generate production-grade engineering design diagrams (architecture,\

16 Tools
engineer-design-diagram Plugin
devops Category

Allowed Tools

ReadWriteGlobGrepBash(git:*)Bash(ls:*)Bash(cat:*)Bash(jq:*)Bash(docker:*)Bash(kubectl:*)Bash(terraform:*)Bash(python3:*)Bash(xdg-open:*)Bash(open:*)Bash(wslview:*)AskUserQuestion

Provided by Plugin

engineer-design-diagram

Generate production-grade engineering design diagrams (architecture, sequence, delta, drift) as self-contained dark-themed HTML files with accessible inline SVG. Grounds every diagram in real repo topology via DCI — package manifests, docker-compose, k8s, terraform, import graph.

devops v0.2.0
View Plugin

Installation

This skill is included in the engineer-design-diagram plugin:

/plugin install engineer-design-diagram@claude-code-plugins-plus

Click to copy

Instructions

Engineer Design Diagram

Generates production-grade engineering design diagrams as single-file HTML with inline SVG, grounded in real repository topology. Credit: design palette + arrow-masking pattern inspired by Cocoon AI's architecture-diagram-generator (MIT). See THIRDPARTYLICENSES.md.

Overview

Most diagramming tools produce pretty pictures disconnected from reality. This skill does the opposite: it reads the actual repo (package manifests, docker-compose, k8s, terraform, import graph) and emits a diagram that reflects the real system. It also knows how the system changed — PR-diff mode highlights structural deltas, trace mode turns a stack/log into a sequence diagram, and drift mode detects when the architecture has wandered from a stored fingerprint.

Four modes share a common pipeline (DCI grounding → node/edge graph → template fill → fingerprint write). Output is a single self-contained HTML file that opens in any browser, plus a Mermaid text block for copy-paste into docs. Dark theme with semantic OKLCH color coding by component role. Accessible by default (ARIA, </code>/<code><desc></code>, reduced-motion, keyboard navigation).</p> <h2>Layout Philosophy — pick the shape before you draw</h2> <p>Dense technical systems want to render <strong>wide</strong>. This is how Anthropic docs, Linear docs, and Vercel architecture pages present multi-component systems: a sticky left rail for context (nav, invariants, legend) and a generous main column for the diagram itself. Mimic that pattern when your content is dense, and use the simpler single-SVG hero when it isn't.</p> <p>Two supported output shapes, one decision up front:</p> <table><thead><tr> <th>Shape</th> <th>Use when</th> <th>Template</th> </tr></thead><tbody> <tr> <td><strong>Single-SVG hero</strong> (classic)</td> <td>≤8 nodes, one-screen takeaway, no sub-grouping, no accompanying explanation needed</td> <td><code>templates/base.html</code></td> </tr> <tr> <td><strong>Docs-layout page</strong> (widescreen)</td> <td>≥8 nodes, multiple planes/groupings/sub-blocks, want invariants + detail cards + legend alongside the diagram</td> <td><code>templates/docs-layout.html</code></td> </tr> </tbody></table> <p><strong>Widescreen-first for docs-layout.</strong> Target <code>min-width: 1024px</code>; do <em>not</em> add mobile breakpoints for docs-layout output — it's architecture documentation, not a landing page. The diagram needs horizontal breathing room. On narrow viewports the diagram stage scrolls horizontally inside its card while the sidebar stays visible.</p> <p><strong>Hybrid rendering</strong> in docs-layout mode: HTML/CSS for all node cards (easier to maintain, free hover states, content edits don't trigger collision math), SVG overlay positioned absolutely <em>over</em> the node grid for arrows only. Arrows are the one place SVG still wins — fixed pixel coords, clean arrowheads, no brittle CSS-line math. Pure-SVG stays the default for the single-hero shape because the payoff of HTML flex doesn't materialize at small node counts.</p> <p><strong>Layout-decision signal:</strong> if during Step 2 the graph has any of — (a) more than one semantic lane/plane, (b) subcomponent lists of 4+ items per node, (c) the user asks for "docs page" / "architecture page" / references Anthropic/Linear/Vercel docs as exemplars — pick <strong>docs-layout</strong>. Otherwise stay with single-SVG.</p> <p>See <a href="references/docs-layout.md">docs-layout.md</a> for the full widescreen spec (grid geometry, design tokens, sidebar anatomy, arrow overlay placement, hover states).</p> <h2>Prerequisites</h2> <ul> <li>Git repository (for generate/diff/watch modes)</li> <li>At least one of: <code>package.json</code>, <code>pyproject.toml</code>, <code>Cargo.toml</code>, <code>go.mod</code>, <code>docker-compose.yml</code>, <code>k8s/<em>.yaml</code>, <code>terraform/</em>.tf</code></li> <li>Python 3.9+ (for <code>scripts/fingerprint.py</code>)</li> <li>A browser to view output</li> <li>Optional: <code>kubectl</code> (cluster introspection), <code>terraform</code> (state parsing), <code>jq</code> (manifest parsing)</li> </ul> <h2>Environment Detection (DCI)</h2> <p>!<code>git rev-parse --show-toplevel 2>/dev/null || echo "not a git repo"</code></p> <p>!<code>git rev-parse --short HEAD 2>/dev/null || echo "no-head"; git branch --show-current 2>/dev/null || echo "no-branch"</code></p> <p>!<code>ls package.json pyproject.toml Cargo.toml go.mod requirements.txt docker-compose.yml Dockerfile 2>/dev/null | head -10 || echo "no manifests"</code></p> <p>!<code>command -v jq >/dev/null 2>&1 && jq -r '.name,(.dependencies // {} | keys[]?)' package.json 2>/dev/null | head -20 || echo "no package.json / no jq"</code></p> <p>!<code>docker compose config --services 2>/dev/null | head -30 || echo "no docker-compose"</code></p> <p>!<code>find . -maxdepth 3 -type d \( -name k8s -o -name kubernetes -o -name manifests \) 2>/dev/null | head -5 || echo "no k8s dir"</code></p> <p>!<code>find . -maxdepth 3 -name '*.tf' 2>/dev/null | head -10 || echo "no terraform"</code></p> <h2>Instructions</h2> <h3>Step 1: Mode Detection</h3> <p>Parse the first token of <code>$ARGUMENTS</code>:</p> <table><thead><tr> <th>Invocation</th> <th>Mode</th> <th>Playbook</th> </tr></thead><tbody> <tr> <td><code>/design:generate</code> or empty</td> <td>generate</td> <td><a href="references/mode-playbooks.md#generate">mode-playbooks.md#generate</a></td> </tr> <tr> <td><code>/design:diff</code></td> <td>diff</td> <td><a href="references/mode-playbooks.md#diff">mode-playbooks.md#diff</a></td> </tr> <tr> <td><code>/design:trace <path></code></td> <td>trace</td> <td><a href="references/mode-playbooks.md#trace">mode-playbooks.md#trace</a></td> </tr> <tr> <td><code>/design:watch</code></td> <td>watch</td> <td><a href="references/mode-playbooks.md#watch">mode-playbooks.md#watch</a></td> </tr> </tbody></table> <p>All modes share Steps 2-5. Mode-specific variations documented in the per-mode playbook.</p> <h3>Step 2: Build the Node/Edge Graph</h3> <p>Use the DCI block output + targeted reads to populate a working graph:</p> <ul> <li><strong>Nodes</strong> from: docker-compose service names, k8s <code>kind: Deployment/Service/StatefulSet</code>, terraform resource blocks, package manifest names (for standalone apps), detected binaries in <code>bin/</code> or <code>cmd/</code>.</li> <li><strong>Edges</strong> from: docker-compose <code>depends_on</code> + exposed ports, k8s Service selectors + NetworkPolicy, terraform resource references, import-graph via Grep (<code>import.*from</code>, <code>require(</code>, <code>use crate::</code>), exposed HTTP/gRPC routes.</li> <li><strong>Role classification</strong> per node using <a href="references/drawing-rules.md#role-inference">drawing-rules.md § Role inference</a>. Defaults to <code>slate</code> (external/unknown) when heuristics don't match.</li> <li><strong>Groups</strong> from: k8s namespaces, terraform modules, docker-compose networks, directory boundaries for monorepos.</li> </ul> <p>For graphs with >50 nodes, fall back to Mermaid (see Step 3 template selection).</p> <h3>Step 3: Choose Template and Render</h3> <p>Apply the layout-selection rule from <strong>Layout Philosophy</strong> first, then pick the template for the chosen mode:</p> <table><thead><tr> <th>Mode</th> <th>Layout shape</th> <th>Template</th> <th>Output</th> </tr></thead><tbody> <tr> <td>generate / diff</td> <td>single-SVG hero (simple graphs)</td> <td><a href="templates/base.html">templates/base.html</a></td> <td>Centered SVG on dark canvas, cards row below</td> </tr> <tr> <td>generate / diff</td> <td>docs-layout page (dense graphs)</td> <td><a href="templates/docs-layout.html">templates/docs-layout.html</a></td> <td>Two-column widescreen page: sticky sidebar + main column with diagram card + detail grid</td> </tr> <tr> <td>diff (any shape)</td> <td>—</td> <td>selected template + delta classes</td> <td><code>delta-added</code> / <code>delta-removed</code> / <code>delta-changed</code> CSS markers on changed nodes/edges</td> </tr> <tr> <td>trace</td> <td>sequence</td> <td><a href="templates/sequence.html">templates/sequence.html</a></td> <td>Sequence diagram with lifelines and message arrows</td> </tr> <tr> <td>watch</td> <td>—</td> <td>none — markdown drift report</td> <td>Markdown only; no HTML render</td> </tr> </tbody></table> <p><strong>For single-SVG hero</strong>: fill placeholders per <a href="references/drawing-rules.md">drawing-rules.md</a> — color palette, SVG arrow-masking, z-order, 40px spacing, dashed boundaries, legend placement. Keep the grid <code><pattern></code>, <code>#020617</code> canvas, pulsing header dot (with reduced-motion guard).</p> <p><strong>For docs-layout page</strong>: fill placeholders per <a href="references/docs-layout.md">docs-layout.md</a> — GitHub-inspired palette (<code>#0f1117</code> / <code>#161b22</code> / <code>#1c2128</code>), Inter + JetBrains Mono, 260px sticky sidebar, fixed-pixel node stage with SVG arrow overlay, hover states on node cards. No mobile breakpoints. Populate the sidebar with: (a) version badge, (b) "On this page" nav, (c) "Architectural invariants" list (surface up to 5 load-bearing constraints you inferred from the code/config — e.g. "Kernel owns durable state", "All ops return Result<T,E>"), (d) node-type legend.</p> <p>If node count >50 OR the model signals layout failure (overlapping boxes, arrows crossing through nodes), switch to <code>templates/mermaid-fallback.html</code> regardless of layout shape.</p> <h3>Step 4: Update Fingerprint State</h3> <p>For generate/diff/watch modes, write structural state to <code>${CLAUDE<em>PLUGIN</em>DATA}/arch-state.json</code> (or <code>~/.claude-state/arch-state.json</code> fallback if the env var is unset):</p> <pre data-lang="bash"><code> python3 ${CLAUDE_SKILL_DIR}/scripts/fingerprint.py write --input /tmp/graph.json </code></pre> <p>Schema documented in <a href="references/fingerprint-spec.md">fingerprint-spec.md</a>. Fingerprint persists across sessions so <code>watch</code> mode can detect drift.</p> <h3>Step 5: Validate and Open</h3> <p>Run the HTML validator before presenting output:</p> <pre data-lang="bash"><code> python3 ${CLAUDE_SKILL_DIR}/scripts/validate_html.py $CWD/.arch/<mode>-<timestamp>.html </code></pre> <p>Validator confirms: ARIA labels present, reduced-motion rule exists, no unexpected external script sources beyond Google Fonts. If validation fails, iterate on the template fill — don't ship an inaccessible diagram.</p> <p>Open the result via <code>${CLAUDE<em>SKILL</em>DIR}/scripts/open<em>in</em>browser.sh</code> (OS-aware: <code>xdg-open</code> on Linux, <code>open</code> on macOS, <code>wslview</code> on WSL). Echo the Mermaid equivalent to the chat as a copy-pasteable text block.</p> <h3>Feedback Loop</h3> <ol> <li>Run <code>validate_html.py</code> after Step 5.</li> <li>If ARIA or contrast checks fail → fix template fill → re-validate.</li> <li>If >50 nodes rendered as overlapping boxes → restart Step 3 with Mermaid fallback.</li> <li><strong>(docs-layout only)</strong> If arrows cross non-endpoint nodes OR text overflows a node box → re-run layout math (see <a href="references/docs-layout.md#verification">docs-layout.md § verification</a>) before re-screenshotting. Don't eyeball it; collision math is cheap and catches what a thumbnail hides.</li> <li>Maximum 3 iterations before falling back to Mermaid and flagging for manual review.</li> </ol> <h2>Output</h2> <ul> <li><strong>generate / diff / trace modes</strong>: one HTML file at <code>$CWD/.arch/<mode>-<timestamp>.html</code> (self-contained, offline-capable, Google Fonts as the sole external dep) + one Mermaid text block echoed to chat.</li> <li><strong>watch mode</strong>: markdown drift report with sections <code>Added</code>, <code>Removed</code>, <code>Changed</code>, each line citing the source file that justified the delta.</li> <li><strong>All modes</strong>: updated fingerprint at <code>${CLAUDE<em>PLUGIN</em>DATA}/arch-state.json</code>.</li> </ul> <p>Detailed output contracts in <a href="references/mode-playbooks.md">mode-playbooks.md</a>.</p> <h2>Examples</h2> <h3>Example 1 — Generate architecture view from a monorepo</h3> <p><strong>Input:</strong></p> <pre><code> /design:generate </code></pre> <p><strong>Behavior:</strong> DCI auto-loads <code>docker-compose.yml</code> services (<code>web</code>, <code>api</code>, <code>db</code>, <code>cache</code>), classifies roles (frontend/backend/db/db), reads <code>web/src/lib/api-client.ts</code> for the HTTP edge to <code>api</code>, reads <code>docker-compose.yml</code> <code>depends_on</code> for api→db and api→cache edges. Fills <code>templates/base.html</code>, writes <code>~/.arch/generate-2026-04-19T12-00.html</code>, writes fingerprint with 4 nodes + 3 edges.</p> <p><strong>Output excerpt (Mermaid block):</strong></p> <pre><code> flowchart TB web["Web Frontend"]:::frontend --> api["API Service"]:::backend api --> db["Postgres"]:::db api --> cache["Redis"]:::db </code></pre> <h3>Example 2 — PR delta on a branch that adds Redis</h3> <p><strong>Input:</strong></p> <pre><code> /design:diff </code></pre> <p><strong>Behavior:</strong> Loads prior fingerprint, re-runs DCI on working tree, computes set-diff. Renders architecture view with <code>class="delta-added"</code> on the <code>cache</code> node and the <code>api→cache</code> edge. Summary line: <code>Added 1 node (cache), 1 edge (api→cache)</code>.</p> <h3>Example 3 — Sequence diagram from a Sentry event</h3> <p><strong>Input:</strong></p> <pre><code> /design:trace ./incidents/sentry-2026-04-18-payment-timeout.json </code></pre> <p><strong>Behavior:</strong> Parses Sentry JSON (exception frames + transaction spans), infers 4 lifelines (<code>checkout-api</code>, <code>payment-service</code>, <code>stripe-webhook-listener</code>, <code>fraud-check</code>), renders sequence diagram with the timeout marked on the final arrow. No fingerprint write in trace mode.</p> <h3>Example 4 — Drift watch after a quiet sprint</h3> <p><strong>Input:</strong></p> <pre><code> /design:watch </code></pre> <p><strong>Behavior:</strong> Loads prior fingerprint, re-runs DCI, diffs. Output:</p> <pre><code> ## Drift report — 2026-04-19 vs 2026-04-12 ### Added - Node `recommendations-service` (backend) — source: docker-compose.yml:services.recommendations - Edge `api` → `recommendations-service` — source: api/src/handlers/product.ts:44 ### Removed - Edge `api` → `legacy-search` — source: api/src/handlers/search.ts removed in f8a2c91 </code></pre> <h2>Edge Cases</h2> <ul> <li><strong>No package manifests</strong>: DCI returns empty. Ask the user to describe the system in prose, fall back to prompt-only generation without DCI grounding.</li> <li><strong>>150 components</strong>: Hard cap at 150 rendered nodes. Overflow bucket labeled <code>… +N more</code>; full list preserved in fingerprint.</li> <li><strong>Stack trace format unknown</strong>: Three parsers (Sentry JSON, OTel span, raw text). Unknown formats emit Mermaid-only sequence rather than failing.</li> <li><strong>No <code>main</code> branch for diff</strong>: Probe <code>origin/HEAD</code> → <code>main</code> → <code>master</code> → <code>trunk</code>. If none found, fall back to <code>HEAD~1</code>.</li> <li><strong>Docker compose binary naming</strong>: Probe both <code>docker compose</code> (modern) and <code>docker-compose</code> (legacy).</li> <li><strong>${CLAUDE<em>PLUGIN</em>DATA} unset</strong>: Fallback chain <code>${CLAUDE<em>PLUGIN</em>DATA}</code> → <code>${XDG<em>STATE</em>HOME}/claude/arch</code> → <code>~/.claude-state/arch</code>.</li> <li><strong>iOS Safari large-diagram rendering</strong>: Avoid SVG paths with >10k points per element; fall back to Mermaid for graphs that approach this limit.</li> <li><strong>k8s manifests with CRDs</strong>: Role defaults to <code>slate</code> (external/unknown) when no heuristic matches; document in the legend.</li> </ul> <h2>Error Handling</h2> <table><thead><tr> <th>Error</th> <th>Cause</th> <th>Solution</th> </tr></thead><tbody> <tr> <td><code>not a git repo</code></td> <td>Running outside git</td> <td>Init a repo or cd to one; DCI handles the empty case but diff/watch require git</td> </tr> <tr> <td><code>no manifests</code></td> <td>No package files found</td> <td>Use prose-only mode; skill will ask user to describe the system</td> </tr> <tr> <td><code>fingerprint schema mismatch</code></td> <td>State file from older schema version</td> <td><code>fingerprint.py</code> refuses cross-version diff and emits baseline-restart guidance</td> </tr> <tr> <td><code>ARIA validation failed</code></td> <td>Template fill omitted <code><title></code>/<code><desc></code></td> <td>Re-fill with role-appropriate labels from <a href="references/accessibility.md">accessibility.md</a></td> </tr> <tr> <td><code>overlapping boxes detected</code></td> <td>Too many nodes for SVG layout</td> <td>Auto-switch to Mermaid fallback (node count >50 trigger)</td> </tr> <tr> <td><code>no diff base</code></td> <td><code>main</code>/<code>master</code>/<code>trunk</code> all missing</td> <td>Use <code>HEAD~1</code> as base, warn user in output</td> </tr> <tr> <td><code>kubectl not installed</code></td> <td>Live cluster introspection unavailable</td> <td>Skip cluster overlay; diagram still renders from static manifests</td> </tr> <tr> <td><code>trace parser unknown format</code></td> <td>Input file matches no known schema</td> <td>Emit Mermaid-only sequence with best-effort parsing, flag for user review</td> </tr> </tbody></table> <h2>Resources</h2> <ul> <li><a href="references/drawing-rules.md">drawing-rules.md</a> — color palette, z-order, arrow-masking, spacing, legend placement (single-SVG hero — Cocoon-AI-inspired patterns)</li> <li><a href="references/docs-layout.md">docs-layout.md</a> — widescreen two-column layout spec (grid geometry, design tokens, sidebar anatomy, HTML+SVG hybrid arrow overlay, verification math) — mirrors Anthropic / Linear / Vercel docs patterns</li> <li><a href="references/mode-playbooks.md">mode-playbooks.md</a> — per-mode workflows with inputs, steps, outputs</li> <li><a href="references/fingerprint-spec.md">fingerprint-spec.md</a> — JSON schema for structural state + diff algorithm</li> <li><a href="references/accessibility.md">accessibility.md</a> — ARIA patterns, reduced-motion, keyboard nav</li> <li><a href="references/dci-block.md">dci-block.md</a> — DCI command catalog with output-size budgets</li> <li><a href="references/troubleshooting.md">troubleshooting.md</a> — iOS Safari, large graphs, schema drift</li> <li><a href="templates/base.html">templates/base.html</a> — single-SVG hero shell (≤8 nodes, simple graphs)</li> <li><a href="templates/docs-layout.html">templates/docs-layout.html</a> — widescreen docs-page shell (≥8 nodes, planes, sub-groupings, detail cards)</li> <li><a href="templates/sequence.html">templates/sequence.html</a> — sequence diagram variant (trace mode)</li> <li><a href="templates/mermaid-fallback.html">templates/mermaid-fallback.html</a> — Mermaid-only render for >50 nodes</li> <li><a href="scripts/fingerprint.py">scripts/fingerprint.py</a> — write / read / diff structural state</li> <li><a href="scripts/validate<em>html.py">scripts/validate</em>html.py</a> — ARIA + no-external-deps checker</li> <li><a href="scripts/collect<em>dci.sh">scripts/collect</em>dci.sh</a> — bounded DCI harvester for large repos</li> <li><a href="scripts/open<em>in</em>browser.sh">scripts/open<em>in</em>browser.sh</a> — OS-aware opener</li> <li><a href="references/THIRD<em>PARTY</em>LICENSES.md">THIRD<em>PARTY</em>LICENSES.md</a> — Cocoon-AI attribution + MIT notice</li> <li><a href="examples/">examples/</a> — Cocoon-AI sample diagrams (web-app, aws-serverless, microservices) kept as visual reference</li> <li>External: <a href="https://github.com/Cocoon-AI/architecture-diagram-generator">Cocoon-AI/architecture-diagram-generator</a> (inspiration source, MIT)</li> <li>External: <a href="https://mermaid.js.org/intro/">Mermaid diagram syntax</a></li> </ul></div> </div> </section> <!-- 8. Bottom CTA --> <section class="detail-cta" data-astro-cid-qcigfm7x> <h2 class="detail-cta__headline" data-astro-cid-qcigfm7x>Ready to use engineer-design-diagram?</h2> <div class="detail-cta__actions" data-astro-cid-qcigfm7x> <button class="detail-cta__primary" data-copy-install="/plugin install engineer-design-diagram@claude-code-plugins-plus" data-astro-cid-qcigfm7x> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" data-astro-cid-qcigfm7x> <rect x="5" y="5" width="9" height="9" rx="1.5" stroke="currentColor" stroke-width="1.5" data-astro-cid-qcigfm7x></rect> <path d="M3 11V3a1 1 0 011-1h8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" data-astro-cid-qcigfm7x></path> </svg> <span class="detail-cta__btn-text" data-astro-cid-qcigfm7x>Copy Install Command</span> </button> </div> </section> <script type="module">document.querySelectorAll("[data-copy-install]").forEach(t=>{t.addEventListener("click",async()=>{const a=t.dataset.copyInstall||"";await navigator.clipboard.writeText(a);const e=t.querySelector(".detail-cta__btn-text");if(e){const o=e.textContent;e.textContent="Copied!",t.classList.add("copied"),window.trackEvent&&window.trackEvent("install_copied",{plugin_name:a.split(" ").pop()||"",source:"detail_cta"}),setTimeout(()=>{e.textContent=o,t.classList.remove("copied")},2e3)}})});</script> <!-- 9. Related Skills (6, with tool pills) --> <section class="related-skills-section" data-astro-cid-jrlgpo3w> <h2 class="related-skills-heading" data-astro-cid-jrlgpo3w>Related Skills</h2> <div class="related-skills-grid" data-astro-cid-jrlgpo3w> <a href="/skills/adk-infra-expert/" class="related-skill-card" data-astro-cid-jrlgpo3w> <h3 class="related-skill-title" data-astro-cid-jrlgpo3w>adk-infra-expert</h3> <p class="related-skill-description" data-astro-cid-jrlgpo3w>'Execute use when provisioning Vertex AI ADK infrastructure with Terraform.</p> <div class="related-skill-tools" data-astro-cid-jrlgpo3w> <span class="related-tool-pill" data-astro-cid-jrlgpo3w>Read</span><span class="related-tool-pill" data-astro-cid-jrlgpo3w>Write</span><span class="related-tool-pill" data-astro-cid-jrlgpo3w>Edit</span> </div> </a><a href="/skills/building-cicd-pipelines/" class="related-skill-card" data-astro-cid-jrlgpo3w> <h3 class="related-skill-title" data-astro-cid-jrlgpo3w>building-cicd-pipelines</h3> <p class="related-skill-description" data-astro-cid-jrlgpo3w>'Execute use when you need to work with deployment and CI/CD.</p> <div class="related-skill-tools" data-astro-cid-jrlgpo3w> <span class="related-tool-pill" data-astro-cid-jrlgpo3w>Read</span><span class="related-tool-pill" data-astro-cid-jrlgpo3w>Write</span><span class="related-tool-pill" data-astro-cid-jrlgpo3w>Edit</span> </div> </a><a href="/skills/building-gitops-workflows/" class="related-skill-card" data-astro-cid-jrlgpo3w> <h3 class="related-skill-title" data-astro-cid-jrlgpo3w>building-gitops-workflows</h3> <p class="related-skill-description" data-astro-cid-jrlgpo3w>'Execute use when constructing GitOps workflows using ArgoCD or Flux.</p> <div class="related-skill-tools" data-astro-cid-jrlgpo3w> <span class="related-tool-pill" data-astro-cid-jrlgpo3w>Read</span><span class="related-tool-pill" data-astro-cid-jrlgpo3w>Write</span><span class="related-tool-pill" data-astro-cid-jrlgpo3w>Edit</span> </div> </a><a href="/skills/building-terraform-modules/" class="related-skill-card" data-astro-cid-jrlgpo3w> <h3 class="related-skill-title" data-astro-cid-jrlgpo3w>building-terraform-modules</h3> <p class="related-skill-description" data-astro-cid-jrlgpo3w>'Execute this skill empowers AI assistant to build reusable terraform.</p> <div class="related-skill-tools" data-astro-cid-jrlgpo3w> <span class="related-tool-pill" data-astro-cid-jrlgpo3w>Read</span><span class="related-tool-pill" data-astro-cid-jrlgpo3w>Write</span><span class="related-tool-pill" data-astro-cid-jrlgpo3w>Edit</span> </div> </a><a href="/skills/changelog-orchestrator/" class="related-skill-card" data-astro-cid-jrlgpo3w> <h3 class="related-skill-title" data-astro-cid-jrlgpo3w>changelog-orchestrator</h3> <p class="related-skill-description" data-astro-cid-jrlgpo3w>Draft changelog PRs by collecting GitHub/Slack/Git changes, formatting.</p> <div class="related-skill-tools" data-astro-cid-jrlgpo3w> <span class="related-tool-pill" data-astro-cid-jrlgpo3w>Read</span><span class="related-tool-pill" data-astro-cid-jrlgpo3w>Write</span><span class="related-tool-pill" data-astro-cid-jrlgpo3w>Edit</span> </div> </a><a href="/skills/checking-infrastructure-compliance/" class="related-skill-card" data-astro-cid-jrlgpo3w> <h3 class="related-skill-title" data-astro-cid-jrlgpo3w>checking-infrastructure-compliance</h3> <p class="related-skill-description" data-astro-cid-jrlgpo3w>'Execute use when you need to work with compliance checking.</p> <div class="related-skill-tools" data-astro-cid-jrlgpo3w> <span class="related-tool-pill" data-astro-cid-jrlgpo3w>Read</span><span class="related-tool-pill" data-astro-cid-jrlgpo3w>Write</span><span class="related-tool-pill" data-astro-cid-jrlgpo3w>Edit</span> </div> </a> </div> </section> <!-- 10. Footer --> <footer class="skill-footer" data-astro-cid-jrlgpo3w> <p data-astro-cid-jrlgpo3w>Part of <a href="/plugins/engineer-design-diagram/" data-astro-cid-jrlgpo3w>engineer-design-diagram</a></p> </footer> </div> <script> document.querySelectorAll('.install-command').forEach(el => { el.addEventListener('click', function() { const text = this.dataset.copy || this.textContent; navigator.clipboard.writeText(text).then(() => { const originalText = this.textContent; this.textContent = 'Copied to clipboard!'; this.style.color = 'var(--brand-green)'; setTimeout(() => { this.textContent = originalText; this.style.color = ''; }, 2000); }); }); }); </script> </main> <!-- Footer --> <footer data-astro-cid-37fxchfa> <div class="footer-inner" data-astro-cid-37fxchfa> <div class="footer-signup-row" data-astro-cid-37fxchfa> <h4 data-astro-cid-37fxchfa>Stay in the Loop</h4> <form data-signup-form="footer" class="footer-signup-form" data-astro-cid-37fxchfa> <input type="email" name="email" placeholder="you@example.com" required data-astro-cid-37fxchfa> <input type="text" name="website" tabindex="-1" autocomplete="off" aria-hidden="true" style="position:absolute;left:-9999px;opacity:0;height:0;width:0;" data-astro-cid-37fxchfa> <button type="submit" data-astro-cid-37fxchfa>Subscribe</button> </form> <p class="footer-form-note" data-astro-cid-37fxchfa>No spam. Unsubscribe anytime.</p> </div> <div class="footer-columns" data-astro-cid-37fxchfa> <div class="footer-col" data-astro-cid-37fxchfa> <h5 data-astro-cid-37fxchfa>Product</h5> <ul data-astro-cid-37fxchfa> <li data-astro-cid-37fxchfa><a href="/explore" data-astro-cid-37fxchfa>Explore</a></li> <li data-astro-cid-37fxchfa><a href="/skills" data-astro-cid-37fxchfa>Skills</a></li> <li data-astro-cid-37fxchfa><a href="/cowork" data-astro-cid-37fxchfa>Cowork</a></li> <li data-astro-cid-37fxchfa><a href="/compare-marketplaces" data-astro-cid-37fxchfa>Compare</a></li> <li data-astro-cid-37fxchfa><a href="/tools" data-astro-cid-37fxchfa>Tools</a></li> </ul> </div> <div class="footer-col" data-astro-cid-37fxchfa> <h5 data-astro-cid-37fxchfa>Resources</h5> <ul data-astro-cid-37fxchfa> <li data-astro-cid-37fxchfa><a href="/docs" data-astro-cid-37fxchfa>Docs</a></li> <li data-astro-cid-37fxchfa><a href="/changelog" data-astro-cid-37fxchfa>Changelog</a></li> <li data-astro-cid-37fxchfa><a href="/collections" data-astro-cid-37fxchfa>Collections</a></li> <li data-astro-cid-37fxchfa><a href="/playbooks" data-astro-cid-37fxchfa>Playbooks</a></li> <li data-astro-cid-37fxchfa><a href="/research" data-astro-cid-37fxchfa>Research</a></li> <li data-astro-cid-37fxchfa><a href="/learning" data-astro-cid-37fxchfa>Learning</a></li> </ul> </div> <div class="footer-col" data-astro-cid-37fxchfa> <h5 data-astro-cid-37fxchfa>Company</h5> <ul data-astro-cid-37fxchfa> <li data-astro-cid-37fxchfa><a href="/community" data-astro-cid-37fxchfa>Community</a></li> <li data-astro-cid-37fxchfa><a href="/community#hall-of-fame" data-astro-cid-37fxchfa>Hall of Fame</a></li> <li data-astro-cid-37fxchfa><a href="https://github.com/jeremylongshore/claude-code-plugins" target="_blank" data-astro-cid-37fxchfa>GitHub</a></li> </ul> </div> <div class="footer-col" data-astro-cid-37fxchfa> <h5 data-astro-cid-37fxchfa>Legal</h5> <ul data-astro-cid-37fxchfa> <li data-astro-cid-37fxchfa><a href="/privacy" data-astro-cid-37fxchfa>Privacy</a></li> <li data-astro-cid-37fxchfa><a href="/terms" data-astro-cid-37fxchfa>Terms</a></li> <li data-astro-cid-37fxchfa><a href="/acceptable-use" data-astro-cid-37fxchfa>Acceptable Use</a></li> </ul> </div> </div> <div class="footer-bottom" data-astro-cid-37fxchfa> <p class="footer-blurb" data-astro-cid-37fxchfa>Tons of Skills by <a href="https://intentsolutions.io" target="_blank" data-astro-cid-37fxchfa>Intent Solutions</a>. Marine. Citadel Grad. 20 years ops → self-taught dev → AI architect.</p> <p class="footer-copyright" data-astro-cid-37fxchfa>© 2026 Tons of Skills | <a href="https://intentsolutions.io" target="_blank" data-astro-cid-37fxchfa>Intent Solutions</a></p> </div> </div> </footer> <!-- Light theme global overrides (must be unscoped to target body/nav/footer) --> <!-- Email Signup Form Handler --> <script type="module"> import { getFunctions, httpsCallable } from 'https://www.gstatic.com/firebasejs/11.8.1/firebase-functions.js'; import { getApp } from 'https://www.gstatic.com/firebasejs/11.8.1/firebase-app.js'; function initSignupForms() { var functions; try { functions = getFunctions(getApp()); } catch (e) { console.warn('[TonsOfSkills] Firebase not initialized — signup forms disabled:', e.message); document.querySelectorAll('[data-signup-form] button').forEach(function(btn) { btn.disabled = true; btn.textContent = 'Unavailable'; btn.style.opacity = '0.5'; btn.style.cursor = 'not-allowed'; }); return; } var subscribeEmail = httpsCallable(functions, 'subscribeEmail'); document.querySelectorAll('[data-signup-form]').forEach(function(form) { form.addEventListener('submit', async function(e) { e.preventDefault(); var input = form.querySelector('input[type="email"]'); const honeypot = form.querySelector('input[name="website"]'); var btn = form.querySelector('button'); var originalText = btn.textContent; btn.textContent = 'Subscribing...'; btn.disabled = true; try { var result = await subscribeEmail({ email: input.value, source: form.dataset.signupForm, website: honeypot ? honeypot.value : '' }); if (result.data.status === 'already_subscribed') { btn.textContent = 'Already on it!'; } else { btn.textContent = 'You\'re in!'; } btn.style.opacity = '0.85'; input.value = ''; if (window.logFirebaseEvent) { window.logFirebaseEvent('email_signup', { source: form.dataset.signupForm }); } } catch (err) { if (err.code === 'invalid-argument' && (!input.value || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input.value))) { btn.textContent = 'Invalid Email'; } else { btn.textContent = 'Something went wrong'; } setTimeout(function() { btn.textContent = originalText; btn.disabled = false; btn.style.opacity = ''; }, 3000); } }); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initSignupForms); } else { initSignupForms(); } </script> <!-- Nomination Form Handler --> <script type="module"> import { getFunctions, httpsCallable } from 'https://www.gstatic.com/firebasejs/11.8.1/firebase-functions.js'; import { getApp } from 'https://www.gstatic.com/firebasejs/11.8.1/firebase-app.js'; function initNominationForms() { var functions; try { functions = getFunctions(getApp()); } catch (e) { document.querySelectorAll('[data-nomination-form] button').forEach(function(btn) { btn.disabled = true; btn.textContent = 'Unavailable'; btn.style.opacity = '0.5'; btn.style.cursor = 'not-allowed'; }); return; } var submitNomination = httpsCallable(functions, 'submitNomination'); var ghPattern = /^https:\/\/github\.com\/[^\/]+\/[^\/\s]+/; document.querySelectorAll('[data-nomination-form]').forEach(function(form) { form.addEventListener('submit', async function(e) { e.preventDefault(); var input = form.querySelector('input[name="repo"]'); var honeypot = form.querySelector('input[name="website"]'); var btn = form.querySelector('button'); var originalText = btn.textContent; if (!ghPattern.test(input.value)) { btn.textContent = 'GitHub URLs only'; setTimeout(function() { btn.textContent = originalText; btn.disabled = false; }, 3000); return; } btn.textContent = 'Submitting...'; btn.disabled = true; try { var result = await submitNomination({ repoUrl: input.value, source: form.dataset.nominationForm, website: honeypot ? honeypot.value : '' }); btn.textContent = result.data.status === 'already_nominated' ? 'Already submitted!' : 'Submitted!'; btn.style.opacity = '0.85'; input.value = ''; if (window.logFirebaseEvent) { window.logFirebaseEvent('killer_skill_nomination', { source: form.dataset.nominationForm }); } } catch (err) { btn.textContent = 'Something went wrong'; setTimeout(function() { btn.textContent = originalText; btn.disabled = false; btn.style.opacity = ''; }, 3000); } }); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initNominationForms); } else { initNominationForms(); } </script> <!-- Theme Toggle Script --> <script> document.addEventListener('DOMContentLoaded', function() { var themeToggle = document.querySelector('.theme-toggle'); if (themeToggle) { themeToggle.addEventListener('click', function() { var current = document.documentElement.getAttribute('data-theme') || 'dark'; var next = current === 'dark' ? 'light' : 'dark'; document.documentElement.setAttribute('data-theme', next); localStorage.setItem('theme', next); var meta = document.querySelector('meta[name="theme-color"]'); if (meta) meta.setAttribute('content', next === 'dark' ? '#1a1816' : '#f5f3f0'); }); } window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', function(e) { if (!localStorage.getItem('theme')) { document.documentElement.setAttribute('data-theme', e.matches ? 'light' : 'dark'); } }); }); </script> <!-- Nav Scroll State --> <script> (function() { var nav = document.querySelector('nav'); if (!nav) return; function onScroll() { if (window.scrollY > 20) { nav.classList.add('scrolled'); } else { nav.classList.remove('scrolled'); } } window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); })(); </script> <!-- Mobile Menu Script --> <script> document.addEventListener('DOMContentLoaded', function() { var menuToggle = document.querySelector('.mobile-menu-toggle'); var navLinks = document.querySelector('.nav-links'); var menuIcon = document.querySelector('.menu-icon'); var closeIcon = document.querySelector('.close-icon'); if (menuToggle && navLinks) { menuToggle.addEventListener('click', function() { navLinks.classList.toggle('active'); document.body.classList.toggle('menu-open'); if (navLinks.classList.contains('active')) { menuIcon.style.display = 'none'; closeIcon.style.display = 'block'; } else { menuIcon.style.display = 'block'; closeIcon.style.display = 'none'; } }); navLinks.querySelectorAll('a').forEach(function(link) { link.addEventListener('click', function() { navLinks.classList.remove('active'); document.body.classList.remove('menu-open'); menuIcon.style.display = 'block'; closeIcon.style.display = 'none'; }); }); document.addEventListener('click', function(e) { if (navLinks.classList.contains('active') && !navLinks.contains(e.target) && !menuToggle.contains(e.target)) { navLinks.classList.remove('active'); document.body.classList.remove('menu-open'); menuIcon.style.display = 'block'; closeIcon.style.display = 'none'; } }); } // Track outbound link clicks (nav + footer) document.querySelectorAll('nav a[target="_blank"], footer a[target="_blank"]').forEach(function(link) { link.addEventListener('click', function() { if (window.trackEvent) { window.trackEvent('outbound_click', { url: link.href, link_text: link.textContent.trim() }); } }); }); }); </script> </body> </html>