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
package/.scripts/done ADDED
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Mark a task as complete in Notion.
4
+
5
+ Usage:
6
+ done "[Journal] feedback" # Search and mark matching task as done
7
+ done --list # Show recent tasks to mark done
8
+ """
9
+
10
+ import argparse
11
+ import json
12
+ import os
13
+ import sys
14
+ import urllib.request
15
+ import urllib.error
16
+
17
+
18
+ NOTION_TOKEN = os.environ.get("NOTION_TOKEN")
19
+ DATABASE_ID = "YOUR-TASKS-DATABASE-ID-HERE"
20
+
21
+
22
+ def query_tasks(search_term=None):
23
+ """Query tasks from Notion."""
24
+ if not NOTION_TOKEN:
25
+ print("❌ NOTION_TOKEN not set")
26
+ sys.exit(1)
27
+
28
+ data = {
29
+ "filter": {
30
+ "property": "Status",
31
+ "status": {"does_not_equal": "Done"}
32
+ },
33
+ "sorts": [
34
+ {"property": "Due date", "direction": "ascending"}
35
+ ]
36
+ }
37
+
38
+ req = urllib.request.Request(
39
+ f"https://api.notion.com/v1/databases/{DATABASE_ID}/query",
40
+ data=json.dumps(data).encode("utf-8"),
41
+ headers={
42
+ "Authorization": f"Bearer {NOTION_TOKEN}",
43
+ "Content-Type": "application/json",
44
+ "Notion-Version": "2022-06-28",
45
+ },
46
+ method="POST",
47
+ )
48
+
49
+ try:
50
+ with urllib.request.urlopen(req) as response:
51
+ result = json.loads(response.read().decode("utf-8"))
52
+ pages = result.get("results", [])
53
+
54
+ if search_term:
55
+ search_lower = search_term.lower()
56
+ pages = [p for p in pages if search_lower in get_title(p).lower()]
57
+
58
+ return pages
59
+ except urllib.error.HTTPError as e:
60
+ error_body = json.loads(e.read().decode("utf-8"))
61
+ print(f"❌ Error: {e.code}")
62
+ print(error_body.get("message", str(error_body)))
63
+ sys.exit(1)
64
+
65
+
66
+ def get_title(page):
67
+ """Get task title from page."""
68
+ title_prop = page.get("properties", {}).get("Task name", {}).get("title", [])
69
+ return title_prop[0].get("plain_text", "") if title_prop else ""
70
+
71
+
72
+ def get_project(page):
73
+ """Get project from page."""
74
+ proj = page.get("properties", {}).get("Project", {}).get("select")
75
+ return proj.get("name", "") if proj else ""
76
+
77
+
78
+ def mark_done(page_id):
79
+ """Mark a task as done."""
80
+ data = {
81
+ "properties": {
82
+ "Status": {"status": {"name": "Done"}}
83
+ }
84
+ }
85
+
86
+ req = urllib.request.Request(
87
+ f"https://api.notion.com/v1/pages/{page_id}",
88
+ data=json.dumps(data).encode("utf-8"),
89
+ headers={
90
+ "Authorization": f"Bearer {NOTION_TOKEN}",
91
+ "Content-Type": "application/json",
92
+ "Notion-Version": "2022-06-28",
93
+ },
94
+ method="PATCH",
95
+ )
96
+
97
+ try:
98
+ with urllib.request.urlopen(req) as response:
99
+ return True
100
+ except urllib.error.HTTPError as e:
101
+ error_body = json.loads(e.read().decode("utf-8"))
102
+ print(f"❌ Error: {e.code}")
103
+ print(error_body.get("message", str(error_body)))
104
+ return False
105
+
106
+
107
+ def main():
108
+ parser = argparse.ArgumentParser(description="Mark tasks as done")
109
+ parser.add_argument("search", nargs="?", help="Search term to find task")
110
+ parser.add_argument("--list", action="store_true", help="List recent incomplete tasks")
111
+
112
+ args = parser.parse_args()
113
+
114
+ if args.list:
115
+ pages = query_tasks()[:15]
116
+ if not pages:
117
+ print("No incomplete tasks found.")
118
+ return
119
+
120
+ print("📋 Incomplete Tasks")
121
+ print("-" * 40)
122
+ for i, page in enumerate(pages, 1):
123
+ title = get_title(page)
124
+ project = get_project(page)
125
+ line = f"{i:2}. {title}"
126
+ if project:
127
+ line += f" [{project}]"
128
+ print(line)
129
+ return
130
+
131
+ if not args.search:
132
+ parser.print_help()
133
+ return
134
+
135
+ pages = query_tasks(args.search)
136
+
137
+ if not pages:
138
+ print(f"No tasks found matching '{args.search}'")
139
+ return
140
+
141
+ if len(pages) == 1:
142
+ page = pages[0]
143
+ title = get_title(page)
144
+ if mark_done(page["id"]):
145
+ print(f"✅ Done: {title}")
146
+ else:
147
+ print(f"Found {len(pages)} matching tasks:")
148
+ print("-" * 40)
149
+ for i, page in enumerate(pages, 1):
150
+ title = get_title(page)
151
+ project = get_project(page)
152
+ line = f"{i}. {title}"
153
+ if project:
154
+ line += f" [{project}]"
155
+ print(line)
156
+
157
+ print()
158
+ try:
159
+ choice = input("Which one? (number or 'q' to cancel): ").strip()
160
+ if choice.lower() == 'q':
161
+ print("Cancelled.")
162
+ return
163
+
164
+ idx = int(choice) - 1
165
+ if 0 <= idx < len(pages):
166
+ page = pages[idx]
167
+ title = get_title(page)
168
+ if mark_done(page["id"]):
169
+ print(f"✅ Done: {title}")
170
+ else:
171
+ print("Invalid choice.")
172
+ except (ValueError, KeyboardInterrupt):
173
+ print("\nCancelled.")
174
+
175
+
176
+ if __name__ == "__main__":
177
+ main()
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Meeting Action Item Extractor
4
+
5
+ This script is designed to work with Claude/Cowork to:
6
+ 1. Find recent meeting transcripts in Notion (pages starting with @)
7
+ 2. Extract action items using pattern matching and AI
8
+ 3. Create corresponding tasks in the Tasks Tracker database
9
+
10
+ Usage with Claude:
11
+ "Run the meeting action extractor on yesterday's meeting with [Supervisor]"
12
+
13
+ Manual usage:
14
+ python extract_meeting_actions.py --date "2026-01-14"
15
+ """
16
+
17
+ import argparse
18
+ import re
19
+ from datetime import datetime, timedelta
20
+ from typing import List, Dict, Optional
21
+ from pathlib import Path
22
+
23
+ from notion_helpers import (
24
+ extract_action_patterns,
25
+ parse_deadline_from_text,
26
+ infer_project_from_context,
27
+ infer_priority_from_context,
28
+ format_task_for_notion,
29
+ )
30
+ from config import DATABASES, CONTEXT_DIR
31
+
32
+
33
+ def find_meeting_transcripts_query(since_date: Optional[str] = None) -> str:
34
+ """
35
+ Generate a search query to find meeting transcripts in Notion.
36
+
37
+ Meeting transcripts are pages with titles like "@14 January 2026 14:31"
38
+ """
39
+ query = "@ January" if not since_date else f"@{since_date}"
40
+ return query
41
+
42
+
43
+ def parse_meeting_title(title: str) -> Optional[datetime]:
44
+ """
45
+ Parse a meeting title like "@14 January 2026 14:31" into a datetime.
46
+ """
47
+ # Pattern: @DD Month YYYY HH:MM
48
+ pattern = r"@(\d{1,2})\s+(\w+)\s+(\d{4})\s+(\d{1,2}):(\d{2})"
49
+ match = re.match(pattern, title)
50
+
51
+ if match:
52
+ day, month, year, hour, minute = match.groups()
53
+ month_map = {
54
+ "january": 1, "february": 2, "march": 3, "april": 4,
55
+ "may": 5, "june": 6, "july": 7, "august": 8,
56
+ "september": 9, "october": 10, "november": 11, "december": 12
57
+ }
58
+ month_num = month_map.get(month.lower())
59
+ if month_num:
60
+ return datetime(int(year), month_num, int(day), int(hour), int(minute))
61
+
62
+ return None
63
+
64
+
65
+ def identify_meeting_participants(content: str) -> List[str]:
66
+ """
67
+ Try to identify who was in the meeting based on content.
68
+ """
69
+ # Known names from context
70
+ known_people = [
71
+ "[Supervisor]", "[Supervisor]", "[Collaborator]", "[Collaborator]", "[Collaborator]",
72
+ "Francois", "David", "Toby", "Walter"
73
+ ]
74
+
75
+ found = []
76
+ content_lower = content.lower()
77
+
78
+ for person in known_people:
79
+ if person.lower() in content_lower:
80
+ found.append(person)
81
+
82
+ return found
83
+
84
+
85
+ def extract_actions_from_transcript(
86
+ content: str,
87
+ meeting_date: str,
88
+ participants: List[str] = None
89
+ ) -> List[Dict]:
90
+ """
91
+ Extract structured action items from meeting transcript content.
92
+
93
+ Args:
94
+ content: The meeting transcript text
95
+ meeting_date: Date string of the meeting
96
+ participants: List of people who were in the meeting
97
+
98
+ Returns:
99
+ List of action item dictionaries ready for task creation
100
+ """
101
+ # Use pattern matching to find potential actions
102
+ raw_actions = extract_action_patterns(content)
103
+
104
+ # Enrich each action with context
105
+ enriched_actions = []
106
+
107
+ for action in raw_actions:
108
+ task_text = action["text"]
109
+
110
+ # Clean up the task text
111
+ task_text = task_text.strip()
112
+ if task_text.endswith((",", ".", ";")):
113
+ task_text = task_text[:-1]
114
+
115
+ # Skip very short or unclear actions
116
+ if len(task_text) < 5:
117
+ continue
118
+
119
+ # Infer additional context
120
+ deadline = parse_deadline_from_text(task_text)
121
+ project = infer_project_from_context(task_text, participants)
122
+ is_supervisor = any(p in ["[Supervisor]", "[Supervisor]"] for p in (participants or []))
123
+ priority = infer_priority_from_context(
124
+ task_text,
125
+ has_deadline=deadline is not None,
126
+ is_supervisor_request=is_supervisor and action["type"] == "request"
127
+ )
128
+
129
+ enriched = {
130
+ "task_name": f"{task_text} - from {meeting_date} meeting",
131
+ "original_text": action["text"],
132
+ "deadline": deadline,
133
+ "project": project,
134
+ "priority": priority,
135
+ "source": "Meeting",
136
+ "meeting_date": meeting_date,
137
+ "participants": participants or [],
138
+ "action_type": action["type"],
139
+ }
140
+
141
+ enriched_actions.append(enriched)
142
+
143
+ return enriched_actions
144
+
145
+
146
+ def generate_task_creation_instructions(actions: List[Dict]) -> str:
147
+ """
148
+ Generate instructions for Claude to create tasks in Notion.
149
+
150
+ This is the output format when used with Claude/Cowork.
151
+ """
152
+ if not actions:
153
+ return "No action items found in this transcript."
154
+
155
+ output = []
156
+ output.append("## Extracted Action Items\n")
157
+ output.append(f"Found {len(actions)} potential action items:\n")
158
+
159
+ for i, action in enumerate(actions, 1):
160
+ output.append(f"### {i}. {action['task_name']}")
161
+ output.append(f"- **Priority:** {action['priority']}")
162
+ if action['deadline']:
163
+ output.append(f"- **Due:** {action['deadline']}")
164
+ if action['project']:
165
+ output.append(f"- **Project:** {action['project']}")
166
+ output.append(f"- **Source:** Meeting ({action['meeting_date']})")
167
+ if action['participants']:
168
+ output.append(f"- **With:** {', '.join(action['participants'])}")
169
+ output.append("")
170
+
171
+ output.append("\n---\n")
172
+ output.append("**To create these tasks in Notion:**")
173
+ output.append("Use the `notion-create-pages` tool with parent:")
174
+ output.append(f" `{{'data_source_id': '{DATABASES['tasks_tracker_collection']}'}}`")
175
+
176
+ return "\n".join(output)
177
+
178
+
179
+ def main():
180
+ parser = argparse.ArgumentParser(description="Extract meeting action items")
181
+ parser.add_argument("--date", help="Meeting date (YYYY-MM-DD)")
182
+ parser.add_argument("--days", type=int, default=7, help="Look back N days")
183
+ parser.add_argument("--dry-run", action="store_true", help="Don't create tasks")
184
+ args = parser.parse_args()
185
+
186
+ print("Meeting Action Extractor")
187
+ print("=" * 40)
188
+ print()
189
+ print("This script helps extract action items from meeting transcripts.")
190
+ print("When used with Claude/Cowork, it will:")
191
+ print(" 1. Search Notion for meeting transcripts (@DATE pages)")
192
+ print(" 2. Extract action items using pattern matching")
193
+ print(" 3. Create tasks in the Tasks Tracker database")
194
+ print()
195
+ print("Example usage with Claude:")
196
+ print(' "Extract action items from my meeting yesterday with [Supervisor]"')
197
+ print(' "Process all unprocessed meeting transcripts from this week"')
198
+ print()
199
+
200
+ if args.dry_run:
201
+ # Demo with sample text
202
+ sample_transcript = """
203
+ the user, I know you would be operative for you, right? So when you have these events,
204
+ you need to give a list of the names of people coming to the desk downstairs at the Shard.
205
+ I'll send you an email about the catering costs by Friday.
206
+ Can you also update the course guide and send it to the students?
207
+ We agreed to meet again next Tuesday to discuss progress.
208
+ """
209
+
210
+ print("Demo extraction from sample text:")
211
+ print("-" * 40)
212
+
213
+ actions = extract_actions_from_transcript(
214
+ sample_transcript,
215
+ meeting_date="14 January 2026",
216
+ participants=["[Supervisor]"]
217
+ )
218
+
219
+ print(generate_task_creation_instructions(actions))
220
+
221
+
222
+ if __name__ == "__main__":
223
+ main()
package/.scripts/focus ADDED
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Quick update to current-focus.md
4
+
5
+ Usage:
6
+ focus "Working on [Journal] section 4" # Update what you're working on
7
+ focus --left-off "Finished draft" # Update where you left off
8
+ focus --next "Review with [Supervisor]" # Add next step
9
+ focus --show # Show current focus
10
+ """
11
+
12
+ import argparse
13
+ import os
14
+ from datetime import datetime
15
+ from pathlib import Path
16
+
17
+
18
+ BASE_DIR = Path(__file__).resolve().parent.parent
19
+ CONTEXT_DIR = BASE_DIR / ".context"
20
+ FOCUS_FILE = CONTEXT_DIR / "current-focus.md"
21
+
22
+
23
+ def read_focus():
24
+ """Read current focus file."""
25
+ if FOCUS_FILE.exists():
26
+ return FOCUS_FILE.read_text()
27
+ return ""
28
+
29
+
30
+ def write_focus(content):
31
+ """Write focus file."""
32
+ FOCUS_FILE.write_text(content)
33
+
34
+
35
+ def update_section(content, section_name, new_value):
36
+ """Update a specific section in the markdown."""
37
+ lines = content.split("\n")
38
+ new_lines = []
39
+ in_section = False
40
+ section_updated = False
41
+
42
+ for i, line in enumerate(lines):
43
+ if line.startswith(f"## {section_name}"):
44
+ in_section = True
45
+ new_lines.append(line)
46
+ new_lines.append(new_value)
47
+ section_updated = True
48
+ continue
49
+ elif line.startswith("## ") and in_section:
50
+ in_section = False
51
+
52
+ if not in_section:
53
+ new_lines.append(line)
54
+ elif not line.strip():
55
+ new_lines.append(line)
56
+
57
+ if not section_updated:
58
+ # Add section at end
59
+ new_lines.append(f"\n## {section_name}")
60
+ new_lines.append(new_value)
61
+
62
+ return "\n".join(new_lines)
63
+
64
+
65
+ def create_default_template():
66
+ """Create default focus template."""
67
+ now = datetime.now().strftime("%Y-%m-%d %H:%M")
68
+ return f"""# Current Focus
69
+
70
+ Last updated: {now}
71
+
72
+ ## What I'm Working On
73
+
74
+
75
+ ## Where I Left Off
76
+
77
+
78
+ ## Next Steps
79
+ -
80
+
81
+ ## Open Questions
82
+ -
83
+
84
+ ## Notes
85
+
86
+ """
87
+
88
+
89
+ def main():
90
+ parser = argparse.ArgumentParser(description="Update current focus")
91
+ parser.add_argument("working_on", nargs="?", help="What you're working on")
92
+ parser.add_argument("--left-off", dest="left_off", help="Where you left off")
93
+ parser.add_argument("--next", dest="next_step", help="Next step to add")
94
+ parser.add_argument("--question", help="Open question to add")
95
+ parser.add_argument("--note", help="Note to add")
96
+ parser.add_argument("--show", action="store_true", help="Show current focus")
97
+ parser.add_argument("--clear", action="store_true", help="Clear and start fresh")
98
+
99
+ args = parser.parse_args()
100
+
101
+ # Show current focus
102
+ if args.show:
103
+ content = read_focus()
104
+ if content:
105
+ print(content)
106
+ else:
107
+ print("No current focus set.")
108
+ return
109
+
110
+ # Clear and start fresh
111
+ if args.clear:
112
+ write_focus(create_default_template())
113
+ print("✅ Focus cleared")
114
+ return
115
+
116
+ # Read existing or create new
117
+ content = read_focus()
118
+ if not content:
119
+ content = create_default_template()
120
+
121
+ # Update timestamp
122
+ now = datetime.now().strftime("%Y-%m-%d %H:%M")
123
+ if "Last updated:" in content:
124
+ lines = content.split("\n")
125
+ for i, line in enumerate(lines):
126
+ if line.startswith("Last updated:"):
127
+ lines[i] = f"Last updated: {now}"
128
+ break
129
+ content = "\n".join(lines)
130
+
131
+ updated = False
132
+
133
+ # Update what working on
134
+ if args.working_on:
135
+ content = update_section(content, "What I'm Working On", args.working_on)
136
+ updated = True
137
+ print(f"✅ Working on: {args.working_on}")
138
+
139
+ # Update where left off
140
+ if args.left_off:
141
+ content = update_section(content, "Where I Left Off", args.left_off)
142
+ updated = True
143
+ print(f"✅ Left off: {args.left_off}")
144
+
145
+ # Add next step
146
+ if args.next_step:
147
+ section = "Next Steps"
148
+ if f"## {section}" in content:
149
+ content = content.replace(f"## {section}\n", f"## {section}\n- {args.next_step}\n")
150
+ updated = True
151
+ print(f"✅ Next: {args.next_step}")
152
+
153
+ # Add question
154
+ if args.question:
155
+ section = "Open Questions"
156
+ if f"## {section}" in content:
157
+ content = content.replace(f"## {section}\n", f"## {section}\n- {args.question}\n")
158
+ updated = True
159
+ print(f"✅ Question: {args.question}")
160
+
161
+ # Add note
162
+ if args.note:
163
+ section = "Notes"
164
+ if f"## {section}" in content:
165
+ content = content.replace(f"## {section}\n", f"## {section}\n{args.note}\n")
166
+ updated = True
167
+ print(f"✅ Note added")
168
+
169
+ if updated:
170
+ write_focus(content)
171
+ else:
172
+ parser.print_help()
173
+
174
+
175
+ if __name__ == "__main__":
176
+ main()