项目文件夹

文件
2026-07-13 12:30:10 +08:00

171 行
5.2 KiB
Python

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
#!/usr/bin/env python3
"""Skill 文件管理器
管理前任 Skill 的文件操作:列出、创建目录、生成组合 SKILL.md。
Usage:
python3 skill_writer.py --action <list|init|combine> --base-dir <path> [--slug <slug>]
"""
import argparse
import os
import sys
import json
from pathlib import Path
from datetime import datetime
def list_skills(base_dir: str):
"""列出所有已生成的前任 Skill"""
if not os.path.isdir(base_dir):
print("还没有创建任何前任 Skill。")
return
skills = []
for slug in sorted(os.listdir(base_dir)):
meta_path = os.path.join(base_dir, slug, 'meta.json')
if os.path.exists(meta_path):
with open(meta_path, 'r', encoding='utf-8') as f:
meta = json.load(f)
skills.append({
'slug': slug,
'name': meta.get('name', slug),
'version': meta.get('version', '?'),
'updated_at': meta.get('updated_at', '?'),
'profile': meta.get('profile', {}),
})
if not skills:
print("还没有创建任何前任 Skill。")
return
print(f"共 {len(skills)} 个前任 Skill\n")
for s in skills:
profile = s['profile']
desc_parts = [profile.get('occupation', ''), profile.get('city', '')]
desc = ' · '.join([p for p in desc_parts if p])
print(f" /{s['slug']}{s['name']}")
if desc:
print(f" {desc}")
print(f" 版本 {s['version']} · 更新于 {s['updated_at'][:10] if len(s['updated_at']) > 10 else s['updated_at']}")
print()
def init_skill(base_dir: str, slug: str):
"""初始化 Skill 目录结构"""
skill_dir = os.path.join(base_dir, slug)
dirs = [
os.path.join(skill_dir, 'versions'),
os.path.join(skill_dir, 'memories', 'chats'),
os.path.join(skill_dir, 'memories', 'photos'),
os.path.join(skill_dir, 'memories', 'social'),
]
for d in dirs:
os.makedirs(d, exist_ok=True)
print(f"已初始化目录:{skill_dir}")
def combine_skill(base_dir: str, slug: str):
"""合并 memory.md + persona.md 生成完整 SKILL.md"""
skill_dir = os.path.join(base_dir, slug)
meta_path = os.path.join(skill_dir, 'meta.json')
memory_path = os.path.join(skill_dir, 'memory.md')
persona_path = os.path.join(skill_dir, 'persona.md')
skill_path = os.path.join(skill_dir, 'SKILL.md')
if not os.path.exists(meta_path):
print(f"错误:meta.json 不存在 {meta_path}", file=sys.stderr)
sys.exit(1)
with open(meta_path, 'r', encoding='utf-8') as f:
meta = json.load(f)
memory_content = ''
if os.path.exists(memory_path):
with open(memory_path, 'r', encoding='utf-8') as f:
memory_content = f.read()
persona_content = ''
if os.path.exists(persona_path):
with open(persona_path, 'r', encoding='utf-8') as f:
persona_content = f.read()
name = meta.get('name', slug)
profile = meta.get('profile', {})
desc_parts = []
if profile.get('occupation'):
desc_parts.append(profile['occupation'])
if profile.get('mbti'):
desc_parts.append(profile['mbti'])
if profile.get('zodiac'):
desc_parts.append(profile['zodiac'])
description = f"{name}{','.join(desc_parts)}" if desc_parts else name
skill_md = f"""---
name: ex-{slug}
description: {description}
user-invocable: true
---
# {name}
{description}
---
## PART A:关系记忆
{memory_content}
---
## PART B:人物性格
{persona_content}
---
## 运行规则
1. 你是{name},不是 AI 助手。用ta的方式说话,用ta的逻辑思考
2. 先由 PART B 判断:ta会怎么回应这个话题?什么态度?
3. 再由 PART A 补充:结合你们的共同记忆,让回应更真实
4. 始终保持 PART B 的表达风格,包括口头禅、语气词、标点习惯
5. Layer 0 硬规则优先级最高:
- 不说ta在现实中绝不可能说的话
- 不突然变得完美或无条件包容(除非ta本来就这样)
- 保持ta的"棱角"——正是这些不完美让ta真实
- 如果被问到"你爱不爱我"这类问题,用ta会用的方式回答,而不是用户想听的答案
"""
with open(skill_path, 'w', encoding='utf-8') as f:
f.write(skill_md)
print(f"已生成 {skill_path}")
def main():
parser = argparse.ArgumentParser(description='Skill 文件管理器')
parser.add_argument('--action', required=True, choices=['list', 'init', 'combine'])
parser.add_argument('--base-dir', default='./exes', help='基础目录')
parser.add_argument('--slug', help='前任代号')
args = parser.parse_args()
if args.action == 'list':
list_skills(args.base_dir)
elif args.action == 'init':
if not args.slug:
print("错误:init 需要 --slug 参数", file=sys.stderr)
sys.exit(1)
init_skill(args.base_dir, args.slug)
elif args.action == 'combine':
if not args.slug:
print("错误:combine 需要 --slug 参数", file=sys.stderr)
sys.exit(1)
combine_skill(args.base_dir, args.slug)
if __name__ == '__main__':
main()