codymaster 4.4.5 → 4.5.2
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 +33 -0
- package/README.md +29 -14
- package/commands/demo.md +1 -1
- package/dist/context-bus.js +70 -0
- package/dist/context-db.js +265 -0
- package/dist/continuity.js +12 -0
- package/dist/file-watcher.js +79 -0
- package/dist/index.js +152 -1
- package/dist/l0-indexer.js +158 -0
- package/dist/mcp-context-server.js +400 -0
- package/dist/migrate-json-to-sqlite.js +126 -0
- package/dist/skill-chain.js +19 -3
- package/dist/token-budget.js +108 -0
- package/dist/uri-resolver.js +203 -0
- package/package.json +5 -1
- package/scripts/gate-0-secrets.js +63 -0
- package/scripts/gate-1-syntax.js +53 -0
- package/scripts/gate-5-dist-verify.js +55 -0
- package/scripts/gate-6-smoke-test.js +30 -0
- package/scripts/index-codebase.sh +552 -0
- package/scripts/mcp-bridge.js +284 -0
- package/scripts/postinstall.js +301 -0
- package/scripts/security-fixer.js +143 -0
- package/scripts/security-scan.js +55 -0
- package/scripts/test-gemini.js +13 -0
- package/scripts/todo-bridge.js +112 -0
- package/skills/_shared/helpers.md +50 -14
- package/skills/cm-autopilot/SKILL.md +29 -0
- package/skills/cm-autopilot/scripts/autopilot.py +190 -0
- package/skills/cm-continuity/SKILL.md +90 -28
- package/skills/cm-skill-chain/SKILL.md +47 -1
- package/skills/cm-start/SKILL.md +11 -2
- package/skills/boxme-git-config/SKILL.md +0 -56
- package/skills/boxme-local-dev/SKILL.md +0 -66
- package/skills/jobs-to-be-done/SKILL.md +0 -266
- package/skills/jobs-to-be-done/references/case-studies.md +0 -154
- package/skills/jobs-to-be-done/references/competitive-strategy.md +0 -280
- package/skills/jobs-to-be-done/references/diagnostics.md +0 -158
- package/skills/jobs-to-be-done/references/innovation-process.md +0 -392
- package/skills/jobs-to-be-done/references/organizational-change.md +0 -328
- package/skills/marketplace-report-crawler/SKILL.md +0 -176
- package/skills/marketplace-report-crawler/config/accounts.json +0 -41
- package/skills/marketplace-report-crawler/config/report-types.json +0 -422
- package/skills/marketplace-report-crawler/config/sessions.json +0 -3
- package/skills/marketplace-report-crawler/scripts/ab-wrapper.sh +0 -102
- package/skills/marketplace-report-crawler/scripts/browser-actions/lazada/lazada-actions.js +0 -114
- package/skills/marketplace-report-crawler/scripts/browser-actions/shopee/shopee-actions.js +0 -94
- package/skills/marketplace-report-crawler/scripts/browser-actions/tiktok/tiktok-actions.js +0 -272
- package/skills/marketplace-report-crawler/scripts/crawl-runner.js +0 -281
- package/skills/marketplace-report-crawler/scripts/session-check.sh +0 -72
- package/skills/marketplace-report-crawler/scripts/session-manager.sh +0 -349
- package/skills/marketplace-report-crawler/scripts/setup-folders.sh +0 -83
- package/skills/medical-research/SKILL.md +0 -194
- package/skills/medical-research/scripts/evidence_checker.py +0 -288
- package/skills/mom-test/SKILL.md +0 -267
- package/skills/mom-test/references/avoiding-bad-data.md +0 -221
- package/skills/mom-test/references/case-studies.md +0 -306
- package/skills/mom-test/references/commitment-advancement.md +0 -219
- package/skills/mom-test/references/finding-conversations.md +0 -251
- package/skills/mom-test/references/processing-learning.md +0 -256
- package/skills/mom-test/references/question-patterns.md +0 -198
- package/skills/pandasai-analytics/SKILL.md +0 -251
- package/skills/release-it/SKILL.md +0 -235
- package/skills/release-it/references/anti-patterns.md +0 -279
- package/skills/release-it/references/capacity-planning.md +0 -285
- package/skills/release-it/references/chaos-engineering.md +0 -325
- package/skills/release-it/references/deployment-strategies.md +0 -331
- package/skills/release-it/references/observability.md +0 -301
- package/skills/release-it/references/stability-patterns.md +0 -355
- package/skills/skill-creator-ultra/.agents/workflows/skill-audit.md +0 -37
- package/skills/skill-creator-ultra/.agents/workflows/skill-compare.md +0 -34
- package/skills/skill-creator-ultra/.agents/workflows/skill-export.md +0 -51
- package/skills/skill-creator-ultra/.agents/workflows/skill-generate.md +0 -39
- package/skills/skill-creator-ultra/.agents/workflows/skill-scaffold.md +0 -52
- package/skills/skill-creator-ultra/.agents/workflows/skill-simulate.md +0 -25
- package/skills/skill-creator-ultra/.agents/workflows/skill-stats.md +0 -31
- package/skills/skill-creator-ultra/.agents/workflows/skill-validate.md +0 -25
- package/skills/skill-creator-ultra/README.md +0 -1242
- package/skills/skill-creator-ultra/SKILL.md +0 -388
- package/skills/skill-creator-ultra/agents/analyzer.md +0 -274
- package/skills/skill-creator-ultra/agents/comparator.md +0 -202
- package/skills/skill-creator-ultra/agents/grader.md +0 -223
- package/skills/skill-creator-ultra/assets/eval_review.html +0 -146
- package/skills/skill-creator-ultra/eval-viewer/generate_review.py +0 -471
- package/skills/skill-creator-ultra/eval-viewer/viewer.html +0 -1325
- package/skills/skill-creator-ultra/examples/example_anthropic_frontend.md +0 -109
- package/skills/skill-creator-ultra/examples/example_anthropic_pdf.md +0 -116
- package/skills/skill-creator-ultra/examples/example_api_docs.md +0 -189
- package/skills/skill-creator-ultra/examples/example_db_migration.md +0 -253
- package/skills/skill-creator-ultra/examples/example_git_commit.md +0 -111
- package/skills/skill-creator-ultra/install.ps1 +0 -289
- package/skills/skill-creator-ultra/install.sh +0 -313
- package/skills/skill-creator-ultra/phases/phase1_interview.md +0 -202
- package/skills/skill-creator-ultra/phases/phase2_extract.md +0 -55
- package/skills/skill-creator-ultra/phases/phase3_detect.md +0 -57
- package/skills/skill-creator-ultra/phases/phase4_generate.md +0 -543
- package/skills/skill-creator-ultra/phases/phase5_test.md +0 -319
- package/skills/skill-creator-ultra/phases/phase6_eval.md +0 -301
- package/skills/skill-creator-ultra/phases/phase7_iterate.md +0 -103
- package/skills/skill-creator-ultra/phases/phase8_optimize.md +0 -113
- package/skills/skill-creator-ultra/resources/advanced_patterns.md +0 -499
- package/skills/skill-creator-ultra/resources/anti_patterns.md +0 -376
- package/skills/skill-creator-ultra/resources/blueprints.md +0 -498
- package/skills/skill-creator-ultra/resources/checklist.md +0 -243
- package/skills/skill-creator-ultra/resources/composition_cookbook.md +0 -291
- package/skills/skill-creator-ultra/resources/description_optimization.md +0 -90
- package/skills/skill-creator-ultra/resources/eval_guide.md +0 -133
- package/skills/skill-creator-ultra/resources/industry_questions.md +0 -189
- package/skills/skill-creator-ultra/resources/interview_questions.md +0 -200
- package/skills/skill-creator-ultra/resources/pattern_detection.md +0 -200
- package/skills/skill-creator-ultra/resources/prompt_engineering.md +0 -531
- package/skills/skill-creator-ultra/resources/schemas.md +0 -430
- package/skills/skill-creator-ultra/resources/script_integration.md +0 -593
- package/skills/skill-creator-ultra/resources/scripts_guide.md +0 -339
- package/skills/skill-creator-ultra/resources/skill_template.md +0 -124
- package/skills/skill-creator-ultra/resources/skill_writing_guide.md +0 -634
- package/skills/skill-creator-ultra/resources/versioning_guide.md +0 -193
- package/skills/skill-creator-ultra/scripts/ci_eval.py +0 -200
- package/skills/skill-creator-ultra/scripts/package_skill.py +0 -165
- package/skills/skill-creator-ultra/scripts/simulate_skill.py +0 -398
- package/skills/skill-creator-ultra/scripts/skill_audit.py +0 -611
- package/skills/skill-creator-ultra/scripts/skill_compare.py +0 -265
- package/skills/skill-creator-ultra/scripts/skill_export.py +0 -334
- package/skills/skill-creator-ultra/scripts/skill_scaffold.py +0 -403
- package/skills/skill-creator-ultra/scripts/skill_stats.py +0 -339
- package/skills/skill-creator-ultra/scripts/validate_skill.py +0 -411
- package/skills/tailwind-mastery/SKILL.md +0 -229
- package/skills/vercel-react-best-practices/AGENTS.md +0 -3373
- package/skills/vercel-react-best-practices/README.md +0 -123
- package/skills/vercel-react-best-practices/SKILL.md +0 -143
- package/skills/vercel-react-best-practices/rules/_sections.md +0 -46
- package/skills/vercel-react-best-practices/rules/_template.md +0 -28
- package/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -55
- package/skills/vercel-react-best-practices/rules/advanced-init-once.md +0 -42
- package/skills/vercel-react-best-practices/rules/advanced-use-latest.md +0 -39
- package/skills/vercel-react-best-practices/rules/async-api-routes.md +0 -38
- package/skills/vercel-react-best-practices/rules/async-defer-await.md +0 -80
- package/skills/vercel-react-best-practices/rules/async-dependencies.md +0 -51
- package/skills/vercel-react-best-practices/rules/async-parallel.md +0 -28
- package/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -99
- package/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -59
- package/skills/vercel-react-best-practices/rules/bundle-conditional.md +0 -31
- package/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -49
- package/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -35
- package/skills/vercel-react-best-practices/rules/bundle-preload.md +0 -50
- package/skills/vercel-react-best-practices/rules/client-event-listeners.md +0 -74
- package/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -71
- package/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -48
- package/skills/vercel-react-best-practices/rules/client-swr-dedup.md +0 -56
- package/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -107
- package/skills/vercel-react-best-practices/rules/js-cache-function-results.md +0 -80
- package/skills/vercel-react-best-practices/rules/js-cache-property-access.md +0 -28
- package/skills/vercel-react-best-practices/rules/js-cache-storage.md +0 -70
- package/skills/vercel-react-best-practices/rules/js-combine-iterations.md +0 -32
- package/skills/vercel-react-best-practices/rules/js-early-exit.md +0 -50
- package/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +0 -60
- package/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -45
- package/skills/vercel-react-best-practices/rules/js-index-maps.md +0 -37
- package/skills/vercel-react-best-practices/rules/js-length-check-first.md +0 -49
- package/skills/vercel-react-best-practices/rules/js-min-max-loop.md +0 -82
- package/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -24
- package/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -57
- package/skills/vercel-react-best-practices/rules/rendering-activity.md +0 -26
- package/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
- package/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -40
- package/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -38
- package/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -46
- package/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
- package/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +0 -30
- package/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +0 -85
- package/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +0 -68
- package/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -28
- package/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +0 -75
- package/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -39
- package/skills/vercel-react-best-practices/rules/rerender-dependencies.md +0 -45
- package/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +0 -40
- package/skills/vercel-react-best-practices/rules/rerender-derived-state.md +0 -29
- package/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -74
- package/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -58
- package/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +0 -38
- package/skills/vercel-react-best-practices/rules/rerender-memo.md +0 -44
- package/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +0 -45
- package/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +0 -82
- package/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +0 -35
- package/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +0 -64
- package/skills/vercel-react-best-practices/rules/rerender-transitions.md +0 -40
- package/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md +0 -59
- package/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +0 -73
- package/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -73
- package/skills/vercel-react-best-practices/rules/server-auth-actions.md +0 -96
- package/skills/vercel-react-best-practices/rules/server-cache-lru.md +0 -41
- package/skills/vercel-react-best-practices/rules/server-cache-react.md +0 -76
- package/skills/vercel-react-best-practices/rules/server-dedup-props.md +0 -65
- package/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +0 -142
- package/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -83
- package/skills/vercel-react-best-practices/rules/server-serialization.md +0 -38
- package/skills/web-design-guidelines/SKILL.md +0 -39
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
skill_compare.py — So sánh 2 phiên bản Skill.md
|
|
4
|
-
|
|
5
|
-
Sử dụng:
|
|
6
|
-
python skill_compare.py /path/to/old/SKILL.md /path/to/new/SKILL.md
|
|
7
|
-
python skill_compare.py /path/to/old-skill/ /path/to/new-skill/
|
|
8
|
-
python skill_compare.py /path/to/old/ /path/to/new/ --json
|
|
9
|
-
|
|
10
|
-
Hiển thị:
|
|
11
|
-
- Diff tổng quan (dòng thêm/xóa/thay đổi)
|
|
12
|
-
- So sánh sections (thêm/xóa/thay đổi kích thước)
|
|
13
|
-
- So sánh metrics (steps, examples, constraints)
|
|
14
|
-
- Đánh giá: cải thiện hay tệ hơn?
|
|
15
|
-
|
|
16
|
-
Developed by Thân Công Hải — Skill Generator v3.2 Expert
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
import sys
|
|
20
|
-
import os
|
|
21
|
-
import re
|
|
22
|
-
import json
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class Colors:
|
|
26
|
-
PASS = '\033[92m'
|
|
27
|
-
FAIL = '\033[91m'
|
|
28
|
-
WARN = '\033[93m'
|
|
29
|
-
INFO = '\033[96m'
|
|
30
|
-
BOLD = '\033[1m'
|
|
31
|
-
DIM = '\033[2m'
|
|
32
|
-
RESET = '\033[0m'
|
|
33
|
-
|
|
34
|
-
@staticmethod
|
|
35
|
-
def enable_windows():
|
|
36
|
-
if os.name == 'nt':
|
|
37
|
-
try:
|
|
38
|
-
import ctypes
|
|
39
|
-
kernel32 = ctypes.windll.kernel32
|
|
40
|
-
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
|
|
41
|
-
except Exception:
|
|
42
|
-
pass
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Colors.enable_windows()
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def parse_frontmatter(content):
|
|
49
|
-
match = re.match(r'^---\s*\n(.*?)\n---\s*\n', content, re.DOTALL)
|
|
50
|
-
if not match:
|
|
51
|
-
return {}, content
|
|
52
|
-
fm_text = match.group(1)
|
|
53
|
-
body = content[match.end():]
|
|
54
|
-
frontmatter = {}
|
|
55
|
-
current_key = None
|
|
56
|
-
current_value = []
|
|
57
|
-
for line in fm_text.split('\n'):
|
|
58
|
-
key_match = re.match(r'^(\w+):\s*(.*)', line)
|
|
59
|
-
if key_match:
|
|
60
|
-
if current_key:
|
|
61
|
-
frontmatter[current_key] = '\n'.join(current_value).strip()
|
|
62
|
-
current_key = key_match.group(1)
|
|
63
|
-
current_value = [key_match.group(2)] if key_match.group(2) and key_match.group(2) != '|' else []
|
|
64
|
-
elif current_key and line.startswith(' '):
|
|
65
|
-
current_value.append(line.strip())
|
|
66
|
-
if current_key:
|
|
67
|
-
frontmatter[current_key] = '\n'.join(current_value).strip()
|
|
68
|
-
return frontmatter, body
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def get_metrics(content, body):
|
|
72
|
-
"""Trích xuất metrics từ skill."""
|
|
73
|
-
return {
|
|
74
|
-
'lines': content.count('\n') + 1,
|
|
75
|
-
'words': len(content.split()),
|
|
76
|
-
'chars': len(content),
|
|
77
|
-
'h1_sections': len(re.findall(r'^#\s+', body, re.MULTILINE)),
|
|
78
|
-
'h2_sections': len(re.findall(r'^##\s+', body, re.MULTILINE)),
|
|
79
|
-
'steps': len(re.findall(r'^\d+\.', body, re.MULTILINE)),
|
|
80
|
-
'examples': len(re.findall(r'^##\s+.*[Vv]í dụ|^##\s+.*[Ee]xample|^###\s+.*[Vv]í dụ', body, re.MULTILINE)),
|
|
81
|
-
'constraints': len(re.findall(r'(KHÔNG ĐƯỢC|LUÔN LUÔN|🚫|✅)', body)),
|
|
82
|
-
'code_blocks': len(re.findall(r'```', body)) // 2,
|
|
83
|
-
'tables': len(re.findall(r'^\|.*\|$', body, re.MULTILINE)),
|
|
84
|
-
'error_handling': len(re.findall(r'(nếu lỗi|fallback|retry|edge case|VERIFY)', body, re.IGNORECASE)),
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def get_sections(body):
|
|
89
|
-
"""Lấy danh sách sections và kích thước."""
|
|
90
|
-
sections = {}
|
|
91
|
-
current = None
|
|
92
|
-
lines = 0
|
|
93
|
-
for line in body.split('\n'):
|
|
94
|
-
h1 = re.match(r'^#\s+(.+)', line)
|
|
95
|
-
if h1:
|
|
96
|
-
if current:
|
|
97
|
-
sections[current] = lines
|
|
98
|
-
current = h1.group(1).strip()
|
|
99
|
-
lines = 0
|
|
100
|
-
else:
|
|
101
|
-
lines += 1
|
|
102
|
-
if current:
|
|
103
|
-
sections[current] = lines
|
|
104
|
-
return sections
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def format_delta(old_val, new_val, higher_is_better=True):
|
|
108
|
-
"""Format thay đổi với màu sắc."""
|
|
109
|
-
delta = new_val - old_val
|
|
110
|
-
if delta == 0:
|
|
111
|
-
return f"{Colors.DIM}= (không đổi){Colors.RESET}"
|
|
112
|
-
|
|
113
|
-
if higher_is_better:
|
|
114
|
-
color = Colors.PASS if delta > 0 else Colors.FAIL
|
|
115
|
-
else:
|
|
116
|
-
color = Colors.FAIL if delta > 0 else Colors.PASS
|
|
117
|
-
|
|
118
|
-
sign = '+' if delta > 0 else ''
|
|
119
|
-
pct = round(delta / old_val * 100) if old_val > 0 else 0
|
|
120
|
-
return f"{color}{sign}{delta} ({sign}{pct}%){Colors.RESET}"
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def compare_skills(path_old, path_new, json_output=False):
|
|
124
|
-
"""So sánh 2 skill."""
|
|
125
|
-
|
|
126
|
-
# Resolve paths
|
|
127
|
-
skill_old = os.path.join(path_old, 'SKILL.md') if os.path.isdir(path_old) else path_old
|
|
128
|
-
skill_new = os.path.join(path_new, 'SKILL.md') if os.path.isdir(path_new) else path_new
|
|
129
|
-
|
|
130
|
-
for path, label in [(skill_old, 'OLD'), (skill_new, 'NEW')]:
|
|
131
|
-
if not os.path.exists(path):
|
|
132
|
-
print(f"{Colors.FAIL}❌ Không tìm thấy {label}: {path}{Colors.RESET}")
|
|
133
|
-
return False
|
|
134
|
-
|
|
135
|
-
with open(skill_old, 'r', encoding='utf-8') as f:
|
|
136
|
-
content_old = f.read()
|
|
137
|
-
with open(skill_new, 'r', encoding='utf-8') as f:
|
|
138
|
-
content_new = f.read()
|
|
139
|
-
|
|
140
|
-
fm_old, body_old = parse_frontmatter(content_old)
|
|
141
|
-
fm_new, body_new = parse_frontmatter(content_new)
|
|
142
|
-
|
|
143
|
-
metrics_old = get_metrics(content_old, body_old)
|
|
144
|
-
metrics_new = get_metrics(content_new, body_new)
|
|
145
|
-
|
|
146
|
-
sections_old = get_sections(body_old)
|
|
147
|
-
sections_new = get_sections(body_new)
|
|
148
|
-
|
|
149
|
-
name_old = fm_old.get('name', 'unknown')
|
|
150
|
-
name_new = fm_new.get('name', 'unknown')
|
|
151
|
-
|
|
152
|
-
# JSON output
|
|
153
|
-
if json_output:
|
|
154
|
-
output = {
|
|
155
|
-
'old': {'name': name_old, 'file': skill_old, 'metrics': metrics_old},
|
|
156
|
-
'new': {'name': name_new, 'file': skill_new, 'metrics': metrics_new},
|
|
157
|
-
'deltas': {k: metrics_new[k] - metrics_old[k] for k in metrics_old},
|
|
158
|
-
'sections': {
|
|
159
|
-
'added': [s for s in sections_new if s not in sections_old],
|
|
160
|
-
'removed': [s for s in sections_old if s not in sections_new],
|
|
161
|
-
'changed': {s: {'old': sections_old.get(s, 0), 'new': sections_new.get(s, 0)}
|
|
162
|
-
for s in set(sections_old) & set(sections_new)
|
|
163
|
-
if sections_old.get(s, 0) != sections_new.get(s, 0)},
|
|
164
|
-
},
|
|
165
|
-
}
|
|
166
|
-
print(json.dumps(output, ensure_ascii=False, indent=2))
|
|
167
|
-
return True
|
|
168
|
-
|
|
169
|
-
# Pretty output
|
|
170
|
-
print()
|
|
171
|
-
print(f"{Colors.BOLD}{'=' * 60}{Colors.RESET}")
|
|
172
|
-
print(f"{Colors.BOLD} 🔄 SKILL COMPARE — So sánh 2 phiên bản{Colors.RESET}")
|
|
173
|
-
print(f"{Colors.BOLD}{'=' * 60}{Colors.RESET}")
|
|
174
|
-
print(f" {Colors.DIM}OLD: {skill_old}{Colors.RESET}")
|
|
175
|
-
print(f" {Colors.INFO}NEW: {skill_new}{Colors.RESET}")
|
|
176
|
-
print()
|
|
177
|
-
|
|
178
|
-
# Metrics comparison
|
|
179
|
-
metric_labels = {
|
|
180
|
-
'lines': ('📏 Dòng', True),
|
|
181
|
-
'words': ('📝 Từ', True),
|
|
182
|
-
'steps': ('📋 Steps', True),
|
|
183
|
-
'examples': ('🎯 Examples', True),
|
|
184
|
-
'constraints': ('⚠️ Constraints', True),
|
|
185
|
-
'code_blocks': ('💻 Code blocks', True),
|
|
186
|
-
'error_handling': ('🛡️ Error handling', True),
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
print(f" {Colors.BOLD}📊 So sánh metrics:{Colors.RESET}")
|
|
190
|
-
print(f" {'Metric':<22} {'OLD':>8} {'NEW':>8} {'Thay đổi'}")
|
|
191
|
-
print(f" {'─' * 55}")
|
|
192
|
-
|
|
193
|
-
improvements = 0
|
|
194
|
-
regressions = 0
|
|
195
|
-
|
|
196
|
-
for key, (label, higher_better) in metric_labels.items():
|
|
197
|
-
old_v = metrics_old[key]
|
|
198
|
-
new_v = metrics_new[key]
|
|
199
|
-
delta_str = format_delta(old_v, new_v, higher_better)
|
|
200
|
-
print(f" {label:<22} {old_v:>8} {new_v:>8} {delta_str}")
|
|
201
|
-
|
|
202
|
-
delta = new_v - old_v
|
|
203
|
-
if (higher_better and delta > 0) or (not higher_better and delta < 0):
|
|
204
|
-
improvements += 1
|
|
205
|
-
elif delta != 0:
|
|
206
|
-
regressions += 1
|
|
207
|
-
|
|
208
|
-
print()
|
|
209
|
-
|
|
210
|
-
# Sections comparison
|
|
211
|
-
added = [s for s in sections_new if s not in sections_old]
|
|
212
|
-
removed = [s for s in sections_old if s not in sections_new]
|
|
213
|
-
|
|
214
|
-
if added or removed:
|
|
215
|
-
print(f" {Colors.BOLD}📋 Sections thay đổi:{Colors.RESET}")
|
|
216
|
-
for s in added:
|
|
217
|
-
print(f" {Colors.PASS}+ {s} ({sections_new[s]} dòng){Colors.RESET}")
|
|
218
|
-
for s in removed:
|
|
219
|
-
print(f" {Colors.FAIL}- {s} ({sections_old[s]} dòng){Colors.RESET}")
|
|
220
|
-
print()
|
|
221
|
-
|
|
222
|
-
# Changed sections
|
|
223
|
-
changed = [(s, sections_old[s], sections_new[s])
|
|
224
|
-
for s in set(sections_old) & set(sections_new)
|
|
225
|
-
if sections_old[s] != sections_new[s]]
|
|
226
|
-
|
|
227
|
-
if changed:
|
|
228
|
-
print(f" {Colors.BOLD}📐 Sections thay đổi kích thước:{Colors.RESET}")
|
|
229
|
-
for name, old_lines, new_lines in sorted(changed, key=lambda x: x[2] - x[1], reverse=True):
|
|
230
|
-
delta = new_lines - old_lines
|
|
231
|
-
color = Colors.PASS if delta > 0 else Colors.FAIL
|
|
232
|
-
sign = '+' if delta > 0 else ''
|
|
233
|
-
print(f" {name[:30]:<30} {old_lines:>5} → {new_lines:>5} ({color}{sign}{delta}{Colors.RESET})")
|
|
234
|
-
print()
|
|
235
|
-
|
|
236
|
-
# Overall verdict
|
|
237
|
-
print(f"{Colors.BOLD}{'=' * 60}{Colors.RESET}")
|
|
238
|
-
if improvements > regressions:
|
|
239
|
-
print(f" {Colors.PASS}🎉 VERDICT: Phiên bản mới TỐT HƠN ({improvements} cải thiện, {regressions} giảm){Colors.RESET}")
|
|
240
|
-
elif regressions > improvements:
|
|
241
|
-
print(f" {Colors.FAIL}⚠️ VERDICT: Phiên bản mới CÓ VẤN ĐỀ ({regressions} giảm, {improvements} cải thiện){Colors.RESET}")
|
|
242
|
-
else:
|
|
243
|
-
print(f" {Colors.INFO}ℹ️ VERDICT: Hai phiên bản TƯƠNG ĐƯƠNG{Colors.RESET}")
|
|
244
|
-
print(f"{Colors.BOLD}{'=' * 60}{Colors.RESET}")
|
|
245
|
-
print()
|
|
246
|
-
|
|
247
|
-
return True
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if __name__ == '__main__':
|
|
251
|
-
if len(sys.argv) < 3:
|
|
252
|
-
print("Sử dụng: python skill_compare.py <old-path> <new-path>")
|
|
253
|
-
print("Options:")
|
|
254
|
-
print(" --json Output dạng JSON")
|
|
255
|
-
print()
|
|
256
|
-
print("Ví dụ:")
|
|
257
|
-
print(" python skill_compare.py ./my-skill-v1/ ./my-skill-v2/")
|
|
258
|
-
print(" python skill_compare.py old-SKILL.md new-SKILL.md")
|
|
259
|
-
sys.exit(1)
|
|
260
|
-
|
|
261
|
-
target_old = sys.argv[1]
|
|
262
|
-
target_new = sys.argv[2]
|
|
263
|
-
json_mode = '--json' in sys.argv
|
|
264
|
-
|
|
265
|
-
compare_skills(target_old, target_new, json_output=json_mode)
|
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
skill_export.py — Chuyển đổi SKILL.md sang format các nền tảng khác
|
|
4
|
-
|
|
5
|
-
Sử dụng:
|
|
6
|
-
python skill_export.py /path/to/SKILL.md --platform cursor
|
|
7
|
-
python skill_export.py /path/to/skill-folder/ --platform claude
|
|
8
|
-
python skill_export.py /path/to/skill-folder/ --platform all
|
|
9
|
-
python skill_export.py /path/to/skill-folder/ --platform all --output ./exports/
|
|
10
|
-
|
|
11
|
-
Nền tảng hỗ trợ:
|
|
12
|
-
cursor → .cursor/rules/<name>.mdc
|
|
13
|
-
claude → CLAUDE.md (Custom Commands)
|
|
14
|
-
windsurf → .windsurf/rules/<name>.md
|
|
15
|
-
cline → .clinerules (Custom Instructions)
|
|
16
|
-
copilot → .github/copilot-instructions.md
|
|
17
|
-
openclaw → System Prompt (text file)
|
|
18
|
-
all → Export tất cả
|
|
19
|
-
|
|
20
|
-
Developed by Thân Công Hải — Skill Generator v3.2 Expert
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
import sys
|
|
24
|
-
import os
|
|
25
|
-
import re
|
|
26
|
-
from pathlib import Path
|
|
27
|
-
from datetime import datetime
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# ============================================================
|
|
31
|
-
# COLOR OUTPUT
|
|
32
|
-
# ============================================================
|
|
33
|
-
|
|
34
|
-
class Colors:
|
|
35
|
-
PASS = '\033[92m'
|
|
36
|
-
FAIL = '\033[91m'
|
|
37
|
-
WARN = '\033[93m'
|
|
38
|
-
INFO = '\033[96m'
|
|
39
|
-
BOLD = '\033[1m'
|
|
40
|
-
DIM = '\033[2m'
|
|
41
|
-
RESET = '\033[0m'
|
|
42
|
-
|
|
43
|
-
@staticmethod
|
|
44
|
-
def enable_windows():
|
|
45
|
-
if os.name == 'nt':
|
|
46
|
-
try:
|
|
47
|
-
import ctypes
|
|
48
|
-
kernel32 = ctypes.windll.kernel32
|
|
49
|
-
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
|
|
50
|
-
except Exception:
|
|
51
|
-
pass
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
Colors.enable_windows()
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# ============================================================
|
|
58
|
-
# PARSERS
|
|
59
|
-
# ============================================================
|
|
60
|
-
|
|
61
|
-
def parse_frontmatter(content):
|
|
62
|
-
"""Trích xuất YAML frontmatter."""
|
|
63
|
-
match = re.match(r'^---\s*\n(.*?)\n---\s*\n', content, re.DOTALL)
|
|
64
|
-
if not match:
|
|
65
|
-
return {}, content
|
|
66
|
-
|
|
67
|
-
fm_text = match.group(1)
|
|
68
|
-
body = content[match.end():]
|
|
69
|
-
frontmatter = {}
|
|
70
|
-
current_key = None
|
|
71
|
-
current_value = []
|
|
72
|
-
|
|
73
|
-
for line in fm_text.split('\n'):
|
|
74
|
-
key_match = re.match(r'^(\w+):\s*(.*)', line)
|
|
75
|
-
if key_match:
|
|
76
|
-
if current_key:
|
|
77
|
-
frontmatter[current_key] = '\n'.join(current_value).strip()
|
|
78
|
-
current_key = key_match.group(1)
|
|
79
|
-
current_value = [key_match.group(2)] if key_match.group(2) and key_match.group(2) != '|' else []
|
|
80
|
-
elif current_key and line.startswith(' '):
|
|
81
|
-
current_value.append(line.strip())
|
|
82
|
-
|
|
83
|
-
if current_key:
|
|
84
|
-
frontmatter[current_key] = '\n'.join(current_value).strip()
|
|
85
|
-
|
|
86
|
-
return frontmatter, body
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def extract_trigger_phrases(description):
|
|
90
|
-
"""Trích xuất trigger phrases từ description."""
|
|
91
|
-
triggers = re.findall(r'"([^"]+)"', description)
|
|
92
|
-
return triggers
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
# ============================================================
|
|
96
|
-
# EXPORTERS
|
|
97
|
-
# ============================================================
|
|
98
|
-
|
|
99
|
-
def export_cursor(frontmatter, body, name):
|
|
100
|
-
"""Export sang Cursor .mdc rule format."""
|
|
101
|
-
desc = frontmatter.get('description', '')
|
|
102
|
-
triggers = extract_trigger_phrases(desc)
|
|
103
|
-
trigger_str = ', '.join([f'"{t}"' for t in triggers]) if triggers else f'"{name}"'
|
|
104
|
-
|
|
105
|
-
return f"""---
|
|
106
|
-
description: {desc.split(chr(10))[0] if desc else name}
|
|
107
|
-
globs:
|
|
108
|
-
alwaysApply: false
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
# {name}
|
|
112
|
-
|
|
113
|
-
> Trigger: {trigger_str}
|
|
114
|
-
> Converted from SKILL.md by Skill Generator v3.2
|
|
115
|
-
|
|
116
|
-
{body}
|
|
117
|
-
|
|
118
|
-
<!-- Generated by Skill Generator v3.2 — Export: Cursor -->
|
|
119
|
-
"""
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def export_claude(frontmatter, body, name):
|
|
123
|
-
"""Export sang Claude Code CLAUDE.md format."""
|
|
124
|
-
desc = frontmatter.get('description', '')
|
|
125
|
-
|
|
126
|
-
return f"""# {name}
|
|
127
|
-
|
|
128
|
-
> {desc}
|
|
129
|
-
> Converted from SKILL.md by Skill Generator v3.2
|
|
130
|
-
|
|
131
|
-
{body}
|
|
132
|
-
|
|
133
|
-
<!-- Generated by Skill Generator v3.2 — Export: Claude Code -->
|
|
134
|
-
"""
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
def export_windsurf(frontmatter, body, name):
|
|
138
|
-
"""Export sang Windsurf Cascade Rule format."""
|
|
139
|
-
desc = frontmatter.get('description', '')
|
|
140
|
-
|
|
141
|
-
return f"""---
|
|
142
|
-
trigger: manual
|
|
143
|
-
description: {desc.split(chr(10))[0] if desc else name}
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
# {name}
|
|
147
|
-
|
|
148
|
-
> {desc}
|
|
149
|
-
> Converted from SKILL.md by Skill Generator v3.2
|
|
150
|
-
|
|
151
|
-
{body}
|
|
152
|
-
|
|
153
|
-
<!-- Generated by Skill Generator v3.2 — Export: Windsurf -->
|
|
154
|
-
"""
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def export_cline(frontmatter, body, name):
|
|
158
|
-
"""Export sang Cline Custom Instructions format."""
|
|
159
|
-
desc = frontmatter.get('description', '')
|
|
160
|
-
|
|
161
|
-
return f"""# Custom Instruction: {name}
|
|
162
|
-
|
|
163
|
-
## Mô tả
|
|
164
|
-
{desc}
|
|
165
|
-
|
|
166
|
-
## Hướng dẫn
|
|
167
|
-
Khi user yêu cầu liên quan đến skill này, hãy tuân theo quy trình dưới đây:
|
|
168
|
-
|
|
169
|
-
{body}
|
|
170
|
-
|
|
171
|
-
<!-- Generated by Skill Generator v3.2 — Export: Cline -->
|
|
172
|
-
"""
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def export_copilot(frontmatter, body, name):
|
|
176
|
-
"""Export sang GitHub Copilot instructions format."""
|
|
177
|
-
desc = frontmatter.get('description', '')
|
|
178
|
-
|
|
179
|
-
return f"""## {name}
|
|
180
|
-
|
|
181
|
-
{desc}
|
|
182
|
-
|
|
183
|
-
### Quy trình thực hiện
|
|
184
|
-
|
|
185
|
-
{body}
|
|
186
|
-
|
|
187
|
-
<!-- Generated by Skill Generator v3.2 — Export: GitHub Copilot -->
|
|
188
|
-
"""
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
def export_openclaw(frontmatter, body, name):
|
|
192
|
-
"""Export sang OpenClaw System Prompt format."""
|
|
193
|
-
desc = frontmatter.get('description', '')
|
|
194
|
-
|
|
195
|
-
return f"""[SYSTEM PROMPT — {name}]
|
|
196
|
-
|
|
197
|
-
Mô tả: {desc}
|
|
198
|
-
|
|
199
|
-
---
|
|
200
|
-
|
|
201
|
-
{body}
|
|
202
|
-
|
|
203
|
-
---
|
|
204
|
-
[END SYSTEM PROMPT]
|
|
205
|
-
Generated by Skill Generator v3.2 — Export: OpenClaw
|
|
206
|
-
"""
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
EXPORTERS = {
|
|
210
|
-
'cursor': {'fn': export_cursor, 'ext': '.mdc', 'dir': '.cursor/rules', 'label': 'Cursor Rule'},
|
|
211
|
-
'claude': {'fn': export_claude, 'ext': '.md', 'dir': '.claude/commands', 'label': 'Claude Code Command'},
|
|
212
|
-
'windsurf': {'fn': export_windsurf, 'ext': '.md', 'dir': '.windsurf/rules', 'label': 'Windsurf Rule'},
|
|
213
|
-
'cline': {'fn': export_cline, 'ext': '.md', 'dir': '.clinerules', 'label': 'Cline Instructions'},
|
|
214
|
-
'copilot': {'fn': export_copilot, 'ext': '.md', 'dir': '.github', 'label': 'Copilot Instructions'},
|
|
215
|
-
'openclaw': {'fn': export_openclaw, 'ext': '.txt', 'dir': 'openclaw', 'label': 'OpenClaw System Prompt'},
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
# ============================================================
|
|
220
|
-
# MAIN
|
|
221
|
-
# ============================================================
|
|
222
|
-
|
|
223
|
-
def export_skill(target_path, platform, output_dir=None):
|
|
224
|
-
"""Chạy export cho skill."""
|
|
225
|
-
|
|
226
|
-
# Tìm SKILL.md
|
|
227
|
-
if os.path.isdir(target_path):
|
|
228
|
-
skill_md = os.path.join(target_path, 'SKILL.md')
|
|
229
|
-
else:
|
|
230
|
-
skill_md = target_path
|
|
231
|
-
|
|
232
|
-
if not os.path.exists(skill_md):
|
|
233
|
-
print(f"{Colors.FAIL}❌ Không tìm thấy SKILL.md tại: {skill_md}{Colors.RESET}")
|
|
234
|
-
return False
|
|
235
|
-
|
|
236
|
-
with open(skill_md, 'r', encoding='utf-8') as f:
|
|
237
|
-
content = f.read()
|
|
238
|
-
|
|
239
|
-
frontmatter, body = parse_frontmatter(content)
|
|
240
|
-
name = frontmatter.get('name', 'unnamed-skill')
|
|
241
|
-
|
|
242
|
-
# Determine platforms
|
|
243
|
-
platforms = list(EXPORTERS.keys()) if platform == 'all' else [platform]
|
|
244
|
-
|
|
245
|
-
if output_dir is None:
|
|
246
|
-
output_dir = os.path.join(os.path.dirname(skill_md), 'exports')
|
|
247
|
-
|
|
248
|
-
os.makedirs(output_dir, exist_ok=True)
|
|
249
|
-
|
|
250
|
-
print()
|
|
251
|
-
print(f"{Colors.BOLD}{'=' * 55}{Colors.RESET}")
|
|
252
|
-
print(f"{Colors.BOLD} 📦 SKILL EXPORT — Chuyển đổi đa nền tảng{Colors.RESET}")
|
|
253
|
-
print(f"{Colors.BOLD} Skill: {Colors.INFO}{name}{Colors.RESET}")
|
|
254
|
-
print(f"{Colors.BOLD}{'=' * 55}{Colors.RESET}")
|
|
255
|
-
print()
|
|
256
|
-
|
|
257
|
-
exported = 0
|
|
258
|
-
|
|
259
|
-
for p in platforms:
|
|
260
|
-
if p not in EXPORTERS:
|
|
261
|
-
print(f" {Colors.FAIL}❌ Nền tảng '{p}' không hỗ trợ{Colors.RESET}")
|
|
262
|
-
continue
|
|
263
|
-
|
|
264
|
-
exporter = EXPORTERS[p]
|
|
265
|
-
export_content = exporter['fn'](frontmatter, body, name)
|
|
266
|
-
|
|
267
|
-
# Create platform subdirectory
|
|
268
|
-
platform_dir = os.path.join(output_dir, p)
|
|
269
|
-
os.makedirs(platform_dir, exist_ok=True)
|
|
270
|
-
|
|
271
|
-
# Write file
|
|
272
|
-
filename = f"{name}{exporter['ext']}"
|
|
273
|
-
filepath = os.path.join(platform_dir, filename)
|
|
274
|
-
|
|
275
|
-
with open(filepath, 'w', encoding='utf-8') as f:
|
|
276
|
-
f.write(export_content)
|
|
277
|
-
|
|
278
|
-
lines = len(export_content.split('\n'))
|
|
279
|
-
print(f" {Colors.PASS}✅ {exporter['label']}{Colors.RESET}")
|
|
280
|
-
print(f" 📄 {filepath}")
|
|
281
|
-
print(f" 📏 {lines} dòng")
|
|
282
|
-
print()
|
|
283
|
-
exported += 1
|
|
284
|
-
|
|
285
|
-
# Summary
|
|
286
|
-
print(f"{Colors.BOLD}{'=' * 55}{Colors.RESET}")
|
|
287
|
-
print(f" {Colors.PASS}📊 Đã export {exported}/{len(platforms)} nền tảng{Colors.RESET}")
|
|
288
|
-
print(f" {Colors.DIM}📁 Output: {output_dir}{Colors.RESET}")
|
|
289
|
-
print(f"{Colors.BOLD}{'=' * 55}{Colors.RESET}")
|
|
290
|
-
print()
|
|
291
|
-
|
|
292
|
-
# Usage hint
|
|
293
|
-
print(f" {Colors.INFO}💡 Cách dùng file export:{Colors.RESET}")
|
|
294
|
-
for p in platforms:
|
|
295
|
-
if p in EXPORTERS:
|
|
296
|
-
ex = EXPORTERS[p]
|
|
297
|
-
print(f" {p}: Copy {ex['dir']}/{name}{ex['ext']} vào dự án")
|
|
298
|
-
print()
|
|
299
|
-
|
|
300
|
-
return exported > 0
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if __name__ == '__main__':
|
|
304
|
-
if len(sys.argv) < 2:
|
|
305
|
-
print("Sử dụng: python skill_export.py <path> --platform <platform>")
|
|
306
|
-
print()
|
|
307
|
-
print("Platforms: cursor, claude, windsurf, cline, copilot, openclaw, all")
|
|
308
|
-
print()
|
|
309
|
-
print("Options:")
|
|
310
|
-
print(" --platform <name> Nền tảng đích (bắt buộc)")
|
|
311
|
-
print(" --output <dir> Thư mục output (mặc định: ./exports/)")
|
|
312
|
-
print()
|
|
313
|
-
print("Ví dụ:")
|
|
314
|
-
print(" python skill_export.py ./my-skill/ --platform cursor")
|
|
315
|
-
print(" python skill_export.py ./my-skill/ --platform all")
|
|
316
|
-
print(" python skill_export.py ./my-skill/ --platform all --output ./exports/")
|
|
317
|
-
sys.exit(1)
|
|
318
|
-
|
|
319
|
-
target = sys.argv[1]
|
|
320
|
-
platform = 'all'
|
|
321
|
-
output = None
|
|
322
|
-
|
|
323
|
-
for i, arg in enumerate(sys.argv):
|
|
324
|
-
if arg == '--platform' and i + 1 < len(sys.argv):
|
|
325
|
-
platform = sys.argv[i + 1].lower()
|
|
326
|
-
elif arg == '--output' and i + 1 < len(sys.argv):
|
|
327
|
-
output = sys.argv[i + 1]
|
|
328
|
-
|
|
329
|
-
if not os.path.exists(target):
|
|
330
|
-
print(f"❌ Không tìm thấy: {target}")
|
|
331
|
-
sys.exit(1)
|
|
332
|
-
|
|
333
|
-
success = export_skill(target, platform, output)
|
|
334
|
-
sys.exit(0 if success else 1)
|