brainiac-cli 0.16.0__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 (77) hide show
  1. brain/__init__.py +39 -0
  2. brain/__main__.py +14 -0
  3. brain/_assets/AGENTS.md +564 -0
  4. brain/_assets/overlay/template/brand/brand-guide.md +24 -0
  5. brain/_assets/overlay/template/keywords/glossary.md +15 -0
  6. brain/_assets/overlay/template/people/roster.md +14 -0
  7. brain/_assets/overlay/template/voice/voice-profile.md +34 -0
  8. brain/_assets/routines/manifest.json +257 -0
  9. brain/_assets/scripts/brain-brief-mac.plist +59 -0
  10. brain/_assets/scripts/brain-brief.sh +74 -0
  11. brain/_assets/scripts/brain-synthesis-mac.plist +48 -0
  12. brain/_assets/scripts/brain-synthesis.sh +214 -0
  13. brain/_assets/scripts/install-brief-mac.sh +152 -0
  14. brain/_assets/scripts/install-brief-windows.ps1 +97 -0
  15. brain/_assets/scripts/register_tasks.py +386 -0
  16. brain/_assets/templates/company.md +21 -0
  17. brain/_assets/templates/concept.md +27 -0
  18. brain/_assets/templates/daily.md +20 -0
  19. brain/_assets/templates/decision.md +42 -0
  20. brain/_assets/templates/meeting.md +33 -0
  21. brain/_assets/templates/person.md +20 -0
  22. brain/_assets/templates/project.md +23 -0
  23. brain/_assets/templates/state-moc.md +40 -0
  24. brain/_version.py +12 -0
  25. brain/anchor.py +121 -0
  26. brain/audit.py +422 -0
  27. brain/backup.py +210 -0
  28. brain/brief.py +417 -0
  29. brain/capture.py +117 -0
  30. brain/chunk.py +249 -0
  31. brain/classification.py +134 -0
  32. brain/cli.py +1906 -0
  33. brain/config.py +368 -0
  34. brain/connect.py +362 -0
  35. brain/context.py +108 -0
  36. brain/core.py +3018 -0
  37. brain/doctor.py +1161 -0
  38. brain/egress.py +148 -0
  39. brain/embed.py +857 -0
  40. brain/encryption.py +217 -0
  41. brain/frontmatter.py +102 -0
  42. brain/golden_probe.py +678 -0
  43. brain/graph.py +369 -0
  44. brain/graphify.py +352 -0
  45. brain/index.py +1576 -0
  46. brain/ingest/__init__.py +19 -0
  47. brain/ingest/handlers/__init__.py +43 -0
  48. brain/ingest/handlers/base.py +95 -0
  49. brain/ingest/handlers/docx.py +78 -0
  50. brain/ingest/handlers/email.py +228 -0
  51. brain/ingest/handlers/html.py +142 -0
  52. brain/ingest/handlers/image.py +91 -0
  53. brain/ingest/handlers/pdf.py +99 -0
  54. brain/ingest/handlers/pptx.py +69 -0
  55. brain/ingest/handlers/tables.py +41 -0
  56. brain/ingest/handlers/text.py +43 -0
  57. brain/ingest/handlers/xlsx.py +100 -0
  58. brain/ingest/handlers/zip.py +163 -0
  59. brain/ingest/pipeline.py +839 -0
  60. brain/ingest/transcript.py +158 -0
  61. brain/init.py +870 -0
  62. brain/maintenance.py +2266 -0
  63. brain/mcp_adapter.py +217 -0
  64. brain/multihop.py +232 -0
  65. brain/notes.py +195 -0
  66. brain/overlay.py +183 -0
  67. brain/projection.py +79 -0
  68. brain/rerank.py +425 -0
  69. brain/snapshot.py +231 -0
  70. brain/update.py +743 -0
  71. brain/vectors.py +225 -0
  72. brainiac_cli-0.16.0.dist-info/METADATA +306 -0
  73. brainiac_cli-0.16.0.dist-info/RECORD +77 -0
  74. brainiac_cli-0.16.0.dist-info/WHEEL +5 -0
  75. brainiac_cli-0.16.0.dist-info/entry_points.txt +4 -0
  76. brainiac_cli-0.16.0.dist-info/licenses/LICENSE +202 -0
  77. brainiac_cli-0.16.0.dist-info/top_level.txt +1 -0
