@boyingliu01/xp-gate 0.34.0 → 0.34.1

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 (33) hide show
  1. package/gate-13-compiler-strictness.sh +129 -4
  2. package/gate-4.sh +8 -1
  3. package/hooks/gate-13-compiler-strictness.sh +129 -4
  4. package/hooks/gate-4.sh +8 -1
  5. package/lib/__tests__/foreign-hooks-global.test.js +125 -0
  6. package/lib/__tests__/uninstall-global-residue.test.js +160 -0
  7. package/lib/doctor.js +70 -0
  8. package/lib/mattpocock-license-verdict.json +1 -1
  9. package/lib/uninstall.js +225 -1
  10. package/mock-policy/AGENTS.md +2 -2
  11. package/mutation/AGENTS.md +2 -2
  12. package/package.json +1 -1
  13. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  14. package/plugins/claude-code/skills/delphi-review/AGENTS.md +2 -2
  15. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +2 -2
  16. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +2 -2
  17. package/plugins/dsh/package.json +1 -1
  18. package/plugins/dsh/skills/delphi-review/AGENTS.md +2 -2
  19. package/plugins/dsh/skills/sprint-flow/AGENTS.md +2 -2
  20. package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +2 -2
  21. package/plugins/opencode/package.json +1 -1
  22. package/plugins/opencode/skills/delphi-review/AGENTS.md +2 -2
  23. package/plugins/opencode/skills/sprint-flow/AGENTS.md +2 -2
  24. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +2 -2
  25. package/plugins/qoder/plugin.json +1 -1
  26. package/plugins/qoder/skills/delphi-review/AGENTS.md +2 -2
  27. package/plugins/qoder/skills/sprint-flow/AGENTS.md +2 -2
  28. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +2 -2
  29. package/principles/AGENTS.md +2 -2
  30. package/principles/rules/solid/dip.ts +94 -14
  31. package/skills/delphi-review/AGENTS.md +2 -2
  32. package/skills/sprint-flow/AGENTS.md +2 -2
  33. package/skills/test-specification-alignment/AGENTS.md +2 -2
@@ -21,19 +21,80 @@ function shouldSkipInstantiation(className: string): boolean {
21
21
  return false;
22
22
  }
23
23
 
