@gird/ccli 1.0.1 → 2.0.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 +0 -5
- package/dist/args.js +35 -0
- package/dist/args.js.map +1 -0
- package/dist/config.js +120 -0
- package/dist/config.js.map +1 -0
- package/dist/env.js +39 -0
- package/dist/env.js.map +1 -0
- package/dist/index.js +36 -226
- package/dist/index.js.map +1 -0
- package/dist/repl.js +59 -0
- package/dist/repl.js.map +1 -0
- package/dist/version.js +13 -0
- package/dist/version.js.map +1 -0
- package/package.json +4 -14
- package/.env.example +0 -23
- package/CCLI.md +0 -15
- package/dist/agent/client-openai.js +0 -166
- package/dist/agent/client.js +0 -63
- package/dist/agent/factory.js +0 -39
- package/dist/agent/loop.js +0 -163
- package/dist/agent/pricing.js +0 -28
- package/dist/agent/prompt.js +0 -43
- package/dist/agent/provider.js +0 -18
- package/dist/cli/headless.js +0 -50
- package/dist/cli/mentions.js +0 -60
- package/dist/cli/session.js +0 -432
- package/dist/commands/custom.js +0 -46
- package/dist/commands/slash.js +0 -148
- package/dist/config/config.js +0 -128
- package/dist/config/dotenv.js +0 -59
- package/dist/context/project.js +0 -155
- package/dist/hooks/hooks.js +0 -82
- package/dist/permissions/permissions.js +0 -87
- package/dist/session/store.js +0 -81
- package/dist/tools/bash.js +0 -97
- package/dist/tools/filesystem.js +0 -215
- package/dist/tools/index.js +0 -45
- package/dist/tools/search.js +0 -0
- package/dist/tools/task.js +0 -35
- package/dist/tools/todo.js +0 -68
- package/dist/tools/types.js +0 -8
- package/dist/tools/web.js +0 -108
- package/dist/ui/diff.js +0 -105
- package/dist/ui/markdown.js +0 -137
- package/dist/ui/spinner.js +0 -64
- package/dist/ui/theme.js +0 -106
package/README.md
CHANGED
|
@@ -100,11 +100,6 @@ ccli -h, --help 查看帮助
|
|
|
100
100
|
| `todo_write` | 协调 | 维护多步任务清单并实时显示进度 |
|
|
101
101
|
| `task` | 协调 | 派生只读子代理执行聚焦调研 |
|
|
102
102
|
|
|
103
|
-
## 文档
|
|
104
|
-
|
|
105
|
-
- [使用指南](https://github.com/GridGit/ai-cli/blob/main/docs/使用指南.md) —— 运行方式、交互、斜杠命令、典型工作流、常见问题、发布流程
|
|
106
|
-
- [架构设计](https://github.com/GridGit/ai-cli/blob/main/docs/架构设计.md) —— 设计理念、分层架构、代理循环数据流、扩展指南
|
|
107
|
-
|
|
108
103
|
## 本地开发
|
|
109
104
|
|
|
110
105
|
```bash
|
package/dist/args.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 极简命令行参数解析。
|
|
3
|
+
*
|
|
4
|
+
* 现在只需要认识几个标志(flag),所以手写解析即可,不引第三方库。
|
|
5
|
+
* 把「原始 argv」翻译成一个结构化的 ParsedArgs,交给 index.ts 决定做什么。
|
|
6
|
+
*/
|
|
7
|
+
export function parseArgs(argv) {
|
|
8
|
+
const result = {
|
|
9
|
+
version: false,
|
|
10
|
+
help: false,
|
|
11
|
+
unknown: [],
|
|
12
|
+
positionals: [],
|
|
13
|
+
};
|
|
14
|
+
for (const arg of argv) {
|
|
15
|
+
switch (arg) {
|
|
16
|
+
case '-v':
|
|
17
|
+
case '--version':
|
|
18
|
+
result.version = true;
|
|
19
|
+
break;
|
|
20
|
+
case '-h':
|
|
21
|
+
case '--help':
|
|
22
|
+
result.help = true;
|
|
23
|
+
break;
|
|
24
|
+
default:
|
|
25
|
+
if (arg.startsWith('-')) {
|
|
26
|
+
result.unknown.push(arg);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
result.positionals.push(arg);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=args.js.map
|
package/dist/args.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,MAAM,UAAU,SAAS,CAAC,IAAc;IACpC,MAAM,MAAM,GAAe;QACvB,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,KAAK;QACX,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;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/config.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置与密钥解析。
|
|
3
|
+
*
|
|
4
|
+
* 优先级(高覆盖低):
|
|
5
|
+
* 环境变量 > 项目 .env 文件 > 全局 ~/.ccli/config.json
|
|
6
|
+
*
|
|
7
|
+
* 支持的后端:
|
|
8
|
+
* - Anthropic (Claude) —— ANTHROPIC_API_KEY [+ ANTHROPIC_BASE_URL / ANTHROPIC_MODEL]
|
|
9
|
+
* - OpenAI 兼容(通义千问等) —— QWEN_API_KEY / OPENAI_API_KEY [+ *_BASE_URL / *_MODEL]
|
|
10
|
+
*
|
|
11
|
+
* 本步只负责「读取并解析成一个清晰的 Config」,不发起任何网络请求。
|
|
12
|
+
*/
|
|
13
|
+
import { homedir } from 'node:os';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
import { loadEnvFile } from './env.js';
|
|
17
|
+
/** 各后端在未显式指定 model 时的兜底默认值。 */
|
|
18
|
+
const DEFAULT_MODEL = {
|
|
19
|
+
anthropic: 'claude-sonnet-4-5',
|
|
20
|
+
openai: 'qwen-plus',
|
|
21
|
+
};
|
|
22
|
+
const GLOBAL_CONFIG_PATH = join(homedir(), '.ccli', 'config.json');
|
|
23
|
+
/** 读取全局 ~/.ccli/config.json(优先级最低);不存在或损坏则返回空。 */
|
|
24
|
+
function loadGlobalConfig() {
|
|
25
|
+
try {
|
|
26
|
+
const raw = JSON.parse(readFileSync(GLOBAL_CONFIG_PATH, 'utf8'));
|
|
27
|
+
return {
|
|
28
|
+
provider: raw.provider,
|
|
29
|
+
apiKey: raw.apiKey,
|
|
30
|
+
baseURL: raw.baseURL,
|
|
31
|
+
model: raw.model,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 解析出最终生效的配置。
|
|
40
|
+
* @param cwd 项目根目录,用于定位 .env
|
|
41
|
+
*/
|
|
42
|
+
export function loadConfig(cwd = process.cwd()) {
|
|
43
|
+
const dotenv = loadEnvFile(join(cwd, '.env'));
|
|
44
|
+
const global = loadGlobalConfig();
|
|
45
|
+
// 按优先级取一个环境风格的键:环境变量 > .env
|
|
46
|
+
const get = (key) => process.env[key] ?? dotenv[key];
|
|
47
|
+
// —— 后端识别(按 Key 出现顺序)——
|
|
48
|
+
// 1) Anthropic
|
|
49
|
+
const anthropicKey = get('ANTHROPIC_API_KEY');
|
|
50
|
+
if (anthropicKey) {
|
|
51
|
+
return {
|
|
52
|
+
ok: true,
|
|
53
|
+
config: {
|
|
54
|
+
provider: 'anthropic',
|
|
55
|
+
apiKey: anthropicKey,
|
|
56
|
+
baseURL: get('ANTHROPIC_BASE_URL'),
|
|
57
|
+
model: get('ANTHROPIC_MODEL') ??
|
|
58
|
+
get('CCLI_MODEL') ??
|
|
59
|
+
DEFAULT_MODEL.anthropic,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// 2) 通义千问 Qwen(OpenAI 兼容的便捷前缀)
|
|
64
|
+
const qwenKey = get('QWEN_API_KEY');
|
|
65
|
+
if (qwenKey) {
|
|
66
|
+
return {
|
|
67
|
+
ok: true,
|
|
68
|
+
config: {
|
|
69
|
+
provider: 'openai',
|
|
70
|
+
apiKey: qwenKey,
|
|
71
|
+
baseURL: get('QWEN_BASE_URL'),
|
|
72
|
+
model: get('QWEN_MODEL') ?? DEFAULT_MODEL.openai,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// 3) 通用 OpenAI 兼容后端(DeepSeek / GLM / 自建)
|
|
77
|
+
const openaiKey = get('OPENAI_API_KEY');
|
|
78
|
+
if (openaiKey) {
|
|
79
|
+
return {
|
|
80
|
+
ok: true,
|
|
81
|
+
config: {
|
|
82
|
+
provider: 'openai',
|
|
83
|
+
apiKey: openaiKey,
|
|
84
|
+
baseURL: get('OPENAI_BASE_URL'),
|
|
85
|
+
model: get('OPENAI_MODEL') ??
|
|
86
|
+
get('CCLI_MODEL') ??
|
|
87
|
+
DEFAULT_MODEL.openai,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// 4) 回退到全局配置
|
|
92
|
+
if (global.apiKey && global.provider) {
|
|
93
|
+
return {
|
|
94
|
+
ok: true,
|
|
95
|
+
config: {
|
|
96
|
+
provider: global.provider,
|
|
97
|
+
apiKey: global.apiKey,
|
|
98
|
+
baseURL: global.baseURL,
|
|
99
|
+
model: global.model ?? DEFAULT_MODEL[global.provider],
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// 都没有 —— 给出可操作的提示,而不是抛异常
|
|
104
|
+
return {
|
|
105
|
+
ok: false,
|
|
106
|
+
message: [
|
|
107
|
+
'未找到可用的 API 配置。请任选其一:',
|
|
108
|
+
' • 设置环境变量 ANTHROPIC_API_KEY=sk-ant-...',
|
|
109
|
+
' • 或在项目 .env 写入 QWEN_API_KEY / QWEN_BASE_URL / QWEN_MODEL',
|
|
110
|
+
' • 或在 ~/.ccli/config.json 配置 { provider, apiKey, baseURL, model }',
|
|
111
|
+
].join('\n'),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** 打码显示密钥,用于安全地回显「用了哪个 key」。 */
|
|
115
|
+
export function maskKey(key) {
|
|
116
|
+
if (key.length <= 8)
|
|
117
|
+
return '****';
|
|
118
|
+
return `${key.slice(0, 4)}…${key.slice(-4)}`;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/env.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 极简 .env 文件解析器(零依赖,不引 dotenv)。
|
|
3
|
+
*
|
|
4
|
+
* 只做最常见的事:逐行读 KEY=VALUE,忽略空行和 # 注释,去掉值两侧引号。
|
|
5
|
+
* 解析结果作为一个普通对象返回,由 config.ts 决定它和真实环境变量谁优先。
|
|
6
|
+
* 特意「不写回 process.env」,避免隐式副作用——优先级由调用方显式控制。
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
9
|
+
export function parseEnv(text) {
|
|
10
|
+
const result = {};
|
|
11
|
+
for (const raw of text.split('\n')) {
|
|
12
|
+
const line = raw.trim();
|
|
13
|
+
if (line.length === 0 || line.startsWith('#'))
|
|
14
|
+
continue;
|
|
15
|
+
const eq = line.indexOf('=');
|
|
16
|
+
if (eq === -1)
|
|
17
|
+
continue; // 没有 = 的行跳过
|
|
18
|
+
const key = line.slice(0, eq).trim();
|
|
19
|
+
let value = line.slice(eq + 1).trim();
|
|
20
|
+
// 去掉成对的首尾引号
|
|
21
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
22
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
23
|
+
value = value.slice(1, -1);
|
|
24
|
+
}
|
|
25
|
+
if (key)
|
|
26
|
+
result[key] = value;
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
/** 读取并解析一个 .env 文件;文件不存在则返回空对象。 */
|
|
31
|
+
export function loadEnvFile(path) {
|
|
32
|
+
try {
|
|
33
|
+
return parseEnv(readFileSync(path, 'utf8'));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return {}; // 文件不存在等情况:静默当作没有配置
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=env.js.map
|
package/dist/env.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,UAAU,QAAQ,CAAC,IAAY;IACjC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAE1C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAExD,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,SAAS,CAAC,YAAY;QAErC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAEtC,YAAY;QACZ,IACI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAChD,CAAC;YACC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/B,CAAC;QAED,IAAI,GAAG;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACjC,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,WAAW,CAAC,IAAY;IACpC,IAAI,CAAC;QACD,OAAO,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC,CAAC,oBAAoB;IACnC,CAAC;AACL,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,237 +1,47 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* CCLI
|
|
3
|
+
* CCLI 入口。
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* ccli
|
|
7
|
-
* ccli -
|
|
8
|
-
* ccli
|
|
9
|
-
* ccli
|
|
10
|
-
* ccli --version / --help
|
|
5
|
+
* 第二步:解析命令行参数。
|
|
6
|
+
* ccli --version / -v 打印版本号
|
|
7
|
+
* ccli --help / -h 打印帮助
|
|
8
|
+
* ccli (暂时)打印占位问候,后续接入交互式会话
|
|
9
|
+
* ccli --xxx 未知标志 → 报错并给出提示,退出码 1
|
|
11
10
|
*/
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
break;
|
|
31
|
-
case '--model':
|
|
32
|
-
case '-m':
|
|
33
|
-
args.model = argv[++i];
|
|
34
|
-
break;
|
|
35
|
-
case '--cwd':
|
|
36
|
-
args.cwd = argv[++i];
|
|
37
|
-
break;
|
|
38
|
-
case '--dangerously-skip-permissions':
|
|
39
|
-
case '--yes':
|
|
40
|
-
case '-y':
|
|
41
|
-
args.autoApprove = true;
|
|
42
|
-
break;
|
|
43
|
-
case '--continue':
|
|
44
|
-
case '-c':
|
|
45
|
-
args.continueSession = true;
|
|
46
|
-
break;
|
|
47
|
-
case '--resume':
|
|
48
|
-
case '-r': {
|
|
49
|
-
args.resume = true;
|
|
50
|
-
const next = argv[i + 1];
|
|
51
|
-
if (next && !next.startsWith('-')) {
|
|
52
|
-
args.resumeId = next;
|
|
53
|
-
i++;
|
|
54
|
-
}
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
case '--version':
|
|
58
|
-
case '-v':
|
|
59
|
-
args.command = 'version';
|
|
60
|
-
break;
|
|
61
|
-
case '--help':
|
|
62
|
-
case '-h':
|
|
63
|
-
args.command = 'help';
|
|
64
|
-
break;
|
|
65
|
-
case 'config':
|
|
66
|
-
args.command = 'config';
|
|
67
|
-
break;
|
|
68
|
-
default:
|
|
69
|
-
// 把裸参数当作 prompt
|
|
70
|
-
if (!a.startsWith('-') && !args.prompt) {
|
|
71
|
-
args.prompt = argv.slice(i).join(' ');
|
|
72
|
-
i = argv.length;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return args;
|
|
77
|
-
}
|
|
78
|
-
function printHelp() {
|
|
79
|
-
const lines = [
|
|
80
|
-
brand.primary(c.bold('CCLI')) + c.dim(` v${VERSION} —— AI 软件工程师(Claude Code 复刻)`),
|
|
81
|
-
'',
|
|
82
|
-
c.bold('用法'),
|
|
83
|
-
` ${brand.accent('ccli')} 进入交互式会话`,
|
|
84
|
-
` ${brand.accent('ccli "重构 utils 模块"')} 直接给出目标并进入会话`,
|
|
85
|
-
` ${brand.accent('ccli -p "..."')} 一次性模式(输出后退出,适合脚本/管道)`,
|
|
86
|
-
` ${brand.accent('ccli config')} 配置 API Key`,
|
|
87
|
-
'',
|
|
88
|
-
c.bold('选项'),
|
|
89
|
-
` ${c.cyan('-p, --print <文本>')} 非交互模式`,
|
|
90
|
-
` ${c.cyan('-m, --model <id>')} 指定模型(默认 claude-opus-4-8)`,
|
|
91
|
-
` ${c.cyan('-y, --yes')} 自动批准所有工具调用`,
|
|
92
|
-
` ${c.cyan('-c, --continue')} 恢复本目录最近一次会话`,
|
|
93
|
-
` ${c.cyan('-r, --resume [id]')} 列出/恢复历史会话`,
|
|
94
|
-
` ${c.cyan('--cwd <目录>')} 指定工作目录`,
|
|
95
|
-
` ${c.cyan('-v, --version')} 版本号`,
|
|
96
|
-
` ${c.cyan('-h, --help')} 帮助`,
|
|
97
|
-
'',
|
|
98
|
-
c.bold('环境变量'),
|
|
99
|
-
` ${c.cyan('ANTHROPIC_API_KEY')} Anthropic API 密钥`,
|
|
100
|
-
` ${c.cyan('ANTHROPIC_BASE_URL')} 自定义 API 端点(可选)`,
|
|
101
|
-
];
|
|
102
|
-
stdout.write(box(lines) + '\n');
|
|
103
|
-
}
|
|
104
|
-
async function onboardApiKey(config) {
|
|
105
|
-
stdout.write(box([
|
|
106
|
-
brand.primary(c.bold('欢迎使用 CCLI')),
|
|
107
|
-
'',
|
|
108
|
-
'首次使用,先配置一个模型后端(将保存到 ~/.ccli/config.json)。',
|
|
109
|
-
'',
|
|
110
|
-
` ${brand.accent('1')} 通义千问 Qwen ${c.dim('(OpenAI 兼容,推荐)')}`,
|
|
111
|
-
` ${brand.accent('2')} Anthropic Claude`,
|
|
112
|
-
` ${brand.accent('3')} 其他 OpenAI 兼容后端 ${c.dim('(DeepSeek / GLM / 自建)')}`,
|
|
113
|
-
], { color: brand.warn }) + '\n');
|
|
114
|
-
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
115
|
-
try {
|
|
116
|
-
const choice = (await rl.question(brand.primary('选择后端 [1] ❯ '))).trim() || '1';
|
|
117
|
-
if (choice === '2') {
|
|
118
|
-
config.data.provider = 'anthropic';
|
|
119
|
-
const key = (await rl.question(brand.primary('Anthropic API Key ❯ '))).trim();
|
|
120
|
-
if (!key)
|
|
121
|
-
return false;
|
|
122
|
-
config.data.apiKey = key;
|
|
123
|
-
config.data.baseURL = undefined;
|
|
124
|
-
config.data.model = DEFAULT_MODEL;
|
|
125
|
-
}
|
|
126
|
-
else if (choice === '3') {
|
|
127
|
-
config.data.provider = 'openai';
|
|
128
|
-
const base = (await rl.question(brand.primary('Base URL ❯ '))).trim();
|
|
129
|
-
const model = (await rl.question(brand.primary('模型 ID ❯ '))).trim();
|
|
130
|
-
const key = (await rl.question(brand.primary('API Key ❯ '))).trim();
|
|
131
|
-
if (!key || !base || !model)
|
|
132
|
-
return false;
|
|
133
|
-
config.data.apiKey = key;
|
|
134
|
-
config.data.baseURL = base;
|
|
135
|
-
config.data.model = model;
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
// 默认:千问
|
|
139
|
-
config.data.provider = 'openai';
|
|
140
|
-
const key = (await rl.question(brand.primary('千问 API Key ') + c.dim('(sk-...) ❯ '))).trim();
|
|
141
|
-
if (!key)
|
|
142
|
-
return false;
|
|
143
|
-
config.data.apiKey = key;
|
|
144
|
-
config.data.baseURL =
|
|
145
|
-
'https://dashscope.aliyuncs.com/compatible-mode/v1';
|
|
146
|
-
const model = (await rl.question(brand.primary('模型 ') + c.dim('[qwen-plus] ❯ '))).trim();
|
|
147
|
-
config.data.model = model || 'qwen-plus';
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
finally {
|
|
151
|
-
rl.close();
|
|
152
|
-
}
|
|
153
|
-
config.saveGlobal();
|
|
154
|
-
stdout.write(brand.ok('✓ 已保存配置。') +
|
|
155
|
-
c.dim(` 后端 ${config.data.provider} · 模型 ${config.data.model}\n\n`));
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
async function main() {
|
|
11
|
+
import { parseArgs } from './args.js';
|
|
12
|
+
import { NAME, VERSION } from './version.js';
|
|
13
|
+
import { startRepl } from './repl.js';
|
|
14
|
+
const HELP = `${NAME} v${VERSION}
|
|
15
|
+
|
|
16
|
+
用法:
|
|
17
|
+
ccli [选项]
|
|
18
|
+
|
|
19
|
+
选项:
|
|
20
|
+
-v, --version 打印版本号
|
|
21
|
+
-h, --help 打印本帮助
|
|
22
|
+
|
|
23
|
+
示例:
|
|
24
|
+
ccli 启动 CCLI
|
|
25
|
+
ccli --version 查看版本
|
|
26
|
+
`;
|
|
27
|
+
function main() {
|
|
28
|
+
// argv 前两个是 node 路径和脚本路径,从第 3 个起才是用户输入
|
|
159
29
|
const args = parseArgs(process.argv.slice(2));
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return;
|
|
30
|
+
if (args.unknown.length > 0) {
|
|
31
|
+
console.error(`未知选项: ${args.unknown.join(', ')}`);
|
|
32
|
+
console.error(`运行 "ccli --help" 查看可用选项。`);
|
|
33
|
+
process.exit(1);
|
|
165
34
|
}
|
|
166
|
-
if (args.
|
|
167
|
-
|
|
35
|
+
if (args.help) {
|
|
36
|
+
console.log(HELP);
|
|
168
37
|
return;
|
|
169
38
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
config.model = args.model;
|
|
173
|
-
}
|
|
174
|
-
if (args.autoApprove) {
|
|
175
|
-
config.data.autoApprove = true;
|
|
176
|
-
}
|
|
177
|
-
if (args.command === 'config') {
|
|
178
|
-
await onboardApiKey(config);
|
|
179
|
-
stdout.write([
|
|
180
|
-
`当前模型:${brand.accent(config.model)}`,
|
|
181
|
-
`配置文件:~/.ccli/config.json`,
|
|
182
|
-
].join('\n') + '\n');
|
|
39
|
+
if (args.version) {
|
|
40
|
+
console.log(VERSION);
|
|
183
41
|
return;
|
|
184
42
|
}
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
if (args.prompt) {
|
|
188
|
-
stdout.write(brand.err('错误:') +
|
|
189
|
-
'未设置 API Key。请先运行 ccli(无参数,引导配置),或 export ANTHROPIC_API_KEY / QWEN_API_KEY。\n');
|
|
190
|
-
process.exit(1);
|
|
191
|
-
}
|
|
192
|
-
const ok = await onboardApiKey(config);
|
|
193
|
-
if (!ok || !config.apiKey) {
|
|
194
|
-
stdout.write(c.dim('已退出。设置 API Key 后再运行 ccli。\n'));
|
|
195
|
-
process.exit(0);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
// 一次性模式
|
|
199
|
-
if (args.prompt) {
|
|
200
|
-
await runHeadless(config, args.cwd, args.prompt);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
// 会话恢复
|
|
204
|
-
let resumeFrom;
|
|
205
|
-
if (args.resume && !args.resumeId) {
|
|
206
|
-
// 列出供选择
|
|
207
|
-
const all = SessionStore.list(args.cwd);
|
|
208
|
-
if (!all.length) {
|
|
209
|
-
stdout.write(c.dim('该目录暂无历史会话,将开启新会话。\n'));
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
stdout.write(brand.primary('该目录的历史会话:\n'));
|
|
213
|
-
for (const s of all.slice(0, 10)) {
|
|
214
|
-
stdout.write(` ${brand.accent(s.id)} ${c.dim(s.updatedAt.replace('T', ' ').slice(0, 16))} ${c.dim(`${s.messages.length} 条`)}\n`);
|
|
215
|
-
}
|
|
216
|
-
stdout.write(c.dim('用 ccli --resume <id> 恢复指定会话。本次将开启新会话。\n\n'));
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
else if ((args.continueSession || args.resumeId) && config.apiKey) {
|
|
220
|
-
const data = SessionStore.load(args.cwd, args.resumeId);
|
|
221
|
-
if (data) {
|
|
222
|
-
resumeFrom = data;
|
|
223
|
-
config.model = data.model;
|
|
224
|
-
stdout.write(c.dim(`已恢复会话 ${data.id}(${data.messages.length} 条历史)。\n\n`));
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
stdout.write(c.dim('未找到可恢复的会话,将开启新会话。\n\n'));
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
// 交互式会话
|
|
231
|
-
const session = new Session(config, args.cwd, resumeFrom);
|
|
232
|
-
await session.start();
|
|
43
|
+
// 无参数:进入交互式会话循环。
|
|
44
|
+
startRepl();
|
|
233
45
|
}
|
|
234
|
-
main()
|
|
235
|
-
|
|
236
|
-
process.exit(1);
|
|
237
|
-
});
|
|
46
|
+
main();
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,IAAI,GAAG,GAAG,IAAI,KAAK,OAAO;;;;;;;;;;;;CAY/B,CAAC;AAEF,SAAS,IAAI;IACT,uCAAuC;IACvC,MAAM,IAAI,GACV,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClD,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClB,OAAO;IACX,CAAC;IAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,OAAO;IACX,CAAC;IAED,iBAAiB;IACjB,SAAS,EAAE,CAAC;AAChB,CAAC;AAED,IAAI,EAAE,CAAC"}
|
package/dist/repl.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 交互式会话循环(REPL:Read-Eval-Print-Loop)。
|
|
3
|
+
*
|
|
4
|
+
* 本步只搭骨架,不接 AI:
|
|
5
|
+
* 读一行用户输入 → 处理(暂时只回显)→ 打印提示符,再读下一行。
|
|
6
|
+
* 支持优雅退出:/exit、/quit,或按 Ctrl-D(EOF)、Ctrl-C。
|
|
7
|
+
*
|
|
8
|
+
* 用 Node 内置 readline,零依赖。等骨架稳定后,下一步把「处理」那一环
|
|
9
|
+
* 换成「调用 AI」即可,循环结构不用动。
|
|
10
|
+
*/
|
|
11
|
+
import { createInterface } from 'node:readline';
|
|
12
|
+
import { NAME, VERSION } from './version.js';
|
|
13
|
+
import { loadConfig, maskKey } from './config.js';
|
|
14
|
+
const PROMPT = '› ';
|
|
15
|
+
/** 用户输入这些即退出。 */
|
|
16
|
+
const EXIT_COMMANDS = new Set(['/exit', '/quit']);
|
|
17
|
+
export function startRepl() {
|
|
18
|
+
// 启动即解析配置:缺失就明确报错退出,不进入循环
|
|
19
|
+
const result = loadConfig();
|
|
20
|
+
if (!result.ok) {
|
|
21
|
+
console.error(result.message);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const { config } = result;
|
|
25
|
+
const rl = createInterface({
|
|
26
|
+
input: process.stdin,
|
|
27
|
+
output: process.stdout,
|
|
28
|
+
prompt: PROMPT,
|
|
29
|
+
});
|
|
30
|
+
console.log(`${NAME} v${VERSION}`);
|
|
31
|
+
console.log(`后端: ${config.provider} 模型: ${config.model} 密钥: ${maskKey(config.apiKey)}`);
|
|
32
|
+
console.log('进入交互式会话。输入 /exit 退出,或按 Ctrl-C。\n');
|
|
33
|
+
rl.prompt();
|
|
34
|
+
rl.on('line', (line) => {
|
|
35
|
+
const input = line.trim();
|
|
36
|
+
if (input.length === 0) {
|
|
37
|
+
// 空行:不处理,直接再次提示
|
|
38
|
+
rl.prompt();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (EXIT_COMMANDS.has(input)) {
|
|
42
|
+
rl.close();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// 暂时的「处理」:回显。下一步换成调用 AI。
|
|
46
|
+
console.log(`你说:${input}`);
|
|
47
|
+
rl.prompt();
|
|
48
|
+
});
|
|
49
|
+
// Ctrl-D(EOF)或 rl.close() 触发
|
|
50
|
+
rl.on('close', () => {
|
|
51
|
+
console.log('\n再见 👋');
|
|
52
|
+
process.exit(0);
|
|
53
|
+
});
|
|
54
|
+
// Ctrl-C:关闭循环(进而触发 close)
|
|
55
|
+
rl.on('SIGINT', () => {
|
|
56
|
+
rl.close();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=repl.js.map
|
package/dist/repl.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repl.js","sourceRoot":"","sources":["../src/repl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,GAAG,IAAI,CAAC;AAEpB,iBAAiB;AACjB,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAElD,MAAM,UAAU,SAAS;IACrB,0BAA0B;IAC1B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAE1B,MAAM,EAAE,GAAG,eAAe,CAAC;QACvB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,MAAM;KACjB,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CACP,OAAO,MAAM,CAAC,QAAQ,SAAS,MAAM,CAAC,KAAK,SAAS,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAC/E,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAChD,EAAE,CAAC,MAAM,EAAE,CAAC;IAEZ,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAE1B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,gBAAgB;YAChB,EAAE,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;QACX,CAAC;QAED,IAAI,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO;QACX,CAAC;QAED,yBAAyB;QACzB,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC;QAE3B,EAAE,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,6BAA6B;IAC7B,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAChB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,0BAA0B;IAC1B,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QACjB,EAAE,CAAC,KAAK,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 读取自身版本号。
|
|
3
|
+
*
|
|
4
|
+
* 版本号的唯一真相是 package.json,运行时从那里读,避免手写常量导致不同步。
|
|
5
|
+
* 编译后 dist/version.js 在 dist/ 下,package.json 在其上一级,故用 ../package.json。
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
const pkgPath = fileURLToPath(new URL('../package.json', import.meta.url));
|
|
10
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
11
|
+
export const NAME = pkg.name;
|
|
12
|
+
export const VERSION = pkg.version;
|
|
13
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAGnD,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;AAC7B,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gird/ccli",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "CCLI ——
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "CCLI —— 一步一步从零实现的代理型(Agentic)终端 AI 编程助手",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"ccli": "./dist/index.js"
|
|
@@ -9,9 +9,7 @@
|
|
|
9
9
|
"main": "./dist/index.js",
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
|
-
"README.md"
|
|
13
|
-
"CCLI.md",
|
|
14
|
-
".env.example"
|
|
12
|
+
"README.md"
|
|
15
13
|
],
|
|
16
14
|
"scripts": {
|
|
17
15
|
"build": "tsc -p tsconfig.json",
|
|
@@ -27,10 +25,7 @@
|
|
|
27
25
|
"ai",
|
|
28
26
|
"cli",
|
|
29
27
|
"agent",
|
|
30
|
-
"coding-assistant"
|
|
31
|
-
"anthropic",
|
|
32
|
-
"qwen",
|
|
33
|
-
"openai"
|
|
28
|
+
"coding-assistant"
|
|
34
29
|
],
|
|
35
30
|
"repository": {
|
|
36
31
|
"type": "git",
|
|
@@ -40,15 +35,10 @@
|
|
|
40
35
|
"url": "https://github.com/GridGit/ai-cli/issues"
|
|
41
36
|
},
|
|
42
37
|
"homepage": "https://github.com/GridGit/ai-cli#readme",
|
|
43
|
-
"author": "",
|
|
44
38
|
"license": "MIT",
|
|
45
39
|
"publishConfig": {
|
|
46
40
|
"access": "public"
|
|
47
41
|
},
|
|
48
|
-
"dependencies": {
|
|
49
|
-
"@anthropic-ai/sdk": "^0.104.1",
|
|
50
|
-
"openai": "^6.42.0"
|
|
51
|
-
},
|
|
52
42
|
"devDependencies": {
|
|
53
43
|
"@types/node": "^25.9.3",
|
|
54
44
|
"tsx": "^4.22.4",
|
package/.env.example
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# CCLI 本地开发默认配置
|
|
2
|
-
# 用法:复制本文件为 .env,填入 API Key 即可。
|
|
3
|
-
# cp .env.example .env
|
|
4
|
-
# .env 已被 .gitignore 忽略,不会提交。启动时自动加载(真实环境变量优先)。
|
|
5
|
-
|
|
6
|
-
# ───────────────────────────────────────────────
|
|
7
|
-
# 后端二选一(不设 ANTHROPIC_API_KEY 时自动用 OpenAI 兼容后端)
|
|
8
|
-
# ───────────────────────────────────────────────
|
|
9
|
-
|
|
10
|
-
# 【方式 A】通义千问 Qwen(OpenAI 兼容,推荐用于本地开发)
|
|
11
|
-
QWEN_API_KEY=sk-在这里填入你的千问key
|
|
12
|
-
QWEN_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
|
13
|
-
QWEN_MODEL=qwen-plus
|
|
14
|
-
|
|
15
|
-
# 【方式 B】Anthropic 官方(若有 Claude key,取消注释即可,会优先生效)
|
|
16
|
-
# ANTHROPIC_API_KEY=sk-ant-...
|
|
17
|
-
# ANTHROPIC_BASE_URL=https://api.anthropic.com
|
|
18
|
-
|
|
19
|
-
# 【方式 C】其他 OpenAI 兼容后端(DeepSeek / GLM / 自建)
|
|
20
|
-
# OPENAI_API_KEY=...
|
|
21
|
-
# OPENAI_BASE_URL=https://your-endpoint/v1
|
|
22
|
-
# CCLI_MODEL=your-model
|
|
23
|
-
# CCLI_PROVIDER=openai
|