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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "mcp__auggie-mcp__codebase-retrieval"
5
+ ]
6
+ }
7
+ }
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. 每个项目每天的工作内容用3-5句话概括
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 设置API密钥
454
+ --set-api-key 设置API密钥
429
455
  --set-api-provider 设置API提供商 (OpenAI/DeepSeek)
430
- --set-api-base-url 设置API基础URL
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 删除g2log配置文件 (~/.git-user-log-config.json)
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提交记录,总结${author}在${since}到${until}期间的主要工作内容。
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}}', author)
692
- .replace('{author}', 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
- console.log(`\n${colorize('📊 ' + author + ' 的工作总结', 'bright')}`);
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 --author="${author}" --since="${since}" --until="${until}" --date=format:"%Y-%m-%d %H:%M:%S"`;
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
- spinner.stop(`📭 未找到 ${author} ${since} ${until} 期间的提交记录`);
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', 'default_author'],
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
- if (!config.default_author) {
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', 'default_author'],
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. 每个项目每天的工作内容用3-5句话概括
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. 每个项目每天的工作内容用3-5句话概括
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: 设置默认作者', 'yellow'));
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
- config.default_author = authorInput.trim() || existingAuthor;
1436
- console.log(colorize(` ✅ 默认作者已设置为: ${config.default_author}`, 'green'));
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
- if (configStatus.missingConfig.includes('default_author')) {
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
- const repoPath = useLocalRepo ? process.cwd() : Object.values(config.repositories)[0] || process.cwd();
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 logSpinner = spinner.start(`🔍 正在获取 ${author} ${since} 至 ${until} 期间的提交记录...`);
1836
- const 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`;
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 = `📭 在指定时间范围内没有找到 ${author} 的提交记录。`;
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
- fs.writeFileSync(outputFile, `# ${author} 的工作总结 (${since} ${until})\n\n${aiSummaryResult}`, 'utf-8');
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📋 ${author} 的Git提交日志 (${since} 至 ${until})\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 outputContent = `# ${author} 的Git提交日志 (${since} 至 ${until})\n\n${result}`;
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "g2log",
3
- "version": "1.4.5",
4
- "description": "查询特定用户和时间范围的Git提交记录并通过AI进行总结,可通过npx直接运行",
3
+ "version": "1.5.1",
4
+ "description": "查询Git提交记录并通过AI进行总结,支持多作者、多仓库,可通过npx直接运行",
5
5
  "main": "git-user-log.js",
6
6
  "bin": {
7
7
  "g2log": "./git-user-log.js"