@softspark/ai-toolkit 4.31.0 → 4.32.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 (50) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/README.md +26 -19
  3. package/app/.claude-plugin/plugin.json +1 -1
  4. package/app/claude-app/hooks/hooks.json +4 -2
  5. package/app/claude-app/skills/ai-toolkit-rules/SKILL.md +30 -16
  6. package/app/hooks/quality-gate.sh +9 -2
  7. package/app/hooks.json +4 -2
  8. package/app/rules/common/git-team.md +33 -0
  9. package/app/rules/common/git-workflow.md +6 -20
  10. package/app/rules/common/performance.md +25 -1
  11. package/app/rules/common/testing.md +7 -1
  12. package/benchmarks/ecosystem-doctor-snapshot.json +17 -15
  13. package/bin/ai-toolkit.js +2 -0
  14. package/kb/procedures/sop-maintenance.md +6 -3
  15. package/kb/reference/cli-reference.md +3 -2
  16. package/kb/reference/global-install-model.md +16 -3
  17. package/kb/reference/hooks-catalog.md +5 -3
  18. package/kb/reference/language-rules.md +28 -10
  19. package/kb/reference/unique-features.md +2 -1
  20. package/llms-full.txt +60 -22
  21. package/manifest.json +2 -2
  22. package/package.json +5 -2
  23. package/scripts/benchmark_ecosystem.py +0 -1
  24. package/scripts/check_split.py +11 -9
  25. package/scripts/claude_app.py +5 -7
  26. package/scripts/codex_skill_adapter.py +4 -12
  27. package/scripts/compile_slm.py +10 -26
  28. package/scripts/doctor.py +322 -0
  29. package/scripts/evaluate_skills.py +1 -1
  30. package/scripts/frontmatter.py +452 -29
  31. package/scripts/generate_augment_rules.py +4 -4
  32. package/scripts/generate_cursor_mdc.py +2 -3
  33. package/scripts/generate_language_rules_skills.py +8 -14
  34. package/scripts/generate_llms_txt.py +1 -15
  35. package/scripts/generate_opencode_agents.py +0 -1
  36. package/scripts/generate_opencode_skills.py +2 -20
  37. package/scripts/generate_windsurf_rules.py +0 -1
  38. package/scripts/generator_base.py +0 -1
  39. package/scripts/inject_hook_cli.py +15 -2
  40. package/scripts/inject_mcp_cli.py +1 -2
  41. package/scripts/install.py +32 -1
  42. package/scripts/install_git_hooks.py +0 -1
  43. package/scripts/install_steps/ai_tools.py +65 -25
  44. package/scripts/install_steps/markers.py +6 -6
  45. package/scripts/install_steps/skill_scope.py +188 -0
  46. package/scripts/instruction_core.py +5 -8
  47. package/scripts/merge-hooks.py +13 -3
  48. package/scripts/pack_codebase.py +1 -1
  49. package/scripts/surface_manifest.py +6 -7
  50. package/scripts/validate.py +180 -11
@@ -3,9 +3,9 @@ title: "Global Install Model"
3
3
  category: reference
4
4
  service: ai-toolkit
5
5
  tags: [install, global, claude, codex, plugins, local-setup]
6
- version: "3.4.0"
6
+ version: "3.5.0"
7
7
  created: "2026-03-26"
8
- last_updated: "2026-08-19"
8
+ last_updated: "2026-09-04"
9
9
  description: "Reference description of Claude Code global install, Claude app plugin export, project-local editor setup, global Codex plugin layering, and command responsibilities in ai-toolkit."
10
10
  ---
11
11
 
@@ -17,6 +17,19 @@ description: "Reference description of Claude Code global install, Claude app pl
17
17
 
18
18
  That means one machine-level install provides agents, skills, hooks, constitution, and rule files to every project without committing toolkit boilerplate into each repository.
19
19
 
20
+ Language knowledge skills are the one part of the global install that is
21
+ scoped by evidence (v4.32.0). Every `<lang>-rules` / `<lang>-patterns` skill is
22
+ still symlinked, but `install` and `update` read the languages detected across
23
+ the projects registered in `projects.json` and set `skillOverrides: off` in
24
+ `~/.claude/settings.json` for the others, so their descriptions stop loading
25
+ into every session. The entries the toolkit wrote are listed in `state.json`
26
+ under `managed_skill_overrides`; only those are ever removed again (when a
27
+ newly registered project brings the language back), a user's own override is
28
+ left alone. With no registered project on disk nothing is disabled.
29
+ `--language-skills all` restores everything and persists the choice
30
+ (`language_skill_scope` in `state.json`); `ai-toolkit doctor` reports the
31
+ resulting listing size and zero-use skills.
32
+
20
33
  Other editor targets are opt-in and only use documented file surfaces. Cursor
21
34
  rules stay project-local because Cursor's global user rules are managed through
22
35
  the settings UI, not a stable merge-safe file. Codex supports both project and
@@ -59,7 +72,7 @@ The `--profile` flag controls how much of each editor's native surface is activa
59
72
  |---------|-----------|----------|
60
73
  | `minimal` | Smallest editor surface. Copilot still receives its root instructions, native agents, and self-contained skills; Codex still receives instructions, agents, skills, and native safety hooks. | You want the smallest supported footprint. |
61
74
  | `standard` (default) | Claude Code + editor rule files. Includes Gemini and Antigravity hooks plus native Copilot instructions, agents, portable skills, and hooks. | Day-to-day installs. Most users. |
