@getmikk/core 2.0.12 → 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 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 language parsers, each following the same interface: `parse(filePath, content)` → `ParsedFile`.
18
+ Three parser families follow the same interface: `parse(filePath, content)` → `ParsedFile`.
19
19
 
20
20
  **TypeScript / TSX**
21
- Uses the TypeScript Compiler API. 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.
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 the TypeScript Compiler API with `ScriptKind` inference (detects JS/JSX/CJS/MJS). Handles: JSX expression containers, default exports, CommonJS `module.exports`, re-exports via barrel files.
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,6 @@
1
1
  {
2
2
  "name": "@getmikk/core",
3
- "version": "2.0.12",
3
+ "version": "2.0.13",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org/"
@@ -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 raw = await fs.readFile(this.contractPath, 'utf-8')
69
- return MikkContractSchema.parse(JSON.parse(raw))
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 fs.writeFile(this.contractPath, JSON.stringify(contract, null, 2), 'utf-8')
74
+ await writeFileAtomic(this.contractPath, JSON.stringify(contract, null, 2), { encoding: 'utf-8' })
74
75
  }
75
76
  }
@@ -1,6 +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 { writeFileAtomic } from '../utils/atomic-write.js'
4
5
 
5
6
  const VERSION = '@getmikk/cli@1.2.1'
6
7
 
@@ -20,7 +21,7 @@ export class ContractWriter {
20
21
  async writeNew(contract: MikkContract, outputPath: string): Promise<void> {
21
22
  await fs.mkdir(path.dirname(outputPath), { recursive: true })
22
23
  const json = JSON.stringify(contract, null, 2)
23
- await fs.writeFile(outputPath, json, 'utf-8')
24
+ await writeFileAtomic(outputPath, json, { encoding: 'utf-8' })
24
25
  }
25
26
 
26
27
  /** Update an existing mikk.json respecting overwrite mode */
@@ -108,6 +109,6 @@ export class ContractWriter {
108
109
  })
109
110
 
110
111
  await fs.mkdir(path.dirname(historyPath), { recursive: true })
111
- await fs.writeFile(historyPath, JSON.stringify(history, null, 2), 'utf-8')
112
+ await writeFileAtomic(historyPath, JSON.stringify(history, null, 2), { encoding: 'utf-8' })
112
113
  }
113
114
  }
@@ -6,6 +6,7 @@ import * as nodePath from 'node:path'
6
6
  import { hashContent } from '../hash/file-hasher.js'
7
7
  import { computeModuleHash, computeRootHash } from '../hash/tree-hasher.js'
8
8
  import { minimatch } from '../utils/minimatch.js'
9
+ import { randomUUID } from 'node:crypto'
9
10
 
10
11
  const VERSION = '@getmikk/cli@1.2.1'
11
12
 
@@ -153,6 +154,8 @@ export class LockCompiler {
153
154
  lastSyncAt: new Date().toISOString(),
154
155
  lockHash: '',
155
156
  contractHash: hashContent(JSON.stringify(contract)),
157
+ generationId: randomUUID(),
158
+ writeVersion: 0,
156
159
  },
157
160
  modules,
158
161
  functions,
@@ -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
- /** Write lock file to disk in compact format */
36
- async write(lock: MikkLock, lockPath: string): Promise<void> {
41
+ serialize(lock: MikkLock): string {
37
42
  const compact = compactifyLock(lock)
38
- const json = JSON.stringify(compact)
39
- await fs.writeFile(lockPath, json, 'utf-8')
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
 
@@ -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'