@andespindola/brainlink 0.1.0-beta.150 → 0.1.0-beta.151
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.
- package/CHANGELOG.md +3 -0
- package/README.md +8 -5
- package/dist/application/index-vault.js +32 -21
- package/dist/cli/commands/write-commands.js +7 -2
- package/dist/domain/markdown.js +2 -9
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/tools.js +9 -2
- package/docs/AGENT_USAGE.md +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
- Added default MCP startup bootstrap behavior controlled by `brainlink_policy.autoBootstrapOnStartup`.
|
|
15
15
|
- Added CLI MCP policy presets through `blink agent policy --preset fully-auto|strict`.
|
|
16
16
|
- Added write-time non-orphan enforcement by auto-linking notes without wiki edges to agent hub notes.
|
|
17
|
+
- Changed graph indexing to keep every non-self Markdown wiki link as a weighted graph edge so the default star graph represents complete note connectivity.
|
|
18
|
+
- Added `blink index --full` and MCP `brainlink_index` `full=true` for complete source reindexing without clearing the existing index first.
|
|
19
|
+
- Improved index migration so stale graph link model metadata automatically triggers a complete source reindex.
|
|
17
20
|
- Added MCP `brainlink_policy` presets (`fully-auto`, `strict`) for one-call policy switching.
|
|
18
21
|
- Added MCP write connectivity metadata in `brainlink_add_note`/`brainlink_add_file` responses.
|
|
19
22
|
- Added MCP `brainlink_recommendations` tool for plug-and-play workflow guidance.
|
package/README.md
CHANGED
|
@@ -538,7 +538,7 @@ Available tools:
|
|
|
538
538
|
- `brainlink_add_file`: ingest a local file as a note and reindex.
|
|
539
539
|
- `brainlink_volatile_add`: write temporary agent-decided memory with TTL; volatile sections are included in context and never create durable graph edges.
|
|
540
540
|
- `brainlink_volatile_clear`: clear temporary memory for the current vault/agent namespace.
|
|
541
|
-
- `brainlink_index`: rebuild the vault index.
|
|
541
|
+
- `brainlink_index`: rebuild the vault index. Pass `full=true` for a complete source reindex.
|
|
542
542
|
- `brainlink_stats`: read indexed vault statistics.
|
|
543
543
|
- `brainlink_validate`: validate broken links and orphan notes.
|
|
544
544
|
- `brainlink_sync`: run index, stats, validation, broken-link and orphan checks in one call.
|
|
@@ -563,7 +563,7 @@ Agents can raise the importance of a relationship by putting priority markers on
|
|
|
563
563
|
Related: [[Incident Runbook]] #critical
|
|
564
564
|
```
|
|
565
565
|
|
|
566
|
-
Indexed edges expose `weight` and `priority` (`low`, `normal`, `high`, `critical`) through CLI JSON, HTTP graph APIs and `brainlink_graph`. Brainlink
|
|
566
|
+
Indexed edges expose `weight` and `priority` (`low`, `normal`, `high`, `critical`) through CLI JSON, HTTP graph APIs and `brainlink_graph`. Brainlink indexes every non-self Markdown `[[wiki link]]` as a graph edge, including structural hub links such as `Memory Hub`, `Knowledge Root`, `MOC` and map notes. Old indexes are rebuilt automatically when their graph link model version is missing or stale.
|
|
567
567
|
|
|
568
568
|
## Graph UI
|
|
569
569
|
|
|
@@ -589,8 +589,8 @@ When native GUI is used, the GUI window automatically closes when the `blink ser
|
|
|
589
589
|
The graph UI shows:
|
|
590
590
|
|
|
591
591
|
- notes as nodes
|
|
592
|
-
-
|
|
593
|
-
- star layout centered on the primary hub, without rewriting or flattening underlying relationships
|
|
592
|
+
- all non-self `[[wiki links]]` as weighted edges
|
|
593
|
+
- default star layout centered on the primary hub, without rewriting or flattening underlying relationships
|
|
594
594
|
- details opened in a non-modal side panel (tags, outgoing links, backlinks, full Markdown content), so zoom and pan remain available while inspecting data
|
|
595
595
|
- neutral graph nodes with segment/group metadata
|
|
596
596
|
- agent selector (id-only labels) for isolated views
|
|
@@ -777,9 +777,12 @@ When action is not `merge`, Brainlink still creates a low-priority related edge
|
|
|
777
777
|
```bash
|
|
778
778
|
blink index
|
|
779
779
|
blink index --vault ./vault
|
|
780
|
+
blink index --vault ./vault --full
|
|
780
781
|
```
|
|
781
782
|
|
|
782
|
-
Rebuilds the local index from Markdown files.
|
|
783
|
+
Rebuilds the local index from Markdown files. By default, unchanged notes reuse existing indexed chunks for speed.
|
|
784
|
+
Use `--full` to force a complete source reindex of every Markdown note. Full reindex builds the replacement index before persisting it, so existing context is not cleared first.
|
|
785
|
+
Brainlink also performs this complete source reindex automatically when it detects that the stored graph link model is older than the current model.
|
|
783
786
|
|
|
784
787
|
### `bench`
|
|
785
788
|
|
|
@@ -85,8 +85,8 @@ const readChangedDocuments = async (absoluteVaultPath, changedSummaries) => {
|
|
|
85
85
|
})));
|
|
86
86
|
return new Map(parsed.map((document) => [document.path, document]));
|
|
87
87
|
};
|
|
88
|
-
export const indexVault = async (vaultPath) => {
|
|
89
|
-
return indexVaultWithOptions(vaultPath,
|
|
88
|
+
export const indexVault = async (vaultPath, options = {}) => {
|
|
89
|
+
return indexVaultWithOptions(vaultPath, options);
|
|
90
90
|
};
|
|
91
91
|
export const indexVaultWithOptions = async (vaultPath, options) => {
|
|
92
92
|
const startedAt = process.hrtime.bigint();
|
|
@@ -113,6 +113,7 @@ export const indexVaultWithOptions = async (vaultPath, options) => {
|
|
|
113
113
|
markdownFiles: summaries.length,
|
|
114
114
|
hasPreviousState: previousState != null
|
|
115
115
|
});
|
|
116
|
+
const fullReindex = options.full === true;
|
|
116
117
|
const index = openFileIndex(absoluteVaultPath);
|
|
117
118
|
try {
|
|
118
119
|
const existingIndexedDocuments = await index.getIndexedDocuments();
|
|
@@ -120,10 +121,13 @@ export const indexVaultWithOptions = async (vaultPath, options) => {
|
|
|
120
121
|
const currentSnapshot = toSnapshot(summaries);
|
|
121
122
|
const currentSnapshotMap = createSnapshotMap(currentSnapshot);
|
|
122
123
|
const previousSnapshotMap = createSnapshotMap(previousState?.files ?? []);
|
|
124
|
+
const graphLinkModelChanged = previousState != null &&
|
|
125
|
+
previousState.graphLinkModelVersion !== graphLinkModelVersion;
|
|
126
|
+
const fullSourceReindex = fullReindex || graphLinkModelChanged;
|
|
123
127
|
const settingsChanged = previousState == null ||
|
|
124
128
|
previousState.chunkSize !== config.chunkSize ||
|
|
125
129
|
previousState.embeddingProvider !== config.embeddingProvider ||
|
|
126
|
-
|
|
130
|
+
graphLinkModelChanged;
|
|
127
131
|
const packSettingsChanged = previousState == null ||
|
|
128
132
|
previousState.searchPackRowChunkSize !== config.searchPack.rowChunkSize ||
|
|
129
133
|
previousState.searchPackCompressionLevel !== config.searchPack.compressionLevel ||
|
|
@@ -132,7 +136,8 @@ export const indexVaultWithOptions = async (vaultPath, options) => {
|
|
|
132
136
|
for (let index = 0; index < summaries.length; index += 1) {
|
|
133
137
|
const summary = summaries[index];
|
|
134
138
|
const previous = previousSnapshotMap.get(summary.relativePath);
|
|
135
|
-
const changed =
|
|
139
|
+
const changed = fullSourceReindex ||
|
|
140
|
+
settingsChanged ||
|
|
136
141
|
previous == null ||
|
|
137
142
|
previous.mtimeMs !== summary.updatedAt.getTime() ||
|
|
138
143
|
previous.size !== summary.size ||
|
|
@@ -148,7 +153,8 @@ export const indexVaultWithOptions = async (vaultPath, options) => {
|
|
|
148
153
|
if (changedPaths.size === 0 &&
|
|
149
154
|
!hasDeletes &&
|
|
150
155
|
existingIndexedDocuments.length === summaries.length &&
|
|
151
|
-
previousState != null
|
|
156
|
+
previousState != null &&
|
|
157
|
+
!fullReindex) {
|
|
152
158
|
const result = {
|
|
153
159
|
...toIndexResult(existingIndexedDocuments),
|
|
154
160
|
elapsedMs: elapsedMs(),
|
|
@@ -205,7 +211,6 @@ export const indexVaultWithOptions = async (vaultPath, options) => {
|
|
|
205
211
|
return needsRelink ? relinkIndexedDocument(existing, titleMaps) : existing;
|
|
206
212
|
});
|
|
207
213
|
emit('persist', 'start', 'Persisting index');
|
|
208
|
-
await index.reset();
|
|
209
214
|
await index.saveDocuments(indexedDocuments);
|
|
210
215
|
emit('persist', 'finish', 'Index persisted', {
|
|
211
216
|
indexedDocuments: indexedDocuments.length
|
|
@@ -217,6 +222,8 @@ export const indexVaultWithOptions = async (vaultPath, options) => {
|
|
|
217
222
|
const previousPendingPackChanges = previousState?.pendingPackChanges ?? 0;
|
|
218
223
|
const pendingPackChanges = previousPendingPackChanges + changedCount;
|
|
219
224
|
const shouldRebuildPacks = !existingPackManifest ||
|
|
225
|
+
fullReindex ||
|
|
226
|
+
graphLinkModelChanged ||
|
|
220
227
|
settingsChanged ||
|
|
221
228
|
packSettingsChanged ||
|
|
222
229
|
hasDeletes ||
|
|
@@ -226,21 +233,25 @@ export const indexVaultWithOptions = async (vaultPath, options) => {
|
|
|
226
233
|
let packResult;
|
|
227
234
|
const packReason = !existingPackManifest
|
|
228
235
|
? 'Missing pack manifest'
|
|
229
|
-
:
|
|
230
|
-
? '
|
|
231
|
-
:
|
|
232
|
-
? '
|
|
233
|
-
:
|
|
234
|
-
? '
|
|
235
|
-
:
|
|
236
|
-
? '
|
|
237
|
-
:
|
|
238
|
-
? '
|
|
239
|
-
:
|
|
240
|
-
? '
|
|
241
|
-
:
|
|
242
|
-
? '
|
|
243
|
-
:
|
|
236
|
+
: fullReindex
|
|
237
|
+
? 'Full reindex requested'
|
|
238
|
+
: graphLinkModelChanged
|
|
239
|
+
? 'Graph link model changed'
|
|
240
|
+
: manifestRecovery.repaired
|
|
241
|
+
? 'Pack manifest repaired from existing packs'
|
|
242
|
+
: settingsChanged
|
|
243
|
+
? 'Index settings changed'
|
|
244
|
+
: packSettingsChanged
|
|
245
|
+
? 'Search pack settings changed'
|
|
246
|
+
: hasDeletes
|
|
247
|
+
? 'Document deletions detected'
|
|
248
|
+
: changedCount >= 400
|
|
249
|
+
? 'Changed file count threshold reached'
|
|
250
|
+
: changeRatio >= 0.04
|
|
251
|
+
? 'Change ratio threshold reached'
|
|
252
|
+
: pendingPackChanges >= 1200
|
|
253
|
+
? 'Pending pack changes threshold reached'
|
|
254
|
+
: 'Pack rebuild skipped';
|
|
244
255
|
if (shouldRebuildPacks) {
|
|
245
256
|
emit('packs', 'start', 'Rebuilding compressed search packs', {
|
|
246
257
|
reason: packReason
|
|
@@ -860,12 +860,17 @@ export const registerWriteCommands = (program) => {
|
|
|
860
860
|
program
|
|
861
861
|
.command('index')
|
|
862
862
|
.option('-v, --vault <vault>', 'vault directory')
|
|
863
|
+
.option('--full', 'force a complete reindex from Markdown source without reusing unchanged index entries')
|
|
863
864
|
.option('--json', 'print machine-readable JSON')
|
|
864
865
|
.description('index markdown notes, links, tags and chunks')
|
|
865
866
|
.action(async (options) => {
|
|
866
867
|
const resolved = await resolveOptions(options);
|
|
867
|
-
const result = await indexVault(resolved.vault
|
|
868
|
-
|
|
868
|
+
const result = await indexVault(resolved.vault, {
|
|
869
|
+
full: options.full === true
|
|
870
|
+
});
|
|
871
|
+
print(options.json, result, () => options.full === true
|
|
872
|
+
? `Fully reindexed ${result.documentCount} documents, ${result.chunkCount} chunks and ${result.linkCount} links`
|
|
873
|
+
: `Indexed ${result.documentCount} documents, ${result.chunkCount} chunks and ${result.linkCount} links`);
|
|
869
874
|
});
|
|
870
875
|
program
|
|
871
876
|
.command('bench')
|
package/dist/domain/markdown.js
CHANGED
|
@@ -18,9 +18,7 @@ const priorityBoosts = {
|
|
|
18
18
|
high: 3,
|
|
19
19
|
critical: 6
|
|
20
20
|
};
|
|
21
|
-
const
|
|
22
|
-
export const graphLinkModelVersion = 2;
|
|
23
|
-
const hubLinkTitlePattern = /\b(?:memory\s*hub|knowledge\s*root|moc|map)\b/i;
|
|
21
|
+
export const graphLinkModelVersion = 3;
|
|
24
22
|
const priorityPatterns = [
|
|
25
23
|
['critical', /\b(?:priority|prioridade|importance|importancia|importância)\s*[:=]\s*(?:critical|critica|crítica|urgent|urgente|p0)\b/i],
|
|
26
24
|
['critical', /#(?:critical|critica|crítica|urgent|urgente|p0)\b/i],
|
|
@@ -34,7 +32,6 @@ const priorityPatterns = [
|
|
|
34
32
|
const normalizeTitle = (title) => title.trim().replace(/\.md$/i, '');
|
|
35
33
|
const unique = (values) => Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)));
|
|
36
34
|
const maxPriority = (left, right) => priorityRanks[left] >= priorityRanks[right] ? left : right;
|
|
37
|
-
const isHubLinkTitle = (title) => hubLinkTitlePattern.test(title);
|
|
38
35
|
const parseFrontmatter = (content) => {
|
|
39
36
|
const match = content.match(frontmatterPattern);
|
|
40
37
|
if (!match) {
|
|
@@ -114,11 +111,7 @@ const compareGraphLinks = (left, right) => {
|
|
|
114
111
|
return left.title.localeCompare(right.title);
|
|
115
112
|
};
|
|
116
113
|
export const selectGraphWikiLinkWeights = (links) => {
|
|
117
|
-
|
|
118
|
-
const structuralLinks = sorted.filter((link) => isHubLinkTitle(link.title) && !['high', 'critical'].includes(link.priority));
|
|
119
|
-
const directLinks = sorted.filter((link) => !structuralLinks.includes(link));
|
|
120
|
-
const selected = (directLinks.length > 0 ? directLinks : sorted).slice(0, graphLinkLimit);
|
|
121
|
-
return selected.length > 0 ? selected : structuralLinks.slice(0, graphLinkLimit);
|
|
114
|
+
return [...links].sort(compareGraphLinks);
|
|
122
115
|
};
|
|
123
116
|
const extractTitle = (filePath, content, frontmatter) => {
|
|
124
117
|
if (frontmatter.title) {
|
package/dist/mcp/server.js
CHANGED
|
@@ -70,7 +70,7 @@ export const createBrainlinkMcpServer = () => {
|
|
|
70
70
|
}, addFileTool);
|
|
71
71
|
server.registerTool('brainlink_index', {
|
|
72
72
|
title: 'Index Brainlink Vault',
|
|
73
|
-
description: 'Rebuild the local Brainlink index from Markdown notes.',
|
|
73
|
+
description: 'Rebuild the local Brainlink index from Markdown notes. Pass full=true to force a complete source rebuild.',
|
|
74
74
|
inputSchema: indexInputSchema
|
|
75
75
|
}, indexTool);
|
|
76
76
|
server.registerTool('brainlink_stats', {
|
package/dist/mcp/tools.js
CHANGED
|
@@ -262,7 +262,12 @@ export const addFileInputSchema = {
|
|
|
262
262
|
allowSensitive: z.boolean().optional().default(false).describe('Allow content that looks like a secret.')
|
|
263
263
|
};
|
|
264
264
|
export const indexInputSchema = {
|
|
265
|
-
...vaultInput
|
|
265
|
+
...vaultInput,
|
|
266
|
+
full: z
|
|
267
|
+
.boolean()
|
|
268
|
+
.optional()
|
|
269
|
+
.default(false)
|
|
270
|
+
.describe('Force a complete reindex from Markdown source without reusing unchanged index entries.')
|
|
266
271
|
};
|
|
267
272
|
export const validateInputSchema = {
|
|
268
273
|
...vaultInput,
|
|
@@ -469,7 +474,9 @@ export const addFileTool = async (input) => {
|
|
|
469
474
|
};
|
|
470
475
|
export const indexTool = async (input) => {
|
|
471
476
|
const context = await resolveExecutionContext(input);
|
|
472
|
-
const result = await indexVault(context.vault
|
|
477
|
+
const result = await indexVault(context.vault, {
|
|
478
|
+
full: input.full === true
|
|
479
|
+
});
|
|
473
480
|
return jsonResult({
|
|
474
481
|
vault: context.vault,
|
|
475
482
|
...result
|
package/docs/AGENT_USAGE.md
CHANGED
|
@@ -161,7 +161,7 @@ Brainlink only builds graph edges from Markdown `[[wiki links]]`.
|
|
|
161
161
|
|
|
162
162
|
The `context` command is read-only. It retrieves indexed notes and returns a compact package for the model, but it does not write memory, create backlinks, infer relationships or modify the graph. If an agent reads context and then learns something durable, the agent must write a note with explicit links before that knowledge becomes connected memory.
|
|
163
163
|
|
|
164
|
-
Graph edges are weighted during indexing. Repeated links increase weight. Links inside headings or task-list lines receive a small boost. Priority markers on the same line as a link raise its priority. The graph relationship model
|
|
164
|
+
Graph edges are weighted during indexing. Repeated links increase weight. Links inside headings or task-list lines receive a small boost. Priority markers on the same line as a link raise its priority. The graph relationship model indexes every non-self Markdown `[[wiki link]]` as an edge, including structural hub links such as `Memory Hub`, `Knowledge Root`, `MOC` and map notes. Older indexes without the current graph link model version are automatically rebuilt on the next index run.
|
|
165
165
|
|
|
166
166
|
```md
|
|
167
167
|
- [ ] Review [[Architecture]] priority: high
|
|
@@ -292,6 +292,8 @@ blink add "Implementation Boundary" \
|
|
|
292
292
|
blink index
|
|
293
293
|
```
|
|
294
294
|
|
|
295
|
+
Use `blink index --full` when you need to rebuild every note from Markdown source. Full reindexing builds the replacement index before persisting it, preserving the previous context until the new index is ready. Brainlink also runs this complete source reindex automatically when it detects an older stored graph link model.
|
|
296
|
+
|
|
295
297
|
### Claude Code-Style Agent
|
|
296
298
|
|
|
297
299
|
Use Brainlink as a preflight memory read before editing files:
|
package/package.json
CHANGED