Skip to content

Instantly share code, notes, and snippets.

@hangox
Last active October 12, 2025 17:05
Show Gist options
  • Select an option

  • Save hangox/69b10be0a640de9d30099d70c021a36c to your computer and use it in GitHub Desktop.

Select an option

Save hangox/69b10be0a640de9d30099d70c021a36c to your computer and use it in GitHub Desktop.
同步所有 Claude Code 项目的脚本 - 自动读取并同步 ~/.claude/projects/ 中的所有项目
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
同步所有 Claude Code 项目的脚本
读取 ~/.claude/projects/ 目录中的所有项目,
并在每个项目目录中运行 specstory sync 命令
支持平台: macOS, Linux, Windows
"""
import json
import os
import platform
import subprocess
import sys
from pathlib import Path
from typing import Optional, Set
def get_system_info() -> dict:
"""获取系统信息"""
system = platform.system().lower()
return {
'system': system,
'is_windows': system == 'windows',
'is_mac': system == 'darwin',
'is_linux': system == 'linux'
}
def get_claude_projects_dir() -> Path:
"""获取 Claude Code 的 projects 目录(跨平台)"""
home = Path.home()
# Windows 和 Unix-like 系统都使用 .claude 目录
claude_dir = home / ".claude" / "projects"
if not claude_dir.exists():
raise FileNotFoundError(
f"Claude projects 目录不存在: {claude_dir}\n"
f"请确认已安装 Claude Code 并创建过项目"
)
return claude_dir
def get_project_path_from_sessions(project_dir: Path) -> Optional[str]:
"""
从项目的 session 文件中提取真实的项目路径
参考 Rust 代码 get_project_path_from_sessions 函数
Args:
project_dir: Claude 项目目录(~/.claude/projects/xxx)
Returns:
项目的真实路径,如果无法提取则返回 None
"""
# 查找所有 .jsonl 文件
jsonl_files = list(project_dir.glob("*.jsonl"))
if not jsonl_files:
return None
# 遍历每个 JSONL 文件
for jsonl_file in jsonl_files:
try:
with open(jsonl_file, 'r', encoding='utf-8') as f:
# 只检查前 10 行(参考 Rust 实现)
for i, line in enumerate(f):
if i >= 10:
break
line = line.strip()
if not line:
continue
try:
data = json.loads(line)
# 提取 cwd 字段
cwd = data.get('cwd')
if cwd and isinstance(cwd, str) and cwd.strip():
return cwd
except json.JSONDecodeError:
continue
except Exception as e:
print(f"读取文件 {jsonl_file} 时出错: {e}")
continue
return None
def get_all_claude_projects() -> Set[str]:
"""
获取所有 Claude Code 项目的真实路径
Returns:
项目路径的集合(去重)
"""
projects_dir = get_claude_projects_dir()
project_paths = set()
print(f"正在扫描 Claude Code 项目目录: {projects_dir}\n")
# 遍历所有子目录
for entry in projects_dir.iterdir():
if not entry.is_dir():
continue
# 从 session 文件中提取真实路径
project_path = get_project_path_from_sessions(entry)
if project_path:
# 验证路径是否存在
if os.path.exists(project_path):
project_paths.add(project_path)
print(f"✓ 找到项目: {project_path}")
else:
print(f"✗ 路径不存在: {project_path}")
else:
print(f"✗ 无法从 {entry.name} 提取项目路径")
return project_paths
def run_specstory_sync(project_path: str) -> bool:
"""
在指定项目目录中运行 specstory sync 命令(跨平台)
Args:
project_path: 项目目录路径
Returns:
命令是否执行成功
"""
print(f"\n{'='*80}")
print(f"正在同步项目: {project_path}")
print(f"{'='*80}")
try:
sys_info = get_system_info()
# 根据不同平台构建命令
if sys_info['is_windows']:
# Windows: 使用 cmd.exe 或 PowerShell
result = subprocess.run(
['specstory', 'sync'],
cwd=project_path,
capture_output=True,
text=True,
timeout=300,
shell=True # Windows 需要 shell=True 来查找命令
)
else:
# macOS / Linux: 直接运行命令
result = subprocess.run(
['specstory', 'sync'],
cwd=project_path,
capture_output=True,
text=True,
timeout=300,
shell=False
)
# 打印输出
if result.stdout:
print(result.stdout)
if result.stderr:
print(result.stderr, flush=True)
if result.returncode == 0:
print(f"✓ 同步成功: {project_path}")
return True
else:
print(f"✗ 同步失败 (退出码 {result.returncode}): {project_path}")
return False
except FileNotFoundError:
print(f"✗ 未找到 specstory 命令,请确认已安装并添加到 PATH")
return False
except subprocess.TimeoutExpired:
print(f"✗ 命令超时: {project_path}")
return False
except Exception as e:
print(f"✗ 执行出错: {e}")
return False
def main():
"""主函数"""
sys_info = get_system_info()
print("Claude Code 项目同步工具")
print(f"运行平台: {sys_info['system']}")
print(f"Python 版本: {sys.version.split()[0]}")
print("="*80)
try:
# 获取所有项目路径
project_paths = get_all_claude_projects()
if not project_paths:
print("\n未找到任何 Claude Code 项目")
return
print(f"\n共找到 {len(project_paths)} 个项目\n")
# 统计结果
success_count = 0
fail_count = 0
# 对每个项目运行 specstory sync
for i, project_path in enumerate(sorted(project_paths), 1):
print(f"\n[{i}/{len(project_paths)}] 处理项目...")
if run_specstory_sync(project_path):
success_count += 1
else:
fail_count += 1
# 打印总结
print(f"\n{'='*80}")
print("同步完成!")
print(f"成功: {success_count} 个项目")
print(f"失败: {fail_count} 个项目")
print(f"总计: {len(project_paths)} 个项目")
print(f"{'='*80}")
except FileNotFoundError as e:
print(f"错误: {e}")
except KeyboardInterrupt:
print("\n\n用户中断操作")
except Exception as e:
print(f"\n发生错误: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment