claude-code-workflow 6.3.43 → 6.3.46
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.
- package/.claude/agents/tdd-developer.md +530 -0
- package/.claude/commands/ccw-coordinator.md +1042 -0
- package/.claude/commands/ccw.md +486 -0
- package/.claude/commands/issue/discover-by-prompt.md +5 -1
- package/.claude/commands/issue/discover.md +472 -468
- package/.claude/commands/issue/execute.md +580 -581
- package/.claude/commands/issue/new.md +417 -413
- package/.claude/commands/issue/plan.md +5 -1
- package/.claude/commands/issue/queue.md +445 -441
- package/.claude/commands/task/breakdown.md +207 -203
- package/.claude/commands/task/replan.md +440 -436
- package/.claude/commands/workflow/action-plan-verify.md +485 -447
- package/.claude/commands/workflow/brainstorm/artifacts.md +457 -453
- package/.claude/commands/workflow/brainstorm/auto-parallel.md +5 -1
- package/.claude/commands/workflow/brainstorm/synthesis.md +402 -398
- package/.claude/commands/workflow/clean.md +67 -35
- package/.claude/commands/workflow/debug-with-file.md +670 -666
- package/.claude/commands/workflow/debug.md +331 -327
- package/.claude/commands/workflow/develop-with-file.md +5 -1
- package/.claude/commands/workflow/execute.md +546 -498
- package/.claude/commands/workflow/lite-execute.md +44 -26
- package/.claude/commands/workflow/lite-fix.md +780 -730
- package/.claude/commands/workflow/lite-lite-lite.md +5 -1
- package/.claude/commands/workflow/lite-plan.md +87 -39
- package/.claude/commands/workflow/multi-cli-plan.md +572 -568
- package/.claude/commands/workflow/plan-verify.md +527 -0
- package/.claude/commands/workflow/plan.md +555 -551
- package/.claude/commands/workflow/replan.md +572 -515
- package/.claude/commands/workflow/review-fix.md +608 -610
- package/.claude/commands/workflow/session/complete.md +37 -14
- package/.claude/commands/workflow/session/solidify.md +303 -299
- package/.claude/commands/workflow/tdd-plan.md +630 -597
- package/.claude/commands/workflow/tdd-verify.md +391 -206
- package/.claude/commands/workflow/tools/conflict-resolution.md +24 -12
- package/.claude/commands/workflow/tools/task-generate-agent.md +583 -563
- package/.claude/commands/workflow/tools/task-generate-tdd.md +749 -517
- package/.claude/commands/workflow/ui-design/animation-extract.md +1154 -1150
- package/.claude/commands/workflow/ui-design/layout-extract.md +792 -788
- package/.claude/commands/workflow/ui-design/style-extract.md +777 -773
- package/.claude/skills/ccw-help/command.json +5 -5
- package/.claude/skills/ccw-help/scripts/analyze_commands.py +337 -337
- package/.claude/workflows/cli-templates/prompts/workflow-impl-plan-template.txt +1 -1
- package/ccw/dist/commands/issue.d.ts +4 -0
- package/ccw/dist/commands/issue.d.ts.map +1 -1
- package/ccw/dist/commands/issue.js +73 -6
- package/ccw/dist/commands/issue.js.map +1 -1
- package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/cli-routes.js +32 -28
- package/ccw/dist/core/routes/cli-routes.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts +10 -0
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.js +45 -0
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
- package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
- package/ccw/dist/tools/codex-lens.js +38 -11
- package/ccw/dist/tools/codex-lens.js.map +1 -1
- package/ccw/dist/tools/command-registry.d.ts +77 -0
- package/ccw/dist/tools/command-registry.d.ts.map +1 -0
- package/ccw/dist/tools/command-registry.js +265 -0
- package/ccw/dist/tools/command-registry.js.map +1 -0
- package/ccw/dist/tools/command-registry.test.d.ts +14 -0
- package/ccw/dist/tools/command-registry.test.d.ts.map +1 -0
- package/ccw/dist/tools/command-registry.test.js.map +1 -0
- package/ccw/dist/tools/index.d.ts +2 -0
- package/ccw/dist/tools/index.d.ts.map +1 -1
- package/ccw/dist/tools/index.js +2 -0
- package/ccw/dist/tools/index.js.map +1 -1
- package/ccw/src/commands/issue.ts +84 -6
- package/ccw/src/core/routes/cli-routes.ts +30 -25
- package/ccw/src/templates/dashboard-js/views/help.js +1 -1
- package/ccw/src/tools/claude-cli-tools.ts +50 -0
- package/ccw/src/tools/codex-lens.ts +40 -11
- package/ccw/src/tools/command-registry.test.ts +669 -0
- package/ccw/src/tools/command-registry.ts +308 -0
- package/ccw/src/tools/index.ts +4 -0
- package/package.json +1 -1
- package/.claude/skills/ccw/SKILL.md +0 -522
- package/.claude/skills/ccw/command.json +0 -641
|
@@ -1,337 +1,337 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Analyze all command/agent files and generate index files for ccw-help skill.
|
|
4
|
-
Outputs relative paths pointing to source files (no reference folder duplication).
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import os
|
|
8
|
-
import re
|
|
9
|
-
import json
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
from collections import defaultdict
|
|
12
|
-
from typing import Dict, List, Any
|
|
13
|
-
|
|
14
|
-
# Base paths
|
|
15
|
-
BASE_DIR = Path("D:/Claude_dms3/.claude")
|
|
16
|
-
COMMANDS_DIR = BASE_DIR / "commands"
|
|
17
|
-
AGENTS_DIR = BASE_DIR / "agents"
|
|
18
|
-
SKILL_DIR = BASE_DIR / "skills" / "ccw-help"
|
|
19
|
-
INDEX_DIR = SKILL_DIR / "index"
|
|
20
|
-
|
|
21
|
-
def parse_frontmatter(content: str) -> Dict[str, Any]:
|
|
22
|
-
"""Extract YAML frontmatter from markdown content."""
|
|
23
|
-
frontmatter = {}
|
|
24
|
-
if content.startswith('---'):
|
|
25
|
-
lines = content.split('\n')
|
|
26
|
-
for i, line in enumerate(lines[1:], 1):
|
|
27
|
-
if line.strip() == '---':
|
|
28
|
-
break
|
|
29
|
-
if ':' in line:
|
|
30
|
-
key, value = line.split(':', 1)
|
|
31
|
-
frontmatter[key.strip()] = value.strip().strip('"')
|
|
32
|
-
return frontmatter
|
|
33
|
-
|
|
34
|
-
def categorize_command(file_path: Path) -> tuple:
|
|
35
|
-
"""Determine category and subcategory from file path."""
|
|
36
|
-
parts = file_path.relative_to(COMMANDS_DIR).parts
|
|
37
|
-
|
|
38
|
-
if len(parts) == 1:
|
|
39
|
-
return "general", None
|
|
40
|
-
|
|
41
|
-
category = parts[0] # cli, memory, task, workflow
|
|
42
|
-
subcategory = parts[1].replace('.md', '') if len(parts) > 2 else None
|
|
43
|
-
|
|
44
|
-
return category, subcategory
|
|
45
|
-
|
|
46
|
-
def determine_usage_scenario(name: str, description: str, category: str) -> str:
|
|
47
|
-
"""Determine primary usage scenario for command."""
|
|
48
|
-
name_lower = name.lower()
|
|
49
|
-
|
|
50
|
-
if any(word in name_lower for word in ['plan', 'design', 'breakdown', 'brainstorm']):
|
|
51
|
-
return "planning"
|
|
52
|
-
if any(word in name_lower for word in ['implement', 'execute', 'generate', 'create', 'write']):
|
|
53
|
-
return "implementation"
|
|
54
|
-
if any(word in name_lower for word in ['test', 'tdd', 'verify', 'coverage']):
|
|
55
|
-
return "testing"
|
|
56
|
-
if any(word in name_lower for word in ['docs', 'documentation', 'memory']):
|
|
57
|
-
return "documentation"
|
|
58
|
-
if any(word in name_lower for word in ['session', 'resume', 'status', 'complete']):
|
|
59
|
-
return "session-management"
|
|
60
|
-
if any(word in name_lower for word in ['analyze', 'review', 'diagnosis']):
|
|
61
|
-
return "analysis"
|
|
62
|
-
return "general"
|
|
63
|
-
|
|
64
|
-
def determine_difficulty(name: str, description: str, category: str) -> str:
|
|
65
|
-
"""Determine difficulty level."""
|
|
66
|
-
name_lower = name.lower()
|
|
67
|
-
|
|
68
|
-
beginner_keywords = ['status', 'list', 'chat', 'analyze', 'version']
|
|
69
|
-
if any(word in name_lower for word in beginner_keywords):
|
|
70
|
-
return "Beginner"
|
|
71
|
-
|
|
72
|
-
advanced_keywords = ['tdd', 'conflict', 'agent', 'auto-parallel', 'coverage', 'synthesis']
|
|
73
|
-
if any(word in name_lower for word in advanced_keywords):
|
|
74
|
-
return "Advanced"
|
|
75
|
-
|
|
76
|
-
return "Intermediate"
|
|
77
|
-
|
|
78
|
-
def analyze_command_file(file_path: Path) -> Dict[str, Any]:
|
|
79
|
-
"""Analyze a single command file and extract metadata."""
|
|
80
|
-
with open(file_path, 'r', encoding='utf-8') as f:
|
|
81
|
-
content = f.read()
|
|
82
|
-
|
|
83
|
-
frontmatter = parse_frontmatter(content)
|
|
84
|
-
|
|
85
|
-
name = frontmatter.get('name', file_path.stem)
|
|
86
|
-
description = frontmatter.get('description', '')
|
|
87
|
-
argument_hint = frontmatter.get('argument-hint', '')
|
|
88
|
-
|
|
89
|
-
category, subcategory = categorize_command(file_path)
|
|
90
|
-
usage_scenario = determine_usage_scenario(name, description, category)
|
|
91
|
-
difficulty = determine_difficulty(name, description, category)
|
|
92
|
-
|
|
93
|
-
# Build relative path from INDEX_DIR (need to go up 3 levels: index -> ccw-help -> skills -> .claude)
|
|
94
|
-
# e.g., "../../../commands/workflow/lite-plan.md"
|
|
95
|
-
rel_from_base = file_path.relative_to(BASE_DIR)
|
|
96
|
-
rel_path = "../../../" + str(rel_from_base).replace('\\', '/')
|
|
97
|
-
|
|
98
|
-
# Build full command name
|
|
99
|
-
if ':' in name:
|
|
100
|
-
command_name = f"/{name}"
|
|
101
|
-
elif category == "general":
|
|
102
|
-
command_name = f"/{name}"
|
|
103
|
-
else:
|
|
104
|
-
if subcategory:
|
|
105
|
-
command_name = f"/{category}:{subcategory}:{name}"
|
|
106
|
-
else:
|
|
107
|
-
command_name = f"/{category}:{name}"
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
"name": name,
|
|
111
|
-
"command": command_name,
|
|
112
|
-
"description": description,
|
|
113
|
-
"arguments": argument_hint,
|
|
114
|
-
"category": category,
|
|
115
|
-
"subcategory": subcategory,
|
|
116
|
-
"usage_scenario": usage_scenario,
|
|
117
|
-
"difficulty": difficulty,
|
|
118
|
-
"source": rel_path # Relative from index/ dir (e.g., "../../../commands/workflow/...")
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
def analyze_agent_file(file_path: Path) -> Dict[str, Any]:
|
|
122
|
-
"""Analyze a single agent file and extract metadata."""
|
|
123
|
-
with open(file_path, 'r', encoding='utf-8') as f:
|
|
124
|
-
content = f.read()
|
|
125
|
-
|
|
126
|
-
frontmatter = parse_frontmatter(content)
|
|
127
|
-
|
|
128
|
-
name = frontmatter.get('name', file_path.stem)
|
|
129
|
-
description = frontmatter.get('description', '')
|
|
130
|
-
|
|
131
|
-
# Build relative path from INDEX_DIR (need to go up 3 levels)
|
|
132
|
-
# e.g., "../../../agents/code-developer.md"
|
|
133
|
-
rel_from_base = file_path.relative_to(BASE_DIR)
|
|
134
|
-
rel_path = "../../../" + str(rel_from_base).replace('\\', '/')
|
|
135
|
-
|
|
136
|
-
return {
|
|
137
|
-
"name": name,
|
|
138
|
-
"description": description,
|
|
139
|
-
"source": rel_path # Relative from index/ dir (e.g., "../../../agents/...")
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
def build_command_relationships() -> Dict[str, Any]:
|
|
143
|
-
"""Build command relationship mappings."""
|
|
144
|
-
return {
|
|
145
|
-
"workflow:plan": {
|
|
146
|
-
"calls_internally": ["workflow:session:start", "workflow:tools:context-gather", "workflow:tools:conflict-resolution", "workflow:tools:task-generate-agent"],
|
|
147
|
-
"next_steps": ["workflow:
|
|
148
|
-
"alternatives": ["workflow:tdd-plan"],
|
|
149
|
-
"prerequisites": []
|
|
150
|
-
},
|
|
151
|
-
"workflow:tdd-plan": {
|
|
152
|
-
"calls_internally": ["workflow:session:start", "workflow:tools:context-gather", "workflow:tools:task-generate-tdd"],
|
|
153
|
-
"next_steps": ["workflow:tdd-verify", "workflow:status", "workflow:execute"],
|
|
154
|
-
"alternatives": ["workflow:plan"],
|
|
155
|
-
"prerequisites": []
|
|
156
|
-
},
|
|
157
|
-
"workflow:execute": {
|
|
158
|
-
"prerequisites": ["workflow:plan", "workflow:tdd-plan"],
|
|
159
|
-
"related": ["workflow:status", "workflow:resume"],
|
|
160
|
-
"next_steps": ["workflow:review", "workflow:tdd-verify"]
|
|
161
|
-
},
|
|
162
|
-
"workflow:
|
|
163
|
-
"prerequisites": ["workflow:plan"],
|
|
164
|
-
"next_steps": ["workflow:execute"],
|
|
165
|
-
"related": ["workflow:status"]
|
|
166
|
-
},
|
|
167
|
-
"workflow:tdd-verify": {
|
|
168
|
-
"prerequisites": ["workflow:execute"],
|
|
169
|
-
"related": ["workflow:tools:tdd-coverage-analysis"]
|
|
170
|
-
},
|
|
171
|
-
"workflow:session:start": {
|
|
172
|
-
"next_steps": ["workflow:plan", "workflow:execute"],
|
|
173
|
-
"related": ["workflow:session:list", "workflow:session:resume"]
|
|
174
|
-
},
|
|
175
|
-
"workflow:session:resume": {
|
|
176
|
-
"alternatives": ["workflow:resume"],
|
|
177
|
-
"related": ["workflow:session:list", "workflow:status"]
|
|
178
|
-
},
|
|
179
|
-
"workflow:lite-plan": {
|
|
180
|
-
"calls_internally": ["workflow:lite-execute"],
|
|
181
|
-
"next_steps": ["workflow:lite-execute", "workflow:status"],
|
|
182
|
-
"alternatives": ["workflow:plan"],
|
|
183
|
-
"prerequisites": []
|
|
184
|
-
},
|
|
185
|
-
"workflow:lite-fix": {
|
|
186
|
-
"next_steps": ["workflow:lite-execute", "workflow:status"],
|
|
187
|
-
"alternatives": ["workflow:lite-plan"],
|
|
188
|
-
"related": ["workflow:test-cycle-execute"]
|
|
189
|
-
},
|
|
190
|
-
"workflow:lite-execute": {
|
|
191
|
-
"prerequisites": ["workflow:lite-plan", "workflow:lite-fix"],
|
|
192
|
-
"related": ["workflow:execute", "workflow:status"]
|
|
193
|
-
},
|
|
194
|
-
"workflow:review-session-cycle": {
|
|
195
|
-
"prerequisites": ["workflow:execute"],
|
|
196
|
-
"next_steps": ["workflow:review-fix"],
|
|
197
|
-
"related": ["workflow:review-module-cycle"]
|
|
198
|
-
},
|
|
199
|
-
"workflow:review-fix": {
|
|
200
|
-
"prerequisites": ["workflow:review-module-cycle", "workflow:review-session-cycle"],
|
|
201
|
-
"related": ["workflow:test-cycle-execute"]
|
|
202
|
-
},
|
|
203
|
-
"memory:docs": {
|
|
204
|
-
"calls_internally": ["workflow:session:start", "workflow:tools:context-gather"],
|
|
205
|
-
"next_steps": ["workflow:execute"]
|
|
206
|
-
},
|
|
207
|
-
"memory:skill-memory": {
|
|
208
|
-
"next_steps": ["workflow:plan", "cli:analyze"],
|
|
209
|
-
"related": ["memory:load-skill-memory"]
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
def identify_essential_commands(all_commands: List[Dict]) -> List[Dict]:
|
|
214
|
-
"""Identify the most essential commands for beginners."""
|
|
215
|
-
essential_names = [
|
|
216
|
-
"workflow:lite-plan", "workflow:lite-fix", "workflow:plan",
|
|
217
|
-
"workflow:execute", "workflow:status", "workflow:session:start",
|
|
218
|
-
"workflow:review-session-cycle", "cli:analyze", "cli:chat",
|
|
219
|
-
"memory:docs", "workflow:brainstorm:artifacts",
|
|
220
|
-
"workflow:
|
|
221
|
-
]
|
|
222
|
-
|
|
223
|
-
essential = []
|
|
224
|
-
for cmd in all_commands:
|
|
225
|
-
cmd_name = cmd['command'].lstrip('/')
|
|
226
|
-
if cmd_name in essential_names:
|
|
227
|
-
essential.append(cmd)
|
|
228
|
-
|
|
229
|
-
essential.sort(key=lambda x: essential_names.index(x['command'].lstrip('/')) if x['command'].lstrip('/') in essential_names else 999)
|
|
230
|
-
return essential[:14]
|
|
231
|
-
|
|
232
|
-
def main():
|
|
233
|
-
"""Main analysis function."""
|
|
234
|
-
import sys
|
|
235
|
-
import io
|
|
236
|
-
|
|
237
|
-
if sys.platform == 'win32':
|
|
238
|
-
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
239
|
-
|
|
240
|
-
print("=== CCW-Help Index Rebuild ===\n")
|
|
241
|
-
|
|
242
|
-
# Analyze command files
|
|
243
|
-
print("=== Analyzing Command Files ===")
|
|
244
|
-
command_files = list(COMMANDS_DIR.rglob("*.md"))
|
|
245
|
-
print(f"Found {len(command_files)} command files")
|
|
246
|
-
|
|
247
|
-
all_commands = []
|
|
248
|
-
for cmd_file in sorted(command_files):
|
|
249
|
-
try:
|
|
250
|
-
metadata = analyze_command_file(cmd_file)
|
|
251
|
-
all_commands.append(metadata)
|
|
252
|
-
print(f" OK {metadata['command']}")
|
|
253
|
-
except Exception as e:
|
|
254
|
-
print(f" ERROR analyzing {cmd_file}: {e}")
|
|
255
|
-
|
|
256
|
-
# Analyze agent files
|
|
257
|
-
print("\n=== Analyzing Agent Files ===")
|
|
258
|
-
agent_files = list(AGENTS_DIR.rglob("*.md"))
|
|
259
|
-
print(f"Found {len(agent_files)} agent files")
|
|
260
|
-
|
|
261
|
-
all_agents = []
|
|
262
|
-
for agent_file in sorted(agent_files):
|
|
263
|
-
try:
|
|
264
|
-
metadata = analyze_agent_file(agent_file)
|
|
265
|
-
all_agents.append(metadata)
|
|
266
|
-
print(f" OK {metadata['name']}")
|
|
267
|
-
except Exception as e:
|
|
268
|
-
print(f" ERROR analyzing {agent_file}: {e}")
|
|
269
|
-
|
|
270
|
-
print(f"\nAnalyzed {len(all_commands)} commands, {len(all_agents)} agents")
|
|
271
|
-
|
|
272
|
-
# Generate index files
|
|
273
|
-
INDEX_DIR.mkdir(parents=True, exist_ok=True)
|
|
274
|
-
|
|
275
|
-
# 1. all-commands.json
|
|
276
|
-
all_commands_path = INDEX_DIR / "all-commands.json"
|
|
277
|
-
with open(all_commands_path, 'w', encoding='utf-8') as f:
|
|
278
|
-
json.dump(all_commands, f, indent=2, ensure_ascii=False)
|
|
279
|
-
print(f"\nOK Generated {all_commands_path.name} ({os.path.getsize(all_commands_path)} bytes)")
|
|
280
|
-
|
|
281
|
-
# 2. all-agents.json
|
|
282
|
-
all_agents_path = INDEX_DIR / "all-agents.json"
|
|
283
|
-
with open(all_agents_path, 'w', encoding='utf-8') as f:
|
|
284
|
-
json.dump(all_agents, f, indent=2, ensure_ascii=False)
|
|
285
|
-
print(f"OK Generated {all_agents_path.name} ({os.path.getsize(all_agents_path)} bytes)")
|
|
286
|
-
|
|
287
|
-
# 3. by-category.json
|
|
288
|
-
by_category = defaultdict(lambda: defaultdict(list))
|
|
289
|
-
for cmd in all_commands:
|
|
290
|
-
cat = cmd['category']
|
|
291
|
-
subcat = cmd['subcategory'] or '_root'
|
|
292
|
-
by_category[cat][subcat].append(cmd)
|
|
293
|
-
|
|
294
|
-
by_category_path = INDEX_DIR / "by-category.json"
|
|
295
|
-
with open(by_category_path, 'w', encoding='utf-8') as f:
|
|
296
|
-
json.dump(dict(by_category), f, indent=2, ensure_ascii=False)
|
|
297
|
-
print(f"OK Generated {by_category_path.name} ({os.path.getsize(by_category_path)} bytes)")
|
|
298
|
-
|
|
299
|
-
# 4. by-use-case.json
|
|
300
|
-
by_use_case = defaultdict(list)
|
|
301
|
-
for cmd in all_commands:
|
|
302
|
-
by_use_case[cmd['usage_scenario']].append(cmd)
|
|
303
|
-
|
|
304
|
-
by_use_case_path = INDEX_DIR / "by-use-case.json"
|
|
305
|
-
with open(by_use_case_path, 'w', encoding='utf-8') as f:
|
|
306
|
-
json.dump(dict(by_use_case), f, indent=2, ensure_ascii=False)
|
|
307
|
-
print(f"OK Generated {by_use_case_path.name} ({os.path.getsize(by_use_case_path)} bytes)")
|
|
308
|
-
|
|
309
|
-
# 5. essential-commands.json
|
|
310
|
-
essential = identify_essential_commands(all_commands)
|
|
311
|
-
essential_path = INDEX_DIR / "essential-commands.json"
|
|
312
|
-
with open(essential_path, 'w', encoding='utf-8') as f:
|
|
313
|
-
json.dump(essential, f, indent=2, ensure_ascii=False)
|
|
314
|
-
print(f"OK Generated {essential_path.name} ({os.path.getsize(essential_path)} bytes)")
|
|
315
|
-
|
|
316
|
-
# 6. command-relationships.json
|
|
317
|
-
relationships = build_command_relationships()
|
|
318
|
-
relationships_path = INDEX_DIR / "command-relationships.json"
|
|
319
|
-
with open(relationships_path, 'w', encoding='utf-8') as f:
|
|
320
|
-
json.dump(relationships, f, indent=2, ensure_ascii=False)
|
|
321
|
-
print(f"OK Generated {relationships_path.name} ({os.path.getsize(relationships_path)} bytes)")
|
|
322
|
-
|
|
323
|
-
# Print summary
|
|
324
|
-
print("\n=== Summary ===")
|
|
325
|
-
print(f"Commands: {len(all_commands)}")
|
|
326
|
-
print(f"Agents: {len(all_agents)}")
|
|
327
|
-
print(f"Essential: {len(essential)}")
|
|
328
|
-
print(f"\nBy category:")
|
|
329
|
-
for cat in sorted(by_category.keys()):
|
|
330
|
-
total = sum(len(cmds) for cmds in by_category[cat].values())
|
|
331
|
-
print(f" {cat}: {total}")
|
|
332
|
-
|
|
333
|
-
print(f"\nIndex: {INDEX_DIR}")
|
|
334
|
-
print("=== Complete ===")
|
|
335
|
-
|
|
336
|
-
if __name__ == '__main__':
|
|
337
|
-
main()
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Analyze all command/agent files and generate index files for ccw-help skill.
|
|
4
|
+
Outputs relative paths pointing to source files (no reference folder duplication).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from collections import defaultdict
|
|
12
|
+
from typing import Dict, List, Any
|
|
13
|
+
|
|
14
|
+
# Base paths
|
|
15
|
+
BASE_DIR = Path("D:/Claude_dms3/.claude")
|
|
16
|
+
COMMANDS_DIR = BASE_DIR / "commands"
|
|
17
|
+
AGENTS_DIR = BASE_DIR / "agents"
|
|
18
|
+
SKILL_DIR = BASE_DIR / "skills" / "ccw-help"
|
|
19
|
+
INDEX_DIR = SKILL_DIR / "index"
|
|
20
|
+
|
|
21
|
+
def parse_frontmatter(content: str) -> Dict[str, Any]:
|
|
22
|
+
"""Extract YAML frontmatter from markdown content."""
|
|
23
|
+
frontmatter = {}
|
|
24
|
+
if content.startswith('---'):
|
|
25
|
+
lines = content.split('\n')
|
|
26
|
+
for i, line in enumerate(lines[1:], 1):
|
|
27
|
+
if line.strip() == '---':
|
|
28
|
+
break
|
|
29
|
+
if ':' in line:
|
|
30
|
+
key, value = line.split(':', 1)
|
|
31
|
+
frontmatter[key.strip()] = value.strip().strip('"')
|
|
32
|
+
return frontmatter
|
|
33
|
+
|
|
34
|
+
def categorize_command(file_path: Path) -> tuple:
|
|
35
|
+
"""Determine category and subcategory from file path."""
|
|
36
|
+
parts = file_path.relative_to(COMMANDS_DIR).parts
|
|
37
|
+
|
|
38
|
+
if len(parts) == 1:
|
|
39
|
+
return "general", None
|
|
40
|
+
|
|
41
|
+
category = parts[0] # cli, memory, task, workflow
|
|
42
|
+
subcategory = parts[1].replace('.md', '') if len(parts) > 2 else None
|
|
43
|
+
|
|
44
|
+
return category, subcategory
|
|
45
|
+
|
|
46
|
+
def determine_usage_scenario(name: str, description: str, category: str) -> str:
|
|
47
|
+
"""Determine primary usage scenario for command."""
|
|
48
|
+
name_lower = name.lower()
|
|
49
|
+
|
|
50
|
+
if any(word in name_lower for word in ['plan', 'design', 'breakdown', 'brainstorm']):
|
|
51
|
+
return "planning"
|
|
52
|
+
if any(word in name_lower for word in ['implement', 'execute', 'generate', 'create', 'write']):
|
|
53
|
+
return "implementation"
|
|
54
|
+
if any(word in name_lower for word in ['test', 'tdd', 'verify', 'coverage']):
|
|
55
|
+
return "testing"
|
|
56
|
+
if any(word in name_lower for word in ['docs', 'documentation', 'memory']):
|
|
57
|
+
return "documentation"
|
|
58
|
+
if any(word in name_lower for word in ['session', 'resume', 'status', 'complete']):
|
|
59
|
+
return "session-management"
|
|
60
|
+
if any(word in name_lower for word in ['analyze', 'review', 'diagnosis']):
|
|
61
|
+
return "analysis"
|
|
62
|
+
return "general"
|
|
63
|
+
|
|
64
|
+
def determine_difficulty(name: str, description: str, category: str) -> str:
|
|
65
|
+
"""Determine difficulty level."""
|
|
66
|
+
name_lower = name.lower()
|
|
67
|
+
|
|
68
|
+
beginner_keywords = ['status', 'list', 'chat', 'analyze', 'version']
|
|
69
|
+
if any(word in name_lower for word in beginner_keywords):
|
|
70
|
+
return "Beginner"
|
|
71
|
+
|
|
72
|
+
advanced_keywords = ['tdd', 'conflict', 'agent', 'auto-parallel', 'coverage', 'synthesis']
|
|
73
|
+
if any(word in name_lower for word in advanced_keywords):
|
|
74
|
+
return "Advanced"
|
|
75
|
+
|
|
76
|
+
return "Intermediate"
|
|
77
|
+
|
|
78
|
+
def analyze_command_file(file_path: Path) -> Dict[str, Any]:
|
|
79
|
+
"""Analyze a single command file and extract metadata."""
|
|
80
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
81
|
+
content = f.read()
|
|
82
|
+
|
|
83
|
+
frontmatter = parse_frontmatter(content)
|
|
84
|
+
|
|
85
|
+
name = frontmatter.get('name', file_path.stem)
|
|
86
|
+
description = frontmatter.get('description', '')
|
|
87
|
+
argument_hint = frontmatter.get('argument-hint', '')
|
|
88
|
+
|
|
89
|
+
category, subcategory = categorize_command(file_path)
|
|
90
|
+
usage_scenario = determine_usage_scenario(name, description, category)
|
|
91
|
+
difficulty = determine_difficulty(name, description, category)
|
|
92
|
+
|
|
93
|
+
# Build relative path from INDEX_DIR (need to go up 3 levels: index -> ccw-help -> skills -> .claude)
|
|
94
|
+
# e.g., "../../../commands/workflow/lite-plan.md"
|
|
95
|
+
rel_from_base = file_path.relative_to(BASE_DIR)
|
|
96
|
+
rel_path = "../../../" + str(rel_from_base).replace('\\', '/')
|
|
97
|
+
|
|
98
|
+
# Build full command name
|
|
99
|
+
if ':' in name:
|
|
100
|
+
command_name = f"/{name}"
|
|
101
|
+
elif category == "general":
|
|
102
|
+
command_name = f"/{name}"
|
|
103
|
+
else:
|
|
104
|
+
if subcategory:
|
|
105
|
+
command_name = f"/{category}:{subcategory}:{name}"
|
|
106
|
+
else:
|
|
107
|
+
command_name = f"/{category}:{name}"
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
"name": name,
|
|
111
|
+
"command": command_name,
|
|
112
|
+
"description": description,
|
|
113
|
+
"arguments": argument_hint,
|
|
114
|
+
"category": category,
|
|
115
|
+
"subcategory": subcategory,
|
|
116
|
+
"usage_scenario": usage_scenario,
|
|
117
|
+
"difficulty": difficulty,
|
|
118
|
+
"source": rel_path # Relative from index/ dir (e.g., "../../../commands/workflow/...")
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
def analyze_agent_file(file_path: Path) -> Dict[str, Any]:
|
|
122
|
+
"""Analyze a single agent file and extract metadata."""
|
|
123
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
124
|
+
content = f.read()
|
|
125
|
+
|
|
126
|
+
frontmatter = parse_frontmatter(content)
|
|
127
|
+
|
|
128
|
+
name = frontmatter.get('name', file_path.stem)
|
|
129
|
+
description = frontmatter.get('description', '')
|
|
130
|
+
|
|
131
|
+
# Build relative path from INDEX_DIR (need to go up 3 levels)
|
|
132
|
+
# e.g., "../../../agents/code-developer.md"
|
|
133
|
+
rel_from_base = file_path.relative_to(BASE_DIR)
|
|
134
|
+
rel_path = "../../../" + str(rel_from_base).replace('\\', '/')
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
"name": name,
|
|
138
|
+
"description": description,
|
|
139
|
+
"source": rel_path # Relative from index/ dir (e.g., "../../../agents/...")
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
def build_command_relationships() -> Dict[str, Any]:
|
|
143
|
+
"""Build command relationship mappings."""
|
|
144
|
+
return {
|
|
145
|
+
"workflow:plan": {
|
|
146
|
+
"calls_internally": ["workflow:session:start", "workflow:tools:context-gather", "workflow:tools:conflict-resolution", "workflow:tools:task-generate-agent"],
|
|
147
|
+
"next_steps": ["workflow:plan-verify", "workflow:status", "workflow:execute"],
|
|
148
|
+
"alternatives": ["workflow:tdd-plan"],
|
|
149
|
+
"prerequisites": []
|
|
150
|
+
},
|
|
151
|
+
"workflow:tdd-plan": {
|
|
152
|
+
"calls_internally": ["workflow:session:start", "workflow:tools:context-gather", "workflow:tools:task-generate-tdd"],
|
|
153
|
+
"next_steps": ["workflow:tdd-verify", "workflow:status", "workflow:execute"],
|
|
154
|
+
"alternatives": ["workflow:plan"],
|
|
155
|
+
"prerequisites": []
|
|
156
|
+
},
|
|
157
|
+
"workflow:execute": {
|
|
158
|
+
"prerequisites": ["workflow:plan", "workflow:tdd-plan"],
|
|
159
|
+
"related": ["workflow:status", "workflow:resume"],
|
|
160
|
+
"next_steps": ["workflow:review", "workflow:tdd-verify"]
|
|
161
|
+
},
|
|
162
|
+
"workflow:plan-verify": {
|
|
163
|
+
"prerequisites": ["workflow:plan"],
|
|
164
|
+
"next_steps": ["workflow:execute"],
|
|
165
|
+
"related": ["workflow:status"]
|
|
166
|
+
},
|
|
167
|
+
"workflow:tdd-verify": {
|
|
168
|
+
"prerequisites": ["workflow:execute"],
|
|
169
|
+
"related": ["workflow:tools:tdd-coverage-analysis"]
|
|
170
|
+
},
|
|
171
|
+
"workflow:session:start": {
|
|
172
|
+
"next_steps": ["workflow:plan", "workflow:execute"],
|
|
173
|
+
"related": ["workflow:session:list", "workflow:session:resume"]
|
|
174
|
+
},
|
|
175
|
+
"workflow:session:resume": {
|
|
176
|
+
"alternatives": ["workflow:resume"],
|
|
177
|
+
"related": ["workflow:session:list", "workflow:status"]
|
|
178
|
+
},
|
|
179
|
+
"workflow:lite-plan": {
|
|
180
|
+
"calls_internally": ["workflow:lite-execute"],
|
|
181
|
+
"next_steps": ["workflow:lite-execute", "workflow:status"],
|
|
182
|
+
"alternatives": ["workflow:plan"],
|
|
183
|
+
"prerequisites": []
|
|
184
|
+
},
|
|
185
|
+
"workflow:lite-fix": {
|
|
186
|
+
"next_steps": ["workflow:lite-execute", "workflow:status"],
|
|
187
|
+
"alternatives": ["workflow:lite-plan"],
|
|
188
|
+
"related": ["workflow:test-cycle-execute"]
|
|
189
|
+
},
|
|
190
|
+
"workflow:lite-execute": {
|
|
191
|
+
"prerequisites": ["workflow:lite-plan", "workflow:lite-fix"],
|
|
192
|
+
"related": ["workflow:execute", "workflow:status"]
|
|
193
|
+
},
|
|
194
|
+
"workflow:review-session-cycle": {
|
|
195
|
+
"prerequisites": ["workflow:execute"],
|
|
196
|
+
"next_steps": ["workflow:review-fix"],
|
|
197
|
+
"related": ["workflow:review-module-cycle"]
|
|
198
|
+
},
|
|
199
|
+
"workflow:review-fix": {
|
|
200
|
+
"prerequisites": ["workflow:review-module-cycle", "workflow:review-session-cycle"],
|
|
201
|
+
"related": ["workflow:test-cycle-execute"]
|
|
202
|
+
},
|
|
203
|
+
"memory:docs": {
|
|
204
|
+
"calls_internally": ["workflow:session:start", "workflow:tools:context-gather"],
|
|
205
|
+
"next_steps": ["workflow:execute"]
|
|
206
|
+
},
|
|
207
|
+
"memory:skill-memory": {
|
|
208
|
+
"next_steps": ["workflow:plan", "cli:analyze"],
|
|
209
|
+
"related": ["memory:load-skill-memory"]
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
def identify_essential_commands(all_commands: List[Dict]) -> List[Dict]:
|
|
214
|
+
"""Identify the most essential commands for beginners."""
|
|
215
|
+
essential_names = [
|
|
216
|
+
"workflow:lite-plan", "workflow:lite-fix", "workflow:plan",
|
|
217
|
+
"workflow:execute", "workflow:status", "workflow:session:start",
|
|
218
|
+
"workflow:review-session-cycle", "cli:analyze", "cli:chat",
|
|
219
|
+
"memory:docs", "workflow:brainstorm:artifacts",
|
|
220
|
+
"workflow:plan-verify", "workflow:resume", "version"
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
essential = []
|
|
224
|
+
for cmd in all_commands:
|
|
225
|
+
cmd_name = cmd['command'].lstrip('/')
|
|
226
|
+
if cmd_name in essential_names:
|
|
227
|
+
essential.append(cmd)
|
|
228
|
+
|
|
229
|
+
essential.sort(key=lambda x: essential_names.index(x['command'].lstrip('/')) if x['command'].lstrip('/') in essential_names else 999)
|
|
230
|
+
return essential[:14]
|
|
231
|
+
|
|
232
|
+
def main():
|
|
233
|
+
"""Main analysis function."""
|
|
234
|
+
import sys
|
|
235
|
+
import io
|
|
236
|
+
|
|
237
|
+
if sys.platform == 'win32':
|
|
238
|
+
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
239
|
+
|
|
240
|
+
print("=== CCW-Help Index Rebuild ===\n")
|
|
241
|
+
|
|
242
|
+
# Analyze command files
|
|
243
|
+
print("=== Analyzing Command Files ===")
|
|
244
|
+
command_files = list(COMMANDS_DIR.rglob("*.md"))
|
|
245
|
+
print(f"Found {len(command_files)} command files")
|
|
246
|
+
|
|
247
|
+
all_commands = []
|
|
248
|
+
for cmd_file in sorted(command_files):
|
|
249
|
+
try:
|
|
250
|
+
metadata = analyze_command_file(cmd_file)
|
|
251
|
+
all_commands.append(metadata)
|
|
252
|
+
print(f" OK {metadata['command']}")
|
|
253
|
+
except Exception as e:
|
|
254
|
+
print(f" ERROR analyzing {cmd_file}: {e}")
|
|
255
|
+
|
|
256
|
+
# Analyze agent files
|
|
257
|
+
print("\n=== Analyzing Agent Files ===")
|
|
258
|
+
agent_files = list(AGENTS_DIR.rglob("*.md"))
|
|
259
|
+
print(f"Found {len(agent_files)} agent files")
|
|
260
|
+
|
|
261
|
+
all_agents = []
|
|
262
|
+
for agent_file in sorted(agent_files):
|
|
263
|
+
try:
|
|
264
|
+
metadata = analyze_agent_file(agent_file)
|
|
265
|
+
all_agents.append(metadata)
|
|
266
|
+
print(f" OK {metadata['name']}")
|
|
267
|
+
except Exception as e:
|
|
268
|
+
print(f" ERROR analyzing {agent_file}: {e}")
|
|
269
|
+
|
|
270
|
+
print(f"\nAnalyzed {len(all_commands)} commands, {len(all_agents)} agents")
|
|
271
|
+
|
|
272
|
+
# Generate index files
|
|
273
|
+
INDEX_DIR.mkdir(parents=True, exist_ok=True)
|
|
274
|
+
|
|
275
|
+
# 1. all-commands.json
|
|
276
|
+
all_commands_path = INDEX_DIR / "all-commands.json"
|
|
277
|
+
with open(all_commands_path, 'w', encoding='utf-8') as f:
|
|
278
|
+
json.dump(all_commands, f, indent=2, ensure_ascii=False)
|
|
279
|
+
print(f"\nOK Generated {all_commands_path.name} ({os.path.getsize(all_commands_path)} bytes)")
|
|
280
|
+
|
|
281
|
+
# 2. all-agents.json
|
|
282
|
+
all_agents_path = INDEX_DIR / "all-agents.json"
|
|
283
|
+
with open(all_agents_path, 'w', encoding='utf-8') as f:
|
|
284
|
+
json.dump(all_agents, f, indent=2, ensure_ascii=False)
|
|
285
|
+
print(f"OK Generated {all_agents_path.name} ({os.path.getsize(all_agents_path)} bytes)")
|
|
286
|
+
|
|
287
|
+
# 3. by-category.json
|
|
288
|
+
by_category = defaultdict(lambda: defaultdict(list))
|
|
289
|
+
for cmd in all_commands:
|
|
290
|
+
cat = cmd['category']
|
|
291
|
+
subcat = cmd['subcategory'] or '_root'
|
|
292
|
+
by_category[cat][subcat].append(cmd)
|
|
293
|
+
|
|
294
|
+
by_category_path = INDEX_DIR / "by-category.json"
|
|
295
|
+
with open(by_category_path, 'w', encoding='utf-8') as f:
|
|
296
|
+
json.dump(dict(by_category), f, indent=2, ensure_ascii=False)
|
|
297
|
+
print(f"OK Generated {by_category_path.name} ({os.path.getsize(by_category_path)} bytes)")
|
|
298
|
+
|
|
299
|
+
# 4. by-use-case.json
|
|
300
|
+
by_use_case = defaultdict(list)
|
|
301
|
+
for cmd in all_commands:
|
|
302
|
+
by_use_case[cmd['usage_scenario']].append(cmd)
|
|
303
|
+
|
|
304
|
+
by_use_case_path = INDEX_DIR / "by-use-case.json"
|
|
305
|
+
with open(by_use_case_path, 'w', encoding='utf-8') as f:
|
|
306
|
+
json.dump(dict(by_use_case), f, indent=2, ensure_ascii=False)
|
|
307
|
+
print(f"OK Generated {by_use_case_path.name} ({os.path.getsize(by_use_case_path)} bytes)")
|
|
308
|
+
|
|
309
|
+
# 5. essential-commands.json
|
|
310
|
+
essential = identify_essential_commands(all_commands)
|
|
311
|
+
essential_path = INDEX_DIR / "essential-commands.json"
|
|
312
|
+
with open(essential_path, 'w', encoding='utf-8') as f:
|
|
313
|
+
json.dump(essential, f, indent=2, ensure_ascii=False)
|
|
314
|
+
print(f"OK Generated {essential_path.name} ({os.path.getsize(essential_path)} bytes)")
|
|
315
|
+
|
|
316
|
+
# 6. command-relationships.json
|
|
317
|
+
relationships = build_command_relationships()
|
|
318
|
+
relationships_path = INDEX_DIR / "command-relationships.json"
|
|
319
|
+
with open(relationships_path, 'w', encoding='utf-8') as f:
|
|
320
|
+
json.dump(relationships, f, indent=2, ensure_ascii=False)
|
|
321
|
+
print(f"OK Generated {relationships_path.name} ({os.path.getsize(relationships_path)} bytes)")
|
|
322
|
+
|
|
323
|
+
# Print summary
|
|
324
|
+
print("\n=== Summary ===")
|
|
325
|
+
print(f"Commands: {len(all_commands)}")
|
|
326
|
+
print(f"Agents: {len(all_agents)}")
|
|
327
|
+
print(f"Essential: {len(essential)}")
|
|
328
|
+
print(f"\nBy category:")
|
|
329
|
+
for cat in sorted(by_category.keys()):
|
|
330
|
+
total = sum(len(cmds) for cmds in by_category[cat].values())
|
|
331
|
+
print(f" {cat}: {total}")
|
|
332
|
+
|
|
333
|
+
print(f"\nIndex: {INDEX_DIR}")
|
|
334
|
+
print("=== Complete ===")
|
|
335
|
+
|
|
336
|
+
if __name__ == '__main__':
|
|
337
|
+
main()
|