diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json
index a90f4e5..8826781 100644
--- a/.claude-plugin/plugin.json
+++ b/.claude-plugin/plugin.json
@@ -33,6 +33,7 @@
"./agents/go-build-resolver.md",
"./agents/go-reviewer.md",
"./agents/planner.md",
+ "./agents/python-reviewer.md",
"./agents/refactor-cleaner.md",
"./agents/security-reviewer.md",
"./agents/tdd-guide.md"
diff --git a/.claude/skills/oneskill/.gitignore b/.claude/skills/oneskill/.gitignore
new file mode 100644
index 0000000..2ec0e8a
--- /dev/null
+++ b/.claude/skills/oneskill/.gitignore
@@ -0,0 +1,90 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variable files
+.env
+.env.test
+.env.production
+
+# parcel-bundler cache
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+out
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line if your project uses Gatsby and you want to debug the
+# build, as it contains some public assets. These assets are usually
+# generated from other files and don't need to be in version control.
+# public
+
+# vue-cli build runner target
+target/
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# Tern device bin file
+.tern-port
+
+# Stores VS Code state
+.vscode
+
+# Build output
+dist/
+
+# OS metadata
+.DS_Store
+Thumbs.db
diff --git a/.claude/skills/oneskill/.openskills.json b/.claude/skills/oneskill/.openskills.json
new file mode 100644
index 0000000..2bab6ae
--- /dev/null
+++ b/.claude/skills/oneskill/.openskills.json
@@ -0,0 +1,7 @@
+{
+ "source": "xu-xiang/oneskill",
+ "sourceType": "git",
+ "repoUrl": "https://github.com/xu-xiang/oneskill",
+ "subpath": "",
+ "installedAt": "2026-01-31T16:23:32.899Z"
+}
\ No newline at end of file
diff --git a/.claude/skills/oneskill/LICENSE b/.claude/skills/oneskill/LICENSE
new file mode 100644
index 0000000..efe8c07
--- /dev/null
+++ b/.claude/skills/oneskill/LICENSE
@@ -0,0 +1,176 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of Your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/.claude/skills/oneskill/README.md b/.claude/skills/oneskill/README.md
new file mode 100644
index 0000000..5499615
--- /dev/null
+++ b/.claude/skills/oneskill/README.md
@@ -0,0 +1,88 @@
+
+
+# OneSkill 元管理器(Meta-Manager)
+
+**AI 智能体技能(Agent Skills)的通用桥梁。**
+从 OpenSkills 注册表中发现、安装并映射功能到您的环境。
+
+[](https://www.npmjs.com/package/oneskill)
+[](LICENSE)
+
+[**🇺🇸 English**](README.md) | [**🇨🇳 中文指南**](README_CN.md)
+
+
+
+---
+
+## ⚡️ 什么是 OneSkill?
+
+**OneSkill** 是一款专为 AI 智能体(Agent)(以及人类)设计的元工具,用于轻松扩展其功能。它是 [OpenSkills](https://github.com/Starttoaster/openskills) 生态系统的搜索引擎和工作流管理器(Workflow Manager)。
+
+虽然 `openskills` 处理文件的原始安装,但 **OneSkill** 提供:
+1. **智能搜索(Intelligent Search)**:使用自然语言或关键词找到适合该任务的工具。
+2. **工作流指南(Workflow Guidance)**:为智能体(Agent)安全获取新技能提供标准化流程。
+3. **环境映射(Environment Mapping)**:至关重要的一点是,它弥合了 `openskills`(标准结构)与 **Gemini CLI**(自定义结构)等使用者之间的鸿沟。
+
+## 🚀 快速开始
+
+您无需永久安装。只需使用 `npx` 运行即可。
+
+```bash
+# 搜索技能(例如,用于浏览网页)
+npx oneskill search "puppeteer browser"
+
+# 搜索按流行度排序的数据库工具
+npx oneskill search "database" --sort stars
+```
+
+## 🛠 工作流
+
+为您的智能体(Agent)添加新功能的标准生命周期:
+
+1. **搜索(Search)**:查找技能。
+ ```bash
+ npx oneskill search "github integration"
+ ```
+2. **安装(Install)**:使用标准的 `openskills` 安装程序。
+ ```bash
+ npx openskills install anthropics/skills
+ ```
+3. **映射(Map)(对 Gemini 至关重要)**:如果您正在使用 **Gemini CLI**,则必须将安装的技能映射到您的配置中。
+ ```bash
+ # 将安装的技能映射到 Gemini 的配置
+ npx oneskill map --target gemini
+ ```
+
+## 📖 命令参考
+
+### `search`
+在全局注册表中搜索技能。
+```bash
+npx oneskill search [options]
+
+# 选项:
+# --category 按类别过滤
+# --sort 按 'stars'、'created' 或 'updated' 排序
+# --limit 限制结果数量(默认值:10)
+```
+
+### `map`
+为特定的智能体(Agent)环境生成配置。
+```bash
+npx oneskill map --target
+
+# 目标:
+# gemini 生成/更新 Gemini CLI 配置
+```
+
+### `list`
+列出本地映射的技能(`openskills list` 的封装)。
+```bash
+npx oneskill list
+```
+
+---
+
+
+ 由 OneSkill 社区用 ❤️ 构建
+
diff --git a/.claude/skills/oneskill/README_CN.md b/.claude/skills/oneskill/README_CN.md
new file mode 100644
index 0000000..9901da5
--- /dev/null
+++ b/.claude/skills/oneskill/README_CN.md
@@ -0,0 +1,92 @@
+
+
+# OneSkill 元管理器 (Meta-Manager)
+
+**AI 智能体 (Agent) 技能的通用桥梁**
+帮助你发现、安装并将 OpenSkills 注册表中的能力映射到你的运行环境。
+
+[](https://www.npmjs.com/package/oneskill)
+[](LICENSE)
+
+[**🇺🇸 English**](README.md) | [**🇨🇳 中文指南**](README_CN.md)
+
+
+
+---
+
+## ⚡️ 什么是 OneSkill?
+
+**OneSkill** 是一个为 AI 智能体 (Agent) 设计的通用技能管理工具。它作为 [OpenSkills](https://github.com/Starttoaster/openskills) 生态系统的搜索引擎和工作流管理器 (Workflow Manager),帮助你发现、安装并将能力映射到你的运行环境中。
+
+虽然 `openskills` 负责文件的下载安装,但 **OneSkill** 提供了:
+1. **智能搜索**: 支持通过自然语言或关键词搜索注册表中的技能 (Skill)。
+2. **工作流引导**: 为智能体 (Agent) 提供了一套标准的扩展能力流程(搜索 -> 确认 -> 安装)。
+3. **环境映射 (Mapping)**: 解决了安装路径与运行环境不一致的问题。特别是对于 **Gemini CLI** 用户,OneSkill 能自动将下载的技能 (Skill) 映射到 Gemini 的配置文件中。
+
+## 🚀 快速开始
+
+无需全局安装,直接使用 `npx` 运行即可:
+
+```bash
+# 搜索技能 (例如:想要网页浏览能力)
+npx oneskill search "puppeteer browser"
+
+# 搜索数据库相关技能,并按星级排序
+npx oneskill search "database" --sort stars
+```
+
+## 🛠 使用流程
+
+为你的智能体 (Agent) 添加新能力的推荐步骤:
+
+1. **搜索 (Search)**: 查找你需要的技能 (Skill)。
+ ```bash
+ npx oneskill search "github integration"
+ ```
+
+2. **安装 (Install)**: 使用 `openskills` 标准命令进行下载。
+ ```bash
+ npx openskills install anthropics/skills
+ ```
+
+3. **映射 (Map)**: **(Gemini 用户必读)**
+ `openskills` 默认将文件下载到通用目录,Gemini CLI 无法直接读取。必须执行映射 (Mapping) 命令:
+ ```bash
+ # 自动识别已安装的技能 (Skill) 并配置到 Gemini
+ npx oneskill map --target gemini
+ ```
+ *如果你的技能 (Skill) 是全局安装的 (加了 --global),这里也需要加 --global。*
+
+## 📖 命令参考
+
+### `search` (搜索)
+在全局注册表中搜索技能 (Skill)。
+```bash
+npx oneskill search <查询词> [选项]
+
+# 选项:
+# --category 按分类筛选
+# --sort 排序方式: 'stars' (星级), 'created' (创建时间), 'updated' (更新时间)
+# --limit 限制返回数量 (默认: 10)
+```
+
+### `map` (映射)
+为特定环境生成配置。
+```bash
+npx oneskill map --target <环境>
+
+# 支持的目标:
+# gemini 更新 Gemini CLI 的配置与路径映射
+```
+
+### `list` (列表)
+查看本地已安装的技能 (Skill)。
+```bash
+npx oneskill list
+```
+
+---
+
+
+ Built with ❤️ by the OneSkill Community
+
diff --git a/.claude/skills/oneskill/SKILL.md b/.claude/skills/oneskill/SKILL.md
new file mode 100644
index 0000000..667135e
--- /dev/null
+++ b/.claude/skills/oneskill/SKILL.md
@@ -0,0 +1,59 @@
+---
+name: oneskill
+description: 发现技能 (Skill),迭代查询,并在任何环境中自动安装技能。
+---
+
+# OneSkill 元管理器 (Meta-Manager)
+
+使用此技能来发现新功能、优化搜索查询,并使用 OpenSkills 简化技能设置。这为扩展环境功能提供了一种统一的方式。
+
+## 何时使用
+
+- 当用户要求的某些功能你目前不具备时。
+- 当任务复杂、属于特定领域,或在尝试 2 次后仍被反复阻断时。
+- 当可能存在更好的技能(Skill)时(例如:网页浏览、GitHub 集成、数据库管理、云基础设施)。
+
+## 工作流 (Workflow)
+
+1. 搜索注册表:
+ - 运行:`npx oneskill search "" [options]`
+ - 支持的选项:`--category`、`--limit`、`--offset`、`--sort`。
+ - 示例:
+ - `npx oneskill search "browser" --sort stars`
+ - `npx oneskill search "" --category database --limit 5`
+2. 分析结果:
+ - 确定最佳匹配项,或优化查询并再次搜索。
+3. 与用户确认:
+ - 说明该技能的功能及其来源。
+4. 在获得明确批准后进行安装(使用 openskills):
+ - 运行:`npx openskills install `
+ - 示例:`npx openskills install anthropics/skills`
+5. 处理特定环境的设置:
+ - **Gemini CLI 用户:** `openskills` 不会自动配置 Gemini。安装后你**必须**运行映射命令:
+ - `npx oneskill map --target gemini`(如果是全局安装,请添加 `--global`)
+6. 应用新技能以完成原始请求。
+
+## OpenSkills 基础
+
+- `npx openskills install [options]` # 从 GitHub、本地路径或私有仓库安装
+- `npx openskills sync [-y] [-o ]` # 更新 AGENTS.md (或自定义输出)
+- `npx openskills list` # 显示已安装的技能
+- `npx openskills read ` # 加载技能(供智能体 (Agent) 使用)
+- `npx openskills update [name...]` # 更新已安装的技能(默认:全部)
+- `npx openskills manage` # 移除技能(交互式)
+- `npx openskills remove ` # 移除特定技能
+
+示例:
+- `npx openskills install anthropics/skills`
+- `npx openskills sync`
+
+默认设置:安装在项目本地(`./.claude/skills`,或者带 `--universal` 参数安装在 `./.agent/skills`)。使用 `--global` 安装在 `~/.claude/skills`。
+
+## 安全提示 (Safety Reminders)
+
+- 未经用户明确确认,请勿安装。
+- 除非用户同意覆盖现有目标,否则避免使用 `--force-map`。
+- 使用 openskills 进行安装/更新;OneSkill 仅为 Gemini 提供搜索和映射。
+- 对于 Gemini,请在安装后运行 `npx oneskill map --target gemini`。
+- 默认安装/映射是项目本地的,与 openskills 相同;全局安装请使用 `--global`。
+- 安装 OneSkill 本身时,建议使用 `--global`,以便在跨项目时可用。
diff --git a/.claude/skills/oneskill/package-lock.json b/.claude/skills/oneskill/package-lock.json
new file mode 100644
index 0000000..7bdde6b
--- /dev/null
+++ b/.claude/skills/oneskill/package-lock.json
@@ -0,0 +1,2026 @@
+{
+ "name": "oneskill",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "oneskill",
+ "version": "0.1.0",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "chalk": "^5.6.2",
+ "commander": "^12.1.0",
+ "openskills": "file:../openskills"
+ },
+ "bin": {
+ "oneskill": "dist/cli.js"
+ },
+ "devDependencies": {
+ "@types/node": "^24.9.1",
+ "tsup": "^8.5.0",
+ "typescript": "^5.9.3",
+ "vitest": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=20.6.0"
+ }
+ },
+ "../openskills": {
+ "version": "1.5.0",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@inquirer/prompts": "^7.9.0",
+ "chalk": "^5.6.2",
+ "commander": "^12.1.0",
+ "ora": "^9.0.0"
+ },
+ "bin": {
+ "openskills": "dist/cli.js"
+ },
+ "devDependencies": {
+ "@types/node": "^24.9.1",
+ "tsup": "^8.5.0",
+ "typescript": "^5.9.3",
+ "vitest": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=20.6.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
+ "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
+ "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
+ "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
+ "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
+ "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
+ "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
+ "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
+ "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
+ "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
+ "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
+ "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
+ "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
+ "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
+ "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
+ "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
+ "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
+ "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
+ "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
+ "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
+ "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
+ "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz",
+ "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz",
+ "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz",
+ "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz",
+ "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz",
+ "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz",
+ "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz",
+ "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz",
+ "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz",
+ "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz",
+ "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz",
+ "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz",
+ "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz",
+ "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz",
+ "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz",
+ "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz",
+ "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz",
+ "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz",
+ "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz",
+ "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz",
+ "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz",
+ "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz",
+ "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz",
+ "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz",
+ "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz",
+ "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/chai": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/deep-eql": "*",
+ "assertion-error": "^2.0.1"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.10.9",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz",
+ "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz",
+ "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.0.0",
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "4.0.18",
+ "@vitest/utils": "4.0.18",
+ "chai": "^6.2.1",
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz",
+ "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "4.0.18",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.21"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^6.0.0 || ^7.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz",
+ "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz",
+ "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "4.0.18",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz",
+ "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.0.18",
+ "magic-string": "^0.30.21",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz",
+ "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz",
+ "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.0.18",
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/bundle-require": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz",
+ "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "load-tsconfig": "^0.2.3"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "esbuild": ">=0.18"
+ }
+ },
+ "node_modules/cac": {
+ "version": "6.7.14",
+ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+ "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chai": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/confbox": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
+ "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/consola": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz",
+ "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.18.0 || >=16.10.0"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+ "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
+ "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.2",
+ "@esbuild/android-arm": "0.27.2",
+ "@esbuild/android-arm64": "0.27.2",
+ "@esbuild/android-x64": "0.27.2",
+ "@esbuild/darwin-arm64": "0.27.2",
+ "@esbuild/darwin-x64": "0.27.2",
+ "@esbuild/freebsd-arm64": "0.27.2",
+ "@esbuild/freebsd-x64": "0.27.2",
+ "@esbuild/linux-arm": "0.27.2",
+ "@esbuild/linux-arm64": "0.27.2",
+ "@esbuild/linux-ia32": "0.27.2",
+ "@esbuild/linux-loong64": "0.27.2",
+ "@esbuild/linux-mips64el": "0.27.2",
+ "@esbuild/linux-ppc64": "0.27.2",
+ "@esbuild/linux-riscv64": "0.27.2",
+ "@esbuild/linux-s390x": "0.27.2",
+ "@esbuild/linux-x64": "0.27.2",
+ "@esbuild/netbsd-arm64": "0.27.2",
+ "@esbuild/netbsd-x64": "0.27.2",
+ "@esbuild/openbsd-arm64": "0.27.2",
+ "@esbuild/openbsd-x64": "0.27.2",
+ "@esbuild/openharmony-arm64": "0.27.2",
+ "@esbuild/sunos-x64": "0.27.2",
+ "@esbuild/win32-arm64": "0.27.2",
+ "@esbuild/win32-ia32": "0.27.2",
+ "@esbuild/win32-x64": "0.27.2"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/expect-type": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
+ "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fix-dts-default-cjs-exports": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz",
+ "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "magic-string": "^0.30.17",
+ "mlly": "^1.7.4",
+ "rollup": "^4.34.8"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/joycon": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
+ "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/load-tsconfig": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz",
+ "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/mlly": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
+ "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "pathe": "^2.0.3",
+ "pkg-types": "^1.3.1",
+ "ufo": "^1.6.1"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/obug": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
+ "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/openskills": {
+ "resolved": "../openskills",
+ "link": true
+ },
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
+ "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/pkg-types": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
+ "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "confbox": "^0.1.8",
+ "mlly": "^1.7.4",
+ "pathe": "^2.0.1"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz",
+ "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^3.1.1"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "jiti": ">=1.21.0",
+ "postcss": ">=8.0.9",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ },
+ "postcss": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz",
+ "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.57.1",
+ "@rollup/rollup-android-arm64": "4.57.1",
+ "@rollup/rollup-darwin-arm64": "4.57.1",
+ "@rollup/rollup-darwin-x64": "4.57.1",
+ "@rollup/rollup-freebsd-arm64": "4.57.1",
+ "@rollup/rollup-freebsd-x64": "4.57.1",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.57.1",
+ "@rollup/rollup-linux-arm-musleabihf": "4.57.1",
+ "@rollup/rollup-linux-arm64-gnu": "4.57.1",
+ "@rollup/rollup-linux-arm64-musl": "4.57.1",
+ "@rollup/rollup-linux-loong64-gnu": "4.57.1",
+ "@rollup/rollup-linux-loong64-musl": "4.57.1",
+ "@rollup/rollup-linux-ppc64-gnu": "4.57.1",
+ "@rollup/rollup-linux-ppc64-musl": "4.57.1",
+ "@rollup/rollup-linux-riscv64-gnu": "4.57.1",
+ "@rollup/rollup-linux-riscv64-musl": "4.57.1",
+ "@rollup/rollup-linux-s390x-gnu": "4.57.1",
+ "@rollup/rollup-linux-x64-gnu": "4.57.1",
+ "@rollup/rollup-linux-x64-musl": "4.57.1",
+ "@rollup/rollup-openbsd-x64": "4.57.1",
+ "@rollup/rollup-openharmony-arm64": "4.57.1",
+ "@rollup/rollup-win32-arm64-msvc": "4.57.1",
+ "@rollup/rollup-win32-ia32-msvc": "4.57.1",
+ "@rollup/rollup-win32-x64-gnu": "4.57.1",
+ "@rollup/rollup-win32-x64-msvc": "4.57.1",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/source-map": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
+ "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/std-env": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
+ "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sucrase": {
+ "version": "3.35.1",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
+ "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "tinyglobby": "^0.2.11",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/sucrase/node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+ "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz",
+ "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "tree-kill": "cli.js"
+ }
+ },
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/tsup": {
+ "version": "8.5.1",
+ "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz",
+ "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bundle-require": "^5.1.0",
+ "cac": "^6.7.14",
+ "chokidar": "^4.0.3",
+ "consola": "^3.4.0",
+ "debug": "^4.4.0",
+ "esbuild": "^0.27.0",
+ "fix-dts-default-cjs-exports": "^1.0.0",
+ "joycon": "^3.1.1",
+ "picocolors": "^1.1.1",
+ "postcss-load-config": "^6.0.1",
+ "resolve-from": "^5.0.0",
+ "rollup": "^4.34.8",
+ "source-map": "^0.7.6",
+ "sucrase": "^3.35.0",
+ "tinyexec": "^0.3.2",
+ "tinyglobby": "^0.2.11",
+ "tree-kill": "^1.2.2"
+ },
+ "bin": {
+ "tsup": "dist/cli-default.js",
+ "tsup-node": "dist/cli-node.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@microsoft/api-extractor": "^7.36.0",
+ "@swc/core": "^1",
+ "postcss": "^8.4.12",
+ "typescript": ">=4.5.0"
+ },
+ "peerDependenciesMeta": {
+ "@microsoft/api-extractor": {
+ "optional": true
+ },
+ "@swc/core": {
+ "optional": true
+ },
+ "postcss": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/ufo": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
+ "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
+ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "esbuild": "^0.27.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz",
+ "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "4.0.18",
+ "@vitest/mocker": "4.0.18",
+ "@vitest/pretty-format": "4.0.18",
+ "@vitest/runner": "4.0.18",
+ "@vitest/snapshot": "4.0.18",
+ "@vitest/spy": "4.0.18",
+ "@vitest/utils": "4.0.18",
+ "es-module-lexer": "^1.7.0",
+ "expect-type": "^1.2.2",
+ "magic-string": "^0.30.21",
+ "obug": "^2.1.1",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3",
+ "std-env": "^3.10.0",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^1.0.2",
+ "tinyglobby": "^0.2.15",
+ "tinyrainbow": "^3.0.3",
+ "vite": "^6.0.0 || ^7.0.0",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@opentelemetry/api": "^1.9.0",
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
+ "@vitest/browser-playwright": "4.0.18",
+ "@vitest/browser-preview": "4.0.18",
+ "@vitest/browser-webdriverio": "4.0.18",
+ "@vitest/ui": "4.0.18",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser-playwright": {
+ "optional": true
+ },
+ "@vitest/browser-preview": {
+ "optional": true
+ },
+ "@vitest/browser-webdriverio": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/tinyexec": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
+ "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ }
+ }
+}
diff --git a/.claude/skills/oneskill/package.json b/.claude/skills/oneskill/package.json
new file mode 100644
index 0000000..7b32ec7
--- /dev/null
+++ b/.claude/skills/oneskill/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "oneskill",
+ "version": "0.1.0",
+ "description": "Meta-skill manager for AI coding agents",
+ "type": "module",
+ "main": "./dist/cli.js",
+ "bin": {
+ "oneskill": "dist/cli.js"
+ },
+ "files": [
+ "dist",
+ "README.md",
+ "SKILL.md"
+ ],
+ "scripts": {
+ "build": "tsup",
+ "dev": "tsup --watch",
+ "typecheck": "tsc --noEmit",
+ "test": "vitest run"
+ },
+ "keywords": [
+ "skills",
+ "ai",
+ "agents",
+ "meta-skill",
+ "codex",
+ "gemini",
+ "claude"
+ ],
+ "author": "OneSkill Contributors",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=20.6.0"
+ },
+ "dependencies": {
+ "chalk": "^5.6.2",
+ "commander": "^12.1.0"
+ },
+ "devDependencies": {
+ "@types/node": "^24.9.1",
+ "tsup": "^8.5.0",
+ "typescript": "^5.9.3",
+ "vitest": "^4.0.3"
+ }
+}
diff --git a/.claude/skills/oneskill/src/cli.ts b/.claude/skills/oneskill/src/cli.ts
new file mode 100644
index 0000000..a1fd8d9
--- /dev/null
+++ b/.claude/skills/oneskill/src/cli.ts
@@ -0,0 +1,82 @@
+#!/usr/bin/env node
+import { Command } from 'commander';
+import { runSearch } from './commands/search.js';
+import { runInfo } from './commands/info.js';
+import { runList } from './commands/list.js';
+import { runDoctor } from './commands/doctor.js';
+import { runSync } from './commands/sync.js';
+import { runMap } from './commands/map.js';
+import { getOneskillVersion } from './core/versions.js';
+
+const program = new Command();
+
+program
+ .name('oneskill')
+ .description('Meta-skill manager for AI coding agents')
+ .version(getOneskillVersion());
+
+program
+ .command('search ')
+ .description('Search the skill registry')
+ .option('--registry ', 'Registry base URL override')
+ .option('--category ', 'Filter by category slug')
+ .option('--limit ', 'Results per page (max 100)', (value) => Number.parseInt(value, 10))
+ .option('--offset ', 'Pagination offset', (value) => Number.parseInt(value, 10))
+ .option('--sort ', 'Sort by: votes, recent, stars')
+ .action(async (query: string, options: { registry?: string; category?: string; limit?: number; offset?: number; sort?: string }) => {
+ await runSearch(query, options);
+ });
+
+program
+ .command('info ')
+ .description('Fetch skill info from registry')
+ .option('--registry ', 'Registry base URL override')
+ .action(async (slug: string, options: { registry?: string }) => {
+ await runInfo(slug, options);
+ });
+
+program
+ .command('sync')
+ .description('Forward to openskills sync')
+ .option('-y, --yes', 'Skip interactive selection, sync all skills')
+ .option('-o, --output ', 'Output file path (default: AGENTS.md)')
+ .action(async (options: { yes?: boolean; output?: string }) => {
+ await runSync(options);
+ });
+
+program
+ .command('map')
+ .description('Map installed skills into Gemini directory')
+ .option('--target ', 'Target environment (gemini only)')
+ .option('--global', 'Map from global openskills install', false)
+ .option('--universal', 'Map from universal (.agent/skills)', false)
+ .option('--force-map', 'Overwrite target mapping if it exists', false)
+ .action(async (options: { target?: string; global?: boolean; universal?: boolean; forceMap?: boolean }) => {
+ await runMap({
+ target: options.target as 'gemini' | undefined,
+ global: options.global,
+ universal: options.universal,
+ forceMap: options.forceMap,
+ });
+ });
+
+program
+ .command('list')
+ .description('List managed skills')
+ .option('--root ', 'Override workspace root')
+ .action(async (options: { root?: string }) => {
+ await runList(options);
+ });
+
+program
+ .command('doctor')
+ .description('Diagnose OneSkill environment')
+ .option('--root ', 'Override workspace root')
+ .action(async (options: { root?: string }) => {
+ await runDoctor(options);
+ });
+
+program.parseAsync(process.argv).catch((error) => {
+ console.error(error instanceof Error ? error.message : String(error));
+ process.exitCode = 1;
+});
diff --git a/.claude/skills/oneskill/src/commands/doctor.ts b/.claude/skills/oneskill/src/commands/doctor.ts
new file mode 100644
index 0000000..88880d3
--- /dev/null
+++ b/.claude/skills/oneskill/src/commands/doctor.ts
@@ -0,0 +1,19 @@
+import { detectRoot } from '../core/root.js';
+import { printJson } from '../utils/json.js';
+
+export interface DoctorCommandOptions {
+ root?: string;
+}
+
+export async function runDoctor(options: DoctorCommandOptions): Promise {
+ const rootInfo = detectRoot(options.root || process.cwd());
+ const root = rootInfo.root;
+ const paths = {
+ agent: `${root}/.agent/skills`,
+ claude: `${root}/.claude/skills`,
+ gemini: `${root}/.gemini/skills`,
+ codex: `${root}/.codex/skills`,
+ oneskillLogs: `${root}/.oneskill/logs`,
+ };
+ printJson({ schemaVersion: '1', root: rootInfo.root, reason: rootInfo.reason, paths });
+}
diff --git a/.claude/skills/oneskill/src/commands/info.ts b/.claude/skills/oneskill/src/commands/info.ts
new file mode 100644
index 0000000..a7081c3
--- /dev/null
+++ b/.claude/skills/oneskill/src/commands/info.ts
@@ -0,0 +1,11 @@
+import { fetchRegistryInfo } from '../core/registry.js';
+import { printJson } from '../utils/json.js';
+
+export interface InfoCommandOptions {
+ registry?: string;
+}
+
+export async function runInfo(slug: string, options: InfoCommandOptions): Promise {
+ const result = await fetchRegistryInfo(slug, options.registry);
+ printJson({ schemaVersion: '1', item: result.item });
+}
diff --git a/.claude/skills/oneskill/src/commands/list.ts b/.claude/skills/oneskill/src/commands/list.ts
new file mode 100644
index 0000000..6455186
--- /dev/null
+++ b/.claude/skills/oneskill/src/commands/list.ts
@@ -0,0 +1,16 @@
+import { spawnSync } from 'child_process';
+import { resolveOpenskillsCli } from '../core/openskills.js';
+
+export interface ListCommandOptions {}
+
+export async function runList(_options: ListCommandOptions): Promise {
+ const cliPath = resolveOpenskillsCli();
+ const args = [cliPath, 'list'];
+ const result = spawnSync(process.execPath, args, {
+ cwd: process.cwd(),
+ stdio: 'inherit',
+ });
+ if (result.status !== 0) {
+ throw new Error('openskills list failed');
+ }
+}
diff --git a/.claude/skills/oneskill/src/commands/map.ts b/.claude/skills/oneskill/src/commands/map.ts
new file mode 100644
index 0000000..5a5d702
--- /dev/null
+++ b/.claude/skills/oneskill/src/commands/map.ts
@@ -0,0 +1,23 @@
+import type { TargetEnvironment } from '../core/types.js';
+import { mapInstalledSkills } from '../core/map.js';
+import { printJson } from '../utils/json.js';
+
+export interface MapCommandOptions {
+ target?: TargetEnvironment;
+ global?: boolean;
+ universal?: boolean;
+ forceMap?: boolean;
+}
+
+export async function runMap(options: MapCommandOptions): Promise {
+ if (!options.target) {
+ throw new Error('map requires --target gemini');
+ }
+ const result = mapInstalledSkills({
+ target: options.target,
+ global: options.global,
+ universal: options.universal,
+ forceMap: options.forceMap,
+ });
+ printJson({ schemaVersion: '1', ...result });
+}
diff --git a/.claude/skills/oneskill/src/commands/search.ts b/.claude/skills/oneskill/src/commands/search.ts
new file mode 100644
index 0000000..62be659
--- /dev/null
+++ b/.claude/skills/oneskill/src/commands/search.ts
@@ -0,0 +1,32 @@
+import { searchRegistry } from '../core/registry.js';
+import { printJson } from '../utils/json.js';
+
+export interface SearchCommandOptions {
+ registry?: string;
+ category?: string;
+ limit?: number;
+ offset?: number;
+ sort?: string;
+}
+
+export async function runSearch(query: string, options: SearchCommandOptions): Promise {
+ const result = await searchRegistry(
+ {
+ q: query,
+ category: options.category,
+ limit: options.limit,
+ offset: options.offset,
+ sort: options.sort,
+ },
+ options.registry
+ );
+ const raw = result.raw as { registry?: unknown; version?: unknown; pagination?: unknown } | undefined;
+ printJson({
+ schemaVersion: '1',
+ query,
+ registry: raw?.registry,
+ version: raw?.version,
+ pagination: raw?.pagination,
+ items: result.items,
+ });
+}
diff --git a/.claude/skills/oneskill/src/commands/sync.ts b/.claude/skills/oneskill/src/commands/sync.ts
new file mode 100644
index 0000000..fc04ae8
--- /dev/null
+++ b/.claude/skills/oneskill/src/commands/sync.ts
@@ -0,0 +1,25 @@
+import { spawnSync } from 'child_process';
+import { resolveOpenskillsCli } from '../core/openskills.js';
+
+export interface SyncCommandOptions {
+ yes?: boolean;
+ output?: string;
+}
+
+export async function runSync(options: SyncCommandOptions): Promise {
+ const cliPath = resolveOpenskillsCli();
+ const args = [cliPath, 'sync'];
+ if (options.yes) {
+ args.push('--yes');
+ }
+ if (options.output) {
+ args.push('--output', options.output);
+ }
+ const result = spawnSync(process.execPath, args, {
+ cwd: process.cwd(),
+ stdio: 'inherit',
+ });
+ if (result.status !== 0) {
+ throw new Error('openskills sync failed');
+ }
+}
diff --git a/.claude/skills/oneskill/src/core/fs.ts b/.claude/skills/oneskill/src/core/fs.ts
new file mode 100644
index 0000000..77ec809
--- /dev/null
+++ b/.claude/skills/oneskill/src/core/fs.ts
@@ -0,0 +1,24 @@
+import { mkdirSync, readFileSync, writeFileSync, existsSync } from 'fs';
+import { dirname, resolve, sep } from 'path';
+
+export function ensureDir(path: string): void {
+ mkdirSync(path, { recursive: true });
+}
+
+export function readJsonFile(path: string): T | null {
+ if (!existsSync(path)) return null;
+ const content = readFileSync(path, 'utf-8');
+ return JSON.parse(content) as T;
+}
+
+export function writeJsonFile(path: string, data: unknown): void {
+ ensureDir(dirname(path));
+ writeFileSync(path, JSON.stringify(data, null, 2) + '\n', 'utf-8');
+}
+
+export function isPathInside(targetPath: string, baseDir: string): boolean {
+ const resolvedTarget = resolve(targetPath);
+ const resolvedBase = resolve(baseDir);
+ const baseWithSep = resolvedBase.endsWith(sep) ? resolvedBase : resolvedBase + sep;
+ return resolvedTarget === resolvedBase || resolvedTarget.startsWith(baseWithSep);
+}
diff --git a/.claude/skills/oneskill/src/core/lock.ts b/.claude/skills/oneskill/src/core/lock.ts
new file mode 100644
index 0000000..ac33c82
--- /dev/null
+++ b/.claude/skills/oneskill/src/core/lock.ts
@@ -0,0 +1,23 @@
+import { join } from 'path';
+import { readJsonFile, writeJsonFile, ensureDir } from './fs.js';
+import type { LockFile, LockedSkill } from './types.js';
+
+const LOCK_FILE_NAME = 'lock.json';
+
+export function getLockPath(root: string): string {
+ return join(root, '.oneskill', LOCK_FILE_NAME);
+}
+
+export function readLock(root: string): LockFile | null {
+ return readJsonFile(getLockPath(root));
+}
+
+export function writeLock(root: string, lock: LockFile): void {
+ ensureDir(join(root, '.oneskill'));
+ writeJsonFile(getLockPath(root), lock);
+}
+
+export function upsertLockSkill(lock: LockFile, skill: LockedSkill): void {
+ lock.skills[skill.id] = skill;
+ lock.updatedAt = new Date().toISOString();
+}
diff --git a/.claude/skills/oneskill/src/core/log.ts b/.claude/skills/oneskill/src/core/log.ts
new file mode 100644
index 0000000..25a78fa
--- /dev/null
+++ b/.claude/skills/oneskill/src/core/log.ts
@@ -0,0 +1,11 @@
+import { appendFileSync } from 'fs';
+import { join } from 'path';
+import { ensureDir } from './fs.js';
+
+export function appendLog(root: string, event: Record): void {
+ const logDir = join(root, '.oneskill', 'logs');
+ ensureDir(logDir);
+ const date = new Date().toISOString().slice(0, 10);
+ const logPath = join(logDir, `${date}.jsonl`);
+ appendFileSync(logPath, JSON.stringify(event) + '\n', 'utf-8');
+}
diff --git a/.claude/skills/oneskill/src/core/manifest.ts b/.claude/skills/oneskill/src/core/manifest.ts
new file mode 100644
index 0000000..5cbb2ad
--- /dev/null
+++ b/.claude/skills/oneskill/src/core/manifest.ts
@@ -0,0 +1,33 @@
+import { lstatSync, readdirSync } from 'fs';
+import { join } from 'path';
+import type { ManifestSummary } from './types.js';
+
+export function buildManifestSummary(root: string): ManifestSummary {
+ let files = 0;
+ let bytes = 0;
+ let hasSymlinks = false;
+
+ const walk = (dir: string): void => {
+ const entries = readdirSync(dir, { withFileTypes: true });
+ for (const entry of entries) {
+ const fullPath = join(dir, entry.name);
+ const stat = lstatSync(fullPath);
+ if (stat.isSymbolicLink()) {
+ hasSymlinks = true;
+ files += 1;
+ continue;
+ }
+ if (stat.isDirectory()) {
+ walk(fullPath);
+ continue;
+ }
+ if (stat.isFile()) {
+ files += 1;
+ bytes += stat.size;
+ }
+ }
+ };
+
+ walk(root);
+ return { files, bytes, hasSymlinks };
+}
diff --git a/.claude/skills/oneskill/src/core/map.ts b/.claude/skills/oneskill/src/core/map.ts
new file mode 100644
index 0000000..e688578
--- /dev/null
+++ b/.claude/skills/oneskill/src/core/map.ts
@@ -0,0 +1,49 @@
+import { existsSync, readdirSync } from 'fs';
+import { basename, join } from 'path';
+import { homedir } from 'os';
+import { mapSkill } from './mapping.js';
+import { detectRoot } from './root.js';
+import type { TargetEnvironment } from './types.js';
+
+export interface MapOptions {
+ target: TargetEnvironment;
+ global?: boolean;
+ universal?: boolean;
+ forceMap?: boolean;
+}
+
+function getSourceBase(root: string, options: MapOptions): string {
+ const baseRoot = options.global ? homedir() : root;
+ const folder = options.universal ? '.agent/skills' : '.claude/skills';
+ return join(baseRoot, folder);
+}
+
+function getTargetRoot(root: string, options: MapOptions): string {
+ return options.global ? homedir() : root;
+}
+
+export function mapInstalledSkills(options: MapOptions): { mapped: number; sourceBase: string; targetRoot: string } {
+ if (options.target !== 'gemini') {
+ throw new Error('map currently supports only --target gemini');
+ }
+ const rootInfo = detectRoot(process.cwd());
+ const sourceBase = getSourceBase(rootInfo.root, options);
+ const targetRoot = getTargetRoot(rootInfo.root, options);
+
+ if (!existsSync(sourceBase)) {
+ return { mapped: 0, sourceBase, targetRoot };
+ }
+
+ const entries = readdirSync(sourceBase, { withFileTypes: true })
+ .filter((entry) => entry.isDirectory())
+ .map((entry) => join(sourceBase, entry.name));
+
+ let mapped = 0;
+ for (const dir of entries) {
+ const name = basename(dir);
+ mapSkill(targetRoot, 'gemini', name, dir, { forceMap: options.forceMap });
+ mapped += 1;
+ }
+
+ return { mapped, sourceBase, targetRoot };
+}
diff --git a/.claude/skills/oneskill/src/core/mapping.ts b/.claude/skills/oneskill/src/core/mapping.ts
new file mode 100644
index 0000000..ef61d68
--- /dev/null
+++ b/.claude/skills/oneskill/src/core/mapping.ts
@@ -0,0 +1,85 @@
+import { existsSync, lstatSync, readlinkSync, rmSync, symlinkSync, cpSync, writeFileSync } from 'fs';
+import { join, resolve } from 'path';
+import { ensureDir, isPathInside } from './fs.js';
+import type { SkillMappingRecord, TargetEnvironment } from './types.js';
+
+const TARGET_DIRS: Record = {
+ codex: '.codex/skills',
+ gemini: '.gemini/skills',
+ claude: '.claude/skills',
+ agent: '.agent/skills',
+};
+
+export interface MapOptions {
+ forceMap?: boolean;
+ codexHome?: string;
+}
+
+function getTargetBase(root: string, target: TargetEnvironment, options?: MapOptions): string {
+ if (target === 'codex' && options?.codexHome) {
+ return resolve(options.codexHome, 'skills');
+ }
+ return join(root, TARGET_DIRS[target]);
+}
+
+function readLinkTarget(path: string): string | null {
+ try {
+ return readlinkSync(path);
+ } catch {
+ return null;
+ }
+}
+
+export function mapSkill(
+ root: string,
+ target: TargetEnvironment,
+ skillName: string,
+ storePath: string,
+ options?: MapOptions
+): SkillMappingRecord {
+ const baseDir = getTargetBase(root, target, options);
+ const targetPath = join(baseDir, skillName);
+
+ if (target !== 'codex' && !isPathInside(targetPath, root)) {
+ throw new Error(`Target path escapes root: ${targetPath}`);
+ }
+
+ ensureDir(baseDir);
+
+ if (existsSync(targetPath)) {
+ const stat = lstatSync(targetPath);
+ if (stat.isSymbolicLink()) {
+ const linkTarget = readLinkTarget(targetPath);
+ if (linkTarget && resolve(linkTarget) === resolve(storePath)) {
+ return { target, path: targetPath, mode: process.platform === 'win32' ? 'junction' : 'symlink', updatedAt: new Date().toISOString() };
+ }
+ if (!options?.forceMap) {
+ throw new Error(`Target already exists (symlink): ${targetPath}`);
+ }
+ } else {
+ if (!options?.forceMap) {
+ throw new Error(`Target already exists: ${targetPath}`);
+ }
+ }
+ rmSync(targetPath, { recursive: true, force: true });
+ }
+
+ const linkType = process.platform === 'win32' ? 'junction' : 'dir';
+ try {
+ symlinkSync(storePath, targetPath, linkType);
+ return { target, path: targetPath, mode: process.platform === 'win32' ? 'junction' : 'symlink', updatedAt: new Date().toISOString() };
+ } catch (error) {
+ if (!options?.forceMap) {
+ // Fall through to copy on Windows permission errors or other failures.
+ }
+ }
+
+ cpSync(storePath, targetPath, { recursive: true, dereference: false });
+ writeFileSync(
+ join(targetPath, '.oneskill-meta.json'),
+ JSON.stringify({ source: storePath, mappedAt: new Date().toISOString(), mode: 'copy' }, null, 2) + '\n',
+ 'utf-8'
+ );
+
+ return { target, path: targetPath, mode: 'copy', updatedAt: new Date().toISOString() };
+}
diff --git a/.claude/skills/oneskill/src/core/openskills.ts b/.claude/skills/oneskill/src/core/openskills.ts
new file mode 100644
index 0000000..af9a073
--- /dev/null
+++ b/.claude/skills/oneskill/src/core/openskills.ts
@@ -0,0 +1,42 @@
+import { readFileSync, existsSync } from 'fs';
+import { dirname, resolve } from 'path';
+import { createRequire } from 'module';
+import { spawnSync } from 'child_process';
+
+const require = createRequire(import.meta.url);
+
+export function resolveOpenskillsCli(): string {
+ const pkgPath = require.resolve('openskills/package.json');
+ const pkgDir = dirname(pkgPath);
+ const content = readFileSync(pkgPath, 'utf-8');
+ const parsed = JSON.parse(content) as { bin?: Record | string; main?: string };
+ const bin = parsed.bin;
+ let candidate: string | null = null;
+ if (typeof bin === 'string') {
+ candidate = resolve(pkgDir, bin);
+ } else if (bin && typeof bin === 'object') {
+ const first = Object.values(bin)[0];
+ if (first) candidate = resolve(pkgDir, first);
+ } else if (parsed.main) {
+ candidate = resolve(pkgDir, parsed.main);
+ }
+
+ if (candidate && existsSync(candidate)) {
+ return candidate;
+ }
+
+ // Build openskills locally if the CLI entry is missing.
+ const result = spawnSync('npm', ['run', 'build'], {
+ cwd: pkgDir,
+ stdio: 'inherit',
+ });
+ if (result.status !== 0) {
+ throw new Error('Failed to build openskills');
+ }
+
+ if (candidate && existsSync(candidate)) {
+ return candidate;
+ }
+
+ throw new Error('Unable to resolve openskills CLI entry');
+}
diff --git a/.claude/skills/oneskill/src/core/registry.ts b/.claude/skills/oneskill/src/core/registry.ts
new file mode 100644
index 0000000..b7cacb3
--- /dev/null
+++ b/.claude/skills/oneskill/src/core/registry.ts
@@ -0,0 +1,110 @@
+import type { RegistryInfoResponse, RegistrySearchResponse, SkillListItem } from './types.js';
+
+const DEFAULT_REGISTRY_URL = 'https://skillsdirectory.com/api/registry';
+
+export interface SearchParams {
+ q?: string;
+ category?: string;
+ limit?: number;
+ offset?: number;
+ sort?: string;
+}
+
+function getRegistryBase(override?: string): string {
+ return (override || process.env.ONESKILL_REGISTRY_URL || DEFAULT_REGISTRY_URL).replace(/\/$/, '');
+}
+
+async function fetchJson(url: string): Promise {
+ const res = await fetch(url, { headers: { 'accept': 'application/json' } });
+ if (!res.ok) {
+ const text = await res.text();
+ throw new Error(`Registry request failed (${res.status}): ${text.slice(0, 200)}`);
+ }
+ return res.json();
+}
+
+function normalizeSkill(item: Record): SkillListItem {
+ const slug = String(item.slug || item.id || item.name || '');
+ const name = String(item.name || item.title || slug || '');
+ const description = String(item.description || item.summary || '');
+ const repository = String(item.repository || item.repo || item.url || '');
+ const verified = typeof item.verified === 'boolean' ? item.verified : undefined;
+ const stars =
+ typeof item.stars === 'number'
+ ? item.stars
+ : typeof (item.github as { stars?: unknown } | undefined)?.stars === 'number'
+ ? (item.github as { stars: number }).stars
+ : undefined;
+ const tags = Array.isArray(item.tags) ? item.tags.map(String) : undefined;
+ const authorObj = item.author as { name?: unknown; url?: unknown } | undefined;
+ const authorName = typeof item.author === 'string' ? String(item.author) : authorObj?.name ? String(authorObj.name) : undefined;
+ const author = authorName
+ ? { name: authorName, url: authorObj?.url ? String(authorObj.url) : undefined }
+ : undefined;
+
+ const signals = {
+ lastUpdated: item.lastUpdated ? String(item.lastUpdated) : undefined,
+ license:
+ item.license ? String(item.license)
+ : typeof (item.github as { license?: unknown } | undefined)?.license === 'string'
+ ? String((item.github as { license: string }).license)
+ : undefined,
+ riskHints: Array.isArray(item.riskHints) ? item.riskHints.map(String) : undefined,
+ };
+
+ return {
+ schemaVersion: '1',
+ slug,
+ name,
+ description,
+ repository,
+ verified,
+ stars,
+ tags,
+ author,
+ signals,
+ };
+}
+
+function normalizeSearchPayload(payload: unknown): SkillListItem[] {
+ if (!payload || typeof payload !== 'object') return [];
+ const data = payload as Record;
+ const candidates =
+ (Array.isArray(data.skills) ? data.skills : null) ||
+ (Array.isArray(data.items) ? data.items : null) ||
+ (Array.isArray(data.results) ? data.results : null) ||
+ (Array.isArray(data.data) ? data.data : null) ||
+ (Array.isArray(payload) ? (payload as unknown[]) : null);
+
+ if (!candidates) return [];
+ return candidates
+ .filter((item) => item && typeof item === 'object')
+ .map((item) => normalizeSkill(item as Record));
+}
+
+export async function searchRegistry(params: SearchParams, overrideUrl?: string): Promise {
+ const base = getRegistryBase(overrideUrl);
+ const url = new URL(base);
+ if (params.q) url.searchParams.set('q', params.q);
+ if (params.category) url.searchParams.set('category', params.category);
+ if (typeof params.limit === 'number') url.searchParams.set('limit', String(params.limit));
+ if (typeof params.offset === 'number') url.searchParams.set('offset', String(params.offset));
+ if (params.sort) url.searchParams.set('sort', params.sort);
+ const raw = await fetchJson(url.toString());
+ const items = normalizeSearchPayload(raw);
+ return { items, raw };
+}
+
+export async function fetchRegistryInfo(slug: string, overrideUrl?: string): Promise {
+ const base = getRegistryBase(overrideUrl);
+ const url = `${base}/${encodeURIComponent(slug)}`;
+ const raw = await fetchJson(url);
+ if (raw && typeof raw === 'object') {
+ const record = raw as Record;
+ const candidate = (record.skill as Record | undefined) || (record.item as Record | undefined) || record;
+ if (candidate && typeof candidate === 'object') {
+ return { item: normalizeSkill(candidate as Record), raw };
+ }
+ }
+ throw new Error('Registry info failed');
+}
diff --git a/.claude/skills/oneskill/src/core/root.ts b/.claude/skills/oneskill/src/core/root.ts
new file mode 100644
index 0000000..7ecb43e
--- /dev/null
+++ b/.claude/skills/oneskill/src/core/root.ts
@@ -0,0 +1,38 @@
+import { existsSync, readFileSync } from 'fs';
+import { dirname, join, resolve } from 'path';
+import type { RootDetection } from './types.js';
+
+const MARKER_DIRS = ['.git', '.agent', '.claude', '.gemini', '.codex'];
+
+function hasOneskillConfig(pkgPath: string): boolean {
+ try {
+ const content = readFileSync(pkgPath, 'utf-8');
+ const parsed = JSON.parse(content) as { oneskill?: unknown };
+ return Boolean(parsed.oneskill);
+ } catch {
+ return false;
+ }
+}
+
+function hasMarkers(dir: string): string | null {
+ for (const marker of MARKER_DIRS) {
+ if (existsSync(join(dir, marker))) return marker;
+ }
+ if (existsSync(join(dir, 'AGENTS.md'))) return 'AGENTS.md';
+ const pkgPath = join(dir, 'package.json');
+ if (existsSync(pkgPath) && hasOneskillConfig(pkgPath)) return 'package.json:oneskill';
+ return null;
+}
+
+export function detectRoot(startDir: string): RootDetection {
+ let current = resolve(startDir);
+ while (true) {
+ const reason = hasMarkers(current);
+ if (reason) return { root: current, reason };
+ const parent = dirname(current);
+ if (parent === current) {
+ return { root: resolve(startDir), reason: 'fallback:cwd' };
+ }
+ current = parent;
+ }
+}
diff --git a/.claude/skills/oneskill/src/core/types.ts b/.claude/skills/oneskill/src/core/types.ts
new file mode 100644
index 0000000..fc70070
--- /dev/null
+++ b/.claude/skills/oneskill/src/core/types.ts
@@ -0,0 +1,81 @@
+export type TargetEnvironment = 'codex' | 'gemini' | 'claude' | 'agent';
+
+export interface SkillListItem {
+ schemaVersion: '1';
+ slug: string;
+ name: string;
+ description: string;
+ repository: string;
+ verified?: boolean;
+ stars?: number;
+ tags?: string[];
+ author?: {
+ name: string;
+ url?: string;
+ };
+ signals?: {
+ lastUpdated?: string;
+ license?: string;
+ riskHints?: string[];
+ };
+}
+
+export interface RegistryInfoResponse {
+ item: SkillListItem;
+ raw: unknown;
+}
+
+export interface RegistrySearchResponse {
+ items: SkillListItem[];
+ raw: unknown;
+}
+
+export interface ManifestSummary {
+ files: number;
+ bytes: number;
+ hasSymlinks: boolean;
+}
+
+export interface SkillSource {
+ input: string;
+ type: 'slug' | 'repository' | 'local';
+ repository?: string;
+ ref?: string;
+}
+
+export interface SkillMappingRecord {
+ target: TargetEnvironment;
+ path: string;
+ mode: 'symlink' | 'junction' | 'copy';
+ updatedAt: string;
+}
+
+export interface LockedSkill {
+ id: string;
+ name: string;
+ source: SkillSource;
+ installedAt: string;
+ storePath: string;
+ manifest: ManifestSummary;
+ mappings: SkillMappingRecord[];
+}
+
+export interface LockFile {
+ schemaVersion: '1';
+ root: string;
+ oneskillVersion: string;
+ openskillsVersion: string;
+ updatedAt: string;
+ skills: Record;
+}
+
+export interface RootDetection {
+ root: string;
+ reason: string;
+}
+
+export interface InstallResult {
+ root: string;
+ skills: LockedSkill[];
+ target?: TargetEnvironment;
+}
diff --git a/.claude/skills/oneskill/src/core/versions.ts b/.claude/skills/oneskill/src/core/versions.ts
new file mode 100644
index 0000000..65712c7
--- /dev/null
+++ b/.claude/skills/oneskill/src/core/versions.ts
@@ -0,0 +1,29 @@
+import { readFileSync } from 'fs';
+import { dirname, join } from 'path';
+import { fileURLToPath } from 'url';
+import { createRequire } from 'module';
+
+const require = createRequire(import.meta.url);
+
+export function getOneskillVersion(): string {
+ const __dirname = dirname(fileURLToPath(import.meta.url));
+ const pkgPath = join(__dirname, '../../package.json');
+ try {
+ const content = readFileSync(pkgPath, 'utf-8');
+ const parsed = JSON.parse(content) as { version?: string };
+ return parsed.version || '0.0.0';
+ } catch {
+ return '0.0.0';
+ }
+}
+
+export function getOpenskillsVersion(): string {
+ try {
+ const pkgPath = require.resolve('openskills/package.json');
+ const content = readFileSync(pkgPath, 'utf-8');
+ const parsed = JSON.parse(content) as { version?: string };
+ return parsed.version || '0.0.0';
+ } catch {
+ return '0.0.0';
+ }
+}
diff --git a/.claude/skills/oneskill/src/utils/json.ts b/.claude/skills/oneskill/src/utils/json.ts
new file mode 100644
index 0000000..4e42179
--- /dev/null
+++ b/.claude/skills/oneskill/src/utils/json.ts
@@ -0,0 +1,3 @@
+export function printJson(data: unknown): void {
+ process.stdout.write(JSON.stringify(data, null, 2) + '\n');
+}
diff --git a/.claude/skills/oneskill/tsconfig.json b/.claude/skills/oneskill/tsconfig.json
new file mode 100644
index 0000000..ffd80c7
--- /dev/null
+++ b/.claude/skills/oneskill/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ES2022",
+ "moduleResolution": "Bundler",
+ "outDir": "dist",
+ "rootDir": "src",
+ "strict": true,
+ "declaration": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true
+ },
+ "include": ["src"]
+}
diff --git a/.claude/skills/oneskill/tsup.config.ts b/.claude/skills/oneskill/tsup.config.ts
new file mode 100644
index 0000000..eb81b2e
--- /dev/null
+++ b/.claude/skills/oneskill/tsup.config.ts
@@ -0,0 +1,10 @@
+import { defineConfig } from 'tsup';
+
+export default defineConfig({
+ entry: ['src/cli.ts'],
+ format: ['esm'],
+ dts: false,
+ sourcemap: true,
+ clean: true,
+ target: 'node20'
+});
diff --git a/.gitignore b/.gitignore
index 31ba5f1..162a0bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,5 @@ examples/sessions/*.tmp
# Local drafts
marketing/
+!.claude/
+!.claude-plugin/
diff --git a/README.md b/README.md
index 272b54d..a28cfbc 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-**语言:** [English](README.md) | 繁體中文
+**语言:** [English](README.md) | **简体中文** | [繁體中文](docs/zh-TW/README.md)
# Everything Claude Code
@@ -9,54 +9,101 @@


-
- English |
- 简体中文
-
+---
-**由 Anthropic 黑客松获胜者整理的 Claude Code 配置完整合集。**
+
-这是在 10 个月以上高强度日常开发真实产品的过程中,不断演进出的生产级智能体(Agents)、技能(Skills)、钩子(Hooks)、命令(Commands)、规则(Rules)以及 MCP 配置。
+**🌐 Language / 语言 / 語言**
+
+[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md)
+
+
---
-## 指南(The Guides)
+**来自 Anthropic 黑客松获胜者的 Claude Code 配置全集。**
-本仓库仅包含原始代码。以下指南将解释所有细节。
+包含生产级的智能体(Agents)、技能(Skills)、钩子(Hooks)、命令(Commands)、规则(Rules)以及 MCP 配置。这些配置源自 10 个多月在构建真实产品过程中的高强度日常使用与演进。
+
+---
+
+## 指南文档
+
+本仓库仅包含原始代码。以下指南将详细解释一切:
-
-
+
+
-
-
+
+
简明指南 (Shorthand Guide) 安装、基础、哲学。请先阅读此篇。
-深度指南 (Longform Guide) Token 优化、内存持久化、评测(Evals)、并行化。
+深度指南 (Longform Guide) Token 优化、记忆持久化、评测(Evals)、并行化。
-| 主题 | 你将学到什么 |
+| 主题 | 你将学到 |
|-------|-------------------|
| Token 优化 | 模型选择、系统提示词精简、后台进程 |
-| 内存持久化 | 跨会话(Session)自动保存/加载上下文的钩子(Hooks) |
-| 持续学习 | 从会话中自动提取模式并转化为可复用的技能(Skills) |
-| 验证循环 | 检查点(Checkpoint)与持续评测(Evals)、评分器类型、pass@k 指标 |
+| 记忆持久化 | 跨会话自动保存/加载上下文的钩子(Hooks) |
+| 持续学习 | 从会话中自动提取模式并转化为可重用的技能(Skills) |
+| 验证循环 | 检查点(Checkpoint)vs 持续评测、评分器类型、pass@k 指标 |
| 并行化 | Git worktrees、级联方法、何时扩展实例 |
| 子智能体编排 | 上下文问题、迭代检索模式 |
---
-## 跨平台支持
+## 🚀 快速开始
-该插件现已全面支持 **Windows、macOS 和 Linux**。所有钩子(Hooks)和脚本都已使用 Node.js 重写,以实现最大的兼容性。
+不到 2 分钟即可完成配置:
+
+### 第一步:安装插件
+
+```bash
+# 添加市场
+/plugin marketplace add affaan-m/everything-claude-code
+
+# 安装插件
+/plugin install everything-claude-code@everything-claude-code
+```
+
+### 第二步:安装规则(必选)
+
+> ⚠️ **重要提示:** Claude Code 插件无法自动分发 `rules`。请手动安装:
+
+```bash
+# 首先克隆仓库
+git clone https://github.com/affaan-m/everything-claude-code.git
+
+# 复制规则(适用于所有项目)
+cp -r everything-claude-code/rules/* ~/.claude/rules/
+```
+
+### 第三步:开始使用
+
+```bash
+# 尝试一个命令
+/plan "Add user authentication"
+
+# 查看可用命令
+/plugin list everything-claude-code@everything-claude-code
+```
+
+✨ **大功告成!** 你现在可以使用 15+ 个智能体、30+ 个技能和 20+ 个命令了。
+
+---
+
+## 🌐 跨平台支持
+
+该插件现已全面支持 **Windows、macOS 和 Linux**。所有钩子和脚本均已使用 Node.js 重写,以确保最大兼容性。
### 包管理器检测
@@ -65,9 +112,9 @@
1. **环境变量**:`CLAUDE_PACKAGE_MANAGER`
2. **项目配置**:`.claude/package-manager.json`
3. **package.json**:`packageManager` 字段
-4. **锁文件**:根据 package-lock.json, yarn.lock, pnpm-lock.yaml 或 bun.lockb 检测
+4. **锁文件**:根据 package-lock.json, yarn.lock, pnpm-lock.yaml, 或 bun.lockb 检测
5. **全局配置**:`~/.claude/package-manager.json`
-6. **备选项**:第一个可用的包管理器
+6. **兜底方案**:第一个可用的包管理器
设置你偏好的包管理器:
@@ -89,65 +136,65 @@ node scripts/setup-package-manager.js --detect
---
-## 包含内容
+## 📦 内容清单
-本仓库是一个 **Claude Code 插件** —— 你可以直接安装它,或者手动复制组件。
+本仓库是一个 **Claude Code 插件** —— 你可以直接安装,也可以手动复制组件。
```
everything-claude-code/
-|-- .claude-plugin/ # 插件与市场清单
-| |-- plugin.json # 插件元数据与组件路径
+|-- .claude-plugin/ # 插件和市场清单文件
+| |-- plugin.json # 插件元数据和组件路径
| |-- marketplace.json # 用于 /plugin marketplace add 的市场目录
|
-|-- agents/ # 用于任务委派的专用子智能体(Subagents)
+|-- agents/ # 用于任务委派的专业化子智能体(Subagents)
| |-- planner.md # 功能实现规划
| |-- architect.md # 系统设计决策
| |-- tdd-guide.md # 测试驱动开发(TDD)
-| |-- code-reviewer.md # 代码质量与安全评审
+| |-- code-reviewer.md # 质量与安全审查
| |-- security-reviewer.md # 漏洞分析
-| |-- build-error-resolver.md # 构建错误解决
-| |-- e2e-runner.md # Playwright 端到端测试
+| |-- build-error-resolver.md # 构建错误修复
+| |-- e2e-runner.md # Playwright 端到端(E2E)测试
| |-- refactor-cleaner.md # 死代码清理
| |-- doc-updater.md # 文档同步
-| |-- go-reviewer.md # Go 代码评审(新增)
-| |-- go-build-resolver.md # Go 构建错误解决(新增)
+| |-- go-reviewer.md # Go 代码审查(新增)
+| |-- go-build-resolver.md # Go 构建错误修复(新增)
|
-|-- skills/ # 工作流(Workflow)定义与领域知识
+|-- skills/ # 工作流定义与领域知识
| |-- coding-standards/ # 语言最佳实践
| |-- backend-patterns/ # API、数据库、缓存模式
| |-- frontend-patterns/ # React、Next.js 模式
-| |-- continuous-learning/ # 从会话中自动提取模式(深度指南内容)
-| |-- continuous-learning-v2/ # 基于直觉(Instinct)的带置信度评分学习系统
-| |-- iterative-retrieval/ # 子智能体的渐进式上下文精炼
-| |-- strategic-compact/ # 手动压缩建议(深度指南内容)
+| |-- continuous-learning/ # 从会话中自动提取模式(深度指南)
+| |-- continuous-learning-v2/ # 基于“直觉(Instinct)”的学习,带有置信度评分
+| |-- iterative-retrieval/ # 为子智能体提供渐进式上下文精炼
+| |-- strategic-compact/ # 手动压缩建议(深度指南)
| |-- tdd-workflow/ # TDD 方法论
-| |-- security-review/ # 安全自查表
-| |-- eval-harness/ # 验证循环评测(深度指南内容)
-| |-- verification-loop/ # 持续验证(深度指南内容)
+| |-- security-review/ # 安全检查清单
+| |-- eval-harness/ # 验证循环评测(深度指南)
+| |-- verification-loop/ # 持续验证(深度指南)
| |-- golang-patterns/ # Go 惯用法与最佳实践(新增)
| |-- golang-testing/ # Go 测试模式、TDD、基准测试(新增)
|
-|-- commands/ # 用于快速执行的斜杠命令(Slash Commands)
+|-- commands/ # 用于快速执行的斜杠命令
| |-- tdd.md # /tdd - 测试驱动开发
-| |-- plan.md # /plan - 实现规划
-| |-- e2e.md # /e2e - 端到端测试生成
-| |-- code-review.md # /code-review - 质量评审
+| |-- plan.md # /plan - 实现方案规划
+| |-- e2e.md # /e2e - 生成 E2E 测试
+| |-- code-review.md # /code-review - 质量审查
| |-- build-fix.md # /build-fix - 修复构建错误
| |-- refactor-clean.md # /refactor-clean - 移除死代码
-| |-- learn.md # /learn - 会话中途提取模式(深度指南内容)
-| |-- checkpoint.md # /checkpoint - 保存验证状态(深度指南内容)
-| |-- verify.md # /verify - 运行验证循环(深度指南内容)
+| |-- learn.md # /learn - 在会话中途提取模式(深度指南)
+| |-- checkpoint.md # /checkpoint - 保存验证状态(深度指南)
+| |-- verify.md # /verify - 运行验证循环(深度指南)
| |-- setup-pm.md # /setup-pm - 配置包管理器
-| |-- go-review.md # /go-review - Go 代码评审(新增)
+| |-- go-review.md # /go-review - Go 代码审查(新增)
| |-- go-test.md # /go-test - Go TDD 工作流(新增)
| |-- go-build.md # /go-build - 修复 Go 构建错误(新增)
-| |-- skill-create.md # /skill-create - 从 git 历史生成技能(新增)
+| |-- skill-create.md # /skill-create - 从 Git 历史生成技能(新增)
| |-- instinct-status.md # /instinct-status - 查看已学习的直觉(新增)
| |-- instinct-import.md # /instinct-import - 导入直觉(新增)
| |-- instinct-export.md # /instinct-export - 导出直觉(新增)
-| |-- evolve.md # /evolve - 将相关直觉聚类为技能(新增)
+| |-- evolve.md # /evolve - 将直觉聚类为技能(新增)
|
-|-- rules/ # 必须遵守的准则(复制到 ~/.claude/rules/)
+|-- rules/ # 必须遵守的指南(需复制到 ~/.claude/rules/)
| |-- security.md # 强制性安全检查
| |-- coding-style.md # 不可变性、文件组织
| |-- testing.md # TDD、80% 覆盖率要求
@@ -157,17 +204,17 @@ everything-claude-code/
|
|-- hooks/ # 基于触发器的自动化
| |-- hooks.json # 所有钩子配置(PreToolUse, PostToolUse, Stop 等)
-| |-- memory-persistence/ # 会话生命周期钩子(深度指南内容)
-| |-- strategic-compact/ # 压缩建议(深度指南内容)
+| |-- memory-persistence/ # 会话生命周期钩子(深度指南)
+| |-- strategic-compact/ # 压缩建议(深度指南)
|
|-- scripts/ # 跨平台 Node.js 脚本(新增)
-| |-- lib/ # 共享实用程序
+| |-- lib/ # 共享工具库
| | |-- utils.js # 跨平台文件/路径/系统工具
| | |-- package-manager.js # 包管理器检测与选择
| |-- hooks/ # 钩子实现
| | |-- session-start.js # 会话开始时加载上下文
| | |-- session-end.js # 会话结束时保存状态
-| | |-- pre-compact.js # 压缩前状态保存
+| | |-- pre-compact.js # 压缩前的状态保存
| | |-- suggest-compact.js # 战略性压缩建议
| | |-- evaluate-session.js # 从会话中提取模式
| |-- setup-package-manager.js # 交互式包管理器设置
@@ -177,108 +224,108 @@ everything-claude-code/
| |-- hooks/ # 钩子测试
| |-- run-all.js # 运行所有测试
|
-|-- contexts/ # 动态系统提示词注入上下文(深度指南内容)
+|-- contexts/ # 动态系统提示词注入上下文(深度指南)
| |-- dev.md # 开发模式上下文
-| |-- review.md # 代码评审模式上下文
+| |-- review.md # 代码审查模式上下文
| |-- research.md # 研究/探索模式上下文
|
-|-- examples/ # 配置与会话示例
+|-- examples/ # 配置和会话示例
| |-- CLAUDE.md # 项目级配置示例
| |-- user-CLAUDE.md # 用户级配置示例
|
|-- mcp-configs/ # MCP 服务配置
-| |-- mcp-servers.json # GitHub, Supabase, Vercel, Railway 等
+| |-- mcp-servers.json # GitHub, Supabase, Vercel, Railway 等配置
|
|-- marketplace.json # 自托管市场配置(用于 /plugin marketplace add)
```
---
-## 生态工具
+## 🛠️ 生态工具
### 技能创建器(Skill Creator)
-有两种方法可以从你的仓库生成 Claude Code 技能:
+有两种方式可以从你的仓库生成 Claude Code 技能:
-#### 方案 A:本地分析(内置)
+#### 选项 A:本地分析(内置)
使用 `/skill-create` 命令进行本地分析,无需外部服务:
```bash
/skill-create # 分析当前仓库
-/skill-create --instincts # 同时为持续学习(continuous-learning)生成直觉(instincts)
+/skill-create --instincts # 同时为持续学习生成“直觉(Instincts)”
```
-该命令会在本地分析你的 git 历史并生成 SKILL.md 文件。
+这会在本地分析你的 Git 历史并生成 SKILL.md 文件。
-#### 方案 B:GitHub App(高级)
+#### 选项 B:GitHub App(高级版)
-适用于高级功能(1万+ commit、自动 PR、团队共享):
+适用于高级功能(10k+ 提交、自动 PR、团队共享):
[安装 GitHub App](https://github.com/apps/skill-creator) | [ecc.tools](https://ecc.tools)
```bash
-# 在任何 issue 下留言:
+# 在任何 Issue 下回复:
/skill-creator analyze
-# 或者在 push 到默认分支时自动触发
+# 或在推送到默认分支时自动触发
```
-两种方案都会创建:
+两种选项都会创建:
- **SKILL.md 文件** - 可直接用于 Claude Code 的技能
-- **直觉集合(Instinct collections)** - 用于 continuous-learning-v2
-- **模式提取(Pattern extraction)** - 从你的提交历史中学习
+- **直觉集合 (Instinct collections)** - 用于 continuous-learning-v2
+- **模式提取** - 从你的提交历史中学习
-### 持续学习(Continuous Learning)v2
+### 🧠 持续学习 v2 (Continuous Learning v2)
-基于直觉(instinct)的学习系统会自动学习你的开发模式:
+基于“直觉(Instinct)”的学习系统会自动学习你的模式:
```bash
-/instinct-status # 显示带有置信度的已学习直觉
+/instinct-status # 显示已学习的直觉及其置信度
/instinct-import # 导入他人的直觉
-/instinct-export # 导出你的直觉以便分享
-/evolve # 将相关的直觉聚类为技能(skills)
+/instinct-export # 导出你的直觉以便共享
+/evolve # 将相关的直觉聚类为技能
```
-详见 `skills/continuous-learning-v2/` 的完整文档。
+详见 `skills/continuous-learning-v2/` 完整文档。
---
-## 要求
+## 📋 运行要求
### Claude Code CLI 版本
**最低版本:v2.1.0 或更高**
-由于插件系统处理钩子(hooks)方式的变更,本插件要求 Claude Code CLI v2.1.0+。
+由于插件系统处理钩子(Hooks)方式的变更,此插件需要 Claude Code CLI v2.1.0+。
检查你的版本:
```bash
claude --version
```
-### 重要:钩子(Hooks)自动加载行为
+### 重要:钩子自动加载行为
-> ⚠️ **对贡献者的提醒:** 请勿在 `.claude-plugin/plugin.json` 中添加 `"hooks"` 字段。这是由回归测试强制执行的。
+> ⚠️ **致贡献者:** 请勿在 `.claude-plugin/plugin.json` 中添加 `"hooks"` 字段。这是通过回归测试强制执行的。
-Claude Code v2.1+ 会**自动加载**已安装插件中约定的 `hooks/hooks.json`。在 `plugin.json` 中显式声明会导致重复检测错误:
+按照约定,Claude Code v2.1+ 会**自动加载**任何已安装插件中的 `hooks/hooks.json`。如果在 `plugin.json` 中显式声明,会导致重复检测错误:
```
Duplicate hooks file detected: ./hooks/hooks.json resolves to already-loaded file
```
-**历史背景:** 此问题在本仓库中曾多次出现修复/回退循环([#29](https://github.com/affaan-m/everything-claude-code/issues/29), [#52](https://github.com/affaan-m/everything-claude-code/issues/52), [#103](https://github.com/affaan-m/everything-claude-code/issues/103))。Claude Code 版本间的行为差异导致了混淆。我们现在已加入回归测试以防止此类问题再次发生。
+**历史背景:** 此问题在本仓库中引发了多次修复/回滚循环([#29](https://github.com/affaan-m/everything-claude-code/issues/29), [#52](https://github.com/affaan-m/everything-claude-code/issues/52), [#103](https://github.com/affaan-m/everything-claude-code/issues/103))。由于 Claude Code 版本间的行为变更导致了混淆,我们现在通过回归测试来防止此问题再次引入。
---
-## 安装
+## 📥 安装
-### 方案 1:作为插件安装(推荐)
+### 选项 1:作为插件安装(推荐)
-这是使用本仓库最简单的方法 —— 作为 Claude Code 插件安装:
+使用本仓库最简单的方式 —— 作为 Claude Code 插件安装:
```bash
-# 将此仓库添加为市场(marketplace)
+# 将此仓库添加为市场
/plugin marketplace add affaan-m/everything-claude-code
# 安装插件
@@ -303,91 +350,91 @@ Duplicate hooks file detected: ./hooks/hooks.json resolves to already-loaded fil
}
```
-安装后即可立即使用所有命令(commands)、智能体(agents)、技能(skills)和钩子(hooks)。
+安装后即可立即使用所有命令、智能体、技能和钩子。
-> **注意:** Claude Code 插件系统目前不支持通过插件分发规则(`rules`)(这是 [上游限制](https://code.claude.com/docs/en/plugins-reference))。你需要手动安装规则:
+> **注意:** Claude Code 插件系统目前不支持通过插件分发 `rules`([上游限制](https://code.claude.com/docs/en/plugins-reference))。你需要手动安装规则:
>
> ```bash
> # 首先克隆仓库
> git clone https://github.com/affaan-m/everything-claude-code.git
>
-> # 方案 A:用户级规则(应用于所有项目)
+> # 选项 A:用户级规则(适用于所有项目)
> cp -r everything-claude-code/rules/* ~/.claude/rules/
>
-> # 方案 B:项目级规则(仅应用于当前项目)
+> # 选项 B:项目级规则(仅适用于当前项目)
> mkdir -p .claude/rules
> cp -r everything-claude-code/rules/* .claude/rules/
> ```
---
-### 方案 2:手动安装
+### 🔧 选项 2:手动安装
-如果你更倾向于手动控制安装的内容:
+如果你更喜欢手动控制安装内容:
```bash
# 克隆仓库
git clone https://github.com/affaan-m/everything-claude-code.git
-# 复制智能体(agents)到你的 Claude 配置目录
+# 将智能体(Agents)复制到你的 Claude 配置中
cp everything-claude-code/agents/*.md ~/.claude/agents/
-# 复制规则(rules)
+# 复制规则(Rules)
cp everything-claude-code/rules/*.md ~/.claude/rules/
-# 复制命令(commands)
+# 复制命令(Commands)
cp everything-claude-code/commands/*.md ~/.claude/commands/
-# 复制技能(skills)
+# 复制技能(Skills)
cp -r everything-claude-code/skills/* ~/.claude/skills/
```
-#### 将钩子(hooks)添加到 settings.json
+#### 将钩子(Hooks)添加到 settings.json
-将 `hooks/hooks.json` 中的钩子配置复制到你的 `~/.claude/settings.json` 中。
+将 `hooks/hooks.json` 中的钩子内容复制到你的 `~/.claude/settings.json`。
-#### 配置 MCPs
+#### 配置 MCP
-将 `mcp-configs/mcp-servers.json` 中你需要的 MCP 服务配置复制到你的 `~/.claude.json`。
+将 `mcp-configs/mcp-servers.json` 中所需的 MCP 服务器配置复制到你的 `~/.claude.json`。
-**重要:** 请将 `YOUR_*_HERE` 占位符替换为你真实的 API 密钥。
+**重要提示:** 请将 `YOUR_*_HERE` 占位符替换为你真实的 API 密钥。
---
-## 核心概念
+## 🎯 核心概念
-### 智能体(Agents)
+### 智能体 (Agents)
-子智能体(Subagents)负责处理受限范围内的委派任务。示例:
+子智能体(Subagents)负责处理具有特定范围的委派任务。示例:
```markdown
---
name: code-reviewer
-description: 评审代码质量、安全性与可维护性
+description: 审查代码的质量、安全性和可维护性
tools: ["Read", "Grep", "Glob", "Bash"]
model: opus
---
-你是一名资深代码评审员...
+你是一名资深代码审查员...
```
-### 技能(Skills)
+### 技能 (Skills)
-技能(Skills)是可由命令或智能体调用的工作流定义:
+技能是可被命令或智能体调用的工作流定义:
```markdown
# TDD 工作流
1. 首先定义接口
-2. 编写失败的测试(RED)
-3. 实现最简代码(GREEN)
-4. 重构(IMPROVE)
-5. 验证覆盖率是否达到 80% 以上
+2. 编写失败的测试 (RED)
+3. 实现最简代码 (GREEN)
+4. 重构 (IMPROVE)
+5. 验证 80%+ 的覆盖率
```
-### 钩子(Hooks)
+### 钩子 (Hooks)
-钩子(Hooks)在工具事件发生时触发。示例 —— 针对 console.log 发出警告:
+钩子在工具事件发生时触发。示例 —— 警告 `console.log` 的使用:
```json
{
@@ -399,22 +446,22 @@ model: opus
}
```
-### 规则(Rules)
+### 规则 (Rules)
-规则(Rules)是必须始终遵守的准则。请保持它们的模块化:
+规则是必须始终遵循的指南。请保持其模块化:
```
~/.claude/rules/
- security.md # 禁止硬编码密钥
+ security.md # 不允许硬编码密钥
coding-style.md # 不可变性、文件限制
testing.md # TDD、覆盖率要求
```
---
-## 运行测试
+## 🧪 运行测试
-本插件包含一个全面的测试套件:
+该插件包含完整的测试套件:
```bash
# 运行所有测试
@@ -428,78 +475,78 @@ node tests/hooks/hooks.test.js
---
-## 贡献
+## 🤝 参与贡献
-**非常欢迎并鼓励贡献。**
+**欢迎并鼓励各类贡献。**
-本仓库旨在成为一个社区资源。如果你有:
+本仓库旨在成为社区资源。如果你有:
- 有用的智能体或技能
- 巧妙的钩子
- 更好的 MCP 配置
- 改进后的规则
-请提交你的贡献!参考 [CONTRIBUTING.md](CONTRIBUTING.md) 了解指南。
+请尽管贡献!请参阅 [CONTRIBUTING.md](CONTRIBUTING.md) 获取指南。
### 贡献思路
-- 语言专用技能(Python, Rust 模式)—— 已包含 Go!
-- 框架专用配置(Django, Rails, Laravel)
+- 语言特定技能(Python, Rust 模式)—— 已包含 Go!
+- 框架特定配置(Django, Rails, Laravel)
- DevOps 智能体(Kubernetes, Terraform, AWS)
- 测试策略(不同框架)
-- 领域特定知识(机器学习、数据工程、移动开发)
+- 领域特定知识(机器学习、数据工程、移动端)
---
-## 背景
+## 📖 项目背景
-我从 Claude Code 实验阶段就开始使用了。在 2025 年 9 月的 Anthropic x Forum Ventures 黑客松中,我与 [@DRodriguezFX](https://x.com/DRodriguezFX) 合作开发了 [zenith.chat](https://zenith.chat),并获得了冠军 —— 该项目完全使用 Claude Code 构建。
+自 Claude Code 实验阶段起我就一直在使用它。2025 年 9 月,我与 [@DRodriguezFX](https://x.com/DRodriguezFX) 凭借 [zenith.chat](https://zenith.chat) 赢得了 Anthropic x Forum Ventures 黑客松 —— 该项目完全使用 Claude Code 构建。
-这些配置在多个生产级应用中经过了实战检验。
+这些配置在多个生产级应用中经过了实战测试。
---
-## 重要提示
+## ⚠️ 重要说明
-### 上下文窗口管理
+### 上下文窗口管理 (Context Window Management)
-**至关重要:** 不要一次性启用所有 MCP。如果启用的工具过多,你的 200k 上下文窗口可能会缩减到 70k。
+**至关重要:** 不要同时启用所有 MCP。过多的工具会导致 200k 的上下文窗口缩减至 70k。
经验法则:
- 配置 20-30 个 MCP
-- 每个项目保持启用 10 个以内
-- 活跃工具总数控制在 80 个以内
+- 每个项目保持启用 10 个以下
+- 活跃工具总数保持在 80 个以下
-在项目配置中使用 `disabledMcpServers` 来禁用不需要的服务。
+使用项目配置中的 `disabledMcpServers` 来禁用不常用的服务器。
### 自定义
这些配置适用于我的工作流。你应该:
-1. 从你产生共鸣的内容开始
+1. 从引起你共鸣的部分开始
2. 根据你的技术栈进行修改
-3. 移除你不需要的内容
-4. 加入你自己的模式
+3. 移除你不需要的部分
+4. 添加你自己的模式
---
-## Star 历史
+## 🌟 Star 历史
[](https://star-history.com/#affaan-m/everything-claude-code&Date)
---
-## 相关链接
+## 🔗 相关链接
-- **简明指南(入门必读):** [Everything Claude Code 简明指南](https://x.com/affaanmustafa/status/2012378465664745795)
-- **深度指南(进阶参考):** [Everything Claude Code 深度指南](https://x.com/affaanmustafa/status/2014040193557471352)
+- **简明指南 (从这里开始):** [Everything Claude Code 简明指南](https://x.com/affaanmustafa/status/2012378465664745795)
+- **深度指南 (进阶必读):** [Everything Claude Code 深度指南](https://x.com/affaanmustafa/status/2014040193557471352)
- **关注我:** [@affaanmustafa](https://x.com/affaanmustafa)
-- **zenith.chat:** [zenith.chat](https://zenith.chat)
+- **zenith.chat:** [zenith.chat](https://zenith.chat)
---
-## 许可证
+## 📄 开源协议
-MIT - 自由使用,按需修改,如果可以请回馈社区。
+MIT - 自由使用、按需修改,如能回馈社区不胜感激。
---
-**如果对你有帮助,请给本仓库点个 Star。阅读两份指南。构建伟大的产品。**
+**如果此仓库对你有帮助,请点亮 Star。阅读两篇指南。去构建伟大的产品吧。**
diff --git a/README.zh-CN.md b/README.zh-CN.md
index a0b5ee9..1499d54 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -7,10 +7,17 @@


-
- English |
- 简体中文
-
+---
+
+
+
+**🌐 Language / 语言 / 語言**
+
+[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md)
+
+
+
+---
**来自 Anthropic 黑客马拉松获胜者的完整 Claude Code 配置集合。**
@@ -52,7 +59,47 @@
---
-## 跨平台支持
+## 🚀 快速开始
+
+在 2 分钟内快速上手:
+
+### 第一步:安装插件
+
+```bash
+# 添加市场
+/plugin marketplace add affaan-m/everything-claude-code
+
+# 安装插件
+/plugin install everything-claude-code@everything-claude-code
+```
+
+### 第二步:安装规则(必需)
+
+> ⚠️ **重要提示:** Claude Code 插件无法自动分发 `rules`,需要手动安装:
+
+```bash
+# 首先克隆仓库
+git clone https://github.com/affaan-m/everything-claude-code.git
+
+# 复制规则(应用于所有项目)
+cp -r everything-claude-code/rules/* ~/.claude/rules/
+```
+
+### 第三步:开始使用
+
+```bash
+# 尝试一个命令
+/plan "添加用户认证"
+
+# 查看可用命令
+/plugin list everything-claude-code@everything-claude-code
+```
+
+✨ **完成!** 你现在可以使用 15+ 代理、30+ 技能和 20+ 命令。
+
+---
+
+## 🌐 跨平台支持
此插件现在完全支持 **Windows、macOS 和 Linux**。所有钩子和脚本都已用 Node.js 重写,以实现最大的兼容性。
@@ -87,7 +134,7 @@ node scripts/setup-package-manager.js --detect
---
-## 里面有什么
+## 📦 里面有什么
这个仓库是一个 **Claude Code 插件** - 直接安装或手动复制组件。
@@ -192,7 +239,7 @@ everything-claude-code/
---
-## 生态系统工具
+## 🛠️ 生态系统工具
### 技能创建器
@@ -227,7 +274,7 @@ everything-claude-code/
- **直觉集合** - 用于 continuous-learning-v2
- **模式提取** - 从你的提交历史中学习
-### 持续学习 v2
+### 🧠 持续学习 v2
基于直觉的学习系统自动学习你的模式:
@@ -242,7 +289,7 @@ everything-claude-code/
---
-## 安装
+## 📥 安装
### 选项 1:作为插件安装(推荐)
@@ -292,7 +339,7 @@ everything-claude-code/
---
-### 选项 2:手动安装
+### 🔧 选项 2:手动安装
如果你希望对安装的内容进行手动控制:
@@ -325,7 +372,7 @@ cp -r everything-claude-code/skills/* ~/.claude/skills/
---
-## 关键概念
+## 🎯 关键概念
### 代理
@@ -383,7 +430,7 @@ model: opus
---
-## 运行测试
+## 🧪 运行测试
插件包含一个全面的测试套件:
@@ -399,7 +446,7 @@ node tests/hooks/hooks.test.js
---
-## 贡献
+## 🤝 贡献
**欢迎并鼓励贡献。**
@@ -421,7 +468,7 @@ node tests/hooks/hooks.test.js
---
-## 背景
+## 📖 背景
自实验性推出以来,我一直在使用 Claude Code。2025 年 9 月,与 [@DRodriguezFX](https://x.com/DRodriguezFX) 一起使用 Claude Code 构建 [zenith.chat](https://zenith.chat),赢得了 Anthropic x Forum Ventures 黑客马拉松。
@@ -429,7 +476,7 @@ node tests/hooks/hooks.test.js
---
-## 重要说明
+## ⚠️ 重要说明
### 上下文窗口管理
@@ -452,13 +499,13 @@ node tests/hooks/hooks.test.js
---
-## Star 历史
+## 🌟 Star 历史
[](https://star-history.com/#affaan-m/everything-claude-code&Date)
---
-## 链接
+## 🔗 链接
- **精简指南(从这里开始):** [The Shorthand Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2012378465664745795)
- **详细指南(高级):** [The Longform Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2014040193557471352)
@@ -467,7 +514,7 @@ node tests/hooks/hooks.test.js
---
-## 许可证
+## 📄 许可证
MIT - 自由使用,根据需要修改,如果可以请回馈。
diff --git a/agents/build-error-resolver.md b/agents/build-error-resolver.md
index c175682..a4cf173 100644
--- a/agents/build-error-resolver.md
+++ b/agents/build-error-resolver.md
@@ -11,12 +11,12 @@ model: opus
## 核心职责
-1. **TypeScript 错误解决** - 修复类型错误、推断问题、泛型约束。
-2. **构建错误修复** - 解决编译失败、模块解析(Module Resolution)问题。
-3. **依赖问题** - 修复导入错误、缺失的包、版本冲突。
-4. **配置错误** - 解决 `tsconfig.json`、webpack、Next.js 配置问题。
-5. **最小差异修改 (Minimal Diffs)** - 尽可能通过最小的改动来修复错误。
-6. **禁止架构更改** - 仅修复错误,不进行重构或重新设计。
+1. **TypeScript 错误解决** - 修复类型错误、推断问题、泛型约束。
+2. **构建错误修复** - 解决编译失败、模块解析(Module Resolution)问题。
+3. **依赖问题** - 修复导入错误、缺失的包、版本冲突。
+4. **配置错误** - 解决 `tsconfig.json`、webpack、Next.js 配置问题。
+5. **最小差异修改 (Minimal Diffs)** - 尽可能通过最小的改动来修复错误。
+6. **禁止架构更改** - 仅修复错误,不进行重构或重新设计。
## 可用工具
diff --git a/agents/python-reviewer.md b/agents/python-reviewer.md
new file mode 100644
index 0000000..8dde17e
--- /dev/null
+++ b/agents/python-reviewer.md
@@ -0,0 +1,469 @@
+---
+name: python-reviewer
+description: Expert Python code reviewer specializing in PEP 8 compliance, Pythonic idioms, type hints, security, and performance. Use for all Python code changes. MUST BE USED for Python projects.
+tools: ["Read", "Grep", "Glob", "Bash"]
+model: opus
+---
+
+你是一位资深的 Python 代码审查员(Code Reviewer),致力于确保代码符合高标准的 Pythonic 规范及最佳实践。
+
+当被调用时:
+1. 运行 `git diff -- '*.py'` 以查看最近的 Python 文件变更
+2. 如果可用,运行静态分析工具(ruff、mypy、pylint、black --check)
+3. 重点关注修改过的 `.py` 文件
+4. 立即开始审查
+
+## 安全检查 (严重/CRITICAL)
+
+- **SQL 注入 (SQL Injection)**:数据库查询中的字符串拼接
+ ```python
+ # 错误做法
+ cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
+ # 正确做法
+ cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
+ ```
+
+- **命令注入 (Command Injection)**:subprocess/os.system 中未经验证的输入
+ ```python
+ # 错误做法
+ os.system(f"curl {url}")
+ # 正确做法
+ subprocess.run(["curl", url], check=True)
+ ```
+
+- **路径穿越 (Path Traversal)**:用户控制的文件路径
+ ```python
+ # 错误做法
+ open(os.path.join(base_dir, user_path))
+ # 正确做法
+ clean_path = os.path.normpath(user_path)
+ if clean_path.startswith(".."):
+ raise ValueError("Invalid path")
+ safe_path = os.path.join(base_dir, clean_path)
+ ```
+
+- **Eval/Exec 滥用**:在 eval/exec 中使用用户输入
+- **Pickle 不安全反序列化**:加载不可信的 pickle 数据
+- **硬编码密钥 (Hardcoded Secrets)**:源码中包含 API 密钥、密码
+- **弱加密**:出于安全目的使用 MD5/SHA1
+- **YAML 不安全加载**:使用不带 Loader 的 yaml.load
+
+## 错误处理 (严重/CRITICAL)
+
+- **空 except 语句 (Bare Except Clauses)**:捕获所有异常
+ ```python
+ # 错误做法
+ try:
+ process()
+ except:
+ pass
+
+ # 正确做法
+ try:
+ process()
+ except ValueError as e:
+ logger.error(f"Invalid value: {e}")
+ ```
+
+- **吞掉异常 (Swallowing Exceptions)**:静默失败
+- **用异常代替流程控制**:将异常用于正常的控制流
+- **缺失 finally**:资源未被清理
+ ```python
+ # 错误做法
+ f = open("file.txt")
+ data = f.read()
+ # 如果发生异常,文件永远不会关闭
+
+ # 正确做法
+ with open("file.txt") as f:
+ data = f.read()
+ # 或者
+ f = open("file.txt")
+ try:
+ data = f.read()
+ finally:
+ f.close()
+ ```
+
+## 类型提示 (高优先级/HIGH)
+
+- **缺失类型提示 (Type Hints)**:公共函数没有类型标注
+ ```python
+ # 错误做法
+ def process_user(user_id):
+ return get_user(user_id)
+
+ # 正确做法
+ from typing import Optional
+
+ def process_user(user_id: str) -> Optional[User]:
+ return get_user(user_id)
+ ```
+
+- **使用 Any 而非特定类型**
+ ```python
+ # 错误做法
+ from typing import Any
+
+ def process(data: Any) -> Any:
+ return data
+
+ # 正确做法
+ from typing import TypeVar
+
+ T = TypeVar('T')
+
+ def process(data: T) -> T:
+ return data
+ ```
+
+- **错误的返回类型**:标注与实际不符
+- **未合理使用 Optional**:可为 None 的参数未标记为 Optional
+
+## Pythonic 代码 (高优先级/HIGH)
+
+- **未使用上下文管理器 (Context Managers)**:手动进行资源管理
+ ```python
+ # 错误做法
+ f = open("file.txt")
+ try:
+ content = f.read()
+ finally:
+ f.close()
+
+ # 正确做法
+ with open("file.txt") as f:
+ content = f.read()
+ ```
+
+- **C 风格循环**:未使用推导式(Comprehensions)或迭代器
+ ```python
+ # 错误做法
+ result = []
+ for item in items:
+ if item.active:
+ result.append(item.name)
+
+ # 正确做法
+ result = [item.name for item in items if item.active]
+ ```
+
+- **使用 isinstance 检查类型**:而非使用 type()
+ ```python
+ # 错误做法
+ if type(obj) == str:
+ process(obj)
+
+ # 正确做法
+ if isinstance(obj, str):
+ process(obj)
+ ```
+
+- **未使用枚举 (Enum) 或存在魔术数字 (Magic Numbers)**
+ ```python
+ # 错误做法
+ if status == 1:
+ process()
+
+ # 正确做法
+ from enum import Enum
+
+ class Status(Enum):
+ ACTIVE = 1
+ INACTIVE = 2
+
+ if status == Status.ACTIVE:
+ process()
+ ```
+
+- **循环中的字符串拼接**:使用 + 构建字符串
+ ```python
+ # 错误做法
+ result = ""
+ for item in items:
+ result += str(item)
+
+ # 正确做法
+ result = "".join(str(item) for item in items)
+ ```
+
+- **可变默认参数 (Mutable Default Arguments)**:经典的 Python 陷阱
+ ```python
+ # 错误做法
+ def process(items=[]):
+ items.append("new")
+ return items
+
+ # 正确做法
+ def process(items=None):
+ if items is None:
+ items = []
+ items.append("new")
+ return items
+ ```
+
+## 代码质量 (高优先级/HIGH)
+
+- **参数过多**:函数参数超过 5 个
+ ```python
+ # 错误做法
+ def process_user(name, email, age, address, phone, status):
+ pass
+
+ # 正确做法
+ from dataclasses import dataclass
+
+ @dataclass
+ class UserData:
+ name: str
+ email: str
+ age: int
+ address: str
+ phone: str
+ status: str
+
+ def process_user(data: UserData):
+ pass
+ ```
+
+- **过长函数**:函数超过 50 行
+- **嵌套过深**:缩进超过 4 层
+- **上帝类/模块 (God Classes/Modules)**:承担了过多职责
+- **重复代码**:重复的模式
+- **魔术数字 (Magic Numbers)**:未命名的常量
+ ```python
+ # 错误做法
+ if len(data) > 512:
+ compress(data)
+
+ # 正确做法
+ MAX_UNCOMPRESSED_SIZE = 512
+
+ if len(data) > MAX_UNCOMPRESSED_SIZE:
+ compress(data)
+ ```
+
+## 并发 (高优先级/HIGH)
+
+- **缺失锁 (Lock)**:共享状态未进行同步
+ ```python
+ # 错误做法
+ counter = 0
+
+ def increment():
+ global counter
+ counter += 1 # 竞态条件 (Race condition)!
+
+ # 正确做法
+ import threading
+
+ counter = 0
+ lock = threading.Lock()
+
+ def increment():
+ global counter
+ with lock:
+ counter += 1
+ ```
+
+- **全局解释器锁 (GIL) 假设**:盲目假设线程安全
+- **Async/Await 滥用**:错误地混合同步和异步代码
+
+## 性能 (中优先级/MEDIUM)
+
+- **N+1 查询**:在循环中进行数据库查询
+ ```python
+ # 错误做法
+ for user in users:
+ orders = get_orders(user.id) # N 次查询!
+
+ # 正确做法
+ user_ids = [u.id for u in users]
+ orders = get_orders_for_users(user_ids) # 1 次查询
+ ```
+
+- **低效的字符串操作**
+ ```python
+ # 错误做法
+ text = "hello"
+ for i in range(1000):
+ text += " world" # O(n²)
+
+ # 正确做法
+ parts = ["hello"]
+ for i in range(1000):
+ parts.append(" world")
+ text = "".join(parts) # O(n)
+ ```
+
+- **布尔上下文中的列表**:使用 len() 而非真值性检查
+ ```python
+ # 错误做法
+ if len(items) > 0:
+ process(items)
+
+ # 正确做法
+ if items:
+ process(items)
+ ```
+
+- **不必要的列表创建**:在不需要时使用 list()
+ ```python
+ # 错误做法
+ for item in list(dict.keys()):
+ process(item)
+
+ # 正确做法
+ for item in dict:
+ process(item)
+ ```
+
+## 最佳实践 (中优先级/MEDIUM)
+
+- **PEP 8 合规性**:代码格式违规
+ - 导入顺序(标准库、第三方库、本地库)
+ - 行宽(Black 默认为 88,PEP 8 为 79)
+ - 命名规范(函数/变量使用 snake_case,类使用 PascalCase)
+ - 运算符周围的空格
+
+- **文档字符串 (Docstrings)**:缺失或格式不良的文档字符串
+ ```python
+ # 错误做法
+ def process(data):
+ return data.strip()
+
+ # 正确做法
+ def process(data: str) -> str:
+ """从输入字符串中移除首尾空格。
+
+ Args:
+ data: 要处理的输入字符串。
+
+ Returns:
+ 移除空格后的处理字符串。
+ """
+ return data.strip()
+ ```
+
+- **日志记录 vs Print**:使用 print() 进行日志记录
+ ```python
+ # 错误做法
+ print("Error occurred")
+
+ # 正确做法
+ import logging
+ logger = logging.getLogger(__name__)
+ logger.error("Error occurred")
+ ```
+
+- **相对导入**:在脚本中使用相对导入
+- **未使用的导入**:死代码 (Dead code)
+- **缺失 `if __name__ == "__main__"`**:脚本入口点未加保护
+
+## Python 特有的反模式 (Anti-Patterns)
+
+- **`from module import *`**:命名空间污染
+ ```python
+ # 错误做法
+ from os.path import *
+
+ # 正确做法
+ from os.path import join, exists
+ ```
+
+- **未使用 `with` 语句**:资源泄露
+- **静默异常**:空的 `except: pass`
+- **使用 == 与 None 比较**
+ ```python
+ # 错误做法
+ if value == None:
+ process()
+
+ # 正确做法
+ if value is None:
+ process()
+ ```
+
+- **未使用 `isinstance` 进行类型检查**:使用了 type()
+- **遮蔽内置变量 (Shadowing Built-ins)**:将变量命名为 `list`、`dict`、`str` 等
+ ```python
+ # 错误做法
+ list = [1, 2, 3] # 遮蔽了内置的 list 类型
+
+ # 正确做法
+ items = [1, 2, 3]
+ ```
+
+## 审查输出格式
+
+针对每个问题:
+```text
+[CRITICAL] SQL 注入漏洞
+文件: app/routes/user.py:42
+问题: 用户输入直接插入到了 SQL 查询中
+修复建议: 使用参数化查询
+
+query = f"SELECT * FROM users WHERE id = {user_id}" # 错误
+query = "SELECT * FROM users WHERE id = %s" # 正确
+cursor.execute(query, (user_id,))
+```
+
+## 诊断命令
+
+运行以下检查:
+```bash
+# 类型检查
+mypy .
+
+# 代码检查 (Linting)
+ruff check .
+pylint app/
+
+# 格式检查
+black --check .
+isort --check-only .
+
+# 安全扫描
+bandit -r .
+
+# 依赖项审计
+pip-audit
+safety check
+
+# 测试
+pytest --cov=app --cov-report=term-missing
+```
+
+## 批准标准
+
+- **批准 (Approve)**:无严重(CRITICAL)或高(HIGH)优先级问题
+- **警告 (Warning)**:仅存在中(MEDIUM)优先级问题(可谨慎合并)
+- **阻止 (Block)**:发现严重(CRITICAL)或高(HIGH)优先级问题
+
+## Python 版本注意事项
+
+- 检查 `pyproject.toml` 或 `setup.py` 以确认 Python 版本要求
+- 注意代码是否使用了较新 Python 版本的特性(类型提示 | 3.5+,f-strings 3.6+,walrus 3.8+,match 3.10+)
+- 标记已弃用的标准库模块
+- 确保类型提示与最低 Python 版本兼容
+
+## 框架特定检查
+
+### Django
+- **N+1 查询**:使用 `select_related` 和 `prefetch_related`
+- **缺失迁移**:模型变更但未生成迁移文件
+- **原生 SQL**:在 ORM 可行的情况下使用了 `raw()` 或 `execute()`
+- **事务管理**:多步操作缺失 `atomic()`
+
+### FastAPI/Flask
+- **CORS 配置错误**:跨域限制过于宽松
+- **依赖注入**:正确使用 Depends/injection
+- **响应模型**:缺失或错误的响应模型
+- **验证**:使用 Pydantic 模型进行请求验证
+
+### 异步 (FastAPI/aiohttp)
+- **异步函数中的阻塞调用**:在异步上下文中使用了同步库
+- **缺失 await**:忘记 await 协程
+- **异步生成器**:正确的异步迭代
+
+审查时请思考:“这段代码能通过顶级 Python 团队或开源项目的审查吗?”
diff --git a/agents/tdd-guide.md b/agents/tdd-guide.md
index 5f13406..3c44d1d 100644
--- a/agents/tdd-guide.md
+++ b/agents/tdd-guide.md
@@ -277,4 +277,4 @@ npm test && npm run lint
npm test -- --coverage --ci
```
-**记住**:没有测试就没有代码。测试不是可选的。它们是安全网,能够让你自信地重构、快速开发并确保生产环境的可靠性。
+**记住**:没有测试就没有代码。测试不是可选的。它们是安全网,能够让你自信地重构、快速开发并确保生产环境的可靠性。
\ No newline at end of file
diff --git a/commands/evolve.md b/commands/evolve.md
index 6f82c12..1f80861 100644
--- a/commands/evolve.md
+++ b/commands/evolve.md
@@ -1,144 +1,144 @@
---
name: evolve
-description: Cluster related instincts into skills, commands, or agents
+description: 将相关的直觉(Instincts)聚类为技能(Skills)、命令(Commands)或智能体(Agents)
command: true
---
-# Evolve Command
+# Evolve 命令
-## Implementation
+## 实现 (Implementation)
-Run the instinct CLI using the plugin root path:
+使用插件根路径运行直觉(Instinct)命令行界面(CLI):
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/skills/continuous-learning-v2/scripts/instinct-cli.py" evolve [--generate]
```
-Or if `CLAUDE_PLUGIN_ROOT` is not set (manual installation):
+或者如果未设置 `CLAUDE_PLUGIN_ROOT`(手动安装):
```bash
python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py evolve [--generate]
```
-Analyzes instincts and clusters related ones into higher-level structures:
-- **Commands**: When instincts describe user-invoked actions
-- **Skills**: When instincts describe auto-triggered behaviors
-- **Agents**: When instincts describe complex, multi-step processes
+分析直觉(Instincts)并将相关的直觉聚类为更高级的结构:
+- **命令(Commands)**:当直觉描述用户调用的操作时
+- **技能(Skills)**:当直觉描述自动触发的行为时
+- **智能体(Agents)**:当直觉描述复杂的、多步骤的流程时
-## Usage
+## 用法 (Usage)
```
-/evolve # Analyze all instincts and suggest evolutions
-/evolve --domain testing # Only evolve instincts in testing domain
-/evolve --dry-run # Show what would be created without creating
-/evolve --threshold 5 # Require 5+ related instincts to cluster
+/evolve # 分析所有直觉并建议演进方案
+/evolve --domain testing # 仅演进测试领域(testing domain)中的直觉
+/evolve --dry-run # 显示将要创建的内容而不实际创建
+/evolve --threshold 5 # 要求至少有 5 个以上的相关直觉才进行聚类
```
-## Evolution Rules
+## 演进规则 (Evolution Rules)
-### → Command (User-Invoked)
-When instincts describe actions a user would explicitly request:
-- Multiple instincts about "when user asks to..."
-- Instincts with triggers like "when creating a new X"
-- Instincts that follow a repeatable sequence
+### → 命令 (Command)(用户调用)
+当直觉描述用户会明确请求的操作时:
+- 多个关于“当用户要求...”的直觉
+- 带有“当创建新的 X 时”等触发器的直觉
+- 遵循可重复序列的直觉
-Example:
+示例:
- `new-table-step1`: "when adding a database table, create migration"
- `new-table-step2`: "when adding a database table, update schema"
- `new-table-step3`: "when adding a database table, regenerate types"
-→ Creates: `/new-table` command
+→ 创建:`/new-table` 命令
-### → Skill (Auto-Triggered)
-When instincts describe behaviors that should happen automatically:
-- Pattern-matching triggers
-- Error handling responses
-- Code style enforcement
+### → 技能 (Skill)(自动触发)
+当直觉描述应该自动发生的行为时:
+- 模式匹配触发器
+- 错误处理响应
+- 代码风格强制执行
-Example:
+示例:
- `prefer-functional`: "when writing functions, prefer functional style"
- `use-immutable`: "when modifying state, use immutable patterns"
- `avoid-classes`: "when designing modules, avoid class-based design"
-→ Creates: `functional-patterns` skill
+→ 创建:`functional-patterns` 技能(Skill)
-### → Agent (Needs Depth/Isolation)
-When instincts describe complex, multi-step processes that benefit from isolation:
-- Debugging workflows
-- Refactoring sequences
-- Research tasks
+### → 智能体 (Agent)(需要深度/隔离)
+当直觉描述复杂的、多步骤的流程,且受益于隔离环境时:
+- 调试工作流(Workflow)
+- 重构序列
+- 研究任务
-Example:
+示例:
- `debug-step1`: "when debugging, first check logs"
- `debug-step2`: "when debugging, isolate the failing component"
- `debug-step3`: "when debugging, create minimal reproduction"
- `debug-step4`: "when debugging, verify fix with test"
-→ Creates: `debugger` agent
+→ 创建:`debugger` 智能体(Agent)
-## What to Do
+## 操作步骤 (What to Do)
-1. Read all instincts from `~/.claude/homunculus/instincts/`
-2. Group instincts by:
- - Domain similarity
- - Trigger pattern overlap
- - Action sequence relationship
-3. For each cluster of 3+ related instincts:
- - Determine evolution type (command/skill/agent)
- - Generate the appropriate file
- - Save to `~/.claude/homunculus/evolved/{commands,skills,agents}/`
-4. Link evolved structure back to source instincts
+1. 从 `~/.claude/homunculus/instincts/` 读取所有直觉(Instincts)
+2. 按以下维度对直觉进行分组:
+ - 领域(Domain)相似性
+ - 触发模式重合度
+ - 操作序列关联性
+3. 对于每个包含 3 个及以上相关直觉的聚类:
+ - 确定演进类型(命令/技能/智能体)
+ - 生成相应的文件
+ - 保存至 `~/.claude/homunculus/evolved/{commands,skills,agents}/`
+4. 将演进后的结构链接回原始直觉
-## Output Format
+## 输出格式 (Output Format)
```
-🧬 Evolve Analysis
+🧬 演进分析 (Evolve Analysis)
==================
-Found 3 clusters ready for evolution:
+发现 3 个已准备好演进的聚类:
-## Cluster 1: Database Migration Workflow
-Instincts: new-table-migration, update-schema, regenerate-types
-Type: Command
-Confidence: 85% (based on 12 observations)
+## 聚类 1:数据库迁移工作流 (Database Migration Workflow)
+直觉 (Instincts): new-table-migration, update-schema, regenerate-types
+类型: 命令 (Command)
+置信度: 85% (基于 12 次观察)
-Would create: /new-table command
-Files:
+将创建: /new-table 命令
+文件:
- ~/.claude/homunculus/evolved/commands/new-table.md
-## Cluster 2: Functional Code Style
-Instincts: prefer-functional, use-immutable, avoid-classes, pure-functions
-Type: Skill
-Confidence: 78% (based on 8 observations)
+## 聚类 2:函数式代码风格 (Functional Code Style)
+直觉 (Instincts): prefer-functional, use-immutable, avoid-classes, pure-functions
+类型: 技能 (Skill)
+置信度: 78% (基于 8 次观察)
-Would create: functional-patterns skill
-Files:
+将创建: functional-patterns 技能 (Skill)
+文件:
- ~/.claude/homunculus/evolved/skills/functional-patterns.md
-## Cluster 3: Debugging Process
-Instincts: debug-check-logs, debug-isolate, debug-reproduce, debug-verify
-Type: Agent
-Confidence: 72% (based on 6 observations)
+## 聚类 3:调试流程 (Debugging Process)
+直觉 (Instincts): debug-check-logs, debug-isolate, debug-reproduce, debug-verify
+类型: 智能体 (Agent)
+置信度: 72% (基于 6 次观察)
-Would create: debugger agent
-Files:
+将创建: debugger 智能体 (Agent)
+文件:
- ~/.claude/homunculus/evolved/agents/debugger.md
---
-Run `/evolve --execute` to create these files.
+运行 `/evolve --execute` 来创建这些文件。
```
-## Flags
+## 参数标志 (Flags)
-- `--execute`: Actually create the evolved structures (default is preview)
-- `--dry-run`: Preview without creating
-- `--domain `: Only evolve instincts in specified domain
-- `--threshold `: Minimum instincts required to form cluster (default: 3)
-- `--type `: Only create specified type
+- `--execute`: 实际创建演进后的结构(默认为预览)
+- `--dry-run`: 预览而不创建
+- `--domain `: 仅演进指定领域(Domain)中的直觉
+- `--threshold `: 形成聚类所需的最小直觉数量(默认值:3)
+- `--type `: 仅创建指定类型
-## Generated File Format
+## 生成的文件格式 (Generated File Format)
-### Command
+### 命令 (Command)
```markdown
---
name: new-table
@@ -150,16 +150,16 @@ evolved_from:
- regenerate-types
---
-# New Table Command
+# New Table 命令
-[Generated content based on clustered instincts]
+[基于聚类直觉生成的正文内容]
-## Steps
+## 步骤
1. ...
2. ...
```
-### Skill
+### 技能 (Skill)
```markdown
---
name: functional-patterns
@@ -170,12 +170,12 @@ evolved_from:
- avoid-classes
---
-# Functional Patterns Skill
+# Functional Patterns 技能 (Skill)
-[Generated content based on clustered instincts]
+[基于聚类直觉生成的正文内容]
```
-### Agent
+### 智能体 (Agent)
```markdown
---
name: debugger
@@ -187,7 +187,7 @@ evolved_from:
- debug-reproduce
---
-# Debugger Agent
+# Debugger 智能体 (Agent)
-[Generated content based on clustered instincts]
+[基于聚类直觉生成的正文内容]
```
diff --git a/commands/go-build.md b/commands/go-build.md
index 5f90b53..aac2773 100644
--- a/commands/go-build.md
+++ b/commands/go-build.md
@@ -8,11 +8,11 @@ description: 增量修复 Go 构建错误、go vet 警告和 linter 问题。调
## 此命令的作用
-1. **运行诊断**:执行 `go build`、`go vet`、`staticcheck`
-2. **解析错误**:按文件分组并按严重程度排序
-3. **增量修复**:一次修复一个错误
-4. **验证每次修复**:每次更改后重新运行构建
-5. **报告摘要**:显示已修复的内容和剩余的问题
+1. **运行诊断**:执行 `go build`、`go vet`、`staticcheck`
+2. **解析错误**:按文件分组并按严重程度排序
+3. **增量修复**:一次修复一个错误
+4. **验证每次修复**:每次更改后重新运行构建
+5. **报告摘要**:显示已修复的内容和剩余的问题
## 何时使用
diff --git a/commands/instinct-export.md b/commands/instinct-export.md
index a93f4e2..7bfaa9e 100644
--- a/commands/instinct-export.md
+++ b/commands/instinct-export.md
@@ -1,38 +1,38 @@
---
name: instinct-export
-description: Export instincts for sharing with teammates or other projects
+description: 导出直觉(Instincts)以便与团队成员或其他项目共享
command: /instinct-export
---
-# Instinct Export Command
+# 直觉导出(Instinct Export)命令
-Exports instincts to a shareable format. Perfect for:
-- Sharing with teammates
-- Transferring to a new machine
-- Contributing to project conventions
+将直觉(Instincts)导出为可共享的格式。非常适用于:
+- 与团队成员共享
+- 迁移到新机器
+- 为项目规范(Conventions)贡献内容
-## Usage
+## 用法
```
-/instinct-export # Export all personal instincts
-/instinct-export --domain testing # Export only testing instincts
-/instinct-export --min-confidence 0.7 # Only export high-confidence instincts
+/instinct-export # 导出所有个人直觉
+/instinct-export --domain testing # 仅导出测试相关的直觉
+/instinct-export --min-confidence 0.7 # 仅导出高置信度的直觉
/instinct-export --output team-instincts.yaml
```
-## What to Do
+## 执行流程
-1. Read instincts from `~/.claude/homunculus/instincts/personal/`
-2. Filter based on flags
-3. Strip sensitive information:
- - Remove session IDs
- - Remove file paths (keep only patterns)
- - Remove timestamps older than "last week"
-4. Generate export file
+1. 从 `~/.claude/homunculus/instincts/personal/` 读取直觉
+2. 根据标志位(Flags)进行过滤
+3. 脱敏处理(剥离敏感信息):
+ - 移除会话 ID(Session IDs)
+ - 移除文件路径(仅保留模式匹配符 Pattern)
+ - 移除早于“上周”的时间戳
+4. 生成导出文件
-## Output Format
+## 输出格式
-Creates a YAML file:
+创建一个 YAML 文件:
```yaml
# Instincts Export
@@ -67,25 +67,25 @@ instincts:
observations: 6
```
-## Privacy Considerations
+## 隐私考量
-Exports include:
-- ✅ Trigger patterns
-- ✅ Actions
-- ✅ Confidence scores
-- ✅ Domains
-- ✅ Observation counts
+导出内容包括:
+- ✅ 触发模式(Trigger patterns)
+- ✅ 动作(Actions)
+- ✅ 置信度分数(Confidence scores)
+- ✅ 域(Domains)
+- ✅ 观察次数(Observation counts)
-Exports do NOT include:
-- ❌ Actual code snippets
-- ❌ File paths
-- ❌ Session transcripts
-- ❌ Personal identifiers
+导出内容 **不包括**:
+- ❌ 实际代码片段
+- ❌ 文件路径
+- ❌ 会话转录文本
+- ❌ 个人身份标识符
-## Flags
+## 标志位(Flags)
-- `--domain `: Export only specified domain
-- `--min-confidence `: Minimum confidence threshold (default: 0.3)
-- `--output `: Output file path (default: instincts-export-YYYYMMDD.yaml)
-- `--format `: Output format (default: yaml)
-- `--include-evidence`: Include evidence text (default: excluded)
+- `--domain `: 仅导出指定的域(Domain)
+- `--min-confidence `: 最低置信度阈值(默认值:0.3)
+- `--output `: 输出文件路径(默认值:instincts-export-YYYYMMDD.yaml)
+- `--format `: 输出格式(默认值:yaml)
+- `--include-evidence`: 包含证据文本(默认值:排除)
diff --git a/commands/instinct-import.md b/commands/instinct-import.md
index 0dea62b..8b1d27a 100644
--- a/commands/instinct-import.md
+++ b/commands/instinct-import.md
@@ -1,32 +1,32 @@
---
name: instinct-import
-description: Import instincts from teammates, Skill Creator, or other sources
+description: 从团队成员、技能创建者(Skill Creator)或其他来源导入直觉(Instincts)
command: true
---
-# Instinct Import Command
+# 直觉导入命令(Instinct Import Command)
-## Implementation
+## 实现
-Run the instinct CLI using the plugin root path:
+使用插件根路径运行直觉 CLI:
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/skills/continuous-learning-v2/scripts/instinct-cli.py" import [--dry-run] [--force] [--min-confidence 0.7]
```
-Or if `CLAUDE_PLUGIN_ROOT` is not set (manual installation):
+如果未设置 `CLAUDE_PLUGIN_ROOT`(手动安装):
```bash
python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py import
```
-Import instincts from:
-- Teammates' exports
-- Skill Creator (repo analysis)
-- Community collections
-- Previous machine backups
+可以从以下来源导入直觉:
+- 团队成员的导出文件
+- 技能创建者(Skill Creator)(仓库分析)
+- 社区集合
+- 以前的机器备份
-## Usage
+## 用法
```
/instinct-import team-instincts.yaml
@@ -34,109 +34,109 @@ Import instincts from:
/instinct-import --from-skill-creator acme/webapp
```
-## What to Do
+## 核心流程
-1. Fetch the instinct file (local path or URL)
-2. Parse and validate the format
-3. Check for duplicates with existing instincts
-4. Merge or add new instincts
-5. Save to `~/.claude/homunculus/instincts/inherited/`
+1. 获取直觉文件(本地路径或 URL)
+2. 解析并验证格式
+3. 检查与现有直觉是否重复
+4. 合并或添加新直觉
+5. 保存至 `~/.claude/homunculus/instincts/inherited/`
-## Import Process
+## 导入过程示例
```
-📥 Importing instincts from: team-instincts.yaml
+📥 正在从以下路径导入直觉:team-instincts.yaml
================================================
-Found 12 instincts to import.
+发现 12 条待导入的直觉。
-Analyzing conflicts...
+正在分析冲突...
-## New Instincts (8)
-These will be added:
- ✓ use-zod-validation (confidence: 0.7)
- ✓ prefer-named-exports (confidence: 0.65)
- ✓ test-async-functions (confidence: 0.8)
+## 新直觉 (8)
+以下内容将被添加:
+ ✓ use-zod-validation (置信度: 0.7)
+ ✓ prefer-named-exports (置信度: 0.65)
+ ✓ test-async-functions (置信度: 0.8)
...
-## Duplicate Instincts (3)
-Already have similar instincts:
+## 重复直觉 (3)
+已存在类似的直觉:
⚠️ prefer-functional-style
- Local: 0.8 confidence, 12 observations
- Import: 0.7 confidence
- → Keep local (higher confidence)
+ 本地: 0.8 置信度, 12 次观察
+ 导入: 0.7 置信度
+ → 保留本地版本(置信度更高)
⚠️ test-first-workflow
- Local: 0.75 confidence
- Import: 0.9 confidence
- → Update to import (higher confidence)
+ 本地: 0.75 置信度
+ 导入: 0.9 置信度
+ → 更新为导入版本(置信度更高)
-## Conflicting Instincts (1)
-These contradict local instincts:
+## 冲突直觉 (1)
+这些直觉与本地直觉冲突:
❌ use-classes-for-services
- Conflicts with: avoid-classes
- → Skip (requires manual resolution)
+ 冲突项:avoid-classes
+ → 跳过(需要手动解决)
---
-Import 8 new, update 1, skip 3?
+导入 8 条新直觉,更新 1 条,跳过 3 条?
```
-## Merge Strategies
+## 合并策略(Merge Strategies)
-### For Duplicates
-When importing an instinct that matches an existing one:
-- **Higher confidence wins**: Keep the one with higher confidence
-- **Merge evidence**: Combine observation counts
-- **Update timestamp**: Mark as recently validated
+### 处理重复项
+当导入的直觉与现有直觉匹配时:
+- **高置信度优先**:保留置信度较高的版本
+- **合并证据**:合并观察计数(observation counts)
+- **更新时间戳**:标记为最近已验证
-### For Conflicts
-When importing an instinct that contradicts an existing one:
-- **Skip by default**: Don't import conflicting instincts
-- **Flag for review**: Mark both as needing attention
-- **Manual resolution**: User decides which to keep
+### 处理冲突
+当导入的直觉与现有直觉相矛盾时:
+- **默认跳过**:不导入冲突的直觉
+- **标记待评审**:将两者都标记为需要关注
+- **手动解决**:由用户决定保留哪一个
-## Source Tracking
+## 来源追踪
-Imported instincts are marked with:
+导入的直觉会带有以下标记:
```yaml
source: "inherited"
imported_from: "team-instincts.yaml"
imported_at: "2025-01-22T10:30:00Z"
-original_source: "session-observation" # or "repo-analysis"
+original_source: "session-observation" # 或 "repo-analysis"
```
-## Skill Creator Integration
+## 技能创建者(Skill Creator)集成
-When importing from Skill Creator:
+从技能创建者导入时:
```
/instinct-import --from-skill-creator acme/webapp
```
-This fetches instincts generated from repo analysis:
-- Source: `repo-analysis`
-- Higher initial confidence (0.7+)
-- Linked to source repository
+这将获取通过仓库分析生成的直觉:
+- 来源:`repo-analysis`
+- 初始置信度较高 (0.7+)
+- 链接到源仓库
-## Flags
+## 参数选项(Flags)
-- `--dry-run`: Preview without importing
-- `--force`: Import even if conflicts exist
-- `--merge-strategy `: How to handle duplicates
-- `--from-skill-creator `: Import from Skill Creator analysis
-- `--min-confidence `: Only import instincts above threshold
+- `--dry-run`:预览而不执行导入
+- `--force`:即使存在冲突也执行导入
+- `--merge-strategy `:如何处理重复项
+- `--from-skill-creator `:从技能创建者分析中导入
+- `--min-confidence `:仅导入高于阈值的直觉
-## Output
+## 输出
-After import:
+导入完成后:
```
-✅ Import complete!
+✅ 导入完成!
-Added: 8 instincts
-Updated: 1 instinct
-Skipped: 3 instincts (2 duplicates, 1 conflict)
+添加:8 条直觉
+更新:1 条直觉
+跳过:3 条直觉 (2 条重复,1 条冲突)
-New instincts saved to: ~/.claude/homunculus/instincts/inherited/
+新直觉已保存至:~/.claude/homunculus/instincts/inherited/
-Run /instinct-status to see all instincts.
+运行 /instinct-status 以查看所有直觉。
```
diff --git a/commands/instinct-status.md b/commands/instinct-status.md
index 346ed47..2a84e36 100644
--- a/commands/instinct-status.md
+++ b/commands/instinct-status.md
@@ -1,28 +1,28 @@
---
name: instinct-status
-description: Show all learned instincts with their confidence levels
+description: 显示所有已学习的本能及其置信度
command: true
---
-# Instinct Status Command
+# 本能状态查询命令(Instinct Status Command)
-Shows all learned instincts with their confidence scores, grouped by domain.
+显示所有已学习的本能(Instincts)及其置信度分数,并按领域(Domain)进行分组。
-## Implementation
+## 实现方式
-Run the instinct CLI using the plugin root path:
+使用插件根路径运行本能 CLI:
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/skills/continuous-learning-v2/scripts/instinct-cli.py" status
```
-Or if `CLAUDE_PLUGIN_ROOT` is not set (manual installation), use:
+如果未设置 `CLAUDE_PLUGIN_ROOT`(手动安装),请使用:
```bash
python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py status
```
-## Usage
+## 用法
```
/instinct-status
@@ -30,57 +30,57 @@ python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py status
/instinct-status --low-confidence
```
-## What to Do
+## 执行逻辑
-1. Read all instinct files from `~/.claude/homunculus/instincts/personal/`
-2. Read inherited instincts from `~/.claude/homunculus/instincts/inherited/`
-3. Display them grouped by domain with confidence bars
+1. 从 `~/.claude/homunculus/instincts/personal/` 读取所有个人本能文件。
+2. 从 `~/.claude/homunculus/instincts/inherited/` 读取继承的本能。
+3. 按领域分组显示,并附带置信度进度条。
-## Output Format
+## 输出格式
```
-📊 Instinct Status
+📊 本能状态 (Instinct Status)
==================
-## Code Style (4 instincts)
+## 代码风格 (Code Style) (4 条本能)
### prefer-functional-style
-Trigger: when writing new functions
-Action: Use functional patterns over classes
-Confidence: ████████░░ 80%
-Source: session-observation | Last updated: 2025-01-22
+触发器 (Trigger):编写新函数时
+动作 (Action):优先使用函数式模式而非类
+置信度 (Confidence):████████░░ 80%
+来源 (Source):会话观察 (session-observation) | 最近更新:2025-01-22
### use-path-aliases
-Trigger: when importing modules
-Action: Use @/ path aliases instead of relative imports
-Confidence: ██████░░░░ 60%
-Source: repo-analysis (github.com/acme/webapp)
+触发器 (Trigger):导入模块时
+动作 (Action):使用 @/ 路径别名而非相对导入
+置信度 (Confidence):██████░░░░ 60%
+来源 (Source):仓库分析 (repo-analysis) (github.com/acme/webapp)
-## Testing (2 instincts)
+## 测试 (Testing) (2 条本能)
### test-first-workflow
-Trigger: when adding new functionality
-Action: Write test first, then implementation
-Confidence: █████████░ 90%
-Source: session-observation
+触发器 (Trigger):添加新功能时
+动作 (Action):先写测试,再写实现
+置信度 (Confidence):█████████░ 90%
+来源 (Source):会话观察 (session-observation)
-## Workflow (3 instincts)
+## 工作流 (Workflow) (3 条本能)
### grep-before-edit
-Trigger: when modifying code
-Action: Search with Grep, confirm with Read, then Edit
-Confidence: ███████░░░ 70%
-Source: session-observation
+触发器 (Trigger):修改代码时
+动作 (Action):先用 Grep 搜索,再用 Read 确认,最后 Edit 编辑
+置信度 (Confidence):███████░░░ 70%
+来源 (Source):会话观察 (session-observation)
---
-Total: 9 instincts (4 personal, 5 inherited)
-Observer: Running (last analysis: 5 min ago)
+总计:9 条本能 (4 条个人, 5 条继承)
+观察器 (Observer):运行中 (最近分析:5 分钟前)
```
-## Flags
+## 参数 (Flags)
-- `--domain `: Filter by domain (code-style, testing, git, etc.)
-- `--low-confidence`: Show only instincts with confidence < 0.5
-- `--high-confidence`: Show only instincts with confidence >= 0.7
-- `--source `: Filter by source (session-observation, repo-analysis, inherited)
-- `--json`: Output as JSON for programmatic use
+- `--domain `: 按领域筛选(如 code-style, testing, git 等)
+- `--low-confidence`: 仅显示置信度 < 0.5 的本能
+- `--high-confidence`: 仅显示置信度 >= 0.7 的本能
+- `--source `: 按来源筛选(session-observation, repo-analysis, inherited)
+- `--json`: 以 JSON 格式输出,便于程序化调用
diff --git a/commands/python-review.md b/commands/python-review.md
new file mode 100644
index 0000000..3a2b9a7
--- /dev/null
+++ b/commands/python-review.md
@@ -0,0 +1,297 @@
+---
+description: 针对 PEP 8 标准、类型提示、安全性及 Pythonic 惯用写法的 Python 代码全面审查。调用 python-reviewer 智能体(Agent)。
+---
+
+# Python 代码审查 (Python Code Review)
+
+此命令调用 **python-reviewer** 智能体(Agent),进行全面的 Python 专项代码审查。
+
+## 此命令的作用
+
+1. **识别 Python 变更**:通过 `git diff` 查找修改过的 `.py` 文件
+2. **运行静态分析**:执行 `ruff`、`mypy`、`pylint`、`black --check`
+3. **安全扫描**:检查 SQL 注入、命令注入、不安全的反序列化
+4. **类型安全审查**:分析类型提示(Type Hints)和 mypy 错误
+5. **Pythonic 代码检查**:验证代码是否符合 PEP 8 和 Python 最佳实践
+6. **生成报告**:按严重程度(Severity)对问题进行分类
+
+## 适用场景
+
+在以下情况下使用 `/python-review`:
+- 编写或修改 Python 代码后
+- 提交 Python 变更前
+- 审查包含 Python 代码的拉取请求(Pull Requests)
+- 接入新的 Python 代码库时
+- 学习 Pythonic 模式和惯用法时
+
+## 审查类别
+
+### 严重 (CRITICAL) (必须修复)
+- SQL/命令注入漏洞
+- 不安全的 eval/exec 使用
+- Pickle 不安全的反序列化
+- 硬编码凭据
+- YAML 不安全的加载 (unsafe load)
+- 隐藏错误的裸 except 子句
+
+### 高 (HIGH) (应该修复)
+- 公共函数缺失类型提示
+- 可变默认参数 (Mutable default arguments)
+- 静默吞掉异常
+- 未对资源使用上下文管理器 (Context Managers)
+- 使用 C 风格循环而非推导式 (Comprehensions)
+- 使用 type() 而非 isinstance()
+- 无锁状态下的竞态条件
+
+### 中 (MEDIUM) (建议考虑)
+- 违反 PEP 8 格式规范
+- 公共函数缺失文档字符串 (Docstrings)
+- 使用 print 语句而非日志 (Logging)
+- 低效的字符串操作
+- 无命名常量的魔术数字 (Magic numbers)
+- 未使用 f-strings 进行格式化
+- 不必要的列表创建
+
+## 运行的自动化检查
+
+```bash
+# 类型检查
+mypy .
+
+# 代码检查与格式化
+ruff check .
+black --check .
+isort --check-only .
+
+# 安全扫描
+bandit -r .
+
+# 依赖审计
+pip-audit
+safety check
+
+# 测试
+pytest --cov=app --cov-report=term-missing
+```
+
+## 示例用法
+
+```text
+User: /python-review
+
+Agent:
+# Python 代码审查报告
+
+## 已审查文件
+- app/routes/user.py (已修改)
+- app/services/auth.py (已修改)
+
+## 静态分析结果
+✓ ruff: 无问题
+✓ mypy: 无错误
+⚠️ black: 2 个文件需要重新格式化
+✓ bandit: 无安全问题
+
+## 发现的问题
+
+[CRITICAL] SQL 注入漏洞
+文件: app/routes/user.py:42
+问题: 用户输入直接插入到 SQL 查询中
+```python
+query = f"SELECT * FROM users WHERE id = {user_id}" # 不良做法
+```
+修复: 使用参数化查询
+```python
+query = "SELECT * FROM users WHERE id = %s" # 推荐做法
+cursor.execute(query, (user_id,))
+```
+
+[HIGH] 可变默认参数
+文件: app/services/auth.py:18
+问题: 可变默认参数会导致状态共享
+```python
+def process_items(items=[]): # 不良做法
+ items.append("new")
+ return items
+```
+修复: 使用 None 作为默认值
+```python
+def process_items(items=None): # 推荐做法
+ if items is None:
+ items = []
+ items.append("new")
+ return items
+```
+
+[MEDIUM] 缺失类型提示
+文件: app/services/auth.py:25
+问题: 公共函数没有类型注解
+```python
+def get_user(user_id): # 不良做法
+ return db.find(user_id)
+```
+修复: 添加类型提示
+```python
+def get_user(user_id: str) -> Optional[User]: # 推荐做法
+ return db.find(user_id)
+```
+
+[MEDIUM] 未使用上下文管理器
+文件: app/routes/user.py:55
+问题: 异常发生时文件未关闭
+```python
+f = open("config.json") # 不良做法
+data = f.read()
+f.close()
+```
+修复: 使用上下文管理器
+```python
+with open("config.json") as f: # 推荐做法
+ data = f.read()
+```
+
+## 摘要
+- 严重 (CRITICAL): 1
+- 高 (HIGH): 1
+- 中 (MEDIUM): 2
+
+建议: ❌ 在修复严重问题前阻止合并
+
+## 需要格式化
+运行: `black app/routes/user.py app/services/auth.py`
+```
+
+## 批准标准
+
+| 状态 | 条件 |
+|--------|-----------|
+| ✅ 批准 (Approve) | 无“严重”或“高”级别问题 |
+| ⚠️ 警告 (Warning) | 仅存在“中”级别问题(谨慎合并) |
+| ❌ 阻止 (Block) | 发现“严重”或“高”级别问题 |
+
+## 与其他命令的集成
+
+- 先使用 `/python-test` 确保测试通过
+- 使用 `/code-review` 处理非 Python 专项的关注点
+- 在提交(commit)前使用 `/python-review`
+- 如果静态分析工具报错,使用 `/build-fix`
+
+## 框架专项审查
+
+### Django 项目
+审查者会检查:
+- N+1 查询问题(使用 `select_related` 和 `prefetch_related`)
+- 模型变更缺失迁移文件
+- 在 ORM 可用的情况下使用原生 SQL
+- 多步操作缺失 `transaction.atomic()`
+
+### FastAPI 项目
+审查者会检查:
+- CORS 配置错误
+- 用于请求校验的 Pydantic 模型
+- 响应模型的正确性
+- 恰当的 async/await 使用
+- 依赖注入模式
+
+### Flask 项目
+审查者会检查:
+- 上下文管理(应用上下文、请求上下文)
+- 恰当的错误处理
+- 蓝图 (Blueprint) 组织结构
+- 配置管理
+
+## 相关
+
+- 智能体 (Agent): `agents/python-reviewer.md`
+- 技能 (Skills): `skills/python-patterns/`, `skills/python-testing/`
+
+## 常见修复方案
+
+### 添加类型提示
+```python
+# 修复前
+def calculate(x, y):
+ return x + y
+
+# 修复后
+from typing import Union
+
+def calculate(x: Union[int, float], y: Union[int, float]) -> Union[int, float]:
+ return x + y
+```
+
+### 使用上下文管理器
+```python
+# 修复前
+f = open("file.txt")
+data = f.read()
+f.close()
+
+# 修复后
+with open("file.txt") as f:
+ data = f.read()
+```
+
+### 使用列表推导式
+```python
+# 修复前
+result = []
+for item in items:
+ if item.active:
+ result.append(item.name)
+
+# 修复后
+result = [item.name for item in items if item.active]
+```
+
+### 修复可变默认参数
+```python
+# 修复前
+def append(value, items=[]):
+ items.append(value)
+ return items
+
+# 修复后
+def append(value, items=None):
+ if items is None:
+ items = []
+ items.append(value)
+ return items
+```
+
+### 使用 f-strings (Python 3.6+)
+```python
+# 修复前
+name = "Alice"
+greeting = "Hello, " + name + "!"
+greeting2 = "Hello, {}".format(name)
+
+# 修复后
+greeting = f"Hello, {name}!"
+```
+
+### 修复循环中的字符串拼接
+```python
+# 修复前
+result = ""
+for item in items:
+ result += str(item)
+
+# 修复后
+result = "".join(str(item) for item in items)
+```
+
+## Python 版本兼容性
+
+审查者会提示代码何时使用了较新 Python 版本的特性:
+
+| 特性 | 最低 Python 版本 |
+|---------|----------------|
+| 类型提示 (Type hints) | 3.5+ |
+| f-strings | 3.6+ |
+| 海象运算符 (Walrus operator `:=`) | 3.8+ |
+| 仅限位置参数 (Position-only parameters) | 3.8+ |
+| 匹配语句 (Match statements) | 3.10+ |
+| 类型联合 (Type unions `x | None`) | 3.10+ |
+
+请确保项目的 `pyproject.toml` 或 `setup.py` 指定了正确的最低 Python 版本。
diff --git a/commands/skill-create.md b/commands/skill-create.md
index dcf1df7..3227180 100644
--- a/commands/skill-create.md
+++ b/commands/skill-create.md
@@ -1,59 +1,59 @@
---
name: skill-create
-description: Analyze local git history to extract coding patterns and generate SKILL.md files. Local version of the Skill Creator GitHub App.
+description: 分析本地 Git 历史以提取编码模式并生成 SKILL.md 文件。Skill Creator GitHub App 的本地版本。
allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"]
---
-# /skill-create - Local Skill Generation
+# /skill-create - 本地技能生成(Local Skill Generation)
-Analyze your repository's git history to extract coding patterns and generate SKILL.md files that teach Claude your team's practices.
+分析你仓库的 Git 历史记录以提取编码模式,并生成 SKILL.md 文件,以便让 Claude 学习你团队的工程实践。
-## Usage
+## 用法(Usage)
```bash
-/skill-create # Analyze current repo
-/skill-create --commits 100 # Analyze last 100 commits
-/skill-create --output ./skills # Custom output directory
-/skill-create --instincts # Also generate instincts for continuous-learning-v2
+/skill-create # 分析当前仓库
+/skill-create --commits 100 # 分析最近 100 条提交
+/skill-create --output ./skills # 指定自定义输出目录
+/skill-create --instincts # 同时为 continuous-learning-v2 生成直觉(instincts)
```
-## What It Does
+## 功能说明(What It Does)
-1. **Parses Git History** - Analyzes commits, file changes, and patterns
-2. **Detects Patterns** - Identifies recurring workflows and conventions
-3. **Generates SKILL.md** - Creates valid Claude Code skill files
-4. **Optionally Creates Instincts** - For the continuous-learning-v2 system
+1. **解析 Git 历史** - 分析提交(commits)、文件变更和模式。
+2. **检测模式** - 识别循环出现的工作流(Workflow)和约定。
+3. **生成 SKILL.md** - 创建有效的 Claude Code 技能(Skill)文件。
+4. **可选生成直觉(Instincts)** - 用于 continuous-learning-v2 系统。
-## Analysis Steps
+## 分析步骤(Analysis Steps)
-### Step 1: Gather Git Data
+### 第 1 步:收集 Git 数据
```bash
-# Get recent commits with file changes
+# 获取带有文件变更的近期提交
git log --oneline -n ${COMMITS:-200} --name-only --pretty=format:"%H|%s|%ad" --date=short
-# Get commit frequency by file
+# 获取按文件统计的提交频率
git log --oneline -n 200 --name-only | grep -v "^$" | grep -v "^[a-f0-9]" | sort | uniq -c | sort -rn | head -20
-# Get commit message patterns
+# 获取提交信息模式
git log --oneline -n 200 | cut -d' ' -f2- | head -50
```
-### Step 2: Detect Patterns
+### 第 2 步:检测模式
-Look for these pattern types:
+寻找以下模式类型:
-| Pattern | Detection Method |
+| 模式 (Pattern) | 检测方法 (Detection Method) |
|---------|-----------------|
-| **Commit conventions** | Regex on commit messages (feat:, fix:, chore:) |
-| **File co-changes** | Files that always change together |
-| **Workflow sequences** | Repeated file change patterns |
-| **Architecture** | Folder structure and naming conventions |
-| **Testing patterns** | Test file locations, naming, coverage |
+| **提交规范 (Commit conventions)** | 对提交信息使用正则匹配 (feat:, fix:, chore:) |
+| **文件关联变更 (File co-changes)** | 总是同时发生变化的文件 |
+| **工作流序列 (Workflow sequences)** | 重复出现的文件变更模式 |
+| **架构 (Architecture)** | 文件夹结构和命名规范 |
+| **测试模式 (Testing patterns)** | 测试文件位置、命名、覆盖率 |
-### Step 3: Generate SKILL.md
+### 第 3 步:生成 SKILL.md
-Output format:
+输出格式:
```markdown
---
@@ -64,24 +64,24 @@ source: local-git-analysis
analyzed_commits: {count}
---
-# {Repo Name} Patterns
+# {Repo Name} 模式
-## Commit Conventions
-{detected commit message patterns}
+## 提交规范
+{检测到的提交信息模式}
-## Code Architecture
-{detected folder structure and organization}
+## 代码架构
+{检测到的文件夹结构和组织方式}
-## Workflows
-{detected repeating file change patterns}
+## 工作流
+{检测到的重复文件变更模式}
-## Testing Patterns
-{detected test conventions}
+## 测试模式
+{检测到的测试约定}
```
-### Step 4: Generate Instincts (if --instincts)
+### 第 4 步:生成直觉 (如果使用了 --instincts)
-For continuous-learning-v2 integration:
+用于 continuous-learning-v2 集成:
```yaml
---
@@ -92,19 +92,19 @@ domain: git
source: local-repo-analysis
---
-# Use Conventional Commits
+# 使用约定式提交 (Conventional Commits)
-## Action
-Prefix commits with: feat:, fix:, chore:, docs:, test:, refactor:
+## 操作 (Action)
+在提交信息前添加前缀:feat:, fix:, chore:, docs:, test:, refactor:
-## Evidence
-- Analyzed {n} commits
-- {percentage}% follow conventional commit format
+## 证据 (Evidence)
+- 已分析 {n} 条提交
+- {percentage}% 遵循约定式提交格式
```
-## Example Output
+## 输出示例
-Running `/skill-create` on a TypeScript project might produce:
+在 TypeScript 项目上运行 `/skill-create` 可能会产生:
```markdown
---
@@ -115,60 +115,60 @@ source: local-git-analysis
analyzed_commits: 150
---
-# My App Patterns
+# My App 模式
-## Commit Conventions
+## 提交规范 (Commit Conventions)
-This project uses **conventional commits**:
-- `feat:` - New features
-- `fix:` - Bug fixes
-- `chore:` - Maintenance tasks
-- `docs:` - Documentation updates
+该项目使用 **约定式提交 (conventional commits)**:
+- `feat:` - 新功能
+- `fix:` - 错误修复
+- `chore:` - 维护任务
+- `docs:` - 文档更新
-## Code Architecture
+## 代码架构 (Code Architecture)
```
src/
-├── components/ # React components (PascalCase.tsx)
-├── hooks/ # Custom hooks (use*.ts)
-├── utils/ # Utility functions
-├── types/ # TypeScript type definitions
-└── services/ # API and external services
+├── components/ # React 组件 (PascalCase.tsx)
+├── hooks/ # 自定义 Hooks (use*.ts)
+├── utils/ # 工具函数
+├── types/ # TypeScript 类型定义
+└── services/ # API 和外部服务
```
-## Workflows
+## 工作流 (Workflows)
-### Adding a New Component
-1. Create `src/components/ComponentName.tsx`
-2. Add tests in `src/components/__tests__/ComponentName.test.tsx`
-3. Export from `src/components/index.ts`
+### 添加新组件
+1. 创建 `src/components/ComponentName.tsx`
+2. 在 `src/components/__tests__/ComponentName.test.tsx` 中添加测试
+3. 从 `src/components/index.ts` 导出
-### Database Migration
-1. Modify `src/db/schema.ts`
-2. Run `pnpm db:generate`
-3. Run `pnpm db:migrate`
+### 数据库迁移
+1. 修改 `src/db/schema.ts`
+2. 运行 `pnpm db:generate`
+3. 运行 `pnpm db:migrate`
-## Testing Patterns
+## 测试模式 (Testing patterns)
-- Test files: `__tests__/` directories or `.test.ts` suffix
-- Coverage target: 80%+
-- Framework: Vitest
+- 测试文件:`__tests__/` 目录或 `.test.ts` 后缀
+- 覆盖率目标:80%+
+- 框架:Vitest
```
-## GitHub App Integration
+## GitHub App 集成
-For advanced features (10k+ commits, team sharing, auto-PRs), use the [Skill Creator GitHub App](https://github.com/apps/skill-creator):
+对于高级功能(1万+ 提交、团队共享、自动 PR),请使用 [Skill Creator GitHub App](https://github.com/apps/skill-creator):
-- Install: [github.com/apps/skill-creator](https://github.com/apps/skill-creator)
-- Comment `/skill-creator analyze` on any issue
-- Receives PR with generated skills
+- 安装:[github.com/apps/skill-creator](https://github.com/apps/skill-creator)
+- 在任何 Issue 上评论 `/skill-creator analyze`
+- 接收包含生成的技能的 PR
-## Related Commands
+## 相关命令
-- `/instinct-import` - Import generated instincts
-- `/instinct-status` - View learned instincts
-- `/evolve` - Cluster instincts into skills/agents
+- `/instinct-import` - 导入生成的直觉
+- `/instinct-status` - 查看已学习的直觉
+- `/evolve` - 将直觉聚类为技能/智能体
---
-*Part of [Everything Claude Code](https://github.com/affaan-m/everything-claude-code)*
+*属于 [Everything Claude Code](https://github.com/affaan-m/everything-claude-code) 的一部分*
diff --git a/commands/tdd.md b/commands/tdd.md
index db9f10e..9f3d037 100644
--- a/commands/tdd.md
+++ b/commands/tdd.md
@@ -8,11 +8,11 @@ description: Enforce test-driven development workflow. Scaffold interfaces, gene
## 此命令的作用
-1. **搭建接口(Scaffold Interfaces)** - 首先定义类型/接口
-2. **先生成测试** - 编写失败的测试(红/RED)
-3. **编写最小化实现代码** - 只编写刚好能通过测试的代码(绿/GREEN)
-4. **重构(Refactor)** - 在保持测试通过的前提下优化代码(重构/REFACTOR)
-5. **验证覆盖率** - 确保测试覆盖率达到 80% 以上
+1. **搭建接口(Scaffold Interfaces)** - 首先定义类型/接口
+2. **先生成测试** - 编写失败的测试(红/RED)
+3. **编写最小化实现代码** - 只编写刚好能通过测试的代码(绿/GREEN)
+4. **重构(Refactor)** - 在保持测试通过的前提下优化代码(重构/REFACTOR)
+5. **验证覆盖率** - 确保测试覆盖率达到 80% 以上
## 适用场景
@@ -27,13 +27,13 @@ description: Enforce test-driven development workflow. Scaffold interfaces, gene
tdd-guide 智能体将:
-1. 为输入/输出**定义接口**
-2. **编写会失败(FAIL)的测试**(因为代码尚未存在)
-3. **运行测试**并验证它们因预期的原因而失败
-4. **编写最小化实现**以使测试通过
-5. **运行测试**并验证它们通过
-6. 在保持测试通过的前提下**重构**代码
-7. **检查覆盖率**,如果低于 80% 则添加更多测试
+1. 为输入/输出**定义接口**
+2. **编写会失败(FAIL)的测试**(因为代码尚未存在)
+3. **运行测试**并验证它们因预期的原因而失败
+4. **编写最小化实现**以使测试通过
+5. **运行测试**并验证它们通过
+6. 在保持测试通过的前提下**重构**代码
+7. **检查覆盖率**,如果低于 80% 则添加更多测试
## TDD 循环
diff --git a/docs/zh-TW/README.md b/docs/zh-TW/README.md
index c4cea37..ad0adb6 100644
--- a/docs/zh-TW/README.md
+++ b/docs/zh-TW/README.md
@@ -7,21 +7,33 @@


-**来自 Anthropic 黑客松冠军的完整 Claude Code 配置集。**
+---
-经过 10 个月以上密集日常使用、打造真实产品所淬炼出的生产就绪智能体(Agents)、技能(Skills)、钩子(Hooks)、指令(Commands)、规则(Rules)和 MCP 配置。
+
+
+**🌐 Language / 语言 / 語言**
+
+[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](README.md)
+
+
+
+---
+
+**來自 Anthropic 黑客松冠軍的完整 Claude Code 設定集合。**
+
+經過 10 個月以上密集日常使用、打造真實產品所淬煉出的生產就緒代理程式、技能、鉤子、指令、規則和 MCP 設定。
---
## 指南
-本仓库仅包含原始代码。指南会解释所有内容。
+本儲存庫僅包含原始程式碼。指南會解釋所有內容。
-
+
@@ -31,50 +43,90 @@
-简明指南 配置、基础、理念。请先阅读此指南。
-完整指南 Token 优化、记忆持久化、评估、并行处理。
+簡明指南 設定、基礎、理念。請先閱讀此指南。
+完整指南 權杖最佳化、記憶持久化、評估、平行處理。
-| 主题 | 学习内容 |
+| 主題 | 學習內容 |
|------|----------|
-| Token 优化 | 模型选择、系统提示精简、后台进程 |
-| 记忆持久化 | 自动跨会话(Session)保存/加载上下文的钩子(Hooks) |
-| 持续学习 | 从会话中自动提取模式并转化为可重用技能(Skills) |
-| 验证循环 | 检查点 vs 持续评估、评分器类型、pass@k 指标 |
-| 并行处理 | Git worktrees、串联方法、何时扩展实例 |
-| 子智能体协调 | 上下文问题、渐进式检索模式 |
+| 權杖最佳化 | 模型選擇、系統提示精簡、背景程序 |
+| 記憶持久化 | 自動跨工作階段儲存/載入上下文的鉤子 |
+| 持續學習 | 從工作階段自動擷取模式並轉化為可重用技能 |
+| 驗證迴圈 | 檢查點 vs 持續評估、評分器類型、pass@k 指標 |
+| 平行處理 | Git worktrees、串聯方法、何時擴展實例 |
+| 子代理程式協調 | 上下文問題、漸進式檢索模式 |
---
-## 跨平台支持
+## 🚀 快速開始
-此插件现已完整支持 **Windows、macOS 和 Linux**。所有钩子和脚本已使用 Node.js 重写以获得最佳兼容性。
+在 2 分鐘內快速上手:
-### 包管理器检测
-
-插件会自动检测您偏好的包管理器(npm、pnpm、yarn 或 bun),优先级如下:
-
-1. **环境变量**:`CLAUDE_PACKAGE_MANAGER`
-2. **项目配置**:`.claude/package-manager.json`
-3. **package.json**:`packageManager` 字段
-4. **锁文件**:从 package-lock.json、yarn.lock、pnpm-lock.yaml 或 bun.lockb 检测
-5. **全局配置**:`~/.claude/package-manager.json`
-6. **备选方案**:第一个可用的包管理器
-
-设置您偏好的包管理器:
+### 第一步:安裝外掛程式
```bash
-# 通过环境变量
+# 新增市集
+/plugin marketplace add affaan-m/everything-claude-code
+
+# 安裝外掛程式
+/plugin install everything-claude-code@everything-claude-code
+```
+
+### 第二步:安裝規則(必需)
+
+> ⚠️ **重要提示:** Claude Code 外掛程式無法自動分發 `rules`,需要手動安裝:
+
+```bash
+# 首先複製儲存庫
+git clone https://github.com/affaan-m/everything-claude-code.git
+
+# 複製規則(應用於所有專案)
+cp -r everything-claude-code/rules/* ~/.claude/rules/
+```
+
+### 第三步:開始使用
+
+```bash
+# 嘗試一個指令
+/plan "新增使用者認證"
+
+# 查看可用指令
+/plugin list everything-claude-code@everything-claude-code
+```
+
+✨ **完成!** 您現在使用 15+ 代理程式、30+ 技能和 20+ 指令。
+
+---
+
+## 🌐 跨平台支援
+
+此外掛程式現已完整支援 **Windows、macOS 和 Linux**。所有鉤子和腳本已使用 Node.js 重寫以獲得最佳相容性。
+
+### 套件管理器偵測
+
+外掛程式會自動偵測您偏好的套件管理器(npm、pnpm、yarn 或 bun),優先順序如下:
+
+1. **環境變數**:`CLAUDE_PACKAGE_MANAGER`
+2. **專案設定**:`.claude/package-manager.json`
+3. **package.json**:`packageManager` 欄位
+4. **鎖定檔案**:從 package-lock.json、yarn.lock、pnpm-lock.yaml 或 bun.lockb 偵測
+5. **全域設定**:`~/.claude/package-manager.json`
+6. **備援方案**:第一個可用的套件管理器
+
+設定您偏好的套件管理器:
+
+```bash
+# 透過環境變數
export CLAUDE_PACKAGE_MANAGER=pnpm
-# 通过全局配置
+# 透過全域設定
node scripts/setup-package-manager.js --global pnpm
-# 通过项目配置
+# 透過專案設定
node scripts/setup-package-manager.js --project bun
-# 检测当前配置
+# 偵測目前設定
node scripts/setup-package-manager.js --detect
```
@@ -82,143 +134,143 @@ node scripts/setup-package-manager.js --detect
---
-## 内容概览
+## 📦 內容概覽
-本仓库是一个 **Claude Code 插件** - 可直接安装或手动复制组件。
+本儲存庫是一個 **Claude Code 外掛程式** - 可直接安裝或手動複製元件。
```
everything-claude-code/
-|-- .claude-plugin/ # 插件和市场清单
-| |-- plugin.json # 插件元数据和组件路径
-| |-- marketplace.json # 用于 /plugin marketplace add 的市场目录
+|-- .claude-plugin/ # 外掛程式和市集清單
+| |-- plugin.json # 外掛程式中繼資料和元件路徑
+| |-- marketplace.json # 用於 /plugin marketplace add 的市集目錄
|
-|-- agents/ # 用于委派任务的专门子智能体(Agents)
-| |-- planner.md # 功能实现规划
-| |-- architect.md # 系统设计决策
-| |-- tdd-guide.md # 测试驱动开发
-| |-- code-reviewer.md # 质量与安全审查
-| |-- security-reviewer.md # 漏洞分析
+|-- agents/ # 用於委派任務的專門子代理程式
+| |-- planner.md # 功能實作規劃
+| |-- architect.md # 系統設計決策
+| |-- tdd-guide.md # 測試驅動開發
+| |-- code-reviewer.md # 品質與安全審查
+| |-- security-reviewer.md # 弱點分析
| |-- build-error-resolver.md
-| |-- e2e-runner.md # Playwright E2E 测试
-| |-- refactor-cleaner.md # 无用代码清理
-| |-- doc-updater.md # 文档同步
-| |-- go-reviewer.md # Go 代码审查(新增)
-| |-- go-build-resolver.md # Go 构建错误解决(新增)
+| |-- e2e-runner.md # Playwright E2E 測試
+| |-- refactor-cleaner.md # 無用程式碼清理
+| |-- doc-updater.md # 文件同步
+| |-- go-reviewer.md # Go 程式碼審查(新增)
+| |-- go-build-resolver.md # Go 建置錯誤解決(新增)
|
-|-- skills/ # 工作流(Workflow)定义和领域知识
-| |-- coding-standards/ # 编程语言最佳实践
-| |-- backend-patterns/ # API、数据库、缓存模式
+|-- skills/ # 工作流程定義和領域知識
+| |-- coding-standards/ # 程式語言最佳實務
+| |-- backend-patterns/ # API、資料庫、快取模式
| |-- frontend-patterns/ # React、Next.js 模式
-| |-- continuous-learning/ # 从会话中自动提取模式(完整指南)
-| |-- continuous-learning-v2/ # 基于本能的学习与信心评分
-| |-- iterative-retrieval/ # 子代理的渐进式上下文精炼
-| |-- strategic-compact/ # 手动压缩建议(完整指南)
-| |-- tdd-workflow/ # TDD 方法论
-| |-- security-review/ # 安全性检查清单
-| |-- eval-harness/ # 验证循环评估(完整指南)
-| |-- verification-loop/ # 持续验证(完整指南)
-| |-- golang-patterns/ # Go 惯用法和最佳实践(新增)
-| |-- golang-testing/ # Go 测试模式、TDD、基准测试(新增)
+| |-- continuous-learning/ # 從工作階段自動擷取模式(完整指南)
+| |-- continuous-learning-v2/ # 基於本能的學習與信心評分
+| |-- iterative-retrieval/ # 子代理程式的漸進式上下文精煉
+| |-- strategic-compact/ # 手動壓縮建議(完整指南)
+| |-- tdd-workflow/ # TDD 方法論
+| |-- security-review/ # 安全性檢查清單
+| |-- eval-harness/ # 驗證迴圈評估(完整指南)
+| |-- verification-loop/ # 持續驗證(完整指南)
+| |-- golang-patterns/ # Go 慣用語法和最佳實務(新增)
+| |-- golang-testing/ # Go 測試模式、TDD、基準測試(新增)
|
-|-- commands/ # 快速执行的斜杠指令(Commands)
-| |-- tdd.md # /tdd - 测试驱动开发
-| |-- plan.md # /plan - 实现规划
-| |-- e2e.md # /e2e - E2E 测试生成
-| |-- code-review.md # /code-review - 质量审查
-| |-- build-fix.md # /build-fix - 修复构建错误
-| |-- refactor-clean.md # /refactor-clean - 移除无用代码
-| |-- learn.md # /learn - 会话中提取模式(完整指南)
-| |-- checkpoint.md # /checkpoint - 保存验证状态(完整指南)
-| |-- verify.md # /verify - 执行验证循环(完整指南)
-| |-- setup-pm.md # /setup-pm - 设置包管理器
-| |-- go-review.md # /go-review - Go 代码审查(新增)
-| |-- go-test.md # /go-test - Go TDD 工作流(新增)
-| |-- go-build.md # /go-build - 修复 Go 构建错误(新增)
+|-- commands/ # 快速執行的斜線指令
+| |-- tdd.md # /tdd - 測試驅動開發
+| |-- plan.md # /plan - 實作規劃
+| |-- e2e.md # /e2e - E2E 測試生成
+| |-- code-review.md # /code-review - 品質審查
+| |-- build-fix.md # /build-fix - 修復建置錯誤
+| |-- refactor-clean.md # /refactor-clean - 移除無用程式碼
+| |-- learn.md # /learn - 工作階段中擷取模式(完整指南)
+| |-- checkpoint.md # /checkpoint - 儲存驗證狀態(完整指南)
+| |-- verify.md # /verify - 執行驗證迴圈(完整指南)
+| |-- setup-pm.md # /setup-pm - 設定套件管理器
+| |-- go-review.md # /go-review - Go 程式碼審查(新增)
+| |-- go-test.md # /go-test - Go TDD 工作流程(新增)
+| |-- go-build.md # /go-build - 修復 Go 建置錯誤(新增)
|
-|-- rules/ # 必须遵守的准则(Rules)(复制到 ~/.claude/rules/)
-| |-- security.md # 强制性安全检查
-| |-- coding-style.md # 不变性、文件组织
-| |-- testing.md # TDD、80% 覆盖率要求
+|-- rules/ # 必須遵守的準則(複製到 ~/.claude/rules/)
+| |-- security.md # 強制性安全檢查
+| |-- coding-style.md # 不可變性、檔案組織
+| |-- testing.md # TDD、80% 覆蓋率要求
| |-- git-workflow.md # 提交格式、PR 流程
-| |-- agents.md # 何时委派给子智能体
-| |-- performance.md # 模型选择、上下文管理
+| |-- agents.md # 何時委派給子代理程式
+| |-- performance.md # 模型選擇、上下文管理
|
-|-- hooks/ # 基于触发器的自动化钩子(Hooks)
-| |-- hooks.json # 所有钩子配置(PreToolUse、PostToolUse、Stop 等)
-| |-- memory-persistence/ # 会话生命周期钩子(完整指南)
-| |-- strategic-compact/ # 压缩建议(完整指南)
+|-- hooks/ # 基於觸發器的自動化
+| |-- hooks.json # 所有鉤子設定(PreToolUse、PostToolUse、Stop 等)
+| |-- memory-persistence/ # 工作階段生命週期鉤子(完整指南)
+| |-- strategic-compact/ # 壓縮建議(完整指南)
|
-|-- scripts/ # 跨平台 Node.js 脚本(新增)
-| |-- lib/ # 共享工具
-| | |-- utils.js # 跨平台文件/路径/系统工具
-| | |-- package-manager.js # 包管理器检测与选择
-| |-- hooks/ # 钩子实现
-| | |-- session-start.js # 会话开始时加载上下文
-| | |-- session-end.js # 会话结束时保存状态
-| | |-- pre-compact.js # 压缩前状态保存
-| | |-- suggest-compact.js # 策略性压缩建议
-| | |-- evaluate-session.js # 从会话中提取模式
-| |-- setup-package-manager.js # 交互式包管理器设置
+|-- scripts/ # 跨平台 Node.js 腳本(新增)
+| |-- lib/ # 共用工具
+| | |-- utils.js # 跨平台檔案/路徑/系統工具
+| | |-- package-manager.js # 套件管理器偵測與選擇
+| |-- hooks/ # 鉤子實作
+| | |-- session-start.js # 工作階段開始時載入上下文
+| | |-- session-end.js # 工作階段結束時儲存狀態
+| | |-- pre-compact.js # 壓縮前狀態儲存
+| | |-- suggest-compact.js # 策略性壓縮建議
+| | |-- evaluate-session.js # 從工作階段擷取模式
+| |-- setup-package-manager.js # 互動式套件管理器設定
|
-|-- tests/ # 测试套件(新增)
-| |-- lib/ # 库测试
-| |-- hooks/ # 钩子测试
-| |-- run-all.js # 执行所有测试
+|-- tests/ # 測試套件(新增)
+| |-- lib/ # 函式庫測試
+| |-- hooks/ # 鉤子測試
+| |-- run-all.js # 執行所有測試
|
-|-- contexts/ # 动态系统提示词(Prompt)注入上下文(完整指南)
-| |-- dev.md # 开发模式上下文
-| |-- review.md # 代码审查模式上下文
+|-- contexts/ # 動態系統提示注入上下文(完整指南)
+| |-- dev.md # 開發模式上下文
+| |-- review.md # 程式碼審查模式上下文
| |-- research.md # 研究/探索模式上下文
|
-|-- examples/ # 示例配置和会话
-| |-- CLAUDE.md # 项目级配置示例
-| |-- user-CLAUDE.md # 用户级配置示例
+|-- examples/ # 範例設定和工作階段
+| |-- CLAUDE.md # 專案層級設定範例
+| |-- user-CLAUDE.md # 使用者層級設定範例
|
-|-- mcp-configs/ # MCP 服务器配置
+|-- mcp-configs/ # MCP 伺服器設定
| |-- mcp-servers.json # GitHub、Supabase、Vercel、Railway 等
|
-|-- marketplace.json # 自托管市场配置(用于 /plugin marketplace add)
+|-- marketplace.json # 自託管市集設定(用於 /plugin marketplace add)
```
---
-## 生态系统工具
+## 🛠️ 生態系統工具
-### ecc.tools - 技能生成器
+### ecc.tools - 技能建立器
-从您的仓库自动生成 Claude Code 技能(Skills)。
+從您的儲存庫自動生成 Claude Code 技能。
-[安装 GitHub App](https://github.com/apps/skill-creator) | [ecc.tools](https://ecc.tools)
+[安裝 GitHub App](https://github.com/apps/skill-creator) | [ecc.tools](https://ecc.tools)
-分析您的仓库并创建:
-- **SKILL.md 文件** - 可直接用于 Claude Code 的技能
-- **本能集合** - 用于 continuous-learning-v2
-- **模式提取** - 从您的提交历史学习
+分析您的儲存庫並建立:
+- **SKILL.md 檔案** - 可直接用於 Claude Code 的技能
+- **本能集合** - 用於 continuous-learning-v2
+- **模式擷取** - 從您的提交歷史學習
```bash
-# 安装 GitHub App 后,技能会出现在:
+# 安裝 GitHub App 後,技能會出現在:
~/.claude/skills/generated/
```
-与 `continuous-learning-v2` 技能无缝整合以继承本能。
+與 `continuous-learning-v2` 技能無縫整合以繼承本能。
---
-## 安装
+## 📥 安裝
-### 选项 1:以插件(Plugin)安装(推荐)
+### 選項 1:以外掛程式安裝(建議)
-使用本仓库最简单的方式 - 安装为 Claude Code 插件:
+使用本儲存庫最簡單的方式 - 安裝為 Claude Code 外掛程式:
```bash
-# 将此仓库添加为市场
+# 將此儲存庫新增為市集
/plugin marketplace add affaan-m/everything-claude-code
-# 安装插件
+# 安裝外掛程式
/plugin install everything-claude-code@everything-claude-code
```
-或直接添加到您的 `~/.claude/settings.json`:
+或直接新增到您的 `~/.claude/settings.json`:
```json
{
@@ -236,48 +288,48 @@ everything-claude-code/
}
```
-这会让您立即访问所有指令、智能体、技能和钩子。
+這會讓您立即存取所有指令、代理程式、技能和鉤子。
---
-### 选项 2:手动安装
+### 🔧 選項 2:手動安裝
-如果您偏好手动控制安装内容:
+如果您偏好手動控制安裝內容:
```bash
-# 克隆仓库
+# 複製儲存庫
git clone https://github.com/affaan-m/everything-claude-code.git
-# 将智能体复制到您的 Claude 配置
+# 將代理程式複製到您的 Claude 設定
cp everything-claude-code/agents/*.md ~/.claude/agents/
-# 复制规则
+# 複製規則
cp everything-claude-code/rules/*.md ~/.claude/rules/
-# 复制指令
+# 複製指令
cp everything-claude-code/commands/*.md ~/.claude/commands/
-# 复制技能
+# 複製技能
cp -r everything-claude-code/skills/* ~/.claude/skills/
```
-#### 将钩子添加到 settings.json
+#### 將鉤子新增到 settings.json
-将 `hooks/hooks.json` 中的钩子复制到您的 `~/.claude/settings.json`。
+將 `hooks/hooks.json` 中的鉤子複製到您的 `~/.claude/settings.json`。
-#### 配置 MCP
+#### 設定 MCP
-将 `mcp-configs/mcp-servers.json` 中所需的 MCP 服务器配置复制到您的 `~/.claude.json`。
+將 `mcp-configs/mcp-servers.json` 中所需的 MCP 伺服器複製到您的 `~/.claude.json`。
-**重要:** 将 `YOUR_*_HERE` 占位符替换为您实际的 API 密钥。
+**重要:** 將 `YOUR_*_HERE` 佔位符替換為您實際的 API 金鑰。
---
-## 核心概念
+## 🎯 核心概念
-### 智能体(Agents)
+### 代理程式(Agents)
-子智能体以有限范围处理委派的任务。示例:
+子代理程式以有限範圍處理委派的任務。範例:
```markdown
---
@@ -292,7 +344,7 @@ You are a senior code reviewer...
### 技能(Skills)
-技能是由指令或智能体调用的工作流定义:
+技能是由指令或代理程式調用的工作流程定義:
```markdown
# TDD Workflow
@@ -304,13 +356,13 @@ You are a senior code reviewer...
5. Verify 80%+ coverage
```
-### 钩子(Hooks)
+### 鉤子(Hooks)
-钩子在工具(Tool)事件时触发。示例 - 警告 console.log:
+鉤子在工具事件時觸發。範例 - 警告 console.log:
```json
{
- "matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\.(ts|tsx|js|jsx)$\"",
+ "matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\\\.(ts|tsx|js|jsx)$\"",
"hooks": [{
"type": "command",
"command": "#!/bin/bash\ngrep -n 'console\\.log' \"$file_path\" && echo '[Hook] Remove console.log' >&2"
@@ -318,28 +370,28 @@ You are a senior code reviewer...
}
```
-### 规则(Rules)
+### 規則(Rules)
-规则是必须遵守的准则。保持模块化:
+規則是必須遵守的準則。保持模組化:
```
~/.claude/rules/
- security.md # 禁止硬编码密钥
- coding-style.md # 不变性、文件组织
- testing.md # TDD、80% 覆盖率要求
+ security.md # 禁止寫死密鑰
+ coding-style.md # 不可變性、檔案限制
+ testing.md # TDD、覆蓋率要求
```
---
-## 执行测试
+## 🧪 執行測試
-插件包含完整的测试套件:
+外掛程式包含完整的測試套件:
```bash
-# 执行所有测试
+# 執行所有測試
node tests/run-all.js
-# 执行个别测试文件
+# 執行個別測試檔案
node tests/lib/utils.test.js
node tests/lib/package-manager.test.js
node tests/hooks/hooks.test.js
@@ -347,78 +399,78 @@ node tests/hooks/hooks.test.js
---
-## 贡献
+## 🤝 貢獻
-**欢迎并鼓励贡献。**
+**歡迎並鼓勵貢獻。**
-本仓库旨在成为社区资源。如果您有:
-- 实用的智能体或技能
-- 巧妙的钩子
-- 更好的 MCP 配置
-- 改进的规则
+本儲存庫旨在成為社群資源。如果您有:
+- 實用的代理程式或技能
+- 巧妙的鉤子
+- 更好的 MCP 設定
+- 改進的規則
-请贡献!详见 [CONTRIBUTING.md](CONTRIBUTING.md) 的指南。
+請貢獻!詳見 [CONTRIBUTING.md](CONTRIBUTING.md) 的指南。
-### 贡献想法
+### 貢獻想法
-- 特定语言的技能(Python、Rust 模式)- Go 现已包含!
-- 特定框架的配置(Django、Rails、Laravel)
-- DevOps 智能体(Kubernetes、Terraform、AWS)
-- 测试策略(不同框架)
-- 特定领域知识(ML、数据工程、移动开发)
+- 特定語言的技能(Python、Rust 模式)- Go 現已包含!
+- 特定框架的設定(Django、Rails、Laravel)
+- DevOps 代理程式(Kubernetes、Terraform、AWS)
+- 測試策略(不同框架)
+- 特定領域知識(ML、資料工程、行動開發)
---
-## 背景
+## 📖 背景
-我从实验性推出就开始使用 Claude Code。2025 年 9 月与 [@DRodriguezFX](https://x.com/DRodriguezFX) 一起使用 Claude Code 打造 [zenith.chat](https://zenith.chat),赢得了 Anthropic x Forum Ventures 黑客松。
+我從實驗性推出就開始使用 Claude Code。2025 年 9 月與 [@DRodriguezFX](https://x.com/DRodriguezFX) 一起使用 Claude Code 打造 [zenith.chat](https://zenith.chat),贏得了 Anthropic x Forum Ventures 黑客松。
-这些配置已在多个生产应用程序中经过实战测试。
+這些設定已在多個生產應用程式中經過實戰測試。
---
-## 重要注意事项
+## ⚠️ 重要注意事項
-### 上下文窗口管理
+### 上下文視窗管理
-**关键:** 不要同时启用所有 MCP。启用过多工具会让您的 200k 上下文窗口缩减至 70k。
+**關鍵:** 不要同時啟用所有 MCP。啟用過多工具會讓您的 200k 上下文視窗縮減至 70k。
-经验法则:
-- 设置 20-30 个 MCP
-- 每个项目启用少于 10 个
-- 启用的工具少于 80 个
+經驗法則:
+- 設定 20-30 個 MCP
+- 每個專案啟用少於 10 個
+- 啟用的工具少於 80 個
-在项目配置中使用 `disabledMcpServers` 来禁用未使用的 MCP。
+在專案設定中使用 `disabledMcpServers` 來停用未使用的 MCP。
-### 自定义
+### 自訂
-这些配置适合我的工作流。您应该:
-1. 从您认同的部分开始
-2. 根据您的技术栈修改
+這些設定適合我的工作流程。您應該:
+1. 從您認同的部分開始
+2. 根據您的技術堆疊修改
3. 移除不需要的部分
4. 添加您自己的模式
---
-## Star 历史
+## 🌟 Star 歷史
[](https://star-history.com/#affaan-m/everything-claude-code&Date)
---
-## 链接
+## 🔗 連結
-- **简明指南(从这里开始):** [Everything Claude Code 简明指南](https://x.com/affaanmustafa/status/2012378465664745795)
-- **完整指南(进阶):** [Everything Claude Code 完整指南](https://x.com/affaanmustafa/status/2014040193557471352)
-- **关注:** [@affaanmustafa](https://x.com/affaanmustafa)
+- **簡明指南(從這裡開始):** [Everything Claude Code 簡明指南](https://x.com/affaanmustafa/status/2012378465664745795)
+- **完整指南(進階):** [Everything Claude Code 完整指南](https://x.com/affaanmustafa/status/2014040193557471352)
+- **追蹤:** [@affaanmustafa](https://x.com/affaanmustafa)
- **zenith.chat:** [zenith.chat](https://zenith.chat)
---
-## 授权
+## 📄 授權
-MIT - 自由使用、依需求修改、如可能请回馈贡献。
+MIT - 自由使用、依需求修改、如可能請回饋貢獻。
---
-**如果有帮助请为本仓库点赞(Star)。阅读两份指南。打造伟大的作品。**
+**如果有幫助請為本儲存庫加星。閱讀兩份指南。打造偉大的作品。**
diff --git a/package-lock.json b/package-lock.json
index b732d4e..db3ddf5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,5 +1,5 @@
{
- "name": "everything-claude-code-zh",
+ "name": "everything-claude-code",
"lockfileVersion": 3,
"requires": true,
"packages": {
@@ -294,7 +294,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -600,7 +599,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -1932,7 +1930,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
diff --git a/skills/continuous-learning-v2/commands/evolve.md b/skills/continuous-learning-v2/commands/evolve.md
deleted file mode 100644
index ec56f2b..0000000
--- a/skills/continuous-learning-v2/commands/evolve.md
+++ /dev/null
@@ -1,186 +0,0 @@
----
-name: evolve
-description: 将相关本能 (Instincts) 聚类为技能 (Skills)、命令 (Commands) 或智能体 (Agents)
-command: /evolve
-implementation: python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py evolve
----
-
-# 演进 (Evolve) 命令
-
-## 实现
-
-```bash
-python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py evolve [--generate]
-```
-
-分析本能 (Instincts) 并将相关的本能聚类为更高级别的结构:
-- **命令 (Commands)**:当本能描述的是用户调用的操作时
-- **技能 (Skills)**:当本能描述的是自动触发的行为时
-- **智能体 (Agents)**:当本能描述的是复杂的、多步骤的过程时
-
-## 用法
-
-```
-/evolve # 分析所有本能并建议演进方案
-/evolve --domain testing # 仅演进测试领域 (testing domain) 中的本能
-/evolve --dry-run # 显示将要创建的内容而不实际执行
-/evolve --threshold 5 # 要求 5 个或更多相关本能才进行聚类
-```
-
-## 演进规则
-
-### → 命令 (Command,用户调用)
-当本能描述用户会显式请求的操作时:
-- 多个关于“当用户要求...”的本能
-- 带有“在创建新的 X 时”等触发器的本能
-- 遵循可重复序列的本能
-
-示例:
-- `new-table-step1`:“在添加数据库表时,创建迁移文件”
-- `new-table-step2`:“在添加数据库表时,更新 schema”
-- `new-table-step3`:“在添加数据库表时,重新生成类型”
-
-→ 创建:`/new-table` 命令
-
-### → 技能 (Skill,自动触发)
-当本能描述应该自动发生的行为时:
-- 模式匹配触发器
-- 错误处理响应
-- 代码风格强制执行
-
-示例:
-- `prefer-functional`:“在编写函数时,优先使用函数式风格”
-- `use-immutable`:“在修改状态时,使用不可变模式”
-- `avoid-classes`:“在设计模块时,避免基于类的设计”
-
-→ 创建:`functional-patterns` 技能
-
-### → 智能体 (Agent,需要深度/隔离)
-当本能描述受益于隔离的复杂、多步骤过程时:
-- 调试工作流 (Workflow)
-- 重构序列
-- 研究任务
-
-示例:
-- `debug-step1`:“调试时,先检查日志”
-- `debug-step2`:“调试时,隔离故障组件”
-- `debug-step3`:“调试时,创建最小复现”
-- `debug-step4`:“调试时,通过测试验证修复”
-
-→ 创建:`debugger` 智能体
-
-## 执行步骤
-
-1. 从 `~/.claude/homunculus/instincts/` 读取所有本能
-2. 按以下维度对本能进行分组:
- - 领域相似性
- - 触发模式重叠
- - 动作序列关系
-3. 对于每个包含 3 个或更多相关本能的聚类:
- - 确定演进类型(命令/技能/智能体)
- - 生成相应的文件
- - 保存到 `~/.claude/homunculus/evolved/{commands,skills,agents}/`
-4. 将演进后的结构链接回源本能
-
-## 输出格式
-
-```
-🧬 演进分析 (Evolve Analysis)
-==================
-
-发现 3 个已准备好演进的聚类:
-
-## 聚类 1: 数据库迁移工作流
-本能: new-table-migration, update-schema, regenerate-types
-类型: 命令 (Command)
-置信度: 85% (基于 12 次观察)
-
-将创建: /new-table 命令
-文件:
- - ~/.claude/homunculus/evolved/commands/new-table.md
-
-## 聚类 2: 函数式代码风格
-本能: prefer-functional, use-immutable, avoid-classes, pure-functions
-类型: 技能 (Skill)
-置信度: 78% (基于 8 次观察)
-
-将创建: functional-patterns 技能
-文件:
- - ~/.claude/homunculus/evolved/skills/functional-patterns.md
-
-## 聚类 3: 调试过程
-本能: debug-check-logs, debug-isolate, debug-reproduce, debug-verify
-类型: 智能体 (Agent)
-置信度: 72% (基于 6 次观察)
-
-将创建: debugger 智能体
-文件:
- - ~/.claude/homunculus/evolved/agents/debugger.md
-
----
-运行 `/evolve --execute` 来创建这些文件。
-```
-
-## 标志 (Flags)
-
-- `--execute`:实际创建演进后的结构(默认为预览)
-- `--dry-run`:预览而不创建
-- `--domain `:仅演进指定领域中的本能
-- `--threshold `:形成聚类所需的最小本能数量(默认:3)
-- `--type `:仅创建指定类型
-
-## 生成文件格式
-
-### 命令 (Command)
-```markdown
----
-name: new-table
-description: 创建带有迁移、schema 更新和类型生成的数据库新表
-command: /new-table
-evolved_from:
- - new-table-migration
- - update-schema
- - regenerate-types
----
-
-# New Table 命令
-
-[基于聚类本能生成的具体内容]
-
-## 步骤
-1. ...
-2. ...
-```
-
-### 技能 (Skill)
-```markdown
----
-name: functional-patterns
-description: 强制执行函数式编程模式
-evolved_from:
- - prefer-functional
- - use-immutable
- - avoid-classes
----
-
-# Functional Patterns 技能
-
-[基于聚类本能生成的具体内容]
-```
-
-### 智能体 (Agent)
-```markdown
----
-name: debugger
-description: 系统化调试智能体
-model: sonnet
-evolved_from:
- - debug-check-logs
- - debug-isolate
- - debug-reproduce
----
-
-# Debugger 智能体
-
-[基于聚类本能生成的具体内容]
-```
diff --git a/skills/continuous-learning-v2/commands/instinct-export.md b/skills/continuous-learning-v2/commands/instinct-export.md
deleted file mode 100644
index 441a959..0000000
--- a/skills/continuous-learning-v2/commands/instinct-export.md
+++ /dev/null
@@ -1,91 +0,0 @@
----
-name: instinct-export
-description: 导出直觉(Instincts)以便与团队成员或其他项目共享
-command: /instinct-export
----
-
-# 直觉导出命令(Instinct Export Command)
-
-将直觉(Instincts)导出为可共享的格式。非常适用于:
-- 与团队成员共享
-- 迁移到新机器
-- 贡献到项目规范(Conventions)中
-
-## 使用方法
-
-```
-/instinct-export # 导出所有个人直觉
-/instinct-export --domain testing # 仅导出测试(Testing)领域的直觉
-/instinct-export --min-confidence 0.7 # 仅导出高置信度的直觉
-/instinct-export --output team-instincts.yaml
-```
-
-## 执行逻辑
-
-1. 从 `~/.claude/homunculus/instincts/personal/` 读取直觉数据
-2. 根据参数(Flags)进行过滤
-3. 脱敏敏感信息:
- - 移除会话 ID(Session IDs)
- - 移除文件路径(仅保留模式串/Patterns)
- - 移除早于“上周”的时间戳
-4. 生成导出文件
-
-## 输出格式
-
-创建一个 YAML 文件:
-
-```yaml
-# Instincts Export
-# Generated: 2025-01-22
-# Source: personal
-# Count: 12 instincts
-
-version: "2.0"
-exported_by: "continuous-learning-v2"
-export_date: "2025-01-22T10:30:00Z"
-
-instincts:
- - id: prefer-functional-style
- trigger: "when writing new functions"
- action: "Use functional patterns over classes"
- confidence: 0.8
- domain: code-style
- observations: 8
-
- - id: test-first-workflow
- trigger: "when adding new functionality"
- action: "Write test first, then implementation"
- confidence: 0.9
- domain: testing
- observations: 12
-
- - id: grep-before-edit
- trigger: "when modifying code"
- action: "Search with Grep, confirm with Read, then Edit"
- confidence: 0.7
- domain: workflow
- observations: 6
-```
-
-## 隐私说明
-
-导出内容**包含**:
-- ✅ 触发模式(Trigger patterns)
-- ✅ 动作(Actions)
-- ✅ 置信度评分(Confidence scores)
-- ✅ 领域(Domains)
-- ✅ 观测计数(Observation counts)
-
-导出内容**不包含**:
-- ❌ 实际代码片段
-- ❌ 文件路径
-- ❌ 会话转录(Session transcripts)
-- ❌ 个人身份标识符
-
-## 参数(Flags)
-
-- `--domain `: 仅导出指定领域(Domain)
-- `--min-confidence `: 最低置信度阈值(默认值:0.3)
-- `--output `: 输出文件路径(默认值:instincts-export-YYYYMMDD.yaml)
-- `--format `: 输出格式(默认值:yaml)
-- `--include-evidence`: 包含证据(Evidence)文本(默认:排除)
diff --git a/skills/continuous-learning-v2/commands/instinct-import.md b/skills/continuous-learning-v2/commands/instinct-import.md
deleted file mode 100644
index 7157b13..0000000
--- a/skills/continuous-learning-v2/commands/instinct-import.md
+++ /dev/null
@@ -1,135 +0,0 @@
----
-name: instinct-import
-description: 从队友、技能生成器(Skill Creator)或其他来源导入直觉(Instincts)
-command: /instinct-import
-implementation: python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py import
----
-
-# 直觉导入命令(Instinct Import Command)
-
-## 实现
-
-```bash
-python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py import [--dry-run] [--force] [--min-confidence 0.7]
-```
-
-从以下来源导入直觉(Instincts):
-- 队友导出的文件
-- 技能生成器(Skill Creator)(仓库分析)
-- 社区集合
-- 之前的机器备份
-
-## 用法
-
-```
-/instinct-import team-instincts.yaml
-/instinct-import https://github.com/org/repo/instincts.yaml
-/instinct-import --from-skill-creator acme/webapp
-```
-
-## 执行流程
-
-1. 获取直觉文件(本地路径或 URL)
-2. 解析并验证格式
-3. 检查是否与现有直觉重复
-4. 合并或添加新直觉
-5. 保存至 `~/.claude/homunculus/instincts/inherited/`
-
-## 导入过程示例
-
-```
-📥 正在从 team-instincts.yaml 导入直觉:
-================================================
-
-发现 12 条待导入的直觉。
-
-正在分析冲突...
-
-## 新直觉 (8)
-这些将被添加:
- ✓ use-zod-validation (置信度: 0.7)
- ✓ prefer-named-exports (置信度: 0.65)
- ✓ test-async-functions (置信度: 0.8)
- ...
-
-## 重复直觉 (3)
-已存在类似的直觉:
- ⚠️ prefer-functional-style
- 本地:0.8 置信度,12 个观测项
- 导入:0.7 置信度
- → 保留本地(置信度更高)
-
- ⚠️ test-first-workflow
- 本地:0.75 置信度
- 导入:0.9 置信度
- → 更新为导入的内容(置信度更高)
-
-## 冲突直觉 (1)
-这些与本地直觉相矛盾:
- ❌ use-classes-for-services
- 与 avoid-classes 冲突
- → 跳过(需要手动解决)
-
----
-导入 8 个新项,更新 1 个,跳过 3 个?
-```
-
-## 合并策略(Merge Strategies)
-
-### 处理重复项
-当导入的直觉与现有直觉匹配时:
-- **高置信度胜出**:保留置信度(Confidence)较高的一方
-- **合并证据**:累计观测项(Observation)计数
-- **更新时间戳**:标记为最近已验证
-
-### 处理冲突
-当导入的直觉与现有直觉冲突时:
-- **默认跳过**:不导入产生冲突的直觉
-- **标记待审查**:将两者都标记为需要关注
-- **手动解决**:由用户决定保留哪一个
-
-## 来源追踪
-
-导入的直觉会被标记以下字段:
-```yaml
-source: "inherited"
-imported_from: "team-instincts.yaml"
-imported_at: "2025-01-22T10:30:00Z"
-original_source: "session-observation" # 或 "repo-analysis"
-```
-
-## 技能生成器(Skill Creator)集成
-
-从技能生成器(Skill Creator)导入时:
-
-```
-/instinct-import --from-skill-creator acme/webapp
-```
-
-这将获取通过仓库分析生成的直觉:
-- 来源:`repo-analysis`
-- 较高的初始置信度(0.7+)
-- 已链接到源仓库
-
-## 参数标志(Flags)
-
-- `--dry-run`:预览而不执行导入
-- `--force`:即使存在冲突也强制导入
-- `--merge-strategy `:如何处理重复项
-- `--from-skill-creator `:从技能生成器(Skill Creator)分析结果导入
-- `--min-confidence `:仅导入置信度高于阈值的直觉
-
-## 输出
-
-导入完成后:
-```
-✅ 导入完成!
-
-已添加:8 条直觉
-已更新:1 条直觉
-已跳过:3 条直觉(2 个重复,1 个冲突)
-
-新直觉已保存至:~/.claude/homunculus/instincts/inherited/
-
-运行 /instinct-status 查看所有直觉。
-```
diff --git a/skills/continuous-learning-v2/commands/instinct-status.md b/skills/continuous-learning-v2/commands/instinct-status.md
deleted file mode 100644
index f440700..0000000
--- a/skills/continuous-learning-v2/commands/instinct-status.md
+++ /dev/null
@@ -1,79 +0,0 @@
----
-name: instinct-status
-description: 显示所有已学习的直觉(Instincts)及其置信度水平
-command: /instinct-status
-implementation: python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py status
----
-
-# Instinct Status 命令
-
-按领域(Domain)分组显示所有已学习的直觉(Instincts)及其置信度得分。
-
-## 实现
-
-```bash
-python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py status
-```
-
-## 用法
-
-```
-/instinct-status
-/instinct-status --domain code-style
-/instinct-status --low-confidence
-```
-
-## 执行逻辑
-
-1. 从 `~/.claude/homunculus/instincts/personal/` 读取所有个人直觉文件
-2. 从 `~/.claude/homunculus/instincts/inherited/` 读取继承的直觉
-3. 按领域分组显示,并附带置信度进度条
-
-## 输出格式
-
-```
-📊 直觉状态 (Instinct Status)
-==================
-
-## 代码风格 (4 个直觉)
-
-### prefer-functional-style
-触发条件 (Trigger):编写新函数时
-动作 (Action):优先使用函数式模式而非类
-置信度 (Confidence):████████░░ 80%
-来源 (Source):session-observation | 最后更新:2025-01-22
-
-### use-path-aliases
-触发条件 (Trigger):导入模块时
-动作 (Action):使用 @/ 路径别名而非相对导入
-置信度 (Confidence):██████░░░░ 60%
-来源 (Source):repo-analysis (github.com/acme/webapp)
-
-## 测试 (2 个直觉)
-
-### test-first-workflow
-触发条件 (Trigger):添加新功能时
-动作 (Action):先写测试,再写实现
-置信度 (Confidence):█████████░ 90%
-来源 (Source):session-observation
-
-## 工作流 (3 个直觉)
-
-### grep-before-edit
-触发条件 (Trigger):修改代码时
-动作 (Action):先用 Grep 搜索,用 Read 确认,再进行编辑 (Edit)
-置信度 (Confidence):███████░░░ 70%
-来源 (Source):session-observation
-
----
-总计:9 个直觉(4 个个人,5 个继承)
-观察器 (Observer):运行中(上次分析:5 分钟前)
-```
-
-## 参数 (Flags)
-
-- `--domain `:按领域过滤(code-style、testing、git 等)
-- `--low-confidence`:仅显示置信度 < 0.5 的直觉
-- `--high-confidence`:仅显示置信度 >= 0.7 的直觉
-- `--source `:按来源过滤(session-observation、repo-analysis、inherited)
-- `--json`:以 JSON 格式输出,供程序化使用
diff --git a/skills/django-patterns/SKILL.md b/skills/django-patterns/SKILL.md
new file mode 100644
index 0000000..fc1ad3b
--- /dev/null
+++ b/skills/django-patterns/SKILL.md
@@ -0,0 +1,734 @@
+---
+name: django-patterns
+description: Django 架构模式、使用 DRF 的 REST API 设计、ORM 最佳实践、缓存、信号(Signals)、中间件(Middleware)以及生产级 Django 应用。
+---
+
+# Django 开发模式
+
+适用于可扩展、可维护应用程序的生产级 Django 架构模式。
+
+## 何时激活
+
+- 构建 Django Web 应用程序时
+- 设计 Django REST Framework (DRF) API 时
+- 处理 Django ORM 和模型时
+* 设置 Django 项目结构时
+* 实现缓存(Caching)、信号(Signals)、中间件(Middleware)时
+
+## 项目结构
+
+### 推荐布局
+
+```
+myproject/
+├── config/
+│ ├── __init__.py
+│ ├── settings/
+│ │ ├── __init__.py
+│ │ ├── base.py # 基础设置
+│ │ ├── development.py # 开发环境设置
+│ │ ├── production.py # 生产环境设置
+│ │ └── test.py # 测试环境设置
+│ ├── urls.py
+│ ├── wsgi.py
+│ └── asgi.py
+├── manage.py
+└── apps/
+ ├── __init__.py
+ ├── users/
+ │ ├── __init__.py
+ │ ├── models.py
+ │ ├── views.py
+ │ ├── serializers.py
+ │ ├── urls.py
+ │ ├── permissions.py
+ │ ├── filters.py
+ │ ├── services.py
+ │ └── tests/
+ └── products/
+ └── ...
+```
+
+### 分离设置模式(Split Settings Pattern)
+
+```python
+# config/settings/base.py
+from pathlib import Path
+
+BASE_DIR = Path(__file__).resolve().parent.parent.parent
+
+SECRET_KEY = env('DJANGO_SECRET_KEY')
+DEBUG = False
+ALLOWED_HOSTS = []
+
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'rest_framework',
+ 'rest_framework.authtoken',
+ 'corsheaders',
+ # 本地应用
+ 'apps.users',
+ 'apps.products',
+]
+
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'whitenoise.middleware.WhiteNoiseMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'corsheaders.middleware.CorsMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'config.urls'
+WSGI_APPLICATION = 'config.wsgi.application'
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.postgresql',
+ 'NAME': env('DB_NAME'),
+ 'USER': env('DB_USER'),
+ 'PASSWORD': env('DB_PASSWORD'),
+ 'HOST': env('DB_HOST'),
+ 'PORT': env('DB_PORT', default='5432'),
+ }
+}
+
+# config/settings/development.py
+from .base import *
+
+DEBUG = True
+ALLOWED_HOSTS = ['localhost', '127.0.0.1']
+
+DATABASES['default']['NAME'] = 'myproject_dev'
+
+INSTALLED_APPS += ['debug_toolbar']
+
+MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']
+
+EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+
+# config/settings/production.py
+from .base import *
+
+DEBUG = False
+ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')
+SECURE_SSL_REDIRECT = True
+SESSION_COOKIE_SECURE = True
+CSRF_COOKIE_SECURE = True
+SECURE_HSTS_SECONDS = 31536000
+SECURE_HSTS_INCLUDE_SUBDOMAINS = True
+SECURE_HSTS_PRELOAD = True
+
+# 日志配置
+LOGGING = {
+ 'version': 1,
+ 'disable_existing_loggers': False,
+ 'handlers': {
+ 'file': {
+ 'level': 'WARNING',
+ 'class': 'logging.FileHandler',
+ 'filename': '/var/log/django/django.log',
+ },
+ },
+ 'loggers': {
+ 'django': {
+ 'handlers': ['file'],
+ 'level': 'WARNING',
+ 'propagate': True,
+ },
+ },
+}
+```
+
+## 模型设计模式
+
+### 模型最佳实践
+
+```python
+from django.db import models
+from django.contrib.auth.models import AbstractUser
+from django.core.validators import MinValueValidator, MaxValueValidator
+
+class User(AbstractUser):
+ """扩展 AbstractUser 的自定义用户模型。"""
+ email = models.EmailField(unique=True)
+ phone = models.CharField(max_length=20, blank=True)
+ birth_date = models.DateField(null=True, blank=True)
+
+ USERNAME_FIELD = 'email'
+ REQUIRED_FIELDS = ['username']
+
+ class Meta:
+ db_table = 'users'
+ verbose_name = 'user'
+ verbose_name_plural = 'users'
+ ordering = ['-date_joined']
+
+ def __str__(self):
+ return self.email
+
+ def get_full_name(self):
+ return f"{self.first_name} {self.last_name}".strip()
+
+class Product(models.Model):
+ """带有适当字段配置的产品模型。"""
+ name = models.CharField(max_length=200)
+ slug = models.SlugField(unique=True, max_length=250)
+ description = models.TextField(blank=True)
+ price = models.DecimalField(
+ max_digits=10,
+ decimal_places=2,
+ validators=[MinValueValidator(0)]
+ )
+ stock = models.PositiveIntegerField(default=0)
+ is_active = models.BooleanField(default=True)
+ category = models.ForeignKey(
+ 'Category',
+ on_delete=models.CASCADE,
+ related_name='products'
+ )
+ tags = models.ManyToManyField('Tag', blank=True, related_name='products')
+ created_at = models.DateTimeField(auto_now_add=True)
+ updated_at = models.DateTimeField(auto_now=True)
+
+ class Meta:
+ db_table = 'products'
+ ordering = ['-created_at']
+ indexes = [
+ models.Index(fields=['slug']),
+ models.Index(fields=['-created_at']),
+ models.Index(fields=['category', 'is_active']),
+ ]
+ constraints = [
+ models.CheckConstraint(
+ check=models.Q(price__gte=0),
+ name='price_non_negative'
+ )
+ ]
+
+ def __str__(self):
+ return self.name
+
+ def save(self, *args, **kwargs):
+ if not self.slug:
+ self.slug = slugify(self.name)
+ super().save(*args, **kwargs)
+```
+
+### QuerySet 最佳实践
+
+```python
+from django.db import models
+
+class ProductQuerySet(models.QuerySet):
+ """Product 模型的自定义 QuerySet。"""
+
+ def active(self):
+ """仅返回已激活的产品。"""
+ return self.filter(is_active=True)
+
+ def with_category(self):
+ """使用 select_related 加载分类,避免 N+1 查询。"""
+ return self.select_related('category')
+
+ def with_tags(self):
+ """使用 prefetch_related 预加载多对多关系的标签。"""
+ return self.prefetch_related('tags')
+
+ def in_stock(self):
+ """返回库存 > 0 的产品。"""
+ return self.filter(stock__gt=0)
+
+ def search(self, query):
+ """按名称或描述搜索产品。"""
+ return self.filter(
+ models.Q(name__icontains=query) |
+ models.Q(description__icontains=query)
+ )
+
+class Product(models.Model):
+ # ... 字段 ...
+
+ objects = ProductQuerySet.as_manager() # 使用自定义 QuerySet
+
+# 用法
+Product.objects.active().with_category().in_stock()
+```
+
+### 管理器(Manager)方法
+
+```python
+class ProductManager(models.Manager):
+ """用于复杂查询的自定义管理器。"""
+
+ def get_or_none(self, **kwargs):
+ """返回对象,或者在不存在时返回 None 而不是抛出 DoesNotExist。"""
+ try:
+ return self.get(**kwargs)
+ except self.model.DoesNotExist:
+ return None
+
+ def create_with_tags(self, name, price, tag_names):
+ """创建产品并关联标签。"""
+ product = self.create(name=name, price=price)
+ tags = [Tag.objects.get_or_create(name=name)[0] for name in tag_names]
+ product.tags.set(tags)
+ return product
+
+ def bulk_update_stock(self, product_ids, quantity):
+ """批量更新多个产品的库存。"""
+ return self.filter(id__in=product_ids).update(stock=quantity)
+
+# 在模型中
+class Product(models.Model):
+ # ... 字段 ...
+ custom = ProductManager()
+```
+
+## Django REST Framework 模式
+
+### 序列化器(Serializer)模式
+
+```python
+from rest_framework import serializers
+from django.contrib.auth.password_validation import validate_password
+from .models import Product, User
+
+class ProductSerializer(serializers.ModelSerializer):
+ """Product 模型的序列化器。"""
+
+ category_name = serializers.CharField(source='category.name', read_only=True)
+ average_rating = serializers.FloatField(read_only=True)
+ discount_price = serializers.SerializerMethodField()
+
+ class Meta:
+ model = Product
+ fields = [
+ 'id', 'name', 'slug', 'description', 'price',
+ 'discount_price', 'stock', 'category_name',
+ 'average_rating', 'created_at'
+ ]
+ read_only_fields = ['id', 'slug', 'created_at']
+
+ def get_discount_price(self, obj):
+ """如果适用,计算折扣价。"""
+ if hasattr(obj, 'discount') and obj.discount:
+ return obj.price * (1 - obj.discount.percent / 100)
+ return obj.price
+
+ def validate_price(self, value):
+ """确保价格非负。"""
+ if value < 0:
+ raise serializers.ValidationError("价格不能为负数。")
+ return value
+
+class ProductCreateSerializer(serializers.ModelSerializer):
+ """用于创建产品的序列化器。"""
+
+ class Meta:
+ model = Product
+ fields = ['name', 'description', 'price', 'stock', 'category']
+
+ def validate(self, data):
+ """多字段的自定义校验。"""
+ if data['price'] > 10000 and data['stock'] > 100:
+ raise serializers.ValidationError(
+ "高价值产品不能有大量库存。"
+ )
+ return data
+
+class UserRegistrationSerializer(serializers.ModelSerializer):
+ """用户注册序列化器。"""
+
+ password = serializers.CharField(
+ write_only=True,
+ required=True,
+ validators=[validate_password],
+ style={'input_type': 'password'}
+ )
+ password_confirm = serializers.CharField(write_only=True, style={'input_type': 'password'})
+
+ class Meta:
+ model = User
+ fields = ['email', 'username', 'password', 'password_confirm']
+
+ def validate(self, data):
+ """校验两次输入的密码是否一致。"""
+ if data['password'] != data['password_confirm']:
+ raise serializers.ValidationError({
+ "password_confirm": "密码字段不匹配。"
+ })
+ return data
+
+ def create(self, validated_data):
+ """创建并保存加密后的密码。"""
+ validated_data.pop('password_confirm')
+ password = validated_data.pop('password')
+ user = User.objects.create(**validated_data)
+ user.set_password(password)
+ user.save()
+ return user
+```
+
+### 视图集(ViewSet)模式
+
+```python
+from rest_framework import viewsets, status, filters
+from rest_framework.decorators import action
+from rest_framework.response import Response
+from rest_framework.permissions import IsAuthenticated, IsAdminUser
+from django_filters.rest_framework import DjangoFilterBackend
+from .models import Product
+from .serializers import ProductSerializer, ProductCreateSerializer
+from .permissions import IsOwnerOrReadOnly
+from .filters import ProductFilter
+from .filters import ProductFilter
+from .services import ProductService
+
+class ProductViewSet(viewsets.ModelViewSet):
+ """Product 模型的视图集。"""
+
+ queryset = Product.objects.select_related('category').prefetch_related('tags')
+ permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
+ filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
+ filterset_class = ProductFilter
+ search_fields = ['name', 'description']
+ ordering_fields = ['price', 'created_at', 'name']
+ ordering = ['-created_at']
+
+ def get_serializer_class(self):
+ """根据操作(action)返回适当的序列化器类。"""
+ if self.action == 'create':
+ return ProductCreateSerializer
+ return ProductSerializer
+
+ def perform_create(self, serializer):
+ """在保存时关联当前用户上下文。"""
+ serializer.save(created_by=self.request.user)
+
+ @action(detail=False, methods=['get'])
+ def featured(self, request):
+ """返回推荐产品。"""
+ featured = self.queryset.filter(is_featured=True)[:10]
+ serializer = self.get_serializer(featured, many=True)
+ return Response(serializer.data)
+
+ @action(detail=True, methods=['post'])
+ def purchase(self, request, pk=None):
+ """购买产品。"""
+ product = self.get_object()
+ service = ProductService()
+ result = service.purchase(product, request.user)
+ return Response(result, status=status.HTTP_201_CREATED)
+
+ @action(detail=False, methods=['get'], permission_classes=[IsAuthenticated])
+ def my_products(self, request):
+ """返回由当前用户创建的产品。"""
+ products = self.queryset.filter(created_by=request.user)
+ page = self.paginate_queryset(products)
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+```
+
+### 自定义操作
+
+```python
+from rest_framework.decorators import api_view, permission_classes
+from rest_framework.permissions import IsAuthenticated
+from rest_framework.response import Response
+
+@api_view(['POST'])
+@permission_classes([IsAuthenticated])
+def add_to_cart(request):
+ """将产品添加到用户购物车。"""
+ product_id = request.data.get('product_id')
+ quantity = request.data.get('quantity', 1)
+
+ try:
+ product = Product.objects.get(id=product_id)
+ except Product.DoesNotExist:
+ return Response(
+ {'error': '产品未找到'},
+ status=status.HTTP_404_NOT_FOUND
+ )
+
+ cart, _ = Cart.objects.get_or_create(user=request.user)
+ CartItem.objects.create(
+ cart=cart,
+ product=product,
+ quantity=quantity
+ )
+
+ return Response({'message': '已添加到购物车'}, status=status.HTTP_201_CREATED)
+```
+
+## 服务层模式(Service Layer Pattern)
+
+```python
+# apps/orders/services.py
+from typing import Optional
+from django.db import transaction
+from .models import Order, OrderItem
+
+class OrderService:
+ """订单相关业务逻辑的服务层。"""
+
+ @staticmethod
+ @transaction.atomic
+ def create_order(user, cart: Cart) -> Order:
+ """从购物车创建订单。"""
+ order = Order.objects.create(
+ user=user,
+ total_price=cart.total_price
+ )
+
+ for item in cart.items.all():
+ OrderItem.objects.create(
+ order=order,
+ product=item.product,
+ quantity=item.quantity,
+ price=item.product.price
+ )
+
+ # 清空购物车
+ cart.items.all().delete()
+
+ return order
+
+ @staticmethod
+ def process_payment(order: Order, payment_data: dict) -> bool:
+ """处理订单支付。"""
+ # 与支付网关集成
+ payment = PaymentGateway.charge(
+ amount=order.total_price,
+ token=payment_data['token']
+ )
+
+ if payment.success:
+ order.status = Order.Status.PAID
+ order.save()
+ # 发送确认邮件
+ OrderService.send_confirmation_email(order)
+ return True
+
+ return False
+
+ @staticmethod
+ def send_confirmation_email(order: Order):
+ """发送订单确认邮件。"""
+ # 邮件发送逻辑
+ pass
+```
+
+## 缓存策略(Caching Strategies)
+
+### 视图级缓存
+
+```python
+from django.views.decorators.cache import cache_page
+from django.utils.decorators import method_decorator
+
+@method_decorator(cache_page(60 * 15), name='dispatch') # 15 分钟
+class ProductListView(generic.ListView):
+ model = Product
+ template_name = 'products/list.html'
+ context_object_name = 'products'
+```
+
+### 模板片段缓存
+
+```django
+{% load cache %}
+{% cache 500 sidebar %}
+ ... 耗时的侧边栏内容 ...
+{% endcache %}
+```
+
+### 低级缓存
+
+```python
+from django.core.cache import cache
+
+def get_featured_products():
+ """通过缓存获取推荐产品。"""
+ cache_key = 'featured_products'
+ products = cache.get(cache_key)
+
+ if products is None:
+ products = list(Product.objects.filter(is_featured=True))
+ cache.set(cache_key, products, timeout=60 * 15) # 15 分钟
+
+ return products
+```
+
+### QuerySet 缓存
+
+```python
+from django.core.cache import cache
+
+def get_popular_categories():
+ cache_key = 'popular_categories'
+ categories = cache.get(cache_key)
+
+ if categories is None:
+ categories = list(Category.objects.annotate(
+ product_count=Count('products')
+ ).filter(product_count__gt=10).order_by('-product_count')[:20])
+ cache.set(cache_key, categories, timeout=60 * 60) # 1 小时
+
+ return categories
+```
+
+## 信号(Signals)
+
+### 信号模式
+
+```python
+# apps/users/signals.py
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+from django.contrib.auth import get_user_model
+from .models import Profile
+
+User = get_user_model()
+
+@receiver(post_save, sender=User)
+def create_user_profile(sender, instance, created, **kwargs):
+ """当用户创建时同步创建 Profile。"""
+ if created:
+ Profile.objects.create(user=instance)
+
+@receiver(post_save, sender=User)
+def save_user_profile(sender, instance, **kwargs):
+ """当用户保存时同步保存 Profile。"""
+ instance.profile.save()
+
+# apps/users/apps.py
+from django.apps import AppConfig
+
+class UsersConfig(AppConfigConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'apps.users'
+
+ def ready(self):
+ """在应用准备就绪时导入信号。"""
+ import apps.users.signals
+```
+
+## 中间件(Middleware)
+
+### 自定义中间件
+
+```python
+# middleware/active_user_middleware.py
+import time
+from django.utils.deprecation import MiddlewareMixin
+
+class ActiveUserMiddleware(MiddlewareMixin):
+ """用于跟踪活跃用户的中间件。"""
+
+ def process_request(self, request):
+ """处理传入请求。"""
+ if request.user.is_authenticated:
+ # 更新最后活跃时间
+ request.user.last_active = timezone.now()
+ request.user.save(update_fields=['last_active'])
+
+class RequestLoggingMiddleware(MiddlewareMixin):
+ """用于记录请求日志的中间件。"""
+
+ def process_request(self, request):
+ """记录请求开始时间。"""
+ request.start_time = time.time()
+
+ def process_response(self, request, response):
+ """记录请求耗时。"""
+ if hasattr(request, 'start_time'):
+ duration = time.time() - request.start_time
+ logger.info(f'{request.method} {request.path} - {response.status_code} - {duration:.3f}s')
+ return response
+```
+
+## 性能优化(Performance Optimization)
+
+### 防止 N+1 查询
+
+```python
+# 差劲 - N+1 查询
+products = Product.objects.all()
+for product in products:
+ print(product.category.name) # 为每个产品单独进行一次查询
+
+# 优秀 - 使用 select_related 进行单次查询
+products = Product.objects.select_related('category').all()
+for product in products:
+ print(product.category.name)
+
+# 优秀 - 对多对多关系使用 prefetch_related
+products = Product.objects.prefetch_related('tags').all()
+for product in products:
+ for tag in product.tags.all():
+ print(tag.name)
+```
+
+### 数据库索引
+
+```python
+class Product(models.Model):
+ name = models.CharField(max_length=200, db_index=True)
+ slug = models.SlugField(unique=True)
+ category = models.ForeignKey('Category', on_delete=models.CASCADE)
+ created_at = models.DateTimeField(auto_now_add=True)
+
+ class Meta:
+ indexes = [
+ models.Index(fields=['name']),
+ models.Index(fields=['-created_at']),
+ models.Index(fields=['category', 'created_at']),
+ ]
+```
+
+### 批量操作
+
+```python
+# 批量创建
+Product.objects.bulk_create([
+ Product(name=f'Product {i}', price=10.00)
+ for i in range(1000)
+])
+
+# 批量更新
+products = Product.objects.all()[:100]
+for product in products:
+ product.is_active = True
+Product.objects.bulk_update(products, ['is_active'])
+
+# 批量删除
+Product.objects.filter(stock=0).delete()
+```
+
+## 快速参考
+
+| 模式 | 描述 |
+|---------|-------------|
+| 分离设置(Split settings) | 区分开发/生产/测试环境配置 |
+| 自定义 QuerySet | 可复用的查询方法 |
+| 服务层(Service Layer) | 业务逻辑解耦 |
+| 视图集(ViewSet) | REST API 端点封装 |
+| 序列化器校验(Serializer validation) | 请求/响应数据的转换与验证 |
+| select_related | 外键关系优化 |
+| prefetch_related | 多对多关系优化 |
+| 缓存优先(Cache first) | 对耗时操作进行缓存 |
+| 信号(Signals) | 事件驱动的操作 |
+| 中间件(Middleware) | 请求/响应拦截处理 |
+
+记住:Django 提供了许多捷径,但对于生产级应用,架构和组织结构比简洁的代码更重要。请为可维护性而构建。
diff --git a/skills/django-security/SKILL.md b/skills/django-security/SKILL.md
new file mode 100644
index 0000000..3c6907d
--- /dev/null
+++ b/skills/django-security/SKILL.md
@@ -0,0 +1,592 @@
+---
+name: django-security
+description: Django 安全最佳实践,涵盖身份认证、授权、CSRF 防护、SQL 注入预防、XSS 预防以及安全的部署配置。
+---
+
+# Django 安全最佳实践
+
+针对 Django 应用程序的全面安全指南,旨在抵御常见的漏洞。
+
+## 何时激活
+
+- 设置 Django 身份认证(Authentication)和授权(Authorization)时
+- 实现用户权限和角色时
+- 配置生产环境安全设置时
+- 审查 Django 应用程序的安全问题时
+- 将 Django 应用程序部署到生产环境时
+
+## 核心安全设置
+
+### 生产环境设置配置
+
+```python
+# settings/production.py
+import os
+
+DEBUG = False # 关键:切勿在生产环境中使用 True
+
+ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')
+
+# 安全头部(Security headers)
+SECURE_SSL_REDIRECT = True
+SESSION_COOKIE_SECURE = True
+CSRF_COOKIE_SECURE = True
+SECURE_HSTS_SECONDS = 31536000 # 1 年
+SECURE_HSTS_INCLUDE_SUBDOMAINS = True
+SECURE_HSTS_PRELOAD = True
+SECURE_CONTENT_TYPE_NOSNIFF = True
+SECURE_BROWSER_XSS_FILTER = True
+X_FRAME_OPTIONS = 'DENY'
+
+# HTTPS 与 Cookie
+SESSION_COOKIE_HTTPONLY = True
+CSRF_COOKIE_HTTPONLY = True
+SESSION_COOKIE_SAMESITE = 'Lax'
+CSRF_COOKIE_SAMESITE = 'Lax'
+
+# 密钥(必须通过环境变量设置)
+SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
+if not SECRET_KEY:
+ raise ImproperlyConfigured('DJANGO_SECRET_KEY environment variable is required')
+
+# 密码校验
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ 'OPTIONS': {
+ 'min_length': 12,
+ }
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+```
+
+## 身份认证(Authentication)
+
+### 自定义用户模型(Custom User Model)
+
+```python
+# apps/users/models.py
+from django.contrib.auth.models import AbstractUser
+from django.db import models
+
+class User(AbstractUser):
+ """为了更好的安全性而自定义的用户模型。"""
+
+ email = models.EmailField(unique=True)
+ phone = models.CharField(max_length=20, blank=True)
+
+ USERNAME_FIELD = 'email' # 使用邮箱作为用户名
+ REQUIRED_FIELDS = ['username']
+
+ class Meta:
+ db_table = 'users'
+ verbose_name = 'User'
+ verbose_name_plural = 'Users'
+
+ def __str__(self):
+ return self.email
+
+# settings/base.py
+AUTH_USER_MODEL = 'users.User'
+```
+
+### 密码哈希(Password Hashing)
+
+```python
+# Django 默认使用 PBKDF2。为了更强的安全性:
+PASSWORD_HASHERS = [
+ 'django.contrib.auth.hashers.Argon2PasswordHasher',
+ 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
+ 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
+ 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
+]
+```
+
+### 会话管理(Session Management)
+
+```python
+# 会话配置
+SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 或 'db'
+SESSION_CACHE_ALIAS = 'default'
+SESSION_COOKIE_AGE = 3600 * 24 * 7 # 1 周
+SESSION_SAVE_EVERY_REQUEST = False
+SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 更好的用户体验,但安全性略低
+```
+
+## 授权(Authorization)
+
+### 权限(Permissions)
+
+```python
+# models.py
+from django.db import models
+from django.contrib.auth.models import Permission
+
+class Post(models.Model):
+ title = models.CharField(max_length=200)
+ content = models.TextField()
+ author = models.ForeignKey(User, on_delete=models.CASCADE)
+
+ class Meta:
+ permissions = [
+ ('can_publish', '可以发布帖子'),
+ ('can_edit_others', '可以编辑他人的帖子'),
+ ]
+
+ def user_can_edit(self, user):
+ """检查用户是否可以编辑此帖子。"""
+ return self.author == user or user.has_perm('app.can_edit_others')
+
+# views.py
+from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
+from django.views.generic import UpdateView
+
+class PostUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
+ model = Post
+ permission_required = 'app.can_edit_others'
+ raise_exception = True # 返回 403 而不是重定向
+
+ def get_queryset(self):
+ """仅允许用户编辑自己的帖子。"""
+ return Post.objects.filter(author=self.request.user)
+```
+
+### 自定义权限
+
+```python
+# permissions.py
+from rest_framework import permissions
+
+class IsOwnerOrReadOnly(permissions.BasePermission):
+ """仅允许所有者编辑对象。"""
+
+ def has_object_permission(self, request, view, obj):
+ # 允许任何请求的读取权限
+ if request.method in permissions.SAFE_METHODS:
+ return True
+
+ # 仅所有者拥有写入权限
+ return obj.author == request.user
+
+class IsAdminOrReadOnly(permissions.BasePermission):
+ """允许管理员执行任何操作,其他人只读。"""
+
+ def has_permission(self, request, view):
+ if request.method in permissions.SAFE_METHODS:
+ return True
+ return request.user and request.user.is_staff
+
+class IsVerifiedUser(permissions.BasePermission):
+ """仅允许已验证的用户。"""
+
+ def has_permission(self, request, view):
+ return request.user and request.user.is_authenticated and request.user.is_verified
+```
+
+### 基于角色的访问控制(RBAC)
+
+```python
+# models.py
+from django.contrib.auth.models import AbstractUser, Group
+
+class User(AbstractUser):
+ ROLE_CHOICES = [
+ ('admin', '管理员'),
+ ('moderator', '版主'),
+ ('user', '普通用户'),
+ ]
+ role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='user')
+
+ def is_admin(self):
+ return self.role == 'admin' or self.is_superuser
+
+ def is_moderator(self):
+ return self.role in ['admin', 'moderator']
+
+# 混入类(Mixins)
+class AdminRequiredMixin:
+ """要求管理员角色的混入类。"""
+
+ def dispatch(self, request, *args, **kwargs):
+ if not request.user.is_authenticated or not request.user.is_admin():
+ from django.core.exceptions import PermissionDenied
+ raise PermissionDenied
+ return super().dispatch(request, *args, **kwargs)
+```
+
+## SQL 注入防护
+
+### Django ORM 保护
+
+```python
+# 推荐:Django ORM 自动转义参数
+def get_user(username):
+ return User.objects.get(username=username) # 安全
+
+# 推荐:在 raw() 中使用参数
+def search_users(query):
+ return User.objects.raw('SELECT * FROM users WHERE username = %s', [query])
+
+# 错误:切勿直接插值用户输入
+def get_user_bad(username):
+ return User.objects.raw(f'SELECT * FROM users WHERE username = {username}') # 存在漏洞!
+
+# 推荐:使用带有正确转义的 filter
+def get_users_by_email(email):
+ return User.objects.filter(email__iexact=email) # 安全
+
+# 推荐:对复杂查询使用 Q 对象
+from django.db.models import Q
+def search_users_complex(query):
+ return User.objects.filter(
+ Q(username__icontains=query) |
+ Q(email__icontains=query)
+ ) # 安全
+```
+
+### 使用 raw() 时的额外安全措施
+
+```python
+# 如果必须使用原生 SQL,请务必使用参数
+User.objects.raw(
+ 'SELECT * FROM users WHERE email = %s AND status = %s',
+ [user_input_email, status]
+)
+```
+
+## XSS 防护
+
+### 模板转义
+
+```django
+{# Django 默认自动转义变量 - 安全 #}
+{{ user_input }} {# 已转义的 HTML #}
+
+{# 仅对受信任的内容显式标记为 safe #}
+{{ trusted_html|safe }} {# 未转义 #}
+
+{# 使用模板过滤器以获得安全的 HTML #}
+{{ user_input|escape }} {# 与默认值相同 #}
+{{ user_input|striptags }} {# 移除所有 HTML 标签 #}
+
+{# JavaScript 转义 #}
+
+```
+
+### 安全字符串处理
+
+```python
+from django.utils.safestring import mark_safe
+from django.utils.html import escape
+
+# 错误:在没有转义的情况下,切勿将用户输入标记为 safe
+def render_bad(user_input):
+ return mark_safe(user_input) # 存在漏洞!
+
+# 推荐:先转义,然后标记为 safe
+def render_good(user_input):
+ return mark_safe(escape(user_input))
+
+# 推荐:对包含变量的 HTML 使用 format_html
+from django.utils.html import format_html
+
+def greet_user(username):
+ return format_html('{} ', escape(username))
+```
+
+### HTTP 头部
+
+```python
+# settings.py
+SECURE_CONTENT_TYPE_NOSNIFF = True # 防止 MIME 嗅探
+SECURE_BROWSER_XSS_FILTER = True # 启用 XSS 过滤器
+X_FRAME_OPTIONS = 'DENY' # 防止点击劫持
+
+# 自定义中间件
+from django.conf import settings
+
+class SecurityHeaderMiddleware:
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ response = self.get_response(request)
+ response['X-Content-Type-Options'] = 'nosniff'
+ response['X-Frame-Options'] = 'DENY'
+ response['X-XSS-Protection'] = '1; mode=block'
+ response['Content-Security-Policy'] = "default-src 'self'"
+ return response
+```
+
+## CSRF 防护
+
+### 默认 CSRF 防护
+
+```python
+# settings.py - CSRF 默认已启用
+CSRF_COOKIE_SECURE = True # 仅通过 HTTPS 发送
+CSRF_COOKIE_HTTPONLY = True # 防止 JavaScript 访问
+CSRF_COOKIE_SAMESITE = 'Lax' # 在某些情况下防止 CSRF
+CSRF_TRUSTED_ORIGINS = ['https://example.com'] # 受信任的域
+
+# 模板用法
+
+
+# AJAX 请求
+function getCookie(name) {
+ let cookieValue = null;
+ if (document.cookie && document.cookie !== '') {
+ const cookies = document.cookie.split(';');
+ for (let i = 0; i < cookies.length; i++) {
+ const cookie = cookies[i].trim();
+ if (cookie.substring(0, name.length + 1) === (name + '=')) {
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+ break;
+ }
+ }
+ }
+ return cookieValue;
+}
+
+fetch('/api/endpoint/', {
+ method: 'POST',
+ headers: {
+ 'X-CSRFToken': getCookie('csrftoken'),
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data)
+});
+```
+
+### 豁免视图(请谨慎使用)
+
+```python
+from django.views.decorators.csrf import csrf_exempt
+
+@csrf_exempt # 仅在绝对必要时使用!
+def webhook_view(request):
+ # 来自外部服务的 Webhook
+ pass
+```
+
+## 文件上传安全
+
+### 文件验证
+
+```python
+import os
+from django.core.exceptions import ValidationError
+
+def validate_file_extension(value):
+ """验证文件扩展名。"""
+ ext = os.path.splitext(value.name)[1]
+ valid_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.pdf']
+ if not ext.lower() in valid_extensions:
+ raise ValidationError('不支持的文件扩展名。')
+
+def validate_file_size(value):
+ """验证文件大小(最大 5MB)。"""
+ filesize = value.size
+ if filesize > 5 * 1024 * 1024:
+ raise ValidationError('文件过大。最大限制为 5MB。')
+
+# models.py
+class Document(models.Model):
+ file = models.FileField(
+ upload_to='documents/',
+ validators=[validate_file_extension, validate_file_size]
+ )
+```
+
+### 安全文件存储
+
+```python
+# settings.py
+MEDIA_ROOT = '/var/www/media/'
+MEDIA_URL = '/media/'
+
+# 生产环境中使用独立的媒体文件域名
+MEDIA_DOMAIN = 'https://media.example.com'
+
+# 不要直接提供用户上传的文件
+# 对静态文件使用 whitenoise 或 CDN
+# 对媒体文件使用独立服务器或 S3
+```
+
+## API 安全
+
+### 速率限制(Rate Limiting)
+
+```python
+# settings.py
+REST_FRAMEWORK = {
+ 'DEFAULT_THROTTLE_CLASSES': [
+ 'rest_framework.throttling.AnonRateThrottle',
+ 'rest_framework.throttling.UserRateThrottle'
+ ],
+ 'DEFAULT_THROTTLE_RATES': {
+ 'anon': '100/day',
+ 'user': '1000/day',
+ 'upload': '10/hour',
+ }
+}
+
+# 自定义节流(Throttle)
+from rest_framework.throttling import UserRateThrottle
+
+class BurstRateThrottle(UserRateThrottle):
+ scope = 'burst'
+ rate = '60/min'
+
+class SustainedRateThrottle(UserRateThrottle):
+ scope = 'sustained'
+ rate = '1000/day'
+```
+
+### API 身份认证
+
+```python
+# settings.py
+REST_FRAMEWORK = {
+ 'DEFAULT_AUTHENTICATION_CLASSES': [
+ 'rest_framework.authentication.TokenAuthentication',
+ 'rest_framework.authentication.SessionAuthentication',
+ 'rest_framework_simplejwt.authentication.JWTAuthentication',
+ ],
+ 'DEFAULT_PERMISSION_CLASSES': [
+ 'rest_framework.permissions.IsAuthenticated',
+ ],
+}
+
+# views.py
+from rest_framework.decorators import api_view, permission_classes
+from rest_framework.permissions import IsAuthenticated
+
+@api_view(['GET', 'POST'])
+@permission_classes([IsAuthenticated])
+def protected_view(request):
+ return Response({'message': 'You are authenticated'})
+```
+
+## 安全头部(Security Headers)
+
+### 内容安全策略(CSP)
+
+```python
+# settings.py
+CSP_DEFAULT_SRC = "'self'"
+CSP_SCRIPT_SRC = "'self' https://cdn.example.com"
+CSP_STYLE_SRC = "'self' 'unsafe-inline'"
+CSP_IMG_SRC = "'self' data: https:"
+CSP_CONNECT_SRC = "'self' https://api.example.com"
+
+# 中间件
+class CSPMiddleware:
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ response = self.get_response(request)
+ response['Content-Security-Policy'] = (
+ f"default-src {CSP_DEFAULT_SRC}; "
+ f"script-src {CSP_SCRIPT_SRC}; "
+ f"style-src {CSP_STYLE_SRC}; "
+ f"img-src {CSP_IMG_SRC}; "
+ f"connect-src {CSP_CONNECT_SRC}"
+ )
+ return response
+```
+
+## 环境变量
+
+### 管理密钥
+
+```python
+# 使用 python-decouple 或 django-environ
+import environ
+
+env = environ.Env(
+ # 设置类型转换、默认值
+ DEBUG=(bool, False)
+)
+
+# 读取 .env 文件
+environ.Env.read_env()
+
+SECRET_KEY = env('DJANGO_SECRET_KEY')
+DATABASE_URL = env('DATABASE_URL')
+ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')
+
+# .env 文件(切勿提交此文件)
+DEBUG=False
+SECRET_KEY=your-secret-key-here
+DATABASE_URL=postgresql://user:password@localhost:5432/dbname
+ALLOWED_HOSTS=example.com,www.example.com
+```
+
+## 记录安全事件
+
+```python
+# settings.py
+LOGGING = {
+ 'version': 1,
+ 'disable_existing_loggers': False,
+ 'handlers': {
+ 'file': {
+ 'level': 'WARNING',
+ 'class': 'logging.FileHandler',
+ 'filename': '/var/log/django/security.log',
+ },
+ 'console': {
+ 'level': 'INFO',
+ 'class': 'logging.StreamHandler',
+ },
+ },
+ 'loggers': {
+ 'django.security': {
+ 'handlers': ['file', 'console'],
+ 'level': 'WARNING',
+ 'propagate': True,
+ },
+ 'django.request': {
+ 'handlers': ['file'],
+ 'level': 'ERROR',
+ 'propagate': False,
+ },
+ },
+}
+```
+
+## 快速安全自检表
+
+| 检查项 | 描述 |
+|-------|-------------|
+| `DEBUG = False` | 切勿在生产环境中开启 DEBUG |
+| 仅限 HTTPS | 强制使用 SSL,启用安全 Cookie |
+| 强密钥 | 为 SECRET_KEY 使用环境变量 |
+| 密码校验 | 启用所有密码验证器 |
+| CSRF 防护 | 默认已启用,请勿禁用 |
+| XSS 防护 | Django 自动转义,请勿对用户输入使用 `|safe` |
+| SQL 注入 | 使用 ORM,切勿在查询中拼接字符串 |
+| 文件上传 | 验证文件类型和大小 |
+| 速率限制 | 对 API 端点进行节流 |
+| 安全头部 | 配置 CSP, X-Frame-Options, HSTS |
+| 日志记录 | 记录安全事件 |
+| 更新 | 保持 Django 及其依赖项为最新版本 |
+
+记住:安全是一个持续的过程,而不是一个产品。请定期审查并更新你的安全实践。
diff --git a/skills/django-tdd/SKILL.md b/skills/django-tdd/SKILL.md
new file mode 100644
index 0000000..7b5de79
--- /dev/null
+++ b/skills/django-tdd/SKILL.md
@@ -0,0 +1,728 @@
+---
+name: django-tdd
+description: 使用 pytest-django、测试驱动开发(TDD)方法论、factory_boy、Mock 模拟、覆盖率统计以及 Django REST Framework API 测试的 Django 测试策略。
+---
+
+# Django TDD 测试指南
+
+使用 pytest、factory_boy 和 Django REST Framework 对 Django 应用进行测试驱动开发(TDD)。
+
+## 何时激活
+
+- 编写新的 Django 应用程序时
+- 实现 Django REST Framework API 时
+- 测试 Django 模型(Models)、视图(Views)和序列化器(Serializers)时
+- 为 Django 项目搭建测试基础设施时
+
+## Django 的 TDD 工作流
+
+### 红-绿-重构循环(Red-Green-Refactor Cycle)
+
+```python
+# 步骤 1:红色(RED) - 编写失败的测试
+def test_user_creation():
+ user = User.objects.create_user(email='test@example.com', password='testpass123')
+ assert user.email == 'test@example.com'
+ assert user.check_password('testpass123')
+ assert not user.is_staff
+
+# 步骤 2:绿色(GREEN) - 编写代码使测试通过
+# 创建 User 模型或工厂(Factory)
+
+# 步骤 3:重构(REFACTOR) - 在保持测试通过的前提下优化代码
+```
+
+## 环境搭建
+
+### pytest 配置
+
+```ini
+# pytest.ini
+[pytest]
+DJANGO_SETTINGS_MODULE = config.settings.test
+testpaths = tests
+python_files = test_*.py
+python_classes = Test*
+python_functions = test_*
+addopts =
+ --reuse-db
+ --nomigrations
+ --cov=apps
+ --cov-report=html
+ --cov-report=term-missing
+ --strict-markers
+markers =
+ slow: 标记为耗时较长的测试
+ integration: 标记为集成测试
+```
+
+### 测试设置(Settings)
+
+```python
+# config/settings/test.py
+from .base import *
+
+DEBUG = True
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': ':memory:',
+ }
+}
+
+# 禁用迁移以提升速度
+class DisableMigrations:
+ def __contains__(self, item):
+ return True
+
+ def __getitem__(self, item):
+ return None
+
+MIGRATION_MODULES = DisableMigrations()
+
+# 使用更快的密码哈希算法
+PASSWORD_HASHERS = [
+ 'django.contrib.auth.hashers.MD5PasswordHasher',
+]
+
+# 邮件后端配置
+EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+
+# Celery 始终同步执行
+CELERY_TASK_ALWAYS_EAGER = True
+CELERY_TASK_EAGER_PROPAGATES = True
+```
+
+### conftest.py
+
+```python
+# tests/conftest.py
+import pytest
+from django.utils import timezone
+from django.contrib.auth import get_user_model
+
+User = get_user_model()
+
+@pytest.fixture(autouse=True)
+def timezone_settings(settings):
+ """确保时区一致。"""
+ settings.TIME_ZONE = 'UTC'
+
+@pytest.fixture
+def user(db):
+ """创建测试用户。"""
+ return User.objects.create_user(
+ email='test@example.com',
+ password='testpass123',
+ username='testuser'
+ )
+
+@pytest.fixture
+def admin_user(db):
+ """创建管理员用户。"""
+ return User.objects.create_superuser(
+ email='admin@example.com',
+ password='adminpass123',
+ username='admin'
+ )
+
+@pytest.fixture
+def authenticated_client(client, user):
+ """返回已认证的客户端。"""
+ client.force_login(user)
+ return client
+
+@pytest.fixture
+def api_client():
+ """返回 DRF API 客户端。"""
+ from rest_framework.test import APIClient
+ return APIClient()
+
+@pytest.fixture
+def authenticated_api_client(api_client, user):
+ """返回已认证的 API 客户端。"""
+ api_client.force_authenticate(user=user)
+ return api_client
+```
+
+## Factory Boy
+
+### 工厂配置
+
+```python
+# tests/factories.py
+import factory
+from factory import fuzzy
+from datetime import datetime, timedelta
+from django.contrib.auth import get_user_model
+from apps.products.models import Product, Category
+
+User = get_user_model()
+
+class UserFactory(factory.django.DjangoModelFactory):
+ """User 模型的工厂类。"""
+
+ class Meta:
+ model = User
+
+ email = factory.Sequence(lambda n: f"user{n}@example.com")
+ username = factory.Sequence(lambda n: f"user{n}")
+ password = factory.PostGenerationMethodCall('set_password', 'testpass123')
+ first_name = factory.Faker('first_name')
+ last_name = factory.Faker('last_name')
+ is_active = True
+
+class CategoryFactory(factory.django.DjangoModelFactory):
+ """Category 模型的工厂类。"""
+
+ class Meta:
+ model = Category
+
+ name = factory.Faker('word')
+ slug = factory.LazyAttribute(lambda obj: obj.name.lower())
+ description = factory.Faker('text')
+
+class ProductFactory(factory.django.DjangoModelFactory):
+ """Product 模型的工厂类。"""
+
+ class Meta:
+ model = Product
+
+ name = factory.Faker('sentence', nb_words=3)
+ slug = factory.LazyAttribute(lambda obj: obj.name.lower().replace(' ', '-'))
+ description = factory.Faker('text')
+ price = fuzzy.FuzzyDecimal(10.00, 1000.00, 2)
+ stock = fuzzy.FuzzyInteger(0, 100)
+ is_active = True
+ category = factory.SubFactory(CategoryFactory)
+ created_by = factory.SubFactory(UserFactory)
+
+ @factory.post_generation
+ def tags(self, create, extracted, **kwargs):
+ """为产品添加标签。"""
+ if not create:
+ return
+ if extracted:
+ for tag in extracted:
+ self.tags.add(tag)
+```
+
+### 使用工厂
+
+```python
+# tests/test_models.py
+import pytest
+from tests.factories import ProductFactory, UserFactory
+
+def test_product_creation():
+ """测试使用工厂创建产品。"""
+ product = ProductFactory(price=100.00, stock=50)
+ assert product.price == 100.00
+ assert product.stock == 50
+ assert product.is_active is True
+
+def test_product_with_tags():
+ """测试带有标签的产品。"""
+ tags = [TagFactory(name='electronics'), TagFactory(name='new')]
+ product = ProductFactory(tags=tags)
+ assert product.tags.count() == 2
+
+def test_multiple_products():
+ """测试创建多个产品。"""
+ products = ProductFactory.create_batch(10)
+ assert len(products) == 10
+```
+
+## 模型测试(Model Testing)
+
+### 模型测试用例
+
+```python
+# tests/test_models.py
+import pytest
+from django.core.exceptions import ValidationError
+from tests.factories import UserFactory, ProductFactory
+
+class TestUserModel:
+ """测试 User 模型。"""
+
+ def test_create_user(self, db):
+ """测试创建普通用户。"""
+ user = UserFactory(email='test@example.com')
+ assert user.email == 'test@example.com'
+ assert user.check_password('testpass123')
+ assert not user.is_staff
+ assert not user.is_superuser
+
+ def test_create_superuser(self, db):
+ """测试创建超级用户。"""
+ user = UserFactory(
+ email='admin@example.com',
+ is_staff=True,
+ is_superuser=True
+ )
+ assert user.is_staff
+ assert user.is_superuser
+
+ def test_user_str(self, db):
+ """测试用户字符串表示形式。"""
+ user = UserFactory(email='test@example.com')
+ assert str(user) == 'test@example.com'
+
+class TestProductModel:
+ """测试 Product 模型。"""
+
+ def test_product_creation(self, db):
+ """测试创建产品。"""
+ product = ProductFactory()
+ assert product.id is not None
+ assert product.is_active is True
+ assert product.created_at is not None
+
+ def test_product_slug_generation(self, db):
+ """测试 Slug 自动生成。"""
+ product = ProductFactory(name='Test Product')
+ assert product.slug == 'test-product'
+
+ def test_product_price_validation(self, db):
+ """测试价格不能为负数。"""
+ product = ProductFactory(price=-10)
+ with pytest.raises(ValidationError):
+ product.full_clean()
+
+ def test_product_manager_active(self, db):
+ """测试 active 管理器方法。"""
+ ProductFactory.create_batch(5, is_active=True)
+ ProductFactory.create_batch(3, is_active=False)
+
+ active_count = Product.objects.active().count()
+ assert active_count == 5
+
+ def test_product_stock_management(self, db):
+ """测试库存管理。"""
+ product = ProductFactory(stock=10)
+ product.reduce_stock(5)
+ product.refresh_from_db()
+ assert product.stock == 5
+
+ with pytest.raises(ValueError):
+ product.reduce_stock(10) # 库存不足
+```
+
+## 视图测试(View Testing)
+
+### Django 视图测试
+
+```python
+# tests/test_views.py
+import pytest
+from django.urls import reverse
+from tests.factories import ProductFactory, UserFactory
+
+class TestProductViews:
+ """测试产品视图。"""
+
+ def test_product_list(self, client, db):
+ """测试产品列表视图。"""
+ ProductFactory.create_batch(10)
+
+ response = client.get(reverse('products:list'))
+
+ assert response.status_code == 200
+ assert len(response.context['products']) == 10
+
+ def test_product_detail(self, client, db):
+ """测试产品详情视图。"""
+ product = ProductFactory()
+
+ response = client.get(reverse('products:detail', kwargs={'slug': product.slug}))
+
+ assert response.status_code == 200
+ assert response.context['product'] == product
+
+ def test_product_create_requires_login(self, client, db):
+ """测试创建产品需要登录认证。"""
+ response = client.get(reverse('products:create'))
+
+ assert response.status_code == 302
+ assert response.url.startswith('/accounts/login/')
+
+ def test_product_create_authenticated(self, authenticated_client, db):
+ """测试以已认证用户身份创建产品。"""
+ response = authenticated_client.get(reverse('products:create'))
+
+ assert response.status_code == 200
+
+ def test_product_create_post(self, authenticated_client, db, category):
+ """测试通过 POST 请求创建产品。"""
+ data = {
+ 'name': 'Test Product',
+ 'description': 'A test product',
+ 'price': '99.99',
+ 'stock': 10,
+ 'category': category.id,
+ }
+
+ response = authenticated_client.post(reverse('products:create'), data)
+
+ assert response.status_code == 302
+ assert Product.objects.filter(name='Test Product').exists()
+```
+
+## DRF API 测试
+
+### 序列化器测试
+
+```python
+# tests/test_serializers.py
+import pytest
+from rest_framework.exceptions import ValidationError
+from apps.products.serializers import ProductSerializer
+from tests.factories import ProductFactory
+
+class TestProductSerializer:
+ """测试 ProductSerializer。"""
+
+ def test_serialize_product(self, db):
+ """测试序列化产品对象。"""
+ product = ProductFactory()
+ serializer = ProductSerializer(product)
+
+ data = serializer.data
+
+ assert data['id'] == product.id
+ assert data['name'] == product.name
+ assert data['price'] == str(product.price)
+
+ def test_deserialize_product(self, db):
+ """测试反序列化产品数据。"""
+ data = {
+ 'name': 'Test Product',
+ 'description': 'Test description',
+ 'price': '99.99',
+ 'stock': 10,
+ 'category': 1,
+ }
+
+ serializer = ProductSerializer(data=data)
+
+ assert serializer.is_valid()
+ product = serializer.save()
+
+ assert product.name == 'Test Product'
+ assert float(product.price) == 99.99
+
+ def test_price_validation(self, db):
+ """测试价格验证。"""
+ data = {
+ 'name': 'Test Product',
+ 'price': '-10.00',
+ 'stock': 10,
+ }
+
+ serializer = ProductSerializer(data=data)
+
+ assert not serializer.is_valid()
+ assert 'price' in serializer.errors
+
+ def test_stock_validation(self, db):
+ """测试库存不能为负数。"""
+ data = {
+ 'name': 'Test Product',
+ 'price': '99.99',
+ 'stock': -5,
+ }
+
+ serializer = ProductSerializer(data=data)
+
+ assert not serializer.is_valid()
+ assert 'stock' in serializer.errors
+```
+
+### API ViewSet 测试
+
+```python
+# tests/test_api.py
+import pytest
+from rest_framework.test import APIClient
+from rest_framework import status
+from django.urls import reverse
+from tests.factories import ProductFactory, UserFactory
+
+class TestProductAPI:
+ """测试产品 API 接口。"""
+
+ @pytest.fixture
+ def api_client(self):
+ """返回 API 客户端。"""
+ return APIClient()
+
+ def test_list_products(self, api_client, db):
+ """测试列出产品。"""
+ ProductFactory.create_batch(10)
+
+ url = reverse('api:product-list')
+ response = api_client.get(url)
+
+ assert response.status_code == status.HTTP_200_OK
+ assert response.data['count'] == 10
+
+ def test_retrieve_product(self, api_client, db):
+ """测试获取单个产品详情。"""
+ product = ProductFactory()
+
+ url = reverse('api:product-detail', kwargs={'pk': product.id})
+ response = api_client.get(url)
+
+ assert response.status_code == status.HTTP_200_OK
+ assert response.data['id'] == product.id
+
+ def test_create_product_unauthorized(self, api_client, db):
+ """测试未授权时创建产品。"""
+ url = reverse('api:product-list')
+ data = {'name': 'Test Product', 'price': '99.99'}
+
+ response = api_client.post(url, data)
+
+ assert response.status_code == status.HTTP_401_UNAUTHORIZED
+
+ def test_create_product_authorized(self, authenticated_api_client, db):
+ """测试以已认证用户身份创建产品。"""
+ url = reverse('api:product-list')
+ data = {
+ 'name': 'Test Product',
+ 'description': 'Test',
+ 'price': '99.99',
+ 'stock': 10,
+ }
+
+ response = authenticated_api_client.post(url, data)
+
+ assert response.status_code == status.HTTP_201_CREATED
+ assert response.data['name'] == 'Test Product'
+
+ def test_update_product(self, authenticated_api_client, db):
+ """测试更新产品。"""
+ product = ProductFactory(created_by=authenticated_api_client.user)
+
+ url = reverse('api:product-detail', kwargs={'pk': product.id})
+ data = {'name': 'Updated Product'}
+
+ response = authenticated_api_client.patch(url, data)
+
+ assert response.status_code == status.HTTP_200_OK
+ assert response.data['name'] == 'Updated Product'
+
+ def test_delete_product(self, authenticated_api_client, db):
+ """测试删除产品。"""
+ product = ProductFactory(created_by=authenticated_api_client.user)
+
+ url = reverse('api:product-detail', kwargs={'pk': product.id})
+ response = authenticated_api_client.delete(url)
+
+ assert response.status_code == status.HTTP_204_NO_CONTENT
+
+ def test_filter_products_by_price(self, api_client, db):
+ """测试按价格过滤产品。"""
+ ProductFactory(price=50)
+ ProductFactory(price=150)
+
+ url = reverse('api:product-list')
+ response = api_client.get(url, {'price_min': 100})
+
+ assert response.status_code == status.HTTP_200_OK
+ assert response.data['count'] == 1
+
+ def test_search_products(self, api_client, db):
+ """测试搜索产品。"""
+ ProductFactory(name='Apple iPhone')
+ ProductFactory(name='Samsung Galaxy')
+
+ url = reverse('api:product-list')
+ response = api_client.get(url, {'search': 'Apple'})
+
+ assert response.status_code == status.HTTP_200_OK
+ assert response.data['count'] == 1
+```
+
+## Mock 模拟与补丁(Mocking and Patching)
+
+### 模拟外部服务
+
+```python
+# tests/test_views.py
+from unittest.mock import patch, Mock
+import pytest
+
+class TestPaymentView:
+ """使用模拟支付网关测试支付视图。"""
+
+ @patch('apps.payments.services.stripe')
+ def test_successful_payment(self, mock_stripe, client, user, product):
+ """使用模拟的 Stripe 测试成功支付。"""
+ # 配置模拟对象
+ mock_stripe.Charge.create.return_value = {
+ 'id': 'ch_123',
+ 'status': 'succeeded',
+ 'amount': 9999,
+ }
+
+ client.force_login(user)
+ response = client.post(reverse('payments:process'), {
+ 'product_id': product.id,
+ 'token': 'tok_visa',
+ })
+
+ assert response.status_code == 302
+ mock_stripe.Charge.create.assert_called_once()
+
+ @patch('apps.payments.services.stripe')
+ def test_failed_payment(self, mock_stripe, client, user, product):
+ """测试支付失败。"""
+ mock_stripe.Charge.create.side_effect = Exception('Card declined')
+
+ client.force_login(user)
+ response = client.post(reverse('payments:process'), {
+ 'product_id': product.id,
+ 'token': 'tok_visa',
+ })
+
+ assert response.status_code == 302
+ assert 'error' in response.url
+```
+
+### 模拟邮件发送
+
+```python
+# tests/test_email.py
+from django.core import mail
+from django.test import override_settings
+
+@override_settings(EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend')
+def test_order_confirmation_email(db, order):
+ """测试订单确认邮件。"""
+ order.send_confirmation_email()
+
+ assert len(mail.outbox) == 1
+ assert order.user.email in mail.outbox[0].to
+ assert 'Order Confirmation' in mail.outbox[0].subject
+```
+
+## 集成测试(Integration Testing)
+
+### 全流程测试
+
+```python
+# tests/test_integration.py
+import pytest
+from django.urls import reverse
+from tests.factories import UserFactory, ProductFactory
+
+class TestCheckoutFlow:
+ """测试完整的结账流程。"""
+
+ def test_guest_to_purchase_flow(self, client, db):
+ """测试从游客到购买完成的完整流程。"""
+ # 步骤 1:注册
+ response = client.post(reverse('users:register'), {
+ 'email': 'test@example.com',
+ 'password': 'testpass123',
+ 'password_confirm': 'testpass123',
+ })
+ assert response.status_code == 302
+
+ # 步骤 2:登录
+ response = client.post(reverse('users:login'), {
+ 'email': 'test@example.com',
+ 'password': 'testpass123',
+ })
+ assert response.status_code == 302
+
+ # 步骤 3:浏览产品
+ product = ProductFactory(price=100)
+ response = client.get(reverse('products:detail', kwargs={'slug': product.slug}))
+ assert response.status_code == 200
+
+ # 步骤 4:添加到购物车
+ response = client.post(reverse('cart:add'), {
+ 'product_id': product.id,
+ 'quantity': 1,
+ })
+ assert response.status_code == 302
+
+ # 步骤 5:结账
+ response = client.get(reverse('checkout:review'))
+ assert response.status_code == 200
+ assert product.name in response.content.decode()
+
+ # 步骤 6:完成购买
+ with patch('apps.checkout.services.process_payment') as mock_payment:
+ mock_payment.return_value = True
+ response = client.post(reverse('checkout:complete'))
+
+ assert response.status_code == 302
+ assert Order.objects.filter(user__email='test@example.com').exists()
+```
+
+## 测试最佳实践
+
+### 推荐做法(DO)
+
+- **使用工厂(Factories)**:避免手动创建对象。
+- **每个测试一个断言**:保持测试专注。
+- **描述性测试名称**:如 `test_user_cannot_delete_others_post`。
+- **测试边界情况**:空输入、None 值、边界条件。
+- **模拟(Mock)外部服务**:不要依赖外部 API。
+- **使用 Fixtures**:消除重复代码。
+- **测试权限控制**:确保授权逻辑正常工作。
+- **保持测试运行迅速**:使用 `--reuse-db` 和 `--nomigrations`。
+
+### 避免做法(DON'T)
+
+- **不要测试 Django 内部机制**:相信 Django 自身已通过测试。
+- **不要测试第三方库代码**:相信库作者的测试。
+- **不要忽略失败的测试**:所有测试都必须通过。
+- **不要让测试产生依赖**:测试应该可以以任何顺序运行。
+- **不要过度模拟**:仅对外部依赖项进行 Mock。
+- **不要测试私有方法**:测试公共接口。
+- **不要使用生产数据库**:始终使用专用测试数据库。
+
+## 覆盖率(Coverage)
+
+### 覆盖率配置
+
+```bash
+# 运行带有覆盖率统计的测试
+pytest --cov=apps --cov-report=html --cov-report=term-missing
+
+# 生成并查看 HTML 报告
+open htmlcov/index.html
+```
+
+### 覆盖率目标
+
+| 组件 | 目标覆盖率 |
+|-----------|-----------------|
+| 模型 (Models) | 90%+ |
+| 序列化器 (Serializers) | 85%+ |
+| 视图 (Views) | 80%+ |
+| 服务层 (Services) | 90%+ |
+| 工具类 (Utilities) | 80%+ |
+| 总体 (Overall) | 80%+ |
+
+## 快速参考
+
+| 模式 | 用法 |
+|---------|-------|
+| `@pytest.mark.django_db` | 启用数据库访问 |
+| `client` | Django 测试客户端 |
+| `api_client` | DRF API 客户端 |
+| `factory.create_batch(n)` | 创建多个对象 |
+| `patch('module.function')` | 模拟外部依赖 |
+| `override_settings` | 临时更改设置 |
+| `force_authenticate()` | 在测试中绕过认证 |
+| `assertRedirects` | 检查重定向 |
+| `assertTemplateUsed` | 验证模板使用情况 |
+| `mail.outbox` | 检查已发送的邮件 |
+
+请记住:测试即文档。良好的测试能够解释代码的预期工作方式。保持测试简单、易读且易于维护。
diff --git a/skills/django-verification/SKILL.md b/skills/django-verification/SKILL.md
new file mode 100644
index 0000000..4a12e9f
--- /dev/null
+++ b/skills/django-verification/SKILL.md
@@ -0,0 +1,460 @@
+---
+name: django-verification
+description: Django 项目的验证循环(Verification loop):包含数据库迁移、代码检查、带覆盖率的测试、安全扫描,以及在发布或 PR 前的部署就绪检查。
+---
+
+# Django 验证循环(Verification Loop)
+
+在提交 PR 之前、重大变更之后以及预部署(Pre-deploy)之前运行,以确保 Django 应用程序的质量和安全性。
+
+## 阶段 1:环境检查 (Phase 1: Environment Check)
+
+```bash
+# 验证 Python 版本
+python --version # 应符合项目要求
+
+# 检查虚拟环境
+which python
+pip list --outdated
+
+# 验证环境变量
+python -c "import os; import environ; print('DJANGO_SECRET_KEY set' if os.environ.get('DJANGO_SECRET_KEY') else 'MISSING: DJANGO_SECRET_KEY')"
+```
+
+如果环境配置错误,请停止并修复。
+
+## 阶段 2:代码质量与格式化 (Phase 2: Code Quality & Formatting)
+
+```bash
+# 类型检查
+mypy . --config-file pyproject.toml
+
+# 使用 ruff 进行 Lint 检查
+ruff check . --fix
+
+# 使用 black 进行格式化
+black . --check
+black . # 自动修复
+
+# 导入语句排序
+isort . --check-only
+isort . # 自动修复
+
+# Django 特有的检查
+python manage.py check --deploy
+```
+
+常见问题:
+- 公共函数缺失类型提示(Type hints)
+- 违反 PEP 8 格式规范
+- 未排序的导入语句
+- 生产环境配置中遗留了调试设置
+
+## 阶段 3:数据库迁移 (Phase 3: Migrations)
+
+```bash
+# 检查是否存在未应用的迁移
+python manage.py showmigrations
+
+# 创建缺失的迁移
+python manage.py makemigrations --check
+
+# 模拟迁移应用(Dry-run)
+python manage.py migrate --plan
+
+# 应用迁移(测试环境)
+python manage.py migrate
+
+# 检查迁移冲突
+python manage.py makemigrations --merge # 仅在存在冲突时运行
+```
+
+报告内容:
+- 待处理迁移的数量
+- 任何迁移冲突
+- 存在模型变更但未生成迁移
+
+## 阶段 4:测试与覆盖率 (Phase 4: Tests + Coverage)
+
+```bash
+# 使用 pytest 运行所有测试
+pytest --cov=apps --cov-report=html --cov-report=term-missing --reuse-db
+
+# 运行特定应用的测试
+pytest apps/users/tests/
+
+# 使用标记运行
+pytest -m "not slow" # 跳过慢速测试
+pytest -m integration # 仅运行集成测试
+
+# 覆盖率报告
+open htmlcov/index.html
+```
+
+报告内容:
+- 测试统计:X 通过,Y 失败,Z 跳过
+- 总体覆盖率:XX%
+- 各应用的覆盖率明细
+
+覆盖率目标:
+
+| 组件 | 目标 |
+|-----------|--------|
+| 模型 (Models) | 90%+ |
+| 序列化器 (Serializers) | 85%+ |
+| 视图 (Views) | 80%+ |
+| 服务 (Services) | 90%+ |
+| 总体 | 80%+ |
+
+## 阶段 5:安全扫描 (Phase 5: Security Scan)
+
+```bash
+# 依赖项漏洞扫描
+pip-audit
+safety check --full-report
+
+# Django 安全检查
+python manage.py check --deploy
+
+# Bandit 安全 Linter
+bandit -r . -f json -o bandit-report.json
+
+# 密钥扫描(如果安装了 gitleaks)
+gitleaks detect --source . --verbose
+
+# 环境变量检查
+python -c "from django.core.exceptions import ImproperlyConfigured; from django.conf import settings; settings.DEBUG"
+```
+
+报告内容:
+- 发现的有漏洞的依赖项
+- 安全配置问题
+- 检测到的硬编码密钥
+- DEBUG 模式状态(在生产环境中应为 False)
+
+## 阶段 6:Django 管理命令 (Phase 6: Django Management Commands)
+
+```bash
+# 检查模型问题
+python manage.py check
+
+# 收集静态文件
+python manage.py collectstatic --noinput --clear
+
+# 创建超级用户(如果测试需要)
+echo "from apps.users.models import User; User.objects.create_superuser('admin@example.com', 'admin')" | python manage.py shell
+
+# 数据库完整性检查
+python manage.py check --database default
+
+# 缓存验证(如果使用 Redis)
+python -c "from django.core.cache import cache; cache.set('test', 'value', 10); print(cache.get('test'))"
+```
+
+## 阶段 7:性能检查 (Phase 7: Performance Checks)
+
+```bash
+# Django Debug Toolbar 输出(检查 N+1 查询)
+# 在 DEBUG=True 的开发模式下运行并访问页面
+# 在 SQL 面板中查找重复查询
+
+# 查询计数分析
+django-admin debugsqlshell # 如果安装了 django-debug-sqlshell
+
+# 检查缺失的索引
+python manage.py shell << EOF
+from django.db import connection
+with connection.cursor() as cursor:
+ cursor.execute("SELECT table_name, index_name FROM information_schema.statistics WHERE table_schema = 'public'")
+ print(cursor.fetchall())
+EOF
+```
+
+报告内容:
+- 每个页面的查询数量(典型页面应 < 50)
+- 缺失的数据库索引
+- 检测到的重复查询
+
+## 阶段 8:静态资源 (Phase 8: Static Assets)
+
+```bash
+# 检查 npm 依赖项(如果使用 npm)
+npm audit
+npm audit fix
+
+# 构建静态文件(如果使用 webpack/vite)
+npm run build
+
+# 验证静态文件
+ls -la staticfiles/
+python manage.py findstatic css/style.css
+```
+
+## 阶段 9:配置审查 (Phase 9: Configuration Review)
+
+```python
+# 在 Python shell 中运行以验证设置
+python manage.py shell << EOF
+from django.conf import settings
+import os
+
+# 关键检查项
+checks = {
+ 'DEBUG is False': not settings.DEBUG,
+ 'SECRET_KEY set': bool(settings.SECRET_KEY and len(settings.SECRET_KEY) > 30),
+ 'ALLOWED_HOSTS set': len(settings.ALLOWED_HOSTS) > 0,
+ 'HTTPS enabled': getattr(settings, 'SECURE_SSL_REDIRECT', False),
+ 'HSTS enabled': getattr(settings, 'SECURE_HSTS_SECONDS', 0) > 0,
+ 'Database configured': settings.DATABASES['default']['ENGINE'] != 'django.db.backends.sqlite3',
+}
+
+for check, result in checks.items():
+ status = '✓' if result else '✗'
+ print(f"{status} {check}")
+EOF
+```
+
+## 阶段 10:日志配置 (Phase 10: Logging Configuration)
+
+```bash
+# 测试日志输出
+python manage.py shell << EOF
+import logging
+logger = logging.getLogger('django')
+logger.warning('Test warning message')
+logger.error('Test error message')
+EOF
+
+# 检查日志文件(如果已配置)
+tail -f /var/log/django/django.log
+```
+
+## 阶段 11:API 文档 (Phase 11: API Documentation)
+
+```bash
+# 生成 Schema
+python manage.py generateschema --format openapi-json > schema.json
+
+# 验证 Schema
+# 检查 schema.json 是否为有效的 JSON
+python -c "import json; json.load(open('schema.json'))"
+
+# 访问 Swagger UI(如果使用 drf-yasg)
+# 在浏览器中访问 http://localhost:8000/swagger/
+```
+
+## 阶段 12:差异审查 (Phase 12: Diff Review)
+
+```bash
+# 显示 diff 统计信息
+git diff --stat
+
+# 显示实际变更
+git diff
+
+# 显示已变更的文件
+git diff --name-only
+
+# 检查常见问题
+git diff | grep -i "todo\|fixme\|hack\|xxx"
+git diff | grep "print(" # 调试语句
+git diff | grep "DEBUG = True" # 调试模式
+git diff | grep "import pdb" # 调试器
+```
+
+检查清单:
+- 无调试语句(print, pdb, breakpoint())
+- 关键代码中无 TODO/FIXME 注释
+- 无硬编码的密钥或凭据
+- 模型变更已包含数据库迁移
+- 配置变更已记录文档
+- 外部调用已包含错误处理
+- 视需要包含事务管理(Transaction management)
+
+## 输出模板 (Output Template)
+
+```
+DJANGO 验证报告
+==========================
+
+阶段 1:环境检查 (Phase 1: Environment Check)
+ ✓ Python 3.11.5
+ ✓ 虚拟环境已激活
+ ✓ 所有环境变量已设置
+
+阶段 2:代码质量 (Phase 2: Code Quality)
+ ✓ mypy: 无类型错误
+ ✗ ruff: 发现 3 个问题(已自动修复)
+ ✓ black: 无格式化问题
+ ✓ isort: 导入语句已排序
+ ✓ manage.py check: 无问题
+
+阶段 3:数据库迁移 (Phase 3: Migrations)
+ ✓ 无未应用的迁移
+ ✓ 无迁移冲突
+ ✓ 所有模型均有迁移
+
+阶段 4:测试与覆盖率 (Phase 4: Tests + Coverage)
+ 测试:247 通过,0 失败,5 跳过
+ 覆盖率:
+ 总体:87%
+ users: 92%
+ products: 89%
+ orders: 85%
+ payments: 91%
+
+阶段 5:安全扫描 (Phase 5: Security Scan)
+ ✗ pip-audit: 发现 2 个漏洞(需要修复)
+ ✓ safety check: 无问题
+ ✓ bandit: 无安全问题
+ ✓ 未检测到密钥
+ ✓ DEBUG = False
+
+阶段 6:Django 命令 (Phase 6: Django Commands)
+ ✓ collectstatic 已完成
+ ✓ 数据库完整性正常
+ ✓ 缓存后端可连接
+
+阶段 7:性能 (Phase 7: Performance)
+ ✓ 未检测到 N+1 查询
+ ✓ 数据库索引已配置
+ ✓ 查询计数在可接受范围内
+
+阶段 8:静态资源 (Phase 8: Static Assets)
+ ✓ npm audit: 无漏洞
+ ✓ 资源构建成功
+ ✓ 静态文件已收集
+
+阶段 9:配置审查 (Phase 9: Configuration)
+ ✓ DEBUG = False
+ ✓ SECRET_KEY 已配置
+ ✓ ALLOWED_HOSTS 已设置
+ ✓ HTTPS 已启用
+ ✓ HSTS 已启用
+ ✓ 数据库已配置
+
+阶段 10:日志 (Phase 10: Logging)
+ ✓ 日志已配置
+ ✓ 日志文件可写
+
+阶段 11:API 文档 (Phase 11: API Documentation)
+ ✓ Schema 已生成
+ ✓ Swagger UI 可访问
+
+阶段 12:差异审查 (Phase 12: Diff Review)
+ 文件变更:12
+ +450, -120 行
+ ✓ 无调试语句
+ ✓ 无硬编码密钥
+ ✓ 已包含迁移
+
+建议:⚠️ 在部署前修复 pip-audit 漏洞
+
+后续步骤:
+1. 更新有漏洞的依赖项
+2. 重新运行安全扫描
+3. 部署到预发布环境进行最终测试
+```
+
+## 预部署检查清单 (Pre-Deployment Checklist)
+
+- [ ] 所有测试均通过
+- [ ] 覆盖率 ≥ 80%
+- [ ] 无安全漏洞
+- [ ] 无未应用的迁移
+- [ ] 生产环境设置中 DEBUG = False
+- [ ] SECRET_KEY 已正确配置
+- [ ] ALLOWED_HOSTS 已正确设置
+- [ ] 数据库备份已启用
+- [ ] 静态文件已收集并提供服务
+- [ ] 日志已配置并正常工作
+- [ ] 错误监控(Sentry 等)已配置
+- [ ] CDN 已配置(如适用)
+- [ ] Redis/缓存后端已配置
+- [ ] Celery worker 正在运行(如适用)
+- [ ] HTTPS/SSL 已配置
+- [ ] 环境变量已记录文档
+
+## 持续集成 (Continuous Integration)
+
+### GitHub Actions 示例
+
+```yaml
+# .github/workflows/django-verification.yml
+name: Django Verification
+
+on: [push, pull_request]
+
+jobs:
+ verify:
+ runs-on: ubuntu-latest
+ services:
+ postgres:
+ image: postgres:14
+ env:
+ POSTGRES_PASSWORD: postgres
+ options: >-
+ --health-cmd pg_isready
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.11'
+
+ - name: Cache pip
+ uses: actions/cache@v3
+ with:
+ path: ~/.cache/pip
+ key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
+
+ - name: Install dependencies
+ run: |
+ pip install -r requirements.txt
+ pip install ruff black mypy pytest pytest-django pytest-cov bandit safety pip-audit
+
+ - name: Code quality checks
+ run: |
+ ruff check .
+ black . --check
+ isort . --check-only
+ mypy .
+
+ - name: Security scan
+ run: |
+ bandit -r . -f json -o bandit-report.json
+ safety check --full-report
+ pip-audit
+
+ - name: Run tests
+ env:
+ DATABASE_URL: postgres://postgres:postgres@localhost:5432/test
+ DJANGO_SECRET_KEY: test-secret-key
+ run: |
+ pytest --cov=apps --cov-report=xml --cov-report=term-missing
+
+ - name: Upload coverage
+ uses: codecov/codecov-action@v3
+```
+
+## 快速参考 (Quick Reference)
+
+| 检查项 | 命令 |
+|-------|---------|
+| 环境 (Environment) | `python --version` |
+| 类型检查 (Type checking) | `mypy .` |
+| 代码检查 (Linting) | `ruff check .` |
+| 格式化 (Formatting) | `black . --check` |
+| 数据库迁移 (Migrations) | `python manage.py makemigrations --check` |
+| 测试 (Tests) | `pytest --cov=apps` |
+| 安全 (Security) | `pip-audit && bandit -r .` |
+| Django 检查 (Django check) | `python manage.py check --deploy` |
+| 收集静态资源 (Collectstatic) | `python manage.py collectstatic --noinput` |
+| 差异统计 (Diff stats) | `git diff --stat` |
+
+记住:自动化验证可以捕捉常见问题,但不能取代手动代码审查和在预发布环境(Staging environment)中的测试。
diff --git a/skills/golang-patterns/SKILL.md b/skills/golang-patterns/SKILL.md
index 8029762..d97ee0c 100644
--- a/skills/golang-patterns/SKILL.md
+++ b/skills/golang-patterns/SKILL.md
@@ -672,4 +672,4 @@ func (c *Counter) Increment() { c.n++ } // 指针接收者
**记住**:Go 代码应当以一种“最乏味”的方式呈现——它是可预测的、一致的且易于理解的。如有疑问,请保持简单。
-```
+```
\ No newline at end of file
diff --git a/skills/java-coding-standards/SKILL.md b/skills/java-coding-standards/SKILL.md
index 9a03a41..d8b266f 100644
--- a/skills/java-coding-standards/SKILL.md
+++ b/skills/java-coding-standards/SKILL.md
@@ -1,91 +1,91 @@
---
name: java-coding-standards
-description: Java coding standards for Spring Boot services: naming, immutability, Optional usage, streams, exceptions, generics, and project layout.
+description: Spring Boot 服务的 Java 编码规范:命名、不可变性(Immutability)、Optional 使用、流(Streams)、异常处理、泛型(Generics)和项目布局。
---
-# Java Coding Standards
+# Java 编码规范
-Standards for readable, maintainable Java (17+) code in Spring Boot services.
+适用于 Spring Boot 服务中易读、可维护的 Java (17+) 代码规范。
-## Core Principles
+## 核心原则
-- Prefer clarity over cleverness
-- Immutable by default; minimize shared mutable state
-- Fail fast with meaningful exceptions
-- Consistent naming and package structure
+- 清晰胜过奇巧
+- 默认不可变(Immutable);尽量减少共享的可变状态
+- 快速失败并抛出有意义的异常
+- 保持一致的命名和包结构
-## Naming
+## 命名
```java
-// ✅ Classes/Records: PascalCase
+// ✅ 类(Classes)/ 记录(Records):大驼峰式(PascalCase)
public class MarketService {}
public record Money(BigDecimal amount, Currency currency) {}
-// ✅ Methods/fields: camelCase
+// ✅ 方法/字段:小驼峰式(camelCase)
private final MarketRepository marketRepository;
public Market findBySlug(String slug) {}
-// ✅ Constants: UPPER_SNAKE_CASE
+// ✅ 常量:大写下划线命名式(UPPER_SNAKE_CASE)
private static final int MAX_PAGE_SIZE = 100;
```
-## Immutability
+## 不可变性(Immutability)
```java
-// ✅ Favor records and final fields
+// ✅ 优先使用记录(Records)和 final 字段
public record MarketDto(Long id, String name, MarketStatus status) {}
public class Market {
private final Long id;
private final String name;
- // getters only, no setters
+ // 仅提供 getter,不提供 setter
}
```
-## Optional Usage
+## Optional 使用
```java
-// ✅ Return Optional from find* methods
+// ✅ find* 方法返回 Optional
Optional market = marketRepository.findBySlug(slug);
-// ✅ Map/flatMap instead of get()
+// ✅ 使用 map/flatMap 而不是 get()
return market
.map(MarketResponse::from)
.orElseThrow(() -> new EntityNotFoundException("Market not found"));
```
-## Streams Best Practices
+## 流(Streams)最佳实践
```java
-// ✅ Use streams for transformations, keep pipelines short
+// ✅ 使用流进行转换,保持流水线简短
List names = markets.stream()
.map(Market::name)
.filter(Objects::nonNull)
.toList();
-// ❌ Avoid complex nested streams; prefer loops for clarity
+// ❌ 避免复杂的嵌套流;为了清晰起见,优先使用循环
```
-## Exceptions
+## 异常处理
-- Use unchecked exceptions for domain errors; wrap technical exceptions with context
-- Create domain-specific exceptions (e.g., `MarketNotFoundException`)
-- Avoid broad `catch (Exception ex)` unless rethrowing/logging centrally
+- 领域错误使用非受检异常(Unchecked Exceptions);为技术异常包装上下文信息
+- 创建领域特定的异常(例如 `MarketNotFoundException`)
+- 避免宽泛的 `catch (Exception ex)`,除非是进行集中式重抛(Rethrow)或日志记录
```java
throw new MarketNotFoundException(slug);
```
-## Generics and Type Safety
+## 泛型与类型安全
-- Avoid raw types; declare generic parameters
-- Prefer bounded generics for reusable utilities
+- 避免原始类型(Raw Types);声明泛型参数
+- 可复用工具类优先使用有界泛型(Bounded Generics)
```java
public Map indexById(Collection items) { ... }
```
-## Project Structure (Maven/Gradle)
+## 项目结构 (Maven/Gradle)
```
src/main/java/com/example/app/
@@ -98,25 +98,25 @@ src/main/java/com/example/app/
util/
src/main/resources/
application.yml
-src/test/java/... (mirrors main)
+src/test/java/... (结构与 main 保持镜像)
```
-## Formatting and Style
+## 格式与风格
-- Use 2 or 4 spaces consistently (project standard)
-- One public top-level type per file
-- Keep methods short and focused; extract helpers
-- Order members: constants, fields, constructors, public methods, protected, private
+- 一致地使用 2 或 4 个空格(遵循项目标准)
+- 每个文件仅包含一个公共顶层类型
+- 保持方法简短且聚焦;提取辅助方法(Helper methods)
+- 成员排序:常量、字段、构造函数、公共方法、受保护方法、私有方法
-## Code Smells to Avoid
+## 需避免的代码坏味道(Code Smells)
-- Long parameter lists → use DTO/builders
-- Deep nesting → early returns
-- Magic numbers → named constants
-- Static mutable state → prefer dependency injection
-- Silent catch blocks → log and act or rethrow
+- 长参数列表 → 使用 DTO/构建器(Builders)
+- 深层嵌套 → 尽早返回(Early returns)
+- 魔法数字 → 具名常量
+- 静态可变状态 → 优先使用依赖注入(Dependency Injection)
+- 静默的 catch 块 → 记录日志并处理或重抛
-## Logging
+## 日志记录
```java
private static final Logger log = LoggerFactory.getLogger(MarketService.class);
@@ -124,15 +124,15 @@ log.info("fetch_market slug={}", slug);
log.error("failed_fetch_market slug={}", slug, ex);
```
-## Null Handling
+## 空值处理(Null Handling)
-- Accept `@Nullable` only when unavoidable; otherwise use `@NonNull`
-- Use Bean Validation (`@NotNull`, `@NotBlank`) on inputs
+- 仅在不可避免时接受 `@Nullable`;否则使用 `@NonNull`
+- 在输入上使用 Bean 校验(`@NotNull`, `@NotBlank`)
-## Testing Expectations
+## 测试预期
-- JUnit 5 + AssertJ for fluent assertions
-- Mockito for mocking; avoid partial mocks where possible
-- Favor deterministic tests; no hidden sleeps
+- 使用 JUnit 5 + AssertJ 进行流式断言(Fluent Assertions)
+- 使用 Mockito 进行模拟(Mocking);尽可能避免部分模拟(Partial Mocks)
+- 倾向于确定性测试;不要包含隐藏的 sleep 操作
-**Remember**: Keep code intentional, typed, and observable. Optimize for maintainability over micro-optimizations unless proven necessary.
+**记住**:保持代码的意图清晰、类型安全且具有可观测性。除非证明有必要,否则应优先考虑可维护性而非微优化。
diff --git a/skills/jpa-patterns/SKILL.md b/skills/jpa-patterns/SKILL.md
index 2bf3213..217541c 100644
--- a/skills/jpa-patterns/SKILL.md
+++ b/skills/jpa-patterns/SKILL.md
@@ -1,13 +1,13 @@
---
name: jpa-patterns
-description: JPA/Hibernate patterns for entity design, relationships, query optimization, transactions, auditing, indexing, pagination, and pooling in Spring Boot.
+description: Spring Boot 中用于实体设计、关联关系、查询优化、事务、审计、索引、分页和连接池的 JPA/Hibernate 模式。
---
-# JPA/Hibernate Patterns
+# JPA/Hibernate 模式
-Use for data modeling, repositories, and performance tuning in Spring Boot.
+用于 Spring Boot 中的数据建模、存储层(Repositories)开发和性能调优。
-## Entity Design
+## 实体设计(Entity Design)
```java
@Entity
@@ -33,29 +33,29 @@ public class MarketEntity {
}
```
-Enable auditing:
+启用审计(Auditing):
```java
@Configuration
@EnableJpaAuditing
class JpaConfig {}
```
-## Relationships and N+1 Prevention
+## 关联关系与 N+1 问题预防
```java
@OneToMany(mappedBy = "market", cascade = CascadeType.ALL, orphanRemoval = true)
private List positions = new ArrayList<>();
```
-- Default to lazy loading; use `JOIN FETCH` in queries when needed
-- Avoid `EAGER` on collections; use DTO projections for read paths
+- 默认使用懒加载(Lazy loading);必要时在查询中使用 `JOIN FETCH`
+- 避免在集合上使用立即加载(`EAGER`);对于读取路径,优先使用 DTO 投影(Projections)
```java
@Query("select m from MarketEntity m left join fetch m.positions where m.id = :id")
Optional findWithPositions(@Param("id") Long id);
```
-## Repository Patterns
+## 存储层模式(Repository Patterns)
```java
public interface MarketRepository extends JpaRepository {
@@ -66,7 +66,7 @@ public interface MarketRepository extends JpaRepository {
}
```
-- Use projections for lightweight queries:
+- 使用投影(Projections)进行轻量级查询:
```java
public interface MarketSummary {
Long getId();
@@ -76,11 +76,11 @@ public interface MarketSummary {
Page findAllBy(Pageable pageable);
```
-## Transactions
+## 事务(Transactions)
-- Annotate service methods with `@Transactional`
-- Use `@Transactional(readOnly = true)` for read paths to optimize
-- Choose propagation carefully; avoid long-running transactions
+- 使用 `@Transactional` 注解 Service 方法
+- 在读取路径上使用 `@Transactional(readOnly = true)` 进行优化
+- 谨慎选择传播行为(Propagation);避免长事务
```java
@Transactional
@@ -92,25 +92,25 @@ public Market updateStatus(Long id, MarketStatus status) {
}
```
-## Pagination
+## 分页(Pagination)
```java
PageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by("createdAt").descending());
Page markets = repo.findByStatus(MarketStatus.ACTIVE, page);
```
-For cursor-like pagination, include `id > :lastId` in JPQL with ordering.
+对于游标式分页(Cursor-like pagination),请在 JPQL 中包含 `id > :lastId` 并配合排序。
-## Indexing and Performance
+## 索引与性能
-- Add indexes for common filters (`status`, `slug`, foreign keys)
-- Use composite indexes matching query patterns (`status, created_at`)
-- Avoid `select *`; project only needed columns
-- Batch writes with `saveAll` and `hibernate.jdbc.batch_size`
+- 为常用过滤器(`status`、`slug`、外键)添加索引(Indexing)
+- 使用匹配查询模式的复合索引(Composite indexes,如 `status, created_at`)
+- 避免使用 `select *`;仅投影所需的列
+- 使用 `saveAll` 并配置 `hibernate.jdbc.batch_size` 进行批量写入
-## Connection Pooling (HikariCP)
+## 连接池(Connection Pooling - HikariCP)
-Recommended properties:
+推荐属性:
```
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
@@ -118,24 +118,24 @@ spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.validation-timeout=5000
```
-For PostgreSQL LOB handling, add:
+对于 PostgreSQL 的 LOB 处理,请添加:
```
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
```
-## Caching
+## 缓存(Caching)
-- 1st-level cache is per EntityManager; avoid keeping entities across transactions
-- For read-heavy entities, consider second-level cache cautiously; validate eviction strategy
+- 一级缓存(1st-level cache)是基于 EntityManager 的;避免跨事务保留实体
+- 对于读多写少的实体,谨慎考虑二级缓存(2nd-level cache);验证逐出策略(Eviction strategy)
-## Migrations
+## 数据迁移(Migrations)
-- Use Flyway or Liquibase; never rely on Hibernate auto DDL in production
-- Keep migrations idempotent and additive; avoid dropping columns without plan
+- 使用 Flyway 或 Liquibase;在生产环境中绝不要依赖 Hibernate 的自动 DDL
+- 保持迁移是幂等(Idempotent)且具有增量性的;避免在没有计划的情况下删除列
-## Testing Data Access
+## 测试数据访问
-- Prefer `@DataJpaTest` with Testcontainers to mirror production
-- Assert SQL efficiency using logs: set `logging.level.org.hibernate.SQL=DEBUG` and `logging.level.org.hibernate.orm.jdbc.bind=TRACE` for parameter values
+- 优先使用 `@DataJpaTest` 配合 Testcontainers 来模拟生产环境
+- 使用日志断言 SQL 效率:设置 `logging.level.org.hibernate.SQL=DEBUG` 以及 `logging.level.org.hibernate.orm.jdbc.bind=TRACE` 以查看参数值
-**Remember**: Keep entities lean, queries intentional, and transactions short. Prevent N+1 with fetch strategies and projections, and index for your read/write paths.
+**记住**:保持实体精简、查询意图明确且事务简短。通过抓取策略(Fetch strategies)和投影(Projections)来防止 N+1 问题,并针对读/写路径建立索引。
diff --git a/skills/python-patterns/SKILL.md b/skills/python-patterns/SKILL.md
new file mode 100644
index 0000000..a1c05b3
--- /dev/null
+++ b/skills/python-patterns/SKILL.md
@@ -0,0 +1,751 @@
+---
+name: python-patterns
+description: Pythonic idioms, PEP 8 standards, type hints, and best practices for building robust, efficient, and maintainable Python applications.
+---
+
+# Python 开发模式 (Python Development Patterns)
+
+构建健壮、高效且可维护的应用程序的 Pythonic 惯用模式和最佳实践。
+
+## 激活时机 (When to Activate)
+
+- 编写新的 Python 代码时
+- 进行 Python 代码审查(Review)时
+- 重构现有的 Python 代码时
+- 设计 Python 包(Package)或模块(Module)时
+
+## 核心原则
+
+### 1. 可读性至上 (Readability Counts)
+
+Python 优先考虑可读性。代码应当直观且易于理解。
+
+```python
+# Good: 清晰且易读
+def get_active_users(users: list[User]) -> list[User]:
+ """仅从提供的列表中返回活跃用户。"""
+ return [user for user in users if user.is_active]
+
+
+# Bad: 巧妙但令人困惑
+def get_active_users(u):
+ return [x for x in u if x.a]
+```
+
+### 2. 显式优于隐式 (Explicit is Better Than Implicit)
+
+避免“魔法”行为;清晰地表达代码的功能。
+
+```python
+# Good: 显式配置
+import logging
+
+logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+)
+
+# Bad: 隐藏的副作用
+import some_module
+some_module.setup() # 这到底做了什么?
+```
+
+### 3. EAFP 模式 - 请求宽恕比请求许可更容易 (Easier to Ask Forgiveness Than Permission)
+
+Python 倾向于使用异常处理而非预先检查条件。
+
+```python
+# Good: EAFP 风格
+def get_value(dictionary: dict, key: str) -> Any:
+ try:
+ return dictionary[key]
+ except KeyError:
+ return default_value
+
+# Bad: LBYL (Look Before You Leap,三思而后行) 风格
+def get_value(dictionary: dict, key: str) -> Any:
+ if key in dictionary:
+ return dictionary[key]
+ else:
+ return default_value
+```
+
+## 类型提示 (Type Hints)
+
+### 基础类型注解
+
+```python
+from typing import Optional, List, Dict, Any
+
+def process_user(
+ user_id: str,
+ data: Dict[str, Any],
+ active: bool = True
+) -> Optional[User]:
+ """处理用户并返回更新后的 User 或 None。"""
+ if not active:
+ return None
+ return User(user_id, data)
+```
+
+### 现代类型提示 (Python 3.9+)
+
+```python
+# Python 3.9+ - 使用内置类型
+def process_items(items: list[str]) -> dict[str, int]:
+ return {item: len(item) for item in items}
+
+# Python 3.8 及更早版本 - 使用 typing 模块
+from typing import List, Dict
+
+def process_items(items: List[str]) -> Dict[str, int]:
+ return {item: len(item) for item in items}
+```
+
+### 类型别名 (Type Aliases) 和 TypeVar
+
+```python
+from typing import TypeVar, Union
+
+# 复杂类型的类型别名
+JSON = Union[dict[str, Any], list[Any], str, int, float, bool, None]
+
+def parse_json(data: str) -> JSON:
+ return json.loads(data)
+
+# 泛型
+T = TypeVar('T')
+
+def first(items: list[T]) -> T | None:
+ """返回第一项,如果列表为空则返回 None。"""
+ return items[0] if items else None
+```
+
+### 基于协议 (Protocol) 的鸭子类型 (Duck Typing)
+
+```python
+from typing import Protocol
+
+class Renderable(Protocol):
+ def render(self) -> str:
+ """将对象渲染为字符串。"""
+
+def render_all(items: list[Renderable]) -> str:
+ """渲染所有实现了 Renderable 协议的项目。"""
+ return "\n".join(item.render() for item in items)
+```
+
+## 异常处理模式 (Error Handling Patterns)
+
+### 特定异常处理
+
+```python
+# Good: 捕获特定的异常
+def load_config(path: str) -> Config:
+ try:
+ with open(path) as f:
+ return Config.from_json(f.read())
+ except FileNotFoundError as e:
+ raise ConfigError(f"Config file not found: {path}") from e
+ except json.JSONDecodeError as e:
+ raise ConfigError(f"Invalid JSON in config: {path}") from e
+
+# Bad: 空异常捕获
+def load_config(path: str) -> Config:
+ try:
+ with open(path) as f:
+ return Config.from_json(f.read())
+ except:
+ return None # 静默失败!
+```
+
+### 异常链 (Exception Chaining)
+
+```python
+def process_data(data: str) -> Result:
+ try:
+ parsed = json.loads(data)
+ except json.JSONDecodeError as e:
+ # 使用异常链以保留堆栈跟踪 (traceback)
+ raise ValueError(f"Failed to parse data: {data}") from e
+```
+
+### 自定义异常层次结构
+
+```python
+class AppError(Exception):
+ """所有应用程序错误的基类。"""
+ pass
+
+class ValidationError(AppError):
+ """当输入验证失败时引发。"""
+ pass
+
+class NotFoundError(AppError):
+ """当请求的资源未找到时引发。"""
+ pass
+
+# 使用示例
+def get_user(user_id: str) -> User:
+ user = db.find_user(user_id)
+ if not user:
+ raise NotFoundError(f"User not found: {user_id}")
+ return user
+```
+
+## 上下文管理器 (Context Managers)
+
+### 资源管理
+
+```python
+# Good: 使用上下文管理器
+def process_file(path: str) -> str:
+ with open(path, 'r') as f:
+ return f.read()
+
+# Bad: 手动资源管理
+def process_file(path: str) -> str:
+ f = open(path, 'r')
+ try:
+ return f.read()
+ finally:
+ f.close()
+```
+
+### 自定义上下文管理器
+
+```python
+from contextlib import contextmanager
+
+@contextmanager
+def timer(name: str):
+ """用于对代码块计时的上下文管理器。"""
+ start = time.perf_counter()
+ yield
+ elapsed = time.perf_counter() - start
+ print(f"{name} took {elapsed:.4f} seconds")
+
+# 使用示例
+with timer("data processing"):
+ process_large_dataset()
+```
+
+### 上下文管理器类
+
+```python
+class DatabaseTransaction:
+ def __init__(self, connection):
+ self.connection = connection
+
+ def __enter__(self):
+ self.connection.begin_transaction()
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ if exc_type is None:
+ self.connection.commit()
+ else:
+ self.connection.rollback()
+ return False # 不要抑制异常
+
+# 使用示例
+with DatabaseTransaction(conn):
+ user = conn.create_user(user_data)
+ conn.create_profile(user.id, profile_data)
+```
+
+## 推导式 (Comprehensions) 与生成器 (Generators)
+
+### 列表推导式 (List Comprehensions)
+
+```python
+# Good: 用于简单转换的列表推导式
+names = [user.name for user in users if user.is_active]
+
+# Bad: 手动循环
+names = []
+for user in users:
+ if user.is_active:
+ names.append(user.name)
+
+# 复杂的推导式应当展开
+# Bad: 太过复杂
+result = [x * 2 for x in items if x > 0 if x % 2 == 0]
+
+# Good: 使用生成器函数
+def filter_and_transform(items: Iterable[int]) -> list[int]:
+ result = []
+ for x in items:
+ if x > 0 and x % 2 == 0:
+ result.append(x * 2)
+ return result
+```
+
+### 生成器表达式 (Generator Expressions)
+
+```python
+# Good: 用于延迟求值的生成器
+total = sum(x * x for x in range(1_000_000))
+
+# Bad: 创建了巨大的中间列表
+total = sum([x * x for x in range(1_000_000)])
+```
+
+### 生成器函数 (Generator Functions)
+
+```python
+def read_large_file(path: str) -> Iterator[str]:
+ """逐行读取大文件。"""
+ with open(path) as f:
+ for line in f:
+ yield line.strip()
+
+# 使用示例
+for line in read_large_file("huge.txt"):
+ process(line)
+```
+
+## 数据类 (Data Classes) 与命名元组 (Named Tuples)
+
+### 数据类 (Data Classes)
+
+```python
+from dataclasses import dataclass, field
+from datetime import datetime
+
+@dataclass
+class User:
+ """具有自动生成 __init__、__repr__ 和 __eq__ 的用户实体。"""
+ id: str
+ name: str
+ email: str
+ created_at: datetime = field(default_factory=datetime.now)
+ is_active: bool = True
+
+# 使用示例
+user = User(
+ id="123",
+ name="Alice",
+ email="alice@example.com"
+)
+```
+
+### 带验证的数据类
+
+```python
+@dataclass
+class User:
+ email: str
+ age: int
+
+ def __post_init__(self):
+ # 验证电子邮件格式
+ if "@" not in self.email:
+ raise ValueError(f"Invalid email: {self.email}")
+ # 验证年龄范围
+ if self.age < 0 or self.age > 150:
+ raise ValueError(f"Invalid age: {self.age}")
+```
+
+### 命名元组 (Named Tuples)
+
+```python
+from typing import NamedTuple
+
+class Point(NamedTuple):
+ """不可变的二维点。"""
+ x: float
+ y: float
+
+ def distance(self, other: 'Point') -> float:
+ return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5
+
+# 使用示例
+p1 = Point(0, 0)
+p2 = Point(3, 4)
+print(p1.distance(p2)) # 5.0
+```
+
+## 装饰器 (Decorators)
+
+### 函数装饰器
+
+```python
+import functools
+import time
+
+def timer(func: Callable) -> Callable:
+ """用于对函数执行进行计时的装饰器。"""
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs):
+ start = time.perf_counter()
+ result = func(*args, **kwargs)
+ elapsed = time.perf_counter() - start
+ print(f"{func.__name__} took {elapsed:.4f}s")
+ return result
+ return wrapper
+
+@timer
+def slow_function():
+ time.sleep(1)
+
+# slow_function() 输出: slow_function took 1.0012s
+```
+
+### 参数化装饰器
+
+```python
+def repeat(times: int):
+ """用于多次重复执行函数的装饰器。"""
+ def decorator(func: Callable) -> Callable:
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs):
+ results = []
+ for _ in range(times):
+ results.append(func(*args, **kwargs))
+ return results
+ return wrapper
+ return decorator
+
+@repeat(times=3)
+def greet(name: str) -> str:
+ return f"Hello, {name}!"
+
+# greet("Alice") 返回 ["Hello, Alice!", "Hello, Alice!", "Hello, Alice!"]
+```
+
+### 基于类的装饰器
+
+```python
+class CountCalls:
+ """统计函数被调用次数的装饰器。"""
+ def __init__(self, func: Callable):
+ functools.update_wrapper(self, func)
+ self.func = func
+ self.count = 0
+
+ def __call__(self, *args, **kwargs):
+ self.count += 1
+ print(f"{self.func.__name__} has been called {self.count} times")
+ return self.func(*args, **kwargs)
+
+@CountCalls
+def process():
+ pass
+
+# 每次调用 process() 都会打印调用计数
+```
+
+## 并发模式 (Concurrency Patterns)
+
+### 线程 (Threading) 处理 I/O 密集型任务
+
+```python
+import concurrent.futures
+import threading
+
+def fetch_url(url: str) -> str:
+ """获取 URL (I/O 密集型操作)。"""
+ import urllib.request
+ with urllib.request.urlopen(url) as response:
+ return response.read().decode()
+
+def fetch_all_urls(urls: list[str]) -> dict[str, str]:
+ """使用线程并发地获取多个 URL。"""
+ with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
+ future_to_url = {executor.submit(fetch_url, url): url for url in urls}
+ results = {}
+ for future in concurrent.futures.as_completed(future_to_url):
+ url = future_to_url[future]
+ try:
+ results[url] = future.result()
+ except Exception as e:
+ results[url] = f"Error: {e}"
+ return results
+```
+
+### 多进程 (Multiprocessing) 处理 CPU 密集型任务
+
+```python
+def process_data(data: list[int]) -> int:
+ """CPU 密集型计算。"""
+ return sum(x ** 2 for x in data)
+
+def process_all(datasets: list[list[int]]) -> list[int]:
+ """使用多个进程处理多个数据集。"""
+ with concurrent.futures.ProcessPoolExecutor() as executor:
+ results = list(executor.map(process_data, datasets))
+ return results
+```
+
+### Async/Await 处理并发 I/O
+
+```python
+import asyncio
+
+async def fetch_async(url: str) -> str:
+ """异步获取 URL。"""
+ import aiohttp
+ async with aiohttp.ClientSession() as session:
+ async with session.get(url) as response:
+ return await response.text()
+
+async def fetch_all(urls: list[str]) -> dict[str, str]:
+ """并发地获取多个 URL。"""
+ tasks = [fetch_async(url) for url in urls]
+ results = await asyncio.gather(*tasks, return_exceptions=True)
+ return dict(zip(urls, results))
+```
+
+## 包组织 (Package Organization)
+
+### 标准项目布局
+
+```
+myproject/
+├── src/
+│ └── mypackage/
+│ ├── __init__.py
+│ ├── main.py
+│ ├── api/
+│ │ ├── __init__.py
+│ │ └── routes.py
+│ ├── models/
+│ │ ├── __init__.py
+│ │ └── user.py
+│ └── utils/
+│ ├── __init__.py
+│ └── helpers.py
+├── tests/
+│ ├── __init__.py
+│ ├── conftest.py
+│ ├── test_api.py
+│ └── test_models.py
+├── pyproject.toml
+├── README.md
+└── .gitignore
+```
+
+### 导入规范
+
+```python
+# Good: 导入顺序 - 标准库、第三方库、本地库
+import os
+import sys
+from pathlib import Path
+
+import requests
+from fastapi import FastAPI
+
+from mypackage.models import User
+from mypackage.utils import format_name
+
+# Good: 使用 isort 自动进行导入排序
+# pip install isort
+```
+
+### 用于包导出的 __init__.py
+
+```python
+# mypackage/__init__.py
+"""mypackage - 一个 Python 包示例。"""
+
+__version__ = "1.0.0"
+
+# 在包级别导出主要的类/函数
+from mypackage.models import User, Post
+from mypackage.utils import format_name
+
+__all__ = ["User", "Post", "format_name"]
+```
+
+## 内存与性能
+
+### 使用 __slots__ 提高内存效率
+
+```python
+# Bad: 常规类使用 __dict__ (占用更多内存)
+class Point:
+ def __init__(self, x: float, y: float):
+ self.x = x
+ self.y = y
+
+# Good: __slots__ 减少内存使用
+class Point:
+ __slots__ = ['x', 'y']
+
+ def __init__(self, x: float, y: float):
+ self.x = x
+ self.y = y
+```
+
+### 用于大数据的生成器
+
+```python
+# Bad: 在内存中返回完整列表
+def read_lines(path: str) -> list[str]:
+ with open(path) as f:
+ return [line.strip() for line in f]
+
+# Good: 一次产出一行
+def read_lines(path: str) -> Iterator[str]:
+ with open(path) as f:
+ for line in f:
+ yield line.strip()
+```
+
+### 避免在循环中进行字符串拼接
+
+```python
+# Bad: 由于字符串不可变性,复杂度为 O(n²)
+result = ""
+for item in items:
+ result += str(item)
+
+# Good: 使用 join,复杂度为 O(n)
+result = "".join(str(item) for item in items)
+
+# Good: 使用 StringIO 进行构建
+from io import StringIO
+
+buffer = StringIO()
+for item in items:
+ buffer.write(str(item))
+result = buffer.getvalue()
+```
+
+## Python 工具链集成
+
+### 常用命令
+
+```bash
+# 代码格式化
+black .
+isort .
+
+# 静态检查 (Linting)
+ruff check .
+pylint mypackage/
+
+# 类型检查
+mypy .
+
+# 测试
+pytest --cov=mypackage --cov-report=html
+
+# 安全扫描
+bandit -r .
+
+# 依赖管理
+pip-audit
+safety check
+```
+
+### pyproject.toml 配置
+
+```toml
+[project]
+name = "mypackage"
+version = "1.0.0"
+requires-python = ">=3.9"
+dependencies = [
+ "requests>=2.31.0",
+ "pydantic>=2.0.0",
+]
+
+[project.optional-dependencies]
+dev = [
+ "pytest>=7.4.0",
+ "pytest-cov>=4.1.0",
+ "black>=23.0.0",
+ "ruff>=0.1.0",
+ "mypy>=1.5.0",
+]
+
+[tool.black]
+line-length = 88
+target-version = ['py39']
+
+[tool.ruff]
+line-length = 88
+select = ["E", "F", "I", "N", "W"]
+
+[tool.mypy]
+python_version = "3.9"
+warn_return_any = true
+warn_unused_configs = true
+disallow_untyped_defs = true
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+addopts = "--cov=mypackage --cov-report=term-missing"
+```
+
+## 快速参考:Python 惯用法 (Python Idioms)
+
+| 惯用法 | 描述 |
+|-------|-------------|
+| EAFP | 请求宽恕比请求许可更容易 (Easier to Ask Forgiveness than Permission) |
+| 上下文管理器 (Context managers) | 使用 `with` 进行资源管理 |
+| 列表推导式 (List comprehensions) | 用于简单转换 |
+| 生成器 (Generators) | 用于延迟求值和大型数据集 |
+| 类型提示 (Type hints) | 为函数签名添加注解 |
+| 数据类 (Dataclasses) | 用于带有自动生成方法的各种数据容器 |
+| `__slots__` | 用于内存优化 |
+| f-strings | 用于字符串格式化 (Python 3.6+) |
+| `pathlib.Path` | 用于路径操作 (Python 3.4+) |
+| `enumerate` | 在循环中获取索引-元素对 |
+
+## 应避免的反模式 (Anti-Patterns)
+
+```python
+# Bad: 可变默认参数
+def append_to(item, items=[]):
+ items.append(item)
+ return items
+
+# Good: 使用 None 并创建新列表
+def append_to(item, items=None):
+ if items is None:
+ items = []
+ items.append(item)
+ return items
+
+# Bad: 使用 type() 检查类型
+if type(obj) == list:
+ process(obj)
+
+# Good: 使用 isinstance
+if isinstance(obj, list):
+ process(obj)
+
+# Bad: 使用 == 与 None 比较
+if value == None:
+ process()
+
+# Good: 使用 is
+if value is None:
+ process()
+
+# Bad: from module import *
+from os.path import *
+
+# Good: 显式导入
+from os.path import join, exists
+
+# Bad: 空异常捕获
+try:
+ risky_operation()
+except:
+ pass
+
+# Good: 特定异常
+try:
+ risky_operation()
+except SpecificError as e:
+ logger.error(f"Operation failed: {e}")
+```
+
+__记住__:Python 代码应当是易读的、显式的,并遵循最小惊讶原则。如有疑问,请优先考虑清晰度而非巧妙性。
+
+```
\ No newline at end of file
diff --git a/skills/python-testing/SKILL.md b/skills/python-testing/SKILL.md
new file mode 100644
index 0000000..89bfd9a
--- /dev/null
+++ b/skills/python-testing/SKILL.md
@@ -0,0 +1,815 @@
+---
+name: python-testing
+description: 使用 pytest、TDD 方法论、fixtures、mocking、参数化和代码覆盖率要求的 Python 测试策略。
+---
+
+# Python 测试模式(Python Testing Patterns)
+
+使用 pytest、测试驱动开发(TDD)方法论及最佳实践的 Python 应用程序全面测试策略。
+
+## 何时激活
+
+- 编写新的 Python 代码时(遵循 TDD:红、绿、重构)
+- 为 Python 项目设计测试套件时
+- 审查 Python 测试覆盖率时
+- 搭建测试基础设施时
+
+## 核心测试理念
+
+### 测试驱动开发(TDD)
+
+始终遵循 TDD 循环:
+
+1. **红(RED)**:为期望的行为编写一个失败的测试
+2. **绿(GREEN)**:编写最少量的代码使测试通过
+3. **重构(REFACTOR)**:在保持测试通过的前提下优化代码
+
+```python
+# 步骤 1:编写失败的测试 (RED)
+def test_add_numbers():
+ result = add(2, 3)
+ assert result == 5
+
+# 步骤 2:编写最小实现 (GREEN)
+def add(a, b):
+ return a + b
+
+# 步骤 3:根据需要进行重构 (REFACTOR)
+```
+
+### 覆盖率要求
+
+- **目标**:80% 以上的代码覆盖率
+- **关键路径**:必须达到 100% 覆盖率
+- 使用 `pytest --cov` 来衡量覆盖率
+
+```bash
+pytest --cov=mypackage --cov-report=term-missing --cov-report=html
+```
+
+## pytest 基础
+
+### 基本测试结构
+
+```python
+import pytest
+
+def test_addition():
+ """测试基础加法。"""
+ assert 2 + 2 == 4
+
+def test_string_uppercase():
+ """测试字符串大写转换。"""
+ text = "hello"
+ assert text.upper() == "HELLO"
+
+def test_list_append():
+ """测试列表追加。"""
+ items = [1, 2, 3]
+ items.append(4)
+ assert 4 in items
+ assert len(items) == 4
+```
+
+### 断言(Assertions)
+
+```python
+# 相等性
+assert result == expected
+
+# 不等性
+assert result != unexpected
+
+# 真值
+assert result # Truthy
+assert not result # Falsy
+assert result is True # 精确为 True
+assert result is False # 精确为 False
+assert result is None # 精确为 None
+
+# 成员资格
+assert item in collection
+assert item not in collection
+
+# 比较
+assert result > 0
+assert 0 <= result <= 100
+
+# 类型检查
+assert isinstance(result, str)
+
+# 异常测试(推荐做法)
+with pytest.raises(ValueError):
+ raise ValueError("error message")
+
+# 检查异常消息
+with pytest.raises(ValueError, match="invalid input"):
+ raise ValueError("invalid input provided")
+
+# 检查异常属性
+with pytest.raises(ValueError) as exc_info:
+ raise ValueError("error message")
+assert str(exc_info.value) == "error message"
+```
+
+## Fixtures
+
+### 基础 Fixture 用法
+
+```python
+import pytest
+
+@pytest.fixture
+def sample_data():
+ """提供示例数据的 Fixture。"""
+ return {"name": "Alice", "age": 30}
+
+def test_sample_data(sample_data):
+ """使用 fixture 的测试。"""
+ assert sample_data["name"] == "Alice"
+ assert sample_data["age"] == 30
+```
+
+### 带有设置(Setup)和清理(Teardown)的 Fixture
+
+```python
+@pytest.fixture
+def database():
+ """带有设置和清理逻辑的 Fixture。"""
+ # 设置 (Setup)
+ db = Database(":memory:")
+ db.create_tables()
+ db.insert_test_data()
+
+ yield db # 提供给测试使用
+
+ # 清理 (Teardown)
+ db.close()
+
+def test_database_query(database):
+ """测试数据库操作。"""
+ result = database.query("SELECT * FROM users")
+ assert len(result) > 0
+```
+
+### Fixture 作用域(Scopes)
+
+```python
+# 函数级作用域 (默认) - 每个测试运行一次
+@pytest.fixture
+def temp_file():
+ with open("temp.txt", "w") as f:
+ yield f
+ os.remove("temp.txt")
+
+# 模块级作用域 - 每个模块运行一次
+@pytest.fixture(scope="module")
+def module_db():
+ db = Database(":memory:")
+ db.create_tables()
+ yield db
+ db.close()
+
+# 会话级作用域 - 整个测试会话运行一次
+@pytest.fixture(scope="session")
+def shared_resource():
+ resource = ExpensiveResource()
+ yield resource
+ resource.cleanup()
+```
+
+### 带参数的 Fixture
+
+```python
+@pytest.fixture(params=[1, 2, 3])
+def number(request):
+ """参数化 Fixture。"""
+ return request.param
+
+def test_numbers(number):
+ """测试将运行 3 次,每个参数一次。"""
+ assert number > 0
+```
+
+### 使用多个 Fixture
+
+```python
+@pytest.fixture
+def user():
+ return User(id=1, name="Alice")
+
+@pytest.fixture
+def admin():
+ return User(id=2, name="Admin", role="admin")
+
+def test_user_admin_interaction(user, admin):
+ """同时使用多个 fixture 的测试。"""
+ assert admin.can_manage(user)
+```
+
+### 自动使用(Autouse) Fixture
+
+```python
+@pytest.fixture(autouse=True)
+def reset_config():
+ """在每个测试前自动运行。"""
+ Config.reset()
+ yield
+ Config.cleanup()
+
+def test_without_fixture_call():
+ # reset_config 会自动运行
+ assert Config.get_setting("debug") is False
+```
+
+### 用于共享 Fixture 的 conftest.py
+
+```python
+# tests/conftest.py
+import pytest
+
+@pytest.fixture
+def client():
+ """供所有测试共享的 fixture。"""
+ app = create_app(testing=True)
+ with app.test_client() as client:
+ yield client
+
+@pytest.fixture
+def auth_headers(client):
+ """为 API 测试生成认证头。"""
+ response = client.post("/api/login", json={
+ "username": "test",
+ "password": "test"
+ })
+ token = response.json["token"]
+ return {"Authorization": f"Bearer {token}"}
+```
+
+## 参数化(Parametrization)
+
+### 基础参数化
+
+```python
+@pytest.mark.parametrize("input,expected", [
+ ("hello", "HELLO"),
+ ("world", "WORLD"),
+ ("PyThOn", "PYTHON"),
+])
+def test_uppercase(input, expected):
+ """测试将使用不同的输入运行 3 次。"""
+ assert input.upper() == expected
+```
+
+### 多个参数
+
+```python
+@pytest.mark.parametrize("a,b,expected", [
+ (2, 3, 5),
+ (0, 0, 0),
+ (-1, 1, 0),
+ (100, 200, 300),
+])
+def test_add(a, b, expected):
+ """使用多组输入测试加法。"""
+ assert add(a, b) == expected
+```
+
+### 带 ID 的参数化
+
+```python
+@pytest.mark.parametrize("input,expected", [
+ ("valid@email.com", True),
+ ("invalid", False),
+ ("@no-domain.com", False),
+], ids=["valid-email", "missing-at", "missing-domain"])
+def test_email_validation(input, expected):
+ """通过可读的测试 ID 测试电子邮件验证。"""
+ assert is_valid_email(input) is expected
+```
+
+### 参数化 Fixtures
+
+```python
+@pytest.fixture(params=["sqlite", "postgresql", "mysql"])
+def db(request):
+ """针对多个数据库后端进行测试。"""
+ if request.param == "sqlite":
+ return Database(":memory:")
+ elif request.param == "postgresql":
+ return Database("postgresql://localhost/test")
+ elif request.param == "mysql":
+ return Database("mysql://localhost/test")
+
+def test_database_operations(db):
+ """测试将运行 3 次,每个数据库一次。"""
+ result = db.query("SELECT 1")
+ assert result is not None
+```
+
+## 标记(Markers)与测试选择
+
+### 自定义标记
+
+```python
+# 标记慢速测试
+@pytest.mark.slow
+def test_slow_operation():
+ time.sleep(5)
+
+# 标记集成测试
+@pytest.mark.integration
+def test_api_integration():
+ response = requests.get("https://api.example.com")
+ assert response.status_code == 200
+
+# 标记单元测试
+@pytest.mark.unit
+def test_unit_logic():
+ assert calculate(2, 3) == 5
+```
+
+### 运行特定测试
+
+```bash
+# 仅运行非慢速测试
+pytest -m "not slow"
+
+# 仅运行集成测试
+pytest -m integration
+
+# 运行集成测试或慢速测试
+pytest -m "integration or slow"
+
+# 运行标记为单元测试且非慢速的测试
+pytest -m "unit and not slow"
+```
+
+### 在 pytest.ini 中配置标记
+
+```ini
+[pytest]
+markers =
+ slow: 将测试标记为慢速
+ integration: 将测试标记为集成测试
+ unit: 将测试标记为单元测试
+ django: 将测试标记为需要 Django 环境
+```
+
+## Mocking 与 Patching
+
+### Mock 函数
+
+```python
+from unittest.mock import patch, Mock
+
+@patch("mypackage.external_api_call")
+def test_with_mock(api_call_mock):
+ """使用 mock 的外部 API 进行测试。"""
+ api_call_mock.return_value = {"status": "success"}
+
+ result = my_function()
+
+ api_call_mock.assert_called_once()
+ assert result["status"] == "success"
+```
+
+### Mock 返回值
+
+```python
+@patch("mypackage.Database.connect")
+def test_database_connection(connect_mock):
+ """使用 mock 的数据库连接进行测试。"""
+ connect_mock.return_value = MockConnection()
+
+ db = Database()
+ db.connect()
+
+ connect_mock.assert_called_once_with("localhost")
+```
+
+### Mock 异常
+
+```python
+@patch("mypackage.api_call")
+def test_api_error_handling(api_call_mock):
+ """使用 mock 异常测试错误处理。"""
+ api_call_mock.side_effect = ConnectionError("Network error")
+
+ with pytest.raises(ConnectionError):
+ api_call()
+
+ api_call_mock.assert_called_once()
+```
+
+### Mock 上下文管理器(Context Managers)
+
+```python
+@patch("builtins.open", new_callable=mock_open)
+def test_file_reading(mock_file):
+ """使用 mock 的 open 测试文件读取。"""
+ mock_file.return_value.read.return_value = "file content"
+
+ result = read_file("test.txt")
+
+ mock_file.assert_called_once_with("test.txt", "r")
+ assert result == "file content"
+```
+
+### 使用 Autospec
+
+```python
+@patch("mypackage.DBConnection", autospec=True)
+def test_autospec(db_mock):
+ """使用 autospec 捕获 API 滥用。"""
+ db = db_mock.return_value
+ db.query("SELECT * FROM users")
+
+ # 如果 DBConnection 没有 query 方法,此处将失败
+ db_mock.assert_called_once()
+```
+
+### Mock 类实例
+
+```python
+class TestUserService:
+ @patch("mypackage.UserRepository")
+ def test_create_user(self, repo_mock):
+ """使用 mock 的仓库进行用户创建测试。"""
+ repo_mock.return_value.save.return_value = User(id=1, name="Alice")
+
+ service = UserService(repo_mock.return_value)
+ user = service.create_user(name="Alice")
+
+ assert user.name == "Alice"
+ repo_mock.return_value.save.assert_called_once()
+```
+
+### Mock 属性(Property)
+
+```python
+@pytest.fixture
+def mock_config():
+ """创建一个带有属性的 mock。"""
+ config = Mock()
+ type(config).debug = PropertyMock(return_value=True)
+ type(config).api_key = PropertyMock(return_value="test-key")
+ return config
+
+def test_with_mock_config(mock_config):
+ """使用 mock 配置属性进行测试。"""
+ assert mock_config.debug is True
+ assert mock_config.api_key == "test-key"
+```
+
+## 测试异步代码
+
+### 使用 pytest-asyncio 进行异步测试
+
+```python
+import pytest
+
+@pytest.mark.asyncio
+async def test_async_function():
+ """测试异步函数。"""
+ result = await async_add(2, 3)
+ assert result == 5
+
+@pytest.mark.asyncio
+async def test_async_with_fixture(async_client):
+ """在异步 fixture 下进行异步测试。"""
+ response = await async_client.get("/api/users")
+ assert response.status_code == 200
+```
+
+### 异步 Fixture
+
+```python
+@pytest.fixture
+async def async_client():
+ """提供异步测试客户端的异步 Fixture。"""
+ app = create_app()
+ async with app.test_client() as client:
+ yield client
+
+@pytest.mark.asyncio
+async def test_api_endpoint(async_client):
+ """使用异步 fixture 进行测试。"""
+ response = await async_client.get("/api/data")
+ assert response.status_code == 200
+```
+
+### Mock 异步函数
+
+```python
+@pytest.mark.asyncio
+@patch("mypackage.async_api_call")
+async def test_async_mock(api_call_mock):
+ """使用 mock 测试异步函数。"""
+ api_call_mock.return_value = {"status": "ok"}
+
+ result = await my_async_function()
+
+ api_call_mock.assert_awaited_once()
+ assert result["status"] == "ok"
+```
+
+## 测试异常
+
+### 测试预期的异常
+
+```python
+def test_divide_by_zero():
+ """测试除以零是否抛出 ZeroDivisionError。"""
+ with pytest.raises(ZeroDivisionError):
+ divide(10, 0)
+
+def test_custom_exception():
+ """使用消息测试自定义异常。"""
+ with pytest.raises(ValueError, match="invalid input"):
+ validate_input("invalid")
+```
+
+### 测试异常属性
+
+```python
+def test_exception_with_details():
+ """测试带有自定义属性的异常。"""
+ with pytest.raises(CustomError) as exc_info:
+ raise CustomError("error", code=400)
+
+ assert exc_info.value.code == 400
+ assert "error" in str(exc_info.value)
+```
+
+## 测试副作用(Side Effects)
+
+### 测试文件操作
+
+```python
+import tempfile
+import os
+
+def test_file_processing():
+ """使用临时文件测试文件处理。"""
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as f:
+ f.write("test content")
+ temp_path = f.name
+
+ try:
+ result = process_file(temp_path)
+ assert result == "processed: test content"
+ finally:
+ os.unlink(temp_path)
+```
+
+### 使用 pytest 的 tmp_path Fixture 进行测试
+
+```python
+def test_with_tmp_path(tmp_path):
+ """使用 pytest 内置的临时路径 fixture 进行测试。"""
+ test_file = tmp_path / "test.txt"
+ test_file.write_text("hello world")
+
+ result = process_file(str(test_file))
+ assert result == "hello world"
+ # tmp_path 会自动清理
+```
+
+### 使用 tmpdir Fixture 进行测试
+
+```python
+def test_with_tmpdir(tmpdir):
+ """使用 pytest 的 tmpdir fixture 进行测试。"""
+ test_file = tmpdir.join("test.txt")
+ test_file.write("data")
+
+ result = process_file(str(test_file))
+ assert result == "data"
+```
+
+## 测试组织
+
+### 目录结构
+
+```
+tests/
+├── conftest.py # 共享的 fixture
+├── __init__.py
+├── unit/ # 单元测试
+│ ├── __init__.py
+│ ├── test_models.py
+│ ├── test_utils.py
+│ └── test_services.py
+├── integration/ # 集成测试
+│ ├── __init__.py
+│ ├── test_api.py
+│ └── test_database.py
+└── e2e/ # 端到端测试
+ ├── __init__.py
+ └── test_user_flow.py
+```
+
+### 测试类
+
+```python
+class TestUserService:
+ """在类中组织相关的测试。"""
+
+ @pytest.fixture(autouse=True)
+ def setup(self):
+ """在该类的每个测试运行前执行设置。"""
+ self.service = UserService()
+
+ def test_create_user(self):
+ """测试用户创建。"""
+ user = self.service.create_user("Alice")
+ assert user.name == "Alice"
+
+ def test_delete_user(self):
+ """测试用户删除。"""
+ user = User(id=1, name="Bob")
+ self.service.delete_user(user)
+ assert not self.service.user_exists(1)
+```
+
+## 最佳实践
+
+### 应该做(DO)
+
+- **遵循 TDD**:先写测试再写代码(红-绿-重构)
+- **只测试一件事**:每个测试应该只验证一种行为
+- **使用描述性的名称**:如 `test_user_login_with_invalid_credentials_fails`
+- **使用 Fixtures**:通过 fixture 消除重复代码
+- **Mock 外部依赖**:不要依赖外部服务
+- **测试边缘情况**:空输入、None 值、边界条件
+- **以 80% 以上的覆盖率为目标**:优先覆盖关键路径
+- **保持测试运行迅速**:使用标记区分慢速测试
+
+### 不该做(DON'T)
+
+- **不要测试实现细节**:测试行为而非内部实现
+- **不要在测试中使用复杂的条件判断**:保持测试逻辑简单
+- **不要忽视失败的测试**:所有测试必须通过
+- **不要测试第三方代码**:相信库本身是工作的
+- **不要在测试间共享状态**:测试应该是相互独立的
+- **不要在测试中捕获异常**:使用 `pytest.raises`
+- **不要使用 print 语句**:使用断言和 pytest 的输出机制
+- **不要编写过于脆弱的测试**:避免过度特异化的 mock
+
+## 常见模式
+
+### 测试 API 端点 (FastAPI/Flask)
+
+```python
+@pytest.fixture
+def client():
+ app = create_app(testing=True)
+ return app.test_client()
+
+def test_get_user(client):
+ response = client.get("/api/users/1")
+ assert response.status_code == 200
+ assert response.json["id"] == 1
+
+def test_create_user(client):
+ response = client.post("/api/users", json={
+ "name": "Alice",
+ "email": "alice@example.com"
+ })
+ assert response.status_code == 201
+ assert response.json["name"] == "Alice"
+```
+
+### 测试数据库操作
+
+```python
+@pytest.fixture
+def db_session():
+ """创建测试数据库会话。"""
+ session = Session(bind=engine)
+ session.begin_nested()
+ yield session
+ session.rollback()
+ session.close()
+
+def test_create_user(db_session):
+ user = User(name="Alice", email="alice@example.com")
+ db_session.add(user)
+ db_session.commit()
+
+ retrieved = db_session.query(User).filter_by(name="Alice").first()
+ assert retrieved.email == "alice@example.com"
+```
+
+### 测试类方法
+
+```python
+class TestCalculator:
+ @pytest.fixture
+ def calculator(self):
+ return Calculator()
+
+ def test_add(self, calculator):
+ assert calculator.add(2, 3) == 5
+
+ def test_divide_by_zero(self, calculator):
+ with pytest.raises(ZeroDivisionError):
+ calculator.divide(10, 0)
+```
+
+## pytest 配置
+
+### pytest.ini
+
+```ini
+[pytest]
+testpaths = tests
+python_files = test_*.py
+python_classes = Test*
+python_functions = test_*
+addopts =
+ --strict-markers
+ --disable-warnings
+ --cov=mypackage
+ --cov-report=term-missing
+ --cov-report=html
+markers =
+ slow: 将测试标记为慢速
+ integration: 将测试标记为集成测试
+ unit: 将测试标记为单元测试
+```
+
+### pyproject.toml
+
+```toml
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+python_files = ["test_*.py"]
+python_classes = ["Test*"]
+python_functions = ["test_*"]
+addopts = [
+ "--strict-markers",
+ "--cov=mypackage",
+ "--cov-report=term-missing",
+ "--cov-report=html",
+]
+markers = [
+ "slow: 将测试标记为慢速",
+ "integration: 将测试标记为集成测试",
+ "unit: 将测试标记为单元测试",
+]
+```
+
+## 运行测试
+
+```bash
+# 运行所有测试
+pytest
+
+# 运行特定文件
+pytest tests/test_utils.py
+
+# 运行特定测试函数
+pytest tests/test_utils.py::test_function
+
+# 运行并输出详细结果
+pytest -v
+
+# 运行并生成覆盖率报告
+pytest --cov=mypackage --cov-report=html
+
+# 仅运行非慢速测试
+pytest -m "not slow"
+
+# 运行并在第一次失败时停止
+pytest -x
+
+# 运行并在发生 N 次失败后停止
+pytest --maxfail=3
+
+# 运行上次失败的测试
+pytest --lf
+
+# 运行匹配模式的测试
+pytest -k "test_user"
+
+# 失败时启动调试器
+pytest --pdb
+```
+
+## 快速参考
+
+| 模式 | 用法 |
+|---------|-------|
+| `pytest.raises()` | 测试预期的异常 |
+| `@pytest.fixture()` | 创建可重用的测试 fixture |
+| `@pytest.mark.parametrize()` | 使用多组输入运行测试 |
+| `@pytest.mark.slow` | 标记慢速测试 |
+| `pytest -m "not slow"` | 跳过慢速测试 |
+| `@patch()` | Mock 函数和类 |
+| `tmp_path` fixture | 自动创建临时目录 |
+| `pytest --cov` | 生成覆盖率报告 |
+| `assert` | 简单且可读的断言 |
+
+**请记住**:测试代码也是代码。保持它们整洁、可读且可维护。好的测试能捕获 Bug;伟大的测试能防止 Bug 产生。
diff --git a/skills/springboot-patterns/SKILL.md b/skills/springboot-patterns/SKILL.md
index 2270dc9..dbcd50d 100644
--- a/skills/springboot-patterns/SKILL.md
+++ b/skills/springboot-patterns/SKILL.md
@@ -1,13 +1,13 @@
---
name: springboot-patterns
-description: Spring Boot architecture patterns, REST API design, layered services, data access, caching, async processing, and logging. Use for Java Spring Boot backend work.
+description: Spring Boot 架构模式、REST API 设计、分层服务、数据访问、缓存、异步处理和日志记录。适用于 Java Spring Boot 后端开发工作。
---
-# Spring Boot Development Patterns
+# Spring Boot 开发模式 (Spring Boot Development Patterns)
-Spring Boot architecture and API patterns for scalable, production-grade services.
+适用于可扩展、生产级服务的 Spring Boot 架构与 API 模式。
-## REST API Structure
+## REST API 结构
```java
@RestController
@@ -36,7 +36,7 @@ class MarketController {
}
```
-## Repository Pattern (Spring Data JPA)
+## 仓储模式 (Repository Pattern - Spring Data JPA)
```java
public interface MarketRepository extends JpaRepository {
@@ -45,7 +45,7 @@ public interface MarketRepository extends JpaRepository {
}
```
-## Service Layer with Transactions
+## 带事务的服务层 (Service Layer with Transactions)
```java
@Service
@@ -65,7 +65,7 @@ public class MarketService {
}
```
-## DTOs and Validation
+## DTO 与校验 (DTOs and Validation)
```java
public record CreateMarketRequest(
@@ -81,7 +81,7 @@ public record MarketResponse(Long id, String name, MarketStatus status) {
}
```
-## Exception Handling
+## 异常处理 (Exception Handling)
```java
@ControllerAdvice
@@ -101,16 +101,16 @@ class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
ResponseEntity handleGeneric(Exception ex) {
- // Log unexpected errors with stack traces
+ // 记录带有堆栈跟踪的非预期错误
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiError.of("Internal server error"));
}
}
```
-## Caching
+## 缓存 (Caching)
-Requires `@EnableCaching` on a configuration class.
+需要在配置类上添加 `@EnableCaching`。
```java
@Service
@@ -133,22 +133,22 @@ public class MarketCacheService {
}
```
-## Async Processing
+## 异步处理 (Async Processing)
-Requires `@EnableAsync` on a configuration class.
+需要在配置类上添加 `@EnableAsync`。
```java
@Service
public class NotificationService {
@Async
public CompletableFuture sendAsync(Notification notification) {
- // send email/SMS
+ // 发送 邮件/短信
return CompletableFuture.completedFuture(null);
}
}
```
-## Logging (SLF4J)
+## 日志记录 (Logging - SLF4J)
```java
@Service
@@ -158,7 +158,7 @@ public class ReportService {
public Report generate(Long marketId) {
log.info("generate_report marketId={}", marketId);
try {
- // logic
+ // 业务逻辑
} catch (Exception ex) {
log.error("generate_report_failed marketId={}", marketId, ex);
throw ex;
@@ -168,7 +168,7 @@ public class ReportService {
}
```
-## Middleware / Filters
+## 中间件 / 过滤器 (Middleware / Filters)
```java
@Component
@@ -190,14 +190,14 @@ public class RequestLoggingFilter extends OncePerRequestFilter {
}
```
-## Pagination and Sorting
+## 分页与排序 (Pagination and Sorting)
```java
PageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by("createdAt").descending());
Page results = marketService.list(page);
```
-## Error-Resilient External Calls
+## 容错性外部调用 (Error-Resilient External Calls)
```java
public T withRetry(Supplier supplier, int maxRetries) {
@@ -221,19 +221,16 @@ public T withRetry(Supplier supplier, int maxRetries) {
}
```
-## Rate Limiting (Filter + Bucket4j)
+## 限流 (Filter + Bucket4j)
-**Security Note**: The `X-Forwarded-For` header is untrusted by default because clients can spoof it.
-Only use forwarded headers when:
-1. Your app is behind a trusted reverse proxy (nginx, AWS ALB, etc.)
-2. You have registered `ForwardedHeaderFilter` as a bean
-3. You have configured `server.forward-headers-strategy=NATIVE` or `FRAMEWORK` in application properties
-4. Your proxy is configured to overwrite (not append to) the `X-Forwarded-For` header
+**安全注意事项**:`X-Forwarded-For` 请求头默认是不可信的,因为客户端可以伪造它。
+仅在以下情况下使用转发请求头:
+1. 你的应用位于受信任的反向代理(nginx、AWS ALB 等)之后
+2. 你已将 `ForwardedHeaderFilter` 注册为 Bean
+3. 你在 application 属性中配置了 `server.forward-headers-strategy=NATIVE` 或 `FRAMEWORK`
+4. 你的代理配置为覆盖(而非追加)`X-Forwarded-For` 请求头
-When `ForwardedHeaderFilter` is properly configured, `request.getRemoteAddr()` will automatically
-return the correct client IP from the forwarded headers. Without this configuration, use
-`request.getRemoteAddr()` directly—it returns the immediate connection IP, which is the only
-trustworthy value.
+当 `ForwardedHeaderFilter` 配置正确时,`request.getRemoteAddr()` 将自动从转发头中返回正确的客户端 IP。如果没有此配置,请直接使用 `request.getRemoteAddr()`——它返回直接连接的 IP,这是唯一可信的值。
```java
@Component
@@ -241,32 +238,31 @@ public class RateLimitFilter extends OncePerRequestFilter {
private final Map buckets = new ConcurrentHashMap<>();
/*
- * SECURITY: This filter uses request.getRemoteAddr() to identify clients for rate limiting.
+ * 安全提示:此过滤器使用 request.getRemoteAddr() 来识别用于限流的客户端。
*
- * If your application is behind a reverse proxy (nginx, AWS ALB, etc.), you MUST configure
- * Spring to handle forwarded headers properly for accurate client IP detection:
+ * 如果你的应用位于反向代理(nginx、AWS ALB 等)之后,你必须配置 Spring
+ * 正确处理转发头,以便准确检测客户端 IP:
*
- * 1. Set server.forward-headers-strategy=NATIVE (for cloud platforms) or FRAMEWORK in
- * application.properties/yaml
- * 2. If using FRAMEWORK strategy, register ForwardedHeaderFilter:
+ * 1. 在 application.properties/yaml 中设置 server.forward-headers-strategy=NATIVE
+ * (适用于云平台) 或 FRAMEWORK
+ * 2. 如果使用 FRAMEWORK 策略,请注册 ForwardedHeaderFilter:
*
* @Bean
* ForwardedHeaderFilter forwardedHeaderFilter() {
* return new ForwardedHeaderFilter();
* }
*
- * 3. Ensure your proxy overwrites (not appends) the X-Forwarded-For header to prevent spoofing
- * 4. Configure server.tomcat.remoteip.trusted-proxies or equivalent for your container
+ * 3. 确保你的代理覆盖(而不是追加)X-Forwarded-For 请求头以防止伪造
+ * 4. 为你的容器配置 server.tomcat.remoteip.trusted-proxies 或等效配置
*
- * Without this configuration, request.getRemoteAddr() returns the proxy IP, not the client IP.
- * Do NOT read X-Forwarded-For directly—it is trivially spoofable without trusted proxy handling.
+ * 如果没有这些配置,request.getRemoteAddr() 将返回代理服务器的 IP,而非客户端 IP。
+ * 不要直接读取 X-Forwarded-For —— 在没有受信任代理处理的情况下,它是极易伪造的。
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
- // Use getRemoteAddr() which returns the correct client IP when ForwardedHeaderFilter
- // is configured, or the direct connection IP otherwise. Never trust X-Forwarded-For
- // headers directly without proper proxy configuration.
+ // 使用 getRemoteAddr(),它在配置了 ForwardedHeaderFilter 时返回正确的客户端 IP,
+ // 否则返回直接连接的 IP。在没有正确代理配置的情况下,切勿直接信任 X-Forwarded-For 头。
String clientIp = request.getRemoteAddr();
Bucket bucket = buckets.computeIfAbsent(clientIp,
@@ -283,22 +279,22 @@ public class RateLimitFilter extends OncePerRequestFilter {
}
```
-## Background Jobs
+## 后台任务 (Background Jobs)
-Use Spring’s `@Scheduled` or integrate with queues (e.g., Kafka, SQS, RabbitMQ). Keep handlers idempotent and observable.
+使用 Spring 的 `@Scheduled` 或集成队列(如 Kafka、SQS、RabbitMQ)。保持处理程序具有幂等性和可观测性。
-## Observability
+## 可观测性 (Observability)
-- Structured logging (JSON) via Logback encoder
-- Metrics: Micrometer + Prometheus/OTel
-- Tracing: Micrometer Tracing with OpenTelemetry or Brave backend
+- 通过 Logback encoder 实现结构化日志(JSON)
+- 指标(Metrics):Micrometer + Prometheus/OTel
+- 链路追踪(Tracing):使用 OpenTelemetry 或 Brave 后端的 Micrometer Tracing
-## Production Defaults
+## 生产环境默认实践 (Production Defaults)
-- Prefer constructor injection, avoid field injection
-- Enable `spring.mvc.problemdetails.enabled=true` for RFC 7807 errors (Spring Boot 3+)
-- Configure HikariCP pool sizes for workload, set timeouts
-- Use `@Transactional(readOnly = true)` for queries
-- Enforce null-safety via `@NonNull` and `Optional` where appropriate
+- 优先使用构造函数注入,避免字段注入
+- 为 RFC 7807 错误启用 `spring.mvc.problemdetails.enabled=true` (Spring Boot 3+)
+- 根据工作负载配置 HikariCP 连接池大小并设置超时
+- 为查询使用 `@Transactional(readOnly = true)`
+- 通过 `@NonNull` 和 `Optional` 在适当时强制执行空安全(null-safety)
-**Remember**: Keep controllers thin, services focused, repositories simple, and errors handled centrally. Optimize for maintainability and testability.
+**切记**:保持控制器(Controller)薄、服务(Service)专注、仓储(Repository)简单,并集中处理错误。针对可维护性和可测试性进行优化。
diff --git a/skills/springboot-security/SKILL.md b/skills/springboot-security/SKILL.md
index f9dc6a2..5b33b6f 100644
--- a/skills/springboot-security/SKILL.md
+++ b/skills/springboot-security/SKILL.md
@@ -1,17 +1,17 @@
---
name: springboot-security
-description: Spring Security best practices for authn/authz, validation, CSRF, secrets, headers, rate limiting, and dependency security in Java Spring Boot services.
+description: Spring Boot 服务中关于身份认证/授权(authn/authz)、校验、CSRF、密钥管理、响应头、限流及依赖安全的 Spring Security 最佳实践。
---
-# Spring Boot Security Review
+# Spring Boot 安全审查
-Use when adding auth, handling input, creating endpoints, or dealing with secrets.
+在添加认证、处理输入、创建端点或处理密钥时使用。
-## Authentication
+## 身份认证(Authentication)
-- Prefer stateless JWT or opaque tokens with revocation list
-- Use `httpOnly`, `Secure`, `SameSite=Strict` cookies for sessions
-- Validate tokens with `OncePerRequestFilter` or resource server
+- 优先使用无状态 JWT 或带有撤回列表(Revocation List)的不透明令牌(Opaque Tokens)
+- 为会话(Session)使用 `httpOnly`、`Secure`、`SameSite=Strict` 的 Cookie
+- 使用 `OncePerRequestFilter` 或资源服务器验证令牌
```java
@Component
@@ -36,27 +36,27 @@ public class JwtAuthFilter extends OncePerRequestFilter {
}
```
-## Authorization
+## 授权(Authorization)
-- Enable method security: `@EnableMethodSecurity`
-- Use `@PreAuthorize("hasRole('ADMIN')")` or `@PreAuthorize("@authz.canEdit(#id)")`
-- Deny by default; expose only required scopes
+- 启用方法级安全:`@EnableMethodSecurity`
+- 使用 `@PreAuthorize("hasRole('ADMIN')")` 或 `@PreAuthorize("@authz.canEdit(#id)")`
+- 默认拒绝(Deny by default);仅暴露必要的权限范围(Scopes)
-## Input Validation
+## 输入校验(Input Validation)
-- Use Bean Validation with `@Valid` on controllers
-- Apply constraints on DTOs: `@NotBlank`, `@Email`, `@Size`, custom validators
-- Sanitize any HTML with a whitelist before rendering
+- 在控制器(Controller)上配合使用 Bean Validation 与 `@Valid`
+- 在 DTO 上应用约束:`@NotBlank`、`@Email`、`@Size` 以及自定义校验器
+- 在渲染之前,通过白名单对任何 HTML 进行净化(Sanitize)
-## SQL Injection Prevention
+## 防止 SQL 注入
-- Use Spring Data repositories or parameterized queries
-- For native queries, use `:param` bindings; never concatenate strings
+- 使用 Spring Data 存储库(Repositories)或参数化查询
+- 对于原生查询,使用 `:param` 绑定;严禁拼接字符串
-## CSRF Protection
+## CSRF 防护
-- For browser session apps, keep CSRF enabled; include token in forms/headers
-- For pure APIs with Bearer tokens, disable CSRF and rely on stateless auth
+- 对于基于浏览器会话的应用,保持启用 CSRF;在表单/请求头中包含令牌
+- 对于使用 Bearer 令牌的纯 API,禁用 CSRF 并依赖无状态认证
```java
http
@@ -64,13 +64,13 @@ http
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
```
-## Secrets Management
+## 密钥管理(Secrets Management)
-- No secrets in source; load from env or vault
-- Keep `application.yml` free of credentials; use placeholders
-- Rotate tokens and DB credentials regularly
+- 源代码中不保留密钥;从环境变量或 Vault 加载
+- 确保 `application.yml` 中没有凭据;使用占位符
+- 定期轮换令牌和数据库凭据
-## Security Headers
+## 安全响应头(Security Headers)
```java
http
@@ -79,41 +79,41 @@ http
.policyDirectives("default-src 'self'"))
.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)
.xssProtection(Customizer.withDefaults())
- .referrerPolicy(rp -> rp.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.NO_REFERRER)));
+ .referrerPolicy(rp -> rp.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.NO_REFER_ER)));
```
-## Rate Limiting
+## 限流(Rate Limiting)
-- Apply Bucket4j or gateway-level limits on expensive endpoints
-- Log and alert on bursts; return 429 with retry hints
+- 在高开销端点上应用 Bucket4j 或网关级限流
+- 对突发流量进行日志记录并告警;返回 429 状态码及重试提示
-## Dependency Security
+## 依赖安全(Dependency Security)
-- Run OWASP Dependency Check / Snyk in CI
-- Keep Spring Boot and Spring Security on supported versions
-- Fail builds on known CVEs
+- 在 CI 中运行 OWASP Dependency Check / Snyk
+- 保持 Spring Boot 和 Spring Security 处于受支持的版本
+- 发现已知 CVE 时构建失败
-## Logging and PII
+## 日志与个人敏感信息(PII)
-- Never log secrets, tokens, passwords, or full PAN data
-- Redact sensitive fields; use structured JSON logging
+- 严禁在日志中记录密钥、令牌、密码或完整的银行卡号(PAN)数据
+- 脱敏敏感字段;使用结构化 JSON 日志记录
-## File Uploads
+## 文件上传
-- Validate size, content type, and extension
-- Store outside web root; scan if required
+- 校验大小、内容类型(Content Type)和扩展名
+- 存储在 Web 根目录之外;必要时进行扫描
-## Checklist Before Release
+## 发布前自检清单
-- [ ] Auth tokens validated and expired correctly
-- [ ] Authorization guards on every sensitive path
-- [ ] All inputs validated and sanitized
-- [ ] No string-concatenated SQL
-- [ ] CSRF posture correct for app type
-- [ ] Secrets externalized; none committed
-- [ ] Security headers configured
-- [ ] Rate limiting on APIs
-- [ ] Dependencies scanned and up to date
-- [ ] Logs free of sensitive data
+- [ ] 身份认证令牌已正确验证并配置过期时间
+- [ ] 每个敏感路径都有授权保护
+- [ ] 所有输入均已校验并净化
+- [ ] 没有字符串拼接的 SQL
+- [ ] CSRF 配置符合应用类型
+- [ ] 密钥已外部化;未提交任何密钥
+- [ ] 安全响应头已配置
+- [ ] API 已配置限流
+- [ ] 依赖已扫描且为最新
+- [ ] 日志中不包含敏感数据
-**Remember**: Deny by default, validate inputs, least privilege, and secure-by-configuration first.
+**记住**:默认拒绝、校验输入、最小权限原则,以及配置优先的安全性。
diff --git a/skills/springboot-tdd/SKILL.md b/skills/springboot-tdd/SKILL.md
index daaa990..5d959dc 100644
--- a/skills/springboot-tdd/SKILL.md
+++ b/skills/springboot-tdd/SKILL.md
@@ -1,26 +1,26 @@
---
name: springboot-tdd
-description: Test-driven development for Spring Boot using JUnit 5, Mockito, MockMvc, Testcontainers, and JaCoCo. Use when adding features, fixing bugs, or refactoring.
+description: 使用 JUnit 5、Mockito、MockMvc、Testcontainers 和 JaCoCo 进行 Spring Boot 的测试驱动开发(TDD)。在添加功能、修复 Bug 或进行重构时使用。
---
-# Spring Boot TDD Workflow
+# Spring Boot 测试驱动开发(TDD)工作流
-TDD guidance for Spring Boot services with 80%+ coverage (unit + integration).
+针对 Spring Boot 服务的 TDD 指南,要求 80% 以上的覆盖率(单元测试 + 集成测试)。
-## When to Use
+## 适用场景
-- New features or endpoints
-- Bug fixes or refactors
-- Adding data access logic or security rules
+- 开发新功能或端点(Endpoints)
+- 修复 Bug 或进行代码重构
+- 添加数据访问逻辑或安全规则
-## Workflow
+## 工作流
-1) Write tests first (they should fail)
-2) Implement minimal code to pass
-3) Refactor with tests green
-4) Enforce coverage (JaCoCo)
+1) 先写测试(测试应当失败)
+2) 实现最少量的代码以使测试通过
+3) 在测试通过(Green)的前提下进行重构
+4) 强制执行覆盖率检查(JaCoCo)
-## Unit Tests (JUnit 5 + Mockito)
+## 单元测试(JUnit 5 + Mockito)
```java
@ExtendWith(MockitoExtension.class)
@@ -41,12 +41,12 @@ class MarketServiceTest {
}
```
-Patterns:
-- Arrange-Act-Assert
-- Avoid partial mocks; prefer explicit stubbing
-- Use `@ParameterizedTest` for variants
+模式:
+- Arrange-Act-Assert(准备-执行-断言)
+- 避免部分打桩(Partial Mocks);优先使用显式桩函数(Stubbing)
+- 使用 `@ParameterizedTest` 处理多种变体场景
-## Web Layer Tests (MockMvc)
+## Web 层测试(MockMvc)
```java
@WebMvcTest(MarketController.class)
@@ -65,7 +65,7 @@ class MarketControllerTest {
}
```
-## Integration Tests (SpringBootTest)
+## 集成测试(SpringBootTest)
```java
@SpringBootTest
@@ -86,7 +86,7 @@ class MarketIntegrationTest {
}
```
-## Persistence Tests (DataJpaTest)
+## 持久层测试(DataJpaTest)
```java
@DataJpaTest
@@ -109,12 +109,12 @@ class MarketRepositoryTest {
## Testcontainers
-- Use reusable containers for Postgres/Redis to mirror production
-- Wire via `@DynamicPropertySource` to inject JDBC URLs into Spring context
+- 使用可重用的容器(如 Postgres/Redis)来模拟生产环境
+- 通过 `@DynamicPropertySource` 进行连接,将 JDBC URL 注入到 Spring 上下文中
-## Coverage (JaCoCo)
+## 覆盖率(JaCoCo)
-Maven snippet:
+Maven 配置片段:
```xml
org.jacoco
@@ -133,13 +133,13 @@ Maven snippet:
```
-## Assertions
+## 断言(Assertions)
-- Prefer AssertJ (`assertThat`) for readability
-- For JSON responses, use `jsonPath`
-- For exceptions: `assertThatThrownBy(...)`
+- 为了提高可读性,优先选择 AssertJ (`assertThat`)
+- 对于 JSON 响应,使用 `jsonPath`
+- 对于异常测试:`assertThatThrownBy(...)`
-## Test Data Builders
+## 测试数据构建器(Test Data Builders)
```java
class MarketBuilder {
@@ -149,9 +149,9 @@ class MarketBuilder {
}
```
-## CI Commands
+## CI 命令
-- Maven: `mvn -T 4 test` or `mvn verify`
-- Gradle: `./gradlew test jacocoTestReport`
+- Maven:`mvn -T 4 test` 或 `mvn verify`
+- Gradle:`./gradlew test jacocoTestReport`
-**Remember**: Keep tests fast, isolated, and deterministic. Test behavior, not implementation details.
+**记住**:保持测试快速、隔离且具有确定性。测试的是行为,而非实现细节。
diff --git a/skills/springboot-verification/SKILL.md b/skills/springboot-verification/SKILL.md
index 909e90a..5c73473 100644
--- a/skills/springboot-verification/SKILL.md
+++ b/skills/springboot-verification/SKILL.md
@@ -1,100 +1,100 @@
---
name: springboot-verification
-description: Verification loop for Spring Boot projects: build, static analysis, tests with coverage, security scans, and diff review before release or PR.
+description: Spring Boot 项目的验证循环(Verification loop):包含构建、静态分析、带覆盖率的测试、安全扫描,以及在发布或 PR 前的差异评审(diff review)。
---
-# Spring Boot Verification Loop
+# Spring Boot 验证循环(Verification Loop)
-Run before PRs, after major changes, and pre-deploy.
+在提交 PR 前、发生重大变更后以及预部署阶段运行此流程。
-## Phase 1: Build
+## 阶段 1:构建(Build)
```bash
mvn -T 4 clean verify -DskipTests
-# or
+# 或者
./gradlew clean assemble -x test
```
-If build fails, stop and fix.
+如果构建失败,请停止并修复。
-## Phase 2: Static Analysis
+## 阶段 2:静态分析(Static Analysis)
-Maven (common plugins):
+Maven(常用插件):
```bash
mvn -T 4 spotbugs:check pmd:check checkstyle:check
```
-Gradle (if configured):
+Gradle(如果已配置):
```bash
./gradlew checkstyleMain pmdMain spotbugsMain
```
-## Phase 3: Tests + Coverage
+## 阶段 3:测试 + 覆盖率(Tests + Coverage)
```bash
mvn -T 4 test
-mvn jacoco:report # verify 80%+ coverage
-# or
+mvn jacoco:report # 验证 80% 以上的覆盖率
+# 或者
./gradlew test jacocoTestReport
```
-Report:
-- Total tests, passed/failed
-- Coverage % (lines/branches)
+报告指标:
+- 测试总数、通过/失败数量
+- 覆盖率 %(行/分支)
-## Phase 4: Security Scan
+## 阶段 4:安全扫描(Security Scan)
```bash
-# Dependency CVEs
+# 依赖项 CVE 漏洞扫描
mvn org.owasp:dependency-check-maven:check
-# or
+# 或者
./gradlew dependencyCheckAnalyze
-# Secrets (git)
-git secrets --scan # if configured
+# 密钥(Secrets)扫描 (git)
+git secrets --scan # 如果已配置
```
-## Phase 5: Lint/Format (optional gate)
+## 阶段 5:代码规范/格式化(Lint/Format,可选阈值)
```bash
-mvn spotless:apply # if using Spotless plugin
+mvn spotless:apply # 如果使用了 Spotless 插件
./gradlew spotlessApply
```
-## Phase 6: Diff Review
+## 阶段 6:差异评审(Diff Review)
```bash
git diff --stat
git diff
```
-Checklist:
-- No debugging logs left (`System.out`, `log.debug` without guards)
-- Meaningful errors and HTTP statuses
-- Transactions and validation present where needed
-- Config changes documented
+自查清单(Checklist):
+- 未残留调试日志(如 `System.out`,或缺少防护检查的 `log.debug`)
+- 错误信息和 HTTP 状态码具有明确语义
+- 在必要处已包含事务(Transactions)和校验(Validation)
+- 配置变更已记录在文档中
-## Output Template
+## 输出模版(Output Template)
```
-VERIFICATION REPORT
+验证报告 (VERIFICATION REPORT)
===================
-Build: [PASS/FAIL]
-Static: [PASS/FAIL] (spotbugs/pmd/checkstyle)
-Tests: [PASS/FAIL] (X/Y passed, Z% coverage)
-Security: [PASS/FAIL] (CVE findings: N)
-Diff: [X files changed]
+构建 (Build): [通过/失败]
+静态分析 (Static): [通过/失败] (spotbugs/pmd/checkstyle)
+测试 (Tests): [通过/失败] (通过 X/Y,覆盖率 Z%)
+安全 (Security): [通过/失败] (CVE 发现数量: N)
+差异 (Diff): [X 个文件已变更]
-Overall: [READY / NOT READY]
+结论 (Overall): [就绪 / 未就绪]
-Issues to Fix:
+待修复问题:
1. ...
2. ...
```
-## Continuous Mode
+## 持续模式(Continuous Mode)
-- Re-run phases on significant changes or every 30–60 minutes in long sessions
-- Keep a short loop: `mvn -T 4 test` + spotbugs for quick feedback
+- 在发生显著变更时,或在长会话中每 30–60 分钟重新运行各阶段。
+- 保持短反馈循环:运行 `mvn -T 4 test` + spotbugs 以获得快速反馈。
-**Remember**: Fast feedback beats late surprises. Keep the gate strict—treat warnings as defects in production systems.
+**记住**:快速反馈优于后期惊讶。保持严格的准入门槛——在生产系统中,将警告(Warnings)视为缺陷(Defects)。
diff --git a/translation_workdir/cache/translation_db.json b/translation_workdir/cache/translation_db.json
index 28081c5..797863f 100644
--- a/translation_workdir/cache/translation_db.json
+++ b/translation_workdir/cache/translation_db.json
@@ -4,8 +4,8 @@
"content": "# Claude Code \u5168\u65b9\u4f4d\u901f\u67e5\u624b\u518c\n\n\n\n---\n\n**\u81ea 2 \u6708\u4efd\u5b9e\u9a8c\u6027\u63a8\u51fa\u4ee5\u6765\uff0c\u6211\u4e00\u76f4\u662f Claude Code \u7684\u5fe0\u5b9e\u7528\u6237\u3002\u6211\u4e0e [@DRodriguezFX](https://x.com/DRodriguezFX) \u5408\u4f5c\uff0c\u5b8c\u5168\u4f7f\u7528 Claude Code \u5f00\u53d1\u4e86 [zenith.chat](https://zenith.chat)\uff0c\u5e76\u8d62\u5f97\u4e86 Anthropic x Forum Ventures \u9ed1\u5ba2\u677e\u3002**\n\n\u4ee5\u4e0b\u662f\u6211\u5728 10 \u4e2a\u6708\u7684\u65e5\u5e38\u4f7f\u7528\u540e\u603b\u7ed3\u51fa\u7684\u5b8c\u6574\u914d\u7f6e\uff1a\u6280\u80fd\uff08Skills\uff09\u3001\u751f\u547d\u5468\u671f\u94a9\u5b50\uff08Hooks\uff09\u3001\u5b50\u667a\u80fd\u4f53\uff08Subagents\uff09\u3001\u6a21\u578b\u4e0a\u4e0b\u6587\u534f\u8bae\uff08MCPs\uff09\u3001\u63d2\u4ef6\uff08Plugins\uff09\u4ee5\u53ca\u771f\u6b63\u884c\u4e4b\u6709\u6548\u7684\u5b9e\u8df5\u65b9\u6848\u3002\n\n---\n\n## \u6280\u80fd\uff08Skills\uff09\u4e0e\u547d\u4ee4\uff08Commands\uff09\n\n\u6280\u80fd\uff08Skills\uff09\u7c7b\u4f3c\u4e8e\u89c4\u5219\uff0c\u4f46\u88ab\u9650\u5b9a\u5728\u7279\u5b9a\u7684\u4f5c\u7528\u57df\u548c\u5de5\u4f5c\u6d41\u4e2d\u3002\u5f53\u4f60\u9700\u8981\u6267\u884c\u7279\u5b9a\u5de5\u4f5c\u6d41\u65f6\uff0c\u5b83\u4eec\u662f\u63d0\u793a\u8bcd\uff08Prompts\uff09\u7684\u5feb\u6377\u65b9\u5f0f\u3002\n\n\u5728\u4f7f\u7528 Opus 4.5 \u8fdb\u884c\u957f\u65f6\u95f4\u7f16\u7801\u540e\uff0c\u60f3\u8981\u6e05\u7406\u5e9f\u5f03\u4ee3\u7801\u548c\u6563\u843d\u7684 .md \u6587\u4ef6\uff1f\u8fd0\u884c `/refactor-clean`\u3002\u9700\u8981\u6d4b\u8bd5\uff1f\u4f7f\u7528 `/tdd`\u3001`/e2e`\u3001`/test-coverage`\u3002\u6280\u80fd\u8fd8\u53ef\u4ee5\u5305\u542b\u4ee3\u7801\u6620\u5c04\uff08Codemaps\uff09\u2014\u2014\u8fd9\u662f\u4e00\u79cd\u8ba9 Claude \u5feb\u901f\u5bfc\u822a\u4ee3\u7801\u5e93\u7684\u65b9\u6cd5\uff0c\u800c\u4e0d\u4f1a\u5728\u63a2\u7d22\u8fc7\u7a0b\u4e2d\u6d6a\u8d39\u4e0a\u4e0b\u6587\uff08Context\uff09\u3002\n\n\n*\u94fe\u5f0f\u8c03\u7528\u591a\u4e2a\u547d\u4ee4*\n\n\u547d\u4ee4\uff08Commands\uff09\u662f\u901a\u8fc7\u659c\u6760\u547d\u4ee4\uff08Slash Commands\uff09\u6267\u884c\u7684\u6280\u80fd\u3002\u5b83\u4eec\u5728\u529f\u80fd\u4e0a\u6709\u91cd\u53e0\uff0c\u4f46\u5b58\u50a8\u65b9\u5f0f\u4e0d\u540c\uff1a\n\n- **\u6280\u80fd (Skills)**: `~/.claude/skills/` - \u66f4\u5e7f\u6cdb\u7684\u5de5\u4f5c\u6d41\u5b9a\u4e49\n- **\u547d\u4ee4 (Commands)**: `~/.claude/commands/` - \u5feb\u901f\u53ef\u6267\u884c\u7684\u63d0\u793a\u8bcd\n\n```bash\n# \u793a\u4f8b\u6280\u80fd\u7ed3\u6784\n~/.claude/skills/\n pmx-guidelines.md # \u9879\u76ee\u7279\u5b9a\u6a21\u5f0f\n coding-standards.md # \u8bed\u8a00\u6700\u4f73\u5b9e\u8df5\n tdd-workflow/ # \u5305\u542b README.md \u7684\u591a\u6587\u4ef6\u6280\u80fd\n security-review/ # \u57fa\u4e8e\u6e05\u5355\u7684\u6280\u80fd\n```\n\n---\n\n## \u751f\u547d\u5468\u671f\u94a9\u5b50\uff08Hooks\uff09\n\n\u751f\u547d\u5468\u671f\u94a9\u5b50\uff08Hooks\uff09\u662f\u57fa\u4e8e\u89e6\u53d1\u5668\u7684\u81ea\u52a8\u5316\u529f\u80fd\uff0c\u5728\u7279\u5b9a\u4e8b\u4ef6\u53d1\u751f\u65f6\u89e6\u53d1\u3002\u4e0e\u6280\u80fd\u4e0d\u540c\uff0c\u5b83\u4eec\u88ab\u9650\u5236\u5728\u5de5\u5177\u8c03\u7528\uff08Tool Calls\uff09\u548c\u751f\u547d\u5468\u671f\u4e8b\u4ef6\u4e2d\u3002\n\n**\u94a9\u5b50\u7c7b\u578b\uff1a**\n\n1. **PreToolUse** - \u5de5\u5177\u6267\u884c\u524d\uff08\u9a8c\u8bc1\u3001\u63d0\u9192\uff09\n2. **PostToolUse** - \u5de5\u5177\u6267\u884c\u540e\uff08\u683c\u5f0f\u5316\u3001\u53cd\u9988\u5faa\u73af\uff09\n3. **UserPromptSubmit** - \u53d1\u9001\u6d88\u606f\u65f6\n4. **Stop** - Claude \u5b8c\u6210\u54cd\u5e94\u65f6\n5. **PreCompact** - \u4e0a\u4e0b\u6587\u538b\u7f29\u524d\n6. **Notification** - \u6743\u9650\u8bf7\u6c42\n\n**\u793a\u4f8b\uff1a\u5728\u6267\u884c\u8017\u65f6\u547d\u4ee4\u524d\u53d1\u9001 tmux \u63d0\u9192**\n\n```json\n{\n \"PreToolUse\": [\n {\n \"matcher\": \"tool == \\\"Bash\\\" && tool_input.command matches \\\"(npm|pnpm|yarn|cargo|pytest)\\\"\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"if [ -z \\\"$TMUX\\\" ]; then echo '[Hook] Consider tmux for session persistence' >&2; fi\"\n }\n ]\n }\n ]\n}\n```\n\n\n*\u8fd0\u884c PostToolUse \u94a9\u5b50\u65f6\u5728 Claude Code \u4e2d\u83b7\u5f97\u7684\u53cd\u9988\u793a\u4f8b*\n\n**\u4e13\u5bb6\u63d0\u793a\uff1a** \u4f7f\u7528 `hookify` \u63d2\u4ef6\u53ef\u4ee5\u901a\u8fc7\u5bf9\u8bdd\u65b9\u5f0f\u521b\u5efa\u94a9\u5b50\uff0c\u800c\u65e0\u9700\u624b\u52a8\u7f16\u5199 JSON\u3002\u8fd0\u884c `/hookify` \u5e76\u63cf\u8ff0\u4f60\u7684\u9700\u6c42\u5373\u53ef\u3002\n\n---\n\n## \u5b50\u667a\u80fd\u4f53\uff08Subagents\uff09\n\n\u5b50\u667a\u80fd\u4f53\uff08Subagents\uff09\u662f\u4f60\u7684\u7f16\u6392\u5668\uff08\u4e3b Claude \u5b9e\u4f8b\uff09\u53ef\u4ee5\u59d4\u6d3e\u4efb\u52a1\u7684\u8fdb\u7a0b\uff0c\u5177\u6709\u53d7\u9650\u7684\u4f5c\u7528\u57df\u3002\u5b83\u4eec\u53ef\u4ee5\u5728\u540e\u53f0\u6216\u524d\u53f0\u8fd0\u884c\uff0c\u4ece\u800c\u4e3a\u4e3b\u667a\u80fd\u4f53\u91ca\u653e\u4e0a\u4e0b\u6587\u7a7a\u95f4\u3002\n\n\u5b50\u667a\u80fd\u4f53\u4e0e\u6280\u80fd\u914d\u5408\u5f97\u975e\u5e38\u597d\u2014\u2014\u80fd\u591f\u6267\u884c\u90e8\u5206\u6280\u80fd\u96c6\u7684\u5b50\u667a\u80fd\u4f53\u53ef\u4ee5\u88ab\u59d4\u6d3e\u4efb\u52a1\u5e76\u81ea\u4e3b\u4f7f\u7528\u8fd9\u4e9b\u6280\u80fd\u3002\u5b83\u4eec\u8fd8\u53ef\u4ee5\u901a\u8fc7\u7279\u5b9a\u7684\u5de5\u5177\u6743\u9650\u8fdb\u884c\u6c99\u7bb1\u5316\u5904\u7406\u3002\n\n```bash\n# \u793a\u4f8b\u5b50\u667a\u80fd\u4f53\u7ed3\u6784\n~/.claude/agents/\n planner.md # \u529f\u80fd\u5b9e\u73b0\u89c4\u5212\n architect.md # \u7cfb\u7edf\u8bbe\u8ba1\u51b3\u7b56\n tdd-guide.md # \u6d4b\u8bd5\u9a71\u52a8\u5f00\u53d1\n code-reviewer.md # \u8d28\u91cf/\u5b89\u5168\u5ba1\u67e5\n security-reviewer.md # \u6f0f\u6d1e\u5206\u6790\n build-error-resolver.md\n e2e-runner.md\n refactor-cleaner.md\n```\n\n\u4e3a\u6bcf\u4e2a\u5b50\u667a\u80fd\u4f53\u914d\u7f6e\u5141\u8bb8\u7684\u5de5\u5177\u3001MCP \u548c\u6743\u9650\uff0c\u4ee5\u5b9e\u73b0\u9002\u5f53\u7684\u4f5c\u7528\u57df\u9650\u5b9a\u3002\n\n---\n\n## \u89c4\u5219\uff08Rules\uff09\u4e0e\u8bb0\u5fc6\uff08Memory\uff09\n\n\u4f60\u7684 `.rules` \u6587\u4ef6\u5939\u5b58\u653e\u7740 Claude \u5e94\u8be5\u59cb\u7ec8\u9075\u5faa\u7684\u6700\u4f73\u5b9e\u8df5 `.md` \u6587\u4ef6\u3002\u6709\u4e24\u79cd\u65b9\u6cd5\uff1a\n\n1. **\u5355\u4e2a CLAUDE.md** - \u6240\u6709\u5185\u5bb9\u653e\u5728\u4e00\u4e2a\u6587\u4ef6\u4e2d\uff08\u7528\u6237\u7ea7\u6216\u9879\u76ee\u7ea7\uff09\n2. **Rules \u6587\u4ef6\u5939** - \u6309\u5173\u6ce8\u70b9\u5206\u7ec4\u7684\u6a21\u5757\u5316 `.md` \u6587\u4ef6\n\n```bash\n~/.claude/rules/\n security.md # \u7981\u6b62\u786c\u7f16\u7801\u5bc6\u94a5\uff0c\u9a8c\u8bc1\u8f93\u5165\n coding-style.md # \u4e0d\u53ef\u53d8\u6027\uff0c\u6587\u4ef6\u7ec4\u7ec7\n testing.md # TDD \u5de5\u4f5c\u6d41\uff0c80% \u8986\u76d6\u7387\n git-workflow.md # \u63d0\u4ea4\u683c\u5f0f\uff0cPR \u6d41\u7a0b\n agents.md # \u4f55\u65f6\u59d4\u6d3e\u7ed9\u5b50\u667a\u80fd\u4f53\n performance.md # \u6a21\u578b\u9009\u62e9\uff0c\u4e0a\u4e0b\u6587\u7ba1\u7406\n```\n\n**\u793a\u4f8b\u89c4\u5219\uff1a**\n\n- \u4ee3\u7801\u5e93\u4e2d\u4e25\u7981\u4f7f\u7528\u8868\u60c5\u7b26\u53f7 (Emojis)\n- \u524d\u7aef\u907f\u514d\u4f7f\u7528\u7d2b\u8272\u8c03\n- \u90e8\u7f72\u524d\u59cb\u7ec8\u6d4b\u8bd5\u4ee3\u7801\n- \u4f18\u5148\u91c7\u7528\u6a21\u5757\u5316\u4ee3\u7801\u800c\u975e\u8d85\u5927\u6587\u4ef6\n- \u4e25\u7981\u63d0\u4ea4 `console.log`\n\n---\n\n## \u6a21\u578b\u4e0a\u4e0b\u6587\u534f\u8bae\uff08MCPs\uff09\n\n\u6a21\u578b\u4e0a\u4e0b\u6587\u534f\u8bae\uff08MCPs\uff09\u5c06 Claude \u76f4\u63a5\u8fde\u63a5\u5230\u5916\u90e8\u670d\u52a1\u3002\u5b83\u4e0d\u662f API \u7684\u66ff\u4ee3\u54c1\uff0c\u800c\u662f\u56f4\u7ed5 API \u7684\u63d0\u793a\u8bcd\u9a71\u52a8\u5c01\u88c5\uff0c\u5141\u8bb8\u5728\u5bfc\u822a\u4fe1\u606f\u65f6\u5177\u6709\u66f4\u9ad8\u7684\u7075\u6d3b\u6027\u3002\n\n**\u793a\u4f8b\uff1a** Supabase MCP \u8ba9 Claude \u80fd\u591f\u62c9\u53d6\u7279\u5b9a\u6570\u636e\uff0c\u76f4\u63a5\u5728\u4e0a\u6e38\u8fd0\u884c SQL\uff0c\u65e0\u9700\u590d\u5236\u7c98\u8d34\u3002\u6570\u636e\u5e93\u3001\u90e8\u7f72\u5e73\u53f0\u7b49\u540c\u7406\u3002\n\n\n*Supabase MCP \u5217\u51fa public \u6a21\u5f0f\u4e0b\u6570\u636e\u8868\u7684\u793a\u4f8b*\n\n**Claude \u5185\u90e8\u7684 Chrome\uff1a** \u662f\u4e00\u4e2a\u5185\u7f6e\u7684 MCP \u63d2\u4ef6\uff0c\u5141\u8bb8 Claude \u81ea\u4e3b\u63a7\u5236\u6d4f\u89c8\u5668\u2014\u2014\u901a\u8fc7\u70b9\u51fb\u6765\u67e5\u770b\u529f\u80fd\u8fd0\u884c\u60c5\u51b5\u3002\n\n**\u5173\u952e\u70b9\uff1a\u4e0a\u4e0b\u6587\u7a97\u53e3\uff08Context Window\uff09\u7ba1\u7406**\n\n\u5bf9 MCP \u8981\u7cbe\u6311\u7ec6\u9009\u3002\u6211\u5c06\u6240\u6709 MCP \u4fdd\u7559\u5728\u7528\u6237\u914d\u7f6e\u4e2d\uff0c\u4f46**\u7981\u7528\u6240\u6709\u4e0d\u4f7f\u7528\u7684 MCP**\u3002\u5bfc\u822a\u81f3 `/plugins` \u5e76\u5411\u4e0b\u6eda\u52a8\u6216\u8fd0\u884c `/mcp`\u3002\n\n\n*\u4f7f\u7528 /plugins \u5bfc\u822a\u81f3 MCP\uff0c\u67e5\u770b\u5f53\u524d\u5df2\u5b89\u88c5\u7684 MCP \u53ca\u5176\u72b6\u6001*\n\n\u7531\u4e8e\u542f\u7528\u4e86\u8fc7\u591a\u5de5\u5177\uff0c\u4f60\u5728\u538b\u7f29\u524d\u7684 200k \u4e0a\u4e0b\u6587\u7a97\u53e3\u53ef\u80fd\u5b9e\u9645\u4e0a\u53ea\u5269 70k\u3002\u6027\u80fd\u4f1a\u663e\u8457\u4e0b\u964d\u3002\n\n**\u7ecf\u9a8c\u6cd5\u5219\uff1a** \u914d\u7f6e\u4e2d\u4fdd\u7559 20-30 \u4e2a MCP\uff0c\u4f46\u4fdd\u6301\u542f\u7528\u7684\u5c11\u4e8e 10 \u4e2a / \u6d3b\u8dc3\u5de5\u5177\u5c11\u4e8e 80 \u4e2a\u3002\n\n```bash\n# \u68c0\u67e5\u5df2\u542f\u7528\u7684 MCP\n/mcp\n\n# \u5728 ~/.claude.json \u7684 projects.disabledMcpServers \u4e2d\u7981\u7528\u4e0d\u4f7f\u7528\u7684 MCP\n```\n\n---\n\n## \u63d2\u4ef6\uff08Plugins\uff09\n\n\u63d2\u4ef6\uff08Plugins\uff09\u5c01\u88c5\u4e86\u5de5\u5177\u4ee5\u4fbf\u4e8e\u5b89\u88c5\uff0c\u907f\u514d\u7e41\u7410\u7684\u624b\u52a8\u8bbe\u7f6e\u3002\u4e00\u4e2a\u63d2\u4ef6\u53ef\u4ee5\u662f\u6280\u80fd\uff08Skill\uff09\u4e0e MCP \u7684\u7ec4\u5408\uff0c\u4e5f\u53ef\u4ee5\u662f\u7ed1\u5b9a\u5728\u4e00\u8d77\u7684\u94a9\u5b50\uff08Hooks\uff09/\u5de5\u5177\uff08Tools\uff09\u3002\n\n**\u5b89\u88c5\u63d2\u4ef6\uff1a**\n\n```bash\n# \u6dfb\u52a0\u5e02\u573a\nclaude plugin marketplace add https://github.com/mixedbread-ai/mgrep\n\n# \u6253\u5f00 Claude\uff0c\u8fd0\u884c /plugins\uff0c\u627e\u5230\u65b0\u5e02\u573a\uff0c\u5e76\u4ece\u4e2d\u5b89\u88c5\n```\n\n\n*\u663e\u793a\u65b0\u5b89\u88c5\u7684 Mixedbread-Grep \u5e02\u573a*\n\n\u5982\u679c\u4f60\u7ecf\u5e38\u5728\u7f16\u8f91\u5668\u4e4b\u5916\u8fd0\u884c Claude Code\uff0c**LSP \u63d2\u4ef6**\u7279\u522b\u6709\u7528\u3002\u8bed\u8a00\u670d\u52a1\u5668\u534f\u8bae\uff08Language Server Protocol\uff09\u4e3a Claude \u63d0\u4f9b\u4e86\u5b9e\u65f6\u7c7b\u578b\u68c0\u67e5\u3001\u8f6c\u5230\u5b9a\u4e49\u548c\u667a\u80fd\u8865\u5168\u529f\u80fd\uff0c\u65e0\u9700\u6253\u5f00 IDE\u3002\n\n```bash\n# \u5df2\u542f\u7528\u63d2\u4ef6\u793a\u4f8b\ntypescript-lsp@claude-plugins-official # TypeScript \u667a\u80fd\u63d0\u793a\npyright-lsp@claude-plugins-official # Python \u7c7b\u578b\u68c0\u67e5\nhookify@claude-plugins-official # \u5bf9\u8bdd\u5f0f\u521b\u5efa\u94a9\u5b50\nmgrep@Mixedbread-Grep # \u6bd4 ripgrep \u66f4\u597d\u7684\u641c\u7d22\n```\n\n\u4e0e MCP \u540c\u6837\u7684\u8b66\u544a\u2014\u2014\u6ce8\u610f\u4f60\u7684\u4e0a\u4e0b\u6587\u7a97\u53e3\u3002\n\n---\n\n## \u6280\u5de7\u4e0e\u5efa\u8bae\n\n### \u952e\u76d8\u5feb\u6377\u952e\n\n- `Ctrl+U` - \u5220\u9664\u6574\u884c\uff08\u6bd4\u72c2\u6309\u9000\u683c\u952e\u5feb\uff09\n- `!` - \u5feb\u901f Bash \u547d\u4ee4\u524d\u7f00\n- `@` - \u641c\u7d22\u6587\u4ef6\n- `/` - \u542f\u52a8\u659c\u6760\u547d\u4ee4\n- `Shift+Enter` - \u591a\u884c\u8f93\u5165\n- `Tab` - \u5207\u6362\u601d\u8003\u8fc7\u7a0b\u663e\u793a\n- `Esc Esc` - \u4e2d\u65ad Claude / \u6062\u590d\u4ee3\u7801\n\n### \u5e76\u884c\u5de5\u4f5c\u6d41\n\n- **\u6d3e\u751f** (`/fork`) - \u6d3e\u751f\u5bf9\u8bdd\u4ee5\u5e76\u884c\u6267\u884c\u4e0d\u91cd\u53e0\u7684\u4efb\u52a1\uff0c\u800c\u4e0d\u662f\u8ba9\u6392\u961f\u7684\u8bf7\u6c42\u5806\u79ef\n- **Git \u5de5\u4f5c\u6811 (Worktrees)** - \u7528\u4e8e\u5e76\u884c\u8fd0\u884c\u591a\u4e2a Claude \u5b9e\u4f8b\u800c\u4e0d\u4f1a\u4ea7\u751f\u51b2\u7a81\u3002\u6bcf\u4e2a\u5de5\u4f5c\u6811\u90fd\u662f\u4e00\u4e2a\u72ec\u7acb\u7684\u68c0\u51fa\u76ee\u5f55\n\n```bash\ngit worktree add ../feature-branch feature-branch\n# \u73b0\u5728\u5728\u6bcf\u4e2a\u5de5\u4f5c\u6811\u4e2d\u8fd0\u884c\u72ec\u7acb\u7684 Claude \u5b9e\u4f8b\n```\n\n### \u4f7f\u7528 tmux \u5904\u7406\u8017\u65f6\u547d\u4ee4\n\n\u4e32\u6d41\u5e76\u89c2\u5bdf Claude \u8fd0\u884c\u7684\u65e5\u5fd7/Bash \u8fdb\u7a0b\uff1a\n\nhttps://github.com/user-attachments/assets/shortform/07-tmux-video.mp4\n\n```bash\ntmux new -s dev\n# Claude \u5728\u8fd9\u91cc\u8fd0\u884c\u547d\u4ee4\uff0c\u4f60\u53ef\u4ee5\u968f\u65f6\u5206\u79bb (detach) \u548c\u91cd\u65b0\u8fde\u63a5 (reattach)\ntmux attach -t dev\n```\n\n### mgrep > grep\n\n`mgrep` \u662f\u5bf9 ripgrep/grep \u7684\u91cd\u5927\u6539\u8fdb\u3002\u901a\u8fc7\u63d2\u4ef6\u5e02\u573a\u5b89\u88c5\uff0c\u7136\u540e\u4f7f\u7528 `/mgrep` \u6280\u80fd\u3002\u540c\u65f6\u652f\u6301\u672c\u5730\u641c\u7d22\u548c\u7f51\u7edc\u641c\u7d22\u3002\n\n```bash\nmgrep \"function handleSubmit\" # \u672c\u5730\u641c\u7d22\nmgrep --web \"Next.js 15 app router changes\" # \u7f51\u7edc\u641c\u7d22\n```\n\n### \u5176\u4ed6\u6709\u7528\u547d\u4ee4\n\n- `/rewind` - \u56de\u9000\u5230\u4e4b\u524d\u7684\u72b6\u6001\n- `/statusline` - \u81ea\u5b9a\u4e49\u663e\u793a\u5206\u652f\u3001\u4e0a\u4e0b\u6587\u5360\u6bd4\u3001\u5f85\u529e\u4e8b\u9879 (Todos)\n- `/checkpoints` - \u6587\u4ef6\u7ea7\u64a4\u9500\u70b9\n- `/compact` - \u624b\u52a8\u89e6\u53d1\u4e0a\u4e0b\u6587\u538b\u7f29\n\n### GitHub Actions CI/CD\n\n\u4f7f\u7528 GitHub Actions \u5728 PR \u4e0a\u8bbe\u7f6e\u4ee3\u7801\u5ba1\u67e5\u3002\u914d\u7f6e\u5b8c\u6210\u540e\uff0cClaude \u53ef\u4ee5\u81ea\u52a8\u5ba1\u67e5 PR\u3002\n\n\n*Claude \u6279\u51c6\u4e86\u4e00\u4e2a\u6f0f\u6d1e\u4fee\u590d PR*\n\n### \u6c99\u7bb1\u5316\uff08Sandboxing\uff09\n\n\u5bf9\u98ce\u9669\u64cd\u4f5c\u4f7f\u7528\u6c99\u7bb1\u6a21\u5f0f\u2014\u2014Claude \u5728\u53d7\u9650\u73af\u5883\u4e2d\u8fd0\u884c\uff0c\u4e0d\u4f1a\u5f71\u54cd\u4f60\u7684\u5b9e\u9645\u7cfb\u7edf\u3002\n\n---\n\n## \u5173\u4e8e\u7f16\u8f91\u5668\n\n\u7f16\u8f91\u5668\u7684\u9009\u62e9\u4f1a\u663e\u8457\u5f71\u54cd Claude Code \u7684\u5de5\u4f5c\u6d41\u3002\u867d\u7136 Claude Code \u53ef\u4ee5\u4ece\u4efb\u4f55\u7ec8\u7aef\u8fd0\u884c\uff0c\u4f46\u914d\u5408\u529f\u80fd\u5f3a\u5927\u7684\u7f16\u8f91\u5668\u53ef\u4ee5\u89e3\u9501\u5b9e\u65f6\u6587\u4ef6\u8ddf\u8e2a\u3001\u5feb\u901f\u5bfc\u822a\u548c\u96c6\u6210\u547d\u4ee4\u6267\u884c\u3002\n\n### Zed\uff08\u6211\u7684\u9996\u9009\uff09\n\n\u6211\u4f7f\u7528 [Zed](https://zed.dev) \u2014\u2014 \u7528 Rust \u7f16\u5199\uff0c\u901f\u5ea6\u6781\u5feb\u3002\u77ac\u95f4\u6253\u5f00\uff0c\u5904\u7406\u5e9e\u5927\u7684\u4ee3\u7801\u5e93\u4e5f\u6e38\u5203\u6709\u4f59\uff0c\u4e14\u51e0\u4e4e\u4e0d\u5360\u7528\u7cfb\u7edf\u8d44\u6e90\u3002\n\n**\u4e3a\u4ec0\u4e48 Zed + Claude Code \u662f\u7edd\u4f73\u7ec4\u5408\uff1a**\n\n- **\u901f\u5ea6** - \u57fa\u4e8e Rust \u7684\u6027\u80fd\u610f\u5473\u7740\u5f53 Claude \u5feb\u901f\u7f16\u8f91\u6587\u4ef6\u65f6\u4e0d\u4f1a\u6709\u5ef6\u8fdf\u3002\u4f60\u7684\u7f16\u8f91\u5668\u80fd\u8ddf\u4e0a\u8282\u594f\n- **\u667a\u80fd\u4f53\u9762\u677f\u96c6\u6210** - Zed \u7684 Claude \u96c6\u6210\u8ba9\u4f60\u5728 Claude \u7f16\u8f91\u65f6\u5b9e\u65f6\u8ddf\u8e2a\u6587\u4ef6\u66f4\u6539\u3002\u65e0\u9700\u79bb\u5f00\u7f16\u8f91\u5668\u5373\u53ef\u5728 Claude \u5f15\u7528\u7684\u6587\u4ef6\u95f4\u8df3\u8f6c\n- **CMD+Shift+R \u547d\u4ee4\u9762\u677f** - \u5728\u53ef\u641c\u7d22\u7684 UI \u4e2d\u5feb\u901f\u8bbf\u95ee\u6240\u6709\u81ea\u5b9a\u4e49\u659c\u6760\u547d\u4ee4\u3001\u8c03\u8bd5\u5668\u548c\u6784\u5efa\u811a\u672c\n- **\u6781\u4f4e\u7684\u8d44\u6e90\u5360\u7528** - \u5728\u6267\u884c\u7e41\u91cd\u64cd\u4f5c\u65f6\u4e0d\u4f1a\u4e0e Claude \u4e89\u593a RAM/CPU\u3002\u8fd0\u884c Opus \u65f6\u8fd9\u4e00\u70b9\u5f88\u91cd\u8981\n- **Vim \u6a21\u5f0f** - \u5982\u679c\u4f60\u4e60\u60ef Vim\uff0c\u5b83\u6709\u5b8c\u6574\u7684 Vim \u952e\u7ed1\u5b9a\u652f\u6301\n\n\n*\u4f7f\u7528 CMD+Shift+R \u5f39\u51fa\u81ea\u5b9a\u4e49\u547d\u4ee4\u4e0b\u62c9\u5217\u8868\u7684 Zed \u7f16\u8f91\u5668\u3002\u53f3\u4e0b\u89d2\u7684\u725b\u773c\u56fe\u6807\u663e\u793a\u4e86\u8ddf\u968f\u6a21\u5f0f (Following mode)\u3002*\n\n**\u7f16\u8f91\u5668\u901a\u7528\u6280\u5de7\uff1a**\n\n1. **\u5206\u5c4f\u663e\u793a** - \u4e00\u4fa7\u662f\u8fd0\u884c Claude Code \u7684\u7ec8\u7aef\uff0c\u53e6\u4e00\u4fa7\u662f\u7f16\u8f91\u5668\n2. **Ctrl + G** - \u5728 Zed \u4e2d\u5feb\u901f\u6253\u5f00 Claude \u5f53\u524d\u6b63\u5728\u5904\u7406\u7684\u6587\u4ef6\n3. **\u81ea\u52a8\u4fdd\u5b58** - \u5f00\u542f\u81ea\u52a8\u4fdd\u5b58\uff0c\u786e\u4fdd Claude \u8bfb\u53d6\u7684\u6587\u4ef6\u59cb\u7ec8\u662f\u6700\u65b0\u7684\n4. **Git \u96c6\u6210** - \u4f7f\u7528\u7f16\u8f91\u5668\u7684 Git \u529f\u80fd\u5728\u63d0\u4ea4\u524d\u5ba1\u67e5 Claude \u7684\u66f4\u6539\n5. **\u6587\u4ef6\u76d1\u542c\u5668** - \u5927\u591a\u6570\u7f16\u8f91\u5668\u4f1a\u81ea\u52a8\u91cd\u8f7d\u66f4\u6539\u540e\u7684\u6587\u4ef6\uff0c\u8bf7\u786e\u8ba4\u8be5\u529f\u80fd\u5df2\u542f\u7528\n\n### VSCode / Cursor\n\n\u8fd9\u4e5f\u662f\u4e00\u4e2a\u53ef\u884c\u7684\u9009\u62e9\uff0c\u5e76\u4e14\u4e0e Claude Code \u914d\u5408\u826f\u597d\u3002\u4f60\u53ef\u4ee5\u901a\u8fc7\u7ec8\u7aef\u5f62\u5f0f\u4f7f\u7528\u5b83\uff0c\u5229\u7528 `\\ide` \u81ea\u52a8\u4e0e\u7f16\u8f91\u5668\u540c\u6b65\u5e76\u542f\u7528 LSP \u529f\u80fd\uff08\u73b0\u5728\u5728\u67d0\u79cd\u7a0b\u5ea6\u4e0a\u4e0e\u63d2\u4ef6\u529f\u80fd\u91cd\u53e0\uff09\u3002\u6216\u8005\u4f60\u53ef\u4ee5\u9009\u62e9\u6269\u5c55\u7a0b\u5e8f\uff0c\u5b83\u4e0e\u7f16\u8f91\u5668\u96c6\u6210\u5ea6\u66f4\u9ad8\uff0c\u5e76\u62e5\u6709\u5339\u914d\u7684 UI\u3002\n\n\n*VS Code \u6269\u5c55\u4e3a Claude Code \u63d0\u4f9b\u4e86\u539f\u751f\u56fe\u5f62\u754c\u9762\uff0c\u76f4\u63a5\u96c6\u6210\u5230\u4f60\u7684 IDE \u4e2d\u3002*\n\n---\n\n## \u6211\u7684\u914d\u7f6e\n\n### \u63d2\u4ef6 (Plugins)\n\n**\u5df2\u5b89\u88c5\uff1a**\uff08\u6211\u901a\u5e38\u4e00\u6b21\u53ea\u542f\u7528 4-5 \u4e2a\uff09\n\n```markdown\nralph-wiggum@claude-code-plugins # \u5faa\u73af\u81ea\u52a8\u5316\nfrontend-design@claude-code-plugins # UI/UX \u6a21\u5f0f\ncommit-commands@claude-code-plugins # Git \u5de5\u4f5c\u6d41\nsecurity-guidance@claude-code-plugins # \u5b89\u5168\u68c0\u67e5\npr-review-toolkit@claude-code-plugins # PR \u81ea\u52a8\u5316\ntypescript-lsp@claude-plugins-official # TS \u667a\u80fd\u63d0\u793a\nhookify@claude-plugins-official # \u94a9\u5b50\u521b\u5efa\ncode-simplifier@claude-plugins-official\nfeature-dev@claude-code-plugins\nexplanatory-output-style@claude-plugins-official\ncode-review@claude-code-plugins\ncontext7@claude-plugins-official # \u5b9e\u65f6\u6587\u6863\npyright-lsp@claude-plugins-official # Python \u7c7b\u578b\nmgrep@Mixedbread-Grep # \u66f4\u597d\u7684\u641c\u7d22\n```\n\n### MCP \u670d\u52a1\u5668\n\n**\u5df2\u914d\u7f6e\uff08\u7528\u6237\u7ea7\uff09\uff1a**\n\n```json\n{\n \"github\": { \"command\": \"npx\", \"args\": [\"-y\", \"@modelcontextprotocol/server-github\"] },\n \"firecrawl\": { \"command\": \"npx\", \"args\": [\"-y\", \"firecrawl-mcp\"] },\n \"supabase\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@supabase/mcp-server-supabase@latest\", \"--project-ref=YOUR_REF\"]\n },\n \"memory\": { \"command\": \"npx\", \"args\": [\"-y\", \"@modelcontextprotocol/server-memory\"] },\n \"sequential-thinking\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@modelcontextprotocol/server-sequential-thinking\"]\n },\n \"vercel\": { \"type\": \"http\", \"url\": \"https://mcp.vercel.com\" },\n \"railway\": { \"command\": \"npx\", \"args\": [\"-y\", \"@railway/mcp-server\"] },\n \"cloudflare-docs\": { \"type\": \"http\", \"url\": \"https://docs.mcp.cloudflare.com/mcp\" },\n \"cloudflare-workers-bindings\": {\n \"type\": \"http\",\n \"url\": \"https://bindings.mcp.cloudflare.com/mcp\"\n },\n \"clickhouse\": { \"type\": \"http\", \"url\": \"https://mcp.clickhouse.cloud/mcp\" },\n \"AbletonMCP\": { \"command\": \"uvx\", \"args\": [\"ableton-mcp\"] },\n \"magic\": { \"command\": \"npx\", \"args\": [\"-y\", \"@magicuidesign/mcp@latest\"] }\n}\n```\n\n\u5173\u952e\u5728\u4e8e\uff1a\u6211\u914d\u7f6e\u4e86 14 \u4e2a MCP\uff0c\u4f46\u6bcf\u4e2a\u9879\u76ee\u53ea\u542f\u7528 ~5-6 \u4e2a\u3002\u8fd9\u80fd\u4fdd\u6301\u4e0a\u4e0b\u6587\u7a97\u53e3\u7684\u5065\u5eb7\u3002\n\n### \u5173\u952e\u94a9\u5b50 (Key Hooks)\n\n```json\n{\n \"PreToolUse\": [\n { \"matcher\": \"npm|pnpm|yarn|cargo|pytest\", \"hooks\": [\"tmux reminder\"] },\n { \"matcher\": \"Write && .md file\", \"hooks\": [\"block unless README/CLAUDE\"] },\n { \"matcher\": \"git push\", \"hooks\": [\"open editor for review\"] }\n ],\n \"PostToolUse\": [\n { \"matcher\": \"Edit && .ts/.tsx/.js/.jsx\", \"hooks\": [\"prettier --write\"] },\n { \"matcher\": \"Edit && .ts/.tsx\", \"hooks\": [\"tsc --noEmit\"] },\n { \"matcher\": \"Edit\", \"hooks\": [\"grep console.log warning\"] }\n ],\n \"Stop\": [\n { \"matcher\": \"*\", \"hooks\": [\"check modified files for console.log\"] }\n ]\n}\n```\n\n### \u81ea\u5b9a\u4e49\u72b6\u6001\u680f (Custom Status Line)\n\n\u663e\u793a\u7528\u6237\u3001\u76ee\u5f55\u3001\u5e26\u810f\u6807\u8bb0\u7684 Git \u5206\u652f\u3001\u5269\u4f59\u4e0a\u4e0b\u6587 %\u3001\u6a21\u578b\u3001\u65f6\u95f4\u548c\u5f85\u529e\u4e8b\u9879\u8ba1\u6570\uff1a\n\n\n*\u6211\u7684 Mac \u6839\u76ee\u5f55\u4e0b\u7684\u72b6\u6001\u680f\u793a\u4f8b*\n\n```\naffoon:~ ctx:65% Opus 4.5 19:52\n\u258c\u258c \u89c4\u5212\u6a21\u5f0f\u5df2\u5f00\u542f (\u6309 shift+tab \u5207\u6362)\n```\n\n### \u89c4\u5219\u7ed3\u6784 (Rules Structure)\n\n```\n~/.claude/rules/\n security.md # \u5f3a\u5236\u6027\u5b89\u5168\u68c0\u67e5\n coding-style.md # \u4e0d\u53ef\u53d8\u6027\uff0c\u6587\u4ef6\u5927\u5c0f\u9650\u5236\n testing.md # TDD\uff0c80% \u8986\u76d6\u7387\n git-workflow.md # \u7ea6\u5b9a\u5f0f\u63d0\u4ea4 (Conventional commits)\n agents.md # \u5b50\u667a\u80fd\u4f53\u59d4\u6d3e\u89c4\u5219\n patterns.md # API \u54cd\u5e94\u683c\u5f0f\n performance.md # \u6a21\u578b\u9009\u62e9 (Haiku vs Sonnet vs Opus)\n hooks.md # \u94a9\u5b50\u6587\u6863\u8bf4\u660e\n```\n\n### \u5b50\u667a\u80fd\u4f53 (Subagents)\n\n```\n~/.claude/agents/\n planner.md # \u5206\u89e3\u529f\u80fd\u9700\u6c42\n architect.md # \u7cfb\u7edf\u8bbe\u8ba1\n tdd-guide.md # \u6d4b\u8bd5\u5148\u884c\n code-reviewer.md # \u8d28\u91cf\u5ba1\u67e5\n security-reviewer.md # \u6f0f\u6d1e\u626b\u63cf\n build-error-resolver.md\n e2e-runner.md # Playwright \u6d4b\u8bd5\n refactor-cleaner.md # \u5e9f\u5f03\u4ee3\u7801\u6e05\u7406\n doc-updater.md # \u4fdd\u6301\u6587\u6863\u540c\u6b65\n```\n\n---\n\n## \u6838\u5fc3\u8981\u70b9\n\n1. **\u4e0d\u8981\u8fc7\u5ea6\u590d\u6742\u5316** - \u5c06\u914d\u7f6e\u89c6\u4e3a\u5fae\u8c03\uff0c\u800c\u975e\u67b6\u6784\u8bbe\u8ba1\n2. **\u4e0a\u4e0b\u6587\u7a97\u53e3\u6781\u5176\u73cd\u8d35** - \u7981\u7528\u4e0d\u4f7f\u7528\u7684 MCP \u548c\u63d2\u4ef6\n3. **\u5e76\u884c\u6267\u884c** - \u6d3e\u751f\u5bf9\u8bdd\uff0c\u5229\u7528 Git \u5de5\u4f5c\u6811\n4. **\u81ea\u52a8\u5316\u91cd\u590d\u6027\u4efb\u52a1** - \u4e3a\u683c\u5f0f\u5316\u3001\u4ee3\u7801\u68c0\u67e5\u3001\u63d0\u9192\u8bbe\u7f6e\u94a9\u5b50\n5. **\u660e\u786e\u5b50\u667a\u80fd\u4f53\u7684\u4f5c\u7528\u57df** - \u53d7\u9650\u7684\u5de5\u5177 = \u4e13\u6ce8\u7684\u6267\u884c\n\n---\n\n## \u53c2\u8003\u8d44\u6599\n\n- [\u63d2\u4ef6\u53c2\u8003 (Plugins Reference)](https://code.claude.com/docs/en/plugins-reference)\n- [\u94a9\u5b50\u6587\u6863 (Hooks Documentation)](https://code.claude.com/docs/en/hooks)\n- [\u68c0\u67e5\u70b9\u529f\u80fd (Checkpointing)](https://code.claude.com/docs/en/checkpointing)\n- [\u4ea4\u4e92\u6a21\u5f0f (Interactive Mode)](https://code.claude.com/docs/en/interactive-mode)\n- [\u8bb0\u5fc6\u7cfb\u7edf (Memory System)](https://code.claude.com/docs/en/memory)\n- [\u5b50\u667a\u80fd\u4f53 (Subagents)](https://code.claude.com/docs/en/sub-agents)\n- [MCP \u6982\u89c8 (MCP Overview)](https://code.claude.com/docs/en/mcp-overview)\n\n---\n\n**\u6ce8\uff1a** \u4ee5\u4e0a\u4ec5\u4e3a\u90e8\u5206\u7ec6\u8282\u3002\u8fdb\u9636\u6a21\u5f0f\u8bf7\u53c2\u9605 [\u957f\u7bc7\u6307\u5357 (Longform Guide)](./the-longform-guide.md)\u3002\n\n---\n\n*\u4e0e [@DRodriguezFX](https://x.com/DRodriguezFX) \u5728\u7ebd\u7ea6\u5171\u540c\u5f00\u53d1 [zenith.chat](https://zenith.chat)\uff0c\u5e76\u8d62\u5f97 Anthropic x Forum Ventures \u9ed1\u5ba2\u677e\u3002*\n"
},
"/Users/Library/Applications/xx/code/github/everything-claude-code-zh/README.md": {
- "md5": "1d090c2531031d817553cd6b1e0b9b6d",
- "content": "**\u8bed\u8a00\uff1a** [English](README.md) | \u7e41\u9ad4\u4e2d\u6587\n\n# Everything Claude Code\n\n[](https://github.com/affaan-m/everything-claude-code/stargazers)\n[](LICENSE)\n\n\n\n\n\n\n English |\n \u7b80\u4f53\u4e2d\u6587 \n
\n\n**\u7531 Anthropic \u9ed1\u5ba2\u677e\u83b7\u80dc\u8005\u6574\u7406\u7684 Claude Code \u914d\u7f6e\u5b8c\u6574\u5408\u96c6\u3002**\n\n\u8fd9\u662f\u5728 10 \u4e2a\u6708\u4ee5\u4e0a\u9ad8\u5f3a\u5ea6\u65e5\u5e38\u5f00\u53d1\u771f\u5b9e\u4ea7\u54c1\u7684\u8fc7\u7a0b\u4e2d\uff0c\u4e0d\u65ad\u6f14\u8fdb\u51fa\u7684\u751f\u4ea7\u7ea7\u667a\u80fd\u4f53\uff08Agents\uff09\u3001\u6280\u80fd\uff08Skills\uff09\u3001\u94a9\u5b50\uff08Hooks\uff09\u3001\u547d\u4ee4\uff08Commands\uff09\u3001\u89c4\u5219\uff08Rules\uff09\u4ee5\u53ca MCP \u914d\u7f6e\u3002\n\n---\n\n## \u6307\u5357\uff08The Guides\uff09\n\n\u672c\u4ed3\u5e93\u4ec5\u5305\u542b\u539f\u59cb\u4ee3\u7801\u3002\u4ee5\u4e0b\u6307\u5357\u5c06\u89e3\u91ca\u6240\u6709\u7ec6\u8282\u3002\n\n\n\n\n\n \n \n \n\n\n \n \n \n \n\n\u7b80\u660e\u6307\u5357 (Shorthand Guide) \u5b89\u88c5\u3001\u57fa\u7840\u3001\u54f2\u5b66\u3002\u8bf7\u5148\u9605\u8bfb\u6b64\u7bc7\u3002 \n\u6df1\u5ea6\u6307\u5357 (Longform Guide) Token \u4f18\u5316\u3001\u5185\u5b58\u6301\u4e45\u5316\u3001\u8bc4\u6d4b\uff08Evals\uff09\u3001\u5e76\u884c\u5316\u3002 \n \n
\n\n| \u4e3b\u9898 | \u4f60\u5c06\u5b66\u5230\u4ec0\u4e48 |\n|-------|-------------------|\n| Token \u4f18\u5316 | \u6a21\u578b\u9009\u62e9\u3001\u7cfb\u7edf\u63d0\u793a\u8bcd\u7cbe\u7b80\u3001\u540e\u53f0\u8fdb\u7a0b |\n| \u5185\u5b58\u6301\u4e45\u5316 | \u8de8\u4f1a\u8bdd\uff08Session\uff09\u81ea\u52a8\u4fdd\u5b58/\u52a0\u8f7d\u4e0a\u4e0b\u6587\u7684\u94a9\u5b50\uff08Hooks\uff09 |\n| \u6301\u7eed\u5b66\u4e60 | \u4ece\u4f1a\u8bdd\u4e2d\u81ea\u52a8\u63d0\u53d6\u6a21\u5f0f\u5e76\u8f6c\u5316\u4e3a\u53ef\u590d\u7528\u7684\u6280\u80fd\uff08Skills\uff09 |\n| \u9a8c\u8bc1\u5faa\u73af | \u68c0\u67e5\u70b9\uff08Checkpoint\uff09\u4e0e\u6301\u7eed\u8bc4\u6d4b\uff08Evals\uff09\u3001\u8bc4\u5206\u5668\u7c7b\u578b\u3001pass@k \u6307\u6807 |\n| \u5e76\u884c\u5316 | Git worktrees\u3001\u7ea7\u8054\u65b9\u6cd5\u3001\u4f55\u65f6\u6269\u5c55\u5b9e\u4f8b |\n| \u5b50\u667a\u80fd\u4f53\u7f16\u6392 | \u4e0a\u4e0b\u6587\u95ee\u9898\u3001\u8fed\u4ee3\u68c0\u7d22\u6a21\u5f0f |\n\n---\n\n## \u8de8\u5e73\u53f0\u652f\u6301\n\n\u8be5\u63d2\u4ef6\u73b0\u5df2\u5168\u9762\u652f\u6301 **Windows\u3001macOS \u548c Linux**\u3002\u6240\u6709\u94a9\u5b50\uff08Hooks\uff09\u548c\u811a\u672c\u90fd\u5df2\u4f7f\u7528 Node.js \u91cd\u5199\uff0c\u4ee5\u5b9e\u73b0\u6700\u5927\u7684\u517c\u5bb9\u6027\u3002\n\n### \u5305\u7ba1\u7406\u5668\u68c0\u6d4b\n\n\u63d2\u4ef6\u4f1a\u81ea\u52a8\u68c0\u6d4b\u4f60\u504f\u597d\u7684\u5305\u7ba1\u7406\u5668\uff08npm, pnpm, yarn, \u6216 bun\uff09\uff0c\u4f18\u5148\u7ea7\u5982\u4e0b\uff1a\n\n1. **\u73af\u5883\u53d8\u91cf**\uff1a`CLAUDE_PACKAGE_MANAGER`\n2. **\u9879\u76ee\u914d\u7f6e**\uff1a`.claude/package-manager.json`\n3. **package.json**\uff1a`packageManager` \u5b57\u6bb5\n4. **\u9501\u6587\u4ef6**\uff1a\u6839\u636e package-lock.json, yarn.lock, pnpm-lock.yaml \u6216 bun.lockb \u68c0\u6d4b\n5. **\u5168\u5c40\u914d\u7f6e**\uff1a`~/.claude/package-manager.json`\n6. **\u5907\u9009\u9879**\uff1a\u7b2c\u4e00\u4e2a\u53ef\u7528\u7684\u5305\u7ba1\u7406\u5668\n\n\u8bbe\u7f6e\u4f60\u504f\u597d\u7684\u5305\u7ba1\u7406\u5668\uff1a\n\n```bash\n# \u901a\u8fc7\u73af\u5883\u53d8\u91cf\nexport CLAUDE_PACKAGE_MANAGER=pnpm\n\n# \u901a\u8fc7\u5168\u5c40\u914d\u7f6e\nnode scripts/setup-package-manager.js --global pnpm\n\n# \u901a\u8fc7\u9879\u76ee\u914d\u7f6e\nnode scripts/setup-package-manager.js --project bun\n\n# \u68c0\u6d4b\u5f53\u524d\u8bbe\u7f6e\nnode scripts/setup-package-manager.js --detect\n```\n\n\u6216\u8005\u5728 Claude Code \u4e2d\u4f7f\u7528 `/setup-pm` \u547d\u4ee4\u3002\n\n---\n\n## \u5305\u542b\u5185\u5bb9\n\n\u672c\u4ed3\u5e93\u662f\u4e00\u4e2a **Claude Code \u63d2\u4ef6** \u2014\u2014 \u4f60\u53ef\u4ee5\u76f4\u63a5\u5b89\u88c5\u5b83\uff0c\u6216\u8005\u624b\u52a8\u590d\u5236\u7ec4\u4ef6\u3002\n\n```\neverything-claude-code/\n|-- .claude-plugin/ # \u63d2\u4ef6\u4e0e\u5e02\u573a\u6e05\u5355\n| |-- plugin.json # \u63d2\u4ef6\u5143\u6570\u636e\u4e0e\u7ec4\u4ef6\u8def\u5f84\n| |-- marketplace.json # \u7528\u4e8e /plugin marketplace add \u7684\u5e02\u573a\u76ee\u5f55\n|\n|-- agents/ # \u7528\u4e8e\u4efb\u52a1\u59d4\u6d3e\u7684\u4e13\u7528\u5b50\u667a\u80fd\u4f53\uff08Subagents\uff09\n| |-- planner.md # \u529f\u80fd\u5b9e\u73b0\u89c4\u5212\n| |-- architect.md # \u7cfb\u7edf\u8bbe\u8ba1\u51b3\u7b56\n| |-- tdd-guide.md # \u6d4b\u8bd5\u9a71\u52a8\u5f00\u53d1\uff08TDD\uff09\n| |-- code-reviewer.md # \u4ee3\u7801\u8d28\u91cf\u4e0e\u5b89\u5168\u8bc4\u5ba1\n| |-- security-reviewer.md # \u6f0f\u6d1e\u5206\u6790\n| |-- build-error-resolver.md # \u6784\u5efa\u9519\u8bef\u89e3\u51b3\n| |-- e2e-runner.md # Playwright \u7aef\u5230\u7aef\u6d4b\u8bd5\n| |-- refactor-cleaner.md # \u6b7b\u4ee3\u7801\u6e05\u7406\n| |-- doc-updater.md # \u6587\u6863\u540c\u6b65\n| |-- go-reviewer.md # Go \u4ee3\u7801\u8bc4\u5ba1\uff08\u65b0\u589e\uff09\n| |-- go-build-resolver.md # Go \u6784\u5efa\u9519\u8bef\u89e3\u51b3\uff08\u65b0\u589e\uff09\n|\n|-- skills/ # \u5de5\u4f5c\u6d41\uff08Workflow\uff09\u5b9a\u4e49\u4e0e\u9886\u57df\u77e5\u8bc6\n| |-- coding-standards/ # \u8bed\u8a00\u6700\u4f73\u5b9e\u8df5\n| |-- backend-patterns/ # API\u3001\u6570\u636e\u5e93\u3001\u7f13\u5b58\u6a21\u5f0f\n| |-- frontend-patterns/ # React\u3001Next.js \u6a21\u5f0f\n| |-- continuous-learning/ # \u4ece\u4f1a\u8bdd\u4e2d\u81ea\u52a8\u63d0\u53d6\u6a21\u5f0f\uff08\u6df1\u5ea6\u6307\u5357\u5185\u5bb9\uff09\n| |-- continuous-learning-v2/ # \u57fa\u4e8e\u76f4\u89c9\uff08Instinct\uff09\u7684\u5e26\u7f6e\u4fe1\u5ea6\u8bc4\u5206\u5b66\u4e60\u7cfb\u7edf\n| |-- iterative-retrieval/ # \u5b50\u667a\u80fd\u4f53\u7684\u6e10\u8fdb\u5f0f\u4e0a\u4e0b\u6587\u7cbe\u70bc\n| |-- strategic-compact/ # \u624b\u52a8\u538b\u7f29\u5efa\u8bae\uff08\u6df1\u5ea6\u6307\u5357\u5185\u5bb9\uff09\n| |-- tdd-workflow/ # TDD \u65b9\u6cd5\u8bba\n| |-- security-review/ # \u5b89\u5168\u81ea\u67e5\u8868\n| |-- eval-harness/ # \u9a8c\u8bc1\u5faa\u73af\u8bc4\u6d4b\uff08\u6df1\u5ea6\u6307\u5357\u5185\u5bb9\uff09\n| |-- verification-loop/ # \u6301\u7eed\u9a8c\u8bc1\uff08\u6df1\u5ea6\u6307\u5357\u5185\u5bb9\uff09\n| |-- golang-patterns/ # Go \u60ef\u7528\u6cd5\u4e0e\u6700\u4f73\u5b9e\u8df5\uff08\u65b0\u589e\uff09\n| |-- golang-testing/ # Go \u6d4b\u8bd5\u6a21\u5f0f\u3001TDD\u3001\u57fa\u51c6\u6d4b\u8bd5\uff08\u65b0\u589e\uff09\n|\n|-- commands/ # \u7528\u4e8e\u5feb\u901f\u6267\u884c\u7684\u659c\u6760\u547d\u4ee4\uff08Slash Commands\uff09\n| |-- tdd.md # /tdd - \u6d4b\u8bd5\u9a71\u52a8\u5f00\u53d1\n| |-- plan.md # /plan - \u5b9e\u73b0\u89c4\u5212\n| |-- e2e.md # /e2e - \u7aef\u5230\u7aef\u6d4b\u8bd5\u751f\u6210\n| |-- code-review.md # /code-review - \u8d28\u91cf\u8bc4\u5ba1\n| |-- build-fix.md # /build-fix - \u4fee\u590d\u6784\u5efa\u9519\u8bef\n| |-- refactor-clean.md # /refactor-clean - \u79fb\u9664\u6b7b\u4ee3\u7801\n| |-- learn.md # /learn - \u4f1a\u8bdd\u4e2d\u9014\u63d0\u53d6\u6a21\u5f0f\uff08\u6df1\u5ea6\u6307\u5357\u5185\u5bb9\uff09\n| |-- checkpoint.md # /checkpoint - \u4fdd\u5b58\u9a8c\u8bc1\u72b6\u6001\uff08\u6df1\u5ea6\u6307\u5357\u5185\u5bb9\uff09\n| |-- verify.md # /verify - \u8fd0\u884c\u9a8c\u8bc1\u5faa\u73af\uff08\u6df1\u5ea6\u6307\u5357\u5185\u5bb9\uff09\n| |-- setup-pm.md # /setup-pm - \u914d\u7f6e\u5305\u7ba1\u7406\u5668\n| |-- go-review.md # /go-review - Go \u4ee3\u7801\u8bc4\u5ba1\uff08\u65b0\u589e\uff09\n| |-- go-test.md # /go-test - Go TDD \u5de5\u4f5c\u6d41\uff08\u65b0\u589e\uff09\n| |-- go-build.md # /go-build - \u4fee\u590d Go \u6784\u5efa\u9519\u8bef\uff08\u65b0\u589e\uff09\n| |-- skill-create.md # /skill-create - \u4ece git \u5386\u53f2\u751f\u6210\u6280\u80fd\uff08\u65b0\u589e\uff09\n| |-- instinct-status.md # /instinct-status - \u67e5\u770b\u5df2\u5b66\u4e60\u7684\u76f4\u89c9\uff08\u65b0\u589e\uff09\n| |-- instinct-import.md # /instinct-import - \u5bfc\u5165\u76f4\u89c9\uff08\u65b0\u589e\uff09\n| |-- instinct-export.md # /instinct-export - \u5bfc\u51fa\u76f4\u89c9\uff08\u65b0\u589e\uff09\n| |-- evolve.md # /evolve - \u5c06\u76f8\u5173\u76f4\u89c9\u805a\u7c7b\u4e3a\u6280\u80fd\uff08\u65b0\u589e\uff09\n|\n|-- rules/ # \u5fc5\u987b\u9075\u5b88\u7684\u51c6\u5219\uff08\u590d\u5236\u5230 ~/.claude/rules/\uff09\n| |-- security.md # \u5f3a\u5236\u6027\u5b89\u5168\u68c0\u67e5\n| |-- coding-style.md # \u4e0d\u53ef\u53d8\u6027\u3001\u6587\u4ef6\u7ec4\u7ec7\n| |-- testing.md # TDD\u300180% \u8986\u76d6\u7387\u8981\u6c42\n| |-- git-workflow.md # \u63d0\u4ea4\u683c\u5f0f\u3001PR \u6d41\u7a0b\n| |-- agents.md # \u4f55\u65f6\u59d4\u6d3e\u7ed9\u5b50\u667a\u80fd\u4f53\n| |-- performance.md # \u6a21\u578b\u9009\u62e9\u3001\u4e0a\u4e0b\u6587\u7ba1\u7406\n|\n|-- hooks/ # \u57fa\u4e8e\u89e6\u53d1\u5668\u7684\u81ea\u52a8\u5316\n| |-- hooks.json # \u6240\u6709\u94a9\u5b50\u914d\u7f6e\uff08PreToolUse, PostToolUse, Stop \u7b49\uff09\n| |-- memory-persistence/ # \u4f1a\u8bdd\u751f\u547d\u5468\u671f\u94a9\u5b50\uff08\u6df1\u5ea6\u6307\u5357\u5185\u5bb9\uff09\n| |-- strategic-compact/ # \u538b\u7f29\u5efa\u8bae\uff08\u6df1\u5ea6\u6307\u5357\u5185\u5bb9\uff09\n|\n|-- scripts/ # \u8de8\u5e73\u53f0 Node.js \u811a\u672c\uff08\u65b0\u589e\uff09\n| |-- lib/ # \u5171\u4eab\u5b9e\u7528\u7a0b\u5e8f\n| | |-- utils.js # \u8de8\u5e73\u53f0\u6587\u4ef6/\u8def\u5f84/\u7cfb\u7edf\u5de5\u5177\n| | |-- package-manager.js # \u5305\u7ba1\u7406\u5668\u68c0\u6d4b\u4e0e\u9009\u62e9\n| |-- hooks/ # \u94a9\u5b50\u5b9e\u73b0\n| | |-- session-start.js # \u4f1a\u8bdd\u5f00\u59cb\u65f6\u52a0\u8f7d\u4e0a\u4e0b\u6587\n| | |-- session-end.js # \u4f1a\u8bdd\u7ed3\u675f\u65f6\u4fdd\u5b58\u72b6\u6001\n| | |-- pre-compact.js # \u538b\u7f29\u524d\u72b6\u6001\u4fdd\u5b58\n| | |-- suggest-compact.js # \u6218\u7565\u6027\u538b\u7f29\u5efa\u8bae\n| | |-- evaluate-session.js # \u4ece\u4f1a\u8bdd\u4e2d\u63d0\u53d6\u6a21\u5f0f\n| |-- setup-package-manager.js # \u4ea4\u4e92\u5f0f\u5305\u7ba1\u7406\u5668\u8bbe\u7f6e\n|\n|-- tests/ # \u6d4b\u8bd5\u5957\u4ef6\uff08\u65b0\u589e\uff09\n| |-- lib/ # \u5e93\u6d4b\u8bd5\n| |-- hooks/ # \u94a9\u5b50\u6d4b\u8bd5\n| |-- run-all.js # \u8fd0\u884c\u6240\u6709\u6d4b\u8bd5\n|\n|-- contexts/ # \u52a8\u6001\u7cfb\u7edf\u63d0\u793a\u8bcd\u6ce8\u5165\u4e0a\u4e0b\u6587\uff08\u6df1\u5ea6\u6307\u5357\u5185\u5bb9\uff09\n| |-- dev.md # \u5f00\u53d1\u6a21\u5f0f\u4e0a\u4e0b\u6587\n| |-- review.md # \u4ee3\u7801\u8bc4\u5ba1\u6a21\u5f0f\u4e0a\u4e0b\u6587\n| |-- research.md # \u7814\u7a76/\u63a2\u7d22\u6a21\u5f0f\u4e0a\u4e0b\u6587\n|\n|-- examples/ # \u914d\u7f6e\u4e0e\u4f1a\u8bdd\u793a\u4f8b\n| |-- CLAUDE.md # \u9879\u76ee\u7ea7\u914d\u7f6e\u793a\u4f8b\n| |-- user-CLAUDE.md # \u7528\u6237\u7ea7\u914d\u7f6e\u793a\u4f8b\n|\n|-- mcp-configs/ # MCP \u670d\u52a1\u914d\u7f6e\n| |-- mcp-servers.json # GitHub, Supabase, Vercel, Railway \u7b49\n|\n|-- marketplace.json # \u81ea\u6258\u7ba1\u5e02\u573a\u914d\u7f6e\uff08\u7528\u4e8e /plugin marketplace add\uff09\n```\n\n---\n\n## \u751f\u6001\u5de5\u5177\n\n### \u6280\u80fd\u521b\u5efa\u5668\uff08Skill Creator\uff09\n\n\u6709\u4e24\u79cd\u65b9\u6cd5\u53ef\u4ee5\u4ece\u4f60\u7684\u4ed3\u5e93\u751f\u6210 Claude Code \u6280\u80fd\uff1a\n\n#### \u65b9\u6848 A\uff1a\u672c\u5730\u5206\u6790\uff08\u5185\u7f6e\uff09\n\n\u4f7f\u7528 `/skill-create` \u547d\u4ee4\u8fdb\u884c\u672c\u5730\u5206\u6790\uff0c\u65e0\u9700\u5916\u90e8\u670d\u52a1\uff1a\n\n```bash\n/skill-create # \u5206\u6790\u5f53\u524d\u4ed3\u5e93\n/skill-create --instincts # \u540c\u65f6\u4e3a\u6301\u7eed\u5b66\u4e60\uff08continuous-learning\uff09\u751f\u6210\u76f4\u89c9\uff08instincts\uff09\n```\n\n\u8be5\u547d\u4ee4\u4f1a\u5728\u672c\u5730\u5206\u6790\u4f60\u7684 git \u5386\u53f2\u5e76\u751f\u6210 SKILL.md \u6587\u4ef6\u3002\n\n#### \u65b9\u6848 B\uff1aGitHub App\uff08\u9ad8\u7ea7\uff09\n\n\u9002\u7528\u4e8e\u9ad8\u7ea7\u529f\u80fd\uff081\u4e07+ commit\u3001\u81ea\u52a8 PR\u3001\u56e2\u961f\u5171\u4eab\uff09\uff1a\n\n[\u5b89\u88c5 GitHub App](https://github.com/apps/skill-creator) | [ecc.tools](https://ecc.tools)\n\n```bash\n# \u5728\u4efb\u4f55 issue \u4e0b\u7559\u8a00\uff1a\n/skill-creator analyze\n\n# \u6216\u8005\u5728 push \u5230\u9ed8\u8ba4\u5206\u652f\u65f6\u81ea\u52a8\u89e6\u53d1\n```\n\n\u4e24\u79cd\u65b9\u6848\u90fd\u4f1a\u521b\u5efa\uff1a\n- **SKILL.md \u6587\u4ef6** - \u53ef\u76f4\u63a5\u7528\u4e8e Claude Code \u7684\u6280\u80fd\n- **\u76f4\u89c9\u96c6\u5408\uff08Instinct collections\uff09** - \u7528\u4e8e continuous-learning-v2\n- **\u6a21\u5f0f\u63d0\u53d6\uff08Pattern extraction\uff09** - \u4ece\u4f60\u7684\u63d0\u4ea4\u5386\u53f2\u4e2d\u5b66\u4e60\n\n### \u6301\u7eed\u5b66\u4e60\uff08Continuous Learning\uff09v2\n\n\u57fa\u4e8e\u76f4\u89c9\uff08instinct\uff09\u7684\u5b66\u4e60\u7cfb\u7edf\u4f1a\u81ea\u52a8\u5b66\u4e60\u4f60\u7684\u5f00\u53d1\u6a21\u5f0f\uff1a\n\n```bash\n/instinct-status # \u663e\u793a\u5e26\u6709\u7f6e\u4fe1\u5ea6\u7684\u5df2\u5b66\u4e60\u76f4\u89c9\n/instinct-import # \u5bfc\u5165\u4ed6\u4eba\u7684\u76f4\u89c9\n/instinct-export # \u5bfc\u51fa\u4f60\u7684\u76f4\u89c9\u4ee5\u4fbf\u5206\u4eab\n/evolve # \u5c06\u76f8\u5173\u7684\u76f4\u89c9\u805a\u7c7b\u4e3a\u6280\u80fd\uff08skills\uff09\n```\n\n\u8be6\u89c1 `skills/continuous-learning-v2/` \u7684\u5b8c\u6574\u6587\u6863\u3002\n\n---\n\n## \u8981\u6c42\n\n### Claude Code CLI \u7248\u672c\n\n**\u6700\u4f4e\u7248\u672c\uff1av2.1.0 \u6216\u66f4\u9ad8**\n\n\u7531\u4e8e\u63d2\u4ef6\u7cfb\u7edf\u5904\u7406\u94a9\u5b50\uff08hooks\uff09\u65b9\u5f0f\u7684\u53d8\u66f4\uff0c\u672c\u63d2\u4ef6\u8981\u6c42 Claude Code CLI v2.1.0+\u3002\n\n\u68c0\u67e5\u4f60\u7684\u7248\u672c\uff1a\n```bash\nclaude --version\n```\n\n### \u91cd\u8981\uff1a\u94a9\u5b50\uff08Hooks\uff09\u81ea\u52a8\u52a0\u8f7d\u884c\u4e3a\n\n> \u26a0\ufe0f **\u5bf9\u8d21\u732e\u8005\u7684\u63d0\u9192\uff1a** \u8bf7\u52ff\u5728 `.claude-plugin/plugin.json` \u4e2d\u6dfb\u52a0 `\"hooks\"` \u5b57\u6bb5\u3002\u8fd9\u662f\u7531\u56de\u5f52\u6d4b\u8bd5\u5f3a\u5236\u6267\u884c\u7684\u3002\n\nClaude Code v2.1+ \u4f1a**\u81ea\u52a8\u52a0\u8f7d**\u5df2\u5b89\u88c5\u63d2\u4ef6\u4e2d\u7ea6\u5b9a\u7684 `hooks/hooks.json`\u3002\u5728 `plugin.json` \u4e2d\u663e\u5f0f\u58f0\u660e\u4f1a\u5bfc\u81f4\u91cd\u590d\u68c0\u6d4b\u9519\u8bef\uff1a\n\n```\nDuplicate hooks file detected: ./hooks/hooks.json resolves to already-loaded file\n```\n\n**\u5386\u53f2\u80cc\u666f\uff1a** \u6b64\u95ee\u9898\u5728\u672c\u4ed3\u5e93\u4e2d\u66fe\u591a\u6b21\u51fa\u73b0\u4fee\u590d/\u56de\u9000\u5faa\u73af\uff08[#29](https://github.com/affaan-m/everything-claude-code/issues/29), [#52](https://github.com/affaan-m/everything-claude-code/issues/52), [#103](https://github.com/affaan-m/everything-claude-code/issues/103)\uff09\u3002Claude Code \u7248\u672c\u95f4\u7684\u884c\u4e3a\u5dee\u5f02\u5bfc\u81f4\u4e86\u6df7\u6dc6\u3002\u6211\u4eec\u73b0\u5728\u5df2\u52a0\u5165\u56de\u5f52\u6d4b\u8bd5\u4ee5\u9632\u6b62\u6b64\u7c7b\u95ee\u9898\u518d\u6b21\u53d1\u751f\u3002\n\n---\n\n## \u5b89\u88c5\n\n### \u65b9\u6848 1\uff1a\u4f5c\u4e3a\u63d2\u4ef6\u5b89\u88c5\uff08\u63a8\u8350\uff09\n\n\u8fd9\u662f\u4f7f\u7528\u672c\u4ed3\u5e93\u6700\u7b80\u5355\u7684\u65b9\u6cd5 \u2014\u2014 \u4f5c\u4e3a Claude Code \u63d2\u4ef6\u5b89\u88c5\uff1a\n\n```bash\n# \u5c06\u6b64\u4ed3\u5e93\u6dfb\u52a0\u4e3a\u5e02\u573a\uff08marketplace\uff09\n/plugin marketplace add affaan-m/everything-claude-code\n\n# \u5b89\u88c5\u63d2\u4ef6\n/plugin install everything-claude-code@everything-claude-code\n```\n\n\u6216\u8005\u76f4\u63a5\u6dfb\u52a0\u5230\u4f60\u7684 `~/.claude/settings.json`\uff1a\n\n```json\n{\n \"extraKnownMarketplaces\": {\n \"everything-claude-code\": {\n \"source\": {\n \"source\": \"github\",\n \"repo\": \"affaan-m/everything-claude-code\"\n }\n }\n },\n \"enabledPlugins\": {\n \"everything-claude-code@everything-claude-code\": true\n }\n}\n```\n\n\u5b89\u88c5\u540e\u5373\u53ef\u7acb\u5373\u4f7f\u7528\u6240\u6709\u547d\u4ee4\uff08commands\uff09\u3001\u667a\u80fd\u4f53\uff08agents\uff09\u3001\u6280\u80fd\uff08skills\uff09\u548c\u94a9\u5b50\uff08hooks\uff09\u3002\n\n> **\u6ce8\u610f\uff1a** Claude Code \u63d2\u4ef6\u7cfb\u7edf\u76ee\u524d\u4e0d\u652f\u6301\u901a\u8fc7\u63d2\u4ef6\u5206\u53d1\u89c4\u5219\uff08`rules`\uff09\uff08\u8fd9\u662f [\u4e0a\u6e38\u9650\u5236](https://code.claude.com/docs/en/plugins-reference)\uff09\u3002\u4f60\u9700\u8981\u624b\u52a8\u5b89\u88c5\u89c4\u5219\uff1a\n>\n> ```bash\n> # \u9996\u5148\u514b\u9686\u4ed3\u5e93\n> git clone https://github.com/affaan-m/everything-claude-code.git\n>\n> # \u65b9\u6848 A\uff1a\u7528\u6237\u7ea7\u89c4\u5219\uff08\u5e94\u7528\u4e8e\u6240\u6709\u9879\u76ee\uff09\n> cp -r everything-claude-code/rules/* ~/.claude/rules/\n>\n> # \u65b9\u6848 B\uff1a\u9879\u76ee\u7ea7\u89c4\u5219\uff08\u4ec5\u5e94\u7528\u4e8e\u5f53\u524d\u9879\u76ee\uff09\n> mkdir -p .claude/rules\n> cp -r everything-claude-code/rules/* .claude/rules/\n> ```\n\n---\n\n### \u65b9\u6848 2\uff1a\u624b\u52a8\u5b89\u88c5\n\n\u5982\u679c\u4f60\u66f4\u503e\u5411\u4e8e\u624b\u52a8\u63a7\u5236\u5b89\u88c5\u7684\u5185\u5bb9\uff1a\n\n```bash\n# \u514b\u9686\u4ed3\u5e93\ngit clone https://github.com/affaan-m/everything-claude-code.git\n\n# \u590d\u5236\u667a\u80fd\u4f53\uff08agents\uff09\u5230\u4f60\u7684 Claude \u914d\u7f6e\u76ee\u5f55\ncp everything-claude-code/agents/*.md ~/.claude/agents/\n\n# \u590d\u5236\u89c4\u5219\uff08rules\uff09\ncp everything-claude-code/rules/*.md ~/.claude/rules/\n\n# \u590d\u5236\u547d\u4ee4\uff08commands\uff09\ncp everything-claude-code/commands/*.md ~/.claude/commands/\n\n# \u590d\u5236\u6280\u80fd\uff08skills\uff09\ncp -r everything-claude-code/skills/* ~/.claude/skills/\n```\n\n#### \u5c06\u94a9\u5b50\uff08hooks\uff09\u6dfb\u52a0\u5230 settings.json\n\n\u5c06 `hooks/hooks.json` \u4e2d\u7684\u94a9\u5b50\u914d\u7f6e\u590d\u5236\u5230\u4f60\u7684 `~/.claude/settings.json` \u4e2d\u3002\n\n#### \u914d\u7f6e MCPs\n\n\u5c06 `mcp-configs/mcp-servers.json` \u4e2d\u4f60\u9700\u8981\u7684 MCP \u670d\u52a1\u914d\u7f6e\u590d\u5236\u5230\u4f60\u7684 `~/.claude.json`\u3002\n\n**\u91cd\u8981\uff1a** \u8bf7\u5c06 `YOUR_*_HERE` \u5360\u4f4d\u7b26\u66ff\u6362\u4e3a\u4f60\u771f\u5b9e\u7684 API \u5bc6\u94a5\u3002\n\n---\n\n## \u6838\u5fc3\u6982\u5ff5\n\n### \u667a\u80fd\u4f53\uff08Agents\uff09\n\n\u5b50\u667a\u80fd\u4f53\uff08Subagents\uff09\u8d1f\u8d23\u5904\u7406\u53d7\u9650\u8303\u56f4\u5185\u7684\u59d4\u6d3e\u4efb\u52a1\u3002\u793a\u4f8b\uff1a\n\n```markdown\n---\nname: code-reviewer\ndescription: \u8bc4\u5ba1\u4ee3\u7801\u8d28\u91cf\u3001\u5b89\u5168\u6027\u4e0e\u53ef\u7ef4\u62a4\u6027\ntools: [\"Read\", \"Grep\", \"Glob\", \"Bash\"]\nmodel: opus\n---\n\n\u4f60\u662f\u4e00\u540d\u8d44\u6df1\u4ee3\u7801\u8bc4\u5ba1\u5458...\n```\n\n### \u6280\u80fd\uff08Skills\uff09\n\n\u6280\u80fd\uff08Skills\uff09\u662f\u53ef\u7531\u547d\u4ee4\u6216\u667a\u80fd\u4f53\u8c03\u7528\u7684\u5de5\u4f5c\u6d41\u5b9a\u4e49\uff1a\n\n```markdown\n# TDD \u5de5\u4f5c\u6d41\n\n1. \u9996\u5148\u5b9a\u4e49\u63a5\u53e3\n2. \u7f16\u5199\u5931\u8d25\u7684\u6d4b\u8bd5\uff08RED\uff09\n3. \u5b9e\u73b0\u6700\u7b80\u4ee3\u7801\uff08GREEN\uff09\n4. \u91cd\u6784\uff08IMPROVE\uff09\n5. \u9a8c\u8bc1\u8986\u76d6\u7387\u662f\u5426\u8fbe\u5230 80% \u4ee5\u4e0a\n```\n\n### \u94a9\u5b50\uff08Hooks\uff09\n\n\u94a9\u5b50\uff08Hooks\uff09\u5728\u5de5\u5177\u4e8b\u4ef6\u53d1\u751f\u65f6\u89e6\u53d1\u3002\u793a\u4f8b \u2014\u2014 \u9488\u5bf9 console.log \u53d1\u51fa\u8b66\u544a\uff1a\n\n```json\n{\n \"matcher\": \"tool == \\\"Edit\\\" && tool_input.file_path matches \\\"\\\\.(ts|tsx|js|jsx)$\\\"\",\n \"hooks\": [{\n \"type\": \"command\",\n \"command\": \"#!/bin/bash\\ngrep -n 'console\\\\.log' \\\"$file_path\\\" && echo '[Hook] Remove console.log' >&2\"\n }]\n}\n```\n\n### \u89c4\u5219\uff08Rules\uff09\n\n\u89c4\u5219\uff08Rules\uff09\u662f\u5fc5\u987b\u59cb\u7ec8\u9075\u5b88\u7684\u51c6\u5219\u3002\u8bf7\u4fdd\u6301\u5b83\u4eec\u7684\u6a21\u5757\u5316\uff1a\n\n```\n~/.claude/rules/\n security.md # \u7981\u6b62\u786c\u7f16\u7801\u5bc6\u94a5\n coding-style.md # \u4e0d\u53ef\u53d8\u6027\u3001\u6587\u4ef6\u9650\u5236\n testing.md # TDD\u3001\u8986\u76d6\u7387\u8981\u6c42\n```\n\n---\n\n## \u8fd0\u884c\u6d4b\u8bd5\n\n\u672c\u63d2\u4ef6\u5305\u542b\u4e00\u4e2a\u5168\u9762\u7684\u6d4b\u8bd5\u5957\u4ef6\uff1a\n\n```bash\n# \u8fd0\u884c\u6240\u6709\u6d4b\u8bd5\nnode tests/run-all.js\n\n# \u8fd0\u884c\u5355\u4e2a\u6d4b\u8bd5\u6587\u4ef6\nnode tests/lib/utils.test.js\nnode tests/lib/package-manager.test.js\nnode tests/hooks/hooks.test.js\n```\n\n---\n\n## \u8d21\u732e\n\n**\u975e\u5e38\u6b22\u8fce\u5e76\u9f13\u52b1\u8d21\u732e\u3002**\n\n\u672c\u4ed3\u5e93\u65e8\u5728\u6210\u4e3a\u4e00\u4e2a\u793e\u533a\u8d44\u6e90\u3002\u5982\u679c\u4f60\u6709\uff1a\n- \u6709\u7528\u7684\u667a\u80fd\u4f53\u6216\u6280\u80fd\n- \u5de7\u5999\u7684\u94a9\u5b50\n- \u66f4\u597d\u7684 MCP \u914d\u7f6e\n- \u6539\u8fdb\u540e\u7684\u89c4\u5219\n\n\u8bf7\u63d0\u4ea4\u4f60\u7684\u8d21\u732e\uff01\u53c2\u8003 [CONTRIBUTING.md](CONTRIBUTING.md) \u4e86\u89e3\u6307\u5357\u3002\n\n### \u8d21\u732e\u601d\u8def\n\n- \u8bed\u8a00\u4e13\u7528\u6280\u80fd\uff08Python, Rust \u6a21\u5f0f\uff09\u2014\u2014 \u5df2\u5305\u542b Go\uff01\n- \u6846\u67b6\u4e13\u7528\u914d\u7f6e\uff08Django, Rails, Laravel\uff09\n- DevOps \u667a\u80fd\u4f53\uff08Kubernetes, Terraform, AWS\uff09\n- \u6d4b\u8bd5\u7b56\u7565\uff08\u4e0d\u540c\u6846\u67b6\uff09\n- \u9886\u57df\u7279\u5b9a\u77e5\u8bc6\uff08\u673a\u5668\u5b66\u4e60\u3001\u6570\u636e\u5de5\u7a0b\u3001\u79fb\u52a8\u5f00\u53d1\uff09\n\n---\n\n## \u80cc\u666f\n\n\u6211\u4ece Claude Code \u5b9e\u9a8c\u9636\u6bb5\u5c31\u5f00\u59cb\u4f7f\u7528\u4e86\u3002\u5728 2025 \u5e74 9 \u6708\u7684 Anthropic x Forum Ventures \u9ed1\u5ba2\u677e\u4e2d\uff0c\u6211\u4e0e [@DRodriguezFX](https://x.com/DRodriguezFX) \u5408\u4f5c\u5f00\u53d1\u4e86 [zenith.chat](https://zenith.chat)\uff0c\u5e76\u83b7\u5f97\u4e86\u51a0\u519b \u2014\u2014 \u8be5\u9879\u76ee\u5b8c\u5168\u4f7f\u7528 Claude Code \u6784\u5efa\u3002\n\n\u8fd9\u4e9b\u914d\u7f6e\u5728\u591a\u4e2a\u751f\u4ea7\u7ea7\u5e94\u7528\u4e2d\u7ecf\u8fc7\u4e86\u5b9e\u6218\u68c0\u9a8c\u3002\n\n---\n\n## \u91cd\u8981\u63d0\u793a\n\n### \u4e0a\u4e0b\u6587\u7a97\u53e3\u7ba1\u7406\n\n**\u81f3\u5173\u91cd\u8981\uff1a** \u4e0d\u8981\u4e00\u6b21\u6027\u542f\u7528\u6240\u6709 MCP\u3002\u5982\u679c\u542f\u7528\u7684\u5de5\u5177\u8fc7\u591a\uff0c\u4f60\u7684 200k \u4e0a\u4e0b\u6587\u7a97\u53e3\u53ef\u80fd\u4f1a\u7f29\u51cf\u5230 70k\u3002\n\n\u7ecf\u9a8c\u6cd5\u5219\uff1a\n- \u914d\u7f6e 20-30 \u4e2a MCP\n- \u6bcf\u4e2a\u9879\u76ee\u4fdd\u6301\u542f\u7528 10 \u4e2a\u4ee5\u5185\n- \u6d3b\u8dc3\u5de5\u5177\u603b\u6570\u63a7\u5236\u5728 80 \u4e2a\u4ee5\u5185\n\n\u5728\u9879\u76ee\u914d\u7f6e\u4e2d\u4f7f\u7528 `disabledMcpServers` \u6765\u7981\u7528\u4e0d\u9700\u8981\u7684\u670d\u52a1\u3002\n\n### \u81ea\u5b9a\u4e49\n\n\u8fd9\u4e9b\u914d\u7f6e\u9002\u7528\u4e8e\u6211\u7684\u5de5\u4f5c\u6d41\u3002\u4f60\u5e94\u8be5\uff1a\n1. \u4ece\u4f60\u4ea7\u751f\u5171\u9e23\u7684\u5185\u5bb9\u5f00\u59cb\n2. \u6839\u636e\u4f60\u7684\u6280\u672f\u6808\u8fdb\u884c\u4fee\u6539\n3. \u79fb\u9664\u4f60\u4e0d\u9700\u8981\u7684\u5185\u5bb9\n4. \u52a0\u5165\u4f60\u81ea\u5df1\u7684\u6a21\u5f0f\n\n---\n\n## Star \u5386\u53f2\n\n[](https://star-history.com/#affaan-m/everything-claude-code&Date)\n\n---\n\n## \u76f8\u5173\u94fe\u63a5\n\n- **\u7b80\u660e\u6307\u5357\uff08\u5165\u95e8\u5fc5\u8bfb\uff09\uff1a** [Everything Claude Code \u7b80\u660e\u6307\u5357](https://x.com/affaanmustafa/status/2012378465664745795)\n- **\u6df1\u5ea6\u6307\u5357\uff08\u8fdb\u9636\u53c2\u8003\uff09\uff1a** [Everything Claude Code \u6df1\u5ea6\u6307\u5357](https://x.com/affaanmustafa/status/2014040193557471352)\n- **\u5173\u6ce8\u6211\uff1a** [@affaanmustafa](https://x.com/affaanmustafa)\n- **zenith.chat\uff1a** [zenith.chat](https://zenith.chat)\n\n---\n\n## \u8bb8\u53ef\u8bc1\n\nMIT - \u81ea\u7531\u4f7f\u7528\uff0c\u6309\u9700\u4fee\u6539\uff0c\u5982\u679c\u53ef\u4ee5\u8bf7\u56de\u9988\u793e\u533a\u3002\n\n---\n\n**\u5982\u679c\u5bf9\u4f60\u6709\u5e2e\u52a9\uff0c\u8bf7\u7ed9\u672c\u4ed3\u5e93\u70b9\u4e2a Star\u3002\u9605\u8bfb\u4e24\u4efd\u6307\u5357\u3002\u6784\u5efa\u4f1f\u5927\u7684\u4ea7\u54c1\u3002**\n"
+ "md5": "fb40e7edeec881dd62c82b13036b5ead",
+ "content": "**\u8bed\u8a00\uff1a** [English](README.md) | **\u7b80\u4f53\u4e2d\u6587** | [\u7e41\u9ad4\u4e2d\u6587](docs/zh-TW/README.md)\n\n# Everything Claude Code\n\n[](https://github.com/affaan-m/everything-claude-code/stargazers)\n[](LICENSE)\n\n\n\n\n\n---\n\n\n\n**\ud83c\udf10 Language / \u8bed\u8a00 / \u8a9e\u8a00**\n\n[**English**](README.md) | [\u7b80\u4f53\u4e2d\u6587](README.zh-CN.md) | [\u7e41\u9ad4\u4e2d\u6587](docs/zh-TW/README.md)\n\n
\n\n---\n\n**\u6765\u81ea Anthropic \u9ed1\u5ba2\u677e\u83b7\u80dc\u8005\u7684 Claude Code \u914d\u7f6e\u5168\u96c6\u3002**\n\n\u5305\u542b\u751f\u4ea7\u7ea7\u7684\u667a\u80fd\u4f53\uff08Agents\uff09\u3001\u6280\u80fd\uff08Skills\uff09\u3001\u94a9\u5b50\uff08Hooks\uff09\u3001\u547d\u4ee4\uff08Commands\uff09\u3001\u89c4\u5219\uff08Rules\uff09\u4ee5\u53ca MCP \u914d\u7f6e\u3002\u8fd9\u4e9b\u914d\u7f6e\u6e90\u81ea 10 \u4e2a\u591a\u6708\u5728\u6784\u5efa\u771f\u5b9e\u4ea7\u54c1\u8fc7\u7a0b\u4e2d\u7684\u9ad8\u5f3a\u5ea6\u65e5\u5e38\u4f7f\u7528\u4e0e\u6f14\u8fdb\u3002\n\n---\n\n## \u6307\u5357\u6587\u6863\n\n\u672c\u4ed3\u5e93\u4ec5\u5305\u542b\u539f\u59cb\u4ee3\u7801\u3002\u4ee5\u4e0b\u6307\u5357\u5c06\u8be6\u7ec6\u89e3\u91ca\u4e00\u5207\uff1a\n\n\n\n\n\n \n \n \n\n\n \n \n \n \n\n\u7b80\u660e\u6307\u5357 (Shorthand Guide) \u5b89\u88c5\u3001\u57fa\u7840\u3001\u54f2\u5b66\u3002\u8bf7\u5148\u9605\u8bfb\u6b64\u7bc7\u3002 \n\u6df1\u5ea6\u6307\u5357 (Longform Guide) Token \u4f18\u5316\u3001\u8bb0\u5fc6\u6301\u4e45\u5316\u3001\u8bc4\u6d4b\uff08Evals\uff09\u3001\u5e76\u884c\u5316\u3002 \n \n
\n\n| \u4e3b\u9898 | \u4f60\u5c06\u5b66\u5230 |\n|-------|-------------------|\n| Token \u4f18\u5316 | \u6a21\u578b\u9009\u62e9\u3001\u7cfb\u7edf\u63d0\u793a\u8bcd\u7cbe\u7b80\u3001\u540e\u53f0\u8fdb\u7a0b |\n| \u8bb0\u5fc6\u6301\u4e45\u5316 | \u8de8\u4f1a\u8bdd\u81ea\u52a8\u4fdd\u5b58/\u52a0\u8f7d\u4e0a\u4e0b\u6587\u7684\u94a9\u5b50\uff08Hooks\uff09 |\n| \u6301\u7eed\u5b66\u4e60 | \u4ece\u4f1a\u8bdd\u4e2d\u81ea\u52a8\u63d0\u53d6\u6a21\u5f0f\u5e76\u8f6c\u5316\u4e3a\u53ef\u91cd\u7528\u7684\u6280\u80fd\uff08Skills\uff09 |\n| \u9a8c\u8bc1\u5faa\u73af | \u68c0\u67e5\u70b9\uff08Checkpoint\uff09vs \u6301\u7eed\u8bc4\u6d4b\u3001\u8bc4\u5206\u5668\u7c7b\u578b\u3001pass@k \u6307\u6807 |\n| \u5e76\u884c\u5316 | Git worktrees\u3001\u7ea7\u8054\u65b9\u6cd5\u3001\u4f55\u65f6\u6269\u5c55\u5b9e\u4f8b |\n| \u5b50\u667a\u80fd\u4f53\u7f16\u6392 | \u4e0a\u4e0b\u6587\u95ee\u9898\u3001\u8fed\u4ee3\u68c0\u7d22\u6a21\u5f0f |\n\n---\n\n## \ud83d\ude80 \u5feb\u901f\u5f00\u59cb\n\n\u4e0d\u5230 2 \u5206\u949f\u5373\u53ef\u5b8c\u6210\u914d\u7f6e\uff1a\n\n### \u7b2c\u4e00\u6b65\uff1a\u5b89\u88c5\u63d2\u4ef6\n\n```bash\n# \u6dfb\u52a0\u5e02\u573a\n/plugin marketplace add affaan-m/everything-claude-code\n\n# \u5b89\u88c5\u63d2\u4ef6\n/plugin install everything-claude-code@everything-claude-code\n```\n\n### \u7b2c\u4e8c\u6b65\uff1a\u5b89\u88c5\u89c4\u5219\uff08\u5fc5\u9009\uff09\n\n> \u26a0\ufe0f **\u91cd\u8981\u63d0\u793a\uff1a** Claude Code \u63d2\u4ef6\u65e0\u6cd5\u81ea\u52a8\u5206\u53d1 `rules`\u3002\u8bf7\u624b\u52a8\u5b89\u88c5\uff1a\n\n```bash\n# \u9996\u5148\u514b\u9686\u4ed3\u5e93\ngit clone https://github.com/affaan-m/everything-claude-code.git\n\n# \u590d\u5236\u89c4\u5219\uff08\u9002\u7528\u4e8e\u6240\u6709\u9879\u76ee\uff09\ncp -r everything-claude-code/rules/* ~/.claude/rules/\n```\n\n### \u7b2c\u4e09\u6b65\uff1a\u5f00\u59cb\u4f7f\u7528\n\n```bash\n# \u5c1d\u8bd5\u4e00\u4e2a\u547d\u4ee4\n/plan \"Add user authentication\"\n\n# \u67e5\u770b\u53ef\u7528\u547d\u4ee4\n/plugin list everything-claude-code@everything-claude-code\n```\n\n\u2728 **\u5927\u529f\u544a\u6210\uff01** \u4f60\u73b0\u5728\u53ef\u4ee5\u4f7f\u7528 15+ \u4e2a\u667a\u80fd\u4f53\u300130+ \u4e2a\u6280\u80fd\u548c 20+ \u4e2a\u547d\u4ee4\u4e86\u3002\n\n---\n\n## \ud83c\udf10 \u8de8\u5e73\u53f0\u652f\u6301\n\n\u8be5\u63d2\u4ef6\u73b0\u5df2\u5168\u9762\u652f\u6301 **Windows\u3001macOS \u548c Linux**\u3002\u6240\u6709\u94a9\u5b50\u548c\u811a\u672c\u5747\u5df2\u4f7f\u7528 Node.js \u91cd\u5199\uff0c\u4ee5\u786e\u4fdd\u6700\u5927\u517c\u5bb9\u6027\u3002\n\n### \u5305\u7ba1\u7406\u5668\u68c0\u6d4b\n\n\u63d2\u4ef6\u4f1a\u81ea\u52a8\u68c0\u6d4b\u4f60\u504f\u597d\u7684\u5305\u7ba1\u7406\u5668\uff08npm, pnpm, yarn, \u6216 bun\uff09\uff0c\u4f18\u5148\u7ea7\u5982\u4e0b\uff1a\n\n1. **\u73af\u5883\u53d8\u91cf**\uff1a`CLAUDE_PACKAGE_MANAGER`\n2. **\u9879\u76ee\u914d\u7f6e**\uff1a`.claude/package-manager.json`\n3. **package.json**\uff1a`packageManager` \u5b57\u6bb5\n4. **\u9501\u6587\u4ef6**\uff1a\u6839\u636e package-lock.json, yarn.lock, pnpm-lock.yaml, \u6216 bun.lockb \u68c0\u6d4b\n5. **\u5168\u5c40\u914d\u7f6e**\uff1a`~/.claude/package-manager.json`\n6. **\u515c\u5e95\u65b9\u6848**\uff1a\u7b2c\u4e00\u4e2a\u53ef\u7528\u7684\u5305\u7ba1\u7406\u5668\n\n\u8bbe\u7f6e\u4f60\u504f\u597d\u7684\u5305\u7ba1\u7406\u5668\uff1a\n\n```bash\n# \u901a\u8fc7\u73af\u5883\u53d8\u91cf\nexport CLAUDE_PACKAGE_MANAGER=pnpm\n\n# \u901a\u8fc7\u5168\u5c40\u914d\u7f6e\nnode scripts/setup-package-manager.js --global pnpm\n\n# \u901a\u8fc7\u9879\u76ee\u914d\u7f6e\nnode scripts/setup-package-manager.js --project bun\n\n# \u68c0\u6d4b\u5f53\u524d\u8bbe\u7f6e\nnode scripts/setup-package-manager.js --detect\n```\n\n\u6216\u8005\u5728 Claude Code \u4e2d\u4f7f\u7528 `/setup-pm` \u547d\u4ee4\u3002\n\n---\n\n## \ud83d\udce6 \u5185\u5bb9\u6e05\u5355\n\n\u672c\u4ed3\u5e93\u662f\u4e00\u4e2a **Claude Code \u63d2\u4ef6** \u2014\u2014 \u4f60\u53ef\u4ee5\u76f4\u63a5\u5b89\u88c5\uff0c\u4e5f\u53ef\u4ee5\u624b\u52a8\u590d\u5236\u7ec4\u4ef6\u3002\n\n```\neverything-claude-code/\n|-- .claude-plugin/ # \u63d2\u4ef6\u548c\u5e02\u573a\u6e05\u5355\u6587\u4ef6\n| |-- plugin.json # \u63d2\u4ef6\u5143\u6570\u636e\u548c\u7ec4\u4ef6\u8def\u5f84\n| |-- marketplace.json # \u7528\u4e8e /plugin marketplace add \u7684\u5e02\u573a\u76ee\u5f55\n|\n|-- agents/ # \u7528\u4e8e\u4efb\u52a1\u59d4\u6d3e\u7684\u4e13\u4e1a\u5316\u5b50\u667a\u80fd\u4f53\uff08Subagents\uff09\n| |-- planner.md # \u529f\u80fd\u5b9e\u73b0\u89c4\u5212\n| |-- architect.md # \u7cfb\u7edf\u8bbe\u8ba1\u51b3\u7b56\n| |-- tdd-guide.md # \u6d4b\u8bd5\u9a71\u52a8\u5f00\u53d1\uff08TDD\uff09\n| |-- code-reviewer.md # \u8d28\u91cf\u4e0e\u5b89\u5168\u5ba1\u67e5\n| |-- security-reviewer.md # \u6f0f\u6d1e\u5206\u6790\n| |-- build-error-resolver.md # \u6784\u5efa\u9519\u8bef\u4fee\u590d\n| |-- e2e-runner.md # Playwright \u7aef\u5230\u7aef\uff08E2E\uff09\u6d4b\u8bd5\n| |-- refactor-cleaner.md # \u6b7b\u4ee3\u7801\u6e05\u7406\n| |-- doc-updater.md # \u6587\u6863\u540c\u6b65\n| |-- go-reviewer.md # Go \u4ee3\u7801\u5ba1\u67e5\uff08\u65b0\u589e\uff09\n| |-- go-build-resolver.md # Go \u6784\u5efa\u9519\u8bef\u4fee\u590d\uff08\u65b0\u589e\uff09\n|\n|-- skills/ # \u5de5\u4f5c\u6d41\u5b9a\u4e49\u4e0e\u9886\u57df\u77e5\u8bc6\n| |-- coding-standards/ # \u8bed\u8a00\u6700\u4f73\u5b9e\u8df5\n| |-- backend-patterns/ # API\u3001\u6570\u636e\u5e93\u3001\u7f13\u5b58\u6a21\u5f0f\n| |-- frontend-patterns/ # React\u3001Next.js \u6a21\u5f0f\n| |-- continuous-learning/ # \u4ece\u4f1a\u8bdd\u4e2d\u81ea\u52a8\u63d0\u53d6\u6a21\u5f0f\uff08\u6df1\u5ea6\u6307\u5357\uff09\n| |-- continuous-learning-v2/ # \u57fa\u4e8e\u201c\u76f4\u89c9\uff08Instinct\uff09\u201d\u7684\u5b66\u4e60\uff0c\u5e26\u6709\u7f6e\u4fe1\u5ea6\u8bc4\u5206\n| |-- iterative-retrieval/ # \u4e3a\u5b50\u667a\u80fd\u4f53\u63d0\u4f9b\u6e10\u8fdb\u5f0f\u4e0a\u4e0b\u6587\u7cbe\u70bc\n| |-- strategic-compact/ # \u624b\u52a8\u538b\u7f29\u5efa\u8bae\uff08\u6df1\u5ea6\u6307\u5357\uff09\n| |-- tdd-workflow/ # TDD \u65b9\u6cd5\u8bba\n| |-- security-review/ # \u5b89\u5168\u68c0\u67e5\u6e05\u5355\n| |-- eval-harness/ # \u9a8c\u8bc1\u5faa\u73af\u8bc4\u6d4b\uff08\u6df1\u5ea6\u6307\u5357\uff09\n| |-- verification-loop/ # \u6301\u7eed\u9a8c\u8bc1\uff08\u6df1\u5ea6\u6307\u5357\uff09\n| |-- golang-patterns/ # Go \u60ef\u7528\u6cd5\u4e0e\u6700\u4f73\u5b9e\u8df5\uff08\u65b0\u589e\uff09\n| |-- golang-testing/ # Go \u6d4b\u8bd5\u6a21\u5f0f\u3001TDD\u3001\u57fa\u51c6\u6d4b\u8bd5\uff08\u65b0\u589e\uff09\n|\n|-- commands/ # \u7528\u4e8e\u5feb\u901f\u6267\u884c\u7684\u659c\u6760\u547d\u4ee4\n| |-- tdd.md # /tdd - \u6d4b\u8bd5\u9a71\u52a8\u5f00\u53d1\n| |-- plan.md # /plan - \u5b9e\u73b0\u65b9\u6848\u89c4\u5212\n| |-- e2e.md # /e2e - \u751f\u6210 E2E \u6d4b\u8bd5\n| |-- code-review.md # /code-review - \u8d28\u91cf\u5ba1\u67e5\n| |-- build-fix.md # /build-fix - \u4fee\u590d\u6784\u5efa\u9519\u8bef\n| |-- refactor-clean.md # /refactor-clean - \u79fb\u9664\u6b7b\u4ee3\u7801\n| |-- learn.md # /learn - \u5728\u4f1a\u8bdd\u4e2d\u9014\u63d0\u53d6\u6a21\u5f0f\uff08\u6df1\u5ea6\u6307\u5357\uff09\n| |-- checkpoint.md # /checkpoint - \u4fdd\u5b58\u9a8c\u8bc1\u72b6\u6001\uff08\u6df1\u5ea6\u6307\u5357\uff09\n| |-- verify.md # /verify - \u8fd0\u884c\u9a8c\u8bc1\u5faa\u73af\uff08\u6df1\u5ea6\u6307\u5357\uff09\n| |-- setup-pm.md # /setup-pm - \u914d\u7f6e\u5305\u7ba1\u7406\u5668\n| |-- go-review.md # /go-review - Go \u4ee3\u7801\u5ba1\u67e5\uff08\u65b0\u589e\uff09\n| |-- go-test.md # /go-test - Go TDD \u5de5\u4f5c\u6d41\uff08\u65b0\u589e\uff09\n| |-- go-build.md # /go-build - \u4fee\u590d Go \u6784\u5efa\u9519\u8bef\uff08\u65b0\u589e\uff09\n| |-- skill-create.md # /skill-create - \u4ece Git \u5386\u53f2\u751f\u6210\u6280\u80fd\uff08\u65b0\u589e\uff09\n| |-- instinct-status.md # /instinct-status - \u67e5\u770b\u5df2\u5b66\u4e60\u7684\u76f4\u89c9\uff08\u65b0\u589e\uff09\n| |-- instinct-import.md # /instinct-import - \u5bfc\u5165\u76f4\u89c9\uff08\u65b0\u589e\uff09\n| |-- instinct-export.md # /instinct-export - \u5bfc\u51fa\u76f4\u89c9\uff08\u65b0\u589e\uff09\n| |-- evolve.md # /evolve - \u5c06\u76f4\u89c9\u805a\u7c7b\u4e3a\u6280\u80fd\uff08\u65b0\u589e\uff09\n|\n|-- rules/ # \u5fc5\u987b\u9075\u5b88\u7684\u6307\u5357\uff08\u9700\u590d\u5236\u5230 ~/.claude/rules/\uff09\n| |-- security.md # \u5f3a\u5236\u6027\u5b89\u5168\u68c0\u67e5\n| |-- coding-style.md # \u4e0d\u53ef\u53d8\u6027\u3001\u6587\u4ef6\u7ec4\u7ec7\n| |-- testing.md # TDD\u300180% \u8986\u76d6\u7387\u8981\u6c42\n| |-- git-workflow.md # \u63d0\u4ea4\u683c\u5f0f\u3001PR \u6d41\u7a0b\n| |-- agents.md # \u4f55\u65f6\u59d4\u6d3e\u7ed9\u5b50\u667a\u80fd\u4f53\n| |-- performance.md # \u6a21\u578b\u9009\u62e9\u3001\u4e0a\u4e0b\u6587\u7ba1\u7406\n|\n|-- hooks/ # \u57fa\u4e8e\u89e6\u53d1\u5668\u7684\u81ea\u52a8\u5316\n| |-- hooks.json # \u6240\u6709\u94a9\u5b50\u914d\u7f6e\uff08PreToolUse, PostToolUse, Stop \u7b49\uff09\n| |-- memory-persistence/ # \u4f1a\u8bdd\u751f\u547d\u5468\u671f\u94a9\u5b50\uff08\u6df1\u5ea6\u6307\u5357\uff09\n| |-- strategic-compact/ # \u538b\u7f29\u5efa\u8bae\uff08\u6df1\u5ea6\u6307\u5357\uff09\n|\n|-- scripts/ # \u8de8\u5e73\u53f0 Node.js \u811a\u672c\uff08\u65b0\u589e\uff09\n| |-- lib/ # \u5171\u4eab\u5de5\u5177\u5e93\n| | |-- utils.js # \u8de8\u5e73\u53f0\u6587\u4ef6/\u8def\u5f84/\u7cfb\u7edf\u5de5\u5177\n| | |-- package-manager.js # \u5305\u7ba1\u7406\u5668\u68c0\u6d4b\u4e0e\u9009\u62e9\n| |-- hooks/ # \u94a9\u5b50\u5b9e\u73b0\n| | |-- session-start.js # \u4f1a\u8bdd\u5f00\u59cb\u65f6\u52a0\u8f7d\u4e0a\u4e0b\u6587\n| | |-- session-end.js # \u4f1a\u8bdd\u7ed3\u675f\u65f6\u4fdd\u5b58\u72b6\u6001\n| | |-- pre-compact.js # \u538b\u7f29\u524d\u7684\u72b6\u6001\u4fdd\u5b58\n| | |-- suggest-compact.js # \u6218\u7565\u6027\u538b\u7f29\u5efa\u8bae\n| | |-- evaluate-session.js # \u4ece\u4f1a\u8bdd\u4e2d\u63d0\u53d6\u6a21\u5f0f\n| |-- setup-package-manager.js # \u4ea4\u4e92\u5f0f\u5305\u7ba1\u7406\u5668\u8bbe\u7f6e\n|\n|-- tests/ # \u6d4b\u8bd5\u5957\u4ef6\uff08\u65b0\u589e\uff09\n| |-- lib/ # \u5e93\u6d4b\u8bd5\n| |-- hooks/ # \u94a9\u5b50\u6d4b\u8bd5\n| |-- run-all.js # \u8fd0\u884c\u6240\u6709\u6d4b\u8bd5\n|\n|-- contexts/ # \u52a8\u6001\u7cfb\u7edf\u63d0\u793a\u8bcd\u6ce8\u5165\u4e0a\u4e0b\u6587\uff08\u6df1\u5ea6\u6307\u5357\uff09\n| |-- dev.md # \u5f00\u53d1\u6a21\u5f0f\u4e0a\u4e0b\u6587\n| |-- review.md # \u4ee3\u7801\u5ba1\u67e5\u6a21\u5f0f\u4e0a\u4e0b\u6587\n| |-- research.md # \u7814\u7a76/\u63a2\u7d22\u6a21\u5f0f\u4e0a\u4e0b\u6587\n|\n|-- examples/ # \u914d\u7f6e\u548c\u4f1a\u8bdd\u793a\u4f8b\n| |-- CLAUDE.md # \u9879\u76ee\u7ea7\u914d\u7f6e\u793a\u4f8b\n| |-- user-CLAUDE.md # \u7528\u6237\u7ea7\u914d\u7f6e\u793a\u4f8b\n|\n|-- mcp-configs/ # MCP \u670d\u52a1\u914d\u7f6e\n| |-- mcp-servers.json # GitHub, Supabase, Vercel, Railway \u7b49\u914d\u7f6e\n|\n|-- marketplace.json # \u81ea\u6258\u7ba1\u5e02\u573a\u914d\u7f6e\uff08\u7528\u4e8e /plugin marketplace add\uff09\n```\n\n---\n\n## \ud83d\udee0\ufe0f \u751f\u6001\u5de5\u5177\n\n### \u6280\u80fd\u521b\u5efa\u5668\uff08Skill Creator\uff09\n\n\u6709\u4e24\u79cd\u65b9\u5f0f\u53ef\u4ee5\u4ece\u4f60\u7684\u4ed3\u5e93\u751f\u6210 Claude Code \u6280\u80fd\uff1a\n\n#### \u9009\u9879 A\uff1a\u672c\u5730\u5206\u6790\uff08\u5185\u7f6e\uff09\n\n\u4f7f\u7528 `/skill-create` \u547d\u4ee4\u8fdb\u884c\u672c\u5730\u5206\u6790\uff0c\u65e0\u9700\u5916\u90e8\u670d\u52a1\uff1a\n\n```bash\n/skill-create # \u5206\u6790\u5f53\u524d\u4ed3\u5e93\n/skill-create --instincts # \u540c\u65f6\u4e3a\u6301\u7eed\u5b66\u4e60\u751f\u6210\u201c\u76f4\u89c9\uff08Instincts\uff09\u201d\n```\n\n\u8fd9\u4f1a\u5728\u672c\u5730\u5206\u6790\u4f60\u7684 Git \u5386\u53f2\u5e76\u751f\u6210 SKILL.md \u6587\u4ef6\u3002\n\n#### \u9009\u9879 B\uff1aGitHub App\uff08\u9ad8\u7ea7\u7248\uff09\n\n\u9002\u7528\u4e8e\u9ad8\u7ea7\u529f\u80fd\uff0810k+ \u63d0\u4ea4\u3001\u81ea\u52a8 PR\u3001\u56e2\u961f\u5171\u4eab\uff09\uff1a\n\n[\u5b89\u88c5 GitHub App](https://github.com/apps/skill-creator) | [ecc.tools](https://ecc.tools)\n\n```bash\n# \u5728\u4efb\u4f55 Issue \u4e0b\u56de\u590d\uff1a\n/skill-creator analyze\n\n# \u6216\u5728\u63a8\u9001\u5230\u9ed8\u8ba4\u5206\u652f\u65f6\u81ea\u52a8\u89e6\u53d1\n```\n\n\u4e24\u79cd\u9009\u9879\u90fd\u4f1a\u521b\u5efa\uff1a\n- **SKILL.md \u6587\u4ef6** - \u53ef\u76f4\u63a5\u7528\u4e8e Claude Code \u7684\u6280\u80fd\n- **\u76f4\u89c9\u96c6\u5408 (Instinct collections)** - \u7528\u4e8e continuous-learning-v2\n- **\u6a21\u5f0f\u63d0\u53d6** - \u4ece\u4f60\u7684\u63d0\u4ea4\u5386\u53f2\u4e2d\u5b66\u4e60\n\n### \ud83e\udde0 \u6301\u7eed\u5b66\u4e60 v2 (Continuous Learning v2)\n\n\u57fa\u4e8e\u201c\u76f4\u89c9\uff08Instinct\uff09\u201d\u7684\u5b66\u4e60\u7cfb\u7edf\u4f1a\u81ea\u52a8\u5b66\u4e60\u4f60\u7684\u6a21\u5f0f\uff1a\n\n```bash\n/instinct-status # \u663e\u793a\u5df2\u5b66\u4e60\u7684\u76f4\u89c9\u53ca\u5176\u7f6e\u4fe1\u5ea6\n/instinct-import # \u5bfc\u5165\u4ed6\u4eba\u7684\u76f4\u89c9\n/instinct-export # \u5bfc\u51fa\u4f60\u7684\u76f4\u89c9\u4ee5\u4fbf\u5171\u4eab\n/evolve # \u5c06\u76f8\u5173\u7684\u76f4\u89c9\u805a\u7c7b\u4e3a\u6280\u80fd\n```\n\n\u8be6\u89c1 `skills/continuous-learning-v2/` \u5b8c\u6574\u6587\u6863\u3002\n\n---\n\n## \ud83d\udccb \u8fd0\u884c\u8981\u6c42\n\n### Claude Code CLI \u7248\u672c\n\n**\u6700\u4f4e\u7248\u672c\uff1av2.1.0 \u6216\u66f4\u9ad8**\n\n\u7531\u4e8e\u63d2\u4ef6\u7cfb\u7edf\u5904\u7406\u94a9\u5b50\uff08Hooks\uff09\u65b9\u5f0f\u7684\u53d8\u66f4\uff0c\u6b64\u63d2\u4ef6\u9700\u8981 Claude Code CLI v2.1.0+\u3002\n\n\u68c0\u67e5\u4f60\u7684\u7248\u672c\uff1a\n```bash\nclaude --version\n```\n\n### \u91cd\u8981\uff1a\u94a9\u5b50\u81ea\u52a8\u52a0\u8f7d\u884c\u4e3a\n\n> \u26a0\ufe0f **\u81f4\u8d21\u732e\u8005\uff1a** \u8bf7\u52ff\u5728 `.claude-plugin/plugin.json` \u4e2d\u6dfb\u52a0 `\"hooks\"` \u5b57\u6bb5\u3002\u8fd9\u662f\u901a\u8fc7\u56de\u5f52\u6d4b\u8bd5\u5f3a\u5236\u6267\u884c\u7684\u3002\n\n\u6309\u7167\u7ea6\u5b9a\uff0cClaude Code v2.1+ \u4f1a**\u81ea\u52a8\u52a0\u8f7d**\u4efb\u4f55\u5df2\u5b89\u88c5\u63d2\u4ef6\u4e2d\u7684 `hooks/hooks.json`\u3002\u5982\u679c\u5728 `plugin.json` \u4e2d\u663e\u5f0f\u58f0\u660e\uff0c\u4f1a\u5bfc\u81f4\u91cd\u590d\u68c0\u6d4b\u9519\u8bef\uff1a\n\n```\nDuplicate hooks file detected: ./hooks/hooks.json resolves to already-loaded file\n```\n\n**\u5386\u53f2\u80cc\u666f\uff1a** \u6b64\u95ee\u9898\u5728\u672c\u4ed3\u5e93\u4e2d\u5f15\u53d1\u4e86\u591a\u6b21\u4fee\u590d/\u56de\u6eda\u5faa\u73af\uff08[#29](https://github.com/affaan-m/everything-claude-code/issues/29), [#52](https://github.com/affaan-m/everything-claude-code/issues/52), [#103](https://github.com/affaan-m/everything-claude-code/issues/103)\uff09\u3002\u7531\u4e8e Claude Code \u7248\u672c\u95f4\u7684\u884c\u4e3a\u53d8\u66f4\u5bfc\u81f4\u4e86\u6df7\u6dc6\uff0c\u6211\u4eec\u73b0\u5728\u901a\u8fc7\u56de\u5f52\u6d4b\u8bd5\u6765\u9632\u6b62\u6b64\u95ee\u9898\u518d\u6b21\u5f15\u5165\u3002\n\n---\n\n## \ud83d\udce5 \u5b89\u88c5\n\n### \u9009\u9879 1\uff1a\u4f5c\u4e3a\u63d2\u4ef6\u5b89\u88c5\uff08\u63a8\u8350\uff09\n\n\u4f7f\u7528\u672c\u4ed3\u5e93\u6700\u7b80\u5355\u7684\u65b9\u5f0f \u2014\u2014 \u4f5c\u4e3a Claude Code \u63d2\u4ef6\u5b89\u88c5\uff1a\n\n```bash\n# \u5c06\u6b64\u4ed3\u5e93\u6dfb\u52a0\u4e3a\u5e02\u573a\n/plugin marketplace add affaan-m/everything-claude-code\n\n# \u5b89\u88c5\u63d2\u4ef6\n/plugin install everything-claude-code@everything-claude-code\n```\n\n\u6216\u8005\u76f4\u63a5\u6dfb\u52a0\u5230\u4f60\u7684 `~/.claude/settings.json`\uff1a\n\n```json\n{\n \"extraKnownMarketplaces\": {\n \"everything-claude-code\": {\n \"source\": {\n \"source\": \"github\",\n \"repo\": \"affaan-m/everything-claude-code\"\n }\n }\n },\n \"enabledPlugins\": {\n \"everything-claude-code@everything-claude-code\": true\n }\n}\n```\n\n\u5b89\u88c5\u540e\u5373\u53ef\u7acb\u5373\u4f7f\u7528\u6240\u6709\u547d\u4ee4\u3001\u667a\u80fd\u4f53\u3001\u6280\u80fd\u548c\u94a9\u5b50\u3002\n\n> **\u6ce8\u610f\uff1a** Claude Code \u63d2\u4ef6\u7cfb\u7edf\u76ee\u524d\u4e0d\u652f\u6301\u901a\u8fc7\u63d2\u4ef6\u5206\u53d1 `rules`\uff08[\u4e0a\u6e38\u9650\u5236](https://code.claude.com/docs/en/plugins-reference)\uff09\u3002\u4f60\u9700\u8981\u624b\u52a8\u5b89\u88c5\u89c4\u5219\uff1a\n>\n> ```bash\n> # \u9996\u5148\u514b\u9686\u4ed3\u5e93\n> git clone https://github.com/affaan-m/everything-claude-code.git\n>\n> # \u9009\u9879 A\uff1a\u7528\u6237\u7ea7\u89c4\u5219\uff08\u9002\u7528\u4e8e\u6240\u6709\u9879\u76ee\uff09\n> cp -r everything-claude-code/rules/* ~/.claude/rules/\n>\n> # \u9009\u9879 B\uff1a\u9879\u76ee\u7ea7\u89c4\u5219\uff08\u4ec5\u9002\u7528\u4e8e\u5f53\u524d\u9879\u76ee\uff09\n> mkdir -p .claude/rules\n> cp -r everything-claude-code/rules/* .claude/rules/\n> ```\n\n---\n\n### \ud83d\udd27 \u9009\u9879 2\uff1a\u624b\u52a8\u5b89\u88c5\n\n\u5982\u679c\u4f60\u66f4\u559c\u6b22\u624b\u52a8\u63a7\u5236\u5b89\u88c5\u5185\u5bb9\uff1a\n\n```bash\n# \u514b\u9686\u4ed3\u5e93\ngit clone https://github.com/affaan-m/everything-claude-code.git\n\n# \u5c06\u667a\u80fd\u4f53\uff08Agents\uff09\u590d\u5236\u5230\u4f60\u7684 Claude \u914d\u7f6e\u4e2d\ncp everything-claude-code/agents/*.md ~/.claude/agents/\n\n# \u590d\u5236\u89c4\u5219\uff08Rules\uff09\ncp everything-claude-code/rules/*.md ~/.claude/rules/\n\n# \u590d\u5236\u547d\u4ee4\uff08Commands\uff09\ncp everything-claude-code/commands/*.md ~/.claude/commands/\n\n# \u590d\u5236\u6280\u80fd\uff08Skills\uff09\ncp -r everything-claude-code/skills/* ~/.claude/skills/\n```\n\n#### \u5c06\u94a9\u5b50\uff08Hooks\uff09\u6dfb\u52a0\u5230 settings.json\n\n\u5c06 `hooks/hooks.json` \u4e2d\u7684\u94a9\u5b50\u5185\u5bb9\u590d\u5236\u5230\u4f60\u7684 `~/.claude/settings.json`\u3002\n\n#### \u914d\u7f6e MCP\n\n\u5c06 `mcp-configs/mcp-servers.json` \u4e2d\u6240\u9700\u7684 MCP \u670d\u52a1\u5668\u914d\u7f6e\u590d\u5236\u5230\u4f60\u7684 `~/.claude.json`\u3002\n\n**\u91cd\u8981\u63d0\u793a\uff1a** \u8bf7\u5c06 `YOUR_*_HERE` \u5360\u4f4d\u7b26\u66ff\u6362\u4e3a\u4f60\u771f\u5b9e\u7684 API \u5bc6\u94a5\u3002\n\n---\n\n## \ud83c\udfaf \u6838\u5fc3\u6982\u5ff5\n\n### \u667a\u80fd\u4f53 (Agents)\n\n\u5b50\u667a\u80fd\u4f53\uff08Subagents\uff09\u8d1f\u8d23\u5904\u7406\u5177\u6709\u7279\u5b9a\u8303\u56f4\u7684\u59d4\u6d3e\u4efb\u52a1\u3002\u793a\u4f8b\uff1a\n\n```markdown\n---\nname: code-reviewer\ndescription: \u5ba1\u67e5\u4ee3\u7801\u7684\u8d28\u91cf\u3001\u5b89\u5168\u6027\u548c\u53ef\u7ef4\u62a4\u6027\ntools: [\"Read\", \"Grep\", \"Glob\", \"Bash\"]\nmodel: opus\n---\n\n\u4f60\u662f\u4e00\u540d\u8d44\u6df1\u4ee3\u7801\u5ba1\u67e5\u5458...\n```\n\n### \u6280\u80fd (Skills)\n\n\u6280\u80fd\u662f\u53ef\u88ab\u547d\u4ee4\u6216\u667a\u80fd\u4f53\u8c03\u7528\u7684\u5de5\u4f5c\u6d41\u5b9a\u4e49\uff1a\n\n```markdown\n# TDD \u5de5\u4f5c\u6d41\n\n1. \u9996\u5148\u5b9a\u4e49\u63a5\u53e3\n2. \u7f16\u5199\u5931\u8d25\u7684\u6d4b\u8bd5 (RED)\n3. \u5b9e\u73b0\u6700\u7b80\u4ee3\u7801 (GREEN)\n4. \u91cd\u6784 (IMPROVE)\n5. \u9a8c\u8bc1 80%+ \u7684\u8986\u76d6\u7387\n```\n\n### \u94a9\u5b50 (Hooks)\n\n\u94a9\u5b50\u5728\u5de5\u5177\u4e8b\u4ef6\u53d1\u751f\u65f6\u89e6\u53d1\u3002\u793a\u4f8b \u2014\u2014 \u8b66\u544a `console.log` \u7684\u4f7f\u7528\uff1a\n\n```json\n{\n \"matcher\": \"tool == \\\"Edit\\\" && tool_input.file_path matches \\\"\\\\.(ts|tsx|js|jsx)$\\\"\",\n \"hooks\": [{\n \"type\": \"command\",\n \"command\": \"#!/bin/bash\\ngrep -n 'console\\\\.log' \\\"$file_path\\\" && echo '[Hook] Remove console.log' >&2\"\n }]\n}\n```\n\n### \u89c4\u5219 (Rules)\n\n\u89c4\u5219\u662f\u5fc5\u987b\u59cb\u7ec8\u9075\u5faa\u7684\u6307\u5357\u3002\u8bf7\u4fdd\u6301\u5176\u6a21\u5757\u5316\uff1a\n\n```\n~/.claude/rules/\n security.md # \u4e0d\u5141\u8bb8\u786c\u7f16\u7801\u5bc6\u94a5\n coding-style.md # \u4e0d\u53ef\u53d8\u6027\u3001\u6587\u4ef6\u9650\u5236\n testing.md # TDD\u3001\u8986\u76d6\u7387\u8981\u6c42\n```\n\n---\n\n## \ud83e\uddea \u8fd0\u884c\u6d4b\u8bd5\n\n\u8be5\u63d2\u4ef6\u5305\u542b\u5b8c\u6574\u7684\u6d4b\u8bd5\u5957\u4ef6\uff1a\n\n```bash\n# \u8fd0\u884c\u6240\u6709\u6d4b\u8bd5\nnode tests/run-all.js\n\n# \u8fd0\u884c\u5355\u4e2a\u6d4b\u8bd5\u6587\u4ef6\nnode tests/lib/utils.test.js\nnode tests/lib/package-manager.test.js\nnode tests/hooks/hooks.test.js\n```\n\n---\n\n## \ud83e\udd1d \u53c2\u4e0e\u8d21\u732e\n\n**\u6b22\u8fce\u5e76\u9f13\u52b1\u5404\u7c7b\u8d21\u732e\u3002**\n\n\u672c\u4ed3\u5e93\u65e8\u5728\u6210\u4e3a\u793e\u533a\u8d44\u6e90\u3002\u5982\u679c\u4f60\u6709\uff1a\n- \u6709\u7528\u7684\u667a\u80fd\u4f53\u6216\u6280\u80fd\n- \u5de7\u5999\u7684\u94a9\u5b50\n- \u66f4\u597d\u7684 MCP \u914d\u7f6e\n- \u6539\u8fdb\u540e\u7684\u89c4\u5219\n\n\u8bf7\u5c3d\u7ba1\u8d21\u732e\uff01\u8bf7\u53c2\u9605 [CONTRIBUTING.md](CONTRIBUTING.md) \u83b7\u53d6\u6307\u5357\u3002\n\n### \u8d21\u732e\u601d\u8def\n\n- \u8bed\u8a00\u7279\u5b9a\u6280\u80fd\uff08Python, Rust \u6a21\u5f0f\uff09\u2014\u2014 \u5df2\u5305\u542b Go\uff01\n- \u6846\u67b6\u7279\u5b9a\u914d\u7f6e\uff08Django, Rails, Laravel\uff09\n- DevOps \u667a\u80fd\u4f53\uff08Kubernetes, Terraform, AWS\uff09\n- \u6d4b\u8bd5\u7b56\u7565\uff08\u4e0d\u540c\u6846\u67b6\uff09\n- \u9886\u57df\u7279\u5b9a\u77e5\u8bc6\uff08\u673a\u5668\u5b66\u4e60\u3001\u6570\u636e\u5de5\u7a0b\u3001\u79fb\u52a8\u7aef\uff09\n\n---\n\n## \ud83d\udcd6 \u9879\u76ee\u80cc\u666f\n\n\u81ea Claude Code \u5b9e\u9a8c\u9636\u6bb5\u8d77\u6211\u5c31\u4e00\u76f4\u5728\u4f7f\u7528\u5b83\u30022025 \u5e74 9 \u6708\uff0c\u6211\u4e0e [@DRodriguezFX](https://x.com/DRodriguezFX) \u51ed\u501f [zenith.chat](https://zenith.chat) \u8d62\u5f97\u4e86 Anthropic x Forum Ventures \u9ed1\u5ba2\u677e \u2014\u2014 \u8be5\u9879\u76ee\u5b8c\u5168\u4f7f\u7528 Claude Code \u6784\u5efa\u3002\n\n\u8fd9\u4e9b\u914d\u7f6e\u5728\u591a\u4e2a\u751f\u4ea7\u7ea7\u5e94\u7528\u4e2d\u7ecf\u8fc7\u4e86\u5b9e\u6218\u6d4b\u8bd5\u3002\n\n---\n\n## \u26a0\ufe0f \u91cd\u8981\u8bf4\u660e\n\n### \u4e0a\u4e0b\u6587\u7a97\u53e3\u7ba1\u7406 (Context Window Management)\n\n**\u81f3\u5173\u91cd\u8981\uff1a** \u4e0d\u8981\u540c\u65f6\u542f\u7528\u6240\u6709 MCP\u3002\u8fc7\u591a\u7684\u5de5\u5177\u4f1a\u5bfc\u81f4 200k \u7684\u4e0a\u4e0b\u6587\u7a97\u53e3\u7f29\u51cf\u81f3 70k\u3002\n\n\u7ecf\u9a8c\u6cd5\u5219\uff1a\n- \u914d\u7f6e 20-30 \u4e2a MCP\n- \u6bcf\u4e2a\u9879\u76ee\u4fdd\u6301\u542f\u7528 10 \u4e2a\u4ee5\u4e0b\n- \u6d3b\u8dc3\u5de5\u5177\u603b\u6570\u4fdd\u6301\u5728 80 \u4e2a\u4ee5\u4e0b\n\n\u4f7f\u7528\u9879\u76ee\u914d\u7f6e\u4e2d\u7684 `disabledMcpServers` \u6765\u7981\u7528\u4e0d\u5e38\u7528\u7684\u670d\u52a1\u5668\u3002\n\n### \u81ea\u5b9a\u4e49\n\n\u8fd9\u4e9b\u914d\u7f6e\u9002\u7528\u4e8e\u6211\u7684\u5de5\u4f5c\u6d41\u3002\u4f60\u5e94\u8be5\uff1a\n1. \u4ece\u5f15\u8d77\u4f60\u5171\u9e23\u7684\u90e8\u5206\u5f00\u59cb\n2. \u6839\u636e\u4f60\u7684\u6280\u672f\u6808\u8fdb\u884c\u4fee\u6539\n3. \u79fb\u9664\u4f60\u4e0d\u9700\u8981\u7684\u90e8\u5206\n4. \u6dfb\u52a0\u4f60\u81ea\u5df1\u7684\u6a21\u5f0f\n\n---\n\n## \ud83c\udf1f Star \u5386\u53f2\n\n[](https://star-history.com/#affaan-m/everything-claude-code&Date)\n\n---\n\n## \ud83d\udd17 \u76f8\u5173\u94fe\u63a5\n\n- **\u7b80\u660e\u6307\u5357 (\u4ece\u8fd9\u91cc\u5f00\u59cb)\uff1a** [Everything Claude Code \u7b80\u660e\u6307\u5357](https://x.com/affaanmustafa/status/2012378465664745795)\n- **\u6df1\u5ea6\u6307\u5357 (\u8fdb\u9636\u5fc5\u8bfb)\uff1a** [Everything Claude Code \u6df1\u5ea6\u6307\u5357](https://x.com/affaanmustafa/status/2014040193557471352)\n- **\u5173\u6ce8\u6211\uff1a** [@affaanmustafa](https://x.com/affaanmustafa)\n- **zenith.chat:** [zenith.chat](https://zenith.chat)\n\n---\n\n## \ud83d\udcc4 \u5f00\u6e90\u534f\u8bae\n\nMIT - \u81ea\u7531\u4f7f\u7528\u3001\u6309\u9700\u4fee\u6539\uff0c\u5982\u80fd\u56de\u9988\u793e\u533a\u4e0d\u80dc\u611f\u6fc0\u3002\n\n---\n\n**\u5982\u679c\u6b64\u4ed3\u5e93\u5bf9\u4f60\u6709\u5e2e\u52a9\uff0c\u8bf7\u70b9\u4eae Star\u3002\u9605\u8bfb\u4e24\u7bc7\u6307\u5357\u3002\u53bb\u6784\u5efa\u4f1f\u5927\u7684\u4ea7\u54c1\u5427\u3002**\n"
},
"/Users/Library/Applications/xx/code/github/everything-claude-code-zh/CONTRIBUTING.md": {
"md5": "eccf62e6ed292589dc5661a208902ddb",
@@ -302,5 +302,93 @@
"/Users/Library/Applications/xx/code/github/everything-claude-code-zh/.claude-plugin/README.md": {
"md5": "8b6aa77ad9181456133d28e477db962e",
"content": "### \u63d2\u4ef6\u6e05\u5355\u6ce8\u610f\u4e8b\u9879\uff08Plugin Manifest Gotchas\uff09\n\n\u5982\u679c\u4f60\u8ba1\u5212\u7f16\u8f91 `.claude-plugin/plugin.json`\uff0c\u8bf7\u6ce8\u610f Claude \u63d2\u4ef6\u9a8c\u8bc1\u5668\uff08plugin validator\uff09\u5f3a\u5236\u6267\u884c\u4e86\u4e00\u4e9b**\u672a\u516c\u5f00\u4f46\u4e25\u683c\u7684\u7ea6\u675f**\uff0c\u8fd9\u4e9b\u7ea6\u675f\u53ef\u80fd\u5bfc\u81f4\u5b89\u88c5\u5931\u8d25\u5e76\u663e\u793a\u6a21\u7cca\u7684\u9519\u8bef\uff08\u4f8b\u5982\uff0c`agents: Invalid input`\uff09\u3002\u7279\u522b\u662f\uff0c\u7ec4\u4ef6\u5b57\u6bb5\u5fc5\u987b\u662f\u6570\u7ec4\uff08arrays\uff09\uff0c`agents` \u5fc5\u987b\u4f7f\u7528\u660e\u786e\u7684\u6587\u4ef6\u8def\u5f84\u800c\u975e\u76ee\u5f55\uff0c\u4e14\u4e3a\u4e86\u5b9e\u73b0\u53ef\u9760\u7684\u9a8c\u8bc1\u548c\u5b89\u88c5\uff0c\u5fc5\u987b\u5305\u542b `version` \u5b57\u6bb5\u3002\n\n\u8fd9\u4e9b\u7ea6\u675f\u5728\u516c\u5f00\u793a\u4f8b\u4e2d\u5e76\u4e0d\u660e\u663e\uff0c\u4e14\u5728\u8fc7\u53bb\u66fe\u591a\u6b21\u5bfc\u81f4\u5b89\u88c5\u5931\u8d25\u3002\u5b83\u4eec\u5728 `.claude-plugin/PLUGIN_SCHEMA_NOTES.md` \u4e2d\u6709\u8be6\u7ec6\u8bb0\u5f55\uff0c\u5728\u5bf9\u63d2\u4ef6\u6e05\u5355\uff08plugin manifest\uff09\u8fdb\u884c\u4efb\u4f55\u66f4\u6539\u4e4b\u524d\uff0c\u5e94\u4ed4\u7ec6\u9605\u8bfb\u8be5\u6587\u6863\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/.claude/skills/oneskill/README.md": {
+ "md5": "dea7f33f771f2ae664e5841b89ff1952",
+ "content": "\n\n# OneSkill \u5143\u7ba1\u7406\u5668\uff08Meta-Manager\uff09\n\n**AI \u667a\u80fd\u4f53\u6280\u80fd\uff08Agent Skills\uff09\u7684\u901a\u7528\u6865\u6881\u3002** \n\u4ece OpenSkills \u6ce8\u518c\u8868\u4e2d\u53d1\u73b0\u3001\u5b89\u88c5\u5e76\u6620\u5c04\u529f\u80fd\u5230\u60a8\u7684\u73af\u5883\u3002\n\n[](https://www.npmjs.com/package/oneskill)\n[](LICENSE)\n\n[**\ud83c\uddfa\ud83c\uddf8 English**](README.md) | [**\ud83c\udde8\ud83c\uddf3 \u4e2d\u6587\u6307\u5357**](README_CN.md)\n\n
\n\n---\n\n## \u26a1\ufe0f \u4ec0\u4e48\u662f OneSkill\uff1f\n\n**OneSkill** \u662f\u4e00\u6b3e\u4e13\u4e3a AI \u667a\u80fd\u4f53\uff08Agent\uff09\uff08\u4ee5\u53ca\u4eba\u7c7b\uff09\u8bbe\u8ba1\u7684\u5143\u5de5\u5177\uff0c\u7528\u4e8e\u8f7b\u677e\u6269\u5c55\u5176\u529f\u80fd\u3002\u5b83\u662f [OpenSkills](https://github.com/Starttoaster/openskills) \u751f\u6001\u7cfb\u7edf\u7684\u641c\u7d22\u5f15\u64ce\u548c\u5de5\u4f5c\u6d41\u7ba1\u7406\u5668\uff08Workflow Manager\uff09\u3002\n\n\u867d\u7136 `openskills` \u5904\u7406\u6587\u4ef6\u7684\u539f\u59cb\u5b89\u88c5\uff0c\u4f46 **OneSkill** \u63d0\u4f9b\uff1a\n1. **\u667a\u80fd\u641c\u7d22\uff08Intelligent Search\uff09**\uff1a\u4f7f\u7528\u81ea\u7136\u8bed\u8a00\u6216\u5173\u952e\u8bcd\u627e\u5230\u9002\u5408\u8be5\u4efb\u52a1\u7684\u5de5\u5177\u3002\n2. **\u5de5\u4f5c\u6d41\u6307\u5357\uff08Workflow Guidance\uff09**\uff1a\u4e3a\u667a\u80fd\u4f53\uff08Agent\uff09\u5b89\u5168\u83b7\u53d6\u65b0\u6280\u80fd\u63d0\u4f9b\u6807\u51c6\u5316\u6d41\u7a0b\u3002\n3. **\u73af\u5883\u6620\u5c04\uff08Environment Mapping\uff09**\uff1a\u81f3\u5173\u91cd\u8981\u7684\u4e00\u70b9\u662f\uff0c\u5b83\u5f25\u5408\u4e86 `openskills`\uff08\u6807\u51c6\u7ed3\u6784\uff09\u4e0e **Gemini CLI**\uff08\u81ea\u5b9a\u4e49\u7ed3\u6784\uff09\u7b49\u4f7f\u7528\u8005\u4e4b\u95f4\u7684\u9e3f\u6c9f\u3002\n\n## \ud83d\ude80 \u5feb\u901f\u5f00\u59cb\n\n\u60a8\u65e0\u9700\u6c38\u4e45\u5b89\u88c5\u3002\u53ea\u9700\u4f7f\u7528 `npx` \u8fd0\u884c\u5373\u53ef\u3002\n\n```bash\n# \u641c\u7d22\u6280\u80fd\uff08\u4f8b\u5982\uff0c\u7528\u4e8e\u6d4f\u89c8\u7f51\u9875\uff09\nnpx oneskill search \"puppeteer browser\"\n\n# \u641c\u7d22\u6309\u6d41\u884c\u5ea6\u6392\u5e8f\u7684\u6570\u636e\u5e93\u5de5\u5177\nnpx oneskill search \"database\" --sort stars\n```\n\n## \ud83d\udee0 \u5de5\u4f5c\u6d41\n\n\u4e3a\u60a8\u7684\u667a\u80fd\u4f53\uff08Agent\uff09\u6dfb\u52a0\u65b0\u529f\u80fd\u7684\u6807\u51c6\u751f\u547d\u5468\u671f\uff1a\n\n1. **\u641c\u7d22\uff08Search\uff09**\uff1a\u67e5\u627e\u6280\u80fd\u3002\n ```bash\n npx oneskill search \"github integration\"\n ```\n2. **\u5b89\u88c5\uff08Install\uff09**\uff1a\u4f7f\u7528\u6807\u51c6\u7684 `openskills` \u5b89\u88c5\u7a0b\u5e8f\u3002\n ```bash\n npx openskills install anthropics/skills\n ```\n3. **\u6620\u5c04\uff08Map\uff09\uff08\u5bf9 Gemini \u81f3\u5173\u91cd\u8981\uff09**\uff1a\u5982\u679c\u60a8\u6b63\u5728\u4f7f\u7528 **Gemini CLI**\uff0c\u5219\u5fc5\u987b\u5c06\u5b89\u88c5\u7684\u6280\u80fd\u6620\u5c04\u5230\u60a8\u7684\u914d\u7f6e\u4e2d\u3002\n ```bash\n # \u5c06\u5b89\u88c5\u7684\u6280\u80fd\u6620\u5c04\u5230 Gemini \u7684\u914d\u7f6e\n npx oneskill map --target gemini\n ```\n\n## \ud83d\udcd6 \u547d\u4ee4\u53c2\u8003\n\n### `search`\n\u5728\u5168\u5c40\u6ce8\u518c\u8868\u4e2d\u641c\u7d22\u6280\u80fd\u3002\n```bash\nnpx oneskill search [options]\n\n# \u9009\u9879\uff1a\n# --category \u6309\u7c7b\u522b\u8fc7\u6ee4\n# --sort \u6309 'stars'\u3001'created' \u6216 'updated' \u6392\u5e8f\n# --limit \u9650\u5236\u7ed3\u679c\u6570\u91cf\uff08\u9ed8\u8ba4\u503c\uff1a10\uff09\n```\n\n### `map`\n\u4e3a\u7279\u5b9a\u7684\u667a\u80fd\u4f53\uff08Agent\uff09\u73af\u5883\u751f\u6210\u914d\u7f6e\u3002\n```bash\nnpx oneskill map --target \n\n# \u76ee\u6807\uff1a\n# gemini \u751f\u6210/\u66f4\u65b0 Gemini CLI \u914d\u7f6e\n```\n\n### `list`\n\u5217\u51fa\u672c\u5730\u6620\u5c04\u7684\u6280\u80fd\uff08`openskills list` \u7684\u5c01\u88c5\uff09\u3002\n```bash\nnpx oneskill list\n```\n\n---\n\n\n \u7531 OneSkill \u793e\u533a\u7528 \u2764\ufe0f \u6784\u5efa \n
\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/.claude/skills/oneskill/README_CN.md": {
+ "md5": "00cb3790dfcf2a984d4316d0c3bfa524",
+ "content": "\n\n# OneSkill \u5143\u7ba1\u7406\u5668 (Meta-Manager)\n\n**AI \u667a\u80fd\u4f53 (Agent) \u6280\u80fd\u7684\u901a\u7528\u6865\u6881** \n\u5e2e\u52a9\u4f60\u53d1\u73b0\u3001\u5b89\u88c5\u5e76\u5c06 OpenSkills \u6ce8\u518c\u8868\u4e2d\u7684\u80fd\u529b\u6620\u5c04\u5230\u4f60\u7684\u8fd0\u884c\u73af\u5883\u3002\n\n[](https://www.npmjs.com/package/oneskill)\n[](LICENSE)\n\n[**\ud83c\uddfa\ud83c\uddf8 English**](README.md) | [**\ud83c\udde8\ud83c\uddf3 \u4e2d\u6587\u6307\u5357**](README_CN.md)\n\n
\n\n---\n\n## \u26a1\ufe0f \u4ec0\u4e48\u662f OneSkill\uff1f\n\n**OneSkill** \u662f\u4e00\u4e2a\u4e3a AI \u667a\u80fd\u4f53 (Agent) \u8bbe\u8ba1\u7684\u901a\u7528\u6280\u80fd\u7ba1\u7406\u5de5\u5177\u3002\u5b83\u4f5c\u4e3a [OpenSkills](https://github.com/Starttoaster/openskills) \u751f\u6001\u7cfb\u7edf\u7684\u641c\u7d22\u5f15\u64ce\u548c\u5de5\u4f5c\u6d41\u7ba1\u7406\u5668 (Workflow Manager)\uff0c\u5e2e\u52a9\u4f60\u53d1\u73b0\u3001\u5b89\u88c5\u5e76\u5c06\u80fd\u529b\u6620\u5c04\u5230\u4f60\u7684\u8fd0\u884c\u73af\u5883\u4e2d\u3002\n\n\u867d\u7136 `openskills` \u8d1f\u8d23\u6587\u4ef6\u7684\u4e0b\u8f7d\u5b89\u88c5\uff0c\u4f46 **OneSkill** \u63d0\u4f9b\u4e86\uff1a\n1. **\u667a\u80fd\u641c\u7d22**: \u652f\u6301\u901a\u8fc7\u81ea\u7136\u8bed\u8a00\u6216\u5173\u952e\u8bcd\u641c\u7d22\u6ce8\u518c\u8868\u4e2d\u7684\u6280\u80fd (Skill)\u3002\n2. **\u5de5\u4f5c\u6d41\u5f15\u5bfc**: \u4e3a\u667a\u80fd\u4f53 (Agent) \u63d0\u4f9b\u4e86\u4e00\u5957\u6807\u51c6\u7684\u6269\u5c55\u80fd\u529b\u6d41\u7a0b\uff08\u641c\u7d22 -> \u786e\u8ba4 -> \u5b89\u88c5\uff09\u3002\n3. **\u73af\u5883\u6620\u5c04 (Mapping)**: \u89e3\u51b3\u4e86\u5b89\u88c5\u8def\u5f84\u4e0e\u8fd0\u884c\u73af\u5883\u4e0d\u4e00\u81f4\u7684\u95ee\u9898\u3002\u7279\u522b\u662f\u5bf9\u4e8e **Gemini CLI** \u7528\u6237\uff0cOneSkill \u80fd\u81ea\u52a8\u5c06\u4e0b\u8f7d\u7684\u6280\u80fd (Skill) \u6620\u5c04\u5230 Gemini \u7684\u914d\u7f6e\u6587\u4ef6\u4e2d\u3002\n\n## \ud83d\ude80 \u5feb\u901f\u5f00\u59cb\n\n\u65e0\u9700\u5168\u5c40\u5b89\u88c5\uff0c\u76f4\u63a5\u4f7f\u7528 `npx` \u8fd0\u884c\u5373\u53ef\uff1a\n\n```bash\n# \u641c\u7d22\u6280\u80fd (\u4f8b\u5982\uff1a\u60f3\u8981\u7f51\u9875\u6d4f\u89c8\u80fd\u529b)\nnpx oneskill search \"puppeteer browser\"\n\n# \u641c\u7d22\u6570\u636e\u5e93\u76f8\u5173\u6280\u80fd\uff0c\u5e76\u6309\u661f\u7ea7\u6392\u5e8f\nnpx oneskill search \"database\" --sort stars\n```\n\n## \ud83d\udee0 \u4f7f\u7528\u6d41\u7a0b\n\n\u4e3a\u4f60\u7684\u667a\u80fd\u4f53 (Agent) \u6dfb\u52a0\u65b0\u80fd\u529b\u7684\u63a8\u8350\u6b65\u9aa4\uff1a\n\n1. **\u641c\u7d22 (Search)**: \u67e5\u627e\u4f60\u9700\u8981\u7684\u6280\u80fd (Skill)\u3002\n ```bash\n npx oneskill search \"github integration\"\n ```\n\n2. **\u5b89\u88c5 (Install)**: \u4f7f\u7528 `openskills` \u6807\u51c6\u547d\u4ee4\u8fdb\u884c\u4e0b\u8f7d\u3002\n ```bash\n npx openskills install anthropics/skills\n ```\n\n3. **\u6620\u5c04 (Map)**: **(Gemini \u7528\u6237\u5fc5\u8bfb)**\n `openskills` \u9ed8\u8ba4\u5c06\u6587\u4ef6\u4e0b\u8f7d\u5230\u901a\u7528\u76ee\u5f55\uff0cGemini CLI \u65e0\u6cd5\u76f4\u63a5\u8bfb\u53d6\u3002\u5fc5\u987b\u6267\u884c\u6620\u5c04 (Mapping) \u547d\u4ee4\uff1a\n ```bash\n # \u81ea\u52a8\u8bc6\u522b\u5df2\u5b89\u88c5\u7684\u6280\u80fd (Skill) \u5e76\u914d\u7f6e\u5230 Gemini\n npx oneskill map --target gemini\n ```\n *\u5982\u679c\u4f60\u7684\u6280\u80fd (Skill) \u662f\u5168\u5c40\u5b89\u88c5\u7684 (\u52a0\u4e86 --global)\uff0c\u8fd9\u91cc\u4e5f\u9700\u8981\u52a0 --global\u3002*\n\n## \ud83d\udcd6 \u547d\u4ee4\u53c2\u8003\n\n### `search` (\u641c\u7d22)\n\u5728\u5168\u5c40\u6ce8\u518c\u8868\u4e2d\u641c\u7d22\u6280\u80fd (Skill)\u3002\n```bash\nnpx oneskill search <\u67e5\u8be2\u8bcd> [\u9009\u9879]\n\n# \u9009\u9879:\n# --category \u6309\u5206\u7c7b\u7b5b\u9009\n# --sort \u6392\u5e8f\u65b9\u5f0f: 'stars' (\u661f\u7ea7), 'created' (\u521b\u5efa\u65f6\u95f4), 'updated' (\u66f4\u65b0\u65f6\u95f4)\n# --limit \u9650\u5236\u8fd4\u56de\u6570\u91cf (\u9ed8\u8ba4: 10)\n```\n\n### `map` (\u6620\u5c04)\n\u4e3a\u7279\u5b9a\u73af\u5883\u751f\u6210\u914d\u7f6e\u3002\n```bash\nnpx oneskill map --target <\u73af\u5883>\n\n# \u652f\u6301\u7684\u76ee\u6807:\n# gemini \u66f4\u65b0 Gemini CLI \u7684\u914d\u7f6e\u4e0e\u8def\u5f84\u6620\u5c04\n```\n\n### `list` (\u5217\u8868)\n\u67e5\u770b\u672c\u5730\u5df2\u5b89\u88c5\u7684\u6280\u80fd (Skill)\u3002\n```bash\nnpx oneskill list\n```\n\n---\n\n\n Built with \u2764\ufe0f by the OneSkill Community \n
\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/.claude/skills/oneskill/SKILL.md": {
+ "md5": "83deb8de388c8bddcf138fa8b052ef70",
+ "content": "---\nname: oneskill\ndescription: \u53d1\u73b0\u6280\u80fd (Skill)\uff0c\u8fed\u4ee3\u67e5\u8be2\uff0c\u5e76\u5728\u4efb\u4f55\u73af\u5883\u4e2d\u81ea\u52a8\u5b89\u88c5\u6280\u80fd\u3002\n---\n\n# OneSkill \u5143\u7ba1\u7406\u5668 (Meta-Manager)\n\n\u4f7f\u7528\u6b64\u6280\u80fd\u6765\u53d1\u73b0\u65b0\u529f\u80fd\u3001\u4f18\u5316\u641c\u7d22\u67e5\u8be2\uff0c\u5e76\u4f7f\u7528 OpenSkills \u7b80\u5316\u6280\u80fd\u8bbe\u7f6e\u3002\u8fd9\u4e3a\u6269\u5c55\u73af\u5883\u529f\u80fd\u63d0\u4f9b\u4e86\u4e00\u79cd\u7edf\u4e00\u7684\u65b9\u5f0f\u3002\n\n## \u4f55\u65f6\u4f7f\u7528\n\n- \u5f53\u7528\u6237\u8981\u6c42\u7684\u67d0\u4e9b\u529f\u80fd\u4f60\u76ee\u524d\u4e0d\u5177\u5907\u65f6\u3002\n- \u5f53\u4efb\u52a1\u590d\u6742\u3001\u5c5e\u4e8e\u7279\u5b9a\u9886\u57df\uff0c\u6216\u5728\u5c1d\u8bd5 2 \u6b21\u540e\u4ecd\u88ab\u53cd\u590d\u963b\u65ad\u65f6\u3002\n- \u5f53\u53ef\u80fd\u5b58\u5728\u66f4\u597d\u7684\u6280\u80fd\uff08Skill\uff09\u65f6\uff08\u4f8b\u5982\uff1a\u7f51\u9875\u6d4f\u89c8\u3001GitHub \u96c6\u6210\u3001\u6570\u636e\u5e93\u7ba1\u7406\u3001\u4e91\u57fa\u7840\u8bbe\u65bd\uff09\u3002\n\n## \u5de5\u4f5c\u6d41 (Workflow)\n\n1. \u641c\u7d22\u6ce8\u518c\u8868\uff1a\n - \u8fd0\u884c\uff1a`npx oneskill search \"\" [options]`\n - \u652f\u6301\u7684\u9009\u9879\uff1a`--category`\u3001`--limit`\u3001`--offset`\u3001`--sort`\u3002\n - \u793a\u4f8b\uff1a\n - `npx oneskill search \"browser\" --sort stars`\n - `npx oneskill search \"\" --category database --limit 5`\n2. \u5206\u6790\u7ed3\u679c\uff1a\n - \u786e\u5b9a\u6700\u4f73\u5339\u914d\u9879\uff0c\u6216\u4f18\u5316\u67e5\u8be2\u5e76\u518d\u6b21\u641c\u7d22\u3002\n3. \u4e0e\u7528\u6237\u786e\u8ba4\uff1a\n - \u8bf4\u660e\u8be5\u6280\u80fd\u7684\u529f\u80fd\u53ca\u5176\u6765\u6e90\u3002\n4. \u5728\u83b7\u5f97\u660e\u786e\u6279\u51c6\u540e\u8fdb\u884c\u5b89\u88c5\uff08\u4f7f\u7528 openskills\uff09\uff1a\n - \u8fd0\u884c\uff1a`npx openskills install `\n - \u793a\u4f8b\uff1a`npx openskills install anthropics/skills`\n5. \u5904\u7406\u7279\u5b9a\u73af\u5883\u7684\u8bbe\u7f6e\uff1a\n - **Gemini CLI \u7528\u6237\uff1a** `openskills` \u4e0d\u4f1a\u81ea\u52a8\u914d\u7f6e Gemini\u3002\u5b89\u88c5\u540e\u4f60**\u5fc5\u987b**\u8fd0\u884c\u6620\u5c04\u547d\u4ee4\uff1a\n - `npx oneskill map --target gemini`\uff08\u5982\u679c\u662f\u5168\u5c40\u5b89\u88c5\uff0c\u8bf7\u6dfb\u52a0 `--global`\uff09\n6. \u5e94\u7528\u65b0\u6280\u80fd\u4ee5\u5b8c\u6210\u539f\u59cb\u8bf7\u6c42\u3002\n\n## OpenSkills \u57fa\u7840\n\n- `npx openskills install [options]` # \u4ece GitHub\u3001\u672c\u5730\u8def\u5f84\u6216\u79c1\u6709\u4ed3\u5e93\u5b89\u88c5\n- `npx openskills sync [-y] [-o ]` # \u66f4\u65b0 AGENTS.md (\u6216\u81ea\u5b9a\u4e49\u8f93\u51fa)\n- `npx openskills list` # \u663e\u793a\u5df2\u5b89\u88c5\u7684\u6280\u80fd\n- `npx openskills read ` # \u52a0\u8f7d\u6280\u80fd\uff08\u4f9b\u667a\u80fd\u4f53 (Agent) \u4f7f\u7528\uff09\n- `npx openskills update [name...]` # \u66f4\u65b0\u5df2\u5b89\u88c5\u7684\u6280\u80fd\uff08\u9ed8\u8ba4\uff1a\u5168\u90e8\uff09\n- `npx openskills manage` # \u79fb\u9664\u6280\u80fd\uff08\u4ea4\u4e92\u5f0f\uff09\n- `npx openskills remove ` # \u79fb\u9664\u7279\u5b9a\u6280\u80fd\n\n\u793a\u4f8b\uff1a\n- `npx openskills install anthropics/skills`\n- `npx openskills sync`\n\n\u9ed8\u8ba4\u8bbe\u7f6e\uff1a\u5b89\u88c5\u5728\u9879\u76ee\u672c\u5730\uff08`./.claude/skills`\uff0c\u6216\u8005\u5e26 `--universal` \u53c2\u6570\u5b89\u88c5\u5728 `./.agent/skills`\uff09\u3002\u4f7f\u7528 `--global` \u5b89\u88c5\u5728 `~/.claude/skills`\u3002\n\n## \u5b89\u5168\u63d0\u793a (Safety Reminders)\n\n- \u672a\u7ecf\u7528\u6237\u660e\u786e\u786e\u8ba4\uff0c\u8bf7\u52ff\u5b89\u88c5\u3002\n- \u9664\u975e\u7528\u6237\u540c\u610f\u8986\u76d6\u73b0\u6709\u76ee\u6807\uff0c\u5426\u5219\u907f\u514d\u4f7f\u7528 `--force-map`\u3002\n- \u4f7f\u7528 openskills \u8fdb\u884c\u5b89\u88c5/\u66f4\u65b0\uff1bOneSkill \u4ec5\u4e3a Gemini \u63d0\u4f9b\u641c\u7d22\u548c\u6620\u5c04\u3002\n- \u5bf9\u4e8e Gemini\uff0c\u8bf7\u5728\u5b89\u88c5\u540e\u8fd0\u884c `npx oneskill map --target gemini`\u3002\n- \u9ed8\u8ba4\u5b89\u88c5/\u6620\u5c04\u662f\u9879\u76ee\u672c\u5730\u7684\uff0c\u4e0e openskills \u76f8\u540c\uff1b\u5168\u5c40\u5b89\u88c5\u8bf7\u4f7f\u7528 `--global`\u3002\n- \u5b89\u88c5 OneSkill \u672c\u8eab\u65f6\uff0c\u5efa\u8bae\u4f7f\u7528 `--global`\uff0c\u4ee5\u4fbf\u5728\u8de8\u9879\u76ee\u65f6\u53ef\u7528\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/agents/python-reviewer.md": {
+ "md5": "eeb704456d8bdc70c9ee36873ba6fd52",
+ "content": "---\nname: python-reviewer\ndescription: Expert Python code reviewer specializing in PEP 8 compliance, Pythonic idioms, type hints, security, and performance. Use for all Python code changes. MUST BE USED for Python projects.\ntools: [\"Read\", \"Grep\", \"Glob\", \"Bash\"]\nmodel: opus\n---\n\n\u4f60\u662f\u4e00\u4f4d\u8d44\u6df1\u7684 Python \u4ee3\u7801\u5ba1\u67e5\u5458\uff08Code Reviewer\uff09\uff0c\u81f4\u529b\u4e8e\u786e\u4fdd\u4ee3\u7801\u7b26\u5408\u9ad8\u6807\u51c6\u7684 Pythonic \u89c4\u8303\u53ca\u6700\u4f73\u5b9e\u8df5\u3002\n\n\u5f53\u88ab\u8c03\u7528\u65f6\uff1a\n1. \u8fd0\u884c `git diff -- '*.py'` \u4ee5\u67e5\u770b\u6700\u8fd1\u7684 Python \u6587\u4ef6\u53d8\u66f4\n2. \u5982\u679c\u53ef\u7528\uff0c\u8fd0\u884c\u9759\u6001\u5206\u6790\u5de5\u5177\uff08ruff\u3001mypy\u3001pylint\u3001black --check\uff09\n3. \u91cd\u70b9\u5173\u6ce8\u4fee\u6539\u8fc7\u7684 `.py` \u6587\u4ef6\n4. \u7acb\u5373\u5f00\u59cb\u5ba1\u67e5\n\n## \u5b89\u5168\u68c0\u67e5 (\u4e25\u91cd/CRITICAL)\n\n- **SQL \u6ce8\u5165 (SQL Injection)**\uff1a\u6570\u636e\u5e93\u67e5\u8be2\u4e2d\u7684\u5b57\u7b26\u4e32\u62fc\u63a5\n ```python\n # \u9519\u8bef\u505a\u6cd5\n cursor.execute(f\"SELECT * FROM users WHERE id = {user_id}\")\n # \u6b63\u786e\u505a\u6cd5\n cursor.execute(\"SELECT * FROM users WHERE id = %s\", (user_id,))\n ```\n\n- **\u547d\u4ee4\u6ce8\u5165 (Command Injection)**\uff1asubprocess/os.system \u4e2d\u672a\u7ecf\u9a8c\u8bc1\u7684\u8f93\u5165\n ```python\n # \u9519\u8bef\u505a\u6cd5\n os.system(f\"curl {url}\")\n # \u6b63\u786e\u505a\u6cd5\n subprocess.run([\"curl\", url], check=True)\n ```\n\n- **\u8def\u5f84\u7a7f\u8d8a (Path Traversal)**\uff1a\u7528\u6237\u63a7\u5236\u7684\u6587\u4ef6\u8def\u5f84\n ```python\n # \u9519\u8bef\u505a\u6cd5\n open(os.path.join(base_dir, user_path))\n # \u6b63\u786e\u505a\u6cd5\n clean_path = os.path.normpath(user_path)\n if clean_path.startswith(\"..\"):\n raise ValueError(\"Invalid path\")\n safe_path = os.path.join(base_dir, clean_path)\n ```\n\n- **Eval/Exec \u6ee5\u7528**\uff1a\u5728 eval/exec \u4e2d\u4f7f\u7528\u7528\u6237\u8f93\u5165\n- **Pickle \u4e0d\u5b89\u5168\u53cd\u5e8f\u5217\u5316**\uff1a\u52a0\u8f7d\u4e0d\u53ef\u4fe1\u7684 pickle \u6570\u636e\n- **\u786c\u7f16\u7801\u5bc6\u94a5 (Hardcoded Secrets)**\uff1a\u6e90\u7801\u4e2d\u5305\u542b API \u5bc6\u94a5\u3001\u5bc6\u7801\n- **\u5f31\u52a0\u5bc6**\uff1a\u51fa\u4e8e\u5b89\u5168\u76ee\u7684\u4f7f\u7528 MD5/SHA1\n- **YAML \u4e0d\u5b89\u5168\u52a0\u8f7d**\uff1a\u4f7f\u7528\u4e0d\u5e26 Loader \u7684 yaml.load\n\n## \u9519\u8bef\u5904\u7406 (\u4e25\u91cd/CRITICAL)\n\n- **\u7a7a except \u8bed\u53e5 (Bare Except Clauses)**\uff1a\u6355\u83b7\u6240\u6709\u5f02\u5e38\n ```python\n # \u9519\u8bef\u505a\u6cd5\n try:\n process()\n except:\n pass\n\n # \u6b63\u786e\u505a\u6cd5\n try:\n process()\n except ValueError as e:\n logger.error(f\"Invalid value: {e}\")\n ```\n\n- **\u541e\u6389\u5f02\u5e38 (Swallowing Exceptions)**\uff1a\u9759\u9ed8\u5931\u8d25\n- **\u7528\u5f02\u5e38\u4ee3\u66ff\u6d41\u7a0b\u63a7\u5236**\uff1a\u5c06\u5f02\u5e38\u7528\u4e8e\u6b63\u5e38\u7684\u63a7\u5236\u6d41\n- **\u7f3a\u5931 finally**\uff1a\u8d44\u6e90\u672a\u88ab\u6e05\u7406\n ```python\n # \u9519\u8bef\u505a\u6cd5\n f = open(\"file.txt\")\n data = f.read()\n # \u5982\u679c\u53d1\u751f\u5f02\u5e38\uff0c\u6587\u4ef6\u6c38\u8fdc\u4e0d\u4f1a\u5173\u95ed\n\n # \u6b63\u786e\u505a\u6cd5\n with open(\"file.txt\") as f:\n data = f.read()\n # \u6216\u8005\n f = open(\"file.txt\")\n try:\n data = f.read()\n finally:\n f.close()\n ```\n\n## \u7c7b\u578b\u63d0\u793a (\u9ad8\u4f18\u5148\u7ea7/HIGH)\n\n- **\u7f3a\u5931\u7c7b\u578b\u63d0\u793a (Type Hints)**\uff1a\u516c\u5171\u51fd\u6570\u6ca1\u6709\u7c7b\u578b\u6807\u6ce8\n ```python\n # \u9519\u8bef\u505a\u6cd5\n def process_user(user_id):\n return get_user(user_id)\n\n # \u6b63\u786e\u505a\u6cd5\n from typing import Optional\n\n def process_user(user_id: str) -> Optional[User]:\n return get_user(user_id)\n ```\n\n- **\u4f7f\u7528 Any \u800c\u975e\u7279\u5b9a\u7c7b\u578b**\n ```python\n # \u9519\u8bef\u505a\u6cd5\n from typing import Any\n\n def process(data: Any) -> Any:\n return data\n\n # \u6b63\u786e\u505a\u6cd5\n from typing import TypeVar\n\n T = TypeVar('T')\n\n def process(data: T) -> T:\n return data\n ```\n\n- **\u9519\u8bef\u7684\u8fd4\u56de\u7c7b\u578b**\uff1a\u6807\u6ce8\u4e0e\u5b9e\u9645\u4e0d\u7b26\n- **\u672a\u5408\u7406\u4f7f\u7528 Optional**\uff1a\u53ef\u4e3a None \u7684\u53c2\u6570\u672a\u6807\u8bb0\u4e3a Optional\n\n## Pythonic \u4ee3\u7801 (\u9ad8\u4f18\u5148\u7ea7/HIGH)\n\n- **\u672a\u4f7f\u7528\u4e0a\u4e0b\u6587\u7ba1\u7406\u5668 (Context Managers)**\uff1a\u624b\u52a8\u8fdb\u884c\u8d44\u6e90\u7ba1\u7406\n ```python\n # \u9519\u8bef\u505a\u6cd5\n f = open(\"file.txt\")\n try:\n content = f.read()\n finally:\n f.close()\n\n # \u6b63\u786e\u505a\u6cd5\n with open(\"file.txt\") as f:\n content = f.read()\n ```\n\n- **C \u98ce\u683c\u5faa\u73af**\uff1a\u672a\u4f7f\u7528\u63a8\u5bfc\u5f0f\uff08Comprehensions\uff09\u6216\u8fed\u4ee3\u5668\n ```python\n # \u9519\u8bef\u505a\u6cd5\n result = []\n for item in items:\n if item.active:\n result.append(item.name)\n\n # \u6b63\u786e\u505a\u6cd5\n result = [item.name for item in items if item.active]\n ```\n\n- **\u4f7f\u7528 isinstance \u68c0\u67e5\u7c7b\u578b**\uff1a\u800c\u975e\u4f7f\u7528 type()\n ```python\n # \u9519\u8bef\u505a\u6cd5\n if type(obj) == str:\n process(obj)\n\n # \u6b63\u786e\u505a\u6cd5\n if isinstance(obj, str):\n process(obj)\n ```\n\n- **\u672a\u4f7f\u7528\u679a\u4e3e (Enum) \u6216\u5b58\u5728\u9b54\u672f\u6570\u5b57 (Magic Numbers)**\n ```python\n # \u9519\u8bef\u505a\u6cd5\n if status == 1:\n process()\n\n # \u6b63\u786e\u505a\u6cd5\n from enum import Enum\n\n class Status(Enum):\n ACTIVE = 1\n INACTIVE = 2\n\n if status == Status.ACTIVE:\n process()\n ```\n\n- **\u5faa\u73af\u4e2d\u7684\u5b57\u7b26\u4e32\u62fc\u63a5**\uff1a\u4f7f\u7528 + \u6784\u5efa\u5b57\u7b26\u4e32\n ```python\n # \u9519\u8bef\u505a\u6cd5\n result = \"\"\n for item in items:\n result += str(item)\n\n # \u6b63\u786e\u505a\u6cd5\n result = \"\".join(str(item) for item in items)\n ```\n\n- **\u53ef\u53d8\u9ed8\u8ba4\u53c2\u6570 (Mutable Default Arguments)**\uff1a\u7ecf\u5178\u7684 Python \u9677\u9631\n ```python\n # \u9519\u8bef\u505a\u6cd5\n def process(items=[]):\n items.append(\"new\")\n return items\n\n # \u6b63\u786e\u505a\u6cd5\n def process(items=None):\n if items is None:\n items = []\n items.append(\"new\")\n return items\n ```\n\n## \u4ee3\u7801\u8d28\u91cf (\u9ad8\u4f18\u5148\u7ea7/HIGH)\n\n- **\u53c2\u6570\u8fc7\u591a**\uff1a\u51fd\u6570\u53c2\u6570\u8d85\u8fc7 5 \u4e2a\n ```python\n # \u9519\u8bef\u505a\u6cd5\n def process_user(name, email, age, address, phone, status):\n pass\n\n # \u6b63\u786e\u505a\u6cd5\n from dataclasses import dataclass\n\n @dataclass\n class UserData:\n name: str\n email: str\n age: int\n address: str\n phone: str\n status: str\n\n def process_user(data: UserData):\n pass\n ```\n\n- **\u8fc7\u957f\u51fd\u6570**\uff1a\u51fd\u6570\u8d85\u8fc7 50 \u884c\n- **\u5d4c\u5957\u8fc7\u6df1**\uff1a\u7f29\u8fdb\u8d85\u8fc7 4 \u5c42\n- **\u4e0a\u5e1d\u7c7b/\u6a21\u5757 (God Classes/Modules)**\uff1a\u627f\u62c5\u4e86\u8fc7\u591a\u804c\u8d23\n- **\u91cd\u590d\u4ee3\u7801**\uff1a\u91cd\u590d\u7684\u6a21\u5f0f\n- **\u9b54\u672f\u6570\u5b57 (Magic Numbers)**\uff1a\u672a\u547d\u540d\u7684\u5e38\u91cf\n ```python\n # \u9519\u8bef\u505a\u6cd5\n if len(data) > 512:\n compress(data)\n\n # \u6b63\u786e\u505a\u6cd5\n MAX_UNCOMPRESSED_SIZE = 512\n\n if len(data) > MAX_UNCOMPRESSED_SIZE:\n compress(data)\n ```\n\n## \u5e76\u53d1 (\u9ad8\u4f18\u5148\u7ea7/HIGH)\n\n- **\u7f3a\u5931\u9501 (Lock)**\uff1a\u5171\u4eab\u72b6\u6001\u672a\u8fdb\u884c\u540c\u6b65\n ```python\n # \u9519\u8bef\u505a\u6cd5\n counter = 0\n\n def increment():\n global counter\n counter += 1 # \u7ade\u6001\u6761\u4ef6 (Race condition)!\n\n # \u6b63\u786e\u505a\u6cd5\n import threading\n\n counter = 0\n lock = threading.Lock()\n\n def increment():\n global counter\n with lock:\n counter += 1\n ```\n\n- **\u5168\u5c40\u89e3\u91ca\u5668\u9501 (GIL) \u5047\u8bbe**\uff1a\u76f2\u76ee\u5047\u8bbe\u7ebf\u7a0b\u5b89\u5168\n- **Async/Await \u6ee5\u7528**\uff1a\u9519\u8bef\u5730\u6df7\u5408\u540c\u6b65\u548c\u5f02\u6b65\u4ee3\u7801\n\n## \u6027\u80fd (\u4e2d\u4f18\u5148\u7ea7/MEDIUM)\n\n- **N+1 \u67e5\u8be2**\uff1a\u5728\u5faa\u73af\u4e2d\u8fdb\u884c\u6570\u636e\u5e93\u67e5\u8be2\n ```python\n # \u9519\u8bef\u505a\u6cd5\n for user in users:\n orders = get_orders(user.id) # N \u6b21\u67e5\u8be2!\n\n # \u6b63\u786e\u505a\u6cd5\n user_ids = [u.id for u in users]\n orders = get_orders_for_users(user_ids) # 1 \u6b21\u67e5\u8be2\n ```\n\n- **\u4f4e\u6548\u7684\u5b57\u7b26\u4e32\u64cd\u4f5c**\n ```python\n # \u9519\u8bef\u505a\u6cd5\n text = \"hello\"\n for i in range(1000):\n text += \" world\" # O(n\u00b2)\n\n # \u6b63\u786e\u505a\u6cd5\n parts = [\"hello\"]\n for i in range(1000):\n parts.append(\" world\")\n text = \"\".join(parts) # O(n)\n ```\n\n- **\u5e03\u5c14\u4e0a\u4e0b\u6587\u4e2d\u7684\u5217\u8868**\uff1a\u4f7f\u7528 len() \u800c\u975e\u771f\u503c\u6027\u68c0\u67e5\n ```python\n # \u9519\u8bef\u505a\u6cd5\n if len(items) > 0:\n process(items)\n\n # \u6b63\u786e\u505a\u6cd5\n if items:\n process(items)\n ```\n\n- **\u4e0d\u5fc5\u8981\u7684\u5217\u8868\u521b\u5efa**\uff1a\u5728\u4e0d\u9700\u8981\u65f6\u4f7f\u7528 list()\n ```python\n # \u9519\u8bef\u505a\u6cd5\n for item in list(dict.keys()):\n process(item)\n\n # \u6b63\u786e\u505a\u6cd5\n for item in dict:\n process(item)\n ```\n\n## \u6700\u4f73\u5b9e\u8df5 (\u4e2d\u4f18\u5148\u7ea7/MEDIUM)\n\n- **PEP 8 \u5408\u89c4\u6027**\uff1a\u4ee3\u7801\u683c\u5f0f\u8fdd\u89c4\n - \u5bfc\u5165\u987a\u5e8f\uff08\u6807\u51c6\u5e93\u3001\u7b2c\u4e09\u65b9\u5e93\u3001\u672c\u5730\u5e93\uff09\n - \u884c\u5bbd\uff08Black \u9ed8\u8ba4\u4e3a 88\uff0cPEP 8 \u4e3a 79\uff09\n - \u547d\u540d\u89c4\u8303\uff08\u51fd\u6570/\u53d8\u91cf\u4f7f\u7528 snake_case\uff0c\u7c7b\u4f7f\u7528 PascalCase\uff09\n - \u8fd0\u7b97\u7b26\u5468\u56f4\u7684\u7a7a\u683c\n\n- **\u6587\u6863\u5b57\u7b26\u4e32 (Docstrings)**\uff1a\u7f3a\u5931\u6216\u683c\u5f0f\u4e0d\u826f\u7684\u6587\u6863\u5b57\u7b26\u4e32\n ```python\n # \u9519\u8bef\u505a\u6cd5\n def process(data):\n return data.strip()\n\n # \u6b63\u786e\u505a\u6cd5\n def process(data: str) -> str:\n \"\"\"\u4ece\u8f93\u5165\u5b57\u7b26\u4e32\u4e2d\u79fb\u9664\u9996\u5c3e\u7a7a\u683c\u3002\n\n Args:\n data: \u8981\u5904\u7406\u7684\u8f93\u5165\u5b57\u7b26\u4e32\u3002\n\n Returns:\n \u79fb\u9664\u7a7a\u683c\u540e\u7684\u5904\u7406\u5b57\u7b26\u4e32\u3002\n \"\"\"\n return data.strip()\n ```\n\n- **\u65e5\u5fd7\u8bb0\u5f55 vs Print**\uff1a\u4f7f\u7528 print() \u8fdb\u884c\u65e5\u5fd7\u8bb0\u5f55\n ```python\n # \u9519\u8bef\u505a\u6cd5\n print(\"Error occurred\")\n\n # \u6b63\u786e\u505a\u6cd5\n import logging\n logger = logging.getLogger(__name__)\n logger.error(\"Error occurred\")\n ```\n\n- **\u76f8\u5bf9\u5bfc\u5165**\uff1a\u5728\u811a\u672c\u4e2d\u4f7f\u7528\u76f8\u5bf9\u5bfc\u5165\n- **\u672a\u4f7f\u7528\u7684\u5bfc\u5165**\uff1a\u6b7b\u4ee3\u7801 (Dead code)\n- **\u7f3a\u5931 `if __name__ == \"__main__\"`**\uff1a\u811a\u672c\u5165\u53e3\u70b9\u672a\u52a0\u4fdd\u62a4\n\n## Python \u7279\u6709\u7684\u53cd\u6a21\u5f0f (Anti-Patterns)\n\n- **`from module import *`**\uff1a\u547d\u540d\u7a7a\u95f4\u6c61\u67d3\n ```python\n # \u9519\u8bef\u505a\u6cd5\n from os.path import *\n\n # \u6b63\u786e\u505a\u6cd5\n from os.path import join, exists\n ```\n\n- **\u672a\u4f7f\u7528 `with` \u8bed\u53e5**\uff1a\u8d44\u6e90\u6cc4\u9732\n- **\u9759\u9ed8\u5f02\u5e38**\uff1a\u7a7a\u7684 `except: pass`\n- **\u4f7f\u7528 == \u4e0e None \u6bd4\u8f83**\n ```python\n # \u9519\u8bef\u505a\u6cd5\n if value == None:\n process()\n\n # \u6b63\u786e\u505a\u6cd5\n if value is None:\n process()\n ```\n\n- **\u672a\u4f7f\u7528 `isinstance` \u8fdb\u884c\u7c7b\u578b\u68c0\u67e5**\uff1a\u4f7f\u7528\u4e86 type()\n- **\u906e\u853d\u5185\u7f6e\u53d8\u91cf (Shadowing Built-ins)**\uff1a\u5c06\u53d8\u91cf\u547d\u540d\u4e3a `list`\u3001`dict`\u3001`str` \u7b49\n ```python\n # \u9519\u8bef\u505a\u6cd5\n list = [1, 2, 3] # \u906e\u853d\u4e86\u5185\u7f6e\u7684 list \u7c7b\u578b\n\n # \u6b63\u786e\u505a\u6cd5\n items = [1, 2, 3]\n ```\n\n## \u5ba1\u67e5\u8f93\u51fa\u683c\u5f0f\n\n\u9488\u5bf9\u6bcf\u4e2a\u95ee\u9898\uff1a\n```text\n[CRITICAL] SQL \u6ce8\u5165\u6f0f\u6d1e\n\u6587\u4ef6: app/routes/user.py:42\n\u95ee\u9898: \u7528\u6237\u8f93\u5165\u76f4\u63a5\u63d2\u5165\u5230\u4e86 SQL \u67e5\u8be2\u4e2d\n\u4fee\u590d\u5efa\u8bae: \u4f7f\u7528\u53c2\u6570\u5316\u67e5\u8be2\n\nquery = f\"SELECT * FROM users WHERE id = {user_id}\" # \u9519\u8bef\nquery = \"SELECT * FROM users WHERE id = %s\" # \u6b63\u786e\ncursor.execute(query, (user_id,))\n```\n\n## \u8bca\u65ad\u547d\u4ee4\n\n\u8fd0\u884c\u4ee5\u4e0b\u68c0\u67e5\uff1a\n```bash\n# \u7c7b\u578b\u68c0\u67e5\nmypy .\n\n# \u4ee3\u7801\u68c0\u67e5 (Linting)\nruff check .\npylint app/\n\n# \u683c\u5f0f\u68c0\u67e5\nblack --check .\nisort --check-only .\n\n# \u5b89\u5168\u626b\u63cf\nbandit -r .\n\n# \u4f9d\u8d56\u9879\u5ba1\u8ba1\npip-audit\nsafety check\n\n# \u6d4b\u8bd5\npytest --cov=app --cov-report=term-missing\n```\n\n## \u6279\u51c6\u6807\u51c6\n\n- **\u6279\u51c6 (Approve)**\uff1a\u65e0\u4e25\u91cd\uff08CRITICAL\uff09\u6216\u9ad8\uff08HIGH\uff09\u4f18\u5148\u7ea7\u95ee\u9898\n- **\u8b66\u544a (Warning)**\uff1a\u4ec5\u5b58\u5728\u4e2d\uff08MEDIUM\uff09\u4f18\u5148\u7ea7\u95ee\u9898\uff08\u53ef\u8c28\u614e\u5408\u5e76\uff09\n- **\u963b\u6b62 (Block)**\uff1a\u53d1\u73b0\u4e25\u91cd\uff08CRITICAL\uff09\u6216\u9ad8\uff08HIGH\uff09\u4f18\u5148\u7ea7\u95ee\u9898\n\n## Python \u7248\u672c\u6ce8\u610f\u4e8b\u9879\n\n- \u68c0\u67e5 `pyproject.toml` \u6216 `setup.py` \u4ee5\u786e\u8ba4 Python \u7248\u672c\u8981\u6c42\n- \u6ce8\u610f\u4ee3\u7801\u662f\u5426\u4f7f\u7528\u4e86\u8f83\u65b0 Python \u7248\u672c\u7684\u7279\u6027\uff08\u7c7b\u578b\u63d0\u793a | 3.5+\uff0cf-strings 3.6+\uff0cwalrus 3.8+\uff0cmatch 3.10+\uff09\n- \u6807\u8bb0\u5df2\u5f03\u7528\u7684\u6807\u51c6\u5e93\u6a21\u5757\n- \u786e\u4fdd\u7c7b\u578b\u63d0\u793a\u4e0e\u6700\u4f4e Python \u7248\u672c\u517c\u5bb9\n\n## \u6846\u67b6\u7279\u5b9a\u68c0\u67e5\n\n### Django\n- **N+1 \u67e5\u8be2**\uff1a\u4f7f\u7528 `select_related` \u548c `prefetch_related`\n- **\u7f3a\u5931\u8fc1\u79fb**\uff1a\u6a21\u578b\u53d8\u66f4\u4f46\u672a\u751f\u6210\u8fc1\u79fb\u6587\u4ef6\n- **\u539f\u751f SQL**\uff1a\u5728 ORM \u53ef\u884c\u7684\u60c5\u51b5\u4e0b\u4f7f\u7528\u4e86 `raw()` \u6216 `execute()`\n- **\u4e8b\u52a1\u7ba1\u7406**\uff1a\u591a\u6b65\u64cd\u4f5c\u7f3a\u5931 `atomic()`\n\n### FastAPI/Flask\n- **CORS \u914d\u7f6e\u9519\u8bef**\uff1a\u8de8\u57df\u9650\u5236\u8fc7\u4e8e\u5bbd\u677e\n- **\u4f9d\u8d56\u6ce8\u5165**\uff1a\u6b63\u786e\u4f7f\u7528 Depends/injection\n- **\u54cd\u5e94\u6a21\u578b**\uff1a\u7f3a\u5931\u6216\u9519\u8bef\u7684\u54cd\u5e94\u6a21\u578b\n- **\u9a8c\u8bc1**\uff1a\u4f7f\u7528 Pydantic \u6a21\u578b\u8fdb\u884c\u8bf7\u6c42\u9a8c\u8bc1\n\n### \u5f02\u6b65 (FastAPI/aiohttp)\n- **\u5f02\u6b65\u51fd\u6570\u4e2d\u7684\u963b\u585e\u8c03\u7528**\uff1a\u5728\u5f02\u6b65\u4e0a\u4e0b\u6587\u4e2d\u4f7f\u7528\u4e86\u540c\u6b65\u5e93\n- **\u7f3a\u5931 await**\uff1a\u5fd8\u8bb0 await \u534f\u7a0b\n- **\u5f02\u6b65\u751f\u6210\u5668**\uff1a\u6b63\u786e\u7684\u5f02\u6b65\u8fed\u4ee3\n\n\u5ba1\u67e5\u65f6\u8bf7\u601d\u8003\uff1a\u201c\u8fd9\u6bb5\u4ee3\u7801\u80fd\u901a\u8fc7\u9876\u7ea7 Python \u56e2\u961f\u6216\u5f00\u6e90\u9879\u76ee\u7684\u5ba1\u67e5\u5417\uff1f\u201d\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/commands/instinct-export.md": {
+ "md5": "17b1afc48ce7380005509993a87c7309",
+ "content": "---\nname: instinct-export\ndescription: \u5bfc\u51fa\u76f4\u89c9\uff08Instincts\uff09\u4ee5\u4fbf\u4e0e\u56e2\u961f\u6210\u5458\u6216\u5176\u4ed6\u9879\u76ee\u5171\u4eab\ncommand: /instinct-export\n---\n\n# \u76f4\u89c9\u5bfc\u51fa\uff08Instinct Export\uff09\u547d\u4ee4\n\n\u5c06\u76f4\u89c9\uff08Instincts\uff09\u5bfc\u51fa\u4e3a\u53ef\u5171\u4eab\u7684\u683c\u5f0f\u3002\u975e\u5e38\u9002\u7528\u4e8e\uff1a\n- \u4e0e\u56e2\u961f\u6210\u5458\u5171\u4eab\n- \u8fc1\u79fb\u5230\u65b0\u673a\u5668\n- \u4e3a\u9879\u76ee\u89c4\u8303\uff08Conventions\uff09\u8d21\u732e\u5185\u5bb9\n\n## \u7528\u6cd5\n\n```\n/instinct-export # \u5bfc\u51fa\u6240\u6709\u4e2a\u4eba\u76f4\u89c9\n/instinct-export --domain testing # \u4ec5\u5bfc\u51fa\u6d4b\u8bd5\u76f8\u5173\u7684\u76f4\u89c9\n/instinct-export --min-confidence 0.7 # \u4ec5\u5bfc\u51fa\u9ad8\u7f6e\u4fe1\u5ea6\u7684\u76f4\u89c9\n/instinct-export --output team-instincts.yaml\n```\n\n## \u6267\u884c\u6d41\u7a0b\n\n1. \u4ece `~/.claude/homunculus/instincts/personal/` \u8bfb\u53d6\u76f4\u89c9\n2. \u6839\u636e\u6807\u5fd7\u4f4d\uff08Flags\uff09\u8fdb\u884c\u8fc7\u6ee4\n3. \u8131\u654f\u5904\u7406\uff08\u5265\u79bb\u654f\u611f\u4fe1\u606f\uff09\uff1a\n - \u79fb\u9664\u4f1a\u8bdd ID\uff08Session IDs\uff09\n - \u79fb\u9664\u6587\u4ef6\u8def\u5f84\uff08\u4ec5\u4fdd\u7559\u6a21\u5f0f\u5339\u914d\u7b26 Pattern\uff09\n - \u79fb\u9664\u65e9\u4e8e\u201c\u4e0a\u5468\u201d\u7684\u65f6\u95f4\u6233\n4. \u751f\u6210\u5bfc\u51fa\u6587\u4ef6\n\n## \u8f93\u51fa\u683c\u5f0f\n\n\u521b\u5efa\u4e00\u4e2a YAML \u6587\u4ef6\uff1a\n\n```yaml\n# Instincts Export\n# Generated: 2025-01-22\n# Source: personal\n# Count: 12 instincts\n\nversion: \"2.0\"\nexported_by: \"continuous-learning-v2\"\nexport_date: \"2025-01-22T10:30:00Z\"\n\ninstincts:\n - id: prefer-functional-style\n trigger: \"when writing new functions\"\n action: \"Use functional patterns over classes\"\n confidence: 0.8\n domain: code-style\n observations: 8\n\n - id: test-first-workflow\n trigger: \"when adding new functionality\"\n action: \"Write test first, then implementation\"\n confidence: 0.9\n domain: testing\n observations: 12\n\n - id: grep-before-edit\n trigger: \"when modifying code\"\n action: \"Search with Grep, confirm with Read, then Edit\"\n confidence: 0.7\n domain: workflow\n observations: 6\n```\n\n## \u9690\u79c1\u8003\u91cf\n\n\u5bfc\u51fa\u5185\u5bb9\u5305\u62ec\uff1a\n- \u2705 \u89e6\u53d1\u6a21\u5f0f\uff08Trigger patterns\uff09\n- \u2705 \u52a8\u4f5c\uff08Actions\uff09\n- \u2705 \u7f6e\u4fe1\u5ea6\u5206\u6570\uff08Confidence scores\uff09\n- \u2705 \u57df\uff08Domains\uff09\n- \u2705 \u89c2\u5bdf\u6b21\u6570\uff08Observation counts\uff09\n\n\u5bfc\u51fa\u5185\u5bb9 **\u4e0d\u5305\u62ec**\uff1a\n- \u274c \u5b9e\u9645\u4ee3\u7801\u7247\u6bb5\n- \u274c \u6587\u4ef6\u8def\u5f84\n- \u274c \u4f1a\u8bdd\u8f6c\u5f55\u6587\u672c\n- \u274c \u4e2a\u4eba\u8eab\u4efd\u6807\u8bc6\u7b26\n\n## \u6807\u5fd7\u4f4d\uff08Flags\uff09\n\n- `--domain `: \u4ec5\u5bfc\u51fa\u6307\u5b9a\u7684\u57df\uff08Domain\uff09\n- `--min-confidence `: \u6700\u4f4e\u7f6e\u4fe1\u5ea6\u9608\u503c\uff08\u9ed8\u8ba4\u503c\uff1a0.3\uff09\n- `--output `: \u8f93\u51fa\u6587\u4ef6\u8def\u5f84\uff08\u9ed8\u8ba4\u503c\uff1ainstincts-export-YYYYMMDD.yaml\uff09\n- `--format `: \u8f93\u51fa\u683c\u5f0f\uff08\u9ed8\u8ba4\u503c\uff1ayaml\uff09\n- `--include-evidence`: \u5305\u542b\u8bc1\u636e\u6587\u672c\uff08\u9ed8\u8ba4\u503c\uff1a\u6392\u9664\uff09\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/commands/instinct-import.md": {
+ "md5": "fbe90485996baa813e09521ec8f7b6ba",
+ "content": "---\nname: instinct-import\ndescription: \u4ece\u56e2\u961f\u6210\u5458\u3001\u6280\u80fd\u521b\u5efa\u8005\uff08Skill Creator\uff09\u6216\u5176\u4ed6\u6765\u6e90\u5bfc\u5165\u76f4\u89c9\uff08Instincts\uff09\ncommand: true\n---\n\n# \u76f4\u89c9\u5bfc\u5165\u547d\u4ee4\uff08Instinct Import Command\uff09\n\n## \u5b9e\u73b0\n\n\u4f7f\u7528\u63d2\u4ef6\u6839\u8def\u5f84\u8fd0\u884c\u76f4\u89c9 CLI\uff1a\n\n```bash\npython3 \"${CLAUDE_PLUGIN_ROOT}/skills/continuous-learning-v2/scripts/instinct-cli.py\" import [--dry-run] [--force] [--min-confidence 0.7]\n```\n\n\u5982\u679c\u672a\u8bbe\u7f6e `CLAUDE_PLUGIN_ROOT`\uff08\u624b\u52a8\u5b89\u88c5\uff09\uff1a\n\n```bash\npython3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py import \n```\n\n\u53ef\u4ee5\u4ece\u4ee5\u4e0b\u6765\u6e90\u5bfc\u5165\u76f4\u89c9\uff1a\n- \u56e2\u961f\u6210\u5458\u7684\u5bfc\u51fa\u6587\u4ef6\n- \u6280\u80fd\u521b\u5efa\u8005\uff08Skill Creator\uff09\uff08\u4ed3\u5e93\u5206\u6790\uff09\n- \u793e\u533a\u96c6\u5408\n- \u4ee5\u524d\u7684\u673a\u5668\u5907\u4efd\n\n## \u7528\u6cd5\n\n```\n/instinct-import team-instincts.yaml\n/instinct-import https://github.com/org/repo/instincts.yaml\n/instinct-import --from-skill-creator acme/webapp\n```\n\n## \u6838\u5fc3\u6d41\u7a0b\n\n1. \u83b7\u53d6\u76f4\u89c9\u6587\u4ef6\uff08\u672c\u5730\u8def\u5f84\u6216 URL\uff09\n2. \u89e3\u6790\u5e76\u9a8c\u8bc1\u683c\u5f0f\n3. \u68c0\u67e5\u4e0e\u73b0\u6709\u76f4\u89c9\u662f\u5426\u91cd\u590d\n4. \u5408\u5e76\u6216\u6dfb\u52a0\u65b0\u76f4\u89c9\n5. \u4fdd\u5b58\u81f3 `~/.claude/homunculus/instincts/inherited/`\n\n## \u5bfc\u5165\u8fc7\u7a0b\u793a\u4f8b\n\n```\n\ud83d\udce5 \u6b63\u5728\u4ece\u4ee5\u4e0b\u8def\u5f84\u5bfc\u5165\u76f4\u89c9\uff1ateam-instincts.yaml\n================================================\n\n\u53d1\u73b0 12 \u6761\u5f85\u5bfc\u5165\u7684\u76f4\u89c9\u3002\n\n\u6b63\u5728\u5206\u6790\u51b2\u7a81...\n\n## \u65b0\u76f4\u89c9 (8)\n\u4ee5\u4e0b\u5185\u5bb9\u5c06\u88ab\u6dfb\u52a0\uff1a\n \u2713 use-zod-validation (\u7f6e\u4fe1\u5ea6: 0.7)\n \u2713 prefer-named-exports (\u7f6e\u4fe1\u5ea6: 0.65)\n \u2713 test-async-functions (\u7f6e\u4fe1\u5ea6: 0.8)\n ...\n\n## \u91cd\u590d\u76f4\u89c9 (3)\n\u5df2\u5b58\u5728\u7c7b\u4f3c\u7684\u76f4\u89c9\uff1a\n \u26a0\ufe0f prefer-functional-style\n \u672c\u5730: 0.8 \u7f6e\u4fe1\u5ea6, 12 \u6b21\u89c2\u5bdf\n \u5bfc\u5165: 0.7 \u7f6e\u4fe1\u5ea6\n \u2192 \u4fdd\u7559\u672c\u5730\u7248\u672c\uff08\u7f6e\u4fe1\u5ea6\u66f4\u9ad8\uff09\n\n \u26a0\ufe0f test-first-workflow\n \u672c\u5730: 0.75 \u7f6e\u4fe1\u5ea6\n \u5bfc\u5165: 0.9 \u7f6e\u4fe1\u5ea6\n \u2192 \u66f4\u65b0\u4e3a\u5bfc\u5165\u7248\u672c\uff08\u7f6e\u4fe1\u5ea6\u66f4\u9ad8\uff09\n\n## \u51b2\u7a81\u76f4\u89c9 (1)\n\u8fd9\u4e9b\u76f4\u89c9\u4e0e\u672c\u5730\u76f4\u89c9\u51b2\u7a81\uff1a\n \u274c use-classes-for-services\n \u51b2\u7a81\u9879\uff1aavoid-classes\n \u2192 \u8df3\u8fc7\uff08\u9700\u8981\u624b\u52a8\u89e3\u51b3\uff09\n\n---\n\u5bfc\u5165 8 \u6761\u65b0\u76f4\u89c9\uff0c\u66f4\u65b0 1 \u6761\uff0c\u8df3\u8fc7 3 \u6761\uff1f\n```\n\n## \u5408\u5e76\u7b56\u7565\uff08Merge Strategies\uff09\n\n### \u5904\u7406\u91cd\u590d\u9879\n\u5f53\u5bfc\u5165\u7684\u76f4\u89c9\u4e0e\u73b0\u6709\u76f4\u89c9\u5339\u914d\u65f6\uff1a\n- **\u9ad8\u7f6e\u4fe1\u5ea6\u4f18\u5148**\uff1a\u4fdd\u7559\u7f6e\u4fe1\u5ea6\u8f83\u9ad8\u7684\u7248\u672c\n- **\u5408\u5e76\u8bc1\u636e**\uff1a\u5408\u5e76\u89c2\u5bdf\u8ba1\u6570\uff08observation counts\uff09\n- **\u66f4\u65b0\u65f6\u95f4\u6233**\uff1a\u6807\u8bb0\u4e3a\u6700\u8fd1\u5df2\u9a8c\u8bc1\n\n### \u5904\u7406\u51b2\u7a81\n\u5f53\u5bfc\u5165\u7684\u76f4\u89c9\u4e0e\u73b0\u6709\u76f4\u89c9\u76f8\u77db\u76fe\u65f6\uff1a\n- **\u9ed8\u8ba4\u8df3\u8fc7**\uff1a\u4e0d\u5bfc\u5165\u51b2\u7a81\u7684\u76f4\u89c9\n- **\u6807\u8bb0\u5f85\u8bc4\u5ba1**\uff1a\u5c06\u4e24\u8005\u90fd\u6807\u8bb0\u4e3a\u9700\u8981\u5173\u6ce8\n- **\u624b\u52a8\u89e3\u51b3**\uff1a\u7531\u7528\u6237\u51b3\u5b9a\u4fdd\u7559\u54ea\u4e00\u4e2a\n\n## \u6765\u6e90\u8ffd\u8e2a\n\n\u5bfc\u5165\u7684\u76f4\u89c9\u4f1a\u5e26\u6709\u4ee5\u4e0b\u6807\u8bb0\uff1a\n```yaml\nsource: \"inherited\"\nimported_from: \"team-instincts.yaml\"\nimported_at: \"2025-01-22T10:30:00Z\"\noriginal_source: \"session-observation\" # \u6216 \"repo-analysis\"\n```\n\n## \u6280\u80fd\u521b\u5efa\u8005\uff08Skill Creator\uff09\u96c6\u6210\n\n\u4ece\u6280\u80fd\u521b\u5efa\u8005\u5bfc\u5165\u65f6\uff1a\n\n```\n/instinct-import --from-skill-creator acme/webapp\n```\n\n\u8fd9\u5c06\u83b7\u53d6\u901a\u8fc7\u4ed3\u5e93\u5206\u6790\u751f\u6210\u7684\u76f4\u89c9\uff1a\n- \u6765\u6e90\uff1a`repo-analysis`\n- \u521d\u59cb\u7f6e\u4fe1\u5ea6\u8f83\u9ad8 (0.7+)\n- \u94fe\u63a5\u5230\u6e90\u4ed3\u5e93\n\n## \u53c2\u6570\u9009\u9879\uff08Flags\uff09\n\n- `--dry-run`\uff1a\u9884\u89c8\u800c\u4e0d\u6267\u884c\u5bfc\u5165\n- `--force`\uff1a\u5373\u4f7f\u5b58\u5728\u51b2\u7a81\u4e5f\u6267\u884c\u5bfc\u5165\n- `--merge-strategy `\uff1a\u5982\u4f55\u5904\u7406\u91cd\u590d\u9879\n- `--from-skill-creator `\uff1a\u4ece\u6280\u80fd\u521b\u5efa\u8005\u5206\u6790\u4e2d\u5bfc\u5165\n- `--min-confidence `\uff1a\u4ec5\u5bfc\u5165\u9ad8\u4e8e\u9608\u503c\u7684\u76f4\u89c9\n\n## \u8f93\u51fa\n\n\u5bfc\u5165\u5b8c\u6210\u540e\uff1a\n```\n\u2705 \u5bfc\u5165\u5b8c\u6210\uff01\n\n\u6dfb\u52a0\uff1a8 \u6761\u76f4\u89c9\n\u66f4\u65b0\uff1a1 \u6761\u76f4\u89c9\n\u8df3\u8fc7\uff1a3 \u6761\u76f4\u89c9 (2 \u6761\u91cd\u590d\uff0c1 \u6761\u51b2\u7a81)\n\n\u65b0\u76f4\u89c9\u5df2\u4fdd\u5b58\u81f3\uff1a~/.claude/homunculus/instincts/inherited/\n\n\u8fd0\u884c /instinct-status \u4ee5\u67e5\u770b\u6240\u6709\u76f4\u89c9\u3002\n```\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/commands/instinct-status.md": {
+ "md5": "dac216bb6e1e3705bfbf0f39b88d6049",
+ "content": "---\nname: instinct-status\ndescription: \u663e\u793a\u6240\u6709\u5df2\u5b66\u4e60\u7684\u672c\u80fd\u53ca\u5176\u7f6e\u4fe1\u5ea6\ncommand: true\n---\n\n# \u672c\u80fd\u72b6\u6001\u67e5\u8be2\u547d\u4ee4\uff08Instinct Status Command\uff09\n\n\u663e\u793a\u6240\u6709\u5df2\u5b66\u4e60\u7684\u672c\u80fd\uff08Instincts\uff09\u53ca\u5176\u7f6e\u4fe1\u5ea6\u5206\u6570\uff0c\u5e76\u6309\u9886\u57df\uff08Domain\uff09\u8fdb\u884c\u5206\u7ec4\u3002\n\n## \u5b9e\u73b0\u65b9\u5f0f\n\n\u4f7f\u7528\u63d2\u4ef6\u6839\u8def\u5f84\u8fd0\u884c\u672c\u80fd CLI\uff1a\n\n```bash\npython3 \"${CLAUDE_PLUGIN_ROOT}/skills/continuous-learning-v2/scripts/instinct-cli.py\" status\n```\n\n\u5982\u679c\u672a\u8bbe\u7f6e `CLAUDE_PLUGIN_ROOT`\uff08\u624b\u52a8\u5b89\u88c5\uff09\uff0c\u8bf7\u4f7f\u7528\uff1a\n\n```bash\npython3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py status\n```\n\n## \u7528\u6cd5\n\n```\n/instinct-status\n/instinct-status --domain code-style\n/instinct-status --low-confidence\n```\n\n## \u6267\u884c\u903b\u8f91\n\n1. \u4ece `~/.claude/homunculus/instincts/personal/` \u8bfb\u53d6\u6240\u6709\u4e2a\u4eba\u672c\u80fd\u6587\u4ef6\u3002\n2. \u4ece `~/.claude/homunculus/instincts/inherited/` \u8bfb\u53d6\u7ee7\u627f\u7684\u672c\u80fd\u3002\n3. \u6309\u9886\u57df\u5206\u7ec4\u663e\u793a\uff0c\u5e76\u9644\u5e26\u7f6e\u4fe1\u5ea6\u8fdb\u5ea6\u6761\u3002\n\n## \u8f93\u51fa\u683c\u5f0f\n\n```\n\ud83d\udcca \u672c\u80fd\u72b6\u6001 (Instinct Status)\n==================\n\n## \u4ee3\u7801\u98ce\u683c (Code Style) (4 \u6761\u672c\u80fd)\n\n### prefer-functional-style\n\u89e6\u53d1\u5668 (Trigger)\uff1a\u7f16\u5199\u65b0\u51fd\u6570\u65f6\n\u52a8\u4f5c (Action)\uff1a\u4f18\u5148\u4f7f\u7528\u51fd\u6570\u5f0f\u6a21\u5f0f\u800c\u975e\u7c7b\n\u7f6e\u4fe1\u5ea6 (Confidence)\uff1a\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2591 80%\n\u6765\u6e90 (Source)\uff1a\u4f1a\u8bdd\u89c2\u5bdf (session-observation) | \u6700\u8fd1\u66f4\u65b0\uff1a2025-01-22\n\n### use-path-aliases\n\u89e6\u53d1\u5668 (Trigger)\uff1a\u5bfc\u5165\u6a21\u5757\u65f6\n\u52a8\u4f5c (Action)\uff1a\u4f7f\u7528 @/ \u8def\u5f84\u522b\u540d\u800c\u975e\u76f8\u5bf9\u5bfc\u5165\n\u7f6e\u4fe1\u5ea6 (Confidence)\uff1a\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2591\u2591\u2591 60%\n\u6765\u6e90 (Source)\uff1a\u4ed3\u5e93\u5206\u6790 (repo-analysis) (github.com/acme/webapp)\n\n## \u6d4b\u8bd5 (Testing) (2 \u6761\u672c\u80fd)\n\n### test-first-workflow\n\u89e6\u53d1\u5668 (Trigger)\uff1a\u6dfb\u52a0\u65b0\u529f\u80fd\u65f6\n\u52a8\u4f5c (Action)\uff1a\u5148\u5199\u6d4b\u8bd5\uff0c\u518d\u5199\u5b9e\u73b0\n\u7f6e\u4fe1\u5ea6 (Confidence)\uff1a\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2591 90%\n\u6765\u6e90 (Source)\uff1a\u4f1a\u8bdd\u89c2\u5bdf (session-observation)\n\n## \u5de5\u4f5c\u6d41 (Workflow) (3 \u6761\u672c\u80fd)\n\n### grep-before-edit\n\u89e6\u53d1\u5668 (Trigger)\uff1a\u4fee\u6539\u4ee3\u7801\u65f6\n\u52a8\u4f5c (Action)\uff1a\u5148\u7528 Grep \u641c\u7d22\uff0c\u518d\u7528 Read \u786e\u8ba4\uff0c\u6700\u540e Edit \u7f16\u8f91\n\u7f6e\u4fe1\u5ea6 (Confidence)\uff1a\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2591\u2591 70%\n\u6765\u6e90 (Source)\uff1a\u4f1a\u8bdd\u89c2\u5bdf (session-observation)\n\n---\n\u603b\u8ba1\uff1a9 \u6761\u672c\u80fd (4 \u6761\u4e2a\u4eba, 5 \u6761\u7ee7\u627f)\n\u89c2\u5bdf\u5668 (Observer)\uff1a\u8fd0\u884c\u4e2d (\u6700\u8fd1\u5206\u6790\uff1a5 \u5206\u949f\u524d)\n```\n\n## \u53c2\u6570 (Flags)\n\n- `--domain `: \u6309\u9886\u57df\u7b5b\u9009\uff08\u5982 code-style, testing, git \u7b49\uff09\n- `--low-confidence`: \u4ec5\u663e\u793a\u7f6e\u4fe1\u5ea6 < 0.5 \u7684\u672c\u80fd\n- `--high-confidence`: \u4ec5\u663e\u793a\u7f6e\u4fe1\u5ea6 >= 0.7 \u7684\u672c\u80fd\n- `--source `: \u6309\u6765\u6e90\u7b5b\u9009\uff08session-observation, repo-analysis, inherited\uff09\n- `--json`: \u4ee5 JSON \u683c\u5f0f\u8f93\u51fa\uff0c\u4fbf\u4e8e\u7a0b\u5e8f\u5316\u8c03\u7528\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/commands/evolve.md": {
+ "md5": "f017b3e4e55aa805913fe81ade6ae2cf",
+ "content": "---\nname: evolve\ndescription: \u5c06\u76f8\u5173\u7684\u76f4\u89c9\uff08Instincts\uff09\u805a\u7c7b\u4e3a\u6280\u80fd\uff08Skills\uff09\u3001\u547d\u4ee4\uff08Commands\uff09\u6216\u667a\u80fd\u4f53\uff08Agents\uff09\ncommand: true\n---\n\n# Evolve \u547d\u4ee4\n\n## \u5b9e\u73b0 (Implementation)\n\n\u4f7f\u7528\u63d2\u4ef6\u6839\u8def\u5f84\u8fd0\u884c\u76f4\u89c9\uff08Instinct\uff09\u547d\u4ee4\u884c\u754c\u9762\uff08CLI\uff09\uff1a\n\n```bash\npython3 \"${CLAUDE_PLUGIN_ROOT}/skills/continuous-learning-v2/scripts/instinct-cli.py\" evolve [--generate]\n```\n\n\u6216\u8005\u5982\u679c\u672a\u8bbe\u7f6e `CLAUDE_PLUGIN_ROOT`\uff08\u624b\u52a8\u5b89\u88c5\uff09\uff1a\n\n```bash\npython3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py evolve [--generate]\n```\n\n\u5206\u6790\u76f4\u89c9\uff08Instincts\uff09\u5e76\u5c06\u76f8\u5173\u7684\u76f4\u89c9\u805a\u7c7b\u4e3a\u66f4\u9ad8\u7ea7\u7684\u7ed3\u6784\uff1a\n- **\u547d\u4ee4\uff08Commands\uff09**\uff1a\u5f53\u76f4\u89c9\u63cf\u8ff0\u7528\u6237\u8c03\u7528\u7684\u64cd\u4f5c\u65f6\n- **\u6280\u80fd\uff08Skills\uff09**\uff1a\u5f53\u76f4\u89c9\u63cf\u8ff0\u81ea\u52a8\u89e6\u53d1\u7684\u884c\u4e3a\u65f6\n- **\u667a\u80fd\u4f53\uff08Agents\uff09**\uff1a\u5f53\u76f4\u89c9\u63cf\u8ff0\u590d\u6742\u7684\u3001\u591a\u6b65\u9aa4\u7684\u6d41\u7a0b\u65f6\n\n## \u7528\u6cd5 (Usage)\n\n```\n/evolve # \u5206\u6790\u6240\u6709\u76f4\u89c9\u5e76\u5efa\u8bae\u6f14\u8fdb\u65b9\u6848\n/evolve --domain testing # \u4ec5\u6f14\u8fdb\u6d4b\u8bd5\u9886\u57df\uff08testing domain\uff09\u4e2d\u7684\u76f4\u89c9\n/evolve --dry-run # \u663e\u793a\u5c06\u8981\u521b\u5efa\u7684\u5185\u5bb9\u800c\u4e0d\u5b9e\u9645\u521b\u5efa\n/evolve --threshold 5 # \u8981\u6c42\u81f3\u5c11\u6709 5 \u4e2a\u4ee5\u4e0a\u7684\u76f8\u5173\u76f4\u89c9\u624d\u8fdb\u884c\u805a\u7c7b\n```\n\n## \u6f14\u8fdb\u89c4\u5219 (Evolution Rules)\n\n### \u2192 \u547d\u4ee4 (Command)\uff08\u7528\u6237\u8c03\u7528\uff09\n\u5f53\u76f4\u89c9\u63cf\u8ff0\u7528\u6237\u4f1a\u660e\u786e\u8bf7\u6c42\u7684\u64cd\u4f5c\u65f6\uff1a\n- \u591a\u4e2a\u5173\u4e8e\u201c\u5f53\u7528\u6237\u8981\u6c42...\u201d\u7684\u76f4\u89c9\n- \u5e26\u6709\u201c\u5f53\u521b\u5efa\u65b0\u7684 X \u65f6\u201d\u7b49\u89e6\u53d1\u5668\u7684\u76f4\u89c9\n- \u9075\u5faa\u53ef\u91cd\u590d\u5e8f\u5217\u7684\u76f4\u89c9\n\n\u793a\u4f8b\uff1a\n- `new-table-step1`: \"when adding a database table, create migration\"\n- `new-table-step2`: \"when adding a database table, update schema\"\n- `new-table-step3`: \"when adding a database table, regenerate types\"\n\n\u2192 \u521b\u5efa\uff1a`/new-table` \u547d\u4ee4\n\n### \u2192 \u6280\u80fd (Skill)\uff08\u81ea\u52a8\u89e6\u53d1\uff09\n\u5f53\u76f4\u89c9\u63cf\u8ff0\u5e94\u8be5\u81ea\u52a8\u53d1\u751f\u7684\u884c\u4e3a\u65f6\uff1a\n- \u6a21\u5f0f\u5339\u914d\u89e6\u53d1\u5668\n- \u9519\u8bef\u5904\u7406\u54cd\u5e94\n- \u4ee3\u7801\u98ce\u683c\u5f3a\u5236\u6267\u884c\n\n\u793a\u4f8b\uff1a\n- `prefer-functional`: \"when writing functions, prefer functional style\"\n- `use-immutable`: \"when modifying state, use immutable patterns\"\n- `avoid-classes`: \"when designing modules, avoid class-based design\"\n\n\u2192 \u521b\u5efa\uff1a`functional-patterns` \u6280\u80fd\uff08Skill\uff09\n\n### \u2192 \u667a\u80fd\u4f53 (Agent)\uff08\u9700\u8981\u6df1\u5ea6/\u9694\u79bb\uff09\n\u5f53\u76f4\u89c9\u63cf\u8ff0\u590d\u6742\u7684\u3001\u591a\u6b65\u9aa4\u7684\u6d41\u7a0b\uff0c\u4e14\u53d7\u76ca\u4e8e\u9694\u79bb\u73af\u5883\u65f6\uff1a\n- \u8c03\u8bd5\u5de5\u4f5c\u6d41\uff08Workflow\uff09\n- \u91cd\u6784\u5e8f\u5217\n- \u7814\u7a76\u4efb\u52a1\n\n\u793a\u4f8b\uff1a\n- `debug-step1`: \"when debugging, first check logs\"\n- `debug-step2`: \"when debugging, isolate the failing component\"\n- `debug-step3`: \"when debugging, create minimal reproduction\"\n- `debug-step4`: \"when debugging, verify fix with test\"\n\n\u2192 \u521b\u5efa\uff1a`debugger` \u667a\u80fd\u4f53\uff08Agent\uff09\n\n## \u64cd\u4f5c\u6b65\u9aa4 (What to Do)\n\n1. \u4ece `~/.claude/homunculus/instincts/` \u8bfb\u53d6\u6240\u6709\u76f4\u89c9\uff08Instincts\uff09\n2. \u6309\u4ee5\u4e0b\u7ef4\u5ea6\u5bf9\u76f4\u89c9\u8fdb\u884c\u5206\u7ec4\uff1a\n - \u9886\u57df\uff08Domain\uff09\u76f8\u4f3c\u6027\n - \u89e6\u53d1\u6a21\u5f0f\u91cd\u5408\u5ea6\n - \u64cd\u4f5c\u5e8f\u5217\u5173\u8054\u6027\n3. \u5bf9\u4e8e\u6bcf\u4e2a\u5305\u542b 3 \u4e2a\u53ca\u4ee5\u4e0a\u76f8\u5173\u76f4\u89c9\u7684\u805a\u7c7b\uff1a\n - \u786e\u5b9a\u6f14\u8fdb\u7c7b\u578b\uff08\u547d\u4ee4/\u6280\u80fd/\u667a\u80fd\u4f53\uff09\n - \u751f\u6210\u76f8\u5e94\u7684\u6587\u4ef6\n - \u4fdd\u5b58\u81f3 `~/.claude/homunculus/evolved/{commands,skills,agents}/`\n4. \u5c06\u6f14\u8fdb\u540e\u7684\u7ed3\u6784\u94fe\u63a5\u56de\u539f\u59cb\u76f4\u89c9\n\n## \u8f93\u51fa\u683c\u5f0f (Output Format)\n\n```\n\ud83e\uddec \u6f14\u8fdb\u5206\u6790 (Evolve Analysis)\n==================\n\n\u53d1\u73b0 3 \u4e2a\u5df2\u51c6\u5907\u597d\u6f14\u8fdb\u7684\u805a\u7c7b\uff1a\n\n## \u805a\u7c7b 1\uff1a\u6570\u636e\u5e93\u8fc1\u79fb\u5de5\u4f5c\u6d41 (Database Migration Workflow)\n\u76f4\u89c9 (Instincts): new-table-migration, update-schema, regenerate-types\n\u7c7b\u578b: \u547d\u4ee4 (Command)\n\u7f6e\u4fe1\u5ea6: 85% (\u57fa\u4e8e 12 \u6b21\u89c2\u5bdf)\n\n\u5c06\u521b\u5efa: /new-table \u547d\u4ee4\n\u6587\u4ef6:\n - ~/.claude/homunculus/evolved/commands/new-table.md\n\n## \u805a\u7c7b 2\uff1a\u51fd\u6570\u5f0f\u4ee3\u7801\u98ce\u683c (Functional Code Style)\n\u76f4\u89c9 (Instincts): prefer-functional, use-immutable, avoid-classes, pure-functions\n\u7c7b\u578b: \u6280\u80fd (Skill)\n\u7f6e\u4fe1\u5ea6: 78% (\u57fa\u4e8e 8 \u6b21\u89c2\u5bdf)\n\n\u5c06\u521b\u5efa: functional-patterns \u6280\u80fd (Skill)\n\u6587\u4ef6:\n - ~/.claude/homunculus/evolved/skills/functional-patterns.md\n\n## \u805a\u7c7b 3\uff1a\u8c03\u8bd5\u6d41\u7a0b (Debugging Process)\n\u76f4\u89c9 (Instincts): debug-check-logs, debug-isolate, debug-reproduce, debug-verify\n\u7c7b\u578b: \u667a\u80fd\u4f53 (Agent)\n\u7f6e\u4fe1\u5ea6: 72% (\u57fa\u4e8e 6 \u6b21\u89c2\u5bdf)\n\n\u5c06\u521b\u5efa: debugger \u667a\u80fd\u4f53 (Agent)\n\u6587\u4ef6:\n - ~/.claude/homunculus/evolved/agents/debugger.md\n\n---\n\u8fd0\u884c `/evolve --execute` \u6765\u521b\u5efa\u8fd9\u4e9b\u6587\u4ef6\u3002\n```\n\n## \u53c2\u6570\u6807\u5fd7 (Flags)\n\n- `--execute`: \u5b9e\u9645\u521b\u5efa\u6f14\u8fdb\u540e\u7684\u7ed3\u6784\uff08\u9ed8\u8ba4\u4e3a\u9884\u89c8\uff09\n- `--dry-run`: \u9884\u89c8\u800c\u4e0d\u521b\u5efa\n- `--domain `: \u4ec5\u6f14\u8fdb\u6307\u5b9a\u9886\u57df\uff08Domain\uff09\u4e2d\u7684\u76f4\u89c9\n- `--threshold `: \u5f62\u6210\u805a\u7c7b\u6240\u9700\u7684\u6700\u5c0f\u76f4\u89c9\u6570\u91cf\uff08\u9ed8\u8ba4\u503c\uff1a3\uff09\n- `--type `: \u4ec5\u521b\u5efa\u6307\u5b9a\u7c7b\u578b\n\n## \u751f\u6210\u7684\u6587\u4ef6\u683c\u5f0f (Generated File Format)\n\n### \u547d\u4ee4 (Command)\n```markdown\n---\nname: new-table\ndescription: Create a new database table with migration, schema update, and type generation\ncommand: /new-table\nevolved_from:\n - new-table-migration\n - update-schema\n - regenerate-types\n---\n\n# New Table \u547d\u4ee4\n\n[\u57fa\u4e8e\u805a\u7c7b\u76f4\u89c9\u751f\u6210\u7684\u6b63\u6587\u5185\u5bb9]\n\n## \u6b65\u9aa4\n1. ...\n2. ...\n```\n\n### \u6280\u80fd (Skill)\n```markdown\n---\nname: functional-patterns\ndescription: Enforce functional programming patterns\nevolved_from:\n - prefer-functional\n - use-immutable\n - avoid-classes\n---\n\n# Functional Patterns \u6280\u80fd (Skill)\n\n[\u57fa\u4e8e\u805a\u7c7b\u76f4\u89c9\u751f\u6210\u7684\u6b63\u6587\u5185\u5bb9]\n```\n\n### \u667a\u80fd\u4f53 (Agent)\n```markdown\n---\nname: debugger\ndescription: Systematic debugging agent\nmodel: sonnet\nevolved_from:\n - debug-check-logs\n - debug-isolate\n - debug-reproduce\n---\n\n# Debugger \u667a\u80fd\u4f53 (Agent)\n\n[\u57fa\u4e8e\u805a\u7c7b\u76f4\u89c9\u751f\u6210\u7684\u6b63\u6587\u5185\u5bb9]\n```\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/commands/python-review.md": {
+ "md5": "3ea7910663111fb2e8e3e99936261207",
+ "content": "---\ndescription: \u9488\u5bf9 PEP 8 \u6807\u51c6\u3001\u7c7b\u578b\u63d0\u793a\u3001\u5b89\u5168\u6027\u53ca Pythonic \u60ef\u7528\u5199\u6cd5\u7684 Python \u4ee3\u7801\u5168\u9762\u5ba1\u67e5\u3002\u8c03\u7528 python-reviewer \u667a\u80fd\u4f53\uff08Agent\uff09\u3002\n---\n\n# Python \u4ee3\u7801\u5ba1\u67e5 (Python Code Review)\n\n\u6b64\u547d\u4ee4\u8c03\u7528 **python-reviewer** \u667a\u80fd\u4f53\uff08Agent\uff09\uff0c\u8fdb\u884c\u5168\u9762\u7684 Python \u4e13\u9879\u4ee3\u7801\u5ba1\u67e5\u3002\n\n## \u6b64\u547d\u4ee4\u7684\u4f5c\u7528\n\n1. **\u8bc6\u522b Python \u53d8\u66f4**\uff1a\u901a\u8fc7 `git diff` \u67e5\u627e\u4fee\u6539\u8fc7\u7684 `.py` \u6587\u4ef6\n2. **\u8fd0\u884c\u9759\u6001\u5206\u6790**\uff1a\u6267\u884c `ruff`\u3001`mypy`\u3001`pylint`\u3001`black --check`\n3. **\u5b89\u5168\u626b\u63cf**\uff1a\u68c0\u67e5 SQL \u6ce8\u5165\u3001\u547d\u4ee4\u6ce8\u5165\u3001\u4e0d\u5b89\u5168\u7684\u53cd\u5e8f\u5217\u5316\n4. **\u7c7b\u578b\u5b89\u5168\u5ba1\u67e5**\uff1a\u5206\u6790\u7c7b\u578b\u63d0\u793a\uff08Type Hints\uff09\u548c mypy \u9519\u8bef\n5. **Pythonic \u4ee3\u7801\u68c0\u67e5**\uff1a\u9a8c\u8bc1\u4ee3\u7801\u662f\u5426\u7b26\u5408 PEP 8 \u548c Python \u6700\u4f73\u5b9e\u8df5\n6. **\u751f\u6210\u62a5\u544a**\uff1a\u6309\u4e25\u91cd\u7a0b\u5ea6\uff08Severity\uff09\u5bf9\u95ee\u9898\u8fdb\u884c\u5206\u7c7b\n\n## \u9002\u7528\u573a\u666f\n\n\u5728\u4ee5\u4e0b\u60c5\u51b5\u4e0b\u4f7f\u7528 `/python-review`\uff1a\n- \u7f16\u5199\u6216\u4fee\u6539 Python \u4ee3\u7801\u540e\n- \u63d0\u4ea4 Python \u53d8\u66f4\u524d\n- \u5ba1\u67e5\u5305\u542b Python \u4ee3\u7801\u7684\u62c9\u53d6\u8bf7\u6c42\uff08Pull Requests\uff09\n- \u63a5\u5165\u65b0\u7684 Python \u4ee3\u7801\u5e93\u65f6\n- \u5b66\u4e60 Pythonic \u6a21\u5f0f\u548c\u60ef\u7528\u6cd5\u65f6\n\n## \u5ba1\u67e5\u7c7b\u522b\n\n### \u4e25\u91cd (CRITICAL) (\u5fc5\u987b\u4fee\u590d)\n- SQL/\u547d\u4ee4\u6ce8\u5165\u6f0f\u6d1e\n- \u4e0d\u5b89\u5168\u7684 eval/exec \u4f7f\u7528\n- Pickle \u4e0d\u5b89\u5168\u7684\u53cd\u5e8f\u5217\u5316\n- \u786c\u7f16\u7801\u51ed\u636e\n- YAML \u4e0d\u5b89\u5168\u7684\u52a0\u8f7d (unsafe load)\n- \u9690\u85cf\u9519\u8bef\u7684\u88f8 except \u5b50\u53e5\n\n### \u9ad8 (HIGH) (\u5e94\u8be5\u4fee\u590d)\n- \u516c\u5171\u51fd\u6570\u7f3a\u5931\u7c7b\u578b\u63d0\u793a\n- \u53ef\u53d8\u9ed8\u8ba4\u53c2\u6570 (Mutable default arguments)\n- \u9759\u9ed8\u541e\u6389\u5f02\u5e38\n- \u672a\u5bf9\u8d44\u6e90\u4f7f\u7528\u4e0a\u4e0b\u6587\u7ba1\u7406\u5668 (Context Managers)\n- \u4f7f\u7528 C \u98ce\u683c\u5faa\u73af\u800c\u975e\u63a8\u5bfc\u5f0f (Comprehensions)\n- \u4f7f\u7528 type() \u800c\u975e isinstance()\n- \u65e0\u9501\u72b6\u6001\u4e0b\u7684\u7ade\u6001\u6761\u4ef6\n\n### \u4e2d (MEDIUM) (\u5efa\u8bae\u8003\u8651)\n- \u8fdd\u53cd PEP 8 \u683c\u5f0f\u89c4\u8303\n- \u516c\u5171\u51fd\u6570\u7f3a\u5931\u6587\u6863\u5b57\u7b26\u4e32 (Docstrings)\n- \u4f7f\u7528 print \u8bed\u53e5\u800c\u975e\u65e5\u5fd7 (Logging)\n- \u4f4e\u6548\u7684\u5b57\u7b26\u4e32\u64cd\u4f5c\n- \u65e0\u547d\u540d\u5e38\u91cf\u7684\u9b54\u672f\u6570\u5b57 (Magic numbers)\n- \u672a\u4f7f\u7528 f-strings \u8fdb\u884c\u683c\u5f0f\u5316\n- \u4e0d\u5fc5\u8981\u7684\u5217\u8868\u521b\u5efa\n\n## \u8fd0\u884c\u7684\u81ea\u52a8\u5316\u68c0\u67e5\n\n```bash\n# \u7c7b\u578b\u68c0\u67e5\nmypy .\n\n# \u4ee3\u7801\u68c0\u67e5\u4e0e\u683c\u5f0f\u5316\nruff check .\nblack --check .\nisort --check-only .\n\n# \u5b89\u5168\u626b\u63cf\nbandit -r .\n\n# \u4f9d\u8d56\u5ba1\u8ba1\npip-audit\nsafety check\n\n# \u6d4b\u8bd5\npytest --cov=app --cov-report=term-missing\n```\n\n## \u793a\u4f8b\u7528\u6cd5\n\n```text\nUser: /python-review\n\nAgent:\n# Python \u4ee3\u7801\u5ba1\u67e5\u62a5\u544a\n\n## \u5df2\u5ba1\u67e5\u6587\u4ef6\n- app/routes/user.py (\u5df2\u4fee\u6539)\n- app/services/auth.py (\u5df2\u4fee\u6539)\n\n## \u9759\u6001\u5206\u6790\u7ed3\u679c\n\u2713 ruff: \u65e0\u95ee\u9898\n\u2713 mypy: \u65e0\u9519\u8bef\n\u26a0\ufe0f black: 2 \u4e2a\u6587\u4ef6\u9700\u8981\u91cd\u65b0\u683c\u5f0f\u5316\n\u2713 bandit: \u65e0\u5b89\u5168\u95ee\u9898\n\n## \u53d1\u73b0\u7684\u95ee\u9898\n\n[CRITICAL] SQL \u6ce8\u5165\u6f0f\u6d1e\n\u6587\u4ef6: app/routes/user.py:42\n\u95ee\u9898: \u7528\u6237\u8f93\u5165\u76f4\u63a5\u63d2\u5165\u5230 SQL \u67e5\u8be2\u4e2d\n```python\nquery = f\"SELECT * FROM users WHERE id = {user_id}\" # \u4e0d\u826f\u505a\u6cd5\n```\n\u4fee\u590d: \u4f7f\u7528\u53c2\u6570\u5316\u67e5\u8be2\n```python\nquery = \"SELECT * FROM users WHERE id = %s\" # \u63a8\u8350\u505a\u6cd5\ncursor.execute(query, (user_id,))\n```\n\n[HIGH] \u53ef\u53d8\u9ed8\u8ba4\u53c2\u6570\n\u6587\u4ef6: app/services/auth.py:18\n\u95ee\u9898: \u53ef\u53d8\u9ed8\u8ba4\u53c2\u6570\u4f1a\u5bfc\u81f4\u72b6\u6001\u5171\u4eab\n```python\ndef process_items(items=[]): # \u4e0d\u826f\u505a\u6cd5\n items.append(\"new\")\n return items\n```\n\u4fee\u590d: \u4f7f\u7528 None \u4f5c\u4e3a\u9ed8\u8ba4\u503c\n```python\ndef process_items(items=None): # \u63a8\u8350\u505a\u6cd5\n if items is None:\n items = []\n items.append(\"new\")\n return items\n```\n\n[MEDIUM] \u7f3a\u5931\u7c7b\u578b\u63d0\u793a\n\u6587\u4ef6: app/services/auth.py:25\n\u95ee\u9898: \u516c\u5171\u51fd\u6570\u6ca1\u6709\u7c7b\u578b\u6ce8\u89e3\n```python\ndef get_user(user_id): # \u4e0d\u826f\u505a\u6cd5\n return db.find(user_id)\n```\n\u4fee\u590d: \u6dfb\u52a0\u7c7b\u578b\u63d0\u793a\n```python\ndef get_user(user_id: str) -> Optional[User]: # \u63a8\u8350\u505a\u6cd5\n return db.find(user_id)\n```\n\n[MEDIUM] \u672a\u4f7f\u7528\u4e0a\u4e0b\u6587\u7ba1\u7406\u5668\n\u6587\u4ef6: app/routes/user.py:55\n\u95ee\u9898: \u5f02\u5e38\u53d1\u751f\u65f6\u6587\u4ef6\u672a\u5173\u95ed\n```python\nf = open(\"config.json\") # \u4e0d\u826f\u505a\u6cd5\ndata = f.read()\nf.close()\n```\n\u4fee\u590d: \u4f7f\u7528\u4e0a\u4e0b\u6587\u7ba1\u7406\u5668\n```python\nwith open(\"config.json\") as f: # \u63a8\u8350\u505a\u6cd5\n data = f.read()\n```\n\n## \u6458\u8981\n- \u4e25\u91cd (CRITICAL): 1\n- \u9ad8 (HIGH): 1\n- \u4e2d (MEDIUM): 2\n\n\u5efa\u8bae: \u274c \u5728\u4fee\u590d\u4e25\u91cd\u95ee\u9898\u524d\u963b\u6b62\u5408\u5e76\n\n## \u9700\u8981\u683c\u5f0f\u5316\n\u8fd0\u884c: `black app/routes/user.py app/services/auth.py`\n```\n\n## \u6279\u51c6\u6807\u51c6\n\n| \u72b6\u6001 | \u6761\u4ef6 |\n|--------|-----------|\n| \u2705 \u6279\u51c6 (Approve) | \u65e0\u201c\u4e25\u91cd\u201d\u6216\u201c\u9ad8\u201d\u7ea7\u522b\u95ee\u9898 |\n| \u26a0\ufe0f \u8b66\u544a (Warning) | \u4ec5\u5b58\u5728\u201c\u4e2d\u201d\u7ea7\u522b\u95ee\u9898\uff08\u8c28\u614e\u5408\u5e76\uff09 |\n| \u274c \u963b\u6b62 (Block) | \u53d1\u73b0\u201c\u4e25\u91cd\u201d\u6216\u201c\u9ad8\u201d\u7ea7\u522b\u95ee\u9898 |\n\n## \u4e0e\u5176\u4ed6\u547d\u4ee4\u7684\u96c6\u6210\n\n- \u5148\u4f7f\u7528 `/python-test` \u786e\u4fdd\u6d4b\u8bd5\u901a\u8fc7\n- \u4f7f\u7528 `/code-review` \u5904\u7406\u975e Python \u4e13\u9879\u7684\u5173\u6ce8\u70b9\n- \u5728\u63d0\u4ea4\uff08commit\uff09\u524d\u4f7f\u7528 `/python-review`\n- \u5982\u679c\u9759\u6001\u5206\u6790\u5de5\u5177\u62a5\u9519\uff0c\u4f7f\u7528 `/build-fix`\n\n## \u6846\u67b6\u4e13\u9879\u5ba1\u67e5\n\n### Django \u9879\u76ee\n\u5ba1\u67e5\u8005\u4f1a\u68c0\u67e5\uff1a\n- N+1 \u67e5\u8be2\u95ee\u9898\uff08\u4f7f\u7528 `select_related` \u548c `prefetch_related`\uff09\n- \u6a21\u578b\u53d8\u66f4\u7f3a\u5931\u8fc1\u79fb\u6587\u4ef6\n- \u5728 ORM \u53ef\u7528\u7684\u60c5\u51b5\u4e0b\u4f7f\u7528\u539f\u751f SQL\n- \u591a\u6b65\u64cd\u4f5c\u7f3a\u5931 `transaction.atomic()`\n\n### FastAPI \u9879\u76ee\n\u5ba1\u67e5\u8005\u4f1a\u68c0\u67e5\uff1a\n- CORS \u914d\u7f6e\u9519\u8bef\n- \u7528\u4e8e\u8bf7\u6c42\u6821\u9a8c\u7684 Pydantic \u6a21\u578b\n- \u54cd\u5e94\u6a21\u578b\u7684\u6b63\u786e\u6027\n- \u6070\u5f53\u7684 async/await \u4f7f\u7528\n- \u4f9d\u8d56\u6ce8\u5165\u6a21\u5f0f\n\n### Flask \u9879\u76ee\n\u5ba1\u67e5\u8005\u4f1a\u68c0\u67e5\uff1a\n- \u4e0a\u4e0b\u6587\u7ba1\u7406\uff08\u5e94\u7528\u4e0a\u4e0b\u6587\u3001\u8bf7\u6c42\u4e0a\u4e0b\u6587\uff09\n- \u6070\u5f53\u7684\u9519\u8bef\u5904\u7406\n- \u84dd\u56fe (Blueprint) \u7ec4\u7ec7\u7ed3\u6784\n- \u914d\u7f6e\u7ba1\u7406\n\n## \u76f8\u5173\n\n- \u667a\u80fd\u4f53 (Agent): `agents/python-reviewer.md`\n- \u6280\u80fd (Skills): `skills/python-patterns/`, `skills/python-testing/`\n\n## \u5e38\u89c1\u4fee\u590d\u65b9\u6848\n\n### \u6dfb\u52a0\u7c7b\u578b\u63d0\u793a\n```python\n# \u4fee\u590d\u524d\ndef calculate(x, y):\n return x + y\n\n# \u4fee\u590d\u540e\nfrom typing import Union\n\ndef calculate(x: Union[int, float], y: Union[int, float]) -> Union[int, float]:\n return x + y\n```\n\n### \u4f7f\u7528\u4e0a\u4e0b\u6587\u7ba1\u7406\u5668\n```python\n# \u4fee\u590d\u524d\nf = open(\"file.txt\")\ndata = f.read()\nf.close()\n\n# \u4fee\u590d\u540e\nwith open(\"file.txt\") as f:\n data = f.read()\n```\n\n### \u4f7f\u7528\u5217\u8868\u63a8\u5bfc\u5f0f\n```python\n# \u4fee\u590d\u524d\nresult = []\nfor item in items:\n if item.active:\n result.append(item.name)\n\n# \u4fee\u590d\u540e\nresult = [item.name for item in items if item.active]\n```\n\n### \u4fee\u590d\u53ef\u53d8\u9ed8\u8ba4\u53c2\u6570\n```python\n# \u4fee\u590d\u524d\ndef append(value, items=[]):\n items.append(value)\n return items\n\n# \u4fee\u590d\u540e\ndef append(value, items=None):\n if items is None:\n items = []\n items.append(value)\n return items\n```\n\n### \u4f7f\u7528 f-strings (Python 3.6+)\n```python\n# \u4fee\u590d\u524d\nname = \"Alice\"\ngreeting = \"Hello, \" + name + \"!\"\ngreeting2 = \"Hello, {}\".format(name)\n\n# \u4fee\u590d\u540e\ngreeting = f\"Hello, {name}!\"\n```\n\n### \u4fee\u590d\u5faa\u73af\u4e2d\u7684\u5b57\u7b26\u4e32\u62fc\u63a5\n```python\n# \u4fee\u590d\u524d\nresult = \"\"\nfor item in items:\n result += str(item)\n\n# \u4fee\u590d\u540e\nresult = \"\".join(str(item) for item in items)\n```\n\n## Python \u7248\u672c\u517c\u5bb9\u6027\n\n\u5ba1\u67e5\u8005\u4f1a\u63d0\u793a\u4ee3\u7801\u4f55\u65f6\u4f7f\u7528\u4e86\u8f83\u65b0 Python \u7248\u672c\u7684\u7279\u6027\uff1a\n\n| \u7279\u6027 | \u6700\u4f4e Python \u7248\u672c |\n|---------|----------------|\n| \u7c7b\u578b\u63d0\u793a (Type hints) | 3.5+ |\n| f-strings | 3.6+ |\n| \u6d77\u8c61\u8fd0\u7b97\u7b26 (Walrus operator `:=`) | 3.8+ |\n| \u4ec5\u9650\u4f4d\u7f6e\u53c2\u6570 (Position-only parameters) | 3.8+ |\n| \u5339\u914d\u8bed\u53e5 (Match statements) | 3.10+ |\n| \u7c7b\u578b\u8054\u5408 (Type unions `x | None`) | 3.10+ |\n\n\u8bf7\u786e\u4fdd\u9879\u76ee\u7684 `pyproject.toml` \u6216 `setup.py` \u6307\u5b9a\u4e86\u6b63\u786e\u7684\u6700\u4f4e Python \u7248\u672c\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/commands/skill-create.md": {
+ "md5": "551b0c52e2996f635fbe16db38fc424a",
+ "content": "---\nname: skill-create\ndescription: \u5206\u6790\u672c\u5730 Git \u5386\u53f2\u4ee5\u63d0\u53d6\u7f16\u7801\u6a21\u5f0f\u5e76\u751f\u6210 SKILL.md \u6587\u4ef6\u3002Skill Creator GitHub App \u7684\u672c\u5730\u7248\u672c\u3002\nallowed_tools: [\"Bash\", \"Read\", \"Write\", \"Grep\", \"Glob\"]\n---\n\n# /skill-create - \u672c\u5730\u6280\u80fd\u751f\u6210\uff08Local Skill Generation\uff09\n\n\u5206\u6790\u4f60\u4ed3\u5e93\u7684 Git \u5386\u53f2\u8bb0\u5f55\u4ee5\u63d0\u53d6\u7f16\u7801\u6a21\u5f0f\uff0c\u5e76\u751f\u6210 SKILL.md \u6587\u4ef6\uff0c\u4ee5\u4fbf\u8ba9 Claude \u5b66\u4e60\u4f60\u56e2\u961f\u7684\u5de5\u7a0b\u5b9e\u8df5\u3002\n\n## \u7528\u6cd5\uff08Usage\uff09\n\n```bash\n/skill-create # \u5206\u6790\u5f53\u524d\u4ed3\u5e93\n/skill-create --commits 100 # \u5206\u6790\u6700\u8fd1 100 \u6761\u63d0\u4ea4\n/skill-create --output ./skills # \u6307\u5b9a\u81ea\u5b9a\u4e49\u8f93\u51fa\u76ee\u5f55\n/skill-create --instincts # \u540c\u65f6\u4e3a continuous-learning-v2 \u751f\u6210\u76f4\u89c9\uff08instincts\uff09\n```\n\n## \u529f\u80fd\u8bf4\u660e\uff08What It Does\uff09\n\n1. **\u89e3\u6790 Git \u5386\u53f2** - \u5206\u6790\u63d0\u4ea4\uff08commits\uff09\u3001\u6587\u4ef6\u53d8\u66f4\u548c\u6a21\u5f0f\u3002\n2. **\u68c0\u6d4b\u6a21\u5f0f** - \u8bc6\u522b\u5faa\u73af\u51fa\u73b0\u7684\u5de5\u4f5c\u6d41\uff08Workflow\uff09\u548c\u7ea6\u5b9a\u3002\n3. **\u751f\u6210 SKILL.md** - \u521b\u5efa\u6709\u6548\u7684 Claude Code \u6280\u80fd\uff08Skill\uff09\u6587\u4ef6\u3002\n4. **\u53ef\u9009\u751f\u6210\u76f4\u89c9\uff08Instincts\uff09** - \u7528\u4e8e continuous-learning-v2 \u7cfb\u7edf\u3002\n\n## \u5206\u6790\u6b65\u9aa4\uff08Analysis Steps\uff09\n\n### \u7b2c 1 \u6b65\uff1a\u6536\u96c6 Git \u6570\u636e\n\n```bash\n# \u83b7\u53d6\u5e26\u6709\u6587\u4ef6\u53d8\u66f4\u7684\u8fd1\u671f\u63d0\u4ea4\ngit log --oneline -n ${COMMITS:-200} --name-only --pretty=format:\"%H|%s|%ad\" --date=short\n\n# \u83b7\u53d6\u6309\u6587\u4ef6\u7edf\u8ba1\u7684\u63d0\u4ea4\u9891\u7387\ngit log --oneline -n 200 --name-only | grep -v \"^$\" | grep -v \"^[a-f0-9]\" | sort | uniq -c | sort -rn | head -20\n\n# \u83b7\u53d6\u63d0\u4ea4\u4fe1\u606f\u6a21\u5f0f\ngit log --oneline -n 200 | cut -d' ' -f2- | head -50\n```\n\n### \u7b2c 2 \u6b65\uff1a\u68c0\u6d4b\u6a21\u5f0f\n\n\u5bfb\u627e\u4ee5\u4e0b\u6a21\u5f0f\u7c7b\u578b\uff1a\n\n| \u6a21\u5f0f (Pattern) | \u68c0\u6d4b\u65b9\u6cd5 (Detection Method) |\n|---------|-----------------|\n| **\u63d0\u4ea4\u89c4\u8303 (Commit conventions)** | \u5bf9\u63d0\u4ea4\u4fe1\u606f\u4f7f\u7528\u6b63\u5219\u5339\u914d (feat:, fix:, chore:) |\n| **\u6587\u4ef6\u5173\u8054\u53d8\u66f4 (File co-changes)** | \u603b\u662f\u540c\u65f6\u53d1\u751f\u53d8\u5316\u7684\u6587\u4ef6 |\n| **\u5de5\u4f5c\u6d41\u5e8f\u5217 (Workflow sequences)** | \u91cd\u590d\u51fa\u73b0\u7684\u6587\u4ef6\u53d8\u66f4\u6a21\u5f0f |\n| **\u67b6\u6784 (Architecture)** | \u6587\u4ef6\u5939\u7ed3\u6784\u548c\u547d\u540d\u89c4\u8303 |\n| **\u6d4b\u8bd5\u6a21\u5f0f (Testing patterns)** | \u6d4b\u8bd5\u6587\u4ef6\u4f4d\u7f6e\u3001\u547d\u540d\u3001\u8986\u76d6\u7387 |\n\n### \u7b2c 3 \u6b65\uff1a\u751f\u6210 SKILL.md\n\n\u8f93\u51fa\u683c\u5f0f\uff1a\n\n```markdown\n---\nname: {repo-name}-patterns\ndescription: Coding patterns extracted from {repo-name}\nversion: 1.0.0\nsource: local-git-analysis\nanalyzed_commits: {count}\n---\n\n# {Repo Name} \u6a21\u5f0f\n\n## \u63d0\u4ea4\u89c4\u8303\n{\u68c0\u6d4b\u5230\u7684\u63d0\u4ea4\u4fe1\u606f\u6a21\u5f0f}\n\n## \u4ee3\u7801\u67b6\u6784\n{\u68c0\u6d4b\u5230\u7684\u6587\u4ef6\u5939\u7ed3\u6784\u548c\u7ec4\u7ec7\u65b9\u5f0f}\n\n## \u5de5\u4f5c\u6d41\n{\u68c0\u6d4b\u5230\u7684\u91cd\u590d\u6587\u4ef6\u53d8\u66f4\u6a21\u5f0f}\n\n## \u6d4b\u8bd5\u6a21\u5f0f\n{\u68c0\u6d4b\u5230\u7684\u6d4b\u8bd5\u7ea6\u5b9a}\n```\n\n### \u7b2c 4 \u6b65\uff1a\u751f\u6210\u76f4\u89c9 (\u5982\u679c\u4f7f\u7528\u4e86 --instincts)\n\n\u7528\u4e8e continuous-learning-v2 \u96c6\u6210\uff1a\n\n```yaml\n---\nid: {repo}-commit-convention\ntrigger: \"when writing a commit message\"\nconfidence: 0.8\ndomain: git\nsource: local-repo-analysis\n---\n\n# \u4f7f\u7528\u7ea6\u5b9a\u5f0f\u63d0\u4ea4 (Conventional Commits)\n\n## \u64cd\u4f5c (Action)\n\u5728\u63d0\u4ea4\u4fe1\u606f\u524d\u6dfb\u52a0\u524d\u7f00\uff1afeat:, fix:, chore:, docs:, test:, refactor:\n\n## \u8bc1\u636e (Evidence)\n- \u5df2\u5206\u6790 {n} \u6761\u63d0\u4ea4\n- {percentage}% \u9075\u5faa\u7ea6\u5b9a\u5f0f\u63d0\u4ea4\u683c\u5f0f\n```\n\n## \u8f93\u51fa\u793a\u4f8b\n\n\u5728 TypeScript \u9879\u76ee\u4e0a\u8fd0\u884c `/skill-create` \u53ef\u80fd\u4f1a\u4ea7\u751f\uff1a\n\n```markdown\n---\nname: my-app-patterns\ndescription: Coding patterns from my-app repository\nversion: 1.0.0\nsource: local-git-analysis\nanalyzed_commits: 150\n---\n\n# My App \u6a21\u5f0f\n\n## \u63d0\u4ea4\u89c4\u8303 (Commit Conventions)\n\n\u8be5\u9879\u76ee\u4f7f\u7528 **\u7ea6\u5b9a\u5f0f\u63d0\u4ea4 (conventional commits)**\uff1a\n- `feat:` - \u65b0\u529f\u80fd\n- `fix:` - \u9519\u8bef\u4fee\u590d\n- `chore:` - \u7ef4\u62a4\u4efb\u52a1\n- `docs:` - \u6587\u6863\u66f4\u65b0\n\n## \u4ee3\u7801\u67b6\u6784 (Code Architecture)\n\n```\nsrc/\n\u251c\u2500\u2500 components/ # React \u7ec4\u4ef6 (PascalCase.tsx)\n\u251c\u2500\u2500 hooks/ # \u81ea\u5b9a\u4e49 Hooks (use*.ts)\n\u251c\u2500\u2500 utils/ # \u5de5\u5177\u51fd\u6570\n\u251c\u2500\u2500 types/ # TypeScript \u7c7b\u578b\u5b9a\u4e49\n\u2514\u2500\u2500 services/ # API \u548c\u5916\u90e8\u670d\u52a1\n```\n\n## \u5de5\u4f5c\u6d41 (Workflows)\n\n### \u6dfb\u52a0\u65b0\u7ec4\u4ef6\n1. \u521b\u5efa `src/components/ComponentName.tsx`\n2. \u5728 `src/components/__tests__/ComponentName.test.tsx` \u4e2d\u6dfb\u52a0\u6d4b\u8bd5\n3. \u4ece `src/components/index.ts` \u5bfc\u51fa\n\n### \u6570\u636e\u5e93\u8fc1\u79fb\n1. \u4fee\u6539 `src/db/schema.ts`\n2. \u8fd0\u884c `pnpm db:generate`\n3. \u8fd0\u884c `pnpm db:migrate`\n\n## \u6d4b\u8bd5\u6a21\u5f0f (Testing patterns)\n\n- \u6d4b\u8bd5\u6587\u4ef6\uff1a`__tests__/` \u76ee\u5f55\u6216 `.test.ts` \u540e\u7f00\n- \u8986\u76d6\u7387\u76ee\u6807\uff1a80%+\n- \u6846\u67b6\uff1aVitest\n```\n\n## GitHub App \u96c6\u6210\n\n\u5bf9\u4e8e\u9ad8\u7ea7\u529f\u80fd\uff081\u4e07+ \u63d0\u4ea4\u3001\u56e2\u961f\u5171\u4eab\u3001\u81ea\u52a8 PR\uff09\uff0c\u8bf7\u4f7f\u7528 [Skill Creator GitHub App](https://github.com/apps/skill-creator)\uff1a\n\n- \u5b89\u88c5\uff1a[github.com/apps/skill-creator](https://github.com/apps/skill-creator)\n- \u5728\u4efb\u4f55 Issue \u4e0a\u8bc4\u8bba `/skill-creator analyze`\n- \u63a5\u6536\u5305\u542b\u751f\u6210\u7684\u6280\u80fd\u7684 PR\n\n## \u76f8\u5173\u547d\u4ee4\n\n- `/instinct-import` - \u5bfc\u5165\u751f\u6210\u7684\u76f4\u89c9\n- `/instinct-status` - \u67e5\u770b\u5df2\u5b66\u4e60\u7684\u76f4\u89c9\n- `/evolve` - \u5c06\u76f4\u89c9\u805a\u7c7b\u4e3a\u6280\u80fd/\u667a\u80fd\u4f53\n\n---\n\n*\u5c5e\u4e8e [Everything Claude Code](https://github.com/affaan-m/everything-claude-code) \u7684\u4e00\u90e8\u5206*\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/skills/django-security/SKILL.md": {
+ "md5": "407fdd4274972aa1720af18e64019ae9",
+ "content": "---\nname: django-security\ndescription: Django \u5b89\u5168\u6700\u4f73\u5b9e\u8df5\uff0c\u6db5\u76d6\u8eab\u4efd\u8ba4\u8bc1\u3001\u6388\u6743\u3001CSRF \u9632\u62a4\u3001SQL \u6ce8\u5165\u9884\u9632\u3001XSS \u9884\u9632\u4ee5\u53ca\u5b89\u5168\u7684\u90e8\u7f72\u914d\u7f6e\u3002\n---\n\n# Django \u5b89\u5168\u6700\u4f73\u5b9e\u8df5\n\n\u9488\u5bf9 Django \u5e94\u7528\u7a0b\u5e8f\u7684\u5168\u9762\u5b89\u5168\u6307\u5357\uff0c\u65e8\u5728\u62b5\u5fa1\u5e38\u89c1\u7684\u6f0f\u6d1e\u3002\n\n## \u4f55\u65f6\u6fc0\u6d3b\n\n- \u8bbe\u7f6e Django \u8eab\u4efd\u8ba4\u8bc1\uff08Authentication\uff09\u548c\u6388\u6743\uff08Authorization\uff09\u65f6\n- \u5b9e\u73b0\u7528\u6237\u6743\u9650\u548c\u89d2\u8272\u65f6\n- \u914d\u7f6e\u751f\u4ea7\u73af\u5883\u5b89\u5168\u8bbe\u7f6e\u65f6\n- \u5ba1\u67e5 Django \u5e94\u7528\u7a0b\u5e8f\u7684\u5b89\u5168\u95ee\u9898\u65f6\n- \u5c06 Django \u5e94\u7528\u7a0b\u5e8f\u90e8\u7f72\u5230\u751f\u4ea7\u73af\u5883\u65f6\n\n## \u6838\u5fc3\u5b89\u5168\u8bbe\u7f6e\n\n### \u751f\u4ea7\u73af\u5883\u8bbe\u7f6e\u914d\u7f6e\n\n```python\n# settings/production.py\nimport os\n\nDEBUG = False # \u5173\u952e\uff1a\u5207\u52ff\u5728\u751f\u4ea7\u73af\u5883\u4e2d\u4f7f\u7528 True\n\nALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')\n\n# \u5b89\u5168\u5934\u90e8\uff08Security headers\uff09\nSECURE_SSL_REDIRECT = True\nSESSION_COOKIE_SECURE = True\nCSRF_COOKIE_SECURE = True\nSECURE_HSTS_SECONDS = 31536000 # 1 \u5e74\nSECURE_HSTS_INCLUDE_SUBDOMAINS = True\nSECURE_HSTS_PRELOAD = True\nSECURE_CONTENT_TYPE_NOSNIFF = True\nSECURE_BROWSER_XSS_FILTER = True\nX_FRAME_OPTIONS = 'DENY'\n\n# HTTPS \u4e0e Cookie\nSESSION_COOKIE_HTTPONLY = True\nCSRF_COOKIE_HTTPONLY = True\nSESSION_COOKIE_SAMESITE = 'Lax'\nCSRF_COOKIE_SAMESITE = 'Lax'\n\n# \u5bc6\u94a5\uff08\u5fc5\u987b\u901a\u8fc7\u73af\u5883\u53d8\u91cf\u8bbe\u7f6e\uff09\nSECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')\nif not SECRET_KEY:\n raise ImproperlyConfigured('DJANGO_SECRET_KEY environment variable is required')\n\n# \u5bc6\u7801\u6821\u9a8c\nAUTH_PASSWORD_VALIDATORS = [\n {\n 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',\n },\n {\n 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',\n 'OPTIONS': {\n 'min_length': 12,\n }\n },\n {\n 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',\n },\n {\n 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',\n },\n]\n```\n\n## \u8eab\u4efd\u8ba4\u8bc1\uff08Authentication\uff09\n\n### \u81ea\u5b9a\u4e49\u7528\u6237\u6a21\u578b\uff08Custom User Model\uff09\n\n```python\n# apps/users/models.py\nfrom django.contrib.auth.models import AbstractUser\nfrom django.db import models\n\nclass User(AbstractUser):\n \"\"\"\u4e3a\u4e86\u66f4\u597d\u7684\u5b89\u5168\u6027\u800c\u81ea\u5b9a\u4e49\u7684\u7528\u6237\u6a21\u578b\u3002\"\"\"\n\n email = models.EmailField(unique=True)\n phone = models.CharField(max_length=20, blank=True)\n\n USERNAME_FIELD = 'email' # \u4f7f\u7528\u90ae\u7bb1\u4f5c\u4e3a\u7528\u6237\u540d\n REQUIRED_FIELDS = ['username']\n\n class Meta:\n db_table = 'users'\n verbose_name = 'User'\n verbose_name_plural = 'Users'\n\n def __str__(self):\n return self.email\n\n# settings/base.py\nAUTH_USER_MODEL = 'users.User'\n```\n\n### \u5bc6\u7801\u54c8\u5e0c\uff08Password Hashing\uff09\n\n```python\n# Django \u9ed8\u8ba4\u4f7f\u7528 PBKDF2\u3002\u4e3a\u4e86\u66f4\u5f3a\u7684\u5b89\u5168\u6027\uff1a\nPASSWORD_HASHERS = [\n 'django.contrib.auth.hashers.Argon2PasswordHasher',\n 'django.contrib.auth.hashers.PBKDF2PasswordHasher',\n 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',\n 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',\n]\n```\n\n### \u4f1a\u8bdd\u7ba1\u7406\uff08Session Management\uff09\n\n```python\n# \u4f1a\u8bdd\u914d\u7f6e\nSESSION_ENGINE = 'django.contrib.sessions.backends.cache' # \u6216 'db'\nSESSION_CACHE_ALIAS = 'default'\nSESSION_COOKIE_AGE = 3600 * 24 * 7 # 1 \u5468\nSESSION_SAVE_EVERY_REQUEST = False\nSESSION_EXPIRE_AT_BROWSER_CLOSE = False # \u66f4\u597d\u7684\u7528\u6237\u4f53\u9a8c\uff0c\u4f46\u5b89\u5168\u6027\u7565\u4f4e\n```\n\n## \u6388\u6743\uff08Authorization\uff09\n\n### \u6743\u9650\uff08Permissions\uff09\n\n```python\n# models.py\nfrom django.db import models\nfrom django.contrib.auth.models import Permission\n\nclass Post(models.Model):\n title = models.CharField(max_length=200)\n content = models.TextField()\n author = models.ForeignKey(User, on_delete=models.CASCADE)\n\n class Meta:\n permissions = [\n ('can_publish', '\u53ef\u4ee5\u53d1\u5e03\u5e16\u5b50'),\n ('can_edit_others', '\u53ef\u4ee5\u7f16\u8f91\u4ed6\u4eba\u7684\u5e16\u5b50'),\n ]\n\n def user_can_edit(self, user):\n \"\"\"\u68c0\u67e5\u7528\u6237\u662f\u5426\u53ef\u4ee5\u7f16\u8f91\u6b64\u5e16\u5b50\u3002\"\"\"\n return self.author == user or user.has_perm('app.can_edit_others')\n\n# views.py\nfrom django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin\nfrom django.views.generic import UpdateView\n\nclass PostUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):\n model = Post\n permission_required = 'app.can_edit_others'\n raise_exception = True # \u8fd4\u56de 403 \u800c\u4e0d\u662f\u91cd\u5b9a\u5411\n\n def get_queryset(self):\n \"\"\"\u4ec5\u5141\u8bb8\u7528\u6237\u7f16\u8f91\u81ea\u5df1\u7684\u5e16\u5b50\u3002\"\"\"\n return Post.objects.filter(author=self.request.user)\n```\n\n### \u81ea\u5b9a\u4e49\u6743\u9650\n\n```python\n# permissions.py\nfrom rest_framework import permissions\n\nclass IsOwnerOrReadOnly(permissions.BasePermission):\n \"\"\"\u4ec5\u5141\u8bb8\u6240\u6709\u8005\u7f16\u8f91\u5bf9\u8c61\u3002\"\"\"\n\n def has_object_permission(self, request, view, obj):\n # \u5141\u8bb8\u4efb\u4f55\u8bf7\u6c42\u7684\u8bfb\u53d6\u6743\u9650\n if request.method in permissions.SAFE_METHODS:\n return True\n\n # \u4ec5\u6240\u6709\u8005\u62e5\u6709\u5199\u5165\u6743\u9650\n return obj.author == request.user\n\nclass IsAdminOrReadOnly(permissions.BasePermission):\n \"\"\"\u5141\u8bb8\u7ba1\u7406\u5458\u6267\u884c\u4efb\u4f55\u64cd\u4f5c\uff0c\u5176\u4ed6\u4eba\u53ea\u8bfb\u3002\"\"\"\n\n def has_permission(self, request, view):\n if request.method in permissions.SAFE_METHODS:\n return True\n return request.user and request.user.is_staff\n\nclass IsVerifiedUser(permissions.BasePermission):\n \"\"\"\u4ec5\u5141\u8bb8\u5df2\u9a8c\u8bc1\u7684\u7528\u6237\u3002\"\"\"\n\n def has_permission(self, request, view):\n return request.user and request.user.is_authenticated and request.user.is_verified\n```\n\n### \u57fa\u4e8e\u89d2\u8272\u7684\u8bbf\u95ee\u63a7\u5236\uff08RBAC\uff09\n\n```python\n# models.py\nfrom django.contrib.auth.models import AbstractUser, Group\n\nclass User(AbstractUser):\n ROLE_CHOICES = [\n ('admin', '\u7ba1\u7406\u5458'),\n ('moderator', '\u7248\u4e3b'),\n ('user', '\u666e\u901a\u7528\u6237'),\n ]\n role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='user')\n\n def is_admin(self):\n return self.role == 'admin' or self.is_superuser\n\n def is_moderator(self):\n return self.role in ['admin', 'moderator']\n\n# \u6df7\u5165\u7c7b\uff08Mixins\uff09\nclass AdminRequiredMixin:\n \"\"\"\u8981\u6c42\u7ba1\u7406\u5458\u89d2\u8272\u7684\u6df7\u5165\u7c7b\u3002\"\"\"\n\n def dispatch(self, request, *args, **kwargs):\n if not request.user.is_authenticated or not request.user.is_admin():\n from django.core.exceptions import PermissionDenied\n raise PermissionDenied\n return super().dispatch(request, *args, **kwargs)\n```\n\n## SQL \u6ce8\u5165\u9632\u62a4\n\n### Django ORM \u4fdd\u62a4\n\n```python\n# \u63a8\u8350\uff1aDjango ORM \u81ea\u52a8\u8f6c\u4e49\u53c2\u6570\ndef get_user(username):\n return User.objects.get(username=username) # \u5b89\u5168\n\n# \u63a8\u8350\uff1a\u5728 raw() \u4e2d\u4f7f\u7528\u53c2\u6570\ndef search_users(query):\n return User.objects.raw('SELECT * FROM users WHERE username = %s', [query])\n\n# \u9519\u8bef\uff1a\u5207\u52ff\u76f4\u63a5\u63d2\u503c\u7528\u6237\u8f93\u5165\ndef get_user_bad(username):\n return User.objects.raw(f'SELECT * FROM users WHERE username = {username}') # \u5b58\u5728\u6f0f\u6d1e\uff01\n\n# \u63a8\u8350\uff1a\u4f7f\u7528\u5e26\u6709\u6b63\u786e\u8f6c\u4e49\u7684 filter\ndef get_users_by_email(email):\n return User.objects.filter(email__iexact=email) # \u5b89\u5168\n\n# \u63a8\u8350\uff1a\u5bf9\u590d\u6742\u67e5\u8be2\u4f7f\u7528 Q \u5bf9\u8c61\nfrom django.db.models import Q\ndef search_users_complex(query):\n return User.objects.filter(\n Q(username__icontains=query) |\n Q(email__icontains=query)\n ) # \u5b89\u5168\n```\n\n### \u4f7f\u7528 raw() \u65f6\u7684\u989d\u5916\u5b89\u5168\u63aa\u65bd\n\n```python\n# \u5982\u679c\u5fc5\u987b\u4f7f\u7528\u539f\u751f SQL\uff0c\u8bf7\u52a1\u5fc5\u4f7f\u7528\u53c2\u6570\nUser.objects.raw(\n 'SELECT * FROM users WHERE email = %s AND status = %s',\n [user_input_email, status]\n)\n```\n\n## XSS \u9632\u62a4\n\n### \u6a21\u677f\u8f6c\u4e49\n\n```django\n{# Django \u9ed8\u8ba4\u81ea\u52a8\u8f6c\u4e49\u53d8\u91cf - \u5b89\u5168 #}\n{{ user_input }} {# \u5df2\u8f6c\u4e49\u7684 HTML #}\n\n{# \u4ec5\u5bf9\u53d7\u4fe1\u4efb\u7684\u5185\u5bb9\u663e\u5f0f\u6807\u8bb0\u4e3a safe #}\n{{ trusted_html|safe }} {# \u672a\u8f6c\u4e49 #}\n\n{# \u4f7f\u7528\u6a21\u677f\u8fc7\u6ee4\u5668\u4ee5\u83b7\u5f97\u5b89\u5168\u7684 HTML #}\n{{ user_input|escape }} {# \u4e0e\u9ed8\u8ba4\u503c\u76f8\u540c #}\n{{ user_input|striptags }} {# \u79fb\u9664\u6240\u6709 HTML \u6807\u7b7e #}\n\n{# JavaScript \u8f6c\u4e49 #}\n\n```\n\n### \u5b89\u5168\u5b57\u7b26\u4e32\u5904\u7406\n\n```python\nfrom django.utils.safestring import mark_safe\nfrom django.utils.html import escape\n\n# \u9519\u8bef\uff1a\u5728\u6ca1\u6709\u8f6c\u4e49\u7684\u60c5\u51b5\u4e0b\uff0c\u5207\u52ff\u5c06\u7528\u6237\u8f93\u5165\u6807\u8bb0\u4e3a safe\ndef render_bad(user_input):\n return mark_safe(user_input) # \u5b58\u5728\u6f0f\u6d1e\uff01\n\n# \u63a8\u8350\uff1a\u5148\u8f6c\u4e49\uff0c\u7136\u540e\u6807\u8bb0\u4e3a safe\ndef render_good(user_input):\n return mark_safe(escape(user_input))\n\n# \u63a8\u8350\uff1a\u5bf9\u5305\u542b\u53d8\u91cf\u7684 HTML \u4f7f\u7528 format_html\nfrom django.utils.html import format_html\n\ndef greet_user(username):\n return format_html('{} ', escape(username))\n```\n\n### HTTP \u5934\u90e8\n\n```python\n# settings.py\nSECURE_CONTENT_TYPE_NOSNIFF = True # \u9632\u6b62 MIME \u55c5\u63a2\nSECURE_BROWSER_XSS_FILTER = True # \u542f\u7528 XSS \u8fc7\u6ee4\u5668\nX_FRAME_OPTIONS = 'DENY' # \u9632\u6b62\u70b9\u51fb\u52ab\u6301\n\n# \u81ea\u5b9a\u4e49\u4e2d\u95f4\u4ef6\nfrom django.conf import settings\n\nclass SecurityHeaderMiddleware:\n def __init__(self, get_response):\n self.get_response = get_response\n\n def __call__(self, request):\n response = self.get_response(request)\n response['X-Content-Type-Options'] = 'nosniff'\n response['X-Frame-Options'] = 'DENY'\n response['X-XSS-Protection'] = '1; mode=block'\n response['Content-Security-Policy'] = \"default-src 'self'\"\n return response\n```\n\n## CSRF \u9632\u62a4\n\n### \u9ed8\u8ba4 CSRF \u9632\u62a4\n\n```python\n# settings.py - CSRF \u9ed8\u8ba4\u5df2\u542f\u7528\nCSRF_COOKIE_SECURE = True # \u4ec5\u901a\u8fc7 HTTPS \u53d1\u9001\nCSRF_COOKIE_HTTPONLY = True # \u9632\u6b62 JavaScript \u8bbf\u95ee\nCSRF_COOKIE_SAMESITE = 'Lax' # \u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\u9632\u6b62 CSRF\nCSRF_TRUSTED_ORIGINS = ['https://example.com'] # \u53d7\u4fe1\u4efb\u7684\u57df\n\n# \u6a21\u677f\u7528\u6cd5\n\n\n# AJAX \u8bf7\u6c42\nfunction getCookie(name) {\n let cookieValue = null;\n if (document.cookie && document.cookie !== '') {\n const cookies = document.cookie.split(';');\n for (let i = 0; i < cookies.length; i++) {\n const cookie = cookies[i].trim();\n if (cookie.substring(0, name.length + 1) === (name + '=')) {\n cookieValue = decodeURIComponent(cookie.substring(name.length + 1));\n break;\n }\n }\n }\n return cookieValue;\n}\n\nfetch('/api/endpoint/', {\n method: 'POST',\n headers: {\n 'X-CSRFToken': getCookie('csrftoken'),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(data)\n});\n```\n\n### \u8c41\u514d\u89c6\u56fe\uff08\u8bf7\u8c28\u614e\u4f7f\u7528\uff09\n\n```python\nfrom django.views.decorators.csrf import csrf_exempt\n\n@csrf_exempt # \u4ec5\u5728\u7edd\u5bf9\u5fc5\u8981\u65f6\u4f7f\u7528\uff01\ndef webhook_view(request):\n # \u6765\u81ea\u5916\u90e8\u670d\u52a1\u7684 Webhook\n pass\n```\n\n## \u6587\u4ef6\u4e0a\u4f20\u5b89\u5168\n\n### \u6587\u4ef6\u9a8c\u8bc1\n\n```python\nimport os\nfrom django.core.exceptions import ValidationError\n\ndef validate_file_extension(value):\n \"\"\"\u9a8c\u8bc1\u6587\u4ef6\u6269\u5c55\u540d\u3002\"\"\"\n ext = os.path.splitext(value.name)[1]\n valid_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.pdf']\n if not ext.lower() in valid_extensions:\n raise ValidationError('\u4e0d\u652f\u6301\u7684\u6587\u4ef6\u6269\u5c55\u540d\u3002')\n\ndef validate_file_size(value):\n \"\"\"\u9a8c\u8bc1\u6587\u4ef6\u5927\u5c0f\uff08\u6700\u5927 5MB\uff09\u3002\"\"\"\n filesize = value.size\n if filesize > 5 * 1024 * 1024:\n raise ValidationError('\u6587\u4ef6\u8fc7\u5927\u3002\u6700\u5927\u9650\u5236\u4e3a 5MB\u3002')\n\n# models.py\nclass Document(models.Model):\n file = models.FileField(\n upload_to='documents/',\n validators=[validate_file_extension, validate_file_size]\n )\n```\n\n### \u5b89\u5168\u6587\u4ef6\u5b58\u50a8\n\n```python\n# settings.py\nMEDIA_ROOT = '/var/www/media/'\nMEDIA_URL = '/media/'\n\n# \u751f\u4ea7\u73af\u5883\u4e2d\u4f7f\u7528\u72ec\u7acb\u7684\u5a92\u4f53\u6587\u4ef6\u57df\u540d\nMEDIA_DOMAIN = 'https://media.example.com'\n\n# \u4e0d\u8981\u76f4\u63a5\u63d0\u4f9b\u7528\u6237\u4e0a\u4f20\u7684\u6587\u4ef6\n# \u5bf9\u9759\u6001\u6587\u4ef6\u4f7f\u7528 whitenoise \u6216 CDN\n# \u5bf9\u5a92\u4f53\u6587\u4ef6\u4f7f\u7528\u72ec\u7acb\u670d\u52a1\u5668\u6216 S3\n```\n\n## API \u5b89\u5168\n\n### \u901f\u7387\u9650\u5236\uff08Rate Limiting\uff09\n\n```python\n# settings.py\nREST_FRAMEWORK = {\n 'DEFAULT_THROTTLE_CLASSES': [\n 'rest_framework.throttling.AnonRateThrottle',\n 'rest_framework.throttling.UserRateThrottle'\n ],\n 'DEFAULT_THROTTLE_RATES': {\n 'anon': '100/day',\n 'user': '1000/day',\n 'upload': '10/hour',\n }\n}\n\n# \u81ea\u5b9a\u4e49\u8282\u6d41\uff08Throttle\uff09\nfrom rest_framework.throttling import UserRateThrottle\n\nclass BurstRateThrottle(UserRateThrottle):\n scope = 'burst'\n rate = '60/min'\n\nclass SustainedRateThrottle(UserRateThrottle):\n scope = 'sustained'\n rate = '1000/day'\n```\n\n### API \u8eab\u4efd\u8ba4\u8bc1\n\n```python\n# settings.py\nREST_FRAMEWORK = {\n 'DEFAULT_AUTHENTICATION_CLASSES': [\n 'rest_framework.authentication.TokenAuthentication',\n 'rest_framework.authentication.SessionAuthentication',\n 'rest_framework_simplejwt.authentication.JWTAuthentication',\n ],\n 'DEFAULT_PERMISSION_CLASSES': [\n 'rest_framework.permissions.IsAuthenticated',\n ],\n}\n\n# views.py\nfrom rest_framework.decorators import api_view, permission_classes\nfrom rest_framework.permissions import IsAuthenticated\n\n@api_view(['GET', 'POST'])\n@permission_classes([IsAuthenticated])\ndef protected_view(request):\n return Response({'message': 'You are authenticated'})\n```\n\n## \u5b89\u5168\u5934\u90e8\uff08Security Headers\uff09\n\n### \u5185\u5bb9\u5b89\u5168\u7b56\u7565\uff08CSP\uff09\n\n```python\n# settings.py\nCSP_DEFAULT_SRC = \"'self'\"\nCSP_SCRIPT_SRC = \"'self' https://cdn.example.com\"\nCSP_STYLE_SRC = \"'self' 'unsafe-inline'\"\nCSP_IMG_SRC = \"'self' data: https:\"\nCSP_CONNECT_SRC = \"'self' https://api.example.com\"\n\n# \u4e2d\u95f4\u4ef6\nclass CSPMiddleware:\n def __init__(self, get_response):\n self.get_response = get_response\n\n def __call__(self, request):\n response = self.get_response(request)\n response['Content-Security-Policy'] = (\n f\"default-src {CSP_DEFAULT_SRC}; \"\n f\"script-src {CSP_SCRIPT_SRC}; \"\n f\"style-src {CSP_STYLE_SRC}; \"\n f\"img-src {CSP_IMG_SRC}; \"\n f\"connect-src {CSP_CONNECT_SRC}\"\n )\n return response\n```\n\n## \u73af\u5883\u53d8\u91cf\n\n### \u7ba1\u7406\u5bc6\u94a5\n\n```python\n# \u4f7f\u7528 python-decouple \u6216 django-environ\nimport environ\n\nenv = environ.Env(\n # \u8bbe\u7f6e\u7c7b\u578b\u8f6c\u6362\u3001\u9ed8\u8ba4\u503c\n DEBUG=(bool, False)\n)\n\n# \u8bfb\u53d6 .env \u6587\u4ef6\nenviron.Env.read_env()\n\nSECRET_KEY = env('DJANGO_SECRET_KEY')\nDATABASE_URL = env('DATABASE_URL')\nALLOWED_HOSTS = env.list('ALLOWED_HOSTS')\n\n# .env \u6587\u4ef6\uff08\u5207\u52ff\u63d0\u4ea4\u6b64\u6587\u4ef6\uff09\nDEBUG=False\nSECRET_KEY=your-secret-key-here\nDATABASE_URL=postgresql://user:password@localhost:5432/dbname\nALLOWED_HOSTS=example.com,www.example.com\n```\n\n## \u8bb0\u5f55\u5b89\u5168\u4e8b\u4ef6\n\n```python\n# settings.py\nLOGGING = {\n 'version': 1,\n 'disable_existing_loggers': False,\n 'handlers': {\n 'file': {\n 'level': 'WARNING',\n 'class': 'logging.FileHandler',\n 'filename': '/var/log/django/security.log',\n },\n 'console': {\n 'level': 'INFO',\n 'class': 'logging.StreamHandler',\n },\n },\n 'loggers': {\n 'django.security': {\n 'handlers': ['file', 'console'],\n 'level': 'WARNING',\n 'propagate': True,\n },\n 'django.request': {\n 'handlers': ['file'],\n 'level': 'ERROR',\n 'propagate': False,\n },\n },\n}\n```\n\n## \u5feb\u901f\u5b89\u5168\u81ea\u68c0\u8868\n\n| \u68c0\u67e5\u9879 | \u63cf\u8ff0 |\n|-------|-------------|\n| `DEBUG = False` | \u5207\u52ff\u5728\u751f\u4ea7\u73af\u5883\u4e2d\u5f00\u542f DEBUG |\n| \u4ec5\u9650 HTTPS | \u5f3a\u5236\u4f7f\u7528 SSL\uff0c\u542f\u7528\u5b89\u5168 Cookie |\n| \u5f3a\u5bc6\u94a5 | \u4e3a SECRET_KEY \u4f7f\u7528\u73af\u5883\u53d8\u91cf |\n| \u5bc6\u7801\u6821\u9a8c | \u542f\u7528\u6240\u6709\u5bc6\u7801\u9a8c\u8bc1\u5668 |\n| CSRF \u9632\u62a4 | \u9ed8\u8ba4\u5df2\u542f\u7528\uff0c\u8bf7\u52ff\u7981\u7528 |\n| XSS \u9632\u62a4 | Django \u81ea\u52a8\u8f6c\u4e49\uff0c\u8bf7\u52ff\u5bf9\u7528\u6237\u8f93\u5165\u4f7f\u7528 `|safe` |\n| SQL \u6ce8\u5165 | \u4f7f\u7528 ORM\uff0c\u5207\u52ff\u5728\u67e5\u8be2\u4e2d\u62fc\u63a5\u5b57\u7b26\u4e32 |\n| \u6587\u4ef6\u4e0a\u4f20 | \u9a8c\u8bc1\u6587\u4ef6\u7c7b\u578b\u548c\u5927\u5c0f |\n| \u901f\u7387\u9650\u5236 | \u5bf9 API \u7aef\u70b9\u8fdb\u884c\u8282\u6d41 |\n| \u5b89\u5168\u5934\u90e8 | \u914d\u7f6e CSP, X-Frame-Options, HSTS |\n| \u65e5\u5fd7\u8bb0\u5f55 | \u8bb0\u5f55\u5b89\u5168\u4e8b\u4ef6 |\n| \u66f4\u65b0 | \u4fdd\u6301 Django \u53ca\u5176\u4f9d\u8d56\u9879\u4e3a\u6700\u65b0\u7248\u672c |\n\n\u8bb0\u4f4f\uff1a\u5b89\u5168\u662f\u4e00\u4e2a\u6301\u7eed\u7684\u8fc7\u7a0b\uff0c\u800c\u4e0d\u662f\u4e00\u4e2a\u4ea7\u54c1\u3002\u8bf7\u5b9a\u671f\u5ba1\u67e5\u5e76\u66f4\u65b0\u4f60\u7684\u5b89\u5168\u5b9e\u8df5\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/skills/django-patterns/SKILL.md": {
+ "md5": "9f3de615e13de63294c92eb0d335fb20",
+ "content": "---\nname: django-patterns\ndescription: Django \u67b6\u6784\u6a21\u5f0f\u3001\u4f7f\u7528 DRF \u7684 REST API \u8bbe\u8ba1\u3001ORM \u6700\u4f73\u5b9e\u8df5\u3001\u7f13\u5b58\u3001\u4fe1\u53f7\uff08Signals\uff09\u3001\u4e2d\u95f4\u4ef6\uff08Middleware\uff09\u4ee5\u53ca\u751f\u4ea7\u7ea7 Django \u5e94\u7528\u3002\n---\n\n# Django \u5f00\u53d1\u6a21\u5f0f\n\n\u9002\u7528\u4e8e\u53ef\u6269\u5c55\u3001\u53ef\u7ef4\u62a4\u5e94\u7528\u7a0b\u5e8f\u7684\u751f\u4ea7\u7ea7 Django \u67b6\u6784\u6a21\u5f0f\u3002\n\n## \u4f55\u65f6\u6fc0\u6d3b\n\n- \u6784\u5efa Django Web \u5e94\u7528\u7a0b\u5e8f\u65f6\n- \u8bbe\u8ba1 Django REST Framework (DRF) API \u65f6\n- \u5904\u7406 Django ORM \u548c\u6a21\u578b\u65f6\n* \u8bbe\u7f6e Django \u9879\u76ee\u7ed3\u6784\u65f6\n* \u5b9e\u73b0\u7f13\u5b58\uff08Caching\uff09\u3001\u4fe1\u53f7\uff08Signals\uff09\u3001\u4e2d\u95f4\u4ef6\uff08Middleware\uff09\u65f6\n\n## \u9879\u76ee\u7ed3\u6784\n\n### \u63a8\u8350\u5e03\u5c40\n\n```\nmyproject/\n\u251c\u2500\u2500 config/\n\u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u251c\u2500\u2500 settings/\n\u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u251c\u2500\u2500 base.py # \u57fa\u7840\u8bbe\u7f6e\n\u2502 \u2502 \u251c\u2500\u2500 development.py # \u5f00\u53d1\u73af\u5883\u8bbe\u7f6e\n\u2502 \u2502 \u251c\u2500\u2500 production.py # \u751f\u4ea7\u73af\u5883\u8bbe\u7f6e\n\u2502 \u2502 \u2514\u2500\u2500 test.py # \u6d4b\u8bd5\u73af\u5883\u8bbe\u7f6e\n\u2502 \u251c\u2500\u2500 urls.py\n\u2502 \u251c\u2500\u2500 wsgi.py\n\u2502 \u2514\u2500\u2500 asgi.py\n\u251c\u2500\u2500 manage.py\n\u2514\u2500\u2500 apps/\n \u251c\u2500\u2500 __init__.py\n \u251c\u2500\u2500 users/\n \u2502 \u251c\u2500\u2500 __init__.py\n \u2502 \u251c\u2500\u2500 models.py\n \u2502 \u251c\u2500\u2500 views.py\n \u2502 \u251c\u2500\u2500 serializers.py\n \u2502 \u251c\u2500\u2500 urls.py\n \u2502 \u251c\u2500\u2500 permissions.py\n \u2502 \u251c\u2500\u2500 filters.py\n \u2502 \u251c\u2500\u2500 services.py\n \u2502 \u2514\u2500\u2500 tests/\n \u2514\u2500\u2500 products/\n \u2514\u2500\u2500 ...\n```\n\n### \u5206\u79bb\u8bbe\u7f6e\u6a21\u5f0f\uff08Split Settings Pattern\uff09\n\n```python\n# config/settings/base.py\nfrom pathlib import Path\n\nBASE_DIR = Path(__file__).resolve().parent.parent.parent\n\nSECRET_KEY = env('DJANGO_SECRET_KEY')\nDEBUG = False\nALLOWED_HOSTS = []\n\nINSTALLED_APPS = [\n 'django.contrib.admin',\n 'django.contrib.auth',\n 'django.contrib.contenttypes',\n 'django.contrib.sessions',\n 'django.contrib.messages',\n 'django.contrib.staticfiles',\n 'rest_framework',\n 'rest_framework.authtoken',\n 'corsheaders',\n # \u672c\u5730\u5e94\u7528\n 'apps.users',\n 'apps.products',\n]\n\nMIDDLEWARE = [\n 'django.middleware.security.SecurityMiddleware',\n 'whitenoise.middleware.WhiteNoiseMiddleware',\n 'django.contrib.sessions.middleware.SessionMiddleware',\n 'corsheaders.middleware.CorsMiddleware',\n 'django.middleware.common.CommonMiddleware',\n 'django.middleware.csrf.CsrfViewMiddleware',\n 'django.contrib.auth.middleware.AuthenticationMiddleware',\n 'django.contrib.messages.middleware.MessageMiddleware',\n 'django.middleware.clickjacking.XFrameOptionsMiddleware',\n]\n\nROOT_URLCONF = 'config.urls'\nWSGI_APPLICATION = 'config.wsgi.application'\n\nDATABASES = {\n 'default': {\n 'ENGINE': 'django.db.backends.postgresql',\n 'NAME': env('DB_NAME'),\n 'USER': env('DB_USER'),\n 'PASSWORD': env('DB_PASSWORD'),\n 'HOST': env('DB_HOST'),\n 'PORT': env('DB_PORT', default='5432'),\n }\n}\n\n# config/settings/development.py\nfrom .base import *\n\nDEBUG = True\nALLOWED_HOSTS = ['localhost', '127.0.0.1']\n\nDATABASES['default']['NAME'] = 'myproject_dev'\n\nINSTALLED_APPS += ['debug_toolbar']\n\nMIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']\n\nEMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'\n\n# config/settings/production.py\nfrom .base import *\n\nDEBUG = False\nALLOWED_HOSTS = env.list('ALLOWED_HOSTS')\nSECURE_SSL_REDIRECT = True\nSESSION_COOKIE_SECURE = True\nCSRF_COOKIE_SECURE = True\nSECURE_HSTS_SECONDS = 31536000\nSECURE_HSTS_INCLUDE_SUBDOMAINS = True\nSECURE_HSTS_PRELOAD = True\n\n# \u65e5\u5fd7\u914d\u7f6e\nLOGGING = {\n 'version': 1,\n 'disable_existing_loggers': False,\n 'handlers': {\n 'file': {\n 'level': 'WARNING',\n 'class': 'logging.FileHandler',\n 'filename': '/var/log/django/django.log',\n },\n },\n 'loggers': {\n 'django': {\n 'handlers': ['file'],\n 'level': 'WARNING',\n 'propagate': True,\n },\n },\n}\n```\n\n## \u6a21\u578b\u8bbe\u8ba1\u6a21\u5f0f\n\n### \u6a21\u578b\u6700\u4f73\u5b9e\u8df5\n\n```python\nfrom django.db import models\nfrom django.contrib.auth.models import AbstractUser\nfrom django.core.validators import MinValueValidator, MaxValueValidator\n\nclass User(AbstractUser):\n \"\"\"\u6269\u5c55 AbstractUser \u7684\u81ea\u5b9a\u4e49\u7528\u6237\u6a21\u578b\u3002\"\"\"\n email = models.EmailField(unique=True)\n phone = models.CharField(max_length=20, blank=True)\n birth_date = models.DateField(null=True, blank=True)\n\n USERNAME_FIELD = 'email'\n REQUIRED_FIELDS = ['username']\n\n class Meta:\n db_table = 'users'\n verbose_name = 'user'\n verbose_name_plural = 'users'\n ordering = ['-date_joined']\n\n def __str__(self):\n return self.email\n\n def get_full_name(self):\n return f\"{self.first_name} {self.last_name}\".strip()\n\nclass Product(models.Model):\n \"\"\"\u5e26\u6709\u9002\u5f53\u5b57\u6bb5\u914d\u7f6e\u7684\u4ea7\u54c1\u6a21\u578b\u3002\"\"\"\n name = models.CharField(max_length=200)\n slug = models.SlugField(unique=True, max_length=250)\n description = models.TextField(blank=True)\n price = models.DecimalField(\n max_digits=10,\n decimal_places=2,\n validators=[MinValueValidator(0)]\n )\n stock = models.PositiveIntegerField(default=0)\n is_active = models.BooleanField(default=True)\n category = models.ForeignKey(\n 'Category',\n on_delete=models.CASCADE,\n related_name='products'\n )\n tags = models.ManyToManyField('Tag', blank=True, related_name='products')\n created_at = models.DateTimeField(auto_now_add=True)\n updated_at = models.DateTimeField(auto_now=True)\n\n class Meta:\n db_table = 'products'\n ordering = ['-created_at']\n indexes = [\n models.Index(fields=['slug']),\n models.Index(fields=['-created_at']),\n models.Index(fields=['category', 'is_active']),\n ]\n constraints = [\n models.CheckConstraint(\n check=models.Q(price__gte=0),\n name='price_non_negative'\n )\n ]\n\n def __str__(self):\n return self.name\n\n def save(self, *args, **kwargs):\n if not self.slug:\n self.slug = slugify(self.name)\n super().save(*args, **kwargs)\n```\n\n### QuerySet \u6700\u4f73\u5b9e\u8df5\n\n```python\nfrom django.db import models\n\nclass ProductQuerySet(models.QuerySet):\n \"\"\"Product \u6a21\u578b\u7684\u81ea\u5b9a\u4e49 QuerySet\u3002\"\"\"\n\n def active(self):\n \"\"\"\u4ec5\u8fd4\u56de\u5df2\u6fc0\u6d3b\u7684\u4ea7\u54c1\u3002\"\"\"\n return self.filter(is_active=True)\n\n def with_category(self):\n \"\"\"\u4f7f\u7528 select_related \u52a0\u8f7d\u5206\u7c7b\uff0c\u907f\u514d N+1 \u67e5\u8be2\u3002\"\"\"\n return self.select_related('category')\n\n def with_tags(self):\n \"\"\"\u4f7f\u7528 prefetch_related \u9884\u52a0\u8f7d\u591a\u5bf9\u591a\u5173\u7cfb\u7684\u6807\u7b7e\u3002\"\"\"\n return self.prefetch_related('tags')\n\n def in_stock(self):\n \"\"\"\u8fd4\u56de\u5e93\u5b58 > 0 \u7684\u4ea7\u54c1\u3002\"\"\"\n return self.filter(stock__gt=0)\n\n def search(self, query):\n \"\"\"\u6309\u540d\u79f0\u6216\u63cf\u8ff0\u641c\u7d22\u4ea7\u54c1\u3002\"\"\"\n return self.filter(\n models.Q(name__icontains=query) |\n models.Q(description__icontains=query)\n )\n\nclass Product(models.Model):\n # ... \u5b57\u6bb5 ...\n\n objects = ProductQuerySet.as_manager() # \u4f7f\u7528\u81ea\u5b9a\u4e49 QuerySet\n\n# \u7528\u6cd5\nProduct.objects.active().with_category().in_stock()\n```\n\n### \u7ba1\u7406\u5668\uff08Manager\uff09\u65b9\u6cd5\n\n```python\nclass ProductManager(models.Manager):\n \"\"\"\u7528\u4e8e\u590d\u6742\u67e5\u8be2\u7684\u81ea\u5b9a\u4e49\u7ba1\u7406\u5668\u3002\"\"\"\n\n def get_or_none(self, **kwargs):\n \"\"\"\u8fd4\u56de\u5bf9\u8c61\uff0c\u6216\u8005\u5728\u4e0d\u5b58\u5728\u65f6\u8fd4\u56de None \u800c\u4e0d\u662f\u629b\u51fa DoesNotExist\u3002\"\"\"\n try:\n return self.get(**kwargs)\n except self.model.DoesNotExist:\n return None\n\n def create_with_tags(self, name, price, tag_names):\n \"\"\"\u521b\u5efa\u4ea7\u54c1\u5e76\u5173\u8054\u6807\u7b7e\u3002\"\"\"\n product = self.create(name=name, price=price)\n tags = [Tag.objects.get_or_create(name=name)[0] for name in tag_names]\n product.tags.set(tags)\n return product\n\n def bulk_update_stock(self, product_ids, quantity):\n \"\"\"\u6279\u91cf\u66f4\u65b0\u591a\u4e2a\u4ea7\u54c1\u7684\u5e93\u5b58\u3002\"\"\"\n return self.filter(id__in=product_ids).update(stock=quantity)\n\n# \u5728\u6a21\u578b\u4e2d\nclass Product(models.Model):\n # ... \u5b57\u6bb5 ...\n custom = ProductManager()\n```\n\n## Django REST Framework \u6a21\u5f0f\n\n### \u5e8f\u5217\u5316\u5668\uff08Serializer\uff09\u6a21\u5f0f\n\n```python\nfrom rest_framework import serializers\nfrom django.contrib.auth.password_validation import validate_password\nfrom .models import Product, User\n\nclass ProductSerializer(serializers.ModelSerializer):\n \"\"\"Product \u6a21\u578b\u7684\u5e8f\u5217\u5316\u5668\u3002\"\"\"\n\n category_name = serializers.CharField(source='category.name', read_only=True)\n average_rating = serializers.FloatField(read_only=True)\n discount_price = serializers.SerializerMethodField()\n\n class Meta:\n model = Product\n fields = [\n 'id', 'name', 'slug', 'description', 'price',\n 'discount_price', 'stock', 'category_name',\n 'average_rating', 'created_at'\n ]\n read_only_fields = ['id', 'slug', 'created_at']\n\n def get_discount_price(self, obj):\n \"\"\"\u5982\u679c\u9002\u7528\uff0c\u8ba1\u7b97\u6298\u6263\u4ef7\u3002\"\"\"\n if hasattr(obj, 'discount') and obj.discount:\n return obj.price * (1 - obj.discount.percent / 100)\n return obj.price\n\n def validate_price(self, value):\n \"\"\"\u786e\u4fdd\u4ef7\u683c\u975e\u8d1f\u3002\"\"\"\n if value < 0:\n raise serializers.ValidationError(\"\u4ef7\u683c\u4e0d\u80fd\u4e3a\u8d1f\u6570\u3002\")\n return value\n\nclass ProductCreateSerializer(serializers.ModelSerializer):\n \"\"\"\u7528\u4e8e\u521b\u5efa\u4ea7\u54c1\u7684\u5e8f\u5217\u5316\u5668\u3002\"\"\"\n\n class Meta:\n model = Product\n fields = ['name', 'description', 'price', 'stock', 'category']\n\n def validate(self, data):\n \"\"\"\u591a\u5b57\u6bb5\u7684\u81ea\u5b9a\u4e49\u6821\u9a8c\u3002\"\"\"\n if data['price'] > 10000 and data['stock'] > 100:\n raise serializers.ValidationError(\n \"\u9ad8\u4ef7\u503c\u4ea7\u54c1\u4e0d\u80fd\u6709\u5927\u91cf\u5e93\u5b58\u3002\"\n )\n return data\n\nclass UserRegistrationSerializer(serializers.ModelSerializer):\n \"\"\"\u7528\u6237\u6ce8\u518c\u5e8f\u5217\u5316\u5668\u3002\"\"\"\n\n password = serializers.CharField(\n write_only=True,\n required=True,\n validators=[validate_password],\n style={'input_type': 'password'}\n )\n password_confirm = serializers.CharField(write_only=True, style={'input_type': 'password'})\n\n class Meta:\n model = User\n fields = ['email', 'username', 'password', 'password_confirm']\n\n def validate(self, data):\n \"\"\"\u6821\u9a8c\u4e24\u6b21\u8f93\u5165\u7684\u5bc6\u7801\u662f\u5426\u4e00\u81f4\u3002\"\"\"\n if data['password'] != data['password_confirm']:\n raise serializers.ValidationError({\n \"password_confirm\": \"\u5bc6\u7801\u5b57\u6bb5\u4e0d\u5339\u914d\u3002\"\n })\n return data\n\n def create(self, validated_data):\n \"\"\"\u521b\u5efa\u5e76\u4fdd\u5b58\u52a0\u5bc6\u540e\u7684\u5bc6\u7801\u3002\"\"\"\n validated_data.pop('password_confirm')\n password = validated_data.pop('password')\n user = User.objects.create(**validated_data)\n user.set_password(password)\n user.save()\n return user\n```\n\n### \u89c6\u56fe\u96c6\uff08ViewSet\uff09\u6a21\u5f0f\n\n```python\nfrom rest_framework import viewsets, status, filters\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\nfrom rest_framework.permissions import IsAuthenticated, IsAdminUser\nfrom django_filters.rest_framework import DjangoFilterBackend\nfrom .models import Product\nfrom .serializers import ProductSerializer, ProductCreateSerializer\nfrom .permissions import IsOwnerOrReadOnly\nfrom .filters import ProductFilter\nfrom .filters import ProductFilter\nfrom .services import ProductService\n\nclass ProductViewSet(viewsets.ModelViewSet):\n \"\"\"Product \u6a21\u578b\u7684\u89c6\u56fe\u96c6\u3002\"\"\"\n\n queryset = Product.objects.select_related('category').prefetch_related('tags')\n permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]\n filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]\n filterset_class = ProductFilter\n search_fields = ['name', 'description']\n ordering_fields = ['price', 'created_at', 'name']\n ordering = ['-created_at']\n\n def get_serializer_class(self):\n \"\"\"\u6839\u636e\u64cd\u4f5c\uff08action\uff09\u8fd4\u56de\u9002\u5f53\u7684\u5e8f\u5217\u5316\u5668\u7c7b\u3002\"\"\"\n if self.action == 'create':\n return ProductCreateSerializer\n return ProductSerializer\n\n def perform_create(self, serializer):\n \"\"\"\u5728\u4fdd\u5b58\u65f6\u5173\u8054\u5f53\u524d\u7528\u6237\u4e0a\u4e0b\u6587\u3002\"\"\"\n serializer.save(created_by=self.request.user)\n\n @action(detail=False, methods=['get'])\n def featured(self, request):\n \"\"\"\u8fd4\u56de\u63a8\u8350\u4ea7\u54c1\u3002\"\"\"\n featured = self.queryset.filter(is_featured=True)[:10]\n serializer = self.get_serializer(featured, many=True)\n return Response(serializer.data)\n\n @action(detail=True, methods=['post'])\n def purchase(self, request, pk=None):\n \"\"\"\u8d2d\u4e70\u4ea7\u54c1\u3002\"\"\"\n product = self.get_object()\n service = ProductService()\n result = service.purchase(product, request.user)\n return Response(result, status=status.HTTP_201_CREATED)\n\n @action(detail=False, methods=['get'], permission_classes=[IsAuthenticated])\n def my_products(self, request):\n \"\"\"\u8fd4\u56de\u7531\u5f53\u524d\u7528\u6237\u521b\u5efa\u7684\u4ea7\u54c1\u3002\"\"\"\n products = self.queryset.filter(created_by=request.user)\n page = self.paginate_queryset(products)\n serializer = self.get_serializer(page, many=True)\n return self.get_paginated_response(serializer.data)\n```\n\n### \u81ea\u5b9a\u4e49\u64cd\u4f5c\n\n```python\nfrom rest_framework.decorators import api_view, permission_classes\nfrom rest_framework.permissions import IsAuthenticated\nfrom rest_framework.response import Response\n\n@api_view(['POST'])\n@permission_classes([IsAuthenticated])\ndef add_to_cart(request):\n \"\"\"\u5c06\u4ea7\u54c1\u6dfb\u52a0\u5230\u7528\u6237\u8d2d\u7269\u8f66\u3002\"\"\"\n product_id = request.data.get('product_id')\n quantity = request.data.get('quantity', 1)\n\n try:\n product = Product.objects.get(id=product_id)\n except Product.DoesNotExist:\n return Response(\n {'error': '\u4ea7\u54c1\u672a\u627e\u5230'},\n status=status.HTTP_404_NOT_FOUND\n )\n\n cart, _ = Cart.objects.get_or_create(user=request.user)\n CartItem.objects.create(\n cart=cart,\n product=product,\n quantity=quantity\n )\n\n return Response({'message': '\u5df2\u6dfb\u52a0\u5230\u8d2d\u7269\u8f66'}, status=status.HTTP_201_CREATED)\n```\n\n## \u670d\u52a1\u5c42\u6a21\u5f0f\uff08Service Layer Pattern\uff09\n\n```python\n# apps/orders/services.py\nfrom typing import Optional\nfrom django.db import transaction\nfrom .models import Order, OrderItem\n\nclass OrderService:\n \"\"\"\u8ba2\u5355\u76f8\u5173\u4e1a\u52a1\u903b\u8f91\u7684\u670d\u52a1\u5c42\u3002\"\"\"\n\n @staticmethod\n @transaction.atomic\n def create_order(user, cart: Cart) -> Order:\n \"\"\"\u4ece\u8d2d\u7269\u8f66\u521b\u5efa\u8ba2\u5355\u3002\"\"\"\n order = Order.objects.create(\n user=user,\n total_price=cart.total_price\n )\n\n for item in cart.items.all():\n OrderItem.objects.create(\n order=order,\n product=item.product,\n quantity=item.quantity,\n price=item.product.price\n )\n\n # \u6e05\u7a7a\u8d2d\u7269\u8f66\n cart.items.all().delete()\n\n return order\n\n @staticmethod\n def process_payment(order: Order, payment_data: dict) -> bool:\n \"\"\"\u5904\u7406\u8ba2\u5355\u652f\u4ed8\u3002\"\"\"\n # \u4e0e\u652f\u4ed8\u7f51\u5173\u96c6\u6210\n payment = PaymentGateway.charge(\n amount=order.total_price,\n token=payment_data['token']\n )\n\n if payment.success:\n order.status = Order.Status.PAID\n order.save()\n # \u53d1\u9001\u786e\u8ba4\u90ae\u4ef6\n OrderService.send_confirmation_email(order)\n return True\n\n return False\n\n @staticmethod\n def send_confirmation_email(order: Order):\n \"\"\"\u53d1\u9001\u8ba2\u5355\u786e\u8ba4\u90ae\u4ef6\u3002\"\"\"\n # \u90ae\u4ef6\u53d1\u9001\u903b\u8f91\n pass\n```\n\n## \u7f13\u5b58\u7b56\u7565\uff08Caching Strategies\uff09\n\n### \u89c6\u56fe\u7ea7\u7f13\u5b58\n\n```python\nfrom django.views.decorators.cache import cache_page\nfrom django.utils.decorators import method_decorator\n\n@method_decorator(cache_page(60 * 15), name='dispatch') # 15 \u5206\u949f\nclass ProductListView(generic.ListView):\n model = Product\n template_name = 'products/list.html'\n context_object_name = 'products'\n```\n\n### \u6a21\u677f\u7247\u6bb5\u7f13\u5b58\n\n```django\n{% load cache %}\n{% cache 500 sidebar %}\n ... \u8017\u65f6\u7684\u4fa7\u8fb9\u680f\u5185\u5bb9 ...\n{% endcache %}\n```\n\n### \u4f4e\u7ea7\u7f13\u5b58\n\n```python\nfrom django.core.cache import cache\n\ndef get_featured_products():\n \"\"\"\u901a\u8fc7\u7f13\u5b58\u83b7\u53d6\u63a8\u8350\u4ea7\u54c1\u3002\"\"\"\n cache_key = 'featured_products'\n products = cache.get(cache_key)\n\n if products is None:\n products = list(Product.objects.filter(is_featured=True))\n cache.set(cache_key, products, timeout=60 * 15) # 15 \u5206\u949f\n\n return products\n```\n\n### QuerySet \u7f13\u5b58\n\n```python\nfrom django.core.cache import cache\n\ndef get_popular_categories():\n cache_key = 'popular_categories'\n categories = cache.get(cache_key)\n\n if categories is None:\n categories = list(Category.objects.annotate(\n product_count=Count('products')\n ).filter(product_count__gt=10).order_by('-product_count')[:20])\n cache.set(cache_key, categories, timeout=60 * 60) # 1 \u5c0f\u65f6\n\n return categories\n```\n\n## \u4fe1\u53f7\uff08Signals\uff09\n\n### \u4fe1\u53f7\u6a21\u5f0f\n\n```python\n# apps/users/signals.py\nfrom django.db.models.signals import post_save\nfrom django.dispatch import receiver\nfrom django.contrib.auth import get_user_model\nfrom .models import Profile\n\nUser = get_user_model()\n\n@receiver(post_save, sender=User)\ndef create_user_profile(sender, instance, created, **kwargs):\n \"\"\"\u5f53\u7528\u6237\u521b\u5efa\u65f6\u540c\u6b65\u521b\u5efa Profile\u3002\"\"\"\n if created:\n Profile.objects.create(user=instance)\n\n@receiver(post_save, sender=User)\ndef save_user_profile(sender, instance, **kwargs):\n \"\"\"\u5f53\u7528\u6237\u4fdd\u5b58\u65f6\u540c\u6b65\u4fdd\u5b58 Profile\u3002\"\"\"\n instance.profile.save()\n\n# apps/users/apps.py\nfrom django.apps import AppConfig\n\nclass UsersConfig(AppConfigConfig):\n default_auto_field = 'django.db.models.BigAutoField'\n name = 'apps.users'\n\n def ready(self):\n \"\"\"\u5728\u5e94\u7528\u51c6\u5907\u5c31\u7eea\u65f6\u5bfc\u5165\u4fe1\u53f7\u3002\"\"\"\n import apps.users.signals\n```\n\n## \u4e2d\u95f4\u4ef6\uff08Middleware\uff09\n\n### \u81ea\u5b9a\u4e49\u4e2d\u95f4\u4ef6\n\n```python\n# middleware/active_user_middleware.py\nimport time\nfrom django.utils.deprecation import MiddlewareMixin\n\nclass ActiveUserMiddleware(MiddlewareMixin):\n \"\"\"\u7528\u4e8e\u8ddf\u8e2a\u6d3b\u8dc3\u7528\u6237\u7684\u4e2d\u95f4\u4ef6\u3002\"\"\"\n\n def process_request(self, request):\n \"\"\"\u5904\u7406\u4f20\u5165\u8bf7\u6c42\u3002\"\"\"\n if request.user.is_authenticated:\n # \u66f4\u65b0\u6700\u540e\u6d3b\u8dc3\u65f6\u95f4\n request.user.last_active = timezone.now()\n request.user.save(update_fields=['last_active'])\n\nclass RequestLoggingMiddleware(MiddlewareMixin):\n \"\"\"\u7528\u4e8e\u8bb0\u5f55\u8bf7\u6c42\u65e5\u5fd7\u7684\u4e2d\u95f4\u4ef6\u3002\"\"\"\n\n def process_request(self, request):\n \"\"\"\u8bb0\u5f55\u8bf7\u6c42\u5f00\u59cb\u65f6\u95f4\u3002\"\"\"\n request.start_time = time.time()\n\n def process_response(self, request, response):\n \"\"\"\u8bb0\u5f55\u8bf7\u6c42\u8017\u65f6\u3002\"\"\"\n if hasattr(request, 'start_time'):\n duration = time.time() - request.start_time\n logger.info(f'{request.method} {request.path} - {response.status_code} - {duration:.3f}s')\n return response\n```\n\n## \u6027\u80fd\u4f18\u5316\uff08Performance Optimization\uff09\n\n### \u9632\u6b62 N+1 \u67e5\u8be2\n\n```python\n# \u5dee\u52b2 - N+1 \u67e5\u8be2\nproducts = Product.objects.all()\nfor product in products:\n print(product.category.name) # \u4e3a\u6bcf\u4e2a\u4ea7\u54c1\u5355\u72ec\u8fdb\u884c\u4e00\u6b21\u67e5\u8be2\n\n# \u4f18\u79c0 - \u4f7f\u7528 select_related \u8fdb\u884c\u5355\u6b21\u67e5\u8be2\nproducts = Product.objects.select_related('category').all()\nfor product in products:\n print(product.category.name)\n\n# \u4f18\u79c0 - \u5bf9\u591a\u5bf9\u591a\u5173\u7cfb\u4f7f\u7528 prefetch_related\nproducts = Product.objects.prefetch_related('tags').all()\nfor product in products:\n for tag in product.tags.all():\n print(tag.name)\n```\n\n### \u6570\u636e\u5e93\u7d22\u5f15\n\n```python\nclass Product(models.Model):\n name = models.CharField(max_length=200, db_index=True)\n slug = models.SlugField(unique=True)\n category = models.ForeignKey('Category', on_delete=models.CASCADE)\n created_at = models.DateTimeField(auto_now_add=True)\n\n class Meta:\n indexes = [\n models.Index(fields=['name']),\n models.Index(fields=['-created_at']),\n models.Index(fields=['category', 'created_at']),\n ]\n```\n\n### \u6279\u91cf\u64cd\u4f5c\n\n```python\n# \u6279\u91cf\u521b\u5efa\nProduct.objects.bulk_create([\n Product(name=f'Product {i}', price=10.00)\n for i in range(1000)\n])\n\n# \u6279\u91cf\u66f4\u65b0\nproducts = Product.objects.all()[:100]\nfor product in products:\n product.is_active = True\nProduct.objects.bulk_update(products, ['is_active'])\n\n# \u6279\u91cf\u5220\u9664\nProduct.objects.filter(stock=0).delete()\n```\n\n## \u5feb\u901f\u53c2\u8003\n\n| \u6a21\u5f0f | \u63cf\u8ff0 |\n|---------|-------------|\n| \u5206\u79bb\u8bbe\u7f6e\uff08Split settings\uff09 | \u533a\u5206\u5f00\u53d1/\u751f\u4ea7/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e |\n| \u81ea\u5b9a\u4e49 QuerySet | \u53ef\u590d\u7528\u7684\u67e5\u8be2\u65b9\u6cd5 |\n| \u670d\u52a1\u5c42\uff08Service Layer\uff09 | \u4e1a\u52a1\u903b\u8f91\u89e3\u8026 |\n| \u89c6\u56fe\u96c6\uff08ViewSet\uff09 | REST API \u7aef\u70b9\u5c01\u88c5 |\n| \u5e8f\u5217\u5316\u5668\u6821\u9a8c\uff08Serializer validation\uff09 | \u8bf7\u6c42/\u54cd\u5e94\u6570\u636e\u7684\u8f6c\u6362\u4e0e\u9a8c\u8bc1 |\n| select_related | \u5916\u952e\u5173\u7cfb\u4f18\u5316 |\n| prefetch_related | \u591a\u5bf9\u591a\u5173\u7cfb\u4f18\u5316 |\n| \u7f13\u5b58\u4f18\u5148\uff08Cache first\uff09 | \u5bf9\u8017\u65f6\u64cd\u4f5c\u8fdb\u884c\u7f13\u5b58 |\n| \u4fe1\u53f7\uff08Signals\uff09 | \u4e8b\u4ef6\u9a71\u52a8\u7684\u64cd\u4f5c |\n| \u4e2d\u95f4\u4ef6\uff08Middleware\uff09 | \u8bf7\u6c42/\u54cd\u5e94\u62e6\u622a\u5904\u7406 |\n\n\u8bb0\u4f4f\uff1aDjango \u63d0\u4f9b\u4e86\u8bb8\u591a\u6377\u5f84\uff0c\u4f46\u5bf9\u4e8e\u751f\u4ea7\u7ea7\u5e94\u7528\uff0c\u67b6\u6784\u548c\u7ec4\u7ec7\u7ed3\u6784\u6bd4\u7b80\u6d01\u7684\u4ee3\u7801\u66f4\u91cd\u8981\u3002\u8bf7\u4e3a\u53ef\u7ef4\u62a4\u6027\u800c\u6784\u5efa\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/skills/python-testing/SKILL.md": {
+ "md5": "2f656e55cdc1ed8b97b1e80819639dff",
+ "content": "---\nname: python-testing\ndescription: \u4f7f\u7528 pytest\u3001TDD \u65b9\u6cd5\u8bba\u3001fixtures\u3001mocking\u3001\u53c2\u6570\u5316\u548c\u4ee3\u7801\u8986\u76d6\u7387\u8981\u6c42\u7684 Python \u6d4b\u8bd5\u7b56\u7565\u3002\n---\n\n# Python \u6d4b\u8bd5\u6a21\u5f0f\uff08Python Testing Patterns\uff09\n\n\u4f7f\u7528 pytest\u3001\u6d4b\u8bd5\u9a71\u52a8\u5f00\u53d1\uff08TDD\uff09\u65b9\u6cd5\u8bba\u53ca\u6700\u4f73\u5b9e\u8df5\u7684 Python \u5e94\u7528\u7a0b\u5e8f\u5168\u9762\u6d4b\u8bd5\u7b56\u7565\u3002\n\n## \u4f55\u65f6\u6fc0\u6d3b\n\n- \u7f16\u5199\u65b0\u7684 Python \u4ee3\u7801\u65f6\uff08\u9075\u5faa TDD\uff1a\u7ea2\u3001\u7eff\u3001\u91cd\u6784\uff09\n- \u4e3a Python \u9879\u76ee\u8bbe\u8ba1\u6d4b\u8bd5\u5957\u4ef6\u65f6\n- \u5ba1\u67e5 Python \u6d4b\u8bd5\u8986\u76d6\u7387\u65f6\n- \u642d\u5efa\u6d4b\u8bd5\u57fa\u7840\u8bbe\u65bd\u65f6\n\n## \u6838\u5fc3\u6d4b\u8bd5\u7406\u5ff5\n\n### \u6d4b\u8bd5\u9a71\u52a8\u5f00\u53d1\uff08TDD\uff09\n\n\u59cb\u7ec8\u9075\u5faa TDD \u5faa\u73af\uff1a\n\n1. **\u7ea2\uff08RED\uff09**\uff1a\u4e3a\u671f\u671b\u7684\u884c\u4e3a\u7f16\u5199\u4e00\u4e2a\u5931\u8d25\u7684\u6d4b\u8bd5\n2. **\u7eff\uff08GREEN\uff09**\uff1a\u7f16\u5199\u6700\u5c11\u91cf\u7684\u4ee3\u7801\u4f7f\u6d4b\u8bd5\u901a\u8fc7\n3. **\u91cd\u6784\uff08REFACTOR\uff09**\uff1a\u5728\u4fdd\u6301\u6d4b\u8bd5\u901a\u8fc7\u7684\u524d\u63d0\u4e0b\u4f18\u5316\u4ee3\u7801\n\n```python\n# \u6b65\u9aa4 1\uff1a\u7f16\u5199\u5931\u8d25\u7684\u6d4b\u8bd5 (RED)\ndef test_add_numbers():\n result = add(2, 3)\n assert result == 5\n\n# \u6b65\u9aa4 2\uff1a\u7f16\u5199\u6700\u5c0f\u5b9e\u73b0 (GREEN)\ndef add(a, b):\n return a + b\n\n# \u6b65\u9aa4 3\uff1a\u6839\u636e\u9700\u8981\u8fdb\u884c\u91cd\u6784 (REFACTOR)\n```\n\n### \u8986\u76d6\u7387\u8981\u6c42\n\n- **\u76ee\u6807**\uff1a80% \u4ee5\u4e0a\u7684\u4ee3\u7801\u8986\u76d6\u7387\n- **\u5173\u952e\u8def\u5f84**\uff1a\u5fc5\u987b\u8fbe\u5230 100% \u8986\u76d6\u7387\n- \u4f7f\u7528 `pytest --cov` \u6765\u8861\u91cf\u8986\u76d6\u7387\n\n```bash\npytest --cov=mypackage --cov-report=term-missing --cov-report=html\n```\n\n## pytest \u57fa\u7840\n\n### \u57fa\u672c\u6d4b\u8bd5\u7ed3\u6784\n\n```python\nimport pytest\n\ndef test_addition():\n \"\"\"\u6d4b\u8bd5\u57fa\u7840\u52a0\u6cd5\u3002\"\"\"\n assert 2 + 2 == 4\n\ndef test_string_uppercase():\n \"\"\"\u6d4b\u8bd5\u5b57\u7b26\u4e32\u5927\u5199\u8f6c\u6362\u3002\"\"\"\n text = \"hello\"\n assert text.upper() == \"HELLO\"\n\ndef test_list_append():\n \"\"\"\u6d4b\u8bd5\u5217\u8868\u8ffd\u52a0\u3002\"\"\"\n items = [1, 2, 3]\n items.append(4)\n assert 4 in items\n assert len(items) == 4\n```\n\n### \u65ad\u8a00\uff08Assertions\uff09\n\n```python\n# \u76f8\u7b49\u6027\nassert result == expected\n\n# \u4e0d\u7b49\u6027\nassert result != unexpected\n\n# \u771f\u503c\nassert result # Truthy\nassert not result # Falsy\nassert result is True # \u7cbe\u786e\u4e3a True\nassert result is False # \u7cbe\u786e\u4e3a False\nassert result is None # \u7cbe\u786e\u4e3a None\n\n# \u6210\u5458\u8d44\u683c\nassert item in collection\nassert item not in collection\n\n# \u6bd4\u8f83\nassert result > 0\nassert 0 <= result <= 100\n\n# \u7c7b\u578b\u68c0\u67e5\nassert isinstance(result, str)\n\n# \u5f02\u5e38\u6d4b\u8bd5\uff08\u63a8\u8350\u505a\u6cd5\uff09\nwith pytest.raises(ValueError):\n raise ValueError(\"error message\")\n\n# \u68c0\u67e5\u5f02\u5e38\u6d88\u606f\nwith pytest.raises(ValueError, match=\"invalid input\"):\n raise ValueError(\"invalid input provided\")\n\n# \u68c0\u67e5\u5f02\u5e38\u5c5e\u6027\nwith pytest.raises(ValueError) as exc_info:\n raise ValueError(\"error message\")\nassert str(exc_info.value) == \"error message\"\n```\n\n## Fixtures\n\n### \u57fa\u7840 Fixture \u7528\u6cd5\n\n```python\nimport pytest\n\n@pytest.fixture\ndef sample_data():\n \"\"\"\u63d0\u4f9b\u793a\u4f8b\u6570\u636e\u7684 Fixture\u3002\"\"\"\n return {\"name\": \"Alice\", \"age\": 30}\n\ndef test_sample_data(sample_data):\n \"\"\"\u4f7f\u7528 fixture \u7684\u6d4b\u8bd5\u3002\"\"\"\n assert sample_data[\"name\"] == \"Alice\"\n assert sample_data[\"age\"] == 30\n```\n\n### \u5e26\u6709\u8bbe\u7f6e\uff08Setup\uff09\u548c\u6e05\u7406\uff08Teardown\uff09\u7684 Fixture\n\n```python\n@pytest.fixture\ndef database():\n \"\"\"\u5e26\u6709\u8bbe\u7f6e\u548c\u6e05\u7406\u903b\u8f91\u7684 Fixture\u3002\"\"\"\n # \u8bbe\u7f6e (Setup)\n db = Database(\":memory:\")\n db.create_tables()\n db.insert_test_data()\n\n yield db # \u63d0\u4f9b\u7ed9\u6d4b\u8bd5\u4f7f\u7528\n\n # \u6e05\u7406 (Teardown)\n db.close()\n\ndef test_database_query(database):\n \"\"\"\u6d4b\u8bd5\u6570\u636e\u5e93\u64cd\u4f5c\u3002\"\"\"\n result = database.query(\"SELECT * FROM users\")\n assert len(result) > 0\n```\n\n### Fixture \u4f5c\u7528\u57df\uff08Scopes\uff09\n\n```python\n# \u51fd\u6570\u7ea7\u4f5c\u7528\u57df (\u9ed8\u8ba4) - \u6bcf\u4e2a\u6d4b\u8bd5\u8fd0\u884c\u4e00\u6b21\n@pytest.fixture\ndef temp_file():\n with open(\"temp.txt\", \"w\") as f:\n yield f\n os.remove(\"temp.txt\")\n\n# \u6a21\u5757\u7ea7\u4f5c\u7528\u57df - \u6bcf\u4e2a\u6a21\u5757\u8fd0\u884c\u4e00\u6b21\n@pytest.fixture(scope=\"module\")\ndef module_db():\n db = Database(\":memory:\")\n db.create_tables()\n yield db\n db.close()\n\n# \u4f1a\u8bdd\u7ea7\u4f5c\u7528\u57df - \u6574\u4e2a\u6d4b\u8bd5\u4f1a\u8bdd\u8fd0\u884c\u4e00\u6b21\n@pytest.fixture(scope=\"session\")\ndef shared_resource():\n resource = ExpensiveResource()\n yield resource\n resource.cleanup()\n```\n\n### \u5e26\u53c2\u6570\u7684 Fixture\n\n```python\n@pytest.fixture(params=[1, 2, 3])\ndef number(request):\n \"\"\"\u53c2\u6570\u5316 Fixture\u3002\"\"\"\n return request.param\n\ndef test_numbers(number):\n \"\"\"\u6d4b\u8bd5\u5c06\u8fd0\u884c 3 \u6b21\uff0c\u6bcf\u4e2a\u53c2\u6570\u4e00\u6b21\u3002\"\"\"\n assert number > 0\n```\n\n### \u4f7f\u7528\u591a\u4e2a Fixture\n\n```python\n@pytest.fixture\ndef user():\n return User(id=1, name=\"Alice\")\n\n@pytest.fixture\ndef admin():\n return User(id=2, name=\"Admin\", role=\"admin\")\n\ndef test_user_admin_interaction(user, admin):\n \"\"\"\u540c\u65f6\u4f7f\u7528\u591a\u4e2a fixture \u7684\u6d4b\u8bd5\u3002\"\"\"\n assert admin.can_manage(user)\n```\n\n### \u81ea\u52a8\u4f7f\u7528\uff08Autouse\uff09 Fixture\n\n```python\n@pytest.fixture(autouse=True)\ndef reset_config():\n \"\"\"\u5728\u6bcf\u4e2a\u6d4b\u8bd5\u524d\u81ea\u52a8\u8fd0\u884c\u3002\"\"\"\n Config.reset()\n yield\n Config.cleanup()\n\ndef test_without_fixture_call():\n # reset_config \u4f1a\u81ea\u52a8\u8fd0\u884c\n assert Config.get_setting(\"debug\") is False\n```\n\n### \u7528\u4e8e\u5171\u4eab Fixture \u7684 conftest.py\n\n```python\n# tests/conftest.py\nimport pytest\n\n@pytest.fixture\ndef client():\n \"\"\"\u4f9b\u6240\u6709\u6d4b\u8bd5\u5171\u4eab\u7684 fixture\u3002\"\"\"\n app = create_app(testing=True)\n with app.test_client() as client:\n yield client\n\n@pytest.fixture\ndef auth_headers(client):\n \"\"\"\u4e3a API \u6d4b\u8bd5\u751f\u6210\u8ba4\u8bc1\u5934\u3002\"\"\"\n response = client.post(\"/api/login\", json={\n \"username\": \"test\",\n \"password\": \"test\"\n })\n token = response.json[\"token\"]\n return {\"Authorization\": f\"Bearer {token}\"}\n```\n\n## \u53c2\u6570\u5316\uff08Parametrization\uff09\n\n### \u57fa\u7840\u53c2\u6570\u5316\n\n```python\n@pytest.mark.parametrize(\"input,expected\", [\n (\"hello\", \"HELLO\"),\n (\"world\", \"WORLD\"),\n (\"PyThOn\", \"PYTHON\"),\n])\ndef test_uppercase(input, expected):\n \"\"\"\u6d4b\u8bd5\u5c06\u4f7f\u7528\u4e0d\u540c\u7684\u8f93\u5165\u8fd0\u884c 3 \u6b21\u3002\"\"\"\n assert input.upper() == expected\n```\n\n### \u591a\u4e2a\u53c2\u6570\n\n```python\n@pytest.mark.parametrize(\"a,b,expected\", [\n (2, 3, 5),\n (0, 0, 0),\n (-1, 1, 0),\n (100, 200, 300),\n])\ndef test_add(a, b, expected):\n \"\"\"\u4f7f\u7528\u591a\u7ec4\u8f93\u5165\u6d4b\u8bd5\u52a0\u6cd5\u3002\"\"\"\n assert add(a, b) == expected\n```\n\n### \u5e26 ID \u7684\u53c2\u6570\u5316\n\n```python\n@pytest.mark.parametrize(\"input,expected\", [\n (\"valid@email.com\", True),\n (\"invalid\", False),\n (\"@no-domain.com\", False),\n], ids=[\"valid-email\", \"missing-at\", \"missing-domain\"])\ndef test_email_validation(input, expected):\n \"\"\"\u901a\u8fc7\u53ef\u8bfb\u7684\u6d4b\u8bd5 ID \u6d4b\u8bd5\u7535\u5b50\u90ae\u4ef6\u9a8c\u8bc1\u3002\"\"\"\n assert is_valid_email(input) is expected\n```\n\n### \u53c2\u6570\u5316 Fixtures\n\n```python\n@pytest.fixture(params=[\"sqlite\", \"postgresql\", \"mysql\"])\ndef db(request):\n \"\"\"\u9488\u5bf9\u591a\u4e2a\u6570\u636e\u5e93\u540e\u7aef\u8fdb\u884c\u6d4b\u8bd5\u3002\"\"\"\n if request.param == \"sqlite\":\n return Database(\":memory:\")\n elif request.param == \"postgresql\":\n return Database(\"postgresql://localhost/test\")\n elif request.param == \"mysql\":\n return Database(\"mysql://localhost/test\")\n\ndef test_database_operations(db):\n \"\"\"\u6d4b\u8bd5\u5c06\u8fd0\u884c 3 \u6b21\uff0c\u6bcf\u4e2a\u6570\u636e\u5e93\u4e00\u6b21\u3002\"\"\"\n result = db.query(\"SELECT 1\")\n assert result is not None\n```\n\n## \u6807\u8bb0\uff08Markers\uff09\u4e0e\u6d4b\u8bd5\u9009\u62e9\n\n### \u81ea\u5b9a\u4e49\u6807\u8bb0\n\n```python\n# \u6807\u8bb0\u6162\u901f\u6d4b\u8bd5\n@pytest.mark.slow\ndef test_slow_operation():\n time.sleep(5)\n\n# \u6807\u8bb0\u96c6\u6210\u6d4b\u8bd5\n@pytest.mark.integration\ndef test_api_integration():\n response = requests.get(\"https://api.example.com\")\n assert response.status_code == 200\n\n# \u6807\u8bb0\u5355\u5143\u6d4b\u8bd5\n@pytest.mark.unit\ndef test_unit_logic():\n assert calculate(2, 3) == 5\n```\n\n### \u8fd0\u884c\u7279\u5b9a\u6d4b\u8bd5\n\n```bash\n# \u4ec5\u8fd0\u884c\u975e\u6162\u901f\u6d4b\u8bd5\npytest -m \"not slow\"\n\n# \u4ec5\u8fd0\u884c\u96c6\u6210\u6d4b\u8bd5\npytest -m integration\n\n# \u8fd0\u884c\u96c6\u6210\u6d4b\u8bd5\u6216\u6162\u901f\u6d4b\u8bd5\npytest -m \"integration or slow\"\n\n# \u8fd0\u884c\u6807\u8bb0\u4e3a\u5355\u5143\u6d4b\u8bd5\u4e14\u975e\u6162\u901f\u7684\u6d4b\u8bd5\npytest -m \"unit and not slow\"\n```\n\n### \u5728 pytest.ini \u4e2d\u914d\u7f6e\u6807\u8bb0\n\n```ini\n[pytest]\nmarkers =\n slow: \u5c06\u6d4b\u8bd5\u6807\u8bb0\u4e3a\u6162\u901f\n integration: \u5c06\u6d4b\u8bd5\u6807\u8bb0\u4e3a\u96c6\u6210\u6d4b\u8bd5\n unit: \u5c06\u6d4b\u8bd5\u6807\u8bb0\u4e3a\u5355\u5143\u6d4b\u8bd5\n django: \u5c06\u6d4b\u8bd5\u6807\u8bb0\u4e3a\u9700\u8981 Django \u73af\u5883\n```\n\n## Mocking \u4e0e Patching\n\n### Mock \u51fd\u6570\n\n```python\nfrom unittest.mock import patch, Mock\n\n@patch(\"mypackage.external_api_call\")\ndef test_with_mock(api_call_mock):\n \"\"\"\u4f7f\u7528 mock \u7684\u5916\u90e8 API \u8fdb\u884c\u6d4b\u8bd5\u3002\"\"\"\n api_call_mock.return_value = {\"status\": \"success\"}\n\n result = my_function()\n\n api_call_mock.assert_called_once()\n assert result[\"status\"] == \"success\"\n```\n\n### Mock \u8fd4\u56de\u503c\n\n```python\n@patch(\"mypackage.Database.connect\")\ndef test_database_connection(connect_mock):\n \"\"\"\u4f7f\u7528 mock \u7684\u6570\u636e\u5e93\u8fde\u63a5\u8fdb\u884c\u6d4b\u8bd5\u3002\"\"\"\n connect_mock.return_value = MockConnection()\n\n db = Database()\n db.connect()\n\n connect_mock.assert_called_once_with(\"localhost\")\n```\n\n### Mock \u5f02\u5e38\n\n```python\n@patch(\"mypackage.api_call\")\ndef test_api_error_handling(api_call_mock):\n \"\"\"\u4f7f\u7528 mock \u5f02\u5e38\u6d4b\u8bd5\u9519\u8bef\u5904\u7406\u3002\"\"\"\n api_call_mock.side_effect = ConnectionError(\"Network error\")\n\n with pytest.raises(ConnectionError):\n api_call()\n\n api_call_mock.assert_called_once()\n```\n\n### Mock \u4e0a\u4e0b\u6587\u7ba1\u7406\u5668\uff08Context Managers\uff09\n\n```python\n@patch(\"builtins.open\", new_callable=mock_open)\ndef test_file_reading(mock_file):\n \"\"\"\u4f7f\u7528 mock \u7684 open \u6d4b\u8bd5\u6587\u4ef6\u8bfb\u53d6\u3002\"\"\"\n mock_file.return_value.read.return_value = \"file content\"\n\n result = read_file(\"test.txt\")\n\n mock_file.assert_called_once_with(\"test.txt\", \"r\")\n assert result == \"file content\"\n```\n\n### \u4f7f\u7528 Autospec\n\n```python\n@patch(\"mypackage.DBConnection\", autospec=True)\ndef test_autospec(db_mock):\n \"\"\"\u4f7f\u7528 autospec \u6355\u83b7 API \u6ee5\u7528\u3002\"\"\"\n db = db_mock.return_value\n db.query(\"SELECT * FROM users\")\n\n # \u5982\u679c DBConnection \u6ca1\u6709 query \u65b9\u6cd5\uff0c\u6b64\u5904\u5c06\u5931\u8d25\n db_mock.assert_called_once()\n```\n\n### Mock \u7c7b\u5b9e\u4f8b\n\n```python\nclass TestUserService:\n @patch(\"mypackage.UserRepository\")\n def test_create_user(self, repo_mock):\n \"\"\"\u4f7f\u7528 mock \u7684\u4ed3\u5e93\u8fdb\u884c\u7528\u6237\u521b\u5efa\u6d4b\u8bd5\u3002\"\"\"\n repo_mock.return_value.save.return_value = User(id=1, name=\"Alice\")\n\n service = UserService(repo_mock.return_value)\n user = service.create_user(name=\"Alice\")\n\n assert user.name == \"Alice\"\n repo_mock.return_value.save.assert_called_once()\n```\n\n### Mock \u5c5e\u6027\uff08Property\uff09\n\n```python\n@pytest.fixture\ndef mock_config():\n \"\"\"\u521b\u5efa\u4e00\u4e2a\u5e26\u6709\u5c5e\u6027\u7684 mock\u3002\"\"\"\n config = Mock()\n type(config).debug = PropertyMock(return_value=True)\n type(config).api_key = PropertyMock(return_value=\"test-key\")\n return config\n\ndef test_with_mock_config(mock_config):\n \"\"\"\u4f7f\u7528 mock \u914d\u7f6e\u5c5e\u6027\u8fdb\u884c\u6d4b\u8bd5\u3002\"\"\"\n assert mock_config.debug is True\n assert mock_config.api_key == \"test-key\"\n```\n\n## \u6d4b\u8bd5\u5f02\u6b65\u4ee3\u7801\n\n### \u4f7f\u7528 pytest-asyncio \u8fdb\u884c\u5f02\u6b65\u6d4b\u8bd5\n\n```python\nimport pytest\n\n@pytest.mark.asyncio\nasync def test_async_function():\n \"\"\"\u6d4b\u8bd5\u5f02\u6b65\u51fd\u6570\u3002\"\"\"\n result = await async_add(2, 3)\n assert result == 5\n\n@pytest.mark.asyncio\nasync def test_async_with_fixture(async_client):\n \"\"\"\u5728\u5f02\u6b65 fixture \u4e0b\u8fdb\u884c\u5f02\u6b65\u6d4b\u8bd5\u3002\"\"\"\n response = await async_client.get(\"/api/users\")\n assert response.status_code == 200\n```\n\n### \u5f02\u6b65 Fixture\n\n```python\n@pytest.fixture\nasync def async_client():\n \"\"\"\u63d0\u4f9b\u5f02\u6b65\u6d4b\u8bd5\u5ba2\u6237\u7aef\u7684\u5f02\u6b65 Fixture\u3002\"\"\"\n app = create_app()\n async with app.test_client() as client:\n yield client\n\n@pytest.mark.asyncio\nasync def test_api_endpoint(async_client):\n \"\"\"\u4f7f\u7528\u5f02\u6b65 fixture \u8fdb\u884c\u6d4b\u8bd5\u3002\"\"\"\n response = await async_client.get(\"/api/data\")\n assert response.status_code == 200\n```\n\n### Mock \u5f02\u6b65\u51fd\u6570\n\n```python\n@pytest.mark.asyncio\n@patch(\"mypackage.async_api_call\")\nasync def test_async_mock(api_call_mock):\n \"\"\"\u4f7f\u7528 mock \u6d4b\u8bd5\u5f02\u6b65\u51fd\u6570\u3002\"\"\"\n api_call_mock.return_value = {\"status\": \"ok\"}\n\n result = await my_async_function()\n\n api_call_mock.assert_awaited_once()\n assert result[\"status\"] == \"ok\"\n```\n\n## \u6d4b\u8bd5\u5f02\u5e38\n\n### \u6d4b\u8bd5\u9884\u671f\u7684\u5f02\u5e38\n\n```python\ndef test_divide_by_zero():\n \"\"\"\u6d4b\u8bd5\u9664\u4ee5\u96f6\u662f\u5426\u629b\u51fa ZeroDivisionError\u3002\"\"\"\n with pytest.raises(ZeroDivisionError):\n divide(10, 0)\n\ndef test_custom_exception():\n \"\"\"\u4f7f\u7528\u6d88\u606f\u6d4b\u8bd5\u81ea\u5b9a\u4e49\u5f02\u5e38\u3002\"\"\"\n with pytest.raises(ValueError, match=\"invalid input\"):\n validate_input(\"invalid\")\n```\n\n### \u6d4b\u8bd5\u5f02\u5e38\u5c5e\u6027\n\n```python\ndef test_exception_with_details():\n \"\"\"\u6d4b\u8bd5\u5e26\u6709\u81ea\u5b9a\u4e49\u5c5e\u6027\u7684\u5f02\u5e38\u3002\"\"\"\n with pytest.raises(CustomError) as exc_info:\n raise CustomError(\"error\", code=400)\n\n assert exc_info.value.code == 400\n assert \"error\" in str(exc_info.value)\n```\n\n## \u6d4b\u8bd5\u526f\u4f5c\u7528\uff08Side Effects\uff09\n\n### \u6d4b\u8bd5\u6587\u4ef6\u64cd\u4f5c\n\n```python\nimport tempfile\nimport os\n\ndef test_file_processing():\n \"\"\"\u4f7f\u7528\u4e34\u65f6\u6587\u4ef6\u6d4b\u8bd5\u6587\u4ef6\u5904\u7406\u3002\"\"\"\n with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as f:\n f.write(\"test content\")\n temp_path = f.name\n\n try:\n result = process_file(temp_path)\n assert result == \"processed: test content\"\n finally:\n os.unlink(temp_path)\n```\n\n### \u4f7f\u7528 pytest \u7684 tmp_path Fixture \u8fdb\u884c\u6d4b\u8bd5\n\n```python\ndef test_with_tmp_path(tmp_path):\n \"\"\"\u4f7f\u7528 pytest \u5185\u7f6e\u7684\u4e34\u65f6\u8def\u5f84 fixture \u8fdb\u884c\u6d4b\u8bd5\u3002\"\"\"\n test_file = tmp_path / \"test.txt\"\n test_file.write_text(\"hello world\")\n\n result = process_file(str(test_file))\n assert result == \"hello world\"\n # tmp_path \u4f1a\u81ea\u52a8\u6e05\u7406\n```\n\n### \u4f7f\u7528 tmpdir Fixture \u8fdb\u884c\u6d4b\u8bd5\n\n```python\ndef test_with_tmpdir(tmpdir):\n \"\"\"\u4f7f\u7528 pytest \u7684 tmpdir fixture \u8fdb\u884c\u6d4b\u8bd5\u3002\"\"\"\n test_file = tmpdir.join(\"test.txt\")\n test_file.write(\"data\")\n\n result = process_file(str(test_file))\n assert result == \"data\"\n```\n\n## \u6d4b\u8bd5\u7ec4\u7ec7\n\n### \u76ee\u5f55\u7ed3\u6784\n\n```\ntests/\n\u251c\u2500\u2500 conftest.py # \u5171\u4eab\u7684 fixture\n\u251c\u2500\u2500 __init__.py\n\u251c\u2500\u2500 unit/ # \u5355\u5143\u6d4b\u8bd5\n\u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u251c\u2500\u2500 test_models.py\n\u2502 \u251c\u2500\u2500 test_utils.py\n\u2502 \u2514\u2500\u2500 test_services.py\n\u251c\u2500\u2500 integration/ # \u96c6\u6210\u6d4b\u8bd5\n\u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u251c\u2500\u2500 test_api.py\n\u2502 \u2514\u2500\u2500 test_database.py\n\u2514\u2500\u2500 e2e/ # \u7aef\u5230\u7aef\u6d4b\u8bd5\n \u251c\u2500\u2500 __init__.py\n \u2514\u2500\u2500 test_user_flow.py\n```\n\n### \u6d4b\u8bd5\u7c7b\n\n```python\nclass TestUserService:\n \"\"\"\u5728\u7c7b\u4e2d\u7ec4\u7ec7\u76f8\u5173\u7684\u6d4b\u8bd5\u3002\"\"\"\n\n @pytest.fixture(autouse=True)\n def setup(self):\n \"\"\"\u5728\u8be5\u7c7b\u7684\u6bcf\u4e2a\u6d4b\u8bd5\u8fd0\u884c\u524d\u6267\u884c\u8bbe\u7f6e\u3002\"\"\"\n self.service = UserService()\n\n def test_create_user(self):\n \"\"\"\u6d4b\u8bd5\u7528\u6237\u521b\u5efa\u3002\"\"\"\n user = self.service.create_user(\"Alice\")\n assert user.name == \"Alice\"\n\n def test_delete_user(self):\n \"\"\"\u6d4b\u8bd5\u7528\u6237\u5220\u9664\u3002\"\"\"\n user = User(id=1, name=\"Bob\")\n self.service.delete_user(user)\n assert not self.service.user_exists(1)\n```\n\n## \u6700\u4f73\u5b9e\u8df5\n\n### \u5e94\u8be5\u505a\uff08DO\uff09\n\n- **\u9075\u5faa TDD**\uff1a\u5148\u5199\u6d4b\u8bd5\u518d\u5199\u4ee3\u7801\uff08\u7ea2-\u7eff-\u91cd\u6784\uff09\n- **\u53ea\u6d4b\u8bd5\u4e00\u4ef6\u4e8b**\uff1a\u6bcf\u4e2a\u6d4b\u8bd5\u5e94\u8be5\u53ea\u9a8c\u8bc1\u4e00\u79cd\u884c\u4e3a\n- **\u4f7f\u7528\u63cf\u8ff0\u6027\u7684\u540d\u79f0**\uff1a\u5982 `test_user_login_with_invalid_credentials_fails`\n- **\u4f7f\u7528 Fixtures**\uff1a\u901a\u8fc7 fixture \u6d88\u9664\u91cd\u590d\u4ee3\u7801\n- **Mock \u5916\u90e8\u4f9d\u8d56**\uff1a\u4e0d\u8981\u4f9d\u8d56\u5916\u90e8\u670d\u52a1\n- **\u6d4b\u8bd5\u8fb9\u7f18\u60c5\u51b5**\uff1a\u7a7a\u8f93\u5165\u3001None \u503c\u3001\u8fb9\u754c\u6761\u4ef6\n- **\u4ee5 80% \u4ee5\u4e0a\u7684\u8986\u76d6\u7387\u4e3a\u76ee\u6807**\uff1a\u4f18\u5148\u8986\u76d6\u5173\u952e\u8def\u5f84\n- **\u4fdd\u6301\u6d4b\u8bd5\u8fd0\u884c\u8fc5\u901f**\uff1a\u4f7f\u7528\u6807\u8bb0\u533a\u5206\u6162\u901f\u6d4b\u8bd5\n\n### \u4e0d\u8be5\u505a\uff08DON'T\uff09\n\n- **\u4e0d\u8981\u6d4b\u8bd5\u5b9e\u73b0\u7ec6\u8282**\uff1a\u6d4b\u8bd5\u884c\u4e3a\u800c\u975e\u5185\u90e8\u5b9e\u73b0\n- **\u4e0d\u8981\u5728\u6d4b\u8bd5\u4e2d\u4f7f\u7528\u590d\u6742\u7684\u6761\u4ef6\u5224\u65ad**\uff1a\u4fdd\u6301\u6d4b\u8bd5\u903b\u8f91\u7b80\u5355\n- **\u4e0d\u8981\u5ffd\u89c6\u5931\u8d25\u7684\u6d4b\u8bd5**\uff1a\u6240\u6709\u6d4b\u8bd5\u5fc5\u987b\u901a\u8fc7\n- **\u4e0d\u8981\u6d4b\u8bd5\u7b2c\u4e09\u65b9\u4ee3\u7801**\uff1a\u76f8\u4fe1\u5e93\u672c\u8eab\u662f\u5de5\u4f5c\u7684\n- **\u4e0d\u8981\u5728\u6d4b\u8bd5\u95f4\u5171\u4eab\u72b6\u6001**\uff1a\u6d4b\u8bd5\u5e94\u8be5\u662f\u76f8\u4e92\u72ec\u7acb\u7684\n- **\u4e0d\u8981\u5728\u6d4b\u8bd5\u4e2d\u6355\u83b7\u5f02\u5e38**\uff1a\u4f7f\u7528 `pytest.raises`\n- **\u4e0d\u8981\u4f7f\u7528 print \u8bed\u53e5**\uff1a\u4f7f\u7528\u65ad\u8a00\u548c pytest \u7684\u8f93\u51fa\u673a\u5236\n- **\u4e0d\u8981\u7f16\u5199\u8fc7\u4e8e\u8106\u5f31\u7684\u6d4b\u8bd5**\uff1a\u907f\u514d\u8fc7\u5ea6\u7279\u5f02\u5316\u7684 mock\n\n## \u5e38\u89c1\u6a21\u5f0f\n\n### \u6d4b\u8bd5 API \u7aef\u70b9 (FastAPI/Flask)\n\n```python\n@pytest.fixture\ndef client():\n app = create_app(testing=True)\n return app.test_client()\n\ndef test_get_user(client):\n response = client.get(\"/api/users/1\")\n assert response.status_code == 200\n assert response.json[\"id\"] == 1\n\ndef test_create_user(client):\n response = client.post(\"/api/users\", json={\n \"name\": \"Alice\",\n \"email\": \"alice@example.com\"\n })\n assert response.status_code == 201\n assert response.json[\"name\"] == \"Alice\"\n```\n\n### \u6d4b\u8bd5\u6570\u636e\u5e93\u64cd\u4f5c\n\n```python\n@pytest.fixture\ndef db_session():\n \"\"\"\u521b\u5efa\u6d4b\u8bd5\u6570\u636e\u5e93\u4f1a\u8bdd\u3002\"\"\"\n session = Session(bind=engine)\n session.begin_nested()\n yield session\n session.rollback()\n session.close()\n\ndef test_create_user(db_session):\n user = User(name=\"Alice\", email=\"alice@example.com\")\n db_session.add(user)\n db_session.commit()\n\n retrieved = db_session.query(User).filter_by(name=\"Alice\").first()\n assert retrieved.email == \"alice@example.com\"\n```\n\n### \u6d4b\u8bd5\u7c7b\u65b9\u6cd5\n\n```python\nclass TestCalculator:\n @pytest.fixture\n def calculator(self):\n return Calculator()\n\n def test_add(self, calculator):\n assert calculator.add(2, 3) == 5\n\n def test_divide_by_zero(self, calculator):\n with pytest.raises(ZeroDivisionError):\n calculator.divide(10, 0)\n```\n\n## pytest \u914d\u7f6e\n\n### pytest.ini\n\n```ini\n[pytest]\ntestpaths = tests\npython_files = test_*.py\npython_classes = Test*\npython_functions = test_*\naddopts =\n --strict-markers\n --disable-warnings\n --cov=mypackage\n --cov-report=term-missing\n --cov-report=html\nmarkers =\n slow: \u5c06\u6d4b\u8bd5\u6807\u8bb0\u4e3a\u6162\u901f\n integration: \u5c06\u6d4b\u8bd5\u6807\u8bb0\u4e3a\u96c6\u6210\u6d4b\u8bd5\n unit: \u5c06\u6d4b\u8bd5\u6807\u8bb0\u4e3a\u5355\u5143\u6d4b\u8bd5\n```\n\n### pyproject.toml\n\n```toml\n[tool.pytest.ini_options]\ntestpaths = [\"tests\"]\npython_files = [\"test_*.py\"]\npython_classes = [\"Test*\"]\npython_functions = [\"test_*\"]\naddopts = [\n \"--strict-markers\",\n \"--cov=mypackage\",\n \"--cov-report=term-missing\",\n \"--cov-report=html\",\n]\nmarkers = [\n \"slow: \u5c06\u6d4b\u8bd5\u6807\u8bb0\u4e3a\u6162\u901f\",\n \"integration: \u5c06\u6d4b\u8bd5\u6807\u8bb0\u4e3a\u96c6\u6210\u6d4b\u8bd5\",\n \"unit: \u5c06\u6d4b\u8bd5\u6807\u8bb0\u4e3a\u5355\u5143\u6d4b\u8bd5\",\n]\n```\n\n## \u8fd0\u884c\u6d4b\u8bd5\n\n```bash\n# \u8fd0\u884c\u6240\u6709\u6d4b\u8bd5\npytest\n\n# \u8fd0\u884c\u7279\u5b9a\u6587\u4ef6\npytest tests/test_utils.py\n\n# \u8fd0\u884c\u7279\u5b9a\u6d4b\u8bd5\u51fd\u6570\npytest tests/test_utils.py::test_function\n\n# \u8fd0\u884c\u5e76\u8f93\u51fa\u8be6\u7ec6\u7ed3\u679c\npytest -v\n\n# \u8fd0\u884c\u5e76\u751f\u6210\u8986\u76d6\u7387\u62a5\u544a\npytest --cov=mypackage --cov-report=html\n\n# \u4ec5\u8fd0\u884c\u975e\u6162\u901f\u6d4b\u8bd5\npytest -m \"not slow\"\n\n# \u8fd0\u884c\u5e76\u5728\u7b2c\u4e00\u6b21\u5931\u8d25\u65f6\u505c\u6b62\npytest -x\n\n# \u8fd0\u884c\u5e76\u5728\u53d1\u751f N \u6b21\u5931\u8d25\u540e\u505c\u6b62\npytest --maxfail=3\n\n# \u8fd0\u884c\u4e0a\u6b21\u5931\u8d25\u7684\u6d4b\u8bd5\npytest --lf\n\n# \u8fd0\u884c\u5339\u914d\u6a21\u5f0f\u7684\u6d4b\u8bd5\npytest -k \"test_user\"\n\n# \u5931\u8d25\u65f6\u542f\u52a8\u8c03\u8bd5\u5668\npytest --pdb\n```\n\n## \u5feb\u901f\u53c2\u8003\n\n| \u6a21\u5f0f | \u7528\u6cd5 |\n|---------|-------|\n| `pytest.raises()` | \u6d4b\u8bd5\u9884\u671f\u7684\u5f02\u5e38 |\n| `@pytest.fixture()` | \u521b\u5efa\u53ef\u91cd\u7528\u7684\u6d4b\u8bd5 fixture |\n| `@pytest.mark.parametrize()` | \u4f7f\u7528\u591a\u7ec4\u8f93\u5165\u8fd0\u884c\u6d4b\u8bd5 |\n| `@pytest.mark.slow` | \u6807\u8bb0\u6162\u901f\u6d4b\u8bd5 |\n| `pytest -m \"not slow\"` | \u8df3\u8fc7\u6162\u901f\u6d4b\u8bd5 |\n| `@patch()` | Mock \u51fd\u6570\u548c\u7c7b |\n| `tmp_path` fixture | \u81ea\u52a8\u521b\u5efa\u4e34\u65f6\u76ee\u5f55 |\n| `pytest --cov` | \u751f\u6210\u8986\u76d6\u7387\u62a5\u544a |\n| `assert` | \u7b80\u5355\u4e14\u53ef\u8bfb\u7684\u65ad\u8a00 |\n\n**\u8bf7\u8bb0\u4f4f**\uff1a\u6d4b\u8bd5\u4ee3\u7801\u4e5f\u662f\u4ee3\u7801\u3002\u4fdd\u6301\u5b83\u4eec\u6574\u6d01\u3001\u53ef\u8bfb\u4e14\u53ef\u7ef4\u62a4\u3002\u597d\u7684\u6d4b\u8bd5\u80fd\u6355\u83b7 Bug\uff1b\u4f1f\u5927\u7684\u6d4b\u8bd5\u80fd\u9632\u6b62 Bug \u4ea7\u751f\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/skills/springboot-patterns/SKILL.md": {
+ "md5": "147a8fe142bd88f7696500a6a5fd7632",
+ "content": "---\nname: springboot-patterns\ndescription: Spring Boot \u67b6\u6784\u6a21\u5f0f\u3001REST API \u8bbe\u8ba1\u3001\u5206\u5c42\u670d\u52a1\u3001\u6570\u636e\u8bbf\u95ee\u3001\u7f13\u5b58\u3001\u5f02\u6b65\u5904\u7406\u548c\u65e5\u5fd7\u8bb0\u5f55\u3002\u9002\u7528\u4e8e Java Spring Boot \u540e\u7aef\u5f00\u53d1\u5de5\u4f5c\u3002\n---\n\n# Spring Boot \u5f00\u53d1\u6a21\u5f0f (Spring Boot Development Patterns)\n\n\u9002\u7528\u4e8e\u53ef\u6269\u5c55\u3001\u751f\u4ea7\u7ea7\u670d\u52a1\u7684 Spring Boot \u67b6\u6784\u4e0e API \u6a21\u5f0f\u3002\n\n## REST API \u7ed3\u6784\n\n```java\n@RestController\n@RequestMapping(\"/api/markets\")\n@Validated\nclass MarketController {\n private final MarketService marketService;\n\n MarketController(MarketService marketService) {\n this.marketService = marketService;\n }\n\n @GetMapping\n ResponseEntity> list(\n @RequestParam(defaultValue = \"0\") int page,\n @RequestParam(defaultValue = \"20\") int size) {\n Page markets = marketService.list(PageRequest.of(page, size));\n return ResponseEntity.ok(markets.map(MarketResponse::from));\n }\n\n @PostMapping\n ResponseEntity create(@Valid @RequestBody CreateMarketRequest request) {\n Market market = marketService.create(request);\n return ResponseEntity.status(HttpStatus.CREATED).body(MarketResponse.from(market));\n }\n}\n```\n\n## \u4ed3\u50a8\u6a21\u5f0f (Repository Pattern - Spring Data JPA)\n\n```java\npublic interface MarketRepository extends JpaRepository {\n @Query(\"select m from MarketEntity m where m.status = :status order by m.volume desc\")\n List findActive(@Param(\"status\") MarketStatus status, Pageable pageable);\n}\n```\n\n## \u5e26\u4e8b\u52a1\u7684\u670d\u52a1\u5c42 (Service Layer with Transactions)\n\n```java\n@Service\npublic class MarketService {\n private final MarketRepository repo;\n\n public MarketService(MarketRepository repo) {\n this.repo = repo;\n }\n\n @Transactional\n public Market create(CreateMarketRequest request) {\n MarketEntity entity = MarketEntity.from(request);\n MarketEntity saved = repo.save(entity);\n return Market.from(saved);\n }\n}\n```\n\n## DTO \u4e0e\u6821\u9a8c (DTOs and Validation)\n\n```java\npublic record CreateMarketRequest(\n @NotBlank @Size(max = 200) String name,\n @NotBlank @Size(max = 2000) String description,\n @NotNull @FutureOrPresent Instant endDate,\n @NotEmpty List<@NotBlank String> categories) {}\n\npublic record MarketResponse(Long id, String name, MarketStatus status) {\n static MarketResponse from(Market market) {\n return new MarketResponse(market.id(), market.name(), market.status());\n }\n}\n```\n\n## \u5f02\u5e38\u5904\u7406 (Exception Handling)\n\n```java\n@ControllerAdvice\nclass GlobalExceptionHandler {\n @ExceptionHandler(MethodArgumentNotValidException.class)\n ResponseEntity handleValidation(MethodArgumentNotValidException ex) {\n String message = ex.getBindingResult().getFieldErrors().stream()\n .map(e -> e.getField() + \": \" + e.getDefaultMessage())\n .collect(Collectors.joining(\", \"));\n return ResponseEntity.badRequest().body(ApiError.validation(message));\n }\n\n @ExceptionHandler(AccessDeniedException.class)\n ResponseEntity handleAccessDenied() {\n return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ApiError.of(\"Forbidden\"));\n }\n\n @ExceptionHandler(Exception.class)\n ResponseEntity handleGeneric(Exception ex) {\n // \u8bb0\u5f55\u5e26\u6709\u5806\u6808\u8ddf\u8e2a\u7684\u975e\u9884\u671f\u9519\u8bef\n return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)\n .body(ApiError.of(\"Internal server error\"));\n }\n}\n```\n\n## \u7f13\u5b58 (Caching)\n\n\u9700\u8981\u5728\u914d\u7f6e\u7c7b\u4e0a\u6dfb\u52a0 `@EnableCaching`\u3002\n\n```java\n@Service\npublic class MarketCacheService {\n private final MarketRepository repo;\n\n public MarketCacheService(MarketRepository repo) {\n this.repo = repo;\n }\n\n @Cacheable(value = \"market\", key = \"#id\")\n public Market getById(Long id) {\n return repo.findById(id)\n .map(Market::from)\n .orElseThrow(() -> new EntityNotFoundException(\"Market not found\"));\n }\n\n @CacheEvict(value = \"market\", key = \"#id\")\n public void evict(Long id) {}\n}\n```\n\n## \u5f02\u6b65\u5904\u7406 (Async Processing)\n\n\u9700\u8981\u5728\u914d\u7f6e\u7c7b\u4e0a\u6dfb\u52a0 `@EnableAsync`\u3002\n\n```java\n@Service\npublic class NotificationService {\n @Async\n public CompletableFuture sendAsync(Notification notification) {\n // \u53d1\u9001 \u90ae\u4ef6/\u77ed\u4fe1\n return CompletableFuture.completedFuture(null);\n }\n}\n```\n\n## \u65e5\u5fd7\u8bb0\u5f55 (Logging - SLF4J)\n\n```java\n@Service\npublic class ReportService {\n private static final Logger log = LoggerFactory.getLogger(ReportService.class);\n\n public Report generate(Long marketId) {\n log.info(\"generate_report marketId={}\", marketId);\n try {\n // \u4e1a\u52a1\u903b\u8f91\n } catch (Exception ex) {\n log.error(\"generate_report_failed marketId={}\", marketId, ex);\n throw ex;\n }\n return new Report();\n }\n}\n```\n\n## \u4e2d\u95f4\u4ef6 / \u8fc7\u6ee4\u5668 (Middleware / Filters)\n\n```java\n@Component\npublic class RequestLoggingFilter extends OncePerRequestFilter {\n private static final Logger log = LoggerFactory.getLogger(RequestLoggingFilter.class);\n\n @Override\n protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n FilterChain filterChain) throws ServletException, IOException {\n long start = System.currentTimeMillis();\n try {\n filterChain.doFilter(request, response);\n } finally {\n long duration = System.currentTimeMillis() - start;\n log.info(\"req method={} uri={} status={} durationMs={}\",\n request.getMethod(), request.getRequestURI(), response.getStatus(), duration);\n }\n }\n}\n```\n\n## \u5206\u9875\u4e0e\u6392\u5e8f (Pagination and Sorting)\n\n```java\nPageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by(\"createdAt\").descending());\nPage results = marketService.list(page);\n```\n\n## \u5bb9\u9519\u6027\u5916\u90e8\u8c03\u7528 (Error-Resilient External Calls)\n\n```java\npublic T withRetry(Supplier supplier, int maxRetries) {\n int attempts = 0;\n while (true) {\n try {\n return supplier.get();\n } catch (Exception ex) {\n attempts++;\n if (attempts >= maxRetries) {\n throw ex;\n }\n try {\n Thread.sleep((long) Math.pow(2, attempts) * 100L);\n } catch (InterruptedException ie) {\n Thread.currentThread().interrupt();\n throw ex;\n }\n }\n }\n}\n```\n\n## \u9650\u6d41 (Filter + Bucket4j)\n\n**\u5b89\u5168\u6ce8\u610f\u4e8b\u9879**\uff1a`X-Forwarded-For` \u8bf7\u6c42\u5934\u9ed8\u8ba4\u662f\u4e0d\u53ef\u4fe1\u7684\uff0c\u56e0\u4e3a\u5ba2\u6237\u7aef\u53ef\u4ee5\u4f2a\u9020\u5b83\u3002\n\u4ec5\u5728\u4ee5\u4e0b\u60c5\u51b5\u4e0b\u4f7f\u7528\u8f6c\u53d1\u8bf7\u6c42\u5934\uff1a\n1. \u4f60\u7684\u5e94\u7528\u4f4d\u4e8e\u53d7\u4fe1\u4efb\u7684\u53cd\u5411\u4ee3\u7406\uff08nginx\u3001AWS ALB \u7b49\uff09\u4e4b\u540e\n2. \u4f60\u5df2\u5c06 `ForwardedHeaderFilter` \u6ce8\u518c\u4e3a Bean\n3. \u4f60\u5728 application \u5c5e\u6027\u4e2d\u914d\u7f6e\u4e86 `server.forward-headers-strategy=NATIVE` \u6216 `FRAMEWORK`\n4. \u4f60\u7684\u4ee3\u7406\u914d\u7f6e\u4e3a\u8986\u76d6\uff08\u800c\u975e\u8ffd\u52a0\uff09`X-Forwarded-For` \u8bf7\u6c42\u5934\n\n\u5f53 `ForwardedHeaderFilter` \u914d\u7f6e\u6b63\u786e\u65f6\uff0c`request.getRemoteAddr()` \u5c06\u81ea\u52a8\u4ece\u8f6c\u53d1\u5934\u4e2d\u8fd4\u56de\u6b63\u786e\u7684\u5ba2\u6237\u7aef IP\u3002\u5982\u679c\u6ca1\u6709\u6b64\u914d\u7f6e\uff0c\u8bf7\u76f4\u63a5\u4f7f\u7528 `request.getRemoteAddr()`\u2014\u2014\u5b83\u8fd4\u56de\u76f4\u63a5\u8fde\u63a5\u7684 IP\uff0c\u8fd9\u662f\u552f\u4e00\u53ef\u4fe1\u7684\u503c\u3002\n\n```java\n@Component\npublic class RateLimitFilter extends OncePerRequestFilter {\n private final Map buckets = new ConcurrentHashMap<>();\n\n /*\n * \u5b89\u5168\u63d0\u793a\uff1a\u6b64\u8fc7\u6ee4\u5668\u4f7f\u7528 request.getRemoteAddr() \u6765\u8bc6\u522b\u7528\u4e8e\u9650\u6d41\u7684\u5ba2\u6237\u7aef\u3002\n *\n * \u5982\u679c\u4f60\u7684\u5e94\u7528\u4f4d\u4e8e\u53cd\u5411\u4ee3\u7406\uff08nginx\u3001AWS ALB \u7b49\uff09\u4e4b\u540e\uff0c\u4f60\u5fc5\u987b\u914d\u7f6e Spring \n * \u6b63\u786e\u5904\u7406\u8f6c\u53d1\u5934\uff0c\u4ee5\u4fbf\u51c6\u786e\u68c0\u6d4b\u5ba2\u6237\u7aef IP\uff1a\n *\n * 1. \u5728 application.properties/yaml \u4e2d\u8bbe\u7f6e server.forward-headers-strategy=NATIVE \n * (\u9002\u7528\u4e8e\u4e91\u5e73\u53f0) \u6216 FRAMEWORK\n * 2. \u5982\u679c\u4f7f\u7528 FRAMEWORK \u7b56\u7565\uff0c\u8bf7\u6ce8\u518c ForwardedHeaderFilter\uff1a\n *\n * @Bean\n * ForwardedHeaderFilter forwardedHeaderFilter() {\n * return new ForwardedHeaderFilter();\n * }\n *\n * 3. \u786e\u4fdd\u4f60\u7684\u4ee3\u7406\u8986\u76d6\uff08\u800c\u4e0d\u662f\u8ffd\u52a0\uff09X-Forwarded-For \u8bf7\u6c42\u5934\u4ee5\u9632\u6b62\u4f2a\u9020\n * 4. \u4e3a\u4f60\u7684\u5bb9\u5668\u914d\u7f6e server.tomcat.remoteip.trusted-proxies \u6216\u7b49\u6548\u914d\u7f6e\n *\n * \u5982\u679c\u6ca1\u6709\u8fd9\u4e9b\u914d\u7f6e\uff0crequest.getRemoteAddr() \u5c06\u8fd4\u56de\u4ee3\u7406\u670d\u52a1\u5668\u7684 IP\uff0c\u800c\u975e\u5ba2\u6237\u7aef IP\u3002\n * \u4e0d\u8981\u76f4\u63a5\u8bfb\u53d6 X-Forwarded-For \u2014\u2014 \u5728\u6ca1\u6709\u53d7\u4fe1\u4efb\u4ee3\u7406\u5904\u7406\u7684\u60c5\u51b5\u4e0b\uff0c\u5b83\u662f\u6781\u6613\u4f2a\u9020\u7684\u3002\n */\n @Override\n protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n FilterChain filterChain) throws ServletException, IOException {\n // \u4f7f\u7528 getRemoteAddr()\uff0c\u5b83\u5728\u914d\u7f6e\u4e86 ForwardedHeaderFilter \u65f6\u8fd4\u56de\u6b63\u786e\u7684\u5ba2\u6237\u7aef IP\uff0c\n // \u5426\u5219\u8fd4\u56de\u76f4\u63a5\u8fde\u63a5\u7684 IP\u3002\u5728\u6ca1\u6709\u6b63\u786e\u4ee3\u7406\u914d\u7f6e\u7684\u60c5\u51b5\u4e0b\uff0c\u5207\u52ff\u76f4\u63a5\u4fe1\u4efb X-Forwarded-For \u5934\u3002\n String clientIp = request.getRemoteAddr();\n\n Bucket bucket = buckets.computeIfAbsent(clientIp,\n k -> Bucket.builder()\n .addLimit(Bandwidth.classic(100, Refill.greedy(100, Duration.ofMinutes(1))))\n .build());\n\n if (bucket.tryConsume(1)) {\n filterChain.doFilter(request, response);\n } else {\n response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());\n }\n }\n}\n```\n\n## \u540e\u53f0\u4efb\u52a1 (Background Jobs)\n\n\u4f7f\u7528 Spring \u7684 `@Scheduled` \u6216\u96c6\u6210\u961f\u5217\uff08\u5982 Kafka\u3001SQS\u3001RabbitMQ\uff09\u3002\u4fdd\u6301\u5904\u7406\u7a0b\u5e8f\u5177\u6709\u5e42\u7b49\u6027\u548c\u53ef\u89c2\u6d4b\u6027\u3002\n\n## \u53ef\u89c2\u6d4b\u6027 (Observability)\n\n- \u901a\u8fc7 Logback encoder \u5b9e\u73b0\u7ed3\u6784\u5316\u65e5\u5fd7\uff08JSON\uff09\n- \u6307\u6807\uff08Metrics\uff09\uff1aMicrometer + Prometheus/OTel\n- \u94fe\u8def\u8ffd\u8e2a\uff08Tracing\uff09\uff1a\u4f7f\u7528 OpenTelemetry \u6216 Brave \u540e\u7aef\u7684 Micrometer Tracing\n\n## \u751f\u4ea7\u73af\u5883\u9ed8\u8ba4\u5b9e\u8df5 (Production Defaults)\n\n- \u4f18\u5148\u4f7f\u7528\u6784\u9020\u51fd\u6570\u6ce8\u5165\uff0c\u907f\u514d\u5b57\u6bb5\u6ce8\u5165\n- \u4e3a RFC 7807 \u9519\u8bef\u542f\u7528 `spring.mvc.problemdetails.enabled=true` (Spring Boot 3+)\n- \u6839\u636e\u5de5\u4f5c\u8d1f\u8f7d\u914d\u7f6e HikariCP \u8fde\u63a5\u6c60\u5927\u5c0f\u5e76\u8bbe\u7f6e\u8d85\u65f6\n- \u4e3a\u67e5\u8be2\u4f7f\u7528 `@Transactional(readOnly = true)`\n- \u901a\u8fc7 `@NonNull` \u548c `Optional` \u5728\u9002\u5f53\u65f6\u5f3a\u5236\u6267\u884c\u7a7a\u5b89\u5168\uff08null-safety\uff09\n\n**\u5207\u8bb0**\uff1a\u4fdd\u6301\u63a7\u5236\u5668\uff08Controller\uff09\u8584\u3001\u670d\u52a1\uff08Service\uff09\u4e13\u6ce8\u3001\u4ed3\u50a8\uff08Repository\uff09\u7b80\u5355\uff0c\u5e76\u96c6\u4e2d\u5904\u7406\u9519\u8bef\u3002\u9488\u5bf9\u53ef\u7ef4\u62a4\u6027\u548c\u53ef\u6d4b\u8bd5\u6027\u8fdb\u884c\u4f18\u5316\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/skills/springboot-security/SKILL.md": {
+ "md5": "55d6c53064128c7a0ba22b7251c08589",
+ "content": "---\nname: springboot-security\ndescription: Spring Boot \u670d\u52a1\u4e2d\u5173\u4e8e\u8eab\u4efd\u8ba4\u8bc1/\u6388\u6743\uff08authn/authz\uff09\u3001\u6821\u9a8c\u3001CSRF\u3001\u5bc6\u94a5\u7ba1\u7406\u3001\u54cd\u5e94\u5934\u3001\u9650\u6d41\u53ca\u4f9d\u8d56\u5b89\u5168\u7684 Spring Security \u6700\u4f73\u5b9e\u8df5\u3002\n---\n\n# Spring Boot \u5b89\u5168\u5ba1\u67e5\n\n\u5728\u6dfb\u52a0\u8ba4\u8bc1\u3001\u5904\u7406\u8f93\u5165\u3001\u521b\u5efa\u7aef\u70b9\u6216\u5904\u7406\u5bc6\u94a5\u65f6\u4f7f\u7528\u3002\n\n## \u8eab\u4efd\u8ba4\u8bc1\uff08Authentication\uff09\n\n- \u4f18\u5148\u4f7f\u7528\u65e0\u72b6\u6001 JWT \u6216\u5e26\u6709\u64a4\u56de\u5217\u8868\uff08Revocation List\uff09\u7684\u4e0d\u900f\u660e\u4ee4\u724c\uff08Opaque Tokens\uff09\n- \u4e3a\u4f1a\u8bdd\uff08Session\uff09\u4f7f\u7528 `httpOnly`\u3001`Secure`\u3001`SameSite=Strict` \u7684 Cookie\n- \u4f7f\u7528 `OncePerRequestFilter` \u6216\u8d44\u6e90\u670d\u52a1\u5668\u9a8c\u8bc1\u4ee4\u724c\n\n```java\n@Component\npublic class JwtAuthFilter extends OncePerRequestFilter {\n private final JwtService jwtService;\n\n public JwtAuthFilter(JwtService jwtService) {\n this.jwtService = jwtService;\n }\n\n @Override\n protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n FilterChain chain) throws ServletException, IOException {\n String header = request.getHeader(HttpHeaders.AUTHORIZATION);\n if (header != null && header.startsWith(\"Bearer \")) {\n String token = header.substring(7);\n Authentication auth = jwtService.authenticate(token);\n SecurityContextHolder.getContext().setAuthentication(auth);\n }\n chain.doFilter(request, response);\n }\n}\n```\n\n## \u6388\u6743\uff08Authorization\uff09\n\n- \u542f\u7528\u65b9\u6cd5\u7ea7\u5b89\u5168\uff1a`@EnableMethodSecurity`\n- \u4f7f\u7528 `@PreAuthorize(\"hasRole('ADMIN')\")` \u6216 `@PreAuthorize(\"@authz.canEdit(#id)\")`\n- \u9ed8\u8ba4\u62d2\u7edd\uff08Deny by default\uff09\uff1b\u4ec5\u66b4\u9732\u5fc5\u8981\u7684\u6743\u9650\u8303\u56f4\uff08Scopes\uff09\n\n## \u8f93\u5165\u6821\u9a8c\uff08Input Validation\uff09\n\n- \u5728\u63a7\u5236\u5668\uff08Controller\uff09\u4e0a\u914d\u5408\u4f7f\u7528 Bean Validation \u4e0e `@Valid`\n- \u5728 DTO \u4e0a\u5e94\u7528\u7ea6\u675f\uff1a`@NotBlank`\u3001`@Email`\u3001`@Size` \u4ee5\u53ca\u81ea\u5b9a\u4e49\u6821\u9a8c\u5668\n- \u5728\u6e32\u67d3\u4e4b\u524d\uff0c\u901a\u8fc7\u767d\u540d\u5355\u5bf9\u4efb\u4f55 HTML \u8fdb\u884c\u51c0\u5316\uff08Sanitize\uff09\n\n## \u9632\u6b62 SQL \u6ce8\u5165\n\n- \u4f7f\u7528 Spring Data \u5b58\u50a8\u5e93\uff08Repositories\uff09\u6216\u53c2\u6570\u5316\u67e5\u8be2\n- \u5bf9\u4e8e\u539f\u751f\u67e5\u8be2\uff0c\u4f7f\u7528 `:param` \u7ed1\u5b9a\uff1b\u4e25\u7981\u62fc\u63a5\u5b57\u7b26\u4e32\n\n## CSRF \u9632\u62a4\n\n- \u5bf9\u4e8e\u57fa\u4e8e\u6d4f\u89c8\u5668\u4f1a\u8bdd\u7684\u5e94\u7528\uff0c\u4fdd\u6301\u542f\u7528 CSRF\uff1b\u5728\u8868\u5355/\u8bf7\u6c42\u5934\u4e2d\u5305\u542b\u4ee4\u724c\n- \u5bf9\u4e8e\u4f7f\u7528 Bearer \u4ee4\u724c\u7684\u7eaf API\uff0c\u7981\u7528 CSRF \u5e76\u4f9d\u8d56\u65e0\u72b6\u6001\u8ba4\u8bc1\n\n```java\nhttp\n .csrf(csrf -> csrf.disable())\n .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS));\n```\n\n## \u5bc6\u94a5\u7ba1\u7406\uff08Secrets Management\uff09\n\n- \u6e90\u4ee3\u7801\u4e2d\u4e0d\u4fdd\u7559\u5bc6\u94a5\uff1b\u4ece\u73af\u5883\u53d8\u91cf\u6216 Vault \u52a0\u8f7d\n- \u786e\u4fdd `application.yml` \u4e2d\u6ca1\u6709\u51ed\u636e\uff1b\u4f7f\u7528\u5360\u4f4d\u7b26\n- \u5b9a\u671f\u8f6e\u6362\u4ee4\u724c\u548c\u6570\u636e\u5e93\u51ed\u636e\n\n## \u5b89\u5168\u54cd\u5e94\u5934\uff08Security Headers\uff09\n\n```java\nhttp\n .headers(headers -> headers\n .contentSecurityPolicy(csp -> csp\n .policyDirectives(\"default-src 'self'\"))\n .frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)\n .xssProtection(Customizer.withDefaults())\n .referrerPolicy(rp -> rp.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.NO_REFER_ER)));\n```\n\n## \u9650\u6d41\uff08Rate Limiting\uff09\n\n- \u5728\u9ad8\u5f00\u9500\u7aef\u70b9\u4e0a\u5e94\u7528 Bucket4j \u6216\u7f51\u5173\u7ea7\u9650\u6d41\n- \u5bf9\u7a81\u53d1\u6d41\u91cf\u8fdb\u884c\u65e5\u5fd7\u8bb0\u5f55\u5e76\u544a\u8b66\uff1b\u8fd4\u56de 429 \u72b6\u6001\u7801\u53ca\u91cd\u8bd5\u63d0\u793a\n\n## \u4f9d\u8d56\u5b89\u5168\uff08Dependency Security\uff09\n\n- \u5728 CI \u4e2d\u8fd0\u884c OWASP Dependency Check / Snyk\n- \u4fdd\u6301 Spring Boot \u548c Spring Security \u5904\u4e8e\u53d7\u652f\u6301\u7684\u7248\u672c\n- \u53d1\u73b0\u5df2\u77e5 CVE \u65f6\u6784\u5efa\u5931\u8d25\n\n## \u65e5\u5fd7\u4e0e\u4e2a\u4eba\u654f\u611f\u4fe1\u606f\uff08PII\uff09\n\n- \u4e25\u7981\u5728\u65e5\u5fd7\u4e2d\u8bb0\u5f55\u5bc6\u94a5\u3001\u4ee4\u724c\u3001\u5bc6\u7801\u6216\u5b8c\u6574\u7684\u94f6\u884c\u5361\u53f7\uff08PAN\uff09\u6570\u636e\n- \u8131\u654f\u654f\u611f\u5b57\u6bb5\uff1b\u4f7f\u7528\u7ed3\u6784\u5316 JSON \u65e5\u5fd7\u8bb0\u5f55\n\n## \u6587\u4ef6\u4e0a\u4f20\n\n- \u6821\u9a8c\u5927\u5c0f\u3001\u5185\u5bb9\u7c7b\u578b\uff08Content Type\uff09\u548c\u6269\u5c55\u540d\n- \u5b58\u50a8\u5728 Web \u6839\u76ee\u5f55\u4e4b\u5916\uff1b\u5fc5\u8981\u65f6\u8fdb\u884c\u626b\u63cf\n\n## \u53d1\u5e03\u524d\u81ea\u68c0\u6e05\u5355\n\n- [ ] \u8eab\u4efd\u8ba4\u8bc1\u4ee4\u724c\u5df2\u6b63\u786e\u9a8c\u8bc1\u5e76\u914d\u7f6e\u8fc7\u671f\u65f6\u95f4\n- [ ] \u6bcf\u4e2a\u654f\u611f\u8def\u5f84\u90fd\u6709\u6388\u6743\u4fdd\u62a4\n- [ ] \u6240\u6709\u8f93\u5165\u5747\u5df2\u6821\u9a8c\u5e76\u51c0\u5316\n- [ ] \u6ca1\u6709\u5b57\u7b26\u4e32\u62fc\u63a5\u7684 SQL\n- [ ] CSRF \u914d\u7f6e\u7b26\u5408\u5e94\u7528\u7c7b\u578b\n- [ ] \u5bc6\u94a5\u5df2\u5916\u90e8\u5316\uff1b\u672a\u63d0\u4ea4\u4efb\u4f55\u5bc6\u94a5\n- [ ] \u5b89\u5168\u54cd\u5e94\u5934\u5df2\u914d\u7f6e\n- [ ] API \u5df2\u914d\u7f6e\u9650\u6d41\n- [ ] \u4f9d\u8d56\u5df2\u626b\u63cf\u4e14\u4e3a\u6700\u65b0\n- [ ] \u65e5\u5fd7\u4e2d\u4e0d\u5305\u542b\u654f\u611f\u6570\u636e\n\n**\u8bb0\u4f4f**\uff1a\u9ed8\u8ba4\u62d2\u7edd\u3001\u6821\u9a8c\u8f93\u5165\u3001\u6700\u5c0f\u6743\u9650\u539f\u5219\uff0c\u4ee5\u53ca\u914d\u7f6e\u4f18\u5148\u7684\u5b89\u5168\u6027\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/skills/jpa-patterns/SKILL.md": {
+ "md5": "c5484f0bb284efe50b188a3bf99bc51c",
+ "content": "---\nname: jpa-patterns\ndescription: Spring Boot \u4e2d\u7528\u4e8e\u5b9e\u4f53\u8bbe\u8ba1\u3001\u5173\u8054\u5173\u7cfb\u3001\u67e5\u8be2\u4f18\u5316\u3001\u4e8b\u52a1\u3001\u5ba1\u8ba1\u3001\u7d22\u5f15\u3001\u5206\u9875\u548c\u8fde\u63a5\u6c60\u7684 JPA/Hibernate \u6a21\u5f0f\u3002\n---\n\n# JPA/Hibernate \u6a21\u5f0f\n\n\u7528\u4e8e Spring Boot \u4e2d\u7684\u6570\u636e\u5efa\u6a21\u3001\u5b58\u50a8\u5c42\uff08Repositories\uff09\u5f00\u53d1\u548c\u6027\u80fd\u8c03\u4f18\u3002\n\n## \u5b9e\u4f53\u8bbe\u8ba1\uff08Entity Design\uff09\n\n```java\n@Entity\n@Table(name = \"markets\", indexes = {\n @Index(name = \"idx_markets_slug\", columnList = \"slug\", unique = true)\n})\n@EntityListeners(AuditingEntityListener.class)\npublic class MarketEntity {\n @Id @GeneratedValue(strategy = GenerationType.IDENTITY)\n private Long id;\n\n @Column(nullable = false, length = 200)\n private String name;\n\n @Column(nullable = false, unique = true, length = 120)\n private String slug;\n\n @Enumerated(EnumType.STRING)\n private MarketStatus status = MarketStatus.ACTIVE;\n\n @CreatedDate private Instant createdAt;\n @LastModifiedDate private Instant updatedAt;\n}\n```\n\n\u542f\u7528\u5ba1\u8ba1\uff08Auditing\uff09\uff1a\n```java\n@Configuration\n@EnableJpaAuditing\nclass JpaConfig {}\n```\n\n## \u5173\u8054\u5173\u7cfb\u4e0e N+1 \u95ee\u9898\u9884\u9632\n\n```java\n@OneToMany(mappedBy = \"market\", cascade = CascadeType.ALL, orphanRemoval = true)\nprivate List positions = new ArrayList<>();\n```\n\n- \u9ed8\u8ba4\u4f7f\u7528\u61d2\u52a0\u8f7d\uff08Lazy loading\uff09\uff1b\u5fc5\u8981\u65f6\u5728\u67e5\u8be2\u4e2d\u4f7f\u7528 `JOIN FETCH`\n- \u907f\u514d\u5728\u96c6\u5408\u4e0a\u4f7f\u7528\u7acb\u5373\u52a0\u8f7d\uff08`EAGER`\uff09\uff1b\u5bf9\u4e8e\u8bfb\u53d6\u8def\u5f84\uff0c\u4f18\u5148\u4f7f\u7528 DTO \u6295\u5f71\uff08Projections\uff09\n\n```java\n@Query(\"select m from MarketEntity m left join fetch m.positions where m.id = :id\")\nOptional findWithPositions(@Param(\"id\") Long id);\n```\n\n## \u5b58\u50a8\u5c42\u6a21\u5f0f\uff08Repository Patterns\uff09\n\n```java\npublic interface MarketRepository extends JpaRepository {\n Optional findBySlug(String slug);\n\n @Query(\"select m from MarketEntity m where m.status = :status\")\n Page findByStatus(@Param(\"status\") MarketStatus status, Pageable pageable);\n}\n```\n\n- \u4f7f\u7528\u6295\u5f71\uff08Projections\uff09\u8fdb\u884c\u8f7b\u91cf\u7ea7\u67e5\u8be2\uff1a\n```java\npublic interface MarketSummary {\n Long getId();\n String getName();\n MarketStatus getStatus();\n}\nPage findAllBy(Pageable pageable);\n```\n\n## \u4e8b\u52a1\uff08Transactions\uff09\n\n- \u4f7f\u7528 `@Transactional` \u6ce8\u89e3 Service \u65b9\u6cd5\n- \u5728\u8bfb\u53d6\u8def\u5f84\u4e0a\u4f7f\u7528 `@Transactional(readOnly = true)` \u8fdb\u884c\u4f18\u5316\n- \u8c28\u614e\u9009\u62e9\u4f20\u64ad\u884c\u4e3a\uff08Propagation\uff09\uff1b\u907f\u514d\u957f\u4e8b\u52a1\n\n```java\n@Transactional\npublic Market updateStatus(Long id, MarketStatus status) {\n MarketEntity entity = repo.findById(id)\n .orElseThrow(() -> new EntityNotFoundException(\"Market\"));\n entity.setStatus(status);\n return Market.from(entity);\n}\n```\n\n## \u5206\u9875\uff08Pagination\uff09\n\n```java\nPageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by(\"createdAt\").descending());\nPage markets = repo.findByStatus(MarketStatus.ACTIVE, page);\n```\n\n\u5bf9\u4e8e\u6e38\u6807\u5f0f\u5206\u9875\uff08Cursor-like pagination\uff09\uff0c\u8bf7\u5728 JPQL \u4e2d\u5305\u542b `id > :lastId` \u5e76\u914d\u5408\u6392\u5e8f\u3002\n\n## \u7d22\u5f15\u4e0e\u6027\u80fd\n\n- \u4e3a\u5e38\u7528\u8fc7\u6ee4\u5668\uff08`status`\u3001`slug`\u3001\u5916\u952e\uff09\u6dfb\u52a0\u7d22\u5f15\uff08Indexing\uff09\n- \u4f7f\u7528\u5339\u914d\u67e5\u8be2\u6a21\u5f0f\u7684\u590d\u5408\u7d22\u5f15\uff08Composite indexes\uff0c\u5982 `status, created_at`\uff09\n- \u907f\u514d\u4f7f\u7528 `select *`\uff1b\u4ec5\u6295\u5f71\u6240\u9700\u7684\u5217\n- \u4f7f\u7528 `saveAll` \u5e76\u914d\u7f6e `hibernate.jdbc.batch_size` \u8fdb\u884c\u6279\u91cf\u5199\u5165\n\n## \u8fde\u63a5\u6c60\uff08Connection Pooling - HikariCP\uff09\n\n\u63a8\u8350\u5c5e\u6027\uff1a\n```\nspring.datasource.hikari.maximum-pool-size=20\nspring.datasource.hikari.minimum-idle=5\nspring.datasource.hikari.connection-timeout=30000\nspring.datasource.hikari.validation-timeout=5000\n```\n\n\u5bf9\u4e8e PostgreSQL \u7684 LOB \u5904\u7406\uff0c\u8bf7\u6dfb\u52a0\uff1a\n```\nspring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true\n```\n\n## \u7f13\u5b58\uff08Caching\uff09\n\n- \u4e00\u7ea7\u7f13\u5b58\uff081st-level cache\uff09\u662f\u57fa\u4e8e EntityManager \u7684\uff1b\u907f\u514d\u8de8\u4e8b\u52a1\u4fdd\u7559\u5b9e\u4f53\n- \u5bf9\u4e8e\u8bfb\u591a\u5199\u5c11\u7684\u5b9e\u4f53\uff0c\u8c28\u614e\u8003\u8651\u4e8c\u7ea7\u7f13\u5b58\uff082nd-level cache\uff09\uff1b\u9a8c\u8bc1\u9010\u51fa\u7b56\u7565\uff08Eviction strategy\uff09\n\n## \u6570\u636e\u8fc1\u79fb\uff08Migrations\uff09\n\n- \u4f7f\u7528 Flyway \u6216 Liquibase\uff1b\u5728\u751f\u4ea7\u73af\u5883\u4e2d\u7edd\u4e0d\u8981\u4f9d\u8d56 Hibernate \u7684\u81ea\u52a8 DDL\n- \u4fdd\u6301\u8fc1\u79fb\u662f\u5e42\u7b49\uff08Idempotent\uff09\u4e14\u5177\u6709\u589e\u91cf\u6027\u7684\uff1b\u907f\u514d\u5728\u6ca1\u6709\u8ba1\u5212\u7684\u60c5\u51b5\u4e0b\u5220\u9664\u5217\n\n## \u6d4b\u8bd5\u6570\u636e\u8bbf\u95ee\n\n- \u4f18\u5148\u4f7f\u7528 `@DataJpaTest` \u914d\u5408 Testcontainers \u6765\u6a21\u62df\u751f\u4ea7\u73af\u5883\n- \u4f7f\u7528\u65e5\u5fd7\u65ad\u8a00 SQL \u6548\u7387\uff1a\u8bbe\u7f6e `logging.level.org.hibernate.SQL=DEBUG` \u4ee5\u53ca `logging.level.org.hibernate.orm.jdbc.bind=TRACE` \u4ee5\u67e5\u770b\u53c2\u6570\u503c\n\n**\u8bb0\u4f4f**\uff1a\u4fdd\u6301\u5b9e\u4f53\u7cbe\u7b80\u3001\u67e5\u8be2\u610f\u56fe\u660e\u786e\u4e14\u4e8b\u52a1\u7b80\u77ed\u3002\u901a\u8fc7\u6293\u53d6\u7b56\u7565\uff08Fetch strategies\uff09\u548c\u6295\u5f71\uff08Projections\uff09\u6765\u9632\u6b62 N+1 \u95ee\u9898\uff0c\u5e76\u9488\u5bf9\u8bfb/\u5199\u8def\u5f84\u5efa\u7acb\u7d22\u5f15\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/skills/springboot-verification/SKILL.md": {
+ "md5": "7af68f3240d10677733cf0609e943c84",
+ "content": "---\nname: springboot-verification\ndescription: Spring Boot \u9879\u76ee\u7684\u9a8c\u8bc1\u5faa\u73af\uff08Verification loop\uff09\uff1a\u5305\u542b\u6784\u5efa\u3001\u9759\u6001\u5206\u6790\u3001\u5e26\u8986\u76d6\u7387\u7684\u6d4b\u8bd5\u3001\u5b89\u5168\u626b\u63cf\uff0c\u4ee5\u53ca\u5728\u53d1\u5e03\u6216 PR \u524d\u7684\u5dee\u5f02\u8bc4\u5ba1\uff08diff review\uff09\u3002\n---\n\n# Spring Boot \u9a8c\u8bc1\u5faa\u73af\uff08Verification Loop\uff09\n\n\u5728\u63d0\u4ea4 PR \u524d\u3001\u53d1\u751f\u91cd\u5927\u53d8\u66f4\u540e\u4ee5\u53ca\u9884\u90e8\u7f72\u9636\u6bb5\u8fd0\u884c\u6b64\u6d41\u7a0b\u3002\n\n## \u9636\u6bb5 1\uff1a\u6784\u5efa\uff08Build\uff09\n\n```bash\nmvn -T 4 clean verify -DskipTests\n# \u6216\u8005\n./gradlew clean assemble -x test\n```\n\n\u5982\u679c\u6784\u5efa\u5931\u8d25\uff0c\u8bf7\u505c\u6b62\u5e76\u4fee\u590d\u3002\n\n## \u9636\u6bb5 2\uff1a\u9759\u6001\u5206\u6790\uff08Static Analysis\uff09\n\nMaven\uff08\u5e38\u7528\u63d2\u4ef6\uff09\uff1a\n```bash\nmvn -T 4 spotbugs:check pmd:check checkstyle:check\n```\n\nGradle\uff08\u5982\u679c\u5df2\u914d\u7f6e\uff09\uff1a\n```bash\n./gradlew checkstyleMain pmdMain spotbugsMain\n```\n\n## \u9636\u6bb5 3\uff1a\u6d4b\u8bd5 + \u8986\u76d6\u7387\uff08Tests + Coverage\uff09\n\n```bash\nmvn -T 4 test\nmvn jacoco:report # \u9a8c\u8bc1 80% \u4ee5\u4e0a\u7684\u8986\u76d6\u7387\n# \u6216\u8005\n./gradlew test jacocoTestReport\n```\n\n\u62a5\u544a\u6307\u6807\uff1a\n- \u6d4b\u8bd5\u603b\u6570\u3001\u901a\u8fc7/\u5931\u8d25\u6570\u91cf\n- \u8986\u76d6\u7387 %\uff08\u884c/\u5206\u652f\uff09\n\n## \u9636\u6bb5 4\uff1a\u5b89\u5168\u626b\u63cf\uff08Security Scan\uff09\n\n```bash\n# \u4f9d\u8d56\u9879 CVE \u6f0f\u6d1e\u626b\u63cf\nmvn org.owasp:dependency-check-maven:check\n# \u6216\u8005\n./gradlew dependencyCheckAnalyze\n\n# \u5bc6\u94a5\uff08Secrets\uff09\u626b\u63cf (git)\ngit secrets --scan # \u5982\u679c\u5df2\u914d\u7f6e\n```\n\n## \u9636\u6bb5 5\uff1a\u4ee3\u7801\u89c4\u8303/\u683c\u5f0f\u5316\uff08Lint/Format\uff0c\u53ef\u9009\u9608\u503c\uff09\n\n```bash\nmvn spotless:apply # \u5982\u679c\u4f7f\u7528\u4e86 Spotless \u63d2\u4ef6\n./gradlew spotlessApply\n```\n\n## \u9636\u6bb5 6\uff1a\u5dee\u5f02\u8bc4\u5ba1\uff08Diff Review\uff09\n\n```bash\ngit diff --stat\ngit diff\n```\n\n\u81ea\u67e5\u6e05\u5355\uff08Checklist\uff09\uff1a\n- \u672a\u6b8b\u7559\u8c03\u8bd5\u65e5\u5fd7\uff08\u5982 `System.out`\uff0c\u6216\u7f3a\u5c11\u9632\u62a4\u68c0\u67e5\u7684 `log.debug`\uff09\n- \u9519\u8bef\u4fe1\u606f\u548c HTTP \u72b6\u6001\u7801\u5177\u6709\u660e\u786e\u8bed\u4e49\n- \u5728\u5fc5\u8981\u5904\u5df2\u5305\u542b\u4e8b\u52a1\uff08Transactions\uff09\u548c\u6821\u9a8c\uff08Validation\uff09\n- \u914d\u7f6e\u53d8\u66f4\u5df2\u8bb0\u5f55\u5728\u6587\u6863\u4e2d\n\n## \u8f93\u51fa\u6a21\u7248\uff08Output Template\uff09\n\n```\n\u9a8c\u8bc1\u62a5\u544a (VERIFICATION REPORT)\n===================\n\u6784\u5efa (Build): [\u901a\u8fc7/\u5931\u8d25]\n\u9759\u6001\u5206\u6790 (Static): [\u901a\u8fc7/\u5931\u8d25] (spotbugs/pmd/checkstyle)\n\u6d4b\u8bd5 (Tests): [\u901a\u8fc7/\u5931\u8d25] (\u901a\u8fc7 X/Y\uff0c\u8986\u76d6\u7387 Z%)\n\u5b89\u5168 (Security): [\u901a\u8fc7/\u5931\u8d25] (CVE \u53d1\u73b0\u6570\u91cf: N)\n\u5dee\u5f02 (Diff): [X \u4e2a\u6587\u4ef6\u5df2\u53d8\u66f4]\n\n\u7ed3\u8bba (Overall): [\u5c31\u7eea / \u672a\u5c31\u7eea]\n\n\u5f85\u4fee\u590d\u95ee\u9898:\n1. ...\n2. ...\n```\n\n## \u6301\u7eed\u6a21\u5f0f\uff08Continuous Mode\uff09\n\n- \u5728\u53d1\u751f\u663e\u8457\u53d8\u66f4\u65f6\uff0c\u6216\u5728\u957f\u4f1a\u8bdd\u4e2d\u6bcf 30\u201360 \u5206\u949f\u91cd\u65b0\u8fd0\u884c\u5404\u9636\u6bb5\u3002\n- \u4fdd\u6301\u77ed\u53cd\u9988\u5faa\u73af\uff1a\u8fd0\u884c `mvn -T 4 test` + spotbugs \u4ee5\u83b7\u5f97\u5feb\u901f\u53cd\u9988\u3002\n\n**\u8bb0\u4f4f**\uff1a\u5feb\u901f\u53cd\u9988\u4f18\u4e8e\u540e\u671f\u60ca\u8bb6\u3002\u4fdd\u6301\u4e25\u683c\u7684\u51c6\u5165\u95e8\u69db\u2014\u2014\u5728\u751f\u4ea7\u7cfb\u7edf\u4e2d\uff0c\u5c06\u8b66\u544a\uff08Warnings\uff09\u89c6\u4e3a\u7f3a\u9677\uff08Defects\uff09\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/skills/django-verification/SKILL.md": {
+ "md5": "e7b594d10b68d40883c7b6903e64a6ba",
+ "content": "---\nname: django-verification\ndescription: Django \u9879\u76ee\u7684\u9a8c\u8bc1\u5faa\u73af\uff08Verification loop\uff09\uff1a\u5305\u542b\u6570\u636e\u5e93\u8fc1\u79fb\u3001\u4ee3\u7801\u68c0\u67e5\u3001\u5e26\u8986\u76d6\u7387\u7684\u6d4b\u8bd5\u3001\u5b89\u5168\u626b\u63cf\uff0c\u4ee5\u53ca\u5728\u53d1\u5e03\u6216 PR \u524d\u7684\u90e8\u7f72\u5c31\u7eea\u68c0\u67e5\u3002\n---\n\n# Django \u9a8c\u8bc1\u5faa\u73af\uff08Verification Loop\uff09\n\n\u5728\u63d0\u4ea4 PR \u4e4b\u524d\u3001\u91cd\u5927\u53d8\u66f4\u4e4b\u540e\u4ee5\u53ca\u9884\u90e8\u7f72\uff08Pre-deploy\uff09\u4e4b\u524d\u8fd0\u884c\uff0c\u4ee5\u786e\u4fdd Django \u5e94\u7528\u7a0b\u5e8f\u7684\u8d28\u91cf\u548c\u5b89\u5168\u6027\u3002\n\n## \u9636\u6bb5 1\uff1a\u73af\u5883\u68c0\u67e5 (Phase 1: Environment Check)\n\n```bash\n# \u9a8c\u8bc1 Python \u7248\u672c\npython --version # \u5e94\u7b26\u5408\u9879\u76ee\u8981\u6c42\n\n# \u68c0\u67e5\u865a\u62df\u73af\u5883\nwhich python\npip list --outdated\n\n# \u9a8c\u8bc1\u73af\u5883\u53d8\u91cf\npython -c \"import os; import environ; print('DJANGO_SECRET_KEY set' if os.environ.get('DJANGO_SECRET_KEY') else 'MISSING: DJANGO_SECRET_KEY')\"\n```\n\n\u5982\u679c\u73af\u5883\u914d\u7f6e\u9519\u8bef\uff0c\u8bf7\u505c\u6b62\u5e76\u4fee\u590d\u3002\n\n## \u9636\u6bb5 2\uff1a\u4ee3\u7801\u8d28\u91cf\u4e0e\u683c\u5f0f\u5316 (Phase 2: Code Quality & Formatting)\n\n```bash\n# \u7c7b\u578b\u68c0\u67e5\nmypy . --config-file pyproject.toml\n\n# \u4f7f\u7528 ruff \u8fdb\u884c Lint \u68c0\u67e5\nruff check . --fix\n\n# \u4f7f\u7528 black \u8fdb\u884c\u683c\u5f0f\u5316\nblack . --check\nblack . # \u81ea\u52a8\u4fee\u590d\n\n# \u5bfc\u5165\u8bed\u53e5\u6392\u5e8f\nisort . --check-only\nisort . # \u81ea\u52a8\u4fee\u590d\n\n# Django \u7279\u6709\u7684\u68c0\u67e5\npython manage.py check --deploy\n```\n\n\u5e38\u89c1\u95ee\u9898\uff1a\n- \u516c\u5171\u51fd\u6570\u7f3a\u5931\u7c7b\u578b\u63d0\u793a\uff08Type hints\uff09\n- \u8fdd\u53cd PEP 8 \u683c\u5f0f\u89c4\u8303\n- \u672a\u6392\u5e8f\u7684\u5bfc\u5165\u8bed\u53e5\n- \u751f\u4ea7\u73af\u5883\u914d\u7f6e\u4e2d\u9057\u7559\u4e86\u8c03\u8bd5\u8bbe\u7f6e\n\n## \u9636\u6bb5 3\uff1a\u6570\u636e\u5e93\u8fc1\u79fb (Phase 3: Migrations)\n\n```bash\n# \u68c0\u67e5\u662f\u5426\u5b58\u5728\u672a\u5e94\u7528\u7684\u8fc1\u79fb\npython manage.py showmigrations\n\n# \u521b\u5efa\u7f3a\u5931\u7684\u8fc1\u79fb\npython manage.py makemigrations --check\n\n# \u6a21\u62df\u8fc1\u79fb\u5e94\u7528\uff08Dry-run\uff09\npython manage.py migrate --plan\n\n# \u5e94\u7528\u8fc1\u79fb\uff08\u6d4b\u8bd5\u73af\u5883\uff09\npython manage.py migrate\n\n# \u68c0\u67e5\u8fc1\u79fb\u51b2\u7a81\npython manage.py makemigrations --merge # \u4ec5\u5728\u5b58\u5728\u51b2\u7a81\u65f6\u8fd0\u884c\n```\n\n\u62a5\u544a\u5185\u5bb9\uff1a\n- \u5f85\u5904\u7406\u8fc1\u79fb\u7684\u6570\u91cf\n- \u4efb\u4f55\u8fc1\u79fb\u51b2\u7a81\n- \u5b58\u5728\u6a21\u578b\u53d8\u66f4\u4f46\u672a\u751f\u6210\u8fc1\u79fb\n\n## \u9636\u6bb5 4\uff1a\u6d4b\u8bd5\u4e0e\u8986\u76d6\u7387 (Phase 4: Tests + Coverage)\n\n```bash\n# \u4f7f\u7528 pytest \u8fd0\u884c\u6240\u6709\u6d4b\u8bd5\npytest --cov=apps --cov-report=html --cov-report=term-missing --reuse-db\n\n# \u8fd0\u884c\u7279\u5b9a\u5e94\u7528\u7684\u6d4b\u8bd5\npytest apps/users/tests/\n\n# \u4f7f\u7528\u6807\u8bb0\u8fd0\u884c\npytest -m \"not slow\" # \u8df3\u8fc7\u6162\u901f\u6d4b\u8bd5\npytest -m integration # \u4ec5\u8fd0\u884c\u96c6\u6210\u6d4b\u8bd5\n\n# \u8986\u76d6\u7387\u62a5\u544a\nopen htmlcov/index.html\n```\n\n\u62a5\u544a\u5185\u5bb9\uff1a\n- \u6d4b\u8bd5\u7edf\u8ba1\uff1aX \u901a\u8fc7\uff0cY \u5931\u8d25\uff0cZ \u8df3\u8fc7\n- \u603b\u4f53\u8986\u76d6\u7387\uff1aXX%\n- \u5404\u5e94\u7528\u7684\u8986\u76d6\u7387\u660e\u7ec6\n\n\u8986\u76d6\u7387\u76ee\u6807\uff1a\n\n| \u7ec4\u4ef6 | \u76ee\u6807 |\n|-----------|--------|\n| \u6a21\u578b (Models) | 90%+ |\n| \u5e8f\u5217\u5316\u5668 (Serializers) | 85%+ |\n| \u89c6\u56fe (Views) | 80%+ |\n| \u670d\u52a1 (Services) | 90%+ |\n| \u603b\u4f53 | 80%+ |\n\n## \u9636\u6bb5 5\uff1a\u5b89\u5168\u626b\u63cf (Phase 5: Security Scan)\n\n```bash\n# \u4f9d\u8d56\u9879\u6f0f\u6d1e\u626b\u63cf\npip-audit\nsafety check --full-report\n\n# Django \u5b89\u5168\u68c0\u67e5\npython manage.py check --deploy\n\n# Bandit \u5b89\u5168 Linter\nbandit -r . -f json -o bandit-report.json\n\n# \u5bc6\u94a5\u626b\u63cf\uff08\u5982\u679c\u5b89\u88c5\u4e86 gitleaks\uff09\ngitleaks detect --source . --verbose\n\n# \u73af\u5883\u53d8\u91cf\u68c0\u67e5\npython -c \"from django.core.exceptions import ImproperlyConfigured; from django.conf import settings; settings.DEBUG\"\n```\n\n\u62a5\u544a\u5185\u5bb9\uff1a\n- \u53d1\u73b0\u7684\u6709\u6f0f\u6d1e\u7684\u4f9d\u8d56\u9879\n- \u5b89\u5168\u914d\u7f6e\u95ee\u9898\n- \u68c0\u6d4b\u5230\u7684\u786c\u7f16\u7801\u5bc6\u94a5\n- DEBUG \u6a21\u5f0f\u72b6\u6001\uff08\u5728\u751f\u4ea7\u73af\u5883\u4e2d\u5e94\u4e3a False\uff09\n\n## \u9636\u6bb5 6\uff1aDjango \u7ba1\u7406\u547d\u4ee4 (Phase 6: Django Management Commands)\n\n```bash\n# \u68c0\u67e5\u6a21\u578b\u95ee\u9898\npython manage.py check\n\n# \u6536\u96c6\u9759\u6001\u6587\u4ef6\npython manage.py collectstatic --noinput --clear\n\n# \u521b\u5efa\u8d85\u7ea7\u7528\u6237\uff08\u5982\u679c\u6d4b\u8bd5\u9700\u8981\uff09\necho \"from apps.users.models import User; User.objects.create_superuser('admin@example.com', 'admin')\" | python manage.py shell\n\n# \u6570\u636e\u5e93\u5b8c\u6574\u6027\u68c0\u67e5\npython manage.py check --database default\n\n# \u7f13\u5b58\u9a8c\u8bc1\uff08\u5982\u679c\u4f7f\u7528 Redis\uff09\npython -c \"from django.core.cache import cache; cache.set('test', 'value', 10); print(cache.get('test'))\"\n```\n\n## \u9636\u6bb5 7\uff1a\u6027\u80fd\u68c0\u67e5 (Phase 7: Performance Checks)\n\n```bash\n# Django Debug Toolbar \u8f93\u51fa\uff08\u68c0\u67e5 N+1 \u67e5\u8be2\uff09\n# \u5728 DEBUG=True \u7684\u5f00\u53d1\u6a21\u5f0f\u4e0b\u8fd0\u884c\u5e76\u8bbf\u95ee\u9875\u9762\n# \u5728 SQL \u9762\u677f\u4e2d\u67e5\u627e\u91cd\u590d\u67e5\u8be2\n\n# \u67e5\u8be2\u8ba1\u6570\u5206\u6790\ndjango-admin debugsqlshell # \u5982\u679c\u5b89\u88c5\u4e86 django-debug-sqlshell\n\n# \u68c0\u67e5\u7f3a\u5931\u7684\u7d22\u5f15\npython manage.py shell << EOF\nfrom django.db import connection\nwith connection.cursor() as cursor:\n cursor.execute(\"SELECT table_name, index_name FROM information_schema.statistics WHERE table_schema = 'public'\")\n print(cursor.fetchall())\nEOF\n```\n\n\u62a5\u544a\u5185\u5bb9\uff1a\n- \u6bcf\u4e2a\u9875\u9762\u7684\u67e5\u8be2\u6570\u91cf\uff08\u5178\u578b\u9875\u9762\u5e94 < 50\uff09\n- \u7f3a\u5931\u7684\u6570\u636e\u5e93\u7d22\u5f15\n- \u68c0\u6d4b\u5230\u7684\u91cd\u590d\u67e5\u8be2\n\n## \u9636\u6bb5 8\uff1a\u9759\u6001\u8d44\u6e90 (Phase 8: Static Assets)\n\n```bash\n# \u68c0\u67e5 npm \u4f9d\u8d56\u9879\uff08\u5982\u679c\u4f7f\u7528 npm\uff09\nnpm audit\nnpm audit fix\n\n# \u6784\u5efa\u9759\u6001\u6587\u4ef6\uff08\u5982\u679c\u4f7f\u7528 webpack/vite\uff09\nnpm run build\n\n# \u9a8c\u8bc1\u9759\u6001\u6587\u4ef6\nls -la staticfiles/\npython manage.py findstatic css/style.css\n```\n\n## \u9636\u6bb5 9\uff1a\u914d\u7f6e\u5ba1\u67e5 (Phase 9: Configuration Review)\n\n```python\n# \u5728 Python shell \u4e2d\u8fd0\u884c\u4ee5\u9a8c\u8bc1\u8bbe\u7f6e\npython manage.py shell << EOF\nfrom django.conf import settings\nimport os\n\n# \u5173\u952e\u68c0\u67e5\u9879\nchecks = {\n 'DEBUG is False': not settings.DEBUG,\n 'SECRET_KEY set': bool(settings.SECRET_KEY and len(settings.SECRET_KEY) > 30),\n 'ALLOWED_HOSTS set': len(settings.ALLOWED_HOSTS) > 0,\n 'HTTPS enabled': getattr(settings, 'SECURE_SSL_REDIRECT', False),\n 'HSTS enabled': getattr(settings, 'SECURE_HSTS_SECONDS', 0) > 0,\n 'Database configured': settings.DATABASES['default']['ENGINE'] != 'django.db.backends.sqlite3',\n}\n\nfor check, result in checks.items():\n status = '\u2713' if result else '\u2717'\n print(f\"{status} {check}\")\nEOF\n```\n\n## \u9636\u6bb5 10\uff1a\u65e5\u5fd7\u914d\u7f6e (Phase 10: Logging Configuration)\n\n```bash\n# \u6d4b\u8bd5\u65e5\u5fd7\u8f93\u51fa\npython manage.py shell << EOF\nimport logging\nlogger = logging.getLogger('django')\nlogger.warning('Test warning message')\nlogger.error('Test error message')\nEOF\n\n# \u68c0\u67e5\u65e5\u5fd7\u6587\u4ef6\uff08\u5982\u679c\u5df2\u914d\u7f6e\uff09\ntail -f /var/log/django/django.log\n```\n\n## \u9636\u6bb5 11\uff1aAPI \u6587\u6863 (Phase 11: API Documentation)\n\n```bash\n# \u751f\u6210 Schema\npython manage.py generateschema --format openapi-json > schema.json\n\n# \u9a8c\u8bc1 Schema\n# \u68c0\u67e5 schema.json \u662f\u5426\u4e3a\u6709\u6548\u7684 JSON\npython -c \"import json; json.load(open('schema.json'))\"\n\n# \u8bbf\u95ee Swagger UI\uff08\u5982\u679c\u4f7f\u7528 drf-yasg\uff09\n# \u5728\u6d4f\u89c8\u5668\u4e2d\u8bbf\u95ee http://localhost:8000/swagger/\n```\n\n## \u9636\u6bb5 12\uff1a\u5dee\u5f02\u5ba1\u67e5 (Phase 12: Diff Review)\n\n```bash\n# \u663e\u793a diff \u7edf\u8ba1\u4fe1\u606f\ngit diff --stat\n\n# \u663e\u793a\u5b9e\u9645\u53d8\u66f4\ngit diff\n\n# \u663e\u793a\u5df2\u53d8\u66f4\u7684\u6587\u4ef6\ngit diff --name-only\n\n# \u68c0\u67e5\u5e38\u89c1\u95ee\u9898\ngit diff | grep -i \"todo\\|fixme\\|hack\\|xxx\"\ngit diff | grep \"print(\" # \u8c03\u8bd5\u8bed\u53e5\ngit diff | grep \"DEBUG = True\" # \u8c03\u8bd5\u6a21\u5f0f\ngit diff | grep \"import pdb\" # \u8c03\u8bd5\u5668\n```\n\n\u68c0\u67e5\u6e05\u5355\uff1a\n- \u65e0\u8c03\u8bd5\u8bed\u53e5\uff08print, pdb, breakpoint()\uff09\n- \u5173\u952e\u4ee3\u7801\u4e2d\u65e0 TODO/FIXME \u6ce8\u91ca\n- \u65e0\u786c\u7f16\u7801\u7684\u5bc6\u94a5\u6216\u51ed\u636e\n- \u6a21\u578b\u53d8\u66f4\u5df2\u5305\u542b\u6570\u636e\u5e93\u8fc1\u79fb\n- \u914d\u7f6e\u53d8\u66f4\u5df2\u8bb0\u5f55\u6587\u6863\n- \u5916\u90e8\u8c03\u7528\u5df2\u5305\u542b\u9519\u8bef\u5904\u7406\n- \u89c6\u9700\u8981\u5305\u542b\u4e8b\u52a1\u7ba1\u7406\uff08Transaction management\uff09\n\n## \u8f93\u51fa\u6a21\u677f (Output Template)\n\n```\nDJANGO \u9a8c\u8bc1\u62a5\u544a\n==========================\n\n\u9636\u6bb5 1\uff1a\u73af\u5883\u68c0\u67e5 (Phase 1: Environment Check)\n \u2713 Python 3.11.5\n \u2713 \u865a\u62df\u73af\u5883\u5df2\u6fc0\u6d3b\n \u2713 \u6240\u6709\u73af\u5883\u53d8\u91cf\u5df2\u8bbe\u7f6e\n\n\u9636\u6bb5 2\uff1a\u4ee3\u7801\u8d28\u91cf (Phase 2: Code Quality)\n \u2713 mypy: \u65e0\u7c7b\u578b\u9519\u8bef\n \u2717 ruff: \u53d1\u73b0 3 \u4e2a\u95ee\u9898\uff08\u5df2\u81ea\u52a8\u4fee\u590d\uff09\n \u2713 black: \u65e0\u683c\u5f0f\u5316\u95ee\u9898\n \u2713 isort: \u5bfc\u5165\u8bed\u53e5\u5df2\u6392\u5e8f\n \u2713 manage.py check: \u65e0\u95ee\u9898\n\n\u9636\u6bb5 3\uff1a\u6570\u636e\u5e93\u8fc1\u79fb (Phase 3: Migrations)\n \u2713 \u65e0\u672a\u5e94\u7528\u7684\u8fc1\u79fb\n \u2713 \u65e0\u8fc1\u79fb\u51b2\u7a81\n \u2713 \u6240\u6709\u6a21\u578b\u5747\u6709\u8fc1\u79fb\n\n\u9636\u6bb5 4\uff1a\u6d4b\u8bd5\u4e0e\u8986\u76d6\u7387 (Phase 4: Tests + Coverage)\n \u6d4b\u8bd5\uff1a247 \u901a\u8fc7\uff0c0 \u5931\u8d25\uff0c5 \u8df3\u8fc7\n \u8986\u76d6\u7387\uff1a\n \u603b\u4f53\uff1a87%\n users: 92%\n products: 89%\n orders: 85%\n payments: 91%\n\n\u9636\u6bb5 5\uff1a\u5b89\u5168\u626b\u63cf (Phase 5: Security Scan)\n \u2717 pip-audit: \u53d1\u73b0 2 \u4e2a\u6f0f\u6d1e\uff08\u9700\u8981\u4fee\u590d\uff09\n \u2713 safety check: \u65e0\u95ee\u9898\n \u2713 bandit: \u65e0\u5b89\u5168\u95ee\u9898\n \u2713 \u672a\u68c0\u6d4b\u5230\u5bc6\u94a5\n \u2713 DEBUG = False\n\n\u9636\u6bb5 6\uff1aDjango \u547d\u4ee4 (Phase 6: Django Commands)\n \u2713 collectstatic \u5df2\u5b8c\u6210\n \u2713 \u6570\u636e\u5e93\u5b8c\u6574\u6027\u6b63\u5e38\n \u2713 \u7f13\u5b58\u540e\u7aef\u53ef\u8fde\u63a5\n\n\u9636\u6bb5 7\uff1a\u6027\u80fd (Phase 7: Performance)\n \u2713 \u672a\u68c0\u6d4b\u5230 N+1 \u67e5\u8be2\n \u2713 \u6570\u636e\u5e93\u7d22\u5f15\u5df2\u914d\u7f6e\n \u2713 \u67e5\u8be2\u8ba1\u6570\u5728\u53ef\u63a5\u53d7\u8303\u56f4\u5185\n\n\u9636\u6bb5 8\uff1a\u9759\u6001\u8d44\u6e90 (Phase 8: Static Assets)\n \u2713 npm audit: \u65e0\u6f0f\u6d1e\n \u2713 \u8d44\u6e90\u6784\u5efa\u6210\u529f\n \u2713 \u9759\u6001\u6587\u4ef6\u5df2\u6536\u96c6\n\n\u9636\u6bb5 9\uff1a\u914d\u7f6e\u5ba1\u67e5 (Phase 9: Configuration)\n \u2713 DEBUG = False\n \u2713 SECRET_KEY \u5df2\u914d\u7f6e\n \u2713 ALLOWED_HOSTS \u5df2\u8bbe\u7f6e\n \u2713 HTTPS \u5df2\u542f\u7528\n \u2713 HSTS \u5df2\u542f\u7528\n \u2713 \u6570\u636e\u5e93\u5df2\u914d\u7f6e\n\n\u9636\u6bb5 10\uff1a\u65e5\u5fd7 (Phase 10: Logging)\n \u2713 \u65e5\u5fd7\u5df2\u914d\u7f6e\n \u2713 \u65e5\u5fd7\u6587\u4ef6\u53ef\u5199\n\n\u9636\u6bb5 11\uff1aAPI \u6587\u6863 (Phase 11: API Documentation)\n \u2713 Schema \u5df2\u751f\u6210\n \u2713 Swagger UI \u53ef\u8bbf\u95ee\n\n\u9636\u6bb5 12\uff1a\u5dee\u5f02\u5ba1\u67e5 (Phase 12: Diff Review)\n \u6587\u4ef6\u53d8\u66f4\uff1a12\n +450, -120 \u884c\n \u2713 \u65e0\u8c03\u8bd5\u8bed\u53e5\n \u2713 \u65e0\u786c\u7f16\u7801\u5bc6\u94a5\n \u2713 \u5df2\u5305\u542b\u8fc1\u79fb\n\n\u5efa\u8bae\uff1a\u26a0\ufe0f \u5728\u90e8\u7f72\u524d\u4fee\u590d pip-audit \u6f0f\u6d1e\n\n\u540e\u7eed\u6b65\u9aa4\uff1a\n1. \u66f4\u65b0\u6709\u6f0f\u6d1e\u7684\u4f9d\u8d56\u9879\n2. \u91cd\u65b0\u8fd0\u884c\u5b89\u5168\u626b\u63cf\n3. \u90e8\u7f72\u5230\u9884\u53d1\u5e03\u73af\u5883\u8fdb\u884c\u6700\u7ec8\u6d4b\u8bd5\n```\n\n## \u9884\u90e8\u7f72\u68c0\u67e5\u6e05\u5355 (Pre-Deployment Checklist)\n\n- [ ] \u6240\u6709\u6d4b\u8bd5\u5747\u901a\u8fc7\n- [ ] \u8986\u76d6\u7387 \u2265 80%\n- [ ] \u65e0\u5b89\u5168\u6f0f\u6d1e\n- [ ] \u65e0\u672a\u5e94\u7528\u7684\u8fc1\u79fb\n- [ ] \u751f\u4ea7\u73af\u5883\u8bbe\u7f6e\u4e2d DEBUG = False\n- [ ] SECRET_KEY \u5df2\u6b63\u786e\u914d\u7f6e\n- [ ] ALLOWED_HOSTS \u5df2\u6b63\u786e\u8bbe\u7f6e\n- [ ] \u6570\u636e\u5e93\u5907\u4efd\u5df2\u542f\u7528\n- [ ] \u9759\u6001\u6587\u4ef6\u5df2\u6536\u96c6\u5e76\u63d0\u4f9b\u670d\u52a1\n- [ ] \u65e5\u5fd7\u5df2\u914d\u7f6e\u5e76\u6b63\u5e38\u5de5\u4f5c\n- [ ] \u9519\u8bef\u76d1\u63a7\uff08Sentry \u7b49\uff09\u5df2\u914d\u7f6e\n- [ ] CDN \u5df2\u914d\u7f6e\uff08\u5982\u9002\u7528\uff09\n- [ ] Redis/\u7f13\u5b58\u540e\u7aef\u5df2\u914d\u7f6e\n- [ ] Celery worker \u6b63\u5728\u8fd0\u884c\uff08\u5982\u9002\u7528\uff09\n- [ ] HTTPS/SSL \u5df2\u914d\u7f6e\n- [ ] \u73af\u5883\u53d8\u91cf\u5df2\u8bb0\u5f55\u6587\u6863\n\n## \u6301\u7eed\u96c6\u6210 (Continuous Integration)\n\n### GitHub Actions \u793a\u4f8b\n\n```yaml\n# .github/workflows/django-verification.yml\nname: Django Verification\n\non: [push, pull_request]\n\njobs:\n verify:\n runs-on: ubuntu-latest\n services:\n postgres:\n image: postgres:14\n env:\n POSTGRES_PASSWORD: postgres\n options: >-\n --health-cmd pg_isready\n --health-interval 10s\n --health-timeout 5s\n --health-retries 5\n\n steps:\n - uses: actions/checkout@v3\n\n - name: Set up Python\n uses: actions/setup-python@v4\n with:\n python-version: '3.11'\n\n - name: Cache pip\n uses: actions/cache@v3\n with:\n path: ~/.cache/pip\n key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}\n\n - name: Install dependencies\n run: |\n pip install -r requirements.txt\n pip install ruff black mypy pytest pytest-django pytest-cov bandit safety pip-audit\n\n - name: Code quality checks\n run: |\n ruff check .\n black . --check\n isort . --check-only\n mypy .\n\n - name: Security scan\n run: |\n bandit -r . -f json -o bandit-report.json\n safety check --full-report\n pip-audit\n\n - name: Run tests\n env:\n DATABASE_URL: postgres://postgres:postgres@localhost:5432/test\n DJANGO_SECRET_KEY: test-secret-key\n run: |\n pytest --cov=apps --cov-report=xml --cov-report=term-missing\n\n - name: Upload coverage\n uses: codecov/codecov-action@v3\n```\n\n## \u5feb\u901f\u53c2\u8003 (Quick Reference)\n\n| \u68c0\u67e5\u9879 | \u547d\u4ee4 |\n|-------|---------|\n| \u73af\u5883 (Environment) | `python --version` |\n| \u7c7b\u578b\u68c0\u67e5 (Type checking) | `mypy .` |\n| \u4ee3\u7801\u68c0\u67e5 (Linting) | `ruff check .` |\n| \u683c\u5f0f\u5316 (Formatting) | `black . --check` |\n| \u6570\u636e\u5e93\u8fc1\u79fb (Migrations) | `python manage.py makemigrations --check` |\n| \u6d4b\u8bd5 (Tests) | `pytest --cov=apps` |\n| \u5b89\u5168 (Security) | `pip-audit && bandit -r .` |\n| Django \u68c0\u67e5 (Django check) | `python manage.py check --deploy` |\n| \u6536\u96c6\u9759\u6001\u8d44\u6e90 (Collectstatic) | `python manage.py collectstatic --noinput` |\n| \u5dee\u5f02\u7edf\u8ba1 (Diff stats) | `git diff --stat` |\n\n\u8bb0\u4f4f\uff1a\u81ea\u52a8\u5316\u9a8c\u8bc1\u53ef\u4ee5\u6355\u6349\u5e38\u89c1\u95ee\u9898\uff0c\u4f46\u4e0d\u80fd\u53d6\u4ee3\u624b\u52a8\u4ee3\u7801\u5ba1\u67e5\u548c\u5728\u9884\u53d1\u5e03\u73af\u5883\uff08Staging environment\uff09\u4e2d\u7684\u6d4b\u8bd5\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/skills/java-coding-standards/SKILL.md": {
+ "md5": "2965b1ddf407a055a8f1f413c17aef12",
+ "content": "---\nname: java-coding-standards\ndescription: Spring Boot \u670d\u52a1\u7684 Java \u7f16\u7801\u89c4\u8303\uff1a\u547d\u540d\u3001\u4e0d\u53ef\u53d8\u6027\uff08Immutability\uff09\u3001Optional \u4f7f\u7528\u3001\u6d41\uff08Streams\uff09\u3001\u5f02\u5e38\u5904\u7406\u3001\u6cdb\u578b\uff08Generics\uff09\u548c\u9879\u76ee\u5e03\u5c40\u3002\n---\n\n# Java \u7f16\u7801\u89c4\u8303\n\n\u9002\u7528\u4e8e Spring Boot \u670d\u52a1\u4e2d\u6613\u8bfb\u3001\u53ef\u7ef4\u62a4\u7684 Java (17+) \u4ee3\u7801\u89c4\u8303\u3002\n\n## \u6838\u5fc3\u539f\u5219\n\n- \u6e05\u6670\u80dc\u8fc7\u5947\u5de7\n- \u9ed8\u8ba4\u4e0d\u53ef\u53d8\uff08Immutable\uff09\uff1b\u5c3d\u91cf\u51cf\u5c11\u5171\u4eab\u7684\u53ef\u53d8\u72b6\u6001\n- \u5feb\u901f\u5931\u8d25\u5e76\u629b\u51fa\u6709\u610f\u4e49\u7684\u5f02\u5e38\n- \u4fdd\u6301\u4e00\u81f4\u7684\u547d\u540d\u548c\u5305\u7ed3\u6784\n\n## \u547d\u540d\n\n```java\n// \u2705 \u7c7b\uff08Classes\uff09/ \u8bb0\u5f55\uff08Records\uff09\uff1a\u5927\u9a7c\u5cf0\u5f0f\uff08PascalCase\uff09\npublic class MarketService {}\npublic record Money(BigDecimal amount, Currency currency) {}\n\n// \u2705 \u65b9\u6cd5/\u5b57\u6bb5\uff1a\u5c0f\u9a7c\u5cf0\u5f0f\uff08camelCase\uff09\nprivate final MarketRepository marketRepository;\npublic Market findBySlug(String slug) {}\n\n// \u2705 \u5e38\u91cf\uff1a\u5927\u5199\u4e0b\u5212\u7ebf\u547d\u540d\u5f0f\uff08UPPER_SNAKE_CASE\uff09\nprivate static final int MAX_PAGE_SIZE = 100;\n```\n\n## \u4e0d\u53ef\u53d8\u6027\uff08Immutability\uff09\n\n```java\n// \u2705 \u4f18\u5148\u4f7f\u7528\u8bb0\u5f55\uff08Records\uff09\u548c final \u5b57\u6bb5\npublic record MarketDto(Long id, String name, MarketStatus status) {}\n\npublic class Market {\n private final Long id;\n private final String name;\n // \u4ec5\u63d0\u4f9b getter\uff0c\u4e0d\u63d0\u4f9b setter\n}\n```\n\n## Optional \u4f7f\u7528\n\n```java\n// \u2705 find* \u65b9\u6cd5\u8fd4\u56de Optional\nOptional market = marketRepository.findBySlug(slug);\n\n// \u2705 \u4f7f\u7528 map/flatMap \u800c\u4e0d\u662f get()\nreturn market\n .map(MarketResponse::from)\n .orElseThrow(() -> new EntityNotFoundException(\"Market not found\"));\n```\n\n## \u6d41\uff08Streams\uff09\u6700\u4f73\u5b9e\u8df5\n\n```java\n// \u2705 \u4f7f\u7528\u6d41\u8fdb\u884c\u8f6c\u6362\uff0c\u4fdd\u6301\u6d41\u6c34\u7ebf\u7b80\u77ed\nList names = markets.stream()\n .map(Market::name)\n .filter(Objects::nonNull)\n .toList();\n\n// \u274c \u907f\u514d\u590d\u6742\u7684\u5d4c\u5957\u6d41\uff1b\u4e3a\u4e86\u6e05\u6670\u8d77\u89c1\uff0c\u4f18\u5148\u4f7f\u7528\u5faa\u73af\n```\n\n## \u5f02\u5e38\u5904\u7406\n\n- \u9886\u57df\u9519\u8bef\u4f7f\u7528\u975e\u53d7\u68c0\u5f02\u5e38\uff08Unchecked Exceptions\uff09\uff1b\u4e3a\u6280\u672f\u5f02\u5e38\u5305\u88c5\u4e0a\u4e0b\u6587\u4fe1\u606f\n- \u521b\u5efa\u9886\u57df\u7279\u5b9a\u7684\u5f02\u5e38\uff08\u4f8b\u5982 `MarketNotFoundException`\uff09\n- \u907f\u514d\u5bbd\u6cdb\u7684 `catch (Exception ex)`\uff0c\u9664\u975e\u662f\u8fdb\u884c\u96c6\u4e2d\u5f0f\u91cd\u629b\uff08Rethrow\uff09\u6216\u65e5\u5fd7\u8bb0\u5f55\n\n```java\nthrow new MarketNotFoundException(slug);\n```\n\n## \u6cdb\u578b\u4e0e\u7c7b\u578b\u5b89\u5168\n\n- \u907f\u514d\u539f\u59cb\u7c7b\u578b\uff08Raw Types\uff09\uff1b\u58f0\u660e\u6cdb\u578b\u53c2\u6570\n- \u53ef\u590d\u7528\u5de5\u5177\u7c7b\u4f18\u5148\u4f7f\u7528\u6709\u754c\u6cdb\u578b\uff08Bounded Generics\uff09\n\n```java\npublic Map indexById(Collection items) { ... }\n```\n\n## \u9879\u76ee\u7ed3\u6784 (Maven/Gradle)\n\n```\nsrc/main/java/com/example/app/\n config/\n controller/\n service/\n repository/\n domain/\n dto/\n util/\nsrc/main/resources/\n application.yml\nsrc/test/java/... (\u7ed3\u6784\u4e0e main \u4fdd\u6301\u955c\u50cf)\n```\n\n## \u683c\u5f0f\u4e0e\u98ce\u683c\n\n- \u4e00\u81f4\u5730\u4f7f\u7528 2 \u6216 4 \u4e2a\u7a7a\u683c\uff08\u9075\u5faa\u9879\u76ee\u6807\u51c6\uff09\n- \u6bcf\u4e2a\u6587\u4ef6\u4ec5\u5305\u542b\u4e00\u4e2a\u516c\u5171\u9876\u5c42\u7c7b\u578b\n- \u4fdd\u6301\u65b9\u6cd5\u7b80\u77ed\u4e14\u805a\u7126\uff1b\u63d0\u53d6\u8f85\u52a9\u65b9\u6cd5\uff08Helper methods\uff09\n- \u6210\u5458\u6392\u5e8f\uff1a\u5e38\u91cf\u3001\u5b57\u6bb5\u3001\u6784\u9020\u51fd\u6570\u3001\u516c\u5171\u65b9\u6cd5\u3001\u53d7\u4fdd\u62a4\u65b9\u6cd5\u3001\u79c1\u6709\u65b9\u6cd5\n\n## \u9700\u907f\u514d\u7684\u4ee3\u7801\u574f\u5473\u9053\uff08Code Smells\uff09\n\n- \u957f\u53c2\u6570\u5217\u8868 \u2192 \u4f7f\u7528 DTO/\u6784\u5efa\u5668\uff08Builders\uff09\n- \u6df1\u5c42\u5d4c\u5957 \u2192 \u5c3d\u65e9\u8fd4\u56de\uff08Early returns\uff09\n- \u9b54\u6cd5\u6570\u5b57 \u2192 \u5177\u540d\u5e38\u91cf\n- \u9759\u6001\u53ef\u53d8\u72b6\u6001 \u2192 \u4f18\u5148\u4f7f\u7528\u4f9d\u8d56\u6ce8\u5165\uff08Dependency Injection\uff09\n- \u9759\u9ed8\u7684 catch \u5757 \u2192 \u8bb0\u5f55\u65e5\u5fd7\u5e76\u5904\u7406\u6216\u91cd\u629b\n\n## \u65e5\u5fd7\u8bb0\u5f55\n\n```java\nprivate static final Logger log = LoggerFactory.getLogger(MarketService.class);\nlog.info(\"fetch_market slug={}\", slug);\nlog.error(\"failed_fetch_market slug={}\", slug, ex);\n```\n\n## \u7a7a\u503c\u5904\u7406\uff08Null Handling\uff09\n\n- \u4ec5\u5728\u4e0d\u53ef\u907f\u514d\u65f6\u63a5\u53d7 `@Nullable`\uff1b\u5426\u5219\u4f7f\u7528 `@NonNull`\n- \u5728\u8f93\u5165\u4e0a\u4f7f\u7528 Bean \u6821\u9a8c\uff08`@NotNull`, `@NotBlank`\uff09\n\n## \u6d4b\u8bd5\u9884\u671f\n\n- \u4f7f\u7528 JUnit 5 + AssertJ \u8fdb\u884c\u6d41\u5f0f\u65ad\u8a00\uff08Fluent Assertions\uff09\n- \u4f7f\u7528 Mockito \u8fdb\u884c\u6a21\u62df\uff08Mocking\uff09\uff1b\u5c3d\u53ef\u80fd\u907f\u514d\u90e8\u5206\u6a21\u62df\uff08Partial Mocks\uff09\n- \u503e\u5411\u4e8e\u786e\u5b9a\u6027\u6d4b\u8bd5\uff1b\u4e0d\u8981\u5305\u542b\u9690\u85cf\u7684 sleep \u64cd\u4f5c\n\n**\u8bb0\u4f4f**\uff1a\u4fdd\u6301\u4ee3\u7801\u7684\u610f\u56fe\u6e05\u6670\u3001\u7c7b\u578b\u5b89\u5168\u4e14\u5177\u6709\u53ef\u89c2\u6d4b\u6027\u3002\u9664\u975e\u8bc1\u660e\u6709\u5fc5\u8981\uff0c\u5426\u5219\u5e94\u4f18\u5148\u8003\u8651\u53ef\u7ef4\u62a4\u6027\u800c\u975e\u5fae\u4f18\u5316\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/skills/django-tdd/SKILL.md": {
+ "md5": "134e3d2ad53f049515358305758fd308",
+ "content": "---\nname: django-tdd\ndescription: \u4f7f\u7528 pytest-django\u3001\u6d4b\u8bd5\u9a71\u52a8\u5f00\u53d1\uff08TDD\uff09\u65b9\u6cd5\u8bba\u3001factory_boy\u3001Mock \u6a21\u62df\u3001\u8986\u76d6\u7387\u7edf\u8ba1\u4ee5\u53ca Django REST Framework API \u6d4b\u8bd5\u7684 Django \u6d4b\u8bd5\u7b56\u7565\u3002\n---\n\n# Django TDD \u6d4b\u8bd5\u6307\u5357\n\n\u4f7f\u7528 pytest\u3001factory_boy \u548c Django REST Framework \u5bf9 Django \u5e94\u7528\u8fdb\u884c\u6d4b\u8bd5\u9a71\u52a8\u5f00\u53d1\uff08TDD\uff09\u3002\n\n## \u4f55\u65f6\u6fc0\u6d3b\n\n- \u7f16\u5199\u65b0\u7684 Django \u5e94\u7528\u7a0b\u5e8f\u65f6\n- \u5b9e\u73b0 Django REST Framework API \u65f6\n- \u6d4b\u8bd5 Django \u6a21\u578b\uff08Models\uff09\u3001\u89c6\u56fe\uff08Views\uff09\u548c\u5e8f\u5217\u5316\u5668\uff08Serializers\uff09\u65f6\n- \u4e3a Django \u9879\u76ee\u642d\u5efa\u6d4b\u8bd5\u57fa\u7840\u8bbe\u65bd\u65f6\n\n## Django \u7684 TDD \u5de5\u4f5c\u6d41\n\n### \u7ea2-\u7eff-\u91cd\u6784\u5faa\u73af\uff08Red-Green-Refactor Cycle\uff09\n\n```python\n# \u6b65\u9aa4 1\uff1a\u7ea2\u8272\uff08RED\uff09 - \u7f16\u5199\u5931\u8d25\u7684\u6d4b\u8bd5\ndef test_user_creation():\n user = User.objects.create_user(email='test@example.com', password='testpass123')\n assert user.email == 'test@example.com'\n assert user.check_password('testpass123')\n assert not user.is_staff\n\n# \u6b65\u9aa4 2\uff1a\u7eff\u8272\uff08GREEN\uff09 - \u7f16\u5199\u4ee3\u7801\u4f7f\u6d4b\u8bd5\u901a\u8fc7\n# \u521b\u5efa User \u6a21\u578b\u6216\u5de5\u5382\uff08Factory\uff09\n\n# \u6b65\u9aa4 3\uff1a\u91cd\u6784\uff08REFACTOR\uff09 - \u5728\u4fdd\u6301\u6d4b\u8bd5\u901a\u8fc7\u7684\u524d\u63d0\u4e0b\u4f18\u5316\u4ee3\u7801\n```\n\n## \u73af\u5883\u642d\u5efa\n\n### pytest \u914d\u7f6e\n\n```ini\n# pytest.ini\n[pytest]\nDJANGO_SETTINGS_MODULE = config.settings.test\ntestpaths = tests\npython_files = test_*.py\npython_classes = Test*\npython_functions = test_*\naddopts =\n --reuse-db\n --nomigrations\n --cov=apps\n --cov-report=html\n --cov-report=term-missing\n --strict-markers\nmarkers =\n slow: \u6807\u8bb0\u4e3a\u8017\u65f6\u8f83\u957f\u7684\u6d4b\u8bd5\n integration: \u6807\u8bb0\u4e3a\u96c6\u6210\u6d4b\u8bd5\n```\n\n### \u6d4b\u8bd5\u8bbe\u7f6e\uff08Settings\uff09\n\n```python\n# config/settings/test.py\nfrom .base import *\n\nDEBUG = True\nDATABASES = {\n 'default': {\n 'ENGINE': 'django.db.backends.sqlite3',\n 'NAME': ':memory:',\n }\n}\n\n# \u7981\u7528\u8fc1\u79fb\u4ee5\u63d0\u5347\u901f\u5ea6\nclass DisableMigrations:\n def __contains__(self, item):\n return True\n\n def __getitem__(self, item):\n return None\n\nMIGRATION_MODULES = DisableMigrations()\n\n# \u4f7f\u7528\u66f4\u5feb\u7684\u5bc6\u7801\u54c8\u5e0c\u7b97\u6cd5\nPASSWORD_HASHERS = [\n 'django.contrib.auth.hashers.MD5PasswordHasher',\n]\n\n# \u90ae\u4ef6\u540e\u7aef\u914d\u7f6e\nEMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'\n\n# Celery \u59cb\u7ec8\u540c\u6b65\u6267\u884c\nCELERY_TASK_ALWAYS_EAGER = True\nCELERY_TASK_EAGER_PROPAGATES = True\n```\n\n### conftest.py\n\n```python\n# tests/conftest.py\nimport pytest\nfrom django.utils import timezone\nfrom django.contrib.auth import get_user_model\n\nUser = get_user_model()\n\n@pytest.fixture(autouse=True)\ndef timezone_settings(settings):\n \"\"\"\u786e\u4fdd\u65f6\u533a\u4e00\u81f4\u3002\"\"\"\n settings.TIME_ZONE = 'UTC'\n\n@pytest.fixture\ndef user(db):\n \"\"\"\u521b\u5efa\u6d4b\u8bd5\u7528\u6237\u3002\"\"\"\n return User.objects.create_user(\n email='test@example.com',\n password='testpass123',\n username='testuser'\n )\n\n@pytest.fixture\ndef admin_user(db):\n \"\"\"\u521b\u5efa\u7ba1\u7406\u5458\u7528\u6237\u3002\"\"\"\n return User.objects.create_superuser(\n email='admin@example.com',\n password='adminpass123',\n username='admin'\n )\n\n@pytest.fixture\ndef authenticated_client(client, user):\n \"\"\"\u8fd4\u56de\u5df2\u8ba4\u8bc1\u7684\u5ba2\u6237\u7aef\u3002\"\"\"\n client.force_login(user)\n return client\n\n@pytest.fixture\ndef api_client():\n \"\"\"\u8fd4\u56de DRF API \u5ba2\u6237\u7aef\u3002\"\"\"\n from rest_framework.test import APIClient\n return APIClient()\n\n@pytest.fixture\ndef authenticated_api_client(api_client, user):\n \"\"\"\u8fd4\u56de\u5df2\u8ba4\u8bc1\u7684 API \u5ba2\u6237\u7aef\u3002\"\"\"\n api_client.force_authenticate(user=user)\n return api_client\n```\n\n## Factory Boy\n\n### \u5de5\u5382\u914d\u7f6e\n\n```python\n# tests/factories.py\nimport factory\nfrom factory import fuzzy\nfrom datetime import datetime, timedelta\nfrom django.contrib.auth import get_user_model\nfrom apps.products.models import Product, Category\n\nUser = get_user_model()\n\nclass UserFactory(factory.django.DjangoModelFactory):\n \"\"\"User \u6a21\u578b\u7684\u5de5\u5382\u7c7b\u3002\"\"\"\n\n class Meta:\n model = User\n\n email = factory.Sequence(lambda n: f\"user{n}@example.com\")\n username = factory.Sequence(lambda n: f\"user{n}\")\n password = factory.PostGenerationMethodCall('set_password', 'testpass123')\n first_name = factory.Faker('first_name')\n last_name = factory.Faker('last_name')\n is_active = True\n\nclass CategoryFactory(factory.django.DjangoModelFactory):\n \"\"\"Category \u6a21\u578b\u7684\u5de5\u5382\u7c7b\u3002\"\"\"\n\n class Meta:\n model = Category\n\n name = factory.Faker('word')\n slug = factory.LazyAttribute(lambda obj: obj.name.lower())\n description = factory.Faker('text')\n\nclass ProductFactory(factory.django.DjangoModelFactory):\n \"\"\"Product \u6a21\u578b\u7684\u5de5\u5382\u7c7b\u3002\"\"\"\n\n class Meta:\n model = Product\n\n name = factory.Faker('sentence', nb_words=3)\n slug = factory.LazyAttribute(lambda obj: obj.name.lower().replace(' ', '-'))\n description = factory.Faker('text')\n price = fuzzy.FuzzyDecimal(10.00, 1000.00, 2)\n stock = fuzzy.FuzzyInteger(0, 100)\n is_active = True\n category = factory.SubFactory(CategoryFactory)\n created_by = factory.SubFactory(UserFactory)\n\n @factory.post_generation\n def tags(self, create, extracted, **kwargs):\n \"\"\"\u4e3a\u4ea7\u54c1\u6dfb\u52a0\u6807\u7b7e\u3002\"\"\"\n if not create:\n return\n if extracted:\n for tag in extracted:\n self.tags.add(tag)\n```\n\n### \u4f7f\u7528\u5de5\u5382\n\n```python\n# tests/test_models.py\nimport pytest\nfrom tests.factories import ProductFactory, UserFactory\n\ndef test_product_creation():\n \"\"\"\u6d4b\u8bd5\u4f7f\u7528\u5de5\u5382\u521b\u5efa\u4ea7\u54c1\u3002\"\"\"\n product = ProductFactory(price=100.00, stock=50)\n assert product.price == 100.00\n assert product.stock == 50\n assert product.is_active is True\n\ndef test_product_with_tags():\n \"\"\"\u6d4b\u8bd5\u5e26\u6709\u6807\u7b7e\u7684\u4ea7\u54c1\u3002\"\"\"\n tags = [TagFactory(name='electronics'), TagFactory(name='new')]\n product = ProductFactory(tags=tags)\n assert product.tags.count() == 2\n\ndef test_multiple_products():\n \"\"\"\u6d4b\u8bd5\u521b\u5efa\u591a\u4e2a\u4ea7\u54c1\u3002\"\"\"\n products = ProductFactory.create_batch(10)\n assert len(products) == 10\n```\n\n## \u6a21\u578b\u6d4b\u8bd5\uff08Model Testing\uff09\n\n### \u6a21\u578b\u6d4b\u8bd5\u7528\u4f8b\n\n```python\n# tests/test_models.py\nimport pytest\nfrom django.core.exceptions import ValidationError\nfrom tests.factories import UserFactory, ProductFactory\n\nclass TestUserModel:\n \"\"\"\u6d4b\u8bd5 User \u6a21\u578b\u3002\"\"\"\n\n def test_create_user(self, db):\n \"\"\"\u6d4b\u8bd5\u521b\u5efa\u666e\u901a\u7528\u6237\u3002\"\"\"\n user = UserFactory(email='test@example.com')\n assert user.email == 'test@example.com'\n assert user.check_password('testpass123')\n assert not user.is_staff\n assert not user.is_superuser\n\n def test_create_superuser(self, db):\n \"\"\"\u6d4b\u8bd5\u521b\u5efa\u8d85\u7ea7\u7528\u6237\u3002\"\"\"\n user = UserFactory(\n email='admin@example.com',\n is_staff=True,\n is_superuser=True\n )\n assert user.is_staff\n assert user.is_superuser\n\n def test_user_str(self, db):\n \"\"\"\u6d4b\u8bd5\u7528\u6237\u5b57\u7b26\u4e32\u8868\u793a\u5f62\u5f0f\u3002\"\"\"\n user = UserFactory(email='test@example.com')\n assert str(user) == 'test@example.com'\n\nclass TestProductModel:\n \"\"\"\u6d4b\u8bd5 Product \u6a21\u578b\u3002\"\"\"\n\n def test_product_creation(self, db):\n \"\"\"\u6d4b\u8bd5\u521b\u5efa\u4ea7\u54c1\u3002\"\"\"\n product = ProductFactory()\n assert product.id is not None\n assert product.is_active is True\n assert product.created_at is not None\n\n def test_product_slug_generation(self, db):\n \"\"\"\u6d4b\u8bd5 Slug \u81ea\u52a8\u751f\u6210\u3002\"\"\"\n product = ProductFactory(name='Test Product')\n assert product.slug == 'test-product'\n\n def test_product_price_validation(self, db):\n \"\"\"\u6d4b\u8bd5\u4ef7\u683c\u4e0d\u80fd\u4e3a\u8d1f\u6570\u3002\"\"\"\n product = ProductFactory(price=-10)\n with pytest.raises(ValidationError):\n product.full_clean()\n\n def test_product_manager_active(self, db):\n \"\"\"\u6d4b\u8bd5 active \u7ba1\u7406\u5668\u65b9\u6cd5\u3002\"\"\"\n ProductFactory.create_batch(5, is_active=True)\n ProductFactory.create_batch(3, is_active=False)\n\n active_count = Product.objects.active().count()\n assert active_count == 5\n\n def test_product_stock_management(self, db):\n \"\"\"\u6d4b\u8bd5\u5e93\u5b58\u7ba1\u7406\u3002\"\"\"\n product = ProductFactory(stock=10)\n product.reduce_stock(5)\n product.refresh_from_db()\n assert product.stock == 5\n\n with pytest.raises(ValueError):\n product.reduce_stock(10) # \u5e93\u5b58\u4e0d\u8db3\n```\n\n## \u89c6\u56fe\u6d4b\u8bd5\uff08View Testing\uff09\n\n### Django \u89c6\u56fe\u6d4b\u8bd5\n\n```python\n# tests/test_views.py\nimport pytest\nfrom django.urls import reverse\nfrom tests.factories import ProductFactory, UserFactory\n\nclass TestProductViews:\n \"\"\"\u6d4b\u8bd5\u4ea7\u54c1\u89c6\u56fe\u3002\"\"\"\n\n def test_product_list(self, client, db):\n \"\"\"\u6d4b\u8bd5\u4ea7\u54c1\u5217\u8868\u89c6\u56fe\u3002\"\"\"\n ProductFactory.create_batch(10)\n\n response = client.get(reverse('products:list'))\n\n assert response.status_code == 200\n assert len(response.context['products']) == 10\n\n def test_product_detail(self, client, db):\n \"\"\"\u6d4b\u8bd5\u4ea7\u54c1\u8be6\u60c5\u89c6\u56fe\u3002\"\"\"\n product = ProductFactory()\n\n response = client.get(reverse('products:detail', kwargs={'slug': product.slug}))\n\n assert response.status_code == 200\n assert response.context['product'] == product\n\n def test_product_create_requires_login(self, client, db):\n \"\"\"\u6d4b\u8bd5\u521b\u5efa\u4ea7\u54c1\u9700\u8981\u767b\u5f55\u8ba4\u8bc1\u3002\"\"\"\n response = client.get(reverse('products:create'))\n\n assert response.status_code == 302\n assert response.url.startswith('/accounts/login/')\n\n def test_product_create_authenticated(self, authenticated_client, db):\n \"\"\"\u6d4b\u8bd5\u4ee5\u5df2\u8ba4\u8bc1\u7528\u6237\u8eab\u4efd\u521b\u5efa\u4ea7\u54c1\u3002\"\"\"\n response = authenticated_client.get(reverse('products:create'))\n\n assert response.status_code == 200\n\n def test_product_create_post(self, authenticated_client, db, category):\n \"\"\"\u6d4b\u8bd5\u901a\u8fc7 POST \u8bf7\u6c42\u521b\u5efa\u4ea7\u54c1\u3002\"\"\"\n data = {\n 'name': 'Test Product',\n 'description': 'A test product',\n 'price': '99.99',\n 'stock': 10,\n 'category': category.id,\n }\n\n response = authenticated_client.post(reverse('products:create'), data)\n\n assert response.status_code == 302\n assert Product.objects.filter(name='Test Product').exists()\n```\n\n## DRF API \u6d4b\u8bd5\n\n### \u5e8f\u5217\u5316\u5668\u6d4b\u8bd5\n\n```python\n# tests/test_serializers.py\nimport pytest\nfrom rest_framework.exceptions import ValidationError\nfrom apps.products.serializers import ProductSerializer\nfrom tests.factories import ProductFactory\n\nclass TestProductSerializer:\n \"\"\"\u6d4b\u8bd5 ProductSerializer\u3002\"\"\"\n\n def test_serialize_product(self, db):\n \"\"\"\u6d4b\u8bd5\u5e8f\u5217\u5316\u4ea7\u54c1\u5bf9\u8c61\u3002\"\"\"\n product = ProductFactory()\n serializer = ProductSerializer(product)\n\n data = serializer.data\n\n assert data['id'] == product.id\n assert data['name'] == product.name\n assert data['price'] == str(product.price)\n\n def test_deserialize_product(self, db):\n \"\"\"\u6d4b\u8bd5\u53cd\u5e8f\u5217\u5316\u4ea7\u54c1\u6570\u636e\u3002\"\"\"\n data = {\n 'name': 'Test Product',\n 'description': 'Test description',\n 'price': '99.99',\n 'stock': 10,\n 'category': 1,\n }\n\n serializer = ProductSerializer(data=data)\n\n assert serializer.is_valid()\n product = serializer.save()\n\n assert product.name == 'Test Product'\n assert float(product.price) == 99.99\n\n def test_price_validation(self, db):\n \"\"\"\u6d4b\u8bd5\u4ef7\u683c\u9a8c\u8bc1\u3002\"\"\"\n data = {\n 'name': 'Test Product',\n 'price': '-10.00',\n 'stock': 10,\n }\n\n serializer = ProductSerializer(data=data)\n\n assert not serializer.is_valid()\n assert 'price' in serializer.errors\n\n def test_stock_validation(self, db):\n \"\"\"\u6d4b\u8bd5\u5e93\u5b58\u4e0d\u80fd\u4e3a\u8d1f\u6570\u3002\"\"\"\n data = {\n 'name': 'Test Product',\n 'price': '99.99',\n 'stock': -5,\n }\n\n serializer = ProductSerializer(data=data)\n\n assert not serializer.is_valid()\n assert 'stock' in serializer.errors\n```\n\n### API ViewSet \u6d4b\u8bd5\n\n```python\n# tests/test_api.py\nimport pytest\nfrom rest_framework.test import APIClient\nfrom rest_framework import status\nfrom django.urls import reverse\nfrom tests.factories import ProductFactory, UserFactory\n\nclass TestProductAPI:\n \"\"\"\u6d4b\u8bd5\u4ea7\u54c1 API \u63a5\u53e3\u3002\"\"\"\n\n @pytest.fixture\n def api_client(self):\n \"\"\"\u8fd4\u56de API \u5ba2\u6237\u7aef\u3002\"\"\"\n return APIClient()\n\n def test_list_products(self, api_client, db):\n \"\"\"\u6d4b\u8bd5\u5217\u51fa\u4ea7\u54c1\u3002\"\"\"\n ProductFactory.create_batch(10)\n\n url = reverse('api:product-list')\n response = api_client.get(url)\n\n assert response.status_code == status.HTTP_200_OK\n assert response.data['count'] == 10\n\n def test_retrieve_product(self, api_client, db):\n \"\"\"\u6d4b\u8bd5\u83b7\u53d6\u5355\u4e2a\u4ea7\u54c1\u8be6\u60c5\u3002\"\"\"\n product = ProductFactory()\n\n url = reverse('api:product-detail', kwargs={'pk': product.id})\n response = api_client.get(url)\n\n assert response.status_code == status.HTTP_200_OK\n assert response.data['id'] == product.id\n\n def test_create_product_unauthorized(self, api_client, db):\n \"\"\"\u6d4b\u8bd5\u672a\u6388\u6743\u65f6\u521b\u5efa\u4ea7\u54c1\u3002\"\"\"\n url = reverse('api:product-list')\n data = {'name': 'Test Product', 'price': '99.99'}\n\n response = api_client.post(url, data)\n\n assert response.status_code == status.HTTP_401_UNAUTHORIZED\n\n def test_create_product_authorized(self, authenticated_api_client, db):\n \"\"\"\u6d4b\u8bd5\u4ee5\u5df2\u8ba4\u8bc1\u7528\u6237\u8eab\u4efd\u521b\u5efa\u4ea7\u54c1\u3002\"\"\"\n url = reverse('api:product-list')\n data = {\n 'name': 'Test Product',\n 'description': 'Test',\n 'price': '99.99',\n 'stock': 10,\n }\n\n response = authenticated_api_client.post(url, data)\n\n assert response.status_code == status.HTTP_201_CREATED\n assert response.data['name'] == 'Test Product'\n\n def test_update_product(self, authenticated_api_client, db):\n \"\"\"\u6d4b\u8bd5\u66f4\u65b0\u4ea7\u54c1\u3002\"\"\"\n product = ProductFactory(created_by=authenticated_api_client.user)\n\n url = reverse('api:product-detail', kwargs={'pk': product.id})\n data = {'name': 'Updated Product'}\n\n response = authenticated_api_client.patch(url, data)\n\n assert response.status_code == status.HTTP_200_OK\n assert response.data['name'] == 'Updated Product'\n\n def test_delete_product(self, authenticated_api_client, db):\n \"\"\"\u6d4b\u8bd5\u5220\u9664\u4ea7\u54c1\u3002\"\"\"\n product = ProductFactory(created_by=authenticated_api_client.user)\n\n url = reverse('api:product-detail', kwargs={'pk': product.id})\n response = authenticated_api_client.delete(url)\n\n assert response.status_code == status.HTTP_204_NO_CONTENT\n\n def test_filter_products_by_price(self, api_client, db):\n \"\"\"\u6d4b\u8bd5\u6309\u4ef7\u683c\u8fc7\u6ee4\u4ea7\u54c1\u3002\"\"\"\n ProductFactory(price=50)\n ProductFactory(price=150)\n\n url = reverse('api:product-list')\n response = api_client.get(url, {'price_min': 100})\n\n assert response.status_code == status.HTTP_200_OK\n assert response.data['count'] == 1\n\n def test_search_products(self, api_client, db):\n \"\"\"\u6d4b\u8bd5\u641c\u7d22\u4ea7\u54c1\u3002\"\"\"\n ProductFactory(name='Apple iPhone')\n ProductFactory(name='Samsung Galaxy')\n\n url = reverse('api:product-list')\n response = api_client.get(url, {'search': 'Apple'})\n\n assert response.status_code == status.HTTP_200_OK\n assert response.data['count'] == 1\n```\n\n## Mock \u6a21\u62df\u4e0e\u8865\u4e01\uff08Mocking and Patching\uff09\n\n### \u6a21\u62df\u5916\u90e8\u670d\u52a1\n\n```python\n# tests/test_views.py\nfrom unittest.mock import patch, Mock\nimport pytest\n\nclass TestPaymentView:\n \"\"\"\u4f7f\u7528\u6a21\u62df\u652f\u4ed8\u7f51\u5173\u6d4b\u8bd5\u652f\u4ed8\u89c6\u56fe\u3002\"\"\"\n\n @patch('apps.payments.services.stripe')\n def test_successful_payment(self, mock_stripe, client, user, product):\n \"\"\"\u4f7f\u7528\u6a21\u62df\u7684 Stripe \u6d4b\u8bd5\u6210\u529f\u652f\u4ed8\u3002\"\"\"\n # \u914d\u7f6e\u6a21\u62df\u5bf9\u8c61\n mock_stripe.Charge.create.return_value = {\n 'id': 'ch_123',\n 'status': 'succeeded',\n 'amount': 9999,\n }\n\n client.force_login(user)\n response = client.post(reverse('payments:process'), {\n 'product_id': product.id,\n 'token': 'tok_visa',\n })\n\n assert response.status_code == 302\n mock_stripe.Charge.create.assert_called_once()\n\n @patch('apps.payments.services.stripe')\n def test_failed_payment(self, mock_stripe, client, user, product):\n \"\"\"\u6d4b\u8bd5\u652f\u4ed8\u5931\u8d25\u3002\"\"\"\n mock_stripe.Charge.create.side_effect = Exception('Card declined')\n\n client.force_login(user)\n response = client.post(reverse('payments:process'), {\n 'product_id': product.id,\n 'token': 'tok_visa',\n })\n\n assert response.status_code == 302\n assert 'error' in response.url\n```\n\n### \u6a21\u62df\u90ae\u4ef6\u53d1\u9001\n\n```python\n# tests/test_email.py\nfrom django.core import mail\nfrom django.test import override_settings\n\n@override_settings(EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend')\ndef test_order_confirmation_email(db, order):\n \"\"\"\u6d4b\u8bd5\u8ba2\u5355\u786e\u8ba4\u90ae\u4ef6\u3002\"\"\"\n order.send_confirmation_email()\n\n assert len(mail.outbox) == 1\n assert order.user.email in mail.outbox[0].to\n assert 'Order Confirmation' in mail.outbox[0].subject\n```\n\n## \u96c6\u6210\u6d4b\u8bd5\uff08Integration Testing\uff09\n\n### \u5168\u6d41\u7a0b\u6d4b\u8bd5\n\n```python\n# tests/test_integration.py\nimport pytest\nfrom django.urls import reverse\nfrom tests.factories import UserFactory, ProductFactory\n\nclass TestCheckoutFlow:\n \"\"\"\u6d4b\u8bd5\u5b8c\u6574\u7684\u7ed3\u8d26\u6d41\u7a0b\u3002\"\"\"\n\n def test_guest_to_purchase_flow(self, client, db):\n \"\"\"\u6d4b\u8bd5\u4ece\u6e38\u5ba2\u5230\u8d2d\u4e70\u5b8c\u6210\u7684\u5b8c\u6574\u6d41\u7a0b\u3002\"\"\"\n # \u6b65\u9aa4 1\uff1a\u6ce8\u518c\n response = client.post(reverse('users:register'), {\n 'email': 'test@example.com',\n 'password': 'testpass123',\n 'password_confirm': 'testpass123',\n })\n assert response.status_code == 302\n\n # \u6b65\u9aa4 2\uff1a\u767b\u5f55\n response = client.post(reverse('users:login'), {\n 'email': 'test@example.com',\n 'password': 'testpass123',\n })\n assert response.status_code == 302\n\n # \u6b65\u9aa4 3\uff1a\u6d4f\u89c8\u4ea7\u54c1\n product = ProductFactory(price=100)\n response = client.get(reverse('products:detail', kwargs={'slug': product.slug}))\n assert response.status_code == 200\n\n # \u6b65\u9aa4 4\uff1a\u6dfb\u52a0\u5230\u8d2d\u7269\u8f66\n response = client.post(reverse('cart:add'), {\n 'product_id': product.id,\n 'quantity': 1,\n })\n assert response.status_code == 302\n\n # \u6b65\u9aa4 5\uff1a\u7ed3\u8d26\n response = client.get(reverse('checkout:review'))\n assert response.status_code == 200\n assert product.name in response.content.decode()\n\n # \u6b65\u9aa4 6\uff1a\u5b8c\u6210\u8d2d\u4e70\n with patch('apps.checkout.services.process_payment') as mock_payment:\n mock_payment.return_value = True\n response = client.post(reverse('checkout:complete'))\n\n assert response.status_code == 302\n assert Order.objects.filter(user__email='test@example.com').exists()\n```\n\n## \u6d4b\u8bd5\u6700\u4f73\u5b9e\u8df5\n\n### \u63a8\u8350\u505a\u6cd5\uff08DO\uff09\n\n- **\u4f7f\u7528\u5de5\u5382\uff08Factories\uff09**\uff1a\u907f\u514d\u624b\u52a8\u521b\u5efa\u5bf9\u8c61\u3002\n- **\u6bcf\u4e2a\u6d4b\u8bd5\u4e00\u4e2a\u65ad\u8a00**\uff1a\u4fdd\u6301\u6d4b\u8bd5\u4e13\u6ce8\u3002\n- **\u63cf\u8ff0\u6027\u6d4b\u8bd5\u540d\u79f0**\uff1a\u5982 `test_user_cannot_delete_others_post`\u3002\n- **\u6d4b\u8bd5\u8fb9\u754c\u60c5\u51b5**\uff1a\u7a7a\u8f93\u5165\u3001None \u503c\u3001\u8fb9\u754c\u6761\u4ef6\u3002\n- **\u6a21\u62df\uff08Mock\uff09\u5916\u90e8\u670d\u52a1**\uff1a\u4e0d\u8981\u4f9d\u8d56\u5916\u90e8 API\u3002\n- **\u4f7f\u7528 Fixtures**\uff1a\u6d88\u9664\u91cd\u590d\u4ee3\u7801\u3002\n- **\u6d4b\u8bd5\u6743\u9650\u63a7\u5236**\uff1a\u786e\u4fdd\u6388\u6743\u903b\u8f91\u6b63\u5e38\u5de5\u4f5c\u3002\n- **\u4fdd\u6301\u6d4b\u8bd5\u8fd0\u884c\u8fc5\u901f**\uff1a\u4f7f\u7528 `--reuse-db` \u548c `--nomigrations`\u3002\n\n### \u907f\u514d\u505a\u6cd5\uff08DON'T\uff09\n\n- **\u4e0d\u8981\u6d4b\u8bd5 Django \u5185\u90e8\u673a\u5236**\uff1a\u76f8\u4fe1 Django \u81ea\u8eab\u5df2\u901a\u8fc7\u6d4b\u8bd5\u3002\n- **\u4e0d\u8981\u6d4b\u8bd5\u7b2c\u4e09\u65b9\u5e93\u4ee3\u7801**\uff1a\u76f8\u4fe1\u5e93\u4f5c\u8005\u7684\u6d4b\u8bd5\u3002\n- **\u4e0d\u8981\u5ffd\u7565\u5931\u8d25\u7684\u6d4b\u8bd5**\uff1a\u6240\u6709\u6d4b\u8bd5\u90fd\u5fc5\u987b\u901a\u8fc7\u3002\n- **\u4e0d\u8981\u8ba9\u6d4b\u8bd5\u4ea7\u751f\u4f9d\u8d56**\uff1a\u6d4b\u8bd5\u5e94\u8be5\u53ef\u4ee5\u4ee5\u4efb\u4f55\u987a\u5e8f\u8fd0\u884c\u3002\n- **\u4e0d\u8981\u8fc7\u5ea6\u6a21\u62df**\uff1a\u4ec5\u5bf9\u5916\u90e8\u4f9d\u8d56\u9879\u8fdb\u884c Mock\u3002\n- **\u4e0d\u8981\u6d4b\u8bd5\u79c1\u6709\u65b9\u6cd5**\uff1a\u6d4b\u8bd5\u516c\u5171\u63a5\u53e3\u3002\n- **\u4e0d\u8981\u4f7f\u7528\u751f\u4ea7\u6570\u636e\u5e93**\uff1a\u59cb\u7ec8\u4f7f\u7528\u4e13\u7528\u6d4b\u8bd5\u6570\u636e\u5e93\u3002\n\n## \u8986\u76d6\u7387\uff08Coverage\uff09\n\n### \u8986\u76d6\u7387\u914d\u7f6e\n\n```bash\n# \u8fd0\u884c\u5e26\u6709\u8986\u76d6\u7387\u7edf\u8ba1\u7684\u6d4b\u8bd5\npytest --cov=apps --cov-report=html --cov-report=term-missing\n\n# \u751f\u6210\u5e76\u67e5\u770b HTML \u62a5\u544a\nopen htmlcov/index.html\n```\n\n### \u8986\u76d6\u7387\u76ee\u6807\n\n| \u7ec4\u4ef6 | \u76ee\u6807\u8986\u76d6\u7387 |\n|-----------|-----------------|\n| \u6a21\u578b (Models) | 90%+ |\n| \u5e8f\u5217\u5316\u5668 (Serializers) | 85%+ |\n| \u89c6\u56fe (Views) | 80%+ |\n| \u670d\u52a1\u5c42 (Services) | 90%+ |\n| \u5de5\u5177\u7c7b (Utilities) | 80%+ |\n| \u603b\u4f53 (Overall) | 80%+ |\n\n## \u5feb\u901f\u53c2\u8003\n\n| \u6a21\u5f0f | \u7528\u6cd5 |\n|---------|-------|\n| `@pytest.mark.django_db` | \u542f\u7528\u6570\u636e\u5e93\u8bbf\u95ee |\n| `client` | Django \u6d4b\u8bd5\u5ba2\u6237\u7aef |\n| `api_client` | DRF API \u5ba2\u6237\u7aef |\n| `factory.create_batch(n)` | \u521b\u5efa\u591a\u4e2a\u5bf9\u8c61 |\n| `patch('module.function')` | \u6a21\u62df\u5916\u90e8\u4f9d\u8d56 |\n| `override_settings` | \u4e34\u65f6\u66f4\u6539\u8bbe\u7f6e |\n| `force_authenticate()` | \u5728\u6d4b\u8bd5\u4e2d\u7ed5\u8fc7\u8ba4\u8bc1 |\n| `assertRedirects` | \u68c0\u67e5\u91cd\u5b9a\u5411 |\n| `assertTemplateUsed` | \u9a8c\u8bc1\u6a21\u677f\u4f7f\u7528\u60c5\u51b5 |\n| `mail.outbox` | \u68c0\u67e5\u5df2\u53d1\u9001\u7684\u90ae\u4ef6 |\n\n\u8bf7\u8bb0\u4f4f\uff1a\u6d4b\u8bd5\u5373\u6587\u6863\u3002\u826f\u597d\u7684\u6d4b\u8bd5\u80fd\u591f\u89e3\u91ca\u4ee3\u7801\u7684\u9884\u671f\u5de5\u4f5c\u65b9\u5f0f\u3002\u4fdd\u6301\u6d4b\u8bd5\u7b80\u5355\u3001\u6613\u8bfb\u4e14\u6613\u4e8e\u7ef4\u62a4\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/skills/springboot-tdd/SKILL.md": {
+ "md5": "b99c9f82ac8f8da340998fed7b06e4a1",
+ "content": "---\nname: springboot-tdd\ndescription: \u4f7f\u7528 JUnit 5\u3001Mockito\u3001MockMvc\u3001Testcontainers \u548c JaCoCo \u8fdb\u884c Spring Boot \u7684\u6d4b\u8bd5\u9a71\u52a8\u5f00\u53d1\uff08TDD\uff09\u3002\u5728\u6dfb\u52a0\u529f\u80fd\u3001\u4fee\u590d Bug \u6216\u8fdb\u884c\u91cd\u6784\u65f6\u4f7f\u7528\u3002\n---\n\n# Spring Boot \u6d4b\u8bd5\u9a71\u52a8\u5f00\u53d1\uff08TDD\uff09\u5de5\u4f5c\u6d41\n\n\u9488\u5bf9 Spring Boot \u670d\u52a1\u7684 TDD \u6307\u5357\uff0c\u8981\u6c42 80% \u4ee5\u4e0a\u7684\u8986\u76d6\u7387\uff08\u5355\u5143\u6d4b\u8bd5 + \u96c6\u6210\u6d4b\u8bd5\uff09\u3002\n\n## \u9002\u7528\u573a\u666f\n\n- \u5f00\u53d1\u65b0\u529f\u80fd\u6216\u7aef\u70b9\uff08Endpoints\uff09\n- \u4fee\u590d Bug \u6216\u8fdb\u884c\u4ee3\u7801\u91cd\u6784\n- \u6dfb\u52a0\u6570\u636e\u8bbf\u95ee\u903b\u8f91\u6216\u5b89\u5168\u89c4\u5219\n\n## \u5de5\u4f5c\u6d41\n\n1) \u5148\u5199\u6d4b\u8bd5\uff08\u6d4b\u8bd5\u5e94\u5f53\u5931\u8d25\uff09\n2) \u5b9e\u73b0\u6700\u5c11\u91cf\u7684\u4ee3\u7801\u4ee5\u4f7f\u6d4b\u8bd5\u901a\u8fc7\n3) \u5728\u6d4b\u8bd5\u901a\u8fc7\uff08Green\uff09\u7684\u524d\u63d0\u4e0b\u8fdb\u884c\u91cd\u6784\n4) \u5f3a\u5236\u6267\u884c\u8986\u76d6\u7387\u68c0\u67e5\uff08JaCoCo\uff09\n\n## \u5355\u5143\u6d4b\u8bd5\uff08JUnit 5 + Mockito\uff09\n\n```java\n@ExtendWith(MockitoExtension.class)\nclass MarketServiceTest {\n @Mock MarketRepository repo;\n @InjectMocks MarketService service;\n\n @Test\n void createsMarket() {\n CreateMarketRequest req = new CreateMarketRequest(\"name\", \"desc\", Instant.now(), List.of(\"cat\"));\n when(repo.save(any())).thenAnswer(inv -> inv.getArgument(0));\n\n Market result = service.create(req);\n\n assertThat(result.name()).isEqualTo(\"name\");\n verify(repo).save(any());\n }\n}\n```\n\n\u6a21\u5f0f\uff1a\n- Arrange-Act-Assert\uff08\u51c6\u5907-\u6267\u884c-\u65ad\u8a00\uff09\n- \u907f\u514d\u90e8\u5206\u6253\u6869\uff08Partial Mocks\uff09\uff1b\u4f18\u5148\u4f7f\u7528\u663e\u5f0f\u6869\u51fd\u6570\uff08Stubbing\uff09\n- \u4f7f\u7528 `@ParameterizedTest` \u5904\u7406\u591a\u79cd\u53d8\u4f53\u573a\u666f\n\n## Web \u5c42\u6d4b\u8bd5\uff08MockMvc\uff09\n\n```java\n@WebMvcTest(MarketController.class)\nclass MarketControllerTest {\n @Autowired MockMvc mockMvc;\n @MockBean MarketService marketService;\n\n @Test\n void returnsMarkets() throws Exception {\n when(marketService.list(any())).thenReturn(Page.empty());\n\n mockMvc.perform(get(\"/api/markets\"))\n .andExpect(status().isOk())\n .andExpect(jsonPath(\"$.content\").isArray());\n }\n}\n```\n\n## \u96c6\u6210\u6d4b\u8bd5\uff08SpringBootTest\uff09\n\n```java\n@SpringBootTest\n@AutoConfigureMockMvc\n@ActiveProfiles(\"test\")\nclass MarketIntegrationTest {\n @Autowired MockMvc mockMvc;\n\n @Test\n void createsMarket() throws Exception {\n mockMvc.perform(post(\"/api/markets\")\n .contentType(MediaType.APPLICATION_JSON)\n .content(\"\"\"\n {\"name\":\"Test\",\"description\":\"Desc\",\"endDate\":\"2030-01-01T00:00:00Z\",\"categories\":[\"general\"]}\n \"\"\"))\n .andExpect(status().isCreated());\n }\n}\n```\n\n## \u6301\u4e45\u5c42\u6d4b\u8bd5\uff08DataJpaTest\uff09\n\n```java\n@DataJpaTest\n@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)\n@Import(TestContainersConfig.class)\nclass MarketRepositoryTest {\n @Autowired MarketRepository repo;\n\n @Test\n void savesAndFinds() {\n MarketEntity entity = new MarketEntity();\n entity.setName(\"Test\");\n repo.save(entity);\n\n Optional found = repo.findByName(\"Test\");\n assertThat(found).isPresent();\n }\n}\n```\n\n## Testcontainers\n\n- \u4f7f\u7528\u53ef\u91cd\u7528\u7684\u5bb9\u5668\uff08\u5982 Postgres/Redis\uff09\u6765\u6a21\u62df\u751f\u4ea7\u73af\u5883\n- \u901a\u8fc7 `@DynamicPropertySource` \u8fdb\u884c\u8fde\u63a5\uff0c\u5c06 JDBC URL \u6ce8\u5165\u5230 Spring \u4e0a\u4e0b\u6587\u4e2d\n\n## \u8986\u76d6\u7387\uff08JaCoCo\uff09\n\nMaven \u914d\u7f6e\u7247\u6bb5\uff1a\n```xml\n\n org.jacoco \n jacoco-maven-plugin \n 0.8.14 \n \n \n prepare-agent \n \n \n report \n verify \n report \n \n \n \n```\n\n## \u65ad\u8a00\uff08Assertions\uff09\n\n- \u4e3a\u4e86\u63d0\u9ad8\u53ef\u8bfb\u6027\uff0c\u4f18\u5148\u9009\u62e9 AssertJ (`assertThat`)\n- \u5bf9\u4e8e JSON \u54cd\u5e94\uff0c\u4f7f\u7528 `jsonPath`\n- \u5bf9\u4e8e\u5f02\u5e38\u6d4b\u8bd5\uff1a`assertThatThrownBy(...)`\n\n## \u6d4b\u8bd5\u6570\u636e\u6784\u5efa\u5668\uff08Test Data Builders\uff09\n\n```java\nclass MarketBuilder {\n private String name = \"Test\";\n MarketBuilder withName(String name) { this.name = name; return this; }\n Market build() { return new Market(null, name, MarketStatus.ACTIVE); }\n}\n```\n\n## CI \u547d\u4ee4\n\n- Maven\uff1a`mvn -T 4 test` \u6216 `mvn verify`\n- Gradle\uff1a`./gradlew test jacocoTestReport`\n\n**\u8bb0\u4f4f**\uff1a\u4fdd\u6301\u6d4b\u8bd5\u5feb\u901f\u3001\u9694\u79bb\u4e14\u5177\u6709\u786e\u5b9a\u6027\u3002\u6d4b\u8bd5\u7684\u662f\u884c\u4e3a\uff0c\u800c\u975e\u5b9e\u73b0\u7ec6\u8282\u3002\n"
+ },
+ "/Users/Library/Applications/xx/code/github/everything-claude-code-zh/skills/python-patterns/SKILL.md": {
+ "md5": "7bb68dce2d4ae7a20d7720761d382bae",
+ "content": "---\nname: python-patterns\ndescription: Pythonic idioms, PEP 8 standards, type hints, and best practices for building robust, efficient, and maintainable Python applications.\n---\n\n# Python \u5f00\u53d1\u6a21\u5f0f (Python Development Patterns)\n\n\u6784\u5efa\u5065\u58ee\u3001\u9ad8\u6548\u4e14\u53ef\u7ef4\u62a4\u7684\u5e94\u7528\u7a0b\u5e8f\u7684 Pythonic \u60ef\u7528\u6a21\u5f0f\u548c\u6700\u4f73\u5b9e\u8df5\u3002\n\n## \u6fc0\u6d3b\u65f6\u673a (When to Activate)\n\n- \u7f16\u5199\u65b0\u7684 Python \u4ee3\u7801\u65f6\n- \u8fdb\u884c Python \u4ee3\u7801\u5ba1\u67e5\uff08Review\uff09\u65f6\n- \u91cd\u6784\u73b0\u6709\u7684 Python \u4ee3\u7801\u65f6\n- \u8bbe\u8ba1 Python \u5305\uff08Package\uff09\u6216\u6a21\u5757\uff08Module\uff09\u65f6\n\n## \u6838\u5fc3\u539f\u5219\n\n### 1. \u53ef\u8bfb\u6027\u81f3\u4e0a (Readability Counts)\n\nPython \u4f18\u5148\u8003\u8651\u53ef\u8bfb\u6027\u3002\u4ee3\u7801\u5e94\u5f53\u76f4\u89c2\u4e14\u6613\u4e8e\u7406\u89e3\u3002\n\n```python\n# Good: \u6e05\u6670\u4e14\u6613\u8bfb\ndef get_active_users(users: list[User]) -> list[User]:\n \"\"\"\u4ec5\u4ece\u63d0\u4f9b\u7684\u5217\u8868\u4e2d\u8fd4\u56de\u6d3b\u8dc3\u7528\u6237\u3002\"\"\"\n return [user for user in users if user.is_active]\n\n\n# Bad: \u5de7\u5999\u4f46\u4ee4\u4eba\u56f0\u60d1\ndef get_active_users(u):\n return [x for x in u if x.a]\n```\n\n### 2. \u663e\u5f0f\u4f18\u4e8e\u9690\u5f0f (Explicit is Better Than Implicit)\n\n\u907f\u514d\u201c\u9b54\u6cd5\u201d\u884c\u4e3a\uff1b\u6e05\u6670\u5730\u8868\u8fbe\u4ee3\u7801\u7684\u529f\u80fd\u3002\n\n```python\n# Good: \u663e\u5f0f\u914d\u7f6e\nimport logging\n\nlogging.basicConfig(\n level=logging.INFO,\n format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'\n)\n\n# Bad: \u9690\u85cf\u7684\u526f\u4f5c\u7528\nimport some_module\nsome_module.setup() # \u8fd9\u5230\u5e95\u505a\u4e86\u4ec0\u4e48\uff1f\n```\n\n### 3. EAFP \u6a21\u5f0f - \u8bf7\u6c42\u5bbd\u6055\u6bd4\u8bf7\u6c42\u8bb8\u53ef\u66f4\u5bb9\u6613 (Easier to Ask Forgiveness Than Permission)\n\nPython \u503e\u5411\u4e8e\u4f7f\u7528\u5f02\u5e38\u5904\u7406\u800c\u975e\u9884\u5148\u68c0\u67e5\u6761\u4ef6\u3002\n\n```python\n# Good: EAFP \u98ce\u683c\ndef get_value(dictionary: dict, key: str) -> Any:\n try:\n return dictionary[key]\n except KeyError:\n return default_value\n\n# Bad: LBYL (Look Before You Leap\uff0c\u4e09\u601d\u800c\u540e\u884c) \u98ce\u683c\ndef get_value(dictionary: dict, key: str) -> Any:\n if key in dictionary:\n return dictionary[key]\n else:\n return default_value\n```\n\n## \u7c7b\u578b\u63d0\u793a (Type Hints)\n\n### \u57fa\u7840\u7c7b\u578b\u6ce8\u89e3\n\n```python\nfrom typing import Optional, List, Dict, Any\n\ndef process_user(\n user_id: str,\n data: Dict[str, Any],\n active: bool = True\n) -> Optional[User]:\n \"\"\"\u5904\u7406\u7528\u6237\u5e76\u8fd4\u56de\u66f4\u65b0\u540e\u7684 User \u6216 None\u3002\"\"\"\n if not active:\n return None\n return User(user_id, data)\n```\n\n### \u73b0\u4ee3\u7c7b\u578b\u63d0\u793a (Python 3.9+)\n\n```python\n# Python 3.9+ - \u4f7f\u7528\u5185\u7f6e\u7c7b\u578b\ndef process_items(items: list[str]) -> dict[str, int]:\n return {item: len(item) for item in items}\n\n# Python 3.8 \u53ca\u66f4\u65e9\u7248\u672c - \u4f7f\u7528 typing \u6a21\u5757\nfrom typing import List, Dict\n\ndef process_items(items: List[str]) -> Dict[str, int]:\n return {item: len(item) for item in items}\n```\n\n### \u7c7b\u578b\u522b\u540d (Type Aliases) \u548c TypeVar\n\n```python\nfrom typing import TypeVar, Union\n\n# \u590d\u6742\u7c7b\u578b\u7684\u7c7b\u578b\u522b\u540d\nJSON = Union[dict[str, Any], list[Any], str, int, float, bool, None]\n\ndef parse_json(data: str) -> JSON:\n return json.loads(data)\n\n# \u6cdb\u578b\nT = TypeVar('T')\n\ndef first(items: list[T]) -> T | None: \n \"\"\"\u8fd4\u56de\u7b2c\u4e00\u9879\uff0c\u5982\u679c\u5217\u8868\u4e3a\u7a7a\u5219\u8fd4\u56de None\u3002\"\"\"\n return items[0] if items else None\n```\n\n### \u57fa\u4e8e\u534f\u8bae (Protocol) \u7684\u9e2d\u5b50\u7c7b\u578b (Duck Typing)\n\n```python\nfrom typing import Protocol\n\nclass Renderable(Protocol):\n def render(self) -> str:\n \"\"\"\u5c06\u5bf9\u8c61\u6e32\u67d3\u4e3a\u5b57\u7b26\u4e32\u3002\"\"\"\n\ndef render_all(items: list[Renderable]) -> str:\n \"\"\"\u6e32\u67d3\u6240\u6709\u5b9e\u73b0\u4e86 Renderable \u534f\u8bae\u7684\u9879\u76ee\u3002\"\"\"\n return \"\\n\".join(item.render() for item in items)\n```\n\n## \u5f02\u5e38\u5904\u7406\u6a21\u5f0f (Error Handling Patterns)\n\n### \u7279\u5b9a\u5f02\u5e38\u5904\u7406\n\n```python\n# Good: \u6355\u83b7\u7279\u5b9a\u7684\u5f02\u5e38\ndef load_config(path: str) -> Config:\n try:\n with open(path) as f:\n return Config.from_json(f.read())\n except FileNotFoundError as e:\n raise ConfigError(f\"Config file not found: {path}\") from e\n except json.JSONDecodeError as e:\n raise ConfigError(f\"Invalid JSON in config: {path}\") from e\n\n# Bad: \u7a7a\u5f02\u5e38\u6355\u83b7\ndef load_config(path: str) -> Config:\n try:\n with open(path) as f:\n return Config.from_json(f.read())\n except:\n return None # \u9759\u9ed8\u5931\u8d25\uff01\n```\n\n### \u5f02\u5e38\u94fe (Exception Chaining)\n\n```python\ndef process_data(data: str) -> Result:\n try:\n parsed = json.loads(data)\n except json.JSONDecodeError as e:\n # \u4f7f\u7528\u5f02\u5e38\u94fe\u4ee5\u4fdd\u7559\u5806\u6808\u8ddf\u8e2a (traceback)\n raise ValueError(f\"Failed to parse data: {data}\") from e\n```\n\n### \u81ea\u5b9a\u4e49\u5f02\u5e38\u5c42\u6b21\u7ed3\u6784\n\n```python\nclass AppError(Exception):\n \"\"\"\u6240\u6709\u5e94\u7528\u7a0b\u5e8f\u9519\u8bef\u7684\u57fa\u7c7b\u3002\"\"\"\n pass\n\nclass ValidationError(AppError):\n \"\"\"\u5f53\u8f93\u5165\u9a8c\u8bc1\u5931\u8d25\u65f6\u5f15\u53d1\u3002\"\"\"\n pass\n\nclass NotFoundError(AppError):\n \"\"\"\u5f53\u8bf7\u6c42\u7684\u8d44\u6e90\u672a\u627e\u5230\u65f6\u5f15\u53d1\u3002\"\"\"\n pass\n\n# \u4f7f\u7528\u793a\u4f8b\ndef get_user(user_id: str) -> User:\n user = db.find_user(user_id)\n if not user:\n raise NotFoundError(f\"User not found: {user_id}\")\n return user\n```\n\n## \u4e0a\u4e0b\u6587\u7ba1\u7406\u5668 (Context Managers)\n\n### \u8d44\u6e90\u7ba1\u7406\n\n```python\n# Good: \u4f7f\u7528\u4e0a\u4e0b\u6587\u7ba1\u7406\u5668\ndef process_file(path: str) -> str:\n with open(path, 'r') as f:\n return f.read()\n\n# Bad: \u624b\u52a8\u8d44\u6e90\u7ba1\u7406\ndef process_file(path: str) -> str:\n f = open(path, 'r')\n try:\n return f.read()\n finally:\n f.close()\n```\n\n### \u81ea\u5b9a\u4e49\u4e0a\u4e0b\u6587\u7ba1\u7406\u5668\n\n```python\nfrom contextlib import contextmanager\n\n@contextmanager\ndef timer(name: str):\n \"\"\"\u7528\u4e8e\u5bf9\u4ee3\u7801\u5757\u8ba1\u65f6\u7684\u4e0a\u4e0b\u6587\u7ba1\u7406\u5668\u3002\"\"\"\n start = time.perf_counter()\n yield\n elapsed = time.perf_counter() - start\n print(f\"{name} took {elapsed:.4f} seconds\")\n\n# \u4f7f\u7528\u793a\u4f8b\nwith timer(\"data processing\"):\n process_large_dataset()\n```\n\n### \u4e0a\u4e0b\u6587\u7ba1\u7406\u5668\u7c7b\n\n```python\nclass DatabaseTransaction:\n def __init__(self, connection):\n self.connection = connection\n\n def __enter__(self):\n self.connection.begin_transaction()\n return self\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n if exc_type is None:\n self.connection.commit()\n else:\n self.connection.rollback()\n return False # \u4e0d\u8981\u6291\u5236\u5f02\u5e38\n\n# \u4f7f\u7528\u793a\u4f8b\nwith DatabaseTransaction(conn):\n user = conn.create_user(user_data)\n conn.create_profile(user.id, profile_data)\n```\n\n## \u63a8\u5bfc\u5f0f (Comprehensions) \u4e0e\u751f\u6210\u5668 (Generators)\n\n### \u5217\u8868\u63a8\u5bfc\u5f0f (List Comprehensions)\n\n```python\n# Good: \u7528\u4e8e\u7b80\u5355\u8f6c\u6362\u7684\u5217\u8868\u63a8\u5bfc\u5f0f\nnames = [user.name for user in users if user.is_active]\n\n# Bad: \u624b\u52a8\u5faa\u73af\nnames = []\nfor user in users:\n if user.is_active:\n names.append(user.name)\n\n# \u590d\u6742\u7684\u63a8\u5bfc\u5f0f\u5e94\u5f53\u5c55\u5f00\n# Bad: \u592a\u8fc7\u590d\u6742\nresult = [x * 2 for x in items if x > 0 if x % 2 == 0]\n\n# Good: \u4f7f\u7528\u751f\u6210\u5668\u51fd\u6570\ndef filter_and_transform(items: Iterable[int]) -> list[int]:\n result = []\n for x in items:\n if x > 0 and x % 2 == 0:\n result.append(x * 2)\n return result\n```\n\n### \u751f\u6210\u5668\u8868\u8fbe\u5f0f (Generator Expressions)\n\n```python\n# Good: \u7528\u4e8e\u5ef6\u8fdf\u6c42\u503c\u7684\u751f\u6210\u5668\ntotal = sum(x * x for x in range(1_000_000))\n\n# Bad: \u521b\u5efa\u4e86\u5de8\u5927\u7684\u4e2d\u95f4\u5217\u8868\ntotal = sum([x * x for x in range(1_000_000)])\n```\n\n### \u751f\u6210\u5668\u51fd\u6570 (Generator Functions)\n\n```python\ndef read_large_file(path: str) -> Iterator[str]:\n \"\"\"\u9010\u884c\u8bfb\u53d6\u5927\u6587\u4ef6\u3002\"\"\"\n with open(path) as f:\n for line in f:\n yield line.strip()\n\n# \u4f7f\u7528\u793a\u4f8b\nfor line in read_large_file(\"huge.txt\"):\n process(line)\n```\n\n## \u6570\u636e\u7c7b (Data Classes) \u4e0e\u547d\u540d\u5143\u7ec4 (Named Tuples)\n\n### \u6570\u636e\u7c7b (Data Classes)\n\n```python\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\n\n@dataclass\nclass User:\n \"\"\"\u5177\u6709\u81ea\u52a8\u751f\u6210 __init__\u3001__repr__ \u548c __eq__ \u7684\u7528\u6237\u5b9e\u4f53\u3002\"\"\"\n id: str\n name: str\n email: str\n created_at: datetime = field(default_factory=datetime.now)\n is_active: bool = True\n\n# \u4f7f\u7528\u793a\u4f8b\nuser = User(\n id=\"123\",\n name=\"Alice\",\n email=\"alice@example.com\"\n)\n```\n\n### \u5e26\u9a8c\u8bc1\u7684\u6570\u636e\u7c7b\n\n```python\n@dataclass\nclass User:\n email: str\n age: int\n\n def __post_init__(self):\n # \u9a8c\u8bc1\u7535\u5b50\u90ae\u4ef6\u683c\u5f0f\n if \"@\" not in self.email:\n raise ValueError(f\"Invalid email: {self.email}\")\n # \u9a8c\u8bc1\u5e74\u9f84\u8303\u56f4\n if self.age < 0 or self.age > 150:\n raise ValueError(f\"Invalid age: {self.age}\")\n```\n\n### \u547d\u540d\u5143\u7ec4 (Named Tuples)\n\n```python\nfrom typing import NamedTuple\n\nclass Point(NamedTuple):\n \"\"\"\u4e0d\u53ef\u53d8\u7684\u4e8c\u7ef4\u70b9\u3002\"\"\"\n x: float\n y: float\n\n def distance(self, other: 'Point') -> float:\n return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5\n\n# \u4f7f\u7528\u793a\u4f8b\np1 = Point(0, 0)\np2 = Point(3, 4)\nprint(p1.distance(p2)) # 5.0\n```\n\n## \u88c5\u9970\u5668 (Decorators)\n\n### \u51fd\u6570\u88c5\u9970\u5668\n\n```python\nimport functools\nimport time\n\ndef timer(func: Callable) -> Callable:\n \"\"\"\u7528\u4e8e\u5bf9\u51fd\u6570\u6267\u884c\u8fdb\u884c\u8ba1\u65f6\u7684\u88c5\u9970\u5668\u3002\"\"\"\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n start = time.perf_counter()\n result = func(*args, **kwargs)\n elapsed = time.perf_counter() - start\n print(f\"{func.__name__} took {elapsed:.4f}s\")\n return result\n return wrapper\n\n@timer\ndef slow_function():\n time.sleep(1)\n\n# slow_function() \u8f93\u51fa: slow_function took 1.0012s\n```\n\n### \u53c2\u6570\u5316\u88c5\u9970\u5668\n\n```python\ndef repeat(times: int):\n \"\"\"\u7528\u4e8e\u591a\u6b21\u91cd\u590d\u6267\u884c\u51fd\u6570\u7684\u88c5\u9970\u5668\u3002\"\"\"\n def decorator(func: Callable) -> Callable:\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n results = []\n for _ in range(times):\n results.append(func(*args, **kwargs))\n return results\n return wrapper\n return decorator\n\n@repeat(times=3)\ndef greet(name: str) -> str:\n return f\"Hello, {name}!\"\n\n# greet(\"Alice\") \u8fd4\u56de [\"Hello, Alice!\", \"Hello, Alice!\", \"Hello, Alice!\"]\n```\n\n### \u57fa\u4e8e\u7c7b\u7684\u88c5\u9970\u5668\n\n```python\nclass CountCalls:\n \"\"\"\u7edf\u8ba1\u51fd\u6570\u88ab\u8c03\u7528\u6b21\u6570\u7684\u88c5\u9970\u5668\u3002\"\"\"\n def __init__(self, func: Callable):\n functools.update_wrapper(self, func)\n self.func = func\n self.count = 0\n\n def __call__(self, *args, **kwargs):\n self.count += 1\n print(f\"{self.func.__name__} has been called {self.count} times\")\n return self.func(*args, **kwargs)\n\n@CountCalls\ndef process():\n pass\n\n# \u6bcf\u6b21\u8c03\u7528 process() \u90fd\u4f1a\u6253\u5370\u8c03\u7528\u8ba1\u6570\n```\n\n## \u5e76\u53d1\u6a21\u5f0f (Concurrency Patterns)\n\n### \u7ebf\u7a0b (Threading) \u5904\u7406 I/O \u5bc6\u96c6\u578b\u4efb\u52a1\n\n```python\nimport concurrent.futures\nimport threading\n\ndef fetch_url(url: str) -> str:\n \"\"\"\u83b7\u53d6 URL (I/O \u5bc6\u96c6\u578b\u64cd\u4f5c)\u3002\"\"\"\n import urllib.request\n with urllib.request.urlopen(url) as response:\n return response.read().decode()\n\ndef fetch_all_urls(urls: list[str]) -> dict[str, str]:\n \"\"\"\u4f7f\u7528\u7ebf\u7a0b\u5e76\u53d1\u5730\u83b7\u53d6\u591a\u4e2a URL\u3002\"\"\"\n with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:\n future_to_url = {executor.submit(fetch_url, url): url for url in urls}\n results = {}\n for future in concurrent.futures.as_completed(future_to_url):\n url = future_to_url[future]\n try:\n results[url] = future.result()\n except Exception as e:\n results[url] = f\"Error: {e}\"\n return results\n```\n\n### \u591a\u8fdb\u7a0b (Multiprocessing) \u5904\u7406 CPU \u5bc6\u96c6\u578b\u4efb\u52a1\n\n```python\ndef process_data(data: list[int]) -> int:\n \"\"\"CPU \u5bc6\u96c6\u578b\u8ba1\u7b97\u3002\"\"\"\n return sum(x ** 2 for x in data)\n\ndef process_all(datasets: list[list[int]]) -> list[int]:\n \"\"\"\u4f7f\u7528\u591a\u4e2a\u8fdb\u7a0b\u5904\u7406\u591a\u4e2a\u6570\u636e\u96c6\u3002\"\"\"\n with concurrent.futures.ProcessPoolExecutor() as executor:\n results = list(executor.map(process_data, datasets))\n return results\n```\n\n### Async/Await \u5904\u7406\u5e76\u53d1 I/O\n\n```python\nimport asyncio\n\nasync def fetch_async(url: str) -> str:\n \"\"\"\u5f02\u6b65\u83b7\u53d6 URL\u3002\"\"\"\n import aiohttp\n async with aiohttp.ClientSession() as session:\n async with session.get(url) as response:\n return await response.text()\n\nasync def fetch_all(urls: list[str]) -> dict[str, str]:\n \"\"\"\u5e76\u53d1\u5730\u83b7\u53d6\u591a\u4e2a URL\u3002\"\"\"\n tasks = [fetch_async(url) for url in urls]\n results = await asyncio.gather(*tasks, return_exceptions=True)\n return dict(zip(urls, results))\n```\n\n## \u5305\u7ec4\u7ec7 (Package Organization)\n\n### \u6807\u51c6\u9879\u76ee\u5e03\u5c40\n\n```\nmyproject/\n\u251c\u2500\u2500 src/\n\u2502 \u2514\u2500\u2500 mypackage/\n\u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u251c\u2500\u2500 main.py\n\u2502 \u251c\u2500\u2500 api/\n\u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u2514\u2500\u2500 routes.py\n\u2502 \u251c\u2500\u2500 models/\n\u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u2514\u2500\u2500 user.py\n\u2502 \u2514\u2500\u2500 utils/\n\u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2514\u2500\u2500 helpers.py\n\u251c\u2500\u2500 tests/\n\u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u251c\u2500\u2500 conftest.py\n\u2502 \u251c\u2500\u2500 test_api.py\n\u2502 \u2514\u2500\u2500 test_models.py\n\u251c\u2500\u2500 pyproject.toml\n\u251c\u2500\u2500 README.md\n\u2514\u2500\u2500 .gitignore\n```\n\n### \u5bfc\u5165\u89c4\u8303\n\n```python\n# Good: \u5bfc\u5165\u987a\u5e8f - \u6807\u51c6\u5e93\u3001\u7b2c\u4e09\u65b9\u5e93\u3001\u672c\u5730\u5e93\nimport os\nimport sys\nfrom pathlib import Path\n\nimport requests\nfrom fastapi import FastAPI\n\nfrom mypackage.models import User\nfrom mypackage.utils import format_name\n\n# Good: \u4f7f\u7528 isort \u81ea\u52a8\u8fdb\u884c\u5bfc\u5165\u6392\u5e8f\n# pip install isort\n```\n\n### \u7528\u4e8e\u5305\u5bfc\u51fa\u7684 __init__.py\n\n```python\n# mypackage/__init__.py\n\"\"\"mypackage - \u4e00\u4e2a Python \u5305\u793a\u4f8b\u3002\"\"\"\n\n__version__ = \"1.0.0\"\n\n# \u5728\u5305\u7ea7\u522b\u5bfc\u51fa\u4e3b\u8981\u7684\u7c7b/\u51fd\u6570\nfrom mypackage.models import User, Post\nfrom mypackage.utils import format_name\n\n__all__ = [\"User\", \"Post\", \"format_name\"]\n```\n\n## \u5185\u5b58\u4e0e\u6027\u80fd\n\n### \u4f7f\u7528 __slots__ \u63d0\u9ad8\u5185\u5b58\u6548\u7387\n\n```python\n# Bad: \u5e38\u89c4\u7c7b\u4f7f\u7528 __dict__ (\u5360\u7528\u66f4\u591a\u5185\u5b58)\nclass Point:\n def __init__(self, x: float, y: float):\n self.x = x\n self.y = y\n\n# Good: __slots__ \u51cf\u5c11\u5185\u5b58\u4f7f\u7528\nclass Point:\n __slots__ = ['x', 'y']\n\n def __init__(self, x: float, y: float):\n self.x = x\n self.y = y\n```\n\n### \u7528\u4e8e\u5927\u6570\u636e\u7684\u751f\u6210\u5668\n\n```python\n# Bad: \u5728\u5185\u5b58\u4e2d\u8fd4\u56de\u5b8c\u6574\u5217\u8868\ndef read_lines(path: str) -> list[str]:\n with open(path) as f:\n return [line.strip() for line in f]\n\n# Good: \u4e00\u6b21\u4ea7\u51fa\u4e00\u884c\ndef read_lines(path: str) -> Iterator[str]:\n with open(path) as f:\n for line in f:\n yield line.strip()\n```\n\n### \u907f\u514d\u5728\u5faa\u73af\u4e2d\u8fdb\u884c\u5b57\u7b26\u4e32\u62fc\u63a5\n\n```python\n# Bad: \u7531\u4e8e\u5b57\u7b26\u4e32\u4e0d\u53ef\u53d8\u6027\uff0c\u590d\u6742\u5ea6\u4e3a O(n\u00b2)\nresult = \"\"\nfor item in items:\n result += str(item)\n\n# Good: \u4f7f\u7528 join\uff0c\u590d\u6742\u5ea6\u4e3a O(n)\nresult = \"\".join(str(item) for item in items)\n\n# Good: \u4f7f\u7528 StringIO \u8fdb\u884c\u6784\u5efa\nfrom io import StringIO\n\nbuffer = StringIO()\nfor item in items:\n buffer.write(str(item))\nresult = buffer.getvalue()\n```\n\n## Python \u5de5\u5177\u94fe\u96c6\u6210\n\n### \u5e38\u7528\u547d\u4ee4\n\n```bash\n# \u4ee3\u7801\u683c\u5f0f\u5316\nblack .\nisort .\n\n# \u9759\u6001\u68c0\u67e5 (Linting)\nruff check .\npylint mypackage/\n\n# \u7c7b\u578b\u68c0\u67e5\nmypy .\n\n# \u6d4b\u8bd5\npytest --cov=mypackage --cov-report=html\n\n# \u5b89\u5168\u626b\u63cf\nbandit -r .\n\n# \u4f9d\u8d56\u7ba1\u7406\npip-audit\nsafety check\n```\n\n### pyproject.toml \u914d\u7f6e\n\n```toml\n[project]\nname = \"mypackage\"\nversion = \"1.0.0\"\nrequires-python = \">=3.9\"\ndependencies = [\n \"requests>=2.31.0\",\n \"pydantic>=2.0.0\",\n]\n\n[project.optional-dependencies]\ndev = [\n \"pytest>=7.4.0\",\n \"pytest-cov>=4.1.0\",\n \"black>=23.0.0\",\n \"ruff>=0.1.0\",\n \"mypy>=1.5.0\",\n]\n\n[tool.black]\nline-length = 88\ntarget-version = ['py39']\n\n[tool.ruff]\nline-length = 88\nselect = [\"E\", \"F\", \"I\", \"N\", \"W\"]\n\n[tool.mypy]\npython_version = \"3.9\"\nwarn_return_any = true\nwarn_unused_configs = true\ndisallow_untyped_defs = true\n\n[tool.pytest.ini_options]\ntestpaths = [\"tests\"]\naddopts = \"--cov=mypackage --cov-report=term-missing\"\n```\n\n## \u5feb\u901f\u53c2\u8003\uff1aPython \u60ef\u7528\u6cd5 (Python Idioms)\n\n| \u60ef\u7528\u6cd5 | \u63cf\u8ff0 |\n|-------|-------------|\n| EAFP | \u8bf7\u6c42\u5bbd\u6055\u6bd4\u8bf7\u6c42\u8bb8\u53ef\u66f4\u5bb9\u6613 (Easier to Ask Forgiveness than Permission) |\n| \u4e0a\u4e0b\u6587\u7ba1\u7406\u5668 (Context managers) | \u4f7f\u7528 `with` \u8fdb\u884c\u8d44\u6e90\u7ba1\u7406 |\n| \u5217\u8868\u63a8\u5bfc\u5f0f (List comprehensions) | \u7528\u4e8e\u7b80\u5355\u8f6c\u6362 |\n| \u751f\u6210\u5668 (Generators) | \u7528\u4e8e\u5ef6\u8fdf\u6c42\u503c\u548c\u5927\u578b\u6570\u636e\u96c6 |\n| \u7c7b\u578b\u63d0\u793a (Type hints) | \u4e3a\u51fd\u6570\u7b7e\u540d\u6dfb\u52a0\u6ce8\u89e3 |\n| \u6570\u636e\u7c7b (Dataclasses) | \u7528\u4e8e\u5e26\u6709\u81ea\u52a8\u751f\u6210\u65b9\u6cd5\u7684\u5404\u79cd\u6570\u636e\u5bb9\u5668 |\n| `__slots__` | \u7528\u4e8e\u5185\u5b58\u4f18\u5316 |\n| f-strings | \u7528\u4e8e\u5b57\u7b26\u4e32\u683c\u5f0f\u5316 (Python 3.6+) |\n| `pathlib.Path` | \u7528\u4e8e\u8def\u5f84\u64cd\u4f5c (Python 3.4+) |\n| `enumerate` | \u5728\u5faa\u73af\u4e2d\u83b7\u53d6\u7d22\u5f15-\u5143\u7d20\u5bf9 |\n\n## \u5e94\u907f\u514d\u7684\u53cd\u6a21\u5f0f (Anti-Patterns)\n\n```python\n# Bad: \u53ef\u53d8\u9ed8\u8ba4\u53c2\u6570\ndef append_to(item, items=[]):\n items.append(item)\n return items\n\n# Good: \u4f7f\u7528 None \u5e76\u521b\u5efa\u65b0\u5217\u8868\ndef append_to(item, items=None):\n if items is None:\n items = []\n items.append(item)\n return items\n\n# Bad: \u4f7f\u7528 type() \u68c0\u67e5\u7c7b\u578b\nif type(obj) == list:\n process(obj)\n\n# Good: \u4f7f\u7528 isinstance\nif isinstance(obj, list):\n process(obj)\n\n# Bad: \u4f7f\u7528 == \u4e0e None \u6bd4\u8f83\nif value == None:\n process()\n\n# Good: \u4f7f\u7528 is\nif value is None:\n process()\n\n# Bad: from module import *\nfrom os.path import *\n\n# Good: \u663e\u5f0f\u5bfc\u5165\nfrom os.path import join, exists\n\n# Bad: \u7a7a\u5f02\u5e38\u6355\u83b7\ntry:\n risky_operation()\nexcept:\n pass\n\n# Good: \u7279\u5b9a\u5f02\u5e38\ntry:\n risky_operation()\nexcept SpecificError as e:\n logger.error(f\"Operation failed: {e}\")\n```\n\n__\u8bb0\u4f4f__\uff1aPython \u4ee3\u7801\u5e94\u5f53\u662f\u6613\u8bfb\u7684\u3001\u663e\u5f0f\u7684\uff0c\u5e76\u9075\u5faa\u6700\u5c0f\u60ca\u8bb6\u539f\u5219\u3002\u5982\u6709\u7591\u95ee\uff0c\u8bf7\u4f18\u5148\u8003\u8651\u6e05\u6670\u5ea6\u800c\u975e\u5de7\u5999\u6027\u3002\n\n```"
}
}
\ No newline at end of file
diff --git a/translation_workdir/scripts/batch_processor.py b/translation_workdir/scripts/batch_processor.py
index 6e8eaa7..7570728 100644
--- a/translation_workdir/scripts/batch_processor.py
+++ b/translation_workdir/scripts/batch_processor.py
@@ -31,13 +31,15 @@ def main():
parser.add_argument("--model", default="gemini-3-flash-preview", help="模型名称")
args = parser.parse_args()
- # 1. 扫描项目中所有的 .md 文件 (排除工作目录本身)
+ # 1. 扫描项目中所有的 .md 文件 (排除工作目录本身及上游中文目录)
all_md_files = []
for root, dirs, files in os.walk("."):
- if any(x in root for x in [WORKDIR, "bak", "node_modules", ".git", "_zh"]):
+ # 排除系统目录、翻译工作区、输出目录以及上游的中文目录 (zh-TW/zh-CN)
+ if any(x in root for x in [WORKDIR, "bak", "node_modules", ".git", "_zh", "zh-TW", "zh-CN"]):
continue
for f in files:
- if f.endswith(".md") and not f.endswith("_zh.md"):
+ # 排除已翻译文件(_zh.md)及上游中文文件(.zh-CN.md等)
+ if f.endswith(".md") and not f.endswith("_zh.md") and "zh-CN" not in f and "zh-TW" not in f:
all_md_files.append(os.path.abspath(os.path.join(root, f)))
if not os.path.exists(args.prompt):
diff --git a/translation_workdir/scripts/sync_upstream.sh b/translation_workdir/scripts/sync_upstream.sh
old mode 100644
new mode 100755