@boyingliu01/opencode-plugin 0.10.9 → 0.10.11
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
|
@@ -55,7 +55,7 @@ async function fetchNpmLatestVersion(url: string): Promise<string | null> {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function readCache(file: string): { ts: number; remoteVersion: string; status?: string } | null {
|
|
58
|
+
function readCache(file: string): { ts: number; remoteVersion: string; localVersion?: string; status?: string } | null {
|
|
59
59
|
try {
|
|
60
60
|
if (!existsSync(file)) return null
|
|
61
61
|
const raw = readFileSync(file, "utf8")
|
|
@@ -100,11 +100,16 @@ function getLocalXpGateVersion(): string | null {
|
|
|
100
100
|
|
|
101
101
|
async function checkXpGateUpdate(): Promise<UpgradeResult> {
|
|
102
102
|
const cached = readCache(XP_GATE_CACHE_FILE)
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
const localVersion = getLocalXpGateVersion()
|
|
104
|
+
|
|
105
|
+
// If cache exists and local version hasn't changed AND status is "current",
|
|
106
|
+
// the cache is still valid — skip network check
|
|
107
|
+
if (cached?.status === "current" && cached.remoteVersion && localVersion && cached.localVersion === localVersion) {
|
|
108
|
+
return { action: "noop", localVersion, remoteVersion: cached.remoteVersion }
|
|
105
109
|
}
|
|
106
110
|
|
|
107
|
-
|
|
111
|
+
// If we have a valid cache but local version changed (e.g., manual upgrade)
|
|
112
|
+
// or cache has no "current" status, we need to re-check remote
|
|
108
113
|
if (!localVersion) return { action: "noop", localVersion: null, remoteVersion: null }
|
|
109
114
|
|
|
110
115
|
const remoteVersion = await fetchNpmLatestVersion(XP_GATE_REGISTRY_URL)
|
|
@@ -124,17 +129,19 @@ async function checkXpGateUpdate(): Promise<UpgradeResult> {
|
|
|
124
129
|
|
|
125
130
|
writeCache(XP_GATE_CACHE_FILE, { ts: Date.now(), localVersion, remoteVersion })
|
|
126
131
|
try {
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
132
|
+
const exitCode = await new Promise<number | null>((resolve, reject) => {
|
|
133
|
+
const child = spawn("npm", ["install", "-g", `${XP_GATE_NPM_PKG}@${remoteVersion}`], {
|
|
134
|
+
stdio: "pipe",
|
|
135
|
+
timeout: 120_000,
|
|
136
|
+
})
|
|
137
|
+
child.on("close", (code) => resolve(code))
|
|
138
|
+
child.on("error", (err) => reject(err))
|
|
135
139
|
})
|
|
136
|
-
|
|
137
|
-
|
|
140
|
+
if (exitCode === 0) {
|
|
141
|
+
writeCache(XP_GATE_CACHE_FILE, { ts: Date.now(), localVersion: remoteVersion, remoteVersion, status: "current" })
|
|
142
|
+
return { action: "upgraded", localVersion, remoteVersion }
|
|
143
|
+
}
|
|
144
|
+
return { action: "error", localVersion, remoteVersion, error: `npm install exited with code ${exitCode}` }
|
|
138
145
|
} catch (err) {
|
|
139
146
|
const msg = err instanceof Error ? err.message : String(err)
|
|
140
147
|
return { action: "error", localVersion, remoteVersion, error: msg }
|
|
@@ -174,6 +181,22 @@ async function checkPluginUpdate(pluginDir: string): Promise<void> {
|
|
|
174
181
|
}
|
|
175
182
|
}
|
|
176
183
|
|
|
184
|
+
// ── TUI upgrade notice ──
|
|
185
|
+
|
|
186
|
+
const UPGRADE_NOTICE_FILE = join(homedir(), ".xp-gate", "upgrade-notice.json")
|
|
187
|
+
|
|
188
|
+
type UpgradeNotice = {
|
|
189
|
+
kind: "upgraded" | "outdated" | "error"
|
|
190
|
+
localVersion: string | null
|
|
191
|
+
remoteVersion: string | null
|
|
192
|
+
message: string
|
|
193
|
+
ts: number
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function writeUpgradeNotice(notice: UpgradeNotice): void {
|
|
197
|
+
writeCache(UPGRADE_NOTICE_FILE, notice)
|
|
198
|
+
}
|
|
199
|
+
|
|
177
200
|
// ── Combined background check (runs once on first chat.message) ──
|
|
178
201
|
|
|
179
202
|
async function runBackgroundUpdates(pluginDir: string): Promise<string | null> {
|
|
@@ -181,10 +204,19 @@ async function runBackgroundUpdates(pluginDir: string): Promise<string | null> {
|
|
|
181
204
|
await checkPluginUpdate(pluginDir)
|
|
182
205
|
|
|
183
206
|
if (result.action === "upgraded") {
|
|
184
|
-
|
|
207
|
+
const msg = `[XP-Gate] Auto-upgraded from v${result.localVersion} to v${result.remoteVersion}`
|
|
208
|
+
writeUpgradeNotice({ kind: "upgraded", localVersion: result.localVersion, remoteVersion: result.remoteVersion, message: msg, ts: Date.now() })
|
|
209
|
+
return msg
|
|
185
210
|
}
|
|
186
211
|
if (result.action === "error") {
|
|
187
|
-
|
|
212
|
+
const msg = `[XP-Gate] Upgrade check: v${result.remoteVersion} available (auto-upgrade failed: ${result.error})`
|
|
213
|
+
writeUpgradeNotice({ kind: "error", localVersion: result.localVersion, remoteVersion: result.remoteVersion, message: msg, ts: Date.now() })
|
|
214
|
+
return msg
|
|
215
|
+
}
|
|
216
|
+
if (result.remoteVersion && result.localVersion && semverLt(result.localVersion, result.remoteVersion)) {
|
|
217
|
+
const msg = `[XP-Gate] New version v${result.remoteVersion} available (you have v${result.localVersion})`
|
|
218
|
+
writeUpgradeNotice({ kind: "outdated", localVersion: result.localVersion, remoteVersion: result.remoteVersion, message: msg, ts: Date.now() })
|
|
219
|
+
return msg
|
|
188
220
|
}
|
|
189
221
|
return null
|
|
190
222
|
}
|
|
@@ -289,11 +321,9 @@ export const XpGatePlugin = async (input: OpenCodePluginInput) => {
|
|
|
289
321
|
"chat.message": async (_input: { message: string }) => {
|
|
290
322
|
if (!checked) {
|
|
291
323
|
checked = true
|
|
292
|
-
runBackgroundUpdates(directory).
|
|
293
|
-
|
|
294
|
-
})
|
|
324
|
+
const msg = await runBackgroundUpdates(directory).catch(() => null)
|
|
325
|
+
if (msg) process.stderr.write(`${msg}\n`)
|
|
295
326
|
}
|
|
296
|
-
return { action: "continue" }
|
|
297
327
|
},
|
|
298
328
|
}
|
|
299
329
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-06-23
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** bac916f
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.10.
|
|
6
|
+
**Version:** 0.10.11.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-23
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** bac916f
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.10.
|
|
6
|
+
**Version:** 0.10.11.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-23
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** bac916f
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.10.
|
|
6
|
+
**Version:** 0.10.11.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
package/tui-plugin.ts
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { existsSync, readFileSync, readdirSync } from "node:fs"
|
|
19
19
|
import { join, dirname, resolve, parse } from "node:path"
|
|
20
|
+
import { homedir } from "node:os"
|
|
20
21
|
import type { TuiPlugin, TuiSlotPlugin, TuiSlotProps } from "@opencode-ai/plugin/tui"
|
|
21
22
|
|
|
22
23
|
// ── Phase constants (inlined from ../../src/npm-package/lib/shared-phase-constants.js) ──
|
|
@@ -174,7 +175,7 @@ function discoverActiveSprints(dir: string): DiscoveredSprint[] {
|
|
|
174
175
|
|
|
175
176
|
// ── Cache (module-level, 5s TTL) ──
|
|
176
177
|
|
|
177
|
-
let _cache: { data: DiscoveredSprint[]; ts: number; dir: string } | null = null;
|
|
178
|
+
let _cache: { data: DiscoveredSprint[]; upgradeNotice: string | null; ts: number; dir: string } | null = null;
|
|
178
179
|
const CACHE_TTL_MS = 5_000;
|
|
179
180
|
|
|
180
181
|
// ── Helpers ──
|
|
@@ -328,6 +329,45 @@ function renderMultiSprintSidebar(sprints: DiscoveredSprint[]): string | null {
|
|
|
328
329
|
return blocks.join("\n---\n");
|
|
329
330
|
}
|
|
330
331
|
|
|
332
|
+
function renderContent(sprints: DiscoveredSprint[], upgradeNotice: string | null): string | null {
|
|
333
|
+
const sprintContent = renderMultiSprintSidebar(sprints)
|
|
334
|
+
if (upgradeNotice && sprintContent) return `${upgradeNotice}\n---\n${sprintContent}`
|
|
335
|
+
if (upgradeNotice) return upgradeNotice
|
|
336
|
+
return sprintContent
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ── Upgrade notice ──
|
|
340
|
+
|
|
341
|
+
const UPGRADE_NOTICE_FILE = join(homedir(), ".xp-gate", "upgrade-notice.json")
|
|
342
|
+
const UPGRADE_NOTICE_TTL_MS = 86_400_000 // 24h
|
|
343
|
+
|
|
344
|
+
type UpgradeNotice = {
|
|
345
|
+
kind: string
|
|
346
|
+
localVersion: string | null
|
|
347
|
+
remoteVersion: string | null
|
|
348
|
+
message: string
|
|
349
|
+
ts: number
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function readUpgradeNotice(): UpgradeNotice | null {
|
|
353
|
+
try {
|
|
354
|
+
if (!existsSync(UPGRADE_NOTICE_FILE)) return null
|
|
355
|
+
const raw = readFileSync(UPGRADE_NOTICE_FILE, "utf8")
|
|
356
|
+
const data = JSON.parse(raw) as UpgradeNotice
|
|
357
|
+
if (Date.now() - data.ts < UPGRADE_NOTICE_TTL_MS && data.message) return data
|
|
358
|
+
return null
|
|
359
|
+
} catch {
|
|
360
|
+
return null
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function renderUpgradeNotice(): string | null {
|
|
365
|
+
const notice = readUpgradeNotice()
|
|
366
|
+
if (!notice) return null
|
|
367
|
+
const icon = notice.kind === "upgraded" ? "✓" : notice.kind === "outdated" ? "↑" : "⚠"
|
|
368
|
+
return `${icon} ${notice.message}`
|
|
369
|
+
}
|
|
370
|
+
|
|
331
371
|
// ── TUI Slot Plugin ──
|
|
332
372
|
|
|
333
373
|
const tuiPlugin: TuiSlotPlugin = {
|
|
@@ -338,12 +378,13 @@ const tuiPlugin: TuiSlotPlugin = {
|
|
|
338
378
|
|
|
339
379
|
// Use cache if still valid for current directory
|
|
340
380
|
if (_cache && _cache.dir === dir && now - _cache.ts < CACHE_TTL_MS) {
|
|
341
|
-
return
|
|
381
|
+
return renderContent(_cache.data, _cache.upgradeNotice);
|
|
342
382
|
}
|
|
343
383
|
|
|
344
384
|
const sprints = discoverActiveSprints(dir);
|
|
345
|
-
|
|
346
|
-
|
|
385
|
+
const upgradeNotice = renderUpgradeNotice()
|
|
386
|
+
_cache = { data: sprints, ts: now, dir, upgradeNotice };
|
|
387
|
+
return renderContent(sprints, upgradeNotice);
|
|
347
388
|
},
|
|
348
389
|
},
|
|
349
390
|
}
|