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/conf ADDED
@@ -0,0 +1,445 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Conference tracking CLI.
4
+
5
+ Usage:
6
+ conf # show upcoming deadlines
7
+ conf --search # open search links in browser
8
+ conf --add # add a new conference
9
+ conf --deadlines # show all deadlines
10
+ conf --remind # weekly reminder check
11
+ """
12
+
13
+ import argparse
14
+ import json
15
+ import os
16
+ import sys
17
+ import webbrowser
18
+ import urllib.request
19
+ import urllib.error
20
+ import urllib.parse
21
+ from datetime import datetime, timedelta
22
+ from pathlib import Path
23
+
24
+
25
+ NOTION_TOKEN = os.environ.get("NOTION_TOKEN")
26
+ DATABASE_ID = "YOUR-CONFERENCES-DATABASE-ID-HERE"
27
+ BASE_DIR = Path(__file__).resolve().parent.parent
28
+ CONTEXT_PATH = BASE_DIR / ".context" / "conferences.md"
29
+
30
+ # Search keywords derived from research topics
31
+ SEARCH_KEYWORDS = [
32
+ # Add your research topic keywords for Google CFP searches
33
+ "your research topic 1",
34
+ "your research topic 2",
35
+ ]
36
+
37
+ TOPICS = [
38
+ # Conference topic tags — match your Notion "Topics" multi-select options
39
+ "Topic A",
40
+ "Topic B",
41
+ ]
42
+
43
+
44
+ def query_conferences(filter_obj=None):
45
+ """Query conferences from Notion."""
46
+ if not NOTION_TOKEN:
47
+ print("❌ NOTION_TOKEN not set")
48
+ sys.exit(1)
49
+
50
+ data = {}
51
+ if filter_obj:
52
+ data["filter"] = filter_obj
53
+
54
+ data["sorts"] = [{"property": "Deadline", "direction": "ascending"}]
55
+
56
+ req = urllib.request.Request(
57
+ f"https://api.notion.com/v1/databases/{DATABASE_ID}/query",
58
+ data=json.dumps(data).encode("utf-8"),
59
+ headers={
60
+ "Authorization": f"Bearer {NOTION_TOKEN}",
61
+ "Content-Type": "application/json",
62
+ "Notion-Version": "2022-06-28",
63
+ },
64
+ method="POST",
65
+ )
66
+
67
+ try:
68
+ with urllib.request.urlopen(req) as response:
69
+ return json.loads(response.read().decode("utf-8"))
70
+ except urllib.error.HTTPError as e:
71
+ error_body = json.loads(e.read().decode("utf-8"))
72
+ print(f"❌ Error: {e.code}")
73
+ print(error_body.get("message", str(error_body)))
74
+ sys.exit(1)
75
+
76
+
77
+ def get_property(page, prop_name, prop_type):
78
+ """Extract property value from a Notion page."""
79
+ prop = page.get("properties", {}).get(prop_name, {})
80
+
81
+ if prop_type == "title":
82
+ title_list = prop.get("title", [])
83
+ return title_list[0].get("plain_text", "") if title_list else ""
84
+ elif prop_type == "select":
85
+ select = prop.get("select")
86
+ return select.get("name", "") if select else ""
87
+ elif prop_type == "multi_select":
88
+ items = prop.get("multi_select", [])
89
+ return [item.get("name", "") for item in items]
90
+ elif prop_type == "date":
91
+ date = prop.get("date")
92
+ return date.get("start", "") if date else ""
93
+ elif prop_type == "url":
94
+ return prop.get("url", "")
95
+ elif prop_type == "rich_text":
96
+ text_list = prop.get("rich_text", [])
97
+ return text_list[0].get("plain_text", "") if text_list else ""
98
+ return ""
99
+
100
+
101
+ def add_conference(name, deadline=None, conf_date=None, topics=None, location=None, url=None, status="Tracking"):
102
+ """Add a conference to Notion."""
103
+ if not NOTION_TOKEN:
104
+ print("❌ NOTION_TOKEN not set")
105
+ sys.exit(1)
106
+
107
+ properties = {
108
+ "Conference": {"title": [{"text": {"content": name}}]},
109
+ "Status": {"select": {"name": status}},
110
+ }
111
+
112
+ if deadline:
113
+ properties["Deadline"] = {"date": {"start": deadline}}
114
+
115
+ if conf_date:
116
+ properties["Conference Date"] = {"date": {"start": conf_date}}
117
+
118
+ if topics:
119
+ properties["Topics"] = {"multi_select": [{"name": t} for t in topics]}
120
+
121
+ if location:
122
+ properties["Location"] = {"rich_text": [{"text": {"content": location}}]}
123
+
124
+ if url:
125
+ properties["URL"] = {"url": url}
126
+
127
+ data = {
128
+ "parent": {"database_id": DATABASE_ID},
129
+ "properties": properties,
130
+ }
131
+
132
+ req = urllib.request.Request(
133
+ "https://api.notion.com/v1/pages",
134
+ data=json.dumps(data).encode("utf-8"),
135
+ headers={
136
+ "Authorization": f"Bearer {NOTION_TOKEN}",
137
+ "Content-Type": "application/json",
138
+ "Notion-Version": "2022-06-28",
139
+ },
140
+ method="POST",
141
+ )
142
+
143
+ try:
144
+ with urllib.request.urlopen(req) as response:
145
+ result = json.loads(response.read().decode("utf-8"))
146
+ print(f"✅ Added: {name}")
147
+ if deadline:
148
+ print(f" Deadline: {deadline}")
149
+ print(f" {result.get('url', '')}")
150
+ return True
151
+ except urllib.error.HTTPError as e:
152
+ error_body = json.loads(e.read().decode("utf-8"))
153
+ print(f"❌ Error: {e.code}")
154
+ print(error_body.get("message", str(error_body)))
155
+ return False
156
+
157
+
158
+ def show_deadlines(upcoming_only=True):
159
+ """Show conference deadlines."""
160
+ today = datetime.now().strftime("%Y-%m-%d")
161
+
162
+ filter_obj = None
163
+ if upcoming_only:
164
+ filter_obj = {
165
+ "and": [
166
+ {"property": "Deadline", "date": {"on_or_after": today}},
167
+ {"property": "Status", "select": {"does_not_equal": "Passed"}}
168
+ ]
169
+ }
170
+
171
+ result = query_conferences(filter_obj)
172
+ pages = result.get("results", [])
173
+
174
+ if not pages:
175
+ print("No upcoming conference deadlines.")
176
+ return
177
+
178
+ print("📅 Conference Deadlines")
179
+ print("=" * 60)
180
+
181
+ for page in pages:
182
+ name = get_property(page, "Conference", "title")
183
+ deadline = get_property(page, "Deadline", "date")
184
+ status = get_property(page, "Status", "select")
185
+ topics = get_property(page, "Topics", "multi_select")
186
+ url = get_property(page, "URL", "url") or get_property(page, "userDefined:URL", "url")
187
+
188
+ # Calculate days until deadline
189
+ if deadline:
190
+ deadline_date = datetime.strptime(deadline, "%Y-%m-%d")
191
+ days_left = (deadline_date - datetime.now()).days
192
+
193
+ if days_left < 0:
194
+ urgency = "⚠️ PASSED"
195
+ elif days_left <= 7:
196
+ urgency = "🔴"
197
+ elif days_left <= 30:
198
+ urgency = "🟡"
199
+ else:
200
+ urgency = "🟢"
201
+
202
+ days_str = f"{days_left}d" if days_left >= 0 else "passed"
203
+ else:
204
+ urgency = "⚪"
205
+ days_str = "no date"
206
+
207
+ print(f"\n{urgency} {name}")
208
+ print(f" Deadline: {deadline or 'TBD'} ({days_str})")
209
+ if topics:
210
+ print(f" Topics: {', '.join(topics)}")
211
+ if status and status != "Tracking":
212
+ print(f" Status: {status}")
213
+ if url:
214
+ print(f" {url}")
215
+
216
+
217
+ def load_search_keywords():
218
+ """Load search keywords from context file."""
219
+ keywords = []
220
+ try:
221
+ with open(CONTEXT_PATH, "r") as f:
222
+ content = f.read()
223
+
224
+ # Extract keywords from the code block under "## Search Keywords"
225
+ if "## Search Keywords" in content:
226
+ section = content.split("## Search Keywords")[1]
227
+ if "```" in section:
228
+ code_block = section.split("```")[1]
229
+ # Remove language identifier if present (e.g., 'python', 'text')
230
+ lines = code_block.strip().split("\n")
231
+ for line in lines:
232
+ line = line.strip()
233
+ if line and not line.startswith("#"):
234
+ keywords.append(line)
235
+ except FileNotFoundError:
236
+ pass
237
+
238
+ return keywords if keywords else [
239
+ "human-AI collaboration",
240
+ "multi-criteria decision",
241
+ "behavioural operations research",
242
+ ]
243
+
244
+
245
+ def open_search_links():
246
+ """Open Google CFP searches in browser."""
247
+ print("🔍 Opening Google CFP searches...")
248
+ print()
249
+
250
+ keywords = load_search_keywords()
251
+ google_base = "https://www.google.com/search"
252
+ year = datetime.now().year
253
+
254
+ # Open first 5 keywords (to not overwhelm)
255
+ for keyword in keywords[:5]:
256
+ query = f'"call for papers" "{keyword}" {year}'
257
+ encoded = urllib.parse.quote(query)
258
+ url = f"{google_base}?q={encoded}"
259
+ print(f" Opening: {keyword}")
260
+ webbrowser.open(url)
261
+
262
+ if len(keywords) > 5:
263
+ print()
264
+ print(f"📝 {len(keywords) - 5} more keywords in context file:")
265
+ for keyword in keywords[5:]:
266
+ print(f" {keyword}")
267
+
268
+ print()
269
+ print(f"💡 Edit keywords: {CONTEXT_PATH}")
270
+ print("💡 Add conferences: conf --add")
271
+
272
+
273
+ def weekly_reminder():
274
+ """Show weekly reminder."""
275
+ today = datetime.now()
276
+ week_ahead = (today + timedelta(days=7)).strftime("%Y-%m-%d")
277
+ month_ahead = (today + timedelta(days=30)).strftime("%Y-%m-%d")
278
+
279
+ print("📆 Weekly Conference Check")
280
+ print("=" * 50)
281
+ print(f"Today: {today.strftime('%A, %d %B %Y')}")
282
+ print()
283
+
284
+ # Get upcoming deadlines
285
+ result = query_conferences({
286
+ "and": [
287
+ {"property": "Deadline", "date": {"on_or_after": today.strftime("%Y-%m-%d")}},
288
+ {"property": "Deadline", "date": {"on_or_before": month_ahead}},
289
+ ]
290
+ })
291
+ pages = result.get("results", [])
292
+
293
+ urgent = [p for p in pages if get_property(p, "Deadline", "date") <= week_ahead]
294
+ upcoming = [p for p in pages if get_property(p, "Deadline", "date") > week_ahead]
295
+
296
+ if urgent:
297
+ print(f"🔴 THIS WEEK ({len(urgent)}):")
298
+ for page in urgent:
299
+ name = get_property(page, "Conference", "title")
300
+ deadline = get_property(page, "Deadline", "date")
301
+ print(f" • {name} — {deadline}")
302
+
303
+ if upcoming:
304
+ print(f"\n🟡 NEXT 30 DAYS ({len(upcoming)}):")
305
+ for page in upcoming:
306
+ name = get_property(page, "Conference", "title")
307
+ deadline = get_property(page, "Deadline", "date")
308
+ print(f" • {name} — {deadline}")
309
+
310
+ if not urgent and not upcoming:
311
+ print("No deadlines in the next 30 days.")
312
+
313
+ print()
314
+ print("📋 Weekly checklist:")
315
+ print(" [ ] Run 'conf --search' to find new CFPs")
316
+ print(" [ ] Add new conferences with 'conf --add'")
317
+ print(" [ ] Review papers ready for submission")
318
+
319
+
320
+ def validate_date(date_str):
321
+ """Validate date format (YYYY-MM-DD). Returns True if valid or empty."""
322
+ if not date_str:
323
+ return True
324
+ try:
325
+ datetime.strptime(date_str, "%Y-%m-%d")
326
+ return True
327
+ except ValueError:
328
+ return False
329
+
330
+
331
+ def get_input(prompt, allow_empty=True, validate_fn=None, error_msg=None):
332
+ """Get input with exit/cancel support and optional validation."""
333
+ while True:
334
+ value = input(prompt).strip()
335
+
336
+ # Allow exit at any point
337
+ if value.lower() in ("exit", "cancel", "q", "quit"):
338
+ print("❌ Cancelled.")
339
+ return None
340
+
341
+ # Check if empty is allowed
342
+ if not value:
343
+ if allow_empty:
344
+ return ""
345
+ print("This field is required. Type 'exit' to cancel.")
346
+ continue
347
+
348
+ # Run validation if provided
349
+ if validate_fn and not validate_fn(value):
350
+ print(error_msg or "Invalid input. Try again or type 'exit' to cancel.")
351
+ continue
352
+
353
+ return value
354
+
355
+
356
+ def interactive_add():
357
+ """Interactively add a conference."""
358
+ print("➕ Add Conference")
359
+ print("-" * 30)
360
+ print("(Type 'exit' at any prompt to cancel)\n")
361
+
362
+ name = get_input("Conference name: ", allow_empty=False)
363
+ if name is None:
364
+ return
365
+
366
+ deadline = get_input(
367
+ "Deadline (YYYY-MM-DD, or press Enter to skip): ",
368
+ validate_fn=validate_date,
369
+ error_msg="Invalid date format. Use YYYY-MM-DD (e.g., 2026-03-15)"
370
+ )
371
+ if deadline is None:
372
+ return
373
+
374
+ conf_date = get_input(
375
+ "Conference date (YYYY-MM-DD, or press Enter to skip): ",
376
+ validate_fn=validate_date,
377
+ error_msg="Invalid date format. Use YYYY-MM-DD (e.g., 2026-06-20)"
378
+ )
379
+ if conf_date is None:
380
+ return
381
+
382
+ location = get_input("Location (or press Enter to skip): ")
383
+ if location is None:
384
+ return
385
+
386
+ url = get_input("URL (or press Enter to skip): ")
387
+ if url is None:
388
+ return
389
+
390
+ print("\nAvailable topics:")
391
+ for i, topic in enumerate(TOPICS, 1):
392
+ print(f" {i}. {topic}")
393
+
394
+ topic_input = get_input("Topics (comma-separated numbers, or press Enter to skip): ")
395
+ if topic_input is None:
396
+ return
397
+
398
+ topics = []
399
+ if topic_input:
400
+ try:
401
+ indices = [int(x.strip()) - 1 for x in topic_input.split(",")]
402
+ topics = [TOPICS[i] for i in indices if 0 <= i < len(TOPICS)]
403
+ except (ValueError, IndexError):
404
+ print("⚠️ Invalid topic selection, skipping topics.")
405
+
406
+ add_conference(
407
+ name=name,
408
+ deadline=deadline or None,
409
+ conf_date=conf_date or None,
410
+ topics=topics or None,
411
+ location=location or None,
412
+ url=url or None,
413
+ )
414
+
415
+
416
+ def main():
417
+ parser = argparse.ArgumentParser(description="Conference tracking")
418
+ parser.add_argument("--search", action="store_true", help="Open search links in browser")
419
+ parser.add_argument("--add", action="store_true", help="Add a new conference")
420
+ parser.add_argument("--deadlines", action="store_true", help="Show all deadlines")
421
+ parser.add_argument("--all", action="store_true", help="Include past conferences")
422
+ parser.add_argument("--remind", action="store_true", help="Weekly reminder")
423
+ parser.add_argument("--topics", action="store_true", help="List available topics")
424
+
425
+ args = parser.parse_args()
426
+
427
+ if args.search:
428
+ open_search_links()
429
+ elif args.add:
430
+ interactive_add()
431
+ elif args.deadlines or args.all:
432
+ show_deadlines(upcoming_only=not args.all)
433
+ elif args.remind:
434
+ weekly_reminder()
435
+ elif args.topics:
436
+ print("Available topics:")
437
+ for topic in TOPICS:
438
+ print(f" • {topic}")
439
+ else:
440
+ # Default: show upcoming deadlines
441
+ show_deadlines(upcoming_only=True)
442
+
443
+
444
+ if __name__ == "__main__":
445
+ main()
@@ -0,0 +1,122 @@
1
+ """
2
+ Configuration for Task Management Scripts
3
+
4
+ This file contains API keys and database IDs needed for automation.
5
+ IMPORTANT: Keep this file secure and don't commit API keys to version control.
6
+ """
7
+
8
+ import os
9
+ from pathlib import Path
10
+
11
+ # Auto-load .env file from project root
12
+ try:
13
+ from dotenv import load_dotenv
14
+ _env_path = Path(__file__).parent.parent / ".env"
15
+ load_dotenv(_env_path)
16
+ except ImportError:
17
+ pass # dotenv not installed, rely on environment variables
18
+
19
+ # Notion Configuration
20
+ # Get your integration token from: https://www.notion.so/my-integrations
21
+ NOTION_TOKEN = os.environ.get("NOTION_TOKEN", "your-notion-integration-token")
22
+
23
+ # Database IDs (extracted from the user's Notion workspace)
24
+ # NOTE: These are Notion page IDs (from URLs). The Notion MCP tools and
25
+ # CLAUDE.md use separate database IDs — both formats are valid.
26
+ DATABASES = {
27
+ "tasks_tracker": "YOUR-TASKS-DATABASE-ID-HERE",
28
+ "research_pipeline": "YOUR-PIPELINE-DATABASE-ID-HERE",
29
+ "conferences": "YOUR-CONFERENCES-DATABASE-ID-HERE",
30
+ }
31
+
32
+ # Page IDs
33
+ PAGES = {
34
+ "dashboard": "YOUR-DASHBOARD-PAGE-ID-HERE",
35
+ }
36
+
37
+ # Local paths
38
+ BASE_DIR = Path(__file__).parent.parent
39
+ CONTEXT_DIR = BASE_DIR / ".context"
40
+ SCRIPTS_DIR = BASE_DIR / ".scripts"
41
+
42
+ # Project mappings (for task categorisation)
43
+ # Keep in sync with .context/projects/_index.md
44
+ PROJECTS = [
45
+ # Add your project names here — keep in sync with .context/projects/_index.md
46
+ "Paper Revision",
47
+ "Literature Review",
48
+ "Conference Submission",
49
+ "Teaching Prep",
50
+ "Personal Admin",
51
+ ]
52
+
53
+ # Source types for tasks
54
+ SOURCES = [
55
+ "Meeting",
56
+ "Email",
57
+ "Supervisor request",
58
+ "Self-initiated",
59
+ "Deadline/calendar",
60
+ "Idea capture",
61
+ ]
62
+
63
+ # Task types
64
+ TASK_TYPES = [
65
+ "🐞 Bug",
66
+ "💬 Feature request",
67
+ "💅 Polish",
68
+ "Claim",
69
+ "📝 Writing",
70
+ "📚 Reading",
71
+ "🔬 Research",
72
+ "📅 Meeting",
73
+ "📋 Admin",
74
+ "📧 Communication",
75
+ ]
76
+
77
+ # Priority levels
78
+ PRIORITIES = ["High", "Medium", "Low"]
79
+
80
+ # Status options (GTD-style)
81
+ STATUSES = ["Inbox", "Not started", "In progress", "Waiting", "Someday", "Done"]
82
+
83
+ # Areas of responsibility (ongoing, no end date)
84
+ AREAS = [
85
+ "Research",
86
+ "Teaching",
87
+ "Career",
88
+ "Personal",
89
+ "Health",
90
+ "Learning",
91
+ ]
92
+
93
+ # Universities
94
+ UNIVERSITIES = [
95
+ # Add your institutions here
96
+ "University A", "University B",
97
+ ]
98
+
99
+
100
+ def validate_config():
101
+ """Check that essential configuration is present."""
102
+ issues = []
103
+
104
+ if NOTION_TOKEN == "your-notion-integration-token":
105
+ issues.append("NOTION_TOKEN not set. Set the NOTION_TOKEN environment variable.")
106
+
107
+ if not CONTEXT_DIR.exists():
108
+ issues.append(f"Context directory not found: {CONTEXT_DIR}")
109
+
110
+ return issues
111
+
112
+
113
+ if __name__ == "__main__":
114
+ issues = validate_config()
115
+ if issues:
116
+ print("Configuration issues:")
117
+ for issue in issues:
118
+ print(f" - {issue}")
119
+ else:
120
+ print("Configuration valid!")
121
+ print(f"Context directory: {CONTEXT_DIR}")
122
+ print(f"Scripts directory: {SCRIPTS_DIR}")