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,275 @@
1
+ #!/usr/bin/env python3
2
+ """Count automation: scan filesystem for ground-truth infrastructure counts
3
+ and propagate them across all documentation files.
4
+
5
+ Usage:
6
+ uv run python .scripts/count_inventory.py [--check | --fix] [--json]
7
+
8
+ Exit codes:
9
+ 0 All counts match (--check) or fixes applied (--fix)
10
+ 1 Stale counts found (--check)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import json
17
+ import re
18
+ import sys
19
+ from dataclasses import dataclass, asdict
20
+ from pathlib import Path
21
+
22
+ # ── Ground truth ─────────────────────────────────────────────────────────
23
+
24
+ # Hardcoded constants (not derivable from filesystem)
25
+ NOTION_DBS = 6
26
+ RESOURCE_REPOS = 43 # 15 academics + 23 general + 5 bibliography
27
+ # Note: skill count is derived from filesystem (skills/*/SKILL.md), not hardcoded
28
+
29
+
30
+ def get_ground_truth(root: Path) -> dict[str, int]:
31
+ """Derive infrastructure counts from the filesystem."""
32
+ skills = len(list((root / "skills").glob("*/SKILL.md")))
33
+ # Also count skill.md (lowercase) to avoid missing any
34
+ skills += len([p for p in (root / "skills").glob("*/skill.md")
35
+ if not any(s.name == "SKILL.md" for s in p.parent.iterdir())])
36
+ agents = len(list((root / ".claude" / "agents").glob("*.md")))
37
+ rules = len(list((root / ".claude" / "rules").glob("*.md")))
38
+ hooks_sh = len(list((root / "hooks").glob("*.sh")))
39
+ hooks_py = len(list((root / "hooks").glob("*.py")))
40
+ hooks = hooks_sh + hooks_py
41
+ return {
42
+ "skills": skills,
43
+ "agents": agents,
44
+ "rules": rules,
45
+ "hooks": hooks,
46
+ }
47
+
48
+
49
+ # ── Scan configuration ──────────────────────────────────────────────────
50
+
51
+ # Files to scan (relative to repo root)
52
+ SCAN_FILES = [
53
+ "CLAUDE.md",
54
+ "README.md",
55
+ "docs/skills.md",
56
+ "docs/rules.md",
57
+ "docs/hooks.md",
58
+ "docs/agents.md",
59
+ "docs/system.md",
60
+ "docs/installation.md",
61
+ ".context/projects/_index.md",
62
+ "skills/shared/skill-index.md",
63
+ "docs/user-manual/user-manual.tex",
64
+ "docs/setup-overview/setup-overview.tex",
65
+ ]
66
+
67
+ # Lines matching these patterns are EXCLUDED from replacement.
68
+ # They are historical log entries, category subtotals, or external references.
69
+ EXCLUDE_LINE_PATTERNS = [
70
+ re.compile(r"\b\d+ to \d+ skills"), # "55 to 62 skills" changelog
71
+ re.compile(r"Added \d+ new skills"), # "Added 7 new skills" changelog
72
+ re.compile(r"\d+ agents \+ multiple skills"), # changelog
73
+ re.compile(r"hugo|sant.anna|clo-author", re.IGNORECASE), # external refs
74
+ re.compile(r"shared/"), # "shared/" references
75
+ re.compile(r"^\s*\|.*category", re.IGNORECASE), # table rows with category subtotals
76
+ re.compile(r"Research & Writing|Task Management.*Code|Publishing & Submission|Utilities", re.IGNORECASE), # skill category subtotal lines
77
+ re.compile(r"^\s*#"), # comment-only lines in code blocks
78
+ re.compile(r"Referee.2 agent performs"), # prose about what an agent does
79
+ re.compile(r"Referee.2 agent .+never"), # prose about agent behavior
80
+ re.compile(r"Referee.2 Agent", re.IGNORECASE), # section title "The Referee~2 Agent"
81
+ ]
82
+
83
+
84
+ @dataclass
85
+ class Mismatch:
86
+ file: str
87
+ line_num: int
88
+ found: int
89
+ expected: int
90
+ context: str # the line text
91
+ count_key: str # skills, agents, rules, hooks
92
+
93
+
94
+ # Pattern tuples: (compiled regex, count_key)
95
+ # Each regex has a single capture group for the number.
96
+ # All patterns are case-insensitive to catch "81 Skills", "25 Hooks" etc.
97
+ SCAN_PATTERNS: list[tuple[re.Pattern, str]] = [
98
+ (re.compile(r"(\d+)\s+skills?\b", re.IGNORECASE), "skills"),
99
+ (re.compile(r"(\d+)\s+skill definitions?\b", re.IGNORECASE), "skills"),
100
+ (re.compile(r"(\d+)\s+reusable\b", re.IGNORECASE), "skills"),
101
+ (re.compile(r"(\d+)\s+agents?\b", re.IGNORECASE), "agents"),
102
+ (re.compile(r"(\d+)\s+agent definitions?\b", re.IGNORECASE), "agents"),
103
+ (re.compile(r"(\d+)\s+rules?\b", re.IGNORECASE), "rules"),
104
+ (re.compile(r"(\d+)\s+auto-loaded\b", re.IGNORECASE), "rules"),
105
+ (re.compile(r"(\d+)\s+hooks?\b", re.IGNORECASE), "hooks"),
106
+ (re.compile(r"(\d+)\s+hook scripts?\b", re.IGNORECASE), "hooks"),
107
+ # Heading format: ## Hooks (N scripts)
108
+ (re.compile(r"\((\d+)\s+scripts?\)"), "hooks"),
109
+ ]
110
+
111
+
112
+ def _is_excluded(line: str) -> bool:
113
+ """Return True if the line should be skipped."""
114
+ return any(p.search(line) for p in EXCLUDE_LINE_PATTERNS)
115
+
116
+
117
+ def scan(root: Path, truth: dict[str, int]) -> list[Mismatch]:
118
+ """Scan all registered files for stale counts."""
119
+ mismatches: list[Mismatch] = []
120
+ seen: set[tuple[str, int, str]] = set() # (file, line_num, count_key)
121
+
122
+ for rel_path in SCAN_FILES:
123
+ fpath = root / rel_path
124
+ if not fpath.exists():
125
+ continue
126
+
127
+ lines = fpath.read_text(encoding="utf-8").splitlines()
128
+ for line_idx, line in enumerate(lines, start=1):
129
+ if _is_excluded(line):
130
+ continue
131
+
132
+ for pattern, key in SCAN_PATTERNS:
133
+ for m in pattern.finditer(line):
134
+ dedup_key = (rel_path, line_idx, key)
135
+ if dedup_key in seen:
136
+ continue
137
+ seen.add(dedup_key)
138
+
139
+ found = int(m.group(1))
140
+ expected = truth[key]
141
+ if found != expected:
142
+ mismatches.append(
143
+ Mismatch(
144
+ file=rel_path,
145
+ line_num=line_idx,
146
+ found=found,
147
+ expected=expected,
148
+ context=line.strip(),
149
+ count_key=key,
150
+ )
151
+ )
152
+
153
+ return mismatches
154
+
155
+
156
+ def fix(root: Path, mismatches: list[Mismatch]) -> int:
157
+ """Apply fixes for all mismatches. Returns number of fixes applied."""
158
+ # Group mismatches by file to minimise I/O
159
+ by_file: dict[str, list[Mismatch]] = {}
160
+ for mm in mismatches:
161
+ by_file.setdefault(mm.file, []).append(mm)
162
+
163
+ total_fixed = 0
164
+
165
+ for rel_path, file_mismatches in by_file.items():
166
+ fpath = root / rel_path
167
+ lines = fpath.read_text(encoding="utf-8").splitlines()
168
+
169
+ # Sort by line number descending so index shifts don't matter
170
+ for mm in sorted(file_mismatches, key=lambda m: m.line_num, reverse=True):
171
+ idx = mm.line_num - 1
172
+ old_line = lines[idx]
173
+
174
+ # Find the specific pattern match and replace the number
175
+ new_line = old_line
176
+ for pattern, key in SCAN_PATTERNS:
177
+ if key != mm.count_key:
178
+ continue
179
+ for m in pattern.finditer(old_line):
180
+ if int(m.group(1)) == mm.found:
181
+ # Replace just this occurrence
182
+ start, end = m.span(1)
183
+ new_line = (
184
+ new_line[:start] + str(mm.expected) + new_line[end:]
185
+ )
186
+ break
187
+ if new_line != old_line:
188
+ break
189
+
190
+ if new_line != old_line:
191
+ lines[idx] = new_line
192
+ total_fixed += 1
193
+
194
+ fpath.write_text("\n".join(lines) + "\n", encoding="utf-8")
195
+
196
+ return total_fixed
197
+
198
+
199
+ # ── CLI ──────────────────────────────────────────────────────────────────
200
+
201
+
202
+ def main() -> int:
203
+ parser = argparse.ArgumentParser(
204
+ description="Count automation for infrastructure docs"
205
+ )
206
+ group = parser.add_mutually_exclusive_group()
207
+ group.add_argument(
208
+ "--check",
209
+ action="store_true",
210
+ help="Report stale counts (default)",
211
+ )
212
+ group.add_argument(
213
+ "--fix", action="store_true", help="Fix stale counts in place"
214
+ )
215
+ parser.add_argument(
216
+ "--json", action="store_true", help="Output as JSON"
217
+ )
218
+ args = parser.parse_args()
219
+
220
+ root = Path(__file__).resolve().parent.parent
221
+ truth = get_ground_truth(root)
222
+ mismatches = scan(root, truth)
223
+
224
+ if args.json:
225
+ data = {
226
+ "ground_truth": truth,
227
+ "constants": {
228
+ "notion_dbs": NOTION_DBS,
229
+ "resource_repos": RESOURCE_REPOS,
230
+ },
231
+ "mismatches": [asdict(m) for m in mismatches],
232
+ "total_mismatches": len(mismatches),
233
+ }
234
+ print(json.dumps(data, indent=2))
235
+ return 1 if mismatches and not args.fix else 0
236
+
237
+ if args.fix:
238
+ if not mismatches:
239
+ print(f"All counts match ground truth: {truth}")
240
+ return 0
241
+
242
+ n_fixed = fix(root, mismatches)
243
+
244
+ # Re-scan to verify
245
+ remaining = scan(root, truth)
246
+ print(f"Ground truth: {truth}")
247
+ print(f"Fixed {n_fixed} stale counts across {len(set(m.file for m in mismatches))} files")
248
+ if remaining:
249
+ print(f"WARNING: {len(remaining)} counts still stale after fix:")
250
+ for m in remaining:
251
+ print(f" {m.file}:{m.line_num}: found {m.found}, expected {m.expected}")
252
+ print(f" {m.context}")
253
+ return 1
254
+ print("All counts now match.")
255
+ return 0
256
+
257
+ # --check (default)
258
+ print(f"Ground truth: {truth}")
259
+ if not mismatches:
260
+ print("All counts match.")
261
+ return 0
262
+
263
+ print(f"\n{len(mismatches)} stale counts found:\n")
264
+ current_file = None
265
+ for m in mismatches:
266
+ if m.file != current_file:
267
+ current_file = m.file
268
+ print(f" {m.file}:")
269
+ print(f" L{m.line_num}: {m.count_key} {m.found} → {m.expected}")
270
+ print(f" {m.context}")
271
+ return 1
272
+
273
+
274
+ if __name__ == "__main__":
275
+ sys.exit(main())
@@ -0,0 +1,288 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Daily Digest Generator
4
+
5
+ Generates a morning briefing to help with daily planning.
6
+ Designed to work with Claude/Cowork for interactive planning sessions.
7
+
8
+ Usage with Claude:
9
+ "Help me plan my day" -> Triggers this workflow
10
+
11
+ The digest includes:
12
+ - Overdue tasks
13
+ - Tasks due today
14
+ - Upcoming deadlines (next 7 days)
15
+ - Current focus from context library
16
+ - Questions to help prioritise
17
+ """
18
+
19
+ import argparse
20
+ from datetime import datetime, timedelta
21
+ from pathlib import Path
22
+ from typing import Dict, List, Optional
23
+
24
+ from config import CONTEXT_DIR, DATABASES
25
+
26
+
27
+ def load_current_focus() -> Optional[str]:
28
+ """
29
+ Load the current-focus.md file from the context library.
30
+ """
31
+ focus_file = CONTEXT_DIR / "current-focus.md"
32
+ if focus_file.exists():
33
+ return focus_file.read_text()
34
+ return None
35
+
36
+
37
+ def generate_planning_questions(
38
+ overdue_count: int = 0,
39
+ due_today_count: int = 0,
40
+ has_meetings: bool = False,
41
+ ) -> List[str]:
42
+ """
43
+ Generate contextual questions to help with daily planning.
44
+ """
45
+ questions = []
46
+
47
+ # Always start with energy check
48
+ questions.append("How are you feeling today - high energy for deep work, or better suited for lighter tasks?")
49
+
50
+ if has_meetings:
51
+ questions.append("You have meetings today. What do you need to prepare?")
52
+
53
+ if overdue_count > 0:
54
+ questions.append(f"You have {overdue_count} overdue task(s). Which of these is most important to address today?")
55
+
56
+ if due_today_count > 3:
57
+ questions.append(f"You have {due_today_count} tasks due today. That's a lot - which 2-3 are truly non-negotiable?")
58
+
59
+ questions.append("What were you working on yesterday? Should you continue, or switch focus?")
60
+ questions.append("Is anything weighing on your mind that we should capture or address?")
61
+
62
+ return questions
63
+
64
+
65
+ def format_task_summary(tasks: List[Dict]) -> str:
66
+ """
67
+ Format a list of tasks into a readable summary.
68
+ """
69
+ if not tasks:
70
+ return "_None_"
71
+
72
+ lines = []
73
+ for task in tasks[:10]: # Limit to 10
74
+ priority = task.get("Priority", "")
75
+ due = task.get("Due date", "")
76
+ project = task.get("Project", "")
77
+
78
+ priority_emoji = {"High": "🔴", "Medium": "🟡", "Low": "🟢"}.get(priority, "⚪")
79
+
80
+ line = f"- {priority_emoji} **{task.get('Task name', 'Untitled')}**"
81
+ if project:
82
+ line += f" [{project}]"
83
+ if due:
84
+ line += f" (due: {due})"
85
+
86
+ lines.append(line)
87
+
88
+ if len(tasks) > 10:
89
+ lines.append(f"_...and {len(tasks) - 10} more_")
90
+
91
+ return "\n".join(lines)
92
+
93
+
94
+ def generate_digest_template(
95
+ date: str,
96
+ overdue_tasks: List[Dict] = None,
97
+ due_today: List[Dict] = None,
98
+ upcoming: List[Dict] = None,
99
+ current_focus: Optional[str] = None,
100
+ ) -> str:
101
+ """
102
+ Generate the full daily digest document.
103
+ """
104
+ today = datetime.now()
105
+ day_name = today.strftime("%A")
106
+
107
+ digest = []
108
+
109
+ digest.append(f"# Daily Planning - {day_name}, {date}")
110
+ digest.append("")
111
+
112
+ # Planning Questions
113
+ digest.append("## Let's Plan Your Day")
114
+ digest.append("")
115
+ digest.append("Before diving in, let me ask a few questions:")
116
+ digest.append("")
117
+
118
+ questions = generate_planning_questions(
119
+ overdue_count=len(overdue_tasks or []),
120
+ due_today_count=len(due_today or []),
121
+ has_meetings=False, # Would need calendar integration
122
+ )
123
+
124
+ for i, q in enumerate(questions, 1):
125
+ digest.append(f"{i}. {q}")
126
+
127
+ digest.append("")
128
+ digest.append("---")
129
+ digest.append("")
130
+
131
+ # Overdue Tasks
132
+ digest.append("## ⚠️ Overdue Tasks")
133
+ digest.append("")
134
+ digest.append(format_task_summary(overdue_tasks or []))
135
+ digest.append("")
136
+
137
+ # Due Today
138
+ digest.append("## 📅 Due Today")
139
+ digest.append("")
140
+ digest.append(format_task_summary(due_today or []))
141
+ digest.append("")
142
+
143
+ # Upcoming (next 7 days)
144
+ digest.append("## 🔮 Upcoming (Next 7 Days)")
145
+ digest.append("")
146
+ digest.append(format_task_summary(upcoming or []))
147
+ digest.append("")
148
+
149
+ # Current Focus
150
+ if current_focus:
151
+ digest.append("## 🎯 Current Focus")
152
+ digest.append("")
153
+ # Extract just the key parts
154
+ digest.append("_From your context library:_")
155
+ digest.append("")
156
+ # Include first 500 chars of current focus
157
+ preview = current_focus[:500]
158
+ if len(current_focus) > 500:
159
+ preview += "..."
160
+ digest.append(preview)
161
+ digest.append("")
162
+
163
+ digest.append("---")
164
+ digest.append("")
165
+
166
+ # Daily Plan Template
167
+ digest.append("## Today's Plan")
168
+ digest.append("")
169
+ digest.append("Based on your answers, here's a suggested structure:")
170
+ digest.append("")
171
+ digest.append("### Must Do (non-negotiable)")
172
+ digest.append("1. ")
173
+ digest.append("")
174
+ digest.append("### Should Do (important but flexible)")
175
+ digest.append("2. ")
176
+ digest.append("3. ")
177
+ digest.append("")
178
+ digest.append("### Could Do (if time permits)")
179
+ digest.append("4. ")
180
+ digest.append("")
181
+ digest.append("### Parking Lot (explicitly deferred)")
182
+ digest.append("- ")
183
+ digest.append("")
184
+
185
+ return "\n".join(digest)
186
+
187
+
188
+ def generate_claude_instructions() -> str:
189
+ """
190
+ Generate instructions for Claude on how to use this for daily planning.
191
+ """
192
+ return """
193
+ ## How to Help the user Plan His Day
194
+
195
+ When the user asks for help with daily planning, follow this workflow:
196
+
197
+ ### Step 1: Ask Orientation Questions
198
+ Don't just dump a task list. Start with questions:
199
+ - "How's your energy today?"
200
+ - "Any meetings or fixed commitments?"
201
+ - "What were you working on yesterday?"
202
+ - "Anything weighing on your mind?"
203
+
204
+ ### Step 2: Query Notion for Tasks
205
+ Use the Notion tools to get:
206
+ 1. **Overdue tasks**: Status != Done AND Due date < today
207
+ 2. **Due today**: Due date = today
208
+ 3. **Upcoming**: Due date within next 7 days
209
+
210
+ Query the Tasks Tracker database:
211
+ - Data source: `collection://YOUR-TASKS-DATABASE-ID-HERE`
212
+
213
+ ### Step 3: Read Context
214
+ Check `.context/current-focus.md` for:
215
+ - What the user was working on
216
+ - Current priorities
217
+ - Open loops
218
+
219
+ ### Step 4: Help Prioritise
220
+ Based on answers and data:
221
+ - Suggest 3-5 realistic tasks
222
+ - Flag blocking items
223
+ - Consider energy level
224
+ - Mix different types of work
225
+
226
+ ### Step 5: Create the Plan
227
+ Format as:
228
+ - **Must Do**: Non-negotiable items
229
+ - **Should Do**: Important but flexible
230
+ - **Could Do**: If time permits
231
+ - **Parking Lot**: Explicitly deferred
232
+
233
+ ### Remember:
234
+ - the user prefers questions over lists
235
+ - Don't overwhelm - be realistic
236
+ - It's OK to suggest removing tasks
237
+ - Update current-focus.md at end of session
238
+ """
239
+
240
+
241
+ def main():
242
+ parser = argparse.ArgumentParser(description="Generate daily planning digest")
243
+ parser.add_argument("--date", help="Date for digest (YYYY-MM-DD)")
244
+ parser.add_argument("--instructions", action="store_true", help="Show Claude instructions")
245
+ args = parser.parse_args()
246
+
247
+ if args.instructions:
248
+ print(generate_claude_instructions())
249
+ return
250
+
251
+ date = args.date or datetime.now().strftime("%d %B %Y")
252
+
253
+ print("Daily Digest Generator")
254
+ print("=" * 40)
255
+ print()
256
+
257
+ # Load current focus
258
+ current_focus = load_current_focus()
259
+
260
+ # Generate template (without actual Notion data - Claude will fill this in)
261
+ print("When used with Claude/Cowork, this generates a daily planning session.")
262
+ print()
263
+ print("Example usage:")
264
+ print(' "Help me plan my day"')
265
+ print(' "What should I focus on today?"')
266
+ print(' "Morning briefing please"')
267
+ print()
268
+ print("-" * 40)
269
+ print()
270
+
271
+ # Show template structure
272
+ print(generate_digest_template(
273
+ date=date,
274
+ overdue_tasks=[
275
+ {"Task name": "[Claude will query Notion]", "Priority": "High", "Project": "..."}
276
+ ],
277
+ due_today=[
278
+ {"Task name": "[Tasks due today]", "Priority": "Medium"}
279
+ ],
280
+ upcoming=[
281
+ {"Task name": "[Upcoming tasks]", "Priority": "Low", "Due date": "..."}
282
+ ],
283
+ current_focus=current_focus,
284
+ ))
285
+
286
+
287
+ if __name__ == "__main__":
288
+ main()