@holdyourvoice/hyv 2.8.8 → 2.8.10

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
 
@@ -4647,6 +4647,27 @@ function assertSafeOpenUrl(url) {
4647
4647
  }
4648
4648
  return url;
4649
4649
  }
4650
+ function assertSafeOAuthUrl(url) {
4651
+ let parsed;
4652
+ try {
4653
+ parsed = new URL(url);
4654
+ } catch {
4655
+ throw new Error("Invalid URL");
4656
+ }
4657
+ if (parsed.protocol !== "https:") {
4658
+ throw new Error("Only https:// URLs can be opened");
4659
+ }
4660
+ if (ALLOWED_API_HOSTS.has(parsed.hostname)) {
4661
+ return url;
4662
+ }
4663
+ if (ALLOWED_OAUTH_HOSTS.has(parsed.hostname)) {
4664
+ if (parsed.hostname === "accounts.google.com" && !parsed.pathname.startsWith("/o/oauth2/")) {
4665
+ throw new Error(`Unexpected OAuth path: ${parsed.pathname}`);
4666
+ }
4667
+ return url;
4668
+ }
4669
+ throw new Error(`URL host not allowed: ${parsed.hostname}`);
4670
+ }
4650
4671
  function assertSafeProfileName(name) {
4651
4672
  const normalized = String(name || "").trim();
4652
4673
  if (!normalized)
@@ -4804,7 +4825,7 @@ function readLastEditSession() {
4804
4825
  return null;
4805
4826
  }
4806
4827
  }
