@boyingliu01/opencode-plugin 0.8.16 → 0.8.17
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/index.ts
CHANGED
|
@@ -2,6 +2,9 @@ import { tool } from "@opencode-ai/plugin"
|
|
|
2
2
|
import { z } from "zod"
|
|
3
3
|
import { exec } from "child_process"
|
|
4
4
|
import { promisify } from "util"
|
|
5
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs"
|
|
6
|
+
import { join } from "node:path"
|
|
7
|
+
import { homedir } from "node:os"
|
|
5
8
|
|
|
6
9
|
const execAsync = promisify(exec)
|
|
7
10
|
|
|
@@ -10,10 +13,6 @@ interface OpenCodePluginInput {
|
|
|
10
13
|
$: (strings: TemplateStringsArray, ...values: unknown[]) => Promise<{ text(): Promise<string> }>
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
/**
|
|
14
|
-
* Run a shell command via async exec, returning stdout or error message.
|
|
15
|
-
* Never throws — returns error string on failure.
|
|
16
|
-
*/
|
|
17
16
|
async function runCmd(cmd: string, cwd: string): Promise<string> {
|
|
18
17
|
try {
|
|
19
18
|
const { stdout } = await execAsync(cmd, { cwd, timeout: 30000 })
|
|
@@ -26,22 +25,15 @@ async function runCmd(cmd: string, cwd: string): Promise<string> {
|
|
|
26
25
|
}
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
/**
|
|
30
|
-
* Check for xp-gate CLI availability and run a command via exec.
|
|
31
|
-
*/
|
|
32
28
|
async function runXpGate(subcommand: string, cwd: string): Promise<string> {
|
|
33
|
-
// Check if xp-gate is on PATH
|
|
34
29
|
try {
|
|
35
30
|
await execAsync("command -v xp-gate", { cwd })
|
|
36
31
|
} catch {
|
|
37
|
-
return ""
|
|
32
|
+
return ""
|
|
38
33
|
}
|
|
39
34
|
return runCmd(`xp-gate ${subcommand}`, cwd)
|
|
40
35
|
}
|
|
41
36
|
|
|
42
|
-
/**
|
|
43
|
-
* Check for a newer xp-gate version (non-blocking, advisory only).
|
|
44
|
-
*/
|
|
45
37
|
async function getUpgradeSuggestion(cwd: string): Promise<string> {
|
|
46
38
|
try {
|
|
47
39
|
const result = await runXpGate("upgrade --preview", cwd)
|
|
@@ -57,6 +49,79 @@ async function getUpgradeSuggestion(cwd: string): Promise<string> {
|
|
|
57
49
|
}
|
|
58
50
|
}
|
|
59
51
|
|
|
52
|
+
// ── Auto-update check for opencode-plugin ──
|
|
53
|
+
|
|
54
|
+
const CACHE_TTL_MS = 86_400_000
|
|
55
|
+
const NPM_REGISTRY_URL = "https://registry.npmjs.org/-/package/@boyingliu01%2Fopencode-plugin/dist-tags"
|
|
56
|
+
const FETCH_TIMEOUT_MS = 5_000
|
|
57
|
+
const CACHE_FILE = join(homedir(), ".xp-gate", "opencode-plugin-version-check.json")
|
|
58
|
+
|
|
59
|
+
let checked = false
|
|
60
|
+
let checkInFlight: Promise<void> | null = null
|
|
61
|
+
|
|
62
|
+
function semverLt(a: string, b: string): boolean {
|
|
63
|
+
const pa = a.replace(/^v/, "").split(".").map(Number)
|
|
64
|
+
const pb = b.replace(/^v/, "").split(".").map(Number)
|
|
65
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
66
|
+
const na = pa[i] ?? 0
|
|
67
|
+
const nb = pb[i] ?? 0
|
|
68
|
+
if (na !== nb) return na < nb
|
|
69
|
+
}
|
|
70
|
+
return false
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function checkPluginUpdate(pluginDir: string): Promise<void> {
|
|
74
|
+
if (checkInFlight) return
|
|
75
|
+
|
|
76
|
+
checkInFlight = (async () => {
|
|
77
|
+
try {
|
|
78
|
+
mkdirSync(join(homedir(), ".xp-gate"), { recursive: true })
|
|
79
|
+
|
|
80
|
+
if (existsSync(CACHE_FILE)) {
|
|
81
|
+
const cached = JSON.parse(readFileSync(CACHE_FILE, "utf8"))
|
|
82
|
+
if (Date.now() - cached.ts < CACHE_TTL_MS) return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let localVersion = ""
|
|
86
|
+
try {
|
|
87
|
+
const pkg = JSON.parse(readFileSync(join(pluginDir, "package.json"), "utf8"))
|
|
88
|
+
localVersion = pkg.version || ""
|
|
89
|
+
} catch {
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const controller = new AbortController()
|
|
94
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS)
|
|
95
|
+
try {
|
|
96
|
+
const response = await fetch(NPM_REGISTRY_URL, { signal: controller.signal })
|
|
97
|
+
if (!response.ok) return
|
|
98
|
+
const data: Record<string, unknown> = await response.json()
|
|
99
|
+
const remoteVersion = String(data.latest || "")
|
|
100
|
+
|
|
101
|
+
if (remoteVersion && localVersion && semverLt(localVersion, remoteVersion)) {
|
|
102
|
+
writeFileSync(CACHE_FILE, JSON.stringify({ ts: Date.now(), localVersion, remoteVersion }))
|
|
103
|
+
process.stderr.write(
|
|
104
|
+
`[XP-Gate] New opencode-plugin version v${remoteVersion} available (you have v${localVersion})\n` +
|
|
105
|
+
`[XP-Gate] Update with: cd ~/.config/opencode && npm update @boyingliu01/opencode-plugin\n`
|
|
106
|
+
)
|
|
107
|
+
} else if (remoteVersion && localVersion) {
|
|
108
|
+
// Cache "up to date" to avoid re-fetching every session
|
|
109
|
+
writeFileSync(CACHE_FILE, JSON.stringify({ ts: Date.now(), localVersion, remoteVersion, status: "current" }))
|
|
110
|
+
}
|
|
111
|
+
} finally {
|
|
112
|
+
clearTimeout(timer)
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
// All errors silently ignored
|
|
116
|
+
}
|
|
117
|
+
})()
|
|
118
|
+
|
|
119
|
+
await checkInFlight
|
|
120
|
+
checkInFlight = null
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ── Plugin definition ──
|
|
124
|
+
|
|
60
125
|
export const XpGatePlugin = async (input: OpenCodePluginInput) => {
|
|
61
126
|
const { directory } = input
|
|
62
127
|
|
|
@@ -77,7 +142,6 @@ export const XpGatePlugin = async (input: OpenCodePluginInput) => {
|
|
|
77
142
|
const upgrade = await getUpgradeSuggestion(cwd)
|
|
78
143
|
return upgrade ? `${result}\n${upgrade}` : result
|
|
79
144
|
}
|
|
80
|
-
// Fallback: invoke xp-gate source directly via node
|
|
81
145
|
const cmd = `node ${directory}/src/npm-package/bin/xp-gate.js check "${target}"${gatesFlag}`
|
|
82
146
|
return runCmd(cmd, cwd)
|
|
83
147
|
},
|
|
@@ -95,7 +159,6 @@ export const XpGatePlugin = async (input: OpenCodePluginInput) => {
|
|
|
95
159
|
const upgrade = await getUpgradeSuggestion(cwd)
|
|
96
160
|
return upgrade ? `${result}\n${upgrade}` : result
|
|
97
161
|
}
|
|
98
|
-
// Fallback: npx tsx on the principles source
|
|
99
162
|
const cmd = `npx -y tsx ${directory}/src/principles/index.ts --files "${target}" --format console`
|
|
100
163
|
return runCmd(cmd, cwd)
|
|
101
164
|
},
|
|
@@ -113,12 +176,18 @@ export const XpGatePlugin = async (input: OpenCodePluginInput) => {
|
|
|
113
176
|
const upgrade = await getUpgradeSuggestion(cwd)
|
|
114
177
|
return upgrade ? `${result}\n${upgrade}` : result
|
|
115
178
|
}
|
|
116
|
-
// Fallback: @archlinter/cli directly
|
|
117
179
|
const cmd = `npx -y @archlinter/cli scan . --config ${config}`
|
|
118
180
|
return runCmd(cmd, cwd)
|
|
119
181
|
},
|
|
120
182
|
}),
|
|
121
183
|
},
|
|
184
|
+
"chat.message": async (_input: { message: string }) => {
|
|
185
|
+
if (!checked) {
|
|
186
|
+
checked = true
|
|
187
|
+
checkPluginUpdate(directory).catch((_err) => { /* silent: non-critical background check */ })
|
|
188
|
+
}
|
|
189
|
+
return { action: "continue" }
|
|
190
|
+
},
|
|
122
191
|
}
|
|
123
192
|
}
|
|
124
193
|
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-06-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** d3a0242
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.8.
|
|
6
|
+
**Version:** 0.8.17.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-06-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** d3a0242
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.8.
|
|
6
|
+
**Version:** 0.8.17.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**11-phase** development pipeline: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → USER ACCEPTANCE → FEEDBACK → SHIP → LAND → CLEANUP. Phase 2 default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE in Phase 1: design must pass Delphi review (≥90% consensus) before any coding.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-06-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** d3a0242
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.8.
|
|
6
|
+
**Version:** 0.8.17.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|