@wangzhizhi/remi 0.1.214 → 0.1.224
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 +1 -1
- package/dist/help.js +10 -4
- package/dist/i18n.js +101 -37
- package/dist/repl.js +9 -3
- package/dist/statusCard.js +131 -0
- package/dist/statusline.js +4 -2
- package/dist/style.js +9 -12
- package/dist/tui/RemiApp.js +275 -21
- package/dist/tui/commands.js +20 -5
- package/dist/tui/hooksPanel.js +4 -4
- package/dist/tui/renderers/MessageList.js +227 -30
- package/dist/tui/renderers/PromptBox.js +5 -2
- package/dist/tui/renderers/StatusLine.js +19 -5
- package/dist/tui/statusStats.js +63 -9
- package/dist/tui/theme.js +1 -1
- package/dist/usage.js +3 -0
- package/dist/version.js +1 -1
- package/node_modules/@remi/compact/dist/index.js +5 -3
- package/node_modules/@remi/compact/package.json +1 -1
- package/node_modules/@remi/config/dist/index.js +37 -3
- package/node_modules/@remi/config/package.json +1 -1
- package/node_modules/@remi/core/dist/contextBuilder.js +174 -41
- package/node_modules/@remi/core/dist/directoryOverview.js +12 -4
- package/node_modules/@remi/core/dist/index.js +610 -170
- package/node_modules/@remi/core/dist/responseStyles.js +62 -31
- package/node_modules/@remi/core/package.json +1 -1
- package/node_modules/@remi/hooks/package.json +1 -1
- package/node_modules/@remi/llm/dist/index.js +41 -6
- package/node_modules/@remi/llm/package.json +1 -1
- package/node_modules/@remi/memory/dist/index.js +166 -46
- package/node_modules/@remi/memory/package.json +1 -1
- package/node_modules/@remi/permissions/package.json +1 -1
- package/node_modules/@remi/sessions/dist/index.js +3 -2
- package/node_modules/@remi/sessions/package.json +1 -1
- package/node_modules/@remi/skills/dist/index.js +5 -1
- package/node_modules/@remi/skills/package.json +1 -1
- package/node_modules/@remi/terminal-markdown/dist/index.js +68 -7
- package/node_modules/@remi/terminal-markdown/package.json +1 -1
- package/node_modules/@remi/tools/dist/index.js +65 -3
- package/node_modules/@remi/tools/package.json +1 -1
- package/package.json +12 -12
- package/prompt/base-system.md +86 -8
- package/prompt/tool-use-system.md +224 -37
package/README.md
CHANGED
package/dist/help.js
CHANGED
|
@@ -16,14 +16,20 @@ export function formatHelp() {
|
|
|
16
16
|
' remi --version Show version',
|
|
17
17
|
'',
|
|
18
18
|
'TUI commands:',
|
|
19
|
+
' /accent-color Choose accent color',
|
|
20
|
+
' /code-panel Choose code panel colors',
|
|
21
|
+
' /compact Compact this session',
|
|
22
|
+
' /exit Exit',
|
|
23
|
+
' /hooks Browse configured hooks',
|
|
19
24
|
' /init Create AGENTS.md repository instructions',
|
|
25
|
+
' /language Choose interface language',
|
|
20
26
|
' /model Show configured model aliases',
|
|
21
27
|
' /new Start a new session',
|
|
22
28
|
' /permissions Choose permissions and approval behavior',
|
|
29
|
+
' /personality Choose response personality',
|
|
30
|
+
' /skills Manage local Remi skills',
|
|
31
|
+
' /stat Show global usage stats',
|
|
32
|
+
' /status Show session status',
|
|
23
33
|
' /statusline Choose status line fields',
|
|
24
|
-
' /style Choose response style',
|
|
25
|
-
' /code-panel Choose code panel colors',
|
|
26
|
-
' /accent-color Choose accent color',
|
|
27
|
-
' /exit Exit',
|
|
28
34
|
].join('\n');
|
|
29
35
|
}
|
package/dist/i18n.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { loadRemiConfig, readProjectRemiConfig, writeProjectRemiConfig } from '@remi/config';
|
|
2
2
|
export const defaultLanguage = 'en';
|
|
3
3
|
export const supportedLanguages = [
|
|
4
|
-
{ code: 'zh-Hans', label: '简体中文', description: '使用简体中文显示 Remi 系统提示语。' },
|
|
5
4
|
{ code: 'en', label: 'English', description: 'Use English for Remi system messages.' },
|
|
5
|
+
{ code: 'zh-Hans', label: '简体中文', description: '使用简体中文显示 Remi 系统提示语。' },
|
|
6
6
|
];
|
|
7
7
|
const dictionaries = {
|
|
8
8
|
en: {
|
|
@@ -10,10 +10,11 @@ const dictionaries = {
|
|
|
10
10
|
'slash.model.description': 'Choose what model and reasoning effort to use',
|
|
11
11
|
'slash.permissions.description': 'Choose model permissions and approval behavior',
|
|
12
12
|
'slash.hooks.description': 'Browse configured hooks',
|
|
13
|
-
'slash.
|
|
13
|
+
'slash.personality.description': 'Choose Remi personality',
|
|
14
14
|
'slash.codePanel.description': 'Choose code panel colors',
|
|
15
15
|
'slash.accentColor.description': 'Choose accent color',
|
|
16
16
|
'slash.stat.description': 'Show global usage stats',
|
|
17
|
+
'slash.status.description': 'Show session status',
|
|
17
18
|
'slash.statusline.description': 'Choose status line fields',
|
|
18
19
|
'slash.skills.description': 'Manage and load local Remi skills',
|
|
19
20
|
'slash.language.description': 'Choose interface language',
|
|
@@ -22,21 +23,21 @@ const dictionaries = {
|
|
|
22
23
|
'slash.exit.description': 'Exit Remi',
|
|
23
24
|
'command.unknown': 'Unknown command: {{command}}',
|
|
24
25
|
'version.message': 'Remi is running version {{version}}.',
|
|
25
|
-
'style.panel.title': 'Select
|
|
26
|
-
'style.panel.subtitle': 'Session
|
|
26
|
+
'style.panel.title': 'Select Personality',
|
|
27
|
+
'style.panel.subtitle': 'Session personality for model replies. It does not change model selection.',
|
|
27
28
|
'style.current': 'current',
|
|
28
|
-
'style.selected': '
|
|
29
|
-
'style.saved': '
|
|
30
|
-
'style.
|
|
31
|
-
'style.
|
|
32
|
-
'style.description.
|
|
33
|
-
'style.description.
|
|
34
|
-
'style.description.mentor': '
|
|
35
|
-
'style.description.minimal': '
|
|
29
|
+
'style.selected': 'Personality selected: {{label}}.',
|
|
30
|
+
'style.saved': 'Personality saved: {{label}}.',
|
|
31
|
+
'style.closed': 'Personality selection closed without saving.',
|
|
32
|
+
'style.unknown': 'Unknown personality: {{style}}. Run /personality to choose.',
|
|
33
|
+
'style.description.friendly': 'Warm, collaborative, and helpful.',
|
|
34
|
+
'style.description.pragmatic': 'Concise, task-focused, and direct.',
|
|
35
|
+
'style.description.mentor': 'Explains reasoning and tradeoffs.',
|
|
36
|
+
'style.description.minimal': 'Fewest words possible.',
|
|
36
37
|
'theme.panel.title': 'Select Code Panel',
|
|
37
38
|
'theme.panel.subtitle': 'Move up/down to live preview code colors.',
|
|
38
39
|
'theme.panel.search': 'Type to filter themes',
|
|
39
|
-
'theme.panel.help': 'Press enter to confirm
|
|
40
|
+
'theme.panel.help': 'Press enter to confirm; Ctrl-C/Esc to go back',
|
|
40
41
|
'theme.current': 'current',
|
|
41
42
|
'theme.selected': 'Code panel selected: {{label}}.',
|
|
42
43
|
'theme.saved': 'Code panel saved: {{label}}.',
|
|
@@ -45,7 +46,7 @@ const dictionaries = {
|
|
|
45
46
|
'accentColor.panel.title': 'Select Accent Color',
|
|
46
47
|
'accentColor.panel.subtitle': 'Move up/down to preview interface accent.',
|
|
47
48
|
'accentColor.current': 'current',
|
|
48
|
-
'accentColor.help': 'Press enter to confirm
|
|
49
|
+
'accentColor.help': 'Press enter to confirm; Ctrl-C/Esc to go back',
|
|
49
50
|
'accentColor.selected': 'Accent color selected: {{label}}.',
|
|
50
51
|
'accentColor.saved': 'Accent color saved: {{label}}.',
|
|
51
52
|
'accentColor.closed': 'Accent color settings closed without saving.',
|
|
@@ -54,16 +55,19 @@ const dictionaries = {
|
|
|
54
55
|
'language.panel.subtitle': 'Choose the language for Remi system messages.',
|
|
55
56
|
'language.current': 'current',
|
|
56
57
|
'language.saved': 'Language saved: {{label}}.',
|
|
58
|
+
'language.closed': 'Language selection closed without saving.',
|
|
57
59
|
'language.unknown': 'Unknown language: {{language}}. Run /language to choose.',
|
|
58
60
|
'model.panel.title': 'Select Model and Effort',
|
|
59
61
|
'model.panel.subtitle': 'Current profile {{profile}} / main uses {{model}} / effort {{effort}}',
|
|
60
62
|
'model.current': 'current',
|
|
61
63
|
'model.selected': 'Model selected: {{label}}.',
|
|
64
|
+
'model.closed': 'Model selection closed without saving.',
|
|
62
65
|
'model.error.load': 'Failed to load model configuration',
|
|
63
66
|
'permissions.panel.title': 'Update Model Permissions',
|
|
64
67
|
'permissions.panel.subtitle': 'Choose how Remi handles local files, shell execution, and approval requests.',
|
|
65
68
|
'permissions.current': 'current',
|
|
66
69
|
'permissions.saved': 'Permission profile saved: {{label}}.',
|
|
70
|
+
'permissions.closed': 'Permission profile selection closed without saving.',
|
|
67
71
|
'permissions.unknown': 'Unknown permission profile: {{profile}}. Run /permissions to choose.',
|
|
68
72
|
'permissions.profile.default.label': 'Default',
|
|
69
73
|
'permissions.profile.default.description': 'Remi can read and edit files in the current workspace, and requests approval for shell execution, internet, or other files.',
|
|
@@ -107,29 +111,53 @@ const dictionaries = {
|
|
|
107
111
|
'permission.savedRule': 'Allowed this session for commands that start with `{{prefix}}`.',
|
|
108
112
|
'permission.savedRules': 'Allowed this session for {{rule}}.',
|
|
109
113
|
'permission.savedPersistentRules': 'Always allowed {{rule}}.',
|
|
114
|
+
'askUserQuestion.customLabel': 'Type your decision',
|
|
115
|
+
'askUserQuestion.customPlaceholder': 'Type something',
|
|
116
|
+
'askUserQuestion.help': 'Enter to select · ↑/↓ to navigate · Esc/Ctrl-C to cancel',
|
|
117
|
+
'askUserQuestion.selected': 'Decision selected: {{label}}.',
|
|
118
|
+
'askUserQuestion.custom': 'Decision typed: {{text}}.',
|
|
119
|
+
'askUserQuestion.cancelled': 'Decision question cancelled.',
|
|
110
120
|
'statusline.panel.title': 'Configure Status Line',
|
|
111
121
|
'statusline.panel.subtitle': 'Select which items to display in the status line.',
|
|
112
122
|
'statusline.panel.search': 'Type to search',
|
|
113
|
-
'statusline.panel.help': 'Press space to toggle; ↑/↓ to move; enter to save and close;
|
|
123
|
+
'statusline.panel.help': 'Press space to toggle; ↑/↓ to move; enter to save and close; Ctrl-C/Esc to close',
|
|
114
124
|
'statusline.saved': 'Status line saved: {{items}}.',
|
|
115
125
|
'statusline.closed': 'Status line settings closed without saving.',
|
|
116
126
|
'statusline.context': 'Context',
|
|
117
127
|
'statusline.used': 'used',
|
|
118
128
|
'statusline.input': 'input',
|
|
119
129
|
'statusline.output': 'output',
|
|
130
|
+
'statusline.cacheHit': 'cache hit',
|
|
120
131
|
'statusline.catalog.model': 'Current main model',
|
|
121
132
|
'statusline.catalog.cwd': 'Current working directory',
|
|
122
133
|
'statusline.catalog.git-branch': 'Current Git branch (hidden when unavailable)',
|
|
123
134
|
'statusline.catalog.branch-changes': 'Working tree line changes, such as +8 -1',
|
|
124
135
|
'statusline.catalog.context-remaining': 'Percentage of context window remaining',
|
|
125
|
-
'statusline.catalog.used-tokens': '
|
|
126
|
-
'statusline.catalog.total-input-tokens': '
|
|
136
|
+
'statusline.catalog.used-tokens': 'Non-cached input plus output tokens in the current session',
|
|
137
|
+
'statusline.catalog.total-input-tokens': 'Non-cached input tokens with cached input shown separately',
|
|
127
138
|
'statusline.catalog.total-output-tokens': 'Total output tokens used in the current session',
|
|
139
|
+
'statusline.catalog.cache-hit-rate': 'Cached input share for the current session',
|
|
128
140
|
'statusline.catalog.run-state': 'Compact session run-state text',
|
|
129
141
|
'statusline.catalog.permissions': 'Current permission profile',
|
|
142
|
+
'status.card.model': 'Model',
|
|
143
|
+
'status.card.directory': 'Directory',
|
|
144
|
+
'status.card.permissions': 'Permissions',
|
|
145
|
+
'status.card.agents': 'Agents.md',
|
|
146
|
+
'status.card.session': 'Session',
|
|
147
|
+
'status.card.personality': 'Personality',
|
|
148
|
+
'status.card.gitBranch': 'Git branch',
|
|
149
|
+
'status.card.reasoning': 'reasoning',
|
|
150
|
+
'status.card.unconfigured': 'unconfigured',
|
|
151
|
+
'status.card.noSession': 'none',
|
|
152
|
+
'status.card.noBranch': 'none',
|
|
153
|
+
'status.card.none': 'none',
|
|
154
|
+
'status.card.permissions.workspaceOnRequest': 'Workspace (on-request)',
|
|
155
|
+
'status.card.permissions.workspaceAutoReview': 'Workspace (auto-review)',
|
|
156
|
+
'status.card.permissions.fullAccess': 'Full Access (bypass)',
|
|
157
|
+
'status.card.permissions.readOnly': 'Read Only',
|
|
130
158
|
'skills.panel.title': 'Skills',
|
|
131
159
|
'skills.panel.subtitle': 'Choose an action',
|
|
132
|
-
'skills.panel.help': 'Press enter to confirm
|
|
160
|
+
'skills.panel.help': 'Press enter to confirm; Ctrl-C/Esc to go back',
|
|
133
161
|
'skills.panel.list': 'List skills',
|
|
134
162
|
'skills.panel.listDescription': 'Tip: press $ to open this list directly.',
|
|
135
163
|
'skills.panel.toggle': 'Enable/Disable Skills',
|
|
@@ -145,6 +173,7 @@ const dictionaries = {
|
|
|
145
173
|
'tool.action.list': 'List',
|
|
146
174
|
'tool.action.read': 'Read',
|
|
147
175
|
'tool.action.search': 'Search',
|
|
176
|
+
'tool.action.fetch': 'Fetch',
|
|
148
177
|
'tool.action.find': 'Find',
|
|
149
178
|
'tool.action.create': 'Create',
|
|
150
179
|
'tool.action.exists': 'Exists',
|
|
@@ -152,18 +181,21 @@ const dictionaries = {
|
|
|
152
181
|
'tool.action.edit': 'Edit',
|
|
153
182
|
'tool.action.delete': 'Delete',
|
|
154
183
|
'tool.action.run': 'Run',
|
|
184
|
+
'tool.action.ask': 'Ask',
|
|
155
185
|
'tool.action.planUpdate': 'Plan update',
|
|
156
186
|
'tool.action.done': 'Done',
|
|
157
187
|
'tool.action.completed': 'Completed',
|
|
158
188
|
'tool.running.listing': 'Listing',
|
|
159
189
|
'tool.running.reading': 'Reading',
|
|
160
190
|
'tool.running.searching': 'Searching',
|
|
191
|
+
'tool.running.fetching': 'Fetching',
|
|
161
192
|
'tool.running.finding': 'Finding',
|
|
162
193
|
'tool.running.creating': 'Creating',
|
|
163
194
|
'tool.running.writing': 'Writing',
|
|
164
195
|
'tool.running.editing': 'Editing',
|
|
165
196
|
'tool.running.deleting': 'Deleting',
|
|
166
197
|
'tool.running.running': 'Running',
|
|
198
|
+
'tool.running.asking': 'Asking',
|
|
167
199
|
'tool.running.updatingPlan': 'Updating plan',
|
|
168
200
|
'tool.count.directory.one': 'directory',
|
|
169
201
|
'tool.count.directory.other': 'directories',
|
|
@@ -192,7 +224,7 @@ const dictionaries = {
|
|
|
192
224
|
'prompt.working': 'Working...',
|
|
193
225
|
'queue.title': 'Messages to be submitted after next tool call',
|
|
194
226
|
'queue.hint': 'press esc to interrupt and send immediately',
|
|
195
|
-
'messages.empty': '
|
|
227
|
+
'messages.empty': '',
|
|
196
228
|
'working.label': 'Working',
|
|
197
229
|
'working.interrupt': 'esc to interrupt',
|
|
198
230
|
'thinking.label': 'Thinking...',
|
|
@@ -221,10 +253,11 @@ const dictionaries = {
|
|
|
221
253
|
'slash.model.description': '选择本次会话使用的模型和推理强度',
|
|
222
254
|
'slash.permissions.description': '选择模型权限和审批行为',
|
|
223
255
|
'slash.hooks.description': '查看已配置 hooks',
|
|
224
|
-
'slash.
|
|
256
|
+
'slash.personality.description': '选择 Remi 的回复人格',
|
|
225
257
|
'slash.codePanel.description': '选择代码面板配色',
|
|
226
258
|
'slash.accentColor.description': '选择高亮色',
|
|
227
259
|
'slash.stat.description': '查看全局使用统计',
|
|
260
|
+
'slash.status.description': '查看当前会话状态',
|
|
228
261
|
'slash.statusline.description': '选择底部状态栏字段',
|
|
229
262
|
'slash.skills.description': '管理和加载本地 Remi skills',
|
|
230
263
|
'slash.language.description': '选择界面语言',
|
|
@@ -233,21 +266,21 @@ const dictionaries = {
|
|
|
233
266
|
'slash.exit.description': '退出 Remi',
|
|
234
267
|
'command.unknown': '未知命令:{{command}}',
|
|
235
268
|
'version.message': 'Remi 当前版本是 {{version}}。',
|
|
236
|
-
'style.panel.title': '
|
|
237
|
-
'style.panel.subtitle': '
|
|
269
|
+
'style.panel.title': '选择回复人格',
|
|
270
|
+
'style.panel.subtitle': '只影响模型回复人格,不改变模型选择。',
|
|
238
271
|
'style.current': '当前',
|
|
239
|
-
'style.selected': '
|
|
240
|
-
'style.saved': '
|
|
241
|
-
'style.
|
|
242
|
-
'style.
|
|
243
|
-
'style.description.
|
|
244
|
-
'style.description.
|
|
245
|
-
'style.description.mentor': '
|
|
246
|
-
'style.description.minimal': '
|
|
272
|
+
'style.selected': '已选择回复人格:{{label}}。',
|
|
273
|
+
'style.saved': '已保存回复人格:{{label}}。',
|
|
274
|
+
'style.closed': '已关闭回复人格选择,未保存更改。',
|
|
275
|
+
'style.unknown': '未知回复人格:{{style}}。运行 /personality 选择。',
|
|
276
|
+
'style.description.friendly': '温暖、协作、乐于帮助。',
|
|
277
|
+
'style.description.pragmatic': '简洁、聚焦任务、直接。',
|
|
278
|
+
'style.description.mentor': '说明推理和取舍。',
|
|
279
|
+
'style.description.minimal': '尽量少字回答。',
|
|
247
280
|
'theme.panel.title': '选择代码面板',
|
|
248
281
|
'theme.panel.subtitle': '上下移动以实时预览代码配色。',
|
|
249
282
|
'theme.panel.search': '输入以筛选主题',
|
|
250
|
-
'theme.panel.help': '
|
|
283
|
+
'theme.panel.help': '按回车确认;Ctrl-C/Esc 返回',
|
|
251
284
|
'theme.current': '当前',
|
|
252
285
|
'theme.selected': '已选择代码面板:{{label}}。',
|
|
253
286
|
'theme.saved': '已保存代码面板:{{label}}。',
|
|
@@ -256,7 +289,7 @@ const dictionaries = {
|
|
|
256
289
|
'accentColor.panel.title': '选择高亮色',
|
|
257
290
|
'accentColor.panel.subtitle': '上下移动以预览界面高亮色。',
|
|
258
291
|
'accentColor.current': '当前',
|
|
259
|
-
'accentColor.help': '
|
|
292
|
+
'accentColor.help': '按回车确认;Ctrl-C/Esc 返回',
|
|
260
293
|
'accentColor.selected': '已选择高亮色:{{label}}。',
|
|
261
294
|
'accentColor.saved': '已保存高亮色:{{label}}。',
|
|
262
295
|
'accentColor.closed': '已关闭高亮色设置,未保存更改。',
|
|
@@ -265,16 +298,19 @@ const dictionaries = {
|
|
|
265
298
|
'language.panel.subtitle': '选择 Remi 系统提示语使用的语言。',
|
|
266
299
|
'language.current': '当前',
|
|
267
300
|
'language.saved': '已保存语言:{{label}}。',
|
|
301
|
+
'language.closed': '已关闭语言选择,未保存更改。',
|
|
268
302
|
'language.unknown': '未知语言:{{language}}。运行 /language 选择。',
|
|
269
303
|
'model.panel.title': '选择模型和推理强度',
|
|
270
304
|
'model.panel.subtitle': '当前 profile {{profile}} / main 使用 {{model}} / effort {{effort}}',
|
|
271
305
|
'model.current': '当前',
|
|
272
306
|
'model.selected': '已选择模型:{{label}}。',
|
|
307
|
+
'model.closed': '已关闭模型选择,未保存更改。',
|
|
273
308
|
'model.error.load': '加载模型配置失败',
|
|
274
309
|
'permissions.panel.title': '更新模型权限',
|
|
275
310
|
'permissions.panel.subtitle': '选择 Remi 如何处理本地文件、shell 执行和审批请求。',
|
|
276
311
|
'permissions.current': '当前',
|
|
277
312
|
'permissions.saved': '已保存权限档位:{{label}}。',
|
|
313
|
+
'permissions.closed': '已关闭权限档位选择,未保存更改。',
|
|
278
314
|
'permissions.unknown': '未知权限档位:{{profile}}。运行 /permissions 选择。',
|
|
279
315
|
'permissions.profile.default.label': 'Default',
|
|
280
316
|
'permissions.profile.default.description': 'Remi 可以读写当前工作区文件;执行 shell、联网或访问其他文件时需要审批。',
|
|
@@ -318,29 +354,53 @@ const dictionaries = {
|
|
|
318
354
|
'permission.savedRule': '本 session 已允许以 `{{prefix}}` 开头的命令。',
|
|
319
355
|
'permission.savedRules': '本 session 已允许{{rule}}。',
|
|
320
356
|
'permission.savedPersistentRules': '已始终允许{{rule}}。',
|
|
357
|
+
'askUserQuestion.customLabel': '输入你的决定',
|
|
358
|
+
'askUserQuestion.customPlaceholder': '输入内容',
|
|
359
|
+
'askUserQuestion.help': 'Enter 选择 · ↑/↓ 移动 · Esc/Ctrl-C 取消',
|
|
360
|
+
'askUserQuestion.selected': '已选择:{{label}}。',
|
|
361
|
+
'askUserQuestion.custom': '已输入决策:{{text}}。',
|
|
362
|
+
'askUserQuestion.cancelled': '已取消决策问题。',
|
|
321
363
|
'statusline.panel.title': '配置状态栏',
|
|
322
364
|
'statusline.panel.subtitle': '选择底部状态栏展示哪些字段。',
|
|
323
365
|
'statusline.panel.search': '输入以搜索',
|
|
324
|
-
'statusline.panel.help': '空格切换;↑/↓ 移动;回车保存并关闭;
|
|
366
|
+
'statusline.panel.help': '空格切换;↑/↓ 移动;回车保存并关闭;Ctrl-C/Esc 关闭',
|
|
325
367
|
'statusline.saved': '已保存状态栏字段:{{items}}。',
|
|
326
368
|
'statusline.closed': '已关闭状态栏设置,未保存更改。',
|
|
327
369
|
'statusline.context': 'Context',
|
|
328
370
|
'statusline.used': 'used',
|
|
329
371
|
'statusline.input': 'input',
|
|
330
372
|
'statusline.output': 'output',
|
|
373
|
+
'statusline.cacheHit': '缓存命中',
|
|
331
374
|
'statusline.catalog.model': '当前 main 模型',
|
|
332
375
|
'statusline.catalog.cwd': '当前工作目录',
|
|
333
376
|
'statusline.catalog.git-branch': '当前 Git 分支(不可用时隐藏)',
|
|
334
377
|
'statusline.catalog.branch-changes': '工作区行级改动,例如 +8 -1',
|
|
335
378
|
'statusline.catalog.context-remaining': '上下文窗口剩余百分比',
|
|
336
|
-
'statusline.catalog.used-tokens': '
|
|
337
|
-
'statusline.catalog.total-input-tokens': '
|
|
379
|
+
'statusline.catalog.used-tokens': '当前会话非缓存 input + output token',
|
|
380
|
+
'statusline.catalog.total-input-tokens': '当前会话非缓存 input token,cached 单独显示',
|
|
338
381
|
'statusline.catalog.total-output-tokens': '当前会话 output token 总数',
|
|
382
|
+
'statusline.catalog.cache-hit-rate': '当前会话 cached input 占比',
|
|
339
383
|
'statusline.catalog.run-state': '紧凑的会话运行状态文本',
|
|
340
384
|
'statusline.catalog.permissions': '当前权限档位',
|
|
385
|
+
'status.card.model': '模型',
|
|
386
|
+
'status.card.directory': '目录',
|
|
387
|
+
'status.card.permissions': '权限',
|
|
388
|
+
'status.card.agents': 'Agents.md',
|
|
389
|
+
'status.card.session': 'Session',
|
|
390
|
+
'status.card.personality': 'Personality',
|
|
391
|
+
'status.card.gitBranch': 'Git 分支',
|
|
392
|
+
'status.card.reasoning': '推理',
|
|
393
|
+
'status.card.unconfigured': '未配置',
|
|
394
|
+
'status.card.noSession': '无',
|
|
395
|
+
'status.card.noBranch': '无',
|
|
396
|
+
'status.card.none': '无',
|
|
397
|
+
'status.card.permissions.workspaceOnRequest': 'Workspace(需要确认)',
|
|
398
|
+
'status.card.permissions.workspaceAutoReview': 'Workspace(自动审查)',
|
|
399
|
+
'status.card.permissions.fullAccess': 'Full Access(bypass)',
|
|
400
|
+
'status.card.permissions.readOnly': '只读',
|
|
341
401
|
'skills.panel.title': 'Skills',
|
|
342
402
|
'skills.panel.subtitle': '选择操作',
|
|
343
|
-
'skills.panel.help': '
|
|
403
|
+
'skills.panel.help': '按回车确认;Ctrl-C/Esc 返回',
|
|
344
404
|
'skills.panel.list': '列出 skills',
|
|
345
405
|
'skills.panel.listDescription': '提示:按 $ 可直接打开这个列表。',
|
|
346
406
|
'skills.panel.toggle': '启用/禁用 Skills',
|
|
@@ -356,6 +416,7 @@ const dictionaries = {
|
|
|
356
416
|
'tool.action.list': '列出',
|
|
357
417
|
'tool.action.read': '读取',
|
|
358
418
|
'tool.action.search': '搜索',
|
|
419
|
+
'tool.action.fetch': '抓取',
|
|
359
420
|
'tool.action.find': '查找',
|
|
360
421
|
'tool.action.create': '创建',
|
|
361
422
|
'tool.action.exists': '已存在',
|
|
@@ -363,18 +424,21 @@ const dictionaries = {
|
|
|
363
424
|
'tool.action.edit': '修改',
|
|
364
425
|
'tool.action.delete': '删除',
|
|
365
426
|
'tool.action.run': '执行',
|
|
427
|
+
'tool.action.ask': '询问',
|
|
366
428
|
'tool.action.planUpdate': '更新计划',
|
|
367
429
|
'tool.action.done': '完成',
|
|
368
430
|
'tool.action.completed': '完成',
|
|
369
431
|
'tool.running.listing': '正在列出',
|
|
370
432
|
'tool.running.reading': '正在读取',
|
|
371
433
|
'tool.running.searching': '正在搜索',
|
|
434
|
+
'tool.running.fetching': '正在抓取',
|
|
372
435
|
'tool.running.finding': '正在查找',
|
|
373
436
|
'tool.running.creating': '正在创建',
|
|
374
437
|
'tool.running.writing': '正在写入',
|
|
375
438
|
'tool.running.editing': '正在修改',
|
|
376
439
|
'tool.running.deleting': '正在删除',
|
|
377
440
|
'tool.running.running': '正在执行',
|
|
441
|
+
'tool.running.asking': '正在询问',
|
|
378
442
|
'tool.running.updatingPlan': '正在更新计划',
|
|
379
443
|
'tool.count.directory.one': '个目录',
|
|
380
444
|
'tool.count.directory.other': '个目录',
|
|
@@ -403,7 +467,7 @@ const dictionaries = {
|
|
|
403
467
|
'prompt.working': '处理中...',
|
|
404
468
|
'queue.title': '排队消息将在下一次工具调用后提交',
|
|
405
469
|
'queue.hint': '按 esc 可中断当前请求并立即发送',
|
|
406
|
-
'messages.empty': '
|
|
470
|
+
'messages.empty': '',
|
|
407
471
|
'working.label': '处理中',
|
|
408
472
|
'working.interrupt': 'esc 中断',
|
|
409
473
|
'thinking.label': '思考中...',
|
package/dist/repl.js
CHANGED
|
@@ -15,6 +15,7 @@ import { formatResponseStyleChanged, formatResponseStyleList, parseResponseStyle
|
|
|
15
15
|
import { formatSyntaxThemeChanged, formatSyntaxThemeList, parseSyntaxThemeArg, resolveConfiguredSyntaxTheme, saveProjectSyntaxTheme, } from './syntaxTheme.js';
|
|
16
16
|
import { addTokenUsage, createEmptyTokenUsage, formatTokenUsage } from './usage.js';
|
|
17
17
|
import { version } from './version.js';
|
|
18
|
+
import { createStatusCardData, formatPlainStatusCard } from './statusCard.js';
|
|
18
19
|
export async function startRepl(options = {}) {
|
|
19
20
|
const out = options.output ?? output;
|
|
20
21
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -45,7 +46,7 @@ export async function startRepl(options = {}) {
|
|
|
45
46
|
}
|
|
46
47
|
};
|
|
47
48
|
write(`Remi ${version}`);
|
|
48
|
-
write('Commands: /
|
|
49
|
+
write('Commands: /accent-color, /code-panel, /exit, /init, /language, /model, /new, /permissions, /personality, /status');
|
|
49
50
|
prompt();
|
|
50
51
|
for await (const rawLine of rl) {
|
|
51
52
|
const line = rawLine.trim();
|
|
@@ -104,8 +105,8 @@ export async function startRepl(options = {}) {
|
|
|
104
105
|
prompt();
|
|
105
106
|
continue;
|
|
106
107
|
}
|
|
107
|
-
if (line === '/
|
|
108
|
-
const args = line.slice('/
|
|
108
|
+
if (line === '/personality' || line.startsWith('/personality ')) {
|
|
109
|
+
const args = line.slice('/personality'.length).trim().split(/\s+/).filter(Boolean);
|
|
109
110
|
const styleArg = args[0];
|
|
110
111
|
if (!styleArg || styleArg === 'list') {
|
|
111
112
|
write(formatResponseStyleList(responseStyle, language));
|
|
@@ -124,6 +125,11 @@ export async function startRepl(options = {}) {
|
|
|
124
125
|
prompt();
|
|
125
126
|
continue;
|
|
126
127
|
}
|
|
128
|
+
if (line === '/status') {
|
|
129
|
+
write(formatPlainStatusCard(createStatusCardData({ cwd, sessionId, responseStyle, permissionProfile, language })));
|
|
130
|
+
prompt();
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
127
133
|
if (line === '/code-panel' || line.startsWith('/code-panel ')) {
|
|
128
134
|
const args = line.slice('/code-panel'.length).trim().split(/\s+/).filter(Boolean);
|
|
129
135
|
const themeArg = args[0];
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { existsSync, statSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join, parse, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { defaultResponseStyleId, responseStylePresets } from '@remi/core';
|
|
5
|
+
import { loadRemiConfig, normalizePermissionProfile } from '@remi/config';
|
|
6
|
+
import { createModelRouter } from '@remi/llm';
|
|
7
|
+
import { loadGitStatus } from './git.js';
|
|
8
|
+
import { t } from './i18n.js';
|
|
9
|
+
import { version } from './version.js';
|
|
10
|
+
const instructionFileNames = ['AGENTS.md', 'AGENT.md', 'REMI.md'];
|
|
11
|
+
export function createStatusCardData(context) {
|
|
12
|
+
return {
|
|
13
|
+
title: `=^ω^= Remi CLI (v${version})`,
|
|
14
|
+
rows: [
|
|
15
|
+
{ label: t(context.language, 'status.card.model'), value: statusModel(context) },
|
|
16
|
+
{ label: t(context.language, 'status.card.directory'), value: compactPath(context.cwd, 52) },
|
|
17
|
+
{ label: t(context.language, 'status.card.permissions'), value: statusPermissions(context) },
|
|
18
|
+
{ label: t(context.language, 'status.card.agents'), value: projectInstructionFiles(context.cwd, context.language) },
|
|
19
|
+
{ label: t(context.language, 'status.card.session'), value: context.sessionId ?? t(context.language, 'status.card.noSession') },
|
|
20
|
+
{ label: t(context.language, 'status.card.personality'), value: responseStyleLabel(context.responseStyle ?? defaultResponseStyleId) },
|
|
21
|
+
{ label: t(context.language, 'status.card.gitBranch'), value: loadGitStatus(context.cwd)?.branch ?? t(context.language, 'status.card.noBranch') },
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function formatPlainStatusCard(data) {
|
|
26
|
+
const labelWidth = Math.max(1, ...data.rows.map(row => row.label.length));
|
|
27
|
+
return [
|
|
28
|
+
data.title,
|
|
29
|
+
'',
|
|
30
|
+
...data.rows.map(row => `${row.label.padEnd(labelWidth)}: ${row.value}`),
|
|
31
|
+
].join('\n');
|
|
32
|
+
}
|
|
33
|
+
function statusModel(context) {
|
|
34
|
+
try {
|
|
35
|
+
const loaded = loadRemiConfig({ cwd: context.cwd });
|
|
36
|
+
const router = createModelRouter(loaded.config);
|
|
37
|
+
if (context.mainModelAlias) {
|
|
38
|
+
router.switchRoleModel('main', context.mainModelAlias);
|
|
39
|
+
}
|
|
40
|
+
const resolved = router.resolve('main');
|
|
41
|
+
const suffix = resolved.effort ? ` (${t(context.language, 'status.card.reasoning')} ${resolved.effort})` : '';
|
|
42
|
+
return `${resolved.displayName ?? resolved.alias}${suffix}`;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return t(context.language, 'status.card.unconfigured');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function statusPermissions(context) {
|
|
49
|
+
let profile = context.permissionProfile;
|
|
50
|
+
let mode;
|
|
51
|
+
try {
|
|
52
|
+
const loaded = loadRemiConfig({ cwd: context.cwd });
|
|
53
|
+
profile ??= normalizePermissionProfile(loaded.config.permissions);
|
|
54
|
+
mode = loaded.config.permissions?.mode;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
profile ??= 'default';
|
|
58
|
+
}
|
|
59
|
+
if (profile === 'full-access' || mode === 'bypass') {
|
|
60
|
+
return t(context.language, 'status.card.permissions.fullAccess');
|
|
61
|
+
}
|
|
62
|
+
if (mode === 'readonly') {
|
|
63
|
+
return t(context.language, 'status.card.permissions.readOnly');
|
|
64
|
+
}
|
|
65
|
+
if (profile === 'auto-review') {
|
|
66
|
+
return t(context.language, 'status.card.permissions.workspaceAutoReview');
|
|
67
|
+
}
|
|
68
|
+
return t(context.language, 'status.card.permissions.workspaceOnRequest');
|
|
69
|
+
}
|
|
70
|
+
function projectInstructionFiles(cwd, language) {
|
|
71
|
+
const files = findProjectInstructionFiles(cwd);
|
|
72
|
+
return files.length > 0 ? files.map(file => displayInstructionPath(cwd, file)).join(', ') : t(language, 'status.card.none');
|
|
73
|
+
}
|
|
74
|
+
function findProjectInstructionFiles(cwd) {
|
|
75
|
+
const start = existingDirectory(cwd);
|
|
76
|
+
const dirs = [];
|
|
77
|
+
let current = start;
|
|
78
|
+
const root = parse(start).root;
|
|
79
|
+
while (true) {
|
|
80
|
+
dirs.push(current);
|
|
81
|
+
if (current === root) {
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
current = dirname(current);
|
|
85
|
+
}
|
|
86
|
+
return dirs.reverse().flatMap(dir => instructionFileNames.flatMap(name => {
|
|
87
|
+
const file = join(dir, name);
|
|
88
|
+
return isRegularFile(file) ? [file] : [];
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
function existingDirectory(cwd) {
|
|
92
|
+
let current = resolve(cwd);
|
|
93
|
+
const root = parse(current).root;
|
|
94
|
+
while (!existsSync(current) && current !== root) {
|
|
95
|
+
current = dirname(current);
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
return statSync(current).isDirectory() ? current : dirname(current);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return root;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function isRegularFile(path) {
|
|
105
|
+
try {
|
|
106
|
+
return statSync(path).isFile();
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function displayInstructionPath(cwd, file) {
|
|
113
|
+
const relativePath = relative(cwd, file);
|
|
114
|
+
if (relativePath.length > 0 && !relativePath.startsWith('..') && !relativePath.startsWith(sep)) {
|
|
115
|
+
return relativePath;
|
|
116
|
+
}
|
|
117
|
+
return compactPath(file, 48);
|
|
118
|
+
}
|
|
119
|
+
function responseStyleLabel(styleId) {
|
|
120
|
+
return responseStylePresets.find(style => style.id === styleId)?.label ?? styleId;
|
|
121
|
+
}
|
|
122
|
+
function compactPath(path, maxLength) {
|
|
123
|
+
const displayPath = path.replace(homedir(), '~');
|
|
124
|
+
if (displayPath.length <= maxLength) {
|
|
125
|
+
return displayPath;
|
|
126
|
+
}
|
|
127
|
+
const parts = displayPath.split('/').filter(Boolean);
|
|
128
|
+
const tail = parts.slice(-2).join('/');
|
|
129
|
+
const compacted = displayPath.startsWith('~/') ? `~/${tail}` : `.../${tail}`;
|
|
130
|
+
return compacted.length <= maxLength ? compacted : compacted.slice(0, maxLength);
|
|
131
|
+
}
|
package/dist/statusline.js
CHANGED
|
@@ -7,9 +7,10 @@ export const statusLineCatalog = [
|
|
|
7
7
|
{ id: 'git-branch', label: 'git-branch', description: 'Current Git branch (hidden when unavailable)' },
|
|
8
8
|
{ id: 'branch-changes', label: 'branch-changes', description: 'Working tree line changes, such as +8 -1' },
|
|
9
9
|
{ id: 'context-remaining', label: 'context-remaining', description: 'Percentage of context window remaining' },
|
|
10
|
-
{ id: 'used-tokens', label: 'used-tokens', description: '
|
|
11
|
-
{ id: 'total-input-tokens', label: 'total-input-tokens', description: '
|
|
10
|
+
{ id: 'used-tokens', label: 'used-tokens', description: 'Non-cached input plus output tokens in the current session' },
|
|
11
|
+
{ id: 'total-input-tokens', label: 'total-input-tokens', description: 'Non-cached input tokens with cached input shown separately' },
|
|
12
12
|
{ id: 'total-output-tokens', label: 'total-output-tokens', description: 'Total output tokens used in the current session' },
|
|
13
|
+
{ id: 'cache-hit-rate', label: 'cache-hit-rate', description: 'Cached input share for the current session' },
|
|
13
14
|
{ id: 'run-state', label: 'run-state', description: 'Compact session run-state text' },
|
|
14
15
|
{ id: 'permissions', label: 'permissions', description: 'Current permission profile' },
|
|
15
16
|
];
|
|
@@ -22,6 +23,7 @@ const statusLineDescriptionKeys = {
|
|
|
22
23
|
'used-tokens': 'statusline.catalog.used-tokens',
|
|
23
24
|
'total-input-tokens': 'statusline.catalog.total-input-tokens',
|
|
24
25
|
'total-output-tokens': 'statusline.catalog.total-output-tokens',
|
|
26
|
+
'cache-hit-rate': 'statusline.catalog.cache-hit-rate',
|
|
25
27
|
'run-state': 'statusline.catalog.run-state',
|
|
26
28
|
permissions: 'statusline.catalog.permissions',
|
|
27
29
|
};
|
package/dist/style.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import { defaultResponseStyleId, isResponseStyleId, responseStylePresets, } from '@remi/core';
|
|
1
|
+
import { defaultResponseStyleId, isResponseStyleId, normalizeResponseStyleId, responseStylePresets, } from '@remi/core';
|
|
2
2
|
import { loadRemiConfig, readProjectRemiConfig, writeProjectRemiConfig } from '@remi/config';
|
|
3
3
|
import { t } from './i18n.js';
|
|
4
4
|
export function parseResponseStyleArg(value) {
|
|
5
5
|
if (!value) {
|
|
6
6
|
return undefined;
|
|
7
7
|
}
|
|
8
|
-
const normalized = value.toLowerCase();
|
|
9
|
-
if (normalized === '极简') {
|
|
10
|
-
return 'minimal';
|
|
11
|
-
}
|
|
8
|
+
const normalized = value.trim().toLowerCase();
|
|
12
9
|
if (isResponseStyleId(normalized)) {
|
|
13
10
|
return normalized;
|
|
14
11
|
}
|
|
@@ -16,7 +13,7 @@ export function parseResponseStyleArg(value) {
|
|
|
16
13
|
}
|
|
17
14
|
export function formatResponseStyleList(activeStyle = defaultResponseStyleId, language) {
|
|
18
15
|
return [
|
|
19
|
-
'
|
|
16
|
+
'Personalities:',
|
|
20
17
|
...responseStylePresets.map(style => {
|
|
21
18
|
const marker = style.id === activeStyle ? '*' : ' ';
|
|
22
19
|
return ` ${marker} ${style.id.padEnd(8)} ${style.label.padEnd(8)} ${styleDescription(style.id, language)}`;
|
|
@@ -24,7 +21,7 @@ export function formatResponseStyleList(activeStyle = defaultResponseStyleId, la
|
|
|
24
21
|
].join('\n');
|
|
25
22
|
}
|
|
26
23
|
export function resolveConfiguredResponseStyle(cwd) {
|
|
27
|
-
return
|
|
24
|
+
return normalizeResponseStyleId(loadRemiConfig({ cwd }).config.responseStyle);
|
|
28
25
|
}
|
|
29
26
|
export function saveProjectResponseStyle(cwd, styleId) {
|
|
30
27
|
const config = readProjectRemiConfig(cwd);
|
|
@@ -36,13 +33,13 @@ export function formatResponseStyleChanged(styleId, saved = false, language) {
|
|
|
36
33
|
return saved ? t(language, 'style.saved', { label }) : t(language, 'style.selected', { label });
|
|
37
34
|
}
|
|
38
35
|
export function styleDescription(styleId, language) {
|
|
39
|
-
if (styleId === '
|
|
40
|
-
return t(language, 'style.description.
|
|
41
|
-
if (styleId === '
|
|
42
|
-
return t(language, 'style.description.
|
|
36
|
+
if (styleId === 'friendly')
|
|
37
|
+
return t(language, 'style.description.friendly');
|
|
38
|
+
if (styleId === 'pragmatic')
|
|
39
|
+
return t(language, 'style.description.pragmatic');
|
|
43
40
|
if (styleId === 'mentor')
|
|
44
41
|
return t(language, 'style.description.mentor');
|
|
45
42
|
if (styleId === 'minimal')
|
|
46
43
|
return t(language, 'style.description.minimal');
|
|
47
|
-
return t(language, 'style.description.
|
|
44
|
+
return t(language, 'style.description.pragmatic');
|
|
48
45
|
}
|