Skip to content

Commit 868a34d

Browse files
author
chenbo
committed
Add sidecar plugin version doctor check
1 parent 3f99784 commit 868a34d

11 files changed

Lines changed: 166 additions & 31 deletions

File tree

README.en.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ It stays quiet by default and only interrupts the TUI when blockers are found.
6565
opencode-plusplus # OpenCode chat mode with the OpenCode++ sidecar
6666
opencode-plusplus report # show the latest sidecar check
6767
opencode-plusplus status # show whether the sidecar is active
68-
opencode-plusplus doctor # diagnose OpenCode / auth / git / context / plugin
68+
opencode-plusplus doctor # diagnose OpenCode / auth / git / context / plugin version
6969
opencode-plusplus --pure # plain OpenCode without OpenCode++
7070
```
7171

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ OpenCode++ 会在外层安静运行:
6464
opencode-plusplus # 进入 OpenCode 聊天模式,并自动启用 OpenCode++ sidecar
6565
opencode-plusplus report # 查看最近一次检查结果
6666
opencode-plusplus status # 查看 sidecar 是否 active
67-
opencode-plusplus doctor # 诊断 OpenCode / auth / git / context / plugin
67+
opencode-plusplus doctor # 诊断 OpenCode / auth / git / context / plugin version
6868
opencode-plusplus --pure # 纯 OpenCode,不启用 OpenCode++
6969
```
7070

docs/integrations/opencode-sidecar.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ opencode-plusplus sidecar verify
130130

131131
`opencode-plusplus report` opens `.agent-context/sidecar/latest.md`.
132132

133-
`opencode-plusplus doctor` checks OpenCode, auth, git, context, plugin, and sidecar readiness.
133+
`opencode-plusplus doctor` checks OpenCode, auth, git, context, sidecar plugin readiness, and CLI/plugin version consistency.
134134

135135
`opencode-plusplus sidecar verify` runs the shared guard stack and writes the latest sidecar report. It is also what the plugin runs automatically on idle when the repository is dirty.
136136

