@softspark/ai-toolkit 1.0.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.
Files changed (327) hide show
  1. package/AGENTS.md +412 -0
  2. package/CHANGELOG.md +68 -0
  3. package/LICENSE +21 -0
  4. package/README.md +632 -0
  5. package/action.yml +53 -0
  6. package/app/.claude-plugin/plugin.json +44 -0
  7. package/app/ARCHITECTURE.md +306 -0
  8. package/app/CLAUDE.md.template +23 -0
  9. package/app/agents/ai-engineer.md +128 -0
  10. package/app/agents/backend-specialist.md +193 -0
  11. package/app/agents/business-intelligence.md +54 -0
  12. package/app/agents/chaos-monkey.md +67 -0
  13. package/app/agents/chief-of-staff.md +51 -0
  14. package/app/agents/code-archaeologist.md +127 -0
  15. package/app/agents/code-reviewer.md +184 -0
  16. package/app/agents/command-expert.md +131 -0
  17. package/app/agents/data-analyst.md +205 -0
  18. package/app/agents/data-scientist.md +151 -0
  19. package/app/agents/database-architect.md +317 -0
  20. package/app/agents/debugger.md +238 -0
  21. package/app/agents/devops-implementer.md +194 -0
  22. package/app/agents/documenter.md +364 -0
  23. package/app/agents/explorer-agent.md +145 -0
  24. package/app/agents/fact-checker.md +172 -0
  25. package/app/agents/frontend-specialist.md +209 -0
  26. package/app/agents/game-developer.md +216 -0
  27. package/app/agents/incident-responder.md +226 -0
  28. package/app/agents/infrastructure-architect.md +127 -0
  29. package/app/agents/infrastructure-validator.md +247 -0
  30. package/app/agents/llm-ops-engineer.md +237 -0
  31. package/app/agents/mcp-expert.md +228 -0
  32. package/app/agents/mcp-server-architect.md +195 -0
  33. package/app/agents/mcp-testing-engineer.md +292 -0
  34. package/app/agents/meta-architect.md +58 -0
  35. package/app/agents/ml-engineer.md +136 -0
  36. package/app/agents/mobile-developer.md +190 -0
  37. package/app/agents/night-watchman.md +55 -0
  38. package/app/agents/nlp-engineer.md +154 -0
  39. package/app/agents/orchestrator.md +437 -0
  40. package/app/agents/performance-optimizer.md +254 -0
  41. package/app/agents/predictive-analyst.md +57 -0
  42. package/app/agents/product-manager.md +194 -0
  43. package/app/agents/project-planner.md +287 -0
  44. package/app/agents/prompt-engineer.md +103 -0
  45. package/app/agents/qa-automation-engineer.md +182 -0
  46. package/app/agents/rag-engineer.md +201 -0
  47. package/app/agents/research-synthesizer.md +138 -0
  48. package/app/agents/search-specialist.md +101 -0
  49. package/app/agents/security-architect.md +62 -0
  50. package/app/agents/security-auditor.md +293 -0
  51. package/app/agents/seo-specialist.md +111 -0
  52. package/app/agents/system-governor.md +57 -0
  53. package/app/agents/tech-lead.md +62 -0
  54. package/app/agents/technical-researcher.md +103 -0
  55. package/app/agents/test-engineer.md +264 -0
  56. package/app/constitution.md +38 -0
  57. package/app/hooks/_profile-check.sh +11 -0
  58. package/app/hooks/guard-destructive.sh +74 -0
  59. package/app/hooks/guard-path.sh +73 -0
  60. package/app/hooks/post-tool-use.sh +35 -0
  61. package/app/hooks/pre-compact.sh +31 -0
  62. package/app/hooks/quality-check.sh +22 -0
  63. package/app/hooks/quality-gate.sh +49 -0
  64. package/app/hooks/save-session.sh +24 -0
  65. package/app/hooks/session-end.sh +37 -0
  66. package/app/hooks/session-start.sh +29 -0
  67. package/app/hooks/subagent-start.sh +16 -0
  68. package/app/hooks/subagent-stop.sh +16 -0
  69. package/app/hooks/track-usage.sh +50 -0
  70. package/app/hooks/user-prompt-submit.sh +25 -0
  71. package/app/hooks.json +178 -0
  72. package/app/mcp-defaults.json +23 -0
  73. package/app/output-styles/golden-rules.md +43 -0
  74. package/app/plugins/README.md +19 -0
  75. package/app/plugins/csharp-pack/README.md +11 -0
  76. package/app/plugins/csharp-pack/plugin.json +18 -0
  77. package/app/plugins/enterprise-pack/README.md +16 -0
  78. package/app/plugins/enterprise-pack/hooks/output-style.sh +6 -0
  79. package/app/plugins/enterprise-pack/hooks/status-line.sh +8 -0
  80. package/app/plugins/enterprise-pack/plugin.json +24 -0
  81. package/app/plugins/frontend-pack/README.md +14 -0
  82. package/app/plugins/frontend-pack/plugin.json +22 -0
  83. package/app/plugins/java-pack/README.md +11 -0
  84. package/app/plugins/java-pack/plugin.json +18 -0
  85. package/app/plugins/kotlin-pack/README.md +11 -0
  86. package/app/plugins/kotlin-pack/plugin.json +18 -0
  87. package/app/plugins/memory-pack/README.md +24 -0
  88. package/app/plugins/memory-pack/hooks/observation-capture.sh +67 -0
  89. package/app/plugins/memory-pack/hooks/session-summary.sh +71 -0
  90. package/app/plugins/memory-pack/plugin.json +22 -0
  91. package/app/plugins/memory-pack/scripts/init_db.py +81 -0
  92. package/app/plugins/memory-pack/scripts/strip_private.py +22 -0
  93. package/app/plugins/memory-pack/skills/mem-search/SKILL.md +70 -0
  94. package/app/plugins/research-pack/README.md +14 -0
  95. package/app/plugins/research-pack/plugin.json +22 -0
  96. package/app/plugins/ruby-pack/README.md +11 -0
  97. package/app/plugins/ruby-pack/plugin.json +18 -0
  98. package/app/plugins/rust-pack/README.md +11 -0
  99. package/app/plugins/rust-pack/plugin.json +18 -0
  100. package/app/plugins/security-pack/README.md +15 -0
  101. package/app/plugins/security-pack/plugin.json +23 -0
  102. package/app/plugins/swift-pack/README.md +11 -0
  103. package/app/plugins/swift-pack/plugin.json +18 -0
  104. package/app/rules/claude-toolkit-rules.md +21 -0
  105. package/app/rules/git-conventions.md +5 -0
  106. package/app/rules/quality-gates.md +10 -0
  107. package/app/skills/_lib/__init__.py +1 -0
  108. package/app/skills/_lib/detect_utils.py +150 -0
  109. package/app/skills/agent-creator/SKILL.md +82 -0
  110. package/app/skills/analyze/SKILL.md +92 -0
  111. package/app/skills/analyze/scripts/complexity.py +165 -0
  112. package/app/skills/api-patterns/SKILL.md +305 -0
  113. package/app/skills/app-builder/SKILL.md +187 -0
  114. package/app/skills/architecture-audit/SKILL.md +141 -0
  115. package/app/skills/architecture-decision/SKILL.md +55 -0
  116. package/app/skills/architecture-decision/templates/adr-template.md +36 -0
  117. package/app/skills/biz-scan/SKILL.md +30 -0
  118. package/app/skills/briefing/SKILL.md +27 -0
  119. package/app/skills/build/SKILL.md +97 -0
  120. package/app/skills/build/scripts/detect-build.py +151 -0
  121. package/app/skills/chaos/SKILL.md +32 -0
  122. package/app/skills/ci/SKILL.md +77 -0
  123. package/app/skills/ci/scripts/ci-detect.py +135 -0
  124. package/app/skills/ci/templates/github-actions-node.yml +38 -0
  125. package/app/skills/ci/templates/github-actions-python.yml +42 -0
  126. package/app/skills/ci-cd-patterns/SKILL.md +299 -0
  127. package/app/skills/clean-code/SKILL.md +110 -0
  128. package/app/skills/clean-code/reference/dart.md +18 -0
  129. package/app/skills/clean-code/reference/go.md +23 -0
  130. package/app/skills/clean-code/reference/php.md +32 -0
  131. package/app/skills/clean-code/reference/python.md +180 -0
  132. package/app/skills/clean-code/reference/typescript.md +26 -0
  133. package/app/skills/command-creator/SKILL.md +83 -0
  134. package/app/skills/commit/SKILL.md +98 -0
  135. package/app/skills/commit/scripts/pre-commit-check.py +87 -0
  136. package/app/skills/commit/templates/conventional-commit.md +52 -0
  137. package/app/skills/csharp-patterns/SKILL.md +450 -0
  138. package/app/skills/database-patterns/SKILL.md +297 -0
  139. package/app/skills/debug/SKILL.md +154 -0
  140. package/app/skills/debug/scripts/error-parser.py +187 -0
  141. package/app/skills/debugging-tactics/SKILL.md +136 -0
  142. package/app/skills/deploy/SKILL.md +130 -0
  143. package/app/skills/deploy/scripts/pre_deploy_check.py +171 -0
  144. package/app/skills/deploy/templates/deployment-checklist.md +31 -0
  145. package/app/skills/design-an-interface/SKILL.md +105 -0
  146. package/app/skills/design-engineering/SKILL.md +260 -0
  147. package/app/skills/docker-devops/SKILL.md +303 -0
  148. package/app/skills/docs/SKILL.md +145 -0
  149. package/app/skills/docs/scripts/doc-inventory.py +176 -0
  150. package/app/skills/docs/templates/adr-template.md +36 -0
  151. package/app/skills/docs/templates/readme-template.md +67 -0
  152. package/app/skills/documentation-standards/SKILL.md +191 -0
  153. package/app/skills/ecommerce-patterns/SKILL.md +209 -0
  154. package/app/skills/evaluate/SKILL.md +132 -0
  155. package/app/skills/evolve/SKILL.md +27 -0
  156. package/app/skills/explain/SKILL.md +54 -0
  157. package/app/skills/explain/scripts/dependency-graph.py +215 -0
  158. package/app/skills/explore/SKILL.md +112 -0
  159. package/app/skills/explore/scripts/visualize.py +117 -0
  160. package/app/skills/fix/SKILL.md +78 -0
  161. package/app/skills/fix/scripts/error-classifier.py +191 -0
  162. package/app/skills/flutter-patterns/SKILL.md +254 -0
  163. package/app/skills/git-mastery/SKILL.md +70 -0
  164. package/app/skills/grill-me/SKILL.md +38 -0
  165. package/app/skills/health/SKILL.md +91 -0
  166. package/app/skills/health/scripts/health_check.py +162 -0
  167. package/app/skills/hive-mind/SKILL.md +56 -0
  168. package/app/skills/hook-creator/SKILL.md +107 -0
  169. package/app/skills/index/SKILL.md +74 -0
  170. package/app/skills/instinct-review/SKILL.md +77 -0
  171. package/app/skills/java-patterns/SKILL.md +442 -0
  172. package/app/skills/kotlin-patterns/SKILL.md +446 -0
  173. package/app/skills/lint/SKILL.md +103 -0
  174. package/app/skills/lint/scripts/detect-linters.py +112 -0
  175. package/app/skills/mcp-patterns/SKILL.md +270 -0
  176. package/app/skills/mem-search/SKILL.md +70 -0
  177. package/app/skills/migrate/SKILL.md +90 -0
  178. package/app/skills/migrate/scripts/migration-status.py +195 -0
  179. package/app/skills/migration-patterns/SKILL.md +260 -0
  180. package/app/skills/night-watch/SKILL.md +28 -0
  181. package/app/skills/observability-patterns/SKILL.md +203 -0
  182. package/app/skills/onboard/SKILL.md +76 -0
  183. package/app/skills/orchestrate/SKILL.md +86 -0
  184. package/app/skills/panic/SKILL.md +30 -0
  185. package/app/skills/performance-profiling/SKILL.md +59 -0
  186. package/app/skills/plan/SKILL.md +110 -0
  187. package/app/skills/plan/templates/plan-template.md +40 -0
  188. package/app/skills/plan-writing/SKILL.md +201 -0
  189. package/app/skills/plugin-creator/SKILL.md +78 -0
  190. package/app/skills/pr/SKILL.md +129 -0
  191. package/app/skills/pr/scripts/pr-summary.py +175 -0
  192. package/app/skills/prd-to-issues/SKILL.md +108 -0
  193. package/app/skills/prd-to-plan/SKILL.md +120 -0
  194. package/app/skills/predict/SKILL.md +30 -0
  195. package/app/skills/qa-session/SKILL.md +110 -0
  196. package/app/skills/rag-patterns/SKILL.md +203 -0
  197. package/app/skills/refactor/SKILL.md +124 -0
  198. package/app/skills/refactor/scripts/refactor-scan.py +210 -0
  199. package/app/skills/refactor-plan/SKILL.md +112 -0
  200. package/app/skills/repeat/SKILL.md +149 -0
  201. package/app/skills/research-mastery/SKILL.md +56 -0
  202. package/app/skills/review/SKILL.md +141 -0
  203. package/app/skills/review/scripts/diff-analyzer.py +170 -0
  204. package/app/skills/rollback/SKILL.md +87 -0
  205. package/app/skills/rollback/scripts/rollback_info.py +149 -0
  206. package/app/skills/ruby-patterns/SKILL.md +454 -0
  207. package/app/skills/rust-patterns/SKILL.md +446 -0
  208. package/app/skills/search/SKILL.md +64 -0
  209. package/app/skills/security-patterns/SKILL.md +91 -0
  210. package/app/skills/security-patterns/reference/authentication.md +37 -0
  211. package/app/skills/security-patterns/reference/authorization.md +22 -0
  212. package/app/skills/security-patterns/reference/input-validation.md +30 -0
  213. package/app/skills/security-patterns/reference/oauth-csrf-audit.md +131 -0
  214. package/app/skills/skill-creator/SKILL.md +154 -0
  215. package/app/skills/skill-creator/templates/dashboard/index.html +130 -0
  216. package/app/skills/skill-creator/templates/reasoning-engine/assets/example.json +12 -0
  217. package/app/skills/skill-creator/templates/reasoning-engine/search.py +110 -0
  218. package/app/skills/subagent-development/SKILL.md +225 -0
  219. package/app/skills/subagent-development/reference/code-quality-reviewer-prompt.md +145 -0
  220. package/app/skills/subagent-development/reference/implementer-prompt.md +118 -0
  221. package/app/skills/subagent-development/reference/spec-reviewer-prompt.md +100 -0
  222. package/app/skills/swarm/SKILL.md +81 -0
  223. package/app/skills/swift-patterns/SKILL.md +500 -0
  224. package/app/skills/tdd/SKILL.md +174 -0
  225. package/app/skills/tdd/reference/deep-modules.md +32 -0
  226. package/app/skills/tdd/reference/interface-design.md +32 -0
  227. package/app/skills/tdd/reference/mocking.md +52 -0
  228. package/app/skills/tdd/reference/refactoring.md +10 -0
  229. package/app/skills/tdd/reference/tests.md +59 -0
  230. package/app/skills/teams/SKILL.md +101 -0
  231. package/app/skills/test/SKILL.md +107 -0
  232. package/app/skills/test/scripts/detect-runner.py +113 -0
  233. package/app/skills/testing-patterns/SKILL.md +73 -0
  234. package/app/skills/testing-patterns/reference/flutter-testing.md +33 -0
  235. package/app/skills/testing-patterns/reference/go-testing.md +52 -0
  236. package/app/skills/testing-patterns/reference/php-phpunit.md +39 -0
  237. package/app/skills/testing-patterns/reference/python-pytest.md +228 -0
  238. package/app/skills/testing-patterns/reference/typescript-vitest.md +50 -0
  239. package/app/skills/triage-issue/SKILL.md +120 -0
  240. package/app/skills/typescript-patterns/SKILL.md +256 -0
  241. package/app/skills/ubiquitous-language/SKILL.md +74 -0
  242. package/app/skills/verification-before-completion/SKILL.md +108 -0
  243. package/app/skills/workflow/SKILL.md +250 -0
  244. package/app/skills/write-a-prd/SKILL.md +129 -0
  245. package/app/skills/write-a-prd/reference/visual-companion.md +78 -0
  246. package/app/skills/write-a-prd/scripts/frame-template.html +111 -0
  247. package/app/skills/write-a-prd/scripts/visual-server.cjs +79 -0
  248. package/app/templates/skill/generator/SKILL.md.template +40 -0
  249. package/app/templates/skill/knowledge/SKILL.md.template +52 -0
  250. package/app/templates/skill/linter/SKILL.md.template +34 -0
  251. package/app/templates/skill/reviewer/SKILL.md.template +51 -0
  252. package/app/templates/skill/workflow/SKILL.md.template +49 -0
  253. package/benchmarks/README.md +111 -0
  254. package/benchmarks/ecosystem-dashboard.json +148 -0
  255. package/benchmarks/ecosystem-harvest.json +148 -0
  256. package/benchmarks/results.json +38 -0
  257. package/benchmarks/run.py +351 -0
  258. package/bin/ai-toolkit.js +345 -0
  259. package/kb/best-practices/README.md +11 -0
  260. package/kb/howto/README.md +11 -0
  261. package/kb/procedures/maintenance-sop.md +306 -0
  262. package/kb/reference/agents-catalog.md +124 -0
  263. package/kb/reference/anti-pattern-registry-format.md +221 -0
  264. package/kb/reference/architecture-overview.md +232 -0
  265. package/kb/reference/benchmark-config.md +62 -0
  266. package/kb/reference/ci-integration.md +66 -0
  267. package/kb/reference/claude-ecosystem-benchmark-snapshot.md +80 -0
  268. package/kb/reference/claude-ecosystem-expansion-foundations.md +102 -0
  269. package/kb/reference/commands-catalog.md +21 -0
  270. package/kb/reference/distribution-model.md +63 -0
  271. package/kb/reference/global-install-model.md +56 -0
  272. package/kb/reference/hierarchical-override-pattern.md +200 -0
  273. package/kb/reference/hooks-catalog.md +306 -0
  274. package/kb/reference/integrations.md +88 -0
  275. package/kb/reference/language-packs.md +52 -0
  276. package/kb/reference/merge-friendly-install-model.md +58 -0
  277. package/kb/reference/plugin-pack-conventions.md +151 -0
  278. package/kb/reference/quick-wins-implementation-summary.md +70 -0
  279. package/kb/reference/skill-templates.md +50 -0
  280. package/kb/reference/skills-catalog.md +215 -0
  281. package/kb/reference/skills-unification.md +57 -0
  282. package/kb/reference/stats.md +69 -0
  283. package/kb/reference/sync.md +76 -0
  284. package/kb/troubleshooting/README.md +11 -0
  285. package/llms-full.txt +3068 -0
  286. package/llms.txt +39 -0
  287. package/package.json +75 -0
  288. package/scripts/_common.py +160 -0
  289. package/scripts/add_rule.py +50 -0
  290. package/scripts/benchmark_config.py +127 -0
  291. package/scripts/benchmark_ecosystem.py +288 -0
  292. package/scripts/check_deps.py +260 -0
  293. package/scripts/create_skill.py +118 -0
  294. package/scripts/doctor.py +504 -0
  295. package/scripts/eject.py +113 -0
  296. package/scripts/emission.py +256 -0
  297. package/scripts/evaluate_skills.py +260 -0
  298. package/scripts/frontmatter.py +58 -0
  299. package/scripts/generate_agents_md.py +91 -0
  300. package/scripts/generate_aider_conf.py +51 -0
  301. package/scripts/generate_cline.py +35 -0
  302. package/scripts/generate_copilot.py +30 -0
  303. package/scripts/generate_cursor_rules.py +35 -0
  304. package/scripts/generate_gemini.py +28 -0
  305. package/scripts/generate_llms_txt.py +164 -0
  306. package/scripts/generate_roo_modes.py +80 -0
  307. package/scripts/generate_windsurf.py +35 -0
  308. package/scripts/generator_base.py +140 -0
  309. package/scripts/harvest_ecosystem.py +50 -0
  310. package/scripts/inject_rule_cli.py +101 -0
  311. package/scripts/inject_section_cli.py +47 -0
  312. package/scripts/injection.py +180 -0
  313. package/scripts/install.py +236 -0
  314. package/scripts/install_git_hooks.py +71 -0
  315. package/scripts/install_steps/__init__.py +5 -0
  316. package/scripts/install_steps/ai_tools.py +261 -0
  317. package/scripts/install_steps/hooks.py +90 -0
  318. package/scripts/install_steps/markers.py +79 -0
  319. package/scripts/install_steps/symlinks.py +87 -0
  320. package/scripts/merge-hooks.py +192 -0
  321. package/scripts/plugin.py +642 -0
  322. package/scripts/plugin_schema.py +138 -0
  323. package/scripts/remove_rule.py +58 -0
  324. package/scripts/stats.py +81 -0
  325. package/scripts/sync.py +215 -0
  326. package/scripts/uninstall.py +292 -0
  327. package/scripts/validate.py +700 -0
