chore(agent): 新增变更日志/checkpoint skill 与 post-commit hook

- AGENTS.md 新增变更日志 Skill 规范:变更后增量更新 document/work-log,更新前先做 Git 拉取检查
- 新增 .codex/skills 下 agent-change-log、git-checkpoint-commit 两个 skill
- 新增 .githooks/post-commit 与 tools/agent-change-log 自动化脚本,提供提交后最低限度日志追加
This commit is contained in:
caoxiaozhu
2026-06-24 10:41:56 +08:00
parent 93212600eb
commit d4ff79f326
11 changed files with 735 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
---
name: agent-change-log
description: Use when working in X-Financial after bug fixes, new features, refactors, behavior changes, documentation edits, config edits, concurrent-agent Git commits, or any codebase modification that should leave an incremental human-readable work log.
---
# Agent Change Log
## Overview
This skill keeps a living, incremental work log for X-Financial. After each meaningful modification batch, write down what changed locally, what other agents already committed upstream, what was operated on, what remains uncertain, and what should happen next.
The log should sound like a careful teammate writing for tomorrow's teammate: concrete, warm, and honest.
## When To Use
- After fixing a bug, adding a feature, refactoring, changing behavior, editing documentation, or changing config.
- Before the final response for any turn that changed files.
- Before updating the log in a branch where other agents may have pushed commits.
- After verification, so the entry can include what was actually checked.
- When a failed attempt changed files, generated artifacts, or revealed a risk worth preserving.
Do not use for read-only investigation with no file changes unless the user explicitly asks for a record.
## Log Location
Use one file per day:
```text
document/work-log/YYYY-MM-DD.md
```
If the file does not exist, create it. If it exists, incrementally update it. Never replace the whole file just to add a new entry.
## Required Structure
Each daily file must use these sections in this order:
```markdown
# YYYY-MM-DD 工作日志
## 当日工作内容
## 遗留问题
## TODO
```
Keep this order stable. Add entries under the existing headings instead of creating duplicate headings.
## Required Git Check
Before writing or updating the daily log, manually check Git for upstream and local-ahead commits from other agents.
Run:
```bash
date '+%Y-%m-%d %H:%M:%S %Z'
git fetch --all --prune
git status -sb
git rev-parse --abbrev-ref --symbolic-full-name @{u}
git log --oneline --decorate --max-count=20 HEAD..@{u}
git log --oneline --decorate --max-count=20 @{u}..HEAD
```
Rules:
- Treat `git fetch --all --prune` as the default safe "pull check"; it updates remote refs without merging into a dirty worktree.
- Treat `HEAD..@{u}` as upstream commits not yet in local history.
- Treat `@{u}..HEAD` as local commits not yet in upstream history; these may also come from another agent working in the same checkout.
- If the worktree is clean and the branch is only behind upstream, `git pull --ff-only` may be used to fast-forward before analysis.
- If the worktree is dirty, diverged, or likely to conflict, do not merge/rebase automatically. Record the upstream commits from `HEAD..@{u}` and the blocker in `遗留问题`.
- If there is no upstream branch, record that fact in `遗留问题` and continue with local-only logging.
- When `HEAD..@{u}` has commits, summarize those commits under `当日工作内容` before describing local edits. Mention commit hash, subject, and inferred impact.
- When `@{u}..HEAD` has commits that were not created in the current task, summarize them too, because another local agent may have committed without pushing yet.
- When no upstream or local-ahead commits exist, still record "Git 提交检查:未发现 upstream 新提交或本地 ahead 新提交" in the work entry.
## Entry Rules
1. Get the current local time first:
```bash
date '+%Y-%m-%d %H:%M:%S %Z'
```
2. Run the required Git check and capture whether upstream or local-ahead has new commits.
3. Append a new timestamped bullet under `## 当日工作内容`.
4. Mention Git commits, changed files or modules, the operation, the intent, and the verification result.
5. Add or update `## 遗留问题` whenever there is risk, uncertainty, skipped verification, design debt, Git divergence, or a likely follow-up.
6. Add or update `## TODO` with checkbox items.
7. When a TODO is completed, keep it in place and mark it as:
```markdown
- [x] ~~任务内容~~(完成于 HH:MM证据...
```
## Writing Style
- Write in Simplified Chinese.
- Be specific and a little human: "我把...", "这次先...", "还需要留意..." are good.
- Keep the tone factual. Do not turn the log into a victory lap.
- Prefer concise file names and module names in prose, but include enough context to find the change.
- Work content should be detailed enough that a future agent can continue without asking "你到底改了啥?"
- Leftover issues should include a suggested next step, not only a complaint.
## Work Content Template
```markdown
- HH:MM我完成了 <本次修改目标>。
- Git 提交检查:<git fetch 后 HEAD..upstream 与 upstream..HEAD 的结果;没有就写未发现 upstream 或本地 ahead 新提交>。
- 修改:<文件/模块><做了什么>。
- 操作:<运行了什么命令、迁移了什么状态、重启了什么服务等>。
- 验证:<测试/构建/检查结果;如果没跑,说明原因>。
- 影响:<用户可见变化或工程边界变化>。
```
## Leftover Issue Template
```markdown
- HH:MM<遗留问题或风险>。建议下一步 <具体建议>。
```
## TODO Template
```markdown
- [ ] <下一步动作>来源HH:MM <上下文>
- [x] ~~<已完成动作>~~(完成于 HH:MM证据<命令/文件/结果>
```
## Final Response Checklist
Before saying work is complete:
- Today's `document/work-log/YYYY-MM-DD.md` exists.
- The Git check ran, and upstream plus local-ahead commits from other agents were summarized or explicitly marked as absent.
- `当日工作内容` includes this modification batch.
- `遗留问题` is either updated with real risks or explicitly says no new leftover issue for this batch.
- `TODO` contains new follow-ups and completed items are checked with evidence.
- The final response mentions that the work log was updated.