24
+ /**
25
+ * Composition-root markers (#627): a class whose JOB is to construct and wire
26
+ * concrete implementations. `new` there is dependency injection working, not being
27
+ * bypassed -- measured on a real 178-file Java repo, all 29 `solid.dip` warnings
28
+ * landed on 9 such wiring points.
29
+ *
30
+ * Matched against the class body text rather than a parsed AST because the rule
31
+ * consumes whatever each language adapter emits as "class code", and those shapes
32
+ * differ. These markers are conventions with low ambiguity, which is what makes a
33
+ * text test acceptable here.
34
+ */
35
+ const COMPOSITION_ROOT_MARKERS: RegExp[] = [
36
+ // Spring / Quarkus / Micronaut configuration and bean producers.
37
+ /@Configuration\b/, /@Bean\b/, /@Provides\b/, /@Produces\b/, /@Factory\b/,
38
+ // DI module definitions (Dagger / Guice / Spring Java Config).
39
+ /@Module\b/, /extends\s+AbstractModule\b/, /implements\s+Module\b/,
40
+ // Framework lifecycle entry points: the framework built the object, so wiring
41
+ // belongs in these methods.
42
+ /\bvoid\s+init\s*\(/, /\bvoid\s+initialize\s*\(/, /\bvoid\s+afterPropertiesSet\s*\(/,
43
+ /\bvoid\s+onCreate\s*\(/,
44
+ ];
45
+
46
+ /** A static factory method -- the class is an assembler by construction. */
47
+ const STATIC_FACTORY_METHOD = /\bstatic\s+[\w<>\[\],.?\s]+\s+\w+\s*\(/;
48
+
49
+ /**
50
+ * True when this class body is a composition root.
51
+ *
52
+ * A class whose name ends in Factory is already handled by
53
+ * shouldSkipInstantiation for its PRODUCT names; this covers the assEMBLER itself,
54
+ * e.g. `PlanCollectorFactory.assemble()` which news eight collaborators.
55
+ */
56
+ function isCompositionRoot(code: string, className?: string): boolean {
57
+ if (className && /(?:Factory|Assembler|Module|Config|Configuration|Provider)$/.test(className)) {
58
+ return true;
59
+ }
60
+ if (COMPOSITION_ROOT_MARKERS.some((marker) => marker.test(code))) return true;
61
+ return STATIC_FACTORY_METHOD.test(code);
62
+ }
63
+
24
64
  /** `new Foo(` -> `Foo`. */
25
65
  function classNameFromNewMatch(match: string): string {
26
66
  return match.replace(/new\s+/, '').replace(/\s*\(/, '');
27
67
  }
28
68
 
29
- /** Violations for one class body: each `new X(` that is not exempt. */
69
+ /**
70
+ * Violations for one class body: each `new X(` that is not exempt.
71
+ *
72
+ * #626: every violation used to carry the CLASS DECLARATION line, because the
73
+ * adapter only reports where the class starts and the match itself carried no
74
+ * position. A 200-line class with one instantiation on line 187 therefore pointed
75
+ * the developer at line 1 -- the message named the right file and the wrong place,
76
+ * which is the least useful combination: it looks actionable and is not.
77
+ *
78
+ * The offset of each match within the class body is now added to the class's
79
+ * starting line, so the reported line is the `new` expression itself. Multi-line
80
+ * matches count their newlines, since a match may span `new Foo(\n arg,\n)`.
81
+ *
82
+ * `classBodyStartLine` is the line the adapter says the class begins on; when the
83
+ * adapter cannot say (0), the violation keeps 0 rather than inventing line 1 --
84
+ * a placeholder 1 would look like a real location.
85
+ */
30
86
  function instantiationViolations(
31
- code: string, line: number, file: string, severity: Severity,
87
+ code: string, classBodyStartLine: number, file: string, severity: Severity,
32
88
  ): Violation[] {
33
89
  const violations: Violation[] = [];
34
- for (const match of code.match(/new\s+(\w+)\s*\(/g) || []) {
35
- const className = classNameFromNewMatch(match);
90
+ const newPattern = /new\s+(\w+)\s*\(/g;
91
+ let match: RegExpExecArray | null;
92
+ while ((match = newPattern.exec(code)) !== null) {
93
+ const className = classNameFromNewMatch(match[0]);
36
94
  if (shouldSkipInstantiation(className)) continue;
95
+ // Offset -> line: count the newlines between the class start and this match.
96
+ const linesIntoBody = (code.slice(0, match.index).match(/\n/g) || []).length;
97
+ const line = classBodyStartLine > 0 ? classBodyStartLine + linesIntoBody : 0;
37
98
  violations.push({
38
99
  file,
39
100
  line,
@@ -45,21 +106,40 @@ function instantiationViolations(
45
106
  return violations;
46
107
  }
47
108
 
109
+ /** Rule severity from the active config, defaulting to warning. */
110
+ function configuredSeverity(): Severity {
111
+ const settings = getActiveConfig().rules['solid']['dip'];
112
+ return (settings.severity as Severity) ?? 'warning';
113
+ }
114
+
115
+ /**
116
+ * Violations contributed by ONE extracted class.
117
+ *
118
+ * Split out of `dipRule.check` to keep that function inside the CCN ratchet: the
119
+ * #627 composition-root test and the #626 line arithmetic each added a branch, and
120
+ * the ratchet is there to force exactly this trade rather than a baseline refresh.
121
+ */
122
+ function classViolations(
123
+ clsAny: unknown, file: string, severity: Severity,
124
+ ): Violation[] {
125
+ const cls = clsAny as { code?: string; line?: number; name?: string };
126
+ if (!cls.code) return [];
127
+ // #627: a composition root's `new` is the inversion working, not bypassed.
128
+ // Skipping the whole class body is right here: when the class IS the wiring
129
+ // point, every instantiation in it is wiring.
130
+ if (isCompositionRoot(cls.code, cls.name)) return [];
131
+ return instantiationViolations(cls.code, cls.line || 0, file, severity);
132
+ }
133
+
48
134
  export const dipRule: Rule = {
49
135
  id: 'solid.dip',
50
136
  name: 'Dependency Inversion Principle Rule',
51
137
  threshold: 0,
52
138
  severity: 'warning',
53
139
  check: (file: string, adapter: import('../../types').Adapter): Violation[] => {
54
- const settings = getActiveConfig().rules['solid']['dip'];
55
- const severity = (settings.severity as Severity) ?? 'warning';
56
-
57
- const violations: Violation[] = [];
58
- for (const clsAny of adapter.extractClasses() || []) {
59
- const cls = clsAny as { code?: string; line?: number };
60
- if (!cls.code) continue;
61
- violations.push(...instantiationViolations(cls.code, cls.line || 0, file, severity));
62
- }
63
- return violations;
140
+ const severity = configuredSeverity();
141
+ return (adapter.extractClasses() || []).flatMap((clsAny) =>
142
+ classViolations(clsAny, file, severity),
143
+ );
64
144
  }
65
145
  };
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
3
  **Generated:** 2026-10-30
4
- **Commit:** 6ac15e8c
4
+ **Commit:** e583bcfb
5
5
  **Branch:** main
6
- **Version:** 0.34.0.0
6
+ **Version:** 0.34.1.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
3
  **Generated:** 2026-10-30
4
- **Commit:** 6ac15e8c
4
+ **Commit:** e583bcfb
5
5
  **Branch:** main
6
- **Version:** 0.34.0.0
6
+ **Version:** 0.34.1.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-10-30
4
- **Commit:** 6ac15e8c
4
+ **Commit:** e583bcfb
5
5
  **Branch:** main
6
- **Version:** 0.34.0.0
6
+ **Version:** 0.34.1.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.