Full upload: MCNVideo AI project with .workbuddy

This commit is contained in:
maogeigei committed 2026-08-06 23:44:59 +08:00
commit 7252d24ba1
755 files changed
+170975

No files matched your search

@@ -0,0 +1,55 @@
#!/bin/bash
# 从YouTube视频下载字幕
# 用法: ./download_subtitles.sh <YouTube_URL> [输出目录]
# 优先下载人工字幕,无人工字幕则下载自动生成字幕
# 语言优先级:中文 > 英文 > 其他
set -e
URL="$1"
OUTPUT_DIR="${2:-.}"
if [ -z "$URL" ]; then
echo "用法: ./download_subtitles.sh <YouTube_URL> [输出目录]"
exit 1
fi
mkdir -p "$OUTPUT_DIR"
echo ">>> 检查可用字幕..."
yt-dlp --list-subs --no-download "$URL" 2>/dev/null | tail -20
echo ""
echo ">>> 尝试下载人工字幕(中文优先)..."
# 尝试1: 人工中文字幕
if yt-dlp --write-subs --sub-langs "zh-Hans,zh-Hant,zh,zh-CN,zh-TW" --sub-format srt --skip-download -o "$OUTPUT_DIR/%(title)s" "$URL" 2>/dev/null; then
FOUND=$(find "$OUTPUT_DIR" -name "*.srt" -newer /tmp/.ytdlp_marker 2>/dev/null | head -1)
if [ -n "$FOUND" ]; then
echo "✅ 下载成功: $FOUND"
exit 0
fi
fi
# 尝试2: 人工英文字幕
echo ">>> 无中文人工字幕,尝试英文..."
if yt-dlp --write-subs --sub-langs "en,en-US,en-GB" --sub-format srt --skip-download -o "$OUTPUT_DIR/%(title)s" "$URL" 2>/dev/null; then
FOUND=$(find "$OUTPUT_DIR" -name "*.srt" -mmin -1 2>/dev/null | head -1)
if [ -n "$FOUND" ]; then
echo "✅ 下载成功: $FOUND"
exit 0
fi
fi
# 尝试3: 自动生成字幕(中文优先)
echo ">>> 无人工字幕,尝试自动生成字幕..."
if yt-dlp --write-auto-subs --sub-langs "zh-Hans,zh,en" --sub-format srt --skip-download -o "$OUTPUT_DIR/%(title)s" "$URL" 2>/dev/null; then
FOUND=$(find "$OUTPUT_DIR" -name "*.srt" -o -name "*.vtt" 2>/dev/null | head -1)
if [ -n "$FOUND" ]; then
echo "✅ 自动字幕下载成功: $FOUND"
exit 0
fi
fi
echo "❌ 未找到任何可用字幕"
exit 1
@@ -0,0 +1,150 @@
#!/usr/bin/env python3
"""
合并6个Agent的调研结果,生成Phase 1.5调研Review检查点的摘要表格。
扫描 references/research/ 目录下的01-06 md文件,统计每个维度的来源数量、
一手/二手占比、关键发现。
用法:
python3 merge_research.py <skill目录路径>
示例:
python3 merge_research.py .claude/skills/elon-musk-perspective
输出: 打印markdown格式的摘要表格到stdout
"""
import sys
import re
from pathlib import Path
AGENTS = {
'01-writings': '著作',
'02-conversations': '对话',
'03-expression-dna': '表达',
'04-external-views': '他者',
'05-decisions': '决策',
'06-timeline': '时间线',
}
def count_sources(content: str) -> dict:
"""统计来源数量和一手/二手占比"""
# 计算URL数量作为来源数
urls = re.findall(r'https?://[^\s\)]+', content)
# 检测一手/二手标记
primary_markers = len(re.findall(r'一手|primary|本人|原文|原始|直接引用', content, re.IGNORECASE))
secondary_markers = len(re.findall(r'二手|secondary|转述|总结|评论|分析', content, re.IGNORECASE))
return {
'url_count': len(urls),
'unique_urls': len(set(urls)),
'primary_markers': primary_markers,
'secondary_markers': secondary_markers,
}
def extract_key_findings(content: str, max_items: int = 3) -> list[str]:
"""提取关键发现(取前几个二级标题或加粗项)"""
# 尝试提取##标题
headings = re.findall(r'^##\s+(.+)$', content, re.MULTILINE)
if headings:
return headings[:max_items]
# fallback: 提取加粗项
bolds = re.findall(r'\*\*(.+?)\*\*', content)
if bolds:
return bolds[:max_items]
# fallback: 取前3个非空行
lines = [l.strip() for l in content.split('\n') if l.strip() and not l.startswith('#')]
return [l[:50] + '...' if len(l) > 50 else l for l in lines[:max_items]]
def find_contradictions(files: dict[str, str]) -> list[str]:
"""简单检测跨文件矛盾(同一关键词出现不同判断)"""
contradictions = []
# 检测「但是」「然而」「相反」「矛盾」等矛盾标记
for name, content in files.items():
matches = re.findall(r'(?:矛盾|相反|但实际上|然而.*?不同|争议).{0,100}', content)
for m in matches:
contradictions.append(f"{AGENTS.get(name, name)}: {m[:80]}")
return contradictions[:5] # 最多5条
def main():
if len(sys.argv) < 2:
print("用法: python3 merge_research.py <skill目录路径>")
sys.exit(1)
skill_dir = Path(sys.argv[1])
research_dir = skill_dir / 'references' / 'research'
if not research_dir.exists():
print(f"❌ 目录不存在: {research_dir}")
sys.exit(1)
files = {}
rows = []
total_sources = 0
total_primary = 0
total_secondary = 0
missing = []
for key, label in AGENTS.items():
md_file = research_dir / f"{key}.md"
if not md_file.exists():
missing.append(label)
rows.append(f"│ {label:<12} │ {'❌ 缺失':<8} │ {'—':<24} │")
continue
content = md_file.read_text(encoding='utf-8')
files[key] = content
stats = count_sources(content)
findings = extract_key_findings(content)
total_sources += stats['unique_urls']
total_primary += stats['primary_markers']
total_secondary += stats['secondary_markers']
findings_str = ', '.join(findings) if findings else '—'
if len(findings_str) > 40:
findings_str = findings_str[:37] + '...'
rows.append(f"│ {label:<12} │ {stats['unique_urls']:<8} │ {findings_str:<24} │")
# 矛盾检测
contradictions = find_contradictions(files)
# 输出
print("┌──────────────┬──────────┬──────────────────────────┐")
print("│ Agent │ 来源数量 │ 关键发现 │")
print("├──────────────┼──────────┼──────────────────────────┤")
for row in rows:
print(row)
print("├──────────────┼──────────┼──────────────────────────┤")
primary_ratio = f"{total_primary}/{total_primary + total_secondary}" if (total_primary + total_secondary) > 0 else "未标记"
print(f"│ 总来源数 │ {total_sources:<8} │ 一手占比: {primary_ratio:<15} │")
if contradictions:
print(f"│ 矛盾点 │ {len(contradictions)}处 │ {contradictions[0][:24]:<24} │")
else:
print(f"│ 矛盾点 │ 0处 │ {'—':<24} │")
if missing:
print(f"│ 信息不足维度 │ {len(missing)}个 │ {', '.join(missing):<24} │")
else:
print(f"│ 信息不足维度 │ 无 │ {'—':<24} │")
print("└──────────────┴──────────┴──────────────────────────┘")
# 总结
if total_sources < 10:
print("\n⚠️ 总来源数 <10,建议降低期望或补充调研")
if missing:
print(f"\n⚠️ 缺失维度: {', '.join(missing)},建议补充或在诚实边界中标注")
if __name__ == '__main__':
main()
@@ -0,0 +1,152 @@
#!/usr/bin/env python3
"""
自动检查生成的SKILL.md是否通过Phase 4质量标准。
对照通过标准表格逐项检查,输出通过/不通过和具体原因。
用法:
python3 quality_check.py <SKILL.md路径>
示例:
python3 quality_check.py .claude/skills/elon-musk-perspective/SKILL.md
"""
import sys
import re
from pathlib import Path
def check_mental_models(content: str) -> tuple[bool, str]:
"""检查心智模型数量(3-7个)"""
# 匹配 ### 模型N: 或 ### N. 等模式
models = re.findall(r'^###\s+(?:模型|Model|心智模型)\s*\d', content, re.MULTILINE)
if not models:
# fallback: 数「### 」开头的行在心智模型section中
in_section = False
count = 0
for line in content.split('\n'):
if re.match(r'^##\s+.*心智模型|Mental Model', line, re.IGNORECASE):
in_section = True
continue
if in_section and re.match(r'^##\s+', line) and '心智模型' not in line:
break
if in_section and re.match(r'^###\s+', line):
count += 1
if count > 0:
passed = 3 <= count <= 7
return passed, f"{count}个心智模型 {'✅' if passed else '❌ (应为3-7个)'}"
count = len(models)
if count == 0:
return False, "未检测到心智模型section"
passed = 3 <= count <= 7
return passed, f"{count}个心智模型 {'✅' if passed else '❌ (应为3-7个)'}"
def check_limitations(content: str) -> tuple[bool, str]:
"""检查每个模型是否有局限性"""
has_limitation = bool(re.search(r'局限|失效|不适用|盲区|limitation|blind spot', content, re.IGNORECASE))
return has_limitation, "有局限性标注 ✅" if has_limitation else "❌ 未找到局限性描述"
def check_expression_dna(content: str) -> tuple[bool, str]:
"""检查表达DNA辨识度"""
dna_section = bool(re.search(r'表达DNA|Expression DNA|表达风格', content, re.IGNORECASE))
if not dna_section:
return False, "❌ 未找到表达DNA section"
# 检查是否有具体的风格描述(句式、词汇等)
style_markers = len(re.findall(r'句式|词汇|语气|幽默|节奏|确定性|引用|口头禅', content))
passed = style_markers >= 3
return passed, f"表达DNA特征: {style_markers}项 {'✅' if passed else '❌ (应≥3项)'}"
def check_honest_boundary(content: str) -> tuple[bool, str]:
"""检查诚实边界(至少3条)"""
# 找诚实边界section
boundary_match = re.search(r'(?:##\s+.*诚实边界|## Honest Boundary)(.*?)(?=\n##\s|\Z)', content, re.DOTALL | re.IGNORECASE)
if not boundary_match:
return False, "❌ 未找到诚实边界section"
boundary_text = boundary_match.group(1)
# 计算列表项
items = re.findall(r'^[-*]\s+', boundary_text, re.MULTILINE)
count = len(items)
passed = count >= 3
return passed, f"诚实边界: {count}条 {'✅' if passed else '❌ (应≥3条)'}"
def check_tensions(content: str) -> tuple[bool, str]:
"""检查内在张力(至少2对)"""
tension_markers = len(re.findall(r'张力|矛盾|tension|paradox|一方面.*另一方面|既.*又', content, re.IGNORECASE))
passed = tension_markers >= 2
return passed, f"内在张力: {tension_markers}处 {'✅' if passed else '❌ (应≥2处)'}"
def check_primary_sources(content: str) -> tuple[bool, str]:
"""检查一手来源占比"""
# 找调研来源section
source_section = re.search(r'(?:##\s+.*来源|## Source|## Reference)(.*?)(?=\n##\s|\Z)', content, re.DOTALL | re.IGNORECASE)
if not source_section:
return True, "未找到来源section(跳过检查)"
source_text = source_section.group(1)
primary = len(re.findall(r'一手|primary|本人著作|原始', source_text, re.IGNORECASE))
secondary = len(re.findall(r'二手|secondary|转述|评论', source_text, re.IGNORECASE))
total = primary + secondary
if total == 0:
return True, "未标记来源类型(跳过检查)"
ratio = primary / total
passed = ratio > 0.5
return passed, f"一手来源占比: {primary}/{total} ({ratio:.0%}) {'✅' if passed else '❌ (应>50%)'}"
def main():
if len(sys.argv) < 2:
print("用法: python3 quality_check.py <SKILL.md路径>")
sys.exit(1)
skill_path = Path(sys.argv[1])
if not skill_path.exists():
print(f"❌ 文件不存在: {skill_path}")
sys.exit(1)
content = skill_path.read_text(encoding='utf-8')
checks = [
("心智模型数量", check_mental_models),
("模型局限性", check_limitations),
("表达DNA辨识度", check_expression_dna),
("诚实边界", check_honest_boundary),
("内在张力", check_tensions),
("一手来源占比", check_primary_sources),
]
print(f"质量检查: {skill_path.name}")
print("=" * 50)
passed_count = 0
total = len(checks)
for name, check_fn in checks:
passed, detail = check_fn(content)
status = "✅ PASS" if passed else "❌ FAIL"
print(f" {name:<12} {status} {detail}")
if passed:
passed_count += 1
print("=" * 50)
print(f"结果: {passed_count}/{total} 通过")
if passed_count == total:
print("🎉 全部通过,可以交付")
elif passed_count >= total - 1:
print("⚠️ 基本通过,建议修复不通过项后交付")
else:
print("❌ 多项不通过,建议回到Phase 2迭代")
sys.exit(0 if passed_count == total else 1)
if __name__ == '__main__':
main()
@@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""
将SRT/VTT字幕文件清洗为干净的纯文本transcript。
去除时间戳、序号、重复行、HTML标签,输出可直接阅读的文本。
用法:
python3 srt_to_transcript.py input.srt [output.txt]
python3 srt_to_transcript.py input.vtt [output.txt]
如果不指定输出文件,默认输出到 input_transcript.txt
"""
import sys
import re
from pathlib import Path
def clean_srt(content: str) -> str:
"""清洗SRT格式字幕"""
lines = content.strip().split('\n')
texts = []
for line in lines:
line = line.strip()
# 跳过序号行(纯数字)
if re.match(r'^\d+$', line):
continue
# 跳过时间戳行
if re.match(r'\d{2}:\d{2}:\d{2}', line):
continue
# 跳过空行
if not line:
continue
# 去除HTML标签
line = re.sub(r'<[^>]+>', '', line)
# 去除VTT的position标记
line = re.sub(r'align:.*$|position:.*$', '', line).strip()
if line:
texts.append(line)
# 去重(自动字幕常有连续重复行)
deduped = []
for text in texts:
if not deduped or text != deduped[-1]:
deduped.append(text)
# 合并成段落:连续的短句合并,遇到句末标点或长停顿换行
result = []
current = []
for text in deduped:
current.append(text)
# 如果当前累积文本够长或遇到句末标点,形成一个段落
joined = ' '.join(current)
if len(joined) > 200 or re.search(r'[。!?.!?]$', text):
result.append(joined)
current = []
if current:
result.append(' '.join(current))
return '\n\n'.join(result)
def clean_vtt(content: str) -> str:
"""清洗VTT格式字幕(先去掉VTT头部,然后按SRT逻辑处理)"""
# 去掉WEBVTT头部
content = re.sub(r'^WEBVTT.*?\n\n', '', content, flags=re.DOTALL)
# 去掉NOTE块
content = re.sub(r'NOTE.*?\n\n', '', content, flags=re.DOTALL)
return clean_srt(content)
def main():
if len(sys.argv) < 2:
print("用法: python3 srt_to_transcript.py <input.srt|input.vtt> [output.txt]")
sys.exit(1)
input_path = Path(sys.argv[1])
if not input_path.exists():
print(f"❌ 文件不存在: {input_path}")
sys.exit(1)
# 默认输出文件名
if len(sys.argv) >= 3:
output_path = Path(sys.argv[2])
else:
output_path = input_path.parent / f"{input_path.stem}_transcript.txt"
# 读取并检测格式
content = input_path.read_text(encoding='utf-8')
if input_path.suffix.lower() == '.vtt' or content.startswith('WEBVTT'):
transcript = clean_vtt(content)
else:
transcript = clean_srt(content)
output_path.write_text(transcript, encoding='utf-8')
# 统计
word_count = len(transcript)
line_count = transcript.count('\n') + 1
print(f"✅ 转换完成: {output_path}")
print(f" 字数: {word_count} 段落数: {line_count}")
if __name__ == '__main__':
main()