4807
- var fs, path, os, HYV_DIR, AUTH_FILE, CONFIG_FILE, PROFILES_DIR, CACHE_DIR, QUEUE_DIR, LAST_SESSION_FILE, ALLOWED_API_HOSTS, API_BASE, PROFILE_NAME_RE;
4828
+ var fs, path, os, HYV_DIR, AUTH_FILE, CONFIG_FILE, PROFILES_DIR, CACHE_DIR, QUEUE_DIR, LAST_SESSION_FILE, ALLOWED_API_HOSTS, ALLOWED_OAUTH_HOSTS, API_BASE, PROFILE_NAME_RE;
4808
4829
  var init_config = __esm({
4809
4830
  "src/lib/config.ts"() {
4810
4831
  "use strict";
@@ -4825,6 +4846,7 @@ var init_config = __esm({
4825
4846
  "localhost",
4826
4847
  "127.0.0.1"
4827
4848
  ]);
4849
+ ALLOWED_OAUTH_HOSTS = /* @__PURE__ */ new Set(["accounts.google.com"]);
4828
4850
  API_BASE = validateApiBase(process.env.HYV_API_URL || "https://holdyourvoice.com");
4829
4851
  PROFILE_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,62}$/i;
4830
4852
  }
@@ -4834,11 +4856,11 @@ var init_config = __esm({
4834
4856
  var require_is_docker = __commonJS({
4835
4857
  "node_modules/is-docker/index.js"(exports2, module2) {
4836
4858
  "use strict";
4837
- var fs24 = require("fs");
4859
+ var fs25 = require("fs");
4838
4860
  var isDocker;
4839
4861
  function hasDockerEnv() {
4840
4862
  try {
4841
- fs24.statSync("/.dockerenv");
4863
+ fs25.statSync("/.dockerenv");
4842
4864
  return true;
4843
4865
  } catch (_) {
4844
4866
  return false;
@@ -4846,7 +4868,7 @@ var require_is_docker = __commonJS({
4846
4868
  }
4847
4869
  function hasDockerCGroup() {
4848
4870
  try {
4849
- return fs24.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4871
+ return fs25.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4850
4872
  } catch (_) {
4851
4873
  return false;
4852
4874
  }
@@ -4865,7 +4887,7 @@ var require_is_wsl = __commonJS({
4865
4887
  "node_modules/is-wsl/index.js"(exports2, module2) {
4866
4888
  "use strict";
4867
4889
  var os9 = require("os");
4868
- var fs24 = require("fs");
4890
+ var fs25 = require("fs");
4869
4891
  var isDocker = require_is_docker();
4870
4892
  var isWsl = () => {
4871
4893
  if (process.platform !== "linux") {
@@ -4878,7 +4900,7 @@ var require_is_wsl = __commonJS({
4878
4900
  return true;
4879
4901
  }
4880
4902
  try {
4881
- return fs24.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
4903
+ return fs25.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
4882
4904
  } catch (_) {
4883
4905
  return false;
4884
4906
  }
@@ -4917,17 +4939,17 @@ var require_define_lazy_prop = __commonJS({
4917
4939
  // node_modules/open/index.js
4918
4940
  var require_open = __commonJS({
4919
4941
  "node_modules/open/index.js"(exports2, module2) {
4920
- var path23 = require("path");
4942
+ var path24 = require("path");
4921
4943
  var childProcess = require("child_process");
4922
- var { promises: fs24, constants: fsConstants } = require("fs");
4944
+ var { promises: fs25, constants: fsConstants } = require("fs");
4923
4945
  var isWsl = require_is_wsl();
4924
4946
  var isDocker = require_is_docker();
4925
4947
  var defineLazyProperty = require_define_lazy_prop();
4926
- var localXdgOpenPath = path23.join(__dirname, "xdg-open");
4948
+ var localXdgOpenPath = path24.join(__dirname, "xdg-open");
4927
4949
  var { platform, arch } = process;
4928
4950
  var hasContainerEnv = () => {
4929
4951
  try {
4930
- fs24.statSync("/run/.containerenv");
4952
+ fs25.statSync("/run/.containerenv");
4931
4953
  return true;
4932
4954
  } catch {
4933
4955
  return false;
@@ -4950,14 +4972,14 @@ var require_open = __commonJS({
4950
4972
  const configFilePath = "/etc/wsl.conf";
4951
4973
  let isConfigFileExists = false;
4952
4974
  try {
4953
- await fs24.access(configFilePath, fsConstants.F_OK);
4975
+ await fs25.access(configFilePath, fsConstants.F_OK);
4954
4976
  isConfigFileExists = true;
4955
4977
  } catch {
4956
4978
  }
4957
4979
  if (!isConfigFileExists) {
4958
4980
  return defaultMountPoint;
4959
4981
  }
4960
- const configContent = await fs24.readFile(configFilePath, { encoding: "utf8" });
4982
+ const configContent = await fs25.readFile(configFilePath, { encoding: "utf8" });
4961
4983
  const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
4962
4984
  if (!configMountPoint) {
4963
4985
  return defaultMountPoint;
@@ -5057,7 +5079,7 @@ var require_open = __commonJS({
5057
5079
  const isBundled = !__dirname || __dirname === "/";
5058
5080
  let exeLocalXdgOpen = false;
5059
5081
  try {
5060
- await fs24.access(localXdgOpenPath, fsConstants.X_OK);
5082
+ await fs25.access(localXdgOpenPath, fsConstants.X_OK);
5061
5083
  exeLocalXdgOpen = true;
5062
5084
  } catch {
5063
5085
  }
@@ -5244,6 +5266,33 @@ var init_version = __esm({
5244
5266
  }
5245
5267
  });
5246
5268
 
5269
+ // src/lib/auth-refresh.ts
5270
+ function tokenExpiresAtMs(expiresAt) {
5271
+ if (!expiresAt)
5272
+ return null;
5273
+ const ms = new Date(expiresAt).getTime();
5274
+ return Number.isFinite(ms) ? ms : null;
5275
+ }
5276
+ function shouldRefreshToken(expiresAt, now = Date.now()) {
5277
+ const expiresMs = tokenExpiresAtMs(expiresAt);
5278
+ if (expiresMs === null)
5279
+ return false;
5280
+ return expiresMs <= now + REFRESH_LEAD_MS;
5281
+ }
5282
+ function isTokenExpired(expiresAt, now = Date.now()) {
5283
+ const expiresMs = tokenExpiresAtMs(expiresAt);
5284
+ if (expiresMs === null)
5285
+ return false;
5286
+ return expiresMs < now;
5287
+ }
5288
+ var REFRESH_LEAD_MS;
5289
+ var init_auth_refresh = __esm({
5290
+ "src/lib/auth-refresh.ts"() {
5291
+ "use strict";
5292
+ REFRESH_LEAD_MS = 5 * 60 * 1e3;
5293
+ }
5294
+ });
5295
+
5247
5296
  // src/lib/auth.ts
5248
5297
  var auth_exports = {};
5249
5298
  __export(auth_exports, {
@@ -5252,8 +5301,14 @@ __export(auth_exports, {
5252
5301
  authenticatedRequest: () => authenticatedRequest,
5253
5302
  checkSession: () => checkSession,
5254
5303
  getValidToken: () => getValidToken,
5255
- refreshToken: () => refreshToken
5304
+ refreshToken: () => refreshToken,
5305
+ verifyOAuthState: () => verifyOAuthState
5256
5306
  });
5307
+ function verifyOAuthState(received, expected) {
5308
+ if (!received || !expected || received !== expected) {
5309
+ throw new Error("OAuth state mismatch \u2014 possible CSRF attempt");
5310
+ }
5311
+ }
5257
5312
  function escapeHtml(value) {
5258
5313
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5259
5314
  }
@@ -5300,16 +5355,12 @@ async function getValidToken() {
5300
5355
  const auth = readAuth();
5301
5356
  if (!auth?.token)
5302
5357
  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
- }
5358
+ if (shouldRefreshToken(auth.expires_at)) {
5359
+ const ok = await refreshToken(auth.token);
5360
+ if (ok)
5361
+ return getToken();
5362
+ if (isTokenExpired(auth.expires_at))
5363
+ return null;
5313
5364
  }
5314
5365
  return auth.token;
5315
5366
  }
@@ -5363,7 +5414,7 @@ async function authenticateWithBrowser() {
5363
5414
  throw new Error("Authentication server did not return OAuth state");
5364
5415
  }
5365
5416
  console.log(import_chalk.default.cyan("\nOpening browser for authentication..."));
5366
- await (0, import_open.default)(assertSafeOpenUrl(auth_url));
5417
+ await (0, import_open.default)(assertSafeOAuthUrl(auth_url));
5367
5418
  const authData = await new Promise((resolve14, reject) => {
5368
5419
  const timeout = setTimeout(() => {
5369
5420
  server.close();
@@ -5382,12 +5433,14 @@ async function authenticateWithBrowser() {
5382
5433
  reject(new Error("Invalid callback parameters"));
5383
5434
  return;
5384
5435
  }
5385
- if (state !== expectedState) {
5436
+ try {
5437
+ verifyOAuthState(state, expectedState);
5438
+ } catch (stateErr) {
5386
5439
  res.writeHead(400, { "Content-Type": "text/html" });
5387
5440
  res.end("<h1>Authentication failed</h1><p>Invalid OAuth state.</p>");
5388
5441
  clearTimeout(timeout);
5389
5442
  server.close();
5390
- reject(new Error("OAuth state mismatch \u2014 possible CSRF attempt"));
5443
+ reject(stateErr);
5391
5444
  return;
5392
5445
  }
5393
5446
  try {
@@ -5481,6 +5534,7 @@ var init_auth = __esm({
5481
5534
  import_open = __toESM(require_open());
5482
5535
  init_config();
5483
5536
  init_version();
5537
+ init_auth_refresh();
5484
5538
  }
5485
5539
  });
5486
5540
 
@@ -5641,11 +5695,11 @@ __export(api_exports, {
5641
5695
  apiPost: () => apiPost,
5642
5696
  requireSubscription: () => requireSubscription
5643
5697
  });
5644
- async function request2(method, path23, body) {
5698
+ async function request2(method, path24, body) {
5645
5699
  const token = await getValidToken();
5646
5700
  if (!token)
5647
5701
  throw new Error("you're not signed in yet. run: hyv init");
5648
- const url = `${API_BASE}${path23}`;
5702
+ const url = `${API_BASE}${path24}`;
5649
5703
  const opts = {
5650
5704
  method,
5651
5705
  headers: {
@@ -5670,11 +5724,11 @@ async function request2(method, path23, body) {
5670
5724
  }
5671
5725
  return res.json();
5672
5726
  }
5673
- function apiGet(path23) {
5674
- return request2("GET", path23);
5727
+ function apiGet(path24) {
5728
+ return request2("GET", path24);
5675
5729
  }
5676
- function apiPost(path23, body) {
5677
- return request2("POST", path23, body);
5730
+ function apiPost(path24, body) {
5731
+ return request2("POST", path24, body);
5678
5732
  }
5679
5733
  async function requireSubscription() {
5680
5734
  const { requirePaidFeature: requirePaidFeature2 } = await Promise.resolve().then(() => (init_access(), access_exports));
@@ -7246,26 +7300,26 @@ function runPipeline(text, profile, applyFixes = false) {
7246
7300
  };
7247
7301
  }
7248
7302
  function readText(source) {
7249
- const fs24 = require("fs");
7250
- const path23 = require("path");
7303
+ const fs25 = require("fs");
7304
+ const path24 = require("path");
7251
7305
  if (source === "-") {
7252
7306
  if (process.stdin.isTTY) {
7253
7307
  console.error("No input provided. Pipe content or specify a file.");
7254
7308
  process.exit(1);
7255
7309
  }
7256
- return { text: fs24.readFileSync(0, "utf-8"), path: "stdin" };
7310
+ return { text: fs25.readFileSync(0, "utf-8"), path: "stdin" };
7257
7311
  }
7258
- const resolved = path23.resolve(source);
7259
- if (!fs24.existsSync(resolved)) {
7312
+ const resolved = path24.resolve(source);
7313
+ if (!fs25.existsSync(resolved)) {
7260
7314
  console.error(`File not found: ${resolved}`);
7261
7315
  process.exit(1);
7262
7316
  }
7263
- const stat = fs24.statSync(resolved);
7317
+ const stat = fs25.statSync(resolved);
7264
7318
  if (stat.isDirectory()) {
7265
7319
  console.error(`${resolved} is a directory, not a file.`);
7266
7320
  process.exit(1);
7267
7321
  }
7268
- return { text: fs24.readFileSync(resolved, "utf-8"), path: resolved };
7322
+ return { text: fs25.readFileSync(resolved, "utf-8"), path: resolved };
7269
7323
  }
7270
7324
  var init_pipeline = __esm({
7271
7325
  "src/lib/pipeline.ts"() {
@@ -8273,7 +8327,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
8273
8327
  g.minimatch.escape = vi.escape;
8274
8328
  g.minimatch.unescape = Ei.unescape;
8275
8329
  });
8276
- var fs24 = R((Wt) => {
8330
+ var fs25 = R((Wt) => {
8277
8331
  "use strict";
8278
8332
  Object.defineProperty(Wt, "__esModule", { value: true });
8279
8333
  Wt.LRUCache = void 0;
@@ -9242,7 +9296,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
9242
9296
  };
9243
9297
  Object.defineProperty(_, "__esModule", { value: true });
9244
9298
  _.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) => {
9299
+ 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
9300
  let t = vs.get(n);
9247
9301
  if (t)
9248
9302
  return t;
@@ -10761,7 +10815,7 @@ var {
10761
10815
  } = import_index.default;
10762
10816
 
10763
10817
  // src/index.ts
10764
- var import_chalk30 = __toESM(require_source());
10818
+ var import_chalk31 = __toESM(require_source());
10765
10819
 
10766
10820
  // src/commands/init.ts
10767
10821
  var import_chalk4 = __toESM(require_source());
@@ -12454,8 +12508,8 @@ function registerImportCommand(program3) {
12454
12508
  }
12455
12509
  function askQuestion(question) {
12456
12510
  return new Promise((resolve14) => {
12457
- const readline = require("readline");
12458
- const rl = readline.createInterface({
12511
+ const readline2 = require("readline");
12512
+ const rl = readline2.createInterface({
12459
12513
  input: process.stdin,
12460
12514
  output: process.stdout
12461
12515
  });
@@ -13040,28 +13094,137 @@ hyv scan ${filePath}`));
13040
13094
 
13041
13095
  // src/commands/doctor.ts
13042
13096
  var import_chalk16 = __toESM(require_source());
13043
- var fs13 = __toESM(require("fs"));
13044
- var path14 = __toESM(require("path"));
13097
+ var fs14 = __toESM(require("fs"));
13098
+ var path15 = __toESM(require("path"));
13045
13099
  var os5 = __toESM(require("os"));
13046
13100
  init_config();
13047
13101
  init_auth();
13048
13102
  init_access();
13049
13103
  init_local_profile();
13050
13104
  init_version();
13105
+
13106
+ // src/lib/mcp-stdio-test.ts
13107
+ var import_child_process2 = require("child_process");
13108
+
13109
+ // src/lib/cli-entry.ts
13110
+ var fs13 = __toESM(require("fs"));
13111
+ var path14 = __toESM(require("path"));
13112
+ function resolveCliEntry() {
13113
+ const candidates = [
13114
+ path14.resolve(process.argv[1] || ""),
13115
+ path14.resolve(__dirname, "index.js"),
13116
+ path14.resolve(__dirname, "..", "dist", "index.js")
13117
+ ];
13118
+ return candidates.find((p) => p && fs13.existsSync(p)) || null;
13119
+ }
13120
+ function mcpServerCommand() {
13121
+ const entry = resolveCliEntry();
13122
+ if (entry) {
13123
+ return { command: process.execPath, args: [entry, "mcp"] };
13124
+ }
13125
+ return { command: "hyv", args: ["mcp"] };
13126
+ }
13127
+ function mcpServerSnippet() {
13128
+ const { command, args: args2 } = mcpServerCommand();
13129
+ return JSON.stringify({ command, args: args2 }, null, 2);
13130
+ }
13131
+
13132
+ // src/lib/mcp-stdio-test.ts
13133
+ async function testMcpStdioSubprocess() {
13134
+ const entry = resolveCliEntry();
13135
+ if (!entry)
13136
+ return { ok: false };
13137
+ return new Promise((resolve14) => {
13138
+ const child = (0, import_child_process2.spawn)(process.execPath, [entry, "mcp"], {
13139
+ stdio: ["pipe", "pipe", "pipe"],
13140
+ env: { ...process.env, HYV_POSTINSTALL_QUIET: "1" }
13141
+ });
13142
+ let buffer = "";
13143
+ let settled = false;
13144
+ const finish = (ok, toolCount) => {
13145
+ if (settled)
13146
+ return;
13147
+ settled = true;
13148
+ clearTimeout(timeout);
13149
+ try {
13150
+ child.kill();
13151
+ } catch {
13152
+ }
13153
+ resolve14({ ok, toolCount });
13154
+ };
13155
+ const timeout = setTimeout(() => finish(false), 1e4);
13156
+ const handleLine = (line) => {
13157
+ const trimmed = line.trim();
13158
+ if (!trimmed.startsWith("{"))
13159
+ return;
13160
+ try {
13161
+ const msg = JSON.parse(trimmed);
13162
+ if (msg.id === 2 && Array.isArray(msg.result?.tools) && msg.result.tools.length > 0) {
13163
+ finish(true, msg.result.tools.length);
13164
+ }
13165
+ } catch {
13166
+ }
13167
+ };
13168
+ child.stdout.on("data", (chunk) => {
13169
+ buffer += chunk.toString();
13170
+ const lines = buffer.split("\n");
13171
+ buffer = lines.pop() || "";
13172
+ for (const line of lines)
13173
+ handleLine(line);
13174
+ });
13175
+ child.on("error", () => finish(false));
13176
+ child.on("exit", () => {
13177
+ if (!settled) {
13178
+ handleLine(buffer);
13179
+ finish(buffer.includes("hyv_scan"));
13180
+ }
13181
+ });
13182
+ setTimeout(() => {
13183
+ const send2 = (payload) => {
13184
+ child.stdin.write(`${JSON.stringify(payload)}
13185
+ `);
13186
+ };
13187
+ send2({
13188
+ jsonrpc: "2.0",
13189
+ id: 1,
13190
+ method: "initialize",
13191
+ params: {
13192
+ protocolVersion: "2024-11-05",
13193
+ capabilities: {},
13194
+ clientInfo: { name: "hyv-self-test", version: "1.0" }
13195
+ }
13196
+ });
13197
+ send2({ jsonrpc: "2.0", method: "notifications/initialized" });
13198
+ send2({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} });
13199
+ }, 500);
13200
+ });
13201
+ }
13202
+
13203
+ // src/commands/doctor.ts
13051
13204
  var HOME = os5.homedir();
13052
13205
  var IS_WIN = process.platform === "win32";
13053
13206
  function claudeDesktopDir() {
13054
13207
  if (IS_WIN)
13055
- return path14.join(HOME, "AppData", "Roaming", "Claude");
13208
+ return path15.join(HOME, "AppData", "Roaming", "Claude");
13056
13209
  if (process.platform === "linux")
13057
- return path14.join(HOME, ".config", "Claude");
13058
- return path14.join(HOME, "Library", "Application Support", "Claude");
13210
+ return path15.join(HOME, ".config", "Claude");
13211
+ return path15.join(HOME, "Library", "Application Support", "Claude");
13212
+ }
13213
+ function isOwnerOnlyFile(filePath) {
13214
+ try {
13215
+ if (!fs14.existsSync(filePath))
13216
+ return true;
13217
+ const mode = fs14.statSync(filePath).mode & 511;
13218
+ return (mode & 63) === 0;
13219
+ } catch {
13220
+ return false;
13221
+ }
13059
13222
  }
13060
13223
  function readMcpHyv(configFile) {
13061
13224
  try {
13062
- if (!fs13.existsSync(configFile))
13225
+ if (!fs14.existsSync(configFile))
13063
13226
  return false;
13064
- const cfg = JSON.parse(fs13.readFileSync(configFile, "utf-8"));
13227
+ const cfg = JSON.parse(fs14.readFileSync(configFile, "utf-8"));
13065
13228
  return Boolean(cfg.mcpServers?.hyv);
13066
13229
  } catch {
13067
13230
  return false;
@@ -13082,28 +13245,28 @@ function registerDoctorCommand(program3) {
13082
13245
  console.log(import_chalk16.default.dim("tip: run hyv welcome for free capabilities\n"));
13083
13246
  console.log(import_chalk16.default.dim("checking cli installation..."));
13084
13247
  const cliPath = process.argv[1];
13085
- if (cliPath && fs13.existsSync(cliPath)) {
13248
+ if (cliPath && fs14.existsSync(cliPath)) {
13086
13249
  console.log(import_chalk16.default.green(" \u2713 cli installed"));
13087
13250
  } else {
13088
13251
  console.log(import_chalk16.default.red(" \u2717 cli not found"));
13089
13252
  issues++;
13090
13253
  }
13091
13254
  console.log(import_chalk16.default.dim("checking .hyv directory..."));
13092
- if (fs13.existsSync(HYV_DIR)) {
13255
+ if (fs14.existsSync(HYV_DIR)) {
13093
13256
  console.log(import_chalk16.default.green(" \u2713 .hyv directory exists"));
13094
13257
  } else {
13095
13258
  console.log(import_chalk16.default.yellow(" ! .hyv directory missing \u2014 creating..."));
13096
- fs13.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
13259
+ fs14.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
13097
13260
  fixed++;
13098
13261
  }
13099
13262
  console.log(import_chalk16.default.dim("checking cache..."));
13100
13263
  const diskProfiles = listDiskCachedProfiles();
13101
- const syncMeta = path14.join(CACHE_DIR, "sync-meta.json");
13102
- if (diskProfiles.length > 0 || fs13.existsSync(syncMeta)) {
13264
+ const syncMeta = path15.join(CACHE_DIR, "sync-meta.json");
13265
+ if (diskProfiles.length > 0 || fs14.existsSync(syncMeta)) {
13103
13266
  console.log(import_chalk16.default.green(` \u2713 profile cache (${diskProfiles.length} full profile(s))`));
13104
- if (fs13.existsSync(syncMeta)) {
13267
+ if (fs14.existsSync(syncMeta)) {
13105
13268
  try {
13106
- const meta = JSON.parse(fs13.readFileSync(syncMeta, "utf-8"));
13269
+ const meta = JSON.parse(fs14.readFileSync(syncMeta, "utf-8"));
13107
13270
  console.log(import_chalk16.default.dim(` last sync: ${meta.synced_at || "unknown"}`));
13108
13271
  } catch {
13109
13272
  }
@@ -13111,6 +13274,26 @@ function registerDoctorCommand(program3) {
13111
13274
  } else {
13112
13275
  console.log(import_chalk16.default.dim(" - no full profile cache (free local engine still works)"));
13113
13276
  }
13277
+ console.log(import_chalk16.default.dim("checking file permissions..."));
13278
+ if (fs14.existsSync(HYV_DIR)) {
13279
+ const hyvMode = fs14.statSync(HYV_DIR).mode & 511;
13280
+ if ((hyvMode & 63) === 0) {
13281
+ console.log(import_chalk16.default.green(" \u2713 .hyv directory permissions"));
13282
+ } else {
13283
+ console.log(import_chalk16.default.yellow(" ! .hyv directory is world/group accessible"));
13284
+ console.log(import_chalk16.default.dim(" run: chmod 700 ~/.hyv"));
13285
+ issues++;
13286
+ }
13287
+ }
13288
+ if (fs14.existsSync(AUTH_FILE)) {
13289
+ if (isOwnerOnlyFile(AUTH_FILE)) {
13290
+ console.log(import_chalk16.default.green(" \u2713 auth.json permissions"));
13291
+ } else {
13292
+ console.log(import_chalk16.default.yellow(" ! auth.json is world/group readable"));
13293
+ console.log(import_chalk16.default.dim(" run: chmod 600 ~/.hyv/auth.json"));
13294
+ issues++;
13295
+ }
13296
+ }
13114
13297
  console.log(import_chalk16.default.dim("checking authentication..."));
13115
13298
  if (isInitialized()) {
13116
13299
  const auth = readAuth();
@@ -13135,9 +13318,9 @@ function registerDoctorCommand(program3) {
13135
13318
  console.log(import_chalk16.default.dim(" run: hyv init for profiles + learning"));
13136
13319
  }
13137
13320
  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") : [];
13321
+ const voiceMd = path15.join(HYV_DIR, "voice.md");
13322
+ const hasVoiceMd = fs14.existsSync(voiceMd) && fs14.readFileSync(voiceMd, "utf-8").trim().length > 50;
13323
+ const profileFiles = fs14.existsSync(PROFILES_DIR) ? fs14.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md") && f !== "voice.md") : [];
13141
13324
  if (hasVoiceMd || profileFiles.length > 0 || diskProfiles.length > 0) {
13142
13325
  if (hasVoiceMd)
13143
13326
  console.log(import_chalk16.default.green(" \u2713 voice.md exists"));
@@ -13150,14 +13333,21 @@ function registerDoctorCommand(program3) {
13150
13333
  console.log(import_chalk16.default.dim(" run: hyv new <name> or hyv init"));
13151
13334
  }
13152
13335
  console.log(import_chalk16.default.dim("checking agent configurations..."));
13336
+ const cursorLegacyRule = path15.join(HOME, ".cursor", "rules", "hyv.md");
13337
+ const cursorRule = path15.join(HOME, ".cursor", "rules", "hyv.mdc");
13338
+ if (fs14.existsSync(cursorLegacyRule) && fs14.existsSync(cursorRule)) {
13339
+ console.log(import_chalk16.default.yellow(" ! stale cursor rule ~/.cursor/rules/hyv.md (use hyv.mdc)"));
13340
+ console.log(import_chalk16.default.dim(" run: rm ~/.cursor/rules/hyv.md (or hyv doctor --fix-agents)"));
13341
+ issues++;
13342
+ }
13153
13343
  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")) }
13344
+ { name: "claude desktop mcp", ok: readMcpHyv(path15.join(claudeDesktopDir(), "claude_desktop_config.json")) },
13345
+ { name: "cursor mcp", ok: readMcpHyv(path15.join(HOME, ".cursor", "mcp.json")) },
13346
+ { name: "cursor rule", ok: fs14.existsSync(cursorRule) },
13347
+ { name: "claude code command", ok: fs14.existsSync(path15.join(HOME, ".claude", "commands", "hyv.md")) },
13348
+ { name: "claude code skill", ok: fs14.existsSync(path15.join(HOME, ".claude", "skills", "hold-your-voice", "SKILL.md")) },
13349
+ { name: "codex agents", ok: fs14.existsSync(path15.join(HOME, ".codex", "AGENTS.md")) && fs14.readFileSync(path15.join(HOME, ".codex", "AGENTS.md"), "utf-8").includes("hyv") },
13350
+ { name: "command code skill", ok: fs14.existsSync(path15.join(HOME, ".commandcode", "skills", "hyv", "SKILL.md")) }
13161
13351
  ];
13162
13352
  for (const agent of agentChecks) {
13163
13353
  if (agent.ok) {
@@ -13168,8 +13358,8 @@ function registerDoctorCommand(program3) {
13168
13358
  }
13169
13359
  if (opts.fixAgents) {
13170
13360
  try {
13171
- const pkgDir = path14.resolve(__dirname, "..");
13172
- const { setupAgents } = require(path14.join(pkgDir, "scripts", "postinstall-lib.js"));
13361
+ const pkgDir = path15.resolve(__dirname, "..");
13362
+ const { setupAgents } = require(path15.join(pkgDir, "scripts", "postinstall-lib.js"));
13173
13363
  const result = setupAgents({ pkgDir, quiet: true });
13174
13364
  console.log(import_chalk16.default.green(` \u2713 re-ran agent setup (${result.configured.join(", ") || "no changes"})`));
13175
13365
  if (result.warnings.length) {
@@ -13181,8 +13371,8 @@ function registerDoctorCommand(program3) {
13181
13371
  }
13182
13372
  }
13183
13373
  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"));
13374
+ const claudeMcp = readMcpHyv(path15.join(claudeDesktopDir(), "claude_desktop_config.json"));
13375
+ const cursorMcp = readMcpHyv(path15.join(HOME, ".cursor", "mcp.json"));
13186
13376
  if (claudeMcp || cursorMcp) {
13187
13377
  if (claudeMcp)
13188
13378
  console.log(import_chalk16.default.green(" \u2713 mcp configured for claude desktop"));
@@ -13192,6 +13382,22 @@ function registerDoctorCommand(program3) {
13192
13382
  console.log(import_chalk16.default.yellow(" ! mcp not configured \u2014 run: hyv doctor --fix-agents or hyv mcp --setup"));
13193
13383
  issues++;
13194
13384
  }
13385
+ try {
13386
+ const stdio = await testMcpStdioSubprocess();
13387
+ if (stdio.ok && (stdio.toolCount || 0) >= 10) {
13388
+ console.log(import_chalk16.default.green(` \u2713 mcp stdio subprocess healthy (${stdio.toolCount} tools)`));
13389
+ } else if (stdio.ok) {
13390
+ console.log(import_chalk16.default.yellow(` ! mcp stdio ok but only ${stdio.toolCount || 0} tools`));
13391
+ issues++;
13392
+ } else {
13393
+ console.log(import_chalk16.default.red(" \u2717 mcp stdio subprocess failed"));
13394
+ console.log(import_chalk16.default.dim(" run: hyv mcp --test"));
13395
+ issues++;
13396
+ }
13397
+ } catch (err) {
13398
+ console.log(import_chalk16.default.red(` \u2717 mcp stdio probe failed: ${err.message}`));
13399
+ issues++;
13400
+ }
13195
13401
  console.log("");
13196
13402
  if (issues === 0 && fixed === 0) {
13197
13403
  console.log(import_chalk16.default.green("\u2713 everything looks good!"));
@@ -13268,15 +13474,51 @@ error: ${error.message}
13268
13474
  }
13269
13475
 
13270
13476
  // src/commands/fix.ts
13271
- var import_chalk18 = __toESM(require_source());
13272
- var fs14 = __toESM(require("fs"));
13273
- var path15 = __toESM(require("path"));
13477
+ var import_chalk19 = __toESM(require_source());
13274
13478
  init_pipeline();
13275
13479
  init_local_profile();
13276
13480
  init_access();
13277
13481
  init_config();
13482
+
13483
+ // src/lib/destructive-write.ts
13484
+ var fs15 = __toESM(require("fs"));
13485
+ var path16 = __toESM(require("path"));
13486
+ var readline = __toESM(require("readline"));
13487
+ var import_chalk18 = __toESM(require_source());
13488
+ async function confirmDestructiveWrite(options) {
13489
+ if (options.yes)
13490
+ return true;
13491
+ const label = options.target ? `${options.action} (${options.target})` : options.action;
13492
+ if (!process.stdin.isTTY) {
13493
+ console.error(import_chalk18.default.red("\nRefusing destructive write without confirmation (non-interactive)."));
13494
+ console.error(import_chalk18.default.dim(" Re-run with --yes to create .bak backups and proceed.\n"));
13495
+ return false;
13496
+ }
13497
+ const question = `
13498
+ ${label}
13499
+ A .bak backup will be created. Proceed? [y/N] `;
13500
+ const answer = await new Promise((resolve14) => {
13501
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
13502
+ rl.question(question, (value) => {
13503
+ rl.close();
13504
+ resolve14(value.trim().toLowerCase());
13505
+ });
13506
+ });
13507
+ return answer === "y" || answer === "yes";
13508
+ }
13509
+ function writeInPlaceWithBackup(filePath, content) {
13510
+ const backupPath = filePath + ".bak";
13511
+ fs15.copyFileSync(filePath, backupPath);
13512
+ fs15.writeFileSync(filePath, content);
13513
+ return backupPath;
13514
+ }
13515
+ function backupBasename(filePath) {
13516
+ return path16.basename(filePath) + ".bak";
13517
+ }
13518
+
13519
+ // src/commands/fix.ts
13278
13520
  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) => {
13521
+ 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
13522
  try {
13281
13523
  const profile = await loadProfileForCommand(options.profile);
13282
13524
  const { text, path: filePath } = readText(file);
@@ -13285,9 +13527,9 @@ function registerFixCommand(program3) {
13285
13527
  if (options.format === "json") {
13286
13528
  console.log(JSON.stringify({ file: filePath, autoFixes: 0, llmIssues: result.stats.needsLLM, changes: [] }));
13287
13529
  } else {
13288
- console.log(import_chalk18.default.green("\n\u2713 No auto-fixable issues found."));
13530
+ console.log(import_chalk19.default.green("\n\u2713 No auto-fixable issues found."));
13289
13531
  if (result.stats.needsLLM > 0) {
13290
- console.log(import_chalk18.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
13532
+ console.log(import_chalk19.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
13291
13533
  }
13292
13534
  }
13293
13535
  return;
@@ -13301,24 +13543,30 @@ function registerFixCommand(program3) {
13301
13543
  fixed: options.dryRun ? void 0 : result.fixed
13302
13544
  }, null, 2));
13303
13545
  } else {
13304
- console.log(import_chalk18.default.dim(`
13546
+ console.log(import_chalk19.default.dim(`
13305
13547
  hyv fix ${filePath}
13306
13548
  `));
13307
13549
  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));
13550
+ 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
13551
  }
13310
- console.log(import_chalk18.default.green(`
13552
+ console.log(import_chalk19.default.green(`
13311
13553
  \u2713 ${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} applied`));
13312
13554
  if (result.stats.needsLLM > 0) {
13313
- console.log(import_chalk18.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
13555
+ console.log(import_chalk19.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
13314
13556
  }
13315
13557
  }
13316
13558
  if (!options.dryRun) {
13317
13559
  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)})`));
13560
+ const confirmed = await confirmDestructiveWrite({
13561
+ yes: options.yes,
13562
+ action: "Apply auto-fixes in-place",
13563
+ target: filePath
13564
+ });
13565
+ if (!confirmed) {
13566
+ process.exit(1);
13567
+ }
13568
+ const backupPath = writeInPlaceWithBackup(filePath, result.fixed);
13569
+ console.log(import_chalk19.default.dim(` Written to ${filePath} (backup: ${backupBasename(filePath)})`));
13322
13570
  saveLastEditSession({
13323
13571
  original_path: filePath,
13324
13572
  edited_path: filePath,
@@ -13326,7 +13574,7 @@ hyv fix ${filePath}
13326
13574
  edited_text: result.fixed,
13327
13575
  profile: options.profile
13328
13576
  });
13329
- console.log(import_chalk18.default.dim(" Tip: hyv reinforce --last to teach your profile from this edit"));
13577
+ console.log(import_chalk19.default.dim(" Tip: hyv reinforce --last to teach your profile from this edit"));
13330
13578
  } else if (filePath === "stdin" || !options.inPlace) {
13331
13579
  process.stdout.write("\n" + result.fixed + "\n");
13332
13580
  if (filePath !== "stdin") {
@@ -13342,14 +13590,14 @@ hyv fix ${filePath}
13342
13590
  }
13343
13591
  await maybeShowLimitedModeHint(!!profile);
13344
13592
  } catch (error) {
13345
- console.error(import_chalk18.default.red(`Error: ${error.message}`));
13593
+ console.error(import_chalk19.default.red(`Error: ${error.message}`));
13346
13594
  process.exit(1);
13347
13595
  }
13348
13596
  });
13349
13597
  }
13350
13598
 
13351
13599
  // src/commands/check.ts
13352
- var import_chalk19 = __toESM(require_source());
13600
+ var import_chalk20 = __toESM(require_source());
13353
13601
  init_pipeline();
13354
13602
  init_local_profile();
13355
13603
  init_access();
@@ -13359,20 +13607,20 @@ function registerCheckCommand(program3) {
13359
13607
  const profile = await loadProfileForCommand(options.profile);
13360
13608
  let inputText = text;
13361
13609
  if (text === "-") {
13362
- const fs25 = require("fs");
13610
+ const fs26 = require("fs");
13363
13611
  if (process.stdin.isTTY) {
13364
- console.error(import_chalk19.default.red("No input provided. Pipe content or pass text as argument."));
13612
+ console.error(import_chalk20.default.red("No input provided. Pipe content or pass text as argument."));
13365
13613
  process.exit(1);
13366
13614
  }
13367
- inputText = fs25.readFileSync(0, "utf-8");
13615
+ inputText = fs26.readFileSync(0, "utf-8");
13368
13616
  }
13369
13617
  if (!inputText.trim()) {
13370
- console.error(import_chalk19.default.red("No text provided."));
13618
+ console.error(import_chalk20.default.red("No text provided."));
13371
13619
  process.exit(1);
13372
13620
  }
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}`));
13621
+ const fs25 = require("fs");
13622
+ if (text !== "-" && fs25.existsSync(text)) {
13623
+ console.log(import_chalk20.default.yellow(`"${text}" looks like a file. Did you mean: hyv scan ${text}`));
13376
13624
  process.exit(1);
13377
13625
  }
13378
13626
  const result = runPipeline(inputText, profile, false);
@@ -13396,30 +13644,30 @@ function registerCheckCommand(program3) {
13396
13644
  }, null, 2));
13397
13645
  } else {
13398
13646
  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`));
13647
+ console.log(import_chalk20.default.green("\n\u2713 Clean \u2014 no AI patterns found."));
13648
+ console.log(import_chalk20.default.dim(` score: ${result.score}/100`));
13401
13649
  } else {
13402
13650
  console.log("");
13403
13651
  printGroupedSignals(result.signalMap.signals.slice(0, 15), profile);
13404
13652
  if (result.signalMap.signals.length > 15) {
13405
- console.log(import_chalk19.default.dim(` ... and ${result.signalMap.signals.length - 15} more`));
13653
+ console.log(import_chalk20.default.dim(` ... and ${result.signalMap.signals.length - 15} more`));
13406
13654
  }
13407
- console.log(import_chalk19.default.yellow(`
13655
+ console.log(import_chalk20.default.yellow(`
13408
13656
  ${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow)`));
13409
- console.log(import_chalk19.default.dim(` score: ${result.score}/100`));
13657
+ console.log(import_chalk20.default.dim(` score: ${result.score}/100`));
13410
13658
  }
13411
13659
  await maybeShowLimitedModeHint(!!profile);
13412
13660
  }
13413
13661
  process.exit(result.stats.totalSignals > 0 ? 1 : 0);
13414
13662
  } catch (error) {
13415
- console.error(import_chalk19.default.red(`Error: ${error.message}`));
13663
+ console.error(import_chalk20.default.red(`Error: ${error.message}`));
13416
13664
  process.exit(1);
13417
13665
  }
13418
13666
  });
13419
13667
  }
13420
13668
 
13421
13669
  // src/commands/score.ts
13422
- var import_chalk20 = __toESM(require_source());
13670
+ var import_chalk21 = __toESM(require_source());
13423
13671
  init_pipeline();
13424
13672
  init_local_profile();
13425
13673
  function registerScoreCommand(program3) {
@@ -13444,14 +13692,14 @@ function registerScoreCommand(program3) {
13444
13692
  process.exit(1);
13445
13693
  }
13446
13694
  } catch (error) {
13447
- console.error(import_chalk20.default.red(`Error: ${error.message}`));
13695
+ console.error(import_chalk21.default.red(`Error: ${error.message}`));
13448
13696
  process.exit(1);
13449
13697
  }
13450
13698
  });
13451
13699
  }
13452
13700
 
13453
13701
  // src/commands/diff.ts
13454
- var import_chalk21 = __toESM(require_source());
13702
+ var import_chalk22 = __toESM(require_source());
13455
13703
  init_pipeline();
13456
13704
  init_local_profile();
13457
13705
  function registerDiffCommand(program3) {
@@ -13461,9 +13709,9 @@ function registerDiffCommand(program3) {
13461
13709
  const { text, path: filePath } = readText(file);
13462
13710
  const result = runPipeline(text, profile, true);
13463
13711
  if (result.changes.length === 0) {
13464
- console.log(import_chalk21.default.green("\n\u2713 No auto-fixable changes."));
13712
+ console.log(import_chalk22.default.green("\n\u2713 No auto-fixable changes."));
13465
13713
  if (result.stats.needsLLM > 0) {
13466
- console.log(import_chalk21.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite`));
13714
+ console.log(import_chalk22.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite`));
13467
13715
  }
13468
13716
  return;
13469
13717
  }
@@ -13478,42 +13726,42 @@ function registerDiffCommand(program3) {
13478
13726
  const originalLines = text.split("\n");
13479
13727
  const fixedLines = result.fixed.split("\n");
13480
13728
  const contextN = parseInt(options.context, 10);
13481
- console.log(import_chalk21.default.dim(`--- ${filePath}`));
13482
- console.log(import_chalk21.default.dim(`+++ ${filePath} (fixed)`));
13729
+ console.log(import_chalk22.default.dim(`--- ${filePath}`));
13730
+ console.log(import_chalk22.default.dim(`+++ ${filePath} (fixed)`));
13483
13731
  for (const change of result.changes) {
13484
13732
  const lineIdx = change.line - 1;
13485
13733
  const start = Math.max(0, lineIdx - contextN);
13486
13734
  const end = Math.min(originalLines.length, lineIdx + contextN + 1);
13487
- console.log(import_chalk21.default.dim(`@@ -${start + 1},${end - start} +${start + 1},${end - start} @@`));
13735
+ console.log(import_chalk22.default.dim(`@@ -${start + 1},${end - start} +${start + 1},${end - start} @@`));
13488
13736
  for (let i = start; i < end; i++) {
13489
13737
  if (i === lineIdx) {
13490
- console.log(import_chalk21.default.red(`-${originalLines[i]}`));
13491
- console.log(import_chalk21.default.green(`+${fixedLines[i]}`));
13738
+ console.log(import_chalk22.default.red(`-${originalLines[i]}`));
13739
+ console.log(import_chalk22.default.green(`+${fixedLines[i]}`));
13492
13740
  } else {
13493
- console.log(import_chalk21.default.dim(` ${originalLines[i]}`));
13741
+ console.log(import_chalk22.default.dim(` ${originalLines[i]}`));
13494
13742
  }
13495
13743
  }
13496
13744
  }
13497
- console.log(import_chalk21.default.green(`
13745
+ console.log(import_chalk22.default.green(`
13498
13746
  ${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`));
13747
+ console.log(import_chalk22.default.dim(` run: hyv fix ${file} -i to apply`));
13500
13748
  if (options.apply && filePath !== "stdin") {
13501
- const fs24 = require("fs");
13502
- const path23 = require("path");
13749
+ const fs25 = require("fs");
13750
+ const path24 = require("path");
13503
13751
  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)}`));
13752
+ fs25.copyFileSync(filePath, backupPath);
13753
+ fs25.writeFileSync(filePath, result.fixed);
13754
+ console.log(import_chalk22.default.green(` \u2713 Applied. Backup: ${path24.basename(backupPath)}`));
13507
13755
  }
13508
13756
  } catch (error) {
13509
- console.error(import_chalk21.default.red(`Error: ${error.message}`));
13757
+ console.error(import_chalk22.default.red(`Error: ${error.message}`));
13510
13758
  process.exit(1);
13511
13759
  }
13512
13760
  });
13513
13761
  }
13514
13762
 
13515
13763
  // src/commands/rules.ts
13516
- var import_chalk22 = __toESM(require_source());
13764
+ var import_chalk23 = __toESM(require_source());
13517
13765
  init_config();
13518
13766
  var RULE_CATALOG = [
13519
13767
  // AI Overused Words
@@ -13607,7 +13855,7 @@ function registerRulesCommand(program3) {
13607
13855
  for (const id of ids)
13608
13856
  disabledRules.delete(id);
13609
13857
  writeConfig({ ...config, disabled_rules: [...disabledRules] });
13610
- console.log(import_chalk22.default.green(`
13858
+ console.log(import_chalk23.default.green(`
13611
13859
  \u2713 Enabled: ${ids.join(", ")}`));
13612
13860
  return;
13613
13861
  }
@@ -13616,13 +13864,13 @@ function registerRulesCommand(program3) {
13616
13864
  for (const id of ids)
13617
13865
  disabledRules.add(id);
13618
13866
  writeConfig({ ...config, disabled_rules: [...disabledRules] });
13619
- console.log(import_chalk22.default.green(`
13867
+ console.log(import_chalk23.default.green(`
13620
13868
  \u2713 Disabled: ${ids.join(", ")}`));
13621
13869
  return;
13622
13870
  }
13623
13871
  if (options.reset) {
13624
13872
  writeConfig({ ...config, disabled_rules: [] });
13625
- console.log(import_chalk22.default.green("\n\u2713 All rules reset to default (enabled)"));
13873
+ console.log(import_chalk23.default.green("\n\u2713 All rules reset to default (enabled)"));
13626
13874
  return;
13627
13875
  }
13628
13876
  let rules = [...RULE_CATALOG];
@@ -13650,40 +13898,40 @@ function registerRulesCommand(program3) {
13650
13898
  }
13651
13899
  console.log("");
13652
13900
  for (const [cat, catRules] of categories) {
13653
- console.log(import_chalk22.default.bold(` ${cat} rules (${catRules.length})
13901
+ console.log(import_chalk23.default.bold(` ${cat} rules (${catRules.length})
13654
13902
  `));
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"));
13903
+ console.log(import_chalk23.default.dim(" ID Sev AutoFix Status"));
13904
+ 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
13905
  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");
13906
+ const sev = rule.severity === "red" ? import_chalk23.default.red("red") : import_chalk23.default.yellow("yel");
13907
+ const fix = rule.autoFixable ? import_chalk23.default.green(" \u2713") : import_chalk23.default.dim(" \u2717");
13908
+ const enabled2 = disabledRules.has(rule.id) ? import_chalk23.default.red(" \u2717 disabled") : import_chalk23.default.green(" \u2713 enabled");
13661
13909
  const id = rule.id.padEnd(24);
13662
- console.log(` ${import_chalk22.default.dim(id)} ${sev} ${fix} ${enabled2}`);
13910
+ console.log(` ${import_chalk23.default.dim(id)} ${sev} ${fix} ${enabled2}`);
13663
13911
  }
13664
13912
  console.log("");
13665
13913
  }
13666
13914
  const enabled = rules.filter((r) => !disabledRules.has(r.id)).length;
13667
13915
  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
13916
+ console.log(import_chalk23.default.dim(` ${rules.length} rules, ${enabled} enabled, ${disabled} disabled`));
13917
+ console.log(import_chalk23.default.dim(` toggle: hyv rules --disable <id>`));
13918
+ console.log(import_chalk23.default.dim(` reset: hyv rules --reset
13671
13919
  `));
13672
13920
  } catch (error) {
13673
- console.error(import_chalk22.default.red(`Error: ${error.message}`));
13921
+ console.error(import_chalk23.default.red(`Error: ${error.message}`));
13674
13922
  process.exit(1);
13675
13923
  }
13676
13924
  });
13677
13925
  }
13678
13926
 
13679
13927
  // src/commands/batch.ts
13680
- var import_chalk23 = __toESM(require_source());
13681
- var fs15 = __toESM(require("fs"));
13682
- var path16 = __toESM(require("path"));
13928
+ var import_chalk24 = __toESM(require_source());
13929
+ var fs16 = __toESM(require("fs"));
13930
+ var path17 = __toESM(require("path"));
13683
13931
  init_pipeline();
13684
13932
  init_local_profile();
13685
13933
  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) => {
13934
+ 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
13935
  try {
13688
13936
  const profile = await loadProfileForCommand(options.profile);
13689
13937
  const glob = require_index_min();
@@ -13692,26 +13940,30 @@ function registerBatchCommand(program3) {
13692
13940
  nodir: true
13693
13941
  });
13694
13942
  if (files.length === 0) {
13695
- console.log(import_chalk23.default.yellow(`
13943
+ console.log(import_chalk24.default.yellow(`
13696
13944
  No files matching: ${pattern}`));
13697
13945
  return;
13698
13946
  }
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);
13947
+ if (options.fix && options.inPlace) {
13948
+ const confirmed = await confirmDestructiveWrite({
13949
+ yes: options.yes,
13950
+ action: `Apply auto-fixes in-place to ${files.length} file(s)`,
13951
+ target: pattern
13952
+ });
13953
+ if (!confirmed)
13954
+ process.exit(1);
13703
13955
  }
13704
13956
  if (options.format === "text") {
13705
- console.log(import_chalk23.default.dim(`
13957
+ console.log(import_chalk24.default.dim(`
13706
13958
  scanning ${files.length} file${files.length === 1 ? "" : "s"}...
13707
13959
  `));
13708
13960
  }
13709
13961
  const results = [];
13710
13962
  for (const file of files) {
13711
- const absPath = path16.resolve(file);
13712
- if (!fs15.existsSync(absPath))
13963
+ const absPath = path17.resolve(file);
13964
+ if (!fs16.existsSync(absPath))
13713
13965
  continue;
13714
- const text = fs15.readFileSync(absPath, "utf-8");
13966
+ const text = fs16.readFileSync(absPath, "utf-8");
13715
13967
  const result = runPipeline(text, profile, options.fix || false);
13716
13968
  results.push({
13717
13969
  file,
@@ -13722,9 +13974,7 @@ No files matching: ${pattern}`));
13722
13974
  autoFixes: result.changes.length
13723
13975
  });
13724
13976
  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);
13977
+ writeInPlaceWithBackup(absPath, result.fixed);
13728
13978
  }
13729
13979
  }
13730
13980
  if (options.sort === "score") {
@@ -13743,23 +13993,23 @@ No files matching: ${pattern}`));
13743
13993
  }
13744
13994
  } else {
13745
13995
  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");
13996
+ const icon = r.issues > 0 ? import_chalk24.default.red("\u25CF") : import_chalk24.default.green("\u25CB");
13997
+ 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);
13998
+ const issueStr = r.issues > 0 ? import_chalk24.default.red(`${r.issues} issues`) : import_chalk24.default.green("clean");
13749
13999
  console.log(` ${icon} ${r.file.padEnd(40)} ${issueStr.padEnd(20)} score: ${score}`);
13750
14000
  }
13751
14001
  const withIssues = results.filter((r) => r.issues > 0).length;
13752
14002
  const totalIssues = results.reduce((sum, r) => sum + r.issues, 0);
13753
14003
  const worst = results.reduce((min, r) => r.score < min.score ? r : min, results[0]);
13754
- console.log(import_chalk23.default.dim(`
14004
+ console.log(import_chalk24.default.dim(`
13755
14005
  \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`));
14006
+ console.log(import_chalk24.default.dim(` ${results.length} files, ${withIssues} with issues, ${totalIssues} total issues`));
13757
14007
  if (withIssues > 0) {
13758
- console.log(import_chalk23.default.dim(` worst: ${worst.file} (${worst.score}/100)`));
14008
+ console.log(import_chalk24.default.dim(` worst: ${worst.file} (${worst.score}/100)`));
13759
14009
  }
13760
14010
  if (options.fix && options.inPlace) {
13761
14011
  const fixed = results.filter((r) => r.autoFixes > 0);
13762
- console.log(import_chalk23.default.green(`
14012
+ console.log(import_chalk24.default.green(`
13763
14013
  \u2713 ${fixed.reduce((sum, r) => sum + r.autoFixes, 0)} auto-fixes applied across ${fixed.length} files`));
13764
14014
  }
13765
14015
  }
@@ -13771,108 +14021,114 @@ No files matching: ${pattern}`));
13771
14021
  if (belowThreshold)
13772
14022
  process.exit(1);
13773
14023
  } catch (error) {
13774
- console.error(import_chalk23.default.red(`Error: ${error.message}`));
14024
+ console.error(import_chalk24.default.red(`Error: ${error.message}`));
13775
14025
  process.exit(1);
13776
14026
  }
13777
14027
  });
13778
14028
  }
13779
14029
 
13780
14030
  // src/commands/watch.ts
13781
- var import_chalk24 = __toESM(require_source());
13782
- var fs16 = __toESM(require("fs"));
13783
- var path17 = __toESM(require("path"));
14031
+ var import_chalk25 = __toESM(require_source());
14032
+ var fs17 = __toESM(require("fs"));
14033
+ var path18 = __toESM(require("path"));
13784
14034
  init_pipeline();
13785
14035
  init_local_profile();
13786
14036
  function registerWatchCommand(program3) {
13787
14037
  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
14038
  try {
13789
14039
  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}`));
14040
+ const absPath = path18.resolve(file);
14041
+ if (!fs17.existsSync(absPath)) {
14042
+ console.error(import_chalk25.default.red(`File not found: ${absPath}`));
13793
14043
  process.exit(1);
13794
14044
  }
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);
14045
+ if (options.command === "fix") {
14046
+ const confirmed = await confirmDestructiveWrite({
14047
+ yes: options.yes,
14048
+ action: "Auto-fix on every save",
14049
+ target: absPath
14050
+ });
14051
+ if (!confirmed)
14052
+ process.exit(1);
13799
14053
  }
13800
- console.log(import_chalk24.default.dim(`
14054
+ console.log(import_chalk25.default.dim(`
13801
14055
  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"));
14056
+ console.log(import_chalk25.default.dim(`command: ${options.command} debounce: ${options.debounce}ms`));
14057
+ console.log(import_chalk25.default.dim("ctrl+c to stop\n"));
13804
14058
  let debounceTimer = null;
13805
14059
  const debounceMs = parseInt(options.debounce, 10);
13806
14060
  let scanCount = 0;
13807
14061
  let lastScore = 0;
14062
+ let ignoreWatchUntil = 0;
13808
14063
  const runScan = () => {
13809
- const text = fs16.readFileSync(absPath, "utf-8");
14064
+ const text = fs17.readFileSync(absPath, "utf-8");
13810
14065
  const result = runPipeline(text, profile, options.command === "fix");
13811
14066
  scanCount++;
13812
14067
  const now = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
13813
- console.log(import_chalk24.default.dim(`[${now}] saved \u2014 ${options.command}ing...`));
14068
+ console.log(import_chalk25.default.dim(`[${now}] saved \u2014 ${options.command}ing...`));
13814
14069
  if (options.command === "score") {
13815
14070
  const score = result.score;
13816
- const color = score < 60 ? import_chalk24.default.red : score < 80 ? import_chalk24.default.yellow : import_chalk24.default.green;
14071
+ const color = score < 60 ? import_chalk25.default.red : score < 80 ? import_chalk25.default.yellow : import_chalk25.default.green;
13817
14072
  console.log(` ${color(score + "/100")}`);
13818
14073
  if (lastScore > 0 && score !== lastScore) {
13819
14074
  const delta = score - lastScore;
13820
- console.log(import_chalk24.default.dim(` ${delta > 0 ? "\u2191" : "\u2193"} ${Math.abs(delta)} from last scan`));
14075
+ console.log(import_chalk25.default.dim(` ${delta > 0 ? "\u2191" : "\u2193"} ${Math.abs(delta)} from last scan`));
13821
14076
  }
13822
14077
  lastScore = score;
13823
14078
  } else if (options.command === "fix") {
13824
14079
  if (result.changes.length > 0) {
13825
14080
  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));
14081
+ 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
14082
  }
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`));
14083
+ ignoreWatchUntil = Date.now() + debounceMs + 200;
14084
+ writeInPlaceWithBackup(absPath, result.fixed);
14085
+ console.log(import_chalk25.default.green(` \u2713 ${result.changes.length} auto-fixes applied`));
13832
14086
  } else {
13833
- console.log(import_chalk24.default.green(" \u2713 no auto-fixable issues"));
14087
+ console.log(import_chalk25.default.green(" \u2713 no auto-fixable issues"));
13834
14088
  }
13835
14089
  } else {
13836
14090
  if (result.stats.totalSignals === 0) {
13837
- console.log(import_chalk24.default.green(` \u2713 clean \u2014 score: ${result.score}/100`));
14091
+ console.log(import_chalk25.default.green(` \u2713 clean \u2014 score: ${result.score}/100`));
13838
14092
  } else {
13839
- console.log(import_chalk24.default.yellow(` ${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow) score: ${result.score}/100`));
14093
+ console.log(import_chalk25.default.yellow(` ${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow) score: ${result.score}/100`));
13840
14094
  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");
14095
+ const sev = signal.severity === "red" ? import_chalk25.default.red("\u25CF") : import_chalk25.default.yellow("\u25CB");
13842
14096
  console.log(` ${sev} line ${signal.line}: ${signal.id} \u2014 ${signal.suggestion}`);
13843
14097
  }
13844
14098
  if (result.signalMap.signals.length > 3) {
13845
- console.log(import_chalk24.default.dim(` ... and ${result.signalMap.signals.length - 3} more`));
14099
+ console.log(import_chalk25.default.dim(` ... and ${result.signalMap.signals.length - 3} more`));
13846
14100
  }
13847
14101
  }
13848
14102
  }
13849
14103
  console.log("");
13850
14104
  };
13851
14105
  runScan();
13852
- fs16.watch(absPath, (eventType) => {
14106
+ fs17.watch(absPath, (eventType) => {
13853
14107
  if (eventType !== "change")
13854
14108
  return;
14109
+ if (Date.now() < ignoreWatchUntil)
14110
+ return;
13855
14111
  if (debounceTimer)
13856
14112
  clearTimeout(debounceTimer);
13857
14113
  debounceTimer = setTimeout(runScan, debounceMs);
13858
14114
  });
13859
14115
  process.on("SIGINT", () => {
13860
- console.log(import_chalk24.default.dim(`
14116
+ console.log(import_chalk25.default.dim(`
13861
14117
  ${scanCount} scans completed. exiting.`));
13862
14118
  process.exit(0);
13863
14119
  });
13864
14120
  await new Promise(() => {
13865
14121
  });
13866
14122
  } catch (error) {
13867
- console.error(import_chalk24.default.red(`Error: ${error.message}`));
14123
+ console.error(import_chalk25.default.red(`Error: ${error.message}`));
13868
14124
  process.exit(1);
13869
14125
  }
13870
14126
  });
13871
14127
  }
13872
14128
 
13873
14129
  // src/commands/demo.ts
13874
- var import_chalk25 = __toESM(require_source());
13875
- var fs17 = __toESM(require("fs"));
14130
+ var import_chalk26 = __toESM(require_source());
14131
+ var fs18 = __toESM(require("fs"));
13876
14132
  var SAMPLES = {
13877
14133
  linkedin: {
13878
14134
  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 +14218,46 @@ function registerDemoCommand(program3) {
13962
14218
  const style = options.clean ? "clean" : options.style;
13963
14219
  const sample = SAMPLES[style];
13964
14220
  if (!sample) {
13965
- console.error(import_chalk25.default.red(`Unknown style: ${options.style}. Valid: ${Object.keys(SAMPLES).filter((k) => k !== "clean").join(", ")}`));
14221
+ console.error(import_chalk26.default.red(`Unknown style: ${options.style}. Valid: ${Object.keys(SAMPLES).filter((k) => k !== "clean").join(", ")}`));
13966
14222
  process.exit(1);
13967
14223
  }
13968
14224
  if (options.output) {
13969
14225
  const outputPath = require("path").resolve(options.output);
13970
- fs17.writeFileSync(outputPath, sample.text);
13971
- console.log(import_chalk25.default.green(`
14226
+ fs18.writeFileSync(outputPath, sample.text);
14227
+ console.log(import_chalk26.default.green(`
13972
14228
  \u2713 Sample written to ${outputPath}`));
13973
14229
  if (sample.patterns.length > 0) {
13974
- console.log(import_chalk25.default.dim(` Contains ${sample.patterns.length} AI patterns: ${sample.patterns.slice(0, 5).join(", ")}...`));
14230
+ console.log(import_chalk26.default.dim(` Contains ${sample.patterns.length} AI patterns: ${sample.patterns.slice(0, 5).join(", ")}...`));
13975
14231
  }
13976
- console.log(import_chalk25.default.dim(`
14232
+ console.log(import_chalk26.default.dim(`
13977
14233
  Scan it: hyv scan ${options.output}`));
13978
- console.log(import_chalk25.default.dim(` Fix it: hyv fix ${options.output}`));
14234
+ console.log(import_chalk26.default.dim(` Fix it: hyv fix ${options.output}`));
13979
14235
  } else {
13980
- console.log(import_chalk25.default.bold(`
14236
+ console.log(import_chalk26.default.bold(`
13981
14237
  \u2500\u2500\u2500 sample (${style}) \u2500\u2500\u2500
13982
14238
  `));
13983
14239
  console.log(sample.text);
13984
- console.log(import_chalk25.default.dim(`
14240
+ console.log(import_chalk26.default.dim(`
13985
14241
  \u2500\u2500\u2500 end \u2500\u2500\u2500`));
13986
14242
  if (sample.patterns.length > 0) {
13987
- console.log(import_chalk25.default.dim(`
14243
+ console.log(import_chalk26.default.dim(`
13988
14244
  ${sample.patterns.length} AI patterns embedded: ${sample.patterns.slice(0, 8).join(", ")}...`));
13989
14245
  }
13990
- console.log(import_chalk25.default.dim(`
14246
+ console.log(import_chalk26.default.dim(`
13991
14247
  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
14248
+ console.log(import_chalk26.default.dim(` Scan it: hyv scan demo.md`));
14249
+ console.log(import_chalk26.default.dim(` Fix it: hyv fix demo.md
13994
14250
  `));
13995
14251
  }
13996
14252
  } catch (error) {
13997
- console.error(import_chalk25.default.red(`Error: ${error.message}`));
14253
+ console.error(import_chalk26.default.red(`Error: ${error.message}`));
13998
14254
  process.exit(1);
13999
14255
  }
14000
14256
  });
14001
14257
  }
14002
14258
 
14003
14259
  // src/commands/open.ts
14004
- var import_chalk26 = __toESM(require_source());
14260
+ var import_chalk27 = __toESM(require_source());
14005
14261
  var PAGES = {
14006
14262
  dashboard: "https://holdyourvoice.com/dashboard",
14007
14263
  profiles: "https://holdyourvoice.com/dashboard",
@@ -14018,12 +14274,12 @@ function registerOpenCommand(program3) {
14018
14274
  return;
14019
14275
  }
14020
14276
  const open3 = (await Promise.resolve().then(() => __toESM(require_open()))).default;
14021
- console.log(import_chalk26.default.dim(`
14277
+ console.log(import_chalk27.default.dim(`
14022
14278
  Opening ${url}...`));
14023
14279
  await open3(url);
14024
- console.log(import_chalk26.default.green(" \u2713 Opened in browser\n"));
14280
+ console.log(import_chalk27.default.green(" \u2713 Opened in browser\n"));
14025
14281
  } catch (error) {
14026
- console.error(import_chalk26.default.red(`Error: ${error.message}`));
14282
+ console.error(import_chalk27.default.red(`Error: ${error.message}`));
14027
14283
  process.exit(1);
14028
14284
  }
14029
14285
  });
@@ -14033,35 +14289,35 @@ function registerOpenCommand(program3) {
14033
14289
  init_welcome();
14034
14290
 
14035
14291
  // src/lib/onboarding.ts
14036
- var fs18 = __toESM(require("fs"));
14037
- var path18 = __toESM(require("path"));
14292
+ var fs19 = __toESM(require("fs"));
14293
+ var path19 = __toESM(require("path"));
14038
14294
  var os6 = __toESM(require("os"));
14039
- var hyvDir = path18.join(os6.homedir(), ".hyv");
14040
- var onboardingFile = path18.join(hyvDir, "onboarding-complete.json");
14295
+ var hyvDir = path19.join(os6.homedir(), ".hyv");
14296
+ var onboardingFile = path19.join(hyvDir, "onboarding-complete.json");
14041
14297
  function hasCompletedOnboarding() {
14042
14298
  try {
14043
- return fs18.existsSync(onboardingFile);
14299
+ return fs19.existsSync(onboardingFile);
14044
14300
  } catch {
14045
14301
  return false;
14046
14302
  }
14047
14303
  }
14048
14304
  function markOnboardingComplete(version) {
14049
- if (!fs18.existsSync(hyvDir))
14050
- fs18.mkdirSync(hyvDir, { recursive: true });
14051
- fs18.writeFileSync(
14305
+ if (!fs19.existsSync(hyvDir))
14306
+ fs19.mkdirSync(hyvDir, { recursive: true });
14307
+ fs19.writeFileSync(
14052
14308
  onboardingFile,
14053
14309
  JSON.stringify({ version, completed_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
14054
14310
  );
14055
14311
  }
14056
14312
 
14057
14313
  // src/commands/welcome.ts
14058
- var fs19 = __toESM(require("fs"));
14059
- var path19 = __toESM(require("path"));
14314
+ var fs20 = __toESM(require("fs"));
14315
+ var path20 = __toESM(require("path"));
14060
14316
  function registerWelcomeCommand(program3) {
14061
14317
  const pkgVersion3 = (() => {
14062
14318
  try {
14063
- const pkgPath3 = path19.resolve(__dirname, "..", "package.json");
14064
- return JSON.parse(fs19.readFileSync(pkgPath3, "utf-8")).version;
14319
+ const pkgPath3 = path20.resolve(__dirname, "..", "package.json");
14320
+ return JSON.parse(fs20.readFileSync(pkgPath3, "utf-8")).version;
14065
14321
  } catch {
14066
14322
  return "0.0.0";
14067
14323
  }
@@ -14085,7 +14341,7 @@ function getWelcomeText() {
14085
14341
  }
14086
14342
 
14087
14343
  // src/commands/content.ts
14088
- var import_chalk27 = __toESM(require_source());
14344
+ var import_chalk28 = __toESM(require_source());
14089
14345
  init_free_paid();
14090
14346
  var TEMPLATES = {
14091
14347
  cursor: {
@@ -14159,68 +14415,68 @@ ${COMMUNITY_URL}`
14159
14415
  function registerContentCommand(program3) {
14160
14416
  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
14417
  if (opts.list) {
14162
- console.log(import_chalk27.default.bold("\nhyv content templates\n"));
14418
+ console.log(import_chalk28.default.bold("\nhyv content templates\n"));
14163
14419
  for (const [key, t2] of Object.entries(TEMPLATES)) {
14164
- console.log(import_chalk27.default.dim(` ${key}`) + ` \u2014 ${t2.title}`);
14420
+ console.log(import_chalk28.default.dim(` ${key}`) + ` \u2014 ${t2.title}`);
14165
14421
  }
14166
14422
  console.log("");
14167
14423
  return;
14168
14424
  }
14169
14425
  const t = TEMPLATES[topic] || TEMPLATES.cursor;
14170
- console.log(import_chalk27.default.bold(`
14426
+ console.log(import_chalk28.default.bold(`
14171
14427
  ${t.title}
14172
14428
  `));
14173
14429
  console.log(t.body);
14174
- console.log(import_chalk27.default.dim("\nBlog: https://holdyourvoice.com/blog\n"));
14430
+ console.log(import_chalk28.default.dim("\nBlog: https://holdyourvoice.com/blog\n"));
14175
14431
  });
14176
14432
  }
14177
14433
 
14178
14434
  // src/commands/upgrade.ts
14179
- var import_chalk28 = __toESM(require_source());
14180
- var import_child_process2 = require("child_process");
14435
+ var import_chalk29 = __toESM(require_source());
14436
+ var import_child_process3 = require("child_process");
14181
14437
  init_version();
14182
14438
  function registerUpgradeCommand(program3) {
14183
14439
  program3.command("upgrade").description("Upgrade to the latest @holdyourvoice/hyv").option("--check", "Only check if an update is available").action(async (opts) => {
14184
14440
  const current = getCliVersion();
14185
- console.log(import_chalk28.default.dim(`
14441
+ console.log(import_chalk29.default.dim(`
14186
14442
  Current: ${getEngineLabel()}
14187
14443
  `));
14188
14444
  let latest = current;
14189
14445
  try {
14190
- const out = (0, import_child_process2.execSync)("npm view @holdyourvoice/hyv version", { encoding: "utf-8", timeout: 15e3 });
14446
+ const out = (0, import_child_process3.execSync)("npm view @holdyourvoice/hyv version", { encoding: "utf-8", timeout: 15e3 });
14191
14447
  latest = out.trim();
14192
14448
  } catch {
14193
- console.log(import_chalk28.default.yellow("Could not reach npm registry. Try: npm i -g @holdyourvoice/hyv@latest"));
14449
+ console.log(import_chalk29.default.yellow("Could not reach npm registry. Try: npm i -g @holdyourvoice/hyv@latest"));
14194
14450
  return;
14195
14451
  }
14196
14452
  const cmp = compareSemver(current, latest);
14197
14453
  if (cmp === 0) {
14198
- console.log(import_chalk28.default.green("\u2713 You are on the latest version."));
14454
+ console.log(import_chalk29.default.green("\u2713 You are on the latest version."));
14199
14455
  return;
14200
14456
  }
14201
14457
  if (cmp > 0) {
14202
- console.log(import_chalk28.default.green(`\u2713 You are ahead of npm (published: ${latest}).`));
14458
+ console.log(import_chalk29.default.green(`\u2713 You are ahead of npm (published: ${latest}).`));
14203
14459
  return;
14204
14460
  }
14205
- console.log(import_chalk28.default.cyan(`Update available: ${current} \u2192 ${latest}`));
14461
+ console.log(import_chalk29.default.cyan(`Update available: ${current} \u2192 ${latest}`));
14206
14462
  if (opts.check) {
14207
- console.log(import_chalk28.default.dim("\nRun: hyv upgrade or npm i -g @holdyourvoice/hyv@latest\n"));
14463
+ console.log(import_chalk29.default.dim("\nRun: hyv upgrade or npm i -g @holdyourvoice/hyv@latest\n"));
14208
14464
  return;
14209
14465
  }
14210
- console.log(import_chalk28.default.dim("Installing..."));
14466
+ console.log(import_chalk29.default.dim("Installing..."));
14211
14467
  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"));
14468
+ (0, import_child_process3.execSync)("npm i -g @holdyourvoice/hyv@latest", { stdio: "inherit" });
14469
+ console.log(import_chalk29.default.green("\n\u2713 Upgraded successfully. Restart your terminal.\n"));
14214
14470
  } catch {
14215
- console.log(import_chalk28.default.red("\nUpgrade failed. Run manually: npm i -g @holdyourvoice/hyv@latest\n"));
14471
+ console.log(import_chalk29.default.red("\nUpgrade failed. Run manually: npm i -g @holdyourvoice/hyv@latest\n"));
14216
14472
  process.exit(1);
14217
14473
  }
14218
14474
  });
14219
14475
  }
14220
14476
 
14221
14477
  // src/mcp.ts
14222
- var fs20 = __toESM(require("fs"));
14223
- var path20 = __toESM(require("path"));
14478
+ var fs21 = __toESM(require("fs"));
14479
+ var path21 = __toESM(require("path"));
14224
14480
  var os7 = __toESM(require("os"));
14225
14481
  init_classifier();
14226
14482
  init_autofix();
@@ -14232,20 +14488,20 @@ init_welcome();
14232
14488
  init_config();
14233
14489
  init_telemetry();
14234
14490
  init_access();
14235
- var VOICE_MD = path20.join(os7.homedir(), ".hyv", "voice.md");
14491
+ var VOICE_MD = path21.join(os7.homedir(), ".hyv", "voice.md");
14236
14492
  var MAX_RESPONSE_CHARS = 12e4;
14237
14493
  var MAX_SCAN_FILE_BYTES = 2 * 1024 * 1024;
14238
14494
  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) {
14495
+ const cwdReal = fs21.realpathSync(process.cwd());
14496
+ const resolved = fs21.realpathSync(path21.resolve(filePath));
14497
+ if (!resolved.startsWith(cwdReal + path21.sep) && resolved !== cwdReal) {
14242
14498
  throw new Error(`file must be in the current working directory (${cwdReal})`);
14243
14499
  }
14244
- const stat = fs20.statSync(resolved);
14500
+ const stat = fs21.statSync(resolved);
14245
14501
  if (stat.size > MAX_SCAN_FILE_BYTES) {
14246
14502
  throw new Error(`file too large (max ${MAX_SCAN_FILE_BYTES} bytes)`);
14247
14503
  }
14248
- return fs20.readFileSync(resolved, "utf-8");
14504
+ return fs21.readFileSync(resolved, "utf-8");
14249
14505
  }
14250
14506
  function toolResultPayload(result) {
14251
14507
  const isError = result.startsWith("Error:") || result.startsWith("Unknown tool:");
@@ -14254,10 +14510,10 @@ function toolResultPayload(result) {
14254
14510
  ...isError ? { isError: true } : {}
14255
14511
  };
14256
14512
  }
14257
- var pkgPath = path20.resolve(__dirname, "..", "package.json");
14513
+ var pkgPath = path21.resolve(__dirname, "..", "package.json");
14258
14514
  var pkgVersion = (() => {
14259
14515
  try {
14260
- return JSON.parse(fs20.readFileSync(pkgPath, "utf-8")).version;
14516
+ return JSON.parse(fs21.readFileSync(pkgPath, "utf-8")).version;
14261
14517
  } catch {
14262
14518
  return "2.7.1";
14263
14519
  }
@@ -14831,7 +15087,7 @@ async function handleRequest(line) {
14831
15087
  }
14832
15088
  async function startMcpServer() {
14833
15089
  const access = await getAccessState().catch(() => null);
14834
- if (fs20.existsSync(VOICE_MD)) {
15090
+ if (fs21.existsSync(VOICE_MD)) {
14835
15091
  mcpLog("info", `voice profile: ${VOICE_MD}`);
14836
15092
  } else {
14837
15093
  mcpLog("info", "free local engine ready \u2014 no voice profile yet");
@@ -14863,228 +15119,142 @@ async function startMcpServer() {
14863
15119
  }
14864
15120
 
14865
15121
  // src/lib/mcp-setup.ts
14866
- var import_chalk29 = __toESM(require_source());
14867
- var fs21 = __toESM(require("fs"));
14868
- var path21 = __toESM(require("path"));
15122
+ var import_chalk30 = __toESM(require_source());
15123
+ var fs22 = __toESM(require("fs"));
15124
+ var path22 = __toESM(require("path"));
14869
15125
  var os8 = __toESM(require("os"));
14870
- var import_child_process3 = require("child_process");
14871
15126
  init_version();
14872
15127
  var HOME2 = os8.homedir();
14873
15128
  var IS_WIN2 = process.platform === "win32";
14874
15129
  function claudeDesktopConfigPath() {
14875
15130
  if (IS_WIN2)
14876
- return path21.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
15131
+ return path22.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
14877
15132
  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);
15133
+ return path22.join(HOME2, ".config", "Claude", "claude_desktop_config.json");
15134
+ return path22.join(HOME2, "Library", "Application Support", "Claude", "claude_desktop_config.json");
14891
15135
  }
14892
15136
  function printMcpSetup() {
14893
- console.log(import_chalk29.default.bold("\nhold your voice \u2014 mcp setup\n"));
14894
- console.log(import_chalk29.default.dim(` ${getEngineLabel()}
15137
+ console.log(import_chalk30.default.bold("\nhold your voice \u2014 mcp setup\n"));
15138
+ console.log(import_chalk30.default.dim(` ${getEngineLabel()}
14895
15139
  `));
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()}
15140
+ console.log(import_chalk30.default.bold("Claude Desktop"));
15141
+ console.log(import_chalk30.default.dim(" Add to claude_desktop_config.json \u2192 mcpServers.hyv:"));
15142
+ console.log(import_chalk30.default.cyan(` ${mcpServerSnippet().split("\n").join("\n ")}`));
15143
+ console.log(import_chalk30.default.dim(` Config: ${claudeDesktopConfigPath()}
14900
15144
  `));
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
- });
15145
+ console.log(import_chalk30.default.bold("Cursor"));
15146
+ console.log(import_chalk30.default.dim(" MCP: ~/.cursor/mcp.json \u2192 mcpServers.hyv (same JSON as above)"));
15147
+ console.log(import_chalk30.default.dim(" Rule: ~/.cursor/rules/hyv.mdc (alwaysApply \u2014 auto via postinstall)\n"));
15148
+ console.log(import_chalk30.default.bold("Claude Code"));
15149
+ console.log(import_chalk30.default.dim(" Command: ~/.claude/commands/hyv.md"));
15150
+ console.log(import_chalk30.default.dim(" Skill: ~/.claude/skills/hold-your-voice/SKILL.md\n"));
15151
+ console.log(import_chalk30.default.bold("Windsurf"));
15152
+ console.log(import_chalk30.default.dim(" Rule: ~/.windsurf/rules/hyv.md (trigger: always_on)\n"));
15153
+ console.log(import_chalk30.default.bold("Codex"));
15154
+ console.log(import_chalk30.default.dim(" Instructions: ~/.codex/AGENTS.md (merged on install)\n"));
15155
+ console.log(import_chalk30.default.bold("Command Code"));
15156
+ console.log(import_chalk30.default.dim(" Skill: ~/.commandcode/skills/hyv/SKILL.md\n"));
15157
+ console.log(import_chalk30.default.bold("ChatGPT"));
15158
+ console.log(import_chalk30.default.dim(" hyv mcp --setup-chatgpt\n"));
15159
+ console.log(import_chalk30.default.bold("Auto-configure"));
15160
+ console.log(import_chalk30.default.dim(" hyv doctor --fix-agents"));
15161
+ console.log(import_chalk30.default.dim(" HYV_AUTO_CONFIGURE_AGENTS=0 npm i -g @holdyourvoice/hyv (skip)\n"));
15162
+ console.log(import_chalk30.default.bold("Verify"));
15163
+ console.log(import_chalk30.default.dim(" hyv mcp --test"));
15164
+ console.log(import_chalk30.default.dim(" HYV_TELEMETRY=1 hyv mcp (optional usage logging to ~/.hyv/telemetry/)\n"));
14995
15165
  }
14996
15166
  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()}
15167
+ console.log(import_chalk30.default.bold("\nhold your voice \u2014 mcp self-test\n"));
15168
+ console.log(import_chalk30.default.dim(` ${getEngineLabel()}
14999
15169
  `));
15000
15170
  let passed = 0;
15001
15171
  let failed = 0;
15002
15172
  const tools = getMcpToolNames();
15003
15173
  if (tools.length >= 10) {
15004
- console.log(import_chalk29.default.green(` \u2713 ${tools.length} MCP tools registered`));
15174
+ console.log(import_chalk30.default.green(` \u2713 ${tools.length} MCP tools registered`));
15005
15175
  passed++;
15006
15176
  } else {
15007
- console.log(import_chalk29.default.red(` \u2717 expected 10+ tools, got ${tools.length}`));
15177
+ console.log(import_chalk30.default.red(` \u2717 expected 10+ tools, got ${tools.length}`));
15008
15178
  failed++;
15009
15179
  }
15010
15180
  const required = ["hyv_welcome", "hyv_scan", "hyv_analyze", "hyv_clean", "hyv_validate", "hyv_list_free_tools"];
15011
15181
  for (const name of required) {
15012
15182
  if (tools.includes(name)) {
15013
- console.log(import_chalk29.default.green(` \u2713 tool: ${name}`));
15183
+ console.log(import_chalk30.default.green(` \u2713 tool: ${name}`));
15014
15184
  passed++;
15015
15185
  } else {
15016
- console.log(import_chalk29.default.red(` \u2717 missing tool: ${name}`));
15186
+ console.log(import_chalk30.default.red(` \u2717 missing tool: ${name}`));
15017
15187
  failed++;
15018
15188
  }
15019
15189
  }
15020
15190
  try {
15021
15191
  const welcome = await invokeMcpTool("hyv_welcome", {});
15022
15192
  if (welcome.includes("Hold Your Voice") || welcome.includes("hold your voice")) {
15023
- console.log(import_chalk29.default.green(" \u2713 hyv_welcome responds"));
15193
+ console.log(import_chalk30.default.green(" \u2713 hyv_welcome responds"));
15024
15194
  passed++;
15025
15195
  } else {
15026
- console.log(import_chalk29.default.red(" \u2717 hyv_welcome unexpected output"));
15196
+ console.log(import_chalk30.default.red(" \u2717 hyv_welcome unexpected output"));
15027
15197
  failed++;
15028
15198
  }
15029
15199
  } catch (e) {
15030
- console.log(import_chalk29.default.red(` \u2717 hyv_welcome failed: ${e.message}`));
15200
+ console.log(import_chalk30.default.red(` \u2717 hyv_welcome failed: ${e.message}`));
15031
15201
  failed++;
15032
15202
  }
15033
15203
  try {
15034
15204
  const demo = await invokeMcpTool("hyv_demo", {});
15035
15205
  if (demo.includes("Score") || demo.includes("issues")) {
15036
- console.log(import_chalk29.default.green(" \u2713 hyv_demo pipeline works"));
15206
+ console.log(import_chalk30.default.green(" \u2713 hyv_demo pipeline works"));
15037
15207
  passed++;
15038
15208
  } else {
15039
- console.log(import_chalk29.default.red(" \u2717 hyv_demo unexpected output"));
15209
+ console.log(import_chalk30.default.red(" \u2717 hyv_demo unexpected output"));
15040
15210
  failed++;
15041
15211
  }
15042
15212
  } catch (e) {
15043
- console.log(import_chalk29.default.red(` \u2717 hyv_demo failed: ${e.message}`));
15213
+ console.log(import_chalk30.default.red(` \u2717 hyv_demo failed: ${e.message}`));
15044
15214
  failed++;
15045
15215
  }
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"));
15216
+ const voiceMd = path22.join(HOME2, ".hyv", "voice.md");
15217
+ if (fs22.existsSync(voiceMd)) {
15218
+ console.log(import_chalk30.default.green(" \u2713 voice profile found"));
15049
15219
  passed++;
15050
15220
  } else {
15051
- console.log(import_chalk29.default.dim(" - no voice.md (free local engine still works)"));
15221
+ console.log(import_chalk30.default.dim(" - no voice.md (free local engine still works)"));
15052
15222
  }
15053
15223
  if (getDemoText().length > 100) {
15054
- console.log(import_chalk29.default.green(" \u2713 demo content available"));
15224
+ console.log(import_chalk30.default.green(" \u2713 demo content available"));
15055
15225
  passed++;
15056
15226
  } else {
15057
- console.log(import_chalk29.default.red(" \u2717 demo content missing"));
15227
+ console.log(import_chalk30.default.red(" \u2717 demo content missing"));
15058
15228
  failed++;
15059
15229
  }
15060
15230
  try {
15061
15231
  const stdio = await testMcpStdioSubprocess();
15062
15232
  if (stdio.ok && (stdio.toolCount || 0) >= 10) {
15063
- console.log(import_chalk29.default.green(` \u2713 stdio MCP subprocess responds (${stdio.toolCount} tools)`));
15233
+ console.log(import_chalk30.default.green(` \u2713 stdio MCP subprocess responds (${stdio.toolCount} tools)`));
15064
15234
  passed++;
15065
15235
  } else if (stdio.ok) {
15066
- console.log(import_chalk29.default.yellow(` ! stdio MCP subprocess ok but only ${stdio.toolCount || 0} tools`));
15236
+ console.log(import_chalk30.default.yellow(` ! stdio MCP subprocess ok but only ${stdio.toolCount || 0} tools`));
15067
15237
  failed++;
15068
15238
  } else {
15069
- console.log(import_chalk29.default.red(" \u2717 stdio MCP subprocess failed"));
15239
+ console.log(import_chalk30.default.red(" \u2717 stdio MCP subprocess failed"));
15070
15240
  failed++;
15071
15241
  }
15072
15242
  } catch (e) {
15073
- console.log(import_chalk29.default.red(` \u2717 stdio MCP subprocess failed: ${e.message}`));
15243
+ console.log(import_chalk30.default.red(` \u2717 stdio MCP subprocess failed: ${e.message}`));
15074
15244
  failed++;
15075
15245
  }
15076
15246
  console.log("");
15077
15247
  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"));
15248
+ console.log(import_chalk30.default.green(`\u2713 all checks passed (${passed})`));
15249
+ console.log(import_chalk30.default.dim("\nStart server: hyv mcp\n"));
15080
15250
  return true;
15081
15251
  }
15082
- console.log(import_chalk29.default.yellow(`! ${failed} check(s) failed, ${passed} passed`));
15252
+ console.log(import_chalk30.default.yellow(`! ${failed} check(s) failed, ${passed} passed`));
15083
15253
  return false;
15084
15254
  }
15085
15255
 
15086
15256
  // src/commands/export.ts
15087
- var fs22 = __toESM(require("fs"));
15257
+ var fs23 = __toESM(require("fs"));
15088
15258
  init_api();
15089
15259
  var FORMATS = {
15090
15260
  claude: {
@@ -15171,7 +15341,7 @@ async function exportCommand(format, opts) {
15171
15341
  const prompt = fmt.wrap(detail.profile, detail.body);
15172
15342
  if (opts.output) {
15173
15343
  const outFile = opts.output.replace("{name}", profile.slug || profile.name);
15174
- fs22.writeFileSync(outFile, prompt);
15344
+ fs23.writeFileSync(outFile, prompt);
15175
15345
  results.push({ profile: profile.name, file: outFile });
15176
15346
  } else {
15177
15347
  results.push({ profile: profile.name, prompt });
@@ -15208,13 +15378,13 @@ async function exportCommand(format, opts) {
15208
15378
  // src/index.ts
15209
15379
  init_access();
15210
15380
  init_welcome();
15211
- var fs23 = __toESM(require("fs"));
15212
- var path22 = __toESM(require("path"));
15381
+ var fs24 = __toESM(require("fs"));
15382
+ var path23 = __toESM(require("path"));
15213
15383
  var program2 = new Command();
15214
- var pkgPath2 = path22.resolve(__dirname, "..", "package.json");
15384
+ var pkgPath2 = path23.resolve(__dirname, "..", "package.json");
15215
15385
  var pkgVersion2 = (() => {
15216
15386
  try {
15217
- return JSON.parse(fs23.readFileSync(pkgPath2, "utf-8")).version;
15387
+ return JSON.parse(fs24.readFileSync(pkgPath2, "utf-8")).version;
15218
15388
  } catch {
15219
15389
  return "2.7.1";
15220
15390
  }
@@ -15258,15 +15428,15 @@ program2.command("mcp").description("Start MCP server (for Claude Desktop and ot
15258
15428
  return;
15259
15429
  }
15260
15430
  if (opts.setupChatgpt) {
15261
- console.log(import_chalk30.default.bold("\nhold your voice \u2014 chatgpt setup\n"));
15431
+ console.log(import_chalk31.default.bold("\nhold your voice \u2014 chatgpt setup\n"));
15262
15432
  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"));
15433
+ console.log(import_chalk31.default.dim(" 1. Go to ") + import_chalk31.default.cyan("https://chatgpt.com/#settings/Connectors"));
15434
+ console.log(import_chalk31.default.dim(" 2. Add a new connector"));
15435
+ console.log(import_chalk31.default.dim(" 3. For local MCP, use: ") + import_chalk31.default.cyan("hyv mcp"));
15436
+ console.log(import_chalk31.default.dim(" 4. ChatGPT Desktop supports stdio MCP servers"));
15267
15437
  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."));
15438
+ console.log(import_chalk31.default.dim("Note: The remote HTTP MCP endpoint is not yet available."));
15439
+ console.log(import_chalk31.default.dim("Use the local stdio MCP server with Claude Desktop or Claude Code instead."));
15270
15440
  return;
15271
15441
  }
15272
15442
  startMcpServer();