# -*- coding: utf-8 -*- """Easel 文件树统计(第 3 棒)—— 只读复用本目标目录内的 easel.tree.json,不重新下载。 产出:取证/easel.tree.stat.json(可复算)。 用法:python _easel_tree_stat.py """ import json, os, sys from collections import Counter, defaultdict HERE = os.path.dirname(os.path.abspath(__file__)) SRC = os.path.join(HERE, "api", "easel.tree.json") OUT = os.path.join(HERE, "easel.tree.stat.json") d = json.load(open(SRC, encoding="utf-8")) tree = d["tree"] blobs = [x for x in tree if x["type"] == "blob"] paths = [x["path"] for x in blobs] res = { "_source": "api/easel.tree.json(GitHub trees API, recursive=1, default branch main)", "_note": "路径级统计;truncated=%s" % d.get("truncated"), "blob_count": len(blobs), "tree_count": len(tree) - len(blobs), "total_entries": len(tree), } # 顶层目录分布 top = Counter(p.split("/")[0] if "/" in p else "(root)" for p in paths) res["top_level"] = dict(sorted(top.items(), key=lambda kv: -kv[1])) # 二级目录分布(用于 web/、skills/、docs/、easel/ 内部) lvl2 = Counter("/".join(p.split("/")[:2]) for p in paths if p.count("/") >= 1) res["level2"] = dict(sorted(lvl2.items(), key=lambda kv: -kv[1])) # 扩展名 ext = Counter(os.path.splitext(p)[1].lower() or "(noext)" for p in paths) res["ext_top"] = dict(sorted(ext.items(), key=lambda kv: -kv[1])[:25]) # 核心面:skills/openclaw 下的一级子目录 == 一个技能(SKILL.md 所在目录) sk = [p for p in paths if p.startswith("skills/openclaw/")] res["skills_openclaw_blobs"] = len(sk) skill_dirs = sorted({p.split("/")[2] for p in sk if p.count("/") >= 2}) res["skills_openclaw_dir_count"] = len(skill_dirs) res["skills_openclaw_dirs"] = skill_dirs res["skills_openclaw_skillmd"] = sorted(p for p in sk if p.endswith("/SKILL.md")) res["skills_openclaw_skillmd_count"] = len([p for p in sk if p.endswith("/SKILL.md")]) # skills/shared sh = [p for p in paths if p.startswith("skills/shared/")] res["skills_shared_blobs"] = len(sh) res["skills_shared_top"] = dict(sorted(Counter("/".join(p.split("/")[:4]) for p in sh).items(), key=lambda kv: -kv[1])[:20]) # docs dc = [p for p in paths if p.startswith("docs/")] res["docs_files"] = sorted(dc) # web web = [p for p in paths if p.startswith("web/")] res["web_blobs"] = len(web) res["web_level3"] = dict(sorted(Counter("/".join(p.split("/")[:3]) for p in web).items(), key=lambda kv: -kv[1])[:25]) res["web_tsx_count"] = len([p for p in web if p.endswith(".tsx")]) res["web_ts_count"] = len([p for p in web if p.endswith(".ts")]) res["web_py_count"] = len([p for p in web if p.endswith(".py")]) # easel CLI cli = [p for p in paths if p.startswith("easel/")] res["easel_cli_files"] = cli # profiles / outputs res["profiles_files"] = sorted(p for p in paths if p.startswith("profiles/")) res["outputs_files"] = len([p for p in paths if p.startswith("outputs/")]) # openclaw 同步面 res["openclaw_files"] = sorted(p for p in paths if p.startswith("openclaw/")) # 媒体/素材 res["assets_blobs"] = len([p for p in paths if p.startswith("assets/")]) res["aux_scripts_py"] = sorted(p for p in paths if p.endswith(".py") and not p.startswith(("skills/", "web/", "easel/"))) with open(OUT, "w", encoding="utf-8") as f: json.dump(res, f, ensure_ascii=False, indent=1) # 控制台摘要 def show(k): v = res[k] print(f"{k}: {v if not isinstance(v,(list,dict)) else (len(v) if isinstance(v,list) else v)}") print("== blob/tree ==", res["blob_count"], res["tree_count"], "truncated:", d.get("truncated")) print("\n== top_level ==") for k, v in res["top_level"].items(): print(f" {k}: {v}") print("\n== skills/openclaw: blobs=%d dirs=%d SKILL.md=%d ==" % ( res["skills_openclaw_blobs"], res["skills_openclaw_dir_count"], res["skills_openclaw_skillmd_count"])) print("\n== docs ==") for p in res["docs_files"]: print(" ", p) print("\n== web: blobs=%d tsx=%d ts=%d py=%d ==" % ( res["web_blobs"], res["web_tsx_count"], res["web_ts_count"], res["web_py_count"])) for k, v in res["web_level3"].items(): print(f" {k}: {v}") print("\n== easel CLI ==") for p in res["easel_cli_files"]: print(" ", p) print("\n== profiles ==") for p in res["profiles_files"]: print(" ", p) print("\n== openclaw sync ==") for p in res["openclaw_files"]: print(" ", p) print("\n== ext_top ==") for k, v in res["ext_top"].items(): print(f" {k}: {v}") print("\nwrote", OUT)