@boyingliu01/xp-gate 0.10.11 → 0.10.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.
Files changed (50) hide show
  1. package/lib/__tests__/check-version.test.js +19 -5
  2. package/lib/__tests__/doctor.test.js +155 -0
  3. package/lib/__tests__/install-skill.test.js +1 -1
  4. package/lib/doctor.js +126 -0
  5. package/lib/init.js +12 -0
  6. package/mock-policy/AGENTS.md +3 -3
  7. package/mutation/AGENTS.md +3 -3
  8. package/package.json +1 -1
  9. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  10. package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
  11. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
  12. package/plugins/claude-code/skills/sprint-flow/SKILL.md +6 -9
  13. package/plugins/claude-code/skills/sprint-flow/references/orchestration-rules.md +8 -6
  14. package/plugins/claude-code/skills/sprint-flow/references/phase-1-plan.md +2 -2
  15. package/plugins/claude-code/skills/sprint-flow/references/phase-3-review.md +3 -1
  16. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
  17. package/plugins/opencode/__tests__/tui-plugin.test.ts +121 -1
  18. package/plugins/opencode/__tests__/xp-gate-e2e-upgrade.test.ts +562 -0
  19. package/plugins/opencode/index.ts +2 -0
  20. package/plugins/opencode/package.json +1 -1
  21. package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
  22. package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
  23. package/plugins/opencode/skills/sprint-flow/SKILL.md +6 -9
  24. package/plugins/opencode/skills/sprint-flow/references/orchestration-rules.md +8 -6
  25. package/plugins/opencode/skills/sprint-flow/references/phase-1-plan.md +2 -2
  26. package/plugins/opencode/skills/sprint-flow/references/phase-3-review.md +3 -1
  27. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
  28. package/plugins/opencode/tui-plugin.ts +24 -7
  29. package/plugins/qoder/plugin.json +1 -1
  30. package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
  31. package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
  32. package/plugins/qoder/skills/sprint-flow/SKILL.md +191 -1088
  33. package/plugins/qoder/skills/sprint-flow/references/orchestration-rules.md +365 -0
  34. package/plugins/qoder/skills/sprint-flow/references/phase-1-plan.md +2 -2
  35. package/plugins/qoder/skills/sprint-flow/references/phase-2-build.md +25 -180
  36. package/plugins/qoder/skills/sprint-flow/references/phase-3-review.md +3 -1
  37. package/plugins/qoder/skills/sprint-flow/references/phase-6-ship.md +4 -188
  38. package/plugins/qoder/skills/sprint-flow/references/phase-7-land.md +4 -135
  39. package/plugins/qoder/skills/sprint-flow/references/phase-8-cleanup.md +4 -187
  40. package/plugins/qoder/skills/sprint-flow/references/phase-minus-0-5-auto-estimate.md +6 -0
  41. package/plugins/qoder/skills/sprint-flow/references/phase-minus-1-isolate.md +58 -0
  42. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
  43. package/principles/AGENTS.md +3 -3
  44. package/skills/delphi-review/AGENTS.md +3 -3
  45. package/skills/sprint-flow/AGENTS.md +3 -3
  46. package/skills/sprint-flow/SKILL.md +6 -9
  47. package/skills/sprint-flow/references/orchestration-rules.md +8 -6
  48. package/skills/sprint-flow/references/phase-1-plan.md +2 -2
  49. package/skills/sprint-flow/references/phase-3-review.md +3 -1
  50. package/skills/test-specification-alignment/AGENTS.md +3 -3
