fallow 2.69.0 → 2.71.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2545 @@
1
+ /**
2
+ * AUTOGENERATED FILE. DO NOT EDIT.
3
+ *
4
+ * Generated from `docs/output-schema.json` by
5
+ * `editors/vscode/scripts/codegen-types.mjs`. The same file is written to
6
+ * `editors/vscode/src/generated/output-contract.d.ts` (extension internal) and
7
+ * `npm/fallow/types/output-contract.d.ts` (published as `fallow/types`).
8
+ *
9
+ * To change a shape:
10
+ * 1. Edit the Rust struct in `crates/types/src/results.rs` (or the
11
+ * duplicates crate at `crates/core/src/duplicates/types.rs`). The Rust
12
+ * side is the runtime source of truth for the JSON output.
13
+ * 2. Update `docs/output-schema.json` to match. The schema is
14
+ * hand-maintained against the Rust structs; the drift-guard test in
15
+ * `crates/cli/src/report/json.rs` enforces only a subset of the
16
+ * contract (the `HealthFindingAction` enum). Field-level drift between
17
+ * Rust and the schema is currently caught by code review.
18
+ * 3. Run `pnpm --filter fallow-vscode codegen:types` from anywhere.
19
+ * 4. Commit both regenerated files alongside the schema edit.
20
+ *
21
+ * CI runs `pnpm --filter fallow-vscode check:codegen` which fails when
22
+ * either committed file disagrees with what regen would produce.
23
+ */
24
+ /* eslint-disable */
25
+
26
+
27
+ /**
28
+ * Schemas for the JSON output of fallow commands. Bare `fallow --format json` produces a combined envelope with `check`, `dupes`, and `health` keys. Individual commands (`fallow dead-code --format json`, `fallow health --format json`, `fallow dupes --format json`, `fallow audit --format json`, `fallow explain <issue-type> --format json`, `fallow coverage setup --json`) produce their own top-level structure.
29
+ */
30
+ export type FallowJsonOutput = (CombinedOutput | CheckOutput | CheckGroupedOutput | HealthOutput | DupesOutput | AuditOutput | ExplainOutput | CoverageSetupOutput | CodeClimateOutput | ReviewEnvelopeOutput | ReviewReconcileOutput)
31
+ /**
32
+ * Schema version for this output format (independent of tool version). Bump policy: ADDITIVE changes (new optional top-level fields, new optional struct fields, new array entries, new MCP tools, new CLI flags that map to new optional fields) do NOT bump the version; consumers receive new fields without breaking. BREAKING changes (renamed fields, removed fields, type changes, enum-variant removals, semantic changes to existing fields) DO bump. To detect newly-added fields without a bump, check field presence via JSON-key existence rather than gating on the version. v4 was introduced alongside fallow-cov-protocol 0.2 (per-finding verdict, stable IDs, evidence block, renamed summary fields); v5 introduced health_score formula_version 2 with scale-invariant scoring semantics; v6 widened `AddToConfigAction.value` from a scalar string to `oneOf: [string, array]` so the new `ignoreExports` action can carry a paste-ready array of `{ file, exports }` rule objects (the legacy `ignoreDependencies` etc. variants still emit strings, so consumers that switch on `config_key` keep working unchanged). The runtime-coverage block is extended additively as the protocol evolves (currently 0.3, which adds an optional capture_quality summary field). Other additive examples: dupes --group-by adds optional grouped_by, total_issues, groups fields without bumping.
33
+ */
34
+ export type SchemaVersion = 6
35
+ /**
36
+ * Fallow tool version that produced this output.
37
+ */
38
+ export type ToolVersion = string
39
+ /**
40
+ * Analysis duration in milliseconds.
41
+ */
42
+ export type ElapsedMs = number
43
+ /**
44
+ * A suggested action for resolving an issue. Each issue includes an array of possible actions: a fix action (auto-fixable or manual), suppress-line, suppress-file, and/or add-to-config.
45
+ */
46
+ export type IssueAction = (FixAction | SuppressLineAction | SuppressFileAction | AddToConfigAction)
47
+ /**
48
+ * Only present inside `fallow audit --format json` sub-results. true means this finding's structural key was not present at the base ref; false means it was inherited from the base ref.
49
+ */
50
+ export type AuditIntroduced = boolean
51
+ /**
52
+ * Overall report verdict. Independent of the per-finding verdict.
53
+ */
54
+ export type RuntimeCoverageReportVerdict = ("clean" | "hot-path-touched" | "cold-code-detected" | "license-expired-grace" | "unknown")
55
+ /**
56
+ * Discrete signal captured during runtime-coverage post-processing. Multiple signals can apply to a single report; `verdict` collapses to the most actionable one for the current context.
57
+ */
58
+ export type RuntimeCoverageSignal = ("license-expired-grace" | "cold-code-detected" | "hot-path-touched")
59
+ /**
60
+ * Protocol-level per-function runtime coverage verdict derived from the decision table in fallow-cov-protocol. The CLI's `runtime_coverage.findings` array omits `active` entries even though the underlying enum still includes it.
61
+ */
62
+ export type RuntimeCoverageVerdict = ("safe_to_delete" | "review_required" | "coverage_unavailable" | "low_traffic" | "active" | "unknown")
63
+ /**
64
+ * Confidence level for a runtime coverage finding.
65
+ */
66
+ export type RuntimeCoverageConfidence = ("very_high" | "high" | "medium" | "low" | "none" | "unknown")
67
+ /**
68
+ * Blast-radius risk band. The current thresholds are high at >=20 static callers or >=1,000,000 traffic-weighted caller reach; medium at >=5 callers or >=50,000 weighted reach; low otherwise.
69
+ */
70
+ export type RuntimeCoverageRiskBand = ("low" | "medium" | "high")
71
+ /**
72
+ * License or trial watermark applied to runtime coverage output.
73
+ */
74
+ export type RuntimeCoverageWatermark = ("trial-expired" | "license-expired-grace" | "unknown")
75
+ /**
76
+ * CodeClimate-compatible issue array. GitLab Code Quality consumes the same shape.
77
+ */
78
+ export type CodeClimateOutput = CodeClimateIssue[]
79
+
80
+ /**
81
+ * Combined output from bare `fallow` invocation. Contains results from all enabled analyses (check, dupes, health). Use --only/--skip to select.
82
+ */
83
+ export interface CombinedOutput {
84
+ schema_version: SchemaVersion
85
+ version: ToolVersion
86
+ elapsed_ms: ElapsedMs
87
+ check?: CheckOutput
88
+ dupes?: DupesOutput
89
+ health?: HealthOutput
90
+ }
91
+ export interface CheckOutput {
92
+ schema_version: SchemaVersion
93
+ version: ToolVersion
94
+ elapsed_ms: ElapsedMs
95
+ /**
96
+ * Total number of issues found across all categories.
97
+ */
98
+ total_issues: number
99
+ unused_files: UnusedFile[]
100
+ unused_exports: UnusedExport[]
101
+ unused_types: UnusedExport[]
102
+ private_type_leaks: PrivateTypeLeak[]
103
+ unused_dependencies: UnusedDependency[]
104
+ unused_dev_dependencies: UnusedDependency[]
105
+ unused_optional_dependencies: UnusedDependency[]
106
+ unused_enum_members: UnusedMember[]
107
+ unused_class_members: UnusedMember[]
108
+ unresolved_imports: UnresolvedImport[]
109
+ unlisted_dependencies: UnlistedDependency[]
110
+ duplicate_exports: DuplicateExport[]
111
+ type_only_dependencies: TypeOnlyDependency[]
112
+ test_only_dependencies: TestOnlyDependency[]
113
+ circular_dependencies: CircularDependency[]
114
+ boundary_violations: BoundaryViolation[]
115
+ stale_suppressions: StaleSuppression[]
116
+ /**
117
+ * Entries in pnpm-workspace.yaml's catalog: or catalogs: sections not referenced by any workspace package via the catalog: protocol.
118
+ */
119
+ unused_catalog_entries?: UnusedCatalogEntry[]
120
+ /**
121
+ * Workspace package.json references to catalogs (catalog: or catalog:<name>) that do not declare the consumed package. pnpm install will error until the named catalog grows to include the package or the reference is switched / removed.
122
+ */
123
+ unresolved_catalog_references?: UnresolvedCatalogReference[]
124
+ entry_points?: EntryPoints
125
+ summary?: CheckSummary
126
+ baseline_deltas?: BaselineDeltas
127
+ baseline?: BaselineMatch
128
+ regression?: RegressionResult
129
+ _meta?: Meta
130
+ }
131
+ export interface UnusedFile {
132
+ /**
133
+ * File path (forward slashes, relative to project root).
134
+ */
135
+ path: string
136
+ /**
137
+ * Suggested actions to resolve this issue.
138
+ */
139
+ actions: IssueAction[]
140
+ introduced?: AuditIntroduced
141
+ }
142
+ export interface FixAction {
143
+ /**
144
+ * Kebab-case identifier for the fix action.
145
+ */
146
+ type: ("remove-export" | "delete-file" | "remove-dependency" | "move-dependency" | "remove-enum-member" | "remove-class-member" | "resolve-import" | "install-dependency" | "remove-duplicate" | "move-to-dev" | "refactor-cycle" | "refactor-boundary" | "export-type" | "remove-catalog-entry" | "update-catalog-reference" | "add-catalog-entry" | "remove-catalog-reference")
147
+ /**
148
+ * Whether `fallow fix` can apply this fix automatically.
149
+ */
150
+ auto_fixable: boolean
151
+ /**
152
+ * Human-readable description of the fix.
153
+ */
154
+ description: string
155
+ /**
156
+ * Optional context note. Present on non-auto-fixable actions, and on auto-fixable re-export findings to warn about public API surface.
157
+ */
158
+ note?: string
159
+ /**
160
+ * Only present on `update-catalog-reference` actions: catalogs in the same workspace that DO declare the package, sorted lexicographically. Lets agents pick the catalog to switch to without re-reading the source.
161
+ */
162
+ available_in_catalogs?: string[]
163
+ }
164
+ export interface SuppressLineAction {
165
+ /**
166
+ * Action type identifier.
167
+ */
168
+ type: "suppress-line"
169
+ /**
170
+ * Always false for suppress actions.
171
+ */
172
+ auto_fixable: false
173
+ /**
174
+ * Human-readable description of the suppression.
175
+ */
176
+ description: string
177
+ /**
178
+ * The inline comment to place above the line (e.g., '// fallow-ignore-next-line unused-export'). When multiple suppressible findings share the same path and line, this may contain a comma-separated issue-kind list such as '// fallow-ignore-next-line unused-export, complexity'.
179
+ */
180
+ comment: string
181
+ /**
182
+ * Present on multi-location issue types (e.g., duplicate_exports) to indicate the comment must be applied at each location.
183
+ */
184
+ scope?: "per-location"
185
+ }
186
+ export interface SuppressFileAction {
187
+ /**
188
+ * Action type identifier.
189
+ */
190
+ type: "suppress-file"
191
+ /**
192
+ * Always false for suppress actions.
193
+ */
194
+ auto_fixable: false
195
+ /**
196
+ * Human-readable description of the suppression.
197
+ */
198
+ description: string
199
+ /**
200
+ * The file-level comment to place at the top of the file (e.g., '// fallow-ignore-file unused-file').
201
+ */
202
+ comment: string
203
+ }
204
+ export interface AddToConfigAction {
205
+ /**
206
+ * Action type identifier.
207
+ */
208
+ type: "add-to-config"
209
+ /**
210
+ * Always false for config actions.
211
+ */
212
+ auto_fixable: false
213
+ /**
214
+ * Human-readable description of the config change.
215
+ */
216
+ description: string
217
+ /**
218
+ * The fallow config key to add the value to (e.g., 'ignoreDependencies').
219
+ */
220
+ config_key: string
221
+ /**
222
+ * Value to add to the config key. Shape depends on `config_key`. For scalar config keys (`ignoreDependencies`, others) this is a string such as `"lodash"`. For `ignoreExports` this is an array of `{ file, exports }` rule objects so the snippet can be merged into the user's config verbatim.
223
+ */
224
+ value: (string | {
225
+ file: string
226
+ exports: string[]
227
+ }[])
228
+ /**
229
+ * Optional URL pointing at a stable JSON Schema fragment that describes the shape of `value`. Agents that intend to validate `value` before writing it into a user's config can fetch the linked schema and run it against `value`. The URL is a JSON Pointer fragment into fallow's main config schema (e.g. `schema.json#/properties/ignoreExports` for the ignoreExports action, or `schema.json#/properties/ignoreDependencies/items` for the per-package ignoreDependencies action). Strictly additive: consumers that ignore the field keep working unchanged.
230
+ */
231
+ value_schema?: string
232
+ }
233
+ export interface UnusedExport {
234
+ /**
235
+ * File path containing the unused export.
236
+ */
237
+ path: string
238
+ /**
239
+ * Name of the unused export.
240
+ */
241
+ export_name: string
242
+ /**
243
+ * Whether this is a type-only export (interface, type alias).
244
+ */
245
+ is_type_only: boolean
246
+ /**
247
+ * 1-based line number.
248
+ */
249
+ line: number
250
+ /**
251
+ * 0-based byte column offset.
252
+ */
253
+ col: number
254
+ /**
255
+ * Byte offset into the source file (used by the fix command).
256
+ */
257
+ span_start: number
258
+ /**
259
+ * Whether this finding comes from a barrel/index re-export rather than the source definition.
260
+ */
261
+ is_re_export: boolean
262
+ /**
263
+ * Suggested actions to resolve this issue.
264
+ */
265
+ actions: IssueAction[]
266
+ introduced?: AuditIntroduced
267
+ }
268
+ export interface PrivateTypeLeak {
269
+ /**
270
+ * File path containing the exported signature.
271
+ */
272
+ path: string
273
+ /**
274
+ * Name of the exported symbol whose signature references a private type.
275
+ */
276
+ export_name: string
277
+ /**
278
+ * Name of the same-file private type referenced by the exported signature.
279
+ */
280
+ type_name: string
281
+ /**
282
+ * 1-based line number.
283
+ */
284
+ line: number
285
+ /**
286
+ * 0-based byte column offset.
287
+ */
288
+ col: number
289
+ /**
290
+ * Byte offset of the private type reference in the source file.
291
+ */
292
+ span_start: number
293
+ /**
294
+ * Suggested actions to resolve this issue.
295
+ */
296
+ actions: IssueAction[]
297
+ introduced?: AuditIntroduced
298
+ }
299
+ export interface UnusedDependency {
300
+ /**
301
+ * Package name, including internal workspace package names.
302
+ */
303
+ package_name: string
304
+ /**
305
+ * Which section of package.json the dependency is listed in.
306
+ */
307
+ location: ("dependencies" | "devDependencies" | "optionalDependencies")
308
+ /**
309
+ * Path to the package.json where this dependency is listed.
310
+ */
311
+ path: string
312
+ /**
313
+ * 1-based line number of the dependency entry in package.json.
314
+ */
315
+ line: number
316
+ /**
317
+ * Workspace roots that import this package even though the declaring workspace does not.
318
+ */
319
+ used_in_workspaces?: string[]
320
+ /**
321
+ * Suggested actions to resolve this issue.
322
+ */
323
+ actions: IssueAction[]
324
+ introduced?: AuditIntroduced
325
+ }
326
+ export interface UnusedMember {
327
+ /**
328
+ * File path containing the unused member.
329
+ */
330
+ path: string
331
+ /**
332
+ * Name of the parent enum or class.
333
+ */
334
+ parent_name: string
335
+ /**
336
+ * Name of the unused member.
337
+ */
338
+ member_name: string
339
+ /**
340
+ * Member kind (e.g., EnumMember, ClassMethod, ClassProperty).
341
+ */
342
+ kind: string
343
+ /**
344
+ * 1-based line number.
345
+ */
346
+ line: number
347
+ /**
348
+ * 0-based byte column offset.
349
+ */
350
+ col: number
351
+ /**
352
+ * Suggested actions to resolve this issue.
353
+ */
354
+ actions: IssueAction[]
355
+ introduced?: AuditIntroduced
356
+ }
357
+ export interface UnresolvedImport {
358
+ /**
359
+ * File path containing the unresolved import.
360
+ */
361
+ path: string
362
+ /**
363
+ * The import specifier that could not be resolved.
364
+ */
365
+ specifier: string
366
+ /**
367
+ * 1-based line number.
368
+ */
369
+ line: number
370
+ /**
371
+ * 0-based byte column offset.
372
+ */
373
+ col: number
374
+ /**
375
+ * Suggested actions to resolve this issue.
376
+ */
377
+ actions: IssueAction[]
378
+ introduced?: AuditIntroduced
379
+ }
380
+ export interface UnlistedDependency {
381
+ /**
382
+ * Package name, including internal workspace package names, that is imported but not listed in package.json.
383
+ */
384
+ package_name: string
385
+ /**
386
+ * Import sites where this unlisted dependency is used.
387
+ */
388
+ imported_from: ImportSite[]
389
+ /**
390
+ * Suggested actions to resolve this issue.
391
+ */
392
+ actions: IssueAction[]
393
+ introduced?: AuditIntroduced
394
+ }
395
+ export interface ImportSite {
396
+ /**
397
+ * File containing the import.
398
+ */
399
+ path: string
400
+ /**
401
+ * 1-based line number.
402
+ */
403
+ line: number
404
+ /**
405
+ * 0-based byte column offset.
406
+ */
407
+ col: number
408
+ }
409
+ export interface DuplicateExport {
410
+ /**
411
+ * The export name that appears in multiple modules.
412
+ */
413
+ export_name: string
414
+ /**
415
+ * Locations where this export name is defined.
416
+ */
417
+ locations: DuplicateLocation[]
418
+ /**
419
+ * Suggested actions to resolve this issue.
420
+ */
421
+ actions: IssueAction[]
422
+ introduced?: AuditIntroduced
423
+ }
424
+ export interface DuplicateLocation {
425
+ /**
426
+ * File containing the duplicate export.
427
+ */
428
+ path: string
429
+ /**
430
+ * 1-based line number.
431
+ */
432
+ line: number
433
+ /**
434
+ * 0-based byte column offset.
435
+ */
436
+ col: number
437
+ }
438
+ export interface TypeOnlyDependency {
439
+ /**
440
+ * Production dependency that is only used via type-only imports.
441
+ */
442
+ package_name: string
443
+ /**
444
+ * Path to the package.json where this dependency is listed.
445
+ */
446
+ path: string
447
+ /**
448
+ * 1-based line number of the dependency entry in package.json.
449
+ */
450
+ line: number
451
+ /**
452
+ * Suggested actions to resolve this issue.
453
+ */
454
+ actions: IssueAction[]
455
+ introduced?: AuditIntroduced
456
+ }
457
+ export interface TestOnlyDependency {
458
+ /**
459
+ * Production dependency that is only imported by test files — consider moving to devDependencies.
460
+ */
461
+ package_name: string
462
+ /**
463
+ * Path to the package.json where this dependency is listed.
464
+ */
465
+ path: string
466
+ /**
467
+ * 1-based line number of the dependency entry in package.json.
468
+ */
469
+ line: number
470
+ /**
471
+ * Suggested actions to resolve this issue.
472
+ */
473
+ actions: IssueAction[]
474
+ introduced?: AuditIntroduced
475
+ }
476
+ export interface CircularDependency {
477
+ /**
478
+ * File paths forming the import cycle, in cycle order.
479
+ */
480
+ files: string[]
481
+ /**
482
+ * Number of files in the cycle.
483
+ */
484
+ length: number
485
+ /**
486
+ * 1-based line number of the import that starts the cycle.
487
+ */
488
+ line: number
489
+ /**
490
+ * 0-based byte column offset of the import that starts the cycle.
491
+ */
492
+ col: number
493
+ /**
494
+ * Whether this cycle crosses workspace package boundaries. Only present when true.
495
+ */
496
+ is_cross_package?: boolean
497
+ /**
498
+ * Suggested actions to resolve this issue.
499
+ */
500
+ actions: IssueAction[]
501
+ introduced?: AuditIntroduced
502
+ }
503
+ export interface BoundaryViolation {
504
+ /**
505
+ * Relative path to the file making the disallowed import.
506
+ */
507
+ from_path: string
508
+ /**
509
+ * Relative path to the file being imported.
510
+ */
511
+ to_path: string
512
+ /**
513
+ * Architecture zone of the importing file.
514
+ */
515
+ from_zone: string
516
+ /**
517
+ * Architecture zone of the imported file.
518
+ */
519
+ to_zone: string
520
+ /**
521
+ * Relative path of the imported file (used as specifier).
522
+ */
523
+ import_specifier: string
524
+ /**
525
+ * 1-based line number of the import statement.
526
+ */
527
+ line: number
528
+ /**
529
+ * 0-based byte column offset of the import statement.
530
+ */
531
+ col: number
532
+ /**
533
+ * Suggested actions to resolve this issue.
534
+ */
535
+ actions: IssueAction[]
536
+ introduced?: AuditIntroduced
537
+ }
538
+ export interface StaleSuppression {
539
+ /**
540
+ * Relative path to the file containing the stale suppression.
541
+ */
542
+ path: string
543
+ /**
544
+ * 1-based line number of the suppression comment or JSDoc tag.
545
+ */
546
+ line: number
547
+ /**
548
+ * 0-based byte column offset.
549
+ */
550
+ col: number
551
+ origin: ({
552
+ type: "comment"
553
+ /**
554
+ * The issue kind token (e.g. 'unused-exports'). Absent for blanket suppressions.
555
+ */
556
+ issue_kind?: string
557
+ /**
558
+ * True for fallow-ignore-file, false for fallow-ignore-next-line.
559
+ */
560
+ is_file_level?: boolean
561
+ } | {
562
+ type: "jsdoc_tag"
563
+ /**
564
+ * Name of the export that was tagged @expected-unused.
565
+ */
566
+ export_name: string
567
+ })
568
+ introduced?: AuditIntroduced
569
+ }
570
+ /**
571
+ * A package declared in a pnpm-workspace.yaml catalog that is not referenced by any workspace package via the catalog: protocol.
572
+ */
573
+ export interface UnusedCatalogEntry {
574
+ /**
575
+ * Package name declared in the catalog (e.g. 'react').
576
+ */
577
+ entry_name: string
578
+ /**
579
+ * Catalog group name. 'default' for the top-level catalog: map, or the named catalog key for entries under catalogs.<name>:.
580
+ */
581
+ catalog_name: string
582
+ /**
583
+ * Relative path to pnpm-workspace.yaml.
584
+ */
585
+ path: string
586
+ /**
587
+ * 1-based line number of the catalog entry.
588
+ */
589
+ line: number
590
+ /**
591
+ * Workspace package.json paths that declare the same package with a hardcoded version range instead of catalog:. Omitted when empty.
592
+ */
593
+ hardcoded_consumers?: string[]
594
+ actions?: IssueAction[]
595
+ introduced?: AuditIntroduced
596
+ }
597
+ /**
598
+ * A workspace package.json reference (catalog: or catalog:<name>) pointing at a catalog that does not declare the consumed package. `pnpm install` will fail with ERR_PNPM_CATALOG_ENTRY_NOT_FOUND_FOR_CATALOG_PROTOCOL.
599
+ */
600
+ export interface UnresolvedCatalogReference {
601
+ /**
602
+ * Package name being referenced via the catalog protocol (e.g. 'old-react').
603
+ */
604
+ entry_name: string
605
+ /**
606
+ * Catalog group the reference points at. 'default' for bare catalog: references, or the named catalog key for catalog:<name> references.
607
+ */
608
+ catalog_name: string
609
+ /**
610
+ * Relative path to the consumer package.json.
611
+ */
612
+ path: string
613
+ /**
614
+ * 1-based line number of the dependency entry in the consumer package.json.
615
+ */
616
+ line: number
617
+ /**
618
+ * Other catalogs in the same workspace that DO declare this package, sorted lexicographically. When non-empty, the suggested fix is to flip the reference to one of those catalogs; when empty, the fix is to add the missing entry to the named catalog or to remove the reference. Omitted when empty.
619
+ */
620
+ available_in_catalogs?: string[]
621
+ actions?: IssueAction[]
622
+ introduced?: AuditIntroduced
623
+ }
624
+ /**
625
+ * Entry point detection summary showing total detected entry points and their sources.
626
+ */
627
+ export interface EntryPoints {
628
+ /**
629
+ * Total number of detected entry points.
630
+ */
631
+ total: number
632
+ /**
633
+ * Breakdown of entry points by detection source (e.g., 'package.json', 'next.js', 'config entry').
634
+ */
635
+ sources: {
636
+ [k: string]: number
637
+ }
638
+ }
639
+ /**
640
+ * Per-category issue counts for dead code analysis.
641
+ */
642
+ export interface CheckSummary {
643
+ unused_files?: number
644
+ unused_exports?: number
645
+ unused_types?: number
646
+ private_type_leaks?: number
647
+ unused_dependencies?: number
648
+ unused_dev_dependencies?: number
649
+ unused_enum_members?: number
650
+ unused_class_members?: number
651
+ unresolved_imports?: number
652
+ unlisted_dependencies?: number
653
+ duplicate_exports?: number
654
+ circular_dependencies?: number
655
+ boundary_violations?: number
656
+ stale_suppressions?: number
657
+ unused_catalog_entries?: number
658
+ unresolved_catalog_references?: number
659
+ }
660
+ /**
661
+ * Per-category delta comparison against a saved baseline. Shows current count, baseline count, and delta for each category.
662
+ */
663
+ export interface BaselineDeltas {
664
+ /**
665
+ * Net change in total issues vs baseline (positive = more issues).
666
+ */
667
+ total_delta: number
668
+ /**
669
+ * Per-category breakdown of current, baseline, and delta counts.
670
+ */
671
+ per_category: {
672
+ [k: string]: {
673
+ /**
674
+ * Current issue count for this category.
675
+ */
676
+ current: number
677
+ /**
678
+ * Baseline issue count for this category.
679
+ */
680
+ baseline: number
681
+ /**
682
+ * Change from baseline (current - baseline).
683
+ */
684
+ delta: number
685
+ }
686
+ }
687
+ }
688
+ /**
689
+ * Baseline match statistics. Shows how many baseline entries existed and how many matched current issues. Useful for detecting stale baselines programmatically.
690
+ */
691
+ export interface BaselineMatch {
692
+ /**
693
+ * Total number of entries in the loaded baseline file.
694
+ */
695
+ entries: number
696
+ /**
697
+ * Number of baseline entries that matched current issues and were filtered.
698
+ */
699
+ matched: number
700
+ }
701
+ /**
702
+ * Result of regression detection (--fail-on-regression). Compares current issue counts against a baseline from config or an explicit file.
703
+ */
704
+ export interface RegressionResult {
705
+ status: ("pass" | "exceeded" | "skipped")
706
+ baseline_total?: number
707
+ current_total?: number
708
+ delta?: number
709
+ tolerance?: number
710
+ tolerance_kind?: ("absolute" | "percentage")
711
+ exceeded: boolean
712
+ /**
713
+ * Only present when status is 'skipped'.
714
+ */
715
+ reason?: string
716
+ }
717
+ /**
718
+ * Metric definitions and documentation links. Included when --explain is passed (always present in MCP responses). Helps AI agents and CI systems interpret metric values.
719
+ */
720
+ export interface Meta {
721
+ /**
722
+ * URL to the documentation page for this command.
723
+ */
724
+ docs?: string
725
+ /**
726
+ * Per-metric definitions: name, description, range, interpretation.
727
+ */
728
+ metrics?: {
729
+ [k: string]: {
730
+ /**
731
+ * Human-readable metric name.
732
+ */
733
+ name?: string
734
+ /**
735
+ * What this metric measures and how it is computed.
736
+ */
737
+ description?: string
738
+ /**
739
+ * Valid value range (e.g., '[0, 100]').
740
+ */
741
+ range?: string
742
+ /**
743
+ * How to read the value (e.g., 'lower is better').
744
+ */
745
+ interpretation?: string
746
+ }
747
+ }
748
+ /**
749
+ * Per-rule definitions for check command output.
750
+ */
751
+ rules?: {
752
+ [k: string]: {
753
+ /**
754
+ * Human-readable rule name.
755
+ */
756
+ name?: string
757
+ /**
758
+ * What this rule detects.
759
+ */
760
+ description?: string
761
+ /**
762
+ * URL to the rule documentation.
763
+ */
764
+ docs?: string
765
+ }
766
+ }
767
+ }
768
+ /**
769
+ * Duplication analysis output. Contains clone groups (individual duplicates), clone families (groups sharing the same files), and aggregate statistics. With --group-by, the project-level fields stay populated and an additional `grouped_by` + `groups` envelope is appended.
770
+ */
771
+ export interface DupesOutput {
772
+ schema_version: SchemaVersion
773
+ version: ToolVersion
774
+ elapsed_ms: ElapsedMs
775
+ /**
776
+ * All detected clone groups. Each group contains 2+ instances of identical or near-identical code.
777
+ */
778
+ clone_groups: CloneGroup[]
779
+ /**
780
+ * Clone families: groups of clone groups sharing the same file set, indicating systematic duplication patterns.
781
+ */
782
+ clone_families: CloneFamily[]
783
+ stats: DuplicationStats
784
+ /**
785
+ * Directory pairs with high structural duplication. Only present when mirrored directories are detected.
786
+ */
787
+ mirrored_directories?: MirroredDirectory[]
788
+ /**
789
+ * Resolver mode used for partitioning. Present only when `--group-by` is active.
790
+ */
791
+ grouped_by?: ("owner" | "directory" | "package" | "section")
792
+ /**
793
+ * Per-group buckets when `--group-by` is active. Each clone group is attributed to its largest-owner key (most instances; alphabetical tiebreak). Sort: most clone groups first, then alphabetical, with `(unowned)` pinned last.
794
+ */
795
+ groups?: DuplicationGroup[]
796
+ _meta?: Meta
797
+ }
798
+ /**
799
+ * A group of code clones — the same (or normalized-equivalent) code appearing in multiple places.
800
+ */
801
+ export interface CloneGroup {
802
+ /**
803
+ * All instances where this duplicated code appears.
804
+ */
805
+ instances: CloneInstance[]
806
+ /**
807
+ * Number of normalized source tokens in the duplicated block.
808
+ */
809
+ token_count: number
810
+ /**
811
+ * Number of source lines in the duplicated block.
812
+ */
813
+ line_count: number
814
+ /**
815
+ * Suggested actions to eliminate this clone group.
816
+ */
817
+ actions?: CloneGroupAction[]
818
+ introduced?: AuditIntroduced
819
+ }
820
+ /**
821
+ * A single instance of duplicated code at a specific location.
822
+ */
823
+ export interface CloneInstance {
824
+ /**
825
+ * File path (relative to project root).
826
+ */
827
+ file: string
828
+ /**
829
+ * 1-based start line of the clone.
830
+ */
831
+ start_line: number
832
+ /**
833
+ * 1-based end line of the clone.
834
+ */
835
+ end_line: number
836
+ /**
837
+ * 0-based start column.
838
+ */
839
+ start_col: number
840
+ /**
841
+ * 0-based end column.
842
+ */
843
+ end_col: number
844
+ /**
845
+ * The actual source code fragment.
846
+ */
847
+ fragment: string
848
+ }
849
+ /**
850
+ * A suggested action for a clone group.
851
+ */
852
+ export interface CloneGroupAction {
853
+ /**
854
+ * Action type identifier.
855
+ */
856
+ type: ("extract-shared" | "suppress-line")
857
+ /**
858
+ * Whether fallow can auto-fix this action.
859
+ */
860
+ auto_fixable: boolean
861
+ /**
862
+ * Human-readable description of the action.
863
+ */
864
+ description: string
865
+ /**
866
+ * The inline comment to insert (e.g., '// fallow-ignore-next-line code-duplication'). Present for suppress-line actions.
867
+ */
868
+ comment?: string
869
+ }
870
+ /**
871
+ * A clone family: a set of clone groups sharing the same file set, indicating systematic duplication.
872
+ */
873
+ export interface CloneFamily {
874
+ /**
875
+ * Files involved in this family (sorted).
876
+ */
877
+ files: string[]
878
+ /**
879
+ * Clone groups belonging to this family.
880
+ */
881
+ groups: CloneGroup[]
882
+ /**
883
+ * Total duplicated lines across all groups in this family.
884
+ */
885
+ total_duplicated_lines: number
886
+ /**
887
+ * Total duplicated tokens across all groups in this family.
888
+ */
889
+ total_duplicated_tokens: number
890
+ /**
891
+ * Refactoring suggestions for this family.
892
+ */
893
+ suggestions: RefactoringSuggestion[]
894
+ /**
895
+ * Suggested actions to eliminate duplication in this family.
896
+ */
897
+ actions?: CloneFamilyAction[]
898
+ }
899
+ export interface RefactoringSuggestion {
900
+ /**
901
+ * What kind of refactoring is suggested.
902
+ */
903
+ kind: ("ExtractFunction" | "ExtractModule")
904
+ /**
905
+ * Human-readable description of the suggestion.
906
+ */
907
+ description: string
908
+ /**
909
+ * Estimated lines that could be eliminated.
910
+ */
911
+ estimated_savings: number
912
+ }
913
+ /**
914
+ * A suggested action for a clone family.
915
+ */
916
+ export interface CloneFamilyAction {
917
+ /**
918
+ * Action type identifier.
919
+ */
920
+ type: ("extract-shared" | "apply-suggestion" | "suppress-line")
921
+ /**
922
+ * Whether fallow can auto-fix this action.
923
+ */
924
+ auto_fixable: boolean
925
+ /**
926
+ * Human-readable description of the action.
927
+ */
928
+ description: string
929
+ /**
930
+ * Additional context for the action.
931
+ */
932
+ note?: string
933
+ /**
934
+ * The inline comment to insert (e.g., '// fallow-ignore-next-line code-duplication'). Present for suppress-line actions.
935
+ */
936
+ comment?: string
937
+ }
938
+ /**
939
+ * Aggregate duplication statistics for the analyzed project.
940
+ */
941
+ export interface DuplicationStats {
942
+ /**
943
+ * Total files analyzed.
944
+ */
945
+ total_files: number
946
+ /**
947
+ * Files containing at least one clone instance.
948
+ */
949
+ files_with_clones: number
950
+ /**
951
+ * Total lines across all analyzed files.
952
+ */
953
+ total_lines: number
954
+ /**
955
+ * Lines that are part of at least one clone.
956
+ */
957
+ duplicated_lines: number
958
+ /**
959
+ * Total tokens across all analyzed files.
960
+ */
961
+ total_tokens: number
962
+ /**
963
+ * Tokens that are part of at least one clone.
964
+ */
965
+ duplicated_tokens: number
966
+ /**
967
+ * Number of clone groups found.
968
+ */
969
+ clone_groups: number
970
+ /**
971
+ * Total clone instances across all groups.
972
+ */
973
+ clone_instances: number
974
+ /**
975
+ * Percentage of duplicated lines (0.0–100.0).
976
+ */
977
+ duplication_percentage: number
978
+ }
979
+ /**
980
+ * A pair of directories with high structural duplication, indicating systematic copy-paste of directory trees.
981
+ */
982
+ export interface MirroredDirectory {
983
+ /**
984
+ * First directory path (relative to project root).
985
+ */
986
+ dir_a: string
987
+ /**
988
+ * Second directory path (relative to project root).
989
+ */
990
+ dir_b: string
991
+ /**
992
+ * File names that appear in both directories with duplicated content.
993
+ */
994
+ shared_files: string[]
995
+ /**
996
+ * Total duplicated lines across all shared files.
997
+ */
998
+ total_lines: number
999
+ }
1000
+ /**
1001
+ * A single grouped duplication bucket. Per-group `stats` are dedup-aware and computed over the FULL group BEFORE any `--top` truncation.
1002
+ */
1003
+ export interface DuplicationGroup {
1004
+ /**
1005
+ * Group label (owner / directory / package / section). `(unowned)` for files with no CODEOWNERS rule, `(no section)` for pre-section rules in section mode.
1006
+ */
1007
+ key: string
1008
+ stats: DuplicationStats
1009
+ /**
1010
+ * Clone groups attributed to this owner. Each group's `primary_owner` is its largest-owner key; per-instance `owner` lets consumers see cross-bucket fan-out without re-resolving paths.
1011
+ */
1012
+ clone_groups: AttributedCloneGroup[]
1013
+ clone_families: CloneFamily[]
1014
+ }
1015
+ /**
1016
+ * A clone group annotated with its largest-owner attribution and per-instance owner keys.
1017
+ */
1018
+ export interface AttributedCloneGroup {
1019
+ /**
1020
+ * Largest-owner attribution: the resolver key with the most instances in this clone group. Ties broken alphabetically (smallest key wins).
1021
+ */
1022
+ primary_owner: string
1023
+ token_count: number
1024
+ line_count: number
1025
+ /**
1026
+ * Each instance carries its own `owner` field alongside the standard CloneInstance shape.
1027
+ */
1028
+ instances: {
1029
+ file: string
1030
+ start_line: number
1031
+ end_line: number
1032
+ start_col: number
1033
+ end_col: number
1034
+ fragment: string
1035
+ /**
1036
+ * Resolver key for this specific instance (per-instance, not the group-level largest-owner).
1037
+ */
1038
+ owner: string
1039
+ }[]
1040
+ /**
1041
+ * Suggested actions for this clone group (extract-shared, suppress-line, etc.).
1042
+ */
1043
+ actions?: unknown[]
1044
+ }
1045
+ export interface HealthOutput {
1046
+ schema_version: SchemaVersion
1047
+ version: ToolVersion
1048
+ elapsed_ms: ElapsedMs
1049
+ summary: HealthSummary
1050
+ /**
1051
+ * Functions and synthetic template entries exceeding complexity thresholds, sorted by the --sort criteria.
1052
+ */
1053
+ findings: HealthFinding[]
1054
+ /**
1055
+ * Per-file health scores. Only present when --file-scores is used. Sorted by maintainability_index ascending (worst first). Zero-function files (barrels) are excluded by default.
1056
+ */
1057
+ file_scores?: FileHealthScore[]
1058
+ /**
1059
+ * Hotspot entries combining git churn with complexity. Only present when --hotspots is used. Sorted by score descending (highest risk first).
1060
+ */
1061
+ hotspots?: HotspotEntry[]
1062
+ hotspot_summary?: HotspotSummary
1063
+ vital_signs?: VitalSigns
1064
+ health_score?: HealthScore
1065
+ /**
1066
+ * Functions exceeding 60 LOC (very high risk). Only present when unit size very-high-risk bin >= 3%. Sorted by line count descending.
1067
+ */
1068
+ large_functions?: LargeFunctionEntry[]
1069
+ /**
1070
+ * Ranked refactoring recommendations. Only present when --targets is used. Sorted by efficiency (priority/effort) descending.
1071
+ */
1072
+ targets?: RefactoringTarget[]
1073
+ target_thresholds?: TargetThresholds
1074
+ health_trend?: HealthTrend
1075
+ coverage_gaps?: CoverageGaps
1076
+ runtime_coverage?: RuntimeCoverageReport
1077
+ /**
1078
+ * Resolver mode used when --group-by is active. Present only on grouped output. The top-level `vital_signs`, `health_score`, and `summary` keep the active run scope (for example after --workspace); per-group versions live inside each entry of `groups`.
1079
+ */
1080
+ grouped_by?: ("owner" | "directory" | "package" | "section")
1081
+ /**
1082
+ * Per-group health output, present only when --group-by is active. Each group recomputes its own vital_signs and health_score from the files in that group, mirroring how --workspace scopes a single subset.
1083
+ */
1084
+ groups?: HealthGroup[]
1085
+ /**
1086
+ * Audit breadcrumb explaining systemic action-array adjustments. Present only when at least one adjustment was made (e.g., health finding suppression hints omitted because a baseline is active). When --group-by is active, each entry of `groups` may carry its own `actions_meta` describing the same omission so per-group consumers do not need to walk back to the report root.
1087
+ */
1088
+ actions_meta?: {
1089
+ /**
1090
+ * True when `suppress-line` actions were not emitted on health findings.
1091
+ */
1092
+ suppression_hints_omitted: boolean
1093
+ /**
1094
+ * Why suppression hints were omitted. `baseline-active` = `--baseline` or `--save-baseline` was passed; `config-disabled` = `health.suggestInlineSuppression: false`; `unspecified` = caller omitted hints without setting a reason.
1095
+ */
1096
+ reason: string
1097
+ /**
1098
+ * The report section affected by this metadata. Other health sections can still carry their own suppress actions.
1099
+ */
1100
+ scope: "health-findings"
1101
+ }
1102
+ _meta?: Meta
1103
+ }
1104
+ export interface HealthSummary {
1105
+ /**
1106
+ * Number of files analyzed for complexity.
1107
+ */
1108
+ files_analyzed: number
1109
+ /**
1110
+ * Total number of functions found.
1111
+ */
1112
+ functions_analyzed: number
1113
+ /**
1114
+ * Number of functions exceeding at least one threshold (before --top truncation).
1115
+ */
1116
+ functions_above_threshold: number
1117
+ /**
1118
+ * Configured cyclomatic complexity threshold.
1119
+ */
1120
+ max_cyclomatic_threshold: number
1121
+ /**
1122
+ * Configured cognitive complexity threshold.
1123
+ */
1124
+ max_cognitive_threshold: number
1125
+ /**
1126
+ * Configured CRAP (Change Risk Anti-Patterns) score threshold. Functions meeting or exceeding this score appear as findings with the `crap` and optional `coverage_pct` fields populated.
1127
+ */
1128
+ max_crap_threshold: number
1129
+ /**
1130
+ * Number of files with health scores. Only present when --file-scores is used. 0 indicates the flag was set but scoring failed.
1131
+ */
1132
+ files_scored?: number
1133
+ /**
1134
+ * Average maintainability index across all scored files (before --top truncation). Only present when --file-scores is used and at least one file was scored.
1135
+ */
1136
+ average_maintainability?: number
1137
+ /**
1138
+ * Coverage model used for CRAP score computation. 'static_estimated' (default) uses per-function graph-based estimation from export references: directly test-referenced = 85%, indirectly reachable = 40%, untested = 0%. 'istanbul' uses real per-function statement coverage from a coverage-final.json file (--coverage flag or auto-detected). 'static_binary' is the legacy binary model. Only present when file scores are computed.
1139
+ */
1140
+ coverage_model?: ("static_binary" | "static_estimated" | "istanbul")
1141
+ /**
1142
+ * Number of functions matched against Istanbul coverage data. Only present when coverage_model is 'istanbul'.
1143
+ */
1144
+ istanbul_matched?: number
1145
+ /**
1146
+ * Total functions evaluated for Istanbul matching. Only present when coverage_model is 'istanbul'.
1147
+ */
1148
+ istanbul_total?: number
1149
+ /**
1150
+ * Number of findings with critical severity (cognitive >= 40 or cyclomatic >= 50).
1151
+ */
1152
+ severity_critical_count: number
1153
+ /**
1154
+ * Number of findings with high severity (cognitive 25-39 or cyclomatic 30-49).
1155
+ */
1156
+ severity_high_count: number
1157
+ /**
1158
+ * Number of findings with moderate severity (below high thresholds).
1159
+ */
1160
+ severity_moderate_count: number
1161
+ }
1162
+ export interface HealthFinding {
1163
+ /**
1164
+ * File path (relative to project root).
1165
+ */
1166
+ path: string
1167
+ /**
1168
+ * Function name, "<anonymous>" for unnamed functions/arrows, or "<template>" for synthetic Angular template findings.
1169
+ */
1170
+ name: string
1171
+ /**
1172
+ * 1-based line number.
1173
+ */
1174
+ line: number
1175
+ /**
1176
+ * 0-based byte column offset.
1177
+ */
1178
+ col: number
1179
+ /**
1180
+ * McCabe cyclomatic complexity (1 + decision points).
1181
+ */
1182
+ cyclomatic: number
1183
+ /**
1184
+ * SonarSource cognitive complexity (structural + nesting penalty).
1185
+ */
1186
+ cognitive: number
1187
+ /**
1188
+ * Number of lines in the function body.
1189
+ */
1190
+ line_count: number
1191
+ /**
1192
+ * Number of parameters (excluding TypeScript's this parameter).
1193
+ */
1194
+ param_count: number
1195
+ /**
1196
+ * Which threshold(s) this finding exceeds. `crap` and its combinations are emitted when `max_crap_threshold` is crossed.
1197
+ */
1198
+ exceeded: ("cyclomatic" | "cognitive" | "both" | "crap" | "cyclomatic_crap" | "cognitive_crap" | "all")
1199
+ /**
1200
+ * CRAP score (rounded to one decimal). Present only when the function exceeded the CRAP threshold; absent otherwise.
1201
+ */
1202
+ crap?: number
1203
+ /**
1204
+ * Per-function statement coverage percentage (0.0 to 100.0) used to derive `crap`. Present only when Istanbul coverage data matched the function; absent for the estimated model or unmatched functions.
1205
+ */
1206
+ coverage_pct?: number
1207
+ /**
1208
+ * Bucketed coverage tier used to drive action selection. Present whenever CRAP triggered the finding (Istanbul or estimated), absent otherwise. `none` = coverage is at most 0% (file not test-reachable, or Istanbul reports 0); `partial` = coverage is in `(0, 70)`; `high` = coverage is at or above the high watermark (default `>= 70`, or the estimated 85% band).
1209
+ */
1210
+ coverage_tier?: ("none" | "partial" | "high")
1211
+ /**
1212
+ * How far above the threshold: moderate (just above), high (recommended for extraction), or critical (immediate extraction candidate). Defaults: cognitive 25/40, cyclomatic 30/50.
1213
+ */
1214
+ severity: ("moderate" | "high" | "critical")
1215
+ /**
1216
+ * Suggested actions to address this complex finding.
1217
+ */
1218
+ actions?: HealthFindingAction[]
1219
+ introduced?: AuditIntroduced
1220
+ }
1221
+ /**
1222
+ * A suggested action for a health complexity finding.
1223
+ */
1224
+ export interface HealthFindingAction {
1225
+ /**
1226
+ * Action type identifier. A single finding's `actions` array can carry MULTIPLE entries of different types: e.g., a finding that exceeded both cyclomatic and CRAP at `coverage_tier`: partial will get BOTH `increase-coverage` AND `refactor-function`, plus `suppress-line`. Consumers that select a single action should treat the FIRST non-`suppress-{line,file}` action as primary. `add-tests` is emitted when CRAP triggered the finding, the function has no test coverage (`coverage_tier`: none), and full coverage can bring CRAP below `max_crap_threshold` (cyclomatic < threshold, since CRAP bottoms out at CC at 100% coverage). `increase-coverage` is emitted when CRAP triggered the finding, some coverage exists (`coverage_tier`: partial or high), and full coverage can bring CRAP below `max_crap_threshold`; the description steers toward targeted branch coverage rather than scaffolding new tests. `refactor-function` is emitted when cyclomatic/cognitive triggered the finding, when full coverage still cannot bring CRAP below `max_crap_threshold` (cyclomatic >= threshold), or as a secondary action when cyclomatic is within 5 of the cyclomatic threshold AND cognitive is at or above `max_cognitive_threshold / 2` (the cognitive floor suppresses false positives on flat type-tag dispatchers and JSX render maps where high cyclomatic comes from a single switch with near-zero cognitive load). `suppress-file` is emitted instead of `suppress-line` for synthetic Angular `<template>` findings on `.html` files, because line-suppression comments cannot be expressed in HTML; the `comment` field carries `<!-- fallow-ignore-file complexity -->` and `placement` is `top-of-template`.
1227
+ */
1228
+ type: ("refactor-function" | "add-tests" | "increase-coverage" | "suppress-file" | "suppress-line")
1229
+ /**
1230
+ * Whether fallow can auto-fix this action.
1231
+ */
1232
+ auto_fixable: boolean
1233
+ /**
1234
+ * Human-readable description of the action.
1235
+ */
1236
+ description: string
1237
+ /**
1238
+ * Additional context for the action.
1239
+ */
1240
+ note?: string
1241
+ /**
1242
+ * The inline comment to insert (e.g., '// fallow-ignore-next-line complexity' or '<!-- fallow-ignore-file complexity -->'). Present for suppress actions.
1243
+ */
1244
+ comment?: string
1245
+ /**
1246
+ * Where to insert the suppress comment (e.g., 'above-function-declaration', 'above-angular-decorator', or 'top-of-template'). Present for suppress actions.
1247
+ */
1248
+ placement?: string
1249
+ }
1250
+ /**
1251
+ * Per-file health score. Formula: MI = 100 - (complexity_density x 30) - (dead_code_ratio x 20) - min(ln(fan_out+1) x 4, 15), clamped to [0, 100]. Type-only exports excluded from dead_code_ratio. CRAP scores combine cyclomatic complexity with static test reachability.
1252
+ */
1253
+ export interface FileHealthScore {
1254
+ /**
1255
+ * File path (relative to project root).
1256
+ */
1257
+ path: string
1258
+ /**
1259
+ * Number of files that import this file.
1260
+ */
1261
+ fan_in: number
1262
+ /**
1263
+ * Number of files this file directly imports.
1264
+ */
1265
+ fan_out: number
1266
+ /**
1267
+ * Fraction of value exports (excluding type-only) with zero references (0.0–1.0).
1268
+ */
1269
+ dead_code_ratio: number
1270
+ /**
1271
+ * Total cyclomatic complexity / lines of code.
1272
+ */
1273
+ complexity_density: number
1274
+ /**
1275
+ * Weighted composite score (0–100, higher is better).
1276
+ */
1277
+ maintainability_index: number
1278
+ /**
1279
+ * Sum of cyclomatic complexity across all functions in this file.
1280
+ */
1281
+ total_cyclomatic: number
1282
+ /**
1283
+ * Sum of cognitive complexity across all functions in this file.
1284
+ */
1285
+ total_cognitive: number
1286
+ /**
1287
+ * Number of functions in this file.
1288
+ */
1289
+ function_count: number
1290
+ /**
1291
+ * Total lines of code in the file.
1292
+ */
1293
+ lines: number
1294
+ /**
1295
+ * Maximum CRAP score among functions in this file. Computed via the active `coverage_model` per the canonical formula CC^2 * (1 - cov/100)^3 + CC (Savoia & Evans, 2007). Coverage source: `static_estimated` (default, graph-based per-function estimate), `istanbul` (real per-function statement coverage from --coverage), or the legacy `static_binary` (whole-file 0%/100%, retained for compatibility).
1296
+ */
1297
+ crap_max: number
1298
+ /**
1299
+ * Count of functions with CRAP score >= 30 (CC >= 5 without test dependency path).
1300
+ */
1301
+ crap_above_threshold: number
1302
+ }
1303
+ /**
1304
+ * A hotspot: a file that is both complex and frequently changing. Score = normalized_churn × normalized_complexity × 100 (0–100, higher = riskier).
1305
+ */
1306
+ export interface HotspotEntry {
1307
+ /**
1308
+ * File path (relative to project root).
1309
+ */
1310
+ path: string
1311
+ /**
1312
+ * Hotspot score (0–100). Higher means more risk.
1313
+ */
1314
+ score: number
1315
+ /**
1316
+ * Number of commits touching this file in the analysis window.
1317
+ */
1318
+ commits: number
1319
+ /**
1320
+ * Recency-weighted commit count (exponential decay, half-life 90 days).
1321
+ */
1322
+ weighted_commits: number
1323
+ /**
1324
+ * Total lines added across all commits in the window.
1325
+ */
1326
+ lines_added: number
1327
+ /**
1328
+ * Total lines deleted across all commits in the window.
1329
+ */
1330
+ lines_deleted: number
1331
+ /**
1332
+ * Total cyclomatic complexity / lines of code.
1333
+ */
1334
+ complexity_density: number
1335
+ /**
1336
+ * Number of files that import this file (blast radius).
1337
+ */
1338
+ fan_in: number
1339
+ /**
1340
+ * Churn trend: accelerating (recent > 1.5× older), stable, or cooling (recent < 0.67× older).
1341
+ */
1342
+ trend: ("accelerating" | "stable" | "cooling")
1343
+ /**
1344
+ * Suggested actions to reduce hotspot risk.
1345
+ */
1346
+ actions?: HotspotAction[]
1347
+ ownership?: OwnershipMetrics
1348
+ }
1349
+ /**
1350
+ * A suggested action for a hotspot file. Ownership-derived action types (low-bus-factor, unowned-hotspot, ownership-drift) appear only when --ownership is enabled.
1351
+ */
1352
+ export interface HotspotAction {
1353
+ /**
1354
+ * Action type identifier.
1355
+ */
1356
+ type: ("refactor-file" | "add-tests" | "low-bus-factor" | "unowned-hotspot" | "ownership-drift")
1357
+ /**
1358
+ * Whether fallow can auto-fix this action.
1359
+ */
1360
+ auto_fixable: boolean
1361
+ /**
1362
+ * Human-readable description of the action.
1363
+ */
1364
+ description: string
1365
+ /**
1366
+ * Additional context for the action.
1367
+ */
1368
+ note?: string
1369
+ /**
1370
+ * Suggested CODEOWNERS pattern. Only present on unowned-hotspot actions. Derived per the heuristic field; consumers should branch on heuristic rather than assume a stable algorithm.
1371
+ */
1372
+ suggested_pattern?: string
1373
+ /**
1374
+ * Strategy used to derive suggested_pattern. Reserved for future evolution (e.g., 'codeowners-cluster').
1375
+ */
1376
+ heuristic?: "directory-deepest"
1377
+ }
1378
+ /**
1379
+ * Per-file ownership signals derived from git author history and CODEOWNERS. Only computed when --ownership is requested.
1380
+ */
1381
+ export interface OwnershipMetrics {
1382
+ /**
1383
+ * Avelino truck factor: minimum contributors covering at least 50% of recency-weighted commits in the analysis window. Lower = higher knowledge-loss risk.
1384
+ */
1385
+ bus_factor: number
1386
+ /**
1387
+ * Distinct authors after bot filtering.
1388
+ */
1389
+ contributor_count: number
1390
+ top_contributor: ContributorEntry
1391
+ /**
1392
+ * Up to three additional contributors by share, ordered descending. Useful for review routing.
1393
+ */
1394
+ recent_contributors?: ContributorEntry[]
1395
+ /**
1396
+ * CODEOWNERS-resolved primary owner for this file, when a rule matches.
1397
+ */
1398
+ declared_owner?: (string | null)
1399
+ /**
1400
+ * Tristate: true = CODEOWNERS file exists but no rule matches; false = a rule matches; null = no CODEOWNERS file discovered for the repository.
1401
+ */
1402
+ unowned: (boolean | null)
1403
+ /**
1404
+ * True when ownership has drifted from the original author to a new top contributor (file age >= 30 days, original author share < 10%).
1405
+ */
1406
+ drift: boolean
1407
+ /**
1408
+ * Human-readable explanation of the drift. Field is omitted (not null) when drift is false.
1409
+ */
1410
+ drift_reason?: string
1411
+ }
1412
+ /**
1413
+ * Per-author contribution summary. The identifier is rendered per the configured ownership.emailMode (handle, hash, or raw); the format field discriminates the three so type-aware consumers can branch without re-parsing.
1414
+ */
1415
+ export interface ContributorEntry {
1416
+ /**
1417
+ * Display string for the contributor. Raw email when format=raw (e.g. alice@example.com), local-part handle when format=handle (e.g. alice), or non-cryptographic pseudonym when format=hash (e.g. xxh3:abcdef0123456789). Do not assume this is an email address.
1418
+ */
1419
+ identifier: string
1420
+ /**
1421
+ * Discriminator for identifier shape. Set by ownership.emailMode at analysis time.
1422
+ */
1423
+ format: ("raw" | "handle" | "hash")
1424
+ /**
1425
+ * Recency-weighted share of total weighted commits, rounded to three decimals.
1426
+ */
1427
+ share: number
1428
+ /**
1429
+ * Days since this contributor last touched the file.
1430
+ */
1431
+ stale_days: number
1432
+ /**
1433
+ * Total commits by this contributor in the analysis window.
1434
+ */
1435
+ commits: number
1436
+ }
1437
+ export interface HotspotSummary {
1438
+ /**
1439
+ * Analysis window display string (e.g., '6 months').
1440
+ */
1441
+ since: string
1442
+ /**
1443
+ * Minimum commits threshold.
1444
+ */
1445
+ min_commits: number
1446
+ /**
1447
+ * Number of files with churn data meeting the threshold.
1448
+ */
1449
+ files_analyzed: number
1450
+ /**
1451
+ * Number of files excluded (below min_commits).
1452
+ */
1453
+ files_excluded: number
1454
+ /**
1455
+ * Whether the repository is a shallow clone.
1456
+ */
1457
+ shallow_clone: boolean
1458
+ }
1459
+ /**
1460
+ * Project-wide vital signs metrics. Optional fields are null when the corresponding analysis was not run.
1461
+ */
1462
+ export interface VitalSigns {
1463
+ /**
1464
+ * Percentage of unreachable files.
1465
+ */
1466
+ dead_file_pct?: (number | null)
1467
+ /**
1468
+ * Percentage of unused exports.
1469
+ */
1470
+ dead_export_pct?: (number | null)
1471
+ /**
1472
+ * Average cyclomatic complexity.
1473
+ */
1474
+ avg_cyclomatic: number
1475
+ /**
1476
+ * Percentage of functions at or above the critical cyclomatic threshold. Used by the scale-invariant health score.
1477
+ */
1478
+ critical_complexity_pct?: (number | null)
1479
+ /**
1480
+ * 90th percentile cyclomatic complexity.
1481
+ */
1482
+ p90_cyclomatic: number
1483
+ /**
1484
+ * Code duplication percentage. Null if duplication pipeline was not run.
1485
+ */
1486
+ duplication_pct?: (number | null)
1487
+ /**
1488
+ * Files with hotspot score >= 50. Null if git history unavailable.
1489
+ */
1490
+ hotspot_count?: (number | null)
1491
+ /**
1492
+ * Files in the top 1% of the within-project hotspot ranking. Null if git history unavailable.
1493
+ */
1494
+ hotspot_top_pct_count?: (number | null)
1495
+ /**
1496
+ * Average maintainability index (0-100).
1497
+ */
1498
+ maintainability_avg?: (number | null)
1499
+ /**
1500
+ * Percentage of scored files with maintainability index below 70. Null if file scores were not computed.
1501
+ */
1502
+ maintainability_low_pct?: (number | null)
1503
+ /**
1504
+ * Number of unused dependencies.
1505
+ */
1506
+ unused_dep_count?: (number | null)
1507
+ /**
1508
+ * Unused dependencies per 1,000 files. Null if dead code analysis did not run.
1509
+ */
1510
+ unused_deps_per_k_files?: (number | null)
1511
+ /**
1512
+ * Number of circular dependency chains.
1513
+ */
1514
+ circular_dep_count?: (number | null)
1515
+ /**
1516
+ * Circular dependency chains per 1,000 files. Null if dead code analysis did not run.
1517
+ */
1518
+ circular_deps_per_k_files?: (number | null)
1519
+ counts?: VitalSignsCounts
1520
+ unit_size_profile?: RiskProfile
1521
+ /**
1522
+ * Functions above 60 LOC per 1,000 functions. Null if no functions analyzed.
1523
+ */
1524
+ functions_over_60_loc_per_k?: (number | null)
1525
+ unit_interfacing_profile?: RiskProfile
1526
+ /**
1527
+ * 95th percentile fan-in across all files. Null if file scores not computed.
1528
+ */
1529
+ p95_fan_in?: (number | null)
1530
+ /**
1531
+ * Percentage of files with fan-in above the effective threshold. Null if file scores not computed.
1532
+ */
1533
+ coupling_high_pct?: (number | null)
1534
+ /**
1535
+ * Total lines of code across all parsed modules. 0 if no modules parsed.
1536
+ */
1537
+ total_loc?: number
1538
+ }
1539
+ /**
1540
+ * Raw counts behind the vital signs percentages.
1541
+ */
1542
+ export interface VitalSignsCounts {
1543
+ /**
1544
+ * Total number of discovered source files.
1545
+ */
1546
+ total_files?: number
1547
+ /**
1548
+ * Total number of exports across all files.
1549
+ */
1550
+ total_exports?: number
1551
+ /**
1552
+ * Number of unreachable files.
1553
+ */
1554
+ dead_files?: number
1555
+ /**
1556
+ * Number of unused exports.
1557
+ */
1558
+ dead_exports?: number
1559
+ }
1560
+ /**
1561
+ * Risk profile: percentage of functions in each risk bin. Bins depend on the measured property (unit size or parameter count).
1562
+ */
1563
+ export interface RiskProfile {
1564
+ /**
1565
+ * Percentage of functions in the low-risk bin.
1566
+ */
1567
+ low_risk: number
1568
+ /**
1569
+ * Percentage of functions in the medium-risk bin.
1570
+ */
1571
+ medium_risk: number
1572
+ /**
1573
+ * Percentage of functions in the high-risk bin.
1574
+ */
1575
+ high_risk: number
1576
+ /**
1577
+ * Percentage of functions in the very-high-risk bin.
1578
+ */
1579
+ very_high_risk: number
1580
+ }
1581
+ /**
1582
+ * Project-level health score. Score = 100 minus available penalties from dead code, complexity, maintainability, hotspots, unused deps, circular deps, unit size, coupling, and duplication. Missing metrics do not penalize; --score computes the score and duplication penalty, while churn-backed hotspot penalties require hotspot analysis (--hotspots, or --targets with --score).
1583
+ */
1584
+ export interface HealthScore {
1585
+ /**
1586
+ * Health score formula version. Version 2 uses scale-invariant density/tail metrics for monorepo-safe scoring.
1587
+ */
1588
+ formula_version: number
1589
+ /**
1590
+ * Overall score (0-100, higher is better). Reproducible: 100 - sum(penalties) == score.
1591
+ */
1592
+ score: number
1593
+ /**
1594
+ * Letter grade. A: score >= 85, B: 70-84, C: 55-69, D: 40-54, F: below 40.
1595
+ */
1596
+ grade: ("A" | "B" | "C" | "D" | "F")
1597
+ penalties: HealthScorePenalties
1598
+ }
1599
+ /**
1600
+ * Per-component penalty breakdown. Each field shows points subtracted. None/absent means the metric was not available (pipeline didn't run). Sum of all penalties = 100 - score.
1601
+ */
1602
+ export interface HealthScorePenalties {
1603
+ /**
1604
+ * Points lost from dead files (max 15). Null if dead code pipeline not run.
1605
+ */
1606
+ dead_files?: (number | null)
1607
+ /**
1608
+ * Points lost from dead exports (max 15). Null if dead code pipeline not run.
1609
+ */
1610
+ dead_exports?: (number | null)
1611
+ /**
1612
+ * Points lost from critical-complexity density (max 20). Older snapshots without density fields fall back to average cyclomatic complexity above 1.5.
1613
+ */
1614
+ complexity: number
1615
+ /**
1616
+ * Points lost from legacy p90 cyclomatic complexity above 10. Current scale-invariant runs report 0 because tail complexity is folded into complexity.
1617
+ */
1618
+ p90_complexity: number
1619
+ /**
1620
+ * Points lost from low maintainability density (max 15). Null if file-scores not computed.
1621
+ */
1622
+ maintainability?: (number | null)
1623
+ /**
1624
+ * Points lost from top-percentile hotspot density (max 10). Null if hotspots not computed.
1625
+ */
1626
+ hotspots?: (number | null)
1627
+ /**
1628
+ * Points lost from unused dependency density (max 25). Null if dead code pipeline not run.
1629
+ */
1630
+ unused_deps?: (number | null)
1631
+ /**
1632
+ * Points lost from circular dependency density (max 25). Null if dead code pipeline not run.
1633
+ */
1634
+ circular_deps?: (number | null)
1635
+ /**
1636
+ * Points lost from oversized-function density (max 10). Null if no functions analyzed.
1637
+ */
1638
+ unit_size?: (number | null)
1639
+ /**
1640
+ * Points lost from coupling concentration density (max 5). Null if file scores not computed.
1641
+ */
1642
+ coupling?: (number | null)
1643
+ /**
1644
+ * Points lost from code duplication (max 10). Penalty = min(max(0, duplication_pct - 5) * 1, 10). Null if duplication pipeline not run.
1645
+ */
1646
+ duplication?: (number | null)
1647
+ }
1648
+ /**
1649
+ * A function exceeding 60 lines of code (very high risk size bin).
1650
+ */
1651
+ export interface LargeFunctionEntry {
1652
+ /**
1653
+ * File path (relative to project root).
1654
+ */
1655
+ path: string
1656
+ /**
1657
+ * Function name, or "<anonymous>" for unnamed functions/arrows.
1658
+ */
1659
+ name: string
1660
+ /**
1661
+ * 1-based line number.
1662
+ */
1663
+ line: number
1664
+ /**
1665
+ * Number of lines in the function body.
1666
+ */
1667
+ line_count: number
1668
+ }
1669
+ export interface RefactoringTarget {
1670
+ /**
1671
+ * File path (relative to project root).
1672
+ */
1673
+ path: string
1674
+ /**
1675
+ * Weighted priority score (0-100). Higher = more urgent.
1676
+ */
1677
+ priority: number
1678
+ /**
1679
+ * priority / effort_numeric (Low=1, Medium=2, High=3). Default sort order.
1680
+ */
1681
+ efficiency: number
1682
+ /**
1683
+ * Human-readable one-line recommendation.
1684
+ */
1685
+ recommendation: string
1686
+ /**
1687
+ * Recommendation category.
1688
+ */
1689
+ category: ("urgent_churn_complexity" | "break_circular_dependency" | "split_high_impact" | "remove_dead_code" | "extract_complex_functions" | "extract_dependencies")
1690
+ /**
1691
+ * Heuristic effort estimate.
1692
+ */
1693
+ effort: ("low" | "medium" | "high")
1694
+ /**
1695
+ * Data source reliability. High = deterministic analysis, Medium = heuristic, Low = git-dependent.
1696
+ */
1697
+ confidence: ("high" | "medium" | "low")
1698
+ /**
1699
+ * Contributing factors that triggered this recommendation. Empty array omitted from JSON.
1700
+ */
1701
+ factors?: {
1702
+ /**
1703
+ * Metric name (fan_in, dead_code_ratio, etc.).
1704
+ */
1705
+ metric: string
1706
+ /**
1707
+ * Raw metric value.
1708
+ */
1709
+ value: number
1710
+ /**
1711
+ * Threshold that was exceeded.
1712
+ */
1713
+ threshold: number
1714
+ /**
1715
+ * Human-readable explanation.
1716
+ */
1717
+ detail: string
1718
+ }[]
1719
+ /**
1720
+ * Structured evidence for supported categories. Omitted when not applicable.
1721
+ */
1722
+ evidence?: {
1723
+ /**
1724
+ * Names of unused exports (for remove_dead_code).
1725
+ */
1726
+ unused_exports?: string[]
1727
+ /**
1728
+ * Complex functions with line numbers (for extract_complex_functions).
1729
+ */
1730
+ complex_functions?: {
1731
+ name?: string
1732
+ line?: number
1733
+ cognitive?: number
1734
+ }[]
1735
+ /**
1736
+ * Files in the import cycle (for break_circular_dependency).
1737
+ */
1738
+ cycle_path?: string[]
1739
+ }
1740
+ /**
1741
+ * Suggested actions for this refactoring target.
1742
+ */
1743
+ actions?: RefactoringTargetAction[]
1744
+ }
1745
+ /**
1746
+ * A suggested action for a refactoring target.
1747
+ */
1748
+ export interface RefactoringTargetAction {
1749
+ /**
1750
+ * Action type identifier.
1751
+ */
1752
+ type: ("apply-refactoring" | "suppress-line")
1753
+ /**
1754
+ * Whether fallow can auto-fix this action.
1755
+ */
1756
+ auto_fixable: boolean
1757
+ /**
1758
+ * Human-readable description of the action.
1759
+ */
1760
+ description: string
1761
+ /**
1762
+ * The recommendation category for apply-refactoring actions.
1763
+ */
1764
+ category?: string
1765
+ /**
1766
+ * The inline comment to insert. Present for suppress-line actions when evidence exists.
1767
+ */
1768
+ comment?: string
1769
+ /**
1770
+ * Where to insert the suppress comment. Present for suppress-line actions.
1771
+ */
1772
+ placement?: string
1773
+ }
1774
+ /**
1775
+ * Adaptive percentile-based thresholds derived from the project's metric distribution. Used for priority scoring and rule matching.
1776
+ */
1777
+ export interface TargetThresholds {
1778
+ /**
1779
+ * Fan-in saturation for priority formula (p95, floor 5).
1780
+ */
1781
+ fan_in_p95: number
1782
+ /**
1783
+ * Fan-in moderate threshold for contributing factors (p75, floor 3).
1784
+ */
1785
+ fan_in_p75: number
1786
+ /**
1787
+ * Fan-out saturation for priority formula (p95, floor 8).
1788
+ */
1789
+ fan_out_p95: number
1790
+ /**
1791
+ * Fan-out high threshold for rules and factors (p90, floor 5).
1792
+ */
1793
+ fan_out_p90: number
1794
+ }
1795
+ /**
1796
+ * Trend comparison between the current run and a previous snapshot. Shows per-metric deltas with directional indicators.
1797
+ */
1798
+ export interface HealthTrend {
1799
+ /**
1800
+ * The snapshot being compared against.
1801
+ */
1802
+ compared_to: {
1803
+ /**
1804
+ * ISO 8601 timestamp of the snapshot.
1805
+ */
1806
+ timestamp: string
1807
+ /**
1808
+ * Git SHA at time of snapshot.
1809
+ */
1810
+ git_sha?: string
1811
+ /**
1812
+ * Health score from the snapshot (stored, not re-derived).
1813
+ */
1814
+ score?: number
1815
+ /**
1816
+ * Letter grade from the snapshot.
1817
+ */
1818
+ grade?: string
1819
+ }
1820
+ metrics: {
1821
+ /**
1822
+ * Metric identifier (e.g., 'score', 'dead_file_pct').
1823
+ */
1824
+ name: string
1825
+ /**
1826
+ * Human-readable label.
1827
+ */
1828
+ label: string
1829
+ /**
1830
+ * Previous value (from snapshot).
1831
+ */
1832
+ previous: number
1833
+ /**
1834
+ * Current value (from this run).
1835
+ */
1836
+ current: number
1837
+ /**
1838
+ * Absolute change (current - previous).
1839
+ */
1840
+ delta: number
1841
+ direction: ("improving" | "declining" | "stable")
1842
+ /**
1843
+ * Unit for display (e.g., '%', '').
1844
+ */
1845
+ unit: string
1846
+ previous_count?: TrendCount
1847
+ current_count?: TrendCount
1848
+ }[]
1849
+ /**
1850
+ * Number of snapshots found in the snapshot directory.
1851
+ */
1852
+ snapshots_loaded: number
1853
+ overall_direction: ("improving" | "declining" | "stable")
1854
+ }
1855
+ /**
1856
+ * Raw numerator/denominator for a percentage metric in trend comparison.
1857
+ */
1858
+ export interface TrendCount {
1859
+ /**
1860
+ * The numerator (e.g., dead files count).
1861
+ */
1862
+ value: number
1863
+ /**
1864
+ * The denominator (e.g., total files).
1865
+ */
1866
+ total: number
1867
+ }
1868
+ /**
1869
+ * Static test coverage gaps derived from the module graph. Shows runtime files and exports with no test dependency path.
1870
+ */
1871
+ export interface CoverageGaps {
1872
+ summary: CoverageGapSummary
1873
+ /**
1874
+ * Runtime files with no test dependency path. Omitted when empty.
1875
+ */
1876
+ files?: UntestedFile[]
1877
+ /**
1878
+ * Runtime exports with no test-reachable reference chain. Omitted when empty.
1879
+ */
1880
+ exports?: UntestedExport[]
1881
+ }
1882
+ /**
1883
+ * Aggregate coverage-gap counters for the current analysis scope.
1884
+ */
1885
+ export interface CoverageGapSummary {
1886
+ /**
1887
+ * Runtime-reachable files in scope.
1888
+ */
1889
+ runtime_files: number
1890
+ /**
1891
+ * Runtime-reachable files also reachable from tests.
1892
+ */
1893
+ covered_files: number
1894
+ /**
1895
+ * Percentage of runtime files that are test-reachable.
1896
+ */
1897
+ file_coverage_pct: number
1898
+ /**
1899
+ * Runtime files with no test dependency path.
1900
+ */
1901
+ untested_files: number
1902
+ /**
1903
+ * Runtime exports with no test-reachable reference chain.
1904
+ */
1905
+ untested_exports: number
1906
+ }
1907
+ /**
1908
+ * A runtime file with no test dependency path.
1909
+ */
1910
+ export interface UntestedFile {
1911
+ /**
1912
+ * File path (relative to project root).
1913
+ */
1914
+ path: string
1915
+ /**
1916
+ * Number of value exports declared by the file.
1917
+ */
1918
+ value_export_count: number
1919
+ }
1920
+ /**
1921
+ * A runtime export that no test-reachable module references.
1922
+ */
1923
+ export interface UntestedExport {
1924
+ /**
1925
+ * File path (relative to project root).
1926
+ */
1927
+ path: string
1928
+ /**
1929
+ * Export name.
1930
+ */
1931
+ export_name: string
1932
+ /**
1933
+ * 1-based source line.
1934
+ */
1935
+ line: number
1936
+ /**
1937
+ * 0-based source column.
1938
+ */
1939
+ col: number
1940
+ }
1941
+ /**
1942
+ * Runtime coverage findings merged into the health report or emitted by `fallow coverage analyze`. Present in health output when --runtime-coverage is used. Shape mirrors the runtime coverage JSON contract; cloud mode fetches runtime facts explicitly and merges them locally with AST/static analysis.
1943
+ */
1944
+ export interface RuntimeCoverageReport {
1945
+ /**
1946
+ * Runtime coverage JSON contract version. This is scoped to the runtime_coverage block and is independent of the top-level fallow JSON schema_version.
1947
+ */
1948
+ schema_version: "1"
1949
+ verdict: RuntimeCoverageReportVerdict
1950
+ /**
1951
+ * Every runtime-coverage signal the report carries, severity-descending. Independent of `verdict`. Empty when `verdict` is `clean` and there is no license grace. Used by JSON / SARIF / MCP consumers that want the complete set rather than the headline.
1952
+ */
1953
+ signals?: RuntimeCoverageSignal[]
1954
+ summary: RuntimeCoverageSummary
1955
+ /**
1956
+ * Surfaced runtime coverage findings (`safe_to_delete`, `review_required`, `low_traffic`, `coverage_unavailable`). Omitted when empty. `active` functions stay out of this list so the CLI output remains actionable.
1957
+ */
1958
+ findings?: RuntimeCoverageFinding[]
1959
+ /**
1960
+ * Top runtime functions by invocation count. Omitted when empty.
1961
+ */
1962
+ hot_paths?: RuntimeCoverageHotPath[]
1963
+ /**
1964
+ * First-class blast-radius entries for runtime-observed functions. Present whenever runtime coverage analysis runs.
1965
+ */
1966
+ blast_radius?: RuntimeCoverageBlastRadiusEntry[]
1967
+ /**
1968
+ * First-class production-importance entries for runtime-observed functions. Present whenever runtime coverage analysis runs.
1969
+ */
1970
+ importance?: RuntimeCoverageImportanceEntry[]
1971
+ watermark?: RuntimeCoverageWatermark
1972
+ /**
1973
+ * Non-fatal merge or coverage diagnostics. Omitted when empty.
1974
+ */
1975
+ warnings?: RuntimeCoverageMessage[]
1976
+ }
1977
+ export interface RuntimeCoverageSummary {
1978
+ /**
1979
+ * Runtime evidence source used for this report. Local mode reads a supplied runtime coverage artifact; cloud mode pulls the latest fallow.cloud runtime context after explicit opt-in.
1980
+ */
1981
+ data_source: ("local" | "cloud")
1982
+ /**
1983
+ * Timestamp of the newest runtime payload included in the report. Null for local single-capture artifacts that do not carry cloud receipt metadata.
1984
+ */
1985
+ last_received_at: (string | null)
1986
+ /**
1987
+ * Number of functions the sidecar could observe in the V8 or Istanbul dump.
1988
+ */
1989
+ functions_tracked: number
1990
+ /**
1991
+ * Tracked functions that received at least one invocation.
1992
+ */
1993
+ functions_hit: number
1994
+ /**
1995
+ * Tracked functions that were never invoked.
1996
+ */
1997
+ functions_unhit: number
1998
+ /**
1999
+ * Functions the sidecar could not track (lazy-parsed, worker thread, dynamic code, unresolved source map).
2000
+ */
2001
+ functions_untracked: number
2002
+ /**
2003
+ * Ratio of functions_hit / functions_tracked, expressed as a percent.
2004
+ */
2005
+ coverage_percent: number
2006
+ /**
2007
+ * Total number of observed invocations across all functions. Denominator for low-traffic classification.
2008
+ */
2009
+ trace_count: number
2010
+ /**
2011
+ * Days of observation covered by the supplied dump (Phase 2 local analysis emits 0 — set by the beacon/cloud in Phase 3+).
2012
+ */
2013
+ period_days: number
2014
+ /**
2015
+ * Distinct deployments contributing to the supplied dump (Phase 2 local analysis emits 0).
2016
+ */
2017
+ deployments_seen: number
2018
+ capture_quality?: RuntimeCoverageCaptureQuality
2019
+ }
2020
+ /**
2021
+ * Quality-of-capture signals emitted by the sidecar so the CLI can explain short-window captures honestly instead of letting users blame the tool.
2022
+ */
2023
+ export interface RuntimeCoverageCaptureQuality {
2024
+ /**
2025
+ * Total observation window in seconds. Finer-grained than period_days (which rounds up to whole days).
2026
+ */
2027
+ window_seconds: number
2028
+ /**
2029
+ * Number of distinct production instances that contributed to the dump.
2030
+ */
2031
+ instances_observed: number
2032
+ /**
2033
+ * True when the untracked-function ratio exceeds the sidecar's lazy-parse threshold (30%). Signals that many untracked functions likely reflect lazy-parsed code rather than unreachable code.
2034
+ */
2035
+ lazy_parse_warning: boolean
2036
+ /**
2037
+ * functions_untracked / functions_tracked as a percentage, rounded to 2 decimal places.
2038
+ */
2039
+ untracked_ratio_percent: number
2040
+ }
2041
+ export interface RuntimeCoverageFinding {
2042
+ /**
2043
+ * Stable content-hash ID of the form `fallow:prod:<hash>`, where <hash> is the first 8 hex characters of SHA-256(file + function + line + 'prod').
2044
+ */
2045
+ id: string
2046
+ /**
2047
+ * File path relative to the project root.
2048
+ */
2049
+ path: string
2050
+ /**
2051
+ * Static function name as reported in the merged coverage result.
2052
+ */
2053
+ function: string
2054
+ /**
2055
+ * 1-indexed line number the function starts on.
2056
+ */
2057
+ line: number
2058
+ verdict: RuntimeCoverageVerdict
2059
+ /**
2060
+ * Observed invocation count for the function. Omitted when the function is untracked (lazy-parsed, worker-thread isolate, dynamic code).
2061
+ */
2062
+ invocations?: number
2063
+ confidence: RuntimeCoverageConfidence
2064
+ evidence: RuntimeCoverageEvidence
2065
+ /**
2066
+ * Suggested actions for this finding. Omitted when empty.
2067
+ */
2068
+ actions?: RuntimeCoverageAction[]
2069
+ }
2070
+ /**
2071
+ * Supporting evidence for a runtime coverage finding. Mirrors the rows of the decision table.
2072
+ */
2073
+ export interface RuntimeCoverageEvidence {
2074
+ /**
2075
+ * `used` when the function is reachable in the module graph, `unused` otherwise.
2076
+ */
2077
+ static_status: ("used" | "unused")
2078
+ /**
2079
+ * `covered` when the project's test suite hits this function, `not_covered` otherwise.
2080
+ */
2081
+ test_coverage: ("covered" | "not_covered")
2082
+ /**
2083
+ * `tracked` when V8 observed the function, `untracked` otherwise.
2084
+ */
2085
+ v8_tracking: ("tracked" | "untracked")
2086
+ /**
2087
+ * Reason the function is untracked. Populated only when v8_tracking is `untracked`. Values: `lazy_parsed`, `worker_thread`, `dynamic_eval`, `unknown`.
2088
+ */
2089
+ untracked_reason?: string
2090
+ /**
2091
+ * Days of observation backing this finding.
2092
+ */
2093
+ observation_days: number
2094
+ /**
2095
+ * Distinct deployments backing this finding.
2096
+ */
2097
+ deployments_observed: number
2098
+ }
2099
+ /**
2100
+ * Suggested follow-up action for a runtime coverage finding.
2101
+ */
2102
+ export interface RuntimeCoverageAction {
2103
+ /**
2104
+ * Action identifier, normalized to `type` in JSON output. Known values emitted by `fallow coverage analyze`: `delete-cold-code` (verdict=safe_to_delete), `review-runtime` (verdict=review_required). The sidecar may emit additional protocol-specific identifiers; consumers should treat unknown values as forward-compat extensions.
2105
+ */
2106
+ type: string
2107
+ /**
2108
+ * Human-readable recommendation.
2109
+ */
2110
+ description: string
2111
+ /**
2112
+ * Whether fallow can apply this action automatically.
2113
+ */
2114
+ auto_fixable: boolean
2115
+ }
2116
+ export interface RuntimeCoverageHotPath {
2117
+ /**
2118
+ * Stable content-hash ID of the form `fallow:hot:<hash>`.
2119
+ */
2120
+ id: string
2121
+ /**
2122
+ * File path relative to the project root.
2123
+ */
2124
+ path: string
2125
+ /**
2126
+ * Function name for the hot path.
2127
+ */
2128
+ function: string
2129
+ /**
2130
+ * 1-indexed line number the function starts on.
2131
+ */
2132
+ line: number
2133
+ /**
2134
+ * 1-indexed line the function ends on (inclusive). `0` indicates the value was unavailable (older protocol-0.4 sidecars), in which case consumers MUST treat the range as a single line at `line`.
2135
+ */
2136
+ end_line: number
2137
+ /**
2138
+ * Observed invocation count for the hot path.
2139
+ */
2140
+ invocations: number
2141
+ /**
2142
+ * Percentile rank over this response's hot-path distribution. 100 = busiest, 0 = quietest that still qualified.
2143
+ */
2144
+ percentile: number
2145
+ /**
2146
+ * Suggested actions for this hot path (e.g., review-on-change). Omitted when empty.
2147
+ */
2148
+ actions?: RuntimeCoverageAction[]
2149
+ }
2150
+ export interface RuntimeCoverageBlastRadiusEntry {
2151
+ /**
2152
+ * Stable content-hash ID of the form `fallow:blast:<hash>`.
2153
+ */
2154
+ id: string
2155
+ /**
2156
+ * File path relative to the project root.
2157
+ */
2158
+ file: string
2159
+ /**
2160
+ * Function name for the blast-radius entry.
2161
+ */
2162
+ function: string
2163
+ /**
2164
+ * 1-indexed line number the function starts on.
2165
+ */
2166
+ line: number
2167
+ /**
2168
+ * Static caller count from the module graph.
2169
+ */
2170
+ caller_count: number
2171
+ /**
2172
+ * Caller reach weighted by observed runtime traffic.
2173
+ */
2174
+ caller_count_weighted_by_traffic: number
2175
+ /**
2176
+ * Distinct deploy SHAs that touched the function in the observation window. Cloud mode only; omitted in local mode.
2177
+ */
2178
+ deploys_touched?: number
2179
+ risk_band: RuntimeCoverageRiskBand
2180
+ }
2181
+ export interface RuntimeCoverageImportanceEntry {
2182
+ /**
2183
+ * Stable content-hash ID of the form `fallow:importance:<hash>`.
2184
+ */
2185
+ id: string
2186
+ /**
2187
+ * File path relative to the project root.
2188
+ */
2189
+ file: string
2190
+ /**
2191
+ * Function name for the importance entry.
2192
+ */
2193
+ function: string
2194
+ /**
2195
+ * 1-indexed line number the function starts on.
2196
+ */
2197
+ line: number
2198
+ /**
2199
+ * Observed invocation count for this function.
2200
+ */
2201
+ invocations: number
2202
+ /**
2203
+ * Cyclomatic complexity from the static health pipeline.
2204
+ */
2205
+ cyclomatic: number
2206
+ /**
2207
+ * Number of CODEOWNERS owners matched for this file. Zero means no owner was resolved.
2208
+ */
2209
+ owner_count: number
2210
+ /**
2211
+ * 0-100 explainable score from log-scaled traffic, capped complexity weight, and ownership-risk weight.
2212
+ */
2213
+ importance_score: number
2214
+ /**
2215
+ * Templated one-sentence explanation for the score.
2216
+ */
2217
+ reason: string
2218
+ }
2219
+ export interface RuntimeCoverageMessage {
2220
+ /**
2221
+ * Stable warning code.
2222
+ */
2223
+ code: string
2224
+ /**
2225
+ * Human-readable warning message.
2226
+ */
2227
+ message: string
2228
+ }
2229
+ /**
2230
+ * A health report scoped to a single resolver bucket (workspace package, CODEOWNERS owner, directory, or GitLab CODEOWNERS section). Carries per-group vital_signs / health_score plus the per-file output (findings, file scores, hotspots, large functions, refactoring targets) restricted to the same subset.
2231
+ */
2232
+ export interface HealthGroup {
2233
+ /**
2234
+ * Group identifier produced by the resolver. For 'package' grouping: workspace package name (e.g. '@scope/app-a') or '(root)' for files outside any workspace. For 'owner' grouping: the CODEOWNERS team. For 'directory' grouping: the top-level directory prefix. For 'section' grouping: the GitLab CODEOWNERS section name, or '(no section)' / '(unowned)' for unmatched files.
2235
+ */
2236
+ key: string
2237
+ /**
2238
+ * Section default owners (GitLab CODEOWNERS `[Section] @owner1 @owner2`). Present only when grouped_by is 'section'.
2239
+ */
2240
+ owners?: string[]
2241
+ /**
2242
+ * Number of files in this group after workspace, ignore, and changed-since filters.
2243
+ */
2244
+ files_analyzed: number
2245
+ /**
2246
+ * Number of findings rendered for this group (post-baseline / post-`--top` truncation, mirroring HealthSummary semantics).
2247
+ */
2248
+ functions_above_threshold: number
2249
+ vital_signs?: VitalSigns
2250
+ health_score?: HealthScore
2251
+ /**
2252
+ * Findings restricted to files in this group. Omitted when empty.
2253
+ */
2254
+ findings?: HealthFinding[]
2255
+ /**
2256
+ * File scores restricted to files in this group. Omitted when empty.
2257
+ */
2258
+ file_scores?: FileHealthScore[]
2259
+ /**
2260
+ * Hotspots restricted to files in this group. Omitted when empty.
2261
+ */
2262
+ hotspots?: HotspotEntry[]
2263
+ /**
2264
+ * Large functions in files belonging to this group. Omitted when empty.
2265
+ */
2266
+ large_functions?: LargeFunctionEntry[]
2267
+ /**
2268
+ * Refactoring targets in files belonging to this group. Omitted when empty.
2269
+ */
2270
+ targets?: RefactoringTarget[]
2271
+ }
2272
+ /**
2273
+ * Grouped dead code output produced when --group-by is active. Issues are partitioned into groups (by CODEOWNERS team, directory prefix, workspace package, or GitLab CODEOWNERS section) instead of flat arrays.
2274
+ */
2275
+ export interface CheckGroupedOutput {
2276
+ schema_version: SchemaVersion
2277
+ version: ToolVersion
2278
+ elapsed_ms: ElapsedMs
2279
+ /**
2280
+ * The grouping strategy used. 'owner' groups by CODEOWNERS team, 'directory' groups by top-level directory prefix, 'package' groups by workspace package name, 'section' groups by GitLab CODEOWNERS `[Section]` header name.
2281
+ */
2282
+ grouped_by: ("owner" | "directory" | "package" | "section")
2283
+ /**
2284
+ * Total number of issues across all groups.
2285
+ */
2286
+ total_issues: number
2287
+ /**
2288
+ * One entry per group, each containing the same issue arrays as the ungrouped CheckOutput.
2289
+ */
2290
+ groups: CheckGroupedEntry[]
2291
+ _meta?: Meta
2292
+ }
2293
+ /**
2294
+ * A single group within grouped dead code output. Contains the group key, a per-group total, and the same issue arrays as CheckOutput.
2295
+ */
2296
+ export interface CheckGroupedEntry {
2297
+ /**
2298
+ * Group identifier. For 'owner' grouping: the CODEOWNERS team (e.g., '@team-name'). For 'directory' grouping: the top-level directory prefix (e.g., 'src/components'). For 'section' grouping: the GitLab CODEOWNERS section name (e.g., 'billing'), or '(no section)' for files owned by a rule declared before any section header.
2299
+ */
2300
+ key: string
2301
+ /**
2302
+ * Section default owners (GitLab CODEOWNERS `[Section] @owner1 @owner2`). Emitted only when grouped_by is 'section'. Empty for the '(no section)' and '(unowned)' buckets.
2303
+ */
2304
+ owners?: string[]
2305
+ /**
2306
+ * Total number of issues in this group.
2307
+ */
2308
+ total_issues: number
2309
+ unused_files: UnusedFile[]
2310
+ unused_exports: UnusedExport[]
2311
+ unused_types: UnusedExport[]
2312
+ private_type_leaks: PrivateTypeLeak[]
2313
+ unused_dependencies: UnusedDependency[]
2314
+ unused_dev_dependencies: UnusedDependency[]
2315
+ unused_optional_dependencies: UnusedDependency[]
2316
+ unused_enum_members: UnusedMember[]
2317
+ unused_class_members: UnusedMember[]
2318
+ unresolved_imports: UnresolvedImport[]
2319
+ unlisted_dependencies: UnlistedDependency[]
2320
+ duplicate_exports: DuplicateExport[]
2321
+ type_only_dependencies: TypeOnlyDependency[]
2322
+ test_only_dependencies: TestOnlyDependency[]
2323
+ circular_dependencies: CircularDependency[]
2324
+ boundary_violations: BoundaryViolation[]
2325
+ stale_suppressions?: StaleSuppression[]
2326
+ }
2327
+ /**
2328
+ * Audit output combining dead code, complexity, and duplication scoped to changed files. Returns a verdict (pass/warn/fail) with per-category summary, optional new-vs-inherited attribution, and full sub-results.
2329
+ */
2330
+ export interface AuditOutput {
2331
+ schema_version: SchemaVersion
2332
+ version: ToolVersion
2333
+ command: "audit"
2334
+ /**
2335
+ * pass = no issues, warn = warn-severity only (exit 0), fail = error-severity issues (exit 1)
2336
+ */
2337
+ verdict: ("pass" | "warn" | "fail")
2338
+ /**
2339
+ * Number of files changed between base ref and HEAD
2340
+ */
2341
+ changed_files_count: number
2342
+ /**
2343
+ * Git ref used as comparison base (explicit or auto-detected)
2344
+ */
2345
+ base_ref: string
2346
+ /**
2347
+ * Short SHA of HEAD (omitted if git unavailable)
2348
+ */
2349
+ head_sha?: string
2350
+ elapsed_ms: ElapsedMs
2351
+ /**
2352
+ * Only emitted when --performance is set. true means audit reused the current run's keys as the base snapshot because every changed file was either a non-behavioral doc or token-equivalent at the base ref (the docs-only-diff fast path); false means the regular base worktree analysis ran.
2353
+ */
2354
+ base_snapshot_skipped?: boolean
2355
+ summary: {
2356
+ dead_code_issues?: number
2357
+ dead_code_has_errors?: boolean
2358
+ complexity_findings?: number
2359
+ max_cyclomatic?: (number | null)
2360
+ duplication_clone_groups?: number
2361
+ }
2362
+ /**
2363
+ * Counts split by whether each finding was introduced by the current changeset or already existed at the base ref. The default audit gate is new-only, so inherited findings are context. With audit.gate or --gate set to all, audit skips the extra base-snapshot attribution pass and these counts stay zero.
2364
+ */
2365
+ attribution: {
2366
+ /**
2367
+ * Which findings affect the audit verdict.
2368
+ */
2369
+ gate: ("new-only" | "all")
2370
+ dead_code_introduced: number
2371
+ dead_code_inherited: number
2372
+ complexity_introduced: number
2373
+ complexity_inherited: number
2374
+ duplication_introduced: number
2375
+ duplication_inherited: number
2376
+ }
2377
+ dead_code?: CheckOutput
2378
+ duplication?: DupesOutput
2379
+ complexity?: HealthOutput
2380
+ }
2381
+ /**
2382
+ * Standalone rule explanation for one issue type. This command does not run project analysis and intentionally returns a compact object without schema_version/version metadata.
2383
+ */
2384
+ export interface ExplainOutput {
2385
+ /**
2386
+ * Canonical rule id, for example fallow/unused-export.
2387
+ */
2388
+ id: string
2389
+ /**
2390
+ * Human-readable rule name.
2391
+ */
2392
+ name: string
2393
+ /**
2394
+ * Short one-line explanation of the issue.
2395
+ */
2396
+ summary: string
2397
+ /**
2398
+ * Why the issue matters and what fallow checks.
2399
+ */
2400
+ rationale: string
2401
+ /**
2402
+ * Concrete example of the finding.
2403
+ */
2404
+ example: string
2405
+ /**
2406
+ * Recommended fix or suppression guidance.
2407
+ */
2408
+ how_to_fix: string
2409
+ /**
2410
+ * Docs URL for the rule.
2411
+ */
2412
+ docs: string
2413
+ }
2414
+ /**
2415
+ * Deterministic agent-readable runtime coverage setup instructions. In workspaces, members contains one entry per detected runtime package and runtime_targets is the union of all member targets. With --explain, an optional _meta object describes fields, enums, and warning semantics.
2416
+ */
2417
+ export interface CoverageSetupOutput {
2418
+ schema_version: "1"
2419
+ /**
2420
+ * Primary detected runtime framework. For workspaces this mirrors the first emitted runtime member; unknown means no runtime member was detected.
2421
+ */
2422
+ framework_detected: ("nextjs" | "nestjs" | "nuxt" | "sveltekit" | "astro" | "remix" | "vite" | "plain_node" | "unknown")
2423
+ package_manager: ("npm" | "pnpm" | "yarn" | "bun" | null)
2424
+ /**
2425
+ * Union of runtime targets across emitted members.
2426
+ */
2427
+ runtime_targets: ("node" | "browser")[]
2428
+ /**
2429
+ * Per-runtime-workspace setup recipes. Pure aggregator roots and build-only library packages are omitted.
2430
+ */
2431
+ members: CoverageSetupMember[]
2432
+ config_written: null
2433
+ commands: string[]
2434
+ /**
2435
+ * Compatibility copy of the primary member's files, with workspace prefixes when the primary member is not the root.
2436
+ */
2437
+ files_to_edit: CoverageSetupFileToEdit[]
2438
+ /**
2439
+ * Compatibility copy of the primary member's snippets, with workspace prefixes when the primary member is not the root.
2440
+ */
2441
+ snippets: CoverageSetupSnippet[]
2442
+ dockerfile_snippet: (string | null)
2443
+ next_steps: string[]
2444
+ warnings: string[]
2445
+ /**
2446
+ * Optional explain metadata emitted only with fallow coverage setup --json --explain.
2447
+ */
2448
+ _meta?: {
2449
+ [k: string]: unknown
2450
+ }
2451
+ }
2452
+ export interface CoverageSetupMember {
2453
+ name: string
2454
+ /**
2455
+ * Workspace path relative to the analyzed root, or . for the root member.
2456
+ */
2457
+ path: string
2458
+ framework_detected: ("nextjs" | "nestjs" | "nuxt" | "sveltekit" | "astro" | "remix" | "vite" | "plain_node" | "unknown")
2459
+ package_manager: ("npm" | "pnpm" | "yarn" | "bun" | null)
2460
+ runtime_targets: ("node" | "browser")[]
2461
+ files_to_edit: CoverageSetupFileToEdit[]
2462
+ snippets: CoverageSetupSnippet[]
2463
+ dockerfile_snippet: (string | null)
2464
+ warnings: string[]
2465
+ }
2466
+ export interface CoverageSetupFileToEdit {
2467
+ path: string
2468
+ reason: string
2469
+ }
2470
+ export interface CoverageSetupSnippet {
2471
+ label: string
2472
+ path: string
2473
+ content: string
2474
+ }
2475
+ export interface CodeClimateIssue {
2476
+ type: "issue"
2477
+ check_name: string
2478
+ description: string
2479
+ categories: string[]
2480
+ severity: ("info" | "minor" | "major" | "critical" | "blocker")
2481
+ fingerprint: string
2482
+ location: {
2483
+ path: string
2484
+ lines: {
2485
+ begin: number
2486
+ }
2487
+ }
2488
+ }
2489
+ /**
2490
+ * Typed review envelope consumed by action/scripts/review.sh and ci/scripts/review.sh.
2491
+ */
2492
+ export interface ReviewEnvelopeOutput {
2493
+ /**
2494
+ * GitHub review event. Omitted for GitLab.
2495
+ */
2496
+ event?: "COMMENT"
2497
+ body: string
2498
+ comments: (GitHubReviewComment | GitLabReviewComment)[]
2499
+ meta: {
2500
+ schema: "fallow-review-envelope/v1"
2501
+ provider: ("github" | "gitlab")
2502
+ check_conclusion?: ("success" | "neutral" | "failure")
2503
+ }
2504
+ }
2505
+ export interface GitHubReviewComment {
2506
+ path: string
2507
+ line: number
2508
+ /**
2509
+ * GitHub review comments target current-state/new-side lines; deletion-side comments are not modeled yet.
2510
+ */
2511
+ side: "RIGHT"
2512
+ body: string
2513
+ fingerprint: string
2514
+ }
2515
+ export interface GitLabReviewComment {
2516
+ body: string
2517
+ position: GitLabReviewPosition
2518
+ fingerprint: string
2519
+ }
2520
+ export interface GitLabReviewPosition {
2521
+ base_sha?: string
2522
+ start_sha?: string
2523
+ head_sha?: string
2524
+ position_type: "text"
2525
+ old_path: string
2526
+ new_path: string
2527
+ new_line: number
2528
+ }
2529
+ export interface ReviewReconcileOutput {
2530
+ schema: "fallow-review-reconcile/v1"
2531
+ provider: ("github" | "gitlab")
2532
+ target: (string | null)
2533
+ dry_run: boolean
2534
+ comments: number
2535
+ current_fingerprints: number
2536
+ existing_fingerprints: number
2537
+ new_fingerprints: number
2538
+ stale_fingerprints: number
2539
+ new: string[]
2540
+ stale: string[]
2541
+ provider_warning?: (string | null)
2542
+ resolution_comments_posted: number
2543
+ threads_resolved: number
2544
+ apply_errors: string[]
2545
+ }