docs: 按照开发规范重构项目,完成七个环节文档整理与.xagent流程搭建

This commit is contained in:
2026-07-09 10:31:36 +08:00
parent 61b6ecb55d
commit 17a90ff7dc
21 changed files with 1546 additions and 0 deletions

107
.xagent/check-dirty.py Normal file
View File

@@ -0,0 +1,107 @@
import os
import sys
import json
import subprocess
import time
# Force stdout to be UTF-8 for Windows terminals
if hasattr(sys.stdout, 'reconfigure'):
sys.stdout.reconfigure(encoding='utf-8')
NOW = time.time()
def get_git_timestamp(filepath):
# Normalize paths to use forward slashes for git compatibility
git_path = filepath.replace('\\', '/')
# 1. Check if the file exists on disk.
if not os.path.exists(filepath):
return 0
# 2. Check for local modifications (staged or unstaged)
try:
status_proc = subprocess.run(
['git', 'status', '--porcelain', '--', git_path],
capture_output=True, text=True, check=True
)
if status_proc.stdout.strip():
# Locally modified files are considered "edited just now"
return NOW
except subprocess.SubprocessError:
pass
# 3. Get the last commit timestamp
try:
log_proc = subprocess.run(
['git', 'log', '-1', '--format=%ct', '--', git_path],
capture_output=True, text=True, check=True
)
output = log_proc.stdout.strip()
if output.isdigit():
return int(output)
except subprocess.SubprocessError:
pass
# 4. Fallback to OS modification time if file exists but has no git commits yet
try:
return os.path.getmtime(filepath)
except OSError:
return 0
def check_dirty():
graph_path = os.path.join(os.path.dirname(__file__), 'graph.json')
if not os.path.exists(graph_path):
print(f"Error: graph.json not found at {graph_path}")
sys.exit(1)
with open(graph_path, 'r', encoding='utf-8') as f:
graph = json.load(f)
files_dict = graph.get('files', {})
timestamps = {}
# Calculate timestamps for all files
for filepath in files_dict.keys():
timestamps[filepath] = get_git_timestamp(filepath)
dirty_files = {}
# Check dependencies
for filepath, info in files_dict.items():
depends_on = info.get('depends_on', [])
file_ts = timestamps[filepath]
# If the file itself doesn't exist, it's not "dirty" in the sense of being out of date,
# but it is missing. However, to keep it clean, if it doesn't exist, we can skip or flag it.
# Let's say if a file exists, we check if its dependencies are newer.
if file_ts == 0:
# File is missing on disk
continue
file_dirty_deps = []
for dep in depends_on:
dep_ts = get_git_timestamp(dep)
if dep_ts > file_ts:
file_dirty_deps.append((dep, dep_ts, file_ts))
if file_dirty_deps:
dirty_files[filepath] = file_dirty_deps
if dirty_files:
print("\n[DIRTY] 发现以下下游文件落后于其上游依赖:")
for filepath, deps in dirty_files.items():
print(f"\n* {filepath}")
for dep, dep_ts, file_ts in deps:
dep_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(dep_ts))
file_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(file_ts))
print(f" [依赖于] {dep}")
print(f" (上游最后提交: {dep_time} > 下游最后提交: {file_time})")
print("\n请跟进并更新上述下游文件,重新 commit 即可刷新基线并自动消脏。\n")
return False
else:
print("\n[CLEAN] 所有文件均已与依赖同步,无脏文件。\n")
return True
if __name__ == '__main__':
success = check_dirty()
sys.exit(0 if success else 1)

4
.xagent/check-dirty.sh Normal file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
# XAgent 判脏检测脚本 - 自动通过 git 历史对比最后提交时间
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
python "$SCRIPT_DIR/check-dirty.py"

145
.xagent/graph.json Normal file
View File

