@softspark/ai-toolkit 2.11.0 → 3.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.
- package/AGENTS.md +21 -21
- package/CHANGELOG.md +62 -0
- package/README.md +16 -9
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/agents/meta-architect.md +44 -0
- package/app/skills/agent-creator/SKILL.md +22 -6
- package/app/skills/analyze/SKILL.md +21 -1
- package/app/skills/api-patterns/SKILL.md +27 -0
- package/app/skills/app-builder/SKILL.md +24 -0
- package/app/skills/architecture-audit/SKILL.md +24 -0
- package/app/skills/architecture-decision/SKILL.md +24 -0
- package/app/skills/biz-scan/SKILL.md +24 -0
- package/app/skills/brand-voice/SKILL.md +36 -0
- package/app/skills/briefing/SKILL.md +41 -1
- package/app/skills/build/SKILL.md +24 -1
- package/app/skills/chaos/SKILL.md +22 -1
- package/app/skills/ci/SKILL.md +22 -1
- package/app/skills/ci-cd-patterns/SKILL.md +25 -0
- package/app/skills/command-creator/SKILL.md +31 -2
- package/app/skills/content-moderation-patterns/SKILL.md +25 -0
- package/app/skills/database-patterns/SKILL.md +25 -0
- package/app/skills/debug/SKILL.md +23 -1
- package/app/skills/design-engineering/SKILL.md +25 -0
- package/app/skills/docker-devops/SKILL.md +26 -0
- package/app/skills/ecommerce-patterns/SKILL.md +26 -0
- package/app/skills/evaluate/SKILL.md +22 -1
- package/app/skills/evolve/SKILL.md +25 -0
- package/app/skills/explain/SKILL.md +26 -1
- package/app/skills/explore/SKILL.md +25 -0
- package/app/skills/fix/SKILL.md +26 -1
- package/app/skills/git-mastery/SKILL.md +26 -0
- package/app/skills/grill-me/SKILL.md +22 -7
- package/app/skills/health/SKILL.md +22 -1
- package/app/skills/hipaa-validate/SKILL.md +25 -8
- package/app/skills/hook-creator/SKILL.md +107 -14
- package/app/skills/index/SKILL.md +22 -1
- package/app/skills/instinct-review/SKILL.md +25 -1
- package/app/skills/introspect/SKILL.md +26 -1
- package/app/skills/lint/SKILL.md +27 -1
- package/app/skills/mcp-builder/SKILL.md +27 -0
- package/app/skills/mem-search/SKILL.md +40 -0
- package/app/skills/migrate/SKILL.md +27 -1
- package/app/skills/migration-patterns/SKILL.md +26 -0
- package/app/skills/night-watch/SKILL.md +22 -1
- package/app/skills/observability-patterns/SKILL.md +26 -0
- package/app/skills/onboard/SKILL.md +22 -1
- package/app/skills/panic/SKILL.md +21 -1
- package/app/skills/performance-profiling/SKILL.md +34 -0
- package/app/skills/persona/SKILL.md +21 -4
- package/app/skills/plan/SKILL.md +26 -0
- package/app/skills/plan-writing/SKILL.md +26 -0
- package/app/skills/plugin-creator/SKILL.md +23 -6
- package/app/skills/pr/SKILL.md +24 -1
- package/app/skills/prd-to-issues/SKILL.md +22 -6
- package/app/skills/prd-to-plan/SKILL.md +22 -4
- package/app/skills/predict/SKILL.md +25 -0
- package/app/skills/qa-session/SKILL.md +23 -7
- package/app/skills/rag-patterns/SKILL.md +26 -0
- package/app/skills/refactor/SKILL.md +26 -0
- package/app/skills/refactor-plan/SKILL.md +23 -6
- package/app/skills/rollback/SKILL.md +27 -1
- package/app/skills/security-patterns/SKILL.md +27 -0
- package/app/skills/skill-audit/SKILL.md +22 -5
- package/app/skills/skill-creator/SKILL.md +58 -13
- package/app/skills/swift-patterns/SKILL.md +27 -107
- package/app/skills/swift-patterns/reference/frameworks.md +111 -0
- package/app/skills/test/SKILL.md +23 -1
- package/app/skills/testing-patterns/SKILL.md +26 -0
- package/app/skills/triage-issue/SKILL.md +22 -4
- package/app/skills/ubiquitous-language/SKILL.md +23 -8
- package/benchmarks/ecosystem-doctor-snapshot.json +395 -0
- package/kb/history/completed/deep-coverage-v3-20260423.md +160 -0
- package/kb/history/completed/ecosystem-deep-sweep-20260423.md +273 -0
- package/kb/procedures/ecosystem-sync-sop.md +255 -0
- package/kb/procedures/maintenance-sop.md +13 -2
- package/kb/procedures/release-preparation-sop.md +91 -12
- package/kb/procedures/release-verification-sop.md +24 -6
- package/kb/reference/global-install-model.md +15 -2
- package/kb/reference/supported-tools-registry.md +229 -0
- package/llms-full.txt +1105 -43
- package/llms.txt +4 -0
- package/manifest.json +1 -1
- package/package.json +4 -1
- package/scripts/ecosystem_doctor.py +348 -0
- package/scripts/ecosystem_tools.json +500 -0
- package/scripts/evaluate_skills.py +84 -0
- package/scripts/generate_aider_conf.py +26 -1
- package/scripts/generate_antigravity.py +77 -8
- package/scripts/generate_augment_agents.py +161 -0
- package/scripts/generate_augment_commands.py +160 -0
- package/scripts/generate_augment_hooks.py +162 -0
- package/scripts/generate_augment_skills.py +98 -0
- package/scripts/generate_cline_rules.py +96 -9
- package/scripts/generate_codex_hooks.py +13 -2
- package/scripts/generate_codex_skills.py +195 -0
- package/scripts/generate_copilot.py +296 -18
- package/scripts/generate_cursor_agents.py +144 -0
- package/scripts/generate_cursor_hooks.py +155 -0
- package/scripts/generate_cursor_mdc.py +20 -8
- package/scripts/generate_gemini_commands.py +158 -0
- package/scripts/generate_gemini_hooks.py +159 -0
- package/scripts/generate_gemini_skills.py +98 -0
- package/scripts/generate_roo_modes.py +42 -1
- package/scripts/generate_windsurf_hooks.py +143 -0
- package/scripts/generate_windsurf_rules.py +162 -10
- package/scripts/install.py +11 -2
- package/scripts/install_steps/ai_tools.py +120 -5
- package/scripts/validate.py +20 -3
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Generate
|
|
2
|
+
"""Generate ``.windsurf/rules/*.md`` (and workflows) for Windsurf IDE.
|
|
3
3
|
|
|
4
|
-
Windsurf reads directory-based rules from
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
Windsurf reads directory-based rules from ``.windsurf/rules/*.md`` (since
|
|
5
|
+
mid-2025). Each rule file supports YAML frontmatter with these fields
|
|
6
|
+
(from docs.windsurf.com/windsurf/cascade/memories):
|
|
7
|
+
|
|
8
|
+
* ``trigger`` — activation mode: ``always_on`` | ``glob`` | ``model_decision``
|
|
9
|
+
| ``manual``. When omitted, Windsurf defaults to manual-only.
|
|
10
|
+
* ``globs`` — comma-separated glob patterns (only when ``trigger: glob``).
|
|
11
|
+
* ``description`` — shown to the model when ``trigger: model_decision``.
|
|
12
|
+
|
|
13
|
+
Windsurf also reads workflow markdown files from ``.windsurf/workflows/*.md``
|
|
14
|
+
which users invoke via ``/<name>`` slash commands (Cascade). This generator
|
|
15
|
+
emits the same workflow catalogue used by Antigravity and Cline.
|
|
16
|
+
|
|
17
|
+
The legacy ``.windsurfrules`` single-file format is still produced by
|
|
18
|
+
``generate_windsurf.py`` for backwards compatibility.
|
|
7
19
|
|
|
8
20
|
Usage:
|
|
9
21
|
python3 scripts/generate_windsurf_rules.py [target-dir]
|
|
@@ -15,21 +27,161 @@ from pathlib import Path
|
|
|
15
27
|
|
|
16
28
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
17
29
|
from dir_rules_shared import (
|
|
30
|
+
CUSTOM_SCOPE,
|
|
31
|
+
LANG_GLOBS,
|
|
32
|
+
LANG_PREFIX,
|
|
33
|
+
LANG_SCOPE,
|
|
34
|
+
PREFIX,
|
|
18
35
|
STANDARD_RULES,
|
|
36
|
+
STANDARD_SCOPE,
|
|
37
|
+
STANDARD_WORKFLOWS,
|
|
19
38
|
build_language_rules,
|
|
20
39
|
build_registered_rules,
|
|
40
|
+
cleanup_stale,
|
|
41
|
+
rule_testing,
|
|
21
42
|
write_rules,
|
|
22
43
|
)
|
|
23
44
|
|
|
24
45
|
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
# Frontmatter helpers — Windsurf rule activation modes
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
def _frontmatter(content: str, *,
|
|
51
|
+
trigger: str,
|
|
52
|
+
description: str = "",
|
|
53
|
+
globs: list[str] | None = None) -> str:
|
|
54
|
+
"""Prepend a Windsurf YAML frontmatter block to rule content.
|
|
55
|
+
|
|
56
|
+
``trigger`` must be one of: ``always_on``, ``glob``, ``model_decision``,
|
|
57
|
+
``manual``. ``description`` is shown to the model for ``model_decision``
|
|
58
|
+
mode; ignored otherwise. ``globs`` is only meaningful for ``glob`` mode.
|
|
59
|
+
"""
|
|
60
|
+
lines = ["---", f"trigger: {trigger}"]
|
|
61
|
+
if description:
|
|
62
|
+
lines.append(f"description: {description}")
|
|
63
|
+
if trigger == "glob" and globs:
|
|
64
|
+
lines.append("globs: " + ",".join(globs))
|
|
65
|
+
lines.append("---")
|
|
66
|
+
lines.append("")
|
|
67
|
+
lines.append(content.rstrip("\n"))
|
|
68
|
+
lines.append("")
|
|
69
|
+
return "\n".join(lines)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _always_on(content_fn):
|
|
73
|
+
"""Wrap a content callable so Windsurf treats it as always-on."""
|
|
74
|
+
return lambda: _frontmatter(content_fn(), trigger="always_on")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _glob_rule(content_fn, globs: list[str]):
|
|
78
|
+
"""Wrap a content callable for Windsurf's glob activation mode."""
|
|
79
|
+
return lambda: _frontmatter(content_fn(), trigger="glob", globs=globs)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _model_decision(content_fn, description: str):
|
|
83
|
+
"""Wrap a content callable for Windsurf's model-decision activation."""
|
|
84
|
+
return lambda: _frontmatter(
|
|
85
|
+
content_fn(), trigger="model_decision", description=description,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _build_rules(language_modules: list[str] | None,
|
|
90
|
+
rules_dir: Path | None) -> dict[str, callable]:
|
|
91
|
+
"""Build the full rule registry with per-rule activation frontmatter."""
|
|
92
|
+
rules: dict[str, callable] = {}
|
|
93
|
+
|
|
94
|
+
# Standard rules: agents/skills index + quality/security are always-on.
|
|
95
|
+
# Code style + testing + workflow are model-decision so Cascade only
|
|
96
|
+
# expands them when relevant.
|
|
97
|
+
always_on_names = {
|
|
98
|
+
f"{PREFIX}agents-and-skills.md",
|
|
99
|
+
f"{PREFIX}security.md",
|
|
100
|
+
f"{PREFIX}quality-standards.md",
|
|
101
|
+
}
|
|
102
|
+
model_decision_descs = {
|
|
103
|
+
f"{PREFIX}code-style.md":
|
|
104
|
+
"Code style conventions for all languages",
|
|
105
|
+
f"{PREFIX}workflow.md":
|
|
106
|
+
"Development workflow, commits, and quality gates",
|
|
107
|
+
}
|
|
108
|
+
testing_globs = ["**/*.test.*", "**/*.spec.*", "**/test_*", "**/tests/**"]
|
|
109
|
+
|
|
110
|
+
for filename, content_fn in STANDARD_RULES.items():
|
|
111
|
+
if filename in always_on_names:
|
|
112
|
+
rules[filename] = _always_on(content_fn)
|
|
113
|
+
elif filename == f"{PREFIX}testing.md":
|
|
114
|
+
rules[filename] = _glob_rule(content_fn, testing_globs)
|
|
115
|
+
elif filename in model_decision_descs:
|
|
116
|
+
rules[filename] = _model_decision(
|
|
117
|
+
content_fn, model_decision_descs[filename],
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
rules[filename] = _always_on(content_fn)
|
|
121
|
+
|
|
122
|
+
# Language rules: scope to their own file globs so they activate only
|
|
123
|
+
# when touching matching files.
|
|
124
|
+
for filename, content_fn in build_language_rules(language_modules).items():
|
|
125
|
+
lang = filename.removeprefix(LANG_PREFIX).removesuffix(".md")
|
|
126
|
+
if lang == "common":
|
|
127
|
+
rules[filename] = _always_on(content_fn)
|
|
128
|
+
continue
|
|
129
|
+
globs = LANG_GLOBS.get(lang)
|
|
130
|
+
if globs:
|
|
131
|
+
rules[filename] = _glob_rule(content_fn, globs)
|
|
132
|
+
else:
|
|
133
|
+
rules[filename] = _always_on(content_fn)
|
|
134
|
+
|
|
135
|
+
# Custom user-registered rules — always-on by default (user intent).
|
|
136
|
+
rules.update({
|
|
137
|
+
name: _always_on(fn)
|
|
138
|
+
for name, fn in build_registered_rules(rules_dir).items()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
return rules
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# ---------------------------------------------------------------------------
|
|
145
|
+
# Workflows — .windsurf/workflows/<name>.md invocable via /<name>
|
|
146
|
+
# ---------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
def _write_workflows(target_dir: Path, *, cleanup: bool = True) -> None:
|
|
149
|
+
"""Write ``.windsurf/workflows/*.md`` files for Cascade slash commands."""
|
|
150
|
+
workflows_dir = target_dir / ".windsurf" / "workflows"
|
|
151
|
+
workflows_dir.mkdir(parents=True, exist_ok=True)
|
|
152
|
+
|
|
153
|
+
if cleanup:
|
|
154
|
+
cleanup_stale(workflows_dir, set(STANDARD_WORKFLOWS.keys()))
|
|
155
|
+
|
|
156
|
+
for filename, content_fn in STANDARD_WORKFLOWS.items():
|
|
157
|
+
(workflows_dir / filename).write_text(content_fn(), encoding="utf-8")
|
|
158
|
+
print(f" Generated: .windsurf/workflows/{filename}")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# ---------------------------------------------------------------------------
|
|
162
|
+
# Main
|
|
163
|
+
# ---------------------------------------------------------------------------
|
|
164
|
+
|
|
25
165
|
def generate(target_dir: Path, *,
|
|
26
166
|
language_modules: list[str] | None = None,
|
|
27
|
-
rules_dir: Path | None = None
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
167
|
+
rules_dir: Path | None = None,
|
|
168
|
+
cleanup: bool = True,
|
|
169
|
+
emit_workflows: bool = True,
|
|
170
|
+
managed_scopes: tuple[str, ...] = (
|
|
171
|
+
STANDARD_SCOPE, LANG_SCOPE, CUSTOM_SCOPE,
|
|
172
|
+
)) -> None:
|
|
173
|
+
"""Write ``.windsurf/rules/*.md`` and ``.windsurf/workflows/*.md``."""
|
|
174
|
+
rules = _build_rules(language_modules, rules_dir)
|
|
175
|
+
write_rules(
|
|
176
|
+
target_dir,
|
|
177
|
+
rules,
|
|
178
|
+
".windsurf/rules",
|
|
179
|
+
cleanup=cleanup,
|
|
180
|
+
managed_scopes=managed_scopes,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if emit_workflows:
|
|
184
|
+
_write_workflows(target_dir, cleanup=cleanup)
|
|
33
185
|
|
|
34
186
|
|
|
35
187
|
def main() -> None:
|
package/scripts/install.py
CHANGED
|
@@ -216,6 +216,7 @@ def parse_args(argv: list[str]) -> dict:
|
|
|
216
216
|
"config": "",
|
|
217
217
|
"refresh_base": False,
|
|
218
218
|
"skip_register": False,
|
|
219
|
+
"codex_skills": False,
|
|
219
220
|
}
|
|
220
221
|
i = 0
|
|
221
222
|
while i < len(argv):
|
|
@@ -274,6 +275,8 @@ def parse_args(argv: list[str]) -> dict:
|
|
|
274
275
|
cfg["refresh_base"] = True
|
|
275
276
|
elif arg == "--skip-register":
|
|
276
277
|
cfg["skip_register"] = True
|
|
278
|
+
elif arg == "--codex-skills":
|
|
279
|
+
cfg["codex_skills"] = True
|
|
277
280
|
elif arg.startswith("-"):
|
|
278
281
|
print(f"Unknown option: {arg}")
|
|
279
282
|
sys.exit(1)
|
|
@@ -361,8 +364,12 @@ def resolve_profile(profile: str, only: str) -> str:
|
|
|
361
364
|
pass
|
|
362
365
|
elif profile == "strict":
|
|
363
366
|
pass
|
|
367
|
+
elif profile == "full":
|
|
368
|
+
# `full` is a `standard`-shaped install for Claude Code components;
|
|
369
|
+
# the extra native surfaces are wired into editor-local generators.
|
|
370
|
+
pass
|
|
364
371
|
else:
|
|
365
|
-
print(f"Unknown profile: {profile} (valid: minimal, standard, strict)")
|
|
372
|
+
print(f"Unknown profile: {profile} (valid: minimal, standard, strict, full)")
|
|
366
373
|
sys.exit(1)
|
|
367
374
|
return only
|
|
368
375
|
|
|
@@ -714,7 +721,9 @@ def main() -> None:
|
|
|
714
721
|
local_editors_arg: str = cfg["editors"]
|
|
715
722
|
install_local_project(rules_dir, dry_run, reset, lang_modules or None,
|
|
716
723
|
editors=local_editors_arg,
|
|
717
|
-
merged_config=merged_config
|
|
724
|
+
merged_config=merged_config,
|
|
725
|
+
profile=profile or "standard",
|
|
726
|
+
codex_skills=cfg.get("codex_skills", False))
|
|
718
727
|
installed_eds: list[str] = [] # local install doesn't track global editors
|
|
719
728
|
install_strict_git_hooks(profile, local, dry_run)
|
|
720
729
|
else:
|
|
@@ -314,7 +314,9 @@ def _resolve_editors(editors_arg: str, cwd: Path) -> list[str]:
|
|
|
314
314
|
def install_local_project(rules_dir: Path, dry_run: bool, reset: bool,
|
|
315
315
|
language_modules: list[str] | None = None,
|
|
316
316
|
editors: str = "",
|
|
317
|
-
merged_config: dict | None = None
|
|
317
|
+
merged_config: dict | None = None,
|
|
318
|
+
profile: str = "standard",
|
|
319
|
+
codex_skills: bool = False) -> None:
|
|
318
320
|
"""Install project-local configs.
|
|
319
321
|
|
|
320
322
|
Claude Code configs (CLAUDE.md, settings, constitution) are always installed.
|
|
@@ -323,6 +325,17 @@ def install_local_project(rules_dir: Path, dry_run: bool, reset: bool,
|
|
|
323
325
|
- ``--editors cursor,aider``: install only these
|
|
324
326
|
- (empty): auto-detect from existing project files, install only those
|
|
325
327
|
|
|
328
|
+
``profile`` controls which native surfaces are emitted for each editor:
|
|
329
|
+
- ``minimal`` / ``standard`` / ``strict``: rules (+ Copilot directory mode
|
|
330
|
+
and Gemini hooks from ``standard`` onwards — both non-breaking).
|
|
331
|
+
- ``full``: adds every native surface an editor can host (subagents,
|
|
332
|
+
custom commands, hooks, skill-catalogue pointers).
|
|
333
|
+
|
|
334
|
+
``codex_skills`` (opt-in, off by default) additionally writes a full skill
|
|
335
|
+
mirror under ``.codex/skills/``. Required even for ``--profile full`` —
|
|
336
|
+
the mirror is intentionally gated behind an extra flag to keep the default
|
|
337
|
+
footprint small.
|
|
338
|
+
|
|
326
339
|
If ``merged_config`` is provided (from .softspark-toolkit.json extends resolution),
|
|
327
340
|
additional rules and constitution amendments from the base config are injected.
|
|
328
341
|
"""
|
|
@@ -340,10 +353,13 @@ def install_local_project(rules_dir: Path, dry_run: bool, reset: bool,
|
|
|
340
353
|
print(f" Editors: {', '.join(resolved_editors)}")
|
|
341
354
|
else:
|
|
342
355
|
print(" Editors: none (use --editors <list> or --editors all to enable)")
|
|
356
|
+
if profile:
|
|
357
|
+
print(f" Profile: {profile}")
|
|
343
358
|
print()
|
|
344
359
|
|
|
345
360
|
if dry_run:
|
|
346
|
-
_install_local_dry_run(reset, resolved_editors
|
|
361
|
+
_install_local_dry_run(reset, resolved_editors, profile=profile,
|
|
362
|
+
codex_skills=codex_skills)
|
|
347
363
|
if language_modules:
|
|
348
364
|
print(f" Would inject language rules: {', '.join(language_modules)}")
|
|
349
365
|
if merged_config:
|
|
@@ -382,7 +398,9 @@ def install_local_project(rules_dir: Path, dry_run: bool, reset: bool,
|
|
|
382
398
|
|
|
383
399
|
# Install editor configs only for resolved editors
|
|
384
400
|
_create_local_ai_tool_configs(cwd, rules_dir, resolved_editors,
|
|
385
|
-
language_modules=language_modules
|
|
401
|
+
language_modules=language_modules,
|
|
402
|
+
profile=profile,
|
|
403
|
+
codex_skills=codex_skills)
|
|
386
404
|
|
|
387
405
|
|
|
388
406
|
def _apply_extends_config(cwd: Path, merged: dict) -> None:
|
|
@@ -533,7 +551,9 @@ def _inject_language_rules(cwd: Path, language_modules: list[str] | None) -> Non
|
|
|
533
551
|
tmp_path.unlink(missing_ok=True)
|
|
534
552
|
|
|
535
553
|
|
|
536
|
-
def _install_local_dry_run(reset: bool, editors: list[str] | None = None
|
|
554
|
+
def _install_local_dry_run(reset: bool, editors: list[str] | None = None,
|
|
555
|
+
profile: str = "standard",
|
|
556
|
+
codex_skills: bool = False) -> None:
|
|
537
557
|
eds = set(editors or [])
|
|
538
558
|
if reset:
|
|
539
559
|
print(" Would remove: CLAUDE.md, .claude/settings.local.json")
|
|
@@ -544,6 +564,10 @@ def _install_local_dry_run(reset: bool, editors: list[str] | None = None) -> Non
|
|
|
544
564
|
print(" Would create: .claude/settings.local.json (if missing)")
|
|
545
565
|
print(" Would inject: .claude/constitution.md")
|
|
546
566
|
|
|
567
|
+
add_copilot_dir = profile in {"standard", "strict", "full"}
|
|
568
|
+
add_gemini_hooks = profile in {"standard", "strict", "full"}
|
|
569
|
+
add_native_surfaces = profile == "full"
|
|
570
|
+
|
|
547
571
|
# Editor-specific dry-run messages
|
|
548
572
|
_EDITOR_DRY_RUN = {
|
|
549
573
|
"copilot": " Would inject: .github/copilot-instructions.md",
|
|
@@ -554,12 +578,31 @@ def _install_local_dry_run(reset: bool, editors: list[str] | None = None) -> Non
|
|
|
554
578
|
"aider": " Would generate: .aider.conf.yml + CONVENTIONS.md",
|
|
555
579
|
"augment": " Would generate: .augment/rules/ai-toolkit-*.md",
|
|
556
580
|
"antigravity": " Would generate: .agent/rules/ + .agent/workflows/",
|
|
581
|
+
"gemini": " Would generate: GEMINI.md",
|
|
557
582
|
"opencode": " Would generate: AGENTS.md + .opencode/{agents,commands,plugins}/ + opencode.json",
|
|
558
583
|
}
|
|
559
584
|
for ed, msg in _EDITOR_DRY_RUN.items():
|
|
560
585
|
if ed in eds:
|
|
561
586
|
print(msg)
|
|
562
587
|
|
|
588
|
+
# Profile-driven extras (matrix in kb/reference/global-install-model.md)
|
|
589
|
+
if "copilot" in eds and add_copilot_dir:
|
|
590
|
+
print(" Would generate: .github/instructions/ + .github/prompts/ (profile >= standard)")
|
|
591
|
+
if "gemini" in eds and add_gemini_hooks:
|
|
592
|
+
print(" Would generate: .gemini/settings.json hooks (profile >= standard)")
|
|
593
|
+
if add_native_surfaces:
|
|
594
|
+
if "cursor" in eds:
|
|
595
|
+
print(" Would generate: .cursor/hooks.json + .cursor/agents/ (profile=full)")
|
|
596
|
+
if "windsurf" in eds:
|
|
597
|
+
print(" Would generate: .windsurf/hooks.json (profile=full)")
|
|
598
|
+
if "augment" in eds:
|
|
599
|
+
print(" Would generate: .augment/agents/ + .augment/commands/ + "
|
|
600
|
+
"$HOME/.augment/settings.json + .augment/skills/ (profile=full)")
|
|
601
|
+
if "gemini" in eds:
|
|
602
|
+
print(" Would generate: .gemini/commands/ + .gemini/skills/ (profile=full)")
|
|
603
|
+
if "codex" in eds and codex_skills:
|
|
604
|
+
print(" Would generate: .codex/skills/ full mirror (--codex-skills)")
|
|
605
|
+
|
|
563
606
|
if not eds:
|
|
564
607
|
print(" No editors selected (use --editors <list> or --editors all)")
|
|
565
608
|
|
|
@@ -695,10 +738,46 @@ def _install_codex_skills(cwd: Path) -> None:
|
|
|
695
738
|
)
|
|
696
739
|
|
|
697
740
|
|
|
741
|
+
def _try_generator(module_name: str, *args, **kwargs) -> bool:
|
|
742
|
+
"""Import and invoke ``<module>.generate(...)``.
|
|
743
|
+
|
|
744
|
+
Post v3.0.0 all referenced generators ship with the toolkit. An ImportError
|
|
745
|
+
here therefore indicates a real packaging bug, not a roll-out gap. We print
|
|
746
|
+
a loud error and re-raise so CI and interactive installs fail fast instead
|
|
747
|
+
of silently skipping a native surface.
|
|
748
|
+
"""
|
|
749
|
+
try:
|
|
750
|
+
module = __import__(module_name)
|
|
751
|
+
except ImportError as exc:
|
|
752
|
+
print(f" INTERNAL ERROR: shipped generator '{module_name}' failed to "
|
|
753
|
+
f"import ({exc.msg}). Please file a bug at "
|
|
754
|
+
f"https://github.com/softspark/ai-toolkit/issues")
|
|
755
|
+
raise
|
|
756
|
+
|
|
757
|
+
gen = getattr(module, "generate", None)
|
|
758
|
+
if gen is None:
|
|
759
|
+
raise RuntimeError(
|
|
760
|
+
f"INTERNAL ERROR: shipped generator '{module_name}' has no "
|
|
761
|
+
f"generate() function. This is a packaging bug."
|
|
762
|
+
)
|
|
763
|
+
|
|
764
|
+
gen(*args, **kwargs)
|
|
765
|
+
return True
|
|
766
|
+
|
|
767
|
+
|
|
698
768
|
def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
699
769
|
editors: list[str],
|
|
700
|
-
language_modules: list[str] | None = None
|
|
770
|
+
language_modules: list[str] | None = None,
|
|
771
|
+
profile: str = "standard",
|
|
772
|
+
codex_skills: bool = False) -> None:
|
|
701
773
|
eds = set(editors)
|
|
774
|
+
# `full` implies the `standard` additions (Copilot dir + Gemini hooks)
|
|
775
|
+
# plus every native-surface generator we ship. Normalize unknown
|
|
776
|
+
# profiles to `standard` so callers never produce a silent no-op.
|
|
777
|
+
_profile = profile if profile in {"minimal", "standard", "strict", "full"} else "standard"
|
|
778
|
+
add_copilot_dir = _profile in {"standard", "strict", "full"}
|
|
779
|
+
add_gemini_hooks = _profile in {"standard", "strict", "full"}
|
|
780
|
+
add_native_surfaces = _profile == "full"
|
|
702
781
|
|
|
703
782
|
if "copilot" in eds:
|
|
704
783
|
inject_with_rules(
|
|
@@ -706,6 +785,12 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
706
785
|
cwd / ".github" / "copilot-instructions.md",
|
|
707
786
|
rules_dir,
|
|
708
787
|
)
|
|
788
|
+
# `standard` and above: emit path-specific instructions + prompt files
|
|
789
|
+
# (directory mode). `minimal` stays backwards-compatible with v2.
|
|
790
|
+
if add_copilot_dir:
|
|
791
|
+
from generate_copilot import generate as gen_copilot_dir
|
|
792
|
+
gen_copilot_dir(cwd, language_modules=language_modules,
|
|
793
|
+
rules_dir=rules_dir)
|
|
709
794
|
|
|
710
795
|
if "cursor" in eds:
|
|
711
796
|
inject_with_rules(
|
|
@@ -716,6 +801,9 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
716
801
|
from generate_cursor_mdc import generate as gen_cursor_mdc
|
|
717
802
|
gen_cursor_mdc(cwd, language_modules=language_modules,
|
|
718
803
|
rules_dir=rules_dir)
|
|
804
|
+
if add_native_surfaces:
|
|
805
|
+
_try_generator("generate_cursor_hooks", cwd)
|
|
806
|
+
_try_generator("generate_cursor_agents", cwd)
|
|
719
807
|
|
|
720
808
|
if "windsurf" in eds:
|
|
721
809
|
inject_with_rules(
|
|
@@ -726,6 +814,8 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
726
814
|
from generate_windsurf_rules import generate as gen_windsurf_rules
|
|
727
815
|
gen_windsurf_rules(cwd, language_modules=language_modules,
|
|
728
816
|
rules_dir=rules_dir)
|
|
817
|
+
if add_native_surfaces:
|
|
818
|
+
_try_generator("generate_windsurf_hooks", cwd)
|
|
729
819
|
|
|
730
820
|
if "cline" in eds:
|
|
731
821
|
# Migrate: remove legacy .clinerules single file (replaced by directory)
|
|
@@ -759,6 +849,13 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
759
849
|
from generate_augment_rules import generate as gen_augment_rules
|
|
760
850
|
gen_augment_rules(cwd, language_modules=language_modules,
|
|
761
851
|
rules_dir=rules_dir)
|
|
852
|
+
if add_native_surfaces:
|
|
853
|
+
_try_generator("generate_augment_agents", cwd)
|
|
854
|
+
_try_generator("generate_augment_commands", cwd)
|
|
855
|
+
# Augment hooks live under $HOME/.augment/settings.json. The
|
|
856
|
+
# generator takes HOME (not cwd) per bucket-1 contract.
|
|
857
|
+
_try_generator("generate_augment_hooks", Path.home())
|
|
858
|
+
_try_generator("generate_augment_skills", cwd)
|
|
762
859
|
|
|
763
860
|
if "antigravity" in eds:
|
|
764
861
|
from generate_antigravity import generate as gen_antigravity
|
|
@@ -786,6 +883,24 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
786
883
|
print(" Created: .codex/hooks.json")
|
|
787
884
|
# .agents/skills/ — filtered symlinks (Codex-compatible skills only)
|
|
788
885
|
_install_codex_skills(cwd)
|
|
886
|
+
# .codex/skills/ — full mirror, gated behind explicit --codex-skills flag
|
|
887
|
+
# (does NOT activate automatically on --profile full — must opt in).
|
|
888
|
+
if codex_skills:
|
|
889
|
+
_try_generator("generate_codex_skills", cwd,
|
|
890
|
+
enable_codex_skills=True)
|
|
891
|
+
|
|
892
|
+
if "gemini" in eds:
|
|
893
|
+
# GEMINI.md — marker injection from the shared generator output
|
|
894
|
+
inject_with_rules(
|
|
895
|
+
"generate-gemini.sh",
|
|
896
|
+
cwd / "GEMINI.md",
|
|
897
|
+
rules_dir,
|
|
898
|
+
)
|
|
899
|
+
if add_gemini_hooks:
|
|
900
|
+
_try_generator("generate_gemini_hooks", cwd)
|
|
901
|
+
if add_native_surfaces:
|
|
902
|
+
_try_generator("generate_gemini_commands", cwd)
|
|
903
|
+
_try_generator("generate_gemini_skills", cwd)
|
|
789
904
|
|
|
790
905
|
if "opencode" in eds:
|
|
791
906
|
# AGENTS.md — shared with Codex via marker injection (opencode reads same file)
|
package/scripts/validate.py
CHANGED
|
@@ -39,9 +39,26 @@ VALID_TOOLS = frozenset({
|
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
VALID_HOOK_EVENTS = frozenset({
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
|
|
42
|
+
# Core lifecycle
|
|
43
|
+
"SessionStart", "SessionEnd", "UserPromptSubmit", "Notification",
|
|
44
|
+
# Tool lifecycle
|
|
45
|
+
"PreToolUse", "PostToolUse",
|
|
46
|
+
# Turn lifecycle
|
|
47
|
+
"Stop", "StopFailure",
|
|
48
|
+
# Subagent lifecycle
|
|
49
|
+
"SubagentStart", "SubagentStop",
|
|
50
|
+
# Compaction
|
|
51
|
+
"PreCompact", "PostCompact",
|
|
52
|
+
# Permissions & elicitation
|
|
53
|
+
"PermissionRequest", "PermissionDenied",
|
|
54
|
+
"Elicitation", "ElicitationResult",
|
|
55
|
+
# Agent Teams
|
|
56
|
+
"TaskCreated", "TaskCompleted", "TeammateIdle",
|
|
57
|
+
# Worktrees & environment
|
|
58
|
+
"WorktreeCreate", "WorktreeRemove",
|
|
59
|
+
"CwdChanged", "FileChanged", "ConfigChange",
|
|
60
|
+
# Setup / bootstrap
|
|
61
|
+
"Setup", "InstructionsLoaded",
|
|
45
62
|
})
|
|
46
63
|
|
|
47
64
|
VALID_KB_CATEGORIES = frozenset({
|