@softspark/ai-toolkit 4.2.1 → 4.2.3

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,37 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v4.2.3 - SEO FAQ rich results deprecation (2026-05-12)
11
+
12
+ Patch release. Updates `seo-specialist` guidance after Google deprecated FAQ rich results.
13
+
14
+ ### Changed
15
+
16
+ - **SEO structured data guidance** - `seo-specialist` now treats `FAQPage` as deprecated for Google rich results and recommends keeping it only when explicitly targeting non-Google engines.
17
+ - **Citability schema checklist** - Prioritizes `Article`, `HowTo`, `QAPage`, and `Person` schema guidance for Google-facing SEO/GEO work.
18
+
19
+ ### Verification
20
+
21
+ - Source checked against [Google Search Central `FAQPage` structured data documentation](https://developers.google.com/search/docs/appearance/structured-data/faqpage).
22
+
23
+ ---
24
+
25
+ ## v4.2.2 - Legacy hook duplicate cleanup (2026-05-12)
26
+
27
+ Patch release. Cleans up legacy untagged hook duplicates found during post-release verification of v4.2.1.
28
+
29
+ ### Fixed
30
+
31
+ - **Legacy notification hook cleanup** - `scripts/merge-hooks.py` now removes old direct `osascript` notification hooks from pre-script installs while preserving user-owned notification hooks.
32
+ - **External hook duplicate cleanup** - `scripts/inject_hook_cli.py` now removes legacy untagged entries matching the same event, matcher, and handler payload as the external hook being re-injected.
33
+
34
+ ### Verification
35
+
36
+ - `bats tests/test_merge_hooks_statusline.bats tests/test_inject_hook.bats`: 42/42 passing.
37
+ - Local `~/.claude/settings.json`: 0 behavior duplicates, 0 exact duplicates, 0 untagged hook groups after cleanup.
38
+
39
+ ---
40
+
10
41
  ## v4.2.1 - Claude Code hook enforcement and README cleanup (2026-05-12)
11
42
 
12
43
  Patch release. Tightens Claude Code runtime enforcement, fixes duplicated hook cleanup during install/update, and removes the README hero image for release.
package/README.md CHANGED
@@ -6,16 +6,15 @@
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
7
  [![Skills](https://img.shields.io/badge/skills-107-brightgreen)](app/skills/)
8
8
  [![Agents](https://img.shields.io/badge/agents-44-blue)](app/agents/)
9
- [![Tests](https://img.shields.io/badge/tests-1049%20passing-success)](tests/)
9
+ [![Tests](https://img.shields.io/badge/tests-1051%20passing-success)](tests/)
10
10
 
11
- ## What's New in v4.2.1
11
+ ## What's New in v4.2.3
12
12
 
13
- Patch release focused on making Claude Code rule enforcement harder to bypass and cleaning the release-facing README.
13
+ Patch release updating SEO guidance after Google's FAQ rich result deprecation.
14
14
 
15
- - **Stop quality gate**: `quality-gate.sh` now runs on `Stop` as well as `TaskCompleted`, blocking final responses when lint/type checks fail.
16
- - **Reliable hook failures**: shell pipelines in the quality gate now preserve the failing tool exit code instead of hiding errors behind `head`.
17
- - **Cleaner installs**: hook merging now strips legacy untagged ai-toolkit duplicates while preserving user-owned hooks.
18
- - **README cleanup**: removed the hero image block for a lighter release README.
15
+ - **SEO agent guidance**: `seo-specialist` now treats `FAQPage` as deprecated for Google rich results.
16
+ - **Schema recommendations**: citability guidance now prioritizes `Article`, `HowTo`, `QAPage`, and `Person` for Google-facing surfaces.
17
+ - **GEO audit checklist**: FAQ markup is now scoped to non-Google engines instead of recommended as a default.
19
18
 
20
19
  See [CHANGELOG.md](CHANGELOG.md) for full history.
21
20
 
@@ -148,7 +147,7 @@ ai-toolkit/
148
147
  │ └── ARCHITECTURE.md # Full system design
149
148
  ├── kb/ # Reference docs, procedures, plans
150
149
  ├── scripts/ # Validation, install, evaluation scripts
151
- ├── tests/ # Bats test suite (1049 tests)
150
+ ├── tests/ # Bats test suite (1051 tests)
152
151
  └── CHANGELOG.md
153
152
  ```
154
153
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ai-toolkit",
3
3
  "description": "Professional-grade Claude Code toolkit with persona presets, skill security auditor, expanded lifecycle hooks, experimental opt-in plugin packs, benchmark harvesting, and multi-tool support.",
4
- "version": "4.2.1",
4
+ "version": "4.2.3",
5
5
  "author": {
6
6
  "name": "SoftSpark",
7
7
  "url": "https://github.com/softspark"
@@ -39,7 +39,7 @@ Optimization for both classical search engines AND generative engines (ChatGPT,
39
39
 
40
40
  ### Structured Data
41
41
  - Schema.org markup
42
- - Rich snippets
42
+ - Rich snippets (note: Google dropped FAQ rich results on May 7, 2026; Search Console API support removed August 2026)
43
43
  - Knowledge graph
44
44
  - Breadcrumbs
45
45
 
@@ -157,7 +157,7 @@ Goal: get cited by ChatGPT, Claude, Perplexity, Gemini, and Google AI Overviews
157
157
  5. **Named author + author bio with credentials.** Authority signal that survives synthesis.
158
158
  6. **Original quotes from named experts.** Quotation blocks are over-represented in LLM citations.
159
159
  7. **Comparison tables with explicit labels.** Tables are extracted whole; vague prose is summarised away.
160
- 8. **Schema.org `Article`, `FAQPage`, `HowTo`, `QAPage`, `Person`.** Mandatory for entity disambiguation.
160
+ 8. **Schema.org `Article`, `HowTo`, `QAPage`, `Person`.** Mandatory for entity disambiguation. `FAQPage` schema: Google dropped FAQ rich results on **May 7, 2026** (Search Console API removed August 2026) — keep markup only if targeting other engines (Bing, etc.).
161
161
  9. **`llms.txt` at site root.** Curated index of canonical pages for LLM crawlers (Anthropic-proposed convention).
162
162
  10. **Stable URLs with semantic slugs.** LLMs cache citations — URL changes erase your authority overnight.
163
163
  11. **Cross-linking with descriptive anchors.** "this article" is dead anchor text; use the actual claim.
@@ -189,7 +189,7 @@ Goal: get cited by ChatGPT, Claude, Perplexity, Gemini, and Google AI Overviews
189
189
  - [ ] At least one comparison table OR numbered list per long-form page
190
190
  - [ ] Author block with real name (not "Admin"), role, and ≥30 words of bio
191
191
  - [ ] Schema.org `Article` with `author` (`Person` + `sameAs`), `datePublished`, `dateModified`
192
- - [ ] Schema.org `FAQPage` for any page with Q/A structure
192
+ - [ ] ~~Schema.org `FAQPage`~~ Google dropped FAQ rich results on **May 7, 2026**; Search Console reporting ends June 2026, API removed August 2026. Remove or keep only if targeting non-Google engines.
193
193
  - [ ] `dateModified` updated AND visible "Updated: [date]" text present
194
194
  - [ ] `llms.txt` at site root with curated canonical URLs
195
195
  - [ ] Page contains at least 3 unique data points (numbers, dates, version strings)
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.2.1",
2
+ "version": "4.2.3",
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.2.1",
3
+ "version": "4.2.3",
4
4
  "description": "AI coding toolkit: 107 skills, 44 agents, 12-editor write-through (Claude, Cursor, Windsurf, Copilot, Gemini, Cline, Roo, Aider, Augment, Antigravity, Codex, opencode), machine-enforced safety constitution, SARIF audit, signed npm provenance.",
5
5
  "keywords": [
6
6
  "claude",
@@ -120,20 +120,56 @@ def _entry_source(entry: dict) -> str | None:
120
120
  return None
121
121
 
122
122
 
123
- def strip_source(hooks: dict, source: str) -> dict:
123
+ def _entry_signature(entry: dict) -> tuple:
124
+ """Return behavior-defining hook fields without source tags."""
125
+ handlers = []
126
+ for hook in entry.get("hooks", []):
127
+ if not isinstance(hook, dict):
128
+ handlers.append(hook)
129
+ continue
130
+ handlers.append(tuple(sorted(
131
+ (key, value)
132
+ for key, value in hook.items()
133
+ if key != "_source"
134
+ )))
135
+ return (entry.get("matcher", ""), tuple(handlers))
136
+
137
+
138
+ def strip_source(hooks: dict, source: str, replacement_hooks: dict | None = None) -> dict:
124
139
  """Remove all entries whose ``_source`` matches *source*.
125
140
 
126
141
  Args:
127
142
  hooks: Existing hooks dict (event-name -> list of entries).
128
143
  source: Source tag to strip.
144
+ replacement_hooks: Optional hook entries being re-injected. When
145
+ provided, untagged legacy entries with the same event/matcher/
146
+ handler payload are stripped as belonging to the same source.
129
147
 
130
148
  Returns:
131
149
  New hooks dict with matching entries removed. Empty event lists
132
150
  are omitted.
133
151
  """
152
+ legacy_signatures: dict[str, set[tuple]] = {}
153
+ if replacement_hooks:
154
+ for event, entries in replacement_hooks.items():
155
+ legacy_signatures[event] = {
156
+ _entry_signature(entry)
157
+ for entry in entries
158
+ if isinstance(entry, dict)
159
+ }
160
+
134
161
  result: dict = {}
135
162
  for event, entries in hooks.items():
136
- filtered = [e for e in entries if _entry_source(e) != source]
163
+ signatures = legacy_signatures.get(event, set())
164
+ filtered = [
165
+ e for e in entries
166
+ if _entry_source(e) != source
167
+ and not (
168
+ isinstance(e, dict)
169
+ and _entry_source(e) is None
170
+ and _entry_signature(e) in signatures
171
+ )
172
+ ]
137
173
  if filtered:
138
174
  result[event] = filtered
139
175
  return result
@@ -174,7 +210,7 @@ def merge_hooks(new_hooks: dict, existing_hooks: dict, source: str) -> dict:
174
210
  Returns:
175
211
  Merged hooks dict.
176
212
  """
177
- merged = strip_source(existing_hooks, source)
213
+ merged = strip_source(existing_hooks, source, new_hooks)
178
214
  for event, entries in new_hooks.items():
179
215
  if event not in merged:
180
216
  merged[event] = []
@@ -27,6 +27,23 @@ import os
27
27
  import sys
28
28
 
29
29
  SOURCE_TAG = "ai-toolkit"
30
+ LEGACY_TOOLKIT_HOOKS = {
31
+ "Notification": [
32
+ {
33
+ "matcher": "",
34
+ "hooks": [
35
+ {
36
+ "type": "command",
37
+ "command": (
38
+ "osascript -e 'display notification "
39
+ '"Claude Code needs your attention" with title "Claude Code"'
40
+ "'"
41
+ ),
42
+ }
43
+ ],
44
+ }
45
+ ],
46
+ }
30
47
 
31
48
 
32
49
  def load_json(path: str) -> dict:
@@ -95,6 +112,12 @@ def strip_toolkit(hooks: dict, toolkit_hooks: dict | None = None) -> dict:
95
112
  for entry in entries
96
113
  if isinstance(entry, dict)
97
114
  }
115
+ for event, entries in LEGACY_TOOLKIT_HOOKS.items():
116
+ legacy_signatures.setdefault(event, set()).update(
117
+ _entry_signature(entry)
118
+ for entry in entries
119
+ if isinstance(entry, dict)
120
+ )
98
121
 
99
122
  result = {}
100
123
  for event, entries in hooks.items():