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.
Files changed (285) hide show
  1. package/.claude/agents/domain-reviewer.md +336 -0
  2. package/.claude/agents/fixer.md +226 -0
  3. package/.claude/agents/paper-critic.md +370 -0
  4. package/.claude/agents/peer-reviewer.md +289 -0
  5. package/.claude/agents/proposal-reviewer.md +215 -0
  6. package/.claude/agents/referee2-reviewer.md +367 -0
  7. package/.claude/agents/references/journal-referee-profiles.md +354 -0
  8. package/.claude/agents/references/paper-critic/council-personas.md +77 -0
  9. package/.claude/agents/references/paper-critic/council-prompts.md +198 -0
  10. package/.claude/agents/references/peer-reviewer/report-template.md +199 -0
  11. package/.claude/agents/references/peer-reviewer/sa-prompts.md +260 -0
  12. package/.claude/agents/references/peer-reviewer/security-scan.md +188 -0
  13. package/.claude/agents/references/proposal-reviewer/report-template.md +144 -0
  14. package/.claude/agents/references/proposal-reviewer/sa-prompts.md +149 -0
  15. package/.claude/agents/references/referee-config.md +114 -0
  16. package/.claude/agents/references/referee2-reviewer/audit-checklists.md +287 -0
  17. package/.claude/agents/references/referee2-reviewer/report-template.md +334 -0
  18. package/.claude/rules/design-before-results.md +52 -0
  19. package/.claude/rules/ignore-agents-md.md +17 -0
  20. package/.claude/rules/ignore-gemini-md.md +17 -0
  21. package/.claude/rules/lean-claude-md.md +45 -0
  22. package/.claude/rules/learn-tags.md +99 -0
  23. package/.claude/rules/overleaf-separation.md +67 -0
  24. package/.claude/rules/plan-first.md +175 -0
  25. package/.claude/rules/read-docs-first.md +50 -0
  26. package/.claude/rules/scope-discipline.md +28 -0
  27. package/.claude/settings.json +125 -0
  28. package/.context/current-focus.md +33 -0
  29. package/.context/preferences/priorities.md +36 -0
  30. package/.context/preferences/task-naming.md +28 -0
  31. package/.context/profile.md +29 -0
  32. package/.context/projects/_index.md +41 -0
  33. package/.context/projects/papers/nudge-exp.md +22 -0
  34. package/.context/projects/papers/uncertainty.md +31 -0
  35. package/.context/resources/claude-scientific-writer-review.md +48 -0
  36. package/.context/resources/cunningham-multi-analyst-agents.md +104 -0
  37. package/.context/resources/cunningham-multilang-code-audit.md +62 -0
  38. package/.context/resources/google-ai-co-scientist-review.md +72 -0
  39. package/.context/resources/karpathy-llm-council-review.md +58 -0
  40. package/.context/resources/multi-coder-reliability-protocol.md +175 -0
  41. package/.context/resources/pedro-santanna-takeaways.md +96 -0
  42. package/.context/resources/venue-rankings/abs_ajg_2024.csv +1823 -0
  43. package/.context/resources/venue-rankings/abs_ajg_2024_econ.csv +356 -0
  44. package/.context/resources/venue-rankings/cabs_4_4star_theory.csv +40 -0
  45. package/.context/resources/venue-rankings/core_2026.csv +801 -0
  46. package/.context/resources/venue-rankings.md +147 -0
  47. package/.context/workflows/README.md +69 -0
  48. package/.context/workflows/daily-review.md +91 -0
  49. package/.context/workflows/meeting-actions.md +108 -0
  50. package/.context/workflows/replication-protocol.md +155 -0
  51. package/.context/workflows/weekly-review.md +113 -0
  52. package/.mcp-server-biblio/formatters.py +158 -0
  53. package/.mcp-server-biblio/pyproject.toml +11 -0
  54. package/.mcp-server-biblio/server.py +678 -0
  55. package/.mcp-server-biblio/sources/__init__.py +14 -0
  56. package/.mcp-server-biblio/sources/base.py +73 -0
  57. package/.mcp-server-biblio/sources/formatters.py +83 -0
  58. package/.mcp-server-biblio/sources/models.py +22 -0
  59. package/.mcp-server-biblio/sources/multi_source.py +243 -0
  60. package/.mcp-server-biblio/sources/openalex_source.py +183 -0
  61. package/.mcp-server-biblio/sources/scopus_source.py +309 -0
  62. package/.mcp-server-biblio/sources/wos_source.py +508 -0
  63. package/.mcp-server-biblio/uv.lock +896 -0
  64. package/.scripts/README.md +161 -0
  65. package/.scripts/ai_pattern_density.py +446 -0
  66. package/.scripts/conf +445 -0
  67. package/.scripts/config.py +122 -0
  68. package/.scripts/count_inventory.py +275 -0
  69. package/.scripts/daily_digest.py +288 -0
  70. package/.scripts/done +177 -0
  71. package/.scripts/extract_meeting_actions.py +223 -0
  72. package/.scripts/focus +176 -0
  73. package/.scripts/generate-codex-agents-md.py +217 -0
  74. package/.scripts/inbox +194 -0
  75. package/.scripts/notion_helpers.py +325 -0
  76. package/.scripts/openalex/query_helpers.py +306 -0
  77. package/.scripts/papers +227 -0
  78. package/.scripts/query +223 -0
  79. package/.scripts/session-history.py +201 -0
  80. package/.scripts/skill-health.py +516 -0
  81. package/.scripts/skill-log-miner.py +273 -0
  82. package/.scripts/sync-to-codex.sh +252 -0
  83. package/.scripts/task +213 -0
  84. package/.scripts/tasks +190 -0
  85. package/.scripts/week +206 -0
  86. package/CLAUDE.md +197 -0
  87. package/LICENSE +21 -0
  88. package/MEMORY.md +38 -0
  89. package/README.md +269 -0
  90. package/docs/agents.md +44 -0
  91. package/docs/bibliography-setup.md +55 -0
  92. package/docs/council-mode.md +36 -0
  93. package/docs/getting-started.md +245 -0
  94. package/docs/hooks.md +38 -0
  95. package/docs/mcp-servers.md +82 -0
  96. package/docs/notion-setup.md +109 -0
  97. package/docs/rules.md +33 -0
  98. package/docs/scripts.md +303 -0
  99. package/docs/setup-overview/setup-overview.pdf +0 -0
  100. package/docs/skills.md +70 -0
  101. package/docs/system.md +159 -0
  102. package/hooks/block-destructive-git.sh +66 -0
  103. package/hooks/context-monitor.py +114 -0
  104. package/hooks/postcompact-restore.py +157 -0
  105. package/hooks/precompact-autosave.py +181 -0
  106. package/hooks/promise-checker.sh +124 -0
  107. package/hooks/protect-source-files.sh +81 -0
  108. package/hooks/resume-context-loader.sh +53 -0
  109. package/hooks/startup-context-loader.sh +102 -0
  110. package/package.json +51 -0
  111. package/packages/cli-council/.github/workflows/claude-code-review.yml +44 -0
  112. package/packages/cli-council/.github/workflows/claude.yml +50 -0
  113. package/packages/cli-council/README.md +100 -0
  114. package/packages/cli-council/pyproject.toml +43 -0
  115. package/packages/cli-council/src/cli_council/__init__.py +19 -0
  116. package/packages/cli-council/src/cli_council/__main__.py +185 -0
  117. package/packages/cli-council/src/cli_council/backends/__init__.py +8 -0
  118. package/packages/cli-council/src/cli_council/backends/base.py +81 -0
  119. package/packages/cli-council/src/cli_council/backends/claude.py +25 -0
  120. package/packages/cli-council/src/cli_council/backends/codex.py +27 -0
  121. package/packages/cli-council/src/cli_council/backends/gemini.py +26 -0
  122. package/packages/cli-council/src/cli_council/checkpoint.py +212 -0
  123. package/packages/cli-council/src/cli_council/config.py +51 -0
  124. package/packages/cli-council/src/cli_council/council.py +391 -0
  125. package/packages/cli-council/src/cli_council/models.py +46 -0
  126. package/packages/llm-council/.github/workflows/claude-code-review.yml +44 -0
  127. package/packages/llm-council/.github/workflows/claude.yml +50 -0
  128. package/packages/llm-council/README.md +453 -0
  129. package/packages/llm-council/pyproject.toml +42 -0
  130. package/packages/llm-council/src/llm_council/__init__.py +23 -0
  131. package/packages/llm-council/src/llm_council/__main__.py +259 -0
  132. package/packages/llm-council/src/llm_council/checkpoint.py +193 -0
  133. package/packages/llm-council/src/llm_council/client.py +253 -0
  134. package/packages/llm-council/src/llm_council/config.py +232 -0
  135. package/packages/llm-council/src/llm_council/council.py +482 -0
  136. package/packages/llm-council/src/llm_council/models.py +46 -0
  137. package/packages/mcp-bibliography/MEMORY.md +31 -0
  138. package/packages/mcp-bibliography/_app.py +226 -0
  139. package/packages/mcp-bibliography/formatters.py +158 -0
  140. package/packages/mcp-bibliography/log/2026-03-13-2100.md +35 -0
  141. package/packages/mcp-bibliography/pyproject.toml +15 -0
  142. package/packages/mcp-bibliography/run.sh +20 -0
  143. package/packages/mcp-bibliography/scholarly_formatters.py +83 -0
  144. package/packages/mcp-bibliography/server.py +1857 -0
  145. package/packages/mcp-bibliography/tools/__init__.py +28 -0
  146. package/packages/mcp-bibliography/tools/_registry.py +19 -0
  147. package/packages/mcp-bibliography/tools/altmetric.py +107 -0
  148. package/packages/mcp-bibliography/tools/core.py +92 -0
  149. package/packages/mcp-bibliography/tools/dblp.py +52 -0
  150. package/packages/mcp-bibliography/tools/openalex.py +296 -0
  151. package/packages/mcp-bibliography/tools/opencitations.py +102 -0
  152. package/packages/mcp-bibliography/tools/openreview.py +179 -0
  153. package/packages/mcp-bibliography/tools/orcid.py +131 -0
  154. package/packages/mcp-bibliography/tools/scholarly.py +575 -0
  155. package/packages/mcp-bibliography/tools/unpaywall.py +63 -0
  156. package/packages/mcp-bibliography/tools/zenodo.py +123 -0
  157. package/packages/mcp-bibliography/uv.lock +711 -0
  158. package/scripts/setup.sh +143 -0
  159. package/skills/beamer-deck/SKILL.md +199 -0
  160. package/skills/beamer-deck/references/quality-rubric.md +54 -0
  161. package/skills/beamer-deck/references/review-prompts.md +106 -0
  162. package/skills/bib-validate/SKILL.md +261 -0
  163. package/skills/bib-validate/references/council-mode.md +34 -0
  164. package/skills/bib-validate/references/deep-verify.md +79 -0
  165. package/skills/bib-validate/references/fix-mode.md +36 -0
  166. package/skills/bib-validate/references/openalex-verification.md +45 -0
  167. package/skills/bib-validate/references/preprint-check.md +31 -0
  168. package/skills/bib-validate/references/ref-manager-crossref.md +41 -0
  169. package/skills/bib-validate/references/report-template.md +82 -0
  170. package/skills/code-archaeology/SKILL.md +141 -0
  171. package/skills/code-review/SKILL.md +265 -0
  172. package/skills/code-review/references/quality-rubric.md +67 -0
  173. package/skills/consolidate-memory/SKILL.md +208 -0
  174. package/skills/context-status/SKILL.md +126 -0
  175. package/skills/creation-guard/SKILL.md +230 -0
  176. package/skills/devils-advocate/SKILL.md +130 -0
  177. package/skills/devils-advocate/references/competing-hypotheses.md +83 -0
  178. package/skills/init-project/SKILL.md +115 -0
  179. package/skills/init-project-course/references/memory-and-settings.md +92 -0
  180. package/skills/init-project-course/references/organise-templates.md +94 -0
  181. package/skills/init-project-course/skill.md +147 -0
  182. package/skills/init-project-light/skill.md +139 -0
  183. package/skills/init-project-research/SKILL.md +368 -0
  184. package/skills/init-project-research/references/atlas-pipeline-sync.md +70 -0
  185. package/skills/init-project-research/references/atlas-schema.md +81 -0
  186. package/skills/init-project-research/references/confirmation-report.md +39 -0
  187. package/skills/init-project-research/references/domain-profile-template.md +104 -0
  188. package/skills/init-project-research/references/interview-round3.md +34 -0
  189. package/skills/init-project-research/references/literature-discovery.md +43 -0
  190. package/skills/init-project-research/references/scaffold-details.md +197 -0
  191. package/skills/init-project-research/templates/field-calibration.md +60 -0
  192. package/skills/init-project-research/templates/pipeline-manifest.md +63 -0
  193. package/skills/init-project-research/templates/run-all.sh +116 -0
  194. package/skills/init-project-research/templates/seed-files.md +337 -0
  195. package/skills/insights-deck/SKILL.md +151 -0
  196. package/skills/interview-me/SKILL.md +157 -0
  197. package/skills/latex/SKILL.md +141 -0
  198. package/skills/latex/references/latex-configs.md +183 -0
  199. package/skills/latex-autofix/SKILL.md +230 -0
  200. package/skills/latex-autofix/references/known-errors.md +183 -0
  201. package/skills/latex-autofix/references/quality-rubric.md +50 -0
  202. package/skills/latex-health-check/SKILL.md +161 -0
  203. package/skills/learn/SKILL.md +220 -0
  204. package/skills/learn/scripts/validate_skill.py +265 -0
  205. package/skills/lessons-learned/SKILL.md +201 -0
  206. package/skills/literature/SKILL.md +335 -0
  207. package/skills/literature/references/agent-templates.md +393 -0
  208. package/skills/literature/references/bibliometric-apis.md +44 -0
  209. package/skills/literature/references/cli-council-search.md +79 -0
  210. package/skills/literature/references/openalex-api-guide.md +371 -0
  211. package/skills/literature/references/openalex-common-queries.md +381 -0
  212. package/skills/literature/references/openalex-workflows.md +248 -0
  213. package/skills/literature/references/reference-manager-sync.md +36 -0
  214. package/skills/literature/references/scopus-api-guide.md +208 -0
  215. package/skills/literature/references/wos-api-guide.md +308 -0
  216. package/skills/multi-perspective/SKILL.md +311 -0
  217. package/skills/multi-perspective/references/computational-many-analysts.md +77 -0
  218. package/skills/pipeline-manifest/SKILL.md +226 -0
  219. package/skills/pre-submission-report/SKILL.md +153 -0
  220. package/skills/process-reviews/SKILL.md +244 -0
  221. package/skills/process-reviews/references/rr-routing.md +101 -0
  222. package/skills/project-deck/SKILL.md +87 -0
  223. package/skills/project-safety/SKILL.md +135 -0
  224. package/skills/proofread/SKILL.md +254 -0
  225. package/skills/proofread/references/quality-rubric.md +104 -0
  226. package/skills/python-env/SKILL.md +57 -0
  227. package/skills/quarto-deck/SKILL.md +226 -0
  228. package/skills/quarto-deck/references/markdown-format.md +143 -0
  229. package/skills/quarto-deck/references/quality-rubric.md +54 -0
  230. package/skills/save-context/SKILL.md +174 -0
  231. package/skills/session-log/SKILL.md +98 -0
  232. package/skills/shared/concept-validation-gate.md +161 -0
  233. package/skills/shared/council-protocol.md +265 -0
  234. package/skills/shared/distribution-diagnostics.md +164 -0
  235. package/skills/shared/engagement-stratified-sampling.md +218 -0
  236. package/skills/shared/escalation-protocol.md +74 -0
  237. package/skills/shared/external-audit-protocol.md +205 -0
  238. package/skills/shared/intercoder-reliability.md +256 -0
  239. package/skills/shared/mcp-degradation.md +81 -0
  240. package/skills/shared/method-probing-questions.md +163 -0
  241. package/skills/shared/multi-language-conventions.md +143 -0
  242. package/skills/shared/paid-api-safety.md +174 -0
  243. package/skills/shared/palettes.md +90 -0
  244. package/skills/shared/progressive-disclosure.md +92 -0
  245. package/skills/shared/project-documentation-content.md +443 -0
  246. package/skills/shared/project-documentation-format.md +281 -0
  247. package/skills/shared/project-documentation.md +100 -0
  248. package/skills/shared/publication-output.md +138 -0
  249. package/skills/shared/quality-scoring.md +70 -0
  250. package/skills/shared/reference-resolution.md +77 -0
  251. package/skills/shared/research-quality-rubric.md +165 -0
  252. package/skills/shared/rhetoric-principles.md +54 -0
  253. package/skills/shared/skill-design-patterns.md +272 -0
  254. package/skills/shared/skill-index.md +240 -0
  255. package/skills/shared/system-documentation.md +334 -0
  256. package/skills/shared/tikz-rules.md +402 -0
  257. package/skills/shared/validation-tiers.md +121 -0
  258. package/skills/shared/venue-guides/README.md +46 -0
  259. package/skills/shared/venue-guides/cell_press_style.md +483 -0
  260. package/skills/shared/venue-guides/conferences_formatting.md +564 -0
  261. package/skills/shared/venue-guides/cs_conference_style.md +463 -0
  262. package/skills/shared/venue-guides/examples/cell_summary_example.md +247 -0
  263. package/skills/shared/venue-guides/examples/medical_structured_abstract.md +313 -0
  264. package/skills/shared/venue-guides/examples/nature_abstract_examples.md +213 -0
  265. package/skills/shared/venue-guides/examples/neurips_introduction_example.md +245 -0
  266. package/skills/shared/venue-guides/journals_formatting.md +486 -0
  267. package/skills/shared/venue-guides/medical_journal_styles.md +535 -0
  268. package/skills/shared/venue-guides/ml_conference_style.md +556 -0
  269. package/skills/shared/venue-guides/nature_science_style.md +405 -0
  270. package/skills/shared/venue-guides/reviewer_expectations.md +417 -0
  271. package/skills/shared/venue-guides/venue_writing_styles.md +321 -0
  272. package/skills/split-pdf/SKILL.md +172 -0
  273. package/skills/split-pdf/methodology.md +48 -0
  274. package/skills/sync-notion/SKILL.md +93 -0
  275. package/skills/system-audit/SKILL.md +157 -0
  276. package/skills/system-audit/references/sub-agent-prompts.md +294 -0
  277. package/skills/task-management/SKILL.md +131 -0
  278. package/skills/update-focus/SKILL.md +204 -0
  279. package/skills/update-project-doc/SKILL.md +194 -0
  280. package/skills/validate-bib/SKILL.md +242 -0
  281. package/skills/validate-bib/references/council-mode.md +34 -0
  282. package/skills/validate-bib/references/deep-verify.md +71 -0
  283. package/skills/validate-bib/references/openalex-verification.md +45 -0
  284. package/skills/validate-bib/references/preprint-check.md +31 -0
  285. package/skills/validate-bib/references/report-template.md +62 -0
