xiaozhou-chat 1.0.26 → 1.0.27
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/bin/cli.js +58 -21
- package/lib/chat.js +38 -0
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
import fs from "node:fs";
|
|
38
38
|
import path from "node:path";
|
|
39
39
|
import readline from "node:readline";
|
|
40
|
+
import { execSync } from "node:child_process";
|
|
40
41
|
import minimist from "minimist";
|
|
41
42
|
import { MCPClient } from "../lib/mcp-lite.js";
|
|
42
43
|
import {
|
|
@@ -54,7 +55,8 @@ import {
|
|
|
54
55
|
exportHistory
|
|
55
56
|
} from "../lib/history.js";
|
|
56
57
|
import {
|
|
57
|
-
chatStream
|
|
58
|
+
chatStream,
|
|
59
|
+
generateCompletion
|
|
58
60
|
} from "../lib/chat.js";
|
|
59
61
|
import {
|
|
60
62
|
builtInTools,
|
|
@@ -431,36 +433,71 @@ rl.on("line", async (line) => {
|
|
|
431
433
|
console.log("⚠️ 历史记录太短,无需压缩。");
|
|
432
434
|
return rl.prompt();
|
|
433
435
|
}
|
|
434
|
-
|
|
435
|
-
// To simplify: we construct a temporary context just for summary
|
|
436
|
+
|
|
436
437
|
const toCompress = messages.slice(0, -2);
|
|
437
438
|
const recent = messages.slice(-2);
|
|
438
439
|
|
|
439
440
|
const summaryPrompt = `
|
|
440
441
|
请总结以下对话的主要内容,提取关键信息、代码片段和决策。
|
|
441
442
|
摘要应简洁明了,以便作为后续对话的上下文。
|
|
443
|
+
保留所有重要的技术细节。
|
|
442
444
|
|
|
443
445
|
对话内容:
|
|
444
446
|
${JSON.stringify(toCompress)}
|
|
445
447
|
`;
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
448
|
+
try {
|
|
449
|
+
const summary = await generateCompletion(activeConfig, [{role: "user", content: summaryPrompt}]);
|
|
450
|
+
messages = [
|
|
451
|
+
{ role: "system", content: `Previous conversation summary:\n${summary}` },
|
|
452
|
+
...recent
|
|
453
|
+
];
|
|
454
|
+
saveHistory(messages);
|
|
455
|
+
console.log("✅ 历史记录已压缩");
|
|
456
|
+
console.log("摘要预览:", summary.slice(0, 100).replace(/\n/g, ' ') + "...");
|
|
457
|
+
} catch (e) {
|
|
458
|
+
console.error("❌ 压缩失败:", e.message);
|
|
459
|
+
}
|
|
460
|
+
return rl.prompt();
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (input === "/commit") {
|
|
464
|
+
try {
|
|
465
|
+
let diff;
|
|
466
|
+
try {
|
|
467
|
+
diff = execSync("git diff --cached", { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
468
|
+
} catch (e) {
|
|
469
|
+
console.log("❌ 获取 git diff 失败,请确认当前目录是 git 仓库");
|
|
470
|
+
return rl.prompt();
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (!diff) {
|
|
474
|
+
console.log("⚠️ 暂存区为空,请先执行 'git add <file>'");
|
|
475
|
+
return rl.prompt();
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
console.log("🤖 正在分析 Diff 并生成 Commit Message...");
|
|
479
|
+
const prompt = `
|
|
480
|
+
你是一个资深的开发者。请根据以下的 Git Diff 内容,生成一个符合 Conventional Commits 规范的 Commit Message。
|
|
481
|
+
只返回 Commit Message 本身,不要包含 markdown 代码块或其他解释。
|
|
482
|
+
|
|
483
|
+
Diff 内容:
|
|
484
|
+
${diff.slice(0, 8000)}
|
|
485
|
+
`;
|
|
486
|
+
const msg = await generateCompletion(activeConfig, [{role: "user", content: prompt}]);
|
|
487
|
+
console.log("\n----- 建议的 Commit Message -----");
|
|
488
|
+
console.log(`\x1b[32m${msg.trim()}\x1b[0m`);
|
|
489
|
+
console.log("-----------------------------------");
|
|
490
|
+
|
|
491
|
+
const ans = await askQuestion("提交吗? (y/n) ");
|
|
492
|
+
if (ans.trim().toLowerCase() === 'y') {
|
|
493
|
+
execSync(`git commit -m "${msg.trim().replace(/"/g, '\\"')}"`, { stdio: 'inherit' });
|
|
494
|
+
console.log("✅ 提交成功");
|
|
495
|
+
} else {
|
|
496
|
+
console.log("🚫 已取消");
|
|
497
|
+
}
|
|
498
|
+
} catch (e) {
|
|
499
|
+
console.error("❌ Commit 生成失败:", e.message);
|
|
500
|
+
}
|
|
464
501
|
return rl.prompt();
|
|
465
502
|
}
|
|
466
503
|
|
package/lib/chat.js
CHANGED
|
@@ -105,6 +105,44 @@ export async function requestWithRetry(url, options, maxRetries = 3) {
|
|
|
105
105
|
throw lastError;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
export async function generateCompletion(config, messages, options = {}) {
|
|
109
|
+
const {
|
|
110
|
+
model = config.model,
|
|
111
|
+
max_tokens = 4096,
|
|
112
|
+
jsonMode = false
|
|
113
|
+
} = options;
|
|
114
|
+
|
|
115
|
+
const requestUrl = `${config.baseUrl}${config.baseUrl.endsWith('/') ? '' : '/'}chat/completions`;
|
|
116
|
+
|
|
117
|
+
// 自动修正 v1
|
|
118
|
+
const finalUrl = (requestUrl.includes("/v1/") || requestUrl.endsWith("/v1"))
|
|
119
|
+
? requestUrl
|
|
120
|
+
: requestUrl.replace("/chat/completions", "/v1/chat/completions");
|
|
121
|
+
|
|
122
|
+
const body = {
|
|
123
|
+
model,
|
|
124
|
+
messages,
|
|
125
|
+
stream: false,
|
|
126
|
+
max_tokens
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
if (jsonMode) {
|
|
130
|
+
body.response_format = { type: "json_object" };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const res = await requestWithRetry(finalUrl, {
|
|
134
|
+
method: "POST",
|
|
135
|
+
headers: {
|
|
136
|
+
"Content-Type": "application/json",
|
|
137
|
+
Authorization: `Bearer ${config.apiKey}`
|
|
138
|
+
},
|
|
139
|
+
body: JSON.stringify(body)
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const data = await res.json();
|
|
143
|
+
return data.choices?.[0]?.message?.content || "";
|
|
144
|
+
}
|
|
145
|
+
|
|
108
146
|
export async function chatStream(context, userInput = null, options = {}) {
|
|
109
147
|
const {
|
|
110
148
|
messages,
|