Files
company-kb/tools/feishu-render.py
yangqianqian 84f7950589 Initial commit: company-kb framework
- 完整的知识库框架(P0-P5 路线图)
- 12篇治理文档
- 工具脚本(kb-init.sh, kb-lint-fm.py, feishu-sync.py, kb-bot)
- 7个 Claude 命令
- 示例项目和测试项目(wanniu-l1)
- 契约文件(meta/kb-contract.yaml)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 18:24:39 +08:00

115 lines
4.1 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""feishu-render.py —— 知识库 → 飞书呈现(单向 md→飞书不回写
两种载体:
--bitable 扫 projects 下页的 frontmatter每页一条记录 upsert 到多维表格(结构型呈现,可筛选/看板)
--docs 代号 把某项目的 _index + 决策同步为飞书云文档(叙事型呈现)
飞书只读呈现,写路径仍是 Obsidian/Git防双写冲突。
凭据由 lark-cli 自管,不入库。字段严格对齐 meta/kb-contract.yaml。
用法:
python3 tools/feishu-render.py --bitable [--dry-run]
python3 tools/feishu-render.py --docs <代号> [--dry-run]
注意本文件是接口骨架。bitable app/table 的创建与 record 写入留 TODO
接入时填 _bitable_upsert()docs 同步复用 lark-doc-manager。
"""
import sys
import os
import re
import glob
import argparse
try:
import yaml
except ImportError:
print("ERROR: 需要 PyYAML。请运行: pip install pyyaml", file=sys.stderr)
sys.exit(2)
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
FM_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
# 呈现给飞书多维表格的字段(对齐 kb-contract.yaml
BITABLE_FIELDS = ["type", "title", "status", "source", "created", "source_link"]
def parse_fm(fp):
text = open(fp, encoding="utf-8").read()
m = FM_RE.match(text)
if not m:
return None
try:
data = yaml.safe_load(m.group(1))
return data if isinstance(data, dict) else None
except yaml.YAMLError:
return None
def collect_pages():
"""扫 projects 下所有知识页,返回 (相对路径, frontmatter) 列表。"""
rows = []
for fp in glob.glob(os.path.join(ROOT, "projects", "**", "*.md"), recursive=True):
fm = parse_fm(fp)
if fm and fm.get("type"):
rel = os.path.relpath(fp, ROOT)
project = rel.split(os.sep)[1] if os.sep in rel else ""
rows.append((rel, project, fm))
return rows
def _bitable_upsert(rows, dry_run):
"""接入时实现:通用 api 写多维表格记录。
唯一键 = 页相对路径(防重复插入,做 upsert
命令参考:
lark-cli api POST /open-apis/bitable/v1/apps/{app}/tables/{table}/records \
--data '{"fields": {...}}'
"""
print(f"[bitable] 采集到 {len(rows)} 页知识:")
for rel, project, fm in rows:
fields = {k: fm.get(k, "") for k in BITABLE_FIELDS}
fields["项目"] = project
fields["页路径"] = rel
if dry_run:
print(f" - {project}/{fm.get('type')}: {fm.get('title', rel)}")
else:
# TODO: lark-cli api POST .../records含 app_token/table_id 配置)
pass
if not dry_run:
raise NotImplementedError(
"Bitable 写入尚未接入P2-d。需先创建多维表并配置 app_token/table_id。"
"当前 --dry-run 可预览将同步的记录。"
)
def _docs_sync(code, dry_run):
idx = os.path.join(ROOT, "projects", code, "_index.md")
if not os.path.isfile(idx):
print(f"ERROR: 找不到 projects/{code}/_index.md", file=sys.stderr)
sys.exit(1)
print(f"[docs] 将同步 projects/{code}/ 的 _index + 决策为飞书云文档")
if dry_run:
print(" dry-run实际同步走 lark-doc-manager / lark-cli docs +create/+update")
return
raise NotImplementedError(
"云文档同步走 lark-doc-manager skill已激活。接入时在此调用其创建/更新流程。"
)
def main():
ap = argparse.ArgumentParser(description="知识库 → 飞书呈现")
g = ap.add_mutually_exclusive_group(required=True)
g.add_argument("--bitable", action="store_true", help="同步全库到多维表格")
g.add_argument("--docs", metavar="代号", help="同步某项目为云文档")
ap.add_argument("--dry-run", action="store_true", help="只预览不写飞书")
args = ap.parse_args()
if args.bitable:
_bitable_upsert(collect_pages(), args.dry_run)
else:
_docs_sync(args.docs, args.dry_run)
if __name__ == "__main__":
main()