fcis 0.1.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.
Files changed (151) hide show
  1. package/.plans/001-fcis-analyzer.md +832 -0
  2. package/.plans/002-fcis-analyzer-improvements.md +205 -0
  3. package/README.md +272 -0
  4. package/TECHNICAL.md +386 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +1836 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/index.d.ts +709 -0
  9. package/dist/index.js +1845 -0
  10. package/dist/index.js.map +1 -0
  11. package/package.json +47 -0
  12. package/pnpm-workspace.yaml +0 -0
  13. package/src/analyzer.ts +266 -0
  14. package/src/classification/classifier.ts +156 -0
  15. package/src/classification/derive-status.ts +171 -0
  16. package/src/classification/quality-scorer.ts +481 -0
  17. package/src/cli.ts +286 -0
  18. package/src/detection/detect-markers.ts +480 -0
  19. package/src/detection/markers.ts +332 -0
  20. package/src/extraction/extract-functions.ts +570 -0
  21. package/src/extraction/extractor.ts +188 -0
  22. package/src/index.ts +111 -0
  23. package/src/reporting/report-console.ts +416 -0
  24. package/src/reporting/report-json.ts +232 -0
  25. package/src/scoring/scorer.ts +504 -0
  26. package/src/types.ts +248 -0
  27. package/tests/classifier.test.ts +480 -0
  28. package/tests/derive-status.test.ts +464 -0
  29. package/tests/detect-markers.test.ts +639 -0
  30. package/tests/extractor.test.ts +155 -0
  31. package/tests/integration.test.ts +706 -0
  32. package/tests/quality-scorer.test.ts +650 -0
  33. package/tests/scorer.test.ts +768 -0
  34. package/tsconfig.json +34 -0
  35. package/tsup.config.ts +17 -0
  36. package/vendor/ts-morph/.editorconfig +10 -0
  37. package/vendor/ts-morph/.gitattributes +11 -0
  38. package/vendor/ts-morph/.github/CODE_OF_CONDUCT.md +77 -0
  39. package/vendor/ts-morph/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  40. package/vendor/ts-morph/.github/ISSUE_TEMPLATE/custom.md +4 -0
  41. package/vendor/ts-morph/.github/ISSUE_TEMPLATE/feature_request.md +18 -0
  42. package/vendor/ts-morph/.github/workflows/ci.yml +50 -0
  43. package/vendor/ts-morph/.github/workflows/publish.yml +53 -0
  44. package/vendor/ts-morph/.vscode/settings.json +10 -0
  45. package/vendor/ts-morph/CONTRIBUTING.md +23 -0
  46. package/vendor/ts-morph/DEVELOPMENT.md +32 -0
  47. package/vendor/ts-morph/LICENSE +21 -0
  48. package/vendor/ts-morph/deno.json +8 -0
  49. package/vendor/ts-morph/deno.lock +1233 -0
  50. package/vendor/ts-morph/docs/CNAME +1 -0
  51. package/vendor/ts-morph/docs/Gemfile +2 -0
  52. package/vendor/ts-morph/docs/_config.yml +5 -0
  53. package/vendor/ts-morph/docs/_layouts/default.html +159 -0
  54. package/vendor/ts-morph/docs/_script-templates/main.ts +116 -0
  55. package/vendor/ts-morph/docs/assets/css/style.scss +212 -0
  56. package/vendor/ts-morph/docs/details/ambient.md +38 -0
  57. package/vendor/ts-morph/docs/details/async.md +31 -0
  58. package/vendor/ts-morph/docs/details/classes.md +314 -0
  59. package/vendor/ts-morph/docs/details/comment-ranges.md +7 -0
  60. package/vendor/ts-morph/docs/details/comments.md +122 -0
  61. package/vendor/ts-morph/docs/details/decorators.md +119 -0
  62. package/vendor/ts-morph/docs/details/documentation.md +73 -0
  63. package/vendor/ts-morph/docs/details/enums.md +117 -0
  64. package/vendor/ts-morph/docs/details/exports.md +308 -0
  65. package/vendor/ts-morph/docs/details/expressions.md +46 -0
  66. package/vendor/ts-morph/docs/details/functions.md +150 -0
  67. package/vendor/ts-morph/docs/details/generators.md +27 -0
  68. package/vendor/ts-morph/docs/details/identifiers.md +79 -0
  69. package/vendor/ts-morph/docs/details/imports.md +191 -0
  70. package/vendor/ts-morph/docs/details/index.md +52 -0
  71. package/vendor/ts-morph/docs/details/initializers.md +40 -0
  72. package/vendor/ts-morph/docs/details/interfaces.md +218 -0
  73. package/vendor/ts-morph/docs/details/literals.md +20 -0
  74. package/vendor/ts-morph/docs/details/modifiers.md +38 -0
  75. package/vendor/ts-morph/docs/details/modules.md +113 -0
  76. package/vendor/ts-morph/docs/details/namespaces.md +7 -0
  77. package/vendor/ts-morph/docs/details/object-literal-expressions.md +106 -0
  78. package/vendor/ts-morph/docs/details/parameters.md +64 -0
  79. package/vendor/ts-morph/docs/details/signatures.md +41 -0
  80. package/vendor/ts-morph/docs/details/source-files.md +292 -0
  81. package/vendor/ts-morph/docs/details/type-aliases.md +34 -0
  82. package/vendor/ts-morph/docs/details/type-parameters.md +72 -0
  83. package/vendor/ts-morph/docs/details/types.md +254 -0
  84. package/vendor/ts-morph/docs/details/variables.md +110 -0
  85. package/vendor/ts-morph/docs/emitting.md +151 -0
  86. package/vendor/ts-morph/docs/index.md +25 -0
  87. package/vendor/ts-morph/docs/manipulation/code-writer.md +20 -0
  88. package/vendor/ts-morph/docs/manipulation/formatting.md +76 -0
  89. package/vendor/ts-morph/docs/manipulation/index.md +136 -0
  90. package/vendor/ts-morph/docs/manipulation/order.md +14 -0
  91. package/vendor/ts-morph/docs/manipulation/performance.md +222 -0
  92. package/vendor/ts-morph/docs/manipulation/removing.md +31 -0
  93. package/vendor/ts-morph/docs/manipulation/renaming.md +106 -0
  94. package/vendor/ts-morph/docs/manipulation/settings.md +76 -0
  95. package/vendor/ts-morph/docs/manipulation/structures.md +117 -0
  96. package/vendor/ts-morph/docs/manipulation/transforms.md +84 -0
  97. package/vendor/ts-morph/docs/metrics/performance.json +4 -0
  98. package/vendor/ts-morph/docs/navigation/ambient-modules.md +22 -0
  99. package/vendor/ts-morph/docs/navigation/compiler-nodes.md +82 -0
  100. package/vendor/ts-morph/docs/navigation/directories.md +287 -0
  101. package/vendor/ts-morph/docs/navigation/example.md +50 -0
  102. package/vendor/ts-morph/docs/navigation/finding-references.md +53 -0
  103. package/vendor/ts-morph/docs/navigation/getting-source-files.md +59 -0
  104. package/vendor/ts-morph/docs/navigation/images/getChildrenVsForEachChild.gif +0 -0
  105. package/vendor/ts-morph/docs/navigation/index.md +94 -0
  106. package/vendor/ts-morph/docs/navigation/language-service.md +23 -0
  107. package/vendor/ts-morph/docs/navigation/program.md +25 -0
  108. package/vendor/ts-morph/docs/navigation/type-checker.md +33 -0
  109. package/vendor/ts-morph/docs/setup/adding-source-files.md +145 -0
  110. package/vendor/ts-morph/docs/setup/ast-viewers.md +46 -0
  111. package/vendor/ts-morph/docs/setup/diagnostics.md +109 -0
  112. package/vendor/ts-morph/docs/setup/file-system.md +106 -0
  113. package/vendor/ts-morph/docs/setup/images/atom-ast.png +0 -0
  114. package/vendor/ts-morph/docs/setup/images/atom-ast_small.png +0 -0
  115. package/vendor/ts-morph/docs/setup/images/atom-command-palette.png +0 -0
  116. package/vendor/ts-morph/docs/setup/images/atom-file.png +0 -0
  117. package/vendor/ts-morph/docs/setup/images/ts-ast-viewer.png +0 -0
  118. package/vendor/ts-morph/docs/setup/index.md +94 -0
  119. package/vendor/ts-morph/docs/utilities.md +55 -0
  120. package/vendor/ts-morph/dprint.json +23 -0
  121. package/vendor/ts-morph/package.json +30 -0
  122. package/vendor/ts-morph/packages/bootstrap/LICENSE +21 -0
  123. package/vendor/ts-morph/packages/bootstrap/lib/ts-morph-bootstrap.d.ts +397 -0
  124. package/vendor/ts-morph/packages/bootstrap/package.json +46 -0
  125. package/vendor/ts-morph/packages/bootstrap/readme.md +200 -0
  126. package/vendor/ts-morph/packages/common/LICENSE +21 -0
  127. package/vendor/ts-morph/packages/common/lib/ts-morph-common.d.ts +1082 -0
  128. package/vendor/ts-morph/packages/common/lib/typescript.d.ts +11439 -0
  129. package/vendor/ts-morph/packages/common/package.json +65 -0
  130. package/vendor/ts-morph/packages/common/readme.md +5 -0
  131. package/vendor/ts-morph/packages/scripts/changeTypeScriptVersion.ts +28 -0
  132. package/vendor/ts-morph/packages/scripts/createDeclarationProject.ts +47 -0
  133. package/vendor/ts-morph/packages/scripts/deps.ts +2 -0
  134. package/vendor/ts-morph/packages/scripts/execScript.ts +31 -0
  135. package/vendor/ts-morph/packages/scripts/folders.ts +11 -0
  136. package/vendor/ts-morph/packages/scripts/getDevCompilerVersions.ts +19 -0
  137. package/vendor/ts-morph/packages/scripts/mod.ts +7 -0
  138. package/vendor/ts-morph/packages/scripts/utils/Memoize.ts +36 -0
  139. package/vendor/ts-morph/packages/scripts/utils/forEachTypeText.ts +23 -0
  140. package/vendor/ts-morph/packages/scripts/utils/makeConstructorsPrivate.ts +26 -0
  141. package/vendor/ts-morph/packages/scripts/utils/mod.ts +4 -0
  142. package/vendor/ts-morph/packages/scripts/utils/printDiagnostics.ts +10 -0
  143. package/vendor/ts-morph/packages/ts-morph/LICENSE +21 -0
  144. package/vendor/ts-morph/packages/ts-morph/lib/ts-morph.d.ts +11198 -0
  145. package/vendor/ts-morph/packages/ts-morph/package.json +78 -0
  146. package/vendor/ts-morph/packages/ts-morph/readme.md +111 -0
  147. package/vendor/ts-morph/readme.md +14 -0
  148. package/vendor/ts-morph/rfcs/README.md +13 -0
  149. package/vendor/ts-morph/rfcs/RFC-0001 - Inserting Into Statements Handling Comments.md +181 -0
  150. package/vendor/ts-morph/tsconfig.common.json +17 -0
  151. package/vitest.config.ts +16 -0