View File

@@ -0,0 +1,4 @@
interface:
display_name: "Agent Change Log"
short_description: "Record X-Financial changes plus upstream/local commits in the daily work log"
default_prompt: "Use $agent-change-log after a bug fix, feature, refactor, upstream/local commit check, or other file modification to update document/work-log/YYYY-MM-DD.md incrementally."

9
.githooks/post-commit Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/sh
# Auto-append a minimal X-Financial agent work-log entry after each commit.
repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
cd "$repo_root" || exit 0
python3 tools/agent-change-log/update_change_log.py \
--event "post-commit hook" \
>/tmp/x-financial-agent-change-log-hook.log 2>&1 || true

View File

@@ -5,6 +5,15 @@
- 所有分析、解释、计划、提交说明和最终回复默认使用简体中文。
- 技术结论要直击重点,必要时给出可验证的文件、命令或测试结果。
## 变更日志 Skill 规范
- 每次修复 bug、新增功能、重构、修改配置或编辑项目文档后必须调用项目级 Skill `agent-change-log`,并增量更新 `document/work-log/YYYY-MM-DD.md`
- 更新日志前必须先执行 Git 拉取检查:默认运行 `git fetch --all --prune``git status -sb``git log HEAD..@{u}``git log @{u}..HEAD`;如果工作区干净且只落后上游,可以 `git pull --ff-only`。发现其他智能体已提交到上游或本地 ahead 提交时,要先把这些提交摘要分析进当天日志。
- 自动化触发由 `tools/agent-change-log/update_change_log.py``.githooks/post-commit` 提供;新 checkout 需要执行 `tools/agent-change-log/install_post_commit_hook.sh` 安装到本地 `.git/hooks/post-commit` 后,提交后才会自动追加最低限度日志。
- 如果当前环境无法写 `.git` 或无法执行 `git fetch`,必须把这个限制写进当天日志;不能把 Skill/AGENTS 规则误当成已经有后台自动化。
- 日志必须保留 `当日工作内容``遗留问题``TODO` 三块TODO 使用 Markdown checkbox完成项勾选并用删除线保留历史。
- 记录要写清楚具体时间、改了什么、操作了什么、验证了什么、还遗留什么问题,以及建议下一步怎么处理。
## 通用代码拆分规范
无论写前端、后端还是算法代码,都必须主动避免“所有方法堆在一个类里 / 一个组件里 / 一个模块里”的写法。遇到类、组件或核心模块持续变大时,优先按职责拆分,而不是继续追加方法和状态。

