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 CHANGED
@@ -7,7 +7,7 @@
7
7
  [![npm version](https://img.shields.io/npm/v/jinzd-ai-cli)](https://www.npmjs.com/package/jinzd-ai-cli)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
9
  [![Node.js](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](https://nodejs.org)
10
- [![Tests](https://img.shields.io/badge/tests-138%20passing-brightgreen)]()
10
+ [![Tests](https://img.shields.io/badge/tests-282%20passing-brightgreen)]()
11
11
  [![GitHub Release](https://img.shields.io/github/v/release/jinzhengdong/ai-cli)](https://github.com/jinzhengdong/ai-cli/releases)
12
12
  [![CI](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml/badge.svg)](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 REPL Commands** — Session management, checkpointing, code review, scaffolding, and more
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 selecting a provider and entering your API key.
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 37 commands.
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 138 tests
352
+ npm test # Run all 282 tests
352
353
  npm run test:watch # Watch mode
353
354
  ```
354
355
 
355
- 8 test suites covering: authentication, sessions, tool types & danger levels, permissions, output truncation, diff rendering, edit-file similarity, error hierarchy.
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
  [![npm version](https://img.shields.io/npm/v/jinzd-ai-cli)](https://www.npmjs.com/package/jinzd-ai-cli)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
9
  [![Node.js](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](https://nodejs.org)
10
- [![Tests](https://img.shields.io/badge/tests-138%20passing-brightgreen)]()
10
+ [![Tests](https://img.shields.io/badge/tests-282%20passing-brightgreen)]()
11
11
  [![GitHub Release](https://img.shields.io/github/v/release/jinzhengdong/ai-cli)](https://github.com/jinzhengdong/ai-cli/releases)
12
12
  [![CI](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml/badge.svg)](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
- - **37 个 REPL 命令** — 会话管理、检查点、代码审查、脚手架等
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
- 首次运行会进入交互式配置向导,选择 Provider 并输入 API Key。配置保存在 `~/.aicli/config.json`。
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` 查看全部 37 个命令。
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 # 运行全部 138 个测试
365
+ npm test # 运行全部 282 个测试
365
366
  npm run test:watch # 监听模式
366
367
  ```
367
368
 
368
- 8 个测试套件覆盖:认证、会话、工具类型与危险级别、权限、输出截断、diff 渲染、edit-file 相似度、错误层级。
369
+ 18 个测试套件覆盖:认证、会话、工具类型与危险级别、权限、输出截断、diff 渲染、edit-file 相似度、错误层级、配置管理、环境变量、Provider 注册、web-fetch、grep-files、Hub 渲染、Hub 讨论、Hub 预设、开发状态。
369
370
 
370
371
  ## 文档
371
372
 
@@ -6,7 +6,7 @@ import {
6
6
  SUBAGENT_DEFAULT_MAX_ROUNDS,
7
7
  SUBAGENT_MAX_ROUNDS_LIMIT,
8
8
  runTestsTool
9
- } from "./chunk-XKWQJ5K4.js";
9
+ } from "./chunk-AR656G5C.js";
10
10
 
11
11
  // src/tools/builtin/bash.ts
12
12
  import { execSync } from "child_process";
@@ -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.20";
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-ZZPMHXTT.js";
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-XKWQJ5K4.js";
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.20";
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-GFNC3Y6D.js");
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-IR6JR3GH.js";
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-ZZPMHXTT.js";
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
- } from "./chunk-XKWQJ5K4.js";
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 (37):"));
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 /doctor"));
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-S3UPP5B4.js");
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 first AI provider.\n");
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
- console.log(theme.success("\nSetup complete! Starting ai-cli...\n"));
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 === "proxy") {
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, AGENTIC_BEHAVIOR_GUIDELINE + budgetInfo];
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.renderer.printWelcome(this.currentProvider, this.currentModel, welcomeModelInfo?.contextWindow);
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-HAZM4ENT.js");
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-J3OGSYQ5.js");
5927
+ const { startHub } = await import("./hub-CNSFX47B.js");
5801
5928
  await startHub(
5802
5929
  {
5803
5930
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-ZIFAZ4EJ.js";
4
+ } from "./chunk-6CJX3RST.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-XKWQJ5K4.js";
5
+ } from "./chunk-AR656G5C.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
@@ -18,7 +18,7 @@ import {
18
18
  renderDiff,
19
19
  runHook,
20
20
  setupProxy
21
- } from "./chunk-IR6JR3GH.js";
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-ZZPMHXTT.js";
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
- } from "./chunk-XKWQJ5K4.js";
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, AGENTIC_BEHAVIOR_GUIDELINE];
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-S3UPP5B4.js");
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-ZZPMHXTT.js";
7
+ } from "./chunk-2JOMWVTE.js";
8
8
  import {
9
9
  SUBAGENT_ALLOWED_TOOLS
10
- } from "./chunk-XKWQJ5K4.js";
10
+ } from "./chunk-AR656G5C.js";
11
11
 
12
12
  // src/hub/task-orchestrator.ts
13
13
  import { createInterface } from "readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.20",
3
+ "version": "0.4.21",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",