什么是skill#
可复用工作流,核心是 SKILL.md,适合沉淀任务步骤、领域知识、参考资料和脚本
my-skill/
├── SKILL.md 核心描述文件
├── scripts/ 可执行脚本
├── references/ 参考文档
└── assets/ 静态资源SKILL.md 标注好技能名称 和作用,之后定义流程,到了哪一步应该做什么事,有什么注意事项,你能调用哪些工具,什么流程调用什么脚本去做
skill示例#
主文件
---
name: weekly-code-report
description: 当用户要求根据 Git 提交记录和实际代码改动生成中文日报、周报、工作日志或进展总结时使用。默认收集本周周一 00:00 到当前时间的提交,也支持用户指定时间范围;生成报告前必须同时参考 commit 信息和代码 diff,并将最终报告写入 Markdown 文档文件。
---
# 每周代码日报生成
使用这个技能,根据 Git 历史和实际代码改动生成中文开发日报或周报。
## 工作流程
1. 确认目标目录是 Git 仓库。
2. 除非用户指定时间范围,否则使用默认范围:
- 开始时间:当前自然周周一 00:00,本地时间。
- 结束时间:当前时间。
- 示例:如果今天是周二,就收集周一到周二当前时间为止的提交。
3. 在目标仓库中运行 `scripts/collect-weekly-git-report.ps1`,收集提交元数据、文件统计和 diff。
4. 先阅读脚本生成的素材 Markdown,再撰写报告。
5. 最终报告必须使用中文,并且只能基于 commit 信息和实际代码变更生成。
6. 最终报告必须写入目标仓库根目录的 Markdown 文档,不要只在对话中输出正文。
7. 文档命名使用脚本素材中的“建议报告文件”,格式为 `YYYY年MM月第N周.md`,例如 `2026年06月第2周.md`。
8. 最终回复只说明已生成的文档路径和必要备注,不要粘贴完整报告正文。
9. 如果时间范围内没有提交,仍按命名规则生成报告文档,在文档中明确说明“本时间范围内没有提交记录”,不要编造工作内容。
## 命令
在目标 Git 仓库中运行:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File "<skill-dir>\scripts\collect-weekly-git-report.ps1" -Output ".\weekly-code-report-source.md"
```
可选参数:
```powershell
-Since "2026-06-08 00:00:00"
-Until "2026-06-10 18:00:00"
-Author "name-or-email"
-MaxCommits 80
-MaxDiffChars 80000
```
## 报告规则
- 优先输出具体工作项,不要只罗列 commit。
- 将相关提交合并成有意义的条目。
- 如果 diff 能看出关键文件或模块,需要在报告中体现。
- 必要时区分已完成工作、进行中工作、问题修复、测试验证和风险后续。
- 不要包含敏感信息或大段代码。
- 不要声称代码未体现的业务影响。
- 如果 commit 信息很模糊,需要结合文件统计和 diff 谨慎推断,并使用保守表述。
- 生成最终报告文档前,先从素材 Markdown 头部读取“建议报告文件”。
- 若目标仓库根目录已存在同名报告,先读取确认内容是否属于同一时间范围,再在现有文件基础上更新;不要覆盖无关文档。
## 输出格式
默认参考 `references/report-format.md`。
最终报告应适合日报或站会使用,保持简洁:
```txt
今日/本周工作总结
主要完成
问题修复
代码质量/工程化
测试与验证
风险与后续
提交范围
```
## 边界情况
- 如果仓库在时间范围内没有提交,生成命名规范的报告文档,正文写明“本时间范围内没有提交记录”,然后停止。
- 如果当前目录不是 Git 仓库,请用户提供目标仓库路径。
- 如果 diff 被脚本截断,需要说明报告基于截断前可见的 diff 上下文。
- 如果存在多个作者,而用户要求生成个人日报,需要询问作者过滤条件;在安全明确时可以使用当前 Git 用户。
- `collect-weekly-git-report.ps1` 需要保持 UTF-8 编码并兼容 Windows PowerShell 5.1;不要把 Markdown 反引号直接写进双引号字符串。
脚本
param(
[string]$Since = "",
[string]$Until = "",
[string]$Author = "",
[string]$Output = "",
[int]$MaxCommits = 80,
[int]$MaxDiffChars = 80000
)
$ErrorActionPreference = "Stop"
function Get-WeekStart {
param([datetime]$Now)
# PowerShell 中 Sunday 是 0,Monday 是 1;这里转换成距离周一的天数。
$daysSinceMonday = (([int]$Now.DayOfWeek + 6) % 7)
return $Now.Date.AddDays(-$daysSinceMonday)
}
function Convert-ToGitDate {
param([datetime]$Date)
return $Date.ToString("yyyy-MM-dd HH:mm:ss zzz")
}
function Get-ReportFileName {
param([datetime]$Date)
# 以周一作为一周开始,计算当前日期在当月属于第几周。
$firstDayOfMonth = Get-Date -Year $Date.Year -Month $Date.Month -Day 1 -Hour 0 -Minute 0 -Second 0
$daysBeforeFirstMonday = (([int]$firstDayOfMonth.DayOfWeek + 6) % 7)
$weekOfMonth = [Math]::Floor((($Date.Day - 1) + $daysBeforeFirstMonday) / 7) + 1
return "{0:yyyy}年{0:MM}月第{1}周.md" -f $Date, $weekOfMonth
}
function Limit-Text {
param(
[string]$Text,
[int]$Limit
)
if ($Limit -le 0 -or $Text.Length -le $Limit) {
return $Text
}
return $Text.Substring(0, $Limit) + "`n`n[内容已截断,原始长度: $($Text.Length) 字符]"
}
$repoRoot = (& git rev-parse --show-toplevel 2>$null)
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($repoRoot)) {
throw "当前目录不是 Git 仓库,请在目标仓库根目录或子目录中运行。"
}
$now = Get-Date
if ([string]::IsNullOrWhiteSpace($Since)) {
$sinceDate = Get-WeekStart -Now $now
} else {
$sinceDate = [datetime]::Parse($Since)
}
if ([string]::IsNullOrWhiteSpace($Until)) {
$untilDate = $now
} else {
$untilDate = [datetime]::Parse($Until)
}
$sinceText = Convert-ToGitDate -Date $sinceDate
$untilText = Convert-ToGitDate -Date $untilDate
$logArgs = @(
"log",
"--since=$sinceText",
"--until=$untilText",
"--date=iso-strict",
"--pretty=format:%H%x09%h%x09%ad%x09%an%x09%ae%x09%s",
"--reverse"
)
if (-not [string]::IsNullOrWhiteSpace($Author)) {
$logArgs += "--author=$Author"
}
$commitLines = @(& git @logArgs)
if ($LASTEXITCODE -ne 0) {
throw "读取 Git 提交记录失败。"
}
if ($commitLines.Count -gt $MaxCommits) {
$commitLines = $commitLines | Select-Object -Last $MaxCommits
$commitLimitNote = "提交数量超过 MaxCommits,仅保留最近 $MaxCommits 条。"
} else {
$commitLimitNote = ""
}
$markdown = New-Object System.Text.StringBuilder
[void]$markdown.AppendLine("# 本周代码提交素材")
[void]$markdown.AppendLine("")
[void]$markdown.AppendLine("- 生成时间:$(Convert-ToGitDate -Date $now)")
[void]$markdown.AppendLine("- 仓库路径:$repoRoot")
[void]$markdown.AppendLine("- 统计范围:$sinceText ~ $untilText")
[void]$markdown.AppendLine("- 建议报告文件:$(Get-ReportFileName -Date $untilDate)")
if (-not [string]::IsNullOrWhiteSpace($Author)) {
[void]$markdown.AppendLine("- 作者过滤:$Author")
}
if (-not [string]::IsNullOrWhiteSpace($commitLimitNote)) {
[void]$markdown.AppendLine("- 注意:$commitLimitNote")
}
[void]$markdown.AppendLine("")
if ($commitLines.Count -eq 0) {
[void]$markdown.AppendLine("## 提交列表")
[void]$markdown.AppendLine("")
[void]$markdown.AppendLine("本时间范围内没有提交记录。")
} else {
[void]$markdown.AppendLine("## 提交列表")
[void]$markdown.AppendLine("")
[void]$markdown.AppendLine("| 时间 | 提交 | 作者 | 信息 |")
[void]$markdown.AppendLine("| --- | --- | --- | --- |")
$commits = @()
foreach ($line in $commitLines) {
if ([string]::IsNullOrWhiteSpace($line)) {
continue
}
$parts = $line -split "`t", 6
if ($parts.Count -lt 6) {
continue
}
$commit = [pscustomobject]@{
Hash = $parts[0]
ShortHash = $parts[1]
Date = $parts[2]
AuthorName = $parts[3]
AuthorEmail = $parts[4]
Subject = $parts[5]
}
$commits += $commit
$safeSubject = $commit.Subject.Replace("|", "\|")
[void]$markdown.AppendLine(('| {0} | `{1}` | {2} | {3} |' -f $commit.Date, $commit.ShortHash, $commit.AuthorName, $safeSubject))
}
[void]$markdown.AppendLine("")
[void]$markdown.AppendLine("## 逐提交代码变更")
foreach ($commit in $commits) {
[void]$markdown.AppendLine("")
[void]$markdown.AppendLine(('### `{0}` {1}' -f $commit.ShortHash, $commit.Subject))
[void]$markdown.AppendLine("")
[void]$markdown.AppendLine("- 作者:$($commit.AuthorName) <$($commit.AuthorEmail)>")
[void]$markdown.AppendLine("- 时间:$($commit.Date)")
[void]$markdown.AppendLine(('- 完整提交:`{0}`' -f $commit.Hash))
[void]$markdown.AppendLine("")
$stat = (& git show --stat --find-renames --format= $commit.Hash)
[void]$markdown.AppendLine("#### 文件统计")
[void]$markdown.AppendLine("")
[void]$markdown.AppendLine('```txt')
[void]$markdown.AppendLine(($stat -join "`n"))
[void]$markdown.AppendLine('```')
[void]$markdown.AppendLine("")
$nameStatus = (& git show --name-status --find-renames --format= $commit.Hash)
[void]$markdown.AppendLine("#### 文件状态")
[void]$markdown.AppendLine("")
[void]$markdown.AppendLine('```txt')
[void]$markdown.AppendLine(($nameStatus -join "`n"))
[void]$markdown.AppendLine('```')
[void]$markdown.AppendLine("")
$diff = (& git show --find-renames --format= --unified=80 --no-ext-diff $commit.Hash) -join "`n"
$diff = Limit-Text -Text $diff -Limit $MaxDiffChars
[void]$markdown.AppendLine("#### Diff")
[void]$markdown.AppendLine("")
[void]$markdown.AppendLine('```diff')
[void]$markdown.AppendLine($diff)
[void]$markdown.AppendLine('```')
}
}
$content = $markdown.ToString()
if ([string]::IsNullOrWhiteSpace($Output)) {
$content
} else {
$resolvedOutput = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Output)
[System.IO.File]::WriteAllText($resolvedOutput, $content, [System.Text.UTF8Encoding]::new($false))
Write-Output "已生成:$resolvedOutput"
}
参考文档
# 报告格式参考
生成日报或周报时,优先使用下面结构。
## 日报格式
```md
# 开发日报
## 今日完成
-
## 问题修复
-
## 代码质量 / 工程化
-
## 测试与验证
-
## 风险与后续
-
## 提交范围
- 时间范围:
- 提交数量:
- 主要提交:
```
## 周内日报格式
如果用户说“本周日报”或“本周到今天”,按周一到今天归纳:
```md
# 本周开发进展
## 时间范围
-
## 主要完成
-
## 重点代码变更
-
## 问题修复
-
## 测试与验证
-
## 风险与后续
-
## 提交依据
-
```
## 写作规则
- 用中文。
- 先总结业务或技术结果,再补充关键代码依据。
- 不要逐条复制 commit message。
- 不要贴大段代码。
- 没有测试信息时写“提交记录中未体现明确测试变更”。
- diff 被截断时写“以下总结基于截断前可见 diff”。
之后就能在codex @这个技能了
什么是mcp#
Model Context Protocol 是由 Anthropic 开源的一种标准化协议
我自己的理解是 mcp 其实就是把之前封装到agent里面的tool,给解耦出来了,然后起一个服务给AI,让AI知道我这个服务里有哪些工具,工具分别都是干嘛的,需要什么参数,返回什么结果
其实就是为了服务化,我这个工具只需要暴漏我自己的能力,不关心调用我的是Claude 还是 gpt
打个比方,在没有mcp之前,tool就像是焊死在机器人上的机械臂,当我想把这个手臂给其他机器人用是不行的,而有了mcp之后,tool就变成了一个有通用标准的模块化外设,只要接入它,就能指挥它
那这个mcp到底长啥样?怎么查看呢?除了查看源码的方式,还有一种方式,就是使用 mcp inspector
<font style="color:rgb(2, 8, 23);">MCP Inspector</font>#
这个东西也是 Anthropic 出的
控制台输入命令
npx @modelcontextprotocol/inspector会打开一个本地服务,可用于调试mcp
用谷歌的chrome-devtools-mcp 演示一下
ok,连接上了,这可以看到一大堆工具
像什么点击页面元素,拖动页面元素,关闭页面,还有打开一个新页面,都是chrome-devtools-mcp暴漏出来的能力
做个示例#
选择<font style="color:rgb(2, 8, 23);">navigate_page这个工具</font>
Go to a URL, or back, forward, or reload. Use project URL if not specified otherwise.
// 这是它工具的描述 AI 根据这个描述判断它能干些啥<font style="color:rgb(75, 85, 99);"></font>
<font style="color:rgb(2, 8, 23);"></font>
注意,地址需要加上协议,不能只填域名
点击run 就会重新开一个浏览器窗口,并打开百度
chrome-devtools-mcp 源码#
可以看到工具的名称,描述,还有调用的参数
自己写一个mcp服务实现系统的自动登录#
为了了解这玩意儿到底是个啥,还是自己写一个吧
安装这三个包
{
"dependencies": {
"@modelcontextprotocol/sdk": "^1.29.0", // 创建mcp服务的sdk
"puppeteer": "^25.1.0", // 控制浏览器的
"zod": "^4.4.3"// 字段校验
},
"scripts": {
"start": "npx tsx index.ts"
}
}
这是最新的api
我这里只是做个演示,其实正常的像这种mcp 直接对接后端比较好,比如说查报表,做单子,批量增加资料什么的,我定义对应的工具,在这里统一字段,然后调用后端api去实现,当然我只是说说大致流程,实际肯定更复杂
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import puppeteer, { Browser } from "puppeteer";
let browser: Browser;
// 2. 初始化 MCP 服务
const server = new McpServer({
name: "erp-system-mcp",
version: "1.0.0",
});
async function getActivePage(browser: Browser) {
let pages = await browser.pages();
const activePage = pages.find((p) => p.url() !== "about:blank") || pages[0];
return activePage || (await browser.newPage());
}
// 3. 注册工具
server.registerTool(
"auto_login",
{
description: "自动登录云飞扬系统",
inputSchema: {
accountSet: z.string().describe("账套"),
username: z.string().describe("用户名"),
password: z.string().describe("密码"),
},
},
async ({ accountSet, username, password }) => {
const page = await getActivePage(browser); // 总是拿到正确的 page
try {
await page.goto("http://localhost:8081/#/login", {
waitUntil: "networkidle0",
});
await page.setViewport({ width: 1920, height: 1080 });
await page.evaluate(() => {
window.dispatchEvent(new Event("resize"));
});
await new Promise((r) => setTimeout(r, 500));
const inputs = await page.$$('input:not([type="hidden"])');
console.error(`DEBUG: Found ${inputs.length} visible inputs`);
for (let i = 0; i < 3; i++) {
await inputs[i].click({ count: 3 });
await inputs[i].press("Backspace");
}
await inputs[0].type(accountSet, { delay: 100 });
await inputs[1].type(username, { delay: 100 });
await inputs[2].type(password, { delay: 100 });
const loginButtonSelector = 'button[type="submit"]';
await page.waitForSelector(loginButtonSelector, { visible: true });
await page.click(loginButtonSelector);
try {
await page.waitForSelector("::-p-text(工作台)", { timeout: 8000 });
return { content: [{ type: "text", text: `登录成功,已进入工作台` }] };
} catch {
// 如果没跳到工作台,检查页面上有没有错误提示
return {
isError: true,
content: [
{ type: "text", text: `登录请求已发出,但未检测到首页跳转` },
],
};
}
} catch (error) {
return {
isError: true,
content: [{ type: "text", text: `操作失败: ${String(error)}` }],
};
}
},
);
async function main() {
browser = await puppeteer.launch({
headless: false,
});
const transport = new StdioServerTransport();
await server.connect(transport);
console.error(" ERP MCP 服务及内置浏览器已成功启动!");
}
main().catch(console.error);
改一下配置,然后连接mcp工具,看到自定义的工具了
这就走通了
codex 安装使用 mcp#
安装一下
codex mcp add chrome-devtools -- npx -y chrome-devtools-mcp@latest安装完成之后,codex 配置文件会多出一条mcp的配置
再次启动codex的时候,也会同时加载启动这个mcp 服务
查看一下它的工具
让它操作一下浏览器