@koriit/opencode-claude-bridge 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -4
- package/src/index.ts +0 -5
- package/src/opencode-builtins.ts +1 -15
- package/src/skill-inject.ts +4 -4
- package/src/version.ts +0 -114
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koriit/opencode-claude-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "An OpenCode plugin that bridges enabled Claude Code plugins (commands, agents, skills, MCP, LSP) into OpenCode at runtime, namespaced so they never shadow your existing items.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,9 +42,6 @@
|
|
|
42
42
|
"engines": {
|
|
43
43
|
"bun": ">=1.2.0"
|
|
44
44
|
},
|
|
45
|
-
"opencode": {
|
|
46
|
-
"supportedRange": ">=1.15.0 <1.16.0"
|
|
47
|
-
},
|
|
48
45
|
"devDependencies": {
|
|
49
46
|
"@opencode-ai/plugin": "1.15.13",
|
|
50
47
|
"@opencode-ai/sdk": "1.15.13",
|
package/src/index.ts
CHANGED
|
@@ -8,7 +8,6 @@ import { injectLsp } from "./lsp-inject.js"
|
|
|
8
8
|
import { createLogger } from "./logger.js"
|
|
9
9
|
import { listClaudePlugins, selectEnabledPlugins } from "./selection.js"
|
|
10
10
|
import { collectExistingSkillNames } from "./skill-scan.js"
|
|
11
|
-
import { checkVersion, fetchOpencodeVersion } from "./version.js"
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Parse an environment-variable value as a boolean, matching the set of truthy
|
|
@@ -57,10 +56,6 @@ export const server: Plugin = async (_input, options) => {
|
|
|
57
56
|
// Replay parse-time validation warnings (strict-promotable).
|
|
58
57
|
for (const w of warnings) logger.warn(w)
|
|
59
58
|
|
|
60
|
-
// §9 version-compat advisory (always soft — still attempts injection).
|
|
61
|
-
const version = await fetchOpencodeVersion(_input.serverUrl)
|
|
62
|
-
checkVersion(version, logger)
|
|
63
|
-
|
|
64
59
|
// §5 mirror-claude resolution.
|
|
65
60
|
const all = await listClaudePlugins(_input.$, logger)
|
|
66
61
|
if (all === null) return // CLI missing/failed — already warned; inject nothing.
|
package/src/opencode-builtins.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* The runtime OpenCode version verified by the spec is **1.15.10**; the source
|
|
5
|
-
* checkout on this machine is **1.15.13**. The constants were re-confirmed
|
|
6
|
-
* unchanged against the 1.15.13 source — the cited line numbers and values are
|
|
7
|
-
* identical in both versions. The pin below records the runtime version (what
|
|
8
|
-
* users run and what the spec validated against); re-validate on the next upgrade.
|
|
9
|
-
*
|
|
10
|
-
* MUST be re-validated on every OpenCode upgrade — these are part of what the
|
|
11
|
-
* §9 compatibility check guards.
|
|
2
|
+
* OpenCode built-in name lists and skill-discovery constants.
|
|
12
3
|
*
|
|
13
4
|
* Source locations (all in `packages/opencode/src/`):
|
|
14
5
|
* - Built-in agents: `agent/agent.ts:127-248`
|
|
@@ -17,11 +8,6 @@
|
|
|
17
8
|
* - Skill scan dirs: `skill/index.ts:22-26, 173-233`
|
|
18
9
|
*/
|
|
19
10
|
|
|
20
|
-
// The single source of truth for the verified OpenCode version is VERIFIED_OPENCODE_VERSION
|
|
21
|
-
// in src/version.ts. Re-export it here so callers in this module don't need a cross-module
|
|
22
|
-
// import, and so the value is never duplicated.
|
|
23
|
-
export { VERIFIED_OPENCODE_VERSION as PINNED_OPENCODE_VERSION } from "./version.js"
|
|
24
|
-
|
|
25
11
|
// ── Built-in agents ─────────────────────────────────────────────────────────
|
|
26
12
|
//
|
|
27
13
|
// Registered as hard-coded entries in the `agents` object before `cfg.agent`
|
package/src/skill-inject.ts
CHANGED
|
@@ -455,12 +455,12 @@ export async function injectSkills(
|
|
|
455
455
|
|
|
456
456
|
const skillsCfg = guardSkillsConfig(mutableCfg)
|
|
457
457
|
|
|
458
|
-
//
|
|
459
|
-
// without triggering the lazy Skill-service fetch (§6.3).
|
|
458
|
+
// Note (not a warning): URL-sourced skill names aren't knowable at hook time
|
|
459
|
+
// without triggering the lazy Skill-service fetch (§6.3). The bridge proceeds
|
|
460
|
+
// without collision-detection for those skills — a normal, acceptable limitation.
|
|
460
461
|
if (skillsCfg.urls.length > 0) {
|
|
461
|
-
logger.
|
|
462
|
+
logger.info(
|
|
462
463
|
"cfg.skills.urls is non-empty; URL-sourced skill names are not available at hook time — bridge cannot detect collisions against URL-sourced skills",
|
|
463
|
-
{ fatalInStrict: false },
|
|
464
464
|
)
|
|
465
465
|
}
|
|
466
466
|
|
package/src/version.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import type { Logger } from "./logger.js"
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Supported OpenCode version range. The injection approach relies on OpenCode-*internal*
|
|
5
|
-
* behavior (a shared mutable config object + lazy-after-`plugin.init()` service init) that
|
|
6
|
-
* is not a documented public contract, so the bridge pins a conservative same-minor window
|
|
7
|
-
* and warns outside it. Widen only when the e2e canary passes against a new version (§9).
|
|
8
|
-
*/
|
|
9
|
-
export const SUPPORTED_OPENCODE_RANGE = ">=1.15.0 <1.16.0"
|
|
10
|
-
|
|
11
|
-
/** The OpenCode version the design and range were verified against (§9). */
|
|
12
|
-
export const VERIFIED_OPENCODE_VERSION = "1.15.10"
|
|
13
|
-
|
|
14
|
-
interface Semver {
|
|
15
|
-
major: number
|
|
16
|
-
minor: number
|
|
17
|
-
patch: number
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Parse a `major.minor.patch` string, tolerating a leading `v` and ignoring any
|
|
22
|
-
* prerelease/build suffix (`-rc.1`, `+build`). Returns `null` if the core triple is
|
|
23
|
-
* not three integers.
|
|
24
|
-
*/
|
|
25
|
-
export function parseSemver(version: string): Semver | null {
|
|
26
|
-
// Anchored end so trailing garbage ("1.2.3abc", "1.2.3.4") is rejected rather than
|
|
27
|
-
// silently truncated to a bogus triple; only a `-prerelease` / `+build` suffix is allowed.
|
|
28
|
-
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version.trim())
|
|
29
|
-
if (!match) return null
|
|
30
|
-
return {
|
|
31
|
-
major: Number(match[1]),
|
|
32
|
-
minor: Number(match[2]),
|
|
33
|
-
patch: Number(match[3]),
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function compare(a: Semver, b: Semver): number {
|
|
38
|
-
return a.major - b.major || a.minor - b.minor || a.patch - b.patch
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const COMPARATORS: Record<string, (cmp: number) => boolean> = {
|
|
42
|
-
">=": (c) => c >= 0,
|
|
43
|
-
"<=": (c) => c <= 0,
|
|
44
|
-
">": (c) => c > 0,
|
|
45
|
-
"<": (c) => c < 0,
|
|
46
|
-
"=": (c) => c === 0,
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Test `version` against a space-separated AND range of simple comparator clauses
|
|
51
|
-
* (e.g. `">=1.15.0 <1.16.0"`). Returns `false` if `version` is unparseable or any
|
|
52
|
-
* clause is malformed — callers treat that as "outside the supported range".
|
|
53
|
-
*/
|
|
54
|
-
export function satisfiesRange(version: string, range: string): boolean {
|
|
55
|
-
const parsed = parseSemver(version)
|
|
56
|
-
if (!parsed) return false
|
|
57
|
-
|
|
58
|
-
const clauses = range.trim().split(/\s+/).filter(Boolean)
|
|
59
|
-
for (const clause of clauses) {
|
|
60
|
-
const match = /^(>=|<=|>|<|=)?\s*(.+)$/.exec(clause)
|
|
61
|
-
if (!match) return false
|
|
62
|
-
const op = match[2] === undefined ? null : (match[1] ?? "=")
|
|
63
|
-
const bound = parseSemver(match[2]!)
|
|
64
|
-
if (op === null || !bound) return false
|
|
65
|
-
if (!COMPARATORS[op]!(compare(parsed, bound))) return false
|
|
66
|
-
}
|
|
67
|
-
return true
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Read the running OpenCode version from the live HTTP server's health endpoint.
|
|
72
|
-
*
|
|
73
|
-
* This is the only mechanism available to an external plugin: the v1 `input.client`
|
|
74
|
-
* exposes no version method, there is no `OPENCODE_VERSION` env var at runtime, and the
|
|
75
|
-
* build-time define global is not visible outside OpenCode's own bundle. The endpoint is
|
|
76
|
-
* mounted at the server root (`GET /global/health` → `{ healthy, version }`) and the HTTP
|
|
77
|
-
* server is already running before `plugin.init()` fires, so calling it from the config
|
|
78
|
-
* hook is safe and touches none of the lazy component services.
|
|
79
|
-
*
|
|
80
|
-
* Returns `null` on any failure (network, parse, missing field) — the caller degrades to
|
|
81
|
-
* "version unknown" rather than blocking injection.
|
|
82
|
-
*/
|
|
83
|
-
export async function fetchOpencodeVersion(serverUrl: URL): Promise<string | null> {
|
|
84
|
-
try {
|
|
85
|
-
const res = await fetch(new URL("/global/health", serverUrl))
|
|
86
|
-
if (!res.ok) return null
|
|
87
|
-
const body = (await res.json()) as { version?: unknown }
|
|
88
|
-
return typeof body.version === "string" ? body.version : null
|
|
89
|
-
} catch {
|
|
90
|
-
return null
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Compare the running version against {@link SUPPORTED_OPENCODE_RANGE} and emit the §9
|
|
96
|
-
* advisory warning when it falls outside. This warning is **always soft** — even under
|
|
97
|
-
* `strict`, the bridge still attempts injection (the e2e suite is the real canary). A
|
|
98
|
-
* `null` version (health unreachable) is reported as an inability to verify, not a failure.
|
|
99
|
-
*/
|
|
100
|
-
export function checkVersion(version: string | null, logger: Logger): void {
|
|
101
|
-
if (version === null) {
|
|
102
|
-
logger.warn(
|
|
103
|
-
`could not determine the running OpenCode version; proceeding (supported range ${SUPPORTED_OPENCODE_RANGE})`,
|
|
104
|
-
{ fatalInStrict: false },
|
|
105
|
-
)
|
|
106
|
-
return
|
|
107
|
-
}
|
|
108
|
-
if (!satisfiesRange(version, SUPPORTED_OPENCODE_RANGE)) {
|
|
109
|
-
logger.warn(
|
|
110
|
-
`untested OpenCode version ${version} (supported range ${SUPPORTED_OPENCODE_RANGE}) — bridge may misbehave; attempting injection anyway`,
|
|
111
|
-
{ fatalInStrict: false },
|
|
112
|
-
)
|
|
113
|
-
}
|
|
114
|
-
}
|