minimax-status 1.1.1 → 1.1.2

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.
Files changed (3) hide show
  1. package/README.md +25 -20
  2. package/cli/renderer.js +239 -238
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -29,6 +29,7 @@ npm install -g minimax-status
29
29
  ```
30
30
 
31
31
  ### 2. 更新(如果已经安装)
32
+
32
33
  ```bash
33
34
  npm update -g minimax-status
34
35
  ```
@@ -114,6 +115,7 @@ npm run package
114
115
  ```json
115
116
  {
116
117
  "statusLine": {
118
+ "type": "command",
117
119
  "command": "minimax statusline"
118
120
  }
119
121
  }
@@ -130,6 +132,7 @@ npm run package
130
132
  显示格式:`📁 目录 | 🤖 模型 | 上下文窗口 | ↻ 使用率·剩余次数/总数 | ⌛ 重置时间 | 到期时间`
131
133
 
132
134
  **颜色说明**:
135
+
133
136
  - **到期时间**: ≤3天红色 | ≤7天黄色 | >7天绿色
134
137
 
135
138
  ### 上下文窗口显示说明
@@ -143,6 +146,7 @@ npm run package
143
146
  - 例如: `200K` 表示当前模型的上下文窗口大小
144
147
 
145
148
  **智能特性**:
149
+
146
150
  - ✅ 自动解析 Claude Code 转录文件(transcript)
147
151
  - ✅ 支持 Anthropic 和 OpenAI 两种 token 格式
148
152
  - ✅ 正确计算缓存 tokens(cache creation + cache read)
@@ -187,36 +191,36 @@ npm run package
187
191
 
188
192
  ## 命令说明
189
193
 
190
- | 命令 | 描述 | 示例 |
191
- |----------------------|------------------------------------------|----------------------------------|
192
- | `minimax auth` | 设置认证凭据 | `minimax auth <token> <groupId>` |
194
+ | 命令 | 描述 | 示例 |
195
+ | -------------------- | ------------------------------------------- | -------------------------------- |
196
+ | `minimax auth` | 设置认证凭据 | `minimax auth <token> <groupId>` |
193
197
  | `minimax status` | 显示当前使用状态(支持 --compact、--watch) | `minimax status` |
194
- | `minimax bar` | 终端底部持续状态栏 | `minimax bar` |
195
- | `minimax statusline` | Claude Code 状态栏集成 | 用于 Claude Code 配置 |
198
+ | `minimax bar` | 终端底部持续状态栏 | `minimax bar` |
199
+ | `minimax statusline` | Claude Code 状态栏集成 | 用于 Claude Code 配置 |
196
200
 
197
201
  ## 状态说明
198
202
 
199
203
  ### 状态图标
200
204
 
201
- | 元素 | 图标 | 说明 |
202
- |------|------|------|
203
- | 目录 | 📁 | 当前工作目录 |
204
- | 模型 | 🤖 | MiniMax 模型名称 |
205
- | 上下文 | ⚡ | 上下文窗口使用率 |
206
- | 使用量 | ↻ | 使用率·剩余次数/总数 |
207
- | 重置 | ⌛ | 额度重置倒计时 |
208
- | 到期 | - | 订阅到期时间(颜色动态变化) |
205
+ | 元素 | 图标 | 说明 |
206
+ | ------ | ---- | ---------------------------- |
207
+ | 目录 | 📁 | 当前工作目录 |
208
+ | 模型 | 🤖 | MiniMax 模型名称 |
209
+ | 上下文 | ⚡ | 上下文窗口使用率 |
210
+ | 使用量 | ↻ | 使用率·剩余次数/总数 |
211
+ | 重置 | ⌛ | 额度重置倒计时 |
212
+ | 到期 | - | 订阅到期时间(颜色动态变化) |
209
213
 
210
214
  ### 颜色规则
211
215
 
