@getmikk/core 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +431 -0
  2. package/package.json +6 -2
  3. package/src/contract/contract-generator.ts +85 -85
  4. package/src/contract/contract-reader.ts +28 -28
  5. package/src/contract/contract-writer.ts +114 -114
  6. package/src/contract/index.ts +12 -12
  7. package/src/contract/lock-compiler.ts +221 -221
  8. package/src/contract/lock-reader.ts +34 -34
  9. package/src/contract/schema.ts +147 -147
  10. package/src/graph/cluster-detector.ts +312 -312
  11. package/src/graph/graph-builder.ts +211 -211
  12. package/src/graph/impact-analyzer.ts +55 -55
  13. package/src/graph/index.ts +4 -4
  14. package/src/graph/types.ts +59 -59
  15. package/src/hash/file-hasher.ts +30 -30
  16. package/src/hash/hash-store.ts +119 -119
  17. package/src/hash/index.ts +3 -3
  18. package/src/hash/tree-hasher.ts +20 -20
  19. package/src/index.ts +12 -12
  20. package/src/parser/base-parser.ts +16 -16
  21. package/src/parser/boundary-checker.ts +211 -211
  22. package/src/parser/index.ts +46 -46
  23. package/src/parser/types.ts +90 -90
  24. package/src/parser/typescript/ts-extractor.ts +543 -543
  25. package/src/parser/typescript/ts-parser.ts +41 -41
  26. package/src/parser/typescript/ts-resolver.ts +86 -86
  27. package/src/utils/errors.ts +42 -42
  28. package/src/utils/fs.ts +75 -75
  29. package/src/utils/fuzzy-match.ts +186 -186
  30. package/src/utils/logger.ts +36 -36
  31. package/src/utils/minimatch.ts +19 -19
  32. package/tests/contract.test.ts +134 -134
  33. package/tests/fixtures/simple-api/package.json +5 -5
  34. package/tests/fixtures/simple-api/src/auth/middleware.ts +9 -9
  35. package/tests/fixtures/simple-api/src/auth/verify.ts +6 -6
  36. package/tests/fixtures/simple-api/src/index.ts +9 -9
  37. package/tests/fixtures/simple-api/src/utils/jwt.ts +3 -3
  38. package/tests/fixtures/simple-api/tsconfig.json +8 -8
  39. package/tests/fuzzy-match.test.ts +142 -142
  40. package/tests/graph.test.ts +169 -169
  41. package/tests/hash.test.ts +49 -49
  42. package/tests/helpers.ts +83 -83
  43. package/tests/parser.test.ts +218 -218
  44. package/tsconfig.json +15 -15
