secondbrain-py 0.2.1__py3-none-any.whl

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 (273) hide show
  1. brain/__init__.py +0 -0
  2. brain/__main__.py +18 -0
  3. brain/_capture_command.py +445 -0
  4. brain/_compose.py +52 -0
  5. brain/activity.py +206 -0
  6. brain/ask.py +631 -0
  7. brain/audio.py +591 -0
  8. brain/backfill/__init__.py +12 -0
  9. brain/backfill/search_extras.py +141 -0
  10. brain/backfill/source_rows.py +101 -0
  11. brain/bin/__init__.py +1 -0
  12. brain/bin/_launcher.py +107 -0
  13. brain/bin/down.py +8 -0
  14. brain/bin/launchd.py +268 -0
  15. brain/bin/monitor.py +570 -0
  16. brain/bin/rebuild.py +8 -0
  17. brain/bin/status.py +8 -0
  18. brain/bin/up.py +8 -0
  19. brain/brief.py +272 -0
  20. brain/capture.py +49 -0
  21. brain/chat.py +293 -0
  22. brain/cli.py +9760 -0
  23. brain/cli_claude.py +81 -0
  24. brain/cli_connect.py +285 -0
  25. brain/cli_demo.py +266 -0
  26. brain/config.py +1949 -0
  27. brain/connect.py +925 -0
  28. brain/db.py +540 -0
  29. brain/demo/__init__.py +452 -0
  30. brain/demo/corpus/manifest.json +403 -0
  31. brain/demo/embedder.py +74 -0
  32. brain/durations.py +84 -0
  33. brain/edit_session.py +156 -0
  34. brain/editor.py +67 -0
  35. brain/elicit/__init__.py +16 -0
  36. brain/elicit/detectors.py +250 -0
  37. brain/elicit/drafter.py +70 -0
  38. brain/elicit/queue.py +220 -0
  39. brain/elicit/schema.py +48 -0
  40. brain/elicit/session.py +445 -0
  41. brain/embedding_targets.py +54 -0
  42. brain/embeddings.py +424 -0
  43. brain/enrichment.py +808 -0
  44. brain/errors.py +357 -0
  45. brain/eval/__init__.py +129 -0
  46. brain/eval/answer_eval.py +281 -0
  47. brain/eval/baseline.py +265 -0
  48. brain/eval/concept_extraction.py +378 -0
  49. brain/eval/corpus.py +152 -0
  50. brain/eval/errors.py +19 -0
  51. brain/eval/graph_baseline.py +226 -0
  52. brain/eval/graph_retrieval.py +202 -0
  53. brain/eval/graph_runner.py +319 -0
  54. brain/eval/metrics.py +101 -0
  55. brain/eval/runner.py +223 -0
  56. brain/format.py +783 -0
  57. brain/gaps.py +390 -0
  58. brain/graph_rag/__init__.py +94 -0
  59. brain/graph_rag/_retrieval_common.py +113 -0
  60. brain/graph_rag/aggregates.py +303 -0
  61. brain/graph_rag/aliases/__init__.py +583 -0
  62. brain/graph_rag/backends/__init__.py +10 -0
  63. brain/graph_rag/backends/_age_helpers.py +473 -0
  64. brain/graph_rag/backends/age.py +782 -0
  65. brain/graph_rag/backends/base.py +272 -0
  66. brain/graph_rag/build.py +344 -0
  67. brain/graph_rag/communities.py +644 -0
  68. brain/graph_rag/communities_summary.py +437 -0
  69. brain/graph_rag/concepts.py +202 -0
  70. brain/graph_rag/cooccur.py +193 -0
  71. brain/graph_rag/cross_type.py +312 -0
  72. brain/graph_rag/extract.py +885 -0
  73. brain/graph_rag/fuse.py +371 -0
  74. brain/graph_rag/global_.py +412 -0
  75. brain/graph_rag/grouping.py +372 -0
  76. brain/graph_rag/person_resolver.py +167 -0
  77. brain/graph_rag/reconcile.py +792 -0
  78. brain/graph_rag/relational.py +353 -0
  79. brain/graph_rag/retrieve.py +526 -0
  80. brain/graph_rag/router.py +288 -0
  81. brain/graph_rag/schema.py +320 -0
  82. brain/graph_rag/sync.py +237 -0
  83. brain/graph_rag/tenancy.py +43 -0
  84. brain/graph_rag/themes.py +501 -0
  85. brain/graph_rag/weighting.py +202 -0
  86. brain/ingest/__init__.py +1926 -0
  87. brain/ingest/chunker.py +249 -0
  88. brain/ingest/docx.py +40 -0
  89. brain/ingest/gmail.py +621 -0
  90. brain/ingest/markdown.py +37 -0
  91. brain/ingest/pdf.py +61 -0
  92. brain/ingest/stdin.py +22 -0
  93. brain/ingest/sub_tokens.py +91 -0
  94. brain/ingest/text.py +16 -0
  95. brain/interactions.py +205 -0
  96. brain/maintenance.py +355 -0
  97. brain/mcp_server.py +3405 -0
  98. brain/migrations/001_init.sql +43 -0
  99. brain/migrations/002_qwen3_embedding.sql +17 -0
  100. brain/migrations/003_vault_model.sql +41 -0
  101. brain/migrations/004_relax_content_hash_uniqueness.sql +18 -0
  102. brain/migrations/005_derived_links.sql +67 -0
  103. brain/migrations/006_dedup_file_by_source_path.sql +25 -0
  104. brain/migrations/007_email_thread_and_draft.sql +15 -0
  105. brain/migrations/008_gmail_thread_unique.sql +11 -0
  106. brain/migrations/009_chunks_weighted_tsv.sql +28 -0
  107. brain/migrations/010_interactions.sql +30 -0
  108. brain/migrations/011_documents_summary.sql +23 -0
  109. brain/migrations/012_graphrag.sql +171 -0
  110. brain/migrations/013_graphrag_communities.sql +125 -0
  111. brain/migrations/014_graphrag_community_summary_hash.sql +33 -0
  112. brain/migrations/015_interactions_graph_targets.sql +89 -0
  113. brain/migrations/016_index_hygiene.sql +61 -0
  114. brain/migrations/017_elicit.sql +30 -0
  115. brain/migrations/018_review_gap_signal_kinds.sql +40 -0
  116. brain/migrations/019_search_queries.sql +35 -0
  117. brain/migrations/020_link_suggestions.sql +40 -0
  118. brain/migrations/021_timeline_doc_date.sql +34 -0
  119. brain/migrations/022_link_suggestions_undirected.sql +84 -0
  120. brain/migrations/023_search_queries_fts_count.sql +28 -0
  121. brain/quartz_overrides/__init__.py +8 -0
  122. brain/quartz_overrides/quartz/bootstrap-cli.mjs +65 -0
  123. brain/quartz_overrides/quartz/build.ts +568 -0
  124. brain/quartz_overrides/quartz/cli/args.js +152 -0
  125. brain/quartz_overrides/quartz/cli/build_partial_handler.js +544 -0
  126. brain/quartz_overrides/quartz/cli/handlers.js +636 -0
  127. brain/quartz_overrides/quartz/components/CommandPalette.tsx +172 -0
  128. brain/quartz_overrides/quartz/components/Explorer.tsx +198 -0
  129. brain/quartz_overrides/quartz/components/Footer.tsx +27 -0
  130. brain/quartz_overrides/quartz/components/Graph.tsx +468 -0
  131. brain/quartz_overrides/quartz/components/PageTitle.tsx +72 -0
  132. brain/quartz_overrides/quartz/components/RelatedDocs.tsx +38 -0
  133. brain/quartz_overrides/quartz/components/Search.tsx +161 -0
  134. brain/quartz_overrides/quartz/components/SummaryLede.tsx +72 -0
  135. brain/quartz_overrides/quartz/components/index.ts +92 -0
  136. brain/quartz_overrides/quartz/components/pages/TagContent.tsx +272 -0
  137. brain/quartz_overrides/quartz/components/scripts/commandPalette.inline.ts +665 -0
  138. brain/quartz_overrides/quartz/components/scripts/explorer.inline.ts +768 -0
  139. brain/quartz_overrides/quartz/components/scripts/graph.inline.ts +2302 -0
  140. brain/quartz_overrides/quartz/components/scripts/relatedDocs.inline.ts +163 -0
  141. brain/quartz_overrides/quartz/components/scripts/search.inline.ts +1011 -0
  142. brain/quartz_overrides/quartz/plugins/emitters/contentIndex.ts +546 -0
  143. brain/quartz_overrides/quartz/plugins/transformers/codeCopy.ts +94 -0
  144. brain/quartz_overrides/quartz/plugins/transformers/derivedFenceMark.ts +302 -0
  145. brain/quartz_overrides/quartz/plugins/transformers/emailThread.ts +148 -0
  146. brain/quartz_overrides/quartz/plugins/transformers/emptyDoorFilter.ts +213 -0
  147. brain/quartz_overrides/quartz/plugins/transformers/index.ts +114 -0
  148. brain/quartz_overrides/quartz/plugins/transformers/linkKindMark.ts +205 -0
  149. brain/quartz_overrides/quartz/plugins/transformers/linkSourceTag.ts +104 -0
  150. brain/quartz_overrides/quartz/plugins/transformers/relativeDate.ts +100 -0
  151. brain/quartz_overrides/quartz/plugins/transformers/reloadSignal.ts +131 -0
  152. brain/quartz_overrides/quartz/processors/parse.ts +371 -0
  153. brain/quartz_overrides/quartz/processors/parser_cache.ts +78 -0
  154. brain/quartz_overrides/quartz/static/brain-logo-dark.png +0 -0
  155. brain/quartz_overrides/quartz/static/brain-logo-light.png +0 -0
  156. brain/quartz_overrides/quartz/static/codeCopy.js +196 -0
  157. brain/quartz_overrides/quartz/static/emailThread.js +334 -0
  158. brain/quartz_overrides/quartz/static/favicon.ico +0 -0
  159. brain/quartz_overrides/quartz/static/icon.png +0 -0
  160. brain/quartz_overrides/quartz/static/linkSourceTag.js +104 -0
  161. brain/quartz_overrides/quartz/static/relativeDate.js +142 -0
  162. brain/quartz_overrides/quartz/static/reload.js +168 -0
  163. brain/quartz_overrides/quartz/styles/brain/_article.scss +252 -0
  164. brain/quartz_overrides/quartz/styles/brain/_atmosphere.scss +113 -0
  165. brain/quartz_overrides/quartz/styles/brain/_callouts.scss +180 -0
  166. brain/quartz_overrides/quartz/styles/brain/_cmdk.scss +7 -0
  167. brain/quartz_overrides/quartz/styles/brain/_code.scss +208 -0
  168. brain/quartz_overrides/quartz/styles/brain/_command_palette.scss +369 -0
  169. brain/quartz_overrides/quartz/styles/brain/_email_thread.scss +228 -0
  170. brain/quartz_overrides/quartz/styles/brain/_explorer.scss +142 -0
  171. brain/quartz_overrides/quartz/styles/brain/_home.scss +182 -0
  172. brain/quartz_overrides/quartz/styles/brain/_links.scss +322 -0
  173. brain/quartz_overrides/quartz/styles/brain/_marginalia.scss +117 -0
  174. brain/quartz_overrides/quartz/styles/brain/_motion.scss +175 -0
  175. brain/quartz_overrides/quartz/styles/brain/_people_hub.scss +100 -0
  176. brain/quartz_overrides/quartz/styles/brain/_related_docs.scss +137 -0
  177. brain/quartz_overrides/quartz/styles/brain/_search.scss +252 -0
  178. brain/quartz_overrides/quartz/styles/brain/_sidebar.scss +468 -0
  179. brain/quartz_overrides/quartz/styles/brain/_summary_lede.scss +56 -0
  180. brain/quartz_overrides/quartz/styles/brain/_surface.scss +43 -0
  181. brain/quartz_overrides/quartz/styles/brain/_tag_content.scss +118 -0
  182. brain/quartz_overrides/quartz/styles/brain/_tokens.scss +197 -0
  183. brain/quartz_overrides/quartz/styles/brain/_typography.scss +92 -0
  184. brain/quartz_overrides/quartz/styles/custom.scss +89 -0
  185. brain/quartz_overrides/quartz/styles/graph.scss +505 -0
  186. brain/quartz_overrides/quartz/util/ctx.ts +92 -0
  187. brain/quartz_overrides/quartz/util/fastpath_manifest.ts +608 -0
  188. brain/quartz_overrides/quartz/util/path.ts +358 -0
  189. brain/quartz_overrides/quartz/util/sourceIcons.ts +55 -0
  190. brain/quartz_overrides/quartz.config.ts +270 -0
  191. brain/quartz_overrides/quartz.layout.ts +314 -0
  192. brain/queries.py +1188 -0
  193. brain/rank_fusion.py +8 -0
  194. brain/resurface.py +210 -0
  195. brain/review/__init__.py +26 -0
  196. brain/review/emit.py +27 -0
  197. brain/review/queries.py +436 -0
  198. brain/review/render.py +196 -0
  199. brain/review/scans.py +355 -0
  200. brain/review/weekly.py +413 -0
  201. brain/search.py +704 -0
  202. brain/set_similarity.py +15 -0
  203. brain/setup.py +1205 -0
  204. brain/tags.py +56 -0
  205. brain/templates/Caddyfile.j2 +9 -0
  206. brain/templates/__init__.py +1 -0
  207. brain/templates/bin/__init__.py +1 -0
  208. brain/templates/bin/_brain-brief-fg.sh +25 -0
  209. brain/templates/bin/_brain-build-fg.sh +53 -0
  210. brain/templates/bin/_brain-watcher-fg.sh +65 -0
  211. brain/templates/bin/brain-down.sh +89 -0
  212. brain/templates/bin/brain-status.sh +83 -0
  213. brain/templates/bin/brain-up.sh +221 -0
  214. brain/templates/docker/age/Dockerfile +79 -0
  215. brain/templates/docker-compose.stock.yml.j2 +26 -0
  216. brain/templates/docker-compose.yml.j2 +34 -0
  217. brain/templates/env.example +190 -0
  218. brain/templates/launchd/__init__.py +1 -0
  219. brain/templates/launchd/com.brain.brief.plist.j2 +45 -0
  220. brain/templates/launchd/com.brain.build.plist.j2 +46 -0
  221. brain/templates/launchd/com.brain.watcher.plist.j2 +46 -0
  222. brain/templates/skill/SKILL.md +63 -0
  223. brain/templates/skill/__init__.py +1 -0
  224. brain/timeline.py +834 -0
  225. brain/todo.py +124 -0
  226. brain/uninstall.py +185 -0
  227. brain/vault/__init__.py +115 -0
  228. brain/vault/_atomic.py +25 -0
  229. brain/vault/daily_index.py +228 -0
  230. brain/vault/derived_links/__init__.py +50 -0
  231. brain/vault/derived_links/directory.py +683 -0
  232. brain/vault/derived_links/fence.py +408 -0
  233. brain/vault/derived_links/gws.py +64 -0
  234. brain/vault/derived_links/participants.py +143 -0
  235. brain/vault/derived_links/pass_runner.py +362 -0
  236. brain/vault/derived_links/rules.py +137 -0
  237. brain/vault/export.py +683 -0
  238. brain/vault/frontmatter.py +165 -0
  239. brain/vault/graph.py +620 -0
  240. brain/vault/graph_format.py +388 -0
  241. brain/vault/link_rewrite.py +235 -0
  242. brain/vault/links.py +260 -0
  243. brain/vault/note_builder.py +211 -0
  244. brain/vault/paths.py +55 -0
  245. brain/vault/quartz_overlay.py +236 -0
  246. brain/vault/rename.py +591 -0
  247. brain/vault/resolver.py +304 -0
  248. brain/vault/slug.py +127 -0
  249. brain/vault/sync.py +1513 -0
  250. brain/vault/sync_summaries.py +264 -0
  251. brain/vault/templates.py +145 -0
  252. brain/vault/watch.py +1052 -0
  253. brain/wiki/__init__.py +6 -0
  254. brain/wiki/_github_slugger.py +76 -0
  255. brain/wiki/_person_name.py +314 -0
  256. brain/wiki/build_homepage.py +541 -0
  257. brain/wiki/build_partial.py +273 -0
  258. brain/wiki/build_people.py +934 -0
  259. brain/wiki/build_related.py +758 -0
  260. brain/wiki/build_swap.py +585 -0
  261. brain/wiki/build_watcher.py +975 -0
  262. brain/wiki/edit_classifier.py +215 -0
  263. brain/wiki/errors.py +10 -0
  264. brain/wiki/fastpath_manifest.py +475 -0
  265. brain/wiki/fastpath_state.py +174 -0
  266. brain/wiki/install.py +296 -0
  267. brain/wiki/slug.py +111 -0
  268. secondbrain_py-0.2.1.dist-info/METADATA +195 -0
  269. secondbrain_py-0.2.1.dist-info/RECORD +273 -0
  270. secondbrain_py-0.2.1.dist-info/WHEEL +5 -0
  271. secondbrain_py-0.2.1.dist-info/entry_points.txt +11 -0
  272. secondbrain_py-0.2.1.dist-info/licenses/LICENSE +21 -0
  273. secondbrain_py-0.2.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,79 @@
