@softspark/ai-toolkit 1.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 (327) hide show
  1. package/AGENTS.md +412 -0
  2. package/CHANGELOG.md +68 -0
  3. package/LICENSE +21 -0
  4. package/README.md +632 -0
  5. package/action.yml +53 -0
  6. package/app/.claude-plugin/plugin.json +44 -0
  7. package/app/ARCHITECTURE.md +306 -0
  8. package/app/CLAUDE.md.template +23 -0
  9. package/app/agents/ai-engineer.md +128 -0
  10. package/app/agents/backend-specialist.md +193 -0
  11. package/app/agents/business-intelligence.md +54 -0
  12. package/app/agents/chaos-monkey.md +67 -0
  13. package/app/agents/chief-of-staff.md +51 -0
  14. package/app/agents/code-archaeologist.md +127 -0
  15. package/app/agents/code-reviewer.md +184 -0
  16. package/app/agents/command-expert.md +131 -0
  17. package/app/agents/data-analyst.md +205 -0
  18. package/app/agents/data-scientist.md +151 -0
  19. package/app/agents/database-architect.md +317 -0
  20. package/app/agents/debugger.md +238 -0
  21. package/app/agents/devops-implementer.md +194 -0
  22. package/app/agents/documenter.md +364 -0
  23. package/app/agents/explorer-agent.md +145 -0
  24. package/app/agents/fact-checker.md +172 -0
  25. package/app/agents/frontend-specialist.md +209 -0
  26. package/app/agents/game-developer.md +216 -0
  27. package/app/agents/incident-responder.md +226 -0
  28. package/app/agents/infrastructure-architect.md +127 -0
  29. package/app/agents/infrastructure-validator.md +247 -0
  30. package/app/agents/llm-ops-engineer.md +237 -0
  31. package/app/agents/mcp-expert.md +228 -0
  32. package/app/agents/mcp-server-architect.md +195 -0
  33. package/app/agents/mcp-testing-engineer.md +292 -0
  34. package/app/agents/meta-architect.md +58 -0
  35. package/app/agents/ml-engineer.md +136 -0
  36. package/app/agents/mobile-developer.md +190 -0
  37. package/app/agents/night-watchman.md +55 -0
  38. package/app/agents/nlp-engineer.md +154 -0
  39. package/app/agents/orchestrator.md +437 -0
  40. package/app/agents/performance-optimizer.md +254 -0
  41. package/app/agents/predictive-analyst.md +57 -0
  42. package/app/agents/product-manager.md +194 -0
  43. package/app/agents/project-planner.md +287 -0
  44. package/app/agents/prompt-engineer.md +103 -0
  45. package/app/agents/qa-automation-engineer.md +182 -0
  46. package/app/agents/rag-engineer.md +201 -0
  47. package/app/agents/research-synthesizer.md +138 -0
  48. package/app/agents/search-specialist.md +101 -0
  49. package/app/agents/security-architect.md +62 -0
  50. package/app/agents/security-auditor.md +293 -0
  51. package/app/agents/seo-specialist.md +111 -0
  52. package/app/agents/system-governor.md +57 -0
  53. package/app/agents/tech-lead.md +62 -0
  54. package/app/agents/technical-researcher.md +103 -0
  55. package/app/agents/test-engineer.md +264 -0
  56. package/app/constitution.md +38 -0
  57. package/app/hooks/_profile-check.sh +11 -0
  58. package/app/hooks/guard-destructive.sh +74 -0
  59. package/app/hooks/guard-path.sh +73 -0
  60. package/app/hooks/post-tool-use.sh +35 -0
  61. package/app/hooks/pre-compact.sh +31 -0
  62. package/app/hooks/quality-check.sh +22 -0
  63. package/app/hooks/quality-gate.sh +49 -0
  64. package/app/hooks/save-session.sh +24 -0
  65. package/app/hooks/session-end.sh +37 -0
  66. package/app/hooks/session-start.sh +29 -0
  67. package/app/hooks/subagent-start.sh +16 -0
  68. package/app/hooks/subagent-stop.sh +16 -0
  69. package/app/hooks/track-usage.sh +50 -0
  70. package/app/hooks/user-prompt-submit.sh +25 -0
  71. package/app/hooks.json +178 -0
  72. package/app/mcp-defaults.json +23 -0
  73. package/app/output-styles/golden-rules.md +43 -0
  74. package/app/plugins/README.md +19 -0
  75. package/app/plugins/csharp-pack/README.md +11 -0
  76. package/app/plugins/csharp-pack/plugin.json +18 -0
  77. package/app/plugins/enterprise-pack/README.md +16 -0
  78. package/app/plugins/enterprise-pack/hooks/output-style.sh +6 -0
  79. package/app/plugins/enterprise-pack/hooks/status-line.sh +8 -0
  80. package/app/plugins/enterprise-pack/plugin.json +24 -0
  81. package/app/plugins/frontend-pack/README.md +14 -0
  82. package/app/plugins/frontend-pack/plugin.json +22 -0
  83. package/app/plugins/java-pack/README.md +11 -0
  84. package/app/plugins/java-pack/plugin.json +18 -0
  85. package/app/plugins/kotlin-pack/README.md +11 -0
  86. package/app/plugins/kotlin-pack/plugin.json +18 -0
  87. package/app/plugins/memory-pack/README.md +24 -0
  88. package/app/plugins/memory-pack/hooks/observation-capture.sh +67 -0
  89. package/app/plugins/memory-pack/hooks/session-summary.sh +71 -0
  90. package/app/plugins/memory-pack/plugin.json +22 -0
  91. package/app/plugins/memory-pack/scripts/init_db.py +81 -0
  92. package/app/plugins/memory-pack/scripts/strip_private.py +22 -0
  93. package/app/plugins/memory-pack/skills/mem-search/SKILL.md +70 -0
  94. package/app/plugins/research-pack/README.md +14 -0
  95. package/app/plugins/research-pack/plugin.json +22 -0
  96. package/app/plugins/ruby-pack/README.md +11 -0
  97. package/app/plugins/ruby-pack/plugin.json +18 -0
  98. package/app/plugins/rust-pack/README.md +11 -0
  99. package/app/plugins/rust-pack/plugin.json +18 -0
  100. package/app/plugins/security-pack/README.md +15 -0
  101. package/app/plugins/security-pack/plugin.json +23 -0
  102. package/app/plugins/swift-pack/README.md +11 -0
  103. package/app/plugins/swift-pack/plugin.json +18 -0
  104. package/app/rules/claude-toolkit-rules.md +21 -0
  105. package/app/rules/git-conventions.md +5 -0
  106. package/app/rules/quality-gates.md +10 -0
  107. package/app/skills/_lib/__init__.py +1 -0
  108. package/app/skills/_lib/detect_utils.py +150 -0
  109. package/app/skills/agent-creator/SKILL.md +82 -0
  110. package/app/skills/analyze/SKILL.md +92 -0
  111. package/app/skills/analyze/scripts/complexity.py +165 -0
  112. package/app/skills/api-patterns/SKILL.md +305 -0
  113. package/app/skills/app-builder/SKILL.md +187 -0
  114. package/app/skills/architecture-audit/SKILL.md +141 -0
  115. package/app/skills/architecture-decision/SKILL.md +55 -0
  116. package/app/skills/architecture-decision/templates/adr-template.md +36 -0
  117. package/app/skills/biz-scan/SKILL.md +30 -0
  118. package/app/skills/briefing/SKILL.md +27 -0
  119. package/app/skills/build/SKILL.md +97 -0
  120. package/app/skills/build/scripts/detect-build.py +151 -0
  121. package/app/skills/chaos/SKILL.md +32 -0
  122. package/app/skills/ci/SKILL.md +77 -0
  123. package/app/skills/ci/scripts/ci-detect.py +135 -0
  124. package/app/skills/ci/templates/github-actions-node.yml +38 -0
  125. package/app/skills/ci/templates/github-actions-python.yml +42 -0
  126. package/app/skills/ci-cd-patterns/SKILL.md +299 -0
  127. package/app/skills/clean-code/SKILL.md +110 -0
  128. package/app/skills/clean-code/reference/dart.md +18 -0
  129. package/app/skills/clean-code/reference/go.md +23 -0
  130. package/app/skills/clean-code/reference/php.md +32 -0
  131. package/app/skills/clean-code/reference/python.md +180 -0
  132. package/app/skills/clean-code/reference/typescript.md +26 -0
  133. package/app/skills/command-creator/SKILL.md +83 -0
  134. package/app/skills/commit/SKILL.md +98 -0
  135. package/app/skills/commit/scripts/pre-commit-check.py +87 -0
  136. package/app/skills/commit/templates/conventional-commit.md +52 -0
  137. package/app/skills/csharp-patterns/SKILL.md +450 -0
  138. package/app/skills/database-patterns/SKILL.md +297 -0
  139. package/app/skills/debug/SKILL.md +154 -0
  140. package/app/skills/debug/scripts/error-parser.py +187 -0
  141. package/app/skills/debugging-tactics/SKILL.md +136 -0
  142. package/app/skills/deploy/SKILL.md +130 -0
  143. package/app/skills/deploy/scripts/pre_deploy_check.py +171 -0
  144. package/app/skills/deploy/templates/deployment-checklist.md +31 -0
  145. package/app/skills/design-an-interface/SKILL.md +105 -0
  146. package/app/skills/design-engineering/SKILL.md +260 -0
  147. package/app/skills/docker-devops/SKILL.md +303 -0
  148. package/app/skills/docs/SKILL.md +145 -0
  149. package/app/skills/docs/scripts/doc-inventory.py +176 -0
  150. package/app/skills/docs/templates/adr-template.md +36 -0
  151. package/app/skills/docs/templates/readme-template.md +67 -0
  152. package/app/skills/documentation-standards/SKILL.md +191 -0
  153. package/app/skills/ecommerce-patterns/SKILL.md +209 -0
  154. package/app/skills/evaluate/SKILL.md +132 -0
  155. package/app/skills/evolve/SKILL.md +27 -0
  156. package/app/skills/explain/SKILL.md +54 -0
  157. package/app/skills/explain/scripts/dependency-graph.py +215 -0
  158. package/app/skills/explore/SKILL.md +112 -0
  159. package/app/skills/explore/scripts/visualize.py +117 -0
  160. package/app/skills/fix/SKILL.md +78 -0
  161. package/app/skills/fix/scripts/error-classifier.py +191 -0
  162. package/app/skills/flutter-patterns/SKILL.md +254 -0
  163. package/app/skills/git-mastery/SKILL.md +70 -0
  164. package/app/skills/grill-me/SKILL.md +38 -0
  165. package/app/skills/health/SKILL.md +91 -0
  166. package/app/skills/health/scripts/health_check.py +162 -0
  167. package/app/skills/hive-mind/SKILL.md +56 -0
  168. package/app/skills/hook-creator/SKILL.md +107 -0
  169. package/app/skills/index/SKILL.md +74 -0
  170. package/app/skills/instinct-review/SKILL.md +77 -0
  171. package/app/skills/java-patterns/SKILL.md +442 -0
  172. package/app/skills/kotlin-patterns/SKILL.md +446 -0
  173. package/app/skills/lint/SKILL.md +103 -0
  174. package/app/skills/lint/scripts/detect-linters.py +112 -0
  175. package/app/skills/mcp-patterns/SKILL.md +270 -0
  176. package/app/skills/mem-search/SKILL.md +70 -0
  177. package/app/skills/migrate/SKILL.md +90 -0
  178. package/app/skills/migrate/scripts/migration-status.py +195 -0
  179. package/app/skills/migration-patterns/SKILL.md +260 -0
  180. package/app/skills/night-watch/SKILL.md +28 -0
  181. package/app/skills/observability-patterns/SKILL.md +203 -0
  182. package/app/skills/onboard/SKILL.md +76 -0
  183. package/app/skills/orchestrate/SKILL.md +86 -0
  184. package/app/skills/panic/SKILL.md +30 -0
  185. package/app/skills/performance-profiling/SKILL.md +59 -0
  186. package/app/skills/plan/SKILL.md +110 -0
  187. package/app/skills/plan/templates/plan-template.md +40 -0
  188. package/app/skills/plan-writing/SKILL.md +201 -0
  189. package/app/skills/plugin-creator/SKILL.md +78 -0
  190. package/app/skills/pr/SKILL.md +129 -0
  191. package/app/skills/pr/scripts/pr-summary.py +175 -0
  192. package/app/skills/prd-to-issues/SKILL.md +108 -0
  193. package/app/skills/prd-to-plan/SKILL.md +120 -0
  194. package/app/skills/predict/SKILL.md +30 -0
  195. package/app/skills/qa-session/SKILL.md +110 -0
  196. package/app/skills/rag-patterns/SKILL.md +203 -0
  197. package/app/skills/refactor/SKILL.md +124 -0
  198. package/app/skills/refactor/scripts/refactor-scan.py +210 -0
  199. package/app/skills/refactor-plan/SKILL.md +112 -0
  200. package/app/skills/repeat/SKILL.md +149 -0
  201. package/app/skills/research-mastery/SKILL.md +56 -0
  202. package/app/skills/review/SKILL.md +141 -0
  203. package/app/skills/review/scripts/diff-analyzer.py +170 -0
  204. package/app/skills/rollback/SKILL.md +87 -0
  205. package/app/skills/rollback/scripts/rollback_info.py +149 -0
  206. package/app/skills/ruby-patterns/SKILL.md +454 -0
  207. package/app/skills/rust-patterns/SKILL.md +446 -0
  208. package/app/skills/search/SKILL.md +64 -0
  209. package/app/skills/security-patterns/SKILL.md +91 -0
  210. package/app/skills/security-patterns/reference/authentication.md +37 -0
  211. package/app/skills/security-patterns/reference/authorization.md +22 -0
  212. package/app/skills/security-patterns/reference/input-validation.md +30 -0
  213. package/app/skills/security-patterns/reference/oauth-csrf-audit.md +131 -0
  214. package/app/skills/skill-creator/SKILL.md +154 -0
  215. package/app/skills/skill-creator/templates/dashboard/index.html +130 -0
  216. package/app/skills/skill-creator/templates/reasoning-engine/assets/example.json +12 -0
  217. package/app/skills/skill-creator/templates/reasoning-engine/search.py +110 -0
  218. package/app/skills/subagent-development/SKILL.md +225 -0
  219. package/app/skills/subagent-development/reference/code-quality-reviewer-prompt.md +145 -0
  220. package/app/skills/subagent-development/reference/implementer-prompt.md +118 -0
  221. package/app/skills/subagent-development/reference/spec-reviewer-prompt.md +100 -0
  222. package/app/skills/swarm/SKILL.md +81 -0
  223. package/app/skills/swift-patterns/SKILL.md +500 -0
  224. package/app/skills/tdd/SKILL.md +174 -0
  225. package/app/skills/tdd/reference/deep-modules.md +32 -0
  226. package/app/skills/tdd/reference/interface-design.md +32 -0
  227. package/app/skills/tdd/reference/mocking.md +52 -0
  228. package/app/skills/tdd/reference/refactoring.md +10 -0
  229. package/app/skills/tdd/reference/tests.md +59 -0
  230. package/app/skills/teams/SKILL.md +101 -0
  231. package/app/skills/test/SKILL.md +107 -0
  232. package/app/skills/test/scripts/detect-runner.py +113 -0
  233. package/app/skills/testing-patterns/SKILL.md +73 -0
  234. package/app/skills/testing-patterns/reference/flutter-testing.md +33 -0
  235. package/app/skills/testing-patterns/reference/go-testing.md +52 -0
  236. package/app/skills/testing-patterns/reference/php-phpunit.md +39 -0
  237. package/app/skills/testing-patterns/reference/python-pytest.md +228 -0
  238. package/app/skills/testing-patterns/reference/typescript-vitest.md +50 -0
  239. package/app/skills/triage-issue/SKILL.md +120 -0
  240. package/app/skills/typescript-patterns/SKILL.md +256 -0
  241. package/app/skills/ubiquitous-language/SKILL.md +74 -0
  242. package/app/skills/verification-before-completion/SKILL.md +108 -0
  243. package/app/skills/workflow/SKILL.md +250 -0
  244. package/app/skills/write-a-prd/SKILL.md +129 -0
  245. package/app/skills/write-a-prd/reference/visual-companion.md +78 -0
  246. package/app/skills/write-a-prd/scripts/frame-template.html +111 -0
  247. package/app/skills/write-a-prd/scripts/visual-server.cjs +79 -0
  248. package/app/templates/skill/generator/SKILL.md.template +40 -0
  249. package/app/templates/skill/knowledge/SKILL.md.template +52 -0
  250. package/app/templates/skill/linter/SKILL.md.template +34 -0
  251. package/app/templates/skill/reviewer/SKILL.md.template +51 -0
  252. package/app/templates/skill/workflow/SKILL.md.template +49 -0
  253. package/benchmarks/README.md +111 -0
  254. package/benchmarks/ecosystem-dashboard.json +148 -0
  255. package/benchmarks/ecosystem-harvest.json +148 -0
  256. package/benchmarks/results.json +38 -0
  257. package/benchmarks/run.py +351 -0
  258. package/bin/ai-toolkit.js +345 -0
  259. package/kb/best-practices/README.md +11 -0
  260. package/kb/howto/README.md +11 -0
  261. package/kb/procedures/maintenance-sop.md +306 -0
  262. package/kb/reference/agents-catalog.md +124 -0
  263. package/kb/reference/anti-pattern-registry-format.md +221 -0
  264. package/kb/reference/architecture-overview.md +232 -0
  265. package/kb/reference/benchmark-config.md +62 -0
  266. package/kb/reference/ci-integration.md +66 -0
  267. package/kb/reference/claude-ecosystem-benchmark-snapshot.md +80 -0
  268. package/kb/reference/claude-ecosystem-expansion-foundations.md +102 -0
  269. package/kb/reference/commands-catalog.md +21 -0
  270. package/kb/reference/distribution-model.md +63 -0
  271. package/kb/reference/global-install-model.md +56 -0
  272. package/kb/reference/hierarchical-override-pattern.md +200 -0
  273. package/kb/reference/hooks-catalog.md +306 -0
  274. package/kb/reference/integrations.md +88 -0
  275. package/kb/reference/language-packs.md +52 -0
  276. package/kb/reference/merge-friendly-install-model.md +58 -0
  277. package/kb/reference/plugin-pack-conventions.md +151 -0
  278. package/kb/reference/quick-wins-implementation-summary.md +70 -0
  279. package/kb/reference/skill-templates.md +50 -0
  280. package/kb/reference/skills-catalog.md +215 -0
  281. package/kb/reference/skills-unification.md +57 -0
  282. package/kb/reference/stats.md +69 -0
  283. package/kb/reference/sync.md +76 -0
  284. package/kb/troubleshooting/README.md +11 -0
  285. package/llms-full.txt +3068 -0
  286. package/llms.txt +39 -0
  287. package/package.json +75 -0
  288. package/scripts/_common.py +160 -0
  289. package/scripts/add_rule.py +50 -0
  290. package/scripts/benchmark_config.py +127 -0
  291. package/scripts/benchmark_ecosystem.py +288 -0
  292. package/scripts/check_deps.py +260 -0
  293. package/scripts/create_skill.py +118 -0
  294. package/scripts/doctor.py +504 -0
  295. package/scripts/eject.py +113 -0
  296. package/scripts/emission.py +256 -0
  297. package/scripts/evaluate_skills.py +260 -0
  298. package/scripts/frontmatter.py +58 -0
  299. package/scripts/generate_agents_md.py +91 -0
  300. package/scripts/generate_aider_conf.py +51 -0
  301. package/scripts/generate_cline.py +35 -0
  302. package/scripts/generate_copilot.py +30 -0
  303. package/scripts/generate_cursor_rules.py +35 -0
  304. package/scripts/generate_gemini.py +28 -0
  305. package/scripts/generate_llms_txt.py +164 -0
  306. package/scripts/generate_roo_modes.py +80 -0
  307. package/scripts/generate_windsurf.py +35 -0
  308. package/scripts/generator_base.py +140 -0
  309. package/scripts/harvest_ecosystem.py +50 -0
  310. package/scripts/inject_rule_cli.py +101 -0
  311. package/scripts/inject_section_cli.py +47 -0
  312. package/scripts/injection.py +180 -0
  313. package/scripts/install.py +236 -0
  314. package/scripts/install_git_hooks.py +71 -0
  315. package/scripts/install_steps/__init__.py +5 -0
  316. package/scripts/install_steps/ai_tools.py +261 -0
  317. package/scripts/install_steps/hooks.py +90 -0
  318. package/scripts/install_steps/markers.py +79 -0
  319. package/scripts/install_steps/symlinks.py +87 -0
  320. package/scripts/merge-hooks.py +192 -0
  321. package/scripts/plugin.py +642 -0
  322. package/scripts/plugin_schema.py +138 -0
  323. package/scripts/remove_rule.py +58 -0
  324. package/scripts/stats.py +81 -0
  325. package/scripts/sync.py +215 -0
  326. package/scripts/uninstall.py +292 -0
  327. package/scripts/validate.py +700 -0
