@code4bug/jarvis-agent 1.1.6 → 1.1.7

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
@@ -3,291 +3,247 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@code4bug/jarvis-agent)](https://www.npmjs.com/package/@code4bug/jarvis-agent)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@code4bug/jarvis-agent)](https://www.npmjs.com/package/@code4bug/jarvis-agent)
5
5
  [![license](https://img.shields.io/npm/l/@code4bug/jarvis-agent)](./LICENSE)
6
- [![node](https://img.shields.io/node/v/@code4bug/jarvis-agent)](https://nodejs.org)
7
6
 
8
- > *"Good evening, sir. What can I do for you?"*
7
+ Jarvis 是一个运行在终端里的轻量级 AI Agent,基于 React + TypeScript + Ink 构建,提供多轮对话、工具调用、流式输出、外部 Skill 扩展,以及多智能体协作能力。
9
8
 
10
- 轻量化单机智能体,基于 React + TypeScript + Ink 构建的终端交互式 AI Agent。
9
+ 它的目标不是做一个“会聊天的命令行”,而是让你在终端里直接完成读取代码、搜索文件、执行命令、拆分子任务、汇总结果这一整套工作流。
11
10
 
12
- 在终端中与 AI 对话,支持多轮推理、工具调用、流式输出,开箱即用。
11
+ ## 核心能力
13
12
 
14
- > 多智能体,不只是多几个助手,而是让需求分析、方案设计、编码实现、测试验证、代码评审等角色真正协同起来。
15
- > 它可以用于多人协作流程构建、跨角色任务接力、复杂项目拆解与推进,也可以用于产品研发、缺陷修复、自动化交付等高频场景。
16
- > 当多个智能体围绕同一目标持续沟通、分工与汇总时,AI 才真正从“单点应答”走向“团队作战”。
13
+ - Agentic Loop:模型可连续推理、调用工具、读取结果并继续决策
14
+ - 流式终端交互:回复与状态实时刷新
15
+ - 内置工具:读写文件、执行命令、目录遍历、文本搜索、语义搜索
16
+ - 外部 Skill:支持从 `~/.jarvis/skills/` 动态加载 Markdown 或 Python Skill
17
+ - 多智能体协作:支持同步子 Agent、后台子 Agent 与消息总线通信
18
+ - 会话持久化:自动保存历史会话、摘要与 Token 统计
19
+ - 长期记忆:支持写入 `~/.jarvis/MEMORY.md`
20
+ - 安全围栏:危险命令识别、确认与持久授权
21
+ - 并行执行:多个只读工具可并行运行,提升响应效率
17
22
 
18
- ---
19
-
20
- ## 为什么叫 Jarvis
21
-
22
- 在漫威宇宙中,J.A.R.V.I.S.(Just A Rather Very Intelligent System)是 Tony Stark 的 AI 管家。他不只是一个语音助手,而是贯穿钢铁侠整个英雄旅程的核心伙伴 -- 管理 Stark 大厦、协助战甲研发、在战斗中提供实时分析,甚至在关键时刻做出独立判断。
23
-
24
- 这个项目取名 Jarvis,正是致敬这种理念:一个真正理解开发者意图、能够独立思考和行动的 AI 搭档。
25
-
26
- 就像 Tony 在车库里对着 Jarvis 说一句话就能启动整套工程流程一样,我们希望开发者在终端里也能拥有这样的体验 -- 你只需要描述你想做什么,Jarvis 会帮你思考、规划、执行。
27
-
28
- 不是冰冷的命令行工具,而是你的 AI 搭档。
29
-
30
- ---
31
-
32
- ## 特性
33
-
34
- - Agentic Loop 架构,支持多轮推理与工具调用闭环
35
- - 流式响应,实时输出模型生成内容
36
- - 内置工具系统:文件读写、命令执行、目录浏览、内容搜索
37
- - 外部 Skill 扩展机制,支持 Python 脚本作为工具
38
- - 多智能体切换,通过 Markdown 定义智能体人格与能力
39
- - 多智能体通信,支持智能体之间分工、协同与结果汇总
40
- - 会话持久化,自动保存历史与 Token 消耗统计
41
- - 安全围栏,危险命令自动拦截并交互式确认
42
- - 终端 UI 优化,清晰的状态指示与结构化消息展示
43
- - **并行任务执行**,多工具调用自动并行,显著提升执行效率
44
-
45
- ## 并行任务
46
-
47
- 当 LLM 在单轮推理中返回多个工具调用时,Jarvis 会自动判断是否可以并行执行,而不是逐个串行等待。
48
-
49
- ### 工作原理
23
+ ## 项目结构
50
24
 
51
- ```
52
- LLM 返回多个工具调用
53
-
54
- canRunInParallel() 判断
55
-
56
- ┌─────────────────────────────┐
57
- 可并行(全部为只读操作) │ → 每个工具在独立 Worker 线程中同时执行
58
- 不可并行(含写操作/Bash) │ → 串行执行,保证顺序与安全
59
- └─────────────────────────────┘
60
-
61
- 所有结果汇总后统一写入 transcript
25
+ ```text
26
+ src/
27
+ ├── index.tsx # 应用入口
28
+ ├── cli.ts # CLI 入口
29
+ ├── agents/ # 智能体定义
30
+ ├── commands/ # 斜杠命令
31
+ ├── components/ # Ink UI 组件
32
+ ├── config/ # 配置、常量、记忆与状态
33
+ ├── core/ # QueryEngine、Worker、消息总线、安全围栏
34
+ ├── hooks/ # 交互相关 Hooks
35
+ ├── screens/ # 主界面
36
+ ├── services/ # LLM 接口、用户画像、长期记忆服务
37
+ ├── skills/ # 外部 Skill 加载器
38
+ ├── tools/ # 内置工具实现
39
+ └── types/ # 类型定义
62
40
  ```
63
41
 
64
- 每个并行工具运行在独立的 Node.js Worker 线程中(`worker_threads`),主线程不阻塞,TUI 实时更新每个工具的执行状态。
42
+ ## 快速开始
65
43
 
66
- ### 并行条件
44
+ ### 方式一:直接使用已发布包
67
45
 
68
- | 场景 | 是否并行 |
69
- |------|----------|
70
- | 多个只读工具(ReadFile / ListDirectory / SearchFiles / SemanticSearch) | 是 |
71
- | 含任意写操作(WriteFile) | 否 |
72
- | 含 Bash 命令 | 否(副作用无法静态分析) |
73
- | 多个只读 Skill | 是 |
46
+ ```bash
47
+ npm i -g @code4bug/jarvis-agent
48
+ jarvis
49
+ ```
74
50
 
75
- ### 安全保障
51
+ ### 方式二:从源码运行
76
52
 
77
- - 写-写冲突、读-写竞态:通过工具类型静态分析提前规避
78
- - 并行模式下的危险 Bash 命令:直接跳过并提示(无法弹出交互式确认)
79
- - 任意工具出错:终止当前轮次,不影响已完成的并行结果
80
- - 用户按 ESC 中断:所有正在运行的 Worker 均收到中断信号
53
+ ```bash
54
+ pnpm install
55
+ pnpm dev
56
+ ```
81
57
 
82
- ## 快速开始
58
+ 也可以使用项目内脚本:
83
59
 
84
60
  ```bash
85
- # 安装
86
- npm i -g @code4bug/jarvis-agent
61
+ pnpm build
62
+ pnpm start
63
+ ```
87
64
 
88
- # 启动
89
- npm run start
65
+ ## 模型配置
90
66
 
91
- # 或开发模式
92
- npm run dev
93
- ```
67
+ Jarvis 会按下面顺序加载配置,后者覆盖前者:
94
68
 
95
- 启动前需要配置 LLM 服务,在 `~/.jarvis/config.json` 中添加模型配置:
69
+ 1. `~/.jarvis/config.json`
70
+ 2. `./.jarvis/config.json`
71
+
72
+ 示例:
96
73
 
97
74
  ```json
98
75
  {
99
76
  "system": {
100
- "model": "your-model-name"
77
+ "model": "default",
78
+ "context_token_limit": 18000,
79
+ "context_compress_threshold": 18000,
80
+ "enable_thinking_mode_toggle": false
101
81
  },
102
82
  "models": {
103
- "your-model-name": {
83
+ "default": {
104
84
  "api_url": "https://your-api-endpoint",
105
85
  "api_key": "your-api-key",
106
- "model": "model-id"
86
+ "model": "your-model-id",
87
+ "temperature": 0.7,
88
+ "max_tokens": 4096,
89
+ "extra_body": {
90
+ "enable_thinking": true
91
+ }
107
92
  }
108
93
  }
109
94
  }
110
95
  ```
111
96
 
112
- ## 架构
113
-
114
- ```
115
- 用户输入 → 思考 → 生成响应 / 工具调用 → 执行工具 → (继续循环或结束)
116
- ```
97
+ 说明:
117
98
 
118
- 采用分层架构:
99
+ - `system.model` 对应 `models` 里的 key
100
+ - `extra_body` 会直接合并进请求体,方便兼容不同服务商
101
+ - 如果没有可用模型配置,程序会回退到 Mock Service,适合本地界面联调
119
102
 
120
- | 层次 | 职责 | 核心文件 |
121
- |------|------|----------|
122
- | 交互层 | 终端 UI、用户输入、消息展示 | `screens/repl.tsx` |
123
- | 组件层 | 可复用 UI 组件 | `components/*` |
124
- | 编排层 | 多轮对话、会话持久化、成本追踪 | `core/QueryEngine.ts` |
125
- | 核心循环层 | 单轮 Agentic Loop 执行 | `core/query.ts` |
126
- | 工具层 | 文件/命令/搜索等执行能力 | `tools/*` |
127
- | 通信层 | 与 LLM 服务交互(支持流式) | `services/api/*` |
128
- | 配置层 | 全局常量与多级配置合并 | `config/*` |
103
+ ## 常用命令
129
104
 
130
- ## 项目结构
105
+ ### CLI
131
106
 
132
- ```
133
- src/
134
- ├── index.tsx # 应用入口
135
- ├── cli.ts # CLI 入口
136
- ├── agents/ # 智能体定义(Markdown)
137
- │ ├── jarvis.md # 默认全能助手
138
- │ ├── code-reviewer.md # 代码审查
139
- │ ├── dba.md # 数据库助手
140
- │ └── ...
141
- ├── components/ # UI 组件
142
- ├── config/ # 配置加载与常量
143
- ├── core/ # 核心引擎(QueryEngine + Agentic Loop)
144
- ├── commands/ # 斜杠命令(/init, /agent 等)
145
- ├── hooks/ # React Hooks
146
- ├── screens/ # 主界面
147
- ├── services/api/ # LLM 通信层
148
- ├── skills/ # 外部 Skill 加载与注册
149
- ├── tools/ # 内置工具
150
- └── types/ # 类型定义
107
+ ```bash
108
+ jarvis
109
+ jarvis --version
151
110
  ```
152
111
 
153
- ## 内置工具
154
-
155
- | 工具 | 说明 |
156
- |------|------|
157
- | `read_file` | 读取指定路径的文件内容 |
158
- | `write_file` | 写入内容到指定文件 |
159
- | `run_command` | 执行系统命令并返回输出 |
160
- | `list_directory` | 列出指定目录的文件和文件夹 |
161
- | `search_files` | 在指定目录中搜索包含关键词的文件 |
162
- | `create_skill` | 创建新的外部 Skill |
163
-
164
- ## 斜杠命令
112
+ ### 斜杠命令
165
113
 
166
114
  | 命令 | 说明 |
167
- |------|------|
168
- | `/init` | 扫描项目信息,生成 JARVIS.md |
115
+ | --- | --- |
116
+ | `/init` | 扫描当前项目并生成 `JARVIS.md` |
169
117
  | `/new` | 开启新会话 |
170
118
  | `/resume` | 恢复历史会话 |
171
119
  | `/agent` | 切换智能体 |
172
- | `/skills` | 查看当前所有 tools 和 skills |
173
- | `/help` | 显示帮助信息 |
120
+ | `/permissions` | 查看持久化授权 |
121
+ | `/skills` | 查看当前工具与外部 Skill |
122
+ | `/session_clear` | 清理非当前会话历史 |
123
+ | `/version` | 显示版本 |
124
+ | `/help` | 显示帮助 |
174
125
 
175
- ## 快捷键
126
+ ### 快捷键
176
127
 
177
- | 快捷键 | 功能 |
178
- |--------|------|
179
- | `Ctrl + L` | 清屏 / 新会话 |
128
+ | 快捷键 | 说明 |
129
+ | --- | --- |
130
+ | `Ctrl + L` | 清屏并开始新会话 |
180
131
  | `Ctrl + C` | 退出 |
181
- | `Esc` | 终止当前任务 / 清空输入框 |
132
+ | `Esc` | 中断当前任务或清空输入 |
182
133
  | `Alt/Option + Enter` | 输入换行 |
183
134
 
184
- ## 智能体系统
135
+ ## 内置工具
185
136
 
186
- 智能体通过 Markdown 文件定义,存放在 `src/agents/` 目录下。每个文件包含 YAML front-matter(元数据)和 Markdown 正文(system prompt)。
137
+ 当前内置工具包括:
187
138
 
188
- 运行时通过 `/agent <name>` 切换,选择会持久化到 `~/.jarvis/agent.json`。
139
+ | 工具名 | 说明 |
140
+ | --- | --- |
141
+ | `read_file` | 读取文件 |
142
+ | `write_file` | 写入文件 |
143
+ | `run_command` | 执行命令 |
144
+ | `list_directory` | 列出目录内容 |
145
+ | `search_files` | 搜索文件内容 |
146
+ | `semantic_search` | 语义搜索 |
147
+ | `create_skill` | 创建外部 Skill |
148
+ | `run_agent` | 同步运行子 Agent |
149
+ | `spawn_agent` | 异步启动后台子 Agent |
150
+ | `send_to_agent` | 向后台子 Agent 发送消息 |
151
+ | `publish_message` | 发布频道消息 |
152
+ | `subscribe_message` | 订阅频道消息 |
153
+ | `read_channel` | 查看频道历史 |
154
+ | `manage_memory` | 读取或维护长期记忆 |
189
155
 
190
- ## 多智能体通信
156
+ ## 多智能体能力
191
157
 
192
- Jarvis 在多智能体能力基础上进一步支持智能体之间的通信与协作,不再局限于单个智能体独立完成任务。
158
+ Jarvis 同时支持两种子 Agent 模式:
193
159
 
194
- 这不只是能力的叠加,而是一种协作方式的跃迁。一个智能体擅长理解需求,另一个智能体擅长架构设计,还有智能体专注编码、测试、评审与复盘。当它们开始彼此沟通、相互补位,整个系统就不再只是“一个 AI 在工作”,而像是一支真正持续运转的数字化团队。
160
+ - `run_agent`:同步执行,主 Agent 等待结果后继续
161
+ - `spawn_agent`:后台执行,主 Agent 通过消息总线持续协作
195
162
 
196
- 你可以让不同角色的智能体围绕同一目标协同工作,例如:
163
+ 后台子 Agent 的默认通信频道约定如下:
197
164
 
198
- - 产品经理智能体负责拆解需求、输出任务边界
199
- - 架构师智能体负责设计技术方案与模块划分
200
- - 开发智能体负责编码实现与联调
201
- - 测试智能体负责验证、回归与问题反馈
165
+ - 收件箱:`agent-inbox:{task_id}`
166
+ - 回复:`agent-reply:{task_id}`
202
167
 
203
- 通过多智能体通信机制,多个智能体可以围绕同一上下文进行信息传递、任务接力和结果汇总,从而搭配实现各种多人协作场景构建。
168
+ 这套机制适合把复杂任务拆成多个独立子任务,例如代码审查、资料整理、命令执行验证、结果汇总等。
204
169
 
205
- 从单点问答,到多角色联动;从工具调用,到团队协作流程编排。多智能体带来的不是简单的“多开几个助手”,而是面向复杂任务的组织能力升级,也为产品构建、研发协同、流程自动化打开了近乎无限的可能。
170
+ 更多设计说明见 [AGENT_INSTRUCTIONS.md](/Users/edward/Documents/workspace/projects/jarvis/AGENT_INSTRUCTIONS.md)。
206
171
 
207
- 适用场景包括:
172
+ ## Agent 与 Skill
208
173
 
209
- - 需求分析 + 方案设计 + 开发落地的一体化协作流程
210
- - 前端、后端、测试等不同角色的分工配合
211
- - 代码评审、缺陷修复、回归验证的闭环协同
212
- - 模拟真实团队的多人协作交付流程
174
+ ### 内置 Agent
213
175
 
214
- 详见 [AGENT_INSTRUCTIONS.md](./AGENT_INSTRUCTIONS.md)。
176
+ 内置 Agent 定义位于 `src/agents/`,当前仓库已包含:
215
177
 
216
- ## Skill 扩展
178
+ - `jarvis`
179
+ - `code-reviewer`
180
+ - `dba`
181
+ - `finance-advisor`
182
+ - `stock-trader`
217
183
 
218
- 外部 Skill 存放在 `~/.jarvis/skills/<skill-name>/SKILL.md`,支持:
184
+ Agent 通过 Markdown 文件定义元信息与系统提示词,切换结果会持久化到 `~/.jarvis/agent.json`。
219
185
 
220
- - Markdown 指令型 Skill(由 LLM 解释执行)
221
- - Python 脚本型 Skill(`skill.py`,直接执行返回结果)
186
+ ### 外部 Skill
222
187
 
223
- 详见 [SKILL_INSTRUCTIONS.md](./SKILL_INSTRUCTIONS.md)。
188
+ 外部 Skill 目录:
224
189
 
225
- ## 配置
190
+ ```text
191
+ ~/.jarvis/skills/<skill-name>/
192
+ ├── SKILL.md
193
+ └── skill.py # 可选
194
+ ```
226
195
 
227
- 配置文件支持两级合并,项目级覆盖全局级:
196
+ 支持两种模式:
228
197
 
229
- 1. `~/.jarvis/config.json` 全局配置
230
- 2. `./.jarvis/config.json` 项目配置
198
+ - 仅 `SKILL.md`:作为指令型 Skill 由模型解释执行
199
+ - `SKILL.md + skill.py`:作为可执行 Skill 直接运行 Python 脚本
231
200
 
232
- ### config.json 模板
201
+ 相关说明见 [SKILL_INSTRUCTIONS.md](/Users/edward/Documents/workspace/projects/jarvis/SKILL_INSTRUCTIONS.md)。
233
202
 
234
- ```json
235
- {
236
- "system": {
237
- "model": "ollama",
238
- "context_token_limit": 20000,
239
- "context_compress_threshold": 18000,
240
- "enable_thinking_mode_toggle": false
241
- },
242
- "models": {
243
- "ollama": {
244
- "api_url": "http://127.0.0.1:11434/v1/chat/completions",
245
- "api_key": "EMPTY",
246
- "model": "gemma4:latest",
247
- "temperature": 0.1,
248
- "max_tokens": 10000
249
- },
250
- "openai": {
251
- "api_url": "https://api.openai.com/v1/chat/completions",
252
- "api_key": "your-openai-api-key",
253
- "model": "gpt-4",
254
- "temperature": 0.1,
255
- "max_tokens": 10000
256
- },
257
- "custom": {
258
- "api_url": "https://your-api-endpoint/v1/chat/completions",
259
- "api_key": "your-api-key",
260
- "model": "model-id",
261
- "temperature": 0.1,
262
- "max_tokens": 10000
263
- }
264
- }
265
- }
203
+ ## 会话、记忆与安全
204
+
205
+ ### 会话与日志
206
+
207
+ - 会话目录:`~/.jarvis/sessions/`
208
+ - 日志目录:`~/.jarvis/logs/`
209
+
210
+ 每次会话会保存消息、摘要、更新时间和累计 Token。
211
+
212
+ ### 长期记忆
213
+
214
+ - 记忆文件:`~/.jarvis/MEMORY.md`
215
+ - 通过 `manage_memory` 工具进行读取、追加、覆盖和定位
216
+
217
+ ### 安全围栏
218
+
219
+ `run_command` 会经过统一安全校验,内置规则会拦截高风险命令,例如:
220
+
221
+ - 递归删除
222
+ - 直接格式化磁盘
223
+ - 修改关键权限
224
+ - 网络下载后直接执行
225
+
226
+ 高风险命令支持会话级或持久化授权,持久化记录保存在 `~/.jarvis/.permissions.json`。
227
+
228
+ ## 开发
229
+
230
+ ```bash
231
+ pnpm install
232
+ pnpm build
233
+ pnpm test
234
+ ```
235
+
236
+ 当前 `test` 脚本实际执行的是一次构建校验:
237
+
238
+ ```bash
239
+ npm run build
266
240
  ```
267
241
 
268
- ### 字段说明
269
-
270
- | 字段 | 类型 | 说明 |
271
- |------|------|------|
272
- | `system.model` | string | 当前使用的模型名称,对应 `models` 中的 key |
273
- | `system.context_token_limit` | number | 上下文 Token 上限,默认 20000 |
274
- | `system.context_compress_threshold` | number | 上下文压缩触发阈值 |
275
- | `system.enable_thinking_mode_toggle` | boolean | 是否启用思考/非思考模式切换,默认 false |
276
- | `models.<name>.api_url` | string | 模型 API 地址,需兼容 OpenAI Chat Completions 格式 |
277
- | `models.<name>.api_key` | string | API 密钥 |
278
- | `models.<name>.model` | string | 模型 ID |
279
- | `models.<name>.temperature` | number | 采样温度,可选 |
280
- | `models.<name>.max_tokens` | number | 最大生成 Token 数,可选 |
281
-
282
- 支持任意兼容 OpenAI Chat Completions API 的服务,包括 Ollama、vLLM、LM Studio、各云厂商 API 等。切换模型只需修改 `system.model` 指向不同的 key。
283
-
284
- ## 技术栈
285
-
286
- - React 18 + Ink 5(终端 UI)
287
- - TypeScript 5
288
- - ink-spinner(加载动画)
289
- - marked + marked-terminal(Markdown 渲染)
290
- - uuid(会话 ID)
242
+ ## 相关文档
243
+
244
+ - [JARVIS.md](/Users/edward/Documents/workspace/projects/jarvis/JARVIS.md)
245
+ - [AGENT-DEMO.md](/Users/edward/Documents/workspace/projects/jarvis/AGENT-DEMO.md)
246
+ - [USAGE_INSTRCUTIONS.md](/Users/edward/Documents/workspace/projects/jarvis/USAGE_INSTRCUTIONS.md)
291
247
 
292
248
  ## License
293
249
 
@@ -11,6 +11,8 @@ export interface SlashCommand {
11
11
  description: string;
12
12
  /** 命令类别 */
13
13
  category: 'agent' | 'tool' | 'builtin';
14
+ /** 回车行为:直接执行 / 弹出列表 / 作为上下文提交 */
15
+ submitMode: 'action' | 'list' | 'context';
14
16
  }
15
17
  export declare function getAgentSubCommands(): SlashCommand[];
16
18
  /** 根据前缀过滤一级命令 */
@@ -6,24 +6,24 @@
6
6
  */
7
7
  /** 内置命令 */
8
8
  const builtinCommands = [
9
- { name: 'init', description: '初始化项目信息,生成 JARVIS.md', category: 'builtin' },
10
- { name: 'new', description: '开启新会话,重新初始化上下文', category: 'builtin' },
11
- { name: 'resume', description: '恢复历史会话上下文', category: 'builtin' },
12
- { name: 'help', description: '显示帮助信息', category: 'builtin' },
13
- { name: 'agent', description: '切换智能体', category: 'builtin' },
14
- { name: 'permissions', description: '查看所有持久化授权列表', category: 'builtin' },
15
- { name: 'skills', description: '查看当前所有 tools 和 skills', category: 'builtin' },
16
- { name: 'session_clear', description: '清理所有非当前会话的历史记录', category: 'builtin' },
17
- { name: 'version', description: '显示当前版本号', category: 'builtin' },
9
+ { name: 'init', description: '初始化项目信息,生成 JARVIS.md', category: 'builtin', submitMode: 'action' },
10
+ { name: 'new', description: '开启新会话,重新初始化上下文', category: 'builtin', submitMode: 'action' },
11
+ { name: 'resume', description: '恢复历史会话上下文', category: 'builtin', submitMode: 'list' },
12
+ { name: 'help', description: '显示帮助信息', category: 'builtin', submitMode: 'action' },
13
+ { name: 'agent', description: '切换智能体', category: 'builtin', submitMode: 'list' },
14
+ { name: 'permissions', description: '查看所有持久化授权列表', category: 'builtin', submitMode: 'action' },
15
+ { name: 'skills', description: '查看当前所有 tools 和 skills', category: 'builtin', submitMode: 'action' },
16
+ { name: 'session_clear', description: '清理所有非当前会话的历史记录', category: 'builtin', submitMode: 'action' },
17
+ { name: 'version', description: '显示当前版本号', category: 'builtin', submitMode: 'action' },
18
18
  ];
19
19
  /** 工具命令 */
20
20
  const toolCommands = [
21
- { name: 'read', description: '读取指定文件内容', category: 'tool' },
22
- { name: 'write', description: '写入内容到文件', category: 'tool' },
23
- { name: 'bash', description: '执行 Bash 命令', category: 'tool' },
24
- { name: 'ls', description: '列出目录文件', category: 'tool' },
25
- { name: 'search', description: '搜索文件内容', category: 'tool' },
26
- { name: 'create_skill', description: '创建新的 Skill 到 ~/.jarvis/skills/', category: 'builtin' },
21
+ { name: 'read', description: '读取指定文件内容', category: 'tool', submitMode: 'context' },
22
+ { name: 'write', description: '写入内容到文件', category: 'tool', submitMode: 'context' },
23
+ { name: 'bash', description: '执行 Bash 命令', category: 'tool', submitMode: 'context' },
24
+ { name: 'ls', description: '列出目录文件', category: 'tool', submitMode: 'context' },
25
+ { name: 'search', description: '搜索文件内容', category: 'tool', submitMode: 'context' },
26
+ { name: 'create_skill', description: '创建新的 Skill 到 ~/.jarvis/skills/', category: 'builtin', submitMode: 'context' },
27
27
  ];
28
28
  /** 智能体子命令:从 agents 目录动态加载(二级菜单) */
29
29
  import { loadAllAgents } from '../agents/index.js';
@@ -39,6 +39,7 @@ export function getAgentSubCommands() {
39
39
  name: agent.meta.name.toLowerCase(),
40
40
  description: agent.meta.description,
41
41
  category: 'agent',
42
+ submitMode: 'action',
42
43
  });
43
44
  }
44
45
  _agentSubCommands = cmds;
@@ -57,6 +58,7 @@ function getTopCommands() {
57
58
  name: s.meta.name,
58
59
  description: `[Skill] ${s.meta.description || s.meta.name}`,
59
60
  category: 'tool',
61
+ submitMode: 'context',
60
62
  }));
61
63
  _topCommands = [...builtinCommands, ...toolCommands, ...skillCommands];
62
64
  return _topCommands;
@@ -174,7 +174,7 @@ export default function MultilineInput({ value, onChange, onSubmit, onUpArrow, o
174
174
  if (raw === '\r' || raw === '\n') {
175
175
  // 斜杠菜单激活时,回车 = 选中当前项
176
176
  if (slashMenuActiveRef.current) {
177
- onSlashMenuSelectRef.current?.();
177
+ onSubmitRef.current(v);
178
178
  return;
179
179
  }
180
180
  const expanded = expandPlaceholders(v);
@@ -256,7 +256,7 @@ export default function MultilineInput({ value, onChange, onSubmit, onUpArrow, o
256
256
  return;
257
257
  // 忽略控制字符和转义序列
258
258
  if (raw === '\t') {
259
- // 斜杠菜单激活时,Tab = 选中当前项
259
+ // 斜杠菜单激活时,Tab = 补全当前项到输入框
260
260
  if (slashMenuActiveRef.current) {
261
261
  onSlashMenuSelectRef.current?.();
262
262
  }
@@ -20,6 +20,6 @@ const LOGO_COLORS = ['cyan', 'cyan', 'blueBright', 'blueBright', 'magenta', 'mag
20
20
  function WelcomeHeader({ width }) {
21
21
  const maxPath = Math.max(width - 10, 20);
22
22
  const showLogo = width >= 52;
23
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, width: width, children: [showLogo && (_jsx(Box, { flexDirection: "column", children: LOGO_LINES.map((line, i) => (_jsx(Text, { color: LOGO_COLORS[i], children: line }, i))) })), _jsxs(Box, { marginTop: 0, children: [_jsx(Text, { color: "gray" }), _jsx(Text, { color: "white", bold: true, children: "Your AI-Powered Dev Companion" }), _jsx(Text, { color: "gray" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: '─'.repeat(Math.min(width - 4, 48)) }) }), _jsxs(Box, { marginTop: 0, children: [_jsx(Text, { color: "gray", children: "model " }), _jsx(Text, { color: "cyan", children: MODEL_NAME }), _jsxs(Text, { color: "gray", children: [" ", APP_NAME, " "] }), _jsx(Text, { color: "magenta", children: APP_VERSION })] }), _jsx(Box, { children: _jsx(Text, { color: "gray", children: truncatePath(process.cwd(), maxPath) }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: '─'.repeat(Math.min(width - 4, 48)) }) }), _jsxs(Box, { children: [_jsx(Text, { color: "gray", children: "/" }), _jsx(Text, { color: "cyan", children: "init" }), _jsx(Text, { color: "gray", children: " \u521D\u59CB\u5316 " }), _jsx(Text, { color: "gray", children: "/" }), _jsx(Text, { color: "cyan", children: "help" }), _jsx(Text, { color: "gray", children: " \u5E2E\u52A9 " }), _jsx(Text, { color: "gray", children: "/" }), _jsx(Text, { color: "cyan", children: "new" }), _jsx(Text, { color: "gray", children: " \u65B0\u4F1A\u8BDD " }), _jsx(Text, { color: "gray", children: "/" }), _jsx(Text, { color: "cyan", children: "agent" }), _jsx(Text, { color: "gray", children: " \u5207\u6362" })] }), _jsx(Text, { children: ' ' })] }));
23
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, width: width, children: [showLogo && (_jsx(Box, { flexDirection: "column", children: LOGO_LINES.map((line, i) => (_jsx(Text, { color: LOGO_COLORS[i], children: line }, i))) })), _jsxs(Box, { marginTop: 0, children: [_jsx(Text, { color: "gray" }), _jsx(Text, { color: "white", bold: true, children: "Your AI-Powered Dev Companion" }), _jsx(Text, { color: "gray" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "green", children: "\u6B22\u8FCE\u4F7F\u7528 Jarvis\uFF0C\u8BF7\u76F4\u63A5\u8F93\u5165\u4F60\u7684\u95EE\u9898\u6216\u9700\u6C42\u3002" }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: '─'.repeat(Math.min(width - 4, 48)) }) }), _jsxs(Box, { marginTop: 0, children: [_jsx(Text, { color: "gray", children: "model " }), _jsx(Text, { color: "cyan", children: MODEL_NAME }), _jsxs(Text, { color: "gray", children: [" ", APP_NAME, " "] }), _jsx(Text, { color: "magenta", children: APP_VERSION })] }), _jsx(Box, { children: _jsx(Text, { color: "gray", children: truncatePath(process.cwd(), maxPath) }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: '─'.repeat(Math.min(width - 4, 48)) }) }), _jsxs(Box, { children: [_jsx(Text, { color: "gray", children: "/" }), _jsx(Text, { color: "cyan", children: "init" }), _jsx(Text, { color: "gray", children: " \u521D\u59CB\u5316 " }), _jsx(Text, { color: "gray", children: "/" }), _jsx(Text, { color: "cyan", children: "help" }), _jsx(Text, { color: "gray", children: " \u5E2E\u52A9 " }), _jsx(Text, { color: "gray", children: "/" }), _jsx(Text, { color: "cyan", children: "new" }), _jsx(Text, { color: "gray", children: " \u65B0\u4F1A\u8BDD " }), _jsx(Text, { color: "gray", children: "/" }), _jsx(Text, { color: "cyan", children: "agent" }), _jsx(Text, { color: "gray", children: " \u5207\u6362" })] }), _jsx(Text, { children: ' ' })] }));
24
24
  }
25
25
  export default React.memo(WelcomeHeader);
@@ -0,0 +1,10 @@
1
+ export declare const DREAM_FILE_PATH: string;
2
+ export declare function ensureDreamHomeDir(): string;
3
+ export declare function ensureDreamFile(): string;
4
+ export declare function readDream(): string;
5
+ export declare function readDreamForPrompt(maxChars?: number): string;
6
+ export declare function initializeDreamCache(): string;
7
+ export declare function getCachedDream(): string;
8
+ export declare function replaceDream(content: string, options?: {
9
+ updateCache?: boolean;
10
+ }): void;
@@ -0,0 +1,60 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ export const DREAM_FILE_PATH = path.join(os.homedir(), '.jarvis', 'DREAM.md');
5
+ const DREAM_FILE_HEADER = [
6
+ '# Jarvis 梦境',
7
+ '',
8
+ '> 这里记录 Jarvis 在闲置时对当前会话、用户画像与长期记忆的回顾与发散。',
9
+ '> 内容用于塑造更稳定的表达气质、关注点与人格倾向,不应包含敏感信息或伪造事实。',
10
+ '',
11
+ ].join('\n');
12
+ let cachedDream = '';
13
+ export function ensureDreamHomeDir() {
14
+ const dir = path.dirname(DREAM_FILE_PATH);
15
+ if (!fs.existsSync(dir)) {
16
+ fs.mkdirSync(dir, { recursive: true });
17
+ }
18
+ return dir;
19
+ }
20
+ export function ensureDreamFile() {
21
+ ensureDreamHomeDir();
22
+ if (!fs.existsSync(DREAM_FILE_PATH)) {
23
+ fs.writeFileSync(DREAM_FILE_PATH, DREAM_FILE_HEADER, 'utf-8');
24
+ }
25
+ return DREAM_FILE_PATH;
26
+ }
27
+ export function readDream() {
28
+ try {
29
+ ensureDreamFile();
30
+ return fs.readFileSync(DREAM_FILE_PATH, 'utf-8').trim();
31
+ }
32
+ catch {
33
+ return '';
34
+ }
35
+ }
36
+ export function readDreamForPrompt(maxChars = 8000) {
37
+ const content = readDream();
38
+ if (!content)
39
+ return '';
40
+ if (content.length <= maxChars)
41
+ return content;
42
+ return `...[已截断,仅保留最近 ${maxChars} 字符]\n${content.slice(-maxChars)}`;
43
+ }
44
+ export function initializeDreamCache() {
45
+ cachedDream = readDreamForPrompt();
46
+ return cachedDream;
47
+ }
48
+ export function getCachedDream() {
49
+ return cachedDream;
50
+ }
51
+ export function replaceDream(content, options) {
52
+ ensureDreamFile();
53
+ const normalized = content.trim();
54
+ const finalContent = normalized || DREAM_FILE_HEADER.trimEnd();
55
+ fs.writeFileSync(DREAM_FILE_PATH, `${finalContent}\n`, 'utf-8');
56
+ if (options?.updateCache) {
57
+ cachedDream = readDreamForPrompt();
58
+ }
59
+ }
60
+ initializeDreamCache();
@@ -1,4 +1,8 @@
1
1
  export declare const USER_PROFILE_PATH: string;
2
2
  export declare function ensureJarvisHomeDir(): string;
3
3
  export declare function readUserProfile(): string;
4
- export declare function writeUserProfile(content: string): void;
4
+ export declare function initializeUserProfileCache(): string;
5
+ export declare function getCachedUserProfile(): string;
6
+ export declare function writeUserProfile(content: string, options?: {
7
+ updateCache?: boolean;
8
+ }): void;