62
- | `strict` | Everything in `standard` plus git-hook wiring for commit-time safety checks. | Solo dev or tight team with zero tolerance for drift. |
75
+ | `strict` | Everything in `standard` plus git-hook wiring for commit-time safety checks and the `git-team` common rule (feature branches, PR size, required approval, review SLA). | Tight team with zero tolerance for drift. A solo maintainer releasing straight to `main` wants `standard`, which keeps the solo-safe `git-workflow` core only. |
63
76
  | `full` | Every native surface across every editor: hooks, sub-agents, custom commands, skill pointers for Cursor / Windsurf / Gemini / Augment / Antigravity. | You want maximum coverage and understand that each editor will carry generated files under its own layout. |
64
77
 
65
78
  Codex installs materialize the full skill catalog under `.agents/skills/`
@@ -3,9 +3,9 @@ title: "Hooks Catalog"
3
3
  category: reference
4
4
  service: ai-toolkit
5
5
  tags: [hooks, quality, safety, enforcement, settings.json]
6
- version: "1.11.0"
6
+ version: "1.12.0"
7
7
  created: "2026-03-27"
8
- last_updated: "2026-08-19"
8
+ last_updated: "2026-09-04"
9
9
  description: "Complete reference of all ai-toolkit hooks: events, scripts, installation, and runtime behavior."
10
10
  ---
11
11
 
@@ -190,6 +190,7 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
190
190
  | Matcher | *(all)* |
191
191
  | Script | `~/.softspark/ai-toolkit/hooks/quality-check.sh` |
192
192
  | Fires | After every Claude response |
193
+ | Mode | Background (`"async": true`). The linter (`npx tsc`, `ruff`) was the bulk of a 3.3 s median Stop chain; its output is advisory and reaches you when it finishes instead of holding the turn open |
193
194
 
194
195
  **Action:** Runs language-appropriate linter:
195
196
  - Python: `ruff check .`
@@ -208,6 +209,7 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
208
209
  | Matcher | *(all)* |
209
210
  | Script | `~/.softspark/ai-toolkit/hooks/save-session.sh` |
210
211
  | Fires | After every Claude response |
212
+ | Mode | Background (`"async": true`): writes a file and exits 0, nothing to wait for |
211
213
 
212
214
  **Action:** Writes enriched session context to the per-repo session store (`~/.softspark/ai-toolkit/sessions/<repo-key>/session-context.md`) for cross-session persistence. Captures:
213
215
  - Session ID and last assistant message (first 5 lines)
@@ -225,7 +227,7 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
225
227
  | Script | `~/.softspark/ai-toolkit/hooks/quality-gate.sh` |
226
228
  | Fires | Before Claude is allowed to finish a response |
227
229
 
228
- **Action:** Runs lint/typecheck. **Blocks stopping (exit 2)** if errors found, so Claude must continue and fix the issues. Missing local tooling is reported as skipped rather than blocking the session.
230
+ **Action:** Runs lint/typecheck. **Blocks stopping (exit 2)** if errors found, so Claude must continue and fix the issues. Missing local tooling is reported as skipped rather than blocking the session. Ruff runs only when the project configured it (`ruff.toml`, `.ruff.toml`, or a `[tool.ruff]` table in `pyproject.toml`); a `pyproject.toml` that only carries build or pytest/mypy metadata does not trigger `ruff check .` under whatever configuration the machine resolves (v4.32.0).
229
231
 
230
232
  Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
231
233
 
@@ -3,9 +3,9 @@ title: "Language Rules System"
3
3
  category: reference
4
4
  service: ai-toolkit
5
5
  tags: [rules, languages, coding-style, testing, patterns, security]
6
- version: "2.1.0"
6
+ version: "2.2.0"
7
7
  created: "2026-04-07"
8
- last_updated: "2026-06-30"
8
+ last_updated: "2026-09-04"
9
9
  description: "Reference for the language-specific rules system: 13 per-language rule sets shipped as knowledge skills, plus common rules installed as Claude Code path-scoped project rules."
10
10
  ---
11
11
 
@@ -55,7 +55,7 @@ app/rules/
55
55
  └── medplum/
56
56
  ```
57
57
 
58
- **Total: 13 per-language directories × 5 files + 1 common directory × 5 files + 3 standalone files** (see README.md for canonical count). Per-language directories ship as `<lang>-rules` knowledge skills; the common directory is installed as Claude Code `.claude/rules/ai-toolkit-*.md` files.
58
+ **Total: 13 per-language directories × 5 files + 1 common directory × 6 files (5 in every profile + `git-team` in `strict`) + 3 standalone files** (see README.md for canonical count). Per-language directories ship as `<lang>-rules` knowledge skills; the common directory is installed as Claude Code `.claude/rules/ai-toolkit-*.md` files.
59
59
 
60
60
  ## Supported Languages
61
61
 
@@ -145,13 +145,16 @@ Common rules are installed as path-scoped Claude Code rule files:
145
145
  ```
146
146
  .claude/rules/
147
147
  ├── ai-toolkit-coding-style.md
148
+ ├── ai-toolkit-git-team.md # --profile strict only
148
149
  ├── ai-toolkit-git-workflow.md
149
150
  ├── ai-toolkit-performance.md
150
151
  ├── ai-toolkit-security.md
151
152
  └── ai-toolkit-testing.md
152
153
  ```
153
154
 
154
- Each file starts with:
155
+ A source rule may also carry `profiles:` (same block-list form). `git-team` declares `profiles: ["strict"]`: branching, pull-request, and review conventions for teams, kept out of `standard` so a solo maintainer who releases straight to `main` is not told to open PRs against themselves. Rerunning `install --local` with a different profile adds or removes the managed file. Only the Claude Code local install honours `profiles`; the Claude app export, `compile-slm`, and editor `lang-common` bundles still receive every common rule (see `DECISIONS.md`, 2026-09-04).
156
+
157
+ Each file's `paths` frontmatter is copied from the source rule in `app/rules/common/<category>.md`. A source rule without a `paths` block is always-on:
155
158
 
156
159
  ```yaml
