g2log 1.4.5 → 1.5.1
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/.claude/settings.local.json +7 -0
- package/CLAUDE.md +169 -0
- package/git-user-log.js +127 -93
- package/package.json +2 -2
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
`g2log` (AI-Git 用户日报生成工具) is a Node.js CLI tool that retrieves Git commit records for specified users within a time range and automatically generates work summaries using AI. It can be run via `npx` without installation or installed globally.
|
|
8
|
+
|
|
9
|
+
## Common Commands
|
|
10
|
+
|
|
11
|
+
### Installation and Setup
|
|
12
|
+
```bash
|
|
13
|
+
# Global installation
|
|
14
|
+
npm install -g g2log
|
|
15
|
+
|
|
16
|
+
# Run via npx (no installation needed)
|
|
17
|
+
npx g2log [options]
|
|
18
|
+
|
|
19
|
+
# Direct execution
|
|
20
|
+
node git-user-log.js
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Configuration Management
|
|
24
|
+
```bash
|
|
25
|
+
# Start interactive configuration wizard
|
|
26
|
+
g2log --config
|
|
27
|
+
|
|
28
|
+
# Set API key for AI summarization
|
|
29
|
+
g2log --set-api-key="YOUR_API_KEY"
|
|
30
|
+
|
|
31
|
+
# Set default author name
|
|
32
|
+
g2log --set-default-author="作者名"
|
|
33
|
+
|
|
34
|
+
# Add repository configuration
|
|
35
|
+
g2log --add-repo="别名" --path="/path/to/repo"
|
|
36
|
+
|
|
37
|
+
# List configured repositories
|
|
38
|
+
g2log --list-repos
|
|
39
|
+
|
|
40
|
+
# Fix configuration file format issues
|
|
41
|
+
g2log --fix-config
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Running the Tool
|
|
45
|
+
```bash
|
|
46
|
+
# Generate daily summary using defaults
|
|
47
|
+
g2log
|
|
48
|
+
|
|
49
|
+
# Specify time range
|
|
50
|
+
g2log --since="2023-01-01" --until="2023-12-31"
|
|
51
|
+
|
|
52
|
+
# Use local repository only
|
|
53
|
+
g2log --local
|
|
54
|
+
|
|
55
|
+
# Save output to file
|
|
56
|
+
g2log --output="today-summary.md"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Architecture
|
|
60
|
+
|
|
61
|
+
### Entry Point and Core Structure
|
|
62
|
+
|
|
63
|
+
- **`git-user-log.js`** - Main CLI entry point (executable, ~1940 lines)
|
|
64
|
+
- Single-file architecture with all functionality inline
|
|
65
|
+
- Uses ES modules via dynamic `import()` for the `ora` spinner library
|
|
66
|
+
- No build process or transpilation required
|
|
67
|
+
|
|
68
|
+
### Key Components
|
|
69
|
+
|
|
70
|
+
1. **Configuration System** (`CONFIG_PATH = ~/.git-user-log-config.json`)
|
|
71
|
+
- `loadConfig()` - Merges user config with `DEFAULT_CONFIG`
|
|
72
|
+
- `saveConfig()` - Persists configuration
|
|
73
|
+
- Handles legacy field migrations (e.g., `deepseek_api_key` → `api_key`)
|
|
74
|
+
- Supports prompt template customization with variable substitution
|
|
75
|
+
|
|
76
|
+
2. **Git Log Retrieval**
|
|
77
|
+
- Single repository: Uses `git -C "{path}" log` with author/time filters
|
|
78
|
+
- Multi-repository: `getLogsFromMultipleRepos()` aggregates from all configured repos
|
|
79
|
+
- Format: `alias | date | hash | message` or simple mode without hash
|
|
80
|
+
|
|
81
|
+
3. **AI Integration**
|
|
82
|
+
- `summarizeWithAI()` - Main orchestrator
|
|
83
|
+
- `getOpenAIResponse()` - OpenAI API with streaming support
|
|
84
|
+
- `getDeepSeekResponse()` - DeepSeek API with streaming support
|
|
85
|
+
- Both use Server-Sent Events (SSE) for real-time output streaming
|
|
86
|
+
|
|
87
|
+
4. **Interactive Configuration Wizard**
|
|
88
|
+
- `setupConfigInteractive()` - Step-by-step CLI prompts
|
|
89
|
+
- Uses Node.js `readline` module for user input
|
|
90
|
+
- Validates Git repository paths before adding
|
|
91
|
+
|
|
92
|
+
### Configuration File Structure
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"api_key": "sk-...",
|
|
97
|
+
"default_author": "用户名",
|
|
98
|
+
"default_since": "today",
|
|
99
|
+
"default_until": "today",
|
|
100
|
+
"model": "deepseek-chat",
|
|
101
|
+
"api_base_url": "https://api.deepseek.com",
|
|
102
|
+
"api_provider": "deepseek",
|
|
103
|
+
"repositories": {
|
|
104
|
+
"别名": "/path/to/repo"
|
|
105
|
+
},
|
|
106
|
+
"prompt_template": "自定义提示词模板,支持 {{GIT_LOGS}} 等变量"
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### CLI Argument Parsing
|
|
111
|
+
|
|
112
|
+
Custom `parseArgs()` function handles:
|
|
113
|
+
- `--key=value` format
|
|
114
|
+
- `--key value` format
|
|
115
|
+
- Boolean flags like `--local`, `--no-color`
|
|
116
|
+
- Special handling for `--save` as alias for `--output`
|
|
117
|
+
|
|
118
|
+
### Color Output System
|
|
119
|
+
|
|
120
|
+
Custom ANSI color implementation:
|
|
121
|
+
- Pre-checks for TTY and `--no-color` flag
|
|
122
|
+
- `colorize()` function wraps text with ANSI codes
|
|
123
|
+
- Custom `createSpinner()` with fallback when `ora` fails to load
|
|
124
|
+
|
|
125
|
+
## Development Notes
|
|
126
|
+
|
|
127
|
+
### Dynamic Import Pattern
|
|
128
|
+
|
|
129
|
+
The `ora` module is loaded dynamically to handle potential import failures:
|
|
130
|
+
```javascript
|
|
131
|
+
let ora;
|
|
132
|
+
import('ora').then(module => { ora = module.default; }).catch(...);
|
|
133
|
+
```
|
|
134
|
+
The spinner function checks if `ora` is loaded and provides a fallback.
|
|
135
|
+
|
|
136
|
+
### Streaming Response Handling
|
|
137
|
+
|
|
138
|
+
Both `getOpenAIResponse()` and `getDeepSeekResponse()` implement SSE parsing:
|
|
139
|
+
- Buffer incomplete messages
|
|
140
|
+
- Split by `\n\n` delimiter
|
|
141
|
+
- Parse `data: {json}` lines
|
|
142
|
+
- Handle `[DONE]` termination signal
|
|
143
|
+
|
|
144
|
+
### Variable Substitution in Prompts
|
|
145
|
+
|
|
146
|
+
The `prompt_template` supports multiple variable formats for compatibility:
|
|
147
|
+
- `{{GIT_LOGS}}` and `{log_content}` for git logs
|
|
148
|
+
- `{{AUTHOR}}` and `{author}` for author name
|
|
149
|
+
- `{{SINCE}}`/`{{UNTIL}}` and `{since}`/`{until}` for dates
|
|
150
|
+
|
|
151
|
+
### NPX Detection
|
|
152
|
+
|
|
153
|
+
The tool detects NPX execution via environment variables:
|
|
154
|
+
```javascript
|
|
155
|
+
const isRunningWithNpx = process.env.npm_lifecycle_event === 'npx' ||
|
|
156
|
+
process.env.npm_execpath?.includes('npx') ||
|
|
157
|
+
process.env.npm_command === 'exec';
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Version and Publishing
|
|
161
|
+
|
|
162
|
+
- Version is defined in `package.json` (currently 1.4.4)
|
|
163
|
+
- See `PUBLISH.md` for publishing workflow to npm
|
|
164
|
+
- Postinstall script sets executable permissions on `git-user-log.js`
|
|
165
|
+
|
|
166
|
+
## Dependencies
|
|
167
|
+
|
|
168
|
+
- **ora** - CLI spinner for loading states (dynamically imported)
|
|
169
|
+
- **No build tools** - Pure Node.js with standard library modules
|
package/git-user-log.js
CHANGED
|
@@ -77,7 +77,6 @@ function colorize(text, color) {
|
|
|
77
77
|
|
|
78
78
|
// 配置文件路径
|
|
79
79
|
const CONFIG_PATH = path.join(os.homedir(), '.git-user-log-config.json');
|
|
80
|
-
console.log(CONFIG_PATH);
|
|
81
80
|
// 默认配置
|
|
82
81
|
const DEFAULT_CONFIG = {
|
|
83
82
|
api_key: '',
|
|
@@ -89,21 +88,21 @@ const DEFAULT_CONFIG = {
|
|
|
89
88
|
api_provider: 'deepseek', // API提供商: deepseek或openai
|
|
90
89
|
repositories: {},
|
|
91
90
|
prompt_template: `
|
|
92
|
-
请根据下面的Git提交记录,用3-5
|
|
91
|
+
请根据下面的Git提交记录,用3-5句话简洁地总结工作内容。
|
|
93
92
|
|
|
94
93
|
以下是Git提交记录:
|
|
95
94
|
|
|
96
95
|
{{GIT_LOGS}}
|
|
97
96
|
|
|
98
97
|
要求:
|
|
99
|
-
1.
|
|
100
|
-
2.
|
|
98
|
+
1. 按项目、日期和作者组织内容
|
|
99
|
+
2. 每个项目每天每个作者的工作内容用3-5句话概括
|
|
101
100
|
3. 使用清晰、专业但不晦涩的语言
|
|
102
101
|
4. 突出重要的功能开发、问题修复和优化改进
|
|
103
102
|
5. 适合放入工作日报的简洁描述
|
|
104
103
|
6. 输出格式为:【日期】:
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
【项目名称】 - 【作者】 - 【工作内容概述】
|
|
105
|
+
【项目名称】 - 【作者】 - 【工作内容概述】
|
|
107
106
|
7. 回复不要出现多余的内容,非必要不要用markdown格式
|
|
108
107
|
`
|
|
109
108
|
};
|
|
@@ -240,20 +239,42 @@ function removeRepository(alias) {
|
|
|
240
239
|
// 获取仓库路径(支持别名)
|
|
241
240
|
function getRepositoryPath(repoIdentifier, useLocalRepo) {
|
|
242
241
|
if (useLocalRepo) {
|
|
243
|
-
return process.cwd();
|
|
242
|
+
return findGitRepository(process.cwd());
|
|
244
243
|
}
|
|
245
|
-
|
|
246
|
-
if (!repoIdentifier) return process.cwd();
|
|
247
|
-
|
|
244
|
+
|
|
245
|
+
if (!repoIdentifier) return findGitRepository(process.cwd());
|
|
246
|
+
|
|
248
247
|
const config = loadConfig();
|
|
249
248
|
if (config.repositories && config.repositories[repoIdentifier]) {
|
|
250
249
|
return config.repositories[repoIdentifier];
|
|
251
250
|
}
|
|
252
|
-
|
|
251
|
+
|
|
253
252
|
// 如果不是别名,就当作路径处理
|
|
254
253
|
return repoIdentifier;
|
|
255
254
|
}
|
|
256
255
|
|
|
256
|
+
// 向上搜索 Git 仓库根目录
|
|
257
|
+
function findGitRepository(startPath) {
|
|
258
|
+
let currentPath = path.resolve(startPath);
|
|
259
|
+
|
|
260
|
+
while (currentPath !== path.dirname(currentPath)) {
|
|
261
|
+
const gitDir = path.join(currentPath, '.git');
|
|
262
|
+
if (fs.existsSync(gitDir)) {
|
|
263
|
+
return currentPath;
|
|
264
|
+
}
|
|
265
|
+
currentPath = path.dirname(currentPath);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// 检查根目录
|
|
269
|
+
const gitDir = path.join(currentPath, '.git');
|
|
270
|
+
if (fs.existsSync(gitDir)) {
|
|
271
|
+
return currentPath;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// 未找到 Git 仓库
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
|
|
257
278
|
// 列出所有配置的仓库
|
|
258
279
|
function listRepositories() {
|
|
259
280
|
const config = loadConfig();
|
|
@@ -416,32 +437,38 @@ function showHelp() {
|
|
|
416
437
|
--until <date> 结束日期 (默认: 今天)
|
|
417
438
|
--days <number> 查询最近n天的记录 (默认: 7)
|
|
418
439
|
|
|
440
|
+
过滤参数:
|
|
441
|
+
--author <name> 按作者过滤提交 (可选,不指定则获取所有作者)
|
|
442
|
+
--local 仅处理本地仓库
|
|
443
|
+
|
|
419
444
|
显示设置:
|
|
420
445
|
--no-color 禁用彩色输出
|
|
421
446
|
--save 保存结果到文件
|
|
447
|
+
--output <file> 保存到指定文件
|
|
422
448
|
--debug 显示调试信息
|
|
423
449
|
--show-prompt 显示完整的prompt内容
|
|
424
450
|
--version 显示当前版本号
|
|
425
451
|
|
|
426
452
|
配置管理:
|
|
427
453
|
--config 启动交互式配置向导
|
|
428
|
-
--set-api-key
|
|
454
|
+
--set-api-key 设置API密钥
|
|
429
455
|
--set-api-provider 设置API提供商 (OpenAI/DeepSeek)
|
|
430
|
-
--set-api-
|
|
456
|
+
--set-api-url 设置API基础URL
|
|
431
457
|
--set-ai-model 设置AI模型
|
|
432
|
-
--set-default-author 设置默认作者
|
|
433
|
-
--add-repo
|
|
434
|
-
--remove-repo
|
|
435
|
-
--list-repos
|
|
436
|
-
--uninstall
|
|
458
|
+
--set-default-author 设置默认作者 (可选)
|
|
459
|
+
--add-repo <alias> --path <path> 添加仓库配置
|
|
460
|
+
--remove-repo <alias> 移除仓库配置
|
|
461
|
+
--list-repos 列出所有配置的仓库
|
|
462
|
+
--uninstall 删除g2log配置文件 (~/.git-user-log-config.json)
|
|
437
463
|
|
|
438
464
|
示例:
|
|
465
|
+
g2log # 获取所有作者的提交
|
|
466
|
+
g2log --author "张三" # 只获取张三的提交
|
|
439
467
|
g2log --since "2024-01-01" --until "2024-01-31"
|
|
440
|
-
g2log --days 30
|
|
468
|
+
g2log --days 30 --local
|
|
441
469
|
g2log --config
|
|
442
470
|
g2log --set-api-key "your-api-key"
|
|
443
|
-
g2log --add-repo "alias" "path/to/repo"
|
|
444
|
-
g2log --remove-repo "alias"
|
|
471
|
+
g2log --add-repo "alias" --path "/path/to/repo"
|
|
445
472
|
g2log --list-repos
|
|
446
473
|
g2log --version
|
|
447
474
|
`);
|
|
@@ -678,7 +705,7 @@ async function summarizeWithAI(gitLogs, author, since, until, spinner = null) {
|
|
|
678
705
|
const apiProvider = config.api_provider || 'openai';
|
|
679
706
|
const apiBaseURL = config.api_base_url || '';
|
|
680
707
|
|
|
681
|
-
let prompt = config.prompt_template || `请根据以下Git
|
|
708
|
+
let prompt = config.prompt_template || `请根据以下Git提交记录,总结工作内容。
|
|
682
709
|
按照类别进行归纳,突出重点任务和成就。
|
|
683
710
|
用清晰的标题和小标题组织内容,确保总结全面且易于阅读。
|
|
684
711
|
|
|
@@ -686,10 +713,11 @@ Git提交记录:
|
|
|
686
713
|
{{GIT_LOGS}}`;
|
|
687
714
|
|
|
688
715
|
// 替换变量 - 支持多种变量格式以兼容用户自定义模板
|
|
716
|
+
const authorText = author || '所有作者';
|
|
689
717
|
prompt = prompt.replace('{{GIT_LOGS}}', gitLogs)
|
|
690
718
|
.replace('{log_content}', gitLogs) // 添加对{log_content}格式的支持
|
|
691
|
-
.replace('{{AUTHOR}}',
|
|
692
|
-
.replace('{author}',
|
|
719
|
+
.replace('{{AUTHOR}}', authorText)
|
|
720
|
+
.replace('{author}', authorText)
|
|
693
721
|
.replace('{{SINCE}}', since)
|
|
694
722
|
.replace('{since}', since)
|
|
695
723
|
.replace('{{UNTIL}}', until)
|
|
@@ -710,7 +738,8 @@ Git提交记录:
|
|
|
710
738
|
const providerLower = apiProvider.toLowerCase();
|
|
711
739
|
|
|
712
740
|
// 输出AI总结的标题信息
|
|
713
|
-
|
|
741
|
+
const summaryTitle = author ? `${author} 的工作总结` : '团队工作总结';
|
|
742
|
+
console.log(`\n${colorize('📊 ' + summaryTitle, 'bright')}`);
|
|
714
743
|
console.log(`${colorize('📅 时间范围: ' + since + ' 至 ' + until, 'green')}`);
|
|
715
744
|
console.log(`${colorize('🤖 使用模型: ' + modelName, 'cyan')}`);
|
|
716
745
|
console.log(`${colorize('=' .repeat(30), 'bright')}\n`);
|
|
@@ -1108,8 +1137,13 @@ async function getLogsFromMultipleRepos(author, since, until, options) {
|
|
|
1108
1137
|
spinner.update(`🔍 正在检查仓库 ${alias} (${repoPath})...`);
|
|
1109
1138
|
execSync(`git -C "${repoPath}" rev-parse --is-inside-work-tree`, { stdio: 'ignore' });
|
|
1110
1139
|
|
|
1111
|
-
// 构建Git
|
|
1112
|
-
let command = `git -C "${repoPath}" log --
|
|
1140
|
+
// 构建Git命令(author 现在是可选的)
|
|
1141
|
+
let command = `git -C "${repoPath}" log --since="${since}" --until="${until}" --date=format:"%Y-%m-%d %H:%M:%S"`;
|
|
1142
|
+
|
|
1143
|
+
// 如果指定了 author,则添加过滤器
|
|
1144
|
+
if (author && author.trim()) {
|
|
1145
|
+
command = `git -C "${repoPath}" log --author="${author}" --since="${since}" --until="${until}" --date=format:"%Y-%m-%d %H:%M:%S"`;
|
|
1146
|
+
}
|
|
1113
1147
|
|
|
1114
1148
|
// 添加选项
|
|
1115
1149
|
if (options.noMerges) {
|
|
@@ -1145,7 +1179,8 @@ async function getLogsFromMultipleRepos(author, since, until, options) {
|
|
|
1145
1179
|
if (logCount > 0) {
|
|
1146
1180
|
spinner.stop(`✅ 从仓库 ${repos > 1 ? `${repos} 个仓库` : Object.keys(config.repositories)[0]} 获取到 ${logCount} 条提交`);
|
|
1147
1181
|
} else {
|
|
1148
|
-
|
|
1182
|
+
const authorText = author ? author : '所有作者';
|
|
1183
|
+
spinner.stop(`📭 未找到 ${authorText} 在 ${since} 至 ${until} 期间的提交记录`);
|
|
1149
1184
|
}
|
|
1150
1185
|
|
|
1151
1186
|
return allLogs;
|
|
@@ -1222,7 +1257,7 @@ function checkConfig(silent = false) {
|
|
|
1222
1257
|
if (!silent) console.log(colorize('⚠️ 检测到配置缺失: 配置文件不存在', 'red'));
|
|
1223
1258
|
return {
|
|
1224
1259
|
needsConfig: true,
|
|
1225
|
-
missingConfig: ['api_key'
|
|
1260
|
+
missingConfig: ['api_key'],
|
|
1226
1261
|
reason: '配置文件不存在',
|
|
1227
1262
|
currentConfig: null
|
|
1228
1263
|
};
|
|
@@ -1232,14 +1267,12 @@ function checkConfig(silent = false) {
|
|
|
1232
1267
|
const config = loadConfig();
|
|
1233
1268
|
const missingConfig = [];
|
|
1234
1269
|
|
|
1235
|
-
//
|
|
1270
|
+
// 检查关键配置是否存在(default_author 现在是可选的)
|
|
1236
1271
|
if (!config.api_key) {
|
|
1237
1272
|
missingConfig.push('api_key');
|
|
1238
1273
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
missingConfig.push('default_author');
|
|
1242
|
-
}
|
|
1274
|
+
|
|
1275
|
+
// default_author 现在是可选的,不再强制要求
|
|
1243
1276
|
|
|
1244
1277
|
// 设置默认时间范围(如果不存在)
|
|
1245
1278
|
if (!config.default_since) {
|
|
@@ -1281,7 +1314,7 @@ function checkConfig(silent = false) {
|
|
|
1281
1314
|
}
|
|
1282
1315
|
return {
|
|
1283
1316
|
needsConfig: true,
|
|
1284
|
-
missingConfig: ['api_key'
|
|
1317
|
+
missingConfig: ['api_key'],
|
|
1285
1318
|
reason: `配置文件解析错误: ${error.message}`,
|
|
1286
1319
|
currentConfig: null
|
|
1287
1320
|
};
|
|
@@ -1313,45 +1346,45 @@ async function setupConfigInteractive() {
|
|
|
1313
1346
|
console.log(colorize('ℹ️ 检测到现有配置,将在其基础上进行修改。', 'blue'));
|
|
1314
1347
|
} else {
|
|
1315
1348
|
console.log(colorize('ℹ️ 未检测到配置文件,将创建新配置。', 'blue'));
|
|
1316
|
-
config = {
|
|
1349
|
+
config = {
|
|
1317
1350
|
repositories: {},
|
|
1318
|
-
prompt_template: `请根据下面的Git提交记录,用3-5
|
|
1351
|
+
prompt_template: `请根据下面的Git提交记录,用3-5句话简洁地总结工作内容。
|
|
1319
1352
|
|
|
1320
1353
|
以下是Git提交记录:
|
|
1321
1354
|
|
|
1322
1355
|
{log_content}
|
|
1323
1356
|
|
|
1324
1357
|
要求:
|
|
1325
|
-
1.
|
|
1326
|
-
2.
|
|
1358
|
+
1. 按项目、日期和作者组织内容
|
|
1359
|
+
2. 每个项目每天每个作者的工作内容用3-5句话概括
|
|
1327
1360
|
3. 使用清晰、专业但不晦涩的语言
|
|
1328
1361
|
4. 突出重要的功能开发、问题修复和优化改进
|
|
1329
1362
|
5. 适合放入工作日报的简洁描述
|
|
1330
1363
|
6. 输出格式为:【日期】:
|
|
1331
|
-
|
|
1332
|
-
|
|
1364
|
+
【项目名称】 - 【作者】 - 【工作内容概述】
|
|
1365
|
+
【项目名称】 - 【作者】 - 【工作内容概述】
|
|
1333
1366
|
7. 回复不要出现多余的内容,非必要不要用markdown格式`
|
|
1334
1367
|
};
|
|
1335
1368
|
}
|
|
1336
1369
|
} catch (error) {
|
|
1337
1370
|
console.log(colorize('⚠️ 读取配置文件时出错,将创建新配置。', 'yellow'));
|
|
1338
|
-
config = {
|
|
1371
|
+
config = {
|
|
1339
1372
|
repositories: {},
|
|
1340
|
-
prompt_template: `请根据下面的Git提交记录,用3-5
|
|
1373
|
+
prompt_template: `请根据下面的Git提交记录,用3-5句话简洁地总结工作内容。
|
|
1341
1374
|
|
|
1342
1375
|
以下是Git提交记录:
|
|
1343
1376
|
|
|
1344
1377
|
{log_content}
|
|
1345
1378
|
|
|
1346
1379
|
要求:
|
|
1347
|
-
1.
|
|
1348
|
-
2.
|
|
1380
|
+
1. 按项目、日期和作者组织内容
|
|
1381
|
+
2. 每个项目每天每个作者的工作内容用3-5句话概括
|
|
1349
1382
|
3. 使用清晰、专业但不晦涩的语言
|
|
1350
1383
|
4. 突出重要的功能开发、问题修复和优化改进
|
|
1351
1384
|
5. 适合放入工作日报的简洁描述
|
|
1352
1385
|
6. 输出格式为:【日期】:
|
|
1353
|
-
|
|
1354
|
-
|
|
1386
|
+
【项目名称】 - 【作者】 - 【工作内容概述】
|
|
1387
|
+
【项目名称】 - 【作者】 - 【工作内容概述】
|
|
1355
1388
|
7. 回复不要出现多余的内容,非必要不要用markdown格式`
|
|
1356
1389
|
};
|
|
1357
1390
|
}
|
|
@@ -1427,13 +1460,19 @@ async function setupConfigInteractive() {
|
|
|
1427
1460
|
console.log(colorize(' ℹ️ API密钥保持不变', 'blue'));
|
|
1428
1461
|
}
|
|
1429
1462
|
|
|
1430
|
-
// 步骤5:
|
|
1431
|
-
console.log(colorize('\n👤 步骤5:
|
|
1463
|
+
// 步骤5: 设置默认作者(可选)
|
|
1464
|
+
console.log(colorize('\n👤 步骤5: 设置默认作者(可选)', 'yellow'));
|
|
1432
1465
|
console.log(colorize(' (示例: 张三, user@example.com, 或Git提交时使用的用户名)', 'cyan'));
|
|
1466
|
+
console.log(colorize(' (留空则不过滤,获取所有作者的提交记录)', 'cyan'));
|
|
1433
1467
|
const existingAuthor = config.default_author || '';
|
|
1434
|
-
const authorInput = await question(colorize(` 请输入默认作者名称 [${existingAuthor}]: `, 'green'));
|
|
1435
|
-
|
|
1436
|
-
|
|
1468
|
+
const authorInput = await question(colorize(` 请输入默认作者名称 [${existingAuthor || '留空'}] (可选,按Enter跳过): `, 'green'));
|
|
1469
|
+
if (authorInput.trim() !== '') {
|
|
1470
|
+
config.default_author = authorInput.trim();
|
|
1471
|
+
console.log(colorize(` ✅ 默认作者已设置为: ${config.default_author}`, 'green'));
|
|
1472
|
+
} else {
|
|
1473
|
+
config.default_author = '';
|
|
1474
|
+
console.log(colorize(` ℹ️ 未设置默认作者,将获取所有作者的提交`, 'blue'));
|
|
1475
|
+
}
|
|
1437
1476
|
|
|
1438
1477
|
// 步骤6: 设置默认时间范围(可选)
|
|
1439
1478
|
console.log(colorize('\n🕒 步骤6: 设置默认时间范围(可选)', 'yellow'));
|
|
@@ -1582,42 +1621,21 @@ async function getGitLogs() {
|
|
|
1582
1621
|
if (isRunningWithNpx || !fs.existsSync(CONFIG_PATH)) {
|
|
1583
1622
|
// 对于NPX运行或首次使用(无配置文件),显示提示并询问是否配置
|
|
1584
1623
|
console.log(colorize('\n⚠️ 检测到配置缺失: ' + configStatus.reason, 'yellow'));
|
|
1585
|
-
|
|
1586
|
-
console.log(colorize('❗ 必须设置默认作者才能使用此工具。', 'red'));
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1624
|
+
|
|
1589
1625
|
// 创建readline接口进行简单询问
|
|
1590
1626
|
const rl = readline.createInterface({
|
|
1591
1627
|
input: process.stdin,
|
|
1592
1628
|
output: process.stdout
|
|
1593
1629
|
});
|
|
1594
|
-
|
|
1630
|
+
|
|
1595
1631
|
const question = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
1596
1632
|
const answer = await question(colorize('❓ 是否现在进行配置?(y/n): ', 'cyan'));
|
|
1597
1633
|
rl.close();
|
|
1598
|
-
|
|
1634
|
+
|
|
1599
1635
|
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
1600
1636
|
// 启动配置向导
|
|
1601
1637
|
await setupConfigInteractive();
|
|
1602
|
-
// 配置完成后,重新加载配置
|
|
1603
|
-
const config = loadConfig();
|
|
1604
|
-
|
|
1605
|
-
// 如果依然缺少必要配置项,提示并退出
|
|
1606
|
-
if (!config.default_author || config.default_author === '') {
|
|
1607
|
-
console.log(colorize('\n❌ 错误: 未设置默认作者,这是必需的。', 'red'));
|
|
1608
|
-
console.log(colorize('💡 请使用 g2log --set-default-author="用户名" 进行设置后再试。', 'yellow'));
|
|
1609
|
-
process.exit(1);
|
|
1610
|
-
}
|
|
1611
|
-
} else if (configStatus.missingConfig.includes('default_author')) {
|
|
1612
|
-
// 如果用户拒绝配置且缺少必要的default_author,提示并退出
|
|
1613
|
-
console.log(colorize('\n❌ 错误: 未设置默认作者,这是必需的。', 'red'));
|
|
1614
|
-
console.log(colorize('💡 请使用 g2log --set-default-author="用户名" 进行设置后再试。', 'yellow'));
|
|
1615
|
-
process.exit(1);
|
|
1616
1638
|
}
|
|
1617
|
-
} else if (configStatus.missingConfig.includes('default_author')) {
|
|
1618
|
-
// 对于非NPX运行但缺少必要default_author的情况,直接错误提示
|
|
1619
|
-
console.error(colorize('❌ 错误: 配置文件中未设置默认作者。请使用 --set-default-author="用户名" 设置默认作者', 'red'));
|
|
1620
|
-
process.exit(1);
|
|
1621
1639
|
}
|
|
1622
1640
|
}
|
|
1623
1641
|
}
|
|
@@ -1765,22 +1783,18 @@ async function getGitLogs() {
|
|
|
1765
1783
|
// 显示NPX运行信息
|
|
1766
1784
|
showNpxInfo();
|
|
1767
1785
|
|
|
1768
|
-
//
|
|
1786
|
+
// 使用参数值或默认配置(author 现在是可选的)
|
|
1769
1787
|
const useLocalRepo = args.local === true;
|
|
1770
|
-
const author = config.default_author;
|
|
1788
|
+
const author = args.author || config.default_author || ''; // 支持命令行参数,可为空
|
|
1771
1789
|
const since = args.since || config.default_since;
|
|
1772
1790
|
const until = args.until || config.default_until;
|
|
1773
|
-
|
|
1791
|
+
|
|
1774
1792
|
// 其他参数从配置文件获取
|
|
1775
1793
|
const simpleMode = true; // 总是使用简单模式
|
|
1776
1794
|
const aiSummary = true; // 总是使用AI总结
|
|
1777
1795
|
const outputFile = args.output;
|
|
1778
|
-
|
|
1779
|
-
//
|
|
1780
|
-
if (!author) {
|
|
1781
|
-
console.error(colorize('错误: 配置文件中未设置默认作者。请使用 --set-default-author="用户名" 设置默认作者', 'red'));
|
|
1782
|
-
process.exit(1);
|
|
1783
|
-
}
|
|
1796
|
+
|
|
1797
|
+
// author 现在是可选的,不再强制验证
|
|
1784
1798
|
|
|
1785
1799
|
// 多仓库处理 - 如果不是--local模式,尝试处理配置中的所有仓库
|
|
1786
1800
|
if (!useLocalRepo) {
|
|
@@ -1819,9 +1833,21 @@ async function getGitLogs() {
|
|
|
1819
1833
|
}
|
|
1820
1834
|
|
|
1821
1835
|
// 单仓库处理逻辑 - 当使用local模式或没有配置多个仓库时
|
|
1822
|
-
|
|
1836
|
+
// 使用 findGitRepository 自动向上搜索 Git 仓库
|
|
1837
|
+
let repoPath;
|
|
1838
|
+
if (useLocalRepo) {
|
|
1839
|
+
repoPath = findGitRepository(process.cwd());
|
|
1840
|
+
} else {
|
|
1841
|
+
repoPath = Object.values(config.repositories)[0] || findGitRepository(process.cwd());
|
|
1842
|
+
}
|
|
1823
1843
|
|
|
1824
1844
|
// 检查仓库路径是否有效
|
|
1845
|
+
if (!repoPath) {
|
|
1846
|
+
console.error(colorize(`❌ 错误: 未找到 Git 仓库。已从当前目录向上搜索到根目录。`, 'red'));
|
|
1847
|
+
console.error(colorize(`💡 提示: 请确保你在 Git 仓库内运行此命令,或使用 --add-repo 添加仓库路径`, 'yellow'));
|
|
1848
|
+
process.exit(1);
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1825
1851
|
try {
|
|
1826
1852
|
const pathSpinner = spinner.start(`🔍 检查仓库路径: ${repoPath}`);
|
|
1827
1853
|
execSync(`git -C "${repoPath}" rev-parse --is-inside-work-tree`, { stdio: 'ignore' });
|
|
@@ -1832,15 +1858,21 @@ async function getGitLogs() {
|
|
|
1832
1858
|
}
|
|
1833
1859
|
|
|
1834
1860
|
// 获取简化格式的日志
|
|
1835
|
-
const
|
|
1836
|
-
const
|
|
1861
|
+
const authorText = author ? author : '所有作者';
|
|
1862
|
+
const logSpinner = spinner.start(`🔍 正在获取 ${authorText} 在 ${since} 至 ${until} 期间的提交记录...`);
|
|
1863
|
+
|
|
1864
|
+
// 构建Git命令(author 现在是可选的)
|
|
1865
|
+
let simpleCommand = `git -C "${repoPath}" log --since="${since}" --until="${until}" --pretty=format:"%ad: %s%n%b%n" --date=format:"%Y-%m-%d %H:%M:%S" --no-merges`;
|
|
1866
|
+
if (author && author.trim()) {
|
|
1867
|
+
simpleCommand = `git -C "${repoPath}" log --author="${author}" --since="${since}" --until="${until}" --pretty=format:"%ad: %s%n%b%n" --date=format:"%Y-%m-%d %H:%M:%S" --no-merges`;
|
|
1868
|
+
}
|
|
1837
1869
|
|
|
1838
1870
|
try {
|
|
1839
1871
|
const result = execSync(simpleCommand, { encoding: 'utf-8' });
|
|
1840
1872
|
logSpinner.stop(`✅ 找到提交记录`);
|
|
1841
1873
|
|
|
1842
1874
|
if (!result.trim()) {
|
|
1843
|
-
const message = `📭 在指定时间范围内没有找到 ${
|
|
1875
|
+
const message = `📭 在指定时间范围内没有找到 ${authorText} 的提交记录。`;
|
|
1844
1876
|
console.log(colorize(message, 'yellow'));
|
|
1845
1877
|
|
|
1846
1878
|
if (outputFile) {
|
|
@@ -1861,20 +1893,22 @@ async function getGitLogs() {
|
|
|
1861
1893
|
// 如果指定了输出文件,保存AI总结结果
|
|
1862
1894
|
if (outputFile) {
|
|
1863
1895
|
const fileSpinner = spinner.start(`💾 正在保存AI总结到文件: ${outputFile}`);
|
|
1864
|
-
|
|
1896
|
+
const summaryTitle = author ? `${author} 的工作总结` : '团队工作总结';
|
|
1897
|
+
fs.writeFileSync(outputFile, `# ${summaryTitle} (${since} 至 ${until})\n\n${aiSummaryResult}`, 'utf-8');
|
|
1865
1898
|
fileSpinner.stop(`✅ AI总结已保存到文件: ${outputFile}`);
|
|
1866
1899
|
return;
|
|
1867
1900
|
}
|
|
1868
1901
|
} catch (error) {
|
|
1869
1902
|
console.error(colorize(`❌ AI总结失败: ${error.message}`, 'red'));
|
|
1870
1903
|
// 如果AI总结失败,输出原始日志
|
|
1871
|
-
console.log(`\n📋 ${
|
|
1904
|
+
console.log(`\n📋 ${authorText} 的Git提交日志 (${since} 至 ${until})\n`);
|
|
1872
1905
|
console.log(result);
|
|
1873
|
-
|
|
1906
|
+
|
|
1874
1907
|
// 如果指定了输出文件,保存结果
|
|
1875
1908
|
if (outputFile) {
|
|
1876
1909
|
const fileSpinner = spinner.start(`💾 正在保存结果到文件: ${outputFile}`);
|
|
1877
|
-
const
|
|
1910
|
+
const summaryTitle = author ? `${author} 的Git提交日志` : 'Git提交日志';
|
|
1911
|
+
const outputContent = `# ${summaryTitle} (${since} 至 ${until})\n\n${result}`;
|
|
1878
1912
|
fs.writeFileSync(outputFile, outputContent, 'utf-8');
|
|
1879
1913
|
fileSpinner.stop(`✅ 结果已保存到文件: ${outputFile}`);
|
|
1880
1914
|
}
|
package/package.json
CHANGED