View File

@@ -0,0 +1,109 @@
---
name: write-development-docs
description: 为 X-Financial 落地开发文档。Use when a task asks to write, create, update, or standardize planning/development documentation under document/development, especially when the expected output is a CONCEPT.md capability document plus a TODO.md implementation checklist following this repository's existing development-doc format.
---
# Write Development Docs
## 目标
使用本技能为一个功能、重构、算法、页面或业务能力创建标准开发文档。
默认输出位置:
```text
document/development/<feature-slug>/
├── CONCEPT.md
└── TODO.md
```
## 工作流
1. 先阅读相邻或同类文档,优先参考 `document/development/*/CONCEPT.md``document/development/*/TODO.md`
2. 读取与本次能力相关的代码、接口、页面、测试或历史文档,不凭空写实现细节。
3. 创建或更新 `CONCEPT.md`,先写清能力边界,再写方案和验收。
4. 创建或更新 `TODO.md`,每个任务必须能回链到 `CONCEPT.md` 的章节。
5. 如果已有实现TODO 可以勾选 `[x]`,但必须写证据;没有验证的任务保持 `[ ]`
6. 完成后检查两份文档是否互相一致,避免 TODO 承诺了 CONCEPT 没定义的能力。
## 命名规则
- 目录名使用小写 kebab-case例如 `receipt-folder``agent-trace-center`
- 能力名在正文中使用清晰中文例如“票据夹功能”“Agent 链路追踪中心”。
- 两个文件名固定为 `CONCEPT.md``TODO.md`
- 不额外创建 README、CHANGELOG、SUMMARY 等文件,除非用户明确要求。
## CONCEPT.md 格式
参考 `assets/CONCEPT.md` 模板。
必须包含:
- 标题:`# <功能名> 概念文档`
- `更新时间YYYY-MM-DD`
- `## 功能一句话`
- `## 背景与问题`
- `## 目标与非目标`
- `## 用户与场景`
- `## 功能能力`
- `## 方案设计`
- `## 算法与公式`
- `## 测试方案`
- `## 指标与验收`
- `## 风险与开放问题`
如任务已经实现或正在收口,可追加:
- `## 本轮实现记录`
写法要求:
- 先讲业务问题和边界,再讲技术方案。
- 目标与非目标必须分开写,避免需求无限扩张。
- 方案设计按前端、后端、算法/规则、数据、权限、降级策略分块;没有的块可以写“当前不涉及”。
- 算法与公式必须明确“不涉及”或写出公式、变量说明和适用边界。
- 验收标准要可验证,不写空泛口号。
## TODO.md 格式
参考 `assets/TODO.md` 模板。
必须包含:
- 标题:`# <功能名> 开发 TODO`
- `更新时间YYYY-MM-DD`
- `## 使用规则`
- 分阶段 checklist。
TODO 条目规则:
- 每条用 `- [ ]``- [x]`
- 每条必须包含 `[CONCEPT: <章节名>]`
- 已完成项必须补证据,格式为 `证据:<文件、接口、命令或验证结果>`
- 没有真实证据时不得勾选 `[x]`
- 阶段建议使用:
- `## 1. 调研与边界`
- `## 2. 契约与设计`
- `## 3. 后端实现`
- `## 4. 算法/规则实现`
- `## 5. 前端实现`
- `## 6. 测试与验证`
- `## 7. 文档收尾`
## 更新既有文档
更新已有 `CONCEPT.md` / `TODO.md` 时:
- 先读两份文件的全文。
- 新需求先补 `CONCEPT.md`,再补 `TODO.md`
- 如果实现发生变化,同步更新“非目标”“风险与开放问题”“本轮实现记录”。
- 不删除历史证据;除非证据已经明显错误,才替换为新证据。
## 验收检查
交付前检查:
- `CONCEPT.md``TODO.md` 都存在。
- TODO 的 `[CONCEPT: ...]` 都能在 `CONCEPT.md` 找到对应章节或语义段落。
- 已勾选项都有证据。
- 文档没有遗留模板占位符,例如 `<功能名>``TODO``待补充`
- 文档路径位于 `document/development/<feature-slug>/`

