@softspark/ai-toolkit 3.0.2 → 3.1.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/AGENTS.md +13 -0
- package/CHANGELOG.md +23 -0
- package/README.md +30 -23
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/skills/cpp-rules/SKILL.md +275 -0
- package/app/skills/csharp-rules/SKILL.md +282 -0
- package/app/skills/dart-rules/SKILL.md +299 -0
- package/app/skills/golang-rules/SKILL.md +262 -0
- package/app/skills/java-rules/SKILL.md +273 -0
- package/app/skills/kotlin-rules/SKILL.md +271 -0
- package/app/skills/medplum-rules/SKILL.md +271 -0
- package/app/skills/php-rules/SKILL.md +292 -0
- package/app/skills/python-rules/SKILL.md +257 -0
- package/app/skills/ruby-rules/SKILL.md +286 -0
- package/app/skills/rust-rules/SKILL.md +276 -0
- package/app/skills/swift-rules/SKILL.md +293 -0
- package/app/skills/typescript-rules/SKILL.md +249 -0
- package/benchmarks/ecosystem-doctor-snapshot.json +14 -14
- package/kb/history/completed/deep-coverage-v3-20260423.md +3 -3
- package/kb/history/completed/ecosystem-deep-sweep-20260423.md +1 -1
- package/kb/procedures/release-preparation-sop.md +4 -4
- package/kb/procedures/release-verification-sop.md +11 -12
- package/kb/reference/architecture-overview.md +1 -1
- package/kb/reference/global-install-model.md +29 -6
- package/kb/reference/language-rules.md +54 -18
- package/kb/reference/mcp-editor-compatibility.md +4 -3
- package/kb/reference/mcp-templates.md +3 -2
- package/kb/reference/supported-tools-registry.md +10 -8
- package/llms-full.txt +133 -58
- package/manifest.json +3 -3
- package/package.json +10 -3
- package/scripts/codex_skill_adapter.py +19 -3
- package/scripts/ecosystem_tools.json +7 -7
- package/scripts/generate_cline_rules.py +17 -8
- package/scripts/generate_codex_skills.py +33 -96
- package/scripts/generate_language_rules_skills.py +232 -0
- package/scripts/generate_roo_rules.py +11 -3
- package/scripts/install.py +6 -1
- package/scripts/install_steps/ai_tools.py +154 -51
- package/scripts/install_steps/install_state.py +14 -2
- package/scripts/mcp_editors.py +7 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Install
|
|
1
|
+
"""Install global and project-local AI tool configs."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import shutil
|
|
@@ -53,14 +53,6 @@ def install_ai_tools(target_dir: Path, rules_dir: Path,
|
|
|
53
53
|
# control Claude components like agents, hooks, rules). If an editor is
|
|
54
54
|
# in the requested set, install it unconditionally.
|
|
55
55
|
|
|
56
|
-
if "cursor" in eds:
|
|
57
|
-
cursor_file = target_dir / ".cursor" / "rules"
|
|
58
|
-
if dry_run:
|
|
59
|
-
print(" Would inject: ~/.cursor/rules")
|
|
60
|
-
else:
|
|
61
|
-
inject_with_rules("generate-cursor-rules.sh", cursor_file, rules_dir)
|
|
62
|
-
installed.append("cursor")
|
|
63
|
-
|
|
64
56
|
if "windsurf" in eds:
|
|
65
57
|
windsurf_file = target_dir / ".codeium" / "windsurf" / "memories" / "global_rules.md"
|
|
66
58
|
if dry_run:
|
|
@@ -85,6 +77,27 @@ def install_ai_tools(target_dir: Path, rules_dir: Path,
|
|
|
85
77
|
inject_with_rules("generate-augment.sh", augment_file, rules_dir)
|
|
86
78
|
installed.append("augment")
|
|
87
79
|
|
|
80
|
+
if "cline" in eds:
|
|
81
|
+
if dry_run:
|
|
82
|
+
print(" Would generate: ~/Documents/Cline/Rules/ai-toolkit-*.md")
|
|
83
|
+
else:
|
|
84
|
+
_install_cline_global(target_dir, rules_dir)
|
|
85
|
+
installed.append("cline")
|
|
86
|
+
|
|
87
|
+
if "roo" in eds:
|
|
88
|
+
if dry_run:
|
|
89
|
+
print(" Would generate: ~/.roo/rules/ai-toolkit-*.md")
|
|
90
|
+
else:
|
|
91
|
+
_install_roo_global(target_dir, rules_dir)
|
|
92
|
+
installed.append("roo")
|
|
93
|
+
|
|
94
|
+
if "aider" in eds:
|
|
95
|
+
if dry_run:
|
|
96
|
+
print(" Would create if missing: ~/.aider.conf.yml + ~/.aider-ai-toolkit-CONVENTIONS.md")
|
|
97
|
+
else:
|
|
98
|
+
_install_aider_global(target_dir, rules_dir)
|
|
99
|
+
installed.append("aider")
|
|
100
|
+
|
|
88
101
|
if "codex" in eds:
|
|
89
102
|
if dry_run:
|
|
90
103
|
print(" Would inject: ~/AGENTS.md, ~/.agents/, ~/.codex/hooks.json")
|
|
@@ -102,7 +115,7 @@ def install_ai_tools(target_dir: Path, rules_dir: Path,
|
|
|
102
115
|
|
|
103
116
|
print()
|
|
104
117
|
print(f" Available: {', '.join(GLOBAL_CAPABLE_EDITORS)}")
|
|
105
|
-
print(" Note:
|
|
118
|
+
print(" Note: Cursor, Copilot, and Antigravity rule installs are project-local; use 'ai-toolkit install --local' for those.")
|
|
106
119
|
|
|
107
120
|
return installed
|
|
108
121
|
|
|
@@ -136,6 +149,77 @@ def _install_codex_global(target_dir: Path, rules_dir: Path) -> None:
|
|
|
136
149
|
_install_codex_skills(target_dir)
|
|
137
150
|
|
|
138
151
|
|
|
152
|
+
def _install_cline_global(target_dir: Path, rules_dir: Path) -> None:
|
|
153
|
+
"""Install Cline global rules in the documented user rules directory."""
|
|
154
|
+
from generate_cline_rules import generate as gen_cline_rules
|
|
155
|
+
|
|
156
|
+
rules_root = target_dir / "Documents" / "Cline" / "Rules"
|
|
157
|
+
gen_cline_rules(
|
|
158
|
+
target_dir,
|
|
159
|
+
rules_dir=rules_dir,
|
|
160
|
+
output_root=rules_root,
|
|
161
|
+
emit_workflows=False,
|
|
162
|
+
managed_scopes=("standard", "custom"),
|
|
163
|
+
)
|
|
164
|
+
print(" Created: ~/Documents/Cline/Rules/ai-toolkit-*.md")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _install_roo_global(target_dir: Path, rules_dir: Path) -> None:
|
|
168
|
+
"""Install Roo Code global rules in ~/.roo/rules."""
|
|
169
|
+
from generate_roo_rules import generate as gen_roo_rules
|
|
170
|
+
|
|
171
|
+
gen_roo_rules(target_dir, rules_dir=rules_dir, output_root=target_dir / ".roo" / "rules")
|
|
172
|
+
print(" Created: ~/.roo/rules/ai-toolkit-*.md")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _install_aider_global(target_dir: Path, rules_dir: Path) -> None:
|
|
176
|
+
"""Install an Aider global config only when it can be created safely.
|
|
177
|
+
|
|
178
|
+
Aider supports ~/.aider.conf.yml, but YAML merging without a parser risks
|
|
179
|
+
clobbering user settings. For existing files we leave the user's config
|
|
180
|
+
untouched and print an explicit next step.
|
|
181
|
+
"""
|
|
182
|
+
conventions_file = target_dir / ".aider-ai-toolkit-CONVENTIONS.md"
|
|
183
|
+
inject_with_rules("generate_conventions.py", conventions_file, rules_dir)
|
|
184
|
+
|
|
185
|
+
config_file = target_dir / ".aider.conf.yml"
|
|
186
|
+
if config_file.exists():
|
|
187
|
+
print(" Kept: ~/.aider.conf.yml (already exists; not merging YAML automatically)")
|
|
188
|
+
print(f" Available: {conventions_file} for manual read: entry")
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
default_model = _aider_default_model()
|
|
192
|
+
config_file.write_text(
|
|
193
|
+
"\n".join([
|
|
194
|
+
"# Aider configuration generated by ai-toolkit",
|
|
195
|
+
"# Aider docs: https://aider.chat/docs/config/aider_conf.html",
|
|
196
|
+
"",
|
|
197
|
+
"architect: true",
|
|
198
|
+
"auto-accept-architect: true",
|
|
199
|
+
"",
|
|
200
|
+
"read:",
|
|
201
|
+
f' - "{conventions_file}"',
|
|
202
|
+
"",
|
|
203
|
+
f"model: {default_model}",
|
|
204
|
+
f"editor-model: {default_model}",
|
|
205
|
+
"",
|
|
206
|
+
'commit-prompt: "Write a short, concise commit message following Conventional Commits (feat/fix/chore/docs/test/refactor)."',
|
|
207
|
+
"attribute-co-authored-by: false",
|
|
208
|
+
"attribute-commit-message-author: false",
|
|
209
|
+
"attribute-commit-message-committer: false",
|
|
210
|
+
"",
|
|
211
|
+
]),
|
|
212
|
+
encoding="utf-8",
|
|
213
|
+
)
|
|
214
|
+
print(" Created: ~/.aider.conf.yml")
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _aider_default_model() -> str:
|
|
218
|
+
from _common import DEFAULT_CLAUDE_MODELS
|
|
219
|
+
|
|
220
|
+
return DEFAULT_CLAUDE_MODELS["sonnet"]
|
|
221
|
+
|
|
222
|
+
|
|
139
223
|
def _install_opencode_global(target_dir: Path, rules_dir: Path) -> None:
|
|
140
224
|
"""Install opencode at the global level (~/.config/opencode/).
|
|
141
225
|
|
|
@@ -331,10 +415,10 @@ def install_local_project(rules_dir: Path, dry_run: bool, reset: bool,
|
|
|
331
415
|
- ``full``: adds every native surface an editor can host (subagents,
|
|
332
416
|
custom commands, hooks, skill-catalogue pointers).
|
|
333
417
|
|
|
334
|
-
``codex_skills`` (opt-in, off by default)
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
418
|
+
``codex_skills`` (opt-in, off by default) explicitly refreshes the full
|
|
419
|
+
Codex skill catalog under ``.agents/skills/``. ``--editors codex`` already
|
|
420
|
+
installs this catalog for normal local installs; the flag is kept as a
|
|
421
|
+
direct generator contract for scripts and dry-run verification.
|
|
338
422
|
|
|
339
423
|
If ``merged_config`` is provided (from .softspark-toolkit.json extends resolution),
|
|
340
424
|
additional rules and constitution amendments from the base config are injected.
|
|
@@ -491,53 +575,67 @@ def _apply_extends_config(cwd: Path, merged: dict) -> None:
|
|
|
491
575
|
|
|
492
576
|
|
|
493
577
|
def _inject_language_rules(cwd: Path, language_modules: list[str] | None) -> None:
|
|
494
|
-
"""Inject
|
|
578
|
+
"""Inject ``app/rules/common/*.md`` content into project's ``.claude/CLAUDE.md``.
|
|
579
|
+
|
|
580
|
+
Per-language rules (``app/rules/<lang>/``) are NOT injected here -- they
|
|
581
|
+
ship as ``<lang>-rules`` knowledge skills under ``app/skills/`` and load
|
|
582
|
+
contextually via the Agent Skills progressive-disclosure mechanism. This
|
|
583
|
+
keeps ``CLAUDE.md`` small while ensuring language-specific guidance still
|
|
584
|
+
reaches Claude when relevant.
|
|
495
585
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
586
|
+
Common rules are language-agnostic (security, git workflow, testing,
|
|
587
|
+
coding-style, performance) and stay inlined so they remain in scope for
|
|
588
|
+
every prompt.
|
|
499
589
|
"""
|
|
500
590
|
if not language_modules:
|
|
501
591
|
return
|
|
502
592
|
|
|
503
593
|
rules_src = app_dir / "rules"
|
|
504
|
-
|
|
594
|
+
common_dir = rules_src / "common"
|
|
595
|
+
if not common_dir.is_dir():
|
|
505
596
|
return
|
|
506
597
|
|
|
507
|
-
# Detect language
|
|
598
|
+
# Detect requested per-language modules so we can name the linked skills
|
|
599
|
+
# in the marker block. The modules themselves are not inlined.
|
|
508
600
|
langs: list[str] = []
|
|
509
601
|
for mod in language_modules:
|
|
510
602
|
if mod.startswith("rules-"):
|
|
511
|
-
|
|
603
|
+
name = mod[6:]
|
|
604
|
+
if name != "common":
|
|
605
|
+
langs.append(name)
|
|
606
|
+
|
|
607
|
+
# Inline full content of every common rule file, stripping YAML
|
|
608
|
+
# frontmatter so the resulting block reads as plain Markdown.
|
|
609
|
+
inlined: list[str] = []
|
|
610
|
+
for f in sorted(common_dir.glob("*.md")):
|
|
611
|
+
body = f.read_text(encoding="utf-8")
|
|
612
|
+
if body.startswith("---"):
|
|
613
|
+
end = body.find("\n---", 3)
|
|
614
|
+
if end != -1:
|
|
615
|
+
body = body[end + 4:].lstrip("\n")
|
|
616
|
+
inlined.append(body.rstrip())
|
|
512
617
|
|
|
513
|
-
if not langs:
|
|
514
|
-
return
|
|
515
|
-
|
|
516
|
-
# Build a lightweight reference pointer — NOT the full rules content.
|
|
517
|
-
# Full rules are available as knowledge skills (auto-loaded by Claude)
|
|
518
|
-
# and as files Claude can Read on demand.
|
|
519
|
-
toolkit_pkg = app_dir.parent
|
|
520
618
|
lines: list[str] = ["# Language Rules", ""]
|
|
521
|
-
lines.append(
|
|
619
|
+
lines.append(
|
|
620
|
+
"Common (language-agnostic) rules apply to every change in this "
|
|
621
|
+
"project. Language-specific rules live in `<lang>-rules` knowledge "
|
|
622
|
+
"skills (e.g. `python-rules`, `typescript-rules`) and load "
|
|
623
|
+
"automatically when their triggers match -- you do not need to "
|
|
624
|
+
"Read them manually."
|
|
625
|
+
)
|
|
626
|
+
if langs:
|
|
627
|
+
skill_names = ", ".join(f"`{l}-rules`" for l in langs)
|
|
628
|
+
lines.append("")
|
|
629
|
+
lines.append(f"Detected languages: {skill_names}.")
|
|
522
630
|
lines.append("")
|
|
523
|
-
lines.append("
|
|
524
|
-
# Resolve actual installed path for the rules
|
|
525
|
-
rules_resolved = str(rules_src.resolve())
|
|
526
|
-
all_dirs: list[str] = ["common"]
|
|
527
|
-
for l in langs:
|
|
528
|
-
if l not in all_dirs:
|
|
529
|
-
all_dirs.append(l)
|
|
530
|
-
for lang in all_dirs:
|
|
531
|
-
lang_path = rules_src / lang
|
|
532
|
-
if lang_path.is_dir():
|
|
533
|
-
categories = ", ".join(f.stem for f in sorted(lang_path.glob("*.md")))
|
|
534
|
-
lines.append(f"- `{lang_path.resolve()}/` ({categories})")
|
|
631
|
+
lines.append("---")
|
|
535
632
|
lines.append("")
|
|
536
|
-
lines.
|
|
633
|
+
lines.extend(inlined)
|
|
537
634
|
|
|
538
|
-
# Write
|
|
635
|
+
# Write to temp file, then inject as a single named section so reruns are
|
|
636
|
+
# idempotent (existing block is replaced, not duplicated).
|
|
539
637
|
import tempfile
|
|
540
|
-
combined = "\n".join(lines)
|
|
638
|
+
combined = "\n\n".join(lines).rstrip() + "\n"
|
|
541
639
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False,
|
|
542
640
|
encoding="utf-8") as tmp:
|
|
543
641
|
tmp.write(combined)
|
|
@@ -545,8 +643,11 @@ def _inject_language_rules(cwd: Path, language_modules: list[str] | None) -> Non
|
|
|
545
643
|
|
|
546
644
|
try:
|
|
547
645
|
inject_section(tmp_path, cwd / ".claude" / "CLAUDE.md", "language-rules")
|
|
548
|
-
|
|
549
|
-
|
|
646
|
+
if langs:
|
|
647
|
+
print(f" Injected: common rules + {len(langs)} language skill(s) "
|
|
648
|
+
f"({', '.join(langs)})")
|
|
649
|
+
else:
|
|
650
|
+
print(" Injected: common rules")
|
|
550
651
|
finally:
|
|
551
652
|
tmp_path.unlink(missing_ok=True)
|
|
552
653
|
|
|
@@ -600,8 +701,10 @@ def _install_local_dry_run(reset: bool, editors: list[str] | None = None,
|
|
|
600
701
|
"$HOME/.augment/settings.json + .augment/skills/ (profile=full)")
|
|
601
702
|
if "gemini" in eds:
|
|
602
703
|
print(" Would generate: .gemini/commands/ + .gemini/skills/ (profile=full)")
|
|
603
|
-
if "codex" in eds
|
|
604
|
-
print(" Would generate: .
|
|
704
|
+
if "codex" in eds:
|
|
705
|
+
print(" Would generate: .agents/skills/ Codex skills")
|
|
706
|
+
if codex_skills:
|
|
707
|
+
print(" Would refresh: .agents/skills/ via --codex-skills")
|
|
605
708
|
|
|
606
709
|
if not eds:
|
|
607
710
|
print(" No editors selected (use --editors <list> or --editors all)")
|
|
@@ -881,10 +984,10 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
881
984
|
from generate_codex_hooks import generate as gen_codex_hooks
|
|
882
985
|
gen_codex_hooks(cwd)
|
|
883
986
|
print(" Created: .codex/hooks.json")
|
|
884
|
-
# .agents/skills/ —
|
|
987
|
+
# .agents/skills/ — Codex discovery path for repo-local skills
|
|
885
988
|
_install_codex_skills(cwd)
|
|
886
|
-
#
|
|
887
|
-
#
|
|
989
|
+
# --codex-skills explicitly re-runs the same Codex skill sync path.
|
|
990
|
+
# Codex upstream discovers skills from .agents/skills/, not .codex/skills/.
|
|
888
991
|
if codex_skills:
|
|
889
992
|
_try_generator("generate_codex_skills", cwd,
|
|
890
993
|
enable_codex_skills=True)
|
|
@@ -88,8 +88,20 @@ def remove_mcp_template(name: str) -> None:
|
|
|
88
88
|
# Default global install: Claude only — no other editors unless --editors is used
|
|
89
89
|
DEFAULT_GLOBAL_EDITORS: list[str] = []
|
|
90
90
|
|
|
91
|
-
# All editors that support global install (opt-in via --editors)
|
|
92
|
-
|
|
91
|
+
# All editors that support global install (opt-in via --editors).
|
|
92
|
+
#
|
|
93
|
+
# Cursor intentionally stays out of this list: its documented global rules
|
|
94
|
+
# surface is the Settings UI, not a stable file path we can merge safely.
|
|
95
|
+
GLOBAL_CAPABLE_EDITORS = [
|
|
96
|
+
"aider",
|
|
97
|
+
"augment",
|
|
98
|
+
"cline",
|
|
99
|
+
"codex",
|
|
100
|
+
"gemini",
|
|
101
|
+
"opencode",
|
|
102
|
+
"roo",
|
|
103
|
+
"windsurf",
|
|
104
|
+
]
|
|
93
105
|
|
|
94
106
|
|
|
95
107
|
def get_global_editors() -> list[str]:
|
package/scripts/mcp_editors.py
CHANGED
|
@@ -41,6 +41,13 @@ EDITOR_SPECS: dict[str, dict[str, str | None]] = {
|
|
|
41
41
|
"format": "json",
|
|
42
42
|
"doc_scope": "project + global",
|
|
43
43
|
},
|
|
44
|
+
"roo": {
|
|
45
|
+
"label": "Roo Code",
|
|
46
|
+
"project_path": ".roo/mcp.json",
|
|
47
|
+
"global_path": None,
|
|
48
|
+
"format": "json",
|
|
49
|
+
"doc_scope": "project",
|
|
50
|
+
},
|
|
44
51
|
"windsurf": {
|
|
45
52
|
"label": "Windsurf",
|
|
46
53
|
"project_path": None,
|