@boyingliu01/opencode-plugin 0.8.12 → 0.8.13
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
|
@@ -1,33 +1,68 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* XP-Gate OpenCode Plugin
|
|
3
|
-
*
|
|
4
|
-
* Exposes 3 OpenCode tools that mirror the equivalent `xp-gate` CLI subcommands:
|
|
5
|
-
* - gate-check: Run user-invokable quality gates (Gate 4 Principles + Gate 6 Arch) on a path
|
|
6
|
-
* - gate-principles: Run Clean Code + SOLID principles checker (Gate 4 standalone)
|
|
7
|
-
* - gate-arch: Run architecture validation (Gate 6 standalone)
|
|
8
|
-
*
|
|
9
|
-
* Dual-surface design (fixes #208): every tool is callable BOTH from inside an
|
|
10
|
-
* OpenCode session (as these tools) AND from a plain shell (as `xp-gate check`,
|
|
11
|
-
* `xp-gate principles`, `xp-gate arch`). The tools prefer the global `xp-gate`
|
|
12
|
-
* CLI when available, but fall back to running the checker source directly via
|
|
13
|
-
* `npx -y tsx` so they work even before `npm install -g @boyingliu01/xp-gate`.
|
|
14
|
-
*/
|
|
15
1
|
import { tool } from "@opencode-ai/plugin"
|
|
16
2
|
import { z } from "zod"
|
|
3
|
+
import { exec } from "child_process"
|
|
4
|
+
import { promisify } from "util"
|
|
5
|
+
|
|
6
|
+
const execAsync = promisify(exec)
|
|
17
7
|
|
|
18
8
|
interface OpenCodePluginInput {
|
|
19
9
|
directory: string
|
|
20
10
|
$: (strings: TemplateStringsArray, ...values: unknown[]) => Promise<{ text(): Promise<string> }>
|
|
21
11
|
}
|
|
22
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Run a shell command via async exec, returning stdout or error message.
|
|
15
|
+
* Never throws — returns error string on failure.
|
|
16
|
+
*/
|
|
17
|
+
async function runCmd(cmd: string, cwd: string): Promise<string> {
|
|
18
|
+
try {
|
|
19
|
+
const { stdout } = await execAsync(cmd, { cwd, timeout: 30000 })
|
|
20
|
+
return stdout || "[XP-Gate] Command completed (no output)."
|
|
21
|
+
} catch (err: any) {
|
|
22
|
+
if (err.stderr) return err.stderr
|
|
23
|
+
if (err.message) return `[XP-Gate] Error: ${err.message}`
|
|
24
|
+
return "[XP-Gate] Command failed."
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check for xp-gate CLI availability and run a command via exec.
|
|
30
|
+
*/
|
|
31
|
+
async function runXpGate(subcommand: string, cwd: string): Promise<string> {
|
|
32
|
+
// Check if xp-gate is on PATH
|
|
33
|
+
try {
|
|
34
|
+
await execAsync("command -v xp-gate", { cwd })
|
|
35
|
+
} catch {
|
|
36
|
+
return "" // CLI not available — caller should fall back
|
|
37
|
+
}
|
|
38
|
+
return runCmd(`xp-gate ${subcommand}`, cwd)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check for a newer xp-gate version (non-blocking, advisory only).
|
|
43
|
+
*/
|
|
44
|
+
async function getUpgradeSuggestion(cwd: string): Promise<string> {
|
|
45
|
+
try {
|
|
46
|
+
const result = await runXpGate("upgrade --preview", cwd)
|
|
47
|
+
if (!result) return ""
|
|
48
|
+
const parsed = JSON.parse(result)
|
|
49
|
+
if (parsed.outdated && parsed.remote) {
|
|
50
|
+
const releaseUrl = `https://github.com/boyingliu01/xp-gate/releases/tag/v${parsed.remote}`
|
|
51
|
+
return `[XP-Gate] New version v${parsed.remote} available (${releaseUrl}) — run: xp-gate upgrade`
|
|
52
|
+
}
|
|
53
|
+
return ""
|
|
54
|
+
} catch {
|
|
55
|
+
return ""
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
23
59
|
export const XpGatePlugin = async (input: OpenCodePluginInput) => {
|
|
24
60
|
const { directory, $ } = input
|
|
25
61
|
|
|
26
62
|
return {
|
|
27
63
|
tool: {
|
|
28
64
|
"gate-check": tool({
|
|
29
|
-
description:
|
|
30
|
-
"Run xp-gate user-invokable quality gates (Gate 4 Principles + Gate 6 Architecture) on a file or directory. Prefers global xp-gate CLI; falls back to running checker source directly.",
|
|
65
|
+
description: "Run xp-gate quality gates (Gate 4 + Gate 6) on a file or directory. Prefers global xp-gate CLI; falls back to direct checker source.",
|
|
31
66
|
args: {
|
|
32
67
|
path: z.string().describe("File or directory path (absolute or relative to workspace)"),
|
|
33
68
|
gates: z.array(z.string()).optional().describe("Optional gate subset (e.g. ['principles', 'arch'])"),
|
|
@@ -36,57 +71,50 @@ export const XpGatePlugin = async (input: OpenCodePluginInput) => {
|
|
|
36
71
|
const cwd = ctx.directory || directory
|
|
37
72
|
const target = args.path.startsWith("/") ? args.path : `${cwd}/${args.path}`
|
|
38
73
|
const gatesFlag = args.gates?.length ? ` --gates ${args.gates.join(",")}` : ""
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
const result = await $`bash -c ${cmd}`
|
|
45
|
-
const text = await result.text()
|
|
46
|
-
return text || "[XP-Gate] Check complete (no violations)."
|
|
47
|
-
} catch (err) {
|
|
48
|
-
return `[XP-Gate] gate-check failed.\nInstall xp-gate CLI: npm install -g @boyingliu01/xp-gate\n${err instanceof Error ? err.message : ""}`
|
|
74
|
+
const result = await runXpGate(`check "${target}"${gatesFlag}`, cwd)
|
|
75
|
+
if (result) {
|
|
76
|
+
const upgrade = await getUpgradeSuggestion(cwd)
|
|
77
|
+
return upgrade ? `${result}\n${upgrade}` : result
|
|
49
78
|
}
|
|
79
|
+
// Fallback: invoke xp-gate source directly via node
|
|
80
|
+
const cmd = `node ${directory}/src/npm-package/bin/xp-gate.js check "${target}"${gatesFlag}`
|
|
81
|
+
return runCmd(cmd, cwd)
|
|
50
82
|
},
|
|
51
83
|
}),
|
|
52
84
|
"gate-principles": tool({
|
|
53
|
-
description:
|
|
54
|
-
"Run Clean Code + SOLID principles checker (Gate 4 standalone) on a file or directory.",
|
|
85
|
+
description: "Run Clean Code + SOLID principles checker (Gate 4 standalone) on a file or directory.",
|
|
55
86
|
args: {
|
|
56
87
|
path: z.string().describe("Source file or directory path to check"),
|
|
57
88
|
},
|
|
58
89
|
async execute(args, ctx) {
|
|
59
90
|
const cwd = ctx.directory || directory
|
|
60
91
|
const target = args.path.startsWith("/") ? args.path : `${cwd}/${args.path}`
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const text = await result.text()
|
|
66
|
-
return text || "[XP-Gate] Principles check complete (no violations)."
|
|
67
|
-
} catch (err) {
|
|
68
|
-
return `[XP-Gate] Principles checker failed.\nInstall xp-gate CLI: npm install -g @boyingliu01/xp-gate\n${err instanceof Error ? err.message : ""}`
|
|
92
|
+
const result = await runXpGate(`principles "${target}"`, cwd)
|
|
93
|
+
if (result) {
|
|
94
|
+
const upgrade = await getUpgradeSuggestion(cwd)
|
|
95
|
+
return upgrade ? `${result}\n${upgrade}` : result
|
|
69
96
|
}
|
|
97
|
+
// Fallback: npx tsx on the principles source
|
|
98
|
+
const cmd = `npx -y tsx ${directory}/src/principles/index.ts --files "${target}" --format console`
|
|
99
|
+
return runCmd(cmd, cwd)
|
|
70
100
|
},
|
|
71
101
|
}),
|
|
72
102
|
"gate-arch": tool({
|
|
73
|
-
description:
|
|
74
|
-
"Run architecture validation (Gate 6 standalone, layer boundary checks) on the repository.",
|
|
103
|
+
description: "Run architecture validation (Gate 6 standalone, layer boundary checks) on the repository.",
|
|
75
104
|
args: {
|
|
76
105
|
config: z.string().describe("Path to architecture config file").default("architecture.yaml"),
|
|
77
106
|
},
|
|
78
107
|
async execute(args, ctx) {
|
|
79
108
|
const cwd = ctx.directory || directory
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const text = await result.text()
|
|
86
|
-
return text || "[XP-Gate] Architecture check complete."
|
|
87
|
-
} catch (err) {
|
|
88
|
-
return `[XP-Gate] Architecture validation failed.\nRequires architecture.yaml in repo root.\nInstall xp-gate CLI: npm install -g @boyingliu01/xp-gate\n${err instanceof Error ? err.message : ""}`
|
|
109
|
+
const config = args.config || "architecture.yaml"
|
|
110
|
+
const result = await runXpGate(`arch --config ${config}`, cwd)
|
|
111
|
+
if (result) {
|
|
112
|
+
const upgrade = await getUpgradeSuggestion(cwd)
|
|
113
|
+
return upgrade ? `${result}\n${upgrade}` : result
|
|
89
114
|
}
|
|
115
|
+
// Fallback: @archlinter/cli directly
|
|
116
|
+
const cmd = `npx -y @archlinter/cli scan . --config ${config}`
|
|
117
|
+
return runCmd(cmd, cwd)
|
|
90
118
|
},
|
|
91
119
|
}),
|
|
92
120
|
},
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-06-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-06-16
|
|
4
|
+
**Commit:** b1d8786
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.8.
|
|
6
|
+
**Version:** 0.8.13.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
|
-
**Generated:** 2026-06-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-06-16
|
|
4
|
+
**Commit:** b1d8786
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.8.
|
|
6
|
+
**Version:** 0.8.13.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
|
-
**Generated:** 2026-06-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-06-16
|
|
4
|
+
**Commit:** b1d8786
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.8.
|
|
6
|
+
**Version:** 0.8.13.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|