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
@@ -0,0 +1,158 @@
1
+ """ING-04 — transcript capture route (ADR-0003 Ruling 1 companion, S06).
2
+
3
+ Meeting transcripts are produced EXTERNALLY (the transcriber MCP/CLI — never
4
+ in-kernel, AGENTS.md) and are already Markdown, so unlike the binary
5
+ drop-zone (``ingest/pipeline.py``) there is no extraction step and no
6
+ "original binary vs. extracted Markdown" split: the transcript file's own
7
+ text IS the note body. What the generic drop-zone pipeline genuinely CANNOT
8
+ express is real-world provenance — its own ``origin`` always points at an
9
+ archived COPY of whatever was dropped, never at the real-world thing the
10
+ content was recorded from. This module's whole job is exactly that gap: an
11
+ EXPLICIT, caller-supplied ``origin`` (a source audio/video file path, or the
12
+ literal string ``"verbal"`` for a no-recording capture) plus an optional
13
+ ``language`` stamp detected from the filename (never guessed from prose).
14
+
15
+ Reuses ``ingest.pipeline``'s hardened building blocks (create-exclusive
16
+ archival, control-char-safe frontmatter, the shared density gate, the SAME
17
+ content-hash dedup manifest as the binary pipeline) rather than
18
+ reimplementing them — one dedup universe across every ingest surface.
19
+ """
20
+ from __future__ import annotations
21
+
22
+ import datetime as _dt
23
+ import hashlib
24
+ import re
25
+ from pathlib import Path
26
+ from typing import Any
27
+
28
+ from . import pipeline as _pipeline
29
+ from .handlers.base import density_gate
30
+ from ..notes import safe_slug
31
+
32
+ MAX_TRANSCRIPT_BYTES = 50 * 1024 * 1024
33
+ _ENCODINGS = ("utf-8", "utf-8-sig", "latin-1")
34
+
35
+ # Presence-based ONLY — "language if present in the filename" (S06 brief)
36
+ # means a literal recognised code as its own path segment (e.g.
37
+ # "standup_2026-07-05_en.md", "reuniao.pt.md"), never an inference from the
38
+ # transcript's prose. A wrong guess is worse than an absent field.
39
+ _LANG_CODES = ("en", "pt", "es", "fr", "de", "it", "nl")
40
+ _LANG_RE = re.compile(r"(?:^|[._-])(" + "|".join(_LANG_CODES) + r")(?:[._-]|$)", re.IGNORECASE)
41
+
42
+
43
+ def detect_language(filename: str) -> str | None:
44
+ m = _LANG_RE.search(Path(filename).stem)
45
+ return m.group(1).lower() if m else None
46
+
47
+
48
+ def ingest_transcript(
49
+ core: Any, path: str | Path, *, origin: str, language: str | None = None,
50
+ document_date: str | None = None, classification: str = "Internal",
51
+ ) -> dict[str, Any]:
52
+ """Promote a transcript ``.md`` file into ``vault/raw/`` via the SAME
53
+ audited host-broker write path as the binary drop-zone. Returns a result
54
+ dict shaped like ``pipeline``'s ``processed``/``duplicates`` entries
55
+ (``ok: False`` + ``reason`` on failure — never raises for an ordinary
56
+ content/quality problem, matching every handler's own contract).
57
+
58
+ HOST-broker only: ``core.write_note`` already fails closed on the VM
59
+ role; ``BrainCore.ingest_transcript`` additionally calls
60
+ ``_require_host`` up front for the same fail-fast-before-any-I/O shape as
61
+ ``ingest_dropzone``.
62
+ """
63
+ path = Path(path)
64
+ vault = core.vault
65
+
66
+ try:
67
+ size = path.stat().st_size
68
+ except OSError as exc:
69
+ return {"ok": False, "reason": f"transcript_read_error:{type(exc).__name__}: {exc}"}
70
+ if size > MAX_TRANSCRIPT_BYTES:
71
+ return {"ok": False, "reason": "file_too_large"}
72
+
73
+ try:
74
+ raw_bytes = path.read_bytes()
75
+ except OSError as exc:
76
+ return {"ok": False, "reason": f"transcript_read_error:{type(exc).__name__}: {exc}"}
77
+
78
+ text = None
79
+ for enc in _ENCODINGS:
80
+ try:
81
+ text = raw_bytes.decode(enc)
82
+ break
83
+ except UnicodeDecodeError:
84
+ continue
85
+ if text is None:
86
+ return {"ok": False, "reason": "transcript_decode_error"}
87
+
88
+ reason = density_gate(text)
89
+ if reason:
90
+ return {"ok": False, "reason": reason}
91
+
92
+ original_sha = hashlib.sha256(raw_bytes).hexdigest()
93
+ manifest = _pipeline._load_manifest(vault)
94
+ if original_sha in manifest:
95
+ return {"ok": True, "duplicate": True, "existing_id": manifest[original_sha], "file": str(path)}
96
+
97
+ today = _dt.date.today().isoformat()
98
+ stem = _pipeline._slugify_stem(path.stem)
99
+ slug = safe_slug(f"{today}-{stem}")
100
+ archive_subdir = vault / "raw" / "originals" / f"{today}-{stem}"
101
+ archive_path = archive_subdir / _pipeline._sanitize_archive_name(path.name)
102
+
103
+ arch_status = _pipeline._create_exclusive_or_collision(archive_path, raw_bytes, known_sha=original_sha)
104
+ if arch_status == "collision":
105
+ return {
106
+ "ok": False, "reason": "archive_collision",
107
+ "detail": f"archived-original target already holds different content: {archive_path}",
108
+ }
109
+
110
+ body_sha = hashlib.sha256(text.encode("utf-8")).hexdigest()
111
+ lang = language or detect_language(path.name)
112
+ meta: dict[str, Any] = {
113
+ "id": slug,
114
+ "type": "source",
115
+ "classification": classification,
116
+ "captured": today,
117
+ "origin": origin,
118
+ "sha256": body_sha,
119
+ "immutable": True,
120
+ }
121
+ if lang:
122
+ meta["language"] = lang
123
+ if document_date:
124
+ meta["document_date"] = document_date
125
+
126
+ note_rel = f"raw/{slug}.md"
127
+ note_path = vault / note_rel
128
+ if note_path.exists():
129
+ # Defense in depth (manifest miss but the target id already exists —
130
+ # e.g. a hand-deleted manifest): same-body -> idempotent no-op,
131
+ # different -> collision, never overwritten. Mirrors pipeline.py.
132
+ from .. import frontmatter as fm
133
+
134
+ existing_meta, _ = fm.parse_text(note_path.read_text(encoding="utf-8"))
135
+ if str(existing_meta.get("sha256", "")) != body_sha:
136
+ return {
137
+ "ok": False, "reason": "note_id_collision",
138
+ "detail": f"raw/{slug}.md already exists with different content",
139
+ }
140
+ manifest[original_sha] = slug
141
+ _pipeline._save_manifest(vault, manifest)
142
+ return {"ok": True, "duplicate": True, "existing_id": slug, "file": str(path)}
143
+
144
+ content = _pipeline._build_frontmatter(meta, text)
145
+ core.write_note(
146
+ note_rel, content,
147
+ reason=f"ingest-transcript {path.name} -> raw/{slug}.md (origin={origin})",
148
+ subtree="raw",
149
+ )
150
+ manifest[original_sha] = slug
151
+ _pipeline._save_manifest(vault, manifest)
152
+
153
+ return {
154
+ "ok": True, "duplicate": False, "id": slug, "note": note_rel,
155
+ "archived": str(archive_path.relative_to(vault)),
156
+ "classification": classification, "origin": origin, "language": lang,
157
+ "file": str(path),
158
+ }