flonat-research 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/domain-reviewer.md +336 -0
- package/.claude/agents/fixer.md +226 -0
- package/.claude/agents/paper-critic.md +370 -0
- package/.claude/agents/peer-reviewer.md +289 -0
- package/.claude/agents/proposal-reviewer.md +215 -0
- package/.claude/agents/referee2-reviewer.md +367 -0
- package/.claude/agents/references/journal-referee-profiles.md +354 -0
- package/.claude/agents/references/paper-critic/council-personas.md +77 -0
- package/.claude/agents/references/paper-critic/council-prompts.md +198 -0
- package/.claude/agents/references/peer-reviewer/report-template.md +199 -0
- package/.claude/agents/references/peer-reviewer/sa-prompts.md +260 -0
- package/.claude/agents/references/peer-reviewer/security-scan.md +188 -0
- package/.claude/agents/references/proposal-reviewer/report-template.md +144 -0
- package/.claude/agents/references/proposal-reviewer/sa-prompts.md +149 -0
- package/.claude/agents/references/referee-config.md +114 -0
- package/.claude/agents/references/referee2-reviewer/audit-checklists.md +287 -0
- package/.claude/agents/references/referee2-reviewer/report-template.md +334 -0
- package/.claude/rules/design-before-results.md +52 -0
- package/.claude/rules/ignore-agents-md.md +17 -0
- package/.claude/rules/ignore-gemini-md.md +17 -0
- package/.claude/rules/lean-claude-md.md +45 -0
- package/.claude/rules/learn-tags.md +99 -0
- package/.claude/rules/overleaf-separation.md +67 -0
- package/.claude/rules/plan-first.md +175 -0
- package/.claude/rules/read-docs-first.md +50 -0
- package/.claude/rules/scope-discipline.md +28 -0
- package/.claude/settings.json +125 -0
- package/.context/current-focus.md +33 -0
- package/.context/preferences/priorities.md +36 -0
- package/.context/preferences/task-naming.md +28 -0
- package/.context/profile.md +29 -0
- package/.context/projects/_index.md +41 -0
- package/.context/projects/papers/nudge-exp.md +22 -0
- package/.context/projects/papers/uncertainty.md +31 -0
- package/.context/resources/claude-scientific-writer-review.md +48 -0
- package/.context/resources/cunningham-multi-analyst-agents.md +104 -0
- package/.context/resources/cunningham-multilang-code-audit.md +62 -0
- package/.context/resources/google-ai-co-scientist-review.md +72 -0
- package/.context/resources/karpathy-llm-council-review.md +58 -0
- package/.context/resources/multi-coder-reliability-protocol.md +175 -0
- package/.context/resources/pedro-santanna-takeaways.md +96 -0
- package/.context/resources/venue-rankings/abs_ajg_2024.csv +1823 -0
- package/.context/resources/venue-rankings/abs_ajg_2024_econ.csv +356 -0
- package/.context/resources/venue-rankings/cabs_4_4star_theory.csv +40 -0
- package/.context/resources/venue-rankings/core_2026.csv +801 -0
- package/.context/resources/venue-rankings.md +147 -0
- package/.context/workflows/README.md +69 -0
- package/.context/workflows/daily-review.md +91 -0
- package/.context/workflows/meeting-actions.md +108 -0
- package/.context/workflows/replication-protocol.md +155 -0
- package/.context/workflows/weekly-review.md +113 -0
- package/.mcp-server-biblio/formatters.py +158 -0
- package/.mcp-server-biblio/pyproject.toml +11 -0
- package/.mcp-server-biblio/server.py +678 -0
- package/.mcp-server-biblio/sources/__init__.py +14 -0
- package/.mcp-server-biblio/sources/base.py +73 -0
- package/.mcp-server-biblio/sources/formatters.py +83 -0
- package/.mcp-server-biblio/sources/models.py +22 -0
- package/.mcp-server-biblio/sources/multi_source.py +243 -0
- package/.mcp-server-biblio/sources/openalex_source.py +183 -0
- package/.mcp-server-biblio/sources/scopus_source.py +309 -0
- package/.mcp-server-biblio/sources/wos_source.py +508 -0
- package/.mcp-server-biblio/uv.lock +896 -0
- package/.scripts/README.md +161 -0
- package/.scripts/ai_pattern_density.py +446 -0
- package/.scripts/conf +445 -0
- package/.scripts/config.py +122 -0
- package/.scripts/count_inventory.py +275 -0
- package/.scripts/daily_digest.py +288 -0
- package/.scripts/done +177 -0
- package/.scripts/extract_meeting_actions.py +223 -0
- package/.scripts/focus +176 -0
- package/.scripts/generate-codex-agents-md.py +217 -0
- package/.scripts/inbox +194 -0
- package/.scripts/notion_helpers.py +325 -0
- package/.scripts/openalex/query_helpers.py +306 -0
- package/.scripts/papers +227 -0
- package/.scripts/query +223 -0
- package/.scripts/session-history.py +201 -0
- package/.scripts/skill-health.py +516 -0
- package/.scripts/skill-log-miner.py +273 -0
- package/.scripts/sync-to-codex.sh +252 -0
- package/.scripts/task +213 -0
- package/.scripts/tasks +190 -0
- package/.scripts/week +206 -0
- package/CLAUDE.md +197 -0
- package/LICENSE +21 -0
- package/MEMORY.md +38 -0
- package/README.md +269 -0
- package/docs/agents.md +44 -0
- package/docs/bibliography-setup.md +55 -0
- package/docs/council-mode.md +36 -0
- package/docs/getting-started.md +245 -0
- package/docs/hooks.md +38 -0
- package/docs/mcp-servers.md +82 -0
- package/docs/notion-setup.md +109 -0
- package/docs/rules.md +33 -0
- package/docs/scripts.md +303 -0
- package/docs/setup-overview/setup-overview.pdf +0 -0
- package/docs/skills.md +70 -0
- package/docs/system.md +159 -0
- package/hooks/block-destructive-git.sh +66 -0
- package/hooks/context-monitor.py +114 -0
- package/hooks/postcompact-restore.py +157 -0
- package/hooks/precompact-autosave.py +181 -0
- package/hooks/promise-checker.sh +124 -0
- package/hooks/protect-source-files.sh +81 -0
- package/hooks/resume-context-loader.sh +53 -0
- package/hooks/startup-context-loader.sh +102 -0
- package/package.json +51 -0
- package/packages/cli-council/.github/workflows/claude-code-review.yml +44 -0
- package/packages/cli-council/.github/workflows/claude.yml +50 -0
- package/packages/cli-council/README.md +100 -0
- package/packages/cli-council/pyproject.toml +43 -0
- package/packages/cli-council/src/cli_council/__init__.py +19 -0
- package/packages/cli-council/src/cli_council/__main__.py +185 -0
- package/packages/cli-council/src/cli_council/backends/__init__.py +8 -0
- package/packages/cli-council/src/cli_council/backends/base.py +81 -0
- package/packages/cli-council/src/cli_council/backends/claude.py +25 -0
- package/packages/cli-council/src/cli_council/backends/codex.py +27 -0
- package/packages/cli-council/src/cli_council/backends/gemini.py +26 -0
- package/packages/cli-council/src/cli_council/checkpoint.py +212 -0
- package/packages/cli-council/src/cli_council/config.py +51 -0
- package/packages/cli-council/src/cli_council/council.py +391 -0
- package/packages/cli-council/src/cli_council/models.py +46 -0
- package/packages/llm-council/.github/workflows/claude-code-review.yml +44 -0
- package/packages/llm-council/.github/workflows/claude.yml +50 -0
- package/packages/llm-council/README.md +453 -0
- package/packages/llm-council/pyproject.toml +42 -0
- package/packages/llm-council/src/llm_council/__init__.py +23 -0
- package/packages/llm-council/src/llm_council/__main__.py +259 -0
- package/packages/llm-council/src/llm_council/checkpoint.py +193 -0
- package/packages/llm-council/src/llm_council/client.py +253 -0
- package/packages/llm-council/src/llm_council/config.py +232 -0
- package/packages/llm-council/src/llm_council/council.py +482 -0
- package/packages/llm-council/src/llm_council/models.py +46 -0
- package/packages/mcp-bibliography/MEMORY.md +31 -0
- package/packages/mcp-bibliography/_app.py +226 -0
- package/packages/mcp-bibliography/formatters.py +158 -0
- package/packages/mcp-bibliography/log/2026-03-13-2100.md +35 -0
- package/packages/mcp-bibliography/pyproject.toml +15 -0
- package/packages/mcp-bibliography/run.sh +20 -0
- package/packages/mcp-bibliography/scholarly_formatters.py +83 -0
- package/packages/mcp-bibliography/server.py +1857 -0
- package/packages/mcp-bibliography/tools/__init__.py +28 -0
- package/packages/mcp-bibliography/tools/_registry.py +19 -0
- package/packages/mcp-bibliography/tools/altmetric.py +107 -0
- package/packages/mcp-bibliography/tools/core.py +92 -0
- package/packages/mcp-bibliography/tools/dblp.py +52 -0
- package/packages/mcp-bibliography/tools/openalex.py +296 -0
- package/packages/mcp-bibliography/tools/opencitations.py +102 -0
- package/packages/mcp-bibliography/tools/openreview.py +179 -0
- package/packages/mcp-bibliography/tools/orcid.py +131 -0
- package/packages/mcp-bibliography/tools/scholarly.py +575 -0
- package/packages/mcp-bibliography/tools/unpaywall.py +63 -0
- package/packages/mcp-bibliography/tools/zenodo.py +123 -0
- package/packages/mcp-bibliography/uv.lock +711 -0
- package/scripts/setup.sh +143 -0
- package/skills/beamer-deck/SKILL.md +199 -0
- package/skills/beamer-deck/references/quality-rubric.md +54 -0
- package/skills/beamer-deck/references/review-prompts.md +106 -0
- package/skills/bib-validate/SKILL.md +261 -0
- package/skills/bib-validate/references/council-mode.md +34 -0
- package/skills/bib-validate/references/deep-verify.md +79 -0
- package/skills/bib-validate/references/fix-mode.md +36 -0
- package/skills/bib-validate/references/openalex-verification.md +45 -0
- package/skills/bib-validate/references/preprint-check.md +31 -0
- package/skills/bib-validate/references/ref-manager-crossref.md +41 -0
- package/skills/bib-validate/references/report-template.md +82 -0
- package/skills/code-archaeology/SKILL.md +141 -0
- package/skills/code-review/SKILL.md +265 -0
- package/skills/code-review/references/quality-rubric.md +67 -0
- package/skills/consolidate-memory/SKILL.md +208 -0
- package/skills/context-status/SKILL.md +126 -0
- package/skills/creation-guard/SKILL.md +230 -0
- package/skills/devils-advocate/SKILL.md +130 -0
- package/skills/devils-advocate/references/competing-hypotheses.md +83 -0
- package/skills/init-project/SKILL.md +115 -0
- package/skills/init-project-course/references/memory-and-settings.md +92 -0
- package/skills/init-project-course/references/organise-templates.md +94 -0
- package/skills/init-project-course/skill.md +147 -0
- package/skills/init-project-light/skill.md +139 -0
- package/skills/init-project-research/SKILL.md +368 -0
- package/skills/init-project-research/references/atlas-pipeline-sync.md +70 -0
- package/skills/init-project-research/references/atlas-schema.md +81 -0
- package/skills/init-project-research/references/confirmation-report.md +39 -0
- package/skills/init-project-research/references/domain-profile-template.md +104 -0
- package/skills/init-project-research/references/interview-round3.md +34 -0
- package/skills/init-project-research/references/literature-discovery.md +43 -0
- package/skills/init-project-research/references/scaffold-details.md +197 -0
- package/skills/init-project-research/templates/field-calibration.md +60 -0
- package/skills/init-project-research/templates/pipeline-manifest.md +63 -0
- package/skills/init-project-research/templates/run-all.sh +116 -0
- package/skills/init-project-research/templates/seed-files.md +337 -0
- package/skills/insights-deck/SKILL.md +151 -0
- package/skills/interview-me/SKILL.md +157 -0
- package/skills/latex/SKILL.md +141 -0
- package/skills/latex/references/latex-configs.md +183 -0
- package/skills/latex-autofix/SKILL.md +230 -0
- package/skills/latex-autofix/references/known-errors.md +183 -0
- package/skills/latex-autofix/references/quality-rubric.md +50 -0
- package/skills/latex-health-check/SKILL.md +161 -0
- package/skills/learn/SKILL.md +220 -0
- package/skills/learn/scripts/validate_skill.py +265 -0
- package/skills/lessons-learned/SKILL.md +201 -0
- package/skills/literature/SKILL.md +335 -0
- package/skills/literature/references/agent-templates.md +393 -0
- package/skills/literature/references/bibliometric-apis.md +44 -0
- package/skills/literature/references/cli-council-search.md +79 -0
- package/skills/literature/references/openalex-api-guide.md +371 -0
- package/skills/literature/references/openalex-common-queries.md +381 -0
- package/skills/literature/references/openalex-workflows.md +248 -0
- package/skills/literature/references/reference-manager-sync.md +36 -0
- package/skills/literature/references/scopus-api-guide.md +208 -0
- package/skills/literature/references/wos-api-guide.md +308 -0
- package/skills/multi-perspective/SKILL.md +311 -0
- package/skills/multi-perspective/references/computational-many-analysts.md +77 -0
- package/skills/pipeline-manifest/SKILL.md +226 -0
- package/skills/pre-submission-report/SKILL.md +153 -0
- package/skills/process-reviews/SKILL.md +244 -0
- package/skills/process-reviews/references/rr-routing.md +101 -0
- package/skills/project-deck/SKILL.md +87 -0
- package/skills/project-safety/SKILL.md +135 -0
- package/skills/proofread/SKILL.md +254 -0
- package/skills/proofread/references/quality-rubric.md +104 -0
- package/skills/python-env/SKILL.md +57 -0
- package/skills/quarto-deck/SKILL.md +226 -0
- package/skills/quarto-deck/references/markdown-format.md +143 -0
- package/skills/quarto-deck/references/quality-rubric.md +54 -0
- package/skills/save-context/SKILL.md +174 -0
- package/skills/session-log/SKILL.md +98 -0
- package/skills/shared/concept-validation-gate.md +161 -0
- package/skills/shared/council-protocol.md +265 -0
- package/skills/shared/distribution-diagnostics.md +164 -0
- package/skills/shared/engagement-stratified-sampling.md +218 -0
- package/skills/shared/escalation-protocol.md +74 -0
- package/skills/shared/external-audit-protocol.md +205 -0
- package/skills/shared/intercoder-reliability.md +256 -0
- package/skills/shared/mcp-degradation.md +81 -0
- package/skills/shared/method-probing-questions.md +163 -0
- package/skills/shared/multi-language-conventions.md +143 -0
- package/skills/shared/paid-api-safety.md +174 -0
- package/skills/shared/palettes.md +90 -0
- package/skills/shared/progressive-disclosure.md +92 -0
- package/skills/shared/project-documentation-content.md +443 -0
- package/skills/shared/project-documentation-format.md +281 -0
- package/skills/shared/project-documentation.md +100 -0
- package/skills/shared/publication-output.md +138 -0
- package/skills/shared/quality-scoring.md +70 -0
- package/skills/shared/reference-resolution.md +77 -0
- package/skills/shared/research-quality-rubric.md +165 -0
- package/skills/shared/rhetoric-principles.md +54 -0
- package/skills/shared/skill-design-patterns.md +272 -0
- package/skills/shared/skill-index.md +240 -0
- package/skills/shared/system-documentation.md +334 -0
- package/skills/shared/tikz-rules.md +402 -0
- package/skills/shared/validation-tiers.md +121 -0
- package/skills/shared/venue-guides/README.md +46 -0
- package/skills/shared/venue-guides/cell_press_style.md +483 -0
- package/skills/shared/venue-guides/conferences_formatting.md +564 -0
- package/skills/shared/venue-guides/cs_conference_style.md +463 -0
- package/skills/shared/venue-guides/examples/cell_summary_example.md +247 -0
- package/skills/shared/venue-guides/examples/medical_structured_abstract.md +313 -0
- package/skills/shared/venue-guides/examples/nature_abstract_examples.md +213 -0
- package/skills/shared/venue-guides/examples/neurips_introduction_example.md +245 -0
- package/skills/shared/venue-guides/journals_formatting.md +486 -0
- package/skills/shared/venue-guides/medical_journal_styles.md +535 -0
- package/skills/shared/venue-guides/ml_conference_style.md +556 -0
- package/skills/shared/venue-guides/nature_science_style.md +405 -0
- package/skills/shared/venue-guides/reviewer_expectations.md +417 -0
- package/skills/shared/venue-guides/venue_writing_styles.md +321 -0
- package/skills/split-pdf/SKILL.md +172 -0
- package/skills/split-pdf/methodology.md +48 -0
- package/skills/sync-notion/SKILL.md +93 -0
- package/skills/system-audit/SKILL.md +157 -0
- package/skills/system-audit/references/sub-agent-prompts.md +294 -0
- package/skills/task-management/SKILL.md +131 -0
- package/skills/update-focus/SKILL.md +204 -0
- package/skills/update-project-doc/SKILL.md +194 -0
- package/skills/validate-bib/SKILL.md +242 -0
- package/skills/validate-bib/references/council-mode.md +34 -0
- package/skills/validate-bib/references/deep-verify.md +71 -0
- package/skills/validate-bib/references/openalex-verification.md +45 -0
- package/skills/validate-bib/references/preprint-check.md +31 -0
- package/skills/validate-bib/references/report-template.md +62 -0
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()
|