svharness 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +531 -0
- package/bin/cli.js +3 -0
- package/dist/adapters/_frontmatter.js +24 -0
- package/dist/adapters/claude-code.js +12 -0
- package/dist/adapters/codechat.js +12 -0
- package/dist/adapters/cursor.js +19 -0
- package/dist/adapters/generic.js +19 -0
- package/dist/adapters/index.js +26 -0
- package/dist/adapters/qoder.js +12 -0
- package/dist/commands/apply.js +272 -0
- package/dist/commands/init.js +420 -0
- package/dist/core/agent-injector.js +192 -0
- package/dist/core/next-steps.js +91 -0
- package/dist/core/render-meta.js +81 -0
- package/dist/core/repomix-pack.js +54 -0
- package/dist/core/scaffold.js +93 -0
- package/dist/core/state.js +80 -0
- package/dist/index.js +239 -0
- package/dist/types.js +5 -0
- package/dist/utils/baseline-copy.js +591 -0
- package/dist/utils/baseline-defaults.js +106 -0
- package/dist/utils/logger.js +56 -0
- package/dist/utils/validate-args.js +132 -0
- package/dist/utils/version.js +23 -0
- package/dist/wiki/abort.js +30 -0
- package/dist/wiki/config.js +79 -0
- package/dist/wiki/defaults.js +16 -0
- package/dist/wiki/envLoader.js +78 -0
- package/dist/wiki/index.js +29 -0
- package/dist/wiki/openaiCompat.js +219 -0
- package/dist/wiki/repowikiCanonicalSections.js +67 -0
- package/dist/wiki/repowikiCheckpoint.js +106 -0
- package/dist/wiki/repowikiConfig.js +9 -0
- package/dist/wiki/repowikiGit.js +73 -0
- package/dist/wiki/repowikiIndexer.js +824 -0
- package/dist/wiki/repowikiMarkdownPost.js +123 -0
- package/dist/wiki/repowikiMetadataContent.js +64 -0
- package/dist/wiki/repowikiMetadataJson.js +15 -0
- package/dist/wiki/repowikiScanner.js +156 -0
- package/dist/wiki/repowikiStructureNav.js +286 -0
- package/dist/wiki/repowikiStructureNormalize.js +218 -0
- package/dist/wiki/wikiStructureXml.js +316 -0
- package/dist/wiki/wikiTasksWriter.js +127 -0
- package/package.json +57 -0
- package/templates/_shared/apply-skills/harness-apply-skills-main.md +91 -0
- package/templates/_shared/build-rules/harness-build-rule-agent-agnostic.md +35 -0
- package/templates/_shared/build-rules/harness-build-rule-chinese-only.md +49 -0
- package/templates/_shared/build-rules/harness-build-rule-memory-write.md +31 -0
- package/templates/_shared/build-rules/harness-build-rule-orchestrator-flow.md +25 -0
- package/templates/_shared/build-rules/harness-build-rule-skills-tasks-output.md +35 -0
- package/templates/_shared/build-rules/harness-build-rule-specs-schema.md +32 -0
- package/templates/_shared/build-rules/harness-build-rule-user-interaction.md +63 -0
- package/templates/_shared/build-skills/harness-build-skill-knowledge-builder.md +120 -0
- package/templates/_shared/build-skills/harness-build-skill-orchestrator.md +87 -0
- package/templates/_shared/build-skills/harness-build-skill-spec-builder.md +85 -0
- package/templates/_shared/build-skills/harness-build-skill-wiki-writer.md +77 -0
- package/templates/_shared/meta/AGENTS.md.ejs +53 -0
- package/templates/_shared/meta/CHANGELOG.md.ejs +15 -0
- package/templates/_shared/meta/README.md.ejs +51 -0
- package/templates/_shared/meta/VERSION.ejs +1 -0
- package/templates/_shared/meta/harness.yaml.ejs +52 -0
- package/templates/_shared/skeleton/agent-env/memory/categories/.gitkeep +1 -0
- package/templates/_shared/skeleton/agent-env/memory/inbox/.gitkeep +1 -0
- package/templates/_shared/skeleton/agent-env/skills/.gitkeep +1 -0
- package/templates/_shared/skeleton/agent-env/tools/.gitkeep +1 -0
- package/templates/_shared/skeleton/assets/baseline/code/.gitkeep +1 -0
- package/templates/_shared/skeleton/assets/baseline/repomix/.gitkeep +1 -0
- package/templates/_shared/skeleton/assets/baseline/wiki/.gitkeep +1 -0
- package/templates/_shared/skeleton/assets/raw/.gitkeep +1 -0
- package/templates/_shared/skeleton/assets/requirements/.gitkeep +1 -0
- package/templates/_shared/skeleton/commands/install/.gitkeep +1 -0
- package/templates/_shared/skeleton/commands/update/.gitkeep +1 -0
- package/templates/_shared/skeleton/specs/behavior/schema.json +39 -0
- package/templates/_shared/skeleton/specs/interfaces/schema.json +38 -0
- package/templates/_shared/skeleton/specs/signals/schema.json +37 -0
- package/templates/_shared/skeleton/specs/ui/schema.json +44 -0
- package/templates/_shared/skeleton/tasks/templates/.gitkeep +0 -0
- package/templates/android-compose/skeleton/agent-env/rules/harness-compose-mandatory.mdc +49 -0
- package/templates/android-compose/skeleton/agent-env/rules/harness-coroutines-scope.mdc +52 -0
- package/templates/android-compose/skeleton/agent-env/rules/harness-hilt-injection.mdc +47 -0
- package/templates/android-compose/skeleton/agent-env/rules/harness-mvi-layering.mdc +58 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/SKILL.md +260 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/references/gradle-module-patterns.md +66 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/references/implementation-checklist.md +45 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/references/udf-data-flow.md +80 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-cli/SKILL.md +79 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-cli/references/interact.md +83 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-cli/references/journeys.md +97 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/SKILL.md +162 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/canonical-sources.md +116 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/diagnostics.md +182 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/report-template.md +135 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/scoring.md +277 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/search-playbook.md +303 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/scripts/compose-reports.init.gradle +58 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-state/SKILL.md +196 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-ui/SKILL.md +192 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-ui/references/composable-api-guide.md +123 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-ui/references/performance-recipes.md +97 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-ui/references/state-patterns.md +93 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-kotlin-coroutines/SKILL.md +167 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/SKILL.md +45 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/CONFIGURATION.md +44 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/KEEP-RULES-IMPACT-HIERARCHY.md +83 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/REDUNDANT-RULES.md +222 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/REFLECTION-GUIDE.md +139 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/android/topic/performance/app-optimization/enable-app-optimization.md +176 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/android/training/testing/other-components/ui-automator.md +312 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/SKILL.md +87 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/analysis-of-the-project-and-layout.md +42 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/android/develop/ui/compose/designsystems/migrate-xml-theme-to-compose.md +168 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/android/develop/ui/compose/setup-compose-dependencies-and-compiler.md +183 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/identify-optimal-xml-candidate.md +31 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/xml-layout-migration.md +86 -0
- package/templates/android-xml/skeleton/agent-env/rules/seed-aidl-thread.md +29 -0
- package/templates/android-xml/skeleton/agent-env/rules/seed-lifecycle-awareness.md +32 -0
- package/templates/android-xml/skeleton/agent-env/rules/seed-mvc-layering.md +32 -0
- package/templates/android-xml/skeleton/agent-env/rules/seed-view-binding.md +33 -0
- package/templates/android-xml/skeleton/agent-env/rules/seed-xml-styling.md +27 -0
- package/templates/cpp/skeleton/agent-env/rules/seed-cmake-explicit-sources.md +31 -0
- package/templates/cpp/skeleton/agent-env/rules/seed-header-guards.md +34 -0
- package/templates/cpp/skeleton/agent-env/rules/seed-include-layering.md +39 -0
- package/templates/cpp/skeleton/agent-env/rules/seed-no-cyclic-deps.md +29 -0
- package/templates/cpp/skeleton/agent-env/rules/seed-raii.md +30 -0
- package/templates/python/skeleton/agent-env/rules/seed-context-managers.md +60 -0
- package/templates/python/skeleton/agent-env/rules/seed-docstrings.md +48 -0
- package/templates/python/skeleton/agent-env/rules/seed-import-order.md +49 -0
- package/templates/python/skeleton/agent-env/rules/seed-pep8-naming.md +45 -0
- package/templates/python/skeleton/agent-env/rules/seed-type-annotations.md +43 -0
- package/templates/web-react/skeleton/agent-env/rules/seed-controlled-component.md +43 -0
- package/templates/web-react/skeleton/agent-env/rules/seed-effect-cleanup.md +43 -0
- package/templates/web-react/skeleton/agent-env/rules/seed-hook-rules.md +42 -0
- package/templates/web-react/skeleton/agent-env/rules/seed-key-stability.md +39 -0
- package/templates/web-react/skeleton/agent-env/rules/seed-no-props-drilling.md +43 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# 头文件守卫(种子规则)
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
所有 `.h` / `.hpp` 文件必须使用 `#pragma once`(或等价的 `#ifndef` 守卫)。
|
|
8
|
+
禁止在头文件中放 `using namespace std;` 或非内联的函数定义。
|
|
9
|
+
|
|
10
|
+
## 正例
|
|
11
|
+
|
|
12
|
+
```cpp
|
|
13
|
+
// wireless/wireless_controller.h
|
|
14
|
+
#pragma once
|
|
15
|
+
#include <cstdint>
|
|
16
|
+
|
|
17
|
+
namespace dsv::wireless {
|
|
18
|
+
class WirelessController { /* ... */ };
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 反例
|
|
23
|
+
|
|
24
|
+
```cpp
|
|
25
|
+
// ❌ 头文件污染命名空间
|
|
26
|
+
using namespace std;
|
|
27
|
+
// ❌ 头文件包含非 inline 的函数体
|
|
28
|
+
int Add(int a, int b) { return a + b; }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 如何检测
|
|
32
|
+
|
|
33
|
+
- 每个头文件首 5 行必须出现 `#pragma once` 或 `#ifndef ... #define`。
|
|
34
|
+
- 头文件中 grep `^using namespace` 命中即违规。
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# include 分层顺序(种子规则)
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
每个 `.cpp` 的 include 顺序:
|
|
8
|
+
**自身配套头 → 本模块头 → 项目其它模块头 → 三方库 → C++ 标准库 → C 标准库**。
|
|
9
|
+
每组之间一个空行。
|
|
10
|
+
|
|
11
|
+
## 正例
|
|
12
|
+
|
|
13
|
+
```cpp
|
|
14
|
+
// wireless_controller.cpp
|
|
15
|
+
#include "wireless/wireless_controller.h" // 自身头
|
|
16
|
+
|
|
17
|
+
#include "wireless/wireless_parser.h" // 本模块
|
|
18
|
+
|
|
19
|
+
#include "common/logger.h" // 其它模块
|
|
20
|
+
|
|
21
|
+
#include <spdlog/spdlog.h> // 三方
|
|
22
|
+
|
|
23
|
+
#include <memory> // 标准库
|
|
24
|
+
#include <vector>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 反例
|
|
28
|
+
|
|
29
|
+
```cpp
|
|
30
|
+
// ❌ 顺序乱,三方夹在自家中间
|
|
31
|
+
#include <spdlog/spdlog.h>
|
|
32
|
+
#include "wireless/wireless_controller.h"
|
|
33
|
+
#include <memory>
|
|
34
|
+
#include "common/logger.h"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 如何检测
|
|
38
|
+
|
|
39
|
+
- clang-format 的 `IncludeCategories` 自动检查;PR 评审拒绝乱序。
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# 禁止模块循环依赖(种子规则)
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
每个模块目录是一个 CMake target;模块间只能**单向依赖**。
|
|
8
|
+
出现循环立即视为 bug —— 通过**下沉公共抽象**或**上浮接口**解决。
|
|
9
|
+
|
|
10
|
+
## 正例
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
core → (no deps)
|
|
14
|
+
common → core
|
|
15
|
+
wireless → common, core
|
|
16
|
+
ui → wireless, common, core
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 反例
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
wireless → ui → wireless ❌
|
|
23
|
+
common → wireless → common ❌
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 如何检测
|
|
27
|
+
|
|
28
|
+
- `cmake --graphviz=deps.dot ..` 生成依赖图,人工/脚本检测环。
|
|
29
|
+
- PR 评审:新增 `target_link_libraries` 时必须追溯目标的反向依赖。
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# RAII:禁止裸 new/delete(种子规则)
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
所有资源(内存、文件、锁、句柄)必须通过 RAII 对象持有;
|
|
8
|
+
禁止裸 `new` / `delete` / `malloc` / `free` / 手写 `fopen/fclose` 对称。
|
|
9
|
+
|
|
10
|
+
## 正例
|
|
11
|
+
|
|
12
|
+
```cpp
|
|
13
|
+
auto buf = std::make_unique<uint8_t[]>(len);
|
|
14
|
+
std::lock_guard<std::mutex> lk(mu_);
|
|
15
|
+
std::ifstream in(path);
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 反例
|
|
19
|
+
|
|
20
|
+
```cpp
|
|
21
|
+
// ❌ 裸指针 + 手动释放,异常安全无保证
|
|
22
|
+
uint8_t* buf = new uint8_t[len];
|
|
23
|
+
doSomething(buf);
|
|
24
|
+
delete[] buf;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 如何检测
|
|
28
|
+
|
|
29
|
+
- grep `new\s+\w` / `delete\s` / `\bmalloc\(` / `\bfree\(` 命中需评审;
|
|
30
|
+
允许白名单:第三方 C API 适配、placement new。
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# 上下文管理器(种子规则)
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
文件、网络连接、锁、数据库游标等资源**必须使用 `with` 语句**管理生命周期,禁止裸 `open()`/`.close()` 配对。
|
|
8
|
+
异常安全由上下文管理器保证,无需手写 `try/finally`。
|
|
9
|
+
|
|
10
|
+
自定义资源类应实现 `__enter__` / `__exit__`,或使用 `contextlib.contextmanager` 装饰器。
|
|
11
|
+
|
|
12
|
+
## 正例
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
with open("data.json", "r", encoding="utf-8") as f:
|
|
16
|
+
data = json.load(f)
|
|
17
|
+
|
|
18
|
+
with httpx.Client() as client:
|
|
19
|
+
resp = client.get("https://api.example.com/health")
|
|
20
|
+
|
|
21
|
+
with threading.Lock():
|
|
22
|
+
shared_counter += 1
|
|
23
|
+
|
|
24
|
+
# 自定义上下文管理器
|
|
25
|
+
from contextlib import contextmanager
|
|
26
|
+
|
|
27
|
+
@contextmanager
|
|
28
|
+
def temp_env(key: str, value: str):
|
|
29
|
+
old = os.environ.get(key)
|
|
30
|
+
os.environ[key] = value
|
|
31
|
+
try:
|
|
32
|
+
yield
|
|
33
|
+
finally:
|
|
34
|
+
if old is None:
|
|
35
|
+
del os.environ[key]
|
|
36
|
+
else:
|
|
37
|
+
os.environ[key] = old
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 反例
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
# ❌ 裸 open/close,异常时泄露
|
|
44
|
+
f = open("data.json")
|
|
45
|
+
data = json.load(f)
|
|
46
|
+
f.close()
|
|
47
|
+
|
|
48
|
+
# ❌ 手写 try/finally 代替 with
|
|
49
|
+
lock = threading.Lock()
|
|
50
|
+
lock.acquire()
|
|
51
|
+
try:
|
|
52
|
+
shared_counter += 1
|
|
53
|
+
finally:
|
|
54
|
+
lock.release()
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 如何检测
|
|
58
|
+
|
|
59
|
+
- `ruff` 规则 `SIM115`(使用上下文管理器打开文件)。
|
|
60
|
+
- grep `\.open\(` 但不在 `with` 块中。
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# 文档字符串(种子规则)
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
所有公共模块、类、函数/方法必须写 docstring(PEP 257)。
|
|
8
|
+
格式统一为 Google style 或 NumPy style,项目内二选一后强制执行。
|
|
9
|
+
docstring 需涵盖:一句话摘要、`Args`/`Returns`/`Raises`/`Examples`。
|
|
10
|
+
|
|
11
|
+
## 正例
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
def divide(a: float, b: float) -> float:
|
|
15
|
+
"""返回 a / b 的商。
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
a: 被除数。
|
|
19
|
+
b: 除数。
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
a / b 的浮点结果。
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
ZeroDivisionError: 当 b 为 0 时抛出。
|
|
26
|
+
"""
|
|
27
|
+
if b == 0:
|
|
28
|
+
raise ZeroDivisionError("除数不能为 0")
|
|
29
|
+
return a / b
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 反例
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
# ❌ 无 docstring
|
|
36
|
+
def divide(a, b):
|
|
37
|
+
return a / b
|
|
38
|
+
|
|
39
|
+
# ❌ 无意义 docstring
|
|
40
|
+
def divide(a, b):
|
|
41
|
+
"""divide function."""
|
|
42
|
+
return a / b
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 如何检测
|
|
46
|
+
|
|
47
|
+
- `ruff` 规则 `D`(pydocstyle)或 `interrogate` 检查覆盖率。
|
|
48
|
+
- CI 设置 docstring 覆盖率阈值(推荐 ≥ 80% 公共 API)。
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# 导入顺序(种子规则)
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
import 语句分三组,组间空一行,组内按字母序排列:
|
|
8
|
+
1. **标准库**(`os`, `sys`, `pathlib` …)
|
|
9
|
+
2. **第三方库**(`requests`, `numpy`, `fastapi` …)
|
|
10
|
+
3. **本地/项目内模块**(`from .utils import ...`)
|
|
11
|
+
|
|
12
|
+
禁止 `from module import *`(星号导入),禁止在函数内 import(除非处理循环引用且加注释说明)。
|
|
13
|
+
|
|
14
|
+
## 正例
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
import requests
|
|
23
|
+
|
|
24
|
+
from myapp.core.config import Settings
|
|
25
|
+
from myapp.utils.validators import validate_email
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 反例
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
# ❌ 星号导入
|
|
32
|
+
from os import *
|
|
33
|
+
|
|
34
|
+
# ❌ 乱序混排
|
|
35
|
+
import requests
|
|
36
|
+
import os
|
|
37
|
+
from myapp.core import config
|
|
38
|
+
import sys
|
|
39
|
+
|
|
40
|
+
# ❌ 函数内无注释 import
|
|
41
|
+
def do_something():
|
|
42
|
+
import json # 无循环引用说明
|
|
43
|
+
...
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 如何检测
|
|
47
|
+
|
|
48
|
+
- `ruff` 规则 `I`(isort)或直接使用 `isort --check-only`。
|
|
49
|
+
- 禁止 `# isort: skip` 无注释使用。
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# PEP 8 命名规范(种子规则)
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
Python 命名必须遵守 PEP 8:
|
|
8
|
+
- 模块/包:`snake_case`(全小写 + 下划线)
|
|
9
|
+
- 函数/方法/变量:`snake_case`
|
|
10
|
+
- 类/异常:`PascalCase`
|
|
11
|
+
- 常量:`UPPER_CASE`
|
|
12
|
+
- 私有成员:单下划线前缀 `_internal`;避免双下划线前缀(name mangling)除非必要
|
|
13
|
+
|
|
14
|
+
## 正例
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
MAX_RETRIES = 3
|
|
18
|
+
|
|
19
|
+
class UserRepository:
|
|
20
|
+
def __init__(self, db_url: str):
|
|
21
|
+
self._db_url = db_url # 私有属性
|
|
22
|
+
|
|
23
|
+
def find_by_id(self, user_id: int) -> User | None:
|
|
24
|
+
...
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 反例
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
# ❌ 驼峰命名函数
|
|
31
|
+
def getUserById(userId):
|
|
32
|
+
...
|
|
33
|
+
|
|
34
|
+
# ❌ 类用小写
|
|
35
|
+
class user_repository:
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
# ❌ 常量用小写
|
|
39
|
+
max_retries = 3
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 如何检测
|
|
43
|
+
|
|
44
|
+
- 启用 `ruff` 规则 `N`(pep8-naming)或 `flake8` 插件 `pep8-naming`。
|
|
45
|
+
- CI 中 `ruff check --select N` 命中即违规。
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 类型注解(种子规则)
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
所有公共函数/方法必须标注参数类型与返回值类型(PEP 484 / PEP 585)。
|
|
8
|
+
使用 `from __future__ import annotations`(Python 3.7+)启用延迟求值,避免循环引用。
|
|
9
|
+
容器类型使用 `list[X]` / `dict[K,V]`(Python 3.9+),而非 `typing.List`。
|
|
10
|
+
|
|
11
|
+
## 正例
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
def parse_config(path: str) -> dict[str, str | int]:
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
def query_users(
|
|
20
|
+
ids: list[int],
|
|
21
|
+
*,
|
|
22
|
+
limit: int = 100,
|
|
23
|
+
) -> list[User]:
|
|
24
|
+
...
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 反例
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
# ❌ 无类型注解
|
|
31
|
+
def parse_config(path):
|
|
32
|
+
...
|
|
33
|
+
|
|
34
|
+
# ❌ 过时的 typing 泛型
|
|
35
|
+
from typing import List, Dict
|
|
36
|
+
def get_map() -> Dict[str, List[int]]:
|
|
37
|
+
...
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 如何检测
|
|
41
|
+
|
|
42
|
+
- `mypy --strict` 或 `pyright` 全覆盖。
|
|
43
|
+
- 禁止 `# type: ignore` 无注释使用(grep 扫 `type: ignore` 需写原因)。
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 表单走受控组件
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
可编辑的表单元素(`<input>` `<textarea>` `<select>`)**必须使用受控模式**:`value` 绑定到 state,`onChange` 同步回写 state。禁止依赖 `defaultValue` + DOM 查询读回值(`ref.current.value`)做业务逻辑。
|
|
8
|
+
|
|
9
|
+
## 正例
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
function LoginForm() {
|
|
13
|
+
const [email, setEmail] = useState('');
|
|
14
|
+
const [pwd, setPwd] = useState('');
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<form onSubmit={(e) => { e.preventDefault(); submit(email, pwd); }}>
|
|
18
|
+
<input value={email} onChange={(e) => setEmail(e.target.value)} />
|
|
19
|
+
<input type="password" value={pwd} onChange={(e) => setPwd(e.target.value)} />
|
|
20
|
+
<button type="submit" disabled={!email || !pwd}>登录</button>
|
|
21
|
+
</form>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 反例
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
function LoginForm() {
|
|
30
|
+
const emailRef = useRef<HTMLInputElement>(null);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<form onSubmit={() => submit(emailRef.current!.value)}> {/* ❌ 直接读 DOM 值 */}
|
|
34
|
+
<input defaultValue="" ref={emailRef} /> {/* ❌ 非受控 */}
|
|
35
|
+
</form>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 如何检测
|
|
41
|
+
|
|
42
|
+
- grep 扫 `defaultValue=` 与业务组件 `useRef<HTMLInputElement>` 的同文件共存,人工复核是否用于读取用户输入。
|
|
43
|
+
- code-review checklist:任何 `<input>`/`<textarea>`/`<select>` 必须同时出现 `value=` 与 `onChange=`。
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# useEffect 必须返回 cleanup
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
`useEffect` 内若创建了**订阅、定时器、事件监听、WebSocket、AbortController、异步 fetch 任务**,必须返回 cleanup 函数在组件卸载或依赖变更时释放,否则会内存泄漏、状态错乱、"Can't perform a React state update on an unmounted component" 警告。
|
|
8
|
+
|
|
9
|
+
## 正例
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const ctrl = new AbortController();
|
|
14
|
+
fetch(`/api/user/${id}`, { signal: ctrl.signal })
|
|
15
|
+
.then((r) => r.json())
|
|
16
|
+
.then(setUser)
|
|
17
|
+
.catch((e) => { if (e.name !== 'AbortError') console.error(e); });
|
|
18
|
+
|
|
19
|
+
const timer = setInterval(tick, 1000);
|
|
20
|
+
window.addEventListener('resize', onResize);
|
|
21
|
+
|
|
22
|
+
return () => {
|
|
23
|
+
ctrl.abort();
|
|
24
|
+
clearInterval(timer);
|
|
25
|
+
window.removeEventListener('resize', onResize);
|
|
26
|
+
};
|
|
27
|
+
}, [id]);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 反例
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setInterval(tick, 1000); // ❌ 无 cleanup,组件卸载后仍在跑
|
|
35
|
+
window.addEventListener('resize', onResize); // ❌ 监听未移除
|
|
36
|
+
fetch(url).then(setData); // ❌ 卸载后仍 setState
|
|
37
|
+
}, []);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 如何检测
|
|
41
|
+
|
|
42
|
+
- 启用 `eslint-plugin-react-hooks` 的 `react-hooks/exhaustive-deps` 规则。
|
|
43
|
+
- grep 扫 `useEffect(` 的同块内出现 `setInterval|addEventListener|new WebSocket|subscribe(` 但无 `return (` 的回调。
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# React Hook 调用铁律
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
React Hook(`useState` / `useEffect` / `useMemo` / 自定义 `useXxx`)**只能在组件函数顶层同步调用**,禁止在条件分支、循环、`try/catch`、嵌套函数内调用,否则 Hook 调用顺序会错乱,React 内部以调用顺序匹配状态槽位,错乱即导致状态读错。
|
|
8
|
+
|
|
9
|
+
## 正例
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
function UserPanel({ id }: { id: string }) {
|
|
13
|
+
const [user, setUser] = useState<User | null>(null);
|
|
14
|
+
const theme = useTheme();
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
let cancelled = false;
|
|
18
|
+
fetchUser(id).then((u) => !cancelled && setUser(u));
|
|
19
|
+
return () => { cancelled = true; };
|
|
20
|
+
}, [id]);
|
|
21
|
+
|
|
22
|
+
if (!user) return <Spinner />;
|
|
23
|
+
return <Card theme={theme}>{user.name}</Card>;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 反例
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
function UserPanel({ id }: { id?: string }) {
|
|
31
|
+
if (!id) return null; // ❌ 提前 return 前后 Hook 数量不一致
|
|
32
|
+
const [user, setUser] = useState(null); // ❌ 条件之后才调用 Hook
|
|
33
|
+
for (const f of fields) {
|
|
34
|
+
const [v, setV] = useState(''); // ❌ 循环内 Hook
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 如何检测
|
|
40
|
+
|
|
41
|
+
- 启用 `eslint-plugin-react-hooks` 的 `react-hooks/rules-of-hooks` 规则(`error` 级)。
|
|
42
|
+
- CI 中禁用 `eslint-disable react-hooks/rules-of-hooks`(grep 扫禁词)。
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# 列表 key 必须稳定
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
`<List>.map` 渲染列表时,`key` **必须是数据本身的稳定唯一标识**(id / uuid / hash),禁止使用数组 `index` 作为 key(除非列表完全静态、永不插入/删除/重排)。index 作 key 会在增删中错位,导致组件状态串台、表单值丢失、动画错误。
|
|
8
|
+
|
|
9
|
+
## 正例
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
{users.map((u) => (
|
|
13
|
+
<UserCard key={u.id} user={u} />
|
|
14
|
+
))}
|
|
15
|
+
|
|
16
|
+
// 若数据天然无 id,在数据层生成稳定 id(crypto.randomUUID),不要在渲染时生成
|
|
17
|
+
const todos = rawTodos.map((t) => ({ ...t, _id: t.id ?? crypto.randomUUID() }));
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 反例
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
{users.map((u, i) => (
|
|
24
|
+
<UserCard key={i} user={u} /> // ❌ index 作 key
|
|
25
|
+
))}
|
|
26
|
+
|
|
27
|
+
{users.map((u) => (
|
|
28
|
+
<UserCard key={Math.random()} /> // ❌ 每次渲染都变
|
|
29
|
+
))}
|
|
30
|
+
|
|
31
|
+
{users.map((u) => (
|
|
32
|
+
<UserCard key={`${u.name}-${u.age}`} /> // ❌ 非唯一,可能重复
|
|
33
|
+
))}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 如何检测
|
|
37
|
+
|
|
38
|
+
- 启用 eslint 规则 `react/no-array-index-key`。
|
|
39
|
+
- grep 扫 `key={.*Math\.random|key=\{[a-z]+Index\}|key=\{i\}|key=\{index\}`。
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 禁止超 2 层 props drilling
|
|
2
|
+
|
|
3
|
+
> 种子规则:可保留、修改或删除。
|
|
4
|
+
|
|
5
|
+
## 是什么
|
|
6
|
+
|
|
7
|
+
同一个 prop 若需**经过 3 层及以上组件透传**(父→子→孙→曾孙),必须改用 `Context` 或状态管理库(Zustand / Redux / Jotai),否则中间层组件被迫声明自己不关心的字段,耦合度急剧上升,重构时一改多处。
|
|
8
|
+
|
|
9
|
+
阈值:**允许 2 层透传,3 层及以上必须改造**。
|
|
10
|
+
|
|
11
|
+
## 正例
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
const ThemeContext = createContext<Theme>('light');
|
|
15
|
+
|
|
16
|
+
function App() {
|
|
17
|
+
const [theme, setTheme] = useState<Theme>('dark');
|
|
18
|
+
return (
|
|
19
|
+
<ThemeContext.Provider value={theme}>
|
|
20
|
+
<Layout /> {/* Layout → Sidebar → MenuItem 都可直接 useContext */}
|
|
21
|
+
</ThemeContext.Provider>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function MenuItem() {
|
|
26
|
+
const theme = useContext(ThemeContext);
|
|
27
|
+
return <li className={theme}>...</li>;
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 反例
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<Layout theme={theme} />
|
|
35
|
+
<Sidebar theme={theme} /> {/* Layout 自己不用 theme,只是透传 */}
|
|
36
|
+
<Menu theme={theme} /> {/* Sidebar 也不用 */}
|
|
37
|
+
<MenuItem theme={theme} /> {/* 第 4 层才真正使用 ❌ */}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 如何检测
|
|
41
|
+
|
|
42
|
+
- code-review:若某个 prop 在连续 3 层组件中仅透传未使用,要求拆为 Context。
|
|
43
|
+
- 可选:eslint 自定义规则扫 `<Foo prop={prop} />` 模式(prop 同名透传)。
|