@softspark/ai-toolkit 2.2.0 → 2.3.1

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/AGENTS.md CHANGED
@@ -602,7 +602,7 @@ Tools: `sync_tasks`, `read_cached_tasks`, `update_task_status`, `update_task`, `
602
602
  3. `get_task_details(task_key="PROJ-123")` -- deep dive (description + comments as markdown)
603
603
  4. `update_task_status(...)` / `add_task_comment(...)` / `log_task_time(...)` -- mutate
604
604
 
605
- ## Comment Templates (8 built-in)
605
+ ## Comment Templates (built-in)
606
606
 
607
607
  | ID | Use for |
608
608
  |----|---------|
package/CHANGELOG.md CHANGED
@@ -7,6 +7,26 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v2.3.1 — Release Quality Gate (2026-04-14)
11
+
12
+ ### Fixed
13
+ - **README "What's New" section** — was stuck at v2.1.3, now auto-validated by `validate.py --strict`
14
+ - **Release SOP** — added mandatory "Update README What's New" step to Phase 3 checklist
15
+
16
+ ---
17
+
18
+ ## v2.3.0 — Jira MCP Template & Cross-Editor Sync (2026-04-14)
19
+
20
+ ### Added
21
+ - **Jira MCP template** — `ai-toolkit mcp add jira` installs `@softspark/jira-mcp` via global binary. Templates now support `postInstall` field for first-time setup hints shown after `mcp add`, `mcp install`, and `mcp show`. MCP template count: 25 → 26.
22
+ - **MCP template tracking** — globally installed templates are recorded in `state.json`. `ai-toolkit update` automatically syncs tracked templates to Claude global config. `ai-toolkit status` shows tracked MCP templates. "Install once, sync everywhere."
23
+
24
+ ### Fixed
25
+ - **Claude MCP config paths** — corrected to `~/.claude.json` (global) and `.mcp.json` (project) per official Claude Code docs. Previously wrote to `~/.claude/settings.json` and `.claude/settings.local.json`.
26
+ - **Jira MCP template uses global binary** — `jira-mcp` instead of `npx -y` for faster startup and offline support
27
+
28
+ ---
29
+
10
30
  ## v2.2.0 — URL Rules & Registry Safety (2026-04-14)
11
31
 
12
32
  ### Added
package/README.md CHANGED
@@ -6,16 +6,17 @@
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
7
  [![Skills](https://img.shields.io/badge/skills-92-brightgreen)](app/skills/)
8
8
  [![Agents](https://img.shields.io/badge/agents-44-blue)](app/agents/)
9
- [![Tests](https://img.shields.io/badge/tests-604%20passing-success)](tests/)
9
+ [![Tests](https://img.shields.io/badge/tests-606%20passing-success)](tests/)
10
10
 
11
11
  ---
12
12
 
13
- ## What's New in v2.1.3
13
+ ## What's New in v2.3.1
14
14
 
15
- - **Idempotent update** — `ai-toolkit update` no longer dirties git with extra blank lines in generated files
16
- - **Custom rules in generators** — `generate:all` preserves registered rules from other repos across all platforms
17
- - **README restructured** — 951 292 lines with TOC, links to KB docs
18
- - **3 new KB docs** — CLI Reference, Unique Features, Ecosystem Comparison
15
+ - **URL rule registration** — `ai-toolkit add-rule https://...` with auto-refresh on every update
16
+ - **Jira MCP template** — `ai-toolkit mcp add jira` with `postInstall` setup hints
17
+ - **MCP template tracking** — globally installed templates saved in `state.json`, auto-synced on `update`
18
+ - **Race condition fix** — parallel project updates no longer drop registry entries (fcntl.flock + atomic writes)
19
+ - **Claude MCP paths fixed** — now uses `~/.claude.json` (global) and `.mcp.json` (project) per official docs
19
20
 
20
21
  See [CHANGELOG.md](CHANGELOG.md) for full history.
21
22
 
@@ -142,7 +143,7 @@ ai-toolkit/
142
143
  │ └── ARCHITECTURE.md # Full system design
143
144
  ├── kb/ # Reference docs, procedures, plans
144
145
  ├── scripts/ # Validation, install, evaluation scripts
