Files
WorkBuddy df56c2c137 初始化提交: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 只排运行时日志、脚本备份副本与一次性探针输出,其余按原样入库。
2026-10-08 08:13:02 +08:00

140 lines
4.7 KiB
Python
Raw Permalink 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.
# -*- coding: utf-8 -*-
"""OpenCreator 文件树统计(可复算)
输入:取证/opencreator/tree_*.json(本棒分片拉取)+ 取证/api/opencreator.tree.top.json(第 1 棒顶层)
输出:取证/opencreator/opencreator.tree.stat.json + 控制台摘要
口径:只数 type=='blob';顶层目录取 path 第一段。
"""
import json, os, collections
HERE = os.path.dirname(os.path.abspath(__file__))
OC = os.path.join(HERE, "opencreator")
API = os.path.join(HERE, "api")
# 切片树里的 path 是相对「该切片根」的,必须补回前缀才是仓根相对路径
SLICES = {
"tree_.github.json": ".github",
"tree_apps.json": "apps",
"tree_assets.json": "assets",
"tree_packages.json": "packages",
"tree_resources.json": "resources",
"tree_runtime.json": "runtime",
"tree_scripts.json": "scripts",
"tree_skills.json": "skills",
"tree_template.json": "template",
"tree_docs.json": "docs",
}
blobs = [] # (path, size)
trees = 0
for f, prefix in SLICES.items():
d = json.load(open(os.path.join(OC, f), encoding="utf-8"))
assert not d.get("truncated"), f + " truncated"
for e in d["tree"]:
if e["type"] == "blob":
blobs.append((prefix + "/" + e["path"], e.get("size", 0)))
else:
trees += 1
root = json.load(open(os.path.join(API, "opencreator.tree.top.json"), encoding="utf-8"))
for e in root["tree"]:
if e["type"] == "blob":
blobs.append((e["path"], e.get("size", 0)))
else:
trees += 1
# 去重(docs/specs 在 docs 整树里已含)
seen, uniq = set(), []
for p, s in blobs:
if p in seen:
continue
seen.add(p)
uniq.append((p, s))
blobs = uniq
by_top = collections.Counter()
for p, _ in blobs:
by_top[p.split("/")[0]] += 1
def sub(prefix, n=2):
c = collections.Counter()
for p, _ in blobs:
if p.startswith(prefix + "/"):
parts = p.split("/")
c["/".join(parts[:n])] += 1
return c
ext = collections.Counter()
for p, _ in blobs:
ext[os.path.splitext(p)[1].lower() or "(noext)"] += 1
apps = sub("apps", 3)
daemon_src = collections.Counter()
for p, _ in blobs:
if p.startswith("apps/daemon/src/"):
parts = p.split("/")
daemon_src["/".join(parts[:4]) if len(parts) > 3 else "/".join(parts[:3])] += 1
web_src = collections.Counter()
for p, _ in blobs:
if p.startswith("apps/web/src/"):
parts = p.split("/")
web_src["/".join(parts[:4]) if len(parts) > 3 else "/".join(parts[:3])] += 1
rt = sub("runtime/krillinai", 3)
tpl = collections.Counter()
for p, _ in blobs:
if p.startswith("template/"):
parts = p.split("/")
tpl[parts[1]] += 1
# 模板条目数(二级目录)
tpl_entries = collections.Counter()
for p, _ in blobs:
if p.startswith("template/") and len(p.split("/")) >= 3:
tpl_entries["/".join(p.split("/")[:2])] += 1
# writing-templates 模板条目
wt = collections.Counter()
for p, _ in blobs:
if p.startswith("packages/writing-templates/templates/"):
wt["/".join(p.split("/")[:4])] += 1
test_files = [p for p, _ in blobs if ".test." in p or ".spec." in p]
out = {
"repo": "krillinai/OpenCreator",
"branch": "master",
"total_blobs": len(blobs),
"total_trees": trees,
"total_bytes": sum(s for _, s in blobs),
"blobs_by_top_dir": dict(by_top.most_common()),
"ext_top20": dict(ext.most_common(20)),
"apps_by_3": dict(apps.most_common()),
"apps_daemon_src": dict(daemon_src.most_common()),
"apps_web_src": dict(web_src.most_common()),
"runtime_krillinai": dict(rt.most_common()),
"template_by_category": dict(tpl.most_common()),
"template_entries_by_category": {k: sum(1 for p, _ in blobs if p.startswith(k + "/"))
for k in tpl},
"writing_templates_entries": dict(wt.most_common()),
"test_file_count": len(test_files),
"test_files_sample": test_files[:15],
}
with open(os.path.join(OC, "opencreator.tree.stat.json"), "w", encoding="utf-8") as f:
json.dump(out, f, ensure_ascii=False, indent=1)
print("total_blobs=%d total_trees=%d total_bytes=%d test_files=%d"
% (len(blobs), trees, out["total_bytes"], len(test_files)))
print("顶层目录 blob 数:", dict(by_top.most_common()))
print()
print("apps 三层:", dict(apps.most_common(12)))
print()
print("daemon/src 四层:", dict(daemon_src.most_common(15)))
print()
print("web/src 四层:", dict(web_src.most_common(15)))
print()
print("runtime/krillinai 三层:", dict(rt.most_common(20)))
print()
print("template 分类 blob 数:", dict(tpl.most_common()))
print("template 条目数(二级目录):", dict(out["template_entries_by_category"]))
print()
print("writing-templates 条目:", dict(wt.most_common()))
print()
print("扩展名 top20:", dict(ext.most_common(20)))