212
- | 场景 | 颜色 | 说明 |
213
- |------|------|------|
214
- | 使用率 < 60% | 绿色 | 正常使用 |
216
+ | 场景 | 颜色 | 说明 |
217
+ | ------------- | ---- | -------- |
218
+ | 使用率 < 60% | 绿色 | 正常使用 |
215
219
  | 使用率 60-85% | 黄色 | 注意使用 |
216
- | 使用率 ≥ 85% | 红色 | 危险状态 |
217
- | 到期 ≤ 3天 | 红色 | 即将到期 |
218
- | 到期 ≤ 7天 | 黄色 | 即将到期 |
219
- | 到期 > 7天 | 绿色 | 订阅正常 |
220
+ | 使用率 ≥ 85% | 红色 | 危险状态 |
221
+ | 到期 ≤ 3天 | 红色 | 即将到期 |
222
+ | 到期 ≤ 7天 | 黄色 | 即将到期 |
223
+ | 到期 > 7天 | 绿色 | 订阅正常 |
220
224
 
221
225
  ## 配置文件
222
226
 
@@ -241,6 +245,7 @@ Claude Code 只需要配置状态栏命令:
241
245
  // ~/.claude/settings.json
242
246
  {
243
247
  "statusLine": {
248
+ "type": "command",
244
249
  "command": "minimax statusline"
245
250
  }
246
251
  }
