mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-21 00:58:07 +02:00
Root package.json declared no main or exports, so OpenCode npm plugin resolution (import.meta.resolve) failed and the plugin was silently skipped (#3127). The .opencode TypeScript sources imported siblings with .js specifiers that only exist after compilation, so the home install, which loads the .ts files in place, crashed the tool registry with ERR_MODULE_NOT_FOUND (#3112). Declare main/types/exports on the root package pointing at the compiled plugin entry, switch the sources to .ts specifiers, and enable allowImportingTsExtensions with rewriteRelativeImportExtensions so the emitted dist keeps working .js specifiers. Add smoke tests that build the package, resolve and import the entry by name from a temp install, and verify every in-place relative import resolves. Fixes #3127 Fixes #3112
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.ts"
|
|
|
|
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.ts")
|
|
let changedFilesStorePromise: Promise<ChangedFilesStore> | undefined
|
|
|
|
async function loadChangedFilesStore(): Promise<ChangedFilesStore> {
|
|
if (!changedFilesStorePromise) {
|
|
changedFilesStorePromise = import("../plugins/lib/changed-files-store.ts").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
|