@ww_nero/mini-cli 1.0.81 → 1.0.83

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/src/utils/git.js CHANGED
@@ -1,89 +1,89 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const { execSync } = require('child_process');
4
- const chalk = require('chalk');
5
-
6
- const DEFAULT_GITIGNORE_ENTRIES = [
7
- 'venv',
8
- 'node_modules',
9
- 'logs',
10
- 'build',
11
- 'coverage',
12
- '*.log',
13
- '*.tmp',
14
- '*.temp',
15
- '*.DS_Store',
16
- 'Thumbs.db',
17
- 'package-lock.json',
18
- 'dist',
19
- '.idea',
20
- '*.pyc'
21
- ];
22
-
23
- const ensureGitRepository = (workspaceRoot) => {
24
- if (!workspaceRoot) {
25
- return { gitInitialized: false, gitignoreCreated: false };
26
- }
27
-
28
- const gitDirPath = path.join(workspaceRoot, '.git');
29
- const gitignorePath = path.join(workspaceRoot, '.gitignore');
30
- let gitInitialized = false;
31
- let gitignoreCreated = false;
32
- let initialCommitCreated = false;
33
-
34
- if (!fs.existsSync(gitDirPath)) {
35
- try {
36
- execSync('git init', { cwd: workspaceRoot, stdio: 'ignore' });
37
- gitInitialized = true;
38
- } catch (error) {
39
- console.log(chalk.yellow(`自动初始化 Git 仓库失败:${error.message}`));
40
- }
41
- }
42
-
43
- if (!fs.existsSync(gitignorePath)) {
44
- try {
45
- const content = DEFAULT_GITIGNORE_ENTRIES.join('\n') + '\n';
46
- fs.writeFileSync(gitignorePath, content, 'utf8');
47
- gitignoreCreated = true;
48
- } catch (error) {
49
- console.log(chalk.yellow(`创建 .gitignore 失败:${error.message}`));
50
- }
51
- }
52
-
53
- // 如果完成了 git init 或 .gitignore 创建,检查是否需要初始提交
54
- if ((gitInitialized || gitignoreCreated) && fs.existsSync(gitDirPath)) {
55
- try {
56
- // 检查是否有任何提交
57
- const hasCommits = (() => {
58
- try {
59
- execSync('git rev-parse HEAD', { cwd: workspaceRoot, stdio: 'ignore' });
60
- return true;
61
- } catch (_) {
62
- return false;
63
- }
64
- })();
65
-
66
- if (!hasCommits) {
67
- // 添加所有文件并创建初始提交
68
- execSync('git add .', { cwd: workspaceRoot, stdio: 'ignore' });
69
- execSync('git commit -m "init project"', { cwd: workspaceRoot, stdio: 'ignore' });
70
- initialCommitCreated = true;
71
- }
72
- } catch (error) {
73
- console.log(chalk.yellow(`创建初始提交失败:${error.message}`));
74
- }
75
- }
76
-
77
- return {
78
- gitInitialized,
79
- gitignoreCreated,
80
- initialCommitCreated,
81
- gitignorePath,
82
- gitDirPath
83
- };
84
- };
85
-
86
- module.exports = {
87
- ensureGitRepository,
88
- DEFAULT_GITIGNORE_ENTRIES
89
- };
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+ const chalk = require('chalk');
5
+
6
+ const DEFAULT_GITIGNORE_ENTRIES = [
7
+ 'venv',
8
+ 'node_modules',
9
+ 'logs',
10
+ 'build',
11
+ 'coverage',
12
+ '*.log',
13
+ '*.tmp',
14
+ '*.temp',
15
+ '*.DS_Store',
16
+ 'Thumbs.db',
17
+ 'package-lock.json',
18
+ 'dist',
19
+ '.idea',
20
+ '*.pyc'
21
+ ];
22
+
23
+ const ensureGitRepository = (workspaceRoot) => {
24
+ if (!workspaceRoot) {
25
+ return { gitInitialized: false, gitignoreCreated: false };
26
+ }
27
+
28
+ const gitDirPath = path.join(workspaceRoot, '.git');
29
+ const gitignorePath = path.join(workspaceRoot, '.gitignore');
30
+ let gitInitialized = false;
31
+ let gitignoreCreated = false;
32
+ let initialCommitCreated = false;
33
+
34
+ if (!fs.existsSync(gitDirPath)) {
35
+ try {
36
+ execSync('git init', { cwd: workspaceRoot, stdio: 'ignore' });
37
+ gitInitialized = true;
38
+ } catch (error) {
39
+ console.log(chalk.yellow(`自动初始化 Git 仓库失败:${error.message}`));
40
+ }
41
+ }
42
+
43
+ if (!fs.existsSync(gitignorePath)) {
44
+ try {
45
+ const content = DEFAULT_GITIGNORE_ENTRIES.join('\n') + '\n';
46
+ fs.writeFileSync(gitignorePath, content, 'utf8');
47
+ gitignoreCreated = true;
48
+ } catch (error) {
49
+ console.log(chalk.yellow(`创建 .gitignore 失败:${error.message}`));
50
+ }
51
+ }
52
+
53
+ // 如果完成了 git init 或 .gitignore 创建,检查是否需要初始提交
54
+ if ((gitInitialized || gitignoreCreated) && fs.existsSync(gitDirPath)) {
55
+ try {
56
+ // 检查是否有任何提交
57
+ const hasCommits = (() => {
58
+ try {
59
+ execSync('git rev-parse HEAD', { cwd: workspaceRoot, stdio: 'ignore' });
60
+ return true;
61
+ } catch (_) {
62
+ return false;
63
+ }
64
+ })();
65
+
66
+ if (!hasCommits) {
67
+ // 添加所有文件并创建初始提交
68
+ execSync('git add .', { cwd: workspaceRoot, stdio: 'ignore' });
69
+ execSync('git commit -m "init project"', { cwd: workspaceRoot, stdio: 'ignore' });
70
+ initialCommitCreated = true;
71
+ }
72
+ } catch (error) {
73
+ console.log(chalk.yellow(`创建初始提交失败:${error.message}`));
74
+ }
75
+ }
76
+
77
+ return {
78
+ gitInitialized,
79
+ gitignoreCreated,
80
+ initialCommitCreated,
81
+ gitignorePath,
82
+ gitDirPath
83
+ };
84
+ };
85
+
86
+ module.exports = {
87
+ ensureGitRepository,
88
+ DEFAULT_GITIGNORE_ENTRIES
89
+ };
@@ -1,181 +1,181 @@
1
- const fs = require('fs');
2
- const os = require('os');
3
- const path = require('path');
4
- const crypto = require('crypto');
5
-
6
- const MAX_HISTORY_RECORDS = 50;
7
- const HISTORY_BASE_DIR = path.join(os.homedir(), '.mini', 'cli');
8
- const HISTORY_FILENAME_PATTERN = /^\d{14}\.json$/;
9
-
10
- const resolveProjectPath = (projectPath) => path.resolve(projectPath || process.cwd());
11
-
12
- const getProjectMd5 = (projectPath) => (
13
- crypto
14
- .createHash('md5')
15
- .update(resolveProjectPath(projectPath), 'utf8')
16
- .digest('hex')
17
- );
18
-
19
- const getProjectHistoryDir = (projectPath) => path.join(HISTORY_BASE_DIR, getProjectMd5(projectPath));
20
-
21
- const ensureHistoryDir = (projectPath) => {
22
- const dir = getProjectHistoryDir(projectPath);
23
- if (!fs.existsSync(dir)) {
24
- fs.mkdirSync(dir, { recursive: true });
25
- }
26
- return dir;
27
- };
28
-
29
- const pad = (value, length = 2) => String(value).padStart(length, '0');
30
-
31
- const formatTimestamp = (date = new Date()) => {
32
- const year = date.getFullYear();
33
- const month = pad(date.getMonth() + 1);
34
- const day = pad(date.getDate());
35
- const hour = pad(date.getHours());
36
- const minute = pad(date.getMinutes());
37
- const second = pad(date.getSeconds());
38
- return `${year}${month}${day}${hour}${minute}${second}`;
39
- };
40
-
41
- const createHistoryFilePath = (projectPath) => {
42
- const historyDir = ensureHistoryDir(projectPath);
43
- let attempt = new Date();
44
- const maxAttempts = 120;
45
-
46
- for (let i = 0; i < maxAttempts; i += 1) {
47
- const name = `${formatTimestamp(attempt)}.json`;
48
- const filePath = path.join(historyDir, name);
49
- if (!fs.existsSync(filePath)) {
50
- return filePath;
51
- }
52
- attempt = new Date(attempt.getTime() + 1000);
53
- }
54
-
55
- throw new Error('无法生成唯一的历史记录文件名');
56
- };
57
-
58
- const normalizeMessagesPayload = (payload) => {
59
- if (Array.isArray(payload)) {
60
- return payload;
61
- }
62
- if (payload && typeof payload === 'object' && Array.isArray(payload.messages)) {
63
- return payload.messages;
64
- }
65
- return null;
66
- };
67
-
68
- const saveHistoryMessages = (filePath, messages) => {
69
- if (!filePath || !Array.isArray(messages)) {
70
- return false;
71
- }
72
- const dir = path.dirname(filePath);
73
- if (!fs.existsSync(dir)) {
74
- fs.mkdirSync(dir, { recursive: true });
75
- }
76
- fs.writeFileSync(filePath, `${JSON.stringify(messages, null, 2)}\n`, 'utf8');
77
- return true;
78
- };
79
-
80
- const readHistoryMessages = (filePath) => {
81
- try {
82
- const raw = fs.readFileSync(filePath, 'utf8');
83
- return normalizeMessagesPayload(JSON.parse(raw));
84
- } catch (error) {
85
- return null;
86
- }
87
- };
88
-
89
- const listHistoryFiles = (projectPath) => {
90
- const historyDir = ensureHistoryDir(projectPath);
91
- try {
92
- const entries = fs.readdirSync(historyDir, { withFileTypes: true });
93
- return entries
94
- .filter((entry) => entry.isFile() && HISTORY_FILENAME_PATTERN.test(entry.name))
95
- .sort((a, b) => b.name.localeCompare(a.name))
96
- .map((entry) => ({
97
- name: entry.name,
98
- filePath: path.join(historyDir, entry.name)
99
- }));
100
- } catch (error) {
101
- return [];
102
- }
103
- };
104
-
105
- const trimHistoryFiles = (maxEntries = MAX_HISTORY_RECORDS, projectPath) => {
106
- if (!Number.isFinite(maxEntries) || maxEntries <= 0) {
107
- return [];
108
- }
109
- const files = listHistoryFiles(projectPath);
110
- if (files.length <= maxEntries) {
111
- return [];
112
- }
113
- const toRemove = files.slice(maxEntries);
114
- toRemove.forEach((entry) => {
115
- try {
116
- fs.unlinkSync(entry.filePath);
117
- } catch (error) {
118
- // ignore deletion errors
119
- }
120
- });
121
- return toRemove.map((entry) => entry.filePath);
122
- };
123
-
124
- const refreshHistoryFilePath = (filePath, projectPath) => {
125
- if (!filePath || typeof filePath !== 'string') {
126
- return null;
127
- }
128
- if (!fs.existsSync(filePath)) {
129
- return null;
130
- }
131
- const dir = ensureHistoryDir(projectPath);
132
- if (path.dirname(filePath) !== dir) {
133
- return filePath;
134
- }
135
- try {
136
- const nextPath = createHistoryFilePath(projectPath);
137
- fs.renameSync(filePath, nextPath);
138
- return nextPath;
139
- } catch (error) {
140
- return filePath;
141
- }
142
- };
143
-
144
- const extractFirstUserQuestion = (messages) => {
145
- if (!Array.isArray(messages)) {
146
- return '';
147
- }
148
- const firstUserMessage = messages.find((msg) => msg && msg.role === 'user');
149
- const text = firstUserMessage && typeof firstUserMessage.content === 'string'
150
- ? firstUserMessage.content.trim()
151
- : '';
152
- return text;
153
- };
154
-
155
- const formatPreviewText = (text, maxLength = 20) => {
156
- if (!text) {
157
- return '(空问题)';
158
- }
159
- const normalized = text.replace(/\s+/g, ' ').trim();
160
- if (normalized.length <= maxLength) {
161
- return normalized;
162
- }
163
- return `${normalized.slice(0, maxLength)}...`;
164
- };
165
-
166
- module.exports = {
167
- HISTORY_BASE_DIR,
168
- MAX_HISTORY_RECORDS,
169
- getProjectMd5,
170
- getProjectHistoryDir,
171
- ensureHistoryDir,
172
- createHistoryFilePath,
173
- saveHistoryMessages,
174
- readHistoryMessages,
175
- listHistoryFiles,
176
- trimHistoryFiles,
177
- refreshHistoryFilePath,
178
- extractFirstUserQuestion,
179
- formatPreviewText,
180
- formatTimestamp
181
- };
1
+ const fs = require('fs');
2
+ const os = require('os');
3
+ const path = require('path');
4
+ const crypto = require('crypto');
5
+
6
+ const MAX_HISTORY_RECORDS = 50;
7
+ const HISTORY_BASE_DIR = path.join(os.homedir(), '.mini', 'cli');
8
+ const HISTORY_FILENAME_PATTERN = /^\d{14}\.json$/;
9
+
10
+ const resolveProjectPath = (projectPath) => path.resolve(projectPath || process.cwd());
11
+
12
+ const getProjectMd5 = (projectPath) => (
13
+ crypto
14
+ .createHash('md5')
15
+ .update(resolveProjectPath(projectPath), 'utf8')
16
+ .digest('hex')
17
+ );
18
+
19
+ const getProjectHistoryDir = (projectPath) => path.join(HISTORY_BASE_DIR, getProjectMd5(projectPath));
20
+
21
+ const ensureHistoryDir = (projectPath) => {
22
+ const dir = getProjectHistoryDir(projectPath);
23
+ if (!fs.existsSync(dir)) {
24
+ fs.mkdirSync(dir, { recursive: true });
25
+ }
26
+ return dir;
27
+ };
28
+
29
+ const pad = (value, length = 2) => String(value).padStart(length, '0');
30
+
31
+ const formatTimestamp = (date = new Date()) => {
32
+ const year = date.getFullYear();
33
+ const month = pad(date.getMonth() + 1);
34
+ const day = pad(date.getDate());
35
+ const hour = pad(date.getHours());
36
+ const minute = pad(date.getMinutes());
37
+ const second = pad(date.getSeconds());
38
+ return `${year}${month}${day}${hour}${minute}${second}`;
39
+ };
40
+
41
+ const createHistoryFilePath = (projectPath) => {
42
+ const historyDir = ensureHistoryDir(projectPath);
43
+ let attempt = new Date();
44
+ const maxAttempts = 120;
45
+
46
+ for (let i = 0; i < maxAttempts; i += 1) {
47
+ const name = `${formatTimestamp(attempt)}.json`;
48
+ const filePath = path.join(historyDir, name);
49
+ if (!fs.existsSync(filePath)) {
50
+ return filePath;
51
+ }
52
+ attempt = new Date(attempt.getTime() + 1000);
53
+ }
54
+
55
+ throw new Error('无法生成唯一的历史记录文件名');
56
+ };
57
+
58
+ const normalizeMessagesPayload = (payload) => {
59
+ if (Array.isArray(payload)) {
60
+ return payload;
61
+ }
62
+ if (payload && typeof payload === 'object' && Array.isArray(payload.messages)) {
63
+ return payload.messages;
64
+ }
65
+ return null;
66
+ };
67
+
68
+ const saveHistoryMessages = (filePath, messages) => {
69
+ if (!filePath || !Array.isArray(messages)) {
70
+ return false;
71
+ }
72
+ const dir = path.dirname(filePath);
73
+ if (!fs.existsSync(dir)) {
74
+ fs.mkdirSync(dir, { recursive: true });
75
+ }
76
+ fs.writeFileSync(filePath, `${JSON.stringify(messages, null, 2)}\n`, 'utf8');
77
+ return true;
78
+ };
79
+
80
+ const readHistoryMessages = (filePath) => {
81
+ try {
82
+ const raw = fs.readFileSync(filePath, 'utf8');
83
+ return normalizeMessagesPayload(JSON.parse(raw));
84
+ } catch (error) {
85
+ return null;
86
+ }
87
+ };
88
+
89
+ const listHistoryFiles = (projectPath) => {
90
+ const historyDir = ensureHistoryDir(projectPath);
91
+ try {
92
+ const entries = fs.readdirSync(historyDir, { withFileTypes: true });
93
+ return entries
94
+ .filter((entry) => entry.isFile() && HISTORY_FILENAME_PATTERN.test(entry.name))
95
+ .sort((a, b) => b.name.localeCompare(a.name))
96
+ .map((entry) => ({
97
+ name: entry.name,
98
+ filePath: path.join(historyDir, entry.name)
99
+ }));
100
+ } catch (error) {
101
+ return [];
102
+ }
103
+ };
104
+
105
+ const trimHistoryFiles = (maxEntries = MAX_HISTORY_RECORDS, projectPath) => {
106
+ if (!Number.isFinite(maxEntries) || maxEntries <= 0) {
107
+ return [];
108
+ }
109
+ const files = listHistoryFiles(projectPath);
110
+ if (files.length <= maxEntries) {
111
+ return [];
112
+ }
113
+ const toRemove = files.slice(maxEntries);
114
+ toRemove.forEach((entry) => {
115
+ try {
116
+ fs.unlinkSync(entry.filePath);
117
+ } catch (error) {
118
+ // ignore deletion errors
119
+ }
120
+ });
121
+ return toRemove.map((entry) => entry.filePath);
122
+ };
123
+
124
+ const refreshHistoryFilePath = (filePath, projectPath) => {
125
+ if (!filePath || typeof filePath !== 'string') {
126
+ return null;
127
+ }
128
+ if (!fs.existsSync(filePath)) {
129
+ return null;
130
+ }
131
+ const dir = ensureHistoryDir(projectPath);
132
+ if (path.dirname(filePath) !== dir) {
133
+ return filePath;
134
+ }
135
+ try {
136
+ const nextPath = createHistoryFilePath(projectPath);
137
+ fs.renameSync(filePath, nextPath);
138
+ return nextPath;
139
+ } catch (error) {
140
+ return filePath;
141
+ }
142
+ };
143
+
144
+ const extractFirstUserQuestion = (messages) => {
145
+ if (!Array.isArray(messages)) {
146
+ return '';
147
+ }
148
+ const firstUserMessage = messages.find((msg) => msg && msg.role === 'user');
149
+ const text = firstUserMessage && typeof firstUserMessage.content === 'string'
150
+ ? firstUserMessage.content.trim()
151
+ : '';
152
+ return text;
153
+ };
154
+
155
+ const formatPreviewText = (text, maxLength = 20) => {
156
+ if (!text) {
157
+ return '(空问题)';
158
+ }
159
+ const normalized = text.replace(/\s+/g, ' ').trim();
160
+ if (normalized.length <= maxLength) {
161
+ return normalized;
162
+ }
163
+ return `${normalized.slice(0, maxLength)}...`;
164
+ };
165
+
166
+ module.exports = {
167
+ HISTORY_BASE_DIR,
168
+ MAX_HISTORY_RECORDS,
169
+ getProjectMd5,
170
+ getProjectHistoryDir,
171
+ ensureHistoryDir,
172
+ createHistoryFilePath,
173
+ saveHistoryMessages,
174
+ readHistoryMessages,
175
+ listHistoryFiles,
176
+ trimHistoryFiles,
177
+ refreshHistoryFilePath,
178
+ extractFirstUserQuestion,
179
+ formatPreviewText,
180
+ formatTimestamp
181
+ };