初始化提交:contentm_agent 工作区全量快照
内容分四块: 1、产品规划产出 —— MCN 短视频整合营销工作台的①段五份(1a 需求/1b 竞品/1c 画像/1d 策略/1e 场景)、②段两份(2a 功能/2b 布局)、③段界面(DESIGN.md 契约与令牌表 + mcn-workbench.html 原型 + 实测/会诊/审查三份 + 23 张闸门截图)。 2、开源竞品调研 —— 5 个内容工作台项目的取证原始件与 1b 系列分析文档。 3、参考资料 —— 竞品视频抽帧 1145 张 + 2 个源视频 + 功能点截图。 4、机制侧 —— 协作脚本与状态台账、工作区记忆日志、抽帧/OCR 脚本。 .gitignore 只排运行时日志、脚本备份副本与一次性探针输出,其余按原样入库。
This commit is contained in:
commit
df56c2c137
1773 files changed
+205840
No files matched your search
@@ -0,0 +1,112 @@
|
||||
# -*- 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)
|
||||
Reference in new issue
Block a user