157
160
  ---
@@ -160,6 +163,18 @@ paths:
160
163
  ---
161
164
  ```
162
165
 
166
+ A source rule with a `paths` block is path-scoped, so Claude Code loads it only when a matching file is touched. As of v4.32.0 `testing` (`**/*.test.*`, `**/*.spec.*`, `**/test_*`, `**/*_test.*`, `**/tests/**`) and `performance` (source-file extensions plus `**/*.sql`) are scoped; `coding-style`, `git-workflow`, and `security` stay always-on because they carry prohibitions that must hold in every session. To change a scope, edit the source frontmatter; `validate.py` rejects inline lists and unquoted globs because the installer reads only the block form:
167
+
168
+ ```yaml
169
+ ---
170
+ language: common
171
+ category: testing
172
+ version: "1.1.0"
173
+ paths:
174
+ - "**/tests/**"
175
+ ---
176
+ ```
177
+
163
178
  The project `CLAUDE.md` receives only a compact index between a single named marker (the per-language markers from v1.x are no longer used):
164
179
 
165
180
  ```
@@ -167,10 +182,13 @@ The project `CLAUDE.md` receives only a compact index between a single named mar
167
182
  # Language Rules
168
183
 
169
184
  Common ai-toolkit rules live in `.claude/rules/ai-toolkit-*.md`
170
- with Claude Code `paths` frontmatter so they load when project files
171
- are opened instead of expanding this CLAUDE.md at session startup.
185
+ with Claude Code `paths` frontmatter instead of expanding this
186
+ CLAUDE.md. Always-on rules load in every session; path-scoped rules
187
+ load only when a matching file is touched.
188
+
189
+ Always-on: `.claude/rules/ai-toolkit-coding-style.md`, ...
172
190
 
173
- Common rule files: `.claude/rules/ai-toolkit-coding-style.md`, ...
191
+ Path-scoped: `.claude/rules/ai-toolkit-performance.md`, ...
174
192
 
175
193
  Language-specific rules live in `<lang>-rules` knowledge skills (e.g.
176
194
  `python-rules`, `typescript-rules`) and load automatically when their
@@ -207,7 +225,7 @@ Language rules are tracked as modules in `manifest.json`:
207
225
 
208
226
  | Module | Description |
209
227
  |--------|-------------|
210
- | `rules-common` | Common coding rules (5 files), included in `standard` profile |
228
+ | `rules-common` | Common coding rules (6 files: 5 in every profile, `git-team` in `strict` only), included in `standard` profile |
211
229
  | `rules-typescript` | TypeScript-specific rules |
212
230
  | `rules-python` | Python-specific rules |
213
231
  | `rules-golang` | Go-specific rules |
@@ -228,9 +246,9 @@ Language rules are tracked as modules in `manifest.json`:
228
246
  |---|---|---|---|
229
247
  | Source | `app/rules/common/` | `app/rules/<lang>/` | `app/skills/<name>/SKILL.md` |
230
248
  | Delivery to Claude | Path-scoped `.claude/rules/ai-toolkit-*.md` files (`--local`) + compact `CLAUDE.md` index | Generated as `<lang>-rules` knowledge skills, loaded contextually | Loaded contextually by description match |
231
- | Visibility | Loaded when project files are opened | Loaded when triggers match (file extensions, framework names) | Loaded when triggers match |
249
+ | Visibility | Always-on (`coding-style`, `git-workflow`, `security`) or loaded when a matching file is touched (`testing`, `performance`) | Loaded when triggers match (file extensions, framework names) | Loaded when triggers match |
232
250
  | Scope | Language-agnostic standards (security, git, testing, perf, style) | Per-language coding-style, frameworks, patterns, security, testing | Domain skills (testing, debugging, RAG, etc.) |
233
- | Install | `ai-toolkit install --local` | Global install (skills directory is symlinked) | Global install |
251
+ | Install | `ai-toolkit install --local` | Global install (skills directory is symlinked); skills for languages no registered project uses are turned off via `skillOverrides` (`--language-skills detected`, the default) unless `--language-skills all` was chosen | Global install |
234
252
  | Other editors | Inlined into editor-specific rule files | Inlined into editor-specific rule files (still full content, not skills) | N/A |
235
253
 
236
254
  Per-language content delivered as a knowledge skill is the same Markdown that other editors receive inlined. The split exists only for Claude, where the Agent Skills progressive-disclosure mechanism keeps the system prompt small.
@@ -4,7 +4,7 @@ category: reference
4
4
  service: ai-toolkit
5
5
  tags: [features, differentiators, constitution, hooks, security, tdd, memory]
6
6
  created: "2026-04-13"
7
- last_updated: "2026-08-06"
7
+ last_updated: "2026-09-04"
8
8
  description: "Detailed description of ai-toolkit's unique features: constitution enforcement, hooks system, security scanning, effort budgeting, quality gates, and more."
9
9
  ---
10
10
 
@@ -119,6 +119,7 @@ added after the thing it checks had already shipped broken.
119
119
  | **Public surface** (`surface_manifest.py` + `app/surface.json`) | a skill, agent, CLI command, frontmatter field, hook, KB category or pack disappearing | `BACKWARD_COMPATIBILITY.md` listed the surfaces; renaming one still left every check green |
