@gird/ccli 2.2.0 → 2.3.0
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/README.md +6 -3
- package/dist/cli/args.js +5 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/repl.js +3 -81
- package/dist/cli/repl.js.map +1 -1
- package/dist/cli/run.js +106 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/index.js +56 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,9 +17,12 @@ npm install -g @gird/ccli
|
|
|
17
17
|
## 用法
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
ccli
|
|
21
|
-
ccli --version
|
|
22
|
-
ccli --help
|
|
20
|
+
ccli # 进入交互式对话
|
|
21
|
+
ccli --version # 打印版本号
|
|
22
|
+
ccli --help # 打印帮助
|
|
23
|
+
ccli "用一句话介绍 CCLI" # 非交互式单次调用
|
|
24
|
+
cat file.ts | ccli "给这段代码加注释" # 从 stdin 读取内容作为上下文
|
|
25
|
+
ccli --yes "在当前目录创建 test.txt" # 自动确认破坏性工具调用
|
|
23
26
|
```
|
|
24
27
|
|
|
25
28
|
进入对话后:
|
package/dist/cli/args.js
CHANGED
|
@@ -8,6 +8,7 @@ export function parseArgs(argv) {
|
|
|
8
8
|
const result = {
|
|
9
9
|
version: false,
|
|
10
10
|
help: false,
|
|
11
|
+
yes: false,
|
|
11
12
|
unknown: [],
|
|
12
13
|
positionals: [],
|
|
13
14
|
};
|
|
@@ -21,6 +22,10 @@ export function parseArgs(argv) {
|
|
|
21
22
|
case '--help':
|
|
22
23
|
result.help = true;
|
|
23
24
|
break;
|
|
25
|
+
case '-y':
|
|
26
|
+
case '--yes':
|
|
27
|
+
result.yes = true;
|
|
28
|
+
break;
|
|
24
29
|
default:
|
|
25
30
|
if (arg.startsWith('-')) {
|
|
26
31
|
result.unknown.push(arg);
|
package/dist/cli/args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,MAAM,UAAU,SAAS,CAAC,IAAc;IACpC,MAAM,MAAM,GAAe;QACvB,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,KAAK;QACX,GAAG,EAAE,KAAK;QACV,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,EAAE;KAClB,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACrB,QAAQ,GAAG,EAAE,CAAC;YACV,KAAK,IAAI,CAAC;YACV,KAAK,WAAW;gBACZ,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;gBACtB,MAAM;YACV,KAAK,IAAI,CAAC;YACV,KAAK,QAAQ;gBACT,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;gBACnB,MAAM;YACV,KAAK,IAAI,CAAC;YACV,KAAK,OAAO;gBACR,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC;gBAClB,MAAM;YACV;gBACI,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBACtB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC7B,CAAC;qBAAM,CAAC;oBACJ,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACjC,CAAC;QACT,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC"}
|
package/dist/cli/repl.js
CHANGED
|
@@ -10,11 +10,11 @@ import { NAME, VERSION } from '../version.js';
|
|
|
10
10
|
import { loadConfig, maskKey } from '../config/config.js';
|
|
11
11
|
import { createAIClient } from '../agent/ai.js';
|
|
12
12
|
import { handleSlash, slashCompleter } from './slash.js';
|
|
13
|
-
import { executeTool, getTool, } from '../tools/index.js';
|
|
14
13
|
import { hasLastSession, loadLastSession, saveLastSession, clearLastSession, } from './session.js';
|
|
15
14
|
import { appendHistory, loadHistory } from './history.js';
|
|
16
15
|
import { InputBuffer } from './input.js';
|
|
17
|
-
import {
|
|
16
|
+
import { emptyStats, formatStats, } from './stats.js';
|
|
17
|
+
import { runAgentTurn } from './run.js';
|
|
18
18
|
const PROMPT = '› ';
|
|
19
19
|
const SYSTEM_PROMPT = '你是 CCLI,一个运行在终端里的 AI 助手。回答简洁、准确,使用中文。' +
|
|
20
20
|
'你可以调用工具读文件、列目录、搜索(read_file / list_dir / grep),' +
|
|
@@ -28,84 +28,6 @@ function question(rl, prompt) {
|
|
|
28
28
|
rl.question(prompt, (ans) => resolve(ans));
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
|
-
/**
|
|
32
|
-
* 解析工具参数用于预览;失败则返回空对象(真正执行时 executeTool 会再报错)。
|
|
33
|
-
*/
|
|
34
|
-
function safeParse(argsJson) {
|
|
35
|
-
try {
|
|
36
|
-
return JSON.parse(argsJson || '{}');
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
return {};
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* 驱动一个用户回合:请求模型 → 若它要调用工具则(必要时确认后)执行并回传 → 再请求 …
|
|
44
|
-
* 直到模型不再调用工具(给出最终回答)或达到轮数上限。
|
|
45
|
-
*/
|
|
46
|
-
async function runAgentTurn(ai, history, confirm, stats) {
|
|
47
|
-
for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
|
|
48
|
-
const turn = await ai.chat(history, (delta) => process.stdout.write(delta));
|
|
49
|
-
// 优先使用后端返回的真实 usage;不支持时降级为字符估算
|
|
50
|
-
if (turn.usage) {
|
|
51
|
-
stats.promptTokens += turn.usage.promptTokens;
|
|
52
|
-
stats.completionTokens += turn.usage.completionTokens;
|
|
53
|
-
stats.totalTokens += turn.usage.totalTokens;
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
const promptTokens = estimatePromptTokens(history);
|
|
57
|
-
const completionTokens = estimateTokens(turn.content);
|
|
58
|
-
stats.promptTokens += promptTokens;
|
|
59
|
-
stats.completionTokens += completionTokens;
|
|
60
|
-
stats.totalTokens += promptTokens + completionTokens;
|
|
61
|
-
}
|
|
62
|
-
// 没有工具调用 → 这是最终回答,收尾退出
|
|
63
|
-
if (turn.toolCalls.length === 0) {
|
|
64
|
-
process.stdout.write('\n');
|
|
65
|
-
history.push({ role: 'assistant', content: turn.content });
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
// 有工具调用:先把带 tool_calls 的 assistant 消息入历史
|
|
69
|
-
history.push({
|
|
70
|
-
role: 'assistant',
|
|
71
|
-
content: turn.content || null,
|
|
72
|
-
tool_calls: turn.toolCalls.map((tc) => ({
|
|
73
|
-
id: tc.id,
|
|
74
|
-
type: 'function',
|
|
75
|
-
function: { name: tc.name, arguments: tc.arguments },
|
|
76
|
-
})),
|
|
77
|
-
});
|
|
78
|
-
// 逐个执行工具,结果以 role:'tool' 消息回传
|
|
79
|
-
for (const tc of turn.toolCalls) {
|
|
80
|
-
const tool = getTool(tc.name);
|
|
81
|
-
// 破坏性工具:执行前展示预览并请用户确认
|
|
82
|
-
if (tool?.needsConfirm) {
|
|
83
|
-
const preview = tool.preview
|
|
84
|
-
? tool.preview(safeParse(tc.arguments))
|
|
85
|
-
: `${tc.name}(${tc.arguments})`;
|
|
86
|
-
const approved = await confirm(preview);
|
|
87
|
-
if (!approved) {
|
|
88
|
-
console.log('已取消。');
|
|
89
|
-
history.push({
|
|
90
|
-
role: 'tool',
|
|
91
|
-
tool_call_id: tc.id,
|
|
92
|
-
content: '用户拒绝执行此操作。请勿重试,可询问用户希望如何继续。',
|
|
93
|
-
});
|
|
94
|
-
continue; // 不执行,继续下一个工具调用
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
console.log(`\n⚙ ${tc.name}(${tc.arguments})`);
|
|
98
|
-
const result = await executeTool(tc.name, tc.arguments);
|
|
99
|
-
history.push({
|
|
100
|
-
role: 'tool',
|
|
101
|
-
tool_call_id: tc.id,
|
|
102
|
-
content: result,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
// 继续下一轮,让模型基于工具结果接着回答
|
|
106
|
-
}
|
|
107
|
-
console.log('\n(已达到工具调用轮数上限)');
|
|
108
|
-
}
|
|
109
31
|
export async function startRepl() {
|
|
110
32
|
// 启动即解析配置:缺失就明确报错退出,不进入循环
|
|
111
33
|
const result = loadConfig();
|
|
@@ -187,7 +109,7 @@ export async function startRepl() {
|
|
|
187
109
|
const mark = history.length; // 出错时回滚到这里,避免半截上下文
|
|
188
110
|
history.push({ role: 'user', content: input });
|
|
189
111
|
try {
|
|
190
|
-
await runAgentTurn(ai, history, confirm, stats);
|
|
112
|
+
await runAgentTurn({ ai, history, confirm, stats });
|
|
191
113
|
// 本回合结束,显示本次 token 消耗
|
|
192
114
|
console.log(`\n${formatStats(stats, ai.getModel())}`);
|
|
193
115
|
}
|
package/dist/cli/repl.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repl.js","sourceRoot":"","sources":["../../src/cli/repl.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"repl.js","sourceRoot":"","sources":["../../src/cli/repl.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/run.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { executeTool, getTool } from '../tools/index.js';
|
|
2
|
+
import { estimatePromptTokens, estimateTokens, } from './stats.js';
|
|
3
|
+
/** 单个用户回合内,最多允许模型连续调用工具的轮数,防止失控循环。 */
|
|
4
|
+
const MAX_TOOL_ROUNDS = 10;
|
|
5
|
+
/**
|
|
6
|
+
* 解析工具参数用于预览;失败则返回空对象(真正执行时 executeTool 会再报错)。
|
|
7
|
+
*/
|
|
8
|
+
function safeParse(argsJson) {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(argsJson || '{}');
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 驱动一个用户回合:请求模型 → 若它要调用工具则(必要时确认后)执行并回传 → 再请求 …
|
|
18
|
+
* 直到模型不再调用工具(给出最终回答)或达到轮数上限。
|
|
19
|
+
*/
|
|
20
|
+
export async function runAgentTurn(options) {
|
|
21
|
+
const { ai, history, confirm, stats, print = process.stdout.write.bind(process.stdout) } = options;
|
|
22
|
+
for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
|
|
23
|
+
const turn = await ai.chat(history, (delta) => print(delta));
|
|
24
|
+
// 优先使用后端返回的真实 usage;不支持时降级为字符估算
|
|
25
|
+
if (turn.usage) {
|
|
26
|
+
stats.promptTokens += turn.usage.promptTokens;
|
|
27
|
+
stats.completionTokens += turn.usage.completionTokens;
|
|
28
|
+
stats.totalTokens += turn.usage.totalTokens;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const promptTokens = estimatePromptTokens(history);
|
|
32
|
+
const completionTokens = estimateTokens(turn.content);
|
|
33
|
+
stats.promptTokens += promptTokens;
|
|
34
|
+
stats.completionTokens += completionTokens;
|
|
35
|
+
stats.totalTokens += promptTokens + completionTokens;
|
|
36
|
+
}
|
|
37
|
+
// 没有工具调用 → 这是最终回答,收尾退出
|
|
38
|
+
if (turn.toolCalls.length === 0) {
|
|
39
|
+
print('\n');
|
|
40
|
+
history.push({ role: 'assistant', content: turn.content });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// 有工具调用:先把带 tool_calls 的 assistant 消息入历史
|
|
44
|
+
history.push({
|
|
45
|
+
role: 'assistant',
|
|
46
|
+
content: turn.content || null,
|
|
47
|
+
tool_calls: turn.toolCalls.map((tc) => ({
|
|
48
|
+
id: tc.id,
|
|
49
|
+
type: 'function',
|
|
50
|
+
function: { name: tc.name, arguments: tc.arguments },
|
|
51
|
+
})),
|
|
52
|
+
});
|
|
53
|
+
// 逐个执行工具,结果以 role:'tool' 消息回传
|
|
54
|
+
for (const tc of turn.toolCalls) {
|
|
55
|
+
const tool = getTool(tc.name);
|
|
56
|
+
// 破坏性工具:执行前展示预览并请用户确认
|
|
57
|
+
if (tool?.needsConfirm) {
|
|
58
|
+
const preview = tool.preview
|
|
59
|
+
? tool.preview(safeParse(tc.arguments))
|
|
60
|
+
: `${tc.name}(${tc.arguments})`;
|
|
61
|
+
const approved = await confirm(preview);
|
|
62
|
+
if (!approved) {
|
|
63
|
+
print('\n已取消。\n');
|
|
64
|
+
history.push({
|
|
65
|
+
role: 'tool',
|
|
66
|
+
tool_call_id: tc.id,
|
|
67
|
+
content: '用户拒绝执行此操作。请勿重试,可询问用户希望如何继续。',
|
|
68
|
+
});
|
|
69
|
+
continue; // 不执行,继续下一个工具调用
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
print(`\n⚙ ${tc.name}(${tc.arguments})\n`);
|
|
73
|
+
const result = await executeTool(tc.name, tc.arguments);
|
|
74
|
+
history.push({
|
|
75
|
+
role: 'tool',
|
|
76
|
+
tool_call_id: tc.id,
|
|
77
|
+
content: result,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// 继续下一轮,让模型基于工具结果接着回答
|
|
81
|
+
}
|
|
82
|
+
print('\n(已达到工具调用轮数上限)\n');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 非交互式单次调用。
|
|
86
|
+
* 以 systemPrompt 开场,把 prompt 作为 user 消息发送,执行一轮后返回。
|
|
87
|
+
*/
|
|
88
|
+
export async function runOnce(options) {
|
|
89
|
+
const { ai, prompt, confirm, print = process.stdout.write.bind(process.stdout), systemPrompt = '你是 CCLI,一个运行在终端里的 AI 助手。回答简洁、准确,使用中文。', } = options;
|
|
90
|
+
const history = [{ role: 'system', content: systemPrompt }];
|
|
91
|
+
history.push({ role: 'user', content: prompt });
|
|
92
|
+
let output = '';
|
|
93
|
+
const capturePrint = (chunk) => {
|
|
94
|
+
output += chunk;
|
|
95
|
+
print(chunk);
|
|
96
|
+
};
|
|
97
|
+
await runAgentTurn({
|
|
98
|
+
ai,
|
|
99
|
+
history,
|
|
100
|
+
confirm,
|
|
101
|
+
stats: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
102
|
+
print: capturePrint,
|
|
103
|
+
});
|
|
104
|
+
return output;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,16 +2,21 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* CCLI 入口。
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* ccli --version / -v
|
|
7
|
-
* ccli --help / -h
|
|
8
|
-
* ccli
|
|
9
|
-
* ccli
|
|
5
|
+
* 支持三种模式:
|
|
6
|
+
* ccli --version / -v 打印版本号
|
|
7
|
+
* ccli --help / -h 打印本帮助
|
|
8
|
+
* ccli config 打印当前解析出的配置(诊断用,密钥打码)
|
|
9
|
+
* ccli "prompt" 非交互式单次调用
|
|
10
|
+
* echo xxx | ccli "prompt" 从 stdin 读取内容作为上下文
|
|
11
|
+
* ccli --yes "prompt" 非交互式下自动确认破坏性工具
|
|
12
|
+
* ccli 启动交互式会话
|
|
10
13
|
*/
|
|
11
14
|
import { parseArgs } from './cli/args.js';
|
|
12
15
|
import { NAME, VERSION } from './version.js';
|
|
13
16
|
import { startRepl } from './cli/repl.js';
|
|
14
|
-
import { printConfig } from './config/config.js';
|
|
17
|
+
import { printConfig, loadConfig } from './config/config.js';
|
|
18
|
+
import { createAIClient } from './agent/ai.js';
|
|
19
|
+
import { runOnce } from './cli/run.js';
|
|
15
20
|
const HELP = `${NAME} v${VERSION}
|
|
16
21
|
|
|
17
22
|
用法:
|
|
@@ -23,12 +28,34 @@ const HELP = `${NAME} v${VERSION}
|
|
|
23
28
|
选项:
|
|
24
29
|
-v, --version 打印版本号
|
|
25
30
|
-h, --help 打印本帮助
|
|
31
|
+
-y, --yes 非交互式模式下自动确认破坏性工具调用
|
|
26
32
|
|
|
27
33
|
示例:
|
|
28
|
-
ccli
|
|
29
|
-
ccli config
|
|
30
|
-
ccli --version
|
|
34
|
+
ccli 启动交互式对话
|
|
35
|
+
ccli config 查看当前生效的后端 / 模型 / 密钥来源
|
|
36
|
+
ccli --version 查看版本
|
|
37
|
+
ccli "解释这段报错" 非交互式单次调用
|
|
38
|
+
cat file.ts | ccli "给这段代码加注释" 从 stdin 读取上下文
|
|
39
|
+
ccli --yes "在当前目录创建 test.txt" 自动确认工具调用
|
|
31
40
|
`;
|
|
41
|
+
/** 从 stdin 读取全部内容;若 stdin 是 TTY 则返回空字符串。 */
|
|
42
|
+
async function readStdin() {
|
|
43
|
+
if (process.stdin.isTTY) {
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
const chunks = [];
|
|
47
|
+
for await (const chunk of process.stdin) {
|
|
48
|
+
chunks.push(chunk);
|
|
49
|
+
}
|
|
50
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
51
|
+
}
|
|
52
|
+
/** 把命令行 prompt 和 stdin 内容合并成最终 prompt。 */
|
|
53
|
+
function buildPrompt(promptFromArgs, stdinText) {
|
|
54
|
+
if (promptFromArgs && stdinText) {
|
|
55
|
+
return `${promptFromArgs}\n\n---\n${stdinText}`;
|
|
56
|
+
}
|
|
57
|
+
return promptFromArgs || stdinText;
|
|
58
|
+
}
|
|
32
59
|
async function main() {
|
|
33
60
|
// argv 前两个是 node 路径和脚本路径,从第 3 个起才是用户输入
|
|
34
61
|
const args = parseArgs(process.argv.slice(2));
|
|
@@ -50,10 +77,26 @@ async function main() {
|
|
|
50
77
|
if (command === 'config') {
|
|
51
78
|
process.exit(printConfig());
|
|
52
79
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
80
|
+
const promptFromArgs = args.positionals.join(' ').trim();
|
|
81
|
+
const stdinText = (await readStdin()).trim();
|
|
82
|
+
const prompt = buildPrompt(promptFromArgs, stdinText);
|
|
83
|
+
// 有 prompt 或 stdin 时,走非交互式单次调用
|
|
84
|
+
if (prompt) {
|
|
85
|
+
const result = loadConfig();
|
|
86
|
+
if (!result.ok) {
|
|
87
|
+
console.error(result.message);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
const { config } = result;
|
|
91
|
+
const ai = createAIClient(config);
|
|
92
|
+
const confirm = args.yes
|
|
93
|
+
? () => Promise.resolve(true)
|
|
94
|
+
: async (preview) => {
|
|
95
|
+
console.error(`\n[非交互式模式] 需要确认以下操作,请添加 --yes 自动批准:\n${preview}`);
|
|
96
|
+
return false;
|
|
97
|
+
};
|
|
98
|
+
await runOnce({ ai, prompt, confirm });
|
|
99
|
+
return;
|
|
57
100
|
}
|
|
58
101
|
// 无参数:进入交互式会话循环。
|
|
59
102
|
await startRepl();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|