@softspark/ai-toolkit 1.3.6 → 1.3.8

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 CHANGED
@@ -7,6 +7,21 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v1.3.8 — Language Rules as References (2026-04-07)
11
+
12
+ ### Changed
13
+ - **Language rules injection**: Instead of inlining full rule content (~705 lines), `install --local` now injects lightweight reference pointers (~12 lines) with absolute paths to rule files. Claude reads them on demand via Read tool, keeping CLAUDE.md compact.
14
+ - **plugin.json**: Version synced from stale 1.2.1 to 1.3.8.
15
+
16
+ ---
17
+
18
+ ## v1.3.7 — Update Notifications (2026-04-07)
19
+
20
+ ### Added
21
+ - **Update notifications**: `session-start.sh` hook checks npm for newer versions (cached 24h, non-blocking). `ai-toolkit status` shows installed vs latest version with upgrade command.
22
+
23
+ ---
24
+
10
25
  ## v1.3.6 — Patch (2026-04-07)
11
26
 
12
27
  ### Fixed
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ai-toolkit",
3
3
  "description": "Professional-grade Claude Code toolkit: 90 skills, 44 agents, persona presets, skill security auditor, expanded lifecycle hooks, experimental opt-in plugin packs, benchmark harvesting, and multi-tool support.",
4
- "version": "1.2.1",
4
+ "version": "1.3.8",
5
5
  "author": {
6
6
  "name": "SoftSpark",
7
7
  "url": "https://github.com/softspark"
@@ -8,7 +8,14 @@
8
8
  echo "MANDATORY: Before answering ANY technical question, apply ALL rules from your CLAUDE.md files (global + project). Follow the exact order of operations defined there. Do NOT skip mandatory steps even if you think you already know the answer."
9
9
  echo "REMINDER: When writing features or fixing bugs, ensure tests cover the changes. When modifying API, config, or setup, update relevant documentation. Propose these steps to the user — do not silently skip them."
10
10
 
11
- # 2. Load session context (if available)
11
+ # 2. Check for updates (cached, max once per 24h, non-blocking)
12
+ TOOLKIT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
13
+ VERSION_MSG=$(python3 "$TOOLKIT_DIR/scripts/version_check.py" 2>/dev/null)
14
+ if [ -n "$VERSION_MSG" ]; then
15
+ echo "$VERSION_MSG"
16
+ fi
17
+
18
+ # 3. Load session context (if available)
12
19
  SESSION_FILE=".claude/session-context.md"
13
20
  if [ -f "$SESSION_FILE" ]; then
14
21
  echo "=== Session Context ==="
@@ -32,17 +32,17 @@ AI Toolkit Config Benchmark
32
32
  ========================
33
33
 
34
34
  ## Your Configuration (~/.claude/)
35
- Agents: 47
35
+ Agents: 44
36
36
  Skills: 80
37
37
  Hooks: 12
38
38
 
39
39
  ## Toolkit Totals
40
- Agents: 47
40
+ Agents: 44
41
41
  Skills: 80
42
42
  Hooks: 12
43
43
 
44
44
  ## Coverage
45
- Agents: 100% (47 / 47)
45
+ Agents: 100% (44 / 44)
46
46
  Skills: 100% (80 / 80)
47
47
  Hooks: 100% (12 / 12)
48
48
 
@@ -384,7 +384,7 @@ All entries are tagged with "_source": "<source-name>" for idempotent updates.
384
384
  "required": true
385
385
  },
