phpstan-mcp-mx 0.4.0 → 0.6.0

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/README.md CHANGED
@@ -286,6 +286,7 @@ antwoord volloopt met wat er al jaren stond.
286
286
  | --- | --- | --- |
287
287
  | `project` | ja | absoluut pad naar de projectroot |
288
288
  | `base` | nee | commit, branch of tag om tegen te vergelijken (standaard `HEAD`) |
289
+ | `staged` | nee | alleen wat gestaged is (`git diff --cached`), voor een check vóór een commit |
289
290
 
290
291
  Verder dezelfde argumenten als `analyse_project`, op `paths` na. Met `base=main` krijg je de
291
292
  hele branch in plaats van alleen je onopgeslagen werk.
@@ -302,6 +303,34 @@ als "overgeslagen" bij het antwoord te staan in plaats van er stil uit te vallen
302
303
  Staat het project niet onder git, dan zegt de tool dat en verwijst hij naar `analyse_project`
303
304
  met expliciete `paths`.
304
305
 
306
+ ### `diagnose_project`
307
+
308
+ Vraagt PHPStan zelf om zijn opstelling: PHP- en PHPStan-versie, de configs die
309
+ Composer-pakketten meebrengen (zoals Larastan) en of de turbo-extensie aanstaat. Draait geen
310
+ analyse.
311
+
312
+ Gebruik dit als een project zich anders gedraagt dan een ander project, vóórdat je aan de
313
+ code gaat twijfelen. Dat is niet theoretisch: hiermee bleek dat één project een dependency
314
+ had (`laravel/pao`) die de uitvoer van PHPStan onderschepte.
315
+
316
+ ### `clear_result_cache`
317
+
318
+ Leegt PHPStans resultcache, zodat de volgende analyse alles opnieuw doorrekent. Nuttig als
319
+ een uitslag niet klopt met de code — bijvoorbeeld na een wijziging aan een extensie of een
320
+ geïncludeerde config.
321
+
322
+ Die cache ligt standaard **niet** in het project maar in de tijdelijke map van het systeem
323
+ (`sys_get_temp_dir()/phpstan`), en wordt dan gedeeld met elk ander project dat geen eigen
324
+ `tmpDir` zet. Bovendien volgt die map `TMPDIR`, dus wat de server leegt hoeft niet dezelfde
325
+ map te zijn als wat je terminal gebruikt. Het antwoord noemt daarom de map die geleegd is:
326
+
327
+ ```
328
+ Result cache cleared from directory:
329
+ /var/tmp/phpstan
330
+ ```
331
+
332
+ Wil je per project een eigen cache, zet dan `tmpDir` in je `phpstan.neon`.
333
+
305
334
  ### `baseline_summary`
306
335
 
307
336
  Leest de `phpstan-baseline*.neon` van een project en telt wat er genegeerd wordt, per
@@ -311,6 +340,45 @@ Let op het onderscheid: wat hier in staat is precies wat `analyse_project` *niet
311
340
  project dat zijn baseline includet, rapporteert alleen de fouten die er nieuw bij komen —
312
341
  die vergelijking maakt PHPStan zelf al.
313
342
 
343
+ ### `generate_baseline` (alleen met schrijfrechten)
344
+
345
+ Zet alle huidige fouten in een baseline, zodat een volgende analyse alleen meldt wat er nieuw
346
+ bij komt. Bedoeld om een bestaand project onder PHPStan te krijgen — niet om fouten weg te
347
+ werken.
348
+
349
+ Deze tool bestaat alleen als de server met `PHPSTAN_MCP_ALLOW_WRITES=1` gestart is:
350
+
351
+ ```bash
352
+ claude mcp add phpstan --scope user \
353
+ --env PHPSTAN_MCP_ROOTS=/pad/naar/projecten \
354
+ --env PHPSTAN_MCP_ALLOW_WRITES=1 \
355
+ -- phpstan-mcp-mx
356
+ ```
357
+
358
+ Zonder die variabele wordt hij niet geregistreerd — hij staat dan dus ook niet in de
359
+ toollijst, in plaats van dat hij bestaat en bij elke aanroep weigert. Alle andere tools
360
+ blijven read-only.
361
+
362
+ | Argument | Verplicht | Betekenis |
363
+ | --- | --- | --- |
364
+ | `project` | ja | absoluut pad naar de projectroot |
365
+ | `file` | nee | doelbestand binnen het project (standaard `phpstan-baseline.neon`) |
366
+ | `allowEmpty` | nee | ook schrijven als er niets te negeren valt |
367
+
368
+ Verder dezelfde `paths`, `level`, `config` en `memoryLimit` als een analyse. Het antwoord
369
+ zegt erbij wat er nog moet gebeuren, want een baseline die nergens ingesloten wordt doet
370
+ niets:
371
+
372
+ ```
373
+ 5 fout(en) in phpstan-baseline.neon gezet.
374
+
375
+ Deze fouten worden vanaf nu niet meer gemeld zodra phpstan-baseline.neon in de
376
+ configuratie staat:
377
+
378
+ includes:
379
+ - phpstan-baseline.neon
380
+ ```
381
+
314
382
  ## Wat je terugkrijgt
315
383
 
316
384
  Een analyse levert eerst tellingen over álle bevindingen en daarna één pagina met de
@@ -354,6 +422,18 @@ Dat onderscheid is wezenlijk: zonder die regel is een lege uitslag niet te onder
354
422
  een schone. Staat er een waarschuwing, dan leest het antwoord ook niet meer "Geen
355
423
  bevindingen" maar "Verder geen bevindingen".
356
424
 
425
+ Doet er een baseline mee, dan staat dat er ook bij:
426
+
427
+ ```
428
+ Ter info:
429
+ - phpstan-baseline.neon wordt geïncludeerd: 1037 genegeerde fout(en) staan niet in dit
430
+ rapport. Gebruik baseline_summary om te zien wat er in staat.
431
+ ```
432
+
433
+ Dat is het verschil tussen "geen bevindingen" en "geen bevindingen, plus duizend die niet
434
+ gemeld worden". De check kijkt of je config het bestand echt includeert — een baseline die
435
+ naast `phpstan.neon` ligt zonder erin te staan, doet niets.
436
+
357
437
  Het `warnings`-veld staat los van `generalErrors`: dat tweede is PHPStans eigen
358
438
  `errors`-array uit de JSON. Beide lijsten zijn begrensd op tien meldingen van elk hoogstens
359
439
  1000 tekens — een mislukte Laravel-bootstrap levert anders een lap tekst op die het hele
