mdkg 0.1.2 → 0.1.4

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 (46) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +31 -15
  3. package/dist/cli.js +182 -85
  4. package/dist/commands/archive.js +20 -8
  5. package/dist/commands/bundle.js +7 -7
  6. package/dist/commands/capability.js +118 -4
  7. package/dist/commands/checkpoint.js +31 -5
  8. package/dist/commands/doctor.js +61 -24
  9. package/dist/commands/index.js +12 -23
  10. package/dist/commands/init.js +7 -1
  11. package/dist/commands/list.js +1 -1
  12. package/dist/commands/new.js +33 -7
  13. package/dist/commands/next.js +1 -1
  14. package/dist/commands/node_card.js +1 -1
  15. package/dist/commands/pack.js +1 -1
  16. package/dist/commands/search.js +1 -1
  17. package/dist/commands/show.js +1 -1
  18. package/dist/commands/subgraph.js +312 -0
  19. package/dist/commands/task.js +21 -7
  20. package/dist/commands/upgrade.js +51 -4
  21. package/dist/commands/validate.js +12 -6
  22. package/dist/commands/work.js +44 -12
  23. package/dist/core/config.js +110 -39
  24. package/dist/graph/capabilities_index_cache.js +2 -2
  25. package/dist/graph/index_cache.js +14 -14
  26. package/dist/graph/indexer.js +1 -1
  27. package/dist/graph/reindex.js +46 -0
  28. package/dist/graph/skills_index_cache.js +2 -2
  29. package/dist/graph/sqlite_index.js +293 -0
  30. package/dist/graph/{bundle_imports.js → subgraphs.js} +216 -142
  31. package/dist/graph/visibility.js +3 -3
  32. package/dist/init/AGENT_START.md +5 -1
  33. package/dist/init/CLI_COMMAND_MATRIX.md +21 -7
  34. package/dist/init/README.md +20 -10
  35. package/dist/init/config.json +6 -2
  36. package/dist/init/core/rule-1-mdkg-conventions.md +2 -1
  37. package/dist/init/core/rule-3-cli-contract.md +32 -24
  38. package/dist/init/core/rule-4-repo-safety-and-ignores.md +28 -12
  39. package/dist/init/core/rule-5-release-and-versioning.md +4 -3
  40. package/dist/init/init-manifest.json +10 -10
  41. package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +1 -1
  42. package/dist/util/argparse.js +2 -0
  43. package/dist/util/atomic.js +44 -0
  44. package/dist/util/lock.js +72 -0
  45. package/package.json +13 -9
  46. package/dist/commands/bundle_import.js +0 -243
