add-skill-kit 3.2.2 → 3.2.4
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/bin/lib/commands/install.js +67 -45
- package/lib/agent-cli/README.md +21 -0
- package/lib/agent-cli/bin/ag-smart.js +158 -0
- package/lib/agent-cli/lib/audit.js +154 -0
- package/lib/agent-cli/lib/audit.test.js +100 -0
- package/lib/agent-cli/lib/auto-learn.js +319 -0
- package/lib/agent-cli/lib/auto_preview.py +148 -0
- package/lib/agent-cli/lib/backup.js +138 -0
- package/lib/agent-cli/lib/backup.test.js +78 -0
- package/lib/agent-cli/lib/checklist.py +222 -0
- package/lib/agent-cli/lib/cognitive-lesson.js +476 -0
- package/lib/agent-cli/lib/completion.js +149 -0
- package/lib/agent-cli/lib/config.js +35 -0
- package/lib/agent-cli/lib/eslint-fix.js +238 -0
- package/lib/agent-cli/lib/evolution-signal.js +215 -0
- package/lib/agent-cli/lib/export.js +86 -0
- package/lib/agent-cli/lib/export.test.js +65 -0
- package/lib/agent-cli/lib/fix.js +337 -0
- package/lib/agent-cli/lib/fix.test.js +80 -0
- package/lib/agent-cli/lib/gemini-export.js +83 -0
- package/lib/agent-cli/lib/generate-registry.js +42 -0
- package/lib/agent-cli/lib/hooks/install-hooks.js +152 -0
- package/lib/agent-cli/lib/hooks/lint-learn.js +172 -0
- package/lib/agent-cli/lib/ignore.js +116 -0
- package/lib/agent-cli/lib/ignore.test.js +58 -0
- package/lib/agent-cli/lib/init.js +124 -0
- package/lib/agent-cli/lib/learn.js +255 -0
- package/lib/agent-cli/lib/learn.test.js +70 -0
- package/lib/agent-cli/lib/migrate-to-v4.js +322 -0
- package/lib/agent-cli/lib/proposals.js +199 -0
- package/lib/agent-cli/lib/proposals.test.js +56 -0
- package/lib/agent-cli/lib/recall.js +820 -0
- package/lib/agent-cli/lib/recall.test.js +107 -0
- package/lib/agent-cli/lib/selfevolution-bridge.js +167 -0
- package/lib/agent-cli/lib/session_manager.py +120 -0
- package/lib/agent-cli/lib/settings.js +203 -0
- package/lib/agent-cli/lib/skill-learn.js +296 -0
- package/lib/agent-cli/lib/stats.js +132 -0
- package/lib/agent-cli/lib/stats.test.js +94 -0
- package/lib/agent-cli/lib/types.js +33 -0
- package/lib/agent-cli/lib/ui/audit-ui.js +146 -0
- package/lib/agent-cli/lib/ui/backup-ui.js +107 -0
- package/lib/agent-cli/lib/ui/clack-helpers.js +317 -0
- package/lib/agent-cli/lib/ui/common.js +83 -0
- package/lib/agent-cli/lib/ui/completion-ui.js +126 -0
- package/lib/agent-cli/lib/ui/custom-select.js +69 -0
- package/lib/agent-cli/lib/ui/dashboard-ui.js +123 -0
- package/lib/agent-cli/lib/ui/evolution-signals-ui.js +107 -0
- package/lib/agent-cli/lib/ui/export-ui.js +94 -0
- package/lib/agent-cli/lib/ui/fix-all-ui.js +191 -0
- package/lib/agent-cli/lib/ui/help-ui.js +49 -0
- package/lib/agent-cli/lib/ui/index.js +169 -0
- package/lib/agent-cli/lib/ui/init-ui.js +56 -0
- package/lib/agent-cli/lib/ui/knowledge-ui.js +55 -0
- package/lib/agent-cli/lib/ui/learn-ui.js +706 -0
- package/lib/agent-cli/lib/ui/lessons-ui.js +148 -0
- package/lib/agent-cli/lib/ui/pretty.js +145 -0
- package/lib/agent-cli/lib/ui/proposals-ui.js +99 -0
- package/lib/agent-cli/lib/ui/recall-ui.js +342 -0
- package/lib/agent-cli/lib/ui/routing-demo.js +79 -0
- package/lib/agent-cli/lib/ui/routing-ui.js +325 -0
- package/lib/agent-cli/lib/ui/settings-ui.js +381 -0
- package/lib/agent-cli/lib/ui/stats-ui.js +123 -0
- package/lib/agent-cli/lib/ui/watch-ui.js +236 -0
- package/lib/agent-cli/lib/verify_all.py +327 -0
- package/lib/agent-cli/lib/watcher.js +181 -0
- package/lib/agent-cli/lib/watcher.test.js +85 -0
- package/lib/agent-cli/package.json +51 -0
- package/lib/agentskillskit-cli/README.md +21 -0
- package/lib/agentskillskit-cli/ag-smart.js +158 -0
- package/lib/agentskillskit-cli/package.json +51 -0
- package/package.json +11 -6
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Master Checklist Runner - Agent Skill Kit
|
|
4
|
+
==========================================
|
|
5
|
+
|
|
6
|
+
Orchestrates all validation scripts in priority order.
|
|
7
|
+
Use this for incremental validation during development.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python scripts/checklist.py . # Run core checks
|
|
11
|
+
python scripts/checklist.py . --url <URL> # Include performance checks
|
|
12
|
+
|
|
13
|
+
Priority Order:
|
|
14
|
+
P0: Security Scan (vulnerabilities, secrets)
|
|
15
|
+
P1: Lint & Type Check (code quality)
|
|
16
|
+
P2: Schema Validation (if database exists)
|
|
17
|
+
P3: Test Runner (unit/integration tests)
|
|
18
|
+
P4: UX Audit (psychology laws, accessibility)
|
|
19
|
+
P5: SEO Check (meta tags, structure)
|
|
20
|
+
P6: Performance (lighthouse - requires URL)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import sys
|
|
24
|
+
import subprocess
|
|
25
|
+
import argparse
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import List, Tuple, Optional
|
|
28
|
+
|
|
29
|
+
# ANSI colors for terminal output
|
|
30
|
+
class Colors:
|
|
31
|
+
HEADER = '\033[95m'
|
|
32
|
+
BLUE = '\033[94m'
|
|
33
|
+
CYAN = '\033[96m'
|
|
34
|
+
GREEN = '\033[92m'
|
|
35
|
+
YELLOW = '\033[93m'
|
|
36
|
+
RED = '\033[91m'
|
|
37
|
+
ENDC = '\033[0m'
|
|
38
|
+
BOLD = '\033[1m'
|
|
39
|
+
|
|
40
|
+
def print_header(text: str):
|
|
41
|
+
print(f"\n{Colors.BOLD}{Colors.CYAN}{'='*60}{Colors.ENDC}")
|
|
42
|
+
print(f"{Colors.BOLD}{Colors.CYAN}{text.center(60)}{Colors.ENDC}")
|
|
43
|
+
print(f"{Colors.BOLD}{Colors.CYAN}{'='*60}{Colors.ENDC}\n")
|
|
44
|
+
|
|
45
|
+
def print_step(text: str):
|
|
46
|
+
print(f"{Colors.BOLD}{Colors.BLUE}🔄 {text}{Colors.ENDC}")
|
|
47
|
+
|
|
48
|
+
def print_success(text: str):
|
|
49
|
+
print(f"{Colors.GREEN}✅ {text}{Colors.ENDC}")
|
|
50
|
+
|
|
51
|
+
def print_warning(text: str):
|
|
52
|
+
print(f"{Colors.YELLOW}⚠️ {text}{Colors.ENDC}")
|
|
53
|
+
|
|
54
|
+
def print_error(text: str):
|
|
55
|
+
print(f"{Colors.RED}❌ {text}{Colors.ENDC}")
|
|
56
|
+
|
|
57
|
+
# Define priority-ordered checks
|
|
58
|
+
CORE_CHECKS = [
|
|
59
|
+
("Security Scan", ".agent/skills/vulnerability-scanner/scripts/security_scan.py", True),
|
|
60
|
+
("Smart Audit", ".agent/scripts/audit.js", True),
|
|
61
|
+
("Lint Check", ".agent/skills/lint-and-validate/scripts/lint_runner.py", True),
|
|
62
|
+
("Schema Validation", ".agent/skills/database-design/scripts/schema_validator.py", False),
|
|
63
|
+
("Test Runner", ".agent/skills/testing-patterns/scripts/test_runner.py", False),
|
|
64
|
+
("UX Audit", ".agent/skills/frontend-design/scripts/ux_audit.py", False),
|
|
65
|
+
("SEO Check", ".agent/skills/seo-fundamentals/scripts/seo_checker.py", False),
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
PERFORMANCE_CHECKS = [
|
|
69
|
+
("Lighthouse Audit", ".agent/skills/performance-profiling/scripts/lighthouse_audit.py", True),
|
|
70
|
+
("Playwright E2E", ".agent/skills/webapp-testing/scripts/playwright_runner.py", False),
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
def check_script_exists(script_path: Path) -> bool:
|
|
74
|
+
"""Check if script file exists"""
|
|
75
|
+
return script_path.exists() and script_path.is_file()
|
|
76
|
+
|
|
77
|
+
def run_script(name: str, script_path: Path, project_path: str, url: Optional[str] = None) -> dict:
|
|
78
|
+
"""
|
|
79
|
+
Run a validation script and capture results
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
dict with keys: name, passed, output, skipped
|
|
83
|
+
"""
|
|
84
|
+
if not check_script_exists(script_path):
|
|
85
|
+
print_warning(f"{name}: Script not found, skipping")
|
|
86
|
+
return {"name": name, "passed": True, "output": "", "skipped": True}
|
|
87
|
+
|
|
88
|
+
print_step(f"Running: {name}")
|
|
89
|
+
|
|
90
|
+
# Build command
|
|
91
|
+
if str(script_path).endswith('.js'):
|
|
92
|
+
cmd = ["node", str(script_path), project_path]
|
|
93
|
+
else:
|
|
94
|
+
cmd = ["python", str(script_path), project_path]
|
|
95
|
+
|
|
96
|
+
if url and ("lighthouse" in script_path.name.lower() or "playwright" in script_path.name.lower()):
|
|
97
|
+
cmd.append(url)
|
|
98
|
+
|
|
99
|
+
# Run script
|
|
100
|
+
try:
|
|
101
|
+
result = subprocess.run(
|
|
102
|
+
cmd,
|
|
103
|
+
capture_output=True,
|
|
104
|
+
text=True,
|
|
105
|
+
timeout=300 # 5 minute timeout
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
passed = result.returncode == 0
|
|
109
|
+
|
|
110
|
+
if passed:
|
|
111
|
+
print_success(f"{name}: PASSED")
|
|
112
|
+
else:
|
|
113
|
+
print_error(f"{name}: FAILED")
|
|
114
|
+
if result.stderr:
|
|
115
|
+
print(f" Error: {result.stderr[:200]}")
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
"name": name,
|
|
119
|
+
"passed": passed,
|
|
120
|
+
"output": result.stdout,
|
|
121
|
+
"error": result.stderr,
|
|
122
|
+
"skipped": False
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
except subprocess.TimeoutExpired:
|
|
126
|
+
print_error(f"{name}: TIMEOUT (>5 minutes)")
|
|
127
|
+
return {"name": name, "passed": False, "output": "", "error": "Timeout", "skipped": False}
|
|
128
|
+
|
|
129
|
+
except Exception as e:
|
|
130
|
+
print_error(f"{name}: ERROR - {str(e)}")
|
|
131
|
+
return {"name": name, "passed": False, "output": "", "error": str(e), "skipped": False}
|
|
132
|
+
|
|
133
|
+
def print_summary(results: List[dict]):
|
|
134
|
+
"""Print final summary report"""
|
|
135
|
+
print_header("📊 CHECKLIST SUMMARY")
|
|
136
|
+
|
|
137
|
+
passed_count = sum(1 for r in results if r["passed"] and not r.get("skipped"))
|
|
138
|
+
failed_count = sum(1 for r in results if not r["passed"] and not r.get("skipped"))
|
|
139
|
+
skipped_count = sum(1 for r in results if r.get("skipped"))
|
|
140
|
+
|
|
141
|
+
print(f"Total Checks: {len(results)}")
|
|
142
|
+
print(f"{Colors.GREEN}✅ Passed: {passed_count}{Colors.ENDC}")
|
|
143
|
+
print(f"{Colors.RED}❌ Failed: {failed_count}{Colors.ENDC}")
|
|
144
|
+
print(f"{Colors.YELLOW}⏭️ Skipped: {skipped_count}{Colors.ENDC}")
|
|
145
|
+
print()
|
|
146
|
+
|
|
147
|
+
# Detailed results
|
|
148
|
+
for r in results:
|
|
149
|
+
if r.get("skipped"):
|
|
150
|
+
status = f"{Colors.YELLOW}⏭️ {Colors.ENDC}"
|
|
151
|
+
elif r["passed"]:
|
|
152
|
+
status = f"{Colors.GREEN}✅{Colors.ENDC}"
|
|
153
|
+
else:
|
|
154
|
+
status = f"{Colors.RED}❌{Colors.ENDC}"
|
|
155
|
+
|
|
156
|
+
print(f"{status} {r['name']}")
|
|
157
|
+
|
|
158
|
+
print()
|
|
159
|
+
|
|
160
|
+
if failed_count > 0:
|
|
161
|
+
print_error(f"{failed_count} check(s) FAILED - Please fix before proceeding")
|
|
162
|
+
return False
|
|
163
|
+
else:
|
|
164
|
+
print_success("All checks PASSED ✨")
|
|
165
|
+
return True
|
|
166
|
+
|
|
167
|
+
def main():
|
|
168
|
+
parser = argparse.ArgumentParser(
|
|
169
|
+
description="Run Agent Skill Kit validation checklist",
|
|
170
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
171
|
+
epilog="""
|
|
172
|
+
Examples:
|
|
173
|
+
python scripts/checklist.py . # Core checks only
|
|
174
|
+
python scripts/checklist.py . --url http://localhost:3000 # Include performance
|
|
175
|
+
"""
|
|
176
|
+
)
|
|
177
|
+
parser.add_argument("project", help="Project path to validate")
|
|
178
|
+
parser.add_argument("--url", help="URL for performance checks (lighthouse, playwright)")
|
|
179
|
+
parser.add_argument("--skip-performance", action="store_true", help="Skip performance checks even if URL provided")
|
|
180
|
+
|
|
181
|
+
args = parser.parse_args()
|
|
182
|
+
|
|
183
|
+
project_path = Path(args.project).resolve()
|
|
184
|
+
|
|
185
|
+
if not project_path.exists():
|
|
186
|
+
print_error(f"Project path does not exist: {project_path}")
|
|
187
|
+
sys.exit(1)
|
|
188
|
+
|
|
189
|
+
print_header("🚀 AGENT SKILLS KIT - MASTER CHECKLIST")
|
|
190
|
+
print(f"Project: {project_path}")
|
|
191
|
+
print(f"URL: {args.url if args.url else 'Not provided (performance checks skipped)'}")
|
|
192
|
+
|
|
193
|
+
results = []
|
|
194
|
+
|
|
195
|
+
# Run core checks
|
|
196
|
+
print_header("📋 CORE CHECKS")
|
|
197
|
+
for name, script_path, required in CORE_CHECKS:
|
|
198
|
+
script = project_path / script_path
|
|
199
|
+
result = run_script(name, script, str(project_path))
|
|
200
|
+
results.append(result)
|
|
201
|
+
|
|
202
|
+
# If required check fails, stop
|
|
203
|
+
if required and not result["passed"] and not result.get("skipped"):
|
|
204
|
+
print_error(f"CRITICAL: {name} failed. Stopping checklist.")
|
|
205
|
+
print_summary(results)
|
|
206
|
+
sys.exit(1)
|
|
207
|
+
|
|
208
|
+
# Run performance checks if URL provided
|
|
209
|
+
if args.url and not args.skip_performance:
|
|
210
|
+
print_header("⚡ PERFORMANCE CHECKS")
|
|
211
|
+
for name, script_path, required in PERFORMANCE_CHECKS:
|
|
212
|
+
script = project_path / script_path
|
|
213
|
+
result = run_script(name, script, str(project_path), args.url)
|
|
214
|
+
results.append(result)
|
|
215
|
+
|
|
216
|
+
# Print summary
|
|
217
|
+
all_passed = print_summary(results)
|
|
218
|
+
|
|
219
|
+
sys.exit(0 if all_passed else 1)
|
|
220
|
+
|
|
221
|
+
if __name__ == "__main__":
|
|
222
|
+
main()
|
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cognitive Lesson Engine - Intelligence Layers
|
|
3
|
+
*
|
|
4
|
+
* This module implements the cognitive intelligence that transforms
|
|
5
|
+
* raw data (mistakes.yaml + improvements.yaml) into Cognitive Lesson Units
|
|
6
|
+
*
|
|
7
|
+
* Levels:
|
|
8
|
+
* 1. Intent Inference - detect purpose
|
|
9
|
+
* 2. Pattern Intelligence - context matching
|
|
10
|
+
* 3. Maturity Calculation - confidence & state
|
|
11
|
+
* 4. Evolution Analysis - gap detection
|
|
12
|
+
* 5. Feedback Loop - self-evolution
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import yaml from 'js-yaml';
|
|
18
|
+
import { fileURLToPath } from 'url';
|
|
19
|
+
|
|
20
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const PROJECT_ROOT = path.join(__dirname, '../../..');
|
|
22
|
+
const KNOWLEDGE_DIR = path.join(PROJECT_ROOT, '.agent/knowledge');
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// LEVEL 0: Data Loading
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Load mistakes from YAML
|
|
30
|
+
*/
|
|
31
|
+
export function loadMistakes() {
|
|
32
|
+
const filepath = path.join(KNOWLEDGE_DIR, 'mistakes.yaml');
|
|
33
|
+
if (!fs.existsSync(filepath)) {
|
|
34
|
+
return { version: 4.0, mistakes: [] };
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const content = fs.readFileSync(filepath, 'utf8');
|
|
38
|
+
return yaml.load(content) || { version: 4.0, mistakes: [] };
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('Error loading mistakes:', error.message);
|
|
41
|
+
return { version: 4.0, mistakes: [] };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Load improvements from YAML
|
|
47
|
+
*/
|
|
48
|
+
export function loadImprovements() {
|
|
49
|
+
const filepath = path.join(KNOWLEDGE_DIR, 'improvements.yaml');
|
|
50
|
+
if (!fs.existsSync(filepath)) {
|
|
51
|
+
return { version: 4.0, improvements: [] };
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const content = fs.readFileSync(filepath, 'utf8');
|
|
55
|
+
return yaml.load(content) || { version: 4.0, improvements: [] };
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error('Error loading improvements:', error.message);
|
|
58
|
+
return { version: 4.0, improvements: [] };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// LEVEL 1: Intent Inference
|
|
64
|
+
// ============================================================================
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Intent vocabulary - maps tag patterns to intents
|
|
68
|
+
*/
|
|
69
|
+
const INTENT_PATTERNS = {
|
|
70
|
+
'safe-rebranding': {
|
|
71
|
+
keywords: ['rebrand', 'rename', 'file-safety'],
|
|
72
|
+
goal: 'Rename files/entities without data loss or breaking changes',
|
|
73
|
+
category: 'file-operations',
|
|
74
|
+
},
|
|
75
|
+
'cli-ux-consistency': {
|
|
76
|
+
keywords: ['cli', 'ux', 'menu', 'navigation', 'clack'],
|
|
77
|
+
goal: 'Provide consistent, intuitive CLI user experience',
|
|
78
|
+
category: 'user-experience',
|
|
79
|
+
},
|
|
80
|
+
'error-prevention': {
|
|
81
|
+
keywords: ['validation', 'error', 'check', 'verify'],
|
|
82
|
+
goal: 'Prevent runtime errors through proactive validation',
|
|
83
|
+
category: 'reliability',
|
|
84
|
+
},
|
|
85
|
+
'code-quality': {
|
|
86
|
+
keywords: ['import', 'architecture', 'quality'],
|
|
87
|
+
goal: 'Maintain clean, maintainable code structure',
|
|
88
|
+
category: 'maintainability',
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Infer lesson intent from tags
|
|
94
|
+
* NO NEW FILE - computed runtime
|
|
95
|
+
*/
|
|
96
|
+
export function inferIntent(tags) {
|
|
97
|
+
if (!tags || tags.length === 0) {
|
|
98
|
+
return {
|
|
99
|
+
id: 'unknown',
|
|
100
|
+
goal: 'General code quality improvement',
|
|
101
|
+
strength: 0.3,
|
|
102
|
+
category: 'general',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let bestMatch = null;
|
|
107
|
+
let bestScore = 0;
|
|
108
|
+
|
|
109
|
+
for (const [intentId, pattern] of Object.entries(INTENT_PATTERNS)) {
|
|
110
|
+
const matchCount = tags.filter(tag =>
|
|
111
|
+
pattern.keywords.some(kw => tag.toLowerCase().includes(kw.toLowerCase()))
|
|
112
|
+
).length;
|
|
113
|
+
|
|
114
|
+
const score = matchCount / tags.length; // 0-1 confidence
|
|
115
|
+
|
|
116
|
+
if (score > bestScore) {
|
|
117
|
+
bestScore = score;
|
|
118
|
+
bestMatch = {
|
|
119
|
+
id: intentId,
|
|
120
|
+
goal: pattern.goal,
|
|
121
|
+
strength: score,
|
|
122
|
+
category: pattern.category,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return bestMatch || {
|
|
128
|
+
id: 'unknown',
|
|
129
|
+
goal: 'General code quality improvement',
|
|
130
|
+
strength: 0.3,
|
|
131
|
+
category: 'general',
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ============================================================================
|
|
136
|
+
// LEVEL 2: Pattern Intelligence
|
|
137
|
+
// ============================================================================
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Build execution context from code
|
|
141
|
+
* Used for contextual pattern matching
|
|
142
|
+
*/
|
|
143
|
+
export function buildContext(filePath, code) {
|
|
144
|
+
const context = {
|
|
145
|
+
framework: null,
|
|
146
|
+
intents: [],
|
|
147
|
+
environment: 'development',
|
|
148
|
+
fileType: path.extname(filePath),
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// Detect framework from imports
|
|
152
|
+
if (code.includes('@clack/prompts')) context.framework = 'clack';
|
|
153
|
+
if (code.includes('inquirer')) context.framework = 'inquirer';
|
|
154
|
+
if (code.includes('react')) context.framework = 'react';
|
|
155
|
+
|
|
156
|
+
// Detect intent from code patterns
|
|
157
|
+
if (code.includes('p.select') || code.includes('showActionMenu')) {
|
|
158
|
+
context.intents.push('select-menu');
|
|
159
|
+
}
|
|
160
|
+
if (code.includes('rename') || code.includes('move')) {
|
|
161
|
+
context.intents.push('file-operation');
|
|
162
|
+
}
|
|
163
|
+
if (code.includes('import')) {
|
|
164
|
+
context.intents.push('module-usage');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return context;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Contextual pattern matching
|
|
172
|
+
* Smarter than text-only regex
|
|
173
|
+
*/
|
|
174
|
+
export function matchPattern(code, mistake, context) {
|
|
175
|
+
// Basic pattern match
|
|
176
|
+
const regex = new RegExp(mistake.pattern);
|
|
177
|
+
const patternMatch = regex.test(code);
|
|
178
|
+
|
|
179
|
+
if (!patternMatch) return false;
|
|
180
|
+
|
|
181
|
+
// Context filtering - skip if not applicable
|
|
182
|
+
if (mistake.when) {
|
|
183
|
+
// Check framework requirement
|
|
184
|
+
if (mistake.when.framework && context.framework !== mistake.when.framework) {
|
|
185
|
+
return false; // Wrong framework, not applicable
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check intent requirement
|
|
189
|
+
if (mistake.when.intent && !context.intents.includes(mistake.when.intent)) {
|
|
190
|
+
return false; // Intent mismatch
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return true; // Pattern + context match
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ============================================================================
|
|
198
|
+
// LEVEL 3: Maturity Calculation
|
|
199
|
+
// ============================================================================
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Calculate evidence score based on hit counts
|
|
203
|
+
*/
|
|
204
|
+
function calculateEvidenceScore(mistakes, improvements) {
|
|
205
|
+
const totalMistakeHits = mistakes.reduce((sum, m) => sum + (m.hitCount || 0), 0);
|
|
206
|
+
const totalImprovementHits = improvements.reduce((sum, i) => sum + (i.appliedCount || 0), 0);
|
|
207
|
+
|
|
208
|
+
const totalHits = totalMistakeHits + totalImprovementHits;
|
|
209
|
+
|
|
210
|
+
if (totalHits === 0) return 0.1; // No evidence
|
|
211
|
+
if (totalHits < 5) return 0.3; // Weak
|
|
212
|
+
if (totalHits < 20) return 0.6; // Moderate
|
|
213
|
+
return 0.9; // Strong
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Calculate recency score
|
|
218
|
+
*/
|
|
219
|
+
function calculateRecencyScore(mistakes, improvements) {
|
|
220
|
+
const now = Date.now();
|
|
221
|
+
|
|
222
|
+
const allDates = [
|
|
223
|
+
...mistakes.map(m => new Date(m.lastHit || m.added).getTime()),
|
|
224
|
+
...improvements.map(i => new Date(i.lastApplied || i.added).getTime()),
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
if (allDates.length === 0) return 0.5;
|
|
228
|
+
|
|
229
|
+
const lastUpdate = Math.max(...allDates);
|
|
230
|
+
const daysSinceUpdate = (now - lastUpdate) / (1000 * 60 * 60 * 24);
|
|
231
|
+
|
|
232
|
+
if (daysSinceUpdate < 7) return 1.0; // This week
|
|
233
|
+
if (daysSinceUpdate < 30) return 0.8; // This month
|
|
234
|
+
if (daysSinceUpdate < 90) return 0.5; // This quarter
|
|
235
|
+
return 0.3; // Stale
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get recommendation based on state and confidence
|
|
240
|
+
*/
|
|
241
|
+
function getRecommendation(state, confidence) {
|
|
242
|
+
if (state === 'RAW') {
|
|
243
|
+
return 'URGENT: Discover best practices for this area';
|
|
244
|
+
}
|
|
245
|
+
if (state === 'LEARNING' && confidence < 0.5) {
|
|
246
|
+
return 'Needs more improvements to balance anti-patterns';
|
|
247
|
+
}
|
|
248
|
+
if (state === 'MATURE' && confidence > 0.8) {
|
|
249
|
+
return 'Stable - can be trusted for skill injection';
|
|
250
|
+
}
|
|
251
|
+
if (state === 'IDEAL') {
|
|
252
|
+
return 'Perfect - all best practices, no anti-patterns';
|
|
253
|
+
}
|
|
254
|
+
return 'Continue learning';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Calculate lesson maturity metrics
|
|
259
|
+
*/
|
|
260
|
+
export function calculateMaturity(mistakes, improvements) {
|
|
261
|
+
const m = mistakes.length;
|
|
262
|
+
const i = improvements.length;
|
|
263
|
+
|
|
264
|
+
// State determination
|
|
265
|
+
let state;
|
|
266
|
+
if (i === 0 && m > 0) state = 'RAW'; // 🟥 Only mistakes
|
|
267
|
+
else if (i > 0 && m === 0) state = 'IDEAL'; // 🟦 Only improvements (rare)
|
|
268
|
+
else if (i >= m) state = 'MATURE'; // 🟩 Balanced or improvement-heavy
|
|
269
|
+
else state = 'LEARNING'; // 🟨 More mistakes than solutions
|
|
270
|
+
|
|
271
|
+
// Multi-factor confidence
|
|
272
|
+
const balanceScore = i / (m + i); // 0-1: improvement ratio
|
|
273
|
+
const evidenceScore = calculateEvidenceScore(mistakes, improvements); // 0-1: hit validation
|
|
274
|
+
const recencyScore = calculateRecencyScore(mistakes, improvements); // 0-1: freshness
|
|
275
|
+
|
|
276
|
+
const confidence = (
|
|
277
|
+
balanceScore * 0.5 + // Balance is most important
|
|
278
|
+
evidenceScore * 0.3 + // Evidence validates
|
|
279
|
+
recencyScore * 0.2 // Fresh is better
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
state,
|
|
284
|
+
confidence: Math.round(confidence * 100) / 100,
|
|
285
|
+
indicators: {
|
|
286
|
+
balance: Math.round(balanceScore * 100) / 100,
|
|
287
|
+
evidence: Math.round(evidenceScore * 100) / 100,
|
|
288
|
+
recency: Math.round(recencyScore * 100) / 100,
|
|
289
|
+
},
|
|
290
|
+
coverage: `${m} mistakes / ${i} improvements`,
|
|
291
|
+
recommendation: getRecommendation(state, confidence),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ============================================================================
|
|
296
|
+
// LEVEL 4: Evolution Analysis
|
|
297
|
+
// ============================================================================
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Check if mistake has related improvement
|
|
301
|
+
*/
|
|
302
|
+
function hasRelatedImprovement(mistake, improvements) {
|
|
303
|
+
return improvements.some(i =>
|
|
304
|
+
i.tags && mistake.tags && i.tags.some(tag => mistake.tags.includes(tag))
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Determine next action from signals
|
|
310
|
+
*/
|
|
311
|
+
function determineNextAction(signals) {
|
|
312
|
+
if (signals.some(s => s.priority === 'CRITICAL')) {
|
|
313
|
+
return 'Document solution for high-frequency violations immediately';
|
|
314
|
+
}
|
|
315
|
+
if (signals.some(s => s.type === 'SUGGEST_IMPROVEMENT_DISCOVERY')) {
|
|
316
|
+
return 'Research and document best practices in underserved areas';
|
|
317
|
+
}
|
|
318
|
+
return 'Continue normal learning';
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Analyze evolution needs and gaps
|
|
323
|
+
*/
|
|
324
|
+
export function analyzeEvolution(mistakes, improvements, intent) {
|
|
325
|
+
const signals = [];
|
|
326
|
+
const missingAreas = [];
|
|
327
|
+
|
|
328
|
+
// Signal 1: Many mistakes, few solutions
|
|
329
|
+
if (mistakes.length > improvements.length * 2) {
|
|
330
|
+
signals.push({
|
|
331
|
+
type: 'SUGGEST_IMPROVEMENT_DISCOVERY',
|
|
332
|
+
priority: 'HIGH',
|
|
333
|
+
reason: `${mistakes.length} anti-patterns but only ${improvements.length} solution(s)`,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Signal 2: Uncovered tags
|
|
338
|
+
const mistakeTags = new Set(mistakes.flatMap(m => m.tags || []));
|
|
339
|
+
const improvementTags = new Set(improvements.flatMap(i => i.tags || []));
|
|
340
|
+
|
|
341
|
+
const uncoveredTags = [...mistakeTags].filter(tag => !improvementTags.has(tag));
|
|
342
|
+
|
|
343
|
+
uncoveredTags.forEach(tag => {
|
|
344
|
+
missingAreas.push({
|
|
345
|
+
area: tag,
|
|
346
|
+
reason: 'Anti-patterns detected but no best practice documented',
|
|
347
|
+
mistakeCount: mistakes.filter(m => m.tags && m.tags.includes(tag)).length,
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Signal 3: High-hit mistakes without solution
|
|
352
|
+
mistakes.forEach(m => {
|
|
353
|
+
if ((m.hitCount || 0) > 10 && !hasRelatedImprovement(m, improvements)) {
|
|
354
|
+
signals.push({
|
|
355
|
+
type: 'HOT_MISTAKE_NEEDS_SOLUTION',
|
|
356
|
+
priority: 'CRITICAL',
|
|
357
|
+
mistake: m.id,
|
|
358
|
+
reason: `${m.title} hit ${m.hitCount} times but no solution documented`,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
missingAreas.push({
|
|
362
|
+
area: `solution-for-${m.id}`,
|
|
363
|
+
reason: 'Frequently violated anti-pattern needs best practice',
|
|
364
|
+
hitCount: m.hitCount,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// Signal 4: Intent under-served
|
|
370
|
+
if (intent && intent.strength > 0.7) {
|
|
371
|
+
const coverageScore = improvements.length / (mistakes.length + improvements.length);
|
|
372
|
+
if (coverageScore < 0.5) {
|
|
373
|
+
signals.push({
|
|
374
|
+
type: 'INTENT_UNDER_SERVED',
|
|
375
|
+
priority: 'MEDIUM',
|
|
376
|
+
reason: `Intent "${intent.goal}" is clear but solutions are scarce`,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
signals,
|
|
383
|
+
missingAreas,
|
|
384
|
+
nextAction: determineNextAction(signals),
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ============================================================================
|
|
389
|
+
// SYNTHESIS: Build Cognitive Lessons
|
|
390
|
+
// ============================================================================
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Format tag as title
|
|
394
|
+
*/
|
|
395
|
+
function formatTagAsTitle(tag) {
|
|
396
|
+
return tag
|
|
397
|
+
.split('-')
|
|
398
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
399
|
+
.join(' ');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Synthesize Cognitive Lesson Units from mistakes + improvements
|
|
404
|
+
* THIS IS THE MAIN FUNCTION
|
|
405
|
+
*/
|
|
406
|
+
export function synthesizeLessons() {
|
|
407
|
+
const mistakesDb = loadMistakes();
|
|
408
|
+
const improvementsDb = loadImprovements();
|
|
409
|
+
|
|
410
|
+
// Group by tag
|
|
411
|
+
const groups = new Map();
|
|
412
|
+
|
|
413
|
+
// Add mistakes to groups
|
|
414
|
+
mistakesDb.mistakes.forEach(m => {
|
|
415
|
+
const tags = m.tags || ['uncategorized'];
|
|
416
|
+
tags.forEach(tag => {
|
|
417
|
+
if (!groups.has(tag)) {
|
|
418
|
+
groups.set(tag, { mistakes: [], improvements: [] });
|
|
419
|
+
}
|
|
420
|
+
groups.get(tag).mistakes.push(m);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// Add improvements to groups
|
|
425
|
+
improvementsDb.improvements.forEach(i => {
|
|
426
|
+
const tags = i.tags || ['uncategorized'];
|
|
427
|
+
tags.forEach(tag => {
|
|
428
|
+
if (!groups.has(tag)) {
|
|
429
|
+
groups.set(tag, { mistakes: [], improvements: [] });
|
|
430
|
+
}
|
|
431
|
+
groups.get(tag).improvements.push(i);
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// Build Cognitive Lessons
|
|
436
|
+
const lessons = [];
|
|
437
|
+
let lessonId = 1;
|
|
438
|
+
|
|
439
|
+
groups.forEach((group, tag) => {
|
|
440
|
+
const intent = inferIntent([tag, ...group.mistakes.flatMap(m => m.tags || [])]);
|
|
441
|
+
const maturity = calculateMaturity(group.mistakes, group.improvements);
|
|
442
|
+
const evolution = analyzeEvolution(group.mistakes, group.improvements, intent);
|
|
443
|
+
|
|
444
|
+
lessons.push({
|
|
445
|
+
id: `LESSON-${String(lessonId++).padStart(3, '0')}`,
|
|
446
|
+
title: formatTagAsTitle(tag),
|
|
447
|
+
tag,
|
|
448
|
+
intent,
|
|
449
|
+
mistakes: group.mistakes,
|
|
450
|
+
improvements: group.improvements,
|
|
451
|
+
maturity,
|
|
452
|
+
evolution,
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// Sort by maturity (MATURE first, RAW last)
|
|
457
|
+
const stateOrder = { 'IDEAL': 0, 'MATURE': 1, 'LEARNING': 2, 'RAW': 3 };
|
|
458
|
+
lessons.sort((a, b) => {
|
|
459
|
+
const stateCompare = stateOrder[a.maturity.state] - stateOrder[b.maturity.state];
|
|
460
|
+
if (stateCompare !== 0) return stateCompare;
|
|
461
|
+
return b.maturity.confidence - a.maturity.confidence; // Higher confidence first
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
return lessons;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export default {
|
|
468
|
+
loadMistakes,
|
|
469
|
+
loadImprovements,
|
|
470
|
+
inferIntent,
|
|
471
|
+
buildContext,
|
|
472
|
+
matchPattern,
|
|
473
|
+
calculateMaturity,
|
|
474
|
+
analyzeEvolution,
|
|
475
|
+
synthesizeLessons,
|
|
476
|
+
};
|