@boyingliu01/xp-gate 0.15.2 → 0.15.4

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.
Files changed (30) hide show
  1. package/bin/xp-gate.js +1 -1
  2. package/hooks/pre-commit +9 -0
  3. package/lib/__tests__/install-cmd.test.js +0 -35
  4. package/lib/doctor-tui.js +198 -0
  5. package/lib/doctor.js +95 -179
  6. package/lib/init.js +6 -4
  7. package/lib/install-cmd.js +31 -42
  8. package/lib/uninstall.js +22 -0
  9. package/mock-policy/AGENTS.md +2 -2
  10. package/mutation/AGENTS.md +2 -2
  11. package/package.json +1 -1
  12. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  13. package/plugins/claude-code/skills/delphi-review/AGENTS.md +2 -2
  14. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +2 -2
  15. package/plugins/claude-code/skills/sprint-flow/references/phase-4-verify.md +10 -0
  16. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +2 -2
  17. package/plugins/opencode/package.json +1 -1
  18. package/plugins/opencode/skills/delphi-review/AGENTS.md +2 -2
  19. package/plugins/opencode/skills/sprint-flow/AGENTS.md +2 -2
  20. package/plugins/opencode/skills/sprint-flow/references/phase-4-verify.md +10 -0
  21. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +2 -2
  22. package/plugins/qoder/plugin.json +1 -1
  23. package/plugins/qoder/skills/delphi-review/AGENTS.md +2 -2
  24. package/plugins/qoder/skills/sprint-flow/AGENTS.md +2 -2
  25. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +2 -2
  26. package/principles/AGENTS.md +2 -2
  27. package/skills/delphi-review/AGENTS.md +2 -2
  28. package/skills/sprint-flow/AGENTS.md +2 -2
  29. package/skills/sprint-flow/references/phase-4-verify.md +10 -0
  30. package/skills/test-specification-alignment/AGENTS.md +2 -2
package/bin/xp-gate.js CHANGED
@@ -179,7 +179,7 @@ const COMMANDS = {
179
179
  usage: 'xp-gate update-hooks [--global] [--force] [--dry-run] [--no-backup] [--scope hooks|adapters|all]'
180
180
  },
