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,325 @@
1
+ """
2
+ Notion API Helper Functions
3
+
4
+ Utilities for interacting with Notion databases.
5
+ Note: For use with Claude/Cowork, these functions describe the intended
6
+ operations that Claude can execute via its Notion MCP tools.
7
+ """
8
+
9
+ from datetime import datetime, timedelta
10
+ from typing import Optional, List, Dict, Any
11
+
12
+
13
+ def format_task_for_notion(
14
+ task_name: str,
15
+ project: Optional[str] = None,
16
+ source: Optional[str] = None,
17
+ priority: str = "Medium",
18
+ due_date: Optional[str] = None,
19
+ task_type: Optional[List[str]] = None,
20
+ uni: Optional[str] = None,
21
+ description: Optional[str] = None,
22
+ ) -> Dict[str, Any]:
23
+ """
24
+ Format a task for creation in Notion Tasks Tracker.
25
+
26
+ Args:
27
+ task_name: The task title (action verb + object + context)
28
+ project: Which project this belongs to (e.g., "Journal Revision")
29
+ source: Where the task came from (e.g., "Meeting")
30
+ priority: "High", "Medium", or "Low"
31
+ due_date: ISO format date string (YYYY-MM-DD)
32
+ task_type: List of types (e.g., ["📝 Writing", "📅 Meeting"])
33
+ uni: Related university
34
+ description: Additional context
35
+
36
+ Returns:
37
+ Dictionary ready for Notion create-pages API
38
+ """
39
+ properties = {
40
+ "Task name": task_name,
41
+ "Status": "Not started",
42
+ "Priority": priority,
43
+ }
44
+
45
+ if project:
46
+ properties["Project"] = project
47
+
48
+ if source:
49
+ properties["Source"] = source
50
+
51
+ if due_date:
52
+ properties["date:Due date:start"] = due_date
53
+ properties["date:Due date:is_datetime"] = 0
54
+
55
+ if task_type:
56
+ properties["Task type"] = task_type
57
+
58
+ if uni:
59
+ properties["Uni"] = uni
60
+
61
+ if description:
62
+ properties["Description"] = description
63
+
64
+ return properties
65
+
66
+
67
+ def parse_deadline_from_text(text: str) -> Optional[str]:
68
+ """
69
+ Extract deadline from natural language text.
70
+
71
+ Examples:
72
+ "by Friday" -> next Friday's date
73
+ "by end of month" -> last day of current month
74
+ "by next Tuesday" -> next Tuesday's date
75
+
76
+ Returns:
77
+ ISO format date string or None
78
+ """
79
+ text_lower = text.lower()
80
+ today = datetime.now()
81
+
82
+ # Day names
83
+ days = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]
84
+
85
+ for i, day in enumerate(days):
86
+ if day in text_lower:
87
+ # Calculate days until that day
88
+ current_day = today.weekday()
89
+ target_day = i
90
+ days_ahead = target_day - current_day
91
+ if days_ahead <= 0:
92
+ days_ahead += 7
93
+ if "next" in text_lower:
94
+ days_ahead += 7
95
+ target_date = today + timedelta(days=days_ahead)
96
+ return target_date.strftime("%Y-%m-%d")
97
+
98
+ if "end of month" in text_lower or "end of the month" in text_lower:
99
+ # Get last day of current month
100
+ if today.month == 12:
101
+ next_month = today.replace(year=today.year + 1, month=1, day=1)
102
+ else:
103
+ next_month = today.replace(month=today.month + 1, day=1)
104
+ last_day = next_month - timedelta(days=1)
105
+ return last_day.strftime("%Y-%m-%d")
106
+
107
+ if "end of week" in text_lower:
108
+ # Friday of current week
109
+ days_until_friday = 4 - today.weekday()
110
+ if days_until_friday < 0:
111
+ days_until_friday += 7
112
+ target_date = today + timedelta(days=days_until_friday)
113
+ return target_date.strftime("%Y-%m-%d")
114
+
115
+ if "tomorrow" in text_lower:
116
+ return (today + timedelta(days=1)).strftime("%Y-%m-%d")
117
+
118
+ if "today" in text_lower:
119
+ return today.strftime("%Y-%m-%d")
120
+
121
+ return None
122
+
123
+
124
+ def infer_project_from_context(text: str, people: List[str] = None) -> Optional[str]:
125
+ """
126
+ Infer which project a task belongs to based on keywords and people mentioned.
127
+
128
+ Args:
129
+ text: The task description or context
130
+ people: List of people mentioned
131
+
132
+ Returns:
133
+ Project name or None
134
+ """
135
+ text_lower = text.lower()
136
+
137
+ # Keyword mapping
138
+ project_keywords = {
139
+ # Map keywords to project names for auto-categorisation
140
+ # "Paper Revision": ["revision", "referee", "resubmit"],
141
+ # "Teaching Prep": ["teaching", "students", "course", "marking"],
142
+ }
143
+
144
+ # People mapping
145
+ people_projects = {
146
+ # Map collaborator names (lowercase) to projects
147
+ # "supervisor_name": "Paper Revision",
148
+ # "collaborator_name": "Joint Project",
149
+ }
150
+
151
+ # Check keywords
152
+ for project, keywords in project_keywords.items():
153
+ if any(kw in text_lower for kw in keywords):
154
+ return project
155
+
156
+ # Check people
157
+ if people:
158
+ for person in people:
159
+ person_lower = person.lower()
160
+ for name, project in people_projects.items():
161
+ if name in person_lower:
162
+ return project
163
+
164
+ return None
165
+
166
+
167
+ def infer_priority_from_context(
168
+ text: str,
169
+ has_deadline: bool = False,
170
+ is_supervisor_request: bool = False,
171
+ is_blocking: bool = False,
172
+ ) -> str:
173
+ """
174
+ Infer task priority based on context clues.
175
+
176
+ Returns:
177
+ "High", "Medium", or "Low"
178
+ """
179
+ text_lower = text.lower()
180
+
181
+ # High priority signals
182
+ high_signals = [
183
+ "urgent", "asap", "immediately", "critical", "important",
184
+ "deadline", "overdue", "waiting on", "blocking"
185
+ ]
186
+
187
+ # Low priority signals
188
+ low_signals = [
189
+ "when you have time", "no rush", "eventually", "nice to have",
190
+ "optional", "someday", "maybe"
191
+ ]
192
+
193
+ if any(signal in text_lower for signal in high_signals):
194
+ return "High"
195
+
196
+ if is_supervisor_request or is_blocking:
197
+ return "High"
198
+
199
+ if has_deadline:
200
+ return "Medium" # At least medium if there's a deadline
201
+
202
+ if any(signal in text_lower for signal in low_signals):
203
+ return "Low"
204
+
205
+ return "Medium" # Default
206
+
207
+
208
+ def extract_action_patterns(text: str) -> List[Dict[str, Any]]:
209
+ """
210
+ Extract potential action items from meeting transcript text.
211
+
212
+ Looks for patterns like:
213
+ - "I'll do X"
214
+ - "I'm going to..."
215
+ - "I need to..."
216
+ - "Can you send..."
217
+ - "We agreed to..."
218
+
219
+ Returns:
220
+ List of dictionaries with 'text', 'assignee', 'type' keys
221
+ """
222
+ actions = []
223
+
224
+ # Patterns that indicate commitments from speaker
225
+ commitment_patterns = [
226
+ r"I'll ([^.!?]+)",
227
+ r"I'm going to ([^.!?]+)",
228
+ r"I need to ([^.!?]+)",
229
+ r"I should ([^.!?]+)",
230
+ r"I can ([^.!?]+)",
231
+ r"I will ([^.!?]+)",
232
+ r"Let me ([^.!?]+)",
233
+ ]
234
+
235
+ # Patterns that indicate requests/asks
236
+ request_patterns = [
237
+ r"[Cc]an you ([^.!?]+)",
238
+ r"[Cc]ould you ([^.!?]+)",
239
+ r"[Pp]lease ([^.!?]+)",
240
+ r"[Ww]ould you ([^.!?]+)",
241
+ ]
242
+
243
+ # Patterns that indicate agreements
244
+ agreement_patterns = [
245
+ r"[Ww]e agreed to ([^.!?]+)",
246
+ r"[Ll]et's ([^.!?]+)",
247
+ r"[Nn]ext step is to ([^.!?]+)",
248
+ r"[Aa]ction item[s]?: ([^.!?]+)",
249
+ ]
250
+
251
+ import re
252
+
253
+ for pattern in commitment_patterns:
254
+ matches = re.findall(pattern, text)
255
+ for match in matches:
256
+ actions.append({
257
+ "text": match.strip(),
258
+ "assignee": "the user",
259
+ "type": "commitment"
260
+ })
261
+
262
+ for pattern in request_patterns:
263
+ matches = re.findall(pattern, text)
264
+ for match in matches:
265
+ actions.append({
266
+ "text": match.strip(),
267
+ "assignee": "the user", # Assume requests are to the user
268
+ "type": "request"
269
+ })
270
+
271
+ for pattern in agreement_patterns:
272
+ matches = re.findall(pattern, text)
273
+ for match in matches:
274
+ actions.append({
275
+ "text": match.strip(),
276
+ "assignee": "the user",
277
+ "type": "agreement"
278
+ })
279
+
280
+ return actions
281
+
282
+
283
+ # Utility functions for Claude integration
284
+ def create_task_prompt(action_items: List[Dict]) -> str:
285
+ """
286
+ Generate a prompt for Claude to create tasks in Notion.
287
+
288
+ Args:
289
+ action_items: List of extracted action items
290
+
291
+ Returns:
292
+ Formatted prompt string
293
+ """
294
+ if not action_items:
295
+ return "No action items to create."
296
+
297
+ prompt = "Please create the following tasks in the Tasks Tracker database:\n\n"
298
+
299
+ for i, item in enumerate(action_items, 1):
300
+ prompt += f"{i}. **{item.get('text', 'Unknown task')}**\n"
301
+ if item.get('deadline'):
302
+ prompt += f" - Due: {item['deadline']}\n"
303
+ if item.get('project'):
304
+ prompt += f" - Project: {item['project']}\n"
305
+ if item.get('context'):
306
+ prompt += f" - Context: {item['context']}\n"
307
+ prompt += "\n"
308
+
309
+ return prompt
310
+
311
+
312
+ if __name__ == "__main__":
313
+ # Test the functions
314
+ print("Testing deadline parsing:")
315
+ print(f" 'by Friday' -> {parse_deadline_from_text('by Friday')}")
316
+ print(f" 'by next Tuesday' -> {parse_deadline_from_text('by next Tuesday')}")
317
+ print(f" 'by end of month' -> {parse_deadline_from_text('by end of month')}")
318
+
319
+ print("\nTesting project inference:")
320
+ print(f" 'journal revision section 4' -> {infer_project_from_context('journal revision section 4')}")
321
+ print(f" 'project keyword test' -> {infer_project_from_context('project keyword test')}")
322
+
323
+ print("\nTesting priority inference:")
324
+ print(f" 'urgent review needed' -> {infer_priority_from_context('urgent review needed')}")
325
+ print(f" 'when you have time' -> {infer_priority_from_context('when you have time')}")
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Helper functions for common OpenAlex query patterns.
4
+
5
+ Provides high-level functions for typical research queries.
6
+ """
7
+
8
+ from typing import List, Dict, Optional, Any
9
+ from biblio_sources import OpenAlexClient
10
+
11
+
12
+ def find_author_works(
13
+ author_name: str,
14
+ client: OpenAlexClient,
15
+ limit: Optional[int] = None
16
+ ) -> List[Dict[str, Any]]:
17
+ """
18
+ Find all works by an author (two-step pattern).
19
+
20
+ Args:
21
+ author_name: Author name to search for
22
+ client: OpenAlexClient instance
23
+ limit: Maximum number of works to return
24
+
25
+ Returns:
26
+ List of works by the author
27
+ """
28
+ # Step 1: Find author ID
29
+ author_response = client._make_request(
30
+ '/authors',
31
+ params={'search': author_name, 'per-page': 1}
32
+ )
33
+
34
+ if not author_response.get('results'):
35
+ print(f"No author found for: {author_name}")
36
+ return []
37
+
38
+ author = author_response['results'][0]
39
+ author_id = author['id'].split('/')[-1] # Extract ID from URL
40
+
41
+ print(f"Found author: {author['display_name']} (ID: {author_id})")
42
+
43
+ # Step 2: Get works by author
44
+ works_params = {
45
+ 'filter': f'authorships.author.id:{author_id}',
46
+ 'per-page': 200
47
+ }
48
+
49
+ if limit and limit <= 200:
50
+ works_params['per-page'] = limit
51
+ response = client._make_request('/works', works_params)
52
+ return response.get('results', [])
53
+ else:
54
+ # Need pagination
55
+ return client.paginate_all('/works', works_params, max_results=limit)
56
+
57
+
58
+ def find_institution_works(
59
+ institution_name: str,
60
+ client: OpenAlexClient,
61
+ limit: Optional[int] = None
62
+ ) -> List[Dict[str, Any]]:
63
+ """
64
+ Find all works from an institution (two-step pattern).
65
+
66
+ Args:
67
+ institution_name: Institution name to search for
68
+ client: OpenAlexClient instance
69
+ limit: Maximum number of works to return
70
+
71
+ Returns:
72
+ List of works from the institution
73
+ """
74
+ # Step 1: Find institution ID
75
+ inst_response = client._make_request(
76
+ '/institutions',
77
+ params={'search': institution_name, 'per-page': 1}
78
+ )
79
+
80
+ if not inst_response.get('results'):
81
+ print(f"No institution found for: {institution_name}")
82
+ return []
83
+
84
+ institution = inst_response['results'][0]
85
+ inst_id = institution['id'].split('/')[-1] # Extract ID from URL
86
+
87
+ print(f"Found institution: {institution['display_name']} (ID: {inst_id})")
88
+
89
+ # Step 2: Get works from institution
90
+ works_params = {
91
+ 'filter': f'authorships.institutions.id:{inst_id}',
92
+ 'per-page': 200
93
+ }
94
+
95
+ if limit and limit <= 200:
96
+ works_params['per-page'] = limit
97
+ response = client._make_request('/works', works_params)
98
+ return response.get('results', [])
99
+ else:
100
+ return client.paginate_all('/works', works_params, max_results=limit)
101
+
102
+
103
+ def find_highly_cited_recent_papers(
104
+ topic: Optional[str] = None,
105
+ years: str = ">2020",
106
+ client: Optional[OpenAlexClient] = None,
107
+ limit: int = 100
108
+ ) -> List[Dict[str, Any]]:
109
+ """
110
+ Find highly cited recent papers, optionally filtered by topic.
111
+
112
+ Args:
113
+ topic: Optional search term for topic filtering
114
+ years: Year filter (e.g., ">2020", "2020-2023")
115
+ client: OpenAlexClient instance
116
+ limit: Maximum number of papers to return
117
+
118
+ Returns:
119
+ List of highly cited papers sorted by citation count
120
+ """
121
+ if client is None:
122
+ client = OpenAlexClient()
123
+
124
+ params = {
125
+ 'filter': f'publication_year:{years}',
126
+ 'sort': 'cited_by_count:desc',
127
+ 'per-page': min(limit, 200)
128
+ }
129
+
130
+ if topic:
131
+ params['search'] = topic
132
+
133
+ if limit <= 200:
134
+ response = client._make_request('/works', params)
135
+ return response.get('results', [])
136
+ else:
137
+ return client.paginate_all('/works', params, max_results=limit)
138
+
139
+
140
+ def get_open_access_papers(
141
+ search_term: str,
142
+ client: OpenAlexClient,
143
+ oa_status: str = "any", # "any", "gold", "green", "hybrid", "bronze"
144
+ limit: int = 100
145
+ ) -> List[Dict[str, Any]]:
146
+ """
147
+ Find open access papers on a topic.
148
+
149
+ Args:
150
+ search_term: Search query
151
+ client: OpenAlexClient instance
152
+ oa_status: Type of OA ("any" for is_oa:true, or specific status)
153
+ limit: Maximum number of papers to return
154
+
155
+ Returns:
156
+ List of open access papers
157
+ """
158
+ if oa_status == "any":
159
+ filter_str = "is_oa:true"
160
+ else:
161
+ filter_str = f"open_access.oa_status:{oa_status}"
162
+
163
+ params = {
164
+ 'search': search_term,
165
+ 'filter': filter_str,
166
+ 'per-page': min(limit, 200)
167
+ }
168
+
169
+ if limit <= 200:
170
+ response = client._make_request('/works', params)
171
+ return response.get('results', [])
172
+ else:
173
+ return client.paginate_all('/works', params, max_results=limit)
174
+
175
+
176
+ def get_publication_trends(
177
+ search_term: Optional[str] = None,
178
+ filter_params: Optional[Dict] = None,
179
+ client: Optional[OpenAlexClient] = None
180
+ ) -> List[Dict[str, Any]]:
181
+ """
182
+ Get publication counts by year.
183
+
184
+ Args:
185
+ search_term: Optional search query
186
+ filter_params: Optional additional filters
187
+ client: OpenAlexClient instance
188
+
189
+ Returns:
190
+ List of {year, count} dictionaries
191
+ """
192
+ if client is None:
193
+ client = OpenAlexClient()
194
+
195
+ params = {'group_by': 'publication_year'}
196
+
197
+ if search_term:
198
+ params['search'] = search_term
199
+
200
+ if filter_params:
201
+ filter_str = ','.join([f"{k}:{v}" for k, v in filter_params.items()])
202
+ params['filter'] = filter_str
203
+
204
+ response = client._make_request('/works', params)
205
+ return response.get('group_by', [])
206
+
207
+
208
+ def analyze_research_output(
209
+ entity_type: str, # 'author' or 'institution'
210
+ entity_name: str,
211
+ client: OpenAlexClient,
212
+ years: str = ">2020"
213
+ ) -> Dict[str, Any]:
214
+ """
215
+ Analyze research output for an author or institution.
216
+
217
+ Args:
218
+ entity_type: 'author' or 'institution'
219
+ entity_name: Name to search for
220
+ client: OpenAlexClient instance
221
+ years: Year filter
222
+
223
+ Returns:
224
+ Dictionary with analysis results
225
+ """
226
+ # Find entity ID
227
+ if entity_type == 'author':
228
+ endpoint = '/authors'
229
+ filter_prefix = 'authorships.author.id'
230
+ else:
231
+ endpoint = '/institutions'
232
+ filter_prefix = 'authorships.institutions.id'
233
+
234
+ # Step 1: Find entity
235
+ entity_response = client._make_request(
236
+ endpoint,
237
+ params={'search': entity_name, 'per-page': 1}
238
+ )
239
+
240
+ if not entity_response.get('results'):
241
+ return {'error': f'No {entity_type} found for: {entity_name}'}
242
+
243
+ entity = entity_response['results'][0]
244
+ entity_id = entity['id'].split('/')[-1]
245
+
246
+ # Step 2: Get statistics
247
+ filter_params = {
248
+ filter_prefix: entity_id,
249
+ 'publication_year': years
250
+ }
251
+
252
+ # Total works
253
+ works_response = client.search_works(
254
+ filter_params=filter_params,
255
+ per_page=1
256
+ )
257
+ total_works = works_response['meta']['count']
258
+
259
+ # Works by year
260
+ trends = client.group_by(
261
+ 'works',
262
+ 'publication_year',
263
+ filter_params={filter_prefix: entity_id, 'publication_year': years}
264
+ )
265
+
266
+ # Top topics
267
+ topics = client.group_by(
268
+ 'works',
269
+ 'topics.id',
270
+ filter_params=filter_params
271
+ )
272
+
273
+ # OA percentage
274
+ oa_works = client.search_works(
275
+ filter_params={**filter_params, 'is_oa': 'true'},
276
+ per_page=1
277
+ )
278
+ oa_count = oa_works['meta']['count']
279
+ oa_percentage = (oa_count / total_works * 100) if total_works > 0 else 0
280
+
281
+ return {
282
+ 'entity_name': entity['display_name'],
283
+ 'entity_id': entity_id,
284
+ 'total_works': total_works,
285
+ 'open_access_works': oa_count,
286
+ 'open_access_percentage': round(oa_percentage, 1),
287
+ 'publications_by_year': trends[:10], # Last 10 years
288
+ 'top_topics': topics[:10] # Top 10 topics
289
+ }
290
+
291
+
292
+ if __name__ == "__main__":
293
+ # Example usage
294
+ import json
295
+
296
+ client = OpenAlexClient(email="your-email@example.com")
297
+
298
+ # Find works by author
299
+ print("\n=== Finding works by author ===")
300
+ works = find_author_works("Einstein", client, limit=5)
301
+ print(f"Found {len(works)} works")
302
+
303
+ # Analyze research output
304
+ print("\n=== Analyzing institution research output ===")
305
+ analysis = analyze_research_output('institution', 'MIT', client)
306
+ print(json.dumps(analysis, indent=2))