@softspark/ai-toolkit 4.25.0 → 4.26.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,81 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v4.26.0 — Python floor is declared and enforced (2026-08-21)
11
+
12
+ ### Added
13
+
14
+ - **Python preflight in the CLI.** `bin/ai-toolkit.js` now verifies `python3`
15
+ exists and reports at least 3.11 before spawning any script, and prints a
16
+ platform-specific fix (`brew install python@3.13` on macOS) instead of a
17
+ traceback from deep inside `scripts/`. The check is lazy and memoized, so
18
+ `help` and `--version` still run without a Python interpreter.
19
+ - **Matching floor check in `scripts/_common.py`.** Every entry point imports
20
+ `_common` first, so `python3 scripts/install.py` run directly fails with the
21
+ same message rather than a `TypeError` from whichever module happens to use
22
+ 3.11+ syntax.
23
+ - **CI import sweep.** The `python-syntax` job now runs on a `['3.11', '3.13']`
24
+ matrix and imports every module under `scripts/` on each. `py_compile` only
25
+ catches syntax, which is exactly why a 3.10-only construct shipped: version-
26
+ gated runtime features surface on import, not on compile.
27
+
28
+ ### Fixed
29
+
30
+ - **`ai-toolkit update --local` crashed on macOS system Python.** `/usr/bin/python3`
31
+ is 3.9 on every Mac, and `scripts/mcp_editors.py` uses
32
+ `@dataclass(frozen=True, slots=True)` (3.10+), so the install path died with
33
+ `TypeError: dataclass() got an unexpected keyword argument 'slots'` while
34
+ importing `install_steps/ai_tools.py`. The interpreter is now rejected up
35
+ front with instructions.
36
+
37
+ ### Changed
38
+
39
+ - **`scripts/check_deps.py` declares Python >= 3.11**, up from a `min_version`
40
+ of `3.8` that never matched what the scripts actually needed, and its reason
41
+ string calls out the macOS 3.9 trap.
42
+ - **Requirements are documented.** README gained a Requirements note above the
43
+ install snippet; `CLAUDE.md` records the three places the floor is declared
44
+ (`bin/ai-toolkit.js`, `scripts/_common.py`, `scripts/check_deps.py`) and the
45
+ CI matrix that guards it.
46
+
47
+ ### Ecosystem
48
+
49
+ - Snapshot refreshed: 11 tools drifted, all class A (content reworded, no
50
+ heading delta). Claude Code 2.1.235 → 2.1.238 and Codex CLI 0.147.0 → 0.148.0
51
+ are upstream patch bumps with no new surface, so no generator changed.
52
+
53
+ ### Tests
54
+
55
+ - Three CLI regression tests: a stale `python3` shim exits 1 with the version
56
+ message, a missing `python3` exits 1 instead of crashing, and `help` still
57
+ works with no interpreter on PATH. Test count: 1637 → 1640.
58
+
59
+ ---
60
+
61
+ ## v4.25.1 — npm advisories count again (2026-08-19)
62
+
63
+ ### Fixed
64
+
65
+ - **`cve-scan` reported zero findings for a vulnerable npm project.** npm audit
66
+ can return the legacy `advisories` object as well as the current
67
+ `vulnerabilities` object. The scanner parsed only the latter, so five real
68
+ lodash advisories remained trapped in `raw_output` while `total_findings` was
69
+ zero and the process exited successfully. Both formats are now normalized,
70
+ and HIGH advisories restore the documented non-zero exit code.
71
+
72
+ ### Changed
73
+
74
+ - **The published-package scanner smoke uses real per-scanner fixtures and
75
+ flags.** It now includes healthcare patterns and a deliberately vulnerable
76
+ locked npm dependency, invokes `cve-scan` with `--json`, and treats raw npm
77
+ advisories with no normalized findings as a parser failure.
78
+
79
+ ### Tests
80
+
81
+ - Added a public-CLI regression test with a stubbed legacy npm audit response;
82
+ it asserts normalized severity, installed version, CVE, fixed range, finding
83
+ count, and exit status.
84
+
10
85
  ## v4.25.0 — editor-native integrations catch up (2026-08-19)
11
86
 
12
87
  ### Added
