@softspark/ai-toolkit 4.29.2 → 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.
Files changed (42) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +44 -18
  3. package/app/.claude-plugin/plugin.json +1 -1
  4. package/app/ARCHITECTURE.md +2 -2
  5. package/app/mcp-templates/README.md +7 -2
  6. package/app/mcp-templates/rag-mcp-legal.json +11 -0
  7. package/app/mcp-templates/rag-mcp.json +11 -0
  8. package/app/surface.json +1 -0
  9. package/benchmarks/ecosystem-doctor-snapshot.json +29 -17
  10. package/bin/ai-toolkit.js +8 -0
  11. package/kb/history/completed/dsh-integration-plan-superseded.md +322 -0
  12. package/kb/history/completed/dsh-native-install-target-plan.md +331 -0
  13. package/kb/procedures/ecosystem-sync-sop.md +7 -5
  14. package/kb/procedures/maintenance-sop.md +1 -1
  15. package/kb/procedures/release-verification-sop.md +35 -5
  16. package/kb/reference/architecture-overview.md +24 -5
  17. package/kb/reference/cli-reference.md +1 -1
  18. package/kb/reference/dsh-compatibility.md +183 -0
  19. package/kb/reference/manifest-install.md +112 -5
  20. package/kb/reference/mcp-templates.md +11 -4
  21. package/kb/reference/plugin-pack-conventions.md +35 -18
  22. package/kb/reference/supported-tools-registry.md +30 -6
  23. package/llms-full.txt +1110 -50
  24. package/llms.txt +3 -0
  25. package/manifest.json +2 -2
  26. package/package.json +2 -2
  27. package/scripts/codex_skill_adapter.py +673 -34
  28. package/scripts/config_resolver.py +80 -14
  29. package/scripts/doctor.py +98 -20
  30. package/scripts/ecosystem_tools.json +51 -1
  31. package/scripts/generate_codex_skills.py +22 -20
  32. package/scripts/install.py +30 -13
  33. package/scripts/install_steps/ai_tools.py +97 -33
  34. package/scripts/install_steps/dsh.py +5063 -0
  35. package/scripts/install_steps/install_state.py +1645 -57
  36. package/scripts/mcp_editors.py +5 -2
  37. package/scripts/plugin.py +2495 -163
  38. package/scripts/plugin_mcp.py +279 -0
  39. package/scripts/plugin_rules.py +389 -0
  40. package/scripts/plugin_schema.py +139 -23
  41. package/scripts/uninstall.py +47 -4
  42. 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
- prepare_codex_skills_dir,
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
@@ -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
- # All known editor identifiers for --editors flag
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
- "copilot", "cursor", "windsurf", "cline", "roo",
757
- "aider", "augment", "antigravity", "codex", "gemini", "opencode",
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
 
@@ -1234,7 +1270,8 @@ def _install_local_dry_run(reset: bool, editors: list[str] | None = None,
1234
1270
  print(" Would generate: .agents/skills/ Codex skills")
1235
1271
  if codex_skills:
1236
1272
  print(" Would refresh: .agents/skills/ via --codex-skills")
1237
-
1273
+ if "dsh" in eds:
1274
+ print(" Would generate: .agents/skills/ DSH-compatible managed skills")
1238
1275
  if not eds:
1239
1276
  print(" No editors selected (use --editors <list> or --editors all)")
1240
1277
 
@@ -1330,42 +1367,53 @@ def _create_local_settings(cwd: Path, reset: bool) -> None:
1330
1367
  print(" Kept: .claude/settings.local.json (already exists)")
1331
1368
 
1332
1369
 
1333
- def _install_codex_skills(cwd: Path) -> None:
1334
- """Install all skills to `.agents/skills/` for Codex.
1370
+ def _install_agent_skills(cwd: Path, *, target: str) -> None:
1371
+ """Install all skills to the managed ``.agents/skills`` surface.
1335
1372
 
1336
- Native Codex-compatible skills are symlinked directly. Skills that rely on
1337
- Claude-only orchestration primitives are rendered into generated wrappers
1338
- 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.
1339
1375
  """
1376
+ if target not in {"codex", "dsh", "shared"}:
1377
+ raise ValueError(f"Unsupported agent skill target: {target}")
1340
1378
  skills_src = app_dir / "skills"
1341
1379
  if not skills_src.is_dir():
1342
1380
  return
1343
1381
 
1344
- skills_dst = prepare_codex_skills_dir(cwd)
1345
- user_names = unmanaged_codex_skill_names(skills_dst, skills_src)
1346
-
1347
1382
  linked = 0
1348
1383
  adapted = 0
1349
1384
  skipped = 0
1350
- for skill_dir in sorted(skills_src.iterdir()):
1351
- if not skill_dir.is_dir() or skill_dir.name.startswith("_"):
1352
- continue
1353
- skill_md = skill_dir / "SKILL.md"
1354
- if not skill_md.is_file():
1355
- continue
1356
- if skill_dir.name in user_names:
1357
- skipped += 1
1358
- continue
1359
-
1360
- mode = sync_codex_skill(skill_dir, skills_dst)
1361
- if mode == "linked":
1362
- linked += 1
1363
- elif mode == "adapted":
1364
- adapted += 1
1365
- else:
1366
- skipped += 1
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
1367
1413
 
1368
- cleanup_codex_skills(skills_dst, skills_src, user_names)
1414
+ cleanup_codex_skills(skills_dst, skills_src, user_names)
1415
+ owners = {"codex", "dsh"} if target == "shared" else {target}
1416
+ transaction.commit(owners)
1369
1417
 
1370
1418
  print(
1371
1419
  f" Installed: {linked + adapted} skills to .agents/skills/"
@@ -1373,6 +1421,16 @@ def _install_codex_skills(cwd: Path) -> None:
1373
1421
  )
1374
1422
 
1375
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
+
1376
1434
  def _install_codex_agents(cwd: Path, *, config_root: Path | None = None) -> None:
1377
1435
  """Generate native Codex custom-agent TOML files."""
1378
1436
  from generate_codex_agents import generate as gen_codex_agents
@@ -1581,13 +1639,19 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
1581
1639
  print(" Created: .codex/hooks.json")
1582
1640
  # .codex/agents/ -- native Codex custom-agent definitions
1583
1641
  _install_codex_agents(cwd)
1584
- # .agents/skills/ — Codex discovery path for repo-local skills
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:
1585
1647
  _install_codex_skills(cwd)
1586
1648
  # --codex-skills explicitly re-runs the same Codex skill sync path.
1587
- # Codex upstream discovers skills from .agents/skills/, not .codex/skills/.
1588
1649
  if codex_skills:
1589
1650
  _try_generator("generate_codex_skills", cwd,
1590
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)
1591
1655
 
1592
1656
  if "gemini" in eds:
1593
1657
  # GEMINI.md — marker injection from the shared generator output