@softspark/ai-toolkit 1.5.1 → 1.6.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 +29 -0
- package/README.md +17 -4
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/ARCHITECTURE.md +3 -1
- package/app/agents/documenter.md +2 -2
- package/app/skills/docs/SKILL.md +1 -1
- package/app/skills/documentation-standards/SKILL.md +1 -1
- package/bin/ai-toolkit.js +1 -1
- package/kb/planning/cloud-security-pack-plan.md +1166 -0
- package/kb/planning/enterprise-config-inheritance-plan.md +917 -0
- package/kb/planning/local-dashboard-plan.md +773 -0
- package/kb/planning/offline-slm-profile-plan.md +742 -0
- package/kb/procedures/maintenance-sop.md +10 -4
- package/kb/reference/architecture-overview.md +2 -2
- package/llms-full.txt +3634 -6
- package/llms.txt +4 -0
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/scripts/add_rule.py +5 -4
- package/scripts/dir_rules_shared.py +96 -0
- package/scripts/generate_aider_conf.py +2 -1
- package/scripts/generate_antigravity.py +16 -5
- package/scripts/generate_augment_rules.py +44 -10
- package/scripts/generate_cline_rules.py +25 -10
- package/scripts/generate_cursor_mdc.py +37 -8
- package/scripts/generate_roo_rules.py +13 -3
- package/scripts/generate_windsurf_rules.py +13 -3
- package/scripts/install_steps/ai_tools.py +22 -15
- package/scripts/validate.py +1 -0
package/llms.txt
CHANGED
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
- [Best Practices](kb/best-practices/README.md)
|
|
15
15
|
- [No Hardcoded Counts in Secondary Docs](kb/best-practices/no-hardcoded-counts.md)
|
|
16
16
|
- [How-To Guides](kb/howto/README.md)
|
|
17
|
+
- [Plan: Cloud Security Pack — Multi-Cloud Audit](kb/planning/cloud-security-pack-plan.md)
|
|
18
|
+
- [Plan: Enterprise Config Inheritance — Multi-Repo Governance with `extends`](kb/planning/enterprise-config-inheritance-plan.md)
|
|
19
|
+
- [Plan: Local Dashboard — `ai-toolkit ui`](kb/planning/local-dashboard-plan.md)
|
|
20
|
+
- [Plan: Offline-First SLM Profile — Lightweight Mode for Local Models](kb/planning/offline-slm-profile-plan.md)
|
|
17
21
|
- [SOP: Claude Toolkit Maintenance](kb/procedures/maintenance-sop.md)
|
|
18
22
|
- [SOP: Release Preparation](kb/procedures/release-preparation-sop.md)
|
|
19
23
|
- [SOP: Release Verification](kb/procedures/release-verification-sop.md)
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softspark/ai-toolkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
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",
|
package/scripts/add_rule.py
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
"""add-rule -- Register a rule file in ~/.ai-toolkit/rules/.
|
|
3
3
|
|
|
4
4
|
Registered rules are automatically injected into all AI tool configs
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
on next 'ai-toolkit install' or 'ai-toolkit update':
|
|
6
|
+
Global: Claude, Cursor, Windsurf, Gemini, Augment
|
|
7
|
+
Local (--local): all of the above + Copilot, Cline, Roo, Aider, Antigravity
|
|
7
8
|
|
|
8
9
|
Usage:
|
|
9
10
|
add_rule.py <rule-file> [rule-name]
|
|
@@ -47,8 +48,8 @@ def main() -> None:
|
|
|
47
48
|
print(f"Registered: '{rule_name}' -> {dest}")
|
|
48
49
|
print()
|
|
49
50
|
print("Apply now:")
|
|
50
|
-
print(" ai-toolkit update # global (Claude, Cursor, Windsurf, Gemini)")
|
|
51
|
-
print(" ai-toolkit update --local # project-local (
|
|
51
|
+
print(" ai-toolkit update # global (Claude, Cursor, Windsurf, Gemini, Augment)")
|
|
52
|
+
print(" ai-toolkit update --local # project-local (all editors)")
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
if __name__ == "__main__":
|
|
@@ -439,3 +439,99 @@ def write_rules(target_dir: Path, rules: dict[str, callable],
|
|
|
439
439
|
(out_dir / filename).write_text(content_fn(), encoding="utf-8")
|
|
440
440
|
tag = f" ({label})" if label else ""
|
|
441
441
|
print(f" Generated: {subdir}/{filename}{tag}")
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
# ---------------------------------------------------------------------------
|
|
445
|
+
# Language → glob patterns for editors with file-type activation
|
|
446
|
+
# ---------------------------------------------------------------------------
|
|
447
|
+
|
|
448
|
+
LANG_GLOBS: dict[str, list[str]] = {
|
|
449
|
+
"python": ["**/*.py"],
|
|
450
|
+
"typescript": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
|
|
451
|
+
"golang": ["**/*.go"],
|
|
452
|
+
"rust": ["**/*.rs"],
|
|
453
|
+
"java": ["**/*.java"],
|
|
454
|
+
"kotlin": ["**/*.kt", "**/*.kts"],
|
|
455
|
+
"swift": ["**/*.swift"],
|
|
456
|
+
"dart": ["**/*.dart"],
|
|
457
|
+
"csharp": ["**/*.cs"],
|
|
458
|
+
"php": ["**/*.php"],
|
|
459
|
+
"cpp": ["**/*.cpp", "**/*.cc", "**/*.cxx", "**/*.hpp", "**/*.h"],
|
|
460
|
+
"ruby": ["**/*.rb"],
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def _resolve_app_rules_dir() -> Path:
|
|
465
|
+
"""Resolve path to app/rules/ directory."""
|
|
466
|
+
return Path(__file__).resolve().parent.parent / "app" / "rules"
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def build_language_rules(
|
|
470
|
+
language_modules: list[str] | None,
|
|
471
|
+
) -> dict[str, callable]:
|
|
472
|
+
"""Build language-specific rule entries from app/rules/<lang>/.
|
|
473
|
+
|
|
474
|
+
Returns dict of filename -> callable (same shape as STANDARD_RULES).
|
|
475
|
+
Language modules use format ``rules-python``, ``rules-typescript``, etc.
|
|
476
|
+
Always includes ``common`` when any language is detected.
|
|
477
|
+
"""
|
|
478
|
+
if not language_modules:
|
|
479
|
+
return {}
|
|
480
|
+
|
|
481
|
+
rules_src = _resolve_app_rules_dir()
|
|
482
|
+
if not rules_src.is_dir():
|
|
483
|
+
return {}
|
|
484
|
+
|
|
485
|
+
langs: list[str] = []
|
|
486
|
+
for mod in language_modules:
|
|
487
|
+
if mod.startswith("rules-"):
|
|
488
|
+
langs.append(mod[6:])
|
|
489
|
+
if not langs:
|
|
490
|
+
return {}
|
|
491
|
+
|
|
492
|
+
all_dirs: list[str] = ["common"]
|
|
493
|
+
for lang in langs:
|
|
494
|
+
if lang not in all_dirs:
|
|
495
|
+
all_dirs.append(lang)
|
|
496
|
+
|
|
497
|
+
result: dict[str, callable] = {}
|
|
498
|
+
for lang in all_dirs:
|
|
499
|
+
lang_path = rules_src / lang
|
|
500
|
+
if not lang_path.is_dir():
|
|
501
|
+
continue
|
|
502
|
+
parts: list[str] = []
|
|
503
|
+
for f in sorted(lang_path.glob("*.md")):
|
|
504
|
+
content = f.read_text(encoding="utf-8")
|
|
505
|
+
# Strip YAML frontmatter if present
|
|
506
|
+
if content.startswith("---"):
|
|
507
|
+
end = content.find("---", 3)
|
|
508
|
+
if end != -1:
|
|
509
|
+
content = content[end + 3:].lstrip("\n")
|
|
510
|
+
parts.append(content.rstrip("\n"))
|
|
511
|
+
|
|
512
|
+
if parts:
|
|
513
|
+
combined = "\n\n".join(parts) + "\n"
|
|
514
|
+
filename = f"{PREFIX}lang-{lang}.md"
|
|
515
|
+
# Capture value via default arg to avoid late-binding closure
|
|
516
|
+
result[filename] = (lambda c: lambda: c)(combined)
|
|
517
|
+
|
|
518
|
+
return result
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def build_registered_rules(
|
|
522
|
+
rules_dir: Path | None,
|
|
523
|
+
) -> dict[str, callable]:
|
|
524
|
+
"""Build entries from user's registered rules (~/.ai-toolkit/rules/*.md).
|
|
525
|
+
|
|
526
|
+
Returns dict of filename -> callable (same shape as STANDARD_RULES).
|
|
527
|
+
"""
|
|
528
|
+
if not rules_dir or not rules_dir.is_dir():
|
|
529
|
+
return {}
|
|
530
|
+
|
|
531
|
+
result: dict[str, callable] = {}
|
|
532
|
+
for rule_file in sorted(rules_dir.glob("*.md")):
|
|
533
|
+
content = rule_file.read_text(encoding="utf-8")
|
|
534
|
+
filename = f"{PREFIX}custom-{rule_file.stem}.md"
|
|
535
|
+
result[filename] = (lambda c: lambda: c)(content)
|
|
536
|
+
|
|
537
|
+
return result
|
|
@@ -18,8 +18,9 @@ def main() -> None:
|
|
|
18
18
|
print("# Use the architect mode for complex tasks, aligned with our 2-phase workflow")
|
|
19
19
|
print("architect: true")
|
|
20
20
|
print()
|
|
21
|
-
print("# Read
|
|
21
|
+
print("# Read project rules (loaded as read-only context)")
|
|
22
22
|
print("read:")
|
|
23
|
+
print(' - "CONVENTIONS.md"')
|
|
23
24
|
print(' - ".claude/CLAUDE.md"')
|
|
24
25
|
print(' - ".claude/constitution.md"')
|
|
25
26
|
print(' - "AGENTS.md"')
|
|
@@ -18,12 +18,23 @@ import sys
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
|
|
20
20
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
21
|
-
from dir_rules_shared import
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
from dir_rules_shared import (
|
|
22
|
+
STANDARD_RULES,
|
|
23
|
+
STANDARD_WORKFLOWS,
|
|
24
|
+
build_language_rules,
|
|
25
|
+
build_registered_rules,
|
|
26
|
+
write_rules,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def generate(target_dir: Path, *,
|
|
31
|
+
language_modules: list[str] | None = None,
|
|
32
|
+
rules_dir: Path | None = None) -> None:
|
|
25
33
|
"""Write .agent/rules/ and .agent/workflows/ files to target_dir."""
|
|
26
|
-
|
|
34
|
+
rules = dict(STANDARD_RULES)
|
|
35
|
+
rules.update(build_language_rules(language_modules))
|
|
36
|
+
rules.update(build_registered_rules(rules_dir))
|
|
37
|
+
write_rules(target_dir, rules, ".agent/rules")
|
|
27
38
|
write_rules(target_dir, STANDARD_WORKFLOWS, ".agent/workflows")
|
|
28
39
|
|
|
29
40
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
Augment supports per-file rules with frontmatter:
|
|
5
5
|
- type: always_apply — always in context
|
|
6
|
-
- type:
|
|
6
|
+
- type: agent_requested + globs — attached for matching files
|
|
7
7
|
|
|
8
8
|
The existing generate_augment.py creates a single always_apply file.
|
|
9
9
|
This generator adds granular per-category rules with appropriate types.
|
|
@@ -18,7 +18,10 @@ from pathlib import Path
|
|
|
18
18
|
|
|
19
19
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
20
20
|
from dir_rules_shared import (
|
|
21
|
+
LANG_GLOBS,
|
|
21
22
|
PREFIX,
|
|
23
|
+
build_language_rules,
|
|
24
|
+
build_registered_rules,
|
|
22
25
|
cleanup_stale,
|
|
23
26
|
rule_agents_and_skills,
|
|
24
27
|
rule_code_style,
|
|
@@ -69,7 +72,7 @@ def _make_rules() -> dict[str, callable]:
|
|
|
69
72
|
f"{PREFIX}code-style.md": lambda: _augment_wrap(
|
|
70
73
|
rule_code_style(),
|
|
71
74
|
description="Code style conventions",
|
|
72
|
-
rule_type="
|
|
75
|
+
rule_type="agent_requested",
|
|
73
76
|
globs=["*.py", "*.ts", "*.tsx", "*.js", "*.jsx", "*.go", "*.rs",
|
|
74
77
|
"*.java", "*.kt", "*.swift", "*.dart", "*.cs", "*.php",
|
|
75
78
|
"*.cpp", "*.cc", "*.rb"],
|
|
@@ -77,7 +80,7 @@ def _make_rules() -> dict[str, callable]:
|
|
|
77
80
|
f"{PREFIX}testing.md": lambda: _augment_wrap(
|
|
78
81
|
rule_testing(),
|
|
79
82
|
description="Testing standards and patterns",
|
|
80
|
-
rule_type="
|
|
83
|
+
rule_type="agent_requested",
|
|
81
84
|
globs=["*.test.*", "*.spec.*", "test_*", "**/tests/**",
|
|
82
85
|
"**/test/**", "**/__tests__/**"],
|
|
83
86
|
),
|
|
@@ -87,14 +90,45 @@ def _make_rules() -> dict[str, callable]:
|
|
|
87
90
|
RULES = _make_rules()
|
|
88
91
|
|
|
89
92
|
|
|
90
|
-
def generate(target_dir: Path
|
|
93
|
+
def generate(target_dir: Path, *,
|
|
94
|
+
language_modules: list[str] | None = None,
|
|
95
|
+
rules_dir: Path | None = None) -> None:
|
|
91
96
|
"""Write .augment/rules/ai-toolkit-*.md files to target_dir."""
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
out_dir = target_dir / ".augment" / "rules"
|
|
98
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
99
|
+
|
|
100
|
+
all_rules: dict[str, callable] = dict(RULES)
|
|
101
|
+
|
|
102
|
+
# Add language rules with Augment frontmatter
|
|
103
|
+
for filename, content_fn in build_language_rules(language_modules).items():
|
|
104
|
+
lang = filename.removeprefix(f"{PREFIX}lang-").removesuffix(".md")
|
|
105
|
+
globs = LANG_GLOBS.get(lang)
|
|
106
|
+
if globs:
|
|
107
|
+
all_rules[filename] = (lambda fn, l, g: lambda: _augment_wrap(
|
|
108
|
+
fn(),
|
|
109
|
+
description=f"{l.title()} language rules",
|
|
110
|
+
rule_type="agent_requested",
|
|
111
|
+
globs=g,
|
|
112
|
+
))(content_fn, lang, globs)
|
|
113
|
+
else:
|
|
114
|
+
# common — always apply
|
|
115
|
+
all_rules[filename] = (lambda fn, l: lambda: _augment_wrap(
|
|
116
|
+
fn(),
|
|
117
|
+
description=f"{l.title()} language rules",
|
|
118
|
+
))(content_fn, lang)
|
|
119
|
+
|
|
120
|
+
# Add registered rules with Augment frontmatter
|
|
121
|
+
for filename, content_fn in build_registered_rules(rules_dir).items():
|
|
122
|
+
name = filename.removeprefix(f"{PREFIX}custom-").removesuffix(".md")
|
|
123
|
+
all_rules[filename] = (lambda fn, n: lambda: _augment_wrap(
|
|
124
|
+
fn(),
|
|
125
|
+
description=f"Custom rule: {n}",
|
|
126
|
+
))(content_fn, name)
|
|
127
|
+
|
|
128
|
+
cleanup_stale(out_dir, set(all_rules.keys()) | {"ai-toolkit.md"})
|
|
129
|
+
|
|
130
|
+
for filename, content_fn in all_rules.items():
|
|
131
|
+
(out_dir / filename).write_text(content_fn(), encoding="utf-8")
|
|
98
132
|
print(f" Generated: .augment/rules/{filename}")
|
|
99
133
|
|
|
100
134
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Generate .
|
|
2
|
+
"""Generate .clinerules/*.md files for Cline.
|
|
3
3
|
|
|
4
|
-
Cline reads
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
Cline reads rules from the .clinerules/ directory (since Cline 3.7).
|
|
5
|
+
Each .md file inside .clinerules/ is automatically loaded.
|
|
6
|
+
The legacy .clinerules single-file format is replaced by this directory.
|
|
7
7
|
|
|
8
8
|
Usage:
|
|
9
9
|
python3 scripts/generate_cline_rules.py [target-dir]
|
|
@@ -14,12 +14,27 @@ import sys
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
16
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
17
|
-
from dir_rules_shared import
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
from dir_rules_shared import (
|
|
18
|
+
STANDARD_RULES,
|
|
19
|
+
build_language_rules,
|
|
20
|
+
build_registered_rules,
|
|
21
|
+
write_rules,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def generate(target_dir: Path, *,
|
|
26
|
+
language_modules: list[str] | None = None,
|
|
27
|
+
rules_dir: Path | None = None) -> None:
|
|
28
|
+
"""Write .clinerules/*.md files to target_dir."""
|
|
29
|
+
# Migrate: if .clinerules exists as a single file, remove it
|
|
30
|
+
# so the directory can be created (Cline 3.7+ uses directory format)
|
|
31
|
+
clinerules = target_dir / ".clinerules"
|
|
32
|
+
if clinerules.is_file():
|
|
33
|
+
clinerules.unlink()
|
|
34
|
+
rules = dict(STANDARD_RULES)
|
|
35
|
+
rules.update(build_language_rules(language_modules))
|
|
36
|
+
rules.update(build_registered_rules(rules_dir))
|
|
37
|
+
write_rules(target_dir, rules, ".clinerules")
|
|
23
38
|
|
|
24
39
|
|
|
25
40
|
def main() -> None:
|
|
@@ -20,7 +20,10 @@ from pathlib import Path
|
|
|
20
20
|
|
|
21
21
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
22
22
|
from dir_rules_shared import (
|
|
23
|
+
LANG_GLOBS,
|
|
23
24
|
PREFIX,
|
|
25
|
+
build_language_rules,
|
|
26
|
+
build_registered_rules,
|
|
24
27
|
cleanup_stale,
|
|
25
28
|
rule_agents_and_skills,
|
|
26
29
|
rule_code_style,
|
|
@@ -100,21 +103,47 @@ RULES = _make_rules()
|
|
|
100
103
|
# Main
|
|
101
104
|
# ---------------------------------------------------------------------------
|
|
102
105
|
|
|
103
|
-
def generate(target_dir: Path
|
|
106
|
+
def generate(target_dir: Path, *,
|
|
107
|
+
language_modules: list[str] | None = None,
|
|
108
|
+
rules_dir: Path | None = None) -> None:
|
|
104
109
|
"""Write .cursor/rules/*.mdc files to target_dir."""
|
|
105
|
-
|
|
106
|
-
|
|
110
|
+
out_dir = target_dir / ".cursor" / "rules"
|
|
111
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
112
|
+
|
|
113
|
+
all_rules: dict[str, callable] = dict(RULES)
|
|
114
|
+
|
|
115
|
+
# Add language rules wrapped in .mdc frontmatter
|
|
116
|
+
for filename, content_fn in build_language_rules(language_modules).items():
|
|
117
|
+
lang = filename.removeprefix(f"{PREFIX}lang-").removesuffix(".md")
|
|
118
|
+
globs = LANG_GLOBS.get(lang)
|
|
119
|
+
mdc_name = filename.replace(".md", ".mdc")
|
|
120
|
+
all_rules[mdc_name] = (lambda fn, l, g: lambda: _mdc(
|
|
121
|
+
fn(),
|
|
122
|
+
description=f"{l.title()} language rules",
|
|
123
|
+
globs=g if g else None,
|
|
124
|
+
always_apply=not g,
|
|
125
|
+
))(content_fn, lang, globs)
|
|
126
|
+
|
|
127
|
+
# Add registered rules wrapped in .mdc frontmatter
|
|
128
|
+
for filename, content_fn in build_registered_rules(rules_dir).items():
|
|
129
|
+
name = filename.removeprefix(f"{PREFIX}custom-").removesuffix(".md")
|
|
130
|
+
mdc_name = filename.replace(".md", ".mdc")
|
|
131
|
+
all_rules[mdc_name] = (lambda fn, n: lambda: _mdc(
|
|
132
|
+
fn(),
|
|
133
|
+
description=f"Custom rule: {n}",
|
|
134
|
+
always_apply=True,
|
|
135
|
+
))(content_fn, name)
|
|
107
136
|
|
|
108
137
|
# Clean stale ai-toolkit-*.mdc files
|
|
109
|
-
current = set(
|
|
110
|
-
if
|
|
111
|
-
for f in
|
|
138
|
+
current = set(all_rules.keys())
|
|
139
|
+
if out_dir.is_dir():
|
|
140
|
+
for f in out_dir.iterdir():
|
|
112
141
|
if f.name.startswith(PREFIX) and f.suffix == ".mdc" and f.name not in current:
|
|
113
142
|
f.unlink()
|
|
114
143
|
print(f" Removed stale: .cursor/rules/{f.name}")
|
|
115
144
|
|
|
116
|
-
for filename, content_fn in
|
|
117
|
-
(
|
|
145
|
+
for filename, content_fn in all_rules.items():
|
|
146
|
+
(out_dir / filename).write_text(content_fn(), encoding="utf-8")
|
|
118
147
|
print(f" Generated: .cursor/rules/{filename}")
|
|
119
148
|
|
|
120
149
|
|
|
@@ -13,12 +13,22 @@ import sys
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
15
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
16
|
-
from dir_rules_shared import
|
|
16
|
+
from dir_rules_shared import (
|
|
17
|
+
STANDARD_RULES,
|
|
18
|
+
build_language_rules,
|
|
19
|
+
build_registered_rules,
|
|
20
|
+
write_rules,
|
|
21
|
+
)
|
|
17
22
|
|
|
18
23
|
|
|
19
|
-
def generate(target_dir: Path
|
|
24
|
+
def generate(target_dir: Path, *,
|
|
25
|
+
language_modules: list[str] | None = None,
|
|
26
|
+
rules_dir: Path | None = None) -> None:
|
|
20
27
|
"""Write .roo/rules/*.md files to target_dir."""
|
|
21
|
-
|
|
28
|
+
rules = dict(STANDARD_RULES)
|
|
29
|
+
rules.update(build_language_rules(language_modules))
|
|
30
|
+
rules.update(build_registered_rules(rules_dir))
|
|
31
|
+
write_rules(target_dir, rules, ".roo/rules")
|
|
22
32
|
|
|
23
33
|
|
|
24
34
|
def main() -> None:
|
|
@@ -14,12 +14,22 @@ import sys
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
16
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
17
|
-
from dir_rules_shared import
|
|
17
|
+
from dir_rules_shared import (
|
|
18
|
+
STANDARD_RULES,
|
|
19
|
+
build_language_rules,
|
|
20
|
+
build_registered_rules,
|
|
21
|
+
write_rules,
|
|
22
|
+
)
|
|
18
23
|
|
|
19
24
|
|
|
20
|
-
def generate(target_dir: Path
|
|
25
|
+
def generate(target_dir: Path, *,
|
|
26
|
+
language_modules: list[str] | None = None,
|
|
27
|
+
rules_dir: Path | None = None) -> None:
|
|
21
28
|
"""Write .windsurf/rules/*.md files to target_dir."""
|
|
22
|
-
|
|
29
|
+
rules = dict(STANDARD_RULES)
|
|
30
|
+
rules.update(build_language_rules(language_modules))
|
|
31
|
+
rules.update(build_registered_rules(rules_dir))
|
|
32
|
+
write_rules(target_dir, rules, ".windsurf/rules")
|
|
23
33
|
|
|
24
34
|
|
|
25
35
|
def main() -> None:
|
|
@@ -136,7 +136,6 @@ _EDITOR_MARKERS: dict[str, str] = {
|
|
|
136
136
|
".windsurfrules": "windsurf",
|
|
137
137
|
".windsurf/rules": "windsurf",
|
|
138
138
|
".clinerules": "cline",
|
|
139
|
-
".cline/rules": "cline",
|
|
140
139
|
".roomodes": "roo",
|
|
141
140
|
".roo/rules": "roo",
|
|
142
141
|
".aider.conf.yml": "aider",
|
|
@@ -228,7 +227,8 @@ def install_local_project(rules_dir: Path, dry_run: bool, reset: bool,
|
|
|
228
227
|
_inject_language_rules(cwd, language_modules)
|
|
229
228
|
|
|
230
229
|
# Install editor configs only for resolved editors
|
|
231
|
-
_create_local_ai_tool_configs(cwd, rules_dir, resolved_editors
|
|
230
|
+
_create_local_ai_tool_configs(cwd, rules_dir, resolved_editors,
|
|
231
|
+
language_modules=language_modules)
|
|
232
232
|
|
|
233
233
|
|
|
234
234
|
def _inject_language_rules(cwd: Path, language_modules: list[str] | None) -> None:
|
|
@@ -308,7 +308,7 @@ def _install_local_dry_run(reset: bool, editors: list[str] | None = None) -> Non
|
|
|
308
308
|
"copilot": " Would inject: .github/copilot-instructions.md",
|
|
309
309
|
"cursor": " Would generate: .cursorrules + .cursor/rules/*.mdc",
|
|
310
310
|
"windsurf": " Would generate: .windsurfrules + .windsurf/rules/*.md",
|
|
311
|
-
"cline": " Would generate: .clinerules
|
|
311
|
+
"cline": " Would generate: .clinerules/*.md",
|
|
312
312
|
"roo": " Would generate: .roomodes + .roo/rules/*.md",
|
|
313
313
|
"aider": " Would generate: .aider.conf.yml + CONVENTIONS.md",
|
|
314
314
|
"augment": " Would generate: .augment/rules/ai-toolkit-*.md",
|
|
@@ -396,7 +396,8 @@ def _create_local_settings(cwd: Path, reset: bool) -> None:
|
|
|
396
396
|
|
|
397
397
|
|
|
398
398
|
def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
399
|
-
editors: list[str]
|
|
399
|
+
editors: list[str],
|
|
400
|
+
language_modules: list[str] | None = None) -> None:
|
|
400
401
|
eds = set(editors)
|
|
401
402
|
|
|
402
403
|
if "copilot" in eds:
|
|
@@ -413,7 +414,8 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
413
414
|
rules_dir,
|
|
414
415
|
)
|
|
415
416
|
from generate_cursor_mdc import generate as gen_cursor_mdc
|
|
416
|
-
gen_cursor_mdc(cwd
|
|
417
|
+
gen_cursor_mdc(cwd, language_modules=language_modules,
|
|
418
|
+
rules_dir=rules_dir)
|
|
417
419
|
|
|
418
420
|
if "windsurf" in eds:
|
|
419
421
|
inject_with_rules(
|
|
@@ -422,23 +424,26 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
422
424
|
rules_dir,
|
|
423
425
|
)
|
|
424
426
|
from generate_windsurf_rules import generate as gen_windsurf_rules
|
|
425
|
-
gen_windsurf_rules(cwd
|
|
427
|
+
gen_windsurf_rules(cwd, language_modules=language_modules,
|
|
428
|
+
rules_dir=rules_dir)
|
|
426
429
|
|
|
427
430
|
if "cline" in eds:
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
431
|
+
# Migrate: remove legacy .clinerules single file (replaced by directory)
|
|
432
|
+
legacy_clinerules = cwd / ".clinerules"
|
|
433
|
+
if legacy_clinerules.is_file():
|
|
434
|
+
legacy_clinerules.unlink()
|
|
435
|
+
print(" Migrated: .clinerules file → .clinerules/ directory")
|
|
433
436
|
from generate_cline_rules import generate as gen_cline_rules
|
|
434
|
-
gen_cline_rules(cwd
|
|
437
|
+
gen_cline_rules(cwd, language_modules=language_modules,
|
|
438
|
+
rules_dir=rules_dir)
|
|
435
439
|
|
|
436
440
|
if "roo" in eds:
|
|
437
441
|
roo_output = run_script("generate-roo-modes.sh", capture=True)
|
|
438
442
|
(cwd / ".roomodes").write_text(roo_output, encoding="utf-8")
|
|
439
443
|
print(" Created: .roomodes")
|
|
440
444
|
from generate_roo_rules import generate as gen_roo_rules
|
|
441
|
-
gen_roo_rules(cwd
|
|
445
|
+
gen_roo_rules(cwd, language_modules=language_modules,
|
|
446
|
+
rules_dir=rules_dir)
|
|
442
447
|
|
|
443
448
|
if "aider" in eds:
|
|
444
449
|
aider_output = run_script("generate-aider-conf.sh", capture=True)
|
|
@@ -448,10 +453,12 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
448
453
|
|
|
449
454
|
if "augment" in eds:
|
|
450
455
|
from generate_augment_rules import generate as gen_augment_rules
|
|
451
|
-
gen_augment_rules(cwd
|
|
456
|
+
gen_augment_rules(cwd, language_modules=language_modules,
|
|
457
|
+
rules_dir=rules_dir)
|
|
452
458
|
|
|
453
459
|
if "antigravity" in eds:
|
|
454
460
|
from generate_antigravity import generate as gen_antigravity
|
|
455
|
-
gen_antigravity(cwd
|
|
461
|
+
gen_antigravity(cwd, language_modules=language_modules,
|
|
462
|
+
rules_dir=rules_dir)
|
|
456
463
|
|
|
457
464
|
run_script("install-git-hooks.sh", str(cwd))
|