jinzd-ai-cli 0.4.20 → 0.4.21
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 +7 -6
- package/README.zh-CN.md +7 -6
- package/dist/{chunk-ZZPMHXTT.js → chunk-2JOMWVTE.js} +1 -1
- package/dist/{chunk-ZIFAZ4EJ.js → chunk-6CJX3RST.js} +31 -1
- package/dist/{chunk-IR6JR3GH.js → chunk-7T2W4LN3.js} +20 -2
- package/dist/{chunk-XKWQJ5K4.js → chunk-AR656G5C.js} +31 -1
- package/dist/{hub-J3OGSYQ5.js → hub-CNSFX47B.js} +1 -1
- package/dist/index.js +144 -17
- package/dist/{run-tests-DFZRFWDU.js → run-tests-3PLCZ4P3.js} +1 -1
- package/dist/{run-tests-S3UPP5B4.js → run-tests-ZTVEN2O7.js} +1 -1
- package/dist/{server-HAZM4ENT.js → server-IDPMZAUF.js} +16 -7
- package/dist/{task-orchestrator-GFNC3Y6D.js → task-orchestrator-UZQRB7AO.js} +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/jinzd-ai-cli)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
[](https://nodejs.org)
|
|
10
|
-
[]()
|
|
11
11
|
[](https://github.com/jinzhengdong/ai-cli/releases)
|
|
12
12
|
[](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml)
|
|
13
13
|
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
- **PWA Support** — Install Web UI as a desktop/mobile app, accessible over LAN
|
|
35
35
|
- **Hierarchical Context** — 3-layer context files (global / project / subdirectory) auto-injected
|
|
36
36
|
- **Headless Mode** — `ai-cli -p "prompt"` for CI/CD pipelines and scripting
|
|
37
|
-
- **
|
|
37
|
+
- **38 REPL Commands** — Session management, checkpointing, code review, scaffolding, user identity, and more
|
|
38
38
|
- **GitHub Actions CI/CD** — Automated testing on Node 20/22 + npm publish on release tags
|
|
39
39
|
- **Cross-Platform** — Windows, macOS, Linux
|
|
40
40
|
|
|
@@ -75,7 +75,7 @@ Pre-built CLI binaries (no Node.js required, ~56 MB):
|
|
|
75
75
|
aicli
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
On first run, an interactive setup wizard guides you through
|
|
78
|
+
On first run, an interactive setup wizard guides you through setting up your profile and entering your API key. Your identity is persisted and injected into every AI conversation.
|
|
79
79
|
|
|
80
80
|
```
|
|
81
81
|
[deepseek] > Hello! Tell me about this project
|
|
@@ -167,12 +167,13 @@ AI autonomously invokes these 16 tools during conversations:
|
|
|
167
167
|
| `/undo` | Undo last file operation |
|
|
168
168
|
| `/doctor` | Health check (API keys, MCP, context) |
|
|
169
169
|
| `/export` | Export session as Markdown or JSON |
|
|
170
|
+
| `/profile` | View/edit your identity (AI knows who you are across all providers) |
|
|
170
171
|
| `/config` | Open configuration wizard |
|
|
171
172
|
| `/help` | Show all available commands |
|
|
172
173
|
|
|
173
174
|
**Multi-line input**: Use `\` at end of line for continuation, or paste multi-line content directly (auto-detected and merged).
|
|
174
175
|
|
|
175
|
-
Type `/help` in the REPL to see all
|
|
176
|
+
Type `/help` in the REPL to see all 38 commands.
|
|
176
177
|
|
|
177
178
|
## CLI Parameters
|
|
178
179
|
|
|
@@ -348,11 +349,11 @@ The Web UI (`aicli web`) provides a full-featured browser interface:
|
|
|
348
349
|
## Testing
|
|
349
350
|
|
|
350
351
|
```bash
|
|
351
|
-
npm test # Run all
|
|
352
|
+
npm test # Run all 282 tests
|
|
352
353
|
npm run test:watch # Watch mode
|
|
353
354
|
```
|
|
354
355
|
|
|
355
|
-
|
|
356
|
+
18 test suites covering: authentication, sessions, tool types & danger levels, permissions, output truncation, diff rendering, edit-file similarity, error hierarchy, config management, env loading, provider registry, web-fetch, grep-files, hub renderer, hub discussion, hub presets, dev-state.
|
|
356
357
|
|
|
357
358
|
## Documentation
|
|
358
359
|
|
package/README.zh-CN.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/jinzd-ai-cli)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
[](https://nodejs.org)
|
|
10
|
-
[]()
|
|
11
11
|
[](https://github.com/jinzhengdong/ai-cli/releases)
|
|
12
12
|
[](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml)
|
|
13
13
|
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
- **PWA 支持** — Web UI 可安装为桌面/移动应用,支持局域网访问
|
|
27
27
|
- **三层级上下文** — 全局 / 项目 / 子目录上下文文件自动注入
|
|
28
28
|
- **无头模式** — `aicli -p "提示词"` 用于 CI/CD 管道和脚本
|
|
29
|
-
- **
|
|
29
|
+
- **38 个 REPL 命令** — 会话管理、检查点、代码审查、脚手架、用户身份等
|
|
30
30
|
- **GitHub Actions CI/CD** — Node 20/22 自动测试 + Release tag 自动发布 npm
|
|
31
31
|
- **跨平台** — Windows、macOS、Linux
|
|
32
32
|
|
|
@@ -67,7 +67,7 @@ npm install -g jinzd-ai-cli
|
|
|
67
67
|
aicli
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
首次运行会进入交互式配置向导,先设置你的身份档案,再选择 Provider 并输入 API Key。身份信息会注入每次 AI 对话。
|
|
71
71
|
|
|
72
72
|
```
|
|
73
73
|
[deepseek] > 你好!帮我分析一下这个项目
|
|
@@ -159,12 +159,13 @@ AI 在对话中可自主调用 16 个工具:
|
|
|
159
159
|
| `/undo` | 撤销上次文件操作 |
|
|
160
160
|
| `/doctor` | 健康检查(API Key、MCP、上下文) |
|
|
161
161
|
| `/export` | 导出会话为 Markdown 或 JSON |
|
|
162
|
+
| `/profile` | 查看/编辑身份档案(AI 跨 Provider 认识你) |
|
|
162
163
|
| `/config` | 打开配置向导 |
|
|
163
164
|
| `/help` | 显示所有命令 |
|
|
164
165
|
|
|
165
166
|
**多行输入**:行末加 `\` 续行,或直接粘贴多行内容(自动检测合并)。
|
|
166
167
|
|
|
167
|
-
在 REPL 中输入 `/help` 查看全部
|
|
168
|
+
在 REPL 中输入 `/help` 查看全部 38 个命令。
|
|
168
169
|
|
|
169
170
|
## CLI 参数
|
|
170
171
|
|
|
@@ -361,11 +362,11 @@ Web UI(`aicli web`)提供功能完备的浏览器界面:
|
|
|
361
362
|
## 测试
|
|
362
363
|
|
|
363
364
|
```bash
|
|
364
|
-
npm test # 运行全部
|
|
365
|
+
npm test # 运行全部 282 个测试
|
|
365
366
|
npm run test:watch # 监听模式
|
|
366
367
|
```
|
|
367
368
|
|
|
368
|
-
|
|
369
|
+
18 个测试套件覆盖:认证、会话、工具类型与危险级别、权限、输出截断、diff 渲染、edit-file 相似度、错误层级、配置管理、环境变量、Provider 注册、web-fetch、grep-files、Hub 渲染、Hub 讨论、Hub 预设、开发状态。
|
|
369
370
|
|
|
370
371
|
## 文档
|
|
371
372
|
|
|
@@ -6,7 +6,7 @@ import { platform } from "os";
|
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
|
|
8
8
|
// src/core/constants.ts
|
|
9
|
-
var VERSION = "0.4.
|
|
9
|
+
var VERSION = "0.4.21";
|
|
10
10
|
var APP_NAME = "ai-cli";
|
|
11
11
|
var CONFIG_DIR_NAME = ".aicli";
|
|
12
12
|
var CONFIG_FILE_NAME = "config.json";
|
|
@@ -85,6 +85,35 @@ var AGENTIC_BEHAVIOR_GUIDELINE = `# Important Behavioral Guidelines
|
|
|
85
85
|
- Only begin using write/execute tools when the user **explicitly requests** an action (e.g., "generate", "create", "modify", "run", "start", etc.).
|
|
86
86
|
- Project context files (CLAUDE.md, AICLI.md) provide background information about the project. They are NOT instructions to start working. Only use them as reference when the user asks a project-related question or task.
|
|
87
87
|
- If you are unsure about the user's intent, use the ask_user tool to confirm with the user, rather than assuming and executing on your own.`;
|
|
88
|
+
function buildUserIdentityPrompt(profile) {
|
|
89
|
+
const lines = [];
|
|
90
|
+
const displayName = profile.nickname || profile.name;
|
|
91
|
+
if (displayName) {
|
|
92
|
+
lines.push(`The user's name is **${profile.name || displayName}**${profile.nickname && profile.name ? ` (prefers to be called **${profile.nickname}**)` : ""}.`);
|
|
93
|
+
}
|
|
94
|
+
if (profile.role) {
|
|
95
|
+
lines.push(`Role: ${profile.role}.`);
|
|
96
|
+
}
|
|
97
|
+
if (profile.bio) {
|
|
98
|
+
lines.push(`About: ${profile.bio}`);
|
|
99
|
+
}
|
|
100
|
+
if (profile.interests && profile.interests.length > 0) {
|
|
101
|
+
lines.push(`Interests & expertise: ${profile.interests.join(", ")}.`);
|
|
102
|
+
}
|
|
103
|
+
if (profile.locale) {
|
|
104
|
+
lines.push(`Preferred language: ${profile.locale}. Please respond in this language unless the user explicitly uses another language.`);
|
|
105
|
+
}
|
|
106
|
+
if (profile.extra) {
|
|
107
|
+
lines.push(`
|
|
108
|
+
${profile.extra}`);
|
|
109
|
+
}
|
|
110
|
+
if (lines.length === 0) return null;
|
|
111
|
+
return `# Who You're Talking To
|
|
112
|
+
|
|
113
|
+
${lines.join("\n")}
|
|
114
|
+
|
|
115
|
+
Address the user personally and adapt your communication style to their background. This identity persists across all conversations and all AI providers.`;
|
|
116
|
+
}
|
|
88
117
|
var AUTHOR = "Jin Zhengdong";
|
|
89
118
|
var AUTHOR_EMAIL = "zhengdong.jin@gmail.com";
|
|
90
119
|
var DESCRIPTION = "Cross-platform REPL-style AI conversation tool with multi-provider and agentic tool calling support";
|
|
@@ -468,6 +497,7 @@ export {
|
|
|
468
497
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
469
498
|
SUBAGENT_ALLOWED_TOOLS,
|
|
470
499
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
500
|
+
buildUserIdentityPrompt,
|
|
471
501
|
AUTHOR,
|
|
472
502
|
AUTHOR_EMAIL,
|
|
473
503
|
DESCRIPTION,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
ProviderNotFoundError,
|
|
8
8
|
RateLimitError,
|
|
9
9
|
schemaToJsonSchema
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-2JOMWVTE.js";
|
|
11
11
|
import {
|
|
12
12
|
APP_NAME,
|
|
13
13
|
CONFIG_DIR_NAME,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
MCP_TOOL_PREFIX,
|
|
21
21
|
PLUGINS_DIR_NAME,
|
|
22
22
|
VERSION
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-AR656G5C.js";
|
|
24
24
|
|
|
25
25
|
// src/config/config-manager.ts
|
|
26
26
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
@@ -58,8 +58,26 @@ var ModelParamsSchema = z.object({
|
|
|
58
58
|
/** thinking 模式的 token 预算(最小 1024,仅 Claude Extended Thinking 使用) */
|
|
59
59
|
thinkingBudget: z.number().int().min(1024).optional()
|
|
60
60
|
});
|
|
61
|
+
var UserProfileSchema = z.object({
|
|
62
|
+
/** 真实姓名(如 "Jin Zhengdong") */
|
|
63
|
+
name: z.string().optional(),
|
|
64
|
+
/** 昵称/称呼偏好(如 "东叔"),AI 会以此称呼你 */
|
|
65
|
+
nickname: z.string().optional(),
|
|
66
|
+
/** 职业角色(如 "Full-stack developer"、"Data scientist") */
|
|
67
|
+
role: z.string().optional(),
|
|
68
|
+
/** 个人简介 / 专长描述(1-3 句话) */
|
|
69
|
+
bio: z.string().optional(),
|
|
70
|
+
/** 兴趣领域或技术栈(如 ["TypeScript", "Rust", "AI/ML"]) */
|
|
71
|
+
interests: z.array(z.string()).default([]),
|
|
72
|
+
/** 语言偏好(如 "zh-CN"、"en"),AI 将据此选择交流语言 */
|
|
73
|
+
locale: z.string().optional(),
|
|
74
|
+
/** 自定义人设补充(自由格式,直接注入 system prompt) */
|
|
75
|
+
extra: z.string().optional()
|
|
76
|
+
}).default({});
|
|
61
77
|
var ConfigSchema = z.object({
|
|
62
78
|
version: z.string().default("1.0.0"),
|
|
79
|
+
// 用户身份档案 — 跨 Provider 的 "灵魂"
|
|
80
|
+
userProfile: UserProfileSchema,
|
|
63
81
|
defaultProvider: z.string().default("claude"),
|
|
64
82
|
// 每个 provider 的默认模型(key 为 provider ID)
|
|
65
83
|
defaultModels: z.record(z.string()).default({}),
|
|
@@ -8,7 +8,7 @@ import { platform } from "os";
|
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
|
|
10
10
|
// src/core/constants.ts
|
|
11
|
-
var VERSION = "0.4.
|
|
11
|
+
var VERSION = "0.4.21";
|
|
12
12
|
var APP_NAME = "ai-cli";
|
|
13
13
|
var CONFIG_DIR_NAME = ".aicli";
|
|
14
14
|
var CONFIG_FILE_NAME = "config.json";
|
|
@@ -88,6 +88,35 @@ var AGENTIC_BEHAVIOR_GUIDELINE = `# Important Behavioral Guidelines
|
|
|
88
88
|
- Only begin using write/execute tools when the user **explicitly requests** an action (e.g., "generate", "create", "modify", "run", "start", etc.).
|
|
89
89
|
- Project context files (CLAUDE.md, AICLI.md) provide background information about the project. They are NOT instructions to start working. Only use them as reference when the user asks a project-related question or task.
|
|
90
90
|
- If you are unsure about the user's intent, use the ask_user tool to confirm with the user, rather than assuming and executing on your own.`;
|
|
91
|
+
function buildUserIdentityPrompt(profile) {
|
|
92
|
+
const lines = [];
|
|
93
|
+
const displayName = profile.nickname || profile.name;
|
|
94
|
+
if (displayName) {
|
|
95
|
+
lines.push(`The user's name is **${profile.name || displayName}**${profile.nickname && profile.name ? ` (prefers to be called **${profile.nickname}**)` : ""}.`);
|
|
96
|
+
}
|
|
97
|
+
if (profile.role) {
|
|
98
|
+
lines.push(`Role: ${profile.role}.`);
|
|
99
|
+
}
|
|
100
|
+
if (profile.bio) {
|
|
101
|
+
lines.push(`About: ${profile.bio}`);
|
|
102
|
+
}
|
|
103
|
+
if (profile.interests && profile.interests.length > 0) {
|
|
104
|
+
lines.push(`Interests & expertise: ${profile.interests.join(", ")}.`);
|
|
105
|
+
}
|
|
106
|
+
if (profile.locale) {
|
|
107
|
+
lines.push(`Preferred language: ${profile.locale}. Please respond in this language unless the user explicitly uses another language.`);
|
|
108
|
+
}
|
|
109
|
+
if (profile.extra) {
|
|
110
|
+
lines.push(`
|
|
111
|
+
${profile.extra}`);
|
|
112
|
+
}
|
|
113
|
+
if (lines.length === 0) return null;
|
|
114
|
+
return `# Who You're Talking To
|
|
115
|
+
|
|
116
|
+
${lines.join("\n")}
|
|
117
|
+
|
|
118
|
+
Address the user personally and adapt your communication style to their background. This identity persists across all conversations and all AI providers.`;
|
|
119
|
+
}
|
|
91
120
|
var AUTHOR = "Jin Zhengdong";
|
|
92
121
|
var AUTHOR_EMAIL = "zhengdong.jin@gmail.com";
|
|
93
122
|
var DESCRIPTION = "Cross-platform REPL-style AI conversation tool with multi-provider and agentic tool calling support";
|
|
@@ -473,6 +502,7 @@ export {
|
|
|
473
502
|
SUBAGENT_ALLOWED_TOOLS,
|
|
474
503
|
CONTEXT_PRESSURE_THRESHOLD,
|
|
475
504
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
505
|
+
buildUserIdentityPrompt,
|
|
476
506
|
AUTHOR,
|
|
477
507
|
AUTHOR_EMAIL,
|
|
478
508
|
DESCRIPTION,
|
|
@@ -387,7 +387,7 @@ ${content}`);
|
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
389
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
390
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
390
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-UZQRB7AO.js");
|
|
391
391
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
392
392
|
let interrupted = false;
|
|
393
393
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
saveDevState,
|
|
24
24
|
sessionHasMeaningfulContent,
|
|
25
25
|
setupProxy
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-7T2W4LN3.js";
|
|
27
27
|
import {
|
|
28
28
|
ToolRegistry,
|
|
29
29
|
askUserContext,
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
theme,
|
|
39
39
|
truncateOutput,
|
|
40
40
|
undoStack
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-2JOMWVTE.js";
|
|
42
42
|
import {
|
|
43
43
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
44
44
|
AUTHOR,
|
|
@@ -57,8 +57,9 @@ import {
|
|
|
57
57
|
PLAN_MODE_SYSTEM_ADDON,
|
|
58
58
|
REPO_URL,
|
|
59
59
|
SKILLS_DIR_NAME,
|
|
60
|
-
VERSION
|
|
61
|
-
|
|
60
|
+
VERSION,
|
|
61
|
+
buildUserIdentityPrompt
|
|
62
|
+
} from "./chunk-AR656G5C.js";
|
|
62
63
|
|
|
63
64
|
// src/index.ts
|
|
64
65
|
import { program } from "commander";
|
|
@@ -166,14 +167,16 @@ var Renderer = class {
|
|
|
166
167
|
const configured = options?.wrapWidth ?? 0;
|
|
167
168
|
this.wrapWidth = configured > 0 ? configured : 0;
|
|
168
169
|
}
|
|
169
|
-
printWelcome(provider, model, contextWindow) {
|
|
170
|
+
printWelcome(provider, model, contextWindow, userNickname) {
|
|
170
171
|
const ctxStr = contextWindow ? theme.dim(` (ctx: ${fmtContextWindow(contextWindow)})`) : "";
|
|
172
|
+
const greeting = userNickname ? ` \u{1F44B} Hello, ${userNickname}!` : "";
|
|
171
173
|
console.log();
|
|
172
174
|
console.log(theme.heading(" \u{1F916} ai-cli") + theme.dim(` v${VERSION}`) + theme.dim(" \u2014 " + AUTHOR + " <" + AUTHOR_EMAIL + ">"));
|
|
175
|
+
if (greeting) console.log(theme.accent(greeting));
|
|
173
176
|
console.log(theme.dim(" " + "\u2500".repeat(55)));
|
|
174
177
|
console.log(theme.dim(` Provider : `) + theme.prompt(provider));
|
|
175
178
|
console.log(theme.dim(` Model : `) + chalk.white(model) + ctxStr);
|
|
176
|
-
console.log(theme.dim(" Commands : ") + theme.dim("/help \xB7 /about \xB7 Ctrl+C to exit"));
|
|
179
|
+
console.log(theme.dim(" Commands : ") + theme.dim("/help \xB7 /about \xB7 /profile \xB7 Ctrl+C to exit"));
|
|
177
180
|
console.log();
|
|
178
181
|
}
|
|
179
182
|
printAbout(pluginCount = 0, mcpInfo) {
|
|
@@ -217,12 +220,12 @@ var Renderer = class {
|
|
|
217
220
|
console.log(tool("spawn_agent", "Delegate to independent sub-agent (isolated dialog + auto tool-call loop)"));
|
|
218
221
|
console.log(tool("run_tests", "Run project tests and return structured report (auto-detect Maven/npm/pytest etc.)"));
|
|
219
222
|
console.log(HR);
|
|
220
|
-
console.log(theme.dim(" REPL Commands (
|
|
223
|
+
console.log(theme.dim(" REPL Commands (38):"));
|
|
221
224
|
console.log(theme.dim(" /help /about /provider /model /clear /compact /plan /session"));
|
|
222
225
|
console.log(theme.dim(" /system /context /status /search /undo /export /copy /paste"));
|
|
223
226
|
console.log(theme.dim(" /cost /init /skill /tools /plugins /mcp /config /checkpoint"));
|
|
224
|
-
console.log(theme.dim(" /review /commands /test /scaffold /add-dir /memory /
|
|
225
|
-
console.log(theme.dim(" /bug /think /diff /fork /yolo /exit"));
|
|
227
|
+
console.log(theme.dim(" /review /commands /test /scaffold /add-dir /memory /profile"));
|
|
228
|
+
console.log(theme.dim(" /doctor /bug /think /diff /fork /yolo /exit"));
|
|
226
229
|
console.log(HR);
|
|
227
230
|
console.log(theme.dim(" Key Features:"));
|
|
228
231
|
console.log(feat("Agentic loop (up to 25 tool-call rounds, final answer streamed)"));
|
|
@@ -233,6 +236,7 @@ var Renderer = class {
|
|
|
233
236
|
console.log(feat("File operation undo (/undo [list|<n>], supports write_file / edit_file / bash-created files/dirs)"));
|
|
234
237
|
console.log(feat("Thinking mode collapse (<think> blocks auto-collapsed, GLM-5 etc.)"));
|
|
235
238
|
console.log(feat("Token usage tracking (per-response + session cumulative, Gemini/Claude/DeepSeek etc.)"));
|
|
239
|
+
console.log(feat("User identity (/profile): persistent profile injected into every AI provider \u2014 AI knows who you are"));
|
|
236
240
|
console.log(feat("MCP protocol support: connect external MCP server tools (config.json mcpServers)"));
|
|
237
241
|
console.log(feat("Plugin system: ~/.aicli/plugins/*.js custom tools (requires allowPlugins:true, off by default)"));
|
|
238
242
|
console.log(feat("Plan Mode: /plan enters read-only planning, AI uses safe tools only, /plan execute resumes"));
|
|
@@ -884,6 +888,7 @@ function createDefaultCommands() {
|
|
|
884
888
|
" /test [command|filter] - Run project tests and show structured report",
|
|
885
889
|
" /scaffold <description> - Generate project scaffolding with AI",
|
|
886
890
|
" /add-dir [path|remove] - Add/remove a directory from AI context",
|
|
891
|
+
" /profile [show|set|clear] - View or edit your identity (AI knows who you are)",
|
|
887
892
|
" /memory [show|add|clear] - View or edit persistent memory (memory.md)",
|
|
888
893
|
" /doctor - Health check (API keys, config, MCP status)",
|
|
889
894
|
" /bug [--copy] - Generate bug report template (--copy to clipboard)",
|
|
@@ -1939,7 +1944,7 @@ ${hint}` : "")
|
|
|
1939
1944
|
usage: "/test [command|filter]",
|
|
1940
1945
|
async execute(args, ctx) {
|
|
1941
1946
|
try {
|
|
1942
|
-
const { executeTests } = await import("./run-tests-
|
|
1947
|
+
const { executeTests } = await import("./run-tests-ZTVEN2O7.js");
|
|
1943
1948
|
const argStr = args.join(" ").trim();
|
|
1944
1949
|
let testArgs = {};
|
|
1945
1950
|
if (argStr) {
|
|
@@ -2033,6 +2038,64 @@ ${hint}` : "")
|
|
|
2033
2038
|
}
|
|
2034
2039
|
}
|
|
2035
2040
|
},
|
|
2041
|
+
// ── /profile ───────────────────────────────────────────────────────────────
|
|
2042
|
+
{
|
|
2043
|
+
name: "profile",
|
|
2044
|
+
description: 'View or edit your identity profile (the "soul" of AI)',
|
|
2045
|
+
usage: "/profile [show|edit|set <field> <value>|clear]",
|
|
2046
|
+
execute(args, ctx) {
|
|
2047
|
+
const sub = args[0] ?? "show";
|
|
2048
|
+
const profile = ctx.config.get("userProfile") ?? {};
|
|
2049
|
+
if (sub === "show" || sub === "view") {
|
|
2050
|
+
const hasProfile = profile.name || profile.nickname || profile.role;
|
|
2051
|
+
if (!hasProfile) {
|
|
2052
|
+
console.log(theme.dim("\n No profile configured yet. Use /profile edit or /config to set up.\n"));
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
console.log(theme.heading("\n\u{1F464} Your Profile\n") + theme.dim("\u2500".repeat(50)));
|
|
2056
|
+
if (profile.name) console.log(` ${theme.dim("Name:")} ${chalk2.white(profile.name)}`);
|
|
2057
|
+
if (profile.nickname) console.log(` ${theme.dim("Nickname:")} ${theme.accent(profile.nickname)}`);
|
|
2058
|
+
if (profile.role) console.log(` ${theme.dim("Role:")} ${chalk2.white(profile.role)}`);
|
|
2059
|
+
if (profile.bio) console.log(` ${theme.dim("Bio:")} ${chalk2.white(profile.bio)}`);
|
|
2060
|
+
if (profile.interests?.length) console.log(` ${theme.dim("Interests:")} ${chalk2.white(profile.interests.join(", "))}`);
|
|
2061
|
+
if (profile.locale) console.log(` ${theme.dim("Locale:")} ${chalk2.white(profile.locale)}`);
|
|
2062
|
+
if (profile.extra) console.log(` ${theme.dim("Extra:")} ${chalk2.white(profile.extra.slice(0, 80))}${profile.extra.length > 80 ? "\u2026" : ""}`);
|
|
2063
|
+
console.log(theme.dim("\u2500".repeat(50)));
|
|
2064
|
+
console.log(theme.dim(" This identity is injected into every AI conversation.\n"));
|
|
2065
|
+
} else if (sub === "edit") {
|
|
2066
|
+
ctx.renderer.printInfo("Use /config to open the interactive profile editor, or use:\n /profile set <field> <value>\n Fields: name, nickname, role, bio, interests, locale, extra");
|
|
2067
|
+
} else if (sub === "set") {
|
|
2068
|
+
const field = args[1];
|
|
2069
|
+
const value = args.slice(2).join(" ").trim();
|
|
2070
|
+
const validFields = ["name", "nickname", "role", "bio", "interests", "locale", "extra"];
|
|
2071
|
+
if (!field || !validFields.includes(field)) {
|
|
2072
|
+
ctx.renderer.printInfo(`Usage: /profile set <field> <value>
|
|
2073
|
+
Fields: ${validFields.join(", ")}`);
|
|
2074
|
+
return;
|
|
2075
|
+
}
|
|
2076
|
+
if (!value) {
|
|
2077
|
+
ctx.renderer.printInfo(`Usage: /profile set ${field} <value>`);
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
const updated = { ...profile };
|
|
2081
|
+
if (field === "interests") {
|
|
2082
|
+
updated.interests = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2083
|
+
} else {
|
|
2084
|
+
updated[field] = value;
|
|
2085
|
+
}
|
|
2086
|
+
ctx.config.set("userProfile", updated);
|
|
2087
|
+
ctx.config.save();
|
|
2088
|
+
const displayVal = field === "interests" ? updated.interests : value;
|
|
2089
|
+
ctx.renderer.printSuccess(`Profile updated: ${field} = ${JSON.stringify(displayVal)}`);
|
|
2090
|
+
} else if (sub === "clear") {
|
|
2091
|
+
ctx.config.set("userProfile", {});
|
|
2092
|
+
ctx.config.save();
|
|
2093
|
+
ctx.renderer.printSuccess("Profile cleared. AI will no longer know who you are.");
|
|
2094
|
+
} else {
|
|
2095
|
+
ctx.renderer.printInfo("Usage: /profile [show|edit|set <field> <value>|clear]");
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
},
|
|
2036
2099
|
// ── /memory ────────────────────────────────────────────────────────────────
|
|
2037
2100
|
{
|
|
2038
2101
|
name: "memory",
|
|
@@ -3009,8 +3072,10 @@ var SetupWizard = class {
|
|
|
3009
3072
|
}
|
|
3010
3073
|
async runFirstRun() {
|
|
3011
3074
|
console.log(theme.heading("\nWelcome to ai-cli!\n"));
|
|
3012
|
-
console.log("Let's set up your
|
|
3075
|
+
console.log("Let's get to know you first, then set up your AI provider.\n");
|
|
3013
3076
|
try {
|
|
3077
|
+
await this.setupProfile();
|
|
3078
|
+
console.log();
|
|
3014
3079
|
const providerId = await select({
|
|
3015
3080
|
message: "Which AI provider do you want to set up first?",
|
|
3016
3081
|
choices: PROVIDERS
|
|
@@ -3018,7 +3083,11 @@ var SetupWizard = class {
|
|
|
3018
3083
|
await this.setupProvider(providerId);
|
|
3019
3084
|
this.config.set("defaultProvider", providerId);
|
|
3020
3085
|
this.config.save();
|
|
3021
|
-
|
|
3086
|
+
const nickname = this.config.get("userProfile")?.nickname;
|
|
3087
|
+
const greeting = nickname ? `Setup complete, ${nickname}!` : "Setup complete!";
|
|
3088
|
+
console.log(theme.success(`
|
|
3089
|
+
${greeting} Starting ai-cli...
|
|
3090
|
+
`));
|
|
3022
3091
|
return true;
|
|
3023
3092
|
} catch {
|
|
3024
3093
|
return false;
|
|
@@ -3031,9 +3100,12 @@ var SetupWizard = class {
|
|
|
3031
3100
|
const currentProxy = this.config.get("proxy") ?? "";
|
|
3032
3101
|
const proxyStatus = currentProxy ? theme.success(`[${currentProxy}]`) : theme.dim("[not configured]");
|
|
3033
3102
|
const googleKeyStatus = this.config.getApiKey("google-search") ? theme.success("[configured]") : theme.dim("[not configured]");
|
|
3103
|
+
const profileName = this.config.get("userProfile")?.nickname || this.config.get("userProfile")?.name;
|
|
3104
|
+
const profileStatus = profileName ? theme.success(`[${profileName}]`) : theme.dim("[not configured]");
|
|
3034
3105
|
const action = await select({
|
|
3035
3106
|
message: "What would you like to configure?",
|
|
3036
3107
|
choices: [
|
|
3108
|
+
{ value: "profile", name: `Edit your profile (identity) ${profileStatus}` },
|
|
3037
3109
|
{ value: "apikey", name: "Manage API key for a provider" },
|
|
3038
3110
|
{ value: "default", name: "Change default provider" },
|
|
3039
3111
|
{ value: "proxy", name: `Configure proxy (HTTP/HTTPS) ${proxyStatus}` },
|
|
@@ -3041,7 +3113,9 @@ var SetupWizard = class {
|
|
|
3041
3113
|
{ value: "done", name: "Done" }
|
|
3042
3114
|
]
|
|
3043
3115
|
});
|
|
3044
|
-
if (action === "
|
|
3116
|
+
if (action === "profile") {
|
|
3117
|
+
await this.setupProfile();
|
|
3118
|
+
} else if (action === "proxy") {
|
|
3045
3119
|
await this.setupProxy();
|
|
3046
3120
|
} else if (action === "google") {
|
|
3047
3121
|
await this.setupGoogleSearch();
|
|
@@ -3214,6 +3288,50 @@ Managing ${displayName} API Key`);
|
|
|
3214
3288
|
}
|
|
3215
3289
|
console.log();
|
|
3216
3290
|
}
|
|
3291
|
+
/** 收集用户身份档案 — aicli 的 "灵魂" 功能 */
|
|
3292
|
+
async setupProfile() {
|
|
3293
|
+
const existing = this.config.get("userProfile") ?? {};
|
|
3294
|
+
console.log(theme.heading("\n\u{1F464} Your Profile"));
|
|
3295
|
+
console.log(theme.dim(" This helps AI know who you are \u2014 across all providers and sessions.\n"));
|
|
3296
|
+
const name = await input({
|
|
3297
|
+
message: "Your name:",
|
|
3298
|
+
default: existing.name || void 0
|
|
3299
|
+
});
|
|
3300
|
+
const nickname = await input({
|
|
3301
|
+
message: "Preferred nickname (AI will call you this):",
|
|
3302
|
+
default: existing.nickname || void 0
|
|
3303
|
+
});
|
|
3304
|
+
const role = await input({
|
|
3305
|
+
message: 'Your role (e.g. "Full-stack developer", "Student"):',
|
|
3306
|
+
default: existing.role || void 0
|
|
3307
|
+
});
|
|
3308
|
+
const bio = await input({
|
|
3309
|
+
message: "Brief intro (1-2 sentences about your background):",
|
|
3310
|
+
default: existing.bio || void 0
|
|
3311
|
+
});
|
|
3312
|
+
const interestsStr = await input({
|
|
3313
|
+
message: "Interests / tech stack (comma-separated):",
|
|
3314
|
+
default: existing.interests?.join(", ") || void 0
|
|
3315
|
+
});
|
|
3316
|
+
const locale = await input({
|
|
3317
|
+
message: 'Preferred language (e.g. "zh-CN", "en", "ja"):',
|
|
3318
|
+
default: existing.locale || void 0
|
|
3319
|
+
});
|
|
3320
|
+
const profile = {};
|
|
3321
|
+
if (name) profile.name = name;
|
|
3322
|
+
if (nickname) profile.nickname = nickname;
|
|
3323
|
+
if (role) profile.role = role;
|
|
3324
|
+
if (bio) profile.bio = bio;
|
|
3325
|
+
if (interestsStr) profile.interests = interestsStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
3326
|
+
if (locale) profile.locale = locale;
|
|
3327
|
+
if (existing.extra) profile.extra = existing.extra;
|
|
3328
|
+
this.config.set("userProfile", profile);
|
|
3329
|
+
this.config.save();
|
|
3330
|
+
const displayName = nickname || name || "User";
|
|
3331
|
+
console.log(theme.success(`
|
|
3332
|
+
Profile saved! AI will know you as "${displayName}".
|
|
3333
|
+
`));
|
|
3334
|
+
}
|
|
3217
3335
|
};
|
|
3218
3336
|
|
|
3219
3337
|
// src/repl/custom-commands.ts
|
|
@@ -3873,7 +3991,15 @@ You have a maximum of ${MAX_TOOL_ROUNDS} tool call rounds per conversation turn.
|
|
|
3873
3991
|
- On Windows, use PowerShell cmdlets (Invoke-RestMethod, Get-ChildItem) instead of Unix commands (curl, grep, find).
|
|
3874
3992
|
- If starting a long-running server process via bash, use background execution \u2014 do not block the tool round.
|
|
3875
3993
|
- Prioritize the most critical tasks first in case you run out of rounds.`;
|
|
3876
|
-
const parts = [dateTimeInfo + "\n" + envInfo
|
|
3994
|
+
const parts = [dateTimeInfo + "\n" + envInfo];
|
|
3995
|
+
const userProfile = this.config.get("userProfile");
|
|
3996
|
+
if (userProfile) {
|
|
3997
|
+
const identityPrompt = buildUserIdentityPrompt(userProfile);
|
|
3998
|
+
if (identityPrompt) {
|
|
3999
|
+
parts.push(identityPrompt);
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
parts.push(AGENTIC_BEHAVIOR_GUIDELINE + budgetInfo);
|
|
3877
4003
|
const memory = this.loadMemoryContent();
|
|
3878
4004
|
if (memory) {
|
|
3879
4005
|
parts.push(`# Persistent Memory
|
|
@@ -4113,7 +4239,8 @@ Session '${this.resumeSessionId}' not found.
|
|
|
4113
4239
|
const pluginCount = await this.toolRegistry.loadPlugins(this.config.getPluginsDir(), allowPlugins);
|
|
4114
4240
|
const welcomeProvider = this.providers.get(this.currentProvider);
|
|
4115
4241
|
const welcomeModelInfo = welcomeProvider?.info.models.find((m) => m.id === this.currentModel);
|
|
4116
|
-
this.
|
|
4242
|
+
const profileNickname = this.config.get("userProfile")?.nickname || this.config.get("userProfile")?.name;
|
|
4243
|
+
this.renderer.printWelcome(this.currentProvider, this.currentModel, welcomeModelInfo?.contextWindow, profileNickname);
|
|
4117
4244
|
if (welcomeModelInfo?.contextWindow) setContextWindow(welcomeModelInfo.contextWindow);
|
|
4118
4245
|
if (this.resumeSessionId) {
|
|
4119
4246
|
const session = this.sessions.current;
|
|
@@ -5564,7 +5691,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
5564
5691
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
5565
5692
|
process.exit(1);
|
|
5566
5693
|
}
|
|
5567
|
-
const { startWebServer } = await import("./server-
|
|
5694
|
+
const { startWebServer } = await import("./server-IDPMZAUF.js");
|
|
5568
5695
|
await startWebServer({ port, host: options.host });
|
|
5569
5696
|
});
|
|
5570
5697
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -5797,7 +5924,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
5797
5924
|
}),
|
|
5798
5925
|
config.get("customProviders")
|
|
5799
5926
|
);
|
|
5800
|
-
const { startHub } = await import("./hub-
|
|
5927
|
+
const { startHub } = await import("./hub-CNSFX47B.js");
|
|
5801
5928
|
await startHub(
|
|
5802
5929
|
{
|
|
5803
5930
|
topic: topic ?? "",
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
renderDiff,
|
|
19
19
|
runHook,
|
|
20
20
|
setupProxy
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-7T2W4LN3.js";
|
|
22
22
|
import {
|
|
23
23
|
AuthManager
|
|
24
24
|
} from "./chunk-BYNY5JPB.js";
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
spawnAgentContext,
|
|
33
33
|
truncateOutput,
|
|
34
34
|
undoStack
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-2JOMWVTE.js";
|
|
36
36
|
import {
|
|
37
37
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
38
38
|
AUTHOR,
|
|
@@ -48,8 +48,9 @@ import {
|
|
|
48
48
|
PLAN_MODE_SYSTEM_ADDON,
|
|
49
49
|
PLUGINS_DIR_NAME,
|
|
50
50
|
SKILLS_DIR_NAME,
|
|
51
|
-
VERSION
|
|
52
|
-
|
|
51
|
+
VERSION,
|
|
52
|
+
buildUserIdentityPrompt
|
|
53
|
+
} from "./chunk-AR656G5C.js";
|
|
53
54
|
|
|
54
55
|
// src/web/server.ts
|
|
55
56
|
import express from "express";
|
|
@@ -396,7 +397,14 @@ function buildSystemPrompt(ctx) {
|
|
|
396
397
|
const envInfo = `OS: ${osName}
|
|
397
398
|
${shellInfo}
|
|
398
399
|
Working directory: ${process.cwd()}`;
|
|
399
|
-
const parts = [dateTimeInfo + "\n" + envInfo
|
|
400
|
+
const parts = [dateTimeInfo + "\n" + envInfo];
|
|
401
|
+
if (ctx.userProfile) {
|
|
402
|
+
const identityPrompt = buildUserIdentityPrompt(ctx.userProfile);
|
|
403
|
+
if (identityPrompt) {
|
|
404
|
+
parts.push(identityPrompt);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
parts.push(AGENTIC_BEHAVIOR_GUIDELINE);
|
|
400
408
|
if (ctx.configDir) {
|
|
401
409
|
const memory = loadMemoryContent(ctx.configDir);
|
|
402
410
|
if (memory) {
|
|
@@ -1490,7 +1498,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1490
1498
|
case "test": {
|
|
1491
1499
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
1492
1500
|
try {
|
|
1493
|
-
const { executeTests } = await import("./run-tests-
|
|
1501
|
+
const { executeTests } = await import("./run-tests-ZTVEN2O7.js");
|
|
1494
1502
|
const argStr = args.join(" ").trim();
|
|
1495
1503
|
let testArgs = {};
|
|
1496
1504
|
if (argStr) {
|
|
@@ -2041,7 +2049,8 @@ Add .md files to create commands.` });
|
|
|
2041
2049
|
activeSystemPrompt: this.activeSystemPrompt,
|
|
2042
2050
|
activeSkill,
|
|
2043
2051
|
planMode: this.planMode,
|
|
2044
|
-
configDir: this.config.getConfigDir()
|
|
2052
|
+
configDir: this.config.getConfigDir(),
|
|
2053
|
+
userProfile: this.config.get("userProfile")
|
|
2045
2054
|
});
|
|
2046
2055
|
if (this.addedDirs.size > 0) {
|
|
2047
2056
|
const MAX_DIR_CONTEXT = 4e4;
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2JOMWVTE.js";
|
|
8
8
|
import {
|
|
9
9
|
SUBAGENT_ALLOWED_TOOLS
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-AR656G5C.js";
|
|
11
11
|
|
|
12
12
|
// src/hub/task-orchestrator.ts
|
|
13
13
|
import { createInterface } from "readline";
|