docs

SKILL.md

Codebase Deep Analysis — Documentation Generator

Generate structured, DeepWiki-style technical documentation for a codebase, with a self-contained HTML viewer (no server required).

When to Use This Skill

Use this skill when the user:

  • Asks to analyze, explain, or document a codebase
  • Wants to generate project documentation or a code wiki
  • Says "explain this project", "document this repo", "create docs for this codebase"
  • Wants to view previously generated docs ("open the docs", "show me the documentation")
  • Asks for architecture analysis or code structure overview

Arguments

Parse the user's request to determine:

  1. Mode:
    • User wants to view existing docs → Serve mode
    • User wants to generate or update docs → Generate mode
  2. Language: Look for language hints (e.g., "in English", "用中文", lang:en). Default: Chinese (zh).
  3. Target path: The project directory to analyze. Default: current project root.

Serve Mode

When the user just wants to view existing docs:

  1. Locate {target}/docs/index.html. If it doesn't exist, tell the user and suggest running generation first.
  2. Open in browser:
    if command -v cmd.exe &>/dev/null; then
      cmd.exe //c start "" "{target}\docs\index.html"
    elif command -v open &>/dev/null; then
      open {target}/docs/index.html
    else
      xdg-open {target}/docs/index.html
    fi
    
  3. Done. No generation, no server needed.

Generate Mode

Step 1: Scan & Plan

  1. Traverse the project directory structure. Identify core modules, subsystems, entry points, package dependencies, and configuration.

  2. Produce a numbered chapter outline dynamically based on the actual codebase — never use a fixed template. Example:

    1   - Project Overview & Architecture
    1.1 - Directory Structure & Module Layout
    2   - Core Subsystem A
    2.1 - Data Structures in A
    3   - Core Subsystem B
    ...
    
  3. Incremental detection — if docs/ already has chapter files, determine what needs (re)generation:

    a. Check if docs/.gen-meta.json exists. This file stores the git commit hash from the last generation:

    { "commit": "abc1234", "generated_at": "2025-06-01T12:00:00Z" }
    

    b. If the file exists AND the project is a git repo, run:

    git diff --name-only {last_commit}..HEAD
    

    to get the list of source files changed since last generation. c. For each existing doc file, parse its <details><summary>Related Source Files</summary> block to extract the list of source files it references. d. Mark each chapter with one of three statuses:

    Status Condition Action
    NEW No matching doc file exists Must generate
    STALE Doc exists, but one or more of its referenced source files appear in the git diff Recommend regeneration
    UP-TO-DATE Doc exists, none of its referenced source files changed Skip

    e. If .gen-meta.json doesn't exist or it's not a git repo, fall back to filename-only comparison: files that exist are marked EXISTS (user decides), missing files are NEW.

  4. Present the outline with status labels to the user. Example:

    1   - Project Overview & Architecture        [UP-TO-DATE]
    1.1 - Directory Structure & Module Layout    [STALE — 3 source files changed]
    2   - Frontend UI Layer                      [STALE — 5 source files changed]
    2.1 - Message Rendering                      [UP-TO-DATE]
    3   - New Subsystem C                        [NEW]
    

    Wait for confirmation before proceeding. The user may override: force-regenerate UP-TO-DATE chapters, or skip STALE ones.

Step 2: Parallel Page Generation (CRITICAL)

After confirmation, generate only the chapters marked NEW or STALE (plus any the user explicitly requested) — simultaneously using parallel Agent calls.

Execution rule: dispatch one Agent call per chapter to generate, ALL in a SINGLE response. This maximizes parallelism.

Skip chapters marked UP-TO-DATE (unless the user explicitly asked to regenerate them).

Each Agent call should use subagent_type: "general-purpose" and include this prompt structure:

You are writing ONE chapter of a codebase documentation project.

## Your Assignment
- Chapter: {number} - {title}
- Output file: docs/{number}-{slug}.md
- Scope: {what this chapter should cover}
- **Language: {LANG}** — ALL prose, headings, summaries, and descriptions MUST be written in this language. Code snippets, file paths, and technical identifiers remain as-is.

