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
package/TECHNICAL.md ADDED
@@ -0,0 +1,386 @@
1
+ # FCIS Analyzer - Technical Documentation
2
+
3
+ This document provides technical details for developers working on or extending the FCIS Analyzer.
4
+
5
+ ## Architecture Overview
6
+
7
+ The FCIS Analyzer follows the Functional Core, Imperative Shell (FCIS) pattern itself:
8
+
9
+ ```
10
+ ┌─────────────────────────────────────────────────────────────────┐
11
+ │ CLI (Shell) │
12
+ │ cli.ts │
13
+ └─────────────────────────────────┬───────────────────────────────┘
14
+
15
+ ┌─────────────────────────────────▼───────────────────────────────┐
16
+ │ Analyzer (Shell) │
17
+ │ analyzer.ts │
18
+ │ Orchestrates the analysis pipeline │
19
+ └───────┬─────────────────────────────────────────────────┬───────┘
20
+ │ │
21
+ ▼ ▼
22
+ ┌───────────────────┐ ┌───────────────────┐
23
+ │ Extraction │ │ Reporting │
24
+ │ (Shell) │ │ (Shell) │
25
+ │ - extractor.ts │ │ - report-json.ts │
26
+ │ - extract-fns.ts │ │ - report-console │
27
+ └───────┬───────────┘ └───────────────────┘
28
+ │ ▲
29
+ ▼ │
30
+ ┌─────────────────────────────────────────────────────────────────┐
31
+ │ PURE CORE │
32
+ │ ┌─────────────┐ ┌─────────────────┐ ┌────────────────────┐ │
33
+ │ │ Detection │ │ Classification │ │ Scoring │ │
34
+ │ │ │ │ │ │ │ │
35
+ │ │ markers.ts │ │ classifier.ts │ │ scorer.ts │ │
36
+ │ │ detect- │ │ quality- │ │ │ │
37
+ │ │ markers.ts │ │ scorer.ts │ │ │ │
38
+ │ │ │ │ derive-status │ │ │ │
39
+ │ └─────────────┘ └─────────────────┘ └────────────────────┘ │
40
+ └─────────────────────────────────────────────────────────────────┘
41
+ ```
42
+
43
+ ## Module Breakdown
44
+
45
+ ### Types (`types.ts`)
46
+
47
+ Central type definitions shared across all modules:
48
+
49
+ ```typescript
50
+ // Core extraction types
51
+ type CallSite = {
52
+ expression: string // e.g., "db.user.findFirst"
53
+ line: number // relative to function start
54
+ isAwaited: boolean
55
+ }
56
+
57
+ type ExtractedFunction = {
58
+ name: string | null
59
+ filePath: string
60
+ startLine: number
61
+ endLine: number
62
+ isAsync: boolean
63
+ isExported: boolean
64
+ bodyLineCount: number
65
+ statementCount: number
66
+ hasConditionals: boolean
67
+ parentContext: string | null
68
+ callSites: CallSite[]
69
+ hasAwait: boolean
70
+ propertyAccessChains: string[]
71
+ kind: 'function' | 'method' | 'arrow' | 'function-expression' | 'getter' | 'setter'
72
+ }
73
+
74
+ // Marker types - strict union for exhaustive matching
75
+ type MarkerType =
76
+ | 'await-expression'
77
+ | 'database-call'
78
+ | 'network-fetch'
79
+ | 'network-http'
80
+ | 'fs-import'
81
+ | 'fs-call'
82
+ | 'env-access'
83
+ | 'console-log'
84
+ | 'logging'
85
+ | 'telemetry'
86
+ | 'queue-enqueue'
87
+ | 'event-emit'
88
+
89
+ // Classification
90
+ type FunctionClassification = 'pure' | 'impure'
91
+ type Status = 'ok' | 'review' | 'refactor'
92
+
93
+ // Scores
94
+ type FileScore = { ... }
95
+ type DirectoryScore = { ... }
96
+ type ProjectScore = { ... }
97
+ ```
98
+
99
+ ### Extraction Layer (Shell)
100
+
101
+ **`extractor.ts`** - Loads TypeScript projects via ts-morph:
102
+
103
+ ```typescript
104
+ function loadProject(options: ExtractorOptions): ExtractorResult
105
+ function getCommitHash(): string | null
106
+ ```
107
+
108
+ **`extract-functions.ts`** - Extracts function data from AST:
109
+
110
+ ```typescript
111
+ function extractFunctions(sourceFile: SourceFile): ExtractedFunction[]
112
+ function extractImports(sourceFile: SourceFile): FileImports
113
+ function isTypeOnlyFile(sourceFile: SourceFile): boolean
114
+ ```
115
+
116
+ ### Detection Layer (Pure Core)
117
+
118
+ **`markers.ts`** - Marker definitions and patterns:
119
+
120
+ ```typescript
121
+ const MARKER_CATALOG: Record<MarkerType, MarkerDefinition>
122
+ const DATABASE_OPERATIONS: Set<string>
123
+ const FS_MODULES: Set<string>
124
+ const HTTP_MODULES: Set<string>
125
+ ```
126
+
127
+ **`detect-markers.ts`** - Marker detection logic:
128
+
129
+ ```typescript
130
+ function detectMarkers(fn: ExtractedFunction, context: DetectionContext): ImpurityMarker[]
131
+ function createDetectionContext(imports: FileImports): DetectionContext
132
+ ```
133
+
134
+ ### Classification Layer (Pure Core)
135
+
136
+ **`classifier.ts`** - Binary classification:
137
+
138
+ ```typescript
139
+ function classifyFunction(fn: ExtractedFunction, markers: ImpurityMarker[]): FunctionClassification
140
+ function shouldExcludeFunction(fn: ExtractedFunction): boolean
141
+ ```
142
+
143
+ **`quality-scorer.ts`** - Quality scoring for impure functions:
144
+
145
+ ```typescript
146
+ function computeQualityScore(fn: ExtractedFunction, markers: ImpurityMarker[], context: DetectionContext): number
147
+ function analyzeFunction(fn: ExtractedFunction, markers: ImpurityMarker[], context: DetectionContext): FunctionAnalysis
148
+ ```
149
+
150
+ **`derive-status.ts`** - Status derivation:
151
+
152
+ ```typescript
153
+ function deriveStatus(classification: FunctionClassification, qualityScore: number | null, thresholds?: QualityThresholds): Status
154
+ ```
155
+
156
+ ### Scoring Layer (Pure Core)
157
+
158
+ **`scorer.ts`** - Metric aggregation:
159
+
160
+ ```typescript
161
+ function scoreFile(filePath: string, functions: ClassifiedFunction[], isTypeOnly?: boolean): FileScore
162
+ function scoreDirectory(dirPath: string, fileScores: FileScore[]): DirectoryScore
163
+ function scoreProject(directoryScores: DirectoryScore[], options?: ProjectScoreOptions): ProjectScore
164
+ ```
165
+
166
+ ### Reporting Layer (Shell)
167
+
168
+ **`report-json.ts`** - JSON output:
169
+
170
+ ```typescript
171
+ function generateJsonReport(score: ProjectScore, options?: JsonReportOptions): string
172
+ function writeJsonReport(score: ProjectScore, outputPath: string, options?: JsonReportOptions): void
173
+ ```
174
+
175
+ **`report-console.ts`** - Console output:
176
+
177
+ ```typescript
178
+ function printConsoleReport(score: ProjectScore, options?: { verbose?: boolean }): void
179
+ function generateSummaryLine(score: ProjectScore): string
180
+ ```
181
+
182
+ ## Classification Rules
183
+
184
+ ### Binary Classification
185
+
186
+ A function is **pure** if it has **zero** impurity markers.
187
+ A function is **impure** if it has **one or more** impurity markers.
188
+
189
+ **Important**: `async` alone does NOT make a function impure. Only actual I/O operations count.
190
+
191
+ ### Marker Detection Scope
192
+
193
+ Markers are detected at the **function level**, not the file level:
194
+
195
+ - A function is marked with `logging` only if it **calls** a logging function
196
+ - A function is marked with `network-http` only if it **calls** an HTTP client function
197
+ - A function is marked with `fs-call` only if it **calls** a file system function
198
+ - File-level imports do NOT automatically taint all functions in the file
199
+
200
+ This ensures pure helper functions in mixed files are correctly classified as pure.
201
+
202
+ **Example**: A file that imports `@sai/logger` may contain:
203
+ - Pure functions that never call the logger (correctly classified as pure)
204
+ - Impure functions that call `log()` or `logger.info()` (correctly classified as impure)
205
+
206
+ ### Marker Detection Rules
207
+
208
+ | Marker Type | Detection Pattern |
209
+ |-------------|-------------------|
210
+ | `await-expression` | Any `await` keyword in function body |
211
+ | `database-call` | `db.*.findFirst`, `prisma.*.create`, etc. |
212
+ | `network-fetch` | `fetch(...)` call |
213
+ | `network-http` | `axios.*` call |
214
+ | `fs-call` | `fs.readFile`, `fs.writeFile`, `readFile`, `writeFile`, etc. |
215
+ | `env-access` | `process.env.*` property access |
216
+ | `console-log` | `console.log`, `console.error`, etc. |
217
+ | `logging` | `logger.*`, `log(...)` calls |
218
+ | `telemetry` | `track*`, `analytics.*` calls |
219
+ | `queue-enqueue` | `*.enqueue`, `queue.add` calls |
220
+ | `event-emit` | `*.emit`, `*.dispatch` calls |
221
+
222
+ ## Quality Scoring
223
+
224
+ Quality score (0-100) measures how well-structured an impure function is.
225
+
226
+ ### Positive Signals
227
+
228
+ | Signal | Points | Description |
229
+ |--------|--------|-------------|
230
+ | Calls `.pure.ts` import | +30 | Explicit FCIS pattern |
231
+ | Calls `plan*/derive*/compute*` | +20 | Pure naming convention |
232
+ | I/O at start (GATHER) | +15 | Good structure |
233
+ | I/O at end (EXECUTE) | +15 | Good structure |
234
+ | Low complexity (≤5) | +10 | Simple orchestration |
235
+ | Shell naming (`handle*`) | +5 | Intent signal |
236
+ | Calls predicates (`is*/has*`) | +5 | Uses pure helpers |
237
+
238
+ ### Negative Signals (Penalties)
239
+
240
+ | Signal | Penalty | Description |
241
+ |--------|---------|-------------|
242
+ | I/O interleaved | -20 | Tangled structure |
243
+ | High complexity (>10) | -15 | Complex logic mixed with I/O |
244
+ | Multiple I/O types | -10 | Too many responsibilities |
245
+ | No pure function calls | -10 | All logic inline |
246
+ | Very long (>100 lines) | -10 | God function |
247
+
248
+ ### Status Thresholds
249
+
250
+ | Classification | Quality Score | Status |
251
+ |---------------|---------------|--------|
252
+ | pure | n/a | ✓ ok |
253
+ | impure | ≥ 70 | ✓ ok |
254
+ | impure | 40-69 | ◐ review |
255
+ | impure | < 40 | ✗ refactor |
256
+
257
+ ## Metrics
258
+
259
+ ### Purity
260
+ ```
261
+ purity = (pureCount / (pureCount + impureCount)) × 100
262
+ ```
263
+
264
+ ### Impurity Quality
265
+ ```
266
+ impurityQuality = sum(qualityScore for impure functions) / impureCount
267
+ ```
268
+
269
+ ### Health
270
+ ```
271
+ health = (functions with status 'ok') / totalFunctions × 100
272
+ ```
273
+
274
+ ### Refactoring Priority
275
+ ```
276
+ priority = bodyLineCount × (100 - qualityScore)
277
+ ```
278
+
279
+ Higher priority = larger, lower-quality functions (most impactful to fix).
280
+
281
+ ## CLI Exit Codes
282
+
283
+ | Code | Meaning |
284
+ |------|---------|
285
+ | 0 | Success, all thresholds passed |
286
+ | 1 | Below threshold (`--min-health`, `--min-purity`, `--min-quality`) |
287
+ | 2 | Configuration error |
288
+ | 3 | Analysis error (zero files analyzed) |
289
+
290
+ ## Extending the Analyzer
291
+
292
+ ### Adding New Markers
293
+
294
+ 1. Add the marker type to the `MarkerType` union in `types.ts`
295
+ 2. Add detection logic in `detect-markers.ts`
296
+ 3. Add marker definition to `MARKER_CATALOG` in `markers.ts`
297
+ 4. Add tests in `detect-markers.test.ts`
298
+
299
+ ### Customizing Quality Scoring
300
+
301
+ Modify `QUALITY_WEIGHTS` in `quality-scorer.ts`:
302
+
303
+ ```typescript
304
+ export const QUALITY_WEIGHTS = {
305
+ callsPureFile: 30,
306
+ callsPureNamingConvention: 20,
307
+ // ... add or modify weights
308
+ }
309
+ ```
310
+
311
+ ### Customizing Status Thresholds
312
+
313
+ Pass custom thresholds to `deriveStatus`:
314
+
315
+ ```typescript
316
+ const customThresholds: QualityThresholds = {
317
+ okThreshold: 80, // Stricter: need 80+ for 'ok'
318
+ reviewThreshold: 50, // Stricter: need 50+ for 'review'
319
+ }
320
+ ```
321
+
322
+ ## Testing
323
+
324
+ ### Unit Tests (Pure Core)
325
+
326
+ Test pure functions with direct input/output assertions:
327
+
328
+ ```typescript
329
+ import { detectMarkers } from '../src/detection/detect-markers.js'
330
+
331
+ it('should detect database calls', () => {
332
+ const fn = createTestFunction({
333
+ callSites: [{ expression: 'db.user.findFirst', line: 3, isAwaited: true }]
334
+ })
335
+ const markers = detectMarkers(fn, context)
336
+ expect(markers).toContainEqual({ type: 'database-call', ... })
337
+ })
338
+ ```
339
+
340
+ ### Integration Tests
341
+
342
+ Use ts-morph's in-memory file system:
343
+
344
+ ```typescript
345
+ import { Project } from 'ts-morph'
346
+
347
+ const project = new Project({ useInMemoryFileSystem: true })
348
+ project.createSourceFile('/test.ts', `
349
+ export async function getUser(id: string) {
350
+ return await db.user.findFirst({ where: { id } })
351
+ }
352
+ `)
353
+ ```
354
+
355
+ ## Performance Considerations
356
+
357
+ - **Target**: Full monorepo analysis < 60 seconds
358
+ - **Single file**: < 2 seconds
359
+ - **Optimization**: ts-morph lazy loading, parallel file processing
360
+
361
+ ### Future: Caching (Post-v1)
362
+
363
+ - Cache `ExtractedFunction[]` per file by content hash
364
+ - Store in `.fcis-cache/` (gitignored)
365
+ - Invalidate on ts-morph or fcis version change
366
+
367
+ ## Dependencies
368
+
369
+ | Package | Purpose |
370
+ |---------|---------|
371
+ | `ts-morph` | TypeScript AST parsing |
372
+ | `zod` | CLI argument validation |
373
+ | `cleye` | CLI argument parsing |
374
+ | `chalk` | Colored console output |
375
+ | `ts-pattern` | Pattern matching (optional) |
376
+
377
+ ## File Exclusions
378
+
379
+ Automatically excluded from analysis:
380
+ - `*.test.ts`, `*.spec.ts` - Test files
381
+ - `*.d.ts` - Type declaration files
382
+ - `*.stories.tsx` - Storybook files
383
+ - `/node_modules/` - Dependencies
384
+ - `/generated/` - Generated code
385
+ - Type-only files (only types/interfaces/enums)
386
+ - Trivial functions (< 3 statements, no conditionals)
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node