docs/reference/cli-help-snapshot.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Commands:
2222
sidecar Inspect and verify OpenCode++ sidecar integrations.
2323
report [options] [repo] Show the latest OpenCode++ sidecar report.
2424
status [options] [repo] Show whether the OpenCode++ OpenCode sidecar is active.
25-
doctor [options] [repo] Check OpenCode, auth, git, context, and OpenCode++ sidecar readiness.
25+
doctor [options] [repo] Check OpenCode, auth, git, context, plugin version, and OpenCode++ sidecar readiness.
2626
opencode OpenCode preset commands.
2727
oc Shortcut for OpenCode preset commands.
2828
build [options] [repo] Generate AGENTS.md and .agent-context outputs.
@@ -206,7 +206,8 @@ Options:
206206
```txt
207207
Usage: opencode-plusplus doctor [options] [repo]
208208
209-
Check OpenCode, auth, git, context, and OpenCode++ sidecar readiness.
209+
Check OpenCode, auth, git, context, plugin version, and OpenCode++ sidecar
210+
readiness.
210211
211212
Arguments:
212213
repo repository path (default: ".")
@@ -231,8 +232,8 @@ Commands:
231232
opencode run -> trace/policy/verify -> decision.
232233
init [options] [repo] Initialize OpenCode commands and agent files for
233234
OpenCode++.
234-
doctor [options] [repo] Check whether OpenCode and the current repository are
235-
ready for the OpenCode preset.
235+
doctor [options] [repo] Check whether OpenCode, plugin version, and the
236+
current repository are ready for the OpenCode preset.
236237
report [options] [repo] Show the latest OpenCode orchestrator report without
237238
opening .agent-context manually.
238239
repair [options] [repo] Print repair guidance from the latest OpenCode
@@ -303,8 +304,8 @@ Options:
303304
```txt
304305
Usage: opencode-plusplus opencode doctor [options] [repo]
305306
306-
Check whether OpenCode and the current repository are ready for the OpenCode
307-
preset.
307+
Check whether OpenCode, plugin version, and the current repository are ready for
308+
the OpenCode preset.
308309
309310
Arguments:
310311
repo repository path (default: ".")
@@ -365,8 +366,8 @@ Commands:
365366
run [options] <args...> Alias for `opencode-plusplus opencode run`.
366367
init [options] [repo] Initialize OpenCode commands and agent files for
367368
OpenCode++.
368-
doctor [options] [repo] Check whether OpenCode and the current repository are
369-
ready for the OpenCode preset.
369+
doctor [options] [repo] Check whether OpenCode, plugin version, and the
370+
current repository are ready for the OpenCode preset.
370371
report [options] [repo] Show the latest OpenCode orchestrator report without
371372
opening .agent-context manually.
372373
repair [options] [repo] Print repair guidance from the latest OpenCode
@@ -436,8 +437,8 @@ Options:
436437
```txt
437438
Usage: opencode-plusplus oc doctor [options] [repo]
438439
439-
Check whether OpenCode and the current repository are ready for the OpenCode
440-
preset.
440+
Check whether OpenCode, plugin version, and the current repository are ready for
441+
the OpenCode preset.
441442
442443
Arguments:
443444
repo repository path (default: ".")

docs/reference/cli-reference.md

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,17 @@ opencode-plusplus orchestrate "task" .
3434
| `opencode-plusplus sidecar record-tool` | Record OpenCode tool.execute.after evidence into sidecar event logs and execution traces. |
3535
| `opencode-plusplus report` | Show the latest OpenCode++ sidecar report. |
3636
| `opencode-plusplus status` | Show whether the OpenCode++ OpenCode sidecar is active. |
37-
| `opencode-plusplus doctor` | Check OpenCode, auth, git, context, and OpenCode++ sidecar readiness. |
37+
| `opencode-plusplus doctor` | Check OpenCode, auth, git, context, plugin version, and OpenCode++ sidecar readiness. |
3838
| `opencode-plusplus opencode` | OpenCode preset commands. |
3939
| `opencode-plusplus opencode run` | Run the harness-led OpenCode preset: plan/pack -> opencode run -> trace/policy/verify -> decision. |
4040
| `opencode-plusplus opencode init` | Initialize OpenCode commands and agent files for OpenCode++. |
41-
| `opencode-plusplus opencode doctor` | Check whether OpenCode and the current repository are ready for the OpenCode preset. |
41+
| `opencode-plusplus opencode doctor` | Check whether OpenCode, plugin version, and the current repository are ready for the OpenCode preset. |
4242
| `opencode-plusplus opencode report` | Show the latest OpenCode orchestrator report without opening .agent-context manually. |
4343
| `opencode-plusplus opencode repair` | Print repair guidance from the latest OpenCode decision. |
4444
| `opencode-plusplus oc` | Shortcut for OpenCode preset commands. |
4545
| `opencode-plusplus oc run` | Alias for `opencode-plusplus opencode run`. |
4646
| `opencode-plusplus oc init` | Initialize OpenCode commands and agent files for OpenCode++. |
47-
| `opencode-plusplus oc doctor` | Check whether OpenCode and the current repository are ready for the OpenCode preset. |
47+
| `opencode-plusplus oc doctor` | Check whether OpenCode, plugin version, and the current repository are ready for the OpenCode preset. |
4848
| `opencode-plusplus oc report` | Show the latest OpenCode orchestrator report without opening .agent-context manually. |
4949
| `opencode-plusplus oc repair` | Print repair guidance from the latest OpenCode decision. |
5050
| `opencode-plusplus build` | Generate AGENTS.md and .agent-context outputs. |
@@ -113,7 +113,7 @@ Commands:
113113
sidecar Inspect and verify OpenCode++ sidecar integrations.
114114
report [options] [repo] Show the latest OpenCode++ sidecar report.
115115
status [options] [repo] Show whether the OpenCode++ OpenCode sidecar is active.
116-
doctor [options] [repo] Check OpenCode, auth, git, context, and OpenCode++ sidecar readiness.
116+
doctor [options] [repo] Check OpenCode, auth, git, context, plugin version, and OpenCode++ sidecar readiness.
117117
opencode OpenCode preset commands.
118118
oc Shortcut for OpenCode preset commands.
119119
build [options] [repo] Generate AGENTS.md and .agent-context outputs.
@@ -297,7 +297,8 @@ Options:
297297
```txt
298298
Usage: opencode-plusplus doctor [options] [repo]
299299
300-
Check OpenCode, auth, git, context, and OpenCode++ sidecar readiness.
300+
Check OpenCode, auth, git, context, plugin version, and OpenCode++ sidecar
301+
readiness.
301302
302303
Arguments:
303304
repo repository path (default: ".")
@@ -322,8 +323,8 @@ Commands:
322323
opencode run -> trace/policy/verify -> decision.
323324
init [options] [repo] Initialize OpenCode commands and agent files for
324325
OpenCode++.
325-
doctor [options] [repo] Check whether OpenCode and the current repository are
326-
ready for the OpenCode preset.
326+
doctor [options] [repo] Check whether OpenCode, plugin version, and the
327+
current repository are ready for the OpenCode preset.
327328
report [options] [repo] Show the latest OpenCode orchestrator report without
328329
opening .agent-context manually.
329330
repair [options] [repo] Print repair guidance from the latest OpenCode
@@ -394,8 +395,8 @@ Options:
394395
```txt
395396
Usage: opencode-plusplus opencode doctor [options] [repo]
396397
397-
Check whether OpenCode and the current repository are ready for the OpenCode
398-
preset.
398+
Check whether OpenCode, plugin version, and the current repository are ready for
399+
the OpenCode preset.
399400
400401
Arguments:
401402
repo repository path (default: ".")
@@ -456,8 +457,8 @@ Commands:
456457
run [options] <args...> Alias for `opencode-plusplus opencode run`.
457458
init [options] [repo] Initialize OpenCode commands and agent files for
458459
OpenCode++.
459-
doctor [options] [repo] Check whether OpenCode and the current repository are
460-
ready for the OpenCode preset.
460+
doctor [options] [repo] Check whether OpenCode, plugin version, and the
461+
current repository are ready for the OpenCode preset.
461462
report [options] [repo] Show the latest OpenCode orchestrator report without
462463
opening .agent-context manually.
463464
repair [options] [repo] Print repair guidance from the latest OpenCode
@@ -527,8 +528,8 @@ Options:
527528
```txt
528529
Usage: opencode-plusplus oc doctor [options] [repo]
529530
530-
Check whether OpenCode and the current repository are ready for the OpenCode
531-
preset.
531+
Check whether OpenCode, plugin version, and the current repository are ready for
532+
the OpenCode preset.
532533
533534
Arguments:
534535
repo repository path (default: ".")