## Full Document Outline (for cross-references)
{paste the complete outline here}

## Instructions
1. Use Glob and Read to find and read all source files relevant to this chapter.
2. Analyze the code thoroughly — trace logic, identify patterns, understand data flow.
3. Write comprehensive documentation following the Page Format below. ALL text must be in **{LANG}**.
4. Use Write to save the result to the output file path.

## Page Format

### File Structure
```markdown
# {Chapter Number} {Title}

<details>
<summary>Related Source Files</summary>

- path/to/file1.ts
- path/to/file2.ts

</details>

## Overview

Brief description of chapter scope and core concepts.

Related chapters: [Chapter Title](./number-slug.md), [Other Chapter](./number-slug.md)

---

## {Section Heading}

(Detailed content with Mermaid diagrams, tables, code excerpts, and explanations)

> **Sources**: `src/foo/bar.ts:42-108`, `src/foo/baz.ts:15-30`

---

## Summary

Source References (Mandatory)

  • Every technical claim MUST cite its source: file_path:start_line-end_line
  • Each section ends with > **Sources**: ... listing all references
  • Top-of-page collapsible lists ALL files referenced on the page

Cross-References

  • Use relative links: [Chapter Title](./number-slug.md)
  • Overview page (1-overview.md) serves as navigation hub linking to all other pages

Mermaid Diagrams

Select diagram type by content:

Scenario Type
Module dependencies, architecture layers graph TD / graph TB
Data flow, pipelines graph LR
Decision logic, conditional branches flowchart TD
Lifecycles, request/response sequences sequenceDiagram
State machines stateDiagram-v2
Type/class relationships, data models classDiagram
Database schema erDiagram

Rules: use subgraph for grouping; descriptive labels like Router["Request Router"]; --> for direct, -.-> for indirect relations; brief explanation near each diagram.

Tables

Use for: field docs (Name | Type | Description), config options, API endpoints, env vars.


File naming convention:

docs/ ├── 1-overview.md ├── 1.1-repo-structure.md ├── 2-core-system-a.md ├── 2.1-data-structures.md └── ...


### Step 3: Build Self-Contained HTML Viewer

After ALL parallel Agent tasks complete:

1. **Write `docs/build-html.js`** — copy the exact script below verbatim (do NOT modify it):

```javascript
#!/usr/bin/env node
// Build a self-contained index.html from all .md files in this directory.
const fs = require('fs');
const path = require('path');

const docsDir = __dirname;

// 1. Discover and sort .md files by chapter number
const mdFiles = fs.readdirSync(docsDir)
  .filter(f => f.endsWith('.md'))
  .sort((a, b) => {
    const na = a.replace('.md', '').split('-')[0];
    const nb = b.replace('.md', '').split('-')[0];
    const pa = na.split('.').map(Number);
    const pb = nb.split('.').map(Number);
    for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
      if ((pa[i] || 0) !== (pb[i] || 0)) return (pa[i] || 0) - (pb[i] || 0);
    }
    return 0;
  });

