@jkwd/inbase 0.1.3 → 0.1.5
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 +3 -1
- package/apps/explorer/scripts/open-editor.d.ts +6 -0
- package/apps/explorer/scripts/open-editor.mjs +219 -0
- package/apps/explorer/scripts/session-store.d.ts +51 -1
- package/apps/explorer/scripts/session-store.mjs +461 -48
- package/apps/explorer/src/App.tsx +178 -78
- package/apps/explorer/src/agentIntent.ts +54 -1
- package/apps/explorer/src/index.css +238 -2
- package/apps/explorer/src/layout.ts +46 -0
- package/apps/explorer/src/scene/FileBlock.tsx +110 -145
- package/apps/explorer/src/scene/FolderArea.tsx +17 -4
- package/apps/explorer/src/scene/MapSelectBorder.tsx +3 -1
- package/apps/explorer/src/scene/MapView.tsx +41 -18
- package/apps/explorer/src/scene/Player.tsx +27 -0
- package/apps/explorer/src/scene/RelationLines.tsx +36 -32
- package/apps/explorer/src/scene/SelectionController.tsx +21 -3
- package/apps/explorer/src/scene/SelectionThumbnail.tsx +451 -0
- package/apps/explorer/src/scene/World.tsx +19 -50
- package/apps/explorer/src/theme.ts +10 -1
- package/apps/explorer/src/types.ts +12 -0
- package/apps/explorer/src/ui/HUD.tsx +699 -390
- package/apps/explorer/vite.config.ts +72 -22
- package/bin/session.mjs +17 -3
- package/package.json +3 -1
- package/skill/inbase/SKILL.md +22 -14
|
@@ -7,10 +7,14 @@ import { fileURLToPath } from 'node:url'
|
|
|
7
7
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
8
8
|
import { emptyIntent } from './scripts/patch-lib.mjs'
|
|
9
9
|
import { dataDir, targetRoot } from './scripts/target-config.mjs'
|
|
10
|
+
import { editorFileUri, openInEditor } from './scripts/open-editor.mjs'
|
|
10
11
|
import {
|
|
11
12
|
answerBlueprint,
|
|
12
13
|
continueDiff,
|
|
14
|
+
discardInactiveDiffSessions,
|
|
15
|
+
inspectTargetFile,
|
|
13
16
|
invokeStep,
|
|
17
|
+
listSessionIntents,
|
|
14
18
|
readActiveSession,
|
|
15
19
|
requestReplan,
|
|
16
20
|
sendBlueprint,
|
|
@@ -43,6 +47,17 @@ function readBody(req: IncomingMessage) {
|
|
|
43
47
|
})
|
|
44
48
|
}
|
|
45
49
|
|
|
50
|
+
function rescanTarget(when: string) {
|
|
51
|
+
const scan = spawnSync(process.execPath, [scanScript], {
|
|
52
|
+
cwd: here,
|
|
53
|
+
encoding: 'utf8',
|
|
54
|
+
env: scanEnv,
|
|
55
|
+
})
|
|
56
|
+
if (scan.status !== 0) {
|
|
57
|
+
console.error(scan.stderr || scan.stdout || `scan failed ${when}`)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
46
61
|
function knownFileIds() {
|
|
47
62
|
try {
|
|
48
63
|
const graph = JSON.parse(fs.readFileSync(codebaseFile, 'utf8')) as {
|
|
@@ -66,6 +81,8 @@ function jsonFilePlugin(): Plugin {
|
|
|
66
81
|
return {
|
|
67
82
|
name: 'visual-coder-json-files',
|
|
68
83
|
configureServer(server) {
|
|
84
|
+
discardInactiveDiffSessions(dataDir, targetRoot)
|
|
85
|
+
rescanTarget('after discarding inactive sessions')
|
|
69
86
|
server.middlewares.use('/api/user-context', (req, res, next) => {
|
|
70
87
|
if (req.method === 'GET') {
|
|
71
88
|
sendJson(res, 200, readUserContext())
|
|
@@ -89,12 +106,22 @@ function jsonFilePlugin(): Plugin {
|
|
|
89
106
|
server.middlewares.use('/api/agent-intent', (req, res, next) => {
|
|
90
107
|
if (req.method === 'GET') {
|
|
91
108
|
const url = new URL(req.url ?? '/', 'http://visual-coder.local')
|
|
92
|
-
const sessionId = url.searchParams.get('sessionId')
|
|
109
|
+
const sessionId = url.searchParams.get('sessionId')
|
|
93
110
|
const diffId = url.searchParams.get('diffId') ?? undefined
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
111
|
+
if (sessionId) {
|
|
112
|
+
const intent = sessionIntent(
|
|
113
|
+
dataDir,
|
|
114
|
+
sessionId,
|
|
115
|
+
knownFileIds(),
|
|
116
|
+
diffId,
|
|
117
|
+
)
|
|
118
|
+
sendJson(res, 200, intent ?? { ...emptyIntent })
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
sendJson(res, 200, {
|
|
122
|
+
focusedSessionId: readActiveSession(dataDir),
|
|
123
|
+
intents: listSessionIntents(dataDir, knownFileIds()),
|
|
124
|
+
})
|
|
98
125
|
return
|
|
99
126
|
}
|
|
100
127
|
|
|
@@ -105,10 +132,46 @@ function jsonFilePlugin(): Plugin {
|
|
|
105
132
|
|
|
106
133
|
next()
|
|
107
134
|
})
|
|
135
|
+
|
|
136
|
+
server.middlewares.use('/api/inspect-file', (req, res, next) => {
|
|
137
|
+
if (req.method === 'POST') {
|
|
138
|
+
void inspectFile(req, res)
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
next()
|
|
142
|
+
})
|
|
108
143
|
},
|
|
109
144
|
}
|
|
110
145
|
}
|
|
111
146
|
|
|
147
|
+
async function inspectFile(req: IncomingMessage, res: ServerResponse) {
|
|
148
|
+
try {
|
|
149
|
+
const body = JSON.parse(await readBody(req)) as {
|
|
150
|
+
sessionId?: string
|
|
151
|
+
diffId?: string
|
|
152
|
+
fileId?: string
|
|
153
|
+
}
|
|
154
|
+
const filePath = inspectTargetFile(dataDir, targetRoot, {
|
|
155
|
+
sessionId: body.sessionId,
|
|
156
|
+
diffId: body.diffId,
|
|
157
|
+
fileId: body.fileId,
|
|
158
|
+
})
|
|
159
|
+
if (!filePath) {
|
|
160
|
+
sendJson(res, 200, { path: null, uri: null, opened: false })
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
const opened = openInEditor(filePath)
|
|
164
|
+
sendJson(res, 200, {
|
|
165
|
+
path: filePath,
|
|
166
|
+
uri: editorFileUri(filePath),
|
|
167
|
+
opened,
|
|
168
|
+
})
|
|
169
|
+
} catch (error) {
|
|
170
|
+
const message = error instanceof Error ? error.message : 'invalid request'
|
|
171
|
+
sendJson(res, 400, { error: message })
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
112
175
|
async function decideIntent(req: IncomingMessage, res: ServerResponse) {
|
|
113
176
|
try {
|
|
114
177
|
const body = JSON.parse(await readBody(req)) as {
|
|
@@ -155,28 +218,14 @@ async function decideIntent(req: IncomingMessage, res: ServerResponse) {
|
|
|
155
218
|
return
|
|
156
219
|
}
|
|
157
220
|
invokeStep(dataDir, body.sessionId, body.step as number, targetRoot)
|
|
158
|
-
|
|
159
|
-
cwd: here,
|
|
160
|
-
encoding: 'utf8',
|
|
161
|
-
env: scanEnv,
|
|
162
|
-
})
|
|
163
|
-
if (scan.status !== 0) {
|
|
164
|
-
console.error(scan.stderr || scan.stdout || 'scan failed after invoking step')
|
|
165
|
-
}
|
|
221
|
+
rescanTarget('after invoking step')
|
|
166
222
|
} else if (action === 'continue') {
|
|
167
223
|
if (!body.diffId) {
|
|
168
224
|
sendJson(res, 400, { error: 'diffId is required for continue' })
|
|
169
225
|
return
|
|
170
226
|
}
|
|
171
227
|
continueDiff(dataDir, targetRoot, body.sessionId, body.diffId)
|
|
172
|
-
|
|
173
|
-
cwd: here,
|
|
174
|
-
encoding: 'utf8',
|
|
175
|
-
env: scanEnv,
|
|
176
|
-
})
|
|
177
|
-
if (scan.status !== 0) {
|
|
178
|
-
console.error(scan.stderr || scan.stdout || 'scan failed after applying patch')
|
|
179
|
-
}
|
|
228
|
+
rescanTarget('after applying patch')
|
|
180
229
|
} else if (action === 'instruct') {
|
|
181
230
|
if (!body.diffId) {
|
|
182
231
|
sendJson(res, 400, { error: 'diffId is required for instruct' })
|
|
@@ -209,7 +258,8 @@ async function decideIntent(req: IncomingMessage, res: ServerResponse) {
|
|
|
209
258
|
addedImports: body.addedImports,
|
|
210
259
|
})
|
|
211
260
|
} else {
|
|
212
|
-
stopSession(dataDir, body.sessionId,
|
|
261
|
+
stopSession(dataDir, body.sessionId, targetRoot)
|
|
262
|
+
rescanTarget('after stopping session')
|
|
213
263
|
}
|
|
214
264
|
const next = sessionIntent(dataDir, body.sessionId, knownFileIds())
|
|
215
265
|
sendJson(res, 200, next ?? { ...emptyIntent })
|
package/bin/session.mjs
CHANGED
|
@@ -52,6 +52,12 @@ export async function waitForBlueprint(args) {
|
|
|
52
52
|
|
|
53
53
|
const started = Date.now()
|
|
54
54
|
const initial = store.readManifest(config.dataDir, sessionId)
|
|
55
|
+
if (store.isWorkflowStopped(config.dataDir, sessionId)) {
|
|
56
|
+
console.error(
|
|
57
|
+
'VISUAL_CODER_STOPPED The workflow was stopped. Do not modify project files.',
|
|
58
|
+
)
|
|
59
|
+
process.exit(2)
|
|
60
|
+
}
|
|
55
61
|
if (!initial) {
|
|
56
62
|
console.error(`No workflow session found for ${sessionId}`)
|
|
57
63
|
process.exit(1)
|
|
@@ -68,6 +74,7 @@ export async function waitForBlueprint(args) {
|
|
|
68
74
|
}
|
|
69
75
|
|
|
70
76
|
while (Date.now() - started < timeoutMs) {
|
|
77
|
+
store.touchSessionConnection(config.dataDir, sessionId)
|
|
71
78
|
const manifest = store.readManifest(config.dataDir, sessionId)
|
|
72
79
|
if (!manifest || manifest.phase === 'stopped') {
|
|
73
80
|
console.error(
|
|
@@ -81,7 +88,7 @@ export async function waitForBlueprint(args) {
|
|
|
81
88
|
const islands = blueprint.userCreatedIslands ?? []
|
|
82
89
|
console.log(
|
|
83
90
|
blueprint.enabled
|
|
84
|
-
? `VISUAL_CODER_BLUEPRINT_READY The user sent ${blocks.length} file(s) and ${islands.length} island(s) for this chat.
|
|
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.`
|
|
85
92
|
: 'VISUAL_CODER_BLUEPRINT_READY The user skipped the blueprint. Continue without user-placed files or islands.',
|
|
86
93
|
)
|
|
87
94
|
console.log('VISUAL_CODER_BLUEPRINT_START')
|
|
@@ -131,6 +138,12 @@ export async function waitForApproval(args) {
|
|
|
131
138
|
const started = Date.now()
|
|
132
139
|
const initial = store.readManifest(config.dataDir, sessionId)
|
|
133
140
|
const initialDiff = initial?.diffs?.at(-1)
|
|
141
|
+
if (store.isWorkflowStopped(config.dataDir, sessionId)) {
|
|
142
|
+
console.error(
|
|
143
|
+
'VISUAL_CODER_STOPPED The workflow was stopped. Do not modify project files.',
|
|
144
|
+
)
|
|
145
|
+
process.exit(2)
|
|
146
|
+
}
|
|
134
147
|
if (!initial) {
|
|
135
148
|
console.error(`No workflow session found for ${sessionId}`)
|
|
136
149
|
process.exit(1)
|
|
@@ -144,6 +157,7 @@ export async function waitForApproval(args) {
|
|
|
144
157
|
)
|
|
145
158
|
|
|
146
159
|
while (Date.now() - started < timeoutMs) {
|
|
160
|
+
store.touchSessionConnection(config.dataDir, sessionId)
|
|
147
161
|
const manifest = store.readManifest(config.dataDir, sessionId)
|
|
148
162
|
if (!manifest) {
|
|
149
163
|
console.error(
|
|
@@ -173,7 +187,7 @@ export async function waitForApproval(args) {
|
|
|
173
187
|
? `\nVISUAL_CODER_INSTRUCTION_START\n${manifest.pendingInstruction}\nVISUAL_CODER_INSTRUCTION_END`
|
|
174
188
|
: ''
|
|
175
189
|
console.log(
|
|
176
|
-
`VISUAL_CODER_REPLAN Keep accepted steps before step ${manifest.currentStep}. Replace the plan from step ${manifest.currentStep} onward using the instruction below. Report the revised tail with inbase report-plan, then wait for invocation.${instruction}`,
|
|
190
|
+
`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}`,
|
|
177
191
|
)
|
|
178
192
|
process.exit(4)
|
|
179
193
|
}
|
|
@@ -201,7 +215,7 @@ export async function proposePatch(args) {
|
|
|
201
215
|
|
|
202
216
|
if (clear) {
|
|
203
217
|
if (!sessionId) usage('propose-patch', '--session <cursor-chat-id> --clear')
|
|
204
|
-
store.stopSession(config.dataDir, sessionId)
|
|
218
|
+
store.stopSession(config.dataDir, sessionId, config.targetRoot)
|
|
205
219
|
console.log(
|
|
206
220
|
`Cleared session ${sessionId}; stored diffs and blueprint drafts were removed.`,
|
|
207
221
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jkwd/inbase",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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",
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
"apps/explorer/src/scene",
|
|
30
30
|
"apps/explorer/src/ui",
|
|
31
31
|
"apps/explorer/scripts/js-source.mjs",
|
|
32
|
+
"apps/explorer/scripts/open-editor.d.ts",
|
|
33
|
+
"apps/explorer/scripts/open-editor.mjs",
|
|
32
34
|
"apps/explorer/scripts/patch-lib.d.ts",
|
|
33
35
|
"apps/explorer/scripts/patch-lib.mjs",
|
|
34
36
|
"apps/explorer/scripts/scan-target.mjs",
|
package/skill/inbase/SKILL.md
CHANGED
|
@@ -48,12 +48,17 @@ npx inbase wait-for-blueprint --session "<current-cursor-chat-id>"
|
|
|
48
48
|
3. Read the handshake output between `VISUAL_CODER_BLUEPRINT_START` and
|
|
49
49
|
`VISUAL_CODER_BLUEPRINT_END`, or read
|
|
50
50
|
`.inbase/diff-sessions/<session-id>/blueprint.json`.
|
|
51
|
-
If `enabled` is true,
|
|
52
|
-
`userCreatedIslands`
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
If `enabled` is true, **the blueprint is leading**. Treat
|
|
52
|
+
`userCreatedBlocks`, `userCreatedIslands`, `addedFunctions`,
|
|
53
|
+
`addedVariables`, and `addedImports` as the source of truth for this chat.
|
|
54
|
+
Create those paths and add those symbols even if they are not on disk.
|
|
55
|
+
They belong to this chat only.
|
|
56
|
+
Do not omit, rename, relocate, or replace a blueprint file, island, symbol,
|
|
57
|
+
or import. Extra edits to existing files are allowed when needed to finish
|
|
58
|
+
the feature. Extra new files that are not in the blueprint are a deviation.
|
|
59
|
+
If the user request, viewpoint, a later instruction, or your own plan would
|
|
60
|
+
differ from the blueprint, **stop and ask the user in chat** before
|
|
61
|
+
reporting the plan. Do not silently deviate.
|
|
57
62
|
4. Read `.inbase/user-context.json` for viewpoint only.
|
|
58
63
|
5. Use the user's viewpoint only when `followLook` is true:
|
|
59
64
|
- `island` is where they are standing
|
|
@@ -62,8 +67,8 @@ npx inbase wait-for-blueprint --session "<current-cursor-chat-id>"
|
|
|
62
67
|
- `filesOnIsland` is the rest of that folder
|
|
63
68
|
Prefer those files while `followLook` is true, unless the request clearly
|
|
64
69
|
needs something else. If `followLook` is false or missing, ignore viewpoint
|
|
65
|
-
and choose files from the request itself.
|
|
66
|
-
blueprint
|
|
70
|
+
and choose files from the request itself. Viewpoint never overrides the
|
|
71
|
+
blueprint: still follow this session's blueprint when `enabled` is true.
|
|
67
72
|
6. List **all** steps needed to finish the feature. Keep steps small enough that
|
|
68
73
|
one patch is one landscape change (usually one new file, or a few related
|
|
69
74
|
edits).
|
|
@@ -136,10 +141,12 @@ npx inbase wait-for-approval --session "<current-cursor-chat-id>"
|
|
|
136
141
|
an earlier diff. Follow the text between
|
|
137
142
|
`VISUAL_CODER_INSTRUCTION_START` and `VISUAL_CODER_INSTRUCTION_END`, read
|
|
138
143
|
this session's `blueprint.json` when it is enabled (files, islands,
|
|
139
|
-
`addedFunctions`, `addedVariables`, `addedImports`)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
144
|
+
`addedFunctions`, `addedVariables`, `addedImports`). The blueprint stays
|
|
145
|
+
leading. If the new instruction would differ from it, ask the user before
|
|
146
|
+
replacing the plan. Read `user-context.json` (follow the viewpoint only if
|
|
147
|
+
`followLook` is true), replace the plan from the current step onward using
|
|
148
|
+
`inbase report-plan`, then wait for the user to invoke the first revised
|
|
149
|
+
step.
|
|
143
150
|
- Exit `2` (`VISUAL_CODER_STOPPED`) or `3` (timeout): make no further
|
|
144
151
|
project changes.
|
|
145
152
|
|
|
@@ -154,8 +161,9 @@ npx inbase propose-patch --session "<current-cursor-chat-id>" --clear
|
|
|
154
161
|
|
|
155
162
|
- Skip `inbase start-session` once this skill applies
|
|
156
163
|
- Skip `inbase wait-for-blueprint` or report a plan before `VISUAL_CODER_BLUEPRINT_READY`
|
|
157
|
-
-
|
|
158
|
-
- Skip
|
|
164
|
+
- Treat the chat request, viewpoint, or your own plan as overriding an enabled blueprint
|
|
165
|
+
- Skip, rename, relocate, or replace this session's `blueprint.json` files, islands, functions, variables, or imports when `enabled` is true
|
|
166
|
+
- Silently differ from the blueprint; ask the user first
|
|
159
167
|
- Read global `user-context.json` for placed files; those live on the session blueprint
|
|
160
168
|
- Follow the user's look when `followLook` is false
|
|
161
169
|
- Write, edit, create, or delete project files directly
|