@neat.is/core 0.3.1 → 0.3.2

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/dist/cli.cjs CHANGED
@@ -381,7 +381,7 @@ __export(cli_exports, {
381
381
  module.exports = __toCommonJS(cli_exports);
382
382
  init_cjs_shims();
383
383
  var import_node_path37 = __toESM(require("path"), 1);
384
- var import_node_fs22 = require("fs");
384
+ var import_node_fs23 = require("fs");
385
385
 
386
386
  // src/graph.ts
387
387
  init_cjs_shims();
@@ -3901,6 +3901,7 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
3901
3901
 
3902
3902
  // src/watch.ts
3903
3903
  init_cjs_shims();
3904
+ var import_node_fs20 = __toESM(require("fs"), 1);
3904
3905
  var import_node_path34 = __toESM(require("path"), 1);
3905
3906
  var import_chokidar = __toESM(require("chokidar"), 1);
3906
3907
 
@@ -5301,6 +5302,16 @@ async function runExtractPhases(graph, scanPath, phases, project = DEFAULT_PROJE
5301
5302
  durationMs: Date.now() - started
5302
5303
  };
5303
5304
  }
5305
+ var IGNORED_WATCH_GLOBS = [
5306
+ "**/node_modules/**",
5307
+ "**/.git/**",
5308
+ "**/dist/**",
5309
+ "**/build/**",
5310
+ "**/.turbo/**",
5311
+ "**/.next/**",
5312
+ "**/neat-out/**",
5313
+ "**/.DS_Store"
5314
+ ];
5304
5315
  var IGNORED_WATCH_PATHS = [
5305
5316
  /(?:^|[\\/])node_modules[\\/]/,
5306
5317
  /(?:^|[\\/])\.git[\\/]/,
@@ -5314,6 +5325,35 @@ var IGNORED_WATCH_PATHS = [
5314
5325
  function shouldIgnore(absPath) {
5315
5326
  return IGNORED_WATCH_PATHS.some((re) => re.test(absPath));
5316
5327
  }
5328
+ var DARWIN_POLLING_DIR_THRESHOLD = 400;
5329
+ function countWatchableDirs(scanPath, limit) {
5330
+ let count = 0;
5331
+ const visit = (dir, depth) => {
5332
+ if (count >= limit) return;
5333
+ let entries;
5334
+ try {
5335
+ entries = import_node_fs20.default.readdirSync(dir, { withFileTypes: true });
5336
+ } catch {
5337
+ return;
5338
+ }
5339
+ for (const e of entries) {
5340
+ if (count >= limit) return;
5341
+ if (!e.isDirectory()) continue;
5342
+ if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path34.default.join(dir, e.name) + import_node_path34.default.sep))) continue;
5343
+ count++;
5344
+ if (depth < 2) visit(import_node_path34.default.join(dir, e.name), depth + 1);
5345
+ }
5346
+ };
5347
+ visit(scanPath, 0);
5348
+ return count;
5349
+ }
5350
+ function shouldUsePolling(scanPath) {
5351
+ const env = process.env.NEAT_WATCH_POLLING;
5352
+ if (env === "1" || env === "true") return true;
5353
+ if (env === "0" || env === "false") return false;
5354
+ if (process.platform !== "darwin") return false;
5355
+ return countWatchableDirs(scanPath, DARWIN_POLLING_DIR_THRESHOLD) >= DARWIN_POLLING_DIR_THRESHOLD;
5356
+ }
5317
5357
  async function startWatch(graph, opts) {
5318
5358
  const debounceMs = opts.debounceMs ?? 1e3;
5319
5359
  const projectName = opts.project ?? DEFAULT_PROJECT;
@@ -5483,10 +5523,19 @@ async function startWatch(graph, opts) {
5483
5523
  }
5484
5524
  schedule();
5485
5525
  };
