@boyingliu01/xp-gate 0.27.0 → 0.27.2
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/adapter-common.sh +0 -0
- package/adapters/cpp.sh +0 -0
- package/adapters/dart.sh +0 -0
- package/adapters/flutter.sh +0 -0
- package/adapters/go.sh +0 -0
- package/adapters/iac.sh +0 -0
- package/adapters/java.sh +0 -0
- package/adapters/kotlin.sh +0 -0
- package/adapters/objectivec.sh +0 -0
- package/adapters/plugins/book299-20132-python/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-20132-python/scripts/install-maven.sh +0 -0
- package/adapters/plugins/book299-4081-c/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-4081-c/scripts/install-maven.sh +0 -0
- package/adapters/plugins/book299-4083-javascriptes5/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-4083-javascriptes5/scripts/install-maven.sh +0 -0
- package/adapters/powershell.sh +0 -0
- package/adapters/python.sh +0 -0
- package/adapters/shell.sh +0 -0
- package/adapters/swift.sh +0 -0
- package/adapters/typescript.sh +0 -0
- package/bin/xp-gate.js +0 -0
- package/gate-10.sh +0 -0
- package/gate-3.sh +0 -0
- package/gate-4.sh +0 -0
- package/gate-7.sh +0 -0
- package/gate-8.sh +0 -0
- package/gate-9.sh +0 -0
- package/hooks/adapter-common.sh +0 -0
- package/hooks/gate-10.sh +0 -0
- package/hooks/gate-3.sh +0 -0
- package/hooks/gate-4.sh +0 -0
- package/hooks/gate-7.sh +0 -0
- package/hooks/gate-8.sh +0 -0
- package/hooks/gate-9.sh +0 -0
- package/hooks/post-merge +0 -0
- package/hooks/pre-commit +0 -0
- package/hooks/pre-push +0 -0
- package/hooks/sprint-gate.sh +0 -0
- package/lib/__tests__/doctor-delphi.test.js +244 -244
- package/lib/__tests__/doctor-exec-retry.test.js +152 -0
- package/lib/__tests__/sprint-audit.test.js +207 -207
- package/lib/__tests__/windows-bash-probe.test.js +57 -7
- package/lib/detect-deps.js +69 -13
- package/lib/doctor.js +45 -11
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/sprint-audit.js +235 -235
- package/mock-policy/AGENTS.md +2 -2
- package/mutation/AGENTS.md +2 -2
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/bin/delphi-review-guard.sh +0 -0
- package/plugins/claude-code/bin/xp-gate-check +0 -0
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/dsh/lib/command.d.ts +32 -0
- package/plugins/dsh/lib/command.d.ts.map +1 -0
- package/plugins/dsh/lib/command.js +81 -0
- package/plugins/dsh/lib/delphi-config.d.ts +75 -0
- package/plugins/dsh/lib/delphi-config.d.ts.map +1 -0
- package/plugins/dsh/lib/delphi-config.js +155 -0
- package/plugins/dsh/lib/delphi-run.d.ts +24 -0
- package/plugins/dsh/lib/delphi-run.d.ts.map +1 -0
- package/plugins/dsh/lib/delphi-run.js +92 -0
- package/plugins/dsh/lib/delphi.d.ts +88 -0
- package/plugins/dsh/lib/delphi.d.ts.map +1 -0
- package/plugins/dsh/lib/delphi.js +161 -0
- package/plugins/dsh/lib/dialect.d.ts +24 -0
- package/plugins/dsh/lib/dialect.d.ts.map +1 -0
- package/plugins/dsh/lib/dialect.js +36 -0
- package/plugins/dsh/lib/gate-runner.d.ts +18 -0
- package/plugins/dsh/lib/gate-runner.d.ts.map +1 -0
- package/plugins/dsh/lib/gate-runner.js +46 -0
- package/plugins/dsh/lib/index.d.ts +5 -0
- package/plugins/dsh/lib/index.d.ts.map +1 -0
- package/plugins/dsh/lib/index.js +162 -0
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/qoder/bin/sprint-flow-guard.sh +0 -0
- package/plugins/qoder/bin/xp-gate-check +0 -0
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +2 -2
- package/principles/AGENTS.md +2 -2
- package/skills/delphi-review/AGENTS.md +2 -2
- package/skills/sprint-flow/AGENTS.md +2 -2
- package/skills/test-specification-alignment/AGENTS.md +2 -2
- package/sprint-gate.sh +0 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delphi cross-review over an external OpenAI-compatible provider.
|
|
3
|
+
*
|
|
4
|
+
* This is the codified form of the manual run that produced PR #456: three
|
|
5
|
+
* DISTINCT models, each asked the same question independently and anonymously,
|
|
6
|
+
* iterated until the approval ratio clears the consensus threshold.
|
|
7
|
+
*
|
|
8
|
+
* Why a plugin tool rather than a shell one-liner: the DSH tool plane is where
|
|
9
|
+
* a session actually lives, so the mechanism has to be callable from there.
|
|
10
|
+
* DSH's own `tool-subagent` cannot serve this purpose -- subagents inherit one
|
|
11
|
+
* host-configured model, so they cannot guarantee three distinct models, and
|
|
12
|
+
* Delphi's whole value rests on that distinctness.
|
|
13
|
+
*
|
|
14
|
+
* @test REQ-DSH-016
|
|
15
|
+
* @intent 用外部 provider 的多个不同模型做匿名交叉评审并判定共识,
|
|
16
|
+
* 把「多模型交叉评审」固化成 DSH 可调用的机制而非一次性脚本。
|
|
17
|
+
* @covers AC-DSH-016-01
|
|
18
|
+
*/
|
|
19
|
+
/** Delphi's defining invariant: exactly three seats and a 90% approval bar. */
|
|
20
|
+
const REQUIRED_EXPERTS = 3;
|
|
21
|
+
const DEFAULT_THRESHOLD_PERCENT = 90;
|
|
22
|
+
const DEFAULT_MAX_TOKENS = 8000;
|
|
23
|
+
const DEFAULT_TIMEOUT_MS = 180_000;
|
|
24
|
+
/** Raised when the configured seats cannot satisfy the Delphi invariants. */
|
|
25
|
+
export class DelphiConfigError extends Error {
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Reject a seat configuration that cannot produce a valid Delphi review.
|
|
29
|
+
* Checked BEFORE any network call so a misconfiguration never looks like a
|
|
30
|
+
* provider failure.
|
|
31
|
+
*/
|
|
32
|
+
export function validateSeats(seats) {
|
|
33
|
+
if (seats.length !== REQUIRED_EXPERTS) {
|
|
34
|
+
throw new DelphiConfigError(`Delphi requires exactly ${REQUIRED_EXPERTS} experts, got ${seats.length}.`);
|
|
35
|
+
}
|
|
36
|
+
for (const s of seats) {
|
|
37
|
+
if (!s.model || s.model.trim() === "") {
|
|
38
|
+
throw new DelphiConfigError(`Expert "${s.id}" has no model (a local fallback cannot count).`);
|
|
39
|
+
}
|
|
40
|
+
if (s.model.includes("${")) {
|
|
41
|
+
throw new DelphiConfigError(`Expert "${s.id}" model "${s.model}" looks like an unexpanded env var.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const distinct = new Set(seats.map((s) => s.model.trim()));
|
|
45
|
+
if (distinct.size !== REQUIRED_EXPERTS) {
|
|
46
|
+
throw new DelphiConfigError(`All ${REQUIRED_EXPERTS} experts must use distinct models; got: ${[...distinct].join(", ")}.`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Parse an expert report into a verdict.
|
|
51
|
+
*
|
|
52
|
+
* The verdict token usually sits on the line AFTER a "### VERDICT" heading, so
|
|
53
|
+
* a same-line `VERDICT: X` pattern misses every real report. A report with no
|
|
54
|
+
* recognisable verdict is INVALID, never a guess: inferring approval from prose
|
|
55
|
+
* would let the gate pass on an assumption.
|
|
56
|
+
*/
|
|
57
|
+
export function parseVerdict(text) {
|
|
58
|
+
const section = /VERDICT[\s\S]{0,120}?\b(APPROVED|REQUEST_CHANGES|PASS_WITH_CAVEATS|BLOCKED)\b/i.exec(text);
|
|
59
|
+
if (!section)
|
|
60
|
+
return { verdict: "INVALID_NO_VERDICT", confidence: 0 };
|
|
61
|
+
const raw = section[1].toUpperCase();
|
|
62
|
+
// PASS_WITH_CAVEATS / BLOCKED are not approvals under the 90% rule.
|
|
63
|
+
const verdict = raw === "APPROVED" ? "APPROVED" : "REQUEST_CHANGES";
|
|
64
|
+
const c = /CONFIDENCE[\s\S]{0,40}?(\d{1,2})/i.exec(text);
|
|
65
|
+
const confidence = c ? Math.min(10, Math.max(0, Number(c[1]))) : 0;
|
|
66
|
+
return { verdict, confidence };
|
|
67
|
+
}
|
|
68
|
+
/** One provider call. Throws on transport failure or empty content. */
|
|
69
|
+
async function chatOnce(opts, seat, system, user) {
|
|
70
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
71
|
+
const controller = new AbortController();
|
|
72
|
+
const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
73
|
+
try {
|
|
74
|
+
const res = await doFetch(`${opts.baseUrl.replace(/\/+$/, "")}/v1/chat/completions`, {
|
|
75
|
+
method: "POST",
|
|
76
|
+
headers: {
|
|
77
|
+
Authorization: `Bearer ${opts.apiKey}`,
|
|
78
|
+
"Content-Type": "application/json",
|
|
79
|
+
},
|
|
80
|
+
body: JSON.stringify({
|
|
81
|
+
model: seat.model,
|
|
82
|
+
messages: [
|
|
83
|
+
{ role: "system", content: system },
|
|
84
|
+
{ role: "user", content: user },
|
|
85
|
+
],
|
|
86
|
+
max_tokens: seat.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
87
|
+
temperature: 0.2,
|
|
88
|
+
}),
|
|
89
|
+
signal: controller.signal,
|
|
90
|
+
});
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
const body = await res.text().catch(() => "");
|
|
93
|
+
throw new Error(`HTTP ${res.status}: ${body.slice(0, 200)}`);
|
|
94
|
+
}
|
|
95
|
+
const json = (await res.json());
|
|
96
|
+
const content = json.choices?.[0]?.message?.content ?? "";
|
|
97
|
+
const finishReason = json.choices?.[0]?.finish_reason ?? "unknown";
|
|
98
|
+
// Empty content is a real failure -- typically a reasoning model that spent
|
|
99
|
+
// its whole budget on hidden tokens. Never let it pass as an empty review.
|
|
100
|
+
if (content.trim() === "") {
|
|
101
|
+
throw new Error(`empty content (finish_reason=${finishReason}, max_tokens=${seat.maxTokens ?? DEFAULT_MAX_TOKENS}); ` +
|
|
102
|
+
`raise max_tokens if this is a reasoning model`);
|
|
103
|
+
}
|
|
104
|
+
return { content, resolvedModel: json.model ?? null, finishReason };
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
clearTimeout(timer);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Run ONE round. All seats are asked concurrently and never see each other's
|
|
112
|
+
* output, which is what makes the round anonymous.
|
|
113
|
+
*/
|
|
114
|
+
export async function runRound(opts, round, system, buildPrompt) {
|
|
115
|
+
const settled = await Promise.allSettled(opts.seats.map(async (seat) => {
|
|
116
|
+
const r = await chatOnce(opts, seat, system, buildPrompt(seat));
|
|
117
|
+
return { seat, ...r };
|
|
118
|
+
}));
|
|
119
|
+
const verdicts = settled.map((s, i) => {
|
|
120
|
+
const seat = opts.seats[i];
|
|
121
|
+
if (s.status !== "fulfilled") {
|
|
122
|
+
return {
|
|
123
|
+
id: seat.id,
|
|
124
|
+
role: seat.role,
|
|
125
|
+
requestedModel: seat.model,
|
|
126
|
+
resolvedModel: null,
|
|
127
|
+
verdict: "ERROR",
|
|
128
|
+
confidence: 0,
|
|
129
|
+
counted: false,
|
|
130
|
+
report: "",
|
|
131
|
+
error: s.reason instanceof Error ? s.reason.message : String(s.reason),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const { verdict, confidence } = parseVerdict(s.value.content);
|
|
135
|
+
return {
|
|
136
|
+
id: seat.id,
|
|
137
|
+
role: seat.role,
|
|
138
|
+
requestedModel: seat.model,
|
|
139
|
+
resolvedModel: s.value.resolvedModel,
|
|
140
|
+
verdict,
|
|
141
|
+
confidence,
|
|
142
|
+
counted: verdict !== "INVALID_NO_VERDICT",
|
|
143
|
+
report: s.value.content,
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
const counted = verdicts.filter((v) => v.counted);
|
|
147
|
+
const approvals = counted.filter((v) => v.verdict === "APPROVED").length;
|
|
148
|
+
// Consensus is measured over VALID responses: a malformed report is not a
|
|
149
|
+
// dissent, but it cannot be counted as agreement either.
|
|
150
|
+
const ratio = counted.length === 0 ? 0 : approvals / counted.length;
|
|
151
|
+
const threshold = opts.thresholdPercent ?? DEFAULT_THRESHOLD_PERCENT;
|
|
152
|
+
return {
|
|
153
|
+
round,
|
|
154
|
+
verdicts,
|
|
155
|
+
validCount: counted.length,
|
|
156
|
+
approvals,
|
|
157
|
+
consensusRatio: ratio,
|
|
158
|
+
reachedConsensus: counted.length === REQUIRED_EXPERTS && ratio * 100 >= threshold,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=delphi.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell dialect selection and quoting.
|
|
3
|
+
*
|
|
4
|
+
* DSH chooses its shell backend per platform: `dsh-base` disables
|
|
5
|
+
* `bash-sandbox`/`tool-bash` and enables `pwsh-sandbox`/`tool-pwsh` on win32.
|
|
6
|
+
* A command string written for the wrong dialect fails to parse, so callers
|
|
7
|
+
* must pick the dialect to match the executor rather than assume POSIX.
|
|
8
|
+
*/
|
|
9
|
+
export type ShellDialect = "posix" | "powershell";
|
|
10
|
+
/** Quote one argument for the target dialect. */
|
|
11
|
+
export declare function shellQuote(value: string, dialect: ShellDialect): string;
|
|
12
|
+
/**
|
|
13
|
+
* POSIX single-quote shell escaping: `'` becomes `'\''`.
|
|
14
|
+
* @deprecated Use {@link shellQuote} with an explicit dialect.
|
|
15
|
+
*/
|
|
16
|
+
export declare function shq(value: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Pick the dialect for the current platform.
|
|
19
|
+
*
|
|
20
|
+
* `process.platform` is the reliable signal available inside a tool body; it
|
|
21
|
+
* mirrors exactly where DSH enables each sandbox/executor pair.
|
|
22
|
+
*/
|
|
23
|
+
export declare function detectDialect(platform?: NodeJS.Platform): ShellDialect;
|
|
24
|
+
//# sourceMappingURL=dialect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialect.d.ts","sourceRoot":"","sources":["../src/dialect.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,YAAY,CAAA;AAEjD,iDAAiD;AACjD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CASvE;AAED;;;GAGG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,YAAY,CAExF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell dialect selection and quoting.
|
|
3
|
+
*
|
|
4
|
+
* DSH chooses its shell backend per platform: `dsh-base` disables
|
|
5
|
+
* `bash-sandbox`/`tool-bash` and enables `pwsh-sandbox`/`tool-pwsh` on win32.
|
|
6
|
+
* A command string written for the wrong dialect fails to parse, so callers
|
|
7
|
+
* must pick the dialect to match the executor rather than assume POSIX.
|
|
8
|
+
*/
|
|
9
|
+
/** Quote one argument for the target dialect. */
|
|
10
|
+
export function shellQuote(value, dialect) {
|
|
11
|
+
if (dialect === "posix") {
|
|
12
|
+
// POSIX single-quote escaping: `'` becomes `'\''`.
|
|
13
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
14
|
+
}
|
|
15
|
+
// PowerShell single-quoted strings escape an embedded quote by doubling it.
|
|
16
|
+
// Backticks and `$(…)` are literal inside single quotes, so no further
|
|
17
|
+
// escaping is needed to keep them inert.
|
|
18
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* POSIX single-quote shell escaping: `'` becomes `'\''`.
|
|
22
|
+
* @deprecated Use {@link shellQuote} with an explicit dialect.
|
|
23
|
+
*/
|
|
24
|
+
export function shq(value) {
|
|
25
|
+
return shellQuote(value, "posix");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Pick the dialect for the current platform.
|
|
29
|
+
*
|
|
30
|
+
* `process.platform` is the reliable signal available inside a tool body; it
|
|
31
|
+
* mirrors exactly where DSH enables each sandbox/executor pair.
|
|
32
|
+
*/
|
|
33
|
+
export function detectDialect(platform = process.platform) {
|
|
34
|
+
return platform === "win32" ? "powershell" : "posix";
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=dialect.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ShellExecRequest, ShellExecSpec, ShellRunResult } from "@deepseek-ai/dsh-shell";
|
|
2
|
+
/**
|
|
3
|
+
* Structural view of `ctx.shell` so the runner is testable with a fake shell
|
|
4
|
+
* without importing the abstract `ShellExecutor` service.
|
|
5
|
+
*/
|
|
6
|
+
export interface XpGateShell {
|
|
7
|
+
resolve(request: ShellExecRequest): ShellExecSpec;
|
|
8
|
+
run(spec: ShellExecSpec): Promise<ShellRunResult>;
|
|
9
|
+
}
|
|
10
|
+
/** Render a completed shell run as a single model-facing text block. */
|
|
11
|
+
export declare function renderGateResult(result: ShellRunResult): string;
|
|
12
|
+
/**
|
|
13
|
+
* Run an xp-gate command through the harness shell seam. Nonzero exits, timeouts,
|
|
14
|
+
* and sandbox denials resolve (and render) rather than reject; only cancellation
|
|
15
|
+
* (the caller's AbortSignal fired first) throws an AbortError with code ABORTED.
|
|
16
|
+
*/
|
|
17
|
+
export declare function runXpGate(shell: XpGateShell, signal: AbortSignal, command: string, workdir: string, timeoutMs: number): Promise<string>;
|
|
18
|
+
//# sourceMappingURL=gate-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate-runner.d.ts","sourceRoot":"","sources":["../src/gate-runner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAE7F;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,aAAa,CAAA;IACjD,GAAG,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;CAClD;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAqB/D;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CASjB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { TOOL_ABORTED } from "@deepseek-ai/dsh-tools";
|
|
2
|
+
import { HarnessError } from "@deepseek-ai/dsh-llm";
|
|
3
|
+
/** Render a completed shell run as a single model-facing text block. */
|
|
4
|
+
export function renderGateResult(result) {
|
|
5
|
+
const lines = [];
|
|
6
|
+
const stdout = result.stdout.text.trim();
|
|
7
|
+
if (stdout.length > 0)
|
|
8
|
+
lines.push(stdout);
|
|
9
|
+
const stderr = result.stderr.text.trim();
|
|
10
|
+
if (stderr.length > 0)
|
|
11
|
+
lines.push(`[stderr] ${stderr}`);
|
|
12
|
+
if (result.timedOut)
|
|
13
|
+
lines.push(`[timed out after ${result.timeoutMs}ms]`);
|
|
14
|
+
if (result.exitCode !== null && result.exitCode !== 0)
|
|
15
|
+
lines.push(`[exit code: ${result.exitCode}]`);
|
|
16
|
+
if (result.signal !== null)
|
|
17
|
+
lines.push(`[signal: ${result.signal}]`);
|
|
18
|
+
if (result.sandbox?.denied === true) {
|
|
19
|
+
lines.push(`[sandbox: file access denied under ${result.sandbox.mode} mode]`);
|
|
20
|
+
}
|
|
21
|
+
if (result.stdout.truncated) {
|
|
22
|
+
const spill = result.stdout.spillPath !== undefined ? `, full output: ${result.stdout.spillPath}` : "";
|
|
23
|
+
lines.push(`[stdout truncated${spill}]`);
|
|
24
|
+
}
|
|
25
|
+
if (result.stderr.truncated) {
|
|
26
|
+
const spill = result.stderr.spillPath !== undefined ? `, full output: ${result.stderr.spillPath}` : "";
|
|
27
|
+
lines.push(`[stderr truncated${spill}]`);
|
|
28
|
+
}
|
|
29
|
+
return lines.length > 0 ? lines.join("\n") : "[XP-Gate] command completed (no output).";
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Run an xp-gate command through the harness shell seam. Nonzero exits, timeouts,
|
|
33
|
+
* and sandbox denials resolve (and render) rather than reject; only cancellation
|
|
34
|
+
* (the caller's AbortSignal fired first) throws an AbortError with code ABORTED.
|
|
35
|
+
*/
|
|
36
|
+
export async function runXpGate(shell, signal, command, workdir, timeoutMs) {
|
|
37
|
+
const spec = shell.resolve({ command, workdir, timeoutMs, signal });
|
|
38
|
+
const result = await shell.run(spec);
|
|
39
|
+
if (result.aborted) {
|
|
40
|
+
const error = new HarnessError("tool call aborted", TOOL_ABORTED);
|
|
41
|
+
error.name = "AbortError";
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
return renderGateResult(result);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=gate-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAOlD,eAAO,MAAM,IAAI,iBAAiB,CAAA;AAClC,eAAO,MAAM,MAAM,UAAqB,CAAA;AAuKxC,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,OAAY,GAAG,IAAI,CAS/D"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
2
|
+
import { GATE_WHITELIST, buildCommand, detectDialect, resolveTarget } from "./command.js";
|
|
3
|
+
import { runXpGate } from "./gate-runner.js";
|
|
4
|
+
import { DelphiSetupError, loadDelphiConfig, toDelphiOptions } from "./delphi-config.js";
|
|
5
|
+
import { formatOutcome, runReview } from "./delphi-run.js";
|
|
6
|
+
export const name = "tool-xp-gate";
|
|
7
|
+
export const inject = ["tools", "shell"];
|
|
8
|
+
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
9
|
+
// Multi-round reviews over large inputs are slow; the transport applies its
|
|
10
|
+
// own per-request timeout on top of this ceiling.
|
|
11
|
+
const DELPHI_TIMEOUT_MS = 900_000;
|
|
12
|
+
// Boy Scout (#447): gate-check's `gates` parameter nests one level too deep
|
|
13
|
+
// inline (parameters → gates → items), so the schema lives at module scope.
|
|
14
|
+
const GATES_PARAMETER = {
|
|
15
|
+
type: "array",
|
|
16
|
+
items: { type: "string", enum: GATE_WHITELIST },
|
|
17
|
+
description: "Optional gate subset to run (e.g. ['principles', 'arch']).",
|
|
18
|
+
};
|
|
19
|
+
// Boy Scout (#447): apply() used to inline four register(defineTool({...}))
|
|
20
|
+
// blocks (149 lines, 5 brace levels). Each tool now lives in its own factory;
|
|
21
|
+
// apply() is pure wiring.
|
|
22
|
+
function gateCheckTool(shell, dialect) {
|
|
23
|
+
return defineTool({
|
|
24
|
+
name: "gate-check",
|
|
25
|
+
description: "Run XP-Gate quality gates (xp-gate check) on a file or directory and return the gate report. Prefers the global xp-gate CLI and degrades to an actionable install hint when it is absent.",
|
|
26
|
+
parameters: {
|
|
27
|
+
path: {
|
|
28
|
+
type: "string",
|
|
29
|
+
required: true,
|
|
30
|
+
description: "File or directory path to check (absolute or relative to the workspace).",
|
|
31
|
+
},
|
|
32
|
+
gates: GATES_PARAMETER,
|
|
33
|
+
},
|
|
34
|
+
output: {
|
|
35
|
+
schema: { type: "string" },
|
|
36
|
+
render: (_args, value) => [{ type: "text", text: value }],
|
|
37
|
+
},
|
|
38
|
+
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
39
|
+
async execute(args, exec) {
|
|
40
|
+
const cwd = exec.agent?.session?.header?.cwd ?? process.cwd();
|
|
41
|
+
const target = resolveTarget(args.path, cwd);
|
|
42
|
+
const command = buildCommand({ subcommand: "check", target, gates: args.gates, dialect });
|
|
43
|
+
return runXpGate(shell, exec.signal, command, cwd, DEFAULT_TIMEOUT_MS);
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function gatePrinciplesTool(shell, dialect) {
|
|
48
|
+
return defineTool({
|
|
49
|
+
name: "gate-principles",
|
|
50
|
+
description: "Run the 14 Clean Code / SOLID rules (xp-gate principles, Gate 4) on a file or directory and return the principles report.",
|
|
51
|
+
parameters: {
|
|
52
|
+
path: {
|
|
53
|
+
type: "string",
|
|
54
|
+
required: true,
|
|
55
|
+
description: "File or directory path to check (absolute or relative to the workspace).",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
output: {
|
|
59
|
+
schema: { type: "string" },
|
|
60
|
+
render: (_args, value) => [{ type: "text", text: value }],
|
|
61
|
+
},
|
|
62
|
+
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
63
|
+
async execute(args, exec) {
|
|
64
|
+
const cwd = exec.agent?.session?.header?.cwd ?? process.cwd();
|
|
65
|
+
const target = resolveTarget(args.path, cwd);
|
|
66
|
+
const command = buildCommand({ subcommand: "principles", target, dialect });
|
|
67
|
+
return runXpGate(shell, exec.signal, command, cwd, DEFAULT_TIMEOUT_MS);
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function gateArchTool(shell, dialect) {
|
|
72
|
+
return defineTool({
|
|
73
|
+
name: "gate-arch",
|
|
74
|
+
description: "Run architecture validation (xp-gate arch, Gate 6) with the pinned local @archlinter/cli built-in rules and baseline ratchet, and return the architecture report. Pass a config file only to opt into archlint's own --config path (pre-0.16 schemas are rejected by archlint itself).",
|
|
75
|
+
parameters: {
|
|
76
|
+
config: {
|
|
77
|
+
type: "string",
|
|
78
|
+
description: "Optional architecture config file (opts into archlint --config; the default built-in-rules path needs none).",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
output: {
|
|
82
|
+
schema: { type: "string" },
|
|
83
|
+
render: (_args, value) => [{ type: "text", text: value }],
|
|
84
|
+
},
|
|
85
|
+
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
86
|
+
async execute(args, exec) {
|
|
87
|
+
const cwd = exec.agent?.session?.header?.cwd ?? process.cwd();
|
|
88
|
+
const command = buildCommand({ subcommand: "arch", config: args.config, dialect });
|
|
89
|
+
return runXpGate(shell, exec.signal, command, cwd, DEFAULT_TIMEOUT_MS);
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// Delphi multi-model cross-review.
|
|
94
|
+
//
|
|
95
|
+
// DSH's own subagents cannot do this: they inherit one host-configured model,
|
|
96
|
+
// so three "experts" would be three instances of the same model, and Delphi's
|
|
97
|
+
// value rests entirely on the reviewers being DIFFERENT models. This tool
|
|
98
|
+
// therefore calls an external OpenAI-compatible provider directly, with one
|
|
99
|
+
// distinct model per seat.
|
|
100
|
+
// Boy Scout (#447): the review body used to sit inline in execute() at 5 brace
|
|
101
|
+
// levels (defineTool → execute → try → runReview options). Module scope keeps
|
|
102
|
+
// the try/catch semantics identical while dropping two levels.
|
|
103
|
+
async function runDelphiReview(subject, addressed, maxRounds, cwd) {
|
|
104
|
+
try {
|
|
105
|
+
const cfg = loadDelphiConfig(cwd);
|
|
106
|
+
const outcome = await runReview({ ...toDelphiOptions(cfg), maxRounds: maxRounds ?? cfg.maxRounds }, subject, addressed);
|
|
107
|
+
const seats = cfg.seats.map((s) => s.model).join(", ");
|
|
108
|
+
return `${formatOutcome(outcome)}\n\nProvider: ${cfg.provider}\nSeats: ${seats}`;
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
// Config problems are the common failure; report them as guidance
|
|
112
|
+
// rather than a stack trace.
|
|
113
|
+
if (e instanceof DelphiSetupError)
|
|
114
|
+
return `Delphi review not started: ${e.message}`;
|
|
115
|
+
throw e;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function delphiReviewTool() {
|
|
119
|
+
return defineTool({
|
|
120
|
+
name: "delphi-review",
|
|
121
|
+
description: "Run a Delphi cross-review of a subject (a diff, a design, a spec) using three DISTINCT models from an external OpenAI-compatible provider configured in .delphi-config.json. Round 1 is anonymous and concurrent; later rounds feed back the aggregate until the approval ratio reaches the configured threshold (default 90%) or the round cap (default 5). Use this whenever a change needs independent multi-model review rather than a single model's opinion.",
|
|
122
|
+
parameters: {
|
|
123
|
+
subject: {
|
|
124
|
+
type: "string",
|
|
125
|
+
required: true,
|
|
126
|
+
description: "The material to review, inline. For a diff, pass the diff text (or a summarized form if very large).",
|
|
127
|
+
},
|
|
128
|
+
addressed: {
|
|
129
|
+
type: "string",
|
|
130
|
+
description: "Optional description of what changed since a previous round, fed back to later rounds so reviewers can revise their position.",
|
|
131
|
+
},
|
|
132
|
+
maxRounds: {
|
|
133
|
+
type: "number",
|
|
134
|
+
description: "Override the configured round cap (Delphi default is 5).",
|
|
135
|
+
},
|
|
136
|
+
cwd: {
|
|
137
|
+
type: "string",
|
|
138
|
+
description: "Project root containing .delphi-config.json (defaults to the session cwd).",
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
output: {
|
|
142
|
+
schema: { type: "string" },
|
|
143
|
+
render: (_args, value) => [{ type: "text", text: value }],
|
|
144
|
+
},
|
|
145
|
+
timeoutMs: DELPHI_TIMEOUT_MS,
|
|
146
|
+
async execute(args, exec) {
|
|
147
|
+
const cwd = args.cwd ?? exec.agent?.session?.header?.cwd ?? process.cwd();
|
|
148
|
+
return runDelphiReview(args.subject, args.addressed ?? "", args.maxRounds, cwd);
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
export function apply(ctx, _config = {}) {
|
|
153
|
+
// DSH runs a POSIX shell off-Windows and PowerShell on Windows; generating the
|
|
154
|
+
// wrong dialect makes every tool fail to parse rather than degrade gracefully.
|
|
155
|
+
const dialect = detectDialect();
|
|
156
|
+
const shell = ctx.shell;
|
|
157
|
+
ctx.tools.register(gateCheckTool(shell, dialect));
|
|
158
|
+
ctx.tools.register(gatePrinciplesTool(shell, dialect));
|
|
159
|
+
ctx.tools.register(gateArchTool(shell, dialect));
|
|
160
|
+
ctx.tools.register(delphiReviewTool());
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=index.js.map
|
package/plugins/dsh/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boyingliu01/dsh-plugin-xp-gate",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.2",
|
|
4
4
|
"description": "XP-Gate deterministic quality gates + AI workflow skills as a DeepSeek Harness (DSH) native plugin: gate-check / gate-principles / gate-arch tools + 12 SKILL.md skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** a5ee6191
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** a5ee6191
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): 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-10-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** a5ee6191
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** a5ee6191
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** a5ee6191
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): 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-10-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** a5ee6191
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xp-gate",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.2",
|
|
4
4
|
"displayName": "XP-Gate",
|
|
5
5
|
"description": "Extreme Programming quality gates + AI workflow skills for Qoder. Includes 12 pre-commit gates (Gate 0-11), 8 pre-push gates, Sprint Flow (6 phases), and Delphi multi-expert review (>=90% consensus).",
|
|
6
6
|
"author": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** a5ee6191
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** a5ee6191
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): 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-10-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** a5ee6191
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
package/principles/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# PRINCIPLES CHECKER MODULE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** a5ee6191
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Clean Code & SOLID principles checker — **Gate 4** of pre-commit. 14 rules × 9 language adapters, SARIF 2.1.0 output. Houses the **Boy Scout Rule** enforcement engine (Gate 6) and warning-baseline storage.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-16
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** a5ee6191
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|