ccg-ros2-workflow 2.2.1 → 3.0.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.
Files changed (186) hide show
  1. package/README.md +211 -96
  2. package/README.zh-CN.md +256 -0
  3. package/dist/cli.mjs +15 -15
  4. package/dist/index.d.mts +61 -34
  5. package/dist/index.d.ts +61 -34
  6. package/dist/index.mjs +4 -4
  7. package/dist/shared/ccg-ros2-workflow.Bhm8c7P1.mjs +5154 -0
  8. package/package.json +31 -12
  9. package/templates/codex/AGENTS.md +348 -0
  10. package/templates/codex/agents/ccg-implement.toml +73 -0
  11. package/templates/codex/agents/ccg-research.toml +73 -0
  12. package/templates/codex/agents/ccg-review.toml +82 -0
  13. package/templates/codex/config.toml +21 -0
  14. package/templates/codex/hooks/ccg-workflow.py +253 -0
  15. package/templates/codex/hooks.json +15 -0
  16. package/templates/commands/agents/planner.md +97 -122
  17. package/templates/commands/agents/system-integrator.md +2 -2
  18. package/templates/commands/agents/team-architect.md +97 -0
  19. package/templates/commands/agents/team-qa.md +121 -0
  20. package/templates/commands/agents/team-reviewer.md +112 -0
  21. package/templates/commands/commit.md +30 -1
  22. package/templates/commands/context.md +332 -0
  23. package/templates/commands/go.md +206 -0
  24. package/templates/commands/init.md +1 -1
  25. package/templates/commands/spec-impl.md +41 -21
  26. package/templates/commands/spec-init.md +21 -27
  27. package/templates/commands/spec-plan.md +54 -21
  28. package/templates/commands/spec-research.md +78 -26
  29. package/templates/commands/spec-review.md +20 -16
  30. package/templates/{commands → commands-legacy}/analyze.md +1 -1
  31. package/templates/commands-legacy/backend.md +224 -0
  32. package/templates/commands-legacy/codex-exec.md +411 -0
  33. package/templates/{commands → commands-legacy}/debug.md +1 -1
  34. package/templates/commands-legacy/enhance.md +55 -0
  35. package/templates/{commands → commands-legacy}/feat.md +2 -2
  36. package/templates/commands-legacy/frontend.md +213 -0
  37. package/templates/{commands → commands-legacy}/optimize.md +1 -1
  38. package/templates/{commands → commands-legacy}/plan.md +1 -15
  39. package/templates/{commands → commands-legacy}/team-plan.md +1 -1
  40. package/templates/commands-legacy/team.md +475 -0
  41. package/templates/{commands → commands-legacy}/test.md +1 -1
  42. package/templates/commands-legacy/workflow.md +283 -0
  43. package/templates/engine/model-router.md +123 -0
  44. package/templates/engine/phase-guide.md +207 -0
  45. package/templates/engine/strategies/debug-investigate.md +169 -0
  46. package/templates/engine/strategies/deep-research.md +141 -0
  47. package/templates/engine/strategies/direct-fix.md +108 -0
  48. package/templates/engine/strategies/full-collaborate.md +389 -0
  49. package/templates/engine/strategies/git-action.md +43 -0
  50. package/templates/engine/strategies/guided-develop.md +282 -0
  51. package/templates/engine/strategies/optimize-measure.md +103 -0
  52. package/templates/engine/strategies/quick-implement.md +96 -0
  53. package/templates/engine/strategies/refactor-safely.md +180 -0
  54. package/templates/engine/strategies/review-audit.md +123 -0
  55. package/templates/hooks/session-start.js +100 -0
  56. package/templates/hooks/skill-router.js +144 -0
  57. package/templates/hooks/subagent-context.js +161 -0
  58. package/templates/hooks/task-utils.js +190 -0
  59. package/templates/hooks/workflow-state.js +55 -0
  60. package/templates/output-styles/abyss-command.md +56 -0
  61. package/templates/output-styles/abyss-concise.md +89 -0
  62. package/templates/output-styles/abyss-ritual.md +70 -0
  63. package/templates/output-styles/engineer-professional.md +20 -3
  64. package/templates/output-styles/laowang-engineer.md +2 -2
  65. package/templates/prompts/antigravity/analyzer.md +59 -0
  66. package/templates/prompts/antigravity/architect.md +55 -0
  67. package/templates/prompts/antigravity/builder.md +52 -0
  68. package/templates/prompts/antigravity/debugger.md +48 -0
  69. package/templates/prompts/antigravity/frontend.md +50 -0
  70. package/templates/prompts/antigravity/optimizer.md +40 -0
  71. package/templates/prompts/antigravity/reviewer.md +67 -0
  72. package/templates/prompts/antigravity/tester.md +39 -0
  73. package/templates/prompts/claude/debugger.md +1 -1
  74. package/templates/prompts/claude/reviewer.md +1 -1
  75. package/templates/prompts/codex/analyzer.md +8 -0
  76. package/templates/prompts/codex/architect.md +9 -1
  77. package/templates/prompts/codex/builder.md +61 -0
  78. package/templates/prompts/codex/debugger.md +9 -1
  79. package/templates/prompts/codex/optimizer.md +7 -0
  80. package/templates/prompts/codex/reviewer.md +7 -0
  81. package/templates/prompts/codex/tester.md +8 -1
  82. package/templates/prompts/gemini/analyzer.md +11 -3
  83. package/templates/prompts/gemini/architect.md +10 -2
  84. package/templates/prompts/gemini/debugger.md +8 -0
  85. package/templates/prompts/gemini/frontend.md +10 -2
  86. package/templates/prompts/gemini/optimizer.md +9 -2
  87. package/templates/prompts/gemini/reviewer.md +7 -0
  88. package/templates/prompts/gemini/tester.md +8 -1
  89. package/templates/rules/ccg-skill-routing.md +91 -0
  90. package/templates/rules/ccg-skills.md +65 -0
  91. package/templates/skills/SKILL.md +92 -0
  92. package/templates/skills/domains/ai/SKILL.md +34 -0
  93. package/templates/skills/domains/ai/agent-dev.md +242 -0
  94. package/templates/skills/domains/ai/llm-security.md +288 -0
  95. package/templates/skills/domains/ai/prompt-and-eval.md +279 -0
  96. package/templates/skills/domains/ai/rag-system.md +542 -0
  97. package/templates/skills/domains/architecture/SKILL.md +42 -0
  98. package/templates/skills/domains/architecture/api-design.md +225 -0
  99. package/templates/skills/domains/architecture/caching.md +299 -0
  100. package/templates/skills/domains/architecture/cloud-native.md +285 -0
  101. package/templates/skills/domains/architecture/message-queue.md +329 -0
  102. package/templates/skills/domains/architecture/security-arch.md +297 -0
  103. package/templates/skills/domains/data-engineering/SKILL.md +207 -0
  104. package/templates/skills/domains/development/SKILL.md +46 -0
  105. package/templates/skills/domains/development/cpp.md +369 -0
  106. package/templates/skills/domains/development/go.md +323 -0
  107. package/templates/skills/domains/development/java.md +277 -0
  108. package/templates/skills/domains/development/python.md +487 -0
  109. package/templates/skills/domains/development/rust.md +313 -0
  110. package/templates/skills/domains/development/shell.md +313 -0
  111. package/templates/skills/domains/development/typescript.md +277 -0
  112. package/templates/skills/domains/devops/SKILL.md +39 -0
  113. package/templates/skills/domains/devops/cost-optimization.md +272 -0
  114. package/templates/skills/domains/devops/database.md +217 -0
  115. package/templates/skills/domains/devops/devsecops.md +198 -0
  116. package/templates/skills/domains/devops/git-workflow.md +181 -0
  117. package/templates/skills/domains/devops/observability.md +280 -0
  118. package/templates/skills/domains/devops/performance.md +336 -0
  119. package/templates/skills/domains/devops/testing.md +283 -0
  120. package/templates/skills/domains/infrastructure/SKILL.md +200 -0
  121. package/templates/skills/domains/mobile/SKILL.md +224 -0
  122. package/templates/skills/domains/orchestration/SKILL.md +29 -0
  123. package/templates/skills/domains/orchestration/multi-agent.md +263 -0
  124. package/templates/skills/domains/ros2-control/SKILL.md +206 -0
  125. package/templates/skills/domains/ros2-hardware/SKILL.md +277 -0
  126. package/templates/skills/domains/ros2-manipulation/SKILL.md +237 -0
  127. package/templates/skills/domains/ros2-navigation/SKILL.md +196 -0
  128. package/templates/skills/domains/ros2-perception/SKILL.md +166 -0
  129. package/templates/skills/domains/ros2-upper-app/SKILL.md +50 -0
  130. package/templates/skills/domains/ros2-upper-app/launch-files.md +224 -0
  131. package/templates/skills/domains/ros2-upper-app/parameters.md +192 -0
  132. package/templates/skills/domains/ros2-upper-app/python-nodes.md +249 -0
  133. package/templates/skills/domains/ros2-upper-app/rviz-config.md +158 -0
  134. package/templates/skills/domains/ros2-upper-app/simulation.md +225 -0
  135. package/templates/skills/domains/security/SKILL.md +72 -0
  136. package/templates/skills/domains/security/blue-team.md +436 -0
  137. package/templates/skills/domains/security/code-audit.md +265 -0
  138. package/templates/skills/domains/security/pentest.md +226 -0
  139. package/templates/skills/domains/security/red-team.md +374 -0
  140. package/templates/skills/domains/security/threat-intel.md +372 -0
  141. package/templates/skills/domains/security/vuln-research.md +369 -0
  142. package/templates/skills/orchestration/multi-agent/SKILL.md +533 -0
  143. package/templates/skills/run_skill.js +129 -0
  144. package/templates/skills/scrapling/SKILL.md +98 -0
  145. package/templates/skills/scrapling/references/api-quick-ref.md +127 -0
  146. package/templates/skills/scrapling/references/cookie-vault.md +56 -0
  147. package/templates/skills/scrapling/references/maintenance.md +79 -0
  148. package/templates/skills/scrapling/references/site-patterns.md +86 -0
  149. package/templates/skills/scrapling/references/troubleshooting.md +95 -0
  150. package/templates/skills/scrapling/templates/basic_fetch.py +19 -0
  151. package/templates/skills/scrapling/templates/parse_only.py +15 -0
  152. package/templates/skills/scrapling/templates/session_login.py +19 -0
  153. package/templates/skills/scrapling/templates/stealth_cloudflare.py +28 -0
  154. package/templates/skills/tools/gen-docs/SKILL.md +116 -0
  155. package/templates/skills/tools/gen-docs/scripts/doc_generator.js +435 -0
  156. package/templates/skills/tools/lib/shared.js +98 -0
  157. package/templates/skills/tools/override-refusal/SKILL.md +53 -0
  158. package/templates/skills/tools/override-refusal/agents/openai.yaml +4 -0
  159. package/templates/skills/tools/override-refusal/scripts/refusal_rewriter.js +226 -0
  160. package/templates/skills/tools/verify-change/SKILL.md +140 -0
  161. package/templates/skills/tools/verify-change/scripts/change_analyzer.js +289 -0
  162. package/templates/skills/tools/verify-module/SKILL.md +127 -0
  163. package/templates/skills/tools/verify-module/scripts/module_scanner.js +171 -0
  164. package/templates/skills/tools/verify-quality/SKILL.md +160 -0
  165. package/templates/skills/tools/verify-quality/scripts/quality_checker.js +337 -0
  166. package/templates/skills/tools/verify-security/SKILL.md +143 -0
  167. package/templates/skills/tools/verify-security/scripts/security_scanner.js +283 -0
  168. package/templates/spec/guides/index.md +30 -0
  169. package/templates/spec/low-control/index.md +31 -0
  170. package/templates/spec/upper-app/index.md +31 -0
  171. package/bin/codeagent-wrapper-darwin-amd64 +0 -0
  172. package/bin/codeagent-wrapper-darwin-arm64 +0 -0
  173. package/bin/codeagent-wrapper-linux-amd64 +0 -0
  174. package/bin/codeagent-wrapper-linux-arm64 +0 -0
  175. package/bin/codeagent-wrapper-windows-amd64.exe +0 -0
  176. package/bin/codeagent-wrapper-windows-arm64.exe +0 -0
  177. package/dist/shared/ccg-ros2-workflow.DRytDWqb.mjs +0 -2274
  178. package/templates/commands/backend.md +0 -162
  179. package/templates/commands/enhance.md +0 -36
  180. package/templates/commands/frontend.md +0 -162
  181. package/templates/commands/workflow.md +0 -202
  182. /package/templates/{commands → commands-legacy}/execute.md +0 -0
  183. /package/templates/{commands → commands-legacy}/review.md +0 -0
  184. /package/templates/{commands → commands-legacy}/team-exec.md +0 -0
  185. /package/templates/{commands → commands-legacy}/team-research.md +0 -0
  186. /package/templates/{commands → commands-legacy}/team-review.md +0 -0