@@ -0,0 +1,292 @@
1
+ #!/usr/bin/env python3
2
+ """AI Toolkit Uninstaller.
3
+
4
+ Removes toolkit symlinks from ~/.claude/ (global install).
5
+
6
+ Handles both old-style (whole-directory symlinks) and new-style
7
+ (per-file symlinks inside agents/ and skills/ directories).
8
+ User-owned files are never removed.
9
+
10
+ Usage:
11
+ python3 scripts/uninstall.py [--yes] [target-dir]
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import re
16
+ import subprocess
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
21
+ from _common import toolkit_dir, app_dir
22
+
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # Helpers
26
+ # ---------------------------------------------------------------------------
27
+
28
+ def _is_toolkit_link(link_target: str) -> bool:
29
+ """Check if a symlink target points into the ai-toolkit app directory."""
30
+ return str(app_dir) in link_target or "/ai-toolkit/app/" in link_target
31
+
32
+
33
+ def _strip_all_toolkit_markers(filepath: Path) -> str | None:
34
+ """Remove all TOOLKIT marker sections from a file.
35
+
36
+ Returns the remaining content, or None if file doesn't exist.
37
+ """
38
+ if not filepath.is_file():
39
+ return None
40
+
41
+ content = filepath.read_text(encoding="utf-8")
42
+ if "<!-- TOOLKIT:" not in content:
43
+ return content
44
+
45
+ lines: list[str] = []
46
+ skip = False
47
+ for line in content.splitlines(keepends=True):
48
+ stripped = line.rstrip("\n")
49
+ if re.match(r"^<!-- TOOLKIT:\S+ START -->$", stripped):
50
+ skip = True
51
+ continue
52
+ if re.match(r"^<!-- TOOLKIT:\S+ END -->$", stripped):
53
+ skip = False
54
+ continue
55
+ if not skip:
56
+ lines.append(line)
57
+
58
+ result = "".join(lines).strip()
59
+ return result
60
+
61
+
62
+ # ---------------------------------------------------------------------------
63
+ # Discovery: count what would be removed
64
+ # ---------------------------------------------------------------------------
65
+
66
+ def discover_components(claude_dir: Path) -> list[tuple[str, str]]:
67
+ """Find all toolkit components installed in claude_dir.
68
+
69
+ Returns a list of (description, type) tuples.
70
+ """
71
+ found: list[tuple[str, str]] = []
72
+
73
+ # Check old-style directory symlinks (backward compat)
74
+ for item in ("agents", "skills"):
75
+ target = claude_dir / item
76
+ if target.is_symlink():
77
+ link_target = str(target.resolve())
78
+ found.append((f"Symlink: {item} -> {link_target} (directory)", "old-dir"))
79
+
80
+ # Check per-file agent symlinks (new-style)
81
+ agents_dir = claude_dir / "agents"
82
+ if agents_dir.is_dir() and not agents_dir.is_symlink():
83
+ for agent in sorted(agents_dir.glob("*.md")):
84
+ if not agent.is_symlink():
85
+ continue
86
+ link_target = str(agent.readlink())
87
+ if _is_toolkit_link(link_target):
88
+ found.append(
89
+ (f"Symlink: agents/{agent.name} -> {link_target}", "agent-link")
90
+ )
91
+
92
+ # Check per-directory skill symlinks (new-style)
93
+ skills_dir = claude_dir / "skills"
94
+ if skills_dir.is_dir() and not skills_dir.is_symlink():
95
+ for skill in sorted(skills_dir.iterdir()):
96
+ if not skill.is_symlink():
97
+ continue
98
+ link_target = str(skill.readlink())
99
+ if _is_toolkit_link(link_target):
100
+ found.append(
101
+ (f"Symlink: skills/{skill.name}/ -> {link_target}", "skill-link")
102
+ )
103
+
104
+ # Check hooks.json for toolkit entries (merged, not symlinked)
105
+ hooks_file = claude_dir / "hooks.json"
106
+ if hooks_file.is_symlink():
107
+ found.append(
108
+ (f"Symlink: hooks.json -> {hooks_file.readlink()} (legacy)", "hooks-link")
109
+ )
110
+ elif hooks_file.is_file():
111
+ content = hooks_file.read_text(encoding="utf-8")
112
+ if '"_source"' in content and '"ai-toolkit"' in content:
113
+ count = content.count('"ai-toolkit"')
114
+ found.append(
115
+ (f"Merged: hooks.json ({count} toolkit entries)", "hooks-merged")
116
+ )
117
+
118
+ # Check marker-injected files (constitution.md, ARCHITECTURE.md)
119
+ for item in ("constitution.md", "ARCHITECTURE.md"):
120
+ target = claude_dir / item
121
+ if target.is_symlink():
122
+ found.append(
123
+ (f"Symlink: {item} -> {target.readlink()} (legacy)", "marker-link")
124
+ )
125
+ elif target.is_file():
126
+ content = target.read_text(encoding="utf-8")
127
+ if "<!-- TOOLKIT:" in content:
128
+ found.append((f"Injected: {item} (marker-based)", "marker-inject"))
129
+
130
+ # Check legacy commands symlink
131
+ commands = claude_dir / "commands"
132
+ if commands.is_symlink():
133
+ found.append(
134
+ (f"Symlink: commands -> {commands.readlink()} (legacy)", "old-dir")
135
+ )
136
+
137
+ return found
138
+
139
+
140
+ # ---------------------------------------------------------------------------
141
+ # Removal
142
+ # ---------------------------------------------------------------------------
143
+
144
+ def remove_components(claude_dir: Path) -> None:
145
+ """Remove all toolkit components from claude_dir."""
146
+
147
+ # Remove old-style directory symlinks (backward compat)
148
+ for item in ("agents", "skills", "commands"):
149
+ target = claude_dir / item
150
+ if target.is_symlink():
151
+ target.unlink()
152
+ print(f" Removed: {item} (directory symlink)")
153
+
154
+ # Remove per-file agent symlinks (only those pointing into toolkit)
155
+ agents_dir = claude_dir / "agents"
156
+ if agents_dir.is_dir() and not agents_dir.is_symlink():
157
+ removed = 0
158
+ for agent in sorted(agents_dir.glob("*.md")):
159
+ if not agent.is_symlink():
160
+ continue
161
+ link_target = str(agent.readlink())
162
+ if _is_toolkit_link(link_target):
163
+ agent.unlink()
164
+ removed += 1
165
+ if removed > 0:
166
+ print(f" Removed: {removed} agent symlink(s)")
167
+ # Remove the agents dir if it is now empty
168
+ try:
169
+ agents_dir.rmdir()
170
+ print(" Removed: agents/ (empty)")
171
+ except OSError:
172
+ pass # Not empty -- user has custom agents
173
+
174
+ # Remove per-directory skill symlinks (only those pointing into toolkit)
175
+ skills_dir = claude_dir / "skills"
176
+ if skills_dir.is_dir() and not skills_dir.is_symlink():
177
+ removed = 0
178
+ for skill in sorted(skills_dir.iterdir()):
179
+ if not skill.is_symlink():
180
+ continue
181
+ link_target = str(skill.readlink())
182
+ if _is_toolkit_link(link_target):
183
+ skill.unlink()
184
+ removed += 1
185
+ if removed > 0:
186
+ print(f" Removed: {removed} skill symlink(s)")
187
+ # Remove the skills dir if it is now empty
188
+ try:
189
+ skills_dir.rmdir()
190
+ print(" Removed: skills/ (empty)")
191
+ except OSError:
192
+ pass # Not empty -- user has custom skills
193
+
194
+ # Remove toolkit hooks from hooks.json (or remove legacy symlink)
195
+ hooks_file = claude_dir / "hooks.json"
196
+ if hooks_file.is_symlink():
197
+ hooks_file.unlink()
198
+ print(" Removed: hooks.json (legacy symlink)")
199
+ elif hooks_file.is_file():
200
+ content = hooks_file.read_text(encoding="utf-8")
201
+ if '"_source"' in content and '"ai-toolkit"' in content:
202
+ merge_hooks = toolkit_dir / "scripts" / "merge-hooks.py"
203
+ subprocess.run(
204
+ ["python3", str(merge_hooks), "strip", str(hooks_file)],
205
+ check=True,
206
+ )
207
+ if hooks_file.is_file():
208
+ print(" Stripped: hooks.json (toolkit entries removed, user hooks preserved)")
209
+ else:
210
+ print(" Removed: hooks.json (no user hooks remaining)")
211
+
212
+ # Remove marker-injected content from constitution.md, ARCHITECTURE.md
213
+ for item in ("constitution.md", "ARCHITECTURE.md"):
214
+ target = claude_dir / item
215
+ if target.is_symlink():
216
+ target.unlink()
217
+ print(f" Removed: {item} (legacy symlink)")
218
+ elif target.is_file():
219
+ content = target.read_text(encoding="utf-8")
220
+ if "<!-- TOOLKIT:" in content:
221
+ remaining = _strip_all_toolkit_markers(target)
222
+ if remaining and remaining.strip():
223
+ target.write_text(remaining + "\n", encoding="utf-8")
224
+ print(f" Stripped: {item} (toolkit content removed, user content preserved)")
225
+ else:
226
+ target.unlink()
227
+ print(f" Removed: {item} (no user content remaining)")
228
+
229
+
230
+ # ---------------------------------------------------------------------------
231
+ # Main
232
+ # ---------------------------------------------------------------------------
233
+
234
+ def main() -> None:
235
+ force = False
236
+ target_dir = Path.home()
237
+
238
+ for arg in sys.argv[1:]:
239
+ if arg in ("--yes", "-y"):
240
+ force = True
241
+ else:
242
+ target_dir = Path(arg)
243
+
244
+ claude_dir = target_dir / ".claude"
245
+
246
+ if not claude_dir.is_dir():
247
+ print(f"Error: No .claude/ directory found in {target_dir}")
248
+ sys.exit(1)
249
+
250
+ print("AI Toolkit Uninstaller")
251
+ print("==========================")
252
+ print(f"Target: {claude_dir}")
253
+ print()
254
+
255
+ # -- Count components to remove ---
256
+ components = discover_components(claude_dir)
257
+
258
+ for desc, _ in components:
259
+ print(f" {desc}")
260
+
261
+ if not components:
262
+ print("No toolkit components found. Nothing to remove.")
263
+ sys.exit(0)
264
+
265
+ print()
266
+ print(f"Found {len(components)} toolkit component(s).")
267
+ print("Note: ~/.claude/CLAUDE.md and settings.local.json are NOT removed.")
268
+ print()
269
+
270
+ # -- Confirm ---
271
+ if not force:
272
+ try:
273
+ response = input("Remove these components? [y/N] ").strip().lower()
274
+ except (EOFError, KeyboardInterrupt):
275
+ print("\nCancelled.")
276
+ sys.exit(0)
277
+ if response not in ("y", "yes"):
278
+ print("Cancelled.")
279
+ sys.exit(0)
280
+
281
+ # -- Remove ---
282
+ remove_components(claude_dir)
283
+
284
+ print()
285
+ print("Toolkit components removed from ~/.claude/ successfully.")
286
+ print(f"{Path.home()}/.claude/CLAUDE.md preserved (contains your global rules).")
287
+ print()
288
+ print("To reinstall: npm install -g @softspark/ai-toolkit && ai-toolkit install")
289
+
290
+
291
+ if __name__ == "__main__":
292
+ main()