@andespindola/brainlink 0.1.0-beta.0 → 0.1.0-beta.10

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 (41) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +252 -19
  3. package/dist/application/add-note.js +62 -13
  4. package/dist/application/analyze-vault.js +104 -9
  5. package/dist/application/frontend/client-css.js +154 -71
  6. package/dist/application/frontend/client-html.js +42 -33
  7. package/dist/application/frontend/client-js.js +255 -70
  8. package/dist/application/get-graph-layout.js +6 -3
  9. package/dist/application/get-graph-node.js +12 -0
  10. package/dist/application/get-graph-summary.js +12 -0
  11. package/dist/application/migrate-vault.js +91 -0
  12. package/dist/application/search-graph-node-ids.js +12 -0
  13. package/dist/application/search-knowledge.js +56 -1
  14. package/dist/application/server/routes.js +27 -1
  15. package/dist/cli/commands/agent-commands.js +412 -0
  16. package/dist/cli/commands/config-commands.js +167 -0
  17. package/dist/cli/commands/read-commands.js +25 -8
  18. package/dist/cli/commands/write-commands.js +191 -7
  19. package/dist/cli/main.js +4 -0
  20. package/dist/cli/runtime.js +5 -2
  21. package/dist/domain/embeddings.js +2 -1
  22. package/dist/domain/graph-layout.js +20 -14
  23. package/dist/domain/markdown.js +36 -4
  24. package/dist/infrastructure/config.js +96 -8
  25. package/dist/infrastructure/file-system-vault.js +15 -0
  26. package/dist/infrastructure/paths.js +9 -1
  27. package/dist/infrastructure/session-state.js +172 -0
  28. package/dist/infrastructure/sqlite/graph-reader.js +252 -105
  29. package/dist/infrastructure/sqlite/recovery.js +83 -0
  30. package/dist/infrastructure/sqlite/schema.js +4 -1
  31. package/dist/infrastructure/sqlite/search-reader.js +104 -72
  32. package/dist/infrastructure/sqlite-index.js +16 -3
  33. package/dist/mcp/main.js +11 -3
  34. package/dist/mcp/server.js +22 -2
  35. package/dist/mcp/startup.js +35 -0
  36. package/dist/mcp/tools.js +617 -21
  37. package/docs/AGENT_USAGE.md +95 -6
  38. package/docs/ARCHITECTURE.md +15 -1
  39. package/docs/QUICKSTART.md +104 -0
  40. package/docs/RELEASE.md +3 -3
  41. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0-beta.4
