foliko 1.0.84 → 1.0.86

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 (170) hide show
  1. package/.agent/agents/code-assistant.json +14 -0
  2. package/.agent/agents/email-assistant.json +14 -0
  3. package/.agent/agents/file-assistant.json +15 -0
  4. package/.agent/agents/system-assistant.json +15 -0
  5. package/.agent/agents/web-assistant.json +12 -0
  6. package/.agent/data/ambient/goals.json +50 -0
  7. package/.agent/data/ambient/memories.json +7 -0
  8. package/.agent/data/default.json +4156 -244
  9. package/.agent/data/plugins-state.json +162 -174
  10. package/.agent/data/scheduler/tasks.json +1 -0
  11. package/.agent/data/weixin.json +6 -0
  12. package/.agent/mcp_config.json +1 -0
  13. package/.agent/package.json +8 -0
  14. package/.agent/plugins/__pycache__/test_plugin.cpython-312.pyc +0 -0
  15. package/.agent/plugins/daytona/README.md +89 -0
  16. package/.agent/plugins/daytona/index.js +377 -0
  17. package/.agent/plugins/daytona/package.json +12 -0
  18. package/.agent/plugins/marknative/README.md +134 -0
  19. package/.agent/plugins/marknative/index.js +233 -0
  20. package/.agent/plugins/marknative/package.json +12 -0
  21. package/.agent/plugins/marknative/update-readme.js +134 -0
  22. package/.agent/plugins/system-info/index.js +387 -0
  23. package/.agent/plugins/system-info/package.json +4 -0
  24. package/.agent/plugins/system-info/test.js +40 -0
  25. package/.agent/plugins/temp-repo/LICENSE +201 -0
  26. package/.agent/plugins/{puppeteer-plugin → temp-repo/puppeteer-plugin}/index.js +101 -3
  27. package/.agent/plugins/test_plugin.py +304 -0
  28. package/.agent/plugins.json +14 -5
  29. package/.agent/python-scripts/test_sample.py +24 -0
  30. package/.agent/skills/agent-browser/SKILL.md +311 -0
  31. package/.agent/skills/agent-browser/TEST_PLAN.md +200 -0
  32. package/.agent/skills/sysinfo/SKILL.md +38 -0
  33. package/.agent/skills/sysinfo/system-info.sh +130 -0
  34. package/.agent/skills/workflow/SKILL.md +324 -0
  35. package/.agent/workflows/email-digest.json +50 -0
  36. package/.agent/workflows/file-backup.json +21 -0
  37. package/.agent/workflows/get-ip-notify.json +32 -0
  38. package/.agent/workflows/news-aggregator.json +93 -0
  39. package/.agent/workflows/news-dashboard-v2.json +94 -0
  40. package/.agent/workflows/notification-batch.json +32 -0
  41. package/.claude/settings.local.json +171 -178
  42. package/.env.example +56 -56
  43. package/README.md +441 -441
  44. package/package.json +2 -2
  45. package/plugins/ambient-agent/EventWatcher.js +4 -4
  46. package/plugins/extension-executor-plugin.js +52 -1
  47. package/plugins/file-system-plugin.js +44 -5
  48. package/plugins/session-plugin.js +3 -3
  49. package/plugins/weixin-plugin.js +278 -29
  50. package/skills/find-skills/AGENTS.md +162 -162
  51. package/skills/find-skills/SKILL.md +133 -133
  52. package/skills/foliko-dev/SKILL.md +67 -0
  53. package/skills/python-plugin-dev/SKILL.md +238 -238
  54. package/src/core/agent-chat.js +11 -21
  55. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +0 -26
  56. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +0 -97
  57. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +0 -101
  58. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +0 -31
  59. package/.agent/.shared/ui-ux-pro-max/data/products.csv +0 -97
  60. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +0 -24
  61. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +0 -45
  62. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  63. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  64. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
  65. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  66. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  67. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  68. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  69. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +0 -54
  70. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
  71. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  72. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  73. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  74. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +0 -59
  75. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +0 -58
  76. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +0 -101
  77. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  78. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +0 -31
  79. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  80. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  81. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +0 -258
  82. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +0 -1067
  83. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +0 -106
  84. package/.agent/ARCHITECTURE.md +0 -288
  85. package/.agent/agents/ambient-agent.md +0 -57
  86. package/.agent/agents/debugger.md +0 -55
  87. package/.agent/agents/email-assistant.md +0 -49
  88. package/.agent/agents/file-manager.md +0 -42
  89. package/.agent/agents/python-developer.md +0 -60
  90. package/.agent/agents/scheduler.md +0 -59
  91. package/.agent/agents/web-developer.md +0 -45
  92. package/.agent/data/puppeteer-sessions/undefined.json +0 -6
  93. package/.agent/mcp_config_updated.json +0 -12
  94. package/.agent/rules/GEMINI.md +0 -273
  95. package/.agent/rules/allow-rule.md +0 -77
  96. package/.agent/rules/log-rule.md +0 -83
  97. package/.agent/rules/security-rule.md +0 -93
  98. package/.agent/scripts/auto_preview.py +0 -148
  99. package/.agent/scripts/checklist.py +0 -217
  100. package/.agent/scripts/session_manager.py +0 -120
  101. package/.agent/scripts/verify_all.py +0 -327
  102. package/.agent/skills/api-patterns/SKILL.md +0 -81
  103. package/.agent/skills/api-patterns/api-style.md +0 -42
  104. package/.agent/skills/api-patterns/auth.md +0 -24
  105. package/.agent/skills/api-patterns/documentation.md +0 -26
  106. package/.agent/skills/api-patterns/graphql.md +0 -41
  107. package/.agent/skills/api-patterns/rate-limiting.md +0 -31
  108. package/.agent/skills/api-patterns/response.md +0 -37
  109. package/.agent/skills/api-patterns/rest.md +0 -40
  110. package/.agent/skills/api-patterns/scripts/api_validator.py +0 -211
  111. package/.agent/skills/api-patterns/security-testing.md +0 -122
  112. package/.agent/skills/api-patterns/trpc.md +0 -41
  113. package/.agent/skills/api-patterns/versioning.md +0 -22
  114. package/.agent/skills/app-builder/SKILL.md +0 -75
  115. package/.agent/skills/app-builder/agent-coordination.md +0 -71
  116. package/.agent/skills/app-builder/feature-building.md +0 -53
  117. package/.agent/skills/app-builder/project-detection.md +0 -34
  118. package/.agent/skills/app-builder/scaffolding.md +0 -118
  119. package/.agent/skills/app-builder/tech-stack.md +0 -40
  120. package/.agent/skills/app-builder/templates/SKILL.md +0 -39
  121. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +0 -76
  122. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +0 -92
  123. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +0 -88
  124. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +0 -88
  125. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +0 -83
  126. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +0 -90
  127. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +0 -90
  128. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +0 -122
  129. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +0 -122
  130. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +0 -169
  131. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +0 -134
  132. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +0 -83
  133. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +0 -119
  134. package/.agent/skills/architecture/SKILL.md +0 -55
  135. package/.agent/skills/architecture/context-discovery.md +0 -43
  136. package/.agent/skills/architecture/examples.md +0 -94
  137. package/.agent/skills/architecture/pattern-selection.md +0 -68
  138. package/.agent/skills/architecture/patterns-reference.md +0 -50
  139. package/.agent/skills/architecture/trade-off-analysis.md +0 -77
  140. package/.agent/skills/clean-code/SKILL.md +0 -201
  141. package/.agent/skills/doc.md +0 -177
  142. package/.agent/skills/frontend-design/SKILL.md +0 -418
  143. package/.agent/skills/frontend-design/animation-guide.md +0 -331
  144. package/.agent/skills/frontend-design/color-system.md +0 -311
  145. package/.agent/skills/frontend-design/decision-trees.md +0 -418
  146. package/.agent/skills/frontend-design/motion-graphics.md +0 -306
  147. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +0 -183
  148. package/.agent/skills/frontend-design/scripts/ux_audit.py +0 -722
  149. package/.agent/skills/frontend-design/typography-system.md +0 -345
  150. package/.agent/skills/frontend-design/ux-psychology.md +0 -1116
  151. package/.agent/skills/frontend-design/visual-effects.md +0 -383
  152. package/.agent/skills/i18n-localization/SKILL.md +0 -154
  153. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +0 -241
  154. package/.agent/skills/mcp-builder/SKILL.md +0 -176
  155. package/.agent/skills/web-design-guidelines/SKILL.md +0 -57
  156. package/.agent/workflows/brainstorm.md +0 -113
  157. package/.agent/workflows/create.md +0 -59
  158. package/.agent/workflows/debug.md +0 -103
  159. package/.agent/workflows/deploy.md +0 -176
  160. package/.agent/workflows/enhance.md +0 -63
  161. package/.agent/workflows/orchestrate.md +0 -237
  162. package/.agent/workflows/plan.md +0 -89
  163. package/.agent/workflows/preview.md +0 -81
  164. package/.agent/workflows/simple-test.md +0 -42
  165. package/.agent/workflows/status.md +0 -86
  166. package/.agent/workflows/structured-orchestrate.md +0 -180
  167. package/.agent/workflows/test.md +0 -144
  168. package/.agent/workflows/ui-ux-pro-max.md +0 -296
  169. /package/.agent/plugins/{puppeteer-plugin → temp-repo/puppeteer-plugin}/README.md +0 -0
  170. /package/.agent/plugins/{puppeteer-plugin → temp-repo/puppeteer-plugin}/package.json +0 -0