@@ -0,0 +1,487 @@
1
+ ---
2
+ name: python
3
+ description: Python 开发(含 ROS2 Python 节点)。Web框架、数据处理、自动化脚本、ROS2 Python 节点、Launch 文件。当用户提到 Python、Django、Flask、FastAPI、pytest、pandas、rclpy、launch 时使用。
4
+ ---
5
+
6
+ # Python 开发(ROS2 增强版)
7
+
8
+ ## ROS2 Python 开发上下文(上层应用核心)
9
+
10
+ ROS2 项目中,Python 节点承担**上层应用**职责:Launch 文件、参数配置、可视化、仿真、状态机编排。
11
+
12
+ ### rclpy 核心模式
13
+
14
+ ```python
15
+ import rclpy
16
+ from rclpy.node import Node
17
+ from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy
18
+ from sensor_msgs.msg import LaserScan
19
+ from geometry_msgs.msg import Twist
20
+
21
+
22
+ class NavigationNode(Node):
23
+ def __init__(self):
24
+ super().__init__('navigation_node')
25
+
26
+ # QoS 策略
27
+ sensor_qos = QoSProfile(
28
+ reliability=ReliabilityPolicy.BEST_EFFORT,
29
+ history=HistoryPolicy.KEEP_LAST,
30
+ depth=10
31
+ )
32
+
33
+ # 订阅传感器
34
+ self.scan_sub = self.create_subscription(
35
+ LaserScan, '/scan', self.scan_callback, sensor_qos)
36
+
37
+ # 发布控制指令
38
+ self.cmd_pub = self.create_publisher(Twist, '/cmd_vel', 10)
39
+
40
+ # 参数声明
41
+ self.declare_parameter('max_speed', 1.0)
42
+ self.declare_parameter('safe_distance', 0.5)
43
+
44
+ def scan_callback(self, msg: LaserScan):
45
+ max_speed = self.get_parameter('max_speed').value
46
+ # 处理逻辑
47
+ cmd = Twist()
48
+ cmd.linear.x = max_speed
49
+ self.cmd_pub.publish(cmd)
50
+
51
+
52
+ def main():
53
+ rclpy.init()
54
+ node = NavigationNode()
55
+ try:
56
+ rclpy.spin(node)
57
+ finally:
58
+ node.destroy_node()
59
+ rclpy.shutdown()
60
+
61
+
62
+ if __name__ == '__main__':
63
+ main()
64
+ ```
65
+
66
+ ### Launch 文件标准模板
67
+
68
+ ```python
69
+ # launch/navigation.launch.py
70
+ from launch import LaunchDescription
71
+ from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
72
+ from launch.launch_description_sources import PythonLaunchDescriptionSource
73
+ from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
74
+ from launch_ros.actions import Node
75
+ from launch_ros.substitutions import FindPackageShare
76
+
77
+
78
+ def generate_launch_description():
79
+ use_sim_time = LaunchConfiguration('use_sim_time', default='false')
80
+ params_file = PathJoinSubstitution([
81
+ FindPackageShare('my_robot'), 'config', 'params.yaml'
82
+ ])
83
+
84
+ return LaunchDescription([
85
+ DeclareLaunchArgument(
86
+ 'use_sim_time',
87
+ default_value='false',
88
+ description='Use simulation clock'),
89
+
90
+ Node(
91
+ package='my_robot',
92
+ executable='navigation_node',
93
+ name='navigation',
94
+ parameters=[params_file, {'use_sim_time': use_sim_time}],
95
+ remappings=[('/scan', '/lidar/scan')],
96
+ output='screen',
97
+ ),
98
+
99
+ IncludeLaunchDescription(
100
+ PythonLaunchDescriptionSource([
101
+ FindPackageShare('nav2_bringup'),
102
+ '/launch/navigation_launch.py'
103
+ ])
104
+ ),
105
+ ])
106
+ ```
107
+
108
+ ### setup.py 标准模板
109
+
110
+ ```python
111
+ from setuptools import setup
112
+ import os
113
+ from glob import glob
114
+
115
+ package_name = 'my_robot'
116
+
117
+ setup(
118
+ name=package_name,
119
+ version='0.1.0',
120
+ packages=[package_name],
121
+ data_files=[
122
+ ('share/ament_index/resource_index/packages',
123
+ ['resource/' + package_name]),
124
+ ('share/' + package_name, ['package.xml']),
125
+ (os.path.join('share', package_name, 'launch'),
126
+ glob('launch/*.launch.py')),
127
+ (os.path.join('share', package_name, 'config'),
128
+ glob('config/*.yaml')),
129
+ (os.path.join('share', package_name, 'rviz'),
130
+ glob('rviz/*.rviz')),
131
+ ],
132
+ install_requires=['setuptools'],
133
+ zip_safe=True,
134
+ entry_points={
135
+ 'console_scripts': [
136
+ 'navigation_node = my_robot.navigation_node:main',
137
+ ],
138
+ },
139
+ )
140
+ ```
141
+
142
+ ### 参数 YAML 配置
143
+
144
+ ```yaml
145
+ # config/params.yaml
146
+ navigation:
147
+ ros__parameters:
148
+ max_speed: 1.0
149
+ safe_distance: 0.5
150
+ update_rate: 50.0
151
+ frame_id: "base_link"
152
+ use_sim_time: false
153
+ ```
154
+
155
+ ### ROS2 Python 测试 (launch_testing)
156
+
157
+ ```python
158
+ import unittest
159
+ import launch_testing
160
+ import pytest
161
+ from launch import LaunchDescription
162
+ from launch_ros.actions import Node
163
+
164
+
165
+ @pytest.mark.launch_test
166
+ def generate_test_description():
167
+ return LaunchDescription([
168
+ Node(
169
+ package='my_robot',
170
+ executable='navigation_node',
171
+ ),
172
+ launch_testing.actions.ReadyToTest(),
173
+ ])
174
+
175
+
176
+ class TestNavigation(unittest.TestCase):
177
+ def test_node_starts(self, proc_info):
178
+ proc_info.assertWaitForStartup(timeout=10)
179
+ ```
180
+
181
+ ### 仿真集成(Gazebo)
182
+
183
+ ```python
184
+ # launch/simulation.launch.py
185
+ def generate_launch_description():
186
+ return LaunchDescription([
187
+ IncludeLaunchDescription(
188
+ PythonLaunchDescriptionSource([
189
+ FindPackageShare('gazebo_ros'),
190
+ '/launch/gazebo.launch.py'
191
+ ])
192
+ ),
193
+ Node(
194
+ package='robot_state_publisher',
195
+ executable='robot_state_publisher',
196
+ parameters=[{'robot_description': robot_description}],
197
+ ),
198
+ ])
199
+ ```
200
+
201
+ ---
202
+
203
+ ## 通用 Python 知识(以下保持原样)
204
+
205
+ # 📜 符箓秘典 · Python
206
+
207
+
208
+ ## Web 框架
209
+
210
+ ### FastAPI (推荐)
211
+ ```python
212
+ from fastapi import FastAPI, HTTPException, Depends
213
+ from pydantic import BaseModel
214
+ from typing import Optional
215
+
216
+ app = FastAPI()
217
+
218
+ class User(BaseModel):
219
+ name: str
220
+ email: str
221
+ age: Optional[int] = None
222
+
223
+ @app.get("/users/{user_id}")
224
+ async def get_user(user_id: int):
225
+ return {"user_id": user_id}
226
+
227
+ @app.post("/users")
228
+ async def create_user(user: User):
229
+ return user
230
+
231
+ # 依赖注入
232
+ async def get_db():
233
+ db = Database()
234
+ try:
235
+ yield db
236
+ finally:
237
+ await db.close()
238
+
239
+ @app.get("/items")
240
+ async def get_items(db = Depends(get_db)):
241
+ return await db.fetch_all("SELECT * FROM items")
242
+ ```
243
+
244
+ ### Flask
245
+ ```python
246
+ from flask import Flask, request, jsonify
247
+
248
+ app = Flask(__name__)
249
+
250
+ @app.route('/api/users', methods=['GET', 'POST'])
251
+ def users():
252
+ if request.method == 'POST':
253
+ data = request.json
254
+ return jsonify(data), 201
255
+ return jsonify([])
256
+
257
+ @app.errorhandler(404)
258
+ def not_found(e):
259
+ return jsonify(error="Not found"), 404
260
+ ```
261
+
262
+ ### Django
263
+ ```python
264
+ # models.py
265
+ from django.db import models
266
+
267
+ class User(models.Model):
268
+ name = models.CharField(max_length=100)
269
+ email = models.EmailField(unique=True)
270
+ created_at = models.DateTimeField(auto_now_add=True)
271
+
272
+ # views.py
273
+ from django.http import JsonResponse
274
+ from django.views import View
275
+
276
+ class UserView(View):
277
+ def get(self, request, user_id):
278
+ user = User.objects.get(id=user_id)
279
+ return JsonResponse({'name': user.name})
280
+
281
+ # urls.py
282
+ urlpatterns = [
283
+ path('users/<int:user_id>/', UserView.as_view()),
284
+ ]
285
+ ```
286
+
287
+ ## 异步编程
288
+
289
+ ```python
290
+ import asyncio
291
+ import aiohttp
292
+
293
+ async def fetch(url: str) -> str:
294
+ async with aiohttp.ClientSession() as session:
295
+ async with session.get(url) as response:
296
+ return await response.text()
297
+
298
+ async def fetch_all(urls: list[str]) -> list[str]:
299
+ tasks = [fetch(url) for url in urls]
300
+ return await asyncio.gather(*tasks)
301
+
302
+ # 运行
303
+ asyncio.run(fetch_all(['http://example.com', 'http://example.org']))
304
+ ```
305
+
306
+ ## 数据处理
307
+
308
+ ### Pandas
309
+ ```python
310
+ import pandas as pd
311
+
312
+ # 读取数据
313
+ df = pd.read_csv('data.csv')
314
+ df = pd.read_json('data.json')
315
+
316
+ # 数据清洗
317
+ df = df.dropna()
318
+ df = df.drop_duplicates()
319
+ df['column'] = df['column'].str.strip()
320
+
321
+ # 数据转换
322
+ df['date'] = pd.to_datetime(df['date'])
323
+ df['category'] = df['category'].astype('category')
324
+
325
+ # 聚合分析
326
+ result = df.groupby('category').agg({
327
+ 'value': ['sum', 'mean', 'count']
328
+ })
329
+
330
+ # 导出
331
+ df.to_csv('output.csv', index=False)
332
+ df.to_json('output.json', orient='records')
333
+ ```
334
+
335
+ ## 测试
336
+
337
+ ### pytest
338
+ ```python
339
+ import pytest
340
+ from myapp import calculate, UserService
341
+
342
+ # 基础测试
343
+ def test_add():
344
+ assert calculate.add(1, 2) == 3
345
+
346
+ # 参数化
347
+ @pytest.mark.parametrize("a,b,expected", [
348
+ (1, 2, 3),
349
+ (0, 0, 0),
350
+ (-1, 1, 0),
351
+ ])
352
+ def test_add_params(a, b, expected):
353
+ assert calculate.add(a, b) == expected
354
+
355
+ # Fixture
356
+ @pytest.fixture
357
+ def user_service():
358
+ service = UserService()
359
+ yield service
360
+ service.cleanup()
361
+
362
+ def test_create_user(user_service):
363
+ user = user_service.create("test")
364
+ assert user.name == "test"
365
+
366
+ # Mock
367
+ from unittest.mock import Mock, patch
368
+
369
+ @patch('myapp.requests.get')
370
+ def test_fetch(mock_get):
371
+ mock_get.return_value.json.return_value = {"id": 1}
372
+ result = fetch_user(1)
373
+ assert result["id"] == 1
374
+
375
+ # 异步测试
376
+ @pytest.mark.asyncio
377
+ async def test_async_fetch():
378
+ result = await async_fetch()
379
+ assert result is not None
380
+ ```
381
+
382
+ ### 运行测试
383
+ ```bash
384
+ pytest # 运行所有
385
+ pytest test_file.py # 指定文件
386
+ pytest -k "test_add" # 匹配名称
387
+ pytest -v # 详细输出
388
+ pytest --cov=myapp # 覆盖率
389
+ pytest -x # 失败即停
390
+ ```
391
+
392
+ ## CLI 工具
393
+
394
+ ### Typer (推荐)
395
+ ```python
396
+ import typer
397
+
398
+ app = typer.Typer()
399
+
400
+ @app.command()
401
+ def hello(name: str, count: int = 1):
402
+ """Say hello NAME, COUNT times."""
403
+ for _ in range(count):
404
+ typer.echo(f"Hello {name}!")
405
+
406
+ @app.command()
407
+ def goodbye(name: str, formal: bool = False):
408
+ if formal:
409
+ typer.echo(f"Goodbye Ms. {name}. Have a good day.")
410
+ else:
411
+ typer.echo(f"Bye {name}!")
412
+
413
+ if __name__ == "__main__":
414
+ app()
415
+ ```
416
+
417
+ ### argparse
418
+ ```python
419
+ import argparse
420
+
421
+ parser = argparse.ArgumentParser(description='My CLI tool')
422
+ parser.add_argument('input', help='Input file')
423
+ parser.add_argument('-o', '--output', default='output.txt')
424
+ parser.add_argument('-v', '--verbose', action='store_true')
425
+
426
+ args = parser.parse_args()
427
+ ```
428
+
429
+ ## 项目结构
430
+
431
+ ```
432
+ myproject/
433
+ ├── pyproject.toml # 项目配置
434
+ ├── README.md
435
+ ├── src/
436
+ │ └── myproject/
437
+ │ ├── __init__.py
438
+ │ ├── main.py
439
+ │ ├── models.py
440
+ │ └── utils.py
441
+ ├── tests/
442
+ │ ├── __init__.py
443
+ │ ├── conftest.py
444
+ │ └── test_main.py
445
+ └── scripts/
446
+ └── run.py
447
+ ```
448
+
449
+ ### pyproject.toml
450
+ ```toml
451
+ [project]
452
+ name = "myproject"
453
+ version = "0.1.0"
454
+ dependencies = [
455
+ "fastapi>=0.100.0",
456
+ "uvicorn>=0.23.0",
457
+ ]
458
+
459
+ [project.optional-dependencies]
460
+ dev = [
461
+ "pytest>=7.0.0",
462
+ "pytest-cov>=4.0.0",
463
+ ]
464
+
465
+ [tool.pytest.ini_options]
466
+ testpaths = ["tests"]
467
+
468
+ [tool.ruff]
469
+ line-length = 120
470
+ select = ["E", "F", "I"]
471
+ ```
472
+
473
+ ## 常用库
474
+
475
+ | 库 | 用途 |
476
+ |---|------|
477
+ | requests/httpx | HTTP 客户端 |
478
+ | aiohttp | 异步 HTTP |
479
+ | SQLAlchemy | ORM |
480
+ | Pydantic | 数据验证 |
481
+ | Click/Typer | CLI |
482
+ | pytest | 测试 |
483
+ | pandas | 数据处理 |
484
+ | loguru | 日志 |
485
+
486
+ ---
487
+