@unson/brainbase-mcp 0.10.3 → 0.10.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unson/brainbase-mcp",
3
- "version": "0.10.3",
3
+ "version": "0.10.4",
4
4
  "description": "Local-first Brainbase judgment OS that lets supported AI agents reuse a user's approved goals, priorities, decisions, and judgment criteria.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -336,5 +336,5 @@
336
336
  "engines": {
337
337
  "node": ">=20"
338
338
  },
339
- "gitHead": "8abcea403a82666c8c4616d4023fb28a10af761a"
339
+ "gitHead": "84863f09b8274b738b16e15d2550ece19ac51124"
340
340
  }
@@ -325,16 +325,62 @@ function contextSectionSkeleton(doc, { title = null, message = null } = {}) {
325
325
  return group;
326
326
  }
327
327
 
328
+ function renderProjectContextGroup(doc, item) {
329
+ const group = makeElement(doc, 'section', {
330
+ className: `bb-pkw-context-group${item.state === 'loading' ? ' bb-pkw-context-group-loading' : ''}`,
331
+ attrs: { 'aria-label': item.title, ...(item.state === 'loading' ? { 'aria-busy': 'true' } : {}) },
332
+ });
333
+ const heading = makeElement(doc, 'div', { className: 'bb-pkw-context-heading' });
334
+ heading.append(makeElement(doc, 'h4', { text: item.title }), badge(doc, item.state === 'ok' ? '利用可能' : item.state === 'loading' ? '読み込み中' : item.state === 'failed' ? '読み取り失敗' : '未接続', item.state === 'ok' ? 'success' : item.state === 'failed' ? 'danger' : 'muted'));
335
+ group.append(heading);
336
+ if (item.message && !['failed', 'unavailable'].includes(item.state)) group.append(makeElement(doc, 'p', { className: 'bb-pkw-muted', text: item.message }));
337
+ if (item.asOf) group.append(makeElement(doc, 'small', { className: 'bb-pkw-muted', text: `読み取った時点: ${formatDateTime(item.asOf)}` }));
338
+ if (item.state === 'loading') {
339
+ group.append(skeletonBars(doc));
340
+ } else if (item.state === 'failed') {
341
+ group.append(workspaceNotice(doc, { label: '読み取り失敗', text: item.message ?? 'このセクションを読み取れませんでした。', tone: 'danger', role: 'alert' }));
342
+ } else if (item.state === 'unavailable') {
343
+ group.append(workspaceNotice(doc, { label: '未接続', text: item.message ?? 'このセクションは接続されていません。', tone: 'warning' }));
344
+ } else if (item.items.length > 0) {
345
+ const list = makeElement(doc, 'ul', { className: 'bb-pkw-context-list' });
346
+ for (const entry of item.items) {
347
+ const row = makeElement(doc, 'li');
348
+ row.append(makeElement(doc, 'strong', { text: entry.title }));
349
+ if (entry.status) row.append(badge(doc, entry.status, 'muted'));
350
+ const facts = [entry.summary, entry.owner ? `担当: ${entry.owner}` : null, entry.dueAt ? `期限: ${entry.dueAt}` : null, entry.updatedAt ? `更新: ${entry.updatedAt}` : null, entry.source ? `出典: ${entry.source}` : null].filter(Boolean);
351
+ if (facts.length > 0) row.append(makeElement(doc, 'p', { className: 'bb-pkw-muted', text: facts.join(' · ') }));
352
+ list.append(row);
353
+ }
354
+ group.append(list);
355
+ } else if (item.state === 'ok') {
356
+ group.append(makeElement(doc, 'p', { className: 'bb-pkw-muted', text: 'このセクションに表示できる記録はありません。' }));
357
+ }
358
+ return group;
359
+ }
360
+
361
+ function projectContextInsertionAnchor(doc) {
362
+ return makeElement(doc, 'span', {
363
+ className: 'bb-pkw-context-anchor',
364
+ attrs: { 'aria-hidden': 'true', hidden: 'true' },
365
+ });
366
+ }
367
+
328
368
  /** Renders supplementary context while preserving each section's own state. */
