@softspark/ai-toolkit 2.0.0 → 2.0.2

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,20 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v2.0.2 — Clean Legacy Directory Removal (2026-04-12)
11
+
12
+ ### Fixed
13
+ - **Migration cleanup** — `~/.ai-toolkit/` is now fully removed after migration instead of leaving an empty directory with a `.migrated` marker
14
+
15
+ ---
16
+
17
+ ## v2.0.1 — Migration Hook Path Fix (2026-04-12)
18
+
19
+ ### Fixed
20
+ - **settings.json hook paths** — migration now rewrites ALL hook commands (including plugin hooks with non-toolkit `_source` tags like `memory-pack`, `enterprise-pack`) from `~/.ai-toolkit/hooks/` to `~/.softspark/ai-toolkit/hooks/`
21
+
22
+ ---
23
+
10
24
  ## v2.0.0 — SoftSpark Namespace Migration (2026-04-12)
11
25
 
12
26
  ### BREAKING CHANGES
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softspark/ai-toolkit",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
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), machine-enforced safety constitution, persona presets, skill security auditor, expanded lifecycle hooks, 11 plugin packs, and benchmark tooling.",
5
5
  "keywords": [
6
6
  "claude",
@@ -21,7 +21,6 @@ import json
21
21
  import os
22
22
  import shutil
23
23
  import sys
24
- from datetime import datetime, timezone
25
24
  from pathlib import Path
26
25
 
27
26
  sys.path.insert(0, str(Path(__file__).resolve().parent))
@@ -36,14 +35,16 @@ from paths import (
36
35
  )
37
36
 
38
37
 
39
- MIGRATED_MARKER = LEGACY_DATA_DIR / ".migrated"
40
-
41
-
42
38
  def needs_migration() -> bool:
43
- """Check if legacy directory exists and hasn't been migrated yet."""
39
+ """Check if legacy directory exists and needs migration.
40
+
41
+ Returns True only when ~/.ai-toolkit/ exists AND contains real data
42
+ (not just an empty dir). After migration the directory is removed entirely.
43
+ """
44
44
  if not LEGACY_DATA_DIR.is_dir():
45
45
  return False
46
- if MIGRATED_MARKER.is_file():
46
+ # Empty dir (or only hidden files) — nothing to migrate
47
+ if not any(LEGACY_DATA_DIR.iterdir()):
47
48
  return False
48
49
  # Don't migrate if AI_TOOLKIT_HOME is set (custom setup)
49
50
  if os.environ.get("AI_TOOLKIT_HOME"):
@@ -77,11 +78,10 @@ def migrate_home_directory(dry_run: bool = False) -> bool:
77
78
  else:
78
79
  # Clean move
79
80
  shutil.move(str(LEGACY_DATA_DIR), str(TOOLKIT_DATA_DIR))
80
- # Recreate legacy dir for marker
81
- LEGACY_DATA_DIR.mkdir(parents=True, exist_ok=True)
82
81
 
83
- # Write migration marker
84
- _write_marker()
82
+ # Remove legacy directory entirely
83
+ if LEGACY_DATA_DIR.exists():
84
+ shutil.rmtree(str(LEGACY_DATA_DIR), ignore_errors=True)
85
85
 
86
86
  print(f" Migrated: {TOOLKIT_DATA_DIR}")
87
87
  return True
@@ -103,21 +103,6 @@ def _merge_directories(src: Path, dst: Path) -> None:
103
103
  shutil.move(str(item), str(dest_item))
104
104
 
105
105
 
106
- def _write_marker() -> None:
107
- """Write .migrated marker pointing to new location."""
108
- LEGACY_DATA_DIR.mkdir(parents=True, exist_ok=True)
109
- now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
110
- marker_data = {
111
- "migrated_to": str(TOOLKIT_DATA_DIR),
112
- "migrated_at": now,
113
- "message": "ai-toolkit data has moved to ~/.softspark/ai-toolkit/. "
114
- "This directory is kept as a marker. Safe to delete.",
115
- }
116
- with open(MIGRATED_MARKER, "w", encoding="utf-8") as f:
117
- json.dump(marker_data, f, indent=2)
118
- f.write("\n")
119
-
120
-
121
106
  def migrate_project_configs(dry_run: bool = False) -> int:
122
107
  """Rename .ai-toolkit.json → .softspark-toolkit.json in registered projects.
123
108
 
@@ -163,8 +148,49 @@ def migrate_project_configs(dry_run: bool = False) -> int:
163
148
  return count
164
149
 
165
150
 
151
+ def migrate_settings_json(dry_run: bool = False) -> int:
152
+ """Rewrite hook commands in ~/.claude/settings.json from old to new path.
153
+
154
+ Replaces ALL occurrences of .ai-toolkit/hooks/ with .softspark/ai-toolkit/hooks/
155
+ in hook command strings, regardless of _source tag. This catches plugin hooks
156
+ (memory-pack, enterprise-pack, etc.) that merge-hooks.py doesn't strip.
157
+
158
+ Returns the number of commands rewritten.
159
+ """
160
+ settings_path = Path.home() / ".claude" / "settings.json"
161
+ if not settings_path.is_file():
162
+ return 0
163
+
164
+ try:
165
+ with open(settings_path, encoding="utf-8") as f:
166
+ data = json.load(f)
167
+ except (json.JSONDecodeError, OSError):
168
+ return 0
169
+
170
+ hooks = data.get("hooks", {})
171
+ old_prefix = ".ai-toolkit/hooks/"
172
+ new_prefix = ".softspark/ai-toolkit/hooks/"
173
+ count = 0
174
+
175
+ for entries in hooks.values():
176
+ for entry in entries:
177
+ for hook in entry.get("hooks", []):
178
+ cmd = hook.get("command", "")
179
+ if old_prefix in cmd and new_prefix not in cmd:
180
+ hook["command"] = cmd.replace(old_prefix, new_prefix)
181
+ count += 1
182
+
183
+ if count > 0 and not dry_run:
184
+ with open(settings_path, "w", encoding="utf-8") as f:
185
+ json.dump(data, f, indent=4, ensure_ascii=False)
186
+ f.write("\n")
187
+ print(f" Rewritten: {count} hook path(s) in settings.json")
188
+
189
+ return count
190
+
191
+
166
192
  def run_full_migration(dry_run: bool = False) -> bool:
167
- """Run complete migration: home directory + project configs.
193
+ """Run complete migration: home directory + project configs + settings.json.
168
194
 
169
195
  Returns True if any migration was performed.
170
196
  """
@@ -174,6 +200,7 @@ def run_full_migration(dry_run: bool = False) -> bool:
174
200
  count = migrate_project_configs(dry_run=dry_run)
175
201
  if count > 0:
176
202
  print(f" Migrated {count} project config(s)")
203
+ migrate_settings_json(dry_run=dry_run)
177
204
 
178
205
  return migrated
179
206
 
@@ -189,8 +216,7 @@ def main() -> None:
189
216
 
190
217
  if success:
191
218
  print()
192
- print("Migration complete. The old ~/.ai-toolkit/ directory contains only a")
193
- print("migration marker and can be safely deleted.")
219
+ print("Migration complete. ~/.ai-toolkit/ has been removed.")
194
220
  elif not dry_run:
195
221
  print("Migration skipped.")
196
222