@boyingliu01/xp-gate 0.12.9 → 0.12.12

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 (58) hide show
  1. package/adapter-common.sh +13 -18
  2. package/adapters/typescript.sh +87 -10
  3. package/gate-3.sh +1 -1
  4. package/hooks/adapter-common.sh +13 -18
  5. package/hooks/pre-commit +166 -27
  6. package/hooks/pre-push +231 -18
  7. package/lib/__tests__/doctor.test.js +65 -38
  8. package/lib/__tests__/sprint-discovery.test.ts +5 -4
  9. package/lib/__tests__/sprint-status.test.ts +5 -4
  10. package/lib/__tests__/tsconfig.json +8 -0
  11. package/lib/check-version.js +19 -4
  12. package/lib/init.js +24 -0
  13. package/lib/upgrade.js +64 -0
  14. package/mock-policy/AGENTS.md +3 -3
  15. package/mutation/AGENTS.md +3 -3
  16. package/mutation/runners/stryker-runner.ts +7 -5
  17. package/package.json +1 -1
  18. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  19. package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
  20. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
  21. package/plugins/claude-code/skills/sprint-flow/SKILL.md +117 -14
  22. package/plugins/claude-code/skills/sprint-flow/__tests__/sprint-flow.test.cjs +444 -0
  23. package/plugins/claude-code/skills/sprint-flow/references/phase-2-build.md +104 -0
  24. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
  25. package/plugins/opencode/package.json +1 -1
  26. package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
  27. package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
  28. package/plugins/opencode/skills/sprint-flow/SKILL.md +117 -14
  29. package/plugins/opencode/skills/sprint-flow/__tests__/sprint-flow.test.cjs +444 -0
  30. package/plugins/opencode/skills/sprint-flow/references/phase-2-build.md +104 -0
  31. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
  32. package/plugins/qoder/plugin.json +1 -1
  33. package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
  34. package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
  35. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
  36. package/principles/AGENTS.md +3 -3
  37. package/principles/__tests__/baseline-storage.test.ts +3 -2
  38. package/principles/__tests__/baseline.test.ts +16 -14
  39. package/principles/__tests__/config.test.ts +1 -1
  40. package/principles/__tests__/types.test.ts +1 -0
  41. package/principles/adapters/__tests__/base.test.ts +8 -8
  42. package/principles/adapters/__tests__/cpp.test.ts +26 -26
  43. package/principles/adapters/__tests__/dart.test.ts +11 -11
  44. package/principles/adapters/__tests__/go.test.ts +9 -9
  45. package/principles/adapters/__tests__/java.test.ts +9 -9
  46. package/principles/adapters/__tests__/kotlin.test.ts +10 -10
  47. package/principles/adapters/__tests__/objectivec.test.ts +27 -27
  48. package/principles/adapters/__tests__/python.test.ts +11 -11
  49. package/principles/adapters/__tests__/swift.test.ts +9 -9
  50. package/principles/adapters/__tests__/typescript.test.ts +13 -13
  51. package/principles/config.ts +1 -1
  52. package/principles/rules/__tests__/clean-code/large-file.test.ts +10 -9
  53. package/skills/delphi-review/AGENTS.md +3 -3
  54. package/skills/sprint-flow/AGENTS.md +3 -3
  55. package/skills/sprint-flow/SKILL.md +117 -14
  56. package/skills/sprint-flow/__tests__/sprint-flow.test.cjs +444 -0
  57. package/skills/sprint-flow/references/phase-2-build.md +104 -0
  58. package/skills/test-specification-alignment/AGENTS.md +3 -3
@@ -20,6 +20,65 @@
20
20
 
21
21
  ---
22
22
 
