@boyingliu01/xp-gate 0.35.11 → 0.35.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/hooks/pre-commit +35 -4
- package/lib/mattpocock-license-verdict.json +1 -1
- 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/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/index.d.ts +21 -0
- package/plugins/dsh/lib/index.d.ts.map +1 -1
- package/plugins/dsh/lib/index.js +23 -5
- 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/dsh/src/index.test.ts +18 -1
- package/plugins/dsh/src/index.ts +24 -5
- package/plugins/opencode/index.ts +8 -4
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/semver.test.ts +64 -0
- 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/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/hooks/pre-commit
CHANGED
|
@@ -100,8 +100,16 @@ fi
|
|
|
100
100
|
# and a plausible wrong number is harder to notice than a visible gap.
|
|
101
101
|
#
|
|
102
102
|
# Resolve from the repo's own declaration, in the order the tools themselves
|
|
103
|
-
# use: pyproject [tool.coverage.report] fail_under, then .coveragerc
|
|
104
|
-
# declaring
|
|
103
|
+
# use: pyproject [tool.coverage.report] fail_under, then .coveragerc, then the
|
|
104
|
+
# JS/TS runner's own threshold block. A repo declaring none keeps the historical 80.
|
|
105
|
+
#
|
|
106
|
+
# #605: the JS/TS branch below enforces THIS number, but the resolver only ever
|
|
107
|
+
# read Python declarations -- so a vitest project declaring `thresholds.lines: 60`
|
|
108
|
+
# was silently judged against 80. The result was the worst possible split:
|
|
109
|
+
# `npx vitest run` goes green, the commit is then rejected, and the two numbers
|
|
110
|
+
# are read from different files with nothing pointing at the disagreement.
|
|
111
|
+
# Same defect shape as #554/#609: a declaration exists, an enforcement exists,
|
|
112
|
+
# and nothing connected them.
|
|
105
113
|
_resolve_coverage_threshold() {
|
|
106
114
|
_rct_value=""
|
|
107
115
|
_rct_src="default"
|
|
@@ -117,13 +125,36 @@ _resolve_coverage_threshold() {
|
|
|
117
125
|
_rct_value=$(grep -oE '^[[:space:]]*fail_under[[:space:]]*=[[:space:]]*[0-9]+' .coveragerc 2>/dev/null | grep -oE '[0-9]+' | head -1 || true)
|
|
118
126
|
[ -n "$_rct_value" ] && _rct_src=".coveragerc fail_under"
|
|
119
127
|
fi
|
|
128
|
+
# #605: JS/TS. Read the runner's own threshold rather than assuming 80, so the
|
|
129
|
+
# hook enforces the number the project actually declares. `lines` specifically:
|
|
130
|
+
# it is the metric the JS branch below compares, and it is the one vitest docs
|
|
131
|
+
# lead with. Only the FLAT shape is read -- the nested `thresholds: { global }`
|
|
132
|
+
# form is vitest 2.x and was proven silently dropped by the 1.6.x config parser
|
|
133
|
+
# (#561), so honouring it here would enforce a number that never takes effect.
|
|
134
|
+
if [ -z "$_rct_value" ]; then
|
|
135
|
+
for _rct_cfg in vitest.config.ts vitest.config.js vitest.config.mts; do
|
|
136
|
+
[ -f "$_rct_cfg" ] || continue
|
|
137
|
+
_rct_value=$(sed -n '/thresholds[[:space:]]*:[[:space:]]*{/,/}/p' "$_rct_cfg" 2>/dev/null \
|
|
138
|
+
| grep -oE 'lines[[:space:]]*:[[:space:]]*[0-9]+' | grep -oE '[0-9]+' | head -1 || true)
|
|
139
|
+
if [ -n "$_rct_value" ]; then
|
|
140
|
+
_rct_src="$_rct_cfg coverage.thresholds.lines"
|
|
141
|
+
break
|
|
142
|
+
fi
|
|
143
|
+
done
|
|
144
|
+
fi
|
|
120
145
|
case "$_rct_value" in
|
|
121
|
-
''|*[!0-9]*) _rct_value="80"; _rct_src="default (no
|
|
146
|
+
''|*[!0-9]*) _rct_value="80"; _rct_src="default (no coverage threshold declared)" ;;
|
|
122
147
|
esac
|
|
123
148
|
COVERAGE_THRESHOLD_SOURCE="$_rct_src"
|
|
124
149
|
printf '%s' "$_rct_value"
|
|
125
150
|
}
|
|
126
|
-
COVERAGE_THRESHOLD="$(_resolve_coverage_threshold)"
|
|
151
|
+
# NOT `COVERAGE_THRESHOLD="$(_resolve_coverage_threshold)"`: command substitution
|
|
152
|
+
# runs in a subshell, so `COVERAGE_THRESHOLD_SOURCE` set inside the function was
|
|
153
|
+
# discarded and the audit record's `threshold_source` field was empty on every
|
|
154
|
+
# single commit. A field that is always blank is worse than a missing one -- it
|
|
155
|
+
# looks populated and says nothing. Call the function in this shell instead.
|
|
156
|
+
_resolve_coverage_threshold >/dev/null
|
|
157
|
+
COVERAGE_THRESHOLD="$_rct_value"
|
|
127
158
|
|
|
128
159
|
# Look up one tier's directory variable by name without eval-ing arbitrary input:
|
|
129
160
|
# the base names are a closed set baked into the generated file.
|
package/mock-policy/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SRC/MOCK-POLICY KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Mock layering policy enforcement — Gate M3 of pre-push hook. Ensures integration tests use real implementations for internal dependencies, mock external dependencies, and annotate pending mocks with removal plans. Combines project scope scanning, mock decision engine, and per-file validation into a single pipeline.
|
package/mutation/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SRC/MUTATION KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**Gate M** (incremental mutation testing) + **Gate M2** helpers (test-layer detection used by `src/mock-policy/`). Pre-push quality gate. TypeScript-only; uses Stryker.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xp-gate",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.13",
|
|
4
4
|
"displayName": "XP-Gate",
|
|
5
5
|
"description": "Extreme Programming quality gates + AI workflow skills for Claude Code. 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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
2
|
import type { Context } from "@deepseek-ai/cordis";
|
|
3
|
+
/**
|
|
4
|
+
* Renders a tool's string result as plain text.
|
|
5
|
+
*
|
|
6
|
+
* Extracted because five tools had written this identical closure inline, which
|
|
7
|
+
* left five separate uncovered functions in the coverage report for one line of
|
|
8
|
+
* behaviour (#562). One definition can be tested once and reads as what it is: a
|
|
9
|
+
* shared output convention, not five coincidences.
|
|
10
|
+
*
|
|
11
|
+
* Generic in the argument type on purpose. `defineTool` types `render` as
|
|
12
|
+
* `(args: <this tool's parameters>, value: string) => ContentBlock[]`, so a
|
|
13
|
+
* `(_args: unknown, ...)` implementation is NOT assignable to it -- `unknown` is
|
|
14
|
+
* the wrong direction for a parameter. Declaring it as `A` lets every call site
|
|
15
|
+
* infer its own argument shape while the return type stays a plain text block.
|
|
16
|
+
*
|
|
17
|
+
* This mismatch was caught by `tsc` in the publish job, not by the test suite:
|
|
18
|
+
* vitest transpiles without type-checking. A green test run is not a green build.
|
|
19
|
+
*/
|
|
20
|
+
export declare const renderText: <A>(_args: A, value: string) => {
|
|
21
|
+
type: "text";
|
|
22
|
+
text: string;
|
|
23
|
+
}[];
|
|
3
24
|
export declare const name = "tool-xp-gate";
|
|
4
25
|
export declare const inject: string[];
|
|
5
26
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,CAAC,MAAM,0BAA0B,CAAA;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AA0BlD,eAAO,MAAM,IAAI,iBAAiB,CAAA;AAGlC,eAAO,MAAM,MAAM,UAA+B,CAAA;AAsZlD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA4BjB,CAAA;AAEF,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,OAAY,GAAG,IAAI,CAgC9D"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,CAAC,MAAM,0BAA0B,CAAA;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AA0BlD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,MAAM;;;GAA6C,CAAA;AAElG,eAAO,MAAM,IAAI,iBAAiB,CAAA;AAGlC,eAAO,MAAM,MAAM,UAA+B,CAAA;AAsZlD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA4BjB,CAAA;AAEF,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,OAAY,GAAG,IAAI,CAgC9D"}
|
package/plugins/dsh/lib/index.js
CHANGED
|
@@ -15,6 +15,24 @@ import { delphiGuardDecision, isCheckablePath, isEditTool } from "./hooks.js";
|
|
|
15
15
|
import { PHASE_STATUSES, SPRINT_SUBCOMMANDS, buildSprintCommand } from "./sprint.js";
|
|
16
16
|
import { createSkillProvider } from "./skill-provider.js";
|
|
17
17
|
import { createCredentialResolver } from "./credentials.js";
|
|
18
|
+
/**
|
|
19
|
+
* Renders a tool's string result as plain text.
|
|
20
|
+
*
|
|
21
|
+
* Extracted because five tools had written this identical closure inline, which
|
|
22
|
+
* left five separate uncovered functions in the coverage report for one line of
|
|
23
|
+
* behaviour (#562). One definition can be tested once and reads as what it is: a
|
|
24
|
+
* shared output convention, not five coincidences.
|
|
25
|
+
*
|
|
26
|
+
* Generic in the argument type on purpose. `defineTool` types `render` as
|
|
27
|
+
* `(args: <this tool's parameters>, value: string) => ContentBlock[]`, so a
|
|
28
|
+
* `(_args: unknown, ...)` implementation is NOT assignable to it -- `unknown` is
|
|
29
|
+
* the wrong direction for a parameter. Declaring it as `A` lets every call site
|
|
30
|
+
* infer its own argument shape while the return type stays a plain text block.
|
|
31
|
+
*
|
|
32
|
+
* This mismatch was caught by `tsc` in the publish job, not by the test suite:
|
|
33
|
+
* vitest transpiles without type-checking. A green test run is not a green build.
|
|
34
|
+
*/
|
|
35
|
+
export const renderText = (_args, value) => [{ type: "text", text: value }];
|
|
18
36
|
export const name = "tool-xp-gate";
|
|
19
37
|
// "skills" is required: the bundled skill provider registers during apply(), and
|
|
20
38
|
// without it ctx.skills may not be mounted yet when this plugin starts.
|
|
@@ -68,7 +86,7 @@ function gateCheckTool(shell, dialect) {
|
|
|
68
86
|
},
|
|
69
87
|
output: {
|
|
70
88
|
schema: { type: "string" },
|
|
71
|
-
render:
|
|
89
|
+
render: renderText,
|
|
72
90
|
},
|
|
73
91
|
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
74
92
|
async execute(args, exec) {
|
|
@@ -92,7 +110,7 @@ function gatePrinciplesTool(shell, dialect) {
|
|
|
92
110
|
},
|
|
93
111
|
output: {
|
|
94
112
|
schema: { type: "string" },
|
|
95
|
-
render:
|
|
113
|
+
render: renderText,
|
|
96
114
|
},
|
|
97
115
|
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
98
116
|
async execute(args, exec) {
|
|
@@ -115,7 +133,7 @@ function gateArchTool(shell, dialect) {
|
|
|
115
133
|
},
|
|
116
134
|
output: {
|
|
117
135
|
schema: { type: "string" },
|
|
118
|
-
render:
|
|
136
|
+
render: renderText,
|
|
119
137
|
},
|
|
120
138
|
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
121
139
|
async execute(args, exec) {
|
|
@@ -297,7 +315,7 @@ function delphiReviewTool(resolveApiKey, settings) {
|
|
|
297
315
|
},
|
|
298
316
|
output: {
|
|
299
317
|
schema: { type: "string" },
|
|
300
|
-
render:
|
|
318
|
+
render: renderText,
|
|
301
319
|
},
|
|
302
320
|
timeoutMs: DELPHI_TIMEOUT_MS,
|
|
303
321
|
async execute(args, exec) {
|
|
@@ -348,7 +366,7 @@ function sprintTool(shell, dialect) {
|
|
|
348
366
|
},
|
|
349
367
|
output: {
|
|
350
368
|
schema: { type: "string" },
|
|
351
|
-
render:
|
|
369
|
+
render: renderText,
|
|
352
370
|
},
|
|
353
371
|
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
354
372
|
async execute(args, exec) {
|
package/plugins/dsh/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boyingliu01/dsh-plugin-xp-gate",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.13",
|
|
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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
@@ -2,7 +2,7 @@ import { describe, it, expect } from "vitest"
|
|
|
2
2
|
import type { Context } from "@deepseek-ai/cordis"
|
|
3
3
|
import type { ShellExecRequest, ShellExecSpec, ShellRunResult } from "@deepseek-ai/dsh-shell"
|
|
4
4
|
import type { SkillProvider, SkillProviderControl, SkillProviderObservation } from "@deepseek-ai/dsh-skill"
|
|
5
|
-
import { apply, inject } from "./index.js"
|
|
5
|
+
import { apply, inject, renderText } from "./index.js"
|
|
6
6
|
|
|
7
7
|
type ProviderFactory = (control: SkillProviderControl) => SkillProvider
|
|
8
8
|
|
|
@@ -169,4 +169,21 @@ describe("apply", () => {
|
|
|
169
169
|
await tool.execute({ config: "custom.yaml" }, { signal: new AbortController().signal })
|
|
170
170
|
expect(requests[1]?.command).toContain("xp-gate arch --use-config 'custom.yaml'")
|
|
171
171
|
})
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @test REQ-562
|
|
175
|
+
* @intent 5 个工具曾各自内联一份完全相同的 render 闭包,在覆盖率报告里表现为 5 个
|
|
176
|
+
* 未覆盖函数,而实际只有一行行为(#562)。提取为共享常量后只需测一次,
|
|
177
|
+
* 也让"共享的输出约定"这件事在代码里显形,而不是 5 个巧合。
|
|
178
|
+
* @covers AC-562-04
|
|
179
|
+
*/
|
|
180
|
+
it("renderText wraps a tool result as a single text part", () => {
|
|
181
|
+
expect(renderText(undefined, "hello")).toEqual([{ type: "text", text: "hello" }])
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it("renderText keeps empty output as an empty text part, not a dropped one", () => {
|
|
185
|
+
// A tool that legitimately produced nothing must still render one part; returning
|
|
186
|
+
// [] would look to the host like the tool never answered.
|
|
187
|
+
expect(renderText({ path: "x" }, "")).toEqual([{ type: "text", text: "" }])
|
|
188
|
+
})
|
|
172
189
|
})
|
package/plugins/dsh/src/index.ts
CHANGED
|
@@ -32,6 +32,25 @@ import { createSkillProvider } from "./skill-provider.js"
|
|
|
32
32
|
import type { ApiKeyResolver } from "./credentials.js"
|
|
33
33
|
import { createCredentialResolver } from "./credentials.js"
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Renders a tool's string result as plain text.
|
|
37
|
+
*
|
|
38
|
+
* Extracted because five tools had written this identical closure inline, which
|
|
39
|
+
* left five separate uncovered functions in the coverage report for one line of
|
|
40
|
+
* behaviour (#562). One definition can be tested once and reads as what it is: a
|
|
41
|
+
* shared output convention, not five coincidences.
|
|
42
|
+
*
|
|
43
|
+
* Generic in the argument type on purpose. `defineTool` types `render` as
|
|
44
|
+
* `(args: <this tool's parameters>, value: string) => ContentBlock[]`, so a
|
|
45
|
+
* `(_args: unknown, ...)` implementation is NOT assignable to it -- `unknown` is
|
|
46
|
+
* the wrong direction for a parameter. Declaring it as `A` lets every call site
|
|
47
|
+
* infer its own argument shape while the return type stays a plain text block.
|
|
48
|
+
*
|
|
49
|
+
* This mismatch was caught by `tsc` in the publish job, not by the test suite:
|
|
50
|
+
* vitest transpiles without type-checking. A green test run is not a green build.
|
|
51
|
+
*/
|
|
52
|
+
export const renderText = <A>(_args: A, value: string) => [{ type: "text" as const, text: value }]
|
|
53
|
+
|
|
35
54
|
export const name = "tool-xp-gate"
|
|
36
55
|
// "skills" is required: the bundled skill provider registers during apply(), and
|
|
37
56
|
// without it ctx.skills may not be mounted yet when this plugin starts.
|
|
@@ -92,7 +111,7 @@ function gateCheckTool(shell: Context["shell"], dialect: ShellDialect) {
|
|
|
92
111
|
},
|
|
93
112
|
output: {
|
|
94
113
|
schema: { type: "string" },
|
|
95
|
-
render:
|
|
114
|
+
render: renderText,
|
|
96
115
|
},
|
|
97
116
|
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
98
117
|
async execute(args, exec) {
|
|
@@ -118,7 +137,7 @@ function gatePrinciplesTool(shell: Context["shell"], dialect: ShellDialect) {
|
|
|
118
137
|
},
|
|
119
138
|
output: {
|
|
120
139
|
schema: { type: "string" },
|
|
121
|
-
render:
|
|
140
|
+
render: renderText,
|
|
122
141
|
},
|
|
123
142
|
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
124
143
|
async execute(args, exec) {
|
|
@@ -144,7 +163,7 @@ function gateArchTool(shell: Context["shell"], dialect: ShellDialect) {
|
|
|
144
163
|
},
|
|
145
164
|
output: {
|
|
146
165
|
schema: { type: "string" },
|
|
147
|
-
render:
|
|
166
|
+
render: renderText,
|
|
148
167
|
},
|
|
149
168
|
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
150
169
|
async execute(args, exec) {
|
|
@@ -352,7 +371,7 @@ function delphiReviewTool(resolveApiKey: ApiKeyResolver, settings: DelphiSetting
|
|
|
352
371
|
},
|
|
353
372
|
output: {
|
|
354
373
|
schema: { type: "string" },
|
|
355
|
-
render:
|
|
374
|
+
render: renderText,
|
|
356
375
|
},
|
|
357
376
|
timeoutMs: DELPHI_TIMEOUT_MS,
|
|
358
377
|
async execute(args, exec) {
|
|
@@ -405,7 +424,7 @@ function sprintTool(shell: Context["shell"], dialect: ShellDialect) {
|
|
|
405
424
|
},
|
|
406
425
|
output: {
|
|
407
426
|
schema: { type: "string" },
|
|
408
|
-
render:
|
|
427
|
+
render: renderText,
|
|
409
428
|
},
|
|
410
429
|
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
411
430
|
async execute(args, exec) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin"
|
|
2
2
|
import { z } from "zod"
|
|
3
|
-
import { exec, execSync, spawn } from "node:child_process"
|
|
3
|
+
import { exec, execFileSync, execSync, spawn } from "node:child_process"
|
|
4
4
|
import { promisify } from "node:util"
|
|
5
5
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, rmSync } from "node:fs"
|
|
6
6
|
import { join } from "node:path"
|
|
@@ -345,7 +345,7 @@ function generateSessionTitle(dbPath: string, sessionId: string): string {
|
|
|
345
345
|
ORDER BY m.time_created DESC
|
|
346
346
|
LIMIT 10
|
|
347
347
|
`
|
|
348
|
-
const raw =
|
|
348
|
+
const raw = execFileSync("sqlite3", [dbPath, sql], {
|
|
349
349
|
encoding: "utf8",
|
|
350
350
|
timeout: 5000,
|
|
351
351
|
})
|
|
@@ -492,7 +492,11 @@ function writeSessionModel(dbPath: string, sessionId: string, modelJson: string)
|
|
|
492
492
|
const escapedModel = modelJson.replace(/'/g, "''")
|
|
493
493
|
const sql = `UPDATE session SET model = '${escapedModel}', time_updated = ${Date.now()} WHERE id = '${escapedId}'`
|
|
494
494
|
try {
|
|
495
|
-
execSync
|
|
495
|
+
// execFileSync, NOT execSync: `dbPath` was interpolated into a shell string with
|
|
496
|
+
// only double quotes for protection, so a path containing `"` or `$()` broke out
|
|
497
|
+
// into shell (semgrep security-audit flagged it as a command-injection sink).
|
|
498
|
+
// Passing the arguments as an array removes the shell from the picture entirely.
|
|
499
|
+
execFileSync("sqlite3", [dbPath, sql], { timeout: SQLITE_TIMEOUT_MS })
|
|
496
500
|
} catch (err) {
|
|
497
501
|
// Contract is to propagate: the caller renders the failure as
|
|
498
502
|
// "Session reload failed: <message>". Wrapped so the IO sits inside
|
|
@@ -616,7 +620,7 @@ function renameSession(sessionId: string, newTitle?: string): string {
|
|
|
616
620
|
const escapedId = sessionId.replace(/'/g, "''")
|
|
617
621
|
const sql = `UPDATE session SET title = '${escapedTitle}', time_updated = ${Date.now()} WHERE id = '${escapedId}'`
|
|
618
622
|
try {
|
|
619
|
-
|
|
623
|
+
execFileSync("sqlite3", [dbPath, sql], { timeout: SQLITE_TIMEOUT_MS })
|
|
620
624
|
return `Session renamed to: "${trimmed}"`
|
|
621
625
|
} catch (err) {
|
|
622
626
|
const msg = err instanceof Error ? err.message : String(err)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @test REQ-562 插件版本比较必须正确,否则升级提示会撒谎
|
|
3
|
+
* @intent `plugins/opencode/index.ts` 有 4 个 export,函数覆盖率 50%,且**没有任何
|
|
4
|
+
* 测试文件**。它不是可选的样板:`semverLt` 决定"要不要提示用户升级",
|
|
5
|
+
* 在 3 处被调用(第 143/249/289 行)。
|
|
6
|
+
*
|
|
7
|
+
* 一个错的 `semverLt` 不会崩溃,只会让提示永远说"已是最新"或永远提示升级——
|
|
8
|
+
* 两种都是"看起来在工作"。这类函数正是最该被钉住的:纯、无副作用、
|
|
9
|
+
* 影响用户可见的行为。
|
|
10
|
+
*
|
|
11
|
+
* 这里只测导出的纯函数,不碰网络和文件系统——测试不该依赖 npm registry
|
|
12
|
+
* 是否在线(那会让 CI 在断网时红,而问题不在代码里)。
|
|
13
|
+
* @covers AC-562-03
|
|
14
|
+
*/
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
const { fileURLToPath } = require('node:url');
|
|
19
|
+
|
|
20
|
+
const PLUGIN_ENTRY = path.resolve(path.dirname(fileURLToPath(import.meta.url)), 'index.ts');
|
|
21
|
+
|
|
22
|
+
describe('opencode 插件版本比较 (#562)', () => {
|
|
23
|
+
let mod;
|
|
24
|
+
|
|
25
|
+
beforeAll(async () => {
|
|
26
|
+
// The plugin is TypeScript with an npm dependency on @opencode-ai/plugin, which
|
|
27
|
+
// is only present in that workspace. Import through tsx so the test itself stays
|
|
28
|
+
// runnable from the root suite.
|
|
29
|
+
mod = await import(PLUGIN_ENTRY);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('AC-562-03: 暴露了纯函数测试钩子', () => {
|
|
33
|
+
expect(mod.__test).toBeDefined();
|
|
34
|
+
expect(typeof mod.__test.semverLt).toBe('function');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('AC-562-03: 主版本更高时判定为更小', () => {
|
|
38
|
+
expect(mod.__test.semverLt('0.35.10', '0.36.0')).toBe(true);
|
|
39
|
+
expect(mod.__test.semverLt('0.36.0', '0.35.10')).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('AC-562-03: patch 与 minor 的位数差异不被误判', () => {
|
|
43
|
+
// This is the case a naive string comparison gets wrong: "0.9.0" < "0.10.0"
|
|
44
|
+
// lexicographically is 9 < 1 == false. Numeric segment compare must win.
|
|
45
|
+
expect(mod.__test.semverLt('0.9.0', '0.10.0')).toBe(true);
|
|
46
|
+
expect(mod.__test.semverLt('0.10.0', '0.9.0')).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('AC-562-03: 前导 v 与缺失段被正确处理', () => {
|
|
50
|
+
expect(mod.__test.semverLt('v0.35.10', '0.35.11')).toBe(true);
|
|
51
|
+
// "0.35" is treated as 0.35.0, so it is LESS than 0.35.1 -- solid? No: it is
|
|
52
|
+
// equal to 0.35.0, and 0.35 < 0.35.1. Asserting the documented behaviour keeps
|
|
53
|
+
// a future refactor from silently changing missing-segment semantics.
|
|
54
|
+
expect(mod.__test.semverLt('0.35', '0.35.1')).toBe(true);
|
|
55
|
+
expect(mod.__test.semverLt('0.35.1', '0.35')).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('AC-562-03: 相等版本不判定为更小', () => {
|
|
59
|
+
// `upToDate = !semverLt(...)` -- if equal versions compared as "less", every
|
|
60
|
+
// run would tell the user an update is available when there is none.
|
|
61
|
+
expect(mod.__test.semverLt('0.35.11', '0.35.11')).toBe(false);
|
|
62
|
+
expect(mod.__test.semverLt('v0.35.11', '0.35.11')).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xp-gate",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.13",
|
|
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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 3978d361
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.35.
|
|
6
|
+
**Version:** 0.35.13.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|