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,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,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,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,149 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
3
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
4
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
5
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
6
|
+
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
|
7
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
8
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
9
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
10
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
11
|
+
import { normalizeAuthor } from "./config.js";
|
|
12
|
+
export var StatsAggregator = /*#__PURE__*/function () {
|
|
13
|
+
function StatsAggregator(config, activeAuthors) {
|
|
14
|
+
_classCallCheck(this, StatsAggregator);
|
|
15
|
+
_defineProperty(this, "config", void 0);
|
|
16
|
+
_defineProperty(this, "activeAuthors", void 0);
|
|
17
|
+
this.config = config;
|
|
18
|
+
this.activeAuthors = activeAuthors;
|
|
19
|
+
}
|
|
20
|
+
_createClass(StatsAggregator, [{
|
|
21
|
+
key: "aggregate",
|
|
22
|
+
value: function aggregate(analyses, since, until, branches, filterAuthor) {
|
|
23
|
+
var authorStatsMap = new Map();
|
|
24
|
+
var _iterator = _createForOfIteratorHelper(analyses),
|
|
25
|
+
_step;
|
|
26
|
+
try {
|
|
27
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
28
|
+
var analysis = _step.value;
|
|
29
|
+
var normalizedAuthor = normalizeAuthor(analysis.commit.author, this.config.authorMapping);
|
|
30
|
+
if (this.activeAuthors && !this.activeAuthors.has(normalizedAuthor)) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (filterAuthor && normalizedAuthor !== filterAuthor) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
var stats = authorStatsMap.get(normalizedAuthor);
|
|
37
|
+
if (!stats) {
|
|
38
|
+
stats = {
|
|
39
|
+
author: normalizedAuthor,
|
|
40
|
+
commits: 0,
|
|
41
|
+
openspecProposals: new Set(),
|
|
42
|
+
codeFilesChanged: 0,
|
|
43
|
+
additions: 0,
|
|
44
|
+
deletions: 0,
|
|
45
|
+
netChanges: 0,
|
|
46
|
+
branchStats: new Map()
|
|
47
|
+
};
|
|
48
|
+
authorStatsMap.set(normalizedAuthor, stats);
|
|
49
|
+
}
|
|
50
|
+
stats.commits++;
|
|
51
|
+
stats.additions += analysis.totalAdditions;
|
|
52
|
+
stats.deletions += analysis.totalDeletions;
|
|
53
|
+
stats.netChanges += analysis.netChanges;
|
|
54
|
+
stats.codeFilesChanged += analysis.codeFiles.length;
|
|
55
|
+
var _iterator3 = _createForOfIteratorHelper(analysis.openspecProposals),
|
|
56
|
+
_step3;
|
|
57
|
+
try {
|
|
58
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
59
|
+
var _proposal = _step3.value;
|
|
60
|
+
stats.openspecProposals.add(_proposal);
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
_iterator3.e(err);
|
|
64
|
+
} finally {
|
|
65
|
+
_iterator3.f();
|
|
66
|
+
}
|
|
67
|
+
if (!stats.lastCommitDate || analysis.commit.date > stats.lastCommitDate) {
|
|
68
|
+
stats.lastCommitDate = analysis.commit.date;
|
|
69
|
+
}
|
|
70
|
+
if (!stats.firstCommitDate || analysis.commit.date < stats.firstCommitDate) {
|
|
71
|
+
stats.firstCommitDate = analysis.commit.date;
|
|
72
|
+
}
|
|
73
|
+
if (analysis.commit.branches && stats.branchStats) {
|
|
74
|
+
var _iterator4 = _createForOfIteratorHelper(analysis.commit.branches),
|
|
75
|
+
_step4;
|
|
76
|
+
try {
|
|
77
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
78
|
+
var branch = _step4.value;
|
|
79
|
+
var branchStat = stats.branchStats.get(branch);
|
|
80
|
+
if (!branchStat) {
|
|
81
|
+
branchStat = {
|
|
82
|
+
branch: branch,
|
|
83
|
+
commits: 0,
|
|
84
|
+
openspecProposals: new Set(),
|
|
85
|
+
codeFilesChanged: 0,
|
|
86
|
+
additions: 0,
|
|
87
|
+
deletions: 0,
|
|
88
|
+
netChanges: 0
|
|
89
|
+
};
|
|
90
|
+
stats.branchStats.set(branch, branchStat);
|
|
91
|
+
}
|
|
92
|
+
branchStat.commits++;
|
|
93
|
+
branchStat.additions += analysis.totalAdditions;
|
|
94
|
+
branchStat.deletions += analysis.totalDeletions;
|
|
95
|
+
branchStat.netChanges += analysis.netChanges;
|
|
96
|
+
branchStat.codeFilesChanged += analysis.codeFiles.length;
|
|
97
|
+
var _iterator5 = _createForOfIteratorHelper(analysis.openspecProposals),
|
|
98
|
+
_step5;
|
|
99
|
+
try {
|
|
100
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
101
|
+
var proposal = _step5.value;
|
|
102
|
+
branchStat.openspecProposals.add(proposal);
|
|
103
|
+
}
|
|
104
|
+
} catch (err) {
|
|
105
|
+
_iterator5.e(err);
|
|
106
|
+
} finally {
|
|
107
|
+
_iterator5.f();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch (err) {
|
|
111
|
+
_iterator4.e(err);
|
|
112
|
+
} finally {
|
|
113
|
+
_iterator4.f();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
_iterator.e(err);
|
|
119
|
+
} finally {
|
|
120
|
+
_iterator.f();
|
|
121
|
+
}
|
|
122
|
+
var _iterator2 = _createForOfIteratorHelper(authorStatsMap.values()),
|
|
123
|
+
_step2;
|
|
124
|
+
try {
|
|
125
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
126
|
+
var _stats = _step2.value;
|
|
127
|
+
if (_stats.firstCommitDate && _stats.lastCommitDate) {
|
|
128
|
+
var days = Math.ceil((_stats.lastCommitDate.getTime() - _stats.firstCommitDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
129
|
+
_stats.statisticsPeriod = days === 0 ? '1 day' : "".concat(days + 1, " days");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
_iterator2.e(err);
|
|
134
|
+
} finally {
|
|
135
|
+
_iterator2.f();
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
timeRange: {
|
|
139
|
+
since: since,
|
|
140
|
+
until: until
|
|
141
|
+
},
|
|
142
|
+
branches: branches,
|
|
143
|
+
authors: authorStatsMap,
|
|
144
|
+
totalCommits: analyses.length
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}]);
|
|
148
|
+
return StatsAggregator;
|
|
149
|
+
}();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function getDefaultTimeRange() {
|
|
2
|
+
var sinceHours = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : -30;
|
|
3
|
+
var untilHours = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 20;
|
|
4
|
+
var now = new Date();
|
|
5
|
+
var today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
6
|
+
var yesterday = new Date(today);
|
|
7
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
8
|
+
var since = new Date(yesterday);
|
|
9
|
+
since.setHours(untilHours, 0, 0, 0);
|
|
10
|
+
var until = new Date(today);
|
|
11
|
+
until.setHours(untilHours, 0, 0, 0);
|
|
12
|
+
return {
|
|
13
|
+
since: since,
|
|
14
|
+
until: until
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function parseDateTime(dateStr) {
|
|
18
|
+
var date = new Date(dateStr);
|
|
19
|
+
if (isNaN(date.getTime())) {
|
|
20
|
+
throw new Error("Invalid date format: ".concat(dateStr));
|
|
21
|
+
}
|
|
22
|
+
return date;
|
|
23
|
+
}
|
|
24
|
+
export function parseBranches(branchesStr) {
|
|
25
|
+
if (!branchesStr) return [];
|
|
26
|
+
return branchesStr.split(',').map(function (b) {
|
|
27
|
+
return b.trim();
|
|
28
|
+
}).filter(function (b) {
|
|
29
|
+
return b;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -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 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openspec-stat",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Track team members' OpenSpec proposals and code changes in Git repositories",
|
|
5
|
+
"main": "dist/cjs/index.js",
|
|
6
|
+
"types": "dist/cjs/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"openspec-stat": "dist/esm/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"dev": "father dev",
|
|
13
|
+
"build": "father build",
|
|
14
|
+
"build:deps": "father prebundle",
|
|
15
|
+
"prepublishOnly": "father doctor && npm run build",
|
|
16
|
+
"test:cli": "node dist/esm/cli.js --help",
|
|
17
|
+
"link:global": "npm link"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"openspec",
|
|
21
|
+
"git",
|
|
22
|
+
"statistics",
|
|
23
|
+
"cli",
|
|
24
|
+
"analytics"
|
|
25
|
+
],
|
|
26
|
+
"authors": [
|
|
27
|
+
"Orchard <orchardyz@outlook.com>"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/Orchardxyz/openspec-stat.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/Orchardxyz/openspec-stat/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/Orchardxyz/openspec-stat#readme",
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"compiled"
|
|
41
|
+
],
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^25.0.3",
|
|
47
|
+
"father": "^4.6.13"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@inquirer/prompts": "^8.1.0",
|
|
51
|
+
"chalk": "^5.6.2",
|
|
52
|
+
"cli-table3": "^0.6.5",
|
|
53
|
+
"commander": "^14.0.2",
|
|
54
|
+
"date-fns": "^4.1.0",
|
|
55
|
+
"simple-git": "^3.30.0"
|
|
56
|
+
}
|
|
57
|
+
}
|