codingbuddy-rules 4.5.0 → 5.0.0
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/.ai-rules/adapters/antigravity.md +6 -6
- package/.ai-rules/adapters/claude-code.md +68 -4
- package/.ai-rules/adapters/codex.md +5 -5
- package/.ai-rules/adapters/cursor.md +2 -2
- package/.ai-rules/adapters/kiro.md +8 -8
- package/.ai-rules/adapters/opencode.md +7 -7
- package/.ai-rules/adapters/q.md +2 -2
- package/.ai-rules/agents/README.md +66 -16
- package/.ai-rules/agents/accessibility-specialist.json +2 -1
- package/.ai-rules/agents/act-mode.json +2 -1
- package/.ai-rules/agents/agent-architect.json +8 -7
- package/.ai-rules/agents/ai-ml-engineer.json +1 -0
- package/.ai-rules/agents/architecture-specialist.json +1 -0
- package/.ai-rules/agents/auto-mode.json +4 -2
- package/.ai-rules/agents/backend-developer.json +1 -0
- package/.ai-rules/agents/code-quality-specialist.json +1 -0
- package/.ai-rules/agents/code-reviewer.json +65 -64
- package/.ai-rules/agents/data-engineer.json +8 -7
- package/.ai-rules/agents/data-scientist.json +10 -9
- package/.ai-rules/agents/devops-engineer.json +1 -0
- package/.ai-rules/agents/documentation-specialist.json +1 -0
- package/.ai-rules/agents/eval-mode.json +20 -19
- package/.ai-rules/agents/event-architecture-specialist.json +1 -0
- package/.ai-rules/agents/frontend-developer.json +1 -0
- package/.ai-rules/agents/i18n-specialist.json +2 -1
- package/.ai-rules/agents/integration-specialist.json +1 -0
- package/.ai-rules/agents/migration-specialist.json +1 -0
- package/.ai-rules/agents/mobile-developer.json +8 -7
- package/.ai-rules/agents/observability-specialist.json +1 -0
- package/.ai-rules/agents/parallel-orchestrator.json +346 -0
- package/.ai-rules/agents/performance-specialist.json +1 -0
- package/.ai-rules/agents/plan-mode.json +3 -1
- package/.ai-rules/agents/plan-reviewer.json +208 -0
- package/.ai-rules/agents/platform-engineer.json +1 -0
- package/.ai-rules/agents/security-engineer.json +9 -8
- package/.ai-rules/agents/security-specialist.json +2 -1
- package/.ai-rules/agents/seo-specialist.json +1 -0
- package/.ai-rules/agents/software-engineer.json +1 -0
- package/.ai-rules/agents/solution-architect.json +11 -10
- package/.ai-rules/agents/systems-developer.json +9 -8
- package/.ai-rules/agents/technical-planner.json +11 -10
- package/.ai-rules/agents/test-engineer.json +7 -6
- package/.ai-rules/agents/test-strategy-specialist.json +1 -0
- package/.ai-rules/agents/tooling-engineer.json +4 -3
- package/.ai-rules/agents/ui-ux-designer.json +1 -0
- package/.ai-rules/keyword-modes.json +4 -4
- package/.ai-rules/rules/clarification-guide.md +14 -14
- package/.ai-rules/rules/core.md +73 -0
- package/.ai-rules/rules/parallel-execution.md +217 -0
- package/.ai-rules/skills/README.md +23 -1
- package/.ai-rules/skills/agent-design/SKILL.md +5 -0
- package/.ai-rules/skills/agent-design/examples/agent-template.json +58 -0
- package/.ai-rules/skills/agent-design/references/expertise-guidelines.md +112 -0
- package/.ai-rules/skills/agent-discussion/SKILL.md +199 -0
- package/.ai-rules/skills/agent-discussion-panel/SKILL.md +448 -0
- package/.ai-rules/skills/api-design/SKILL.md +5 -0
- package/.ai-rules/skills/api-design/examples/error-response.json +159 -0
- package/.ai-rules/skills/api-design/examples/openapi-template.yaml +393 -0
- package/.ai-rules/skills/build-fix/SKILL.md +234 -0
- package/.ai-rules/skills/code-explanation/SKILL.md +4 -0
- package/.ai-rules/skills/context-management/SKILL.md +1 -0
- package/.ai-rules/skills/cost-budget/SKILL.md +348 -0
- package/.ai-rules/skills/cross-repo-issues/SKILL.md +257 -0
- package/.ai-rules/skills/database-migration/SKILL.md +1 -0
- package/.ai-rules/skills/deepsearch/SKILL.md +214 -0
- package/.ai-rules/skills/deployment-checklist/SKILL.md +1 -0
- package/.ai-rules/skills/error-analysis/SKILL.md +1 -0
- package/.ai-rules/skills/finishing-a-development-branch/SKILL.md +281 -0
- package/.ai-rules/skills/frontend-design/SKILL.md +5 -0
- package/.ai-rules/skills/frontend-design/examples/component-template.tsx +203 -0
- package/.ai-rules/skills/frontend-design/references/css-patterns.md +243 -0
- package/.ai-rules/skills/git-master/SKILL.md +358 -0
- package/.ai-rules/skills/incident-response/SKILL.md +1 -0
- package/.ai-rules/skills/legacy-modernization/SKILL.md +1 -0
- package/.ai-rules/skills/mcp-builder/SKILL.md +7 -0
- package/.ai-rules/skills/mcp-builder/examples/resource-example.ts +233 -0
- package/.ai-rules/skills/mcp-builder/examples/tool-example.ts +203 -0
- package/.ai-rules/skills/mcp-builder/references/protocol-spec.md +215 -0
- package/.ai-rules/skills/performance-optimization/SKILL.md +3 -0
- package/.ai-rules/skills/plan-and-review/SKILL.md +115 -0
- package/.ai-rules/skills/pr-all-in-one/SKILL.md +15 -13
- package/.ai-rules/skills/pr-all-in-one/configuration-guide.md +7 -7
- package/.ai-rules/skills/pr-all-in-one/pr-templates.md +10 -10
- package/.ai-rules/skills/pr-review/SKILL.md +4 -0
- package/.ai-rules/skills/receiving-code-review/SKILL.md +347 -0
- package/.ai-rules/skills/refactoring/SKILL.md +1 -0
- package/.ai-rules/skills/requesting-code-review/SKILL.md +348 -0
- package/.ai-rules/skills/rule-authoring/SKILL.md +5 -0
- package/.ai-rules/skills/rule-authoring/examples/rule-template.md +142 -0
- package/.ai-rules/skills/rule-authoring/examples/trigger-patterns.md +126 -0
- package/.ai-rules/skills/security-audit/SKILL.md +4 -0
- package/.ai-rules/skills/skill-creator/SKILL.md +461 -0
- package/.ai-rules/skills/skill-creator/agents/analyzer.md +206 -0
- package/.ai-rules/skills/skill-creator/agents/comparator.md +167 -0
- package/.ai-rules/skills/skill-creator/agents/grader.md +152 -0
- package/.ai-rules/skills/skill-creator/assets/eval_review.html +289 -0
- package/.ai-rules/skills/skill-creator/assets/skill-template.md +43 -0
- package/.ai-rules/skills/skill-creator/eval-viewer/generate_review.py +496 -0
- package/.ai-rules/skills/skill-creator/references/frontmatter-guide.md +632 -0
- package/.ai-rules/skills/skill-creator/references/multi-tool-compat.md +480 -0
- package/.ai-rules/skills/skill-creator/references/schemas.md +784 -0
- package/.ai-rules/skills/skill-creator/scripts/aggregate_benchmark.py +302 -0
- package/.ai-rules/skills/skill-creator/scripts/init_skill.sh +196 -0
- package/.ai-rules/skills/skill-creator/scripts/run_loop.py +327 -0
- package/.ai-rules/skills/systematic-debugging/SKILL.md +1 -0
- package/.ai-rules/skills/tech-debt/SKILL.md +1 -0
- package/.ai-rules/skills/test-coverage-gate/SKILL.md +303 -0
- package/.ai-rules/skills/tmux-master/SKILL.md +491 -0
- package/.ai-rules/skills/using-git-worktrees/SKILL.md +368 -0
- package/.ai-rules/skills/verification-before-completion/SKILL.md +234 -0
- package/.ai-rules/skills/widget-slot-architecture/SKILL.md +6 -0
- package/.ai-rules/skills/widget-slot-architecture/examples/parallel-route-setup.tsx +206 -0
- package/.ai-rules/skills/widget-slot-architecture/examples/widget-component.tsx +250 -0
- package/.ai-rules/skills/writing-plans/SKILL.md +78 -0
- package/bin/cli.js +178 -0
- package/lib/init/detect-stack.js +148 -0
- package/lib/init/generate-config.js +31 -0
- package/lib/init/index.js +86 -0
- package/lib/init/prompt.js +60 -0
- package/lib/init/scaffold.js +67 -0
- package/lib/init/suggest-agent.js +46 -0
- package/package.json +10 -2
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Aggregate benchmark results from an iteration directory.
|
|
3
|
+
|
|
4
|
+
Reads grading.json and timing.json from each eval-N/{with_skill,without_skill}/
|
|
5
|
+
subdirectory and produces benchmark.json + benchmark.md in the iteration directory.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python aggregate_benchmark.py <iteration-dir> --skill-name <name>
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
python aggregate_benchmark.py workspace/iteration-1 --skill-name test-driven-development
|
|
12
|
+
|
|
13
|
+
Requirements:
|
|
14
|
+
Python 3.8+ (standard library only)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import argparse
|
|
20
|
+
import json
|
|
21
|
+
import math
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any, Dict, List, Optional
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
|
|
28
|
+
parser = argparse.ArgumentParser(
|
|
29
|
+
description="Aggregate benchmark results from a skill evaluation iteration.",
|
|
30
|
+
epilog=(
|
|
31
|
+
"Example:\n"
|
|
32
|
+
" python aggregate_benchmark.py workspace/iteration-1 "
|
|
33
|
+
"--skill-name test-driven-development"
|
|
34
|
+
),
|
|
35
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
36
|
+
)
|
|
37
|
+
parser.add_argument(
|
|
38
|
+
"iteration_dir",
|
|
39
|
+
type=str,
|
|
40
|
+
help="Path to the iteration directory (e.g. workspace/iteration-1)",
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--skill-name",
|
|
44
|
+
required=True,
|
|
45
|
+
help="Skill name in kebab-case (e.g. test-driven-development)",
|
|
46
|
+
)
|
|
47
|
+
return parser.parse_args(argv)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _load_json(path: Path) -> Optional[Dict[str, Any]]:
|
|
51
|
+
"""Load a JSON file, returning None on failure."""
|
|
52
|
+
try:
|
|
53
|
+
with open(path, encoding="utf-8") as f:
|
|
54
|
+
return json.load(f)
|
|
55
|
+
except FileNotFoundError:
|
|
56
|
+
print(f"[WARN] File not found, skipping: {path}", file=sys.stderr)
|
|
57
|
+
return None
|
|
58
|
+
except json.JSONDecodeError as exc:
|
|
59
|
+
print(f"[WARN] Invalid JSON in {path}: {exc}", file=sys.stderr)
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _pass_rate(grading: Dict[str, Any]) -> float:
|
|
64
|
+
"""Calculate pass rate from a grading.json structure."""
|
|
65
|
+
expectations = grading.get("expectations", [])
|
|
66
|
+
if not expectations:
|
|
67
|
+
return 0.0
|
|
68
|
+
passed = sum(1 for e in expectations if e.get("passed", False))
|
|
69
|
+
return passed / len(expectations)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _mean(values: List[float]) -> float:
|
|
73
|
+
if not values:
|
|
74
|
+
return 0.0
|
|
75
|
+
return sum(values) / len(values)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _stddev(values: List[float]) -> float:
|
|
79
|
+
if len(values) < 2:
|
|
80
|
+
return 0.0
|
|
81
|
+
m = _mean(values)
|
|
82
|
+
variance = sum((x - m) ** 2 for x in values) / len(values)
|
|
83
|
+
return math.sqrt(variance)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _extract_iteration_number(iteration_dir: Path) -> int:
|
|
87
|
+
"""Extract iteration number from directory name like 'iteration-3'."""
|
|
88
|
+
name = iteration_dir.name
|
|
89
|
+
if name.startswith("iteration-"):
|
|
90
|
+
try:
|
|
91
|
+
return int(name.split("-", 1)[1])
|
|
92
|
+
except ValueError:
|
|
93
|
+
pass
|
|
94
|
+
return 1
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _discover_evals(iteration_dir: Path) -> List[int]:
|
|
98
|
+
"""Discover eval-N directories and return sorted eval IDs."""
|
|
99
|
+
eval_ids: List[int] = []
|
|
100
|
+
if not iteration_dir.is_dir():
|
|
101
|
+
return eval_ids
|
|
102
|
+
for entry in iteration_dir.iterdir():
|
|
103
|
+
if entry.is_dir() and entry.name.startswith("eval-"):
|
|
104
|
+
try:
|
|
105
|
+
eval_id = int(entry.name.split("-", 1)[1])
|
|
106
|
+
eval_ids.append(eval_id)
|
|
107
|
+
except ValueError:
|
|
108
|
+
continue
|
|
109
|
+
return sorted(eval_ids)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _collect_eval_result(
|
|
113
|
+
eval_dir: Path, eval_id: int
|
|
114
|
+
) -> Optional[Dict[str, Any]]:
|
|
115
|
+
"""Collect with_skill vs baseline results for a single eval."""
|
|
116
|
+
with_skill_dir = eval_dir / "with_skill"
|
|
117
|
+
without_skill_dir = eval_dir / "without_skill"
|
|
118
|
+
|
|
119
|
+
ws_grading = _load_json(with_skill_dir / "grading.json")
|
|
120
|
+
ws_timing = _load_json(with_skill_dir / "timing.json")
|
|
121
|
+
bl_grading = _load_json(without_skill_dir / "grading.json")
|
|
122
|
+
bl_timing = _load_json(without_skill_dir / "timing.json")
|
|
123
|
+
|
|
124
|
+
if ws_grading is None or ws_timing is None:
|
|
125
|
+
print(
|
|
126
|
+
f"[WARN] Incomplete with_skill data for eval-{eval_id}, skipping.",
|
|
127
|
+
file=sys.stderr,
|
|
128
|
+
)
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
if bl_grading is None or bl_timing is None:
|
|
132
|
+
print(
|
|
133
|
+
f"[WARN] Incomplete baseline data for eval-{eval_id}, skipping.",
|
|
134
|
+
file=sys.stderr,
|
|
135
|
+
)
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
"eval_id": eval_id,
|
|
140
|
+
"with_skill": {
|
|
141
|
+
"pass_rate": round(_pass_rate(ws_grading), 4),
|
|
142
|
+
"tokens": ws_timing.get("total_tokens", 0),
|
|
143
|
+
"duration": ws_timing.get("total_duration_seconds", 0.0),
|
|
144
|
+
},
|
|
145
|
+
"baseline": {
|
|
146
|
+
"pass_rate": round(_pass_rate(bl_grading), 4),
|
|
147
|
+
"tokens": bl_timing.get("total_tokens", 0),
|
|
148
|
+
"duration": bl_timing.get("total_duration_seconds", 0.0),
|
|
149
|
+
},
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _build_summary(
|
|
154
|
+
eval_results: List[Dict[str, Any]],
|
|
155
|
+
) -> Dict[str, Dict[str, float]]:
|
|
156
|
+
"""Build summary statistics from with_skill results."""
|
|
157
|
+
pass_rates = [r["with_skill"]["pass_rate"] for r in eval_results]
|
|
158
|
+
tokens = [float(r["with_skill"]["tokens"]) for r in eval_results]
|
|
159
|
+
durations = [r["with_skill"]["duration"] for r in eval_results]
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
"pass_rate": {
|
|
163
|
+
"mean": round(_mean(pass_rates), 4),
|
|
164
|
+
"stddev": round(_stddev(pass_rates), 4),
|
|
165
|
+
},
|
|
166
|
+
"tokens": {
|
|
167
|
+
"mean": round(_mean(tokens), 2),
|
|
168
|
+
"stddev": round(_stddev(tokens), 2),
|
|
169
|
+
},
|
|
170
|
+
"duration_seconds": {
|
|
171
|
+
"mean": round(_mean(durations), 2),
|
|
172
|
+
"stddev": round(_stddev(durations), 2),
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _generate_markdown(benchmark: Dict[str, Any]) -> str:
|
|
178
|
+
"""Generate a human-readable markdown report from benchmark data."""
|
|
179
|
+
lines: List[str] = []
|
|
180
|
+
skill = benchmark["skill_name"]
|
|
181
|
+
iteration = benchmark["iteration"]
|
|
182
|
+
summary = benchmark["summary"]
|
|
183
|
+
|
|
184
|
+
lines.append(f"# Benchmark Report: {skill}")
|
|
185
|
+
lines.append(f"\n**Iteration:** {iteration}")
|
|
186
|
+
lines.append("")
|
|
187
|
+
|
|
188
|
+
lines.append("## Summary")
|
|
189
|
+
lines.append("")
|
|
190
|
+
lines.append("| Metric | Mean | Std Dev |")
|
|
191
|
+
lines.append("|--------|------|---------|")
|
|
192
|
+
lines.append(
|
|
193
|
+
f"| Pass Rate | {summary['pass_rate']['mean']:.2%} "
|
|
194
|
+
f"| {summary['pass_rate']['stddev']:.4f} |"
|
|
195
|
+
)
|
|
196
|
+
lines.append(
|
|
197
|
+
f"| Tokens | {summary['tokens']['mean']:.0f} "
|
|
198
|
+
f"| {summary['tokens']['stddev']:.0f} |"
|
|
199
|
+
)
|
|
200
|
+
lines.append(
|
|
201
|
+
f"| Duration (s) | {summary['duration_seconds']['mean']:.2f} "
|
|
202
|
+
f"| {summary['duration_seconds']['stddev']:.2f} |"
|
|
203
|
+
)
|
|
204
|
+
lines.append("")
|
|
205
|
+
|
|
206
|
+
lines.append("## Eval Results")
|
|
207
|
+
lines.append("")
|
|
208
|
+
lines.append(
|
|
209
|
+
"| Eval | With Skill (pass) | Baseline (pass) | "
|
|
210
|
+
"With Skill (tokens) | Baseline (tokens) | "
|
|
211
|
+
"With Skill (dur) | Baseline (dur) |"
|
|
212
|
+
)
|
|
213
|
+
lines.append(
|
|
214
|
+
"|------|-------------------|-----------------|"
|
|
215
|
+
"--------------------|-------------------|"
|
|
216
|
+
"-----------------|----------------|"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
for r in benchmark["eval_results"]:
|
|
220
|
+
ws = r["with_skill"]
|
|
221
|
+
bl = r["baseline"]
|
|
222
|
+
lines.append(
|
|
223
|
+
f"| eval-{r['eval_id']} "
|
|
224
|
+
f"| {ws['pass_rate']:.2%} | {bl['pass_rate']:.2%} "
|
|
225
|
+
f"| {ws['tokens']} | {bl['tokens']} "
|
|
226
|
+
f"| {ws['duration']:.2f}s | {bl['duration']:.2f}s |"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
lines.append("")
|
|
230
|
+
return "\n".join(lines)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
234
|
+
args = parse_args(argv)
|
|
235
|
+
iteration_dir = Path(args.iteration_dir).resolve()
|
|
236
|
+
|
|
237
|
+
if not iteration_dir.is_dir():
|
|
238
|
+
print(f"[ERROR] Not a directory: {iteration_dir}", file=sys.stderr)
|
|
239
|
+
return 1
|
|
240
|
+
|
|
241
|
+
eval_ids = _discover_evals(iteration_dir)
|
|
242
|
+
if not eval_ids:
|
|
243
|
+
print(
|
|
244
|
+
f"[ERROR] No eval-N directories found in {iteration_dir}",
|
|
245
|
+
file=sys.stderr,
|
|
246
|
+
)
|
|
247
|
+
return 1
|
|
248
|
+
|
|
249
|
+
eval_results: List[Dict[str, Any]] = []
|
|
250
|
+
for eval_id in eval_ids:
|
|
251
|
+
eval_dir = iteration_dir / f"eval-{eval_id}"
|
|
252
|
+
result = _collect_eval_result(eval_dir, eval_id)
|
|
253
|
+
if result is not None:
|
|
254
|
+
eval_results.append(result)
|
|
255
|
+
|
|
256
|
+
if not eval_results:
|
|
257
|
+
print(
|
|
258
|
+
"[ERROR] No complete eval results found. "
|
|
259
|
+
"Check warnings above for details.",
|
|
260
|
+
file=sys.stderr,
|
|
261
|
+
)
|
|
262
|
+
return 1
|
|
263
|
+
|
|
264
|
+
iteration_number = _extract_iteration_number(iteration_dir)
|
|
265
|
+
|
|
266
|
+
benchmark: Dict[str, Any] = {
|
|
267
|
+
"skill_name": args.skill_name,
|
|
268
|
+
"iteration": iteration_number,
|
|
269
|
+
"summary": _build_summary(eval_results),
|
|
270
|
+
"eval_results": eval_results,
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# Write benchmark.json
|
|
274
|
+
json_path = iteration_dir / "benchmark.json"
|
|
275
|
+
with open(json_path, "w", encoding="utf-8") as f:
|
|
276
|
+
json.dump(benchmark, f, indent=2, ensure_ascii=False)
|
|
277
|
+
print(f"[OK] Written: {json_path}")
|
|
278
|
+
|
|
279
|
+
# Write benchmark.md
|
|
280
|
+
md_path = iteration_dir / "benchmark.md"
|
|
281
|
+
with open(md_path, "w", encoding="utf-8") as f:
|
|
282
|
+
f.write(_generate_markdown(benchmark))
|
|
283
|
+
print(f"[OK] Written: {md_path}")
|
|
284
|
+
|
|
285
|
+
# Print summary
|
|
286
|
+
s = benchmark["summary"]
|
|
287
|
+
print(
|
|
288
|
+
f"\n--- Iteration {iteration_number} Summary ---\n"
|
|
289
|
+
f" Evals collected: {len(eval_results)}\n"
|
|
290
|
+
f" Pass rate: {s['pass_rate']['mean']:.2%} "
|
|
291
|
+
f"(stddev {s['pass_rate']['stddev']:.4f})\n"
|
|
292
|
+
f" Tokens: {s['tokens']['mean']:.0f} "
|
|
293
|
+
f"(stddev {s['tokens']['stddev']:.0f})\n"
|
|
294
|
+
f" Duration: {s['duration_seconds']['mean']:.2f}s "
|
|
295
|
+
f"(stddev {s['duration_seconds']['stddev']:.2f}s)"
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
return 0
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
if __name__ == "__main__":
|
|
302
|
+
sys.exit(main())
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# init_skill.sh — Scaffold a new skill directory with SKILL.md template.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# ./init_skill.sh <skill-name> [--path <dir>] [--help]
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# ./init_skill.sh my-awesome-skill
|
|
9
|
+
# ./init_skill.sh my-awesome-skill --path /custom/skills/dir
|
|
10
|
+
#
|
|
11
|
+
# Creates:
|
|
12
|
+
# <dir>/<skill-name>/
|
|
13
|
+
# ├── SKILL.md # Skill template with frontmatter
|
|
14
|
+
# └── references/ # Supporting files directory
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
VERSION="1.0.0"
|
|
19
|
+
|
|
20
|
+
# ──────────────────────────────────────────────
|
|
21
|
+
# Help
|
|
22
|
+
# ──────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
usage() {
|
|
25
|
+
cat <<'HELP'
|
|
26
|
+
init_skill.sh — Scaffold a new skill directory
|
|
27
|
+
|
|
28
|
+
USAGE
|
|
29
|
+
./init_skill.sh <skill-name> [OPTIONS]
|
|
30
|
+
|
|
31
|
+
ARGUMENTS
|
|
32
|
+
skill-name Skill name in kebab-case (e.g. my-awesome-skill)
|
|
33
|
+
Must start with a letter, contain only lowercase
|
|
34
|
+
letters, digits, and hyphens.
|
|
35
|
+
|
|
36
|
+
OPTIONS
|
|
37
|
+
--path <dir> Parent directory for the skill (default: current directory)
|
|
38
|
+
--help Show this help message
|
|
39
|
+
--version Show version
|
|
40
|
+
|
|
41
|
+
EXAMPLES
|
|
42
|
+
./init_skill.sh test-driven-development
|
|
43
|
+
./init_skill.sh my-skill --path ./skills
|
|
44
|
+
./init_skill.sh code-review --path /absolute/path/to/skills
|
|
45
|
+
|
|
46
|
+
OUTPUT
|
|
47
|
+
Creates the following structure:
|
|
48
|
+
|
|
49
|
+
<skill-name>/
|
|
50
|
+
├── SKILL.md Skill definition with frontmatter template
|
|
51
|
+
└── references/ Directory for supporting files
|
|
52
|
+
HELP
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# ──────────────────────────────────────────────
|
|
56
|
+
# Argument parsing
|
|
57
|
+
# ──────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
SKILL_NAME=""
|
|
60
|
+
PARENT_DIR="."
|
|
61
|
+
|
|
62
|
+
while [[ $# -gt 0 ]]; do
|
|
63
|
+
case "$1" in
|
|
64
|
+
--help|-h)
|
|
65
|
+
usage
|
|
66
|
+
exit 0
|
|
67
|
+
;;
|
|
68
|
+
--version|-v)
|
|
69
|
+
echo "init_skill.sh v${VERSION}"
|
|
70
|
+
exit 0
|
|
71
|
+
;;
|
|
72
|
+
--path)
|
|
73
|
+
if [[ -z "${2:-}" ]]; then
|
|
74
|
+
echo "[ERROR] --path requires a directory argument" >&2
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
PARENT_DIR="$2"
|
|
78
|
+
shift 2
|
|
79
|
+
;;
|
|
80
|
+
-*)
|
|
81
|
+
echo "[ERROR] Unknown option: $1" >&2
|
|
82
|
+
echo "Run './init_skill.sh --help' for usage." >&2
|
|
83
|
+
exit 1
|
|
84
|
+
;;
|
|
85
|
+
*)
|
|
86
|
+
if [[ -n "$SKILL_NAME" ]]; then
|
|
87
|
+
echo "[ERROR] Unexpected argument: $1" >&2
|
|
88
|
+
echo "Only one skill name is allowed." >&2
|
|
89
|
+
exit 1
|
|
90
|
+
fi
|
|
91
|
+
SKILL_NAME="$1"
|
|
92
|
+
shift
|
|
93
|
+
;;
|
|
94
|
+
esac
|
|
95
|
+
done
|
|
96
|
+
|
|
97
|
+
# ──────────────────────────────────────────────
|
|
98
|
+
# Validation
|
|
99
|
+
# ──────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
if [[ -z "$SKILL_NAME" ]]; then
|
|
102
|
+
echo "[ERROR] Skill name is required." >&2
|
|
103
|
+
echo "Run './init_skill.sh --help' for usage." >&2
|
|
104
|
+
exit 1
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
# Validate kebab-case: starts with letter, lowercase + digits + hyphens only
|
|
108
|
+
if ! echo "$SKILL_NAME" | grep -qE '^[a-z][a-z0-9-]*$'; then
|
|
109
|
+
echo "[ERROR] Invalid skill name: '$SKILL_NAME'" >&2
|
|
110
|
+
echo "Must be kebab-case: start with a letter, contain only lowercase letters, digits, and hyphens." >&2
|
|
111
|
+
exit 1
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
if [[ ! -d "$PARENT_DIR" ]]; then
|
|
115
|
+
echo "[ERROR] Parent directory does not exist: $PARENT_DIR" >&2
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
SKILL_DIR="${PARENT_DIR}/${SKILL_NAME}"
|
|
120
|
+
|
|
121
|
+
# Prevent overwriting existing directory
|
|
122
|
+
if [[ -d "$SKILL_DIR" ]]; then
|
|
123
|
+
echo "[ERROR] Directory already exists: $SKILL_DIR" >&2
|
|
124
|
+
echo "Remove it first or choose a different name." >&2
|
|
125
|
+
exit 1
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# ──────────────────────────────────────────────
|
|
129
|
+
# Scaffold
|
|
130
|
+
# ──────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
mkdir -p "${SKILL_DIR}/references" "${SKILL_DIR}/examples" "${SKILL_DIR}/scripts"
|
|
133
|
+
|
|
134
|
+
cat > "${SKILL_DIR}/SKILL.md" <<TEMPLATE
|
|
135
|
+
---
|
|
136
|
+
name: ${SKILL_NAME}
|
|
137
|
+
description: >-
|
|
138
|
+
TODO: One-line description of what this skill does and when to use it.
|
|
139
|
+
Be specific — this text drives trigger matching in recommend_skills.
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
# ${SKILL_NAME}
|
|
143
|
+
|
|
144
|
+
## Overview
|
|
145
|
+
|
|
146
|
+
TODO: Brief explanation of the problem this skill solves.
|
|
147
|
+
|
|
148
|
+
**Core principle:** TODO: The single most important rule this skill enforces.
|
|
149
|
+
|
|
150
|
+
**Iron Law:**
|
|
151
|
+
\`\`\`
|
|
152
|
+
TODO: The non-negotiable constraint. One line.
|
|
153
|
+
\`\`\`
|
|
154
|
+
|
|
155
|
+
## When to Use
|
|
156
|
+
|
|
157
|
+
- TODO: Specific scenario 1
|
|
158
|
+
- TODO: Specific scenario 2
|
|
159
|
+
- TODO: Specific scenario 3
|
|
160
|
+
|
|
161
|
+
## When NOT to Use
|
|
162
|
+
|
|
163
|
+
- TODO: Anti-scenario 1
|
|
164
|
+
- TODO: Anti-scenario 2
|
|
165
|
+
|
|
166
|
+
## Process
|
|
167
|
+
|
|
168
|
+
### Phase 1: TODO
|
|
169
|
+
|
|
170
|
+
1. Step 1
|
|
171
|
+
2. Step 2
|
|
172
|
+
3. Step 3
|
|
173
|
+
|
|
174
|
+
### Phase 2: TODO
|
|
175
|
+
|
|
176
|
+
1. Step 1
|
|
177
|
+
2. Step 2
|
|
178
|
+
3. Step 3
|
|
179
|
+
|
|
180
|
+
## Checklist
|
|
181
|
+
|
|
182
|
+
- [ ] TODO: Verification item 1
|
|
183
|
+
- [ ] TODO: Verification item 2
|
|
184
|
+
- [ ] TODO: Verification item 3
|
|
185
|
+
TEMPLATE
|
|
186
|
+
|
|
187
|
+
echo "[OK] Skill scaffolded: ${SKILL_DIR}"
|
|
188
|
+
echo " - ${SKILL_DIR}/SKILL.md"
|
|
189
|
+
echo " - ${SKILL_DIR}/references/"
|
|
190
|
+
echo " - ${SKILL_DIR}/examples/"
|
|
191
|
+
echo " - ${SKILL_DIR}/scripts/"
|
|
192
|
+
echo ""
|
|
193
|
+
echo "Next steps:"
|
|
194
|
+
echo " 1. Edit ${SKILL_DIR}/SKILL.md — fill in the TODOs"
|
|
195
|
+
echo " 2. Add supporting files to ${SKILL_DIR}/references/"
|
|
196
|
+
echo " 3. Test with: recommend_skills(prompt='your test prompt')"
|