120
120
  | **Script invocation** (`validate.py`) | a skill running its own script by any path other than `${CLAUDE_SKILL_DIR}`, or through the wrong interpreter | nine skills shipped invocations that resolved only in the repo, never on an installed machine |
121
121
  | **Skill body budget** (`validate.py`) | a `SKILL.md` body over 20,000 bytes; warns over 18,000 | the body loads on every trigger match, including accidental ones; three skills sat at 24–35 KB |
122
+ | **Skill description budget** (`validate.py`) | a description over 1,024 characters (the Agent Skills cap; the runtime truncates past it), or an unquoted description containing `: ` or ` #` (strict YAML drops every field, `allowed-tools` included); warns over 400; applied to plugin-pack skills too | two installed skills sat at 791 and 883 characters and made up 8% of the model's skill listing on their own |
122
123
  | **Split integrity** (`check_split.py`) | a body → `reference/` refactor that lost a fenced code line, a required section, the description, or a working link | run by hand during a split; caught a heading corrupted inside a fenced example on first use |
123
124
 
124
125
  The surface check is deliberately one-directional: **removals fail, additions pass.**
package/llms-full.txt CHANGED
@@ -7204,9 +7204,9 @@ title: "SOP: AI Toolkit Maintenance"
7204
7204
  category: procedures
7205
7205
  service: ai-toolkit
7206
7206
  tags: [sop, maintenance, agents, skills, install]
7207
- version: "3.4.0"
7207
+ version: "3.5.0"
7208
7208
  created: "2026-03-23"
7209
- last_updated: "2026-08-06"
7209
+ last_updated: "2026-09-04"
7210
7210
  description: "Standard operating procedures for installing, maintaining, and evolving the ai-toolkit."
7211
7211
  ---
7212
7212
 
@@ -7430,7 +7430,9 @@ that runtime should receive the change.
7430
7430
  greets the user with a traceback, and one that reads stdin must answer an
7431
7431
  empty stdin with an error rather than blocking forever.
7432
7432
  3. Update `kb/reference/skills-catalog.md` and `app/ARCHITECTURE.md`
7433
- 4. Run `scripts/validate.py` — it checks the invocation, the body budget, and
7433
+ 4. Run `scripts/validate.py` — it checks the invocation, the body budget, the
7434
+ description budget (warn over 400 characters, error over 1024; unquoted
7435
+ descriptions containing `: ` or ` #` are rejected, use `>-`), and
7434
7436
  `reference/` link resolution
7435
7437
  5. Run `python3 scripts/surface_manifest.py` before the next release to adopt the
7436
7438
  new skill into the protected surface
@@ -7506,6 +7508,7 @@ bats tests/test_install.bats tests/test_codex.bats
7506
7508
  bats tests/test_claude_app.bats tests/test_hooks_per_editor.bats
7507
7509
  python3 scripts/claude_app.py verify
7508
7510
  python3 scripts/validate.py --strict
