shenxiang-ai-cli 0.4.4 → 0.5.1
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/dist/index.js +166 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,6 +5,9 @@ import { Command } from "commander";
|
|
|
5
5
|
|
|
6
6
|
// src/commands/chat.ts
|
|
7
7
|
import inquirer from "inquirer";
|
|
8
|
+
import fs5 from "fs/promises";
|
|
9
|
+
import path5 from "path";
|
|
10
|
+
import { glob as glob4 } from "glob";
|
|
8
11
|
|
|
9
12
|
// src/core/providers/openai-compatible.ts
|
|
10
13
|
import OpenAI from "openai";
|
|
@@ -737,7 +740,7 @@ function banner() {
|
|
|
737
740
|
const w = chalk.white.bold;
|
|
738
741
|
const lines = [
|
|
739
742
|
``,
|
|
740
|
-
` ${p("/\\_/\\")} ${b("\u6C88\u7FD4\u7684AI\u52A9\u624B")} ${m("v0.
|
|
743
|
+
` ${p("/\\_/\\")} ${b("\u6C88\u7FD4\u7684AI\u52A9\u624B")} ${m("v0.5.1")}`,
|
|
741
744
|
` ${p("(")} ${y("o")}${p(".")}${y("o")} ${p(")")} ${d("\u7EC8\u7AEF\u91CC\u7684AI\u5168\u6808\u5F00\u53D1\u642D\u6863")}`,
|
|
742
745
|
` ${p("> ")}${r("^")}${p(" <")}`,
|
|
743
746
|
` ${p("/|")}${p(" |")}${p("\\")} ${d("\u4F60\u7684\u7F16\u7A0B\u597D\u4F19\u4F34")} ${chalk.hex("#A78BFA")("\u{1F43E}")}`,
|
|
@@ -890,7 +893,8 @@ var writeFileTool = {
|
|
|
890
893
|
return { success: false, output: "", error: `Failed to write file: ${msg}` };
|
|
891
894
|
}
|
|
892
895
|
},
|
|
893
|
-
requiresConfirmation:
|
|
896
|
+
requiresConfirmation: false
|
|
897
|
+
// auto-approve for speed; use /auto off + delete_file for safety
|
|
894
898
|
};
|
|
895
899
|
var editFileTool = {
|
|
896
900
|
definition: {
|
|
@@ -953,7 +957,8 @@ var editFileTool = {
|
|
|
953
957
|
return { success: false, output: "", error: `Failed to edit file: ${msg}` };
|
|
954
958
|
}
|
|
955
959
|
},
|
|
956
|
-
requiresConfirmation:
|
|
960
|
+
requiresConfirmation: false
|
|
961
|
+
// auto-approve for speed
|
|
957
962
|
};
|
|
958
963
|
var deleteFileTool = {
|
|
959
964
|
definition: {
|
|
@@ -1673,7 +1678,8 @@ function generateSystemPrompt(ctx) {
|
|
|
1673
1678
|
[\u89D2\u8272] \u7528\u6237\u662F\u4EA7\u54C1\u8D1F\u8D23\u4EBA\uFF0C\u4F60\u6765\u5B9E\u73B0\u3002\u901A\u4FD7\u89E3\u91CA\uFF0C\u4E0D\u5806\u672F\u8BED\u3002\u65B9\u5411\u4E0D\u5BF9\u5C31\u63D0\u9192\u3002\u5BF9\u5C40\u9650\u6027\u8BDA\u5B9E\u3002`);
|
|
1674
1679
|
parts.push(`[\u534F\u4F5C] \u63A2\u7D22\u9700\u6C42\u2192\u89C4\u5212\u65B9\u6848\u2192\u5206\u9636\u6BB5\u6784\u5EFA\u2192\u6253\u78E8\u7EC6\u8282\u2192\u4EA4\u4ED8\u90E8\u7F72\u3002\u5173\u952E\u51B3\u7B56\u70B9\u505C\u4E0B\u786E\u8BA4\uFF0C\u9047\u95EE\u9898\u7ED9\u9009\u9879\u800C\u975E\u66FF\u7528\u6237\u51B3\u5B9A\u3002`);
|
|
1675
1680
|
parts.push(`
|
|
1676
|
-
[\u539F\u5219] \u5148\u8BFB\u518D\u6539\uFF1Bedit_file\u5C40\u90E8\u7F16\u8F91\uFF1B\u7834\u574F\u6027\u64CD\u4F5C\u8C28\u614E\uFF1B\u9075\u5FAA\u9879\u76EE\u98CE\u683C\uFF1B\u4E0D\u786E\u5B9A\u5C31\u5148\u641C\u7D22\uFF1B\u7528${ctx.packageManager}\u6267\u884C\u547D\u4EE4\
|
|
1681
|
+
[\u539F\u5219] \u5148\u8BFB\u518D\u6539\uFF1Bedit_file\u5C40\u90E8\u7F16\u8F91\uFF1B\u7834\u574F\u6027\u64CD\u4F5C\u8C28\u614E\uFF1B\u9075\u5FAA\u9879\u76EE\u98CE\u683C\uFF1B\u4E0D\u786E\u5B9A\u5C31\u5148\u641C\u7D22\uFF1B\u7528${ctx.packageManager}\u6267\u884C\u547D\u4EE4\u3002`);
|
|
1682
|
+
parts.push(`[\u6548\u7387] \u91CD\u8981\uFF1A\u5C3D\u91CF\u5728\u4E00\u6B21\u56DE\u590D\u4E2D\u8FD4\u56DE\u591A\u4E2A\u5DE5\u5177\u8C03\u7528\uFF08\u5E76\u884C\uFF09\uFF0C\u4E0D\u8981\u4E00\u4E2A\u4E00\u4E2A\u6765\u3002\u4F8B\u5982\u9700\u8981\u521B\u5EFA3\u4E2A\u6587\u4EF6\uFF0C\u4E00\u6B21\u6027\u8FD4\u56DE3\u4E2Awrite_file\u8C03\u7528\uFF0C\u800C\u4E0D\u662F\u5199\u5B8C\u4E00\u4E2A\u518D\u5199\u4E0B\u4E00\u4E2A\u3002\u8FD9\u6837\u5927\u5E45\u51CF\u5C11\u7B49\u5F85\u65F6\u95F4\u3002`);
|
|
1677
1683
|
if (ctx.projectType !== "unknown") {
|
|
1678
1684
|
const info = [`\u76EE\u5F55:${ctx.rootDir}`, `\u7C7B\u578B:${ctx.projectType}`];
|
|
1679
1685
|
if (ctx.projectName) info.push(`\u540D\u79F0:${ctx.projectName}`);
|
|
@@ -2014,7 +2020,8 @@ var Agent = class {
|
|
|
2014
2020
|
if (reasoningContent) assistantMsg.reasoning_content = reasoningContent;
|
|
2015
2021
|
this.messages.push(assistantMsg);
|
|
2016
2022
|
console.log();
|
|
2017
|
-
|
|
2023
|
+
if (toolCalls.length === 1) {
|
|
2024
|
+
const tc = toolCalls[0];
|
|
2018
2025
|
let args;
|
|
2019
2026
|
try {
|
|
2020
2027
|
args = JSON.parse(tc.function.arguments);
|
|
@@ -2028,6 +2035,26 @@ var Agent = class {
|
|
|
2028
2035
|
tool_call_id: tc.id,
|
|
2029
2036
|
name: tc.function.name
|
|
2030
2037
|
});
|
|
2038
|
+
} else {
|
|
2039
|
+
const tasks = toolCalls.map(async (tc) => {
|
|
2040
|
+
let args;
|
|
2041
|
+
try {
|
|
2042
|
+
args = JSON.parse(tc.function.arguments);
|
|
2043
|
+
} catch {
|
|
2044
|
+
args = {};
|
|
2045
|
+
}
|
|
2046
|
+
const result = await executeTool(tc.function.name, args);
|
|
2047
|
+
return { tc, result };
|
|
2048
|
+
});
|
|
2049
|
+
const results = await Promise.all(tasks);
|
|
2050
|
+
for (const { tc, result } of results) {
|
|
2051
|
+
this.messages.push({
|
|
2052
|
+
role: "tool",
|
|
2053
|
+
content: result.error || result.output,
|
|
2054
|
+
tool_call_id: tc.id,
|
|
2055
|
+
name: tc.function.name
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2031
2058
|
}
|
|
2032
2059
|
}
|
|
2033
2060
|
if (iteration >= MAX_TOOL_ITERATIONS) {
|
|
@@ -2071,6 +2098,30 @@ var Agent = class {
|
|
|
2071
2098
|
printWarning(t("errors.tokenLimit"));
|
|
2072
2099
|
}
|
|
2073
2100
|
}
|
|
2101
|
+
/**
|
|
2102
|
+
* Add file content to conversation context (for /read command)
|
|
2103
|
+
*/
|
|
2104
|
+
addFileContext(filePath, content) {
|
|
2105
|
+
const maxChars = 5e4;
|
|
2106
|
+
const truncated = content.length > maxChars ? content.substring(0, maxChars) + `
|
|
2107
|
+
|
|
2108
|
+
... [\u6587\u4EF6\u88AB\u622A\u65AD\uFF0C\u5171 ${content.length} \u5B57\u7B26\uFF0C\u53EA\u663E\u793A\u524D ${maxChars} \u5B57\u7B26]` : content;
|
|
2109
|
+
this.messages.push({
|
|
2110
|
+
role: "user",
|
|
2111
|
+
content: `[\u7528\u6237\u52A0\u8F7D\u4E86\u6587\u4EF6: ${filePath}]
|
|
2112
|
+
|
|
2113
|
+
\u4EE5\u4E0B\u662F\u6587\u4EF6\u5185\u5BB9\uFF0C\u8BF7\u8BB0\u4F4F\u5B83\uFF0C\u540E\u7EED\u6211\u4F1A\u57FA\u4E8E\u8FD9\u4E2A\u6587\u4EF6\u5185\u5BB9\u7ED9\u4F60\u6307\u4EE4\uFF1A
|
|
2114
|
+
|
|
2115
|
+
---
|
|
2116
|
+
${truncated}
|
|
2117
|
+
---`
|
|
2118
|
+
});
|
|
2119
|
+
this.messages.push({
|
|
2120
|
+
role: "assistant",
|
|
2121
|
+
content: `\u597D\u7684\uFF0C\u6211\u5DF2\u7ECF\u8BFB\u53D6\u4E86 ${filePath} \u7684\u5185\u5BB9\u3002\u8BF7\u544A\u8BC9\u6211\u4F60\u60F3\u57FA\u4E8E\u8FD9\u4E2A\u6587\u4EF6\u505A\u4EC0\u4E48\u3002`
|
|
2122
|
+
});
|
|
2123
|
+
this.trimMessages();
|
|
2124
|
+
}
|
|
2074
2125
|
/**
|
|
2075
2126
|
* Clear conversation history (keep system prompt)
|
|
2076
2127
|
*/
|
|
@@ -2240,6 +2291,25 @@ async function chatCommand(options = {}) {
|
|
|
2240
2291
|
if (projectCtx?.projectMemory) {
|
|
2241
2292
|
console.log(theme.dim(` \u{1F4CB} \u5DF2\u52A0\u8F7D SXAI.md \u9879\u76EE\u6307\u4EE4`));
|
|
2242
2293
|
}
|
|
2294
|
+
try {
|
|
2295
|
+
const docHintPatterns = ["*.md", "\u9700\u6C42*.md", "PRD*", "prd*", "\u8BBE\u8BA1*.md", "docs/*.md"];
|
|
2296
|
+
const foundDocs = [];
|
|
2297
|
+
for (const p of docHintPatterns) {
|
|
2298
|
+
const files = await glob4(p, {
|
|
2299
|
+
cwd: process.cwd(),
|
|
2300
|
+
ignore: ["node_modules/**", ".git/**", "dist/**", "SXAI.md"],
|
|
2301
|
+
nodir: true
|
|
2302
|
+
});
|
|
2303
|
+
for (const f of files) {
|
|
2304
|
+
if (!foundDocs.includes(f) && foundDocs.length < 5) foundDocs.push(f);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
if (foundDocs.length > 0) {
|
|
2308
|
+
console.log(theme.dim(` \u{1F4C4} \u68C0\u6D4B\u5230\u6587\u6863: ${foundDocs.join(", ")}`));
|
|
2309
|
+
console.log(theme.dim(` \u8F93\u5165 /read <\u6587\u4EF6\u540D> \u52A0\u8F7D\u5230\u5BF9\u8BDD\uFF0C\u6216 /docs \u67E5\u770B\u5168\u90E8`));
|
|
2310
|
+
}
|
|
2311
|
+
} catch {
|
|
2312
|
+
}
|
|
2243
2313
|
printSeparator();
|
|
2244
2314
|
console.log();
|
|
2245
2315
|
} catch (err) {
|
|
@@ -2358,15 +2428,18 @@ async function handleSlashCommand(input, agent) {
|
|
|
2358
2428
|
case "/h":
|
|
2359
2429
|
console.log(`
|
|
2360
2430
|
\u53EF\u7528\u547D\u4EE4:
|
|
2361
|
-
/
|
|
2362
|
-
/
|
|
2363
|
-
/
|
|
2364
|
-
/
|
|
2365
|
-
/
|
|
2366
|
-
/
|
|
2367
|
-
/
|
|
2368
|
-
/
|
|
2369
|
-
/
|
|
2431
|
+
/read <\u8DEF\u5F84> \u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9\u5582\u7ED9AI\uFF08\u5982 /read \u9700\u6C42\u6587\u6863.md\uFF09
|
|
2432
|
+
/docs \u626B\u63CF\u9879\u76EE\u4E2D\u7684\u6587\u6863\u6587\u4EF6
|
|
2433
|
+
/model \u67E5\u770B/\u5207\u6362\u6A21\u578B
|
|
2434
|
+
/auto \u5207\u6362\u81EA\u52A8\u6A21\u5F0F\uFF08\u8DF3\u8FC7\u786E\u8BA4\uFF09
|
|
2435
|
+
/clear \u6E05\u9664\u5BF9\u8BDD\u5386\u53F2
|
|
2436
|
+
/account \u67E5\u770B\u8D26\u6237\u4FE1\u606F
|
|
2437
|
+
/status \u67E5\u770B\u9879\u76EE\u4FE1\u606F
|
|
2438
|
+
/config \u67E5\u770B\u914D\u7F6E
|
|
2439
|
+
/logout \u9000\u51FA\u767B\u5F55
|
|
2440
|
+
/exit \u9000\u51FA\u7A0B\u5E8F
|
|
2441
|
+
|
|
2442
|
+
\u63D0\u793A: \u53EF\u4EE5\u76F4\u63A5\u8BF4"\u8BF7\u8BFB\u53D6 xxx \u6587\u4EF6"\u8BA9AI\u81EA\u5DF1\u53BB\u8BFB
|
|
2370
2443
|
`);
|
|
2371
2444
|
break;
|
|
2372
2445
|
case "/auto":
|
|
@@ -2380,6 +2453,82 @@ async function handleSlashCommand(input, agent) {
|
|
|
2380
2453
|
}
|
|
2381
2454
|
break;
|
|
2382
2455
|
}
|
|
2456
|
+
case "/read": {
|
|
2457
|
+
const filePath = args.join(" ").trim();
|
|
2458
|
+
if (!filePath) {
|
|
2459
|
+
printError("\u7528\u6CD5: /read <\u6587\u4EF6\u8DEF\u5F84> \u4F8B\u5982: /read \u9700\u6C42\u6587\u6863.md");
|
|
2460
|
+
break;
|
|
2461
|
+
}
|
|
2462
|
+
try {
|
|
2463
|
+
const absPath = path5.resolve(process.cwd(), filePath);
|
|
2464
|
+
const content = await fs5.readFile(absPath, "utf-8");
|
|
2465
|
+
const lines = content.split("\n").length;
|
|
2466
|
+
const sizeKB = (Buffer.byteLength(content, "utf-8") / 1024).toFixed(1);
|
|
2467
|
+
printSuccess(`\u5DF2\u8BFB\u53D6: ${filePath} (${lines} \u884C, ${sizeKB}KB)`);
|
|
2468
|
+
agent.addFileContext(filePath, content);
|
|
2469
|
+
console.log(theme.dim(` \u6587\u4EF6\u5185\u5BB9\u5DF2\u6DFB\u52A0\u5230\u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF0C\u73B0\u5728\u53EF\u4EE5\u57FA\u4E8E\u5B83\u63D0\u95EE\u6216\u6307\u4EE4\u3002`));
|
|
2470
|
+
console.log(theme.dim(` \u4F8B\u5982: "\u6839\u636E\u8FD9\u4E2A\u9700\u6C42\u6587\u6863\u5F00\u59CB\u5F00\u53D1" \u6216 "\u5206\u6790\u8FD9\u4E2A\u6587\u4EF6\u7684\u95EE\u9898"`));
|
|
2471
|
+
} catch (err) {
|
|
2472
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2473
|
+
if (msg.includes("ENOENT")) {
|
|
2474
|
+
printError(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`);
|
|
2475
|
+
} else {
|
|
2476
|
+
printError(`\u8BFB\u53D6\u5931\u8D25: ${msg}`);
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
break;
|
|
2480
|
+
}
|
|
2481
|
+
case "/docs": {
|
|
2482
|
+
const docPatterns = [
|
|
2483
|
+
"*.md",
|
|
2484
|
+
"*.txt",
|
|
2485
|
+
"docs/**/*.md",
|
|
2486
|
+
"doc/**/*.md",
|
|
2487
|
+
"\u9700\u6C42*.md",
|
|
2488
|
+
"PRD*",
|
|
2489
|
+
"prd*",
|
|
2490
|
+
"\u8BBE\u8BA1*.md",
|
|
2491
|
+
"DESIGN*",
|
|
2492
|
+
"TODO*",
|
|
2493
|
+
"CHANGELOG*",
|
|
2494
|
+
"ARCHITECTURE*"
|
|
2495
|
+
];
|
|
2496
|
+
const docFiles = [];
|
|
2497
|
+
for (const pattern of docPatterns) {
|
|
2498
|
+
try {
|
|
2499
|
+
const files = await glob4(pattern, {
|
|
2500
|
+
cwd: process.cwd(),
|
|
2501
|
+
ignore: ["node_modules/**", ".git/**", "dist/**"],
|
|
2502
|
+
nodir: true
|
|
2503
|
+
});
|
|
2504
|
+
for (const f of files) {
|
|
2505
|
+
if (!docFiles.includes(f)) docFiles.push(f);
|
|
2506
|
+
}
|
|
2507
|
+
} catch {
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
if (docFiles.length === 0) {
|
|
2511
|
+
printInfo("\u672A\u627E\u5230\u6587\u6863\u6587\u4EF6\u3002");
|
|
2512
|
+
} else {
|
|
2513
|
+
console.log(theme.dim(`
|
|
2514
|
+
\u2500\u2500 \u9879\u76EE\u6587\u6863 \u2500\u2500`));
|
|
2515
|
+
for (const f of docFiles.sort().slice(0, 20)) {
|
|
2516
|
+
try {
|
|
2517
|
+
const stat = await fs5.stat(path5.resolve(process.cwd(), f));
|
|
2518
|
+
const sizeKB = (stat.size / 1024).toFixed(1);
|
|
2519
|
+
console.log(` \u{1F4C4} ${theme.info(f)} ${theme.dim(`(${sizeKB}KB)`)}`);
|
|
2520
|
+
} catch {
|
|
2521
|
+
console.log(` \u{1F4C4} ${theme.info(f)}`);
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
if (docFiles.length > 20) {
|
|
2525
|
+
console.log(theme.dim(` ... \u8FD8\u6709 ${docFiles.length - 20} \u4E2A\u6587\u4EF6`));
|
|
2526
|
+
}
|
|
2527
|
+
console.log(theme.dim(`
|
|
2528
|
+
\u4F7F\u7528 /read <\u6587\u4EF6\u540D> \u52A0\u8F7D\u6587\u4EF6\u5230\u5BF9\u8BDD\u4E0A\u4E0B\u6587`));
|
|
2529
|
+
}
|
|
2530
|
+
break;
|
|
2531
|
+
}
|
|
2383
2532
|
case "/clear":
|
|
2384
2533
|
agent.clearHistory();
|
|
2385
2534
|
printSuccess("\u5BF9\u8BDD\u5386\u53F2\u5DF2\u6E05\u9664\u3002");
|
|
@@ -2904,8 +3053,8 @@ async function adminCommand() {
|
|
|
2904
3053
|
break;
|
|
2905
3054
|
}
|
|
2906
3055
|
}
|
|
2907
|
-
async function adminFetch(config3,
|
|
2908
|
-
const response = await fetch(`${config3.apiBaseUrl}${
|
|
3056
|
+
async function adminFetch(config3, path6, options) {
|
|
3057
|
+
const response = await fetch(`${config3.apiBaseUrl}${path6}`, {
|
|
2909
3058
|
...options,
|
|
2910
3059
|
headers: {
|
|
2911
3060
|
"Content-Type": "application/json",
|
|
@@ -3175,7 +3324,7 @@ function fmtTokens3(n) {
|
|
|
3175
3324
|
var program = new Command();
|
|
3176
3325
|
var config2 = getConfig();
|
|
3177
3326
|
setLocale(config2.locale);
|
|
3178
|
-
program.name("sxai").description(t("description")).version("0.
|
|
3327
|
+
program.name("sxai").description(t("description")).version("0.5.1");
|
|
3179
3328
|
program.command("chat", { isDefault: true }).description("\u542F\u52A8\u4EA4\u4E92\u5F0FAI\u5BF9\u8BDD\uFF08\u9ED8\u8BA4\u547D\u4EE4\uFF09").option("-m, --model <model>", "\u6307\u5B9AAI\u6A21\u578B", config2.model).option("-y, --yolo", "\u81EA\u52A8\u6A21\u5F0F\uFF1A\u8DF3\u8FC7\u6240\u6709\u786E\u8BA4\uFF0C\u8BA9AI\u81EA\u4E3B\u6267\u884C").action(async (options) => {
|
|
3180
3329
|
if (options.model && options.model !== config2.model) {
|
|
3181
3330
|
setConfig("model", options.model);
|