@softspark/ai-toolkit 3.4.0 → 3.4.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/CHANGELOG.md +15 -0
- package/README.md +8 -7
- package/app/.claude-plugin/plugin.json +1 -1
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/scripts/hook_sources.py +6 -0
- package/scripts/rule_sources.py +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## v3.4.1 — URL-tracked sources protected from path-write demotion (2026-05-06)
|
|
11
|
+
|
|
12
|
+
Patch release. Fixes a regression introduced in v3.4.0 where `ai-toolkit update` could rewrite a URL-tracked hook entry into a local-path entry.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **`register_path_source` no longer overwrites URL entries** in `scripts/hook_sources.py` and `scripts/rule_sources.py`. The function now returns early when an existing entry has a `url` field. Previously, `scripts/install_steps/markers.py:refresh_url_hooks` re-injected URL hooks from their cached file, and `inject_hook_cli.py` would call `register_path_source` with that cached path — silently demoting the URL record to a local one on every update.
|
|
17
|
+
- **Regression test** in `tests/test_inject_hook.bats` that pre-seeds a URL entry, runs the local-path inject path, and asserts the URL entry survives untouched.
|
|
18
|
+
|
|
19
|
+
### Recovery
|
|
20
|
+
|
|
21
|
+
- If your `~/.softspark/ai-toolkit/hooks/external/sources.json` has a `path` instead of a `url` for a hook that was originally URL-installed, run `ai-toolkit remove-hook <name>` followed by `ai-toolkit inject-hook <original-url>` to re-register from the URL.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
10
25
|
## v3.4.0 — Local-path source tracking & orphan detection (2026-05-06)
|
|
11
26
|
|
|
12
27
|
Minor release. Closes the visibility gap for rules and hooks installed from local files: `add-rule` and `inject-hook` now record the origin path in `sources.json`, and `status` flags any rule files on disk that have no source recorded.
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](app/skills/)
|
|
8
8
|
[](app/agents/)
|
|
9
|
-
[](tests/)
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<img src="assets/ai-toolkit-readme-hero.png" alt="ai-toolkit 3 README hero showing one toolkit for 12 AI editors" width="900">
|
|
@@ -14,13 +14,14 @@
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
-
## What's New in v3.4.
|
|
17
|
+
## What's New in v3.4.1
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Patch release. Fixes a v3.4.0 regression where `ai-toolkit update` could demote URL-tracked hook entries to local-path entries on every refresh. `register_path_source` now never overwrites a URL-tracked entry. Also adds a regression test.
|
|
20
20
|
|
|
21
|
-
-
|
|
22
|
-
- **`
|
|
23
|
-
-
|
|
21
|
+
- **URL-tracked hooks and rules are protected** — `register_path_source` returns early when the existing entry has a `url` field.
|
|
22
|
+
- **`add-rule` and `inject-hook` still track local paths** — file-sourced rules and hooks record `{"path": ..., "sha256": ...}` in `sources.json`, alongside URL entries.
|
|
23
|
+
- **`status` distinguishes URL vs local sources** — local entries show the absolute origin path with a `[local]` tag.
|
|
24
|
+
- **Orphan rule detection** — rule files on disk with no source recorded are listed with `(orphan, no source recorded — re-run add-rule)`.
|
|
24
25
|
|
|
25
26
|
See [CHANGELOG.md](CHANGELOG.md) for full history.
|
|
26
27
|
|
|
@@ -153,7 +154,7 @@ ai-toolkit/
|
|
|
153
154
|
│ └── ARCHITECTURE.md # Full system design
|
|
154
155
|
├── kb/ # Reference docs, procedures, plans
|
|
155
156
|
├── scripts/ # Validation, install, evaluation scripts
|
|
156
|
-
├── tests/ # Bats test suite (
|
|
157
|
+
├── tests/ # Bats test suite (1047 tests)
|
|
157
158
|
└── CHANGELOG.md
|
|
158
159
|
```
|
|
159
160
|
|
|
@@ -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": "3.4.
|
|
4
|
+
"version": "3.4.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "SoftSpark",
|
|
7
7
|
"url": "https://github.com/softspark"
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softspark/ai-toolkit",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.1",
|
|
4
4
|
"description": "AI coding toolkit: 112 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",
|
package/scripts/hook_sources.py
CHANGED
|
@@ -135,6 +135,12 @@ def register_path_source(
|
|
|
135
135
|
raise ValueError(f"Invalid hook name: {hook_name!r}")
|
|
136
136
|
hooks_dir = hooks_dir or EXTERNAL_HOOKS_DIR
|
|
137
137
|
sources = load_sources(hooks_dir)
|
|
138
|
+
existing = sources.get(hook_name) or {}
|
|
139
|
+
# Never demote a URL-tracked entry to a local-path entry. update() flows
|
|
140
|
+
# call inject() with the cached file path after URL fetch, which would
|
|
141
|
+
# otherwise overwrite the URL.
|
|
142
|
+
if "url" in existing:
|
|
143
|
+
return
|
|
138
144
|
entry: dict[str, Any] = {
|
|
139
145
|
"path": str(Path(path).resolve()),
|
|
140
146
|
"fetched_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
package/scripts/rule_sources.py
CHANGED
|
@@ -136,6 +136,11 @@ def register_path_source(
|
|
|
136
136
|
raise ValueError(f"Invalid rule name: {rule_name!r}")
|
|
137
137
|
rules_dir = rules_dir or RULES_DIR
|
|
138
138
|
sources = load_sources(rules_dir)
|
|
139
|
+
existing = sources.get(rule_name) or {}
|
|
140
|
+
# Never demote a URL-tracked entry to a local-path entry. Symmetric with
|
|
141
|
+
# register_path_source() in hook_sources.py.
|
|
142
|
+
if "url" in existing:
|
|
143
|
+
return
|
|
139
144
|
entry: dict[str, Any] = {
|
|
140
145
|
"path": str(Path(path).resolve()),
|
|
141
146
|
"fetched_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|