7511
+ npm run test:py # pytest over scripts/ logic incl. the frontmatter corpus test (dev venv, see CONTRIBUTING.md)
7509
7512
  ```
7510
7513
 
7511
7514
  ## Managing Plugins
@@ -10453,7 +10456,7 @@ category: reference
10453
10456
  service: ai-toolkit
10454
10457
  tags: [cli, commands, reference, install, update, plugin, mcp, telemetry]
10455
10458
  created: "2026-04-13"
10456
- last_updated: "2026-08-19"
10459
+ last_updated: "2026-09-04"
10457
10460
  description: "Complete CLI reference for all ai-toolkit commands, options, and flags."
10458
10461
  ---
10459
10462
 
@@ -10468,6 +10471,7 @@ Usage: ai-toolkit <command> [options]
10468
10471
  | Command | Description |
10469
10472
  |---------|-------------|
10470
10473
  | `install` | First-time global install into `~/.claude/` + Cursor, Windsurf, Gemini |
10474
+ | `install --language-skills detected\|all` | `detected` (default): turn off `<lang>-rules`/`<lang>-patterns` skills for languages no registered project uses, via `skillOverrides` in `~/.claude/settings.json` (entries are tracked in `state.json` and restored when a project brings the language back; a user's own override is never touched); `all`: keep every language skill on. The choice persists across `install`/`update` |
10471
10475
  | `install --local` | Claude Code configs only; add `--editors all` or `--editors cursor,aider` for other tools |
10472
10476
  | `update` | Re-apply toolkit after `npm install -g @softspark/ai-toolkit@latest` |
10473
10477
  | `update --local` | Re-apply + auto-detect editors from existing project files |
@@ -10475,7 +10479,7 @@ Usage: ai-toolkit <command> [options]
10475
10479
  | `status` | Show installed modules and version |
10476
10480
  | `uninstall` | Remove toolkit from `~/.claude/` |
10477
10481
  | `validate` | Verify toolkit integrity (`--strict` for CI-grade, warnings = errors) |
10478
- | `doctor` | Diagnose install health, hooks, quick-win assets, and artifact drift |
10482
+ | `doctor` | Diagnose install health, hooks, quick-win assets, artifact drift, context budget (est. resident tokens of the skill/agent listings and user rules, plus skills with zero recorded use; read-only, prints the `skillOverrides` key to paste), and permission rules (`permissions.allow` wildcards on interpreters, task runners, package installs, `gh api`, `curl`, `git fetch`, destructive commands; warns only, never edits) |
10479
10483
  | `doctor --fix` | Auto-repair broken symlinks, missing hooks, stale artifacts |
10480
10484
  | `eject [dir]` | Export standalone config (no symlinks, no toolkit dependency) |
10481
10485
  | `claude-app export [--output FILE] [--no-custom-rules] [--verify]` | Build an uploadable Claude Chat/Desktop/Cowork plugin ZIP and global-instructions file |
@@ -12780,9 +12784,9 @@ title: "Global Install Model"
12780
12784
  category: reference
12781
12785
  service: ai-toolkit
12782
12786
  tags: [install, global, claude, codex, plugins, local-setup]
12783
- version: "3.4.0"
12787
+ version: "3.5.0"
12784
12788
  created: "2026-03-26"
12785
- last_updated: "2026-08-19"
12789
+ last_updated: "2026-09-04"
12786
12790
  description: "Reference description of Claude Code global install, Claude app plugin export, project-local editor setup, global Codex plugin layering, and command responsibilities in ai-toolkit."
12787
12791
  ---
12788
12792
 
@@ -12794,6 +12798,19 @@ description: "Reference description of Claude Code global install, Claude app pl
12794
12798
 
12795
12799
  That means one machine-level install provides agents, skills, hooks, constitution, and rule files to every project without committing toolkit boilerplate into each repository.
12796
12800
 
12801
+ Language knowledge skills are the one part of the global install that is
12802
+ scoped by evidence (v4.32.0). Every `<lang>-rules` / `<lang>-patterns` skill is
12803
+ still symlinked, but `install` and `update` read the languages detected across
12804
+ the projects registered in `projects.json` and set `skillOverrides: off` in
12805
+ `~/.claude/settings.json` for the others, so their descriptions stop loading
12806
+ into every session. The entries the toolkit wrote are listed in `state.json`
12807
+ under `managed_skill_overrides`; only those are ever removed again (when a
12808
+ newly registered project brings the language back), a user's own override is
12809
+ left alone. With no registered project on disk nothing is disabled.
12810
+ `--language-skills all` restores everything and persists the choice
12811
+ (`language_skill_scope` in `state.json`); `ai-toolkit doctor` reports the
12812
+ resulting listing size and zero-use skills.
12813
+
12797
12814
  Other editor targets are opt-in and only use documented file surfaces. Cursor
12798
12815
  rules stay project-local because Cursor's global user rules are managed through
12799
12816
  the settings UI, not a stable merge-safe file. Codex supports both project and
@@ -12836,7 +12853,7 @@ The `--profile` flag controls how much of each editor's native surface is activa
12836
12853
  |---------|-----------|----------|
12837
12854
  | `minimal` | Smallest editor surface. Copilot still receives its root instructions, native agents, and self-contained skills; Codex still receives instructions, agents, skills, and native safety hooks. | You want the smallest supported footprint. |
12838
12855
  | `standard` (default) | Claude Code + editor rule files. Includes Gemini and Antigravity hooks plus native Copilot instructions, agents, portable skills, and hooks. | Day-to-day installs. Most users. |
12839
- | `strict` | Everything in `standard` plus git-hook wiring for commit-time safety checks. | Solo dev or tight team with zero tolerance for drift. |
12856
+ | `strict` | Everything in `standard` plus git-hook wiring for commit-time safety checks and the `git-team` common rule (feature branches, PR size, required approval, review SLA). | Tight team with zero tolerance for drift. A solo maintainer releasing straight to `main` wants `standard`, which keeps the solo-safe `git-workflow` core only. |
12840
12857
  | `full` | Every native surface across every editor: hooks, sub-agents, custom commands, skill pointers for Cursor / Windsurf / Gemini / Augment / Antigravity. | You want maximum coverage and understand that each editor will carry generated files under its own layout. |
12841
12858
 
12842
12859
  Codex installs materialize the full skill catalog under `.agents/skills/`
@@ -13225,9 +13242,9 @@ title: "Hooks Catalog"
13225
13242
  category: reference
13226
13243
  service: ai-toolkit
13227
13244
  tags: [hooks, quality, safety, enforcement, settings.json]
13228
- version: "1.11.0"
13245
+ version: "1.12.0"
13229
13246
  created: "2026-03-27"
13230
- last_updated: "2026-08-19"
13247
+ last_updated: "2026-09-04"
13231
13248
  description: "Complete reference of all ai-toolkit hooks: events, scripts, installation, and runtime behavior."
13232
13249
  ---
13233
13250
 
@@ -13412,6 +13429,7 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
13412
13429
  | Matcher | *(all)* |
13413
13430
  | Script | `~/.softspark/ai-toolkit/hooks/quality-check.sh` |
13414
13431
  | Fires | After every Claude response |
13432
+ | Mode | Background (`"async": true`). The linter (`npx tsc`, `ruff`) was the bulk of a 3.3 s median Stop chain; its output is advisory and reaches you when it finishes instead of holding the turn open |
13415
13433
 
13416
13434
  **Action:** Runs language-appropriate linter:
13417
13435
  - Python: `ruff check .`
@@ -13430,6 +13448,7 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
13430
13448
  | Matcher | *(all)* |
13431
13449
  | Script | `~/.softspark/ai-toolkit/hooks/save-session.sh` |
13432
13450
  | Fires | After every Claude response |
13451
+ | Mode | Background (`"async": true`): writes a file and exits 0, nothing to wait for |
13433
13452
 
13434
13453
  **Action:** Writes enriched session context to the per-repo session store (`~/.softspark/ai-toolkit/sessions/<repo-key>/session-context.md`) for cross-session persistence. Captures:
13435
13454
  - Session ID and last assistant message (first 5 lines)
@@ -13447,7 +13466,7 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
13447
13466
  | Script | `~/.softspark/ai-toolkit/hooks/quality-gate.sh` |
13448
13467
  | Fires | Before Claude is allowed to finish a response |
13449
13468
 
13450
- **Action:** Runs lint/typecheck. **Blocks stopping (exit 2)** if errors found, so Claude must continue and fix the issues. Missing local tooling is reported as skipped rather than blocking the session.
13469
+ **Action:** Runs lint/typecheck. **Blocks stopping (exit 2)** if errors found, so Claude must continue and fix the issues. Missing local tooling is reported as skipped rather than blocking the session. Ruff runs only when the project configured it (`ruff.toml`, `.ruff.toml`, or a `[tool.ruff]` table in `pyproject.toml`); a `pyproject.toml` that only carries build or pytest/mypy metadata does not trigger `ruff check .` under whatever configuration the machine resolves (v4.32.0).
13451
13470
 
13452
13471
  Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
13453
13472
 
@@ -14045,9 +14064,9 @@ title: "Language Rules System"
14045
14064
  category: reference
14046
14065
  service: ai-toolkit
14047
14066
  tags: [rules, languages, coding-style, testing, patterns, security]
14048
- version: "2.1.0"
14067
+ version: "2.2.0"
14049
14068
  created: "2026-04-07"
14050
- last_updated: "2026-06-30"
14069
+ last_updated: "2026-09-04"
14051
14070
  description: "Reference for the language-specific rules system: 13 per-language rule sets shipped as knowledge skills, plus common rules installed as Claude Code path-scoped project rules."
14052
14071
  ---
14053
14072
 
@@ -14097,7 +14116,7 @@ app/rules/
14097
14116
  └── medplum/
14098
14117
  ```
