mirror of
https://github.com/affaan-m/ECC.git
synced 2026-08-17 21:15:40 +02:00
* 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>
105 lines
3.9 KiB
TypeScript
105 lines
3.9 KiB
TypeScript
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"
|
|
import type { ChangeType, TreeNode } from "../plugins/lib/changed-files-store.js"
|
|
|
|
const INDICATORS: Record<ChangeType, string> = {
|
|
added: "+",
|
|
modified: "~",
|
|
deleted: "-",
|
|
}
|
|
|
|
function renderTree(nodes: TreeNode[], indent: string): string {
|
|
const lines: string[] = []
|
|
for (const node of nodes) {
|
|
const indicator = node.changeType ? ` (${INDICATORS[node.changeType]})` : ""
|
|
const name = node.changeType ? `${node.name}${indicator}` : `${node.name}/`
|
|
lines.push(`${indent}${name}`)
|
|
if (node.children.length > 0) {
|
|
lines.push(renderTree(node.children, `${indent} `))
|
|
}
|
|
}
|
|
return lines.join("\n")
|
|
}
|
|
|
|
// Loaded lazily (instead of via a top-level import) so that a missing or
|
|
// partially-installed `~/.opencode/plugins` directory only breaks this one
|
|
// tool when it's actually invoked, rather than throwing during module
|
|
// evaluation. `tools/index.ts` re-exports every tool from a single barrel
|
|
// file, so a static import failure here previously took down the entire
|
|
// tools module -- and with it, the whole OpenCode session -- on the very
|
|
// first tool-loading pass (see #2530).
|
|
type ChangedFilesStore = typeof import("../plugins/lib/changed-files-store.js")
|
|
let changedFilesStorePromise: Promise<ChangedFilesStore> | undefined
|
|
|
|
async function loadChangedFilesStore(): Promise<ChangedFilesStore> {
|
|
if (!changedFilesStorePromise) {
|
|
changedFilesStorePromise = import("../plugins/lib/changed-files-store.js").catch(() => {
|
|
changedFilesStorePromise = undefined
|
|
throw new Error(
|
|
"changed-files tool: could not load the changed-files store. " +
|
|
"This usually means the ~/.opencode/plugins directory is missing or incomplete " +
|
|
"(an interrupted or partial ECC install can leave tools/ populated without plugins/). " +
|
|
"Run `node scripts/repair.js --target opencode` (or `ecc repair --target opencode`) " +
|
|
"from the ECC repo to restore the missing files."
|
|
)
|
|
})
|
|
}
|
|
return changedFilesStorePromise
|
|
}
|
|
|
|
const changedFilesTool: ToolDefinition = tool({
|
|
description:
|
|
"List files changed by agents in this session as a navigable tree. Shows added (+), modified (~), and deleted (-) indicators. Use filter to show only specific change types. Returns paths for git diff.",
|
|
args: {
|
|
filter: tool.schema
|
|
.enum(["all", "added", "modified", "deleted"])
|
|
.optional()
|
|
.describe("Filter by change type (default: all)"),
|
|
format: tool.schema
|
|
.enum(["tree", "json"])
|
|
.optional()
|
|
.describe("Output format: tree for terminal display, json for structured data (default: tree)"),
|
|
},
|
|
async execute(args, context) {
|
|
const { buildTree, getChangedPaths, hasChanges } = await loadChangedFilesStore()
|
|
const filter = args.filter === "all" || !args.filter ? undefined : (args.filter as ChangeType)
|
|
const format = args.format ?? "tree"
|
|
|
|
if (!hasChanges()) {
|
|
return JSON.stringify({ changed: false, message: "No files changed in this session" })
|
|
}
|
|
|
|
const paths = getChangedPaths(filter)
|
|
|
|
if (format === "json") {
|
|
return JSON.stringify(
|
|
{
|
|
changed: true,
|
|
filter: filter ?? "all",
|
|
files: paths.map((p) => ({ path: p.path, changeType: p.changeType })),
|
|
diffCommands: paths
|
|
.filter((p) => p.changeType !== "added")
|
|
.map((p) => `git diff ${p.path}`),
|
|
},
|
|
null,
|
|
2
|
|
)
|
|
}
|
|
|
|
const tree = buildTree(filter)
|
|
const treeStr = renderTree(tree, "")
|
|
const diffHint = paths
|
|
.filter((p) => p.changeType !== "added")
|
|
.slice(0, 5)
|
|
.map((p) => ` git diff ${p.path}`)
|
|
.join("\n")
|
|
|
|
let output = `Changed files (${paths.length}):\n\n${treeStr}`
|
|
if (diffHint) {
|
|
output += `\n\nTo view diff for a file:\n${diffHint}`
|
|
}
|
|
return output
|
|
},
|
|
})
|
|
|
|
export default changedFilesTool
|