@ww_nero/mini-cli 1.0.81 → 1.0.82

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.
@@ -1,131 +1,131 @@
1
- const chalk = require('chalk');
2
- const { promoteEndpointInConfig } = require('../config');
3
-
4
- const describeModel = (endpoint) => {
5
- if (!endpoint) {
6
- return '未知模型';
7
- }
8
- const alias = typeof endpoint.name === 'string' ? endpoint.name.trim() : '';
9
- const label = alias || endpoint.model;
10
- if (alias && alias !== endpoint.model) {
11
- return `${label} (${endpoint.model})`;
12
- }
13
- return label;
14
- };
15
-
16
- const normalizeModelInput = (value = '') => {
17
- const trimmed = value.trim();
18
- if (!trimmed) {
19
- return '';
20
- }
21
- const quote = trimmed[0];
22
- if ((quote === '"' || quote === '\'') && trimmed.endsWith(quote) && trimmed.length > 1) {
23
- return trimmed.slice(1, -1).trim();
24
- }
25
- return trimmed;
26
- };
27
-
28
- const findModelIndexByName = (endpoints, name = '') => {
29
- const normalized = name.toLowerCase();
30
- return endpoints.findIndex((endpoint) => {
31
- const candidates = [endpoint.name, endpoint.model];
32
- return candidates.some((candidate) => typeof candidate === 'string' && candidate.toLowerCase() === normalized);
33
- });
34
- };
35
-
36
- const createModelManager = (endpoints, { configPath } = {}) => {
37
- let defaultEndpointIndex = 0;
38
- let activeEndpointIndex = 0;
39
-
40
- const getActiveEndpoint = () => endpoints[activeEndpointIndex];
41
- const getDefaultEndpoint = () => endpoints[defaultEndpointIndex];
42
- const getTotalModels = () => endpoints.length;
43
- const getDefaultModelDescription = () => describeModel(getDefaultEndpoint());
44
-
45
- const resetToDefault = () => {
46
- setActiveModel(defaultEndpointIndex, { updateDefault: false, announce: false, persistConfig: false });
47
- };
48
-
49
- const setActiveModel = (
50
- index,
51
- { updateDefault = false, announce = true, announcePrefix = '已切换至', reason = '', persistConfig = true } = {}
52
- ) => {
53
- if (index < 0 || index >= endpoints.length) {
54
- return false;
55
- }
56
- activeEndpointIndex = index;
57
- if (updateDefault) {
58
- defaultEndpointIndex = index;
59
- if (persistConfig && configPath) {
60
- const endpoint = endpoints[index];
61
- if (endpoint && endpoint.signature) {
62
- promoteEndpointInConfig(configPath, endpoint.signature);
63
- }
64
- }
65
- }
66
- if (announce) {
67
- console.log(chalk.green(`${announcePrefix}模型 ${describeModel(endpoints[index])}`));
68
- if (reason) {
69
- console.log(chalk.gray(reason));
70
- }
71
- }
72
- return true;
73
- };
74
-
75
- const applyModelByName = (rawName, { source = 'command', announceSuccess = true, showError = true } = {}) => {
76
- const normalizedInput = normalizeModelInput(rawName || '');
77
- if (!normalizedInput) {
78
- if (showError) {
79
- console.log(chalk.yellow('请输入要切换的模型名称。'));
80
- }
81
- return false;
82
- }
83
- const index = findModelIndexByName(endpoints, normalizedInput);
84
- if (index === -1) {
85
- if (showError) {
86
- console.error(chalk.red(`未找到模型 ${normalizedInput},已保持 ${describeModel(getDefaultEndpoint())}。`));
87
- }
88
- setActiveModel(defaultEndpointIndex, { updateDefault: false, announce: false, persistConfig: false });
89
- return false;
90
- }
91
- const announcePrefix = source === 'cli' ? '已应用启动' : '已切换至';
92
- setActiveModel(index, { updateDefault: true, announce: announceSuccess, announcePrefix });
93
- return true;
94
- };
95
-
96
- const listModels = () => {
97
- console.log(chalk.gray('可用模型列表:'));
98
- endpoints.forEach((endpoint, idx) => {
99
- const markers = [];
100
- if (idx === activeEndpointIndex) {
101
- markers.push(chalk.green('当前'));
102
- }
103
- if (idx === defaultEndpointIndex) {
104
- markers.push(chalk.cyan('默认'));
105
- }
106
- const markerText = markers.length > 0 ? ` [${markers.join('/')}]` : '';
107
- console.log(` ${idx + 1}. ${describeModel(endpoint)}${markerText}`);
108
- });
109
- console.log(chalk.gray('使用 /model <名称|序号> 切换模型。'));
110
- };
111
-
112
- return {
113
- getActiveEndpoint,
114
- getDefaultEndpoint,
115
- getTotalModels,
116
- getDefaultModelDescription,
117
- resetToDefault,
118
- setActiveModel,
119
- applyModelByName,
120
- listModels,
121
- getModels: () => endpoints,
122
- getActiveIndex: () => activeEndpointIndex,
123
- getDefaultIndex: () => defaultEndpointIndex,
124
- describeEndpoint: (endpoint) => describeModel(endpoint)
125
- };
126
- };
127
-
128
- module.exports = {
129
- describeModel,
130
- createModelManager
131
- };
1
+ const chalk = require('chalk');
2
+ const { promoteEndpointInConfig } = require('../config');
3
+
4
+ const describeModel = (endpoint) => {
5
+ if (!endpoint) {
6
+ return '未知模型';
7
+ }
8
+ const alias = typeof endpoint.name === 'string' ? endpoint.name.trim() : '';
9
+ const label = alias || endpoint.model;
10
+ if (alias && alias !== endpoint.model) {
11
+ return `${label} (${endpoint.model})`;
12
+ }
13
+ return label;
14
+ };
15
+
16
+ const normalizeModelInput = (value = '') => {
17
+ const trimmed = value.trim();
18
+ if (!trimmed) {
19
+ return '';
20
+ }
21
+ const quote = trimmed[0];
22
+ if ((quote === '"' || quote === '\'') && trimmed.endsWith(quote) && trimmed.length > 1) {
23
+ return trimmed.slice(1, -1).trim();
24
+ }
25
+ return trimmed;
26
+ };
27
+
28
+ const findModelIndexByName = (endpoints, name = '') => {
29
+ const normalized = name.toLowerCase();
30
+ return endpoints.findIndex((endpoint) => {
31
+ const candidates = [endpoint.name, endpoint.model];
32
+ return candidates.some((candidate) => typeof candidate === 'string' && candidate.toLowerCase() === normalized);
33
+ });
34
+ };
35
+
36
+ const createModelManager = (endpoints, { configPath } = {}) => {
37
+ let defaultEndpointIndex = 0;
38
+ let activeEndpointIndex = 0;
39
+
40
+ const getActiveEndpoint = () => endpoints[activeEndpointIndex];
41
+ const getDefaultEndpoint = () => endpoints[defaultEndpointIndex];
42
+ const getTotalModels = () => endpoints.length;
43
+ const getDefaultModelDescription = () => describeModel(getDefaultEndpoint());
44
+
45
+ const resetToDefault = () => {
46
+ setActiveModel(defaultEndpointIndex, { updateDefault: false, announce: false, persistConfig: false });
47
+ };
48
+
49
+ const setActiveModel = (
50
+ index,
51
+ { updateDefault = false, announce = true, announcePrefix = '已切换至', reason = '', persistConfig = true } = {}
52
+ ) => {
53
+ if (index < 0 || index >= endpoints.length) {
54
+ return false;
55
+ }
56
+ activeEndpointIndex = index;
57
+ if (updateDefault) {
58
+ defaultEndpointIndex = index;
59
+ if (persistConfig && configPath) {
60
+ const endpoint = endpoints[index];
61
+ if (endpoint && endpoint.signature) {
62
+ promoteEndpointInConfig(configPath, endpoint.signature);
63
+ }
64
+ }
65
+ }
66
+ if (announce) {
67
+ console.log(chalk.green(`${announcePrefix}模型 ${describeModel(endpoints[index])}`));
68
+ if (reason) {
69
+ console.log(chalk.gray(reason));
70
+ }
71
+ }
72
+ return true;
73
+ };
74
+
75
+ const applyModelByName = (rawName, { source = 'command', announceSuccess = true, showError = true } = {}) => {
76
+ const normalizedInput = normalizeModelInput(rawName || '');
77
+ if (!normalizedInput) {
78
+ if (showError) {
79
+ console.log(chalk.yellow('请输入要切换的模型名称。'));
80
+ }
81
+ return false;
82
+ }
83
+ const index = findModelIndexByName(endpoints, normalizedInput);
84
+ if (index === -1) {
85
+ if (showError) {
86
+ console.error(chalk.red(`未找到模型 ${normalizedInput},已保持 ${describeModel(getDefaultEndpoint())}。`));
87
+ }
88
+ setActiveModel(defaultEndpointIndex, { updateDefault: false, announce: false, persistConfig: false });
89
+ return false;
90
+ }
91
+ const announcePrefix = source === 'cli' ? '已应用启动' : '已切换至';
92
+ setActiveModel(index, { updateDefault: true, announce: announceSuccess, announcePrefix });
93
+ return true;
94
+ };
95
+
96
+ const listModels = () => {
97
+ console.log(chalk.gray('可用模型列表:'));
98
+ endpoints.forEach((endpoint, idx) => {
99
+ const markers = [];
100
+ if (idx === activeEndpointIndex) {
101
+ markers.push(chalk.green('当前'));
102
+ }
103
+ if (idx === defaultEndpointIndex) {
104
+ markers.push(chalk.cyan('默认'));
105
+ }
106
+ const markerText = markers.length > 0 ? ` [${markers.join('/')}]` : '';
107
+ console.log(` ${idx + 1}. ${describeModel(endpoint)}${markerText}`);
108
+ });
109
+ console.log(chalk.gray('使用 /model <名称|序号> 切换模型。'));
110
+ };
111
+
112
+ return {
113
+ getActiveEndpoint,
114
+ getDefaultEndpoint,
115
+ getTotalModels,
116
+ getDefaultModelDescription,
117
+ resetToDefault,
118
+ setActiveModel,
119
+ applyModelByName,
120
+ listModels,
121
+ getModels: () => endpoints,
122
+ getActiveIndex: () => activeEndpointIndex,
123
+ getDefaultIndex: () => defaultEndpointIndex,
124
+ describeEndpoint: (endpoint) => describeModel(endpoint)
125
+ };
126
+ };
127
+
128
+ module.exports = {
129
+ describeModel,
130
+ createModelManager
131
+ };
@@ -1,75 +1,75 @@
1
- const chalk = require('chalk');
2
- const { splitThinkContent, summarizeReasoning } = require('./think');
3
-
4
- const mergeReasoningContent = (...segments) => {
5
- const normalized = segments
6
- .map((segment) => (typeof segment === 'string' ? segment.trim() : ''))
7
- .filter(Boolean);
8
- if (normalized.length === 0) {
9
- return '';
10
- }
11
- return normalized.join('\n');
12
- };
13
-
14
- const printReasoningSummary = (text, ensureNewline) => {
15
- if (!text) return;
16
- ensureNewline();
17
- console.log(chalk.gray(`思考:${summarizeReasoning(text)}`));
18
- };
19
-
20
- const printAssistantContent = (text, ensureNewline) => {
21
- if (typeof text !== 'string' || !text) {
22
- return;
23
- }
24
- ensureNewline();
25
- const output = chalk.white(text);
26
- process.stdout.write(output);
27
- if (!text.endsWith('\n')) {
28
- process.stdout.write('\n');
29
- }
30
- };
31
-
32
- const renderAssistantOutput = (reasoning, content, ensureNewline) => {
33
- printReasoningSummary(reasoning, ensureNewline);
34
- printAssistantContent(content, ensureNewline);
35
- };
36
-
37
- const sanitizeContentWithThink = (primary = '', fallback = '') => {
38
- const normalizedPrimary = typeof primary === 'string' ? primary.trim() : '';
39
- if (normalizedPrimary) {
40
- return normalizedPrimary;
41
- }
42
- if (typeof fallback !== 'string' || !fallback.trim()) {
43
- return '';
44
- }
45
- const { content } = splitThinkContent(fallback);
46
- return (content || '').trim();
47
- };
48
-
49
- const formatTokensToK = (totalTokens) => {
50
- if (typeof totalTokens !== 'number' || Number.isNaN(totalTokens)) {
51
- return null;
52
- }
53
- return `${(totalTokens / 1000).toFixed(1)}k`;
54
- };
55
-
56
- const printUsageTokens = (usage) => {
57
- if (!usage || typeof usage !== 'object') {
58
- return;
59
- }
60
- const totalTokens = usage.total_tokens;
61
- const formatted = formatTokensToK(totalTokens);
62
- if (!formatted) {
63
- return;
64
- }
65
- console.log(chalk.gray(`已使用tokens: ${formatted}`));
66
- };
67
-
68
- module.exports = {
69
- mergeReasoningContent,
70
- printReasoningSummary,
71
- printAssistantContent,
72
- renderAssistantOutput,
73
- sanitizeContentWithThink,
74
- printUsageTokens
75
- };
1
+ const chalk = require('chalk');
2
+ const { splitThinkContent, summarizeReasoning } = require('./think');
3
+
4
+ const mergeReasoningContent = (...segments) => {
5
+ const normalized = segments
6
+ .map((segment) => (typeof segment === 'string' ? segment.trim() : ''))
7
+ .filter(Boolean);
8
+ if (normalized.length === 0) {
9
+ return '';
10
+ }
11
+ return normalized.join('\n');
12
+ };
13
+
14
+ const printReasoningSummary = (text, ensureNewline) => {
15
+ if (!text) return;
16
+ ensureNewline();
17
+ console.log(chalk.gray(`思考:${summarizeReasoning(text)}`));
18
+ };
19
+
20
+ const printAssistantContent = (text, ensureNewline) => {
21
+ if (typeof text !== 'string' || !text) {
22
+ return;
23
+ }
24
+ ensureNewline();
25
+ const output = chalk.white(text);
26
+ process.stdout.write(output);
27
+ if (!text.endsWith('\n')) {
28
+ process.stdout.write('\n');
29
+ }
30
+ };
31
+
32
+ const renderAssistantOutput = (reasoning, content, ensureNewline) => {
33
+ printReasoningSummary(reasoning, ensureNewline);
34
+ printAssistantContent(content, ensureNewline);
35
+ };
36
+
37
+ const sanitizeContentWithThink = (primary = '', fallback = '') => {
38
+ const normalizedPrimary = typeof primary === 'string' ? primary.trim() : '';
39
+ if (normalizedPrimary) {
40
+ return normalizedPrimary;
41
+ }
42
+ if (typeof fallback !== 'string' || !fallback.trim()) {
43
+ return '';
44
+ }
45
+ const { content } = splitThinkContent(fallback);
46
+ return (content || '').trim();
47
+ };
48
+
49
+ const formatTokensToK = (totalTokens) => {
50
+ if (typeof totalTokens !== 'number' || Number.isNaN(totalTokens)) {
51
+ return null;
52
+ }
53
+ return `${(totalTokens / 1000).toFixed(1)}k`;
54
+ };
55
+
56
+ const printUsageTokens = (usage) => {
57
+ if (!usage || typeof usage !== 'object') {
58
+ return;
59
+ }
60
+ const totalTokens = usage.total_tokens;
61
+ const formatted = formatTokensToK(totalTokens);
62
+ if (!formatted) {
63
+ return;
64
+ }
65
+ console.log(chalk.gray(`已使用tokens: ${formatted}`));
66
+ };
67
+
68
+ module.exports = {
69
+ mergeReasoningContent,
70
+ printReasoningSummary,
71
+ printAssistantContent,
72
+ renderAssistantOutput,
73
+ sanitizeContentWithThink,
74
+ printUsageTokens
75
+ };
@@ -1,102 +1,102 @@
1
- const chalk = require('chalk');
2
-
3
- const truncateText = (text, maxLength = 200) => {
4
- if (!text || typeof text !== 'string') {
5
- return text;
6
- }
7
- if (text.length <= maxLength) {
8
- return text;
9
- }
10
- return text.substring(0, maxLength) + '...';
11
- };
12
-
13
- const countLines = (text = '') => {
14
- if (!text) return 0;
15
- const normalized = text.replace(/\r/g, '');
16
- const newlineMatches = normalized.match(/\n/g);
17
- const newlineCount = Array.isArray(newlineMatches) ? newlineMatches.length : 0;
18
- const endsWithNewline = normalized.endsWith('\n');
19
- return endsWithNewline ? newlineCount : newlineCount + 1;
20
- };
21
-
22
- const formatWriteHeader = (args = {}) => {
23
- const filePath = args.filePath || '';
24
- const lineCount = countLines(args.content);
25
- return ` (${filePath}, ${chalk.green(`+${lineCount}`)})`;
26
- };
27
-
28
- const formatEditHeader = (args = {}) => {
29
- const filePath = args.filePath || '';
30
- const searchLines = countLines(args.search);
31
- const replaceLines = countLines(args.replace);
32
- return ` (${filePath}, ${chalk.green(`+${replaceLines}`)} ${chalk.red(`-${searchLines}`)})`;
33
- };
34
-
35
- const formatHeader = (name, args, options = {}) => {
36
- const statusTag = options.statusTag || '';
37
- const extraMeta = Array.isArray(options.extraMeta) ? options.extraMeta : [];
38
- const labelBase = `${chalk.yellow('🔧')} ${chalk.yellow(name)}`;
39
-
40
- let specialHeader = '';
41
- if (name === 'write') {
42
- specialHeader = formatWriteHeader(args);
43
- } else if (name === 'edit') {
44
- specialHeader = formatEditHeader(args);
45
- }
46
-
47
- const labelWithInfo = `${labelBase}${specialHeader}`;
48
- const label = statusTag ? `${labelWithInfo}${statusTag}` : labelWithInfo;
49
- const metaParts = [...extraMeta];
50
-
51
- if (name === 'bash') {
52
- if (args.command) metaParts.push(`command="${truncateText(args.command)}"`);
53
- if (args.workingDirectory && args.workingDirectory !== '.') {
54
- metaParts.push(`dir=${args.workingDirectory}`);
55
- }
56
- } else if (name === 'read') {
57
- if (args.filePath) metaParts.push(args.filePath);
58
- } else if (name === 'todos') {
59
- const count = Array.isArray(args.todos) ? args.todos.length : 0;
60
- metaParts.push(`${count} items`);
61
- } else if (name === 'convert') {
62
- if (typeof args.input === 'string') {
63
- metaParts.push(`input=${truncateText(args.input, 160)}`);
64
- } else if (Array.isArray(args.input)) {
65
- metaParts.push(`input=${truncateText(args.input.join(', '), 160)}`);
66
- }
67
- if (args.output) {
68
- metaParts.push(`output=${truncateText(String(args.output), 160)}`);
69
- }
70
- }
71
-
72
- const meta = metaParts.length > 0 ? ` (${metaParts.join(', ')})` : '';
73
- return `${label}${meta}`;
74
- };
75
-
76
- const safeStringify = (value) => {
77
- try {
78
- return JSON.stringify(value);
79
- } catch (_) {
80
- return String(value);
81
- }
82
- };
83
-
84
- const compactText = (text) => String(text || '').replace(/\s+/g, ' ').trim();
85
-
86
- const renderToolCall = ({ functionName, args = {} }, mcpToolNames = new Set()) => {
87
- const isMcpTool = mcpToolNames.has(functionName);
88
-
89
- const extraMeta = [];
90
- if (isMcpTool) {
91
- const argsPreview = truncateText(compactText(safeStringify(args) || ''), 200);
92
- if (argsPreview) {
93
- extraMeta.push(argsPreview);
94
- }
95
- }
96
-
97
- console.log(formatHeader(functionName, args, { extraMeta }));
98
- };
99
-
100
- module.exports = {
101
- renderToolCall
102
- };
1
+ const chalk = require('chalk');
2
+
3
+ const truncateText = (text, maxLength = 200) => {
4
+ if (!text || typeof text !== 'string') {
5
+ return text;
6
+ }
7
+ if (text.length <= maxLength) {
8
+ return text;
9
+ }
10
+ return text.substring(0, maxLength) + '...';
11
+ };
12
+
13
+ const countLines = (text = '') => {
14
+ if (!text) return 0;
15
+ const normalized = text.replace(/\r/g, '');
16
+ const newlineMatches = normalized.match(/\n/g);
17
+ const newlineCount = Array.isArray(newlineMatches) ? newlineMatches.length : 0;
18
+ const endsWithNewline = normalized.endsWith('\n');
19
+ return endsWithNewline ? newlineCount : newlineCount + 1;
20
+ };
21
+
22
+ const formatWriteHeader = (args = {}) => {
23
+ const filePath = args.filePath || '';
24
+ const lineCount = countLines(args.content);
25
+ return ` (${filePath}, ${chalk.green(`+${lineCount}`)})`;
26
+ };
27
+
28
+ const formatEditHeader = (args = {}) => {
29
+ const filePath = args.filePath || '';
30
+ const searchLines = countLines(args.search);
31
+ const replaceLines = countLines(args.replace);
32
+ return ` (${filePath}, ${chalk.green(`+${replaceLines}`)} ${chalk.red(`-${searchLines}`)})`;
33
+ };
34
+
35
+ const formatHeader = (name, args, options = {}) => {
36
+ const statusTag = options.statusTag || '';
37
+ const extraMeta = Array.isArray(options.extraMeta) ? options.extraMeta : [];
38
+ const labelBase = `${chalk.yellow('🔧')} ${chalk.yellow(name)}`;
39
+
40
+ let specialHeader = '';
41
+ if (name === 'write') {
42
+ specialHeader = formatWriteHeader(args);
43
+ } else if (name === 'edit') {
44
+ specialHeader = formatEditHeader(args);
45
+ }
46
+
47
+ const labelWithInfo = `${labelBase}${specialHeader}`;
48
+ const label = statusTag ? `${labelWithInfo}${statusTag}` : labelWithInfo;
49
+ const metaParts = [...extraMeta];
50
+
51
+ if (name === 'bash') {
52
+ if (args.command) metaParts.push(`command="${truncateText(args.command)}"`);
53
+ if (args.workingDirectory && args.workingDirectory !== '.') {
54
+ metaParts.push(`dir=${args.workingDirectory}`);
55
+ }
56
+ } else if (name === 'read') {
57
+ if (args.filePath) metaParts.push(args.filePath);
58
+ } else if (name === 'todos') {
59
+ const count = Array.isArray(args.todos) ? args.todos.length : 0;
60
+ metaParts.push(`${count} items`);
61
+ } else if (name === 'convert') {
62
+ if (typeof args.input === 'string') {
63
+ metaParts.push(`input=${truncateText(args.input, 160)}`);
64
+ } else if (Array.isArray(args.input)) {
65
+ metaParts.push(`input=${truncateText(args.input.join(', '), 160)}`);
66
+ }
67
+ if (args.output) {
68
+ metaParts.push(`output=${truncateText(String(args.output), 160)}`);
69
+ }
70
+ }
71
+
72
+ const meta = metaParts.length > 0 ? ` (${metaParts.join(', ')})` : '';
73
+ return `${label}${meta}`;
74
+ };
75
+
76
+ const safeStringify = (value) => {
77
+ try {
78
+ return JSON.stringify(value);
79
+ } catch (_) {
80
+ return String(value);
81
+ }
82
+ };
83
+
84
+ const compactText = (text) => String(text || '').replace(/\s+/g, ' ').trim();
85
+
86
+ const renderToolCall = ({ functionName, args = {} }, mcpToolNames = new Set()) => {
87
+ const isMcpTool = mcpToolNames.has(functionName);
88
+
89
+ const extraMeta = [];
90
+ if (isMcpTool) {
91
+ const argsPreview = truncateText(compactText(safeStringify(args) || ''), 200);
92
+ if (argsPreview) {
93
+ extraMeta.push(argsPreview);
94
+ }
95
+ }
96
+
97
+ console.log(formatHeader(functionName, args, { extraMeta }));
98
+ };
99
+
100
+ module.exports = {
101
+ renderToolCall
102
+ };