npx skills add https://github.com/chenxizhang/agent-skills --skill docs为代码库生成结构化的、DeepWiki风格的技术文档,附带一个独立的HTML查看器(无需服务器)。
当用户出现以下情况时使用此技能:
解析用户的请求以确定:
lang:en)。默认:中文 (zh)。当用户只想查看现有文档时:
定位 {target}/docs/index.html。如果不存在,告知用户并建议先运行生成。
在浏览器中打开:
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
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
完成。无需生成,无需服务器。
遍历项目目录结构。识别核心模块、子系统、入口点、包依赖项和配置。
根据实际的代码库动态生成一个带编号的章节大纲 — 切勿使用固定模板。示例:
1 - 项目概述与架构
1.1 - 目录结构与模块布局
2 - 核心子系统 A
2.1 - A 中的数据结构和
3 - 核心子系统 B
...
增量检测 — 如果 docs/ 目录已存在章节文件,确定哪些需要(重新)生成:
a. 检查 docs/.gen-meta.json 是否存在。此文件存储了上次生成时的 git 提交哈希:
{ "commit": "abc1234", "generated_at": "2025-06-01T12:00:00Z" }
b. 如果该文件存在且项目是一个 git 仓库,运行:
git diff --name-only {last_commit}..HEAD
以获取自上次生成以来更改的源文件列表。
c. 对于每个现有的文档文件,解析其 <details><summary>相关源文件</summary> 块以提取其引用的源文件列表。
d. 用以下三种状态之一标记每个章节:
| 状态 | 条件 | 操作 |
|---|---|---|
| NEW | 没有匹配的文档文件存在 | 必须生成 |
| STALE | 文档存在,但其引用的一个或多个源文件出现在 git diff 中 | 建议重新生成 |
| UP-TO-DATE | 文档存在,其引用的所有源文件均未更改 | 跳过 |
e. 如果 .gen-meta.json 不存在或项目不是 git 仓库,则回退到仅基于文件名的比较:存在的文件标记为 EXISTS(由用户决定),缺失的文件标记为 NEW。
向用户展示带有状态标签的大纲。示例:
1 - 项目概述与架构 [UP-TO-DATE]
1.1 - 目录结构与模块布局 [STALE — 3 个源文件已更改]
2 - 前端 UI 层 [STALE — 5 个源文件已更改]
2.1 - 消息渲染 [UP-TO-DATE]
3 - 新子系统 C [NEW]
在继续之前等待用户确认。 用户可以覆盖:强制重新生成 UP-TO-DATE 的章节,或跳过 STALE 的章节。
确认后,仅生成标记为 NEW 或 STALE 的章节(加上用户明确请求的章节)— 同时使用并行 Agent 调用。
执行规则:为每个要生成的章节分派一个 Agent 调用,全部在单个响应中完成。这能最大化并行性。
跳过标记为 UP-TO-DATE 的章节(除非用户明确要求重新生成它们)。
每个 Agent 调用应使用 subagent_type: "general-purpose" 并包含以下提示结构:
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
file_path:start_line-end_line> **Sources**: ... 结尾,列出所有引用[Chapter Title](./number-slug.md)根据内容选择图表类型:
| 场景 | 类型 |
|---|---|
| 模块依赖关系、架构层 | graph TD / graph TB |
| 数据流、管道 | graph LR |
| 决策逻辑、条件分支 | flowchart TD |
| 生命周期、请求/响应序列 | sequenceDiagram |
| 状态机 | stateDiagram-v2 |
| 类型/类关系、数据模型 | classDiagram |
| 数据库模式 | erDiagram |
规则:使用 subgraph 进行分组;使用描述性标签如 Router["Request Router"];--> 表示直接关系,-.-> 表示间接关系;每个图表附近附上简要说明。
用于:字段文档(名称 | 类型 | 描述)、配置选项、API 端点、环境变量。
文件命名约定:
docs/ ├── 1-overview.md ├── 1.1-repo-structure.md ├── 2-core-system-a.md ├── 2.1-data-structures.md └── ...
所有并行 Agent 任务完成后:
docs/build-html.js — 原样复制下面的确切脚本(请勿修改):```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')">☰</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'})">↑</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">▼</span><strong style="font-size:14px;color:var(--t2)">'+
ch.title.replace(/</g,'<')+'</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`);
2. 运行构建脚本 :
cd {target}/docs && node build-html.js
3. 编写 docs/.gen-meta.json 以记录当前生成状态(用于未来的增量检测):
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
4. 在浏览器中打开(无需服务器 — 它是一个独立的 HTML 文件):
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
生成的文档绝不能包含机密或敏感信息。每个 Agent 必须遵守以下规则:
sk-xxx...、<your-api-key>)。https://api.example.com。user@example.com、John Doe。process.env.ANTHROPIC_API_KEY)和代码片段中的占位符_值_是可以的 — 仅禁止真实值。如有疑问,请进行编辑。使用占位符总是比冒险泄露敏感信息更安全。
每周安装
1
仓库
首次出现
今天
安全审计
安装于
zencoder1
amp1
cline1
openclaw1
opencode1
cursor1
agent-browser 浏览器自动化工具 - Vercel Labs 命令行网页操作与测试
147,400 周安装