@softspark/ai-toolkit 1.3.2 → 1.3.4

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,20 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v1.3.4 — Patch (2026-04-07)
11
+
12
+ ### Fixed
13
+ - **skills-catalog.md**: Added 6 missing language pattern skills (rust, java, csharp, kotlin, swift, ruby) to Development section (10→16). Total now correctly sums to 90.
14
+
15
+ ---
16
+
17
+ ## v1.3.3 — Patch (2026-04-07)
18
+
19
+ ### Fixed
20
+ - **manifest.json missing from npm package**: Added `manifest.json` to `package.json` `files` array. Without it, `--auto-detect` and `--modules` could not read module definitions from installed package.
21
+
22
+ ---
23
+
10
24
  ## v1.3.2 — Patch (2026-04-07)
11
25
 
12
26
  ### Fixed
@@ -24,8 +24,17 @@ log_event() {
24
24
  local EVENT="$1"
25
25
  local DETAIL="$2"
26
26
  mkdir -p "$LOG_DIR"
27
- printf '{"timestamp":"%s","event":"%s","tool":"%s","detail":"%s","session":"%s"}\n' \
28
- "$TIMESTAMP" "$EVENT" "$TOOL_NAME" "$DETAIL" "$SESSION" >> "$LOG_FILE"
27
+ if command -v jq >/dev/null 2>&1; then
28
+ jq -nc --arg ts "$TIMESTAMP" --arg ev "$EVENT" --arg tl "$TOOL_NAME" --arg dt "$DETAIL" --arg ss "$SESSION" \
29
+ '{"timestamp":$ts,"event":$ev,"tool":$tl,"detail":$dt,"session":$ss}' >> "$LOG_FILE"
30
+ else
31
+ # Escape double quotes in variable fields for basic JSON safety
32
+ local SAFE_DETAIL="${DETAIL//\"/\\\"}"
33
+ local SAFE_TOOL="${TOOL_NAME//\"/\\\"}"
34
+ local SAFE_SESSION="${SESSION//\"/\\\"}"
35
+ printf '{"timestamp":"%s","event":"%s","tool":"%s","detail":"%s","session":"%s"}\n' \
36
+ "$TIMESTAMP" "$EVENT" "$SAFE_TOOL" "$SAFE_DETAIL" "$SAFE_SESSION" >> "$LOG_FILE"
37
+ fi
29
38
  }
30
39
 
31
40
  case "$TOOL_NAME" in
package/bin/ai-toolkit.js CHANGED
@@ -8,6 +8,11 @@ const fs = require('fs');
8
8
  const TOOLKIT_DIR = path.dirname(__dirname);
9
9
  const CWD = process.cwd();
10
10
 
11
+ if (!process.env.HOME) {
12
+ console.error('Error: HOME environment variable is not set');
13
+ process.exit(1);
14
+ }
15
+
11
16
  // ---------------------------------------------------------------------------
12
17
  // Generator map: command name -> { script, dest, mkdir? }
13
18
  // Used by individual generator commands, `generate-all`, and the default case.
@@ -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.0"
6
+ version: "1.3.3"
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."
@@ -113,7 +113,7 @@ Hybrid skills combine slash-command invocation with domain knowledge that agents
113
113
  | `application-deploy` | 3 | Deploy → smoke test → release notes |
114
114
  | `proactive-troubleshooting` | 4 | Investigate → check perf → preventive fix → docs |
115
115
 
116
- ## Knowledge Skills - Development (10)
116
+ ## Knowledge Skills - Development (16)
117
117
 
118
118
  | Skill | Directory | Domain |
119
119
  |-------|-----------|--------|
@@ -124,6 +124,12 @@ Hybrid skills combine slash-command invocation with domain knowledge that agents
124
124
  | **ecommerce-patterns** | `skills/ecommerce-patterns/` | E-commerce: catalog, cart, checkout, payments |
125
125
  | **clean-code** | `skills/clean-code/` | Multi-language code quality: Python, TS, PHP, Go, Dart |
126
126
  | **typescript-patterns** | `skills/typescript-patterns/` | TypeScript/JavaScript patterns for frontend and backend |
