fallow 2.70.0 → 2.72.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2549 @@
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 in the reported `clone_groups[]` array. Matches `clone_groups[].length` post `minOccurrences` filtering; the count of groups hidden by the filter is exposed in `clone_groups_below_min_occurrences`.
968
+ */
969
+ clone_groups: number
970
+ /**
971
+ * Total clone instances across all reported groups. Matches `clone_groups[]`'s flattened instance count post `minOccurrences` filtering.
972
+ */
973
+ clone_instances: number
974
+ /**
975
+ * Percentage of duplicated lines (0.0 to 100.0). Always reflects the FULL corpus, computed BEFORE the `minOccurrences` filter so trend lines and `threshold` gates stay stable when the filter changes.
976
+ */
977
+ duplication_percentage: number
978
+ /**
979
+ * Number of clone groups hidden by `duplicates.minOccurrences`. Absent (or `0`) when the filter is at its default of `2` and nothing was hidden. Pre-filter clone group count = `clone_groups + clone_groups_below_min_occurrences`.
980
+ */
981
+ clone_groups_below_min_occurrences?: number
982
+ }
983
+ /**
984
+ * A pair of directories with high structural duplication, indicating systematic copy-paste of directory trees.
985
+ */
986
+ export interface MirroredDirectory {
987
+ /**
988
+ * First directory path (relative to project root).
989
+ */
990
+ dir_a: string
991
+ /**
992
+ * Second directory path (relative to project root).
993
+ */
994
+ dir_b: string
995
+ /**
996
+ * File names that appear in both directories with duplicated content.
997
+ */
998
+ shared_files: string[]
999
+ /**
1000
+ * Total duplicated lines across all shared files.
1001
+ */
1002
+ total_lines: number
1003
+ }
1004
+ /**
1005
+ * A single grouped duplication bucket. Per-group `stats` are dedup-aware and computed over the FULL group BEFORE any `--top` truncation.
1006
+ */
1007
+ export interface DuplicationGroup {
1008
+ /**
1009
+ * Group label (owner / directory / package / section). `(unowned)` for files with no CODEOWNERS rule, `(no section)` for pre-section rules in section mode.
1010
+ */
1011
+ key: string
1012
+ stats: DuplicationStats
1013
+ /**
1014
+ * 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.
1015
+ */
1016
+ clone_groups: AttributedCloneGroup[]
1017
+ clone_families: CloneFamily[]
1018
+ }
1019
+ /**
1020
+ * A clone group annotated with its largest-owner attribution and per-instance owner keys.
1021
+ */
1022
+ export interface AttributedCloneGroup {
1023
+ /**
1024
+ * Largest-owner attribution: the resolver key with the most instances in this clone group. Ties broken alphabetically (smallest key wins).
1025
+ */
1026
+ primary_owner: string
1027
+ token_count: number
1028
+ line_count: number
1029
+ /**
1030
+ * Each instance carries its own `owner` field alongside the standard CloneInstance shape.
1031
+ */
1032
+ instances: {
1033
+ file: string
1034
+ start_line: number
1035
+ end_line: number
1036
+ start_col: number
1037
+ end_col: number
1038
+ fragment: string
1039
+ /**
1040
+ * Resolver key for this specific instance (per-instance, not the group-level largest-owner).
1041
+ */
1042
+ owner: string
1043
+ }[]
1044
+ /**
1045
+ * Suggested actions for this clone group (extract-shared, suppress-line, etc.).
1046
+ */
1047
+ actions?: unknown[]
1048
+ }
1049
+ export interface HealthOutput {
1050
+ schema_version: SchemaVersion
1051
+ version: ToolVersion
1052
+ elapsed_ms: ElapsedMs
1053
+ summary: HealthSummary
1054
+ /**
1055
+ * Functions and synthetic template entries exceeding complexity thresholds, sorted by the --sort criteria.
1056
+ */
1057
+ findings: HealthFinding[]
1058
+ /**
1059
+ * 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.
1060
+ */
1061
+ file_scores?: FileHealthScore[]
1062
+ /**
1063
+ * Hotspot entries combining git churn with complexity. Only present when --hotspots is used. Sorted by score descending (highest risk first).
1064
+ */
1065
+ hotspots?: HotspotEntry[]
1066
+ hotspot_summary?: HotspotSummary
1067
+ vital_signs?: VitalSigns
1068
+ health_score?: HealthScore
1069
+ /**
1070
+ * Functions exceeding 60 LOC (very high risk). Only present when unit size very-high-risk bin >= 3%. Sorted by line count descending.
1071
+ */
1072
+ large_functions?: LargeFunctionEntry[]
1073
+ /**
1074
+ * Ranked refactoring recommendations. Only present when --targets is used. Sorted by efficiency (priority/effort) descending.
1075
+ */
1076
+ targets?: RefactoringTarget[]
1077
+ target_thresholds?: TargetThresholds
1078
+ health_trend?: HealthTrend
1079
+ coverage_gaps?: CoverageGaps
1080
+ runtime_coverage?: RuntimeCoverageReport
1081
+ /**
1082
+ * 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`.
1083
+ */
1084
+ grouped_by?: ("owner" | "directory" | "package" | "section")
1085
+ /**
1086
+ * 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.
1087
+ */
1088
+ groups?: HealthGroup[]
1089
+ /**
1090
+ * 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.
1091
+ */
1092
+ actions_meta?: {
1093
+ /**
1094
+ * True when `suppress-line` actions were not emitted on health findings.
1095
+ */
1096
+ suppression_hints_omitted: boolean
1097
+ /**
1098
+ * 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.
1099
+ */
1100
+ reason: string
1101
+ /**
1102
+ * The report section affected by this metadata. Other health sections can still carry their own suppress actions.
1103
+ */
1104
+ scope: "health-findings"
1105
+ }
1106
+ _meta?: Meta
1107
+ }
1108
+ export interface HealthSummary {
1109
+ /**
1110
+ * Number of files analyzed for complexity.
1111
+ */
1112
+ files_analyzed: number
1113
+ /**
1114
+ * Total number of functions found.
1115
+ */
1116
+ functions_analyzed: number
1117
+ /**
1118
+ * Number of functions exceeding at least one threshold (before --top truncation).
1119
+ */
1120
+ functions_above_threshold: number
1121
+ /**
1122
+ * Configured cyclomatic complexity threshold.
1123
+ */
1124
+ max_cyclomatic_threshold: number
1125
+ /**
1126
+ * Configured cognitive complexity threshold.
1127
+ */
1128
+ max_cognitive_threshold: number
1129
+ /**
1130
+ * 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.
1131
+ */
1132
+ max_crap_threshold: number
1133
+ /**
1134
+ * Number of files with health scores. Only present when --file-scores is used. 0 indicates the flag was set but scoring failed.
1135
+ */
1136
+ files_scored?: number
1137
+ /**
1138
+ * Average maintainability index across all scored files (before --top truncation). Only present when --file-scores is used and at least one file was scored.
1139
+ */
1140
+ average_maintainability?: number
1141
+ /**
1142
+ * 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.
1143
+ */
1144
+ coverage_model?: ("static_binary" | "static_estimated" | "istanbul")
1145
+ /**
1146
+ * Number of functions matched against Istanbul coverage data. Only present when coverage_model is 'istanbul'.
1147
+ */
1148
+ istanbul_matched?: number
1149
+ /**
1150
+ * Total functions evaluated for Istanbul matching. Only present when coverage_model is 'istanbul'.
1151
+ */
1152
+ istanbul_total?: number
1153
+ /**
1154
+ * Number of findings with critical severity (cognitive >= 40 or cyclomatic >= 50).
1155
+ */
1156
+ severity_critical_count: number
1157
+ /**
1158
+ * Number of findings with high severity (cognitive 25-39 or cyclomatic 30-49).
1159
+ */
1160
+ severity_high_count: number
1161
+ /**
1162
+ * Number of findings with moderate severity (below high thresholds).
1163
+ */
1164
+ severity_moderate_count: number
1165
+ }
1166
+ export interface HealthFinding {
1167
+ /**
1168
+ * File path (relative to project root).
1169
+ */
1170
+ path: string
1171
+ /**
1172
+ * Function name, "<anonymous>" for unnamed functions/arrows, or "<template>" for synthetic Angular template findings.
1173
+ */
1174
+ name: string
1175
+ /**
1176
+ * 1-based line number.
1177
+ */
1178
+ line: number
1179
+ /**
1180
+ * 0-based byte column offset.
1181
+ */
1182
+ col: number
1183
+ /**
1184
+ * McCabe cyclomatic complexity (1 + decision points).
1185
+ */
1186
+ cyclomatic: number
1187
+ /**
1188
+ * SonarSource cognitive complexity (structural + nesting penalty).
1189
+ */
1190
+ cognitive: number
1191
+ /**
1192
+ * Number of lines in the function body.
1193
+ */
1194
+ line_count: number
1195
+ /**
1196
+ * Number of parameters (excluding TypeScript's this parameter).
1197
+ */
1198
+ param_count: number
1199
+ /**
1200
+ * Which threshold(s) this finding exceeds. `crap` and its combinations are emitted when `max_crap_threshold` is crossed.
1201
+ */
1202
+ exceeded: ("cyclomatic" | "cognitive" | "both" | "crap" | "cyclomatic_crap" | "cognitive_crap" | "all")
1203
+ /**
1204
+ * CRAP score (rounded to one decimal). Present only when the function exceeded the CRAP threshold; absent otherwise.
1205
+ */
1206
+ crap?: number
1207
+ /**
1208
+ * 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.
1209
+ */
1210
+ coverage_pct?: number
1211
+ /**
1212
+ * 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).
1213
+ */
1214
+ coverage_tier?: ("none" | "partial" | "high")
1215
+ /**
1216
+ * How far above the threshold: moderate (just above), high (recommended for extraction), or critical (immediate extraction candidate). Defaults: cognitive 25/40, cyclomatic 30/50.
1217
+ */
1218
+ severity: ("moderate" | "high" | "critical")
1219
+ /**
1220
+ * Suggested actions to address this complex finding.
1221
+ */
1222
+ actions?: HealthFindingAction[]
1223
+ introduced?: AuditIntroduced
1224
+ }
1225
+ /**
1226
+ * A suggested action for a health complexity finding.
1227
+ */
1228
+ export interface HealthFindingAction {
1229
+ /**
1230
+ * 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`.
1231
+ */
1232
+ type: ("refactor-function" | "add-tests" | "increase-coverage" | "suppress-file" | "suppress-line")
1233
+ /**
1234
+ * Whether fallow can auto-fix this action.
1235
+ */
1236
+ auto_fixable: boolean
1237
+ /**
1238
+ * Human-readable description of the action.
1239
+ */
1240
+ description: string
1241
+ /**
1242
+ * Additional context for the action.
1243
+ */
1244
+ note?: string
1245
+ /**
1246
+ * The inline comment to insert (e.g., '// fallow-ignore-next-line complexity' or '<!-- fallow-ignore-file complexity -->'). Present for suppress actions.
1247
+ */
1248
+ comment?: string
1249
+ /**
1250
+ * Where to insert the suppress comment (e.g., 'above-function-declaration', 'above-angular-decorator', or 'top-of-template'). Present for suppress actions.
1251
+ */
1252
+ placement?: string
1253
+ }
1254
+ /**
1255
+ * 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.
1256
+ */
1257
+ export interface FileHealthScore {
1258
+ /**
1259
+ * File path (relative to project root).
1260
+ */
1261
+ path: string
1262
+ /**
1263
+ * Number of files that import this file.
1264
+ */
1265
+ fan_in: number
1266
+ /**
1267
+ * Number of files this file directly imports.
1268
+ */
1269
+ fan_out: number
1270
+ /**
1271
+ * Fraction of value exports (excluding type-only) with zero references (0.0–1.0).
1272
+ */
1273
+ dead_code_ratio: number
1274
+ /**
1275
+ * Total cyclomatic complexity / lines of code.
1276
+ */
1277
+ complexity_density: number
1278
+ /**
1279
+ * Weighted composite score (0–100, higher is better).
1280
+ */
1281
+ maintainability_index: number
1282
+ /**
1283
+ * Sum of cyclomatic complexity across all functions in this file.
1284
+ */
1285
+ total_cyclomatic: number
1286
+ /**
1287
+ * Sum of cognitive complexity across all functions in this file.
1288
+ */
1289
+ total_cognitive: number
1290
+ /**
1291
+ * Number of functions in this file.
1292
+ */
1293
+ function_count: number
1294
+ /**
1295
+ * Total lines of code in the file.
1296
+ */
1297
+ lines: number
1298
+ /**
1299
+ * 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).
1300
+ */
1301
+ crap_max: number
1302
+ /**
1303
+ * Count of functions with CRAP score >= 30 (CC >= 5 without test dependency path).
1304
+ */
1305
+ crap_above_threshold: number
1306
+ }
1307
+ /**
1308
+ * A hotspot: a file that is both complex and frequently changing. Score = normalized_churn × normalized_complexity × 100 (0–100, higher = riskier).
1309
+ */
1310
+ export interface HotspotEntry {
1311
+ /**
1312
+ * File path (relative to project root).
1313
+ */
1314
+ path: string
1315
+ /**
1316
+ * Hotspot score (0–100). Higher means more risk.
1317
+ */
1318
+ score: number
1319
+ /**
1320
+ * Number of commits touching this file in the analysis window.
1321
+ */
1322
+ commits: number
1323
+ /**
1324
+ * Recency-weighted commit count (exponential decay, half-life 90 days).
1325
+ */
1326
+ weighted_commits: number
1327
+ /**
1328
+ * Total lines added across all commits in the window.
1329
+ */
1330
+ lines_added: number
1331
+ /**
1332
+ * Total lines deleted across all commits in the window.
1333
+ */
1334
+ lines_deleted: number
1335
+ /**
1336
+ * Total cyclomatic complexity / lines of code.
1337
+ */
1338
+ complexity_density: number
1339
+ /**
1340
+ * Number of files that import this file (blast radius).
1341
+ */
1342
+ fan_in: number
1343
+ /**
1344
+ * Churn trend: accelerating (recent > 1.5× older), stable, or cooling (recent < 0.67× older).
1345
+ */
1346
+ trend: ("accelerating" | "stable" | "cooling")
1347
+ /**
1348
+ * Suggested actions to reduce hotspot risk.
1349
+ */
1350
+ actions?: HotspotAction[]
1351
+ ownership?: OwnershipMetrics
1352
+ }
1353
+ /**
1354
+ * A suggested action for a hotspot file. Ownership-derived action types (low-bus-factor, unowned-hotspot, ownership-drift) appear only when --ownership is enabled.
1355
+ */
1356
+ export interface HotspotAction {
1357
+ /**
1358
+ * Action type identifier.
1359
+ */
1360
+ type: ("refactor-file" | "add-tests" | "low-bus-factor" | "unowned-hotspot" | "ownership-drift")
1361
+ /**
1362
+ * Whether fallow can auto-fix this action.
1363
+ */
1364
+ auto_fixable: boolean
1365
+ /**
1366
+ * Human-readable description of the action.
1367
+ */
1368
+ description: string
1369
+ /**
1370
+ * Additional context for the action.
1371
+ */
1372
+ note?: string
1373
+ /**
1374
+ * 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.
1375
+ */
1376
+ suggested_pattern?: string
1377
+ /**
1378
+ * Strategy used to derive suggested_pattern. Reserved for future evolution (e.g., 'codeowners-cluster').
1379
+ */
1380
+ heuristic?: "directory-deepest"
1381
+ }
1382
+ /**
1383
+ * Per-file ownership signals derived from git author history and CODEOWNERS. Only computed when --ownership is requested.
1384
+ */
1385
+ export interface OwnershipMetrics {
1386
+ /**
1387
+ * Avelino truck factor: minimum contributors covering at least 50% of recency-weighted commits in the analysis window. Lower = higher knowledge-loss risk.
1388
+ */
1389
+ bus_factor: number
1390
+ /**
1391
+ * Distinct authors after bot filtering.
1392
+ */
1393
+ contributor_count: number
1394
+ top_contributor: ContributorEntry
1395
+ /**
1396
+ * Up to three additional contributors by share, ordered descending. Useful for review routing.
1397
+ */
1398
+ recent_contributors?: ContributorEntry[]
1399
+ /**
1400
+ * CODEOWNERS-resolved primary owner for this file, when a rule matches.
1401
+ */
1402
+ declared_owner?: (string | null)
1403
+ /**
1404
+ * Tristate: true = CODEOWNERS file exists but no rule matches; false = a rule matches; null = no CODEOWNERS file discovered for the repository.
1405
+ */
1406
+ unowned: (boolean | null)
1407
+ /**
1408
+ * True when ownership has drifted from the original author to a new top contributor (file age >= 30 days, original author share < 10%).
1409
+ */
1410
+ drift: boolean
1411
+ /**
1412
+ * Human-readable explanation of the drift. Field is omitted (not null) when drift is false.
1413
+ */
1414
+ drift_reason?: string
1415
+ }
1416
+ /**
1417
+ * 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.
1418
+ */
1419
+ export interface ContributorEntry {
1420
+ /**
1421
+ * 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.
1422
+ */
1423
+ identifier: string
1424
+ /**
1425
+ * Discriminator for identifier shape. Set by ownership.emailMode at analysis time.
1426
+ */
1427
+ format: ("raw" | "handle" | "hash")
1428
+ /**
1429
+ * Recency-weighted share of total weighted commits, rounded to three decimals.
1430
+ */
1431
+ share: number
1432
+ /**
1433
+ * Days since this contributor last touched the file.
1434
+ */
1435
+ stale_days: number
1436
+ /**
1437
+ * Total commits by this contributor in the analysis window.
1438
+ */
1439
+ commits: number
1440
+ }
1441
+ export interface HotspotSummary {
1442
+ /**
1443
+ * Analysis window display string (e.g., '6 months').
1444
+ */
1445
+ since: string
1446
+ /**
1447
+ * Minimum commits threshold.
1448
+ */
1449
+ min_commits: number
1450
+ /**
1451
+ * Number of files with churn data meeting the threshold.
1452
+ */
1453
+ files_analyzed: number
1454
+ /**
1455
+ * Number of files excluded (below min_commits).
1456
+ */
1457
+ files_excluded: number
1458
+ /**
1459
+ * Whether the repository is a shallow clone.
1460
+ */
1461
+ shallow_clone: boolean
1462
+ }
1463
+ /**
1464
+ * Project-wide vital signs metrics. Optional fields are null when the corresponding analysis was not run.
1465
+ */
1466
+ export interface VitalSigns {
1467
+ /**
1468
+ * Percentage of unreachable files.
1469
+ */
1470
+ dead_file_pct?: (number | null)
1471
+ /**
1472
+ * Percentage of unused exports.
1473
+ */
1474
+ dead_export_pct?: (number | null)
1475
+ /**
1476
+ * Average cyclomatic complexity.
1477
+ */
1478
+ avg_cyclomatic: number
1479
+ /**
1480
+ * Percentage of functions at or above the critical cyclomatic threshold. Used by the scale-invariant health score.
1481
+ */
1482
+ critical_complexity_pct?: (number | null)
1483
+ /**
1484
+ * 90th percentile cyclomatic complexity.
1485
+ */
1486
+ p90_cyclomatic: number
1487
+ /**
1488
+ * Code duplication percentage. Null if duplication pipeline was not run.
1489
+ */
1490
+ duplication_pct?: (number | null)
1491
+ /**
1492
+ * Files with hotspot score >= 50. Null if git history unavailable.
1493
+ */
1494
+ hotspot_count?: (number | null)
1495
+ /**
1496
+ * Files in the top 1% of the within-project hotspot ranking. Null if git history unavailable.
1497
+ */
1498
+ hotspot_top_pct_count?: (number | null)
1499
+ /**
1500
+ * Average maintainability index (0-100).
1501
+ */
1502
+ maintainability_avg?: (number | null)
1503
+ /**
1504
+ * Percentage of scored files with maintainability index below 70. Null if file scores were not computed.
1505
+ */
1506
+ maintainability_low_pct?: (number | null)
1507
+ /**
1508
+ * Number of unused dependencies.
1509
+ */
1510
+ unused_dep_count?: (number | null)
1511
+ /**
1512
+ * Unused dependencies per 1,000 files. Null if dead code analysis did not run.
1513
+ */
1514
+ unused_deps_per_k_files?: (number | null)
1515
+ /**
1516
+ * Number of circular dependency chains.
1517
+ */
1518
+ circular_dep_count?: (number | null)
1519
+ /**
1520
+ * Circular dependency chains per 1,000 files. Null if dead code analysis did not run.
1521
+ */
1522
+ circular_deps_per_k_files?: (number | null)
1523
+ counts?: VitalSignsCounts
1524
+ unit_size_profile?: RiskProfile
1525
+ /**
1526
+ * Functions above 60 LOC per 1,000 functions. Null if no functions analyzed.
1527
+ */
1528
+ functions_over_60_loc_per_k?: (number | null)
1529
+ unit_interfacing_profile?: RiskProfile
1530
+ /**
1531
+ * 95th percentile fan-in across all files. Null if file scores not computed.
1532
+ */
1533
+ p95_fan_in?: (number | null)
1534
+ /**
1535
+ * Percentage of files with fan-in above the effective threshold. Null if file scores not computed.
1536
+ */
1537
+ coupling_high_pct?: (number | null)
1538
+ /**
1539
+ * Total lines of code across all parsed modules. 0 if no modules parsed.
1540
+ */
1541
+ total_loc?: number
1542
+ }
1543
+ /**
1544
+ * Raw counts behind the vital signs percentages.
1545
+ */
1546
+ export interface VitalSignsCounts {
1547
+ /**
1548
+ * Total number of discovered source files.
1549
+ */
1550
+ total_files?: number
1551
+ /**
1552
+ * Total number of exports across all files.
1553
+ */
1554
+ total_exports?: number
1555
+ /**
1556
+ * Number of unreachable files.
1557
+ */
1558
+ dead_files?: number
1559
+ /**
1560
+ * Number of unused exports.
1561
+ */
1562
+ dead_exports?: number
1563
+ }
1564
+ /**
1565
+ * Risk profile: percentage of functions in each risk bin. Bins depend on the measured property (unit size or parameter count).
1566
+ */
1567
+ export interface RiskProfile {
1568
+ /**
1569
+ * Percentage of functions in the low-risk bin.
1570
+ */
1571
+ low_risk: number
1572
+ /**
1573
+ * Percentage of functions in the medium-risk bin.
1574
+ */
1575
+ medium_risk: number
1576
+ /**
1577
+ * Percentage of functions in the high-risk bin.
1578
+ */
1579
+ high_risk: number
1580
+ /**
1581
+ * Percentage of functions in the very-high-risk bin.
1582
+ */
1583
+ very_high_risk: number
1584
+ }
1585
+ /**
1586
+ * 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).
1587
+ */
1588
+ export interface HealthScore {
1589
+ /**
1590
+ * Health score formula version. Version 2 uses scale-invariant density/tail metrics for monorepo-safe scoring.
1591
+ */
1592
+ formula_version: number
1593
+ /**
1594
+ * Overall score (0-100, higher is better). Reproducible: 100 - sum(penalties) == score.
1595
+ */
1596
+ score: number
1597
+ /**
1598
+ * Letter grade. A: score >= 85, B: 70-84, C: 55-69, D: 40-54, F: below 40.
1599
+ */
1600
+ grade: ("A" | "B" | "C" | "D" | "F")
1601
+ penalties: HealthScorePenalties
1602
+ }
1603
+ /**
1604
+ * 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.
1605
+ */
1606
+ export interface HealthScorePenalties {
1607
+ /**
1608
+ * Points lost from dead files (max 15). Null if dead code pipeline not run.
1609
+ */
1610
+ dead_files?: (number | null)
1611
+ /**
1612
+ * Points lost from dead exports (max 15). Null if dead code pipeline not run.
1613
+ */
1614
+ dead_exports?: (number | null)
1615
+ /**
1616
+ * Points lost from critical-complexity density (max 20). Older snapshots without density fields fall back to average cyclomatic complexity above 1.5.
1617
+ */
1618
+ complexity: number
1619
+ /**
1620
+ * Points lost from legacy p90 cyclomatic complexity above 10. Current scale-invariant runs report 0 because tail complexity is folded into complexity.
1621
+ */
1622
+ p90_complexity: number
1623
+ /**
1624
+ * Points lost from low maintainability density (max 15). Null if file-scores not computed.
1625
+ */
1626
+ maintainability?: (number | null)
1627
+ /**
1628
+ * Points lost from top-percentile hotspot density (max 10). Null if hotspots not computed.
1629
+ */
1630
+ hotspots?: (number | null)
1631
+ /**
1632
+ * Points lost from unused dependency density (max 25). Null if dead code pipeline not run.
1633
+ */
1634
+ unused_deps?: (number | null)
1635
+ /**
1636
+ * Points lost from circular dependency density (max 25). Null if dead code pipeline not run.
1637
+ */
1638
+ circular_deps?: (number | null)
1639
+ /**
1640
+ * Points lost from oversized-function density (max 10). Null if no functions analyzed.
1641
+ */
1642
+ unit_size?: (number | null)
1643
+ /**
1644
+ * Points lost from coupling concentration density (max 5). Null if file scores not computed.
1645
+ */
1646
+ coupling?: (number | null)
1647
+ /**
1648
+ * Points lost from code duplication (max 10). Penalty = min(max(0, duplication_pct - 5) * 1, 10). Null if duplication pipeline not run.
1649
+ */
1650
+ duplication?: (number | null)
1651
+ }
1652
+ /**
1653
+ * A function exceeding 60 lines of code (very high risk size bin).
1654
+ */
1655
+ export interface LargeFunctionEntry {
1656
+ /**
1657
+ * File path (relative to project root).
1658
+ */
1659
+ path: string
1660
+ /**
1661
+ * Function name, or "<anonymous>" for unnamed functions/arrows.
1662
+ */
1663
+ name: string
1664
+ /**
1665
+ * 1-based line number.
1666
+ */
1667
+ line: number
1668
+ /**
1669
+ * Number of lines in the function body.
1670
+ */
1671
+ line_count: number
1672
+ }
1673
+ export interface RefactoringTarget {
1674
+ /**
1675
+ * File path (relative to project root).
1676
+ */
1677
+ path: string
1678
+ /**
1679
+ * Weighted priority score (0-100). Higher = more urgent.
1680
+ */
1681
+ priority: number
1682
+ /**
1683
+ * priority / effort_numeric (Low=1, Medium=2, High=3). Default sort order.
1684
+ */
1685
+ efficiency: number
1686
+ /**
1687
+ * Human-readable one-line recommendation.
1688
+ */
1689
+ recommendation: string
1690
+ /**
1691
+ * Recommendation category.
1692
+ */
1693
+ category: ("urgent_churn_complexity" | "break_circular_dependency" | "split_high_impact" | "remove_dead_code" | "extract_complex_functions" | "extract_dependencies")
1694
+ /**
1695
+ * Heuristic effort estimate.
1696
+ */
1697
+ effort: ("low" | "medium" | "high")
1698
+ /**
1699
+ * Data source reliability. High = deterministic analysis, Medium = heuristic, Low = git-dependent.
1700
+ */
1701
+ confidence: ("high" | "medium" | "low")
1702
+ /**
1703
+ * Contributing factors that triggered this recommendation. Empty array omitted from JSON.
1704
+ */
1705
+ factors?: {
1706
+ /**
1707
+ * Metric name (fan_in, dead_code_ratio, etc.).
1708
+ */
1709
+ metric: string
1710
+ /**
1711
+ * Raw metric value.
1712
+ */
1713
+ value: number
1714
+ /**
1715
+ * Threshold that was exceeded.
1716
+ */
1717
+ threshold: number
1718
+ /**
1719
+ * Human-readable explanation.
1720
+ */
1721
+ detail: string
1722
+ }[]
1723
+ /**
1724
+ * Structured evidence for supported categories. Omitted when not applicable.
1725
+ */
1726
+ evidence?: {
1727
+ /**
1728
+ * Names of unused exports (for remove_dead_code).
1729
+ */
1730
+ unused_exports?: string[]
1731
+ /**
1732
+ * Complex functions with line numbers (for extract_complex_functions).
1733
+ */
1734
+ complex_functions?: {
1735
+ name?: string
1736
+ line?: number
1737
+ cognitive?: number
1738
+ }[]
1739
+ /**
1740
+ * Files in the import cycle (for break_circular_dependency).
1741
+ */
1742
+ cycle_path?: string[]
1743
+ }
1744
+ /**
1745
+ * Suggested actions for this refactoring target.
1746
+ */
1747
+ actions?: RefactoringTargetAction[]
1748
+ }
1749
+ /**
1750
+ * A suggested action for a refactoring target.
1751
+ */
1752
+ export interface RefactoringTargetAction {
1753
+ /**
1754
+ * Action type identifier.
1755
+ */
1756
+ type: ("apply-refactoring" | "suppress-line")
1757
+ /**
1758
+ * Whether fallow can auto-fix this action.
1759
+ */
1760
+ auto_fixable: boolean
1761
+ /**
1762
+ * Human-readable description of the action.
1763
+ */
1764
+ description: string
1765
+ /**
1766
+ * The recommendation category for apply-refactoring actions.
1767
+ */
1768
+ category?: string
1769
+ /**
1770
+ * The inline comment to insert. Present for suppress-line actions when evidence exists.
1771
+ */
1772
+ comment?: string
1773
+ /**
1774
+ * Where to insert the suppress comment. Present for suppress-line actions.
1775
+ */
1776
+ placement?: string
1777
+ }
1778
+ /**
1779
+ * Adaptive percentile-based thresholds derived from the project's metric distribution. Used for priority scoring and rule matching.
1780
+ */
1781
+ export interface TargetThresholds {
1782
+ /**
1783
+ * Fan-in saturation for priority formula (p95, floor 5).
1784
+ */
1785
+ fan_in_p95: number
1786
+ /**
1787
+ * Fan-in moderate threshold for contributing factors (p75, floor 3).
1788
+ */
1789
+ fan_in_p75: number
1790
+ /**
1791
+ * Fan-out saturation for priority formula (p95, floor 8).
1792
+ */
1793
+ fan_out_p95: number
1794
+ /**
1795
+ * Fan-out high threshold for rules and factors (p90, floor 5).
1796
+ */
1797
+ fan_out_p90: number
1798
+ }
1799
+ /**
1800
+ * Trend comparison between the current run and a previous snapshot. Shows per-metric deltas with directional indicators.
1801
+ */
1802
+ export interface HealthTrend {
1803
+ /**
1804
+ * The snapshot being compared against.
1805
+ */
1806
+ compared_to: {
1807
+ /**
1808
+ * ISO 8601 timestamp of the snapshot.
1809
+ */
1810
+ timestamp: string
1811
+ /**
1812
+ * Git SHA at time of snapshot.
1813
+ */
1814
+ git_sha?: string
1815
+ /**
1816
+ * Health score from the snapshot (stored, not re-derived).
1817
+ */
1818
+ score?: number
1819
+ /**
1820
+ * Letter grade from the snapshot.
1821
+ */
1822
+ grade?: string
1823
+ }
1824
+ metrics: {
1825
+ /**
1826
+ * Metric identifier (e.g., 'score', 'dead_file_pct').
1827
+ */
1828
+ name: string
1829
+ /**
1830
+ * Human-readable label.
1831
+ */
1832
+ label: string
1833
+ /**
1834
+ * Previous value (from snapshot).
1835
+ */
1836
+ previous: number
1837
+ /**
1838
+ * Current value (from this run).
1839
+ */
1840
+ current: number
1841
+ /**
1842
+ * Absolute change (current - previous).
1843
+ */
1844
+ delta: number
1845
+ direction: ("improving" | "declining" | "stable")
1846
+ /**
1847
+ * Unit for display (e.g., '%', '').
1848
+ */
1849
+ unit: string
1850
+ previous_count?: TrendCount
1851
+ current_count?: TrendCount
1852
+ }[]
1853
+ /**
1854
+ * Number of snapshots found in the snapshot directory.
1855
+ */
1856
+ snapshots_loaded: number
1857
+ overall_direction: ("improving" | "declining" | "stable")
1858
+ }
1859
+ /**
1860
+ * Raw numerator/denominator for a percentage metric in trend comparison.
1861
+ */
1862
+ export interface TrendCount {
1863
+ /**
1864
+ * The numerator (e.g., dead files count).
1865
+ */
1866
+ value: number
1867
+ /**
1868
+ * The denominator (e.g., total files).
1869
+ */
1870
+ total: number
1871
+ }
1872
+ /**
1873
+ * Static test coverage gaps derived from the module graph. Shows runtime files and exports with no test dependency path.
1874
+ */
1875
+ export interface CoverageGaps {
1876
+ summary: CoverageGapSummary
1877
+ /**
1878
+ * Runtime files with no test dependency path. Omitted when empty.
1879
+ */
1880
+ files?: UntestedFile[]
1881
+ /**
1882
+ * Runtime exports with no test-reachable reference chain. Omitted when empty.
1883
+ */
1884
+ exports?: UntestedExport[]
1885
+ }
1886
+ /**
1887
+ * Aggregate coverage-gap counters for the current analysis scope.
1888
+ */
1889
+ export interface CoverageGapSummary {
1890
+ /**
1891
+ * Runtime-reachable files in scope.
1892
+ */
1893
+ runtime_files: number
1894
+ /**
1895
+ * Runtime-reachable files also reachable from tests.
1896
+ */
1897
+ covered_files: number
1898
+ /**
1899
+ * Percentage of runtime files that are test-reachable.
1900
+ */
1901
+ file_coverage_pct: number
1902
+ /**
1903
+ * Runtime files with no test dependency path.
1904
+ */
1905
+ untested_files: number
1906
+ /**
1907
+ * Runtime exports with no test-reachable reference chain.
1908
+ */
1909
+ untested_exports: number
1910
+ }
1911
+ /**
1912
+ * A runtime file with no test dependency path.
1913
+ */
1914
+ export interface UntestedFile {
1915
+ /**
1916
+ * File path (relative to project root).
1917
+ */
1918
+ path: string
1919
+ /**
1920
+ * Number of value exports declared by the file.
1921
+ */
1922
+ value_export_count: number
1923
+ }
1924
+ /**
1925
+ * A runtime export that no test-reachable module references.
1926
+ */
1927
+ export interface UntestedExport {
1928
+ /**
1929
+ * File path (relative to project root).
1930
+ */
1931
+ path: string
1932
+ /**
1933
+ * Export name.
1934
+ */
1935
+ export_name: string
1936
+ /**
1937
+ * 1-based source line.
1938
+ */
1939
+ line: number
1940
+ /**
1941
+ * 0-based source column.
1942
+ */
1943
+ col: number
1944
+ }
1945
+ /**
1946
+ * 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.
1947
+ */
1948
+ export interface RuntimeCoverageReport {
1949
+ /**
1950
+ * Runtime coverage JSON contract version. This is scoped to the runtime_coverage block and is independent of the top-level fallow JSON schema_version.
1951
+ */
1952
+ schema_version: "1"
1953
+ verdict: RuntimeCoverageReportVerdict
1954
+ /**
1955
+ * 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.
1956
+ */
1957
+ signals?: RuntimeCoverageSignal[]
1958
+ summary: RuntimeCoverageSummary
1959
+ /**
1960
+ * 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.
1961
+ */
1962
+ findings?: RuntimeCoverageFinding[]
1963
+ /**
1964
+ * Top runtime functions by invocation count. Omitted when empty.
1965
+ */
1966
+ hot_paths?: RuntimeCoverageHotPath[]
1967
+ /**
1968
+ * First-class blast-radius entries for runtime-observed functions. Present whenever runtime coverage analysis runs.
1969
+ */
1970
+ blast_radius?: RuntimeCoverageBlastRadiusEntry[]
1971
+ /**
1972
+ * First-class production-importance entries for runtime-observed functions. Present whenever runtime coverage analysis runs.
1973
+ */
1974
+ importance?: RuntimeCoverageImportanceEntry[]
1975
+ watermark?: RuntimeCoverageWatermark
1976
+ /**
1977
+ * Non-fatal merge or coverage diagnostics. Omitted when empty.
1978
+ */
1979
+ warnings?: RuntimeCoverageMessage[]
1980
+ }
1981
+ export interface RuntimeCoverageSummary {
1982
+ /**
1983
+ * 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.
1984
+ */
1985
+ data_source: ("local" | "cloud")
1986
+ /**
1987
+ * Timestamp of the newest runtime payload included in the report. Null for local single-capture artifacts that do not carry cloud receipt metadata.
1988
+ */
1989
+ last_received_at: (string | null)
1990
+ /**
1991
+ * Number of functions the sidecar could observe in the V8 or Istanbul dump.
1992
+ */
1993
+ functions_tracked: number
1994
+ /**
1995
+ * Tracked functions that received at least one invocation.
1996
+ */
1997
+ functions_hit: number
1998
+ /**
1999
+ * Tracked functions that were never invoked.
2000
+ */
2001
+ functions_unhit: number
2002
+ /**
2003
+ * Functions the sidecar could not track (lazy-parsed, worker thread, dynamic code, unresolved source map).
2004
+ */
2005
+ functions_untracked: number
2006
+ /**
2007
+ * Ratio of functions_hit / functions_tracked, expressed as a percent.
2008
+ */
2009
+ coverage_percent: number
2010
+ /**
2011
+ * Total number of observed invocations across all functions. Denominator for low-traffic classification.
2012
+ */
2013
+ trace_count: number
2014
+ /**
2015
+ * Days of observation covered by the supplied dump (Phase 2 local analysis emits 0 — set by the beacon/cloud in Phase 3+).
2016
+ */
2017
+ period_days: number
2018
+ /**
2019
+ * Distinct deployments contributing to the supplied dump (Phase 2 local analysis emits 0).
2020
+ */
2021
+ deployments_seen: number
2022
+ capture_quality?: RuntimeCoverageCaptureQuality
2023
+ }
2024
+ /**
2025
+ * Quality-of-capture signals emitted by the sidecar so the CLI can explain short-window captures honestly instead of letting users blame the tool.
2026
+ */
2027
+ export interface RuntimeCoverageCaptureQuality {
2028
+ /**
2029
+ * Total observation window in seconds. Finer-grained than period_days (which rounds up to whole days).
2030
+ */
2031
+ window_seconds: number
2032
+ /**
2033
+ * Number of distinct production instances that contributed to the dump.
2034
+ */
2035
+ instances_observed: number
2036
+ /**
2037
+ * 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.
2038
+ */
2039
+ lazy_parse_warning: boolean
2040
+ /**
2041
+ * functions_untracked / functions_tracked as a percentage, rounded to 2 decimal places.
2042
+ */
2043
+ untracked_ratio_percent: number
2044
+ }
2045
+ export interface RuntimeCoverageFinding {
2046
+ /**
2047
+ * 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').
2048
+ */
2049
+ id: string
2050
+ /**
2051
+ * File path relative to the project root.
2052
+ */
2053
+ path: string
2054
+ /**
2055
+ * Static function name as reported in the merged coverage result.
2056
+ */
2057
+ function: string
2058
+ /**
2059
+ * 1-indexed line number the function starts on.
2060
+ */
2061
+ line: number
2062
+ verdict: RuntimeCoverageVerdict
2063
+ /**
2064
+ * Observed invocation count for the function. Omitted when the function is untracked (lazy-parsed, worker-thread isolate, dynamic code).
2065
+ */
2066
+ invocations?: number
2067
+ confidence: RuntimeCoverageConfidence
2068
+ evidence: RuntimeCoverageEvidence
2069
+ /**
2070
+ * Suggested actions for this finding. Omitted when empty.
2071
+ */
2072
+ actions?: RuntimeCoverageAction[]
2073
+ }
2074
+ /**
2075
+ * Supporting evidence for a runtime coverage finding. Mirrors the rows of the decision table.
2076
+ */
2077
+ export interface RuntimeCoverageEvidence {
2078
+ /**
2079
+ * `used` when the function is reachable in the module graph, `unused` otherwise.
2080
+ */
2081
+ static_status: ("used" | "unused")
2082
+ /**
2083
+ * `covered` when the project's test suite hits this function, `not_covered` otherwise.
2084
+ */
2085
+ test_coverage: ("covered" | "not_covered")
2086
+ /**
2087
+ * `tracked` when V8 observed the function, `untracked` otherwise.
2088
+ */
2089
+ v8_tracking: ("tracked" | "untracked")
2090
+ /**
2091
+ * Reason the function is untracked. Populated only when v8_tracking is `untracked`. Values: `lazy_parsed`, `worker_thread`, `dynamic_eval`, `unknown`.
2092
+ */
2093
+ untracked_reason?: string
2094
+ /**
2095
+ * Days of observation backing this finding.
2096
+ */
2097
+ observation_days: number
2098
+ /**
2099
+ * Distinct deployments backing this finding.
2100
+ */
2101
+ deployments_observed: number
2102
+ }
2103
+ /**
2104
+ * Suggested follow-up action for a runtime coverage finding.
2105
+ */
2106
+ export interface RuntimeCoverageAction {
2107
+ /**
2108
+ * 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.
2109
+ */
2110
+ type: string
2111
+ /**
2112
+ * Human-readable recommendation.
2113
+ */
2114
+ description: string
2115
+ /**
2116
+ * Whether fallow can apply this action automatically.
2117
+ */
2118
+ auto_fixable: boolean
2119
+ }
2120
+ export interface RuntimeCoverageHotPath {
2121
+ /**
2122
+ * Stable content-hash ID of the form `fallow:hot:<hash>`.
2123
+ */
2124
+ id: string
2125
+ /**
2126
+ * File path relative to the project root.
2127
+ */
2128
+ path: string
2129
+ /**
2130
+ * Function name for the hot path.
2131
+ */
2132
+ function: string
2133
+ /**
2134
+ * 1-indexed line number the function starts on.
2135
+ */
2136
+ line: number
2137
+ /**
2138
+ * 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`.
2139
+ */
2140
+ end_line: number
2141
+ /**
2142
+ * Observed invocation count for the hot path.
2143
+ */
2144
+ invocations: number
2145
+ /**
2146
+ * Percentile rank over this response's hot-path distribution. 100 = busiest, 0 = quietest that still qualified.
2147
+ */
2148
+ percentile: number
2149
+ /**
2150
+ * Suggested actions for this hot path (e.g., review-on-change). Omitted when empty.
2151
+ */
2152
+ actions?: RuntimeCoverageAction[]
2153
+ }
2154
+ export interface RuntimeCoverageBlastRadiusEntry {
2155
+ /**
2156
+ * Stable content-hash ID of the form `fallow:blast:<hash>`.
2157
+ */
2158
+ id: string
2159
+ /**
2160
+ * File path relative to the project root.
2161
+ */
2162
+ file: string
2163
+ /**
2164
+ * Function name for the blast-radius entry.
2165
+ */
2166
+ function: string
2167
+ /**
2168
+ * 1-indexed line number the function starts on.
2169
+ */
2170
+ line: number
2171
+ /**
2172
+ * Static caller count from the module graph.
2173
+ */
2174
+ caller_count: number
2175
+ /**
2176
+ * Caller reach weighted by observed runtime traffic.
2177
+ */
2178
+ caller_count_weighted_by_traffic: number
2179
+ /**
2180
+ * Distinct deploy SHAs that touched the function in the observation window. Cloud mode only; omitted in local mode.
2181
+ */
2182
+ deploys_touched?: number
2183
+ risk_band: RuntimeCoverageRiskBand
2184
+ }
2185
+ export interface RuntimeCoverageImportanceEntry {
2186
+ /**
2187
+ * Stable content-hash ID of the form `fallow:importance:<hash>`.
2188
+ */
2189
+ id: string
2190
+ /**
2191
+ * File path relative to the project root.
2192
+ */
2193
+ file: string
2194
+ /**
2195
+ * Function name for the importance entry.
2196
+ */
2197
+ function: string
2198
+ /**
2199
+ * 1-indexed line number the function starts on.
2200
+ */
2201
+ line: number
2202
+ /**
2203
+ * Observed invocation count for this function.
2204
+ */
2205
+ invocations: number
2206
+ /**
2207
+ * Cyclomatic complexity from the static health pipeline.
2208
+ */
2209
+ cyclomatic: number
2210
+ /**
2211
+ * Number of CODEOWNERS owners matched for this file. Zero means no owner was resolved.
2212
+ */
2213
+ owner_count: number
2214
+ /**
2215
+ * 0-100 explainable score from log-scaled traffic, capped complexity weight, and ownership-risk weight.
2216
+ */
2217
+ importance_score: number
2218
+ /**
2219
+ * Templated one-sentence explanation for the score.
2220
+ */
2221
+ reason: string
2222
+ }
2223
+ export interface RuntimeCoverageMessage {
2224
+ /**
2225
+ * Stable warning code.
2226
+ */
2227
+ code: string
2228
+ /**
2229
+ * Human-readable warning message.
2230
+ */
2231
+ message: string
2232
+ }
2233
+ /**
2234
+ * 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.
2235
+ */
2236
+ export interface HealthGroup {
2237
+ /**
2238
+ * 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.
2239
+ */
2240
+ key: string
2241
+ /**
2242
+ * Section default owners (GitLab CODEOWNERS `[Section] @owner1 @owner2`). Present only when grouped_by is 'section'.
2243
+ */
2244
+ owners?: string[]
2245
+ /**
2246
+ * Number of files in this group after workspace, ignore, and changed-since filters.
2247
+ */
2248
+ files_analyzed: number
2249
+ /**
2250
+ * Number of findings rendered for this group (post-baseline / post-`--top` truncation, mirroring HealthSummary semantics).
2251
+ */
2252
+ functions_above_threshold: number
2253
+ vital_signs?: VitalSigns
2254
+ health_score?: HealthScore
2255
+ /**
2256
+ * Findings restricted to files in this group. Omitted when empty.
2257
+ */
2258
+ findings?: HealthFinding[]
2259
+ /**
2260
+ * File scores restricted to files in this group. Omitted when empty.
2261
+ */
2262
+ file_scores?: FileHealthScore[]
2263
+ /**
2264
+ * Hotspots restricted to files in this group. Omitted when empty.
2265
+ */
2266
+ hotspots?: HotspotEntry[]
2267
+ /**
2268
+ * Large functions in files belonging to this group. Omitted when empty.
2269
+ */
2270
+ large_functions?: LargeFunctionEntry[]
2271
+ /**
2272
+ * Refactoring targets in files belonging to this group. Omitted when empty.
2273
+ */
2274
+ targets?: RefactoringTarget[]
2275
+ }
2276
+ /**
2277
+ * 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.
2278
+ */
2279
+ export interface CheckGroupedOutput {
2280
+ schema_version: SchemaVersion
2281
+ version: ToolVersion
2282
+ elapsed_ms: ElapsedMs
2283
+ /**
2284
+ * 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.
2285
+ */
2286
+ grouped_by: ("owner" | "directory" | "package" | "section")
2287
+ /**
2288
+ * Total number of issues across all groups.
2289
+ */
2290
+ total_issues: number
2291
+ /**
2292
+ * One entry per group, each containing the same issue arrays as the ungrouped CheckOutput.
2293
+ */
2294
+ groups: CheckGroupedEntry[]
2295
+ _meta?: Meta
2296
+ }
2297
+ /**
2298
+ * A single group within grouped dead code output. Contains the group key, a per-group total, and the same issue arrays as CheckOutput.
2299
+ */
2300
+ export interface CheckGroupedEntry {
2301
+ /**
2302
+ * 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.
2303
+ */
2304
+ key: string
2305
+ /**
2306
+ * Section default owners (GitLab CODEOWNERS `[Section] @owner1 @owner2`). Emitted only when grouped_by is 'section'. Empty for the '(no section)' and '(unowned)' buckets.
2307
+ */
2308
+ owners?: string[]
2309
+ /**
2310
+ * Total number of issues in this group.
2311
+ */
2312
+ total_issues: number
2313
+ unused_files: UnusedFile[]
2314
+ unused_exports: UnusedExport[]
2315
+ unused_types: UnusedExport[]
2316
+ private_type_leaks: PrivateTypeLeak[]
2317
+ unused_dependencies: UnusedDependency[]
2318
+ unused_dev_dependencies: UnusedDependency[]
2319
+ unused_optional_dependencies: UnusedDependency[]
2320
+ unused_enum_members: UnusedMember[]
2321
+ unused_class_members: UnusedMember[]
2322
+ unresolved_imports: UnresolvedImport[]
2323
+ unlisted_dependencies: UnlistedDependency[]
2324
+ duplicate_exports: DuplicateExport[]
2325
+ type_only_dependencies: TypeOnlyDependency[]
2326
+ test_only_dependencies: TestOnlyDependency[]
2327
+ circular_dependencies: CircularDependency[]
2328
+ boundary_violations: BoundaryViolation[]
2329
+ stale_suppressions?: StaleSuppression[]
2330
+ }
2331
+ /**
2332
+ * 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.
2333
+ */
2334
+ export interface AuditOutput {
2335
+ schema_version: SchemaVersion
2336
+ version: ToolVersion
2337
+ command: "audit"
2338
+ /**
2339
+ * pass = no issues, warn = warn-severity only (exit 0), fail = error-severity issues (exit 1)
2340
+ */
2341
+ verdict: ("pass" | "warn" | "fail")
2342
+ /**
2343
+ * Number of files changed between base ref and HEAD
2344
+ */
2345
+ changed_files_count: number
2346
+ /**
2347
+ * Git ref used as comparison base (explicit or auto-detected)
2348
+ */
2349
+ base_ref: string
2350
+ /**
2351
+ * Short SHA of HEAD (omitted if git unavailable)
2352
+ */
2353
+ head_sha?: string
2354
+ elapsed_ms: ElapsedMs
2355
+ /**
2356
+ * 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.
2357
+ */
2358
+ base_snapshot_skipped?: boolean
2359
+ summary: {
2360
+ dead_code_issues?: number
2361
+ dead_code_has_errors?: boolean
2362
+ complexity_findings?: number
2363
+ max_cyclomatic?: (number | null)
2364
+ duplication_clone_groups?: number
2365
+ }
2366
+ /**
2367
+ * 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.
2368
+ */
2369
+ attribution: {
2370
+ /**
2371
+ * Which findings affect the audit verdict.
2372
+ */
2373
+ gate: ("new-only" | "all")
2374
+ dead_code_introduced: number
2375
+ dead_code_inherited: number
2376
+ complexity_introduced: number
2377
+ complexity_inherited: number
2378
+ duplication_introduced: number
2379
+ duplication_inherited: number
2380
+ }
2381
+ dead_code?: CheckOutput
2382
+ duplication?: DupesOutput
2383
+ complexity?: HealthOutput
2384
+ }
2385
+ /**
2386
+ * 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.
2387
+ */
2388
+ export interface ExplainOutput {
2389
+ /**
2390
+ * Canonical rule id, for example fallow/unused-export.
2391
+ */
2392
+ id: string
2393
+ /**
2394
+ * Human-readable rule name.
2395
+ */
2396
+ name: string
2397
+ /**
2398
+ * Short one-line explanation of the issue.
2399
+ */
2400
+ summary: string
2401
+ /**
2402
+ * Why the issue matters and what fallow checks.
2403
+ */
2404
+ rationale: string
2405
+ /**
2406
+ * Concrete example of the finding.
2407
+ */
2408
+ example: string
2409
+ /**
2410
+ * Recommended fix or suppression guidance.
2411
+ */
2412
+ how_to_fix: string
2413
+ /**
2414
+ * Docs URL for the rule.
2415
+ */
2416
+ docs: string
2417
+ }
2418
+ /**
2419
+ * 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.
2420
+ */
2421
+ export interface CoverageSetupOutput {
2422
+ schema_version: "1"
2423
+ /**
2424
+ * Primary detected runtime framework. For workspaces this mirrors the first emitted runtime member; unknown means no runtime member was detected.
2425
+ */
2426
+ framework_detected: ("nextjs" | "nestjs" | "nuxt" | "sveltekit" | "astro" | "remix" | "vite" | "plain_node" | "unknown")
2427
+ package_manager: ("npm" | "pnpm" | "yarn" | "bun" | null)
2428
+ /**
2429
+ * Union of runtime targets across emitted members.
2430
+ */
2431
+ runtime_targets: ("node" | "browser")[]
2432
+ /**
2433
+ * Per-runtime-workspace setup recipes. Pure aggregator roots and build-only library packages are omitted.
2434
+ */
2435
+ members: CoverageSetupMember[]
2436
+ config_written: null
2437
+ commands: string[]
2438
+ /**
2439
+ * Compatibility copy of the primary member's files, with workspace prefixes when the primary member is not the root.
2440
+ */
2441
+ files_to_edit: CoverageSetupFileToEdit[]
2442
+ /**
2443
+ * Compatibility copy of the primary member's snippets, with workspace prefixes when the primary member is not the root.
2444
+ */
2445
+ snippets: CoverageSetupSnippet[]
2446
+ dockerfile_snippet: (string | null)
2447
+ next_steps: string[]
2448
+ warnings: string[]
2449
+ /**
2450
+ * Optional explain metadata emitted only with fallow coverage setup --json --explain.
2451
+ */
2452
+ _meta?: {
2453
+ [k: string]: unknown
2454
+ }
2455
+ }
2456
+ export interface CoverageSetupMember {
2457
+ name: string
2458
+ /**
2459
+ * Workspace path relative to the analyzed root, or . for the root member.
2460
+ */
2461
+ path: string
2462
+ framework_detected: ("nextjs" | "nestjs" | "nuxt" | "sveltekit" | "astro" | "remix" | "vite" | "plain_node" | "unknown")
2463
+ package_manager: ("npm" | "pnpm" | "yarn" | "bun" | null)
2464
+ runtime_targets: ("node" | "browser")[]
2465
+ files_to_edit: CoverageSetupFileToEdit[]
2466
+ snippets: CoverageSetupSnippet[]
2467
+ dockerfile_snippet: (string | null)
2468
+ warnings: string[]
2469
+ }
2470
+ export interface CoverageSetupFileToEdit {
2471
+ path: string
2472
+ reason: string
2473
+ }
2474
+ export interface CoverageSetupSnippet {
2475
+ label: string
2476
+ path: string
2477
+ content: string
2478
+ }
2479
+ export interface CodeClimateIssue {
2480
+ type: "issue"
2481
+ check_name: string
2482
+ description: string
2483
+ categories: string[]
2484
+ severity: ("info" | "minor" | "major" | "critical" | "blocker")
2485
+ fingerprint: string
2486
+ location: {
2487
+ path: string
2488
+ lines: {
2489
+ begin: number
2490
+ }
2491
+ }
2492
+ }
2493
+ /**
2494
+ * Typed review envelope consumed by action/scripts/review.sh and ci/scripts/review.sh.
2495
+ */
2496
+ export interface ReviewEnvelopeOutput {
2497
+ /**
2498
+ * GitHub review event. Omitted for GitLab.
2499
+ */
2500
+ event?: "COMMENT"
2501
+ body: string
2502
+ comments: (GitHubReviewComment | GitLabReviewComment)[]
2503
+ meta: {
2504
+ schema: "fallow-review-envelope/v1"
2505
+ provider: ("github" | "gitlab")
2506
+ check_conclusion?: ("success" | "neutral" | "failure")
2507
+ }
2508
+ }
2509
+ export interface GitHubReviewComment {
2510
+ path: string
2511
+ line: number
2512
+ /**
2513
+ * GitHub review comments target current-state/new-side lines; deletion-side comments are not modeled yet.
2514
+ */
2515
+ side: "RIGHT"
2516
+ body: string
2517
+ fingerprint: string
2518
+ }
2519
+ export interface GitLabReviewComment {
2520
+ body: string
2521
+ position: GitLabReviewPosition
2522
+ fingerprint: string
2523
+ }
2524
+ export interface GitLabReviewPosition {
2525
+ base_sha?: string
2526
+ start_sha?: string
2527
+ head_sha?: string
2528
+ position_type: "text"
2529
+ old_path: string
2530
+ new_path: string
2531
+ new_line: number
2532
+ }
2533
+ export interface ReviewReconcileOutput {
2534
+ schema: "fallow-review-reconcile/v1"
2535
+ provider: ("github" | "gitlab")
2536
+ target: (string | null)
2537
+ dry_run: boolean
2538
+ comments: number
2539
+ current_fingerprints: number
2540
+ existing_fingerprints: number
2541
+ new_fingerprints: number
2542
+ stale_fingerprints: number
2543
+ new: string[]
2544
+ stale: string[]
2545
+ provider_warning?: (string | null)
2546
+ resolution_comments_posted: number
2547
+ threads_resolved: number
2548
+ apply_errors: string[]
2549
+ }