codymaster 4.4.4 → 4.5.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 +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 +7 -1
- 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-quality-gate/SKILL.md +11 -1
- package/skills/cm-safe-deploy/SKILL.md +38 -2
- package/skills/cm-security-gate/SKILL.md +158 -34
- package/skills/cm-skill-chain/SKILL.md +47 -1
- package/skills/cm-start/SKILL.md +11 -2
- package/skills/cm-test-gate/SKILL.md +3 -0
- 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,611 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
skill_audit.py — Tự audit Skill theo 7 Nguyên Tắc Hoàn Hảo
|
|
4
|
-
|
|
5
|
-
Sử dụng:
|
|
6
|
-
python skill_audit.py /path/to/SKILL.md
|
|
7
|
-
python skill_audit.py /path/to/skill-folder/
|
|
8
|
-
python skill_audit.py /path/to/skill-folder/ --json # Output JSON
|
|
9
|
-
python skill_audit.py /path/to/skill-folder/ --strict # Chấm khắt khe
|
|
10
|
-
|
|
11
|
-
7 Nguyên tắc:
|
|
12
|
-
1. Atomic Logic — 1 skill = 1 việc (tên không có AND)
|
|
13
|
-
2. Semantic Trigger — Description đủ dài, có trigger phrases
|
|
14
|
-
3. 4 Core Sections — Goal + Instructions + Examples + Constraints
|
|
15
|
-
4. Show Don't Tell — ≥2 ví dụ có Input/Output
|
|
16
|
-
5. Semantic Precision — Không dùng từ mơ hồ
|
|
17
|
-
6. Error Recovery — Có xử lý lỗi, fallback
|
|
18
|
-
7. Black Box Scripts — Script có --help, --dry-run (nếu có scripts/)
|
|
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
|
-
import json
|
|
27
|
-
from pathlib import Path
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# ============================================================
|
|
31
|
-
# COLOR OUTPUT
|
|
32
|
-
# ============================================================
|
|
33
|
-
|
|
34
|
-
class Colors:
|
|
35
|
-
"""ANSI color codes cho terminal output."""
|
|
36
|
-
PASS = '\033[92m'
|
|
37
|
-
FAIL = '\033[91m'
|
|
38
|
-
WARN = '\033[93m'
|
|
39
|
-
INFO = '\033[96m'
|
|
40
|
-
BOLD = '\033[1m'
|
|
41
|
-
DIM = '\033[2m'
|
|
42
|
-
RESET = '\033[0m'
|
|
43
|
-
|
|
44
|
-
@staticmethod
|
|
45
|
-
def enable_windows():
|
|
46
|
-
"""Bật ANSI colors cho Windows terminal."""
|
|
47
|
-
if os.name == 'nt':
|
|
48
|
-
try:
|
|
49
|
-
import ctypes
|
|
50
|
-
kernel32 = ctypes.windll.kernel32
|
|
51
|
-
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
|
|
52
|
-
except Exception:
|
|
53
|
-
pass
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
Colors.enable_windows()
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# ============================================================
|
|
60
|
-
# PARSERS
|
|
61
|
-
# ============================================================
|
|
62
|
-
|
|
63
|
-
def parse_frontmatter(content):
|
|
64
|
-
"""Trích xuất YAML frontmatter từ nội dung SKILL.md."""
|
|
65
|
-
match = re.match(r'^---\s*\n(.*?)\n---\s*\n', content, re.DOTALL)
|
|
66
|
-
if not match:
|
|
67
|
-
return None, content
|
|
68
|
-
|
|
69
|
-
fm_text = match.group(1)
|
|
70
|
-
body = content[match.end():]
|
|
71
|
-
|
|
72
|
-
frontmatter = {}
|
|
73
|
-
current_key = None
|
|
74
|
-
current_value = []
|
|
75
|
-
|
|
76
|
-
for line in fm_text.split('\n'):
|
|
77
|
-
key_match = re.match(r'^(\w+):\s*(.*)', line)
|
|
78
|
-
if key_match:
|
|
79
|
-
if current_key:
|
|
80
|
-
frontmatter[current_key] = '\n'.join(current_value).strip()
|
|
81
|
-
current_key = key_match.group(1)
|
|
82
|
-
current_value = [key_match.group(2)] if key_match.group(2) and key_match.group(2) != '|' else []
|
|
83
|
-
elif current_key and line.startswith(' '):
|
|
84
|
-
current_value.append(line.strip())
|
|
85
|
-
|
|
86
|
-
if current_key:
|
|
87
|
-
frontmatter[current_key] = '\n'.join(current_value).strip()
|
|
88
|
-
|
|
89
|
-
return frontmatter, body
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def find_sections(body):
|
|
93
|
-
"""Tìm tất cả heading sections (# Level 1) trong body."""
|
|
94
|
-
sections = {}
|
|
95
|
-
current_section = None
|
|
96
|
-
current_content = []
|
|
97
|
-
|
|
98
|
-
for line in body.split('\n'):
|
|
99
|
-
h1_match = re.match(r'^#\s+(.+)', line)
|
|
100
|
-
if h1_match:
|
|
101
|
-
if current_section:
|
|
102
|
-
sections[current_section.lower()] = '\n'.join(current_content)
|
|
103
|
-
current_section = h1_match.group(1).strip()
|
|
104
|
-
current_content = []
|
|
105
|
-
else:
|
|
106
|
-
current_content.append(line)
|
|
107
|
-
|
|
108
|
-
if current_section:
|
|
109
|
-
sections[current_section.lower()] = '\n'.join(current_content)
|
|
110
|
-
|
|
111
|
-
return sections
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
# ============================================================
|
|
115
|
-
# 7 AUDIT PRINCIPLES
|
|
116
|
-
# ============================================================
|
|
117
|
-
|
|
118
|
-
AMBIGUOUS_WORDS = [
|
|
119
|
-
'xử lý', 'kiểm tra', 'tối ưu', 'cải thiện', 'tốt',
|
|
120
|
-
'sạch', 'nhiều', 'lớn', 'nhanh', 'phù hợp',
|
|
121
|
-
'hợp lý', 'chính xác', 'đầy đủ', 'chuẩn', 'ổn',
|
|
122
|
-
]
|
|
123
|
-
|
|
124
|
-
ERROR_KEYWORDS = [
|
|
125
|
-
'nếu lỗi', 'nếu không', 'if error', 'fallback', 'retry',
|
|
126
|
-
'thất bại', 'failed', 'không tìm thấy', 'not found',
|
|
127
|
-
'trường hợp đặc biệt', 'edge case', 'ngoại lệ', 'exception',
|
|
128
|
-
'VERIFY', 'verify', 'kiểm tra lại', 'double-check',
|
|
129
|
-
]
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def audit_principle_1_atomic(frontmatter, body):
|
|
133
|
-
"""Nguyên tắc 1: Atomic Logic — 1 skill = 1 việc."""
|
|
134
|
-
result = {'name': 'Atomic Logic', 'score': 0, 'max': 15, 'details': []}
|
|
135
|
-
|
|
136
|
-
name = frontmatter.get('name', '')
|
|
137
|
-
desc = frontmatter.get('description', '')
|
|
138
|
-
|
|
139
|
-
# Check 1: Tên không chứa AND/và/+
|
|
140
|
-
if not re.search(r'\band\b|\bvà\b|\+', name, re.IGNORECASE):
|
|
141
|
-
result['score'] += 5
|
|
142
|
-
result['details'].append(('PASS', f'Tên "{name}" không chứa AND — atomic ✓'))
|
|
143
|
-
else:
|
|
144
|
-
result['details'].append(('FAIL', f'Tên "{name}" chứa AND/và/+ — có thể ôm đồm'))
|
|
145
|
-
|
|
146
|
-
# Check 2: Description trả lời "Làm 1 việc gì?"
|
|
147
|
-
verbs_found = len(re.findall(r'\b(tạo|sinh|phân tích|kiểm tra|chuyển|soạn|tìm|generate|create|validate|parse)\b', desc, re.IGNORECASE))
|
|
148
|
-
if 1 <= verbs_found <= 3:
|
|
149
|
-
result['score'] += 5
|
|
150
|
-
result['details'].append(('PASS', f'Description có {verbs_found} động từ chính — tập trung ✓'))
|
|
151
|
-
elif verbs_found > 3:
|
|
152
|
-
result['details'].append(('WARN', f'Description có {verbs_found} động từ — có thể ôm đồm'))
|
|
153
|
-
result['score'] += 2
|
|
154
|
-
else:
|
|
155
|
-
result['details'].append(('WARN', 'Description thiếu động từ hành động'))
|
|
156
|
-
result['score'] += 1
|
|
157
|
-
|
|
158
|
-
# Check 3: Không quá nhiều H2 sections trong Instructions (≤10 phases)
|
|
159
|
-
phases = len(re.findall(r'^##\s+', body, re.MULTILINE))
|
|
160
|
-
if phases <= 10:
|
|
161
|
-
result['score'] += 5
|
|
162
|
-
result['details'].append(('PASS', f'{phases} phases/sections — acceptable ✓'))
|
|
163
|
-
else:
|
|
164
|
-
result['details'].append(('WARN', f'{phases} phases — quá phức tạp, cân nhắc tách skill'))
|
|
165
|
-
result['score'] += 2
|
|
166
|
-
|
|
167
|
-
return result
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def audit_principle_2_trigger(frontmatter):
|
|
171
|
-
"""Nguyên tắc 2: Semantic Trigger — Description rõ ràng."""
|
|
172
|
-
result = {'name': 'Semantic Trigger', 'score': 0, 'max': 15, 'details': []}
|
|
173
|
-
|
|
174
|
-
desc = frontmatter.get('description', '')
|
|
175
|
-
|
|
176
|
-
# Check 1: Description đủ dài (≥30 ký tự)
|
|
177
|
-
if len(desc) >= 30:
|
|
178
|
-
result['score'] += 3
|
|
179
|
-
result['details'].append(('PASS', f'Description {len(desc)} ký tự (≥30) ✓'))
|
|
180
|
-
else:
|
|
181
|
-
result['details'].append(('FAIL', f'Description chỉ {len(desc)} ký tự — quá ngắn (cần ≥30)'))
|
|
182
|
-
|
|
183
|
-
# Check 2: Có trigger phrases (khi nào kích hoạt)
|
|
184
|
-
trigger_patterns = [
|
|
185
|
-
r'kích hoạt khi', r'khi user', r'khi người dùng',
|
|
186
|
-
r'trigger', r'activated when', r'khi nói', r'khi yêu cầu',
|
|
187
|
-
]
|
|
188
|
-
has_trigger = any(re.search(p, desc, re.IGNORECASE) for p in trigger_patterns)
|
|
189
|
-
if has_trigger:
|
|
190
|
-
result['score'] += 4
|
|
191
|
-
result['details'].append(('PASS', 'Có trigger phrases — AI biết khi nào kích hoạt ✓'))
|
|
192
|
-
else:
|
|
193
|
-
result['details'].append(('WARN', 'Thiếu trigger phrases — AI có thể không biết khi nào dùng'))
|
|
194
|
-
result['score'] += 1
|
|
195
|
-
|
|
196
|
-
# Check 3: Trả lời "Làm gì?"
|
|
197
|
-
action_words = re.findall(r'\b(tạo|sinh|phân tích|kiểm tra|chuyển|soạn|tìm|generate|create|validate)\b', desc, re.IGNORECASE)
|
|
198
|
-
if action_words:
|
|
199
|
-
result['score'] += 4
|
|
200
|
-
result['details'].append(('PASS', f'Trả lời "Làm gì?" — {action_words[0]} ✓'))
|
|
201
|
-
else:
|
|
202
|
-
result['details'].append(('FAIL', 'Không trả lời "Làm gì?" — thiếu động từ hành động'))
|
|
203
|
-
|
|
204
|
-
# Check 4: Trả lời "Cho ai?"
|
|
205
|
-
audience_words = re.findall(r'\b(dành cho|cho|user|người dùng|developer|team)\b', desc, re.IGNORECASE)
|
|
206
|
-
if audience_words:
|
|
207
|
-
result['score'] += 4
|
|
208
|
-
result['details'].append(('PASS', 'Trả lời "Cho ai?" ✓'))
|
|
209
|
-
else:
|
|
210
|
-
result['details'].append(('WARN', 'Không rõ "Cho ai?" — nên thêm đối tượng sử dụng'))
|
|
211
|
-
result['score'] += 1
|
|
212
|
-
|
|
213
|
-
return result
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def audit_principle_3_sections(sections):
|
|
217
|
-
"""Nguyên tắc 3: 4 Core Sections — Goal + Instructions + Examples + Constraints."""
|
|
218
|
-
result = {'name': '4 Core Sections', 'score': 0, 'max': 15, 'details': []}
|
|
219
|
-
|
|
220
|
-
required = {
|
|
221
|
-
'goal': ('Goal', 4),
|
|
222
|
-
'instructions': ('Instructions', 4),
|
|
223
|
-
'examples': ('Examples', 4),
|
|
224
|
-
'constraints': ('Constraints', 3),
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
for key, (label, points) in required.items():
|
|
228
|
-
found = any(key in s for s in sections.keys())
|
|
229
|
-
if found:
|
|
230
|
-
result['score'] += points
|
|
231
|
-
result['details'].append(('PASS', f'Có section "{label}" ✓'))
|
|
232
|
-
else:
|
|
233
|
-
result['details'].append(('FAIL', f'THIẾU section "{label}" — bắt buộc!'))
|
|
234
|
-
|
|
235
|
-
return result
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
def audit_principle_4_show_dont_tell(sections, body):
|
|
239
|
-
"""Nguyên tắc 4: Show Don't Tell — ≥2 ví dụ có Input/Output."""
|
|
240
|
-
result = {'name': 'Show Don\'t Tell', 'score': 0, 'max': 15, 'details': []}
|
|
241
|
-
|
|
242
|
-
# Đếm ví dụ
|
|
243
|
-
example_headings = len(re.findall(r'^##\s+.*[Vv]í dụ|^##\s+.*[Ee]xample', body, re.MULTILINE))
|
|
244
|
-
if example_headings == 0:
|
|
245
|
-
# Thử đếm ### level
|
|
246
|
-
example_headings = len(re.findall(r'^###\s+.*[Vv]í dụ|^###\s+.*[Ee]xample', body, re.MULTILINE))
|
|
247
|
-
|
|
248
|
-
if example_headings >= 3:
|
|
249
|
-
result['score'] += 6
|
|
250
|
-
result['details'].append(('PASS', f'{example_headings} ví dụ — tuyệt vời (≥3 = 92% accuracy) ✓'))
|
|
251
|
-
elif example_headings >= 2:
|
|
252
|
-
result['score'] += 5
|
|
253
|
-
result['details'].append(('PASS', f'{example_headings} ví dụ — tốt (≥2 = 85% accuracy) ✓'))
|
|
254
|
-
elif example_headings >= 1:
|
|
255
|
-
result['score'] += 3
|
|
256
|
-
result['details'].append(('WARN', f'{example_headings} ví dụ — cần thêm (1 = 65% accuracy)'))
|
|
257
|
-
else:
|
|
258
|
-
result['details'].append(('FAIL', 'KHÔNG có ví dụ — AI sẽ hallucinate!'))
|
|
259
|
-
|
|
260
|
-
# Check: Có Input/Output rõ ràng
|
|
261
|
-
has_input = bool(re.search(r'\*\*Input\*\*|\*\*Đầu vào\*\*|Input:', body, re.IGNORECASE))
|
|
262
|
-
has_output = bool(re.search(r'\*\*Output\*\*|\*\*Đầu ra\*\*|Output:', body, re.IGNORECASE))
|
|
263
|
-
|
|
264
|
-
if has_input and has_output:
|
|
265
|
-
result['score'] += 5
|
|
266
|
-
result['details'].append(('PASS', 'Ví dụ có Input + Output rõ ràng ✓'))
|
|
267
|
-
elif has_input or has_output:
|
|
268
|
-
result['score'] += 3
|
|
269
|
-
result['details'].append(('WARN', 'Ví dụ chỉ có Input hoặc Output — cần cả hai'))
|
|
270
|
-
else:
|
|
271
|
-
result['details'].append(('WARN', 'Ví dụ thiếu format Input/Output rõ ràng'))
|
|
272
|
-
result['score'] += 1
|
|
273
|
-
|
|
274
|
-
# Check: Có Thought Process
|
|
275
|
-
has_thought = bool(re.search(r'[Tt]hought [Pp]rocess|[Pp]hân tích|Suy luận', body))
|
|
276
|
-
if has_thought:
|
|
277
|
-
result['score'] += 4
|
|
278
|
-
result['details'].append(('PASS', 'Ví dụ có Thought Process — AI hiểu logic ✓'))
|
|
279
|
-
else:
|
|
280
|
-
result['details'].append(('INFO', 'Không có Thought Process — khuyến khích thêm'))
|
|
281
|
-
result['score'] += 2
|
|
282
|
-
|
|
283
|
-
return result
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
def audit_principle_5_precision(body):
|
|
287
|
-
"""Nguyên tắc 5: Semantic Precision — Không dùng từ mơ hồ."""
|
|
288
|
-
result = {'name': 'Semantic Precision', 'score': 0, 'max': 15, 'details': []}
|
|
289
|
-
|
|
290
|
-
# Đếm từ mơ hồ trong Instructions
|
|
291
|
-
instructions_section = ''
|
|
292
|
-
in_instructions = False
|
|
293
|
-
for line in body.split('\n'):
|
|
294
|
-
if re.match(r'^#\s+Instructions', line, re.IGNORECASE):
|
|
295
|
-
in_instructions = True
|
|
296
|
-
continue
|
|
297
|
-
elif re.match(r'^#\s+', line) and in_instructions:
|
|
298
|
-
break
|
|
299
|
-
if in_instructions:
|
|
300
|
-
instructions_section += line + '\n'
|
|
301
|
-
|
|
302
|
-
ambiguous_found = []
|
|
303
|
-
for word in AMBIGUOUS_WORDS:
|
|
304
|
-
count = len(re.findall(rf'\b{word}\b', instructions_section, re.IGNORECASE))
|
|
305
|
-
if count > 0:
|
|
306
|
-
ambiguous_found.append((word, count))
|
|
307
|
-
|
|
308
|
-
if len(ambiguous_found) == 0:
|
|
309
|
-
result['score'] += 8
|
|
310
|
-
result['details'].append(('PASS', 'Không có từ mơ hồ trong Instructions ✓'))
|
|
311
|
-
elif len(ambiguous_found) <= 2:
|
|
312
|
-
result['score'] += 5
|
|
313
|
-
words_str = ', '.join([f'"{w}" ({c}x)' for w, c in ambiguous_found])
|
|
314
|
-
result['details'].append(('WARN', f'Có {len(ambiguous_found)} từ mơ hồ: {words_str}'))
|
|
315
|
-
else:
|
|
316
|
-
result['score'] += 2
|
|
317
|
-
words_str = ', '.join([f'"{w}" ({c}x)' for w, c in ambiguous_found[:5]])
|
|
318
|
-
result['details'].append(('FAIL', f'Có {len(ambiguous_found)} từ mơ hồ: {words_str}'))
|
|
319
|
-
|
|
320
|
-
# Check: Dùng động từ cụ thể
|
|
321
|
-
precise_verbs = re.findall(
|
|
322
|
-
r'\b(analyze|transform|validate|generate|execute|parse|filter|compare|extract|calculate|tính|trích|sinh|xác nhận|phân loại)\b',
|
|
323
|
-
instructions_section, re.IGNORECASE
|
|
324
|
-
)
|
|
325
|
-
if len(precise_verbs) >= 3:
|
|
326
|
-
result['score'] += 7
|
|
327
|
-
result['details'].append(('PASS', f'Dùng {len(precise_verbs)} động từ chính xác ✓'))
|
|
328
|
-
elif len(precise_verbs) >= 1:
|
|
329
|
-
result['score'] += 4
|
|
330
|
-
result['details'].append(('WARN', f'Chỉ có {len(precise_verbs)} động từ chính xác — cần thêm'))
|
|
331
|
-
else:
|
|
332
|
-
result['details'].append(('FAIL', 'Không có động từ chính xác — Instructions quá mơ hồ'))
|
|
333
|
-
|
|
334
|
-
return result
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
def audit_principle_6_error_recovery(body):
|
|
338
|
-
"""Nguyên tắc 6: Error Recovery — Có xử lý lỗi."""
|
|
339
|
-
result = {'name': 'Error Recovery', 'score': 0, 'max': 15, 'details': []}
|
|
340
|
-
|
|
341
|
-
error_count = 0
|
|
342
|
-
for keyword in ERROR_KEYWORDS:
|
|
343
|
-
if re.search(rf'{keyword}', body, re.IGNORECASE):
|
|
344
|
-
error_count += 1
|
|
345
|
-
|
|
346
|
-
if error_count >= 5:
|
|
347
|
-
result['score'] += 8
|
|
348
|
-
result['details'].append(('PASS', f'{error_count} error handling patterns — xuất sắc ✓'))
|
|
349
|
-
elif error_count >= 3:
|
|
350
|
-
result['score'] += 6
|
|
351
|
-
result['details'].append(('PASS', f'{error_count} error handling patterns — tốt ✓'))
|
|
352
|
-
elif error_count >= 1:
|
|
353
|
-
result['score'] += 3
|
|
354
|
-
result['details'].append(('WARN', f'{error_count} error handling — cần thêm'))
|
|
355
|
-
else:
|
|
356
|
-
result['details'].append(('FAIL', 'KHÔNG có error handling — skill sẽ crash khi gặp lỗi'))
|
|
357
|
-
|
|
358
|
-
# Check: Có decision tree / branching
|
|
359
|
-
branching = len(re.findall(r'nếu|if |→|rẽ nhánh|otherwise|else', body, re.IGNORECASE))
|
|
360
|
-
if branching >= 5:
|
|
361
|
-
result['score'] += 7
|
|
362
|
-
result['details'].append(('PASS', f'{branching} branching points — logic mạnh ✓'))
|
|
363
|
-
elif branching >= 2:
|
|
364
|
-
result['score'] += 4
|
|
365
|
-
result['details'].append(('WARN', f'{branching} branching points — cần thêm'))
|
|
366
|
-
else:
|
|
367
|
-
result['details'].append(('FAIL', 'Thiếu branching logic — skill chỉ chạy happy path'))
|
|
368
|
-
result['score'] += 1
|
|
369
|
-
|
|
370
|
-
return result
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
def audit_principle_7_scripts(skill_dir):
|
|
374
|
-
"""Nguyên tắc 7: Black Box Scripts — Script có --help, --dry-run."""
|
|
375
|
-
result = {'name': 'Black Box Scripts', 'score': 0, 'max': 10, 'details': []}
|
|
376
|
-
|
|
377
|
-
if not skill_dir or not os.path.isdir(skill_dir):
|
|
378
|
-
result['score'] += 10
|
|
379
|
-
result['details'].append(('INFO', 'Không có thư mục skill — bỏ qua check scripts (full marks)'))
|
|
380
|
-
return result
|
|
381
|
-
|
|
382
|
-
scripts_dir = os.path.join(skill_dir, 'scripts')
|
|
383
|
-
if not os.path.isdir(scripts_dir):
|
|
384
|
-
result['score'] += 10
|
|
385
|
-
result['details'].append(('INFO', 'Không có scripts/ — skill không cần scripts (full marks)'))
|
|
386
|
-
return result
|
|
387
|
-
|
|
388
|
-
scripts = [f for f in os.listdir(scripts_dir) if f.endswith(('.py', '.sh', '.bash'))]
|
|
389
|
-
|
|
390
|
-
if not scripts:
|
|
391
|
-
result['score'] += 10
|
|
392
|
-
result['details'].append(('INFO', 'Thư mục scripts/ trống (full marks)'))
|
|
393
|
-
return result
|
|
394
|
-
|
|
395
|
-
result['details'].append(('INFO', f'Tìm thấy {len(scripts)} scripts'))
|
|
396
|
-
|
|
397
|
-
help_count = 0
|
|
398
|
-
dryrun_count = 0
|
|
399
|
-
|
|
400
|
-
for script_name in scripts:
|
|
401
|
-
script_path = os.path.join(scripts_dir, script_name)
|
|
402
|
-
try:
|
|
403
|
-
with open(script_path, 'r', encoding='utf-8') as f:
|
|
404
|
-
content = f.read()
|
|
405
|
-
|
|
406
|
-
has_help = bool(re.search(r'--help|argparse|ArgumentParser', content))
|
|
407
|
-
has_dryrun = bool(re.search(r'--dry-run|dry.run|DRY_RUN', content))
|
|
408
|
-
|
|
409
|
-
if has_help:
|
|
410
|
-
help_count += 1
|
|
411
|
-
if has_dryrun:
|
|
412
|
-
dryrun_count += 1
|
|
413
|
-
|
|
414
|
-
except Exception:
|
|
415
|
-
result['details'].append(('WARN', f'Không đọc được {script_name}'))
|
|
416
|
-
|
|
417
|
-
if help_count == len(scripts):
|
|
418
|
-
result['score'] += 5
|
|
419
|
-
result['details'].append(('PASS', f'Tất cả {len(scripts)} scripts có --help ✓'))
|
|
420
|
-
elif help_count > 0:
|
|
421
|
-
result['score'] += 3
|
|
422
|
-
result['details'].append(('WARN', f'{help_count}/{len(scripts)} scripts có --help'))
|
|
423
|
-
else:
|
|
424
|
-
result['details'].append(('FAIL', 'Không script nào có --help'))
|
|
425
|
-
|
|
426
|
-
if dryrun_count > 0:
|
|
427
|
-
result['score'] += 5
|
|
428
|
-
result['details'].append(('PASS', f'{dryrun_count}/{len(scripts)} scripts có --dry-run ✓'))
|
|
429
|
-
else:
|
|
430
|
-
result['score'] += 3
|
|
431
|
-
result['details'].append(('INFO', 'Không script nào có --dry-run — khuyến khích thêm'))
|
|
432
|
-
|
|
433
|
-
return result
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
# ============================================================
|
|
437
|
-
# MAIN AUDIT
|
|
438
|
-
# ============================================================
|
|
439
|
-
|
|
440
|
-
TIER_LABELS = {
|
|
441
|
-
'S': ('🏆 S-tier — Hoàn hảo', Colors.PASS),
|
|
442
|
-
'A': ('⭐ A-tier — Xuất sắc', Colors.PASS),
|
|
443
|
-
'B': ('✅ B-tier — Tốt', Colors.INFO),
|
|
444
|
-
'C': ('⚠️ C-tier — Cần cải thiện', Colors.WARN),
|
|
445
|
-
'D': ('❌ D-tier — Yếu', Colors.FAIL),
|
|
446
|
-
'F': ('💀 F-tier — Fail', Colors.FAIL),
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
def get_tier(score):
|
|
451
|
-
"""Xác định tier dựa trên điểm."""
|
|
452
|
-
if score >= 95:
|
|
453
|
-
return 'S'
|
|
454
|
-
elif score >= 85:
|
|
455
|
-
return 'A'
|
|
456
|
-
elif score >= 70:
|
|
457
|
-
return 'B'
|
|
458
|
-
elif score >= 55:
|
|
459
|
-
return 'C'
|
|
460
|
-
elif score >= 40:
|
|
461
|
-
return 'D'
|
|
462
|
-
else:
|
|
463
|
-
return 'F'
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
def audit_skill(target_path, strict=False, json_output=False):
|
|
467
|
-
"""Chạy toàn bộ audit cho skill."""
|
|
468
|
-
|
|
469
|
-
# Tìm SKILL.md
|
|
470
|
-
if os.path.isdir(target_path):
|
|
471
|
-
skill_dir = target_path
|
|
472
|
-
skill_md = os.path.join(target_path, 'SKILL.md')
|
|
473
|
-
else:
|
|
474
|
-
skill_dir = os.path.dirname(target_path)
|
|
475
|
-
skill_md = target_path
|
|
476
|
-
|
|
477
|
-
if not os.path.exists(skill_md):
|
|
478
|
-
print(f"{Colors.FAIL}❌ Không tìm thấy SKILL.md tại: {skill_md}{Colors.RESET}")
|
|
479
|
-
return False
|
|
480
|
-
|
|
481
|
-
# Đọc file
|
|
482
|
-
with open(skill_md, 'r', encoding='utf-8') as f:
|
|
483
|
-
content = f.read()
|
|
484
|
-
|
|
485
|
-
frontmatter, body = parse_frontmatter(content)
|
|
486
|
-
|
|
487
|
-
if not frontmatter:
|
|
488
|
-
print(f"{Colors.FAIL}❌ Không tìm thấy YAML frontmatter!{Colors.RESET}")
|
|
489
|
-
return False
|
|
490
|
-
|
|
491
|
-
sections = find_sections(body)
|
|
492
|
-
skill_name = frontmatter.get('name', 'unknown')
|
|
493
|
-
|
|
494
|
-
# Header
|
|
495
|
-
if not json_output:
|
|
496
|
-
print()
|
|
497
|
-
print(f"{Colors.BOLD}{'=' * 60}{Colors.RESET}")
|
|
498
|
-
print(f"{Colors.BOLD} 🔍 SKILL AUDIT — 7 Nguyên Tắc Hoàn Hảo{Colors.RESET}")
|
|
499
|
-
print(f"{Colors.BOLD} Skill: {Colors.INFO}{skill_name}{Colors.RESET}")
|
|
500
|
-
print(f"{Colors.BOLD} File: {Colors.DIM}{skill_md}{Colors.RESET}")
|
|
501
|
-
print(f"{Colors.BOLD}{'=' * 60}{Colors.RESET}")
|
|
502
|
-
print()
|
|
503
|
-
|
|
504
|
-
# Run 7 audits
|
|
505
|
-
results = []
|
|
506
|
-
results.append(audit_principle_1_atomic(frontmatter, body))
|
|
507
|
-
results.append(audit_principle_2_trigger(frontmatter))
|
|
508
|
-
results.append(audit_principle_3_sections(sections))
|
|
509
|
-
results.append(audit_principle_4_show_dont_tell(sections, body))
|
|
510
|
-
results.append(audit_principle_5_precision(body))
|
|
511
|
-
results.append(audit_principle_6_error_recovery(body))
|
|
512
|
-
results.append(audit_principle_7_scripts(skill_dir if os.path.isdir(target_path) else None))
|
|
513
|
-
|
|
514
|
-
total_score = sum(r['score'] for r in results)
|
|
515
|
-
total_max = sum(r['max'] for r in results)
|
|
516
|
-
percentage = round(total_score / total_max * 100)
|
|
517
|
-
tier = get_tier(percentage)
|
|
518
|
-
|
|
519
|
-
# JSON output
|
|
520
|
-
if json_output:
|
|
521
|
-
output = {
|
|
522
|
-
'skill': skill_name,
|
|
523
|
-
'file': skill_md,
|
|
524
|
-
'score': total_score,
|
|
525
|
-
'max': total_max,
|
|
526
|
-
'percentage': percentage,
|
|
527
|
-
'tier': tier,
|
|
528
|
-
'principles': []
|
|
529
|
-
}
|
|
530
|
-
for i, r in enumerate(results, 1):
|
|
531
|
-
output['principles'].append({
|
|
532
|
-
'number': i,
|
|
533
|
-
'name': r['name'],
|
|
534
|
-
'score': r['score'],
|
|
535
|
-
'max': r['max'],
|
|
536
|
-
'percentage': round(r['score'] / r['max'] * 100),
|
|
537
|
-
'details': [{'status': d[0], 'message': d[1]} for d in r['details']]
|
|
538
|
-
})
|
|
539
|
-
print(json.dumps(output, ensure_ascii=False, indent=2))
|
|
540
|
-
return percentage >= 70
|
|
541
|
-
|
|
542
|
-
# Pretty output
|
|
543
|
-
for i, r in enumerate(results, 1):
|
|
544
|
-
p_pct = round(r['score'] / r['max'] * 100)
|
|
545
|
-
if p_pct >= 90:
|
|
546
|
-
color = Colors.PASS
|
|
547
|
-
icon = '🟢'
|
|
548
|
-
elif p_pct >= 70:
|
|
549
|
-
color = Colors.WARN
|
|
550
|
-
icon = '🟡'
|
|
551
|
-
else:
|
|
552
|
-
color = Colors.FAIL
|
|
553
|
-
icon = '🔴'
|
|
554
|
-
|
|
555
|
-
print(f" {icon} {Colors.BOLD}Nguyên tắc {i}: {r['name']}{Colors.RESET}")
|
|
556
|
-
print(f" Điểm: {color}{r['score']}/{r['max']} ({p_pct}%){Colors.RESET}")
|
|
557
|
-
|
|
558
|
-
for status, message in r['details']:
|
|
559
|
-
if status == 'PASS':
|
|
560
|
-
print(f" {Colors.PASS}✅ {message}{Colors.RESET}")
|
|
561
|
-
elif status == 'FAIL':
|
|
562
|
-
print(f" {Colors.FAIL}❌ {message}{Colors.RESET}")
|
|
563
|
-
elif status == 'WARN':
|
|
564
|
-
print(f" {Colors.WARN}⚠️ {message}{Colors.RESET}")
|
|
565
|
-
else:
|
|
566
|
-
print(f" {Colors.DIM}ℹ️ {message}{Colors.RESET}")
|
|
567
|
-
|
|
568
|
-
print()
|
|
569
|
-
|
|
570
|
-
# Summary
|
|
571
|
-
tier_label, tier_color = TIER_LABELS[tier]
|
|
572
|
-
print(f"{Colors.BOLD}{'=' * 60}{Colors.RESET}")
|
|
573
|
-
print(f" {Colors.BOLD}📊 TỔNG ĐIỂM: {tier_color}{total_score}/{total_max} ({percentage}%){Colors.RESET}")
|
|
574
|
-
print(f" {Colors.BOLD}🏅 XẾP HẠNG: {tier_color}{tier_label}{Colors.RESET}")
|
|
575
|
-
print(f"{Colors.BOLD}{'=' * 60}{Colors.RESET}")
|
|
576
|
-
print()
|
|
577
|
-
|
|
578
|
-
# Recommendations
|
|
579
|
-
low_principles = [r for r in results if r['score'] / r['max'] < 0.7]
|
|
580
|
-
if low_principles:
|
|
581
|
-
print(f" {Colors.WARN}💡 Khuyến nghị cải thiện:{Colors.RESET}")
|
|
582
|
-
for r in low_principles:
|
|
583
|
-
fails = [d[1] for d in r['details'] if d[0] in ('FAIL', 'WARN')]
|
|
584
|
-
for f in fails[:2]:
|
|
585
|
-
print(f" → {r['name']}: {f}")
|
|
586
|
-
print()
|
|
587
|
-
|
|
588
|
-
return percentage >= (85 if strict else 70)
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
if __name__ == '__main__':
|
|
592
|
-
if len(sys.argv) < 2:
|
|
593
|
-
print("Sử dụng: python skill_audit.py <path-to-SKILL.md-or-skill-folder>")
|
|
594
|
-
print("Options:")
|
|
595
|
-
print(" --json Output dạng JSON")
|
|
596
|
-
print(" --strict Chấm khắt khe (cần ≥85% thay vì ≥70%)")
|
|
597
|
-
print()
|
|
598
|
-
print("Ví dụ: python skill_audit.py ./my-skill/")
|
|
599
|
-
print(" python skill_audit.py ./my-skill/ --json")
|
|
600
|
-
sys.exit(1)
|
|
601
|
-
|
|
602
|
-
target = sys.argv[1]
|
|
603
|
-
json_mode = '--json' in sys.argv
|
|
604
|
-
strict_mode = '--strict' in sys.argv
|
|
605
|
-
|
|
606
|
-
if not os.path.exists(target):
|
|
607
|
-
print(f"❌ Không tìm thấy: {target}")
|
|
608
|
-
sys.exit(1)
|
|
609
|
-
|
|
610
|
-
success = audit_skill(target, strict=strict_mode, json_output=json_mode)
|
|
611
|
-
sys.exit(0 if success else 1)
|