View File

@@ -0,0 +1,4 @@
interface:
display_name: "开发文档落地"
short_description: "按项目格式生成 CONCEPT 和 TODO 文档"
default_prompt: "Use $write-development-docs to create a development CONCEPT and TODO document for a new X-Financial feature."

View File

@@ -0,0 +1,132 @@
# <功能名> 概念文档
更新时间:<YYYY-MM-DD>
## 功能一句话
用一句话说明这个能力解决什么问题、服务谁、交付什么结果。
## 背景与问题
- 当前现状:
- 用户痛点:
- 业务影响:
- 为什么现在需要做:
## 目标与非目标
### 目标
- [G1]
- [G2]
- [G3]
### 非目标
- [NG1] 本轮不做:
- [NG2] 本轮不改变:
- [NG3] 后续再评估:
## 用户与场景
- 目标用户:
- 使用入口:
- 核心场景:
1.
2.
3.
- 异常场景:
-
## 功能能力
- [C1] 输入能力:
- [C2] 处理能力:
- [C3] 输出能力:
- [C4] 状态与权限:
- [C5] 边界与降级:
## 方案设计
### 前端
- 页面/组件:
- 交互状态:
- 展示规则:
- 降级处理:
### 后端
- 接口/服务:
- 权限与校验:
- 持久化:
- 降级处理:
### 算法与规则
- 输入:
- 流程:
- 输出:
- 解释:
### 数据与契约
- 核心字段:
- 状态枚举:
- 兼容策略:
- 版本/审计:
## 算法与公式
如果当前功能不涉及公式,写明:
当前功能不涉及显式数学公式。
如果涉及公式,使用如下格式:
$$
metric = input_a + input_b
$$
变量说明:
- $metric$
- $input_a$
- $input_b$
## 测试方案
后端:
-
前端:
-
集成:
-
手工验证:
-
## 指标与验收
- [A1] 功能验收:
- [A2] 性能指标:
- [A3] 质量指标:
- [A4] 安全/权限指标:
- [A5] 可观测性:
## 风险与开放问题
- 风险:
- 已处理依赖:
- 待确认:
- 降级策略:
## 本轮实现记录
-

View File

