@ghenya/clinn 0.8.4 → 0.8.6
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/agent.cjs +48 -4
- package/Src/index.jsx +42 -18
- package/Tools/extended_tools.js +149 -12
- package/Tools/file_tools.js +7 -1
- package/Tools/index.js +2 -0
- package/Tools/kaomoji.js +273 -0
- package/Tools/syntax_check.js +119 -0
- package/Tools/template_engine.js +105 -0
- package/config.json +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 语法校验工具 — 写文件后自动检查,当场报错
|
|
3
|
+
* 支持: .js/.mjs/.cjs (node --check), .ts/.tsx (tsc), .py (py_compile), .json (JSON.parse)
|
|
4
|
+
*/
|
|
5
|
+
const { execSync } = require("child_process");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 对文件进行语法检查
|
|
11
|
+
* @param {string} filePath - 文件路径
|
|
12
|
+
* @returns {string|null} - 错误信息,null 表示通过
|
|
13
|
+
*/
|
|
14
|
+
function syntaxCheck(filePath) {
|
|
15
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
switch (ext) {
|
|
19
|
+
case ".js":
|
|
20
|
+
case ".mjs":
|
|
21
|
+
case ".cjs": {
|
|
22
|
+
execSync(`node --check ${JSON.stringify(filePath)}`, {
|
|
23
|
+
encoding: "utf-8",
|
|
24
|
+
timeout: 10000,
|
|
25
|
+
stdio: "pipe",
|
|
26
|
+
});
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
case ".ts":
|
|
31
|
+
case ".tsx": {
|
|
32
|
+
// 检查项目根是否有 tsconfig.json
|
|
33
|
+
const dir = path.dirname(filePath);
|
|
34
|
+
const tsconfig = findUp("tsconfig.json", dir);
|
|
35
|
+
if (tsconfig) {
|
|
36
|
+
execSync(`npx tsc --noEmit --pretty false 2>&1 | grep ${JSON.stringify(path.basename(filePath))} || true`, {
|
|
37
|
+
encoding: "utf-8",
|
|
38
|
+
timeout: 30000,
|
|
39
|
+
cwd: path.dirname(tsconfig),
|
|
40
|
+
stdio: "pipe",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
// tsc 太慢,只做 node 基础检查
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
case ".py": {
|
|
48
|
+
execSync(`python3 -m py_compile ${JSON.stringify(filePath)}`, {
|
|
49
|
+
encoding: "utf-8",
|
|
50
|
+
timeout: 10000,
|
|
51
|
+
stdio: "pipe",
|
|
52
|
+
});
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
case ".json": {
|
|
57
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
58
|
+
JSON.parse(raw);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
case ".html":
|
|
63
|
+
case ".css":
|
|
64
|
+
case ".scss":
|
|
65
|
+
case ".md":
|
|
66
|
+
case ".txt":
|
|
67
|
+
case ".yaml":
|
|
68
|
+
case ".yml":
|
|
69
|
+
case ".toml":
|
|
70
|
+
// 这些格式不做语法检查
|
|
71
|
+
return null;
|
|
72
|
+
|
|
73
|
+
default:
|
|
74
|
+
// 未知扩展名,尝试 node --check(可能是无扩展名的 JS)
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
} catch (e) {
|
|
78
|
+
// 提取有用的错误信息
|
|
79
|
+
let msg = (e.stderr || e.stdout || e.message || "").toString();
|
|
80
|
+
|
|
81
|
+
// 精简 tsc 输出
|
|
82
|
+
if (ext === ".ts" || ext === ".tsx") {
|
|
83
|
+
const lines = msg.split("\n").filter((l) => l.includes(path.basename(filePath)));
|
|
84
|
+
if (lines.length > 0) {
|
|
85
|
+
msg = lines.slice(0, 3).join("\n");
|
|
86
|
+
} else {
|
|
87
|
+
msg = msg.slice(0, 300);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 精简 node 输出
|
|
92
|
+
if (ext === ".js" || ext === ".mjs" || ext === ".cjs") {
|
|
93
|
+
msg = msg.split("\n").slice(0, 3).join("\n");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 精简 python 输出
|
|
97
|
+
if (ext === ".py") {
|
|
98
|
+
msg = msg.split("\n").filter((l) => l.trim()).slice(0, 3).join("\n");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return msg.slice(0, 500);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 向上查找文件
|
|
107
|
+
*/
|
|
108
|
+
function findUp(filename, startDir) {
|
|
109
|
+
let dir = path.resolve(startDir);
|
|
110
|
+
const root = path.parse(dir).root;
|
|
111
|
+
while (true) {
|
|
112
|
+
const p = path.join(dir, filename);
|
|
113
|
+
if (fs.existsSync(p)) return p;
|
|
114
|
+
if (dir === root) return null;
|
|
115
|
+
dir = path.dirname(dir);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = { syntaxCheck, findUp };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模版引擎 — 类似 Trae 的代码骨架系统
|
|
3
|
+
* 从 Skills/templates/ 加载预置模版,生成带语法检查的代码骨架
|
|
4
|
+
*/
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const { syntaxCheck } = require("./syntax_check");
|
|
8
|
+
|
|
9
|
+
const TEMPLATES_DIR = path.join(__dirname, "..", "Skills", "templates");
|
|
10
|
+
|
|
11
|
+
function loadTemplates() {
|
|
12
|
+
if (!fs.existsSync(TEMPLATES_DIR)) return {};
|
|
13
|
+
const files = fs.readdirSync(TEMPLATES_DIR).filter((f) => f.endsWith(".cjs"));
|
|
14
|
+
const templates = {};
|
|
15
|
+
for (const file of files) {
|
|
16
|
+
try {
|
|
17
|
+
const mod = require(path.join(TEMPLATES_DIR, file));
|
|
18
|
+
const name = file.replace(/\.cjs$/, "");
|
|
19
|
+
templates[name] = {
|
|
20
|
+
name,
|
|
21
|
+
description: mod.description || "(无描述)",
|
|
22
|
+
category: mod.category || "general",
|
|
23
|
+
params: mod.params || {},
|
|
24
|
+
generate: mod.generate,
|
|
25
|
+
};
|
|
26
|
+
} catch (_) {}
|
|
27
|
+
}
|
|
28
|
+
return templates;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const useTemplateTool = {
|
|
32
|
+
name: "use_template",
|
|
33
|
+
description: "代码模版引擎: 列出可用模版 或 按模版生成代码骨架. 类似 Trae 的模板系统",
|
|
34
|
+
parameters: {
|
|
35
|
+
action: { type: "string", required: true, description: "list(列出所有模版) 或 generate(按模版名生成代码)" },
|
|
36
|
+
name: { type: "string", required: false, description: "模版名, generate 时必填, 如 'express-api' 或 'react-component'" },
|
|
37
|
+
params: { type: "string", required: false, description: "模版参数, JSON 字符串, 如 '{\"name\":\"myApp\"}'" },
|
|
38
|
+
},
|
|
39
|
+
execute: async ({ action, name, params }) => {
|
|
40
|
+
const templates = loadTemplates();
|
|
41
|
+
|
|
42
|
+
if (action === "list") {
|
|
43
|
+
const names = Object.keys(templates);
|
|
44
|
+
if (names.length === 0) return "(暂无可用模版)\n提示: 将模版文件放入 Skills/templates/ 目录";
|
|
45
|
+
|
|
46
|
+
// 按分类分组
|
|
47
|
+
const groups = {};
|
|
48
|
+
for (const [key, t] of Object.entries(templates)) {
|
|
49
|
+
if (!groups[t.category]) groups[t.category] = [];
|
|
50
|
+
groups[t.category].push(t);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const lines = [`可用模版 (${names.length} 个):`];
|
|
54
|
+
for (const [cat, items] of Object.entries(groups)) {
|
|
55
|
+
lines.push(`\n[${cat}]`);
|
|
56
|
+
for (const t of items) {
|
|
57
|
+
lines.push(` ${t.name} — ${t.description}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
lines.push(`\n用法: use_template action=generate name=<模版名>`);
|
|
61
|
+
return lines.join("\n");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (action === "generate") {
|
|
65
|
+
if (!name) return "[失败] 请指定模版名, 用 action=list 查看可用模版";
|
|
66
|
+
|
|
67
|
+
const template = templates[name];
|
|
68
|
+
if (!template) {
|
|
69
|
+
const available = Object.keys(templates).join(", ");
|
|
70
|
+
return `[不存在] 模版 "${name}"\n可用: ${available || "(无)"}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let parsedParams = {};
|
|
74
|
+
if (params) {
|
|
75
|
+
try {
|
|
76
|
+
parsedParams = JSON.parse(params);
|
|
77
|
+
} catch (_) {
|
|
78
|
+
return `[失败] params 不是合法 JSON: ${params}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const code = template.generate(parsedParams);
|
|
84
|
+
return [
|
|
85
|
+
`[模版: ${name}] ${template.description}`,
|
|
86
|
+
`参数: ${JSON.stringify(parsedParams)}`,
|
|
87
|
+
"",
|
|
88
|
+
"```",
|
|
89
|
+
code,
|
|
90
|
+
"```",
|
|
91
|
+
"",
|
|
92
|
+
"提示: 用 write_file 将以上代码写入文件",
|
|
93
|
+
].join("\n");
|
|
94
|
+
} catch (e) {
|
|
95
|
+
return `[模版生成失败] ${name}: ${e.message}`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return `[错误] 未知 action: ${action}, 可用: list / generate`;
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
use_template: useTemplateTool,
|
|
105
|
+
};
|
package/config.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghenya/clinn",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.6",
|
|
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",
|
|
@@ -58,4 +59,4 @@
|
|
|
58
59
|
"react": "^18.3.1",
|
|
59
60
|
"tsx": "^4.0.0"
|
|
60
61
|
}
|
|
61
|
-
}
|
|
62
|
+
}
|