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.
- package/README.md +8 -0
- package/package.json +28 -10
- package/schema.json +59 -1
- package/skills/fallow/SKILL.md +47 -47
- package/skills/fallow/references/cli-reference.md +12 -10
- package/types/output-contract.d.ts +2549 -0
|
@@ -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
|
+
}
|