package/README.md CHANGED
@@ -6,20 +6,19 @@
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-1636%20passing-success)](tests/)
9
+ [![Tests](https://img.shields.io/badge/tests-1640%20passing-success)](tests/)
10
10
 
11
- ## What's New in v4.25.0
11
+ ## What's New in v4.26.0
12
12
 
13
- **v4.25.0** brings the editor integrations onto their current native surfaces:
13
+ **v4.26.0** declares and enforces the Python floor the scripts already needed:
14
14
 
15
- - Codex gets its exact 11-event hook contract and a deterministic native plugin
16
- exporter with offline verification.
17
- - Google Antigravity gets native hooks, agents, plugin packaging, MCP mapping,
18
- and local/global install profiles.
19
- - Gemini gains native agents, OpenCode gains native skill directories, and Cline
20
- gains native rules plus its exact 8-event hook lifecycle.
21
- - Claude Code validation now matches the current prompt, agent, HTTP hook, and
22
- plugin contracts, with transactional symlink protections across new writers.
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.
23
22
 
24
23
  See [CHANGELOG.md](CHANGELOG.md) for full history.
25
24
 
@@ -42,6 +41,10 @@ See [CHANGELOG.md](CHANGELOG.md) for full history.
42
41
 
43
42
  ## Install
44
43
 
44
+ **Requirements:** Node.js >= 18 and Python >= 3.11.
45
+
46
+ > **macOS:** `/usr/bin/python3` is Python 3.9 and will not run the toolkit. Install a supported one with `brew install python@3.13` and make sure `which python3` no longer points at `/usr/bin/python3`.
47
+
45
48
  ```bash
46
49
  # Option A: install globally (once per machine)
47
50
  npm install -g @softspark/ai-toolkit
@@ -185,7 +188,7 @@ ai-toolkit/
185
188
  │ └── ARCHITECTURE.md # Full system design
186
189
  ├── kb/ # Reference docs, procedures, plans
187
190
  ├── scripts/ # Validation, install, evaluation scripts
188
- ├── tests/ # Bats and Python test suite (1636 tests)
191
+ ├── tests/ # Bats and Python test suite (1640 tests)
189
192
  └── CHANGELOG.md
190
193
  ```
191
194
 
@@ -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.25.0",
6
+ "version": "4.26.0",
7
7
  "author": {
8
8
  "name": "SoftSpark",
9
9
  "url": "https://github.com/softspark"
@@ -9,7 +9,6 @@ Stdlib only. No external dependencies.
9
9
  """
10
10
 
11
11
  import json
12
- import os
13
12
  import shutil
14
13
  import subprocess
15
14
  import sys
@@ -141,7 +140,7 @@ def run_audit(eco: dict, root: Path, fix: bool = False) -> dict:
141
140
  except FileNotFoundError:
142
141
  result["error"] = f"Tool '{eco['tool']}' not found in PATH"
143
142
  except subprocess.TimeoutExpired:
144
- result["error"] = f"Audit command timed out after 120s"
143
+ result["error"] = "Audit command timed out after 120s"
145
144
 
146
145
  return result
147
146
 
@@ -168,6 +167,21 @@ def parse_npm_audit(raw: str) -> list[dict]:
168
167
  "url": via.get("url", ""),
169
168
  "fixed_in": info.get("fixAvailable", {}).get("version", "unknown") if isinstance(info.get("fixAvailable"), dict) else "unknown",
170
169
  })
170
+
171
+ for advisory in data.get("advisories", {}).values():
172
+ installed_versions = sorted({
173
+ finding.get("version", "unknown")
174
+ for finding in advisory.get("findings", [])
175
+ })
176
+ findings.append({
177
+ "severity": advisory.get("severity", "unknown").upper(),
178
+ "package": advisory.get("module_name", "unknown"),
179
+ "installed": ", ".join(installed_versions) or "unknown",
180
+ "cve": advisory.get("cves", []) or [advisory.get("github_advisory_id", "N/A")],
181
+ "title": advisory.get("title", "Unknown"),
182
+ "url": advisory.get("url", ""),
183
+ "fixed_in": advisory.get("patched_versions", "unknown"),
184
+ })
171
185
  return findings
172
186
 
173
187
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "last_run": "2026-08-19T14:01:09Z",
2
+ "last_run": "2026-08-21T08:34:52Z",
3
3
  "schema_version": 1,
4
4
  "tools": {
5
5
  "aider": {
@@ -24,7 +24,7 @@
24
24
  }
25
25
  },
26
26
  "augment": {
27
- "docs_hash": "0f7df1fdf4962938",
27
+ "docs_hash": "516d8db28cb3ec63",
28
28
  "headings": [
29
29
  "Admin",
30
30
  "Auggie CLI",
@@ -66,7 +66,7 @@
66
66
  }
67
67
  },
68
68
  "claude-app": {
69
- "docs_hash": "5bba2a625b109b69",
69
+ "docs_hash": "7e06a1985d5bb822",
70
70
  "headings": [
71
71
  "Add global and folder instructions",
72
72
  "Availability",
@@ -107,7 +107,7 @@
107
107
  }
108
108
  },
109
109
  "claude-code": {
110
- "docs_hash": "3d4394d0bf5dd8c1",
110
+ "docs_hash": "2b3d6b11c4be766f",
111
111
  "headings": [
112
112
  "Core concepts",
113
113
  "Documentation Index",
@@ -173,10 +173,10 @@
173
173
  "userConfig": false,
174
174
  "workflows": true
175
175
  },
176
- "version": "2.1.235 (Claude Code)"
176
+ "version": "2.1.238 (Claude Code)"
177
177
  },
178
178
  "cline": {
179
- "docs_hash": "bf058b01d03e0455",
179
+ "docs_hash": "f93d3a8437c1165f",
180
180
  "headings": [
181
181
  "API Reference",
182
182
  "Best Practices",
@@ -223,7 +223,7 @@
223
223
  }
224
224
  },
225
225
  "codex-cli": {
226
- "docs_hash": "61aea7a06c8c6355",
226
+ "docs_hash": "fc1c7e43bec9f748",
227
227
  "headings": [
228
228
  "API",
229
229
  "API Reference",
@@ -362,10 +362,10 @@
362
362
  "plugin marketplace": false,
363
363
  "sandbox": true
364
364
  },
365
- "version": "codex-cli 0.147.0"
365
+ "version": "codex-cli 0.148.0"
366
366
  },
367
367
  "cursor": {
368
- "docs_hash": "793ca4e25e676e17",
368
+ "docs_hash": "2abfadb030c12d07",
369
369
  "headings": [
370
370
  "Agent",
371
371
  "CLI",
@@ -397,7 +397,7 @@
397
397
  }
398
398
  },
399
399
  "gemini-cli": {
400
- "docs_hash": "f343c5f7c60fefcf",
400
+ "docs_hash": "a2c9759e0296670d",
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": "79d741f4e3273f8d",
441
+ "docs_hash": "ab9b7f5b4b692e05",
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": "8cbf9aaffb6b57bf",
478
+ "docs_hash": "9fe532d247c3e361",
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": "b112229db465d2e4",
505
+ "docs_hash": "4287f7d767dda4a8",
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": "fddce54b82ac479e",
567
+ "docs_hash": "60e25f8cb4c381be",
568
568
  "headings": [
569
569
  "Accounts",
570
570
  "Advanced",
package/bin/ai-toolkit.js CHANGED
@@ -12,6 +12,9 @@ const fs = require('fs');
12
12
  const TOOLKIT_DIR = path.dirname(__dirname);
13
13
  const CWD = process.cwd();
14
14
 
15
+ /** Minimum Python the toolkit scripts require: [major, minor]. */
16
+ const PYTHON_MIN = [3, 11];
17
+
15
18
  if (!process.env.HOME) {
16
19
  console.error('Error: HOME environment variable is not set');
17
20
  process.exit(1);
@@ -139,6 +142,60 @@ function scriptPath(scriptName) {
139
142
  return path.join(TOOLKIT_DIR, 'scripts', scriptName);
140
143
  }
141
144
 
145
+ /** Memoized result of requirePython(); null until the first check runs. */
146
+ let pythonOk = null;
147
+
148
+ /**
149
+ * Verify `python3` exists and is >= PYTHON_MIN before any script is spawned.
150
+ * Without this the caller sees a raw traceback from deep inside scripts/
151
+ * (e.g. `dataclass() got an unexpected keyword argument 'slots'` on Python
152
+ * 3.9, which is what macOS ships as /usr/bin/python3).
153
+ * Exits with an actionable message when the interpreter is missing or too old.
154
+ */
155
+ function requirePython() {
156
+ if (pythonOk) return;
157
+ const probe = spawnSync('python3', ['-c', 'import sys; sys.stdout.write("%d.%d.%d" % sys.version_info[:3])'], {
158
+ encoding: 'utf8',
159
+ });
160
+ const want = PYTHON_MIN.join('.');
161
+
162
+ if (probe.error || probe.status !== 0) {
163
+ console.error(`Error: python3 not found on PATH — ai-toolkit requires Python >= ${want}`);
164
+ console.error(pythonHint());
165
+ process.exit(1);
166
+ }
167
+
168
+ const found = probe.stdout.trim();
169
+ const [major, minor] = found.split('.').map(Number);
170
+ if (major < PYTHON_MIN[0] || (major === PYTHON_MIN[0] && minor < PYTHON_MIN[1])) {
171
+ console.error(`Error: ai-toolkit requires Python >= ${want}, found ${found}`);
172
+ console.error(pythonHint());
173
+ process.exit(1);
174
+ }
175
+ pythonOk = true;
176
+ }
177
+
178
+ /**
179
+ * Platform-specific instructions for installing a supported Python.
180
+ * @returns {string} Multi-line hint text
181
+ */
182
+ function pythonHint() {
183
+ const lines = [];
184
+ if (process.platform === 'darwin') {
185
+ lines.push('macOS ships Python 3.9 as /usr/bin/python3. Install a newer one:');
186
+ lines.push(' brew install python@3.13');
187
+ lines.push('Then make sure it comes first on PATH (`which python3` should not be /usr/bin/python3).');
188
+ } else if (process.platform === 'win32') {
189
+ lines.push('Install Python 3.11+ and make sure `python3` resolves to it:');
190
+ lines.push(' winget install Python.Python.3.13');
191
+ } else {
192
+ lines.push('Install Python 3.11+ via your package manager, e.g.:');
193
+ lines.push(' sudo apt install python3.13 # Debian/Ubuntu');
194
+ lines.push(' sudo dnf install python3.13 # Fedora/RHEL');
195
+ }
196
+ return lines.join('\n');
197
+ }
198
+
142
199
  /**
143
200
  * Execute a generator script synchronously via python3, returning its stdout.
144
201
  * Exits the process on failure.
@@ -147,6 +204,7 @@ function scriptPath(scriptName) {
147
204
  * @returns {Buffer} stdout output
148
205
  */
149
206
  function runGenerator(scriptName, extraArgs = []) {
207
+ requirePython();
150
208
  try {
151
209
  return execFileSync('python3', [scriptPath(scriptName), ...extraArgs], { cwd: TOOLKIT_DIR });
152
210
  } catch (err) {
@@ -162,6 +220,7 @@ function runGenerator(scriptName, extraArgs = []) {
162
220
  * @param {{ cwd?: string }} [opts={}] - Options (cwd override)
163
221
  */
164
222
  function run(script, args = [], opts = {}) {
223
+ requirePython();
165
224
  const result = spawnSync('python3', [script, ...args], {
166
225
  stdio: 'inherit',
167
226
  cwd: opts.cwd || CWD,
@@ -178,6 +237,7 @@ function run(script, args = [], opts = {}) {
178
237
  * @param {...string} flags - Flags to pass: --rules, --hooks, --mcp
179
238
  */
180
239
  function propagateGlobal(...flags) {
240
+ requirePython();
181
241
  const result = spawnSync('python3', [scriptPath('propagate_global.py'), ...flags], {
182
242
  stdio: 'inherit',
183
243
  cwd: CWD,
@@ -4,6 +4,7 @@ service: ai-toolkit
4
4
  category: best-practices
5
5
  tags: [best-practices, guidelines]
6
6
  last_updated: "2026-03-25"
7
+ description: "Section index for ai-toolkit best-practices. No documents are filed here yet; new ones follow the best-practices template."
7
8
  ---
8
9
 
9
10
  # Best Practices
@@ -4,6 +4,7 @@ service: ai-toolkit
4
4
  category: howto
5
5
  tags: [howto, guides]
6
6
  last_updated: "2026-03-25"
7
+ description: "Section index for ai-toolkit howto. No documents are filed here yet; new ones follow the howto template."
7
8
  ---
8
9
 
9
10
  # How-To Guides
@@ -3,9 +3,9 @@ title: "SOP: Post-Release Testing"
3
3
  category: procedures
4
4
  service: ai-toolkit
5
5
  tags: [sop, post-release, smoke-test, npm, sandbox, plugin-pack, provenance, isolation]
6
- version: "1.1.0"
6
+ version: "1.2.0"
7
7
  created: "2026-07-26"
8
- last_updated: "2026-08-06"
8
+ last_updated: "2026-08-19"
9
9
  description: "Smoke-test a published @softspark/ai-toolkit release from npm in an isolated HOME and npm prefix, without touching the maintainer's real install. Covers provenance, CLI, doctor, per-skill script resolution, scanner wiring, and the full plugin-pack lifecycle including the degraded-install path. Written for v4.18.0 and not run; v4.18.0 shipped a pack that broke every command it touched. First actually run on v4.22.0, which added Phases 4b and 4c after that release fixed four skills whose documented script path had never resolved and two that shipped a scanner nothing invoked."
10
10
  ---
11
11
 
@@ -159,21 +159,30 @@ cat > "$FX/index.html" <<'EOF'
159
159
  <!DOCTYPE html><html><head><title>t</title></head>
160
160
  <body><h1>A</h1><h3>skipped h2</h3><img src="x.png"><input type="text"><div onclick="go()">click</div></body></html>
161
161
  EOF
162
- printf '{"name":"fx","dependencies":{"react":"18"}}\n' > "$FX/package.json"
163
-
164
- for s in a11y-validate seo-validate hipaa-validate cve-scan; do
165
- D="$HOME/.claude/skills/$s"
166
- script=$(ls "$D"/scripts/*.py 2>/dev/null | head -1)
167
- [ -n "$script" ] || continue
168
- echo "=== $s ==="
169
- CLAUDE_SKILL_DIR="$D" timeout 60 python3 "$script" "$FX" --output json </dev/null 2>&1 | head -3
170
- done
162
+ cat > "$FX/patient.py" <<'EOF'
163
+ patient = {"ssn": "123-45-6789", "diagnosis": "fixture"}
164
+ endpoint = "http://example.com/patient"
165
+ encrypt = False
166
+ EOF
167
+ printf '{"name":"fx","dependencies":{"lodash":"4.17.20","react":"18"}}\n' > "$FX/package.json"
168
+ npm install --package-lock-only --ignore-scripts --prefix "$FX"
169
+
170
+ D="$HOME/.claude/skills/a11y-validate"
171
+ CLAUDE_SKILL_DIR="$D" timeout 60 python3 "$D/scripts/a11y-scanner.py" "$FX" --output json </dev/null
172
+ D="$HOME/.claude/skills/seo-validate"
173
+ CLAUDE_SKILL_DIR="$D" timeout 60 python3 "$D/scripts/seo-scanner.py" "$FX" --output json </dev/null
174
+ D="$HOME/.claude/skills/hipaa-validate"
175
+ CLAUDE_SKILL_DIR="$D" timeout 60 python3 "$D/scripts/hipaa_scan.py" "$FX" --output json </dev/null
176
+ D="$HOME/.claude/skills/cve-scan"
177
+ CLAUDE_SKILL_DIR="$D" timeout 60 python3 "$D/scripts/cve_scan.py" "$FX" --json </dev/null
171
178
  ```
172
179
 
173
180
  **Verify:**
174
181
  - [ ] The scanner returns findings, not an empty set — the fixture has real defects
175
182
  - [ ] Findings span more than one category, proving the whole check set ran
176
183
  - [ ] A scanner that exits non-zero on findings is doing its job, not failing
184
+ - [ ] CVE output has `total_findings > 0`; raw npm advisories with an empty
185
+ normalized `findings` array are a parser failure, not a clean scan
177
186
 
178
187
  `a11y-validate` and `seo-validate` shipped working scanners that **no step in
179
188
  either skill invoked** for their entire life before v4.22.0. The model was told to
@@ -11,7 +11,7 @@ tags:
11
11
  doc_type: reference
12
12
  created: "2026-04-11"
13
13
  last_updated: "2026-07-26"
14
- description: "Comprehensive guide for setting up and using ai-toolkit configuration inheritance. Covers base config creation, project setup, enforcement rules, CI integration, and troubleshooting."
14
+ description: "Configuration inheritance for ai-toolkit projects extends base settings from npm packages, Git URLs, or local paths. Defines merge semantics, enforcement rules, and CLI commands for validating and diffing configurations."
15
15
  ---
16
16
 
17
17
  # Enterprise Config Inheritance Guide
@@ -4,6 +4,7 @@ service: ai-toolkit
4
4
  category: troubleshooting
5
5
  tags: [troubleshooting, debugging]
6
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."
7
8
  ---
8
9
 
9
10
  # Troubleshooting
package/llms-full.txt CHANGED
@@ -243,6 +243,7 @@ service: ai-toolkit
243
243
  category: best-practices
244
244
  tags: [best-practices, guidelines]
245
245
  last_updated: "2026-03-25"
246
+ description: "Section index for ai-toolkit best-practices. No documents are filed here yet; new ones follow the best-practices template."
246
247
  ---
247
248
 
248
249
  # Best Practices
@@ -5081,6 +5082,7 @@ service: ai-toolkit
5081
5082
  category: howto
5082
5083
  tags: [howto, guides]
5083
5084
  last_updated: "2026-03-25"
5085
+ description: "Section index for ai-toolkit howto. No documents are filed here yet; new ones follow the howto template."
5084
5086
  ---
5085
5087
 
5086
5088
  # How-To Guides
@@ -7041,9 +7043,9 @@ title: "SOP: Post-Release Testing"
7041
7043
  category: procedures
7042
7044
  service: ai-toolkit
7043
7045
  tags: [sop, post-release, smoke-test, npm, sandbox, plugin-pack, provenance, isolation]
7044
- version: "1.1.0"
7046
+ version: "1.2.0"
7045
7047
  created: "2026-07-26"
7046
- last_updated: "2026-08-06"
7048
+ last_updated: "2026-08-19"
7047
7049
  description: "Smoke-test a published @softspark/ai-toolkit release from npm in an isolated HOME and npm prefix, without touching the maintainer's real install. Covers provenance, CLI, doctor, per-skill script resolution, scanner wiring, and the full plugin-pack lifecycle including the degraded-install path. Written for v4.18.0 and not run; v4.18.0 shipped a pack that broke every command it touched. First actually run on v4.22.0, which added Phases 4b and 4c after that release fixed four skills whose documented script path had never resolved and two that shipped a scanner nothing invoked."
7048
7050
  ---
7049
7051
 
@@ -7197,21 +7199,30 @@ cat > "$FX/index.html" <<'EOF'
7197
7199
  <!DOCTYPE html><html><head><title>t</title></head>
7198
7200
  <body><h1>A</h1><h3>skipped h2</h3><img src="x.png"><input type="text"><div onclick="go()">click</div></body></html>
7199
7201
  EOF
7200
- printf '{"name":"fx","dependencies":{"react":"18"}}\n' > "$FX/package.json"
7201
-
7202
- for s in a11y-validate seo-validate hipaa-validate cve-scan; do
7203
- D="$HOME/.claude/skills/$s"
7204
- script=$(ls "$D"/scripts/*.py 2>/dev/null | head -1)
7205
- [ -n "$script" ] || continue
7206
- echo "=== $s ==="
7207
- CLAUDE_SKILL_DIR="$D" timeout 60 python3 "$script" "$FX" --output json </dev/null 2>&1 | head -3
7208
- done
7202
+ cat > "$FX/patient.py" <<'EOF'
7203
+ patient = {"ssn": "123-45-6789", "diagnosis": "fixture"}
7204
+ endpoint = "http://example.com/patient"
7205
+ encrypt = False
7206
+ EOF
7207
+ printf '{"name":"fx","dependencies":{"lodash":"4.17.20","react":"18"}}\n' > "$FX/package.json"
7208
+ npm install --package-lock-only --ignore-scripts --prefix "$FX"
7209
+
7210
+ D="$HOME/.claude/skills/a11y-validate"
7211
+ CLAUDE_SKILL_DIR="$D" timeout 60 python3 "$D/scripts/a11y-scanner.py" "$FX" --output json </dev/null
7212
+ D="$HOME/.claude/skills/seo-validate"
7213
+ CLAUDE_SKILL_DIR="$D" timeout 60 python3 "$D/scripts/seo-scanner.py" "$FX" --output json </dev/null
7214
+ D="$HOME/.claude/skills/hipaa-validate"
7215
+ CLAUDE_SKILL_DIR="$D" timeout 60 python3 "$D/scripts/hipaa_scan.py" "$FX" --output json </dev/null
7216
+ D="$HOME/.claude/skills/cve-scan"
7217
+ CLAUDE_SKILL_DIR="$D" timeout 60 python3 "$D/scripts/cve_scan.py" "$FX" --json </dev/null
7209
7218
  ```
7210
7219
 
7211
7220
  **Verify:**
7212
7221
  - [ ] The scanner returns findings, not an empty set — the fixture has real defects
7213
7222
  - [ ] Findings span more than one category, proving the whole check set ran
7214
7223
  - [ ] A scanner that exits non-zero on findings is doing its job, not failing
7224
+ - [ ] CVE output has `total_findings > 0`; raw npm advisories with an empty
7225
+ normalized `findings` array are a parser failure, not a clean scan
7215
7226
 
7216
7227
  `a11y-validate` and `seo-validate` shipped working scanners that **no step in
7217
7228
  either skill invoked** for their entire life before v4.22.0. The model was told to
@@ -11046,7 +11057,7 @@ tags:
11046
11057
  doc_type: reference
11047
11058
  created: "2026-04-11"
11048
11059
  last_updated: "2026-07-26"
11049
- description: "Comprehensive guide for setting up and using ai-toolkit configuration inheritance. Covers base config creation, project setup, enforcement rules, CI integration, and troubleshooting."
11060
+ description: "Configuration inheritance for ai-toolkit projects extends base settings from npm packages, Git URLs, or local paths. Defines merge semantics, enforcement rules, and CLI commands for validating and diffing configurations."
11050
11061
  ---
11051
11062
 
11052
11063
  # Enterprise Config Inheritance Guide
@@ -16120,6 +16131,7 @@ service: ai-toolkit
16120
16131
  category: troubleshooting
16121
16132
  tags: [troubleshooting, debugging]
16122
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."
16123
16135
  ---
16124
16136
 
16125
16137
  # Troubleshooting
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.25.0",
2
+ "version": "4.26.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.25.0",
3
+ "version": "4.26.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",
@@ -20,8 +20,35 @@ from __future__ import annotations
20
20
  import os
21
21
  import platform
22
22
  import shutil
23
+ import sys
23
24
  from pathlib import Path
24
25
 
26
+ # ---------------------------------------------------------------------------
27
+ # Python version floor
28
+ # ---------------------------------------------------------------------------
29
+ # Kept in sync with PYTHON_MIN in bin/ai-toolkit.js and the python3 entry in
30
+ # scripts/check_deps.py. Checked here because _common is imported first by
31
+ # every entry point, so a stale interpreter fails with this message instead of
32
+ # a traceback from whichever module happens to use 3.11+ syntax.
33
+ PYTHON_MIN = (3, 11)
34
+
35
+ if sys.version_info < PYTHON_MIN:
36
+ _want = ".".join(str(n) for n in PYTHON_MIN)
37
+ _found = ".".join(str(n) for n in sys.version_info[:3])
38
+ sys.stderr.write(
39
+ "Error: ai-toolkit requires Python >= {}, found {} ({})\n".format(
40
+ _want, _found, sys.executable
41
+ )
42
+ )
43
+ if sys.platform == "darwin":
44
+ sys.stderr.write(
45
+ "macOS ships Python 3.9 as /usr/bin/python3. Install a newer one:\n"
46
+ " brew install python@3.13\n"
47
+ )
48
+ else:
49
+ sys.stderr.write("Install Python 3.11+ via your package manager.\n")
50
+ sys.exit(1)
51
+
25
52
  # ---------------------------------------------------------------------------
26
53
  # Re-exports from frontmatter module
27
54
  # ---------------------------------------------------------------------------
@@ -33,7 +33,7 @@ REQUIRED = [
33
33
  {
34
34
  "name": "python3",
35
35
  "check": "python3",
36
- "min_version": "3.8",
36
+ "min_version": "3.11",
37
37
  "packages": {
38
38
  "brew": "python3",
39
39
  "apt": "python3",
@@ -45,7 +45,7 @@ REQUIRED = [
45
45
  "choco": "python",
46
46
  "scoop": "python",
47
47
  },
48
- "reason": "All toolkit scripts run on Python 3 (stdlib only, no pip needed)",
48
+ "reason": "All toolkit scripts run on Python 3.11+ (stdlib only, no pip needed). macOS /usr/bin/python3 is 3.9 and will not work.",
49
49
  },
50
50
  {
51
51
  "name": "git",