5526
+ const usePolling = shouldUsePolling(opts.scanPath);
5527
+ if (usePolling) {
5528
+ const reason = process.env.NEAT_WATCH_POLLING === "1" || process.env.NEAT_WATCH_POLLING === "true" ? "NEAT_WATCH_POLLING env override" : "darwin heuristic \u2014 large scan root, kqueue cap risk";
5529
+ console.log(`[${projectName}] watch: usePolling=true (${reason})`);
5530
+ }
5486
5531
  const watcher = import_chokidar.default.watch(opts.scanPath, {
5487
5532
  ignoreInitial: true,
5488
- ignored: (p) => shouldIgnore(p),
5533
+ // Glob array prunes at descent time (#233) so chokidar never opens a
5534
+ // kqueue handle for `node_modules` and friends. The function backstop
5535
+ // catches any path that slipped through and matches the regex set.
5536
+ ignored: [...IGNORED_WATCH_GLOBS, (p) => shouldIgnore(p)],
5489
5537
  persistent: true,
5538
+ usePolling,
5490
5539
  awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
5491
5540
  });
5492
5541
  watcher.on("add", onPath);
@@ -5522,7 +5571,7 @@ init_cjs_shims();
5522
5571
 
5523
5572
  // src/installers/javascript.ts
5524
5573
  init_cjs_shims();
5525
- var import_node_fs20 = require("fs");
5574
+ var import_node_fs21 = require("fs");
5526
5575
  var import_node_path35 = __toESM(require("path"), 1);
