alanbox 0.1.2 → 0.1.4
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/0boxer/AGENTS.md +26 -0
- package/0boxer/src/AGENTS.md +16 -0
- package/0boxer/src/cli.js +53 -0
- package/0boxer/src/commands/AGENTS.md +16 -0
- package/{0commondflowv1 → 0boxer}/src/commands/install.js +56 -0
- package/{0commondflowv1 → 1swarmer}/AGENTS.md +14 -12
- package/1swarmer/src/AGENTS.md +28 -0
- package/{0commondflowv1 → 1swarmer}/src/args.js +8 -1
- package/{0commondflowv1 → 1swarmer}/src/cli.js +27 -17
- package/1swarmer/src/commands/AGENTS.md +31 -0
- package/{0commondflowv1 → 1swarmer}/src/commands/doctor.js +2 -2
- package/1swarmer/src/commands/review-file.js +997 -0
- package/{0commondflowv1 → 1swarmer}/src/core/AGENTS.md +2 -2
- package/{0commondflowv1 → 1swarmer}/src/core/prompt-templates.js +1 -1
- package/{0commondflowv1 → 1swarmer}/src/core/storage.js +1 -1
- package/{0commondflowv1 → 1swarmer}/src/prompt/AGENTS.md +1 -1
- package/{0commondflowv1 → 1swarmer}/src/prompt/default.md +1 -1
- package/{0commondflowv1 → 1swarmer}/src/prompt/synthesizer.md +1 -1
- package/{0commondflowv1 → 1swarmer}/src/prompt/verifier.md +1 -1
- package/{0commondflowv1 → 1swarmer}/src/runner/AGENTS.md +4 -3
- package/{0commondflowv1 → 1swarmer}/src/runner/codex-runner.js +23 -3
- package/2designer/README.md +42 -0
- package/2designer/dist/cdp-engine-4AIWSWXO.js +314 -0
- package/2designer/dist/cdp-engine-4AIWSWXO.js.map +1 -0
- package/2designer/dist/cdp-engine-SG4K2BCX.js +10 -0
- package/2designer/dist/cdp-engine-SG4K2BCX.js.map +1 -0
- package/2designer/dist/chunk-7X7PTLZH.js +185 -0
- package/2designer/dist/chunk-7X7PTLZH.js.map +1 -0
- package/2designer/dist/chunk-DPOWNFOH.js +313 -0
- package/2designer/dist/chunk-DPOWNFOH.js.map +1 -0
- package/2designer/dist/chunk-ISUUIOO7.js +58 -0
- package/2designer/dist/chunk-ISUUIOO7.js.map +1 -0
- package/2designer/dist/chunk-NLYFLQ3C.js +74 -0
- package/2designer/dist/chunk-NLYFLQ3C.js.map +1 -0
- package/2designer/dist/chunk-UVKSRKXR.js +71 -0
- package/2designer/dist/chunk-UVKSRKXR.js.map +1 -0
- package/2designer/dist/cli.js +748 -0
- package/2designer/dist/cli.js.map +1 -0
- package/2designer/dist/index.d.ts +118 -0
- package/2designer/dist/index.js +37 -0
- package/2designer/dist/index.js.map +1 -0
- package/2designer/dist/playwright-engine-YXBY3KEN.js +186 -0
- package/2designer/dist/playwright-engine-YXBY3KEN.js.map +1 -0
- package/2designer/dist/playwright-engine-YXGDTSZ5.js +8 -0
- package/2designer/dist/playwright-engine-YXGDTSZ5.js.map +1 -0
- package/2designer/dist/tint-UD4CJ7S2.js +7 -0
- package/2designer/dist/tint-UD4CJ7S2.js.map +1 -0
- package/2designer/dist/tint-YN63MLVN.js +60 -0
- package/2designer/dist/tint-YN63MLVN.js.map +1 -0
- package/2designer/package.json +56 -0
- package/4reporter/README.md +24 -0
- package/4reporter/dist/cli.js +464 -0
- package/4reporter/dist/cli.js.map +1 -0
- package/4reporter/dist/index.d.ts +108 -0
- package/4reporter/dist/index.js +445 -0
- package/4reporter/dist/index.js.map +1 -0
- package/4reporter/package.json +39 -0
- package/README.md +20 -9
- package/bin/alanbox.js +11 -0
- package/bin/designer.js +10 -0
- package/bin/reporter.js +11 -0
- package/bin/swarmer.js +11 -0
- package/cli.js +178 -0
- package/hooks/hooks.json +1 -1
- package/mcp/README.md +7 -1
- package/mcp/config.toml +4 -0
- package/package.json +28 -11
- package/plugin/AGENTS.md +2 -2
- package/plugin/plugin.json +7 -7
- package/shared/AGENTS.md +15 -0
- package/shared/package-args.js +68 -0
- package/skills/AGENTS.md +9 -5
- package/skills/aitool/SKILL.md +36 -0
- package/skills/desginer/SKILL.md +142 -0
- package/skills/swarmer/SKILL.md +146 -0
- package/0commondflowv1/src/AGENTS.md +0 -26
- package/0commondflowv1/src/commands/AGENTS.md +0 -29
- package/bin/multirunagent.js +0 -15
- package/skills/aibox-swam/SKILL.md +0 -77
- package/skills/sub-codex-doctor/SKILL.md +0 -27
- package/skills/sub-codex-swarm/SKILL.md +0 -56
- /package/{0commondflowv1 → 1swarmer}/res/three-lens-review.js +0 -0
- /package/{0commondflowv1 → 1swarmer}/src/commands/info.js +0 -0
- /package/{0commondflowv1 → 1swarmer}/src/commands/swarm/auto.js +0 -0
- /package/{0commondflowv1 → 1swarmer}/src/commands/swarm/custom.js +0 -0
- /package/{0commondflowv1 → 1swarmer}/src/commands/swarm/index.js +0 -0
- /package/{0commondflowv1 → 1swarmer}/src/core/handoff.js +0 -0
- /package/{0commondflowv1 → 1swarmer}/src/core/prompt-builder.js +0 -0
- /package/{0commondflowv1 → 1swarmer}/src/core/swarm-executor.js +0 -0
- /package/{0commondflowv1 → 1swarmer}/src/core/workers.js +0 -0
- /package/{0commondflowv1 → 1swarmer}/src/core/workflow-planner.js +0 -0
- /package/{0commondflowv1 → 1swarmer}/src/core/workflow-storage.js +0 -0
- /package/{0commondflowv1 → 1swarmer}/src/prompt/reviewer.md +0 -0
- /package/{0commondflowv1 → 1swarmer}/src/runner/config.json +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "designer",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Runtime UI measurement CLI for AI agents.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"designer": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup",
|
|
13
|
+
"dev": "tsup --watch",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"lint": "tsc --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"design",
|
|
23
|
+
"verification",
|
|
24
|
+
"css",
|
|
25
|
+
"figma",
|
|
26
|
+
"overlay",
|
|
27
|
+
"ai"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/Fzhiyu1/designer.git"
|
|
33
|
+
},
|
|
34
|
+
"packageManager": "pnpm@10.28.2",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"commander": "^14.0.3",
|
|
37
|
+
"sharp": "^0.34.5",
|
|
38
|
+
"ws": "^8.20.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^25.5.2",
|
|
42
|
+
"@types/ws": "^8.18.1",
|
|
43
|
+
"tsup": "^8.5.1",
|
|
44
|
+
"typescript": "^5.8.3",
|
|
45
|
+
"vitest": "^4.1.4"
|
|
46
|
+
},
|
|
47
|
+
"optionalDependencies": {
|
|
48
|
+
"playwright": "^1.59.1"
|
|
49
|
+
},
|
|
50
|
+
"pnpm": {
|
|
51
|
+
"onlyBuiltDependencies": [
|
|
52
|
+
"esbuild",
|
|
53
|
+
"sharp"
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# reporter
|
|
2
|
+
|
|
3
|
+
`reporter` scans local Git history for a user and writes a weekly report in Chinese Markdown.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```powershell
|
|
8
|
+
reporter weekly --path "C:\path\to\repo-or-file" --user "Your Git Name"
|
|
9
|
+
alanbox reporter weekly --path "C:\path\to\repo-or-file" --user "you@example.com" --output weekly.md
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
If `--output` is omitted, reports are written under `C:\Users\lenovo\Desktop\all-project\.tmp\reporter`.
|
|
13
|
+
|
|
14
|
+
By default it scans all refs from local Monday 00:00 to now. Use `--current-branch` to restrict the scan, or `--since` / `--until` for a fixed range.
|
|
15
|
+
The scan is scoped to the file or folder passed with `--path`; use `--whole-repo` to scan every path in the Git repository.
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
```powershell
|
|
20
|
+
reporter weekly --path "C:\repo" --user "Alice"
|
|
21
|
+
reporter weekly --path "C:\repo\src\index.ts" --user "Alice,alice@example.com" --format json
|
|
22
|
+
reporter weekly --path "C:\repo" --user "Alice" --since 2026-06-15 --until 2026-06-18 --output weekly.md
|
|
23
|
+
reporter weekly --path "C:\repo\packages\app" --user "Alice" --whole-repo
|
|
24
|
+
```
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// src/cli.ts
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
|
|
7
|
+
// src/commands/weekly.ts
|
|
8
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
9
|
+
import { dirname as dirname2, join, resolve as resolve2 } from "path";
|
|
10
|
+
|
|
11
|
+
// src/report.ts
|
|
12
|
+
var CATEGORY_ORDER = [
|
|
13
|
+
{ id: "feature", label: "\u529F\u80FD\u5F00\u53D1" },
|
|
14
|
+
{ id: "fix", label: "\u95EE\u9898\u4FEE\u590D" },
|
|
15
|
+
{ id: "refactor", label: "\u91CD\u6784\u7EF4\u62A4" },
|
|
16
|
+
{ id: "test", label: "\u6D4B\u8BD5\u9A8C\u8BC1" },
|
|
17
|
+
{ id: "docs", label: "\u6587\u6863\u8BF4\u660E" },
|
|
18
|
+
{ id: "config", label: "\u914D\u7F6E\u6784\u5EFA" },
|
|
19
|
+
{ id: "other", label: "\u5176\u4ED6\u5DE5\u4F5C" }
|
|
20
|
+
];
|
|
21
|
+
function summarizeWeeklyScan(scan) {
|
|
22
|
+
const categoryMap = /* @__PURE__ */ new Map();
|
|
23
|
+
const fileMap = /* @__PURE__ */ new Map();
|
|
24
|
+
const followUps = /* @__PURE__ */ new Set();
|
|
25
|
+
for (const commit of scan.commits) {
|
|
26
|
+
const category = classifyCommit(commit);
|
|
27
|
+
const commits = categoryMap.get(category) ?? [];
|
|
28
|
+
commits.push(commit);
|
|
29
|
+
categoryMap.set(category, commits);
|
|
30
|
+
if (/\b(todo|fixme|wip)\b|待|临时|暂/i.test(commit.subject)) {
|
|
31
|
+
followUps.add(cleanSubject(commit.subject));
|
|
32
|
+
}
|
|
33
|
+
for (const file of commit.files) {
|
|
34
|
+
const current = fileMap.get(file.path) ?? {
|
|
35
|
+
path: file.path,
|
|
36
|
+
commits: 0,
|
|
37
|
+
insertions: 0,
|
|
38
|
+
deletions: 0
|
|
39
|
+
};
|
|
40
|
+
current.commits += 1;
|
|
41
|
+
current.insertions += file.insertions;
|
|
42
|
+
current.deletions += file.deletions;
|
|
43
|
+
fileMap.set(file.path, current);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
commitCount: scan.commits.length,
|
|
48
|
+
fileCount: fileMap.size,
|
|
49
|
+
insertions: sumCommits(scan.commits, "insertions"),
|
|
50
|
+
deletions: sumCommits(scan.commits, "deletions"),
|
|
51
|
+
categories: CATEGORY_ORDER.map((category) => ({
|
|
52
|
+
...category,
|
|
53
|
+
commits: categoryMap.get(category.id) ?? []
|
|
54
|
+
})).filter((category) => category.commits.length > 0),
|
|
55
|
+
topFiles: [...fileMap.values()].sort((a, b) => b.insertions + b.deletions + b.commits - (a.insertions + a.deletions + a.commits)).slice(0, 8),
|
|
56
|
+
followUps: [...followUps].slice(0, 8)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function classifyCommit(commit) {
|
|
60
|
+
const subject = commit.subject.toLowerCase();
|
|
61
|
+
const files = commit.files.map((file) => file.path.toLowerCase());
|
|
62
|
+
if (/(fix|bug|hotfix|修复|问题|错误|缺陷)/i.test(subject)) return "fix";
|
|
63
|
+
if (/(test|spec|vitest|jest|测试|验证)/i.test(subject) || files.some(isTestFile)) return "test";
|
|
64
|
+
if (/(docs?|readme|文档|说明)/i.test(subject) || files.every(isDocFile)) return "docs";
|
|
65
|
+
if (/(refactor|cleanup|chore|重构|整理|维护)/i.test(subject)) return "refactor";
|
|
66
|
+
if (/(build|ci|config|deps?|package|配置|构建|依赖)/i.test(subject) || files.some(isConfigFile)) {
|
|
67
|
+
return "config";
|
|
68
|
+
}
|
|
69
|
+
if (/(feat|feature|add|init|实现|新增|添加|完成|支持)/i.test(subject)) return "feature";
|
|
70
|
+
return "other";
|
|
71
|
+
}
|
|
72
|
+
function formatWeeklyReport(scan, summary = summarizeWeeklyScan(scan)) {
|
|
73
|
+
const lines = [];
|
|
74
|
+
const since = formatDate(scan.range.since);
|
|
75
|
+
const until = formatDate(scan.range.until);
|
|
76
|
+
lines.push(`# \u5468\u62A5\uFF1A${since.date} \u81F3 ${until.date}`);
|
|
77
|
+
lines.push("");
|
|
78
|
+
lines.push(`- \u4ED3\u5E93\uFF1A${scan.repo.name}`);
|
|
79
|
+
lines.push(`- \u8DEF\u5F84\uFF1A${scan.repo.root}`);
|
|
80
|
+
lines.push(`- \u626B\u63CF\u8303\u56F4\uFF1A${scan.target.scopedPath ?? "\u6574\u4E2A\u4ED3\u5E93"}`);
|
|
81
|
+
lines.push(`- \u5206\u652F\uFF1A${scan.repo.branch}`);
|
|
82
|
+
lines.push(`- \u7528\u6237\uFF1A${scan.user}`);
|
|
83
|
+
lines.push(`- \u65F6\u95F4\u8303\u56F4\uFF1A${since.full} - ${until.full}`);
|
|
84
|
+
lines.push(`- \u63D0\u4EA4\u7EDF\u8BA1\uFF1A${summary.commitCount} \u4E2A\u63D0\u4EA4\uFF0C${summary.fileCount} \u4E2A\u6587\u4EF6\uFF0C+${summary.insertions} / -${summary.deletions}`);
|
|
85
|
+
if (scan.repo.remote) {
|
|
86
|
+
lines.push(`- \u8FDC\u7AEF\uFF1A${scan.repo.remote}`);
|
|
87
|
+
}
|
|
88
|
+
lines.push("");
|
|
89
|
+
if (summary.commitCount === 0) {
|
|
90
|
+
lines.push("## \u672C\u5468\u5DE5\u4F5C");
|
|
91
|
+
lines.push("");
|
|
92
|
+
lines.push("\u672C\u5468\u5728\u5F53\u524D\u4ED3\u5E93\u548C\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u626B\u63CF\u5230\u5339\u914D\u7528\u6237\u7684\u63D0\u4EA4\u3002\u8BF7\u786E\u8BA4 `--user` \u662F\u5426\u4E0E Git author/committer \u7684 name \u6216 email \u4E00\u81F4\uFF0C\u6216\u4F7F\u7528 `--since` / `--until` \u8C03\u6574\u8303\u56F4\u3002");
|
|
93
|
+
return lines.join("\n");
|
|
94
|
+
}
|
|
95
|
+
lines.push("## \u672C\u5468\u91CD\u70B9");
|
|
96
|
+
lines.push("");
|
|
97
|
+
for (const category of summary.categories) {
|
|
98
|
+
const subjects = category.commits.slice(0, 3).map((commit) => cleanSubject(commit.subject));
|
|
99
|
+
const suffix = category.commits.length > subjects.length ? ` \u7B49 ${category.commits.length} \u9879` : "";
|
|
100
|
+
lines.push(`- ${category.label}\uFF1A${subjects.join("\uFF1B")}${suffix}`);
|
|
101
|
+
}
|
|
102
|
+
lines.push("");
|
|
103
|
+
lines.push("## \u5177\u4F53\u5DE5\u4F5C");
|
|
104
|
+
lines.push("");
|
|
105
|
+
for (const category of summary.categories) {
|
|
106
|
+
lines.push(`### ${category.label}`);
|
|
107
|
+
for (const commit of category.commits.slice(0, 10)) {
|
|
108
|
+
lines.push(formatCommitLine(commit));
|
|
109
|
+
}
|
|
110
|
+
if (category.commits.length > 10) {
|
|
111
|
+
lines.push(`- \u5176\u4F59 ${category.commits.length - 10} \u4E2A\u63D0\u4EA4\u5DF2\u7701\u7565\u3002`);
|
|
112
|
+
}
|
|
113
|
+
lines.push("");
|
|
114
|
+
}
|
|
115
|
+
if (summary.topFiles.length > 0) {
|
|
116
|
+
lines.push("## \u91CD\u70B9\u53D8\u66F4\u6587\u4EF6");
|
|
117
|
+
lines.push("");
|
|
118
|
+
for (const file of summary.topFiles) {
|
|
119
|
+
lines.push(`- ${file.path}\uFF1A${file.commits} \u6B21\u63D0\u4EA4\uFF0C+${file.insertions} / -${file.deletions}`);
|
|
120
|
+
}
|
|
121
|
+
lines.push("");
|
|
122
|
+
}
|
|
123
|
+
lines.push("## \u98CE\u9669\u4E0E\u5F85\u8DDF\u8FDB");
|
|
124
|
+
lines.push("");
|
|
125
|
+
if (summary.followUps.length > 0) {
|
|
126
|
+
for (const item of summary.followUps) {
|
|
127
|
+
lines.push(`- ${item}`);
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
lines.push("- \u6682\u672A\u4ECE\u63D0\u4EA4\u4FE1\u606F\u4E2D\u8BC6\u522B\u51FA\u660E\u786E\u963B\u585E\u6216\u5F85\u529E\u3002");
|
|
131
|
+
}
|
|
132
|
+
if (scan.uncommitted?.files.length) {
|
|
133
|
+
lines.push("");
|
|
134
|
+
lines.push("## \u672A\u63D0\u4EA4\u53D8\u66F4");
|
|
135
|
+
lines.push("");
|
|
136
|
+
lines.push("\u4EE5\u4E0B\u5185\u5BB9\u6765\u81EA\u5DE5\u4F5C\u533A\u72B6\u6001\uFF0C\u672A\u6309\u7528\u6237\u540D\u5F52\u56E0\uFF1A");
|
|
137
|
+
for (const file of scan.uncommitted.files.slice(0, 12)) {
|
|
138
|
+
lines.push(`- ${file.status} ${file.path}`);
|
|
139
|
+
}
|
|
140
|
+
if (scan.uncommitted.files.length > 12) {
|
|
141
|
+
lines.push(`- \u5176\u4F59 ${scan.uncommitted.files.length - 12} \u4E2A\u6587\u4EF6\u5DF2\u7701\u7565\u3002`);
|
|
142
|
+
}
|
|
143
|
+
if (scan.uncommitted.shortstat) {
|
|
144
|
+
lines.push(`- Diff \u6458\u8981\uFF1A${scan.uncommitted.shortstat}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return lines.join("\n").trimEnd();
|
|
148
|
+
}
|
|
149
|
+
function formatCommitLine(commit) {
|
|
150
|
+
const files = summarizeFiles(commit.files);
|
|
151
|
+
return `- ${cleanSubject(commit.subject)}\uFF08${commit.shortHash}\uFF0C${files}\uFF0C+${commit.insertions} / -${commit.deletions}\uFF09`;
|
|
152
|
+
}
|
|
153
|
+
function summarizeFiles(files) {
|
|
154
|
+
if (files.length === 0) return "\u65E0\u6587\u4EF6\u7EDF\u8BA1";
|
|
155
|
+
const preview = files.slice(0, 2).map((file) => file.path).join("\u3001");
|
|
156
|
+
if (files.length > 2) return `${preview} \u7B49 ${files.length} \u4E2A\u6587\u4EF6`;
|
|
157
|
+
return `${preview}\uFF0C${files.length} \u4E2A\u6587\u4EF6`;
|
|
158
|
+
}
|
|
159
|
+
function cleanSubject(subject) {
|
|
160
|
+
return subject.replace(/^(feat|fix|docs|chore|refactor|test|style|perf|build|ci)(\([^)]+\))?:\s*/i, "").trim() || "(\u65E0\u63D0\u4EA4\u8BF4\u660E)";
|
|
161
|
+
}
|
|
162
|
+
function formatDate(value) {
|
|
163
|
+
const date = new Date(value);
|
|
164
|
+
const year = date.getFullYear();
|
|
165
|
+
const month = pad(date.getMonth() + 1);
|
|
166
|
+
const day = pad(date.getDate());
|
|
167
|
+
const hour = pad(date.getHours());
|
|
168
|
+
const minute = pad(date.getMinutes());
|
|
169
|
+
return {
|
|
170
|
+
date: `${year}-${month}-${day}`,
|
|
171
|
+
full: `${year}-${month}-${day} ${hour}:${minute}`
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function pad(value) {
|
|
175
|
+
return String(value).padStart(2, "0");
|
|
176
|
+
}
|
|
177
|
+
function sumCommits(commits, field) {
|
|
178
|
+
return commits.reduce((sum, commit) => sum + commit[field], 0);
|
|
179
|
+
}
|
|
180
|
+
function isTestFile(file) {
|
|
181
|
+
return /(^|\/)(__tests__|test|tests|spec)\//.test(file) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(file);
|
|
182
|
+
}
|
|
183
|
+
function isDocFile(file) {
|
|
184
|
+
return /\.(md|mdx|txt|rst)$/i.test(file);
|
|
185
|
+
}
|
|
186
|
+
function isConfigFile(file) {
|
|
187
|
+
return /(^|\/)(package\.json|pnpm-lock\.yaml|tsconfig\.json|vite\.config|vitest\.config|tsup\.config|\.github\/|config\/)/i.test(file);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/git.ts
|
|
191
|
+
import { execFileSync } from "child_process";
|
|
192
|
+
import { existsSync, statSync } from "fs";
|
|
193
|
+
import { basename, dirname, isAbsolute, relative, resolve } from "path";
|
|
194
|
+
var RECORD_SEPARATOR = "";
|
|
195
|
+
var FIELD_SEPARATOR = "";
|
|
196
|
+
function findGitRoot(inputPath) {
|
|
197
|
+
const absolutePath = resolve(inputPath);
|
|
198
|
+
if (!existsSync(absolutePath)) {
|
|
199
|
+
throw new Error(`path does not exist: ${absolutePath}`);
|
|
200
|
+
}
|
|
201
|
+
const startPath = statSync(absolutePath).isDirectory() ? absolutePath : dirname(absolutePath);
|
|
202
|
+
const root = runGit(startPath, ["rev-parse", "--show-toplevel"]).trim();
|
|
203
|
+
if (!root) {
|
|
204
|
+
throw new Error(`not a Git repository: ${absolutePath}`);
|
|
205
|
+
}
|
|
206
|
+
return root;
|
|
207
|
+
}
|
|
208
|
+
function getRepoInfo(root) {
|
|
209
|
+
const branch = runGit(root, ["branch", "--show-current"], { allowFailure: true }).trim() || `detached:${runGit(root, ["rev-parse", "--short", "HEAD"], { allowFailure: true }).trim()}` || "unknown";
|
|
210
|
+
const remote = runGit(root, ["config", "--get", "remote.origin.url"], { allowFailure: true }).trim();
|
|
211
|
+
return {
|
|
212
|
+
root,
|
|
213
|
+
name: basename(root),
|
|
214
|
+
branch,
|
|
215
|
+
remote: remote || void 0
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function scanWeeklyWork(options) {
|
|
219
|
+
const root = findGitRoot(options.targetPath);
|
|
220
|
+
const scopedPath = options.wholeRepo ? void 0 : getScopedPath(root, options.targetPath);
|
|
221
|
+
const userAliases = parseUserAliases(options.user);
|
|
222
|
+
const commits = readCommits(root, {
|
|
223
|
+
since: options.since,
|
|
224
|
+
until: options.until,
|
|
225
|
+
includeAllRefs: options.includeAllRefs ?? true,
|
|
226
|
+
scopedPath
|
|
227
|
+
}).filter((commit) => matchesUser(commit, userAliases));
|
|
228
|
+
return {
|
|
229
|
+
repo: getRepoInfo(root),
|
|
230
|
+
target: {
|
|
231
|
+
inputPath: resolve(options.targetPath),
|
|
232
|
+
scopedPath
|
|
233
|
+
},
|
|
234
|
+
user: options.user,
|
|
235
|
+
userAliases,
|
|
236
|
+
range: {
|
|
237
|
+
since: options.since.toISOString(),
|
|
238
|
+
until: options.until.toISOString()
|
|
239
|
+
},
|
|
240
|
+
commits,
|
|
241
|
+
uncommitted: options.includeUncommitted ? readUncommittedSummary(root, scopedPath) : void 0
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function readCommits(root, options) {
|
|
245
|
+
const args = ["log"];
|
|
246
|
+
if (options.includeAllRefs) args.push("--all");
|
|
247
|
+
args.push(
|
|
248
|
+
`--since=${options.since.toISOString()}`,
|
|
249
|
+
`--until=${options.until.toISOString()}`,
|
|
250
|
+
"--date=iso-strict",
|
|
251
|
+
"--numstat",
|
|
252
|
+
`--format=${RECORD_SEPARATOR}%H${FIELD_SEPARATOR}%an${FIELD_SEPARATOR}%ae${FIELD_SEPARATOR}%cn${FIELD_SEPARATOR}%ce${FIELD_SEPARATOR}%aI${FIELD_SEPARATOR}%s`
|
|
253
|
+
);
|
|
254
|
+
if (options.scopedPath) {
|
|
255
|
+
args.push("--", options.scopedPath);
|
|
256
|
+
}
|
|
257
|
+
return parseGitLog(runGit(root, args));
|
|
258
|
+
}
|
|
259
|
+
function parseGitLog(raw) {
|
|
260
|
+
const commits = [];
|
|
261
|
+
const seen = /* @__PURE__ */ new Set();
|
|
262
|
+
for (const block of raw.split(RECORD_SEPARATOR)) {
|
|
263
|
+
const trimmedBlock = block.trim();
|
|
264
|
+
if (!trimmedBlock) continue;
|
|
265
|
+
const lines = trimmedBlock.split(/\r?\n/);
|
|
266
|
+
const fields = (lines.shift() ?? "").split(FIELD_SEPARATOR);
|
|
267
|
+
if (fields.length < 7) continue;
|
|
268
|
+
const [hash, authorName, authorEmail, committerName, committerEmail, date, subject] = fields;
|
|
269
|
+
if (!hash || seen.has(hash)) continue;
|
|
270
|
+
seen.add(hash);
|
|
271
|
+
const files = lines.map((line) => parseNumstatLine(line)).filter((file) => Boolean(file));
|
|
272
|
+
const insertions = files.reduce((sum, file) => sum + file.insertions, 0);
|
|
273
|
+
const deletions = files.reduce((sum, file) => sum + file.deletions, 0);
|
|
274
|
+
commits.push({
|
|
275
|
+
hash,
|
|
276
|
+
shortHash: hash.slice(0, 7),
|
|
277
|
+
authorName,
|
|
278
|
+
authorEmail,
|
|
279
|
+
committerName,
|
|
280
|
+
committerEmail,
|
|
281
|
+
date,
|
|
282
|
+
subject: subject || "(no subject)",
|
|
283
|
+
files,
|
|
284
|
+
insertions,
|
|
285
|
+
deletions
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
return commits;
|
|
289
|
+
}
|
|
290
|
+
function parseUserAliases(user) {
|
|
291
|
+
const rawAliases = Array.isArray(user) ? user : user.split(",");
|
|
292
|
+
const aliases = rawAliases.map((alias) => alias.trim().toLowerCase()).filter(Boolean);
|
|
293
|
+
if (!aliases.length) {
|
|
294
|
+
throw new Error("--user must include at least one Git name or email alias");
|
|
295
|
+
}
|
|
296
|
+
return aliases;
|
|
297
|
+
}
|
|
298
|
+
function matchesUser(commit, user) {
|
|
299
|
+
const aliases = parseUserAliases(user);
|
|
300
|
+
const haystack = [
|
|
301
|
+
commit.authorName,
|
|
302
|
+
commit.authorEmail,
|
|
303
|
+
commit.committerName,
|
|
304
|
+
commit.committerEmail
|
|
305
|
+
].join(" ").toLowerCase();
|
|
306
|
+
return aliases.some((alias) => haystack.includes(alias));
|
|
307
|
+
}
|
|
308
|
+
function parseNumstatLine(line) {
|
|
309
|
+
if (!line.trim()) return void 0;
|
|
310
|
+
const [insertionsRaw, deletionsRaw, ...pathParts] = line.split(" ");
|
|
311
|
+
const filePath = pathParts.join(" ").trim();
|
|
312
|
+
if (!filePath) return void 0;
|
|
313
|
+
const binary = insertionsRaw === "-" || deletionsRaw === "-";
|
|
314
|
+
return {
|
|
315
|
+
path: filePath,
|
|
316
|
+
insertions: binary ? 0 : Number.parseInt(insertionsRaw, 10) || 0,
|
|
317
|
+
deletions: binary ? 0 : Number.parseInt(deletionsRaw, 10) || 0,
|
|
318
|
+
binary
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function readUncommittedSummary(root, scopedPath) {
|
|
322
|
+
const pathArgs = scopedPath ? ["--", scopedPath] : [];
|
|
323
|
+
const status = runGit(root, ["status", "--porcelain=v1", ...pathArgs], { allowFailure: true });
|
|
324
|
+
const files = status.split(/\r?\n/).map((line) => line.trimEnd()).filter(Boolean).map((line) => ({
|
|
325
|
+
status: line.slice(0, 2).trim() || "??",
|
|
326
|
+
path: line.slice(3).trim()
|
|
327
|
+
}));
|
|
328
|
+
const shortstats = [
|
|
329
|
+
runGit(root, ["diff", "--cached", "--shortstat", ...pathArgs], { allowFailure: true }).trim(),
|
|
330
|
+
runGit(root, ["diff", "--shortstat", ...pathArgs], { allowFailure: true }).trim()
|
|
331
|
+
].filter(Boolean);
|
|
332
|
+
return {
|
|
333
|
+
files,
|
|
334
|
+
shortstat: shortstats.join("; ") || void 0
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function getScopedPath(root, inputPath) {
|
|
338
|
+
const rootPath = resolve(root);
|
|
339
|
+
const absoluteInput = resolve(inputPath);
|
|
340
|
+
const scopedPath = relative(rootPath, absoluteInput);
|
|
341
|
+
if (!scopedPath) return void 0;
|
|
342
|
+
if (scopedPath.startsWith("..") || isAbsolute(scopedPath)) return void 0;
|
|
343
|
+
return scopedPath.replace(/\\/g, "/");
|
|
344
|
+
}
|
|
345
|
+
function runGit(cwd, args, options = {}) {
|
|
346
|
+
try {
|
|
347
|
+
return execFileSync("git", ["-C", cwd, ...args], {
|
|
348
|
+
encoding: "utf8",
|
|
349
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
350
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
351
|
+
});
|
|
352
|
+
} catch (error) {
|
|
353
|
+
if (options.allowFailure) return "";
|
|
354
|
+
const gitError = error;
|
|
355
|
+
const stderr = gitError.stderr?.toString().trim();
|
|
356
|
+
const detail = stderr || gitError.message || "unknown git error";
|
|
357
|
+
throw new Error(`git ${args.join(" ")} failed in ${cwd}: ${detail}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// src/commands/weekly.ts
|
|
362
|
+
var DEFAULT_REPORT_DIR = "C:\\Users\\lenovo\\Desktop\\all-project\\.tmp\\reporter";
|
|
363
|
+
function getLocalWeekRange(now = /* @__PURE__ */ new Date()) {
|
|
364
|
+
const since = new Date(now);
|
|
365
|
+
since.setHours(0, 0, 0, 0);
|
|
366
|
+
const day = since.getDay();
|
|
367
|
+
const diffToMonday = day === 0 ? -6 : 1 - day;
|
|
368
|
+
since.setDate(since.getDate() + diffToMonday);
|
|
369
|
+
return { since, until: now };
|
|
370
|
+
}
|
|
371
|
+
function buildWeeklyOptions(raw, now = /* @__PURE__ */ new Date()) {
|
|
372
|
+
const targetPath = raw.path ?? raw.repo ?? raw.address;
|
|
373
|
+
if (!targetPath) {
|
|
374
|
+
throw new Error("--path is required");
|
|
375
|
+
}
|
|
376
|
+
if (!raw.user) {
|
|
377
|
+
throw new Error("--user is required");
|
|
378
|
+
}
|
|
379
|
+
const defaultRange = getLocalWeekRange(now);
|
|
380
|
+
const since = parseDateOption(raw.since, "start") ?? defaultRange.since;
|
|
381
|
+
const until = parseDateOption(raw.until, "end") ?? defaultRange.until;
|
|
382
|
+
if (since.getTime() > until.getTime()) {
|
|
383
|
+
throw new Error("--since must be earlier than --until");
|
|
384
|
+
}
|
|
385
|
+
const format = normalizeFormat(raw.format);
|
|
386
|
+
const explicitOutput = Boolean(raw.output);
|
|
387
|
+
const output = explicitOutput ? resolve2(String(raw.output)) : buildDefaultOutputPath(targetPath, raw.user, since, format);
|
|
388
|
+
return {
|
|
389
|
+
targetPath: String(targetPath),
|
|
390
|
+
user: String(raw.user),
|
|
391
|
+
since,
|
|
392
|
+
until,
|
|
393
|
+
includeAllRefs: !raw.currentBranch,
|
|
394
|
+
wholeRepo: Boolean(raw.wholeRepo),
|
|
395
|
+
includeUncommitted: Boolean(raw.includeUncommitted),
|
|
396
|
+
format,
|
|
397
|
+
output,
|
|
398
|
+
explicitOutput
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
function renderWeeklyOutput(scan, format) {
|
|
402
|
+
const summary = summarizeWeeklyScan(scan);
|
|
403
|
+
if (format === "json") {
|
|
404
|
+
return JSON.stringify({ scan, summary }, null, 2);
|
|
405
|
+
}
|
|
406
|
+
return formatWeeklyReport(scan, summary);
|
|
407
|
+
}
|
|
408
|
+
async function weekly(raw) {
|
|
409
|
+
const options = buildWeeklyOptions(raw);
|
|
410
|
+
const scan = scanWeeklyWork(options);
|
|
411
|
+
const rendered = renderWeeklyOutput(scan, options.format);
|
|
412
|
+
mkdirSync(dirname2(options.output), { recursive: true });
|
|
413
|
+
writeFileSync(options.output, rendered, "utf8");
|
|
414
|
+
console.log(`weekly report written to ${options.output}`);
|
|
415
|
+
}
|
|
416
|
+
function buildDefaultOutputPath(targetPath, user, since, format) {
|
|
417
|
+
const ext = format === "json" ? "json" : "md";
|
|
418
|
+
const date = formatDateForFile(since);
|
|
419
|
+
const userSlug = slugify(user) || "user";
|
|
420
|
+
const targetSlug = slugify(targetPath.split(/[\\/]/).filter(Boolean).pop() ?? "repo") || "repo";
|
|
421
|
+
return join(DEFAULT_REPORT_DIR, `${date}-${targetSlug}-${userSlug}.${ext}`);
|
|
422
|
+
}
|
|
423
|
+
function parseDateOption(value, boundary) {
|
|
424
|
+
if (value == null || value === "") return void 0;
|
|
425
|
+
const raw = String(value).trim();
|
|
426
|
+
const dateOnly = /^\d{4}-\d{2}-\d{2}$/.test(raw);
|
|
427
|
+
const date = dateOnly ? /* @__PURE__ */ new Date(`${raw}T${boundary === "start" ? "00:00:00" : "23:59:59"}`) : new Date(raw);
|
|
428
|
+
if (Number.isNaN(date.getTime())) {
|
|
429
|
+
throw new Error(`invalid date: ${raw}`);
|
|
430
|
+
}
|
|
431
|
+
return date;
|
|
432
|
+
}
|
|
433
|
+
function normalizeFormat(value) {
|
|
434
|
+
const format = String(value ?? "markdown").toLowerCase();
|
|
435
|
+
if (format === "markdown" || format === "json") return format;
|
|
436
|
+
throw new Error("--format must be markdown or json");
|
|
437
|
+
}
|
|
438
|
+
function formatDateForFile(date) {
|
|
439
|
+
const year = date.getFullYear();
|
|
440
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
441
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
442
|
+
return `${year}-${month}-${day}`;
|
|
443
|
+
}
|
|
444
|
+
function slugify(value) {
|
|
445
|
+
return value.trim().replace(/@/g, "-at-").replace(/[^a-zA-Z0-9\u4e00-\u9fa5._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// src/cli.ts
|
|
449
|
+
var program = new Command();
|
|
450
|
+
program.name("reporter").description("Generate Chinese weekly reports from local Git history.").version("0.1.0");
|
|
451
|
+
program.addHelpText("after", `
|
|
452
|
+
Workflow:
|
|
453
|
+
1. Provide --path for a file or folder inside a Git repository
|
|
454
|
+
2. Provide --user with one or more Git author aliases
|
|
455
|
+
3. reporter scans this week by default and writes a Markdown report
|
|
456
|
+
|
|
457
|
+
Examples:
|
|
458
|
+
$ reporter weekly --path C:\\repo --user "Alice"
|
|
459
|
+
$ reporter weekly --path C:\\repo\\src\\index.ts --user "Alice,alice@example.com" --output weekly.md
|
|
460
|
+
$ reporter weekly --path C:\\repo --user "Alice" --since 2026-06-15 --until 2026-06-18
|
|
461
|
+
`);
|
|
462
|
+
program.command("weekly").description("Scan Git commits for this week and generate a weekly report.").option("-p, --path <path>", "File or directory inside the target Git repository").option("--repo <path>", "Alias of --path").option("--address <path>", "Alias of --path").requiredOption("-u, --user <user>", "Git user name/email aliases, comma-separated").option("--since <date>", "Start date/time; default is local Monday 00:00").option("--until <date>", "End date/time; default is now").option("--current-branch", "Only inspect the current branch instead of all refs").option("--whole-repo", "Scan the whole repository instead of limiting to --path").option("--include-uncommitted", "Append an unattributed summary of uncommitted changes").option("--output <path>", "Write the report to a file (default: C:\\Users\\lenovo\\Desktop\\all-project\\.tmp\\reporter)").option("--format <format>", "Output format: markdown | json", "markdown").action(weekly);
|
|
463
|
+
program.parse();
|
|
464
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/weekly.ts","../src/report.ts","../src/git.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { weekly } from './commands/weekly.js'\n\nconst program = new Command()\n\nprogram\n .name('reporter')\n .description('Generate Chinese weekly reports from local Git history.')\n .version('0.1.0')\n\nprogram.addHelpText('after', `\nWorkflow:\n 1. Provide --path for a file or folder inside a Git repository\n 2. Provide --user with one or more Git author aliases\n 3. reporter scans this week by default and writes a Markdown report\n\nExamples:\n $ reporter weekly --path C:\\\\repo --user \"Alice\"\n $ reporter weekly --path C:\\\\repo\\\\src\\\\index.ts --user \"Alice,alice@example.com\" --output weekly.md\n $ reporter weekly --path C:\\\\repo --user \"Alice\" --since 2026-06-15 --until 2026-06-18\n`)\n\nprogram\n .command('weekly')\n .description('Scan Git commits for this week and generate a weekly report.')\n .option('-p, --path <path>', 'File or directory inside the target Git repository')\n .option('--repo <path>', 'Alias of --path')\n .option('--address <path>', 'Alias of --path')\n .requiredOption('-u, --user <user>', 'Git user name/email aliases, comma-separated')\n .option('--since <date>', 'Start date/time; default is local Monday 00:00')\n .option('--until <date>', 'End date/time; default is now')\n .option('--current-branch', 'Only inspect the current branch instead of all refs')\n .option('--whole-repo', 'Scan the whole repository instead of limiting to --path')\n .option('--include-uncommitted', 'Append an unattributed summary of uncommitted changes')\n .option('--output <path>', 'Write the report to a file (default: C:\\\\Users\\\\lenovo\\\\Desktop\\\\all-project\\\\.tmp\\\\reporter)')\n .option('--format <format>', 'Output format: markdown | json', 'markdown')\n .action(weekly)\n\nprogram.parse()\n","import { mkdirSync, writeFileSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport { formatWeeklyReport, summarizeWeeklyScan } from '../report.js'\nimport { scanWeeklyWork, type WeeklyScan } from '../git.js'\n\nexport type WeeklyOutputFormat = 'markdown' | 'json'\n\nexport const DEFAULT_REPORT_DIR = 'C:\\\\Users\\\\lenovo\\\\Desktop\\\\all-project\\\\.tmp\\\\reporter'\n\nexport interface WeeklyCommandOptions {\n targetPath: string\n user: string\n since: Date\n until: Date\n includeAllRefs: boolean\n wholeRepo: boolean\n includeUncommitted: boolean\n format: WeeklyOutputFormat\n output: string\n explicitOutput: boolean\n}\n\nexport function getLocalWeekRange(now = new Date()): { since: Date; until: Date } {\n const since = new Date(now)\n since.setHours(0, 0, 0, 0)\n const day = since.getDay()\n const diffToMonday = day === 0 ? -6 : 1 - day\n since.setDate(since.getDate() + diffToMonday)\n\n return { since, until: now }\n}\n\nexport function buildWeeklyOptions(raw: Record<string, any>, now = new Date()): WeeklyCommandOptions {\n const targetPath = raw.path ?? raw.repo ?? raw.address\n if (!targetPath) {\n throw new Error('--path is required')\n }\n if (!raw.user) {\n throw new Error('--user is required')\n }\n\n const defaultRange = getLocalWeekRange(now)\n const since = parseDateOption(raw.since, 'start') ?? defaultRange.since\n const until = parseDateOption(raw.until, 'end') ?? defaultRange.until\n\n if (since.getTime() > until.getTime()) {\n throw new Error('--since must be earlier than --until')\n }\n\n const format = normalizeFormat(raw.format)\n const explicitOutput = Boolean(raw.output)\n const output = explicitOutput\n ? resolve(String(raw.output))\n : buildDefaultOutputPath(targetPath, raw.user, since, format)\n\n return {\n targetPath: String(targetPath),\n user: String(raw.user),\n since,\n until,\n includeAllRefs: !raw.currentBranch,\n wholeRepo: Boolean(raw.wholeRepo),\n includeUncommitted: Boolean(raw.includeUncommitted),\n format,\n output,\n explicitOutput,\n }\n}\n\nexport function renderWeeklyOutput(scan: WeeklyScan, format: WeeklyOutputFormat): string {\n const summary = summarizeWeeklyScan(scan)\n if (format === 'json') {\n return JSON.stringify({ scan, summary }, null, 2)\n }\n return formatWeeklyReport(scan, summary)\n}\n\nexport async function weekly(raw: Record<string, any>): Promise<void> {\n const options = buildWeeklyOptions(raw)\n const scan = scanWeeklyWork(options)\n const rendered = renderWeeklyOutput(scan, options.format)\n\n mkdirSync(dirname(options.output), { recursive: true })\n writeFileSync(options.output, rendered, 'utf8')\n console.log(`weekly report written to ${options.output}`)\n}\n\nexport function buildDefaultOutputPath(\n targetPath: string,\n user: string,\n since: Date,\n format: WeeklyOutputFormat,\n): string {\n const ext = format === 'json' ? 'json' : 'md'\n const date = formatDateForFile(since)\n const userSlug = slugify(user) || 'user'\n const targetSlug = slugify(targetPath.split(/[\\\\/]/).filter(Boolean).pop() ?? 'repo') || 'repo'\n\n return join(DEFAULT_REPORT_DIR, `${date}-${targetSlug}-${userSlug}.${ext}`)\n}\n\nfunction parseDateOption(value: unknown, boundary: 'start' | 'end'): Date | undefined {\n if (value == null || value === '') return undefined\n\n const raw = String(value).trim()\n const dateOnly = /^\\d{4}-\\d{2}-\\d{2}$/.test(raw)\n const date = dateOnly\n ? new Date(`${raw}T${boundary === 'start' ? '00:00:00' : '23:59:59'}`)\n : new Date(raw)\n\n if (Number.isNaN(date.getTime())) {\n throw new Error(`invalid date: ${raw}`)\n }\n\n return date\n}\n\nfunction normalizeFormat(value: unknown): WeeklyOutputFormat {\n const format = String(value ?? 'markdown').toLowerCase()\n if (format === 'markdown' || format === 'json') return format\n throw new Error('--format must be markdown or json')\n}\n\nfunction formatDateForFile(date: Date): string {\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const day = String(date.getDate()).padStart(2, '0')\n return `${year}-${month}-${day}`\n}\n\nfunction slugify(value: string): string {\n return value\n .trim()\n .replace(/@/g, '-at-')\n .replace(/[^a-zA-Z0-9\\u4e00-\\u9fa5._-]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 80)\n}\n","import type { GitCommit, GitFileStat, WeeklyScan } from './git.js'\n\nexport interface CategorySummary {\n id: string\n label: string\n commits: GitCommit[]\n}\n\nexport interface FileSummary {\n path: string\n commits: number\n insertions: number\n deletions: number\n}\n\nexport interface WeeklySummary {\n commitCount: number\n fileCount: number\n insertions: number\n deletions: number\n categories: CategorySummary[]\n topFiles: FileSummary[]\n followUps: string[]\n}\n\nconst CATEGORY_ORDER = [\n { id: 'feature', label: '功能开发' },\n { id: 'fix', label: '问题修复' },\n { id: 'refactor', label: '重构维护' },\n { id: 'test', label: '测试验证' },\n { id: 'docs', label: '文档说明' },\n { id: 'config', label: '配置构建' },\n { id: 'other', label: '其他工作' },\n]\n\nexport function summarizeWeeklyScan(scan: WeeklyScan): WeeklySummary {\n const categoryMap = new Map<string, GitCommit[]>()\n const fileMap = new Map<string, FileSummary>()\n const followUps = new Set<string>()\n\n for (const commit of scan.commits) {\n const category = classifyCommit(commit)\n const commits = categoryMap.get(category) ?? []\n commits.push(commit)\n categoryMap.set(category, commits)\n\n if (/\\b(todo|fixme|wip)\\b|待|临时|暂/i.test(commit.subject)) {\n followUps.add(cleanSubject(commit.subject))\n }\n\n for (const file of commit.files) {\n const current = fileMap.get(file.path) ?? {\n path: file.path,\n commits: 0,\n insertions: 0,\n deletions: 0,\n }\n current.commits += 1\n current.insertions += file.insertions\n current.deletions += file.deletions\n fileMap.set(file.path, current)\n }\n }\n\n return {\n commitCount: scan.commits.length,\n fileCount: fileMap.size,\n insertions: sumCommits(scan.commits, 'insertions'),\n deletions: sumCommits(scan.commits, 'deletions'),\n categories: CATEGORY_ORDER\n .map(category => ({\n ...category,\n commits: categoryMap.get(category.id) ?? [],\n }))\n .filter(category => category.commits.length > 0),\n topFiles: [...fileMap.values()]\n .sort((a, b) => (b.insertions + b.deletions + b.commits) - (a.insertions + a.deletions + a.commits))\n .slice(0, 8),\n followUps: [...followUps].slice(0, 8),\n }\n}\n\nexport function classifyCommit(commit: GitCommit): string {\n const subject = commit.subject.toLowerCase()\n const files = commit.files.map(file => file.path.toLowerCase())\n\n if (/(fix|bug|hotfix|修复|问题|错误|缺陷)/i.test(subject)) return 'fix'\n if (/(test|spec|vitest|jest|测试|验证)/i.test(subject) || files.some(isTestFile)) return 'test'\n if (/(docs?|readme|文档|说明)/i.test(subject) || files.every(isDocFile)) return 'docs'\n if (/(refactor|cleanup|chore|重构|整理|维护)/i.test(subject)) return 'refactor'\n if (\n /(build|ci|config|deps?|package|配置|构建|依赖)/i.test(subject)\n || files.some(isConfigFile)\n ) {\n return 'config'\n }\n if (/(feat|feature|add|init|实现|新增|添加|完成|支持)/i.test(subject)) return 'feature'\n\n return 'other'\n}\n\nexport function formatWeeklyReport(scan: WeeklyScan, summary = summarizeWeeklyScan(scan)): string {\n const lines: string[] = []\n const since = formatDate(scan.range.since)\n const until = formatDate(scan.range.until)\n\n lines.push(`# 周报:${since.date} 至 ${until.date}`)\n lines.push('')\n lines.push(`- 仓库:${scan.repo.name}`)\n lines.push(`- 路径:${scan.repo.root}`)\n lines.push(`- 扫描范围:${scan.target.scopedPath ?? '整个仓库'}`)\n lines.push(`- 分支:${scan.repo.branch}`)\n lines.push(`- 用户:${scan.user}`)\n lines.push(`- 时间范围:${since.full} - ${until.full}`)\n lines.push(`- 提交统计:${summary.commitCount} 个提交,${summary.fileCount} 个文件,+${summary.insertions} / -${summary.deletions}`)\n\n if (scan.repo.remote) {\n lines.push(`- 远端:${scan.repo.remote}`)\n }\n\n lines.push('')\n\n if (summary.commitCount === 0) {\n lines.push('## 本周工作')\n lines.push('')\n lines.push('本周在当前仓库和时间范围内没有扫描到匹配用户的提交。请确认 `--user` 是否与 Git author/committer 的 name 或 email 一致,或使用 `--since` / `--until` 调整范围。')\n return lines.join('\\n')\n }\n\n lines.push('## 本周重点')\n lines.push('')\n for (const category of summary.categories) {\n const subjects = category.commits.slice(0, 3).map(commit => cleanSubject(commit.subject))\n const suffix = category.commits.length > subjects.length ? ` 等 ${category.commits.length} 项` : ''\n lines.push(`- ${category.label}:${subjects.join(';')}${suffix}`)\n }\n\n lines.push('')\n lines.push('## 具体工作')\n lines.push('')\n for (const category of summary.categories) {\n lines.push(`### ${category.label}`)\n for (const commit of category.commits.slice(0, 10)) {\n lines.push(formatCommitLine(commit))\n }\n if (category.commits.length > 10) {\n lines.push(`- 其余 ${category.commits.length - 10} 个提交已省略。`)\n }\n lines.push('')\n }\n\n if (summary.topFiles.length > 0) {\n lines.push('## 重点变更文件')\n lines.push('')\n for (const file of summary.topFiles) {\n lines.push(`- ${file.path}:${file.commits} 次提交,+${file.insertions} / -${file.deletions}`)\n }\n lines.push('')\n }\n\n lines.push('## 风险与待跟进')\n lines.push('')\n if (summary.followUps.length > 0) {\n for (const item of summary.followUps) {\n lines.push(`- ${item}`)\n }\n } else {\n lines.push('- 暂未从提交信息中识别出明确阻塞或待办。')\n }\n\n if (scan.uncommitted?.files.length) {\n lines.push('')\n lines.push('## 未提交变更')\n lines.push('')\n lines.push('以下内容来自工作区状态,未按用户名归因:')\n for (const file of scan.uncommitted.files.slice(0, 12)) {\n lines.push(`- ${file.status} ${file.path}`)\n }\n if (scan.uncommitted.files.length > 12) {\n lines.push(`- 其余 ${scan.uncommitted.files.length - 12} 个文件已省略。`)\n }\n if (scan.uncommitted.shortstat) {\n lines.push(`- Diff 摘要:${scan.uncommitted.shortstat}`)\n }\n }\n\n return lines.join('\\n').trimEnd()\n}\n\nfunction formatCommitLine(commit: GitCommit): string {\n const files = summarizeFiles(commit.files)\n return `- ${cleanSubject(commit.subject)}(${commit.shortHash},${files},+${commit.insertions} / -${commit.deletions})`\n}\n\nfunction summarizeFiles(files: GitFileStat[]): string {\n if (files.length === 0) return '无文件统计'\n const preview = files.slice(0, 2).map(file => file.path).join('、')\n if (files.length > 2) return `${preview} 等 ${files.length} 个文件`\n return `${preview},${files.length} 个文件`\n}\n\nfunction cleanSubject(subject: string): string {\n return subject\n .replace(/^(feat|fix|docs|chore|refactor|test|style|perf|build|ci)(\\([^)]+\\))?:\\s*/i, '')\n .trim()\n || '(无提交说明)'\n}\n\nfunction formatDate(value: string): { date: string; full: string } {\n const date = new Date(value)\n const year = date.getFullYear()\n const month = pad(date.getMonth() + 1)\n const day = pad(date.getDate())\n const hour = pad(date.getHours())\n const minute = pad(date.getMinutes())\n\n return {\n date: `${year}-${month}-${day}`,\n full: `${year}-${month}-${day} ${hour}:${minute}`,\n }\n}\n\nfunction pad(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nfunction sumCommits(commits: GitCommit[], field: 'insertions' | 'deletions'): number {\n return commits.reduce((sum, commit) => sum + commit[field], 0)\n}\n\nfunction isTestFile(file: string): boolean {\n return /(^|\\/)(__tests__|test|tests|spec)\\//.test(file) || /\\.(test|spec)\\.[cm]?[jt]sx?$/.test(file)\n}\n\nfunction isDocFile(file: string): boolean {\n return /\\.(md|mdx|txt|rst)$/i.test(file)\n}\n\nfunction isConfigFile(file: string): boolean {\n return /(^|\\/)(package\\.json|pnpm-lock\\.yaml|tsconfig\\.json|vite\\.config|vitest\\.config|tsup\\.config|\\.github\\/|config\\/)/i.test(file)\n}\n","import { execFileSync } from 'node:child_process'\nimport { existsSync, statSync } from 'node:fs'\nimport { basename, dirname, isAbsolute, relative, resolve } from 'node:path'\n\nconst RECORD_SEPARATOR = '\\x1e'\nconst FIELD_SEPARATOR = '\\x1f'\n\nexport interface GitFileStat {\n path: string\n insertions: number\n deletions: number\n binary: boolean\n}\n\nexport interface GitCommit {\n hash: string\n shortHash: string\n authorName: string\n authorEmail: string\n committerName: string\n committerEmail: string\n date: string\n subject: string\n files: GitFileStat[]\n insertions: number\n deletions: number\n}\n\nexport interface RepoInfo {\n root: string\n name: string\n branch: string\n remote?: string\n}\n\nexport interface UncommittedSummary {\n files: Array<{ status: string; path: string }>\n shortstat?: string\n}\n\nexport interface WeeklyScan {\n repo: RepoInfo\n target: {\n inputPath: string\n scopedPath?: string\n }\n user: string\n userAliases: string[]\n range: {\n since: string\n until: string\n }\n commits: GitCommit[]\n uncommitted?: UncommittedSummary\n}\n\nexport interface WeeklyScanOptions {\n targetPath: string\n user: string\n since: Date\n until: Date\n includeAllRefs?: boolean\n wholeRepo?: boolean\n includeUncommitted?: boolean\n}\n\ninterface RunGitOptions {\n allowFailure?: boolean\n}\n\nexport function findGitRoot(inputPath: string): string {\n const absolutePath = resolve(inputPath)\n if (!existsSync(absolutePath)) {\n throw new Error(`path does not exist: ${absolutePath}`)\n }\n\n const startPath = statSync(absolutePath).isDirectory() ? absolutePath : dirname(absolutePath)\n const root = runGit(startPath, ['rev-parse', '--show-toplevel']).trim()\n if (!root) {\n throw new Error(`not a Git repository: ${absolutePath}`)\n }\n return root\n}\n\nexport function getRepoInfo(root: string): RepoInfo {\n const branch = runGit(root, ['branch', '--show-current'], { allowFailure: true }).trim()\n || `detached:${runGit(root, ['rev-parse', '--short', 'HEAD'], { allowFailure: true }).trim()}`\n || 'unknown'\n const remote = runGit(root, ['config', '--get', 'remote.origin.url'], { allowFailure: true }).trim()\n\n return {\n root,\n name: basename(root),\n branch,\n remote: remote || undefined,\n }\n}\n\nexport function scanWeeklyWork(options: WeeklyScanOptions): WeeklyScan {\n const root = findGitRoot(options.targetPath)\n const scopedPath = options.wholeRepo ? undefined : getScopedPath(root, options.targetPath)\n const userAliases = parseUserAliases(options.user)\n const commits = readCommits(root, {\n since: options.since,\n until: options.until,\n includeAllRefs: options.includeAllRefs ?? true,\n scopedPath,\n }).filter(commit => matchesUser(commit, userAliases))\n\n return {\n repo: getRepoInfo(root),\n target: {\n inputPath: resolve(options.targetPath),\n scopedPath,\n },\n user: options.user,\n userAliases,\n range: {\n since: options.since.toISOString(),\n until: options.until.toISOString(),\n },\n commits,\n uncommitted: options.includeUncommitted ? readUncommittedSummary(root, scopedPath) : undefined,\n }\n}\n\nexport function readCommits(\n root: string,\n options: { since: Date; until: Date; includeAllRefs: boolean; scopedPath?: string },\n): GitCommit[] {\n const args = ['log']\n if (options.includeAllRefs) args.push('--all')\n args.push(\n `--since=${options.since.toISOString()}`,\n `--until=${options.until.toISOString()}`,\n '--date=iso-strict',\n '--numstat',\n `--format=${RECORD_SEPARATOR}%H${FIELD_SEPARATOR}%an${FIELD_SEPARATOR}%ae${FIELD_SEPARATOR}%cn${FIELD_SEPARATOR}%ce${FIELD_SEPARATOR}%aI${FIELD_SEPARATOR}%s`,\n )\n if (options.scopedPath) {\n args.push('--', options.scopedPath)\n }\n\n return parseGitLog(runGit(root, args))\n}\n\nexport function parseGitLog(raw: string): GitCommit[] {\n const commits: GitCommit[] = []\n const seen = new Set<string>()\n\n for (const block of raw.split(RECORD_SEPARATOR)) {\n const trimmedBlock = block.trim()\n if (!trimmedBlock) continue\n\n const lines = trimmedBlock.split(/\\r?\\n/)\n const fields = (lines.shift() ?? '').split(FIELD_SEPARATOR)\n if (fields.length < 7) continue\n\n const [hash, authorName, authorEmail, committerName, committerEmail, date, subject] = fields\n if (!hash || seen.has(hash)) continue\n seen.add(hash)\n\n const files = lines\n .map(line => parseNumstatLine(line))\n .filter((file): file is GitFileStat => Boolean(file))\n const insertions = files.reduce((sum, file) => sum + file.insertions, 0)\n const deletions = files.reduce((sum, file) => sum + file.deletions, 0)\n\n commits.push({\n hash,\n shortHash: hash.slice(0, 7),\n authorName,\n authorEmail,\n committerName,\n committerEmail,\n date,\n subject: subject || '(no subject)',\n files,\n insertions,\n deletions,\n })\n }\n\n return commits\n}\n\nexport function parseUserAliases(user: string | string[]): string[] {\n const rawAliases = Array.isArray(user) ? user : user.split(',')\n const aliases = rawAliases\n .map(alias => alias.trim().toLowerCase())\n .filter(Boolean)\n\n if (!aliases.length) {\n throw new Error('--user must include at least one Git name or email alias')\n }\n\n return aliases\n}\n\nexport function matchesUser(commit: GitCommit, user: string | string[]): boolean {\n const aliases = parseUserAliases(user)\n const haystack = [\n commit.authorName,\n commit.authorEmail,\n commit.committerName,\n commit.committerEmail,\n ].join(' ').toLowerCase()\n\n return aliases.some(alias => haystack.includes(alias))\n}\n\nfunction parseNumstatLine(line: string): GitFileStat | undefined {\n if (!line.trim()) return undefined\n const [insertionsRaw, deletionsRaw, ...pathParts] = line.split('\\t')\n const filePath = pathParts.join('\\t').trim()\n if (!filePath) return undefined\n\n const binary = insertionsRaw === '-' || deletionsRaw === '-'\n return {\n path: filePath,\n insertions: binary ? 0 : Number.parseInt(insertionsRaw, 10) || 0,\n deletions: binary ? 0 : Number.parseInt(deletionsRaw, 10) || 0,\n binary,\n }\n}\n\nfunction readUncommittedSummary(root: string, scopedPath?: string): UncommittedSummary {\n const pathArgs = scopedPath ? ['--', scopedPath] : []\n const status = runGit(root, ['status', '--porcelain=v1', ...pathArgs], { allowFailure: true })\n const files = status\n .split(/\\r?\\n/)\n .map(line => line.trimEnd())\n .filter(Boolean)\n .map(line => ({\n status: line.slice(0, 2).trim() || '??',\n path: line.slice(3).trim(),\n }))\n\n const shortstats = [\n runGit(root, ['diff', '--cached', '--shortstat', ...pathArgs], { allowFailure: true }).trim(),\n runGit(root, ['diff', '--shortstat', ...pathArgs], { allowFailure: true }).trim(),\n ].filter(Boolean)\n\n return {\n files,\n shortstat: shortstats.join('; ') || undefined,\n }\n}\n\nfunction getScopedPath(root: string, inputPath: string): string | undefined {\n const rootPath = resolve(root)\n const absoluteInput = resolve(inputPath)\n const scopedPath = relative(rootPath, absoluteInput)\n if (!scopedPath) return undefined\n if (scopedPath.startsWith('..') || isAbsolute(scopedPath)) return undefined\n\n return scopedPath.replace(/\\\\/g, '/')\n}\n\nfunction runGit(cwd: string, args: string[], options: RunGitOptions = {}): string {\n try {\n return execFileSync('git', ['-C', cwd, ...args], {\n encoding: 'utf8',\n maxBuffer: 20 * 1024 * 1024,\n stdio: ['ignore', 'pipe', 'pipe'],\n })\n } catch (error) {\n if (options.allowFailure) return ''\n\n const gitError = error as { stderr?: Buffer | string; message?: string }\n const stderr = gitError.stderr?.toString().trim()\n const detail = stderr || gitError.message || 'unknown git error'\n throw new Error(`git ${args.join(' ')} failed in ${cwd}: ${detail}`)\n }\n}\n"],"mappings":"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","names":["dirname","resolve","resolve","dirname"]}
|