@@ -318,6 +318,7 @@ module.exports = function (Plugin) {
318
318
  description: '获取页面HTML内容(注意:复杂页面会导致token超标,建议使用 page_structure 获取页面结构)',
319
319
  inputSchema: z.object({
320
320
  contentOnly: z.boolean().optional().describe('仅获取body内容'),
321
+ toMarkdown: z.boolean().optional().default(true).describe('是否将HTML转换为Markdown格式,默认开启'),
321
322
  pageId: z.string().optional().describe('页面ID,默认当前页面'),
322
323
  }),
323
324
  execute: async (args) => {
@@ -325,19 +326,116 @@ module.exports = function (Plugin) {
325
326
  ? this.pages.get(args.pageId)
326
327
  : this.getCurrentPage();
327
328
 
328
- const html = args.contentOnly
329
+ let html = args.contentOnly
329
330
  ? await page.evaluate(() => document.body ? document.body.innerHTML : '')
330
331
  : await page.content();
331
332
 
333
+ let content = html;
334
+ let isMarkdown = false;
335
+
336
+ // 如果需要转换为 Markdown(默认为 true)
337
+ const shouldConvertToMarkdown = args.toMarkdown !== false;
338
+ if (shouldConvertToMarkdown) {
339
+ content = this._htmlToMarkdown(html);
340
+ isMarkdown = true;
341
+ }
342
+
332
343
  return {
333
344
  success: true,
334
- html: html,
335
- length: html.length,
345
+ html: isMarkdown ? undefined : html,
346
+ markdown: isMarkdown ? content : undefined,
347
+ content: content,
348
+ length: content.length,
349
+ isMarkdown: isMarkdown,
336
350
  url: page.url()
337
351
  };
338
352
  }
339
353
  },
340
354
 
355
+ // HTML 转 Markdown 辅助方法
356
+ _htmlToMarkdown(html) {
357
+ if (!html) return '';
358
+
359
+ // 移除脚本和样式标签内容
360
+ let markdown = html
361
+ .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
362
+ .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
363
+ .replace(/<!--[\s\S]*?-->/g, '');
364
+
365
+ // 标题转换
366
+ markdown = markdown.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, '# $1\n\n');
367
+ markdown = markdown.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, '## $1\n\n');
368
+ markdown = markdown.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, '### $1\n\n');
369
+ markdown = markdown.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, '#### $1\n\n');
370
+ markdown = markdown.replace(/<h5[^>]*>([\s\S]*?)<\/h5>/gi, '##### $1\n\n');
371
+ markdown = markdown.replace(/<h6[^>]*>([\s\S]*?)<\/h6>/gi, '###### $1\n\n');
372
+
373
+ // 换行和段落
374
+ markdown = markdown.replace(/<br\s*\/?>/gi, '\n');
375
+ markdown = markdown.replace(/<\/p>/gi, '\n\n');
376
+ markdown = markdown.replace(/<\/div>/gi, '\n');
377
+ markdown = markdown.replace(/<\/li>/gi, '\n');
378
+
379
+ // 列表
380
+ markdown = markdown.replace(/<ul[^>]*>/gi, '\n');
381
+ markdown = markdown.replace(/<ol[^>]*>/gi, '\n');
382
+ markdown = markdown.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, '- $1\n');
383
+
384
+ // 链接和图片
385
+ markdown = markdown.replace(/<a[^>]*href=["']([^"']*)["'][^>]*>([\s\S]*?)<\/a>/gi, '[$2]($1)');
386
+ markdown = markdown.replace(/<img[^>]*src=["']([^"']*)["'][^>]*alt=["']([^"']*)["'][^>]*\/?>/gi, '![$2]($1)');
387
+ markdown = markdown.replace(/<img[^>]*alt=["']([^"']*)["'][^>]*src=["']([^"']*)["'][^>]*\/?>/gi, '![$1]($2)');
388
+ markdown = markdown.replace(/<img[^>]*src=["']([^"']*)["'][^>]*\/?>/gi, '![]($1)');
389
+
390
+ // 粗体和斜体
391
+ markdown = markdown.replace(/<strong[^>]*>([\s\S]*?)<\/strong>/gi, '**$1**');
392
+ markdown = markdown.replace(/<b[^>]*>([\s\S]*?)<\/b>/gi, '**$1**');
393
+ markdown = markdown.replace(/<em[^>]*>([\s\S]*?)<\/em>/gi, '*$1*');
394
+ markdown = markdown.replace(/<i[^>]*>([\s\S]*?)<\/i>/gi, '*$1*');
395
+
396
+ // 代码
397
+ markdown = markdown.replace(/<code[^>]*>([\s\S]*?)<\/code>/gi, '`$1`');
398
+ markdown = markdown.replace(/<pre[^>]*>([\s\S]*?)<\/pre>/gi, '```\n$1\n```');
399
+
400
+ // 表格 (简单支持)
401
+ const tableRegex = /<table[^>]*>([\s\S]*?)<\/table>/gi;
402
+ markdown = markdown.replace(tableRegex, (match, tableContent) => {
403
+ let mdTable = '';
404
+ const rows = tableContent.match(/<tr[^>]*>([\s\S]*?)<\/tr>/gi) || [];
405
+ rows.forEach((row, idx) => {
406
+ const cells = row.match(/<t[hd][^>]*>([\s\S]*?)<\/t[hd]>/gi) || [];
407
+ const mdRow = cells.map(cell => {
408
+ return cell.replace(/<t[hd][^>]*>/, '').replace(/<\/t[hd]>/, '').trim();
409
+ }).join(' | ');
410
+ if (idx === 0) {
411
+ mdTable += mdRow + '\n' + mdRow.split('|').map(() => '---').join('|') + '\n';
412
+ } else {
413
+ mdTable += mdRow + '\n';
414
+ }
415
+ });
416
+ return mdTable + '\n';
417
+ });
418
+
419
+ // 移除所有剩余 HTML 标签
420
+ markdown = markdown.replace(/<[^>]+>/g, '');
421
+
422
+ // 清理实体编码
423
+ markdown = markdown
424
+ .replace(/&nbsp;/g, ' ')
425
+ .replace(/&amp;/g, '&')
426
+ .replace(/&lt;/g, '<')
427
+ .replace(/&gt;/g, '>')
428
+ .replace(/&quot;/g, '"')
429
+ .replace(/&#39;/g, "'")
430
+ .replace(/&nbsp;/g, ' ')
431
+ .replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec));
432
+
433
+ // 清理多余空行
434
+ markdown = markdown.replace(/\n{3,}/g, '\n\n').trim();
435
+
436
+ return markdown;
437
+ },
438
+
341
439
  // 获取页面结构(优化版 - 返回精简的结构化数据,避免token超标)
