datacron 2026.718.2__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 (64) hide show
  1. datacron/__init__.py +28 -0
  2. datacron/bootstrap.py +161 -0
  3. datacron/cli.py +1258 -0
  4. datacron/contradictions.py +971 -0
  5. datacron/core/__init__.py +16 -0
  6. datacron/core/config.py +434 -0
  7. datacron/core/durability.py +315 -0
  8. datacron/core/frontmatter.py +229 -0
  9. datacron/core/hashing.py +72 -0
  10. datacron/core/logger.py +194 -0
  11. datacron/core/markdown_sections.py +129 -0
  12. datacron/core/models.py +324 -0
  13. datacron/core/operation_log.py +459 -0
  14. datacron/core/paths.py +167 -0
  15. datacron/core/protocols.py +356 -0
  16. datacron/core/query_expansion.py +112 -0
  17. datacron/core/scope.py +223 -0
  18. datacron/core/security.py +123 -0
  19. datacron/core/temporal.py +99 -0
  20. datacron/core/vault.py +435 -0
  21. datacron/core/vault_writer.py +731 -0
  22. datacron/eval/__init__.py +51 -0
  23. datacron/eval/baseline.py +222 -0
  24. datacron/eval/harness.py +369 -0
  25. datacron/eval/metrics.py +100 -0
  26. datacron/eval/transport.py +93 -0
  27. datacron/indexing/__init__.py +22 -0
  28. datacron/indexing/chunker.py +422 -0
  29. datacron/indexing/fts5_store.py +947 -0
  30. datacron/indexing/rebuild.py +209 -0
  31. datacron/indexing/reconcile.py +152 -0
  32. datacron/indexing/ripgrep.py +398 -0
  33. datacron/indexing/wikilinks.py +180 -0
  34. datacron/installers/__init__.py +16 -0
  35. datacron/installers/claude_desktop.py +281 -0
  36. datacron/installers/mcp_clients.py +559 -0
  37. datacron/installers/protocol.py +559 -0
  38. datacron/mcp/__init__.py +16 -0
  39. datacron/mcp/health.py +200 -0
  40. datacron/mcp/identity.py +82 -0
  41. datacron/mcp/resources.py +257 -0
  42. datacron/mcp/sandbox.py +192 -0
  43. datacron/mcp/security_manifest.py +65 -0
  44. datacron/mcp/server.py +401 -0
  45. datacron/mcp/tool_contract.py +406 -0
  46. datacron/mcp/tools/__init__.py +54 -0
  47. datacron/mcp/tools/advisory.py +180 -0
  48. datacron/mcp/tools/ops.py +167 -0
  49. datacron/mcp/tools/payloads.py +74 -0
  50. datacron/mcp/tools/read.py +527 -0
  51. datacron/mcp/tools/registry.py +458 -0
  52. datacron/mcp/tools/search.py +485 -0
  53. datacron/mcp/tools/write.py +651 -0
  54. datacron/mcp/tools/write_validation.py +287 -0
  55. datacron/py.typed +0 -0
  56. datacron/reliability.py +545 -0
  57. datacron/reliability_evidence.json +23 -0
  58. datacron/scrubber.py +735 -0
  59. datacron/setup_wizard.py +547 -0
  60. datacron-2026.718.2.dist-info/METADATA +363 -0
  61. datacron-2026.718.2.dist-info/RECORD +64 -0
  62. datacron-2026.718.2.dist-info/WHEEL +4 -0
  63. datacron-2026.718.2.dist-info/entry_points.txt +3 -0
  64. datacron-2026.718.2.dist-info/licenses/LICENSE +201 -0
