@softspark/ai-toolkit 4.5.0 → 4.6.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 +50 -0
- package/CHANGELOG.md +37 -0
- package/README.md +10 -10
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/hooks/loop-guard.sh +2 -1
- package/benchmarks/ecosystem-doctor-snapshot.json +67 -54
- package/bin/ai-toolkit.js +2 -5
- package/kb/procedures/release-preparation-sop.md +24 -16
- package/kb/procedures/release-verification-sop.md +3 -5
- package/kb/reference/architecture-overview.md +2 -3
- package/kb/reference/cli-reference.md +2 -3
- package/kb/reference/global-install-model.md +3 -3
- package/kb/reference/opencode-compatibility.md +2 -2
- package/kb/reference/supported-tools-registry.md +20 -18
- package/llms-full.txt +57 -51
- package/manifest.json +1 -1
- package/package.json +2 -3
- package/scripts/ecosystem_tools.json +18 -15
- package/scripts/generate_antigravity.py +11 -7
- package/scripts/generate_augment_agents.py +9 -10
- package/scripts/generate_augment_commands.py +4 -17
- package/scripts/generate_augment_hooks.py +7 -5
- package/scripts/generate_codex.py +28 -0
- package/scripts/generate_codex_hooks.py +23 -1
- package/scripts/generate_cursor_agents.py +13 -29
- package/scripts/generate_opencode_commands.py +9 -9
- package/scripts/install.py +1 -1
- package/scripts/install_steps/ai_tools.py +47 -26
- package/scripts/plugin.py +2 -2
- package/scripts/generate_codex_rules.py +0 -52
|
@@ -79,7 +79,7 @@ def install_ai_tools(target_dir: Path, rules_dir: Path,
|
|
|
79
79
|
|
|
80
80
|
if "cline" in eds:
|
|
81
81
|
if dry_run:
|
|
82
|
-
print(" Would generate:
|
|
82
|
+
print(" Would generate: ~/Documents/Cline/Rules/ai-toolkit-*.md")
|
|
83
83
|
print(" Would generate: ~/.cline/skills/ai-toolkit-skill-catalogue/SKILL.md")
|
|
84
84
|
else:
|
|
85
85
|
_install_cline_global(target_dir, rules_dir)
|
|
@@ -125,8 +125,8 @@ def _install_codex_global(target_dir: Path, rules_dir: Path) -> None:
|
|
|
125
125
|
"""Install Codex at the global level (~/ layer).
|
|
126
126
|
|
|
127
127
|
Creates:
|
|
128
|
-
- ~/AGENTS.md (marker injection
|
|
129
|
-
|
|
128
|
+
- ~/AGENTS.md (marker injection; universal coding rules inlined here —
|
|
129
|
+
Codex reads instructions only from AGENTS.md, not .agents/rules/)
|
|
130
130
|
- ~/.agents/skills/* (skill symlinks)
|
|
131
131
|
- ~/.codex/hooks.json (lifecycle hooks)
|
|
132
132
|
"""
|
|
@@ -136,13 +136,6 @@ def _install_codex_global(target_dir: Path, rules_dir: Path) -> None:
|
|
|
136
136
|
rules_dir,
|
|
137
137
|
)
|
|
138
138
|
|
|
139
|
-
from generate_codex_rules import generate as gen_codex_rules
|
|
140
|
-
gen_codex_rules(
|
|
141
|
-
target_dir,
|
|
142
|
-
rules_dir=rules_dir,
|
|
143
|
-
managed_scopes=("standard", "custom"),
|
|
144
|
-
)
|
|
145
|
-
|
|
146
139
|
from generate_codex_hooks import generate as gen_codex_hooks
|
|
147
140
|
gen_codex_hooks(target_dir)
|
|
148
141
|
print(" Created: ~/.codex/hooks.json")
|
|
@@ -150,12 +143,33 @@ def _install_codex_global(target_dir: Path, rules_dir: Path) -> None:
|
|
|
150
143
|
_install_codex_skills(target_dir)
|
|
151
144
|
|
|
152
145
|
|
|
146
|
+
def _claude_skills_discoverable(project_dir: Path | None = None) -> bool:
|
|
147
|
+
"""True when real ai-toolkit skills already exist at a ``.claude/skills``
|
|
148
|
+
directory the editor scans natively (project-local or global
|
|
149
|
+
``~/.claude/skills``), making an editor skill-catalogue pointer redundant.
|
|
150
|
+
|
|
151
|
+
Cursor, Cline and Augment discover ``SKILL.md`` from ``.claude/skills`` and
|
|
152
|
+
``~/.claude/skills`` by default, so when those are populated the pointer adds
|
|
153
|
+
nothing. (Windsurf/Devin gates that scan behind a setting and is excluded
|
|
154
|
+
from the conditional by its callers.) Returns ``False`` for editor-only
|
|
155
|
+
installs (no Claude skills present), where the pointer is the only
|
|
156
|
+
skill-discovery surface.
|
|
157
|
+
"""
|
|
158
|
+
candidates = [Path.home() / ".claude" / "skills"]
|
|
159
|
+
if project_dir is not None:
|
|
160
|
+
candidates.insert(0, project_dir / ".claude" / "skills")
|
|
161
|
+
return any(d.is_dir() and any(d.glob("*/SKILL.md")) for d in candidates)
|
|
162
|
+
|
|
163
|
+
|
|
153
164
|
def _install_windsurf_global(target_dir: Path, rules_dir: Path) -> None:
|
|
154
165
|
"""Install Windsurf global rules plus an Agent Skills catalogue pointer."""
|
|
155
166
|
windsurf_file = target_dir / ".codeium" / "windsurf" / "memories" / "global_rules.md"
|
|
156
167
|
inject_with_rules("generate-windsurf.sh", windsurf_file, rules_dir)
|
|
157
168
|
|
|
158
169
|
from generate_windsurf_skills import generate as gen_windsurf_skills
|
|
170
|
+
# Windsurf is excluded from the .claude/skills conditional: its native scan
|
|
171
|
+
# of .claude/skills is gated behind a Devin "Claude Code config reading"
|
|
172
|
+
# setting, so we always emit the pointer as a reliable fallback.
|
|
159
173
|
gen_windsurf_skills(
|
|
160
174
|
target_dir,
|
|
161
175
|
skill_root=".codeium/windsurf/skills",
|
|
@@ -166,7 +180,9 @@ def _install_cline_global(target_dir: Path, rules_dir: Path) -> None:
|
|
|
166
180
|
"""Install Cline global rules in the documented ~/.cline directory."""
|
|
167
181
|
from generate_cline_rules import generate as gen_cline_rules
|
|
168
182
|
|
|
169
|
-
|
|
183
|
+
# Cline reads GLOBAL rules from ~/Documents/Cline/Rules/ (docs.cline.bot);
|
|
184
|
+
# ~/.cline/rules/ is not a Cline-read path.
|
|
185
|
+
rules_root = target_dir / "Documents" / "Cline" / "Rules"
|
|
170
186
|
gen_cline_rules(
|
|
171
187
|
target_dir,
|
|
172
188
|
rules_dir=rules_dir,
|
|
@@ -174,10 +190,10 @@ def _install_cline_global(target_dir: Path, rules_dir: Path) -> None:
|
|
|
174
190
|
emit_workflows=False,
|
|
175
191
|
managed_scopes=("standard", "custom"),
|
|
176
192
|
)
|
|
177
|
-
print(" Created:
|
|
193
|
+
print(" Created: ~/Documents/Cline/Rules/ai-toolkit-*.md")
|
|
178
194
|
|
|
179
195
|
from generate_cline_skills import generate as gen_cline_skills
|
|
180
|
-
gen_cline_skills(target_dir)
|
|
196
|
+
gen_cline_skills(target_dir, emit_skill_pointer=not _claude_skills_discoverable(target_dir))
|
|
181
197
|
|
|
182
198
|
|
|
183
199
|
def _install_roo_global(target_dir: Path, rules_dir: Path) -> None:
|
|
@@ -371,6 +387,10 @@ _EDITOR_MARKERS: dict[str, str] = {
|
|
|
371
387
|
".aider.conf.yml": "aider",
|
|
372
388
|
"CONVENTIONS.md": "aider",
|
|
373
389
|
".augment/rules": "augment",
|
|
390
|
+
# Antigravity 2.0 default is plural .agents/; .agents/workflows is the
|
|
391
|
+
# unambiguous marker (Codex also writes .agents/rules but no workflows).
|
|
392
|
+
# Singular .agent/rules is kept to detect legacy installs.
|
|
393
|
+
".agents/workflows": "antigravity",
|
|
374
394
|
".agent/rules": "antigravity",
|
|
375
395
|
".agents/skills": "codex",
|
|
376
396
|
".codex": "codex",
|
|
@@ -694,7 +714,7 @@ def _install_local_dry_run(reset: bool, editors: list[str] | None = None,
|
|
|
694
714
|
"roo": " Would generate: .roomodes + .roo/rules/*.md",
|
|
695
715
|
"aider": " Would generate: .aider.conf.yml + CONVENTIONS.md",
|
|
696
716
|
"augment": " Would generate: .augment/rules/ai-toolkit-*.md",
|
|
697
|
-
"antigravity": " Would generate: .
|
|
717
|
+
"antigravity": " Would generate: .agents/rules/ + .agents/workflows/",
|
|
698
718
|
"gemini": " Would generate: GEMINI.md",
|
|
699
719
|
"opencode": " Would generate: AGENTS.md + .opencode/{agents,commands,plugins}/ + opencode.json",
|
|
700
720
|
}
|
|
@@ -899,6 +919,12 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
899
919
|
add_copilot_dir = _profile in {"standard", "strict", "full"}
|
|
900
920
|
add_gemini_hooks = _profile in {"standard", "strict", "full"}
|
|
901
921
|
add_native_surfaces = _profile == "full"
|
|
922
|
+
# Skip the editor skill-catalogue pointer when real skills are already
|
|
923
|
+
# discoverable at .claude/skills (project or ~/.claude/skills global):
|
|
924
|
+
# Cursor/Cline/Augment read those natively and by default. Emit it only as
|
|
925
|
+
# the editor-only fallback when no Claude skills are present. Windsurf is
|
|
926
|
+
# excluded (its .claude scan is gated) and keeps its pointer unconditionally.
|
|
927
|
+
emit_pointer = not _claude_skills_discoverable(cwd)
|
|
902
928
|
|
|
903
929
|
if "copilot" in eds:
|
|
904
930
|
inject_with_rules(
|
|
@@ -925,7 +951,7 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
925
951
|
if add_native_surfaces:
|
|
926
952
|
_try_generator("generate_cursor_hooks", cwd)
|
|
927
953
|
_try_generator("generate_cursor_agents", cwd)
|
|
928
|
-
_try_generator("generate_cursor_skills", cwd)
|
|
954
|
+
_try_generator("generate_cursor_skills", cwd, emit_skill_pointer=emit_pointer)
|
|
929
955
|
|
|
930
956
|
if "windsurf" in eds:
|
|
931
957
|
inject_with_rules(
|
|
@@ -938,6 +964,7 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
938
964
|
rules_dir=rules_dir)
|
|
939
965
|
if add_native_surfaces:
|
|
940
966
|
_try_generator("generate_windsurf_hooks", cwd)
|
|
967
|
+
# Windsurf pointer stays unconditional (its .claude scan is gated).
|
|
941
968
|
_try_generator("generate_windsurf_skills", cwd)
|
|
942
969
|
|
|
943
970
|
if "cline" in eds:
|
|
@@ -954,7 +981,7 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
954
981
|
managed_scopes=("standard", "lang", "custom"),
|
|
955
982
|
)
|
|
956
983
|
if add_native_surfaces:
|
|
957
|
-
_try_generator("generate_cline_skills", cwd)
|
|
984
|
+
_try_generator("generate_cline_skills", cwd, emit_skill_pointer=emit_pointer)
|
|
958
985
|
|
|
959
986
|
if "roo" in eds:
|
|
960
987
|
roo_output = run_script("generate-roo-modes.sh", capture=True)
|
|
@@ -980,7 +1007,7 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
980
1007
|
# Augment hooks live under $HOME/.augment/settings.json. The
|
|
981
1008
|
# generator takes HOME (not cwd) per bucket-1 contract.
|
|
982
1009
|
_try_generator("generate_augment_hooks", Path.home())
|
|
983
|
-
_try_generator("generate_augment_skills", cwd)
|
|
1010
|
+
_try_generator("generate_augment_skills", cwd, emit_skill_pointer=emit_pointer)
|
|
984
1011
|
|
|
985
1012
|
if "antigravity" in eds:
|
|
986
1013
|
from generate_antigravity import generate as gen_antigravity
|
|
@@ -988,20 +1015,14 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
988
1015
|
rules_dir=rules_dir)
|
|
989
1016
|
|
|
990
1017
|
if "codex" in eds:
|
|
991
|
-
# AGENTS.md — marker injection
|
|
1018
|
+
# AGENTS.md — marker injection; universal coding rules are inlined into
|
|
1019
|
+
# AGENTS.md (Codex reads instructions only from AGENTS.md, not a
|
|
1020
|
+
# .agents/rules/ directory). Language rules reach Codex via .agents/skills/.
|
|
992
1021
|
inject_with_rules(
|
|
993
1022
|
"generate_codex.py",
|
|
994
1023
|
cwd / "AGENTS.md",
|
|
995
1024
|
rules_dir,
|
|
996
1025
|
)
|
|
997
|
-
# .agents/rules/ — directory-based rules
|
|
998
|
-
from generate_codex_rules import generate as gen_codex_rules
|
|
999
|
-
gen_codex_rules(
|
|
1000
|
-
cwd,
|
|
1001
|
-
language_modules=language_modules,
|
|
1002
|
-
rules_dir=rules_dir,
|
|
1003
|
-
managed_scopes=("standard", "lang", "custom"),
|
|
1004
|
-
)
|
|
1005
1026
|
# .codex/hooks.json — Codex lifecycle hooks
|
|
1006
1027
|
from generate_codex_hooks import generate as gen_codex_hooks
|
|
1007
1028
|
gen_codex_hooks(cwd)
|
package/scripts/plugin.py
CHANGED
|
@@ -33,7 +33,6 @@ sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
|
33
33
|
from _common import app_dir, inject_section, inject_rule, remove_rule_section
|
|
34
34
|
from codex_skill_adapter import cleanup_codex_skills, sync_codex_skill
|
|
35
35
|
from generate_codex_hooks import generate as generate_codex_hooks
|
|
36
|
-
from generate_codex_rules import generate as generate_codex_rules
|
|
37
36
|
from install_steps.ai_tools import inject_with_rules
|
|
38
37
|
from paths import HOOKS_DIR as _HOOKS_DIR
|
|
39
38
|
from paths import RULES_DIR, TOOLKIT_DATA_DIR
|
|
@@ -536,8 +535,9 @@ def _install_codex_extra_skills(pack: dict, pack_dir: Path) -> None:
|
|
|
536
535
|
|
|
537
536
|
def _install_codex_base() -> None:
|
|
538
537
|
_ensure_core_hook_scripts()
|
|
538
|
+
# Universal coding rules are inlined into AGENTS.md by generate_codex.py;
|
|
539
|
+
# Codex does not read a .agents/rules/ directory.
|
|
539
540
|
inject_with_rules("generate_codex.py", CODEX_ROOT / "AGENTS.md", RULES_DIR)
|
|
540
|
-
generate_codex_rules(CODEX_ROOT, rules_dir=RULES_DIR)
|
|
541
541
|
hooks_path = CODEX_ROOT / ".codex" / "hooks.json"
|
|
542
542
|
existing = _load_json(hooks_path, {"hooks": {}})
|
|
543
543
|
plugin_entries: dict[str, list[dict]] = {}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Generate Codex CLI .agents/rules/ files.
|
|
3
|
-
|
|
4
|
-
Codex discovers rules in .agents/rules/ at the project root.
|
|
5
|
-
This generator follows the same pattern as generate_antigravity.py.
|
|
6
|
-
|
|
7
|
-
Usage:
|
|
8
|
-
python3 scripts/generate_codex_rules.py [target-dir]
|
|
9
|
-
|
|
10
|
-
Writes files directly to target-dir/.agents/rules/.
|
|
11
|
-
"""
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
|
-
import sys
|
|
15
|
-
from pathlib import Path
|
|
16
|
-
|
|
17
|
-
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
18
|
-
from dir_rules_shared import (
|
|
19
|
-
STANDARD_RULES,
|
|
20
|
-
STANDARD_SCOPE,
|
|
21
|
-
build_language_rules,
|
|
22
|
-
build_registered_rules,
|
|
23
|
-
write_rules,
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def generate(target_dir: Path, *,
|
|
28
|
-
language_modules: list[str] | None = None,
|
|
29
|
-
rules_dir: Path | None = None,
|
|
30
|
-
cleanup: bool = True,
|
|
31
|
-
managed_scopes: tuple[str, ...] = (STANDARD_SCOPE,)) -> None:
|
|
32
|
-
"""Write .agents/rules/ files to target_dir."""
|
|
33
|
-
rules = dict(STANDARD_RULES)
|
|
34
|
-
rules.update(build_language_rules(language_modules))
|
|
35
|
-
rules.update(build_registered_rules(rules_dir))
|
|
36
|
-
write_rules(
|
|
37
|
-
target_dir,
|
|
38
|
-
rules,
|
|
39
|
-
".agents/rules",
|
|
40
|
-
cleanup=cleanup,
|
|
41
|
-
managed_scopes=managed_scopes,
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def main() -> None:
|
|
46
|
-
target = Path(sys.argv[1]) if len(sys.argv) > 1 else Path.cwd()
|
|
47
|
-
from paths import RULES_DIR
|
|
48
|
-
generate(target, rules_dir=RULES_DIR)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if __name__ == "__main__":
|
|
52
|
-
main()
|