4
+
5
+ - Added bootstrap session-state persistence in `$BRAINLINK_HOME/session-state.json` for vault/agent readiness tracking.
6
+ - Added MCP `brainlink_policy` tool and default bootstrap enforcement for read tools.
7
+ - Added `agent install --self-test` diagnostics and bootstrap readiness details in `agent status`.
8
+ - Added `agent upgrade` for legacy installations to reapply latest MCP/plugin defaults with self-test diagnostics.
9
+ - Added `config doctor --fix` safe autofix mode with dry-run default behavior.
10
+ - Added detailed per-file migration reporting through `migrate-vault --report`.
11
+ - Added `quickstart` command to run plug-and-play vault + bootstrap + agent setup in one flow.
12
+ - Added structured MCP `nextActions` in bootstrap/policy/preflight responses for automatic client continuation.
13
+ - Added default MCP read auto-bootstrap behavior controlled by `brainlink_policy.autoBootstrapOnRead`.
14
+ - Added default MCP startup bootstrap behavior controlled by `brainlink_policy.autoBootstrapOnStartup`.
15
+ - Added CLI MCP policy presets through `blink agent policy --preset fully-auto|strict`.
16
+ - Added write-time non-orphan enforcement by auto-linking notes without wiki edges to agent hub notes.
17
+ - Added MCP `brainlink_policy` presets (`fully-auto`, `strict`) for one-call policy switching.
18
+ - Added MCP write connectivity metadata in `brainlink_add_note`/`brainlink_add_file` responses.
19
+ - Added MCP `brainlink_recommendations` tool for plug-and-play workflow guidance.
20
+ - Improved graph/index robustness by splitting oversized paragraphs into bounded chunks and dropping self-referential links.
21
+ - Added `agentProfiles` configuration support so CLI and MCP can resolve per-agent defaults for mode/limit/tokens.
22
+ - Added short-lived hybrid search cache with automatic invalidation on index changes.
23
+ - Added `stats --extended` observability output with storage, quality and latency probes.
24
+ - Added `docs/QUICKSTART.md` and aligned README/agent docs with the latest CLI/MCP flows.
25
+
26
+ ## 0.1.0-beta.3
27
+
28
+ - Added CLI configuration commands for effective vault management, including `config where`, `config get`, `config doctor` and `config set-vault`.
29
+ - Added explicit `migrate-vault` command with `--dry-run` preview and conflict-preserving copy behavior.
30
+ - Added one-command agent setup through `agent install` plus `agent status` diagnostics.
31
+ - Added MCP `brainlink_bootstrap` default entrypoint guidance for plug-and-play agent memory flows.
32
+ - Added migration coverage for S3 bucket vault targets.
33
+ - Updated architecture and agent-usage documentation to reflect current CLI/MCP behavior and configuration precedence.
34
+
35
+ ## 0.1.0-beta.2
36
+
37
+ - Added MCP installation guidance for direct server configuration and local client stores.
38
+ - Documented MCP vault allowlisting with `BRAINLINK_ALLOWED_VAULTS`.
39
+ - Aligned the documented MCP tool list with the current server tools.
40
+ - Updated release documentation for the beta package line.
41
+
42
+ ## 0.1.0-beta.0
43
+
44
+ - Promoted the package to the beta prerelease channel.
45
+ - Added built-in MCP stdio server distribution through `brainlink-mcp`.
46
+ - Added agent namespaces, auto-indexing on writes and file ingestion flows.
47
+ - Added S3-compatible bucket vault support and weighted graph relationships.
48
+
3
49
  ## 0.1.0-alpha.0
4
50
 
5
51
  - Added local-first Markdown vault indexing.
package/README.md CHANGED
@@ -57,6 +57,7 @@ LLMs do not have infinite context. Brainlink gives agents an external memory lay
57
57
  6. Brainlink returns compact, source-backed context.
58
58
 
59
59
  Markdown is the source of truth. `.brainlink/brainlink.db` is only a rebuildable index.
60
+ Brainlink now keeps an automatic rollback snapshot at `.brainlink/brainlink.db.backup`. If the main SQLite file is corrupted, Brainlink automatically restores from snapshot (or recreates a clean index when no snapshot exists).
60
61
 
61
62
  ## Features
62
63
 
@@ -106,6 +107,8 @@ npx @andespindola/brainlink@latest --help
106
107
 
107
108
  This section is written for agents. If you are an AI agent operating in a user workspace, use this workflow to install and use Brainlink without requiring the user to run commands manually.
108
109
 
110
+ For a condensed setup path, see [docs/QUICKSTART.md](./docs/QUICKSTART.md).
111
+
109
112
  ### 1. Check Whether Brainlink Is Installed
110
113
 
111
114
  ```bash
@@ -191,7 +194,9 @@ blink add "Testing Policy" \
191
194
  --content "Run npm run check before final delivery. Related: [[Release Checklist]]. #testing #process"
192
195
  ```
193
196
 
194
- Brainlink does not infer durable graph relationships from generated context. A context result is only a read package for the model. To create a real link in the knowledge graph, the agent must write Markdown that contains an explicit `[[Note Title]]` wiki link and then rebuild the index.
197
+ Brainlink does not infer durable graph relationships from generated context. A context result is only a read package for the model. To create a real link in the knowledge graph, the agent must write Markdown that contains an explicit `[[Note Title]]` wiki link.
198
+
199
+ Writes with `blink add` reindex the vault automatically by default. This can be disabled with `--no-auto-index` and controlled globally with `autoIndexOnWrite` in `brainlink.config.json`.
195
200
 
196
201
  When adding memory, follow this contract:
197
202
 
@@ -200,11 +205,7 @@ When adding memory, follow this contract:
200
205
  - Add retrieval tags such as `#architecture`, `#decision`, `#runbook` or `#preference`.
201
206
  - Do not leave isolated notes unless they are intentionally root concepts.
202
207
 
203
- Rebuild the index:
204
-
205
- ```bash
206
- blink index
207
- ```
208
+ If you disable auto-index, run `blink index` after batched writes.
208
209
 
