Skip to content

Commit 2ca0ae7

Browse files
committed
fix(app): more defensive, handle no git
1 parent 19123b6 commit 2ca0ae7

File tree

7 files changed

+94
-23
lines changed

7 files changed

+94
-23
lines changed

packages/app/src/pages/session.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ export default function Page() {
502502
// Restore the prompt from the reverted message
503503
const parts = sync.data.part[message.id]
504504
if (parts) {
505-
const restored = extractPromptFromParts(parts)
505+
const restored = extractPromptFromParts(parts, { directory: sdk.directory })
506506
prompt.set(restored)
507507
}
508508
// Navigate to the message before the reverted one (which will be the new last visible message)

packages/app/src/utils/prompt.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,25 @@ function textPartValue(parts: Part[]) {
5353
* Extract prompt content from message parts for restoring into the prompt input.
5454
* This is used by undo to restore the original user prompt.
5555
*/
56-
export function extractPromptFromParts(parts: Part[]): Prompt {
56+
export function extractPromptFromParts(parts: Part[], opts?: { directory?: string }): Prompt {
5757
const textPart = textPartValue(parts)
5858
const text = textPart?.text ?? ""
59+
const directory = opts?.directory
60+
61+
const toRelative = (path: string) => {
62+
if (!directory) return path
63+
64+
const prefix = directory.endsWith("/") ? directory : directory + "/"
65+
if (path.startsWith(prefix)) return path.slice(prefix.length)
66+
67+
if (path.startsWith(directory)) {
68+
const next = path.slice(directory.length)
69+
if (next.startsWith("/")) return next.slice(1)
70+
return next
71+
}
72+
73+
return path
74+
}
5975

6076
const inline: Inline[] = []
6177
const images: ImageAttachmentPart[] = []
@@ -78,7 +94,7 @@ export function extractPromptFromParts(parts: Part[]): Prompt {
7894
start,
7995
end,
8096
value,
81-
path,
97+
path: toRelative(path),
8298
selection: selectionFromFileUrl(filePart.url),
8399
})
84100
continue
@@ -158,20 +174,21 @@ export function extractPromptFromParts(parts: Part[]): Prompt {
158174

159175
for (const item of inline) {
160176
if (item.start < 0 || item.end < item.start) continue
161-
if (item.end > text.length) continue
162-
if (item.start < cursor) continue
163177

164-
pushText(text.slice(cursor, item.start))
178+
const expected = item.value
179+
if (!expected) continue
165180

166-
if (item.type === "file") {
167-
pushFile(item)
168-
}
181+
const mismatch = item.end > text.length || item.start < cursor || text.slice(item.start, item.end) !== expected
182+
const start = mismatch ? text.indexOf(expected, cursor) : item.start
183+
if (start === -1) continue
184+
const end = mismatch ? start + expected.length : item.end
169185

170-
if (item.type === "agent") {
171-
pushAgent(item)
172-
}
186+
pushText(text.slice(cursor, start))
187+
188+
if (item.type === "file") pushFile(item)
189+
if (item.type === "agent") pushAgent(item)
173190

174-
cursor = item.end
191+
cursor = end
175192
}
176193

177194
pushText(text.slice(cursor))

packages/opencode/src/file/watcher.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export namespace FileWatcher {
8585
.cwd(Instance.worktree)
8686
.text()
8787
.then((x) => path.resolve(Instance.worktree, x.trim()))
88+
.catch(() => undefined)
8889
if (vcsDir && !cfgIgnores.includes(".git") && !cfgIgnores.includes(vcsDir)) {
8990
const gitDirContents = await readdir(vcsDir).catch(() => [])
9091
const ignoreList = gitDirContents.filter((entry) => entry !== "HEAD")

packages/opencode/src/project/project.ts

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,22 @@ export namespace Project {
5353
if (git) {
5454
let sandbox = path.dirname(git)
5555

56+
const gitBinary = Bun.which("git")
57+
5658
// cached id calculation
5759
let id = await Bun.file(path.join(git, "opencode"))
5860
.text()
5961
.then((x) => x.trim())
60-
.catch(() => {})
62+
.catch(() => undefined)
63+
64+
if (!gitBinary) {
65+
return {
66+
id: id ?? "global",
67+
worktree: sandbox,
68+
sandbox: sandbox,
69+
vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
70+
}
71+
}
6172

6273
// generate id from root commit
6374
if (!id) {
@@ -73,24 +84,53 @@ export namespace Project {
7384
.map((x) => x.trim())
7485
.toSorted(),
7586
)
87+
.catch(() => undefined)
88+
89+
if (!roots) {
90+
return {
91+
id: "global",
92+
worktree: sandbox,
93+
sandbox: sandbox,
94+
vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
95+
}
96+
}
97+
7698
id = roots[0]
77-
if (id) Bun.file(path.join(git, "opencode")).write(id)
99+
if (id) {
100+
void Bun.file(path.join(git, "opencode"))
101+
.write(id)
102+
.catch(() => undefined)
103+
}
78104
}
79105

80-
if (!id)
106+
if (!id) {
81107
return {
82108
id: "global",
83109
worktree: sandbox,
84110
sandbox: sandbox,
85111
vcs: "git",
86112
}
113+
}
87114

88-
sandbox = await $`git rev-parse --show-toplevel`
115+
const top = await $`git rev-parse --show-toplevel`
89116
.quiet()
90117
.nothrow()
91118
.cwd(sandbox)
92119
.text()
93120
.then((x) => path.resolve(sandbox, x.trim()))
121+
.catch(() => undefined)
122+
123+
if (!top) {
124+
return {
125+
id,
126+
sandbox,
127+
worktree: sandbox,
128+
vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
129+
}
130+
}
131+
132+
sandbox = top
133+
94134
const worktree = await $`git rev-parse --git-common-dir`
95135
.quiet()
96136
.nothrow()
@@ -101,6 +141,17 @@ export namespace Project {
101141
if (dirname === ".") return sandbox
102142
return dirname
103143
})
144+
.catch(() => undefined)
145+
146+
if (!worktree) {
147+
return {
148+
id,
149+
sandbox,
150+
worktree: sandbox,
151+
vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
152+
}
153+
}
154+
104155
return {
105156
id,
106157
sandbox,

packages/opencode/src/snapshot/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,14 @@ export namespace Snapshot {
179179
.quiet()
180180
.nothrow()
181181
.text()
182+
const added = isBinaryFile ? 0 : parseInt(additions)
183+
const deleted = isBinaryFile ? 0 : parseInt(deletions)
182184
result.push({
183185
file,
184186
before,
185187
after,
186-
additions: parseInt(additions),
187-
deletions: parseInt(deletions),
188+
additions: Number.isFinite(added) ? added : 0,
189+
deletions: Number.isFinite(deleted) ? deleted : 0,
188190
})
189191
}
190192
return result

packages/opencode/src/util/filesystem.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export namespace Filesystem {
3131
const result = []
3232
while (true) {
3333
const search = join(current, target)
34-
if (await exists(search)) result.push(search)
34+
if (await exists(search).catch(() => false)) result.push(search)
3535
if (stop === current) break
3636
const parent = dirname(current)
3737
if (parent === current) break
@@ -46,7 +46,7 @@ export namespace Filesystem {
4646
while (true) {
4747
for (const target of targets) {
4848
const search = join(current, target)
49-
if (await exists(search)) yield search
49+
if (await exists(search).catch(() => false)) yield search
5050
}
5151
if (stop === current) break
5252
const parent = dirname(current)

packages/ui/src/components/session-review.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,11 @@ export const SessionReview = (props: SessionReviewProps) => {
123123
diffStyle={diffStyle()}
124124
before={{
125125
name: diff.file!,
126-
contents: diff.before!,
126+
contents: typeof diff.before === "string" ? diff.before : "",
127127
}}
128128
after={{
129129
name: diff.file!,
130-
contents: diff.after!,
130+
contents: typeof diff.after === "string" ? diff.after : "",
131131
}}
132132
/>
133133
</Accordion.Content>

0 commit comments

Comments
 (0)