14099
14118
 
14100
- **Total: 13 per-language directories × 5 files + 1 common directory × 5 files + 3 standalone files** (see README.md for canonical count). Per-language directories ship as `<lang>-rules` knowledge skills; the common directory is installed as Claude Code `.claude/rules/ai-toolkit-*.md` files.
14119
+ **Total: 13 per-language directories × 5 files + 1 common directory × 6 files (5 in every profile + `git-team` in `strict`) + 3 standalone files** (see README.md for canonical count). Per-language directories ship as `<lang>-rules` knowledge skills; the common directory is installed as Claude Code `.claude/rules/ai-toolkit-*.md` files.
14101
14120
 
14102
14121
  ## Supported Languages
14103
14122
 
@@ -14187,13 +14206,16 @@ Common rules are installed as path-scoped Claude Code rule files:
14187
14206
  ```
14188
14207
  .claude/rules/
14189
14208
  ├── ai-toolkit-coding-style.md
14209
+ ├── ai-toolkit-git-team.md # --profile strict only
14190
14210
  ├── ai-toolkit-git-workflow.md
14191
14211
  ├── ai-toolkit-performance.md
14192
14212
  ├── ai-toolkit-security.md
14193
14213
  └── ai-toolkit-testing.md
14194
14214
  ```
14195
14215
 
14196
- Each file starts with:
14216
+ A source rule may also carry `profiles:` (same block-list form). `git-team` declares `profiles: ["strict"]`: branching, pull-request, and review conventions for teams, kept out of `standard` so a solo maintainer who releases straight to `main` is not told to open PRs against themselves. Rerunning `install --local` with a different profile adds or removes the managed file. Only the Claude Code local install honours `profiles`; the Claude app export, `compile-slm`, and editor `lang-common` bundles still receive every common rule (see `DECISIONS.md`, 2026-09-04).
14217
+
14218
+ Each file's `paths` frontmatter is copied from the source rule in `app/rules/common/<category>.md`. A source rule without a `paths` block is always-on:
14197
14219
 
14198
14220
  ```yaml
14199
14221
  ---
@@ -14202,6 +14224,18 @@ paths:
14202
14224
  ---
14203
14225
  ```
14204
14226
 
14227
+ A source rule with a `paths` block is path-scoped, so Claude Code loads it only when a matching file is touched. As of v4.32.0 `testing` (`**/*.test.*`, `**/*.spec.*`, `**/test_*`, `**/*_test.*`, `**/tests/**`) and `performance` (source-file extensions plus `**/*.sql`) are scoped; `coding-style`, `git-workflow`, and `security` stay always-on because they carry prohibitions that must hold in every session. To change a scope, edit the source frontmatter; `validate.py` rejects inline lists and unquoted globs because the installer reads only the block form:
14228
+
14229
+ ```yaml
14230
+ ---
14231
+ language: common
14232
+ category: testing
14233
+ version: "1.1.0"
14234
+ paths:
14235
+ - "**/tests/**"
14236
+ ---
14237
+ ```
14238
+
14205
14239
  The project `CLAUDE.md` receives only a compact index between a single named marker (the per-language markers from v1.x are no longer used):
14206
14240
 
