@softspark/ai-toolkit 4.12.0 → 4.13.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 +30 -0
- package/README.md +33 -15
- package/app/.claude-plugin/plugin.json +11 -19
- package/app/ARCHITECTURE.md +6 -6
- package/app/claude-app/global-instructions.md +10 -0
- package/app/claude-app/hooks/hooks.json +284 -0
- package/app/claude-app/skills/ai-toolkit-rules/SKILL.md +359 -0
- package/app/hooks/config-desync-guard.sh +63 -23
- package/app/plugins/README.md +4 -1
- package/benchmarks/ecosystem-doctor-snapshot.json +91 -25
- package/bin/ai-toolkit.js +21 -3
- package/kb/planning/drop-cascade-hooks-after-sunset.md +13 -8
- package/kb/procedures/ecosystem-sync-sop.md +5 -5
- package/kb/procedures/maintenance-sop.md +42 -4
- package/kb/procedures/release-preparation-sop.md +5 -1
- package/kb/procedures/release-verification-sop.md +25 -9
- package/kb/reference/architecture-overview.md +7 -2
- package/kb/reference/claude-ecosystem-expansion-foundations.md +12 -3
- package/kb/reference/cli-reference.md +4 -2
- package/kb/reference/global-install-model.md +24 -3
- package/kb/reference/hooks-catalog.md +2 -3
- package/kb/reference/plugin-pack-conventions.md +5 -5
- package/kb/reference/skills-catalog.md +2 -0
- package/kb/reference/supported-tools-registry.md +30 -13
- package/kb/reference/unique-features.md +3 -2
- package/llms-full.txt +179 -60
- package/manifest.json +8 -8
- package/package.json +4 -3
- package/scripts/claude_app.py +347 -0
- package/scripts/doctor.py +85 -4
- package/scripts/ecosystem_tools.json +33 -5
- package/scripts/generate_devin_hooks.py +3 -4
- package/scripts/generate_windsurf_skills.py +5 -6
- package/scripts/install.py +23 -21
- package/scripts/install_steps/ai_tools.py +96 -6
- package/scripts/install_steps/install_state.py +2 -0
- package/scripts/update_projects.py +12 -1
- package/scripts/validate.py +34 -2
- package/AGENTS.md +0 -655
- package/scripts/generate_windsurf_hooks.py +0 -152
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Generate .windsurf/hooks.json for Windsurf Cascade.
|
|
3
|
-
|
|
4
|
-
DEPRECATED SURFACE: the Cascade agent is available only through 2026-07-01
|
|
5
|
-
(Devin Local is the default agent since the 2026-06-02 Devin Desktop rebrand),
|
|
6
|
-
and this hooks.json schema is Cascade-scoped. Migrate to the Devin CLI
|
|
7
|
-
lifecycle-hooks surface (docs.devin.ai/cli/extensibility/hooks/*) before that
|
|
8
|
-
date; tracked in the ecosystem registry status_note for windsurf.
|
|
9
|
-
|
|
10
|
-
Writes `<target>/.windsurf/hooks.json`. Existing user hook entries are
|
|
11
|
-
preserved; only entries tagged `_source: ai-toolkit` are replaced on
|
|
12
|
-
regeneration. Windsurf merges system / user / workspace hooks at runtime, so
|
|
13
|
-
this file is safe to live alongside `~/.codeium/windsurf/hooks.json`.
|
|
14
|
-
|
|
15
|
-
Windsurf Cascade events (per docs.windsurf.com/windsurf/cascade/hooks.md):
|
|
16
|
-
pre_read_code, post_read_code, pre_write_code, post_write_code,
|
|
17
|
-
pre_run_command, post_run_command, pre_mcp_tool_use, post_mcp_tool_use,
|
|
18
|
-
pre_user_prompt, post_cascade_response,
|
|
19
|
-
post_cascade_response_with_transcript, post_setup_worktree
|
|
20
|
-
(12 total).
|
|
21
|
-
|
|
22
|
-
Pre-hooks can block via exit code 2 (see `guard-destructive.sh`). Each entry
|
|
23
|
-
takes `command` (macOS/Linux) + optional `powershell` (Windows). We only emit
|
|
24
|
-
`command` because our hook scripts are bash-only.
|
|
25
|
-
|
|
26
|
-
Usage:
|
|
27
|
-
python3 scripts/generate_windsurf_hooks.py [target-dir]
|
|
28
|
-
"""
|
|
29
|
-
from __future__ import annotations
|
|
30
|
-
|
|
31
|
-
import json
|
|
32
|
-
import sys
|
|
33
|
-
from pathlib import Path
|
|
34
|
-
|
|
35
|
-
HOOKS_PREFIX = '"$HOME/.softspark/ai-toolkit/hooks/'
|
|
36
|
-
SOURCE_TAG = "ai-toolkit"
|
|
37
|
-
|
|
38
|
-
# Event -> list of script names.
|
|
39
|
-
WINDSURF_HOOKS: dict[str, list[str]] = {
|
|
40
|
-
"pre_read_code": [
|
|
41
|
-
"guard-path.sh",
|
|
42
|
-
],
|
|
43
|
-
"pre_write_code": [
|
|
44
|
-
"guard-path.sh",
|
|
45
|
-
"guard-config.sh",
|
|
46
|
-
],
|
|
47
|
-
"post_write_code": [
|
|
48
|
-
"post-tool-use.sh",
|
|
49
|
-
"governance-capture.sh",
|
|
50
|
-
"test-cohesion.sh",
|
|
51
|
-
],
|
|
52
|
-
"pre_run_command": [
|
|
53
|
-
"guard-destructive.sh",
|
|
54
|
-
"commit-quality.sh",
|
|
55
|
-
"revert-guard.sh",
|
|
56
|
-
],
|
|
57
|
-
"post_run_command": [
|
|
58
|
-
"governance-capture.sh",
|
|
59
|
-
],
|
|
60
|
-
"pre_mcp_tool_use": [
|
|
61
|
-
"guard-config.sh",
|
|
62
|
-
],
|
|
63
|
-
"post_mcp_tool_use": [
|
|
64
|
-
"search-tracker.sh",
|
|
65
|
-
],
|
|
66
|
-
"pre_user_prompt": [
|
|
67
|
-
"user-prompt-submit.sh",
|
|
68
|
-
"track-usage.sh",
|
|
69
|
-
],
|
|
70
|
-
"post_cascade_response": [
|
|
71
|
-
"quality-check.sh",
|
|
72
|
-
"save-session.sh",
|
|
73
|
-
"stop-search-check.sh",
|
|
74
|
-
],
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def build_hook_entry(script: str) -> dict:
|
|
79
|
-
"""Build a single Windsurf hook entry.
|
|
80
|
-
|
|
81
|
-
Uses `bash -c` semantics via the raw command (Windsurf already runs the
|
|
82
|
-
macOS/Linux `command` via `bash -c`).
|
|
83
|
-
"""
|
|
84
|
-
return {
|
|
85
|
-
"_source": SOURCE_TAG,
|
|
86
|
-
"command": f"{HOOKS_PREFIX}{script}\"",
|
|
87
|
-
"show_output": False,
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def build_toolkit_hooks() -> dict[str, list[dict]]:
|
|
92
|
-
return {event: [build_hook_entry(s) for s in scripts]
|
|
93
|
-
for event, scripts in WINDSURF_HOOKS.items()}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def _is_toolkit_entry(entry: dict) -> bool:
|
|
97
|
-
return isinstance(entry, dict) and entry.get("_source") == SOURCE_TAG
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def strip_toolkit_hooks(hooks: dict) -> dict:
|
|
101
|
-
kept: dict = {}
|
|
102
|
-
for event, entries in hooks.items():
|
|
103
|
-
if not isinstance(entries, list):
|
|
104
|
-
kept[event] = entries
|
|
105
|
-
continue
|
|
106
|
-
survivors = [e for e in entries if not _is_toolkit_entry(e)]
|
|
107
|
-
if survivors:
|
|
108
|
-
kept[event] = survivors
|
|
109
|
-
return kept
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def merge_hooks(existing: dict, toolkit: dict) -> dict:
|
|
113
|
-
merged = strip_toolkit_hooks(existing)
|
|
114
|
-
for event, entries in toolkit.items():
|
|
115
|
-
merged.setdefault(event, []).extend(entries)
|
|
116
|
-
return merged
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
def generate(target_dir: Path) -> Path:
|
|
120
|
-
ws_dir = target_dir / ".windsurf"
|
|
121
|
-
ws_dir.mkdir(parents=True, exist_ok=True)
|
|
122
|
-
path = ws_dir / "hooks.json"
|
|
123
|
-
|
|
124
|
-
doc: dict = {}
|
|
125
|
-
if path.is_file():
|
|
126
|
-
try:
|
|
127
|
-
with open(path, encoding="utf-8") as f:
|
|
128
|
-
doc = json.load(f)
|
|
129
|
-
if not isinstance(doc, dict):
|
|
130
|
-
doc = {}
|
|
131
|
-
except (json.JSONDecodeError, OSError):
|
|
132
|
-
doc = {}
|
|
133
|
-
|
|
134
|
-
existing_hooks = doc.get("hooks") if isinstance(doc.get("hooks"), dict) else {}
|
|
135
|
-
doc["hooks"] = merge_hooks(existing_hooks or {}, build_toolkit_hooks())
|
|
136
|
-
|
|
137
|
-
with open(path, "w", encoding="utf-8") as f:
|
|
138
|
-
json.dump(doc, f, indent=4, ensure_ascii=False, sort_keys=True)
|
|
139
|
-
f.write("\n")
|
|
140
|
-
return path
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
def main() -> None:
|
|
144
|
-
target = Path(sys.argv[1]) if len(sys.argv) > 1 else Path.cwd()
|
|
145
|
-
path = generate(target)
|
|
146
|
-
total = sum(len(v) for v in WINDSURF_HOOKS.values())
|
|
147
|
-
print(f"Generated: {path.relative_to(target) if path.is_relative_to(target) else path} "
|
|
148
|
-
f"({total} hooks across {len(WINDSURF_HOOKS)} events)")
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if __name__ == "__main__":
|
|
152
|
-
main()
|