@jkwd/inbase 0.1.0
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 +76 -0
- package/apps/explorer/index.html +12 -0
- package/apps/explorer/package.json +28 -0
- package/apps/explorer/scripts/js-source.mjs +188 -0
- package/apps/explorer/scripts/patch-lib.d.ts +115 -0
- package/apps/explorer/scripts/patch-lib.mjs +472 -0
- package/apps/explorer/scripts/scan-target.mjs +188 -0
- package/apps/explorer/scripts/session-store.d.ts +156 -0
- package/apps/explorer/scripts/session-store.mjs +809 -0
- package/apps/explorer/scripts/target-config.d.ts +8 -0
- package/apps/explorer/scripts/target-config.mjs +42 -0
- package/apps/explorer/src/App.tsx +941 -0
- package/apps/explorer/src/agentIntent.ts +182 -0
- package/apps/explorer/src/codebase.ts +15 -0
- package/apps/explorer/src/index.css +632 -0
- package/apps/explorer/src/layout.ts +508 -0
- package/apps/explorer/src/main.tsx +16 -0
- package/apps/explorer/src/scene/BlockPlacer.tsx +87 -0
- package/apps/explorer/src/scene/Bridge.tsx +290 -0
- package/apps/explorer/src/scene/FileBlock.tsx +256 -0
- package/apps/explorer/src/scene/FolderArea.tsx +96 -0
- package/apps/explorer/src/scene/IslandPlacer.tsx +40 -0
- package/apps/explorer/src/scene/MapSelectBorder.tsx +57 -0
- package/apps/explorer/src/scene/MapView.tsx +247 -0
- package/apps/explorer/src/scene/Player.tsx +245 -0
- package/apps/explorer/src/scene/RelationLines.tsx +223 -0
- package/apps/explorer/src/scene/SelectionController.tsx +89 -0
- package/apps/explorer/src/scene/UserContextTracker.tsx +152 -0
- package/apps/explorer/src/scene/World.tsx +323 -0
- package/apps/explorer/src/theme.ts +111 -0
- package/apps/explorer/src/types.ts +245 -0
- package/apps/explorer/src/ui/CanvasErrorBoundary.tsx +38 -0
- package/apps/explorer/src/ui/HUD.tsx +1090 -0
- package/apps/explorer/src/ui/NameInput.tsx +45 -0
- package/apps/explorer/src/userContext.ts +73 -0
- package/apps/explorer/src/userCreated.ts +354 -0
- package/apps/explorer/src/vite-env.d.ts +1 -0
- package/apps/explorer/tsconfig.json +21 -0
- package/apps/explorer/vite.config.ts +295 -0
- package/bin/inbase.mjs +170 -0
- package/bin/project.mjs +94 -0
- package/bin/session.mjs +241 -0
- package/package.json +63 -0
- package/skill/inbase/SKILL.md +167 -0
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Inbase
|
|
2
|
+
|
|
3
|
+
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
|
+
|
|
5
|
+
Install the package in a project, run `inbase init`, then `inbase run`. Cursor uses the installed skill so code changes go through the visual map.
|
|
6
|
+
|
|
7
|
+
The npm package is `@jkwd/inbase` (npm blocks the unscoped name `inbase`). The command is still `inbase`.
|
|
8
|
+
|
|
9
|
+
## Use in a project
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -D @jkwd/inbase
|
|
13
|
+
npx inbase init
|
|
14
|
+
npx inbase run
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or install globally:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g @jkwd/inbase
|
|
21
|
+
inbase init
|
|
22
|
+
inbase run
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Open the printed URL (http://localhost:5173 by default), click the scene, then walk with WASD.
|
|
26
|
+
|
|
27
|
+
`inbase run` maps the current directory. To map another folder:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
inbase run --target /path/to/your/project
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`inbase init` copies a Cursor skill into `.cursor/skills/inbase/` and gitignores `.inbase/`. After that, ask Cursor to change source files in this repo — it should follow the visual plan/patch workflow while `inbase run` is up.
|
|
34
|
+
|
|
35
|
+
## Commands
|
|
36
|
+
|
|
37
|
+
| Command | What it does |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| `inbase init` | Install the Cursor skill in this repo |
|
|
40
|
+
| `inbase run` | Scan this repo and start the local map |
|
|
41
|
+
| `inbase run --port 5174` | Start on another port |
|
|
42
|
+
|
|
43
|
+
Session commands (`start-session`, `wait-for-blueprint`, `report-plan`, `wait-for-approval`, `propose-patch`) are used by the Cursor skill. You do not need to run them yourself.
|
|
44
|
+
|
|
45
|
+
## Language support
|
|
46
|
+
|
|
47
|
+
Source files (`.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`) become blocks with **functions**, **classes**, and **variables**, plus import edges from `import` and `require()`. Styles and docs (`.css`, `.scss`, `.html`, `.json`, `.md`) show up as blocks without symbols. Only relative specifiers (`./`, `../`) become edges — package names like `react` or `@angular/core` do not.
|
|
48
|
+
|
|
49
|
+
## How to read the map
|
|
50
|
+
|
|
51
|
+
- Each **file** is a block. Taller blocks have more lines of code.
|
|
52
|
+
- Small cubes on a block are **functions** (blue) and **variables** (tan).
|
|
53
|
+
- Each **folder** is a floor area with a center walkway.
|
|
54
|
+
- At the far end of an area, **bridges** lead into child folders. The folder name hangs above the bridge.
|
|
55
|
+
- Click a block to see **import relations**. Connected files stay lit and arcs draw to them.
|
|
56
|
+
|
|
57
|
+
## Develop Inbase itself
|
|
58
|
+
|
|
59
|
+
This repository is a workspace. The bundled demo target is `apps/example-target`.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install
|
|
63
|
+
npm run dev
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
That still maps `apps/example-target` by default. To map a different project without the CLI:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
VISUAL_CODER_TARGET=/path/to/your/project npm run dev
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
To run the example React app itself:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm run dev:target
|
|
76
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Inbase</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@visual-coder/explorer",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"scan": "node scripts/scan-target.mjs",
|
|
8
|
+
"dev": "node scripts/scan-target.mjs && vite",
|
|
9
|
+
"build": "node scripts/scan-target.mjs && tsc --noEmit && vite build",
|
|
10
|
+
"test": "node --test scripts/*.test.mjs",
|
|
11
|
+
"preview": "vite preview"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@react-three/drei": "^9.117.3",
|
|
15
|
+
"@react-three/fiber": "^8.17.10",
|
|
16
|
+
"react": "^18.3.1",
|
|
17
|
+
"react-dom": "^18.3.1",
|
|
18
|
+
"three": "^0.170.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/react": "^18.3.12",
|
|
22
|
+
"@types/react-dom": "^18.3.1",
|
|
23
|
+
"@types/three": "^0.170.0",
|
|
24
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
25
|
+
"typescript": "^5.6.3",
|
|
26
|
+
"vite": "^6.0.3"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
export const FILE_EXTENSIONS = new Set([
|
|
2
|
+
'.ts',
|
|
3
|
+
'.tsx',
|
|
4
|
+
'.js',
|
|
5
|
+
'.jsx',
|
|
6
|
+
'.mjs',
|
|
7
|
+
'.cjs',
|
|
8
|
+
'.css',
|
|
9
|
+
'.scss',
|
|
10
|
+
'.json',
|
|
11
|
+
'.html',
|
|
12
|
+
'.md',
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
export const SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'])
|
|
16
|
+
|
|
17
|
+
export const RESOLVE_EXTENSIONS = [
|
|
18
|
+
'.tsx',
|
|
19
|
+
'.ts',
|
|
20
|
+
'.jsx',
|
|
21
|
+
'.js',
|
|
22
|
+
'.mjs',
|
|
23
|
+
'.cjs',
|
|
24
|
+
'.css',
|
|
25
|
+
'.scss',
|
|
26
|
+
'.json',
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
export function extractJsSymbols(source) {
|
|
30
|
+
const symbols = []
|
|
31
|
+
const seen = new Set()
|
|
32
|
+
|
|
33
|
+
const add = (name, kind) => {
|
|
34
|
+
if (!name || seen.has(name)) return
|
|
35
|
+
seen.add(name)
|
|
36
|
+
symbols.push({ name, kind })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const match of source.matchAll(/\b(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)/g)) {
|
|
40
|
+
add(match[1], 'function')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const match of source.matchAll(
|
|
44
|
+
/\b(?:export\s+)?(?:default\s+)?(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)/g,
|
|
45
|
+
)) {
|
|
46
|
+
add(match[1], 'class')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const match of source.matchAll(
|
|
50
|
+
/(?:export\s+)?(?:const|let)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/g,
|
|
51
|
+
)) {
|
|
52
|
+
add(match[1], 'function')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (const match of source.matchAll(/(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)/g)) {
|
|
56
|
+
add(match[1], 'variable')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const match of source.matchAll(
|
|
60
|
+
/(?:export\s+)?(?:const|let|var)\s+(\[[^\]]+\]|\{[^}]+\})/g,
|
|
61
|
+
)) {
|
|
62
|
+
for (const ident of destructuredNames(match[1])) add(ident, 'variable')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return symbols
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function collectImportSpecifiers(source) {
|
|
69
|
+
const specifiers = new Set()
|
|
70
|
+
for (const match of source.matchAll(/from\s+['"]([^'"]+)['"]/g)) {
|
|
71
|
+
specifiers.add(match[1])
|
|
72
|
+
}
|
|
73
|
+
for (const match of source.matchAll(/import\s+['"]([^'"]+)['"]/g)) {
|
|
74
|
+
specifiers.add(match[1])
|
|
75
|
+
}
|
|
76
|
+
for (const match of source.matchAll(/\brequire\(\s*['"]([^'"]+)['"]\s*\)/g)) {
|
|
77
|
+
specifiers.add(match[1])
|
|
78
|
+
}
|
|
79
|
+
return [...specifiers]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function extractImportBindings(source) {
|
|
83
|
+
const bindings = []
|
|
84
|
+
const seen = new Set()
|
|
85
|
+
|
|
86
|
+
const add = (name, from) => {
|
|
87
|
+
const ident = name?.trim()
|
|
88
|
+
const specifier = from?.trim()
|
|
89
|
+
if (!ident || !specifier) return
|
|
90
|
+
const key = `${ident}\0${specifier}`
|
|
91
|
+
if (seen.has(key)) return
|
|
92
|
+
seen.add(key)
|
|
93
|
+
bindings.push({ name: ident, from: specifier })
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (const match of source.matchAll(/^[ \t]*import\s+['"]([^'"]+)['"]/gm)) {
|
|
97
|
+
add(match[1], match[1])
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const match of source.matchAll(
|
|
101
|
+
/^[ \t]*import\s+(?:type\s+)?([\s\S]*?)\s+from\s+['"]([^'"]+)['"]/gm,
|
|
102
|
+
)) {
|
|
103
|
+
const clause = match[1].trim()
|
|
104
|
+
const from = match[2]
|
|
105
|
+
const star = clause.match(/^\*\s+as\s+([A-Za-z_$][\w$]*)/)
|
|
106
|
+
if (star) {
|
|
107
|
+
add(star[1], from)
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const named = clause.match(/\{([^}]*)\}/)
|
|
112
|
+
const defaultPart = clause
|
|
113
|
+
.replace(/\{[^}]*\}/, '')
|
|
114
|
+
.replace(/,/g, '')
|
|
115
|
+
.replace(/^type\s+/, '')
|
|
116
|
+
.trim()
|
|
117
|
+
if (defaultPart && /^[A-Za-z_$][\w$]*$/.test(defaultPart)) {
|
|
118
|
+
add(defaultPart, from)
|
|
119
|
+
}
|
|
120
|
+
if (!named) continue
|
|
121
|
+
for (const ident of destructuredNames(`{${named[1]}}`, { importClause: true })) {
|
|
122
|
+
add(ident, from)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const boundRequires = new Set()
|
|
127
|
+
|
|
128
|
+
for (const match of source.matchAll(
|
|
129
|
+
/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*require\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
130
|
+
)) {
|
|
131
|
+
add(match[1], match[2])
|
|
132
|
+
boundRequires.add(match[2])
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (const match of source.matchAll(
|
|
136
|
+
/\b(?:const|let|var)\s+(\{[^}]+\}|\[[^\]]+\])\s*=\s*require\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
137
|
+
)) {
|
|
138
|
+
boundRequires.add(match[2])
|
|
139
|
+
for (const ident of destructuredNames(match[1])) add(ident, match[2])
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
for (const match of source.matchAll(/\brequire\(\s*['"]([^'"]+)['"]\s*\)/g)) {
|
|
143
|
+
if (boundRequires.has(match[1])) continue
|
|
144
|
+
add(match[1], match[1])
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return bindings
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function resolveSpecifierAgainst(candidate, known) {
|
|
151
|
+
if (known.has(candidate)) return candidate
|
|
152
|
+
for (const ext of RESOLVE_EXTENSIONS) {
|
|
153
|
+
if (known.has(candidate + ext)) return candidate + ext
|
|
154
|
+
}
|
|
155
|
+
for (const ext of RESOLVE_EXTENSIONS) {
|
|
156
|
+
const indexFile = `${candidate}/index${ext}`
|
|
157
|
+
if (known.has(indexFile)) return indexFile
|
|
158
|
+
}
|
|
159
|
+
return null
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function destructuredNames(pattern, { importClause = false } = {}) {
|
|
163
|
+
const names = []
|
|
164
|
+
const inner = pattern.slice(1, -1)
|
|
165
|
+
for (const part of inner.split(',')) {
|
|
166
|
+
let token = part.trim()
|
|
167
|
+
if (!token) continue
|
|
168
|
+
token = token.replace(/^\.\.\./, '')
|
|
169
|
+
if (importClause) {
|
|
170
|
+
const aliased = token.match(/^(?:type\s+)?[A-Za-z_$][\w$]*\s+as\s+([A-Za-z_$][\w$]*)$/)
|
|
171
|
+
if (aliased) {
|
|
172
|
+
names.push(aliased[1])
|
|
173
|
+
continue
|
|
174
|
+
}
|
|
175
|
+
const ident = token.replace(/^type\s+/, '').trim()
|
|
176
|
+
if (/^[A-Za-z_$][\w$]*$/.test(ident)) names.push(ident)
|
|
177
|
+
continue
|
|
178
|
+
}
|
|
179
|
+
const aliased = token.match(/^[A-Za-z_$][\w$]*\s*:\s*([A-Za-z_$][\w$]*)/)
|
|
180
|
+
if (aliased) {
|
|
181
|
+
names.push(aliased[1])
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
const ident = token.split(/\s*=/)[0].trim()
|
|
185
|
+
if (/^[A-Za-z_$][\w$]*$/.test(ident)) names.push(ident)
|
|
186
|
+
}
|
|
187
|
+
return names
|
|
188
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export type PatchImport = {
|
|
2
|
+
from: string
|
|
3
|
+
to: string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type PatchSymbolAddition = {
|
|
7
|
+
name: string
|
|
8
|
+
file: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type PatchImportAddition = {
|
|
12
|
+
name: string
|
|
13
|
+
from: string
|
|
14
|
+
file: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function parseUnifiedPatch(patch: string): {
|
|
18
|
+
files: string[]
|
|
19
|
+
creates: string[]
|
|
20
|
+
deletes: string[]
|
|
21
|
+
createLines: Record<string, number>
|
|
22
|
+
entries: Array<{
|
|
23
|
+
id: string
|
|
24
|
+
kind: 'add' | 'modify' | 'delete'
|
|
25
|
+
addedLines: number
|
|
26
|
+
hunks: unknown[]
|
|
27
|
+
}>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function extractPatchImports(
|
|
31
|
+
entries: Array<{
|
|
32
|
+
id: string
|
|
33
|
+
kind: 'add' | 'modify' | 'delete'
|
|
34
|
+
hunks: unknown[]
|
|
35
|
+
}>,
|
|
36
|
+
knownFileIds?: string[],
|
|
37
|
+
): PatchImport[]
|
|
38
|
+
|
|
39
|
+
export function extractPatchAdditions(
|
|
40
|
+
entries: Array<{
|
|
41
|
+
id: string
|
|
42
|
+
kind: 'add' | 'modify' | 'delete'
|
|
43
|
+
hunks: unknown[]
|
|
44
|
+
}>,
|
|
45
|
+
): {
|
|
46
|
+
addedFunctions: PatchSymbolAddition[]
|
|
47
|
+
addedVariables: PatchSymbolAddition[]
|
|
48
|
+
addedImports: PatchImportAddition[]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function accumulatePatchAdditions(patches?: string[]): {
|
|
52
|
+
addedFunctions: PatchSymbolAddition[]
|
|
53
|
+
addedVariables: PatchSymbolAddition[]
|
|
54
|
+
addedImports: PatchImportAddition[]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function applyUnifiedPatch(
|
|
58
|
+
patch: string,
|
|
59
|
+
targetRoot: string,
|
|
60
|
+
): ReturnType<typeof parseUnifiedPatch>
|
|
61
|
+
|
|
62
|
+
export function folderOfFile(fileId: string): string
|
|
63
|
+
export function folderParent(folderPath: string): string | null
|
|
64
|
+
export function foldersFromFileIds(ids?: string[]): Set<string>
|
|
65
|
+
export function collectCreateFolders(
|
|
66
|
+
creates?: string[],
|
|
67
|
+
existingFolders?: Iterable<string>,
|
|
68
|
+
): string[]
|
|
69
|
+
|
|
70
|
+
export const emptyIntent: {
|
|
71
|
+
updatedAt: null
|
|
72
|
+
showMap: boolean
|
|
73
|
+
status: string
|
|
74
|
+
feature: null
|
|
75
|
+
steps: unknown[]
|
|
76
|
+
step: null
|
|
77
|
+
files: string[]
|
|
78
|
+
creates: string[]
|
|
79
|
+
deletes: string[]
|
|
80
|
+
createFolders: string[]
|
|
81
|
+
createLines: Record<string, number>
|
|
82
|
+
imports: PatchImport[]
|
|
83
|
+
addedFunctions: PatchSymbolAddition[]
|
|
84
|
+
addedVariables: PatchSymbolAddition[]
|
|
85
|
+
addedImports: PatchImportAddition[]
|
|
86
|
+
reason: null
|
|
87
|
+
sessionId: null
|
|
88
|
+
diffId: null
|
|
89
|
+
parentDiffId: null
|
|
90
|
+
chainIndex: null
|
|
91
|
+
chain: unknown[]
|
|
92
|
+
isActiveDiff: boolean
|
|
93
|
+
preview: boolean
|
|
94
|
+
phase: null
|
|
95
|
+
working: boolean
|
|
96
|
+
creationMode: boolean
|
|
97
|
+
canEnterBlueprint: boolean
|
|
98
|
+
blueprintSessionId: string | null
|
|
99
|
+
userCreatedBlocks: unknown[]
|
|
100
|
+
userCreatedIslands: unknown[]
|
|
101
|
+
blueprintFunctions: unknown[]
|
|
102
|
+
blueprintVariables: unknown[]
|
|
103
|
+
blueprintImports: unknown[]
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function isLastStep(intent: {
|
|
107
|
+
step?: number | null
|
|
108
|
+
steps?: unknown[]
|
|
109
|
+
}): boolean
|
|
110
|
+
|
|
111
|
+
export function overlayPatch(
|
|
112
|
+
intent: Record<string, unknown>,
|
|
113
|
+
patchText: string,
|
|
114
|
+
knownFileIds?: string[],
|
|
115
|
+
): Record<string, unknown>
|