@boyingliu01/xp-gate 0.20.1 → 0.20.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/java.sh +373 -27
- package/gate-3.sh +51 -3
- package/gate-4.sh +32 -1
- package/hooks/gate-3.sh +51 -3
- package/hooks/gate-4.sh +32 -1
- package/hooks/pre-commit +275 -36
- package/lib/__tests__/doctor-spawn-capability.test.js +116 -0
- package/lib/__tests__/doctor-tui-skill-freshness.test.js +49 -10
- package/lib/doctor-tui.js +21 -3
- package/lib/doctor.js +111 -8
- package/lib/gate-audit.ts +3 -0
- package/lib/mattpocock-license-verdict.json +1 -1
- package/mock-policy/AGENTS.md +2 -2
- package/mutation/AGENTS.md +2 -2
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/admin-template-guidelines/SKILL.md +1 -0
- package/plugins/claude-code/skills/batch-grill-me/SKILL.md +1 -0
- package/plugins/claude-code/skills/clipboard-vision/SKILL.md +1 -0
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/claude-code/skills/domain-modeling/SKILL.md +1 -0
- package/plugins/claude-code/skills/grill-with-docs/SKILL.md +1 -0
- package/plugins/claude-code/skills/grilling/SKILL.md +1 -0
- package/plugins/claude-code/skills/improve-codebase-architecture/SKILL.md +1 -0
- package/plugins/claude-code/skills/ralph-loop/SKILL.md +1 -0
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/claude-code/skills/test-driven-development/SKILL.md +1 -0
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/claude-code/skills/test-specification-alignment/SKILL.md +1 -0
- package/plugins/claude-code/skills/to-issues/SKILL.md +1 -0
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/skills/admin-template-guidelines/SKILL.md +1 -0
- package/plugins/dsh/skills/batch-grill-me/SKILL.md +1 -0
- package/plugins/dsh/skills/clipboard-vision/SKILL.md +1 -0
- package/plugins/dsh/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/dsh/skills/domain-modeling/SKILL.md +1 -0
- package/plugins/dsh/skills/grill-with-docs/SKILL.md +1 -0
- package/plugins/dsh/skills/grilling/SKILL.md +1 -0
- package/plugins/dsh/skills/improve-codebase-architecture/SKILL.md +1 -0
- package/plugins/dsh/skills/ralph-loop/SKILL.md +1 -0
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/dsh/skills/test-driven-development/SKILL.md +1 -0
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/dsh/skills/test-specification-alignment/SKILL.md +1 -0
- package/plugins/dsh/skills/to-issues/SKILL.md +1 -0
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/admin-template-guidelines/SKILL.md +1 -0
- package/plugins/opencode/skills/batch-grill-me/SKILL.md +1 -0
- package/plugins/opencode/skills/clipboard-vision/SKILL.md +1 -0
- package/plugins/opencode/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/opencode/skills/domain-modeling/SKILL.md +1 -0
- package/plugins/opencode/skills/grill-with-docs/SKILL.md +1 -0
- package/plugins/opencode/skills/grilling/SKILL.md +1 -0
- package/plugins/opencode/skills/improve-codebase-architecture/SKILL.md +1 -0
- package/plugins/opencode/skills/ralph-loop/SKILL.md +1 -0
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/opencode/skills/test-driven-development/SKILL.md +1 -0
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/opencode/skills/test-specification-alignment/SKILL.md +1 -0
- package/plugins/opencode/skills/to-issues/SKILL.md +1 -0
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +2 -2
- package/principles/AGENTS.md +2 -2
- package/principles/adapters/__tests__/java.test.ts +317 -0
- package/principles/adapters/java.ts +325 -13
- package/skills/admin-template-guidelines/SKILL.md +1 -0
- package/skills/batch-grill-me/SKILL.md +1 -0
- package/skills/clipboard-vision/SKILL.md +1 -0
- package/skills/delphi-review/AGENTS.md +2 -2
- package/skills/domain-modeling/SKILL.md +1 -0
- package/skills/grill-with-docs/SKILL.md +1 -0
- package/skills/grilling/SKILL.md +1 -0
- package/skills/improve-codebase-architecture/SKILL.md +1 -0
- package/skills/ralph-loop/SKILL.md +1 -0
- package/skills/sprint-flow/AGENTS.md +2 -2
- package/skills/test-driven-development/SKILL.md +1 -0
- package/skills/test-specification-alignment/AGENTS.md +2 -2
- package/skills/test-specification-alignment/SKILL.md +1 -0
- package/skills/to-issues/SKILL.md +1 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: admin-template-guidelines
|
|
3
|
+
version: 1.0.0
|
|
3
4
|
description: "6 maintainability rules for AI-generated admin interfaces (Fastify + Nunjucks + HTMX + Alpine.js). Prevents route bloat, test inconsistency, Nunjucks traps, repeated data logic, auth blind spots, and HTMX/Alpine confusion."
|
|
4
5
|
maturity: alpha
|
|
5
6
|
---
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
3
|
+
**Generated:** 2026-10-12
|
|
4
4
|
**Commit:** unknown
|
|
5
5
|
**Branch:** unknown
|
|
6
|
-
**Version:** 0.20.
|
|
6
|
+
**Version:** 0.20.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: domain-modeling
|
|
3
|
+
version: 1.0.0
|
|
3
4
|
description: Build and sharpen a project's domain model. Use when the user wants to pin down domain terminology or a ubiquitous language, record an architectural decision, or when another skill needs to maintain the domain model.
|
|
4
5
|
---
|
|
5
6
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: improve-codebase-architecture
|
|
3
|
+
version: 1.0.0
|
|
3
4
|
description: Find deepening opportunities in a codebase, informed by domain language in CONTEXT.md and decisions in ADRs. Use when user wants to improve architecture, find refactoring opportunities, consolidate tightly-coupled modules, or make a codebase more AI-navigable. Run periodically (weekly or after surges of development).
|
|
4
5
|
---
|
|
5
6
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
3
|
+
**Generated:** 2026-10-12
|
|
4
4
|
**Commit:** unknown
|
|
5
5
|
**Branch:** unknown
|
|
6
|
-
**Version:** 0.20.
|
|
6
|
+
**Version:** 0.20.2.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
|
-
**Generated:** 2026-10-
|
|
3
|
+
**Generated:** 2026-10-12
|
|
4
4
|
**Commit:** unknown
|
|
5
5
|
**Branch:** unknown
|
|
6
|
-
**Version:** 0.20.
|
|
6
|
+
**Version:** 0.20.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: to-issues
|
|
3
|
+
version: 1.0.0
|
|
3
4
|
description: Break a plan, spec, or PRD into independently-grabbable issues using tracer-bullet vertical slices. Use when user wants to convert a plan into issues, create implementation tickets, or break down work into vertical slices. Integrates with XP-Gate sprint-flow Phase 1.
|
|
4
5
|
---
|
|
5
6
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: admin-template-guidelines
|
|
3
|
+
version: 1.0.0
|
|
3
4
|
description: "6 maintainability rules for AI-generated admin interfaces (Fastify + Nunjucks + HTMX + Alpine.js). Prevents route bloat, test inconsistency, Nunjucks traps, repeated data logic, auth blind spots, and HTMX/Alpine confusion."
|
|
4
5
|
maturity: alpha
|
|
5
6
|
---
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
3
|
+
**Generated:** 2026-10-12
|
|
4
4
|
**Commit:** unknown
|
|
5
5
|
**Branch:** unknown
|
|
6
|
-
**Version:** 0.20.
|
|
6
|
+
**Version:** 0.20.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: domain-modeling
|
|
3
|
+
version: 1.0.0
|
|
3
4
|
description: Build and sharpen a project's domain model. Use when the user wants to pin down domain terminology or a ubiquitous language, record an architectural decision, or when another skill needs to maintain the domain model.
|
|
4
5
|
---
|
|
5
6
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: improve-codebase-architecture
|
|
3
|
+
version: 1.0.0
|
|
3
4
|
description: Find deepening opportunities in a codebase, informed by domain language in CONTEXT.md and decisions in ADRs. Use when user wants to improve architecture, find refactoring opportunities, consolidate tightly-coupled modules, or make a codebase more AI-navigable. Run periodically (weekly or after surges of development).
|
|
4
5
|
---
|
|
5
6
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
3
|
+
**Generated:** 2026-10-12
|
|
4
4
|
**Commit:** unknown
|
|
5
5
|
**Branch:** unknown
|
|
6
|
-
**Version:** 0.20.
|
|
6
|
+
**Version:** 0.20.2.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
|
-
**Generated:** 2026-10-
|
|
3
|
+
**Generated:** 2026-10-12
|
|
4
4
|
**Commit:** unknown
|
|
5
5
|
**Branch:** unknown
|
|
6
|
-
**Version:** 0.20.
|
|
6
|
+
**Version:** 0.20.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: to-issues
|
|
3
|
+
version: 1.0.0
|
|
3
4
|
description: Break a plan, spec, or PRD into independently-grabbable issues using tracer-bullet vertical slices. Use when user wants to convert a plan into issues, create implementation tickets, or break down work into vertical slices. Integrates with XP-Gate sprint-flow Phase 1.
|
|
4
5
|
---
|
|
5
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xp-gate",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.2",
|
|
4
4
|
"displayName": "XP-Gate",
|
|
5
5
|
"description": "Extreme Programming quality gates + AI workflow skills for Qoder. Includes 12 pre-commit gates (Gate 0-11), 8 pre-push gates, Sprint Flow (6 phases), and Delphi multi-expert review (>=90% consensus).",
|
|
6
6
|
"author": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
3
|
+
**Generated:** 2026-10-12
|
|
4
4
|
**Commit:** unknown
|
|
5
5
|
**Branch:** unknown
|
|
6
|
-
**Version:** 0.20.
|
|
6
|
+
**Version:** 0.20.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
3
|
+
**Generated:** 2026-10-12
|
|
4
4
|
**Commit:** unknown
|
|
5
5
|
**Branch:** unknown
|
|
6
|
-
**Version:** 0.20.
|
|
6
|
+
**Version:** 0.20.2.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
|
-
**Generated:** 2026-10-
|
|
3
|
+
**Generated:** 2026-10-12
|
|
4
4
|
**Commit:** unknown
|
|
5
5
|
**Branch:** unknown
|
|
6
|
-
**Version:** 0.20.
|
|
6
|
+
**Version:** 0.20.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
package/principles/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# PRINCIPLES CHECKER MODULE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
3
|
+
**Generated:** 2026-10-12
|
|
4
4
|
**Commit:** unknown
|
|
5
5
|
**Branch:** unknown
|
|
6
|
-
**Version:** 0.20.
|
|
6
|
+
**Version:** 0.20.2.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.
|
|
@@ -65,4 +65,321 @@ describe('JavaAdapter', () => {
|
|
|
65
65
|
const adapter = new JavaAdapter('test.ts');
|
|
66
66
|
expect(adapter.detectLanguage()).toBe('typescript');
|
|
67
67
|
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @test REQ-507-04
|
|
72
|
+
* @intent 验证 JavaAdapter 为各规则提供其所需度量字段(#507 S4):修复前适配器
|
|
73
|
+
* 只返回 {name,type,line,code},读结构字段的 12 条规则在 Java 上静默
|
|
74
|
+
* 拿到 undefined,G4 报告已检查但 0 覆盖。覆盖矩阵见
|
|
75
|
+
* docs/java-principles-coverage.md。
|
|
76
|
+
* @covers AC-507-04-01
|
|
77
|
+
*/
|
|
78
|
+
describe('JavaAdapter metric extraction (#507)', () => {
|
|
79
|
+
interface FnInfo {
|
|
80
|
+
name: string;
|
|
81
|
+
startLine?: number;
|
|
82
|
+
line?: number;
|
|
83
|
+
length?: number;
|
|
84
|
+
paramCount?: number;
|
|
85
|
+
nestingDepth?: number;
|
|
86
|
+
hasTryCatch?: boolean;
|
|
87
|
+
ioOperations?: string[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const LONG_BODY = Array.from({ length: 60 }, (_, i) => ` int v${i} = ${i};`).join('\n');
|
|
91
|
+
|
|
92
|
+
const SAMPLE = `
|
|
93
|
+
public class Sample {
|
|
94
|
+
public void eightParams(int a, int b, int c, int d, int e, int f, int g, int h) {
|
|
95
|
+
System.out.println(a);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public void longOne() {
|
|
99
|
+
${LONG_BODY}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public void deepOne(int x) {
|
|
104
|
+
if (x > 0) {
|
|
105
|
+
if (x > 1) {
|
|
106
|
+
if (x > 2) {
|
|
107
|
+
if (x > 3) {
|
|
108
|
+
if (x > 4) {
|
|
109
|
+
System.out.println(x);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public void guardedIo() {
|
|
118
|
+
try {
|
|
119
|
+
java.sql.Connection c = java.sql.DriverManager.getConnection("jdbc:x");
|
|
120
|
+
c.createStatement().executeQuery("SELECT 1");
|
|
121
|
+
} catch (Exception e) {
|
|
122
|
+
// swallowed
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public void rawIo() {
|
|
127
|
+
java.sql.Connection c = java.sql.DriverManager.getConnection("jdbc:x");
|
|
128
|
+
c.createStatement().executeQuery("SELECT 1");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public int magic() { return 42; }
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
|
|
135
|
+
function adapterFor(content = SAMPLE): JavaAdapter {
|
|
136
|
+
(readFileSync as Mock).mockReturnValue(content);
|
|
137
|
+
return new JavaAdapter('Sample.java');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function findFn(fns: unknown[], name: string): FnInfo {
|
|
141
|
+
const found = fns.find(f => (f as FnInfo).name === name);
|
|
142
|
+
if (!found) throw new Error(`fixture function not extracted: ${name}`);
|
|
143
|
+
return found as FnInfo;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
it('reports paramCount (too-many-params contract)', () => {
|
|
147
|
+
const fns = adapterFor().extractFunctions() as FnInfo[];
|
|
148
|
+
expect(findFn(fns, 'eightParams').paramCount).toBe(8);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('reports length body span (long-function contract)', () => {
|
|
152
|
+
const fns = adapterFor().extractFunctions() as FnInfo[];
|
|
153
|
+
expect(findFn(fns, 'longOne').length).toBeGreaterThan(50);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('reports nestingDepth (deep-nesting contract)', () => {
|
|
157
|
+
const fns = adapterFor().extractFunctions() as FnInfo[];
|
|
158
|
+
expect(findFn(fns, 'deepOne').nestingDepth).toBeGreaterThanOrEqual(5);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('reports hasTryCatch and ioOperations (missing-error-handling contract)', () => {
|
|
162
|
+
const fns = adapterFor().extractFunctions() as FnInfo[];
|
|
163
|
+
const guarded = findFn(fns, 'guardedIo');
|
|
164
|
+
expect(guarded.hasTryCatch).toBe(true);
|
|
165
|
+
expect((guarded.ioOperations ?? []).length).toBeGreaterThan(0);
|
|
166
|
+
|
|
167
|
+
const raw = findFn(fns, 'rawIo');
|
|
168
|
+
expect(raw.hasTryCatch).toBe(false);
|
|
169
|
+
expect(raw.ioOperations).toContain('getConnection');
|
|
170
|
+
expect(raw.ioOperations).toContain('executeQuery');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('reports startLine for every function (violation line attribution)', () => {
|
|
174
|
+
const fns = adapterFor().extractFunctions() as FnInfo[];
|
|
175
|
+
for (const f of fns) {
|
|
176
|
+
expect(f.startLine).toBeGreaterThan(0);
|
|
177
|
+
expect(f.name).toBeTruthy();
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('does not report control keywords as functions', () => {
|
|
182
|
+
const fns = adapterFor().extractFunctions() as FnInfo[];
|
|
183
|
+
const names = fns.map(f => f.name);
|
|
184
|
+
for (const kw of ['if', 'for', 'while', 'switch', 'catch', 'return']) {
|
|
185
|
+
expect(names).not.toContain(kw);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('extract (magic numbers)', () => {
|
|
190
|
+
it('returns {value,line} and masks strings/comments', () => {
|
|
191
|
+
const content = `
|
|
192
|
+
// 999 in a comment must not count
|
|
193
|
+
String s = "1234 in a string must not count";
|
|
194
|
+
int x = 12345;
|
|
195
|
+
`;
|
|
196
|
+
(readFileSync as Mock).mockReturnValue(content);
|
|
197
|
+
const adapter = new JavaAdapter('M.java') as unknown as {
|
|
198
|
+
extract: () => Array<{ value: number; line: number }>;
|
|
199
|
+
};
|
|
200
|
+
const values = adapter.extract().map(n => n.value);
|
|
201
|
+
expect(values).toContain(12345);
|
|
202
|
+
expect(values).not.toContain(999);
|
|
203
|
+
expect(values).not.toContain(1234);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('reports digits used as call arguments (#507 blind review M2)', () => {
|
|
207
|
+
const content = `
|
|
208
|
+
public class Calls {
|
|
209
|
+
public int go() {
|
|
210
|
+
return Math.max(1, 99);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
`;
|
|
214
|
+
(readFileSync as Mock).mockReturnValue(content);
|
|
215
|
+
const adapter = new JavaAdapter('C.java') as unknown as {
|
|
216
|
+
extract: () => Array<{ value: number; line: number }>;
|
|
217
|
+
};
|
|
218
|
+
const values = adapter.extract().map(n => n.value);
|
|
219
|
+
expect(values).toContain(99);
|
|
220
|
+
expect(values).toContain(1);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('masks block comments including cross-line ones (#507 blind review M3)', () => {
|
|
224
|
+
const content = `
|
|
225
|
+
public class Commented {
|
|
226
|
+
/* int inlineBlock = 555; */
|
|
227
|
+
// string containing slashes must not swallow the rest: see below
|
|
228
|
+
String url = "http://x"; int y = 42;
|
|
229
|
+
/*
|
|
230
|
+
* int multi = 777;
|
|
231
|
+
* int line2 = 888;
|
|
232
|
+
*/
|
|
233
|
+
int live = 11;
|
|
234
|
+
}
|
|
235
|
+
`;
|
|
236
|
+
(readFileSync as Mock).mockReturnValue(content);
|
|
237
|
+
const adapter = new JavaAdapter('B.java') as unknown as {
|
|
238
|
+
extract: () => Array<{ value: number; line: number }>;
|
|
239
|
+
};
|
|
240
|
+
const values = adapter.extract().map(n => n.value);
|
|
241
|
+
expect(values).toContain(42); // after a string with `//`, same line
|
|
242
|
+
expect(values).toContain(11); // after a multi-line block comment
|
|
243
|
+
expect(values).not.toContain(555);
|
|
244
|
+
expect(values).not.toContain(777);
|
|
245
|
+
expect(values).not.toContain(888);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('skips static final constant initializers but reports bare literals', () => {
|
|
249
|
+
// The rule's remedy is "use a named constant" -- flagging the constant
|
|
250
|
+
// itself would make violations unfixable (#446 precedent on TS).
|
|
251
|
+
const content = `
|
|
252
|
+
public class C {
|
|
253
|
+
public static final int MAX = 3;
|
|
254
|
+
public int bare() { return 137; }
|
|
255
|
+
}
|
|
256
|
+
`;
|
|
257
|
+
(readFileSync as Mock).mockReturnValue(content);
|
|
258
|
+
const adapter = new JavaAdapter('C.java') as unknown as {
|
|
259
|
+
extract: () => Array<{ value: number; line: number }>;
|
|
260
|
+
};
|
|
261
|
+
const values = adapter.extract().map(n => n.value);
|
|
262
|
+
expect(values).not.toContain(3);
|
|
263
|
+
expect(values).toContain(137);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe('imports (unused-imports contract)', () => {
|
|
268
|
+
it('marks unused and used imports with line numbers', () => {
|
|
269
|
+
const content = `
|
|
270
|
+
import java.util.ArrayList;
|
|
271
|
+
import java.io.File;
|
|
272
|
+
|
|
273
|
+
public class C {
|
|
274
|
+
public ArrayList<String> go() { return new ArrayList<>(); }
|
|
275
|
+
}
|
|
276
|
+
`;
|
|
277
|
+
(readFileSync as Mock).mockReturnValue(content);
|
|
278
|
+
const adapter = new JavaAdapter('C.java') as unknown as {
|
|
279
|
+
imports: Array<{ name: string; line: number; used: boolean }>;
|
|
280
|
+
};
|
|
281
|
+
const imports = adapter.imports;
|
|
282
|
+
const list = imports.find(i => i.name === 'java.util.ArrayList');
|
|
283
|
+
expect(list?.used).toBe(true);
|
|
284
|
+
const file = imports.find(i => i.name === 'java.io.File');
|
|
285
|
+
expect(file?.used).toBe(false);
|
|
286
|
+
expect(file?.line).toBe(3);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
describe('extractClasses / extractInterfaces (srp/isp contracts)', () => {
|
|
291
|
+
it('reports methodCount per class (srp reads it directly)', () => {
|
|
292
|
+
const content = `
|
|
293
|
+
public class Busy {
|
|
294
|
+
public void m1() {}
|
|
295
|
+
public void m2() {}
|
|
296
|
+
public void m3() {}
|
|
297
|
+
public void m4() {}
|
|
298
|
+
public void m5() {}
|
|
299
|
+
public void m6() {}
|
|
300
|
+
}
|
|
301
|
+
`;
|
|
302
|
+
(readFileSync as Mock).mockReturnValue(content);
|
|
303
|
+
const classes = adapterFor(content).extractClasses() as Array<{ name: string; methodCount: number }>;
|
|
304
|
+
expect(classes.find(c => c.name === 'Busy')?.methodCount).toBe(6);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('extractInterfaces reports interfaces with methodCount (isp contract)', () => {
|
|
308
|
+
const content = `
|
|
309
|
+
public interface Worker {
|
|
310
|
+
void run();
|
|
311
|
+
void stop();
|
|
312
|
+
}
|
|
313
|
+
`;
|
|
314
|
+
(readFileSync as Mock).mockReturnValue(content);
|
|
315
|
+
const adapter = new JavaAdapter('W.java') as unknown as {
|
|
316
|
+
extractInterfaces: () => Array<{ name: string; methodCount: number }>;
|
|
317
|
+
};
|
|
318
|
+
const ifaces = adapter.extractInterfaces();
|
|
319
|
+
expect(ifaces.find(i => i.name === 'Worker')?.methodCount).toBe(2);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('finds generic classes and interfaces (#507 blind review M1)', () => {
|
|
323
|
+
const content = `
|
|
324
|
+
public class Box<T> {
|
|
325
|
+
private T value;
|
|
326
|
+
public T get() { return value; }
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
public interface Repo<T, ID> {
|
|
330
|
+
T findById(ID id);
|
|
331
|
+
}
|
|
332
|
+
`;
|
|
333
|
+
(readFileSync as Mock).mockReturnValue(content);
|
|
334
|
+
const classes = adapterFor(content).extractClasses() as Array<{ name: string; methodCount: number }>;
|
|
335
|
+
expect(classes.find(c => c.name === 'Box')?.methodCount).toBe(1);
|
|
336
|
+
const adapter = adapterFor(content) as unknown as {
|
|
337
|
+
extractInterfaces: () => Array<{ name: string; methodCount: number }>;
|
|
338
|
+
};
|
|
339
|
+
expect(adapter.extractInterfaces().find(i => i.name === 'Repo')?.methodCount).toBe(1);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('does not count masked-out members (strings/comments/inner calls)', () => {
|
|
343
|
+
const content = `
|
|
344
|
+
public class Noisy {
|
|
345
|
+
public void real() {
|
|
346
|
+
String s = "fake(1)";
|
|
347
|
+
// phantomCall(
|
|
348
|
+
/* alsoFake(2) */
|
|
349
|
+
helper(1);
|
|
350
|
+
}
|
|
351
|
+
public void second() {}
|
|
352
|
+
}
|
|
353
|
+
`;
|
|
354
|
+
(readFileSync as Mock).mockReturnValue(content);
|
|
355
|
+
const classes = adapterFor(content).extractClasses() as Array<{ name: string; methodCount: number; methods: string[] }>;
|
|
356
|
+
const methods = classes.find(c => c.name === 'Noisy')?.methods ?? [];
|
|
357
|
+
// The Java member heuristic mirrors the TS adapter (pure `name(` scan on
|
|
358
|
+
// the class body), so `helper(` -- a real, unqualified call statement --
|
|
359
|
+
// DOES count. What must never surface are the masked-out members:
|
|
360
|
+
// fake( lived inside a string literal
|
|
361
|
+
// phantomCall( lived in a line comment
|
|
362
|
+
// alsoFake( lived in a block comment
|
|
363
|
+
expect(methods).not.toContain('fake');
|
|
364
|
+
expect(methods).not.toContain('phantomCall');
|
|
365
|
+
expect(methods).not.toContain('alsoFake');
|
|
366
|
+
expect(methods).toEqual(expect.arrayContaining(['real', 'second']));
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('does not leak brace depth from string literals (#507 blind review M4)', () => {
|
|
370
|
+
const content = `
|
|
371
|
+
public class Formatter {
|
|
372
|
+
public String weird() {
|
|
373
|
+
String s = "unbalanced { brace";
|
|
374
|
+
return s;
|
|
375
|
+
}
|
|
376
|
+
public int after() { return 0; }
|
|
377
|
+
}
|
|
378
|
+
`;
|
|
379
|
+
(readFileSync as Mock).mockReturnValue(content);
|
|
380
|
+
const fns = adapterFor(content).extractFunctions() as Array<{ name: string; nestingDepth: number }>;
|
|
381
|
+
// `after` must not inherit inflated depth from the string's `{`.
|
|
382
|
+
expect(findFn(fns as FnInfo[], 'after').nestingDepth).toBeLessThan(3);
|
|
383
|
+
});
|
|
384
|
+
});
|
|
68
385
|
});
|