Files
2026-07-13 12:30:10 +08:00

171 lines
5.2 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.
#!/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()