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,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()
|