@@ -0,0 +1,256 @@
1
+ """Markdown emission helpers for agent/skill listing and generator content blocks.
2
+
3
+ Provides functions to emit agents and skills as markdown headings or
4
+ bullet lists, plus shared content blocks (guidelines, quality standards)
5
+ used by the various ``generate_*.py`` scripts.
6
+
7
+ Stdlib-only.
8
+
9
+ Usage::
10
+
11
+ from emission import emit_agents_headings, emit_skills_bullets
12
+ from emission import generate_general_guidelines
13
+ """
14
+ from __future__ import annotations
15
+
16
+ from pathlib import Path
17
+
18
+ from frontmatter import frontmatter_field
19
+
20
+
21
+ def _resolve_toolkit_dir() -> Path:
22
+ """Resolve the toolkit root directory (parent of scripts/)."""
23
+ return Path(__file__).resolve().parent.parent
24
+
25
+
26
+ toolkit_dir: Path = _resolve_toolkit_dir()
27
+ app_dir: Path = toolkit_dir / "app"
28
+ agents_dir: Path = app_dir / "agents"
29
+ skills_dir: Path = app_dir / "skills"
30
+
31
+
32
+ # ---------------------------------------------------------------------------
33
+ # Counting
34
+ # ---------------------------------------------------------------------------
35
+
36
+ def agent_count() -> int:
37
+ """Count agent .md files in app/agents/."""
38
+ if not agents_dir.is_dir():
39
+ return 0
40
+ return sum(1 for f in agents_dir.iterdir() if f.suffix == ".md" and f.is_file())
41
+
42
+
43
+ def skill_count() -> int:
44
+ """Count skill directories containing SKILL.md in app/skills/.
45
+
46
+ Directories starting with ``_`` (like ``_lib``) are excluded.
47
+ """
48
+ if not skills_dir.is_dir():
49
+ return 0
50
+ return sum(1 for d in skills_dir.iterdir()
51
+ if d.is_dir() and not d.name.startswith("_") and (d / "SKILL.md").is_file())
52
+
53
+
54
+ def count_agents_and_skills() -> tuple[int, int]:
55
+ """Return (agent_count, skill_count) as a convenience tuple."""
56
+ return agent_count(), skill_count()
57
+
58
+
59
+ # ---------------------------------------------------------------------------
60
+ # Markdown emission
61
+ # ---------------------------------------------------------------------------
62
+
63
+ def emit_agents_headings(level: str = "##") -> str:
64
+ """Emit agents as markdown headings with descriptions."""
65
+ lines: list[str] = []
66
+ for agent_file in sorted(agents_dir.glob("*.md")):
67
+ name = frontmatter_field(agent_file, "name")
68
+ description = frontmatter_field(agent_file, "description")
69
+ if not name or not description:
70
+ continue
71
+ lines.append(f"{level} {name}")
72
+ lines.append(description)
73
+ lines.append("")
74
+ return "\n".join(lines)
75
+
76
+
77
+ def emit_agents_bullets() -> str:
78
+ """Emit agents as bullet list: - **name**: description."""
79
+ lines: list[str] = []
80
+ for agent_file in sorted(agents_dir.glob("*.md")):
81
+ name = frontmatter_field(agent_file, "name")
82
+ description = frontmatter_field(agent_file, "description")
83
+ if not name or not description:
84
+ continue
85
+ lines.append(f"- **{name}**: {description}")
86
+ return "\n".join(lines)
87
+
88
+
89
+ def emit_skills_headings(level: str = "##") -> str:
90
+ """Emit skills as markdown headings with descriptions."""
91
+ lines: list[str] = []
92
+ for skill_dir in sorted(skills_dir.iterdir()):
93
+ if skill_dir.name.startswith("_"):
94
+ continue
95
+ skill_file = skill_dir / "SKILL.md"
96
+ if not skill_file.is_file():
97
+ continue
98
+ name = frontmatter_field(skill_file, "name")
99
+ description = frontmatter_field(skill_file, "description")
100
+ if not name or not description:
101
+ continue
102
+ lines.append(f"{level} {name}")
103
+ lines.append(description)
104
+ lines.append("")
105
+ return "\n".join(lines)
106
+
107
+
108
+ def emit_skills_bullets() -> str:
109
+ """Emit skills as bullet list: - **name**: description."""
110
+ lines: list[str] = []
111
+ for skill_dir in sorted(skills_dir.iterdir()):
112
+ if skill_dir.name.startswith("_"):
113
+ continue
114
+ skill_file = skill_dir / "SKILL.md"
115
+ if not skill_file.is_file():
116
+ continue
117
+ name = frontmatter_field(skill_file, "name")
118
+ description = frontmatter_field(skill_file, "description")
119
+ if not name or not description:
120
+ continue
121
+ lines.append(f"- **{name}**: {description}")
122
+ return "\n".join(lines)
123
+
124
+
125
+ # ---------------------------------------------------------------------------
126
+ # Toolkit markers (print helpers)
127
+ # ---------------------------------------------------------------------------
128
+
129
+ def print_toolkit_start() -> None:
130
+ """Print the standard toolkit start marker and auto-generated comment."""
131
+ print("<!-- TOOLKIT:ai-toolkit START -->")
132
+ print("<!-- Auto-generated by ai-toolkit. Re-run to update. -->")
133
+ print()
134
+
135
+
136
+ def print_toolkit_end() -> None:
137
+ """Print the standard toolkit end marker."""
138
+ print("<!-- TOOLKIT:ai-toolkit END -->")
139
+
140
+
141
+ # ---------------------------------------------------------------------------
142
+ # Shared content blocks for generators
143
+ # ---------------------------------------------------------------------------
144
+
145
+ def generate_general_guidelines() -> str:
146
+ """Return the general guidelines block used by cursor/cline/windsurf generators."""
147
+ lines = [
148
+ "## General Guidelines",
149
+ "",
150
+ '- Apply "Safety First": no data loss, no blind execution, max 3 loop iterations',
151
+ "- Research before acting: check existing code and context before proposing changes",
152
+ "- Use structured commits: feat/fix/docs/refactor/test/chore prefixes",
153
+ "- Quality gates: lint must pass, types must check, tests must be green before done",
154
+ "- Prefer editing existing files over creating new ones",
155
+ "- Never commit secrets or credentials",
156
+ ]
157
+ return "\n".join(lines)
158
+
159
+
160
+ def generate_quality_standards() -> str:
161
+ """Return the full constitution-based quality standards for Gemini."""
162
+ lines = [
163
+ "## Quality Standards",
164
+ "",
165
+ "Derived from the immutable safety constitution (5 articles):",
166
+ "",
167
+ "**Article I — Safety First**",
168
+ "- No data loss: never delete files without backup verification"
169
+ " or using reversible operations",
170
+ "- No blind execution: never run LLM-generated code without"
171
+ " static analysis or review",
172
+ "- No infinite loops: all autonomous loops must have a maximum"
173
+ " iteration count (max 3)",
174
+ "",
175
+ "**Article II — Hierarchy of Truth**",
176
+ "- The Knowledge Base (`kb/`) is the source of truth;"
177
+ " if code contradicts KB, check KB freshness",
178
+ "- Use the research-mastery skill before any major decision;"
179
+ " guessing is forbidden",
180
+ "",
181
+ "**Article III — Operational Integrity**",
182
+ '- "Green Tests" is the only definition of Done;'
183
+ " forced merges on red tests are unacceptable",
184
+ "- Never delete audit logs or KB archives without explicit"
185
+ " user approval and backup verification",
186
+ "- Agents cannot change their own model or tool permissions"
187
+ " without user approval",
188
+ "",
189
+ "**Article IV — Self-Preservation**",
190
+ "- The constitution file is read-only for all agents except the user",
191
+ "- If a constitutional violation is detected, halt the offending"
192
+ " operation immediately",
193
+ "",
194
+ "**Article V — Resource Governance**",
195
+ "- Commands like `rm -rf`, `DROP TABLE`, `FORMAT` require explicit"
196
+ " user confirmation",
197
+ "- Operate within assigned model tiers; model tier changes"
198
+ " require user approval",
199
+ ]
200
+ return "\n".join(lines)
201
+
202
+
203
+ def generate_workflow_guidelines() -> str:
204
+ """Return the workflow guidelines block used by the Gemini generator."""
205
+ lines = [
206
+ "## Workflow Guidelines",
207
+ "",
208
+ "- **Plan First**: Tasks longer than 1 hour require a plan,"
209
+ " success criteria, and pre-mortem",
210
+ "- **Multi-Agent**: Use minimum 3 agents for complex tasks;"
211
+ " single-agent for simple tasks",
212
+ "- **2-Phase Execution**: Plan \u2192 User Approval \u2192 Implement"
213
+ " (never skip the approval checkpoint)",
214
+ "- **KB-First Research**: Search the knowledge base before writing"
215
+ " code or answering questions",
216
+ "- **Structured Commits**: Use `feat/fix/docs/refactor/test/chore`"
217
+ " prefixes (Conventional Commits)",
218
+ "- **Quality Gates**: Run `ruff check .` (Python), `tsc` (TypeScript),"
219
+ " `go vet` (Go) before marking done",
220
+ "- **Cite Sources**: Always reference `[PATH: ...]` when making"
221
+ " decisions based on existing knowledge",
222
+ "- **Read-Only Exploration**: Discovery agents never write;"
223
+ " writing agents never explore blindly",
224
+ "- **No Secrets in Code**: Never commit credentials, API keys,"
225
+ " or sensitive configuration values",
226
+ ]
227
+ return "\n".join(lines)
228
+
229
+
230
+ def generate_quality_guidelines() -> str:
231
+ """Return the quality guidelines block used by the Copilot generator."""
232
+ lines = [
233
+ "## Quality Guidelines",
234
+ "",
235
+ '- **Safety First**: No data loss, no blind execution,'
236
+ " maximum 3 autonomous loop iterations",
237
+ "- **No Blind Execution**: Never run LLM-generated code"
238
+ " without static analysis or review",
239
+ '- **Tests are Sacred**: "Green Tests" is the only definition of Done;'
240
+ " never force-merge on red tests",
241
+ "- **No Destructive Commands**: Commands like `rm -rf`, `DROP TABLE`,"
242
+ " `FORMAT` require explicit user confirmation",
243
+ "- **No Secrets in Code**: Never commit secrets, credentials,"
244
+ " or API keys to the repository",
245
+ "- **Research Before Acting**: Check existing code and context"
246
+ " before proposing changes",
247
+ "- **Structured Commits**: Use `feat/fix/docs/refactor/test/chore`"
248
+ " prefixes (Conventional Commits)",
249
+ "- **Quality Gates**: Lint must pass, types must check,"
250
+ " tests must be green before marking done",
251
+ "- **Prefer Editing**: Edit existing files over creating new ones;"
252
+ " avoid unnecessary churn",
253
+ "- **Cite Sources**: Always reference where information came from"
254
+ " when making architectural decisions",
255
+ ]
256
+ return "\n".join(lines)
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env python3
2
+ """AI Toolkit Skill Evaluation.
3
+
4
+ Validates all skills against the Agent Skills standard.
5
+ Checks frontmatter fields, naming, classification, reference links,
6
+ template directories, and dependency resolution.
7
+
8
+ Usage:
9
+ evaluate_skills.py
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import re
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
18
+ from _common import frontmatter_block, frontmatter_field, skills_dir
19
+
20
+ # Deprecated frontmatter fields and their replacements
21
+ _DEPRECATED_FIELDS: list[tuple[str, str]] = [
22
+ ("version", "deprecated 'version' field in frontmatter"),
23
+ ("color", "deprecated 'color' field in frontmatter"),
24
+ ("tools", "deprecated 'tools' field (use 'allowed-tools')"),
25
+ ("delegate-agent", "deprecated 'delegate-agent' field (rename to 'agent:')"),
26
+ ("run-mode", "deprecated 'run-mode' field (rename to 'context:')"),
27
+ ]
28
+
29
+
30
+ def _fm_has(fm_text: str, field: str) -> bool:
31
+ """Check if frontmatter text contains a field line."""
32
+ return any(
33
+ line.startswith(f"{field}:")
34
+ for line in fm_text.splitlines()
35
+ )
36
+
37
+
38
+ def _fm_value(fm_text: str, field: str) -> str:
39
+ """Extract a field value from raw frontmatter text."""
40
+ for line in fm_text.splitlines():
41
+ if line.startswith(f"{field}:"):
42
+ val = line[len(field) + 1:].strip()
43
+ if len(val) >= 2 and val[0] == val[-1] and val[0] in ('"', "'"):
44
+ val = val[1:-1]
45
+ return val
46
+ return ""
47
+
48
+
49
+ def _check_frontmatter_fields(fm: str, skill_path: Path, line_count: int) -> list[str]:
50
+ """Check required fields, name format, description, line count, and deprecated fields."""
51
+ issues: list[str] = []
52
+
53
+ if not _fm_has(fm, "name"):
54
+ issues.append("missing name field")
55
+ if not _fm_has(fm, "description"):
56
+ issues.append("missing description field")
57
+
58
+ name_value = _fm_value(fm, "name")
59
+ if name_value:
60
+ if len(name_value) > 64:
61
+ issues.append(f"name exceeds 64 chars ({len(name_value)})")
62
+ if re.search(r"[^a-z0-9-]", name_value):
63
+ issues.append(f"name has invalid chars: {name_value}")
64
+
65
+ desc_value = _fm_value(fm, "description")
66
+ if desc_value and re.match(r"^(I |You |We )", desc_value):
67
+ issues.append("description not in third person")
68
+
69
+ if line_count > 500:
70
+ issues.append(f"SKILL.md has {line_count} lines (max: 500)")
71
+
72
+ for field, message in _DEPRECATED_FIELDS:
73
+ if _fm_has(fm, field):
74
+ issues.append(message)
75
+
76
+ return issues
77
+
78
+
79
+ def _check_references_and_templates(skill_path: Path, content: str) -> list[str]:
80
+ """Check reference links, orphan files, and template directories."""
81
+ issues: list[str] = []
82
+
83
+ ref_dir = skill_path / "reference"
84
+ if ref_dir.is_dir():
85
+ for match in re.finditer(r"\(reference/([^)]+)\)", content):
86
+ ref_link = match.group(0).strip("()")
87
+ if not (skill_path / ref_link).is_file():
88
+ issues.append(f"broken reference link: {ref_link}")
89
+ for ref_file in sorted(ref_dir.glob("*.md")):
90
+ if f"reference/{ref_file.name}" not in content:
91
+ issues.append(f"orphan reference file: reference/{ref_file.name}")
92
+
93
+ tmpl_dir = skill_path / "templates"
94
+ if tmpl_dir.is_dir():
95
+ tmpl_files = [f for f in tmpl_dir.rglob("*") if f.is_file()]
96
+ if not tmpl_files:
97
+ issues.append("empty templates/ directory")
98
+
99
+ return issues
100
+
101
+
102
+ def _determine_type_label(fm: str) -> str:
103
+ """Determine skill type from frontmatter fields."""
104
+ has_disable = _fm_has(fm, "disable-model-invocation") and "true" in _fm_value(fm, "disable-model-invocation")
105
+ has_user_invocable_false = _fm_has(fm, "user-invocable") and "false" in _fm_value(fm, "user-invocable")
106
+
107
+ if has_disable:
108
+ return "task"
109
+ elif has_user_invocable_false:
110
+ return "knowledge"
111
+ return "hybrid"
112
+
113
+
114
+ def _evaluate_skill(skill_path: Path) -> tuple[str, list[str]]:
115
+ """Evaluate a single skill directory.
116
+
117
+ Returns:
118
+ (type_label, issues) where type_label is "task", "knowledge", or "hybrid"
119
+ and issues is a list of problem descriptions (empty if passing).
120
+ """
121
+ skill_file = skill_path / "SKILL.md"
122
+
123
+ if not skill_file.is_file():
124
+ return ("unknown", ["Missing SKILL.md"])
125
+
126
+ fm = frontmatter_block(skill_file)
127
+ content = skill_file.read_text(encoding="utf-8")
128
+ line_count = content.count("\n") + (1 if content and not content.endswith("\n") else 0)
129
+
130
+ issues = _check_frontmatter_fields(fm, skill_path, line_count)
131
+ issues.extend(_check_references_and_templates(skill_path, content))
132
+
133
+ return (_determine_type_label(fm), issues)
134
+
135
+
136
+ def _evaluate_all_skills() -> tuple[int, int, int, int, int]:
137
+ """Evaluate all skills and print per-skill results.
138
+
139
+ Returns (total, pass_count, fail_count, task_count, knowledge_count).
140
+ """
141
+ pass_count = 0
142
+ fail_count = 0
143
+ total = 0
144
+ task_count = 0
145
+ knowledge_count = 0
146
+
147
+ for skill_path in sorted(skills_dir.iterdir()):
148
+ if not skill_path.is_dir() or skill_path.name.startswith("_"):
149
+ continue
150
+ total += 1
151
+ skill_file = skill_path / "SKILL.md"
152
+
153
+ if not skill_file.is_file():
154
+ print(f"FAIL: {skill_path.name} - Missing SKILL.md")
155
+ fail_count += 1
156
+ continue
157
+
158
+ content = skill_file.read_text(encoding="utf-8")
159
+ line_count = content.count("\n") + (1 if content and not content.endswith("\n") else 0)
160
+
161
+ type_label, issues = _evaluate_skill(skill_path)
162
+
163
+ if issues:
164
+ print(f"FAIL: {skill_path.name} ({line_count} lines)")
165
+ for issue in issues:
166
+ print(f" {issue}")
167
+ print()
168
+ fail_count += 1
169
+ else:
170
+ print(f"PASS: {skill_path.name} ({type_label}, {line_count} lines)")
171
+ pass_count += 1
172
+
173
+ if type_label == "task":
174
+ task_count += 1
175
+ elif type_label == "knowledge":
176
+ knowledge_count += 1
177
+
178
+ return total, pass_count, fail_count, task_count, knowledge_count
179
+
180
+
181
+ def _collect_quality_metrics() -> tuple[int, int, int, int, int, int]:
182
+ """Collect quality metrics across all skills.
183
+
184
+ Returns (ref_count, tmpl_count, inject_count, over500, depends_count, orphan_deps).
185
+ """
186
+ ref_count = sum(1 for _ in skills_dir.rglob("reference/*.md"))
187
+ tmpl_count = sum(1 for _ in skills_dir.rglob("templates/*") if _.is_file())
188
+
189
+ inject_count = 0
190
+ over500 = 0
191
+ depends_count = 0
192
+ orphan_deps = 0
193
+
194
+ for sf in skills_dir.glob("*/SKILL.md"):
195
+ text = sf.read_text(encoding="utf-8")
196
+ lc = text.count("\n") + (1 if text and not text.endswith("\n") else 0)
197
+ if "!`" in text:
198
+ inject_count += 1
199
+ if lc > 500:
200
+ over500 += 1
201
+ fm = frontmatter_block(sf)
202
+ dep_line = _fm_value(fm, "depends-on")
203
+ if dep_line:
204
+ depends_count += 1
205
+ for dep in dep_line.split(","):
206
+ dep = dep.strip()
207
+ if not dep:
208
+ continue
209
+ if not (skills_dir / dep / "SKILL.md").is_file():
210
+ orphan_deps += 1
211
+
212
+ return ref_count, tmpl_count, inject_count, over500, depends_count, orphan_deps
213
+
214
+
215
+ def main() -> None:
216
+ """Run evaluation across all skills and print report."""
217
+ print("AI Toolkit Skill Evaluation")
218
+ print("================================")
219
+ print()
220
+
221
+ if not skills_dir.is_dir():
222
+ print("No skills directory found.")
223
+ sys.exit(1)
224
+
225
+ total, pass_count, fail_count, task_count, knowledge_count = _evaluate_all_skills()
226
+ hybrid_count = total - task_count - knowledge_count
227
+
228
+ print()
229
+ print("================================")
230
+ print(f"Total: {total} skills")
231
+ print(f"Pass: {pass_count}")
232
+ print(f"Fail: {fail_count}")
233
+ print()
234
+
235
+ print("Classification:")
236
+ print(f" Task: {task_count}")
237
+ print(f" Hybrid: {hybrid_count}")
238
+ print(f" Knowledge: {knowledge_count}")
239
+ print()
240
+
241
+ ref_count, tmpl_count, inject_count, over500, depends_count, orphan_deps = _collect_quality_metrics()
242
+
243
+ print("Quality Metrics:")
244
+ print(f" Reference files: {ref_count}")
245
+ print(f" Template files: {tmpl_count}")
246
+ print(f" Dynamic injection: {inject_count} skills")
247
+ print(f" Over 500 lines: {over500}")
248
+ print(f" Skills with depends-on: {depends_count}")
249
+ print(f" Orphan dependencies: {orphan_deps}")
250
+ print()
251
+
252
+ if fail_count > 0:
253
+ print("EVALUATION: ISSUES FOUND")
254
+ sys.exit(1)
255
+ else:
256
+ print("EVALUATION: ALL SKILLS PASS")
257
+
258
+
259
+ if __name__ == "__main__":
260
+ main()
@@ -0,0 +1,58 @@
1
+ """YAML frontmatter parsing for agent and skill markdown files.
2
+
3
+ Stdlib-only. Extracts fields from ``---`` delimited frontmatter blocks.
4
+
5
+ Usage::
6
+
7
+ from frontmatter import frontmatter_field, frontmatter_block
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+
13
+
14
+ def frontmatter_field(filepath: str | Path, field: str) -> str:
15
+ """Extract a YAML frontmatter field value from a file.
16
+
17
+ Reads lines between the first pair of ``---`` delimiters and returns
18
+ the value for the given field. Strips surrounding quotes.
19
+ """
20
+ filepath = Path(filepath)
21
+ if not filepath.is_file():
22
+ return ""
23
+ in_frontmatter = False
24
+ with open(filepath, encoding="utf-8") as f:
25
+ for line in f:
26
+ stripped = line.rstrip("\n")
27
+ if stripped == "---":
28
+ if in_frontmatter:
29
+ break
30
+ in_frontmatter = True
31
+ continue
32
+ if in_frontmatter and stripped.startswith(f"{field}:"):
33
+ value = stripped[len(field) + 1:].strip()
34
+ # Strip surrounding quotes
35
+ if len(value) >= 2 and value[0] == value[-1] and value[0] in ('"', "'"):
36
+ value = value[1:-1]
37
+ return value
38
+ return ""
39
+
40
+
41
+ def frontmatter_block(filepath: str | Path) -> str:
42
+ """Return the raw frontmatter text (excluding --- delimiters)."""
43
+ filepath = Path(filepath)
44
+ if not filepath.is_file():
45
+ return ""
46
+ lines: list[str] = []
47
+ in_fm = False
48
+ with open(filepath, encoding="utf-8") as f:
49
+ for line in f:
50
+ stripped = line.rstrip("\n")
51
+ if stripped == "---":
52
+ if in_fm:
53
+ break
54
+ in_fm = True
55
+ continue
56
+ if in_fm:
57
+ lines.append(stripped)
58
+ return "\n".join(lines)
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env python3
2
+ """Generate AGENTS.md from app/agents/*.md frontmatter.
3
+
4
+ Output is compatible with Codex, OpenCode, and Gemini CLI AGENTS.md format.
5
+ Usage: ./scripts/generate_agents_md.py > AGENTS.md
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
13
+ from _common import agents_dir, frontmatter_field
14
+
15
+
16
+ def main() -> None:
17
+ print("# AGENTS.md")
18
+ print()
19
+ print(
20
+ "This file describes the specialized AI agents bundled with ai-toolkit."
21
+ )
22
+ print(
23
+ "It is auto-generated from `app/agents/*.md` frontmatter"
24
+ " — do not edit manually."
25
+ )
26
+ print()
27
+ print("To regenerate: `python3 scripts/generate_agents_md.py > AGENTS.md`")
28
+ print()
29
+ print("Compatible with: Claude Code, Codex, OpenCode, Gemini CLI.")
30
+ print()
31
+ print("---")
32
+ print()
33
+ print("## Usage")
34
+ print()
35
+ print("### Claude Code")
36
+ print(
37
+ "Agents are loaded automatically from `.claude/agents/`"
38
+ " after running `install.sh`."
39
+ )
40
+ print("Invoke via the Agent tool:")
41
+ print("```")
42
+ print(
43
+ 'Use subagent_type: "backend-specialist" to implement the API endpoint.'
44
+ )
45
+ print("```")
46
+ print()
47
+ print("### Codex / OpenCode")
48
+ print("Reference agents by name in your prompts:")
49
+ print("```")
50
+ print("@backend-specialist implement the payment API")
51
+ print("```")
52
+ print()
53
+ print("### Gemini CLI")
54
+ print("Use agent descriptions as system context:")
55
+ print("```")
56
+ print(
57
+ 'gemini --system "$(cat .claude/agents/backend-specialist.md)"'
58
+ ' "implement the API"'
59
+ )
60
+ print("```")
61
+ print()
62
+ print("---")
63
+ print()
64
+ print("## Agents")
65
+ print()
66
+
67
+ for agent_file in sorted(agents_dir.glob("*.md")):
68
+ if not agent_file.is_file():
69
+ continue
70
+
71
+ name = frontmatter_field(agent_file, "name")
72
+ description = frontmatter_field(agent_file, "description")
73
+ tools = frontmatter_field(agent_file, "tools")
74
+
75
+ if not name:
76
+ continue
77
+
78
+ print(f"### `{name}`")
79
+ print()
80
+ if description:
81
+ print(description)
82
+ print()
83
+ if tools:
84
+ print(f"**Tools:** `{tools}`")
85
+ print()
86
+ print("---")
87
+ print()
88
+
89
+
90
+ if __name__ == "__main__":
91
+ main()