145
- ├── tests/ # Bats test suite (604 tests)
146
+ ├── tests/ # Bats test suite (606 tests)
146
147
  └── CHANGELOG.md
147
148
  ```
148
149
 
@@ -174,7 +175,7 @@ ai-toolkit/
174
175
 
175
176
  **68 language rules** — 13 languages, 5 categories each. Auto-detected or explicit `--lang`. See [Language Rules](kb/reference/language-rules.md).
176
177
 
177
- **25 MCP templates** — Ready-to-use configs for GitHub, PostgreSQL, Slack, Sentry, and more. See [MCP Templates](kb/reference/mcp-templates.md).
178
+ **26 MCP templates** — Ready-to-use configs for GitHub, PostgreSQL, Slack, Jira, Sentry, and more. See [MCP Templates](kb/reference/mcp-templates.md).
178
179
 
179
180
  See [Unique Features](kb/reference/unique-features.md) for detailed descriptions of all differentiators.
180
181
 
@@ -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": "2.2.0",
4
+ "version": "2.3.1",
5
5
  "author": {
6
6
  "name": "SoftSpark",
7
7
  "url": "https://github.com/softspark"
@@ -302,7 +302,7 @@ Lead Session (You)
302
302
  ## Extension Points
303
303
 
304
304
  ### MCP Templates (25)
305
- `app/plugins/mcp-templates/` ships 25 ready-to-use MCP server config templates (filesystems, databases, GitHub, Slack, etc.). Opt-in via `ai-toolkit install --modules mcp-templates` or activated automatically with `--profile strict|full`.
305
+ `app/plugins/mcp-templates/` ships 26 ready-to-use MCP server config templates (filesystems, databases, GitHub, Slack, etc.). Opt-in via `ai-toolkit install --modules mcp-templates` or activated automatically with `--profile strict|full`.
306
306
 
307
307
  ### Language Rules (68 files, 13 languages)
308
308
  `app/rules/` contains per-language coding rules. Supported languages: TypeScript, Python, Go, Rust, Java, Kotlin, Swift, Dart, C#, PHP, C++, Ruby, and common (shared). Auto-detected from project files via `--auto-detect` or selected with `--modules rules-<lang>`.
@@ -77,6 +77,7 @@ Example -- adding GitHub to `.mcp.json`:
77
77
  | `vercel` | Vercel deployments and settings |
78
78
  | `datadog` | Datadog monitoring and metrics |
79
79
  | `grafana` | Grafana dashboards and alerting |
80
+ | `jira` | Jira multi-instance routing, ADF, caching, comment templates |
80
81
  | `custom-template` | Empty template for custom servers |
81
82
 
82
83
  ## Contributing a new template
@@ -100,6 +101,7 @@ Example -- adding GitHub to `.mcp.json`:
100
101
  ```
101
102
 
102
103
  2. Use `${ENV_VAR}` syntax for any secrets or tokens