23
+ ---
24
+
25
+ ## Uncommitted Changes Gate
26
+
27
+ **Purpose**: Prevent entering BUILD with uncommitted changes that could mix with sprint work.
28
+
29
+ **Execution**: Before entering Phase 2 BUILD, the orchestrator MUST check for uncommitted changes in the worktree.
30
+
31
+ ### Gate Logic
32
+
33
+ ```bash
34
+ # Check for uncommitted changes
35
+ UNCOMMITTED=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
36
+
37
+ if [ "$SKIP_UNCOMMITTED_GATE" = "1" ]; then
38
+ echo "[UNCOMMITTED-GATE] Skipped (SKIP_UNCOMMITTED_GATE=1)"
39
+ echo "{\"skipped\":true,\"reason\":\"SKIP_UNCOMMITTED_GATE=1\",\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" > .sprint-state/uncommitted-gate-log.json
40
+ elif [ "$UNCOMMITTED" -gt 0 ]; then
41
+ echo "⚠️ [UNCOMMITTED-GATE] Found ${UNCOMMITTED} uncommitted files in worktree:"
42
+ git status --short 2>/dev/null | head -20
43
+ echo ""
44
+ echo "Uncommitted changes may conflict with sprint work. Recommended actions:"
45
+ echo " 1. Commit changes: git add -A && git commit -m 'pre-sprint: save work before BUILD'"
46
+ echo " 2. Stash changes: git stash push -m 'pre-sprint stash'"
47
+ echo " 3. Skip gate: export SKIP_UNCOMMITTED_GATE=1 (not recommended)"
48
+ echo ""
49
+ echo "Logging to .sprint-state/uncommitted-gate-log.json"
50
+ echo "{\"blocked\":true,\"uncommitted_files\":${UNCOMMITTED},\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" > .sprint-state/uncommitted-gate-log.json
51
+ echo "[BLOCK] Uncommitted changes detected. Please commit, stash, or set SKIP_UNCOMMITTED_GATE=1 to continue."
52
+ exit 1
53
+ else
54
+ echo "✅ [UNCOMMITTED-GATE] Worktree clean. Proceeding to BUILD."
55
+ echo "{\"clean\":true,\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" > .sprint-state/uncommitted-gate-log.json
56
+ fi
57
+ ```
58
+
59
+ ### Escape Valve
60
+
61
+ ```bash
62
+ # Skip the uncommitted changes gate (use with caution)
63
+ SKIP_UNCOMMITTED_GATE=1
64
+ ```
65
+
66
+ ### Log Format (`.sprint-state/uncommitted-gate-log.json`)
67
+
68
+ ```json
69
+ {
70
+ "clean": true,
71
+ "blocked": false,
72
+ "skipped": false,
73
+ "uncommitted_files": 0,
74
+ "timestamp": "2026-07-05T10:00:00Z"
75
+ }
76
+ ```
77
+
78
+ **Log location**: `.sprint-state/uncommitted-gate-log.json` — written on every gate execution for audit trail.
79
+
80
+ ---
81
+
23
82
  ## TDD 强制执行
24
83
 
25
84
  ### Gate 5a-BLOCK: 新增文件测试强制
@@ -41,3 +100,48 @@
41
100
  # 非 main/master 分支临时跳过 Gate 5a-BLOCK
42
101
  SKIP_GATE_5A_BLOCK=1 git commit -m "message"
