docs: 按照开发规范重构项目,完成七个环节文档整理与.xagent流程搭建
This commit is contained in:
107
.xagent/check-dirty.py
Normal file
107
.xagent/check-dirty.py
Normal 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
4
.xagent/check-dirty.sh
Normal 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
145
.xagent/graph.json
Normal 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
65
.xagent/stages.json
Normal 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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user