fix(opencode): don't crash the whole session when plugins/lib is missing (#2538)

* fix(opencode): don't crash the whole session when plugins/lib is missing (#2530)

* fix: address review feedback (trailing newlines, symptom wording)

* fix(opencode): also lazy-load the store in ecc-hooks.ts plugin entrypoint

* test(opencode): add regression coverage for missing/present plugins/lib in ecc-hooks

* fix(opencode): guard diagnostic log() against unhandled rejection; tighten test assertion

* fix(opencode): only publish store after init succeeds; scrub loader errors from warning; catch sync log throws

* fix(opencode): scrub raw loader error from changed-files tool; add tool-level regression tests

* fix(ci): make OpenCode checks cross-platform

---------

Co-authored-by: haelyra <49814733+haelyra@users.noreply.github.com>
This commit is contained in:
Shanujan Suresh
2026-07-28 21:37:41 -04:00
committed by GitHub
co-authored by haelyra
parent afa34d1aca
commit 591ab5cbd3
5 changed files with 267 additions and 19 deletions
+78
View File
@@ -234,6 +234,84 @@ async function main() {
])
}
// Test changed-files tool
if (tools.changedfiles) {
tests.push([
"changed-files: reports an actionable, scrubbed error when plugins/lib is missing",
async () => withTempProject([], async (projectDir) => {
const repoRoot = path.join(__dirname, "..")
const libDir = path.join(repoRoot, ".opencode", "dist", "plugins", "lib")
const backupDir = path.join(
repoRoot,
".opencode",
"dist",
"plugins",
"lib.missing-store-test-backup"
)
fs.renameSync(libDir, backupDir)
try {
const context = createMockContext(projectDir)
await assert.rejects(
() => tools.changedfiles.execute({}, context),
(error) => {
assert.ok(error instanceof Error)
assert.ok(
error.message.includes("ecc repair --target opencode"),
"Expected the error to point at the repair command"
)
assert.ok(
!error.message.includes(repoRoot),
"Error message must not leak the local filesystem path"
)
assert.ok(
!error.message.includes("Original error"),
"Error message must not include the raw underlying loader error"
)
return true
}
)
} finally {
fs.renameSync(backupDir, libDir)
}
}),
])
tests.push([
"changed-files: renders tracked changes once plugins/lib is present",
async () => withTempProject([], async (projectDir) => {
const repoRoot = path.join(__dirname, "..")
const storeUrl = pathToFileURL(
path.join(repoRoot, ".opencode", "dist", "plugins", "lib", "changed-files-store.js")
).href
const store = await import(storeUrl)
store.initStore(projectDir)
store.clearChanges()
store.recordChange("src/example.ts", "modified")
store.recordChange("src/new-file.ts", "added")
try {
const context = createMockContext(projectDir)
const result = await tools.changedfiles.execute({ format: "json" }, context)
const parsed = JSON.parse(result)
assert.strictEqual(parsed.changed, true)
assert.ok(
parsed.files.some(
(f) =>
f.path === path.normalize("src/example.ts") && f.changeType === "modified"
)
)
assert.ok(
parsed.files.some(
(f) => f.path === path.normalize("src/new-file.ts") && f.changeType === "added"
)
)
} finally {
store.clearChanges()
}
}),
])
}
// Run all tests
let passed = 0
let failed = 0