@@ -22,7 +22,12 @@ const PACK_EDGE_KEYS = new Set([
22
22
  const NEXT_WORK_STRATEGIES = new Set(["chain_then_priority"]);
23
23
  const WORKSPACE_VISIBILITY_VALUES = new Set(["private", "internal", "public"]);
24
24
  const BUNDLE_PROFILE_VALUES = new Set(["private", "public"]);
25
+ const INDEX_BACKEND_VALUES = new Set(["json", "sqlite"]);
26
+ const SUBGRAPH_PERMISSION_VALUES = new Set(["read", "request", "propose", "mutate"]);
25
27
  const DEFAULT_ARCHIVE_LARGE_CACHE_WARNING_BYTES = 26214400;
28
+ const DEFAULT_SQLITE_COMMIT_WARNING_BYTES = 52428800;
29
+ const DEFAULT_LOCK_TIMEOUT_MS = 10000;
30
+ const DEFAULT_SUBGRAPH_MAX_STALE_SECONDS = 3600;
26
31
  function isObject(value) {
27
32
  return typeof value === "object" && value !== null && !Array.isArray(value);
28
33
  }
@@ -169,16 +174,16 @@ function validateWorkspaceAlias(alias, errors) {
169
174
  errors.push(`workspaces.${alias} alias must be lowercase and use [a-z0-9_]`);
170
175
  }
171
176
  }
172
- function validateBundleImportAlias(alias, workspaces, errors) {
177
+ function validateSubgraphAlias(alias, workspaces, errors) {
173
178
  if (alias === "all") {
174
- errors.push("bundle_imports.all alias is reserved");
179
+ errors.push("subgraphs.all alias is reserved");
175
180
  return;
176
181
  }
177
182
  if (alias !== alias.toLowerCase() || !WORKSPACE_ALIAS_RE.test(alias)) {
178
- errors.push(`bundle_imports.${alias} alias must be lowercase and use [a-z0-9_]`);
183
+ errors.push(`subgraphs.${alias} alias must be lowercase and use [a-z0-9_]`);
179
184
  }
180
185
  if (workspaces[alias]) {
181
- errors.push(`bundle_imports.${alias} must not collide with workspaces.${alias}`);
186
+ errors.push(`subgraphs.${alias} must not collide with workspaces.${alias}`);
182
187
  }
183
188
  }
184
189
  function requireContainedPath(value, path, errors) {
@@ -213,9 +218,15 @@ function validateConfigSchema(raw) {
213
218
  const bundlesRaw = raw.bundles === undefined
214
219
  ? { output_dir: ".mdkg/bundles", default_profile: "private" }
215
220
  : requireObject(raw.bundles, "bundles", errors);
216
- const bundleImportsRaw = raw.bundle_imports === undefined
217
- ? {}
218
- : requireObject(raw.bundle_imports, "bundle_imports", errors);
221
+ if (raw.bundle_imports !== undefined && raw.subgraphs !== undefined) {
222
+ errors.push("config must not define both subgraphs and legacy bundle_imports");
223
+ }
224
+ const subgraphsFromLegacyBundleImports = raw.subgraphs === undefined && raw.bundle_imports !== undefined;
225
+ const subgraphsRaw = raw.subgraphs === undefined
226
+ ? raw.bundle_imports === undefined
227
+ ? {}
228
+ : requireObject(raw.bundle_imports, "bundle_imports", errors)
229
+ : requireObject(raw.subgraphs, "subgraphs", errors);
219
230
  const packRaw = requireObject(raw.pack, "pack", errors);
220
231
  const templatesRaw = requireObject(raw.templates, "templates", errors);
221
232
  const workRaw = requireObject(raw.work, "work", errors);
@@ -231,7 +242,19 @@ function validateConfigSchema(raw) {
231
242
  ? {
232
243
  auto_reindex: requireBoolean(indexRaw.auto_reindex, "index.auto_reindex", errors),
233
244
  tolerant: requireBoolean(indexRaw.tolerant, "index.tolerant", errors),
245
+ backend: indexRaw.backend === undefined
246
+ ? "json"
247
+ : requireStringInSet(indexRaw.backend, "index.backend", INDEX_BACKEND_VALUES, errors),
234
248
  global_index_path: requireContainedPath(indexRaw.global_index_path, "index.global_index_path", errors),
249
+ sqlite_path: indexRaw.sqlite_path === undefined
250
+ ? ".mdkg/index/mdkg.sqlite"
251
+ : requireContainedPath(indexRaw.sqlite_path, "index.sqlite_path", errors),
252
+ sqlite_commit_warning_bytes: indexRaw.sqlite_commit_warning_bytes === undefined
253
+ ? DEFAULT_SQLITE_COMMIT_WARNING_BYTES
254
+ : requireNonNegativeInteger(indexRaw.sqlite_commit_warning_bytes, "index.sqlite_commit_warning_bytes", errors),
255
+ lock_timeout_ms: indexRaw.lock_timeout_ms === undefined
256
+ ? DEFAULT_LOCK_TIMEOUT_MS
257
+ : requirePositiveInteger(indexRaw.lock_timeout_ms, "index.lock_timeout_ms", errors),
235
258
  }
236
259
  : undefined;
237
260
  const capabilities = capabilitiesRaw
@@ -374,48 +397,96 @@ function validateConfigSchema(raw) {
374
397
  errors.push('workspaces.root.mdkg_dir must be ".mdkg"');
375
398
  }
376
399
  }
377
- const bundle_imports = {};
378
- if (bundleImportsRaw) {
379
- for (const [alias, entry] of Object.entries(bundleImportsRaw)) {
380
- validateBundleImportAlias(alias, workspaces, errors);
381
- const rawImport = requireObject(entry, `bundle_imports.${alias}`, errors);
382
- if (!rawImport) {
400
+ const subgraphs = {};
401
+ if (subgraphsRaw) {
402
+ for (const [alias, entry] of Object.entries(subgraphsRaw)) {
403
+ validateSubgraphAlias(alias, workspaces, errors);
404
+ const basePath = subgraphsFromLegacyBundleImports ? `bundle_imports.${alias}` : `subgraphs.${alias}`;
405
+ const rawSubgraph = requireObject(entry, basePath, errors);
406
+ if (!rawSubgraph) {
383
407
  continue;
384
408
  }
385
- const importPath = requireContainedPath(rawImport.path, `bundle_imports.${alias}.path`, errors);
386
- const enabled = rawImport.enabled === undefined
409
+ const enabled = rawSubgraph.enabled === undefined
387
410
  ? true
388
- : requireBoolean(rawImport.enabled, `bundle_imports.${alias}.enabled`, errors);
389
- const visibility = rawImport.visibility === undefined
411
+ : requireBoolean(rawSubgraph.enabled, `${basePath}.enabled`, errors);
412
+ const visibility = rawSubgraph.visibility === undefined
390
413
  ? "private"
391
- : requireStringInSet(rawImport.visibility, `bundle_imports.${alias}.visibility`, WORKSPACE_VISIBILITY_VALUES, errors);
392
- const expectedProfile = rawImport.expected_profile === undefined
393
- ? "private"
394
- : requireStringInSet(rawImport.expected_profile, `bundle_imports.${alias}.expected_profile`, BUNDLE_PROFILE_VALUES, errors);
395
- const sourcePath = rawImport.source_path === undefined
396
- ? undefined
397
- : requireContainedPath(rawImport.source_path, `bundle_imports.${alias}.source_path`, errors);
398
- const sourceRepo = rawImport.source_repo === undefined
414
+ : requireStringInSet(rawSubgraph.visibility, `${basePath}.visibility`, WORKSPACE_VISIBILITY_VALUES, errors);
415
+ const permissions = rawSubgraph.permissions === undefined || subgraphsFromLegacyBundleImports
416
+ ? ["read"]
417
+ : requireKnownLowercaseUniqueStringArray(rawSubgraph.permissions, `${basePath}.permissions`, SUBGRAPH_PERMISSION_VALUES, errors);
418
+ const maxStaleSeconds = rawSubgraph.max_stale_seconds === undefined
419
+ ? DEFAULT_SUBGRAPH_MAX_STALE_SECONDS
420
+ : requirePositiveInteger(rawSubgraph.max_stale_seconds, `${basePath}.max_stale_seconds`, errors);
421
+ const sourcePath = rawSubgraph.source_path === undefined
399
422
  ? undefined
400
- : requireString(rawImport.source_repo, `bundle_imports.${alias}.source_repo`, errors);
401
- const maxStaleSeconds = rawImport.max_stale_seconds === undefined
423
+ : requireContainedPath(rawSubgraph.source_path, `${basePath}.source_path`, errors);
424
+ const sourceRepo = rawSubgraph.source_repo === undefined
402
425
  ? undefined
403
- : requirePositiveInteger(rawImport.max_stale_seconds, `bundle_imports.${alias}.max_stale_seconds`, errors);
404
- if (visibility !== undefined &&
405
- expectedProfile !== undefined &&
406
- visibility !== "private" &&
407
- expectedProfile !== "public") {
408
- errors.push(`bundle_imports.${alias}.expected_profile must be public when visibility is ${visibility}`);
426
+ : requireString(rawSubgraph.source_repo, `${basePath}.source_repo`, errors);
427
+ const rawSources = subgraphsFromLegacyBundleImports
428
+ ? [{ path: rawSubgraph.path, enabled: true, expected_profile: rawSubgraph.expected_profile }]
429
+ : rawSubgraph.sources;
430
+ if (!Array.isArray(rawSources)) {
431
+ errors.push(`${basePath}.sources must be an array`);
432
+ continue;
433
+ }
434
+ if (rawSources.length === 0) {
435
+ errors.push(`${basePath}.sources must not be empty`);
436
+ continue;
437
+ }
438
+ const sources = [];
439
+ const labels = new Set();
440
+ for (let i = 0; i < rawSources.length; i += 1) {
441
+ const sourceBasePath = `${basePath}.sources[${i}]`;
442
+ const rawSource = requireObject(rawSources[i], sourceBasePath, errors);
443
+ if (!rawSource) {
444
+ continue;
445
+ }
446
+ const sourceBundlePath = requireContainedPath(rawSource.path, `${sourceBasePath}.path`, errors);
447
+ const sourceEnabled = rawSource.enabled === undefined
448
+ ? true
449
+ : requireBoolean(rawSource.enabled, `${sourceBasePath}.enabled`, errors);
450
+ const expectedProfile = rawSource.expected_profile === undefined
451
+ ? "private"
452
+ : requireStringInSet(rawSource.expected_profile, `${sourceBasePath}.expected_profile`, BUNDLE_PROFILE_VALUES, errors);
453
+ const label = rawSource.label === undefined
454
+ ? undefined
455
+ : requireString(rawSource.label, `${sourceBasePath}.label`, errors);
456
+ if (label !== undefined) {
457
+ if (label.trim().length === 0) {
458
+ errors.push(`${sourceBasePath}.label must not be empty`);
459
+ }
460
+ if (labels.has(label)) {
461
+ errors.push(`${basePath}.sources label must be unique: ${label}`);
462
+ }
463
+ labels.add(label);
464
+ }
465
+ if (visibility !== undefined &&
466
+ expectedProfile !== undefined &&
467
+ sourceEnabled !== false &&
468
+ visibility !== "private" &&
469
+ expectedProfile !== "public") {
470
+ errors.push(`${sourceBasePath}.expected_profile must be public when ${basePath}.visibility is ${visibility}`);
471
+ }
472
+ if (sourceBundlePath && sourceEnabled !== undefined && expectedProfile) {
473
+ sources.push({
474
+ path: sourceBundlePath,
475
+ enabled: sourceEnabled,
476
+ expected_profile: expectedProfile,
477
+ ...(label ? { label } : {}),
478
+ });
479
+ }
409
480
  }
410
- if (importPath && enabled !== undefined && visibility && expectedProfile) {
411
- bundle_imports[alias] = {
412
- path: importPath,
481
+ if (enabled !== undefined && visibility && permissions && maxStaleSeconds !== undefined && sources.length > 0) {
482
+ subgraphs[alias] = {
413
483
  enabled,
414
484
  visibility: visibility,
415
- expected_profile: expectedProfile,
485
+ permissions,
486
+ max_stale_seconds: maxStaleSeconds,
416
487
  ...(sourcePath ? { source_path: sourcePath } : {}),
417
488
  ...(sourceRepo ? { source_repo: sourceRepo } : {}),
418
- ...(maxStaleSeconds !== undefined ? { max_stale_seconds: maxStaleSeconds } : {}),
489
+ sources,
419
490
  };
420
491
  }
421
492
  }
@@ -431,7 +502,7 @@ function validateConfigSchema(raw) {
431
502
  index: index,
432
503
  capabilities: capabilities,
433
504
  bundles: bundles,
434
- bundle_imports,
505
+ subgraphs,
435
506
  pack: pack,
436
507
  templates: templates,
437
508
  work: work,
@@ -9,6 +9,7 @@ exports.loadCapabilitiesIndex = loadCapabilitiesIndex;
9
9
  const fs_1 = __importDefault(require("fs"));
10
10
  const path_1 = __importDefault(require("path"));
11
11
  const paths_1 = require("../core/paths");
12
+ const atomic_1 = require("../util/atomic");
12
13
  const workspace_files_1 = require("./workspace_files");
13
14
  const capabilities_indexer_1 = require("./capabilities_indexer");
14
15
  function mtimeMs(filePath) {
@@ -76,8 +77,7 @@ function readCapabilitiesIndex(indexPath) {
76
77
  }
77
78
  }
78
79
  function writeCapabilitiesIndex(indexPath, index) {
79
- fs_1.default.mkdirSync(path_1.default.dirname(indexPath), { recursive: true });
80
- fs_1.default.writeFileSync(indexPath, JSON.stringify(index, null, 2), "utf8");
80
+ (0, atomic_1.atomicWriteFile)(indexPath, JSON.stringify(index, null, 2));
81
81
  }
82
82
  function loadCapabilitiesIndex(options) {
83
83
  const useCache = options.useCache ?? true;
@@ -8,9 +8,10 @@ exports.loadIndex = loadIndex;
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const sort_1 = require("../util/sort");
11
+ const atomic_1 = require("../util/atomic");
11
12
  const indexer_1 = require("./indexer");
12
13
  const staleness_1 = require("./staleness");
13
- const bundle_imports_1 = require("./bundle_imports");
14
+ const subgraphs_1 = require("./subgraphs");
14
15
  function readIndex(indexPath) {
15
16
  try {
16
17
  const raw = fs_1.default.readFileSync(indexPath, "utf8");
@@ -23,8 +24,7 @@ function readIndex(indexPath) {
23
24
  }
24
25
  function writeIndex(indexPath, index) {
25
26
  const sortedIndex = { ...index, nodes: (0, sort_1.sortIndexNodes)(index.nodes) };
26
- fs_1.default.mkdirSync(path_1.default.dirname(indexPath), { recursive: true });
27
- fs_1.default.writeFileSync(indexPath, JSON.stringify(sortedIndex, null, 2));
27
+ (0, atomic_1.atomicWriteFile)(indexPath, JSON.stringify(sortedIndex, null, 2));
28
28
  }
29
29
  function loadIndex(options) {
30
30
  const useCache = options.useCache ?? true;
@@ -32,36 +32,36 @@ function loadIndex(options) {
32
32
  const tolerant = options.tolerant ?? options.config.index.tolerant;
33
33
  const includeImports = options.includeImports ?? true;
34
34
  const indexPath = path_1.default.resolve(options.root, options.config.index.global_index_path);
35
- const withImports = (index, rebuilt, stale) => {
36
- if (!includeImports || Object.keys(options.config.bundle_imports).length === 0) {
35
+ const withSubgraphs = (index, rebuilt, stale) => {
36
+ if (!includeImports || Object.keys(options.config.subgraphs).length === 0) {
37
37
  return { index, rebuilt, stale, warnings: [] };
38
38
  }
39
- const imports = (0, bundle_imports_1.buildBundleImportsIndex)(options.root, options.config);
39
+ const subgraphs = (0, subgraphs_1.buildSubgraphsIndex)(options.root, options.config);
40
40
  if (allowReindex) {
41
- (0, bundle_imports_1.writeBundleImportsIndex)((0, bundle_imports_1.resolveBundleImportsIndexPath)(options.root), imports.index);
41
+ (0, subgraphs_1.writeSubgraphsIndex)((0, subgraphs_1.resolveSubgraphsIndexPath)(options.root), subgraphs.index);
42
42
  }
43
43
  return {
44
- index: (0, bundle_imports_1.mergeBundleImportsIntoIndex)(index, imports),
44
+ index: (0, subgraphs_1.mergeSubgraphsIntoIndex)(index, subgraphs),
45
45
  rebuilt,
46
- stale: stale || (0, bundle_imports_1.isBundleImportsIndexStale)(options.root, options.config),
47
- warnings: (0, bundle_imports_1.importWarnings)(imports),
46
+ stale: stale || (0, subgraphs_1.isSubgraphsIndexStale)(options.root, options.config),
47
+ warnings: (0, subgraphs_1.subgraphWarnings)(subgraphs),
48
48
  };
49
49
  };
50
50
  if (!useCache) {
51
51
  const index = (0, indexer_1.buildIndex)(options.root, options.config, { tolerant });
52
- return withImports(index, true, false);
52
+ return withSubgraphs(index, true, false);
53
53
  }
54
54
  const stale = (0, staleness_1.isIndexStale)(options.root, options.config);
55
55
  if (fs_1.default.existsSync(indexPath) && !stale) {
56
- return withImports(readIndex(indexPath), false, false);
56
+ return withSubgraphs(readIndex(indexPath), false, false);
57
57
  }
58
58
  if (allowReindex) {
59
59
  const index = (0, indexer_1.buildIndex)(options.root, options.config, { tolerant });
60
60
  writeIndex(indexPath, index);
61
- return withImports(index, true, stale);
61
+ return withSubgraphs(index, true, stale);
62
62
  }
63
63
  if (fs_1.default.existsSync(indexPath)) {
64
- return withImports(readIndex(indexPath), false, true);
64
+ return withSubgraphs(readIndex(indexPath), false, true);
65
65
  }
66
66
  throw new Error("index missing and auto-reindex is disabled");
67
67
  }
@@ -143,7 +143,7 @@ function buildIndex(root, config, options = {}) {
143
143
  };
144
144
  (0, validate_graph_1.validateGraph)(index, {
145
145
  allowMissing: tolerant,
146
- externalWorkspaces: new Set(Object.keys(config.bundle_imports ?? {})),
146
+ externalWorkspaces: new Set(Object.keys(config.subgraphs ?? {})),
147
147
  });
148
148
  const latestCheckpointByWorkspace = {};
149
149
  for (const alias of workspaceAliases) {
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.writeDerivedIndexes = writeDerivedIndexes;
7
+ const path_1 = __importDefault(require("path"));
8
+ const capabilities_indexer_1 = require("./capabilities_indexer");
9
+ const capabilities_index_cache_1 = require("./capabilities_index_cache");
10
+ const subgraphs_1 = require("./subgraphs");
11
+ const indexer_1 = require("./indexer");
12
+ const index_cache_1 = require("./index_cache");
13
+ const skills_index_cache_1 = require("./skills_index_cache");
14
+ const skills_indexer_1 = require("./skills_indexer");
15
+ const sqlite_index_1 = require("./sqlite_index");
16
+ function writeDerivedIndexes(root, config, nodeIndex, options) {
17
+ const nextNodeIndex = nodeIndex ?? (0, indexer_1.buildIndex)(root, config, { tolerant: options?.tolerant ?? config.index.tolerant });
18
+ const skillsIndex = (0, skills_indexer_1.buildSkillsIndex)(root, config);
19
+ const capabilitiesIndex = (0, capabilities_indexer_1.buildCapabilitiesIndex)(root, config, nextNodeIndex);
20
+ const subgraphsIndex = (0, subgraphs_1.buildSubgraphsIndex)(root, config);
21
+ const nodesOutputPath = path_1.default.resolve(root, config.index.global_index_path);
22
+ const skillsOutputPath = (0, skills_indexer_1.resolveSkillsIndexPath)(root);
23
+ const capabilitiesOutputPath = (0, capabilities_indexer_1.resolveCapabilitiesIndexPath)(root, config);
24
+ const subgraphsOutputPath = (0, subgraphs_1.resolveSubgraphsIndexPath)(root);
25
+ (0, index_cache_1.writeIndex)(nodesOutputPath, nextNodeIndex);
26
+ (0, skills_index_cache_1.writeSkillsIndex)(skillsOutputPath, skillsIndex);
27
+ (0, capabilities_index_cache_1.writeCapabilitiesIndex)(capabilitiesOutputPath, capabilitiesIndex);
28
+ (0, subgraphs_1.writeSubgraphsIndex)(subgraphsOutputPath, subgraphsIndex.index);
29
+ const paths = {
30
+ nodes: nodesOutputPath,
31
+ skills: skillsOutputPath,
32
+ capabilities: capabilitiesOutputPath,
33
+ subgraphs: subgraphsOutputPath,
34
+ };
35
+ if ((0, sqlite_index_1.isSqliteBackend)(config)) {
36
+ paths.sqlite = (0, sqlite_index_1.writeSqliteIndex)({
37
+ root,
38
+ config,
39
+ nodeIndex: nextNodeIndex,
40
+ skillsIndex,
41
+ capabilitiesIndex,
42
+ subgraphsIndex: subgraphsIndex.index,
43
+ });
44
+ }
45
+ return { nodeIndex: nextNodeIndex, paths };
46
+ }
@@ -8,6 +8,7 @@ exports.loadSkillsIndex = loadSkillsIndex;
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const paths_1 = require("../core/paths");
11
+ const atomic_1 = require("../util/atomic");
11
12
  const skills_indexer_1 = require("./skills_indexer");
12
13
  function mtimeMs(filePath) {
13
14
  return fs_1.default.statSync(filePath).mtimeMs;
@@ -67,8 +68,7 @@ function writeSkillsIndex(indexPath, index) {
67
68
  ...index,
68
69
  skills: sortedSkills,
69
70
  };
70
- fs_1.default.mkdirSync(path_1.default.dirname(indexPath), { recursive: true });
71
- fs_1.default.writeFileSync(indexPath, JSON.stringify(sortedIndex, null, 2), "utf8");
71
+ (0, atomic_1.atomicWriteFile)(indexPath, JSON.stringify(sortedIndex, null, 2));
72
72
  }
73
73
  function loadSkillsIndex(options) {
74
74
  const useCache = options.useCache ?? true;