@softspark/ai-toolkit 4.29.1 → 4.30.2
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 +115 -0
- package/README.md +44 -18
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/ARCHITECTURE.md +2 -2
- package/app/mcp-templates/README.md +7 -2
- package/app/mcp-templates/rag-mcp-legal.json +11 -0
- package/app/mcp-templates/rag-mcp.json +11 -0
- package/app/surface.json +1 -0
- package/benchmarks/ecosystem-doctor-snapshot.json +29 -17
- package/bin/ai-toolkit.js +8 -0
- package/kb/history/completed/dsh-integration-plan-superseded.md +322 -0
- package/kb/history/completed/dsh-native-install-target-plan.md +331 -0
- package/kb/procedures/ecosystem-sync-sop.md +7 -5
- package/kb/procedures/maintenance-sop.md +1 -1
- package/kb/procedures/release-verification-sop.md +35 -5
- package/kb/reference/architecture-overview.md +24 -5
- package/kb/reference/cli-reference.md +1 -1
- package/kb/reference/dsh-compatibility.md +183 -0
- package/kb/reference/manifest-install.md +112 -5
- package/kb/reference/mcp-templates.md +11 -4
- package/kb/reference/plugin-pack-conventions.md +35 -18
- package/kb/reference/supported-tools-registry.md +30 -6
- package/llms-full.txt +1110 -50
- package/llms.txt +3 -0
- package/manifest.json +2 -2
- package/package.json +2 -2
- package/scripts/codex_skill_adapter.py +673 -34
- package/scripts/config_resolver.py +80 -14
- package/scripts/doctor.py +98 -20
- package/scripts/ecosystem_tools.json +51 -1
- package/scripts/generate_codex_skills.py +22 -20
- package/scripts/install.py +30 -13
- package/scripts/install_steps/ai_tools.py +104 -34
- package/scripts/install_steps/dsh.py +5063 -0
- package/scripts/install_steps/install_state.py +1645 -57
- package/scripts/mcp_editors.py +5 -2
- package/scripts/plugin.py +2495 -163
- package/scripts/plugin_mcp.py +279 -0
- package/scripts/plugin_rules.py +389 -0
- package/scripts/plugin_schema.py +139 -23
- package/scripts/uninstall.py +47 -4
- package/scripts/validate.py +421 -0
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"""Install global and project-local AI tool configs."""
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
+
from dataclasses import dataclass
|
|
8
9
|
import os
|
|
9
10
|
import shutil
|
|
10
11
|
import subprocess
|
|
@@ -13,8 +14,10 @@ from pathlib import Path
|
|
|
13
14
|
from _common import app_dir, inject_section, toolkit_dir
|
|
14
15
|
from codex_skill_adapter import (
|
|
15
16
|
cleanup_codex_skills,
|
|
16
|
-
|
|
17
|
+
managed_skill_surface_transaction,
|
|
18
|
+
skill_surface_owners,
|
|
17
19
|
sync_codex_skill,
|
|
20
|
+
sync_dsh_skill,
|
|
18
21
|
unmanaged_codex_skill_names,
|
|
19
22
|
)
|
|
20
23
|
from mcp_editors import sync_project_mcp_to_editors
|
|
@@ -173,7 +176,7 @@ def install_ai_tools(target_dir: Path, rules_dir: Path,
|
|
|
173
176
|
if "codex" in eds:
|
|
174
177
|
if dry_run:
|
|
175
178
|
print(" Would inject: $CODEX_HOME/AGENTS.md, $CODEX_HOME/agents/, "
|
|
176
|
-
"$CODEX_HOME/hooks.json")
|
|
179
|
+
"$CODEX_HOME/hooks.json + $CODEX_HOME/ai-toolkit-hooks/")
|
|
177
180
|
print(" Would generate: ~/.agents/skills/ (shared Codex skill discovery)")
|
|
178
181
|
else:
|
|
179
182
|
_install_codex_global(target_dir, rules_dir)
|
|
@@ -751,11 +754,40 @@ def run_script(script_name: str, *args: str, capture: bool = False) -> str:
|
|
|
751
754
|
return result.stdout if capture else ""
|
|
752
755
|
|
|
753
756
|
|
|
754
|
-
|
|
757
|
+
@dataclass(frozen=True, slots=True)
|
|
758
|
+
class EditorCapability:
|
|
759
|
+
"""Selection policy for one ``--editors`` target."""
|
|
760
|
+
|
|
761
|
+
include_in_all: bool = True
|
|
762
|
+
requires_local: bool = False
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
EDITOR_CAPABILITIES = {
|
|
766
|
+
"copilot": EditorCapability(),
|
|
767
|
+
"cursor": EditorCapability(),
|
|
768
|
+
"windsurf": EditorCapability(),
|
|
769
|
+
"cline": EditorCapability(),
|
|
770
|
+
"roo": EditorCapability(),
|
|
771
|
+
"aider": EditorCapability(),
|
|
772
|
+
"augment": EditorCapability(),
|
|
773
|
+
"antigravity": EditorCapability(),
|
|
774
|
+
"codex": EditorCapability(),
|
|
775
|
+
"gemini": EditorCapability(),
|
|
776
|
+
"opencode": EditorCapability(),
|
|
777
|
+
"dsh": EditorCapability(include_in_all=False, requires_local=True),
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
SELECTABLE_EDITORS = list(EDITOR_CAPABILITIES)
|
|
755
781
|
ALL_EDITORS = [
|
|
756
|
-
|
|
757
|
-
|
|
782
|
+
editor
|
|
783
|
+
for editor, capability in EDITOR_CAPABILITIES.items()
|
|
784
|
+
if capability.include_in_all
|
|
758
785
|
]
|
|
786
|
+
LOCAL_ONLY_EDITORS = {
|
|
787
|
+
editor
|
|
788
|
+
for editor, capability in EDITOR_CAPABILITIES.items()
|
|
789
|
+
if capability.requires_local
|
|
790
|
+
}
|
|
759
791
|
|
|
760
792
|
# Map of project files/dirs → editor names for auto-detection
|
|
761
793
|
_EDITOR_MARKERS: dict[str, str] = {
|
|
@@ -812,6 +844,10 @@ def _detect_editors(cwd: Path) -> list[str]:
|
|
|
812
844
|
# The canonical workspace Antigravity pointer also lives in
|
|
813
845
|
# .agents/skills/; only materialized skills indicate Codex.
|
|
814
846
|
continue
|
|
847
|
+
if marker == ".agents/skills":
|
|
848
|
+
owners = skill_surface_owners(p)
|
|
849
|
+
if owners is not None and "codex" not in owners:
|
|
850
|
+
continue
|
|
815
851
|
found.add(editor)
|
|
816
852
|
return sorted(found)
|
|
817
853
|
|
|
@@ -1224,11 +1260,18 @@ def _install_local_dry_run(reset: bool, editors: list[str] | None = None,
|
|
|
1224
1260
|
if "opencode" in eds:
|
|
1225
1261
|
print(" Would generate: .opencode/skills/ (profile=full)")
|
|
1226
1262
|
if "codex" in eds:
|
|
1263
|
+
# Hooks are not profile-gated: the live path runs gen_codex_hooks
|
|
1264
|
+
# unconditionally whenever codex is selected, so the preview must say so
|
|
1265
|
+
# at every profile. Announcing the asset directory as well as the
|
|
1266
|
+
# manifest matters here — the hook scripts are what a user reviews and
|
|
1267
|
+
# trusts with /hooks before Codex will run them.
|
|
1268
|
+
print(" Would generate: .codex/hooks.json + .codex/hooks/ (hook scripts)")
|
|
1227
1269
|
print(" Would generate: .codex/agents/ native agents")
|
|
1228
1270
|
print(" Would generate: .agents/skills/ Codex skills")
|
|
1229
1271
|
if codex_skills:
|
|
1230
1272
|
print(" Would refresh: .agents/skills/ via --codex-skills")
|
|
1231
|
-
|
|
1273
|
+
if "dsh" in eds:
|
|
1274
|
+
print(" Would generate: .agents/skills/ DSH-compatible managed skills")
|
|
1232
1275
|
if not eds:
|
|
1233
1276
|
print(" No editors selected (use --editors <list> or --editors all)")
|
|
1234
1277
|
|
|
@@ -1324,42 +1367,53 @@ def _create_local_settings(cwd: Path, reset: bool) -> None:
|
|
|
1324
1367
|
print(" Kept: .claude/settings.local.json (already exists)")
|
|
1325
1368
|
|
|
1326
1369
|
|
|
1327
|
-
def
|
|
1328
|
-
"""Install all skills to
|
|
1370
|
+
def _install_agent_skills(cwd: Path, *, target: str) -> None:
|
|
1371
|
+
"""Install all skills to the managed ``.agents/skills`` surface.
|
|
1329
1372
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
with Codex-native delegation guidance.
|
|
1373
|
+
Codex-only installs retain Codex-specific wrappers. DSH-only and shared
|
|
1374
|
+
Codex/DSH installs use portable wrappers that retain DSH invocation fields.
|
|
1333
1375
|
"""
|
|
1376
|
+
if target not in {"codex", "dsh", "shared"}:
|
|
1377
|
+
raise ValueError(f"Unsupported agent skill target: {target}")
|
|
1334
1378
|
skills_src = app_dir / "skills"
|
|
1335
1379
|
if not skills_src.is_dir():
|
|
1336
1380
|
return
|
|
1337
1381
|
|
|
1338
|
-
skills_dst = prepare_codex_skills_dir(cwd)
|
|
1339
|
-
user_names = unmanaged_codex_skill_names(skills_dst, skills_src)
|
|
1340
|
-
|
|
1341
1382
|
linked = 0
|
|
1342
1383
|
adapted = 0
|
|
1343
1384
|
skipped = 0
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1385
|
+
with managed_skill_surface_transaction(cwd, skills_src) as transaction:
|
|
1386
|
+
skills_dst = transaction.skills_dst
|
|
1387
|
+
user_names = unmanaged_codex_skill_names(skills_dst, skills_src)
|
|
1388
|
+
|
|
1389
|
+
for skill_dir in sorted(skills_src.iterdir()):
|
|
1390
|
+
if not skill_dir.is_dir() or skill_dir.name.startswith("_"):
|
|
1391
|
+
continue
|
|
1392
|
+
skill_md = skill_dir / "SKILL.md"
|
|
1393
|
+
if not skill_md.is_file():
|
|
1394
|
+
continue
|
|
1395
|
+
if skill_dir.name in user_names:
|
|
1396
|
+
skipped += 1
|
|
1397
|
+
continue
|
|
1398
|
+
|
|
1399
|
+
if target == "codex":
|
|
1400
|
+
mode = sync_codex_skill(skill_dir, skills_dst)
|
|
1401
|
+
else:
|
|
1402
|
+
mode = sync_dsh_skill(
|
|
1403
|
+
skill_dir,
|
|
1404
|
+
skills_dst,
|
|
1405
|
+
shared=target == "shared",
|
|
1406
|
+
)
|
|
1407
|
+
if mode == "linked":
|
|
1408
|
+
linked += 1
|
|
1409
|
+
elif mode == "adapted":
|
|
1410
|
+
adapted += 1
|
|
1411
|
+
else:
|
|
1412
|
+
skipped += 1
|
|
1361
1413
|
|
|
1362
|
-
|
|
1414
|
+
cleanup_codex_skills(skills_dst, skills_src, user_names)
|
|
1415
|
+
owners = {"codex", "dsh"} if target == "shared" else {target}
|
|
1416
|
+
transaction.commit(owners)
|
|
1363
1417
|
|
|
1364
1418
|
print(
|
|
1365
1419
|
f" Installed: {linked + adapted} skills to .agents/skills/"
|
|
@@ -1367,6 +1421,16 @@ def _install_codex_skills(cwd: Path) -> None:
|
|
|
1367
1421
|
)
|
|
1368
1422
|
|
|
1369
1423
|
|
|
1424
|
+
def _install_codex_skills(cwd: Path) -> None:
|
|
1425
|
+
"""Install the Codex-specific managed skill surface."""
|
|
1426
|
+
_install_agent_skills(cwd, target="codex")
|
|
1427
|
+
|
|
1428
|
+
|
|
1429
|
+
def _install_dsh_skills(cwd: Path, *, shared: bool = False) -> None:
|
|
1430
|
+
"""Install DSH-portable skills, optionally shared with Codex."""
|
|
1431
|
+
_install_agent_skills(cwd, target="shared" if shared else "dsh")
|
|
1432
|
+
|
|
1433
|
+
|
|
1370
1434
|
def _install_codex_agents(cwd: Path, *, config_root: Path | None = None) -> None:
|
|
1371
1435
|
"""Generate native Codex custom-agent TOML files."""
|
|
1372
1436
|
from generate_codex_agents import generate as gen_codex_agents
|
|
@@ -1575,13 +1639,19 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
1575
1639
|
print(" Created: .codex/hooks.json")
|
|
1576
1640
|
# .codex/agents/ -- native Codex custom-agent definitions
|
|
1577
1641
|
_install_codex_agents(cwd)
|
|
1578
|
-
|
|
1642
|
+
if {"codex", "dsh"}.issubset(eds):
|
|
1643
|
+
# The clients share one discovery directory. A joint install therefore
|
|
1644
|
+
# converges on one portable rendering instead of depending on list order.
|
|
1645
|
+
_install_dsh_skills(cwd, shared=True)
|
|
1646
|
+
elif "codex" in eds:
|
|
1579
1647
|
_install_codex_skills(cwd)
|
|
1580
1648
|
# --codex-skills explicitly re-runs the same Codex skill sync path.
|
|
1581
|
-
# Codex upstream discovers skills from .agents/skills/, not .codex/skills/.
|
|
1582
1649
|
if codex_skills:
|
|
1583
1650
|
_try_generator("generate_codex_skills", cwd,
|
|
1584
1651
|
enable_codex_skills=True)
|
|
1652
|
+
elif "dsh" in eds:
|
|
1653
|
+
# DSH receives only the shared skill surface, without Codex config.
|
|
1654
|
+
_install_dsh_skills(cwd)
|
|
1585
1655
|
|
|
1586
1656
|
if "gemini" in eds:
|
|
1587
1657
|
# GEMINI.md — marker injection from the shared generator output
|