@@ -37,6 +37,19 @@ export declare class BaselineError extends Error {
37
37
  * van pakketten in vendor/ oppikken.
38
38
  */
39
39
  export declare function findBaselineFiles(project: ResolvedProject): string[];
40
+ /**
41
+ * Meldt of de configuratie van dit project een baseline includeert, en hoeveel
42
+ * fouten die dan verbergt. Null als er geen baseline meedoet.
43
+ *
44
+ * Dit is nodig omdat een schone uitslag dan geen schone code betekent: PHPStan past
45
+ * de baseline zelf toe en rapporteert alleen wat er nieuw bij komt. Dat staat in de
46
+ * toolbeschrijving, maar niet in het antwoord waar het model naar kijkt.
47
+ *
48
+ * De include-check is regelgericht, net als de parser zelf: een lijst-entry die naar
49
+ * een baseline-bestand verwijst. Op de hele bestandsinhoud matchen zou ook aanslaan
50
+ * op een naam in een comment of onder een andere sleutel.
51
+ */
52
+ export declare function describeIncludedBaseline(project: ResolvedProject): string | null;
40
53
  /**
41
54
  * Leest een baseline-bestand uit.
42
55
  *
package/dist/baseline.js CHANGED
@@ -21,6 +21,53 @@ export function findBaselineFiles(project) {
21
21
  return [];
22
22
  }
23
23
  }
24
+ /**
25
+ * Meldt of de configuratie van dit project een baseline includeert, en hoeveel
26
+ * fouten die dan verbergt. Null als er geen baseline meedoet.
27
+ *
28
+ * Dit is nodig omdat een schone uitslag dan geen schone code betekent: PHPStan past
29
+ * de baseline zelf toe en rapporteert alleen wat er nieuw bij komt. Dat staat in de
30
+ * toolbeschrijving, maar niet in het antwoord waar het model naar kijkt.
31
+ *
32
+ * De include-check is regelgericht, net als de parser zelf: een lijst-entry die naar
33
+ * een baseline-bestand verwijst. Op de hele bestandsinhoud matchen zou ook aanslaan
34
+ * op een naam in een comment of onder een andere sleutel.
35
+ */
36
+ export function describeIncludedBaseline(project) {
37
+ if (project.config === null)
38
+ return null;
39
+ let config;
40
+ try {
41
+ config = readFileSync(join(project.path, project.config), 'utf8');
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ const included = findBaselineFiles(project).filter((file) => isIncludedIn(config, file));
47
+ if (included.length === 0)
48
+ return null;
49
+ const namen = included.join(', ');
50
+ let errors;
51
+ try {
52
+ const summary = summariseBaseline(project);
53
+ errors = summary.files
54
+ .filter((entry) => included.includes(entry.file))
55
+ .reduce((total, entry) => total + entry.errors, 0);
56
+ }
57
+ catch {
58
+ // Een onleesbare baseline mag geen analyse omleggen. Dat hij meedoet is ook
59
+ // zonder aantal het vermelden waard — juist dan, want dan is niet te zeggen
60
+ // hoeveel er verborgen wordt.
61
+ return `${namen} wordt geïncludeerd en is niet te lezen; wat daarin staat, meldt deze analyse niet.`;
62
+ }
63
+ return (`${namen} wordt geïncludeerd: ${String(errors)} genegeerde fout(en) staan niet in dit ` +
64
+ 'rapport. Gebruik baseline_summary om te zien wat er in staat.');
65
+ }
66
+ /** Of `file` in deze config als lijst-entry voorkomt, met of zonder ./ ervoor. */
67
+ function isIncludedIn(config, file) {
68
+ const escaped = file.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
69
+ return new RegExp(`^\\s*-\\s*['"]?(?:\\./)?${escaped}['"]?\\s*$`, 'm').test(config);
70
+ }
24
71
  /**
25
72
  * Leest een baseline-bestand uit.
26
73
  *
@@ -1 +1 @@
1
- {"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,8DAA8D;AAC9D,MAAM,YAAY,GAAG,EAAE,CAAC;AAoBxB,+EAA+E;AAC/E,MAAM,OAAO,aAAc,SAAQ,KAAK;CAAG;AAE3C;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAwB;IACxD,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;aACtD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;aAClF,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;aAC1B,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,IAAI,OAAO,GAAkE,IAAI,CAAC;IAElF,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO;QAC7B,yEAAyE;QACzE,kEAAkE;QAClE,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC;gBACX,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,qBAAqB;gBACvD,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;aAC1B,CAAC,CAAC;QACL,CAAC;QACD,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAE5B,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;YACpB,KAAK,EAAE,CAAC;YACR,OAAO,GAAG,EAAE,CAAC;YACb,SAAS;QACX,CAAC;QACD,IAAI,OAAO,KAAK,IAAI;YAAE,SAAS;QAE/B,MAAM,KAAK,GAAG,mCAAmC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChE,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QAE7B,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAEjC,IAAI,GAAG,KAAK,YAAY;YAAE,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;aACzD,IAAI,GAAG,KAAK,MAAM;YAAE,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;aAClD,IAAI,GAAG,KAAK,OAAO;YAAE,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IAC5E,CAAC;IAED,KAAK,EAAE,CAAC;IACR,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,iBAAiB,CAAC,OAAwB;IACxD,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,aAAa,CACrB,GAAG,OAAO,CAAC,IAAI,sEAAsE,CACtF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAwD,EAAE,CAAC;IACxE,MAAM,GAAG,GAAoB,EAAE,CAAC;IAEhC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QAEvC,6EAA6E;QAC7E,6EAA6E;QAC7E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YAC7D,MAAM,IAAI,aAAa,CACrB,GAAG,IAAI,iEAAiE;gBACtE,oDAAoD,CACvD,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,IAAI,CAAC;YACX,IAAI;YACJ,OAAO,EAAE,OAAO,CAAC,MAAM;YACvB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;SAC7D,CAAC,CAAC;QACH,GAAG,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;IACvB,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,IAAI;QACrB,KAAK,EAAE,OAAO;QACd,MAAM,EAAE;YACN,OAAO,EAAE,GAAG,CAAC,MAAM;YACnB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;SACzD;QACD,YAAY,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;YAClF,UAAU;YACV,KAAK;SACN,CAAC,CAAC;QACH,MAAM,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;KACpF,CAAC;AACJ,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,cAAc,CAAC,OAAwB;IACrD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACnC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,IAAI,CACR,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,2BAA2B,OAAO,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAC/F,CAAC;IAEF,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,KAAK,CAAC,IAAI,CACR,EAAE,EACF,kBAAkB,EAClB,GAAG,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,UAAU,EAAE,CAAC,EAC9E,EAAE,EACF,4BAA4B,EAC5B,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC,EAClE,EAAE,EACF,wFAAwF,EACxF,mFAAmF,CACpF,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,yFAAyF;AACzF,SAAS,KAAK,CAAC,OAAiC,EAAE,GAAqC;IACrF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;QACxB,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;SACzB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACvD,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,OAAO,CAAC,KAAa;IAC5B,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/D,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;AAC7B,CAAC;AAED,SAAS,GAAG,CAAC,KAAa;IACxB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC"}
1
+ {"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,8DAA8D;AAC9D,MAAM,YAAY,GAAG,EAAE,CAAC;AAoBxB,+EAA+E;AAC/E,MAAM,OAAO,aAAc,SAAQ,KAAK;CAAG;AAE3C;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAwB;IACxD,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;aACtD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;aAClF,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;aAC1B,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAwB;IAC/D,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAEzC,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IACzF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAElC,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,GAAG,OAAO,CAAC,KAAK;aACnB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;aAChD,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,4EAA4E;QAC5E,4EAA4E;QAC5E,8BAA8B;QAC9B,OAAO,GAAG,KAAK,qFAAqF,CAAC;IACvG,CAAC;IAED,OAAO,CACL,GAAG,KAAK,wBAAwB,MAAM,CAAC,MAAM,CAAC,yCAAyC;QACvF,+DAA+D,CAChE,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,SAAS,YAAY,CAAC,MAAc,EAAE,IAAY;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IAC5D,OAAO,IAAI,MAAM,CAAC,2BAA2B,OAAO,YAAY,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACtF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,IAAI,OAAO,GAAkE,IAAI,CAAC;IAElF,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO;QAC7B,yEAAyE;QACzE,kEAAkE;QAClE,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC;gBACX,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,qBAAqB;gBACvD,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;aAC1B,CAAC,CAAC;QACL,CAAC;QACD,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAE5B,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;YACpB,KAAK,EAAE,CAAC;YACR,OAAO,GAAG,EAAE,CAAC;YACb,SAAS;QACX,CAAC;QACD,IAAI,OAAO,KAAK,IAAI;YAAE,SAAS;QAE/B,MAAM,KAAK,GAAG,mCAAmC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChE,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QAE7B,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAEjC,IAAI,GAAG,KAAK,YAAY;YAAE,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;aACzD,IAAI,GAAG,KAAK,MAAM;YAAE,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;aAClD,IAAI,GAAG,KAAK,OAAO;YAAE,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IAC5E,CAAC;IAED,KAAK,EAAE,CAAC;IACR,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,iBAAiB,CAAC,OAAwB;IACxD,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,aAAa,CACrB,GAAG,OAAO,CAAC,IAAI,sEAAsE,CACtF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAwD,EAAE,CAAC;IACxE,MAAM,GAAG,GAAoB,EAAE,CAAC;IAEhC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QAEvC,6EAA6E;QAC7E,6EAA6E;QAC7E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YAC7D,MAAM,IAAI,aAAa,CACrB,GAAG,IAAI,iEAAiE;gBACtE,oDAAoD,CACvD,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,IAAI,CAAC;YACX,IAAI;YACJ,OAAO,EAAE,OAAO,CAAC,MAAM;YACvB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;SAC7D,CAAC,CAAC;QACH,GAAG,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;IACvB,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,IAAI;QACrB,KAAK,EAAE,OAAO;QACd,MAAM,EAAE;YACN,OAAO,EAAE,GAAG,CAAC,MAAM;YACnB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;SACzD;QACD,YAAY,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;YAClF,UAAU;YACV,KAAK;SACN,CAAC,CAAC;QACH,MAAM,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;KACpF,CAAC;AACJ,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,cAAc,CAAC,OAAwB;IACrD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACnC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,IAAI,CACR,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,2BAA2B,OAAO,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAC/F,CAAC;IAEF,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,KAAK,CAAC,IAAI,CACR,EAAE,EACF,kBAAkB,EAClB,GAAG,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,UAAU,EAAE,CAAC,EAC9E,EAAE,EACF,4BAA4B,EAC5B,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC,EAClE,EAAE,EACF,wFAAwF,EACxF,mFAAmF,CACpF,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,yFAAyF;AACzF,SAAS,KAAK,CAAC,OAAiC,EAAE,GAAqC;IACrF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;QACxB,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;SACzB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACvD,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,OAAO,CAAC,KAAa;IAC5B,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/D,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;AAC7B,CAAC;AAED,SAAS,GAAG,CAAC,KAAa;IACxB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC"}
package/dist/changed.d.ts CHANGED
@@ -7,6 +7,8 @@ export declare const DEFAULT_BASE = "HEAD";
7
7
  /** De gewijzigde PHP-bestanden van een project, met wat er niet bruikbaar was. */
8
8
  export interface ChangedFiles {
9
9
  readonly base: string;
10
+ /** Of er naar de index gekeken is in plaats van naar de werkboom. */
11
+ readonly staged: boolean;
10
12
  /** Absolute paden binnen het project, alfabetisch zodat de volgorde vastligt. */
11
13
  readonly files: readonly string[];
12
14
  /** Paden die git noemde maar die niet te gebruiken zijn, met de reden. */
@@ -22,4 +24,4 @@ export interface ChangedFiles {
22
24
  * veranderd is, en `ls-files --others` de nieuwe bestanden die nog nergens in staan.
23
25
  * Zonder die tweede mist juist het bestand dat net is aangemaakt.
24
26
  */
25
- export declare function changedPhpFiles(project: ResolvedProject, base?: string): Promise<ChangedFiles>;
27
+ export declare function changedPhpFiles(project: ResolvedProject, base?: string, staged?: boolean): Promise<ChangedFiles>;
package/dist/changed.js CHANGED
@@ -17,7 +17,7 @@ const GIT_TIMEOUT_MS = 15_000;
17
17
  * veranderd is, en `ls-files --others` de nieuwe bestanden die nog nergens in staan.
18
18
  * Zonder die tweede mist juist het bestand dat net is aangemaakt.
19
19
  */
20
- export async function changedPhpFiles(project, base = DEFAULT_BASE) {
20
+ export async function changedPhpFiles(project, base = DEFAULT_BASE, staged = false) {
21
21
  // --relative: zonder dat rapporteert git paden vanaf de repositoryroot, en die
22
22
  // hoeft niet de projectroot te zijn — een project kan in een monorepo liggen.
23
23
  //
@@ -32,10 +32,17 @@ export async function changedPhpFiles(project, base = DEFAULT_BASE) {
32
32
  '--name-only',
33
33
  '--relative',
34
34
  '--diff-filter=ACMR',
35
+ // --cached vergelijkt de index met `base` in plaats van de werkboom: dat is de
36
+ // vraag "wat sta ik op het punt te committen".
37
+ ...(staged ? ['--cached'] : []),
35
38
  '--end-of-options',
36
39
  checkBase(base),
37
40
  ]);
38
- const untracked = await git(project.path, ['ls-files', '--others', '--exclude-standard']);
41
+ // Niet-toegevoegde bestanden horen er alleen bij als je naar de werkboom kijkt.
42
+ // Bij --cached zijn ze per definitie niet gestaged, dus dan blijft die uitvraag weg.
43
+ const untracked = staged
44
+ ? ''
45
+ : await git(project.path, ['ls-files', '--others', '--exclude-standard']);
39
46
  const candidates = [...new Set([...lines(changed), ...lines(untracked)])]
40
47
  .filter(isAnalysablePhp)
41
48
  .sort();
@@ -58,7 +65,7 @@ export async function changedPhpFiles(project, base = DEFAULT_BASE) {
58
65
  });
59
66
  }
60
67
  }
61
- return { base, files, skipped };
68
+ return { base, staged, files, skipped };
62
69
  }
63
70
  /**
64
71
  * Blade-templates eindigen óók op `.php`, en juist die moeten er niet in: een
@@ -1 +1 @@
1
- {"version":3,"file":"changed.js","sourceRoot":"","sources":["../src/changed.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"changed.js","sourceRoot":"","sources":["../src/changed.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { loadDefaultMemoryLimit, loadMaxParallel } from './phpstan.js';
4
+ import { WRITES_ENV_VAR, writesAllowed } from './tools.js';
4
5
  import { loadRoots } from './roots.js';
5
6
  import { VERSION, createServer } from './server.js';
6
7
  async function main() {
@@ -11,7 +12,8 @@ async function main() {
11
12
  await server.connect(new StdioServerTransport());
12
13
  console.error(`phpstan-mcp ${VERSION} luistert op stdio, ${roots.length} root(s) geconfigureerd` +
13
14
  describeMemoryDefault() +
14
- describeMaxParallel());
15
+ describeMaxParallel() +
16
+ describeWrites());
15
17
  }
16
18
  /**
17
19
  * Zet de standaard geheugenlimiet in de opstartregel, zodat je in het clientlog ziet
@@ -19,6 +21,19 @@ async function main() {
19
21
  * kan er verder prima mee door, en analyse() legt het probleem alsnog uit aan wie
20
22
  * een analyse aanvraagt.
21
23
  */
24
+ /**
25
+ * Zegt of schrijvende tools meedoen. Ook bij "uit" iets melden zou ruis zijn, maar
26
+ * een waarde die níet herkend wordt moet je wel zien: die staat er met een bedoeling
27
+ * en doet niets.
28
+ */
29
+ function describeWrites() {
30
+ if (writesAllowed())
31
+ return ', schrijvende tools aan';
32
+ const configured = process.env[WRITES_ENV_VAR]?.trim();
33
+ return configured === undefined || configured === ''
34
+ ? ''
35
+ : `, LET OP: ${WRITES_ENV_VAR} staat op "${configured}"; alleen "1" en "true" zetten schrijven aan`;
36
+ }
22
37
  /** Zelfde afweging als bij de geheugenlimiet: melden, niet fataal. */
23
38
  function describeMaxParallel() {
24
39
  try {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEpD,KAAK,UAAU,IAAI;IACjB,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IAEnC,8EAA8E;IAC9E,kEAAkE;IAClE,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;IAEjD,OAAO,CAAC,KAAK,CACX,eAAe,OAAO,uBAAuB,KAAK,CAAC,MAAM,yBAAyB;QAChF,qBAAqB,EAAE;QACvB,mBAAmB,EAAE,CACxB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,sEAAsE;AACtE,SAAS,mBAAmB;IAC1B,IAAI,CAAC;QACH,OAAO,eAAe,MAAM,CAAC,eAAe,EAAE,CAAC,WAAW,CAAC;IAC7D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,aAAa,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAC/E,CAAC;AACH,CAAC;AAED,SAAS,qBAAqB;IAC5B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,sBAAsB,EAAE,CAAC;QACvC,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,oBAAoB,KAAK,EAAE,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,aAAa,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAC/E,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;IACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEpD,KAAK,UAAU,IAAI;IACjB,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IAEnC,8EAA8E;IAC9E,kEAAkE;IAClE,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;IAEjD,OAAO,CAAC,KAAK,CACX,eAAe,OAAO,uBAAuB,KAAK,CAAC,MAAM,yBAAyB;QAChF,qBAAqB,EAAE;QACvB,mBAAmB,EAAE;QACrB,cAAc,EAAE,CACnB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH;;;;GAIG;AACH,SAAS,cAAc;IACrB,IAAI,aAAa,EAAE;QAAE,OAAO,yBAAyB,CAAC;IAEtD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,CAAC;IACvD,OAAO,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,EAAE;QAClD,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,aAAa,cAAc,cAAc,UAAU,8CAA8C,CAAC;AACxG,CAAC;AAED,sEAAsE;AACtE,SAAS,mBAAmB;IAC1B,IAAI,CAAC;QACH,OAAO,eAAe,MAAM,CAAC,eAAe,EAAE,CAAC,WAAW,CAAC;IAC7D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,aAAa,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAC/E,CAAC;AACH,CAAC;AAED,SAAS,qBAAqB;IAC5B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,sBAAsB,EAAE,CAAC;QACvC,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,oBAAoB,KAAK,EAAE,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,aAAa,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAC/E,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;IACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/dist/phpstan.d.ts CHANGED
@@ -45,6 +45,61 @@ export interface AnalyseOptions {
45
45
  /** Fout die als bruikbare melding naar het model teruggaat, niet als crash. */
46
46
  export declare class PhpstanError extends Error {
47
47
  }
48
+ /** Uitkomst van `phpstan diagnose`: PHPStans eigen tekst, ongewijzigd. */
49
+ export interface Diagnosis {
50
+ readonly project: string;
51
+ /** De regels zoals PHPStan ze op stdout zet. */
52
+ readonly output: string;
53
+ }
54
+ /** Uitkomst van het legen van de resultcache. */
55
+ export interface CacheCleared {
56
+ readonly project: string;
57
+ /** PHPStans eigen tekst; die noemt de map die geleegd is. */
58
+ readonly output: string;
59
+ }
60
+ /**
61
+ * Leegt PHPStans resultcache voor dit project.
62
+ *
63
+ * Wél door de queue van dit project: een analyse die tegelijk draait gebruikt
64
+ * diezelfde cache. Let op dat de cache standaard *niet* in het project ligt maar in
65
+ * `sys_get_temp_dir()/phpstan`, en dus gedeeld wordt met elk ander project dat geen
66
+ * eigen `tmpDir` in zijn config zet. PHPStans uitvoer noemt de map, zodat aan het
67
+ * antwoord te zien is wat er precies geleegd is.
68
+ */
69
+ export declare function clearResultCache(project: ResolvedProject, options?: Pick<AnalyseOptions, 'config' | 'timeoutMs' | 'signal'>): Promise<CacheCleared>;
70
+ /** Opties voor het genereren van een baseline. */
71
+ export interface GenerateBaselineOptions extends AnalyseOptions {
72
+ /** Absoluut pad waar de baseline geschreven wordt. */
73
+ readonly file: string;
74
+ /** Ook schrijven als er niets te negeren valt. */
75
+ readonly allowEmpty?: boolean;
76
+ }
77
+ /** Uitkomst van het genereren van een baseline. */
78
+ export interface BaselineGenerated {
79
+ readonly project: string;
80
+ readonly file: string;
81
+ /** Hoeveel fouten er in de baseline gezet zijn. */
82
+ readonly errors: number;
83
+ readonly output: string;
84
+ }
85
+ /**
86
+ * Laat PHPStan een baseline schrijven: alle huidige fouten worden erin gezet, zodat
87
+ * een volgende analyse alleen meldt wat er nieuw bij komt.
88
+ *
89
+ * Dit schrijft een bestand in het project — de enige tool die dat doet, en daarom
90
+ * alleen bereikbaar als de server met schrijfrechten gestart is.
91
+ */
92
+ export declare function generateBaseline(project: ResolvedProject, options: GenerateBaselineOptions): Promise<BaselineGenerated>;
93
+ /**
94
+ * Draait `phpstan diagnose` in één project: PHP- en PHPStan-versie, de configs die
95
+ * Composer-pakketten meebrengen, en of de turbo-extensie aanstaat. Geen analyse, dus
96
+ * geen bevindingen — dit is wat je wil weten als een project zich vreemd gedraagt.
97
+ *
98
+ * Wel door de semafoor en niet door de queue per project: dit is een PHPStan-proces
99
+ * dat de machine belast, maar het schrijft geen resultcache waar een analyse
100
+ * overheen zou kunnen lopen.
101
+ */
102
+ export declare function diagnose(project: ResolvedProject, options?: Pick<AnalyseOptions, 'timeoutMs' | 'signal'>): Promise<Diagnosis>;
48
103
  /**
49
104
  * Draait PHPStan in één project en levert de bevindingen.
50
105
  *
package/dist/phpstan.js CHANGED
@@ -20,6 +20,100 @@ const MEMORY_LIMIT_PATTERN = /^(-1|\d+[KMG]?)$/i;
20
20
  /** Fout die als bruikbare melding naar het model teruggaat, niet als crash. */
21
21
  export class PhpstanError extends Error {
22
22
  }
23
+ /**
24
+ * Leegt PHPStans resultcache voor dit project.
25
+ *
26
+ * Wél door de queue van dit project: een analyse die tegelijk draait gebruikt
27
+ * diezelfde cache. Let op dat de cache standaard *niet* in het project ligt maar in
28
+ * `sys_get_temp_dir()/phpstan`, en dus gedeeld wordt met elk ander project dat geen
29
+ * eigen `tmpDir` in zijn config zet. PHPStans uitvoer noemt de map, zodat aan het
30
+ * antwoord te zien is wat er precies geleegd is.
31
+ */
32
+ export async function clearResultCache(project, options = {}) {
33
+ const binary = requireBinary(project);
34
+ throwIfAborted(project.path, options.signal);
35
+ return enqueue(project.path, () => withSlot(async () => {
36
+ throwIfAborted(project.path, options.signal);
37
+ const args = ['clear-result-cache', '--no-ansi'];
38
+ if (options.config !== undefined)
39
+ args.push(`--configuration=${options.config}`);
40
+ const execution = await execute(binary, args, project.path, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, options.signal);
41
+ const output = execution.stdout.trim();
42
+ if (execution.code !== 0 || output === '') {
43
+ throw new PhpstanError(`PHPStan kon de resultcache van ${project.path} niet legen (exit code ${execution.code ?? '?'}).\n\n` +
44
+ (execution.stderr.trim() || output || 'Geen output op stdout of stderr.'));
45
+ }
46
+ return { project: project.path, output };
47
+ }));
48
+ }
49
+ /**
50
+ * Laat PHPStan een baseline schrijven: alle huidige fouten worden erin gezet, zodat
51
+ * een volgende analyse alleen meldt wat er nieuw bij komt.
52
+ *
53
+ * Dit schrijft een bestand in het project — de enige tool die dat doet, en daarom
54
+ * alleen bereikbaar als de server met schrijfrechten gestart is.
55
+ */
56
+ export async function generateBaseline(project, options) {
57
+ const binary = requireBinary(project);
58
+ throwIfAborted(project.path, options.signal);
59
+ const installed = { ...project, binary };
60
+ return enqueue(project.path, () => withSlot(async () => {
61
+ throwIfAborted(project.path, options.signal);
62
+ const execution = await execute(binary, buildBaselineArgs(installed, options), project.path, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, options.signal);
63
+ // Op de tekst afgaan en niet op de exit code, om dezelfde reden als bij een
64
+ // analyse: PHPStan meldt "niets te schrijven" met exit 1 én met een bruikbare
65
+ // uitleg erbij, en die uitleg is meer waard dan een eigen foutmelding.
66
+ const output = (execution.stdout.trim() || execution.stderr.trim()).slice(0, 2000);
67
+ const match = /Baseline generated with (\d+) error/i.exec(output);
68
+ if (match === null) {
69
+ throw new PhpstanError(`PHPStan heeft geen baseline geschreven in ${project.path}.\n\n` +
70
+ (output === '' ? 'Geen output op stdout of stderr.' : output));
71
+ }
72
+ return { project: project.path, file: options.file, errors: Number(match[1]), output };
73
+ }));
74
+ }
75
+ /** Weigert een project zonder PHPStan met de melding die zegt wat er wél kan. */
76
+ function requireBinary(project) {
77
+ if (project.binary === null) {
78
+ throw new PhpstanError(`PHPStan is niet geïnstalleerd in ${project.path} (vendor/bin/phpstan ontbreekt). ` +
79
+ 'Draai daar eerst `composer install`.');
80
+ }
81
+ return project.binary;
82
+ }
83
+ /**
84
+ * Draait `phpstan diagnose` in één project: PHP- en PHPStan-versie, de configs die
85
+ * Composer-pakketten meebrengen, en of de turbo-extensie aanstaat. Geen analyse, dus
86
+ * geen bevindingen — dit is wat je wil weten als een project zich vreemd gedraagt.
87
+ *
88
+ * Wel door de semafoor en niet door de queue per project: dit is een PHPStan-proces
89
+ * dat de machine belast, maar het schrijft geen resultcache waar een analyse
90
+ * overheen zou kunnen lopen.
91
+ */
92
+ export async function diagnose(project, options = {}) {
93
+ const binary = requireBinary(project);
94
+ throwIfAborted(project.path, options.signal);
95
+ return withSlot(async () => {
96
+ throwIfAborted(project.path, options.signal);
97
+ const execution = await execute(binary, ['diagnose', '--no-ansi'], project.path, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, options.signal);
98
+ if (execution.spawnError !== null) {
99
+ throw new PhpstanError(`PHPStan kon niet gestart worden in ${project.path}: ${execution.spawnError.message}`);
100
+ }
101
+ if (execution.signal !== null) {
102
+ throw new PhpstanError(execution.reason === 'geannuleerd'
103
+ ? `De diagnose van ${project.path} is afgebroken omdat het verzoek geannuleerd is.`
104
+ : `De diagnose van ${project.path} is afgebroken (signaal ${execution.signal}).`);
105
+ }
106
+ // diagnose zet zijn rapport op stdout en alleen de configuratieregel op stderr.
107
+ // Een lege stdout betekent dus dat er iets anders mis is; dan is PHPStans eigen
108
+ // uitleg bruikbaarder dan een zelfbedachte melding.
109
+ const output = execution.stdout.trim();
110
+ if (output === '') {
111
+ throw new PhpstanError(`PHPStan gaf geen diagnose terug in ${project.path} (exit code ${execution.code ?? '?'}).\n\n` +
112
+ (execution.stderr.trim() || 'Geen output op stdout of stderr.'));
113
+ }
114
+ return { project: project.path, output };
115
+ });
116
+ }
23
117
  /**
24
118
  * Draait PHPStan in één project en levert de bevindingen.
25
119
  *
@@ -28,13 +122,8 @@ export class PhpstanError extends Error {
28
122
  * overheen. Verschillende projecten draaien wél parallel.
29
123
  */
30
124
  export async function analyse(project, options = {}) {
31
- if (project.binary === null) {
32
- throw new PhpstanError(`PHPStan is niet geïnstalleerd in ${project.path} (vendor/bin/phpstan ontbreekt). ` +
33
- 'Draai daar eerst `composer install`.');
34
- }
35
- // Na de null-check is binary een string; dit maakt dat expliciet zodat run()
36
- // er geen cast voor nodig heeft.
37
- const installed = { ...project, binary: project.binary };
125
+ // requireBinary levert de string op, zodat run() er geen cast voor nodig heeft.
126
+ const installed = { ...project, binary: requireBinary(project) };
38
127
  // Vóór de queue: een verzoek dat al afgebroken is hoeft niet eens in de rij.
39
128
  throwIfAborted(installed.path, options.signal);
40
129
  return enqueue(installed.path, () => withSlot(() => run(installed, options)));
@@ -81,7 +170,33 @@ export function loadDefaultMemoryLimit(env = process.env) {
81
170
  return configured;
82
171
  }
83
172
  function buildArgs(project, options) {
84
- const args = ['analyse', '--error-format=json', '--no-progress', '--no-interaction', '--no-ansi'];
173
+ return [
174
+ 'analyse',
175
+ '--error-format=json',
176
+ ...QUIET_FLAGS,
177
+ ...flags(options),
178
+ ...targets(project, options),
179
+ ];
180
+ }
181
+ /**
182
+ * Dezelfde aanroep, maar met `--generate-baseline` in plaats van een errorformat:
183
+ * PHPStan schrijft dan een bestand en rapporteert in tekst hoeveel het er waren.
184
+ */
185
+ function buildBaselineArgs(project, options) {
186
+ return [
187
+ 'analyse',
188
+ ...QUIET_FLAGS,
189
+ `--generate-baseline=${options.file}`,
190
+ ...(options.allowEmpty === true ? ['--allow-empty-baseline'] : []),
191
+ ...flags(options),
192
+ ...targets(project, options),
193
+ ];
194
+ }
195
+ /** Vlaggen die er altijd bij horen, zodat de uitvoer machineleesbaar blijft. */
196
+ const QUIET_FLAGS = ['--no-progress', '--no-interaction', '--no-ansi'];
197
+ /** De vlaggen die uit de opties komen, in de volgorde die PHPStan zelf aanhoudt. */
198
+ function flags(options) {
199
+ const args = [];
85
200
  // Vóór de rest, zoals PHPStan het zelf ook documenteert: de config bepaalt wat de
86
201
  // overige vlaggen overrulen.
87
202
  if (options.config !== undefined)
@@ -92,14 +207,17 @@ function buildArgs(project, options) {
92
207
  const memoryLimit = options.memoryLimit ?? loadDefaultMemoryLimit();
93
208
  if (memoryLimit !== undefined)
94
209
  args.push(`--memory-limit=${memoryLimit}`);
210
+ return args;
211
+ }
212
+ /** De paden die geanalyseerd worden, met de controle die PHPStan zelf niet uitlegt. */
213
+ function targets(project, options) {
95
214
  // Zonder config én zonder paden weet PHPStan niet wat het moet doen; dan is een
96
215
  // duidelijke melding beter dan PHPStans eigen usage-tekst.
97
216
  const paths = options.paths ?? [];
98
217
  if (paths.length === 0 && project.config === null) {
99
218
  throw new PhpstanError(`${project.path} heeft geen phpstan.neon(.dist). Geef expliciet paths mee, of maak een config aan.`);
100
219
  }
101
- args.push(...paths);
102
- return args;
220
+ return paths;
103
221
  }
104
222
  /** Hoe lang een gedode procesgroep de tijd krijgt voordat SIGKILL volgt. */
105
223
  const KILL_GRACE_MS = 5_000;
@@ -115,7 +233,19 @@ const KILL_GRACE_MS = 5_000;
115
233
  function execute(binary, args, cwd, timeoutMs, signal) {
116
234
  return new Promise((resolve) => {
117
235
  // Geen `timeout`-optie van spawn: die doodt alleen het proces zelf.
118
- const child = spawn(binary, args, { cwd, shell: false, detached: true });
236
+ const child = spawn(binary, args, {
237
+ cwd,
238
+ shell: false,
239
+ detached: true,
240
+ // PAO_DISABLE hoort bij --no-progress en --no-ansi: allemaal om PHPStans
241
+ // eigen, machineleesbare uitvoer te krijgen. laravel/pao hangt zich via
242
+ // autoload.files in de autoloader van het project, ziet aan variabelen als
243
+ // CLAUDECODE dat er een agent draait, en vervangt de uitvoer dan door zijn
244
+ // eigen formaat ({"tool":"phpstan","result":"passed"}). Dat parseert hier
245
+ // niet, dus zonder deze regel is zo'n project onanalyseerbaar — en de server
246
+ // erft die agent-variabelen juist altijd, want de client start hem.
247
+ env: { ...process.env, PAO_DISABLE: '1' },
248
+ });
119
249
  const stdout = [];
120
250
  const stderr = [];
121
251
  let spawnError = null;