43
102
  ```
103
+
104
+ ---
105
+
106
+ ## Timing & Stability
107
+
108
+ This section documents expected execution times and timeout handling for each Phase 2 sub-step to reduce execution timing stddev and prevent pipeline stalls.
109
+
110
+ ### Expected Execution Times
111
+
112
+ | Step | Description | Expected Time | Timeout | On Timeout |
113
+ |------|-------------|--------------|---------|------------|
114
+ | DELPHI-GATE | Verify delphi-reviewed.json exists | 1-2s | 10s | BLOCK (critical gate) |
115
+ | ralph-loop (per REQ) | TDD + verification per requirement | 5-15 min | 30 min/REQ | Mark REQ as `timeout`, continue next REQ |
116
+ | parallel dispatch | Multi-agent parallel build | 10-30 min | 45 min | Collect partial results, continue |
117
+ | TDD cycle (per unit) | RED → GREEN → REFACTOR | 2-5 min | 10 min | Skip unit, log failure |
118
+ | freeze + blind-review | Code review in isolation | 5-10 min | 20 min | WARNING, continue |
119
+ | verification | Test suite + coverage check | 2-5 min | 15 min | Retry once, then BLOCK |
120
+ | cost monitor | Token cost accounting | <1s | 5s | Skip, log warning |
121
+ | Phase 2 total (lightweight) | ≤3 REQs | 15-30 min | 45 min | — |
122
+ | Phase 2 total (standard) | 4-10 REQs | 30-120 min | 150 min | — |
123
+ | Phase 2 total (complex) | >10 REQs | 60-240 min | 300 min | — |
124
+
125
+ ### Stability Guidelines
126
+
127
+ 1. **Timeout handling**: All Phase 2 sub-steps MUST have explicit timeouts. If a step times out, log the failure to `.sprint-state/phase-outputs/phase-2-errors.json` and continue to the next step (except DELPHI-GATE which is a hard BLOCK).
128
+
129
+ 2. **Retry strategy**: For recoverable failures (verification, TDD cycle):
130
+ - First failure: log warning, retry once
131
+ - Second failure: log error, BLOCK and prompt user decision
132
+ - Do NOT retry more than twice for any single sub-step
133
+
134
+ 3. **Parallel dispatch stability**: When using `--mode parallel`, if any agent fails:
135
+ - Collect partial results from successful agents
136
+ - Rerun failed agent(s) individually with `--mode ralph-loop`
137
+ - Do NOT rerun the entire parallel batch
138
+
139
+ 4. **Cost monitor thresholds**:
140
+ - Token cost per REQ > 50,000 → WARNING (review REQ scope)
141
+ - Token cost per REQ > 100,000 → BLOCK (REQ too large, split into smaller units)
142
+
143
+ 5. **StdDev reduction**: To reduce execution timing variability:
144
+ - Cache dependency installation results between REQs
145
+ - Reuse TDD scaffolding across similar REQ types
146
+ - Pre-compute code structure analysis once at Phase start
147
+ - Batch lint/format operations at the end, not per REQ
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-07-05
4
- **Commit:** a620d9b
3
+ **Generated:** 2026-07-06
4
+ **Commit:** 132a12e
5
5
  **Branch:** main
6
- **Version:** 0.12.9.0
6
+ **Version:** 0.12.12.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.12.9",
3
+ "version": "0.12.12",
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
- **Generated:** 2026-07-05
4
- **Commit:** a620d9b
3
+ **Generated:** 2026-07-06
4
+ **Commit:** 132a12e
5
5
  **Branch:** main
6
- **Version:** 0.12.9.0
6
+ **Version:** 0.12.12.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-07-05
4
- **Commit:** a620d9b
3
+ **Generated:** 2026-07-06
4
+ **Commit:** 132a12e
5
5
  **Branch:** main
6
- **Version:** 0.12.9.0
6
+ **Version:** 0.12.12.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.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-07-05
4
- **Commit:** a620d9b
3
+ **Generated:** 2026-07-06
4
+ **Commit:** 132a12e
5
5
  **Branch:** main
6
- **Version:** 0.12.9.0
6
+ **Version:** 0.12.12.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
- **Generated:** 2026-07-05
4
- **Commit:** a620d9b
3
+ **Generated:** 2026-07-06
4
+ **Commit:** 132a12e
5
5
  **Branch:** main
6
- **Version:** 0.12.9.0
6
+ **Version:** 0.12.12.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.
@@ -154,8 +154,9 @@ describe('Baseline Storage', () => {
154
154
  expect(stats.totalFiles).toBe(2);
155
155
  expect(stats.totalWarnings).toBe(7);
156
156
  expect(stats.averageWarningsPerFile).toBe(3.5);
157
- expect(stats.eslint?.totalWarnings).toBe(6);
158
- expect(stats.eslint?.totalErrors).toBe(1);
157
+ const richStats = stats as { totalFiles: number; totalWarnings: number; averageWarningsPerFile: number; eslint: { totalWarnings: number; totalErrors: number } };
158
+ expect(richStats.eslint.totalWarnings).toBe(6);
159
+ expect(richStats.eslint.totalErrors).toBe(1);
159
160
  });
160
161
 
161
162
  it('creates baseline from files with warning data', async () => {
@@ -19,7 +19,7 @@ describe('BaselineEntry - lint tool fields (ruff, golangci, shellcheck)', () =>
19
19
  lastAnalyzed: new Date().toISOString(),
20
20
  },
21
21
  };
22
- expect(() => storage.validate(entry as Record<string, import('../baseline').BaselineEntry>)).not.toThrow();
22
+ expect(() => storage.validate(entry as unknown as Record<string, import('../baseline').BaselineEntry>)).not.toThrow();
23
23
  });
24
24
 
25
25
  it('accepts golangci field in BaselineEntry', () => {
@@ -31,7 +31,7 @@ describe('BaselineEntry - lint tool fields (ruff, golangci, shellcheck)', () =>
31
31
  lastAnalyzed: new Date().toISOString(),
32
32
  },
33
33
  };
34
- expect(() => storage.validate(entry as Record<string, import('../baseline').BaselineEntry>)).not.toThrow();
34
+ expect(() => storage.validate(entry as unknown as Record<string, import('../baseline').BaselineEntry>)).not.toThrow();
35
35
  });
36
36
 
37
37
  it('accepts shellcheck field in BaselineEntry', () => {
@@ -43,7 +43,7 @@ describe('BaselineEntry - lint tool fields (ruff, golangci, shellcheck)', () =>
43
43
  lastAnalyzed: new Date().toISOString(),
44
44
  },
45
45
  };
46
- expect(() => storage.validate(entry as Record<string, import('../baseline').BaselineEntry>)).not.toThrow();
46
+ expect(() => storage.validate(entry as unknown as Record<string, import('../baseline').BaselineEntry>)).not.toThrow();
47
47
  });
48
48
 
49
49
  it('rejects ruff entry with non-numeric warnings', () => {
@@ -55,7 +55,7 @@ describe('BaselineEntry - lint tool fields (ruff, golangci, shellcheck)', () =>
55
55
  lastAnalyzed: new Date().toISOString(),
56
56
  },
57
57
  };
58
- expect(() => storage.validate(entry as Record<string, import('../baseline').BaselineEntry>)).toThrow(/Invalid ruff properties/);
58
+ expect(() => storage.validate(entry as unknown as Record<string, import('../baseline').BaselineEntry>)).toThrow(/Invalid ruff properties/);
59
59
  });
60
60
 
61
61
  it('combines all lint tools in getSummaryStatistics', () => {
@@ -82,12 +82,13 @@ describe('BaselineEntry - lint tool fields (ruff, golangci, shellcheck)', () =>
82
82
 
83
83
  expect(stats.totalFiles).toBe(3);
84
84
  expect(stats.totalWarnings).toBe(10);
85
- expect(stats.ruff?.totalWarnings).toBe(5);
86
- expect(stats.ruff?.totalErrors).toBe(2);
87
- expect(stats.golangci?.totalWarnings).toBe(3);
88
- expect(stats.golangci?.totalErrors).toBe(1);
89
- expect(stats.shellcheck?.totalWarnings).toBe(2);
90
- expect(stats.shellcheck?.totalErrors).toBe(0);
85
+ const richStats = stats as typeof stats & { ruff: { totalWarnings: number; totalErrors: number }; golangci: { totalWarnings: number; totalErrors: number }; shellcheck: { totalWarnings: number; totalErrors: number } };
86
+ expect(richStats.ruff.totalWarnings).toBe(5);
87
+ expect(richStats.ruff.totalErrors).toBe(2);
88
+ expect(richStats.golangci.totalWarnings).toBe(3);
89
+ expect(richStats.golangci.totalErrors).toBe(1);
90
+ expect(richStats.shellcheck.totalWarnings).toBe(2);
91
+ expect(richStats.shellcheck.totalErrors).toBe(0);
91
92
  });
92
93
  });
93
94
 
@@ -121,11 +122,12 @@ describe('BaselineStorage - extended coverage', () => {
121
122
  },
122
123
  };
123
124
 
124
- const stats = storage.getSummaryStatistics(baseline);
125
+ const rawStats = storage.getSummaryStatistics(baseline);
126
+ const stats = rawStats as { totalFiles: number; totalWarnings: number; averageWarningsPerFile: number; ccn: { totalWarnings: number; totalMax: number } };
125
127
 
126
128
  expect(stats.totalFiles).toBe(2);
127
- expect(stats.ccn?.totalWarnings).toBe(8);
128
- expect(stats.ccn?.totalMax).toBe(30);
129
+ expect(stats.ccn.totalWarnings).toBe(8);
130
+ expect(stats.ccn.totalMax).toBe(30);
129
131
  });
130
132
 
131
133
  it('returns averageWarningsPerFile of 0 for empty baseline', () => {
@@ -135,7 +137,7 @@ describe('BaselineStorage - extended coverage', () => {
135
137
  expect(stats.totalFiles).toBe(0);
136
138
  expect(stats.totalWarnings).toBe(0);
137
139
  expect(stats.averageWarningsPerFile).toBe(0);
138
- expect(stats.ccn).toBeUndefined();
140
+ expect('ccn' in stats ? (stats as Record<string, unknown>).ccn : undefined).toBeUndefined();
139
141
  });
140
142
  });
141
143
 
@@ -14,7 +14,7 @@ describe('config.ts - Configuration Loader', () => {
14
14
 
15
15
  // Clean code thresholds from design Section 6
16
16
  expect(config.rules['clean-code']['long-function'].threshold).toBe(50);
17
- expect(config.rules['clean-code']['large-file'].threshold).toBe(650);
17
+ expect(config.rules['clean-code']['large-file'].threshold).toBe(1000);
18
18
  expect(config.rules['clean-code']['god-class'].threshold).toBe(15);
19
19
  expect(config.rules['clean-code']['deep-nesting'].threshold).toBe(4);
20
20
  expect(config.rules['clean-code']['too-many-params'].threshold).toBe(7);
@@ -90,6 +90,7 @@ describe('types.ts - Core Interfaces', () => {
90
90
  parseAST: () => null,
91
91
  extractFunctions: () => [],
92
92
  extractClasses: () => [],
93
+ extractExports: () => [],
93
94
  countLines: () => 0,
94
95
  };
95
96
 
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, vi } from 'vitest';
1
+ import { describe, it, expect, vi, type Mock } from 'vitest';;
2
2
  import type { Adapter } from '../../types';
3
3
  import { BaseAdapter } from '../base';
4
4
 
@@ -24,7 +24,7 @@ import { readFileSync } from 'fs';
24
24
  describe('BaseAdapter - Default Implementation', () => {
25
25
 
26
26
  it('should implement the Adapter interface', () => {
27
- (readFileSync as vi.Mock).mockReturnValue('mock file content\ntest line 2');
27
+ (readFileSync as Mock).mockReturnValue('mock file content\ntest line 2');
28
28
  const adapter = new MockAdapter('./test.ts');
29
29
  const implemented: Adapter = adapter as unknown as Adapter;
30
30
 
@@ -37,7 +37,7 @@ describe('BaseAdapter - Default Implementation', () => {
37
37
  });
38
38
 
39
39
  it('should detect language based on file extension', () => {
40
- (readFileSync as vi.Mock).mockReturnValue('file content');
40
+ (readFileSync as Mock).mockReturnValue('file content');
41
41
 
42
42
  const adapterTs = new MockAdapter('./test.ts');
43
43
  expect(adapterTs.detectLanguage()).toBe('typescript');
@@ -56,35 +56,35 @@ describe('BaseAdapter - Default Implementation', () => {
56
56
  });
57
57
 
58
58
  it('should parse file AST correctly', () => {
59
- (readFileSync as vi.Mock).mockReturnValue('mock file content\ntest line 2');
59
+ (readFileSync as Mock).mockReturnValue('mock file content\ntest line 2');
60
60
  const adapter = new MockAdapter('./test.ts');
61
61
  const ast = adapter.parseAST();
62
62
  expect(ast).toEqual({ content: 'mock file content\ntest line 2', astType: 'mock' });
63
63
  });
64
64
 
65
65
  it('should extract functions from AST', () => {
66
- (readFileSync as vi.Mock).mockReturnValue('');
66
+ (readFileSync as Mock).mockReturnValue('');
67
67
  const adapter = new MockAdapter('./test.ts');
68
68
  const functions = adapter.extractFunctions();
69
69
  expect(Array.isArray(functions)).toBe(true);
70
70
  });
71
71
 
72
72
  it('should extract classes from AST', () => {
73
- (readFileSync as vi.Mock).mockReturnValue('');
73
+ (readFileSync as Mock).mockReturnValue('');
74
74
  const adapter = new MockAdapter('./test.ts');
75
75
  const classes = adapter.extractClasses();
76
76
  expect(Array.isArray(classes)).toBe(true);
77
77
  });
78
78
 
79
79
  it('should count physical lines correctly', () => {
80
- (readFileSync as vi.Mock).mockReturnValue('line 1\nline 2\nline 3');
80
+ (readFileSync as Mock).mockReturnValue('line 1\nline 2\nline 3');
81
81
  const adapter = new MockAdapter('./test.ts');
82
82
  const lineCount = adapter.countLines();
83
83
  expect(lineCount).toBe(3);
84
84
  });
85
85
 
86
86
  it('should throw error for unsupported file operations', () => {
87
- (readFileSync as vi.Mock).mockImplementation(() => {
87
+ (readFileSync as Mock).mockImplementation(() => {
88
88
  throw new Error('Could not read file');
89
89
  });
90
90
 
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, vi } from 'vitest';
1
+ import { describe, it, expect, vi, type Mock } from 'vitest';;
2
2
  import { CppAdapter } from '../cpp';
3
3
 
4
4
  type MockFunction = Record<string, unknown>;
@@ -13,7 +13,7 @@ import { readFileSync } from 'fs';
13
13
  describe('CppAdapter', () => {
14
14
 
15
15
  it('should implement the Adapter interface', () => {
16
- (readFileSync as vi.Mock).mockReturnValue('int main() { return 0; }');
16
+ (readFileSync as Mock).mockReturnValue('int main() { return 0; }');
17
17
  const adapter = new CppAdapter('test.cpp');
18
18
 
19
19
  expect(adapter).toHaveProperty('detectLanguage');
@@ -24,59 +24,59 @@ describe('CppAdapter', () => {
24
24
  });
25
25
 
26
26
  it('should detect language as cpp for .cpp files', () => {
27
- (readFileSync as vi.Mock).mockReturnValue('int main() { return 0; }');
27
+ (readFileSync as Mock).mockReturnValue('int main() { return 0; }');
28
28
  const adapter = new CppAdapter('test.cpp');
29
29
  const detected = adapter.detectLanguage();
30
30
  expect(detected).toBe('cpp');
31
31
  });
32
32
 
33
33
  it('should detect language as cpp for .cxx files', () => {
34
- (readFileSync as vi.Mock).mockReturnValue('int main() { return 0; }');
34
+ (readFileSync as Mock).mockReturnValue('int main() { return 0; }');
35
35
  const adapter = new CppAdapter('test.cxx');
36
36
  const detected = adapter.detectLanguage();
37
37
  expect(detected).toBe('cpp');
38
38
  });
39
39
 
40
40
  it('should detect language as cpp for .cc files', () => {
41
- (readFileSync as vi.Mock).mockReturnValue('int main() { return 0; }');
41
+ (readFileSync as Mock).mockReturnValue('int main() { return 0; }');
42
42
  const adapter = new CppAdapter('test.cc');
43
43
  const detected = adapter.detectLanguage();
44
44
  expect(detected).toBe('cpp');
45
45
  });
46
46
 
47
47
  it('should detect language as cpp for .c files', () => {
48
- (readFileSync as vi.Mock).mockReturnValue('int main() { return 0; }');
48
+ (readFileSync as Mock).mockReturnValue('int main() { return 0; }');
49
49
  const adapter = new CppAdapter('test.c');
50
50
  const detected = adapter.detectLanguage();
51
51
  expect(detected).toBe('cpp');
52
52
  });
53
53
 
54
54
  it('should detect language as cpp for .hpp files', () => {
55
- (readFileSync as vi.Mock).mockReturnValue('#pragma once');
55
+ (readFileSync as Mock).mockReturnValue('#pragma once');
56
56
  const adapter = new CppAdapter('test.hpp');
57
57
  const detected = adapter.detectLanguage();
58
58
  expect(detected).toBe('cpp');
59
59
  });
60
60
 
61
61
  it('should detect language as cpp for .h files', () => {
62
- (readFileSync as vi.Mock).mockReturnValue('#pragma once');
62
+ (readFileSync as Mock).mockReturnValue('#pragma once');
63
63
  const adapter = new CppAdapter('test.h');
64
64
  const detected = adapter.detectLanguage();
65
65
  expect(detected).toBe('cpp');
66
66
  });
67
67
 
68
68
  it('should parse C++ file AST correctly', () => {
69
- (readFileSync as vi.Mock).mockReturnValue('int main() { return 0; }');
69
+ (readFileSync as Mock).mockReturnValue('int main() { return 0; }');
70
70
  const adapter = new CppAdapter('test.cpp');
71
71
  const ast = adapter.parseAST();
72
72
  expect(ast).toHaveProperty('content');
73
73
  expect(ast).toHaveProperty('language');
74
74
  expect(ast).toHaveProperty('filePath');
75
- expect(ast.language).toBe('cpp');
75
+ expect((ast as { language: unknown }).language).toBe('cpp');
76
76
  });
77
77
 
78
78
  it('should extract functions from C++ code', () => {
79
- (readFileSync as vi.Mock).mockReturnValue(`
79
+ (readFileSync as Mock).mockReturnValue(`
80
80
  int add(int a, int b) {
81
81
  return a + b;
82
82
  }
@@ -92,7 +92,7 @@ int main() {
92
92
  });
93
93
 
94
94
  it('should handle C++ strings with special characters', () => {
95
- (readFileSync as vi.Mock).mockReturnValue(`
95
+ (readFileSync as Mock).mockReturnValue(`
96
96
  int main() {
97
97
  const char* str = "Hello \\"World\\"";
98
98
  char c = '\\'';
@@ -107,7 +107,7 @@ int main() {
107
107
  });
108
108
 
109
109
  it('should throw error when file cannot be read', () => {
110
- (readFileSync as vi.Mock).mockImplementation(() => {
110
+ (readFileSync as Mock).mockImplementation(() => {
111
111
  throw new Error('Could not read file');
112
112
  });
113
113
 
@@ -125,7 +125,7 @@ int main() {
125
125
  describe('CppAdapter - extractCodeBlock edge cases', () => {
126
126
  it('should skip single-line block comment before the opening brace', () => {
127
127
  const src = `int foo() /* inline comment */ { return 1; }`;
128
- (readFileSync as vi.Mock).mockReturnValue(src);
128
+ (readFileSync as Mock).mockReturnValue(src);
129
129
  const adapter = new CppAdapter('test.cpp');
130
130
  const block = adapter.extractCodeBlock(0);
131
131
  expect(block).toContain('{ return 1; }');
@@ -142,7 +142,7 @@ describe('CppAdapter - extractCodeBlock edge cases', () => {
142
142
  ' return 42;',
143
143
  '}',
144
144
  ].join('\n');
145
- (readFileSync as vi.Mock).mockReturnValue(src);
145
+ (readFileSync as Mock).mockReturnValue(src);
146
146
  const adapter = new CppAdapter('test.cpp');
147
147
  const block = adapter.extractCodeBlock(0);
148
148
  expect(block).toContain('return 42;');
@@ -151,7 +151,7 @@ describe('CppAdapter - extractCodeBlock edge cases', () => {
151
151
 
152
152
  it('should ignore braces inside block comments', () => {
153
153
  const src = `int foo() /* fake { brace } here */ { return 7; }`;
154
- (readFileSync as vi.Mock).mockReturnValue(src);
154
+ (readFileSync as Mock).mockReturnValue(src);
155
155
  const adapter = new CppAdapter('test.cpp');
156
156
  const block = adapter.extractCodeBlock(0);
157
157
  expect(block).toContain('return 7;');
@@ -160,7 +160,7 @@ describe('CppAdapter - extractCodeBlock edge cases', () => {
160
160
 
161
161
  it('should count nested braces correctly via braceCount++ path', () => {
162
162
  const src = `void foo() { if (x) { return; } }`;
163
- (readFileSync as vi.Mock).mockReturnValue(src);
163
+ (readFileSync as Mock).mockReturnValue(src);
164
164
  const adapter = new CppAdapter('test.cpp');
165
165
  const block = adapter.extractCodeBlock(0);
166
166
  expect(block).toBe('void foo() { if (x) { return; } }');
@@ -168,7 +168,7 @@ describe('CppAdapter - extractCodeBlock edge cases', () => {
168
168
 
169
169
  it('should handle deeply nested braces (triple nesting)', () => {
170
170
  const src = `int bar() { if (a) { while (b) { c++; } } return 0; }`;
171
- (readFileSync as vi.Mock).mockReturnValue(src);
171
+ (readFileSync as Mock).mockReturnValue(src);
172
172
  const adapter = new CppAdapter('test.cpp');
173
173
  const block = adapter.extractCodeBlock(0);
174
174
  expect(block.trim().endsWith('}')).toBe(true);
@@ -190,7 +190,7 @@ describe('CppAdapter - extractCodeBlock edge cases', () => {
190
190
  ' return 0;',
191
191
  '}',
192
192
  ].join('\n');
193
- (readFileSync as vi.Mock).mockReturnValue(src);
193
+ (readFileSync as Mock).mockReturnValue(src);
194
194
  const adapter = new CppAdapter('test.cpp');
195
195
  const block = adapter.extractCodeBlock(0);
196
196
  expect(block.trim().endsWith('}')).toBe(true);
@@ -205,7 +205,7 @@ describe('CppAdapter - extractCodeBlock edge cases', () => {
205
205
  ' return 5;',
206
206
  '}',
207
207
  ].join('\n');
208
- (readFileSync as vi.Mock).mockReturnValue(src);
208
+ (readFileSync as Mock).mockReturnValue(src);
209
209
  const adapter = new CppAdapter('test.cpp');
210
210
  const block = adapter.extractCodeBlock(0);
211
211
  expect(block).toContain('return 5;');
@@ -223,7 +223,7 @@ describe('CppAdapter - extractCodeBlock edge cases', () => {
223
223
  ' int value_;',
224
224
  '};',
225
225
  ].join('\n');
226
- (readFileSync as vi.Mock).mockReturnValue(src);
226
+ (readFileSync as Mock).mockReturnValue(src);
227
227
  const adapter = new CppAdapter('test.cpp');
228
228
  const functions = adapter.extractFunctions();
229
229
  expect(Array.isArray(functions)).toBe(true);
@@ -240,7 +240,7 @@ describe('CppAdapter - extractCodeBlock edge cases', () => {
240
240
  ' int x;',
241
241
  '};',
242
242
  ].join('\n');
243
- (readFileSync as vi.Mock).mockReturnValue(src);
243
+ (readFileSync as Mock).mockReturnValue(src);
244
244
  const adapter = new CppAdapter('test.cpp');
245
245
  const classes = adapter.extractClasses();
246
246
  expect(Array.isArray(classes)).toBe(true);
@@ -250,13 +250,13 @@ describe('CppAdapter - extractCodeBlock edge cases', () => {
250
250
  });
251
251
 
252
252
  it('should count lines correctly', () => {
253
- (readFileSync as vi.Mock).mockReturnValue('line1\nline2\nline3');
253
+ (readFileSync as Mock).mockReturnValue('line1\nline2\nline3');
254
254
  const adapter = new CppAdapter('test.cpp');
255
255
  expect(adapter.countLines()).toBe(3);
256
256
  });
257
257
 
258
258
  it('should fall back to super.detectLanguage for non-cpp extensions', () => {
259
- (readFileSync as vi.Mock).mockReturnValue('content');
259
+ (readFileSync as Mock).mockReturnValue('content');
260
260
  const adapter = new CppAdapter('test.txt');
261
261
  expect(adapter.detectLanguage()).toBe('unknown');
262
262
  });
@@ -272,7 +272,7 @@ describe('CppAdapter - extractCodeBlock edge cases', () => {
272
272
  ' return -x;',
273
273
  '}',
274
274
  ].join('\n');
275
- (readFileSync as vi.Mock).mockReturnValue(src);
275
+ (readFileSync as Mock).mockReturnValue(src);
276
276
  const adapter = new CppAdapter('test.cpp');
277
277
  const functions = adapter.extractFunctions();
278
278
  expect(Array.isArray(functions)).toBe(true);
@@ -281,7 +281,7 @@ describe('CppAdapter - extractCodeBlock edge cases', () => {
281
281
 
282
282
  it('should extract functions with scope prefix (e.g. MyClass::method)', () => {
283
283
  const src = 'void MyClass::method() { return; }';
284
- (readFileSync as vi.Mock).mockReturnValue(src);
284
+ (readFileSync as Mock).mockReturnValue(src);
285
285
  const adapter = new CppAdapter('test.cpp');
286
286
  const functions = adapter.extractFunctions();
287
287
  expect(Array.isArray(functions)).toBe(true);