@jkwd/inbase 0.1.19 → 0.1.21

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.
@@ -2,6 +2,7 @@ import { useEffect, useRef, useState, type ReactNode } from 'react'
2
2
  import { persistAddContextFiles, persistInitialInstruction, persistRemoveContextFile } from '../agentIntent'
3
3
  import { NameInput } from './NameInput'
4
4
  import { SelectionThumbnail } from '../scene/SelectionThumbnail'
5
+ import { CanvasErrorBoundary } from './CanvasErrorBoundary'
5
6
  import {
6
7
  canStopSession,
7
8
  isReviewingIntent,
@@ -10,6 +11,8 @@ import {
10
11
  type AimedRelation,
11
12
  type BlueprintNote,
12
13
  type BlueprintNoteKind,
14
+ type BlueprintPointer,
15
+ type BlueprintPointerKind,
13
16
  type BranchChanges,
14
17
  type CodebaseGraph,
15
18
  type PatchImportAddition,
@@ -18,7 +21,8 @@ import {
18
21
  type WorldLayout,
19
22
  type WorkflowAction,
20
23
  } from '../types'
21
- import { findBlueprintNote } from '../userCreated'
24
+ import { findBlueprintNote, findBlueprintPointer } from '../userCreated'
25
+ import { EyeIcon } from './EyeIcon'
22
26
  import { beginKeyboardIsolation, shouldIgnoreShortcut } from '../keyboard'
23
27
 
24
28
  function reviewTitle(status: AgentIntentStatus) {
@@ -311,25 +315,43 @@ function BlueprintSymbolRow({
311
315
  className,
312
316
  hasNote,
313
317
  noteOpen,
318
+ pointed,
314
319
  canEdit,
315
320
  canRemove,
316
321
  onRemove,
317
322
  onOpenNote,
323
+ onTogglePoint,
318
324
  }: {
319
325
  name: string
320
326
  className?: string
321
327
  hasNote: boolean
322
328
  noteOpen?: boolean
329
+ pointed?: boolean
323
330
  canEdit: boolean
324
331
  canRemove?: boolean
325
332
  onRemove?: () => void
326
333
  onOpenNote: () => void
334
+ onTogglePoint?: () => void
327
335
  }) {
328
336
  return (
329
337
  <li>
330
338
  <span className={className}>{name}</span>
331
339
  {canEdit && (
332
340
  <div className="hud-item-actions">
341
+ {onTogglePoint && (
342
+ <button
343
+ className="hud-item-point"
344
+ type="button"
345
+ data-pointed={pointed ? 'true' : 'false'}
346
+ aria-label={
347
+ pointed ? `Stop pointing to ${name}` : `Point to ${name}`
348
+ }
349
+ aria-pressed={Boolean(pointed)}
350
+ onClick={onTogglePoint}
351
+ >
352
+ <EyeIcon size={13} />
353
+ </button>
354
+ )}
333
355
  <button
334
356
  className="hud-item-note"
335
357
  type="button"
@@ -570,7 +592,8 @@ function blueprintIsDefined(intent: AgentIntent) {
570
592
  (intent.blueprintFunctions?.length ?? 0) > 0 ||
571
593
  (intent.blueprintVariables?.length ?? 0) > 0 ||
572
594
  (intent.blueprintImports?.length ?? 0) > 0 ||
573
- (intent.blueprintNotes?.length ?? 0) > 0
595
+ (intent.blueprintNotes?.length ?? 0) > 0 ||
596
+ (intent.blueprintPointers?.length ?? 0) > 0
574
597
  )
575
598
  }
576
599
 
@@ -680,6 +703,11 @@ function sessionLiveStatus(intent: AgentIntent) {
680
703
  if (intent.awaitingAttach) {
681
704
  return { text: 'Waiting for /inbase in Cursor', busy: false }
682
705
  }
706
+ if (intent.status === 'pending') {
707
+ return intent.listening
708
+ ? { text: 'LLM is listening — accept or send an instruction', busy: false }
709
+ : { text: 'Review this step', busy: false }
710
+ }
683
711
  if (kind === 'execute') {
684
712
  return { text: `LLM received ${detail}`, busy: true }
685
713
  }
@@ -708,11 +736,6 @@ function sessionLiveStatus(intent: AgentIntent) {
708
736
  ? { text: 'LLM is listening — run the next step', busy: false }
709
737
  : { text: 'Plan ready', busy: false }
710
738
  }
711
- if (intent.status === 'pending') {
712
- return intent.listening
713
- ? { text: 'LLM is listening — accept or send an instruction', busy: false }
714
- : { text: 'Review this step', busy: false }
715
- }
716
739
  if (
717
740
  kind === 'attached' ||
718
741
  intent.status === 'blueprint' ||
@@ -797,13 +820,16 @@ function SessionPanel({
797
820
  const [contextBusy, setContextBusy] = useState(false)
798
821
  const [contextError, setContextError] = useState<string | null>(null)
799
822
  const sessionId = intent.sessionId
800
- const pending = intent.status === 'pending' && intent.isActiveDiff
823
+ const latestEntry = intent.isActiveDiff ? intent.chain.at(-1) : null
824
+ const pending =
825
+ latestEntry?.status === 'pending' ||
826
+ (intent.status === 'pending' && intent.isActiveDiff)
801
827
  const askingBlueprint = intent.status === 'blueprint_ask'
802
828
  const sendingBlueprint = intent.status === 'blueprint'
803
829
  const canPlace = Boolean(intent.creationMode)
804
830
  const preparing = intent.status === 'preparing'
805
- const planReady = intent.status === 'planned'
806
831
  const working = intent.status === 'working' || intent.status === 'replanning'
832
+ const planReady = intent.status === 'planned' && !pending
807
833
  const previewing = intent.preview
808
834
  const chainIndex = intent.chainIndex ?? 0
809
835
  const previousDiff = intent.chain[chainIndex - 1]
@@ -812,10 +838,6 @@ function SessionPanel({
812
838
  intent.step && intent.steps?.length > 0
813
839
  ? `Step ${intent.step} of ${intent.steps.length}`
814
840
  : 'Patch'
815
- const lastStep =
816
- typeof intent.step === 'number' &&
817
- intent.steps.length > 0 &&
818
- intent.step >= intent.steps.length
819
841
  const stepByStep = intent.stepByStep !== false
820
842
  const addedFunctions = intent.addedFunctions ?? []
821
843
  const addedVariables = intent.addedVariables ?? []
@@ -826,25 +848,27 @@ function SessionPanel({
826
848
  intent.status === 'finished'
827
849
  ? intent.steps.map((step) => step.index)
828
850
  : intent.chain
829
- .filter(
830
- (entry) =>
831
- entry.status === 'applied' || entry.status === 'extended',
832
- )
851
+ .filter((entry) => entry.status === 'applied')
833
852
  .map((entry) => entry.step),
834
853
  )
835
854
  if (intent.status === 'approved' && typeof intent.step === 'number') {
836
855
  acceptedSteps.add(intent.step)
837
856
  }
838
- const proposalStep =
839
- pending && typeof intent.step === 'number' ? intent.step : null
857
+ const proposalStep = pending
858
+ ? (latestEntry?.status === 'pending' ? latestEntry.step : intent.step)
859
+ : null
840
860
  const processingStep =
841
861
  working && typeof intent.step === 'number' ? intent.step : null
842
862
  const invokeStep =
843
- planReady && !working
863
+ planReady && !working && proposalStep === null
844
864
  ? (intent.steps.find((step) => !acceptedSteps.has(step.index)) ?? null)
845
865
  : null
846
866
  const canRunNext = stepByStep && Boolean(invokeStep)
847
867
  const canAcceptProposal = proposalStep !== null
868
+ const lastStep =
869
+ typeof proposalStep === 'number' &&
870
+ intent.steps.length > 0 &&
871
+ proposalStep >= intent.steps.length
848
872
  const panelDone =
849
873
  intent.status === 'finished' ||
850
874
  intent.status === 'approved' ||
@@ -1071,7 +1095,7 @@ function SessionPanel({
1071
1095
  {intent.steps.map((step) => {
1072
1096
  const proposed = proposalStep === step.index
1073
1097
  const processing = processingStep === step.index
1074
- const accepted = acceptedSteps.has(step.index)
1098
+ const accepted = acceptedSteps.has(step.index) && !proposed
1075
1099
  const creating = processing && !proposed
1076
1100
  const showStepAction =
1077
1101
  creating ||
@@ -1270,7 +1294,7 @@ function SessionPanel({
1270
1294
  value={instruction}
1271
1295
  maxLength={4000}
1272
1296
  rows={3}
1273
- placeholder="Describe what should change in the next diff…"
1297
+ placeholder="Describe what should change in this proposal…"
1274
1298
  onChange={(event) => setInstruction(event.target.value)}
1275
1299
  onKeyDown={(event) => event.stopPropagation()}
1276
1300
  />
@@ -1546,6 +1570,11 @@ function explorerInstructions({
1546
1570
  ? [
1547
1571
  { id: 'space', keys: ['Space'], label: 'Place file' },
1548
1572
  { id: 'b-island', keys: ['B'], label: 'Place island' },
1573
+ {
1574
+ id: 'point-to',
1575
+ keys: ['Point to'],
1576
+ label: 'Keep a file, folder, or function in mind',
1577
+ },
1549
1578
  ]
1550
1579
  : []),
1551
1580
  ...backspace,
@@ -1629,7 +1658,7 @@ function explorerInstructions({
1629
1658
  {
1630
1659
  id: 'add-file-folder',
1631
1660
  keys: ['Right-click'],
1632
- label: 'Add file or folder',
1661
+ label: 'Add file or folder, or point to a folder',
1633
1662
  },
1634
1663
  ]
1635
1664
  : []),
@@ -1739,6 +1768,7 @@ type HUDProps = {
1739
1768
  blueprintVariables?: PatchSymbolAddition[]
1740
1769
  blueprintImports?: PatchImportAddition[]
1741
1770
  blueprintNotes?: BlueprintNote[]
1771
+ blueprintPointers?: BlueprintPointer[]
1742
1772
  onAddBlueprintFunction?: (fileId: string, name: string) => boolean
1743
1773
  onAddBlueprintVariable?: (fileId: string, name: string) => boolean
1744
1774
  onAddBlueprintImport?: (fileId: string, raw: string) => boolean
@@ -1755,6 +1785,11 @@ type HUDProps = {
1755
1785
  name?: string
1756
1786
  note: string
1757
1787
  }) => void
1788
+ onToggleBlueprintPointer?: (next: {
1789
+ kind: BlueprintPointerKind
1790
+ path: string
1791
+ name?: string
1792
+ }) => void
1758
1793
  onMapAddFile?: (folderPath: string) => void
1759
1794
  onMapAddFolder?: (folderPath: string) => void
1760
1795
  onInspectFile?: (fileId: string) => void
@@ -1814,6 +1849,7 @@ export function HUD({
1814
1849
  blueprintVariables = [],
1815
1850
  blueprintImports = [],
1816
1851
  blueprintNotes = [],
1852
+ blueprintPointers = [],
1817
1853
  onAddBlueprintFunction,
1818
1854
  onAddBlueprintVariable,
1819
1855
  onAddBlueprintImport,
@@ -1821,6 +1857,7 @@ export function HUD({
1821
1857
  onRemoveBlueprintVariable,
1822
1858
  onRemoveBlueprintImport,
1823
1859
  onSetBlueprintNote,
1860
+ onToggleBlueprintPointer,
1824
1861
  onMapAddFile,
1825
1862
  onMapAddFolder,
1826
1863
  onInspectFile,
@@ -1950,6 +1987,16 @@ export function HUD({
1950
1987
  const selectedFileNote = selected
1951
1988
  ? findBlueprintNote(blueprintNotes, selected.id, 'file')
1952
1989
  : ''
1990
+ const selectedFilePointed = selected
1991
+ ? findBlueprintPointer(blueprintPointers, 'file', selected.id)
1992
+ : false
1993
+ const selectedFolderPointed = selectedFolderNode
1994
+ ? findBlueprintPointer(
1995
+ blueprintPointers,
1996
+ 'folder',
1997
+ selectedFolderNode.path,
1998
+ )
1999
+ : false
1953
2000
  const openFileNote = () => {
1954
2001
  if (!selected || !onSetBlueprintNote) return
1955
2002
  setInstructionsOpen(false)
@@ -2282,6 +2329,20 @@ export function HUD({
2282
2329
  Inspect file
2283
2330
  </button>
2284
2331
  )}
2332
+ {canEditBlueprint && onToggleBlueprintPointer && (
2333
+ <button
2334
+ className="hud-button hud-inspect hud-point"
2335
+ type="button"
2336
+ data-pointed={selectedFilePointed ? 'true' : 'false'}
2337
+ aria-pressed={selectedFilePointed}
2338
+ onClick={() =>
2339
+ onToggleBlueprintPointer({ kind: 'file', path: selected.id })
2340
+ }
2341
+ >
2342
+ <EyeIcon size={15} />
2343
+ {selectedFilePointed ? 'Stop pointing' : 'Point to file'}
2344
+ </button>
2345
+ )}
2285
2346
  {canEditBlueprint && onSetBlueprintNote && (
2286
2347
  <button
2287
2348
  className="hud-button hud-inspect"
@@ -2373,10 +2434,26 @@ export function HUD({
2373
2434
  }
2374
2435
  canEdit={Boolean(canEditBlueprint && onSetBlueprintNote)}
2375
2436
  canRemove={Boolean(canEditBlueprint && symbol.intended)}
2437
+ pointed={findBlueprintPointer(
2438
+ blueprintPointers,
2439
+ 'function',
2440
+ selected.id,
2441
+ symbol.name,
2442
+ )}
2376
2443
  onRemove={() =>
2377
2444
  onRemoveBlueprintFunction?.(selected.id, symbol.name)
2378
2445
  }
2379
2446
  onOpenNote={() => openSymbolNote('function', symbol.name)}
2447
+ onTogglePoint={
2448
+ onToggleBlueprintPointer
2449
+ ? () =>
2450
+ onToggleBlueprintPointer({
2451
+ kind: 'function',
2452
+ path: selected.id,
2453
+ name: symbol.name,
2454
+ })
2455
+ : undefined
2456
+ }
2380
2457
  />
2381
2458
  ))}
2382
2459
  {extraAddedFunctions.map((item) => (
@@ -2398,7 +2475,23 @@ export function HUD({
2398
2475
  noteEditor.name === item.name
2399
2476
  }
2400
2477
  canEdit={Boolean(canEditBlueprint && onSetBlueprintNote)}
2478
+ pointed={findBlueprintPointer(
2479
+ blueprintPointers,
2480
+ 'function',
2481
+ selected.id,
2482
+ item.name,
2483
+ )}
2401
2484
  onOpenNote={() => openSymbolNote('function', item.name)}
2485
+ onTogglePoint={
2486
+ onToggleBlueprintPointer
2487
+ ? () =>
2488
+ onToggleBlueprintPointer({
2489
+ kind: 'function',
2490
+ path: selected.id,
2491
+ name: item.name,
2492
+ })
2493
+ : undefined
2494
+ }
2402
2495
  />
2403
2496
  ))}
2404
2497
  </ul>
@@ -2439,10 +2532,26 @@ export function HUD({
2439
2532
  }
2440
2533
  canEdit={Boolean(canEditBlueprint && onSetBlueprintNote)}
2441
2534
  canRemove={Boolean(canEditBlueprint && symbol.intended)}
2535
+ pointed={findBlueprintPointer(
2536
+ blueprintPointers,
2537
+ 'variable',
2538
+ selected.id,
2539
+ symbol.name,
2540
+ )}
2442
2541
  onRemove={() =>
2443
2542
  onRemoveBlueprintVariable?.(selected.id, symbol.name)
2444
2543
  }
2445
2544
  onOpenNote={() => openSymbolNote('variable', symbol.name)}
2545
+ onTogglePoint={
2546
+ onToggleBlueprintPointer
2547
+ ? () =>
2548
+ onToggleBlueprintPointer({
2549
+ kind: 'variable',
2550
+ path: selected.id,
2551
+ name: symbol.name,
2552
+ })
2553
+ : undefined
2554
+ }
2446
2555
  />
2447
2556
  ))}
2448
2557
  {extraAddedVariables.map((item) => (
@@ -2464,7 +2573,23 @@ export function HUD({
2464
2573
  noteEditor.name === item.name
2465
2574
  }
2466
2575
  canEdit={Boolean(canEditBlueprint && onSetBlueprintNote)}
2576
+ pointed={findBlueprintPointer(
2577
+ blueprintPointers,
2578
+ 'variable',
2579
+ selected.id,
2580
+ item.name,
2581
+ )}
2467
2582
  onOpenNote={() => openSymbolNote('variable', item.name)}
2583
+ onTogglePoint={
2584
+ onToggleBlueprintPointer
2585
+ ? () =>
2586
+ onToggleBlueprintPointer({
2587
+ kind: 'variable',
2588
+ path: selected.id,
2589
+ name: item.name,
2590
+ })
2591
+ : undefined
2592
+ }
2468
2593
  />
2469
2594
  ))}
2470
2595
  </ul>
@@ -2608,6 +2733,24 @@ export function HUD({
2608
2733
  ))}
2609
2734
  </ul>
2610
2735
  )}
2736
+ {canPlace && onToggleBlueprintPointer && (
2737
+ <button
2738
+ className="hud-button hud-inspect hud-point"
2739
+ type="button"
2740
+ data-pointed={selectedFolderPointed ? 'true' : 'false'}
2741
+ aria-pressed={selectedFolderPointed}
2742
+ disabled={naming || selectedFolderNode.path.startsWith('draft:')}
2743
+ onClick={() =>
2744
+ onToggleBlueprintPointer({
2745
+ kind: 'folder',
2746
+ path: selectedFolderNode.path,
2747
+ })
2748
+ }
2749
+ >
2750
+ <EyeIcon size={15} />
2751
+ {selectedFolderPointed ? 'Stop pointing' : 'Point to folder'}
2752
+ </button>
2753
+ )}
2611
2754
  {canPlace && mapping && onMapAddFile && onMapAddFolder && (
2612
2755
  <div className="hud-decide hud-map-blueprint">
2613
2756
  <button
@@ -2634,7 +2777,8 @@ export function HUD({
2634
2777
  )}
2635
2778
 
2636
2779
  {mapping && thumbnailVisible && (
2637
- <SelectionThumbnail
2780
+ <CanvasErrorBoundary fallback={null}>
2781
+ <SelectionThumbnail
2638
2782
  graph={graph}
2639
2783
  layout={layout}
2640
2784
  selectedId={selectedId}
@@ -2646,6 +2790,20 @@ export function HUD({
2646
2790
  plannedIds={plannedIds}
2647
2791
  createdIds={createdIds}
2648
2792
  deletedIds={deletedIds}
2793
+ pointedFileIds={
2794
+ blueprintHidden
2795
+ ? []
2796
+ : blueprintPointers.flatMap((item) =>
2797
+ item.kind === 'folder' ? [] : [item.path],
2798
+ )
2799
+ }
2800
+ pointedFolderPaths={
2801
+ blueprintHidden
2802
+ ? []
2803
+ : blueprintPointers.flatMap((item) =>
2804
+ item.kind === 'folder' ? [item.path] : [],
2805
+ )
2806
+ }
2649
2807
  onMinimize={() => {
2650
2808
  setThumbnailMaximized(false)
2651
2809
  setThumbnailMinimized((current) => !current)
@@ -2660,6 +2818,7 @@ export function HUD({
2660
2818
  setThumbnailMaximized(false)
2661
2819
  }}
2662
2820
  />
2821
+ </CanvasErrorBoundary>
2663
2822
  )}
2664
2823
  </div>
2665
2824
 
@@ -10,15 +10,19 @@ export type MapContextMenuState = {
10
10
 
11
11
  type MapContextMenuProps = {
12
12
  menu: MapContextMenuState | null
13
+ pointed?: boolean
13
14
  onAddFile: (folder: string) => void
14
15
  onAddFolder: (folder: string) => void
16
+ onPointToFolder?: (folder: string) => void
15
17
  onClose: () => void
16
18
  }
17
19
 
18
20
  export function MapContextMenu({
19
21
  menu,
22
+ pointed = false,
20
23
  onAddFile,
21
24
  onAddFolder,
25
+ onPointToFolder,
22
26
  onClose,
23
27
  }: MapContextMenuProps) {
24
28
  useEffect(() => {
@@ -48,7 +52,7 @@ export function MapContextMenu({
48
52
 
49
53
  const pad = 8
50
54
  const width = 176
51
- const height = 84
55
+ const height = onPointToFolder ? 126 : 84
52
56
  const left = Math.min(
53
57
  Math.max(pad, menu.x),
54
58
  window.innerWidth - width - pad,
@@ -85,6 +89,19 @@ export function MapContextMenu({
85
89
  >
86
90
  Add folder
87
91
  </button>
92
+ {onPointToFolder && (
93
+ <button
94
+ type="button"
95
+ role="menuitem"
96
+ aria-pressed={pointed}
97
+ onClick={() => {
98
+ onPointToFolder(menu.folder)
99
+ onClose()
100
+ }}
101
+ >
102
+ {pointed ? 'Stop pointing' : 'Point to folder'}
103
+ </button>
104
+ )}
88
105
  </div>,
89
106
  document.body,
90
107
  )
@@ -3,6 +3,8 @@ import { folderOfFile, folderParent } from './layout'
3
3
  import type {
4
4
  BlueprintNote,
5
5
  BlueprintNoteKind,
6
+ BlueprintPointer,
7
+ BlueprintPointerKind,
6
8
  CodebaseGraph,
7
9
  FileNode,
8
10
  PatchImportAddition,
@@ -437,6 +439,101 @@ export function dropBlueprintSymbolNote(
437
439
  )
438
440
  }
439
441
 
442
+ export function blueprintPointerKey(
443
+ pointer: Pick<BlueprintPointer, 'kind' | 'path' | 'name'>,
444
+ ) {
445
+ return pointer.kind === 'file' || pointer.kind === 'folder'
446
+ ? `${pointer.kind}:${pointer.path}`
447
+ : `${pointer.kind}:${pointer.path}:${pointer.name ?? ''}`
448
+ }
449
+
450
+ function parseBlueprintPointer(value: unknown): BlueprintPointer | null {
451
+ if (!value || typeof value !== 'object') return null
452
+ const item = value as Partial<BlueprintPointer>
453
+ const path = typeof item.path === 'string' ? item.path.trim() : ''
454
+ if (!path) return null
455
+ if (item.kind === 'file' || item.kind === 'folder') {
456
+ return { kind: item.kind, path }
457
+ }
458
+ if (item.kind !== 'function' && item.kind !== 'variable') return null
459
+ const name = typeof item.name === 'string' ? item.name.trim() : ''
460
+ if (!name) return null
461
+ return { kind: item.kind, path, name }
462
+ }
463
+
464
+ export function parseBlueprintPointers(value: unknown): BlueprintPointer[] {
465
+ if (!Array.isArray(value)) return []
466
+ const seen = new Set<string>()
467
+ const pointers: BlueprintPointer[] = []
468
+ for (const item of value) {
469
+ const parsed = parseBlueprintPointer(item)
470
+ if (!parsed) continue
471
+ const key = blueprintPointerKey(parsed)
472
+ if (seen.has(key)) continue
473
+ seen.add(key)
474
+ pointers.push(parsed)
475
+ }
476
+ return pointers
477
+ }
478
+
479
+ export function findBlueprintPointer(
480
+ pointers: BlueprintPointer[],
481
+ kind: BlueprintPointerKind,
482
+ path: string,
483
+ name?: string,
484
+ ) {
485
+ return pointers.some((item) =>
486
+ kind === 'file' || kind === 'folder'
487
+ ? item.kind === kind && item.path === path
488
+ : item.kind === kind && item.path === path && item.name === name,
489
+ )
490
+ }
491
+
492
+ export function toggleBlueprintPointer(
493
+ pointers: BlueprintPointer[],
494
+ next: {
495
+ kind: BlueprintPointerKind
496
+ path: string
497
+ name?: string
498
+ },
499
+ ): BlueprintPointer[] {
500
+ const path = next.path.trim()
501
+ if (!path || path.startsWith('draft:')) return pointers
502
+ const key = blueprintPointerKey({ ...next, path })
503
+ const without = pointers.filter((item) => blueprintPointerKey(item) !== key)
504
+ if (without.length !== pointers.length) return without
505
+ if (next.kind === 'file' || next.kind === 'folder') {
506
+ return [...without, { kind: next.kind, path }]
507
+ }
508
+ const name = (next.name ?? '').trim()
509
+ if (!name) return without
510
+ return [...without, { kind: next.kind, path, name }]
511
+ }
512
+
513
+ export function dropBlueprintFilePointers(
514
+ pointers: BlueprintPointer[],
515
+ fileIds: Iterable<string>,
516
+ folderPaths: Iterable<string> = [],
517
+ ) {
518
+ const files = new Set(fileIds)
519
+ const folders = new Set(folderPaths)
520
+ return pointers.filter((item) =>
521
+ item.kind === 'folder' ? !folders.has(item.path) : !files.has(item.path),
522
+ )
523
+ }
524
+
525
+ export function dropBlueprintSymbolPointer(
526
+ pointers: BlueprintPointer[],
527
+ path: string,
528
+ kind: 'function' | 'variable',
529
+ name: string,
530
+ ) {
531
+ return pointers.filter(
532
+ (item) =>
533
+ !(item.kind === kind && item.path === path && item.name === name),
534
+ )
535
+ }
536
+
440
537
  export function parseBlueprintImport(
441
538
  raw: string,
442
539
  file: string,
@@ -114,6 +114,7 @@ function blueprintIntentFields() {
114
114
  blueprintVariables: blueprint.addedVariables,
115
115
  blueprintImports: blueprint.addedImports,
116
116
  blueprintNotes: blueprint.notes,
117
+ blueprintPointers: blueprint.pointers,
117
118
  }
118
119
  }
119
120
 
@@ -267,6 +268,7 @@ async function decideIntent(req: IncomingMessage, res: ServerResponse) {
267
268
  addedVariables?: unknown[]
268
269
  addedImports?: unknown[]
269
270
  notes?: unknown[]
271
+ pointers?: unknown[]
270
272
  files?: unknown[]
271
273
  fileId?: string
272
274
  }
@@ -360,6 +362,7 @@ async function decideIntent(req: IncomingMessage, res: ServerResponse) {
360
362
  addedVariables: body.addedVariables,
361
363
  addedImports: body.addedImports,
362
364
  notes: body.notes,
365
+ pointers: body.pointers,
363
366
  })
364
367
  } else if (action === 'blueprint_clear') {
365
368
  clearBlueprint(dataDir)
@@ -375,6 +378,7 @@ async function decideIntent(req: IncomingMessage, res: ServerResponse) {
375
378
  addedVariables: body.addedVariables,
376
379
  addedImports: body.addedImports,
377
380
  notes: body.notes,
381
+ pointers: body.pointers,
378
382
  })
379
383
  } else if (action === 'setup_session') {
380
384
  const manifest = setupSession(dataDir, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jkwd/inbase",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "A first-person 3D map of a codebase, with a visual coding workflow for Cursor.",
5
5
  "license": "MIT",
6
6
  "author": "Joris Kuijper",