209
210
  ### 6. Validate Memory Health
210
211
 
@@ -223,7 +224,7 @@ Use this loop during real work:
223
224
  3. Use returned sources as project memory.
224
225
  4. Perform the task.
225
226
  5. Save only durable learnings with `blink add`, including `[[wiki links]]` to related notes.
226
- 6. Run `blink index`.
227
+ 6. Run `blink index` only when auto-index was disabled during a batch.
227
228
  7. Validate with `blink validate`, `blink broken-links` and `blink orphans` when graph links matter.
228
229
 
229
230
  Do not store secrets, credentials, private keys, access tokens or transient chat noise.
@@ -241,8 +242,6 @@ blink add "Auth Decision" \
241
242
  --vault ./vault \
242
243
  --content "We chose JWT for API clients. [[Architecture]] #auth #jwt"
243
244
 
244
- blink index --vault ./vault
245
-
246
245
  blink search "jwt auth" --vault ./vault
247
246
 
248
247
  blink context "how does auth work?" --vault ./vault
@@ -384,18 +383,154 @@ Example MCP client configuration:
384
383
  }
385
384
  ```
386
385
 
386
+ ### One-Command Agent Setup
387
+
388
+ If your agent runtime is Codex-compatible, run:
389
+
390
+ ```bash
391
+ blink agent install --self-test
392
+ blink agent upgrade
393
+ ```
394
+
395
+ This configures `~/.codex/config.toml` with Brainlink MCP (`brainlink-mcp`) so Brainlink is available by default in agent sessions.
396
+
397
+ If you are inside this repository and want plugin gallery setup too:
398
+
399
+ ```bash
400
+ blink agent install --plugin-path ./plugins/brainlink
401
+ ```
402
+
403
+ To verify:
404
+
405
+ ```bash
406
+ blink agent status
407
+ ```
408
+
409
+ For fully automated first run (vault index + health + bootstrap readiness + agent integration):
410
+
411
+ ```bash
412
+ blink quickstart --query "what should I know before this task?" --json
413
+ ```
414
+
415
+ For a locked-down setup, allowlist the vaults that MCP clients may access:
416
+
417
+ ```json
418
+ {
419
+ "mcpServers": {
420
+ "brainlink": {
421
+ "command": "brainlink-mcp",
422
+ "env": {
423
+ "BRAINLINK_ALLOWED_VAULTS": "/absolute/path/to/project-vault,/absolute/path/to/team-vault"
424
+ }
425
+ }
426
+ }
427
+ }
428
+ ```
429
+
430
+ ### Install In MCP Client Stores
431
+
432
+ Brainlink can be exposed to MCP-compatible client stores in two ways:
433
+
434
+ 1. Register the stdio server directly when the client accepts `mcpServers` configuration.
435
+ 2. Register the local plugin from this repository when the client supports a plugin gallery or local marketplace.
436
+
437
+ Direct MCP server setup:
438
+
439
+ ```bash
440
+ npm install -g @andespindola/brainlink@latest
441
+ command -v brainlink-mcp
442
+ ```
443
+
444
+ Use this server configuration in any MCP-compatible client that reads a JSON MCP manifest:
445
+
446
+ ```json
447
+ {
448
+ "mcpServers": {
449
+ "brainlink": {
450
+ "command": "brainlink-mcp"
451
+ }
452
+ }
453
+ }
454
+ ```
455
+
456
+ Local plugin gallery setup:
457
+
458
+ ```bash
459
+ npm install -g @andespindola/brainlink@latest
460
+ git clone https://github.com/andersonflima/brainlink.git "$HOME/brainlink"
461
+ mkdir -p "$HOME/plugins"
462
+ ln -s "$HOME/brainlink/plugins/brainlink" "$HOME/plugins/brainlink"
463
+ ```
464
+
465
+ Then register the plugin in the local marketplace file used by compatible clients:
466
+
467
+ ```bash
468
+ node <<'NODE'
469
+ const fs = require('node:fs')
470
+ const os = require('node:os')
471
+ const path = require('node:path')
472
+
473
+ const marketplacePath = path.join(os.homedir(), '.agents', 'plugins', 'marketplace.json')
474
+ const pluginEntry = {
475
+ name: 'brainlink',
476
+ source: {
477
+ source: 'local',
478
+ path: './plugins/brainlink'
479
+ },
480
+ policy: {
481
+ installation: 'AVAILABLE',
482
+ authentication: 'ON_INSTALL'
483
+ },
484
+ category: 'Productivity'
485
+ }
486
+
487
+ fs.mkdirSync(path.dirname(marketplacePath), { recursive: true })
488
+
489
+ const marketplace = fs.existsSync(marketplacePath)
490
+ ? JSON.parse(fs.readFileSync(marketplacePath, 'utf8'))
491
+ : {
492
+ name: 'local',
493
+ interface: {
494
+ displayName: 'Local'
495
+ },
496
+ plugins: []
497
+ }
498
+
499
+ const plugins = Array.isArray(marketplace.plugins) ? marketplace.plugins : []
500
+ marketplace.plugins = [...plugins.filter((plugin) => plugin?.name !== 'brainlink'), pluginEntry]
501
+
502
+ fs.writeFileSync(marketplacePath, `${JSON.stringify(marketplace, null, 2)}\n`)
503
+ NODE
504
+ ```
505
+
506
+ Restart the client after changing marketplace or MCP configuration so it reloads the Brainlink entry. The plugin starts `brainlink-mcp` and exposes the same tool set listed below.
507
+
387
508
  Available tools:
388
509
 
510
+ - `brainlink_bootstrap`: plug-and-play entrypoint that runs index + health checks and can return context in one call.
511
+ - `brainlink_policy`: read or update bootstrap/context-first policy, including presets (`preset: "fully-auto" | "strict"`).
512
+ - `brainlink_recommendations`: return an automatic action plan so agents can run Brainlink in the recommended order.
389
513
  - `brainlink_context`: read indexed context for a task or question.
390
514
  - `brainlink_search`: search indexed notes.
391
515
  - `brainlink_add_note`: write durable Markdown memory and reindex.
516
+ - `brainlink_add_file`: ingest a local file as a note and reindex.
392
517
  - `brainlink_index`: rebuild the vault index.
518
+ - `brainlink_stats`: read indexed vault statistics.
393
519
  - `brainlink_validate`: validate broken links and orphan notes.
520
+ - `brainlink_sync`: run index, stats, validation, broken-link and orphan checks in one call.
394
521
  - `brainlink_graph`: read indexed graph nodes and weighted links.
395
522
  - `brainlink_broken_links`: list unresolved wiki links.
396
523
  - `brainlink_orphans`: list disconnected notes.
397
524
 
398
- The same linking rule applies through MCP: `brainlink_context` is read-only, and real graph links require Markdown notes with explicit `[[wiki links]]` followed by indexing.
525
+ For the most automatic workflow, start MCP sessions with `brainlink_bootstrap` (optionally with `query`) and then continue with `brainlink_context`/`brainlink_add_note`.
526
+ By default, Brainlink enforces context-first for MCP reads (`enforceContextFirst=true`): non-context read tools return preflight until `brainlink_context` is called for the vault/agent session.
527
+ By default, MCP startup already runs bootstrap on the configured default vault/agent (`autoBootstrapOnStartup=true`), so sessions begin warm.
528
+ By default, Brainlink enforces bootstrap and auto-runs it for read tools when session state is missing or stale (`autoBootstrapOnRead=true`).
529
+ If you disable `autoBootstrapOnRead` through `brainlink_policy`, read tools return a preflight instruction with suggested `brainlink_bootstrap` arguments.
530
+ `brainlink_bootstrap`, `brainlink_policy` and preflight responses include structured `nextActions` so MCP clients can continue automatically without custom parsing.
531
+ For one-call planning, use `brainlink_recommendations` to get the recommended tool sequence for the current vault/agent/query.
532
+
533
+ The same linking rule applies through MCP: `brainlink_context` is read-only, and real graph links require Markdown notes with explicit `[[wiki links]]`. `brainlink_add_note` and `brainlink_add_file` reindex by default and include index + `writeConnectivity` metadata. Brainlink guarantees at least one edge per new note by auto-linking when needed.
399
534
 
400
535
  Agents can raise the importance of a relationship by putting priority markers on the same line as a wiki link:
401
536
 
@@ -420,11 +555,14 @@ The graph UI shows:
420
555
 
421
556
  - notes as nodes
422
557
  - `[[wiki links]]` as weighted edges
423
- - backlinks and outgoing links
424
- - full Markdown content for the selected note
558
+ - details opened on node click (tags, outgoing links, backlinks, full Markdown content)
425
559
  - neutral graph nodes with segment/group metadata
426
560
  - agent selector for isolated views
561
+ - graph filter matches title, path, tags and note content
427
562
  - realtime refresh while `--watch` is enabled
563
+ - graph controls for zoom in, zoom out, fit visible nodes and reset-to-fit-all
564
+ - wheel zoom anchored to cursor position for faster navigation in large graphs
565
+ - floating graph totals (notes, links, tags) below the Brainlink title
428
566
 
429
567
  The server indexes before starting by default. Use `--no-index` to skip that step:
430
568
 
@@ -443,6 +581,7 @@ Routes:
443
581
  - `GET /api/agents`
444
582
  - `GET /api/graph`
445
583
  - `GET /api/graph-layout`
584
+ - `GET /api/graph-node?id=<node-id>`
446
585
  - `GET /api/search?q=<query>&limit=10&mode=hybrid`
447
586
  - `GET /api/context?q=<query>&limit=12&tokens=2000&mode=hybrid`
448
587
  - `GET /api/links`
@@ -464,23 +603,91 @@ Read routes accept `agent=<agent-id>`:
464
603
 
465
604
  Every command works with either `brainlink` or `blink`.
466
605
 
606
+ ### `agent`
607
+
608
+ ```bash
609
+ blink agent install
610
+ blink agent install --self-test
611
+ blink agent upgrade
612
+ blink agent policy --preset fully-auto
613
+ blink agent policy --preset strict
614
+ blink agent policy --enforce-context-first false
615
+ blink agent install --plugin-path ./plugins/brainlink
616
+ blink agent install --mcp-only --allowed-vaults "/absolute/vault,/absolute/team-vault"
617
+ blink agent status
618
+ ```
619
+
620
+ Installs/checks agent integration. `install` writes Brainlink MCP config into `~/.codex/config.toml`.
621
+ When plugin files are available, it also links Brainlink plugin files into `~/plugins/brainlink` and updates `~/.agents/plugins/marketplace.json`.
622
+ With `--self-test`, install also validates MCP block presence, command wiring and local plugin registration signals.
623
+ Use `agent upgrade` on legacy installations to reapply current defaults and run the same self-test diagnostics.
624
+ Use `agent policy --preset fully-auto` for plug-and-play defaults, or `agent policy --preset strict` to require explicit bootstrap calls.
625
+ Both presets keep `enforceContextFirst=true` so Brainlink stays the primary context source for MCP sessions.
626
+
627
+ ### `quickstart`
628
+
629
+ ```bash
630
+ blink quickstart --json
631
+ blink quickstart --vault ./team-vault --agent coding-agent --query "architecture decisions" --json
632
+ blink quickstart --vault ./team-vault --mcp-only --json
633
+ ```
634
+
635
+ Runs index + doctor + stats + validation, refreshes bootstrap session readiness, optionally returns context for a query, and (by default) upgrades local agent integration for plug-and-play MCP usage.
636
+ When `--mode`, `--limit` or `--tokens` are omitted, quickstart uses agent profile defaults when available.
637
+
638
+ ### `config`
639
+
640
+ ```bash
641
+ blink config where
642
+ blink config get vault
643
+ blink config doctor
644
+ blink config doctor --fix
645
+ blink config set-vault /absolute/path/to/existing-vault
646
+ blink config set-vault /absolute/path/to/existing-vault --migrate-from ~/.brainlink/vault
647
+ blink config set-vault "s3://my-memory-bucket/brainlink" --global
648
+ ```
649
+
650
+ `config set-vault` writes configuration through CLI (no manual file edits required).
651
+ By default it writes local config (`./brainlink.config.json`), appends the vault to `allowedVaults`, and migrates Markdown memory from the current configured vault when the target is empty.
652
+ Use `--global` to write to `$BRAINLINK_HOME/brainlink.config.json`, `--no-migrate` to skip migration, and `--no-index` to skip post-migration indexing.
653
+ `config doctor` is dry-run by default; use `--fix` to apply safe config normalization and allowlist fixes.
654
+
655
+ ### `migrate-vault`
656
+
657
+ ```bash
658
+ blink migrate-vault --from ~/.brainlink/vault --to ./team-vault --dry-run
659
+ blink migrate-vault --from ~/.brainlink/vault --to ./team-vault
660
+ blink migrate-vault --from ~/.brainlink/vault --to "s3://my-memory-bucket/brainlink"
661
+ blink migrate-vault --from ~/.brainlink/vault --to ./team-vault --report ./migration-report.json
662
+ ```
663
+
664
+ Runs explicit markdown migration between vaults while preserving conflicts as `.conflict-<timestamp>` files.
665
+ Use `--dry-run` to preview `copied`, `conflicted` and `unchanged` counts before writing.
666
+
467
667
  ### `init`
468
668
 
469
669
  ```bash
