@getmikk/core 2.0.11 → 2.0.13
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 +12 -3
- package/package.json +1 -1
- package/src/contract/adr-manager.ts +5 -4
- package/src/contract/contract-generator.ts +0 -2
- package/src/contract/contract-writer.ts +3 -3
- package/src/contract/lock-compiler.ts +4 -3
- package/src/contract/lock-reader.ts +62 -5
- package/src/contract/schema.ts +8 -0
- package/src/index.ts +12 -1
- package/src/parser/index.ts +301 -74
- package/src/parser/oxc-parser.ts +3 -2
- package/src/parser/tree-sitter/parser.ts +24 -1
- package/src/parser/tree-sitter/queries.ts +27 -0
- package/src/parser/types.ts +1 -1
- package/src/utils/artifact-transaction.ts +176 -0
- package/src/utils/atomic-write.ts +131 -0
- package/src/utils/fs.ts +33 -13
- package/src/utils/language-registry.ts +82 -0
- package/tests/adr-manager.test.ts +6 -0
- package/tests/artifact-transaction.test.ts +73 -0
- package/tests/contract.test.ts +12 -0
- package/tests/dead-code.test.ts +12 -0
- package/tests/esm-resolver.test.ts +6 -0
- package/tests/fs.test.ts +22 -1
- package/tests/fuzzy-match.test.ts +6 -0
- package/tests/go-parser.test.ts +7 -0
- package/tests/graph.test.ts +10 -0
- package/tests/hash.test.ts +6 -0
- package/tests/impact-classified.test.ts +13 -0
- package/tests/js-parser.test.ts +10 -0
- package/tests/language-registry.test.ts +64 -0
- package/tests/parse-diagnostics.test.ts +115 -0
- package/tests/parser.test.ts +36 -0
- package/tests/tree-sitter-parser.test.ts +201 -0
- package/tests/ts-parser.test.ts +6 -0
package/README.md
CHANGED
|
@@ -15,13 +15,16 @@ Foundation package for the Mikk ecosystem. All other packages depend on core —
|
|
|
15
15
|
|
|
16
16
|
### Parsers
|
|
17
17
|
|
|
18
|
-
Three
|
|
18
|
+
Three parser families follow the same interface: `parse(filePath, content)` → `ParsedFile`.
|
|
19
19
|
|
|
20
20
|
**TypeScript / TSX**
|
|
21
|
-
Uses
|
|
21
|
+
Uses OXC (Rust parser). Extracts: functions (name, params with types, return type, start/end line, async flag, decorators, generics), classes (methods, properties, inheritance), imports (named, default, namespace, type-only) with full resolution (tsconfig `paths` alias resolution, recursive `extends` chain, index file inference, extension inference). Every extracted function has its exact byte-accurate body location.
|
|
22
22
|
|
|
23
23
|
**JavaScript / JSX**
|
|
24
|
-
Uses
|
|
24
|
+
Uses OXC with `ScriptKind` inference (detects JS/JSX/CJS/MJS). Handles: JSX expression containers, default exports, CommonJS `module.exports`, re-exports via barrel files.
|
|
25
|
+
|
|
26
|
+
**Polyglot (Tree-sitter)**
|
|
27
|
+
Python, Java, Kotlin (`.kt`, `.kts`), Swift, C/C++ (`.cpp`, `.cc`, `.cxx`, `.hpp`, `.hxx`, `.hh`), C#, Rust, PHP, and Ruby via tree-sitter grammars.
|
|
25
28
|
|
|
26
29
|
**Go**
|
|
27
30
|
Regex + stateful scanning. No Go toolchain dependency. Extracts: functions, methods (with receiver types), structs, interfaces, package imports. `go.mod` used for project boundary detection.
|
|
@@ -81,6 +84,12 @@ Lock format v1.7.0:
|
|
|
81
84
|
|
|
82
85
|
Read and write `mikk.json` and `mikk.lock.json`. `LockReader.write()` uses atomic temp-file + rename to prevent corruption.
|
|
83
86
|
|
|
87
|
+
`AdrManager` writes `mikk.json` atomically as well (temp file + rename + file lock), reducing corruption risk in concurrent agent workflows.
|
|
88
|
+
|
|
89
|
+
### Parse Diagnostics
|
|
90
|
+
|
|
91
|
+
`parseFilesWithDiagnostics` returns both parsed files and parser/read/import-resolution diagnostics. This enables strict parse enforcement in CLI commands (`mikk init --strict-parsing`, `mikk analyze --strict-parsing`) for high-assurance pipelines.
|
|
92
|
+
|
|
84
93
|
### AdrManager
|
|
85
94
|
|
|
86
95
|
CRUD for Architectural Decision Records in `mikk.json`. Add, update, remove, list, and get individual decisions. ADRs surface in all AI context queries via the MCP server.
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises'
|
|
2
1
|
import type { MikkContract, MikkDecision } from './schema.js'
|
|
3
2
|
import { MikkContractSchema } from './schema.js'
|
|
3
|
+
import { readJsonSafe } from '../utils/json.js'
|
|
4
|
+
import { writeFileAtomic } from '../utils/atomic-write.js'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* AdrManager — CRUD operations on Architectural Decision Records
|
|
@@ -65,11 +66,11 @@ export class AdrManager {
|
|
|
65
66
|
// ─── Helpers ───────────────────────────────────────────────────
|
|
66
67
|
|
|
67
68
|
private async readContract(): Promise<MikkContract> {
|
|
68
|
-
const
|
|
69
|
-
return MikkContractSchema.parse(
|
|
69
|
+
const json = await readJsonSafe(this.contractPath, 'mikk.json')
|
|
70
|
+
return MikkContractSchema.parse(json)
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
private async writeContract(contract: MikkContract): Promise<void> {
|
|
73
|
-
await
|
|
74
|
+
await writeFileAtomic(this.contractPath, JSON.stringify(contract, null, 2), { encoding: 'utf-8' })
|
|
74
75
|
}
|
|
75
76
|
}
|
|
@@ -96,7 +96,6 @@ export class ContractGenerator {
|
|
|
96
96
|
const clusterFileSet = new Set(cluster.files)
|
|
97
97
|
const purposes: string[] = []
|
|
98
98
|
const fnNames: string[] = []
|
|
99
|
-
let hasExported = 0
|
|
100
99
|
let totalFunctions = 0
|
|
101
100
|
|
|
102
101
|
for (const file of parsedFiles) {
|
|
@@ -104,7 +103,6 @@ export class ContractGenerator {
|
|
|
104
103
|
for (const fn of file.functions) {
|
|
105
104
|
totalFunctions++
|
|
106
105
|
fnNames.push(fn.name)
|
|
107
|
-
if (fn.isExported) hasExported++
|
|
108
106
|
if (fn.purpose) purposes.push(fn.purpose)
|
|
109
107
|
}
|
|
110
108
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'node:fs/promises'
|
|
2
2
|
import * as path from 'node:path'
|
|
3
3
|
import type { MikkContract } from './schema.js'
|
|
4
|
-
import {
|
|
4
|
+
import { writeFileAtomic } from '../utils/atomic-write.js'
|
|
5
5
|
|
|
6
6
|
const VERSION = '@getmikk/cli@1.2.1'
|
|
7
7
|
|
|
@@ -21,7 +21,7 @@ export class ContractWriter {
|
|
|
21
21
|
async writeNew(contract: MikkContract, outputPath: string): Promise<void> {
|
|
22
22
|
await fs.mkdir(path.dirname(outputPath), { recursive: true })
|
|
23
23
|
const json = JSON.stringify(contract, null, 2)
|
|
24
|
-
await
|
|
24
|
+
await writeFileAtomic(outputPath, json, { encoding: 'utf-8' })
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/** Update an existing mikk.json respecting overwrite mode */
|
|
@@ -109,6 +109,6 @@ export class ContractWriter {
|
|
|
109
109
|
})
|
|
110
110
|
|
|
111
111
|
await fs.mkdir(path.dirname(historyPath), { recursive: true })
|
|
112
|
-
await
|
|
112
|
+
await writeFileAtomic(historyPath, JSON.stringify(history, null, 2), { encoding: 'utf-8' })
|
|
113
113
|
}
|
|
114
114
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import * as path from 'node:path'
|
|
2
|
-
import { createHash } from 'node:crypto'
|
|
3
1
|
import type { MikkContract, MikkLock } from './schema.js'
|
|
4
2
|
import type { DependencyGraph } from '../graph/types.js'
|
|
5
3
|
import type { ParsedFile } from '../parser/types.js'
|
|
@@ -8,6 +6,7 @@ import * as nodePath from 'node:path'
|
|
|
8
6
|
import { hashContent } from '../hash/file-hasher.js'
|
|
9
7
|
import { computeModuleHash, computeRootHash } from '../hash/tree-hasher.js'
|
|
10
8
|
import { minimatch } from '../utils/minimatch.js'
|
|
9
|
+
import { randomUUID } from 'node:crypto'
|
|
11
10
|
|
|
12
11
|
const VERSION = '@getmikk/cli@1.2.1'
|
|
13
12
|
|
|
@@ -155,6 +154,8 @@ export class LockCompiler {
|
|
|
155
154
|
lastSyncAt: new Date().toISOString(),
|
|
156
155
|
lockHash: '',
|
|
157
156
|
contractHash: hashContent(JSON.stringify(contract)),
|
|
157
|
+
generationId: randomUUID(),
|
|
158
|
+
writeVersion: 0,
|
|
158
159
|
},
|
|
159
160
|
modules,
|
|
160
161
|
functions,
|
|
@@ -338,7 +339,7 @@ export class LockCompiler {
|
|
|
338
339
|
private compileFiles(
|
|
339
340
|
parsedFiles: ParsedFile[],
|
|
340
341
|
contract: MikkContract,
|
|
341
|
-
|
|
342
|
+
_graph: DependencyGraph
|
|
342
343
|
): Record<string, MikkLock['files'][string]> {
|
|
343
344
|
const result: Record<string, MikkLock['files'][string]> = {}
|
|
344
345
|
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises'
|
|
2
1
|
import { MikkLockSchema, type MikkLock } from './schema.js'
|
|
3
2
|
import { LockNotFoundError } from '../utils/errors.js'
|
|
4
3
|
import { readJsonSafe } from '../utils/json.js'
|
|
4
|
+
import { writeFileAtomic } from '../utils/atomic-write.js'
|
|
5
|
+
import { randomUUID } from 'node:crypto'
|
|
6
|
+
|
|
7
|
+
export interface LockWriteOptions {
|
|
8
|
+
expectedGenerationId?: string
|
|
9
|
+
expectedWriteVersion?: number
|
|
10
|
+
}
|
|
5
11
|
|
|
6
12
|
/**
|
|
7
13
|
* LockReader -- reads and validates mikk.lock.json from disk.
|
|
@@ -32,11 +38,62 @@ export class LockReader {
|
|
|
32
38
|
return result.data
|
|
33
39
|
}
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
async write(lock: MikkLock, lockPath: string): Promise<void> {
|
|
41
|
+
serialize(lock: MikkLock): string {
|
|
37
42
|
const compact = compactifyLock(lock)
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
return JSON.stringify(compact)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async prepareForWrite(lock: MikkLock, lockPath: string, options: LockWriteOptions = {}): Promise<MikkLock> {
|
|
47
|
+
let existing: MikkLock | null = null
|
|
48
|
+
try {
|
|
49
|
+
existing = await this.read(lockPath)
|
|
50
|
+
} catch (err: any) {
|
|
51
|
+
if (!(err instanceof LockNotFoundError)) {
|
|
52
|
+
throw err
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const prepared: MikkLock = {
|
|
57
|
+
...lock,
|
|
58
|
+
syncState: {
|
|
59
|
+
...lock.syncState,
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (existing) {
|
|
64
|
+
const existingGeneration = existing.syncState.generationId
|
|
65
|
+
const existingWriteVersion = existing.syncState.writeVersion ?? 0
|
|
66
|
+
|
|
67
|
+
if (
|
|
68
|
+
options.expectedGenerationId &&
|
|
69
|
+
existingGeneration &&
|
|
70
|
+
options.expectedGenerationId !== existingGeneration
|
|
71
|
+
) {
|
|
72
|
+
throw new Error('Lock write rejected: generation mismatch (stale writer).')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (
|
|
76
|
+
typeof options.expectedWriteVersion === 'number' &&
|
|
77
|
+
options.expectedWriteVersion !== existingWriteVersion
|
|
78
|
+
) {
|
|
79
|
+
throw new Error('Lock write rejected: writeVersion mismatch (stale writer).')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
prepared.syncState.generationId = existingGeneration || prepared.syncState.generationId || randomUUID()
|
|
83
|
+
prepared.syncState.writeVersion = existingWriteVersion + 1
|
|
84
|
+
} else {
|
|
85
|
+
prepared.syncState.generationId = prepared.syncState.generationId || randomUUID()
|
|
86
|
+
prepared.syncState.writeVersion = prepared.syncState.writeVersion ?? 0
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return prepared
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Write lock file to disk in compact format */
|
|
93
|
+
async write(lock: MikkLock, lockPath: string, options: LockWriteOptions = {}): Promise<void> {
|
|
94
|
+
const prepared = await this.prepareForWrite(lock, lockPath, options)
|
|
95
|
+
const json = this.serialize(prepared)
|
|
96
|
+
await writeFileAtomic(lockPath, json, { encoding: 'utf-8' })
|
|
40
97
|
}
|
|
41
98
|
}
|
|
42
99
|
|
package/src/contract/schema.ts
CHANGED
|
@@ -174,6 +174,14 @@ export const MikkLockSchema = z.object({
|
|
|
174
174
|
lastSyncAt: z.string(),
|
|
175
175
|
lockHash: z.string(),
|
|
176
176
|
contractHash: z.string(),
|
|
177
|
+
generationId: z.string().optional(),
|
|
178
|
+
writeVersion: z.number().int().nonnegative().optional(),
|
|
179
|
+
parseDiagnostics: z.object({
|
|
180
|
+
requestedFiles: z.number().int().nonnegative(),
|
|
181
|
+
parsedFiles: z.number().int().nonnegative(),
|
|
182
|
+
fallbackFiles: z.number().int().nonnegative(),
|
|
183
|
+
diagnostics: z.number().int().nonnegative(),
|
|
184
|
+
}).optional(),
|
|
177
185
|
}),
|
|
178
186
|
modules: z.record(MikkLockModuleSchema),
|
|
179
187
|
functions: z.record(MikkLockFunctionSchema),
|
package/src/index.ts
CHANGED
|
@@ -13,4 +13,15 @@ export type { } from './error-handler.js'
|
|
|
13
13
|
export { discoverFiles, discoverContextFiles, readFileContent, writeFileContent, fileExists, setupMikkDirectory, readMikkIgnore, parseMikkIgnore, detectProjectLanguage, getDiscoveryPatterns, generateMikkIgnore, updateGitIgnore, cleanupGitIgnore } from './utils/fs.js'
|
|
14
14
|
export type { ContextFile, ContextFileType, ProjectLanguage } from './utils/fs.js'
|
|
15
15
|
export { minimatch } from './utils/minimatch.js'
|
|
16
|
-
export { scoreFunctions, findFuzzyMatches, levenshtein, splitCamelCase, extractKeywords } from './utils/fuzzy-match.js'
|
|
16
|
+
export { scoreFunctions, findFuzzyMatches, levenshtein, splitCamelCase, extractKeywords } from './utils/fuzzy-match.js'
|
|
17
|
+
export { writeFileAtomic, writeJsonAtomic } from './utils/atomic-write.js'
|
|
18
|
+
export type { AtomicWriteOptions } from './utils/atomic-write.js'
|
|
19
|
+
export {
|
|
20
|
+
runArtifactWriteTransaction,
|
|
21
|
+
recoverArtifactWriteTransactions,
|
|
22
|
+
} from './utils/artifact-transaction.js'
|
|
23
|
+
export type {
|
|
24
|
+
ArtifactWriteInput,
|
|
25
|
+
ArtifactTransactionOptions,
|
|
26
|
+
RecoverySummary,
|
|
27
|
+
} from './utils/artifact-transaction.js'
|