package/README.md ADDED
@@ -0,0 +1,431 @@
1
+ # @getmikk/core
2
+
3
+ > AST parsing, dependency graph construction, Merkle-tree hashing, contract management, and foundational utilities for the Mikk ecosystem.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@getmikk/core)](https://www.npmjs.com/package/@getmikk/core)
6
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](../../LICENSE)
7
+
8
+ `@getmikk/core` is the foundation package that every other Mikk package depends on. It provides the complete pipeline for understanding a TypeScript codebase: parsing source files into structured ASTs, building a full dependency graph, computing Merkle-tree hashes for drift detection, and managing the `mikk.json` contract and `mikk.lock.json` lock file.
9
+
10
+ ---
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @getmikk/core
16
+ # or
17
+ bun add @getmikk/core
18
+ ```
19
+
20
+ ---
21
+
22
+ ## Architecture Overview
23
+
24
+ ```
25
+ Source Files (.ts/.tsx)
26
+
27
+
28
+ ┌─────────┐
29
+ │ Parser │ ← TypeScriptParser + TypeScriptExtractor
30
+ └────┬────┘
31
+ │ ParsedFile[]
32
+
33
+ ┌──────────────┐
34
+ │ GraphBuilder │ ← Two-pass: nodes → edges
35
+ └──────┬───────┘
36
+ │ DependencyGraph
37
+
38
+ ┌────────────────┐
39
+ │ LockCompiler │ ← Merkle-tree hashes
40
+ └───────┬────────┘
41
+ │ MikkLock
42
+
43
+ ┌─────────────────┐
44
+ │ ContractWriter │ ← Permission model (never/ask/explicit)
45
+ └─────────────────┘
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Modules
51
+
52
+ ### 1. Parser — Source Code Analysis
53
+
54
+ The parser module turns raw TypeScript/TSX files into structured `ParsedFile` objects using the TypeScript Compiler API.
55
+
56
+ ```typescript
57
+ import { TypeScriptParser, getParser, parseFiles } from '@getmikk/core'
58
+
59
+ // Parse a single file
60
+ const parser = new TypeScriptParser()
61
+ const parsed = parser.parse('/src/utils/math.ts', fileContent)
62
+
63
+ console.log(parsed.functions) // ParsedFunction[] — name, params, returnType, startLine, endLine, calls[]
64
+ console.log(parsed.classes) // ParsedClass[] — name, methods[], properties[], decorators[]
65
+ console.log(parsed.imports) // ParsedImport[] — source, specifiers, isTypeOnly
66
+ console.log(parsed.exports) // ParsedExport[] — name, isDefault, isTypeOnly
67
+ console.log(parsed.generics) // ParsedGeneric[] — interfaces, types, const declarations
68
+
69
+ // Factory — auto-selects parser by file extension
70
+ const parser = getParser('component.tsx') // returns TypeScriptParser
71
+
72
+ // Batch parse with import resolution
73
+ const files = await parseFiles(filePaths, projectRoot, readFileFn)
74
+ // Returns ParsedFile[] with all import paths resolved to absolute paths
75
+ ```
76
+
77
+ #### TypeScriptExtractor
78
+
79
+ The extractor walks the TypeScript AST and pulls out detailed metadata:
80
+
81
+ - **Functions**: name, parameters (with types & defaults), return type, line range, internal calls, `async`/generator flags, decorators, type parameters
82
+ - **Classes**: name, methods (with full function metadata), properties, decorators, `extends`/`implements`, type parameters
83
+ - **Generics**: interfaces, type aliases, const declarations, enums
84
+ - **Imports**: named, default, namespace, type-only imports
85
+ - **Exports**: named, default, re-exports
86
+
87
+ #### TypeScriptResolver
88
+
89
+ Resolves import paths against the actual project filesystem:
90
+
91
+ ```typescript
92
+ import { TypeScriptResolver } from '@getmikk/core'
93
+
94
+ const resolver = new TypeScriptResolver()
95
+ // Resolves: relative paths, path aliases (tsconfig paths), index files, extension inference (.ts/.tsx/.js)
96
+ const resolved = resolver.resolve(importDecl, fromFilePath, allProjectFiles)
97
+ ```
98
+
99
+ ---
100
+
101
+ ### 2. Graph — Dependency Graph Construction
102
+
103
+ The graph module builds a complete dependency graph from parsed files.
104
+
105
+ ```typescript
106
+ import { GraphBuilder, ImpactAnalyzer, ClusterDetector } from '@getmikk/core'
107
+
108
+ // Build the graph
109
+ const builder = new GraphBuilder()
110
+ const graph = builder.build(parsedFiles)
111
+
112
+ console.log(graph.nodes) // Map<string, GraphNode> — file, function, class, generic nodes
113
+ console.log(graph.edges) // GraphEdge[] — import, call, containment, implements edges
114
+ console.log(graph.adjacency) // Map<string, string[]> — forward adjacency
115
+ console.log(graph.reverse) // Map<string, string[]> — reverse adjacency
116
+ ```
117
+
118
+ #### GraphBuilder
119
+
120
+ Two-pass construction:
121
+ 1. **Pass 1 — Nodes**: Creates nodes for every file, function, class, and generic declaration
122
+ 2. **Pass 2 — Edges**: Creates edges for imports, function calls, class containment, and cross-file references
123
+
124
+ Node types: `file`, `function`, `class`, `generic`
125
+ Edge types: `import`, `call`, `containment`, `implements`
126
+
127
+ #### ImpactAnalyzer
128
+
129
+ BFS backward walk to find everything affected by a change:
130
+
131
+ ```typescript
132
+ const analyzer = new ImpactAnalyzer(graph)
133
+ const impact = analyzer.analyze(['src/utils/math.ts::calculateTotal'])
134
+
135
+ console.log(impact.changed) // string[] — directly changed node IDs
136
+ console.log(impact.impacted) // string[] — transitively affected nodes
137
+ console.log(impact.depth) // number — max propagation depth
138
+ console.log(impact.confidence) // number — 0-1 confidence score
139
+ ```
140
+
141
+ #### ClusterDetector
142
+
143
+ Greedy agglomeration algorithm for automatic module discovery:
144
+
145
+ ```typescript
146
+ const detector = new ClusterDetector(graph, /* minClusterSize */ 3, /* minCouplingScore */ 0.1)
147
+ const clusters = detector.detect()
148
+
149
+ // Returns ModuleCluster[] with:
150
+ // - id, label (auto-generated from common paths)
151
+ // - nodeIds[] — functions/classes in this cluster
152
+ // - cohesion — internal coupling score (0-1)
153
+ // - coupling — Map<clusterId, score> — external coupling
154
+ ```
155
+
156
+ The algorithm starts with one cluster per file, then iteratively merges the pair with the highest coupling score until no pair exceeds the threshold.
157
+
158
+ ---
159
+
160
+ ### 3. Contract — mikk.json & mikk.lock.json
161
+
162
+ The contract module manages the two core Mikk files using Zod validation.
163
+
164
+ #### Schemas
165
+
166
+ All schemas are exported as Zod objects for runtime validation:
167
+
168
+ ```typescript
169
+ import {
170
+ MikkContractSchema, // mikk.json validation
171
+ MikkLockSchema, // mikk.lock.json validation
172
+ MikkModuleSchema, // Module definition
173
+ MikkDecisionSchema, // Architecture decision record
174
+ } from '@getmikk/core'
175
+
176
+ // Validate a contract
177
+ const result = MikkContractSchema.safeParse(rawJson)
178
+ if (!result.success) console.error(result.error.issues)
179
+ ```
180
+
181
+ #### mikk.json (Contract)
182
+
183
+ Defines the project's architectural rules:
184
+
185
+ ```typescript
186
+ type MikkContract = {
187
+ name: string
188
+ version: string
189
+ modules: Record<string, MikkModule> // Module definitions with intent, public API, constraints
190
+ decisions: MikkDecision[] // Architecture Decision Records (ADRs)
191
+ overwrite: {
192
+ permission: 'never' | 'ask' | 'explicit'
193
+ lastOverwrittenBy?: string
194
+ lastOverwrittenAt?: string
195
+ }
196
+ }
197
+ ```
198
+
199
+ #### mikk.lock.json (Lock File)
200
+
201
+ Auto-generated snapshot of the entire codebase:
202
+
203
+ ```typescript
204
+ type MikkLock = {
205
+ generatorVersion: string
206
+ generatedAt: string
207
+ rootHash: string // Merkle root of entire project
208
+ modules: Record<string, MikkLockModule>
209
+ }
210
+
211
+ type MikkLockModule = {
212
+ hash: string // Merkle hash of all files in module
213
+ files: Record<string, MikkLockFile>
214
+ }
215
+
216
+ type MikkLockFile = {
217
+ hash: string
218
+ functions: Record<string, MikkLockFunction>
219
+ classes: Record<string, MikkLockClass>
220
+ generics: Record<string, MikkLockGeneric>
221
+ }
222
+ ```
223
+
224
+ #### ContractReader / LockReader
225
+
226
+ ```typescript
227
+ import { ContractReader, LockReader } from '@getmikk/core'
228
+
229
+ const contractReader = new ContractReader()
230
+ const contract = await contractReader.read('./mikk.json')
231
+
232
+ const lockReader = new LockReader()
233
+ const lock = await lockReader.read('./mikk.lock.json')
234
+ await lockReader.write(updatedLock, './mikk.lock.json')
235
+ ```
236
+
237
+ #### ContractWriter — Permission Model
238
+
239
+ ```typescript
240
+ import { ContractWriter } from '@getmikk/core'
241
+
242
+ const writer = new ContractWriter()
243
+
244
+ // First-time write
245
+ await writer.writeNew(contract, './mikk.json')
246
+
247
+ // Update with permission model
248
+ const result = await writer.update(existingContract, updates, './mikk.json')
249
+ // result.updated — boolean
250
+ // result.requiresConfirmation — true if permission is 'ask'
251
+ // result.proposedChanges — diff object when confirmation needed
252
+ ```
253
+
254
+ Permission levels:
255
+ - **`never`** — Contract is read-only, updates are rejected
256
+ - **`ask`** — Returns `requiresConfirmation: true` with proposed changes
257
+ - **`explicit`** — Auto-applies updates with audit trail
258
+
259
+ #### LockCompiler
260
+
261
+ Compiles the full lock file from graph + contract + parsed files:
262
+
263
+ ```typescript
264
+ import { LockCompiler } from '@getmikk/core'
265
+
266
+ const compiler = new LockCompiler()
267
+ const lock = compiler.compile(graph, contract, parsedFiles)
268
+ // Computes Merkle-tree hashes at every level:
269
+ // function → file → module → root
270
+ ```
271
+
272
+ #### ContractGenerator
273
+
274
+ Auto-generates a `mikk.json` skeleton from detected clusters:
275
+
276
+ ```typescript
277
+ import { ContractGenerator } from '@getmikk/core'
278
+
279
+ const generator = new ContractGenerator()
280
+ const contract = generator.generateFromClusters(clusters, parsedFiles, 'my-project')
281
+ ```
282
+
283
+ #### BoundaryChecker
284
+
285
+ CI-ready enforcement layer:
286
+
287
+ ```typescript
288
+ import { BoundaryChecker } from '@getmikk/core'
289
+
290
+ const checker = new BoundaryChecker(contract, lock)
291
+ const result = checker.check()
292
+
293
+ if (!result.pass) {
294
+ for (const v of result.violations) {
295
+ console.error(`${v.severity}: ${v.message}`)
296
+ // severity: 'error' | 'warning'
297
+ // type: 'boundary-crossing' | 'constraint-violation'
298
+ }
299
+ process.exit(1)
300
+ }
301
+ ```
302
+
303
+ ---
304
+
305
+ ### 4. Hash — Merkle-Tree Integrity
306
+
307
+ ```typescript
308
+ import { hashContent, hashFile, hashFunctionBody, computeModuleHash, computeRootHash } from '@getmikk/core'
309
+
310
+ // Hash raw content (SHA-256)
311
+ const h1 = hashContent('function foo() {}')
312
+
313
+ // Hash a file from disk
314
+ const h2 = await hashFile('/src/index.ts')
315
+
316
+ // Hash a specific line range (function body)
317
+ const h3 = hashFunctionBody(fileContent, 10, 25)
318
+
319
+ // Merkle tree
320
+ const moduleHash = computeModuleHash(['fileHash1', 'fileHash2'])
321
+ const rootHash = computeRootHash(['moduleHash1', 'moduleHash2'])
322
+ ```
323
+
324
+ #### HashStore — SQLite Persistence
325
+
326
+ ```typescript
327
+ import { HashStore } from '@getmikk/core'
328
+
329
+ const store = new HashStore('/project/.mikk/hashes.db')
330
+
331
+ store.set('src/index.ts', 'abc123...', 4096)
332
+ const entry = store.get('src/index.ts')
333
+ // { path, hash, size, updatedAt }
334
+
335
+ const changed = store.getChangedSince(Date.now() - 60_000)
336
+ store.delete('src/old-file.ts')
337
+
338
+ // Batch operations use SQLite transactions for performance
339
+ const allPaths = store.getAllPaths()
340
+ ```
341
+
342
+ Uses SQLite in WAL mode for concurrent read access and fast writes.
343
+
344
+ ---
345
+
346
+ ### 5. Utilities
347
+
348
+ #### Error Hierarchy
349
+
350
+ ```typescript
351
+ import {
352
+ MikkError, // Base error class
353
+ ParseError, // File parsing failures
354
+ ContractNotFoundError, // mikk.json not found
355
+ LockNotFoundError, // mikk.lock.json not found
356
+ UnsupportedLanguageError, // Unsupported file extension
357
+ OverwritePermissionError, // Contract overwrite denied
358
+ SyncStateError, // Lock file out of sync
359
+ } from '@getmikk/core'
360
+ ```
361
+
362
+ #### Logging
363
+
364
+ ```typescript
365
+ import { logger, setLogLevel } from '@getmikk/core'
366
+
367
+ setLogLevel('debug') // 'debug' | 'info' | 'warn' | 'error' | 'silent'
368
+
369
+ logger.info('Analysis complete', { files: 42, duration: '1.2s' })
370
+ // Outputs structured JSON to stderr
371
+ ```
372
+
373
+ #### File Utilities
374
+
375
+ ```typescript
376
+ import { discoverFiles, readFileContent, writeFileContent, fileExists, setupMikkDirectory } from '@getmikk/core'
377
+
378
+ // Discover TypeScript files
379
+ const files = await discoverFiles('/project', ['src/**/*.ts'], ['node_modules'])
380
+
381
+ // Read/write
382
+ const content = await readFileContent('/src/index.ts')
383
+ await writeFileContent('/output/result.json', jsonString) // auto-creates directories
384
+
385
+ // Initialize .mikk/ directory structure
386
+ await setupMikkDirectory('/project')
387
+ ```
388
+
389
+ #### Fuzzy Matching
390
+
391
+ ```typescript
392
+ import { scoreFunctions, findFuzzyMatches, levenshtein } from '@getmikk/core'
393
+
394
+ // Score lock file functions against a natural-language prompt
395
+ const matches = scoreFunctions('calculate the total price', lock, 10)
396
+ // Returns FuzzyMatch[] sorted by relevance score
397
+
398
+ // "Did you mean?" suggestions
399
+ const suggestions = findFuzzyMatches('calcualteTotal', lock, 5)
400
+ // Returns closest function names by Levenshtein distance
401
+
402
+ // Raw edit distance
403
+ const dist = levenshtein('kitten', 'sitting') // 3
404
+ ```
405
+
406
+ ---
407
+
408
+ ## Types
409
+
410
+ All types are exported and can be imported directly:
411
+
412
+ ```typescript
413
+ import type {
414
+ // Parser
415
+ ParsedFile, ParsedFunction, ParsedClass, ParsedImport, ParsedExport, ParsedParam, ParsedGeneric,
416
+ // Graph
417
+ DependencyGraph, GraphNode, GraphEdge, ImpactResult, NodeType, EdgeType, ModuleCluster,
418
+ // Contract
419
+ MikkContract, MikkLock, MikkModule, MikkDecision, MikkLockFunction, MikkLockModule, MikkLockFile,
420
+ // Boundary
421
+ BoundaryViolation, BoundaryCheckResult, ViolationSeverity,
422
+ // Writer
423
+ UpdateResult,
424
+ } from '@getmikk/core'
425
+ ```
426
+
427
+ ---
428
+
429
+ ## License
430
+
431
+ [Apache-2.0](../../LICENSE)
package/package.json CHANGED
@@ -1,6 +1,11 @@
1
1
  {
2
2
  "name": "@getmikk/core",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
+ "license": "Apache-2.0",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/Ansh-dhanani/mikk"
8
+ },
4
9
  "type": "module",