470
670
  blink init
471
671
  blink init ./vault
672
+ blink init ./team-vault --migrate-from ~/.brainlink/vault
472
673
  ```
473
674
 
474
675
  Initializes vault metadata. Without an argument, Brainlink initializes the default vault at `$HOME/.brainlink/vault`.
676
+ When initializing an empty custom vault, existing Markdown content from the default vault is copied into it and reindexed so context is not left behind. Use `--no-migrate-existing` to start with an empty custom vault, or `--migrate-from <vault>` to copy from a specific source. Existing target files are never overwritten; conflicting source files are preserved with a `.conflict-<timestamp>` suffix.
475
677
 
476
678
  ### `add`
477
679
 
478
680
  ```bash
479
681
  blink add "Note Title" --agent coding-agent --content "Markdown content"
480
682
  blink add "Note Title" --vault ./vault --agent coding-agent --content "Markdown content"
683
+ blink add "Note Title" --vault ./vault --content-file ./notes.md
684
+ blink add "Note Title" --vault ./vault --content-file ./notes.md --no-auto-index
481
685
  ```
482
686
 
687
+ `--content` and `--content-file` are mutually exclusive. Add `--no-auto-index` when you want to defer reindexing.
688
+
483
689
  Creates a Markdown note under `agents/<agent-id>/`. Common secret patterns are blocked by default; use `--allow-sensitive` only for an intentionally protected vault.
690
+ To avoid disconnected memory, Brainlink auto-adds a fallback wiki edge when a note is written without links, creating agent hub notes when needed.
484
691
 
485
692
  ### `index`
486
693
 
@@ -509,6 +716,7 @@ blink search "query" --vault ./vault --mode semantic --json
509
716
  ```
