openspec-stat 1.0.0
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/README.md +234 -0
- package/README.zh-CN.md +234 -0
- package/dist/cjs/branch-selector.d.ts +7 -0
- package/dist/cjs/branch-selector.js +124 -0
- package/dist/cjs/cli.d.ts +2 -0
- package/dist/cjs/cli.js +151 -0
- package/dist/cjs/config.d.ts +3 -0
- package/dist/cjs/config.js +66 -0
- package/dist/cjs/formatters.d.ts +7 -0
- package/dist/cjs/formatters.js +222 -0
- package/dist/cjs/git-analyzer.d.ts +10 -0
- package/dist/cjs/git-analyzer.js +170 -0
- package/dist/cjs/i18n/index.d.ts +7 -0
- package/dist/cjs/i18n/index.js +84 -0
- package/dist/cjs/i18n/locales/en.json +72 -0
- package/dist/cjs/i18n/locales/zh-CN.json +72 -0
- package/dist/cjs/index.d.ts +6 -0
- package/dist/cjs/index.js +50 -0
- package/dist/cjs/stats-aggregator.d.ts +7 -0
- package/dist/cjs/stats-aggregator.js +117 -0
- package/dist/cjs/time-utils.d.ts +6 -0
- package/dist/cjs/time-utils.js +55 -0
- package/dist/cjs/types.d.ts +77 -0
- package/dist/cjs/types.js +17 -0
- package/dist/esm/branch-selector.d.ts +7 -0
- package/dist/esm/branch-selector.js +218 -0
- package/dist/esm/cli.d.ts +2 -0
- package/dist/esm/cli.js +157 -0
- package/dist/esm/config.d.ts +3 -0
- package/dist/esm/config.js +78 -0
- package/dist/esm/formatters.d.ts +7 -0
- package/dist/esm/formatters.js +207 -0
- package/dist/esm/git-analyzer.d.ts +10 -0
- package/dist/esm/git-analyzer.js +335 -0
- package/dist/esm/i18n/index.d.ts +7 -0
- package/dist/esm/i18n/index.js +49 -0
- package/dist/esm/i18n/locales/en.json +72 -0
- package/dist/esm/i18n/locales/zh-CN.json +72 -0
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/stats-aggregator.d.ts +7 -0
- package/dist/esm/stats-aggregator.js +149 -0
- package/dist/esm/time-utils.d.ts +6 -0
- package/dist/esm/time-utils.js +31 -0
- package/dist/esm/types.d.ts +77 -0
- package/dist/esm/types.js +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/i18n/index.ts
|
|
20
|
+
var i18n_exports = {};
|
|
21
|
+
__export(i18n_exports, {
|
|
22
|
+
getLanguage: () => getLanguage,
|
|
23
|
+
initI18n: () => initI18n,
|
|
24
|
+
setLanguage: () => setLanguage,
|
|
25
|
+
t: () => t
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(i18n_exports);
|
|
28
|
+
var import_fs = require("fs");
|
|
29
|
+
var import_path = require("path");
|
|
30
|
+
var import_url = require("url");
|
|
31
|
+
var import_meta = {};
|
|
32
|
+
var __filename = (0, import_url.fileURLToPath)(import_meta.url);
|
|
33
|
+
var __dirname = (0, import_path.dirname)(__filename);
|
|
34
|
+
var currentLanguage = "en";
|
|
35
|
+
var translations = {
|
|
36
|
+
"en": {},
|
|
37
|
+
"zh-CN": {}
|
|
38
|
+
};
|
|
39
|
+
function loadTranslations() {
|
|
40
|
+
try {
|
|
41
|
+
const enPath = (0, import_path.join)(__dirname, "locales", "en.json");
|
|
42
|
+
const zhPath = (0, import_path.join)(__dirname, "locales", "zh-CN.json");
|
|
43
|
+
translations["en"] = JSON.parse((0, import_fs.readFileSync)(enPath, "utf-8"));
|
|
44
|
+
translations["zh-CN"] = JSON.parse((0, import_fs.readFileSync)(zhPath, "utf-8"));
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error("Failed to load translations:", error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function setLanguage(lang) {
|
|
50
|
+
currentLanguage = lang;
|
|
51
|
+
}
|
|
52
|
+
function getLanguage() {
|
|
53
|
+
return currentLanguage;
|
|
54
|
+
}
|
|
55
|
+
function t(key, params) {
|
|
56
|
+
if (Object.keys(translations[currentLanguage]).length === 0) {
|
|
57
|
+
loadTranslations();
|
|
58
|
+
}
|
|
59
|
+
let text = translations[currentLanguage][key] || translations["en"][key] || key;
|
|
60
|
+
if (params) {
|
|
61
|
+
Object.keys(params).forEach((paramKey) => {
|
|
62
|
+
text = text.replace(new RegExp(`{{${paramKey}}}`, "g"), String(params[paramKey]));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return text;
|
|
66
|
+
}
|
|
67
|
+
function initI18n(lang) {
|
|
68
|
+
if (lang && (lang === "en" || lang === "zh-CN" || lang === "zh")) {
|
|
69
|
+
setLanguage(lang === "zh" ? "zh-CN" : lang);
|
|
70
|
+
} else {
|
|
71
|
+
const envLang = process.env.LANG || process.env.LANGUAGE || "";
|
|
72
|
+
if (envLang.includes("zh") || envLang.includes("CN")) {
|
|
73
|
+
setLanguage("zh-CN");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
loadTranslations();
|
|
77
|
+
}
|
|
78
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
79
|
+
0 && (module.exports = {
|
|
80
|
+
getLanguage,
|
|
81
|
+
initI18n,
|
|
82
|
+
setLanguage,
|
|
83
|
+
t
|
|
84
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli.description": "Track team members' OpenSpec proposals and code changes in Git repositories",
|
|
3
|
+
"cli.option.repo": "Repository path",
|
|
4
|
+
"cli.option.branches": "Branch list, comma-separated",
|
|
5
|
+
"cli.option.noInteractive": "Disable interactive branch selection",
|
|
6
|
+
"cli.option.since": "Start time (default: yesterday 20:00)",
|
|
7
|
+
"cli.option.until": "End time (default: today 20:00)",
|
|
8
|
+
"cli.option.author": "Filter by specific author",
|
|
9
|
+
"cli.option.json": "Output in JSON format",
|
|
10
|
+
"cli.option.csv": "Output in CSV format",
|
|
11
|
+
"cli.option.markdown": "Output in Markdown format",
|
|
12
|
+
"cli.option.config": "Configuration file path",
|
|
13
|
+
"cli.option.verbose": "Verbose output mode",
|
|
14
|
+
"cli.option.lang": "Language for output (en, zh-CN)",
|
|
15
|
+
|
|
16
|
+
"loading.config": "🔍 Loading configuration...",
|
|
17
|
+
"loading.activeUsers": "🔍 Fetching active users...",
|
|
18
|
+
"loading.analyzing": "🔍 Analyzing commit history...",
|
|
19
|
+
|
|
20
|
+
"info.timeRange": "📅 Time Range: {{since}} ~ {{until}}",
|
|
21
|
+
"info.branches": "🌿 Branches: {{branches}}",
|
|
22
|
+
"info.allBranches": "All branches",
|
|
23
|
+
"info.activeUsers": " Active users (within {{weeks}} weeks): {{users}}",
|
|
24
|
+
"info.foundCommits": "📝 Found {{count}} commits, analyzing...",
|
|
25
|
+
"info.analysisProgress": " Analysis progress: {{current}}/{{total}}",
|
|
26
|
+
"info.qualifyingCommits": "✅ Found {{count}} qualifying commits (containing OpenSpec proposals and code changes)",
|
|
27
|
+
|
|
28
|
+
"warning.noCommits": "⚠️ No commits found matching the criteria",
|
|
29
|
+
"warning.noQualifyingCommits": "⚠️ No commits found containing both OpenSpec proposals and code changes",
|
|
30
|
+
"warning.noBranches": "⚠️ No remote branches found",
|
|
31
|
+
|
|
32
|
+
"error.prefix": "❌ Error:",
|
|
33
|
+
|
|
34
|
+
"branch.fetching": "\n🔍 Fetching active branches...",
|
|
35
|
+
"branch.selectMode": "How would you like to select branches?",
|
|
36
|
+
"branch.mode.select": "Select from active branches",
|
|
37
|
+
"branch.mode.default": "Use default branches from config",
|
|
38
|
+
"branch.mode.custom": "Custom input",
|
|
39
|
+
"branch.selectPrompt": "Select branches to analyze:",
|
|
40
|
+
"branch.customInput": "Enter branch names (comma-separated):",
|
|
41
|
+
"branch.additionalInput": "Enter additional branch names (comma-separated):",
|
|
42
|
+
"branch.customSeparator": "--- Custom input ---",
|
|
43
|
+
"branch.selected": "\n✓ Selected branches:",
|
|
44
|
+
"branch.lastCommit": "last commit: {{date}}",
|
|
45
|
+
|
|
46
|
+
"output.title": "\n📊 OpenSpec Statistics Report\n",
|
|
47
|
+
"output.timeRange": "Time Range: {{since}} ~ {{until}}\n",
|
|
48
|
+
"output.branches": "Branches: {{branches}}\n",
|
|
49
|
+
"output.totalCommits": "Total Commits: {{count}}\n\n",
|
|
50
|
+
"output.proposals": " Proposals: {{proposals}}\n",
|
|
51
|
+
|
|
52
|
+
"table.branch": "Branch",
|
|
53
|
+
"table.period": "Period",
|
|
54
|
+
"table.commits": "Commits",
|
|
55
|
+
"table.proposals": "Proposals",
|
|
56
|
+
"table.codeFiles": "Code Files",
|
|
57
|
+
"table.additions": "Additions",
|
|
58
|
+
"table.deletions": "Deletions",
|
|
59
|
+
"table.netChanges": "Net Changes",
|
|
60
|
+
"table.author": "Author",
|
|
61
|
+
"table.lastCommitDate": "Last Commit Date",
|
|
62
|
+
"table.proposalsList": "Proposals List",
|
|
63
|
+
"table.proposalsCount": "Proposals Count",
|
|
64
|
+
"table.totalDeduplicated": "TOTAL (Deduplicated)",
|
|
65
|
+
|
|
66
|
+
"markdown.title": "# OpenSpec Statistics Report\n\n",
|
|
67
|
+
"markdown.timeRange": "**Time Range**: {{since}} ~ {{until}}\n\n",
|
|
68
|
+
"markdown.branches": "**Branches**: {{branches}}\n\n",
|
|
69
|
+
"markdown.totalCommits": "**Total Commits**: {{count}}\n\n",
|
|
70
|
+
"markdown.statistics": "## Statistics\n\n",
|
|
71
|
+
"markdown.proposalDetails": "\n## Proposal Details\n\n"
|
|
72
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli.description": "追踪团队成员的 OpenSpec 提案和代码变更",
|
|
3
|
+
"cli.option.repo": "仓库路径",
|
|
4
|
+
"cli.option.branches": "分支列表,逗号分隔",
|
|
5
|
+
"cli.option.noInteractive": "禁用交互式分支选择",
|
|
6
|
+
"cli.option.since": "开始时间(默认:昨天 20:00)",
|
|
7
|
+
"cli.option.until": "结束时间(默认:今天 20:00)",
|
|
8
|
+
"cli.option.author": "按特定作者筛选",
|
|
9
|
+
"cli.option.json": "以 JSON 格式输出",
|
|
10
|
+
"cli.option.csv": "以 CSV 格式输出",
|
|
11
|
+
"cli.option.markdown": "以 Markdown 格式输出",
|
|
12
|
+
"cli.option.config": "配置文件路径",
|
|
13
|
+
"cli.option.verbose": "详细输出模式",
|
|
14
|
+
"cli.option.lang": "输出语言(en, zh-CN)",
|
|
15
|
+
|
|
16
|
+
"loading.config": "🔍 正在加载配置...",
|
|
17
|
+
"loading.activeUsers": "🔍 正在获取活跃用户...",
|
|
18
|
+
"loading.analyzing": "🔍 正在分析提交历史...",
|
|
19
|
+
|
|
20
|
+
"info.timeRange": "📅 时间范围:{{since}} ~ {{until}}",
|
|
21
|
+
"info.branches": "🌿 分支:{{branches}}",
|
|
22
|
+
"info.allBranches": "所有分支",
|
|
23
|
+
"info.activeUsers": " 活跃用户({{weeks}} 周内):{{users}}",
|
|
24
|
+
"info.foundCommits": "📝 找到 {{count}} 个提交,正在分析...",
|
|
25
|
+
"info.analysisProgress": " 分析进度:{{current}}/{{total}}",
|
|
26
|
+
"info.qualifyingCommits": "✅ 找到 {{count}} 个符合条件的提交(包含 OpenSpec 提案和代码变更)",
|
|
27
|
+
|
|
28
|
+
"warning.noCommits": "⚠️ 未找到符合条件的提交",
|
|
29
|
+
"warning.noQualifyingCommits": "⚠️ 未找到同时包含 OpenSpec 提案和代码变更的提交",
|
|
30
|
+
"warning.noBranches": "⚠️ 未找到远程分支",
|
|
31
|
+
|
|
32
|
+
"error.prefix": "❌ 错误:",
|
|
33
|
+
|
|
34
|
+
"branch.fetching": "\n🔍 正在获取活跃分支...",
|
|
35
|
+
"branch.selectMode": "您想如何选择分支?",
|
|
36
|
+
"branch.mode.select": "从活跃分支中选择",
|
|
37
|
+
"branch.mode.default": "使用配置文件中的默认分支",
|
|
38
|
+
"branch.mode.custom": "自定义输入",
|
|
39
|
+
"branch.selectPrompt": "选择要分析的分支:",
|
|
40
|
+
"branch.customInput": "输入分支名称(逗号分隔):",
|
|
41
|
+
"branch.additionalInput": "输入额外的分支名称(逗号分隔):",
|
|
42
|
+
"branch.customSeparator": "--- 自定义输入 ---",
|
|
43
|
+
"branch.selected": "\n✓ 已选择的分支:",
|
|
44
|
+
"branch.lastCommit": "最后提交:{{date}}",
|
|
45
|
+
|
|
46
|
+
"output.title": "\n📊 OpenSpec 统计报告\n",
|
|
47
|
+
"output.timeRange": "时间范围:{{since}} ~ {{until}}\n",
|
|
48
|
+
"output.branches": "分支:{{branches}}\n",
|
|
49
|
+
"output.totalCommits": "总提交数:{{count}}\n\n",
|
|
50
|
+
"output.proposals": " 提案:{{proposals}}\n",
|
|
51
|
+
|
|
52
|
+
"table.branch": "分支",
|
|
53
|
+
"table.period": "周期",
|
|
54
|
+
"table.commits": "提交数",
|
|
55
|
+
"table.proposals": "提案数",
|
|
56
|
+
"table.codeFiles": "代码文件",
|
|
57
|
+
"table.additions": "新增行数",
|
|
58
|
+
"table.deletions": "删除行数",
|
|
59
|
+
"table.netChanges": "净变更",
|
|
60
|
+
"table.author": "作者",
|
|
61
|
+
"table.lastCommitDate": "最后提交日期",
|
|
62
|
+
"table.proposalsList": "提案列表",
|
|
63
|
+
"table.proposalsCount": "提案数量",
|
|
64
|
+
"table.totalDeduplicated": "总计(去重)",
|
|
65
|
+
|
|
66
|
+
"markdown.title": "# OpenSpec 统计报告\n\n",
|
|
67
|
+
"markdown.timeRange": "**时间范围**:{{since}} ~ {{until}}\n\n",
|
|
68
|
+
"markdown.branches": "**分支**:{{branches}}\n\n",
|
|
69
|
+
"markdown.totalCommits": "**总提交数**:{{count}}\n\n",
|
|
70
|
+
"markdown.statistics": "## 统计数据\n\n",
|
|
71
|
+
"markdown.proposalDetails": "\n## 提案详情\n\n"
|
|
72
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { GitAnalyzer } from './git-analyzer.js';
|
|
2
|
+
export { StatsAggregator } from './stats-aggregator.js';
|
|
3
|
+
export { OutputFormatter } from './formatters.js';
|
|
4
|
+
export { loadConfig, normalizeAuthor } from './config.js';
|
|
5
|
+
export { getDefaultTimeRange, parseDateTime, parseBranches } from './time-utils.js';
|
|
6
|
+
export * from './types.js';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
GitAnalyzer: () => import_git_analyzer.GitAnalyzer,
|
|
24
|
+
OutputFormatter: () => import_formatters.OutputFormatter,
|
|
25
|
+
StatsAggregator: () => import_stats_aggregator.StatsAggregator,
|
|
26
|
+
getDefaultTimeRange: () => import_time_utils.getDefaultTimeRange,
|
|
27
|
+
loadConfig: () => import_config.loadConfig,
|
|
28
|
+
normalizeAuthor: () => import_config.normalizeAuthor,
|
|
29
|
+
parseBranches: () => import_time_utils.parseBranches,
|
|
30
|
+
parseDateTime: () => import_time_utils.parseDateTime
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(src_exports);
|
|
33
|
+
var import_git_analyzer = require("./git-analyzer.js");
|
|
34
|
+
var import_stats_aggregator = require("./stats-aggregator.js");
|
|
35
|
+
var import_formatters = require("./formatters.js");
|
|
36
|
+
var import_config = require("./config.js");
|
|
37
|
+
var import_time_utils = require("./time-utils.js");
|
|
38
|
+
__reExport(src_exports, require("./types.js"), module.exports);
|
|
39
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
40
|
+
0 && (module.exports = {
|
|
41
|
+
GitAnalyzer,
|
|
42
|
+
OutputFormatter,
|
|
43
|
+
StatsAggregator,
|
|
44
|
+
getDefaultTimeRange,
|
|
45
|
+
loadConfig,
|
|
46
|
+
normalizeAuthor,
|
|
47
|
+
parseBranches,
|
|
48
|
+
parseDateTime,
|
|
49
|
+
...require("./types.js")
|
|
50
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CommitAnalysis, StatsResult, Config } from './types.js';
|
|
2
|
+
export declare class StatsAggregator {
|
|
3
|
+
private config;
|
|
4
|
+
private activeAuthors?;
|
|
5
|
+
constructor(config: Config, activeAuthors?: Set<string>);
|
|
6
|
+
aggregate(analyses: CommitAnalysis[], since: Date, until: Date, branches: string[], filterAuthor?: string): StatsResult;
|
|
7
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/stats-aggregator.ts
|
|
20
|
+
var stats_aggregator_exports = {};
|
|
21
|
+
__export(stats_aggregator_exports, {
|
|
22
|
+
StatsAggregator: () => StatsAggregator
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(stats_aggregator_exports);
|
|
25
|
+
var import_config = require("./config.js");
|
|
26
|
+
var StatsAggregator = class {
|
|
27
|
+
constructor(config, activeAuthors) {
|
|
28
|
+
this.config = config;
|
|
29
|
+
this.activeAuthors = activeAuthors;
|
|
30
|
+
}
|
|
31
|
+
aggregate(analyses, since, until, branches, filterAuthor) {
|
|
32
|
+
const authorStatsMap = /* @__PURE__ */ new Map();
|
|
33
|
+
for (const analysis of analyses) {
|
|
34
|
+
const normalizedAuthor = (0, import_config.normalizeAuthor)(
|
|
35
|
+
analysis.commit.author,
|
|
36
|
+
this.config.authorMapping
|
|
37
|
+
);
|
|
38
|
+
if (this.activeAuthors && !this.activeAuthors.has(normalizedAuthor)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (filterAuthor && normalizedAuthor !== filterAuthor) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
let stats = authorStatsMap.get(normalizedAuthor);
|
|
45
|
+
if (!stats) {
|
|
46
|
+
stats = {
|
|
47
|
+
author: normalizedAuthor,
|
|
48
|
+
commits: 0,
|
|
49
|
+
openspecProposals: /* @__PURE__ */ new Set(),
|
|
50
|
+
codeFilesChanged: 0,
|
|
51
|
+
additions: 0,
|
|
52
|
+
deletions: 0,
|
|
53
|
+
netChanges: 0,
|
|
54
|
+
branchStats: /* @__PURE__ */ new Map()
|
|
55
|
+
};
|
|
56
|
+
authorStatsMap.set(normalizedAuthor, stats);
|
|
57
|
+
}
|
|
58
|
+
stats.commits++;
|
|
59
|
+
stats.additions += analysis.totalAdditions;
|
|
60
|
+
stats.deletions += analysis.totalDeletions;
|
|
61
|
+
stats.netChanges += analysis.netChanges;
|
|
62
|
+
stats.codeFilesChanged += analysis.codeFiles.length;
|
|
63
|
+
for (const proposal of analysis.openspecProposals) {
|
|
64
|
+
stats.openspecProposals.add(proposal);
|
|
65
|
+
}
|
|
66
|
+
if (!stats.lastCommitDate || analysis.commit.date > stats.lastCommitDate) {
|
|
67
|
+
stats.lastCommitDate = analysis.commit.date;
|
|
68
|
+
}
|
|
69
|
+
if (!stats.firstCommitDate || analysis.commit.date < stats.firstCommitDate) {
|
|
70
|
+
stats.firstCommitDate = analysis.commit.date;
|
|
71
|
+
}
|
|
72
|
+
if (analysis.commit.branches && stats.branchStats) {
|
|
73
|
+
for (const branch of analysis.commit.branches) {
|
|
74
|
+
let branchStat = stats.branchStats.get(branch);
|
|
75
|
+
if (!branchStat) {
|
|
76
|
+
branchStat = {
|
|
77
|
+
branch,
|
|
78
|
+
commits: 0,
|
|
79
|
+
openspecProposals: /* @__PURE__ */ new Set(),
|
|
80
|
+
codeFilesChanged: 0,
|
|
81
|
+
additions: 0,
|
|
82
|
+
deletions: 0,
|
|
83
|
+
netChanges: 0
|
|
84
|
+
};
|
|
85
|
+
stats.branchStats.set(branch, branchStat);
|
|
86
|
+
}
|
|
87
|
+
branchStat.commits++;
|
|
88
|
+
branchStat.additions += analysis.totalAdditions;
|
|
89
|
+
branchStat.deletions += analysis.totalDeletions;
|
|
90
|
+
branchStat.netChanges += analysis.netChanges;
|
|
91
|
+
branchStat.codeFilesChanged += analysis.codeFiles.length;
|
|
92
|
+
for (const proposal of analysis.openspecProposals) {
|
|
93
|
+
branchStat.openspecProposals.add(proposal);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
for (const stats of authorStatsMap.values()) {
|
|
99
|
+
if (stats.firstCommitDate && stats.lastCommitDate) {
|
|
100
|
+
const days = Math.ceil(
|
|
101
|
+
(stats.lastCommitDate.getTime() - stats.firstCommitDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
102
|
+
);
|
|
103
|
+
stats.statisticsPeriod = days === 0 ? "1 day" : `${days + 1} days`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
timeRange: { since, until },
|
|
108
|
+
branches,
|
|
109
|
+
authors: authorStatsMap,
|
|
110
|
+
totalCommits: analyses.length
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
115
|
+
0 && (module.exports = {
|
|
116
|
+
StatsAggregator
|
|
117
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/time-utils.ts
|
|
20
|
+
var time_utils_exports = {};
|
|
21
|
+
__export(time_utils_exports, {
|
|
22
|
+
getDefaultTimeRange: () => getDefaultTimeRange,
|
|
23
|
+
parseBranches: () => parseBranches,
|
|
24
|
+
parseDateTime: () => parseDateTime
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(time_utils_exports);
|
|
27
|
+
function getDefaultTimeRange(sinceHours = -30, untilHours = 20) {
|
|
28
|
+
const now = /* @__PURE__ */ new Date();
|
|
29
|
+
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
30
|
+
const yesterday = new Date(today);
|
|
31
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
32
|
+
const since = new Date(yesterday);
|
|
33
|
+
since.setHours(untilHours, 0, 0, 0);
|
|
34
|
+
const until = new Date(today);
|
|
35
|
+
until.setHours(untilHours, 0, 0, 0);
|
|
36
|
+
return { since, until };
|
|
37
|
+
}
|
|
38
|
+
function parseDateTime(dateStr) {
|
|
39
|
+
const date = new Date(dateStr);
|
|
40
|
+
if (isNaN(date.getTime())) {
|
|
41
|
+
throw new Error(`Invalid date format: ${dateStr}`);
|
|
42
|
+
}
|
|
43
|
+
return date;
|
|
44
|
+
}
|
|
45
|
+
function parseBranches(branchesStr) {
|
|
46
|
+
if (!branchesStr)
|
|
47
|
+
return [];
|
|
48
|
+
return branchesStr.split(",").map((b) => b.trim()).filter((b) => b);
|
|
49
|
+
}
|
|
50
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
51
|
+
0 && (module.exports = {
|
|
52
|
+
getDefaultTimeRange,
|
|
53
|
+
parseBranches,
|
|
54
|
+
parseDateTime
|
|
55
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export interface Config {
|
|
2
|
+
defaultBranches?: string[];
|
|
3
|
+
defaultSinceHours?: number;
|
|
4
|
+
defaultUntilHours?: number;
|
|
5
|
+
authorMapping?: Record<string, string>;
|
|
6
|
+
openspecDir?: string;
|
|
7
|
+
codeFileExtensions?: string[];
|
|
8
|
+
excludeExtensions?: string[];
|
|
9
|
+
activeUserWeeks?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface CliOptions {
|
|
12
|
+
repo: string;
|
|
13
|
+
branches?: string;
|
|
14
|
+
since?: string;
|
|
15
|
+
until?: string;
|
|
16
|
+
author?: string;
|
|
17
|
+
json?: boolean;
|
|
18
|
+
csv?: boolean;
|
|
19
|
+
markdown?: boolean;
|
|
20
|
+
config?: string;
|
|
21
|
+
verbose?: boolean;
|
|
22
|
+
interactive?: boolean;
|
|
23
|
+
lang?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface CommitInfo {
|
|
26
|
+
hash: string;
|
|
27
|
+
author: string;
|
|
28
|
+
email: string;
|
|
29
|
+
date: Date;
|
|
30
|
+
message: string;
|
|
31
|
+
branches?: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface FileChange {
|
|
34
|
+
path: string;
|
|
35
|
+
additions: number;
|
|
36
|
+
deletions: number;
|
|
37
|
+
status: string;
|
|
38
|
+
}
|
|
39
|
+
export interface CommitAnalysis {
|
|
40
|
+
commit: CommitInfo;
|
|
41
|
+
openspecProposals: Set<string>;
|
|
42
|
+
codeFiles: FileChange[];
|
|
43
|
+
totalAdditions: number;
|
|
44
|
+
totalDeletions: number;
|
|
45
|
+
netChanges: number;
|
|
46
|
+
}
|
|
47
|
+
export interface AuthorStats {
|
|
48
|
+
author: string;
|
|
49
|
+
commits: number;
|
|
50
|
+
openspecProposals: Set<string>;
|
|
51
|
+
codeFilesChanged: number;
|
|
52
|
+
additions: number;
|
|
53
|
+
deletions: number;
|
|
54
|
+
netChanges: number;
|
|
55
|
+
lastCommitDate?: Date;
|
|
56
|
+
firstCommitDate?: Date;
|
|
57
|
+
statisticsPeriod?: string;
|
|
58
|
+
branchStats?: Map<string, BranchStats>;
|
|
59
|
+
}
|
|
60
|
+
export interface BranchStats {
|
|
61
|
+
branch: string;
|
|
62
|
+
commits: number;
|
|
63
|
+
openspecProposals: Set<string>;
|
|
64
|
+
codeFilesChanged: number;
|
|
65
|
+
additions: number;
|
|
66
|
+
deletions: number;
|
|
67
|
+
netChanges: number;
|
|
68
|
+
}
|
|
69
|
+
export interface StatsResult {
|
|
70
|
+
timeRange: {
|
|
71
|
+
since: Date;
|
|
72
|
+
until: Date;
|
|
73
|
+
};
|
|
74
|
+
branches: string[];
|
|
75
|
+
authors: Map<string, AuthorStats>;
|
|
76
|
+
totalCommits: number;
|
|
77
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __copyProps = (to, from, except, desc) => {
|
|
6
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
7
|
+
for (let key of __getOwnPropNames(from))
|
|
8
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
9
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
10
|
+
}
|
|
11
|
+
return to;
|
|
12
|
+
};
|
|
13
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
14
|
+
|
|
15
|
+
// src/types.ts
|
|
16
|
+
var types_exports = {};
|
|
17
|
+
module.exports = __toCommonJS(types_exports);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface BranchInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
lastCommitDate: Date;
|
|
4
|
+
commitCount: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function getActiveBranches(repoPath: string, limit?: number): Promise<BranchInfo[]>;
|
|
7
|
+
export declare function selectBranches(repoPath: string, defaultBranches?: string[]): Promise<string[]>;
|