@softspark/ai-toolkit 4.26.0 → 4.27.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 CHANGED
@@ -7,6 +7,24 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v4.27.0 — Doctor sees the Claude app plugin (2026-08-21)
11
+
12
+ ### Added
13
+
14
+ - **Doctor check 11: plugin double-load.** `ai-toolkit doctor` now reads the
15
+ Claude Code plugin registry (`~/.claude/plugins/installed_plugins.json`) and
16
+ `enabledPlugins` in `~/.claude/settings.json`. When an uploaded Claude app
17
+ plugin is active next to the global install, both feed the same session:
18
+ Claude Code merges plugin hooks with user hooks without deduplication, so every
19
+ toolkit hook fires twice per event and skills and agents load twice. The check
20
+ warns with the hook count, and `--fix` disables the plugin for Claude Code
21
+ while leaving the global install authoritative. Previously this passed as a
22
+ healthy install with no signal at all.
23
+ - `kb/troubleshooting/plugin-double-load.md` documents the symptom, the debug-log
24
+ evidence, and the fix.
25
+
26
+ ---
27
+
10
28
  ## v4.26.0 — Python floor is declared and enforced (2026-08-21)
11
29
 
12
30
  ### Added
package/README.md CHANGED
@@ -6,19 +6,23 @@
6
6
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
7
7
  [![Skills](https://img.shields.io/badge/skills-109-brightgreen)](app/skills/)
8
8
  [![Agents](https://img.shields.io/badge/agents-44-blue)](app/agents/)
9
- [![Tests](https://img.shields.io/badge/tests-1640%20passing-success)](tests/)
9
+ [![Tests](https://img.shields.io/badge/tests-1645%20passing-success)](tests/)
10
10
 
11
- ## What's New in v4.26.0
11
+ ## What's New in v4.27.0
12
12
 
13
- **v4.26.0** declares and enforces the Python floor the scripts already needed:
13
+ **v4.27.0** teaches `doctor` about a collision it used to report as healthy:
14
14
 
15
- - `ai-toolkit` now checks `python3` before running anything and requires 3.11+.
16
- - macOS `/usr/bin/python3` is 3.9, which used to crash `update --local` with a
17
- raw `dataclass() got an unexpected keyword argument 'slots'` traceback. You
18
- now get the version, the requirement, and `brew install python@3.13`.
19
- - `python3 scripts/*.py` run directly fails the same way, via `_common.py`.
20
- - CI runs the syntax check plus a full import sweep on both 3.11 and 3.13, so a
21
- version-gated runtime feature cannot slip through `py_compile` again.
15
+ - Uploading the Claude app plugin ZIP registers it under `~/.claude/plugins`,
16
+ which Claude Code reads too. The plugin then carries the same skills, agents,
17
+ and hooks as the global install.
18
+ - Claude Code merges plugin hooks with user hooks without deduplication, so
19
+ every toolkit hook fired twice per event and skills and agents loaded twice.
20
+ `doctor` printed `HEALTH CHECK PASSED` through all of it.
21
+ - New check 11 reports the collision with the hook count; `doctor --fix`
22
+ disables the plugin for Claude Code and leaves the global install
23
+ authoritative.
24
+ - `kb/troubleshooting/plugin-double-load.md` documents the debug-log evidence
25
+ and the verification steps.
22
26
 
23
27
  See [CHANGELOG.md](CHANGELOG.md) for full history.
24
28
 
@@ -188,7 +192,7 @@ ai-toolkit/
188
192
  │ └── ARCHITECTURE.md # Full system design
189
193
  ├── kb/ # Reference docs, procedures, plans
190
194
  ├── scripts/ # Validation, install, evaluation scripts
191
- ├── tests/ # Bats and Python test suite (1640 tests)
195
+ ├── tests/ # Bats and Python test suite (1645 tests)
192
196
  └── CHANGELOG.md
193
197
  ```
194
198
 
@@ -3,7 +3,7 @@
3
3
  "name": "ai-toolkit",
4
4
  "displayName": "AI Toolkit",
5
5
  "description": "Professional-grade engineering skills, agents, rules, and lifecycle guardrails for Claude Code, Claude Chat, and Cowork.",
6
- "version": "4.26.0",
6
+ "version": "4.27.0",
7
7
  "author": {
8
8
  "name": "SoftSpark",
9
9
  "url": "https://github.com/softspark"
@@ -1,5 +1,5 @@
1
1
  {
2
- "last_run": "2026-08-21T08:34:52Z",
2
+ "last_run": "2026-08-21T15:52:27Z",
3
3
  "schema_version": 1,
4
4
  "tools": {
5
5
  "aider": {
@@ -66,7 +66,7 @@
66
66
  }
67
67
  },
68
68
  "claude-app": {
69
- "docs_hash": "7e06a1985d5bb822",
69
+ "docs_hash": "cacc8b579baa5e86",
70
70
  "headings": [
71
71
  "Add global and folder instructions",
72
72
  "Availability",
@@ -223,7 +223,7 @@
223
223
  }
224
224
  },
225
225
  "codex-cli": {
226
- "docs_hash": "fc1c7e43bec9f748",
226
+ "docs_hash": "df3b133739abfa24",
227
227
  "headings": [
228
228
  "API",
229
229
  "API Reference",
@@ -397,7 +397,7 @@
397
397
  }
398
398
  },
399
399
  "gemini-cli": {
400
- "docs_hash": "a2c9759e0296670d",
400
+ "docs_hash": "c66bb0647b70c7cf",
401
401
  "headings": [
402
402
  "Breadcrumbs",
403
403
  "Directory actions",
@@ -438,7 +438,7 @@
438
438
  "version": "0.55.1"
439
439
  },
440
440
  "github-copilot": {
441
- "docs_hash": "ab9b7f5b4b692e05",
441
+ "docs_hash": "2e595a058e906644",
442
442
  "headings": [
443
443
  "About Copilot auto model selection",
444
444
  "About Copilot automations",
@@ -475,7 +475,7 @@
475
475
  }
476
476
  },
477
477
  "google-antigravity": {
478
- "docs_hash": "9fe532d247c3e361",
478
+ "docs_hash": "8cbf9aaffb6b57bf",
479
479
  "headings": [],
480
480
  "markers": {
481
481
  "AGENTS.md": false,
@@ -502,7 +502,7 @@
502
502
  }
503
503
  },
504
504
  "opencode": {
505
- "docs_hash": "4287f7d767dda4a8",
505
+ "docs_hash": "3e9f45631e71a28c",
506
506
  "headings": [
507
507
  "Add features",
508
508
  "Ask questions",
@@ -564,7 +564,7 @@
564
564
  }
565
565
  },
566
566
  "windsurf": {
567
- "docs_hash": "60e25f8cb4c381be",
567
+ "docs_hash": "b08c4f883c276129",
568
568
  "headings": [
569
569
  "Accounts",
570
570
  "Advanced",
@@ -3,10 +3,12 @@ title: "Troubleshooting"
3
3
  service: ai-toolkit
4
4
  category: troubleshooting
5
5
  tags: [troubleshooting, debugging]
6
- last_updated: "2026-03-25"
7
- description: "Section index for ai-toolkit troubleshooting. No documents are filed here yet; new ones follow the troubleshooting template."
6
+ last_updated: "2026-08-21"
7
+ description: "Section index for ai-toolkit troubleshooting, covering the Claude app plugin double-load collision."
8
8
  ---
9
9
 
10
10
  # Troubleshooting
11
11
 
12
- Problem resolution guides. Guides will be added here as they are created.
12
+ Problem resolution guides.
13
+
14
+ - [Claude App Plugin Loads Twice in Claude Code](plugin-double-load.md) -- hooks fire twice after the plugin ZIP is uploaded next to a global install.
@@ -0,0 +1,97 @@
1
+ ---
2
+ title: "Claude App Plugin Loads Twice in Claude Code"
3
+ category: troubleshooting
4
+ service: ai-toolkit
5
+ tags: [plugin, claude-app, hooks, duplication, doctor]
6
+ created: "2026-08-21"
7
+ last_updated: "2026-08-21"
8
+ description: "Every toolkit hook fires twice and skills and agents load twice after the Claude app plugin ZIP is uploaded on a machine that already has the global install. Cause: the app registers the plugin under ~/.claude/plugins, which Claude Code also reads."
9
+ ---
10
+
11
+ # Claude App Plugin Loads Twice in Claude Code
12
+
13
+ ## Symptom
14
+
15
+ After uploading `ai-toolkit-claude-app.zip` through `Customize > Plugins`, Claude
16
+ Code sessions get slower and every hook side effect appears twice: duplicate rows
17
+ in `governance.log`, duplicate session state writes, two Stop gates per turn.
18
+ `ai-toolkit doctor` reports a healthy install because it only inspected
19
+ `~/.claude` and `app/plugins`.
20
+
21
+ ## Cause
22
+
23
+ The Claude app writes uploaded plugins into
24
+ `~/.claude/plugins/marketplaces/local-desktop-app-uploads/` and sets
25
+ `enabledPlugins` in `~/.claude/settings.json`. Both paths belong to Claude Code
26
+ as well, so Claude Code loads the plugin on top of the global install. The
27
+ bundle carries the same catalog as `~/.claude`, and plugin hooks merge with user
28
+ hooks without deduplication.
29
+
30
+ Confirmed in a Claude Code debug log (`claude --debug -p ...`, then read
31
+ `~/.claude/debug/latest`):
32
+
33
+ ```
34
+ Loaded 111 unique skills (... user: 111 ...)
35
+ Total plugin skills loaded: 111 (0 duplicate/user-owned entries skipped)
36
+ Total plugin agents loaded: 44
37
+ Read manifest hooks for plugin ai-toolkit (enabled=true): ./claude-app/hooks/hooks.json
38
+ ```
39
+
40
+ The `skills` manifest field adds to the default `skills/` directory instead of
41
+ replacing it, so the plugin contributes its 109 catalog skills plus the 2
42
+ app-only rule skills.
43
+
44
+ ## Diagnosis
45
+
46
+ ```bash
47
+ ai-toolkit doctor
48
+ ```
49
+
50
+ Check 11 reports the collision:
51
+
52
+ ```
53
+ ## 11. Plugin Double-Load
54
+ WARN: ai-toolkit@local-desktop-app-uploads is active next to the global install: 28 toolkit hooks fire twice per event and skills/agents load twice (run: ai-toolkit doctor --fix)
55
+ ```
56
+
57
+ To see the duplication directly, count hook invocations per source in a fresh
58
+ session log:
59
+
60
+ ```bash
61
+ grep -oE "[^\"' ]*hooks/[a-z0-9._-]+\.sh" ~/.claude/debug/latest | sort | uniq -c
62
+ ```
63
+
64
+ ## Fix
65
+
66
+ ```bash
67
+ ai-toolkit doctor --fix
68
+ ```
69
+
70
+ That sets the plugin to `false` in `enabledPlugins` and leaves the global
71
+ install authoritative. Claude Code then logs
72
+ `enabled=false; will NOT register, plugin is disabled`, and plugin skills and
73
+ agents drop to 0.
74
+
75
+ The global install is the richer surface for Claude Code: it delivers rules as
76
+ real files under `~/.claude/rules/`, which are always in context, while the
77
+ plugin exposes them as an `ai-toolkit-rules` skill the model has to load. The
78
+ plugin also has no `session-context.sh` hook.
79
+
80
+ Keep the plugin enabled only when Claude Code has no global install, for example
81
+ a machine that runs the Claude app alone. In that case the plugin is the single
82
+ source and check 11 stays quiet.
83
+
84
+ ## Verification
85
+
86
+ ```bash
87
+ claude --debug -p "ok"
88
+ grep -E "Total plugin (skills|agents) loaded|enabled=" ~/.claude/debug/latest
89
+ ```
90
+
91
+ Expect `Total plugin skills loaded: 0`, `Total plugin agents loaded: 0`, and the
92
+ `plugin is disabled` line.
93
+
94
+ ## Related
95
+
96
+ - `kb/reference/global-install-model.md`
97
+ - `kb/procedures/maintenance-sop.md`
package/llms-full.txt CHANGED
@@ -73,6 +73,7 @@
73
73
  - [Unique Features & Differentiators](kb/reference/unique-features.md)
74
74
  - [Windows Support](kb/reference/windows-support.md)
75
75
  - [Troubleshooting](kb/troubleshooting/README.md)
76
+ - [Claude App Plugin Loads Twice in Claude Code](kb/troubleshooting/plugin-double-load.md)
76
77
 
77
78
  ## Skills
78
79
 
@@ -16130,13 +16131,117 @@ title: "Troubleshooting"
16130
16131
  service: ai-toolkit
16131
16132
  category: troubleshooting
16132
16133
  tags: [troubleshooting, debugging]
16133
- last_updated: "2026-03-25"
16134
- description: "Section index for ai-toolkit troubleshooting. No documents are filed here yet; new ones follow the troubleshooting template."
16134
+ last_updated: "2026-08-21"
16135
+ description: "Section index for ai-toolkit troubleshooting, covering the Claude app plugin double-load collision."
16135
16136
  ---
16136
16137
 
16137
16138
  # Troubleshooting
16138
16139
 
16139
- Problem resolution guides. Guides will be added here as they are created.
16140
+ Problem resolution guides.
16141
+
16142
+ - [Claude App Plugin Loads Twice in Claude Code](plugin-double-load.md) -- hooks fire twice after the plugin ZIP is uploaded next to a global install.
16143
+
16144
+ ---
16145
+
16146
+ ## kb/troubleshooting/plugin-double-load.md
16147
+
16148
+ ---
16149
+ title: "Claude App Plugin Loads Twice in Claude Code"
16150
+ category: troubleshooting
16151
+ service: ai-toolkit
16152
+ tags: [plugin, claude-app, hooks, duplication, doctor]
16153
+ created: "2026-08-21"
16154
+ last_updated: "2026-08-21"
16155
+ description: "Every toolkit hook fires twice and skills and agents load twice after the Claude app plugin ZIP is uploaded on a machine that already has the global install. Cause: the app registers the plugin under ~/.claude/plugins, which Claude Code also reads."
16156
+ ---
16157
+
16158
+ # Claude App Plugin Loads Twice in Claude Code
16159
+
16160
+ ## Symptom
16161
+
16162
+ After uploading `ai-toolkit-claude-app.zip` through `Customize > Plugins`, Claude
16163
+ Code sessions get slower and every hook side effect appears twice: duplicate rows
16164
+ in `governance.log`, duplicate session state writes, two Stop gates per turn.
16165
+ `ai-toolkit doctor` reports a healthy install because it only inspected
16166
+ `~/.claude` and `app/plugins`.
16167
+
16168
+ ## Cause
16169
+
16170
+ The Claude app writes uploaded plugins into
16171
+ `~/.claude/plugins/marketplaces/local-desktop-app-uploads/` and sets
16172
+ `enabledPlugins` in `~/.claude/settings.json`. Both paths belong to Claude Code
16173
+ as well, so Claude Code loads the plugin on top of the global install. The
16174
+ bundle carries the same catalog as `~/.claude`, and plugin hooks merge with user
16175
+ hooks without deduplication.
16176
+
16177
+ Confirmed in a Claude Code debug log (`claude --debug -p ...`, then read
16178
+ `~/.claude/debug/latest`):
16179
+
16180
+ ```
16181
+ Loaded 111 unique skills (... user: 111 ...)
16182
+ Total plugin skills loaded: 111 (0 duplicate/user-owned entries skipped)
16183
+ Total plugin agents loaded: 44
16184
+ Read manifest hooks for plugin ai-toolkit (enabled=true): ./claude-app/hooks/hooks.json
16185
+ ```
16186
+
16187
+ The `skills` manifest field adds to the default `skills/` directory instead of
16188
+ replacing it, so the plugin contributes its 109 catalog skills plus the 2
16189
+ app-only rule skills.
16190
+
16191
+ ## Diagnosis
16192
+
16193
+ ```bash
16194
+ ai-toolkit doctor
16195
+ ```
16196
+
16197
+ Check 11 reports the collision:
16198
+
16199
+ ```
16200
+ ## 11. Plugin Double-Load
16201
+ WARN: ai-toolkit@local-desktop-app-uploads is active next to the global install: 28 toolkit hooks fire twice per event and skills/agents load twice (run: ai-toolkit doctor --fix)
16202
+ ```
16203
+
16204
+ To see the duplication directly, count hook invocations per source in a fresh
16205
+ session log:
16206
+
16207
+ ```bash
16208
+ grep -oE "[^\"' ]*hooks/[a-z0-9._-]+\.sh" ~/.claude/debug/latest | sort | uniq -c
16209
+ ```
16210
+
16211
+ ## Fix
16212
+
16213
+ ```bash
16214
+ ai-toolkit doctor --fix
16215
+ ```
16216
+
16217
+ That sets the plugin to `false` in `enabledPlugins` and leaves the global
16218
+ install authoritative. Claude Code then logs
16219
+ `enabled=false; will NOT register, plugin is disabled`, and plugin skills and
16220
+ agents drop to 0.
16221
+
16222
+ The global install is the richer surface for Claude Code: it delivers rules as
16223
+ real files under `~/.claude/rules/`, which are always in context, while the
16224
+ plugin exposes them as an `ai-toolkit-rules` skill the model has to load. The
16225
+ plugin also has no `session-context.sh` hook.
16226
+
16227
+ Keep the plugin enabled only when Claude Code has no global install, for example
16228
+ a machine that runs the Claude app alone. In that case the plugin is the single
16229
+ source and check 11 stays quiet.
16230
+
16231
+ ## Verification
16232
+
16233
+ ```bash
16234
+ claude --debug -p "ok"
16235
+ grep -E "Total plugin (skills|agents) loaded|enabled=" ~/.claude/debug/latest
16236
+ ```
16237
+
16238
+ Expect `Total plugin skills loaded: 0`, `Total plugin agents loaded: 0`, and the
16239
+ `plugin is disabled` line.
16240
+
16241
+ ## Related
16242
+
16243
+ - `kb/reference/global-install-model.md`
16244
+ - `kb/procedures/maintenance-sop.md`
16140
16245
 
16141
16246
  ---
16142
16247
 
package/llms.txt CHANGED
@@ -73,3 +73,4 @@
73
73
  - [Unique Features & Differentiators](kb/reference/unique-features.md)
74
74
  - [Windows Support](kb/reference/windows-support.md)
75
75
  - [Troubleshooting](kb/troubleshooting/README.md)
76
+ - [Claude App Plugin Loads Twice in Claude Code](kb/troubleshooting/plugin-double-load.md)
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.26.0",
2
+ "version": "4.27.0",
3
3
  "components": {
4
4
  "agents": {
5
5
  "description": "44 specialized agents (orchestrator, backend, frontend, security, devops, etc.)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softspark/ai-toolkit",
3
- "version": "4.26.0",
3
+ "version": "4.27.0",
4
4
  "description": "AI coding toolkit: 109 skills, 44 agents, 12 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",
package/scripts/doctor.py CHANGED
@@ -15,6 +15,8 @@ Checks:
15
15
  7. Benchmark freshness
16
16
  8. Stale rules
17
17
  9. URL hook sources
18
+ 10. Language rules drift (project-local)
19
+ 11. Plugin double-load (Claude app plugin vs global install)
18
20
 
19
21
  Exit codes:
20
22
  0 all checks pass
@@ -44,6 +46,7 @@ HOOKS_DIR = _HOOKS_DIR
44
46
  RULES_DIR = _RULES_DIR
45
47
  EXTERNAL_HOOKS_DIR = _EXTERNAL_HOOKS_DIR
46
48
  BENCHMARK_DASHBOARD = toolkit_dir / "benchmarks" / "ecosystem-dashboard.json"
49
+ PLUGIN_REGISTRY = CLAUDE_DIR / "plugins" / "installed_plugins.json"
47
50
 
48
51
  VALID_EVENTS = frozenset({
49
52
  "SessionStart", "Notification", "PreToolUse", "PostToolUse", "Stop",
@@ -668,6 +671,84 @@ def check_language_drift(dr: DiagResult) -> None:
668
671
  dr.warn(f"{lang} detected but {skill} not injected — run: ai-toolkit install --local --lang {lang}")
669
672
 
670
673
 
674
+ # ---------------------------------------------------------------------------
675
+ # Check 11: Plugin Double-Load
676
+ # ---------------------------------------------------------------------------
677
+
678
+ def _registered_toolkit_plugins(registry: dict) -> list[str]:
679
+ """Return ai-toolkit plugin keys from the Claude Code plugin registry."""
680
+ plugins = registry.get("plugins", {})
681
+ if not isinstance(plugins, dict):
682
+ return []
683
+ return [key for key in plugins if str(key).split("@", 1)[0] == "ai-toolkit"]
684
+
685
+
686
+ def check_plugin_double_load(dr: DiagResult, fix_mode: bool) -> None:
687
+ """Warn when the Claude app plugin and the global install both feed Claude Code.
688
+
689
+ Uploading the ``claude-app export`` ZIP from the Claude app registers it under
690
+ ``~/.claude/plugins``, which Claude Code reads as well. The plugin carries the
691
+ same skills, agents, and hooks as the global install, and Claude Code merges
692
+ plugin hooks with user hooks without deduplication, so every toolkit hook runs
693
+ twice per event.
694
+ """
695
+ print()
696
+ print("## 11. Plugin Double-Load")
697
+
698
+ if not PLUGIN_REGISTRY.is_file():
699
+ dr.skip("no Claude Code plugin registry")
700
+ return
701
+ try:
702
+ registry = json.loads(PLUGIN_REGISTRY.read_text(encoding="utf-8"))
703
+ except (OSError, json.JSONDecodeError):
704
+ dr.warn(f"{PLUGIN_REGISTRY} is not valid JSON")
705
+ return
706
+
707
+ keys = _registered_toolkit_plugins(registry)
708
+ if not keys:
709
+ dr.ok("no ai-toolkit plugin registered in Claude Code")
710
+ return
711
+
712
+ settings_json = CLAUDE_DIR / "settings.json"
713
+ try:
714
+ settings = json.loads(settings_json.read_text(encoding="utf-8"))
715
+ except (OSError, json.JSONDecodeError):
716
+ settings = {}
717
+ enabled = settings.get("enabledPlugins", {})
718
+ if not isinstance(enabled, dict):
719
+ enabled = {}
720
+
721
+ active = [key for key in keys if enabled.get(key, True)]
722
+ if not active:
723
+ dr.ok("ai-toolkit plugin registered but disabled for Claude Code")
724
+ return
725
+
726
+ hook_count, _ = _installed_toolkit_hook_count(settings)
727
+ if hook_count == 0:
728
+ dr.ok("ai-toolkit plugin active without global hooks (single source)")
729
+ return
730
+
731
+ for key in active:
732
+ dr.warn(
733
+ f"{key} is active next to the global install: {hook_count} toolkit hooks "
734
+ "fire twice per event and skills/agents load twice "
735
+ "(run: ai-toolkit doctor --fix)"
736
+ )
737
+
738
+ if not fix_mode:
739
+ return
740
+
741
+ enabled.update({key: False for key in active})
742
+ settings["enabledPlugins"] = enabled
743
+ try:
744
+ settings_json.write_text(json.dumps(settings, indent=2) + "\n", encoding="utf-8")
745
+ except OSError as exc:
746
+ dr.fail(f"could not disable plugin in settings.json: {exc}")
747
+ return
748
+ for key in active:
749
+ dr.fixed(f"disabled {key} for Claude Code (global install stays authoritative)")
750
+
751
+
671
752
  # ---------------------------------------------------------------------------
672
753
  # Main
673
754
  # ---------------------------------------------------------------------------
@@ -691,6 +772,7 @@ def main() -> None:
691
772
  check_stale_rules(dr, fix_mode)
692
773
  check_url_hooks(dr, fix_mode)
693
774
  check_language_drift(dr)
775
+ check_plugin_double_load(dr, fix_mode)
694
776
 
695
777
  # Summary
696
778
  print("========================")