@jkwd/inbase 0.1.19 → 0.1.20

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.
@@ -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.20",
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",
@@ -43,8 +43,8 @@ over the recorded diffs.
43
43
  live project files with Write, StrReplace, and Delete for that step only. Then
44
44
  run `inbase propose-patch` with no patch file. Inbase diffs the working tree
45
45
  against the snapshot taken at invoke and stores that patch. A later instruction
46
- replaces the withdrawn step; edit from the accepted live files, not from the
47
- withdrawn proposal. Do not write a unified diff yourself.
46
+ updates that live proposal; edit those files, then propose-patch again. Wait
47
+ for **Accept proposal** before the next step. Do not write a unified diff yourself.
48
48
 
49
49
  The visualizer stores immutable diffs under
50
50
  `.inbase/diff-sessions/<session-id>/diffs/`. Inbase must already be running
@@ -180,11 +180,15 @@ npx inbase wait-for-approval --session "<session-id>"
180
180
  12. Read the wait command output. Reply in chat with the `VISUAL_CODER_ACK`
181
181
  line first, then:
182
182
 
183
- - Exit `0` (`VISUAL_CODER_ACK execute` / `VISUAL_CODER_EXECUTE`): the
184
- highlighted step was invoked. If this is a later step, implement it now —
185
- do not explore, re-plan, or run `wait-for-blueprint`. Re-read the shared
186
- `blueprint.json` if you need placed files. Edit live files for that step
187
- only, record with `inbase propose-patch` (no patch file), then wait again.
183
+ - Exit `0` (`VISUAL_CODER_ACK execute` / `VISUAL_CODER_EXECUTE`): follow
184
+ the printed `VISUAL_CODER_EXECUTE` line. If it says **Update the current
185
+ proposal**, edit those live files, record with `inbase propose-patch` (no
186
+ patch file), then wait again for **Accept proposal**. Do not start the
187
+ next step and do not report a new plan. Otherwise the highlighted step
188
+ was invoked: implement that step now — do not explore, re-plan, or run
189
+ `wait-for-blueprint`. Re-read the shared `blueprint.json` if you need
190
+ placed files. Edit live files for that step only, record with
191
+ `inbase propose-patch` (no patch file), then wait again.
188
192
  - Exit `6` (`VISUAL_CODER_ACK blueprint` / `VISUAL_CODER_BLUEPRINT`): the
189
193
  shared blueprint changed. Follow the latest files, islands, functions,
190
194
  variables, and imports. Do not omit, rename, relocate, or replace them.
@@ -195,19 +199,12 @@ npx inbase wait-for-approval --session "<session-id>"
195
199
  stored session diffs. The shared blueprint remains. Optionally run
196
200
  `--clear` if anything remains, tell the user the feature is done, and
197
201
  **stop**. Do not propose another patch.
198
- - Exit `4` (`VISUAL_CODER_ACK replan` / `VISUAL_CODER_REPLAN`): do **not**
199
- edit project files and do not rewrite an earlier accepted patch. The
200
- withdrawn proposal is no longer live; disk is baseline + accepted patch
201
- files. Follow the text between `VISUAL_CODER_INSTRUCTION_START` and
202
- `VISUAL_CODER_INSTRUCTION_END`, read the shared `blueprint.json` when
203
- it is enabled (files, islands, `addedFunctions`, `addedVariables`,
204
- `addedImports`). The blueprint stays leading. If the new instruction would
205
- differ from it, ask the user before replacing the plan. Read
206
- `user-context.json` (follow the viewpoint only if `followLook` is true),
207
- replace the plan from the current step onward using `inbase report-plan`,
208
- then wait for the next `VISUAL_CODER_EXECUTE` before editing again. The
209
- replacement edits must sit on the accepted live files, not the withdrawn
210
- proposal.
202
+ - Exit `4` (`VISUAL_CODER_ACK replan` / `VISUAL_CODER_REPLAN`): live files
203
+ still contain the current proposal. Do not reset them and do not report a
204
+ new plan. Follow the text between `VISUAL_CODER_INSTRUCTION_START` and
205
+ `VISUAL_CODER_INSTRUCTION_END`, edit those live files, then
206
+ `inbase propose-patch`. Wait for **Accept proposal**. Do not start the
207
+ next step.
211
208
  - Exit `2` (`VISUAL_CODER_ACK stopped` / `VISUAL_CODER_STOPPED`) or `3`
212
209
  (`VISUAL_CODER_ACK timeout`): make no further project changes.
213
210