codesift-mcp 0.7.0 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/README.md +3 -3
  2. package/dist/cli/git-hooks-installer.d.ts.map +1 -1
  3. package/dist/cli/git-hooks-installer.js +18 -5
  4. package/dist/cli/git-hooks-installer.js.map +1 -1
  5. package/dist/cli/hooks.d.ts.map +1 -1
  6. package/dist/cli/hooks.js +106 -2
  7. package/dist/cli/hooks.js.map +1 -1
  8. package/dist/cli/setup.d.ts +5 -0
  9. package/dist/cli/setup.d.ts.map +1 -1
  10. package/dist/cli/setup.js +31 -5
  11. package/dist/cli/setup.js.map +1 -1
  12. package/dist/config.d.ts +2 -1
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +10 -1
  15. package/dist/config.js.map +1 -1
  16. package/dist/instructions.d.ts +1 -1
  17. package/dist/instructions.d.ts.map +1 -1
  18. package/dist/instructions.js +6 -1
  19. package/dist/instructions.js.map +1 -1
  20. package/dist/parser/extractors/hono.d.ts.map +1 -1
  21. package/dist/parser/extractors/hono.js +21 -13
  22. package/dist/parser/extractors/hono.js.map +1 -1
  23. package/dist/parser/extractors/php.d.ts +12 -0
  24. package/dist/parser/extractors/php.d.ts.map +1 -1
  25. package/dist/parser/extractors/php.js +440 -26
  26. package/dist/parser/extractors/php.js.map +1 -1
  27. package/dist/register-tool-loaders.d.ts +16 -0
  28. package/dist/register-tool-loaders.d.ts.map +1 -1
  29. package/dist/register-tool-loaders.js +26 -0
  30. package/dist/register-tool-loaders.js.map +1 -1
  31. package/dist/register-tools.d.ts +3 -1
  32. package/dist/register-tools.d.ts.map +1 -1
  33. package/dist/register-tools.js +354 -7
  34. package/dist/register-tools.js.map +1 -1
  35. package/dist/retrieval/codebase-retrieval.d.ts.map +1 -1
  36. package/dist/retrieval/codebase-retrieval.js +22 -0
  37. package/dist/retrieval/codebase-retrieval.js.map +1 -1
  38. package/dist/retrieval/retrieval-schemas.d.ts +4 -0
  39. package/dist/retrieval/retrieval-schemas.d.ts.map +1 -1
  40. package/dist/retrieval/semantic-handlers.js +1 -1
  41. package/dist/retrieval/semantic-handlers.js.map +1 -1
  42. package/dist/search/semantic.d.ts +21 -5
  43. package/dist/search/semantic.d.ts.map +1 -1
  44. package/dist/search/semantic.js +129 -4
  45. package/dist/search/semantic.js.map +1 -1
  46. package/dist/search/tool-ranker.js +1 -1
  47. package/dist/search/tool-ranker.js.map +1 -1
  48. package/dist/server-helpers.d.ts.map +1 -1
  49. package/dist/server-helpers.js +96 -1
  50. package/dist/server-helpers.js.map +1 -1
  51. package/dist/storage/index-store.d.ts.map +1 -1
  52. package/dist/storage/index-store.js +7 -5
  53. package/dist/storage/index-store.js.map +1 -1
  54. package/dist/storage/registry.d.ts +28 -4
  55. package/dist/storage/registry.d.ts.map +1 -1
  56. package/dist/storage/registry.js +126 -5
  57. package/dist/storage/registry.js.map +1 -1
  58. package/dist/storage/usage-stats.d.ts +2 -0
  59. package/dist/storage/usage-stats.d.ts.map +1 -1
  60. package/dist/storage/usage-stats.js +6 -0
  61. package/dist/storage/usage-stats.js.map +1 -1
  62. package/dist/storage/usage-tracker.js +1 -1
  63. package/dist/storage/usage-tracker.js.map +1 -1
  64. package/dist/tools/_helpers.d.ts.map +1 -1
  65. package/dist/tools/_helpers.js +14 -0
  66. package/dist/tools/_helpers.js.map +1 -1
  67. package/dist/tools/conversation-tools.js +1 -1
  68. package/dist/tools/conversation-tools.js.map +1 -1
  69. package/dist/tools/index-tools.d.ts +12 -0
  70. package/dist/tools/index-tools.d.ts.map +1 -1
  71. package/dist/tools/index-tools.js +52 -5
  72. package/dist/tools/index-tools.js.map +1 -1
  73. package/dist/tools/insights-tools.d.ts +137 -0
  74. package/dist/tools/insights-tools.d.ts.map +1 -0
  75. package/dist/tools/insights-tools.js +438 -0
  76. package/dist/tools/insights-tools.js.map +1 -0
  77. package/dist/tools/pattern-tools.d.ts +7 -0
  78. package/dist/tools/pattern-tools.d.ts.map +1 -1
  79. package/dist/tools/pattern-tools.js +287 -15
  80. package/dist/tools/pattern-tools.js.map +1 -1
  81. package/dist/tools/php-tools.d.ts +78 -4
  82. package/dist/tools/php-tools.d.ts.map +1 -1
  83. package/dist/tools/php-tools.js +824 -42
  84. package/dist/tools/php-tools.js.map +1 -1
  85. package/dist/tools/php8-compat-tools.d.ts +62 -0
  86. package/dist/tools/php8-compat-tools.d.ts.map +1 -0
  87. package/dist/tools/php8-compat-tools.js +287 -0
  88. package/dist/tools/php8-compat-tools.js.map +1 -0
  89. package/dist/tools/php8-migration-candidates-tools.d.ts +68 -0
  90. package/dist/tools/php8-migration-candidates-tools.d.ts.map +1 -0
  91. package/dist/tools/php8-migration-candidates-tools.js +476 -0
  92. package/dist/tools/php8-migration-candidates-tools.js.map +1 -0
  93. package/dist/tools/phpstan-baseline-tools.d.ts +62 -0
  94. package/dist/tools/phpstan-baseline-tools.d.ts.map +1 -0
  95. package/dist/tools/phpstan-baseline-tools.js +263 -0
  96. package/dist/tools/phpstan-baseline-tools.js.map +1 -0
  97. package/dist/tools/project-tools.d.ts +4 -2
  98. package/dist/tools/project-tools.d.ts.map +1 -1
  99. package/dist/tools/project-tools.js +19 -6
  100. package/dist/tools/project-tools.js.map +1 -1
  101. package/dist/tools/react-tools.d.ts +24 -0
  102. package/dist/tools/react-tools.d.ts.map +1 -1
  103. package/dist/tools/react-tools.js +292 -3
  104. package/dist/tools/react-tools.js.map +1 -1
  105. package/dist/tools/search-tools.d.ts.map +1 -1
  106. package/dist/tools/search-tools.js +92 -10
  107. package/dist/tools/search-tools.js.map +1 -1
  108. package/dist/tools/symbol-tools.d.ts.map +1 -1
  109. package/dist/tools/symbol-tools.js +4 -1
  110. package/dist/tools/symbol-tools.js.map +1 -1
  111. package/dist/tools/yii-console-tools.d.ts +69 -0
  112. package/dist/tools/yii-console-tools.d.ts.map +1 -0
  113. package/dist/tools/yii-console-tools.js +256 -0
  114. package/dist/tools/yii-console-tools.js.map +1 -0
  115. package/dist/tools/yii-migrations-tools.d.ts +79 -0
  116. package/dist/tools/yii-migrations-tools.d.ts.map +1 -0
  117. package/dist/tools/yii-migrations-tools.js +543 -0
  118. package/dist/tools/yii-migrations-tools.js.map +1 -0
  119. package/dist/tools/yii-modules-tools.d.ts +63 -0
  120. package/dist/tools/yii-modules-tools.d.ts.map +1 -0
  121. package/dist/tools/yii-modules-tools.js +201 -0
  122. package/dist/tools/yii-modules-tools.js.map +1 -0
  123. package/dist/tools/yii-rbac-tools.d.ts +89 -0
  124. package/dist/tools/yii-rbac-tools.d.ts.map +1 -0
  125. package/dist/tools/yii-rbac-tools.js +238 -0
  126. package/dist/tools/yii-rbac-tools.js.map +1 -0
  127. package/dist/tools/yii3-attribute-candidates-tools.d.ts +72 -0
  128. package/dist/tools/yii3-attribute-candidates-tools.d.ts.map +1 -0
  129. package/dist/tools/yii3-attribute-candidates-tools.js +301 -0
  130. package/dist/tools/yii3-attribute-candidates-tools.js.map +1 -0
  131. package/dist/tools/yii3-migration-tools.d.ts +74 -0
  132. package/dist/tools/yii3-migration-tools.d.ts.map +1 -0
  133. package/dist/tools/yii3-migration-tools.js +440 -0
  134. package/dist/tools/yii3-migration-tools.js.map +1 -0
  135. package/dist/types.d.ts +5 -1
  136. package/dist/types.d.ts.map +1 -1
  137. package/dist/utils/constant-file-pattern.d.ts +3 -1
  138. package/dist/utils/constant-file-pattern.d.ts.map +1 -1
  139. package/dist/utils/constant-file-pattern.js +6 -4
  140. package/dist/utils/constant-file-pattern.js.map +1 -1
  141. package/dist/utils/heritage-edges.d.ts +16 -0
  142. package/dist/utils/heritage-edges.d.ts.map +1 -1
  143. package/dist/utils/heritage-edges.js +31 -10
  144. package/dist/utils/heritage-edges.js.map +1 -1
  145. package/dist/utils/source-stripper.d.ts +23 -0
  146. package/dist/utils/source-stripper.d.ts.map +1 -0
  147. package/dist/utils/source-stripper.js +239 -0
  148. package/dist/utils/source-stripper.js.map +1 -0
  149. package/dist/utils/tsconfig-paths.d.ts +2 -2
  150. package/dist/utils/tsconfig-paths.d.ts.map +1 -1
  151. package/dist/utils/tsconfig-paths.js +10 -4
  152. package/dist/utils/tsconfig-paths.js.map +1 -1
  153. package/dist/utils/wall-clock.d.ts +9 -0
  154. package/dist/utils/wall-clock.d.ts.map +1 -0
  155. package/dist/utils/wall-clock.js +19 -0
  156. package/dist/utils/wall-clock.js.map +1 -0
  157. package/package.json +1 -1
  158. package/rules/codesift.md +10 -3
  159. package/rules/codesift.mdc +10 -3
  160. package/rules/codex.md +10 -3
  161. package/rules/gemini.md +10 -3
