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.
Files changed (34) hide show
  1. ClaudeSettings/DOT.claudeignore +7 -0
  2. ClaudeSettings/README.md +100 -0
  3. ClaudeSettings/commands/generate_changelog.sh +49 -0
  4. ClaudeSettings/commands/show_env +92 -0
  5. ClaudeSettings/commands/zco-clean +164 -0
  6. ClaudeSettings/commands/zco-git-summary +15 -0
  7. ClaudeSettings/commands/zco-git-tag +42 -0
  8. ClaudeSettings/hooks/CHANGELOG.md +157 -0
  9. ClaudeSettings/hooks/README.md +254 -0
  10. ClaudeSettings/hooks/save_chat_plain.py +148 -0
  11. ClaudeSettings/hooks/save_chat_spec.py +398 -0
  12. ClaudeSettings/rules/README.md +270 -0
  13. ClaudeSettings/rules/go/.golangci.yml.template +170 -0
  14. ClaudeSettings/rules/go/GoBuildAutoVersion.v250425.md +95 -0
  15. ClaudeSettings/rules/go/check-standards.sh +128 -0
  16. ClaudeSettings/rules/go/coding-standards.md +973 -0
  17. ClaudeSettings/rules/go/example.go +207 -0
  18. ClaudeSettings/rules/go/go-testing.md +691 -0
  19. ClaudeSettings/rules/go/list-comments.sh +85 -0
  20. ClaudeSettings/settings.sample.json +71 -0
  21. ClaudeSettings/skills/README.md +225 -0
  22. ClaudeSettings/skills/zco-docs-update/SKILL.md +381 -0
  23. ClaudeSettings/skills/zco-help/SKILL.md +601 -0
  24. ClaudeSettings/skills/zco-plan/SKILL.md +661 -0
  25. ClaudeSettings/skills/zco-plan-new/SKILL.md +585 -0
  26. ClaudeSettings/zco-scripts/co-docs-update.sh +150 -0
  27. ClaudeSettings/zco-scripts/test_update_plan_metadata.py +328 -0
  28. ClaudeSettings/zco-scripts/update-plan-metadata.py +324 -0
  29. zco_claude-0.0.8.dist-info/METADATA +190 -0
  30. zco_claude-0.0.8.dist-info/RECORD +34 -0
  31. zco_claude-0.0.8.dist-info/WHEEL +5 -0
  32. zco_claude-0.0.8.dist-info/entry_points.txt +3 -0
  33. zco_claude-0.0.8.dist-info/top_level.txt +1 -0
  34. zco_claude_init.py +1732 -0