package/cli/renderer.js CHANGED
@@ -1,238 +1,239 @@
1
- #!/usr/bin/env node
2
-
3
- const chalk = require('chalk').default;
4
-
5
- class Renderer {
6
- constructor() {
7
- this.RESET = '\x1b[0m';
8
- }
9
-
10
- formatTokens(tokens) {
11
- if (tokens >= 1000000) {
12
- return `${(tokens / 1000000).toFixed(1)}M`;
13
- }
14
- if (tokens >= 1000) {
15
- return `${(tokens / 1000).toFixed(1)}k`;
16
- }
17
- return tokens.toString();
18
- }
19
-
20
- formatContextSize(size) {
21
- if (size >= 1000000) {
22
- return `${Math.round(size / 100000) / 10}M`;
23
- }
24
- if (size >= 1000) {
25
- return `${Math.round(size / 1000)}K`;
26
- }
27
- return `${size}`;
28
- }
29
-
30
- formatDuration(ms) {
31
- if (ms < 60000) {
32
- const secs = Math.round(ms / 1000);
33
- return secs < 1 ? '<1s' : `${secs}s`;
34
- }
35
- const mins = Math.floor(ms / 60000);
36
- const secs = Math.round((ms % 60000) / 1000);
37
- return `${mins}m ${secs}s`;
38
- }
39
-
40
- truncatePath(path, maxLen = 20) {
41
- if (!path || path.length <= maxLen) return path;
42
- const parts = path.split(/[/\\]/);
43
- const filename = parts.pop() || path;
44
- if (filename.length >= maxLen) {
45
- return filename.slice(0, maxLen - 3) + '...';
46
- }
47
- return '.../' + filename;
48
- }
49
-
50
- truncateDesc(desc, maxLen = 40) {
51
- if (!desc || desc.length <= maxLen) return desc;
52
- return desc.slice(0, maxLen - 3) + '...';
53
- }
54
-
55
- getStatusColor(percentage) {
56
- if (percentage >= 85) return chalk.red;
57
- if (percentage >= 60) return chalk.yellow;
58
- return chalk.green;
59
- }
60
-
61
- renderSessionLine(data) {
62
- const {
63
- modelName,
64
- currentDir,
65
- usagePercentage,
66
- usage,
67
- remaining,
68
- expiry,
69
- contextUsage,
70
- contextSize,
71
- configCounts,
72
- sessionDuration,
73
- } = data;
74
-
75
- const parts = [];
76
-
77
- if (currentDir) {
78
- parts.push(`${chalk.blue('📁')} ${chalk.cyan(currentDir)}`);
79
- }
80
-
81
- parts.push(`${chalk.magenta('🤖')} ${chalk.magenta(modelName)}`);
82
-
83
- // 上下文窗口在前
84
- if (contextUsage !== null && contextUsage !== undefined) {
85
- const contextPercent = Math.round((contextUsage / contextSize) * 100);
86
- const contextColor = this.getStatusColor(contextPercent);
87
- parts.push(`${contextColor('⚡')} ${contextColor(contextPercent + '%')}`);
88
- parts.push(`${chalk.white(this.formatTokens(contextUsage) + '/' + this.formatContextSize(contextSize))}`);
89
- } else {
90
- parts.push(`${chalk.cyan(this.formatContextSize(contextSize))}`);
91
- }
92
-
93
- // 使用量合并
94
- const usageColor = this.getStatusColor(usagePercentage);
95
- parts.push(`${chalk.yellow('↻')} ${usageColor(usagePercentage + '%')}${chalk.yellow('·')}${chalk.white(usage.remaining + '/' + usage.total)}`);
96
-
97
- const remainingText = remaining.hours > 0
98
- ? `${remaining.hours}h${remaining.minutes}m`
99
- : `${remaining.minutes}m`;
100
- parts.push(`${chalk.yellow('⌛')} ${chalk.white(remainingText)}`);
101
-
102
- if (configCounts.claudeMdCount > 0) {
103
- parts.push(`${chalk.white(configCounts.claudeMdCount + ' CLAUDE.md')}`);
104
- }
105
- if (configCounts.rulesCount > 0) {
106
- parts.push(`${chalk.cyan(configCounts.rulesCount + ' rules')}`);
107
- }
108
- if (configCounts.mcpCount > 0) {
109
- parts.push(`${chalk.yellow(configCounts.mcpCount + ' MCPs')}`);
110
- }
111
-
112
- if (expiry) {
113
- const expiryColor = expiry.daysRemaining <= 3 ? chalk.red : expiry.daysRemaining <= 7 ? chalk.yellow : chalk.green;
114
- parts.push(`${expiryColor('到期: ' + expiry.daysRemaining + '天')}`);
115
- }
116
-
117
- return parts.join(' | ');
118
- }
119
-
120
- renderToolsLine(tools) {
121
- if (!tools || tools.length === 0) {
122
- return null;
123
- }
124
-
125
- const parts = [];
126
- const runningTools = tools.filter(t => t.status === 'running');
127
- const completedTools = tools.filter(t => t.status === 'completed' || t.status === 'error');
128
-
129
- for (const tool of runningTools.slice(-2)) {
130
- const target = tool.target ? this.truncatePath(tool.target) : '';
131
- parts.push(`${chalk.yellow('◐')} ${chalk.cyan(tool.name)}${target ? chalk.cyan(': ' + target) : ''}`);
132
- }
133
-
134
- const toolCounts = new Map();
135
- for (const tool of completedTools) {
136
- const count = toolCounts.get(tool.name) || 0;
137
- toolCounts.set(tool.name, count + 1);
138
- }
139
-
140
- const sortedTools = Array.from(toolCounts.entries())
141
- .sort((a, b) => b[1] - a[1])
142
- .slice(0, 4);
143
-
144
- for (const [name, count] of sortedTools) {
145
- parts.push(`${chalk.green('✓')} ${name} ${chalk.green('×' + count)}`);
146
- }
147
-
148
- if (parts.length === 0) {
149
- return null;
150
- }
151
-
152
- return parts.join(' | ');
153
- }
154
-
155
- renderAgentsLine(agents) {
156
- if (!agents || agents.length === 0) {
157
- return null;
158
- }
159
-
160
- const runningAgents = agents.filter(a => a.status === 'running');
161
- const recentCompleted = agents
162
- .filter(a => a.status === 'completed')
163
- .slice(-2);
164
-
165
- const toShow = [...runningAgents, ...recentCompleted].slice(-3);
166
-
167
- if (toShow.length === 0) {
168
- return null;
169
- }
170
-
171
- const lines = [];
172
- for (const agent of toShow) {
173
- const statusIcon = agent.status === 'running' ? chalk.yellow('◐') : chalk.green('✓');
174
- const type = chalk.magenta(agent.type);
175
- const model = agent.model ? chalk.cyan('[' + agent.model + ']') : '';
176
- const desc = agent.description ? chalk.white(': ' + this.truncateDesc(agent.description)) : '';
177
-
178
- const now = Date.now();
179
- const start = agent.startTime?.getTime() || now;
180
- const end = agent.endTime?.getTime() || now;
181
- const elapsed = this.formatDuration(end - start);
182
-
183
- lines.push(`${statusIcon} ${type}${model}${desc} ${chalk.yellow('(' + elapsed + ')')}`);
184
- }
185
-
186
- return lines.join('\n');
187
- }
188
-
189
- renderTodosLine(todos) {
190
- if (!todos || todos.length === 0) {
191
- return null;
192
- }
193
-
194
- const inProgress = todos.find(t => t.status === 'in_progress');
195
- const completed = todos.filter(t => t.status === 'completed').length;
196
- const total = todos.length;
197
-
198
- if (!inProgress) {
199
- if (completed === total && total > 0) {
200
- return `${chalk.green('✓')} All todos complete ${chalk.green('(' + completed + '/' + total + ')')}`;
201
- }
202
- return null;
203
- }
204
-
205
- const content = this.truncateDesc(inProgress.content, 50);
206
- const progress = chalk.white('(' + completed + '/' + total + ')');
207
-
208
- return `${chalk.yellow('▸')} ${content} ${progress}`;
209
- }
210
-
211
- render(context) {
212
- const lines = [];
213
-
214
- const sessionLine = this.renderSessionLine(context);
215
- if (sessionLine) {
216
- lines.push(sessionLine);
217
- }
218
-
219
- const toolsLine = this.renderToolsLine(context.tools);
220
- if (toolsLine) {
221
- lines.push(toolsLine);
222
- }
223
-
224
- const agentsLine = this.renderAgentsLine(context.agents);
225
- if (agentsLine) {
226
- lines.push(agentsLine);
227
- }
228
-
229
- const todosLine = this.renderTodosLine(context.todos);
230
- if (todosLine) {
231
- lines.push(todosLine);
232
- }
233
-
234
- return lines.join('\n');
235
- }
236
- }
237
-
238
- module.exports = Renderer;
1
+ #!/usr/bin/env node
2
+
3
+ const chalk = require('chalk').default;
4
+
5
+ class Renderer {
6
+ constructor() {
7
+ this.RESET = '\x1b[0m';
8
+ }
9
+
10
+ formatTokens(tokens) {
11
+ if (tokens >= 1000000) {
12
+ return `${(tokens / 1000000).toFixed(1)}M`;
13
+ }
14
+ if (tokens >= 1000) {
15
+ return `${(tokens / 1000).toFixed(1)}k`;
16
+ }
17
+ return tokens.toString();
18
+ }
19
+
20
+ formatContextSize(size) {
21
+ if (size >= 1000000) {
22
+ return `${Math.round(size / 100000) / 10}M`;
23
+ }
24
+ if (size >= 1000) {
25
+ return `${Math.round(size / 1000)}K`;
26
+ }
27
+ return `${size}`;
28
+ }
29
+
30
+ formatDuration(ms) {
31
+ if (ms < 60000) {
32
+ const secs = Math.round(ms / 1000);
33
+ return secs < 1 ? '<1s' : `${secs}s`;
34
+ }
35
+ const mins = Math.floor(ms / 60000);
36
+ const secs = Math.round((ms % 60000) / 1000);
37
+ return `${mins}m ${secs}s`;
38
+ }
39
+
40
+ truncatePath(path, maxLen = 20) {
41
+ if (!path || path.length <= maxLen) return path;
42
+ const parts = path.split(/[/\\]/);
43
+ const filename = parts.pop() || path;
44
+ if (filename.length >= maxLen) {
45
+ return filename.slice(0, maxLen - 3) + '...';
46
+ }
47
+ return '.../' + filename;
48
+ }
49
+
50
+ truncateDesc(desc, maxLen = 40) {
51
+ if (!desc || desc.length <= maxLen) return desc;
52
+ return desc.slice(0, maxLen - 3) + '...';
53
+ }
54
+
55
+ getStatusColor(percentage) {
56
+ if (percentage >= 85) return chalk.red;
57
+ if (percentage >= 60) return chalk.yellow;
58
+ return chalk.green;
59
+ }
60
+
61
+ renderSessionLine(data) {
62
+ const {
63
+ modelName,
64
+ currentDir,
65
+ usagePercentage,
66
+ usage,
67
+ remaining,
68
+ expiry,
69
+ contextUsage,
70
+ contextSize,
71
+ configCounts,
72
+ sessionDuration,
73
+ } = data;
74
+
75
+ const parts = [];
76
+
77
+ if (currentDir) {
78
+ parts.push(`${chalk.blue('📁')} ${chalk.cyan(currentDir)}`);
79
+ }
80
+
81
+ parts.push(`${chalk.magenta('🤖')} ${chalk.magenta(modelName)}`);
82
+
83
+ // 上下文窗口在前
84
+ if (contextUsage !== null && contextUsage !== undefined) {
85
+ const contextPercent = Math.round((contextUsage / contextSize) * 100);
86
+ const contextColor = this.getStatusColor(contextPercent);
87
+ // 合并百分比和实际使用量,用 · 分割,统一颜色
88
+ const contextStr = `${contextColor('⚡' + contextPercent + '%')}${contextColor('·')}${contextColor(this.formatTokens(contextUsage) + ' tokens')}`;
89
+ parts.push(contextStr);
90
+ } else {
91
+ parts.push(`${chalk.cyan(this.formatContextSize(contextSize))}`);
92
+ }
93
+
94
+ // 使用量合并
95
+ const usageColor = this.getStatusColor(usagePercentage);
96
+ parts.push(`${chalk.yellow('↻')} ${usageColor(usagePercentage + '%')}${chalk.yellow('·')}${chalk.white(usage.remaining + '/' + usage.total)}`);
97
+
98
+ const remainingText = remaining.hours > 0
99
+ ? `${remaining.hours}h${remaining.minutes}m`
100
+ : `${remaining.minutes}m`;
101
+ parts.push(`${chalk.yellow('⌛')} ${chalk.white(remainingText)}`);
102
+
103
+ if (configCounts.claudeMdCount > 0) {
104
+ parts.push(`${chalk.white(configCounts.claudeMdCount + ' CLAUDE.md')}`);
105
+ }
106
+ if (configCounts.rulesCount > 0) {
107
+ parts.push(`${chalk.cyan(configCounts.rulesCount + ' rules')}`);
108
+ }
109
+ if (configCounts.mcpCount > 0) {
110
+ parts.push(`${chalk.yellow(configCounts.mcpCount + ' MCPs')}`);
111
+ }
112
+
113
+ if (expiry) {
114
+ const expiryColor = expiry.daysRemaining <= 3 ? chalk.red : expiry.daysRemaining <= 7 ? chalk.yellow : chalk.green;
115
+ parts.push(`${expiryColor('到期: ' + expiry.daysRemaining + '天')}`);
116
+ }
117
+
118
+ return parts.join(' | ');
119
+ }
120
+
121
+ renderToolsLine(tools) {
122
+ if (!tools || tools.length === 0) {
123
+ return null;
124
+ }
125
+
126
+ const parts = [];
127
+ const runningTools = tools.filter(t => t.status === 'running');
128
+ const completedTools = tools.filter(t => t.status === 'completed' || t.status === 'error');
129
+
130
+ for (const tool of runningTools.slice(-2)) {
131
+ const target = tool.target ? this.truncatePath(tool.target) : '';
132
+ parts.push(`${chalk.yellow('◐')} ${chalk.cyan(tool.name)}${target ? chalk.cyan(': ' + target) : ''}`);
133
+ }
134
+
135
+ const toolCounts = new Map();
136
+ for (const tool of completedTools) {
137
+ const count = toolCounts.get(tool.name) || 0;
138
+ toolCounts.set(tool.name, count + 1);
139
+ }
140
+
141
+ const sortedTools = Array.from(toolCounts.entries())
142
+ .sort((a, b) => b[1] - a[1])
143
+ .slice(0, 4);
144
+
145
+ for (const [name, count] of sortedTools) {
146
+ parts.push(`${chalk.green('✓')} ${name} ${chalk.green('×' + count)}`);
147
+ }
148
+
149
+ if (parts.length === 0) {
150
+ return null;
151
+ }
152
+
153
+ return parts.join(' | ');
154
+ }
155
+
156
+ renderAgentsLine(agents) {
157
+ if (!agents || agents.length === 0) {
158
+ return null;
159
+ }
160
+
161
+ const runningAgents = agents.filter(a => a.status === 'running');
162
+ const recentCompleted = agents
163
+ .filter(a => a.status === 'completed')
164
+ .slice(-2);
165
+
166
+ const toShow = [...runningAgents, ...recentCompleted].slice(-3);
167
+
168
+ if (toShow.length === 0) {
169
+ return null;
170
+ }
171
+
172
+ const lines = [];
173
+ for (const agent of toShow) {
174
+ const statusIcon = agent.status === 'running' ? chalk.yellow('◐') : chalk.green('✓');
175
+ const type = chalk.magenta(agent.type);
176
+ const model = agent.model ? chalk.cyan('[' + agent.model + ']') : '';
177
+ const desc = agent.description ? chalk.white(': ' + this.truncateDesc(agent.description)) : '';
178
+
179
+ const now = Date.now();
180
+ const start = agent.startTime?.getTime() || now;
181
+ const end = agent.endTime?.getTime() || now;
182
+ const elapsed = this.formatDuration(end - start);
183
+
184
+ lines.push(`${statusIcon} ${type}${model}${desc} ${chalk.yellow('(' + elapsed + ')')}`);
185
+ }
186
+
187
+ return lines.join('\n');
188
+ }
189
+
190
+ renderTodosLine(todos) {
191
+ if (!todos || todos.length === 0) {
192
+ return null;
193
+ }
194
+
195
+ const inProgress = todos.find(t => t.status === 'in_progress');
196
+ const completed = todos.filter(t => t.status === 'completed').length;
197
+ const total = todos.length;
198
+
199
+ if (!inProgress) {
200
+ if (completed === total && total > 0) {
201
+ return `${chalk.green('✓')} All todos complete ${chalk.green('(' + completed + '/' + total + ')')}`;
202
+ }
203
+ return null;
204
+ }
205
+
206
+ const content = this.truncateDesc(inProgress.content, 50);
207
+ const progress = chalk.white('(' + completed + '/' + total + ')');
208
+
209
+ return `${chalk.yellow('▸')} ${content} ${progress}`;
210
+ }
211
+
212
+ render(context) {
213
+ const lines = [];
214
+
215
+ const sessionLine = this.renderSessionLine(context);
216
+ if (sessionLine) {
217
+ lines.push(sessionLine);
218
+ }
219
+
220
+ const toolsLine = this.renderToolsLine(context.tools);
221
+ if (toolsLine) {
222
+ lines.push(toolsLine);
223
+ }
224
+
225
+ const agentsLine = this.renderAgentsLine(context.agents);
226
+ if (agentsLine) {
227
+ lines.push(agentsLine);
228
+ }
229
+
230
+ const todosLine = this.renderTodosLine(context.todos);
231
+ if (todosLine) {
232
+ lines.push(todosLine);
233
+ }
234
+
235
+ return lines.join('\n');
236
+ }
237
+ }
238
+
239
+ module.exports = Renderer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minimax-status",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "MiniMax Claude Code 使用状态监控工具",
5
5
  "bin": {
6
6
  "minimax-status": "cli/index.js",