103
- 3. Keep the `name` field matching the filename (without `.json`)
104
- 4. Update this README table
105
- 5. Run `python3 scripts/validate.py` to verify
104
+ 3. Optional: add `"postInstall": "setup instructions"` for first-time config hints
105
+ 4. Keep the `name` field matching the filename (without `.json`)
106
+ 5. Update this README table
107
+ 6. Run `python3 scripts/validate.py` to verify
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "jira",
3
+ "description": "Jira integration — multi-instance routing, ADF formatting, task caching, and comment templates",
4
+ "mcpServers": {
5
+ "jira": {
6
+ "command": "jira-mcp",
7
+ "args": []
8
+ }
9
+ },
10
+ "postInstall": "Run 'npm install -g @softspark/jira-mcp && jira-mcp config init' to install and set up credentials."
11
+ }
@@ -157,6 +157,18 @@ Add entry at the top of `CHANGELOG.md` (after the header, before previous releas
157
157
  - Date format: `YYYY-MM-DD`
158
158
  - Title: short, descriptive, no version number repetition
159
159
 
160
+ ### Update README "What's New" section
161
+
162
+ **MANDATORY on every release.** Update the `## What's New in vX.Y.Z` section in `README.md`:
163
+
164
+ 1. Change the heading version: `## What's New in vX.Y.Z`
165
+ 2. Replace bullet points with 3-5 highlights from this release
166
+ 3. Keep the `See [CHANGELOG.md](CHANGELOG.md) for full history.` link
167
+
168
+ > **Warning:** This section is the first thing users see after the badges.
169
+ > A stale version here (e.g., "What's New in v2.1.3" when shipping v2.3.0)
170
+ > signals an unmaintained project. Do NOT skip this step.
171
+
160
172
  ---
161
173
 
162
174
  ## Phase 4: Regenerate Artifacts
@@ -335,7 +335,7 @@ Severity levels: HIGH (blocks deployment), WARN (should fix), INFO (best practic
335
335
  ## Extension Points
336
336
 
337
337
  ### MCP Templates
338
- `app/plugins/mcp-templates/` contains 25 ready-to-use MCP server config templates. Opt-in via `ai-toolkit install --modules mcp-templates` or activated automatically with `--profile strict|full`.
338
+ `app/plugins/mcp-templates/` contains 26 ready-to-use MCP server config templates. Opt-in via `ai-toolkit install --modules mcp-templates` or activated automatically with `--profile strict|full`.
339
339
 
340
340
  ### Language Rules
341
341
  `app/rules/` provides language-specific rule files covering 13 languages (TypeScript, Python, Go, Rust, Java, Kotlin, Swift, Dart, C#, PHP, C++, Ruby, common). Auto-detected from project files via `--auto-detect` or selectable with `--modules rules-<lang>`. See README.md for current count.
@@ -43,7 +43,7 @@ Usage: ai-toolkit <command> [options]
43
43
 
44
44
  | Command | Description |
45
45
  |---------|-------------|
46
- | `mcp list` | List available MCP server templates (25 templates) |
46
+ | `mcp list` | List available MCP server templates (26 templates) |
47
47
  | `mcp editors` | List editors with native MCP config adapters and scopes |
48
48
  | `mcp add <name> [names...]` | Add MCP server template(s) to `.mcp.json` |
49
49
  | `mcp install --editor <name[,..]> [names...]` | Install templates into native editor MCP config |
@@ -39,7 +39,7 @@ Modules are defined in `manifest.json` at the repository root. There are 17 modu
39
39
  | `rules-php` | PHP-specific rules (5 files) | auto-detect |
40
40
  | `rules-cpp` | C++-specific rules (5 files) | auto-detect |
41
41
  | `rules-ruby` | Ruby-specific rules (5 files) | auto-detect |
42
- | `mcp-templates` | 25 MCP server config templates | strict, full |
42
+ | `mcp-templates` | 26 MCP server config templates | strict, full |
43
43
 
44
44
  ## Profiles
45
45
 
@@ -6,14 +6,14 @@ tags: [mcp, templates, servers, configuration, editors]
6
6
  version: "1.1.0"
7
7
  created: "2026-04-07"
8
8
  last_updated: "2026-04-12"
9
- description: "Reference for 25 MCP server templates plus native editor MCP installation support."
9
+ description: "Reference for 26 MCP server templates plus native editor MCP installation support."
10
10
  ---
11
11
 
12
12
  # MCP Server Templates
13
13
 
14
14
  ## Overview
15
15
 
16
- ai-toolkit ships 25 ready-to-use MCP server configuration templates in `app/mcp-templates/`. Each template is a JSON file that defines the canonical `mcpServers` block for a specific service. Templates can be merged into the project's `.mcp.json` and rendered into editor-native MCP config files via the `ai-toolkit mcp` CLI subcommand.
16
+ ai-toolkit ships 26 ready-to-use MCP server configuration templates in `app/mcp-templates/`. Each template is a JSON file that defines the canonical `mcpServers` block for a specific service. Templates can be merged into the project's `.mcp.json` and rendered into editor-native MCP config files via the `ai-toolkit mcp` CLI subcommand.
17
17
 
18
18
  ## CLI
19
19
 
package/llms-full.txt CHANGED
@@ -3669,6 +3669,18 @@ Add entry at the top of `CHANGELOG.md` (after the header, before previous releas
3669
3669
  - Date format: `YYYY-MM-DD`
3670
3670
  - Title: short, descriptive, no version number repetition
3671
3671
 
3672
+ ### Update README "What's New" section
3673
+
3674
+ **MANDATORY on every release.** Update the `## What's New in vX.Y.Z` section in `README.md`:
3675
+
3676
+ 1. Change the heading version: `## What's New in vX.Y.Z`
3677
+ 2. Replace bullet points with 3-5 highlights from this release
3678
+ 3. Keep the `See [CHANGELOG.md](CHANGELOG.md) for full history.` link
3679
+
3680
+ > **Warning:** This section is the first thing users see after the badges.
3681
+ > A stale version here (e.g., "What's New in v2.1.3" when shipping v2.3.0)
3682
+ > signals an unmaintained project. Do NOT skip this step.
3683
+
3672
3684
  ---
3673
3685
 
3674
3686
  ## Phase 4: Regenerate Artifacts
@@ -4774,7 +4786,7 @@ Severity levels: HIGH (blocks deployment), WARN (should fix), INFO (best practic
4774
4786
  ## Extension Points
4775
4787
 
4776
4788
  ### MCP Templates
4777
- `app/plugins/mcp-templates/` contains 25 ready-to-use MCP server config templates. Opt-in via `ai-toolkit install --modules mcp-templates` or activated automatically with `--profile strict|full`.
4789
+ `app/plugins/mcp-templates/` contains 26 ready-to-use MCP server config templates. Opt-in via `ai-toolkit install --modules mcp-templates` or activated automatically with `--profile strict|full`.
4778
4790
 
4779
4791
  ### Language Rules
4780
4792
  `app/rules/` provides language-specific rule files covering 13 languages (TypeScript, Python, Go, Rust, Java, Kotlin, Swift, Dart, C#, PHP, C++, Ruby, common). Auto-detected from project files via `--auto-detect` or selectable with `--modules rules-<lang>`. See README.md for current count.
@@ -5164,7 +5176,7 @@ Usage: ai-toolkit <command> [options]
5164
5176
 
5165
5177
  | Command | Description |
5166
5178
  |---------|-------------|
5167
- | `mcp list` | List available MCP server templates (25 templates) |
5179
+ | `mcp list` | List available MCP server templates (26 templates) |
5168
5180
  | `mcp editors` | List editors with native MCP config adapters and scopes |
5169
5181
  | `mcp add <name> [names...]` | Add MCP server template(s) to `.mcp.json` |
5170
5182
  | `mcp install --editor <name[,..]> [names...]` | Install templates into native editor MCP config |
@@ -7814,7 +7826,7 @@ Modules are defined in `manifest.json` at the repository root. There are 17 modu
7814
7826
  | `rules-php` | PHP-specific rules (5 files) | auto-detect |
7815
7827
  | `rules-cpp` | C++-specific rules (5 files) | auto-detect |
7816
7828
  | `rules-ruby` | Ruby-specific rules (5 files) | auto-detect |
7817
- | `mcp-templates` | 25 MCP server config templates | strict, full |
7829
+ | `mcp-templates` | 26 MCP server config templates | strict, full |
7818
7830
 
7819
7831
  ## Profiles
7820
7832
 
@@ -8018,14 +8030,14 @@ tags: [mcp, templates, servers, configuration, editors]
8018
8030
  version: "1.1.0"
8019
8031
  created: "2026-04-07"
8020
8032
  last_updated: "2026-04-12"
8021
- description: "Reference for 25 MCP server templates plus native editor MCP installation support."
8033
+ description: "Reference for 26 MCP server templates plus native editor MCP installation support."
8022
8034
  ---
8023
8035
 
8024
8036
  # MCP Server Templates
8025
8037
 
8026
8038
  ## Overview
8027
8039
 
8028
- ai-toolkit ships 25 ready-to-use MCP server configuration templates in `app/mcp-templates/`. Each template is a JSON file that defines the canonical `mcpServers` block for a specific service. Templates can be merged into the project's `.mcp.json` and rendered into editor-native MCP config files via the `ai-toolkit mcp` CLI subcommand.
8040
+ ai-toolkit ships 26 ready-to-use MCP server configuration templates in `app/mcp-templates/`. Each template is a JSON file that defines the canonical `mcpServers` block for a specific service. Templates can be merged into the project's `.mcp.json` and rendered into editor-native MCP config files via the `ai-toolkit mcp` CLI subcommand.
8029
8041
 
8030
8042
  ## CLI
8031
8043
 
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.2.0",
2
+ "version": "2.3.1",
3
3
  "components": {
4
4
  "agents": {
5
5
  "description": "44 specialized agents (orchestrator, backend, frontend, security, devops, etc.)",
@@ -146,7 +146,7 @@
146
146
  "auto_detect": ["Gemfile", "*.gemspec"]
147
147
  },
148
148
  "mcp-templates": {
149
- "description": "25 MCP server config templates",
149
+ "description": "26 MCP server config templates",
150
150
  "default": false
151
151
  }
152
152
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softspark/ai-toolkit",
3
- "version": "2.2.0",
3
+ "version": "2.3.1",
4
4
  "description": "Professional-grade AI coding toolkit: 92 skills, 44 agents, multi-platform support (Claude, Cursor, Windsurf, Copilot, Gemini, Cline, Roo Code, Aider, Augment, Google Antigravity, Codex CLI), machine-enforced safety constitution, persona presets, skill security auditor, expanded lifecycle hooks, 11 plugin packs, and benchmark tooling.",
5
5
  "keywords": [
6
6
  "claude",
@@ -432,6 +432,38 @@ def install_claude_code(target_dir: Path, hooks_scripts_dir: Path,
432
432
  inject_rules(claude_dir, target_dir, rules_dir, only, skip, dry_run,
433
433
  refresh_urls=True)
434
434
 
435
+ _sync_mcp_templates(dry_run)
436
+
437
+
438
+ def _sync_mcp_templates(dry_run: bool) -> None:
439
+ """Re-install tracked MCP templates into Claude global config."""
440
+ from install_steps.install_state import get_mcp_templates
441
+
442
+ templates = get_mcp_templates()
443
+ if not templates:
444
+ return
445
+
446
+ if dry_run:
447
+ print(f" Would sync MCP templates: {', '.join(templates)}")
448
+ return
449
+
450
+ from mcp_editors import install_servers
451
+
452
+ servers: dict = {}
453
+ for name in templates:
454
+ try:
455
+ tpl_path = app_dir / "mcp-templates" / f"{name}.json"
456
+ if tpl_path.is_file():
457
+ import json as _json
458
+ data = _json.loads(tpl_path.read_text(encoding="utf-8"))
459
+ servers.update(data.get("mcpServers", {}))
460
+ except Exception:
461
+ pass # Skip broken templates silently
462
+
463
+ if servers:
464
+ install_servers(["claude"], servers, scope="global")
465
+ print(f" MCP synced: {', '.join(templates)}")
466
+
435
467
 
436
468
  VALID_PERSONAS = ("backend-lead", "frontend-lead", "devops-eng", "junior-dev")
437
469
 
@@ -60,6 +60,31 @@ def get_installed_profile() -> str:
60
60
  return state.get("profile", "")
61
61
 
62
62
 
63
+ def get_mcp_templates() -> list[str]:
64
+ """Return list of globally tracked MCP template names."""
65
+ state = load_state()
66
+ templates = state.get("mcp_templates", [])
67
+ return templates if isinstance(templates, list) else []
68
+
69
+
70
+ def record_mcp_template(name: str) -> None:
71
+ """Add a template name to the tracked set in state.json."""
72
+ state = load_state()
73
+ templates = set(state.get("mcp_templates", []))
74
+ templates.add(name)
75
+ state["mcp_templates"] = sorted(templates)
76
+ save_state(state)
77
+
78
+
79
+ def remove_mcp_template(name: str) -> None:
80
+ """Remove a template name from the tracked set in state.json."""
81
+ state = load_state()
82
+ templates = set(state.get("mcp_templates", []))
83
+ templates.discard(name)
84
+ state["mcp_templates"] = sorted(templates)
85
+ save_state(state)
86
+
87
+
63
88
  def _now_iso() -> str:
64
89
  """Return current UTC time in ISO 8601 format."""
65
90
  return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
@@ -138,6 +163,10 @@ def print_status() -> None:
138
163
  langs = [m.replace("rules-", "") for m in detected]
139
164
  print(f" Detected: {', '.join(langs)}")
140
165
 
166
+ mcp = state.get("mcp_templates", [])
167
+ if mcp:
168
+ print(f" MCP: {', '.join(mcp)}")
169
+
141
170
  extends = state.get("extends")
142
171
  if extends:
143
172
  print(f" Extends: {extends.get('source', 'unknown')}")
@@ -15,8 +15,8 @@ except ModuleNotFoundError: # pragma: no cover - Python 3.11+ should have tomll
15
15
  EDITOR_SPECS: dict[str, dict[str, str | None]] = {
16
16
  "claude": {
17
17
  "label": "Claude Code",
18
- "project_path": ".claude/settings.local.json",
19
- "global_path": ".claude/settings.json",
18
+ "project_path": ".mcp.json",
19
+ "global_path": ".claude.json",
20
20
  "format": "json",
21
21
  "doc_scope": "project + global",
22
22
  },
@@ -130,6 +130,11 @@ def cmd_show(name: str) -> None:
130
130
  for key, val in env_vars:
131
131
  print(f" {key} = {val}")
132
132
 
133
+ post_install = data.get("postInstall")
134
+ if post_install:
135
+ print()
136
+ print(f"Setup: {post_install}")
137
+
133
138
 
134
139
  def cmd_add(names: list[str], target_dir: Path) -> None:
135
140
  """Add one or more MCP templates to .mcp.json."""
@@ -155,6 +160,13 @@ def cmd_add(names: list[str], target_dir: Path) -> None:
155
160
  write_mcp_config(target_dir, config)
156
161
  print(f"Added: {', '.join(added)}")
157
162
 
163
+ # Show postInstall hints from added templates
164
+ for name in names:
165
+ data = load_template(name)
166
+ post_install = data.get("postInstall")
167
+ if post_install:
168
+ print(f"\n Note ({name}): {post_install}")
169
+
158
170
 
159
171
  def cmd_install(
160
172
  names: list[str],
@@ -203,6 +215,20 @@ def cmd_install(
203
215
  for path in updated:
204
216
  print(f"Updated: {path}")
205
217
 
218
+ # Track globally installed templates in state.json
219
+ if eff_scope == "global" and names:
220
+ from install_steps.install_state import record_mcp_template
221
+ for name in names:
222
+ record_mcp_template(name)
223
+
224
+ # Show postInstall hints from installed templates
225
+ if names:
226
+ for name in names:
227
+ data = load_template(name)
228
+ post_install = data.get("postInstall")
229
+ if post_install:
230
+ print(f"\n Note ({name}): {post_install}")
231
+
206
232
 
207
233
  def cmd_remove(name: str, target_dir: Path | None, *, editors: list[str], scope: str | None) -> None:
208
234
  """Remove an MCP server from .mcp.json."""
@@ -223,6 +249,9 @@ def cmd_remove(name: str, target_dir: Path | None, *, editors: list[str], scope:
223
249
  )
224
250
  else:
225
251
  updated = remove_servers(editors, [name], scope="global")
252
+ # Untrack globally removed template from state.json
253
+ from install_steps.install_state import remove_mcp_template
254
+ remove_mcp_template(name)
226
255
  for path in updated:
227
256
  print(f"Updated: {path}")
228
257
  print(f"Removed: {name}")
@@ -646,6 +646,23 @@ def _validate_version_sync(tk_dir: Path, vr: ValidationResult) -> None:
646
646
  detail = ", ".join(f"{k}={v}" for k, v in versions.items())
647
647
  vr.error(f"Version mismatch across files: {detail}")
648
648
 
649
+ # Check README "What's New" section matches current version
650
+ pkg_version = versions.get("package.json", "")
651
+ if pkg_version:
652
+ readme = tk_dir / "README.md"
653
+ if readme.is_file():
654
+ content = readme.read_text(encoding="utf-8")
655
+ m = re.search(r"## What's New in v([\d.]+)", content)
656
+ if m:
657
+ whats_new_ver = m.group(1)
658
+ if whats_new_ver == pkg_version:
659
+ print(f" OK: README \"What's New\" (v{whats_new_ver})")
660
+ else:
661
+ vr.error(
662
+ f"README \"What's New in v{whats_new_ver}\" "
663
+ f"is stale (package.json is v{pkg_version})"
664
+ )
665
+
649
666
 
650
667
  def validate_content_quality(tk_dir: Path, vr: ValidationResult) -> None:
651
668
  """Check content quality: name matches directory, non-empty body."""