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.
- datacron/__init__.py +28 -0
- datacron/bootstrap.py +161 -0
- datacron/cli.py +1258 -0
- datacron/contradictions.py +971 -0
- datacron/core/__init__.py +16 -0
- datacron/core/config.py +434 -0
- datacron/core/durability.py +315 -0
- datacron/core/frontmatter.py +229 -0
- datacron/core/hashing.py +72 -0
- datacron/core/logger.py +194 -0
- datacron/core/markdown_sections.py +129 -0
- datacron/core/models.py +324 -0
- datacron/core/operation_log.py +459 -0
- datacron/core/paths.py +167 -0
- datacron/core/protocols.py +356 -0
- datacron/core/query_expansion.py +112 -0
- datacron/core/scope.py +223 -0
- datacron/core/security.py +123 -0
- datacron/core/temporal.py +99 -0
- datacron/core/vault.py +435 -0
- datacron/core/vault_writer.py +731 -0
- datacron/eval/__init__.py +51 -0
- datacron/eval/baseline.py +222 -0
- datacron/eval/harness.py +369 -0
- datacron/eval/metrics.py +100 -0
- datacron/eval/transport.py +93 -0
- datacron/indexing/__init__.py +22 -0
- datacron/indexing/chunker.py +422 -0
- datacron/indexing/fts5_store.py +947 -0
- datacron/indexing/rebuild.py +209 -0
- datacron/indexing/reconcile.py +152 -0
- datacron/indexing/ripgrep.py +398 -0
- datacron/indexing/wikilinks.py +180 -0
- datacron/installers/__init__.py +16 -0
- datacron/installers/claude_desktop.py +281 -0
- datacron/installers/mcp_clients.py +559 -0
- datacron/installers/protocol.py +559 -0
- datacron/mcp/__init__.py +16 -0
- datacron/mcp/health.py +200 -0
- datacron/mcp/identity.py +82 -0
- datacron/mcp/resources.py +257 -0
- datacron/mcp/sandbox.py +192 -0
- datacron/mcp/security_manifest.py +65 -0
- datacron/mcp/server.py +401 -0
- datacron/mcp/tool_contract.py +406 -0
- datacron/mcp/tools/__init__.py +54 -0
- datacron/mcp/tools/advisory.py +180 -0
- datacron/mcp/tools/ops.py +167 -0
- datacron/mcp/tools/payloads.py +74 -0
- datacron/mcp/tools/read.py +527 -0
- datacron/mcp/tools/registry.py +458 -0
- datacron/mcp/tools/search.py +485 -0
- datacron/mcp/tools/write.py +651 -0
- datacron/mcp/tools/write_validation.py +287 -0
- datacron/py.typed +0 -0
- datacron/reliability.py +545 -0
- datacron/reliability_evidence.json +23 -0
- datacron/scrubber.py +735 -0
- datacron/setup_wizard.py +547 -0
- datacron-2026.718.2.dist-info/METADATA +363 -0
- datacron-2026.718.2.dist-info/RECORD +64 -0
- datacron-2026.718.2.dist-info/WHEEL +4 -0
- datacron-2026.718.2.dist-info/entry_points.txt +3 -0
- 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
|
+
)
|