brain/vectors.py ADDED
@@ -0,0 +1,225 @@
1
+ """Dense-vector backend ADAPTER INTERFACE + a fallback backend.
2
+
3
+ CORE-01 hardening (r2-codex): "Define an adapter interface with a fallback
4
+ vector backend BEFORE any retrieval code is written." sqlite-vec is pre-v1
5
+ (breaking changes expected) and may fail to load on a locked Windows install or
6
+ an unbuilt Cowork VM. The retrieval layer therefore depends ONLY on the
7
+ ``VectorBackend`` protocol, never on sqlite-vec directly.
8
+
9
+ Two backends ship:
10
+ * ``SqliteVecBackend`` — sqlite-vec ``vec0`` virtual table (fast ANN). Used
11
+ when the extension loads.
12
+ * ``BruteForceBackend`` — vectors stored as BLOBs in a plain table; cosine
13
+ computed in Python. No extension, works with ANY
14
+ sqlite build (including a future SQLCipher build),
15
+ correct everywhere. The guaranteed fallback.
16
+
17
+ ``get_backend()`` selects sqlite-vec when available and degrades to brute force
18
+ otherwise — the caller's retrieval code is identical either way.
19
+ """
20
+ from __future__ import annotations
21
+
22
+ import math
23
+ import sqlite3
24
+ import struct
25
+ from typing import Protocol, Sequence, runtime_checkable
26
+
27
+
28
+ def pack_vector(vec: Sequence[float]) -> bytes:
29
+ return struct.pack(f"{len(vec)}f", *vec)
30
+
31
+
32
+ def unpack_vector(blob: bytes) -> list[float]:
33
+ return list(struct.unpack(f"{len(blob) // 4}f", blob))
34
+
35
+
36
+ def cosine(a: Sequence[float], b: Sequence[float]) -> float:
37
+ dot = sum(x * y for x, y in zip(a, b))
38
+ na = math.sqrt(sum(x * x for x in a))
39
+ nb = math.sqrt(sum(y * y for y in b))
40
+ if na == 0.0 or nb == 0.0:
41
+ return 0.0
42
+ return dot / (na * nb)
43
+
44
+
45
+ @runtime_checkable
46
+ class VectorBackend(Protocol):
47
+ """The only contract the retrieval layer is allowed to depend on."""
48
+
49
+ name: str
50
+
51
+ def setup(self, conn: sqlite3.Connection, dim: int) -> None: ...
52
+ def upsert(self, conn: sqlite3.Connection, rowid: int, vec: Sequence[float]) -> None: ...
53
+ def delete(self, conn: sqlite3.Connection, rowid: int) -> None: ...
54
+ def delete_all(self, conn: sqlite3.Connection) -> None: ...
55
+ def search(
56
+ self, conn: sqlite3.Connection, query: Sequence[float], k: int
57
+ ) -> list[tuple[int, float]]:
58
+ """Return [(rowid, similarity 0..1)] best-first."""
59
+
60
+ def get_vectors(
61
+ self, conn: sqlite3.Connection, rowids: Sequence[int]
62
+ ) -> dict[int, list[float]]:
63
+ """Fetch stored vectors for the given chunk rowids (missing rowids are
64
+ simply absent from the result). Used by retrieval-time diversity /
65
+ near-duplicate suppression, which needs candidate-vs-candidate cosine
66
+ without re-embedding."""
67
+
68
+
69
+ class SqliteVecBackend:
70
+ name = "sqlite-vec"
71
+
72
+ def __init__(self) -> None:
73
+ import sqlite_vec # noqa: F401 (import-time check; raises if absent)
74
+
75
+ self._sqlite_vec = sqlite_vec
76
+ self._dim = 0
77
+
78
+ @staticmethod
79
+ def available() -> bool:
80
+ try:
81
+ import sqlite_vec # noqa: F401
82
+
83
+ return True
84
+ except Exception:
85
+ return False
86
+
87
+ @staticmethod
88
+ def loadable() -> bool:
89
+ """True iff the native ``vec0`` extension actually DLOPENS — not just that
90
+ the Python wrapper imports. A packaged build can ship the ``sqlite_vec``
91
+ Python module but omit the native ``vec0.dylib``/``.so``/``.dll`` (observed
92
+ S10: macOS PyInstaller bundle). ``available()`` (import-only) returns True
93
+ in that case but ``setup()`` then crashes on dlopen — so the ``auto``
94
+ selector probes a real load here and degrades to brute-force on failure."""
95
+ try:
96
+ import sqlite3 as _sq
97
+
98
+ be = SqliteVecBackend()
99
+ con = _sq.connect(":memory:")
100
+ try:
101
+ be.load_into(con)
102
+ return True
103
+ finally:
104
+ con.close()
105
+ except Exception:
106
+ return False
107
+
108
+ def load_into(self, conn: sqlite3.Connection) -> None:
109
+ conn.enable_load_extension(True)
110
+ self._sqlite_vec.load(conn)
111
+ conn.enable_load_extension(False)
112
+
113
+ def setup(self, conn: sqlite3.Connection, dim: int) -> None:
114
+ self._dim = dim
115
+ self.load_into(conn)
116
+ conn.execute("DROP TABLE IF EXISTS vec_index")
117
+ conn.execute(f"CREATE VIRTUAL TABLE vec_index USING vec0(embedding float[{dim}])")
118
+
119
+ def upsert(self, conn: sqlite3.Connection, rowid: int, vec: Sequence[float]) -> None:
120
+ conn.execute("DELETE FROM vec_index WHERE rowid = ?", (rowid,))
121
+ conn.execute(
122
+ "INSERT INTO vec_index(rowid, embedding) VALUES (?, ?)",
123
+ (rowid, pack_vector(vec)),
124
+ )
125
+
126
+ def delete(self, conn: sqlite3.Connection, rowid: int) -> None:
127
+ conn.execute("DELETE FROM vec_index WHERE rowid = ?", (rowid,))
128
+
129
+ def delete_all(self, conn: sqlite3.Connection) -> None:
130
+ conn.execute("DELETE FROM vec_index")
131
+
132
+ def search(self, conn, query, k):
133
+ # Express k as an ``AND k = ?`` MATCH constraint, NOT a bound ``LIMIT ?``.
134
+ # Newer sqlite (>=~3.41) pushes a parameterised LIMIT into vec0's query
135
+ # planner so ``ORDER BY distance LIMIT ?`` works, but older builds (e.g.
136
+ # the Cowork device VM's bundled sqlite) do not, and vec0 then raises
137
+ # "A LIMIT or 'k = ?' constraint is required on vec0 knn queries". The
138
+ # ``k = ?`` form is sqlite-vec's canonical KNN API and is version-robust.
139
+ rows = conn.execute(
140
+ "SELECT rowid, distance FROM vec_index "
141
+ "WHERE embedding MATCH ? AND k = ? ORDER BY distance",
142
+ (pack_vector(query), k),
143
+ ).fetchall()
144
+ # vec0 default metric is L2; convert to a 0..1 similarity for a uniform
145
+ # contract with the brute-force backend.
146
+ return [(int(r[0]), 1.0 / (1.0 + float(r[1]))) for r in rows]
147
+
148
+ def get_vectors(self, conn, rowids):
149
+ if not rowids:
150
+ return {}
151
+ qmarks = ",".join("?" * len(rowids))
152
+ out: dict[int, list[float]] = {}
153
+ for rowid, blob in conn.execute(
154
+ f"SELECT rowid, embedding FROM vec_index WHERE rowid IN ({qmarks})",
155
+ tuple(int(r) for r in rowids),
156
+ ):
157
+ out[int(rowid)] = unpack_vector(blob)
158
+ return out
159
+
160
+
161
+ class BruteForceBackend:
162
+ """Pure-Python fallback. Stores vectors as BLOBs in a normal table and ranks
163
+ by cosine in Python. Slower at scale but correct and dependency-free; the
164
+ SQLCipher-safe path (no loadable extension required)."""
165
+
166
+ name = "brute-force"
167
+
168
+ def setup(self, conn: sqlite3.Connection, dim: int) -> None:
169
+ conn.execute("DROP TABLE IF EXISTS vec_blob")
170
+ conn.execute(
171
+ "CREATE TABLE vec_blob (rowid INTEGER PRIMARY KEY, embedding BLOB NOT NULL)"
172
+ )
173
+
174
+ def upsert(self, conn: sqlite3.Connection, rowid: int, vec: Sequence[float]) -> None:
175
+ conn.execute(
176
+ "INSERT OR REPLACE INTO vec_blob(rowid, embedding) VALUES (?, ?)",
177
+ (rowid, pack_vector(vec)),
178
+ )
179
+
180
+ def delete(self, conn: sqlite3.Connection, rowid: int) -> None:
181
+ conn.execute("DELETE FROM vec_blob WHERE rowid = ?", (rowid,))
182
+
183
+ def delete_all(self, conn: sqlite3.Connection) -> None:
184
+ conn.execute("DELETE FROM vec_blob")
185
+
186
+ def search(self, conn, query, k):
187
+ scored = [
188
+ (int(rowid), cosine(query, unpack_vector(blob)))
189
+ for rowid, blob in conn.execute("SELECT rowid, embedding FROM vec_blob")
190
+ ]
191
+ scored.sort(key=lambda t: t[1], reverse=True)
192
+ return scored[:k]
193
+
194
+ def get_vectors(self, conn, rowids):
195
+ if not rowids:
196
+ return {}
197
+ qmarks = ",".join("?" * len(rowids))
198
+ out: dict[int, list[float]] = {}
199
+ for rowid, blob in conn.execute(
200
+ f"SELECT rowid, embedding FROM vec_blob WHERE rowid IN ({qmarks})",
201
+ tuple(int(r) for r in rowids),
202
+ ):
203
+ out[int(rowid)] = unpack_vector(blob)
204
+ return out
205
+
206
+
207
+ def get_backend(prefer: str = "auto") -> VectorBackend:
208
+ """Adapter selection. ``auto`` uses sqlite-vec if loadable, else brute force.
209
+
210
+ ``prefer`` may also be ``"sqlite-vec"`` (raises if unavailable) or
211
+ ``"brute-force"`` (forces the fallback — used by tests and SQLCipher mode).
212
+ """
213
+ if prefer == "brute-force":
214
+ return BruteForceBackend()
215
+ if prefer == "sqlite-vec":
216
+ return SqliteVecBackend()
217
+ # auto — probe a REAL extension load (not just the import) so a packaged
218
+ # build that ships the wrapper but omits the native vec0 lib degrades to
219
+ # brute-force instead of crashing at setup() (S10 finding).
220
+ if SqliteVecBackend.available() and SqliteVecBackend.loadable():
221
+ try:
222
+ return SqliteVecBackend()
223
+ except Exception:
224
+ pass
225
+ return BruteForceBackend()
@@ -0,0 +1,306 @@
1
+ Metadata-Version: 2.4
2
+ Name: brainiac-cli
3
+ Version: 0.16.0
4
+ Summary: Brainiac — local any-LLM second-brain core engine + brain CLI (Markdown truth, derived SQLite index).
5
+ License-Expression: Apache-2.0
6
+ Requires-Python: >=3.9
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: onnxruntime>=1.17
10
+ Requires-Dist: tokenizers>=0.15
11
+ Requires-Dist: numpy<3,>=1.24
12
+ Requires-Dist: sqlite-vec>=0.1.6
13
+ Requires-Dist: huggingface-hub>=0.20
14
+ Requires-Dist: cryptography>=41
15
+ Requires-Dist: PyYAML>=6
16
+ Requires-Dist: regex>=2023.0.0
17
+ Requires-Dist: keyring>=24; sys_platform == "win32"
18
+ Requires-Dist: pypdf>=4.0
19
+ Requires-Dist: python-docx>=1.1
20
+ Requires-Dist: python-pptx>=0.6.23
21
+ Requires-Dist: openpyxl>=3.1
22
+ Requires-Dist: Pillow>=10
23
+ Provides-Extra: vec
24
+ Requires-Dist: sqlite-vec>=0.1.6; extra == "vec"
25
+ Provides-Extra: audit
26
+ Requires-Dist: cryptography>=41; extra == "audit"
27
+ Provides-Extra: embed
28
+ Requires-Dist: fastembed>=0.3; extra == "embed"
29
+ Provides-Extra: corporate
30
+ Requires-Dist: onnxruntime>=1.17; extra == "corporate"
31
+ Requires-Dist: tokenizers>=0.15; extra == "corporate"
32
+ Requires-Dist: numpy<3,>=1.24; extra == "corporate"
33
+ Requires-Dist: sqlite-vec>=0.1.6; extra == "corporate"
34
+ Requires-Dist: huggingface-hub>=0.20; extra == "corporate"
35
+ Requires-Dist: cryptography>=41; extra == "corporate"
36
+ Requires-Dist: PyYAML>=6; extra == "corporate"
37
+ Provides-Extra: yaml
38
+ Requires-Dist: PyYAML>=6; extra == "yaml"
39
+ Provides-Extra: ingest
40
+ Requires-Dist: pypdf>=4.0; extra == "ingest"
41
+ Requires-Dist: python-docx>=1.1; extra == "ingest"
42
+ Requires-Dist: python-pptx>=0.6.23; extra == "ingest"
43
+ Requires-Dist: openpyxl>=3.1; extra == "ingest"
44
+ Requires-Dist: Pillow>=10; extra == "ingest"
45
+ Provides-Extra: dev
46
+ Requires-Dist: pytest>=7; extra == "dev"
47
+ Requires-Dist: build>=1; extra == "dev"
48
+ Provides-Extra: eval
49
+ Requires-Dist: ranx>=0.3.20; extra == "eval"
50
+ Requires-Dist: numpy>=1.24; extra == "eval"
51
+ Provides-Extra: quant-tools
52
+ Requires-Dist: onnx>=1.14; extra == "quant-tools"
53
+ Provides-Extra: mcp
54
+ Requires-Dist: mcp>=1.0; extra == "mcp"
55
+ Provides-Extra: index
56
+ Requires-Dist: regex>=2023.0.0; extra == "index"
57
+ Dynamic: license-file
58
+
59
+ # Brainiac
60
+
61
+ A local, any-LLM **second brain**: your notes stay plain Markdown + YAML on
62
+ your own disk, and the `brain` CLI gives any LLM harness (Claude Code, Codex,
63
+ Gemini CLI, Claude Desktop / Cowork, ...) fast, sourced search over them — no
64
+ vendor lock-in, no cloud index, no plugin ecosystem to keep alive.
65
+
66
+ ## Why
67
+
68
+ Retrieval-augmented note-taking usually means picking a proprietary app and
69
+ trusting its plugin/embedding pipeline forever. Brainiac inverts that: the
70
+ substrate is just files (`vault/brain/`, `vault/raw/`), the search index is a
71
+ disposable cache you can rebuild any time, and every read goes through a
72
+ deny-by-default classification filter (the **egress gate** — see
73
+ `docs/glossary.md`) before it reaches a model — so you control what an LLM is
74
+ allowed to see, note by note. See `AGENTS.md` for the full conventions and
75
+ security model.
76
+
77
+ ## Installing Brainiac for the first time
78
+
79
+ **Installing? → [`docs/install/README.md`](docs/install/README.md) (pick your
80
+ platform: Claude Code, Cowork, Codex, or Gemini CLI).**
81
+
82
+ The single most common path (Claude Code, on your own machine):
83
+
84
+ ```text
85
+ claude> /plugin marketplace add Autopsias/brainiac
86
+ claude> /plugin install brainiac-manager@brainiac
87
+ claude> /brainiac-install <path-to-your-vault>
88
+ ```
89
+
90
+ **Or the one-command way** (PyPI-first, no clone needed):
91
+
92
+ ```bash
93
+ curl -fsSL https://raw.githubusercontent.com/Autopsias/brainiac/main/install.sh -o /tmp/brainiac-install.sh
94
+ bash /tmp/brainiac-install.sh
95
+ ```
96
+
97
+ `install.sh` tries, in order, `uv tool install`, `pipx install`, then
98
+ `pip install --user` — first success wins, and it tells you which one it
99
+ used. Every channel installs `brainiac-cli[mcp]` so `brain-mcp` (the
100
+ optional Claude Desktop bridge) works out of the box. Plain `pip install
101
+ brainiac-cli[mcp]` works too, without the channel-fallback convenience.
102
+
103
+ **Windows (PowerShell)?** Same install, no bash/WSL required:
104
+
105
+ ```powershell
106
+ irm https://raw.githubusercontent.com/Autopsias/brainiac/main/install.ps1 -OutFile install.ps1
107
+ .\install.ps1
108
+ ```
109
+
110
+ **Contributing to Brainiac, or no PyPI/network access?** Clone and pass
111
+ `--dev` / `-Dev` — an editable install from the checkout, the pre-PyPI
112
+ behavior:
113
+
114
+ ```bash
115
+ git clone https://github.com/Autopsias/brainiac.git && cd brainiac
116
+ ./install.sh --dev # macOS/Linux
117
+ # or: .\install.ps1 -Dev # Windows
118
+ ```
119
+
120
+ The `--dev` path also builds a **lexical-only** index for the checkout's
121
+ bundled sample vault (`BRAIN_EMBEDDER=hash` — no model download, no network
122
+ needed) — try `brain search "arctic-embed vs e5" --json` against it right
123
+ away. The default PyPI-first path has no local sample vault to index — run
124
+ this against your own vault instead (see `docs/install/README.md`):
125
+
126
+ ```bash
127
+ BRAIN_VAULT=<workspace>/vault brain init --full --apply # scaffolds + seeds 3 sample notes AND indexes them
128
+ BRAIN_VAULT=<workspace>/vault brain search "welcome" --json # first search works right away
129
+ ```
130
+
131
+ Either way, semantic search downloads its embedding model (a few hundred MB)
132
+ lazily on first real use, or run `brain warmup` up front.
133
+
134
+ Then ask your first question:
135
+
136
+ ```bash
137
+ brain search "welcome" --json # fresh vault (matches the seeded sample notes)
138
+ brain search "arctic-embed vs e5" --json # --dev checkout's own bundled vault
139
+ ```
140
+
141
+ Every search hit carries the note's file path and its classification tier,
142
+ and an `egress` block reports how many notes were withheld by the
143
+ classification filter.
144
+
145
+ Two things worth knowing about where files live:
146
+
147
+ - **Your notes** live in the vault (`vault/` by default — plain Markdown, the
148
+ single source of truth).
149
+ - **The search index** lives in your per-user app-data folder
150
+ (`~/Library/Application Support/profile-a-brain` on macOS,
151
+ `%LOCALAPPDATA%\profile-a-brain` on Windows, `~/.local/share/...` on
152
+ Linux). It is a derived cache — deleting it loses nothing; `brain rebuild`
153
+ recreates it from the vault.
154
+
155
+ Per-client walkthroughs (Claude Code vs Codex vs Cowork):
156
+ `docs/install/README.md`. Run `brain --help` any time — the CLI is self-describing and is the one
157
+ source of truth for what's shipped.
158
+
159
+ ## Updating an existing install
160
+
161
+ Already installed? Don't re-run the first-time setup — update in place.
162
+
163
+ **Check where you stand (read-only).** In your terminal:
164
+
165
+ ```
166
+ brain doctor
167
+ ```
168
+
169
+ prints one health + version table across every surface — engine venv, CLI
170
+ plugins, staged Cowork workspaces, marketplace cache, and the Desktop/Cowork
171
+ store — each ✅/⚠️ with the exact command to fix anything stale. It changes
172
+ nothing and exits non-zero when a required surface is behind. (Available from
173
+ v0.10.0 onward.)
174
+
175
+ **Bring everything current.** In Claude Code on the host:
176
+
177
+ ```
178
+ /brainiac-update
179
+ ```
180
+
181
+ now **runs** the update instead of printing a checklist: marketplace refresh →
182
+ downgrade-safe CLI-plugin reinstall → engine venv reinstall → every registered
183
+ Cowork workspace re-staged → a final `brain doctor` verify — then a before→after
184
+ version table and one pass/fail. It handles the reconciliation downgrade
185
+ (installed newer than the marketplace) automatically and never touches your
186
+ notes, audit chain, or runtime state. Prefer the terminal? `brain update` is the
187
+ same flow — add `--dry-run` to preview every decision without mutating anything.
188
+ Full detail: **`docs/install/README.md`** (§ Updating).
189
+
190
+ **Installed under the old names?** Marketplace `profile-a-marketplace` and
191
+ plugins `profile-a-kernel`/`profile-a-extras` were renamed to `brainiac` /
192
+ `brainiac-kernel`/`brainiac-extras` (2026-07-11). `brain doctor` flags a
193
+ stale-name registration under either old name. Recovery (add-new-before-
194
+ remove-old — never the reverse):
195
+
196
+ ```
197
+ claude plugin marketplace add Autopsias/brainiac
198
+ claude plugin install brainiac-manager@brainiac
199
+ ```
200
+
201
+ then run `/brainiac-update` — it detects the old-name registrations and
202
+ finishes the migration automatically (installs the new-name plugins,
203
+ verifies the lifecycle skills resolve, then removes the old marketplace and
204
+ plugins). See `docs/adr/0006-distribution-naming.md`.
205
+
206
+ ## Using it with a new project (second vault, third, ...)
207
+
208
+ The install is **per machine**; vaults are **per project**. You never
209
+ reinstall — you point the same `brain` at a different vault folder, and each
210
+ vault automatically gets its own index and audit chain (no configuration):
211
+
212
+ ```bash
213
+ export BRAIN_VAULT=~/vaults/my-new-project # which vault to use
214
+ brain init --full --apply # once per vault: scaffold + seed + index
215
+ ```
216
+
217
+ Full detail (per-vault overlay, the scheduled-task gotcha):
218
+ **`docs/install/second-vault.md`**.
219
+
220
+ ## For technical & security teams
221
+
222
+ A plain summary of what this repo does and doesn't do, for a corporate
223
+ review:
224
+
225
+ - **All data stays on the local disk.** Notes are plain Markdown; the index
226
+ is a local SQLite file. There is no server, no telemetry, no cloud sync,
227
+ and the project holds **no model API keys** — the only egress is whatever
228
+ LLM client the owner already runs.
229
+ - **Deny-by-default egress gate.** Every read command filters notes by their
230
+ `classification` tier before printing; a note with a missing or unknown
231
+ label is treated as most-restrictive and withheld. Scheme:
232
+ `docs/classification-scheme.md`.
233
+ - **Signed audit chain.** Every committed write is Ed25519-signed and
234
+ hash-chained; the key lives in the OS secret store, fail-closed (no file
235
+ fallback). Rotation runbook and stated limitations: `SECURITY.md`.
236
+ - **Trust split.** Untrusted/sandboxed legs (the Cowork Linux VM) get a
237
+ read-only snapshot and a draft inbox — they can never sign, index, or
238
+ mutate the canonical store. `AGENTS.md` §6.
239
+ - **Dependencies.** The default install pulls a small, auditable set
240
+ (onnxruntime, tokenizers, numpy, sqlite-vec, huggingface-hub,
241
+ cryptography, PyYAML, regex — see `pyproject.toml`, which documents why
242
+ each exists). The code degrades gracefully without any of them, so a
243
+ constrained deployment (Intune, air-gapped) can install with
244
+ `pip install --no-deps .` and re-add only what policy allows; an SBOM
245
+ generator ships at `tools/generate_sbom.py`. Offline model provisioning:
246
+ set `$BRAIN_MODEL_CACHE` to a pre-fetched model dir and no download is
247
+ attempted.
248
+ - **License & provenance.** Apache-2.0. Built clean-room; the AGPL project
249
+ consulted as a design reference was never forked or vendored — log and
250
+ audit gate: `docs/clean-room-log.md`, `tools/code_origin_audit.py`.
251
+ - **Vulnerability reporting:** `SECURITY.md`. Deeper notes:
252
+ `docs/SECURITY_NOTES.md`, `docs/operations/`.
253
+
254
+ ## How the AI assistants are wired
255
+
256
+ `AGENTS.md` is the canonical instruction file. `CLAUDE.md` imports it
257
+ verbatim (`@AGENTS.md`) so Claude Code reads the same contract; Codex reads
258
+ `AGENTS.md` natively; Gemini CLI is pointed at it via `.gemini/`. All of them
259
+ call the `brain` CLI through their normal shell — **no MCP required**. The
260
+ one exception is the Claude Desktop **Chat tab** (the only surface that
261
+ can't run a command): for that, `pip install -e ".[mcp]"` adds the optional,
262
+ deletable `brain-mcp` bridge. Full matrix: `docs/harness-wiring.md`.
263
+
264
+ ## More
265
+
266
+ - **`AGENTS.md`** — the conventions/schema every harness reads at startup:
267
+ note shape, link style, capture rules, the four agent-facing verbs
268
+ (search/get/recent/draft-capture), and the security posture.
269
+ - **`docs/install/`** — installation, starting at the
270
+ [platform picker](docs/install/README.md) (Claude Code, Cowork — the Claude
271
+ Desktop Linux VM sandbox client — Codex, Gemini CLI) and
272
+ `docs/install/new-owner.md` for the five-minute "what runs where" mental model.
273
+ - **`docs/glossary.md`** — one-line definitions for the jargon used across
274
+ these docs (PARA, MNPI, egress gate, Cowork, host-broker, overlay, ...).
275
+ - **`SECURITY.md`** — vulnerability reporting, supported versions, audit-key
276
+ rotation.
277
+ - **`LICENSE`** — Apache-2.0.
278
+
279
+ ## Layout
280
+
281
+ ```
282
+ AGENTS.md conventions + frontmatter schema
283
+ src/brain/ the brain CLI + engine (index, search, audit, ...)
284
+ docs/ specs (substrate, classification, install, security notes)
285
+ tools/validate.py conventions validator (stdlib-only; PyYAML optional)
286
+ vault/ the tiny sample vault used in the quickstart above
287
+ raw/ immutable captured sources
288
+ brain/ agent-owned atomic notes + index.md + generated backlinks.md
289
+ .brain/ per-vault runtime (published snapshot, capture inbox) — gitignored
290
+ ```
291
+
292
+ ## Validate
293
+
294
+ ```bash
295
+ python3 tools/validate.py vault # exit 0 = conventions clean
296
+ python3 tools/validate.py vault --backlinks # regenerate brain/backlinks.md
297
+ python3 tools/validate.py vault --okf # + optional OKF lint
298
+ ```
299
+
300
+ ## Scope note
301
+
302
+ Substrate readiness is not the same as operational cutover. This repo makes
303
+ the substrate *ready* to replace an existing tool (e.g. Obsidian + Smart
304
+ Connections) and emits the cutover hooks (`docs/corpus-migration.md`,
305
+ `docs/dependency-inventory.md`); actually retiring your old setup is a
306
+ separate, owner-specific step. See `AGENTS.md` §7.
@@ -0,0 +1,77 @@
1
+ brain/__init__.py,sha256=X1-_-A7-DOJrM53MjcgFvkV6gyTmMcKxq0adY_Ve-pk,1870
2
+ brain/__main__.py,sha256=RnmXo52qFPkq9WQH2i-PTZZdpKYdfkmo9PaQ2Ncvdx4,431
3
+ brain/_version.py,sha256=IVFuOcK8mkureEFjMM6_nu3-Yad3LY6tBzCF3H1u5TQ,621
4
+ brain/anchor.py,sha256=0ThDeoXmI3CDc2oF4qMzPeZh34b5hE0VU6TUOtMIuNI,5148
5
+ brain/audit.py,sha256=HkA2Z2Gw9X4vfV-t-Y7EDsP2-EC6N_yApsbckN5DvKc,16681
6
+ brain/backup.py,sha256=_oWERCSKaSHp-ku0oY1118peXhGs7tzRnQfNiR7nEpQ,8868
7
+ brain/brief.py,sha256=SRkFuw9Ic3sVoqRGsOQM3JZLPlCDeDUJWFvGdCGKbe8,16802
8
+ brain/capture.py,sha256=UWZPSho_4IxxHic9inS5BAY9k2jl5zZGhnNh9P5h5RQ,3997
9
+ brain/chunk.py,sha256=r6iTnx2sF86wyN8i4jx7EYfy-R08Cf2hwbd4Ly0y9jI,10089
10
+ brain/classification.py,sha256=ihR5fwUR76Pud18dYoCqP0X_6YY20q60f7tBvztvfJs,5659
11
+ brain/cli.py,sha256=4EaWYpswVCWKXVpp6CD5_Q5EoE-IqTqp2ChN9S64ISQ,98442
12
+ brain/config.py,sha256=Zk2EmS0aoth8fl1r63iGBSi420qXjYYFpwsad3-91KQ,17646
13
+ brain/connect.py,sha256=WGXEkthfT-G-2IMAwSHVI7_PaYyITeLz_Z18UE8Bf6Q,15471
14
+ brain/context.py,sha256=e25fvfyFzi5JqdIzFMrJwGqVFjygp6M8yRixQVZuvuU,4707
15
+ brain/core.py,sha256=KKf-AKcUBCkIAzQzI6O7V1lvwfSI7O9fptenxpJMo04,159636
16
+ brain/doctor.py,sha256=u94HD2LDDNV2tKWK1y0HZecLU-JRlSiEaGoxKAxA9NA,57560
17
+ brain/egress.py,sha256=EGcxDkdE1OeDxR8bI0G9Tg6mqJPe6CQrOBXOU-NsjO8,7476
18
+ brain/embed.py,sha256=zfQmiAxoCJaF3WYCu5A1PFk2GywdkP09pd24oNkg0zo,38550
19
+ brain/encryption.py,sha256=zRZ9mg4E_kBD_0Y3HDF-4D3sngy2wPaLV9zn3m6PeM4,8968
20
+ brain/frontmatter.py,sha256=3s39Y9hEBTebTZ1fCGVbm9D7KDUtGJsvlhtIQYwEAGY,3624
21
+ brain/golden_probe.py,sha256=M-paNnwMS7nt88IZHKJ6RMfVIrq71EH2iy3MEuKMX5U,32963
22
+ brain/graph.py,sha256=VKNGg3yHV-wg7VSudKlc4dQlfBv6DkGKqzkNhVmO1V4,14807
23
+ brain/graphify.py,sha256=yjxN7w5d6aZlu1Tj93BHMR7KumIPShgtVDEIW4iwiWM,16040
24
+ brain/index.py,sha256=7VnYmgwqxbfoXRRAXl1vnLSPn9yOICfFAwVUqD7NGzk,77479
25
+ brain/init.py,sha256=_8bX4o_QH0jzYJaYdZ2KvzTXapmCvM1l1n6PpCEs4Lw,36393
26
+ brain/maintenance.py,sha256=uR3MCFjE16pOmbl5SaOs_tPrNg4BMASo_2-8Pe0STqk,107955
27
+ brain/mcp_adapter.py,sha256=yDUPVsWm7CroJTH1U_AAzExRavX3dBfxJRmtwYpxfs0,11400
28
+ brain/multihop.py,sha256=SEOYB1fNY3YPyPd9LhOFRva4sV-Ifl16NQn1i9IrEGE,9683
29
+ brain/notes.py,sha256=7otaNlAfamsfkBdfVDMptFHX_E_jd6koIZMNHoT7Y8c,7632
30
+ brain/overlay.py,sha256=9rsuS3STthgshYkcNUPL5bI7w3xJlok_l0LuED279Hk,6966
31
+ brain/projection.py,sha256=XyAZPBAoZ5MQLgCd2jBgNgyTMGjV3HcWGPIoEHLRlfk,2885
32
+ brain/rerank.py,sha256=yyMp80zsguvYM_hztHhEmpy4Tvg4BMVImvVTSZ73PGw,18399
33
+ brain/snapshot.py,sha256=g7BCq2ofzDZYvoiiGx-1gEWS1YbV5YhXFgOu9do3Pi8,8199
34
+ brain/update.py,sha256=FyBfTNZ8-y5f9VrwfnJElN4SCzY0RmNsS4hO0xS5qu4,37039
35
+ brain/vectors.py,sha256=EdBRBI6_rAcbelGU1QOpZEld01wgJGyOlkoeY7lIQ0s,8813
36
+ brain/_assets/AGENTS.md,sha256=MsbBXm_Vdmp-1lr41Tmgvdvgoi0zu60-QVO043DOr4E,33235
37
+ brain/_assets/overlay/template/brand/brand-guide.md,sha256=osiarUtl0BDdy-FkdIKrO4TxYWwyXLopqpjaf-BzW3U,735
38
+ brain/_assets/overlay/template/keywords/glossary.md,sha256=L5pHOlGNDFeUJKy2Gkpr9Pm9yRWPULdGOgZRC8S-vJI,387
39
+ brain/_assets/overlay/template/people/roster.md,sha256=ZO8NOvkqY3vqU9y-KjSQAYw8jICwlgNxMk_6CZLxUUk,377
40
+ brain/_assets/overlay/template/voice/voice-profile.md,sha256=srRWsWz11TEmB4N0qH4upfetlY8HPdGtFwwyOeRzHBI,932
41
+ brain/_assets/routines/manifest.json,sha256=ZfNIFDsokQyUqkcTuajjjN4bg-fUIT9Ied_3eeuwnOk,23599
42
+ brain/_assets/scripts/brain-brief-mac.plist,sha256=yUCCyTbG2ssocvC4NUXNHB_NWjzEbg3Le_mW5VqtNKg,2665
43
+ brain/_assets/scripts/brain-brief.sh,sha256=1fh4o9s9nDqQ9Pu9Xps-pAFfXtKp7OEaS77f51HdBzs,3896
44
+ brain/_assets/scripts/brain-synthesis-mac.plist,sha256=kLA__aADJi6C2YtkUWPJRiL-9r3DyGCPu88w1yp-11I,1715
45
+ brain/_assets/scripts/brain-synthesis.sh,sha256=gpSUJUfCfRiIgRwFGi08pR1THhHp2RVCNwMB9JR4uYs,9886
46
+ brain/_assets/scripts/install-brief-mac.sh,sha256=zw2T_I28nXAknNyX1ep3K3iozN9Fc_DhioWvOT6QDzQ,7048
47
+ brain/_assets/scripts/install-brief-windows.ps1,sha256=VE6SJg64hON8O6K1_7AWey0qa6KDILa9zyj9mZsNQs0,4665
48
+ brain/_assets/scripts/register_tasks.py,sha256=XDWtn6WpBZ7d9RRqpcI-zYxe6c3bb6zc0Qtyb3WSnD8,17838
49
+ brain/_assets/templates/company.md,sha256=ZFjeepGAoFdZlvtmPg19__5C-_pe9HH7oiRnl64yE-k,231
50
+ brain/_assets/templates/concept.md,sha256=852qP9pqhwtOLblwYlkLnhFr9GgpNvaqR7eqptabA0g,438
51
+ brain/_assets/templates/daily.md,sha256=G0t_oUTlPBYS8nQK7_gt1Rb2FaZZPtRIX4fHerBoHrc,217
52
+ brain/_assets/templates/decision.md,sha256=Bkb-dHXTjZ6x_ENVqxdwRIJ8v126SRJ8mCZBNWlQXIw,914
53
+ brain/_assets/templates/meeting.md,sha256=tzLwoihXdUXv3aXoJyNKPifOPT2xXWK5UR-CGCzbh8c,531
54
+ brain/_assets/templates/person.md,sha256=ENbuUsR79EdVebLhyEu4REgCFg1MlmoUqnFR2tHj9bI,228
55
+ brain/_assets/templates/project.md,sha256=gZYTGUqJz_woVZqTWju-E3hkDlv7-2b4aU-SgEEGsr4,241
56
+ brain/_assets/templates/state-moc.md,sha256=ZSusJ73sUpMF6uCYFdUeWw-XvivYiOruAwKRpXpd0J4,1009
57
+ brain/ingest/__init__.py,sha256=YWIwBJXGDwj59BKrJPd-QZvXBYRC6kI0omDC2QT2sCI,918
58
+ brain/ingest/pipeline.py,sha256=tlikO2z4hppJaNGLqRP75b-j-EKcyBkBEOIrvH5H7V8,38009
59
+ brain/ingest/transcript.py,sha256=dtT3rAj0Wm147XGhw5sui5fIFdrtx7aIc13QhnxJKjY,6430
60
+ brain/ingest/handlers/__init__.py,sha256=0iU29LmHTi-glc5tmyunXAHoqVtol1wTC7hxaGhevIw,1321
61
+ brain/ingest/handlers/base.py,sha256=2gFybMPzzx7ixiNh9BsD--4IUQ-GPWi4OtNsFOnaO2c,3619
62
+ brain/ingest/handlers/docx.py,sha256=KiQl-6khxEZsHHNzml2xoqo58owKXGYBqQ534zzR7UU,2983
63
+ brain/ingest/handlers/email.py,sha256=ky3q68Ya5VuaBjvN1QJtzBDqAP-61GRdsw8QZvj5iW0,8291
64
+ brain/ingest/handlers/html.py,sha256=KKwaF_RrgmA9dextJVYqSrpBeRUgLg52XUst_lS5XVM,5022
65
+ brain/ingest/handlers/image.py,sha256=UC7CbyMYpMhoR6b-RLkbrt9Xebr5NlNINi3pbPQHuGI,3373
66
+ brain/ingest/handlers/pdf.py,sha256=b3EJw8XTHatfgZSiUYH9MlhdJ_m5u96lomNy12F0EXY,3655
67
+ brain/ingest/handlers/pptx.py,sha256=eqkI6Gm11hvBqAzOS7OJ_48ioNhjSFFCwgJQFVQHHlc,2328
68
+ brain/ingest/handlers/tables.py,sha256=wKYnBwNkPBLRuABJKzGSEbkEs8MDVl4phUKXg0N7GE4,1563
69
+ brain/ingest/handlers/text.py,sha256=2NUxvphrnIow8X3U1ChFS7IMYbuIKbSS5w_qIceIMX8,1478
70
+ brain/ingest/handlers/xlsx.py,sha256=EoLqKN-DTGmxbYF5F-xX3KvoLkUKNEPYxChJLzWiGoA,4102
71
+ brain/ingest/handlers/zip.py,sha256=aTOO6DR418IGwYonWLdscpEjzB1Ngg63bQy7Admv6Fk,7450
72
+ brainiac_cli-0.16.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
73
+ brainiac_cli-0.16.0.dist-info/METADATA,sha256=x4isWPqPZJHa693yZqPWW3Lhd9Vnn-AfWjeFbBFpGR8,13144
74
+ brainiac_cli-0.16.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
75
+ brainiac_cli-0.16.0.dist-info/entry_points.txt,sha256=gaRS9IrVXze1IdVJm_BcN7kAXzst2FDou63OEwpsQNU,122
76
+ brainiac_cli-0.16.0.dist-info/top_level.txt,sha256=0pJ5ecu89EDo7ZExlRWAh7IWxf55ZhTYilz2jLXwRSg,6
77
+ brainiac_cli-0.16.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ brain = brain.cli:main
3
+ brain-golden-probe = brain.golden_probe:main
4
+ brain-mcp = brain.mcp_adapter:serve