@@ -0,0 +1,476 @@
1
+ /**
2
+ * PHP 8 modernization candidate finder (M1).
3
+ *
4
+ * Companion to M3 (php8_compat_check). Where M3 is the gating tool — "will
5
+ * this PHP 8 merge break anything" — M1 is the post-merge modernization
6
+ * tool. After PHP 8 lands, run M1 to surface places where the legacy
7
+ * 7.2-style idiom can be tightened up using new language features.
8
+ *
9
+ * Sub-checks (each is a regex pattern over file content):
10
+ *
11
+ * promotable-ctor ctor with N parameters whose body only does
12
+ * $this->x = $x assignments — collapse to
13
+ * promoted constructor (PHP 8.0+).
14
+ * docblock-to-typed-property /** @var T *\/ above an untyped public/private
15
+ * property — convert to inline `public T $x`
16
+ * (PHP 7.4+).
17
+ * nullable-flag-to-syntax /** @var T|null *\/ — same conversion but
18
+ * produces `public ?T $x`.
19
+ * readonly-candidate property only assigned in __construct — add
20
+ * `readonly` modifier (PHP 8.1+).
21
+ * enum-from-class-consts class with N const NAME = 'value' entries +
22
+ * a getValues()/getOptions() method — convert
23
+ * to a backed enum (PHP 8.1+).
24
+ * match-from-switch switch ($x) { case A: return ...; ...}
25
+ * without fall-through — convert to match.
26
+ *
27
+ * Each finding includes:
28
+ * - file, line, snippet
29
+ * - rule_id, severity (always "modernize" — informational, never blocking)
30
+ * - suggested_replacement: rough sketch of the new form (string, NOT
31
+ * auto-applied)
32
+ * - confidence: "high" | "medium" | "low"
33
+ *
34
+ * The tool is intentionally lossy: many candidates will be rejected after
35
+ * human review (e.g., a property "only assigned in ctor" might still be
36
+ * mutated via reflection for testing). The point is to triage thousands
37
+ * of files into a small reviewable list, not to auto-fix.
38
+ */
39
+ import { readFile } from "node:fs/promises";
40
+ import { join } from "node:path";
41
+ import { getCodeIndex } from "./index-tools.js";
42
+ // ---------------------------------------------------------------------------
43
+ // Implementation
44
+ // ---------------------------------------------------------------------------
45
+ const VENDOR_RE = /(^|\/)(?:vendor|node_modules|runtime|tests\/_data)(\/|$)/;
46
+ const SAMPLE_LIMIT_PER_RULE = 5;
47
+ export async function findPhp8MigrationCandidates(repo, options) {
48
+ const index = await getCodeIndex(repo);
49
+ if (!index)
50
+ throw new Error(`Repository "${repo}" not found.`);
51
+ const sampleLimit = options?.max_samples_per_rule ?? SAMPLE_LIMIT_PER_RULE;
52
+ const includeVendor = options?.include_vendor ?? false;
53
+ const filePattern = options?.file_pattern;
54
+ const ruleFilter = options?.rules ? new Set(options.rules) : null;
55
+ const phpFiles = index.files.filter((f) => {
56
+ if (!f.path.endsWith(".php"))
57
+ return false;
58
+ if (!includeVendor && VENDOR_RE.test(f.path))
59
+ return false;
60
+ if (filePattern && !f.path.includes(filePattern))
61
+ return false;
62
+ return true;
63
+ });
64
+ const candidates = [];
65
+ // Symbol-level rules use the index directly (faster, no second file read).
66
+ if (!ruleFilter || ruleFilter.has("docblock-to-typed-property")) {
67
+ findDocblockToTypedProperty(index, candidates);
68
+ }
69
+ if (!ruleFilter || ruleFilter.has("nullable-flag-to-syntax")) {
70
+ findNullableFlagToSyntax(index, candidates);
71
+ }
72
+ if (!ruleFilter || ruleFilter.has("readonly-candidate")) {
73
+ findReadonlyCandidates(index, candidates);
74
+ }
75
+ if (!ruleFilter || ruleFilter.has("enum-from-class-consts")) {
76
+ findEnumFromClassConsts(index, candidates);
77
+ }
78
+ if (!ruleFilter || ruleFilter.has("promotable-ctor")) {
79
+ findPromotableCtor(index, candidates);
80
+ }
81
+ // File-level rule (match-from-switch) needs the raw source — switch/match
82
+ // can span across functions, so a per-symbol scan would miss cross-method
83
+ // patterns. Scoped to the file set we already filtered.
84
+ if (!ruleFilter || ruleFilter.has("match-from-switch")) {
85
+ await Promise.all(phpFiles.map(async (f) => {
86
+ let content;
87
+ try {
88
+ content = await readFile(join(index.root, f.path), "utf-8");
89
+ }
90
+ catch {
91
+ return;
92
+ }
93
+ findMatchFromSwitch(content, f.path, candidates);
94
+ }));
95
+ }
96
+ // Group by rule with sample cap.
97
+ const byRuleMap = new Map();
98
+ for (const c of candidates) {
99
+ if (!byRuleMap.has(c.rule_id))
100
+ byRuleMap.set(c.rule_id, []);
101
+ byRuleMap.get(c.rule_id).push(c);
102
+ }
103
+ const byRule = [...byRuleMap.entries()].map(([rule_id, list]) => ({
104
+ rule_id,
105
+ count: list.length,
106
+ description: RULE_DESCRIPTIONS[rule_id],
107
+ samples: list.slice(0, sampleLimit),
108
+ }));
109
+ byRule.sort((a, b) => b.count - a.count);
110
+ return {
111
+ repo,
112
+ scanned_files: phpFiles.length,
113
+ total_candidates: candidates.length,
114
+ by_rule: byRule,
115
+ candidates,
116
+ };
117
+ }
118
+ // ---------------------------------------------------------------------------
119
+ // Rule descriptions (used in output for clarity)
120
+ // ---------------------------------------------------------------------------
121
+ const RULE_DESCRIPTIONS = {
122
+ "promotable-ctor": "__construct that only assigns parameters to same-named properties — collapse to PHP 8.0 promoted constructor.",
123
+ "docblock-to-typed-property": "/** @var T */ above an untyped property — convert to PHP 7.4 inline typed property `public T $x;`.",
124
+ "nullable-flag-to-syntax": "/** @var T|null */ above a property — convert to PHP 7.4 nullable typed property `public ?T $x;`.",
125
+ "readonly-candidate": "Property only assigned in __construct, no setter, no unset — add `readonly` modifier (PHP 8.1+).",
126
+ "enum-from-class-consts": "Pre-enum idiom: class of `const FOO = 'foo'` literals + getValues()/getOptions() — convert to PHP 8.1 backed enum.",
127
+ "match-from-switch": "switch with no fall-through where every case returns a value — convert to PHP 8.0 match expression.",
128
+ };
129
+ // ---------------------------------------------------------------------------
130
+ // Rule implementations
131
+ // ---------------------------------------------------------------------------
132
+ /**
133
+ * docblock-to-typed-property: inline type would replace @var. Uses the v2.0.0
134
+ * extractor's `meta.type_source: "phpdoc"` flag — that's exactly the cohort
135
+ * we want (property has type info in the docblock but not inline).
136
+ */
137
+ function findDocblockToTypedProperty(index, out) {
138
+ for (const sym of index.symbols) {
139
+ if (sym.kind !== "field")
140
+ continue;
141
+ if (!sym.file.endsWith(".php"))
142
+ continue;
143
+ if (sym.meta?.from_constructor)
144
+ continue; // skip promoted-ctor synth fields
145
+ const ts = sym.meta?.type_source;
146
+ if (ts !== "phpdoc")
147
+ continue;
148
+ const t = sym.meta?.type;
149
+ if (!t)
150
+ continue;
151
+ // Skip rule 2 patterns (nullable union) — they belong to nullable-flag-to-syntax
152
+ if (/\|null\b|\bnull\|/.test(t))
153
+ continue;
154
+ const visibility = sym.meta?.visibility ?? "public";
155
+ const propName = sym.name.replace(/^\$/, "");
156
+ out.push({
157
+ rule_id: "docblock-to-typed-property",
158
+ file: sym.file,
159
+ line: sym.start_line,
160
+ snippet: `/** @var ${t} */ ${visibility} ${sym.name}`,
161
+ description: RULE_DESCRIPTIONS["docblock-to-typed-property"],
162
+ suggested_replacement: `${visibility} ${normalizeDocblockType(t)} \$${propName};`,
163
+ confidence: rateTypeConfidence(t),
164
+ });
165
+ }
166
+ }
167
+ /**
168
+ * nullable-flag-to-syntax: `@var T|null` → `?T`. Carved out from
169
+ * docblock-to-typed-property so callers can decide they want only the
170
+ * nullable conversion (which is the safer of the two — it never loses
171
+ * information).
172
+ */
173
+ function findNullableFlagToSyntax(index, out) {
174
+ for (const sym of index.symbols) {
175
+ if (sym.kind !== "field")
176
+ continue;
177
+ if (!sym.file.endsWith(".php"))
178
+ continue;
179
+ if (sym.meta?.from_constructor)
180
+ continue;
181
+ if (sym.meta?.type_source !== "phpdoc")
182
+ continue;
183
+ const t = sym.meta?.type;
184
+ if (!t)
185
+ continue;
186
+ if (!/\|null\b|\bnull\|/.test(t))
187
+ continue;
188
+ const baseType = t.replace(/\|null$|^null\|/, "");
189
+ if (!baseType || baseType.includes("|"))
190
+ continue; // Multi-type union — not a simple ?T
191
+ const visibility = sym.meta?.visibility ?? "public";
192
+ const propName = sym.name.replace(/^\$/, "");
193
+ out.push({
194
+ rule_id: "nullable-flag-to-syntax",
195
+ file: sym.file,
196
+ line: sym.start_line,
197
+ snippet: `/** @var ${t} */ ${visibility} ${sym.name}`,
198
+ description: RULE_DESCRIPTIONS["nullable-flag-to-syntax"],
199
+ suggested_replacement: `${visibility} ?${normalizeDocblockType(baseType)} \$${propName};`,
200
+ confidence: rateTypeConfidence(baseType),
201
+ });
202
+ }
203
+ }
204
+ /**
205
+ * readonly-candidate: property assigned only in __construct. We approximate
206
+ * by walking the parent class's source for `$this->propName = ` patterns
207
+ * and checking that all of them appear inside the __construct body (or
208
+ * inside a method we know is the ctor). Conservative — false positives
209
+ * acceptable for a discovery tool.
210
+ */
211
+ function findReadonlyCandidates(index, out) {
212
+ // Group fields by parent class id so we can scan each class once.
213
+ const fieldsByClass = new Map();
214
+ for (const sym of index.symbols) {
215
+ if (sym.kind !== "field")
216
+ continue;
217
+ if (!sym.parent)
218
+ continue;
219
+ if (sym.meta?.is_readonly)
220
+ continue; // already readonly
221
+ if (sym.meta?.is_static)
222
+ continue;
223
+ if (!fieldsByClass.has(sym.parent))
224
+ fieldsByClass.set(sym.parent, []);
225
+ fieldsByClass.get(sym.parent).push(sym);
226
+ }
227
+ for (const [classId, fields] of fieldsByClass.entries()) {
228
+ const cls = index.symbols.find((s) => s.id === classId);
229
+ if (!cls || !cls.source)
230
+ continue;
231
+ // Find the constructor method by name (could be __construct).
232
+ const ctor = index.symbols.find((s) => s.parent === classId && s.kind === "method" && s.name === "__construct");
233
+ const ctorSource = ctor?.source ?? "";
234
+ for (const f of fields) {
235
+ const propName = f.name.replace(/^\$/, "");
236
+ // All assignment sites for $this->propName in the class body.
237
+ const assignRe = new RegExp(`\\$this->${escapeRegex(propName)}\\s*=`, "g");
238
+ const allAssigns = (cls.source.match(assignRe) ?? []).length;
239
+ if (allAssigns === 0)
240
+ continue;
241
+ // How many of those assignments are inside the ctor body?
242
+ const ctorAssigns = (ctorSource.match(assignRe) ?? []).length;
243
+ if (ctorAssigns !== allAssigns)
244
+ continue;
245
+ // Skip if there is an explicit setter that mutates this property.
246
+ const setterRe = new RegExp(`function\\s+set${propName.charAt(0).toUpperCase()}${propName.slice(1)}\\s*\\(`, "i");
247
+ if (setterRe.test(cls.source))
248
+ continue;
249
+ const visibility = f.meta?.visibility ?? "public";
250
+ const inlineType = f.meta?.type ?? "mixed";
251
+ out.push({
252
+ rule_id: "readonly-candidate",
253
+ file: f.file,
254
+ line: f.start_line,
255
+ snippet: `${visibility} ${inlineType} \$${propName}`,
256
+ description: RULE_DESCRIPTIONS["readonly-candidate"],
257
+ suggested_replacement: `${visibility} readonly ${inlineType} \$${propName};`,
258
+ confidence: f.meta?.type_source === "inline" ? "high" : "medium",
259
+ });
260
+ }
261
+ }
262
+ }
263
+ /**
264
+ * enum-from-class-consts: classes that look like a pre-enum bag-of-constants.
265
+ * Heuristic: a class with >=3 `constant` symbols whose values are all
266
+ * string literals AND a static method named getValues / getOptions /
267
+ * getList / cases (which devs commonly add to enumerate the constants).
268
+ */
269
+ function findEnumFromClassConsts(index, out) {
270
+ const constsByClass = new Map();
271
+ for (const sym of index.symbols) {
272
+ if (sym.kind !== "constant")
273
+ continue;
274
+ if (!sym.parent)
275
+ continue;
276
+ if (!sym.file.endsWith(".php"))
277
+ continue;
278
+ if (!constsByClass.has(sym.parent))
279
+ constsByClass.set(sym.parent, []);
280
+ constsByClass.get(sym.parent).push(sym);
281
+ }
282
+ const ENUM_HELPER_NAMES = new Set([
283
+ "getValues",
284
+ "getOptions",
285
+ "getList",
286
+ "all",
287
+ "labels",
288
+ "names",
289
+ "values",
290
+ ]);
291
+ for (const [classId, consts] of constsByClass.entries()) {
292
+ if (consts.length < 3)
293
+ continue;
294
+ const cls = index.symbols.find((s) => s.id === classId);
295
+ if (!cls || cls.kind !== "class")
296
+ continue;
297
+ // All constants must have string-literal values for this to be a true
298
+ // pre-enum. Mixed types or method calls are a sign of a config class.
299
+ let allStrings = true;
300
+ for (const c of consts) {
301
+ if (!c.source) {
302
+ allStrings = false;
303
+ break;
304
+ }
305
+ // const_element source is just `NAME = 'value'` (no `const` keyword,
306
+ // no trailing `;` — those belong to the enclosing const_declaration).
307
+ // Match a string literal anywhere after the `=`.
308
+ if (!/=\s*['"][^'"]*['"]/.test(c.source)) {
309
+ allStrings = false;
310
+ break;
311
+ }
312
+ }
313
+ if (!allStrings)
314
+ continue;
315
+ // At least one helper method name must be present (or class name ends
316
+ // in "Enum" / "Status" / "Type" — common pre-enum suffixes).
317
+ const helperPresent = index.symbols.some((s) => s.parent === classId &&
318
+ s.kind === "method" &&
319
+ ENUM_HELPER_NAMES.has(s.name));
320
+ const naming = /(?:Enum|Status|Type|Kind|Code)$/.test(cls.name);
321
+ if (!helperPresent && !naming)
322
+ continue;
323
+ out.push({
324
+ rule_id: "enum-from-class-consts",
325
+ file: cls.file,
326
+ line: cls.start_line,
327
+ snippet: `class ${cls.name} { ${consts.length} consts }`,
328
+ description: RULE_DESCRIPTIONS["enum-from-class-consts"],
329
+ suggested_replacement: `enum ${cls.name}: string { case Foo = 'foo'; /* … convert each const */ }`,
330
+ confidence: helperPresent ? "high" : "medium",
331
+ });
332
+ }
333
+ }
334
+ /**
335
+ * promotable-ctor: __construct(T $x, U $y) { $this->x = $x; $this->y = $y; }
336
+ * is mechanically promotable to __construct(public T $x, public U $y) {}.
337
+ *
338
+ * Heuristic: extract the constructor body, count assignment statements,
339
+ * count formal parameters. If every parameter $X has exactly one matching
340
+ * `$this->X = $X;` in the body and there are no other statements (modulo
341
+ * whitespace), the constructor is promotable.
342
+ */
343
+ function findPromotableCtor(index, out) {
344
+ for (const sym of index.symbols) {
345
+ if (sym.kind !== "method" || sym.name !== "__construct")
346
+ continue;
347
+ if (!sym.source)
348
+ continue;
349
+ // Skip if already using promoted ctor (visibility on parameters).
350
+ if (/\b(?:public|private|protected)\s+(?:readonly\s+)?[\w\\?|&]+\s+\$\w+/.test(sym.source)) {
351
+ // This is a heuristic — promoted params have visibility BEFORE the
352
+ // type. Already-promoted ctors have nothing to modernize.
353
+ continue;
354
+ }
355
+ // Pull formal parameter names from the signature
356
+ const sigMatch = /__construct\s*\(([^)]*)\)/.exec(sym.source);
357
+ if (!sigMatch)
358
+ continue;
359
+ const sig = sigMatch[1];
360
+ if (!sig.trim())
361
+ continue;
362
+ const paramNames = [];
363
+ for (const part of sig.split(",")) {
364
+ const pm = /\$(\w+)/.exec(part);
365
+ if (pm)
366
+ paramNames.push(pm[1]);
367
+ }
368
+ if (paramNames.length === 0)
369
+ continue;
370
+ // Pull body { … }
371
+ const bodyStart = sym.source.indexOf("{");
372
+ const bodyEnd = sym.source.lastIndexOf("}");
373
+ if (bodyStart === -1 || bodyEnd <= bodyStart)
374
+ continue;
375
+ const body = sym.source.slice(bodyStart + 1, bodyEnd).trim();
376
+ // Count $this->NAME = $NAME; statements with names matching params.
377
+ let matched = 0;
378
+ let extras = 0;
379
+ const statements = body
380
+ .split(/;\s*(?:\n|$)/)
381
+ .map((s) => s.trim())
382
+ .filter(Boolean);
383
+ for (const stmt of statements) {
384
+ const am = /^\$this->(\w+)\s*=\s*\$(\w+)$/.exec(stmt);
385
+ if (am && am[1] === am[2] && paramNames.includes(am[1])) {
386
+ matched++;
387
+ }
388
+ else if (stmt && !/^\/\/|^#/.test(stmt)) {
389
+ extras++;
390
+ }
391
+ }
392
+ if (matched !== paramNames.length || extras > 0)
393
+ continue;
394
+ out.push({
395
+ rule_id: "promotable-ctor",
396
+ file: sym.file,
397
+ line: sym.start_line,
398
+ snippet: sym.source.slice(0, Math.min(160, sym.source.length)).replace(/\s+/g, " "),
399
+ description: RULE_DESCRIPTIONS["promotable-ctor"],
400
+ suggested_replacement: `public function __construct(${paramNames
401
+ .map((n) => `public \$${n}`)
402
+ .join(", ")}) {}`,
403
+ confidence: "high",
404
+ });
405
+ }
406
+ }
407
+ /**
408
+ * match-from-switch: every case returns a value, no fall-through. Pattern
409
+ * is regex-based — over-approximates by including switches that already
410
+ * have break statements. The auditor reviews each finding.
411
+ */
412
+ function findMatchFromSwitch(content, file, out) {
413
+ // Bounded regex: switch with at least 2 case-return blocks and no
414
+ // visible break / fall-through within a 2000-char window after the
415
+ // switch keyword.
416
+ const switchRe = /switch\s*\(\s*(\$\w+(?:->\w+)?|\w+\s*\([^)]*\))\s*\)\s*\{/g;
417
+ let m;
418
+ while ((m = switchRe.exec(content)) !== null) {
419
+ const body = content.slice(m.index, m.index + 2000);
420
+ // Each case must contain a return; no `break` between cases means
421
+ // every branch terminates the switch.
422
+ const caseReturns = (body.match(/case\s+[^:]+:\s*[^\n]*return/g) ?? []).length;
423
+ const breaks = (body.match(/\bbreak\s*;/g) ?? []).length;
424
+ if (caseReturns >= 2 && breaks === 0) {
425
+ out.push({
426
+ rule_id: "match-from-switch",
427
+ file,
428
+ line: countLines(content, m.index),
429
+ snippet: extractLine(content, m.index),
430
+ description: RULE_DESCRIPTIONS["match-from-switch"],
431
+ suggested_replacement: "return match($expr) { … };",
432
+ confidence: caseReturns >= 4 ? "high" : "medium",
433
+ });
434
+ }
435
+ }
436
+ }
437
+ // ---------------------------------------------------------------------------
438
+ // Helpers
439
+ // ---------------------------------------------------------------------------
440
+ function rateTypeConfidence(type) {
441
+ // Plain primitives = high confidence; class types = medium (might need
442
+ // alias resolution); union/intersection = low.
443
+ if (/^(?:string|int|integer|float|bool|boolean|array|mixed|void|object|iterable|callable)$/i.test(type)) {
444
+ return "high";
445
+ }
446
+ if (/[|&]/.test(type))
447
+ return "low";
448
+ return "medium";
449
+ }
450
+ function normalizeDocblockType(type) {
451
+ // Map common docblock spellings to PHP keyword forms.
452
+ const map = {
453
+ integer: "int",
454
+ boolean: "bool",
455
+ double: "float",
456
+ };
457
+ return map[type.toLowerCase()] ?? type;
458
+ }
459
+ function escapeRegex(s) {
460
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
461
+ }
462
+ function countLines(source, idx) {
463
+ let line = 1;
464
+ for (let i = 0; i < idx; i++) {
465
+ if (source.charCodeAt(i) === 10)
466
+ line++;
467
+ }
468
+ return line;
469
+ }
470
+ function extractLine(source, idx) {
471
+ const start = source.lastIndexOf("\n", idx) + 1;
472
+ const end = source.indexOf("\n", idx);
473
+ const line = source.slice(start, end === -1 ? source.length : end);
474
+ return line.trim().slice(0, 200);
475
+ }
476
+ //# sourceMappingURL=php8-migration-candidates-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"php8-migration-candidates-tools.js","sourceRoot":"","sources":["../../src/tools/php8-migration-candidates-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * PHPStan baseline analyzer (N6).
3
+ *
4
+ * Mature PHP codebases adopt PHPStan incrementally — they raise the level
5
+ * to e.g. 6, generate a baseline file containing every existing error,
6
+ * and treat the baseline as a debt ledger. The Yii2 panel in this gap
7
+ * analysis (tgm-panel) ships an 18 011-line phpstan-baseline.neon — that's
8
+ * roughly 1 800 ignored errors. The team knows the debt is there but has
9
+ * no per-path triage data.
10
+ *
11
+ * This tool parses phpstan-baseline.neon and surfaces:
12
+ * - by_path: files ranked by error count
13
+ * - by_category: error types (no-return-type, undefined-property,
14
+ * iterable-no-value-type, ...) ranked by frequency
15
+ * - quick_wins: files with 1-3 errors — easy refactor targets
16
+ * - aggregate counts (total_ignored, by-severity if hints present)
17
+ *
18
+ * The tool is universal — works on any PHP repo with PHPStan, not only
19
+ * Yii2. Auto-loads on composer.json detection.
20
+ *
21
+ * NEON parsing: NEON is a YAML superset with PHP-specific bracket forms.
22
+ * Rather than pull in a NEON parser dependency we hand-roll a minimal
23
+ * reader — the baseline format is mechanical (parameters: → ignoreErrors:
24
+ * → array of {message, count, path}). Edge cases like nested includes are
25
+ * out of scope; the tool would surface a file_unparseable note rather
26
+ * than crash.
27
+ */
28
+ export interface PhpStanBaselineEntry {
29
+ message: string;
30
+ count: number;
31
+ path: string;
32
+ /** Best-effort category derived from the message text. */
33
+ category: string;
34
+ }
35
+ export interface PhpStanBaselineAudit {
36
+ repo: string;
37
+ baseline_file: string | null;
38
+ /** Total ignored errors (sum of count fields). */
39
+ total_ignored: number;
40
+ /** Files containing at least one ignored error. */
41
+ total_files: number;
42
+ by_path: Array<{
43
+ path: string;
44
+ count: number;
45
+ }>;
46
+ by_category: Record<string, number>;
47
+ /** Files with 1-3 ignored errors — quickest to clear. */
48
+ quick_wins: Array<{
49
+ path: string;
50
+ count: number;
51
+ categories: string[];
52
+ }>;
53
+ /** Raw entries (full list, sorted by count descending). */
54
+ entries: PhpStanBaselineEntry[];
55
+ /** Diagnostic when the baseline couldn't be parsed cleanly. */
56
+ parse_warnings: string[];
57
+ }
58
+ export declare function analyzePhpStanBaseline(repo: string, options?: {
59
+ baseline_path?: string;
60
+ max_paths?: number;
61
+ }): Promise<PhpStanBaselineAudit>;
62
+ //# sourceMappingURL=phpstan-baseline-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phpstan-baseline-tools.d.ts","sourceRoot":"","sources":["../../src/tools/phpstan-baseline-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAUH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kDAAkD;IAClD,aAAa,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,yDAAyD;IACzD,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACzE,2DAA2D;IAC3D,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,+DAA+D;IAC/D,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAQD,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD,OAAO,CAAC,oBAAoB,CAAC,CAuF/B"}