386
386
  "agents": {
387
- "description": "47 specialized agents",
387
+ "description": "44 specialized agents",
388
388
  "files": ["app/agents/*.md"],
389
389
  "required": false,
390
390
  "default": true
@@ -3,7 +3,7 @@ title: "Extension API Reference"
3
3
  category: reference
4
4
  service: ai-toolkit
5
5
  tags: [extension-api, inject-rule, inject-hook, mcp-templates, integration]
6
- version: "1.3.0"
6
+ version: "1.3.8"
7
7
  created: "2026-04-07"
8
8
  last_updated: "2026-04-07"
9
9
  description: "Reference for ai-toolkit's extension API: inject-rule, inject-hook, remove-rule, remove-hook, and mcp template management."
@@ -24,7 +24,7 @@ Modules are defined in `manifest.json` at the repository root. There are 17 modu
24
24
  | Module | Description | In Profile |
25
25
  |--------|-------------|-----------|
26
26
  | `core` | Core hooks and essential skills | minimal, standard, strict, full |
27
- | `agents` | 47 specialized agents | standard, strict, full |
27
+ | `agents` | 44 specialized agents | standard, strict, full |
28
28
  | `skills` | 90 skills (task, hybrid, knowledge) | standard, strict, full |
29
29
  | `rules-common` | Common coding rules (5 files) | standard, strict, full |
30
30
  | `rules-typescript` | TypeScript-specific rules (5 files) | auto-detect |
@@ -3,7 +3,7 @@ title: "AI Toolkit - Skills Catalog"
3
3
  category: reference
4
4
  service: ai-toolkit
5
5
  tags: [skills, domain-knowledge, catalog, task-skills, hybrid-skills]
6
- version: "1.3.3"
6
+ version: "1.3.8"
7
7
  created: "2026-03-23"
8
8
  last_updated: "2026-04-07"
9
9
  description: "Complete catalog of 90 skills: 28 task, 30 hybrid, 32 knowledge. Includes effort levels, skill-scoped hooks, executable scripts, security auditor, and persona presets."
package/llms-full.txt CHANGED
@@ -1199,17 +1199,17 @@ AI Toolkit Config Benchmark
1199
1199
  ========================
1200
1200
 
1201
1201
  ## Your Configuration (~/.claude/)
1202
- Agents: 47
1202
+ Agents: 44
1203
1203
  Skills: 80
1204
1204
  Hooks: 12
1205
1205
 
1206
1206
  ## Toolkit Totals
1207
- Agents: 47
1207
+ Agents: 44
1208
1208
  Skills: 80
1209
1209
  Hooks: 12
1210
1210
 
1211
1211
  ## Coverage
1212
- Agents: 100% (47 / 47)
1212
+ Agents: 100% (44 / 44)
1213
1213
  Skills: 100% (80 / 80)
1214
1214
  Hooks: 100% (12 / 12)
1215
1215
 
@@ -1907,7 +1907,7 @@ All entries are tagged with "_source": "<source-name>" for idempotent updates.
1907
1907
  "required": true
1908
1908
  },