329
- export function renderProjectContext(doc, context) {
369
+ export function renderProjectContext(doc, context, {
370
+ sections: selectedSections = null,
371
+ includeStatus = true,
372
+ includeHeading = true,
373
+ } = {}) {
330
374
  if (!context) return null;
331
- const sections = Array.isArray(context.sections) ? context.sections : [];
375
+ const sections = Array.isArray(selectedSections)
376
+ ? selectedSections
377
+ : Array.isArray(context.sections) ? context.sections : [];
332
378
  const section = makeElement(doc, 'section', {
333
379
  className: 'bb-pkw-section bb-pkw-context',
334
380
  attrs: { 'aria-label': '補足情報' },
335
381
  });
336
- section.append(makeElement(doc, 'h3', { text: '補足情報' }));
337
- if (context.state !== 'ok') {
382
+ if (includeHeading) section.append(makeElement(doc, 'h3', { text: '補足情報' }));
383
+ if (includeStatus && context.state !== 'ok') {
338
384
  if (context.state === 'loading') {
339
385
  section.append(workspaceNotice(doc, { label: '読み込み中', text: context.message ?? '補足情報を読み込んでいます。', tone: 'info', role: 'status' }));
340
386
  if (sections.length === 0) section.append(contextSectionSkeleton(doc));
@@ -342,42 +388,11 @@ export function renderProjectContext(doc, context) {
342
388
  section.append(workspaceNotice(doc, { label: context.state === 'failed' ? '読み取り失敗' : '未接続', text: context.message ?? '補足情報を確認できません。', tone: context.state === 'failed' ? 'danger' : 'warning' }));
343
389
  }
344
390
  }
345
- if (context.state === 'ok' && sections.length === 0) {
391
+ if (includeStatus && context.state === 'ok' && sections.length === 0) {
346
392
  section.append(makeElement(doc, 'p', { className: 'bb-pkw-muted', text: '補足情報のセクションはありません。' }));
347
393
  return section;
348
394
  }
349
- for (const item of sections) {
350
- const group = makeElement(doc, 'section', {
351
- className: `bb-pkw-context-group${item.state === 'loading' ? ' bb-pkw-context-group-loading' : ''}`,
352
- attrs: { 'aria-label': item.title, ...(item.state === 'loading' ? { 'aria-busy': 'true' } : {}) },
353
- });
354
- const heading = makeElement(doc, 'div', { className: 'bb-pkw-context-heading' });
355
- heading.append(makeElement(doc, 'h4', { text: item.title }), badge(doc, item.state === 'ok' ? '利用可能' : item.state === 'loading' ? '読み込み中' : item.state === 'failed' ? '読み取り失敗' : '未接続', item.state === 'ok' ? 'success' : item.state === 'failed' ? 'danger' : 'muted'));
356
- group.append(heading);
357
- if (item.message && !['failed', 'unavailable'].includes(item.state)) group.append(makeElement(doc, 'p', { className: 'bb-pkw-muted', text: item.message }));
358
- if (item.asOf) group.append(makeElement(doc, 'small', { className: 'bb-pkw-muted', text: `読み取った時点: ${formatDateTime(item.asOf)}` }));
359
- if (item.state === 'loading') {
360
- group.append(skeletonBars(doc));
361
- } else if (item.state === 'failed') {
362
- group.append(workspaceNotice(doc, { label: '読み取り失敗', text: item.message ?? 'このセクションを読み取れませんでした。', tone: 'danger', role: 'alert' }));
363
- } else if (item.state === 'unavailable') {
364
- group.append(workspaceNotice(doc, { label: '未接続', text: item.message ?? 'このセクションは接続されていません。', tone: 'warning' }));
365
- } else if (item.items.length > 0) {
366
- const list = makeElement(doc, 'ul', { className: 'bb-pkw-context-list' });
367
- for (const entry of item.items) {
368
- const row = makeElement(doc, 'li');
369
- row.append(makeElement(doc, 'strong', { text: entry.title }));
370
- if (entry.status) row.append(badge(doc, entry.status, 'muted'));
371
- const facts = [entry.summary, entry.owner ? `担当: ${entry.owner}` : null, entry.dueAt ? `期限: ${entry.dueAt}` : null, entry.updatedAt ? `更新: ${entry.updatedAt}` : null, entry.source ? `出典: ${entry.source}` : null].filter(Boolean);
372
- if (facts.length > 0) row.append(makeElement(doc, 'p', { className: 'bb-pkw-muted', text: facts.join(' · ') }));
373
- list.append(row);
374
- }
375
- group.append(list);
376
- } else if (item.state === 'ok') {
377
- group.append(makeElement(doc, 'p', { className: 'bb-pkw-muted', text: 'このセクションに表示できる記録はありません。' }));
378
- }
379
- section.append(group);
380
- }
395
+ for (const item of sections) section.append(renderProjectContextGroup(doc, item));
381
396
  return section;
382
397
  }
383
398
 
@@ -387,6 +402,12 @@ export function renderProjectKnowledgeSkeleton(doc, { context = null } = {}) {
387
402
  className: 'bb-pkw-panel bb-pkw-overview bb-pkw-overview-loading',
388
403
  attrs: { 'aria-label': '概要' },
389
404
  });
405
+ const contextSections = Array.isArray(context?.sections) ? context.sections : [];
406
+ const readySections = contextSections.filter((item) => item?.state === 'ok' && Array.isArray(item.items) && item.items.length > 0);
407
+ const deferredSections = contextSections.filter((item) => !readySections.includes(item));
408
+ if (readySections.length > 0) {
409
+ panel.append(renderProjectContext(doc, context, { sections: readySections, includeStatus: false }));
410
+ }
390
411
  const columns = makeElement(doc, 'div', { className: 'bb-pkw-columns' });
391
412
  for (const title of ['目的と現在地', '関係者と役割']) {
392
413
  const item = makeElement(doc, 'section', { className: 'bb-pkw-section bb-pkw-skeleton-section', attrs: { 'aria-label': title, 'aria-busy': 'true' } });
@@ -399,7 +420,9 @@ export function renderProjectKnowledgeSkeleton(doc, { context = null } = {}) {
399
420
  item.append(makeElement(doc, 'h3', { text: title }), skeletonBars(doc, 2));
400
421
  panel.append(item);
401
422
  }
402
- const contextBlock = renderProjectContext(doc, context ?? { state: 'loading', sections: [] });
423
+ const contextBlock = readySections.length > 0
424
+ ? (deferredSections.length > 0 ? renderProjectContext(doc, context, { sections: deferredSections, includeHeading: false }) : null)
425
+ : renderProjectContext(doc, context ?? { state: 'loading', sections: [] });
403
426
  if (contextBlock) panel.append(contextBlock);
404
427
  return panel;
405
428
  }
@@ -439,6 +462,16 @@ function renderOverview(doc, detail, context, edges, entities, onSelect, onConte
439
462
  columns.append(people);
440
463
  panel.append(columns);
441
464
 
465
+ const contextBlock = renderProjectContext(doc, context);
466
+ if (contextBlock) {
467
+ panel.append(contextBlock);
468
+ if (typeof onContext === 'function') onContext(contextBlock, null);
469
+ } else {
470
+ const contextAnchor = projectContextInsertionAnchor(doc);
471
+ panel.append(contextAnchor);
472
+ if (typeof onContext === 'function') onContext(null, contextAnchor);
473
+ }
474
+
442
475
  const decisions = makeElement(doc, 'section', { className: 'bb-pkw-section', attrs: { 'aria-label': '記録された判断' } });
443
476
  decisions.append(makeElement(doc, 'h3', { text: '記録された判断' }));
444
477
  const decisionEdges = edges.filter((edge) => entities.get(edge.source)?.type === 'decision' || entities.get(edge.target)?.type === 'decision');
@@ -493,11 +526,6 @@ function renderOverview(doc, detail, context, edges, entities, onSelect, onConte
493
526
  missing.append(list);
494
527
  panel.append(missing);
495
528
  }
496
- const contextBlock = renderProjectContext(doc, context);
497
- if (contextBlock) {
498
- panel.append(contextBlock);
499
- if (typeof onContext === 'function') onContext(contextBlock);
500
- }
501
529
  return panel;
502
530
  }
503
531
 
@@ -543,8 +571,16 @@ export function createProjectKnowledgeWorkspace({
543
571
  let selectedPanel = null;
544
572
  let overviewPanel = null;
545
573
  let contextBlock = null;
546
-
547
- const root = makeElement(doc, 'section', { className: 'bb-pkw', attrs: { 'data-contract-version': PROJECT_WORKSPACE_CONTRACT_VERSION, 'aria-label': 'プロジェクトの知識' } });
574
+ let contextAnchor = null;
575
+
576
+ const root = makeElement(doc, 'section', {
577
+ className: 'bb-pkw',
578
+ attrs: {
579
+ 'data-contract-version': PROJECT_WORKSPACE_CONTRACT_VERSION,
580
+ 'data-project-id': cleanId(project.id),
581
+ 'aria-label': 'プロジェクトの知識',
582
+ },
583
+ });
548
584
  const body = makeElement(doc, 'div', { className: 'bb-pkw-body' });
549
585
  const tabs = makeElement(doc, 'div', { className: 'bb-pkw-tabs', attrs: { role: 'tablist', 'aria-label': 'プロジェクト情報の表示' } });
550
586
  const panels = makeElement(doc, 'div', { className: 'bb-pkw-panels' });
@@ -779,8 +815,15 @@ export function createProjectKnowledgeWorkspace({
779
815
  if (destroyed) return;
780
816
  const nextBlock = renderProjectContext(doc, currentContext);
781
817
  if (!nextBlock) {
782
- if (contextBlock?.parentNode?.removeChild) contextBlock.parentNode.removeChild(contextBlock);
783
- contextBlock = null;
818
+ if (contextBlock?.parentNode && typeof contextBlock.parentNode.replaceChild === 'function') {
819
+ const nextAnchor = projectContextInsertionAnchor(doc);
820
+ contextBlock.parentNode.replaceChild(nextAnchor, contextBlock);
821
+ contextBlock = null;
822
+ contextAnchor = nextAnchor;
823
+ } else if (contextBlock?.parentNode?.removeChild) {
824
+ contextBlock.parentNode.removeChild(contextBlock);
825
+ contextBlock = null;
826
+ }
784
827
  return;
785
828
  }
786
829
  if (contextBlock?.parentNode && typeof contextBlock.parentNode.replaceChild === 'function') {
@@ -788,14 +831,16 @@ export function createProjectKnowledgeWorkspace({
788
831
  contextBlock = nextBlock;
789
832
  return;
790
833
  }
791
- if (contextBlock?.replaceWith) {
792
- contextBlock.replaceWith(nextBlock);
834
+ if (contextAnchor?.parentNode && typeof contextAnchor.parentNode.replaceChild === 'function') {
835
+ contextAnchor.parentNode.replaceChild(nextBlock, contextAnchor);
793
836
  contextBlock = nextBlock;
837
+ contextAnchor = null;
794
838
  return;
795
839
  }
796
- if (overviewPanel && !contextBlock) {
797
- overviewPanel.append(nextBlock);
840
+ if (contextBlock?.replaceWith) {
841
+ contextBlock.replaceWith(nextBlock);
798
842
  contextBlock = nextBlock;
843
+ return;
799
844
  }
800
845
  }
801
846
 
@@ -806,7 +851,11 @@ export function createProjectKnowledgeWorkspace({
806
851
  tabs.replaceChildren(tabButton('概要', 'overview'), tabButton('情報を探す', 'graph'));
807
852
  panels.replaceChildren();
808
853
  contextBlock = null;
809
- const overview = renderOverview(doc, payload, currentContext, overviewEdges, overviewEntities, selectAndExplore, (node) => { contextBlock = node; });
854
+ contextAnchor = null;
855
+ const overview = renderOverview(doc, payload, currentContext, overviewEdges, overviewEntities, selectAndExplore, (node, anchor) => {
856
+ contextBlock = node;
857
+ contextAnchor = anchor;
858
+ });
810
859
  overview.id = 'bb-pkw-panel-overview';
811
860
  overview.hidden = activeTab !== 'overview';
812
861
  overviewPanel = overview;