Files
vibe-coding-cn/.github/extensions/sync-doc-toc.py
T
tradecatlabsandGitHub 0920cd4c59 refactor: maintenance - 平铺共享模块并移除lib目录 (#159)
* refactor: toolbox - 收口Context Skills Extensions三域资源

* fix: toolbox - 重定向文集中的嵌套标签链接

* fix: toolbox - 保全被ignore遮蔽的原跟踪事实文件

* refactor: toolbox - 退役workflows残留并平铺原定义

* docs: gongfa - 整理Lambda演算正文并冻结首发来源

* docs: gongfa - 登记Lambda演算天级上品并紧随拼好码

* docs: gongfa - 同步Lambda演算AI引用覆盖

* docs: readme - 移除工具箱怎么用整块

* refactor: skills - 删除16个官方引用仅保留自有入口

* refactor: extensions - 退役33个Skill脚本并清理调用

* refactor: maintenance - 迁移校验测试至.github维护层

* chore: extensions - 移除用户点名的八项程序与资料

* refactor: maintenance - 平铺共享模块并移除lib目录
2026-10-18 13:51:45 +08:00

171 lines
5.3 KiB
Python

#!/usr/bin/env python3
"""Regenerate collapsible fine-grained TOC blocks in docs linear README files."""
from __future__ import annotations
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
# 同一平铺维护模块由校验器和生成器共享,使用真实入口位置而非调用者cwd。
sys.path.insert(0, str(ROOT / '.github'))
from taxonomy import doc_readmes_from_taxonomy
SUMMARY_LINE = "<summary><strong>完整细粒度目录(点击展开/收起)</strong></summary>"
ANCHOR_PATTERN = re.compile(r"<a\s+id=[\"']([^\"']+)[\"']")
HEADING_PATTERN = re.compile(r"^(#{2,6})\s+(.+?)\s*#*\s*$")
def heading_after(lines: list[str], start: int) -> tuple[int, str] | None:
for line in lines[start + 1 : start + 8]:
stripped = line.strip()
if not stripped:
continue
match = HEADING_PATTERN.match(stripped)
if not match:
return None
return len(match.group(1)), match.group(2).strip()
return None
def title_from_anchor(anchor: str, main_anchors: list[str]) -> str:
for main_anchor in sorted(main_anchors, key=len, reverse=True):
if anchor == main_anchor:
return main_anchor
prefix = f"{main_anchor}-"
if anchor.startswith(prefix):
return anchor.removeprefix(prefix).replace("-", " ")
return anchor.replace("-", " ")
def collect_toc_entries(text: str, main_anchors: list[str]) -> list[tuple[int, str, str]]:
lines = text.splitlines()
required_prefixes = tuple(f"{anchor}-" for anchor in main_anchors)
entries: list[tuple[int, str, str]] = []
seen: set[str] = set()
for idx, line in enumerate(lines):
for anchor in ANCHOR_PATTERN.findall(line):
if anchor in seen:
continue
if anchor not in main_anchors and not anchor.startswith(required_prefixes):
continue
seen.add(anchor)
heading = heading_after(lines, idx)
if heading:
level, title = heading
else:
level, title = 3, title_from_anchor(anchor, main_anchors)
entries.append((level, title, anchor))
return entries
def render_toc(entries: list[tuple[int, str, str]]) -> str:
rendered = [
"<details>",
SUMMARY_LINE,
"",
"### 细粒度目录",
"",
]
for level, title, anchor in entries:
indent = " " * max(level - 2, 0)
rendered.append(f"{indent}- [{title}](#{anchor})")
rendered.extend(["", "</details>"])
return "\n".join(rendered)
def active_toc_bounds(text: str) -> tuple[int, int] | None:
in_fence = False
fence_marker = ""
offset = 0
details_start: int | None = None
for line in text.splitlines(keepends=True):
stripped = line.lstrip()
if stripped.startswith(("```", "~~~")):
marker = stripped[:3]
if not in_fence:
in_fence = True
fence_marker = marker
elif marker == fence_marker:
in_fence = False
fence_marker = ""
if not in_fence:
if "<details>" in line:
details_start = offset + line.index("<details>")
if SUMMARY_LINE in line and details_start is not None:
end = text.find("</details>", offset)
if end == -1:
return None
return details_start, end + len("</details>")
offset += len(line)
return None
def replace_toc_block(text: str, toc: str) -> tuple[str, bool]:
bounds = active_toc_bounds(text)
if bounds is None:
return text, False
details_start, details_end = bounds
return f"{text[:details_start]}{toc}{text[details_end:]}", True
def main() -> int:
changed: list[str] = []
errors: list[str] = []
doc_readmes = doc_readmes_from_taxonomy()
if not doc_readmes:
print("OK synced docs TOC blocks: 0 changed")
return 0
for rel_path, main_anchors in doc_readmes.items():
path = ROOT / rel_path
if not path.exists():
errors.append(f"{rel_path}: missing docs README")
continue
text = path.read_text(encoding='utf-8')
if text.startswith(('<!-- generated: sync-gongfa-catalog.py -->',
'<!-- generated: sync-faqi-catalog.py -->')):
# 两本文集的目录由唯一JSON源的生成器维护;结构检查仍覆盖完整锚点。
continue
entries = collect_toc_entries(text, main_anchors)
if not entries:
errors.append(f"{rel_path}: no TOC entries generated")
continue
new_text, replaced = replace_toc_block(text, render_toc(entries))
if not replaced:
errors.append(f"{rel_path}: missing standard collapsible TOC block")
continue
if new_text != text:
path.write_text(new_text, encoding="utf-8")
changed.append(str(rel_path))
if errors:
print("SYNC_DOC_TOC_ERRORS")
for error in errors:
print(error)
return 1
for rel_path in changed:
print(f"updated {rel_path}")
print(f"OK synced docs TOC blocks: {len(changed)} changed")
return 0
if __name__ == "__main__":
sys.exit(main())