1909
1909
  "agents": {
1910
- "description": "47 specialized agents",
1910
+ "description": "44 specialized agents",
1911
1911
  "files": ["app/agents/*.md"],
1912
1912
  "required": false,
1913
1913
  "default": true
@@ -2276,7 +2276,7 @@ title: "Extension API Reference"
2276
2276
  category: reference
2277
2277
  service: ai-toolkit
2278
2278
  tags: [extension-api, inject-rule, inject-hook, mcp-templates, integration]
2279
- version: "1.3.0"
2279
+ version: "1.3.8"
2280
2280
  created: "2026-04-07"
2281
2281
  last_updated: "2026-04-07"
2282
2282
  description: "Reference for ai-toolkit's extension API: inject-rule, inject-hook, remove-rule, remove-hook, and mcp template management."
@@ -3454,7 +3454,7 @@ Modules are defined in `manifest.json` at the repository root. There are 17 modu
3454
3454
  | Module | Description | In Profile |
3455
3455
  |--------|-------------|-----------|
3456
3456
  | `core` | Core hooks and essential skills | minimal, standard, strict, full |
3457
- | `agents` | 47 specialized agents | standard, strict, full |
3457
+ | `agents` | 44 specialized agents | standard, strict, full |
3458
3458
  | `skills` | 90 skills (task, hybrid, knowledge) | standard, strict, full |
3459
3459
  | `rules-common` | Common coding rules (5 files) | standard, strict, full |
3460
3460
  | `rules-typescript` | TypeScript-specific rules (5 files) | auto-detect |
@@ -4067,7 +4067,7 @@ title: "AI Toolkit - Skills Catalog"
4067
4067
  category: reference
4068
4068
  service: ai-toolkit
4069
4069
  tags: [skills, domain-knowledge, catalog, task-skills, hybrid-skills]
4070
- version: "1.3.0"
4070
+ version: "1.3.8"
4071
4071
  created: "2026-03-23"
4072
4072
  last_updated: "2026-04-07"
4073
4073
  description: "Complete catalog of 90 skills: 28 task, 30 hybrid, 32 knowledge. Includes effort levels, skill-scoped hooks, executable scripts, security auditor, and persona presets."
@@ -4177,7 +4177,7 @@ Hybrid skills combine slash-command invocation with domain knowledge that agents
4177
4177
  | `application-deploy` | 3 | Deploy → smoke test → release notes |
4178
4178
  | `proactive-troubleshooting` | 4 | Investigate → check perf → preventive fix → docs |
4179
4179
 
4180
- ## Knowledge Skills - Development (10)
4180
+ ## Knowledge Skills - Development (16)
4181
4181
 
4182
4182
  | Skill | Directory | Domain |
4183
4183
  |-------|-----------|--------|
@@ -4188,6 +4188,12 @@ Hybrid skills combine slash-command invocation with domain knowledge that agents
4188
4188
  | **ecommerce-patterns** | `skills/ecommerce-patterns/` | E-commerce: catalog, cart, checkout, payments |
4189
4189
  | **clean-code** | `skills/clean-code/` | Multi-language code quality: Python, TS, PHP, Go, Dart |
4190
4190
  | **typescript-patterns** | `skills/typescript-patterns/` | TypeScript/JavaScript patterns for frontend and backend |
4191
+ | **rust-patterns** | `skills/rust-patterns/` | Ownership, borrowing, error handling, Cargo, tokio, serde |
4192
+ | **java-patterns** | `skills/java-patterns/` | Records, sealed classes, Stream API, Spring Boot, JUnit 5 |
4193
+ | **csharp-patterns** | `skills/csharp-patterns/` | Nullable refs, async/await, ASP.NET Core, EF Core |
4194
+ | **kotlin-patterns** | `skills/kotlin-patterns/` | Coroutines, DSLs, sealed classes, Ktor, MockK |
4195
+ | **swift-patterns** | `skills/swift-patterns/` | Protocol-oriented, SwiftUI, async/await, SPM |
4196
+ | **ruby-patterns** | `skills/ruby-patterns/` | Blocks, Rails conventions, RSpec, ActiveRecord |
4191
4197
  | **design-engineering** | `skills/design-engineering/` | UI polish, animation craft, easing, transforms, accessibility |
4192
4198
  | **documentation-standards** | `skills/documentation-standards/` | KB document conventions, frontmatter validation, category taxonomy |
4193
4199
  | **brand-voice** | `skills/brand-voice/` | Anti-trope list, voice principles, LLM rhetoric prevention |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softspark/ai-toolkit",
3
- "version": "1.3.6",
3
+ "version": "1.3.8",
4
4
  "description": "Professional-grade AI coding toolkit: 90 skills, 44 agents, multi-platform support (Claude, Cursor, Windsurf, Copilot, Gemini, Cline, Roo Code, Aider, Augment), machine-enforced safety constitution, persona presets, skill security auditor, expanded lifecycle hooks, 11 plugin packs, and benchmark tooling.",
5
5
  "keywords": [
6
6
  "claude",
@@ -166,11 +166,11 @@ def install_local_project(rules_dir: Path, dry_run: bool, reset: bool,
166
166
 
167
167
 
168
168
  def _inject_language_rules(cwd: Path, language_modules: list[str] | None) -> None:
169
- """Inject language-specific rules into project's CLAUDE.md.
169
+ """Inject language-specific rule summary into project's .claude/CLAUDE.md.
170
170
 
171
- Concatenates all .md files from matching app/rules/<lang>/ directories
172
- into a single marker-injected section in the project's .claude/CLAUDE.md.
173
- Also always includes common rules.
171
+ Instead of injecting full rule content (hundreds of lines), injects a
172
+ compact summary with the key rules per category. Full rules are available
173
+ as knowledge skills that Claude auto-loads contextually.
174
174
  """
175
175
  if not language_modules:
176
176
  return
@@ -179,35 +179,40 @@ def _inject_language_rules(cwd: Path, language_modules: list[str] | None) -> Non
179
179
  if not rules_src.is_dir():
180
180
  return
181
181
 
182
- # Always include common + detected languages
183
- lang_dirs: list[str] = ["common"]
182
+ # Detect language names
183
+ langs: list[str] = []
184
184
  for mod in language_modules:
185
- # module name format: "rules-typescript" → dir "typescript"
186
185
  if mod.startswith("rules-"):
187
- lang_dirs.append(mod[6:])
186
+ langs.append(mod[6:])
188
187
 
189
- # Collect all rule content
190
- parts: list[str] = []
191
- for lang_dir in lang_dirs:
192
- lang_path = rules_src / lang_dir
193
- if not lang_path.is_dir():
194
- continue
195
- for rule_file in sorted(lang_path.glob("*.md")):
196
- content = rule_file.read_text(encoding="utf-8").strip()
197
- # Strip YAML frontmatter
198
- if content.startswith("---"):
199
- end = content.find("---", 3)
200
- if end != -1:
201
- content = content[end + 3:].strip()
202
- if content:
203
- parts.append(content)
204
-
205
- if not parts:
188
+ if not langs:
206
189
  return
207
190
 
208
- # Write concatenated rules to a temp file, then inject as a section
191
+ # Build a lightweight reference pointer NOT the full rules content.
192
+ # Full rules are available as knowledge skills (auto-loaded by Claude)
193
+ # and as files Claude can Read on demand.
194
+ toolkit_pkg = app_dir.parent
195
+ lines: list[str] = ["# Language Rules", ""]
196
+ lines.append(f"This project uses: **{', '.join(langs)}**")
197
+ lines.append("")
198
+ lines.append("When writing or reviewing code, use the Glob and Read tools to read the rules:")
199
+ # Resolve actual installed path for the rules
200
+ rules_resolved = str(rules_src.resolve())
201
+ all_dirs: list[str] = ["common"]
202
+ for l in langs:
203
+ if l not in all_dirs:
204
+ all_dirs.append(l)
205
+ for lang in all_dirs:
206
+ lang_path = rules_src / lang
207
+ if lang_path.is_dir():
208
+ categories = ", ".join(f.stem for f in sorted(lang_path.glob("*.md")))
209
+ lines.append(f"- `{lang_path.resolve()}/` ({categories})")
210
+ lines.append("")
211
+ lines.append("Read the relevant rule files before making code changes. Do NOT guess — read first.")
212
+
213
+ # Write summary to temp file, then inject as section
209
214
  import tempfile
210
- combined = "\n\n".join(parts)
215
+ combined = "\n".join(lines)
211
216
  with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False,
212
217
  encoding="utf-8") as tmp:
213
218
  tmp.write(combined)
@@ -215,8 +220,8 @@ def _inject_language_rules(cwd: Path, language_modules: list[str] | None) -> Non
215
220
 
216
221
  try:
217
222
  inject_section(tmp_path, cwd / ".claude" / "CLAUDE.md", "language-rules")
218
- detected = ", ".join(d for d in lang_dirs if d != "common")
219
- print(f" Injected: language rules (common + {detected})")
223
+ lang_names = [l for l in langs if l != "common"]
224
+ print(f" Injected: language rules summary (common + {', '.join(lang_names)})")
220
225
  finally:
221
226
  tmp_path.unlink(missing_ok=True)
222
227
 
@@ -115,3 +115,18 @@ def print_status() -> None:
115
115
  # Strip "rules-" prefix for readability
116
116
  langs = [m.replace("rules-", "") for m in detected]
117
117
  print(f" Detected: {', '.join(langs)}")
118
+
119
+ # Check for updates
120
+ try:
121
+ import sys as _sys
122
+ _sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
123
+ from version_check import check
124
+ result = check()
125
+ if result["update_available"]:
126
+ print()
127
+ print(f" Update available: {result['installed']} -> {result['latest']}")
128
+ print(f" Run: npm install -g @softspark/ai-toolkit@latest && ai-toolkit update")
129
+ else:
130
+ print(f" Latest: {result['latest']} (up to date)")
131
+ except Exception:
132
+ pass # version check is optional
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env python3
2
+ """Check if a newer version of ai-toolkit is available on npm.
3
+
4
+ Caches the result for 24 hours to avoid hitting npm on every session.
5
+
6
+ Usage:
7
+ version_check.py # Print update notice if available (for hooks)
8
+ version_check.py --status # Print detailed version info (for ai-toolkit status)
9
+ version_check.py --force # Force re-check (ignore cache)
10
+
11
+ Exit codes:
12
+ 0 Up to date (or check skipped/failed)
13
+ 1 Update available
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import os
19
+ import subprocess
20
+ import sys
21
+ import time
22
+ from pathlib import Path
23
+
24
+ CACHE_FILE = Path.home() / ".ai-toolkit" / "version-check.json"
25
+ CACHE_TTL = 86400 # 24 hours
26
+ PACKAGE_NAME = "@softspark/ai-toolkit"
27
+
28
+
29
+ def _get_installed_version() -> str:
30
+ """Read version from the toolkit's own package.json."""
31
+ pkg = Path(__file__).resolve().parent.parent / "package.json"
32
+ if pkg.is_file():
33
+ with open(pkg, encoding="utf-8") as f:
34
+ return json.load(f).get("version", "unknown")
35
+ return "unknown"
36
+
37
+
38
+ def _get_cached() -> dict | None:
39
+ """Read cached version check result."""
40
+ if not CACHE_FILE.is_file():
41
+ return None
42
+ try:
43
+ with open(CACHE_FILE, encoding="utf-8") as f:
44
+ data = json.load(f)
45
+ checked_at = data.get("checked_at", 0)
46
+ if time.time() - checked_at > CACHE_TTL:
47
+ return None # expired
48
+ return data
49
+ except (json.JSONDecodeError, OSError):
50
+ return None
51
+
52
+
53
+ def _save_cache(installed: str, latest: str) -> None:
54
+ """Save version check result to cache."""
55
+ CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
56
+ data = {
57
+ "installed": installed,
58
+ "latest": latest,
59
+ "checked_at": time.time(),
60
+ }
61
+ tmp = str(CACHE_FILE) + ".tmp"
62
+ with open(tmp, "w", encoding="utf-8") as f:
63
+ json.dump(data, f)
64
+ os.replace(tmp, str(CACHE_FILE))
65
+
66
+
67
+ def _fetch_latest_version() -> str | None:
68
+ """Fetch latest version from npm registry (2s timeout)."""
69
+ try:
70
+ result = subprocess.run(
71
+ ["npm", "view", PACKAGE_NAME, "version"],
72
+ capture_output=True, text=True, timeout=5,
73
+ )
74
+ if result.returncode == 0:
75
+ return result.stdout.strip()
76
+ except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
77
+ pass
78
+ return None
79
+
80
+
81
+ def _parse_semver(version: str) -> tuple[int, ...]:
82
+ """Parse version string to comparable tuple."""
83
+ try:
84
+ return tuple(int(x) for x in version.split("."))
85
+ except (ValueError, AttributeError):
86
+ return (0, 0, 0)
87
+
88
+
89
+ def _is_newer(latest: str, installed: str) -> bool:
90
+ """Check if latest is newer than installed."""
91
+ return _parse_semver(latest) > _parse_semver(installed)
92
+
93
+
94
+ def check(force: bool = False) -> dict:
95
+ """Check for updates. Returns {installed, latest, update_available}."""
96
+ installed = _get_installed_version()
97
+
98
+ if not force:
99
+ cached = _get_cached()
100
+ if cached:
101
+ return {
102
+ "installed": installed,
103
+ "latest": cached.get("latest", installed),
104
+ "update_available": _is_newer(cached.get("latest", ""), installed),
105
+ "cached": True,
106
+ }
107
+
108
+ latest = _fetch_latest_version()
109
+ if latest:
110
+ _save_cache(installed, latest)
111
+ return {
112
+ "installed": installed,
113
+ "latest": latest,
114
+ "update_available": _is_newer(latest, installed),
115
+ "cached": False,
116
+ }
117
+
118
+ return {
119
+ "installed": installed,
120
+ "latest": "unknown",
121
+ "update_available": False,
122
+ "cached": False,
123
+ }
124
+
125
+
126
+ def main() -> None:
127
+ force = "--force" in sys.argv
128
+ status_mode = "--status" in sys.argv
129
+
130
+ result = check(force=force)
131
+
132
+ if status_mode:
133
+ print(f" Installed: {result['installed']}")
134
+ print(f" Latest: {result['latest']}")
135
+ if result["update_available"]:
136
+ print(f" Update: {result['installed']} -> {result['latest']}")
137
+ print(f" Run: npm install -g {PACKAGE_NAME}@latest && ai-toolkit update")
138
+ else:
139
+ print(" Update: up to date")
140
+ sys.exit(1 if result["update_available"] else 0)
141
+
142
+ # Hook mode: only print if update available
143
+ if result["update_available"]:
144
+ print(
145
+ f"ai-toolkit update available: {result['installed']} -> {result['latest']} "
146
+ f"(npm install -g {PACKAGE_NAME}@latest)"
147
+ )
148
+ sys.exit(1)
149
+
150
+ sys.exit(0)
151
+
152
+
153
+ if __name__ == "__main__":
154
+ main()