@@ -0,0 +1,273 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Skill Log Miner — Extracts skill invocation data from session logs.
4
+
5
+ Fallback observation method when the Skill tool doesn't emit hook events.
6
+ Scans log/*.md files for `/skill-name` references, extracts structured
7
+ invocation records, and writes to ~/.claude/ecc/observations-*.jsonl.
8
+
9
+ Idempotent: tracks which log files have been mined in a watermark file.
10
+ Re-running only processes new logs.
11
+
12
+ Usage:
13
+ uv run python .scripts/skill-log-miner.py # mine new logs
14
+ uv run python .scripts/skill-log-miner.py --backfill # re-mine all logs
15
+ uv run python .scripts/skill-log-miner.py --dry-run # show what would be mined
16
+ """
17
+
18
+ import argparse
19
+ import hashlib
20
+ import json
21
+ import os
22
+ import re
23
+ import sys
24
+ from datetime import datetime, timezone
25
+ from pathlib import Path
26
+
27
+ # ── Config ──────────────────────────────────────────────────────────────────
28
+ ECC_DIR = Path.home() / ".claude" / "ecc"
29
+ WATERMARK_FILE = ECC_DIR / "miner-watermark.json"
30
+
31
+ # Where to find session logs — resolve from config
32
+ def get_task_mgmt() -> Path | None:
33
+ cfg = Path.home() / ".config" / "task-mgmt" / "path"
34
+ if cfg.exists():
35
+ p = Path(cfg.read_text().strip())
36
+ if p.is_dir():
37
+ return p
38
+ return None
39
+
40
+ # Also check project-specific log directories
41
+ def find_log_dirs() -> list[Path]:
42
+ dirs = []
43
+ tm = get_task_mgmt()
44
+ if tm:
45
+ log_dir = tm / "log"
46
+ if log_dir.is_dir():
47
+ dirs.append(log_dir)
48
+ return dirs
49
+
50
+ # Skill name pattern: `/skill-name` in backticks or at line start
51
+ # Matches: `/proofread`, `/latex-autofix`, `/bib-validate`
52
+ # Excludes: `/path/to/file`, `/dev/null`, `/usr/bin`, `/tmp/`
53
+ SKILL_PATTERN = re.compile(
54
+ r'`/([a-z][a-z0-9-]+)`' # backtick-quoted: `/proofread`
55
+ r'|(?:^|\s)/([a-z][a-z0-9-]+)' # bare: /proofread at line start or after space
56
+ , re.MULTILINE
57
+ )
58
+
59
+ # Known non-skill patterns to exclude
60
+ NON_SKILLS = {
61
+ "dev", "tmp", "usr", "bin", "etc", "var", "opt", "home",
62
+ "Users", "Library", "Applications", "System", "Volumes",
63
+ "clear", "help", "compact", "cost", "doctor", "init",
64
+ "login", "logout", "status", "version", "model",
65
+ }
66
+
67
+ # Date pattern from log filenames: YYYY-MM-DD-HHMM.md or YYYY-MM-DD-HHMM-compact.md
68
+ LOG_DATE_RE = re.compile(r"(\d{4}-\d{2}-\d{2})-(\d{2})(\d{2})")
69
+
70
+ # Project header in session logs
71
+ PROJECT_RE = re.compile(r"^## Project:\s*(.+)", re.MULTILINE)
72
+
73
+
74
+ def sha256_prefix(value: str, length: int = 12) -> str:
75
+ return hashlib.sha256(value.encode()).hexdigest()[:length]
76
+
77
+
78
+ def load_known_skills() -> set[str]:
79
+ """Load valid skill names from ~/.claude/skills/."""
80
+ skills_dir = Path.home() / ".claude" / "skills"
81
+ if not skills_dir.is_dir():
82
+ return set()
83
+ return {d.name for d in skills_dir.iterdir() if d.is_dir() and (d / "SKILL.md").exists()}
84
+
85
+
86
+ def extract_skills_from_log(filepath: Path, known_skills: set[str]) -> list[dict]:
87
+ """Extract skill invocations from a session log file."""
88
+ try:
89
+ content = filepath.read_text(encoding="utf-8", errors="replace")
90
+ except OSError:
91
+ return []
92
+
93
+ # Skip compact logs (auto-generated, minimal skill data)
94
+ if "-compact" in filepath.name:
95
+ return []
96
+
97
+ # Extract date from filename
98
+ date_match = LOG_DATE_RE.search(filepath.stem)
99
+ if not date_match:
100
+ return []
101
+
102
+ date_str = date_match.group(1)
103
+ hour = date_match.group(2)
104
+ minute = date_match.group(3)
105
+ try:
106
+ log_date = datetime.strptime(f"{date_str}T{hour}:{minute}:00", "%Y-%m-%dT%H:%M:%S")
107
+ log_date = log_date.replace(tzinfo=timezone.utc)
108
+ except ValueError:
109
+ return []
110
+
111
+ # Extract project name
112
+ project_match = PROJECT_RE.search(content)
113
+ project_label = project_match.group(1).strip() if project_match else "unknown"
114
+
115
+ # Find all skill references
116
+ found_skills: dict[str, int] = {}
117
+ for match in SKILL_PATTERN.finditer(content):
118
+ skill_name = match.group(1) or match.group(2)
119
+ if not skill_name:
120
+ continue
121
+
122
+ # Filter out non-skills
123
+ if skill_name in NON_SKILLS:
124
+ continue
125
+ if len(skill_name) < 2:
126
+ continue
127
+
128
+ # Prefer known skills, but also accept plausible names
129
+ # (handles renamed/deleted skills in historical logs)
130
+ if skill_name in known_skills or len(skill_name) >= 3:
131
+ found_skills[skill_name] = found_skills.get(skill_name, 0) + 1
132
+
133
+ # Create events
134
+ events = []
135
+ for skill_name, mention_count in found_skills.items():
136
+ # Validate against known skills (strict mode for non-obvious names)
137
+ if skill_name not in known_skills and len(skill_name) < 5:
138
+ continue
139
+
140
+ event = {
141
+ "schema": "skill-event.v1",
142
+ "phase": "mined",
143
+ "skill": skill_name,
144
+ "session_hash": sha256_prefix(filepath.name),
145
+ "timestamp": log_date.isoformat(),
146
+ "project_label": project_label,
147
+ "project_hash": sha256_prefix(project_label),
148
+ "source_file": str(filepath.name),
149
+ "mention_count": mention_count,
150
+ "agent_context": "direct",
151
+ }
152
+ events.append(event)
153
+
154
+ return events
155
+
156
+
157
+ def load_watermark() -> dict:
158
+ """Load the watermark file tracking which logs have been mined."""
159
+ if WATERMARK_FILE.exists():
160
+ try:
161
+ return json.loads(WATERMARK_FILE.read_text())
162
+ except (json.JSONDecodeError, OSError):
163
+ pass
164
+ return {"mined_files": [], "last_run": None}
165
+
166
+
167
+ def save_watermark(watermark: dict):
168
+ """Save the watermark file."""
169
+ ECC_DIR.mkdir(parents=True, exist_ok=True)
170
+ os.chmod(ECC_DIR, 0o700)
171
+ old_umask = os.umask(0o077)
172
+ try:
173
+ WATERMARK_FILE.write_text(json.dumps(watermark, indent=2))
174
+ finally:
175
+ os.umask(old_umask)
176
+
177
+
178
+ def write_events(events: list[dict]):
179
+ """Write events to daily JSONL files."""
180
+ if not events:
181
+ return
182
+
183
+ ECC_DIR.mkdir(parents=True, exist_ok=True)
184
+ os.chmod(ECC_DIR, 0o700)
185
+
186
+ # Group events by date (from their timestamp)
187
+ by_date: dict[str, list[dict]] = {}
188
+ for event in events:
189
+ try:
190
+ ts = datetime.fromisoformat(event["timestamp"])
191
+ date_key = ts.strftime("%Y-%m-%d")
192
+ except (KeyError, ValueError):
193
+ date_key = datetime.now().strftime("%Y-%m-%d")
194
+ by_date.setdefault(date_key, []).append(event)
195
+
196
+ old_umask = os.umask(0o077)
197
+ try:
198
+ for date_key, date_events in by_date.items():
199
+ filepath = ECC_DIR / f"observations-{date_key}.jsonl"
200
+ with open(filepath, "a") as f:
201
+ for event in date_events:
202
+ f.write(json.dumps(event, separators=(",", ":")) + "\n")
203
+ finally:
204
+ os.umask(old_umask)
205
+
206
+
207
+ def main():
208
+ parser = argparse.ArgumentParser(description="Mine session logs for skill invocations")
209
+ parser.add_argument("--backfill", action="store_true", help="Re-mine all logs (ignore watermark)")
210
+ parser.add_argument("--dry-run", action="store_true", help="Show what would be mined without writing")
211
+ args = parser.parse_args()
212
+
213
+ known_skills = load_known_skills()
214
+ if not known_skills:
215
+ print("Warning: no skills found in ~/.claude/skills/", file=sys.stderr)
216
+
217
+ log_dirs = find_log_dirs()
218
+ if not log_dirs:
219
+ print("No log directories found.", file=sys.stderr)
220
+ sys.exit(1)
221
+
222
+ watermark = load_watermark()
223
+ mined_set = set(watermark.get("mined_files", [])) if not args.backfill else set()
224
+
225
+ # Collect all log files
226
+ all_logs = []
227
+ for log_dir in log_dirs:
228
+ for filepath in sorted(log_dir.glob("*.md")):
229
+ if filepath.name not in mined_set:
230
+ all_logs.append(filepath)
231
+
232
+ if not all_logs:
233
+ print("No new session logs to mine.")
234
+ return
235
+
236
+ # Mine each log
237
+ total_events = 0
238
+ total_skills = set()
239
+ all_events = []
240
+ newly_mined = []
241
+
242
+ for filepath in all_logs:
243
+ events = extract_skills_from_log(filepath, known_skills)
244
+ if events:
245
+ all_events.extend(events)
246
+ total_events += len(events)
247
+ for e in events:
248
+ total_skills.add(e["skill"])
249
+ if args.dry_run:
250
+ skills_in_log = [e["skill"] for e in events]
251
+ print(f" {filepath.name}: {', '.join(skills_in_log)}")
252
+ newly_mined.append(filepath.name)
253
+
254
+ if args.dry_run:
255
+ print(f"\nDry run: {total_events} events from {len(newly_mined)} logs, "
256
+ f"{len(total_skills)} unique skills")
257
+ return
258
+
259
+ # Write events
260
+ write_events(all_events)
261
+
262
+ # Update watermark
263
+ mined_set.update(newly_mined)
264
+ watermark["mined_files"] = sorted(mined_set)
265
+ watermark["last_run"] = datetime.now(timezone.utc).isoformat()
266
+ save_watermark(watermark)
267
+
268
+ print(f"Mined {total_events} skill references from {len(newly_mined)} logs "
269
+ f"({len(total_skills)} unique skills)")
270
+
271
+
272
+ if __name__ == "__main__":
273
+ main()
@@ -0,0 +1,252 @@
1
+ #!/bin/bash
2
+ # Sync Claude Code infrastructure to Codex CLI
3
+ # Usage: bash .scripts/sync-to-codex.sh [--dry-run]
4
+ #
5
+ # Phases:
6
+ # 1. Backup existing ~/.codex/ state
7
+ # 2. Symlink skills from ~/.claude/skills/ → ~/.codex/skills/
8
+ # 3. Generate ~/.codex/AGENTS.md from agents + rules
9
+ # 4. Update ~/.codex/config.toml (model, sandbox, MCP servers)
10
+ # 5. Validate
11
+
12
+ set -euo pipefail
13
+
14
+ DRY_RUN=false
15
+ [[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
16
+
17
+ CLAUDE_HOME="$HOME/.claude"
18
+ CODEX_HOME="$HOME/.codex"
19
+ SKILLS_SRC="$CLAUDE_HOME/skills"
20
+ SKILLS_DST="$CODEX_HOME/skills"
21
+ AGENTS_SRC="$CLAUDE_HOME/agents"
22
+ RULES_SRC="$CLAUDE_HOME/rules"
23
+
24
+ # Resolve Task Management root
25
+ _CONFIG="$HOME/.config/task-mgmt/path"
26
+ if [[ ! -f "$_CONFIG" ]]; then echo "Missing $_CONFIG" >&2; exit 1; fi
27
+ TM="$(head -1 "$_CONFIG" | tr -d '\n')"
28
+
29
+ # Colours
30
+ GREEN='\033[0;32m'
31
+ YELLOW='\033[1;33m'
32
+ RED='\033[0;31m'
33
+ NC='\033[0m'
34
+
35
+ info() { echo -e "${GREEN}✓${NC} $*"; }
36
+ warn() { echo -e "${YELLOW}⚠${NC} $*"; }
37
+ error() { echo -e "${RED}✗${NC} $*" >&2; }
38
+
39
+ # ── Phase 0: Pre-flight checks ──────────────────────────────────────────
40
+
41
+ if [[ ! -d "$CODEX_HOME" ]]; then
42
+ error "Codex home directory not found at $CODEX_HOME"
43
+ exit 1
44
+ fi
45
+
46
+ if ! command -v codex &>/dev/null; then
47
+ error "Codex CLI not found in PATH"
48
+ exit 1
49
+ fi
50
+
51
+ if [[ ! -d "$SKILLS_SRC" ]]; then
52
+ error "Claude skills directory not found at $SKILLS_SRC"
53
+ exit 1
54
+ fi
55
+
56
+ echo "Syncing Claude Code → Codex CLI"
57
+ echo " Claude home: $CLAUDE_HOME"
58
+ echo " Codex home: $CODEX_HOME"
59
+ echo " Dry run: $DRY_RUN"
60
+ echo ""
61
+
62
+ # ── Phase 1: Backup ─────────────────────────────────────────────────────
63
+
64
+ BACKUP_DIR="$CODEX_HOME/backups/$(date +%Y%m%d-%H%M%S)"
65
+ if [[ "$DRY_RUN" == "false" ]]; then
66
+ mkdir -p "$BACKUP_DIR"
67
+ # Back up AGENTS.md and config.toml if they exist
68
+ [[ -f "$CODEX_HOME/AGENTS.md" ]] && cp "$CODEX_HOME/AGENTS.md" "$BACKUP_DIR/"
69
+ [[ -f "$CODEX_HOME/config.toml" ]] && cp "$CODEX_HOME/config.toml" "$BACKUP_DIR/"
70
+ info "Backup saved to $BACKUP_DIR"
71
+ else
72
+ info "[dry-run] Would backup AGENTS.md and config.toml to $BACKUP_DIR"
73
+ fi
74
+
75
+ # ── Phase 2: Symlink skills ─────────────────────────────────────────────
76
+
77
+ echo ""
78
+ echo "Phase 2: Skills"
79
+
80
+ mkdir -p "$SKILLS_DST"
81
+
82
+ # Track counts
83
+ LINKED=0
84
+ SKIPPED=0
85
+ REMOVED=0
86
+
87
+ # Remove stale symlinks (point to skills that no longer exist in Claude)
88
+ for link in "$SKILLS_DST"/*/; do
89
+ [[ ! -L "${link%/}" ]] && continue # skip non-symlinks
90
+ link_name="$(basename "${link%/}")"
91
+ # Skip system skills
92
+ [[ "$link_name" == ".system" ]] && continue
93
+ # Check if symlink target still exists
94
+ if [[ ! -e "${link%/}" ]]; then
95
+ if [[ "$DRY_RUN" == "false" ]]; then
96
+ rm "$SKILLS_DST/$link_name"
97
+ ((REMOVED++))
98
+ else
99
+ warn "[dry-run] Would remove stale symlink: $link_name"
100
+ ((REMOVED++))
101
+ fi
102
+ fi
103
+ done
104
+
105
+ # Create symlinks for each Claude skill
106
+ for skill_dir in "$SKILLS_SRC"/*/; do
107
+ [[ ! -d "$skill_dir" ]] && continue
108
+ skill_name="$(basename "$skill_dir")"
109
+
110
+ # Skip hidden directories
111
+ [[ "$skill_name" == .* ]] && continue
112
+
113
+ # Check for SKILL.md (required)
114
+ if [[ ! -f "$skill_dir/SKILL.md" ]]; then
115
+ warn "Skipping $skill_name (no SKILL.md)"
116
+ ((SKIPPED++))
117
+ continue
118
+ fi
119
+
120
+ # Check for collision with system skills
121
+ if [[ -d "$SKILLS_DST/.system/$skill_name" ]]; then
122
+ warn "Skipping $skill_name (conflicts with Codex system skill)"
123
+ ((SKIPPED++))
124
+ continue
125
+ fi
126
+
127
+ # Create or update symlink
128
+ if [[ "$DRY_RUN" == "false" ]]; then
129
+ ln -sfn "$skill_dir" "$SKILLS_DST/$skill_name"
130
+ ((LINKED++))
131
+ else
132
+ if [[ -L "$SKILLS_DST/$skill_name" ]]; then
133
+ ((LINKED++))
134
+ else
135
+ info "[dry-run] Would link: $skill_name"
136
+ ((LINKED++))
137
+ fi
138
+ fi
139
+ done
140
+
141
+ info "Skills: $LINKED linked, $SKIPPED skipped, $REMOVED stale removed"
142
+
143
+ # ── Phase 3: Generate AGENTS.md ─────────────────────────────────────────
144
+
145
+ echo ""
146
+ echo "Phase 3: AGENTS.md"
147
+
148
+ if [[ "$DRY_RUN" == "false" ]]; then
149
+ uv run python "$TM/.scripts/generate-codex-agents-md.py" \
150
+ --agents-dir "$AGENTS_SRC" \
151
+ --rules-dir "$RULES_SRC" \
152
+ --output "$CODEX_HOME/AGENTS.md"
153
+ info "Generated $CODEX_HOME/AGENTS.md"
154
+ else
155
+ info "[dry-run] Would generate AGENTS.md from $AGENTS_SRC + $RULES_SRC"
156
+ fi
157
+
158
+ # ── Phase 4: Update config.toml ─────────────────────────────────────────
159
+
160
+ echo ""
161
+ echo "Phase 4: config.toml"
162
+
163
+ CONFIG_FILE="$CODEX_HOME/config.toml"
164
+
165
+ # Read existing config to preserve user settings (model, trust, migrations)
166
+ # We only add/update the sections we manage; we don't overwrite the whole file.
167
+
168
+ # Check if our managed sections already exist
169
+ if grep -q "# --- Managed by sync-to-codex ---" "$CONFIG_FILE" 2>/dev/null; then
170
+ # Remove our managed block and re-append
171
+ if [[ "$DRY_RUN" == "false" ]]; then
172
+ sed -i '' '/^# --- Managed by sync-to-codex ---$/,/^# --- End managed ---$/d' "$CONFIG_FILE"
173
+ fi
174
+ fi
175
+
176
+ MANAGED_BLOCK=$(cat <<'TOML'
177
+ # --- Managed by sync-to-codex ---
178
+ # Do not edit this section manually; it is regenerated by sync-to-codex.sh
179
+
180
+ [sandbox]
181
+ # Default to workspace-write for interactive use
182
+ permissions = ["disk-full-read-access"]
183
+
184
+ [history]
185
+ # Persist conversation history
186
+ persistence = "save"
187
+
188
+ # --- End managed ---
189
+ TOML
190
+ )
191
+
192
+ if [[ "$DRY_RUN" == "false" ]]; then
193
+ echo "" >> "$CONFIG_FILE"
194
+ echo "$MANAGED_BLOCK" >> "$CONFIG_FILE"
195
+ info "Updated config.toml (managed block appended)"
196
+ else
197
+ info "[dry-run] Would append managed block to config.toml"
198
+ fi
199
+
200
+ # ── Phase 5: Validation ─────────────────────────────────────────────────
201
+
202
+ echo ""
203
+ echo "Phase 5: Validation"
204
+
205
+ ERRORS=0
206
+
207
+ # Check skills count
208
+ SKILL_COUNT=$(find "$SKILLS_DST" -maxdepth 1 -type l | wc -l | tr -d ' ')
209
+ if [[ "$SKILL_COUNT" -gt 0 ]]; then
210
+ info "Skills directory: $SKILL_COUNT symlinks"
211
+ else
212
+ error "No skill symlinks found"
213
+ ((ERRORS++))
214
+ fi
215
+
216
+ # Check AGENTS.md exists and is non-empty
217
+ if [[ -f "$CODEX_HOME/AGENTS.md" && -s "$CODEX_HOME/AGENTS.md" ]]; then
218
+ AGENTS_LINES=$(wc -l < "$CODEX_HOME/AGENTS.md" | tr -d ' ')
219
+ info "AGENTS.md: $AGENTS_LINES lines"
220
+ else
221
+ if [[ "$DRY_RUN" == "false" ]]; then
222
+ error "AGENTS.md missing or empty"
223
+ ((ERRORS++))
224
+ fi
225
+ fi
226
+
227
+ # Check config.toml is parseable (basic check)
228
+ if [[ -f "$CONFIG_FILE" ]]; then
229
+ if python3 -c "
230
+ import tomllib, sys
231
+ with open('$CONFIG_FILE', 'rb') as f:
232
+ tomllib.load(f)
233
+ " 2>/dev/null; then
234
+ info "config.toml: valid TOML"
235
+ else
236
+ error "config.toml: invalid TOML"
237
+ ((ERRORS++))
238
+ fi
239
+ fi
240
+
241
+ # Check Codex CLI version
242
+ CODEX_VERSION=$(codex --version 2>/dev/null || echo "unknown")
243
+ info "Codex CLI: $CODEX_VERSION"
244
+
245
+ # Summary
246
+ echo ""
247
+ if [[ "$ERRORS" -eq 0 ]]; then
248
+ echo -e "${GREEN}Sync complete — no errors.${NC}"
249
+ else
250
+ echo -e "${RED}Sync completed with $ERRORS error(s).${NC}"
251
+ exit 1
252
+ fi