mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-20 16:47:59 +02:00
Merge pull request #3167 from affaan-m/fix/opencode-plugin-entry
fix(opencode): add resolvable package entry and loadable in-place sources
This commit is contained in:
+1
-1
@@ -37,4 +37,4 @@
|
||||
// Export the main plugin
|
||||
// opencode's legacy plugin loader iterates every module export and throws if
|
||||
// any is not a plugin function, so only the plugin function may be exported.
|
||||
export { default } from "./plugins/index.js"
|
||||
export { default } from "./plugins/index.ts"
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import * as fs from "fs"
|
||||
import * as path from "path"
|
||||
import changedFilesTool from "../tools/changed-files.js"
|
||||
import dependencyAnalyzerTool from "../tools/dependency-analyzer.js"
|
||||
import changedFilesTool from "../tools/changed-files.ts"
|
||||
import dependencyAnalyzerTool from "../tools/dependency-analyzer.ts"
|
||||
|
||||
/**
|
||||
* Type definitions for better type safety
|
||||
@@ -111,9 +111,9 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({
|
||||
// This plugin is OpenCode's startup entry point, so a static import
|
||||
// failure here previously crashed the whole plugin -- and with it, the
|
||||
// entire OpenCode session -- before any hooks could load (see #2530).
|
||||
let changedFilesStore: typeof import("./lib/changed-files-store.js") | undefined
|
||||
let changedFilesStore: typeof import("./lib/changed-files-store.ts") | undefined
|
||||
try {
|
||||
const store = await import("./lib/changed-files-store.js")
|
||||
const store = await import("./lib/changed-files-store.ts")
|
||||
store.initStore(worktreePath)
|
||||
changedFilesStore = store
|
||||
} catch {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* while taking advantage of OpenCode's more sophisticated 20+ event types.
|
||||
*/
|
||||
|
||||
export { ECCHooksPlugin, default } from "./ecc-hooks.js"
|
||||
export { ECCHooksPlugin, default } from "./ecc-hooks.ts"
|
||||
|
||||
// Re-export for named imports
|
||||
export * from "./ecc-hooks.js"
|
||||
export * from "./ecc-hooks.ts"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"
|
||||
import type { ChangeType, TreeNode } from "../plugins/lib/changed-files-store.js"
|
||||
import type { ChangeType, TreeNode } from "../plugins/lib/changed-files-store.ts"
|
||||
|
||||
const INDICATORS: Record<ChangeType, string> = {
|
||||
added: "+",
|
||||
@@ -27,12 +27,12 @@ function renderTree(nodes: TreeNode[], indent: string): string {
|
||||
// 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")
|
||||
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.js").catch(() => {
|
||||
changedFilesStorePromise = import("../plugins/lib/changed-files-store.ts").catch(() => {
|
||||
changedFilesStorePromise = undefined
|
||||
throw new Error(
|
||||
"changed-files tool: could not load the changed-files store. " +
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
*/
|
||||
|
||||
// Re-export all tools
|
||||
export { default as runTests } from "./run-tests.js"
|
||||
export { default as checkCoverage } from "./check-coverage.js"
|
||||
export { default as securityAudit } from "./security-audit.js"
|
||||
export { default as formatCode } from "./format-code.js"
|
||||
export { default as lintCheck } from "./lint-check.js"
|
||||
export { default as gitSummary } from "./git-summary.js"
|
||||
export { default as changedFiles } from "./changed-files.js"
|
||||
export { default as dependencyAnalyzer } from "./dependency-analyzer.js"
|
||||
export { default as runTests } from "./run-tests.ts"
|
||||
export { default as checkCoverage } from "./check-coverage.ts"
|
||||
export { default as securityAudit } from "./security-audit.ts"
|
||||
export { default as formatCode } from "./format-code.ts"
|
||||
export { default as lintCheck } from "./lint-check.ts"
|
||||
export { default as gitSummary } from "./git-summary.ts"
|
||||
export { default as changedFiles } from "./changed-files.ts"
|
||||
export { default as dependencyAnalyzer } from "./dependency-analyzer.ts"
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"rewriteRelativeImportExtensions": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": [
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
"name": "ecc-universal",
|
||||
"version": "2.2.1",
|
||||
"description": "Harness-native agent operating system for Codex, OpenCode, Cursor, Gemini, Claude Code, and terminal workflows - skills, hooks, rules, MCP conventions, and operator control-plane patterns",
|
||||
"main": ".opencode/dist/index.js",
|
||||
"types": ".opencode/dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./.opencode/dist/index.d.ts",
|
||||
"import": "./.opencode/dist/index.js",
|
||||
"default": "./.opencode/dist/index.js"
|
||||
},
|
||||
"./package.json": "./package.json",
|
||||
"./*": "./*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
const assert = require("assert")
|
||||
const fs = require("fs")
|
||||
const os = require("os")
|
||||
const path = require("path")
|
||||
const { spawnSync } = require("child_process")
|
||||
const { getNpmPackEntry } = require("../lib/npm-pack-output")
|
||||
@@ -46,6 +47,72 @@ function main() {
|
||||
assert.strictEqual(result.status, 0, result.stderr)
|
||||
assert.ok(fs.existsSync(distEntry), ".opencode/dist/index.js should exist after build")
|
||||
}],
|
||||
["package.json declares a resolvable OpenCode plugin entry", () => {
|
||||
assert.strictEqual(packageJson.main, ".opencode/dist/index.js")
|
||||
assert.ok(packageJson.exports, "package.json must declare an exports map")
|
||||
assert.deepStrictEqual(packageJson.exports["."], {
|
||||
types: "./.opencode/dist/index.d.ts",
|
||||
import: "./.opencode/dist/index.js",
|
||||
default: "./.opencode/dist/index.js",
|
||||
})
|
||||
}],
|
||||
["installed package resolves and imports its root module by name", () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ecc-opencode-entry-"))
|
||||
try {
|
||||
fs.mkdirSync(path.join(tempDir, "node_modules"), { recursive: true })
|
||||
fs.symlinkSync(
|
||||
repoRoot,
|
||||
path.join(tempDir, "node_modules", "ecc-universal"),
|
||||
process.platform === "win32" ? "junction" : "dir"
|
||||
)
|
||||
const probe = `
|
||||
const resolved = import.meta.resolve("ecc-universal")
|
||||
if (!resolved.endsWith("/.opencode/dist/index.js")) {
|
||||
throw new Error("unexpected entry resolution: " + resolved)
|
||||
}
|
||||
const mod = await import("ecc-universal")
|
||||
if (Object.keys(mod).join(",") !== "default" || typeof mod.default !== "function") {
|
||||
throw new Error("root module must export exactly the plugin function")
|
||||
}
|
||||
`
|
||||
const probePath = path.join(tempDir, "probe.mjs")
|
||||
fs.writeFileSync(probePath, probe)
|
||||
const result = spawnSync(process.execPath, [probePath], {
|
||||
cwd: tempDir,
|
||||
encoding: "utf8",
|
||||
})
|
||||
assert.strictEqual(result.status, 0, result.stderr)
|
||||
} finally {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true })
|
||||
}
|
||||
}],
|
||||
["OpenCode TypeScript sources resolve their relative imports in place", () => {
|
||||
const opencodeDir = path.join(repoRoot, ".opencode")
|
||||
const sourceFiles = []
|
||||
const walk = (dir) => {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const entryPath = path.join(dir, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
if (entry.name !== "node_modules" && entry.name !== "dist") walk(entryPath)
|
||||
} else if (entry.name.endsWith(".ts")) {
|
||||
sourceFiles.push(entryPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
walk(opencodeDir)
|
||||
assert.ok(sourceFiles.length > 0, "expected OpenCode TypeScript sources")
|
||||
const unresolved = []
|
||||
for (const sourceFile of sourceFiles) {
|
||||
const source = fs.readFileSync(sourceFile, "utf8")
|
||||
for (const match of source.matchAll(/(?:from|import)\s*\(?\s*"(\.[^"]+)"/g)) {
|
||||
const target = path.resolve(path.dirname(sourceFile), match[1])
|
||||
if (!fs.existsSync(target)) {
|
||||
unresolved.push(`${path.relative(repoRoot, sourceFile)} -> ${match[1]}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.deepStrictEqual(unresolved, [])
|
||||
}],
|
||||
["built OpenCode entry exports only the plugin function", () => {
|
||||
const check = `
|
||||
const assert = require("assert")
|
||||
|
||||
Reference in New Issue
Block a user