5
10
  "main": "./dist/index.js",
6
11
  "types": "./dist/index.d.ts",
@@ -13,7 +18,6 @@
13
18
  "scripts": {
14
19
  "build": "tsc",
15
20
  "test": "bun test",
16
- "publish": "npm publish --access public",
17
21
  "dev": "tsc --watch"
18
22
  },
19
23
  "dependencies": {
@@ -1,85 +1,85 @@
1
- import type { MikkContract } from './schema.js'
2
- import type { ModuleCluster } from '../graph/types.js'
3
- import type { ParsedFile } from '../parser/types.js'
4
-
5
- /**
6
- * ContractGenerator — generates a mikk.json skeleton from graph analysis.
7
- * Takes detected module clusters and produces a human-refinable contract.
8
- */
9
- export class ContractGenerator {
10
- /** Generate a full mikk.json contract from detected clusters */
11
- generateFromClusters(
12
- clusters: ModuleCluster[],
13
- parsedFiles: ParsedFile[],
14
- projectName: string
15
- ): MikkContract {
16
- const modules = clusters.map(cluster => ({
17
- id: cluster.id,
18
- name: cluster.suggestedName,
19
- description: `Contains ${cluster.files.length} files with ${cluster.functions.length} functions`,
20
- intent: '',
21
- paths: this.inferPaths(cluster.files),
22
- entryFunctions: this.inferEntryFunctions(cluster, parsedFiles),
23
- }))
24
-
25
- // Detect entry points (files with no importedBy)
26
- const entryPoints = parsedFiles
27
- .filter(f => {
28
- const basename = f.path.split('/').pop() || ''
29
- return basename === 'index.ts' || basename === 'server.ts' || basename === 'main.ts' || basename === 'app.ts'
30
- })
31
- .map(f => f.path)
32
-
33
- return {
34
- version: '1.0.0',
35
- project: {
36
- name: projectName,
37
- description: '',
38
- language: 'typescript',
39
- entryPoints: entryPoints.length > 0 ? entryPoints : [parsedFiles[0]?.path ?? 'src/index.ts'],
40
- },
41
- declared: {
42
- modules,
43
- constraints: [],
44
- decisions: [],
45
- },
46
- overwrite: {
47
- mode: 'never',
48
- requireConfirmation: true,
49
- },
50
- }
51
- }
52
-
53
- /** Infer path patterns from a list of files */
54
- private inferPaths(files: string[]): string[] {
55
- // Find common directory prefix
56
- if (files.length === 0) return []
57
-
58
- const dirs = new Set<string>()
59
- for (const file of files) {
60
- const parts = file.split('/')
61
- parts.pop() // Remove filename
62
- dirs.add(parts.join('/'))
63
- }
64
-
65
- // Use glob patterns for each unique directory
66
- return [...dirs].map(dir => `${dir}/**`)
67
- }
68
-
69
- /** Find exported functions in a cluster — these are likely entry points */
70
- private inferEntryFunctions(cluster: ModuleCluster, parsedFiles: ParsedFile[]): string[] {
71
- const clusterFileSet = new Set(cluster.files)
72
- const entryFunctions: string[] = []
73
-
74
- for (const file of parsedFiles) {
75
- if (!clusterFileSet.has(file.path)) continue
76
- for (const fn of file.functions) {
77
- if (fn.isExported) {
78
- entryFunctions.push(fn.name)
79
- }
80
- }
81
- }
82
-
83
- return entryFunctions
84
- }
85
- }
1
+ import type { MikkContract } from './schema.js'
2
+ import type { ModuleCluster } from '../graph/types.js'
3
+ import type { ParsedFile } from '../parser/types.js'
4
+
5
+ /**
6
+ * ContractGenerator — generates a mikk.json skeleton from graph analysis.
7
+ * Takes detected module clusters and produces a human-refinable contract.
8
+ */
9
+ export class ContractGenerator {
10
+ /** Generate a full mikk.json contract from detected clusters */
11
+ generateFromClusters(
12
+ clusters: ModuleCluster[],
13
+ parsedFiles: ParsedFile[],
14
+ projectName: string
15
+ ): MikkContract {
16
+ const modules = clusters.map(cluster => ({
17
+ id: cluster.id,
18
+ name: cluster.suggestedName,
19
+ description: `Contains ${cluster.files.length} files with ${cluster.functions.length} functions`,
20
+ intent: '',
21
+ paths: this.inferPaths(cluster.files),
22
+ entryFunctions: this.inferEntryFunctions(cluster, parsedFiles),
23
+ }))
24
+
25
+ // Detect entry points (files with no importedBy)
26
+ const entryPoints = parsedFiles
27
+ .filter(f => {
28
+ const basename = f.path.split('/').pop() || ''
29
+ return basename === 'index.ts' || basename === 'server.ts' || basename === 'main.ts' || basename === 'app.ts'
30
+ })
31
+ .map(f => f.path)
32
+
33
+ return {
34
+ version: '1.0.0',
35
+ project: {
36
+ name: projectName,
37
+ description: '',
38
+ language: 'typescript',
39
+ entryPoints: entryPoints.length > 0 ? entryPoints : [parsedFiles[0]?.path ?? 'src/index.ts'],
40
+ },
41
+ declared: {
42
+ modules,
43
+ constraints: [],
44
+ decisions: [],
45
+ },
46
+ overwrite: {
47
+ mode: 'never',
48
+ requireConfirmation: true,
49
+ },
50
+ }
51
+ }
52
+
53
+ /** Infer path patterns from a list of files */
54
+ private inferPaths(files: string[]): string[] {
55
+ // Find common directory prefix
56
+ if (files.length === 0) return []
57
+
58
+ const dirs = new Set<string>()
59
+ for (const file of files) {
60
+ const parts = file.split('/')
61
+ parts.pop() // Remove filename
62
+ dirs.add(parts.join('/'))
63
+ }
64
+
65
+ // Use glob patterns for each unique directory
66
+ return [...dirs].map(dir => `${dir}/**`)
67
+ }
68
+
69
+ /** Find exported functions in a cluster — these are likely entry points */
70
+ private inferEntryFunctions(cluster: ModuleCluster, parsedFiles: ParsedFile[]): string[] {
71
+ const clusterFileSet = new Set(cluster.files)
72
+ const entryFunctions: string[] = []
73
+
74
+ for (const file of parsedFiles) {
75
+ if (!clusterFileSet.has(file.path)) continue
76
+ for (const fn of file.functions) {
77
+ if (fn.isExported) {
78
+ entryFunctions.push(fn.name)
79
+ }
80
+ }
81
+ }
82
+
83
+ return entryFunctions
84
+ }
85
+ }
@@ -1,28 +1,28 @@
1
- import * as fs from 'node:fs/promises'
2
- import { MikkContractSchema, type MikkContract } from './schema.js'
3
- import { ContractNotFoundError } from '../utils/errors.js'
4
-
5
- /**
6
- * ContractReader — reads and validates mikk.json from disk.
7
- */
8
- export class ContractReader {
9
- /** Read and validate mikk.json */
10
- async read(contractPath: string): Promise<MikkContract> {
11
- let content: string
12
- try {
13
- content = await fs.readFile(contractPath, 'utf-8')
14
- } catch {
15
- throw new ContractNotFoundError(contractPath)
16
- }
17
-
18
- const json = JSON.parse(content)
19
- const result = MikkContractSchema.safeParse(json)
20
-
21
- if (!result.success) {
22
- const errors = result.error.issues.map(i => ` ${i.path.join('.')}: ${i.message}`).join('\n')
23
- throw new Error(`Invalid mikk.json:\n${errors}`)
24
- }
25
-
26
- return result.data
27
- }
28
- }
1
+ import * as fs from 'node:fs/promises'
2
+ import { MikkContractSchema, type MikkContract } from './schema.js'
3
+ import { ContractNotFoundError } from '../utils/errors.js'
4
+
5
+ /**
6
+ * ContractReader — reads and validates mikk.json from disk.
7
+ */
8
+ export class ContractReader {
9
+ /** Read and validate mikk.json */
10
+ async read(contractPath: string): Promise<MikkContract> {
11
+ let content: string
12
+ try {
13
+ content = await fs.readFile(contractPath, 'utf-8')
14
+ } catch {
15
+ throw new ContractNotFoundError(contractPath)
16
+ }
17
+
18
+ const json = JSON.parse(content)
19
+ const result = MikkContractSchema.safeParse(json)
20
+
21
+ if (!result.success) {
22
+ const errors = result.error.issues.map(i => ` ${i.path.join('.')}: ${i.message}`).join('\n')
23
+ throw new Error(`Invalid mikk.json:\n${errors}`)
24
+ }
25
+
26
+ return result.data
27
+ }
28
+ }