flonat-research 0.1.0
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/.claude/agents/domain-reviewer.md +336 -0
- package/.claude/agents/fixer.md +226 -0
- package/.claude/agents/paper-critic.md +370 -0
- package/.claude/agents/peer-reviewer.md +289 -0
- package/.claude/agents/proposal-reviewer.md +215 -0
- package/.claude/agents/referee2-reviewer.md +367 -0
- package/.claude/agents/references/journal-referee-profiles.md +354 -0
- package/.claude/agents/references/paper-critic/council-personas.md +77 -0
- package/.claude/agents/references/paper-critic/council-prompts.md +198 -0
- package/.claude/agents/references/peer-reviewer/report-template.md +199 -0
- package/.claude/agents/references/peer-reviewer/sa-prompts.md +260 -0
- package/.claude/agents/references/peer-reviewer/security-scan.md +188 -0
- package/.claude/agents/references/proposal-reviewer/report-template.md +144 -0
- package/.claude/agents/references/proposal-reviewer/sa-prompts.md +149 -0
- package/.claude/agents/references/referee-config.md +114 -0
- package/.claude/agents/references/referee2-reviewer/audit-checklists.md +287 -0
- package/.claude/agents/references/referee2-reviewer/report-template.md +334 -0
- package/.claude/rules/design-before-results.md +52 -0
- package/.claude/rules/ignore-agents-md.md +17 -0
- package/.claude/rules/ignore-gemini-md.md +17 -0
- package/.claude/rules/lean-claude-md.md +45 -0
- package/.claude/rules/learn-tags.md +99 -0
- package/.claude/rules/overleaf-separation.md +67 -0
- package/.claude/rules/plan-first.md +175 -0
- package/.claude/rules/read-docs-first.md +50 -0
- package/.claude/rules/scope-discipline.md +28 -0
- package/.claude/settings.json +125 -0
- package/.context/current-focus.md +33 -0
- package/.context/preferences/priorities.md +36 -0
- package/.context/preferences/task-naming.md +28 -0
- package/.context/profile.md +29 -0
- package/.context/projects/_index.md +41 -0
- package/.context/projects/papers/nudge-exp.md +22 -0
- package/.context/projects/papers/uncertainty.md +31 -0
- package/.context/resources/claude-scientific-writer-review.md +48 -0
- package/.context/resources/cunningham-multi-analyst-agents.md +104 -0
- package/.context/resources/cunningham-multilang-code-audit.md +62 -0
- package/.context/resources/google-ai-co-scientist-review.md +72 -0
- package/.context/resources/karpathy-llm-council-review.md +58 -0
- package/.context/resources/multi-coder-reliability-protocol.md +175 -0
- package/.context/resources/pedro-santanna-takeaways.md +96 -0
- package/.context/resources/venue-rankings/abs_ajg_2024.csv +1823 -0
- package/.context/resources/venue-rankings/abs_ajg_2024_econ.csv +356 -0
- package/.context/resources/venue-rankings/cabs_4_4star_theory.csv +40 -0
- package/.context/resources/venue-rankings/core_2026.csv +801 -0
- package/.context/resources/venue-rankings.md +147 -0
- package/.context/workflows/README.md +69 -0
- package/.context/workflows/daily-review.md +91 -0
- package/.context/workflows/meeting-actions.md +108 -0
- package/.context/workflows/replication-protocol.md +155 -0
- package/.context/workflows/weekly-review.md +113 -0
- package/.mcp-server-biblio/formatters.py +158 -0
- package/.mcp-server-biblio/pyproject.toml +11 -0
- package/.mcp-server-biblio/server.py +678 -0
- package/.mcp-server-biblio/sources/__init__.py +14 -0
- package/.mcp-server-biblio/sources/base.py +73 -0
- package/.mcp-server-biblio/sources/formatters.py +83 -0
- package/.mcp-server-biblio/sources/models.py +22 -0
- package/.mcp-server-biblio/sources/multi_source.py +243 -0
- package/.mcp-server-biblio/sources/openalex_source.py +183 -0
- package/.mcp-server-biblio/sources/scopus_source.py +309 -0
- package/.mcp-server-biblio/sources/wos_source.py +508 -0
- package/.mcp-server-biblio/uv.lock +896 -0
- package/.scripts/README.md +161 -0
- package/.scripts/ai_pattern_density.py +446 -0
- package/.scripts/conf +445 -0
- package/.scripts/config.py +122 -0
- package/.scripts/count_inventory.py +275 -0
- package/.scripts/daily_digest.py +288 -0
- package/.scripts/done +177 -0
- package/.scripts/extract_meeting_actions.py +223 -0
- package/.scripts/focus +176 -0
- package/.scripts/generate-codex-agents-md.py +217 -0
- package/.scripts/inbox +194 -0
- package/.scripts/notion_helpers.py +325 -0
- package/.scripts/openalex/query_helpers.py +306 -0
- package/.scripts/papers +227 -0
- package/.scripts/query +223 -0
- package/.scripts/session-history.py +201 -0
- package/.scripts/skill-health.py +516 -0
- package/.scripts/skill-log-miner.py +273 -0
- package/.scripts/sync-to-codex.sh +252 -0
- package/.scripts/task +213 -0
- package/.scripts/tasks +190 -0
- package/.scripts/week +206 -0
- package/CLAUDE.md +197 -0
- package/LICENSE +21 -0
- package/MEMORY.md +38 -0
- package/README.md +269 -0
- package/docs/agents.md +44 -0
- package/docs/bibliography-setup.md +55 -0
- package/docs/council-mode.md +36 -0
- package/docs/getting-started.md +245 -0
- package/docs/hooks.md +38 -0
- package/docs/mcp-servers.md +82 -0
- package/docs/notion-setup.md +109 -0
- package/docs/rules.md +33 -0
- package/docs/scripts.md +303 -0
- package/docs/setup-overview/setup-overview.pdf +0 -0
- package/docs/skills.md +70 -0
- package/docs/system.md +159 -0
- package/hooks/block-destructive-git.sh +66 -0
- package/hooks/context-monitor.py +114 -0
- package/hooks/postcompact-restore.py +157 -0
- package/hooks/precompact-autosave.py +181 -0
- package/hooks/promise-checker.sh +124 -0
- package/hooks/protect-source-files.sh +81 -0
- package/hooks/resume-context-loader.sh +53 -0
- package/hooks/startup-context-loader.sh +102 -0
- package/package.json +51 -0
- package/packages/cli-council/.github/workflows/claude-code-review.yml +44 -0
- package/packages/cli-council/.github/workflows/claude.yml +50 -0
- package/packages/cli-council/README.md +100 -0
- package/packages/cli-council/pyproject.toml +43 -0
- package/packages/cli-council/src/cli_council/__init__.py +19 -0
- package/packages/cli-council/src/cli_council/__main__.py +185 -0
- package/packages/cli-council/src/cli_council/backends/__init__.py +8 -0
- package/packages/cli-council/src/cli_council/backends/base.py +81 -0
- package/packages/cli-council/src/cli_council/backends/claude.py +25 -0
- package/packages/cli-council/src/cli_council/backends/codex.py +27 -0
- package/packages/cli-council/src/cli_council/backends/gemini.py +26 -0
- package/packages/cli-council/src/cli_council/checkpoint.py +212 -0
- package/packages/cli-council/src/cli_council/config.py +51 -0
- package/packages/cli-council/src/cli_council/council.py +391 -0
- package/packages/cli-council/src/cli_council/models.py +46 -0
- package/packages/llm-council/.github/workflows/claude-code-review.yml +44 -0
- package/packages/llm-council/.github/workflows/claude.yml +50 -0
- package/packages/llm-council/README.md +453 -0
- package/packages/llm-council/pyproject.toml +42 -0
- package/packages/llm-council/src/llm_council/__init__.py +23 -0
- package/packages/llm-council/src/llm_council/__main__.py +259 -0
- package/packages/llm-council/src/llm_council/checkpoint.py +193 -0
- package/packages/llm-council/src/llm_council/client.py +253 -0
- package/packages/llm-council/src/llm_council/config.py +232 -0
- package/packages/llm-council/src/llm_council/council.py +482 -0
- package/packages/llm-council/src/llm_council/models.py +46 -0
- package/packages/mcp-bibliography/MEMORY.md +31 -0
- package/packages/mcp-bibliography/_app.py +226 -0
- package/packages/mcp-bibliography/formatters.py +158 -0
- package/packages/mcp-bibliography/log/2026-03-13-2100.md +35 -0
- package/packages/mcp-bibliography/pyproject.toml +15 -0
- package/packages/mcp-bibliography/run.sh +20 -0
- package/packages/mcp-bibliography/scholarly_formatters.py +83 -0
- package/packages/mcp-bibliography/server.py +1857 -0
- package/packages/mcp-bibliography/tools/__init__.py +28 -0
- package/packages/mcp-bibliography/tools/_registry.py +19 -0
- package/packages/mcp-bibliography/tools/altmetric.py +107 -0
- package/packages/mcp-bibliography/tools/core.py +92 -0
- package/packages/mcp-bibliography/tools/dblp.py +52 -0
- package/packages/mcp-bibliography/tools/openalex.py +296 -0
- package/packages/mcp-bibliography/tools/opencitations.py +102 -0
- package/packages/mcp-bibliography/tools/openreview.py +179 -0
- package/packages/mcp-bibliography/tools/orcid.py +131 -0
- package/packages/mcp-bibliography/tools/scholarly.py +575 -0
- package/packages/mcp-bibliography/tools/unpaywall.py +63 -0
- package/packages/mcp-bibliography/tools/zenodo.py +123 -0
- package/packages/mcp-bibliography/uv.lock +711 -0
- package/scripts/setup.sh +143 -0
- package/skills/beamer-deck/SKILL.md +199 -0
- package/skills/beamer-deck/references/quality-rubric.md +54 -0
- package/skills/beamer-deck/references/review-prompts.md +106 -0
- package/skills/bib-validate/SKILL.md +261 -0
- package/skills/bib-validate/references/council-mode.md +34 -0
- package/skills/bib-validate/references/deep-verify.md +79 -0
- package/skills/bib-validate/references/fix-mode.md +36 -0
- package/skills/bib-validate/references/openalex-verification.md +45 -0
- package/skills/bib-validate/references/preprint-check.md +31 -0
- package/skills/bib-validate/references/ref-manager-crossref.md +41 -0
- package/skills/bib-validate/references/report-template.md +82 -0
- package/skills/code-archaeology/SKILL.md +141 -0
- package/skills/code-review/SKILL.md +265 -0
- package/skills/code-review/references/quality-rubric.md +67 -0
- package/skills/consolidate-memory/SKILL.md +208 -0
- package/skills/context-status/SKILL.md +126 -0
- package/skills/creation-guard/SKILL.md +230 -0
- package/skills/devils-advocate/SKILL.md +130 -0
- package/skills/devils-advocate/references/competing-hypotheses.md +83 -0
- package/skills/init-project/SKILL.md +115 -0
- package/skills/init-project-course/references/memory-and-settings.md +92 -0
- package/skills/init-project-course/references/organise-templates.md +94 -0
- package/skills/init-project-course/skill.md +147 -0
- package/skills/init-project-light/skill.md +139 -0
- package/skills/init-project-research/SKILL.md +368 -0
- package/skills/init-project-research/references/atlas-pipeline-sync.md +70 -0
- package/skills/init-project-research/references/atlas-schema.md +81 -0
- package/skills/init-project-research/references/confirmation-report.md +39 -0
- package/skills/init-project-research/references/domain-profile-template.md +104 -0
- package/skills/init-project-research/references/interview-round3.md +34 -0
- package/skills/init-project-research/references/literature-discovery.md +43 -0
- package/skills/init-project-research/references/scaffold-details.md +197 -0
- package/skills/init-project-research/templates/field-calibration.md +60 -0
- package/skills/init-project-research/templates/pipeline-manifest.md +63 -0
- package/skills/init-project-research/templates/run-all.sh +116 -0
- package/skills/init-project-research/templates/seed-files.md +337 -0
- package/skills/insights-deck/SKILL.md +151 -0
- package/skills/interview-me/SKILL.md +157 -0
- package/skills/latex/SKILL.md +141 -0
- package/skills/latex/references/latex-configs.md +183 -0
- package/skills/latex-autofix/SKILL.md +230 -0
- package/skills/latex-autofix/references/known-errors.md +183 -0
- package/skills/latex-autofix/references/quality-rubric.md +50 -0
- package/skills/latex-health-check/SKILL.md +161 -0
- package/skills/learn/SKILL.md +220 -0
- package/skills/learn/scripts/validate_skill.py +265 -0
- package/skills/lessons-learned/SKILL.md +201 -0
- package/skills/literature/SKILL.md +335 -0
- package/skills/literature/references/agent-templates.md +393 -0
- package/skills/literature/references/bibliometric-apis.md +44 -0
- package/skills/literature/references/cli-council-search.md +79 -0
- package/skills/literature/references/openalex-api-guide.md +371 -0
- package/skills/literature/references/openalex-common-queries.md +381 -0
- package/skills/literature/references/openalex-workflows.md +248 -0
- package/skills/literature/references/reference-manager-sync.md +36 -0
- package/skills/literature/references/scopus-api-guide.md +208 -0
- package/skills/literature/references/wos-api-guide.md +308 -0
- package/skills/multi-perspective/SKILL.md +311 -0
- package/skills/multi-perspective/references/computational-many-analysts.md +77 -0
- package/skills/pipeline-manifest/SKILL.md +226 -0
- package/skills/pre-submission-report/SKILL.md +153 -0
- package/skills/process-reviews/SKILL.md +244 -0
- package/skills/process-reviews/references/rr-routing.md +101 -0
- package/skills/project-deck/SKILL.md +87 -0
- package/skills/project-safety/SKILL.md +135 -0
- package/skills/proofread/SKILL.md +254 -0
- package/skills/proofread/references/quality-rubric.md +104 -0
- package/skills/python-env/SKILL.md +57 -0
- package/skills/quarto-deck/SKILL.md +226 -0
- package/skills/quarto-deck/references/markdown-format.md +143 -0
- package/skills/quarto-deck/references/quality-rubric.md +54 -0
- package/skills/save-context/SKILL.md +174 -0
- package/skills/session-log/SKILL.md +98 -0
- package/skills/shared/concept-validation-gate.md +161 -0
- package/skills/shared/council-protocol.md +265 -0
- package/skills/shared/distribution-diagnostics.md +164 -0
- package/skills/shared/engagement-stratified-sampling.md +218 -0
- package/skills/shared/escalation-protocol.md +74 -0
- package/skills/shared/external-audit-protocol.md +205 -0
- package/skills/shared/intercoder-reliability.md +256 -0
- package/skills/shared/mcp-degradation.md +81 -0
- package/skills/shared/method-probing-questions.md +163 -0
- package/skills/shared/multi-language-conventions.md +143 -0
- package/skills/shared/paid-api-safety.md +174 -0
- package/skills/shared/palettes.md +90 -0
- package/skills/shared/progressive-disclosure.md +92 -0
- package/skills/shared/project-documentation-content.md +443 -0
- package/skills/shared/project-documentation-format.md +281 -0
- package/skills/shared/project-documentation.md +100 -0
- package/skills/shared/publication-output.md +138 -0
- package/skills/shared/quality-scoring.md +70 -0
- package/skills/shared/reference-resolution.md +77 -0
- package/skills/shared/research-quality-rubric.md +165 -0
- package/skills/shared/rhetoric-principles.md +54 -0
- package/skills/shared/skill-design-patterns.md +272 -0
- package/skills/shared/skill-index.md +240 -0
- package/skills/shared/system-documentation.md +334 -0
- package/skills/shared/tikz-rules.md +402 -0
- package/skills/shared/validation-tiers.md +121 -0
- package/skills/shared/venue-guides/README.md +46 -0
- package/skills/shared/venue-guides/cell_press_style.md +483 -0
- package/skills/shared/venue-guides/conferences_formatting.md +564 -0
- package/skills/shared/venue-guides/cs_conference_style.md +463 -0
- package/skills/shared/venue-guides/examples/cell_summary_example.md +247 -0
- package/skills/shared/venue-guides/examples/medical_structured_abstract.md +313 -0
- package/skills/shared/venue-guides/examples/nature_abstract_examples.md +213 -0
- package/skills/shared/venue-guides/examples/neurips_introduction_example.md +245 -0
- package/skills/shared/venue-guides/journals_formatting.md +486 -0
- package/skills/shared/venue-guides/medical_journal_styles.md +535 -0
- package/skills/shared/venue-guides/ml_conference_style.md +556 -0
- package/skills/shared/venue-guides/nature_science_style.md +405 -0
- package/skills/shared/venue-guides/reviewer_expectations.md +417 -0
- package/skills/shared/venue-guides/venue_writing_styles.md +321 -0
- package/skills/split-pdf/SKILL.md +172 -0
- package/skills/split-pdf/methodology.md +48 -0
- package/skills/sync-notion/SKILL.md +93 -0
- package/skills/system-audit/SKILL.md +157 -0
- package/skills/system-audit/references/sub-agent-prompts.md +294 -0
- package/skills/task-management/SKILL.md +131 -0
- package/skills/update-focus/SKILL.md +204 -0
- package/skills/update-project-doc/SKILL.md +194 -0
- package/skills/validate-bib/SKILL.md +242 -0
- package/skills/validate-bib/references/council-mode.md +34 -0
- package/skills/validate-bib/references/deep-verify.md +71 -0
- package/skills/validate-bib/references/openalex-verification.md +45 -0
- package/skills/validate-bib/references/preprint-check.md +31 -0
- package/skills/validate-bib/references/report-template.md +62 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""Validate a skill directory against conventions.
|
|
2
|
+
|
|
3
|
+
Checks frontmatter, naming, description quality, body length,
|
|
4
|
+
referenced files, and allowed_tools format.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
uv run python validate_skill.py <skill-directory> [--strict] [--all]
|
|
8
|
+
|
|
9
|
+
Exit codes: 0 = valid, 1 = errors found, 2 = warnings promoted by --strict.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
import yaml
|
|
18
|
+
except ImportError:
|
|
19
|
+
print("pyyaml not installed. Run: uv pip install pyyaml", file=sys.stderr)
|
|
20
|
+
sys.exit(1)
|
|
21
|
+
|
|
22
|
+
_NAME_PATTERN = re.compile(r"^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$")
|
|
23
|
+
_PLACEHOLDER_PATTERNS = (
|
|
24
|
+
re.compile(r"\bTODO\b", re.IGNORECASE),
|
|
25
|
+
re.compile(r"\bplaceholder\b", re.IGNORECASE),
|
|
26
|
+
re.compile(r"\bFIXME\b", re.IGNORECASE),
|
|
27
|
+
re.compile(r"\bXXX\b"),
|
|
28
|
+
)
|
|
29
|
+
_KNOWN_TOOLS = {
|
|
30
|
+
"Read", "Write", "Edit", "Glob", "Grep", "Bash",
|
|
31
|
+
"AskUserQuestion", "Task", "WebFetch", "WebSearch",
|
|
32
|
+
"NotebookEdit", "EnterPlanMode", "ExitPlanMode", "Skill",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _split_frontmatter(content: str) -> tuple[str, str]:
|
|
37
|
+
"""Split SKILL.md into frontmatter string and body."""
|
|
38
|
+
lines = content.split("\n")
|
|
39
|
+
start = 0
|
|
40
|
+
while start < len(lines) and lines[start].strip() == "":
|
|
41
|
+
start += 1
|
|
42
|
+
if start >= len(lines) or lines[start].strip() != "---":
|
|
43
|
+
return "", ""
|
|
44
|
+
close = None
|
|
45
|
+
for i in range(start + 1, len(lines)):
|
|
46
|
+
if lines[i].strip() == "---":
|
|
47
|
+
close = i
|
|
48
|
+
break
|
|
49
|
+
if close is None:
|
|
50
|
+
return "", ""
|
|
51
|
+
frontmatter_str = "\n".join(lines[start + 1 : close])
|
|
52
|
+
body = "\n".join(lines[close + 1 :]).strip()
|
|
53
|
+
return frontmatter_str, body
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _parse_yaml(frontmatter_str: str) -> dict | None:
|
|
57
|
+
"""Parse YAML frontmatter."""
|
|
58
|
+
try:
|
|
59
|
+
data = yaml.safe_load(frontmatter_str)
|
|
60
|
+
return data if isinstance(data, dict) else None
|
|
61
|
+
except yaml.YAMLError:
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _has_placeholder(text: str) -> bool:
|
|
66
|
+
return any(p.search(text) for p in _PLACEHOLDER_PATTERNS)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _count_body_lines(body: str) -> int:
|
|
70
|
+
return len([line for line in body.split("\n") if line.strip()])
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def validate_skill(path: Path, strict: bool = False) -> tuple[bool, list[str], list[str]]:
|
|
74
|
+
"""Validate a skill directory.
|
|
75
|
+
|
|
76
|
+
Returns (is_valid, errors, warnings).
|
|
77
|
+
"""
|
|
78
|
+
errors: list[str] = []
|
|
79
|
+
warnings: list[str] = []
|
|
80
|
+
skill_dir = Path(path)
|
|
81
|
+
|
|
82
|
+
# --- SKILL.md exists ---
|
|
83
|
+
skill_md = skill_dir / "SKILL.md"
|
|
84
|
+
if not skill_md.exists():
|
|
85
|
+
errors.append("SKILL.md not found.")
|
|
86
|
+
return (False, errors, warnings)
|
|
87
|
+
|
|
88
|
+
content = skill_md.read_text()
|
|
89
|
+
|
|
90
|
+
# --- Valid frontmatter ---
|
|
91
|
+
frontmatter_str, body = _split_frontmatter(content)
|
|
92
|
+
if not frontmatter_str:
|
|
93
|
+
errors.append("Missing or invalid YAML frontmatter (needs --- delimiters).")
|
|
94
|
+
return (False, errors, warnings)
|
|
95
|
+
|
|
96
|
+
data = _parse_yaml(frontmatter_str)
|
|
97
|
+
if data is None:
|
|
98
|
+
errors.append("Frontmatter is not valid YAML.")
|
|
99
|
+
return (False, errors, warnings)
|
|
100
|
+
|
|
101
|
+
# --- Body non-empty ---
|
|
102
|
+
if not body:
|
|
103
|
+
errors.append("Body (system prompt) is empty.")
|
|
104
|
+
|
|
105
|
+
# --- Name present and valid ---
|
|
106
|
+
name = data.get("name")
|
|
107
|
+
if not name or not isinstance(name, str):
|
|
108
|
+
errors.append("Required field 'name' is missing.")
|
|
109
|
+
elif not _NAME_PATTERN.match(name):
|
|
110
|
+
errors.append(f"Invalid name '{name}': must be lowercase alphanumeric + hyphens, 1-64 chars.")
|
|
111
|
+
else:
|
|
112
|
+
if name != skill_dir.name:
|
|
113
|
+
errors.append(f"Name '{name}' does not match directory '{skill_dir.name}'.")
|
|
114
|
+
|
|
115
|
+
if isinstance(name, str) and _has_placeholder(name):
|
|
116
|
+
errors.append(f"Name '{name}' contains placeholder text.")
|
|
117
|
+
|
|
118
|
+
# --- Description present and useful ---
|
|
119
|
+
description = data.get("description")
|
|
120
|
+
if not description or not isinstance(description, str):
|
|
121
|
+
errors.append("Required field 'description' is missing.")
|
|
122
|
+
else:
|
|
123
|
+
desc_clean = description.replace("\n", " ").strip()
|
|
124
|
+
if len(desc_clean) > 1024:
|
|
125
|
+
errors.append(f"Description too long ({len(desc_clean)} chars, max 1024).")
|
|
126
|
+
if _has_placeholder(desc_clean):
|
|
127
|
+
errors.append("Description contains placeholder text.")
|
|
128
|
+
if len(desc_clean) < 20:
|
|
129
|
+
warnings.append(f"Description very short ({len(desc_clean)} chars) — may not trigger correctly.")
|
|
130
|
+
if not any(kw in desc_clean.lower() for kw in ("use when", "use for", "use this", "when ")):
|
|
131
|
+
warnings.append("Description lacks activation hint (e.g., 'Use when...'). May not trigger reliably.")
|
|
132
|
+
|
|
133
|
+
# --- Body quality ---
|
|
134
|
+
if body:
|
|
135
|
+
line_count = _count_body_lines(body)
|
|
136
|
+
if line_count < 5:
|
|
137
|
+
warnings.append(f"Body is very short ({line_count} non-empty lines).")
|
|
138
|
+
if line_count > 300:
|
|
139
|
+
warnings.append(f"Body is long ({line_count} lines). Consider moving detail to references/.")
|
|
140
|
+
|
|
141
|
+
# --- allowed_tools / allowed-tools ---
|
|
142
|
+
tools = data.get("allowed_tools") or data.get("allowed-tools")
|
|
143
|
+
if tools and isinstance(tools, list):
|
|
144
|
+
for tool in tools:
|
|
145
|
+
if isinstance(tool, str):
|
|
146
|
+
# Strip Bash(pattern) to just Bash for the check
|
|
147
|
+
base = tool.split("(")[0]
|
|
148
|
+
if base not in _KNOWN_TOOLS:
|
|
149
|
+
warnings.append(f"Unknown tool '{tool}' in allowed_tools.")
|
|
150
|
+
|
|
151
|
+
# --- Referenced files exist ---
|
|
152
|
+
refs_dir = skill_dir / "references"
|
|
153
|
+
scripts_dir = skill_dir / "scripts"
|
|
154
|
+
# Check for broken relative links in body (strip #anchors before checking)
|
|
155
|
+
for match in re.finditer(r'\[.*?\]\(((?!http)[^)]+)\)', body):
|
|
156
|
+
rel_path = match.group(1)
|
|
157
|
+
file_path_str = rel_path.split("#")[0] # strip fragment
|
|
158
|
+
if not file_path_str or file_path_str.startswith("<"): # skip placeholders like <name>.md
|
|
159
|
+
continue
|
|
160
|
+
full_path = skill_dir / file_path_str
|
|
161
|
+
if not full_path.exists():
|
|
162
|
+
errors.append(f"Broken link: '{rel_path}' — file '{file_path_str}' does not exist.")
|
|
163
|
+
|
|
164
|
+
# Check that references/ and scripts/ dirs, if linked to, actually exist
|
|
165
|
+
# Only flag when there's a markdown link pointing into these dirs
|
|
166
|
+
refs_link = re.search(r'\]\(references/', body)
|
|
167
|
+
scripts_link = re.search(r'\]\(scripts/', body)
|
|
168
|
+
if refs_link and not refs_dir.exists():
|
|
169
|
+
warnings.append("Body links to 'references/' but directory does not exist.")
|
|
170
|
+
if scripts_link and not scripts_dir.exists():
|
|
171
|
+
warnings.append("Body links to 'scripts/' but directory does not exist.")
|
|
172
|
+
|
|
173
|
+
# --- Determine validity ---
|
|
174
|
+
if strict:
|
|
175
|
+
is_valid = len(errors) + len(warnings) == 0
|
|
176
|
+
else:
|
|
177
|
+
is_valid = len(errors) == 0
|
|
178
|
+
|
|
179
|
+
return (is_valid, errors, warnings)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _color(text: str, code: str) -> str:
|
|
183
|
+
return f"\033[{code}m{text}\033[0m"
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def print_report(skill_path: Path, errors: list[str], warnings: list[str], strict: bool) -> None:
|
|
187
|
+
print(f"\n Validating: {skill_path.name}")
|
|
188
|
+
|
|
189
|
+
if not errors and not warnings:
|
|
190
|
+
print(f" {_color('PASS', '32')} All checks passed.\n")
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
for err in errors:
|
|
194
|
+
print(f" {_color('ERROR', '31')} {err}")
|
|
195
|
+
|
|
196
|
+
for warn in warnings:
|
|
197
|
+
label = "ERROR" if strict else "WARN "
|
|
198
|
+
color = "31" if strict else "33"
|
|
199
|
+
print(f" {_color(label, color)} {warn}")
|
|
200
|
+
|
|
201
|
+
total = len(errors) + (len(warnings) if strict else 0)
|
|
202
|
+
if total > 0:
|
|
203
|
+
print(f" {_color(f'{total} issue(s)', '31')}\n")
|
|
204
|
+
else:
|
|
205
|
+
print(f" {_color('PASS', '32')} {len(warnings)} warning(s)\n")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def main() -> int:
|
|
209
|
+
import argparse
|
|
210
|
+
|
|
211
|
+
parser = argparse.ArgumentParser(description="Validate skill directories.")
|
|
212
|
+
parser.add_argument("skill_directory", nargs="?", type=Path, help="Path to skill directory")
|
|
213
|
+
parser.add_argument("--strict", action="store_true", help="Promote warnings to errors")
|
|
214
|
+
parser.add_argument("--all", action="store_true", help="Validate all skills in the parent directory")
|
|
215
|
+
args = parser.parse_args()
|
|
216
|
+
|
|
217
|
+
if args.all:
|
|
218
|
+
# Find skills root: if inside a skill dir, go up; otherwise use cwd
|
|
219
|
+
if args.skill_directory:
|
|
220
|
+
skills_root = args.skill_directory.resolve()
|
|
221
|
+
else:
|
|
222
|
+
skills_root = Path.cwd()
|
|
223
|
+
|
|
224
|
+
total_errors = 0
|
|
225
|
+
total_warnings = 0
|
|
226
|
+
total_pass = 0
|
|
227
|
+
total_fail = 0
|
|
228
|
+
|
|
229
|
+
for child in sorted(skills_root.iterdir()):
|
|
230
|
+
if not child.is_dir() or child.name == "shared" or child.name.startswith("."):
|
|
231
|
+
continue
|
|
232
|
+
skill_md = child / "SKILL.md"
|
|
233
|
+
if not skill_md.exists():
|
|
234
|
+
continue
|
|
235
|
+
|
|
236
|
+
is_valid, errors, warnings = validate_skill(child, strict=args.strict)
|
|
237
|
+
print_report(child, errors, warnings, strict=args.strict)
|
|
238
|
+
total_errors += len(errors)
|
|
239
|
+
total_warnings += len(warnings)
|
|
240
|
+
if is_valid:
|
|
241
|
+
total_pass += 1
|
|
242
|
+
else:
|
|
243
|
+
total_fail += 1
|
|
244
|
+
|
|
245
|
+
print(f" Summary: {total_pass} passed, {total_fail} failed, {total_warnings} warnings\n")
|
|
246
|
+
return 1 if total_fail > 0 else 0
|
|
247
|
+
|
|
248
|
+
if not args.skill_directory:
|
|
249
|
+
parser.error("skill_directory is required unless --all is used")
|
|
250
|
+
|
|
251
|
+
skill_path = args.skill_directory.resolve()
|
|
252
|
+
if not skill_path.is_dir():
|
|
253
|
+
print(f"Error: '{skill_path}' is not a directory.", file=sys.stderr)
|
|
254
|
+
return 1
|
|
255
|
+
|
|
256
|
+
is_valid, errors, warnings = validate_skill(skill_path, strict=args.strict)
|
|
257
|
+
print_report(skill_path, errors, warnings, strict=args.strict)
|
|
258
|
+
|
|
259
|
+
if not is_valid:
|
|
260
|
+
return 2 if (not errors and args.strict) else 1
|
|
261
|
+
return 0
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
if __name__ == "__main__":
|
|
265
|
+
sys.exit(main())
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lessons-learned
|
|
3
|
+
description: "Use when you need a structured post-mortem after incidents, mistakes, or stuck sessions."
|
|
4
|
+
allowed_tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Write
|
|
7
|
+
- Edit
|
|
8
|
+
- Glob
|
|
9
|
+
- Grep
|
|
10
|
+
- AskUserQuestion
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Lessons Learned: Structured Retrospective
|
|
14
|
+
|
|
15
|
+
Analyse incidents using a structured framework, identify root causes, and encode preventive measures directly into skills, guards, or documentation. The goal is systematic improvement, not blame.
|
|
16
|
+
|
|
17
|
+
## When to Use
|
|
18
|
+
|
|
19
|
+
- After incidents, mistakes, rollbacks, or near-misses
|
|
20
|
+
- When the user says "what went wrong", "lessons learned", "post-mortem", "retrospective", or "how do we prevent this"
|
|
21
|
+
- After a stuck session where significant time was lost
|
|
22
|
+
- After a wrong-approach event (plan existed but execution diverged)
|
|
23
|
+
|
|
24
|
+
## Process
|
|
25
|
+
|
|
26
|
+
### Phase 1: Incident Definition
|
|
27
|
+
|
|
28
|
+
Capture the facts first, analysis later.
|
|
29
|
+
|
|
30
|
+
```markdown
|
|
31
|
+
## Incident Summary
|
|
32
|
+
|
|
33
|
+
**What happened:** [Factual description]
|
|
34
|
+
**When:** [Date/time]
|
|
35
|
+
**Impact:** [What was affected, scope]
|
|
36
|
+
**Resolution:** [How it was fixed/rolled back]
|
|
37
|
+
**Time to resolution:** [How long to fix]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Phase 2: Timeline Reconstruction
|
|
41
|
+
|
|
42
|
+
Build a chronological sequence of events:
|
|
43
|
+
|
|
44
|
+
| Time | Action | Actor | Outcome |
|
|
45
|
+
|------|--------|-------|---------|
|
|
46
|
+
| HH:MM | [What was done] | [Claude/User] | [Result] |
|
|
47
|
+
|
|
48
|
+
Key questions:
|
|
49
|
+
- What was the trigger?
|
|
50
|
+
- Where did the sequence diverge from expected?
|
|
51
|
+
- What was the point of no return?
|
|
52
|
+
|
|
53
|
+
### Phase 3: Root Cause Analysis (5 Whys)
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
1. Why did [incident] happen?
|
|
57
|
+
→ Because [immediate cause]
|
|
58
|
+
|
|
59
|
+
2. Why did [immediate cause] happen?
|
|
60
|
+
→ Because [deeper cause]
|
|
61
|
+
|
|
62
|
+
3. Why did [deeper cause] happen?
|
|
63
|
+
→ Because [systemic issue]
|
|
64
|
+
|
|
65
|
+
4. Why did [systemic issue] exist?
|
|
66
|
+
→ Because [process gap]
|
|
67
|
+
|
|
68
|
+
5. Why did [process gap] exist?
|
|
69
|
+
→ Because [root cause]
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Stop when you reach a cause that can be addressed by a concrete change to the system (skill, rule, hook, doc).
|
|
73
|
+
|
|
74
|
+
### Phase 4: Contributing Factors
|
|
75
|
+
|
|
76
|
+
Identify all factors, not just the root cause:
|
|
77
|
+
|
|
78
|
+
| Category | Factor | Contribution |
|
|
79
|
+
|----------|--------|--------------|
|
|
80
|
+
| **Process** | Missing checkpoint, unclear workflow | [How it contributed] |
|
|
81
|
+
| **Communication** | Ambiguous instructions, assumed consent | [How it contributed] |
|
|
82
|
+
| **Technical** | Missing guard, no validation | [How it contributed] |
|
|
83
|
+
| **Context** | Session continuation, prior assumptions | [How it contributed] |
|
|
84
|
+
| **Human** | Fatigue, time pressure, overconfidence | [How it contributed] |
|
|
85
|
+
|
|
86
|
+
### Phase 5: Fix Classification
|
|
87
|
+
|
|
88
|
+
Classify each fix by type:
|
|
89
|
+
|
|
90
|
+
| Fix Type | When to Use | How to Encode |
|
|
91
|
+
|----------|-------------|---------------|
|
|
92
|
+
| **Skill** | Recurring workflow needs structure | Create SKILL.md via `/learn` |
|
|
93
|
+
| **Guard** | Action requires mandatory checkpoint | Add approval gate to existing skill |
|
|
94
|
+
| **Rule** | Behavioural constraint needed globally | Create `.claude/rules/*.md` |
|
|
95
|
+
| **Documentation** | Knowledge gap caused the issue | Update CLAUDE.md, MEMORY.md, or docs/ |
|
|
96
|
+
| **Hook** | Manual step was forgotten | Create script in hooks/ |
|
|
97
|
+
| **Checklist** | Multiple steps need verification | Add to existing skill |
|
|
98
|
+
|
|
99
|
+
### Phase 6: Fix Implementation
|
|
100
|
+
|
|
101
|
+
**Implement fixes during the retrospective, not after.** This is the critical difference from a report-only post-mortem.
|
|
102
|
+
|
|
103
|
+
For each fix:
|
|
104
|
+
1. Implement it (create/edit the file)
|
|
105
|
+
2. Record what was done:
|
|
106
|
+
|
|
107
|
+
| Fix | Type | Location | Status |
|
|
108
|
+
|-----|------|----------|--------|
|
|
109
|
+
| [Description] | Skill/Guard/Rule/Doc/Hook | [File path] | Created/Updated |
|
|
110
|
+
|
|
111
|
+
Also record a `[LEARN]` tag in MEMORY.md for each key correction (per the `learn-tags` rule).
|
|
112
|
+
|
|
113
|
+
### Phase 7: Verification
|
|
114
|
+
|
|
115
|
+
Define how to verify the fix works:
|
|
116
|
+
|
|
117
|
+
```markdown
|
|
118
|
+
## Verification
|
|
119
|
+
|
|
120
|
+
**Test scenario:** [How to test the fix]
|
|
121
|
+
**Success criteria:** [What "fixed" looks like]
|
|
122
|
+
**Review date:** [When to check if fix is working — default: 2 weeks]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Output
|
|
126
|
+
|
|
127
|
+
Write the report to `log/incidents/YYYY-MM-DD_short-description.md`:
|
|
128
|
+
|
|
129
|
+
```markdown
|
|
130
|
+
# Lessons Learned: [Incident Title]
|
|
131
|
+
|
|
132
|
+
**Date:** YYYY-MM-DD
|
|
133
|
+
**Severity:** [Low|Medium|High|Critical]
|
|
134
|
+
**Status:** [Resolved|Monitoring|Open]
|
|
135
|
+
|
|
136
|
+
## Incident Summary
|
|
137
|
+
[Brief description]
|
|
138
|
+
|
|
139
|
+
## Timeline
|
|
140
|
+
| Time | Action | Actor | Outcome |
|
|
141
|
+
|------|--------|-------|---------|
|
|
142
|
+
|
|
143
|
+
## Root Cause
|
|
144
|
+
[The fundamental issue]
|
|
145
|
+
|
|
146
|
+
## Contributing Factors
|
|
147
|
+
- [Factor 1]
|
|
148
|
+
- [Factor 2]
|
|
149
|
+
|
|
150
|
+
## Fixes Implemented
|
|
151
|
+
| Fix | Type | Location | Status |
|
|
152
|
+
|-----|------|----------|--------|
|
|
153
|
+
|
|
154
|
+
## Prevention
|
|
155
|
+
[How this prevents recurrence]
|
|
156
|
+
|
|
157
|
+
## Lessons
|
|
158
|
+
1. [Key takeaway 1]
|
|
159
|
+
2. [Key takeaway 2]
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Create `log/incidents/` if it doesn't exist.
|
|
163
|
+
|
|
164
|
+
## Common Incident Patterns
|
|
165
|
+
|
|
166
|
+
| Pattern | Symptom | Root Cause | Typical Fix |
|
|
167
|
+
|---------|---------|------------|-------------|
|
|
168
|
+
| Premature action | Action taken before approval | Implied consent ≠ explicit | Add approval gate to skill |
|
|
169
|
+
| Sequence error | Steps in wrong order | Missing dependency chain | Encode sequence in skill |
|
|
170
|
+
| Missing validation | Bad data passed through | No checkpoint | Add pre-flight check |
|
|
171
|
+
| Context carryover | Stale assumptions from prior session | State assumed to persist | Explicit context verification |
|
|
172
|
+
| Scope creep | Did more than requested | Task scope too broad | Clarifying questions first |
|
|
173
|
+
| Planning loop | Re-planned instead of executing | Perfectionism / uncertainty | Execution stall detector |
|
|
174
|
+
|
|
175
|
+
## Anti-Patterns
|
|
176
|
+
|
|
177
|
+
| Anti-Pattern | Problem | Instead |
|
|
178
|
+
|--------------|---------|---------|
|
|
179
|
+
| Blame assignment | Creates defensiveness | Focus on process, not people |
|
|
180
|
+
| Single-cause thinking | Oversimplifies | Use 5 Whys, multiple factors |
|
|
181
|
+
| Recommend without acting | Lessons forgotten, recurs | Implement fixes during retro |
|
|
182
|
+
| Vague fixes ("be more careful") | Not verifiable | Encode specific changes |
|
|
183
|
+
| Skip verification | No way to know if fix worked | Define success criteria |
|
|
184
|
+
|
|
185
|
+
## Cross-References
|
|
186
|
+
|
|
187
|
+
- **`[LEARN]` tags** — record one-liner corrections in MEMORY.md (the quick complement to this skill)
|
|
188
|
+
- **`/learn`** — extract a full skill from a session (when the fix type is "Skill")
|
|
189
|
+
- **`/ideas`** — if a fix is too large for this session, capture as an idea for later
|
|
190
|
+
|
|
191
|
+
## Success Criteria
|
|
192
|
+
|
|
193
|
+
The retrospective is complete when:
|
|
194
|
+
- [ ] Incident clearly defined with timeline
|
|
195
|
+
- [ ] Root cause identified (not just symptoms)
|
|
196
|
+
- [ ] Contributing factors documented
|
|
197
|
+
- [ ] At least one fix implemented (not just recommended)
|
|
198
|
+
- [ ] Fix encoded in appropriate location (skill, rule, hook, doc)
|
|
199
|
+
- [ ] `[LEARN]` tags recorded in MEMORY.md
|
|
200
|
+
- [ ] Verification criteria defined
|
|
201
|
+
- [ ] Report written to `log/incidents/`
|