127
+ | **rust-patterns** | `skills/rust-patterns/` | Ownership, borrowing, error handling, Cargo, tokio, serde |
128
+ | **java-patterns** | `skills/java-patterns/` | Records, sealed classes, Stream API, Spring Boot, JUnit 5 |
129
+ | **csharp-patterns** | `skills/csharp-patterns/` | Nullable refs, async/await, ASP.NET Core, EF Core |
130
+ | **kotlin-patterns** | `skills/kotlin-patterns/` | Coroutines, DSLs, sealed classes, Ktor, MockK |
131
+ | **swift-patterns** | `skills/swift-patterns/` | Protocol-oriented, SwiftUI, async/await, SPM |
132
+ | **ruby-patterns** | `skills/ruby-patterns/` | Blocks, Rails conventions, RSpec, ActiveRecord |
127
133
  | **design-engineering** | `skills/design-engineering/` | UI polish, animation craft, easing, transforms, accessibility |
128
134
  | **documentation-standards** | `skills/documentation-standards/` | KB document conventions, frontmatter validation, category taxonomy |
129
135
  | **brand-voice** | `skills/brand-voice/` | Anti-trope list, voice principles, LLM rhetoric prevention |
package/llms-full.txt CHANGED
@@ -3343,10 +3343,10 @@ The `common/` directory uses the same structure except `frameworks.md` is replac
3343
3343
 
3344
3344
  ## Auto-Detection
3345
3345
 
3346
- When `--auto-detect` is passed, `scripts/install_steps/detect_language.py` scans the current directory for known marker files and selects the matching language module:
3346
+ `--local` automatically enables language auto-detection. `scripts/install_steps/detect_language.py` scans the current directory for known marker files and selects matching language modules:
3347
3347
 
3348
3348
  ```bash
3349
- ai-toolkit install --local --auto-detect
3349
+ ai-toolkit install --local # auto-detects language (--auto-detect is implied)
3350
3350
  ```
3351
3351
 
3352
3352
  Detection logic (first match wins when multiple markers are present):
@@ -3368,14 +3368,14 @@ Common rules are always injected regardless of detected language.
3368
3368
  ## Installation
3369
3369
 
3370
3370
  ```bash
3371
- # Auto-detect language from project files
3372
- ai-toolkit install --local --auto-detect
3371
+ # Auto-detect language from project files (default with --local)
3372
+ ai-toolkit install --local
3373
3373
 
3374
3374
  # Explicitly select a language
3375
3375
  ai-toolkit install --local --lang typescript
3376
3376
 
3377
- # Install without language rules
3378
- ai-toolkit install --local
3377
+ # Skip auto-detect, install specific modules only
3378
+ ai-toolkit install --local --modules core,agents
3379
3379
  ```
3380
3380
 
3381
3381
  Language rules are injected into the project `CLAUDE.md` between named markers:
@@ -3491,8 +3491,8 @@ ai-toolkit install --profile standard
3491
3491
  # Module-based install (new)
3492
3492
  ai-toolkit install --modules core,agents,rules-typescript
3493
3493
 
3494
- # Auto-detect project language and install matching rules
3495
- ai-toolkit install --local --auto-detect
3494
+ # --local implies --auto-detect (language rules auto-detected)
3495
+ ai-toolkit install --local
3496
3496
 
3497
3497
  # Show currently installed modules and their state
3498
3498
  ai-toolkit status