14207
14241
  ```
@@ -14209,10 +14243,13 @@ The project `CLAUDE.md` receives only a compact index between a single named mar
14209
14243
  # Language Rules
14210
14244
 
14211
14245
  Common ai-toolkit rules live in `.claude/rules/ai-toolkit-*.md`
14212
- with Claude Code `paths` frontmatter so they load when project files
14213
- are opened instead of expanding this CLAUDE.md at session startup.
14246
+ with Claude Code `paths` frontmatter instead of expanding this
14247
+ CLAUDE.md. Always-on rules load in every session; path-scoped rules
14248
+ load only when a matching file is touched.
14214
14249
 
14215
- Common rule files: `.claude/rules/ai-toolkit-coding-style.md`, ...
14250
+ Always-on: `.claude/rules/ai-toolkit-coding-style.md`, ...
14251
+
14252
+ Path-scoped: `.claude/rules/ai-toolkit-performance.md`, ...
14216
14253
 
14217
14254
  Language-specific rules live in `<lang>-rules` knowledge skills (e.g.
14218
14255
  `python-rules`, `typescript-rules`) and load automatically when their
@@ -14249,7 +14286,7 @@ Language rules are tracked as modules in `manifest.json`:
14249
14286
 
14250
14287
  | Module | Description |
14251
14288
  |--------|-------------|
14252
- | `rules-common` | Common coding rules (5 files), included in `standard` profile |
14289
+ | `rules-common` | Common coding rules (6 files: 5 in every profile, `git-team` in `strict` only), included in `standard` profile |
14253
14290
  | `rules-typescript` | TypeScript-specific rules |
14254
14291
  | `rules-python` | Python-specific rules |
14255
14292
  | `rules-golang` | Go-specific rules |
@@ -14270,9 +14307,9 @@ Language rules are tracked as modules in `manifest.json`:
14270
14307
  |---|---|---|---|
14271
14308
  | Source | `app/rules/common/` | `app/rules/<lang>/` | `app/skills/<name>/SKILL.md` |
14272
14309
  | Delivery to Claude | Path-scoped `.claude/rules/ai-toolkit-*.md` files (`--local`) + compact `CLAUDE.md` index | Generated as `<lang>-rules` knowledge skills, loaded contextually | Loaded contextually by description match |
14273
- | Visibility | Loaded when project files are opened | Loaded when triggers match (file extensions, framework names) | Loaded when triggers match |
14310
+ | Visibility | Always-on (`coding-style`, `git-workflow`, `security`) or loaded when a matching file is touched (`testing`, `performance`) | Loaded when triggers match (file extensions, framework names) | Loaded when triggers match |
14274
14311
  | Scope | Language-agnostic standards (security, git, testing, perf, style) | Per-language coding-style, frameworks, patterns, security, testing | Domain skills (testing, debugging, RAG, etc.) |
14275
- | Install | `ai-toolkit install --local` | Global install (skills directory is symlinked) | Global install |
14312
+ | Install | `ai-toolkit install --local` | Global install (skills directory is symlinked); skills for languages no registered project uses are turned off via `skillOverrides` (`--language-skills detected`, the default) unless `--language-skills all` was chosen | Global install |
14276
14313
  | Other editors | Inlined into editor-specific rule files | Inlined into editor-specific rule files (still full content, not skills) | N/A |
14277
14314
 
14278
14315
  Per-language content delivered as a knowledge skill is the same Markdown that other editors receive inlined. The split exists only for Claude, where the Agent Skills progressive-disclosure mechanism keeps the system prompt small.
@@ -17070,7 +17107,7 @@ category: reference
17070
17107
  service: ai-toolkit
17071
17108
  tags: [features, differentiators, constitution, hooks, security, tdd, memory]
17072
17109
  created: "2026-04-13"
17073
- last_updated: "2026-08-06"
17110
+ last_updated: "2026-09-04"
17074
17111
  description: "Detailed description of ai-toolkit's unique features: constitution enforcement, hooks system, security scanning, effort budgeting, quality gates, and more."
17075
17112
  ---
17076
17113
 
@@ -17185,6 +17222,7 @@ added after the thing it checks had already shipped broken.
17185
17222
  | **Public surface** (`surface_manifest.py` + `app/surface.json`) | a skill, agent, CLI command, frontmatter field, hook, KB category or pack disappearing | `BACKWARD_COMPATIBILITY.md` listed the surfaces; renaming one still left every check green |
17186
17223
  | **Script invocation** (`validate.py`) | a skill running its own script by any path other than `${CLAUDE_SKILL_DIR}`, or through the wrong interpreter | nine skills shipped invocations that resolved only in the repo, never on an installed machine |
17187
17224
  | **Skill body budget** (`validate.py`) | a `SKILL.md` body over 20,000 bytes; warns over 18,000 | the body loads on every trigger match, including accidental ones; three skills sat at 24–35 KB |
17225
+ | **Skill description budget** (`validate.py`) | a description over 1,024 characters (the Agent Skills cap; the runtime truncates past it), or an unquoted description containing `: ` or ` #` (strict YAML drops every field, `allowed-tools` included); warns over 400; applied to plugin-pack skills too | two installed skills sat at 791 and 883 characters and made up 8% of the model's skill listing on their own |
17188
17226
  | **Split integrity** (`check_split.py`) | a body → `reference/` refactor that lost a fenced code line, a required section, the description, or a working link | run by hand during a split; caught a heading corrupted inside a fenced example on first use |
17189
17227
 
17190
17228
  The surface check is deliberately one-directional: **removals fail, additions pass.**
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.31.0",
2
+ "version": "4.32.0",
3
3
  "components": {
4
4
  "agents": {
5
5
  "description": "44 specialized agents (orchestrator, backend, frontend, security, devops, etc.)",
@@ -141,7 +141,7 @@
141
141
  "default": true
142
142
  },
143
143
  "rules-common": {
144
- "description": "Common coding rules (5 files)",
144
+ "description": "Common coding rules (6 files; git-team ships with the strict profile only)",
145
145
  "default": true
146
146
  },
147
147
  "rules-typescript": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softspark/ai-toolkit",
3
- "version": "4.31.0",
3
+ "version": "4.32.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",
@@ -75,7 +75,10 @@
75
75
  "generate:all": "npm run generate:language-rules && npm run generate:claude-app && npm run generate:agents && npm run generate:opencode-agents && npm run generate:opencode-commands && npm run generate:opencode-skills && npm run generate:cursor && npm run generate:cursor-mdc && npm run generate:windsurf && npm run generate:windsurf-rules && npm run generate:copilot && npm run generate:gemini && npm run generate:gemini-agents && npm run generate:cline && npm run generate:cline-hooks && npm run generate:roo && npm run generate:roo-rules && npm run generate:aider && npm run generate:augment-rules && npm run generate:antigravity && npm run generate:llms && npm run generate:badges",
76
76
  "generate:badges": "python3 scripts/sync_badges.py",
77
77
  "surface": "python3 scripts/surface_manifest.py",
78
- "surface:update": "python3 scripts/surface_manifest.py --update"
78
+ "surface:update": "python3 scripts/surface_manifest.py --update",
79
+ "test:py": "python3 -m pytest tests/python",
80
+ "lint:py": "ruff check scripts --select E,F --ignore E501,E402 --line-length 120 --extend-exclude app/skills/*/scripts,benchmarks",
81
+ "typecheck:py": "python3 -m mypy"
79
82
  },
