@holdyourvoice/hyv 2.8.8 → 2.8.9

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/index.js CHANGED
@@ -973,8 +973,8 @@ var require_command = __commonJS({
973
973
  "node_modules/commander/lib/command.js"(exports2) {
974
974
  var EventEmitter = require("node:events").EventEmitter;
975
975
  var childProcess = require("node:child_process");
976
- var path23 = require("node:path");
977
- var fs24 = require("node:fs");
976
+ var path24 = require("node:path");
977
+ var fs25 = require("node:fs");
978
978
  var process2 = require("node:process");
979
979
  var { Argument: Argument2, humanReadableArgName } = require_argument();
980
980
  var { CommanderError: CommanderError2 } = require_error();
@@ -1916,13 +1916,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
1916
1916
  let launchWithNode = false;
1917
1917
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1918
1918
  function findFile(baseDir, baseName) {
1919
- const localBin = path23.resolve(baseDir, baseName);
1920
- if (fs24.existsSync(localBin))
1919
+ const localBin = path24.resolve(baseDir, baseName);
1920
+ if (fs25.existsSync(localBin))
1921
1921
  return localBin;
1922
- if (sourceExt.includes(path23.extname(baseName)))
1922
+ if (sourceExt.includes(path24.extname(baseName)))
1923
1923
  return void 0;
1924
1924
  const foundExt = sourceExt.find(
1925
- (ext) => fs24.existsSync(`${localBin}${ext}`)
1925
+ (ext) => fs25.existsSync(`${localBin}${ext}`)
1926
1926
  );
1927
1927
  if (foundExt)
1928
1928
  return `${localBin}${foundExt}`;
@@ -1935,21 +1935,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
1935
1935
  if (this._scriptPath) {
1936
1936
  let resolvedScriptPath;
1937
1937
  try {
1938
- resolvedScriptPath = fs24.realpathSync(this._scriptPath);
1938
+ resolvedScriptPath = fs25.realpathSync(this._scriptPath);
1939
1939
  } catch (err) {
1940
1940
  resolvedScriptPath = this._scriptPath;
1941
1941
  }
1942
- executableDir = path23.resolve(
1943
- path23.dirname(resolvedScriptPath),
1942
+ executableDir = path24.resolve(
1943
+ path24.dirname(resolvedScriptPath),
1944
1944
  executableDir
1945
1945
  );
1946
1946
  }
1947
1947
  if (executableDir) {
1948
1948
  let localFile = findFile(executableDir, executableFile);
1949
1949
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1950
- const legacyName = path23.basename(
1950
+ const legacyName = path24.basename(
1951
1951
  this._scriptPath,
1952
- path23.extname(this._scriptPath)
1952
+ path24.extname(this._scriptPath)
1953
1953
  );
1954
1954
  if (legacyName !== this._name) {
1955
1955
  localFile = findFile(
@@ -1960,7 +1960,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1960
1960
  }
1961
1961
  executableFile = localFile || executableFile;
1962
1962
  }
1963
- launchWithNode = sourceExt.includes(path23.extname(executableFile));
1963
+ launchWithNode = sourceExt.includes(path24.extname(executableFile));
1964
1964
  let proc;
1965
1965
  if (process2.platform !== "win32") {
1966
1966
  if (launchWithNode) {
@@ -2817,7 +2817,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2817
2817
  * @return {Command}
2818
2818
  */
2819
2819
  nameFromFilename(filename) {
2820
- this._name = path23.basename(filename, path23.extname(filename));
2820
+ this._name = path24.basename(filename, path24.extname(filename));
2821
2821
  return this;
2822
2822
  }
2823
2823
  /**
@@ -2831,10 +2831,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
2831
2831
  * @param {string} [path]
2832
2832
  * @return {(string|null|Command)}
2833
2833
  */
2834
- executableDir(path24) {
2835
- if (path24 === void 0)
2834
+ executableDir(path25) {
2835
+ if (path25 === void 0)
2836
2836
  return this._executableDir;
2837
- this._executableDir = path24;
2837
+ this._executableDir = path25;
2838
2838
  return this;
2839
2839
  }
2840
2840
  /**
@@ -3934,15 +3934,15 @@ var require_route = __commonJS({
3934
3934
  };
3935
3935
  }
3936
3936
  function wrapConversion(toModel, graph) {
3937
- const path23 = [graph[toModel].parent, toModel];
3937
+ const path24 = [graph[toModel].parent, toModel];
3938
3938
  let fn = conversions[graph[toModel].parent][toModel];
3939
3939
  let cur = graph[toModel].parent;
3940
3940
  while (graph[cur].parent) {
3941
- path23.unshift(graph[cur].parent);
3941
+ path24.unshift(graph[cur].parent);
3942
3942
  fn = link(conversions[graph[cur].parent][cur], fn);
3943
3943
  cur = graph[cur].parent;
3944
3944
  }
3945
- fn.conversion = path23;
3945
+ fn.conversion = path24;
3946
3946
  return fn;
3947
3947
  }
3948
3948
  module2.exports = function(fromModel) {
@@ -4381,14 +4381,14 @@ var require_templates = __commonJS({
4381
4381
  }
4382
4382
  return results;
4383
4383
  }
4384
- function buildStyle(chalk31, styles) {
4384
+ function buildStyle(chalk32, styles) {
4385
4385
  const enabled = {};
4386
4386
  for (const layer of styles) {
4387
4387
  for (const style of layer.styles) {
4388
4388
  enabled[style[0]] = layer.inverse ? null : style.slice(1);
4389
4389
  }
4390
4390
  }
4391
- let current = chalk31;
4391
+ let current = chalk32;
4392
4392
  for (const [styleName, styles2] of Object.entries(enabled)) {
4393
4393
  if (!Array.isArray(styles2)) {
4394
4394
  continue;
@@ -4400,7 +4400,7 @@ var require_templates = __commonJS({
4400
4400
  }
4401
4401
  return current;
4402
4402
  }
4403
- module2.exports = (chalk31, temporary) => {
4403
+ module2.exports = (chalk32, temporary) => {
4404
4404
  const styles = [];
4405
4405
  const chunks = [];
4406
4406
  let chunk = [];
@@ -4410,13 +4410,13 @@ var require_templates = __commonJS({
4410
4410
  } else if (style) {
4411
4411
  const string = chunk.join("");
4412
4412
  chunk = [];
4413
- chunks.push(styles.length === 0 ? string : buildStyle(chalk31, styles)(string));
4413
+ chunks.push(styles.length === 0 ? string : buildStyle(chalk32, styles)(string));
4414
4414
  styles.push({ inverse, styles: parseStyle(style) });
4415
4415
  } else if (close) {
4416
4416
  if (styles.length === 0) {
4417
4417
  throw new Error("Found extraneous } in Chalk template literal");
4418
4418
  }
4419
- chunks.push(buildStyle(chalk31, styles)(chunk.join("")));
4419
+ chunks.push(buildStyle(chalk32, styles)(chunk.join("")));
4420
4420
  chunk = [];
4421
4421
  styles.pop();
4422
4422
  } else {
@@ -4464,16 +4464,16 @@ var require_source = __commonJS({
4464
4464
  }
4465
4465
  };
4466
4466
  var chalkFactory = (options) => {
4467
- const chalk32 = {};
4468
- applyOptions(chalk32, options);
4469
- chalk32.template = (...arguments_) => chalkTag(chalk32.template, ...arguments_);
4470
- Object.setPrototypeOf(chalk32, Chalk.prototype);
4471
- Object.setPrototypeOf(chalk32.template, chalk32);
4472
- chalk32.template.constructor = () => {
4467
+ const chalk33 = {};
4468
+ applyOptions(chalk33, options);
4469
+ chalk33.template = (...arguments_) => chalkTag(chalk33.template, ...arguments_);
4470
+ Object.setPrototypeOf(chalk33, Chalk.prototype);
4471
+ Object.setPrototypeOf(chalk33.template, chalk33);
4472
+ chalk33.template.constructor = () => {
4473
4473
  throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.");
4474
4474
  };
4475
- chalk32.template.Instance = ChalkClass;
4476
- return chalk32.template;
4475
+ chalk33.template.Instance = ChalkClass;
4476
+ return chalk33.template;
4477
4477
  };
4478
4478
  function Chalk(options) {
4479
4479
  return chalkFactory(options);
@@ -4584,7 +4584,7 @@ var require_source = __commonJS({
4584
4584
  return openAll + string + closeAll;
4585
4585
  };
4586
4586
  var template;
4587
- var chalkTag = (chalk32, ...strings) => {
4587
+ var chalkTag = (chalk33, ...strings) => {
4588
4588
  const [firstString] = strings;
4589
4589
  if (!isArray(firstString) || !isArray(firstString.raw)) {
4590
4590
  return strings.join(" ");
@@ -4600,14 +4600,14 @@ var require_source = __commonJS({
4600
4600
  if (template === void 0) {
4601
4601
  template = require_templates();
4602
4602
  }
4603
- return template(chalk32, parts.join(""));
4603
+ return template(chalk33, parts.join(""));
4604
4604
  };
4605
4605
  Object.defineProperties(Chalk.prototype, styles);
4606
- var chalk31 = Chalk();
4607
- chalk31.supportsColor = stdoutColor;
4608
- chalk31.stderr = Chalk({ level: stderrColor ? stderrColor.level : 0 });
4609
- chalk31.stderr.supportsColor = stderrColor;
4610
- module2.exports = chalk31;
4606
+ var chalk32 = Chalk();
4607
+ chalk32.supportsColor = stdoutColor;
4608
+ chalk32.stderr = Chalk({ level: stderrColor ? stderrColor.level : 0 });
4609
+ chalk32.stderr.supportsColor = stderrColor;
4610
+ module2.exports = chalk32;
4611
4611
  }
4612
4612
  });
4613
4613
 
@@ -4834,11 +4834,11 @@ var init_config = __esm({
4834
4834
  var require_is_docker = __commonJS({
4835
4835
  "node_modules/is-docker/index.js"(exports2, module2) {
4836
4836
  "use strict";
4837
- var fs24 = require("fs");
4837
+ var fs25 = require("fs");
4838
4838
  var isDocker;
4839
4839
  function hasDockerEnv() {
4840
4840
  try {
4841
- fs24.statSync("/.dockerenv");
4841
+ fs25.statSync("/.dockerenv");
4842
4842
  return true;
4843
4843
  } catch (_) {
4844
4844
  return false;
@@ -4846,7 +4846,7 @@ var require_is_docker = __commonJS({
4846
4846
  }
4847
4847
  function hasDockerCGroup() {
4848
4848
  try {
4849
- return fs24.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4849
+ return fs25.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4850
4850
  } catch (_) {
4851
4851
  return false;
4852
4852
  }
@@ -4865,7 +4865,7 @@ var require_is_wsl = __commonJS({
4865
4865
  "node_modules/is-wsl/index.js"(exports2, module2) {
4866
4866
  "use strict";
4867
4867
  var os9 = require("os");
4868
- var fs24 = require("fs");
4868
+ var fs25 = require("fs");
4869
4869
  var isDocker = require_is_docker();
4870
4870
  var isWsl = () => {
4871
4871
  if (process.platform !== "linux") {
@@ -4878,7 +4878,7 @@ var require_is_wsl = __commonJS({
4878
4878
  return true;
4879
4879
  }
4880
4880
  try {
4881
- return fs24.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
4881
+ return fs25.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
4882
4882
  } catch (_) {
4883
4883
  return false;
4884
4884
  }
@@ -4917,17 +4917,17 @@ var require_define_lazy_prop = __commonJS({
4917
4917
  // node_modules/open/index.js
4918
4918
  var require_open = __commonJS({
4919
4919
  "node_modules/open/index.js"(exports2, module2) {
4920
- var path23 = require("path");
4920
+ var path24 = require("path");
4921
4921
  var childProcess = require("child_process");
4922
- var { promises: fs24, constants: fsConstants } = require("fs");
4922
+ var { promises: fs25, constants: fsConstants } = require("fs");
4923
4923
  var isWsl = require_is_wsl();
4924
4924
  var isDocker = require_is_docker();
4925
4925
  var defineLazyProperty = require_define_lazy_prop();
4926
- var localXdgOpenPath = path23.join(__dirname, "xdg-open");
4926
+ var localXdgOpenPath = path24.join(__dirname, "xdg-open");
4927
4927
  var { platform, arch } = process;
4928
4928
  var hasContainerEnv = () => {
4929
4929
  try {
4930
- fs24.statSync("/run/.containerenv");
4930
+ fs25.statSync("/run/.containerenv");
4931
4931
  return true;
4932
4932
  } catch {
4933
4933
  return false;
@@ -4950,14 +4950,14 @@ var require_open = __commonJS({
4950
4950
  const configFilePath = "/etc/wsl.conf";
4951
4951
  let isConfigFileExists = false;
4952
4952
  try {
4953
- await fs24.access(configFilePath, fsConstants.F_OK);
4953
+ await fs25.access(configFilePath, fsConstants.F_OK);
4954
4954
  isConfigFileExists = true;
4955
4955
  } catch {
4956
4956
  }
4957
4957
  if (!isConfigFileExists) {
4958
4958
  return defaultMountPoint;
4959
4959
  }
4960
- const configContent = await fs24.readFile(configFilePath, { encoding: "utf8" });
4960
+ const configContent = await fs25.readFile(configFilePath, { encoding: "utf8" });
4961
4961
  const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
4962
4962
  if (!configMountPoint) {
4963
4963
  return defaultMountPoint;
@@ -5057,7 +5057,7 @@ var require_open = __commonJS({
5057
5057
  const isBundled = !__dirname || __dirname === "/";
5058
5058
  let exeLocalXdgOpen = false;
5059
5059
  try {
5060
- await fs24.access(localXdgOpenPath, fsConstants.X_OK);
5060
+ await fs25.access(localXdgOpenPath, fsConstants.X_OK);
5061
5061
  exeLocalXdgOpen = true;
5062
5062
  } catch {
5063
5063
  }
@@ -5244,6 +5244,33 @@ var init_version = __esm({
5244
5244
  }
5245
5245
  });
5246
5246
 
5247
+ // src/lib/auth-refresh.ts
5248
+ function tokenExpiresAtMs(expiresAt) {
5249
+ if (!expiresAt)
5250
+ return null;
5251
+ const ms = new Date(expiresAt).getTime();
5252
+ return Number.isFinite(ms) ? ms : null;
5253
+ }
5254
+ function shouldRefreshToken(expiresAt, now = Date.now()) {
5255
+ const expiresMs = tokenExpiresAtMs(expiresAt);
5256
+ if (expiresMs === null)
5257
+ return false;
5258
+ return expiresMs <= now + REFRESH_LEAD_MS;
5259
+ }
5260
+ function isTokenExpired(expiresAt, now = Date.now()) {
5261
+ const expiresMs = tokenExpiresAtMs(expiresAt);
5262
+ if (expiresMs === null)
5263
+ return false;
5264
+ return expiresMs < now;
5265
+ }
5266
+ var REFRESH_LEAD_MS;
5267
+ var init_auth_refresh = __esm({
5268
+ "src/lib/auth-refresh.ts"() {
5269
+ "use strict";
5270
+ REFRESH_LEAD_MS = 5 * 60 * 1e3;
5271
+ }
5272
+ });
5273
+
5247
5274
  // src/lib/auth.ts
5248
5275
  var auth_exports = {};
5249
5276
  __export(auth_exports, {
@@ -5252,8 +5279,14 @@ __export(auth_exports, {
5252
5279
  authenticatedRequest: () => authenticatedRequest,
5253
5280
  checkSession: () => checkSession,
5254
5281
  getValidToken: () => getValidToken,
5255
- refreshToken: () => refreshToken
5282
+ refreshToken: () => refreshToken,
5283
+ verifyOAuthState: () => verifyOAuthState
5256
5284
  });
5285
+ function verifyOAuthState(received, expected) {
5286
+ if (!received || !expected || received !== expected) {
5287
+ throw new Error("OAuth state mismatch \u2014 possible CSRF attempt");
5288
+ }
5289
+ }
5257
5290
  function escapeHtml(value) {
5258
5291
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5259
5292
  }
@@ -5300,16 +5333,12 @@ async function getValidToken() {
5300
5333
  const auth = readAuth();
5301
5334
  if (!auth?.token)
5302
5335
  return null;
5303
- if (auth.expires_at) {
5304
- const expires = new Date(auth.expires_at);
5305
- const refreshBy = Date.now() + 5 * 60 * 1e3;
5306
- if (expires.getTime() <= refreshBy) {
5307
- const ok = await refreshToken(auth.token);
5308
- if (ok)
5309
- return getToken();
5310
- if (expires.getTime() < Date.now())
5311
- return null;
5312
- }
5336
+ if (shouldRefreshToken(auth.expires_at)) {
5337
+ const ok = await refreshToken(auth.token);
5338
+ if (ok)
5339
+ return getToken();
5340
+ if (isTokenExpired(auth.expires_at))
5341
+ return null;
5313
5342
  }
5314
5343
  return auth.token;
5315
5344
  }
@@ -5382,12 +5411,14 @@ async function authenticateWithBrowser() {
5382
5411
  reject(new Error("Invalid callback parameters"));
5383
5412
  return;
5384
5413
  }
5385
- if (state !== expectedState) {
5414
+ try {
5415
+ verifyOAuthState(state, expectedState);
5416
+ } catch (stateErr) {
5386
5417
  res.writeHead(400, { "Content-Type": "text/html" });
5387
5418
  res.end("<h1>Authentication failed</h1><p>Invalid OAuth state.</p>");
5388
5419
  clearTimeout(timeout);
5389
5420
  server.close();
5390
- reject(new Error("OAuth state mismatch \u2014 possible CSRF attempt"));
5421
+ reject(stateErr);
5391
5422
  return;
5392
5423
  }
5393
5424
  try {
@@ -5481,6 +5512,7 @@ var init_auth = __esm({
5481
5512
  import_open = __toESM(require_open());
5482
5513
  init_config();
5483
5514
  init_version();
5515
+ init_auth_refresh();
5484
5516
  }
5485
5517
  });
5486
5518
 
@@ -5641,11 +5673,11 @@ __export(api_exports, {
5641
5673
  apiPost: () => apiPost,
5642
5674
  requireSubscription: () => requireSubscription
5643
5675
  });
5644
- async function request2(method, path23, body) {
5676
+ async function request2(method, path24, body) {
5645
5677
  const token = await getValidToken();
5646
5678
  if (!token)
5647
5679
  throw new Error("you're not signed in yet. run: hyv init");
5648
- const url = `${API_BASE}${path23}`;
5680
+ const url = `${API_BASE}${path24}`;
5649
5681
  const opts = {
5650
5682
  method,
5651
5683
  headers: {
@@ -5670,11 +5702,11 @@ async function request2(method, path23, body) {
5670
5702
  }
5671
5703
  return res.json();
5672
5704
  }
5673
- function apiGet(path23) {
5674
- return request2("GET", path23);
5705
+ function apiGet(path24) {
5706
+ return request2("GET", path24);
5675
5707
  }
5676
- function apiPost(path23, body) {
5677
- return request2("POST", path23, body);
5708
+ function apiPost(path24, body) {
5709
+ return request2("POST", path24, body);
5678
5710
  }
5679
5711
  async function requireSubscription() {
5680
5712
  const { requirePaidFeature: requirePaidFeature2 } = await Promise.resolve().then(() => (init_access(), access_exports));
@@ -7246,26 +7278,26 @@ function runPipeline(text, profile, applyFixes = false) {
7246
7278
  };
7247
7279
  }
7248
7280
  function readText(source) {
7249
- const fs24 = require("fs");
7250
- const path23 = require("path");
7281
+ const fs25 = require("fs");
7282
+ const path24 = require("path");
7251
7283
  if (source === "-") {
7252
7284
  if (process.stdin.isTTY) {
7253
7285
  console.error("No input provided. Pipe content or specify a file.");
7254
7286
  process.exit(1);
7255
7287
  }
7256
- return { text: fs24.readFileSync(0, "utf-8"), path: "stdin" };
7288
+ return { text: fs25.readFileSync(0, "utf-8"), path: "stdin" };
7257
7289
  }
7258
- const resolved = path23.resolve(source);
7259
- if (!fs24.existsSync(resolved)) {
7290
+ const resolved = path24.resolve(source);
7291
+ if (!fs25.existsSync(resolved)) {
7260
7292
  console.error(`File not found: ${resolved}`);
7261
7293
  process.exit(1);
7262
7294
  }
7263
- const stat = fs24.statSync(resolved);
7295
+ const stat = fs25.statSync(resolved);
7264
7296
  if (stat.isDirectory()) {
7265
7297
  console.error(`${resolved} is a directory, not a file.`);
7266
7298
  process.exit(1);
7267
7299
  }
7268
- return { text: fs24.readFileSync(resolved, "utf-8"), path: resolved };
7300
+ return { text: fs25.readFileSync(resolved, "utf-8"), path: resolved };
7269
7301
  }
7270
7302
  var init_pipeline = __esm({
7271
7303
  "src/lib/pipeline.ts"() {
@@ -8273,7 +8305,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
8273
8305
  g.minimatch.escape = vi.escape;
8274
8306
  g.minimatch.unescape = Ei.unescape;
8275
8307
  });
8276
- var fs24 = R((Wt) => {
8308
+ var fs25 = R((Wt) => {
8277
8309
  "use strict";
8278
8310
  Object.defineProperty(Wt, "__esModule", { value: true });
8279
8311
  Wt.LRUCache = void 0;
@@ -9242,7 +9274,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
9242
9274
  };
9243
9275
  Object.defineProperty(_, "__esModule", { value: true });
9244
9276
  _.PathScurry = _.Path = _.PathScurryDarwin = _.PathScurryPosix = _.PathScurryWin32 = _.PathScurryBase = _.PathPosix = _.PathWin32 = _.PathBase = _.ChildrenCache = _.ResolveCache = void 0;
9245
- var Qt = fs24(), Yt = require("node:path"), yr = require("node:url"), pt = require("fs"), Sr = br(require("node:fs")), vr = pt.realpathSync.native, Ht = require("node:fs/promises"), bs = Oe(), mt = { lstatSync: pt.lstatSync, readdir: pt.readdir, readdirSync: pt.readdirSync, readlinkSync: pt.readlinkSync, realpathSync: vr, promises: { lstat: Ht.lstat, readdir: Ht.readdir, readlink: Ht.readlink, realpath: Ht.realpath } }, _s = (n) => !n || n === mt || n === Sr ? mt : { ...mt, ...n, promises: { ...mt.promises, ...n.promises || {} } }, Os = /^\\\\\?\\([a-z]:)\\?$/i, Er = (n) => n.replace(/\//g, "\\").replace(Os, "$1\\"), _r = /[\\\/]/, N = 0, xs = 1, Ts = 2, G = 4, Cs = 6, Rs = 8, Q = 10, As = 12, j = 15, dt = ~j, xe = 16, ys = 32, gt = 64, W = 128, Vt = 256, Xt = 512, Ss = gt | W | Xt, Or = 1023, Te = (n) => n.isFile() ? Rs : n.isDirectory() ? G : n.isSymbolicLink() ? Q : n.isCharacterDevice() ? Ts : n.isBlockDevice() ? Cs : n.isSocket() ? As : n.isFIFO() ? xs : N, vs = new Qt.LRUCache({ max: 2 ** 12 }), wt = (n) => {
9277
+ var Qt = fs25(), Yt = require("node:path"), yr = require("node:url"), pt = require("fs"), Sr = br(require("node:fs")), vr = pt.realpathSync.native, Ht = require("node:fs/promises"), bs = Oe(), mt = { lstatSync: pt.lstatSync, readdir: pt.readdir, readdirSync: pt.readdirSync, readlinkSync: pt.readlinkSync, realpathSync: vr, promises: { lstat: Ht.lstat, readdir: Ht.readdir, readlink: Ht.readlink, realpath: Ht.realpath } }, _s = (n) => !n || n === mt || n === Sr ? mt : { ...mt, ...n, promises: { ...mt.promises, ...n.promises || {} } }, Os = /^\\\\\?\\([a-z]:)\\?$/i, Er = (n) => n.replace(/\//g, "\\").replace(Os, "$1\\"), _r = /[\\\/]/, N = 0, xs = 1, Ts = 2, G = 4, Cs = 6, Rs = 8, Q = 10, As = 12, j = 15, dt = ~j, xe = 16, ys = 32, gt = 64, W = 128, Vt = 256, Xt = 512, Ss = gt | W | Xt, Or = 1023, Te = (n) => n.isFile() ? Rs : n.isDirectory() ? G : n.isSymbolicLink() ? Q : n.isCharacterDevice() ? Ts : n.isBlockDevice() ? Cs : n.isSocket() ? As : n.isFIFO() ? xs : N, vs = new Qt.LRUCache({ max: 2 ** 12 }), wt = (n) => {
9246
9278
  let t = vs.get(n);
9247
9279
  if (t)
9248
9280
  return t;
@@ -10761,7 +10793,7 @@ var {
10761
10793
  } = import_index.default;
10762
10794
 
10763
10795
  // src/index.ts
10764
- var import_chalk30 = __toESM(require_source());
10796
+ var import_chalk31 = __toESM(require_source());
10765
10797
 
10766
10798
  // src/commands/init.ts
10767
10799
  var import_chalk4 = __toESM(require_source());
@@ -12454,8 +12486,8 @@ function registerImportCommand(program3) {
12454
12486
  }
12455
12487
  function askQuestion(question) {
12456
12488
  return new Promise((resolve14) => {
12457
- const readline = require("readline");
12458
- const rl = readline.createInterface({
12489
+ const readline2 = require("readline");
12490
+ const rl = readline2.createInterface({
12459
12491
  input: process.stdin,
12460
12492
  output: process.stdout
12461
12493
  });
@@ -13040,28 +13072,137 @@ hyv scan ${filePath}`));
13040
13072
 
13041
13073
  // src/commands/doctor.ts
13042
13074
  var import_chalk16 = __toESM(require_source());
13043
- var fs13 = __toESM(require("fs"));
13044
- var path14 = __toESM(require("path"));
13075
+ var fs14 = __toESM(require("fs"));
13076
+ var path15 = __toESM(require("path"));
13045
13077
  var os5 = __toESM(require("os"));
13046
13078
  init_config();
13047
13079
  init_auth();
13048
13080
  init_access();
13049
13081
  init_local_profile();
13050
13082
  init_version();
13083
+
13084
+ // src/lib/mcp-stdio-test.ts
13085
+ var import_child_process2 = require("child_process");
13086
+
13087
+ // src/lib/cli-entry.ts
13088
+ var fs13 = __toESM(require("fs"));
13089
+ var path14 = __toESM(require("path"));
13090
+ function resolveCliEntry() {
13091
+ const candidates = [
13092
+ path14.resolve(process.argv[1] || ""),
13093
+ path14.resolve(__dirname, "index.js"),
13094
+ path14.resolve(__dirname, "..", "dist", "index.js")
13095
+ ];
13096
+ return candidates.find((p) => p && fs13.existsSync(p)) || null;
13097
+ }
13098
+ function mcpServerCommand() {
13099
+ const entry = resolveCliEntry();
13100
+ if (entry) {
13101
+ return { command: process.execPath, args: [entry, "mcp"] };
13102
+ }
13103
+ return { command: "hyv", args: ["mcp"] };
13104
+ }
13105
+ function mcpServerSnippet() {
13106
+ const { command, args: args2 } = mcpServerCommand();
13107
+ return JSON.stringify({ command, args: args2 }, null, 2);
13108
+ }
13109
+
13110
+ // src/lib/mcp-stdio-test.ts
13111
+ async function testMcpStdioSubprocess() {
13112
+ const entry = resolveCliEntry();
13113
+ if (!entry)
13114
+ return { ok: false };
13115
+ return new Promise((resolve14) => {
13116
+ const child = (0, import_child_process2.spawn)(process.execPath, [entry, "mcp"], {
13117
+ stdio: ["pipe", "pipe", "pipe"],
13118
+ env: { ...process.env, HYV_POSTINSTALL_QUIET: "1" }
13119
+ });
13120
+ let buffer = "";
13121
+ let settled = false;
13122
+ const finish = (ok, toolCount) => {
13123
+ if (settled)
13124
+ return;
13125
+ settled = true;
13126
+ clearTimeout(timeout);
13127
+ try {
13128
+ child.kill();
13129
+ } catch {
13130
+ }
13131
+ resolve14({ ok, toolCount });
13132
+ };
13133
+ const timeout = setTimeout(() => finish(false), 1e4);
13134
+ const handleLine = (line) => {
13135
+ const trimmed = line.trim();
13136
+ if (!trimmed.startsWith("{"))
13137
+ return;
13138
+ try {
13139
+ const msg = JSON.parse(trimmed);
13140
+ if (msg.id === 2 && Array.isArray(msg.result?.tools) && msg.result.tools.length > 0) {
13141
+ finish(true, msg.result.tools.length);
13142
+ }
13143
+ } catch {
13144
+ }
13145
+ };
13146
+ child.stdout.on("data", (chunk) => {
13147
+ buffer += chunk.toString();
13148
+ const lines = buffer.split("\n");
13149
+ buffer = lines.pop() || "";
13150
+ for (const line of lines)
13151
+ handleLine(line);
13152
+ });
13153
+ child.on("error", () => finish(false));
13154
+ child.on("exit", () => {
13155
+ if (!settled) {
13156
+ handleLine(buffer);
13157
+ finish(buffer.includes("hyv_scan"));
13158
+ }
13159
+ });
13160
+ setTimeout(() => {
13161
+ const send2 = (payload) => {
13162
+ child.stdin.write(`${JSON.stringify(payload)}
13163
+ `);
13164
+ };
13165
+ send2({
13166
+ jsonrpc: "2.0",
13167
+ id: 1,
13168
+ method: "initialize",
13169
+ params: {
13170
+ protocolVersion: "2024-11-05",
13171
+ capabilities: {},
13172
+ clientInfo: { name: "hyv-self-test", version: "1.0" }
13173
+ }
13174
+ });
13175
+ send2({ jsonrpc: "2.0", method: "notifications/initialized" });
13176
+ send2({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} });
13177
+ }, 500);
13178
+ });
13179
+ }
13180
+
13181
+ // src/commands/doctor.ts
13051
13182
  var HOME = os5.homedir();
13052
13183
  var IS_WIN = process.platform === "win32";
13053
13184
  function claudeDesktopDir() {
13054
13185
  if (IS_WIN)
13055
- return path14.join(HOME, "AppData", "Roaming", "Claude");
13186
+ return path15.join(HOME, "AppData", "Roaming", "Claude");
13056
13187
  if (process.platform === "linux")
13057
- return path14.join(HOME, ".config", "Claude");
13058
- return path14.join(HOME, "Library", "Application Support", "Claude");
13188
+ return path15.join(HOME, ".config", "Claude");
13189
+ return path15.join(HOME, "Library", "Application Support", "Claude");
13190
+ }
13191
+ function isOwnerOnlyFile(filePath) {
13192
+ try {
13193
+ if (!fs14.existsSync(filePath))
13194
+ return true;
13195
+ const mode = fs14.statSync(filePath).mode & 511;
13196
+ return (mode & 63) === 0;
13197
+ } catch {
13198
+ return false;
13199
+ }
13059
13200
  }
13060
13201
  function readMcpHyv(configFile) {
13061
13202
  try {
13062
- if (!fs13.existsSync(configFile))
13203
+ if (!fs14.existsSync(configFile))
13063
13204
  return false;
13064
- const cfg = JSON.parse(fs13.readFileSync(configFile, "utf-8"));
13205
+ const cfg = JSON.parse(fs14.readFileSync(configFile, "utf-8"));
13065
13206
  return Boolean(cfg.mcpServers?.hyv);
13066
13207
  } catch {
13067
13208
  return false;
@@ -13082,28 +13223,28 @@ function registerDoctorCommand(program3) {
13082
13223
  console.log(import_chalk16.default.dim("tip: run hyv welcome for free capabilities\n"));
13083
13224
  console.log(import_chalk16.default.dim("checking cli installation..."));
13084
13225
  const cliPath = process.argv[1];
13085
- if (cliPath && fs13.existsSync(cliPath)) {
13226
+ if (cliPath && fs14.existsSync(cliPath)) {
13086
13227
  console.log(import_chalk16.default.green(" \u2713 cli installed"));
13087
13228
  } else {
13088
13229
  console.log(import_chalk16.default.red(" \u2717 cli not found"));
13089
13230
  issues++;
13090
13231
  }
13091
13232
  console.log(import_chalk16.default.dim("checking .hyv directory..."));
13092
- if (fs13.existsSync(HYV_DIR)) {
13233
+ if (fs14.existsSync(HYV_DIR)) {
13093
13234
  console.log(import_chalk16.default.green(" \u2713 .hyv directory exists"));
13094
13235
  } else {
13095
13236
  console.log(import_chalk16.default.yellow(" ! .hyv directory missing \u2014 creating..."));
13096
- fs13.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
13237
+ fs14.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
13097
13238
  fixed++;
13098
13239
  }
13099
13240
  console.log(import_chalk16.default.dim("checking cache..."));
13100
13241
  const diskProfiles = listDiskCachedProfiles();
13101
- const syncMeta = path14.join(CACHE_DIR, "sync-meta.json");
13102
- if (diskProfiles.length > 0 || fs13.existsSync(syncMeta)) {
13242
+ const syncMeta = path15.join(CACHE_DIR, "sync-meta.json");
13243
+ if (diskProfiles.length > 0 || fs14.existsSync(syncMeta)) {
13103
13244
  console.log(import_chalk16.default.green(` \u2713 profile cache (${diskProfiles.length} full profile(s))`));
13104
- if (fs13.existsSync(syncMeta)) {
13245
+ if (fs14.existsSync(syncMeta)) {
13105
13246
  try {
13106
- const meta = JSON.parse(fs13.readFileSync(syncMeta, "utf-8"));
13247
+ const meta = JSON.parse(fs14.readFileSync(syncMeta, "utf-8"));
13107
13248
  console.log(import_chalk16.default.dim(` last sync: ${meta.synced_at || "unknown"}`));
13108
13249
  } catch {
13109
13250
  }
@@ -13111,6 +13252,26 @@ function registerDoctorCommand(program3) {
13111
13252
  } else {
13112
13253
  console.log(import_chalk16.default.dim(" - no full profile cache (free local engine still works)"));
13113
13254
  }
13255
+ console.log(import_chalk16.default.dim("checking file permissions..."));
13256
+ if (fs14.existsSync(HYV_DIR)) {
13257
+ const hyvMode = fs14.statSync(HYV_DIR).mode & 511;
13258
+ if ((hyvMode & 63) === 0) {
13259
+ console.log(import_chalk16.default.green(" \u2713 .hyv directory permissions"));
13260
+ } else {
13261
+ console.log(import_chalk16.default.yellow(" ! .hyv directory is world/group accessible"));
13262
+ console.log(import_chalk16.default.dim(" run: chmod 700 ~/.hyv"));
13263
+ issues++;
13264
+ }
13265
+ }
13266
+ if (fs14.existsSync(AUTH_FILE)) {
13267
+ if (isOwnerOnlyFile(AUTH_FILE)) {
13268
+ console.log(import_chalk16.default.green(" \u2713 auth.json permissions"));
13269
+ } else {
13270
+ console.log(import_chalk16.default.yellow(" ! auth.json is world/group readable"));
13271
+ console.log(import_chalk16.default.dim(" run: chmod 600 ~/.hyv/auth.json"));
13272
+ issues++;
13273
+ }
13274
+ }
13114
13275
  console.log(import_chalk16.default.dim("checking authentication..."));
13115
13276
  if (isInitialized()) {
13116
13277
  const auth = readAuth();
@@ -13135,9 +13296,9 @@ function registerDoctorCommand(program3) {
13135
13296
  console.log(import_chalk16.default.dim(" run: hyv init for profiles + learning"));
13136
13297
  }
13137
13298
  console.log(import_chalk16.default.dim("checking voice profile..."));
13138
- const voiceMd = path14.join(HYV_DIR, "voice.md");
13139
- const hasVoiceMd = fs13.existsSync(voiceMd) && fs13.readFileSync(voiceMd, "utf-8").trim().length > 50;
13140
- const profileFiles = fs13.existsSync(PROFILES_DIR) ? fs13.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md") && f !== "voice.md") : [];
13299
+ const voiceMd = path15.join(HYV_DIR, "voice.md");
13300
+ const hasVoiceMd = fs14.existsSync(voiceMd) && fs14.readFileSync(voiceMd, "utf-8").trim().length > 50;
13301
+ const profileFiles = fs14.existsSync(PROFILES_DIR) ? fs14.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md") && f !== "voice.md") : [];
13141
13302
  if (hasVoiceMd || profileFiles.length > 0 || diskProfiles.length > 0) {
13142
13303
  if (hasVoiceMd)
13143
13304
  console.log(import_chalk16.default.green(" \u2713 voice.md exists"));
@@ -13150,14 +13311,21 @@ function registerDoctorCommand(program3) {
13150
13311
  console.log(import_chalk16.default.dim(" run: hyv new <name> or hyv init"));
13151
13312
  }
13152
13313
  console.log(import_chalk16.default.dim("checking agent configurations..."));
13314
+ const cursorLegacyRule = path15.join(HOME, ".cursor", "rules", "hyv.md");
13315
+ const cursorRule = path15.join(HOME, ".cursor", "rules", "hyv.mdc");
13316
+ if (fs14.existsSync(cursorLegacyRule) && fs14.existsSync(cursorRule)) {
13317
+ console.log(import_chalk16.default.yellow(" ! stale cursor rule ~/.cursor/rules/hyv.md (use hyv.mdc)"));
13318
+ console.log(import_chalk16.default.dim(" run: rm ~/.cursor/rules/hyv.md (or hyv doctor --fix-agents)"));
13319
+ issues++;
13320
+ }
13153
13321
  const agentChecks = [
13154
- { name: "claude desktop mcp", ok: readMcpHyv(path14.join(claudeDesktopDir(), "claude_desktop_config.json")) },
13155
- { name: "cursor mcp", ok: readMcpHyv(path14.join(HOME, ".cursor", "mcp.json")) },
13156
- { name: "cursor rule", ok: fs13.existsSync(path14.join(HOME, ".cursor", "rules", "hyv.mdc")) },
13157
- { name: "claude code command", ok: fs13.existsSync(path14.join(HOME, ".claude", "commands", "hyv.md")) },
13158
- { name: "claude code skill", ok: fs13.existsSync(path14.join(HOME, ".claude", "skills", "hold-your-voice", "SKILL.md")) },
13159
- { name: "codex agents", ok: fs13.existsSync(path14.join(HOME, ".codex", "AGENTS.md")) && fs13.readFileSync(path14.join(HOME, ".codex", "AGENTS.md"), "utf-8").includes("hyv") },
13160
- { name: "command code skill", ok: fs13.existsSync(path14.join(HOME, ".commandcode", "skills", "hyv", "SKILL.md")) }
13322
+ { name: "claude desktop mcp", ok: readMcpHyv(path15.join(claudeDesktopDir(), "claude_desktop_config.json")) },
13323
+ { name: "cursor mcp", ok: readMcpHyv(path15.join(HOME, ".cursor", "mcp.json")) },
13324
+ { name: "cursor rule", ok: fs14.existsSync(cursorRule) },
13325
+ { name: "claude code command", ok: fs14.existsSync(path15.join(HOME, ".claude", "commands", "hyv.md")) },
13326
+ { name: "claude code skill", ok: fs14.existsSync(path15.join(HOME, ".claude", "skills", "hold-your-voice", "SKILL.md")) },
13327
+ { name: "codex agents", ok: fs14.existsSync(path15.join(HOME, ".codex", "AGENTS.md")) && fs14.readFileSync(path15.join(HOME, ".codex", "AGENTS.md"), "utf-8").includes("hyv") },
13328
+ { name: "command code skill", ok: fs14.existsSync(path15.join(HOME, ".commandcode", "skills", "hyv", "SKILL.md")) }
13161
13329
  ];
13162
13330
  for (const agent of agentChecks) {
13163
13331
  if (agent.ok) {
@@ -13168,8 +13336,8 @@ function registerDoctorCommand(program3) {
13168
13336
  }
13169
13337
  if (opts.fixAgents) {
13170
13338
  try {
13171
- const pkgDir = path14.resolve(__dirname, "..");
13172
- const { setupAgents } = require(path14.join(pkgDir, "scripts", "postinstall-lib.js"));
13339
+ const pkgDir = path15.resolve(__dirname, "..");
13340
+ const { setupAgents } = require(path15.join(pkgDir, "scripts", "postinstall-lib.js"));
13173
13341
  const result = setupAgents({ pkgDir, quiet: true });
13174
13342
  console.log(import_chalk16.default.green(` \u2713 re-ran agent setup (${result.configured.join(", ") || "no changes"})`));
13175
13343
  if (result.warnings.length) {
@@ -13181,8 +13349,8 @@ function registerDoctorCommand(program3) {
13181
13349
  }
13182
13350
  }
13183
13351
  console.log(import_chalk16.default.dim("checking mcp server..."));
13184
- const claudeMcp = readMcpHyv(path14.join(claudeDesktopDir(), "claude_desktop_config.json"));
13185
- const cursorMcp = readMcpHyv(path14.join(HOME, ".cursor", "mcp.json"));
13352
+ const claudeMcp = readMcpHyv(path15.join(claudeDesktopDir(), "claude_desktop_config.json"));
13353
+ const cursorMcp = readMcpHyv(path15.join(HOME, ".cursor", "mcp.json"));
13186
13354
  if (claudeMcp || cursorMcp) {
13187
13355
  if (claudeMcp)
13188
13356
  console.log(import_chalk16.default.green(" \u2713 mcp configured for claude desktop"));
@@ -13192,6 +13360,22 @@ function registerDoctorCommand(program3) {
13192
13360
  console.log(import_chalk16.default.yellow(" ! mcp not configured \u2014 run: hyv doctor --fix-agents or hyv mcp --setup"));
13193
13361
  issues++;
13194
13362
  }
13363
+ try {
13364
+ const stdio = await testMcpStdioSubprocess();
13365
+ if (stdio.ok && (stdio.toolCount || 0) >= 10) {
13366
+ console.log(import_chalk16.default.green(` \u2713 mcp stdio subprocess healthy (${stdio.toolCount} tools)`));
13367
+ } else if (stdio.ok) {
13368
+ console.log(import_chalk16.default.yellow(` ! mcp stdio ok but only ${stdio.toolCount || 0} tools`));
13369
+ issues++;
13370
+ } else {
13371
+ console.log(import_chalk16.default.red(" \u2717 mcp stdio subprocess failed"));
13372
+ console.log(import_chalk16.default.dim(" run: hyv mcp --test"));
13373
+ issues++;
13374
+ }
13375
+ } catch (err) {
13376
+ console.log(import_chalk16.default.red(` \u2717 mcp stdio probe failed: ${err.message}`));
13377
+ issues++;
13378
+ }
13195
13379
  console.log("");
13196
13380
  if (issues === 0 && fixed === 0) {
13197
13381
  console.log(import_chalk16.default.green("\u2713 everything looks good!"));
@@ -13268,15 +13452,51 @@ error: ${error.message}
13268
13452
  }
13269
13453
 
13270
13454
  // src/commands/fix.ts
13271
- var import_chalk18 = __toESM(require_source());
13272
- var fs14 = __toESM(require("fs"));
13273
- var path15 = __toESM(require("path"));
13455
+ var import_chalk19 = __toESM(require_source());
13274
13456
  init_pipeline();
13275
13457
  init_local_profile();
13276
13458
  init_access();
13277
13459
  init_config();
13460
+
13461
+ // src/lib/destructive-write.ts
13462
+ var fs15 = __toESM(require("fs"));
13463
+ var path16 = __toESM(require("path"));
13464
+ var readline = __toESM(require("readline"));
13465
+ var import_chalk18 = __toESM(require_source());
13466
+ async function confirmDestructiveWrite(options) {
13467
+ if (options.yes)
13468
+ return true;
13469
+ const label = options.target ? `${options.action} (${options.target})` : options.action;
13470
+ if (!process.stdin.isTTY) {
13471
+ console.error(import_chalk18.default.red("\nRefusing destructive write without confirmation (non-interactive)."));
13472
+ console.error(import_chalk18.default.dim(" Re-run with --yes to create .bak backups and proceed.\n"));
13473
+ return false;
13474
+ }
13475
+ const question = `
13476
+ ${label}
13477
+ A .bak backup will be created. Proceed? [y/N] `;
13478
+ const answer = await new Promise((resolve14) => {
13479
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
13480
+ rl.question(question, (value) => {
13481
+ rl.close();
13482
+ resolve14(value.trim().toLowerCase());
13483
+ });
13484
+ });
13485
+ return answer === "y" || answer === "yes";
13486
+ }
13487
+ function writeInPlaceWithBackup(filePath, content) {
13488
+ const backupPath = filePath + ".bak";
13489
+ fs15.copyFileSync(filePath, backupPath);
13490
+ fs15.writeFileSync(filePath, content);
13491
+ return backupPath;
13492
+ }
13493
+ function backupBasename(filePath) {
13494
+ return path16.basename(filePath) + ".bak";
13495
+ }
13496
+
13497
+ // src/commands/fix.ts
13278
13498
  function registerFixCommand(program3) {
13279
- program3.command("fix").description("Apply auto-fixes (word swaps, connector removals) without an LLM").argument("<file>", "File to fix (or - for stdin)").option("--dry-run", "Show changes without writing").option("-i, --in-place", "Write fixes back to the file").option("--profile <name>", "Voice profile for never-list checks").option("--ignore <rules>", "Comma-separated rule IDs to skip").option("--format <type>", "Output format (text or json)", "text").action(async (file, options) => {
13499
+ program3.command("fix").description("Apply auto-fixes (word swaps, connector removals) without an LLM").argument("<file>", "File to fix (or - for stdin)").option("--dry-run", "Show changes without writing").option("-i, --in-place", "Write fixes back to the file").option("-y, --yes", "Confirm in-place write without prompting (creates .bak backup)").option("--profile <name>", "Voice profile for never-list checks").option("--ignore <rules>", "Comma-separated rule IDs to skip").option("--format <type>", "Output format (text or json)", "text").action(async (file, options) => {
13280
13500
  try {
13281
13501
  const profile = await loadProfileForCommand(options.profile);
13282
13502
  const { text, path: filePath } = readText(file);
@@ -13285,9 +13505,9 @@ function registerFixCommand(program3) {
13285
13505
  if (options.format === "json") {
13286
13506
  console.log(JSON.stringify({ file: filePath, autoFixes: 0, llmIssues: result.stats.needsLLM, changes: [] }));
13287
13507
  } else {
13288
- console.log(import_chalk18.default.green("\n\u2713 No auto-fixable issues found."));
13508
+ console.log(import_chalk19.default.green("\n\u2713 No auto-fixable issues found."));
13289
13509
  if (result.stats.needsLLM > 0) {
13290
- console.log(import_chalk18.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
13510
+ console.log(import_chalk19.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
13291
13511
  }
13292
13512
  }
13293
13513
  return;
@@ -13301,24 +13521,30 @@ function registerFixCommand(program3) {
13301
13521
  fixed: options.dryRun ? void 0 : result.fixed
13302
13522
  }, null, 2));
13303
13523
  } else {
13304
- console.log(import_chalk18.default.dim(`
13524
+ console.log(import_chalk19.default.dim(`
13305
13525
  hyv fix ${filePath}
13306
13526
  `));
13307
13527
  for (const change of result.changes) {
13308
- console.log(import_chalk18.default.dim(` Line ${change.line}: `) + import_chalk18.default.red(change.before) + import_chalk18.default.dim(" \u2192 ") + import_chalk18.default.green(change.after));
13528
+ console.log(import_chalk19.default.dim(` Line ${change.line}: `) + import_chalk19.default.red(change.before) + import_chalk19.default.dim(" \u2192 ") + import_chalk19.default.green(change.after));
13309
13529
  }
13310
- console.log(import_chalk18.default.green(`
13530
+ console.log(import_chalk19.default.green(`
13311
13531
  \u2713 ${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} applied`));
13312
13532
  if (result.stats.needsLLM > 0) {
13313
- console.log(import_chalk18.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
13533
+ console.log(import_chalk19.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
13314
13534
  }
13315
13535
  }
13316
13536
  if (!options.dryRun) {
13317
13537
  if (options.inPlace && filePath !== "stdin") {
13318
- const backupPath = filePath + ".bak";
13319
- fs14.copyFileSync(filePath, backupPath);
13320
- fs14.writeFileSync(filePath, result.fixed);
13321
- console.log(import_chalk18.default.dim(` Written to ${filePath} (backup: ${path15.basename(backupPath)})`));
13538
+ const confirmed = await confirmDestructiveWrite({
13539
+ yes: options.yes,
13540
+ action: "Apply auto-fixes in-place",
13541
+ target: filePath
13542
+ });
13543
+ if (!confirmed) {
13544
+ process.exit(1);
13545
+ }
13546
+ const backupPath = writeInPlaceWithBackup(filePath, result.fixed);
13547
+ console.log(import_chalk19.default.dim(` Written to ${filePath} (backup: ${backupBasename(filePath)})`));
13322
13548
  saveLastEditSession({
13323
13549
  original_path: filePath,
13324
13550
  edited_path: filePath,
@@ -13326,7 +13552,7 @@ hyv fix ${filePath}
13326
13552
  edited_text: result.fixed,
13327
13553
  profile: options.profile
13328
13554
  });
13329
- console.log(import_chalk18.default.dim(" Tip: hyv reinforce --last to teach your profile from this edit"));
13555
+ console.log(import_chalk19.default.dim(" Tip: hyv reinforce --last to teach your profile from this edit"));
13330
13556
  } else if (filePath === "stdin" || !options.inPlace) {
13331
13557
  process.stdout.write("\n" + result.fixed + "\n");
13332
13558
  if (filePath !== "stdin") {
@@ -13342,14 +13568,14 @@ hyv fix ${filePath}
13342
13568
  }
13343
13569
  await maybeShowLimitedModeHint(!!profile);
13344
13570
  } catch (error) {
13345
- console.error(import_chalk18.default.red(`Error: ${error.message}`));
13571
+ console.error(import_chalk19.default.red(`Error: ${error.message}`));
13346
13572
  process.exit(1);
13347
13573
  }
13348
13574
  });
13349
13575
  }
13350
13576
 
13351
13577
  // src/commands/check.ts
13352
- var import_chalk19 = __toESM(require_source());
13578
+ var import_chalk20 = __toESM(require_source());
13353
13579
  init_pipeline();
13354
13580
  init_local_profile();
13355
13581
  init_access();
@@ -13359,20 +13585,20 @@ function registerCheckCommand(program3) {
13359
13585
  const profile = await loadProfileForCommand(options.profile);
13360
13586
  let inputText = text;
13361
13587
  if (text === "-") {
13362
- const fs25 = require("fs");
13588
+ const fs26 = require("fs");
13363
13589
  if (process.stdin.isTTY) {
13364
- console.error(import_chalk19.default.red("No input provided. Pipe content or pass text as argument."));
13590
+ console.error(import_chalk20.default.red("No input provided. Pipe content or pass text as argument."));
13365
13591
  process.exit(1);
13366
13592
  }
13367
- inputText = fs25.readFileSync(0, "utf-8");
13593
+ inputText = fs26.readFileSync(0, "utf-8");
13368
13594
  }
13369
13595
  if (!inputText.trim()) {
13370
- console.error(import_chalk19.default.red("No text provided."));
13596
+ console.error(import_chalk20.default.red("No text provided."));
13371
13597
  process.exit(1);
13372
13598
  }
13373
- const fs24 = require("fs");
13374
- if (text !== "-" && fs24.existsSync(text)) {
13375
- console.log(import_chalk19.default.yellow(`"${text}" looks like a file. Did you mean: hyv scan ${text}`));
13599
+ const fs25 = require("fs");
13600
+ if (text !== "-" && fs25.existsSync(text)) {
13601
+ console.log(import_chalk20.default.yellow(`"${text}" looks like a file. Did you mean: hyv scan ${text}`));
13376
13602
  process.exit(1);
13377
13603
  }
13378
13604
  const result = runPipeline(inputText, profile, false);
@@ -13396,30 +13622,30 @@ function registerCheckCommand(program3) {
13396
13622
  }, null, 2));
13397
13623
  } else {
13398
13624
  if (result.stats.totalSignals === 0) {
13399
- console.log(import_chalk19.default.green("\n\u2713 Clean \u2014 no AI patterns found."));
13400
- console.log(import_chalk19.default.dim(` score: ${result.score}/100`));
13625
+ console.log(import_chalk20.default.green("\n\u2713 Clean \u2014 no AI patterns found."));
13626
+ console.log(import_chalk20.default.dim(` score: ${result.score}/100`));
13401
13627
  } else {
13402
13628
  console.log("");
13403
13629
  printGroupedSignals(result.signalMap.signals.slice(0, 15), profile);
13404
13630
  if (result.signalMap.signals.length > 15) {
13405
- console.log(import_chalk19.default.dim(` ... and ${result.signalMap.signals.length - 15} more`));
13631
+ console.log(import_chalk20.default.dim(` ... and ${result.signalMap.signals.length - 15} more`));
13406
13632
  }
13407
- console.log(import_chalk19.default.yellow(`
13633
+ console.log(import_chalk20.default.yellow(`
13408
13634
  ${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow)`));
13409
- console.log(import_chalk19.default.dim(` score: ${result.score}/100`));
13635
+ console.log(import_chalk20.default.dim(` score: ${result.score}/100`));
13410
13636
  }
13411
13637
  await maybeShowLimitedModeHint(!!profile);
13412
13638
  }
13413
13639
  process.exit(result.stats.totalSignals > 0 ? 1 : 0);
13414
13640
  } catch (error) {
13415
- console.error(import_chalk19.default.red(`Error: ${error.message}`));
13641
+ console.error(import_chalk20.default.red(`Error: ${error.message}`));
13416
13642
  process.exit(1);
13417
13643
  }
13418
13644
  });
13419
13645
  }
13420
13646
 
13421
13647
  // src/commands/score.ts
13422
- var import_chalk20 = __toESM(require_source());
13648
+ var import_chalk21 = __toESM(require_source());
13423
13649
  init_pipeline();
13424
13650
  init_local_profile();
13425
13651
  function registerScoreCommand(program3) {
@@ -13444,14 +13670,14 @@ function registerScoreCommand(program3) {
13444
13670
  process.exit(1);
13445
13671
  }
13446
13672
  } catch (error) {
13447
- console.error(import_chalk20.default.red(`Error: ${error.message}`));
13673
+ console.error(import_chalk21.default.red(`Error: ${error.message}`));
13448
13674
  process.exit(1);
13449
13675
  }
13450
13676
  });
13451
13677
  }
13452
13678
 
13453
13679
  // src/commands/diff.ts
13454
- var import_chalk21 = __toESM(require_source());
13680
+ var import_chalk22 = __toESM(require_source());
13455
13681
  init_pipeline();
13456
13682
  init_local_profile();
13457
13683
  function registerDiffCommand(program3) {
@@ -13461,9 +13687,9 @@ function registerDiffCommand(program3) {
13461
13687
  const { text, path: filePath } = readText(file);
13462
13688
  const result = runPipeline(text, profile, true);
13463
13689
  if (result.changes.length === 0) {
13464
- console.log(import_chalk21.default.green("\n\u2713 No auto-fixable changes."));
13690
+ console.log(import_chalk22.default.green("\n\u2713 No auto-fixable changes."));
13465
13691
  if (result.stats.needsLLM > 0) {
13466
- console.log(import_chalk21.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite`));
13692
+ console.log(import_chalk22.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite`));
13467
13693
  }
13468
13694
  return;
13469
13695
  }
@@ -13478,42 +13704,42 @@ function registerDiffCommand(program3) {
13478
13704
  const originalLines = text.split("\n");
13479
13705
  const fixedLines = result.fixed.split("\n");
13480
13706
  const contextN = parseInt(options.context, 10);
13481
- console.log(import_chalk21.default.dim(`--- ${filePath}`));
13482
- console.log(import_chalk21.default.dim(`+++ ${filePath} (fixed)`));
13707
+ console.log(import_chalk22.default.dim(`--- ${filePath}`));
13708
+ console.log(import_chalk22.default.dim(`+++ ${filePath} (fixed)`));
13483
13709
  for (const change of result.changes) {
13484
13710
  const lineIdx = change.line - 1;
13485
13711
  const start = Math.max(0, lineIdx - contextN);
13486
13712
  const end = Math.min(originalLines.length, lineIdx + contextN + 1);
13487
- console.log(import_chalk21.default.dim(`@@ -${start + 1},${end - start} +${start + 1},${end - start} @@`));
13713
+ console.log(import_chalk22.default.dim(`@@ -${start + 1},${end - start} +${start + 1},${end - start} @@`));
13488
13714
  for (let i = start; i < end; i++) {
13489
13715
  if (i === lineIdx) {
13490
- console.log(import_chalk21.default.red(`-${originalLines[i]}`));
13491
- console.log(import_chalk21.default.green(`+${fixedLines[i]}`));
13716
+ console.log(import_chalk22.default.red(`-${originalLines[i]}`));
13717
+ console.log(import_chalk22.default.green(`+${fixedLines[i]}`));
13492
13718
  } else {
13493
- console.log(import_chalk21.default.dim(` ${originalLines[i]}`));
13719
+ console.log(import_chalk22.default.dim(` ${originalLines[i]}`));
13494
13720
  }
13495
13721
  }
13496
13722
  }
13497
- console.log(import_chalk21.default.green(`
13723
+ console.log(import_chalk22.default.green(`
13498
13724
  ${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} available`));
13499
- console.log(import_chalk21.default.dim(` run: hyv fix ${file} -i to apply`));
13725
+ console.log(import_chalk22.default.dim(` run: hyv fix ${file} -i to apply`));
13500
13726
  if (options.apply && filePath !== "stdin") {
13501
- const fs24 = require("fs");
13502
- const path23 = require("path");
13727
+ const fs25 = require("fs");
13728
+ const path24 = require("path");
13503
13729
  const backupPath = filePath + ".bak";
13504
- fs24.copyFileSync(filePath, backupPath);
13505
- fs24.writeFileSync(filePath, result.fixed);
13506
- console.log(import_chalk21.default.green(` \u2713 Applied. Backup: ${path23.basename(backupPath)}`));
13730
+ fs25.copyFileSync(filePath, backupPath);
13731
+ fs25.writeFileSync(filePath, result.fixed);
13732
+ console.log(import_chalk22.default.green(` \u2713 Applied. Backup: ${path24.basename(backupPath)}`));
13507
13733
  }
13508
13734
  } catch (error) {
13509
- console.error(import_chalk21.default.red(`Error: ${error.message}`));
13735
+ console.error(import_chalk22.default.red(`Error: ${error.message}`));
13510
13736
  process.exit(1);
13511
13737
  }
13512
13738
  });
13513
13739
  }
13514
13740
 
13515
13741
  // src/commands/rules.ts
13516
- var import_chalk22 = __toESM(require_source());
13742
+ var import_chalk23 = __toESM(require_source());
13517
13743
  init_config();
13518
13744
  var RULE_CATALOG = [
13519
13745
  // AI Overused Words
@@ -13607,7 +13833,7 @@ function registerRulesCommand(program3) {
13607
13833
  for (const id of ids)
13608
13834
  disabledRules.delete(id);
13609
13835
  writeConfig({ ...config, disabled_rules: [...disabledRules] });
13610
- console.log(import_chalk22.default.green(`
13836
+ console.log(import_chalk23.default.green(`
13611
13837
  \u2713 Enabled: ${ids.join(", ")}`));
13612
13838
  return;
13613
13839
  }
@@ -13616,13 +13842,13 @@ function registerRulesCommand(program3) {
13616
13842
  for (const id of ids)
13617
13843
  disabledRules.add(id);
13618
13844
  writeConfig({ ...config, disabled_rules: [...disabledRules] });
13619
- console.log(import_chalk22.default.green(`
13845
+ console.log(import_chalk23.default.green(`
13620
13846
  \u2713 Disabled: ${ids.join(", ")}`));
13621
13847
  return;
13622
13848
  }
13623
13849
  if (options.reset) {
13624
13850
  writeConfig({ ...config, disabled_rules: [] });
13625
- console.log(import_chalk22.default.green("\n\u2713 All rules reset to default (enabled)"));
13851
+ console.log(import_chalk23.default.green("\n\u2713 All rules reset to default (enabled)"));
13626
13852
  return;
13627
13853
  }
13628
13854
  let rules = [...RULE_CATALOG];
@@ -13650,40 +13876,40 @@ function registerRulesCommand(program3) {
13650
13876
  }
13651
13877
  console.log("");
13652
13878
  for (const [cat, catRules] of categories) {
13653
- console.log(import_chalk22.default.bold(` ${cat} rules (${catRules.length})
13879
+ console.log(import_chalk23.default.bold(` ${cat} rules (${catRules.length})
13654
13880
  `));
13655
- console.log(import_chalk22.default.dim(" ID Sev AutoFix Status"));
13656
- console.log(import_chalk22.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
13881
+ console.log(import_chalk23.default.dim(" ID Sev AutoFix Status"));
13882
+ console.log(import_chalk23.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
13657
13883
  for (const rule of catRules) {
13658
- const sev = rule.severity === "red" ? import_chalk22.default.red("red") : import_chalk22.default.yellow("yel");
13659
- const fix = rule.autoFixable ? import_chalk22.default.green(" \u2713") : import_chalk22.default.dim(" \u2717");
13660
- const enabled2 = disabledRules.has(rule.id) ? import_chalk22.default.red(" \u2717 disabled") : import_chalk22.default.green(" \u2713 enabled");
13884
+ const sev = rule.severity === "red" ? import_chalk23.default.red("red") : import_chalk23.default.yellow("yel");
13885
+ const fix = rule.autoFixable ? import_chalk23.default.green(" \u2713") : import_chalk23.default.dim(" \u2717");
13886
+ const enabled2 = disabledRules.has(rule.id) ? import_chalk23.default.red(" \u2717 disabled") : import_chalk23.default.green(" \u2713 enabled");
13661
13887
  const id = rule.id.padEnd(24);
13662
- console.log(` ${import_chalk22.default.dim(id)} ${sev} ${fix} ${enabled2}`);
13888
+ console.log(` ${import_chalk23.default.dim(id)} ${sev} ${fix} ${enabled2}`);
13663
13889
  }
13664
13890
  console.log("");
13665
13891
  }
13666
13892
  const enabled = rules.filter((r) => !disabledRules.has(r.id)).length;
13667
13893
  const disabled = rules.length - enabled;
13668
- console.log(import_chalk22.default.dim(` ${rules.length} rules, ${enabled} enabled, ${disabled} disabled`));
13669
- console.log(import_chalk22.default.dim(` toggle: hyv rules --disable <id>`));
13670
- console.log(import_chalk22.default.dim(` reset: hyv rules --reset
13894
+ console.log(import_chalk23.default.dim(` ${rules.length} rules, ${enabled} enabled, ${disabled} disabled`));
13895
+ console.log(import_chalk23.default.dim(` toggle: hyv rules --disable <id>`));
13896
+ console.log(import_chalk23.default.dim(` reset: hyv rules --reset
13671
13897
  `));
13672
13898
  } catch (error) {
13673
- console.error(import_chalk22.default.red(`Error: ${error.message}`));
13899
+ console.error(import_chalk23.default.red(`Error: ${error.message}`));
13674
13900
  process.exit(1);
13675
13901
  }
13676
13902
  });
13677
13903
  }
13678
13904
 
13679
13905
  // src/commands/batch.ts
13680
- var import_chalk23 = __toESM(require_source());
13681
- var fs15 = __toESM(require("fs"));
13682
- var path16 = __toESM(require("path"));
13906
+ var import_chalk24 = __toESM(require_source());
13907
+ var fs16 = __toESM(require("fs"));
13908
+ var path17 = __toESM(require("path"));
13683
13909
  init_pipeline();
13684
13910
  init_local_profile();
13685
13911
  function registerBatchCommand(program3) {
13686
- program3.command("batch").description("Scan or fix multiple files matching a glob").argument("<pattern>", 'Glob pattern (e.g., "posts/**/*.md")').option("--fix", "Apply auto-fixes (default: scan only)").option("-i, --in-place", "Write fixes back to files").option("-y, --yes", "Confirm destructive in-place fixes without prompting").option("--threshold <n>", "Fail if any file score < threshold").option("--fail-on-hit", "Exit non-zero if any file has issues").option("--sort <field>", "Sort by: issues, score, name", "issues").option("--format <type>", "Output format (text, json, csv)", "text").option("--profile <name>", "Voice profile").option("--ignore <patterns>", "Comma-separated glob ignores").action(async (pattern, options) => {
13912
+ program3.command("batch").description("Scan or fix multiple files matching a glob").argument("<pattern>", 'Glob pattern (e.g., "posts/**/*.md")').option("--fix", "Apply auto-fixes (default: scan only)").option("-i, --in-place", "Write fixes back to files").option("-y, --yes", "Confirm destructive in-place fixes without prompting").option("--threshold <n>", "Fail if any file score < threshold").option("--fail-on-hit", "Exit with code 2 if any file has issues").option("--sort <field>", "Sort by: issues, score, name", "issues").option("--format <type>", "Output format (text, json, csv)", "text").option("--profile <name>", "Voice profile").option("--ignore <patterns>", "Comma-separated glob ignores").action(async (pattern, options) => {
13687
13913
  try {
13688
13914
  const profile = await loadProfileForCommand(options.profile);
13689
13915
  const glob = require_index_min();
@@ -13692,26 +13918,30 @@ function registerBatchCommand(program3) {
13692
13918
  nodir: true
13693
13919
  });
13694
13920
  if (files.length === 0) {
13695
- console.log(import_chalk23.default.yellow(`
13921
+ console.log(import_chalk24.default.yellow(`
13696
13922
  No files matching: ${pattern}`));
13697
13923
  return;
13698
13924
  }
13699
- if (options.fix && options.inPlace && !options.yes) {
13700
- console.error(import_chalk23.default.red("\nRefusing to write fixes in-place without confirmation."));
13701
- console.error(import_chalk23.default.dim(" Re-run with --yes to create .bak backups and apply fixes.\n"));
13702
- process.exit(1);
13925
+ if (options.fix && options.inPlace) {
13926
+ const confirmed = await confirmDestructiveWrite({
13927
+ yes: options.yes,
13928
+ action: `Apply auto-fixes in-place to ${files.length} file(s)`,
13929
+ target: pattern
13930
+ });
13931
+ if (!confirmed)
13932
+ process.exit(1);
13703
13933
  }
13704
13934
  if (options.format === "text") {
13705
- console.log(import_chalk23.default.dim(`
13935
+ console.log(import_chalk24.default.dim(`
13706
13936
  scanning ${files.length} file${files.length === 1 ? "" : "s"}...
13707
13937
  `));
13708
13938
  }
13709
13939
  const results = [];
13710
13940
  for (const file of files) {
13711
- const absPath = path16.resolve(file);
13712
- if (!fs15.existsSync(absPath))
13941
+ const absPath = path17.resolve(file);
13942
+ if (!fs16.existsSync(absPath))
13713
13943
  continue;
13714
- const text = fs15.readFileSync(absPath, "utf-8");
13944
+ const text = fs16.readFileSync(absPath, "utf-8");
13715
13945
  const result = runPipeline(text, profile, options.fix || false);
13716
13946
  results.push({
13717
13947
  file,
@@ -13722,9 +13952,7 @@ No files matching: ${pattern}`));
13722
13952
  autoFixes: result.changes.length
13723
13953
  });
13724
13954
  if (options.fix && options.inPlace && result.changes.length > 0) {
13725
- const backupPath = absPath + ".bak";
13726
- fs15.copyFileSync(absPath, backupPath);
13727
- fs15.writeFileSync(absPath, result.fixed);
13955
+ writeInPlaceWithBackup(absPath, result.fixed);
13728
13956
  }
13729
13957
  }
13730
13958
  if (options.sort === "score") {
@@ -13743,23 +13971,23 @@ No files matching: ${pattern}`));
13743
13971
  }
13744
13972
  } else {
13745
13973
  for (const r of results) {
13746
- const icon = r.issues > 0 ? import_chalk23.default.red("\u25CF") : import_chalk23.default.green("\u25CB");
13747
- const score = r.score < 60 ? import_chalk23.default.red(r.score) : r.score < 80 ? import_chalk23.default.yellow(r.score) : import_chalk23.default.green(r.score);
13748
- const issueStr = r.issues > 0 ? import_chalk23.default.red(`${r.issues} issues`) : import_chalk23.default.green("clean");
13974
+ const icon = r.issues > 0 ? import_chalk24.default.red("\u25CF") : import_chalk24.default.green("\u25CB");
13975
+ const score = r.score < 60 ? import_chalk24.default.red(r.score) : r.score < 80 ? import_chalk24.default.yellow(r.score) : import_chalk24.default.green(r.score);
13976
+ const issueStr = r.issues > 0 ? import_chalk24.default.red(`${r.issues} issues`) : import_chalk24.default.green("clean");
13749
13977
  console.log(` ${icon} ${r.file.padEnd(40)} ${issueStr.padEnd(20)} score: ${score}`);
13750
13978
  }
13751
13979
  const withIssues = results.filter((r) => r.issues > 0).length;
13752
13980
  const totalIssues = results.reduce((sum, r) => sum + r.issues, 0);
13753
13981
  const worst = results.reduce((min, r) => r.score < min.score ? r : min, results[0]);
13754
- console.log(import_chalk23.default.dim(`
13982
+ console.log(import_chalk24.default.dim(`
13755
13983
  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`));
13756
- console.log(import_chalk23.default.dim(` ${results.length} files, ${withIssues} with issues, ${totalIssues} total issues`));
13984
+ console.log(import_chalk24.default.dim(` ${results.length} files, ${withIssues} with issues, ${totalIssues} total issues`));
13757
13985
  if (withIssues > 0) {
13758
- console.log(import_chalk23.default.dim(` worst: ${worst.file} (${worst.score}/100)`));
13986
+ console.log(import_chalk24.default.dim(` worst: ${worst.file} (${worst.score}/100)`));
13759
13987
  }
13760
13988
  if (options.fix && options.inPlace) {
13761
13989
  const fixed = results.filter((r) => r.autoFixes > 0);
13762
- console.log(import_chalk23.default.green(`
13990
+ console.log(import_chalk24.default.green(`
13763
13991
  \u2713 ${fixed.reduce((sum, r) => sum + r.autoFixes, 0)} auto-fixes applied across ${fixed.length} files`));
13764
13992
  }
13765
13993
  }
@@ -13771,108 +13999,114 @@ No files matching: ${pattern}`));
13771
13999
  if (belowThreshold)
13772
14000
  process.exit(1);
13773
14001
  } catch (error) {
13774
- console.error(import_chalk23.default.red(`Error: ${error.message}`));
14002
+ console.error(import_chalk24.default.red(`Error: ${error.message}`));
13775
14003
  process.exit(1);
13776
14004
  }
13777
14005
  });
13778
14006
  }
13779
14007
 
13780
14008
  // src/commands/watch.ts
13781
- var import_chalk24 = __toESM(require_source());
13782
- var fs16 = __toESM(require("fs"));
13783
- var path17 = __toESM(require("path"));
14009
+ var import_chalk25 = __toESM(require_source());
14010
+ var fs17 = __toESM(require("fs"));
14011
+ var path18 = __toESM(require("path"));
13784
14012
  init_pipeline();
13785
14013
  init_local_profile();
13786
14014
  function registerWatchCommand(program3) {
13787
14015
  program3.command("watch").description("Watch a file and re-scan on every save").argument("<file>", "File to watch").option("--command <cmd>", "What to run on change: scan, score, fix", "scan").option("--debounce <ms>", "Delay after save before scanning", "300").option("--notify", "OS notification when issues found").option("-y, --yes", "Confirm destructive auto-fix writes without prompting").option("--profile <name>", "Voice profile").action(async (file, options) => {
13788
14016
  try {
13789
14017
  const profile = await loadProfileForCommand(options.profile);
13790
- const absPath = path17.resolve(file);
13791
- if (!fs16.existsSync(absPath)) {
13792
- console.error(import_chalk24.default.red(`File not found: ${absPath}`));
14018
+ const absPath = path18.resolve(file);
14019
+ if (!fs17.existsSync(absPath)) {
14020
+ console.error(import_chalk25.default.red(`File not found: ${absPath}`));
13793
14021
  process.exit(1);
13794
14022
  }
13795
- if (options.command === "fix" && !options.yes) {
13796
- console.error(import_chalk24.default.red("\nRefusing to auto-fix on save without confirmation."));
13797
- console.error(import_chalk24.default.dim(" Re-run with --yes to create .bak backups before each fix.\n"));
13798
- process.exit(1);
14023
+ if (options.command === "fix") {
14024
+ const confirmed = await confirmDestructiveWrite({
14025
+ yes: options.yes,
14026
+ action: "Auto-fix on every save",
14027
+ target: absPath
14028
+ });
14029
+ if (!confirmed)
14030
+ process.exit(1);
13799
14031
  }
13800
- console.log(import_chalk24.default.dim(`
14032
+ console.log(import_chalk25.default.dim(`
13801
14033
  watching ${absPath}`));
13802
- console.log(import_chalk24.default.dim(`command: ${options.command} debounce: ${options.debounce}ms`));
13803
- console.log(import_chalk24.default.dim("ctrl+c to stop\n"));
14034
+ console.log(import_chalk25.default.dim(`command: ${options.command} debounce: ${options.debounce}ms`));
14035
+ console.log(import_chalk25.default.dim("ctrl+c to stop\n"));
13804
14036
  let debounceTimer = null;
13805
14037
  const debounceMs = parseInt(options.debounce, 10);
13806
14038
  let scanCount = 0;
13807
14039
  let lastScore = 0;
14040
+ let ignoreWatchUntil = 0;
13808
14041
  const runScan = () => {
13809
- const text = fs16.readFileSync(absPath, "utf-8");
14042
+ const text = fs17.readFileSync(absPath, "utf-8");
13810
14043
  const result = runPipeline(text, profile, options.command === "fix");
13811
14044
  scanCount++;
13812
14045
  const now = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
13813
- console.log(import_chalk24.default.dim(`[${now}] saved \u2014 ${options.command}ing...`));
14046
+ console.log(import_chalk25.default.dim(`[${now}] saved \u2014 ${options.command}ing...`));
13814
14047
  if (options.command === "score") {
13815
14048
  const score = result.score;
13816
- const color = score < 60 ? import_chalk24.default.red : score < 80 ? import_chalk24.default.yellow : import_chalk24.default.green;
14049
+ const color = score < 60 ? import_chalk25.default.red : score < 80 ? import_chalk25.default.yellow : import_chalk25.default.green;
13817
14050
  console.log(` ${color(score + "/100")}`);
13818
14051
  if (lastScore > 0 && score !== lastScore) {
13819
14052
  const delta = score - lastScore;
13820
- console.log(import_chalk24.default.dim(` ${delta > 0 ? "\u2191" : "\u2193"} ${Math.abs(delta)} from last scan`));
14053
+ console.log(import_chalk25.default.dim(` ${delta > 0 ? "\u2191" : "\u2193"} ${Math.abs(delta)} from last scan`));
13821
14054
  }
13822
14055
  lastScore = score;
13823
14056
  } else if (options.command === "fix") {
13824
14057
  if (result.changes.length > 0) {
13825
14058
  for (const change of result.changes) {
13826
- console.log(import_chalk24.default.dim(` Line ${change.line}: `) + import_chalk24.default.red(change.before) + import_chalk24.default.dim(" \u2192 ") + import_chalk24.default.green(change.after));
14059
+ console.log(import_chalk25.default.dim(` Line ${change.line}: `) + import_chalk25.default.red(change.before) + import_chalk25.default.dim(" \u2192 ") + import_chalk25.default.green(change.after));
13827
14060
  }
13828
- const backupPath = absPath + ".bak";
13829
- fs16.copyFileSync(absPath, backupPath);
13830
- fs16.writeFileSync(absPath, result.fixed);
13831
- console.log(import_chalk24.default.green(` \u2713 ${result.changes.length} auto-fixes applied`));
14061
+ ignoreWatchUntil = Date.now() + debounceMs + 200;
14062
+ writeInPlaceWithBackup(absPath, result.fixed);
14063
+ console.log(import_chalk25.default.green(` \u2713 ${result.changes.length} auto-fixes applied`));
13832
14064
  } else {
13833
- console.log(import_chalk24.default.green(" \u2713 no auto-fixable issues"));
14065
+ console.log(import_chalk25.default.green(" \u2713 no auto-fixable issues"));
13834
14066
  }
13835
14067
  } else {
13836
14068
  if (result.stats.totalSignals === 0) {
13837
- console.log(import_chalk24.default.green(` \u2713 clean \u2014 score: ${result.score}/100`));
14069
+ console.log(import_chalk25.default.green(` \u2713 clean \u2014 score: ${result.score}/100`));
13838
14070
  } else {
13839
- console.log(import_chalk24.default.yellow(` ${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow) score: ${result.score}/100`));
14071
+ console.log(import_chalk25.default.yellow(` ${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow) score: ${result.score}/100`));
13840
14072
  for (const signal of result.signalMap.signals.slice(0, 3)) {
13841
- const sev = signal.severity === "red" ? import_chalk24.default.red("\u25CF") : import_chalk24.default.yellow("\u25CB");
14073
+ const sev = signal.severity === "red" ? import_chalk25.default.red("\u25CF") : import_chalk25.default.yellow("\u25CB");
13842
14074
  console.log(` ${sev} line ${signal.line}: ${signal.id} \u2014 ${signal.suggestion}`);
13843
14075
  }
13844
14076
  if (result.signalMap.signals.length > 3) {
13845
- console.log(import_chalk24.default.dim(` ... and ${result.signalMap.signals.length - 3} more`));
14077
+ console.log(import_chalk25.default.dim(` ... and ${result.signalMap.signals.length - 3} more`));
13846
14078
  }
13847
14079
  }
13848
14080
  }
13849
14081
  console.log("");
13850
14082
  };
13851
14083
  runScan();
13852
- fs16.watch(absPath, (eventType) => {
14084
+ fs17.watch(absPath, (eventType) => {
13853
14085
  if (eventType !== "change")
13854
14086
  return;
14087
+ if (Date.now() < ignoreWatchUntil)
14088
+ return;
13855
14089
  if (debounceTimer)
13856
14090
  clearTimeout(debounceTimer);
13857
14091
  debounceTimer = setTimeout(runScan, debounceMs);
13858
14092
  });
13859
14093
  process.on("SIGINT", () => {
13860
- console.log(import_chalk24.default.dim(`
14094
+ console.log(import_chalk25.default.dim(`
13861
14095
  ${scanCount} scans completed. exiting.`));
13862
14096
  process.exit(0);
13863
14097
  });
13864
14098
  await new Promise(() => {
13865
14099
  });
13866
14100
  } catch (error) {
13867
- console.error(import_chalk24.default.red(`Error: ${error.message}`));
14101
+ console.error(import_chalk25.default.red(`Error: ${error.message}`));
13868
14102
  process.exit(1);
13869
14103
  }
13870
14104
  });
13871
14105
  }
13872
14106
 
13873
14107
  // src/commands/demo.ts
13874
- var import_chalk25 = __toESM(require_source());
13875
- var fs17 = __toESM(require("fs"));
14108
+ var import_chalk26 = __toESM(require_source());
14109
+ var fs18 = __toESM(require("fs"));
13876
14110
  var SAMPLES = {
13877
14111
  linkedin: {
13878
14112
  text: `In today's fast-paced digital landscape, it's important to note that leveraging the right tools is not just nice to have, but essential for success. Let's delve into the holistic approach that will transform your workflow.
@@ -13962,46 +14196,46 @@ function registerDemoCommand(program3) {
13962
14196
  const style = options.clean ? "clean" : options.style;
13963
14197
  const sample = SAMPLES[style];
13964
14198
  if (!sample) {
13965
- console.error(import_chalk25.default.red(`Unknown style: ${options.style}. Valid: ${Object.keys(SAMPLES).filter((k) => k !== "clean").join(", ")}`));
14199
+ console.error(import_chalk26.default.red(`Unknown style: ${options.style}. Valid: ${Object.keys(SAMPLES).filter((k) => k !== "clean").join(", ")}`));
13966
14200
  process.exit(1);
13967
14201
  }
13968
14202
  if (options.output) {
13969
14203
  const outputPath = require("path").resolve(options.output);
13970
- fs17.writeFileSync(outputPath, sample.text);
13971
- console.log(import_chalk25.default.green(`
14204
+ fs18.writeFileSync(outputPath, sample.text);
14205
+ console.log(import_chalk26.default.green(`
13972
14206
  \u2713 Sample written to ${outputPath}`));
13973
14207
  if (sample.patterns.length > 0) {
13974
- console.log(import_chalk25.default.dim(` Contains ${sample.patterns.length} AI patterns: ${sample.patterns.slice(0, 5).join(", ")}...`));
14208
+ console.log(import_chalk26.default.dim(` Contains ${sample.patterns.length} AI patterns: ${sample.patterns.slice(0, 5).join(", ")}...`));
13975
14209
  }
13976
- console.log(import_chalk25.default.dim(`
14210
+ console.log(import_chalk26.default.dim(`
13977
14211
  Scan it: hyv scan ${options.output}`));
13978
- console.log(import_chalk25.default.dim(` Fix it: hyv fix ${options.output}`));
14212
+ console.log(import_chalk26.default.dim(` Fix it: hyv fix ${options.output}`));
13979
14213
  } else {
13980
- console.log(import_chalk25.default.bold(`
14214
+ console.log(import_chalk26.default.bold(`
13981
14215
  \u2500\u2500\u2500 sample (${style}) \u2500\u2500\u2500
13982
14216
  `));
13983
14217
  console.log(sample.text);
13984
- console.log(import_chalk25.default.dim(`
14218
+ console.log(import_chalk26.default.dim(`
13985
14219
  \u2500\u2500\u2500 end \u2500\u2500\u2500`));
13986
14220
  if (sample.patterns.length > 0) {
13987
- console.log(import_chalk25.default.dim(`
14221
+ console.log(import_chalk26.default.dim(`
13988
14222
  ${sample.patterns.length} AI patterns embedded: ${sample.patterns.slice(0, 8).join(", ")}...`));
13989
14223
  }
13990
- console.log(import_chalk25.default.dim(`
14224
+ console.log(import_chalk26.default.dim(`
13991
14225
  Save to file: hyv demo --output demo.md`));
13992
- console.log(import_chalk25.default.dim(` Scan it: hyv scan demo.md`));
13993
- console.log(import_chalk25.default.dim(` Fix it: hyv fix demo.md
14226
+ console.log(import_chalk26.default.dim(` Scan it: hyv scan demo.md`));
14227
+ console.log(import_chalk26.default.dim(` Fix it: hyv fix demo.md
13994
14228
  `));
13995
14229
  }
13996
14230
  } catch (error) {
13997
- console.error(import_chalk25.default.red(`Error: ${error.message}`));
14231
+ console.error(import_chalk26.default.red(`Error: ${error.message}`));
13998
14232
  process.exit(1);
13999
14233
  }
14000
14234
  });
14001
14235
  }
14002
14236
 
14003
14237
  // src/commands/open.ts
14004
- var import_chalk26 = __toESM(require_source());
14238
+ var import_chalk27 = __toESM(require_source());
14005
14239
  var PAGES = {
14006
14240
  dashboard: "https://holdyourvoice.com/dashboard",
14007
14241
  profiles: "https://holdyourvoice.com/dashboard",
@@ -14018,12 +14252,12 @@ function registerOpenCommand(program3) {
14018
14252
  return;
14019
14253
  }
14020
14254
  const open3 = (await Promise.resolve().then(() => __toESM(require_open()))).default;
14021
- console.log(import_chalk26.default.dim(`
14255
+ console.log(import_chalk27.default.dim(`
14022
14256
  Opening ${url}...`));
14023
14257
  await open3(url);
14024
- console.log(import_chalk26.default.green(" \u2713 Opened in browser\n"));
14258
+ console.log(import_chalk27.default.green(" \u2713 Opened in browser\n"));
14025
14259
  } catch (error) {
14026
- console.error(import_chalk26.default.red(`Error: ${error.message}`));
14260
+ console.error(import_chalk27.default.red(`Error: ${error.message}`));
14027
14261
  process.exit(1);
14028
14262
  }
14029
14263
  });
@@ -14033,35 +14267,35 @@ function registerOpenCommand(program3) {
14033
14267
  init_welcome();
14034
14268
 
14035
14269
  // src/lib/onboarding.ts
14036
- var fs18 = __toESM(require("fs"));
14037
- var path18 = __toESM(require("path"));
14270
+ var fs19 = __toESM(require("fs"));
14271
+ var path19 = __toESM(require("path"));
14038
14272
  var os6 = __toESM(require("os"));
14039
- var hyvDir = path18.join(os6.homedir(), ".hyv");
14040
- var onboardingFile = path18.join(hyvDir, "onboarding-complete.json");
14273
+ var hyvDir = path19.join(os6.homedir(), ".hyv");
14274
+ var onboardingFile = path19.join(hyvDir, "onboarding-complete.json");
14041
14275
  function hasCompletedOnboarding() {
14042
14276
  try {
14043
- return fs18.existsSync(onboardingFile);
14277
+ return fs19.existsSync(onboardingFile);
14044
14278
  } catch {
14045
14279
  return false;
14046
14280
  }
14047
14281
  }
14048
14282
  function markOnboardingComplete(version) {
14049
- if (!fs18.existsSync(hyvDir))
14050
- fs18.mkdirSync(hyvDir, { recursive: true });
14051
- fs18.writeFileSync(
14283
+ if (!fs19.existsSync(hyvDir))
14284
+ fs19.mkdirSync(hyvDir, { recursive: true });
14285
+ fs19.writeFileSync(
14052
14286
  onboardingFile,
14053
14287
  JSON.stringify({ version, completed_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
14054
14288
  );
14055
14289
  }
14056
14290
 
14057
14291
  // src/commands/welcome.ts
14058
- var fs19 = __toESM(require("fs"));
14059
- var path19 = __toESM(require("path"));
14292
+ var fs20 = __toESM(require("fs"));
14293
+ var path20 = __toESM(require("path"));
14060
14294
  function registerWelcomeCommand(program3) {
14061
14295
  const pkgVersion3 = (() => {
14062
14296
  try {
14063
- const pkgPath3 = path19.resolve(__dirname, "..", "package.json");
14064
- return JSON.parse(fs19.readFileSync(pkgPath3, "utf-8")).version;
14297
+ const pkgPath3 = path20.resolve(__dirname, "..", "package.json");
14298
+ return JSON.parse(fs20.readFileSync(pkgPath3, "utf-8")).version;
14065
14299
  } catch {
14066
14300
  return "0.0.0";
14067
14301
  }
@@ -14085,7 +14319,7 @@ function getWelcomeText() {
14085
14319
  }
14086
14320
 
14087
14321
  // src/commands/content.ts
14088
- var import_chalk27 = __toESM(require_source());
14322
+ var import_chalk28 = __toESM(require_source());
14089
14323
  init_free_paid();
14090
14324
  var TEMPLATES = {
14091
14325
  cursor: {
@@ -14159,68 +14393,68 @@ ${COMMUNITY_URL}`
14159
14393
  function registerContentCommand(program3) {
14160
14394
  program3.command("content").description("Blog outlines, CI snippets, and share templates").argument("[topic]", "cursor | agents | ci | share", "cursor").option("--list", "List available templates").action((topic, opts) => {
14161
14395
  if (opts.list) {
14162
- console.log(import_chalk27.default.bold("\nhyv content templates\n"));
14396
+ console.log(import_chalk28.default.bold("\nhyv content templates\n"));
14163
14397
  for (const [key, t2] of Object.entries(TEMPLATES)) {
14164
- console.log(import_chalk27.default.dim(` ${key}`) + ` \u2014 ${t2.title}`);
14398
+ console.log(import_chalk28.default.dim(` ${key}`) + ` \u2014 ${t2.title}`);
14165
14399
  }
14166
14400
  console.log("");
14167
14401
  return;
14168
14402
  }
14169
14403
  const t = TEMPLATES[topic] || TEMPLATES.cursor;
14170
- console.log(import_chalk27.default.bold(`
14404
+ console.log(import_chalk28.default.bold(`
14171
14405
  ${t.title}
14172
14406
  `));
14173
14407
  console.log(t.body);
14174
- console.log(import_chalk27.default.dim("\nBlog: https://holdyourvoice.com/blog\n"));
14408
+ console.log(import_chalk28.default.dim("\nBlog: https://holdyourvoice.com/blog\n"));
14175
14409
  });
14176
14410
  }
14177
14411
 
14178
14412
  // src/commands/upgrade.ts
14179
- var import_chalk28 = __toESM(require_source());
14180
- var import_child_process2 = require("child_process");
14413
+ var import_chalk29 = __toESM(require_source());
14414
+ var import_child_process3 = require("child_process");
14181
14415
  init_version();
14182
14416
  function registerUpgradeCommand(program3) {
14183
14417
  program3.command("upgrade").description("Upgrade to the latest @holdyourvoice/hyv").option("--check", "Only check if an update is available").action(async (opts) => {
14184
14418
  const current = getCliVersion();
14185
- console.log(import_chalk28.default.dim(`
14419
+ console.log(import_chalk29.default.dim(`
14186
14420
  Current: ${getEngineLabel()}
14187
14421
  `));
14188
14422
  let latest = current;
14189
14423
  try {
14190
- const out = (0, import_child_process2.execSync)("npm view @holdyourvoice/hyv version", { encoding: "utf-8", timeout: 15e3 });
14424
+ const out = (0, import_child_process3.execSync)("npm view @holdyourvoice/hyv version", { encoding: "utf-8", timeout: 15e3 });
14191
14425
  latest = out.trim();
14192
14426
  } catch {
14193
- console.log(import_chalk28.default.yellow("Could not reach npm registry. Try: npm i -g @holdyourvoice/hyv@latest"));
14427
+ console.log(import_chalk29.default.yellow("Could not reach npm registry. Try: npm i -g @holdyourvoice/hyv@latest"));
14194
14428
  return;
14195
14429
  }
14196
14430
  const cmp = compareSemver(current, latest);
14197
14431
  if (cmp === 0) {
14198
- console.log(import_chalk28.default.green("\u2713 You are on the latest version."));
14432
+ console.log(import_chalk29.default.green("\u2713 You are on the latest version."));
14199
14433
  return;
14200
14434
  }
14201
14435
  if (cmp > 0) {
14202
- console.log(import_chalk28.default.green(`\u2713 You are ahead of npm (published: ${latest}).`));
14436
+ console.log(import_chalk29.default.green(`\u2713 You are ahead of npm (published: ${latest}).`));
14203
14437
  return;
14204
14438
  }
14205
- console.log(import_chalk28.default.cyan(`Update available: ${current} \u2192 ${latest}`));
14439
+ console.log(import_chalk29.default.cyan(`Update available: ${current} \u2192 ${latest}`));
14206
14440
  if (opts.check) {
14207
- console.log(import_chalk28.default.dim("\nRun: hyv upgrade or npm i -g @holdyourvoice/hyv@latest\n"));
14441
+ console.log(import_chalk29.default.dim("\nRun: hyv upgrade or npm i -g @holdyourvoice/hyv@latest\n"));
14208
14442
  return;
14209
14443
  }
14210
- console.log(import_chalk28.default.dim("Installing..."));
14444
+ console.log(import_chalk29.default.dim("Installing..."));
14211
14445
  try {
14212
- (0, import_child_process2.execSync)("npm i -g @holdyourvoice/hyv@latest", { stdio: "inherit" });
14213
- console.log(import_chalk28.default.green("\n\u2713 Upgraded successfully. Restart your terminal.\n"));
14446
+ (0, import_child_process3.execSync)("npm i -g @holdyourvoice/hyv@latest", { stdio: "inherit" });
14447
+ console.log(import_chalk29.default.green("\n\u2713 Upgraded successfully. Restart your terminal.\n"));
14214
14448
  } catch {
14215
- console.log(import_chalk28.default.red("\nUpgrade failed. Run manually: npm i -g @holdyourvoice/hyv@latest\n"));
14449
+ console.log(import_chalk29.default.red("\nUpgrade failed. Run manually: npm i -g @holdyourvoice/hyv@latest\n"));
14216
14450
  process.exit(1);
14217
14451
  }
14218
14452
  });
14219
14453
  }
14220
14454
 
14221
14455
  // src/mcp.ts
14222
- var fs20 = __toESM(require("fs"));
14223
- var path20 = __toESM(require("path"));
14456
+ var fs21 = __toESM(require("fs"));
14457
+ var path21 = __toESM(require("path"));
14224
14458
  var os7 = __toESM(require("os"));
14225
14459
  init_classifier();
14226
14460
  init_autofix();
@@ -14232,20 +14466,20 @@ init_welcome();
14232
14466
  init_config();
14233
14467
  init_telemetry();
14234
14468
  init_access();
14235
- var VOICE_MD = path20.join(os7.homedir(), ".hyv", "voice.md");
14469
+ var VOICE_MD = path21.join(os7.homedir(), ".hyv", "voice.md");
14236
14470
  var MAX_RESPONSE_CHARS = 12e4;
14237
14471
  var MAX_SCAN_FILE_BYTES = 2 * 1024 * 1024;
14238
14472
  function readScanFile(filePath) {
14239
- const cwdReal = fs20.realpathSync(process.cwd());
14240
- const resolved = fs20.realpathSync(path20.resolve(filePath));
14241
- if (!resolved.startsWith(cwdReal + path20.sep) && resolved !== cwdReal) {
14473
+ const cwdReal = fs21.realpathSync(process.cwd());
14474
+ const resolved = fs21.realpathSync(path21.resolve(filePath));
14475
+ if (!resolved.startsWith(cwdReal + path21.sep) && resolved !== cwdReal) {
14242
14476
  throw new Error(`file must be in the current working directory (${cwdReal})`);
14243
14477
  }
14244
- const stat = fs20.statSync(resolved);
14478
+ const stat = fs21.statSync(resolved);
14245
14479
  if (stat.size > MAX_SCAN_FILE_BYTES) {
14246
14480
  throw new Error(`file too large (max ${MAX_SCAN_FILE_BYTES} bytes)`);
14247
14481
  }
14248
- return fs20.readFileSync(resolved, "utf-8");
14482
+ return fs21.readFileSync(resolved, "utf-8");
14249
14483
  }
14250
14484
  function toolResultPayload(result) {
14251
14485
  const isError = result.startsWith("Error:") || result.startsWith("Unknown tool:");
@@ -14254,10 +14488,10 @@ function toolResultPayload(result) {
14254
14488
  ...isError ? { isError: true } : {}
14255
14489
  };
14256
14490
  }
14257
- var pkgPath = path20.resolve(__dirname, "..", "package.json");
14491
+ var pkgPath = path21.resolve(__dirname, "..", "package.json");
14258
14492
  var pkgVersion = (() => {
14259
14493
  try {
14260
- return JSON.parse(fs20.readFileSync(pkgPath, "utf-8")).version;
14494
+ return JSON.parse(fs21.readFileSync(pkgPath, "utf-8")).version;
14261
14495
  } catch {
14262
14496
  return "2.7.1";
14263
14497
  }
@@ -14831,7 +15065,7 @@ async function handleRequest(line) {
14831
15065
  }
14832
15066
  async function startMcpServer() {
14833
15067
  const access = await getAccessState().catch(() => null);
14834
- if (fs20.existsSync(VOICE_MD)) {
15068
+ if (fs21.existsSync(VOICE_MD)) {
14835
15069
  mcpLog("info", `voice profile: ${VOICE_MD}`);
14836
15070
  } else {
14837
15071
  mcpLog("info", "free local engine ready \u2014 no voice profile yet");
@@ -14863,228 +15097,142 @@ async function startMcpServer() {
14863
15097
  }
14864
15098
 
14865
15099
  // src/lib/mcp-setup.ts
14866
- var import_chalk29 = __toESM(require_source());
14867
- var fs21 = __toESM(require("fs"));
14868
- var path21 = __toESM(require("path"));
15100
+ var import_chalk30 = __toESM(require_source());
15101
+ var fs22 = __toESM(require("fs"));
15102
+ var path22 = __toESM(require("path"));
14869
15103
  var os8 = __toESM(require("os"));
14870
- var import_child_process3 = require("child_process");
14871
15104
  init_version();
14872
15105
  var HOME2 = os8.homedir();
14873
15106
  var IS_WIN2 = process.platform === "win32";
14874
15107
  function claudeDesktopConfigPath() {
14875
15108
  if (IS_WIN2)
14876
- return path21.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
15109
+ return path22.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
14877
15110
  if (process.platform === "linux")
14878
- return path21.join(HOME2, ".config", "Claude", "claude_desktop_config.json");
14879
- return path21.join(HOME2, "Library", "Application Support", "Claude", "claude_desktop_config.json");
14880
- }
14881
- function mcpSnippet() {
14882
- const entry = [
14883
- path21.resolve(process.argv[1] || ""),
14884
- path21.resolve(__dirname, "index.js"),
14885
- path21.resolve(__dirname, "..", "dist", "index.js")
14886
- ].find((p) => p && fs21.existsSync(p));
14887
- if (entry) {
14888
- return JSON.stringify({ command: process.execPath, args: [entry, "mcp"] }, null, 2);
14889
- }
14890
- return JSON.stringify({ command: "hyv", args: ["mcp"] }, null, 2);
15111
+ return path22.join(HOME2, ".config", "Claude", "claude_desktop_config.json");
15112
+ return path22.join(HOME2, "Library", "Application Support", "Claude", "claude_desktop_config.json");
14891
15113
  }
14892
15114
  function printMcpSetup() {
14893
- console.log(import_chalk29.default.bold("\nhold your voice \u2014 mcp setup\n"));
14894
- console.log(import_chalk29.default.dim(` ${getEngineLabel()}
15115
+ console.log(import_chalk30.default.bold("\nhold your voice \u2014 mcp setup\n"));
15116
+ console.log(import_chalk30.default.dim(` ${getEngineLabel()}
14895
15117
  `));
14896
- console.log(import_chalk29.default.bold("Claude Desktop"));
14897
- console.log(import_chalk29.default.dim(" Add to claude_desktop_config.json \u2192 mcpServers.hyv:"));
14898
- console.log(import_chalk29.default.cyan(` ${mcpSnippet().split("\n").join("\n ")}`));
14899
- console.log(import_chalk29.default.dim(` Config: ${claudeDesktopConfigPath()}
15118
+ console.log(import_chalk30.default.bold("Claude Desktop"));
15119
+ console.log(import_chalk30.default.dim(" Add to claude_desktop_config.json \u2192 mcpServers.hyv:"));
15120
+ console.log(import_chalk30.default.cyan(` ${mcpServerSnippet().split("\n").join("\n ")}`));
15121
+ console.log(import_chalk30.default.dim(` Config: ${claudeDesktopConfigPath()}
14900
15122
  `));
14901
- console.log(import_chalk29.default.bold("Cursor"));
14902
- console.log(import_chalk29.default.dim(" MCP: ~/.cursor/mcp.json \u2192 mcpServers.hyv (same JSON as above)"));
14903
- console.log(import_chalk29.default.dim(" Rule: ~/.cursor/rules/hyv.mdc (alwaysApply \u2014 auto via postinstall)\n"));
14904
- console.log(import_chalk29.default.bold("Claude Code"));
14905
- console.log(import_chalk29.default.dim(" Command: ~/.claude/commands/hyv.md"));
14906
- console.log(import_chalk29.default.dim(" Skill: ~/.claude/skills/hold-your-voice/SKILL.md\n"));
14907
- console.log(import_chalk29.default.bold("Windsurf"));
14908
- console.log(import_chalk29.default.dim(" Rule: ~/.windsurf/rules/hyv.md (trigger: always_on)\n"));
14909
- console.log(import_chalk29.default.bold("Codex"));
14910
- console.log(import_chalk29.default.dim(" Instructions: ~/.codex/AGENTS.md (merged on install)\n"));
14911
- console.log(import_chalk29.default.bold("Command Code"));
14912
- console.log(import_chalk29.default.dim(" Skill: ~/.commandcode/skills/hyv/SKILL.md\n"));
14913
- console.log(import_chalk29.default.bold("ChatGPT"));
14914
- console.log(import_chalk29.default.dim(" hyv mcp --setup-chatgpt\n"));
14915
- console.log(import_chalk29.default.bold("Auto-configure"));
14916
- console.log(import_chalk29.default.dim(" hyv doctor --fix-agents"));
14917
- console.log(import_chalk29.default.dim(" HYV_AUTO_CONFIGURE_AGENTS=0 npm i -g @holdyourvoice/hyv (skip)\n"));
14918
- console.log(import_chalk29.default.bold("Verify"));
14919
- console.log(import_chalk29.default.dim(" hyv mcp --test"));
14920
- console.log(import_chalk29.default.dim(" HYV_TELEMETRY=1 hyv mcp (optional usage logging to ~/.hyv/telemetry/)\n"));
14921
- }
14922
- async function testMcpStdioSubprocess() {
14923
- const candidates = [
14924
- path21.resolve(process.argv[1] || ""),
14925
- path21.resolve(__dirname, "index.js"),
14926
- path21.resolve(__dirname, "..", "dist", "index.js")
14927
- ];
14928
- const entry = candidates.find((p) => p && fs21.existsSync(p));
14929
- if (!entry)
14930
- return { ok: false };
14931
- return new Promise((resolve14) => {
14932
- const child = (0, import_child_process3.spawn)(process.execPath, [entry, "mcp"], {
14933
- stdio: ["pipe", "pipe", "pipe"],
14934
- env: { ...process.env, HYV_POSTINSTALL_QUIET: "1" }
14935
- });
14936
- let buffer = "";
14937
- let settled = false;
14938
- const finish = (ok, toolCount) => {
14939
- if (settled)
14940
- return;
14941
- settled = true;
14942
- clearTimeout(timeout);
14943
- try {
14944
- child.kill();
14945
- } catch {
14946
- }
14947
- resolve14({ ok, toolCount });
14948
- };
14949
- const timeout = setTimeout(() => finish(false), 1e4);
14950
- const handleLine = (line) => {
14951
- const trimmed = line.trim();
14952
- if (!trimmed.startsWith("{"))
14953
- return;
14954
- try {
14955
- const msg = JSON.parse(trimmed);
14956
- if (msg.id === 2 && Array.isArray(msg.result?.tools) && msg.result.tools.length > 0) {
14957
- finish(true, msg.result.tools.length);
14958
- }
14959
- } catch {
14960
- }
14961
- };
14962
- child.stdout.on("data", (chunk) => {
14963
- buffer += chunk.toString();
14964
- const lines = buffer.split("\n");
14965
- buffer = lines.pop() || "";
14966
- for (const line of lines)
14967
- handleLine(line);
14968
- });
14969
- child.on("error", () => finish(false));
14970
- child.on("exit", () => {
14971
- if (!settled) {
14972
- handleLine(buffer);
14973
- finish(buffer.includes("hyv_scan"));
14974
- }
14975
- });
14976
- setTimeout(() => {
14977
- const send2 = (payload) => {
14978
- child.stdin.write(`${JSON.stringify(payload)}
14979
- `);
14980
- };
14981
- send2({
14982
- jsonrpc: "2.0",
14983
- id: 1,
14984
- method: "initialize",
14985
- params: {
14986
- protocolVersion: "2024-11-05",
14987
- capabilities: {},
14988
- clientInfo: { name: "hyv-self-test", version: "1.0" }
14989
- }
14990
- });
14991
- send2({ jsonrpc: "2.0", method: "notifications/initialized" });
14992
- send2({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} });
14993
- }, 500);
14994
- });
15123
+ console.log(import_chalk30.default.bold("Cursor"));
15124
+ console.log(import_chalk30.default.dim(" MCP: ~/.cursor/mcp.json \u2192 mcpServers.hyv (same JSON as above)"));
15125
+ console.log(import_chalk30.default.dim(" Rule: ~/.cursor/rules/hyv.mdc (alwaysApply \u2014 auto via postinstall)\n"));
15126
+ console.log(import_chalk30.default.bold("Claude Code"));
15127
+ console.log(import_chalk30.default.dim(" Command: ~/.claude/commands/hyv.md"));
15128
+ console.log(import_chalk30.default.dim(" Skill: ~/.claude/skills/hold-your-voice/SKILL.md\n"));
15129
+ console.log(import_chalk30.default.bold("Windsurf"));
15130
+ console.log(import_chalk30.default.dim(" Rule: ~/.windsurf/rules/hyv.md (trigger: always_on)\n"));
15131
+ console.log(import_chalk30.default.bold("Codex"));
15132
+ console.log(import_chalk30.default.dim(" Instructions: ~/.codex/AGENTS.md (merged on install)\n"));
15133
+ console.log(import_chalk30.default.bold("Command Code"));
15134
+ console.log(import_chalk30.default.dim(" Skill: ~/.commandcode/skills/hyv/SKILL.md\n"));
15135
+ console.log(import_chalk30.default.bold("ChatGPT"));
15136
+ console.log(import_chalk30.default.dim(" hyv mcp --setup-chatgpt\n"));
15137
+ console.log(import_chalk30.default.bold("Auto-configure"));
15138
+ console.log(import_chalk30.default.dim(" hyv doctor --fix-agents"));
15139
+ console.log(import_chalk30.default.dim(" HYV_AUTO_CONFIGURE_AGENTS=0 npm i -g @holdyourvoice/hyv (skip)\n"));
15140
+ console.log(import_chalk30.default.bold("Verify"));
15141
+ console.log(import_chalk30.default.dim(" hyv mcp --test"));
15142
+ console.log(import_chalk30.default.dim(" HYV_TELEMETRY=1 hyv mcp (optional usage logging to ~/.hyv/telemetry/)\n"));
14995
15143
  }
14996
15144
  async function runMcpSelfTest() {
14997
- console.log(import_chalk29.default.bold("\nhold your voice \u2014 mcp self-test\n"));
14998
- console.log(import_chalk29.default.dim(` ${getEngineLabel()}
15145
+ console.log(import_chalk30.default.bold("\nhold your voice \u2014 mcp self-test\n"));
15146
+ console.log(import_chalk30.default.dim(` ${getEngineLabel()}
14999
15147
  `));
15000
15148
  let passed = 0;
15001
15149
  let failed = 0;
15002
15150
  const tools = getMcpToolNames();
15003
15151
  if (tools.length >= 10) {
15004
- console.log(import_chalk29.default.green(` \u2713 ${tools.length} MCP tools registered`));
15152
+ console.log(import_chalk30.default.green(` \u2713 ${tools.length} MCP tools registered`));
15005
15153
  passed++;
15006
15154
  } else {
15007
- console.log(import_chalk29.default.red(` \u2717 expected 10+ tools, got ${tools.length}`));
15155
+ console.log(import_chalk30.default.red(` \u2717 expected 10+ tools, got ${tools.length}`));
15008
15156
  failed++;
15009
15157
  }
15010
15158
  const required = ["hyv_welcome", "hyv_scan", "hyv_analyze", "hyv_clean", "hyv_validate", "hyv_list_free_tools"];
15011
15159
  for (const name of required) {
15012
15160
  if (tools.includes(name)) {
15013
- console.log(import_chalk29.default.green(` \u2713 tool: ${name}`));
15161
+ console.log(import_chalk30.default.green(` \u2713 tool: ${name}`));
15014
15162
  passed++;
15015
15163
  } else {
15016
- console.log(import_chalk29.default.red(` \u2717 missing tool: ${name}`));
15164
+ console.log(import_chalk30.default.red(` \u2717 missing tool: ${name}`));
15017
15165
  failed++;
15018
15166
  }
15019
15167
  }
15020
15168
  try {
15021
15169
  const welcome = await invokeMcpTool("hyv_welcome", {});
15022
15170
  if (welcome.includes("Hold Your Voice") || welcome.includes("hold your voice")) {
15023
- console.log(import_chalk29.default.green(" \u2713 hyv_welcome responds"));
15171
+ console.log(import_chalk30.default.green(" \u2713 hyv_welcome responds"));
15024
15172
  passed++;
15025
15173
  } else {
15026
- console.log(import_chalk29.default.red(" \u2717 hyv_welcome unexpected output"));
15174
+ console.log(import_chalk30.default.red(" \u2717 hyv_welcome unexpected output"));
15027
15175
  failed++;
15028
15176
  }
15029
15177
  } catch (e) {
15030
- console.log(import_chalk29.default.red(` \u2717 hyv_welcome failed: ${e.message}`));
15178
+ console.log(import_chalk30.default.red(` \u2717 hyv_welcome failed: ${e.message}`));
15031
15179
  failed++;
15032
15180
  }
15033
15181
  try {
15034
15182
  const demo = await invokeMcpTool("hyv_demo", {});
15035
15183
  if (demo.includes("Score") || demo.includes("issues")) {
15036
- console.log(import_chalk29.default.green(" \u2713 hyv_demo pipeline works"));
15184
+ console.log(import_chalk30.default.green(" \u2713 hyv_demo pipeline works"));
15037
15185
  passed++;
15038
15186
  } else {
15039
- console.log(import_chalk29.default.red(" \u2717 hyv_demo unexpected output"));
15187
+ console.log(import_chalk30.default.red(" \u2717 hyv_demo unexpected output"));
15040
15188
  failed++;
15041
15189
  }
15042
15190
  } catch (e) {
15043
- console.log(import_chalk29.default.red(` \u2717 hyv_demo failed: ${e.message}`));
15191
+ console.log(import_chalk30.default.red(` \u2717 hyv_demo failed: ${e.message}`));
15044
15192
  failed++;
15045
15193
  }
15046
- const voiceMd = path21.join(HOME2, ".hyv", "voice.md");
15047
- if (fs21.existsSync(voiceMd)) {
15048
- console.log(import_chalk29.default.green(" \u2713 voice profile found"));
15194
+ const voiceMd = path22.join(HOME2, ".hyv", "voice.md");
15195
+ if (fs22.existsSync(voiceMd)) {
15196
+ console.log(import_chalk30.default.green(" \u2713 voice profile found"));
15049
15197
  passed++;
15050
15198
  } else {
15051
- console.log(import_chalk29.default.dim(" - no voice.md (free local engine still works)"));
15199
+ console.log(import_chalk30.default.dim(" - no voice.md (free local engine still works)"));
15052
15200
  }
15053
15201
  if (getDemoText().length > 100) {
15054
- console.log(import_chalk29.default.green(" \u2713 demo content available"));
15202
+ console.log(import_chalk30.default.green(" \u2713 demo content available"));
15055
15203
  passed++;
15056
15204
  } else {
15057
- console.log(import_chalk29.default.red(" \u2717 demo content missing"));
15205
+ console.log(import_chalk30.default.red(" \u2717 demo content missing"));
15058
15206
  failed++;
15059
15207
  }
15060
15208
  try {
15061
15209
  const stdio = await testMcpStdioSubprocess();
15062
15210
  if (stdio.ok && (stdio.toolCount || 0) >= 10) {
15063
- console.log(import_chalk29.default.green(` \u2713 stdio MCP subprocess responds (${stdio.toolCount} tools)`));
15211
+ console.log(import_chalk30.default.green(` \u2713 stdio MCP subprocess responds (${stdio.toolCount} tools)`));
15064
15212
  passed++;
15065
15213
  } else if (stdio.ok) {
15066
- console.log(import_chalk29.default.yellow(` ! stdio MCP subprocess ok but only ${stdio.toolCount || 0} tools`));
15214
+ console.log(import_chalk30.default.yellow(` ! stdio MCP subprocess ok but only ${stdio.toolCount || 0} tools`));
15067
15215
  failed++;
15068
15216
  } else {
15069
- console.log(import_chalk29.default.red(" \u2717 stdio MCP subprocess failed"));
15217
+ console.log(import_chalk30.default.red(" \u2717 stdio MCP subprocess failed"));
15070
15218
  failed++;
15071
15219
  }
15072
15220
  } catch (e) {
15073
- console.log(import_chalk29.default.red(` \u2717 stdio MCP subprocess failed: ${e.message}`));
15221
+ console.log(import_chalk30.default.red(` \u2717 stdio MCP subprocess failed: ${e.message}`));
15074
15222
  failed++;
15075
15223
  }
15076
15224
  console.log("");
15077
15225
  if (failed === 0) {
15078
- console.log(import_chalk29.default.green(`\u2713 all checks passed (${passed})`));
15079
- console.log(import_chalk29.default.dim("\nStart server: hyv mcp\n"));
15226
+ console.log(import_chalk30.default.green(`\u2713 all checks passed (${passed})`));
15227
+ console.log(import_chalk30.default.dim("\nStart server: hyv mcp\n"));
15080
15228
  return true;
15081
15229
  }
15082
- console.log(import_chalk29.default.yellow(`! ${failed} check(s) failed, ${passed} passed`));
15230
+ console.log(import_chalk30.default.yellow(`! ${failed} check(s) failed, ${passed} passed`));
15083
15231
  return false;
15084
15232
  }
15085
15233
 
15086
15234
  // src/commands/export.ts
15087
- var fs22 = __toESM(require("fs"));
15235
+ var fs23 = __toESM(require("fs"));
15088
15236
  init_api();
15089
15237
  var FORMATS = {
15090
15238
  claude: {
@@ -15171,7 +15319,7 @@ async function exportCommand(format, opts) {
15171
15319
  const prompt = fmt.wrap(detail.profile, detail.body);
15172
15320
  if (opts.output) {
15173
15321
  const outFile = opts.output.replace("{name}", profile.slug || profile.name);
15174
- fs22.writeFileSync(outFile, prompt);
15322
+ fs23.writeFileSync(outFile, prompt);
15175
15323
  results.push({ profile: profile.name, file: outFile });
15176
15324
  } else {
15177
15325
  results.push({ profile: profile.name, prompt });
@@ -15208,13 +15356,13 @@ async function exportCommand(format, opts) {
15208
15356
  // src/index.ts
15209
15357
  init_access();
15210
15358
  init_welcome();
15211
- var fs23 = __toESM(require("fs"));
15212
- var path22 = __toESM(require("path"));
15359
+ var fs24 = __toESM(require("fs"));
15360
+ var path23 = __toESM(require("path"));
15213
15361
  var program2 = new Command();
15214
- var pkgPath2 = path22.resolve(__dirname, "..", "package.json");
15362
+ var pkgPath2 = path23.resolve(__dirname, "..", "package.json");
15215
15363
  var pkgVersion2 = (() => {
15216
15364
  try {
15217
- return JSON.parse(fs23.readFileSync(pkgPath2, "utf-8")).version;
15365
+ return JSON.parse(fs24.readFileSync(pkgPath2, "utf-8")).version;
15218
15366
  } catch {
15219
15367
  return "2.7.1";
15220
15368
  }
@@ -15258,15 +15406,15 @@ program2.command("mcp").description("Start MCP server (for Claude Desktop and ot
15258
15406
  return;
15259
15407
  }
15260
15408
  if (opts.setupChatgpt) {
15261
- console.log(import_chalk30.default.bold("\nhold your voice \u2014 chatgpt setup\n"));
15409
+ console.log(import_chalk31.default.bold("\nhold your voice \u2014 chatgpt setup\n"));
15262
15410
  console.log("To connect HYV to ChatGPT:");
15263
- console.log(import_chalk30.default.dim(" 1. Go to ") + import_chalk30.default.cyan("https://chatgpt.com/#settings/Connectors"));
15264
- console.log(import_chalk30.default.dim(" 2. Add a new connector"));
15265
- console.log(import_chalk30.default.dim(" 3. For local MCP, use: ") + import_chalk30.default.cyan("hyv mcp"));
15266
- console.log(import_chalk30.default.dim(" 4. ChatGPT Desktop supports stdio MCP servers"));
15411
+ console.log(import_chalk31.default.dim(" 1. Go to ") + import_chalk31.default.cyan("https://chatgpt.com/#settings/Connectors"));
15412
+ console.log(import_chalk31.default.dim(" 2. Add a new connector"));
15413
+ console.log(import_chalk31.default.dim(" 3. For local MCP, use: ") + import_chalk31.default.cyan("hyv mcp"));
15414
+ console.log(import_chalk31.default.dim(" 4. ChatGPT Desktop supports stdio MCP servers"));
15267
15415
  console.log("");
15268
- console.log(import_chalk30.default.dim("Note: The remote HTTP MCP endpoint is not yet available."));
15269
- console.log(import_chalk30.default.dim("Use the local stdio MCP server with Claude Desktop or Claude Code instead."));
15416
+ console.log(import_chalk31.default.dim("Note: The remote HTTP MCP endpoint is not yet available."));
15417
+ console.log(import_chalk31.default.dim("Use the local stdio MCP server with Claude Desktop or Claude Code instead."));
15270
15418
  return;
15271
15419
  }
15272
15420
  startMcpServer();