510
717
 
511
718
  Runs retrieval over indexed chunks.
719
+ If `--mode` or `--limit` is omitted, Brainlink resolves values from the current agent profile before falling back to global defaults.
512
720
 
513
721
  Modes:
514
722
 
@@ -516,6 +724,8 @@ Modes:
516
724
  - `fts`: exact lexical retrieval through SQLite FTS.
517
725
  - `semantic`: local deterministic embedding similarity only.
518
726
 
727
+ Hybrid results are cached in-memory for a short TTL and invalidated automatically when the local index file changes.
728
+
519
729
  ### `context`
520
730
 
521
731
  ```bash
@@ -558,9 +768,11 @@ Prints indexed graph data. Edges include `weight` and `priority` so agents can c
558
768
  ```bash
559
769
  blink stats --vault ./vault
560
770
  blink stats --vault ./vault --agent coding-agent --json
771
+ blink stats --vault ./vault --agent coding-agent --extended --json
561
772
  ```
562
773
 
563
774
  Prints vault metrics.
775
+ Use `--extended` to include storage footprint, link quality ratios and observability probes (`index`, `search`, `context` latencies).
564
776
 
565
777
  ### `broken-links`
566
778
 
@@ -592,7 +804,7 @@ Validates graph health. The command exits non-zero when required checks fail.
592
804
  blink doctor --vault ./vault
593
805
  ```
