@softspark/ai-toolkit 1.9.0 → 2.0.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 +40 -14
- package/README.md +18 -18
- package/app/ARCHITECTURE.md +3 -3
- package/app/hooks/governance-capture.sh +1 -1
- package/app/hooks/pre-compact-save.sh +1 -1
- package/app/hooks/session-context.sh +1 -1
- package/app/hooks/track-usage.sh +2 -2
- package/app/hooks.json +20 -20
- package/app/plugins/memory-pack/README.md +1 -1
- package/app/plugins/memory-pack/hooks/observation-capture.sh +2 -2
- package/app/plugins/memory-pack/hooks/session-summary.sh +1 -1
- package/app/plugins/memory-pack/scripts/init_db.py +2 -2
- package/app/plugins/memory-pack/skills/mem-search/SKILL.md +3 -3
- package/app/skills/hook-creator/SKILL.md +2 -2
- package/app/skills/mem-search/SKILL.md +3 -3
- package/app/skills/repeat/SKILL.md +1 -1
- package/bin/ai-toolkit.js +9 -4
- package/kb/history/completed/enterprise-config-inheritance-plan-20260412.md +32 -34
- package/kb/history/completed/offline-slm-profile-plan-20260411.md +12 -12
- package/kb/planning/cloud-security-pack-plan.md +3 -3
- package/kb/procedures/maintenance-sop.md +3 -3
- package/kb/reference/architecture-overview.md +5 -5
- package/kb/reference/benchmark-config.md +1 -1
- package/kb/reference/competitive-features-implementation.md +2 -2
- package/kb/reference/enterprise-config-guide.md +9 -9
- package/kb/reference/extension-api.md +1 -1
- package/kb/reference/global-install-model.md +2 -2
- package/kb/reference/hooks-catalog.md +30 -30
- package/kb/reference/integrations.md +3 -3
- package/kb/reference/manifest-install.md +4 -4
- package/kb/reference/plugin-pack-conventions.md +5 -5
- package/kb/reference/stats.md +3 -3
- package/kb/reference/sync.md +3 -3
- package/llms-full.txt +118 -899
- package/llms.txt +0 -1
- package/manifest.json +3 -3
- package/package.json +1 -1
- package/scripts/add_rule.py +3 -2
- package/scripts/benchmark_config.py +3 -1
- package/scripts/compile_slm.py +9 -7
- package/scripts/config_cli.py +7 -7
- package/scripts/config_lock.py +12 -4
- package/scripts/config_merger.py +1 -1
- package/scripts/config_resolver.py +20 -7
- package/scripts/config_scaffold.py +4 -4
- package/scripts/config_validator.py +3 -3
- package/scripts/dir_rules_shared.py +1 -1
- package/scripts/doctor.py +3 -2
- package/scripts/install.py +12 -7
- package/scripts/install_git_hooks.py +1 -1
- package/scripts/install_steps/ai_tools.py +4 -4
- package/scripts/install_steps/hooks.py +1 -1
- package/scripts/install_steps/install_state.py +8 -6
- package/scripts/install_steps/project_registry.py +5 -5
- package/scripts/migrate.py +241 -0
- package/scripts/paths.py +50 -0
- package/scripts/plugin.py +8 -6
- package/scripts/remove_rule.py +5 -4
- package/scripts/stats.py +3 -2
- package/scripts/sync.py +3 -2
- package/scripts/update_projects.py +1 -1
- package/scripts/version_check.py +5 -2
- package/kb/planning/local-dashboard-plan.md +0 -773
package/llms.txt
CHANGED
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
- [Plan: Offline-First SLM Profile — Lightweight Mode for Local Models](kb/history/completed/offline-slm-profile-plan-20260411.md)
|
|
18
18
|
- [How-To Guides](kb/howto/README.md)
|
|
19
19
|
- [Plan: Cloud Security Pack — Multi-Cloud Audit](kb/planning/cloud-security-pack-plan.md)
|
|
20
|
-
- [Plan: Local Dashboard — `ai-toolkit ui`](kb/planning/local-dashboard-plan.md)
|
|
21
20
|
- [SOP: Claude Toolkit Maintenance](kb/procedures/maintenance-sop.md)
|
|
22
21
|
- [SOP: Release Preparation](kb/procedures/release-preparation-sop.md)
|
|
23
22
|
- [SOP: Release Verification](kb/procedures/release-verification-sop.md)
|
package/manifest.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"hooks": {
|
|
19
19
|
"description": "21 global hook entries across 12 lifecycle events + 5 skill-scoped lifecycle hooks",
|
|
20
20
|
"path": "app/hooks.json + app/hooks/*.sh",
|
|
21
|
-
"target": "~/.claude/settings.json (merge) + ~/.ai-toolkit/hooks/ (copy)",
|
|
21
|
+
"target": "~/.claude/settings.json (merge) + ~/.softspark/ai-toolkit/hooks/ (copy)",
|
|
22
22
|
"type": "merge+copy",
|
|
23
23
|
"tags": ["core", "hooks", "quality"]
|
|
24
24
|
},
|
|
@@ -159,9 +159,9 @@
|
|
|
159
159
|
},
|
|
160
160
|
"config_inheritance": {
|
|
161
161
|
"schema": "scripts/schemas/ai-toolkit-config.schema.json",
|
|
162
|
-
"project_config": ".
|
|
162
|
+
"project_config": ".softspark-toolkit.json",
|
|
163
163
|
"base_config": "ai-toolkit.config.json",
|
|
164
|
-
"lock_file": ".
|
|
164
|
+
"lock_file": ".softspark-toolkit.lock.json",
|
|
165
165
|
"v1_fields": ["extends", "profile", "agents", "rules", "constitution", "enforce"],
|
|
166
166
|
"max_extends_depth": 5
|
|
167
167
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softspark/ai-toolkit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""add-rule -- Register a rule file in ~/.ai-toolkit/rules/.
|
|
2
|
+
"""add-rule -- Register a rule file in ~/.softspark/ai-toolkit/rules/.
|
|
3
3
|
|
|
4
4
|
Registered rules are automatically injected into all AI tool configs
|
|
5
5
|
on next 'ai-toolkit install' or 'ai-toolkit update':
|
|
@@ -39,7 +39,8 @@ def main() -> None:
|
|
|
39
39
|
if not rule_name:
|
|
40
40
|
print("Error: rule name is empty after sanitization", file=sys.stderr)
|
|
41
41
|
sys.exit(1)
|
|
42
|
-
|
|
42
|
+
from paths import RULES_DIR
|
|
43
|
+
rules_dir = RULES_DIR
|
|
43
44
|
rules_dir.mkdir(parents=True, exist_ok=True)
|
|
44
45
|
|
|
45
46
|
dest = rules_dir / f"{rule_name}.md"
|
|
@@ -9,8 +9,10 @@ from pathlib import Path
|
|
|
9
9
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
10
10
|
from _common import toolkit_dir
|
|
11
11
|
|
|
12
|
+
from paths import HOOKS_DIR as _HOOKS_DIR
|
|
13
|
+
|
|
12
14
|
CLAUDE_DIR = Path.home() / ".claude"
|
|
13
|
-
HOOKS_DIR =
|
|
15
|
+
HOOKS_DIR = _HOOKS_DIR
|
|
14
16
|
DASHBOARD = toolkit_dir / "benchmarks" / "ecosystem-dashboard.json"
|
|
15
17
|
|
|
16
18
|
|
package/scripts/compile_slm.py
CHANGED
|
@@ -14,7 +14,7 @@ Options:
|
|
|
14
14
|
--model-size SIZE Model size: 7b, 8b, 14b, 32b, 70b (default: auto-detect)
|
|
15
15
|
--persona NAME Persona preset to prioritize (e.g., backend-lead)
|
|
16
16
|
--lang LANGS Comma-separated languages to include (e.g., python,typescript)
|
|
17
|
-
--output PATH Output file path (default: ~/.ai-toolkit/compiled/slm-system-prompt.md)
|
|
17
|
+
--output PATH Output file path (default: ~/.softspark/ai-toolkit/compiled/slm-system-prompt.md)
|
|
18
18
|
--format FORMAT Output format: raw, ollama, json-string, aider (default: raw)
|
|
19
19
|
--dry-run Show what would be included without writing output
|
|
20
20
|
--level LEVEL Compression level: ultra-light, light, standard, extended (default: auto)
|
|
@@ -171,7 +171,8 @@ class Component:
|
|
|
171
171
|
|
|
172
172
|
def _load_usage_stats() -> dict[str, int]:
|
|
173
173
|
"""Load skill invocation counts from stats.json."""
|
|
174
|
-
|
|
174
|
+
from paths import STATS_FILE
|
|
175
|
+
stats_path = STATS_FILE
|
|
175
176
|
if not stats_path.is_file():
|
|
176
177
|
return {}
|
|
177
178
|
try:
|
|
@@ -840,13 +841,13 @@ Note: Replace the FROM line in the Modelfile with your base model (e.g., FROM ll
|
|
|
840
841
|
## Aider Setup
|
|
841
842
|
|
|
842
843
|
1. Compile system prompt:
|
|
843
|
-
ai-toolkit compile-slm --format aider --model-size {model_size} --output .
|
|
844
|
+
ai-toolkit compile-slm --format aider --model-size {model_size} --output .softspark-toolkit-system.md
|
|
844
845
|
|
|
845
846
|
2. Run Aider with custom system prompt:
|
|
846
|
-
aider --system-prompt-file .
|
|
847
|
+
aider --system-prompt-file .softspark-toolkit-system.md
|
|
847
848
|
|
|
848
849
|
3. Or add to .aider.conf.yml:
|
|
849
|
-
system-prompt-file: .
|
|
850
|
+
system-prompt-file: .softspark-toolkit-system.md""",
|
|
850
851
|
|
|
851
852
|
"continue-dev": """\
|
|
852
853
|
## Continue.dev Setup
|
|
@@ -949,7 +950,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
949
950
|
)
|
|
950
951
|
parser.add_argument(
|
|
951
952
|
"--output", default="",
|
|
952
|
-
help="Output file path (default: ~/.ai-toolkit/compiled/slm-system-prompt.md)",
|
|
953
|
+
help="Output file path (default: ~/.softspark/ai-toolkit/compiled/slm-system-prompt.md)",
|
|
953
954
|
)
|
|
954
955
|
parser.add_argument(
|
|
955
956
|
"--format",
|
|
@@ -1021,7 +1022,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1021
1022
|
# Write output
|
|
1022
1023
|
output_path = args.output
|
|
1023
1024
|
if not output_path:
|
|
1024
|
-
|
|
1025
|
+
from paths import COMPILED_DIR
|
|
1026
|
+
compiled_dir = COMPILED_DIR
|
|
1025
1027
|
compiled_dir.mkdir(parents=True, exist_ok=True)
|
|
1026
1028
|
output_path = str(compiled_dir / "slm-system-prompt.md")
|
|
1027
1029
|
|
package/scripts/config_cli.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""CLI dispatcher for ai-toolkit config subcommands.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
|
-
ai-toolkit config validate [path] — Validate .
|
|
5
|
+
ai-toolkit config validate [path] — Validate .softspark-toolkit.json
|
|
6
6
|
ai-toolkit config diff [path] — Show project vs base differences
|
|
7
7
|
ai-toolkit config init — Interactive config setup (MVP Phase 3)
|
|
8
8
|
ai-toolkit config create-base <name> — Scaffold base config package (MVP Phase 3)
|
|
@@ -33,7 +33,7 @@ from config_scaffold import create_base_package, create_project_config
|
|
|
33
33
|
from config_lock import check_lock_staleness
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
from paths import PROJECT_CONFIG_FILENAME
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
# ---------------------------------------------------------------------------
|
|
@@ -41,7 +41,7 @@ PROJECT_CONFIG_FILENAME = ".ai-toolkit.json"
|
|
|
41
41
|
# ---------------------------------------------------------------------------
|
|
42
42
|
|
|
43
43
|
def cmd_validate(args: list[str]) -> int:
|
|
44
|
-
"""Validate .
|
|
44
|
+
"""Validate .softspark-toolkit.json schema and extends resolution."""
|
|
45
45
|
project_dir = Path(args[0]) if args else Path.cwd()
|
|
46
46
|
|
|
47
47
|
try:
|
|
@@ -350,7 +350,7 @@ def cmd_check(args: list[str]) -> int:
|
|
|
350
350
|
Exit codes:
|
|
351
351
|
0 — project complies with base config
|
|
352
352
|
1 — violations found
|
|
353
|
-
2 — .
|
|
353
|
+
2 — .softspark-toolkit.json not found
|
|
354
354
|
"""
|
|
355
355
|
project_dir = Path(args[0]) if args and not args[0].startswith("-") else Path.cwd()
|
|
356
356
|
json_output = "--json" in args
|
|
@@ -365,7 +365,7 @@ def cmd_check(args: list[str]) -> int:
|
|
|
365
365
|
return 1
|
|
366
366
|
if config is None:
|
|
367
367
|
if json_output:
|
|
368
|
-
print(json.dumps({"status": "error", "code": 2, "message": "No .
|
|
368
|
+
print(json.dumps({"status": "error", "code": 2, "message": "No .softspark-toolkit.json found"}))
|
|
369
369
|
else:
|
|
370
370
|
print(f" ✗ No {PROJECT_CONFIG_FILENAME} found in {project_dir}")
|
|
371
371
|
return 2
|
|
@@ -513,9 +513,9 @@ def main() -> None:
|
|
|
513
513
|
print("Usage: ai-toolkit config <subcommand> [args...]")
|
|
514
514
|
print()
|
|
515
515
|
print("Subcommands:")
|
|
516
|
-
print(" validate [path] Validate .
|
|
516
|
+
print(" validate [path] Validate .softspark-toolkit.json schema + extends")
|
|
517
517
|
print(" diff [path] Show project vs base config differences")
|
|
518
|
-
print(" init [flags] Create .
|
|
518
|
+
print(" init [flags] Create .softspark-toolkit.json (--extends, --profile, --no-extends)")
|
|
519
519
|
print(" create-base <name> Scaffold base config npm package")
|
|
520
520
|
print(" check [path] CI enforcement check (exit 0=pass, 1=fail, 2=no config)")
|
|
521
521
|
sys.exit(0)
|
package/scripts/config_lock.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""Lock file management for ai-toolkit config inheritance.
|
|
3
3
|
|
|
4
|
-
Generates and consumes .
|
|
4
|
+
Generates and consumes .softspark-toolkit.lock.json for reproducible
|
|
5
5
|
extends resolution across team members and CI.
|
|
6
6
|
|
|
7
7
|
Stdlib-only — no external dependencies.
|
|
@@ -15,7 +15,7 @@ from pathlib import Path
|
|
|
15
15
|
from typing import Any
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
LOCK_FILENAME = ".
|
|
18
|
+
LOCK_FILENAME = ".softspark-toolkit.lock.json"
|
|
19
19
|
LOCK_VERSION = 1
|
|
20
20
|
|
|
21
21
|
|
|
@@ -23,14 +23,22 @@ LOCK_VERSION = 1
|
|
|
23
23
|
# Public API
|
|
24
24
|
# ---------------------------------------------------------------------------
|
|
25
25
|
|
|
26
|
+
LEGACY_LOCK_FILENAME = ".ai-toolkit.lock.json"
|
|
27
|
+
|
|
28
|
+
|
|
26
29
|
def load_lock_file(project_dir: Path) -> dict[str, Any] | None:
|
|
27
30
|
"""Load lock file from project directory.
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
Falls back to legacy .ai-toolkit.lock.json if the new file doesn't exist.
|
|
33
|
+
Returns None if neither file exists.
|
|
30
34
|
"""
|
|
31
35
|
lock_path = project_dir / LOCK_FILENAME
|
|
32
36
|
if not lock_path.is_file():
|
|
33
|
-
|
|
37
|
+
legacy_path = project_dir / LEGACY_LOCK_FILENAME
|
|
38
|
+
if legacy_path.is_file():
|
|
39
|
+
lock_path = legacy_path
|
|
40
|
+
else:
|
|
41
|
+
return None
|
|
34
42
|
try:
|
|
35
43
|
with open(lock_path, encoding="utf-8") as f:
|
|
36
44
|
return json.load(f)
|
package/scripts/config_merger.py
CHANGED
|
@@ -66,7 +66,7 @@ def merge_config_chain(
|
|
|
66
66
|
|
|
67
67
|
Args:
|
|
68
68
|
base_configs: Ordered list of base config dicts (deepest ancestor first).
|
|
69
|
-
project_config: The project-level .
|
|
69
|
+
project_config: The project-level .softspark-toolkit.json data.
|
|
70
70
|
|
|
71
71
|
Returns:
|
|
72
72
|
MergeResult with the final merged config.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""Config resolver for ai-toolkit extends system.
|
|
3
3
|
|
|
4
4
|
Resolves base configs from npm packages, git URLs, or local paths.
|
|
5
|
-
Caches resolved configs in ~/.ai-toolkit/config-cache/.
|
|
5
|
+
Caches resolved configs in ~/.softspark/ai-toolkit/config-cache/.
|
|
6
6
|
|
|
7
7
|
Stdlib-only — no external dependencies.
|
|
8
8
|
"""
|
|
@@ -25,15 +25,23 @@ from typing import Any
|
|
|
25
25
|
# Constants
|
|
26
26
|
# ---------------------------------------------------------------------------
|
|
27
27
|
|
|
28
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
29
|
+
from paths import (
|
|
30
|
+
BASE_CONFIG_FILENAME,
|
|
31
|
+
CONFIG_CACHE_DIR,
|
|
32
|
+
LEGACY_PROJECT_CONFIG,
|
|
33
|
+
PROJECT_CONFIG_FILENAME as _PROJECT_CONFIG,
|
|
34
|
+
)
|
|
35
|
+
|
|
28
36
|
MAX_EXTENDS_DEPTH = 5
|
|
29
|
-
CONFIG_FILENAME =
|
|
30
|
-
PROJECT_CONFIG_FILENAME =
|
|
37
|
+
CONFIG_FILENAME = BASE_CONFIG_FILENAME
|
|
38
|
+
PROJECT_CONFIG_FILENAME = _PROJECT_CONFIG
|
|
31
39
|
CACHE_DIR_NAME = "config-cache"
|
|
32
40
|
|
|
33
41
|
|
|
34
42
|
def _cache_root() -> Path:
|
|
35
43
|
"""Return the cache root directory."""
|
|
36
|
-
return
|
|
44
|
+
return CONFIG_CACHE_DIR
|
|
37
45
|
|
|
38
46
|
|
|
39
47
|
# ---------------------------------------------------------------------------
|
|
@@ -84,7 +92,7 @@ def resolve_extends(
|
|
|
84
92
|
"""Resolve an extends chain into an ordered list of base configs.
|
|
85
93
|
|
|
86
94
|
Args:
|
|
87
|
-
extends_value: The extends string from .
|
|
95
|
+
extends_value: The extends string from .softspark-toolkit.json.
|
|
88
96
|
project_root: The project directory (for resolving relative paths).
|
|
89
97
|
refresh: Force re-fetch ignoring cache.
|
|
90
98
|
|
|
@@ -100,12 +108,17 @@ def resolve_extends(
|
|
|
100
108
|
|
|
101
109
|
|
|
102
110
|
def load_project_config(project_root: str | Path) -> dict[str, Any] | None:
|
|
103
|
-
"""Load .
|
|
111
|
+
"""Load .softspark-toolkit.json from the project root.
|
|
104
112
|
|
|
105
|
-
|
|
113
|
+
Falls back to legacy .ai-toolkit.json if the new file doesn't exist.
|
|
114
|
+
Returns None if neither file exists.
|
|
106
115
|
"""
|
|
107
116
|
config_path = Path(project_root) / PROJECT_CONFIG_FILENAME
|
|
108
117
|
if not config_path.is_file():
|
|
118
|
+
# Fallback to legacy filename
|
|
119
|
+
legacy_path = Path(project_root) / LEGACY_PROJECT_CONFIG
|
|
120
|
+
if legacy_path.is_file():
|
|
121
|
+
return _load_json(legacy_path)
|
|
109
122
|
return None
|
|
110
123
|
return _load_json(config_path)
|
|
111
124
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""Scaffolding for ai-toolkit config inheritance.
|
|
3
3
|
|
|
4
4
|
- create_base_package(): Scaffold an npm base config package
|
|
5
|
-
- create_project_config(): Generate .
|
|
5
|
+
- create_project_config(): Generate .softspark-toolkit.json for a project
|
|
6
6
|
|
|
7
7
|
Stdlib-only — no external dependencies.
|
|
8
8
|
"""
|
|
@@ -118,7 +118,7 @@ Shared ai-toolkit configuration for {org}.
|
|
|
118
118
|
|
|
119
119
|
### In your project
|
|
120
120
|
|
|
121
|
-
Create `.
|
|
121
|
+
Create `.softspark-toolkit.json` in your project root:
|
|
122
122
|
|
|
123
123
|
```json
|
|
124
124
|
{{
|
|
@@ -201,7 +201,7 @@ def create_project_config(
|
|
|
201
201
|
extends: str = "",
|
|
202
202
|
profile: str = "standard",
|
|
203
203
|
) -> Path:
|
|
204
|
-
"""Generate .
|
|
204
|
+
"""Generate .softspark-toolkit.json for a project.
|
|
205
205
|
|
|
206
206
|
Args:
|
|
207
207
|
project_dir: Project root directory.
|
|
@@ -218,7 +218,7 @@ def create_project_config(
|
|
|
218
218
|
|
|
219
219
|
config["profile"] = profile
|
|
220
220
|
|
|
221
|
-
config_path = project_dir / ".
|
|
221
|
+
config_path = project_dir / ".softspark-toolkit.json"
|
|
222
222
|
_write_json(config_path, config)
|
|
223
223
|
return config_path
|
|
224
224
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""Config validator for ai-toolkit extends system.
|
|
3
3
|
|
|
4
|
-
Validates .
|
|
4
|
+
Validates .softspark-toolkit.json against schema, checks enforce constraints,
|
|
5
5
|
verifies constitution integrity, and validates referenced files exist.
|
|
6
6
|
|
|
7
7
|
Stdlib-only — no external dependencies.
|
|
@@ -45,7 +45,7 @@ def validate_project_config(
|
|
|
45
45
|
config: dict[str, Any],
|
|
46
46
|
project_root: Path | None = None,
|
|
47
47
|
) -> list[str]:
|
|
48
|
-
"""Validate a project-level .
|
|
48
|
+
"""Validate a project-level .softspark-toolkit.json.
|
|
49
49
|
|
|
50
50
|
Returns list of error strings (empty = valid).
|
|
51
51
|
"""
|
|
@@ -352,7 +352,7 @@ def _validate_file_references(
|
|
|
352
352
|
# ---------------------------------------------------------------------------
|
|
353
353
|
|
|
354
354
|
def main() -> None:
|
|
355
|
-
"""CLI: validate .
|
|
355
|
+
"""CLI: validate .softspark-toolkit.json and print results."""
|
|
356
356
|
if len(sys.argv) < 2:
|
|
357
357
|
print("Usage: config_validator.py <config.json> [--strict]", file=sys.stderr)
|
|
358
358
|
sys.exit(1)
|
|
@@ -521,7 +521,7 @@ def build_language_rules(
|
|
|
521
521
|
def build_registered_rules(
|
|
522
522
|
rules_dir: Path | None,
|
|
523
523
|
) -> dict[str, callable]:
|
|
524
|
-
"""Build entries from user's registered rules (~/.ai-toolkit/rules/*.md).
|
|
524
|
+
"""Build entries from user's registered rules (~/.softspark/ai-toolkit/rules/*.md).
|
|
525
525
|
|
|
526
526
|
Returns dict of filename -> callable (same shape as STANDARD_RULES).
|
|
527
527
|
"""
|
package/scripts/doctor.py
CHANGED
|
@@ -27,6 +27,7 @@ from pathlib import Path
|
|
|
27
27
|
|
|
28
28
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
29
29
|
from _common import toolkit_dir
|
|
30
|
+
from paths import HOOKS_DIR as _HOOKS_DIR, RULES_DIR as _RULES_DIR
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
# ---------------------------------------------------------------------------
|
|
@@ -34,8 +35,8 @@ from _common import toolkit_dir
|
|
|
34
35
|
# ---------------------------------------------------------------------------
|
|
35
36
|
|
|
36
37
|
CLAUDE_DIR = Path.home() / ".claude"
|
|
37
|
-
HOOKS_DIR =
|
|
38
|
-
RULES_DIR =
|
|
38
|
+
HOOKS_DIR = _HOOKS_DIR
|
|
39
|
+
RULES_DIR = _RULES_DIR
|
|
39
40
|
BENCHMARK_DASHBOARD = toolkit_dir / "benchmarks" / "ecosystem-dashboard.json"
|
|
40
41
|
|
|
41
42
|
VALID_EVENTS = frozenset({
|
package/scripts/install.py
CHANGED
|
@@ -16,7 +16,7 @@ Other tools (global config locations):
|
|
|
16
16
|
- Windsurf: ~/.codeium/windsurf/memories/global_rules.md
|
|
17
17
|
- Gemini: ~/.gemini/GEMINI.md
|
|
18
18
|
|
|
19
|
-
Registered rules (~/.ai-toolkit/rules/*.md) are also injected into
|
|
19
|
+
Registered rules (~/.softspark/ai-toolkit/rules/*.md) are also injected into
|
|
20
20
|
all of the above. Add rules with: ai-toolkit add-rule <rule.md>
|
|
21
21
|
|
|
22
22
|
Usage:
|
|
@@ -467,9 +467,9 @@ def resolve_extends_config(
|
|
|
467
467
|
config_path: str = "",
|
|
468
468
|
refresh: bool = False,
|
|
469
469
|
) -> dict | None:
|
|
470
|
-
"""Resolve .
|
|
470
|
+
"""Resolve .softspark-toolkit.json extends and return merged config.
|
|
471
471
|
|
|
472
|
-
Returns None if no .
|
|
472
|
+
Returns None if no .softspark-toolkit.json or no extends field.
|
|
473
473
|
Prints warnings/errors and exits on fatal errors.
|
|
474
474
|
"""
|
|
475
475
|
if config_path:
|
|
@@ -596,6 +596,10 @@ def main() -> None:
|
|
|
596
596
|
print_status()
|
|
597
597
|
sys.exit(0)
|
|
598
598
|
|
|
599
|
+
# Auto-migrate from legacy ~/.ai-toolkit if needed
|
|
600
|
+
from migrate import run_full_migration
|
|
601
|
+
run_full_migration(dry_run=cfg.get("dry_run", False))
|
|
602
|
+
|
|
599
603
|
target_dir: Path = cfg["target_dir"]
|
|
600
604
|
only: str = cfg["only"]
|
|
601
605
|
skip: str = cfg["skip"]
|
|
@@ -618,8 +622,9 @@ def main() -> None:
|
|
|
618
622
|
if not local:
|
|
619
623
|
local = True # language rules are project-local
|
|
620
624
|
|
|
621
|
-
|
|
622
|
-
|
|
625
|
+
from paths import RULES_DIR, HOOKS_DIR, TOOLKIT_DATA_DIR
|
|
626
|
+
rules_dir = RULES_DIR
|
|
627
|
+
hooks_scripts_dir = HOOKS_DIR
|
|
623
628
|
|
|
624
629
|
# --local always implies --auto-detect (why install locally without language rules?)
|
|
625
630
|
if local and not auto_detect and not modules_arg:
|
|
@@ -653,7 +658,7 @@ def main() -> None:
|
|
|
653
658
|
|
|
654
659
|
if local:
|
|
655
660
|
# --local: project-local only, no global install
|
|
656
|
-
# Check for .
|
|
661
|
+
# Check for .softspark-toolkit.json extends system
|
|
657
662
|
config_path_arg: str = cfg["config"]
|
|
658
663
|
refresh_base: bool = cfg["refresh_base"]
|
|
659
664
|
merged_config = resolve_extends_config(
|
|
@@ -716,7 +721,7 @@ def main() -> None:
|
|
|
716
721
|
extends=extends_source,
|
|
717
722
|
)
|
|
718
723
|
if is_new:
|
|
719
|
-
print(f" Registered project in
|
|
724
|
+
print(f" Registered project in {TOOLKIT_DATA_DIR / 'projects.json'}")
|
|
720
725
|
|
|
721
726
|
print_summary(local=local)
|
|
722
727
|
|
|
@@ -30,7 +30,7 @@ if git diff --cached -S'<<<<<<<' --name-only | grep -q '.*'; then
|
|
|
30
30
|
fi
|
|
31
31
|
|
|
32
32
|
# 2. Call the global quality-check if it exists
|
|
33
|
-
QUALITY_CHECK="$HOME/.ai-toolkit/hooks/quality-check.sh"
|
|
33
|
+
QUALITY_CHECK="$HOME/.softspark/ai-toolkit/hooks/quality-check.sh"
|
|
34
34
|
if [ -x "$QUALITY_CHECK" ]; then
|
|
35
35
|
if ! "$QUALITY_CHECK"; then
|
|
36
36
|
echo "ERROR: Linter or type checks failed."
|
|
@@ -182,7 +182,7 @@ def install_local_project(rules_dir: Path, dry_run: bool, reset: bool,
|
|
|
182
182
|
- ``--editors cursor,aider``: install only these
|
|
183
183
|
- (empty): auto-detect from existing project files, install only those
|
|
184
184
|
|
|
185
|
-
If ``merged_config`` is provided (from .
|
|
185
|
+
If ``merged_config`` is provided (from .softspark-toolkit.json extends resolution),
|
|
186
186
|
additional rules and constitution amendments from the base config are injected.
|
|
187
187
|
"""
|
|
188
188
|
cwd = Path.cwd()
|
|
@@ -192,7 +192,7 @@ def install_local_project(rules_dir: Path, dry_run: bool, reset: bool,
|
|
|
192
192
|
print(f"## Project-local ({cwd})")
|
|
193
193
|
if merged_config and merged_config.get("_extends_meta"):
|
|
194
194
|
meta = merged_config["_extends_meta"]
|
|
195
|
-
print(f" Config: .
|
|
195
|
+
print(f" Config: .softspark-toolkit.json (extends: {meta['source']})")
|
|
196
196
|
if reset:
|
|
197
197
|
print(" Mode: RESET (all local configs will be wiped and recreated)")
|
|
198
198
|
if resolved_editors:
|
|
@@ -326,9 +326,9 @@ def _apply_extends_config(cwd: Path, merged: dict) -> None:
|
|
|
326
326
|
# Record merged config summary
|
|
327
327
|
meta = merged.get("_extends_meta")
|
|
328
328
|
if meta:
|
|
329
|
-
state_file = cwd / ".
|
|
329
|
+
state_file = cwd / ".softspark-toolkit-extends.json"
|
|
330
330
|
state_file.write_text(_json.dumps(meta, indent=2) + "\n", encoding="utf-8")
|
|
331
|
-
print(f" Saved: .
|
|
331
|
+
print(f" Saved: .softspark-toolkit-extends.json (resolution metadata)")
|
|
332
332
|
|
|
333
333
|
|
|
334
334
|
def _inject_language_rules(cwd: Path, language_modules: list[str] | None) -> None:
|
|
@@ -55,7 +55,7 @@ def _copy_hook_scripts(claude_dir: Path, hooks_scripts_dir: Path) -> None:
|
|
|
55
55
|
shutil.copy2(hook_file, dst)
|
|
56
56
|
dst.chmod(dst.stat().st_mode | 0o111)
|
|
57
57
|
copied += 1
|
|
58
|
-
print(f" Copied: {copied} hook scripts to ~/.ai-toolkit/hooks/")
|
|
58
|
+
print(f" Copied: {copied} hook scripts to ~/.softspark/ai-toolkit/hooks/")
|
|
59
59
|
legacy_hooks = claude_dir / "hooks"
|
|
60
60
|
if legacy_hooks.is_symlink():
|
|
61
61
|
legacy_hooks.unlink()
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
"""Track installed modules and versions in ~/.ai-toolkit/state.json."""
|
|
1
|
+
"""Track installed modules and versions in ~/.softspark/ai-toolkit/state.json."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
|
+
import sys
|
|
5
6
|
from datetime import datetime, timezone
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
10
|
+
from paths import TOOLKIT_DATA_DIR, STATE_FILE
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
def _state_path() -> Path:
|
|
12
14
|
"""Return the canonical path to state.json."""
|
|
13
|
-
return
|
|
15
|
+
return STATE_FILE
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
def load_state() -> dict:
|
|
17
|
-
"""Load state from ~/.ai-toolkit/state.json.
|
|
19
|
+
"""Load state from ~/.softspark/ai-toolkit/state.json.
|
|
18
20
|
|
|
19
21
|
Returns an empty dict if the file does not exist or is malformed.
|
|
20
22
|
"""
|
|
@@ -32,7 +34,7 @@ def load_state() -> dict:
|
|
|
32
34
|
|
|
33
35
|
|
|
34
36
|
def save_state(state: dict) -> None:
|
|
35
|
-
"""Save state to ~/.ai-toolkit/state.json.
|
|
37
|
+
"""Save state to ~/.softspark/ai-toolkit/state.json.
|
|
36
38
|
|
|
37
39
|
Creates the parent directory if it does not exist.
|
|
38
40
|
"""
|
|
@@ -113,7 +115,7 @@ def print_status() -> None:
|
|
|
113
115
|
"""Print a human-readable summary of the install state."""
|
|
114
116
|
state = load_state()
|
|
115
117
|
if not state:
|
|
116
|
-
print("No install state found (
|
|
118
|
+
print(f"No install state found ({STATE_FILE} missing).")
|
|
117
119
|
print("Run 'ai-toolkit install' to set up the toolkit.")
|
|
118
120
|
return
|
|
119
121
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Project registry — tracks which directories have ai-toolkit installed locally.
|
|
2
2
|
|
|
3
|
-
Stores registry in ~/.ai-toolkit/projects.json.
|
|
3
|
+
Stores registry in ~/.softspark/ai-toolkit/projects.json.
|
|
4
4
|
Used by `ai-toolkit update` to propagate updates to all registered projects,
|
|
5
5
|
and by `ai-toolkit projects` to list/manage them.
|
|
6
6
|
|
|
@@ -9,18 +9,18 @@ Stdlib-only — no external dependencies.
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
import json
|
|
12
|
-
import
|
|
12
|
+
import sys
|
|
13
13
|
from datetime import datetime, timezone
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
from typing import Any
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
18
|
+
from paths import PROJECTS_FILE
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def _registry_path() -> Path:
|
|
22
22
|
"""Return the canonical path to projects.json."""
|
|
23
|
-
return
|
|
23
|
+
return PROJECTS_FILE
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def _now_iso() -> str:
|