// 2. Read each file, extract metadata
const chapters = mdFiles.map(f => {
  const id = f.replace('.md', '');
  const raw = fs.readFileSync(path.join(docsDir, f), 'utf-8');
  const titleMatch = raw.match(/^#\s+(.+)/m);
  const title = titleMatch ? titleMatch[1].trim() : id;
  const isSub = id.includes('.');

  // Pre-process mermaid: replace ```mermaid...``` with placeholder divs
  const content = raw.replace(/```mermaid\n([\s\S]*?)```/g, (_, code) => {
    return '<div class="mermaid">\n' + code.trim() + '\n</div>';
  });

  return { id, title, sub: isSub, content };
});

// 3. Build the data script block safely
//    JSON.stringify handles all escaping, but we must also prevent </script> from
//    appearing in the output (it would prematurely close the <script> tag).
function safeJsonEmbed(str) {
  return JSON.stringify(str).replace(/<\//g, '<\\/');
}

const chaptersJson = JSON.stringify(chapters.map(c => ({ id: c.id, title: c.title, sub: c.sub })));

const dataLines = chapters.map(c =>
  'allMd[' + safeJsonEmbed(c.id) + ']=' + safeJsonEmbed(c.content) + ';'
);

// 4. Build the HTML (using string concatenation to avoid template literal issues)
const parts = [];

parts.push(`<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Codebase Documentation</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/typescript.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/bash.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/sql.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/dockerfile.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/json.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/css.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/yaml.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/markdown.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"><\/script>
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{--sw:280px;--bg:#fff;--bg2:#f8f9fa;--sbg:#1e293b;--shov:#334155;--acc:#3b82f6;--t:#1e293b;--t2:#64748b;--brd:#e2e8f0;--cbg:#f1f5f9}
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;color:var(--t);background:var(--bg);line-height:1.7}
.sidebar{position:fixed;left:0;top:0;bottom:0;width:var(--sw);background:var(--sbg);overflow-y:auto;z-index:100;scrollbar-width:thin;scrollbar-color:#475569 transparent}
.sidebar::-webkit-scrollbar{width:6px}.sidebar::-webkit-scrollbar-thumb{background:#475569;border-radius:3px}
.sidebar-header{padding:20px 16px;border-bottom:1px solid #334155;position:sticky;top:0;background:var(--sbg);z-index:1}
.sidebar-header h1{font-size:16px;font-weight:700;color:#fff;letter-spacing:-.02em}
.sidebar-header .sub{font-size:11px;color:#94a3b8;margin-top:4px}
.sidebar-nav{padding:8px 0}
.nav-item{display:block;padding:7px 16px 7px 20px;color:#cbd5e1;text-decoration:none;font-size:13px;cursor:pointer;transition:all .15s;border-left:3px solid transparent;line-height:1.4}
.nav-item:hover{background:var(--shov);color:#e2e8f0}
.nav-item.active{background:rgba(59,130,246,.15);color:#fff;border-left-color:var(--acc);font-weight:500}
.nav-item.is-sub{padding-left:32px;font-size:12px;color:#94a3b8}
.nav-item.is-sub:hover{color:#cbd5e1}.nav-item.is-sub.active{color:#fff}
.main{margin-left:var(--sw);max-width:920px;padding:40px 48px 80px}
.chapter{margin-bottom:56px;padding-bottom:40px;border-bottom:1px solid var(--brd)}.chapter:last-child{border-bottom:none}
.ch-toggle{display:flex;align-items:center;gap:8px;cursor:pointer;user-select:none;padding:8px 0;margin-bottom:4px}
.ch-toggle .arrow{display:inline-block;width:20px;height:20px;font-size:12px;color:var(--t2);transition:transform .2s;text-align:center;line-height:20px}
.ch-toggle .arrow.collapsed{transform:rotate(-90deg)}
.ch-content.collapsed{display:none}
.ch-content h1{font-size:28px;font-weight:700;letter-spacing:-.03em;margin-bottom:16px;color:var(--t);padding-bottom:12px;border-bottom:2px solid var(--acc)}
.ch-content h2{font-size:20px;font-weight:600;margin:32px 0 12px;color:var(--t)}
.ch-content h3{font-size:16px;font-weight:600;margin:24px 0 8px;color:var(--t)}
.ch-content h4{font-size:14px;font-weight:600;margin:20px 0 6px;color:var(--t)}
.ch-content p{margin:8px 0}
.ch-content a{color:var(--acc);text-decoration:none}.ch-content a:hover{text-decoration:underline}
.ch-content ul,.ch-content ol{margin:8px 0;padding-left:24px}.ch-content li{margin:4px 0}
.ch-content blockquote{border-left:3px solid var(--acc);padding:4px 16px;margin:12px 0;background:#f0f7ff;color:var(--t2);font-size:14px}
.ch-content code{background:var(--cbg);padding:2px 6px;border-radius:4px;font-size:13px;font-family:'SF Mono','Fira Code','Cascadia Code',Consolas,monospace}
.ch-content pre{background:var(--cbg);border:1px solid var(--brd);border-radius:8px;padding:16px;overflow-x:auto;margin:12px 0;font-size:13px;line-height:1.5}
.ch-content pre code{background:none;padding:0;border-radius:0}
.ch-content table{border-collapse:collapse;width:100%;margin:12px 0;font-size:14px}
.ch-content th,.ch-content td{border:1px solid var(--brd);padding:8px 12px;text-align:left}
.ch-content th{background:var(--bg2);font-weight:600}
.ch-content details{background:var(--bg2);border:1px solid var(--brd);border-radius:6px;padding:8px 16px;margin:12px 0}
.ch-content summary{cursor:pointer;font-weight:500;color:var(--t2);font-size:14px}
.ch-content hr{border:none;border-top:1px solid var(--brd);margin:24px 0}
.mermaid{text-align:center;margin:16px 0;background:#fff;border-radius:8px;padding:16px}
.menu-toggle{display:none;position:fixed;top:12px;left:12px;z-index:200;background:var(--sbg);color:#fff;border:none;border-radius:6px;padding:8px 12px;font-size:18px;cursor:pointer}
.back-to-top{position:fixed;bottom:24px;right:24px;width:40px;height:40px;border-radius:50%;background:var(--acc);color:#fff;border:none;font-size:18px;cursor:pointer;opacity:0;transition:opacity .3s;z-index:99;box-shadow:0 2px 8px rgba(0,0,0,.15)}
.back-to-top.visible{opacity:1}
@media(max-width:768px){.sidebar{transform:translateX(-100%);transition:transform .3s}.sidebar.open{transform:translateX(0)}.main{margin-left:0;padding:24px 16px 80px}.menu-toggle{display:block}}
@media print{.sidebar,.menu-toggle,.back-to-top{display:none}.main{margin-left:0;max-width:100%;padding:20px}.chapter{break-inside:avoid}.ch-content.collapsed{display:block!important}}
</style>
</head>
<body>
<button class="menu-toggle" onclick="document.querySelector('.sidebar').classList.toggle('open')">&#9776;</button>
<nav class="sidebar" id="sidebar">
  <div class="sidebar-header"><h1>Codebase Documentation</h1><div class="sub">Generated by AI</div></div>
  <div class="sidebar-nav" id="sidebarNav"></div>
</nav>
<main class="main" id="main"></main>
<button class="back-to-top" id="backToTop" onclick="window.scrollTo({top:0,behavior:'smooth'})">&#8593;</button>
`);

// Data script — all content embedded here
parts.push('<script>\nvar chapters=' + chaptersJson + ';\nvar allMd={};\n');
parts.push(dataLines.join('\n'));
parts.push('\n<\/script>\n');

// App script — renders everything
parts.push(`<script>
document.addEventListener('DOMContentLoaded',async function(){
  marked.setOptions({breaks:false,gfm:true});
  mermaid.initialize({startOnLoad:false,theme:'default',securityLevel:'loose',
    fontFamily:'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif'});

  // Build sidebar
  var nav=document.getElementById('sidebarNav');
  chapters.forEach(function(ch){
    var a=document.createElement('a');
    a.className='nav-item'+(ch.sub?' is-sub':'');
    a.textContent=ch.title;
    a.href='#'+ch.id;
    a.onclick=function(e){
      e.preventDefault();
      var el=document.getElementById(ch.id);
      if(el)el.scrollIntoView({behavior:'smooth'});
      document.querySelector('.sidebar').classList.remove('open');
    };
    nav.appendChild(a);
  });

  // Render chapters
  var main=document.getElementById('main');
  for(var i=0;i<chapters.length;i++){
    var ch=chapters[i], md=allMd[ch.id];
    if(!md) continue;

    var section=document.createElement('section');
    section.className='chapter'; section.id=ch.id;

    var toggle=document.createElement('div');
    toggle.className='ch-toggle';
    toggle.innerHTML='<span class="arrow">&#9660;</span><strong style="font-size:14px;color:var(--t2)">'+
      ch.title.replace(/</g,'&lt;')+'</strong>';

    var content=document.createElement('div');
    content.className='ch-content';

    // marked.parse — mermaid divs already in the markdown as raw HTML, marked passes them through
    content.innerHTML=marked.parse(md);

    toggle.onclick=(function(c,t){return function(){
      c.classList.toggle('collapsed');
      t.querySelector('.arrow').classList.toggle('collapsed');
    };})(content,toggle);

    section.appendChild(toggle);
    section.appendChild(content);
    main.appendChild(section);
  }

  // Render mermaid diagrams
  try{await mermaid.run({querySelector:'.mermaid'});}catch(e){console.warn('Mermaid:',e);}

  // Syntax highlight code blocks
  document.querySelectorAll('pre code').forEach(function(b){
    if(!b.classList.contains('hljs')){
      var lang=b.className.match(/language-(\\w+)/);
      if(lang&&hljs.getLanguage(lang[1])) hljs.highlightElement(b);
      else hljs.highlightElement(b);
    }
  });

  // Scroll spy — highlight active nav item
  var observer=new IntersectionObserver(function(entries){
    entries.forEach(function(e){
      if(e.isIntersecting){
        document.querySelectorAll('.nav-item').forEach(function(n){n.classList.remove('active')});
        var link=document.querySelector('.nav-item[href="#'+e.target.id+'"]');
        if(link) link.classList.add('active');
      }
    });
  },{rootMargin:'-80px 0px -80% 0px'});
  document.querySelectorAll('.chapter').forEach(function(c){observer.observe(c)});

  // Back to top
  var btn=document.getElementById('backToTop');
  window.addEventListener('scroll',function(){btn.classList.toggle('visible',window.scrollY>400)});
});
<\/script>
</body>
</html>`);

const output = parts.join('');
fs.writeFileSync(path.join(docsDir, 'index.html'), output, 'utf-8');
const sizeKB = Math.round(Buffer.byteLength(output, 'utf-8') / 1024);
console.log(`Built index.html: ${chapters.length} chapters, ${sizeKB}KB`);
  1. Run the build script:

    cd {target}/docs && node build-html.js
    
  2. Write docs/.gen-meta.json to record the current generation state (for future incremental detection):

    echo '{"commit":"'$(git -C {target} rev-parse HEAD 2>/dev/null || echo "none")'","generated_at":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' > {target}/docs/.gen-meta.json
    
  3. Open in browser (no server needed — it's a self-contained HTML file):

    if command -v cmd.exe &>/dev/null; then
      cmd.exe //c start "" "{target}\docs\index.html"
    elif command -v open &>/dev/null; then
      open {target}/docs/index.html
    else
      xdg-open {target}/docs/index.html
    fi
    

Confidentiality Rules (CRITICAL)

Generated documentation must NEVER contain confidential or sensitive information. Each agent MUST follow these rules:

  • NO real secrets: Never include actual API keys, tokens, passwords, connection strings, or credentials. If source code contains them, redact or replace with placeholders (e.g., sk-xxx..., <your-api-key>).
  • NO internal URLs or IPs: Do not expose internal endpoints, private domain names, or IP addresses. Use generic examples like https://api.example.com.
  • NO personal data: Do not include real email addresses, employee names, user IDs, or other PII. Use placeholders like user@example.com, John Doe.
  • Code examples are OK: Environment variable names (e.g., process.env.ANTHROPIC_API_KEY) and placeholder values in code snippets are fine — only real values are prohibited.
  • Public info is OK: Open-source author names, public repository URLs, and Docker Hub image names from the source code may be included as-is.

If in doubt, redact. It is always safer to use a placeholder than to risk leaking sensitive information.

Output Rules

  1. Step 1 output: chapter outline with status labels (NEW/STALE/UP-TO-DATE). Wait for user confirmation.
  2. Step 2: launch agents ONLY for chapters that need generation, ALL in parallel in ONE response.
  3. Step 3: write build script, run it, open HTML in browser.
Weekly Installs
1
First Seen
6 days ago
Installed on
zencoder1
amp1
cline1
openclaw1
opencode1
cursor1