@boyingliu01/xp-gate 0.9.4 → 0.10.0
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/adapters/python.sh +61 -1
- package/lib/doctor.js +34 -18
- package/lib/shared-phase-constants.js +23 -9
- package/mock-policy/AGENTS.md +3 -3
- package/mutation/AGENTS.md +3 -3
- package/mutation/gate-m.ts +209 -274
- package/mutation/runners/index.ts +22 -0
- package/mutation/runners/mutmut-runner.ts +220 -0
- package/mutation/runners/stryker-runner.ts +146 -0
- package/mutation/runners/types.ts +99 -0
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/claude-code/skills/delphi-review/SKILL.md +34 -0
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/claude-code/skills/sprint-flow/SKILL.md +53 -1
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/opencode/__tests__/xp-gate-update.test.ts +1 -11
- package/plugins/opencode/index.ts +0 -1
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/opencode/skills/delphi-review/SKILL.md +34 -0
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/opencode/skills/sprint-flow/SKILL.md +53 -1
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/opencode/tui-plugin.ts +69 -6
- package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
- package/principles/AGENTS.md +3 -3
- package/skills/delphi-review/AGENTS.md +3 -3
- package/skills/delphi-review/SKILL.md +34 -0
- package/skills/sprint-flow/AGENTS.md +3 -3
- package/skills/sprint-flow/SKILL.md +53 -1
- package/skills/test-specification-alignment/AGENTS.md +3 -3
package/adapters/python.sh
CHANGED
|
@@ -101,4 +101,64 @@ run_coverage() {
|
|
|
101
101
|
fi
|
|
102
102
|
|
|
103
103
|
return $PYTEST_EXIT
|
|
104
|
-
}
|
|
104
|
+
}
|
|
105
|
+
# ── Gate M: Python mutation testing (mutmut) ──
|
|
106
|
+
|
|
107
|
+
run_mutation() {
|
|
108
|
+
local files_arg="$1"
|
|
109
|
+
local timeout_ms="${2:-120000}"
|
|
110
|
+
local timeout_s=$((timeout_ms / 1000))
|
|
111
|
+
|
|
112
|
+
if ! detect_python_mutation_testable; then
|
|
113
|
+
echo "⚠ mutmut not installed. SKIP — Gate M (Python)."
|
|
114
|
+
return 0
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# Parse comma-separated file list into mutmut --paths-to-mulate args
|
|
118
|
+
local file_list=""
|
|
119
|
+
IFS=',' read -ra FILE_ARRAY <<< "$files_arg"
|
|
120
|
+
for f in "${FILE_ARRAY[@]}"; do
|
|
121
|
+
f=$(echo "$f" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
122
|
+
[ -f "$f" ] && file_list="$file_list $f"
|
|
123
|
+
done
|
|
124
|
+
|
|
125
|
+
if [ -z "$file_list" ]; then
|
|
126
|
+
echo "📚 No valid Python source files for mutation. SKIP — Gate M (Python)."
|
|
127
|
+
return 0
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
echo "🐍 Running Python mutation testing (mutmut) on: $file_list"
|
|
131
|
+
|
|
132
|
+
# Gate M orchestrator (TypeScript) handles mutmut via MutmutRunner in src/mutation/runners/
|
|
133
|
+
# The shell adapter delegates to the TS runner for consistency with TS Gate M.
|
|
134
|
+
if [ -f "src/mutation/gate-m.ts" ]; then
|
|
135
|
+
timeout "${timeout_s}s" npx tsx src/mutation/gate-m.ts \
|
|
136
|
+
--changed-files "$files_arg" 2>&1
|
|
137
|
+
return $?
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
# Fallback: direct mutmut CLI if TS gate module not available
|
|
141
|
+
local MUTATION_OUTPUT
|
|
142
|
+
MUTATION_OUTPUT=$(mktemp)
|
|
143
|
+
|
|
144
|
+
timeout "${timeout_s}s" mutmut run --paths-to-mutate $file_list > "$MUTATION_OUTPUT" 2>&1
|
|
145
|
+
local EXIT_CODE=$?
|
|
146
|
+
|
|
147
|
+
cat "$MUTATION_OUTPUT"
|
|
148
|
+
rm -f "$MUTATION_OUTPUT"
|
|
149
|
+
|
|
150
|
+
case $EXIT_CODE in
|
|
151
|
+
0)
|
|
152
|
+
echo "✅ Gate M (Python): PASS"
|
|
153
|
+
return 0
|
|
154
|
+
;;
|
|
155
|
+
124)
|
|
156
|
+
echo "⏱ Gate M (Python): TIMEOUT (${timeout_s}s). Allowing push with warning."
|
|
157
|
+
return 0
|
|
158
|
+
;;
|
|
159
|
+
*)
|
|
160
|
+
echo "❌ Gate M (Python): mutation score below threshold"
|
|
161
|
+
return 1
|
|
162
|
+
;;
|
|
163
|
+
esac
|
|
164
|
+
}
|
package/lib/doctor.js
CHANGED
|
@@ -386,21 +386,15 @@ function fixMissingAdapters(mode, srcDir, adaptersDir) {
|
|
|
386
386
|
return false;
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
|
|
390
|
-
* Attempt to fix known issues.
|
|
391
|
-
* Only operates when mode === 'active' (local or global).
|
|
392
|
-
*/
|
|
393
|
-
function fixIssues(checks, config) {
|
|
394
|
-
console.log('');
|
|
395
|
-
console.log('Attempting fixes...');
|
|
396
|
-
console.log('-------------------');
|
|
397
|
-
|
|
398
|
-
const srcDir = PKG_DIR;
|
|
389
|
+
function fixConfigMismatches(config) {
|
|
399
390
|
let fixed = false;
|
|
400
|
-
|
|
401
391
|
fixed = fixVersionMismatch(config, getPackageVersion()) || fixed;
|
|
402
392
|
fixed = fixTemplateDirMismatch(config, getTemplateDir()) || fixed;
|
|
393
|
+
return fixed;
|
|
394
|
+
}
|
|
403
395
|
|
|
396
|
+
function fixHooksByMode(config, srcDir) {
|
|
397
|
+
let fixed = false;
|
|
404
398
|
if (config.mode === 'local') {
|
|
405
399
|
const gitDir = getGitDir();
|
|
406
400
|
if (gitDir) {
|
|
@@ -410,18 +404,40 @@ function fixIssues(checks, config) {
|
|
|
410
404
|
} else {
|
|
411
405
|
fixed = fixMissingHooks('global', srcDir, GLOBAL_HOOKS_DIR) || fixed;
|
|
412
406
|
}
|
|
407
|
+
return fixed;
|
|
408
|
+
}
|
|
413
409
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
410
|
+
function fixGlobalHooksPath(config) {
|
|
411
|
+
if (config.mode !== 'global') return false;
|
|
412
|
+
const hooksPath = getCurrentHooksPath();
|
|
413
|
+
if (hooksPath !== GLOBAL_HOOKS_DIR) {
|
|
414
|
+
return fixCoreHooksPath(GLOBAL_HOOKS_DIR);
|
|
419
415
|
}
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
420
418
|
|
|
421
|
-
|
|
419
|
+
function getAdaptersDirByMode(config) {
|
|
420
|
+
return config.mode === 'local'
|
|
422
421
|
? path.join(path.dirname(getGitDir() || ''), 'githooks', 'adapters')
|
|
423
422
|
: GLOBAL_ADAPTERS_DIR;
|
|
424
|
-
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Attempt to fix known issues.
|
|
427
|
+
* Only operates when mode === 'active' (local or global).
|
|
428
|
+
*/
|
|
429
|
+
function fixIssues(checks, config) {
|
|
430
|
+
console.log('');
|
|
431
|
+
console.log('Attempting fixes...');
|
|
432
|
+
console.log('-------------------');
|
|
433
|
+
|
|
434
|
+
const srcDir = PKG_DIR;
|
|
435
|
+
let fixed = false;
|
|
436
|
+
|
|
437
|
+
fixed = fixConfigMismatches(config) || fixed;
|
|
438
|
+
fixed = fixHooksByMode(config, srcDir) || fixed;
|
|
439
|
+
fixed = fixGlobalHooksPath(config) || fixed;
|
|
440
|
+
fixed = fixMissingAdapters(config.mode, srcDir, getAdaptersDirByMode(config)) || fixed;
|
|
425
441
|
|
|
426
442
|
if (!fixed) {
|
|
427
443
|
console.log(' No fixable issues found.');
|
|
@@ -24,6 +24,27 @@ const PHASE_NAMES = {
|
|
|
24
24
|
|
|
25
25
|
const PHASE_ORDER = ['-1', '-0.5', '0', '1', '2', '3', '4', '5', '6', '7', '8'];
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Safely parse a date-like value into epoch ms.
|
|
29
|
+
* @param {*} value - Timestamp to parse
|
|
30
|
+
* @returns {number} Epoch ms, or NaN if invalid
|
|
31
|
+
*/
|
|
32
|
+
function parseTime(value) {
|
|
33
|
+
return new Date(value).getTime();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get the max of an existing value and a new timestamp (if valid and larger).
|
|
38
|
+
* @param {number} current - Current best value
|
|
39
|
+
* @param {*} candidate - Candidate timestamp
|
|
40
|
+
* @returns {number} Max value
|
|
41
|
+
*/
|
|
42
|
+
function maxValid(current, candidate) {
|
|
43
|
+
if (!candidate) return current;
|
|
44
|
+
const t = parseTime(candidate);
|
|
45
|
+
return !isNaN(t) && t > current ? t : current;
|
|
46
|
+
}
|
|
47
|
+
|
|
27
48
|
/**
|
|
28
49
|
* Find the most recent timestamp across started_at and phase_history.
|
|
29
50
|
* @param {object} state - Sprint state object
|
|
@@ -31,19 +52,12 @@ const PHASE_ORDER = ['-1', '-0.5', '0', '1', '2', '3', '4', '5', '6', '7', '8'];
|
|
|
31
52
|
*/
|
|
32
53
|
function getLatestTimestamp(state) {
|
|
33
54
|
if (!state || !state.started_at) return 0;
|
|
34
|
-
const started =
|
|
55
|
+
const started = parseTime(state.started_at);
|
|
35
56
|
if (isNaN(started)) return 0;
|
|
36
57
|
let latest = started;
|
|
37
58
|
if (Array.isArray(state.phase_history)) {
|
|
38
59
|
for (const ph of state.phase_history) {
|
|
39
|
-
|
|
40
|
-
const t = new Date(ph.completed_at).getTime();
|
|
41
|
-
if (!isNaN(t) && t > latest) latest = t;
|
|
42
|
-
}
|
|
43
|
-
if (ph.started_at) {
|
|
44
|
-
const t = new Date(ph.started_at).getTime();
|
|
45
|
-
if (!isNaN(t) && t > latest) latest = t;
|
|
46
|
-
}
|
|
60
|
+
latest = maxValid(maxValid(latest, ph.completed_at), ph.started_at);
|
|
47
61
|
}
|
|
48
62
|
}
|
|
49
63
|
return latest;
|
package/mock-policy/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SRC/MOCK-POLICY KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-06-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-06-22
|
|
4
|
+
**Commit:** 984d568
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.10.0.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
|
-
**Generated:** 2026-06-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-06-22
|
|
4
|
+
**Commit:** 984d568
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.10.0.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.
|