80
83
  "files": [
81
84
  "bin/",
@@ -11,7 +11,6 @@ Usage:
11
11
  from __future__ import annotations
12
12
 
13
13
  import json
14
- import os
15
14
  import sys
16
15
  import urllib.request
17
16
  from datetime import date, datetime, timezone
@@ -54,6 +54,8 @@ from pathlib import Path
54
54
 
55
55
  sys.path.insert(0, str(Path(__file__).resolve().parent))
56
56
  from _common import toolkit_dir as default_toolkit_dir
57
+ from frontmatter import FrontmatterError
58
+ from frontmatter import split_frontmatter as _split_frontmatter
57
59
 
58
60
  # Sections that carry prescriptive process and environment-specific traps.
59
61
  # They must load on every run, so they never move down into reference/.
@@ -75,16 +77,16 @@ NON_FILE_PREFIXES = ("#", "/", "mailto:", "tel:", "$")
75
77
  # ---------------------------------------------------------------------------
76
78
 
77
79
  def split_frontmatter(text: str) -> tuple[str, str]:
78
- """Return (frontmatter_block, body). Frontmatter is '' when absent."""
79
- lines = text.splitlines()
80
- if not lines or lines[0].strip() != "---":
80
+ """Return (frontmatter_block, body). Frontmatter is '' when absent.
81
+
82
+ An unterminated block counts as body rather than being swallowed, so the
83
+ split gate still compares every byte of such a file.
84
+ """
85
+ try:
86
+ block, body = _split_frontmatter(text)
87
+ except FrontmatterError:
81
88
  return "", text
82
- for idx in range(1, len(lines)):
83
- if lines[idx].strip() == "---":
84
- return "\n".join(lines[1:idx]), "\n".join(lines[idx + 1:])
85
- # Unterminated frontmatter — treat the whole file as body rather than
86
- # silently swallowing it.
87
- return "", text
89
+ return block or "", body
88
90
 
89
91
 
90
92
  def description_of(frontmatter: str) -> str:
@@ -27,6 +27,9 @@ import tempfile
27
27
  import zipfile
28
28
  from pathlib import Path
29
29
 
30
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
31
+ from frontmatter import split_frontmatter # noqa: E402
32
+
30
33
 
31
34
  TOOLKIT_DIR = Path(__file__).resolve().parent.parent
32
35
  APP_DIR = TOOLKIT_DIR / "app"
@@ -55,13 +58,8 @@ def _write_text(path: Path, content: str) -> None:
55
58
 
56
59
 
57
60
  def _strip_frontmatter(content: str) -> str:
58
- lines = content.splitlines()
59
- if not lines or lines[0].strip() != "---":
60
- return content.strip()
61
- for index in range(1, len(lines)):
62
- if lines[index].strip() == "---":
63
- return "\n".join(lines[index + 1 :]).strip()
64
- return content.strip()
61
+ """Body of a rule file with surrounding whitespace removed."""
62
+ return split_frontmatter(content)[1].strip()
65
63
 
66
64
 
67
65
  def render_plugin_hooks() -> str:
@@ -280,8 +280,10 @@ def _build_portable_skill_text(skill_file: Path, platform: str) -> str:
280
280
  if invocation_lines:
281
281
  rendered_frontmatter += "\n" + "\n".join(invocation_lines)
282
282
  else:
283
- frontmatter = _parse_frontmatter(match.group("frontmatter"))
284
- rendered_frontmatter = _render_frontmatter(frontmatter)
283
+ # Native skills pass their frontmatter through untouched. The old
284
+ # line-by-line re-render flattened nested keys (`hooks:` blocks) into
285
+ # top-level `key: ` lines, which is not the document the author wrote.
286
+ rendered_frontmatter = match.group("frontmatter").strip("\n")
285
287
 
286
288
  return f"---\n{rendered_frontmatter}\n---\n{body.rstrip()}\n"
287
289
 
@@ -1112,16 +1114,6 @@ def _is_relative_to(path: Path, parent: Path) -> bool:
1112
1114
  return False
1113
1115
 
1114
1116
 
1115
- def _parse_frontmatter(frontmatter_text: str) -> list[tuple[str, str]]:
1116
- entries: list[tuple[str, str]] = []
1117
- for line in frontmatter_text.splitlines():
1118
- if ":" not in line:
1119
- continue
1120
- key, value = line.split(":", 1)
1121
- entries.append((key.strip(), value.strip()))
1122
- return entries
1123
-
1124
-
1125
1117
  def _invocation_metadata_lines(frontmatter_text: str) -> list[str]:
1126
1118
  """Return exact canonical invocation lines, rejecting duplicate fields."""
1127
1119
  fields = ("user-invocable", "disable-model-invocation")