datacron/__init__.py ADDED
@@ -0,0 +1,28 @@
1
+ # Copyright 2026 Julien Bombled
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Datacron - local-first MCP server for Markdown vaults.
15
+
16
+ Public API surface kept intentionally narrow. Most consumers should import
17
+ from :mod:`datacron.core.models` for the frozen Pydantic types, or use the
18
+ ``datacron`` CLI.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ # Calendar Versioning: YYYY.MMDD.XX (UTC year, zero-padded month+day, same-day
24
+ # build counter starting at 00). Single source of truth; pyproject reads it via
25
+ # hatch's dynamic version.
26
+ __version__ = "2026.0718.02"
27
+
28
+ __all__ = ["__version__"]
datacron/bootstrap.py ADDED
@@ -0,0 +1,161 @@
1
+ # Copyright 2026 Julien Bombled
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Vault bootstrap primitives shared by ``datacron init`` and ``datacron setup``.
15
+
16
+ This module owns the side-effecting sequence that turns any Markdown folder into
17
+ a Datacron vault: creating the ``.datacron/`` sidecar tree and writing
18
+ ``VAULT.yaml``. Keeping it here (rather than inline in the CLI) lets both the
19
+ low-level ``init`` command and the guided ``setup`` wizard share one
20
+ implementation, so the two can never drift apart.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from dataclasses import dataclass
26
+ from datetime import UTC, datetime
27
+ from pathlib import Path
28
+ from typing import Final
29
+
30
+ import yaml
31
+ from ulid import ULID
32
+
33
+ from datacron import __version__
34
+ from datacron.core.config import (
35
+ DEFAULT_ENCODING,
36
+ DEFAULT_EXCLUDED_FILES,
37
+ DEFAULT_EXCLUDED_FOLDERS,
38
+ DEFAULT_HISTORY_MODE,
39
+ DEFAULT_HISTORY_RETENTION_DAYS,
40
+ DEFAULT_LINE_ENDINGS,
41
+ HISTORY_DIR_NAME,
42
+ OPLOG_DIR_NAME,
43
+ OPLOG_PENDING_DIR_NAME,
44
+ VAULT_VERSION_KEY,
45
+ )
46
+ from datacron.core.logger import get_logger
47
+ from datacron.core.paths import (
48
+ sidecar_dir,
49
+ sidecar_index_dir,
50
+ sidecar_vault_config,
51
+ )
52
+ from datacron.core.query_expansion import query_expansion_seed
53
+
54
+ __all__ = ["DEFAULT_DRAFTS_FOLDER", "DEFAULT_JOURNAL_FOLDER", "BootstrapResult", "initialize_vault"]
55
+
56
+ _LOGGER = get_logger(__name__)
57
+
58
+ DEFAULT_DRAFTS_FOLDER: Final[str] = "_drafts"
59
+ DEFAULT_JOURNAL_FOLDER: Final[str] = "_journal"
60
+ _LOGS_DIR_NAME: Final[str] = "logs"
61
+
62
+
63
+ @dataclass(frozen=True)
64
+ class BootstrapResult:
65
+ """Outcome of :func:`initialize_vault`.
66
+
67
+ Attributes:
68
+ vault_path: The resolved vault root that was initialized.
69
+ sidecar_path: The ``.datacron/`` sidecar directory.
70
+ config_path: The ``.datacron/VAULT.yaml`` file.
71
+ vault_id: The vault identifier. ``None`` when an existing config was
72
+ kept (``created`` is then ``False``).
73
+ created: ``True`` when a fresh ``VAULT.yaml`` was written, ``False``
74
+ when an existing config was preserved.
75
+ """
76
+
77
+ vault_path: Path
78
+ sidecar_path: Path
79
+ config_path: Path
80
+ vault_id: str | None
81
+ created: bool
82
+
83
+
84
+ def _format_vault_yaml(vault_id: str, created: datetime) -> str:
85
+ """Serialize a fresh ``VAULT.yaml`` payload with seeded defaults."""
86
+ payload = {
87
+ VAULT_VERSION_KEY: __version__,
88
+ "vault_id": vault_id,
89
+ "created": created.isoformat(),
90
+ "encoding": DEFAULT_ENCODING,
91
+ "line_endings": DEFAULT_LINE_ENDINGS,
92
+ "history_retention_days": DEFAULT_HISTORY_RETENTION_DAYS,
93
+ "history_mode": DEFAULT_HISTORY_MODE,
94
+ "folders": {
95
+ "drafts": DEFAULT_DRAFTS_FOLDER,
96
+ "journal": DEFAULT_JOURNAL_FOLDER,
97
+ },
98
+ "excluded_folders": list(DEFAULT_EXCLUDED_FOLDERS),
99
+ "excluded_files": list(DEFAULT_EXCLUDED_FILES),
100
+ "query_expansion": query_expansion_seed(),
101
+ }
102
+ return yaml.safe_dump(payload, sort_keys=False, allow_unicode=True)
103
+
104
+
105
+ def initialize_vault(vault_path: Path, *, force: bool = False) -> BootstrapResult:
106
+ """Create the ``.datacron/`` sidecar tree and ``VAULT.yaml`` for a vault.
107
+
108
+ The vault directory is created if it does not exist. The sidecar
109
+ subdirectories (index, logs, history, operation journal) are created
110
+ idempotently. ``VAULT.yaml`` is written only when absent, unless ``force``
111
+ is set, so re-running never clobbers an existing vault identity by accident.
112
+
113
+ Args:
114
+ vault_path: Path to the Markdown vault to initialize. It is expanded and
115
+ resolved to an absolute path.
116
+ force: When ``True``, overwrite an existing ``VAULT.yaml``.
117
+
118
+ Returns:
119
+ A :class:`BootstrapResult` describing what was created or preserved.
120
+
121
+ Raises:
122
+ NotADirectoryError: If ``vault_path`` exists and is not a directory.
123
+ """
124
+ resolved = vault_path.expanduser().resolve()
125
+ if resolved.exists() and not resolved.is_dir():
126
+ raise NotADirectoryError(f"{resolved} exists and is not a directory.")
127
+
128
+ if not resolved.exists():
129
+ resolved.mkdir(parents=True, exist_ok=True)
130
+ _LOGGER.info("Created vault directory %s", resolved)
131
+
132
+ sidecar = sidecar_dir(resolved)
133
+ sidecar.mkdir(parents=True, exist_ok=True)
134
+ sidecar_index_dir(resolved).mkdir(parents=True, exist_ok=True)
135
+ (sidecar / _LOGS_DIR_NAME).mkdir(parents=True, exist_ok=True)
136
+ (sidecar / HISTORY_DIR_NAME).mkdir(parents=True, exist_ok=True)
137
+ (sidecar / OPLOG_DIR_NAME / OPLOG_PENDING_DIR_NAME).mkdir(parents=True, exist_ok=True)
138
+
139
+ config_path = sidecar_vault_config(resolved)
140
+ if config_path.exists() and not force:
141
+ _LOGGER.info("VAULT.yaml already present at %s; keeping existing config", config_path)
142
+ return BootstrapResult(
143
+ vault_path=resolved,
144
+ sidecar_path=sidecar,
145
+ config_path=config_path,
146
+ vault_id=None,
147
+ created=False,
148
+ )
149
+
150
+ vault_id = str(ULID())
151
+ now = datetime.now(tz=UTC)
152
+ config_path.write_text(_format_vault_yaml(vault_id, now), encoding=DEFAULT_ENCODING)
153
+ _LOGGER.info("Initialized Datacron vault at %s (vault_id=%s)", resolved, vault_id)
154
+
155
+ return BootstrapResult(
156
+ vault_path=resolved,
157
+ sidecar_path=sidecar,
158
+ config_path=config_path,
159
+ vault_id=vault_id,
160
+ created=True,
161
+ )