@softspark/ai-toolkit 1.8.0 → 2.0.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +47 -13
  2. package/README.md +69 -17
  3. package/app/.claude-plugin/plugin.json +1 -1
  4. package/app/ARCHITECTURE.md +7 -1
  5. package/app/hooks/governance-capture.sh +1 -1
  6. package/app/hooks/pre-compact-save.sh +1 -1
  7. package/app/hooks/session-context.sh +1 -1
  8. package/app/hooks/track-usage.sh +2 -2
  9. package/app/hooks.json +20 -20
  10. package/app/plugins/memory-pack/README.md +1 -1
  11. package/app/plugins/memory-pack/hooks/observation-capture.sh +2 -2
  12. package/app/plugins/memory-pack/hooks/session-summary.sh +1 -1
  13. package/app/plugins/memory-pack/scripts/init_db.py +2 -2
  14. package/app/plugins/memory-pack/skills/mem-search/SKILL.md +3 -3
  15. package/app/skills/hook-creator/SKILL.md +2 -2
  16. package/app/skills/mem-search/SKILL.md +3 -3
  17. package/app/skills/repeat/SKILL.md +1 -1
  18. package/bin/ai-toolkit.js +32 -3
  19. package/kb/history/completed/enterprise-config-inheritance-plan-20260412.md +32 -34
  20. package/kb/history/completed/offline-slm-profile-plan-20260411.md +12 -12
  21. package/kb/planning/cloud-security-pack-plan.md +3 -3
  22. package/kb/procedures/maintenance-sop.md +3 -3
  23. package/kb/reference/architecture-overview.md +5 -5
  24. package/kb/reference/benchmark-config.md +1 -1
  25. package/kb/reference/competitive-features-implementation.md +2 -2
  26. package/kb/reference/enterprise-config-guide.md +9 -9
  27. package/kb/reference/extension-api.md +1 -1
  28. package/kb/reference/global-install-model.md +2 -2
  29. package/kb/reference/hooks-catalog.md +30 -30
  30. package/kb/reference/integrations.md +3 -3
  31. package/kb/reference/manifest-install.md +4 -4
  32. package/kb/reference/plugin-pack-conventions.md +5 -5
  33. package/kb/reference/stats.md +3 -3
  34. package/kb/reference/sync.md +3 -3
  35. package/llms-full.txt +118 -899
  36. package/llms.txt +0 -1
  37. package/manifest.json +4 -4
  38. package/package.json +1 -1
  39. package/scripts/add_rule.py +3 -2
  40. package/scripts/benchmark_config.py +3 -1
  41. package/scripts/compile_slm.py +9 -7
  42. package/scripts/config_cli.py +7 -7
  43. package/scripts/config_lock.py +12 -4
  44. package/scripts/config_merger.py +1 -1
  45. package/scripts/config_resolver.py +20 -7
  46. package/scripts/config_scaffold.py +4 -4
  47. package/scripts/config_validator.py +3 -3
  48. package/scripts/dir_rules_shared.py +1 -1
  49. package/scripts/doctor.py +3 -2
  50. package/scripts/install.py +25 -6
  51. package/scripts/install_git_hooks.py +1 -1
  52. package/scripts/install_steps/ai_tools.py +4 -4
  53. package/scripts/install_steps/hooks.py +1 -1
  54. package/scripts/install_steps/install_state.py +8 -6
  55. package/scripts/install_steps/project_registry.py +142 -0
  56. package/scripts/migrate.py +199 -0
  57. package/scripts/paths.py +50 -0
  58. package/scripts/plugin.py +8 -6
  59. package/scripts/projects_cli.py +110 -0
  60. package/scripts/remove_rule.py +5 -4
  61. package/scripts/stats.py +3 -2
  62. package/scripts/sync.py +3 -2
  63. package/scripts/update_projects.py +141 -0
  64. package/scripts/version_check.py +5 -2
  65. package/kb/planning/local-dashboard-plan.md +0 -773
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env python3
2
+ """Update all registered projects in parallel.
3
+
4
+ Reads ~/.softspark/ai-toolkit/projects.json and runs install.py --local in each
5
+ project directory concurrently using a thread pool.
6
+
7
+ Stdlib-only — no external dependencies.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import subprocess
13
+ import sys
14
+ import time
15
+ from concurrent.futures import ThreadPoolExecutor, as_completed
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
20
+ from install_steps.project_registry import get_active_projects, prune_stale
21
+
22
+
23
+ def _update_project(project: dict[str, Any], install_script: str, extra_args: list[str]) -> dict:
24
+ """Run install --local in a single project. Returns result dict."""
25
+ project_path = project["path"]
26
+ start = time.monotonic()
27
+
28
+ try:
29
+ proc = subprocess.run(
30
+ ["python3", install_script, "--local"] + extra_args,
31
+ cwd=project_path,
32
+ capture_output=True,
33
+ text=True,
34
+ timeout=120,
35
+ )
36
+ elapsed = time.monotonic() - start
37
+ return {
38
+ "path": project_path,
39
+ "profile": project.get("profile", ""),
40
+ "extends": project.get("extends", ""),
41
+ "success": proc.returncode == 0,
42
+ "elapsed": round(elapsed, 1),
43
+ "output": proc.stdout,
44
+ "error": proc.stderr if proc.returncode != 0 else "",
45
+ }
46
+ except subprocess.TimeoutExpired:
47
+ return {
48
+ "path": project_path,
49
+ "success": False,
50
+ "elapsed": 120.0,
51
+ "output": "",
52
+ "error": "Timed out after 120s",
53
+ }
54
+ except OSError as e:
55
+ return {
56
+ "path": project_path,
57
+ "success": False,
58
+ "elapsed": 0,
59
+ "output": "",
60
+ "error": str(e),
61
+ }
62
+
63
+
64
+ def main() -> None:
65
+ """Update all registered projects."""
66
+ # Parse args
67
+ verbose = "--verbose" in sys.argv or "-v" in sys.argv
68
+ json_output = "--json" in sys.argv
69
+ extra_args = [a for a in sys.argv[1:] if a not in ("--verbose", "-v", "--json")]
70
+
71
+ # Prune stale projects first
72
+ pruned = prune_stale()
73
+ if pruned and not json_output:
74
+ for p in pruned:
75
+ print(f" Pruned stale project: {p}")
76
+
77
+ # Get active projects
78
+ projects = get_active_projects()
79
+
80
+ if not projects:
81
+ if json_output:
82
+ print(json.dumps({"projects": [], "summary": "No registered projects"}))
83
+ else:
84
+ print(" No registered projects.")
85
+ print(" Run 'ai-toolkit install --local' in a project to register it.")
86
+ sys.exit(0)
87
+
88
+ install_script = str(Path(__file__).resolve().parent / "install.py")
89
+
90
+ if not json_output:
91
+ print(f" Updating {len(projects)} registered project(s)...")
92
+ print()
93
+
94
+ # Run in parallel (max 8 workers — don't overwhelm the system)
95
+ max_workers = min(len(projects), 8)
96
+ results: list[dict] = []
97
+
98
+ with ThreadPoolExecutor(max_workers=max_workers) as pool:
99
+ futures = {
100
+ pool.submit(_update_project, p, install_script, extra_args): p
101
+ for p in projects
102
+ }
103
+ for future in as_completed(futures):
104
+ result = future.result()
105
+ results.append(result)
106
+
107
+ if not json_output:
108
+ status = "✓" if result["success"] else "✗"
109
+ path_short = result["path"].replace(str(Path.home()), "~")
110
+ extends_info = f" (extends: {result.get('extends', '')})" if result.get("extends") else ""
111
+ print(f" {status} {path_short}{extends_info} ({result['elapsed']}s)")
112
+
113
+ if verbose and result["output"]:
114
+ for line in result["output"].strip().split("\n"):
115
+ print(f" {line}")
116
+
117
+ if result.get("error"):
118
+ for line in result["error"].strip().split("\n"):
119
+ print(f" ERROR: {line}")
120
+
121
+ # Summary
122
+ passed = sum(1 for r in results if r["success"])
123
+ failed = len(results) - passed
124
+
125
+ if json_output:
126
+ print(json.dumps({
127
+ "projects": results,
128
+ "total": len(results),
129
+ "passed": passed,
130
+ "failed": failed,
131
+ }, indent=2))
132
+ else:
133
+ print()
134
+ print(f" Updated: {passed}/{len(results)} projects" +
135
+ (f" ({failed} failed)" if failed else ""))
136
+
137
+ sys.exit(1 if failed else 0)
138
+
139
+
140
+ if __name__ == "__main__":
141
+ main()
@@ -21,7 +21,10 @@ import sys
21
21
  import time
22
22
  from pathlib import Path
23
23
 
24
- CACHE_FILE = Path.home() / ".ai-toolkit" / "version-check.json"
24
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
25
+ from paths import VERSION_CHECK_FILE, STATE_FILE
26
+
27
+ CACHE_FILE = VERSION_CHECK_FILE
25
28
  CACHE_TTL = 86400 # 24 hours
26
29
  PACKAGE_NAME = "@softspark/ai-toolkit"
27
30
 
@@ -34,7 +37,7 @@ def _get_installed_version() -> str:
34
37
  installed/updated, while package.json may be newer if the npm package
35
38
  was upgraded but `ai-toolkit update` was not run yet.
36
39
  """
37
- state_file = Path.home() / ".ai-toolkit" / "state.json"
40
+ state_file = STATE_FILE
38
41
  if state_file.is_file():
39
42
  try:
40
43
  with open(state_file, encoding="utf-8") as f: