permachine 0.3.0 → 0.4.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 (2) hide show
  1. package/dist/cli.js +207 -322
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -5845,9 +5845,12 @@ function createCustomContext(overrides) {
5845
5845
  return { ...base, ...overrides };
5846
5846
  }
5847
5847
  var FILTER_REGEX = /\{([a-zA-Z0-9_-]+)(=|!=|~|\^)([a-zA-Z0-9_*.,\-]+)\}/g;
5848
+ var BASE_PLACEHOLDER_REGEX = /\{base\}/gi;
5848
5849
  function parseFilters(filename) {
5849
5850
  const filters = [];
5850
5851
  let match2;
5852
+ BASE_PLACEHOLDER_REGEX.lastIndex = 0;
5853
+ const hasBasePlaceholder = BASE_PLACEHOLDER_REGEX.test(filename);
5851
5854
  FILTER_REGEX.lastIndex = 0;
5852
5855
  while ((match2 = FILTER_REGEX.exec(filename)) !== null) {
5853
5856
  const [raw, key, operator, value] = match2;
@@ -5860,11 +5863,14 @@ function parseFilters(filename) {
5860
5863
  }
5861
5864
  let baseFilename = filename.replace(/\.?\{[^}]+\}/g, "");
5862
5865
  baseFilename = baseFilename.replace(/\.{2,}/g, ".");
5863
- return { filters, baseFilename };
5866
+ return { filters, baseFilename, hasBasePlaceholder };
5864
5867
  }
5865
5868
  function hasFilters(filename) {
5866
5869
  FILTER_REGEX.lastIndex = 0;
5867
- return FILTER_REGEX.test(filename);
5870
+ const hasFilterSyntax = FILTER_REGEX.test(filename);
5871
+ BASE_PLACEHOLDER_REGEX.lastIndex = 0;
5872
+ const hasBasePlaceholder = BASE_PLACEHOLDER_REGEX.test(filename);
5873
+ return hasFilterSyntax || hasBasePlaceholder;
5868
5874
  }
5869
5875
  function evaluateFilter(filter2, context) {
5870
5876
  const contextValue = context[filter2.key];
@@ -6370,148 +6376,12 @@ async function performAllMerges(operations) {
6370
6376
  return results;
6371
6377
  }
6372
6378
 
6373
- // src/core/git-hooks.ts
6379
+ // src/core/gitignore-manager.ts
6374
6380
  import fs2 from "node:fs/promises";
6375
6381
  import path5 from "node:path";
6376
6382
  import { exec } from "node:child_process";
6377
6383
  import { promisify } from "node:util";
6378
- import { fileURLToPath as fileURLToPath3 } from "node:url";
6379
6384
  var execAsync = promisify(exec);
6380
- var __filename2 = fileURLToPath3(import.meta.url);
6381
- var __dirname2 = path5.dirname(__filename2);
6382
- var HOOK_NAMES = ["post-checkout", "post-merge", "post-commit"];
6383
- var HOOKS_DIR = ".permachine/hooks";
6384
- async function installHooks(options = {}) {
6385
- const warnings = [];
6386
- if (!await isGitRepository()) {
6387
- throw new Error('Not a git repository. Run "git init" first.');
6388
- }
6389
- const cwd = process.cwd();
6390
- const existingHooksPath = await getGitConfig("core.hooksPath");
6391
- if (existingHooksPath && existingHooksPath !== HOOKS_DIR && !options.legacy) {
6392
- warnings.push(`Git core.hooksPath is already set to: ${existingHooksPath}`, "Use --legacy flag to install hooks in .git/hooks instead");
6393
- }
6394
- const useLegacy = options.legacy || existingHooksPath && existingHooksPath !== HOOKS_DIR;
6395
- if (useLegacy) {
6396
- return await installLegacyHooks(warnings);
6397
- } else {
6398
- return await installHooksPathMethod(warnings);
6399
- }
6400
- }
6401
- async function installHooksPathMethod(warnings) {
6402
- const cwd = process.cwd();
6403
- const hooksDir = path5.join(cwd, HOOKS_DIR);
6404
- await fs2.mkdir(hooksDir, { recursive: true });
6405
- let templatesDir = path5.join(__dirname2, "../../templates/hooks");
6406
- if (!await fileExists2(path5.join(templatesDir, "post-checkout"))) {
6407
- templatesDir = path5.join(__dirname2, "../templates/hooks");
6408
- }
6409
- const installedHooks = [];
6410
- for (const hookName of HOOK_NAMES) {
6411
- const templatePath = path5.join(templatesDir, hookName);
6412
- const hookPath = path5.join(hooksDir, hookName);
6413
- const content = await fs2.readFile(templatePath, "utf-8");
6414
- await fs2.writeFile(hookPath, content, { mode: 493 });
6415
- installedHooks.push(hookName);
6416
- }
6417
- await setGitConfig("core.hooksPath", HOOKS_DIR);
6418
- logger.success(`Installed git hooks via core.hooksPath`);
6419
- return {
6420
- method: "hooksPath",
6421
- hooksInstalled: installedHooks,
6422
- warnings
6423
- };
6424
- }
6425
- async function installLegacyHooks(warnings) {
6426
- const cwd = process.cwd();
6427
- const gitHooksDir = path5.join(cwd, ".git/hooks");
6428
- const installedHooks = [];
6429
- for (const hookName of HOOK_NAMES) {
6430
- const hookPath = path5.join(gitHooksDir, hookName);
6431
- const backupPath = path5.join(gitHooksDir, `${hookName}.pre-mcs`);
6432
- const hookExists = await fileExists2(hookPath);
6433
- if (hookExists) {
6434
- await fs2.rename(hookPath, backupPath);
6435
- }
6436
- const hookContent = `#!/bin/sh
6437
- # Auto-generated by permachine (legacy mode)
6438
-
6439
- permachine merge --silent
6440
-
6441
- # Call original hook if it existed
6442
- if [ -f "${backupPath}" ]; then
6443
- "${backupPath}" "$@"
6444
- fi
6445
-
6446
- exit 0
6447
- `;
6448
- await fs2.writeFile(hookPath, hookContent, { mode: 493 });
6449
- installedHooks.push(hookName);
6450
- }
6451
- logger.success("Installed git hooks via legacy .git/hooks wrapping");
6452
- return {
6453
- method: "legacy",
6454
- hooksInstalled: installedHooks,
6455
- warnings
6456
- };
6457
- }
6458
- async function uninstallHooks() {
6459
- const cwd = process.cwd();
6460
- const hooksPath = await getGitConfig("core.hooksPath");
6461
- if (hooksPath === HOOKS_DIR) {
6462
- await execAsync("git config --unset core.hooksPath");
6463
- const hooksDir = path5.join(cwd, HOOKS_DIR);
6464
- await fs2.rm(hooksDir, { recursive: true, force: true });
6465
- logger.success("Uninstalled git hooks (removed core.hooksPath)");
6466
- } else {
6467
- const gitHooksDir = path5.join(cwd, ".git/hooks");
6468
- for (const hookName of HOOK_NAMES) {
6469
- const hookPath = path5.join(gitHooksDir, hookName);
6470
- const backupPath = path5.join(gitHooksDir, `${hookName}.pre-mcs`);
6471
- if (await fileExists2(hookPath)) {
6472
- await fs2.unlink(hookPath);
6473
- }
6474
- if (await fileExists2(backupPath)) {
6475
- await fs2.rename(backupPath, hookPath);
6476
- }
6477
- }
6478
- logger.success("Uninstalled git hooks (restored original hooks)");
6479
- }
6480
- }
6481
- async function isGitRepository() {
6482
- try {
6483
- await execAsync("git rev-parse --git-dir");
6484
- return true;
6485
- } catch {
6486
- return false;
6487
- }
6488
- }
6489
- async function getGitConfig(key) {
6490
- try {
6491
- const { stdout } = await execAsync(`git config --get ${key}`);
6492
- return stdout.trim() || null;
6493
- } catch {
6494
- return null;
6495
- }
6496
- }
6497
- async function setGitConfig(key, value) {
6498
- await execAsync(`git config ${key} "${value}"`);
6499
- }
6500
- async function fileExists2(filePath) {
6501
- try {
6502
- await fs2.access(filePath);
6503
- return true;
6504
- } catch {
6505
- return false;
6506
- }
6507
- }
6508
-
6509
- // src/core/gitignore-manager.ts
6510
- import fs3 from "node:fs/promises";
6511
- import path6 from "node:path";
6512
- import { exec as exec2 } from "node:child_process";
6513
- import { promisify as promisify2 } from "node:util";
6514
- var execAsync2 = promisify2(exec2);
6515
6385
  async function manageGitignore(outputPaths, options = {}) {
6516
6386
  const result = {
6517
6387
  added: [],
@@ -6522,13 +6392,13 @@ async function manageGitignore(outputPaths, options = {}) {
6522
6392
  return result;
6523
6393
  }
6524
6394
  const cwd = options.cwd || process.cwd();
6525
- const gitignorePath = path6.join(cwd, ".gitignore");
6395
+ const gitignorePath = path5.join(cwd, ".gitignore");
6526
6396
  try {
6527
- const relativePaths = outputPaths.map((p) => path6.relative(cwd, p).replace(/\\/g, "/"));
6397
+ const relativePaths = outputPaths.map((p) => path5.relative(cwd, p).replace(/\\/g, "/"));
6528
6398
  let gitignoreContent = "";
6529
6399
  let gitignoreExists = false;
6530
6400
  try {
6531
- gitignoreContent = await fs3.readFile(gitignorePath, "utf-8");
6401
+ gitignoreContent = await fs2.readFile(gitignorePath, "utf-8");
6532
6402
  gitignoreExists = true;
6533
6403
  } catch (error) {
6534
6404
  if (error.code !== "ENOENT") {
@@ -6555,7 +6425,7 @@ async function manageGitignore(outputPaths, options = {}) {
6555
6425
  ` : newEntries.join(`
6556
6426
  `) + `
6557
6427
  `;
6558
- await fs3.writeFile(gitignorePath, updatedContent, "utf-8");
6428
+ await fs2.writeFile(gitignorePath, updatedContent, "utf-8");
6559
6429
  if (!gitignoreExists) {
6560
6430
  logger.info("Created .gitignore");
6561
6431
  }
@@ -6564,7 +6434,7 @@ async function manageGitignore(outputPaths, options = {}) {
6564
6434
  try {
6565
6435
  const isTracked = await isFileTrackedByGit(relPath, cwd);
6566
6436
  if (isTracked) {
6567
- await execAsync2(`git rm --cached "${relPath}"`, { cwd });
6437
+ await execAsync(`git rm --cached "${relPath}"`, { cwd });
6568
6438
  result.removed.push(relPath);
6569
6439
  logger.info(`Removed ${relPath} from git tracking`);
6570
6440
  }
@@ -6581,7 +6451,7 @@ async function manageGitignore(outputPaths, options = {}) {
6581
6451
  }
6582
6452
  async function isFileTrackedByGit(filePath, cwd) {
6583
6453
  try {
6584
- await execAsync2(`git ls-files --error-unmatch "${filePath}"`, { cwd });
6454
+ await execAsync(`git ls-files --error-unmatch "${filePath}"`, { cwd });
6585
6455
  return true;
6586
6456
  } catch {
6587
6457
  return false;
@@ -6679,7 +6549,7 @@ class ReaddirpStream extends Readable {
6679
6549
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
6680
6550
  const statMethod = opts.lstat ? lstat2 : stat;
6681
6551
  if (wantBigintFsStats) {
6682
- this._stat = (path7) => statMethod(path7, { bigint: true });
6552
+ this._stat = (path6) => statMethod(path6, { bigint: true });
6683
6553
  } else {
6684
6554
  this._stat = statMethod;
6685
6555
  }
@@ -6704,8 +6574,8 @@ class ReaddirpStream extends Readable {
6704
6574
  const par = this.parent;
6705
6575
  const fil = par && par.files;
6706
6576
  if (fil && fil.length > 0) {
6707
- const { path: path7, depth } = par;
6708
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path7));
6577
+ const { path: path6, depth } = par;
6578
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path6));
6709
6579
  const awaited = await Promise.all(slice);
6710
6580
  for (const entry of awaited) {
6711
6581
  if (!entry)
@@ -6745,20 +6615,20 @@ class ReaddirpStream extends Readable {
6745
6615
  this.reading = false;
6746
6616
  }
6747
6617
  }
6748
- async _exploreDir(path7, depth) {
6618
+ async _exploreDir(path6, depth) {
6749
6619
  let files;
6750
6620
  try {
6751
- files = await readdir2(path7, this._rdOptions);
6621
+ files = await readdir2(path6, this._rdOptions);
6752
6622
  } catch (error) {
6753
6623
  this._onError(error);
6754
6624
  }
6755
- return { files, depth, path: path7 };
6625
+ return { files, depth, path: path6 };
6756
6626
  }
6757
- async _formatEntry(dirent, path7) {
6627
+ async _formatEntry(dirent, path6) {
6758
6628
  let entry;
6759
6629
  const basename = this._isDirent ? dirent.name : dirent;
6760
6630
  try {
6761
- const fullPath = presolve(pjoin(path7, basename));
6631
+ const fullPath = presolve(pjoin(path6, basename));
6762
6632
  entry = { path: prelative(this._root, fullPath), fullPath, basename };
6763
6633
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
6764
6634
  } catch (err) {
@@ -7157,16 +7027,16 @@ var delFromSet = (main, prop, item) => {
7157
7027
  };
7158
7028
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
7159
7029
  var FsWatchInstances = new Map;
7160
- function createFsWatchInstance(path7, options, listener, errHandler, emitRaw) {
7030
+ function createFsWatchInstance(path6, options, listener, errHandler, emitRaw) {
7161
7031
  const handleEvent = (rawEvent, evPath) => {
7162
- listener(path7);
7163
- emitRaw(rawEvent, evPath, { watchedPath: path7 });
7164
- if (evPath && path7 !== evPath) {
7165
- fsWatchBroadcast(sp.resolve(path7, evPath), KEY_LISTENERS, sp.join(path7, evPath));
7032
+ listener(path6);
7033
+ emitRaw(rawEvent, evPath, { watchedPath: path6 });
7034
+ if (evPath && path6 !== evPath) {
7035
+ fsWatchBroadcast(sp.resolve(path6, evPath), KEY_LISTENERS, sp.join(path6, evPath));
7166
7036
  }
7167
7037
  };
7168
7038
  try {
7169
- return fs_watch(path7, {
7039
+ return fs_watch(path6, {
7170
7040
  persistent: options.persistent
7171
7041
  }, handleEvent);
7172
7042
  } catch (error) {
@@ -7182,12 +7052,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
7182
7052
  listener(val1, val2, val3);
7183
7053
  });
7184
7054
  };
7185
- var setFsWatchListener = (path7, fullPath, options, handlers) => {
7055
+ var setFsWatchListener = (path6, fullPath, options, handlers) => {
7186
7056
  const { listener, errHandler, rawEmitter } = handlers;
7187
7057
  let cont = FsWatchInstances.get(fullPath);
7188
7058
  let watcher;
7189
7059
  if (!options.persistent) {
7190
- watcher = createFsWatchInstance(path7, options, listener, errHandler, rawEmitter);
7060
+ watcher = createFsWatchInstance(path6, options, listener, errHandler, rawEmitter);
7191
7061
  if (!watcher)
7192
7062
  return;
7193
7063
  return watcher.close.bind(watcher);
@@ -7197,7 +7067,7 @@ var setFsWatchListener = (path7, fullPath, options, handlers) => {
7197
7067
  addAndConvert(cont, KEY_ERR, errHandler);
7198
7068
  addAndConvert(cont, KEY_RAW, rawEmitter);
7199
7069
  } else {
7200
- watcher = createFsWatchInstance(path7, options, fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), errHandler, fsWatchBroadcast.bind(null, fullPath, KEY_RAW));
7070
+ watcher = createFsWatchInstance(path6, options, fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), errHandler, fsWatchBroadcast.bind(null, fullPath, KEY_RAW));
7201
7071
  if (!watcher)
7202
7072
  return;
7203
7073
  watcher.on(EV.ERROR, async (error) => {
@@ -7206,7 +7076,7 @@ var setFsWatchListener = (path7, fullPath, options, handlers) => {
7206
7076
  cont.watcherUnusable = true;
7207
7077
  if (isWindows && error.code === "EPERM") {
7208
7078
  try {
7209
- const fd = await open(path7, "r");
7079
+ const fd = await open(path6, "r");
7210
7080
  await fd.close();
7211
7081
  broadcastErr(error);
7212
7082
  } catch (err) {}
@@ -7236,7 +7106,7 @@ var setFsWatchListener = (path7, fullPath, options, handlers) => {
7236
7106
  };
7237
7107
  };
7238
7108
  var FsWatchFileInstances = new Map;
7239
- var setFsWatchFileListener = (path7, fullPath, options, handlers) => {
7109
+ var setFsWatchFileListener = (path6, fullPath, options, handlers) => {
7240
7110
  const { listener, rawEmitter } = handlers;
7241
7111
  let cont = FsWatchFileInstances.get(fullPath);
7242
7112
  const copts = cont && cont.options;
@@ -7258,7 +7128,7 @@ var setFsWatchFileListener = (path7, fullPath, options, handlers) => {
7258
7128
  });
7259
7129
  const currmtime = curr.mtimeMs;
7260
7130
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
7261
- foreach(cont.listeners, (listener2) => listener2(path7, curr));
7131
+ foreach(cont.listeners, (listener2) => listener2(path6, curr));
7262
7132
  }
7263
7133
  })
7264
7134
  };
@@ -7283,13 +7153,13 @@ class NodeFsHandler {
7283
7153
  this.fsw = fsW;
7284
7154
  this._boundHandleError = (error) => fsW._handleError(error);
7285
7155
  }
7286
- _watchWithNodeFs(path7, listener) {
7156
+ _watchWithNodeFs(path6, listener) {
7287
7157
  const opts = this.fsw.options;
7288
- const directory = sp.dirname(path7);
7289
- const basename2 = sp.basename(path7);
7158
+ const directory = sp.dirname(path6);
7159
+ const basename2 = sp.basename(path6);
7290
7160
  const parent = this.fsw._getWatchedDir(directory);
7291
7161
  parent.add(basename2);
7292
- const absolutePath = sp.resolve(path7);
7162
+ const absolutePath = sp.resolve(path6);
7293
7163
  const options = {
7294
7164
  persistent: opts.persistent
7295
7165
  };
@@ -7299,12 +7169,12 @@ class NodeFsHandler {
7299
7169
  if (opts.usePolling) {
7300
7170
  const enableBin = opts.interval !== opts.binaryInterval;
7301
7171
  options.interval = enableBin && isBinaryPath(basename2) ? opts.binaryInterval : opts.interval;
7302
- closer = setFsWatchFileListener(path7, absolutePath, options, {
7172
+ closer = setFsWatchFileListener(path6, absolutePath, options, {
7303
7173
  listener,
7304
7174
  rawEmitter: this.fsw._emitRaw
7305
7175
  });
7306
7176
  } else {
7307
- closer = setFsWatchListener(path7, absolutePath, options, {
7177
+ closer = setFsWatchListener(path6, absolutePath, options, {
7308
7178
  listener,
7309
7179
  errHandler: this._boundHandleError,
7310
7180
  rawEmitter: this.fsw._emitRaw
@@ -7322,7 +7192,7 @@ class NodeFsHandler {
7322
7192
  let prevStats = stats;
7323
7193
  if (parent.has(basename2))
7324
7194
  return;
7325
- const listener = async (path7, newStats) => {
7195
+ const listener = async (path6, newStats) => {
7326
7196
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
7327
7197
  return;
7328
7198
  if (!newStats || newStats.mtimeMs === 0) {
@@ -7336,11 +7206,11 @@ class NodeFsHandler {
7336
7206
  this.fsw._emit(EV.CHANGE, file, newStats2);
7337
7207
  }
7338
7208
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
7339
- this.fsw._closeFile(path7);
7209
+ this.fsw._closeFile(path6);
7340
7210
  prevStats = newStats2;
7341
7211
  const closer2 = this._watchWithNodeFs(file, listener);
7342
7212
  if (closer2)
7343
- this.fsw._addPathCloser(path7, closer2);
7213
+ this.fsw._addPathCloser(path6, closer2);
7344
7214
  } else {
7345
7215
  prevStats = newStats2;
7346
7216
  }
@@ -7364,7 +7234,7 @@ class NodeFsHandler {
7364
7234
  }
7365
7235
  return closer;
7366
7236
  }
7367
- async _handleSymlink(entry, directory, path7, item) {
7237
+ async _handleSymlink(entry, directory, path6, item) {
7368
7238
  if (this.fsw.closed) {
7369
7239
  return;
7370
7240
  }
@@ -7374,7 +7244,7 @@ class NodeFsHandler {
7374
7244
  this.fsw._incrReadyCount();
7375
7245
  let linkPath;
7376
7246
  try {
7377
- linkPath = await fsrealpath(path7);
7247
+ linkPath = await fsrealpath(path6);
7378
7248
  } catch (e) {
7379
7249
  this.fsw._emitReady();
7380
7250
  return true;
@@ -7384,12 +7254,12 @@ class NodeFsHandler {
7384
7254
  if (dir.has(item)) {
7385
7255
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
7386
7256
  this.fsw._symlinkPaths.set(full, linkPath);
7387
- this.fsw._emit(EV.CHANGE, path7, entry.stats);
7257
+ this.fsw._emit(EV.CHANGE, path6, entry.stats);
7388
7258
  }
7389
7259
  } else {
7390
7260
  dir.add(item);
7391
7261
  this.fsw._symlinkPaths.set(full, linkPath);
7392
- this.fsw._emit(EV.ADD, path7, entry.stats);
7262
+ this.fsw._emit(EV.ADD, path6, entry.stats);
7393
7263
  }
7394
7264
  this.fsw._emitReady();
7395
7265
  return true;
@@ -7419,9 +7289,9 @@ class NodeFsHandler {
7419
7289
  return;
7420
7290
  }
7421
7291
  const item = entry.path;
7422
- let path7 = sp.join(directory, item);
7292
+ let path6 = sp.join(directory, item);
7423
7293
  current.add(item);
7424
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path7, item)) {
7294
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path6, item)) {
7425
7295
  return;
7426
7296
  }
7427
7297
  if (this.fsw.closed) {
@@ -7430,8 +7300,8 @@ class NodeFsHandler {
7430
7300
  }
7431
7301
  if (item === target || !target && !previous.has(item)) {
7432
7302
  this.fsw._incrReadyCount();
7433
- path7 = sp.join(dir, sp.relative(dir, path7));
7434
- this._addToNodeFs(path7, initialAdd, wh, depth + 1);
7303
+ path6 = sp.join(dir, sp.relative(dir, path6));
7304
+ this._addToNodeFs(path6, initialAdd, wh, depth + 1);
7435
7305
  }
7436
7306
  }).on(EV.ERROR, this._boundHandleError);
7437
7307
  return new Promise((resolve2, reject) => {
@@ -7480,13 +7350,13 @@ class NodeFsHandler {
7480
7350
  }
7481
7351
  return closer;
7482
7352
  }
7483
- async _addToNodeFs(path7, initialAdd, priorWh, depth, target) {
7353
+ async _addToNodeFs(path6, initialAdd, priorWh, depth, target) {
7484
7354
  const ready = this.fsw._emitReady;
7485
- if (this.fsw._isIgnored(path7) || this.fsw.closed) {
7355
+ if (this.fsw._isIgnored(path6) || this.fsw.closed) {
7486
7356
  ready();
7487
7357
  return false;
7488
7358
  }
7489
- const wh = this.fsw._getWatchHelpers(path7);
7359
+ const wh = this.fsw._getWatchHelpers(path6);
7490
7360
  if (priorWh) {
7491
7361
  wh.filterPath = (entry) => priorWh.filterPath(entry);
7492
7362
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -7502,8 +7372,8 @@ class NodeFsHandler {
7502
7372
  const follow = this.fsw.options.followSymlinks;
7503
7373
  let closer;
7504
7374
  if (stats.isDirectory()) {
7505
- const absPath = sp.resolve(path7);
7506
- const targetPath = follow ? await fsrealpath(path7) : path7;
7375
+ const absPath = sp.resolve(path6);
7376
+ const targetPath = follow ? await fsrealpath(path6) : path6;
7507
7377
  if (this.fsw.closed)
7508
7378
  return;
7509
7379
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -7513,29 +7383,29 @@ class NodeFsHandler {
7513
7383
  this.fsw._symlinkPaths.set(absPath, targetPath);
7514
7384
  }
7515
7385
  } else if (stats.isSymbolicLink()) {
7516
- const targetPath = follow ? await fsrealpath(path7) : path7;
7386
+ const targetPath = follow ? await fsrealpath(path6) : path6;
7517
7387
  if (this.fsw.closed)
7518
7388
  return;
7519
7389
  const parent = sp.dirname(wh.watchPath);
7520
7390
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
7521
7391
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
7522
- closer = await this._handleDir(parent, stats, initialAdd, depth, path7, wh, targetPath);
7392
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path6, wh, targetPath);
7523
7393
  if (this.fsw.closed)
7524
7394
  return;
7525
7395
  if (targetPath !== undefined) {
7526
- this.fsw._symlinkPaths.set(sp.resolve(path7), targetPath);
7396
+ this.fsw._symlinkPaths.set(sp.resolve(path6), targetPath);
7527
7397
  }
7528
7398
  } else {
7529
7399
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
7530
7400
  }
7531
7401
  ready();
7532
7402
  if (closer)
7533
- this.fsw._addPathCloser(path7, closer);
7403
+ this.fsw._addPathCloser(path6, closer);
7534
7404
  return false;
7535
7405
  } catch (error) {
7536
7406
  if (this.fsw._handleError(error)) {
7537
7407
  ready();
7538
- return path7;
7408
+ return path6;
7539
7409
  }
7540
7410
  }
7541
7411
  }
@@ -7579,24 +7449,24 @@ function createPattern(matcher) {
7579
7449
  }
7580
7450
  return () => false;
7581
7451
  }
7582
- function normalizePath(path7) {
7583
- if (typeof path7 !== "string")
7452
+ function normalizePath(path6) {
7453
+ if (typeof path6 !== "string")
7584
7454
  throw new Error("string expected");
7585
- path7 = sp2.normalize(path7);
7586
- path7 = path7.replace(/\\/g, "/");
7455
+ path6 = sp2.normalize(path6);
7456
+ path6 = path6.replace(/\\/g, "/");
7587
7457
  let prepend = false;
7588
- if (path7.startsWith("//"))
7458
+ if (path6.startsWith("//"))
7589
7459
  prepend = true;
7590
- path7 = path7.replace(DOUBLE_SLASH_RE, "/");
7460
+ path6 = path6.replace(DOUBLE_SLASH_RE, "/");
7591
7461
  if (prepend)
7592
- path7 = "/" + path7;
7593
- return path7;
7462
+ path6 = "/" + path6;
7463
+ return path6;
7594
7464
  }
7595
7465
  function matchPatterns(patterns, testString, stats) {
7596
- const path7 = normalizePath(testString);
7466
+ const path6 = normalizePath(testString);
7597
7467
  for (let index = 0;index < patterns.length; index++) {
7598
7468
  const pattern = patterns[index];
7599
- if (pattern(path7, stats)) {
7469
+ if (pattern(path6, stats)) {
7600
7470
  return true;
7601
7471
  }
7602
7472
  }
@@ -7634,19 +7504,19 @@ var toUnix = (string) => {
7634
7504
  }
7635
7505
  return str;
7636
7506
  };
7637
- var normalizePathToUnix = (path7) => toUnix(sp2.normalize(toUnix(path7)));
7638
- var normalizeIgnored = (cwd = "") => (path7) => {
7639
- if (typeof path7 === "string") {
7640
- return normalizePathToUnix(sp2.isAbsolute(path7) ? path7 : sp2.join(cwd, path7));
7507
+ var normalizePathToUnix = (path6) => toUnix(sp2.normalize(toUnix(path6)));
7508
+ var normalizeIgnored = (cwd = "") => (path6) => {
7509
+ if (typeof path6 === "string") {
7510
+ return normalizePathToUnix(sp2.isAbsolute(path6) ? path6 : sp2.join(cwd, path6));
7641
7511
  } else {
7642
- return path7;
7512
+ return path6;
7643
7513
  }
7644
7514
  };
7645
- var getAbsolutePath = (path7, cwd) => {
7646
- if (sp2.isAbsolute(path7)) {
7647
- return path7;
7515
+ var getAbsolutePath = (path6, cwd) => {
7516
+ if (sp2.isAbsolute(path6)) {
7517
+ return path6;
7648
7518
  }
7649
- return sp2.join(cwd, path7);
7519
+ return sp2.join(cwd, path6);
7650
7520
  };
7651
7521
  var EMPTY_SET = Object.freeze(new Set);
7652
7522
 
@@ -7713,10 +7583,10 @@ class WatchHelper {
7713
7583
  dirParts;
7714
7584
  followSymlinks;
7715
7585
  statMethod;
7716
- constructor(path7, follow, fsw) {
7586
+ constructor(path6, follow, fsw) {
7717
7587
  this.fsw = fsw;
7718
- const watchPath = path7;
7719
- this.path = path7 = path7.replace(REPLACER_RE, "");
7588
+ const watchPath = path6;
7589
+ this.path = path6 = path6.replace(REPLACER_RE, "");
7720
7590
  this.watchPath = watchPath;
7721
7591
  this.fullWatchPath = sp2.resolve(watchPath);
7722
7592
  this.dirParts = [];
@@ -7847,20 +7717,20 @@ class FSWatcher extends EventEmitter2 {
7847
7717
  this._closePromise = undefined;
7848
7718
  let paths = unifyPaths(paths_);
7849
7719
  if (cwd) {
7850
- paths = paths.map((path7) => {
7851
- const absPath = getAbsolutePath(path7, cwd);
7720
+ paths = paths.map((path6) => {
7721
+ const absPath = getAbsolutePath(path6, cwd);
7852
7722
  return absPath;
7853
7723
  });
7854
7724
  }
7855
- paths.forEach((path7) => {
7856
- this._removeIgnoredPath(path7);
7725
+ paths.forEach((path6) => {
7726
+ this._removeIgnoredPath(path6);
7857
7727
  });
7858
7728
  this._userIgnored = undefined;
7859
7729
  if (!this._readyCount)
7860
7730
  this._readyCount = 0;
7861
7731
  this._readyCount += paths.length;
7862
- Promise.all(paths.map(async (path7) => {
7863
- const res = await this._nodeFsHandler._addToNodeFs(path7, !_internal, undefined, 0, _origAdd);
7732
+ Promise.all(paths.map(async (path6) => {
7733
+ const res = await this._nodeFsHandler._addToNodeFs(path6, !_internal, undefined, 0, _origAdd);
7864
7734
  if (res)
7865
7735
  this._emitReady();
7866
7736
  return res;
@@ -7879,17 +7749,17 @@ class FSWatcher extends EventEmitter2 {
7879
7749
  return this;
7880
7750
  const paths = unifyPaths(paths_);
7881
7751
  const { cwd } = this.options;
7882
- paths.forEach((path7) => {
7883
- if (!sp2.isAbsolute(path7) && !this._closers.has(path7)) {
7752
+ paths.forEach((path6) => {
7753
+ if (!sp2.isAbsolute(path6) && !this._closers.has(path6)) {
7884
7754
  if (cwd)
7885
- path7 = sp2.join(cwd, path7);
7886
- path7 = sp2.resolve(path7);
7755
+ path6 = sp2.join(cwd, path6);
7756
+ path6 = sp2.resolve(path6);
7887
7757
  }
7888
- this._closePath(path7);
7889
- this._addIgnoredPath(path7);
7890
- if (this._watched.has(path7)) {
7758
+ this._closePath(path6);
7759
+ this._addIgnoredPath(path6);
7760
+ if (this._watched.has(path6)) {
7891
7761
  this._addIgnoredPath({
7892
- path: path7,
7762
+ path: path6,
7893
7763
  recursive: true
7894
7764
  });
7895
7765
  }
@@ -7938,38 +7808,38 @@ class FSWatcher extends EventEmitter2 {
7938
7808
  if (event !== EVENTS.ERROR)
7939
7809
  this.emit(EVENTS.ALL, event, ...args);
7940
7810
  }
7941
- async _emit(event, path7, stats) {
7811
+ async _emit(event, path6, stats) {
7942
7812
  if (this.closed)
7943
7813
  return;
7944
7814
  const opts = this.options;
7945
7815
  if (isWindows)
7946
- path7 = sp2.normalize(path7);
7816
+ path6 = sp2.normalize(path6);
7947
7817
  if (opts.cwd)
7948
- path7 = sp2.relative(opts.cwd, path7);
7949
- const args = [path7];
7818
+ path6 = sp2.relative(opts.cwd, path6);
7819
+ const args = [path6];
7950
7820
  if (stats != null)
7951
7821
  args.push(stats);
7952
7822
  const awf = opts.awaitWriteFinish;
7953
7823
  let pw;
7954
- if (awf && (pw = this._pendingWrites.get(path7))) {
7824
+ if (awf && (pw = this._pendingWrites.get(path6))) {
7955
7825
  pw.lastChange = new Date;
7956
7826
  return this;
7957
7827
  }
7958
7828
  if (opts.atomic) {
7959
7829
  if (event === EVENTS.UNLINK) {
7960
- this._pendingUnlinks.set(path7, [event, ...args]);
7830
+ this._pendingUnlinks.set(path6, [event, ...args]);
7961
7831
  setTimeout(() => {
7962
- this._pendingUnlinks.forEach((entry, path8) => {
7832
+ this._pendingUnlinks.forEach((entry, path7) => {
7963
7833
  this.emit(...entry);
7964
7834
  this.emit(EVENTS.ALL, ...entry);
7965
- this._pendingUnlinks.delete(path8);
7835
+ this._pendingUnlinks.delete(path7);
7966
7836
  });
7967
7837
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
7968
7838
  return this;
7969
7839
  }
7970
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path7)) {
7840
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path6)) {
7971
7841
  event = EVENTS.CHANGE;
7972
- this._pendingUnlinks.delete(path7);
7842
+ this._pendingUnlinks.delete(path6);
7973
7843
  }
7974
7844
  }
7975
7845
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -7987,16 +7857,16 @@ class FSWatcher extends EventEmitter2 {
7987
7857
  this.emitWithAll(event, args);
7988
7858
  }
7989
7859
  };
7990
- this._awaitWriteFinish(path7, awf.stabilityThreshold, event, awfEmit);
7860
+ this._awaitWriteFinish(path6, awf.stabilityThreshold, event, awfEmit);
7991
7861
  return this;
7992
7862
  }
7993
7863
  if (event === EVENTS.CHANGE) {
7994
- const isThrottled = !this._throttle(EVENTS.CHANGE, path7, 50);
7864
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path6, 50);
7995
7865
  if (isThrottled)
7996
7866
  return this;
7997
7867
  }
7998
7868
  if (opts.alwaysStat && stats === undefined && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
7999
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path7) : path7;
7869
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path6) : path6;
8000
7870
  let stats2;
8001
7871
  try {
8002
7872
  stats2 = await stat3(fullPath);
@@ -8015,23 +7885,23 @@ class FSWatcher extends EventEmitter2 {
8015
7885
  }
8016
7886
  return error || this.closed;
8017
7887
  }
8018
- _throttle(actionType, path7, timeout) {
7888
+ _throttle(actionType, path6, timeout) {
8019
7889
  if (!this._throttled.has(actionType)) {
8020
7890
  this._throttled.set(actionType, new Map);
8021
7891
  }
8022
7892
  const action = this._throttled.get(actionType);
8023
7893
  if (!action)
8024
7894
  throw new Error("invalid throttle");
8025
- const actionPath = action.get(path7);
7895
+ const actionPath = action.get(path6);
8026
7896
  if (actionPath) {
8027
7897
  actionPath.count++;
8028
7898
  return false;
8029
7899
  }
8030
7900
  let timeoutObject;
8031
7901
  const clear = () => {
8032
- const item = action.get(path7);
7902
+ const item = action.get(path6);
8033
7903
  const count = item ? item.count : 0;
8034
- action.delete(path7);
7904
+ action.delete(path6);
8035
7905
  clearTimeout(timeoutObject);
8036
7906
  if (item)
8037
7907
  clearTimeout(item.timeoutObject);
@@ -8039,50 +7909,50 @@ class FSWatcher extends EventEmitter2 {
8039
7909
  };
8040
7910
  timeoutObject = setTimeout(clear, timeout);
8041
7911
  const thr = { timeoutObject, clear, count: 0 };
8042
- action.set(path7, thr);
7912
+ action.set(path6, thr);
8043
7913
  return thr;
8044
7914
  }
8045
7915
  _incrReadyCount() {
8046
7916
  return this._readyCount++;
8047
7917
  }
8048
- _awaitWriteFinish(path7, threshold, event, awfEmit) {
7918
+ _awaitWriteFinish(path6, threshold, event, awfEmit) {
8049
7919
  const awf = this.options.awaitWriteFinish;
8050
7920
  if (typeof awf !== "object")
8051
7921
  return;
8052
7922
  const pollInterval = awf.pollInterval;
8053
7923
  let timeoutHandler;
8054
- let fullPath = path7;
8055
- if (this.options.cwd && !sp2.isAbsolute(path7)) {
8056
- fullPath = sp2.join(this.options.cwd, path7);
7924
+ let fullPath = path6;
7925
+ if (this.options.cwd && !sp2.isAbsolute(path6)) {
7926
+ fullPath = sp2.join(this.options.cwd, path6);
8057
7927
  }
8058
7928
  const now = new Date;
8059
7929
  const writes = this._pendingWrites;
8060
7930
  function awaitWriteFinishFn(prevStat) {
8061
7931
  statcb(fullPath, (err, curStat) => {
8062
- if (err || !writes.has(path7)) {
7932
+ if (err || !writes.has(path6)) {
8063
7933
  if (err && err.code !== "ENOENT")
8064
7934
  awfEmit(err);
8065
7935
  return;
8066
7936
  }
8067
7937
  const now2 = Number(new Date);
8068
7938
  if (prevStat && curStat.size !== prevStat.size) {
8069
- writes.get(path7).lastChange = now2;
7939
+ writes.get(path6).lastChange = now2;
8070
7940
  }
8071
- const pw = writes.get(path7);
7941
+ const pw = writes.get(path6);
8072
7942
  const df = now2 - pw.lastChange;
8073
7943
  if (df >= threshold) {
8074
- writes.delete(path7);
7944
+ writes.delete(path6);
8075
7945
  awfEmit(undefined, curStat);
8076
7946
  } else {
8077
7947
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
8078
7948
  }
8079
7949
  });
8080
7950
  }
8081
- if (!writes.has(path7)) {
8082
- writes.set(path7, {
7951
+ if (!writes.has(path6)) {
7952
+ writes.set(path6, {
8083
7953
  lastChange: now,
8084
7954
  cancelWait: () => {
8085
- writes.delete(path7);
7955
+ writes.delete(path6);
8086
7956
  clearTimeout(timeoutHandler);
8087
7957
  return event;
8088
7958
  }
@@ -8090,8 +7960,8 @@ class FSWatcher extends EventEmitter2 {
8090
7960
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);
8091
7961
  }
8092
7962
  }
8093
- _isIgnored(path7, stats) {
8094
- if (this.options.atomic && DOT_RE.test(path7))
7963
+ _isIgnored(path6, stats) {
7964
+ if (this.options.atomic && DOT_RE.test(path6))
8095
7965
  return true;
8096
7966
  if (!this._userIgnored) {
8097
7967
  const { cwd } = this.options;
@@ -8101,13 +7971,13 @@ class FSWatcher extends EventEmitter2 {
8101
7971
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
8102
7972
  this._userIgnored = anymatch(list, undefined);
8103
7973
  }
8104
- return this._userIgnored(path7, stats);
7974
+ return this._userIgnored(path6, stats);
8105
7975
  }
8106
- _isntIgnored(path7, stat4) {
8107
- return !this._isIgnored(path7, stat4);
7976
+ _isntIgnored(path6, stat4) {
7977
+ return !this._isIgnored(path6, stat4);
8108
7978
  }
8109
- _getWatchHelpers(path7) {
8110
- return new WatchHelper(path7, this.options.followSymlinks, this);
7979
+ _getWatchHelpers(path6) {
7980
+ return new WatchHelper(path6, this.options.followSymlinks, this);
8111
7981
  }
8112
7982
  _getWatchedDir(directory) {
8113
7983
  const dir = sp2.resolve(directory);
@@ -8121,57 +7991,57 @@ class FSWatcher extends EventEmitter2 {
8121
7991
  return Boolean(Number(stats.mode) & 256);
8122
7992
  }
8123
7993
  _remove(directory, item, isDirectory) {
8124
- const path7 = sp2.join(directory, item);
8125
- const fullPath = sp2.resolve(path7);
8126
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path7) || this._watched.has(fullPath);
8127
- if (!this._throttle("remove", path7, 100))
7994
+ const path6 = sp2.join(directory, item);
7995
+ const fullPath = sp2.resolve(path6);
7996
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path6) || this._watched.has(fullPath);
7997
+ if (!this._throttle("remove", path6, 100))
8128
7998
  return;
8129
7999
  if (!isDirectory && this._watched.size === 1) {
8130
8000
  this.add(directory, item, true);
8131
8001
  }
8132
- const wp = this._getWatchedDir(path7);
8002
+ const wp = this._getWatchedDir(path6);
8133
8003
  const nestedDirectoryChildren = wp.getChildren();
8134
- nestedDirectoryChildren.forEach((nested) => this._remove(path7, nested));
8004
+ nestedDirectoryChildren.forEach((nested) => this._remove(path6, nested));
8135
8005
  const parent = this._getWatchedDir(directory);
8136
8006
  const wasTracked = parent.has(item);
8137
8007
  parent.remove(item);
8138
8008
  if (this._symlinkPaths.has(fullPath)) {
8139
8009
  this._symlinkPaths.delete(fullPath);
8140
8010
  }
8141
- let relPath = path7;
8011
+ let relPath = path6;
8142
8012
  if (this.options.cwd)
8143
- relPath = sp2.relative(this.options.cwd, path7);
8013
+ relPath = sp2.relative(this.options.cwd, path6);
8144
8014
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
8145
8015
  const event = this._pendingWrites.get(relPath).cancelWait();
8146
8016
  if (event === EVENTS.ADD)
8147
8017
  return;
8148
8018
  }
8149
- this._watched.delete(path7);
8019
+ this._watched.delete(path6);
8150
8020
  this._watched.delete(fullPath);
8151
8021
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
8152
- if (wasTracked && !this._isIgnored(path7))
8153
- this._emit(eventName, path7);
8154
- this._closePath(path7);
8022
+ if (wasTracked && !this._isIgnored(path6))
8023
+ this._emit(eventName, path6);
8024
+ this._closePath(path6);
8155
8025
  }
8156
- _closePath(path7) {
8157
- this._closeFile(path7);
8158
- const dir = sp2.dirname(path7);
8159
- this._getWatchedDir(dir).remove(sp2.basename(path7));
8026
+ _closePath(path6) {
8027
+ this._closeFile(path6);
8028
+ const dir = sp2.dirname(path6);
8029
+ this._getWatchedDir(dir).remove(sp2.basename(path6));
8160
8030
  }
8161
- _closeFile(path7) {
8162
- const closers = this._closers.get(path7);
8031
+ _closeFile(path6) {
8032
+ const closers = this._closers.get(path6);
8163
8033
  if (!closers)
8164
8034
  return;
8165
8035
  closers.forEach((closer) => closer());
8166
- this._closers.delete(path7);
8036
+ this._closers.delete(path6);
8167
8037
  }
8168
- _addPathCloser(path7, closer) {
8038
+ _addPathCloser(path6, closer) {
8169
8039
  if (!closer)
8170
8040
  return;
8171
- let list = this._closers.get(path7);
8041
+ let list = this._closers.get(path6);
8172
8042
  if (!list) {
8173
8043
  list = [];
8174
- this._closers.set(path7, list);
8044
+ this._closers.set(path6, list);
8175
8045
  }
8176
8046
  list.push(closer);
8177
8047
  }
@@ -8201,7 +8071,7 @@ function watch(paths, options = {}) {
8201
8071
  var chokidar_default = { watch, FSWatcher };
8202
8072
 
8203
8073
  // src/core/watcher.ts
8204
- import path7 from "node:path";
8074
+ import path6 from "node:path";
8205
8075
  function formatTime() {
8206
8076
  const now = new Date;
8207
8077
  const hours = String(now.getHours()).padStart(2, "0");
@@ -8234,7 +8104,7 @@ function getWatchPaths(operations) {
8234
8104
  return Array.from(paths);
8235
8105
  }
8236
8106
  async function handleFileChange(changedPath, state, options) {
8237
- const relPath = path7.relative(options.cwd || process.cwd(), changedPath);
8107
+ const relPath = path6.relative(options.cwd || process.cwd(), changedPath);
8238
8108
  if (!logger.isSilent() && !options.verbose) {
8239
8109
  console.log(`[${formatTime()}] Changed: ${relPath}`);
8240
8110
  }
@@ -8248,9 +8118,9 @@ async function handleFileChange(changedPath, state, options) {
8248
8118
  for (const op of affectedOps) {
8249
8119
  const result = await performMerge(op);
8250
8120
  if (result.success && result.changed) {
8251
- const baseFile = op.basePath ? path7.basename(op.basePath) : "";
8252
- const machineFile = path7.basename(op.machinePath);
8253
- const outputFile = path7.basename(op.outputPath);
8121
+ const baseFile = op.basePath ? path6.basename(op.basePath) : "";
8122
+ const machineFile = path6.basename(op.machinePath);
8123
+ const outputFile = path6.basename(op.outputPath);
8254
8124
  if (!logger.isSilent()) {
8255
8125
  if (baseFile) {
8256
8126
  console.log(`[${formatTime()}] Merged ${baseFile} + ${machineFile} → ${outputFile}`);
@@ -8261,7 +8131,7 @@ async function handleFileChange(changedPath, state, options) {
8261
8131
  } else if (!result.success && result.error) {
8262
8132
  logger.error(`Failed to merge: ${result.error.message}`);
8263
8133
  } else if (options.verbose && !result.changed) {
8264
- logger.info(`No changes needed for ${path7.basename(op.outputPath)}`);
8134
+ logger.info(`No changes needed for ${path6.basename(op.outputPath)}`);
8265
8135
  }
8266
8136
  }
8267
8137
  if (!logger.isSilent() && !options.verbose) {
@@ -8292,7 +8162,7 @@ async function startWatcher(machineName, options = {}) {
8292
8162
  if (!logger.isSilent()) {
8293
8163
  console.log(`✓ Watching ${watchPaths.length} file(s) for changes...`);
8294
8164
  for (const watchPath of watchPaths) {
8295
- console.log(` - ${path7.relative(cwd, watchPath)}`);
8165
+ console.log(` - ${path6.relative(cwd, watchPath)}`);
8296
8166
  }
8297
8167
  console.log("");
8298
8168
  }
@@ -8305,9 +8175,9 @@ async function startWatcher(machineName, options = {}) {
8305
8175
  }
8306
8176
  });
8307
8177
  watcher.on("change", (changedPath) => {
8308
- const absolutePath = path7.resolve(cwd, changedPath);
8178
+ const absolutePath = path6.resolve(cwd, changedPath);
8309
8179
  if (options.verbose) {
8310
- logger.info(`File changed: ${path7.relative(cwd, absolutePath)}`);
8180
+ logger.info(`File changed: ${path6.relative(cwd, absolutePath)}`);
8311
8181
  }
8312
8182
  const existingTimer = state.debounceTimers.get(absolutePath);
8313
8183
  if (existingTimer) {
@@ -8320,9 +8190,9 @@ async function startWatcher(machineName, options = {}) {
8320
8190
  state.debounceTimers.set(absolutePath, timer);
8321
8191
  });
8322
8192
  watcher.on("add", async (addedPath) => {
8323
- const absolutePath = path7.resolve(cwd, addedPath);
8193
+ const absolutePath = path6.resolve(cwd, addedPath);
8324
8194
  if (options.verbose) {
8325
- logger.info(`File added: ${path7.relative(cwd, absolutePath)}`);
8195
+ logger.info(`File added: ${path6.relative(cwd, absolutePath)}`);
8326
8196
  }
8327
8197
  const newOperations = await scanForMergeOperations(machineName, cwd);
8328
8198
  state.operations = newOperations;
@@ -8331,7 +8201,7 @@ async function startWatcher(machineName, options = {}) {
8331
8201
  });
8332
8202
  watcher.on("unlink", (deletedPath) => {
8333
8203
  if (options.verbose) {
8334
- const relPath = path7.relative(cwd, deletedPath);
8204
+ const relPath = path6.relative(cwd, deletedPath);
8335
8205
  logger.warn(`File deleted: ${relPath}`);
8336
8206
  logger.info("Run merge manually or restart watch to update operations");
8337
8207
  }
@@ -8352,22 +8222,37 @@ async function startWatcher(machineName, options = {}) {
8352
8222
  }
8353
8223
 
8354
8224
  // src/cli.ts
8355
- import fs4 from "node:fs/promises";
8356
- import path8 from "node:path";
8357
- import { fileURLToPath as fileURLToPath4 } from "node:url";
8225
+ import fs3 from "node:fs/promises";
8226
+ import path7 from "node:path";
8227
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
8358
8228
  import { createInterface } from "node:readline";
8359
- var __filename3 = fileURLToPath4(import.meta.url);
8360
- var __dirname3 = path8.dirname(__filename3);
8229
+ var __filename2 = fileURLToPath3(import.meta.url);
8230
+ var __dirname2 = path7.dirname(__filename2);
8361
8231
  async function checkExistingOutputFiles(operations) {
8362
8232
  const existing = [];
8363
8233
  for (const op of operations) {
8364
8234
  try {
8365
- await fs4.access(op.outputPath);
8235
+ await fs3.access(op.outputPath);
8366
8236
  existing.push(op.outputPath);
8367
8237
  } catch {}
8368
8238
  }
8369
8239
  return existing;
8370
8240
  }
8241
+ async function checkTrackedOutputFiles(operations) {
8242
+ const tracked = [];
8243
+ const cwd = process.cwd();
8244
+ for (const op of operations) {
8245
+ try {
8246
+ await fs3.access(op.outputPath);
8247
+ const relativePath = path7.relative(cwd, op.outputPath);
8248
+ const isTracked = await isFileTrackedByGit(relativePath, cwd);
8249
+ if (isTracked) {
8250
+ tracked.push(op.outputPath);
8251
+ }
8252
+ } catch {}
8253
+ }
8254
+ return tracked;
8255
+ }
8371
8256
  async function promptConfirmation(message) {
8372
8257
  const rl = createInterface({
8373
8258
  input: process.stdin,
@@ -8434,11 +8319,11 @@ async function handleInit(argv) {
8434
8319
  const operations = await scanForMergeOperations(machineName);
8435
8320
  if (operations.length > 0) {
8436
8321
  logger.info(`Found ${operations.length} machine-specific file(s)`);
8437
- const existingFiles = await checkExistingOutputFiles(operations);
8438
- if (existingFiles.length > 0 && !argv["no-gitignore"]) {
8322
+ const trackedFiles = await checkTrackedOutputFiles(operations);
8323
+ if (trackedFiles.length > 0 && !argv["no-gitignore"]) {
8439
8324
  logger.warn("⚠️ Warning: The following files will be overwritten and untracked from git:");
8440
- for (const file of existingFiles) {
8441
- logger.warn(` - ${path8.relative(process.cwd(), file)}`);
8325
+ for (const file of trackedFiles) {
8326
+ logger.warn(` - ${path7.relative(process.cwd(), file)}`);
8442
8327
  }
8443
8328
  logger.info("");
8444
8329
  const confirmed = await promptConfirmation("Do you want to continue?");
@@ -8502,11 +8387,11 @@ async function handleMerge(argv) {
8502
8387
  return;
8503
8388
  }
8504
8389
  if (!argv.silent && !argv["no-gitignore"]) {
8505
- const existingFiles = await checkExistingOutputFiles(operations);
8506
- if (existingFiles.length > 0) {
8390
+ const trackedFiles = await checkTrackedOutputFiles(operations);
8391
+ if (trackedFiles.length > 0) {
8507
8392
  logger.warn("⚠️ Warning: The following files will be overwritten and untracked from git:");
8508
- for (const file of existingFiles) {
8509
- logger.warn(` - ${path8.relative(process.cwd(), file)}`);
8393
+ for (const file of trackedFiles) {
8394
+ logger.warn(` - ${path7.relative(process.cwd(), file)}`);
8510
8395
  }
8511
8396
  logger.info("");
8512
8397
  const confirmed = await promptConfirmation("Do you want to continue?");
@@ -8544,11 +8429,11 @@ async function handleInfo(argv) {
8544
8429
  const operations = await scanForMergeOperations(machineName);
8545
8430
  console.log(`Machine name: ${machineName}`);
8546
8431
  console.log(`Repository: ${process.cwd()}`);
8547
- const { exec: exec3 } = await import("node:child_process");
8548
- const { promisify: promisify3 } = await import("node:util");
8549
- const execAsync3 = promisify3(exec3);
8432
+ const { exec: exec2 } = await import("node:child_process");
8433
+ const { promisify: promisify2 } = await import("node:util");
8434
+ const execAsync2 = promisify2(exec2);
8550
8435
  try {
8551
- const { stdout } = await execAsync3("git config --get core.hooksPath");
8436
+ const { stdout } = await execAsync2("git config --get core.hooksPath");
8552
8437
  const hooksPath = stdout.trim();
8553
8438
  if (hooksPath) {
8554
8439
  console.log(`Hooks method: core.hooksPath`);
@@ -8561,9 +8446,9 @@ async function handleInfo(argv) {
8561
8446
  }
8562
8447
  console.log(`Tracked patterns: ${operations.length}`);
8563
8448
  for (const op of operations) {
8564
- const baseName = op.basePath ? path8.basename(op.basePath) : "(none)";
8565
- const machineName2 = path8.basename(op.machinePath);
8566
- const outputName = path8.basename(op.outputPath);
8449
+ const baseName = op.basePath ? path7.basename(op.basePath) : "(none)";
8450
+ const machineName2 = path7.basename(op.machinePath);
8451
+ const outputName = path7.basename(op.outputPath);
8567
8452
  console.log(` - ${baseName} + ${machineName2} → ${outputName}`);
8568
8453
  }
8569
8454
  if (operations.length > 0) {
@@ -8573,7 +8458,7 @@ async function handleInfo(argv) {
8573
8458
  if (existingFiles.length > 0) {
8574
8459
  console.log("Existing output files:");
8575
8460
  for (const file of existingFiles) {
8576
- console.log(` - ${path8.relative(process.cwd(), file)}`);
8461
+ console.log(` - ${path7.relative(process.cwd(), file)}`);
8577
8462
  }
8578
8463
  }
8579
8464
  }
@@ -8654,8 +8539,8 @@ DOCUMENTATION:
8654
8539
  }
8655
8540
  async function showVersion() {
8656
8541
  try {
8657
- const packageJsonPath = path8.join(__dirname3, "../package.json");
8658
- const packageJson = JSON.parse(await fs4.readFile(packageJsonPath, "utf-8"));
8542
+ const packageJsonPath = path7.join(__dirname2, "../package.json");
8543
+ const packageJson = JSON.parse(await fs3.readFile(packageJsonPath, "utf-8"));
8659
8544
  console.log(packageJson.version);
8660
8545
  } catch {
8661
8546
  console.log("unknown");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "permachine",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Automatically merge machine-specific config files with base configs using git hooks",
5
5
  "type": "module",
6
6
  "bin": {