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.
Files changed (47) hide show
  1. package/README.md +234 -0
  2. package/README.zh-CN.md +234 -0
  3. package/dist/cjs/branch-selector.d.ts +7 -0
  4. package/dist/cjs/branch-selector.js +124 -0
  5. package/dist/cjs/cli.d.ts +2 -0
  6. package/dist/cjs/cli.js +151 -0
  7. package/dist/cjs/config.d.ts +3 -0
  8. package/dist/cjs/config.js +66 -0
  9. package/dist/cjs/formatters.d.ts +7 -0
  10. package/dist/cjs/formatters.js +222 -0
  11. package/dist/cjs/git-analyzer.d.ts +10 -0
  12. package/dist/cjs/git-analyzer.js +170 -0
  13. package/dist/cjs/i18n/index.d.ts +7 -0
  14. package/dist/cjs/i18n/index.js +84 -0
  15. package/dist/cjs/i18n/locales/en.json +72 -0
  16. package/dist/cjs/i18n/locales/zh-CN.json +72 -0
  17. package/dist/cjs/index.d.ts +6 -0
  18. package/dist/cjs/index.js +50 -0
  19. package/dist/cjs/stats-aggregator.d.ts +7 -0
  20. package/dist/cjs/stats-aggregator.js +117 -0
  21. package/dist/cjs/time-utils.d.ts +6 -0
  22. package/dist/cjs/time-utils.js +55 -0
  23. package/dist/cjs/types.d.ts +77 -0
  24. package/dist/cjs/types.js +17 -0
  25. package/dist/esm/branch-selector.d.ts +7 -0
  26. package/dist/esm/branch-selector.js +218 -0
  27. package/dist/esm/cli.d.ts +2 -0
  28. package/dist/esm/cli.js +157 -0
  29. package/dist/esm/config.d.ts +3 -0
  30. package/dist/esm/config.js +78 -0
  31. package/dist/esm/formatters.d.ts +7 -0
  32. package/dist/esm/formatters.js +207 -0
  33. package/dist/esm/git-analyzer.d.ts +10 -0
  34. package/dist/esm/git-analyzer.js +335 -0
  35. package/dist/esm/i18n/index.d.ts +7 -0
  36. package/dist/esm/i18n/index.js +49 -0
  37. package/dist/esm/i18n/locales/en.json +72 -0
  38. package/dist/esm/i18n/locales/zh-CN.json +72 -0
  39. package/dist/esm/index.d.ts +6 -0
  40. package/dist/esm/index.js +6 -0
  41. package/dist/esm/stats-aggregator.d.ts +7 -0
  42. package/dist/esm/stats-aggregator.js +149 -0
  43. package/dist/esm/time-utils.d.ts +6 -0
  44. package/dist/esm/time-utils.js +31 -0
  45. package/dist/esm/types.d.ts +77 -0
  46. package/dist/esm/types.js +1 -0
  47. 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,6 @@
1
+ export declare function getDefaultTimeRange(sinceHours?: number, untilHours?: number): {
2
+ since: Date;
3
+ until: Date;
4
+ };
5
+ export declare function parseDateTime(dateStr: string): Date;
6
+ export declare function parseBranches(branchesStr?: string): string[];
@@ -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
+ }