package/manifest.json ADDED
@@ -0,0 +1,159 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "components": {
4
+ "agents": {
5
+ "description": "44 specialized agents (orchestrator, backend, frontend, security, devops, etc.)",
6
+ "path": "app/agents",
7
+ "target": ".claude/agents",
8
+ "type": "symlink",
9
+ "tags": ["core", "agents"]
10
+ },
11
+ "skills": {
12
+ "description": "90 skills (28 task + 30 hybrid + 32 knowledge)",
13
+ "path": "app/skills",
14
+ "target": ".claude/skills",
15
+ "type": "symlink",
16
+ "tags": ["core", "skills", "commands"]
17
+ },
18
+ "hooks": {
19
+ "description": "21 global hook entries across 12 lifecycle events + 5 skill-scoped lifecycle hooks",
20
+ "path": "app/hooks.json + app/hooks/*.sh",
21
+ "target": "~/.claude/settings.json (merge) + ~/.ai-toolkit/hooks/ (copy)",
22
+ "type": "merge+copy",
23
+ "tags": ["core", "hooks", "quality"]
24
+ },
25
+ "plugin-packs": {
26
+ "description": "Experimental domain plugin packs and optional hook/policy modules",
27
+ "path": "app/plugins",
28
+ "target": "opt-in / not installed by default",
29
+ "type": "experimental",
30
+ "tags": ["plugins", "packs", "experimental"]
31
+ },
32
+ "constitution": {
33
+ "description": "5-article machine-enforced safety constitution",
34
+ "path": "app/constitution.md",
35
+ "target": ".claude/constitution.md",
36
+ "type": "symlink",
37
+ "tags": ["core", "safety", "constitution"]
38
+ },
39
+ "architecture": {
40
+ "description": "System architecture documentation",
41
+ "path": "app/ARCHITECTURE.md",
42
+ "target": ".claude/ARCHITECTURE.md",
43
+ "type": "symlink",
44
+ "tags": ["docs"]
45
+ },
46
+ "output-styles": {
47
+ "description": "System prompt output style overrides (e.g. Golden Rules enforcement)",
48
+ "path": "app/output-styles",
49
+ "target": ".claude/output-styles",
50
+ "type": "symlink",
51
+ "tags": ["core", "output-styles"]
52
+ },
53
+ "rules": {
54
+ "description": "Auto-injected rules for CLAUDE.md (jira, quality-gates, etc.)",
55
+ "path": "app/rules",
56
+ "target": "CLAUDE.md (injected)",
57
+ "type": "injection",
58
+ "tags": ["core", "rules"]
59
+ }
60
+ },
61
+ "bundles": {
62
+ "minimal": {
63
+ "description": "Hooks and constitution only -- safety without agents",
64
+ "includes": ["hooks", "constitution"]
65
+ },
66
+ "standard": {
67
+ "description": "Full toolkit (recommended)",
68
+ "includes": ["agents", "skills", "hooks", "constitution", "rules"]
69
+ },
70
+ "agents-only": {
71
+ "description": "Agents without skills (for custom skill setup)",
72
+ "includes": ["agents", "hooks", "constitution"]
73
+ },
74
+ "skills-only": {
75
+ "description": "Skills without agents",
76
+ "includes": ["skills", "hooks", "constitution"]
77
+ },
78
+ "safety": {
79
+ "description": "Constitution + hooks only (zero agent overhead)",
80
+ "includes": ["hooks", "constitution"]
81
+ }
82
+ },
83
+ "modules": {
84
+ "core": {
85
+ "description": "Core hooks and essential skills",
86
+ "required": true
87
+ },
88
+ "agents": {
89
+ "description": "44 specialized agents",
90
+ "default": true
91
+ },
92
+ "skills": {
93
+ "description": "90 skills (task, hybrid, knowledge)",
94
+ "default": true
95
+ },
96
+ "rules-common": {
97
+ "description": "Common coding rules (5 files)",
98
+ "default": true
99
+ },
100
+ "rules-typescript": {
101
+ "description": "TypeScript-specific rules",
102
+ "auto_detect": ["package.json", "tsconfig.json"]
103
+ },
104
+ "rules-python": {
105
+ "description": "Python-specific rules",
106
+ "auto_detect": ["requirements.txt", "pyproject.toml", "setup.py", "Pipfile"]
107
+ },
108
+ "rules-golang": {
109
+ "description": "Go-specific rules",
110
+ "auto_detect": ["go.mod"]
111
+ },
112
+ "rules-rust": {
113
+ "description": "Rust-specific rules",
114
+ "auto_detect": ["Cargo.toml"]
115
+ },
116
+ "rules-java": {
117
+ "description": "Java-specific rules",
118
+ "auto_detect": ["pom.xml", "build.gradle", "build.gradle.kts"]
119
+ },
120
+ "rules-kotlin": {
121
+ "description": "Kotlin-specific rules",
122
+ "auto_detect": ["build.gradle.kts"]
123
+ },
124
+ "rules-swift": {
125
+ "description": "Swift-specific rules",
126
+ "auto_detect": ["Package.swift", "*.xcodeproj"]
127
+ },
128
+ "rules-dart": {
129
+ "description": "Dart/Flutter-specific rules",
130
+ "auto_detect": ["pubspec.yaml"]
131
+ },
132
+ "rules-csharp": {
133
+ "description": "C#/.NET-specific rules",
134
+ "auto_detect": ["*.csproj", "*.sln"]
135
+ },
136
+ "rules-php": {
137
+ "description": "PHP-specific rules",
138
+ "auto_detect": ["composer.json"]
139
+ },
140
+ "rules-cpp": {
141
+ "description": "C++-specific rules",
142
+ "auto_detect": ["CMakeLists.txt", "Makefile", "*.cpp"]
143
+ },
144
+ "rules-ruby": {
145
+ "description": "Ruby-specific rules",
146
+ "auto_detect": ["Gemfile", "*.gemspec"]
147
+ },
148
+ "mcp-templates": {
149
+ "description": "25 MCP server config templates",
150
+ "default": false
151
+ }
152
+ },
153
+ "profiles": {
154
+ "minimal": ["core"],
155
+ "standard": ["core", "agents", "skills", "rules-common"],
156
+ "strict": ["core", "agents", "skills", "rules-common", "mcp-templates"],
157
+ "full": ["core", "agents", "skills", "rules-common", "mcp-templates"]
158
+ }
159
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softspark/ai-toolkit",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
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",
@@ -62,6 +62,7 @@
62
62
  "kb/",