342
440
  page_structure: {
343
441
  description: '获取页面关键结构信息(推荐使用),返回可交互元素的精简结构,避免获取完整HTML导致的token超标问题',
@@ -0,0 +1,304 @@
1
+ # .agent/plugins/test_plugin.py
2
+ """
3
+ Python 测试插件 - 提供单元测试和覆盖率测试功能
4
+ """
5
+
6
+ PLUGIN = {
7
+ "name": "test_plugin",
8
+ "version": "1.0.0",
9
+ "description": "Python 单元测试和覆盖率测试插件"
10
+ }
11
+
12
+ TOOLS = [
13
+ {
14
+ "name": "run_tests",
15
+ "description": "运行 Python 测试文件或目录中的测试用例",
16
+ "params": {
17
+ "path": "string",
18
+ "verbose": "boolean",
19
+ "pattern": "string"
20
+ }
21
+ },
22
+ {
23
+ "name": "create_test",
24
+ "description": "创建单元测试文件模板",
25
+ "params": {
26
+ "path": "string",
27
+ "class_name": "string",
28
+ "test_methods": "array",
29
+ "module_name": "string"
30
+ }
31
+ },
32
+ {
33
+ "name": "run_coverage",
34
+ "description": "运行测试并生成覆盖率报告",
35
+ "params": {
36
+ "path": "string",
37
+ "source": "string",
38
+ "report_type": "string"
39
+ }
40
+ },
41
+ {
42
+ "name": "assert_equal",
43
+ "description": "断言两个值相等(测试辅助)",
44
+ "params": {
45
+ "actual": "any",
46
+ "expected": "any",
47
+ "message": "string"
48
+ }
49
+ },
50
+ {
51
+ "name": "mock_function",
52
+ "description": "创建模拟函数用于测试",
53
+ "params": {
54
+ "return_value": "any",
55
+ "side_effect": "array",
56
+ "called_count": "number"
57
+ }
58
+ }
59
+ ]
60
+
61
+ # === 工具实现 ===
62
+
63
+ import os
64
+ import json
65
+ import subprocess
66
+ import traceback
67
+ from datetime import datetime
68
+
69
+
70
+ def run_tests(params):
71
+ """运行 Python 测试文件或目录中的测试用例"""
72
+ path = params.get("path", ".")
73
+ verbose = params.get("verbose", True)
74
+ pattern = params.get("pattern", "test_*.py")
75
+
76
+ if not os.path.exists(path):
77
+ return {"success": False, "error": f"Path not found: {path}"}
78
+
79
+ # 构建 pytest 命令
80
+ cmd = ["pytest", path]
81
+ if verbose:
82
+ cmd.append("-v")
83
+
84
+ try:
85
+ result = subprocess.run(
86
+ cmd,
87
+ capture_output=True,
88
+ text=True,
89
+ timeout=60
90
+ )
91
+
92
+ output = result.stdout + result.stderr
93
+
94
+ return {
95
+ "success": result.returncode == 0,
96
+ "result": {
97
+ "passed": result.returncode == 0,
98
+ "output": output,
99
+ "return_code": result.returncode
100
+ }
101
+ }
102
+ except subprocess.TimeoutExpired:
103
+ return {"success": False, "error": "Test execution timed out"}
104
+ except Exception as e:
105
+ return {"success": False, "error": str(e)}
106
+
107
+
108
+ def create_test(params):
109
+ """创建单元测试文件模板"""
110
+ path = params.get("path")
111
+ class_name = params.get("class_name", "TestModule")
112
+ test_methods = params.get("test_methods", ["test_case"])
113
+ module_name = params.get("module_name", "")
114
+
115
+ if not path:
116
+ return {"success": False, "error": "path is required"}
117
+
118
+ # 确保目录存在
119
+ os.makedirs(os.path.dirname(path) if os.path.dirname(path) else ".", exist_ok=True)
120
+
121
+ # 生成测试模板
122
+ template = f'''"""
123
+ 测试模块 - {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
124
+ """
125
+
126
+ import unittest
127
+ {"import " + module_name if module_name else ""}
128
+
129
+
130
+ class {class_name}(unittest.TestCase):
131
+ """{class_name} 测试类"""
132
+ '''
133
+
134
+ for method in test_methods:
135
+ template += f'''
136
+ def {method}(self):
137
+ """测试用例: {method}"""
138
+ # TODO: 实现测试逻辑
139
+ self.assertTrue(True)
140
+ '''
141
+
142
+ template += '''
143
+
144
+ if __name__ == "__main__":
145
+ unittest.main()
146
+ '''
147
+
148
+ try:
149
+ with open(path, "w", encoding="utf-8") as f:
150
+ f.write(template)
151
+
152
+ return {
153
+ "success": True,
154
+ "result": {
155
+ "path": path,
156
+ "class_name": class_name,
157
+ "methods": test_methods
158
+ }
159
+ }
160
+ except Exception as e:
161
+ return {"success": False, "error": str(e)}
162
+
163
+
164
+ def run_coverage(params):
165
+ """运行测试并生成覆盖率报告"""
166
+ path = params.get("path", ".")
167
+ source = params.get("source", "")
168
+ report_type = params.get("report_type", "term")
169
+
170
+ if not os.path.exists(path):
171
+ return {"success": False, "error": f"Path not found: {path}"}
172
+
173
+ # 构建 coverage 命令
174
+ cmd = ["coverage", "run", "-m", "pytest", path]
175
+ if source:
176
+ cmd = ["coverage", "run", "--source", source, "-m", "pytest", path]
177
+
178
+ try:
179
+ # 运行测试
180
+ subprocess.run(cmd, capture_output=True, timeout=60)
181
+
182
+ # 生成报告
183
+ if report_type == "term":
184
+ report_result = subprocess.run(
185
+ ["coverage", "report"],
186
+ capture_output=True,
187
+ text=True
188
+ )
189
+ output = report_result.stdout
190
+ elif report_type == "html":
191
+ subprocess.run(
192
+ ["coverage", "html", "-d", "htmlcov"],
193
+ capture_output=True,
194
+ timeout=30
195
+ )
196
+ output = "Coverage HTML report generated at htmlcov/"
197
+ elif report_type == "json":
198
+ report_result = subprocess.run(
199
+ ["coverage", "json"],
200
+ capture_output=True,
201
+ text=True
202
+ )
203
+ output = report_result.stdout
204
+ else:
205
+ output = "Unknown report type"
206
+
207
+ return {
208
+ "success": True,
209
+ "result": {
210
+ "report": output,
211
+ "type": report_type
212
+ }
213
+ }
214
+ except subprocess.TimeoutExpired:
215
+ return {"success": False, "error": "Coverage execution timed out"}
216
+ except FileNotFoundError:
217
+ return {"success": False, "error": "coverage module not installed. Run: pip install coverage"}
218
+ except Exception as e:
219
+ return {"success": False, "error": str(e)}
220
+
221
+
222
+ def assert_equal(params):
223
+ """断言两个值相等(测试辅助)"""
224
+ actual = params.get("actual")
225
+ expected = params.get("expected")
226
+ message = params.get("message", "")
227
+
228
+ try:
229
+ # 处理 JSON 字符串比较
230
+ if isinstance(actual, str):
231
+ try:
232
+ actual = json.loads(actual)
233
+ except (json.JSONDecodeError, TypeError):
234
+ pass
235
+
236
+ if isinstance(expected, str):
237
+ try:
238
+ expected = json.loads(expected)
239
+ except (json.JSONDecodeError, TypeError):
240
+ pass
241
+
242
+ if actual == expected:
243
+ return {
244
+ "success": True,
245
+ "result": {
246
+ "passed": True,
247
+ "message": f"Assertion passed: {actual} == {expected}",
248
+ "actual": actual,
249
+ "expected": expected
250
+ }
251
+ }
252
+ else:
253
+ return {
254
+ "success": True,
255
+ "result": {
256
+ "passed": False,
257
+ "message": f"Assertion failed: {actual} != {expected}",
258
+ "actual": actual,
259
+ "expected": expected
260
+ }
261
+ }
262
+ except Exception as e:
263
+ return {"success": False, "error": str(e)}
264
+
265
+
266
+ def mock_function(params):
267
+ """创建模拟函数用于测试"""
268
+ return_value = params.get("return_value")
269
+ side_effect = params.get("side_effect", [])
270
+ called_count = params.get("called_count", 0)
271
+
272
+ class MockResult:
273
+ def __init__(self):
274
+ self.call_count = 0
275
+ self.call_args = []
276
+ self.side_effects = side_effect
277
+
278
+ def __call__(self, *args, **kwargs):
279
+ self.call_count += 1
280
+ self.call_args.append({"args": args, "kwargs": kwargs})
281
+
282
+ # 如果有 side_effect,按顺序返回
283
+ if self.side_effects:
284
+ index = min(self.call_count - 1, len(self.side_effects) - 1)
285
+ return self.side_effects[index]
286
+
287
+ return return_value
288
+
289
+ def reset(self):
290
+ self.call_count = 0
291
+ self.call_args = []
292
+
293
+ mock = MockResult()
294
+ mock.call_count = called_count
295
+
296
+ return {
297
+ "success": True,
298
+ "result": {
299
+ "mock_type": "MockFunction",
300
+ "return_value": return_value,
301
+ "side_effects": side_effect,
302
+ "description": "Use this mock function in your tests"
303
+ }
304
+ }
@@ -1,5 +1,14 @@
1
- {
2
- "weixin": {
3
- "enabled": true
4
- }
5
- }
1
+ {
2
+ "email": {
3
+ "enabled": true,
4
+ "smtp": {
5
+ "host": "smtp.gmail.com",
6
+ "port": 587,
7
+ "secure": false
8
+ },
9
+ "imap": {
10
+ "host": "imap.gmail.com",
11
+ "port": 993
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,24 @@
1
+ """
2
+ 测试模块 - 2026-04-02 02:52:12
3
+ """
4
+
5
+ import unittest
6
+
7
+
8
+
9
+ class TestMath(unittest.TestCase):
10
+ """TestMath 测试类"""
11
+
12
+ def test_add(self):
13
+ """测试用例: test_add"""
14
+ # TODO: 实现测试逻辑
15
+ self.assertTrue(True)
16
+
17
+ def test_subtract(self):
18
+ """测试用例: test_subtract"""
19
+ # TODO: 实现测试逻辑
20
+ self.assertTrue(True)
21
+
22
+
23
+ if __name__ == "__main__":
24
+ unittest.main()