转换/处理单个文件。返回 (成功与否, 消息)。 Pipeline: 1. 非 .md:markitdown 转为 markdown 文本;.md:直接读原文 2. postprocess.process 加锚点 + 生成 outline 数据 3. 写 .md(仅当内容变化)+ 写 .outline.json
(md: MarkItDown, source: Path)
| 168 | |
| 169 | |
| 170 | def convert_file(md: MarkItDown, source: Path) -> tuple[bool, str]: |
| 171 | """ |
| 172 | 转换/处理单个文件。返回 (成功与否, 消息)。 |
| 173 | |
| 174 | Pipeline: |
| 175 | 1. 非 .md:markitdown 转为 markdown 文本;.md:直接读原文 |
| 176 | 2. postprocess.process 加锚点 + 生成 outline 数据 |
| 177 | 3. 写 .md(仅当内容变化)+ 写 .outline.json |
| 178 | """ |
| 179 | is_md = source.suffix.lower() == ".md" |
| 180 | |
| 181 | if is_md: |
| 182 | try: |
| 183 | markdown = source.read_text(encoding="utf-8") |
| 184 | except Exception as e: |
| 185 | return False, f"读取失败: {e}" |
| 186 | target_md = source |
| 187 | else: |
| 188 | result = md.convert(str(source)) |
| 189 | markdown = result.markdown if result.markdown else "" |
| 190 | if not markdown.strip(): |
| 191 | return False, "转换结果为空" |
| 192 | target_md = source.with_suffix(".md") |
| 193 | |
| 194 | target_outline = source.with_suffix(".outline.json") |
| 195 | doc_path = _project_relative_posix(target_md) |
| 196 | |
| 197 | # 读旧 outline(如果存在),让 process 保留 agent_summary |
| 198 | previous_outline = None |
| 199 | if target_outline.exists(): |
| 200 | try: |
| 201 | previous_outline = json.loads(target_outline.read_text(encoding="utf-8")) |
| 202 | except Exception: |
| 203 | previous_outline = None |
| 204 | |
| 205 | text_with_anchors, outline_data = postprocess_text( |
| 206 | markdown, doc_path, previous_outline=previous_outline |
| 207 | ) |
| 208 | |
| 209 | # 仅当内容变化时写 .md(保护 git 工作树) |
| 210 | md_changed = ( |
| 211 | not target_md.exists() |
| 212 | or target_md.read_text(encoding="utf-8") != text_with_anchors |
| 213 | ) |
| 214 | if md_changed: |
| 215 | _atomic_write_text(target_md, text_with_anchors) |
| 216 | |
| 217 | _atomic_write_text( |
| 218 | target_outline, |
| 219 | json.dumps(outline_data, ensure_ascii=False, indent=2), |
| 220 | ) |
| 221 | |
| 222 | sec_count = sum(_count_sections(s) for s in outline_data["sections"]) |
| 223 | md_msg = "新增" if md_changed else "未变" |
| 224 | return True, ( |
| 225 | f"-> {target_md.name} ({md_msg}, {len(text_with_anchors)} 字符, " |
| 226 | f"{sec_count} 章节, {outline_data['doc_paragraphs']} 段)" |
| 227 | ) |
no test coverage detected