zco-claude 0.0.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ClaudeSettings/DOT.claudeignore +7 -0
- ClaudeSettings/README.md +100 -0
- ClaudeSettings/commands/generate_changelog.sh +49 -0
- ClaudeSettings/commands/show_env +92 -0
- ClaudeSettings/commands/zco-clean +164 -0
- ClaudeSettings/commands/zco-git-summary +15 -0
- ClaudeSettings/commands/zco-git-tag +42 -0
- ClaudeSettings/hooks/CHANGELOG.md +157 -0
- ClaudeSettings/hooks/README.md +254 -0
- ClaudeSettings/hooks/save_chat_plain.py +148 -0
- ClaudeSettings/hooks/save_chat_spec.py +398 -0
- ClaudeSettings/rules/README.md +270 -0
- ClaudeSettings/rules/go/.golangci.yml.template +170 -0
- ClaudeSettings/rules/go/GoBuildAutoVersion.v250425.md +95 -0
- ClaudeSettings/rules/go/check-standards.sh +128 -0
- ClaudeSettings/rules/go/coding-standards.md +973 -0
- ClaudeSettings/rules/go/example.go +207 -0
- ClaudeSettings/rules/go/go-testing.md +691 -0
- ClaudeSettings/rules/go/list-comments.sh +85 -0
- ClaudeSettings/settings.sample.json +71 -0
- ClaudeSettings/skills/README.md +225 -0
- ClaudeSettings/skills/zco-docs-update/SKILL.md +381 -0
- ClaudeSettings/skills/zco-help/SKILL.md +601 -0
- ClaudeSettings/skills/zco-plan/SKILL.md +661 -0
- ClaudeSettings/skills/zco-plan-new/SKILL.md +585 -0
- ClaudeSettings/zco-scripts/co-docs-update.sh +150 -0
- ClaudeSettings/zco-scripts/test_update_plan_metadata.py +328 -0
- ClaudeSettings/zco-scripts/update-plan-metadata.py +324 -0
- zco_claude-0.0.8.dist-info/METADATA +190 -0
- zco_claude-0.0.8.dist-info/RECORD +34 -0
- zco_claude-0.0.8.dist-info/WHEEL +5 -0
- zco_claude-0.0.8.dist-info/entry_points.txt +3 -0
- zco_claude-0.0.8.dist-info/top_level.txt +1 -0
- zco_claude_init.py +1732 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Claude Code 对话自动保存 Hooks
|
|
2
|
+
|
|
3
|
+
自动保存 Claude Code 对话记录到 Markdown 文件。
|
|
4
|
+
|
|
5
|
+
## 📦 可用脚本
|
|
6
|
+
|
|
7
|
+
### 1. `save-conversation-simple.py` - 简洁版 ⭐推荐
|
|
8
|
+
|
|
9
|
+
**特点**:
|
|
10
|
+
- 只保存核心对话内容(用户提问 + Claude 回答)
|
|
11
|
+
- 格式简洁,接近终端输出体验
|
|
12
|
+
- 适合日常快速回顾
|
|
13
|
+
|
|
14
|
+
**文件名**: `claude_log_YYMMDD_HHMMSS_simple.md`
|
|
15
|
+
|
|
16
|
+
### 2. `save-conversation-enhanced.py` - 增强版
|
|
17
|
+
|
|
18
|
+
**特点**:
|
|
19
|
+
- 包含工具使用统计(如 Bash 14次、Edit 7次)
|
|
20
|
+
- 提取参考资源列表(读取的文件、访问的 URL)
|
|
21
|
+
- 附带详细的工具调用参数
|
|
22
|
+
- 适合深度分析和复盘
|
|
23
|
+
|
|
24
|
+
**文件名**: `YYMMDDHH_{关键词}.md` + `YYMMDDHH_{关键词}_resources.txt`
|
|
25
|
+
|
|
26
|
+
### 3. `install-to-project.sh` - 一键安装脚本
|
|
27
|
+
|
|
28
|
+
快速将 hooks 部署到其他项目。
|
|
29
|
+
|
|
30
|
+
## 🚀 快速安装到其他项目
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# 方法 1: 使用安装脚本(推荐)
|
|
34
|
+
./.claude/hooks/install-to-project.sh /path/to/your-project
|
|
35
|
+
|
|
36
|
+
# 方法 2: 手动复制
|
|
37
|
+
mkdir -p /path/to/project/.claude/hooks
|
|
38
|
+
cp .claude/hooks/save-conversation-*.py /path/to/project/.claude/hooks/
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
然后在目标项目创建 `.claude/settings.local.json`:
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"hooks": {
|
|
45
|
+
"Stop": [{
|
|
46
|
+
"hooks": [{
|
|
47
|
+
"type": "command",
|
|
48
|
+
"command": "python3 \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/save-conversation-simple.py"
|
|
49
|
+
}]
|
|
50
|
+
}]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 🔧 跨项目使用说明
|
|
56
|
+
|
|
57
|
+
### ✅ 关键点
|
|
58
|
+
|
|
59
|
+
**不需要配置的内容**:
|
|
60
|
+
- ✅ `python3` 路径:已在系统 PATH 中,不需要绝对路径
|
|
61
|
+
- ✅ 项目路径:脚本自动从 hook 输入数据中获取 `cwd`
|
|
62
|
+
- ✅ transcript 路径:由 Claude Code 自动传递
|
|
63
|
+
|
|
64
|
+
**环境变量**:
|
|
65
|
+
- `$CLAUDE_PROJECT_DIR`:Claude Code 自动设置,指向当前项目根目录
|
|
66
|
+
- 可以在 `command` 中使用这个变量引用脚本
|
|
67
|
+
|
|
68
|
+
### 三种部署方案
|
|
69
|
+
|
|
70
|
+
#### 方案 1:每个项目独立(推荐)
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# 安装
|
|
74
|
+
./install-to-project.sh /path/to/project-A
|
|
75
|
+
./install-to-project.sh /path/to/project-B
|
|
76
|
+
|
|
77
|
+
# 每个项目都有自己的配置和脚本副本
|
|
78
|
+
project-A/.claude/hooks/save-conversation-*.py
|
|
79
|
+
project-B/.claude/hooks/save-conversation-*.py
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**优点**:每个项目可以独立定制脚本
|
|
83
|
+
|
|
84
|
+
#### 方案 2:全局共享
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# 1. 创建全局 hooks 目录
|
|
88
|
+
mkdir -p ~/.claude/shared-hooks
|
|
89
|
+
cp .claude/hooks/save-conversation-*.py ~/.claude/shared-hooks/
|
|
90
|
+
|
|
91
|
+
# 2. 在各项目的 .claude/settings.local.json 中引用
|
|
92
|
+
{
|
|
93
|
+
"hooks": {
|
|
94
|
+
"Stop": [{
|
|
95
|
+
"hooks": [{
|
|
96
|
+
"type": "command",
|
|
97
|
+
"command": "python3 ~/.claude/shared-hooks/save-conversation-simple.py"
|
|
98
|
+
}]
|
|
99
|
+
}]
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**优点**:所有项目共享一份脚本,便于统一维护
|
|
105
|
+
|
|
106
|
+
#### 方案 3:符号链接
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# 创建中央仓库
|
|
110
|
+
mkdir -p ~/code/claude-hooks
|
|
111
|
+
cp .claude/hooks/save-conversation-*.py ~/code/claude-hooks/
|
|
112
|
+
|
|
113
|
+
# 在各项目中创建符号链接
|
|
114
|
+
mkdir -p /path/to/project/.claude/hooks
|
|
115
|
+
ln -s ~/code/claude-hooks/save-conversation-simple.py \
|
|
116
|
+
/path/to/project/.claude/hooks/
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**优点**:便于版本控制和同步更新
|
|
120
|
+
|
|
121
|
+
## 工作原理
|
|
122
|
+
|
|
123
|
+
1. **用户提问** → Claude 回答 → 对话进行中...
|
|
124
|
+
2. **对话结束** → 触发 `Stop` Hook
|
|
125
|
+
3. **脚本执行**:
|
|
126
|
+
- 读取会话文件(`transcript_path`)
|
|
127
|
+
- 解析 JSONL 格式的对话记录
|
|
128
|
+
- 提取用户提问和 Claude 回答
|
|
129
|
+
- 生成 Markdown 格式
|
|
130
|
+
- 保存到 `_.claude_hist/`
|
|
131
|
+
|
|
132
|
+
## 查看保存的对话
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# 查看最近的对话记录
|
|
136
|
+
ls -lt _.claude_hist/ | head -10
|
|
137
|
+
|
|
138
|
+
# 查看今天的对话
|
|
139
|
+
ls _.claude_hist/$(date +%y%m%d)*.md
|
|
140
|
+
|
|
141
|
+
# 搜索包含特定关键词的对话
|
|
142
|
+
grep -l "API" _.claude_hist/*.md
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## 手动保存对话
|
|
146
|
+
|
|
147
|
+
如果自动保存未触发,可以手动运行脚本:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# 需要知道会话文件路径
|
|
151
|
+
python3 .claude/hooks/save-conversation.py << EOF
|
|
152
|
+
{
|
|
153
|
+
"hook_event_name": "Stop",
|
|
154
|
+
"transcript_path": "~/.claude/projects/项目路径/sessions/会话ID.jsonl",
|
|
155
|
+
"cwd": "$(pwd)"
|
|
156
|
+
}
|
|
157
|
+
EOF
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## 禁用自动保存
|
|
161
|
+
|
|
162
|
+
如果需要临时禁用自动保存,编辑 `.claude/settings.json`,注释掉 `hooks.Stop` 部分:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"hooks": {
|
|
167
|
+
// "Stop": [...] // 注释掉这行即可禁用
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## 自定义配置
|
|
173
|
+
|
|
174
|
+
### 修改关键词提取数量
|
|
175
|
+
|
|
176
|
+
编辑 `save-conversation.py`,找到 `extract_keywords` 函数:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
def extract_keywords(text: str, max_keywords: int = 3): # 改为你想要的数量
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 修改文件名格式
|
|
183
|
+
|
|
184
|
+
编辑 `save-conversation.py`,找到文件名生成部分:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
timestamp = datetime.now().strftime('%y%m%d%H') # 自定义时间格式
|
|
188
|
+
filename = f"{timestamp}_{keywords}.md" # 自定义文件名格式
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### 添加更多元数据
|
|
192
|
+
|
|
193
|
+
在 Markdown 头部添加更多信息:
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
lines.append(f"**时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
197
|
+
lines.append(f"**项目**: {project_dir}\n") # 添加项目路径
|
|
198
|
+
lines.append(f"**会话ID**: {session_id}\n") # 添加会话ID
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## 故障排查
|
|
202
|
+
|
|
203
|
+
### 对话没有自动保存
|
|
204
|
+
|
|
205
|
+
1. 检查脚本是否可执行:
|
|
206
|
+
```bash
|
|
207
|
+
ls -la .claude/hooks/save-conversation.py
|
|
208
|
+
# 应该有 x 权限
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
2. 检查 hooks 配置:
|
|
212
|
+
```bash
|
|
213
|
+
cat .claude/settings.json | grep -A 10 "hooks"
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
3. 查看错误日志(如果有):
|
|
217
|
+
```bash
|
|
218
|
+
# Claude Code 的日志通常在控制台输出
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 文件名中的关键词不准确
|
|
222
|
+
|
|
223
|
+
这是正常的,关键词提取是基于简单的算法。你可以:
|
|
224
|
+
1. 手动重命名文件
|
|
225
|
+
2. 修改 `extract_keywords` 函数的逻辑
|
|
226
|
+
3. 添加更多停用词
|
|
227
|
+
|
|
228
|
+
### Python 3 未安装
|
|
229
|
+
|
|
230
|
+
确保系统安装了 Python 3:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
python3 --version
|
|
234
|
+
# 应该显示 Python 3.x.x
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
如果未安装:
|
|
238
|
+
```bash
|
|
239
|
+
# Ubuntu/Debian
|
|
240
|
+
sudo apt install python3
|
|
241
|
+
|
|
242
|
+
# macOS
|
|
243
|
+
brew install python3
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## 维护
|
|
247
|
+
|
|
248
|
+
- **定期清理**: `_.claude_hist/` 目录可能会积累大量文件,建议定期归档
|
|
249
|
+
- **备份**: 重要对话建议备份到其他位置
|
|
250
|
+
- **版本控制**: 可以选择将对话记录提交到 Git(但建议使用 `.gitignore` 排除)
|
|
251
|
+
|
|
252
|
+
## 更新日志
|
|
253
|
+
|
|
254
|
+
- 2026-01-06: 初始版本,支持自动保存对话为 Markdown 格式
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Claude Code 对话保存脚本(简单版)
|
|
4
|
+
直接从 transcript 提取纯文本,保留 Claude 的原始输出格式
|
|
5
|
+
|
|
6
|
+
Environment Variables:
|
|
7
|
+
- YJ_CLAUDE_CHAT_SAVE_PLAIN: Must be "1" to enable this hook
|
|
8
|
+
- YJ_CLAUDE_CHAT_SAVE_DIR: Output directory (default: _.claude_hist)
|
|
9
|
+
"""
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
import subprocess
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
def get_git_root(project_dir: Path=None) -> Path:
|
|
18
|
+
"""获取当前 Git 仓库根目录"""
|
|
19
|
+
try:
|
|
20
|
+
# 执行 git rev-parse --show-toplevel 命令
|
|
21
|
+
if project_dir:
|
|
22
|
+
result = subprocess.run(
|
|
23
|
+
['git', '-C', str(project_dir), 'rev-parse', '--show-toplevel'],
|
|
24
|
+
capture_output=True, text=True, check=True
|
|
25
|
+
)
|
|
26
|
+
else:
|
|
27
|
+
result = subprocess.run(
|
|
28
|
+
['git', 'rev-parse', '--show-toplevel'],
|
|
29
|
+
capture_output=True, text=True, check=True
|
|
30
|
+
)
|
|
31
|
+
return Path(result.stdout.strip())
|
|
32
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
33
|
+
return Path.cwd()
|
|
34
|
+
|
|
35
|
+
def get_hist_dir(project_dir: Path=None) -> Path:
|
|
36
|
+
"""获取历史记录目录"""
|
|
37
|
+
hist_dir_name = os.environ.get('YJ_CLAUDE_CHAT_SAVE_DIR', None)
|
|
38
|
+
git_root = get_git_root(project_dir)
|
|
39
|
+
if not hist_dir_name:
|
|
40
|
+
hist_dir = git_root / '_.claude_hist'
|
|
41
|
+
else:
|
|
42
|
+
hist_dir = os.path.abspath(os.path.join(str(git_root), hist_dir_name))
|
|
43
|
+
hist_dir.mkdir(exist_ok=True)
|
|
44
|
+
return hist_dir
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def extract_text_from_message(msg: dict) -> str:
|
|
48
|
+
"""从消息中提取纯文本"""
|
|
49
|
+
msg_type = msg.get('type', '')
|
|
50
|
+
if msg_type not in ['user', 'assistant']:
|
|
51
|
+
return ''
|
|
52
|
+
|
|
53
|
+
inner_msg = msg.get('message', {})
|
|
54
|
+
content = inner_msg.get('content', '')
|
|
55
|
+
|
|
56
|
+
# 用户消息通常是字符串
|
|
57
|
+
if isinstance(content, str):
|
|
58
|
+
return content
|
|
59
|
+
|
|
60
|
+
# Assistant 消息是列表
|
|
61
|
+
if isinstance(content, list):
|
|
62
|
+
text_parts = []
|
|
63
|
+
for item in content:
|
|
64
|
+
if isinstance(item, dict):
|
|
65
|
+
if item.get('type') == 'text':
|
|
66
|
+
text_parts.append(item.get('text', ''))
|
|
67
|
+
return '\n'.join(text_parts)
|
|
68
|
+
|
|
69
|
+
return ''
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def save_simple_conversation(transcript_path: str, project_dir: str):
|
|
73
|
+
"""保存对话为简单的纯文本格式"""
|
|
74
|
+
try:
|
|
75
|
+
# 解析 transcript
|
|
76
|
+
messages = []
|
|
77
|
+
with open(transcript_path, 'r', encoding='utf-8') as f:
|
|
78
|
+
for line in f:
|
|
79
|
+
try:
|
|
80
|
+
msg = json.loads(line.strip())
|
|
81
|
+
if msg.get('type') in ['user', 'assistant']:
|
|
82
|
+
messages.append(msg)
|
|
83
|
+
except:
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
if not messages:
|
|
87
|
+
print("No messages to save", file=sys.stderr)
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
# 生成文件名
|
|
91
|
+
timestamp = datetime.now().strftime('%y%m%d_%H%M%S')
|
|
92
|
+
filename = f"claude_log_{timestamp}_simple.md"
|
|
93
|
+
hist_dir = get_hist_dir(project_dir)
|
|
94
|
+
output_file = hist_dir / filename
|
|
95
|
+
|
|
96
|
+
# 生成简单的 Markdown
|
|
97
|
+
with open(output_file, 'w', encoding='utf-8') as f:
|
|
98
|
+
f.write(f"# Claude Code Conversation\n\n")
|
|
99
|
+
f.write(f"**Time**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
|
100
|
+
f.write("---\n\n")
|
|
101
|
+
|
|
102
|
+
for msg in messages:
|
|
103
|
+
msg_type = msg.get('type', '')
|
|
104
|
+
text = extract_text_from_message(msg)
|
|
105
|
+
|
|
106
|
+
if not text.strip():
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
if msg_type == 'user':
|
|
110
|
+
f.write(f"**User**:\n{text}\n\n")
|
|
111
|
+
elif msg_type == 'assistant':
|
|
112
|
+
f.write(f"**Claude**:\n{text}\n\n")
|
|
113
|
+
|
|
114
|
+
f.write(f"\n---\n*Generated at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n")
|
|
115
|
+
|
|
116
|
+
print(f"Simple conversation saved to: {output_file}", file=sys.stderr)
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
120
|
+
import traceback
|
|
121
|
+
traceback.print_exc(file=sys.stderr)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def main():
|
|
125
|
+
try:
|
|
126
|
+
# Check if this hook is enabled via environment variable
|
|
127
|
+
if os.environ.get('YJ_CLAUDE_CHAT_SAVE_PLAIN') != '1':
|
|
128
|
+
# Silently exit if not enabled
|
|
129
|
+
sys.exit(0)
|
|
130
|
+
|
|
131
|
+
input_data = json.load(sys.stdin)
|
|
132
|
+
hook_event = input_data.get('hook_event_name', '')
|
|
133
|
+
|
|
134
|
+
if hook_event == 'Stop':
|
|
135
|
+
transcript_path = input_data.get('transcript_path', '')
|
|
136
|
+
cwd = input_data.get('cwd', '')
|
|
137
|
+
|
|
138
|
+
if transcript_path and cwd:
|
|
139
|
+
save_simple_conversation(transcript_path, cwd)
|
|
140
|
+
|
|
141
|
+
sys.exit(0)
|
|
142
|
+
except Exception as e:
|
|
143
|
+
print(f"Hook error: {e}", file=sys.stderr)
|
|
144
|
+
sys.exit(1)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if __name__ == '__main__':
|
|
148
|
+
main()
|