project-knowledge 0.1.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/CHANGELOG.md +34 -0
- package/INDEX.md +53 -0
- package/README.md +79 -0
- package/_site/README.md +63 -0
- package/_site/_test/ai-profile-test.js +199 -0
- package/_site/_test/baseline-schema-test.js +132 -0
- package/_site/_test/commit-analysis-test.js +184 -0
- package/_site/_test/context-pack-test.js +199 -0
- package/_site/_test/draft-apply-test.js +363 -0
- package/_site/_test/git-validation-test.js +171 -0
- package/_site/_test/hook-trigger-test.js +257 -0
- package/_site/_test/initial-analysis-test.js +228 -0
- package/_site/_test/job-orchestrator-test.js +297 -0
- package/_site/_test/kb-v2-templates-test.js +189 -0
- package/_site/_test/pr-consumer-contract-test.js +236 -0
- package/_site/_test/run-all-tests.js +135 -0
- package/_site/_test/scanner-test.js +206 -0
- package/_site/_test/ui-smoke-test.js +237 -0
- package/_site/_test/ui-test.js +237 -0
- package/_site/index.html +1166 -0
- package/_site/lib/ai-adapter.js +287 -0
- package/_site/lib/analysis-orchestrator.js +433 -0
- package/_site/lib/context-pack-builder.js +290 -0
- package/_site/lib/draft-apply.js +219 -0
- package/_site/lib/git-runner.js +26 -0
- package/_site/lib/hook-manager.js +148 -0
- package/_site/lib/job-orchestrator.js +231 -0
- package/_site/lib/kb-validator.js +224 -0
- package/_site/lib/llm-client.js +126 -0
- package/_site/lib/scanner.js +94 -0
- package/_site/scripts/hook-trigger.js +133 -0
- package/_site/scripts/safe-runner.js +151 -0
- package/_site/server.js +1058 -0
- package/_site/start.bat +26 -0
- package/_site/stop.bat +11 -0
- package/ai-profiles.json +18 -0
- package/docs/ai-knowledge-base-system-design.md +395 -0
- package/docs/pr-consumer-contract.md +198 -0
- package/docs/project-goal.md +72 -0
- package/docs/project-registry-schema.md +46 -0
- package/docs/testing-strategy.md +169 -0
- package/iterations.json +23 -0
- package/package.json +47 -0
- package/scripts/gen-commit-doc.ps1 +178 -0
- package/scripts/gen-commit-doc.sh +197 -0
- package/scripts/list-features.ps1 +41 -0
- package/scripts/register-scheduled-task.bat +5 -0
- package/templates/change.md +59 -0
- package/templates/commit-feature.md +56 -0
- package/templates/feature.md +44 -0
- package/templates/framework.md +80 -0
- package/templates/index-header.md +3 -0
- package/templates/kb-manifest.json +38 -0
- package/templates/module.md +58 -0
- package/templates/project-analysis.md +48 -0
- package/templates/project-goal.md +55 -0
- package/templates/project-readme.md +60 -0
- package/templates/quality-review-rules.md +37 -0
- package/templates/update-entry.md +7 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Project Goal
|
|
2
|
+
|
|
3
|
+
Status: accepted
|
|
4
|
+
Owner: SanQian.Xu
|
|
5
|
+
Last updated: 2026-06-12
|
|
6
|
+
|
|
7
|
+
## One-Sentence Goal
|
|
8
|
+
|
|
9
|
+
将 `project-knowledge-base` 建设成一个以项目目标为最高事实源、由 Git 提交触发、通过 AI 自动分析代码并生成可审核知识库草稿的本地项目监督系统,为后续 PR 自动审查与项目优化工具提供准确、可信、可追溯的知识基础。
|
|
10
|
+
|
|
11
|
+
## Original Starting Goal
|
|
12
|
+
|
|
13
|
+
项目一开始的目标是做一个本地 Git 项目知识库管理工具:
|
|
14
|
+
|
|
15
|
+
- 管理多个本地项目的知识库目录。
|
|
16
|
+
- 从 `projects.json` 读取项目列表。
|
|
17
|
+
- 通过本地 UI 导入或查看项目。
|
|
18
|
+
- 基于 Git 提交记录生成 Markdown 文档。
|
|
19
|
+
- 通过脚本和定时任务持续更新项目提交文档。
|
|
20
|
+
|
|
21
|
+
这个早期目标偏向“本地知识库目录 + 管理 UI + Git commit 文档生成”。
|
|
22
|
+
|
|
23
|
+
## Current Product Goal
|
|
24
|
+
|
|
25
|
+
当前目标已经升级为一个 AI 项目知识库监督系统。它必须能够:
|
|
26
|
+
|
|
27
|
+
- 导入用户选中的本地项目。
|
|
28
|
+
- 校验项目路径和 Git 仓库状态。
|
|
29
|
+
- 初始化项目知识库目录结构。
|
|
30
|
+
- 记录项目状态,包括仓库状态、HEAD、待分析提交、上次已分析提交、知识库初始化状态和异常点。
|
|
31
|
+
- 配置 AI 模型、API 参数和每个项目的知识库输出语言。
|
|
32
|
+
- 自动扫描 Git 提交,发现尚未进入知识库的变更。
|
|
33
|
+
- 自动构建 context pack,收集 Git diff、相关源码、已有知识库、项目目标、配置文件和测试文件。
|
|
34
|
+
- 使用 AI 对项目做整体分析,生成 `project-goal.md` 和 `project-analysis.md` 草稿。
|
|
35
|
+
- 使用 AI 对增量提交做深度分析,判断变更是新功能、旧功能更新、bug fix、重构、基础设施、测试还是文档变更。
|
|
36
|
+
- 生成功能、模块、变更等知识库草稿。
|
|
37
|
+
- 将 AI 结果先写入 `_ai/drafts/`,由用户审核后再 apply 到正式知识库。
|
|
38
|
+
- 保证正式知识库内容是可审核、可回溯、可测试的,而不是 AI 静默写入的结果。
|
|
39
|
+
- 为未来的 PR 项目提供稳定读取契约,让 PR 项目可以基于项目目标和已接受知识库自动检查代码、优化代码、辅助测试。
|
|
40
|
+
|
|
41
|
+
## Core Truth Hierarchy
|
|
42
|
+
|
|
43
|
+
1. `project-goal.md`
|
|
44
|
+
项目的真实目标,是最高优先级的人类确认事实。AI 可以提出修改建议,但不能自动覆盖它。
|
|
45
|
+
|
|
46
|
+
2. `project-analysis.md`
|
|
47
|
+
当前代码实现的分析,是“项目目标如何被实现”的现状说明。它是证据,不是目标本身,可以随着代码变化更新。
|
|
48
|
+
|
|
49
|
+
3. `features/`, `modules/`, `changes/`
|
|
50
|
+
记录功能、模块和提交变更。它们必须说明和项目目标的关系,并尽量引用证据。
|
|
51
|
+
|
|
52
|
+
4. `_ai/drafts/`, `_ai/runs/`, `_ai/context-packs/`
|
|
53
|
+
AI 工作区。这里的内容默认不是可信知识,只有通过审核和 apply 后才能进入正式知识库。
|
|
54
|
+
|
|
55
|
+
## Non-Goals
|
|
56
|
+
|
|
57
|
+
- 不把 AI 输出直接当作可信知识。
|
|
58
|
+
- 不让 AI 静默修改 `project-goal.md`。
|
|
59
|
+
- 不要求用户手动挑选相关文件再交给 AI;上下文收集必须尽量自动化。
|
|
60
|
+
- 不把每个 commit 都简单变成一个孤立文档,而忽略它对功能、模块和项目目标的影响。
|
|
61
|
+
- 不让未来 PR 项目读取 `_ai/drafts/` 作为权威事实。
|
|
62
|
+
|
|
63
|
+
## Development Guardrails
|
|
64
|
+
|
|
65
|
+
- 项目目标优先于项目分析。
|
|
66
|
+
- 项目分析是实现现状,不是目标本身。
|
|
67
|
+
- AI 生成内容必须先进入草稿区。
|
|
68
|
+
- apply 必须是显式动作。
|
|
69
|
+
- `lastAnalyzedCommit` 只能在知识库正式应用成功后推进。
|
|
70
|
+
- 任何面向未来 PR 项目的契约都必须稳定、可测试。
|
|
71
|
+
- 没有测试通过的功能不能视为完成。
|
|
72
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Project Registry Schema
|
|
2
|
+
|
|
3
|
+
Status: baseline schema for TASK-001
|
|
4
|
+
Date: 2026-06-11
|
|
5
|
+
|
|
6
|
+
`projects.json` is the registry for projects supervised by the knowledge base.
|
|
7
|
+
|
|
8
|
+
The current schema version is `v1`. The server normalizes project entries on read and write so older entries can coexist with newer fields.
|
|
9
|
+
|
|
10
|
+
## Required Identity Fields
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"displayName": "Example Project",
|
|
15
|
+
"localPath": "D:\\SanQian.Xu\\Example",
|
|
16
|
+
"gitPath": "D:\\SanQian.Xu\\Example",
|
|
17
|
+
"kbPath": "D:\\SanQian.Xu\\project-knowledge-base\\projects\\example"
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Baseline Fields Added In TASK-001
|
|
22
|
+
|
|
23
|
+
| Field | Default | Purpose |
|
|
24
|
+
|------|---------|---------|
|
|
25
|
+
| `enabled` | `true` | Whether the project is included in future scan/analyze workflows. |
|
|
26
|
+
| `repoStatus` | `"unknown"` | Placeholder for Git validation state. TASK-002 will set concrete values. |
|
|
27
|
+
| `headCommit` | `null` | Latest known repository head commit. |
|
|
28
|
+
| `lastSeenCommit` | `null` | Latest commit observed by a scan. |
|
|
29
|
+
| `lastAnalyzedCommit` | `null` | Latest commit whose knowledge updates were applied. |
|
|
30
|
+
| `aiProfileId` | `"default-agent"` | Analyzer profile selected for this project. |
|
|
31
|
+
| `kbSchemaVersion` | `"v1"` | Knowledge-base layout/schema generation used by the project. |
|
|
32
|
+
| `goalStatus` | `"not-created"` | State of `project-goal.md` for future workflows. |
|
|
33
|
+
|
|
34
|
+
## Normalization Rules
|
|
35
|
+
|
|
36
|
+
- Missing `gitPath` defaults to `localPath`.
|
|
37
|
+
- Missing `kbPath` defaults to `<KB_ROOT>\\projects\\<slug>`.
|
|
38
|
+
- Legacy `D:\\SanQian.Xu\\kb\\projects\\<slug>` paths are replaced with the current `project-knowledge-base` root.
|
|
39
|
+
- Missing `tags` becomes an empty array.
|
|
40
|
+
- String `tags` are split on commas.
|
|
41
|
+
- Existing unknown fields are preserved.
|
|
42
|
+
|
|
43
|
+
## Compatibility
|
|
44
|
+
|
|
45
|
+
`kbInitialized`, `commitCount`, and `moduleCount` are runtime status fields returned by the site. Older project entries may still contain them; TASK-001 does not remove them to avoid unnecessary data churn.
|
|
46
|
+
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Testing Strategy for AI Knowledge Base Development
|
|
2
|
+
|
|
3
|
+
Status: draft
|
|
4
|
+
Date: 2026-06-11
|
|
5
|
+
|
|
6
|
+
## 1. Testing Rule
|
|
7
|
+
|
|
8
|
+
No development task is complete until its tests pass.
|
|
9
|
+
|
|
10
|
+
If tests fail, the task returns to implementation. The developer must fix the feature or update an incorrect test with a clear reason, then run the test plan again.
|
|
11
|
+
|
|
12
|
+
## 2. Test Levels
|
|
13
|
+
|
|
14
|
+
### 2.1 Static and Syntax Tests
|
|
15
|
+
|
|
16
|
+
Purpose: catch broken files before runtime.
|
|
17
|
+
|
|
18
|
+
Required checks:
|
|
19
|
+
|
|
20
|
+
- JSON files parse successfully.
|
|
21
|
+
- Node server scripts pass syntax checks.
|
|
22
|
+
- PowerShell scripts parse and support `-WhatIf` or dry-run where possible.
|
|
23
|
+
- Markdown files that define machine contracts contain required headings and frontmatter.
|
|
24
|
+
|
|
25
|
+
Example commands:
|
|
26
|
+
|
|
27
|
+
```powershell
|
|
28
|
+
node --check _site\server.js
|
|
29
|
+
powershell -NoProfile -Command "$null = Get-Content projects.json -Raw | ConvertFrom-Json"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2.2 API Integration Tests
|
|
33
|
+
|
|
34
|
+
Purpose: prove the local API performs real file and Git operations safely.
|
|
35
|
+
|
|
36
|
+
Required coverage:
|
|
37
|
+
|
|
38
|
+
- `GET /api/state`
|
|
39
|
+
- project upsert
|
|
40
|
+
- Git validation success
|
|
41
|
+
- Git validation failure: missing path
|
|
42
|
+
- Git validation failure: path exists but is not a Git repo
|
|
43
|
+
- Git validation edge case: repo exists but has no commits
|
|
44
|
+
- analysis job creation
|
|
45
|
+
- draft listing
|
|
46
|
+
- draft apply
|
|
47
|
+
- failure reporting
|
|
48
|
+
|
|
49
|
+
### 2.3 Git Fixture Tests
|
|
50
|
+
|
|
51
|
+
Purpose: make Git-driven behavior deterministic.
|
|
52
|
+
|
|
53
|
+
The test harness should create temporary repositories with:
|
|
54
|
+
|
|
55
|
+
- no commits
|
|
56
|
+
- one initial commit
|
|
57
|
+
- multiple commits
|
|
58
|
+
- feature commit touching one module
|
|
59
|
+
- refactor commit touching multiple modules
|
|
60
|
+
- commit that changes tests only
|
|
61
|
+
- commit with binary or large files
|
|
62
|
+
|
|
63
|
+
The analyzer should never require real user repositories for automated tests.
|
|
64
|
+
|
|
65
|
+
### 2.4 AI Adapter Tests
|
|
66
|
+
|
|
67
|
+
Purpose: test orchestration without relying on live AI calls.
|
|
68
|
+
|
|
69
|
+
Required adapters:
|
|
70
|
+
|
|
71
|
+
- `mock-agent`: deterministic output for tests
|
|
72
|
+
- `recorded-agent`: replay saved responses
|
|
73
|
+
- `real-agent`: manual or nightly verification only
|
|
74
|
+
|
|
75
|
+
Automated CI-style tests must use `mock-agent` or `recorded-agent`.
|
|
76
|
+
|
|
77
|
+
### 2.5 Knowledge Write Snapshot Tests
|
|
78
|
+
|
|
79
|
+
Purpose: ensure generated knowledge files are stable and safe.
|
|
80
|
+
|
|
81
|
+
For a known Git fixture and mock AI response, assert:
|
|
82
|
+
|
|
83
|
+
- `project-goal.md` is created as draft first.
|
|
84
|
+
- `project-analysis.md` includes evidence.
|
|
85
|
+
- `features/<feature>.md` is created or updated correctly.
|
|
86
|
+
- `changes/<commit>.md` exists.
|
|
87
|
+
- `kb-manifest.json` points to valid files.
|
|
88
|
+
- accepted knowledge is not overwritten without an apply step.
|
|
89
|
+
|
|
90
|
+
### 2.6 UI End-to-End Tests
|
|
91
|
+
|
|
92
|
+
Purpose: prove the user can complete workflows from the browser.
|
|
93
|
+
|
|
94
|
+
Required Playwright flows:
|
|
95
|
+
|
|
96
|
+
- import valid Git project
|
|
97
|
+
- reject invalid Git project
|
|
98
|
+
- show repository status
|
|
99
|
+
- configure AI profile
|
|
100
|
+
- run initial analysis with mock agent
|
|
101
|
+
- view generated drafts
|
|
102
|
+
- edit project goal
|
|
103
|
+
- apply draft
|
|
104
|
+
- run incremental commit analysis
|
|
105
|
+
- inspect failed analysis run
|
|
106
|
+
|
|
107
|
+
### 2.7 Regression Tests for Existing Site
|
|
108
|
+
|
|
109
|
+
The current site behavior must remain working:
|
|
110
|
+
|
|
111
|
+
- projects render
|
|
112
|
+
- add project form works
|
|
113
|
+
- schedule controls work
|
|
114
|
+
- manual run starts a job
|
|
115
|
+
- log output renders
|
|
116
|
+
- tree view renders
|
|
117
|
+
|
|
118
|
+
## 3. Task Completion Gate
|
|
119
|
+
|
|
120
|
+
Every task file must list:
|
|
121
|
+
|
|
122
|
+
- automated tests to add
|
|
123
|
+
- manual checks to run
|
|
124
|
+
- fixtures required
|
|
125
|
+
- failure behavior
|
|
126
|
+
- definition of done
|
|
127
|
+
|
|
128
|
+
Completion requires:
|
|
129
|
+
|
|
130
|
+
1. All task-specific tests pass.
|
|
131
|
+
2. Existing regression tests pass.
|
|
132
|
+
3. The developer records the executed tests in the task file or test report.
|
|
133
|
+
4. Known failures are either fixed or explicitly moved into a follow-up task.
|
|
134
|
+
|
|
135
|
+
## 4. Failure Loop
|
|
136
|
+
|
|
137
|
+
```mermaid
|
|
138
|
+
flowchart TD
|
|
139
|
+
A["Implement task"] --> B["Run required tests"]
|
|
140
|
+
B --> C{"All tests pass?"}
|
|
141
|
+
C -->|No| D["Fix implementation or justified test issue"]
|
|
142
|
+
D --> B
|
|
143
|
+
C -->|Yes| E["Mark task complete"]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 5. Recommended Test File Layout
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
_site/_test/
|
|
150
|
+
├─ ui-test.js
|
|
151
|
+
├─ api-test.js
|
|
152
|
+
├─ fixtures/
|
|
153
|
+
│ ├─ make-git-repos.js
|
|
154
|
+
│ └─ mock-agent-responses/
|
|
155
|
+
├─ snapshots/
|
|
156
|
+
└─ TEST-REPORT.md
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Future non-UI scripts can use:
|
|
160
|
+
|
|
161
|
+
```text
|
|
162
|
+
tests/
|
|
163
|
+
├─ git-inspector.test.js
|
|
164
|
+
├─ context-pack-builder.test.js
|
|
165
|
+
├─ draft-writer.test.js
|
|
166
|
+
├─ manifest.test.js
|
|
167
|
+
└─ fixtures/
|
|
168
|
+
```
|
|
169
|
+
|
package/iterations.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "里程碑定义,供 gen-commit-doc.ps1 按里程碑聚合使用。since/until 格式 YYYY-MM-DD,null 表示开放。",
|
|
3
|
+
"claude-devsprite": [
|
|
4
|
+
{ "id": "v1.0-skill-core", "since": "2024-01-01", "until": "2025-04-30", "title": "Skill 核心能力" },
|
|
5
|
+
{ "id": "v2.0-knowledge-graph", "since": "2025-05-01", "until": "2026-04-30", "title": "知识图谱化" },
|
|
6
|
+
{ "id": "v2.1-dashboard", "since": "2026-05-01", "until": null, "title": "Web Dashboard 与多 Agent" }
|
|
7
|
+
],
|
|
8
|
+
"web-remote-control": [
|
|
9
|
+
{ "id": "v0.1-webrtc-poc", "since": "2026-04-01", "until": "2026-05-15", "title": "WebRTC PoC" },
|
|
10
|
+
{ "id": "v0.2-rrweb-and-deploy", "since": "2026-05-16", "until": null, "title": "rrweb 集成与部署" }
|
|
11
|
+
],
|
|
12
|
+
"token-consumption-leaderboard": [
|
|
13
|
+
{ "id": "v1.0-local-dashboard", "since": "2024-01-01", "until": "2024-04-30", "title": "本地 Dashboard" },
|
|
14
|
+
{ "id": "v2.0-leaderboard", "since": "2024-05-01", "until": null, "title": "排行榜与认证" }
|
|
15
|
+
],
|
|
16
|
+
"quant-platform": [
|
|
17
|
+
{ "id": "v1.0-base", "since": null, "until": "2026-03-12", "title": "基础框架" },
|
|
18
|
+
{ "id": "v3.0-multi-market", "since": "2026-03-13", "until": "2026-03-14", "title": "多市场 + 真实数据源" }
|
|
19
|
+
],
|
|
20
|
+
"claude-code-ui": [
|
|
21
|
+
{ "id": "v1.32.0", "since": "2026-01-01", "until": "2026-05-19", "title": "上游 v1.32.0 引用" }
|
|
22
|
+
]
|
|
23
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "project-knowledge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Knowledge base manager with Git integration, AI-driven analysis, and bilingual (zh-CN/en-US) knowledge output",
|
|
5
|
+
"main": "_site/server.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node _site/server.js",
|
|
8
|
+
"test": "node _site/_test/run-all-tests.js"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=18"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"_site/server.js",
|
|
15
|
+
"_site/index.html",
|
|
16
|
+
"_site/lib/",
|
|
17
|
+
"_site/scripts/",
|
|
18
|
+
"_site/_test/*.js",
|
|
19
|
+
"_site/_test/fixtures/",
|
|
20
|
+
"_site/start.bat",
|
|
21
|
+
"_site/stop.bat",
|
|
22
|
+
"templates/",
|
|
23
|
+
"scripts/",
|
|
24
|
+
"ai-profiles.json",
|
|
25
|
+
"iterations.json",
|
|
26
|
+
"docs/",
|
|
27
|
+
"README.md",
|
|
28
|
+
"INDEX.md",
|
|
29
|
+
"CHANGELOG.md"
|
|
30
|
+
],
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/SanQianX/project-knowledge-base.git"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"knowledge-base",
|
|
37
|
+
"git",
|
|
38
|
+
"ai",
|
|
39
|
+
"claude",
|
|
40
|
+
"documentation",
|
|
41
|
+
"scanner",
|
|
42
|
+
"context-pack",
|
|
43
|
+
"post-commit-hook"
|
|
44
|
+
],
|
|
45
|
+
"author": "SanQianX",
|
|
46
|
+
"license": "UNLICENSED"
|
|
47
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# gen-commit-doc.ps1
|
|
2
|
+
# All Chinese strings are loaded from template files to avoid PowerShell
|
|
3
|
+
# parser issues with multi-byte UTF-8 characters in script source.
|
|
4
|
+
[CmdletBinding()]
|
|
5
|
+
param(
|
|
6
|
+
[Parameter(Mandatory = $true)]
|
|
7
|
+
[string] $ProjectSlug,
|
|
8
|
+
[string] $KbRoot = "D:\SanQian.Xu\project-knowledge-base",
|
|
9
|
+
[int] $MaxCommits = 0
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
$ErrorActionPreference = "Stop"
|
|
13
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
14
|
+
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
15
|
+
$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'
|
|
16
|
+
$PSDefaultParameterValues['Set-Content:Encoding'] = 'utf8'
|
|
17
|
+
$PSDefaultParameterValues['Add-Content:Encoding'] = 'utf8'
|
|
18
|
+
|
|
19
|
+
$projectsJsonPath = Join-Path $KbRoot "projects.json"
|
|
20
|
+
if (-not (Test-Path $projectsJsonPath)) {
|
|
21
|
+
throw "Cannot find projects.json: $projectsJsonPath"
|
|
22
|
+
}
|
|
23
|
+
$projects = Get-Content $projectsJsonPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
24
|
+
|
|
25
|
+
$templatePath = Join-Path $KbRoot "templates\commit-feature.md"
|
|
26
|
+
$updateEntryPath = Join-Path $KbRoot "templates\update-entry.md"
|
|
27
|
+
$indexHeaderPath = Join-Path $KbRoot "templates\index-header.md"
|
|
28
|
+
if (-not (Test-Path $templatePath)) { throw "Cannot find template: $templatePath" }
|
|
29
|
+
if (-not (Test-Path $updateEntryPath)) { throw "Cannot find template: $updateEntryPath" }
|
|
30
|
+
if (-not (Test-Path $indexHeaderPath)) { throw "Cannot find template: $indexHeaderPath" }
|
|
31
|
+
|
|
32
|
+
$FEATURE_TEMPLATE = Get-Content $templatePath -Raw -Encoding UTF8
|
|
33
|
+
$UPDATE_ENTRY_TEMPLATE = Get-Content $updateEntryPath -Raw -Encoding UTF8
|
|
34
|
+
$INDEX_HEADER_TEMPLATE = Get-Content $indexHeaderPath -Raw -Encoding UTF8
|
|
35
|
+
|
|
36
|
+
$slugs = @()
|
|
37
|
+
if ($ProjectSlug -eq "ALL") {
|
|
38
|
+
$slugs = @($projects.PSObject.Properties.Name)
|
|
39
|
+
} else {
|
|
40
|
+
if (-not $projects.$ProjectSlug) {
|
|
41
|
+
throw "Slug $ProjectSlug not in projects.json"
|
|
42
|
+
}
|
|
43
|
+
$slugs = @($ProjectSlug)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function Get-FeatureSlug([string]$Subject) {
|
|
47
|
+
$s = $Subject -replace '^(feat|fix|refactor|perf|docs|chore|test|style|build|ci)(\([^)]+\))?:\s*', ''
|
|
48
|
+
# Try ASCII slug first
|
|
49
|
+
$slug = $s.ToLower() -replace '[^a-z0-9]+', '-' -replace '-+', '-' -replace '^-|-$', ''
|
|
50
|
+
if ([string]::IsNullOrWhiteSpace($slug)) {
|
|
51
|
+
# Non-ASCII subject (e.g. Chinese): use a sanitized slice of the original
|
|
52
|
+
$slug = $s.Substring(0, [Math]::Min(20, $s.Length)) -replace '[\s\\/:*?"<>|]+', '-' -replace '-+', '-' -replace '^-|-$', ''
|
|
53
|
+
}
|
|
54
|
+
if ([string]::IsNullOrWhiteSpace($slug)) { $slug = "untitled" }
|
|
55
|
+
if ($slug.Length -gt 50) { $slug = $slug.Substring(0, 50).TrimEnd('-') }
|
|
56
|
+
return $slug
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function Get-CommitType([string]$Subject) {
|
|
60
|
+
if ($Subject -match '^(feat|fix|refactor|perf|docs|chore|test|style|build|ci)(\([^)]+\))?:\s*') {
|
|
61
|
+
return $Matches[1]
|
|
62
|
+
}
|
|
63
|
+
# No conventional prefix: treat as feat (the most common case for older projects)
|
|
64
|
+
return "feat"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function Test-CommitWriteable([string]$Type) {
|
|
68
|
+
return (@('feat','fix','refactor','perf') -contains $Type)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function New-FeatureDocFromTemplate([string]$Project, [string]$FeatureSlug, [string]$Hash, [string]$Date, [string]$Author, [string]$Subject, [string]$Type) {
|
|
72
|
+
$today = Get-Date -Format "yyyy-MM-dd"
|
|
73
|
+
$subjectLine = "# ${Subject}"
|
|
74
|
+
$headerLine = "---`nfeature: ${FeatureSlug}`nproject: ${Project}`nfirstCommit: ${Hash} (${Date}, ${Author})`nlastUpdate: ${Hash} (${Date}, ${Author})`nstatus: active`ncommitType: ${Type}`nrelatedModule: null`ngeneratedAt: ${today}`n---"
|
|
75
|
+
$noteLine = "> Auto-generated by gen-commit-doc.ps1 on $today."
|
|
76
|
+
$initialLine = "## Initial Implementation`n`n- **Commit**: ${Hash} (${Date}, ${Author}): ${Subject}`n- **Type**: ${Type}`n- **Business Impact**: TODO"
|
|
77
|
+
$relatedLine = "## Related Commits`n`n- ${Hash}: ${Subject}"
|
|
78
|
+
$linkedModules = "## Related Modules`n`n- [Framework](../framework.md)"
|
|
79
|
+
$body = "$headerLine`n`n$subjectLine`n`n$noteLine`n`n## Overview`n`nTODO - human to fill in the description.`n`n$initialLine`n`n## Update History (reverse chronological)`n`n$relatedLine`n`n$linkedModules"
|
|
80
|
+
return $body
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function New-UpdateEntry([string]$Date, [string]$Hash, [string]$Subject, [string]$Type, [string]$Author) {
|
|
84
|
+
$body = $UPDATE_ENTRY_TEMPLATE
|
|
85
|
+
$body = $body.Replace('__DATE__', $Date)
|
|
86
|
+
$body = $body.Replace('__HASH__', $Hash)
|
|
87
|
+
$body = $body.Replace('__SUBJECT__', $Subject)
|
|
88
|
+
$body = $body.Replace('__TYPE__', $Type)
|
|
89
|
+
$body = $body.Replace('__AUTHOR__', $Author)
|
|
90
|
+
return $body
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function Rebuild-Index([string]$Slug, [string]$CommitsDir) {
|
|
94
|
+
$files = Get-ChildItem -Path $CommitsDir -Filter "*.md" |
|
|
95
|
+
Where-Object { $_.Name -ne "00-index.md" } |
|
|
96
|
+
Sort-Object Name -Descending
|
|
97
|
+
$header = $INDEX_HEADER_TEMPLATE.Replace('__SLUG__', $Slug)
|
|
98
|
+
$sb = New-Object System.Text.StringBuilder
|
|
99
|
+
[void]$sb.AppendLine($header)
|
|
100
|
+
[void]$sb.AppendLine("| File | Size | Modified |")
|
|
101
|
+
[void]$sb.AppendLine("|------|------|----------|")
|
|
102
|
+
foreach ($f in $files) {
|
|
103
|
+
$size = $f.Length
|
|
104
|
+
$mtime = $f.LastWriteTime.ToString("yyyy-MM-dd HH:mm")
|
|
105
|
+
[void]$sb.AppendLine("| [$($f.Name)](./$($f.Name)) | $size | $mtime |")
|
|
106
|
+
}
|
|
107
|
+
[void]$sb.AppendLine("")
|
|
108
|
+
[void]$sb.AppendLine("---")
|
|
109
|
+
[void]$sb.AppendLine("")
|
|
110
|
+
[void]$sb.AppendLine("## Auto Update")
|
|
111
|
+
[void]$sb.AppendLine("")
|
|
112
|
+
[void]$sb.AppendLine("Run daily at 08:00: ``project-knowledge-base\scripts\gen-commit-doc.ps1 -ProjectSlug $Slug``")
|
|
113
|
+
[void]$sb.AppendLine("")
|
|
114
|
+
$indexPath = Join-Path $CommitsDir "00-index.md"
|
|
115
|
+
Set-Content -Path $indexPath -Value $sb.ToString() -Encoding UTF8
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function Process-Project([string]$Slug, [object]$Project) {
|
|
119
|
+
$gitPath = $Project.gitPath
|
|
120
|
+
$commitsDir = Join-Path $KbRoot "projects\$Slug\commits"
|
|
121
|
+
if (-not (Test-Path $commitsDir)) {
|
|
122
|
+
New-Item -ItemType Directory -Force -Path $commitsDir | Out-Null
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
Write-Host ""
|
|
126
|
+
Write-Host "=== $Slug ==="
|
|
127
|
+
Write-Host " git: $gitPath"
|
|
128
|
+
|
|
129
|
+
$logArgs = @("log", "--no-merges", "--pretty=format:%h|%ad|%an|%s%n", "--date=short")
|
|
130
|
+
if ($MaxCommits -gt 0) { $logArgs += @("-n", "$MaxCommits") }
|
|
131
|
+
$rawOutput = & git -C $gitPath @logArgs 2>&1
|
|
132
|
+
if ($LASTEXITCODE -ne 0) { Write-Warning "git log failed: $rawOutput"; return }
|
|
133
|
+
if ($rawOutput -is [array]) { $rawLog = ($rawOutput -join "`n") } else { $rawLog = [string]$rawOutput }
|
|
134
|
+
if ([string]::IsNullOrWhiteSpace($rawLog)) { Write-Host " (no commits)"; return }
|
|
135
|
+
|
|
136
|
+
$commits = @($rawLog -split "`n" | Where-Object { $_ -match '\|' })
|
|
137
|
+
$newCount = 0; $updatedCount = 0; $skippedCount = 0
|
|
138
|
+
|
|
139
|
+
$existingMap = @{}
|
|
140
|
+
Get-ChildItem -Path $commitsDir -Filter "*.md" | ForEach-Object {
|
|
141
|
+
$name = $_.BaseName
|
|
142
|
+
if ($name -match '_(.+)$') { $slugKey = $Matches[1] } else { $slugKey = $name }
|
|
143
|
+
$existingMap[$slugKey] = $_.FullName
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
foreach ($line in $commits) {
|
|
147
|
+
$parts = $line -split '\|', 4
|
|
148
|
+
if ($parts.Count -lt 4) { continue }
|
|
149
|
+
$hash, $date, $author, $subject = $parts
|
|
150
|
+
$type = Get-CommitType $subject
|
|
151
|
+
if (-not (Test-CommitWriteable $type)) { $skippedCount++; continue }
|
|
152
|
+
$slug = Get-FeatureSlug $subject
|
|
153
|
+
$shortHash = $hash.Substring(0, [Math]::Min(7, $hash.Length))
|
|
154
|
+
$filename = "${date}_${shortHash}_${slug}.md"
|
|
155
|
+
$filepath = Join-Path $commitsDir $filename
|
|
156
|
+
|
|
157
|
+
if (Test-Path $filepath) { $existingMap[$slug] = $filepath; continue }
|
|
158
|
+
if ($existingMap.ContainsKey($slug)) {
|
|
159
|
+
$entry = New-UpdateEntry $date $shortHash $subject $type $author
|
|
160
|
+
Add-Content -Path $existingMap[$slug] -Value $entry -Encoding UTF8
|
|
161
|
+
$updatedCount++
|
|
162
|
+
} else {
|
|
163
|
+
$body = New-FeatureDocFromTemplate $Slug $slug $shortHash $date $author $subject $type
|
|
164
|
+
Set-Content -Path $filepath -Value $body -Encoding UTF8
|
|
165
|
+
$newCount++
|
|
166
|
+
$existingMap[$slug] = $filepath
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
Write-Host " new: $newCount, updated: $updatedCount, skipped: $skippedCount"
|
|
171
|
+
Rebuild-Index $Slug $commitsDir
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
foreach ($slug in $slugs) {
|
|
175
|
+
Process-Project $slug $projects.$slug
|
|
176
|
+
}
|
|
177
|
+
Write-Host ""
|
|
178
|
+
Write-Host "Done."
|