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.
Files changed (122) hide show
  1. package/.ai-rules/adapters/antigravity.md +6 -6
  2. package/.ai-rules/adapters/claude-code.md +68 -4
  3. package/.ai-rules/adapters/codex.md +5 -5
  4. package/.ai-rules/adapters/cursor.md +2 -2
  5. package/.ai-rules/adapters/kiro.md +8 -8
  6. package/.ai-rules/adapters/opencode.md +7 -7
  7. package/.ai-rules/adapters/q.md +2 -2
  8. package/.ai-rules/agents/README.md +66 -16
  9. package/.ai-rules/agents/accessibility-specialist.json +2 -1
  10. package/.ai-rules/agents/act-mode.json +2 -1
  11. package/.ai-rules/agents/agent-architect.json +8 -7
  12. package/.ai-rules/agents/ai-ml-engineer.json +1 -0
  13. package/.ai-rules/agents/architecture-specialist.json +1 -0
  14. package/.ai-rules/agents/auto-mode.json +4 -2
  15. package/.ai-rules/agents/backend-developer.json +1 -0
  16. package/.ai-rules/agents/code-quality-specialist.json +1 -0
  17. package/.ai-rules/agents/code-reviewer.json +65 -64
  18. package/.ai-rules/agents/data-engineer.json +8 -7
  19. package/.ai-rules/agents/data-scientist.json +10 -9
  20. package/.ai-rules/agents/devops-engineer.json +1 -0
  21. package/.ai-rules/agents/documentation-specialist.json +1 -0
  22. package/.ai-rules/agents/eval-mode.json +20 -19
  23. package/.ai-rules/agents/event-architecture-specialist.json +1 -0
  24. package/.ai-rules/agents/frontend-developer.json +1 -0
  25. package/.ai-rules/agents/i18n-specialist.json +2 -1
  26. package/.ai-rules/agents/integration-specialist.json +1 -0
  27. package/.ai-rules/agents/migration-specialist.json +1 -0
  28. package/.ai-rules/agents/mobile-developer.json +8 -7
  29. package/.ai-rules/agents/observability-specialist.json +1 -0
  30. package/.ai-rules/agents/parallel-orchestrator.json +346 -0
  31. package/.ai-rules/agents/performance-specialist.json +1 -0
  32. package/.ai-rules/agents/plan-mode.json +3 -1
  33. package/.ai-rules/agents/plan-reviewer.json +208 -0
  34. package/.ai-rules/agents/platform-engineer.json +1 -0
  35. package/.ai-rules/agents/security-engineer.json +9 -8
  36. package/.ai-rules/agents/security-specialist.json +2 -1
  37. package/.ai-rules/agents/seo-specialist.json +1 -0
  38. package/.ai-rules/agents/software-engineer.json +1 -0
  39. package/.ai-rules/agents/solution-architect.json +11 -10
  40. package/.ai-rules/agents/systems-developer.json +9 -8
  41. package/.ai-rules/agents/technical-planner.json +11 -10
  42. package/.ai-rules/agents/test-engineer.json +7 -6
  43. package/.ai-rules/agents/test-strategy-specialist.json +1 -0
  44. package/.ai-rules/agents/tooling-engineer.json +4 -3
  45. package/.ai-rules/agents/ui-ux-designer.json +1 -0
  46. package/.ai-rules/keyword-modes.json +4 -4
  47. package/.ai-rules/rules/clarification-guide.md +14 -14
  48. package/.ai-rules/rules/core.md +73 -0
  49. package/.ai-rules/rules/parallel-execution.md +217 -0
  50. package/.ai-rules/skills/README.md +23 -1
  51. package/.ai-rules/skills/agent-design/SKILL.md +5 -0
  52. package/.ai-rules/skills/agent-design/examples/agent-template.json +58 -0
  53. package/.ai-rules/skills/agent-design/references/expertise-guidelines.md +112 -0
  54. package/.ai-rules/skills/agent-discussion/SKILL.md +199 -0
  55. package/.ai-rules/skills/agent-discussion-panel/SKILL.md +448 -0
  56. package/.ai-rules/skills/api-design/SKILL.md +5 -0
  57. package/.ai-rules/skills/api-design/examples/error-response.json +159 -0
  58. package/.ai-rules/skills/api-design/examples/openapi-template.yaml +393 -0
  59. package/.ai-rules/skills/build-fix/SKILL.md +234 -0
  60. package/.ai-rules/skills/code-explanation/SKILL.md +4 -0
  61. package/.ai-rules/skills/context-management/SKILL.md +1 -0
  62. package/.ai-rules/skills/cost-budget/SKILL.md +348 -0
  63. package/.ai-rules/skills/cross-repo-issues/SKILL.md +257 -0
  64. package/.ai-rules/skills/database-migration/SKILL.md +1 -0
  65. package/.ai-rules/skills/deepsearch/SKILL.md +214 -0
  66. package/.ai-rules/skills/deployment-checklist/SKILL.md +1 -0
  67. package/.ai-rules/skills/error-analysis/SKILL.md +1 -0
  68. package/.ai-rules/skills/finishing-a-development-branch/SKILL.md +281 -0
  69. package/.ai-rules/skills/frontend-design/SKILL.md +5 -0
  70. package/.ai-rules/skills/frontend-design/examples/component-template.tsx +203 -0
  71. package/.ai-rules/skills/frontend-design/references/css-patterns.md +243 -0
  72. package/.ai-rules/skills/git-master/SKILL.md +358 -0
  73. package/.ai-rules/skills/incident-response/SKILL.md +1 -0
  74. package/.ai-rules/skills/legacy-modernization/SKILL.md +1 -0
  75. package/.ai-rules/skills/mcp-builder/SKILL.md +7 -0
  76. package/.ai-rules/skills/mcp-builder/examples/resource-example.ts +233 -0
  77. package/.ai-rules/skills/mcp-builder/examples/tool-example.ts +203 -0
  78. package/.ai-rules/skills/mcp-builder/references/protocol-spec.md +215 -0
  79. package/.ai-rules/skills/performance-optimization/SKILL.md +3 -0
  80. package/.ai-rules/skills/plan-and-review/SKILL.md +115 -0
  81. package/.ai-rules/skills/pr-all-in-one/SKILL.md +15 -13
  82. package/.ai-rules/skills/pr-all-in-one/configuration-guide.md +7 -7
  83. package/.ai-rules/skills/pr-all-in-one/pr-templates.md +10 -10
  84. package/.ai-rules/skills/pr-review/SKILL.md +4 -0
  85. package/.ai-rules/skills/receiving-code-review/SKILL.md +347 -0
  86. package/.ai-rules/skills/refactoring/SKILL.md +1 -0
  87. package/.ai-rules/skills/requesting-code-review/SKILL.md +348 -0
  88. package/.ai-rules/skills/rule-authoring/SKILL.md +5 -0
  89. package/.ai-rules/skills/rule-authoring/examples/rule-template.md +142 -0
  90. package/.ai-rules/skills/rule-authoring/examples/trigger-patterns.md +126 -0
  91. package/.ai-rules/skills/security-audit/SKILL.md +4 -0
  92. package/.ai-rules/skills/skill-creator/SKILL.md +461 -0
  93. package/.ai-rules/skills/skill-creator/agents/analyzer.md +206 -0
  94. package/.ai-rules/skills/skill-creator/agents/comparator.md +167 -0
  95. package/.ai-rules/skills/skill-creator/agents/grader.md +152 -0
  96. package/.ai-rules/skills/skill-creator/assets/eval_review.html +289 -0
  97. package/.ai-rules/skills/skill-creator/assets/skill-template.md +43 -0
  98. package/.ai-rules/skills/skill-creator/eval-viewer/generate_review.py +496 -0
  99. package/.ai-rules/skills/skill-creator/references/frontmatter-guide.md +632 -0
  100. package/.ai-rules/skills/skill-creator/references/multi-tool-compat.md +480 -0
  101. package/.ai-rules/skills/skill-creator/references/schemas.md +784 -0
  102. package/.ai-rules/skills/skill-creator/scripts/aggregate_benchmark.py +302 -0
  103. package/.ai-rules/skills/skill-creator/scripts/init_skill.sh +196 -0
  104. package/.ai-rules/skills/skill-creator/scripts/run_loop.py +327 -0
  105. package/.ai-rules/skills/systematic-debugging/SKILL.md +1 -0
  106. package/.ai-rules/skills/tech-debt/SKILL.md +1 -0
  107. package/.ai-rules/skills/test-coverage-gate/SKILL.md +303 -0
  108. package/.ai-rules/skills/tmux-master/SKILL.md +491 -0
  109. package/.ai-rules/skills/using-git-worktrees/SKILL.md +368 -0
  110. package/.ai-rules/skills/verification-before-completion/SKILL.md +234 -0
  111. package/.ai-rules/skills/widget-slot-architecture/SKILL.md +6 -0
  112. package/.ai-rules/skills/widget-slot-architecture/examples/parallel-route-setup.tsx +206 -0
  113. package/.ai-rules/skills/widget-slot-architecture/examples/widget-component.tsx +250 -0
  114. package/.ai-rules/skills/writing-plans/SKILL.md +78 -0
  115. package/bin/cli.js +178 -0
  116. package/lib/init/detect-stack.js +148 -0
  117. package/lib/init/generate-config.js +31 -0
  118. package/lib/init/index.js +86 -0
  119. package/lib/init/prompt.js +60 -0
  120. package/lib/init/scaffold.js +67 -0
  121. package/lib/init/suggest-agent.js +46 -0
  122. 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')"