@@ -0,0 +1,71 @@
# <功能名> 开发 TODO
更新时间:<YYYY-MM-DD>
## 使用规则
- 每个 TODO 必须对应 `CONCEPT.md` 中的目标、能力、方案或验收点。
- 只有完成并验证后,才能把 `[ ]` 改成 `[x]`
- 勾选时在任务后补充简短证据,例如文件、接口、命令或验证结果。
- 如果需求发生变化,先更新 `CONCEPT.md`,再调整本 TODO。
## 1. 调研与边界
- [ ] [CONCEPT: 背景与问题] 阅读相关页面、接口、服务、测试和历史文档,记录当前实现事实。
证据:
- [ ] [CONCEPT: 目标与非目标] 确认本轮开发范围,写清楚不做项。
证据:
- [ ] [CONCEPT: 风险与开放问题] 标记无法立即确认的依赖、风险和假设。
证据:
## 2. 契约与设计
- [ ] [CONCEPT: 功能能力] 定义输入、输出、状态、权限和边界条件。
证据:
- [ ] [CONCEPT: 方案设计] 明确前端、后端、算法、数据的职责边界。
证据:
- [ ] [CONCEPT: 算法与公式] 补全公式、变量解释或明确当前不涉及公式。
证据:
- [ ] [CONCEPT: 指标与验收] 把验收标准转成可验证的检查点。
证据:
## 3. 后端实现
- [ ] [CONCEPT: 后端] 新增或调整 schema、service、endpoint、权限和持久化逻辑。
证据:
- [ ] [CONCEPT: 数据与契约] 保持响应结构、状态枚举和兼容策略清晰。
证据:
## 4. 算法/规则实现
- [ ] [CONCEPT: 算法与规则] 实现核心处理流程、规则判断或计算逻辑。
证据:
- [ ] [CONCEPT: 结果解释] 输出可读解释、证据链、贡献项或降级原因。
证据:
## 5. 前端实现
- [ ] [CONCEPT: 前端] 新增或调整页面、组件、服务 API 和视图模型。
证据:
- [ ] [CONCEPT: 前端] 实现加载、空态、错误态、权限态和刷新。
证据:
- [ ] [CONCEPT: 前端] 对齐现有企业级直角、低饱和、密集信息风格。
证据:
## 6. 测试与验证
- [ ] [CONCEPT: 测试方案] 补充后端 service/API 定向测试,容器内运行,超时控制在 60s 内。
证据:
- [ ] [CONCEPT: 测试方案] 补充前端视图模型、路由、组件或构建验证。
证据:
- [ ] [CONCEPT: 指标与验收] 记录验证命令、结果和未覆盖风险。
证据:
## 7. 文档收尾
- [ ] [CONCEPT: 指标与验收] 回看所有验收点,确认均有实现或验证证据。
证据:
- [ ] [CONCEPT: 风险与开放问题] 更新剩余风险、后续任务和明确不做项。
证据:
- [ ] [CONCEPT: 功能一句话] 确认最终实现没有偏离原始目标。
证据:

View File

@@ -0,0 +1,22 @@
# Agent Change Log Automation
这个目录提供 `agent-change-log` 的可执行部分。
## 手动写入一条日志
```bash
python3 tools/agent-change-log/update_change_log.py --event "manual"
```
## 安装提交后自动日志 hook
```bash
tools/agent-change-log/install_post_commit_hook.sh
```
安装后,每次 `git commit` 完成,`.githooks/post-commit` 会调用
`update_change_log.py`把最近提交、Git 双向提交检查和触发时间追加到
`document/work-log/YYYY-MM-DD.md`
注意hook 只能覆盖“提交后自动记录”。没有提交的普通文件修改,仍需要执行代理在任务完成前按
`AGENTS.md``agent-change-log` 主动记录。

View File

@@ -0,0 +1,13 @@
#!/bin/sh
# Install the versioned agent change-log post-commit hook into this checkout.
set -eu
repo_root="$(git rev-parse --show-toplevel)"
hook_path="$(git rev-parse --git-path hooks/post-commit)"
mkdir -p "$(dirname "$hook_path")"
cp "$repo_root/.githooks/post-commit" "$hook_path"
chmod +x "$hook_path"
printf 'installed_hook=%s\n' "$hook_path"

View File