594
806
 
595
- Runs environment and vault checks.
807
+ Runs environment and vault checks. When vault has zero markdown and zero indexed documents, `doctor` prints recommended next steps (add note, inspect config source, migrate memory).
596
808
 
597
809
  ### `watch`
598
810
 
@@ -629,7 +841,13 @@ npm run --silent dev -- context "question" --vault ./vault --json
629
841
 
630
842
  ## Configuration
631
843
 
632
- Brainlink reads `brainlink.config.json` or `.brainlink.json` from the current working directory. If no `vault` is configured and no `--vault` flag is passed, Brainlink uses `$HOME/.brainlink/vault`.
844
+ Brainlink merges configuration in this order:
845
+
846
+ 1. Global: `$BRAINLINK_HOME/brainlink.config.json` (or `$HOME/.brainlink/brainlink.config.json` by default)
847
+ 2. Local: `brainlink.config.json` in the current working directory
848
+ 3. Local legacy compatibility: `.brainlink.json` in the current working directory
849
+
850
+ If no `vault` is configured and no `--vault` flag is passed, Brainlink uses `$HOME/.brainlink/vault`.
633
851
 
634
852
  ```json
635
853
  {
@@ -638,15 +856,29 @@ Brainlink reads `brainlink.config.json` or `.brainlink.json` from the current wo
638
856
  "port": 4321,
639
857
  "allowedVaults": [".brainlink-vault"],
640
858
  "defaultAgent": "shared",
859
+ "autoIndexOnWrite": true,
641
860
  "defaultSearchLimit": 10,
642
861
  "defaultContextTokens": 2000,
643
862
  "embeddingProvider": "local",
644
863
  "defaultSearchMode": "hybrid",
645
- "chunkSize": 1200
864
+ "chunkSize": 1200,
865
+ "agentProfiles": {
866
+ "coding-agent": {
867
+ "defaultSearchMode": "semantic",
868
+ "defaultSearchLimit": 8,
869
+ "defaultContextTokens": 2400
870
+ },
871
+ "*": {
872
+ "defaultSearchMode": "hybrid"
873
+ }
874
+ }
646
875
  }
876
+ ```
647
877
 
