@jkwd/inbase 0.1.4 → 0.1.6
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 +6 -1
- package/apps/explorer/scripts/patch-lib.d.ts +13 -0
- package/apps/explorer/scripts/patch-lib.mjs +162 -25
- package/apps/explorer/scripts/session-store.d.ts +37 -0
- package/apps/explorer/scripts/session-store.mjs +415 -53
- package/apps/explorer/src/App.tsx +307 -88
- package/apps/explorer/src/agentIntent.ts +42 -1
- package/apps/explorer/src/index.css +312 -5
- package/apps/explorer/src/layout.ts +111 -1
- package/apps/explorer/src/scene/Bridge.tsx +23 -5
- package/apps/explorer/src/scene/FileBlock.tsx +113 -144
- package/apps/explorer/src/scene/FolderArea.tsx +35 -16
- package/apps/explorer/src/scene/MapSelectBorder.tsx +3 -1
- package/apps/explorer/src/scene/MapView.tsx +77 -22
- package/apps/explorer/src/scene/Player.tsx +36 -1
- package/apps/explorer/src/scene/RelationLines.tsx +36 -32
- package/apps/explorer/src/scene/SelectionController.tsx +57 -16
- package/apps/explorer/src/scene/SelectionThumbnail.tsx +895 -0
- package/apps/explorer/src/scene/World.tsx +42 -44
- package/apps/explorer/src/theme.ts +17 -4
- package/apps/explorer/src/types.ts +17 -0
- package/apps/explorer/src/ui/HUD.tsx +946 -385
- package/apps/explorer/vite.config.ts +44 -22
- package/bin/session.mjs +34 -8
- package/package.json +1 -1
- package/skill/inbase/SKILL.md +35 -22
package/README.md
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/inbase-logo-white.png" />
|
|
4
|
+
<img src="docs/inbase-logo.png" alt="InBase — Dive into your codebase" width="520" />
|
|
5
|
+
</picture>
|
|
6
|
+
</p>
|
|
2
7
|
|
|
3
8
|
A first-person 3D map of a JavaScript or TypeScript codebase. Files become blocks, folders become walkable areas, and imports become lines in the air.
|
|
4
9
|
|
|
@@ -46,14 +46,23 @@ export function extractPatchAdditions(
|
|
|
46
46
|
addedFunctions: PatchSymbolAddition[]
|
|
47
47
|
addedVariables: PatchSymbolAddition[]
|
|
48
48
|
addedImports: PatchImportAddition[]
|
|
49
|
+
changedFunctions: PatchSymbolAddition[]
|
|
50
|
+
changedVariables: PatchSymbolAddition[]
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
export function accumulatePatchAdditions(patches?: string[]): {
|
|
52
54
|
addedFunctions: PatchSymbolAddition[]
|
|
53
55
|
addedVariables: PatchSymbolAddition[]
|
|
54
56
|
addedImports: PatchImportAddition[]
|
|
57
|
+
changedFunctions: PatchSymbolAddition[]
|
|
58
|
+
changedVariables: PatchSymbolAddition[]
|
|
55
59
|
}
|
|
56
60
|
|
|
61
|
+
export function applyUnifiedPatchToContents(
|
|
62
|
+
files: Map<string, string>,
|
|
63
|
+
patch: string,
|
|
64
|
+
): ReturnType<typeof parseUnifiedPatch>
|
|
65
|
+
|
|
57
66
|
export function applyUnifiedPatch(
|
|
58
67
|
patch: string,
|
|
59
68
|
targetRoot: string,
|
|
@@ -74,6 +83,7 @@ export const emptyIntent: {
|
|
|
74
83
|
feature: null
|
|
75
84
|
steps: unknown[]
|
|
76
85
|
step: null
|
|
86
|
+
stepByStep: boolean
|
|
77
87
|
files: string[]
|
|
78
88
|
creates: string[]
|
|
79
89
|
deletes: string[]
|
|
@@ -83,6 +93,8 @@ export const emptyIntent: {
|
|
|
83
93
|
addedFunctions: PatchSymbolAddition[]
|
|
84
94
|
addedVariables: PatchSymbolAddition[]
|
|
85
95
|
addedImports: PatchImportAddition[]
|
|
96
|
+
changedFunctions: PatchSymbolAddition[]
|
|
97
|
+
changedVariables: PatchSymbolAddition[]
|
|
86
98
|
reason: null
|
|
87
99
|
sessionId: null
|
|
88
100
|
diffId: null
|
|
@@ -93,6 +105,7 @@ export const emptyIntent: {
|
|
|
93
105
|
preview: boolean
|
|
94
106
|
phase: null
|
|
95
107
|
working: boolean
|
|
108
|
+
stalledWait: boolean
|
|
96
109
|
creationMode: boolean
|
|
97
110
|
canEnterBlueprint: boolean
|
|
98
111
|
blueprintSessionId: string | null
|
|
@@ -39,7 +39,7 @@ function parseHunks(section) {
|
|
|
39
39
|
let current = null
|
|
40
40
|
|
|
41
41
|
for (const line of lines) {
|
|
42
|
-
const header = line.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))?
|
|
42
|
+
const header = line.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)$/)
|
|
43
43
|
if (header) {
|
|
44
44
|
if (current) hunks.push(current)
|
|
45
45
|
current = {
|
|
@@ -47,6 +47,7 @@ function parseHunks(section) {
|
|
|
47
47
|
oldCount: Number(header[2] ?? '1'),
|
|
48
48
|
newStart: Number(header[3]),
|
|
49
49
|
newCount: Number(header[4] ?? '1'),
|
|
50
|
+
headerContext: (header[5] ?? '').trim(),
|
|
50
51
|
lines: [],
|
|
51
52
|
}
|
|
52
53
|
continue
|
|
@@ -182,30 +183,107 @@ function emptyAdditions() {
|
|
|
182
183
|
addedFunctions: [],
|
|
183
184
|
addedVariables: [],
|
|
184
185
|
addedImports: [],
|
|
186
|
+
changedFunctions: [],
|
|
187
|
+
changedVariables: [],
|
|
185
188
|
}
|
|
186
189
|
}
|
|
187
190
|
|
|
191
|
+
function isImportLine(line) {
|
|
192
|
+
return /^\s*import\b/.test(line)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function isTopLevelSymbolLine(line) {
|
|
196
|
+
return extractSymbols(line).length > 0 && !/^\s/.test(line)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function enclosingChangedSymbols(entry) {
|
|
200
|
+
if (entry.kind === 'add') return []
|
|
201
|
+
const found = []
|
|
202
|
+
const seen = new Set()
|
|
203
|
+
|
|
204
|
+
for (const hunk of entry.hunks) {
|
|
205
|
+
const contextLines = []
|
|
206
|
+
let addedTopLevel = false
|
|
207
|
+
let hasBodyEdit = false
|
|
208
|
+
for (const line of hunk.lines) {
|
|
209
|
+
const tag = line[0]
|
|
210
|
+
const body = line.slice(1)
|
|
211
|
+
if (tag === ' ') contextLines.push(body)
|
|
212
|
+
if (tag !== '+' && tag !== '-') continue
|
|
213
|
+
if (tag === '+' && isTopLevelSymbolLine(body)) addedTopLevel = true
|
|
214
|
+
if (
|
|
215
|
+
!addedTopLevel &&
|
|
216
|
+
extractSymbols(body).length === 0 &&
|
|
217
|
+
!isImportLine(body) &&
|
|
218
|
+
body.trim()
|
|
219
|
+
) {
|
|
220
|
+
hasBodyEdit = true
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (!hasBodyEdit) continue
|
|
224
|
+
for (const symbol of [
|
|
225
|
+
...extractSymbols(hunk.headerContext ?? ''),
|
|
226
|
+
...extractSymbols(contextLines.join('\n')),
|
|
227
|
+
]) {
|
|
228
|
+
const key = `${symbol.kind}:${symbol.name}`
|
|
229
|
+
if (seen.has(key)) continue
|
|
230
|
+
seen.add(key)
|
|
231
|
+
found.push(symbol)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return found
|
|
236
|
+
}
|
|
237
|
+
|
|
188
238
|
function applyEntriesToAdditions(current, entries) {
|
|
189
239
|
const functions = new Map(
|
|
190
|
-
current.addedFunctions.map((item) => [additionKey(item.file, item.name), item]),
|
|
240
|
+
(current.addedFunctions ?? []).map((item) => [additionKey(item.file, item.name), item]),
|
|
191
241
|
)
|
|
192
242
|
const variables = new Map(
|
|
193
|
-
current.addedVariables.map((item) => [additionKey(item.file, item.name), item]),
|
|
243
|
+
(current.addedVariables ?? []).map((item) => [additionKey(item.file, item.name), item]),
|
|
244
|
+
)
|
|
245
|
+
const changedFunctions = new Map(
|
|
246
|
+
(current.changedFunctions ?? []).map((item) => [
|
|
247
|
+
additionKey(item.file, item.name),
|
|
248
|
+
item,
|
|
249
|
+
]),
|
|
250
|
+
)
|
|
251
|
+
const changedVariables = new Map(
|
|
252
|
+
(current.changedVariables ?? []).map((item) => [
|
|
253
|
+
additionKey(item.file, item.name),
|
|
254
|
+
item,
|
|
255
|
+
]),
|
|
194
256
|
)
|
|
195
257
|
const imports = new Map(
|
|
196
|
-
current.addedImports.map((item) => [
|
|
258
|
+
(current.addedImports ?? []).map((item) => [
|
|
259
|
+
additionKey(item.file, item.name, item.from),
|
|
260
|
+
item,
|
|
261
|
+
]),
|
|
197
262
|
)
|
|
198
263
|
|
|
199
|
-
const
|
|
200
|
-
for (const key of [...
|
|
201
|
-
if (
|
|
264
|
+
const dropFrom = (bucket, fileId) => {
|
|
265
|
+
for (const key of [...bucket.keys()]) {
|
|
266
|
+
if (bucket.get(key).file === fileId) bucket.delete(key)
|
|
202
267
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const dropFile = (fileId) => {
|
|
271
|
+
dropFrom(functions, fileId)
|
|
272
|
+
dropFrom(variables, fileId)
|
|
273
|
+
dropFrom(changedFunctions, fileId)
|
|
274
|
+
dropFrom(changedVariables, fileId)
|
|
275
|
+
dropFrom(imports, fileId)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const markChanged = (kind, file, name) => {
|
|
279
|
+
const key = additionKey(file, name)
|
|
280
|
+
if (kind === 'function') {
|
|
281
|
+
if (functions.has(key)) return
|
|
282
|
+
changedFunctions.set(key, { name, file })
|
|
283
|
+
return
|
|
208
284
|
}
|
|
285
|
+
if (variables.has(key)) return
|
|
286
|
+
changedVariables.set(key, { name, file })
|
|
209
287
|
}
|
|
210
288
|
|
|
211
289
|
for (const entry of entries) {
|
|
@@ -224,15 +302,36 @@ function applyEntriesToAdditions(current, entries) {
|
|
|
224
302
|
)
|
|
225
303
|
|
|
226
304
|
for (const symbol of removedSymbols) {
|
|
227
|
-
|
|
228
|
-
if (symbol.kind
|
|
229
|
-
|
|
305
|
+
const key = additionKey(entry.id, symbol.name)
|
|
306
|
+
if (keptSymbolKeys.has(`${symbol.kind}:${symbol.name}`)) {
|
|
307
|
+
markChanged(symbol.kind, entry.id, symbol.name)
|
|
308
|
+
continue
|
|
309
|
+
}
|
|
310
|
+
if (symbol.kind === 'function') {
|
|
311
|
+
functions.delete(key)
|
|
312
|
+
changedFunctions.delete(key)
|
|
313
|
+
} else {
|
|
314
|
+
variables.delete(key)
|
|
315
|
+
changedVariables.delete(key)
|
|
316
|
+
}
|
|
230
317
|
}
|
|
231
318
|
for (const symbol of addedSymbols) {
|
|
232
|
-
if (previousSymbolKeys.has(`${symbol.kind}:${symbol.name}`))
|
|
319
|
+
if (previousSymbolKeys.has(`${symbol.kind}:${symbol.name}`)) {
|
|
320
|
+
markChanged(symbol.kind, entry.id, symbol.name)
|
|
321
|
+
continue
|
|
322
|
+
}
|
|
233
323
|
const item = { name: symbol.name, file: entry.id }
|
|
234
|
-
|
|
235
|
-
|
|
324
|
+
const key = additionKey(entry.id, symbol.name)
|
|
325
|
+
if (symbol.kind === 'function') {
|
|
326
|
+
changedFunctions.delete(key)
|
|
327
|
+
functions.set(key, item)
|
|
328
|
+
} else {
|
|
329
|
+
changedVariables.delete(key)
|
|
330
|
+
variables.set(key, item)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
for (const symbol of enclosingChangedSymbols(entry)) {
|
|
334
|
+
markChanged(symbol.kind, entry.id, symbol.name)
|
|
236
335
|
}
|
|
237
336
|
|
|
238
337
|
const removedBindings = extractImportBindings(removedSource(entry))
|
|
@@ -262,6 +361,8 @@ function applyEntriesToAdditions(current, entries) {
|
|
|
262
361
|
addedFunctions: [...functions.values()],
|
|
263
362
|
addedVariables: [...variables.values()],
|
|
264
363
|
addedImports: [...imports.values()],
|
|
364
|
+
changedFunctions: [...changedFunctions.values()],
|
|
365
|
+
changedVariables: [...changedVariables.values()],
|
|
265
366
|
}
|
|
266
367
|
}
|
|
267
368
|
|
|
@@ -345,30 +446,58 @@ function applyHunks(original, hunks) {
|
|
|
345
446
|
return `${lines.join('\n')}\n`
|
|
346
447
|
}
|
|
347
448
|
|
|
348
|
-
export function
|
|
449
|
+
export function applyUnifiedPatchToContents(files, patch) {
|
|
349
450
|
const parsed = parseUnifiedPatch(patch)
|
|
350
451
|
if (parsed.entries.length === 0) {
|
|
351
452
|
throw new Error('Patch did not contain any file changes')
|
|
352
453
|
}
|
|
353
454
|
|
|
354
455
|
for (const file of parsed.entries) {
|
|
355
|
-
const absolute = path.join(targetRoot, file.id)
|
|
356
456
|
if (file.kind === 'delete') {
|
|
357
|
-
|
|
457
|
+
files.delete(file.id)
|
|
358
458
|
continue
|
|
359
459
|
}
|
|
360
460
|
|
|
361
461
|
const original =
|
|
362
|
-
file.kind === 'add' || !
|
|
363
|
-
? ''
|
|
364
|
-
: fs.readFileSync(absolute, 'utf8')
|
|
462
|
+
file.kind === 'add' || !files.has(file.id) ? '' : files.get(file.id)
|
|
365
463
|
|
|
366
464
|
if (file.kind === 'modify' && original === '') {
|
|
367
465
|
throw new Error(`Cannot modify missing file ${file.id}`)
|
|
368
466
|
}
|
|
369
467
|
|
|
468
|
+
files.set(file.id, applyHunks(original, file.hunks))
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return parsed
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export function applyUnifiedPatch(patch, targetRoot) {
|
|
475
|
+
const parsed = parseUnifiedPatch(patch)
|
|
476
|
+
if (parsed.entries.length === 0) {
|
|
477
|
+
throw new Error('Patch did not contain any file changes')
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const files = new Map()
|
|
481
|
+
for (const file of parsed.entries) {
|
|
482
|
+
const absolute = path.join(targetRoot, file.id)
|
|
483
|
+
if (
|
|
484
|
+
file.kind !== 'add' &&
|
|
485
|
+
fs.existsSync(absolute) &&
|
|
486
|
+
fs.statSync(absolute).isFile()
|
|
487
|
+
) {
|
|
488
|
+
files.set(file.id, fs.readFileSync(absolute, 'utf8'))
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
applyUnifiedPatchToContents(files, patch)
|
|
492
|
+
|
|
493
|
+
for (const file of parsed.entries) {
|
|
494
|
+
const absolute = path.join(targetRoot, file.id)
|
|
495
|
+
if (file.kind === 'delete') {
|
|
496
|
+
if (fs.existsSync(absolute)) fs.rmSync(absolute)
|
|
497
|
+
continue
|
|
498
|
+
}
|
|
370
499
|
fs.mkdirSync(path.dirname(absolute), { recursive: true })
|
|
371
|
-
fs.writeFileSync(absolute,
|
|
500
|
+
fs.writeFileSync(absolute, files.get(file.id))
|
|
372
501
|
}
|
|
373
502
|
|
|
374
503
|
return parsed
|
|
@@ -381,6 +510,7 @@ export const emptyIntent = {
|
|
|
381
510
|
feature: null,
|
|
382
511
|
steps: [],
|
|
383
512
|
step: null,
|
|
513
|
+
stepByStep: true,
|
|
384
514
|
files: [],
|
|
385
515
|
creates: [],
|
|
386
516
|
deletes: [],
|
|
@@ -390,6 +520,8 @@ export const emptyIntent = {
|
|
|
390
520
|
addedFunctions: [],
|
|
391
521
|
addedVariables: [],
|
|
392
522
|
addedImports: [],
|
|
523
|
+
changedFunctions: [],
|
|
524
|
+
changedVariables: [],
|
|
393
525
|
reason: null,
|
|
394
526
|
sessionId: null,
|
|
395
527
|
diffId: null,
|
|
@@ -400,6 +532,7 @@ export const emptyIntent = {
|
|
|
400
532
|
preview: false,
|
|
401
533
|
phase: null,
|
|
402
534
|
working: false,
|
|
535
|
+
stalledWait: false,
|
|
403
536
|
creationMode: false,
|
|
404
537
|
canEnterBlueprint: false,
|
|
405
538
|
blueprintSessionId: null,
|
|
@@ -435,6 +568,8 @@ export function overlayPatch(intent, patchText, knownFileIds = []) {
|
|
|
435
568
|
addedFunctions: [],
|
|
436
569
|
addedVariables: [],
|
|
437
570
|
addedImports: [],
|
|
571
|
+
changedFunctions: [],
|
|
572
|
+
changedVariables: [],
|
|
438
573
|
}
|
|
439
574
|
}
|
|
440
575
|
|
|
@@ -451,6 +586,8 @@ export function overlayPatch(intent, patchText, knownFileIds = []) {
|
|
|
451
586
|
addedFunctions: base.addedFunctions ?? [],
|
|
452
587
|
addedVariables: base.addedVariables ?? [],
|
|
453
588
|
addedImports: base.addedImports ?? [],
|
|
589
|
+
changedFunctions: base.changedFunctions ?? [],
|
|
590
|
+
changedVariables: base.changedVariables ?? [],
|
|
454
591
|
}
|
|
455
592
|
}
|
|
456
593
|
|
|
@@ -37,6 +37,7 @@ export type DiffManifest = {
|
|
|
37
37
|
activeDiffId: string | null
|
|
38
38
|
pendingInstruction: string | null
|
|
39
39
|
workStartedAt: string | null
|
|
40
|
+
stepByStep: boolean
|
|
40
41
|
createdAt: string
|
|
41
42
|
updatedAt: string
|
|
42
43
|
diffs: DiffEntry[]
|
|
@@ -45,10 +46,33 @@ export type DiffManifest = {
|
|
|
45
46
|
export function assertSessionId(value: unknown): string
|
|
46
47
|
export function readActiveSession(dataDir: string): string | null
|
|
47
48
|
export function writeActiveSession(dataDir: string, sessionId: string | null): void
|
|
49
|
+
export function touchSessionConnection(dataDir: string, sessionId: string): void
|
|
50
|
+
export function isSessionConnected(
|
|
51
|
+
dataDir: string,
|
|
52
|
+
sessionId: string,
|
|
53
|
+
waiterIds?: Set<string>,
|
|
54
|
+
): boolean
|
|
55
|
+
export function listStoredSessionIds(dataDir: string): string[]
|
|
56
|
+
export function listOpenSessionIds(
|
|
57
|
+
dataDir: string,
|
|
58
|
+
waiterIds?: Set<string>,
|
|
59
|
+
): string[]
|
|
60
|
+
export function discardInactiveDiffSessions(
|
|
61
|
+
dataDir: string,
|
|
62
|
+
targetRoot?: string | null,
|
|
63
|
+
waiterIds?: Iterable<string>,
|
|
64
|
+
): string[]
|
|
65
|
+
export function listSessionIntents(
|
|
66
|
+
dataDir: string,
|
|
67
|
+
knownFileIds?: string[],
|
|
68
|
+
): Array<Record<string, unknown>>
|
|
48
69
|
export function readBlueprintSession(dataDir: string): string | null
|
|
49
70
|
export function writeBlueprintSession(dataDir: string, sessionId: string | null): void
|
|
50
71
|
export function readManifest(dataDir: string, sessionId: string): DiffManifest | null
|
|
51
72
|
export function writeManifest(dataDir: string, manifest: DiffManifest): void
|
|
73
|
+
export function isSessionStopped(dataDir: string, sessionId: string): boolean
|
|
74
|
+
export function isWorkflowStopped(dataDir: string, sessionId: string): boolean
|
|
75
|
+
export function sessionStoppedError(sessionId: string): Error
|
|
52
76
|
export function startSession(
|
|
53
77
|
dataDir: string,
|
|
54
78
|
input: {
|
|
@@ -107,6 +131,18 @@ export function reportPlan(
|
|
|
107
131
|
stepTitles: string[]
|
|
108
132
|
},
|
|
109
133
|
): DiffManifest
|
|
134
|
+
export function isStepByStep(manifest: DiffManifest | null | undefined): boolean
|
|
135
|
+
export function autoAdvance(
|
|
136
|
+
dataDir: string,
|
|
137
|
+
sessionId: string,
|
|
138
|
+
targetRoot?: string | null,
|
|
139
|
+
): DiffManifest | null
|
|
140
|
+
export function setStepByStep(
|
|
141
|
+
dataDir: string,
|
|
142
|
+
sessionId: string,
|
|
143
|
+
enabled: boolean,
|
|
144
|
+
targetRoot?: string | null,
|
|
145
|
+
): DiffManifest
|
|
110
146
|
export function invokeStep(
|
|
111
147
|
dataDir: string,
|
|
112
148
|
sessionId: string,
|
|
@@ -118,6 +154,7 @@ export function sessionIntent(
|
|
|
118
154
|
sessionId: string,
|
|
119
155
|
knownFileIds?: string[],
|
|
120
156
|
selectedDiffId?: string,
|
|
157
|
+
waiterIds?: Set<string>,
|
|
121
158
|
): Record<string, unknown> | null
|
|
122
159
|
export function resolveTargetFile(
|
|
123
160
|
targetRoot: string,
|