src/cli/commands/doctor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export function registerDoctorCommand(program: Command): void {
66
.command("doctor")
77
.argument("[repo]", "repository path", ".")
88
.option("--json", "print machine-readable doctor report")
9-
.description("Check OpenCode, auth, git, context, and OpenCode++ sidecar readiness.")
9+
.description("Check OpenCode, auth, git, context, plugin version, and OpenCode++ sidecar readiness.")
1010
.action(async (repo: string, options: { json?: boolean }) => {
1111
const report = await runOpenCodePlusplusDoctor(repo);
1212
console.log(options.json ? JSON.stringify(report, null, 2) : renderOpenCodePlusplusDoctor(report));

src/cli/commands/opencode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ function addOpencodeDoctorCommand(parent: Command): void {
119119
.command("doctor")
120120
.argument("[repo]", "repository path", ".")
121121
.option("--json", "print machine-readable doctor report")
122-
.description("Check whether OpenCode and the current repository are ready for the OpenCode preset.")
122+
.description("Check whether OpenCode, plugin version, and the current repository are ready for the OpenCode preset.")
123123
.action((repo: string, options: { json?: boolean }) => {
124124
const report = runOpencodeDoctor(repo);
125125
console.log(options.json ? JSON.stringify(report, null, 2) : renderOpencodeDoctorReport(report));

src/cli/opencode-plusplus-commands.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { existsSync, readFileSync, statSync } from "node:fs";
22
import path from "node:path";
3+
import { getOpenCodePlusplusPackageVersion, OPENCODE_PLUSPLUS_PACKAGE_NAME } from "../core/package-info.js";
34
import { runOpencodeDoctor, type OpencodeDoctorCheck } from "./opencode-preset.js";
45
import { OPENCODE_SIDECAR_PLUGIN_PATH } from "../integrations/opencode/plugin-template.js";
56
import { verifyOpencodeSidecar } from "../integrations/opencode/sidecar.js";
@@ -69,7 +70,10 @@ export async function runOpenCodePlusplusDoctor(repo = "."): Promise<OpenCodePlu
6970
const opencode = runOpencodeDoctor(root);
7071
const sidecar = await verifyOpencodeSidecar(root);
7172
const sidecarPluginCheck = sidecar.checks.find((check) => check.name.endsWith("opencode-plusplus.ts"));
73+
const pluginPath = path.join(root, OPENCODE_SIDECAR_PLUGIN_PATH);
74+
const versionCheck = buildOpenCodePlusplusVersionCheck(pluginPath);
7275
const sidecarChecks: OpencodeDoctorCheck[] = [
76+
versionCheck,
7377
{
7478
id: "sidecar-plugin",
7579
label: "OpenCode++ sidecar plugin",
@@ -97,6 +101,49 @@ export async function runOpenCodePlusplusDoctor(repo = "."): Promise<OpenCodePlu
97101
return { repo: root, ok: checks.every((check) => check.status !== "fail"), checks };
98102
}
99103

104+
export interface OpenCodePlusplusPluginMetadata {
105+
generatedBy?: string;
106+
headerVersion?: string;
107+
constantVersion?: string;
108+
generatedAt?: string;
109+
}
110+
111+
export function readOpenCodePlusplusPluginMetadata(pluginPath: string): OpenCodePlusplusPluginMetadata | undefined {
112+
if (!existsSync(pluginPath)) return undefined;
113+
const source = readFileSync(pluginPath, "utf8");
114+
return {
115+
generatedBy: matchSource(source, /Generated by:\s*([^\r\n*]+)/),
116+
headerVersion: matchSource(source, /Package version:\s*([^\r\n*]+)/),
117+
generatedAt: matchSource(source, /(?:Generated at:\s*([^\r\n*]+)|OPENCODE_PLUSPLUS_GENERATED_AT\s*=\s*["']([^"']+)["'])/),
118+
constantVersion: matchSource(source, /OPENCODE_PLUSPLUS_PLUGIN_VERSION\s*=\s*["']([^"']+)["']/)
119+
};
120+
}
121+
122+
function buildOpenCodePlusplusVersionCheck(pluginPath: string): OpencodeDoctorCheck {
123+
const cliVersion = getOpenCodePlusplusPackageVersion();
124+
const metadata = readOpenCodePlusplusPluginMetadata(pluginPath);
125+
if (!metadata) {
126+
return {
127+
id: "opencode-plusplus-version",
128+
label: "CLI/plugin version",
129+
status: "warn",
130+
details: `CLI ${cliVersion}; sidecar plugin not generated yet`
131+
};
132+
}
133+
134+
const pluginVersion = metadata.constantVersion ?? metadata.headerVersion ?? "unknown";
135+
const generatedByCurrentCli =
136+
metadata.generatedBy === OPENCODE_PLUSPLUS_PACKAGE_NAME && metadata.headerVersion === cliVersion && metadata.constantVersion === cliVersion;
137+
return {
138+
id: "opencode-plusplus-version",
139+
label: "CLI/plugin version",
140+
status: generatedByCurrentCli ? "pass" : "warn",
141+
details: generatedByCurrentCli
142+
? `${cliVersion} (generated at ${metadata.generatedAt ?? "unknown"})`
143+
: `CLI ${cliVersion}; plugin ${pluginVersion}; generatedBy ${metadata.generatedBy ?? "unknown"}; generatedAt ${metadata.generatedAt ?? "unknown"}`
144+
};
145+
}
146+
100147
export function renderOpenCodePlusplusStatus(report: OpenCodePlusplusStatusReport): string {
101148
return [
102149
"OpenCode++ Status",
@@ -143,3 +190,8 @@ function readLatestJson(filePath: string): { blockers?: unknown } | null {
143190
return null;
144191
}
145192
}
193+
194+
function matchSource(source: string, pattern: RegExp): string | undefined {
195+
const match = source.match(pattern);
196+
return (match?.[1] ?? match?.[2])?.trim();
197+
}

src/core/package-info.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { existsSync, readFileSync } from "node:fs";
2+
import path from "node:path";
3+
import { fileURLToPath } from "node:url";
4+
5+
export const OPENCODE_PLUSPLUS_PACKAGE_NAME = "opencode-plusplus";
6+
7+
export function getOpenCodePlusplusPackageVersion(): string {
8+
const start = path.dirname(fileURLToPath(import.meta.url));
9+
const packagePath = findPackageJson(start);
10+
if (!packagePath) return "unknown";
11+
12+
try {
13+
const parsed = JSON.parse(readFileSync(packagePath, "utf8")) as { name?: unknown; version?: unknown };
14+
if (parsed.name === OPENCODE_PLUSPLUS_PACKAGE_NAME && typeof parsed.version === "string") return parsed.version;
15+
} catch {
16+
return "unknown";
17+
}
18+
return "unknown";
19+
}
20+
21+
function findPackageJson(start: string): string | undefined {
22+
let current = start;
23+
while (true) {
24+
const candidate = path.join(current, "package.json");
25+
if (existsSync(candidate)) return candidate;
26+
const parent = path.dirname(current);
27+
if (parent === current) return undefined;
28+
current = parent;
29+
}
30+
}

src/integrations/opencode/plugin-template.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,30 @@
1+
import { getOpenCodePlusplusPackageVersion, OPENCODE_PLUSPLUS_PACKAGE_NAME } from "../../core/package-info.js";
2+
13
export const OPENCODE_SIDECAR_PLUGIN_PATH = ".opencode/plugins/opencode-plusplus.ts";
24

3-
export function opencodeSidecarPluginTemplate(runtimeImport = opencodeSidecarRuntimeImport()): string {
5+
export interface OpenCodeSidecarPluginTemplateOptions {
6+
version?: string;
7+
generatedAt?: string;
8+
}
9+
10+
export function opencodeSidecarPluginTemplate(runtimeImport = opencodeSidecarRuntimeImport(), options: OpenCodeSidecarPluginTemplateOptions = {}): string {
11+
const version = options.version ?? getOpenCodePlusplusPackageVersion();
12+
const generatedAt = options.generatedAt ?? new Date().toISOString();
413
return `/**
514
* OpenCode++ OpenCode Sidecar Plugin
615
*
7-
* Generated by \`opencode-plusplus\`. Edit only when you want to customize how OpenCode
16+
* Generated by: ${OPENCODE_PLUSPLUS_PACKAGE_NAME}
17+
* Package version: ${version}
18+
* Generated at: ${generatedAt}
19+
*
20+
* Edit only when you want to customize how OpenCode
821
* presents the OpenCode++ harness inside this repository.
922
*/
1023
1124
import { createOpenCodePlusPlusSidecar } from ${JSON.stringify(runtimeImport)};
1225
26+
export const OPENCODE_PLUSPLUS_PLUGIN_VERSION = ${JSON.stringify(version)};
27+
export const OPENCODE_PLUSPLUS_GENERATED_AT = ${JSON.stringify(generatedAt)};
1328
export const OpenCodePlusPlusSidecar = createOpenCodePlusPlusSidecar;
1429
export default OpenCodePlusPlusSidecar;
1530
`;

0 commit comments

Comments
 (0)