@jkwd/inbase 0.1.8 → 0.1.10

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.
@@ -29,6 +29,16 @@ function reviewTitle(status: AgentIntentStatus) {
29
29
  return 'Visual workflow'
30
30
  }
31
31
 
32
+ function sessionTabLabel(intent: AgentIntent, sessions: AgentIntent[]) {
33
+ const title = intent.feature?.trim() || reviewTitle(intent.status)
34
+ const same = sessions.filter(
35
+ (item) => (item.feature?.trim() || reviewTitle(item.status)) === title,
36
+ )
37
+ if (same.length < 2) return title
38
+ const id = intent.sessionId ?? ''
39
+ return `${title} · ${id.slice(-4)}`
40
+ }
41
+
32
42
  function fileBase(id: string) {
33
43
  return id.split('/').pop() ?? id
34
44
  }
@@ -264,7 +274,8 @@ function SessionPanel({
264
274
  const sessionId = intent.sessionId
265
275
  const pending = intent.status === 'pending' && intent.isActiveDiff
266
276
  const askingBlueprint = intent.status === 'blueprint_ask'
267
- const creatingBlueprint = intent.creationMode || intent.status === 'blueprint'
277
+ const sendingBlueprint = intent.status === 'blueprint'
278
+ const canPlace = Boolean(intent.creationMode)
268
279
  const preparing = intent.status === 'preparing'
269
280
  const planReady = intent.status === 'planned'
270
281
  const working = intent.status === 'working' || intent.status === 'replanning'
@@ -286,7 +297,7 @@ function SessionPanel({
286
297
  const addedImports = intent.addedImports ?? []
287
298
  const changedFunctions = intent.changedFunctions ?? []
288
299
  const changedVariables = intent.changedVariables ?? []
289
- const doneSteps = new Set(
300
+ const acceptedSteps = new Set(
290
301
  intent.status === 'finished'
291
302
  ? intent.steps.map((step) => step.index)
292
303
  : intent.chain
@@ -297,22 +308,23 @@ function SessionPanel({
297
308
  .map((entry) => entry.step),
298
309
  )
299
310
  if (intent.status === 'approved' && typeof intent.step === 'number') {
300
- doneSteps.add(intent.step)
301
- }
302
- if (pending && typeof intent.step === 'number' && !lastStep) {
303
- doneSteps.add(intent.step)
311
+ acceptedSteps.add(intent.step)
304
312
  }
305
- const nextStep =
306
- intent.status === 'finished'
307
- ? null
308
- : (intent.steps.find((step) => !doneSteps.has(step.index)) ?? null)
309
- const canRunNext =
310
- Boolean(nextStep) && (planReady || pending) && !working
311
- const canComplete = pending && lastStep
313
+ const proposalStep =
314
+ pending && typeof intent.step === 'number' ? intent.step : null
315
+ const processingStep =
316
+ working && typeof intent.step === 'number' ? intent.step : null
317
+ const invokeStep =
318
+ planReady && !working
319
+ ? (intent.steps.find((step) => !acceptedSteps.has(step.index)) ?? null)
320
+ : null
321
+ const canRunNext = stepByStep && Boolean(invokeStep)
322
+ const canAcceptProposal = proposalStep !== null
312
323
  const panelDone =
313
324
  intent.status === 'finished' ||
314
325
  intent.status === 'approved' ||
315
- doneSteps.size > 0
326
+ acceptedSteps.size > 0 ||
327
+ canAcceptProposal
316
328
 
317
329
  useEffect(() => {
318
330
  setInstruction('')
@@ -358,22 +370,26 @@ function SessionPanel({
358
370
  {!stepByStep && (
359
371
  <p className="hud-mode-hint">
360
372
  LLM implements the full plan. You can still walk the diffs, then
361
- Complete.
373
+ Accept proposal.
374
+ </p>
375
+ )}
376
+ {intent.llmIdle && !working && !preparing && (
377
+ <p className="hud-mode-hint">
378
+ LLM is idle. Current changes stay on the map.
362
379
  </p>
363
380
  )}
364
381
  {intent.feature && <p className="hud-feature">{intent.feature}</p>}
365
382
  {askingBlueprint ? (
366
383
  <>
367
384
  <p>
368
- {intent.canEnterBlueprint
369
- ? 'Place files and folders for this chat, then send them as a blueprint for the LLM?'
370
- : `Blueprint edit mode is active in another chat (${intent.blueprintSessionId}). Finish or stop it before starting here.`}
385
+ Place files and folders for this chat, then send them as a
386
+ blueprint for the LLM? You can still add files and islands on
387
+ later steps.
371
388
  </p>
372
389
  <div className="hud-decide">
373
390
  <button
374
391
  className="hud-button hud-button-approve"
375
392
  type="button"
376
- disabled={!intent.canEnterBlueprint}
377
393
  onClick={() => act('blueprint_yes')}
378
394
  >
379
395
  Create blueprint
@@ -394,12 +410,12 @@ function SessionPanel({
394
410
  </button>
395
411
  </div>
396
412
  </>
397
- ) : creatingBlueprint ? (
413
+ ) : sendingBlueprint ? (
398
414
  <>
399
415
  <p>
400
416
  Walk the map, press <kbd>Space</kbd> for a file and{' '}
401
417
  <kbd>B</kbd> for an island. Send the blueprint when the layout
402
- is ready.
418
+ is ready. You can keep placing files after this handshake.
403
419
  </p>
404
420
  <div className="hud-decide">
405
421
  <button
@@ -457,50 +473,53 @@ function SessionPanel({
457
473
  {intent.reason ? ` · ${intent.reason}` : ''}
458
474
  </p>
459
475
  )}
460
- {!askingBlueprint && !creatingBlueprint && (
476
+ {!askingBlueprint && !sendingBlueprint && (
461
477
  <>
478
+ {canPlace && (
479
+ <p>
480
+ <kbd>Space</kbd> places a file, <kbd>B</kbd> an island for
481
+ this session.
482
+ </p>
483
+ )}
462
484
  {intent.steps?.length > 0 && (
463
485
  <ol className="hud-steps">
464
- {intent.steps.map((step) => (
465
- <li
466
- key={step.index}
467
- data-current={
468
- intent.status !== 'finished' &&
469
- step.index === intent.step &&
470
- !doneSteps.has(step.index)
471
- }
472
- data-done={doneSteps.has(step.index)}
473
- data-next={
474
- nextStep?.index === step.index &&
475
- !doneSteps.has(step.index)
476
- }
477
- >
478
- <span className="hud-step-index">{step.index}.</span>
479
- <span className="hud-step-main">
480
- <span className="hud-step-title">{step.title}</span>
481
- {((stepByStep &&
482
- canRunNext &&
483
- nextStep?.index === step.index) ||
484
- (canComplete && step.index === intent.step)) && (
485
- <button
486
- className="hud-button hud-button-approve hud-run-step"
487
- type="button"
488
- onClick={() =>
489
- canComplete && step.index === intent.step
490
- ? act('continue')
491
- : act('invoke', {
492
- step: step.index,
493
- })
494
- }
495
- >
496
- {canComplete && step.index === intent.step
497
- ? 'Complete'
498
- : 'Run step'}
499
- </button>
500
- )}
501
- </span>
502
- </li>
503
- ))}
486
+ {intent.steps.map((step) => {
487
+ const proposed = proposalStep === step.index
488
+ const processing = processingStep === step.index
489
+ const accepted = acceptedSteps.has(step.index)
490
+ return (
491
+ <li
492
+ key={step.index}
493
+ data-done={accepted || proposed}
494
+ data-active={processing || proposed}
495
+ >
496
+ <span className="hud-step-index">{step.index}.</span>
497
+ <span className="hud-step-main">
498
+ <span className="hud-step-title">{step.title}</span>
499
+ {((canRunNext && invokeStep?.index === step.index) ||
500
+ (canAcceptProposal && proposed)) && (
501
+ <button
502
+ className="hud-button hud-button-approve hud-run-step"
503
+ type="button"
504
+ onClick={() =>
505
+ proposed
506
+ ? lastStep
507
+ ? act('continue')
508
+ : act('invoke', {
509
+ step: step.index + 1,
510
+ })
511
+ : act('invoke', {
512
+ step: step.index,
513
+ })
514
+ }
515
+ >
516
+ {proposed ? 'Accept proposal' : 'Run step'}
517
+ </button>
518
+ )}
519
+ </span>
520
+ </li>
521
+ )
522
+ })}
504
523
  </ol>
505
524
  )}
506
525
  {intent.chain.length > 0 && (
@@ -661,15 +680,6 @@ function SessionPanel({
661
680
  />
662
681
  </label>
663
682
  <div className="hud-decide">
664
- {lastStep && (
665
- <button
666
- className="hud-button hud-button-approve"
667
- type="button"
668
- onClick={() => act('continue')}
669
- >
670
- Complete
671
- </button>
672
- )}
673
683
  <button
674
684
  className="hud-button hud-button-extend"
675
685
  type="button"
@@ -732,7 +742,7 @@ function InstructionList({ items }: { items: ExplorerInstruction[] }) {
732
742
  }
733
743
 
734
744
  function explorerInstructions({
735
- creatingBlueprint,
745
+ canPlace,
736
746
  hasChangeSet,
737
747
  changePathsOnly,
738
748
  selectedUserCreated,
@@ -742,7 +752,7 @@ function explorerInstructions({
742
752
  canStop,
743
753
  sessionCount,
744
754
  }: {
745
- creatingBlueprint: boolean
755
+ canPlace: boolean
746
756
  hasChangeSet: boolean
747
757
  changePathsOnly: boolean
748
758
  selectedUserCreated: boolean
@@ -753,7 +763,7 @@ function explorerInstructions({
753
763
  sessionCount: number
754
764
  }): ExplorerInstructionSection[] {
755
765
  const backspace: ExplorerInstruction[] =
756
- selectedUserCreated && creatingBlueprint
766
+ selectedUserCreated && canPlace
757
767
  ? [{ id: 'backspace', keys: ['Backspace'], label: 'Delete' }]
758
768
  : []
759
769
  const info: ExplorerInstruction[] = [
@@ -796,7 +806,7 @@ function explorerInstructions({
796
806
  { id: 'wasd', keys: ['W', 'A', 'S', 'D'], label: 'Walk' },
797
807
  { id: 'mouse-look', keys: ['Mouse'], label: 'Look around' },
798
808
  { id: 'shift', keys: ['Shift'], label: 'Sprint' },
799
- ...(creatingBlueprint
809
+ ...(canPlace
800
810
  ? [
801
811
  { id: 'space', keys: ['Space'], label: 'Place file' },
802
812
  { id: 'b-island', keys: ['B'], label: 'Place island' },
@@ -856,7 +866,7 @@ function explorerInstructions({
856
866
  keys: ['Click'],
857
867
  label: 'An island for its files',
858
868
  },
859
- ...(creatingBlueprint
869
+ ...(canPlace
860
870
  ? [
861
871
  { id: 'select-island', keys: ['Click'], label: 'Select an island' },
862
872
  {
@@ -1042,9 +1052,7 @@ export function HUD({
1042
1052
  const [thumbnailMinimized, setThumbnailMinimized] = useState(false)
1043
1053
  const [thumbnailMaximized, setThumbnailMaximized] = useState(false)
1044
1054
  const infoPanelRef = useRef<HTMLDivElement>(null)
1045
- const creatingBlueprint = sessions.some(
1046
- (item) => item.creationMode || item.status === 'blueprint',
1047
- )
1055
+ const canPlace = Boolean(intent.creationMode)
1048
1056
  const previewing = intent.preview
1049
1057
  const addedFunctions = intent.addedFunctions ?? []
1050
1058
  const addedVariables = intent.addedVariables ?? []
@@ -1111,7 +1119,7 @@ export function HUD({
1111
1119
  (item) => !selected?.imports.includes(item.from),
1112
1120
  )
1113
1121
  const canEditBlueprint =
1114
- creatingBlueprint && Boolean(selected) && !selected?.id.startsWith('draft:')
1122
+ canPlace && Boolean(selected) && !selected?.id.startsWith('draft:')
1115
1123
  const canInspectFile = (fileId: string, userCreated = false) =>
1116
1124
  Boolean(onInspectFile) &&
1117
1125
  !fileId.startsWith('draft:') &&
@@ -1268,7 +1276,7 @@ export function HUD({
1268
1276
  }, [instructionsOpen])
1269
1277
 
1270
1278
  const instructionSections = explorerInstructions({
1271
- creatingBlueprint,
1279
+ canPlace,
1272
1280
  hasChangeSet,
1273
1281
  changePathsOnly,
1274
1282
  selectedUserCreated: Boolean(selected?.userCreated),
@@ -1298,7 +1306,7 @@ export function HUD({
1298
1306
  <p>
1299
1307
  <kbd>W</kbd> <kbd>A</kbd> <kbd>S</kbd> <kbd>D</kbd> walk,{' '}
1300
1308
  <kbd>Shift</kbd> sprint
1301
- {creatingBlueprint ? (
1309
+ {canPlace ? (
1302
1310
  <>
1303
1311
  , <kbd>Space</kbd> place a file, <kbd>B</kbd> place an island
1304
1312
  </>
@@ -1362,19 +1370,39 @@ export function HUD({
1362
1370
 
1363
1371
  {sessions.length > 0 && (
1364
1372
  <div className="hud-left-stack">
1365
- {sessions.map((session) => (
1366
- <SessionPanel
1367
- key={session.sessionId}
1368
- intent={session}
1369
- focused={session.sessionId === (focusedSessionId ?? intent.sessionId)}
1370
- naming={naming}
1371
- onFocus={() => {
1372
- if (session.sessionId) onFocusSession?.(session.sessionId)
1373
- }}
1374
- onWorkflowAction={onWorkflowAction}
1375
- onNavigateDiff={onNavigateDiff}
1376
- />
1377
- ))}
1373
+ {sessions.length > 1 && (
1374
+ <div className="hud-session-tabs" role="tablist" aria-label="LLM sessions">
1375
+ {sessions.map((session) => {
1376
+ const active =
1377
+ session.sessionId === (focusedSessionId ?? intent.sessionId)
1378
+ return (
1379
+ <button
1380
+ className="hud-button hud-session-tab"
1381
+ type="button"
1382
+ role="tab"
1383
+ aria-selected={active}
1384
+ data-active={active}
1385
+ key={session.sessionId}
1386
+ onClick={() => {
1387
+ if (session.sessionId) onFocusSession?.(session.sessionId)
1388
+ }}
1389
+ >
1390
+ {sessionTabLabel(session, sessions)}
1391
+ </button>
1392
+ )
1393
+ })}
1394
+ </div>
1395
+ )}
1396
+ <SessionPanel
1397
+ intent={intent}
1398
+ focused
1399
+ naming={naming}
1400
+ onFocus={() => {
1401
+ if (intent.sessionId) onFocusSession?.(intent.sessionId)
1402
+ }}
1403
+ onWorkflowAction={onWorkflowAction}
1404
+ onNavigateDiff={onNavigateDiff}
1405
+ />
1378
1406
  </div>
1379
1407
  )}
1380
1408
 
@@ -1675,7 +1703,7 @@ export function HUD({
1675
1703
  ))}
1676
1704
  </ul>
1677
1705
  )}
1678
- {creatingBlueprint && mapping && onMapAddFile && onMapAddFolder && (
1706
+ {canPlace && mapping && onMapAddFile && onMapAddFolder && (
1679
1707
  <div className="hud-decide hud-map-blueprint">
1680
1708
  <button
1681
1709
  className="hud-button hud-button-approve"
@@ -10,7 +10,7 @@ import { dataDir, targetRoot } from './scripts/target-config.mjs'
10
10
  import { editorFileUri, openInEditor } from './scripts/open-editor.mjs'
11
11
  import {
12
12
  answerBlueprint,
13
- clearDiffSessions,
13
+ recoverOpenDiffSessions,
14
14
  continueDiff,
15
15
  inspectTargetFile,
16
16
  invokeStep,
@@ -20,6 +20,7 @@ import {
20
20
  sendBlueprint,
21
21
  sessionIntent,
22
22
  setStepByStep,
23
+ focusSession,
23
24
  stopSession,
24
25
  updateBlueprint,
25
26
  } from './scripts/session-store.mjs'
@@ -84,8 +85,8 @@ function jsonFilePlugin(): Plugin {
84
85
  return {
85
86
  name: 'visual-coder-json-files',
86
87
  configureServer(server) {
87
- clearDiffSessions(dataDir, targetRoot)
88
- rescanTarget('after clearing diff sessions')
88
+ recoverOpenDiffSessions(dataDir, targetRoot)
89
+ rescanTarget('after recovering diff sessions')
89
90
  server.middlewares.use('/api/user-context', (req, res, next) => {
90
91
  if (req.method === 'GET') {
91
92
  sendJson(res, 200, readUserContext())
@@ -208,6 +209,7 @@ async function decideIntent(req: IncomingMessage, res: ServerResponse) {
208
209
  action !== 'blueprint_no' &&
209
210
  action !== 'blueprint_send' &&
210
211
  action !== 'blueprint_update' &&
212
+ action !== 'focus' &&
211
213
  action !== 'set_step_by_step'
212
214
  ) {
213
215
  sendJson(res, 400, { error: 'invalid workflow action' })
@@ -249,7 +251,9 @@ async function decideIntent(req: IncomingMessage, res: ServerResponse) {
249
251
  body.sessionId,
250
252
  body.diffId,
251
253
  body.instruction ?? '',
254
+ targetRoot,
252
255
  )
256
+ rescanTarget('after withdrawing a patch')
253
257
  } else if (action === 'blueprint_yes') {
254
258
  answerBlueprint(dataDir, body.sessionId, true)
255
259
  } else if (action === 'blueprint_no') {
@@ -270,6 +274,8 @@ async function decideIntent(req: IncomingMessage, res: ServerResponse) {
270
274
  addedVariables: body.addedVariables,
271
275
  addedImports: body.addedImports,
272
276
  })
277
+ } else if (action === 'focus') {
278
+ focusSession(dataDir, body.sessionId)
273
279
  } else if (action === 'set_step_by_step') {
274
280
  if (typeof body.stepByStep !== 'boolean') {
275
281
  sendJson(res, 400, { error: 'stepByStep is required' })
package/bin/session.mjs CHANGED
@@ -88,8 +88,8 @@ export async function waitForBlueprint(args) {
88
88
  const islands = blueprint.userCreatedIslands ?? []
89
89
  console.log(
90
90
  blueprint.enabled
91
- ? `VISUAL_CODER_BLUEPRINT_READY The user sent ${blocks.length} file(s) and ${islands.length} island(s) for this chat. The blueprint is leading: create those paths and honor addedFunctions, addedVariables, and addedImports even if they are not on disk. Do not omit, rename, relocate, or replace them. Extra new files not in the blueprint are a deviation. If you would differ from the blueprint, ask the user first; do not silently deviate.`
92
- : 'VISUAL_CODER_BLUEPRINT_READY The user skipped the blueprint. Continue without user-placed files or islands.',
91
+ ? `VISUAL_CODER_BLUEPRINT_READY The user sent ${blocks.length} file(s) and ${islands.length} island(s) for this chat. The blueprint is leading: create those paths and honor addedFunctions, addedVariables, and addedImports even if they are not on disk. Do not omit, rename, relocate, or replace them. Extra new files not in the blueprint are a deviation. If you would differ from the blueprint, ask the user first; do not silently deviate. The user can still place files and islands on later steps; re-read this session's blueprint.json before each step.`
92
+ : 'VISUAL_CODER_BLUEPRINT_READY The user skipped the initial blueprint. They can still place files and islands on later steps; re-read this session\'s blueprint.json before each step. Continue without user-placed files until that file is enabled.',
93
93
  )
94
94
  console.log('VISUAL_CODER_BLUEPRINT_START')
95
95
  console.log(JSON.stringify(blueprint, null, 2))
@@ -157,8 +157,8 @@ export async function waitForApproval(args) {
157
157
  ? `Waiting for the user to invoke step ${afterAdvance.currentStep}...`
158
158
  : afterAdvance.phase === 'review' && afterAdvance.diffs?.at(-1)
159
159
  ? afterAdvance.stepByStep === false
160
- ? `Waiting for the finish step after ${afterAdvance.diffs.at(-1).id}...`
161
- : `Waiting for the user to run the next step after ${afterAdvance.diffs.at(-1).id}...`
160
+ ? `Waiting for the user to accept the proposal after ${afterAdvance.diffs.at(-1).id}...`
161
+ : `Waiting for the user to accept the proposal on ${afterAdvance.diffs.at(-1).id}...`
162
162
  : `Waiting for the visual workflow in session ${sessionId}...`,
163
163
  )
164
164
 
@@ -185,7 +185,7 @@ export async function waitForApproval(args) {
185
185
  if (manifest.phase === 'working') {
186
186
  const next = manifest.steps.find((step) => step.index === manifest.currentStep)
187
187
  console.log(
188
- `VISUAL_CODER_EXECUTE Step ${manifest.currentStep} is invoked${next ? `: ${next.title}` : ''}. Implement only this step, publish its incremental diff with inbase propose-patch --session ${sessionId}, then wait again.`,
188
+ `VISUAL_CODER_EXECUTE Step ${manifest.currentStep} is invoked${next ? `: ${next.title}` : ''}. Re-read this session's blueprint.json before implementing; the user can place files and islands on any step. Patch files are the source of truth: write only this step's unified diff against the current live files (baseline + accepted patches), publish it with inbase propose-patch --session ${sessionId}, then wait again. Do not Write, StrReplace, or Delete project files.`,
189
189
  )
190
190
  process.exit(0)
191
191
  }
@@ -194,7 +194,7 @@ export async function waitForApproval(args) {
194
194
  ? `\nVISUAL_CODER_INSTRUCTION_START\n${manifest.pendingInstruction}\nVISUAL_CODER_INSTRUCTION_END`
195
195
  : ''
196
196
  console.log(
197
- `VISUAL_CODER_REPLAN Keep accepted steps before step ${manifest.currentStep}. Replace the plan from step ${manifest.currentStep} onward using the instruction below. The session blueprint remains leading; if this instruction would differ from it, ask the user before replacing the plan. Report the revised tail with inbase report-plan, then wait for invocation.${instruction}`,
197
+ `VISUAL_CODER_REPLAN Keep accepted patch files before step ${manifest.currentStep}. Replace the withdrawn step with a new patch against those live files, not against the withdrawn proposal. Do not edit project files. The session blueprint remains leading; if this instruction would differ from it, ask the user before replacing the plan. Report the revised tail with inbase report-plan, then wait for invocation.${instruction}`,
198
198
  )
199
199
  process.exit(4)
200
200
  }
@@ -259,9 +259,9 @@ export async function proposePatch(args) {
259
259
  const last = entry.step >= manifest.steps.length
260
260
  console.log(
261
261
  manifest.phase === 'working'
262
- ? `VISUAL_CODER_STEP_READY Published diff ${entry.id} for session ${sessionId}, step ${entry.step}/${manifest.steps.length}: ${parsed.files.length} changed, ${parsed.creates.length} added. Step by step is off, so the next step is already invoked. Wait again.`
262
+ ? `VISUAL_CODER_STEP_READY Stored patch ${entry.id} for session ${sessionId}, step ${entry.step}/${manifest.steps.length}: ${parsed.files.length} changed, ${parsed.creates.length} added. Applied the live patch files. Do not edit project files. Step by step is off, so the next step is already invoked. Wait again.`
263
263
  : last
264
- ? `VISUAL_CODER_STEP_READY Published diff ${entry.id} for session ${sessionId}, step ${entry.step}/${manifest.steps.length}: ${parsed.files.length} changed, ${parsed.creates.length} added. Walk the diffs, then Complete to finish.`
265
- : `VISUAL_CODER_STEP_READY Published diff ${entry.id} for session ${sessionId}, step ${entry.step}/${manifest.steps.length}: ${parsed.files.length} changed, ${parsed.creates.length} added. Wait for Continue or an alternative instruction.`,
264
+ ? `VISUAL_CODER_STEP_READY Stored patch ${entry.id} for session ${sessionId}, step ${entry.step}/${manifest.steps.length}: ${parsed.files.length} changed, ${parsed.creates.length} added. Applied the live patch files. Do not edit project files. Walk the diffs, then Accept proposal to finish.`
265
+ : `VISUAL_CODER_STEP_READY Stored patch ${entry.id} for session ${sessionId}, step ${entry.step}/${manifest.steps.length}: ${parsed.files.length} changed, ${parsed.creates.length} added. Applied the live patch files. Do not edit project files. Wait for Accept proposal or an alternative instruction.`,
266
266
  )
267
267
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jkwd/inbase",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "A first-person 3D map of a codebase, with a visual coding workflow for Cursor.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -3,8 +3,10 @@ name: inbase
3
3
  description: >-
4
4
  Grounds source-file changes in the Inbase visual map. Use when creating,
5
5
  editing, or deleting application source files in this repository. Lists every
6
- feature step, then works only via patch files. Do not use for git, docs-only,
7
- lockfiles, or questions.
6
+ feature step, then works only via patch files. Patch files are the single
7
+ source of truth: never Write, StrReplace, or Delete project files; only write
8
+ a unified diff and publish it. Do not use for git, docs-only, lockfiles, or
9
+ questions.
8
10
  ---
9
11
 
10
12
  # Inbase visual edits
@@ -14,13 +16,19 @@ Skip it for git, lockfiles, `.inbase`, `.cursor`, or questions with no code
14
16
  changes.
15
17
 
16
18
  The LLM uses a plan-first loop. It reports the complete plan before writing a
17
- patch. When **Step by step** is on, it waits for the user to invoke the
18
- highlighted step, publishes only that step's diff, then waits for **Run step**
19
- on the following step or an alternative instruction. When **Step by step** is
19
+ patch. When **Step by step** is on, it waits for the user to invoke the first
20
+ step, publishes only that step's diff, then waits for **Accept proposal** on
21
+ that step or an alternative instruction. When **Step by step** is
20
22
  off, `inbase wait-for-approval` returns `VISUAL_CODER_EXECUTE` for every
21
- remaining step without the user clicking Run step; after the last patch, wait
22
- for the user to **Complete**. They can still walk Previous/Next over the diffs.
23
- Always work via patch files. Do not Write, StrReplace, or Delete project files.
23
+ remaining step without the user clicking; after the last patch, wait
24
+ for the user to **Accept proposal**. They can still walk Previous/Next over the diffs.
25
+
26
+ **Patch files are the single source of truth.** Do not Write, StrReplace, or
27
+ Delete project files. Only write a unified diff and publish it with
28
+ `inbase propose-patch`. That command stores the patch and applies the live
29
+ patch chain (baseline + accepted patches + this step). A later instruction
30
+ replaces the withdrawn step's patch; write the new diff against the accepted
31
+ live files, not against the withdrawn proposal.
24
32
 
25
33
  Every Cursor chat has an explicit session ID. Pass that same ID to every
26
34
  command. The visualizer stores immutable diffs under `.inbase/diff-sessions/<session-id>/diffs/`.
@@ -45,9 +53,12 @@ npx inbase wait-for-blueprint --session "<current-cursor-chat-id>"
45
53
  ```
46
54
 
47
55
  The explorer asks **Setup blueprint: Yes vs No**.
48
- - **No**: skip placement; continue without user-placed files or islands.
56
+ - **No**: skip the initial placement. The user can still place files and
57
+ islands on later steps; re-read this session's `blueprint.json` before
58
+ each step.
49
59
  - **Yes**: the user places files (`Space`) and islands (`B`), then clicks
50
- **Send blueprint**.
60
+ **Send blueprint**. They can keep placing on later steps. This chat's
61
+ blueprint is stored only for this session.
51
62
  3. Read the handshake output between `VISUAL_CODER_BLUEPRINT_START` and
52
63
  `VISUAL_CODER_BLUEPRINT_END`, or read
53
64
  `.inbase/diff-sessions/<session-id>/blueprint.json`.
@@ -92,8 +103,11 @@ npx inbase wait-for-approval --session "<current-cursor-chat-id>"
92
103
  ```
93
104
 
94
105
  Do not write a patch until this prints `VISUAL_CODER_EXECUTE`. If Step by
95
- step is off, this returns immediately for each remaining step.
96
- 9. Implement only the invoked step as a unified diff. Paths are relative to the
106
+ step is off, this returns immediately for each remaining step. Before
107
+ implementing, re-read this session's `blueprint.json`; the user can place
108
+ files and islands on any step.
109
+ 9. Implement only the invoked step as a unified diff against the current live
110
+ files (baseline + accepted patch files). Paths are relative to the
97
111
  project root (same ids as `codebase.json`):
98
112
 
99
113
  ```diff
@@ -122,12 +136,15 @@ npx inbase propose-patch \
122
136
  ```
123
137
 
124
138
  The patch path or stdin is required. Never write or replace a patch already
125
- stored in the session folder.
139
+ stored in the session folder. `inbase propose-patch` stores the new file and
140
+ applies the live patch chain. Do not `git apply`, copy files, or edit
141
+ project files yourself.
126
142
 
127
- 10. **Stop.** Do not apply the patch and do not edit project files directly.
128
- 11. Wait until the user clicks **Run step** on the next step (or, when Step by
143
+ 10. **Stop.** Do not edit project files. The stored patch files are already the
144
+ live tree.
145
+ 11. Wait until the user clicks **Accept proposal** on the current step (or, when Step by
129
146
  step is off, until the next step is auto-invoked), sends an alternative
130
- instruction, clicks **Complete** on the last step, or stops the workflow:
147
+ instruction, or stops the workflow:
131
148
 
132
149
  ```bash
133
150
  npx inbase wait-for-approval --session "<current-cursor-chat-id>"
@@ -135,15 +152,18 @@ npx inbase wait-for-approval --session "<current-cursor-chat-id>"
135
152
 
136
153
  12. Read the wait command output:
137
154
 
138
- - Exit `0` (`VISUAL_CODER_EXECUTE`): the highlighted step was invoked. Build
139
- only that step, publish its incremental diff with `inbase propose-patch`,
140
- then wait again.
155
+ - Exit `0` (`VISUAL_CODER_EXECUTE`): the highlighted step was invoked. Re-read
156
+ this session's `blueprint.json` first; the user can place files and islands
157
+ on any step, not only during the initial blueprint. Build only that step as
158
+ a unified diff against the live files, publish it with `inbase propose-patch`,
159
+ then wait again. Do not edit project files.
141
160
  - Exit `5` (`VISUAL_CODER_FINISHED`): that was the last step. The visualizer
142
161
  already applied the final patch and removed stored session diffs and
143
162
  blueprint drafts. Optionally run `--clear` if anything remains, tell the
144
163
  user the feature is done, and **stop**. Do not propose another patch.
145
- - Exit `4` (`VISUAL_CODER_REPLAN`): do **not** apply files and do not rewrite
146
- an earlier diff. Follow the text between
164
+ - Exit `4` (`VISUAL_CODER_REPLAN`): do **not** edit project files and do not
165
+ rewrite an earlier accepted patch. The withdrawn proposal is no longer live;
166
+ disk is baseline + accepted patch files. Follow the text between
147
167
  `VISUAL_CODER_INSTRUCTION_START` and `VISUAL_CODER_INSTRUCTION_END`, read
148
168
  this session's `blueprint.json` when it is enabled (files, islands,
149
169
  `addedFunctions`, `addedVariables`, `addedImports`). The blueprint stays
@@ -151,7 +171,8 @@ npx inbase wait-for-approval --session "<current-cursor-chat-id>"
151
171
  replacing the plan. Read `user-context.json` (follow the viewpoint only if
152
172
  `followLook` is true), replace the plan from the current step onward using
153
173
  `inbase report-plan`, then wait for the user to invoke the first revised
154
- step.
174
+ step. The replacement patch must apply on top of the accepted live files,
175
+ not the withdrawn proposal.
155
176
  - Exit `2` (`VISUAL_CODER_STOPPED`) or `3` (timeout): make no further
156
177
  project changes.
157
178
 
@@ -172,6 +193,9 @@ npx inbase propose-patch --session "<current-cursor-chat-id>" --clear
172
193
  - Read global `user-context.json` for placed files; those live on the session blueprint
173
194
  - Follow the user's look when `followLook` is false
174
195
  - Write, edit, create, or delete project files directly
196
+ - `git apply`, copy, or otherwise materialize a patch yourself
197
+ - Treat on-disk project files as something to edit; they are a replay of the
198
+ stored patch files
175
199
  - Announce file lists instead of a patch
176
200
  - Write a patch before its plan step is invoked
177
201
  - Propose the next step before `inbase wait-for-approval` returns `VISUAL_CODER_EXECUTE`