@ghenya/clinn 0.8.5 → 0.8.7
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/Skills/code_review.cjs +48 -0
- package/Skills/git_workflow.cjs +47 -0
- package/Skills/refactor.cjs +40 -0
- package/Skills/systematic_debug.cjs +52 -0
- package/Skills/templates/cli-tool.cjs +55 -0
- package/Skills/templates/express-api.cjs +66 -0
- package/Skills/templates/python-script.cjs +64 -0
- package/Skills/templates/react-component.cjs +71 -0
- package/Skills/templates/test-jest.cjs +41 -0
- package/Skills/test_first.cjs +45 -0
- package/Src/index.jsx +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 代码审查技能 — 写完后自检,不等用户发现问题
|
|
3
|
+
*/
|
|
4
|
+
module.exports = {
|
|
5
|
+
description: "写完代码后系统性地自审: 安全/性能/可读性/边界条件 四维检查",
|
|
6
|
+
category: "development",
|
|
7
|
+
trigger: "每次写完新文件或修改 >20 行代码时自动调用",
|
|
8
|
+
instructions: `
|
|
9
|
+
你刚完成了一次代码修改。现在必须对自己的代码进行系统审查,不能直接说"完成了"。
|
|
10
|
+
|
|
11
|
+
审查按以下四个维度进行:
|
|
12
|
+
|
|
13
|
+
【安全】
|
|
14
|
+
- 有无 SQL 注入?(检查字符串拼接构建 SQL)
|
|
15
|
+
- 有无 XSS?(检查未经转义的用户输入输出)
|
|
16
|
+
- 有无命令注入?(检查 exec/execSync 中的用户输入)
|
|
17
|
+
- 敏感信息是否硬编码?(API key/password)
|
|
18
|
+
|
|
19
|
+
【性能】
|
|
20
|
+
- 有无 N+1 查询?
|
|
21
|
+
- 循环内有无同步 I/O?
|
|
22
|
+
- 大文件是否一次性读入内存?
|
|
23
|
+
|
|
24
|
+
【可读性】
|
|
25
|
+
- 函数是否超过 30 行?
|
|
26
|
+
- 变量名是否清晰?
|
|
27
|
+
- 有无魔数?
|
|
28
|
+
|
|
29
|
+
【边界条件】
|
|
30
|
+
- null/undefined 是否处理?
|
|
31
|
+
- 空数组/空字符串?
|
|
32
|
+
- 极端值(0, -1, MAX_INT)?
|
|
33
|
+
`,
|
|
34
|
+
workflow: [
|
|
35
|
+
"读取刚写的文件完整内容",
|
|
36
|
+
"逐维度检查,列出发现的问题",
|
|
37
|
+
"对每个问题判定: 严重/建议",
|
|
38
|
+
"用 search_replace 修复严重问题",
|
|
39
|
+
"修复后重新运行语法检查",
|
|
40
|
+
"汇报: 检查了X个维度,发现Y个问题,修复了Z个",
|
|
41
|
+
],
|
|
42
|
+
pitfalls: [
|
|
43
|
+
"不要只读几行就下结论——读完整文件",
|
|
44
|
+
"不要假设代码没问题——每一个维度都要真正去看",
|
|
45
|
+
"严重问题必须当场修,不能只报告",
|
|
46
|
+
"如果代码确实完美,也要明确说'四维度检查通过,无问题'",
|
|
47
|
+
],
|
|
48
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git 工作流技能
|
|
3
|
+
*/
|
|
4
|
+
module.exports = {
|
|
5
|
+
description: "标准 Git 工作流: 分支→提交→推送, 含提交信息规范和代码审查前检查",
|
|
6
|
+
category: "development",
|
|
7
|
+
trigger: "用户说 '提交'/'commit'/'push'/'PR' 或写完代码准备提交时使用",
|
|
8
|
+
instructions: `
|
|
9
|
+
【提交前检查】
|
|
10
|
+
- 运行语法检查 (node --check / py_compile)
|
|
11
|
+
- 如果项目有测试,运行相关测试
|
|
12
|
+
- 检查是否有调试代码 (console.log / print / debugger)
|
|
13
|
+
- 检查是否有敏感信息 (API key / token / password)
|
|
14
|
+
|
|
15
|
+
【分支策略】
|
|
16
|
+
- 新功能: feature/<描述>
|
|
17
|
+
- Bug修复: fix/<描述>
|
|
18
|
+
- 重构: refactor/<描述>
|
|
19
|
+
- 不要直接在 main/master 上提交
|
|
20
|
+
|
|
21
|
+
【提交信息规范】
|
|
22
|
+
格式: <type>: <简短描述>
|
|
23
|
+
type: feat / fix / refactor / docs / test / chore
|
|
24
|
+
描述用中文或英文,动词开头,不超过 50 字
|
|
25
|
+
如有必要,空一行后加详细说明
|
|
26
|
+
|
|
27
|
+
【推送与 PR】
|
|
28
|
+
- push 前先 git pull --rebase
|
|
29
|
+
- 解决冲突后重新运行测试
|
|
30
|
+
- PR 描述写清楚: 做了什么 / 为什么 / 怎么测
|
|
31
|
+
`,
|
|
32
|
+
workflow: [
|
|
33
|
+
"检查当前分支和状态 (git status)",
|
|
34
|
+
"创建功能分支 (如需要)",
|
|
35
|
+
"运行语法检查和测试",
|
|
36
|
+
"检查无调试代码和敏感信息",
|
|
37
|
+
"git add 相关文件(不提交无关文件)",
|
|
38
|
+
"按规范撰写提交信息并提交",
|
|
39
|
+
"git pull --rebase 同步远程",
|
|
40
|
+
"推送并汇报",
|
|
41
|
+
],
|
|
42
|
+
pitfalls: [
|
|
43
|
+
"绝对不提交 node_modules / .env / 编译产物",
|
|
44
|
+
"不要 git add . 一把梭——逐文件确认",
|
|
45
|
+
"提交信息不要写 'update' / 'fix' / '修改' 这种无意义描述",
|
|
46
|
+
],
|
|
47
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 安全重构技能
|
|
3
|
+
*/
|
|
4
|
+
module.exports = {
|
|
5
|
+
description: "安全重构: 先测试覆盖, 再小步改, 每步验证. 避免一次大改动导致全线崩溃",
|
|
6
|
+
category: "development",
|
|
7
|
+
trigger: "用户说 '重构'/'整理'/'优化结构' 时使用",
|
|
8
|
+
instructions: `
|
|
9
|
+
重构的前提是:行为不变,结构变好。
|
|
10
|
+
|
|
11
|
+
【第零步: 安全网】
|
|
12
|
+
- 先确认现有测试是否通过
|
|
13
|
+
- 如果没有测试,先写"特征测试"(验证当前行为)
|
|
14
|
+
- 为要修改的模块的公共接口写测试
|
|
15
|
+
|
|
16
|
+
【重构步】
|
|
17
|
+
- 每次只做一个原子变换(如: 提取函数、重命名、移动文件)
|
|
18
|
+
- 每次变换后立即运行测试
|
|
19
|
+
- 测试失败 → 立即回退 → 分析原因 → 换方式再试
|
|
20
|
+
|
|
21
|
+
【验证步】
|
|
22
|
+
- 全部重构完成后运行全量测试
|
|
23
|
+
- 对比重构前后的代码行数和复杂度
|
|
24
|
+
- 确认没有引入新的 lint 警告
|
|
25
|
+
`,
|
|
26
|
+
workflow: [
|
|
27
|
+
"运行现有测试确认基线绿色",
|
|
28
|
+
"如无测试,为要重构的模块写特征测试",
|
|
29
|
+
"识别代码坏味道(长函数/重复/耦合)",
|
|
30
|
+
"按优先级逐个处理,每次只做一个变换",
|
|
31
|
+
"每次变换后运行测试",
|
|
32
|
+
"全量测试验证",
|
|
33
|
+
"汇报重构结果",
|
|
34
|
+
],
|
|
35
|
+
pitfalls: [
|
|
36
|
+
"绝对禁止同时做多个重构",
|
|
37
|
+
"没有测试不要重构——先写测试",
|
|
38
|
+
"不要顺便改功能——重构只改结构不改行为",
|
|
39
|
+
],
|
|
40
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 系统调试技能 — 四阶段根因分析
|
|
3
|
+
*/
|
|
4
|
+
module.exports = {
|
|
5
|
+
description: "遇到错误时进行四阶段根因分析: 复现→定位→修复→验证, 不猜测、不跳过",
|
|
6
|
+
category: "development",
|
|
7
|
+
trigger: "任何命令返回非零退出码、抛出异常、或用户报告 bug 时使用",
|
|
8
|
+
instructions: `
|
|
9
|
+
遇到错误时,严格按以下四阶段执行。禁止跳过任何阶段。
|
|
10
|
+
|
|
11
|
+
【阶段1: 复现】
|
|
12
|
+
- 读取错误信息全文(不要截断)
|
|
13
|
+
- 找到触发错误的具体输入/操作
|
|
14
|
+
- 用同样的输入再次执行,确认可复现
|
|
15
|
+
- 如果是间歇性问题,执行 3 次
|
|
16
|
+
|
|
17
|
+
【阶段2: 定位】
|
|
18
|
+
- 读取报错文件的相关代码(错误行前后 20 行)
|
|
19
|
+
- 追踪数据流:输入从哪里来→经过了什么处理→在哪里出错
|
|
20
|
+
- 用 console.log / print 插入诊断点,确认中间值
|
|
21
|
+
- 不要猜原因——必须看到证据
|
|
22
|
+
|
|
23
|
+
【阶段3: 修复】
|
|
24
|
+
- 确认根因后再改代码
|
|
25
|
+
- 最小改动原则:只改必要的部分
|
|
26
|
+
- 修改后立即运行语法检查
|
|
27
|
+
- 如果有测试,运行相关测试
|
|
28
|
+
|
|
29
|
+
【阶段4: 验证】
|
|
30
|
+
- 用原来触发错误的输入重新测试
|
|
31
|
+
- 测试边界条件(空值/极值)
|
|
32
|
+
- 确认修复没有引入新问题
|
|
33
|
+
- 报告:错误原因→修复内容→验证结果
|
|
34
|
+
`,
|
|
35
|
+
workflow: [
|
|
36
|
+
"读取完整错误信息",
|
|
37
|
+
"定位错误文件和行号",
|
|
38
|
+
"读取错误上下文代码(前后20行)",
|
|
39
|
+
"插入诊断,确认根因",
|
|
40
|
+
"实施最小修复",
|
|
41
|
+
"运行语法检查",
|
|
42
|
+
"用原始输入重新测试",
|
|
43
|
+
"边界条件验证",
|
|
44
|
+
"汇报修复全过程",
|
|
45
|
+
],
|
|
46
|
+
pitfalls: [
|
|
47
|
+
"绝对禁止猜测错误原因——每个结论必须有工具输出支撑",
|
|
48
|
+
"不要跳过诊断直接改代码",
|
|
49
|
+
"不要一次改多个地方——改一处验证一处",
|
|
50
|
+
"如果 3 次尝试仍未修复,停下来总结已知信息,重新分析",
|
|
51
|
+
],
|
|
52
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js CLI 工具模版
|
|
3
|
+
*/
|
|
4
|
+
module.exports = {
|
|
5
|
+
description: "Node.js CLI 工具 (commander + chalk)",
|
|
6
|
+
category: "nodejs",
|
|
7
|
+
params: {
|
|
8
|
+
name: "命令名, 如 'my-cli'",
|
|
9
|
+
description: "CLI 描述",
|
|
10
|
+
commands: "子命令, 逗号分隔, 如 'init,build'",
|
|
11
|
+
},
|
|
12
|
+
generate: function (params) {
|
|
13
|
+
var name = params.name || "my-cli";
|
|
14
|
+
var desc = params.description || "A CLI tool";
|
|
15
|
+
var cmds = params.commands ? params.commands.split(",").map(function (s) { return s.trim(); }) : ["hello"];
|
|
16
|
+
|
|
17
|
+
var lines = [
|
|
18
|
+
"#!/usr/bin/env node",
|
|
19
|
+
"const { program } = require('commander');",
|
|
20
|
+
"const chalk = require('chalk');",
|
|
21
|
+
"const fs = require('fs');",
|
|
22
|
+
"const path = require('path');",
|
|
23
|
+
"",
|
|
24
|
+
"// ---------- 配置 ----------",
|
|
25
|
+
"const pkg = require('../package.json');",
|
|
26
|
+
"program",
|
|
27
|
+
" .name('" + name + "')",
|
|
28
|
+
" .description('" + desc + "')",
|
|
29
|
+
" .version(pkg.version)",
|
|
30
|
+
" .option('-v, --verbose', '详细输出');",
|
|
31
|
+
"",
|
|
32
|
+
"// ---------- 子命令 ----------",
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
cmds.forEach(function (cmd) {
|
|
36
|
+
lines.push("program");
|
|
37
|
+
lines.push(" .command('" + cmd + "')");
|
|
38
|
+
lines.push(" .description('" + cmd + " command')");
|
|
39
|
+
lines.push(" .action(function () {");
|
|
40
|
+
lines.push(" console.log(chalk.green('Running " + cmd + "...'));");
|
|
41
|
+
lines.push(" });");
|
|
42
|
+
lines.push("");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
lines.push("// ---------- 启动 ----------");
|
|
46
|
+
lines.push("program.parse(process.argv);");
|
|
47
|
+
lines.push("");
|
|
48
|
+
lines.push("// 无参数时显示帮助");
|
|
49
|
+
lines.push("if (!process.argv.slice(2).length) {");
|
|
50
|
+
lines.push(" program.outputHelp();");
|
|
51
|
+
lines.push("}");
|
|
52
|
+
|
|
53
|
+
return lines.join("\n");
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express REST API 模版
|
|
3
|
+
* 生成一个完整的 Express 服务器,含路由、中间件、错误处理
|
|
4
|
+
*/
|
|
5
|
+
module.exports = {
|
|
6
|
+
description: "Express.js REST API 服务器 (含路由/中间件/错误处理)",
|
|
7
|
+
category: "nodejs",
|
|
8
|
+
params: {
|
|
9
|
+
name: "项目名",
|
|
10
|
+
port: "端口号, 默认 3000",
|
|
11
|
+
routes: "路由列表, 逗号分隔, 如 'users,posts'",
|
|
12
|
+
},
|
|
13
|
+
generate: function (params) {
|
|
14
|
+
var port = params.port || 3000;
|
|
15
|
+
var routeNames = params.routes ? params.routes.split(",").map(function (s) { return s.trim(); }) : ["api"];
|
|
16
|
+
|
|
17
|
+
var routeUses = routeNames
|
|
18
|
+
.map(function (r) { return "app.use('/api/" + r + "', " + r + "Router);"; })
|
|
19
|
+
.join("\n");
|
|
20
|
+
|
|
21
|
+
var lines = [
|
|
22
|
+
"const express = require('express');",
|
|
23
|
+
"const cors = require('cors');",
|
|
24
|
+
"const morgan = require('morgan');",
|
|
25
|
+
"",
|
|
26
|
+
"const app = express();",
|
|
27
|
+
"const PORT = process.env.PORT || " + port + ";",
|
|
28
|
+
"",
|
|
29
|
+
"// ---------- 中间件 ----------",
|
|
30
|
+
"app.use(cors());",
|
|
31
|
+
"app.use(morgan('dev'));",
|
|
32
|
+
"app.use(express.json());",
|
|
33
|
+
"app.use(express.urlencoded({ extended: true }));",
|
|
34
|
+
"",
|
|
35
|
+
"// ---------- 路由 ----------",
|
|
36
|
+
routeUses,
|
|
37
|
+
"",
|
|
38
|
+
"app.get('/health', (req, res) => {",
|
|
39
|
+
" res.json({ status: 'ok', uptime: process.uptime() });",
|
|
40
|
+
"});",
|
|
41
|
+
"",
|
|
42
|
+
"// ---------- 404 ----------",
|
|
43
|
+
"app.use((req, res) => {",
|
|
44
|
+
" res.status(404).json({ error: 'Not Found' });",
|
|
45
|
+
"});",
|
|
46
|
+
"",
|
|
47
|
+
"// ---------- 全局错误处理 ----------",
|
|
48
|
+
"app.use((err, req, res, next) => {",
|
|
49
|
+
" console.error('[Error]', err.message);",
|
|
50
|
+
" res.status(err.status || 500).json({",
|
|
51
|
+
" error: err.message || 'Internal Server Error',",
|
|
52
|
+
" });",
|
|
53
|
+
"});",
|
|
54
|
+
"",
|
|
55
|
+
"// ---------- 启动 ----------",
|
|
56
|
+
"if (require.main === module) {",
|
|
57
|
+
" app.listen(PORT, function () {",
|
|
58
|
+
" console.log('Server running on http://localhost:' + PORT);",
|
|
59
|
+
" });",
|
|
60
|
+
"}",
|
|
61
|
+
"",
|
|
62
|
+
"module.exports = app;",
|
|
63
|
+
];
|
|
64
|
+
return lines.join("\n");
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python 脚本模版
|
|
3
|
+
*/
|
|
4
|
+
module.exports = {
|
|
5
|
+
description: "Python 脚本骨架 (argparse + logging)",
|
|
6
|
+
category: "python",
|
|
7
|
+
params: {
|
|
8
|
+
name: "脚本描述",
|
|
9
|
+
},
|
|
10
|
+
generate: function (params) {
|
|
11
|
+
var desc = params.name || "Python script";
|
|
12
|
+
return [
|
|
13
|
+
"#!/usr/bin/env python3",
|
|
14
|
+
'"""' + desc + '"""',
|
|
15
|
+
"",
|
|
16
|
+
"import argparse",
|
|
17
|
+
"import logging",
|
|
18
|
+
"import sys",
|
|
19
|
+
"from pathlib import Path",
|
|
20
|
+
"",
|
|
21
|
+
"# ---------- 日志 ----------",
|
|
22
|
+
"logging.basicConfig(",
|
|
23
|
+
' level=logging.INFO,',
|
|
24
|
+
' format="%(asctime)s [%(levelname)s] %(message)s",',
|
|
25
|
+
' datefmt="%Y-%m-%d %H:%M:%S",',
|
|
26
|
+
")",
|
|
27
|
+
"logger = logging.getLogger(__name__)",
|
|
28
|
+
"",
|
|
29
|
+
"",
|
|
30
|
+
"def main() -> None:",
|
|
31
|
+
' parser = argparse.ArgumentParser(description="' + desc + '")',
|
|
32
|
+
' parser.add_argument("--verbose", "-v", action="store_true", help="详细输出")',
|
|
33
|
+
' parser.add_argument("input", nargs="?", help="输入文件路径")',
|
|
34
|
+
" args = parser.parse_args()",
|
|
35
|
+
"",
|
|
36
|
+
" if args.verbose:",
|
|
37
|
+
" logging.getLogger().setLevel(logging.DEBUG)",
|
|
38
|
+
"",
|
|
39
|
+
' logger.info("Starting...")',
|
|
40
|
+
"",
|
|
41
|
+
" try:",
|
|
42
|
+
" # TODO: 主逻辑",
|
|
43
|
+
" if args.input:",
|
|
44
|
+
" path = Path(args.input)",
|
|
45
|
+
" if not path.exists():",
|
|
46
|
+
' logger.error(f"File not found: {args.input}")',
|
|
47
|
+
" sys.exit(1)",
|
|
48
|
+
' logger.info(f"Processing: {path}")',
|
|
49
|
+
"",
|
|
50
|
+
' logger.info("Done.")',
|
|
51
|
+
"",
|
|
52
|
+
" except KeyboardInterrupt:",
|
|
53
|
+
' logger.info("Interrupted by user")',
|
|
54
|
+
" sys.exit(130)",
|
|
55
|
+
" except Exception as e:",
|
|
56
|
+
' logger.exception(f"Fatal error: {e}")',
|
|
57
|
+
" sys.exit(1)",
|
|
58
|
+
"",
|
|
59
|
+
"",
|
|
60
|
+
'if __name__ == "__main__":',
|
|
61
|
+
" main()",
|
|
62
|
+
].join("\n");
|
|
63
|
+
},
|
|
64
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React 函数组件模版 — TypeScript + Hooks
|
|
3
|
+
*/
|
|
4
|
+
module.exports = {
|
|
5
|
+
description: "React 函数组件 (TypeScript + Hooks + Props)",
|
|
6
|
+
category: "react",
|
|
7
|
+
params: {
|
|
8
|
+
name: "组件名 (PascalCase), 如 'UserCard'",
|
|
9
|
+
props: "Props 定义, 如 'title:string,count:number'",
|
|
10
|
+
useState: "state 变量, 逗号分隔",
|
|
11
|
+
useEffect: "useEffect 依赖",
|
|
12
|
+
},
|
|
13
|
+
generate: function (params) {
|
|
14
|
+
var name = params.name || "MyComponent";
|
|
15
|
+
var propsDef = params.props || "";
|
|
16
|
+
var states = params.useState ? params.useState.split(",").map(function (s) { return s.trim(); }) : [];
|
|
17
|
+
var hasEffect = !!params.useEffect;
|
|
18
|
+
var effectDep = params.useEffect || "";
|
|
19
|
+
|
|
20
|
+
var lines = ["import React" + (states.length > 0 ? ", { useState" + (hasEffect ? ", useEffect" : "") + " }" : "") + " from 'react';"];
|
|
21
|
+
|
|
22
|
+
// Props interface
|
|
23
|
+
if (propsDef) {
|
|
24
|
+
lines.push("");
|
|
25
|
+
lines.push("interface " + name + "Props {");
|
|
26
|
+
propsDef.split(",").forEach(function (p) {
|
|
27
|
+
var parts = p.split(":").map(function (s) { return s.trim(); });
|
|
28
|
+
lines.push(" " + parts[0] + (parts[1] ? ": " + parts[1] : ": any") + ";");
|
|
29
|
+
});
|
|
30
|
+
lines.push("}");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Destructure props
|
|
34
|
+
var propsKeys = propsDef ? propsDef.split(",").map(function (p) { return p.split(":")[0].trim(); }) : [];
|
|
35
|
+
var propsDestruct = propsKeys.length > 0 ? "{ " + propsKeys.join(", ") + " }" : "";
|
|
36
|
+
|
|
37
|
+
var compType = propsDef ? "React.FC<" + name + "Props>" : "React.FC";
|
|
38
|
+
|
|
39
|
+
lines.push("");
|
|
40
|
+
lines.push("export const " + name + ": " + compType + " = (" + propsDestruct + ") => {");
|
|
41
|
+
|
|
42
|
+
// State declarations
|
|
43
|
+
if (states.length > 0) {
|
|
44
|
+
states.forEach(function (s) {
|
|
45
|
+
var cap = s.charAt(0).toUpperCase() + s.slice(1);
|
|
46
|
+
var type = s === "loading" ? "boolean" : s === "error" ? "string | null" : "any";
|
|
47
|
+
lines.push(" const [" + s + ", set" + cap + "] = useState<" + type + ">(null);");
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// useEffect
|
|
52
|
+
if (hasEffect) {
|
|
53
|
+
lines.push("");
|
|
54
|
+
lines.push(" useEffect(function () {");
|
|
55
|
+
lines.push(" // 当 " + effectDep + " 变化时执行");
|
|
56
|
+
lines.push(" }, [" + effectDep.split(",").map(function (s) { return s.trim(); }).join(", ") + "]);");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
lines.push("");
|
|
60
|
+
lines.push(" return (");
|
|
61
|
+
lines.push(" <div className=\"" + name.toLowerCase() + "\">");
|
|
62
|
+
lines.push(" {/* TODO: 组件内容 */}");
|
|
63
|
+
lines.push(" </div>");
|
|
64
|
+
lines.push(" );");
|
|
65
|
+
lines.push("};");
|
|
66
|
+
lines.push("");
|
|
67
|
+
lines.push("export default " + name + ";");
|
|
68
|
+
|
|
69
|
+
return lines.join("\n");
|
|
70
|
+
},
|
|
71
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jest 测试模版
|
|
3
|
+
*/
|
|
4
|
+
module.exports = {
|
|
5
|
+
description: "Jest 测试文件 (describe/it/beforeEach)",
|
|
6
|
+
category: "testing",
|
|
7
|
+
params: {
|
|
8
|
+
module: "被测试模块路径, 如 '../src/utils'",
|
|
9
|
+
functions: "导出函数, 逗号分隔, 如 'parse,validate'",
|
|
10
|
+
},
|
|
11
|
+
generate: function (params) {
|
|
12
|
+
var modulePath = params.module || "../src/module";
|
|
13
|
+
var funcs = params.functions ? params.functions.split(",").map(function (s) { return s.trim(); }) : ["myFunction"];
|
|
14
|
+
|
|
15
|
+
var lines = [
|
|
16
|
+
"const { " + funcs.join(", ") + " } = require('" + modulePath + "');",
|
|
17
|
+
"",
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
funcs.forEach(function (fn) {
|
|
21
|
+
lines.push("describe('" + fn + "', function () {");
|
|
22
|
+
lines.push(" it('should return expected value for valid input', function () {");
|
|
23
|
+
lines.push(" const result = " + fn + "(/* valid input */);");
|
|
24
|
+
lines.push(" expect(result).toBeDefined();");
|
|
25
|
+
lines.push(" });");
|
|
26
|
+
lines.push("");
|
|
27
|
+
lines.push(" it('should handle edge case: null input', function () {");
|
|
28
|
+
lines.push(" expect(function () { " + fn + "(null); }).toThrow();");
|
|
29
|
+
lines.push(" });");
|
|
30
|
+
lines.push("");
|
|
31
|
+
lines.push(" it('should handle edge case: empty input', function () {");
|
|
32
|
+
lines.push(" const result = " + fn + "(/* empty input */);");
|
|
33
|
+
lines.push(" // TODO: define expected behavior");
|
|
34
|
+
lines.push(" });");
|
|
35
|
+
lines.push("});");
|
|
36
|
+
lines.push("");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return lines.join("\n");
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 测试驱动开发技能 — 红→绿→重构
|
|
3
|
+
*/
|
|
4
|
+
module.exports = {
|
|
5
|
+
description: "测试驱动开发(TDD): 先写失败的测试, 再写最小实现, 最后重构. 严格红→绿→重构循环",
|
|
6
|
+
category: "development",
|
|
7
|
+
trigger: "用户说 '写一个函数'/'实现'/'开发' 时使用; 写新模块时使用",
|
|
8
|
+
instructions: `
|
|
9
|
+
严格遵循 RED → GREEN → REFACTOR 三步循环。不允许跳过任何步骤。
|
|
10
|
+
|
|
11
|
+
【RED — 写失败的测试】
|
|
12
|
+
- 先确定函数的输入/输出规格
|
|
13
|
+
- 写一个测试用例,描述期望行为
|
|
14
|
+
- 运行测试,确认它失败(红灯)
|
|
15
|
+
- 如果测试直接通过,说明测试写得不对或功能已存在
|
|
16
|
+
|
|
17
|
+
【GREEN — 最小实现】
|
|
18
|
+
- 写刚好让测试通过的最少代码
|
|
19
|
+
- 不要"顺便"加功能——只满足当前测试
|
|
20
|
+
- 运行测试,确认通过(绿灯)
|
|
21
|
+
- 如果没通过,只看测试输出,不要猜
|
|
22
|
+
|
|
23
|
+
【REFACTOR — 重构】
|
|
24
|
+
- 绿灯后才能重构
|
|
25
|
+
- 消除重复、改善命名、提取函数
|
|
26
|
+
- 每次重构后立即运行测试
|
|
27
|
+
- 红灯出现说明重构引入 bug,立即回退
|
|
28
|
+
`,
|
|
29
|
+
workflow: [
|
|
30
|
+
"确定函数签名和预期行为",
|
|
31
|
+
"写测试文件,覆盖正常输入+边界条件",
|
|
32
|
+
"运行测试确认 RED(失败)",
|
|
33
|
+
"写最小实现代码确保测试通过",
|
|
34
|
+
"运行测试确认 GREEN",
|
|
35
|
+
"重构代码改善质量",
|
|
36
|
+
"再次运行测试确认仍然 GREEN",
|
|
37
|
+
"报告: RED→GREEN→REFACTOR 完成",
|
|
38
|
+
],
|
|
39
|
+
pitfalls: [
|
|
40
|
+
"绝对禁止先写实现后补测试",
|
|
41
|
+
"测试必须包含边界条件(null/空/极值)",
|
|
42
|
+
"最小实现就是最小——不要额外功能",
|
|
43
|
+
"重构必须在绿灯下进行",
|
|
44
|
+
],
|
|
45
|
+
};
|
package/Src/index.jsx
CHANGED
|
@@ -22,7 +22,7 @@ const CLINN_CONFIG = path.join(CLINN_DIR, "config.json");
|
|
|
22
22
|
const PKG_CONFIG = path.join(__dirname, "..", "config.json");
|
|
23
23
|
const LOGO_PATH = path.join(__dirname, "..", "Logos", "StartLogo.txt");
|
|
24
24
|
|
|
25
|
-
const VER = "0.8.
|
|
25
|
+
const VER = "0.8.7";
|
|
26
26
|
|
|
27
27
|
function ensureDir() { if (!fs.existsSync(CLINN_DIR)) fs.mkdirSync(CLINN_DIR, { recursive: true }); }
|
|
28
28
|
function loadConfig() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghenya/clinn",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.7",
|
|
4
4
|
"description": "终端原生 AI 编程助手 — DeepSeek 驱动,50+ 工具,对话记忆,虚拟浏览器,Ink 全屏界面",
|
|
5
5
|
"main": "Src/index.jsx",
|
|
6
6
|
"bin": {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"Tools/",
|
|
12
12
|
"Mem/",
|
|
13
13
|
"Logos/",
|
|
14
|
+
"Skills/",
|
|
14
15
|
"bin/",
|
|
15
16
|
"config.json",
|
|
16
17
|
"README.md",
|