@@ -0,0 +1,150 @@
1
+ #!/bin/bash
2
+
3
+ #
4
+ # co-docs-update.sh - 自动更新 CLAUDE.md 文档元信息
5
+ #
6
+ # 用途:
7
+ # - 更新 Git Commit ID、Branch、最新提交信息
8
+ # - 更新文档修改时间
9
+ # - 保持文档与代码同步
10
+ #
11
+ # 使用方法:
12
+ # bash scripts/co-docs-update.sh
13
+ #
14
+ # 作者:Claude Code
15
+ # 日期:2026-01-07
16
+ #
17
+
18
+ set -e # 遇到错误立即退出
19
+
20
+ # 颜色定义
21
+ RED='\033[0;31m'
22
+ GREEN='\033[0;32m'
23
+ YELLOW='\033[1;33m'
24
+ BLUE='\033[0;34m'
25
+ NC='\033[0m' # No Color
26
+
27
+ # 日志函数
28
+ log_info() {
29
+ echo -e "${BLUE}[INFO]${NC} $1"
30
+ }
31
+
32
+ log_success() {
33
+ echo -e "${GREEN}[SUCCESS]${NC} $1"
34
+ }
35
+
36
+ log_warn() {
37
+ echo -e "${YELLOW}[WARN]${NC} $1"
38
+ }
39
+
40
+ log_error() {
41
+ echo -e "${RED}[ERROR]${NC} $1"
42
+ }
43
+
44
+ # 获取项目根目录
45
+ PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
46
+ if [ -z "$PROJECT_ROOT" ]; then
47
+ log_error "未找到 Git 仓库,请在 Git 项目中运行此脚本"
48
+ exit 1
49
+ fi
50
+
51
+ CLAUDE_MD="$PROJECT_ROOT/CLAUDE.md"
52
+
53
+ log_info "项目根目录: $PROJECT_ROOT"
54
+ log_info "CLAUDE.md 路径: $CLAUDE_MD"
55
+
56
+ # 检查 CLAUDE.md 是否存在
57
+ if [ ! -f "$CLAUDE_MD" ]; then
58
+ log_error "CLAUDE.md 文件不存在: $CLAUDE_MD"
59
+ log_info "请先创建 CLAUDE.md 文件"
60
+ exit 1
61
+ fi
62
+
63
+ ## 创建备份
64
+ #BACKUP_FILE="${CLAUDE_MD}._.$(date +%y%m%d_%H%M).md"
65
+ BACKUP_FILE="$PROJECT_ROOT/CLAUDE._.v$(date +%y%m%d_%H%M).md"
66
+ cp "$CLAUDE_MD" "$BACKUP_FILE"
67
+ log_info "已创建备份: $BACKUP_FILE"
68
+
69
+ # 收集 Git 信息
70
+ log_info "收集 Git 信息..."
71
+
72
+ # 当前时间
73
+ CURRENT_TIME=$(date '+%Y-%m-%d %H:%M:%S')
74
+
75
+ # Git Commit 信息
76
+ GIT_COMMIT_FULL=$(git log -1 --pretty=format:"%H" 2>/dev/null || echo "unknown")
77
+ GIT_COMMIT_SHORT=$(git log -1 --pretty=format:"%h" 2>/dev/null || echo "unknown")
78
+ GIT_COMMIT_MSG=$(git log -1 --pretty=format:"%s" 2>/dev/null || echo "unknown")
79
+ GIT_AUTHOR=$(git log -1 --pretty=format:"%an" 2>/dev/null || echo "unknown")
80
+ GIT_DATE=$(git log -1 --pretty=format:"%ad" --date=iso 2>/dev/null || echo "unknown")
81
+
82
+ # Git Branch
83
+ GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
84
+
85
+ # 应用版本(从 main.go 中提取)
86
+ MAIN_GO="$PROJECT_ROOT/main.go"
87
+ APP_VERSION="unknown"
88
+ if [ -f "$MAIN_GO" ]; then
89
+ APP_VERSION=$(grep -E 'const version string = "' "$MAIN_GO" | sed -E 's/.*const version string = "(.*)".*/\1/' || echo "unknown")
90
+ fi
91
+
92
+ log_info " - 更新时间: $CURRENT_TIME"
93
+ log_info " - Git Commit: $GIT_COMMIT_SHORT ($GIT_COMMIT_FULL)"
94
+ log_info " - Git Branch: $GIT_BRANCH"
95
+ log_info " - 最新提交: $GIT_COMMIT_MSG"
96
+ log_info " - 提交作者: $GIT_AUTHOR"
97
+ log_info " - 提交时间: $GIT_DATE"
98
+ log_info " - 应用版本: $APP_VERSION"
99
+
100
+ # 使用 sed 更新 CLAUDE.md 中的元信息
101
+ log_info "更新 CLAUDE.md 元信息..."
102
+
103
+ # 临时文件
104
+ TEMP_FILE=$(mktemp)
105
+
106
+ # 更新各个字段
107
+ sed -e "s|^- \*\*更新时间\*\*:.*|- **更新时间**: $CURRENT_TIME|" \
108
+ -e "s|^- \*\*Git Commit\*\*:.*|- **Git Commit**: \`$GIT_COMMIT_SHORT\` ($GIT_COMMIT_FULL)|" \
109
+ -e "s|^- \*\*Git Branch\*\*:.*|- **Git Branch**: \`$GIT_BRANCH\`|" \
110
+ -e "s|^- \*\*最新提交\*\*:.*|- **最新提交**: $GIT_COMMIT_MSG (by $GIT_AUTHOR, $GIT_DATE)|" \
111
+ -e "s|^- \*\*应用版本\*\*:.*|- **应用版本**: $APP_VERSION|" \
112
+ "$CLAUDE_MD" >"$TEMP_FILE"
113
+
114
+ # 更新文档底部的最后更新时间
115
+ sed -i "s|^\*\*最后更新\*\*:.*|\*\*最后更新\*\*: $CURRENT_TIME|" "$TEMP_FILE"
116
+
117
+ # 替换原文件
118
+ mv "$TEMP_FILE" "$CLAUDE_MD"
119
+
120
+ log_success "CLAUDE.md 元信息更新完成!"
121
+
122
+ # 显示差异
123
+ echo ""
124
+ log_info "更新内容对比:"
125
+ echo "----------------------------------------"
126
+ git diff --no-index --color=always "$BACKUP_FILE" "$CLAUDE_MD" | tail -n +5 || true
127
+ echo "----------------------------------------"
128
+
129
+ # 询问是否删除备份
130
+ echo ""
131
+ read -p "$(echo -e ${YELLOW}是否删除备份文件? [y/N]: ${NC})" -n 1 -r
132
+ echo
133
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
134
+ rm -f "$BACKUP_FILE"
135
+ log_success "备份文件已删除"
136
+ else
137
+ log_info "备份文件保留在: $BACKUP_FILE"
138
+ fi
139
+
140
+ # 显示文档状态
141
+ echo ""
142
+ log_success "✅ 文档更新完成!"
143
+ echo ""
144
+ log_info "下一步操作:"
145
+ log_info " 1. 查看文档: cat CLAUDE.md"
146
+ log_info " 2. 提交更改: git add CLAUDE.md && git commit -m 'docs: update CLAUDE.md metadata'"
147
+ log_info " 3. 验证 Claude Code 加载: claude code"
148
+ echo ""
149
+
150
+ exit 0
@@ -0,0 +1,328 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Unit tests for update-plan-metadata.py
4
+
5
+ Tests:
6
+ 1. Parse valid YAML front matter
7
+ 2. Update status transitions
8
+ 3. Set created_at only on first execution
9
+ 4. Update updated_at on every execution
10
+ 5. Merge tags without overwriting
11
+ 6. Handle missing fields gracefully
12
+ 7. Handle malformed YAML (error case)
13
+ 8. Preserve Markdown body unchanged
14
+ 9. Atomic write on success
15
+ 10. Backwards compatibility with old format
16
+ """
17
+
18
+ import unittest
19
+ import tempfile
20
+ import json
21
+ from pathlib import Path
22
+ from datetime import datetime
23
+ import sys
24
+ import subprocess
25
+ import time
26
+
27
+ # Add parent dir to path to import the script
28
+ sys.path.insert(0, str(Path(__file__).parent))
29
+
30
+
31
+ class TestPlanMetadataUpdater(unittest.TestCase):
32
+ """Test suite for plan metadata updates"""
33
+
34
+ def setUp(self):
35
+ """Create temporary test directory"""
36
+ self.test_dir = tempfile.mkdtemp()
37
+ self.test_dir_path = Path(self.test_dir)
38
+
39
+ def tearDown(self):
40
+ """Clean up temporary files"""
41
+ import shutil
42
+ shutil.rmtree(self.test_dir, ignore_errors=True)
43
+
44
+ def create_test_plan(self, filename: str, content: str) -> Path:
45
+ """Helper: Create a test plan file"""
46
+ plan_path = self.test_dir_path / filename
47
+ plan_path.write_text(content, encoding='utf-8')
48
+ return plan_path
49
+
50
+ def run_update_script(self, plan_path: Path, action: str, tags: list = None) -> dict:
51
+ """
52
+ Helper: Run update-plan-metadata.py script
53
+
54
+ Args:
55
+ plan_path: Path to plan file
56
+ action: Action to perform (start, complete, fail, cancel)
57
+ tags: Optional tags list
58
+
59
+ Returns:
60
+ dict: JSON result from script
61
+ """
62
+ input_data = {
63
+ 'plan_path': str(plan_path),
64
+ 'action': action
65
+ }
66
+
67
+ if tags is not None:
68
+ input_data['tags'] = tags
69
+
70
+ input_json = json.dumps(input_data)
71
+
72
+ script_path = Path(__file__).parent / 'update-plan-metadata.py'
73
+
74
+ result = subprocess.run(
75
+ ['python3', str(script_path)],
76
+ input=input_json,
77
+ capture_output=True,
78
+ text=True
79
+ )
80
+
81
+ if result.returncode == 0:
82
+ return json.loads(result.stdout)
83
+ else:
84
+ return json.loads(result.stderr)
85
+
86
+ def test_01_parse_valid_yaml(self):
87
+ """Test 1: Parse valid YAML front matter"""
88
+ content = """---
89
+ seq: 001
90
+ title: "Test Plan"
91
+ status: "draft:0"
92
+ priority: "p2:中:可纳入后续迭代计划"
93
+ created_at: ""
94
+ updated_at: ""
95
+ tags: []
96
+ ---
97
+
98
+ # Test Plan
99
+
100
+ This is a test.
101
+ """
102
+ plan_path = self.create_test_plan('plan.001.test.md', content)
103
+
104
+ result = self.run_update_script(plan_path, 'start')
105
+
106
+ self.assertTrue(result['success'])
107
+ self.assertEqual(result['old_status'], 'draft:0')
108
+ self.assertEqual(result['new_status'], 'ongoing:2')
109
+
110
+ def test_02_status_transitions(self):
111
+ """Test 2: Status transitions work correctly"""
112
+ content = """---
113
+ seq: 002
114
+ title: "Status Test"
115
+ status: "draft:0"
116
+ ---
117
+
118
+ # Status Test
119
+ """
120
+ plan_path = self.create_test_plan('plan.002.test.md', content)
121
+
122
+ # Transition: draft → ongoing
123
+ result = self.run_update_script(plan_path, 'start')
124
+ self.assertEqual(result['new_status'], 'ongoing:2')
125
+
126
+ # Transition: ongoing → completed
127
+ result = self.run_update_script(plan_path, 'complete')
128
+ self.assertEqual(result['old_status'], 'ongoing:2')
129
+ self.assertEqual(result['new_status'], 'completed:3')
130
+
131
+ def test_03_created_at_set_once(self):
132
+ """Test 3: created_at set only on first execution"""
133
+ content = """---
134
+ seq: 003
135
+ title: "Timestamp Test"
136
+ status: "draft:0"
137
+ created_at: ""
138
+ updated_at: ""
139
+ ---
140
+
141
+ # Timestamp Test
142
+ """
143
+ plan_path = self.create_test_plan('plan.003.test.md', content)
144
+
145
+ # First execution
146
+ result1 = self.run_update_script(plan_path, 'start')
147
+ created_at_1 = result1['created_at']
148
+ updated_at_1 = result1['updated_at']
149
+
150
+ self.assertNotEqual(created_at_1, '')
151
+ self.assertNotEqual(updated_at_1, '')
152
+
153
+ # Wait a moment
154
+ time.sleep(1)
155
+
156
+ # Second execution
157
+ result2 = self.run_update_script(plan_path, 'complete')
158
+ created_at_2 = result2['created_at']
159
+ updated_at_2 = result2['updated_at']
160
+
161
+ # created_at should not change
162
+ self.assertEqual(created_at_1, created_at_2)
163
+
164
+ # updated_at should change
165
+ self.assertNotEqual(updated_at_1, updated_at_2)
166
+
167
+ def test_04_updated_at_always_updates(self):
168
+ """Test 4: updated_at updates on every execution"""
169
+ content = """---
170
+ seq: 004
171
+ title: "Update Test"
172
+ status: "draft:0"
173
+ created_at: "2026-01-01 10:00:00"
174
+ updated_at: "2026-01-01 10:00:00"
175
+ ---
176
+
177
+ # Update Test
178
+ """
179
+ plan_path = self.create_test_plan('plan.004.test.md', content)
180
+
181
+ time.sleep(1)
182
+
183
+ result = self.run_update_script(plan_path, 'start')
184
+
185
+ # updated_at should be newer than the original
186
+ self.assertNotEqual(result['updated_at'], '2026-01-01 10:00:00')
187
+
188
+ def test_05_merge_tags(self):
189
+ """Test 5: Merge tags without overwriting other fields"""
190
+ content = """---
191
+ seq: 005
192
+ title: "Tag Test"
193
+ status: "draft:0"
194
+ tags: []
195
+ ---
196
+
197
+ # Tag Test
198
+ """
199
+ plan_path = self.create_test_plan('plan.005.test.md', content)
200
+
201
+ result = self.run_update_script(plan_path, 'start', tags=['feature', 'backend'])
202
+
203
+ self.assertTrue(result['success'])
204
+ self.assertIn('feature', result['tags'])
205
+ self.assertIn('backend', result['tags'])
206
+
207
+ def test_06_handle_missing_fields(self):
208
+ """Test 6: Handle missing fields gracefully"""
209
+ content = """---
210
+ seq: 006
211
+ title: "Minimal Plan"
212
+ ---
213
+
214
+ # Minimal Plan
215
+ """
216
+ plan_path = self.create_test_plan('plan.006.test.md', content)
217
+
218
+ result = self.run_update_script(plan_path, 'start')
219
+
220
+ self.assertTrue(result['success'])
221
+ self.assertEqual(result['new_status'], 'ongoing:2')
222
+ self.assertNotEqual(result['created_at'], '')
223
+
224
+ def test_07_malformed_yaml_error(self):
225
+ """Test 7: Handle malformed YAML with clear error"""
226
+ content = """---
227
+ seq: 007
228
+ title: "Missing quote
229
+ status: draft:0
230
+ ---
231
+
232
+ # Bad YAML
233
+ """
234
+ plan_path = self.create_test_plan('plan.007.test.md', content)
235
+
236
+ result = self.run_update_script(plan_path, 'start')
237
+
238
+ self.assertFalse(result['success'])
239
+ self.assertIn('error', result)
240
+ self.assertIn('YAML', result['error'])
241
+
242
+ def test_08_preserve_markdown_body(self):
243
+ """Test 8: Preserve Markdown body unchanged"""
244
+ original_body = """
245
+ # Test Plan
246
+
247
+ ## 🎯 Goal
248
+ Do something important
249
+
250
+ ## 📋 Requirements
251
+ - Requirement 1
252
+ - Requirement 2
253
+
254
+ ## ✅ Verification
255
+ - [ ] Test 1
256
+ - [ ] Test 2
257
+ """
258
+
259
+ content = f"""---
260
+ seq: 008
261
+ title: "Body Preservation Test"
262
+ status: "draft:0"
263
+ ---
264
+ {original_body}"""
265
+
266
+ plan_path = self.create_test_plan('plan.008.test.md', content)
267
+
268
+ result = self.run_update_script(plan_path, 'start')
269
+
270
+ self.assertTrue(result['success'])
271
+
272
+ # Read file and check body
273
+ updated_content = plan_path.read_text(encoding='utf-8')
274
+ self.assertIn('## 🎯 Goal', updated_content)
275
+ self.assertIn('Do something important', updated_content)
276
+ self.assertIn('- Requirement 1', updated_content)
277
+ self.assertIn('- [ ] Test 1', updated_content)
278
+
279
+ def test_09_backwards_compatibility(self):
280
+ """Test 10: Backwards compatibility with old status format"""
281
+ content = """---
282
+ seq: 010
283
+ title: "Old Format Test"
284
+ status: pending
285
+ priority: medium
286
+ created: 2026-01-01 10:00:00
287
+ updated: 2026-01-01 10:00:00
288
+ ---
289
+
290
+ # Old Format Test
291
+ """
292
+ plan_path = self.create_test_plan('plan.010.test.md', content)
293
+
294
+ result = self.run_update_script(plan_path, 'start')
295
+
296
+ self.assertTrue(result['success'])
297
+ # Old 'pending' should convert to 'draft:0' first, then to 'ongoing:2'
298
+ self.assertEqual(result['new_status'], 'ongoing:2')
299
+
300
+ def test_10_missing_yaml_error(self):
301
+ """Test: Handle missing YAML front matter"""
302
+ content = """# No YAML Plan
303
+
304
+ This plan has no YAML front matter.
305
+ """
306
+ plan_path = self.create_test_plan('plan.999.no-yaml.md', content)
307
+
308
+ result = self.run_update_script(plan_path, 'start')
309
+
310
+ self.assertFalse(result['success'])
311
+ self.assertIn('No YAML front matter', result['error'])
312
+
313
+
314
+ def run_tests():
315
+ """Run all tests and report results"""
316
+ # Create test suite
317
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestPlanMetadataUpdater)
318
+
319
+ # Run tests with verbose output
320
+ runner = unittest.TextTestRunner(verbosity=2)
321
+ result = runner.run(suite)
322
+
323
+ # Return exit code
324
+ return 0 if result.wasSuccessful() else 1
325
+
326
+
327
+ if __name__ == '__main__':
328
+ sys.exit(run_tests())