@jkwd/inbase 0.1.21 → 0.1.22
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 +13 -7
- package/apps/explorer/package.json +1 -0
- package/apps/explorer/scripts/explain-store.d.ts +135 -0
- package/apps/explorer/scripts/explain-store.mjs +666 -0
- package/apps/explorer/scripts/patch-lib.mjs +4 -0
- package/apps/explorer/scripts/scan-target.mjs +22 -5
- package/apps/explorer/scripts/session-store.d.ts +86 -11
- package/apps/explorer/scripts/session-store.mjs +371 -58
- package/apps/explorer/scripts/target-config.d.ts +38 -3
- package/apps/explorer/scripts/target-config.mjs +147 -3
- package/apps/explorer/src/App.tsx +1073 -158
- package/apps/explorer/src/agentIntent.ts +61 -7
- package/apps/explorer/src/codebase.ts +1 -1
- package/apps/explorer/src/devTargets.ts +66 -0
- package/apps/explorer/src/explain.ts +312 -0
- package/apps/explorer/src/index.css +874 -222
- package/apps/explorer/src/layout.ts +55 -0
- package/apps/explorer/src/scene/DistantFileBlocks.tsx +4 -2
- package/apps/explorer/src/scene/FileBlock.tsx +95 -72
- package/apps/explorer/src/scene/FolderArea.tsx +55 -32
- package/apps/explorer/src/scene/MapView.tsx +506 -33
- package/apps/explorer/src/scene/RelationLines.tsx +7 -0
- package/apps/explorer/src/scene/World.tsx +146 -32
- package/apps/explorer/src/speech.ts +228 -0
- package/apps/explorer/src/theme.ts +29 -0
- package/apps/explorer/src/types.ts +98 -8
- package/apps/explorer/src/ui/CanvasErrorBoundary.tsx +1 -1
- package/apps/explorer/src/ui/ExplainAskCard.tsx +142 -0
- package/apps/explorer/src/ui/ExplainHud.tsx +524 -0
- package/apps/explorer/src/ui/ExplainInfoPanel.tsx +135 -0
- package/apps/explorer/src/ui/ExplainPointer.tsx +73 -0
- package/apps/explorer/src/ui/EyeIcon.tsx +38 -1
- package/apps/explorer/src/ui/HUD.tsx +1067 -795
- package/apps/explorer/src/ui/NameInput.tsx +114 -5
- package/apps/explorer/src/userContext.ts +0 -11
- package/apps/explorer/src/userCreated.ts +54 -1
- package/apps/explorer/vite.config.ts +173 -26
- package/bin/inbase.mjs +11 -2
- package/bin/project.mjs +1 -1
- package/bin/session.mjs +287 -38
- package/package.json +4 -1
- package/skill/commands/amber.md +23 -0
- package/skill/commands/blue.md +13 -0
- package/skill/commands/coral.md +23 -0
- package/skill/commands/explain.md +77 -0
- package/skill/commands/green.md +23 -0
- package/skill/commands/inbase.md +7 -5
- package/skill/commands/lime.md +23 -0
- package/skill/commands/orange.md +23 -0
- package/skill/commands/purple.md +23 -0
- package/skill/commands/red.md +23 -0
- package/skill/commands/skipinbase.md +1 -1
- package/skill/commands/violet.md +23 -0
- package/skill/commands/yellow.md +23 -0
- package/skill/inbase/SKILL.md +124 -76
- package/apps/explorer/src/scene/BlockPlacer.tsx +0 -78
- package/apps/explorer/src/scene/IslandPlacer.tsx +0 -31
- package/apps/explorer/src/scene/SelectionThumbnail.tsx +0 -1069
package/bin/session.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { pathToFileURL } from 'node:url'
|
|
4
|
-
import { explorerRoot, takeFlagValue, takeFlagValues } from './project.mjs'
|
|
4
|
+
import { explorerRoot, instanceFile, readInstanceFile, takeFlagValue, takeFlagValues } from './project.mjs'
|
|
5
5
|
|
|
6
6
|
async function loadExplorer() {
|
|
7
7
|
const storePath = pathToFileURL(
|
|
@@ -104,7 +104,19 @@ function emitStopped(store, dataDir, sessionId) {
|
|
|
104
104
|
process.exit(2)
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
function
|
|
107
|
+
function emitTargetExplain(store, explain, dataDir, sessionId) {
|
|
108
|
+
const consumed = explain.consumeExplainStart(dataDir)
|
|
109
|
+
if (!consumed) return false
|
|
110
|
+
const label = explain.explainTargetLabel(consumed)
|
|
111
|
+
signalAck(store, dataDir, sessionId, 'explain', label)
|
|
112
|
+
const quoted = JSON.stringify(consumed.question)
|
|
113
|
+
console.log(
|
|
114
|
+
`VISUAL_CODER_EXPLAIN The user clicked Explain on the ${label}. Do not edit project files. Do not accept or invoke the next step. Do not start the map walk overlay. The visualizer shows a single-explanation card. Run: npx inbase explain start --question ${quoted} Then inspect that ${consumed.kind} and where it fits in the codebase, and report one explanation with npx inbase explain report --question ${quoted} --step "..." --body "...". Use a single --step. After reporting, run npx inbase explain wait. If explain wait returns VISUAL_CODER_EXPLAIN for another file or folder, replace the explanation with one new step. When explain wait returns stopped or timeout, run wait-for-approval again.`,
|
|
115
|
+
)
|
|
116
|
+
process.exit(7)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function emitApprovalHandshake(store, explain, dataDir, sessionId, manifest, initialDiff, pendingStart) {
|
|
108
120
|
const current = initialDiff
|
|
109
121
|
? manifest?.diffs.find((entry) => entry.id === initialDiff.id)
|
|
110
122
|
: null
|
|
@@ -119,6 +131,36 @@ function emitApprovalHandshake(store, dataDir, sessionId, manifest, initialDiff)
|
|
|
119
131
|
)
|
|
120
132
|
process.exit(5)
|
|
121
133
|
}
|
|
134
|
+
if (pendingStart) {
|
|
135
|
+
emitTargetExplain(store, explain, dataDir, sessionId)
|
|
136
|
+
}
|
|
137
|
+
if (manifest.pendingExplain) {
|
|
138
|
+
const consumed = store.consumeExplainRequest(dataDir, sessionId) ?? manifest
|
|
139
|
+
const reviewing = consumed.phase === 'review'
|
|
140
|
+
const active = reviewing ? consumed.diffs?.at(-1) : null
|
|
141
|
+
const step = active?.step ?? consumed.currentStep
|
|
142
|
+
const title =
|
|
143
|
+
consumed.steps?.find((item) => item.index === step)?.title ||
|
|
144
|
+
active?.title ||
|
|
145
|
+
`step ${step}`
|
|
146
|
+
signalAck(
|
|
147
|
+
store,
|
|
148
|
+
dataDir,
|
|
149
|
+
sessionId,
|
|
150
|
+
'explain',
|
|
151
|
+
`the proposal for ${title}`,
|
|
152
|
+
)
|
|
153
|
+
const target = reviewing
|
|
154
|
+
? 'inspect the live files this proposal changed'
|
|
155
|
+
: 'inspect the live files and folders this plan step will use'
|
|
156
|
+
const waiting = reviewing
|
|
157
|
+
? 'The proposal is still waiting for Accept proposal.'
|
|
158
|
+
: 'The plan is still waiting for Create proposal.'
|
|
159
|
+
console.log(
|
|
160
|
+
`VISUAL_CODER_EXPLAIN The user clicked Explain proposal for step ${step}: ${title}. Do not edit project files. Do not accept or invoke the next step. Start explain mode and walk this proposal on the map. Run: npx inbase explain start --question "Explain the current proposal: ${title}" Then ${target} and report steps with npx inbase explain report --question "Explain the current proposal: ${title}" --step "..." --body "..." --files path [--folders path] [--select path] [--zoom path] [--relations from:to] [--info] [--highlight function:name] [--point function:name]. After reporting, run npx inbase explain wait. If the user asks about a step, report sub-steps with --parent and wait again. When explain wait returns stopped or timeout, run wait-for-approval again. ${waiting}`,
|
|
161
|
+
)
|
|
162
|
+
process.exit(7)
|
|
163
|
+
}
|
|
122
164
|
if (manifest.phase === 'working') {
|
|
123
165
|
const next = manifest.steps.find((step) => step.index === manifest.currentStep)
|
|
124
166
|
const title = next?.title
|
|
@@ -150,7 +192,7 @@ function emitApprovalHandshake(store, dataDir, sessionId, manifest, initialDiff)
|
|
|
150
192
|
console.log(
|
|
151
193
|
continuing
|
|
152
194
|
? `VISUAL_CODER_EXECUTE Step ${manifest.currentStep} is invoked${title ? `: ${title}` : ''}. Continue immediately: edit live files for this step only, then inbase propose-patch --session ${sessionId} with no patch file. Do not explore, re-plan, or run wait-for-blueprint.`
|
|
153
|
-
: `VISUAL_CODER_EXECUTE Step ${manifest.currentStep} is invoked${title ? `: ${title}` : ''}. Re-read the
|
|
195
|
+
: `VISUAL_CODER_EXECUTE Step ${manifest.currentStep} is invoked${title ? `: ${title}` : ''}. Re-read the global blueprint.json and this session's local blueprint before implementing; the user can place files and islands at any time. Edit the live project files for this step only (Write, StrReplace, Delete). Then record the step with inbase propose-patch --session ${sessionId} — no patch file. Inbase diffs those edits against the invoke snapshot and stores the patch. Do not write a unified diff yourself.`,
|
|
154
196
|
)
|
|
155
197
|
}
|
|
156
198
|
process.exit(0)
|
|
@@ -190,26 +232,84 @@ export async function startSession(args) {
|
|
|
190
232
|
|
|
191
233
|
export async function attachSession(args) {
|
|
192
234
|
const { store, config } = await loadExplorer()
|
|
235
|
+
if (!readInstanceFile(instanceFile(config.dataDir))) {
|
|
236
|
+
console.error(store.NOT_RUNNING_MESSAGE)
|
|
237
|
+
process.exit(1)
|
|
238
|
+
}
|
|
193
239
|
const sessionId = takeFlagValue(args, '--session')
|
|
194
|
-
const
|
|
240
|
+
const colorQuery = takeFlagValue(args, '--color')
|
|
241
|
+
const manifest = store.attachSession(config.dataDir, sessionId, { color: colorQuery })
|
|
242
|
+
const color = store.resolveSessionColor(manifest.color)
|
|
243
|
+
const colorName = color?.name || null
|
|
195
244
|
console.log(`VISUAL_CODER_SESSION ${manifest.sessionId}`)
|
|
196
|
-
|
|
245
|
+
if (colorName) console.log(`VISUAL_CODER_COLOR ${colorName}`)
|
|
246
|
+
printAck('attached', colorName || manifest.name || manifest.sessionId)
|
|
197
247
|
console.log(
|
|
198
|
-
|
|
248
|
+
colorName
|
|
249
|
+
? `VISUAL_CODER_ATTACHED Attached to the ${colorName} session (${manifest.phase}). Tell the user you connected to the ${colorName} chat. Use --session ${manifest.sessionId} for every later command. Run inbase wait-for-blueprint --session ${manifest.sessionId} to read the optional blueprint, instruction, and attached files; it does not wait. Then run wait-for-approval so map Explain clicks are heard.`
|
|
250
|
+
: `VISUAL_CODER_ATTACHED Attached to the next waiting visualizer session ${manifest.name || manifest.sessionId} (${manifest.phase}). Use --session ${manifest.sessionId} for every later command. Run inbase wait-for-blueprint --session ${manifest.sessionId} to read the optional blueprint, instruction, and attached files; it does not wait. Then run wait-for-approval so map Explain clicks are heard.`,
|
|
199
251
|
)
|
|
200
252
|
}
|
|
201
253
|
|
|
202
|
-
function printBlueprintDump(blueprint) {
|
|
254
|
+
function printBlueprintDump(blueprint, options = {}) {
|
|
203
255
|
const blocks = blueprint.userCreatedBlocks ?? []
|
|
204
256
|
const islands = blueprint.userCreatedIslands ?? []
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
257
|
+
const local = options.local === true
|
|
258
|
+
const colorName = options.colorName || 'session'
|
|
259
|
+
const readyTag = local
|
|
260
|
+
? 'VISUAL_CODER_LOCAL_BLUEPRINT_READY'
|
|
261
|
+
: 'VISUAL_CODER_BLUEPRINT_READY'
|
|
262
|
+
const startTag = local
|
|
263
|
+
? 'VISUAL_CODER_LOCAL_BLUEPRINT_START'
|
|
264
|
+
: 'VISUAL_CODER_BLUEPRINT_START'
|
|
265
|
+
const endTag = local
|
|
266
|
+
? 'VISUAL_CODER_LOCAL_BLUEPRINT_END'
|
|
267
|
+
: 'VISUAL_CODER_BLUEPRINT_END'
|
|
268
|
+
if (local) {
|
|
269
|
+
console.log(
|
|
270
|
+
blueprint.enabled
|
|
271
|
+
? `${readyTag} The ${colorName} session blueprint has ${blocks.length} file(s) and ${islands.length} island(s). This local blueprint is only for this ${colorName} chat. It is leading together with the global blueprint: create those paths and honor addedFunctions, addedVariables, addedImports, and notes even if they are not on disk. Do not omit, rename, relocate, or replace them. Extra new files not in either blueprint are a deviation. If you would differ from this local blueprint, ask the user first.`
|
|
272
|
+
: `${readyTag} The ${colorName} session blueprint is empty. Only this ${colorName} chat can see a local blueprint if the user places one later.`,
|
|
273
|
+
)
|
|
274
|
+
} else {
|
|
275
|
+
console.log(
|
|
276
|
+
blueprint.enabled
|
|
277
|
+
? `${readyTag} The global blueprint has ${blocks.length} file(s) and ${islands.length} island(s). The global blueprint is shared with every session and is leading: create those paths and honor addedFunctions, addedVariables, addedImports, and notes even if they are not on disk. Notes are extra instructions or pseudo code for a file, function, or variable — follow them when implementing those items. Do not omit, rename, relocate, or replace them. Extra new files that are not in the global or this session's local blueprint are a deviation. If you would differ from the blueprint, ask the user first; do not silently deviate. The user can keep placing files and islands; re-read the global blueprint.json when it is printed again.`
|
|
278
|
+
: `${readyTag} The global blueprint is empty. The user can still place files and islands on the global or this session's color; re-read the global blueprint.json when it is printed again. Continue without user-placed files until that file has content.`,
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
console.log(startTag)
|
|
211
282
|
console.log(JSON.stringify(blueprint, null, 2))
|
|
212
|
-
console.log(
|
|
283
|
+
console.log(endTag)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function printSessionBlueprints(store, dataDir, sessionId) {
|
|
287
|
+
const global = store.readBlueprint(dataDir)
|
|
288
|
+
const local = store.readLocalBlueprint(dataDir, sessionId)
|
|
289
|
+
const colorName =
|
|
290
|
+
store.resolveSessionColor(store.readManifest(dataDir, sessionId)?.color)?.name ||
|
|
291
|
+
'session'
|
|
292
|
+
const blocks = (global.userCreatedBlocks ?? []).length
|
|
293
|
+
const islands = (global.userCreatedIslands ?? []).length
|
|
294
|
+
const localBlocks = (local.userCreatedBlocks ?? []).length
|
|
295
|
+
const localIslands = (local.userCreatedIslands ?? []).length
|
|
296
|
+
const detail = [
|
|
297
|
+
global.enabled ? `global ${blocks} file(s), ${islands} island(s)` : null,
|
|
298
|
+
local.enabled
|
|
299
|
+
? `${colorName} ${localBlocks} file(s), ${localIslands} island(s)`
|
|
300
|
+
: null,
|
|
301
|
+
]
|
|
302
|
+
.filter(Boolean)
|
|
303
|
+
.join('; ')
|
|
304
|
+
printBlueprintDump(global)
|
|
305
|
+
printBlueprintDump(local, { local: true, colorName })
|
|
306
|
+
store.markBlueprintSeen(dataDir, sessionId, global.revision, local.revision)
|
|
307
|
+
return {
|
|
308
|
+
global,
|
|
309
|
+
local,
|
|
310
|
+
colorName,
|
|
311
|
+
detail: detail || 'none',
|
|
312
|
+
}
|
|
213
313
|
}
|
|
214
314
|
|
|
215
315
|
export async function waitForBlueprint(args) {
|
|
@@ -233,20 +333,14 @@ export async function waitForBlueprint(args) {
|
|
|
233
333
|
emitStopped(store, config.dataDir, sessionId)
|
|
234
334
|
}
|
|
235
335
|
|
|
236
|
-
const
|
|
237
|
-
const blocks = blueprint.userCreatedBlocks ?? []
|
|
238
|
-
const islands = blueprint.userCreatedIslands ?? []
|
|
336
|
+
const dumped = printSessionBlueprints(store, config.dataDir, sessionId)
|
|
239
337
|
signalAck(
|
|
240
338
|
store,
|
|
241
339
|
config.dataDir,
|
|
242
340
|
sessionId,
|
|
243
341
|
'blueprint',
|
|
244
|
-
|
|
245
|
-
? `${blocks.length} file(s), ${islands.length} island(s)`
|
|
246
|
-
: 'none',
|
|
342
|
+
dumped.detail,
|
|
247
343
|
)
|
|
248
|
-
printBlueprintDump(blueprint)
|
|
249
|
-
store.markBlueprintSeen(config.dataDir, sessionId, blueprint.revision)
|
|
250
344
|
const instruction =
|
|
251
345
|
typeof manifest.initialInstruction === 'string'
|
|
252
346
|
? manifest.initialInstruction.trim()
|
|
@@ -273,6 +367,10 @@ export async function waitForBlueprint(args) {
|
|
|
273
367
|
console.log('VISUAL_CODER_CONTEXT_FILE_END')
|
|
274
368
|
}
|
|
275
369
|
}
|
|
370
|
+
const explain = await loadExplainStore()
|
|
371
|
+
if (explain.readExplain(config.dataDir).pendingStart) {
|
|
372
|
+
emitTargetExplain(store, explain, config.dataDir, sessionId)
|
|
373
|
+
}
|
|
276
374
|
process.exit(0)
|
|
277
375
|
}
|
|
278
376
|
|
|
@@ -323,35 +421,57 @@ export async function waitForApproval(args) {
|
|
|
323
421
|
}
|
|
324
422
|
store.autoAdvance(config.dataDir, sessionId, config.targetRoot)
|
|
325
423
|
|
|
424
|
+
const explain = await loadExplainStore()
|
|
326
425
|
const { manifest: manifestPath } = store.sessionPaths(config.dataDir, sessionId)
|
|
426
|
+
const explainFile = path.join(config.dataDir, explain.EXPLAIN_FILE)
|
|
327
427
|
const gate = createManifestGate(manifestPath)
|
|
428
|
+
const explainGate = createManifestGate(explainFile)
|
|
328
429
|
let lastWaiting = null
|
|
329
430
|
try {
|
|
330
431
|
while (Date.now() - started < timeoutMs) {
|
|
331
432
|
store.touchSessionConnection(config.dataDir, sessionId)
|
|
332
433
|
store.autoAdvance(config.dataDir, sessionId, config.targetRoot)
|
|
333
434
|
const manifest = store.readManifest(config.dataDir, sessionId)
|
|
334
|
-
|
|
435
|
+
const pendingStart = explain.readExplain(config.dataDir).pendingStart
|
|
436
|
+
emitApprovalHandshake(
|
|
437
|
+
store,
|
|
438
|
+
explain,
|
|
439
|
+
config.dataDir,
|
|
440
|
+
sessionId,
|
|
441
|
+
manifest,
|
|
442
|
+
initialDiff,
|
|
443
|
+
pendingStart,
|
|
444
|
+
)
|
|
335
445
|
if (!manifest) continue
|
|
336
|
-
const
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
446
|
+
const global = store.readBlueprint(config.dataDir)
|
|
447
|
+
const local = store.readLocalBlueprint(config.dataDir, sessionId)
|
|
448
|
+
const seenGlobal = manifest.blueprintRevision ?? 0
|
|
449
|
+
const seenLocal = manifest.localBlueprintRevision ?? 0
|
|
450
|
+
const globalChanged = global.revision > seenGlobal
|
|
451
|
+
const localChanged = local.revision > seenLocal
|
|
452
|
+
if (globalChanged || localChanged) {
|
|
453
|
+
const dumped = printSessionBlueprints(store, config.dataDir, sessionId)
|
|
454
|
+
const colorName = dumped.colorName
|
|
341
455
|
signalAck(
|
|
342
456
|
store,
|
|
343
457
|
config.dataDir,
|
|
344
458
|
sessionId,
|
|
345
459
|
'blueprint',
|
|
346
|
-
|
|
347
|
-
? `${blocks.length} file(s), ${islands.length} island(s)`
|
|
348
|
-
: 'none',
|
|
349
|
-
)
|
|
350
|
-
console.log(
|
|
351
|
-
'VISUAL_CODER_BLUEPRINT The shared blueprint changed. Follow the latest files, islands, functions, variables, imports, and notes. Do not omit, rename, relocate, or replace them. If this would differ from the current plan, ask the user before replacing the plan. Then run wait-for-approval again.',
|
|
460
|
+
dumped.detail,
|
|
352
461
|
)
|
|
353
|
-
|
|
354
|
-
|
|
462
|
+
if (globalChanged && localChanged) {
|
|
463
|
+
console.log(
|
|
464
|
+
`VISUAL_CODER_BLUEPRINT The global blueprint and the ${colorName} session blueprint changed. Follow the latest files, islands, functions, variables, imports, and notes from both. Do not omit, rename, relocate, or replace them. The ${colorName} blueprint is only for this chat. If this would differ from the current plan, ask the user before replacing the plan. Then run wait-for-approval again.`,
|
|
465
|
+
)
|
|
466
|
+
} else if (localChanged) {
|
|
467
|
+
console.log(
|
|
468
|
+
`VISUAL_CODER_BLUEPRINT The ${colorName} session blueprint changed. This local blueprint is only for this chat. Follow its latest files, islands, functions, variables, imports, and notes together with the global blueprint. Do not omit, rename, relocate, or replace them. If this would differ from the current plan, ask the user before replacing the plan. Then run wait-for-approval again.`,
|
|
469
|
+
)
|
|
470
|
+
} else {
|
|
471
|
+
console.log(
|
|
472
|
+
'VISUAL_CODER_BLUEPRINT The global blueprint changed. Follow the latest files, islands, functions, variables, imports, and notes. Do not omit, rename, relocate, or replace them. If this would differ from the current plan, ask the user before replacing the plan. Then run wait-for-approval again.',
|
|
473
|
+
)
|
|
474
|
+
}
|
|
355
475
|
process.exit(6)
|
|
356
476
|
}
|
|
357
477
|
const waiting = waitingMessage(sessionId, manifest)
|
|
@@ -359,13 +479,15 @@ export async function waitForApproval(args) {
|
|
|
359
479
|
console.log(waiting)
|
|
360
480
|
lastWaiting = waiting
|
|
361
481
|
}
|
|
362
|
-
await gate.wait(50)
|
|
482
|
+
await Promise.race([gate.wait(50), explainGate.wait(50)])
|
|
363
483
|
}
|
|
364
484
|
} finally {
|
|
365
485
|
gate.close()
|
|
486
|
+
explainGate.close()
|
|
366
487
|
}
|
|
367
488
|
|
|
368
489
|
signalAck(store, config.dataDir, sessionId, 'timeout', 'no visualizer signal')
|
|
490
|
+
store.stopSession(config.dataDir, sessionId, config.targetRoot)
|
|
369
491
|
console.error('Timed out waiting for visualizer review. Do not modify files.')
|
|
370
492
|
process.exit(3)
|
|
371
493
|
}
|
|
@@ -383,7 +505,7 @@ export async function proposePatch(args) {
|
|
|
383
505
|
if (!sessionId) usage('propose-patch', '--session <cursor-chat-id> --clear')
|
|
384
506
|
store.stopSession(config.dataDir, sessionId, config.targetRoot)
|
|
385
507
|
console.log(
|
|
386
|
-
`Cleared session ${sessionId}; stored diffs were removed. The
|
|
508
|
+
`Cleared session ${sessionId}; stored diffs were removed. The global blueprint remains.`,
|
|
387
509
|
)
|
|
388
510
|
process.exit(0)
|
|
389
511
|
}
|
|
@@ -429,3 +551,130 @@ export async function proposePatch(args) {
|
|
|
429
551
|
: `VISUAL_CODER_STEP_READY Recorded live edits as patch ${entry.id} for session ${sessionId}, step ${entry.step}/${manifest.steps.length}: ${parsed.files.length} changed, ${parsed.creates.length} added. Immediately run wait-for-approval. Do not explore or plan. Accept proposal invokes the next step; when EXECUTE returns, implement that step at once.`,
|
|
430
552
|
)
|
|
431
553
|
}
|
|
554
|
+
|
|
555
|
+
async function loadExplainStore() {
|
|
556
|
+
const explainPath = pathToFileURL(
|
|
557
|
+
path.join(explorerRoot, 'scripts/explain-store.mjs'),
|
|
558
|
+
).href
|
|
559
|
+
return import(explainPath)
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
export async function runExplain(args) {
|
|
563
|
+
const { store, config } = await loadExplorer()
|
|
564
|
+
if (!readInstanceFile(instanceFile(config.dataDir))) {
|
|
565
|
+
console.error(store.NOT_RUNNING_MESSAGE)
|
|
566
|
+
process.exit(1)
|
|
567
|
+
}
|
|
568
|
+
const explain = await loadExplainStore()
|
|
569
|
+
const parsed = explain.parseExplainCli(args)
|
|
570
|
+
if (parsed.action === 'stop') {
|
|
571
|
+
explain.stopExplain(config.dataDir)
|
|
572
|
+
console.log('VISUAL_CODER_EXPLAIN_STOPPED Explain mode is off.')
|
|
573
|
+
return
|
|
574
|
+
}
|
|
575
|
+
if (parsed.action === 'wait') {
|
|
576
|
+
await waitForExplain(explain, config.dataDir, args)
|
|
577
|
+
return
|
|
578
|
+
}
|
|
579
|
+
if (parsed.action === 'start') {
|
|
580
|
+
if (!parsed.question) {
|
|
581
|
+
usage('explain start', '--question "How does this work?"')
|
|
582
|
+
}
|
|
583
|
+
explain.startExplain(config.dataDir, parsed.question)
|
|
584
|
+
console.log(`VISUAL_CODER_EXPLAIN_STARTED ${parsed.question}`)
|
|
585
|
+
console.log(
|
|
586
|
+
'The map is in explain mode. Explore the codebase, then run inbase explain report with --step / --body / --files / --folders / --select / --zoom / --relations / --info / --highlight / --point.',
|
|
587
|
+
)
|
|
588
|
+
return
|
|
589
|
+
}
|
|
590
|
+
if (!parsed.steps.length) {
|
|
591
|
+
usage(
|
|
592
|
+
'explain report',
|
|
593
|
+
'--question "How does this work?" --step "Title" --body "..." --files path [--folders path] [--select path] [--zoom path] [--relations from:to] [--info] [--highlight function:name] [--point function:name] [--parent 7]',
|
|
594
|
+
)
|
|
595
|
+
}
|
|
596
|
+
const next = explain.reportExplain(config.dataDir, {
|
|
597
|
+
question: parsed.question,
|
|
598
|
+
parent: parsed.parent,
|
|
599
|
+
steps: parsed.steps,
|
|
600
|
+
})
|
|
601
|
+
if (parsed.parent) {
|
|
602
|
+
const added = next.steps.filter((step) =>
|
|
603
|
+
explain.isExplainDescendant(step.index, parsed.parent),
|
|
604
|
+
).length
|
|
605
|
+
console.log(
|
|
606
|
+
`VISUAL_CODER_EXPLAIN_READY Reported ${added} follow-up step(s) under ${parsed.parent} for "${parsed.question || next.question}". Walk ${parsed.parent}.1 … then continue at the next parent step.`,
|
|
607
|
+
)
|
|
608
|
+
} else {
|
|
609
|
+
console.log(
|
|
610
|
+
`VISUAL_CODER_EXPLAIN_READY Reported ${next.steps.length} explanation step(s) for "${next.question}". The user can walk them on the map.`,
|
|
611
|
+
)
|
|
612
|
+
}
|
|
613
|
+
console.log(
|
|
614
|
+
'Run inbase explain wait for a question about the current step, or until the user exits.',
|
|
615
|
+
)
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
async function waitForExplain(explain, dataDir, args) {
|
|
619
|
+
const timeoutMs = Number(takeFlagValue(args, '--timeout') ?? 600000)
|
|
620
|
+
const started = Date.now()
|
|
621
|
+
const explainFile = path.join(dataDir, explain.EXPLAIN_FILE)
|
|
622
|
+
const gate = createManifestGate(explainFile)
|
|
623
|
+
let lastWaiting = null
|
|
624
|
+
try {
|
|
625
|
+
while (Date.now() - started < timeoutMs) {
|
|
626
|
+
const current = explain.readExplain(dataDir)
|
|
627
|
+
if (!current.active) {
|
|
628
|
+
printAck('stopped', 'explain mode was closed')
|
|
629
|
+
console.log(
|
|
630
|
+
'VISUAL_CODER_EXPLAIN_STOPPED The user exited explain mode. Do not report more steps.',
|
|
631
|
+
)
|
|
632
|
+
process.exit(2)
|
|
633
|
+
}
|
|
634
|
+
if (current.pendingStart) {
|
|
635
|
+
const request = explain.consumeExplainStart(dataDir)
|
|
636
|
+
if (!request) continue
|
|
637
|
+
const quoted = JSON.stringify(request.question)
|
|
638
|
+
printAck('explain', explain.explainTargetLabel(request))
|
|
639
|
+
console.log(
|
|
640
|
+
`VISUAL_CODER_EXPLAIN The user clicked Explain on the ${explain.explainTargetLabel(request)}. Replace the current explanation. Do not edit project files. Do not report sub-steps. Do not start the map walk overlay. The visualizer shows a single-explanation card. Run: npx inbase explain start --question ${quoted} Then inspect that ${request.kind} and where it fits in the codebase, and report one explanation with npx inbase explain report --question ${quoted} --step "..." --body "...". Use a single --step. Then run npx inbase explain wait again.`,
|
|
641
|
+
)
|
|
642
|
+
process.exit(0)
|
|
643
|
+
}
|
|
644
|
+
if (current.pendingQuestion && !current.answering) {
|
|
645
|
+
const asked = explain.consumeExplainQuestion(dataDir)
|
|
646
|
+
if (!asked) continue
|
|
647
|
+
const insertParent = asked.parent
|
|
648
|
+
const aboutId = asked.from && asked.from !== asked.parent ? asked.from : asked.parent
|
|
649
|
+
const about =
|
|
650
|
+
current.steps.find((step) => step.index === aboutId) ??
|
|
651
|
+
current.steps.find((step) => step.index === insertParent)
|
|
652
|
+
const title = asked.fromTitle || about?.title || `step ${aboutId}`
|
|
653
|
+
printAck('question', `step ${aboutId}`)
|
|
654
|
+
console.log(
|
|
655
|
+
`VISUAL_CODER_EXPLAIN_QUESTION The user asked about step ${aboutId}: ${title}. Do not replace the whole explanation. Report one-level sub-steps under ${insertParent} with --parent "${insertParent}". This replaces any current sub-steps. Do not nest further (no ${insertParent}.1.1).`,
|
|
656
|
+
)
|
|
657
|
+
console.log(`VISUAL_CODER_PARENT ${insertParent}`)
|
|
658
|
+
console.log(`VISUAL_CODER_INSTRUCTION_START\n${asked.question}\nVISUAL_CODER_INSTRUCTION_END`)
|
|
659
|
+
console.log(
|
|
660
|
+
`Run: npx inbase explain report --parent "${insertParent}" --question ${JSON.stringify(asked.question)} --step "..." --body "..." --files path [--folders path] [--select path] [--zoom path] [--relations from:to] [--info] [--highlight function:name] [--point function:name]. Repeat --step for ${insertParent}.1, ${insertParent}.2, … Then run npx inbase explain wait again.`,
|
|
661
|
+
)
|
|
662
|
+
process.exit(0)
|
|
663
|
+
}
|
|
664
|
+
const waiting = 'Waiting for a question on an explanation step...'
|
|
665
|
+
if (waiting !== lastWaiting) {
|
|
666
|
+
console.log(waiting)
|
|
667
|
+
lastWaiting = waiting
|
|
668
|
+
}
|
|
669
|
+
await gate.wait(50)
|
|
670
|
+
}
|
|
671
|
+
} finally {
|
|
672
|
+
gate.close()
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
printAck('timeout', 'no explain question')
|
|
676
|
+
console.error(
|
|
677
|
+
'Timed out waiting for an explain question. Do not report more steps.',
|
|
678
|
+
)
|
|
679
|
+
process.exit(3)
|
|
680
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jkwd/inbase",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
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",
|
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
"apps/explorer/src/ui",
|
|
46
46
|
"apps/explorer/scripts/branch-changes.d.ts",
|
|
47
47
|
"apps/explorer/scripts/branch-changes.mjs",
|
|
48
|
+
"apps/explorer/scripts/explain-store.d.ts",
|
|
49
|
+
"apps/explorer/scripts/explain-store.mjs",
|
|
48
50
|
"apps/explorer/scripts/js-source.mjs",
|
|
49
51
|
"apps/explorer/scripts/open-editor.d.ts",
|
|
50
52
|
"apps/explorer/scripts/open-editor.mjs",
|
|
@@ -76,6 +78,7 @@
|
|
|
76
78
|
"@react-three/drei": "^9.117.3",
|
|
77
79
|
"@react-three/fiber": "^8.17.10",
|
|
78
80
|
"@vitejs/plugin-react": "^4.3.4",
|
|
81
|
+
"kokoro-js": "^1.2.1",
|
|
79
82
|
"react": "^18.3.1",
|
|
80
83
|
"react-dom": "^18.3.1",
|
|
81
84
|
"three": "^0.170.0",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Attach this chat to the Amber Inbase session
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
The user invoked `/amber`. Attach this chat to the **Amber** Inbase session, not the next empty slot.
|
|
6
|
+
|
|
7
|
+
Do **not** ask for a session id. Do **not** run `inbase start-session`.
|
|
8
|
+
|
|
9
|
+
1. Attach to the Amber session:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx inbase attach --color amber
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If attach fails with `VISUAL_CODER_NOT_RUNNING`, `VISUAL_CODER_CHAT_LIMIT`, `VISUAL_CODER_COLOR_BUSY`, or `VISUAL_CODER_COLOR_UNKNOWN`, reply with that message and stop.
|
|
16
|
+
|
|
17
|
+
2. Read `VISUAL_CODER_SESSION` from the output. That id is the session to use for every later `inbase` command.
|
|
18
|
+
|
|
19
|
+
3. Continue the Inbase visual edits skill from `wait-for-blueprint` onward with that `--session` id. Attach already started the session. `wait-for-blueprint` only reads the optional blueprint, instruction, and attached files; it does not wait.
|
|
20
|
+
|
|
21
|
+
The user's request is:
|
|
22
|
+
|
|
23
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Blue is the global blueprint, not an Inbase chat
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
The user invoked `/blue`. Blue is the **global blueprint**, not a chat slot. There is no Blue LLM session.
|
|
6
|
+
|
|
7
|
+
Reply with exactly this, then stop. Do not run `inbase attach`.
|
|
8
|
+
|
|
9
|
+
Blue is the global blueprint, not a chat. Connect with `/coral`, `/amber`, `/lime`, `/orange`, or `/violet` (or `/red`, `/yellow`, `/green`, `/purple`).
|
|
10
|
+
|
|
11
|
+
The user's request was:
|
|
12
|
+
|
|
13
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Attach this chat to the Coral Inbase session
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
The user invoked `/coral`. Attach this chat to the **Coral** Inbase session, not the next empty slot.
|
|
6
|
+
|
|
7
|
+
Do **not** ask for a session id. Do **not** run `inbase start-session`.
|
|
8
|
+
|
|
9
|
+
1. Attach to the Coral session:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx inbase attach --color coral
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If attach fails with `VISUAL_CODER_NOT_RUNNING`, `VISUAL_CODER_CHAT_LIMIT`, `VISUAL_CODER_COLOR_BUSY`, or `VISUAL_CODER_COLOR_UNKNOWN`, reply with that message and stop.
|
|
16
|
+
|
|
17
|
+
2. Read `VISUAL_CODER_SESSION` from the output. That id is the session to use for every later `inbase` command.
|
|
18
|
+
|
|
19
|
+
3. Continue the Inbase visual edits skill from `wait-for-blueprint` onward with that `--session` id. Attach already started the session. `wait-for-blueprint` only reads the optional blueprint, instruction, and attached files; it does not wait.
|
|
20
|
+
|
|
21
|
+
The user's request is:
|
|
22
|
+
|
|
23
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Explain a codebase question on the Inbase map
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
The user invoked `/explain`. Do **not** attach an Inbase coding session. Do **not** edit project files. Do **not** run `inbase start-session`.
|
|
6
|
+
|
|
7
|
+
Explain mode only runs on the map. Start it so the visualizer switches to a map-only overlay, then walk the question step by step on that map.
|
|
8
|
+
|
|
9
|
+
The user's question is:
|
|
10
|
+
|
|
11
|
+
$ARGUMENTS
|
|
12
|
+
|
|
13
|
+
1. Start explain mode (requires `npx inbase run`):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx inbase explain start --question "$ARGUMENTS"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
If that fails with `VISUAL_CODER_NOT_RUNNING`, reply with that message and stop.
|
|
20
|
+
|
|
21
|
+
2. Reply in this chat first with one short sentence that you opened explain mode on the map.
|
|
22
|
+
|
|
23
|
+
3. Read the codebase for this question. Use the map's files and folders as the source of truth: file ids are repo-relative paths.
|
|
24
|
+
|
|
25
|
+
4. Report the explanation as ordered steps. Each step can highlight files and folders, select a block to show import relations, and zoom the map:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx inbase explain report \
|
|
29
|
+
--question "$ARGUMENTS" \
|
|
30
|
+
--step "Short title" \
|
|
31
|
+
--body "What this step is showing." \
|
|
32
|
+
--files path/to/file.ts \
|
|
33
|
+
--folders path/to/folder \
|
|
34
|
+
--select path/to/file.ts \
|
|
35
|
+
--zoom path/to/folder \
|
|
36
|
+
--relations path/to/file.ts:path/to/other.ts \
|
|
37
|
+
--info \
|
|
38
|
+
--highlight function:currentExplainStep \
|
|
39
|
+
--point function:currentExplainStep
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Repeat `--step` for every step. Flags after a `--step` apply to that step. Use `--imported-by` when the step should show who imports the selected file. `--files` and `--folders` stay at full opacity; everything else on the map goes to 0.5. `--select` clicks that block so its relations draw. `--zoom` frames that folder or file.
|
|
43
|
+
|
|
44
|
+
`--info` opens the file info panel (functions, vars, classes). Pass `--info path/to/file.ts` to choose the file; otherwise it uses `--select` or the first `--files` path. `--highlight function:name` (or `variable:`, `class:`, or a bare name) lights those symbols in the panel. `--point function:name` draws an arrow from this step to that row. `--point file` points at the file title. `--highlight` / `--point` imply `--info`.
|
|
45
|
+
|
|
46
|
+
Keep steps small. Prefer real paths from the repo.
|
|
47
|
+
|
|
48
|
+
5. Wait for a follow-up on a step, or until the user exits:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx inbase explain wait
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
6. Read the wait output. Reply in this chat with the `VISUAL_CODER_ACK` line first, then:
|
|
55
|
+
|
|
56
|
+
- Exit `0` (`VISUAL_CODER_ACK explain` / `VISUAL_CODER_EXPLAIN`): the user clicked `?` on a file, folder, or symbol. Replace the current explanation. Do not use `--parent`. Do not walk the map. Read the path from the ACK line, then start and report **one** explanation for the modal:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx inbase explain start --question "Explain the function of the file path/to/file.ts and where it fits in the codebase."
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Use the printed `--question` text. Then inspect that file or folder and report a single `--step` / `--body` with `npx inbase explain report`. Then run `npx inbase explain wait` again.
|
|
63
|
+
|
|
64
|
+
- Exit `0` (`VISUAL_CODER_ACK question` / `VISUAL_CODER_EXPLAIN_QUESTION`): the user asked about the printed parent step. Do not replace the whole explanation. Read the question between `VISUAL_CODER_INSTRUCTION_START` and `VISUAL_CODER_INSTRUCTION_END`. Report closer sub-steps with `--parent` (they become `7.1`, `7.2`, … and Next walks those until the original next step):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx inbase explain report \
|
|
68
|
+
--parent "7" \
|
|
69
|
+
--question "the user's follow-up" \
|
|
70
|
+
--step "Closer look" \
|
|
71
|
+
--body "What this sub-step is showing." \
|
|
72
|
+
--files path/to/file.ts
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Then run `npx inbase explain wait` again. Asking another question replaces the current sub-steps. Sub-steps stay one level (`7.1`, `7.2`) — never `7.1.1`. If they asked from `7.1`, still report with `--parent "7"`.
|
|
76
|
+
|
|
77
|
+
- Exit `2` (`VISUAL_CODER_ACK stopped` / `VISUAL_CODER_EXPLAIN_STOPPED`) or `3` (`VISUAL_CODER_ACK timeout`): stop. Do not attach a coding session.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Attach this chat to the Lime (green) Inbase session
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
The user invoked `/green`. Attach this chat to the **Lime** Inbase session, not the next empty slot.
|
|
6
|
+
|
|
7
|
+
Do **not** ask for a session id. Do **not** run `inbase start-session`.
|
|
8
|
+
|
|
9
|
+
1. Attach to the Lime session:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx inbase attach --color green
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If attach fails with `VISUAL_CODER_NOT_RUNNING`, `VISUAL_CODER_CHAT_LIMIT`, `VISUAL_CODER_COLOR_BUSY`, or `VISUAL_CODER_COLOR_UNKNOWN`, reply with that message and stop.
|
|
16
|
+
|
|
17
|
+
2. Read `VISUAL_CODER_SESSION` from the output. That id is the session to use for every later `inbase` command.
|
|
18
|
+
|
|
19
|
+
3. Continue the Inbase visual edits skill from `wait-for-blueprint` onward with that `--session` id. Attach already started the session. `wait-for-blueprint` only reads the optional blueprint, instruction, and attached files; it does not wait.
|
|
20
|
+
|
|
21
|
+
The user's request is:
|
|
22
|
+
|
|
23
|
+
$ARGUMENTS
|
package/skill/commands/inbase.md
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Attach this chat to the next
|
|
2
|
+
description: Attach this chat to the next empty Inbase session
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
The user invoked `/inbase`.
|
|
5
|
+
The user invoked `/inbase`. Attach this chat to the next unconnected Inbase session. You do not need this command — a regular Cursor chat does the same thing.
|
|
6
6
|
|
|
7
|
-
Do **not** ask for a session id. Do **not** run `inbase start-session`.
|
|
7
|
+
Do **not** ask for a session id. Do **not** run `inbase start-session`.
|
|
8
8
|
|
|
9
|
-
1. Attach to the next
|
|
9
|
+
1. Attach to the next unconnected visualizer session (oldest first; skip sessions that already have an LLM):
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
npx inbase attach
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
If attach fails with `VISUAL_CODER_NOT_RUNNING`, `VISUAL_CODER_CHAT_LIMIT`, `VISUAL_CODER_COLOR_BUSY`, or `VISUAL_CODER_COLOR_UNKNOWN`, reply with that message and stop.
|
|
16
|
+
|
|
15
17
|
2. Read `VISUAL_CODER_SESSION` from the output. That id is the session to use for every later `inbase` command.
|
|
16
18
|
|
|
17
|
-
3. Continue the Inbase visual edits skill from `wait-for-blueprint` onward with that `--session` id.
|
|
19
|
+
3. Continue the Inbase visual edits skill from `wait-for-blueprint` onward with that `--session` id. Attach already started the session. `wait-for-blueprint` only reads the optional blueprint, instruction, and attached files; it does not wait.
|