648
878
  `defaultAgent` is optional. When set, CLI and MCP calls that omit `--agent`/`agent` use this value automatically. If not set, behavior remains as before.
649
- ```
879
+ `agentProfiles` is optional. When present, CLI and MCP resolve `mode`, `limit` and `tokens` per agent automatically, then fallback to global defaults.
880
+
881
+ `autoIndexOnWrite` is optional and defaults to `true`. Set it to `false` to defer indexing after writes.
650
882
 
651
883
  Use `"embeddingProvider": "none"` when you want FTS-only indexing.
652
884
 
@@ -759,17 +991,18 @@ Detailed notes:
759
991
  - HTTP API is local and unauthenticated.
760
992
  - Watch mode depends on the platform filesystem watcher.
761
993
 
762
- ## Alpha Scope
994
+ ## Beta Scope
763
995
 
764
- `0.1.0-alpha.0` is intended to prove the local-first memory loop:
996
+ The `0.1.0-beta` line is intended to stabilize the local-first memory loop:
765
997
 
766
998
  - Markdown as durable memory.
767
999
  - SQLite FTS plus local embeddings and semantic buckets as rebuildable retrieval index.
768
1000
  - CLI as the primary agent interface.
769
1001
  - HTTP graph API and frontend as inspection tools.
770
1002
  - Agent namespaces to avoid context mixing.
1003
+ - MCP tools for context retrieval, durable memory writes and graph maintenance.
771
1004
 
772
- The alpha includes local semantic retrieval. Remote embedding providers, remote auth, advanced deduplication and graph editing are future milestones.
1005
+ The beta includes local semantic retrieval. Remote embedding providers, remote auth, advanced deduplication and graph editing are future milestones.
773
1006
 
774
1007
  ## Security
775
1008
 
@@ -1,30 +1,79 @@
1
+ import { access } from 'node:fs/promises';
2
+ import { join } from 'node:path';
1
3
  import { writeMarkdownFile } from '../infrastructure/file-system-vault.js';
2
4
  import { sanitizeAgentId, sharedAgentId } from '../domain/agents.js';
5
+ import { extractWikiLinks } from '../domain/markdown.js';
3
6
  import { validateNoteInput } from '../domain/note-safety.js';
7
+ import { ensureVault } from '../infrastructure/file-system-vault.js';
4
8
  const slugify = (title) => title
5
9
  .normalize('NFKD')
6
10
  .replace(/[\u0300-\u036f]/g, '')
7
11
  .toLowerCase()
8
12
  .replace(/[^a-z0-9]+/g, '-')
9
13
  .replace(/^-+|-+$/g, '');
10
- export const addNote = async (vaultPath, title, content, agentId = sharedAgentId, options = {}) => {
14
+ const systemHubTitle = 'Memory Hub';
15
+ const systemRootTitle = 'Knowledge Root';
16
+ const normalizeTitle = (title) => title.trim().replace(/\.md$/i, '').toLowerCase();
17
+ const noteFilename = (agentId, title) => `agents/${agentId}/${slugify(title) || 'untitled'}.md`;
18
+ const buildNote = (title, content, agentId) => [
19
+ `---`,
20
+ `title: "${title.replaceAll('"', '\\"')}"`,
21
+ `agent: "${agentId}"`,
22
+ `---`,
23
+ '',
24
+ `# ${title}`,
25
+ '',
26
+ content.trim(),
27
+ ''
28
+ ].join('\n');
29
+ const ensureSystemNote = async (vaultPath, absoluteVaultPath, agentId, title, content) => {
30
+ const filename = noteFilename(agentId, title);
31
+ const absolutePath = join(absoluteVaultPath, filename);
32
+ try {
33
+ await access(absolutePath);
34
+ return;
35
+ }
36
+ catch { }
37
+ await writeMarkdownFile(vaultPath, filename, buildNote(title, content, agentId));
38
+ };
39
+ const ensureNonOrphanContent = async (vaultPath, absoluteVaultPath, title, content, agentId) => {
40
+ const links = extractWikiLinks(content).filter((link) => normalizeTitle(link) !== normalizeTitle(title));
41
+ if (links.length > 0) {
42
+ return {
43
+ content: content.trim(),
44
+ autoLinked: false,
45
+ linkTarget: null
46
+ };
47
+ }
48
+ const fallbackTitle = normalizeTitle(title) === normalizeTitle(systemHubTitle) ? systemRootTitle : systemHubTitle;
49
+ if (fallbackTitle === systemRootTitle) {
50
+ await ensureSystemNote(vaultPath, absoluteVaultPath, agentId, systemRootTitle, `Entry point for agent memory. [[${systemHubTitle}]] #memory #root`);
51
+ }
52
+ else {
53
+ await ensureSystemNote(vaultPath, absoluteVaultPath, agentId, systemHubTitle, 'Central memory index for this agent namespace. #memory #hub');
54
+ }
55
+ return {
56
+ content: `${content.trim()}\n\nRelated: [[${fallbackTitle}]]`,
57
+ autoLinked: true,
58
+ linkTarget: fallbackTitle
59
+ };
60
+ };
61
+ export const addNoteWithMetadata = async (vaultPath, title, content, agentId = sharedAgentId, options = {}) => {
11
62
  validateNoteInput({
12
63
  title,
13
64
  content,
14
65
  allowSensitive: options.allowSensitive
15
66
  });
16
67
  const sanitizedAgentId = sanitizeAgentId(agentId);
68
+ const absoluteVaultPath = await ensureVault(vaultPath);
17
69
  const filename = `agents/${sanitizedAgentId}/${slugify(title) || 'untitled'}.md`;
18
- const note = [
19
- `---`,
20
- `title: "${title.replaceAll('"', '\\"')}"`,
21
- `agent: "${sanitizedAgentId}"`,
22
- `---`,
23
- '',
24
- `# ${title}`,
25
- '',
26
- content.trim(),
27
- ''
28
- ].join('\n');
29
- return writeMarkdownFile(vaultPath, filename, note);
70
+ const linkedContent = await ensureNonOrphanContent(vaultPath, absoluteVaultPath, title, content, sanitizedAgentId);
71
+ const note = buildNote(title, linkedContent.content, sanitizedAgentId);
72
+ const path = await writeMarkdownFile(vaultPath, filename, note);
73
+ return {
74
+ path,
75
+ autoLinked: linkedContent.autoLinked,
76
+ linkTarget: linkedContent.linkTarget
77
+ };
30
78
  };
79
+ export const addNote = async (vaultPath, title, content, agentId = sharedAgentId, options = {}) => (await addNoteWithMetadata(vaultPath, title, content, agentId, options)).path;