@softspark/ai-toolkit 4.11.0 → 4.13.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/CHANGELOG.md +54 -0
- package/README.md +33 -15
- package/app/.claude-plugin/plugin.json +11 -19
- package/app/ARCHITECTURE.md +6 -6
- package/app/claude-app/global-instructions.md +10 -0
- package/app/claude-app/hooks/hooks.json +284 -0
- package/app/claude-app/skills/ai-toolkit-rules/SKILL.md +359 -0
- package/app/hooks/config-desync-guard.sh +63 -23
- package/app/plugins/README.md +4 -1
- package/benchmarks/ecosystem-doctor-snapshot.json +91 -25
- package/bin/ai-toolkit.js +21 -3
- package/kb/planning/drop-cascade-hooks-after-sunset.md +13 -8
- package/kb/procedures/ecosystem-sync-sop.md +5 -5
- package/kb/procedures/maintenance-sop.md +42 -4
- package/kb/procedures/release-preparation-sop.md +5 -1
- package/kb/procedures/release-verification-sop.md +25 -9
- package/kb/reference/architecture-overview.md +7 -2
- package/kb/reference/claude-ecosystem-expansion-foundations.md +12 -3
- package/kb/reference/cli-reference.md +4 -2
- package/kb/reference/codex-cli-compatibility.md +15 -10
- package/kb/reference/global-install-model.md +37 -11
- package/kb/reference/hooks-catalog.md +2 -3
- package/kb/reference/plugin-pack-conventions.md +5 -5
- package/kb/reference/skills-catalog.md +2 -0
- package/kb/reference/supported-tools-registry.md +59 -33
- package/kb/reference/unique-features.md +3 -2
- package/llms-full.txt +236 -98
- package/manifest.json +8 -8
- package/package.json +4 -3
- package/scripts/claude_app.py +347 -0
- package/scripts/doctor.py +85 -4
- package/scripts/ecosystem_tools.json +45 -9
- package/scripts/generate_antigravity.py +15 -0
- package/scripts/generate_codex_hooks.py +8 -5
- package/scripts/generate_copilot.py +10 -3
- package/scripts/generate_devin_hooks.py +3 -4
- package/scripts/generate_windsurf_skills.py +5 -6
- package/scripts/inject_hook_cli.py +16 -2
- package/scripts/install.py +25 -23
- package/scripts/install_steps/ai_tools.py +234 -14
- package/scripts/install_steps/install_state.py +11 -2
- package/scripts/plugin.py +31 -8
- package/scripts/update_projects.py +12 -1
- package/scripts/validate.py +34 -2
- package/AGENTS.md +0 -790
- package/scripts/generate_windsurf_hooks.py +0 -152
package/scripts/plugin.py
CHANGED
|
@@ -23,6 +23,7 @@ Actions:
|
|
|
23
23
|
from __future__ import annotations
|
|
24
24
|
|
|
25
25
|
import json
|
|
26
|
+
import re
|
|
26
27
|
import shutil
|
|
27
28
|
import sqlite3 as sqlite
|
|
28
29
|
import subprocess
|
|
@@ -31,6 +32,7 @@ from pathlib import Path
|
|
|
31
32
|
|
|
32
33
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
33
34
|
from _common import app_dir, inject_section, inject_rule, remove_rule_section
|
|
35
|
+
from injection import strip_section, trim_trailing_blanks
|
|
34
36
|
from codex_skill_adapter import cleanup_codex_skills, sync_codex_skill
|
|
35
37
|
from generate_codex_hooks import generate as generate_codex_hooks
|
|
36
38
|
from install_steps.ai_tools import inject_with_rules
|
|
@@ -536,8 +538,9 @@ def _install_codex_extra_skills(pack: dict, pack_dir: Path) -> None:
|
|
|
536
538
|
def _install_codex_base() -> None:
|
|
537
539
|
_ensure_core_hook_scripts()
|
|
538
540
|
# Universal coding rules are inlined into AGENTS.md by generate_codex.py;
|
|
539
|
-
# Codex does not read a .agents/rules/ directory.
|
|
540
|
-
|
|
541
|
+
# Codex does not read a .agents/rules/ directory. Global instructions must
|
|
542
|
+
# live at $CODEX_HOME/AGENTS.md (default ~/.codex/AGENTS.md), not ~/AGENTS.md.
|
|
543
|
+
inject_with_rules("generate_codex.py", CODEX_ROOT / ".codex" / "AGENTS.md", RULES_DIR)
|
|
541
544
|
hooks_path = CODEX_ROOT / ".codex" / "hooks.json"
|
|
542
545
|
existing = _load_json(hooks_path, {"hooks": {}})
|
|
543
546
|
plugin_entries: dict[str, list[dict]] = {}
|
|
@@ -632,21 +635,41 @@ def _strip_codex_hooks(name: str) -> None:
|
|
|
632
635
|
|
|
633
636
|
|
|
634
637
|
def _install_codex_rules(name: str, rule_specs: list[dict]) -> None:
|
|
635
|
-
|
|
636
|
-
|
|
638
|
+
# Codex reads instructions only from AGENTS.md, never from a .agents/rules/
|
|
639
|
+
# directory, so pack rules are marker-injected into ~/.codex/AGENTS.md (the
|
|
640
|
+
# documented global instruction file) instead of written as dead files.
|
|
641
|
+
agents_md = CODEX_ROOT / ".codex" / "AGENTS.md"
|
|
637
642
|
for spec in rule_specs:
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
print(f"
|
|
643
|
+
section = f"plugin-{name}-{spec['name']}"
|
|
644
|
+
inject_section(spec["source"], agents_md, section)
|
|
645
|
+
print(f" Injected Codex rule: {spec['name']} -> ~/.codex/AGENTS.md")
|
|
646
|
+
_clean_legacy_codex_rule_files(name)
|
|
641
647
|
|
|
642
648
|
|
|
643
649
|
def _remove_codex_rules(name: str) -> None:
|
|
650
|
+
agents_md = CODEX_ROOT / ".codex" / "AGENTS.md"
|
|
651
|
+
if agents_md.is_file():
|
|
652
|
+
content = agents_md.read_text(encoding="utf-8")
|
|
653
|
+
changed = False
|
|
654
|
+
for match in re.findall(r"<!-- TOOLKIT:(plugin-" + re.escape(name) + r"-[^ ]+) START -->", content):
|
|
655
|
+
content = strip_section(content, match)
|
|
656
|
+
changed = True
|
|
657
|
+
if changed:
|
|
658
|
+
agents_md.write_text(trim_trailing_blanks(content) + "\n", encoding="utf-8")
|
|
659
|
+
print(f" Removed Codex rules for {name} from ~/.codex/AGENTS.md")
|
|
660
|
+
_clean_legacy_codex_rule_files(name)
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def _clean_legacy_codex_rule_files(name: str) -> None:
|
|
664
|
+
"""Remove dead ~/.agents/rules/plugin-<name>-*.md files written by earlier
|
|
665
|
+
versions. Codex never read them; Antigravity's .agents/rules/ is a separate,
|
|
666
|
+
project-local surface, so this only touches our own plugin-prefixed files."""
|
|
644
667
|
rules_dir = CODEX_ROOT / ".agents" / "rules"
|
|
645
668
|
if not rules_dir.is_dir():
|
|
646
669
|
return
|
|
647
670
|
for rule_file in sorted(rules_dir.glob(f"plugin-{name}-*.md")):
|
|
648
671
|
rule_file.unlink()
|
|
649
|
-
print(f" Removed Codex rule: {rule_file.name}")
|
|
672
|
+
print(f" Removed legacy Codex rule file: {rule_file.name}")
|
|
650
673
|
|
|
651
674
|
|
|
652
675
|
def install_pack_codex(name: str, pack: dict, pack_dir: Path) -> bool:
|
|
@@ -28,8 +28,19 @@ def _update_project(project: dict[str, Any], install_script: str, extra_args: li
|
|
|
28
28
|
# Pass saved editors from registry so update re-installs the same editors
|
|
29
29
|
cmd_args = ["python3", install_script, "--local"] + extra_args
|
|
30
30
|
project_editors = project.get("editors", [])
|
|
31
|
-
|
|
31
|
+
has_editor_override = any(
|
|
32
|
+
arg == "--editors" or arg.startswith("--editors=")
|
|
33
|
+
for arg in extra_args
|
|
34
|
+
)
|
|
35
|
+
if project_editors and not has_editor_override:
|
|
32
36
|
cmd_args.extend(["--editors", ",".join(project_editors)])
|
|
37
|
+
has_profile_override = any(
|
|
38
|
+
arg == "--profile" or arg.startswith("--profile=")
|
|
39
|
+
for arg in extra_args
|
|
40
|
+
)
|
|
41
|
+
project_profile = project.get("profile", "")
|
|
42
|
+
if project_profile and not has_profile_override:
|
|
43
|
+
cmd_args.extend(["--profile", project_profile])
|
|
33
44
|
|
|
34
45
|
try:
|
|
35
46
|
proc = subprocess.run(
|
package/scripts/validate.py
CHANGED
|
@@ -124,6 +124,10 @@ LANGUAGE_RULE_CATEGORIES = frozenset({
|
|
|
124
124
|
|
|
125
125
|
PLANNED_ASSETS = [
|
|
126
126
|
"app/.claude-plugin/plugin.json",
|
|
127
|
+
"app/claude-app/hooks/hooks.json",
|
|
128
|
+
"app/claude-app/skills/ai-toolkit-rules/SKILL.md",
|
|
129
|
+
"app/claude-app/global-instructions.md",
|
|
130
|
+
"scripts/claude_app.py",
|
|
127
131
|
"scripts/doctor.py",
|
|
128
132
|
"scripts/benchmark_ecosystem.py",
|
|
129
133
|
"scripts/harvest_ecosystem.py",
|
|
@@ -561,9 +565,37 @@ def validate_planned_assets(tk_dir: Path, vr: ValidationResult) -> None:
|
|
|
561
565
|
assert d["name"]
|
|
562
566
|
assert d["version"]
|
|
563
567
|
assert d["description"]
|
|
564
|
-
|
|
568
|
+
assert isinstance(d.get("repository"), str)
|
|
569
|
+
ignored = {"npm", "install", "marketplace", "claude-code"} & set(d)
|
|
570
|
+
assert not ignored
|
|
571
|
+
for path_field in ("hooks", "skills"):
|
|
572
|
+
relative = str(d[path_field]).removeprefix("./")
|
|
573
|
+
assert (tk_dir / "app" / relative).exists()
|
|
574
|
+
print(" OK: plugin manifest matches the official schema surface")
|
|
565
575
|
except (json.JSONDecodeError, KeyError, AssertionError):
|
|
566
|
-
vr.error("Invalid plugin manifest JSON or
|
|
576
|
+
vr.error("Invalid plugin manifest JSON, official fields, or component paths")
|
|
577
|
+
|
|
578
|
+
try:
|
|
579
|
+
from claude_app import (
|
|
580
|
+
GLOBAL_INSTRUCTIONS_PATH,
|
|
581
|
+
PLUGIN_HOOKS_PATH,
|
|
582
|
+
PLUGIN_RULES_SKILL_PATH,
|
|
583
|
+
render_global_instructions,
|
|
584
|
+
render_plugin_hooks,
|
|
585
|
+
render_rules_skill,
|
|
586
|
+
)
|
|
587
|
+
generated = (
|
|
588
|
+
(PLUGIN_HOOKS_PATH, render_plugin_hooks()),
|
|
589
|
+
(PLUGIN_RULES_SKILL_PATH, render_rules_skill()),
|
|
590
|
+
(GLOBAL_INSTRUCTIONS_PATH, render_global_instructions()),
|
|
591
|
+
)
|
|
592
|
+
stale = [path for path, expected in generated if path.read_text(encoding="utf-8") != expected]
|
|
593
|
+
if stale:
|
|
594
|
+
vr.error("Stale Claude app assets; run: python3 scripts/claude_app.py sync")
|
|
595
|
+
else:
|
|
596
|
+
print(" OK: Claude app plugin assets match canonical rules/hooks")
|
|
597
|
+
except (OSError, ValueError, json.JSONDecodeError) as exc:
|
|
598
|
+
vr.error(f"Invalid Claude app generated assets: {exc}")
|
|
567
599
|
|
|
568
600
|
# Validate benchmark dashboard JSON
|
|
569
601
|
benchmark_dashboard = tk_dir / "benchmarks" / "ecosystem-dashboard.json"
|