1
+ # Custom Postgres image for second-brain GraphRAG (task G0-1).
2
+ # PostgreSQL 16 + pgvector (vector) + pgcrypto (contrib) + Apache AGE (openCypher graph).
3
+ #
4
+ # Canonical, packaged source of truth for the AGE image. The repo-root
5
+ # docker-compose.yml does NOT build this — prod stays on stock
6
+ # `pgvector/pgvector:pg16` until the gated AGE cut-over. This image is built only
7
+ # by docker-compose.age-test.yml (the port-5434 test instance, build context
8
+ # ./src/brain/templates/docker/age) and by the rendered $BRAIN_HOME compose:
9
+ # `brain setup` materializes a copy into $BRAIN_HOME/docker/age/Dockerfile so the
10
+ # generated compose can build it there.
11
+ #
12
+ # Why a custom image:
13
+ # - stock `pgvector/pgvector:pg16` has pgvector + pgcrypto but NOT Apache AGE
14
+ # - stock `apache/age:PG16` has AGE but NOT our exact pgvector setup
15
+ # so we start from the pgvector image (keeps the running DB's pgvector ABI/data
16
+ # layout identical) and compile AGE from a pinned release tag against the SAME
17
+ # PG16 server headers, so the resulting .so/.control/.sql match the server exactly.
18
+ #
19
+ # Pins (immutable):
20
+ # - Base: pgvector/pgvector:0.8.2-pg16 (PG16 + pgvector 0.8.2; matches running DB)
21
+ # - AGE : release tag PG16/v1.5.0-rc0 (commit 0048900f372d4dfd5c236f78b8a8453bcdbb458e)
22
+ # NOTE: upstream Apache AGE has NO GA PG16 tag. `PG16/v1.5.0-rc0` is the
23
+ # latest released PG16 tag (the only newer one is `PG16/v1.6.0-rc0`). It is
24
+ # a release candidate, labelled honestly as "1.5.0-rc0" everywhere — not GA.
25
+ #
26
+ # Apache AGE does NOT require `shared_preload_libraries=age` for the per-session
27
+ # `LOAD 'age';` bootstrap used by `brain init` / `connect`. It can be enabled
28
+ # optionally for performance (see docs/specs/2026-05-20-graphrag-age-image.md).
29
+
30
+ FROM pgvector/pgvector:0.8.2-pg16
31
+
32
+ # Pinned Apache AGE source. AGE_COMMIT is the immutable checkout; AGE_REF is the
33
+ # release tag it must resolve to — verified at build time so the build FAILS if the
34
+ # tag ever moves off the pinned commit (drift guard). Override via --build-arg to
35
+ # upgrade (bump BOTH the ref and the commit together).
36
+ ARG AGE_REPO=https://github.com/apache/age.git
37
+ ARG AGE_REF=PG16/v1.5.0-rc0
38
+ ARG AGE_COMMIT=0048900f372d4dfd5c236f78b8a8453bcdbb458e
39
+
40
+ # Build AGE from source against the image's PG16, then drop all build deps so the
41
+ # final image only carries the compiled extension artifacts (lean image).
42
+ RUN set -eux; \
43
+ apt-mark hold locales; \
44
+ apt-get update; \
45
+ apt-get install -y --no-install-recommends \
46
+ build-essential \
47
+ git \
48
+ ca-certificates \
49
+ postgresql-server-dev-16 \
50
+ bison \
51
+ flex \
52
+ libreadline-dev \
53
+ zlib1g-dev; \
54
+ git clone "$AGE_REPO" /tmp/age; \
55
+ # drift guard: the pinned tag MUST resolve to the pinned commit
56
+ test "$(git -C /tmp/age rev-list -n 1 "$AGE_REF")" = "$AGE_COMMIT"; \
57
+ git -C /tmp/age checkout "$AGE_COMMIT"; \
58
+ make -C /tmp/age PG_CONFIG="$(command -v pg_config)"; \
59
+ make -C /tmp/age PG_CONFIG="$(command -v pg_config)" install; \
60
+ # sanity: AGE control file must land where PG16 expects extensions
61
+ test -f "$(pg_config --sharedir)/extension/age.control"; \
62
+ rm -rf /tmp/age; \
63
+ apt-get purge -y --auto-remove \
64
+ build-essential \
65
+ git \
66
+ postgresql-server-dev-16 \
67
+ bison \
68
+ flex \
69
+ libreadline-dev \
70
+ zlib1g-dev; \
71
+ apt-mark unhold locales; \
72
+ rm -rf /var/lib/apt/lists/*
73
+
74
+ LABEL org.opencontainers.image.title="second-brain-pg16-age-pgvector" \
75
+ org.opencontainers.image.description="PostgreSQL 16 + pgvector 0.8.2 + pgcrypto + Apache AGE 1.5.0-rc0 for second-brain GraphRAG" \
76
+ org.opencontainers.image.base.name="pgvector/pgvector:0.8.2-pg16" \
77
+ brain.age.ref="PG16/v1.5.0-rc0" \
78
+ brain.age.commit="0048900f372d4dfd5c236f78b8a8453bcdbb458e" \
79
+ brain.pgvector.version="0.8.2"
@@ -0,0 +1,26 @@
1
+ # Jinja variables: brain_home, pg_port, compose_project, container_name
2
+ # Stock pgvector compose — the FTS/hybrid twin of the committed root
3
+ # docker-compose.yml, with NO AGE build stanza. Used by `brain setup --profile
4
+ # minimal|standard`; the AGE image (docker-compose.yml.j2) is full-profile only.
5
+ name: {{ compose_project }}
6
+ services:
7
+ postgres:
8
+ image: pgvector/pgvector:pg16
9
+ container_name: {{ container_name }}
10
+ # shared_buffers 512MB: the working set exceeds the stock 128MB default,
11
+ # forcing cold FTS re-reads from disk. Applied as a `postgres -c` flag
12
+ # (no custom postgresql.conf). Matches the committed root compose.
13
+ command: postgres -c shared_buffers=512MB
14
+ environment:
15
+ POSTGRES_USER: brain
16
+ POSTGRES_PASSWORD: brain
17
+ POSTGRES_DB: second_brain
18
+ ports:
19
+ - "{{ pg_port }}:5432"
20
+ volumes:
21
+ - {{ brain_home }}/data/postgres:/var/lib/postgresql/data
22
+ healthcheck:
23
+ test: ["CMD-SHELL", "pg_isready -U brain -d second_brain"]
24
+ interval: 5s
25
+ timeout: 3s
26
+ retries: 10
@@ -0,0 +1,34 @@
1
+ # Jinja variables: brain_home, pg_port, compose_project, container_name
2
+ name: {{ compose_project }}
3
+ services:
4
+ postgres:
5
+ # Custom PG16 image: pgvector 0.8.2 + pgcrypto + Apache AGE 1.5.0-rc0 (GraphRAG).
6
+ # `image:` points at the prebuilt multi-arch image on GHCR, so a fresh install
7
+ # can `docker compose pull` it instead of compiling AGE from C source (slow +
8
+ # fragile on Apple Silicon). The `build:` stanza is a LOCAL FALLBACK: `brain
9
+ # setup` materializes the packaged Dockerfile into
10
+ # {{ brain_home }}/docker/age/Dockerfile, so `docker compose up --build`
11
+ # resolves and compiles locally when the registry image is unavailable.
12
+ build:
13
+ context: {{ brain_home }}/docker/age
14
+ dockerfile: Dockerfile
15
+ image: ghcr.io/mshtawythug/second-brain-age:pg16-v1.5.0-rc0-pgv0.8.2
16
+ container_name: {{ container_name }}
17
+ # shared_buffers 512MB: the working set exceeds the stock 128MB default,
18
+ # forcing cold FTS re-reads from disk. Applied as a `postgres -c` flag (no
19
+ # custom postgresql.conf). Matches the stock template + committed root
20
+ # compose; the heavier full profile (graph + FTS + vector) benefits most.
21
+ command: postgres -c shared_buffers=512MB
22
+ environment:
23
+ POSTGRES_USER: brain
24
+ POSTGRES_PASSWORD: brain
25
+ POSTGRES_DB: second_brain
26
+ ports:
27
+ - "{{ pg_port }}:5432"
28
+ volumes:
29
+ - {{ brain_home }}/data/postgres:/var/lib/postgresql/data
30
+ healthcheck:
31
+ test: ["CMD-SHELL", "pg_isready -U brain -d second_brain"]
32
+ interval: 5s
33
+ timeout: 3s
34
+ retries: 10
@@ -0,0 +1,190 @@
1
+ # Embedder backend: arctic (default, free, local) | voyage (paid SaaS)
2
+ # | qwen3 (free, local, China-origin)
3
+ BRAIN_EMBEDDER=arctic
4
+
5
+ # Ollama (required for arctic + qwen3; install: brew install ollama && brew services start ollama)
6
+ # For arctic: ollama pull snowflake-arctic-embed2
7
+ # For qwen3: ollama pull qwen3-embedding:8b
8
+ OLLAMA_HOST=http://localhost:11434
9
+ QWEN3_MODEL=qwen3-embedding:8b
10
+
11
+ # Voyage AI (required only when BRAIN_EMBEDDER=voyage)
12
+ # VOYAGE_API_KEY=
13
+
14
+ # Local Postgres (Docker)
15
+ # DATABASE_URL → prod on the stock pgvector image, port 55432
16
+ # (docker-compose.yml, ./data/postgres bind-mount). `brain setup` substitutes
17
+ # the chosen --port into the {{ pg_port }} placeholder below (default 55432);
18
+ # the same value is rendered into docker-compose.yml so the two never drift.
19
+ # The Apache AGE cutover of the prod container is gated — see
20
+ # docs/specs/2026-05-20-graphrag-age-image.md.
21
+ # TEST_DATABASE_URL → the SEPARATE Apache-AGE test instance on port 5434
22
+ # (docker-compose.age-test.yml, named volume), so the suite runs against the
23
+ # PG16 + pgvector 0.8.2 + pgcrypto + Apache AGE 1.5.0-rc0 superset image.
24
+ # Start it: docker compose -f docker-compose.age-test.yml up -d --build
25
+ DATABASE_URL=postgresql://brain:brain@localhost:{{ pg_port }}/second_brain
26
+ TEST_DATABASE_URL=postgresql://brain:brain@localhost:5434/second_brain_test
27
+
28
+ # Vault folder (default: ~/brain-vault). Override to point elsewhere — e.g.
29
+ # ~/Documents/brain-vault for iCloud-friendly sync on macOS.
30
+ # BRAIN_VAULT_PATH=
31
+
32
+ # Owner email — used by the email-thread reading mode (P4.4) to power the
33
+ # "Show only my replies" filter on `_ingested/gmail/<thread>/` pages. The
34
+ # Quartz build reads this at build time and bakes it into rendered pages
35
+ # as `window.BRAIN_USER_EMAIL`. Leave unset to render the toggle without
36
+ # a user identity (the filter matches nothing).
37
+ # BRAIN_USER_EMAIL=you@example.com
38
+
39
+ # Comma-separated list of identifiers (emails AND/OR display names) that
40
+ # count as the corpus owner. Stripped from participant_keys before
41
+ # derived-edge rules R2 (shared_participant) and R3 (same_day_participant)
42
+ # evaluate, so a meeting/email isn't linked to every other doc the owner
43
+ # is on. Both forms accepted; matching is case-insensitive (entries are
44
+ # trimmed + lowercased at load time). Leave unset for the default
45
+ # behaviour (no stripping).
46
+ # Example: BRAIN_OWNER_PARTICIPANTS="Ali Sarkis,redacted@example.com,redacted@example.com"
47
+ # BRAIN_OWNER_PARTICIPANTS=
48
+
49
+ # Recency boost half-life in days. After RRF, each document's score is
50
+ # multiplied by 0.5 ** (age_days / halflife_days) where age_days comes from
51
+ # coalesce(sent_at, ingested_at). Default 180 days. Set to a very large value
52
+ # (e.g. 999999) to effectively disable the boost. Must be a positive float.
53
+ # BRAIN_RECENCY_HALFLIFE_DAYS=180
54
+
55
+ # Snippet-context expansion budget (tokens). After the best-matching chunk is
56
+ # identified, this many tokens of neighboring-chunk context are stitched
57
+ # around it to give richer reading context. Default 200. Set to 0 to use the
58
+ # legacy single-chunk snippet. Must be a non-negative integer.
59
+ # BRAIN_SNIPPET_CONTEXT_TOKENS=200
60
+
61
+ # Enrichment model (Ollama) for auto-summary + auto-tags. Default
62
+ # llama3.1:8b. Override for higher quality (gemma3:27b — ~60s/doc on M1 Pro,
63
+ # best summarization in the non-Chinese open-weight space) or higher speed
64
+ # (llama3.2:3b — ~4s/doc, looser tags). Switching models causes
65
+ # `brain enrich --backfill` to re-enrich all docs whose summary_model no
66
+ # longer matches the current setting.
67
+ # BRAIN_ENRICH_MODEL=llama3.1:8b
68
+
69
+ # Enrichment HTTP timeout in seconds. Default 60. Raise when running a
70
+ # larger model (gemma3:27b / phi4:14b / mistral-small) — cold-load + long
71
+ # inputs can exceed 60s on Apple Silicon.
72
+ # BRAIN_ENRICH_TIMEOUT_SECONDS=60
73
+
74
+ # Minimum input tokens before a doc is enriched. Docs shorter than this
75
+ # skip enrichment (a 1-paragraph note doesn't benefit from a summary).
76
+ # Default 50. Set to 0 to enrich every doc.
77
+ # BRAIN_ENRICH_MIN_TOKENS=50
78
+
79
+ # Maximum input tokens fed to the enricher. Long docs are head-truncated.
80
+ # Default 4000. Lower to speed up the backfill at the cost of summary
81
+ # fidelity on long docs.
82
+ # BRAIN_ENRICH_MAX_INPUT_TOKENS=4000
83
+
84
+ # --- GraphRAG (Apache AGE entity graph) --------------------------------------
85
+ # Default-ON entity-graph sync alongside the vector/hybrid search. `brain setup`
86
+ # provisions a Postgres image that ships Apache AGE; the committed
87
+ # docker-compose.yml in the repo root stays on stock pgvector and operators flip
88
+ # to AGE via a local docker-compose.override.yml — see
89
+ # docs/specs/2026-05-20-graphrag-age-image.md. On a stock pgvector DB the graph
90
+ # stack auto-degrades to a no-op (init prints "graph skipped — AGE not
91
+ # available"; ingest's graph sync is best-effort and never raises). Names match
92
+ # the design spec §10.
93
+
94
+ # Enable the people-aspect graph sync hook. When true AND the DB ships Apache
95
+ # AGE, every ingest/edit/delete keeps the people graph in lock-step with the
96
+ # documents table; on a stock pgvector DB the sync is a best-effort no-op
97
+ # (silent no-op when AGE is absent; logged + dropped if AGE is present but reconcile raises). Accepts 1/true/yes/on or 0/false/no/off.
98
+ # Default true (the brain setup template provisions an AGE-capable image).
99
+ # Set to false to disable graph sync at ingest time.
100
+ # BRAIN_GRAPH_ENABLED=true
101
+
102
+ # Tenant id stamped on every graph row/vertex/edge and every query. Single-user
103
+ # local deployments leave this at the default. Default "default".
104
+ # BRAIN_GRAPH_TENANT=default
105
+
106
+ # Co-occurrence sliding-window radius (inclusive, unit-agnostic). For the people
107
+ # aspect every participant shares one notional position, so any window >= 1
108
+ # yields the complete graph over a document's persons. Positive integer.
109
+ # Default 3.
110
+ # BRAIN_GRAPH_COOCCUR_WINDOW=3
111
+
112
+ # Per-document entity cap — bounds the O(pairs) co-occurrence work on an
113
+ # entity-dense document. A positive integer, or "none"/"unlimited" to disable
114
+ # the cap. Default 40.
115
+ # BRAIN_GRAPH_MAX_ENTITIES_PER_DOC=40
116
+
117
+ # Generic-entity document-frequency ratio. An entity appearing in more than
118
+ # round(ratio * tenant_corpus_N) documents is treated as generic and its edges
119
+ # are suppressed at derive time. A float in (0.0, 1.0]. Changing it forces a
120
+ # corpus-wide re-derive — run `brain graphrag refresh`. Default 0.30.
121
+ # BRAIN_GRAPH_GENERIC_DF=0.30
122
+
123
+ # --- GraphRAG concept extraction + retrieval ---------------------------------
124
+ # Names match the design spec §10. The concept extractor v2 passed its eval
125
+ # gate (f1 ~0.88 on the local eval set) so the aspect is default-ON.
126
+
127
+ # Enable the concept aspect (LLM entity extraction over topics/projects/orgs/
128
+ # tools) alongside the always-on people aspect. Requires
129
+ # BRAIN_GRAPH_EXTRACT_MODEL to be pullable via `ollama pull <name>` (default
130
+ # llama3.1:8b — ~4.7 GB). Set to false to disable concept extraction (people
131
+ # aspect still works without an LLM). Default true.
132
+ # BRAIN_GRAPH_CONCEPTS=true
133
+
134
+ # Ollama model for the concept entity extractor. Any model pullable via
135
+ # `ollama pull <name>` that supports JSON-mode output. Default llama3.1:8b.
136
+ # BRAIN_GRAPH_EXTRACT_MODEL=llama3.1:8b
137
+
138
+ # Bounded traversal radius for local/themes retrieval (variable-length hops).
139
+ # Positive integer. Default 2.
140
+ # BRAIN_GRAPH_DEPTH=2
141
+
142
+ # Frontier cap — max entities reached per seed during traversal. Positive
143
+ # integer. Default 200.
144
+ # BRAIN_GRAPH_FRONTIER_CAP=200
145
+
146
+ # Per-node expansion fan-out cap during traversal. Positive integer. Default 50.
147
+ # BRAIN_GRAPH_MAX_DEGREE=50
148
+
149
+ # Minimum normalized-lift edge weight admitted into a traversal. A float in
150
+ # [0.0, 1.0]; 0.0 admits every edge. Default 0.20.
151
+ # BRAIN_GRAPH_MIN_EDGE_WEIGHT=0.20
152
+
153
+ # Number of ranked theme groups returned by "themes with X". Positive integer.
154
+ # Default 5.
155
+ # BRAIN_GRAPH_THEME_LIMIT=5
156
+
157
+ # --- GraphRAG global communities (wave G3) -----------------------------------
158
+ # Parsed in wave G3; consumed by `brain graphrag communities build|refresh`
159
+ # (networkx Louvain over the tenant entity graph) and the global retrieval path.
160
+ # §17c pins the migration-013 schema + perf budgets but NOT these tuning
161
+ # defaults — the values below are chosen to be consistent with the other graph
162
+ # knobs.
163
+
164
+ # Louvain resolution. Higher values yield more, smaller communities; lower
165
+ # values yield fewer, larger ones. Positive float. Default 1.0 (networkx
166
+ # default).
167
+ # BRAIN_GRAPH_COMMUNITY_RESOLUTION=1.0
168
+
169
+ # RNG seed for deterministic Louvain detection (so repeated builds over an
170
+ # unchanged graph produce stable communities). Non-negative integer.
171
+ # Default 1234.
172
+ # BRAIN_GRAPH_COMMUNITY_SEED=1234
173
+
174
+ # Minimum members for a community to be materialized — smaller communities are
175
+ # dropped. Positive integer. Default 3.
176
+ # BRAIN_GRAPH_COMMUNITY_MIN_SIZE=3
177
+
178
+ # Jaccard overlap threshold for stable-identity matching across rebuilds: a new
179
+ # community whose member set overlaps an old one by >= this fraction reuses the
180
+ # old community_key. A float in [0.0, 1.0]. Default 0.5.
181
+ # BRAIN_GRAPH_COMMUNITY_JACCARD=0.5
182
+
183
+ # Number of ranked communities returned by the global retrieval mode. Positive
184
+ # integer. Default 5.
185
+ # BRAIN_GRAPH_COMMUNITY_LIMIT=5
186
+
187
+ # Ops safety cap on the number of communities materialized per tenant per build
188
+ # (bounds the summary + embedding cost). A positive integer, or "none"/
189
+ # "unlimited" to disable. Default none (unlimited).
190
+ # BRAIN_GRAPH_COMMUNITY_MAX=none
@@ -0,0 +1 @@
1
+ """Marker so importlib.resources can resolve this subdirectory in pipx-installed wheels."""
@@ -0,0 +1,45 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!-- Jinja variables: brain_home, user_home, pipx_bin_dir, vault_path, brain_py
3
+ The launchd renderer (brain.bin.launchd.render_plist) MUST XML-escape every
4
+ value before substitution (Path strings with '&' / '<' / '>' would otherwise
5
+ produce invalid plist XML and launchctl bootstrap would fail).
6
+
7
+ Unlike the watcher/build agents this is a ONE-SHOT job: StartCalendarInterval
8
+ fires the brief once at 07:00 local each day (no KeepAlive, no RunAtLoad).
9
+ The brief writes the daily digest page; LLM suggestions are best-effort and
10
+ skipped silently if Ollama is down. -->
11
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
12
+ <plist version="1.0">
13
+ <dict>
14
+ <key>Label</key>
15
+ <string>com.brain.brief</string>
16
+ <key>ProgramArguments</key>
17
+ <array>
18
+ <string>{{ brain_home }}/.shims/_brain-brief-fg</string>
19
+ </array>
20
+ <key>EnvironmentVariables</key>
21
+ <dict>
22
+ <key>HOME</key>
23
+ <string>{{ user_home }}</string>
24
+ <key>PATH</key>
25
+ <string>{{ pipx_bin_dir }}:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
26
+ <key>BRAIN_VAULT_PATH</key>
27
+ <string>{{ vault_path }}</string>
28
+ <key>BRAIN_PY</key>
29
+ <string>{{ brain_py }}</string>
30
+ </dict>
31
+ <key>StartCalendarInterval</key>
32
+ <dict>
33
+ <key>Hour</key>
34
+ <integer>7</integer>
35
+ <key>Minute</key>
36
+ <integer>0</integer>
37
+ </dict>
38
+ <key>WorkingDirectory</key>
39
+ <string>{{ brain_home }}</string>
40
+ <key>StandardOutPath</key>
41
+ <string>{{ brain_home }}/logs/com.brain.brief.out.log</string>
42
+ <key>StandardErrorPath</key>
43
+ <string>{{ brain_home }}/logs/com.brain.brief.err.log</string>
44
+ </dict>
45
+ </plist>
@@ -0,0 +1,46 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!-- Jinja variables: brain_home, user_home, pipx_bin_dir, vault_path, brain_py
3
+ T1.7 renderer MUST XML-escape every value before substitution
4
+ (Path strings with '&' / '<' / '>' would otherwise produce invalid
5
+ plist XML and launchctl bootstrap would fail with a parse error). -->
6
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
7
+ <plist version="1.0">
8
+ <dict>
9
+ <key>Label</key>
10
+ <string>com.brain.build</string>
11
+ <key>ProgramArguments</key>
12
+ <array>
13
+ <string>{{ brain_home }}/.shims/_brain-build-fg</string>
14
+ </array>
15
+ <key>EnvironmentVariables</key>
16
+ <dict>
17
+ <key>HOME</key>
18
+ <string>{{ user_home }}</string>
19
+ <key>PATH</key>
20
+ <string>{{ pipx_bin_dir }}:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
21
+ <key>BRAIN_VAULT_PATH</key>
22
+ <string>{{ vault_path }}</string>
23
+ <!-- BRAIN_PY pins the exact Python interpreter that has `brain` importable.
24
+ For a pipx install this is the pipx venv Python
25
+ (e.g. ~/.local/pipx/venvs/secondbrain-py/bin/python).
26
+ T1.7's plist generator resolves and substitutes this variable.
27
+ Without it the shell scripts fall back to system python3 which
28
+ does NOT have brain on sys.path and every `python -m brain.*`
29
+ call would fail with ModuleNotFoundError. -->
30
+ <key>BRAIN_PY</key>
31
+ <string>{{ brain_py }}</string>
32
+ </dict>
33
+ <key>RunAtLoad</key>
34
+ <true/>
35
+ <key>KeepAlive</key>
36
+ <true/>
37
+ <key>ThrottleInterval</key>
38
+ <integer>10</integer>
39
+ <key>WorkingDirectory</key>
40
+ <string>{{ brain_home }}</string>
41
+ <key>StandardOutPath</key>
42
+ <string>{{ brain_home }}/logs/com.brain.build.out.log</string>
43
+ <key>StandardErrorPath</key>
44
+ <string>{{ brain_home }}/logs/com.brain.build.err.log</string>
45
+ </dict>
46
+ </plist>
@@ -0,0 +1,46 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!-- Jinja variables: brain_home, user_home, pipx_bin_dir, vault_path, brain_py
3
+ T1.7 renderer MUST XML-escape every value before substitution
4
+ (Path strings with '&' / '<' / '>' would otherwise produce invalid
5
+ plist XML and launchctl bootstrap would fail with a parse error). -->
6
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
7
+ <plist version="1.0">
8
+ <dict>
9
+ <key>Label</key>
10
+ <string>com.brain.watcher</string>
11
+ <key>ProgramArguments</key>
12
+ <array>
13
+ <string>{{ brain_home }}/.shims/_brain-watcher-fg</string>
14
+ </array>
15
+ <key>EnvironmentVariables</key>
16
+ <dict>
17
+ <key>HOME</key>
18
+ <string>{{ user_home }}</string>
19
+ <key>PATH</key>
20
+ <string>{{ pipx_bin_dir }}:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
21
+ <key>BRAIN_VAULT_PATH</key>
22
+ <string>{{ vault_path }}</string>
23
+ <!-- BRAIN_PY pins the exact Python interpreter that has `brain` importable.
24
+ For a pipx install this is the pipx venv Python
25
+ (e.g. ~/.local/pipx/venvs/secondbrain-py/bin/python).
26
+ T1.7's plist generator resolves and substitutes this variable.
27
+ Without it the shell scripts fall back to system python3 which
28
+ does NOT have brain on sys.path and every `python -m brain.*`
29
+ call would fail with ModuleNotFoundError. -->
30
+ <key>BRAIN_PY</key>
31
+ <string>{{ brain_py }}</string>
32
+ </dict>
33
+ <key>RunAtLoad</key>
34
+ <true/>
35
+ <key>KeepAlive</key>
36
+ <true/>
37
+ <key>ThrottleInterval</key>
38
+ <integer>10</integer>
39
+ <key>WorkingDirectory</key>
40
+ <string>{{ brain_home }}</string>
41
+ <key>StandardOutPath</key>
42
+ <string>{{ brain_home }}/logs/com.brain.watcher.out.log</string>
43
+ <key>StandardErrorPath</key>
44
+ <string>{{ brain_home }}/logs/com.brain.watcher.err.log</string>
45
+ </dict>
46
+ </plist>
@@ -0,0 +1,63 @@
1
+ ---
2
+ description: Query the user's local second-brain knowledge base (career, past meetings, Slack/Gmail/Krisp transcripts) via the `brain` CLI. Use when the user asks about their background, past meetings, past Slack threads, past emails, interview prep, or specific people / projects they've mentioned.
3
+ when_to_use: Trigger on phrases like "what did I say about X", "my thoughts on Y", "meetings about Z", "interview prep", "tell me about my work on W", or any question requiring personal context the user has stored in their second brain.
4
+ ---
5
+
6
+ # Second Brain — `brain` CLI
7
+
8
+ The user has a personal knowledge base at their vault (default
9
+ `~/brain-vault`). Use it whenever a conversation touches their career,
10
+ past meetings, past Slack threads, past emails, interview prep, or
11
+ specific people / projects they've mentioned.
12
+
13
+ **Search** — `brain search "..."` returns the top 5 matching documents
14
+ with snippets and IDs. Use `--json` if you need to parse output.
15
+ Basic filters: `--source krisp|slack|gmail|manual`, `--tag X`,
16
+ `--has-tag X`, `--without-tag X`, `--since N` (days), `--limit N`,
17
+ `--fts-only`.
18
+ Metadata filters: `--person "Name"` (resolved via the directory, same
19
+ as `brain people`), `--after YYYY-MM-DD`, `--before YYYY-MM-DD`,
20
+ `--kind transcript|email|email_thread|note|markdown|pdf|...`,
21
+ `--thread <gmail-thread-id>`, `--draft` (only drafts) or `--no-draft`
22
+ (only published).
23
+ Prefer narrower queries — e.g. `brain search "interview prep"
24
+ --person "person-x" --after 2026-03-01 --kind transcript` is much better
25
+ signal than the same query unfiltered.
26
+
27
+ **Explain a search** — `brain explain "..."` shows per-result FTS rank,
28
+ vector cosine, RRF contributions, and recency boost so you can debug
29
+ why a result ranked where it did. `--verbose` also shows active filters.
30
+
31
+ **Read full** — `brain show <id-prefix>` (6+ chars). Returns the
32
+ document body. `brain show <id> --json` may also include a `summary`
33
+ field if the document has been auto-summarized; the key is omitted on
34
+ docs that haven't been summarized yet.
35
+
36
+ **Browse people** — `brain people` lists people with at least
37
+ `BRAIN_PEOPLE_HUB_MIN_DOCS` appearances in the corpus (default 3, owner
38
+ filtered out via `BRAIN_OWNER_PARTICIPANTS`). `brain people "Name"`
39
+ takes a case-insensitive substring of a display name and lists that
40
+ person's docs.
41
+
42
+ **Action items** — `brain todo` lists open Krisp-meeting action items.
43
+ Flags: `--source krisp` (only source supported today), `--since N`
44
+ (only items from docs ingested in the last N days), `--closed` (include
45
+ closed items, default open only), `--limit/-n N`, `--json`.
46
+
47
+ **Record a rating** — when the user explicitly says a search result was
48
+ useful or irrelevant, call `brain rate <id-prefix> useful` (or
49
+ `irrelevant`) so the interaction is logged. Don't volunteer this — only
50
+ when the user asks.
51
+
52
+ **Ingest Gmail** — `brain ingest-gmail` (requires one or more scope
53
+ flags — never bulk-ingest the inbox). Scope flags: `--query/-q` (raw
54
+ Gmail search), `--label/-l`, `--from`, `--since YYYY/MM/DD`,
55
+ `--until YYYY/MM/DD`. Optional: `--tag/-t`, `--max N` (default 50),
56
+ `--dry-run`, `--no-enrich`.
57
+
58
+ **Ingest from Krisp / Slack** — these don't have CLIs; pipe content via
59
+ `brain ingest-stdin --source krisp|slack --external-id … --title … --date
60
+ …` after fetching from the relevant MCP.
61
+
62
+ **When in doubt:** `brain doctor` confirms it's installed and healthy.
63
+ `brain --help` lists every subcommand.
@@ -0,0 +1 @@
1
+ """Marker so importlib.resources can resolve this subdirectory in pipx-installed wheels."""