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,543 @@
1
+ /**
2
+ * Yii2 migrations audit (N2).
3
+ *
4
+ * Yii2 ships its own PHP-DSL migration system — `extends Migration` with
5
+ * `$this->createTable()`, `$this->addColumn()`, `$this->dropTable()`, etc.
6
+ * The generic SQL toolchain (migration_lint, analyze_schema, sql_audit) is
7
+ * auto-loaded for composer.json projects but parses only `.sql` files, so
8
+ * the 379 migrations in tgm-panel are invisible to it.
9
+ *
10
+ * This tool fills that gap. For each migration class we:
11
+ * 1. Map the DSL calls to a structured operation list
12
+ * (create_table, drop_table, add_column, ...).
13
+ * 2. Run audit checks per migration:
14
+ * - missing_safe_down (irreversible)
15
+ * - drop_without_safety (drop in safeUp without index/backup hint)
16
+ * - alter_without_online_ddl (alter on large tables without
17
+ * ALGORITHM=INPLACE, LOCK=NONE for MySQL 8)
18
+ * - fk_without_index (addForeignKey on a column without
19
+ * a corresponding createIndex earlier)
20
+ * 3. Surface a per-table operation index (which migrations touch table X).
21
+ *
22
+ * The DSL parser is deliberately regex-based, not tree-sitter. Migration
23
+ * methods follow a small, stable grammar (a few verbs from yii\\db\\Migration
24
+ * with predictable argument shapes) and the AST coverage we'd buy with a
25
+ * full walker isn't worth the cost. The audit is a discovery tool, not a
26
+ * gate — false positives are acceptable, false negatives are the failure
27
+ * mode we minimize.
28
+ */
29
+ import { readFile } from "node:fs/promises";
30
+ import { join } from "node:path";
31
+ import { getCodeIndex } from "./index-tools.js";
32
+ const OP_MATCHERS = [
33
+ {
34
+ // $this->createTable('users', [...]) — plain string OR ::tableName() call.
35
+ re: /\$this->createTable\s*\(\s*(?:['"]([^'"]+)['"]|([\w\\]+)::tableName\s*\(\s*\))/g,
36
+ build: (m, line) => ({
37
+ kind: "create_table",
38
+ table: m[1] ?? m[2] ?? null,
39
+ raw: "createTable",
40
+ line,
41
+ }),
42
+ },
43
+ {
44
+ re: /\$this->dropTable\s*\(\s*(?:['"]([^'"]+)['"]|([\w\\]+)::tableName\s*\(\s*\))/g,
45
+ build: (m, line) => ({
46
+ kind: "drop_table",
47
+ table: m[1] ?? m[2] ?? null,
48
+ raw: "dropTable",
49
+ line,
50
+ }),
51
+ },
52
+ {
53
+ re: /\$this->renameTable\s*\(\s*['"]([^'"]+)['"]/g,
54
+ build: (m, line) => ({
55
+ kind: "rename_table",
56
+ table: m[1],
57
+ raw: "renameTable",
58
+ line,
59
+ }),
60
+ },
61
+ {
62
+ re: /\$this->addColumn\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g,
63
+ build: (m, line) => ({
64
+ kind: "add_column",
65
+ table: m[1],
66
+ column: m[2],
67
+ raw: "addColumn",
68
+ line,
69
+ }),
70
+ },
71
+ {
72
+ re: /\$this->dropColumn\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g,
73
+ build: (m, line) => ({
74
+ kind: "drop_column",
75
+ table: m[1],
76
+ column: m[2],
77
+ raw: "dropColumn",
78
+ line,
79
+ }),
80
+ },
81
+ {
82
+ re: /\$this->alterColumn\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g,
83
+ build: (m, line) => ({
84
+ kind: "alter_column",
85
+ table: m[1],
86
+ column: m[2],
87
+ raw: "alterColumn",
88
+ line,
89
+ }),
90
+ },
91
+ {
92
+ re: /\$this->renameColumn\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g,
93
+ build: (m, line) => ({
94
+ kind: "rename_column",
95
+ table: m[1],
96
+ column: m[2],
97
+ raw: "renameColumn",
98
+ line,
99
+ }),
100
+ },
101
+ {
102
+ re: /\$this->createIndex\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g,
103
+ build: (m, line) => ({
104
+ kind: "create_index",
105
+ table: m[2],
106
+ index_name: m[1],
107
+ raw: "createIndex",
108
+ line,
109
+ }),
110
+ },
111
+ {
112
+ re: /\$this->dropIndex\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g,
113
+ build: (m, line) => ({
114
+ kind: "drop_index",
115
+ table: m[2],
116
+ index_name: m[1],
117
+ raw: "dropIndex",
118
+ line,
119
+ }),
120
+ },
121
+ {
122
+ re: /\$this->addPrimaryKey\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g,
123
+ build: (m, line) => ({
124
+ kind: "add_primary_key",
125
+ table: m[2],
126
+ index_name: m[1],
127
+ raw: "addPrimaryKey",
128
+ line,
129
+ }),
130
+ },
131
+ {
132
+ re: /\$this->dropPrimaryKey\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g,
133
+ build: (m, line) => ({
134
+ kind: "drop_primary_key",
135
+ table: m[2],
136
+ index_name: m[1],
137
+ raw: "dropPrimaryKey",
138
+ line,
139
+ }),
140
+ },
141
+ {
142
+ // addForeignKey('fk_name', 'table', 'column', 'refTable', 'refColumn', ...)
143
+ re: /\$this->addForeignKey\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g,
144
+ build: (m, line) => ({
145
+ kind: "add_foreign_key",
146
+ fk_name: m[1],
147
+ table: m[2],
148
+ column: m[3],
149
+ raw: "addForeignKey",
150
+ line,
151
+ }),
152
+ },
153
+ {
154
+ re: /\$this->dropForeignKey\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g,
155
+ build: (m, line) => ({
156
+ kind: "drop_foreign_key",
157
+ fk_name: m[1],
158
+ table: m[2],
159
+ raw: "dropForeignKey",
160
+ line,
161
+ }),
162
+ },
163
+ {
164
+ re: /\$this->execute\s*\(\s*['"`]/g,
165
+ build: (_m, line) => ({
166
+ kind: "execute_raw_sql",
167
+ table: null,
168
+ raw: "execute",
169
+ line,
170
+ }),
171
+ },
172
+ {
173
+ re: /\$this->insert\s*\(\s*['"]([^'"]+)['"]/g,
174
+ build: (m, line) => ({
175
+ kind: "insert",
176
+ table: m[1],
177
+ raw: "insert",
178
+ line,
179
+ }),
180
+ },
181
+ {
182
+ re: /\$this->batchInsert\s*\(\s*['"]([^'"]+)['"]/g,
183
+ build: (m, line) => ({
184
+ kind: "batch_insert",
185
+ table: m[1],
186
+ raw: "batchInsert",
187
+ line,
188
+ }),
189
+ },
190
+ {
191
+ re: /\$this->update\s*\(\s*['"]([^'"]+)['"]/g,
192
+ build: (m, line) => ({
193
+ kind: "update",
194
+ table: m[1],
195
+ raw: "update",
196
+ line,
197
+ }),
198
+ },
199
+ {
200
+ re: /\$this->delete\s*\(\s*['"]([^'"]+)['"]/g,
201
+ build: (m, line) => ({
202
+ kind: "delete",
203
+ table: m[1],
204
+ raw: "delete",
205
+ line,
206
+ }),
207
+ },
208
+ ];
209
+ // ---------------------------------------------------------------------------
210
+ // Implementation
211
+ // ---------------------------------------------------------------------------
212
+ const MIGRATION_BASE_NAMES = new Set(["Migration"]);
213
+ const MIGRATION_FILENAME_RE = /(?:^|\/)m\d+_\d+_[\w-]+\.php$/;
214
+ function isMigrationHierarchy(cls, index, visited = new Set(), depth = 0) {
215
+ if (depth > 5)
216
+ return false;
217
+ if (visited.has(cls.name))
218
+ return false;
219
+ visited.add(cls.name);
220
+ const exts = cls.extends ?? [];
221
+ for (const baseFqcn of exts) {
222
+ const last = baseFqcn.split(/[\\\\]+/).pop() ?? baseFqcn;
223
+ if (MIGRATION_BASE_NAMES.has(last))
224
+ return true;
225
+ const baseSym = index.symbols.find((s) => s.kind === "class" && s.name === last);
226
+ if (baseSym && isMigrationHierarchy(baseSym, index, visited, depth + 1)) {
227
+ return true;
228
+ }
229
+ }
230
+ if (!cls.extends && cls.source) {
231
+ return /extends\s+(?:\\?yii\\db\\Migration|Migration)\b/.test(cls.source);
232
+ }
233
+ return false;
234
+ }
235
+ export async function analyzeYiiMigrations(repo, options) {
236
+ const index = await getCodeIndex(repo);
237
+ if (!index)
238
+ throw new Error(`Repository "${repo}" not found.`);
239
+ const ruleFilter = options?.rules ? new Set(options.rules) : null;
240
+ const filePattern = options?.file_pattern;
241
+ // Find migration class symbols. We require BOTH a Yii2 migration
242
+ // filename pattern AND a Migration ancestor — file name alone is a weak
243
+ // signal (some app code sits in folders named `migrations/`), and class
244
+ // alone misses tgm-panel's `extends Model` data-fix scripts which we
245
+ // intentionally exclude.
246
+ const migrationClasses = index.symbols.filter((s) => {
247
+ if (s.kind !== "class")
248
+ return false;
249
+ if (!s.file.endsWith(".php"))
250
+ return false;
251
+ if (!MIGRATION_FILENAME_RE.test(s.file))
252
+ return false;
253
+ if (filePattern && !s.file.includes(filePattern))
254
+ return false;
255
+ if (!isMigrationHierarchy(s, index))
256
+ return false;
257
+ return true;
258
+ });
259
+ const migrations = [];
260
+ const byTable = new Map();
261
+ for (const cls of migrationClasses) {
262
+ let content;
263
+ try {
264
+ content = await readFile(join(index.root, cls.file), "utf-8");
265
+ }
266
+ catch {
267
+ continue;
268
+ }
269
+ // Locate up/down method bodies. We approximate by finding the method
270
+ // header and grabbing the bytes between the next `{` and a balanced `}`.
271
+ // Yii2 method names: bare `up`/`down` (all lowercase) OR `safeUp`/`safeDown`
272
+ // (camelCase). Regex must accept both cases — the first version used
273
+ // `(?:safe)?Up` which only matched the camelCase form.
274
+ const upInfo = findMethodBody(content, /(?:safeUp|up)/);
275
+ const downInfo = findMethodBody(content, /(?:safeDown|down)/);
276
+ const isSafeTransactional = /\bfunction\s+safe(?:Up|Down)\s*\(/.test(content);
277
+ const upOps = upInfo ? extractOps(upInfo.body, upInfo.lineOffset) : [];
278
+ const downOps = downInfo ? extractOps(downInfo.body, downInfo.lineOffset) : [];
279
+ const tables = new Set();
280
+ for (const op of [...upOps, ...downOps]) {
281
+ if (op.table)
282
+ tables.add(op.table);
283
+ }
284
+ for (const t of tables) {
285
+ if (!byTable.has(t))
286
+ byTable.set(t, new Set());
287
+ byTable.get(t).add(cls.file);
288
+ }
289
+ const findings = auditMigration({
290
+ file: cls.file,
291
+ content,
292
+ upOps,
293
+ downOps,
294
+ hasSafeDown: !!downInfo,
295
+ ruleFilter,
296
+ });
297
+ migrations.push({
298
+ file: cls.file,
299
+ class_name: cls.name,
300
+ up_ops: upOps,
301
+ down_ops: downOps,
302
+ is_safe_transactional: isSafeTransactional,
303
+ tables: [...tables].sort(),
304
+ findings,
305
+ });
306
+ }
307
+ // Stable order: chronological by filename (timestamps sort lex-correctly
308
+ // in Yii2 since `m\d{6}_\d{6}_` always pads zeros).
309
+ migrations.sort((a, b) => a.file.localeCompare(b.file));
310
+ // Aggregate counts.
311
+ const findingsSummary = {};
312
+ for (const m of migrations) {
313
+ for (const f of m.findings) {
314
+ findingsSummary[f.rule_id] = (findingsSummary[f.rule_id] ?? 0) + 1;
315
+ }
316
+ }
317
+ const byTableObj = {};
318
+ for (const [t, files] of byTable.entries()) {
319
+ byTableObj[t] = [...files].sort();
320
+ }
321
+ return {
322
+ repo,
323
+ scanned_files: migrationClasses.length,
324
+ total_migrations: migrations.length,
325
+ migrations,
326
+ by_table: byTableObj,
327
+ findings_summary: findingsSummary,
328
+ };
329
+ }
330
+ // ---------------------------------------------------------------------------
331
+ // Helpers
332
+ // ---------------------------------------------------------------------------
333
+ /**
334
+ * Find a method body within a class source by name pattern. Returns the
335
+ * body text (between the `{` after the method header and its matching
336
+ * `}`) plus the 1-based line of the body's opening brace, so per-op line
337
+ * numbers can be computed against the original file.
338
+ *
339
+ * Brace balancing uses a simple counter that ignores braces inside
340
+ * single/double-quoted strings, comments, and heredocs (the last is a
341
+ * best-effort skip — heredocs are rare in migrations).
342
+ */
343
+ function findMethodBody(source, nameRe) {
344
+ // Build a bounded regex for "function NAME(...)"
345
+ const headerRe = new RegExp(`\\bfunction\\s+(${nameRe.source})\\s*\\(`, "g");
346
+ const headerMatch = headerRe.exec(source);
347
+ if (!headerMatch)
348
+ return null;
349
+ // Walk to the next `{` after the header.
350
+ const braceStart = source.indexOf("{", headerRe.lastIndex);
351
+ if (braceStart === -1)
352
+ return null;
353
+ // Balanced match.
354
+ let depth = 1;
355
+ let i = braceStart + 1;
356
+ while (i < source.length && depth > 0) {
357
+ const c = source[i];
358
+ if (c === "/" && source[i + 1] === "/") {
359
+ const nl = source.indexOf("\n", i);
360
+ i = nl === -1 ? source.length : nl + 1;
361
+ continue;
362
+ }
363
+ if (c === "/" && source[i + 1] === "*") {
364
+ const end = source.indexOf("*/", i + 2);
365
+ i = end === -1 ? source.length : end + 2;
366
+ continue;
367
+ }
368
+ if (c === '"' || c === "'") {
369
+ const quote = c;
370
+ i++;
371
+ while (i < source.length) {
372
+ if (source[i] === "\\") {
373
+ i += 2;
374
+ continue;
375
+ }
376
+ if (source[i] === quote) {
377
+ i++;
378
+ break;
379
+ }
380
+ i++;
381
+ }
382
+ continue;
383
+ }
384
+ if (c === "{")
385
+ depth++;
386
+ else if (c === "}")
387
+ depth--;
388
+ i++;
389
+ }
390
+ const body = source.slice(braceStart + 1, i - 1);
391
+ // 1-based line of the body's opening brace
392
+ const lineOffset = source.slice(0, braceStart).split("\n").length;
393
+ return { body, lineOffset };
394
+ }
395
+ /**
396
+ * Strip PHP comments (// line, # line, /* … *\/ block) from a source string,
397
+ * preserving line breaks so byte-for-byte line numbers stay aligned with the
398
+ * original. Used by alter-without-online-ddl detection so that developer
399
+ * notes mentioning the hint don't silence the finding.
400
+ *
401
+ * Doesn't try to handle string literals — comments inside strings are rare
402
+ * in migrations and the false-negative impact is minimal.
403
+ */
404
+ function stripPhpComments(source) {
405
+ let out = "";
406
+ let i = 0;
407
+ while (i < source.length) {
408
+ const c = source[i];
409
+ if (c === "/" && source[i + 1] === "/") {
410
+ const nl = source.indexOf("\n", i);
411
+ if (nl === -1) {
412
+ i = source.length;
413
+ }
414
+ else {
415
+ i = nl;
416
+ }
417
+ continue;
418
+ }
419
+ if (c === "/" && source[i + 1] === "*") {
420
+ const end = source.indexOf("*/", i + 2);
421
+ if (end === -1) {
422
+ i = source.length;
423
+ }
424
+ else {
425
+ // Preserve newlines inside the block so subsequent line numbers
426
+ // stay correct against the original source.
427
+ const block = source.slice(i, end + 2);
428
+ for (const ch of block)
429
+ if (ch === "\n")
430
+ out += "\n";
431
+ i = end + 2;
432
+ }
433
+ continue;
434
+ }
435
+ if (c === "#") {
436
+ const nl = source.indexOf("\n", i);
437
+ if (nl === -1) {
438
+ i = source.length;
439
+ }
440
+ else {
441
+ i = nl;
442
+ }
443
+ continue;
444
+ }
445
+ out += c;
446
+ i++;
447
+ }
448
+ return out;
449
+ }
450
+ function extractOps(body, baseLine) {
451
+ const ops = [];
452
+ for (const matcher of OP_MATCHERS) {
453
+ matcher.re.lastIndex = 0;
454
+ let m;
455
+ while ((m = matcher.re.exec(body)) !== null) {
456
+ const lineInBody = body.slice(0, m.index).split("\n").length - 1;
457
+ ops.push(matcher.build(m, baseLine + lineInBody));
458
+ }
459
+ }
460
+ // Sort by line so callers see operations in source order.
461
+ ops.sort((a, b) => a.line - b.line);
462
+ return ops;
463
+ }
464
+ function auditMigration(ctx) {
465
+ const out = [];
466
+ const include = (id) => !ctx.ruleFilter || ctx.ruleFilter.has(id);
467
+ // missing-safe-down: any up-side op exists but down is empty
468
+ if (include("missing-safe-down") && ctx.upOps.length > 0 && ctx.downOps.length === 0) {
469
+ out.push({
470
+ rule_id: "missing-safe-down",
471
+ severity: "medium",
472
+ description: "Migration has up()/safeUp() operations but no down()/safeDown() — irreversible deploys",
473
+ fix: "Implement safeDown() that reverses the up operations (or throw a clear exception explicitly).",
474
+ });
475
+ }
476
+ // alter-without-online-ddl: alter/drop column on tables without an
477
+ // accompanying ALGORITHM=INPLACE/LOCK=NONE hint anywhere in the migration.
478
+ // We require the user to have written a raw `execute()` with the hint OR
479
+ // a comment containing "ALGORITHM=INPLACE" — anything else flags. This
480
+ // is the highest-volume tgm-panel db-audit finding (DB6-001).
481
+ if (include("alter-without-online-ddl")) {
482
+ const destructive = [...ctx.upOps, ...ctx.downOps].filter((o) => o.kind === "alter_column" || o.kind === "drop_column" || o.kind === "drop_table");
483
+ if (destructive.length > 0) {
484
+ // Look for the hint only in code, not comments. A developer note like
485
+ // "TODO: add ALGORITHM=INPLACE" should NOT silence this finding —
486
+ // the hint must actually be in the migration's executed SQL.
487
+ const codeOnly = stripPhpComments(ctx.content);
488
+ const hasOnlineDdl = /ALGORITHM\s*=\s*INPLACE/i.test(codeOnly) ||
489
+ /LOCK\s*=\s*NONE/i.test(codeOnly) ||
490
+ /pt-online-schema-change|gh-ost/i.test(codeOnly);
491
+ if (!hasOnlineDdl) {
492
+ out.push({
493
+ rule_id: "alter-without-online-ddl",
494
+ severity: "high",
495
+ description: `Destructive operations (${destructive.map((d) => d.kind).join(", ")}) without explicit ALGORITHM=INPLACE/LOCK=NONE hint — risk of multi-minute lock on large tables under MySQL 8`,
496
+ fix: "Either invoke the destructive ops via raw SQL with ALGORITHM=INPLACE, LOCK=NONE, or document a pt-online-schema-change / gh-ost runbook for large tables.",
497
+ });
498
+ }
499
+ }
500
+ }
501
+ // fk-without-index: addForeignKey on (table, column) where no createIndex
502
+ // on the same (table, column) appears earlier in this migration (or
503
+ // already exists in a previous one — out of scope for a single-file scan).
504
+ if (include("fk-without-index")) {
505
+ const fks = ctx.upOps.filter((o) => o.kind === "add_foreign_key");
506
+ for (const fk of fks) {
507
+ if (!fk.table || !fk.column)
508
+ continue;
509
+ const idxBefore = ctx.upOps.find((o) => o.kind === "create_index" &&
510
+ o.table === fk.table &&
511
+ o.line < fk.line);
512
+ if (!idxBefore) {
513
+ out.push({
514
+ rule_id: "fk-without-index",
515
+ severity: "medium",
516
+ description: `addForeignKey on ${fk.table}.${fk.column} without createIndex on the same column earlier in this migration — slow FK lookups under load`,
517
+ fix: `Add $this->createIndex('idx_${fk.table}_${fk.column}', '${fk.table}', '${fk.column}') before the addForeignKey call.`,
518
+ });
519
+ }
520
+ }
521
+ }
522
+ // raw-sql-without-comment: raw $this->execute() without any preceding
523
+ // // comment — these need explanation for reviewers. Bounded to first
524
+ // execute() per migration to avoid noise.
525
+ if (include("raw-sql-without-comment")) {
526
+ const execOp = ctx.upOps.find((o) => o.kind === "execute_raw_sql");
527
+ if (execOp) {
528
+ // Heuristic: the line BEFORE the execute() call should be a comment.
529
+ const lines = ctx.content.split("\n");
530
+ const before = lines[execOp.line - 2] ?? "";
531
+ if (!/^\s*(\/\/|#|\/\*)/.test(before)) {
532
+ out.push({
533
+ rule_id: "raw-sql-without-comment",
534
+ severity: "low",
535
+ description: "Raw $this->execute() SQL with no preceding comment — review effort goes up sharply when raw SQL appears unexplained",
536
+ fix: "Add a // comment above the execute() line explaining why the DSL wasn't sufficient.",
537
+ });
538
+ }
539
+ }
540
+ }
541
+ return out;
542
+ }
543
+ //# sourceMappingURL=yii-migrations-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yii-migrations-tools.js","sourceRoot":"","sources":["../../src/tools/yii-migrations-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Yii2 module inventory (N1).
3
+ *
4
+ * Scans a Yii2 codebase for classes extending `yii\\base\\Module` and emits
5
+ * a structured per-module summary: controller namespace, controllers, views,
6
+ * migrations path, components, sub-modules. Also resolves URL prefixes by
7
+ * cross-referencing config/web.php urlManager rules + the application's
8
+ * `modules` registration.
9
+ *
10
+ * Why this is its own tool (vs. squeezing into php_project_audit):
11
+ * - Modules are the primary architectural unit of medium/large Yii2 apps
12
+ * (tgm-panel: 11 modules; Mobi 2: similar). Routing, RBAC, and god-model
13
+ * analysis all benefit from being able to scope to a module.
14
+ * - The output is a graph of cross-file references (controllers ↔ views ↔
15
+ * migrations ↔ config), not a flat findings list.
16
+ *
17
+ * Implementation depends on the v2.0.0 PHP extractor — uses `s.extends`
18
+ * (introduced in Sprint 1) to detect Module subclasses structurally.
19
+ * Falls back to a regex check on `s.source` when `extends` is absent so
20
+ * stale indexes don't go silent.
21
+ */
22
+ import { relative } from "node:path";
23
+ export interface YiiControllerRef {
24
+ class: string;
25
+ file: string;
26
+ /** Action methods discovered as children — names retain the `action` prefix
27
+ * so callers can map them back to URL action ids without ambiguity. */
28
+ actions: string[];
29
+ }
30
+ export interface YiiModuleSummary {
31
+ /** Module id derived from the directory name containing Module.php. */
32
+ id: string;
33
+ /** Fully-qualified class name (e.g. app\\modules\\review\\Module). */
34
+ class: string;
35
+ /** Path to the Module.php file. */
36
+ file: string;
37
+ /** Value of `controllerNamespace` property if declared, else null. */
38
+ controllerNamespace: string | null;
39
+ /** Path containing the module's controllers, derived from the Module.php
40
+ * location. */
41
+ controllers_path: string;
42
+ controllers: YiiControllerRef[];
43
+ /** Per-module views directory. */
44
+ views_path: string;
45
+ views_count: number;
46
+ /** Module's own migrations directory if present (e.g. modules/<id>/migrations/). */
47
+ migrations_path: string | null;
48
+ migrations_count: number;
49
+ /** Sub-module ids (modules nested under this module's modules() method). */
50
+ submodules: string[];
51
+ /** URL prefixes from main app config that route into this module. */
52
+ url_prefixes: string[];
53
+ }
54
+ export interface YiiModulesAudit {
55
+ repo: string;
56
+ total_modules: number;
57
+ modules: YiiModuleSummary[];
58
+ }
59
+ export declare function analyzeYiiModules(repo: string, options?: {
60
+ module_id?: string;
61
+ }): Promise<YiiModulesAudit>;
62
+ export { relative as _relativePathForTests };
63
+ //# sourceMappingURL=yii-modules-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yii-modules-tools.d.ts","sourceRoot":"","sources":["../../src/tools/yii-modules-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAiB,QAAQ,EAAE,MAAM,WAAW,CAAC;AAOpD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb;4EACwE;IACxE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,EAAE,EAAE,MAAM,CAAC;IACX,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC;oBACgB;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,oFAAoF;IACpF,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,4EAA4E;IAC5E,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,qEAAqE;IACrE,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B;AAyCD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,OAAO,CAAC,eAAe,CAAC,CAuG1B;AA0ED,OAAO,EAAE,QAAQ,IAAI,qBAAqB,EAAE,CAAC"}