openspec-stat 1.3.5 → 1.4.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 +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/esm/branch-selector.js +87 -214
- package/dist/esm/cli.js +14 -64
- package/dist/esm/commands/init.js +17 -44
- package/dist/esm/commands/multi.js +123 -180
- package/dist/esm/commands/single.js +143 -152
- package/dist/esm/config.js +22 -64
- package/dist/esm/formatters.js +285 -520
- package/dist/esm/git-analyzer.js +116 -354
- package/dist/esm/i18n/index.js +22 -10
- package/dist/esm/i18n/locales/en.json +43 -43
- package/dist/esm/i18n/locales/zh-CN.json +43 -43
- package/dist/esm/multi/config-validator.d.ts +1 -1
- package/dist/esm/multi/config-validator.js +25 -26
- package/dist/esm/multi/config-wizard.js +217 -459
- package/dist/esm/multi/multi-repo-analyzer.d.ts +11 -1
- package/dist/esm/multi/multi-repo-analyzer.js +228 -426
- package/dist/esm/stats-aggregator.js +115 -193
- package/dist/esm/time-utils.js +11 -17
- package/dist/esm/ui/spinner.d.ts +12 -0
- package/dist/esm/ui/spinner.js +38 -0
- package/package.json +10 -6
- package/dist/cjs/branch-selector.d.ts +0 -7
- package/dist/cjs/branch-selector.js +0 -128
- package/dist/cjs/cli.d.ts +0 -2
- package/dist/cjs/cli.js +0 -19
- package/dist/cjs/commands/init.d.ts +0 -7
- package/dist/cjs/commands/init.js +0 -58
- package/dist/cjs/commands/multi.d.ts +0 -16
- package/dist/cjs/commands/multi.js +0 -172
- package/dist/cjs/commands/single.d.ts +0 -2
- package/dist/cjs/commands/single.js +0 -148
- package/dist/cjs/config.d.ts +0 -3
- package/dist/cjs/config.js +0 -66
- package/dist/cjs/formatters.d.ts +0 -7
- package/dist/cjs/formatters.js +0 -482
- package/dist/cjs/git-analyzer.d.ts +0 -11
- package/dist/cjs/git-analyzer.js +0 -165
- package/dist/cjs/i18n/index.d.ts +0 -7
- package/dist/cjs/i18n/index.js +0 -84
- package/dist/cjs/i18n/locales/en.json +0 -154
- package/dist/cjs/i18n/locales/zh-CN.json +0 -154
- package/dist/cjs/index.d.ts +0 -6
- package/dist/cjs/index.js +0 -50
- package/dist/cjs/multi/config-validator.d.ts +0 -3
- package/dist/cjs/multi/config-validator.js +0 -130
- package/dist/cjs/multi/config-wizard.d.ts +0 -50
- package/dist/cjs/multi/config-wizard.js +0 -331
- package/dist/cjs/multi/multi-repo-analyzer.d.ts +0 -14
- package/dist/cjs/multi/multi-repo-analyzer.js +0 -210
- package/dist/cjs/stats-aggregator.d.ts +0 -7
- package/dist/cjs/stats-aggregator.js +0 -155
- package/dist/cjs/time-utils.d.ts +0 -6
- package/dist/cjs/time-utils.js +0 -55
- package/dist/cjs/types.d.ts +0 -136
- package/dist/cjs/types.js +0 -17
package/dist/cjs/i18n/index.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,154 +0,0 @@
|
|
|
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
|
-
"loading.fetching": "🔄 Fetching remote branches...",
|
|
20
|
-
|
|
21
|
-
"info.timeRange": "📅 Time Range: {{since}} ~ {{until}}",
|
|
22
|
-
"info.branches": "🌿 Branches: {{branches}}",
|
|
23
|
-
"info.allBranches": "All branches",
|
|
24
|
-
"info.activeUsers": " Active users (within {{weeks}} weeks): {{users}}",
|
|
25
|
-
"info.foundCommits": "📝 Found {{count}} commits, analyzing...",
|
|
26
|
-
"info.analysisProgress": " Analysis progress: {{current}}/{{total}}",
|
|
27
|
-
"info.qualifyingCommits": "✅ Found {{count}} qualifying commits (containing OpenSpec proposals and code changes)",
|
|
28
|
-
|
|
29
|
-
"warning.noCommits": "⚠️ No commits found matching the criteria",
|
|
30
|
-
"warning.noQualifyingCommits": "⚠️ No commits found containing both OpenSpec proposals and code changes",
|
|
31
|
-
"warning.noBranches": "⚠️ No remote branches found",
|
|
32
|
-
"warning.noBranchesSelected": "⚠️ No branches selected. Please select at least one branch to analyze.",
|
|
33
|
-
|
|
34
|
-
"error.prefix": "❌ Error:",
|
|
35
|
-
|
|
36
|
-
"branch.fetching": "\n🔍 Fetching active branches...",
|
|
37
|
-
"branch.selectMode": "How would you like to select branches?",
|
|
38
|
-
"branch.mode.select": "Select from active branches",
|
|
39
|
-
"branch.mode.default": "Use default branches from config",
|
|
40
|
-
"branch.mode.custom": "Custom input",
|
|
41
|
-
"branch.selectPrompt": "Select branches to analyze:",
|
|
42
|
-
"branch.customInput": "Enter branch names (comma-separated):",
|
|
43
|
-
"branch.additionalInput": "Enter additional branch names (comma-separated):",
|
|
44
|
-
"branch.customSeparator": "--- Custom input ---",
|
|
45
|
-
"branch.selected": "\n✓ Selected branches:",
|
|
46
|
-
"branch.lastCommit": "last commit: {{date}}",
|
|
47
|
-
|
|
48
|
-
"output.title": "\n📊 OpenSpec Statistics Report\n",
|
|
49
|
-
"output.timeRange": "Time Range: {{since}} ~ {{until}}\n",
|
|
50
|
-
"output.branches": "Branches: {{branches}}\n",
|
|
51
|
-
"output.totalCommits": "Total Commits: {{count}}\n\n",
|
|
52
|
-
"output.proposals": " Proposals: {{proposals}}\n",
|
|
53
|
-
"output.proposalSummary": "📋 Proposal Summary (by proposal)",
|
|
54
|
-
"output.proposalTotal": " 📊 Total: {{count}} proposals | {{commits}} commits | {{files}} files | +{{additions}}/-{{deletions}} lines (net: {{netChanges}})\n",
|
|
55
|
-
"output.proposalTotalLabel": "Proposal Summary Total",
|
|
56
|
-
"output.multiProposalWarning": "Note: Some proposals contain commits shared with other proposals. Code changes have been evenly distributed among proposals.",
|
|
57
|
-
"output.sharedWithOthers": "shared with other proposals",
|
|
58
|
-
"output.authorSummary": "👥 Author Summary (by contributor)",
|
|
59
|
-
"output.contributorHint": "💡 Use --show-contributors to see detailed statistics for each contributor",
|
|
60
|
-
|
|
61
|
-
"table.branch": "Branch",
|
|
62
|
-
"table.period": "Period",
|
|
63
|
-
"table.commits": "Commits",
|
|
64
|
-
"table.proposals": "Proposals",
|
|
65
|
-
"table.proposal": "Proposal",
|
|
66
|
-
"table.contributors": "Contributors",
|
|
67
|
-
"table.codeFiles": "Code Files",
|
|
68
|
-
"table.additions": "Additions",
|
|
69
|
-
"table.deletions": "Deletions",
|
|
70
|
-
"table.netChanges": "Net Changes",
|
|
71
|
-
"table.author": "Author",
|
|
72
|
-
"table.lastCommitDate": "Last Commit Date",
|
|
73
|
-
"table.proposalsList": "Proposals List",
|
|
74
|
-
"table.proposalsCount": "Proposals Count",
|
|
75
|
-
"table.totalDeduplicated": "TOTAL (Deduplicated)",
|
|
76
|
-
|
|
77
|
-
"markdown.title": "# OpenSpec Statistics Report\n\n",
|
|
78
|
-
"markdown.timeRange": "**Time Range**: {{since}} ~ {{until}}\n\n",
|
|
79
|
-
"markdown.branches": "**Branches**: {{branches}}\n\n",
|
|
80
|
-
"markdown.totalCommits": "**Total Commits**: {{count}}\n\n",
|
|
81
|
-
"markdown.statistics": "## Statistics\n\n",
|
|
82
|
-
"markdown.proposalDetails": "\n## Proposal Details\n\n",
|
|
83
|
-
|
|
84
|
-
"multi.beta.warning": "⚠️ BETA: Multi-repository mode is experimental",
|
|
85
|
-
"multi.beta.feedback": " Please report issues at: https://github.com/Orchardxyz/openspec-stat/issues",
|
|
86
|
-
"multi.loading.config": "🔍 Loading multi-repository configuration...",
|
|
87
|
-
"multi.repo.cloning": "☁️ Cloning {{repo}}...",
|
|
88
|
-
"multi.repo.cloned": "✅ Successfully cloned {{repo}}",
|
|
89
|
-
"multi.repo.fetching": "🔄 Fetching remote branches for {{repo}}...",
|
|
90
|
-
"multi.repo.analyzing": "📊 Analyzing {{repo}} ({{type}})...",
|
|
91
|
-
"multi.repo.completed": "✅ Completed {{repo}}: {{commits}} commits",
|
|
92
|
-
"multi.repo.failed": "❌ Failed {{repo}}: {{error}}",
|
|
93
|
-
"multi.repo.skipped": "⏭️ Skipped {{repo}}: disabled",
|
|
94
|
-
"multi.cleanup.start": "🧹 Cleaning up temporary directories...",
|
|
95
|
-
"multi.cleanup.done": "✅ Cleanup completed",
|
|
96
|
-
"multi.summary.title": "\n📦 Multi-Repository Summary\n",
|
|
97
|
-
"multi.summary.repos": "Repositories: {{total}} ({{success}} succeeded, {{failed}} failed)",
|
|
98
|
-
"multi.progress.batch": "Processing batch {{current}}/{{total}}...",
|
|
99
|
-
"multi.table.repository": "Repository",
|
|
100
|
-
"multi.table.type": "Type",
|
|
101
|
-
|
|
102
|
-
"init.welcome": "\n📋 OpenSpec Configuration Wizard\n",
|
|
103
|
-
"init.welcomeMulti": "\n📋 OpenSpec Multi-Repository Configuration Wizard (BETA)\n",
|
|
104
|
-
"init.configName": "Configuration file name:",
|
|
105
|
-
"init.addRepository": "\n📦 Repository {{number}}",
|
|
106
|
-
"init.repoType": "Repository type:",
|
|
107
|
-
"init.repoType.local": "📁 Local - I have this repository on my machine",
|
|
108
|
-
"init.repoType.remote": "☁️ Remote - Clone from remote URL",
|
|
109
|
-
"init.repoName": "Repository name (for display):",
|
|
110
|
-
"init.repoPath": "Local path (absolute or relative):",
|
|
111
|
-
"init.repoUrl": "Git URL (e.g., git@github.com:org/repo.git):",
|
|
112
|
-
"init.repoUrlInvalid": "Invalid Git URL format",
|
|
113
|
-
"init.useFullClone": "Use full clone (recommended for accuracy)?",
|
|
114
|
-
"init.cloneDepth": "Clone depth (number of commits):",
|
|
115
|
-
"init.branches": "Branches to analyze (comma-separated):",
|
|
116
|
-
"init.addMore": "Add another repository?",
|
|
117
|
-
"init.timeConfig": "\n⏰ Time Range Configuration",
|
|
118
|
-
"init.useDefaultTime": "Use default time range (yesterday 20:00 - today 20:00)?",
|
|
119
|
-
"init.sinceHours": "Start time offset (hours, negative for past):",
|
|
120
|
-
"init.untilHours": "End time (hour of day, 0-23):",
|
|
121
|
-
"init.advanced": "\n⚙️ Advanced Options",
|
|
122
|
-
"init.configureAdvanced": "Configure advanced options?",
|
|
123
|
-
"init.openspecDir": "OpenSpec directory path:",
|
|
124
|
-
"init.maxConcurrent": "Maximum concurrent repository operations:",
|
|
125
|
-
"init.authorMapping": "Configure author name mapping?",
|
|
126
|
-
"init.authorMappingInfo": "\nAuthor mapping helps unify multiple Git identities for the same person.\n",
|
|
127
|
-
"init.gitIdentity": "Git identity (name or email):",
|
|
128
|
-
"init.unifiedName": "Unified name for \"{{identity}}\":",
|
|
129
|
-
"init.addMoreMapping": "Add another mapping?",
|
|
130
|
-
"init.preview": "\n✅ Configuration Preview:\n",
|
|
131
|
-
"init.save": "\nSave this configuration?",
|
|
132
|
-
"init.saved": "\n✅ Configuration saved to {{path}}",
|
|
133
|
-
"init.runCommand": "\nRun: openspec-stat multi -c {{path}}",
|
|
134
|
-
"init.templateCreated": "✅ Template created at {{path}}",
|
|
135
|
-
"init.templateEdit": "Please edit the file and configure your repositories.",
|
|
136
|
-
|
|
137
|
-
"config.validation.noRepos": "Invalid config: \"repositories\" must be an array",
|
|
138
|
-
"config.validation.emptyRepos": "Invalid config: at least one repository is required",
|
|
139
|
-
"config.validation.noName": "Repository {{index}}: \"name\" is required",
|
|
140
|
-
"config.validation.invalidType": "Repository \"{{name}}\": \"type\" must be \"local\" or \"remote\"",
|
|
141
|
-
"config.validation.noPath": "Repository \"{{name}}\": \"path\" is required for local type",
|
|
142
|
-
"config.validation.noUrl": "Repository \"{{name}}\": \"url\" is required for remote type",
|
|
143
|
-
"config.validation.noBranches": "Repository \"{{name}}\": at least one branch is required",
|
|
144
|
-
"config.summary.title": "\n📋 Configuration Summary\n",
|
|
145
|
-
"config.summary.repositories": "Repositories:",
|
|
146
|
-
"config.summary.timeRange": "\nTime Range:",
|
|
147
|
-
"config.summary.since": " Since: {{hours}} hours",
|
|
148
|
-
"config.summary.until": " Until: {{hours}}:00",
|
|
149
|
-
"config.summary.parallelism": "\nParallelism:",
|
|
150
|
-
"config.summary.maxConcurrent": " Max concurrent: {{count}}",
|
|
151
|
-
"config.summary.remoteCache": "\nRemote Cache:",
|
|
152
|
-
"config.summary.cacheDir": " Directory: {{dir}}",
|
|
153
|
-
"config.summary.autoCleanup": " Auto cleanup: {{enabled}}"
|
|
154
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
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
|
-
"loading.fetching": "🔄 正在拉取远程分支...",
|
|
20
|
-
|
|
21
|
-
"info.timeRange": "📅 时间范围:{{since}} ~ {{until}}",
|
|
22
|
-
"info.branches": "🌿 分支:{{branches}}",
|
|
23
|
-
"info.allBranches": "所有分支",
|
|
24
|
-
"info.activeUsers": " 活跃用户({{weeks}} 周内):{{users}}",
|
|
25
|
-
"info.foundCommits": "📝 找到 {{count}} 个提交,正在分析...",
|
|
26
|
-
"info.analysisProgress": " 分析进度:{{current}}/{{total}}",
|
|
27
|
-
"info.qualifyingCommits": "✅ 找到 {{count}} 个符合条件的提交(包含 OpenSpec 提案和代码变更)",
|
|
28
|
-
|
|
29
|
-
"warning.noCommits": "⚠️ 未找到符合条件的提交",
|
|
30
|
-
"warning.noQualifyingCommits": "⚠️ 未找到同时包含 OpenSpec 提案和代码变更的提交",
|
|
31
|
-
"warning.noBranches": "⚠️ 未找到远程分支",
|
|
32
|
-
"warning.noBranchesSelected": "⚠️ 未选择任何分支。请至少选择一个分支进行分析。",
|
|
33
|
-
|
|
34
|
-
"error.prefix": "❌ 错误:",
|
|
35
|
-
|
|
36
|
-
"branch.fetching": "\n🔍 正在获取活跃分支...",
|
|
37
|
-
"branch.selectMode": "您想如何选择分支?",
|
|
38
|
-
"branch.mode.select": "从活跃分支中选择",
|
|
39
|
-
"branch.mode.default": "使用配置文件中的默认分支",
|
|
40
|
-
"branch.mode.custom": "自定义输入",
|
|
41
|
-
"branch.selectPrompt": "选择要分析的分支:",
|
|
42
|
-
"branch.customInput": "输入分支名称(逗号分隔):",
|
|
43
|
-
"branch.additionalInput": "输入额外的分支名称(逗号分隔):",
|
|
44
|
-
"branch.customSeparator": "--- 自定义输入 ---",
|
|
45
|
-
"branch.selected": "\n✓ 已选择的分支:",
|
|
46
|
-
"branch.lastCommit": "最后提交:{{date}}",
|
|
47
|
-
|
|
48
|
-
"output.title": "\n📊 OpenSpec 统计报告\n",
|
|
49
|
-
"output.timeRange": "时间范围:{{since}} ~ {{until}}\n",
|
|
50
|
-
"output.branches": "分支:{{branches}}\n",
|
|
51
|
-
"output.totalCommits": "总提交数:{{count}}\n\n",
|
|
52
|
-
"output.proposals": " 提案:{{proposals}}\n",
|
|
53
|
-
"output.proposalSummary": "📋 提案汇总(按提案统计)",
|
|
54
|
-
"output.proposalTotal": " 📊 总计:{{count}} 个提案 | {{commits}} 次提交 | {{files}} 个文件 | +{{additions}}/-{{deletions}} 行(净变更:{{netChanges}})\n",
|
|
55
|
-
"output.proposalTotalLabel": "提案汇总总计",
|
|
56
|
-
"output.multiProposalWarning": "注意:部分提案包含多提案共享的 commit,代码变更已按提案数量平均分配",
|
|
57
|
-
"output.sharedWithOthers": "与其他提案共享",
|
|
58
|
-
"output.authorSummary": "👥 作者汇总(按贡献者统计)",
|
|
59
|
-
"output.contributorHint": "💡 使用 --show-contributors 选项可查看每个贡献者的详细统计信息",
|
|
60
|
-
|
|
61
|
-
"table.branch": "分支",
|
|
62
|
-
"table.period": "周期",
|
|
63
|
-
"table.commits": "提交数",
|
|
64
|
-
"table.proposals": "提案数",
|
|
65
|
-
"table.proposal": "提案",
|
|
66
|
-
"table.contributors": "贡献者",
|
|
67
|
-
"table.codeFiles": "代码文件",
|
|
68
|
-
"table.additions": "新增行数",
|
|
69
|
-
"table.deletions": "删除行数",
|
|
70
|
-
"table.netChanges": "净变更",
|
|
71
|
-
"table.author": "作者",
|
|
72
|
-
"table.lastCommitDate": "最后提交日期",
|
|
73
|
-
"table.proposalsList": "提案列表",
|
|
74
|
-
"table.proposalsCount": "提案数量",
|
|
75
|
-
"table.totalDeduplicated": "总计(去重)",
|
|
76
|
-
|
|
77
|
-
"markdown.title": "# OpenSpec 统计报告\n\n",
|
|
78
|
-
"markdown.timeRange": "**时间范围**:{{since}} ~ {{until}}\n\n",
|
|
79
|
-
"markdown.branches": "**分支**:{{branches}}\n\n",
|
|
80
|
-
"markdown.totalCommits": "**总提交数**:{{count}}\n\n",
|
|
81
|
-
"markdown.statistics": "## 统计数据\n\n",
|
|
82
|
-
"markdown.proposalDetails": "\n## 提案详情\n\n",
|
|
83
|
-
|
|
84
|
-
"multi.beta.warning": "⚠️ 测试版:多仓库模式为实验性功能",
|
|
85
|
-
"multi.beta.feedback": " 请反馈问题至:https://github.com/Orchardxyz/openspec-stat/issues",
|
|
86
|
-
"multi.loading.config": "🔍 正在加载多仓库配置...",
|
|
87
|
-
"multi.repo.cloning": "☁️ 正在克隆 {{repo}}...",
|
|
88
|
-
"multi.repo.cloned": "✅ 成功克隆 {{repo}}",
|
|
89
|
-
"multi.repo.fetching": "🔄 正在拉取 {{repo}} 的远程分支...",
|
|
90
|
-
"multi.repo.analyzing": "📊 正在分析 {{repo}} ({{type}})...",
|
|
91
|
-
"multi.repo.completed": "✅ 完成 {{repo}}:{{commits}} 次提交",
|
|
92
|
-
"multi.repo.failed": "❌ 失败 {{repo}}:{{error}}",
|
|
93
|
-
"multi.repo.skipped": "⏭️ 跳过 {{repo}}:已禁用",
|
|
94
|
-
"multi.cleanup.start": "🧹 正在清理临时目录...",
|
|
95
|
-
"multi.cleanup.done": "✅ 清理完成",
|
|
96
|
-
"multi.summary.title": "\n📦 多仓库汇总\n",
|
|
97
|
-
"multi.summary.repos": "仓库:{{total}} 个({{success}} 成功,{{failed}} 失败)",
|
|
98
|
-
"multi.progress.batch": "正在处理批次 {{current}}/{{total}}...",
|
|
99
|
-
"multi.table.repository": "仓库",
|
|
100
|
-
"multi.table.type": "类型",
|
|
101
|
-
|
|
102
|
-
"init.welcome": "\n📋 OpenSpec 配置向导\n",
|
|
103
|
-
"init.welcomeMulti": "\n📋 OpenSpec 多仓库配置向导(测试版)\n",
|
|
104
|
-
"init.configName": "配置文件名:",
|
|
105
|
-
"init.addRepository": "\n📦 仓库 {{number}}",
|
|
106
|
-
"init.repoType": "仓库类型:",
|
|
107
|
-
"init.repoType.local": "📁 本地 - 我的机器上已有此仓库",
|
|
108
|
-
"init.repoType.remote": "☁️ 远程 - 从远程 URL 克隆",
|
|
109
|
-
"init.repoName": "仓库名称(用于显示):",
|
|
110
|
-
"init.repoPath": "本地路径(绝对或相对路径):",
|
|
111
|
-
"init.repoUrl": "Git URL(例如:git@github.com:org/repo.git):",
|
|
112
|
-
"init.repoUrlInvalid": "无效的 Git URL 格式",
|
|
113
|
-
"init.useFullClone": "使用完整克隆(推荐以确保准确性)?",
|
|
114
|
-
"init.cloneDepth": "克隆深度(提交数量):",
|
|
115
|
-
"init.branches": "要分析的分支(逗号分隔):",
|
|
116
|
-
"init.addMore": "添加另一个仓库?",
|
|
117
|
-
"init.timeConfig": "\n⏰ 时间范围配置",
|
|
118
|
-
"init.useDefaultTime": "使用默认时间范围(昨天 20:00 - 今天 20:00)?",
|
|
119
|
-
"init.sinceHours": "开始时间偏移(小时,负数表示过去):",
|
|
120
|
-
"init.untilHours": "结束时间(一天中的小时,0-23):",
|
|
121
|
-
"init.advanced": "\n⚙️ 高级选项",
|
|
122
|
-
"init.configureAdvanced": "配置高级选项?",
|
|
123
|
-
"init.openspecDir": "OpenSpec 目录路径:",
|
|
124
|
-
"init.maxConcurrent": "最大并发仓库操作数:",
|
|
125
|
-
"init.authorMapping": "配置作者名称映射?",
|
|
126
|
-
"init.authorMappingInfo": "\n作者映射帮助统一同一人的多个 Git 身份。\n",
|
|
127
|
-
"init.gitIdentity": "Git 身份(名称或邮箱):",
|
|
128
|
-
"init.unifiedName": "\"{{identity}}\" 的统一名称:",
|
|
129
|
-
"init.addMoreMapping": "添加另一个映射?",
|
|
130
|
-
"init.preview": "\n✅ 配置预览:\n",
|
|
131
|
-
"init.save": "\n保存此配置?",
|
|
132
|
-
"init.saved": "\n✅ 配置已保存至 {{path}}",
|
|
133
|
-
"init.runCommand": "\n运行:openspec-stat multi -c {{path}}",
|
|
134
|
-
"init.templateCreated": "✅ 模板已创建于 {{path}}",
|
|
135
|
-
"init.templateEdit": "请编辑文件并配置您的仓库。",
|
|
136
|
-
|
|
137
|
-
"config.validation.noRepos": "无效配置:\"repositories\" 必须是数组",
|
|
138
|
-
"config.validation.emptyRepos": "无效配置:至少需要一个仓库",
|
|
139
|
-
"config.validation.noName": "仓库 {{index}}:需要 \"name\"",
|
|
140
|
-
"config.validation.invalidType": "仓库 \"{{name}}\":\"type\" 必须是 \"local\" 或 \"remote\"",
|
|
141
|
-
"config.validation.noPath": "仓库 \"{{name}}\":本地类型需要 \"path\"",
|
|
142
|
-
"config.validation.noUrl": "仓库 \"{{name}}\":远程类型需要 \"url\"",
|
|
143
|
-
"config.validation.noBranches": "仓库 \"{{name}}\":至少需要一个分支",
|
|
144
|
-
"config.summary.title": "\n📋 配置摘要\n",
|
|
145
|
-
"config.summary.repositories": "仓库:",
|
|
146
|
-
"config.summary.timeRange": "\n时间范围:",
|
|
147
|
-
"config.summary.since": " 开始:{{hours}} 小时",
|
|
148
|
-
"config.summary.until": " 结束:{{hours}}:00",
|
|
149
|
-
"config.summary.parallelism": "\n并发:",
|
|
150
|
-
"config.summary.maxConcurrent": " 最大并发数:{{count}}",
|
|
151
|
-
"config.summary.remoteCache": "\n远程缓存:",
|
|
152
|
-
"config.summary.cacheDir": " 目录:{{dir}}",
|
|
153
|
-
"config.summary.autoCleanup": " 自动清理:{{enabled}}"
|
|
154
|
-
}
|
package/dist/cjs/index.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
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';
|
package/dist/cjs/index.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
11
|
-
var __copyProps = (to, from, except, desc) => {
|
|
12
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
-
for (let key of __getOwnPropNames(from))
|
|
14
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
-
mod
|
|
26
|
-
));
|
|
27
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
-
|
|
29
|
-
// src/multi/config-validator.ts
|
|
30
|
-
var config_validator_exports = {};
|
|
31
|
-
__export(config_validator_exports, {
|
|
32
|
-
printConfigSummary: () => printConfigSummary,
|
|
33
|
-
validateAndFillDefaults: () => validateAndFillDefaults
|
|
34
|
-
});
|
|
35
|
-
module.exports = __toCommonJS(config_validator_exports);
|
|
36
|
-
var import_chalk = __toESM(require("chalk"));
|
|
37
|
-
var import_i18n = require("../i18n/index.js");
|
|
38
|
-
var DEFAULT_MULTI_REPO_CONFIG = {
|
|
39
|
-
mode: "multi-repo",
|
|
40
|
-
defaultSinceHours: -30,
|
|
41
|
-
defaultUntilHours: 20,
|
|
42
|
-
openspecDir: "openspec/",
|
|
43
|
-
excludeExtensions: [".md", ".txt", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp"],
|
|
44
|
-
activeUserWeeks: 2,
|
|
45
|
-
authorMapping: {},
|
|
46
|
-
parallelism: {
|
|
47
|
-
maxConcurrent: 3,
|
|
48
|
-
timeout: 6e5
|
|
49
|
-
},
|
|
50
|
-
remoteCache: {
|
|
51
|
-
dir: "/tmp/openspec-stat-cache",
|
|
52
|
-
autoCleanup: true,
|
|
53
|
-
cleanupOnComplete: true,
|
|
54
|
-
cleanupOnError: true
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
function validateAndFillDefaults(config) {
|
|
58
|
-
if (!config.repositories || !Array.isArray(config.repositories)) {
|
|
59
|
-
throw new Error((0, import_i18n.t)("config.validation.noRepos"));
|
|
60
|
-
}
|
|
61
|
-
if (config.repositories.length === 0) {
|
|
62
|
-
throw new Error((0, import_i18n.t)("config.validation.emptyRepos"));
|
|
63
|
-
}
|
|
64
|
-
config.repositories.forEach((repo, index) => {
|
|
65
|
-
if (!repo.name) {
|
|
66
|
-
throw new Error((0, import_i18n.t)("config.validation.noName", { index: String(index + 1) }));
|
|
67
|
-
}
|
|
68
|
-
if (!repo.type || !["local", "remote"].includes(repo.type)) {
|
|
69
|
-
throw new Error((0, import_i18n.t)("config.validation.invalidType", { name: repo.name }));
|
|
70
|
-
}
|
|
71
|
-
if (repo.type === "local" && !repo.path) {
|
|
72
|
-
throw new Error((0, import_i18n.t)("config.validation.noPath", { name: repo.name }));
|
|
73
|
-
}
|
|
74
|
-
if (repo.type === "remote" && !repo.url) {
|
|
75
|
-
throw new Error((0, import_i18n.t)("config.validation.noUrl", { name: repo.name }));
|
|
76
|
-
}
|
|
77
|
-
if (!repo.branches || !Array.isArray(repo.branches) || repo.branches.length === 0) {
|
|
78
|
-
throw new Error((0, import_i18n.t)("config.validation.noBranches", { name: repo.name }));
|
|
79
|
-
}
|
|
80
|
-
if (repo.enabled === void 0) {
|
|
81
|
-
repo.enabled = true;
|
|
82
|
-
}
|
|
83
|
-
if (repo.type === "remote" && !repo.cloneOptions) {
|
|
84
|
-
repo.cloneOptions = { depth: null, singleBranch: false };
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
const mergedConfig = {
|
|
88
|
-
...DEFAULT_MULTI_REPO_CONFIG,
|
|
89
|
-
...config,
|
|
90
|
-
parallelism: {
|
|
91
|
-
...DEFAULT_MULTI_REPO_CONFIG.parallelism,
|
|
92
|
-
...config.parallelism
|
|
93
|
-
},
|
|
94
|
-
remoteCache: {
|
|
95
|
-
...DEFAULT_MULTI_REPO_CONFIG.remoteCache,
|
|
96
|
-
...config.remoteCache
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
return mergedConfig;
|
|
100
|
-
}
|
|
101
|
-
function printConfigSummary(config) {
|
|
102
|
-
var _a, _b, _c, _d;
|
|
103
|
-
console.log(import_chalk.default.blue((0, import_i18n.t)("config.summary.title")));
|
|
104
|
-
console.log(import_chalk.default.cyan((0, import_i18n.t)("config.summary.repositories")));
|
|
105
|
-
(_a = config.repositories) == null ? void 0 : _a.forEach((repo, i) => {
|
|
106
|
-
const icon = repo.type === "local" ? "📁" : "☁️";
|
|
107
|
-
const location = repo.type === "local" ? repo.path : repo.url;
|
|
108
|
-
console.log(` ${i + 1}. ${icon} ${import_chalk.default.bold(repo.name)} (${repo.type})`);
|
|
109
|
-
console.log(` ${import_chalk.default.gray(location)}`);
|
|
110
|
-
console.log(` ${import_chalk.default.gray("Branches:")} ${repo.branches.join(", ")}`);
|
|
111
|
-
});
|
|
112
|
-
console.log(import_chalk.default.cyan((0, import_i18n.t)("config.summary.timeRange")));
|
|
113
|
-
console.log((0, import_i18n.t)("config.summary.since", { hours: String(config.defaultSinceHours) }));
|
|
114
|
-
console.log((0, import_i18n.t)("config.summary.until", { hours: String(config.defaultUntilHours) }));
|
|
115
|
-
console.log(import_chalk.default.cyan((0, import_i18n.t)("config.summary.parallelism")));
|
|
116
|
-
console.log((0, import_i18n.t)("config.summary.maxConcurrent", { count: String(((_b = config.parallelism) == null ? void 0 : _b.maxConcurrent) || 3) }));
|
|
117
|
-
console.log(import_chalk.default.cyan((0, import_i18n.t)("config.summary.remoteCache")));
|
|
118
|
-
console.log((0, import_i18n.t)("config.summary.cacheDir", { dir: ((_c = config.remoteCache) == null ? void 0 : _c.dir) || "/tmp/openspec-stat-cache" }));
|
|
119
|
-
console.log(
|
|
120
|
-
(0, import_i18n.t)("config.summary.autoCleanup", {
|
|
121
|
-
enabled: ((_d = config.remoteCache) == null ? void 0 : _d.cleanupOnComplete) ? "Yes" : "No"
|
|
122
|
-
})
|
|
123
|
-
);
|
|
124
|
-
console.log();
|
|
125
|
-
}
|
|
126
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
127
|
-
0 && (module.exports = {
|
|
128
|
-
printConfigSummary,
|
|
129
|
-
validateAndFillDefaults
|
|
130
|
-
});
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
export declare function runConfigWizard(isMultiRepo?: boolean): Promise<void>;
|
|
2
|
-
export declare const SINGLE_REPO_TEMPLATE: {
|
|
3
|
-
defaultBranches: string[];
|
|
4
|
-
defaultSinceHours: number;
|
|
5
|
-
defaultUntilHours: number;
|
|
6
|
-
authorMapping: {
|
|
7
|
-
'user@email1.com': string;
|
|
8
|
-
'user@email2.com': string;
|
|
9
|
-
};
|
|
10
|
-
openspecDir: string;
|
|
11
|
-
excludeExtensions: string[];
|
|
12
|
-
activeUserWeeks: number;
|
|
13
|
-
};
|
|
14
|
-
export declare const MULTI_REPO_TEMPLATE: {
|
|
15
|
-
mode: string;
|
|
16
|
-
repositories: ({
|
|
17
|
-
name: string;
|
|
18
|
-
type: string;
|
|
19
|
-
path: string;
|
|
20
|
-
branches: string[];
|
|
21
|
-
url?: undefined;
|
|
22
|
-
cloneOptions?: undefined;
|
|
23
|
-
} | {
|
|
24
|
-
name: string;
|
|
25
|
-
type: string;
|
|
26
|
-
url: string;
|
|
27
|
-
branches: string[];
|
|
28
|
-
cloneOptions: {
|
|
29
|
-
depth: null;
|
|
30
|
-
singleBranch: boolean;
|
|
31
|
-
};
|
|
32
|
-
path?: undefined;
|
|
33
|
-
})[];
|
|
34
|
-
defaultSinceHours: number;
|
|
35
|
-
defaultUntilHours: number;
|
|
36
|
-
authorMapping: {};
|
|
37
|
-
openspecDir: string;
|
|
38
|
-
excludeExtensions: string[];
|
|
39
|
-
activeUserWeeks: number;
|
|
40
|
-
parallelism: {
|
|
41
|
-
maxConcurrent: number;
|
|
42
|
-
timeout: number;
|
|
43
|
-
};
|
|
44
|
-
remoteCache: {
|
|
45
|
-
dir: string;
|
|
46
|
-
autoCleanup: boolean;
|
|
47
|
-
cleanupOnComplete: boolean;
|
|
48
|
-
cleanupOnError: boolean;
|
|
49
|
-
};
|
|
50
|
-
};
|