5527
5576
  var SDK_PACKAGES = [
5528
5577
  { name: "@opentelemetry/api", version: "^1.9.0" },
@@ -5539,7 +5588,7 @@ var OTEL_ENV = {
5539
5588
  };
5540
5589
  async function readPackageJson(serviceDir) {
5541
5590
  try {
5542
- const raw = await import_node_fs20.promises.readFile(import_node_path35.default.join(serviceDir, "package.json"), "utf8");
5591
+ const raw = await import_node_fs21.promises.readFile(import_node_path35.default.join(serviceDir, "package.json"), "utf8");
5543
5592
  return JSON.parse(raw);
5544
5593
  } catch {
5545
5594
  return null;
@@ -5605,7 +5654,7 @@ async function apply(installPlan) {
5605
5654
  const originals = /* @__PURE__ */ new Map();
5606
5655
  for (const file of touched) {
5607
5656
  try {
5608
- originals.set(file, await import_node_fs20.promises.readFile(file, "utf8"));
5657
+ originals.set(file, await import_node_fs21.promises.readFile(file, "utf8"));
5609
5658
  } catch {
5610
5659
  }
5611
5660
  }
@@ -5631,8 +5680,8 @@ async function apply(installPlan) {
5631
5680
  }
5632
5681
  const newRaw = JSON.stringify(pkg, null, 2) + "\n";
5633
5682
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
5634
- await import_node_fs20.promises.writeFile(tmp, newRaw, "utf8");
5635
- await import_node_fs20.promises.rename(tmp, file);
5683
+ await import_node_fs21.promises.writeFile(tmp, newRaw, "utf8");
5684
+ await import_node_fs21.promises.rename(tmp, file);
5636
5685
  }
5637
5686
  } catch (err) {
5638
5687
  await rollback(installPlan, originals);
@@ -5643,7 +5692,7 @@ async function rollback(installPlan, originals) {
5643
5692
  const restored = [];
5644
5693
  for (const [file, raw] of originals.entries()) {
5645
5694
  try {
5646
- await import_node_fs20.promises.writeFile(file, raw, "utf8");
5695
+ await import_node_fs21.promises.writeFile(file, raw, "utf8");
5647
5696
  restored.push(file);
5648
5697
  } catch {
5649
5698
  }
@@ -5658,7 +5707,7 @@ async function rollback(installPlan, originals) {
5658
5707
  ""
5659
5708
  ];
5660
5709
  const rollbackPath = import_node_path35.default.join(installPlan.serviceDir, "neat-rollback.patch");
5661
- await import_node_fs20.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
5710
+ await import_node_fs21.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
5662
5711
  }
5663
5712
  var javascriptInstaller = {
5664
5713
  name: "javascript",
@@ -5669,7 +5718,7 @@ var javascriptInstaller = {
5669
5718
 
5670
5719
  // src/installers/python.ts
5671
5720
  init_cjs_shims();
5672
- var import_node_fs21 = require("fs");
5721
+ var import_node_fs22 = require("fs");
5673
5722
  var import_node_path36 = __toESM(require("path"), 1);
5674
5723
  var SDK_PACKAGES2 = [
5675
5724
  { name: "opentelemetry-distro", version: ">=0.49b0" },
@@ -5682,7 +5731,7 @@ var OTEL_ENV2 = {
5682
5731
  };
5683
5732
  async function exists2(p) {
5684
5733
  try {
5685
- await import_node_fs21.promises.stat(p);
5734
+ await import_node_fs22.promises.stat(p);
5686
5735
  return true;
5687
5736
  } catch {
5688
5737
  return false;
@@ -5703,7 +5752,7 @@ function reqPackageName(line) {
5703
5752
  async function planRequirementsTxtEdits(serviceDir) {
5704
5753
  const file = import_node_path36.default.join(serviceDir, "requirements.txt");
5705
5754
  if (!await exists2(file)) return null;
5706
- const raw = await import_node_fs21.promises.readFile(file, "utf8");
5755
+ const raw = await import_node_fs22.promises.readFile(file, "utf8");
5707
5756
  const presentNames = new Set(
5708
5757
  raw.split(/\r?\n/).map(reqPackageName).filter((n) => n.length > 0)
5709
5758
  );
@@ -5713,7 +5762,7 @@ async function planRequirementsTxtEdits(serviceDir) {
5713
5762
  async function planProcfileEdits(serviceDir) {
5714
5763
  const procfile = import_node_path36.default.join(serviceDir, "Procfile");
5715
5764
  if (!await exists2(procfile)) return [];
5716
- const raw = await import_node_fs21.promises.readFile(procfile, "utf8");
5765
+ const raw = await import_node_fs22.promises.readFile(procfile, "utf8");
5717
5766
  const edits = [];
5718
5767
  for (const line of raw.split(/\r?\n/)) {
5719
5768
  if (line.length === 0) continue;
@@ -5765,8 +5814,8 @@ async function applyRequirementsTxt(manifest, edits, original) {
5765
5814
  const next = `${original}${trailing}${newlines.join("\n")}
5766
5815
  `;
5767
5816
  const tmp = `${manifest}.${process.pid}.${Date.now()}.tmp`;
5768
- await import_node_fs21.promises.writeFile(tmp, next, "utf8");
5769
- await import_node_fs21.promises.rename(tmp, manifest);
5817
+ await import_node_fs22.promises.writeFile(tmp, next, "utf8");
5818
+ await import_node_fs22.promises.rename(tmp, manifest);
5770
5819
  }
5771
5820
  async function applyProcfile(procfile, edits, original) {
5772
5821
  let next = original;
@@ -5775,8 +5824,8 @@ async function applyProcfile(procfile, edits, original) {
5775
5824
  next = next.replace(e.before, e.after);
5776
5825
  }
5777
5826
  const tmp = `${procfile}.${process.pid}.${Date.now()}.tmp`;
5778
- await import_node_fs21.promises.writeFile(tmp, next, "utf8");
5779
- await import_node_fs21.promises.rename(tmp, procfile);
5827
+ await import_node_fs22.promises.writeFile(tmp, next, "utf8");
5828
+ await import_node_fs22.promises.rename(tmp, procfile);
5780
5829
  }
5781
5830
  async function apply2(installPlan) {
5782
5831
  const touched = /* @__PURE__ */ new Set();
@@ -5786,7 +5835,7 @@ async function apply2(installPlan) {
5786
5835
  const originals = /* @__PURE__ */ new Map();
5787
5836
  for (const file of touched) {
5788
5837
  try {
5789
- originals.set(file, await import_node_fs21.promises.readFile(file, "utf8"));
5838
+ originals.set(file, await import_node_fs22.promises.readFile(file, "utf8"));
5790
5839
  } catch {
5791
5840
  }
5792
5841
  }
@@ -5814,7 +5863,7 @@ async function rollback2(installPlan, originals) {
5814
5863
  const restored = [];
5815
5864
  for (const [file, raw] of originals.entries()) {
5816
5865
  try {
5817
- await import_node_fs21.promises.writeFile(file, raw, "utf8");
5866
+ await import_node_fs22.promises.writeFile(file, raw, "utf8");
5818
5867
  restored.push(file);
5819
5868
  } catch {
5820
5869
  }
@@ -5829,7 +5878,7 @@ async function rollback2(installPlan, originals) {
5829
5878
  ""
5830
5879
  ];
5831
5880
  const rollbackPath = import_node_path36.default.join(installPlan.serviceDir, "neat-rollback.patch");
5832
- await import_node_fs21.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
5881
+ await import_node_fs22.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
5833
5882
  }
5834
5883
  var pythonInstaller = {
5835
5884
  name: "python",
@@ -6665,7 +6714,7 @@ async function buildPatchSections(services) {
6665
6714
  }
6666
6715
  async function runInit(opts) {
6667
6716
  const written = [];
6668
- const stat = await import_node_fs22.promises.stat(opts.scanPath).catch(() => null);
6717
+ const stat = await import_node_fs23.promises.stat(opts.scanPath).catch(() => null);
6669
6718
  if (!stat || !stat.isDirectory()) {
6670
6719
  console.error(`neat init: ${opts.scanPath} is not a directory`);
6671
6720
  return { exitCode: 2, writtenFiles: written };
@@ -6676,7 +6725,7 @@ async function runInit(opts) {
6676
6725
  const patch = renderPatch(sections);
6677
6726
  const patchPath = import_node_path37.default.join(opts.scanPath, "neat.patch");
6678
6727
  if (opts.dryRun) {
6679
- await import_node_fs22.promises.writeFile(patchPath, patch, "utf8");
6728
+ await import_node_fs23.promises.writeFile(patchPath, patch, "utf8");
6680
6729
  written.push(patchPath);
6681
6730
  console.log(`dry-run: patch written to ${patchPath}`);
6682
6731
  console.log("rerun without --dry-run to register and snapshot.");
@@ -6716,7 +6765,7 @@ async function runInit(opts) {
6716
6765
  console.log("patch applied. Run `npm install` (or your language equivalent) to refresh lockfiles.");
6717
6766
  }
6718
6767
  } else {
6719
- await import_node_fs22.promises.writeFile(patchPath, patch, "utf8");
6768
+ await import_node_fs23.promises.writeFile(patchPath, patch, "utf8");
6720
6769
  written.push(patchPath);
6721
6770
  }
6722
6771
  }
@@ -6770,7 +6819,7 @@ async function runSkill(opts) {
6770
6819
  const target = claudeConfigPath();
6771
6820
  let existing = {};
6772
6821
  try {
6773
- existing = JSON.parse(await import_node_fs22.promises.readFile(target, "utf8"));
6822
+ existing = JSON.parse(await import_node_fs23.promises.readFile(target, "utf8"));
6774
6823
  } catch (err) {
6775
6824
  if (err.code !== "ENOENT") {
6776
6825
  console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
@@ -6782,8 +6831,8 @@ async function runSkill(opts) {
6782
6831
  ...existing,
6783
6832
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
6784
6833
  };
6785
- await import_node_fs22.promises.mkdir(import_node_path37.default.dirname(target), { recursive: true });
6786
- await import_node_fs22.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
6834
+ await import_node_fs23.promises.mkdir(import_node_path37.default.dirname(target), { recursive: true });
6835
+ await import_node_fs23.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
6787
6836
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
6788
6837
  console.log("restart Claude Code to pick up the new MCP server.");
6789
6838
  return { exitCode: 0 };
@@ -6843,7 +6892,7 @@ async function main() {
6843
6892
  process.exit(2);
6844
6893
  }
6845
6894
  const scanPath = import_node_path37.default.resolve(target);
6846
- const stat = await import_node_fs22.promises.stat(scanPath).catch(() => null);
6895
+ const stat = await import_node_fs23.promises.stat(scanPath).catch(() => null);
6847
6896
  if (!stat || !stat.isDirectory()) {
6848
6897
  console.error(`neat watch: ${scanPath} is not a directory`);
6849
6898
  process.exit(2);