@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.
- package/README.md +26 -4
- package/apps/explorer/scripts/patch-lib.d.ts +1 -0
- package/apps/explorer/scripts/patch-lib.mjs +1 -0
- package/apps/explorer/scripts/session-store.d.ts +6 -0
- package/apps/explorer/scripts/session-store.mjs +124 -67
- package/apps/explorer/src/App.tsx +84 -60
- package/apps/explorer/src/agentIntent.ts +15 -0
- package/apps/explorer/src/index.css +28 -10
- package/apps/explorer/src/types.ts +2 -0
- package/apps/explorer/src/ui/HUD.tsx +124 -96
- package/apps/explorer/vite.config.ts +9 -3
- package/bin/session.mjs +9 -9
- package/package.json +1 -1
- package/skill/inbase/SKILL.md +46 -22
|
@@ -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
|
|
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
|
|
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
|
-
|
|
301
|
-
}
|
|
302
|
-
if (pending && typeof intent.step === 'number' && !lastStep) {
|
|
303
|
-
doneSteps.add(intent.step)
|
|
311
|
+
acceptedSteps.add(intent.step)
|
|
304
312
|
}
|
|
305
|
-
const
|
|
306
|
-
intent.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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
|
-
) :
|
|
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 && !
|
|
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
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 &&
|
|
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
|
-
...(
|
|
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
|
-
...(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
{
|
|
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.
|
|
1366
|
-
<
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
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
|
-
{
|
|
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
|
-
|
|
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
|
-
|
|
88
|
-
rescanTarget('after
|
|
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
|
|
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
|
|
161
|
-
: `Waiting for the user to
|
|
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}` : ''}.
|
|
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
|
|
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
|
|
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
|
|
265
|
-
: `VISUAL_CODER_STEP_READY
|
|
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
package/skill/inbase/SKILL.md
CHANGED
|
@@ -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.
|
|
7
|
-
|
|
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
|
-
|
|
19
|
-
|
|
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
|
|
22
|
-
for the user to **
|
|
23
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
128
|
-
|
|
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,
|
|
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.
|
|
139
|
-
|
|
140
|
-
|
|
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**
|
|
146
|
-
an earlier
|
|
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`
|