@@ -0,0 +1,145 @@
{
"files": {
"Docs/10_requirements-overview/main.md": {
"depends_on": [],
"lock": null
},
"Docs/20_requirements-detail/main.md": {
"depends_on": [
"Docs/10_requirements-overview/main.md"
],
"lock": null
},
"Docs/30_architecture/main.md": {
"depends_on": [
"Docs/20_requirements-detail/main.md"
],
"lock": null
},
"Docs/40_ui-design/main.md": {
"depends_on": [
"Docs/20_requirements-detail/main.md"
],
"lock": null
},
"Docs/50_module-breakdown/mod-sys.md": {
"depends_on": [
"Docs/30_architecture/main.md",
"Docs/40_ui-design/main.md"
],
"lock": null
},
"Docs/50_module-breakdown/mod-lcd.md": {
"depends_on": [
"Docs/30_architecture/main.md",
"Docs/40_ui-design/main.md"
],
"lock": null
},
"Docs/50_module-breakdown/mod-rf.md": {
"depends_on": [
"Docs/30_architecture/main.md",
"Docs/40_ui-design/main.md"
],
"lock": null
},
"Docs/50_module-breakdown/mod-power.md": {
"depends_on": [
"Docs/30_architecture/main.md",
"Docs/40_ui-design/main.md"
],
"lock": null
},
"Docs/50_module-breakdown/mod-key.md": {
"depends_on": [
"Docs/30_architecture/main.md",
"Docs/40_ui-design/main.md"
],
"lock": null
},
"Docs/60_coding/mod-sys.md": {
"depends_on": [
"Docs/50_module-breakdown/mod-sys.md"
],
"lock": null
},
"Docs/60_coding/mod-lcd.md": {
"depends_on": [
"Docs/50_module-breakdown/mod-lcd.md"
],
"lock": null
},
"Docs/60_coding/mod-rf.md": {
"depends_on": [
"Docs/50_module-breakdown/mod-rf.md"
],
"lock": null
},
"Docs/60_coding/mod-power.md": {
"depends_on": [
"Docs/50_module-breakdown/mod-power.md"
],
"lock": null
},
"Docs/60_coding/mod-key.md": {
"depends_on": [
"Docs/50_module-breakdown/mod-key.md"
],
"lock": null
},
"App/main.c": {
"depends_on": [
"Docs/60_coding/mod-sys.md",
"Docs/60_coding/mod-key.md",
"Docs/60_coding/mod-power.md"
],
"lock": null
},
"App/config.h": {
"depends_on": [
"Docs/60_coding/mod-sys.md"
],
"lock": null
},
"Drivers/lcd.c": {
"depends_on": [
"Docs/60_coding/mod-lcd.md"
],
"lock": null
},
"Drivers/lcd.h": {
"depends_on": [
"Docs/60_coding/mod-lcd.md"
],
"lock": null
},
"Drivers/lcd_font.h": {
"depends_on": [
"Docs/60_coding/mod-lcd.md"
],
"lock": null
},
"Drivers/rf.c": {
"depends_on": [
"Docs/60_coding/mod-rf.md"
],
"lock": null
},
"Drivers/rf.h": {
"depends_on": [
"Docs/60_coding/mod-rf.md"
],
"lock": null
},
"Docs/70_testing/main.md": {
"depends_on": [
"Docs/60_coding/mod-sys.md",
"Docs/60_coding/mod-lcd.md",
"Docs/60_coding/mod-rf.md",
"Docs/60_coding/mod-power.md",
"Docs/60_coding/mod-key.md"
],
"lock": null
}
}
}

65
.xagent/stages.json Normal file
View File

@@ -0,0 +1,65 @@
{
"stages": [
{
"id": "10_requirements-overview",
"path": "Docs/10_requirements-overview",
"role": "需求分析师",
"type": "single_document",
"default_depends_on": []
},
{
"id": "20_requirements-detail",
"path": "Docs/20_requirements-detail",
"role": "需求分析师",
"type": "single_document",
"default_depends_on": [
"Docs/10_requirements-overview/main.md"
]
},
{
"id": "30_architecture",
"path": "Docs/30_architecture",
"role": "架构师",
"type": "single_document",
"default_depends_on": [
"Docs/20_requirements-detail/main.md"
]
},
{
"id": "40_ui-design",
"path": "Docs/40_ui-design",
"role": "UI设计师",
"type": "single_document",
"default_depends_on": [
"Docs/20_requirements-detail/main.md"
]
},
{
"id": "50_module-breakdown",
"path": "Docs/50_module-breakdown",
"role": "架构师",
"type": "parallel_tasks",
"default_depends_on": [
"Docs/30_architecture/main.md",
"Docs/40_ui-design/main.md"
]
},
{
"id": "60_coding",
"path": "Docs/60_coding",
"role": "开发者",
"type": "parallel_tasks",
"default_depends_on": [],
"link_same_name_from": "Docs/50_module-breakdown"
},
{
"id": "70_testing",
"path": "Docs/70_testing",
"role": "测试工程师",
"type": "single_document",
"default_depends_on": [
"Docs/60_coding"
]
}
]
}