brainkeeper 0.2.2__tar.gz → 0.3.0__tar.gz
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.
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/CHANGELOG.md +14 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/PKG-INFO +16 -9
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/README.md +15 -8
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/pyproject.toml +1 -1
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/__init__.py +1 -1
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/cli/__init__.py +13 -15
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/cli/init.py +12 -5
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/cli/serve.py +5 -2
- brainkeeper-0.3.0/tests/unit/test_cli.py +45 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/uv.lock +1 -1
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/.github/workflows/ci.yml +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/.github/workflows/release.yml +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/.gitignore +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/LICENSE +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/docs/branding/architecture.jpg +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/docs/branding/logo.svg +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/docs/branding/palette.md +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/docs/design.md +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/docs/plans/2026-04-24-brainkeeper-spec.md +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/docs/plans/2026-04-24-vault-remediation.md +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/docs/plans/2026-04-27-mcp-core.md +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/docs/plans/vault-audit-summary-2026-04-24.md +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/spec/README.md +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/spec/SPEC.md +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/spec/examples/.gitkeep +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/spec/examples/.invalid/README.md +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/spec/examples/.invalid/absolute-path.yaml +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/spec/examples/.invalid/unknown-root-key.yaml +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/spec/examples/daniels-vault.yaml +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/spec/examples/minimal.yaml +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/spec/examples/zettelkasten.yaml +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/spec/schema/brainkeeper.schema.json +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/__main__.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/core/__init__.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/core/config.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/core/frontmatter.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/core/fs.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/core/index.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/core/rescanner.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/core/watcher.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/mcp/__init__.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/mcp/prompts/__init__.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/mcp/prompts/triage.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/mcp/server.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/mcp/tools/__init__.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/mcp/tools/convention.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/mcp/tools/primitives.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/mcp/tools/semantic.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/src/brainkeeper/spec/__init__.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/conftest.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/fixtures/minimal-vault/00 Inbox/.gitkeep +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/fixtures/minimal-vault/10 Journal/.gitkeep +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/fixtures/minimal-vault/20 Projects/.gitkeep +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/fixtures/minimal-vault/30 Areas/.gitkeep +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/fixtures/minimal-vault/40 Brain/.gitkeep +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/fixtures/minimal-vault/90 Archive/.gitkeep +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/fixtures/minimal-vault/brainkeeper.yaml +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/integration/test_convention.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/integration/test_primitives.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/integration/test_prompts.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/integration/test_semantic.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/unit/test_config.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/unit/test_frontmatter.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/unit/test_fs.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/unit/test_index.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/unit/test_rescanner.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/unit/test_server.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tests/unit/test_watcher.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tools/__init__.py +0 -0
- {brainkeeper-0.2.2 → brainkeeper-0.3.0}/tools/audit_vault.py +0 -0
|
@@ -9,6 +9,19 @@ Two artifacts are versioned independently:
|
|
|
9
9
|
- **`spec-vX.Y.Z`**: the brainkeeper specification (`spec/`).
|
|
10
10
|
- **`brainkeeper-vX.Y.Z`**: the `brainkeeper` Python package, which contains the vault engine library, the MCP server, and the CLI.
|
|
11
11
|
|
|
12
|
+
## [brainkeeper-v0.3.0] - 2026-07-06
|
|
13
|
+
|
|
14
|
+
> ⚠️ **Breaking release.** The vault location changes: it is now fixed at `~/.brainkeeper/vault`, deliberately not configurable. Existing vaults must be moved into place and `--vault` flags removed (see Migration below).
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- **Breaking:** `serve` loses `--vault`. The server always serves `~/.brainkeeper/vault`; if it doesn't exist, `serve` exits with an error pointing at `brainkeeper init`.
|
|
18
|
+
- **Breaking:** `init` loses its `path` positional. It always scaffolds `~/.brainkeeper/vault`.
|
|
19
|
+
- New `brainkeeper.cli.vault_path()` helper: the single source of truth for the fixed vault location.
|
|
20
|
+
|
|
21
|
+
### Migration
|
|
22
|
+
- Move any existing vault into place instead of running `init` (the target must not exist yet): `mkdir -p ~/.brainkeeper && mv ~/MyVault ~/.brainkeeper/vault`.
|
|
23
|
+
- Remove `--vault` from every MCP client entry that references `brainkeeper serve`. The command is now just `uvx brainkeeper serve`.
|
|
24
|
+
|
|
12
25
|
## [brainkeeper-v0.2.2] - 2026-05-05
|
|
13
26
|
|
|
14
27
|
### Fixed
|
|
@@ -136,6 +149,7 @@ First public release of the `brainkeeper` Python package. Implements spec v0.1.4
|
|
|
136
149
|
- Archive semantics narrowed to completed projects only; retired Areas are deleted or distilled into Brain, not archived.
|
|
137
150
|
- Domain tags: cardinality relaxed to 0..n (optional but recommended); vocabulary derived from folders under `projects/` and `areas/` rather than an enumerated list in the config.
|
|
138
151
|
|
|
152
|
+
[brainkeeper-v0.3.0]: https://github.com/dasirra/brainkeeper/releases/tag/brainkeeper-v0.3.0
|
|
139
153
|
[brainkeeper-v0.2.2]: https://github.com/dasirra/brainkeeper/releases/tag/brainkeeper-v0.2.2
|
|
140
154
|
[brainkeeper-v0.2.1]: https://github.com/dasirra/brainkeeper/releases/tag/brainkeeper-v0.2.1
|
|
141
155
|
[spec-v0.2.0]: https://github.com/dasirra/brainkeeper/releases/tag/spec-v0.2.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brainkeeper
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Structured Markdown vault standard with a Python vault engine and MCP interface
|
|
5
5
|
Project-URL: Homepage, https://github.com/dasirra/brainkeeper
|
|
6
6
|
Project-URL: Repository, https://github.com/dasirra/brainkeeper
|
|
@@ -94,15 +94,22 @@ Either way, you need Python 3.11 or newer.
|
|
|
94
94
|
If you don't already have a brainkeeper vault, create one:
|
|
95
95
|
|
|
96
96
|
```bash
|
|
97
|
-
uvx brainkeeper init
|
|
97
|
+
uvx brainkeeper init
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
This
|
|
100
|
+
This scaffolds the vault at `~/.brainkeeper/vault`: the six layer directories plus a `brainkeeper.yaml` (the minimal reference config) at the vault root. Open the YAML to adjust folder names and area substructure to taste. Validate against the schema at any time:
|
|
101
101
|
|
|
102
102
|
```bash
|
|
103
103
|
uvx check-jsonschema \
|
|
104
104
|
--schemafile https://raw.githubusercontent.com/dasirra/brainkeeper/main/spec/schema/brainkeeper.schema.json \
|
|
105
|
-
|
|
105
|
+
~/.brainkeeper/vault/brainkeeper.yaml
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Already have a vault elsewhere? Move it into place instead of running `init` (the target must not exist yet):
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
mkdir -p ~/.brainkeeper
|
|
112
|
+
mv ~/MyVault ~/.brainkeeper/vault
|
|
106
113
|
```
|
|
107
114
|
|
|
108
115
|
### 3. Configure your MCP client
|
|
@@ -112,7 +119,7 @@ The MCP server is launched by your LLM harness over stdio. Pick the snippet for
|
|
|
112
119
|
**Claude Code** (one command):
|
|
113
120
|
|
|
114
121
|
```bash
|
|
115
|
-
claude mcp add --scope user brainkeeper -- uvx brainkeeper serve
|
|
122
|
+
claude mcp add --scope user brainkeeper -- uvx brainkeeper serve
|
|
116
123
|
```
|
|
117
124
|
|
|
118
125
|
**Claude Desktop**: edit the config file at
|
|
@@ -127,15 +134,15 @@ and add:
|
|
|
127
134
|
"mcpServers": {
|
|
128
135
|
"brainkeeper": {
|
|
129
136
|
"command": "uvx",
|
|
130
|
-
"args": ["brainkeeper", "serve"
|
|
137
|
+
"args": ["brainkeeper", "serve"]
|
|
131
138
|
}
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
141
|
```
|
|
135
142
|
|
|
136
|
-
Then restart Claude Desktop.
|
|
143
|
+
Then restart Claude Desktop.
|
|
137
144
|
|
|
138
|
-
**Other clients**: any MCP-capable harness that speaks stdio works. The command is `uvx brainkeeper serve
|
|
145
|
+
**Other clients**: any MCP-capable harness that speaks stdio works. The command is `uvx brainkeeper serve` (or `brainkeeper serve` if you `pip install`ed).
|
|
139
146
|
|
|
140
147
|
### 4. Verify
|
|
141
148
|
|
|
@@ -206,7 +213,7 @@ The current spec version is **v0.1.4**. See [`CHANGELOG.md`](https://github.com/
|
|
|
206
213
|
|
|
207
214
|
This is the first public release. Known constraints:
|
|
208
215
|
|
|
209
|
-
- **One vault per server instance.**
|
|
216
|
+
- **One vault per server instance.** The server always serves the fixed vault at `~/.brainkeeper/vault`. Multiple vaults are not supported.
|
|
210
217
|
- **`move_note` does not rewrite wikilinks.** Inbound links to a moved note become stale until you fix them manually. Planned for a later release.
|
|
211
218
|
- **No always-on indexing.** The MCP runs only while its host (Claude Code, Claude Desktop, etc.) is running. There is no background daemon.
|
|
212
219
|
- **The MCP encodes the spec contract.** If you let an LLM use raw filesystem tools (Read, Write, Edit) on the vault path, it will produce notes that violate the frontmatter contract or land in the wrong layer. Tell your agent to use only the brainkeeper tools when working inside the vault.
|
|
@@ -58,15 +58,22 @@ Either way, you need Python 3.11 or newer.
|
|
|
58
58
|
If you don't already have a brainkeeper vault, create one:
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
|
-
uvx brainkeeper init
|
|
61
|
+
uvx brainkeeper init
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
This
|
|
64
|
+
This scaffolds the vault at `~/.brainkeeper/vault`: the six layer directories plus a `brainkeeper.yaml` (the minimal reference config) at the vault root. Open the YAML to adjust folder names and area substructure to taste. Validate against the schema at any time:
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
67
|
uvx check-jsonschema \
|
|
68
68
|
--schemafile https://raw.githubusercontent.com/dasirra/brainkeeper/main/spec/schema/brainkeeper.schema.json \
|
|
69
|
-
|
|
69
|
+
~/.brainkeeper/vault/brainkeeper.yaml
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Already have a vault elsewhere? Move it into place instead of running `init` (the target must not exist yet):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
mkdir -p ~/.brainkeeper
|
|
76
|
+
mv ~/MyVault ~/.brainkeeper/vault
|
|
70
77
|
```
|
|
71
78
|
|
|
72
79
|
### 3. Configure your MCP client
|
|
@@ -76,7 +83,7 @@ The MCP server is launched by your LLM harness over stdio. Pick the snippet for
|
|
|
76
83
|
**Claude Code** (one command):
|
|
77
84
|
|
|
78
85
|
```bash
|
|
79
|
-
claude mcp add --scope user brainkeeper -- uvx brainkeeper serve
|
|
86
|
+
claude mcp add --scope user brainkeeper -- uvx brainkeeper serve
|
|
80
87
|
```
|
|
81
88
|
|
|
82
89
|
**Claude Desktop**: edit the config file at
|
|
@@ -91,15 +98,15 @@ and add:
|
|
|
91
98
|
"mcpServers": {
|
|
92
99
|
"brainkeeper": {
|
|
93
100
|
"command": "uvx",
|
|
94
|
-
"args": ["brainkeeper", "serve"
|
|
101
|
+
"args": ["brainkeeper", "serve"]
|
|
95
102
|
}
|
|
96
103
|
}
|
|
97
104
|
}
|
|
98
105
|
```
|
|
99
106
|
|
|
100
|
-
Then restart Claude Desktop.
|
|
107
|
+
Then restart Claude Desktop.
|
|
101
108
|
|
|
102
|
-
**Other clients**: any MCP-capable harness that speaks stdio works. The command is `uvx brainkeeper serve
|
|
109
|
+
**Other clients**: any MCP-capable harness that speaks stdio works. The command is `uvx brainkeeper serve` (or `brainkeeper serve` if you `pip install`ed).
|
|
103
110
|
|
|
104
111
|
### 4. Verify
|
|
105
112
|
|
|
@@ -170,7 +177,7 @@ The current spec version is **v0.1.4**. See [`CHANGELOG.md`](https://github.com/
|
|
|
170
177
|
|
|
171
178
|
This is the first public release. Known constraints:
|
|
172
179
|
|
|
173
|
-
- **One vault per server instance.**
|
|
180
|
+
- **One vault per server instance.** The server always serves the fixed vault at `~/.brainkeeper/vault`. Multiple vaults are not supported.
|
|
174
181
|
- **`move_note` does not rewrite wikilinks.** Inbound links to a moved note become stale until you fix them manually. Planned for a later release.
|
|
175
182
|
- **No always-on indexing.** The MCP runs only while its host (Claude Code, Claude Desktop, etc.) is running. There is no background daemon.
|
|
176
183
|
- **The MCP encodes the spec contract.** If you let an LLM use raw filesystem tools (Read, Write, Edit) on the vault path, it will produce notes that violate the frontmatter contract or land in the wrong layer. Tell your agent to use only the brainkeeper tools when working inside the vault.
|
|
@@ -9,6 +9,12 @@ from . import serve as _serve
|
|
|
9
9
|
from . import init as _init
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
def vault_path() -> Path:
|
|
13
|
+
"""Fixed vault location: the single source of truth."""
|
|
14
|
+
# resolve() keeps the watcher/index path keys canonical under symlinked homes
|
|
15
|
+
return (Path.home() / ".brainkeeper" / "vault").resolve()
|
|
16
|
+
|
|
17
|
+
|
|
12
18
|
def main(argv: list[str] | None = None) -> int:
|
|
13
19
|
parser = argparse.ArgumentParser(
|
|
14
20
|
prog="brainkeeper",
|
|
@@ -17,28 +23,20 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
17
23
|
sub = parser.add_subparsers(dest="command", metavar="COMMAND")
|
|
18
24
|
|
|
19
25
|
sp_serve = sub.add_parser("serve", help="Start the MCP server")
|
|
20
|
-
sp_serve.add_argument(
|
|
21
|
-
"--vault",
|
|
22
|
-
type=Path,
|
|
23
|
-
required=True,
|
|
24
|
-
help="Path to vault root containing brainkeeper.yaml",
|
|
25
|
-
)
|
|
26
26
|
sp_serve.add_argument("-v", "--verbose", action="store_true")
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
sp_init.add_argument(
|
|
30
|
-
"path", type=Path, help="Path for the new vault root (created if absent)"
|
|
31
|
-
)
|
|
28
|
+
sub.add_parser("init", help="Bootstrap the vault at ~/.brainkeeper/vault")
|
|
32
29
|
|
|
33
30
|
args = parser.parse_args(argv)
|
|
34
31
|
|
|
32
|
+
if args.command is None:
|
|
33
|
+
parser.print_help()
|
|
34
|
+
return 0
|
|
35
|
+
|
|
36
|
+
args.vault = vault_path()
|
|
35
37
|
if args.command == "serve":
|
|
36
38
|
return _serve.run(args)
|
|
37
|
-
|
|
38
|
-
return _init.run(args)
|
|
39
|
-
|
|
40
|
-
parser.print_help()
|
|
41
|
-
return 0
|
|
39
|
+
return _init.run(args)
|
|
42
40
|
|
|
43
41
|
|
|
44
42
|
if __name__ == "__main__":
|
|
@@ -31,11 +31,18 @@ def _find_minimal_yaml() -> Path:
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
def run(args) -> int:
|
|
34
|
-
vault = args.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
vault = args.vault
|
|
35
|
+
try:
|
|
36
|
+
vault.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
for layer in _LAYER_DIRS:
|
|
38
|
+
(vault / layer).mkdir(exist_ok=True)
|
|
39
|
+
except (FileExistsError, NotADirectoryError):
|
|
40
|
+
print(
|
|
41
|
+
f"error: cannot create {vault}: a file is in the way. "
|
|
42
|
+
"Remove it and re-run `brainkeeper init`.",
|
|
43
|
+
file=sys.stderr,
|
|
44
|
+
)
|
|
45
|
+
return 1
|
|
39
46
|
|
|
40
47
|
config_path = vault / "brainkeeper.yaml"
|
|
41
48
|
if config_path.exists():
|
|
@@ -13,9 +13,12 @@ def run(args) -> int:
|
|
|
13
13
|
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
|
14
14
|
stream=sys.stderr,
|
|
15
15
|
)
|
|
16
|
-
vault = args.vault
|
|
16
|
+
vault = args.vault
|
|
17
17
|
if not vault.is_dir():
|
|
18
|
-
print(
|
|
18
|
+
print(
|
|
19
|
+
f"error: no vault at {vault}. Run `brainkeeper init` to create it.",
|
|
20
|
+
file=sys.stderr,
|
|
21
|
+
)
|
|
19
22
|
return 1
|
|
20
23
|
srv = BrainkeeperServer(vault)
|
|
21
24
|
srv.run_stdio()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from brainkeeper.cli import main
|
|
4
|
+
|
|
5
|
+
_LAYER_DIRS = [
|
|
6
|
+
"00 Inbox",
|
|
7
|
+
"10 Journal",
|
|
8
|
+
"20 Projects",
|
|
9
|
+
"30 Areas",
|
|
10
|
+
"40 Brain",
|
|
11
|
+
"90 Archive",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_init_creates_layers_and_config(tmp_path: Path, monkeypatch):
|
|
16
|
+
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
|
17
|
+
assert main(["init"]) == 0
|
|
18
|
+
vault = tmp_path / ".brainkeeper" / "vault"
|
|
19
|
+
for layer in _LAYER_DIRS:
|
|
20
|
+
assert (vault / layer).is_dir()
|
|
21
|
+
assert (vault / "brainkeeper.yaml").exists()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_reinit_warns_and_preserves_config(tmp_path: Path, monkeypatch, capsys):
|
|
25
|
+
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
|
26
|
+
main(["init"])
|
|
27
|
+
config_path = tmp_path / ".brainkeeper" / "vault" / "brainkeeper.yaml"
|
|
28
|
+
config_path.write_text("# user edit\n")
|
|
29
|
+
|
|
30
|
+
assert main(["init"]) == 0
|
|
31
|
+
assert "already exists" in capsys.readouterr().err
|
|
32
|
+
assert config_path.read_text() == "# user edit\n"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_serve_missing_vault_exits_1(tmp_path: Path, monkeypatch, capsys):
|
|
36
|
+
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
|
37
|
+
assert main(["serve"]) == 1
|
|
38
|
+
assert "brainkeeper init" in capsys.readouterr().err
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_init_blocked_by_file_exits_1(tmp_path: Path, monkeypatch, capsys):
|
|
42
|
+
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
|
43
|
+
(tmp_path / ".brainkeeper").write_text("not a directory\n")
|
|
44
|
+
assert main(["init"]) == 1
|
|
45
|
+
assert "error" in capsys.readouterr().err
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|