@@ -0,0 +1,227 @@
#!/usr/bin/env python3
"""Append an incremental X-Financial agent work-log entry."""
from __future__ import annotations
import argparse
import os
import subprocess
import sys
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
SECTION_WORK = "## 当日工作内容"
SECTION_ISSUES = "## 遗留问题"
SECTION_TODO = "## TODO"
@dataclass
class CommandResult:
args: list[str]
returncode: int
stdout: str
stderr: str
@property
def ok(self) -> bool:
return self.returncode == 0
@property
def message(self) -> str:
return (self.stderr.strip() or self.stdout.strip()).strip()
def run(args: list[str], cwd: Path, timeout: int = 60) -> CommandResult:
try:
result = subprocess.run(
args,
cwd=cwd,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
timeout=timeout,
)
except subprocess.TimeoutExpired as exc:
return CommandResult(args, 124, exc.stdout or "", exc.stderr or "command timed out")
return CommandResult(args, result.returncode, result.stdout, result.stderr)
def git(args: list[str], cwd: Path, timeout: int = 60) -> CommandResult:
return run(["git", *args], cwd, timeout=timeout)
def repo_root() -> Path:
result = run(["git", "rev-parse", "--show-toplevel"], Path.cwd())
if not result.ok:
raise SystemExit(f"Not inside a git repository: {result.message}")
return Path(result.stdout.strip())
def one_line(value: str) -> str:
return " ".join(value.strip().split())
def indent_block(text: str, limit: int = 8) -> list[str]:
lines = [line.strip() for line in text.splitlines() if line.strip()]
if not lines:
return ["未发现"]
shown = lines[:limit]
if len(lines) > limit:
shown.append(f"... 另有 {len(lines) - limit}")
return shown
def ensure_document(path: Path, date_text: str) -> str:
if path.exists():
content = path.read_text(encoding="utf-8")
else:
content = f"# {date_text} 工作日志\n\n{SECTION_WORK}\n\n{SECTION_ISSUES}\n\n{SECTION_TODO}\n"
if not content.endswith("\n"):
content += "\n"
for heading in (SECTION_WORK, SECTION_ISSUES, SECTION_TODO):
if heading not in content:
content += f"\n{heading}\n"
return content
def insert_under_section(content: str, section: str, entry: str) -> str:
marker = f"{section}\n"
start = content.find(marker)
if start == -1:
return content.rstrip() + f"\n\n{section}\n\n{entry.rstrip()}\n"
insert_at = start + len(marker)
next_section = content.find("\n## ", insert_at)
entry_text = "\n" + entry.rstrip() + "\n"
if next_section == -1:
return content[:insert_at].rstrip() + entry_text
before = content[:next_section].rstrip()
after = content[next_section:]
return before + entry_text + after
def collect_git(root: Path, *, no_fetch: bool, max_commits: int) -> dict[str, str | bool]:
data: dict[str, str | bool] = {}
fetch_result = CommandResult(["git", "fetch"], 0, "", "")
if no_fetch:
data["fetch"] = "已跳过 fetch--no-fetch"
else:
fetch_result = git(["fetch", "--all", "--prune"], root, timeout=60)
data["fetch"] = "成功" if fetch_result.ok else f"失败:{one_line(fetch_result.message)}"
status = git(["status", "-sb"], root)
data["status"] = status.stdout.strip() if status.ok else f"读取失败:{one_line(status.message)}"
upstream = git(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], root)
if upstream.ok:
data["upstream"] = upstream.stdout.strip()
behind = git(["log", "--oneline", "--decorate", f"--max-count={max_commits}", "HEAD..@{u}"], root)
ahead = git(["log", "--oneline", "--decorate", f"--max-count={max_commits}", "@{u}..HEAD"], root)
data["behind"] = behind.stdout.strip() if behind.ok else f"读取失败:{one_line(behind.message)}"
data["ahead"] = ahead.stdout.strip() if ahead.ok else f"读取失败:{one_line(ahead.message)}"
else:
data["upstream"] = ""
data["behind"] = "无 upstream 分支"
data["ahead"] = "无 upstream 分支"
head_hash = git(["rev-parse", "--short", "HEAD"], root)
head_subject = git(["show", "-s", "--format=%s", "HEAD"], root)
data["head_hash"] = head_hash.stdout.strip() if head_hash.ok else ""
data["head_subject"] = head_subject.stdout.strip() if head_subject.ok else ""
stat = git(["show", "--stat", "--oneline", "--decorate", "--max-count=1", "HEAD"], root)
data["head_stat"] = stat.stdout.strip() if stat.ok else ""
data["fetch_failed"] = not fetch_result.ok
return data
def build_work_entry(now: datetime, event: str, git_data: dict[str, str | bool]) -> str:
time_text = now.strftime("%H:%M")
head_hash = str(git_data.get("head_hash") or "")
head_subject = str(git_data.get("head_subject") or "")
behind = str(git_data.get("behind") or "")
ahead = str(git_data.get("ahead") or "")
behind_lines = indent_block(behind)
ahead_lines = indent_block(ahead)
behind_text = "".join(behind_lines)
ahead_text = "".join(ahead_lines)
marker = f"auto-log:{head_hash}" if head_hash else "auto-log:no-head"
return "\n".join(
[
f"- {time_text}:自动记录 `{head_hash}` 提交后的工作日志。({marker}",
f" - Git 提交检查fetch {git_data.get('fetch')}upstream `{git_data.get('upstream') or '未配置'}`upstream 新提交:{behind_text};本地 ahead 提交:{ahead_text}",
f" - 修改:最近提交为 `{head_hash} {head_subject}`。",
f" - 操作:{event} 触发 `tools/agent-change-log/update_change_log.py`,自动读取 Git 状态并写入当天日志。",
" - 验证:自动脚本只记录提交和 Git 状态,不替代业务测试;业务验证仍以本次任务实际运行结果为准。",
" - 影响:提交后即使执行代理忘记手动写日志,也会留下最低限度的时间、提交和分支状态记录。",
]
)
def build_issue_entry(now: datetime, git_data: dict[str, str | bool]) -> str | None:
time_text = now.strftime("%H:%M")
issues: list[str] = []
if git_data.get("fetch_failed"):
issues.append(f"fetch 未成功:{git_data.get('fetch')}")
if not git_data.get("upstream"):
issues.append("当前分支没有 upstream无法判断其他智能体是否已推送新提交")
if not issues:
return None
return f"- {time_text}:自动日志触发时发现 {''.join(issues)}。建议后续在有 Git 写权限和网络权限的环境里重新执行拉取检查。"
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--event", default="manual", help="Human-readable trigger source.")
parser.add_argument("--date", help="Override log date YYYY-MM-DD.")
parser.add_argument("--log-path", help="Override log file path.")
parser.add_argument("--dry-run", action="store_true", help="Print the entry without writing files.")
parser.add_argument("--force", action="store_true", help="Write even if this commit marker already exists.")
parser.add_argument("--no-fetch", action="store_true", help="Skip git fetch; useful for tests or offline hooks.")
parser.add_argument("--max-commits", type=int, default=20, help="Maximum commits to summarize per direction.")
return parser.parse_args()
def main() -> int:
args = parse_args()
root = repo_root()
now = datetime.now().astimezone()
date_text = args.date or now.strftime("%Y-%m-%d")
log_path = Path(args.log_path) if args.log_path else root / "document" / "work-log" / f"{date_text}.md"
git_data = collect_git(root, no_fetch=args.no_fetch, max_commits=args.max_commits)
entry = build_work_entry(now, args.event, git_data)
issue_entry = build_issue_entry(now, git_data)
marker = f"auto-log:{git_data.get('head_hash')}"
if args.dry_run:
print(entry)
if issue_entry:
print()
print(issue_entry)
return 0
content = ensure_document(log_path, date_text)
if marker in content and not args.force:
print(f"Work log already contains {marker}; skipping.")
return 0
content = insert_under_section(content, SECTION_WORK, entry)
if issue_entry:
content = insert_under_section(content, SECTION_ISSUES, issue_entry)
log_path.parent.mkdir(parents=True, exist_ok=True)
log_path.write_text(content, encoding="utf-8")
print(f"updated_log={os.path.relpath(log_path, root)}")
return 0
if __name__ == "__main__":
raise SystemExit(main())