@@ -180,11 +180,11 @@ describe('check-version.js — REQ-001-01', () => {
180
180
  }
181
181
 
182
182
  function withMockedHttps(latestVersion, fn) {
183
- const fs = require('fs');
184
- const os = require('os');
185
- const cpPath = require('path').join(os.homedir(), '.xp-gate', 'version-cache.json');
186
- if (fs.existsSync(cpPath)) {
187
- try { fs.unlinkSync(cpPath); } catch { }
183
+ const fsm = require('fs');
184
+ const osm = require('os');
185
+ const cpPath = require('path').join(osm.homedir(), '.xp-gate', 'version-cache.json');
186
+ if (fsm.existsSync(cpPath)) {
187
+ try { fsm.unlinkSync(cpPath); } catch { }
188
188
  }
189
189
  evictCache();
190
190
  const https = require('https');
@@ -204,11 +204,25 @@ describe('check-version.js — REQ-001-01', () => {
204
204
  callback(mockRes);
205
205
  return { on: () => undefined, destroy: () => undefined };
206
206
  };
207
+ // Mock getLocalVersion to return a fixed version so tests don't
208
+ // depend on the real package.json version (which drifts over time).
209
+ const savedReadFileSync = fsm.readFileSync;
210
+ const pkgJsonDir = require('path').join(
211
+ require('path').dirname(require.resolve('../check-version')),
212
+ '..'
213
+ );
214
+ fsm.readFileSync = (...args) => {
215
+ if (String(args[0]).endsWith('package.json') && String(args[0]) === require('path').join(pkgJsonDir, 'package.json')) {
216
+ return JSON.stringify({ name: '@boyingliu01/xp-gate', version: '0.8.21' });
217
+ }
218
+ return savedReadFileSync.apply(fsm, args);
219
+ };
207
220
  try {
208
221
  const m = require('../check-version');
209
222
  return fn(m);
210
223
  } finally {
211
224
  https.get = saved;
225
+ fsm.readFileSync = savedReadFileSync;
212
226
  }
213
227
  }
214
228
 
@@ -102,6 +102,15 @@ describe('doctor', () => {
102
102
  );
103
103
  }
104
104
 
105
+ function tuiJsonPath() {
106
+ return path.join(tmpHome, '.config', 'opencode', 'tui.json');
107
+ }
108
+
109
+ function ensureTuiRegistered() {
110
+ fs.mkdirSync(path.dirname(tuiJsonPath()), { recursive: true });
111
+ fs.writeFileSync(tuiJsonPath(), JSON.stringify({ plugin: ['@boyingliu01/opencode-plugin/tui'] }, null, 2));
112
+ }
113
+
105
114
  function setupLocalInstall() {
106
115
  createXpGatePreCommit(projectHooksDir());
107
116
  createXpGatePrePush(projectHooksDir());
@@ -183,6 +192,7 @@ describe('doctor', () => {
183
192
 
184
193
  it('AC-05: doctor reports all checks passed for healthy local install', async () => {
185
194
  setupLocalInstall();
195
+ ensureTuiRegistered();
186
196
  seedVersionCache();
187
197
  mockExecSuccess();
188
198
  const { doctor } = require('../doctor');
@@ -198,6 +208,7 @@ describe('doctor', () => {
198
208
 
199
209
  it('AC-05: doctor reports all checks passed for healthy global install', async () => {
200
210
  setupGlobalInstall();
211
+ ensureTuiRegistered();
201
212
  seedVersionCache();
202
213
  mockExecSuccess();
203
214
  const { doctor } = require('../doctor');
@@ -352,6 +363,7 @@ describe('doctor', () => {
352
363
 
353
364
  it('AC-10: doctor --fix corrects core.hooksPath in global mode', async () => {
354
365
  setupGlobalInstall();
366
+ ensureTuiRegistered();
355
367
  seedVersionCache();
356
368
  // Mock hooksPath pointing somewhere else
357
369
  execSpy = vi.spyOn(childProcess, 'execSync').mockImplementation((cmd) => {
@@ -455,6 +467,7 @@ describe('doctor', () => {
455
467
 
456
468
  it('passes version check when config version matches package version', async () => {
457
469
  setupLocalInstall();
470
+ ensureTuiRegistered();
458
471
  seedVersionCache();
459
472
  // Write config with matching version
460
473
  const pkg = JSON.parse(fs.readFileSync(
@@ -476,6 +489,7 @@ describe('doctor', () => {
476
489
 
477
490
  it('passes version check when config has no version field (legacy)', async () => {
478
491
  setupLocalInstall();
492
+ ensureTuiRegistered();
479
493
  seedVersionCache();
480
494
  // Config without version field — legacy install, should not fail
481
495
  mockExecSuccess();
@@ -521,6 +535,7 @@ describe('doctor', () => {
521
535
 
522
536
  it('passes templateDir check when templateDir matches current platform', async () => {
523
537
  setupLocalInstall();
538
+ ensureTuiRegistered();
524
539
  seedVersionCache();
525
540
  // Write config with correct qoder templateDir
526
541
  const cfg = JSON.parse(fs.readFileSync(configFile(), 'utf8'));
@@ -546,6 +561,7 @@ describe('doctor', () => {
546
561
 
547
562
  it('passes templateDir check when config has no templateDir field (legacy)', async () => {
548
563
  setupLocalInstall();
564
+ ensureTuiRegistered();
549
565
  seedVersionCache();
550
566
  // Config without templateDir — legacy install, should not fail
551
567
  mockExecSuccess();
@@ -563,6 +579,7 @@ describe('doctor', () => {
563
579
 
564
580
  it('AC-004-01: doctor shows upgrade prompt at end when outdated', async () => {
565
581
  setupLocalInstall();
582
+ ensureTuiRegistered();
566
583
  mockExecSuccess();
567
584
  // Remove version cache so checkUpgrade hits the real npm registry
568
585
  const cachePath = path.join(tmpHome, '.xp-gate', 'version-cache.json');
@@ -582,6 +599,7 @@ describe('doctor', () => {
582
599
 
583
600
  it('AC-004-02: doctor does NOT show upgrade prompt when up to date', async () => {
584
601
  setupLocalInstall();
602
+ ensureTuiRegistered();
585
603
  mockExecSuccess();
586
604
  // Write a cache with a future version so checkUpgrade says "not outdated"
587
605
  const cachePath = path.join(tmpHome, '.xp-gate', 'version-cache.json');
@@ -606,6 +624,7 @@ describe('doctor', () => {
606
624
 
607
625
  it('AC-004-03: doctor does NOT fail when version check throws', async () => {
608
626
  setupLocalInstall();
627
+ ensureTuiRegistered();
609
628
  seedVersionCache();
610
629
  mockExecSuccess();
611
630
  // Write corrupt cache to trigger checkUpgrade error path
@@ -642,4 +661,140 @@ describe('doctor', () => {
642
661
  expect.stringContaining('Restored')
643
662
  );
644
663
  });
664
+
665
+ // === Check 9: TUI auto-registration (tui.json) ===
666
+
667
+ it('Check 9: PASS when tui.json has @boyingliu01/opencode-plugin/tui registered', async () => {
668
+ setupLocalInstall();
669
+ seedVersionCache();
670
+ mockExecSuccess();
671
+ // Create tui.json with the plugin registered
672
+ fs.mkdirSync(path.dirname(tuiJsonPath()), { recursive: true });
673
+ fs.writeFileSync(tuiJsonPath(), JSON.stringify({ plugin: ['@boyingliu01/opencode-plugin/tui'] }, null, 2));
674
+ const { doctor } = require('../doctor');
675
+
676
+ const result = await doctor([]);
677
+
678
+ expect(result).toBe(0);
679
+ expect(logSpy).toHaveBeenCalledWith(
680
+ expect.stringContaining('@boyingliu01/opencode-plugin/tui registered')
681
+ );
682
+ });
683
+
684
+ it('Check 9: FAIL when tui.json is missing', async () => {
685
+ setupLocalInstall();
686
+ seedVersionCache();
687
+ mockExecSuccess();
688
+ // Ensure no tui.json
689
+ if (fs.existsSync(tuiJsonPath())) fs.unlinkSync(tuiJsonPath());
690
+ const { doctor } = require('../doctor');
691
+
692
+ const result = await doctor([]);
693
+
694
+ expect(result).toBe(1);
695
+ expect(logSpy).toHaveBeenCalledWith(
696
+ expect.stringContaining('TUI registration: Not registered')
697
+ );
698
+ });
699
+
700
+ it('Check 9: FAIL when tui.json exists without @boyingliu01/opencode-plugin/tui entry', async () => {
701
+ setupLocalInstall();
702
+ seedVersionCache();
703
+ mockExecSuccess();
704
+ fs.mkdirSync(path.dirname(tuiJsonPath()), { recursive: true });
705
+ fs.writeFileSync(tuiJsonPath(), JSON.stringify({ plugin: ['some-other-plugin'] }, null, 2));
706
+ const { doctor } = require('../doctor');
707
+
708
+ const result = await doctor([]);
709
+
710
+ expect(result).toBe(1);
711
+ expect(logSpy).toHaveBeenCalledWith(
712
+ expect.stringContaining('TUI registration: Not registered')
713
+ );
714
+ });
715
+
716
+ it('Check 9: WARN when tui.json exists but has no plugin array', async () => {
717
+ setupLocalInstall();
718
+ seedVersionCache();
719
+ mockExecSuccess();
720
+ fs.mkdirSync(path.dirname(tuiJsonPath()), { recursive: true });
721
+ fs.writeFileSync(tuiJsonPath(), JSON.stringify({ someKey: 'value' }, null, 2));
722
+ const { doctor } = require('../doctor');
723
+
724
+ const result = await doctor([]);
725
+
726
+ expect(result).toBe(1);
727
+ expect(logSpy).toHaveBeenCalledWith(
728
+ expect.stringContaining('TUI registration: Not registered')
729
+ );
730
+ });
731
+
732
+ it('Check 9: FAIL with backup when tui.json is corrupt JSON', async () => {
733
+ setupLocalInstall();
734
+ seedVersionCache();
735
+ mockExecSuccess();
736
+ fs.mkdirSync(path.dirname(tuiJsonPath()), { recursive: true });
737
+ fs.writeFileSync(tuiJsonPath(), 'this is not { valid json');
738
+ const { doctor } = require('../doctor');
739
+
740
+ const result = await doctor([]);
741
+
742
+ expect(result).toBe(1);
743
+ expect(logSpy).toHaveBeenCalledWith(
744
+ expect.stringContaining('TUI registration: Corrupt')
745
+ );
746
+ });
747
+
748
+ // === --fix TUI registration ===
749
+
750
+ it('--fix: diagnoses TUI not registered then re-diagnoses after fix', async () => {
751
+ setupLocalInstall();
752
+ seedVersionCache();
753
+ mockExecSuccess();
754
+ // No tui.json
755
+ if (fs.existsSync(path.join(tmpHome, '.config', 'opencode'))) {
756
+ fs.rmSync(path.join(tmpHome, '.config', 'opencode'), { recursive: true, force: true });
757
+ }
758
+ const { doctor } = require('../doctor');
759
+
760
+ const result = await doctor(['--fix']);
761
+
762
+ expect(result).toBe(0);
763
+ // After fix, tui.json should exist with the plugin registered
764
+ expect(fs.existsSync(tuiJsonPath())).toBe(true);
765
+ const tui = JSON.parse(fs.readFileSync(tuiJsonPath(), 'utf8'));
766
+ expect(tui.plugin).toContain('@boyingliu01/opencode-plugin/tui');
767
+ });
768
+
769
+ it('--fix: append to existing tui.json plugin array', async () => {
770
+ setupLocalInstall();
771
+ seedVersionCache();
772
+ mockExecSuccess();
773
+ fs.mkdirSync(path.dirname(tuiJsonPath()), { recursive: true });
774
+ fs.writeFileSync(tuiJsonPath(), JSON.stringify({ plugin: ['some-other-plugin'] }, null, 2));
775
+ const { doctor } = require('../doctor');
776
+
777
+ const result = await doctor(['--fix']);
778
+
779
+ expect(result).toBe(0);
780
+ const tui = JSON.parse(fs.readFileSync(tuiJsonPath(), 'utf8'));
781
+ expect(tui.plugin).toContain('some-other-plugin');
782
+ expect(tui.plugin).toContain('@boyingliu01/opencode-plugin/tui');
783
+ });
784
+
785
+ it('--fix: idempotent — does not duplicate entry when already registered', async () => {
786
+ setupLocalInstall();
787
+ seedVersionCache();
788
+ mockExecSuccess();
789
+ fs.mkdirSync(path.dirname(tuiJsonPath()), { recursive: true });
790
+ fs.writeFileSync(tuiJsonPath(), JSON.stringify({ plugin: ['@boyingliu01/opencode-plugin/tui'] }, null, 2));
791
+ const { doctor } = require('../doctor');
792
+
793
+ const result = await doctor(['--fix']);
794
+
795
+ expect(result).toBe(0);
796
+ const tui = JSON.parse(fs.readFileSync(tuiJsonPath(), 'utf8'));
797
+ // Should still have exactly one entry
798
+ expect(tui.plugin.filter(p => p === '@boyingliu01/opencode-plugin/tui').length).toBe(1);
799
+ });
645
800
  });
@@ -192,7 +192,7 @@ describe('install-skill', () => {
192
192
  const result = await installSkill('sprint-flow');
193
193
 
194
194
  expect(result).toBe(1);
195
- expect(console.error).toHaveBeenCalledWith('Error: sprint-flow is already installed');
195
+ expect(console.error).toHaveBeenCalledWith('Error: sprint-flow is already installed\nUse --force to overwrite');
196
196
  });
197
197
 
198
198
  it('backs up and replaces when target exists and force=true', async () => {
package/lib/doctor.js CHANGED
@@ -229,6 +229,9 @@ function diagnose() {
229
229
  // --- Check 6: Environment dependencies ---
230
230
  checkEnv(checks);
231
231
 
232
+ // --- Check 7: TUI auto-registration ---
233
+ issues += diagnoseTuiRegistration(checks);
234
+
232
235
  return { checks, issues };
233
236
  }
234
237
 
@@ -438,6 +441,7 @@ function fixIssues(checks, config) {
438
441
  fixed = fixHooksByMode(config, srcDir) || fixed;
439
442
  fixed = fixGlobalHooksPath(config) || fixed;
440
443
  fixed = fixMissingAdapters(config.mode, srcDir, getAdaptersDirByMode(config)) || fixed;
444
+ fixed = fixTuiRegistration() || fixed;
441
445
 
442
446
  if (!fixed) {
443
447
  console.log(' No fixable issues found.');
@@ -499,6 +503,124 @@ function diagnoseOpenCodePlugin(checks) {
499
503
  return 0;
500
504
  }
501
505
 
506
+ /**
507
+ * TUI registration path and expected plugin entry.
508
+ */
509
+ const TUI_JSON_PATH = path.join(HOME_DIR, '.config', 'opencode', 'tui.json');
510
+ const TUI_PLUGIN_ENTRY = '@boyingliu01/opencode-plugin/tui';
511
+
512
+ /**
513
+ * Read and parse tui.json, returning { data, error }.
514
+ * data = parsed object on success, null on file missing, undefined on corrupt.
515
+ */
516
+ function readTuiJson() {
517
+ if (!fs.existsSync(TUI_JSON_PATH)) return { data: null, error: null };
518
+ try {
519
+ const raw = fs.readFileSync(TUI_JSON_PATH, 'utf8');
520
+ const data = JSON.parse(raw);
521
+ return { data, error: null };
522
+ } catch (e) {
523
+ return { data: undefined, error: `Corrupt JSON: ${e.message}` };
524
+ }
525
+ }
526
+
527
+ /**
528
+ * Check 9: TUI auto-registration in ~/.config/opencode/tui.json.
529
+ * @returns {number} issue count
530
+ */
531
+ function diagnoseTuiRegistration(checks) {
532
+ const { data, error } = readTuiJson();
533
+
534
+ if (error) {
535
+ checks.push({ name: 'TUI registration', status: 'FAIL', detail: error });
536
+ return 1;
537
+ }
538
+
539
+ if (data === null) {
540
+ checks.push({ name: 'TUI registration', status: 'FAIL', detail: 'Not registered' });
541
+ return 1;
542
+ }
543
+
544
+ const plugins = Array.isArray(data.plugin) ? data.plugin : [];
545
+ if (plugins.includes(TUI_PLUGIN_ENTRY)) {
546
+ checks.push({ name: 'TUI registration', status: 'PASS', detail: `${TUI_PLUGIN_ENTRY} registered` });
547
+ return 0;
548
+ }
549
+
550
+ checks.push({ name: 'TUI registration', status: 'FAIL', detail: 'Not registered' });
551
+ return 1;
552
+ }
553
+
554
+ /**
555
+ * Fix TUI registration: ensure @boyingliu01/opencode-plugin/tui is in tui.json.
556
+ * Uses atomic write (tmp + renameSync) for JSON safety.
557
+ * On corrupt JSON: backup to .corrupt-{timestamp}.bak then rebuild.
558
+ * Idempotent: skips if already registered.
559
+ * @returns {boolean} Whether a fix was applied
560
+ */
561
+ function fixTuiRegistration() {
562
+ const { data, error } = readTuiJson();
563
+
564
+ // Corrupt JSON: backup old file, rebuild from scratch
565
+ if (error && data === undefined) {
566
+ const ts = Date.now();
567
+ const backupPath = `${TUI_JSON_PATH}.corrupt-${ts}.bak`;
568
+ try {
569
+ fs.copyFileSync(TUI_JSON_PATH, backupPath);
570
+ console.log(` ⚠ TUI config corrupted — backed up to ${backupPath}`);
571
+ } catch { /* non-critical */ }
572
+ // Rebuild fresh
573
+ const dir = path.dirname(TUI_JSON_PATH);
574
+ fs.mkdirSync(dir, { recursive: true });
575
+ const newConfig = { plugin: [TUI_PLUGIN_ENTRY] };
576
+ const tmpPath = `${TUI_JSON_PATH}.tmp`;
577
+ fs.writeFileSync(tmpPath, JSON.stringify(newConfig, null, 2));
578
+ fs.renameSync(tmpPath, TUI_JSON_PATH);
579
+ console.log(` ✓ Registered ${TUI_PLUGIN_ENTRY} in TUI (rebuilt after corrupt backup)`);
580
+ return true;
581
+ }
582
+
583
+ // File doesn't exist: create it
584
+ if (data === null) {
585
+ const dir = path.dirname(TUI_JSON_PATH);
586
+ fs.mkdirSync(dir, { recursive: true });
587
+ const newConfig = { plugin: [TUI_PLUGIN_ENTRY] };
588
+ const tmpPath = `${TUI_JSON_PATH}.tmp`;
589
+ fs.writeFileSync(tmpPath, JSON.stringify(newConfig, null, 2));
590
+ fs.renameSync(tmpPath, TUI_JSON_PATH);
591
+ console.log(` ✓ Created TUI config with ${TUI_PLUGIN_ENTRY}`);
592
+ return true;
593
+ }
594
+
595
+ // File exists, check if plugin already registered (idempotent)
596
+ const plugins = Array.isArray(data.plugin) ? data.plugin : [];
597
+ if (plugins.includes(TUI_PLUGIN_ENTRY)) return false;
598
+
599
+ // Append plugin entry
600
+ data.plugin = plugins.concat([TUI_PLUGIN_ENTRY]);
601
+ const tmpPath = `${TUI_JSON_PATH}.tmp`;
602
+ fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
603
+ fs.renameSync(tmpPath, TUI_JSON_PATH);
604
+ console.log(` ✓ Added ${TUI_PLUGIN_ENTRY} to TUI config`);
605
+ return true;
606
+ }
607
+
608
+ /**
609
+ * Wrapper for init.js: ensure TUI is registered without console output.
610
+ * Returns true if the plugin is already registered or was just added.
611
+ */
612
+ function ensureTuiRegistration() {
613
+ const { data } = readTuiJson();
614
+ if (data === null || data === undefined) {
615
+ fixTuiRegistration();
616
+ return;
617
+ }
618
+ const plugins = Array.isArray(data.plugin) ? data.plugin : [];
619
+ if (!plugins.includes(TUI_PLUGIN_ENTRY)) {
620
+ fixTuiRegistration();
621
+ }
622
+ }
623
+
502
624
  async function doctor(args) {
503
625
  const fixMode = args.includes('--fix');
504
626
 
@@ -551,4 +673,8 @@ module.exports = {
551
673
  fixMissingHooks,
552
674
  fixCoreHooksPath,
553
675
  fixMissingAdapters,
676
+ diagnoseTuiRegistration,
677
+ fixTuiRegistration,
678
+ ensureTuiRegistration,
679
+ readTuiJson,
554
680
  };
package/lib/init.js CHANGED
@@ -340,6 +340,12 @@ async function installLocal(args) {
340
340
  injectKarpathyPrinciples(projectRoot);
341
341
  configureOpenCodePlugin(srcDir, projectRoot);
342
342
 
343
+ // Auto-register TUI plugin globally (idempotent)
344
+ try {
345
+ const { ensureTuiRegistration } = require('../lib/doctor.js');
346
+ ensureTuiRegistration();
347
+ } catch { /* non-critical: TUI registration is best-effort */ }
348
+
343
349
  console.log('\nInstallation complete!');
344
350
  console.log('Run git commit to trigger quality gates');
345
351
  return 0;
@@ -390,6 +396,12 @@ async function setupGlobal(args) {
390
396
  const manifest = generateGlobalManifest(srcDir);
391
397
  updateConfig({ lastInit: new Date().toISOString(), mode: 'global', templateDir: TEMPLATE_DIR, manifest });
392
398
 
399
+ // Auto-register TUI plugin globally (idempotent)
400
+ try {
401
+ const { ensureTuiRegistration } = require('../lib/doctor.js');
402
+ ensureTuiRegistration();
403
+ } catch { /* non-critical: TUI registration is best-effort */ }
404
+
393
405
  console.log('\nGlobal setup complete!');
394
406
  console.log('All git repositories will now use xp-gate quality gates.');
395
407
  console.log('Per-project adapters can still override by creating <repo>/githooks/');
@@ -1,9 +1,9 @@
1
1
  # SRC/MOCK-POLICY KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-06-23
4
- **Commit:** bac916f
3
+ **Generated:** 2026-06-24
4
+ **Commit:** 5019773
5
5
  **Branch:** main
6
- **Version:** 0.10.11.0
6
+ **Version:** 0.10.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.
@@ -1,9 +1,9 @@
1
1
  # SRC/MUTATION KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-06-23
4
- **Commit:** bac916f
3
+ **Generated:** 2026-06-24
4
+ **Commit:** 5019773
5
5
  **Branch:** main
6
- **Version:** 0.10.11.0
6
+ **Version:** 0.10.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": "@boyingliu01/xp-gate",
3
- "version": "0.10.11",
3
+ "version": "0.10.13",
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.10.11",
3
+ "version": "0.10.13",
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
- **Generated:** 2026-06-23
4
- **Commit:** bac916f
3
+ **Generated:** 2026-06-24
4
+ **Commit:** 5019773
5
5
  **Branch:** main
6
- **Version:** 0.10.11.0
6
+ **Version:** 0.10.13.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/SPRINT-FLOW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-06-23
4
- **Commit:** bac916f
3
+ **Generated:** 2026-06-24
4
+ **Commit:** 5019773
5
5
  **Branch:** main
6
- **Version:** 0.10.11.0
6
+ **Version:** 0.10.13.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **11-phase** development pipeline: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → USER ACCEPTANCE → FEEDBACK → SHIP → LAND → CLEANUP. Phase 2 default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE in Phase 1: design must pass Delphi review (≥90% consensus) before any coding.
@@ -237,11 +237,11 @@ Sprint Flow: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW
237
237
  - **HARD-GATE**: 设计未批准 → 不可进入实现
238
238
 
239
239
  ### Phase 1: PLAN(共识评审)
240
- - **注意**: `autoplan` 和 `to-issues` 是交互式 skill,**必须由 orchestrator 直接执行**(Issue #225, #248)
241
- - **执行模式(三阶段)**:
240
+ - **注意**: `autoplan`、`delphi-review` 和 `to-issues` 均为交互式 skill,**必须由 orchestrator 直接执行**(Issue #225, #248, #249
241
+ - **执行模式(三阶段,全部 orchestrator 直接执行)**:
242
242
  1. **Orchestrator 直接执行 autoplan**: `skill(name="autoplan")` → 用户确认 taste_decisions
243
- 2. **Orchestrator 直接执行 to-issues**: `skill(name="to-issues")` 用户确认 Issue 拆分方案
244
- 3. **Subagent 执行 delphi-review**: `task(category="deep", load_skills=["delphi-review"])` 非交互式,可自动运行至 APPROVED
243
+ 2. **Orchestrator 直接执行 delphi-review**: `skill(name="delphi-review")` 等待 APPROVED(非 APPROVED 时需等待用户确认/处理)
244
+ 3. **Orchestrator 直接执行 to-issues**: `skill(name="to-issues")` 用户确认 Issue 拆分方案
245
245
  - 输入: phase-0-summary.md + 设计文档
246
246
  - 输出: `specification.yaml`(含 user_stories[])+ `slices-manifest.json`
247
247
 
@@ -276,13 +276,10 @@ Sprint Flow: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW
276
276
  5. **Mock Minimization**: integration-first, mock 仅限 external services, 密度 > 30% 需 `@mock-justified`
277
277
 
278
278
  ### Phase 3: REVIEW + TEST(验证)
279
- - **Subagent dispatch**: orchestrator 通过 `task(category="deep", load_skills=["delphi-review", "test-specification-alignment"])` 启动独立 session
279
+ - **Orchestrator 直接执行**: delphi-review code-walkthrough 需要用户确认 verdict(Issue #249),**必须由 orchestrator 直接调用** `skill(name="delphi-review")`
280
+ - **Orchestrator 按序执行**: `delphi-review --mode code-walkthrough` → 等待 APPROVED → `test-specification-alignment` → `browse` (gstack) → 可选 `qa`/`design-review`/`benchmark` (gstack)
280
281
  - 输入: phase-2-summary.md + MVP 代码
281
282
  - 输出: 评审报告 + 测试对齐结果
282
- - `delphi-review --mode code-walkthrough` — 多专家匿名代码走查
283
- - `test-specification-alignment` — 测试与 Spec 对齐验证
284
- - `browse` (gstack) — 浏览器自动化测试
285
- - `k6` / `locust` / `gatling` — 负载/压力测试(可选,后端项目)
286
283
 
287
284
  ### 负载/压力测试(可选)
288
285
  - **适用项目**:主要用于后端服务的压力测试 (k6/Locust/Gatling),Web 前端已有 `benchmark` 技能覆盖 Core Web Vitals、加载时间和资源大小等性能指标
@@ -31,9 +31,9 @@
31
31
  | -1 | ISOLATE | ❌ | Bash(直接执行) | 无 | orchestrator |
32
32
  | -0.5 | AUTO-ESTIMATE | ❌ | Bash(直接执行) | 无 | orchestrator |
33
33
  | 0 | THINK | ❌ | orchestrator(直接执行) | `["brainstorming"]` | orchestrator |
34
- | 1 | PLAN | ⚠️ | orchestrator 执行 autoplan + to-issues(交互)→ subagent 执行 delphi-review | 见下方说明 | orchestrator + subagent |
34
+ | 1 | PLAN | | orchestrator(直接执行) | `["autoplan", "delphi-review", "to-issues"]` | orchestrator |
35
35
  | 2 | BUILD | ✅ | ralph-loop | `["test-driven-development"]` | subagent |
36
- | 3 | REVIEW | | `deep` | `["delphi-review", "test-specification-alignment"]` | subagent |
36
+ | 3 | REVIEW | | orchestrator(直接执行) | `["delphi-review", "test-specification-alignment"]` | orchestrator |
37
37
  | 4 | USER ACCEPT | ❌ | **强制人工** | 无 | 用户 |
38
38
  | 5 | FEEDBACK | ✅ | `quick` | `["learn", "retro", "systematic-debugging"]` | subagent |
39
39
  | 6 | SHIP | ❌ | orchestrator(直接执行) | `["finishing-a-development-branch", "ship"]` | orchestrator |
@@ -46,15 +46,17 @@
46
46
  |-------|-------|---------------------------|
47
47
  | 0 | `brainstorming` | 需要与用户对话确认需求、提出澄清问题。Subagent 是 fire-and-forget 模式,无法暂停等待用户输入(Issue #217) |
48
48
  | 1 | `autoplan` | taste_decisions 节点暂停等待用户确认。必须由 orchestrator 直接执行(Issue #225) |
49
+ | 1 | `delphi-review` | design 模式需等待 verdict APPROVED;非 APPROVED 时需用户确认是否接受分歧方案(Issue #249) |
49
50
  | 1 | `to-issues` | Step 6 "向用户确认" — 展示拆分结果,等待用户批准后才生成 slices-manifest.json |
51
+ | 3 | `delphi-review --mode code-walkthrough` | Code walkthrough 非 APPROVED 时需暂停等待用户处理 Critical Issues(Issue #249) |
50
52
  | 6 | `finishing-a-development-branch` | 4 选项菜单 (merge/PR/keep/discard) 需要用户选择;Option 4 (discard) 要求 typed confirmation |
51
53
  | 6 | `ship` | PR 创建前需要用户确认;包含 AskUserQuestion STOP 点 |
52
54
  | 7 | `land-and-deploy` | Merge 确认、rollback 决策 — 均为用户交互点 |
53
55
 
54
- **Phase 1 执行模式(两阶段)**:
56
+ **Phase 1 执行模式(全部 orchestrator 直接执行)**:
55
57
  1. **Orchestrator 直接执行 autoplan**:`skill(name="autoplan")` → 等待用户确认 taste_decisions
56
- 2. **Orchestrator 直接执行 to-issues**:`skill(name="to-issues")` → 等待用户确认 Issue 拆分
57
- 3. **Subagent 执行 delphi-review**:`task(category="deep", load_skills=["delphi-review"])` 非交互式,可自动运行至 APPROVED
58
+ 2. **Orchestrator 直接执行 delphi-review**:`skill(name="delphi-review")` → 等待 APPROVED(非 APPROVED 时暂停等待用户确认)
59
+ 3. **Orchestrator 直接执行 to-issues**:`skill(name="to-issues")` 等待用户确认 Issue 拆分
58
60
 
59
61
  **上下文隔离原则**:
60
62
  - 每个 Subagent 在**独立 session** 中启动,不继承 orchestrator 的对话历史
@@ -71,7 +73,7 @@
71
73
  | Phase 0 | phase--1-summary(仅路径) | 隔离环境信息(worktree 路径) |
72
74
  | Phase 1 | phase-0-summary.md + design-doc | 设计决策 + 结构化规格 |
73
75
  | Phase 2 | phase-1-summary.md + specification.yaml | 评审结论 + REQ 列表 |
74
- | Phase 3 | phase-2-summary.md + MVP 代码 | 构建结果 |
76
+ | Phase 3 | phase-2-summary.md + MVP 代码 | 构建结果(orchestrator 直接执行,无 subagent 上下文继承) |
75
77
  | Phase 4 | — | **人工验收**。Phase 4 不产生 subagent summary,但用户验收结果记录在 `.sprint-state/phase-outputs/emergent-issues.md`(如有 emergent issues)。Phase 5 加载此文件。 |
76
78
  | Phase 5 | phase-4-summary.md + emergent-issues.md | 验收结论 |
77
79
  | Phase 6 | phase-5-summary.md + feedback-log.md | 复盘结论 |
@@ -105,7 +105,7 @@ Decision 2: [决策描述]
105
105
 
106
106
  ---
107
107
 
108
- ### Step 2b: 调用 delphi-review(强制)
108
+ ### Step 2b: 调用 delphi-review(强制,orchestrator 直接执行)
109
109
 
110
110
  ```bash
111
111
  skill(name="delphi-review", user_message="[设计文档 + taste_decisions 确认结果]")
@@ -117,7 +117,7 @@ delphi-review 执行:
117
117
  - 输出: APPROVED / REQUEST_CHANGES
118
118
 
119
119
  **如果 REQUEST_CHANGES**:
120
- - ⚠️ 暂停等待用户修复
120
+ - ⚠️ 暂停等待用户处理(orchestrator 直接执行,可交互确认)
121
121
  - 修复后重新评审(从 Round 2 起步)
122
122
  - 直到 APPROVED
123
123
 
@@ -4,6 +4,8 @@
4
4
 
5
5
  多专家代码走查、测试对齐、浏览器测试。确保 MVP 符合 specification。
6
6
 
7
+ **执行者**: orchestrator 直接执行全部步骤(非 subagent dispatch)— Issue #249。
8
+
7
9
  Web 前端项目额外增加:系统化 QA、视觉审计、性能基线。
8
10
 
9
11
  ---
@@ -46,7 +48,7 @@ delphi code-walkthrough 执行:
46
48
  - ≥90% 共识 + APPROVED 才通过
47
49
 
48
50
  **如果 REQUEST_CHANGES**:
49
- - ⚠️ 暂停等待用户修复 Critical Issues + 处理 Major Concerns
51
+ - ⚠️ 暂停等待用户处理 Critical Issues + Major Concerns(orchestrator 直接执行,可交互)
50
52
  - 修复后回到 Round 2 重新评审
51
53
 
52
54
  **如果 APPROVED**:
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-06-23
4
- **Commit:** bac916f
3
+ **Generated:** 2026-06-24
4
+ **Commit:** 5019773
5
5
  **Branch:** main
6
- **Version:** 0.10.11.0
6
+ **Version:** 0.10.13.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.