@@ -0,0 +1,709 @@
1
+ import { SourceFile, Project } from 'ts-morph';
2
+
3
+ /**
4
+ * Core type definitions for the FCIS Analyzer
5
+ *
6
+ * These types form the contract between the analyzer's layers:
7
+ * - Extraction layer produces ExtractedFunction and FileImports
8
+ * - Detection layer produces ImpurityMarker[]
9
+ * - Classification layer produces ClassifiedFunction
10
+ * - Scoring layer produces FileScore, DirectoryScore, ProjectScore
11
+ */
12
+ /**
13
+ * CallSite captures position and context for each call expression,
14
+ * enabling sequence analysis (GATHER→DECIDE→EXECUTE detection)
15
+ */
16
+ type CallSite = {
17
+ expression: string;
18
+ line: number;
19
+ isAwaited: boolean;
20
+ };
21
+ /**
22
+ * Represents a function extracted from source code
23
+ */
24
+ type ExtractedFunction = {
25
+ name: string | null;
26
+ filePath: string;
27
+ startLine: number;
28
+ endLine: number;
29
+ isAsync: boolean;
30
+ isExported: boolean;
31
+ bodyLineCount: number;
32
+ statementCount: number;
33
+ hasConditionals: boolean;
34
+ parentContext: string | null;
35
+ callSites: CallSite[];
36
+ hasAwait: boolean;
37
+ propertyAccessChains: string[];
38
+ kind: 'function' | 'method' | 'arrow' | 'function-expression' | 'getter' | 'setter';
39
+ };
40
+ /**
41
+ * Represents imports in a file
42
+ */
43
+ type FileImports = {
44
+ filePath: string;
45
+ imports: {
46
+ moduleSpecifier: string;
47
+ namedImports: string[];
48
+ }[];
49
+ };
50
+ /**
51
+ * Strict marker type union — prevents typos, enables exhaustive matching
52
+ * Note: 'async-function' removed — async alone is not an impurity marker
53
+ * Note: React hook markers deferred to v2
54
+ */
55
+ type MarkerType = 'await-expression' | 'database-call' | 'network-fetch' | 'network-http' | 'fs-import' | 'fs-call' | 'env-access' | 'console-log' | 'logging' | 'telemetry' | 'queue-enqueue' | 'event-emit';
56
+ /**
57
+ * Represents a detected impurity marker in a function
58
+ */
59
+ type ImpurityMarker = {
60
+ type: MarkerType;
61
+ detail: string;
62
+ line?: number;
63
+ };
64
+ /**
65
+ * Binary classification — objective, no heuristics
66
+ */
67
+ type FunctionClassification = 'pure' | 'impure';
68
+ /**
69
+ * Derived status for UX — actionable guidance
70
+ */
71
+ type Status = 'ok' | 'review' | 'refactor';
72
+ /**
73
+ * A function that has been classified with markers, classification, quality score, and status
74
+ */
75
+ type ClassifiedFunction = ExtractedFunction & {
76
+ markers: ImpurityMarker[];
77
+ classification: FunctionClassification;
78
+ qualityScore: number | null;
79
+ status: Status;
80
+ };
81
+ /**
82
+ * Breakdown of function statuses
83
+ */
84
+ type StatusBreakdown = {
85
+ ok: number;
86
+ review: number;
87
+ refactor: number;
88
+ };
89
+ /**
90
+ * Refactoring candidate summary for reporting
91
+ */
92
+ type RefactoringCandidate = {
93
+ name: string | null;
94
+ filePath: string;
95
+ startLine: number;
96
+ bodyLineCount: number;
97
+ qualityScore: number;
98
+ markers: MarkerType[];
99
+ };
100
+ /**
101
+ * Score for a single file
102
+ */
103
+ type FileScore = {
104
+ filePath: string;
105
+ purity: number;
106
+ impurityQuality: number | null;
107
+ health: number;
108
+ pureCount: number;
109
+ impureCount: number;
110
+ excludedCount: number;
111
+ statusBreakdown: StatusBreakdown;
112
+ pureLineCount: number;
113
+ impureLineCount: number;
114
+ functions: ClassifiedFunction[];
115
+ refactoringCandidates: Omit<RefactoringCandidate, 'filePath'>[];
116
+ allExcluded?: boolean;
117
+ typeOnly?: boolean;
118
+ };
119
+ /**
120
+ * Score for a directory
121
+ */
122
+ type DirectoryScore = {
123
+ dirPath: string;
124
+ purity: number;
125
+ impurityQuality: number | null;
126
+ health: number;
127
+ pureCount: number;
128
+ impureCount: number;
129
+ excludedCount: number;
130
+ statusBreakdown: StatusBreakdown;
131
+ pureLineCount: number;
132
+ impureLineCount: number;
133
+ fileScores: FileScore[];
134
+ };
135
+ /**
136
+ * Project-level score (top-level output)
137
+ */
138
+ type ProjectScore = {
139
+ purity: number;
140
+ impurityQuality: number | null;
141
+ health: number;
142
+ pureCount: number;
143
+ impureCount: number;
144
+ excludedCount: number;
145
+ statusBreakdown: StatusBreakdown;
146
+ pureLineCount: number;
147
+ impureLineCount: number;
148
+ directoryScores: DirectoryScore[];
149
+ timestamp: string;
150
+ commitHash: string | null;
151
+ refactoringCandidates: RefactoringCandidate[];
152
+ allExcluded?: boolean;
153
+ subset?: boolean;
154
+ filesGlob?: string;
155
+ errors?: AnalysisError[];
156
+ };
157
+ /**
158
+ * Error encountered during analysis
159
+ */
160
+ type AnalysisError = {
161
+ filePath: string;
162
+ error: string;
163
+ };
164
+ /**
165
+ * Configuration options for the analyzer
166
+ */
167
+ type AnalyzerConfig = {
168
+ tsconfigPath: string;
169
+ filesGlob?: string;
170
+ minHealth?: number;
171
+ minPurity?: number;
172
+ minQuality?: number;
173
+ format?: 'console' | 'json' | 'summary';
174
+ outputPath?: string;
175
+ quiet?: boolean;
176
+ verbose?: boolean;
177
+ };
178
+ /**
179
+ * Quality scoring thresholds (configurable)
180
+ */
181
+ type QualityThresholds = {
182
+ okThreshold: number;
183
+ reviewThreshold: number;
184
+ };
185
+ /**
186
+ * Default quality thresholds
187
+ */
188
+ declare const DEFAULT_QUALITY_THRESHOLDS: QualityThresholds;
189
+ /**
190
+ * Result of extracting functions from a file
191
+ */
192
+ type ExtractionResult = {
193
+ filePath: string;
194
+ functions: ExtractedFunction[];
195
+ imports: FileImports;
196
+ isTypeOnly: boolean;
197
+ error?: string;
198
+ };
199
+ /**
200
+ * Detection context passed to marker detection
201
+ */
202
+ type DetectionContext = {
203
+ imports: FileImports;
204
+ pureFileImports: Set<string>;
205
+ };
206
+
207
+ /**
208
+ * Analyzer - Main Orchestration Layer (Shell)
209
+ *
210
+ * This module orchestrates the full analysis pipeline:
211
+ * 1. Load project via ts-morph
212
+ * 2. Extract functions from source files
213
+ * 3. Detect impurity markers
214
+ * 4. Classify functions
215
+ * 5. Compute quality scores
216
+ * 6. Aggregate into file, directory, and project scores
217
+ *
218
+ * This is a SHELL module - it coordinates I/O and calls pure functions.
219
+ */
220
+
221
+ /**
222
+ * Run the full analysis pipeline
223
+ *
224
+ * @param config - Analyzer configuration
225
+ * @returns Project score with all metrics
226
+ */
227
+ declare function analyze(config: AnalyzerConfig): Promise<ProjectScore>;
228
+ /**
229
+ * Analyze a single source file
230
+ *
231
+ * @param sourceFile - The ts-morph source file to analyze
232
+ * @returns FileScore with all metrics
233
+ */
234
+ declare function analyzeFile(sourceFile: SourceFile): FileScore;
235
+ /**
236
+ * Analyze a single file from a file path (convenience function)
237
+ *
238
+ * @param tsconfigPath - Path to tsconfig.json
239
+ * @param filePath - Path to the file to analyze
240
+ * @returns FileScore with all metrics
241
+ */
242
+ declare function analyzeFilePath(tsconfigPath: string, filePath: string): Promise<FileScore | null>;
243
+ /**
244
+ * Check if analysis passes thresholds
245
+ *
246
+ * @param score - Project score to check
247
+ * @param config - Configuration with threshold values
248
+ * @returns Object indicating pass/fail status and messages
249
+ */
250
+ declare function checkThresholds(score: ProjectScore, config: Pick<AnalyzerConfig, 'minHealth' | 'minPurity' | 'minQuality'>): {
251
+ passed: boolean;
252
+ failures: string[];
253
+ };
254
+
255
+ /**
256
+ * Detect all impurity markers in a function
257
+ *
258
+ * @param fn - The extracted function to analyze
259
+ * @param context - Detection context including imports
260
+ * @returns Array of detected impurity markers
261
+ */
262
+ declare function detectMarkers(fn: ExtractedFunction, context: DetectionContext): ImpurityMarker[];
263
+ /**
264
+ * Create detection context from file imports
265
+ */
266
+ declare function createDetectionContext(imports: FileImports): DetectionContext;
267
+ /**
268
+ * Get the names of functions imported from .pure files
269
+ */
270
+ declare function getPureImportedFunctions(context: DetectionContext): Set<string>;
271
+
272
+ /**
273
+ * Marker Definitions and Catalog
274
+ *
275
+ * This module defines the impurity marker types and provides a catalog
276
+ * of patterns for detecting each marker type. This is PURE code - all
277
+ * functions take data in and return data out with no I/O.
278
+ *
279
+ * The marker catalog is defined as a data structure to enable:
280
+ * - Extensibility (custom markers can be added)
281
+ * - Documentation (each marker has a description)
282
+ * - Testing (patterns can be validated in isolation)
283
+ */
284
+
285
+ /**
286
+ * Marker definition with detection patterns
287
+ */
288
+ type MarkerDefinition = {
289
+ type: MarkerType;
290
+ description: string;
291
+ category: 'io' | 'state' | 'side-effect' | 'environment';
292
+ severity: 'high' | 'medium' | 'low';
293
+ };
294
+ /**
295
+ * Marker catalog - all recognized impurity markers
296
+ */
297
+ declare const MARKER_CATALOG: Record<MarkerType, MarkerDefinition>;
298
+ /**
299
+ * Get marker definition by type
300
+ */
301
+ declare function getMarkerDefinition(type: MarkerType): MarkerDefinition;
302
+ /**
303
+ * Get all marker types
304
+ */
305
+ declare function getAllMarkerTypes(): MarkerType[];
306
+
307
+ /**
308
+ * Function Classifier - Pure Core
309
+ *
310
+ * Classifies functions as pure or impure based on their markers.
311
+ * This is a PURE module - all functions take data in and return data out.
312
+ *
313
+ * Classification is binary and objective:
314
+ * - Pure: Zero I/O markers
315
+ * - Impure: Has one or more I/O markers
316
+ *
317
+ * Note: `async` without `await` (and without other I/O markers) is still pure.
318
+ */
319
+
320
+ /**
321
+ * Classify a function based on its markers
322
+ *
323
+ * @param fn - The extracted function
324
+ * @param markers - Detected impurity markers
325
+ * @returns The function classification ('pure' or 'impure')
326
+ */
327
+ declare function classifyFunction(_fn: ExtractedFunction, markers: ImpurityMarker[]): FunctionClassification;
328
+ /**
329
+ * Check if a function should be excluded from scoring
330
+ *
331
+ * Excluded functions:
332
+ * - Trivial functions (< 3 statements and no conditionals)
333
+ * - Test files (should already be filtered at extraction)
334
+ * - Type-only files (should already be filtered at extraction)
335
+ */
336
+ declare function shouldExcludeFunction(fn: ExtractedFunction): boolean;
337
+ /**
338
+ * Create a fully classified function with all computed properties
339
+ *
340
+ * @param fn - The extracted function
341
+ * @param markers - Detected impurity markers
342
+ * @param qualityScore - Quality score (null for pure functions)
343
+ * @param status - Derived status
344
+ * @returns A fully classified function
345
+ */
346
+ declare function createClassifiedFunction(fn: ExtractedFunction, markers: ImpurityMarker[], qualityScore: number | null, status: Status): ClassifiedFunction;
347
+
348
+ /**
349
+ * Quality Scorer - Pure Core
350
+ *
351
+ * Computes quality scores (0-100) for impure functions measuring how
352
+ * well-structured the impurity is. This is a PURE module - all functions
353
+ * take data in and return data out with no I/O.
354
+ *
355
+ * Quality signals measure structural quality, not whether impurity exists.
356
+ * A high score means the impure function is well-organized and maintainable.
357
+ */
358
+
359
+ /**
360
+ * Quality scoring weights and thresholds
361
+ */
362
+ declare const QUALITY_WEIGHTS: {
363
+ readonly callsPureFile: 30;
364
+ readonly callsPureNamingConvention: 20;
365
+ readonly ioConcentratedAtStart: 15;
366
+ readonly ioConcentratedAtEnd: 15;
367
+ readonly lowComplexity: 10;
368
+ readonly shellNamingConvention: 5;
369
+ readonly callsPredicateFunctions: 5;
370
+ readonly ioInterleaved: -20;
371
+ readonly highComplexity: -15;
372
+ readonly multipleIoTypes: -10;
373
+ readonly noPureFunctionCalls: -10;
374
+ readonly veryLongFunction: -10;
375
+ };
376
+ /**
377
+ * Compute quality score for an impure function
378
+ *
379
+ * @param fn - The extracted function data
380
+ * @param markers - The detected impurity markers
381
+ * @param context - Detection context with import information
382
+ * @returns Quality score from 0-100
383
+ */
384
+ declare function computeQualityScore(fn: ExtractedFunction, markers: ImpurityMarker[], context: DetectionContext): number;
385
+ /**
386
+ * Analysis result for a function
387
+ */
388
+ type FunctionAnalysis = {
389
+ callsPureFile: boolean;
390
+ callsPureNamingConvention: boolean;
391
+ ioConcentratedAtStart: boolean;
392
+ ioConcentratedAtEnd: boolean;
393
+ lowComplexity: boolean;
394
+ shellNamingConvention: boolean;
395
+ callsPredicateFunctions: boolean;
396
+ ioInterleaved: boolean;
397
+ highComplexity: boolean;
398
+ multipleIoTypes: boolean;
399
+ noPureFunctionCalls: boolean;
400
+ veryLongFunction: boolean;
401
+ estimatedComplexity: number;
402
+ ioMarkerCount: number;
403
+ uniqueIoTypes: Set<MarkerType>;
404
+ pureCallCount: number;
405
+ predicateCallCount: number;
406
+ };
407
+ /**
408
+ * Analyze a function for quality signals
409
+ */
410
+ declare function analyzeFunction(fn: ExtractedFunction, markers: ImpurityMarker[], context: DetectionContext): FunctionAnalysis;
411
+
412
+ /**
413
+ * Status Derivation - Pure Core
414
+ *
415
+ * Derives actionable status from classification and quality score.
416
+ * This is a PURE module - all functions take data in and return data out with no I/O.
417
+ *
418
+ * Status provides developer guidance:
419
+ * - ok: No action needed (pure function, or well-structured impure function)
420
+ * - review: Consider improving if touching this code
421
+ * - refactor: Tangled code, prioritize cleanup
422
+ */
423
+
424
+ /**
425
+ * Derive status from classification and quality score
426
+ *
427
+ * Status rules:
428
+ * - Pure functions always get 'ok' (testable without mocks)
429
+ * - Impure functions with quality >= okThreshold (default 70) get 'ok'
430
+ * - Impure functions with quality >= reviewThreshold (default 40) get 'review'
431
+ * - Impure functions with quality < reviewThreshold get 'refactor'
432
+ *
433
+ * @param classification - 'pure' or 'impure'
434
+ * @param qualityScore - Quality score for impure functions (0-100), null for pure
435
+ * @param thresholds - Optional custom thresholds
436
+ * @returns Status: 'ok' | 'review' | 'refactor'
437
+ */
438
+ declare function deriveStatus(classification: FunctionClassification, qualityScore: number | null, thresholds?: QualityThresholds): Status;
439
+ /**
440
+ * Get status description for display
441
+ */
442
+ declare function getStatusDescription(status: Status): string;
443
+ /**
444
+ * Get status emoji for display
445
+ */
446
+ declare function getStatusEmoji(status: Status): string;
447
+ /**
448
+ * Get status color for terminal display
449
+ */
450
+ declare function getStatusColor(status: Status): 'green' | 'yellow' | 'red';
451
+
452
+ /**
453
+ * Scoring Engine - Pure Core
454
+ *
455
+ * Aggregates classification and quality scores into metrics at
456
+ * file, directory, and project levels. This is a PURE module -
457
+ * all functions take data in and return data out with no I/O.
458
+ *
459
+ * Metrics:
460
+ * - Purity: percentage of pure functions
461
+ * - Impurity Quality: average quality score of impure functions
462
+ * - Health: percentage of functions with status 'ok'
463
+ */
464
+
465
+ /**
466
+ * Score a single file based on its classified functions
467
+ *
468
+ * @param filePath - Path to the file
469
+ * @param functions - Classified functions from the file
470
+ * @param isTypeOnly - Whether the file contains only types
471
+ * @returns FileScore with all computed metrics
472
+ */
473
+ declare function scoreFile(filePath: string, functions: ClassifiedFunction[], isTypeOnly?: boolean): FileScore;
474
+ /**
475
+ * Score a directory by aggregating its file scores
476
+ *
477
+ * @param dirPath - Path to the directory
478
+ * @param fileScores - Scores for all files in the directory
479
+ * @returns DirectoryScore with weighted averages
480
+ */
481
+ declare function scoreDirectory(dirPath: string, fileScores: FileScore[]): DirectoryScore;
482
+ /**
483
+ * Score an entire project by aggregating directory scores
484
+ *
485
+ * @param directoryScores - Scores for all directories in the project
486
+ * @param options - Additional options for the project score
487
+ * @returns ProjectScore with all metrics
488
+ */
489
+ declare function scoreProject(directoryScores: DirectoryScore[], options?: {
490
+ timestamp?: string;
491
+ commitHash?: string | null;
492
+ subset?: boolean;
493
+ filesGlob?: string;
494
+ errors?: {
495
+ filePath: string;
496
+ error: string;
497
+ }[];
498
+ }): ProjectScore;
499
+ /**
500
+ * Group file scores by directory
501
+ *
502
+ * @param fileScores - Array of file scores
503
+ * @returns Map of directory path to file scores
504
+ */
505
+ declare function groupFilesByDirectory(fileScores: FileScore[]): Map<string, FileScore[]>;
506
+ /**
507
+ * Calculate delta between two project scores
508
+ *
509
+ * @param current - Current project score
510
+ * @param previous - Previous project score
511
+ * @returns Delta metrics
512
+ */
513
+ declare function calculateDelta(current: ProjectScore, previous: ProjectScore): {
514
+ purityDelta: number;
515
+ impurityQualityDelta: number | null;
516
+ healthDelta: number;
517
+ pureCountDelta: number;
518
+ impureCountDelta: number;
519
+ };
520
+ /**
521
+ * Get diagnostic insights based on metrics
522
+ *
523
+ * @param score - Project or file score
524
+ * @returns Array of insight messages
525
+ */
526
+ declare function getDiagnosticInsights(score: Pick<ProjectScore, 'purity' | 'impurityQuality' | 'health'>): string[];
527
+
528
+ /**
529
+ * JSON Report Generator - Shell Layer
530
+ *
531
+ * Serializes analysis results to JSON format for storage, diffing, and
532
+ * trend analysis. This is part of the SHELL layer as it produces output.
533
+ *
534
+ * The JSON output includes:
535
+ * - Timestamp and commit hash for versioning
536
+ * - Full project score with all metrics
537
+ * - Directory and file breakdowns
538
+ * - Refactoring candidates
539
+ * - Any errors encountered during analysis
540
+ */
541
+
542
+ /**
543
+ * JSON report options
544
+ */
545
+ type JsonReportOptions = {
546
+ pretty?: boolean;
547
+ includeFunction?: boolean;
548
+ };
549
+ /**
550
+ * Generate JSON report string from project score
551
+ *
552
+ * @param score - The project score to serialize
553
+ * @param options - Report options
554
+ * @returns JSON string representation
555
+ */
556
+ declare function generateJsonReport(score: ProjectScore, options?: JsonReportOptions): string;
557
+ /**
558
+ * Write JSON report to file
559
+ *
560
+ * @param score - The project score to write
561
+ * @param outputPath - Path to write the JSON file
562
+ * @param options - Report options
563
+ */
564
+ declare function writeJsonReport(score: ProjectScore, outputPath: string, options?: JsonReportOptions): void;
565
+ /**
566
+ * Read a previous JSON report from file
567
+ *
568
+ * @param inputPath - Path to the JSON file
569
+ * @returns Parsed ProjectScore or null if file doesn't exist
570
+ */
571
+ declare function readJsonReport(inputPath: string): ProjectScore | null;
572
+ /**
573
+ * Create a summary JSON suitable for PR comments or CI output
574
+ *
575
+ * @param score - The project score
576
+ * @returns Minimal JSON object with key metrics
577
+ */
578
+ declare function createSummaryJson(score: ProjectScore): {
579
+ health: number;
580
+ purity: number;
581
+ impurityQuality: number | null;
582
+ pureCount: number;
583
+ impureCount: number;
584
+ statusBreakdown: {
585
+ ok: number;
586
+ review: number;
587
+ refactor: number;
588
+ };
589
+ timestamp: string;
590
+ commitHash: string | null;
591
+ topRefactoringCandidates: Array<{
592
+ name: string | null;
593
+ filePath: string;
594
+ qualityScore: number;
595
+ }>;
596
+ };
597
+ /**
598
+ * Compare two project scores and generate a diff report
599
+ *
600
+ * @param current - Current project score
601
+ * @param previous - Previous project score
602
+ * @returns JSON object with comparison data
603
+ */
604
+ declare function generateComparisonReport(current: ProjectScore, previous: ProjectScore): {
605
+ current: ReturnType<typeof createSummaryJson>;
606
+ previous: ReturnType<typeof createSummaryJson>;
607
+ delta: {
608
+ health: number;
609
+ purity: number;
610
+ impurityQuality: number | null;
611
+ pureCount: number;
612
+ impureCount: number;
613
+ };
614
+ improved: boolean;
615
+ newRefactoringCandidates: Array<{
616
+ name: string | null;
617
+ filePath: string;
618
+ }>;
619
+ resolvedRefactoringCandidates: Array<{
620
+ name: string | null;
621
+ filePath: string;
622
+ }>;
623
+ };
624
+
625
+ /**
626
+ * Console Report Generator - Shell Layer
627
+ *
628
+ * Produces human-readable console output with colors, visual bars,
629
+ * and formatted tables. This is part of the SHELL layer as it
630
+ * performs I/O (writing to console).
631
+ */
632
+
633
+ /**
634
+ * Generate and print the full console report
635
+ */
636
+ declare function printConsoleReport(score: ProjectScore, options?: {
637
+ verbose?: boolean;
638
+ }): void;
639
+ /**
640
+ * Generate a single-line summary suitable for CI output
641
+ */
642
+ declare function generateSummaryLine(score: ProjectScore): string;
643
+ /**
644
+ * Print a file-level report (for verbose output)
645
+ */
646
+ declare function printFileReport(fileScore: FileScore): void;
647
+
648
+ /**
649
+ * Extractor - Shell layer for loading TypeScript projects via ts-morph
650
+ *
651
+ * This module handles the I/O of loading a TypeScript project from disk.
652
+ * It's a thin shell that delegates to ts-morph for actual parsing.
653
+ */
654
+
655
+ type ExtractorOptions = {
656
+ tsconfigPath: string;
657
+ filesGlob?: string;
658
+ };
659
+ type ExtractorResult = {
660
+ project: Project;
661
+ sourceFiles: SourceFile[];
662
+ errors: {
663
+ filePath: string;
664
+ error: string;
665
+ }[];
666
+ };
667
+ /**
668
+ * Loads a TypeScript project from a tsconfig.json path
669
+ *
670
+ * @param options - Configuration options for the extractor
671
+ * @returns The loaded project and source files
672
+ * @throws Error if tsconfig.json doesn't exist or is invalid
673
+ */
674
+ declare function loadProject(options: ExtractorOptions): ExtractorResult;
675
+ /**
676
+ * Gets the commit hash from git if available
677
+ */
678
+ declare function getCommitHash(): string | null;
679
+
680
+ /**
681
+ * AST Function Extraction
682
+ *
683
+ * Extracts all function-like nodes from a TypeScript source file into
684
+ * normalized ExtractedFunction structures. This is part of the SHELL layer
685
+ * as it interacts with ts-morph AST nodes.
686
+ *
687
+ * Handles all function forms:
688
+ * - FunctionDeclaration
689
+ * - MethodDeclaration
690
+ * - ArrowFunction (including those passed as arguments)
691
+ * - FunctionExpression (including those passed as arguments)
692
+ * - GetAccessorDeclaration
693
+ * - SetAccessorDeclaration
694
+ */
695
+
696
+ /**
697
+ * Extract all functions from a source file
698
+ */
699
+ declare function extractFunctions(sourceFile: SourceFile): ExtractedFunction[];
700
+ /**
701
+ * Extract imports from a source file
702
+ */
703
+ declare function extractImports(sourceFile: SourceFile): FileImports;
704
+ /**
705
+ * Check if a file is type-only (contains only types, interfaces, enums, no function bodies)
706
+ */
707
+ declare function isTypeOnlyFile(sourceFile: SourceFile): boolean;
708
+
709
+ export { type AnalysisError, type AnalyzerConfig, type CallSite, type ClassifiedFunction, DEFAULT_QUALITY_THRESHOLDS, type DetectionContext, type DirectoryScore, type ExtractedFunction, type ExtractionResult, type ExtractorOptions, type ExtractorResult, type FileImports, type FileScore, type FunctionAnalysis, type FunctionClassification, type ImpurityMarker, type JsonReportOptions, MARKER_CATALOG, type MarkerDefinition, type MarkerType, type ProjectScore, QUALITY_WEIGHTS, type QualityThresholds, type RefactoringCandidate, type Status, type StatusBreakdown, analyze, analyzeFile, analyzeFilePath, analyzeFunction as analyzeFunctionQuality, calculateDelta, checkThresholds, classifyFunction, computeQualityScore, createClassifiedFunction, createDetectionContext, createSummaryJson, deriveStatus, detectMarkers, extractFunctions, extractImports, generateComparisonReport, generateJsonReport, generateSummaryLine, getAllMarkerTypes, getCommitHash, getDiagnosticInsights, getMarkerDefinition, getPureImportedFunctions, getStatusColor, getStatusDescription, getStatusEmoji, groupFilesByDirectory, isTypeOnlyFile, loadProject, printConsoleReport, printFileReport, readJsonReport, scoreDirectory, scoreFile, scoreProject, shouldExcludeFunction, writeJsonReport };