181
181
  'install': {
182
- description: 'One-step install (init + bootstrap + doctor)',
182
+ description: 'One-step install (init + baseline + bootstrap + doctor --fix)',
183
183
  run: subargs => {
184
184
  const { install } = require('../lib/install-cmd');
185
185
  install(subargs).then(code => process.exit(code));
package/hooks/pre-commit CHANGED
@@ -174,6 +174,8 @@ echo ""
174
174
  # ============================================================================
175
175
  # Gate 0: Version Consistency Check (Protected Branches)
176
176
  # ============================================================================
177
+ GATE_0_START=$(gate_start_ms)
178
+ GATE_0_STATUS="PASS"
177
179
 
178
180
  CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
179
181
  PROTECTED_BRANCHES="master develop trunk mainline"
@@ -327,6 +329,8 @@ Or use --no-verify (discouraged) or include [skip-version-check] in commit messa
327
329
 
328
330
  Staged files: $(echo "$STAGED_FILES" | tr '\n' ', ' | sed 's/,$//')
329
331
  "
332
+ GATE_0_STATUS="BLOCK"
333
+ record_gate_audit "gate-0" "version-consistency" "$GATE_0_STATUS" "1" "$GATE_0_START"
330
334
  exit 1
331
335
  fi
332
336
  fi
@@ -336,6 +340,8 @@ else
336
340
  echo "✅ PASSED - Gate 0: Version Consistency Check (non-protected branch: $CURRENT_BRANCH)"
337
341
  fi
338
342
 
343
+ record_gate_audit "gate-0" "version-consistency" "$GATE_0_STATUS" "0" "$GATE_0_START"
344
+
339
345
  echo ""
340
346
 
341
347
  # Get list of changed files
@@ -2345,6 +2351,7 @@ fi
2345
2351
  GATE_9_END=$(date +%s)
2346
2352
  GATE_9_DURATION=$((GATE_9_END - GATE_9_START))
2347
2353
  echo "✅ Gate 9 completed in ${GATE_9_DURATION}s"
2354
+ record_gate_audit "gate-9" "build-integrity" "$GATE_9_STATUS" "0" "$GATE_9_START"
2348
2355
  echo ""
2349
2356
  # ============================================================================
2350
2357
 
@@ -2379,6 +2386,7 @@ if [ -n "$SPRINT_GATE_SCRIPT" ]; then
2379
2386
  if ! bash "$SPRINT_GATE_SCRIPT" --pre-commit; then
2380
2387
  GATE_11_STATUS="BLOCK"
2381
2388
  echo "❌ BLOCKED - Gate 11: Sprint Flow Enforcement"
2389
+ record_gate_audit "gate-11" "sprint-flow" "$GATE_11_STATUS" "1" "$GATE_11_START"
2382
2390
  # Sprint gate failure is a hard block — exit immediately
2383
2391
  exit 1
2384
2392
  fi
@@ -2390,6 +2398,7 @@ fi
2390
2398
  GATE_11_END=$(date +%s)
2391
2399
  GATE_11_DURATION=$((GATE_11_END - GATE_11_START))
2392
2400
  echo "✅ Gate 11 completed in ${GATE_11_DURATION}s"
2401
+ record_gate_audit "gate-11" "sprint-flow" "$GATE_11_STATUS" "0" "$GATE_11_START"
2393
2402
  echo ""
2394
2403
  # ============================================================================
2395
2404
 
@@ -1,41 +1,6 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const os = require('os');
4
-
5
1
  describe('install-cmd', () => {
6
- let tmpDir;
7
- let consoleLogSpy;
8
-
9
- beforeEach(() => {
10
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xpgate-install-'));
11
- consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
12
- vi.resetModules();
13
- });
14
-
15
- afterEach(() => {
16
- fs.rmSync(tmpDir, { recursive: true, force: true });
17
- vi.restoreAllMocks();
18
- });
19
-
20
2
  it('exports install function', () => {
21
3
  const { install } = require('../install-cmd');
22
4
  expect(typeof install).toBe('function');
23
5
  });
24
-
25
- it('install accepts --global flag', async () => {
26
- const { install } = require('../install-cmd');
27
- const code = await install(['--global'], tmpDir);
28
- expect(typeof code).toBe('number');
29
- });
30
-
31
- it('install runs without --global (local mode) in git repo', async () => {
32
- // Simulate git repo
33
- const gitDir = path.join(tmpDir, '.git');
34
- fs.mkdirSync(gitDir, { recursive: true });
35
- fs.mkdirSync(path.join(gitDir, 'hooks'), { recursive: true });
36
-
37
- const { install } = require('../install-cmd');
38
- const code = await install([], tmpDir);
39
- expect(typeof code).toBe('number');
40
- });
41
6
  });
@@ -0,0 +1,198 @@
1
+ /**
2
+ * TUI registration and installed-skills diagnostics for doctor.
3
+ * Extracted from doctor.js to keep file size under the large-file threshold.
4
+ */
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const { HOME_DIR, detectPlatform } = require('./shared-paths.js');
8
+
9
+ // npm package source dir (template hooks/adapters)
10
+ const PKG_DIR = path.dirname(__dirname);
11
+
12
+ /**
13
+ * TUI registration path and expected plugin entry.
14
+ */
15
+ const TUI_JSON_PATH = path.join(HOME_DIR, '.config', 'opencode', 'tui.json');
16
+ const TUI_PLUGIN_ENTRY = '@boyingliu01/opencode-plugin/tui';
17
+
18
+ /**
19
+ * Read and parse tui.json, returning { data, error }.
20
+ * data = parsed object on success, null on file missing, undefined on corrupt.
21
+ */
22
+ function readTuiJson() {
23
+ if (!fs.existsSync(TUI_JSON_PATH)) return { data: null, error: null };
24
+ try {
25
+ const raw = fs.readFileSync(TUI_JSON_PATH, 'utf8');
26
+ const data = JSON.parse(raw);
27
+ return { data, error: null };
28
+ } catch (e) {
29
+ return { data: undefined, error: `Corrupt JSON: ${e.message}` };
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Check 9: TUI auto-registration in ~/.config/opencode/tui.json.
35
+ * @returns {number} issue count
36
+ */
37
+ function diagnoseTuiRegistration(checks) {
38
+ const { data, error } = readTuiJson();
39
+
40
+ if (error) {
41
+ checks.push({ name: 'TUI registration', status: 'FAIL', detail: error });
42
+ return 1;
43
+ }
44
+
45
+ if (data === null) {
46
+ checks.push({ name: 'TUI registration', status: 'FAIL', detail: 'Not registered' });
47
+ return 1;
48
+ }
49
+
50
+ const plugins = Array.isArray(data.plugin) ? data.plugin : [];
51
+ if (plugins.includes(TUI_PLUGIN_ENTRY)) {
52
+ checks.push({ name: 'TUI registration', status: 'PASS', detail: `${TUI_PLUGIN_ENTRY} registered` });
53
+ return 0;
54
+ }
55
+
56
+ checks.push({ name: 'TUI registration', status: 'FAIL', detail: 'Not registered' });
57
+ return 1;
58
+ }
59
+
60
+ /**
61
+ * Check 9: Installed skills vs package-bundled skills (#332).
62
+ * Reports WARN for each installed skill whose SKILL.md differs from the bundled version.
63
+ * @param {object} config
64
+ * @param {Array} checks
65
+ * @returns {number} issue count
66
+ */
67
+ function diagnoseInstalledSkills(config, checks) {
68
+ const installedSkills = config.installedSkills || {};
69
+ const skillNames = Object.keys(installedSkills);
70
+ if (skillNames.length === 0) {
71
+ checks.push({ name: 'Installed skills', status: 'SKIP', detail: 'No skills installed' });
72
+ return 0;
73
+ }
74
+
75
+ const bundledSkillsDir = path.join(PKG_DIR, 'skills');
76
+ if (!fs.existsSync(bundledSkillsDir)) {
77
+ checks.push({ name: 'Installed skills', status: 'SKIP', detail: 'Package skills dir not found' });
78
+ return 0;
79
+ }
80
+
81
+ const platform = detectPlatform();
82
+ let userSkillsDir;
83
+ if (platform === 'qoder') {
84
+ userSkillsDir = path.join(HOME_DIR, '.qoder', 'skills');
85
+ } else if (platform === 'claude-code') {
86
+ userSkillsDir = path.join(HOME_DIR, '.claude', 'skills');
87
+ } else {
88
+ userSkillsDir = path.join(HOME_DIR, '.config', 'opencode', 'skills');
89
+ }
90
+
91
+ let issues = 0;
92
+ for (const name of skillNames) {
93
+ const bundledSkillMd = path.join(bundledSkillsDir, name, 'SKILL.md');
94
+ const userSkillMd = path.join(userSkillsDir, name, 'SKILL.md');
95
+
96
+ if (!fs.existsSync(bundledSkillMd)) continue;
97
+ if (!fs.existsSync(userSkillMd)) {
98
+ checks.push({ name: `Skill: ${name}`, status: 'FAIL', detail: 'Installed SKILL.md missing' });
99
+ issues++;
100
+ continue;
101
+ }
102
+
103
+ const bundledContent = fs.readFileSync(bundledSkillMd, 'utf8');
104
+ const userContent = fs.readFileSync(userSkillMd, 'utf8');
105
+ if (bundledContent !== userContent) {
106
+ checks.push({
107
+ name: `Skill: ${name}`,
108
+ status: 'WARN',
109
+ detail: 'Outdated — run xp-gate update-skill --all',
110
+ });
111
+ issues++;
112
+ } else {
113
+ checks.push({ name: `Skill: ${name}`, status: 'PASS', detail: 'Up to date' });
114
+ }
115
+ }
116
+
117
+ return issues;
118
+ }
119
+
120
+ /**
121
+ * Fix TUI registration: ensure @boyingliu01/opencode-plugin/tui is in tui.json.
122
+ * Uses atomic write (tmp + renameSync) for JSON safety.
123
+ * On corrupt JSON: backup to .corrupt-{timestamp}.bak then rebuild.
124
+ * Idempotent: skips if already registered.
125
+ * @returns {boolean} Whether a fix was applied
126
+ */
127
+ function fixTuiRegistration() {
128
+ const { data, error } = readTuiJson();
129
+
130
+ // Corrupt JSON: backup old file, rebuild from scratch
131
+ if (error && data === undefined) {
132
+ const ts = Date.now();
133
+ const backupPath = `${TUI_JSON_PATH}.corrupt-${ts}.bak`;
134
+ try {
135
+ fs.copyFileSync(TUI_JSON_PATH, backupPath);
136
+ console.log(` ⚠ TUI config corrupted — backed up to ${backupPath}`);
137
+ } catch { /* non-critical */ }
138
+ // Rebuild fresh
139
+ const dir = path.dirname(TUI_JSON_PATH);
140
+ fs.mkdirSync(dir, { recursive: true });
141
+ const newConfig = { plugin: [TUI_PLUGIN_ENTRY] };
142
+ const tmpPath = `${TUI_JSON_PATH}.tmp`;
143
+ fs.writeFileSync(tmpPath, JSON.stringify(newConfig, null, 2));
144
+ fs.renameSync(tmpPath, TUI_JSON_PATH);
145
+ console.log(` ✓ Registered ${TUI_PLUGIN_ENTRY} in TUI (rebuilt after corrupt backup)`);
146
+ return true;
147
+ }
148
+
149
+ // File doesn't exist: create it
150
+ if (data === null) {
151
+ const dir = path.dirname(TUI_JSON_PATH);
152
+ fs.mkdirSync(dir, { recursive: true });
153
+ const newConfig = { plugin: [TUI_PLUGIN_ENTRY] };
154
+ const tmpPath = `${TUI_JSON_PATH}.tmp`;
155
+ fs.writeFileSync(tmpPath, JSON.stringify(newConfig, null, 2));
156
+ fs.renameSync(tmpPath, TUI_JSON_PATH);
157
+ console.log(` ✓ Created TUI config with ${TUI_PLUGIN_ENTRY}`);
158
+ return true;
159
+ }
160
+
161
+ // File exists, check if plugin already registered (idempotent)
162
+ const plugins = Array.isArray(data.plugin) ? data.plugin : [];
163
+ if (plugins.includes(TUI_PLUGIN_ENTRY)) return false;
164
+
165
+ // Append plugin entry
166
+ data.plugin = plugins.concat([TUI_PLUGIN_ENTRY]);
167
+ const tmpPath = `${TUI_JSON_PATH}.tmp`;
168
+ fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
169
+ fs.renameSync(tmpPath, TUI_JSON_PATH);
170
+ console.log(` ✓ Added ${TUI_PLUGIN_ENTRY} to TUI config`);
171
+ return true;
172
+ }
173
+
174
+ /**
175
+ * Wrapper for init.js: ensure TUI is registered without console output.
176
+ * Returns true if the plugin is already registered or was just added.
177
+ */
178
+ function ensureTuiRegistration() {
179
+ const { data } = readTuiJson();
180
+ if (data === null || data === undefined) {
181
+ fixTuiRegistration();
182
+ return;
183
+ }
184
+ const plugins = Array.isArray(data.plugin) ? data.plugin : [];
185
+ if (!plugins.includes(TUI_PLUGIN_ENTRY)) {
186
+ fixTuiRegistration();
187
+ }
188
+ }
189
+
190
+ module.exports = {
191
+ TUI_JSON_PATH,
192
+ TUI_PLUGIN_ENTRY,
193
+ readTuiJson,
194
+ diagnoseTuiRegistration,
195
+ diagnoseInstalledSkills,
196
+ fixTuiRegistration,
197
+ ensureTuiRegistration,
198
+ };
package/lib/doctor.js CHANGED
@@ -15,6 +15,10 @@ const {
15
15
  } = require('./shared-paths.js');
16
16
  const { checkUpgrade, formatUpgradeMsg } = require('./check-version.js');
17
17
  const { GATE_CLI_TOOLS, checkCliTool, getToolInstallCmd } = require('./detect-deps.js');
18
+ const {
19
+ diagnoseTuiRegistration, diagnoseInstalledSkills,
20
+ fixTuiRegistration, ensureTuiRegistration, readTuiJson,
21
+ } = require('./doctor-tui.js');
18
22
 
19
23
  // npm package source dir (template hooks/adapters)
20
24
  const PKG_DIR = path.dirname(__dirname);
@@ -779,6 +783,8 @@ function fixIssues(checks, config) {
779
783
  fixed = fixGlobalHooksPath(config) || fixed;
780
784
  fixed = fixMissingAdapters(config.mode, srcDir, getAdaptersDirByMode(config)) || fixed;
781
785
  fixed = fixMissingGateScripts(srcDir, getAdaptersDirByMode(config)) || fixed;
786
+ fixed = fixStaleHooks(config) || fixed;
787
+ fixed = fixMissingCliTools() || fixed;
782
788
  fixed = fixTuiRegistration() || fixed;
783
789
  fixed = printCliToolGuidance() || fixed;
784
790
 
@@ -787,6 +793,65 @@ function fixIssues(checks, config) {
787
793
  }
788
794
  }
789
795
 
796
+ /**
797
+ * Sync hooks/adapters from the installed package to the project/global dir.
798
+ * Catches cases where xp-gate was upgraded (npm update) but hooks are stale.
799
+ * Uses update-hooks logic with --force to overwrite outdated files.
800
+ *
801
+ * @returns {boolean} Whether any hooks were updated
802
+ */
803
+ function fixStaleHooks(config) {
804
+ try {
805
+ const { updateHooks, detectLocalModifications, getPackageRoot } = require('./update-hooks.js');
806
+ const isGlobal = config.mode === 'global';
807
+ const srcDir = getPackageRoot();
808
+
809
+ let hooksDestDir, adaptersDestDir;
810
+ if (isGlobal) {
811
+ hooksDestDir = GLOBAL_HOOKS_DIR;
812
+ adaptersDestDir = GLOBAL_ADAPTERS_DIR;
813
+ } else {
814
+ const gitDir = getGitDirSync();
815
+ if (!gitDir) return false;
816
+ const projectRoot = path.dirname(gitDir);
817
+ hooksDestDir = path.join(gitDir, 'hooks');
818
+ adaptersDestDir = path.join(projectRoot, 'githooks');
819
+ }
820
+
821
+ // Check if any hook files are stale (differ from package version)
822
+ const modified = detectLocalModifications(srcDir, hooksDestDir, adaptersDestDir);
823
+ if (modified.length === 0) return false;
824
+
825
+ console.log(` Syncing ${modified.length} outdated hook/adapter file(s)...`);
826
+ updateHooks({ global: isGlobal, force: true, dryRun: false, noBackup: true, scope: 'all' });
827
+ return true;
828
+ } catch {
829
+ return false;
830
+ }
831
+ }
832
+
833
+ /**
834
+ * Auto-install missing CLI tools via bootstrap.
835
+ * @returns {boolean} Whether any tools were installed
836
+ */
837
+ function fixMissingCliTools() {
838
+ try {
839
+ const { bootstrap } = require('./bootstrap.js');
840
+ const missing = [];
841
+ for (const entry of GATE_CLI_TOOLS) {
842
+ const { available } = checkCliTool(entry.tool);
843
+ if (!available) missing.push(entry.tool);
844
+ }
845
+ if (missing.length === 0) return false;
846
+
847
+ console.log(` Auto-installing ${missing.length} missing CLI tool(s)...`);
848
+ const code = bootstrap([]);
849
+ return code === 0;
850
+ } catch {
851
+ return false;
852
+ }
853
+ }
854
+
790
855
  /**
791
856
  * Print install guidance for missing CLI tools.
792
857
  * Does NOT auto-install — just shows commands.
@@ -880,184 +945,6 @@ function diagnoseOpenCodePlugin(checks) {
880
945
  return 0;
881
946
  }
882
947
 
883
- /**
884
- * TUI registration path and expected plugin entry.
885
- */
886
- const TUI_JSON_PATH = path.join(HOME_DIR, '.config', 'opencode', 'tui.json');
887
- const TUI_PLUGIN_ENTRY = '@boyingliu01/opencode-plugin/tui';
888
-
889
- /**
890
- * Read and parse tui.json, returning { data, error }.
891
- * data = parsed object on success, null on file missing, undefined on corrupt.
892
- */
893
- function readTuiJson() {
894
- if (!fs.existsSync(TUI_JSON_PATH)) return { data: null, error: null };
895
- try {
896
- const raw = fs.readFileSync(TUI_JSON_PATH, 'utf8');
897
- const data = JSON.parse(raw);
898
- return { data, error: null };
899
- } catch (e) {
900
- return { data: undefined, error: `Corrupt JSON: ${e.message}` };
901
- }
902
- }
903
-
904
- /**
905
- * Check 9: TUI auto-registration in ~/.config/opencode/tui.json.
906
- * @returns {number} issue count
907
- */
908
- function diagnoseTuiRegistration(checks) {
909
- const { data, error } = readTuiJson();
910
-
911
- if (error) {
912
- checks.push({ name: 'TUI registration', status: 'FAIL', detail: error });
913
- return 1;
914
- }
915
-
916
- if (data === null) {
917
- checks.push({ name: 'TUI registration', status: 'FAIL', detail: 'Not registered' });
918
- return 1;
919
- }
920
-
921
- const plugins = Array.isArray(data.plugin) ? data.plugin : [];
922
- if (plugins.includes(TUI_PLUGIN_ENTRY)) {
923
- checks.push({ name: 'TUI registration', status: 'PASS', detail: `${TUI_PLUGIN_ENTRY} registered` });
924
- return 0;
925
- }
926
-
927
- checks.push({ name: 'TUI registration', status: 'FAIL', detail: 'Not registered' });
928
- return 1;
929
- }
930
-
931
- /**
932
- * Check 9: Installed skills vs package-bundled skills (#332).
933
- * Reports WARN for each installed skill whose SKILL.md differs from the bundled version.
934
- * @param {object} config
935
- * @param {Array} checks
936
- * @returns {number} issue count
937
- */
938
- function diagnoseInstalledSkills(config, checks) {
939
- const installedSkills = config.installedSkills || {};
940
- const skillNames = Object.keys(installedSkills);
941
- if (skillNames.length === 0) {
942
- checks.push({ name: 'Installed skills', status: 'SKIP', detail: 'No skills installed' });
943
- return 0;
944
- }
945
-
946
- const bundledSkillsDir = path.join(PKG_DIR, 'skills');
947
- if (!fs.existsSync(bundledSkillsDir)) {
948
- checks.push({ name: 'Installed skills', status: 'SKIP', detail: 'Package skills dir not found' });
949
- return 0;
950
- }
951
-
952
- const platform = detectPlatform();
953
- let userSkillsDir;
954
- if (platform === 'qoder') {
955
- userSkillsDir = path.join(HOME_DIR, '.qoder', 'skills');
956
- } else if (platform === 'claude-code') {
957
- userSkillsDir = path.join(HOME_DIR, '.claude', 'skills');
958
- } else {
959
- userSkillsDir = path.join(HOME_DIR, '.config', 'opencode', 'skills');
960
- }
961
-
962
- let issues = 0;
963
- for (const name of skillNames) {
964
- const bundledSkillMd = path.join(bundledSkillsDir, name, 'SKILL.md');
965
- const userSkillMd = path.join(userSkillsDir, name, 'SKILL.md');
966
-
967
- if (!fs.existsSync(bundledSkillMd)) continue;
968
- if (!fs.existsSync(userSkillMd)) {
969
- checks.push({ name: `Skill: ${name}`, status: 'FAIL', detail: 'Installed SKILL.md missing' });
970
- issues++;
971
- continue;
972
- }
973
-
974
- const bundledContent = fs.readFileSync(bundledSkillMd, 'utf8');
975
- const userContent = fs.readFileSync(userSkillMd, 'utf8');
976
- if (bundledContent !== userContent) {
977
- checks.push({
978
- name: `Skill: ${name}`,
979
- status: 'WARN',
980
- detail: 'Outdated — run xp-gate update-skill --all',
981
- });
982
- issues++;
983
- } else {
984
- checks.push({ name: `Skill: ${name}`, status: 'PASS', detail: 'Up to date' });
985
- }
986
- }
987
-
988
- return issues;
989
- }
990
-
991
- /**
992
- * Fix TUI registration: ensure @boyingliu01/opencode-plugin/tui is in tui.json.
993
- * Uses atomic write (tmp + renameSync) for JSON safety.
994
- * On corrupt JSON: backup to .corrupt-{timestamp}.bak then rebuild.
995
- * Idempotent: skips if already registered.
996
- * @returns {boolean} Whether a fix was applied
997
- */
998
- function fixTuiRegistration() {
999
- const { data, error } = readTuiJson();
1000
-
1001
- // Corrupt JSON: backup old file, rebuild from scratch
1002
- if (error && data === undefined) {
1003
- const ts = Date.now();
1004
- const backupPath = `${TUI_JSON_PATH}.corrupt-${ts}.bak`;
1005
- try {
1006
- fs.copyFileSync(TUI_JSON_PATH, backupPath);
1007
- console.log(` ⚠ TUI config corrupted — backed up to ${backupPath}`);
1008
- } catch { /* non-critical */ }
1009
- // Rebuild fresh
1010
- const dir = path.dirname(TUI_JSON_PATH);
1011
- fs.mkdirSync(dir, { recursive: true });
1012
- const newConfig = { plugin: [TUI_PLUGIN_ENTRY] };
1013
- const tmpPath = `${TUI_JSON_PATH}.tmp`;
1014
- fs.writeFileSync(tmpPath, JSON.stringify(newConfig, null, 2));
1015
- fs.renameSync(tmpPath, TUI_JSON_PATH);
1016
- console.log(` ✓ Registered ${TUI_PLUGIN_ENTRY} in TUI (rebuilt after corrupt backup)`);
1017
- return true;
1018
- }
1019
-
1020
- // File doesn't exist: create it
1021
- if (data === null) {
1022
- const dir = path.dirname(TUI_JSON_PATH);
1023
- fs.mkdirSync(dir, { recursive: true });
1024
- const newConfig = { plugin: [TUI_PLUGIN_ENTRY] };
1025
- const tmpPath = `${TUI_JSON_PATH}.tmp`;
1026
- fs.writeFileSync(tmpPath, JSON.stringify(newConfig, null, 2));
1027
- fs.renameSync(tmpPath, TUI_JSON_PATH);
1028
- console.log(` ✓ Created TUI config with ${TUI_PLUGIN_ENTRY}`);
1029
- return true;
1030
- }
1031
-
1032
- // File exists, check if plugin already registered (idempotent)
1033
- const plugins = Array.isArray(data.plugin) ? data.plugin : [];
1034
- if (plugins.includes(TUI_PLUGIN_ENTRY)) return false;
1035
-
1036
- // Append plugin entry
1037
- data.plugin = plugins.concat([TUI_PLUGIN_ENTRY]);
1038
- const tmpPath = `${TUI_JSON_PATH}.tmp`;
1039
- fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
1040
- fs.renameSync(tmpPath, TUI_JSON_PATH);
1041
- console.log(` ✓ Added ${TUI_PLUGIN_ENTRY} to TUI config`);
1042
- return true;
1043
- }
1044
-
1045
- /**
1046
- * Wrapper for init.js: ensure TUI is registered without console output.
1047
- * Returns true if the plugin is already registered or was just added.
1048
- */
1049
- function ensureTuiRegistration() {
1050
- const { data } = readTuiJson();
1051
- if (data === null || data === undefined) {
1052
- fixTuiRegistration();
1053
- return;
1054
- }
1055
- const plugins = Array.isArray(data.plugin) ? data.plugin : [];
1056
- if (!plugins.includes(TUI_PLUGIN_ENTRY)) {
1057
- fixTuiRegistration();
1058
- }
1059
- }
1060
-
1061
948
  /**
1062
949
  * Diagnose language-specific tool availability.
1063
950
  * @returns {number} Number of issues found (missing required tools)
@@ -1149,7 +1036,36 @@ async function doctor(args) {
1149
1036
  issues += diagnoseOpenCodePlugin(checks);
1150
1037
 
1151
1038
  // Language-specific tool status
1152
- issues += diagnoseLanguageTools();
1039
+ const langIssues = diagnoseLanguageTools();
1040
+ issues += langIssues;
1041
+
1042
+ // In --fix mode, auto-install missing language tools
1043
+ if (fixMode && langIssues > 0) {
1044
+ try {
1045
+ const { detectProjectLanguages, getToolsForLanguages, installTool, generateProjectConfig, LANGUAGE_REGISTRY } = require('./language-tools.js');
1046
+ const projectRoot = process.cwd();
1047
+ const { detected } = detectProjectLanguages(projectRoot);
1048
+ if (detected.length > 0) {
1049
+ const toolStatus = getToolsForLanguages(detected);
1050
+ let installed = 0;
1051
+ for (const [lang, tools] of Object.entries(toolStatus)) {
1052
+ for (const t of tools) {
1053
+ if (t.status === 'missing') {
1054
+ const result = installTool(t.tool);
1055
+ if (result.success) {
1056
+ console.log(` \u2713 ${t.tool.name} (${lang}) installed`);
1057
+ installed++;
1058
+ }
1059
+ }
1060
+ }
1061
+ }
1062
+ if (installed > 0) {
1063
+ console.log(` ${installed} language tool(s) installed.`);
1064
+ generateProjectConfig(projectRoot, detected, toolStatus);
1065
+ }
1066
+ }
1067
+ } catch { /* non-blocking */ }
1068
+ }
1153
1069
 
1154
1070
  if (issues === 0) {
1155
1071
  console.log('\n✓ All checks passed');
package/lib/init.js CHANGED
@@ -341,14 +341,16 @@ async function init(args) {
341
341
  if (!installMode) { printUsage(); return 0; }
342
342
  if (installMode === 'global') return setupGlobal(args);
343
343
  const code = await installLocal(args);
344
- if (code === 0 && args.includes('--baseline')) {
344
+ // Always create lint baseline for local installs (Boy Scout Rule needs it)
345
+ // --baseline flag is now a no-op (baseline is always created)
346
+ if (code === 0) {
345
347
  try {
346
348
  const { createBaseline } = require('./baseline.js');
347
- console.log('\nCreating lint baseline...');
349
+ console.log('\nCreating lint baseline (Boy Scout Rule)...');
348
350
  const baseline = await createBaseline();
349
- console.log(`✅ Lint baseline created — ${Object.keys(baseline).length} files tracked.`);
351
+ console.log(`\u2705 Lint baseline created \u2014 ${Object.keys(baseline).length} files tracked.`);
350
352
  } catch (e) {
351
- console.log(`ℹ️ Lint baseline creation skipped: ${e.message}`);
353
+ console.log(`\u2139\ufe0f Lint baseline creation skipped: ${e.message}`);
352
354
  }
353
355
  }
354
356
  return code;
@@ -1,57 +1,46 @@
1
- const fs = require('fs');
1
+ /**
2
+ * One-step install command — delegates to init + doctor.
3
+ *
4
+ * `xp-gate install` is the single entry point for new users.
5
+ * It wraps init (which already handles hooks, bootstrap, language detection,
6
+ * and baseline creation) and follows up with doctor --fix for health verification.
7
+ *
8
+ * Local mode: init --core-only --yes → auto baseline → doctor --fix
9
+ * Global mode: init --global --yes → doctor --fix
10
+ */
2
11
  const path = require('path');
3
- const { execSync } = require('child_process');
4
- const { copyHooks, copyAdapters } = require('./shared-utils');
5
12
 
6
- async function install(args = [], cwd = process.cwd()) {
13
+ async function install(args = []) {
7
14
  const isGlobal = args.includes('--global');
8
15
 
9
- if (isGlobal) {
10
- return setupGlobal(cwd);
11
- }
12
- return setupLocal(cwd);
13
- }
14
-
15
- function setupGlobal(cwd) {
16
- const srcDir = path.dirname(__dirname);
17
-
18
- const globalHooksDir = path.join(require('os').homedir(), '.config', 'xp-gate', 'hooks');
19
- const globalAdaptersDir = path.join(require('os').homedir(), '.config', 'xp-gate', 'adapters');
16
+ console.log('XP-Gate One-Step Install');
17
+ console.log('========================\n');
20
18
 
21
- fs.mkdirSync(globalHooksDir, { recursive: true });
22
- fs.mkdirSync(globalAdaptersDir, { recursive: true });
19
+ // Build init args: always auto-yes for non-interactive friendliness
20
+ const initArgs = isGlobal ? ['--global', '--yes'] : ['--core-only', '--yes'];
23
21
 
24
- copyHooks(srcDir, globalHooksDir);
25
- copyAdapters(srcDir, globalAdaptersDir);
22
+ const { init } = require('./init.js');
23
+ const code = await init(initArgs);
26
24
 
27
- try {
28
- execSync(`git config --global core.hooksPath "${globalHooksDir}"`, { stdio: 'pipe' });
29
- } catch (err) {
30
- console.warn(` Warning: Could not set global core.hooksPath: ${err.message}`);
25
+ if (code !== 0) {
26
+ console.error('\nInstallation encountered errors.');
27
+ console.error('Run "xp-gate doctor" for diagnostics.');
28
+ return code;
31
29
  }
32
30
 
33
- console.log('Global installation complete.');
34
- return 0;
35
- }
31
+ // Post-install: run doctor --fix to verify and auto-repair
32
+ console.log('\n━━━ Post-Install Health Check ━━━\n');
33
+ const { doctor } = require('./doctor.js');
34
+ const doctorCode = await doctor(['--fix']);
36
35
 
37
- function setupLocal(projectRoot) {
38
- const gitDir = path.join(projectRoot, '.git');
39
- if (!fs.existsSync(gitDir)) {
40
- console.error('Error: Not a git repository');
41
- return 1;
36
+ if (doctorCode === 0) {
37
+ console.log('\n✓ Installation complete and verified!');
38
+ } else {
39
+ console.log('\n⚠ Installation complete, but some issues remain.');
40
+ console.log(' Run "xp-gate doctor" for details.');
42
41
  }
43
42
 
44
- const srcDir = path.dirname(__dirname);
45
- const hooksDir = path.join(gitDir, 'hooks');
46
- const githooksDir = path.join(projectRoot, 'githooks');
47
-
48
- copyHooks(srcDir, hooksDir);
49
-
50
- fs.mkdirSync(path.join(githooksDir, 'adapters'), { recursive: true });
51
- copyAdapters(srcDir, githooksDir);
52
-
53
- console.log('Local installation complete.');
54
- return 0;
43
+ return doctorCode;
55
44
  }
56
45
 
57
46
  module.exports = { install };
package/lib/uninstall.js CHANGED
@@ -218,6 +218,28 @@ function buildPlan(mode) {
218
218
  critical: true
219
219
  });
220
220
  }
221
+
222
+ // Project-level config files (generated by init)
223
+ const langConfig = path.join(projectRoot, '.xp-gate-config.json');
224
+ if (fs.existsSync(langConfig)) {
225
+ plan.push({
226
+ type: 'file',
227
+ path: langConfig,
228
+ signature: '"languages"',
229
+ label: '.xp-gate-config.json',
230
+ critical: false
231
+ });
232
+ }
233
+ const baselineFile = path.join(projectRoot, '.warnings-baseline.json');
234
+ if (fs.existsSync(baselineFile)) {
235
+ plan.push({
236
+ type: 'file',
237
+ path: baselineFile,
238
+ signature: '"warnings"',
239
+ label: '.warnings-baseline.json',
240
+ critical: false
241
+ });
242
+ }
221
243
  }
222
244
  }
223
245
 
@@ -1,9 +1,9 @@
1
1
  # SRC/MOCK-POLICY KNOWLEDGE BASE
2
2
 
3
3
  **Generated:** 2026-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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.
@@ -1,9 +1,9 @@
1
1
  # SRC/MUTATION KNOWLEDGE BASE
2
2
 
3
3
  **Generated:** 2026-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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": "@boyingliu01/xp-gate",
3
- "version": "0.15.2",
3
+ "version": "0.15.4",
4
4
  "description": "AI-driven development workflow: 6 quality gates + Delphi review + Sprint Flow",
5
5
  "bin": {
6
6
  "xp-gate": "./bin/xp-gate.js"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xp-gate",
3
- "version": "0.15.2",
3
+ "version": "0.15.4",
4
4
  "displayName": "XP-Gate",
5
5
  "description": "Extreme Programming quality gates + AI workflow skills for Claude Code. Includes 10 quality gates (Gate 0-9), Sprint Flow (11 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-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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.
@@ -59,6 +59,16 @@ skill(name="test-specification-alignment", user_message="--spec specification.ya
59
59
 
60
60
  Phase 1: 验证对齐(可修改测试) → Phase 2: 执行测试(禁止修改测试)
61
61
 
62
+ #### Step 2.5: 质量门禁全量验证
63
+
64
+ ```
65
+ npx xp-gate check --all
66
+ ```
67
+
68
+ 运行所有可用的质量门禁(Gate 0–9)并输出汇总报告。任何 BLOCK 状态 → 回退 Phase 3/6 修复。
69
+
70
+ > 此步骤自动调用各独立门禁(lint、复杂度、principles、架构、安全扫描等),无需手动逐个执行。
71
+
62
72
  #### Step 2.5-2.10: 项目类型特定验证(Web/Mobile/Backend)
63
73
 
64
74
  - **Web**: qa → design-review → benchmark
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
3
  **Generated:** 2026-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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": "@boyingliu01/opencode-plugin",
3
- "version": "0.15.2",
3
+ "version": "0.15.4",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "description": "XP-Gate quality gates + AI workflow skills + Sprint Flow TUI sidebar for OpenCode",
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
3
  **Generated:** 2026-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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.
@@ -59,6 +59,16 @@ skill(name="test-specification-alignment", user_message="--spec specification.ya
59
59
 
60
60
  Phase 1: 验证对齐(可修改测试) → Phase 2: 执行测试(禁止修改测试)
61
61
 
62
+ #### Step 2.5: 质量门禁全量验证
63
+
64
+ ```
65
+ npx xp-gate check --all
66
+ ```
67
+
68
+ 运行所有可用的质量门禁(Gate 0–9)并输出汇总报告。任何 BLOCK 状态 → 回退 Phase 3/6 修复。
69
+
70
+ > 此步骤自动调用各独立门禁(lint、复杂度、principles、架构、安全扫描等),无需手动逐个执行。
71
+
62
72
  #### Step 2.5-2.10: 项目类型特定验证(Web/Mobile/Backend)
63
73
 
64
74
  - **Web**: qa → design-review → benchmark
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
3
  **Generated:** 2026-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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.15.2",
3
+ "version": "0.15.4",
4
4
  "displayName": "XP-Gate",
5
5
  "description": "Extreme Programming quality gates + AI workflow skills for Qoder. Includes 10 quality gates (Gate 0-9), Sprint Flow (11 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-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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
  # PRINCIPLES CHECKER MODULE
2
2
 
3
3
  **Generated:** 2026-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.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.
@@ -59,6 +59,16 @@ skill(name="test-specification-alignment", user_message="--spec specification.ya
59
59
 
60
60
  Phase 1: 验证对齐(可修改测试) → Phase 2: 执行测试(禁止修改测试)
61
61
 
62
+ #### Step 2.5: 质量门禁全量验证
63
+
64
+ ```
65
+ npx xp-gate check --all
66
+ ```
67
+
68
+ 运行所有可用的质量门禁(Gate 0–9)并输出汇总报告。任何 BLOCK 状态 → 回退 Phase 3/6 修复。
69
+
70
+ > 此步骤自动调用各独立门禁(lint、复杂度、principles、架构、安全扫描等),无需手动逐个执行。
71
+
62
72
  #### Step 2.5-2.10: 项目类型特定验证(Web/Mobile/Backend)
63
73
 
64
74
  - **Web**: qa → design-review → benchmark
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
3
  **Generated:** 2026-07-23
4
- **Commit:** 0779b4f
4
+ **Commit:** 884f68c
5
5
  **Branch:** main
6
- **Version:** 0.15.2.0
6
+ **Version:** 0.15.4.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.