@softspark/ai-toolkit 4.30.2 → 4.31.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.
- package/CHANGELOG.md +57 -0
- package/README.md +27 -28
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/agents/code-reviewer.md +14 -0
- package/app/agents/frontend-specialist.md +27 -24
- package/app/agents/infrastructure-validator.md +2 -2
- package/app/claude-app/skills/ai-toolkit-rules/SKILL.md +44 -1
- package/app/personas/frontend-lead.md +20 -12
- package/app/rules/claude-toolkit-rules.md +4 -1
- package/app/rules/edit-discipline.md +37 -0
- package/app/skills/app-builder/SKILL.md +37 -6
- package/app/skills/claude-toolkit-rules/SKILL.md +37 -0
- package/app/skills/design-engineering/SKILL.md +87 -102
- package/app/skills/design-engineering/reference/animation-recipes.md +67 -0
- package/app/skills/design-engineering/reference/component-craft.md +28 -0
- package/app/skills/documentation-standards/SKILL.md +1 -1
- package/app/skills/edit-discipline/SKILL.md +50 -0
- package/app/skills/git-conventions/SKILL.md +18 -0
- package/app/skills/output-mode/SKILL.md +41 -0
- package/app/skills/quality-gates/SKILL.md +23 -0
- package/app/skills/review/SKILL.md +10 -0
- package/kb/procedures/{ecosystem-sync-sop.md → sop-ecosystem-sync.md} +2 -2
- package/kb/procedures/{post-release-testing-sop.md → sop-post-release-testing.md} +3 -3
- package/kb/procedures/sop-pre-commit.md +96 -0
- package/kb/procedures/{release-preparation-sop.md → sop-release.md} +87 -26
- package/kb/reference/architecture-overview.md +1 -1
- package/kb/reference/copilot-compatibility.md +1 -1
- package/kb/reference/licensing.md +2 -2
- package/kb/reference/quick-wins-implementation-summary.md +1 -1
- package/kb/reference/supported-tools-registry.md +2 -2
- package/kb/troubleshooting/plugin-double-load.md +1 -1
- package/llms-full.txt +974 -806
- package/llms.txt +6 -5
- package/manifest.json +1 -1
- package/package.json +2 -1
- package/scripts/doctor.py +1 -1
- package/scripts/generate_toolkit_rules_skills.py +189 -0
- package/scripts/validate.py +1 -1
- /package/kb/procedures/{maintenance-sop.md → sop-maintenance.md} +0 -0
- /package/kb/procedures/{release-verification-sop.md → sop-release-verification.md} +0 -0
package/llms.txt
CHANGED
|
@@ -32,11 +32,12 @@
|
|
|
32
32
|
- [Closed: Tool-Output Token Reduction](kb/history/completed/tool-output-token-reduction-closed-20260727.md)
|
|
33
33
|
- [How-To Guides](kb/howto/README.md)
|
|
34
34
|
- [Plan: Cloud Security Pack — Multi-Cloud Audit](kb/planning/cloud-security-pack-plan.md)
|
|
35
|
-
- [SOP: Ecosystem Sync](kb/procedures/ecosystem-sync
|
|
36
|
-
- [SOP: AI Toolkit Maintenance](kb/procedures/maintenance
|
|
37
|
-
- [SOP: Post-Release Testing](kb/procedures/post-release-testing
|
|
38
|
-
- [SOP:
|
|
39
|
-
- [SOP: Release Verification](kb/procedures/release-verification
|
|
35
|
+
- [SOP: Ecosystem Sync](kb/procedures/sop-ecosystem-sync.md)
|
|
36
|
+
- [SOP: AI Toolkit Maintenance](kb/procedures/sop-maintenance.md)
|
|
37
|
+
- [SOP: Post-Release Testing](kb/procedures/sop-post-release-testing.md)
|
|
38
|
+
- [SOP: Pre-Commit Gate](kb/procedures/sop-pre-commit.md)
|
|
39
|
+
- [SOP: Release Verification](kb/procedures/sop-release-verification.md)
|
|
40
|
+
- [SOP: Release Preparation](kb/procedures/sop-release.md)
|
|
40
41
|
- [Agents Catalog](kb/reference/agents-catalog.md)
|
|
41
42
|
- [Anti-Pattern Registry Format](kb/reference/anti-pattern-registry-format.md)
|
|
42
43
|
- [AI Toolkit Architecture](kb/reference/architecture-overview.md)
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softspark/ai-toolkit",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.31.0",
|
|
4
4
|
"description": "AI coding toolkit: 109 skills, 44 agents, 14 developer-tool integrations, recoverable native tool-output filtering, Claude Chat/Cowork export, safety constitution, SARIF audit, and signed npm provenance.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"generate:roo": "python3 scripts/generate_roo_modes.py > .roomodes",
|
|
63
63
|
"generate:aider": "python3 scripts/generate_aider_conf.py > .aider.conf.yml",
|
|
64
64
|
"generate:language-rules": "python3 scripts/generate_language_rules_skills.py",
|
|
65
|
+
"generate:toolkit-rules": "python3 scripts/generate_toolkit_rules_skills.py",
|
|
65
66
|
"generate:opencode-agents": "python3 scripts/generate_opencode_agents.py .",
|
|
66
67
|
"generate:opencode-commands": "python3 scripts/generate_opencode_commands.py .",
|
|
67
68
|
"generate:opencode-skills": "python3 scripts/generate_opencode_skills.py .",
|
package/scripts/doctor.py
CHANGED
|
@@ -124,7 +124,7 @@ SEMVER_OUTPUT = re.compile(
|
|
|
124
124
|
rf"(?:\.{_SEMVER_PRERELEASE_IDENTIFIER})*)?"
|
|
125
125
|
rf"(?:\+{_SEMVER_BUILD_IDENTIFIER}"
|
|
126
126
|
rf"(?:\.{_SEMVER_BUILD_IDENTIFIER})*)?)"
|
|
127
|
-
r"(?![0-9A-Za-z
|
|
127
|
+
r"(?![0-9A-Za-z+-]|\.(?!\s|$))"
|
|
128
128
|
)
|
|
129
129
|
VERSION_LIKE_OUTPUT = re.compile(r"(?<![0-9])[0-9]+\.[0-9]+\.[^\s,;()]+")
|
|
130
130
|
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
# Copyright 2024-2026 Lukasz Krzemien (biuro@softspark.eu)
|
|
4
|
+
# Source: https://github.com/softspark/ai-toolkit
|
|
5
|
+
|
|
6
|
+
"""Generate knowledge skills from the top-level rule files in ``app/rules/``.
|
|
7
|
+
|
|
8
|
+
Each ``app/rules/<stem>.md`` becomes ``app/skills/<stem>/SKILL.md``. The
|
|
9
|
+
per-language directories are not touched here; ``generate_language_rules_skills``
|
|
10
|
+
owns those.
|
|
11
|
+
|
|
12
|
+
Why this exists
|
|
13
|
+
---------------
|
|
14
|
+
The rules were reachable only as Claude Code user-level files under
|
|
15
|
+
``~/.claude/rules/``, with the global ``CLAUDE.md`` carrying a pointer to them
|
|
16
|
+
rather than their content. That pointer is a Claude Code idiom. DeepSeek Harness
|
|
17
|
+
reads ``AGENTS.md``, ``AGENTS.local.md``, ``CLAUDE.md`` and ``CLAUDE.local.md``
|
|
18
|
+
and has no rules-directory support at all, so under DSH every one of those rules
|
|
19
|
+
was inert: named in an injected file, never loaded, never applied.
|
|
20
|
+
|
|
21
|
+
Shipping them as skills fixes that without changing the Claude Code surface. A
|
|
22
|
+
skill is discovered by every editor that reads the shared skill catalogue, and
|
|
23
|
+
the catalogue injects each skill's *description* into the session. So the
|
|
24
|
+
description here is written as the rule itself in one imperative line — it is in
|
|
25
|
+
context whether or not the model chooses to load the body.
|
|
26
|
+
|
|
27
|
+
Idempotent: rerunning overwrites the generated ``SKILL.md`` and leaves anything
|
|
28
|
+
else in the skill directory alone.
|
|
29
|
+
|
|
30
|
+
Usage:
|
|
31
|
+
python3 scripts/generate_toolkit_rules_skills.py # write all
|
|
32
|
+
python3 scripts/generate_toolkit_rules_skills.py --check # dry-run
|
|
33
|
+
python3 scripts/generate_toolkit_rules_skills.py --rules git-conventions
|
|
34
|
+
"""
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
import argparse
|
|
38
|
+
import sys
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
from textwrap import dedent
|
|
41
|
+
|
|
42
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
43
|
+
RULES_DIR = ROOT / "app" / "rules"
|
|
44
|
+
SKILLS_DIR = ROOT / "app" / "skills"
|
|
45
|
+
|
|
46
|
+
# The description is the rule. It is injected into every session through the
|
|
47
|
+
# skill catalogue, so it has to state the obligation on its own — a reader who
|
|
48
|
+
# never opens the body should still know what is required. Keep it imperative,
|
|
49
|
+
# keep it one line, and put the reasoning in the file under app/rules/.
|
|
50
|
+
DESCRIPTIONS: dict[str, str] = {
|
|
51
|
+
"edit-discipline": (
|
|
52
|
+
"Change files with the edit and write tools, never by rewriting them "
|
|
53
|
+
"through bash (sed, awk, tee, heredoc, redirection), and show `git diff` "
|
|
54
|
+
"before reporting a file-changing task as done. "
|
|
55
|
+
"Triggers: edit, write, modify, refactor, patch, fix, diff, review changes."
|
|
56
|
+
),
|
|
57
|
+
"git-conventions": (
|
|
58
|
+
"Conventional Commits only (feat, fix, docs, refactor, test, chore); no "
|
|
59
|
+
"AI co-authorship trailer and no AI signature in a commit message. "
|
|
60
|
+
"Triggers: commit, git, message, changelog, release, pull request."
|
|
61
|
+
),
|
|
62
|
+
"output-mode": (
|
|
63
|
+
"Answer concisely: lead with the result, no preamble, tables over prose, "
|
|
64
|
+
"no trailing restatement of a diff the reader can already see. "
|
|
65
|
+
"Triggers: response style, verbosity, summary, explanation, report."
|
|
66
|
+
),
|
|
67
|
+
"quality-gates": (
|
|
68
|
+
"Plan before work over an hour, and hold the gates: ruff clean, mypy "
|
|
69
|
+
"--strict clean, pytest coverage above 70 percent, no secrets in code. "
|
|
70
|
+
"Triggers: quality, lint, mypy, pytest, coverage, gate, definition of done."
|
|
71
|
+
),
|
|
72
|
+
"claude-toolkit-rules": (
|
|
73
|
+
"SoftSpark working agreement: never guess a home directory path, give at "
|
|
74
|
+
"least three alternatives, and apply a devil's advocate critique to "
|
|
75
|
+
"decisions. Triggers: toolkit, conventions, workflow, alternatives, review."
|
|
76
|
+
),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _strip_frontmatter(text: str) -> str:
|
|
81
|
+
"""Remove YAML frontmatter (--- ... ---) if present."""
|
|
82
|
+
if not text.startswith("---\n"):
|
|
83
|
+
return text
|
|
84
|
+
end = text.find("\n---\n", 4)
|
|
85
|
+
if end == -1:
|
|
86
|
+
return text
|
|
87
|
+
return text[end + len("\n---\n"):]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _title(stem: str) -> str:
|
|
91
|
+
return stem.replace("-", " ").title()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _build_skill_md(stem: str, rule_file: Path) -> str:
|
|
95
|
+
description = DESCRIPTIONS[stem]
|
|
96
|
+
body = _strip_frontmatter(rule_file.read_text(encoding="utf-8")).strip()
|
|
97
|
+
frontmatter = dedent(
|
|
98
|
+
f"""\
|
|
99
|
+
---
|
|
100
|
+
name: {stem}
|
|
101
|
+
description: "{description}"
|
|
102
|
+
effort: low
|
|
103
|
+
user-invocable: false
|
|
104
|
+
allowed-tools: Read
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
# {_title(stem)}
|
|
108
|
+
|
|
109
|
+
This rule comes from `app/rules/{stem}.md` in ai-toolkit. It applies to
|
|
110
|
+
every task in this workspace, not only when it is loaded.
|
|
111
|
+
|
|
112
|
+
"""
|
|
113
|
+
)
|
|
114
|
+
return frontmatter + body + "\n"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def discover_rules() -> list[str]:
|
|
118
|
+
"""List top-level rule files that have a description to ship them under.
|
|
119
|
+
|
|
120
|
+
A rule with no entry in DESCRIPTIONS is skipped loudly rather than shipped
|
|
121
|
+
with a generated summary: the description is the part that reaches every
|
|
122
|
+
session, so it is written by a person.
|
|
123
|
+
"""
|
|
124
|
+
if not RULES_DIR.is_dir():
|
|
125
|
+
return []
|
|
126
|
+
found: list[str] = []
|
|
127
|
+
for path in sorted(RULES_DIR.glob("*.md")):
|
|
128
|
+
if path.stem in DESCRIPTIONS:
|
|
129
|
+
found.append(path.stem)
|
|
130
|
+
else:
|
|
131
|
+
print(
|
|
132
|
+
f" SKIP: {path.name} has no entry in DESCRIPTIONS; add one to ship it",
|
|
133
|
+
file=sys.stderr,
|
|
134
|
+
)
|
|
135
|
+
return found
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def generate(rules: list[str] | None = None, check: bool = False) -> int:
|
|
139
|
+
"""Generate skills. Returns the number written, or that would be written."""
|
|
140
|
+
selected = rules if rules else discover_rules()
|
|
141
|
+
written = 0
|
|
142
|
+
for stem in selected:
|
|
143
|
+
rule_file = RULES_DIR / f"{stem}.md"
|
|
144
|
+
if not rule_file.is_file():
|
|
145
|
+
print(f" SKIP: {stem} (app/rules/{stem}.md missing)", file=sys.stderr)
|
|
146
|
+
continue
|
|
147
|
+
if stem not in DESCRIPTIONS:
|
|
148
|
+
print(f" SKIP: {stem} (no description)", file=sys.stderr)
|
|
149
|
+
continue
|
|
150
|
+
|
|
151
|
+
skill_dir = SKILLS_DIR / stem
|
|
152
|
+
skill_md = skill_dir / "SKILL.md"
|
|
153
|
+
content = _build_skill_md(stem, rule_file)
|
|
154
|
+
|
|
155
|
+
if check:
|
|
156
|
+
existing = skill_md.read_text(encoding="utf-8") if skill_md.is_file() else ""
|
|
157
|
+
status = "OK" if existing == content else "DIFF"
|
|
158
|
+
print(f" [{status}] {skill_md.relative_to(ROOT)}")
|
|
159
|
+
if existing != content:
|
|
160
|
+
written += 1
|
|
161
|
+
continue
|
|
162
|
+
|
|
163
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
164
|
+
skill_md.write_text(content, encoding="utf-8")
|
|
165
|
+
print(f" wrote {skill_md.relative_to(ROOT)}")
|
|
166
|
+
written += 1
|
|
167
|
+
return written
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def main() -> int:
|
|
171
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
172
|
+
parser.add_argument("--check", action="store_true", help="report drift, write nothing")
|
|
173
|
+
parser.add_argument("--rules", help="comma-separated rule stems")
|
|
174
|
+
args = parser.parse_args()
|
|
175
|
+
|
|
176
|
+
rules = [r.strip() for r in args.rules.split(",")] if args.rules else None
|
|
177
|
+
written = generate(rules, check=args.check)
|
|
178
|
+
if args.check:
|
|
179
|
+
if written:
|
|
180
|
+
print(f"{written} generated skill(s) differ from app/rules/", file=sys.stderr)
|
|
181
|
+
return 1
|
|
182
|
+
print("toolkit rule skills are up to date")
|
|
183
|
+
return 0
|
|
184
|
+
print(f"{written} toolkit rule skill(s) written")
|
|
185
|
+
return 0
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
if __name__ == "__main__":
|
|
189
|
+
raise SystemExit(main())
|
package/scripts/validate.py
CHANGED
|
@@ -147,7 +147,7 @@ MAX_EMITTED_SKILL_NODES = 10_000
|
|
|
147
147
|
#
|
|
148
148
|
# Ratchet: lower WARN by 2_000 each release until it reaches 12_000. Never lower a
|
|
149
149
|
# threshold in the same change that something violates it — split the skill first,
|
|
150
|
-
# then tighten. The step lives in kb/procedures/release
|
|
150
|
+
# then tighten. The step lives in kb/procedures/sop-release.md so it
|
|
151
151
|
# does not rot as a comment nobody reads.
|
|
152
152
|
SKILL_BODY_BUDGET_ERROR = 20_000
|
|
153
153
|
SKILL_BODY_BUDGET_WARN = 18_000
|
|
File without changes
|
|
File without changes
|