63
63
  "benchmarks/",
64
64
  "action.yml",
65
+ "manifest.json",
65
66
  "README.md",
66
67
  "CHANGELOG.md",
67
68
  "LICENSE",
@@ -14,6 +14,7 @@ Arguments:
14
14
  """
15
15
  from __future__ import annotations
16
16
 
17
+ import re
17
18
  import shutil
18
19
  import sys
19
20
  from pathlib import Path
@@ -33,6 +34,10 @@ def main() -> None:
33
34
  sys.exit(1)
34
35
 
35
36
  rule_name = sys.argv[2] if len(sys.argv) > 2 else rule_file.stem
37
+ rule_name = re.sub(r"[^a-zA-Z0-9_-]", "", rule_name)
38
+ if not rule_name:
39
+ print("Error: rule name is empty after sanitization", file=sys.stderr)
40
+ sys.exit(1)
36
41
  rules_dir = Path.home() / ".ai-toolkit" / "rules"
37
42
  rules_dir.mkdir(parents=True, exist_ok=True)
38
43
 
package/scripts/plugin.py CHANGED
@@ -588,6 +588,9 @@ def _parse_clean_args(args: list[str]) -> tuple[list[str], int]:
588
588
  if args[i] == "--days" and i + 1 < len(args):
589
589
  try:
590
590
  days = int(args[i + 1])
591
+ if days <= 0:
592
+ print(f" ERROR: --days must be positive, got {days}")
593
+ sys.exit(1)
591
594
  except ValueError:
592
595
  print(f" ERROR: --days requires a number, got '{args[i + 1]}'")
593
596
  sys.exit(1)
package/scripts/stats.py CHANGED
@@ -59,7 +59,31 @@ def main() -> None:
59
59
  print("No invocations recorded.")
60
60
  return
61
61
 
62
- rows = sorted(data.items(), key=lambda x: x[1].get("count", 0), reverse=True)
62
+ # Handle both formats: {skill: {count, last_used}} and {loop_runs: [...]}
63
+ if "loop_runs" in data:
64
+ runs = data["loop_runs"]
65
+ if not runs:
66
+ print("No invocations recorded.")
67
+ return
68
+ # Aggregate loop_runs by command
69
+ agg: dict[str, dict] = {}
70
+ for run in runs:
71
+ cmd = run.get("command", "unknown")
72
+ iters = run.get("iterations", [])
73
+ if cmd not in agg:
74
+ agg[cmd] = {"count": 0, "last_used": "unknown"}
75
+ agg[cmd]["count"] += len(iters) if iters else 1
76
+ started = run.get("started_at", "")
77
+ if started > agg[cmd]["last_used"]:
78
+ agg[cmd]["last_used"] = started
79
+ rows = sorted(agg.items(), key=lambda x: x[1]["count"], reverse=True)
80
+ else:
81
+ # Original format: {skill_name: {count, last_used}}
82
+ skill_data = {k: v for k, v in data.items() if isinstance(v, dict)}
83
+ if not skill_data:
84
+ print("No invocations recorded.")
85
+ return
86
+ rows = sorted(skill_data.items(), key=lambda x: x[1].get("count", 0), reverse=True)
63
87
 
64
88
  print(f"{'Skill':<30} {'Count':>6} {'Last Used':<20}")
65
89
  print("-" * 60)
@@ -68,10 +92,10 @@ def main() -> None:
68
92
  last = info.get("last_used", "unknown")
69
93
  print(f"{name:<30} {count:>6} {last:<20}")
70
94
 
71
- total = sum(v.get("count", 0) for v in data.values())
95
+ total = sum(v.get("count", 0) for _, v in rows)
72
96
  print()
73
97
  print(f"Total invocations: {total}")
74
- print(f"Unique skills: {len(data)}")
98
+ print(f"Unique skills: {len(rows)}")
75
99
  print()
76
100
  print(f"File: {STATS_FILE}")
77
101
  print("Reset: ai-toolkit stats --reset")
package/scripts/sync.py CHANGED
@@ -66,9 +66,23 @@ def do_import(source: str) -> None:
66
66
  """Import config from file or URL."""
67
67
  tmpfile: str | None = None
68
68
 
69
- if source.startswith("http"):
70
- tmpfile = tempfile.mktemp(suffix=".json")
71
- urllib.request.urlretrieve(source, tmpfile)
69
+ if source.startswith("https://") or source.startswith("http://"):
70
+ if not source.startswith("https://"):
71
+ print("Error: HTTP imports are not supported. Use HTTPS for security.", file=sys.stderr)
72
+ sys.exit(1)
73
+ tmp_fd, tmpfile = tempfile.mkstemp(suffix=".json")
74
+ os.close(tmp_fd)
75
+ try:
76
+ import ssl
77
+ ctx = ssl.create_default_context()
78
+ with urllib.request.urlopen(source, timeout=30, context=ctx) as resp:
79
+ data = resp.read(10 * 1024 * 1024) # 10MB max
80
+ with open(tmpfile, 'wb') as f:
81
+ f.write(data)
82
+ except Exception:
83
+ if os.path.exists(tmpfile):
84
+ os.unlink(tmpfile)
85
+ raise
72
86
  source = tmpfile
73
87
 
74
88
  source_path = Path(source)
@@ -112,7 +126,8 @@ def do_push() -> None:
112
126
  print("Error: gh not authenticated. Run: gh auth login")
113
127
  sys.exit(1)
114
128
 
115
- tmpfile = tempfile.mktemp(suffix=".json")
129
+ tmp_fd, tmpfile = tempfile.mkstemp(suffix=".json")
130
+ os.close(tmp_fd)
116
131
  Path(tmpfile).write_text(do_export(), encoding="utf-8")
117
132
 
118
133
  if GIST_ID_FILE.is_file():
@@ -153,12 +168,14 @@ def do_pull(gist_id: str = "") -> None:
153
168
  print("Usage: ai-toolkit sync --pull <gist-id>")
154
169
  sys.exit(1)
155
170
 
156
- tmpfile = tempfile.mktemp(suffix=".json")
157
- subprocess.run(
158
- ["gh", "gist", "view", gist_id, "-f", "ai-toolkit-config.json"],
159
- stdout=open(tmpfile, "w"),
160
- check=True,
161
- )
171
+ tmp_fd, tmpfile = tempfile.mkstemp(suffix=".json")
172
+ os.close(tmp_fd)
173
+ with open(tmpfile, "w") as f:
174
+ subprocess.run(
175
+ ["gh", "gist", "view", gist_id, "-f", "ai-toolkit-config.json"],
176
+ stdout=f,
177
+ check=True,
178
+ )
162
179
  do_import(tmpfile)
163
180
  os.unlink(tmpfile)
164
181
 
@@ -30,7 +30,7 @@ from plugin_schema import validate_references as _validate_plugin_references
30
30
  # ---------------------------------------------------------------------------
31
31
 
32
32
  VALID_TOOLS = frozenset({
33
- "Read", "Write", "Edit", "Bash", "Grep", "Glob", "Agent",
33
+ "Read", "Write", "Edit", "MultiEdit", "Bash", "Grep", "Glob", "Agent",
34
34
  "WebSearch", "WebFetch", "TodoRead", "TodoWrite",
35
35
  "TeamCreate", "TeamDelete", "SendMessage",
36
36
  "TaskCreate", "TaskList", "TaskUpdate", "TaskGet", "TaskOutput", "TaskStop",