@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 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 promotes only representative graph links per note: high-priority and high-weight links win, structural hub links such as `Memory Hub`, `Knowledge Root`, `MOC` and map notes are suppressed when stronger direct links exist, and old indexes are rebuilt automatically when their graph link model version is missing or stale.
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
- - representative `[[wiki links]]` as weighted edges
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
- previousState.graphLinkModelVersion !== graphLinkModelVersion;
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 = settingsChanged ||
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
- : manifestRecovery.repaired
230
- ? 'Pack manifest repaired from existing packs'
231
- : settingsChanged
232
- ? 'Index settings changed'
233
- : packSettingsChanged
234
- ? 'Search pack settings changed'
235
- : hasDeletes
236
- ? 'Document deletions detected'
237
- : changedCount >= 400
238
- ? 'Changed file count threshold reached'
239
- : changeRatio >= 0.04
240
- ? 'Change ratio threshold reached'
241
- : pendingPackChanges >= 1200
242
- ? 'Pending pack changes threshold reached'
243
- : 'Pack rebuild skipped';
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
- print(options.json, result, () => `Indexed ${result.documentCount} documents, ${result.chunkCount} chunks and ${result.linkCount} links`);
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')
@@ -18,9 +18,7 @@ const priorityBoosts = {
18
18
  high: 3,
19
19
  critical: 6
20
20
  };
21
- const graphLinkLimit = 4;
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
- const sorted = [...links].sort(compareGraphLinks);
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) {
@@ -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
@@ -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 promotes only representative links per note: high-priority and high-weight links win, structural hub links such as `Memory Hub`, `Knowledge Root`, `MOC` and map notes are not promoted when stronger direct links exist, and each note emits a small bounded set of graph edges. Older indexes without the current graph link model version are automatically rebuilt on the next index run.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andespindola/brainlink",
3
- "version": "0.1.0-beta.150",
3
+ "version": "0.1.0-beta.151",
4
4
  "description": "Local-first knowledge memory for agents with Markdown, backlinks, indexing and context retrieval.",
5
5
  "type": "module",
6
6
  "license": "MIT",