@holdyourvoice/hyv 2.8.9 → 2.9.0

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 path24 = require("node:path");
977
- var fs25 = require("node:fs");
976
+ var path25 = require("node:path");
977
+ var fs26 = 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 = path24.resolve(baseDir, baseName);
1920
- if (fs25.existsSync(localBin))
1919
+ const localBin = path25.resolve(baseDir, baseName);
1920
+ if (fs26.existsSync(localBin))
1921
1921
  return localBin;
1922
- if (sourceExt.includes(path24.extname(baseName)))
1922
+ if (sourceExt.includes(path25.extname(baseName)))
1923
1923
  return void 0;
1924
1924
  const foundExt = sourceExt.find(
1925
- (ext) => fs25.existsSync(`${localBin}${ext}`)
1925
+ (ext) => fs26.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 = fs25.realpathSync(this._scriptPath);
1938
+ resolvedScriptPath = fs26.realpathSync(this._scriptPath);
1939
1939
  } catch (err) {
1940
1940
  resolvedScriptPath = this._scriptPath;
1941
1941
  }
1942
- executableDir = path24.resolve(
1943
- path24.dirname(resolvedScriptPath),
1942
+ executableDir = path25.resolve(
1943
+ path25.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 = path24.basename(
1950
+ const legacyName = path25.basename(
1951
1951
  this._scriptPath,
1952
- path24.extname(this._scriptPath)
1952
+ path25.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(path24.extname(executableFile));
1963
+ launchWithNode = sourceExt.includes(path25.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 = path24.basename(filename, path24.extname(filename));
2820
+ this._name = path25.basename(filename, path25.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(path25) {
2835
- if (path25 === void 0)
2834
+ executableDir(path26) {
2835
+ if (path26 === void 0)
2836
2836
  return this._executableDir;
2837
- this._executableDir = path25;
2837
+ this._executableDir = path26;
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 path24 = [graph[toModel].parent, toModel];
3937
+ const path25 = [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
- path24.unshift(graph[cur].parent);
3941
+ path25.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 = path24;
3945
+ fn.conversion = path25;
3946
3946
  return fn;
3947
3947
  }
3948
3948
  module2.exports = function(fromModel) {
@@ -4182,7 +4182,7 @@ var require_has_flag = __commonJS({
4182
4182
  var require_supports_color = __commonJS({
4183
4183
  "node_modules/supports-color/index.js"(exports2, module2) {
4184
4184
  "use strict";
4185
- var os9 = require("os");
4185
+ var os10 = require("os");
4186
4186
  var tty = require("tty");
4187
4187
  var hasFlag = require_has_flag();
4188
4188
  var { env } = process;
@@ -4230,7 +4230,7 @@ var require_supports_color = __commonJS({
4230
4230
  return min;
4231
4231
  }
4232
4232
  if (process.platform === "win32") {
4233
- const osRelease = os9.release().split(".");
4233
+ const osRelease = os10.release().split(".");
4234
4234
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
4235
4235
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
4236
4236
  }
@@ -4381,14 +4381,14 @@ var require_templates = __commonJS({
4381
4381
  }
4382
4382
  return results;
4383
4383
  }
4384
- function buildStyle(chalk32, styles) {
4384
+ function buildStyle(chalk33, 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 = chalk32;
4391
+ let current = chalk33;
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 = (chalk32, temporary) => {
4403
+ module2.exports = (chalk33, 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(chalk32, styles)(string));
4413
+ chunks.push(styles.length === 0 ? string : buildStyle(chalk33, 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(chalk32, styles)(chunk.join("")));
4419
+ chunks.push(buildStyle(chalk33, 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 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 = () => {
4467
+ const chalk34 = {};
4468
+ applyOptions(chalk34, options);
4469
+ chalk34.template = (...arguments_) => chalkTag(chalk34.template, ...arguments_);
4470
+ Object.setPrototypeOf(chalk34, Chalk.prototype);
4471
+ Object.setPrototypeOf(chalk34.template, chalk34);
4472
+ chalk34.template.constructor = () => {
4473
4473
  throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.");
4474
4474
  };
4475
- chalk33.template.Instance = ChalkClass;
4476
- return chalk33.template;
4475
+ chalk34.template.Instance = ChalkClass;
4476
+ return chalk34.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 = (chalk33, ...strings) => {
4587
+ var chalkTag = (chalk34, ...strings) => {
4588
4588
  const [firstString] = strings;
4589
4589
  if (!isArray(firstString) || !isArray(firstString.raw)) {
4590
4590
  return strings.join(" ");
@@ -4600,18 +4600,52 @@ var require_source = __commonJS({
4600
4600
  if (template === void 0) {
4601
4601
  template = require_templates();
4602
4602
  }
4603
- return template(chalk33, parts.join(""));
4603
+ return template(chalk34, parts.join(""));
4604
4604
  };
4605
4605
  Object.defineProperties(Chalk.prototype, styles);
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;
4606
+ var chalk33 = Chalk();
4607
+ chalk33.supportsColor = stdoutColor;
4608
+ chalk33.stderr = Chalk({ level: stderrColor ? stderrColor.level : 0 });
4609
+ chalk33.stderr.supportsColor = stderrColor;
4610
+ module2.exports = chalk33;
4611
4611
  }
4612
4612
  });
4613
4613
 
4614
4614
  // src/lib/config.ts
4615
+ var config_exports = {};
4616
+ __export(config_exports, {
4617
+ API_BASE: () => API_BASE,
4618
+ AUTH_FILE: () => AUTH_FILE,
4619
+ CACHE_DIR: () => CACHE_DIR,
4620
+ CONFIG_FILE: () => CONFIG_FILE,
4621
+ HYV_DIR: () => HYV_DIR,
4622
+ LAST_SESSION_FILE: () => LAST_SESSION_FILE,
4623
+ PROFILES_DIR: () => PROFILES_DIR,
4624
+ QUEUE_DIR: () => QUEUE_DIR,
4625
+ appendSecureLine: () => appendSecureLine,
4626
+ assertSafeOAuthUrl: () => assertSafeOAuthUrl,
4627
+ assertSafeOpenUrl: () => assertSafeOpenUrl,
4628
+ assertSafeProfileName: () => assertSafeProfileName,
4629
+ clearAuth: () => clearAuth,
4630
+ clearQueuedSignals: () => clearQueuedSignals,
4631
+ cliApiUrl: () => cliApiUrl,
4632
+ ensureHyvDir: () => ensureHyvDir,
4633
+ getQueuedSignals: () => getQueuedSignals,
4634
+ getToken: () => getToken,
4635
+ isInitialized: () => isInitialized,
4636
+ listCachedProfiles: () => listCachedProfiles,
4637
+ profilePathForName: () => profilePathForName,
4638
+ queueSignal: () => queueSignal,
4639
+ readAuth: () => readAuth,
4640
+ readCachedProfile: () => readCachedProfile,
4641
+ readConfig: () => readConfig,
4642
+ readLastEditSession: () => readLastEditSession,
4643
+ saveLastEditSession: () => saveLastEditSession,
4644
+ writeAuth: () => writeAuth,
4645
+ writeCachedProfile: () => writeCachedProfile,
4646
+ writeConfig: () => writeConfig,
4647
+ writeSecureFile: () => writeSecureFile
4648
+ });
4615
4649
  function validateApiBase(raw) {
4616
4650
  const trimmed = raw.replace(/\/$/, "");
4617
4651
  let parsed;
@@ -4647,6 +4681,27 @@ function assertSafeOpenUrl(url) {
4647
4681
  }
4648
4682
  return url;
4649
4683
  }
4684
+ function assertSafeOAuthUrl(url) {
4685
+ let parsed;
4686
+ try {
4687
+ parsed = new URL(url);
4688
+ } catch {
4689
+ throw new Error("Invalid URL");
4690
+ }
4691
+ if (parsed.protocol !== "https:") {
4692
+ throw new Error("Only https:// URLs can be opened");
4693
+ }
4694
+ if (ALLOWED_API_HOSTS.has(parsed.hostname)) {
4695
+ return url;
4696
+ }
4697
+ if (ALLOWED_OAUTH_HOSTS.has(parsed.hostname)) {
4698
+ if (parsed.hostname === "accounts.google.com" && !parsed.pathname.startsWith("/o/oauth2/")) {
4699
+ throw new Error(`Unexpected OAuth path: ${parsed.pathname}`);
4700
+ }
4701
+ return url;
4702
+ }
4703
+ throw new Error(`URL host not allowed: ${parsed.hostname}`);
4704
+ }
4650
4705
  function assertSafeProfileName(name) {
4651
4706
  const normalized = String(name || "").trim();
4652
4707
  if (!normalized)
@@ -4682,6 +4737,10 @@ function ensureHyvDir() {
4682
4737
  }
4683
4738
  }
4684
4739
  }
4740
+ function writeSecureFile(filePath, content) {
4741
+ ensureHyvDir();
4742
+ fs.writeFileSync(filePath, content, { mode: 384 });
4743
+ }
4685
4744
  function appendSecureLine(filePath, line, dir) {
4686
4745
  if (dir) {
4687
4746
  if (!fs.existsSync(dir))
@@ -4720,6 +4779,11 @@ function writeAuth(auth) {
4720
4779
  ensureHyvDir();
4721
4780
  fs.writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), { mode: 384 });
4722
4781
  }
4782
+ function clearAuth() {
4783
+ if (fs.existsSync(AUTH_FILE)) {
4784
+ fs.unlinkSync(AUTH_FILE);
4785
+ }
4786
+ }
4723
4787
  function readConfig() {
4724
4788
  try {
4725
4789
  if (!fs.existsSync(CONFIG_FILE))
@@ -4790,6 +4854,17 @@ function queueSignal(signal) {
4790
4854
  const filePath = path.join(QUEUE_DIR, `${id}.json`);
4791
4855
  fs.writeFileSync(filePath, JSON.stringify(signal, null, 2), { mode: 384 });
4792
4856
  }
4857
+ function clearQueuedSignals() {
4858
+ try {
4859
+ if (!fs.existsSync(QUEUE_DIR))
4860
+ return;
4861
+ const files = fs.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
4862
+ for (const f of files) {
4863
+ fs.unlinkSync(path.join(QUEUE_DIR, f));
4864
+ }
4865
+ } catch {
4866
+ }
4867
+ }
4793
4868
  function saveLastEditSession(session) {
4794
4869
  ensureHyvDir();
4795
4870
  const data = { ...session, saved_at: (/* @__PURE__ */ new Date()).toISOString() };
@@ -4804,7 +4879,7 @@ function readLastEditSession() {
4804
4879
  return null;
4805
4880
  }
4806
4881
  }
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;
4882
+ 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
4883
  var init_config = __esm({
4809
4884
  "src/lib/config.ts"() {
4810
4885
  "use strict";
@@ -4825,6 +4900,7 @@ var init_config = __esm({
4825
4900
  "localhost",
4826
4901
  "127.0.0.1"
4827
4902
  ]);
4903
+ ALLOWED_OAUTH_HOSTS = /* @__PURE__ */ new Set(["accounts.google.com"]);
4828
4904
  API_BASE = validateApiBase(process.env.HYV_API_URL || "https://holdyourvoice.com");
4829
4905
  PROFILE_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,62}$/i;
4830
4906
  }
@@ -4834,11 +4910,11 @@ var init_config = __esm({
4834
4910
  var require_is_docker = __commonJS({
4835
4911
  "node_modules/is-docker/index.js"(exports2, module2) {
4836
4912
  "use strict";
4837
- var fs25 = require("fs");
4913
+ var fs26 = require("fs");
4838
4914
  var isDocker;
4839
4915
  function hasDockerEnv() {
4840
4916
  try {
4841
- fs25.statSync("/.dockerenv");
4917
+ fs26.statSync("/.dockerenv");
4842
4918
  return true;
4843
4919
  } catch (_) {
4844
4920
  return false;
@@ -4846,7 +4922,7 @@ var require_is_docker = __commonJS({
4846
4922
  }
4847
4923
  function hasDockerCGroup() {
4848
4924
  try {
4849
- return fs25.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4925
+ return fs26.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4850
4926
  } catch (_) {
4851
4927
  return false;
4852
4928
  }
@@ -4864,21 +4940,21 @@ var require_is_docker = __commonJS({
4864
4940
  var require_is_wsl = __commonJS({
4865
4941
  "node_modules/is-wsl/index.js"(exports2, module2) {
4866
4942
  "use strict";
4867
- var os9 = require("os");
4868
- var fs25 = require("fs");
4943
+ var os10 = require("os");
4944
+ var fs26 = require("fs");
4869
4945
  var isDocker = require_is_docker();
4870
4946
  var isWsl = () => {
4871
4947
  if (process.platform !== "linux") {
4872
4948
  return false;
4873
4949
  }
4874
- if (os9.release().toLowerCase().includes("microsoft")) {
4950
+ if (os10.release().toLowerCase().includes("microsoft")) {
4875
4951
  if (isDocker()) {
4876
4952
  return false;
4877
4953
  }
4878
4954
  return true;
4879
4955
  }
4880
4956
  try {
4881
- return fs25.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
4957
+ return fs26.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
4882
4958
  } catch (_) {
4883
4959
  return false;
4884
4960
  }
@@ -4917,17 +4993,17 @@ var require_define_lazy_prop = __commonJS({
4917
4993
  // node_modules/open/index.js
4918
4994
  var require_open = __commonJS({
4919
4995
  "node_modules/open/index.js"(exports2, module2) {
4920
- var path24 = require("path");
4996
+ var path25 = require("path");
4921
4997
  var childProcess = require("child_process");
4922
- var { promises: fs25, constants: fsConstants } = require("fs");
4998
+ var { promises: fs26, constants: fsConstants } = require("fs");
4923
4999
  var isWsl = require_is_wsl();
4924
5000
  var isDocker = require_is_docker();
4925
5001
  var defineLazyProperty = require_define_lazy_prop();
4926
- var localXdgOpenPath = path24.join(__dirname, "xdg-open");
5002
+ var localXdgOpenPath = path25.join(__dirname, "xdg-open");
4927
5003
  var { platform, arch } = process;
4928
5004
  var hasContainerEnv = () => {
4929
5005
  try {
4930
- fs25.statSync("/run/.containerenv");
5006
+ fs26.statSync("/run/.containerenv");
4931
5007
  return true;
4932
5008
  } catch {
4933
5009
  return false;
@@ -4950,14 +5026,14 @@ var require_open = __commonJS({
4950
5026
  const configFilePath = "/etc/wsl.conf";
4951
5027
  let isConfigFileExists = false;
4952
5028
  try {
4953
- await fs25.access(configFilePath, fsConstants.F_OK);
5029
+ await fs26.access(configFilePath, fsConstants.F_OK);
4954
5030
  isConfigFileExists = true;
4955
5031
  } catch {
4956
5032
  }
4957
5033
  if (!isConfigFileExists) {
4958
5034
  return defaultMountPoint;
4959
5035
  }
4960
- const configContent = await fs25.readFile(configFilePath, { encoding: "utf8" });
5036
+ const configContent = await fs26.readFile(configFilePath, { encoding: "utf8" });
4961
5037
  const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
4962
5038
  if (!configMountPoint) {
4963
5039
  return defaultMountPoint;
@@ -5057,7 +5133,7 @@ var require_open = __commonJS({
5057
5133
  const isBundled = !__dirname || __dirname === "/";
5058
5134
  let exeLocalXdgOpen = false;
5059
5135
  try {
5060
- await fs25.access(localXdgOpenPath, fsConstants.X_OK);
5136
+ await fs26.access(localXdgOpenPath, fsConstants.X_OK);
5061
5137
  exeLocalXdgOpen = true;
5062
5138
  } catch {
5063
5139
  }
@@ -5080,14 +5156,14 @@ var require_open = __commonJS({
5080
5156
  }
5081
5157
  const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
5082
5158
  if (options.wait) {
5083
- return new Promise((resolve14, reject) => {
5159
+ return new Promise((resolve15, reject) => {
5084
5160
  subprocess.once("error", reject);
5085
5161
  subprocess.once("close", (exitCode) => {
5086
5162
  if (!options.allowNonzeroExitCode && exitCode > 0) {
5087
5163
  reject(new Error(`Exited with code ${exitCode}`));
5088
5164
  return;
5089
5165
  }
5090
- resolve14(subprocess);
5166
+ resolve15(subprocess);
5091
5167
  });
5092
5168
  });
5093
5169
  }
@@ -5218,16 +5294,16 @@ function compareSemver(a, b) {
5218
5294
  return 0;
5219
5295
  }
5220
5296
  function fetchLatestNpmVersion(timeoutMs = 8e3) {
5221
- return new Promise((resolve14) => {
5297
+ return new Promise((resolve15) => {
5222
5298
  (0, import_child_process.execFile)(
5223
5299
  "npm",
5224
5300
  ["view", "@holdyourvoice/hyv", "version"],
5225
5301
  { timeout: timeoutMs, encoding: "utf-8" },
5226
5302
  (err, stdout) => {
5227
5303
  if (err || !stdout?.trim())
5228
- resolve14(null);
5304
+ resolve15(null);
5229
5305
  else
5230
- resolve14(stdout.trim());
5306
+ resolve15(stdout.trim());
5231
5307
  }
5232
5308
  );
5233
5309
  });
@@ -5291,7 +5367,7 @@ function escapeHtml(value) {
5291
5367
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5292
5368
  }
5293
5369
  function request(url, options = {}) {
5294
- return new Promise((resolve14, reject) => {
5370
+ return new Promise((resolve15, reject) => {
5295
5371
  const urlObj = new URL(url);
5296
5372
  const isHttps = urlObj.protocol === "https:";
5297
5373
  const client = isHttps ? https : http;
@@ -5312,9 +5388,9 @@ function request(url, options = {}) {
5312
5388
  res.on("data", (chunk) => data += chunk);
5313
5389
  res.on("end", () => {
5314
5390
  try {
5315
- resolve14({ status: res.statusCode || 0, data: JSON.parse(data) });
5391
+ resolve15({ status: res.statusCode || 0, data: JSON.parse(data) });
5316
5392
  } catch {
5317
- resolve14({ status: res.statusCode || 0, data });
5393
+ resolve15({ status: res.statusCode || 0, data });
5318
5394
  }
5319
5395
  });
5320
5396
  });
@@ -5372,9 +5448,9 @@ async function authenticateWithLicense(licenseKey) {
5372
5448
  }
5373
5449
  async function authenticateWithBrowser() {
5374
5450
  const server = http.createServer();
5375
- const port = await new Promise((resolve14) => {
5451
+ const port = await new Promise((resolve15) => {
5376
5452
  server.listen(0, "127.0.0.1", () => {
5377
- resolve14(server.address().port);
5453
+ resolve15(server.address().port);
5378
5454
  });
5379
5455
  });
5380
5456
  const redirectUri = `http://127.0.0.1:${port}/callback`;
@@ -5392,8 +5468,8 @@ async function authenticateWithBrowser() {
5392
5468
  throw new Error("Authentication server did not return OAuth state");
5393
5469
  }
5394
5470
  console.log(import_chalk.default.cyan("\nOpening browser for authentication..."));
5395
- await (0, import_open.default)(assertSafeOpenUrl(auth_url));
5396
- const authData = await new Promise((resolve14, reject) => {
5471
+ await (0, import_open.default)(assertSafeOAuthUrl(auth_url));
5472
+ const authData = await new Promise((resolve15, reject) => {
5397
5473
  const timeout = setTimeout(() => {
5398
5474
  server.close();
5399
5475
  reject(new Error("Authentication timeout. Please try again."));
@@ -5441,7 +5517,7 @@ async function authenticateWithBrowser() {
5441
5517
  `);
5442
5518
  clearTimeout(timeout);
5443
5519
  server.close();
5444
- resolve14(data);
5520
+ resolve15(data);
5445
5521
  } catch (error) {
5446
5522
  res.writeHead(500, { "Content-Type": "text/html" });
5447
5523
  res.end(`<h1>Authentication failed</h1><p>${escapeHtml(error.message)}</p>`);
@@ -5673,11 +5749,11 @@ __export(api_exports, {
5673
5749
  apiPost: () => apiPost,
5674
5750
  requireSubscription: () => requireSubscription
5675
5751
  });
5676
- async function request2(method, path24, body) {
5752
+ async function request2(method, path25, body) {
5677
5753
  const token = await getValidToken();
5678
5754
  if (!token)
5679
5755
  throw new Error("you're not signed in yet. run: hyv init");
5680
- const url = `${API_BASE}${path24}`;
5756
+ const url = `${API_BASE}${path25}`;
5681
5757
  const opts = {
5682
5758
  method,
5683
5759
  headers: {
@@ -5702,11 +5778,11 @@ async function request2(method, path24, body) {
5702
5778
  }
5703
5779
  return res.json();
5704
5780
  }
5705
- function apiGet(path24) {
5706
- return request2("GET", path24);
5781
+ function apiGet(path25) {
5782
+ return request2("GET", path25);
5707
5783
  }
5708
- function apiPost(path24, body) {
5709
- return request2("POST", path24, body);
5784
+ function apiPost(path25, body) {
5785
+ return request2("POST", path25, body);
5710
5786
  }
5711
5787
  async function requireSubscription() {
5712
5788
  const { requirePaidFeature: requirePaidFeature2 } = await Promise.resolve().then(() => (init_access(), access_exports));
@@ -7278,34 +7354,940 @@ function runPipeline(text, profile, applyFixes = false) {
7278
7354
  };
7279
7355
  }
7280
7356
  function readText(source) {
7281
- const fs25 = require("fs");
7282
- const path24 = require("path");
7357
+ const fs26 = require("fs");
7358
+ const path25 = require("path");
7283
7359
  if (source === "-") {
7284
7360
  if (process.stdin.isTTY) {
7285
7361
  console.error("No input provided. Pipe content or specify a file.");
7286
7362
  process.exit(1);
7287
7363
  }
7288
- return { text: fs25.readFileSync(0, "utf-8"), path: "stdin" };
7364
+ return { text: fs26.readFileSync(0, "utf-8"), path: "stdin" };
7365
+ }
7366
+ const resolved = path25.resolve(source);
7367
+ if (!fs26.existsSync(resolved)) {
7368
+ console.error(`File not found: ${resolved}`);
7369
+ process.exit(1);
7370
+ }
7371
+ const stat = fs26.statSync(resolved);
7372
+ if (stat.isDirectory()) {
7373
+ console.error(`${resolved} is a directory, not a file.`);
7374
+ process.exit(1);
7375
+ }
7376
+ return { text: fs26.readFileSync(resolved, "utf-8"), path: resolved };
7377
+ }
7378
+ var init_pipeline = __esm({
7379
+ "src/lib/pipeline.ts"() {
7380
+ "use strict";
7381
+ init_signals();
7382
+ init_classifier();
7383
+ init_autofix();
7384
+ init_validator();
7385
+ }
7386
+ });
7387
+
7388
+ // src/lib/patterns.ts
7389
+ function scanLine(line, lineNum, filePath) {
7390
+ const findings = [];
7391
+ for (const pat of ALL_PATTERNS) {
7392
+ pat.regex.lastIndex = 0;
7393
+ let match;
7394
+ while ((match = pat.regex.exec(line)) !== null) {
7395
+ findings.push({
7396
+ file: filePath,
7397
+ line: lineNum,
7398
+ column: match.index + 1,
7399
+ pattern: pat.id,
7400
+ category: pat.category,
7401
+ severity: pat.severity,
7402
+ excerpt: highlightMatch2(line.trim(), match.index, match[0].length),
7403
+ suggestion: pat.suggestion
7404
+ });
7405
+ }
7406
+ }
7407
+ return findings;
7408
+ }
7409
+ function highlightMatch2(line, start, len) {
7410
+ const before = line.slice(0, start);
7411
+ const match = line.slice(start, start + len);
7412
+ const after = line.slice(start + len);
7413
+ return `${before}\xAB${match}\xBB${after}`.slice(0, 120);
7414
+ }
7415
+ var AI_OVERUSED2, FORMULAIC2, HEDGING2, STRUCTURE2, ENGAGEMENT_BAIT2, AI_CRINGE2, INSIDER_CLAIMS2, FORMATTING, OGILVY2, ALL_PATTERNS;
7416
+ var init_patterns = __esm({
7417
+ "src/lib/patterns.ts"() {
7418
+ "use strict";
7419
+ AI_OVERUSED2 = [
7420
+ { id: "ai.delve", category: "ai-slop", severity: "red", regex: /\bdelve\b/gi, suggestion: "use a specific verb: dig, explore, look at" },
7421
+ { id: "ai.leverage", category: "ai-slop", severity: "red", regex: /\bleverage\b/gi, suggestion: "use: use, apply, build on" },
7422
+ { id: "ai.utilize", category: "ai-slop", severity: "red", regex: /\butilize\b/gi, suggestion: "use: use" },
7423
+ { id: "ai.tapestry", category: "ai-slop", severity: "red", regex: /\btapestry\b/gi, suggestion: "be specific about what you mean" },
7424
+ { id: "ai.holistic", category: "ai-slop", severity: "red", regex: /\bholistic\b/gi, suggestion: "describe the actual approach" },
7425
+ { id: "ai.robust", category: "ai-slop", severity: "yellow", regex: /\brobust\b/gi, suggestion: "say what actually makes it strong" },
7426
+ { id: "ai.pivotal", category: "ai-slop", severity: "yellow", regex: /\bpivotal\b/gi, suggestion: "say why it matters specifically" },
7427
+ { id: "ai.foster", category: "ai-slop", severity: "yellow", regex: /\bfoster\b/gi, suggestion: "use: build, grow, encourage, support" },
7428
+ { id: "ai.harness", category: "ai-slop", severity: "yellow", regex: /\bharness\b/gi, suggestion: "use: use, apply, work with" },
7429
+ { id: "ai.illuminate", category: "ai-slop", severity: "yellow", regex: /\billuminate\b/gi, suggestion: "use: show, explain, highlight" },
7430
+ { id: "ai.ever-evolving", category: "ai-slop", severity: "red", regex: /\b(?:ever[\s-]evolving|ever[\s-]changing)\b/gi, suggestion: "cut this \u2014 it says nothing" },
7431
+ { id: "ai.fast-paced", category: "ai-slop", severity: "red", regex: /\bfast[\s-]paced\b/gi, suggestion: "cut this \u2014 every industry says this" },
7432
+ { id: "ai.game-changer", category: "ai-slop", severity: "red", regex: /\bgame[\s-]changer\b/gi, suggestion: "explain what actually changed" },
7433
+ { id: "ai.paradigm", category: "ai-slop", severity: "red", regex: /\bparadigm\b/gi, suggestion: "describe the actual shift" },
7434
+ { id: "ai.synergy", category: "ai-slop", severity: "red", regex: /\bsynergy\b/gi, suggestion: "describe what works together and why" },
7435
+ { id: "ai.ecosystem", category: "ai-slop", severity: "yellow", regex: /\becosystem\b/gi, suggestion: "name the specific tools/partners/platforms" },
7436
+ { id: "ai.seamless", category: "ai-slop", severity: "yellow", regex: /\bseamless\b/gi, suggestion: "describe how it actually works" },
7437
+ { id: "ai.actionable", category: "ai-slop", severity: "yellow", regex: /\bactionable\b/gi, suggestion: "just give the action, don't label it" },
7438
+ { id: "ai.granular", category: "ai-slop", severity: "yellow", regex: /\bgranular\b/gi, suggestion: "say: specific, detailed, or name the level" },
7439
+ { id: "ai.impactful", category: "ai-slop", severity: "yellow", regex: /\bimpactful\b/gi, suggestion: "describe the actual impact" },
7440
+ { id: "ai.landscape", category: "ai-slop", severity: "red", regex: /\blandscape\b/gi, suggestion: "name the specific market/field/area" },
7441
+ { id: "ai.realm", category: "ai-slop", severity: "red", regex: /\brealm\b/gi, suggestion: "name the specific domain" },
7442
+ { id: "ai.straightforward", category: "ai-slop", severity: "yellow", regex: /\bstraightforward\b/gi, suggestion: "just explain it directly" }
7443
+ ];
7444
+ FORMULAIC2 = [
7445
+ { id: "formula.firstly", category: "ai-slop", severity: "yellow", regex: /\bfirstly\b/gi, suggestion: 'just start \u2014 "firstly" is filler' },
7446
+ { id: "formula.secondly", category: "ai-slop", severity: "yellow", regex: /\bsecondly\b/gi, suggestion: 'just continue \u2014 "secondly" is filler' },
7447
+ { id: "formula.lastly", category: "ai-slop", severity: "yellow", regex: /\blastly\b/gi, suggestion: 'just end \u2014 "lastly" is filler' },
7448
+ { id: "formula.moreover", category: "ai-slop", severity: "yellow", regex: /\bmoreover\b/gi, suggestion: "just add the point" },
7449
+ { id: "formula.furthermore", category: "ai-slop", severity: "yellow", regex: /\bfurthermore\b/gi, suggestion: "just add the point" },
7450
+ { id: "formula.in-conclusion", category: "ai-slop", severity: "red", regex: /\bin conclusion\b/gi, suggestion: "just end. readers know it's the end." },
7451
+ { id: "formula.in-summary", category: "ai-slop", severity: "red", regex: /\bin summary\b/gi, suggestion: "just summarize. the label is redundant." },
7452
+ { id: "formula.it-is-important", category: "ai-slop", severity: "yellow", regex: /\bit is important to note\b/gi, suggestion: "just note it. skip the preamble." },
7453
+ { id: "formula.at-the-end", category: "ai-slop", severity: "yellow", regex: /\bat the end of the day\b/gi, suggestion: "cut this \u2014 it means nothing" },
7454
+ { id: "formula.needless-to-say", category: "ai-slop", severity: "yellow", regex: /\bneedless to say\b/gi, suggestion: "if it's needless, don't say it" },
7455
+ { id: "formula.it-goes-without", category: "ai-slop", severity: "yellow", regex: /\bit goes without saying\b/gi, suggestion: "then don't say it" },
7456
+ { id: "formula.in-today", category: "ai-slop", severity: "red", regex: /\bin today'?s\b/gi, suggestion: "start with your actual point instead" },
7457
+ { id: "formula.lets-dive", category: "ai-slop", severity: "red", regex: /\blet'?s (?:dive|jump|dig|delve)\b/gi, suggestion: "just start. no diving needed." },
7458
+ { id: "formula.without-further", category: "ai-slop", severity: "red", regex: /\bwithout further ado\b/gi, suggestion: "cut this \u2014 just get to it" },
7459
+ { id: "formula.its-worth-noting", category: "ai-slop", severity: "yellow", regex: /\bit'?s worth noting\b/gi, suggestion: "just note it directly" },
7460
+ { id: "formula.moving-forward", category: "ai-slop", severity: "yellow", regex: /\bmoving forward\b/gi, suggestion: "just say what happens next" },
7461
+ { id: "formula.to-put-in-perspective", category: "ai-slop", severity: "yellow", regex: /\bto put this in perspective\b/gi, suggestion: "just give the perspective directly" },
7462
+ { id: "formula.what-makes-interesting", category: "ai-slop", severity: "yellow", regex: /\bwhat makes this particularly interesting\b/gi, suggestion: "just say the interesting thing" },
7463
+ { id: "formula.implications", category: "ai-slop", severity: "yellow", regex: /\bthe implications here are\b/gi, suggestion: "state the implications directly" },
7464
+ { id: "formula.in-other-words", category: "ai-slop", severity: "red", regex: /\bin other words\b/gi, suggestion: "say it once, well" }
7465
+ ];
7466
+ HEDGING2 = [
7467
+ { id: "hedge.some-might", category: "voice-drift", severity: "yellow", regex: /\bsome might say\b/gi, suggestion: "commit to the claim or drop it" },
7468
+ { id: "hedge.arguably", category: "voice-drift", severity: "yellow", regex: /\barguably\b/gi, suggestion: "commit. say it or don't." },
7469
+ { id: "hedge.worth-noting", category: "voice-drift", severity: "yellow", regex: /\bit'?s worth noting\b/gi, suggestion: "just note it directly" },
7470
+ { id: "hedge.to-some-extent", category: "voice-drift", severity: "yellow", regex: /\bto some extent\b/gi, suggestion: "be specific about the extent" },
7471
+ { id: "hedge.perhaps", category: "voice-drift", severity: "yellow", regex: /\bperhaps\b/gi, suggestion: "commit or cut" },
7472
+ { id: "hedge.it-seems", category: "voice-drift", severity: "yellow", regex: /\bit seems\b/gi, suggestion: "state it directly" }
7473
+ ];
7474
+ STRUCTURE2 = [
7475
+ { id: "struct.antithesis", category: "structure", severity: "yellow", regex: /\bnot (?:just|only) .{3,50}, but .{3,50}/gi, suggestion: "this antithesis pattern is an AI tell \u2014 restructure" },
7476
+ { id: "struct.more-than-just", category: "structure", severity: "yellow", regex: /\bmore than just\b/gi, suggestion: "say what it IS, not what it isn't" },
7477
+ { id: "struct.in-order-to", category: "structure", severity: "yellow", regex: /\bin order to\b/gi, suggestion: 'just use "to"' },
7478
+ { id: "struct.due-to-the-fact", category: "structure", severity: "yellow", regex: /\bdue to the fact that\b/gi, suggestion: 'use "because"' },
7479
+ { id: "struct.for-the-purpose", category: "structure", severity: "yellow", regex: /\bfor the purpose of\b/gi, suggestion: 'use "to"' },
7480
+ { id: "struct.which-is-another", category: "structure", severity: "red", regex: /\bwhich is another way of saying\b/gi, suggestion: "just say the thing directly" },
7481
+ { id: "struct.this-is-why", category: "structure", severity: "yellow", regex: /\bthis is (?:also )?(?:why|how|where|what\b)/gi, suggestion: "signpost claims are AI tells \u2014 just make the point" },
7482
+ { id: "struct.heres-where", category: "structure", severity: "yellow", regex: /\bhere'?s (?:where|why|what|the part|the (?:harder|real|actual|main|bigger) problem)\b/gi, suggestion: "just make the point without the signpost" },
7483
+ { id: "struct.rhetorical-truth", category: "structure", severity: "yellow", regex: /\b(?:the\s+)?(?:uncomfortable|hard|harsh|brutal|real|honest)\s+(?:truth|reality)\b/gi, suggestion: "state the fact directly, skip the framing" },
7484
+ { id: "struct.lesson-setup", category: "structure", severity: "yellow", regex: /\bhere'?s what .{3,60} taught\b/gi, suggestion: "just share the lesson" },
7485
+ // THE BIG ONE — antithesis negation pattern (Voice DNA: FATAL)
7486
+ { id: "struct.this-isnt-x-this-is-y", category: "structure", severity: "red", regex: /\bthis isn'?t .{2,40}\.?\s*(?:this is|it'?s) .{2,40}/gi, suggestion: "FATAL: delete the negation, just state the positive claim" },
7487
+ { id: "struct.not-x-y", category: "structure", severity: "red", regex: /\bnot .{2,30}\.?\s*.{2,30}\b/gi, suggestion: 'the "Not X. Y." pattern is an AI tell \u2014 just state Y' },
7488
+ { id: "struct.forget-x", category: "structure", severity: "red", regex: /\bforget .{2,40}\.?\s*(?:this is|it'?s|you need)/gi, suggestion: "don't negate \u2014 just state what you mean" }
7489
+ ];
7490
+ ENGAGEMENT_BAIT2 = [
7491
+ { id: "bait.let-that-sink", category: "engagement-bait", severity: "red", regex: /\blet that sink in\b/gi, suggestion: "cut the sink. make your point and move on." },
7492
+ { id: "bait.read-that-again", category: "engagement-bait", severity: "red", regex: /\bread that again\b/gi, suggestion: "if it needs repeating, repeat it yourself" },
7493
+ { id: "bait.full-stop", category: "engagement-bait", severity: "red", regex: /\bfull stop\b/gi, suggestion: "the period already does this job" },
7494
+ { id: "bait.this-changes-everything", category: "engagement-bait", severity: "red", regex: /\bthis changes everything\b/gi, suggestion: "prove it with specifics" },
7495
+ { id: "bait.paying-attention", category: "engagement-bait", severity: "red", regex: /\bare you paying attention\b/gi, suggestion: "don't patronize the reader" },
7496
+ { id: "bait.not-ready", category: "engagement-bait", severity: "red", regex: /\byou'?re not ready for this\b/gi, suggestion: "just deliver the content" }
7497
+ ];
7498
+ AI_CRINGE2 = [
7499
+ { id: "cringe.supercharge", category: "ai-cringe", severity: "red", regex: /\bsupercharge\b/gi, suggestion: "describe what it actually does" },
7500
+ { id: "cringe.unlock", category: "ai-cringe", severity: "yellow", regex: /\bunlock\b/gi, suggestion: "describe what they get access to" },
7501
+ { id: "cringe.future-proof", category: "ai-cringe", severity: "red", regex: /\bfuture[\s-]proof\b/gi, suggestion: "explain what specifically makes it durable" },
7502
+ { id: "cringe.10x", category: "ai-cringe", severity: "red", regex: /\b10x\b/gi, suggestion: "use the actual numbers" },
7503
+ { id: "cringe.ai-revolution", category: "ai-cringe", severity: "red", regex: /\bthe ai revolution\b/gi, suggestion: "describe the specific change" },
7504
+ { id: "cringe.age-of-ai", category: "ai-cringe", severity: "red", regex: /\bin the age of ai\b/gi, suggestion: "just talk about what's happening now" },
7505
+ { id: "cringe.happy-to-help", category: "ai-cringe", severity: "red", regex: /\bi'?d be happy to help\b/gi, suggestion: "just help. don't announce it." }
7506
+ ];
7507
+ INSIDER_CLAIMS2 = [
7508
+ { id: "insider.nobody-talking", category: "insider-claim", severity: "red", regex: /\bhere'?s the part nobody'?s? talking about\b/gi, suggestion: "just say the thing. the framing is noise." },
7509
+ { id: "insider.nobody-tells", category: "insider-claim", severity: "red", regex: /\bwhat nobody tells you\b/gi, suggestion: "just tell them." },
7510
+ { id: "insider.most-people", category: "insider-claim", severity: "yellow", regex: /\bmost people don'?t realize\b/gi, suggestion: "just explain it. skip the setup." },
7511
+ { id: "insider.nobody-realizes", category: "insider-claim", severity: "red", regex: /\bnobody (?:realizes|talks about|mentions)\b/gi, suggestion: "if nobody talks about it, just talk about it" }
7512
+ ];
7513
+ FORMATTING = [
7514
+ { id: "format.em-dash", category: "formatting", severity: "red", regex: /\u2014/g, suggestion: "NO em dashes. use commas, periods, colons, semicolons, or parentheses." }
7515
+ ];
7516
+ OGILVY2 = [
7517
+ // Jargon — words that hide lack of understanding
7518
+ { id: "ogilvy.jargon-utilize", category: "ai-slop", severity: "red", regex: /\butilize[sd]?\b/gi, suggestion: 'Ogilvy: use "use" instead' },
7519
+ { id: "ogilvy.jargon-leverage", category: "ai-slop", severity: "red", regex: /\bleverage[ds]?\b/gi, suggestion: "Ogilvy: say what you actually mean" },
7520
+ { id: "ogilvy.jargon-synergy", category: "ai-slop", severity: "red", regex: /\bsynerg(?:y|ies|istic)\b/gi, suggestion: "Ogilvy: describe what works together and why" },
7521
+ { id: "ogilvy.jargon-bandwidth", category: "ai-slop", severity: "yellow", regex: /\bbandwidth\b/gi, suggestion: 'Ogilvy: say "time" or "capacity"' },
7522
+ { id: "ogilvy.jargon-circle-back", category: "ai-slop", severity: "red", regex: /\bcircle back\b/gi, suggestion: 'Ogilvy: say "follow up" or "talk later"' },
7523
+ { id: "ogilvy.jargon-low-hanging", category: "ai-slop", severity: "red", regex: /\blow-hanging fruit\b/gi, suggestion: "Ogilvy: name the specific easy win" },
7524
+ { id: "ogilvy.jargon-move-the-needle", category: "ai-slop", severity: "red", regex: /\bmove the needle\b/gi, suggestion: "Ogilvy: describe the actual impact" },
7525
+ { id: "ogilvy.jargon-touch-base", category: "ai-slop", severity: "red", regex: /\btouch base\b/gi, suggestion: 'Ogilvy: say "talk" or "meet"' },
7526
+ { id: "ogilvy.jargon-take-it-offline", category: "ai-slop", severity: "red", regex: /\btake it offline\b/gi, suggestion: 'Ogilvy: say "discuss later"' },
7527
+ { id: "ogilvy.jargon-deep-dive", category: "ai-slop", severity: "yellow", regex: /\bdeep dive\b/gi, suggestion: 'Ogilvy: say "look closely at" or "examine"' },
7528
+ // Throat-clearing openers
7529
+ { id: "ogilvy.preamble-i-want-to", category: "voice-drift", severity: "yellow", regex: /^\s*i want to (?:share|talk about|discuss|mention)\b/gi, suggestion: "Ogilvy: just say it. skip the preamble." },
7530
+ { id: "ogilvy.preamble-just-wanted", category: "voice-drift", severity: "yellow", regex: /^\s*(?:i just wanted|i wanted to)\b/gi, suggestion: "Ogilvy: just say it." }
7531
+ ];
7532
+ ALL_PATTERNS = [
7533
+ ...AI_OVERUSED2,
7534
+ ...FORMULAIC2,
7535
+ ...HEDGING2,
7536
+ ...STRUCTURE2,
7537
+ ...ENGAGEMENT_BAIT2,
7538
+ ...AI_CRINGE2,
7539
+ ...INSIDER_CLAIMS2,
7540
+ ...FORMATTING,
7541
+ ...OGILVY2
7542
+ ];
7543
+ }
7544
+ });
7545
+
7546
+ // src/lib/scan.ts
7547
+ function words(text) {
7548
+ return (text || "").match(/[a-zA-Z][a-zA-Z0-9']*/g) || [];
7549
+ }
7550
+ function sentences(text) {
7551
+ return (text || "").split(/(?<=[.!?])\s+|\n{2,}/).map((s) => s.trim()).filter((s) => words(s).length > 0);
7552
+ }
7553
+ function paragraphs(text) {
7554
+ return (text || "").split(/\n\s*\n/).map((p) => p.trim()).filter((p) => words(p).length >= 6);
7555
+ }
7556
+ function lineStyleHits(line) {
7557
+ const low = (line || "").trim().toLowerCase();
7558
+ if (!low)
7559
+ return [];
7560
+ const hits = [];
7561
+ const lineWords = low.match(/[a-z']+/g) || [];
7562
+ const abstractCount = lineWords.filter((w) => ABSTRACT_STYLE_WORDS.has(w)).length;
7563
+ if (abstractCount >= 3 && !/\b(?:for example|for instance|such as)\b|\d/i.test(low)) {
7564
+ hits.push({
7565
+ line: 0,
7566
+ // Will be set by caller
7567
+ rule: "abstract_noun_cluster",
7568
+ phrase: line.trim().slice(0, 160)
7569
+ });
7570
+ }
7571
+ if (GENERIC_OPENERS.test(low)) {
7572
+ hits.push({
7573
+ line: 0,
7574
+ rule: "generic_opening_generalization",
7575
+ phrase: line.trim().slice(0, 160)
7576
+ });
7577
+ }
7578
+ if (QUESTION_OPENER.test(low)) {
7579
+ hits.push({
7580
+ line: 0,
7581
+ rule: "voice_question_opener",
7582
+ phrase: "opens with a question instead of a concrete observation"
7583
+ });
7584
+ }
7585
+ if (LESSON_OPENER.test(low)) {
7586
+ hits.push({
7587
+ line: 0,
7588
+ rule: "voice_lesson_opener",
7589
+ phrase: "opens with a lesson or inspirational claim"
7590
+ });
7591
+ }
7592
+ return hits;
7593
+ }
7594
+ function scanText(text) {
7595
+ const hits = [];
7596
+ const safeText = text || "";
7597
+ for (const rule of AI_PATTERN_RULES) {
7598
+ const regex = new RegExp(rule.pattern.source, rule.pattern.flags.replace("g", "") + "g");
7599
+ let match;
7600
+ let lastIndex = 0;
7601
+ while ((match = regex.exec(safeText)) !== null) {
7602
+ const snippet = match[0].trim();
7603
+ if (!snippet) {
7604
+ if (match.index === regex.lastIndex)
7605
+ regex.lastIndex++;
7606
+ continue;
7607
+ }
7608
+ const lineNo = safeText.slice(0, match.index).split("\n").length;
7609
+ hits.push({
7610
+ line: lineNo,
7611
+ rule: rule.id,
7612
+ phrase: snippet.slice(0, 160)
7613
+ });
7614
+ if (match.index === regex.lastIndex)
7615
+ regex.lastIndex++;
7616
+ }
7617
+ }
7618
+ const lines = safeText.split("\n");
7619
+ for (let i = 0; i < lines.length; i++) {
7620
+ const lineNum = i + 1;
7621
+ const lineText = lines[i];
7622
+ const patternHits = scanLine(lineText, lineNum, "inline");
7623
+ for (const hit of patternHits) {
7624
+ hits.push({ line: hit.line, rule: hit.pattern, severity: hit.severity, phrase: hit.excerpt || "", text: lineText.trim().slice(0, 120) });
7625
+ }
7626
+ const lineHits = lineStyleHits(lineText);
7627
+ for (const hit of lineHits) {
7628
+ hit.line = lineNum;
7629
+ hit.text = lineText.trim().slice(0, 240);
7630
+ hits.push(hit);
7631
+ }
7632
+ }
7633
+ const sentenceHits = [];
7634
+ for (let i = 0; i < lines.length; i++) {
7635
+ const lineSentences = lines[i].split(/(?<=[.!?])\s+/);
7636
+ for (const sentence of lineSentences) {
7637
+ const wordCount = words(sentence).length;
7638
+ if (wordCount > 0) {
7639
+ sentenceHits.push({ line: i + 1, text: sentence.trim(), wordCount });
7640
+ }
7641
+ }
7642
+ }
7643
+ for (let i = 0; i < sentenceHits.length - 2; i++) {
7644
+ const window = sentenceHits.slice(i, i + 3);
7645
+ if (window.every((s) => s.wordCount <= 5)) {
7646
+ hits.push({
7647
+ line: window[0].line,
7648
+ rule: "voice_staccato_triplet",
7649
+ phrase: "three short sentences in a row reads like performance",
7650
+ text: window[0].text
7651
+ });
7652
+ break;
7653
+ }
7654
+ }
7655
+ return hits.sort((a, b) => {
7656
+ if (a.line !== b.line)
7657
+ return a.line - b.line;
7658
+ return a.rule.localeCompare(b.rule);
7659
+ });
7660
+ }
7661
+ var AI_PATTERN_RULES, ABSTRACT_STYLE_WORDS, GENERIC_OPENERS, QUESTION_OPENER, LESSON_OPENER;
7662
+ var init_scan = __esm({
7663
+ "src/lib/scan.ts"() {
7664
+ "use strict";
7665
+ init_patterns();
7666
+ AI_PATTERN_RULES = [
7667
+ {
7668
+ id: "ai_antithesis",
7669
+ pattern: /\b(?:it'?s|isn'?t|is\s+not)\s+not\s+just\b.{0,80}\b(?:it'?s|but)\b/i
7670
+ },
7671
+ {
7672
+ id: "not_just_but",
7673
+ pattern: /\bnot\s+just\b.{3,80}\bbut\s+(?:also\s+)?/i
7674
+ },
7675
+ {
7676
+ id: "more_than_just",
7677
+ pattern: /\bmore\s+than\s+just\b/i
7678
+ },
7679
+ {
7680
+ id: "rhetorical_truth_setup",
7681
+ pattern: /\b(?:the\s+)?(?:uncomfortable|hard|harsh|brutal|ugly|unsexy|real|honest)\s+(?:truth|reality)\b/i
7682
+ },
7683
+ {
7684
+ id: "truth_is",
7685
+ pattern: /\bthe\s+truth\s+is\b/i
7686
+ },
7687
+ {
7688
+ id: "lesson_setup",
7689
+ pattern: /\b(?:here'?s\s+)?what\s+.{3,80}\s+(?:taught|teaches)\s+(?:me|us|you|everyone)\b/i
7690
+ },
7691
+ {
7692
+ id: "negation_cascade",
7693
+ pattern: /\b(?:no|not)\s+\w[^.!?\n]{0,80}[.!?][ \t]*\n?[ \t]*(?:no|not)\s+\w[^.!?\n]{0,80}[.!?][ \t]*\n?[ \t]*(?:no|not)\s+\w/i
7694
+ },
7695
+ {
7696
+ id: "formulaic_connector",
7697
+ pattern: /\b(?:firstly|secondly|thirdly|lastly|moreover|furthermore|in conclusion|to summarize|to sum up|in summary|it is important to note|it should be noted)\b/i
7698
+ },
7699
+ {
7700
+ id: "ai_words",
7701
+ pattern: /\b(?:delve|underscore|testament|intricate|multifaceted|cornerstone|landscape|foster|harness|leverage|tapestry|illuminate|pivotal|elevate|empower|seamlessly|revolutionize|supercharge|transformative|holistic|comprehensive|innovative|impactful|meaningful|utilize|paradigm|navigate|endeavor|realm|profound|encapsulate|synergy|robust|facilitate|bolster|streamline|differentiate|myriad|unlock|transform)\b/i
7702
+ },
7703
+ {
7704
+ id: "em_dash",
7705
+ pattern: /\u2014/
7706
+ },
7707
+ {
7708
+ id: "signpost_claim",
7709
+ pattern: /\bthis\s+is\s+(?:also\s+)?(?:why|how|where|what\b|what\s+happens\s+when)\b|\b(?:here'?s|here\s+is)\s+(?:where|why|what|the\s+part|the\s+(?:harder|real|actual|main|bigger)\s+problem)\b/i
7710
+ },
7711
+ {
7712
+ id: "generic_buyer_psychology",
7713
+ pattern: /\bpeople\s+don'?t\s+just\s+buy\b|\bpeople\s+buy\s+the\s+feeling\b/i
7714
+ },
7715
+ {
7716
+ id: "founder_cadence_restatement",
7717
+ pattern: /\bwhich\s+is\s+another\s+way\s+of\s+saying\b|\bin\s+other\s+words\b/i
7718
+ },
7719
+ {
7720
+ id: "founder_cadence_moment_becomes",
7721
+ pattern: /\b(?:the\s+)?moment\b.{3,80}\bbecomes?\b|\bbecomes?\s+(?:dangerous|useful|interesting|real|obvious)\s+the\s+moment\b/i
7722
+ },
7723
+ {
7724
+ id: "founder_cadence_same_better",
7725
+ pattern: /\bsame\s+[^.!?\n]{1,35}[.!?]\s*(?:better|nicer|cleaner|calmer|safer)\s+[^.!?\n]{1,35}[.!?]?/i
7726
+ }
7727
+ ];
7728
+ ABSTRACT_STYLE_WORDS = /* @__PURE__ */ new Set([
7729
+ "alignment",
7730
+ "authenticity",
7731
+ "awareness",
7732
+ "clarity",
7733
+ "confidence",
7734
+ "consistency",
7735
+ "differentiation",
7736
+ "execution",
7737
+ "framework",
7738
+ "identity",
7739
+ "messaging",
7740
+ "narrative",
7741
+ "personality",
7742
+ "positioning",
7743
+ "preference",
7744
+ "presence",
7745
+ "recall",
7746
+ "relevance",
7747
+ "resonance",
7748
+ "signal",
7749
+ "strategy",
7750
+ "trust",
7751
+ "utility",
7752
+ "value"
7753
+ ]);
7754
+ GENERIC_OPENERS = /^(?:most|many)\s+(?:brands|teams|people|founders|companies)\b/i;
7755
+ QUESTION_OPENER = /^(?:have you|do you|did you|what if|why do|how do)\b/i;
7756
+ LESSON_OPENER = /^(?:the most important thing|the key to|success is|if you want to|what i learned)\b/i;
7757
+ }
7758
+ });
7759
+
7760
+ // src/lib/welcome-flow.ts
7761
+ function readWelcomeState() {
7762
+ try {
7763
+ if (!fs11.existsSync(STATE_FILE)) {
7764
+ return { completed_steps: [], updated_at: (/* @__PURE__ */ new Date()).toISOString() };
7765
+ }
7766
+ return JSON.parse(fs11.readFileSync(STATE_FILE, "utf-8"));
7767
+ } catch {
7768
+ return { completed_steps: [], updated_at: (/* @__PURE__ */ new Date()).toISOString() };
7769
+ }
7770
+ }
7771
+ function writeWelcomeState(patch) {
7772
+ ensureHyvDir();
7773
+ const next = {
7774
+ ...readWelcomeState(),
7775
+ ...patch,
7776
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
7777
+ };
7778
+ fs11.writeFileSync(STATE_FILE, JSON.stringify(next, null, 2), { mode: 384 });
7779
+ return next;
7780
+ }
7781
+ function markStepComplete(step) {
7782
+ const state = readWelcomeState();
7783
+ if (!state.completed_steps.includes(step)) {
7784
+ writeWelcomeState({ completed_steps: [...state.completed_steps, step] });
7785
+ }
7786
+ }
7787
+ function divider() {
7788
+ return import_chalk11.default.dim(" " + "\u2500".repeat(44));
7789
+ }
7790
+ function formatFlowOverview() {
7791
+ const lines = [divider(), ""];
7792
+ for (const step of FLOW_STEPS) {
7793
+ lines.push(` ${import_chalk11.default.bold(String(step.n))} ${step.title}`);
7794
+ lines.push(import_chalk11.default.dim(` ${step.hint}`));
7795
+ lines.push("");
7796
+ }
7797
+ lines.push(divider());
7798
+ return lines.join("\n");
7799
+ }
7800
+ function buildVoiceExtractionPrompt(profileName) {
7801
+ return `You are helping set up Hold Your Voice for "${profileName}".
7802
+
7803
+ Read this user's messages in our conversation (and any writing they shared). Extract how they actually write \u2014 not a generic brand persona.
7804
+
7805
+ Return ONLY a markdown voice profile with these sections:
7806
+
7807
+ # Voice Profile: ${profileName}
7808
+
7809
+ ## Voice keywords
7810
+ 8\u201310 traits with a short note each
7811
+
7812
+ ## Signature
7813
+ Sentence rhythm, typical openings, tone, formality
7814
+
7815
+ ## Anchor passages
7816
+ 3 short original-style lines written in their voice (invented but faithful)
7817
+
7818
+ ## Never list
7819
+ Phrases that sound unlike them or generic AI slop
7820
+
7821
+ ## Voice rules
7822
+ 5 concrete rules for rewriting in their voice
7823
+
7824
+ Use lowercase section headings after the title. Be specific. No filler.`;
7825
+ }
7826
+ function buildStepGuide(step, profileName) {
7827
+ const name = profileName || readWelcomeState().profile_name || "<name>";
7828
+ switch (step) {
7829
+ case 1:
7830
+ return [
7831
+ "### Step 1 \u2014 name your profile",
7832
+ "",
7833
+ "Ask the user what to call their voice profile (short slug, e.g. `my-voice`).",
7834
+ "",
7835
+ `Then save locally: \`hyv import ${name} profile.md\` after step 2, or note the name for later.`
7836
+ ].join("\n");
7837
+ case 2:
7838
+ return [
7839
+ "### Step 2 \u2014 add writing samples",
7840
+ "",
7841
+ "Pick one:",
7842
+ "- **paste** \u2014 user pastes newsletters, posts, or docs",
7843
+ "- **folder** \u2014 `hyv new <name> --from-samples ./writing`",
7844
+ "- **link** \u2014 user shares a URL; fetch readable text if you can",
7845
+ "- **from chat** \u2014 run the extraction prompt below on their prior messages",
7846
+ "",
7847
+ "**Extraction prompt (for chat / agent):**",
7848
+ "",
7849
+ buildVoiceExtractionPrompt(name),
7850
+ "",
7851
+ "Let the user edit the profile markdown, then save:",
7852
+ `\`hyv import ${name} ./voice-profile.md\``
7853
+ ].join("\n");
7854
+ case 3:
7855
+ return [
7856
+ "### Step 3 \u2014 test on a draft",
7857
+ "",
7858
+ "Run a free local scan:",
7859
+ "`hyv scan draft.md` or MCP `hyv_scan` on pasted text.",
7860
+ "",
7861
+ "Optional rewrite (needs profile):",
7862
+ `\`hyv rewrite draft.md --profile ${name}\``,
7863
+ "",
7864
+ "Show score + top issues. Ask if it feels right."
7865
+ ].join("\n");
7866
+ case 4:
7867
+ return [
7868
+ "### Step 4 \u2014 save & unlock",
7869
+ "",
7870
+ "When they are happy with the local profile:",
7871
+ "1. `hyv init` \u2014 browser signup",
7872
+ "2. `hyv plan --upgrade` \u2014 first month $1, unlocks learning + sync",
7873
+ "",
7874
+ `Pricing: ${PRICING_URL}`,
7875
+ "",
7876
+ "Local scan stays free forever."
7877
+ ].join("\n");
7878
+ default:
7879
+ return buildWelcomeGuide({ profileName });
7880
+ }
7881
+ }
7882
+ function buildWelcomeGuide(opts = {}) {
7883
+ const lines = [
7884
+ WELCOME_TAGLINE,
7885
+ "",
7886
+ formatFlowOverview().replace(/\u001b\[[0-9;]*m/g, ""),
7887
+ ""
7888
+ ];
7889
+ if (opts.forLlm) {
7890
+ lines.push("## For agents \u2014 run these steps in order");
7891
+ lines.push("");
7892
+ for (let i = 1; i <= 4; i += 1) {
7893
+ lines.push(buildStepGuide(i, opts.profileName));
7894
+ lines.push("");
7895
+ }
7896
+ lines.push("Call `hyv_welcome` with `step: 2` and `mode: extract_prompt` for the chat extraction prompt.");
7897
+ return lines.join("\n");
7898
+ }
7899
+ const state = readWelcomeState();
7900
+ const name = opts.profileName || state.profile_name;
7901
+ if (name) {
7902
+ lines.push(`current profile: ${name}`);
7903
+ lines.push("");
7904
+ }
7905
+ lines.push("start in terminal:");
7906
+ lines.push(" hyv welcome walk through all 4 steps");
7907
+ lines.push(" hyv welcome --guide show this guide (for agents / scripts)");
7908
+ lines.push("");
7909
+ lines.push("or jump in:");
7910
+ lines.push(" hyv import <name> <file.md> save a profile");
7911
+ lines.push(" hyv scan draft.md test a draft");
7912
+ lines.push(" hyv init signup when ready");
7913
+ lines.push("");
7914
+ return lines.join("\n");
7915
+ }
7916
+ function buildWelcomeHeader(opts = {}) {
7917
+ if (opts.condensed) {
7918
+ return [
7919
+ WELCOME_TAGLINE,
7920
+ "",
7921
+ " hyv welcome set up your voice (4 steps)",
7922
+ " hyv scan test any draft free",
7923
+ ""
7924
+ ].join("\n");
7925
+ }
7926
+ return [WELCOME_TAGLINE, "", formatFlowOverview(), ""].join("\n");
7927
+ }
7928
+ function extractStats(samples) {
7929
+ const combined = samples.map((s) => s.text).join("\n\n");
7930
+ const allWords = words(combined);
7931
+ const allSentences = sentences(combined);
7932
+ const allParagraphs = paragraphs(combined);
7933
+ const sentenceLengths = allSentences.map((s) => words(s).length);
7934
+ const avgSentence = sentenceLengths.length ? Math.round(sentenceLengths.reduce((a, b) => a + b, 0) / sentenceLengths.length) : 0;
7935
+ const paragraphLengths = allParagraphs.map((p) => sentences(p).length);
7936
+ const avgParagraph = paragraphLengths.length ? Math.round(paragraphLengths.reduce((a, b) => a + b, 0) / paragraphLengths.length) : 0;
7937
+ const starters = allSentences.map((s) => {
7938
+ const match = s.trim().match(/[A-Za-z]/);
7939
+ return match ? match[0] : "";
7940
+ }).filter(Boolean);
7941
+ const lowerCount = starters.filter((c2) => c2 === c2.toLowerCase()).length;
7942
+ const caseStyle = starters.length && lowerCount / starters.length > 0.85 ? "mostly lowercase" : "standard";
7943
+ return {
7944
+ word_count: allWords.length,
7945
+ sentence_count: allSentences.length,
7946
+ paragraph_count: allParagraphs.length,
7947
+ avg_sentence_length: avgSentence,
7948
+ avg_paragraph_length: avgParagraph,
7949
+ case_style: caseStyle,
7950
+ sample_count: samples.length
7951
+ };
7952
+ }
7953
+ function profileMarkdownFromSamples(name, samples) {
7954
+ const stats = extractStats(samples);
7955
+ const excerpt = samples.map((s) => s.text.trim().slice(0, 400)).filter(Boolean).slice(0, 3).map((t, i) => `### sample ${i + 1}
7956
+ ${t}${t.length >= 400 ? "\u2026" : ""}`).join("\n\n");
7957
+ return `# Voice Profile: ${name}
7958
+
7959
+ ## Signature
7960
+ - average sentence: ${stats.avg_sentence_length} words
7961
+ - case style: ${stats.case_style}
7962
+ - analyzed ${stats.sample_count} sample(s), ${stats.word_count} words total
7963
+
7964
+ ## Anchor passages
7965
+ ${excerpt || "_add more samples to sharpen anchors_"}
7966
+
7967
+ ## Never list
7968
+ - delve / dive in / landscape
7969
+ - it's important to note
7970
+ - holistic / robust / leverage
7971
+
7972
+ ## Voice rules
7973
+ 1. match the ${stats.avg_sentence_length}-word sentence rhythm
7974
+ 2. keep ${stats.case_style} casing
7975
+ 3. open with something concrete, not a throat-clear
7976
+ 4. cut ai filler; keep the user's actual opinions
7977
+ 5. rewrite should sound like the samples above
7978
+
7979
+ ---
7980
+ _created via hyv welcome_
7981
+ `;
7982
+ }
7983
+ function profileMarkdownFromChat(name, extracted) {
7984
+ const body = extracted.trim();
7985
+ if (body.toLowerCase().includes("# voice profile"))
7986
+ return body;
7987
+ return `# Voice Profile: ${name}
7988
+
7989
+ ${body}
7990
+
7991
+ ---
7992
+ _created via hyv welcome (chat extract)_
7993
+ `;
7994
+ }
7995
+ function saveLocalProfile(name, content) {
7996
+ const safe = assertSafeProfileName(name);
7997
+ ensureHyvDir();
7998
+ writeCachedProfile(safe, content);
7999
+ const config = readConfig();
8000
+ writeConfig({ ...config, default_profile: safe, profile: safe });
8001
+ writeWelcomeState({ profile_name: safe });
8002
+ markStepComplete("name");
8003
+ markStepComplete("samples");
8004
+ return path12.join(os5.homedir(), ".hyv", "profiles", `${safe}.md`);
8005
+ }
8006
+ function collectSamplesFromFolder(dir) {
8007
+ const dirPath = path12.resolve(dir);
8008
+ if (!fs11.existsSync(dirPath))
8009
+ throw new Error(`folder not found: ${dirPath}`);
8010
+ const files = fs11.readdirSync(dirPath).filter((f) => /\.(md|txt|markdown)$/i.test(f)).map((f) => path12.join(dirPath, f));
8011
+ if (!files.length)
8012
+ throw new Error("no .md or .txt files in folder");
8013
+ const samples = [];
8014
+ for (const file of files) {
8015
+ const text = fs11.readFileSync(file, "utf-8");
8016
+ if (text.trim())
8017
+ samples.push({ path: file, text });
8018
+ }
8019
+ if (!samples.length)
8020
+ throw new Error("no readable text in folder");
8021
+ return samples;
8022
+ }
8023
+ function fetchUrlText(url) {
8024
+ return new Promise((resolve15, reject) => {
8025
+ let parsed;
8026
+ try {
8027
+ parsed = new URL(url);
8028
+ } catch {
8029
+ reject(new Error("invalid url"));
8030
+ return;
8031
+ }
8032
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
8033
+ reject(new Error("only http(s) links supported"));
8034
+ return;
8035
+ }
8036
+ const client = parsed.protocol === "https:" ? https2 : http2;
8037
+ const req = client.get(
8038
+ url,
8039
+ { headers: { "User-Agent": "hyv-cli/welcome" }, timeout: 15e3 },
8040
+ (res) => {
8041
+ if ((res.statusCode || 0) >= 400) {
8042
+ reject(new Error(`http ${res.statusCode}`));
8043
+ return;
8044
+ }
8045
+ let data = "";
8046
+ res.on("data", (chunk) => {
8047
+ data += chunk;
8048
+ if (data.length > 5e5)
8049
+ req.destroy(new Error("response too large"));
8050
+ });
8051
+ res.on("end", () => {
8052
+ const text = data.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
8053
+ resolve15(text.slice(0, 12e3));
8054
+ });
8055
+ }
8056
+ );
8057
+ req.on("error", reject);
8058
+ req.on("timeout", () => {
8059
+ req.destroy();
8060
+ reject(new Error("request timeout"));
8061
+ });
8062
+ });
8063
+ }
8064
+ async function collectSamplesFromLink(url) {
8065
+ const text = await fetchUrlText(url);
8066
+ if (!text || words(text).length < 40) {
8067
+ throw new Error("could not extract enough text from link \u2014 try paste instead");
8068
+ }
8069
+ return [{ path: url, text }];
8070
+ }
8071
+ function askLine(prompt) {
8072
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
8073
+ return new Promise((resolve15) => {
8074
+ rl.question(prompt, (answer) => {
8075
+ rl.close();
8076
+ resolve15(answer.trim());
8077
+ });
8078
+ });
8079
+ }
8080
+ async function askMultiline(intro) {
8081
+ console.log(intro);
8082
+ console.log(import_chalk11.default.dim(" paste below, then type --- on its own line when done\n"));
8083
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
8084
+ const lines = [];
8085
+ return new Promise((resolve15) => {
8086
+ rl.on("line", (line) => {
8087
+ if (line.trim() === "---") {
8088
+ rl.close();
8089
+ resolve15(lines.join("\n").trim());
8090
+ return;
8091
+ }
8092
+ lines.push(line);
8093
+ });
8094
+ });
8095
+ }
8096
+ async function askYesNo(prompt, defaultNo = true) {
8097
+ const suffix = defaultNo ? " [y/N] " : " [Y/n] ";
8098
+ const answer = (await askLine(import_chalk11.default.cyan(prompt + suffix))).toLowerCase();
8099
+ if (!answer)
8100
+ return !defaultNo;
8101
+ return answer.startsWith("y");
8102
+ }
8103
+ function formatScanResult(text, profileName) {
8104
+ const result = runPipeline(text, profileName, false);
8105
+ const tells = result.signalMap.signals.slice(0, 3).map((s) => s.id).join(", ");
8106
+ const tail = tells ? ` \u2014 e.g. ${tells}` : "";
8107
+ return `${result.score}/100, ${result.signalMap.signals.length} issues${tail}`;
8108
+ }
8109
+ async function stepName() {
8110
+ console.log(import_chalk11.default.bold("\nstep 1 \xB7 name your profile\n"));
8111
+ const name = await askLine(import_chalk11.default.cyan(" profile name (e.g. my-voice): "));
8112
+ return assertSafeProfileName(name);
8113
+ }
8114
+ async function stepSamples(profileName) {
8115
+ console.log(import_chalk11.default.bold("\nstep 2 \xB7 add writing samples\n"));
8116
+ console.log(" how do you want to add samples?\n");
8117
+ console.log(import_chalk11.default.dim(" [1] paste text"));
8118
+ console.log(import_chalk11.default.dim(" [2] folder of files"));
8119
+ console.log(import_chalk11.default.dim(" [3] link to your writing"));
8120
+ console.log(import_chalk11.default.dim(" [4] extract from this chat (agent / llm)\n"));
8121
+ const choice = await askLine(import_chalk11.default.cyan(" choice [1-4]: ")) || "1";
8122
+ let content = "";
8123
+ if (choice === "2") {
8124
+ const folder = await askLine(import_chalk11.default.cyan(" folder path: "));
8125
+ const samples = collectSamplesFromFolder(folder);
8126
+ console.log(import_chalk11.default.dim(`
8127
+ read ${samples.length} file(s)`));
8128
+ content = profileMarkdownFromSamples(profileName, samples);
8129
+ writeWelcomeState({ samples_source: "folder" });
8130
+ } else if (choice === "3") {
8131
+ const url = await askLine(import_chalk11.default.cyan(" https://\u2026 "));
8132
+ try {
8133
+ const samples = await collectSamplesFromLink(url);
8134
+ console.log(import_chalk11.default.dim(`
8135
+ fetched ${words(samples[0].text).length} words`));
8136
+ content = profileMarkdownFromSamples(profileName, samples);
8137
+ writeWelcomeState({ samples_source: "link" });
8138
+ } catch (err) {
8139
+ console.log(import_chalk11.default.yellow(`
8140
+ ${err.message}`));
8141
+ content = `# Voice Profile: ${profileName}
8142
+
8143
+ ## source
8144
+ ${url}
8145
+
8146
+ _Add pasted samples to train this profile._
8147
+ `;
8148
+ writeWelcomeState({ samples_source: "link" });
8149
+ }
8150
+ } else if (choice === "4") {
8151
+ console.log(import_chalk11.default.bold("\n extraction prompt \u2014 paste this into your llm / agent:\n"));
8152
+ console.log(import_chalk11.default.dim("\u2500".repeat(50)));
8153
+ console.log(buildVoiceExtractionPrompt(profileName));
8154
+ console.log(import_chalk11.default.dim("\u2500".repeat(50)));
8155
+ console.log("");
8156
+ const extracted = await askMultiline(" paste the profile markdown the agent returned:");
8157
+ if (!extracted)
8158
+ throw new Error("no profile content pasted");
8159
+ content = profileMarkdownFromChat(profileName, extracted);
8160
+ writeWelcomeState({ samples_source: "chat" });
8161
+ } else {
8162
+ const pasted = await askMultiline(" paste newsletters, posts, or any writing in your voice:");
8163
+ if (!pasted || words(pasted).length < 30) {
8164
+ throw new Error("need at least ~30 words of sample text");
8165
+ }
8166
+ content = profileMarkdownFromSamples(profileName, [{ path: "paste", text: pasted }]);
8167
+ writeWelcomeState({ samples_source: "paste" });
8168
+ }
8169
+ const saved = saveLocalProfile(profileName, content);
8170
+ console.log(import_chalk11.default.green(`
8171
+ \u2713 profile saved`));
8172
+ console.log(import_chalk11.default.dim(` ${saved}`));
8173
+ }
8174
+ async function stepTest(profileName) {
8175
+ console.log(import_chalk11.default.bold("\nstep 3 \xB7 test on a draft\n"));
8176
+ console.log(import_chalk11.default.dim(" paste a draft, or enter a file path (.md / .txt)\n"));
8177
+ const input = await askLine(import_chalk11.default.cyan(" draft or path: "));
8178
+ if (!input) {
8179
+ console.log(import_chalk11.default.dim(" skipped \u2014 run `hyv scan draft.md` anytime"));
8180
+ return;
7289
8181
  }
7290
- const resolved = path24.resolve(source);
7291
- if (!fs25.existsSync(resolved)) {
7292
- console.error(`File not found: ${resolved}`);
7293
- process.exit(1);
8182
+ let text = input;
8183
+ const maybePath = path12.resolve(input);
8184
+ if (fs11.existsSync(maybePath) && fs11.statSync(maybePath).isFile()) {
8185
+ text = fs11.readFileSync(maybePath, "utf-8");
8186
+ }
8187
+ const summary = formatScanResult(text, profileName);
8188
+ console.log(import_chalk11.default.green(`
8189
+ scan: ${summary}`));
8190
+ console.log(import_chalk11.default.dim(` try: hyv rewrite ${input.includes("/") ? input : "draft.md"} --profile ${profileName}`));
8191
+ markStepComplete("test");
8192
+ }
8193
+ async function stepSignup(profileName) {
8194
+ console.log(import_chalk11.default.bold("\nstep 4 \xB7 save & unlock\n"));
8195
+ console.log(" local profile is ready. signup syncs it and unlocks learning.\n");
8196
+ console.log(import_chalk11.default.dim(` free forever: hyv scan, fix, check, mcp`));
8197
+ console.log(import_chalk11.default.dim(` paid: profiles that learn, hybrid rewrite, dashboard
8198
+ `));
8199
+ const ready = await askYesNo(" create your account now?");
8200
+ if (!ready) {
8201
+ console.log(import_chalk11.default.dim("\n whenever you are ready:"));
8202
+ console.log(import_chalk11.default.dim(" hyv init"));
8203
+ console.log(import_chalk11.default.dim(" hyv plan --upgrade"));
8204
+ console.log(import_chalk11.default.dim(` ${PRICING_URL}
8205
+ `));
8206
+ return;
7294
8207
  }
7295
- const stat = fs25.statSync(resolved);
7296
- if (stat.isDirectory()) {
7297
- console.error(`${resolved} is a directory, not a file.`);
7298
- process.exit(1);
8208
+ if (!isInitialized() || !getToken()) {
8209
+ console.log(import_chalk11.default.cyan("\n opening browser for signup...\n"));
8210
+ await authenticateWithBrowser();
8211
+ }
8212
+ const content = fs11.readFileSync(
8213
+ path12.join(os5.homedir(), ".hyv", "profiles", `${profileName}.md`),
8214
+ "utf-8"
8215
+ );
8216
+ try {
8217
+ const response = await authenticatedRequest(cliApiUrl("/cli/profiles/new"), {
8218
+ method: "POST",
8219
+ body: { name: profileName, content, source: "welcome" }
8220
+ });
8221
+ if (response.status === 200) {
8222
+ console.log(import_chalk11.default.green(" \u2713 profile synced to your account"));
8223
+ } else {
8224
+ console.log(import_chalk11.default.yellow(" profile saved locally \u2014 sync again with `hyv sync` after upgrade"));
8225
+ }
8226
+ } catch {
8227
+ console.log(import_chalk11.default.yellow(" profile saved locally \u2014 run `hyv sync` after you upgrade"));
8228
+ }
8229
+ console.log(import_chalk11.default.cyan("\n opening pricing..."));
8230
+ const { default: open3 } = await Promise.resolve().then(() => __toESM(require_open()));
8231
+ const { assertSafeOpenUrl: assertSafeOpenUrl2 } = await Promise.resolve().then(() => (init_config(), config_exports));
8232
+ await open3(assertSafeOpenUrl2(PRICING_URL));
8233
+ markStepComplete("signup");
8234
+ console.log(import_chalk11.default.dim("\n or run: hyv plan --upgrade\n"));
8235
+ }
8236
+ async function runInteractiveWelcome() {
8237
+ recordEvent("welcome_interactive");
8238
+ console.log("\n" + buildWelcomeHeader());
8239
+ try {
8240
+ const profileName = await stepName();
8241
+ await stepSamples(profileName);
8242
+ await stepTest(profileName);
8243
+ await stepSignup(profileName);
8244
+ console.log(import_chalk11.default.green("done \u2014 your voice profile is ready.\n"));
8245
+ console.log(import_chalk11.default.dim(" hyv scan draft.md"));
8246
+ console.log(import_chalk11.default.dim(` hyv rewrite draft.md --profile ${profileName}`));
8247
+ console.log(import_chalk11.default.dim(" hyv mcp --setup\n"));
8248
+ } catch (err) {
8249
+ console.error(import_chalk11.default.red(`
8250
+ ${err.message || "welcome flow stopped"}
8251
+ `));
8252
+ console.log(import_chalk11.default.dim(" resume anytime: hyv welcome\n"));
8253
+ process.exitCode = 1;
7299
8254
  }
7300
- return { text: fs25.readFileSync(resolved, "utf-8"), path: resolved };
7301
8255
  }
7302
- var init_pipeline = __esm({
7303
- "src/lib/pipeline.ts"() {
8256
+ function getMcpWelcomeResponse(args2) {
8257
+ if (args2.mode === "extract_prompt") {
8258
+ const name = args2.profile || readWelcomeState().profile_name || "my-voice";
8259
+ return buildVoiceExtractionPrompt(name);
8260
+ }
8261
+ if (args2.step && args2.step >= 1 && args2.step <= 4) {
8262
+ return [WELCOME_TAGLINE, "", buildStepGuide(args2.step, args2.profile)].join("\n");
8263
+ }
8264
+ return buildWelcomeGuide({ profileName: args2.profile, forLlm: true });
8265
+ }
8266
+ var import_chalk11, fs11, http2, https2, os5, path12, readline, WELCOME_TAGLINE, FLOW_STEPS, STATE_FILE;
8267
+ var init_welcome_flow = __esm({
8268
+ "src/lib/welcome-flow.ts"() {
7304
8269
  "use strict";
7305
- init_signals();
7306
- init_classifier();
7307
- init_autofix();
7308
- init_validator();
8270
+ import_chalk11 = __toESM(require_source());
8271
+ fs11 = __toESM(require("fs"));
8272
+ http2 = __toESM(require("http"));
8273
+ https2 = __toESM(require("https"));
8274
+ os5 = __toESM(require("os"));
8275
+ path12 = __toESM(require("path"));
8276
+ readline = __toESM(require("readline"));
8277
+ init_pipeline();
8278
+ init_config();
8279
+ init_auth();
8280
+ init_scan();
8281
+ init_free_paid();
8282
+ init_telemetry();
8283
+ WELCOME_TAGLINE = "Hold Your Voice \u2014 make your AI agents sound exactly like you.";
8284
+ FLOW_STEPS = [
8285
+ { n: 1, key: "name", title: "name your profile", hint: "pick a short name \u2014 e.g. my-voice" },
8286
+ { n: 2, key: "samples", title: "add writing samples", hint: "paste, folder, link, or extract from chat" },
8287
+ { n: 3, key: "test", title: "test on a draft", hint: "scan or rewrite to see it work" },
8288
+ { n: 4, key: "signup", title: "save & unlock", hint: "signup syncs your profile and unlocks learning" }
8289
+ ];
8290
+ STATE_FILE = path12.join(os5.homedir(), ".hyv", "welcome-state.json");
7309
8291
  }
7310
8292
  });
7311
8293
 
@@ -7313,11 +8295,13 @@ var init_pipeline = __esm({
7313
8295
  var welcome_exports = {};
7314
8296
  __export(welcome_exports, {
7315
8297
  DEMO_TEXT: () => DEMO_TEXT,
7316
- ONBOARDING_STEPS: () => ONBOARDING_STEPS,
8298
+ FLOW_STEPS: () => FLOW_STEPS,
8299
+ WELCOME_TAGLINE: () => WELCOME_TAGLINE,
7317
8300
  buildWelcomeMessage: () => buildWelcomeMessage,
7318
8301
  formatCompactDemoResult: () => formatCompactDemoResult,
7319
8302
  formatDemoResult: () => formatDemoResult,
7320
8303
  formatFreeToolsList: () => formatFreeToolsList,
8304
+ plainFlowOverview: () => plainFlowOverview,
7321
8305
  printWelcome: () => printWelcome,
7322
8306
  runWelcomeDemo: () => runWelcomeDemo
7323
8307
  });
@@ -7329,9 +8313,7 @@ function runWelcomeDemo() {
7329
8313
  issueCount: signals.length,
7330
8314
  red: result.stats.red,
7331
8315
  yellow: result.stats.yellow,
7332
- topIssues: signals.slice(0, 6).map(
7333
- (s) => `line ${s.line}: ${s.id} \u2014 ${s.suggestion}`
7334
- )
8316
+ topIssues: signals.slice(0, 6).map((s) => `line ${s.line}: ${s.id} \u2014 ${s.suggestion}`)
7335
8317
  };
7336
8318
  }
7337
8319
  function formatDemoResult(demo) {
@@ -7351,7 +8333,7 @@ function formatCompactDemoResult(demo) {
7351
8333
  return m ? m[1] : i.split(" \u2014 ")[0];
7352
8334
  }).join(", ");
7353
8335
  const tail = tells ? ` \u2014 e.g. ${tells}` : "";
7354
- return `quick demo: ${demo.score}/100, ${demo.issueCount} issues${tail}`;
8336
+ return `sample scan: ${demo.score}/100, ${demo.issueCount} issues${tail}`;
7355
8337
  }
7356
8338
  function formatFreeToolsList() {
7357
8339
  const cli = FREE_CLI_COMMANDS.map((c2) => ` \u2022 ${c2}`).join("\n");
@@ -7370,54 +8352,48 @@ function formatFreeToolsList() {
7370
8352
  ].join("\n");
7371
8353
  }
7372
8354
  function buildWelcomeMessage(opts = {}) {
7373
- if (opts.condensed) {
7374
- return [
7375
- "hold your voice \u2014 make your AI agents sound exactly like you.",
7376
- "",
7377
- " hyv scan draft.md test a draft",
7378
- " hyv init build your voice",
7379
- " hyv mcp --setup connect cursor / claude",
7380
- "",
7381
- ` profiles + learning \u2192 ${PRICING_URL}`
7382
- ].join("\n");
8355
+ if (opts.guide) {
8356
+ return buildWelcomeGuide();
7383
8357
  }
7384
- const lines = [
7385
- "Hold Your Voice \u2014 make your AI agents sound exactly like you.",
7386
- "",
7387
- "4 steps:",
7388
- ""
7389
- ];
7390
- for (const step of ONBOARDING_STEPS) {
7391
- lines.push(` ${step.n}. ${step.title}`);
7392
- lines.push(` ${step.cmd}`);
7393
- lines.push(` ${step.hint}`);
7394
- lines.push("");
8358
+ if (opts.condensed) {
8359
+ return buildWelcomeHeader({ condensed: true });
7395
8360
  }
8361
+ const lines = [buildWelcomeHeader(), ""];
7396
8362
  if (!opts.skipDemo) {
7397
8363
  const demo = runWelcomeDemo();
7398
8364
  recordEvent("welcome_demo", { score: demo.score, issues: demo.issueCount });
7399
- lines.push(" try it now:");
8365
+ lines.push(" try it now");
7400
8366
  lines.push(` ${formatCompactDemoResult(demo)}`);
7401
8367
  lines.push(" hyv scan draft.md");
7402
8368
  lines.push("");
7403
8369
  }
7404
- lines.push("Ready for voice profiles that learn from every edit?");
7405
- lines.push(` hyv plan --upgrade \u2192 ${PRICING_URL}`);
7406
- lines.push(" first month $1 \xB7 local scan stays free forever");
8370
+ lines.push(" set up your voice");
8371
+ lines.push(" hyv welcome");
8372
+ lines.push("");
8373
+ lines.push(` unlock learning \u2192 ${PRICING_URL}`);
7407
8374
  lines.push("");
7408
8375
  return lines.join("\n");
7409
8376
  }
7410
8377
  function printWelcome(opts = {}) {
7411
- recordEvent("welcome_view", { condensed: !!opts.condensed });
8378
+ recordEvent("welcome_view", { condensed: !!opts.condensed, guide: !!opts.guide });
8379
+ if (opts.guide) {
8380
+ console.log("\n" + buildWelcomeGuide());
8381
+ return;
8382
+ }
7412
8383
  console.log("\n" + buildWelcomeMessage(opts));
7413
8384
  }
7414
- var DEMO_TEXT, ONBOARDING_STEPS;
8385
+ function plainFlowOverview() {
8386
+ return formatFlowOverview().replace(/\u001b\[[0-9;]*m/g, "");
8387
+ }
8388
+ var DEMO_TEXT;
7415
8389
  var init_welcome = __esm({
7416
8390
  "src/lib/welcome.ts"() {
7417
8391
  "use strict";
7418
8392
  init_pipeline();
7419
8393
  init_free_paid();
7420
8394
  init_telemetry();
8395
+ init_welcome_flow();
8396
+ init_welcome_flow();
7421
8397
  DEMO_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.
7422
8398
 
7423
8399
  Firstly, you need to harness the power of seamless integration. Moreover, this robust ecosystem will foster innovation and unlock new opportunities for growth.
@@ -7425,12 +8401,6 @@ Firstly, you need to harness the power of seamless integration. Moreover, this r
7425
8401
  Here's the uncomfortable truth: most teams are not equipped to handle the paradigm shift. But arguably, those who embrace this cutting-edge technology will have a game-changer advantage.
7426
8402
 
7427
8403
  In conclusion, the key to success is to empower your team with actionable insights and foster a culture of continuous improvement.`;
7428
- ONBOARDING_STEPS = [
7429
- { n: 1, title: "install", cmd: "npm i -g @holdyourvoice/hyv@latest", hint: "one command, works in every terminal and agent" },
7430
- { n: 2, title: "test a draft", cmd: "hyv scan draft.md", hint: "free local scan \u2014 flags ai tells offline" },
7431
- { n: 3, title: "build your voice", cmd: "hyv init", hint: "profile from your writing \u2014 unlocks rewrite + learning" },
7432
- { n: 4, title: "connect your agent", cmd: "hyv mcp --setup", hint: "cursor, claude code, codex \u2014 tools inside the chat" }
7433
- ];
7434
8404
  }
7435
8405
  });
7436
8406
 
@@ -8305,15 +9275,15 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
8305
9275
  g.minimatch.escape = vi.escape;
8306
9276
  g.minimatch.unescape = Ei.unescape;
8307
9277
  });
8308
- var fs25 = R((Wt) => {
9278
+ var fs26 = R((Wt) => {
8309
9279
  "use strict";
8310
9280
  Object.defineProperty(Wt, "__esModule", { value: true });
8311
9281
  Wt.LRUCache = void 0;
8312
9282
  var er = typeof performance == "object" && performance && typeof performance.now == "function" ? performance : Date, as = /* @__PURE__ */ new Set(), ge = typeof process == "object" && process ? process : {}, ls = (n, t, e, s) => {
8313
9283
  typeof ge.emitWarning == "function" ? ge.emitWarning(n, t, e, s) : console.error(`[${e}] ${t}: ${n}`);
8314
- }, Lt = globalThis.AbortController, os9 = globalThis.AbortSignal;
9284
+ }, Lt = globalThis.AbortController, os10 = globalThis.AbortSignal;
8315
9285
  if (typeof Lt > "u") {
8316
- os9 = class {
9286
+ os10 = class {
8317
9287
  onabort;
8318
9288
  _onabort = [];
8319
9289
  reason;
@@ -8325,7 +9295,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
8325
9295
  constructor() {
8326
9296
  t();
8327
9297
  }
8328
- signal = new os9();
9298
+ signal = new os10();
8329
9299
  abort(e) {
8330
9300
  if (!this.signal.aborted) {
8331
9301
  this.signal.reason = e, this.signal.aborted = true;
@@ -9274,7 +10244,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
9274
10244
  };
9275
10245
  Object.defineProperty(_, "__esModule", { value: true });
9276
10246
  _.PathScurry = _.Path = _.PathScurryDarwin = _.PathScurryPosix = _.PathScurryWin32 = _.PathScurryBase = _.PathPosix = _.PathWin32 = _.PathBase = _.ChildrenCache = _.ResolveCache = void 0;
9277
- var Qt = fs25(), Yt = require("node:path"), yr = require("node:url"), pt = require("fs"), Sr = br(require("node:fs")), vr = pt.realpathSync.native, Ht = require("node:fs/promises"), bs = Oe(), mt = { lstatSync: pt.lstatSync, readdir: pt.readdir, readdirSync: pt.readdirSync, readlinkSync: pt.readlinkSync, realpathSync: vr, promises: { lstat: Ht.lstat, readdir: Ht.readdir, readlink: Ht.readlink, realpath: Ht.realpath } }, _s = (n) => !n || n === mt || n === Sr ? mt : { ...mt, ...n, promises: { ...mt.promises, ...n.promises || {} } }, Os = /^\\\\\?\\([a-z]:)\\?$/i, Er = (n) => n.replace(/\//g, "\\").replace(Os, "$1\\"), _r = /[\\\/]/, N = 0, xs = 1, Ts = 2, G = 4, Cs = 6, Rs = 8, Q = 10, As = 12, j = 15, dt = ~j, xe = 16, ys = 32, gt = 64, W = 128, Vt = 256, Xt = 512, Ss = gt | W | Xt, Or = 1023, Te = (n) => n.isFile() ? Rs : n.isDirectory() ? G : n.isSymbolicLink() ? Q : n.isCharacterDevice() ? Ts : n.isBlockDevice() ? Cs : n.isSocket() ? As : n.isFIFO() ? xs : N, vs = new Qt.LRUCache({ max: 2 ** 12 }), wt = (n) => {
10247
+ var Qt = fs26(), 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) => {
9278
10248
  let t = vs.get(n);
9279
10249
  if (t)
9280
10250
  return t;
@@ -10793,7 +11763,7 @@ var {
10793
11763
  } = import_index.default;
10794
11764
 
10795
11765
  // src/index.ts
10796
- var import_chalk31 = __toESM(require_source());
11766
+ var import_chalk32 = __toESM(require_source());
10797
11767
 
10798
11768
  // src/commands/init.ts
10799
11769
  var import_chalk4 = __toESM(require_source());
@@ -10942,7 +11912,7 @@ function registerInitCommand(program3) {
10942
11912
  const mcpResults = configureMcpForDesktop();
10943
11913
  const { printPaidUnlockReminder: printPaidUnlockReminder2 } = await Promise.resolve().then(() => (init_marketing_hints(), marketing_hints_exports));
10944
11914
  console.log("\nNext steps:");
10945
- console.log(import_chalk4.default.dim(" 1. Create a voice profile: hyv new <name>"));
11915
+ console.log(import_chalk4.default.dim(" 1. Set up your voice: hyv welcome"));
10946
11916
  console.log(import_chalk4.default.dim(" 2. Scan a draft: hyv scan draft.md"));
10947
11917
  console.log(import_chalk4.default.dim(" 3. Rewrite with voice: hyv rewrite draft.md"));
10948
11918
  printPaidUnlockReminder2();
@@ -11482,435 +12452,77 @@ function registerRewriteCommand(program3) {
11482
12452
  program3.command("rewrite").description("Generate rewrite prompt for LLM").argument("<file>", "File to rewrite (or - for stdin)").option("--profile <name>", "Voice profile to use").option("--format <type>", "Content format (linkedin, blog, email)").option("--constraints <text>", "Additional constraints").option("--no-scan", "Skip local scan").option("--output <file>", "Write prompt to file instead of stdout").option("--dry-run", "Show what profile data would be injected").action(async (file, options) => {
11483
12453
  try {
11484
12454
  const profile = await loadProfileForCommand(options.profile, { allowServerFetch: true });
11485
- if (options.profile && !profile) {
11486
- await requirePaidFeature("premiumPrompts");
11487
- }
11488
- const { text: draftContent, path: draftPath } = readText(file);
11489
- const result = runPipeline(draftContent, profile, false);
11490
- const promptResult = buildRewritePrompt({
11491
- draftPath,
11492
- draftContent,
11493
- profileName: options.profile,
11494
- profile,
11495
- format: options.format,
11496
- constraints: options.constraints,
11497
- skipScan: options.scan === false
11498
- });
11499
- console.log(import_chalk9.default.dim(`
11500
- Draft: ${draftPath}`));
11501
- console.log(import_chalk9.default.dim(`Profile: ${promptResult.profileUsed || "none (using generic rules)"}`));
11502
- if (profile && hasRichProfile(profile)) {
11503
- console.log(import_chalk9.default.dim(` never-list: ${profile.never_list?.length || 0} learned: ${profile.learned_patterns?.length || 0}`));
11504
- }
11505
- console.log(import_chalk9.default.dim(`Issues found: ${result.stats.totalSignals}`));
11506
- console.log(import_chalk9.default.dim(`Score: ${result.score}/100`));
11507
- if (options.dryRun && profile) {
11508
- console.log(import_chalk9.default.bold("\n--- Profile injection preview ---\n"));
11509
- console.log(import_chalk9.default.dim(profile.body?.slice(0, 500) + (profile.body?.length > 500 ? "..." : "")));
11510
- console.log("");
11511
- }
11512
- if (result.stats.totalSignals === 0) {
11513
- console.log(import_chalk9.default.green("\n\u2713 No issues found. Draft looks clean!"));
11514
- return;
11515
- }
11516
- console.log(import_chalk9.default.bold("\nIssues:"));
11517
- for (const signal of result.signalMap.signals.slice(0, 10)) {
11518
- const sev = signal.severity === "red" ? import_chalk9.default.red("\u25CF") : import_chalk9.default.yellow("\u25CB");
11519
- const fix = signal.autoFixable ? import_chalk9.default.dim(" [auto-fixable]") : "";
11520
- console.log(` ${sev} line ${signal.line}: ${import_chalk9.default.dim(signal.id)} \u2014 ${signal.suggestion}${fix}`);
11521
- }
11522
- if (result.signalMap.signals.length > 10) {
11523
- console.log(import_chalk9.default.dim(` ... and ${result.signalMap.signals.length - 10} more`));
11524
- }
11525
- if (result.stats.autoFixed > 0) {
11526
- console.log(import_chalk9.default.green(`
11527
- ${result.stats.autoFixed} issues can be auto-fixed \u2014 run: hyv fix ${file}`));
11528
- }
11529
- if (options.output) {
11530
- const outputPath = path10.resolve(options.output);
11531
- fs9.writeFileSync(outputPath, promptResult.prompt);
11532
- console.log(import_chalk9.default.green(`
11533
- \u2713 Prompt written to ${outputPath}`));
11534
- } else {
11535
- console.log(import_chalk9.default.bold("\n--- Rewrite Prompt ---\n"));
11536
- console.log(promptResult.prompt);
11537
- console.log(import_chalk9.default.dim("\n--- End Prompt ---\n"));
11538
- console.log(import_chalk9.default.dim("Copy this prompt and paste it into your LLM."));
11539
- console.log(import_chalk9.default.dim("Or pipe directly: hyv rewrite draft.md | pbcopy"));
11540
- }
11541
- await maybeShowLimitedModeHint(!!profile);
11542
- } catch (error) {
11543
- console.error(import_chalk9.default.red(`Error: ${error.message}`));
11544
- process.exit(1);
11545
- }
11546
- });
11547
- }
11548
-
11549
- // src/commands/learning.ts
11550
- var import_chalk10 = __toESM(require_source());
11551
- var fs10 = __toESM(require("fs"));
11552
- var path11 = __toESM(require("path"));
11553
-
11554
- // src/lib/patterns.ts
11555
- var AI_OVERUSED2 = [
11556
- { id: "ai.delve", category: "ai-slop", severity: "red", regex: /\bdelve\b/gi, suggestion: "use a specific verb: dig, explore, look at" },
11557
- { id: "ai.leverage", category: "ai-slop", severity: "red", regex: /\bleverage\b/gi, suggestion: "use: use, apply, build on" },
11558
- { id: "ai.utilize", category: "ai-slop", severity: "red", regex: /\butilize\b/gi, suggestion: "use: use" },
11559
- { id: "ai.tapestry", category: "ai-slop", severity: "red", regex: /\btapestry\b/gi, suggestion: "be specific about what you mean" },
11560
- { id: "ai.holistic", category: "ai-slop", severity: "red", regex: /\bholistic\b/gi, suggestion: "describe the actual approach" },
11561
- { id: "ai.robust", category: "ai-slop", severity: "yellow", regex: /\brobust\b/gi, suggestion: "say what actually makes it strong" },
11562
- { id: "ai.pivotal", category: "ai-slop", severity: "yellow", regex: /\bpivotal\b/gi, suggestion: "say why it matters specifically" },
11563
- { id: "ai.foster", category: "ai-slop", severity: "yellow", regex: /\bfoster\b/gi, suggestion: "use: build, grow, encourage, support" },
11564
- { id: "ai.harness", category: "ai-slop", severity: "yellow", regex: /\bharness\b/gi, suggestion: "use: use, apply, work with" },
11565
- { id: "ai.illuminate", category: "ai-slop", severity: "yellow", regex: /\billuminate\b/gi, suggestion: "use: show, explain, highlight" },
11566
- { id: "ai.ever-evolving", category: "ai-slop", severity: "red", regex: /\b(?:ever[\s-]evolving|ever[\s-]changing)\b/gi, suggestion: "cut this \u2014 it says nothing" },
11567
- { id: "ai.fast-paced", category: "ai-slop", severity: "red", regex: /\bfast[\s-]paced\b/gi, suggestion: "cut this \u2014 every industry says this" },
11568
- { id: "ai.game-changer", category: "ai-slop", severity: "red", regex: /\bgame[\s-]changer\b/gi, suggestion: "explain what actually changed" },
11569
- { id: "ai.paradigm", category: "ai-slop", severity: "red", regex: /\bparadigm\b/gi, suggestion: "describe the actual shift" },
11570
- { id: "ai.synergy", category: "ai-slop", severity: "red", regex: /\bsynergy\b/gi, suggestion: "describe what works together and why" },
11571
- { id: "ai.ecosystem", category: "ai-slop", severity: "yellow", regex: /\becosystem\b/gi, suggestion: "name the specific tools/partners/platforms" },
11572
- { id: "ai.seamless", category: "ai-slop", severity: "yellow", regex: /\bseamless\b/gi, suggestion: "describe how it actually works" },
11573
- { id: "ai.actionable", category: "ai-slop", severity: "yellow", regex: /\bactionable\b/gi, suggestion: "just give the action, don't label it" },
11574
- { id: "ai.granular", category: "ai-slop", severity: "yellow", regex: /\bgranular\b/gi, suggestion: "say: specific, detailed, or name the level" },
11575
- { id: "ai.impactful", category: "ai-slop", severity: "yellow", regex: /\bimpactful\b/gi, suggestion: "describe the actual impact" },
11576
- { id: "ai.landscape", category: "ai-slop", severity: "red", regex: /\blandscape\b/gi, suggestion: "name the specific market/field/area" },
11577
- { id: "ai.realm", category: "ai-slop", severity: "red", regex: /\brealm\b/gi, suggestion: "name the specific domain" },
11578
- { id: "ai.straightforward", category: "ai-slop", severity: "yellow", regex: /\bstraightforward\b/gi, suggestion: "just explain it directly" }
11579
- ];
11580
- var FORMULAIC2 = [
11581
- { id: "formula.firstly", category: "ai-slop", severity: "yellow", regex: /\bfirstly\b/gi, suggestion: 'just start \u2014 "firstly" is filler' },
11582
- { id: "formula.secondly", category: "ai-slop", severity: "yellow", regex: /\bsecondly\b/gi, suggestion: 'just continue \u2014 "secondly" is filler' },
11583
- { id: "formula.lastly", category: "ai-slop", severity: "yellow", regex: /\blastly\b/gi, suggestion: 'just end \u2014 "lastly" is filler' },
11584
- { id: "formula.moreover", category: "ai-slop", severity: "yellow", regex: /\bmoreover\b/gi, suggestion: "just add the point" },
11585
- { id: "formula.furthermore", category: "ai-slop", severity: "yellow", regex: /\bfurthermore\b/gi, suggestion: "just add the point" },
11586
- { id: "formula.in-conclusion", category: "ai-slop", severity: "red", regex: /\bin conclusion\b/gi, suggestion: "just end. readers know it's the end." },
11587
- { id: "formula.in-summary", category: "ai-slop", severity: "red", regex: /\bin summary\b/gi, suggestion: "just summarize. the label is redundant." },
11588
- { id: "formula.it-is-important", category: "ai-slop", severity: "yellow", regex: /\bit is important to note\b/gi, suggestion: "just note it. skip the preamble." },
11589
- { id: "formula.at-the-end", category: "ai-slop", severity: "yellow", regex: /\bat the end of the day\b/gi, suggestion: "cut this \u2014 it means nothing" },
11590
- { id: "formula.needless-to-say", category: "ai-slop", severity: "yellow", regex: /\bneedless to say\b/gi, suggestion: "if it's needless, don't say it" },
11591
- { id: "formula.it-goes-without", category: "ai-slop", severity: "yellow", regex: /\bit goes without saying\b/gi, suggestion: "then don't say it" },
11592
- { id: "formula.in-today", category: "ai-slop", severity: "red", regex: /\bin today'?s\b/gi, suggestion: "start with your actual point instead" },
11593
- { id: "formula.lets-dive", category: "ai-slop", severity: "red", regex: /\blet'?s (?:dive|jump|dig|delve)\b/gi, suggestion: "just start. no diving needed." },
11594
- { id: "formula.without-further", category: "ai-slop", severity: "red", regex: /\bwithout further ado\b/gi, suggestion: "cut this \u2014 just get to it" },
11595
- { id: "formula.its-worth-noting", category: "ai-slop", severity: "yellow", regex: /\bit'?s worth noting\b/gi, suggestion: "just note it directly" },
11596
- { id: "formula.moving-forward", category: "ai-slop", severity: "yellow", regex: /\bmoving forward\b/gi, suggestion: "just say what happens next" },
11597
- { id: "formula.to-put-in-perspective", category: "ai-slop", severity: "yellow", regex: /\bto put this in perspective\b/gi, suggestion: "just give the perspective directly" },
11598
- { id: "formula.what-makes-interesting", category: "ai-slop", severity: "yellow", regex: /\bwhat makes this particularly interesting\b/gi, suggestion: "just say the interesting thing" },
11599
- { id: "formula.implications", category: "ai-slop", severity: "yellow", regex: /\bthe implications here are\b/gi, suggestion: "state the implications directly" },
11600
- { id: "formula.in-other-words", category: "ai-slop", severity: "red", regex: /\bin other words\b/gi, suggestion: "say it once, well" }
11601
- ];
11602
- var HEDGING2 = [
11603
- { id: "hedge.some-might", category: "voice-drift", severity: "yellow", regex: /\bsome might say\b/gi, suggestion: "commit to the claim or drop it" },
11604
- { id: "hedge.arguably", category: "voice-drift", severity: "yellow", regex: /\barguably\b/gi, suggestion: "commit. say it or don't." },
11605
- { id: "hedge.worth-noting", category: "voice-drift", severity: "yellow", regex: /\bit'?s worth noting\b/gi, suggestion: "just note it directly" },
11606
- { id: "hedge.to-some-extent", category: "voice-drift", severity: "yellow", regex: /\bto some extent\b/gi, suggestion: "be specific about the extent" },
11607
- { id: "hedge.perhaps", category: "voice-drift", severity: "yellow", regex: /\bperhaps\b/gi, suggestion: "commit or cut" },
11608
- { id: "hedge.it-seems", category: "voice-drift", severity: "yellow", regex: /\bit seems\b/gi, suggestion: "state it directly" }
11609
- ];
11610
- var STRUCTURE2 = [
11611
- { id: "struct.antithesis", category: "structure", severity: "yellow", regex: /\bnot (?:just|only) .{3,50}, but .{3,50}/gi, suggestion: "this antithesis pattern is an AI tell \u2014 restructure" },
11612
- { id: "struct.more-than-just", category: "structure", severity: "yellow", regex: /\bmore than just\b/gi, suggestion: "say what it IS, not what it isn't" },
11613
- { id: "struct.in-order-to", category: "structure", severity: "yellow", regex: /\bin order to\b/gi, suggestion: 'just use "to"' },
11614
- { id: "struct.due-to-the-fact", category: "structure", severity: "yellow", regex: /\bdue to the fact that\b/gi, suggestion: 'use "because"' },
11615
- { id: "struct.for-the-purpose", category: "structure", severity: "yellow", regex: /\bfor the purpose of\b/gi, suggestion: 'use "to"' },
11616
- { id: "struct.which-is-another", category: "structure", severity: "red", regex: /\bwhich is another way of saying\b/gi, suggestion: "just say the thing directly" },
11617
- { id: "struct.this-is-why", category: "structure", severity: "yellow", regex: /\bthis is (?:also )?(?:why|how|where|what\b)/gi, suggestion: "signpost claims are AI tells \u2014 just make the point" },
11618
- { id: "struct.heres-where", category: "structure", severity: "yellow", regex: /\bhere'?s (?:where|why|what|the part|the (?:harder|real|actual|main|bigger) problem)\b/gi, suggestion: "just make the point without the signpost" },
11619
- { id: "struct.rhetorical-truth", category: "structure", severity: "yellow", regex: /\b(?:the\s+)?(?:uncomfortable|hard|harsh|brutal|real|honest)\s+(?:truth|reality)\b/gi, suggestion: "state the fact directly, skip the framing" },
11620
- { id: "struct.lesson-setup", category: "structure", severity: "yellow", regex: /\bhere'?s what .{3,60} taught\b/gi, suggestion: "just share the lesson" },
11621
- // THE BIG ONE — antithesis negation pattern (Voice DNA: FATAL)
11622
- { id: "struct.this-isnt-x-this-is-y", category: "structure", severity: "red", regex: /\bthis isn'?t .{2,40}\.?\s*(?:this is|it'?s) .{2,40}/gi, suggestion: "FATAL: delete the negation, just state the positive claim" },
11623
- { id: "struct.not-x-y", category: "structure", severity: "red", regex: /\bnot .{2,30}\.?\s*.{2,30}\b/gi, suggestion: 'the "Not X. Y." pattern is an AI tell \u2014 just state Y' },
11624
- { id: "struct.forget-x", category: "structure", severity: "red", regex: /\bforget .{2,40}\.?\s*(?:this is|it'?s|you need)/gi, suggestion: "don't negate \u2014 just state what you mean" }
11625
- ];
11626
- var ENGAGEMENT_BAIT2 = [
11627
- { id: "bait.let-that-sink", category: "engagement-bait", severity: "red", regex: /\blet that sink in\b/gi, suggestion: "cut the sink. make your point and move on." },
11628
- { id: "bait.read-that-again", category: "engagement-bait", severity: "red", regex: /\bread that again\b/gi, suggestion: "if it needs repeating, repeat it yourself" },
11629
- { id: "bait.full-stop", category: "engagement-bait", severity: "red", regex: /\bfull stop\b/gi, suggestion: "the period already does this job" },
11630
- { id: "bait.this-changes-everything", category: "engagement-bait", severity: "red", regex: /\bthis changes everything\b/gi, suggestion: "prove it with specifics" },
11631
- { id: "bait.paying-attention", category: "engagement-bait", severity: "red", regex: /\bare you paying attention\b/gi, suggestion: "don't patronize the reader" },
11632
- { id: "bait.not-ready", category: "engagement-bait", severity: "red", regex: /\byou'?re not ready for this\b/gi, suggestion: "just deliver the content" }
11633
- ];
11634
- var AI_CRINGE2 = [
11635
- { id: "cringe.supercharge", category: "ai-cringe", severity: "red", regex: /\bsupercharge\b/gi, suggestion: "describe what it actually does" },
11636
- { id: "cringe.unlock", category: "ai-cringe", severity: "yellow", regex: /\bunlock\b/gi, suggestion: "describe what they get access to" },
11637
- { id: "cringe.future-proof", category: "ai-cringe", severity: "red", regex: /\bfuture[\s-]proof\b/gi, suggestion: "explain what specifically makes it durable" },
11638
- { id: "cringe.10x", category: "ai-cringe", severity: "red", regex: /\b10x\b/gi, suggestion: "use the actual numbers" },
11639
- { id: "cringe.ai-revolution", category: "ai-cringe", severity: "red", regex: /\bthe ai revolution\b/gi, suggestion: "describe the specific change" },
11640
- { id: "cringe.age-of-ai", category: "ai-cringe", severity: "red", regex: /\bin the age of ai\b/gi, suggestion: "just talk about what's happening now" },
11641
- { id: "cringe.happy-to-help", category: "ai-cringe", severity: "red", regex: /\bi'?d be happy to help\b/gi, suggestion: "just help. don't announce it." }
11642
- ];
11643
- var INSIDER_CLAIMS2 = [
11644
- { id: "insider.nobody-talking", category: "insider-claim", severity: "red", regex: /\bhere'?s the part nobody'?s? talking about\b/gi, suggestion: "just say the thing. the framing is noise." },
11645
- { id: "insider.nobody-tells", category: "insider-claim", severity: "red", regex: /\bwhat nobody tells you\b/gi, suggestion: "just tell them." },
11646
- { id: "insider.most-people", category: "insider-claim", severity: "yellow", regex: /\bmost people don'?t realize\b/gi, suggestion: "just explain it. skip the setup." },
11647
- { id: "insider.nobody-realizes", category: "insider-claim", severity: "red", regex: /\bnobody (?:realizes|talks about|mentions)\b/gi, suggestion: "if nobody talks about it, just talk about it" }
11648
- ];
11649
- var FORMATTING = [
11650
- { id: "format.em-dash", category: "formatting", severity: "red", regex: /\u2014/g, suggestion: "NO em dashes. use commas, periods, colons, semicolons, or parentheses." }
11651
- ];
11652
- var OGILVY2 = [
11653
- // Jargon — words that hide lack of understanding
11654
- { id: "ogilvy.jargon-utilize", category: "ai-slop", severity: "red", regex: /\butilize[sd]?\b/gi, suggestion: 'Ogilvy: use "use" instead' },
11655
- { id: "ogilvy.jargon-leverage", category: "ai-slop", severity: "red", regex: /\bleverage[ds]?\b/gi, suggestion: "Ogilvy: say what you actually mean" },
11656
- { id: "ogilvy.jargon-synergy", category: "ai-slop", severity: "red", regex: /\bsynerg(?:y|ies|istic)\b/gi, suggestion: "Ogilvy: describe what works together and why" },
11657
- { id: "ogilvy.jargon-bandwidth", category: "ai-slop", severity: "yellow", regex: /\bbandwidth\b/gi, suggestion: 'Ogilvy: say "time" or "capacity"' },
11658
- { id: "ogilvy.jargon-circle-back", category: "ai-slop", severity: "red", regex: /\bcircle back\b/gi, suggestion: 'Ogilvy: say "follow up" or "talk later"' },
11659
- { id: "ogilvy.jargon-low-hanging", category: "ai-slop", severity: "red", regex: /\blow-hanging fruit\b/gi, suggestion: "Ogilvy: name the specific easy win" },
11660
- { id: "ogilvy.jargon-move-the-needle", category: "ai-slop", severity: "red", regex: /\bmove the needle\b/gi, suggestion: "Ogilvy: describe the actual impact" },
11661
- { id: "ogilvy.jargon-touch-base", category: "ai-slop", severity: "red", regex: /\btouch base\b/gi, suggestion: 'Ogilvy: say "talk" or "meet"' },
11662
- { id: "ogilvy.jargon-take-it-offline", category: "ai-slop", severity: "red", regex: /\btake it offline\b/gi, suggestion: 'Ogilvy: say "discuss later"' },
11663
- { id: "ogilvy.jargon-deep-dive", category: "ai-slop", severity: "yellow", regex: /\bdeep dive\b/gi, suggestion: 'Ogilvy: say "look closely at" or "examine"' },
11664
- // Throat-clearing openers
11665
- { id: "ogilvy.preamble-i-want-to", category: "voice-drift", severity: "yellow", regex: /^\s*i want to (?:share|talk about|discuss|mention)\b/gi, suggestion: "Ogilvy: just say it. skip the preamble." },
11666
- { id: "ogilvy.preamble-just-wanted", category: "voice-drift", severity: "yellow", regex: /^\s*(?:i just wanted|i wanted to)\b/gi, suggestion: "Ogilvy: just say it." }
11667
- ];
11668
- var ALL_PATTERNS = [
11669
- ...AI_OVERUSED2,
11670
- ...FORMULAIC2,
11671
- ...HEDGING2,
11672
- ...STRUCTURE2,
11673
- ...ENGAGEMENT_BAIT2,
11674
- ...AI_CRINGE2,
11675
- ...INSIDER_CLAIMS2,
11676
- ...FORMATTING,
11677
- ...OGILVY2
11678
- ];
11679
- function scanLine(line, lineNum, filePath) {
11680
- const findings = [];
11681
- for (const pat of ALL_PATTERNS) {
11682
- pat.regex.lastIndex = 0;
11683
- let match;
11684
- while ((match = pat.regex.exec(line)) !== null) {
11685
- findings.push({
11686
- file: filePath,
11687
- line: lineNum,
11688
- column: match.index + 1,
11689
- pattern: pat.id,
11690
- category: pat.category,
11691
- severity: pat.severity,
11692
- excerpt: highlightMatch2(line.trim(), match.index, match[0].length),
11693
- suggestion: pat.suggestion
11694
- });
11695
- }
11696
- }
11697
- return findings;
11698
- }
11699
- function highlightMatch2(line, start, len) {
11700
- const before = line.slice(0, start);
11701
- const match = line.slice(start, start + len);
11702
- const after = line.slice(start + len);
11703
- return `${before}\xAB${match}\xBB${after}`.slice(0, 120);
11704
- }
11705
-
11706
- // src/lib/scan.ts
11707
- var AI_PATTERN_RULES = [
11708
- {
11709
- id: "ai_antithesis",
11710
- pattern: /\b(?:it'?s|isn'?t|is\s+not)\s+not\s+just\b.{0,80}\b(?:it'?s|but)\b/i
11711
- },
11712
- {
11713
- id: "not_just_but",
11714
- pattern: /\bnot\s+just\b.{3,80}\bbut\s+(?:also\s+)?/i
11715
- },
11716
- {
11717
- id: "more_than_just",
11718
- pattern: /\bmore\s+than\s+just\b/i
11719
- },
11720
- {
11721
- id: "rhetorical_truth_setup",
11722
- pattern: /\b(?:the\s+)?(?:uncomfortable|hard|harsh|brutal|ugly|unsexy|real|honest)\s+(?:truth|reality)\b/i
11723
- },
11724
- {
11725
- id: "truth_is",
11726
- pattern: /\bthe\s+truth\s+is\b/i
11727
- },
11728
- {
11729
- id: "lesson_setup",
11730
- pattern: /\b(?:here'?s\s+)?what\s+.{3,80}\s+(?:taught|teaches)\s+(?:me|us|you|everyone)\b/i
11731
- },
11732
- {
11733
- id: "negation_cascade",
11734
- pattern: /\b(?:no|not)\s+\w[^.!?\n]{0,80}[.!?][ \t]*\n?[ \t]*(?:no|not)\s+\w[^.!?\n]{0,80}[.!?][ \t]*\n?[ \t]*(?:no|not)\s+\w/i
11735
- },
11736
- {
11737
- id: "formulaic_connector",
11738
- pattern: /\b(?:firstly|secondly|thirdly|lastly|moreover|furthermore|in conclusion|to summarize|to sum up|in summary|it is important to note|it should be noted)\b/i
11739
- },
11740
- {
11741
- id: "ai_words",
11742
- pattern: /\b(?:delve|underscore|testament|intricate|multifaceted|cornerstone|landscape|foster|harness|leverage|tapestry|illuminate|pivotal|elevate|empower|seamlessly|revolutionize|supercharge|transformative|holistic|comprehensive|innovative|impactful|meaningful|utilize|paradigm|navigate|endeavor|realm|profound|encapsulate|synergy|robust|facilitate|bolster|streamline|differentiate|myriad|unlock|transform)\b/i
11743
- },
11744
- {
11745
- id: "em_dash",
11746
- pattern: /\u2014/
11747
- },
11748
- {
11749
- id: "signpost_claim",
11750
- pattern: /\bthis\s+is\s+(?:also\s+)?(?:why|how|where|what\b|what\s+happens\s+when)\b|\b(?:here'?s|here\s+is)\s+(?:where|why|what|the\s+part|the\s+(?:harder|real|actual|main|bigger)\s+problem)\b/i
11751
- },
11752
- {
11753
- id: "generic_buyer_psychology",
11754
- pattern: /\bpeople\s+don'?t\s+just\s+buy\b|\bpeople\s+buy\s+the\s+feeling\b/i
11755
- },
11756
- {
11757
- id: "founder_cadence_restatement",
11758
- pattern: /\bwhich\s+is\s+another\s+way\s+of\s+saying\b|\bin\s+other\s+words\b/i
11759
- },
11760
- {
11761
- id: "founder_cadence_moment_becomes",
11762
- pattern: /\b(?:the\s+)?moment\b.{3,80}\bbecomes?\b|\bbecomes?\s+(?:dangerous|useful|interesting|real|obvious)\s+the\s+moment\b/i
11763
- },
11764
- {
11765
- id: "founder_cadence_same_better",
11766
- pattern: /\bsame\s+[^.!?\n]{1,35}[.!?]\s*(?:better|nicer|cleaner|calmer|safer)\s+[^.!?\n]{1,35}[.!?]?/i
11767
- }
11768
- ];
11769
- var ABSTRACT_STYLE_WORDS = /* @__PURE__ */ new Set([
11770
- "alignment",
11771
- "authenticity",
11772
- "awareness",
11773
- "clarity",
11774
- "confidence",
11775
- "consistency",
11776
- "differentiation",
11777
- "execution",
11778
- "framework",
11779
- "identity",
11780
- "messaging",
11781
- "narrative",
11782
- "personality",
11783
- "positioning",
11784
- "preference",
11785
- "presence",
11786
- "recall",
11787
- "relevance",
11788
- "resonance",
11789
- "signal",
11790
- "strategy",
11791
- "trust",
11792
- "utility",
11793
- "value"
11794
- ]);
11795
- var GENERIC_OPENERS = /^(?:most|many)\s+(?:brands|teams|people|founders|companies)\b/i;
11796
- var QUESTION_OPENER = /^(?:have you|do you|did you|what if|why do|how do)\b/i;
11797
- var LESSON_OPENER = /^(?:the most important thing|the key to|success is|if you want to|what i learned)\b/i;
11798
- function words(text) {
11799
- return (text || "").match(/[a-zA-Z][a-zA-Z0-9']*/g) || [];
11800
- }
11801
- function sentences(text) {
11802
- return (text || "").split(/(?<=[.!?])\s+|\n{2,}/).map((s) => s.trim()).filter((s) => words(s).length > 0);
11803
- }
11804
- function paragraphs(text) {
11805
- return (text || "").split(/\n\s*\n/).map((p) => p.trim()).filter((p) => words(p).length >= 6);
11806
- }
11807
- function lineStyleHits(line) {
11808
- const low = (line || "").trim().toLowerCase();
11809
- if (!low)
11810
- return [];
11811
- const hits = [];
11812
- const lineWords = low.match(/[a-z']+/g) || [];
11813
- const abstractCount = lineWords.filter((w) => ABSTRACT_STYLE_WORDS.has(w)).length;
11814
- if (abstractCount >= 3 && !/\b(?:for example|for instance|such as)\b|\d/i.test(low)) {
11815
- hits.push({
11816
- line: 0,
11817
- // Will be set by caller
11818
- rule: "abstract_noun_cluster",
11819
- phrase: line.trim().slice(0, 160)
11820
- });
11821
- }
11822
- if (GENERIC_OPENERS.test(low)) {
11823
- hits.push({
11824
- line: 0,
11825
- rule: "generic_opening_generalization",
11826
- phrase: line.trim().slice(0, 160)
11827
- });
11828
- }
11829
- if (QUESTION_OPENER.test(low)) {
11830
- hits.push({
11831
- line: 0,
11832
- rule: "voice_question_opener",
11833
- phrase: "opens with a question instead of a concrete observation"
11834
- });
11835
- }
11836
- if (LESSON_OPENER.test(low)) {
11837
- hits.push({
11838
- line: 0,
11839
- rule: "voice_lesson_opener",
11840
- phrase: "opens with a lesson or inspirational claim"
11841
- });
11842
- }
11843
- return hits;
11844
- }
11845
- function scanText(text) {
11846
- const hits = [];
11847
- const safeText = text || "";
11848
- for (const rule of AI_PATTERN_RULES) {
11849
- const regex = new RegExp(rule.pattern.source, rule.pattern.flags.replace("g", "") + "g");
11850
- let match;
11851
- let lastIndex = 0;
11852
- while ((match = regex.exec(safeText)) !== null) {
11853
- const snippet = match[0].trim();
11854
- if (!snippet) {
11855
- if (match.index === regex.lastIndex)
11856
- regex.lastIndex++;
11857
- continue;
12455
+ if (options.profile && !profile) {
12456
+ await requirePaidFeature("premiumPrompts");
11858
12457
  }
11859
- const lineNo = safeText.slice(0, match.index).split("\n").length;
11860
- hits.push({
11861
- line: lineNo,
11862
- rule: rule.id,
11863
- phrase: snippet.slice(0, 160)
12458
+ const { text: draftContent, path: draftPath } = readText(file);
12459
+ const result = runPipeline(draftContent, profile, false);
12460
+ const promptResult = buildRewritePrompt({
12461
+ draftPath,
12462
+ draftContent,
12463
+ profileName: options.profile,
12464
+ profile,
12465
+ format: options.format,
12466
+ constraints: options.constraints,
12467
+ skipScan: options.scan === false
11864
12468
  });
11865
- if (match.index === regex.lastIndex)
11866
- regex.lastIndex++;
11867
- }
11868
- }
11869
- const lines = safeText.split("\n");
11870
- for (let i = 0; i < lines.length; i++) {
11871
- const lineNum = i + 1;
11872
- const lineText = lines[i];
11873
- const patternHits = scanLine(lineText, lineNum, "inline");
11874
- for (const hit of patternHits) {
11875
- hits.push({ line: hit.line, rule: hit.pattern, severity: hit.severity, phrase: hit.excerpt || "", text: lineText.trim().slice(0, 120) });
11876
- }
11877
- const lineHits = lineStyleHits(lineText);
11878
- for (const hit of lineHits) {
11879
- hit.line = lineNum;
11880
- hit.text = lineText.trim().slice(0, 240);
11881
- hits.push(hit);
11882
- }
11883
- }
11884
- const sentenceHits = [];
11885
- for (let i = 0; i < lines.length; i++) {
11886
- const lineSentences = lines[i].split(/(?<=[.!?])\s+/);
11887
- for (const sentence of lineSentences) {
11888
- const wordCount = words(sentence).length;
11889
- if (wordCount > 0) {
11890
- sentenceHits.push({ line: i + 1, text: sentence.trim(), wordCount });
12469
+ console.log(import_chalk9.default.dim(`
12470
+ Draft: ${draftPath}`));
12471
+ console.log(import_chalk9.default.dim(`Profile: ${promptResult.profileUsed || "none (using generic rules)"}`));
12472
+ if (profile && hasRichProfile(profile)) {
12473
+ console.log(import_chalk9.default.dim(` never-list: ${profile.never_list?.length || 0} learned: ${profile.learned_patterns?.length || 0}`));
11891
12474
  }
12475
+ console.log(import_chalk9.default.dim(`Issues found: ${result.stats.totalSignals}`));
12476
+ console.log(import_chalk9.default.dim(`Score: ${result.score}/100`));
12477
+ if (options.dryRun && profile) {
12478
+ console.log(import_chalk9.default.bold("\n--- Profile injection preview ---\n"));
12479
+ console.log(import_chalk9.default.dim(profile.body?.slice(0, 500) + (profile.body?.length > 500 ? "..." : "")));
12480
+ console.log("");
12481
+ }
12482
+ if (result.stats.totalSignals === 0) {
12483
+ console.log(import_chalk9.default.green("\n\u2713 No issues found. Draft looks clean!"));
12484
+ return;
12485
+ }
12486
+ console.log(import_chalk9.default.bold("\nIssues:"));
12487
+ for (const signal of result.signalMap.signals.slice(0, 10)) {
12488
+ const sev = signal.severity === "red" ? import_chalk9.default.red("\u25CF") : import_chalk9.default.yellow("\u25CB");
12489
+ const fix = signal.autoFixable ? import_chalk9.default.dim(" [auto-fixable]") : "";
12490
+ console.log(` ${sev} line ${signal.line}: ${import_chalk9.default.dim(signal.id)} \u2014 ${signal.suggestion}${fix}`);
12491
+ }
12492
+ if (result.signalMap.signals.length > 10) {
12493
+ console.log(import_chalk9.default.dim(` ... and ${result.signalMap.signals.length - 10} more`));
12494
+ }
12495
+ if (result.stats.autoFixed > 0) {
12496
+ console.log(import_chalk9.default.green(`
12497
+ ${result.stats.autoFixed} issues can be auto-fixed \u2014 run: hyv fix ${file}`));
12498
+ }
12499
+ if (options.output) {
12500
+ const outputPath = path10.resolve(options.output);
12501
+ fs9.writeFileSync(outputPath, promptResult.prompt);
12502
+ console.log(import_chalk9.default.green(`
12503
+ \u2713 Prompt written to ${outputPath}`));
12504
+ } else {
12505
+ console.log(import_chalk9.default.bold("\n--- Rewrite Prompt ---\n"));
12506
+ console.log(promptResult.prompt);
12507
+ console.log(import_chalk9.default.dim("\n--- End Prompt ---\n"));
12508
+ console.log(import_chalk9.default.dim("Copy this prompt and paste it into your LLM."));
12509
+ console.log(import_chalk9.default.dim("Or pipe directly: hyv rewrite draft.md | pbcopy"));
12510
+ }
12511
+ await maybeShowLimitedModeHint(!!profile);
12512
+ } catch (error) {
12513
+ console.error(import_chalk9.default.red(`Error: ${error.message}`));
12514
+ process.exit(1);
11892
12515
  }
11893
- }
11894
- for (let i = 0; i < sentenceHits.length - 2; i++) {
11895
- const window = sentenceHits.slice(i, i + 3);
11896
- if (window.every((s) => s.wordCount <= 5)) {
11897
- hits.push({
11898
- line: window[0].line,
11899
- rule: "voice_staccato_triplet",
11900
- phrase: "three short sentences in a row reads like performance",
11901
- text: window[0].text
11902
- });
11903
- break;
11904
- }
11905
- }
11906
- return hits.sort((a, b) => {
11907
- if (a.line !== b.line)
11908
- return a.line - b.line;
11909
- return a.rule.localeCompare(b.rule);
11910
12516
  });
11911
12517
  }
11912
12518
 
12519
+ // src/commands/learning.ts
12520
+ var import_chalk10 = __toESM(require_source());
12521
+ var fs10 = __toESM(require("fs"));
12522
+ var path11 = __toESM(require("path"));
12523
+
11913
12524
  // src/lib/diff.ts
12525
+ init_scan();
11914
12526
  function linesChanged(origLine, accLine) {
11915
12527
  return origLine.trim() !== accLine.trim();
11916
12528
  }
@@ -12228,11 +12840,12 @@ function printProfileImpact(impact, data) {
12228
12840
  }
12229
12841
 
12230
12842
  // src/commands/onboarding.ts
12231
- var import_chalk11 = __toESM(require_source());
12232
- var fs11 = __toESM(require("fs"));
12233
- var path12 = __toESM(require("path"));
12843
+ var import_chalk12 = __toESM(require_source());
12844
+ var fs12 = __toESM(require("fs"));
12845
+ var path13 = __toESM(require("path"));
12234
12846
  init_config();
12235
12847
  init_auth();
12848
+ init_scan();
12236
12849
  var UNIVERSAL_QUESTIONS = [
12237
12850
  { key: "tone", question: "How would you describe your tone? (e.g., casual, professional, witty, direct)" },
12238
12851
  { key: "audience", question: "Who is your audience? (e.g., founders, developers, marketers)" },
@@ -12259,7 +12872,7 @@ var INDUSTRY_QUESTIONS = {
12259
12872
  { key: "length", question: "Do you prefer short punchy content or longer pieces?" }
12260
12873
  ]
12261
12874
  };
12262
- function extractStats(samples) {
12875
+ function extractStats2(samples) {
12263
12876
  const combined = samples.map((s) => s.text).join("\n\n");
12264
12877
  const allWords = words(combined);
12265
12878
  const allSentences = sentences(combined);
@@ -12315,7 +12928,7 @@ function registerOnboardingCommands(program3) {
12315
12928
  await flashcardOnboarding(name, token);
12316
12929
  }
12317
12930
  } catch (error) {
12318
- console.error(import_chalk11.default.red(`
12931
+ console.error(import_chalk12.default.red(`
12319
12932
  Error: ${error.message}`));
12320
12933
  process.exit(1);
12321
12934
  }
@@ -12325,16 +12938,16 @@ Error: ${error.message}`));
12325
12938
  async function flashcardOnboarding(name, token) {
12326
12939
  const { printWelcome: printWelcome2 } = await Promise.resolve().then(() => (init_welcome(), welcome_exports));
12327
12940
  printWelcome2({ condensed: true, skipDemo: true });
12328
- console.log(import_chalk11.default.bold(`Creating voice profile: ${name}
12941
+ console.log(import_chalk12.default.bold(`Creating voice profile: ${name}
12329
12942
  `));
12330
- console.log(import_chalk11.default.dim("Answer these questions to define your voice.\n"));
12943
+ console.log(import_chalk12.default.dim("Answer these questions to define your voice.\n"));
12331
12944
  const answers = {};
12332
12945
  for (const q of UNIVERSAL_QUESTIONS) {
12333
12946
  const answer = await askQuestion(q.question);
12334
12947
  answers[q.key] = answer;
12335
12948
  }
12336
12949
  const industry = answers.format?.toLowerCase().includes("code") || answers.audience?.toLowerCase().includes("developer") ? "tech" : answers.format?.toLowerCase().includes("market") || answers.audience?.toLowerCase().includes("market") ? "marketing" : "default";
12337
- console.log(import_chalk11.default.dim(`
12950
+ console.log(import_chalk12.default.dim(`
12338
12951
  Industry: ${industry}
12339
12952
  `));
12340
12953
  for (const q of INDUSTRY_QUESTIONS[industry]) {
@@ -12342,7 +12955,7 @@ Industry: ${industry}
12342
12955
  answers[q.key] = answer;
12343
12956
  }
12344
12957
  if (token) {
12345
- console.log(import_chalk11.default.cyan("\nGenerating profile on server..."));
12958
+ console.log(import_chalk12.default.cyan("\nGenerating profile on server..."));
12346
12959
  const response = await authenticatedRequest(
12347
12960
  cliApiUrl("/cli/profiles/new"),
12348
12961
  {
@@ -12359,60 +12972,60 @@ Industry: ${industry}
12359
12972
  const profileContent = data.content || generateLocalProfile(name, answers);
12360
12973
  ensureHyvDir();
12361
12974
  writeCachedProfile(name, profileContent);
12362
- console.log(import_chalk11.default.green(`
12975
+ console.log(import_chalk12.default.green(`
12363
12976
  \u2713 Profile created: ${name}`));
12364
- console.log(import_chalk11.default.dim("Run `hyv profiles` to see your profiles."));
12977
+ console.log(import_chalk12.default.dim("Run `hyv profiles` to see your profiles."));
12365
12978
  } else {
12366
- console.log(import_chalk11.default.yellow("\nServer unavailable. Creating local profile..."));
12979
+ console.log(import_chalk12.default.yellow("\nServer unavailable. Creating local profile..."));
12367
12980
  const profileContent = generateLocalProfile(name, answers);
12368
12981
  ensureHyvDir();
12369
12982
  writeCachedProfile(name, profileContent);
12370
- console.log(import_chalk11.default.green(`
12983
+ console.log(import_chalk12.default.green(`
12371
12984
  \u2713 Local profile created: ${name}`));
12372
12985
  }
12373
12986
  } else {
12374
- console.log(import_chalk11.default.yellow("\nNot authenticated. Creating local profile..."));
12987
+ console.log(import_chalk12.default.yellow("\nNot authenticated. Creating local profile..."));
12375
12988
  const profileContent = generateLocalProfile(name, answers);
12376
12989
  ensureHyvDir();
12377
12990
  writeCachedProfile(name, profileContent);
12378
- console.log(import_chalk11.default.green(`
12991
+ console.log(import_chalk12.default.green(`
12379
12992
  \u2713 Local profile created: ${name}`));
12380
12993
  }
12381
12994
  }
12382
12995
  async function createFromSamples(name, sampleDir, token) {
12383
- const dirPath = path12.resolve(sampleDir);
12384
- if (!fs11.existsSync(dirPath)) {
12996
+ const dirPath = path13.resolve(sampleDir);
12997
+ if (!fs12.existsSync(dirPath)) {
12385
12998
  throw new Error(`Directory not found: ${dirPath}`);
12386
12999
  }
12387
- console.log(import_chalk11.default.bold(`
13000
+ console.log(import_chalk12.default.bold(`
12388
13001
  Creating voice profile: ${name}`));
12389
- console.log(import_chalk11.default.dim(`Reading samples from: ${dirPath}
13002
+ console.log(import_chalk12.default.dim(`Reading samples from: ${dirPath}
12390
13003
  `));
12391
- const files = fs11.readdirSync(dirPath).filter((f) => f.endsWith(".md") || f.endsWith(".txt")).map((f) => path12.join(dirPath, f));
13004
+ const files = fs12.readdirSync(dirPath).filter((f) => f.endsWith(".md") || f.endsWith(".txt")).map((f) => path13.join(dirPath, f));
12392
13005
  if (files.length === 0) {
12393
13006
  throw new Error("No .md or .txt files found in directory");
12394
13007
  }
12395
13008
  const samples = [];
12396
13009
  for (const file of files) {
12397
- const text = fs11.readFileSync(file, "utf-8");
13010
+ const text = fs12.readFileSync(file, "utf-8");
12398
13011
  if (text.trim().length > 0) {
12399
13012
  samples.push({ path: file, text });
12400
- console.log(import_chalk11.default.dim(` \u2022 ${path12.basename(file)} (${words(text).length} words)`));
13013
+ console.log(import_chalk12.default.dim(` \u2022 ${path13.basename(file)} (${words(text).length} words)`));
12401
13014
  }
12402
13015
  }
12403
13016
  if (samples.length === 0) {
12404
13017
  throw new Error("No readable text found in files");
12405
13018
  }
12406
- const stats = extractStats(samples);
12407
- console.log(import_chalk11.default.dim(`
13019
+ const stats = extractStats2(samples);
13020
+ console.log(import_chalk12.default.dim(`
12408
13021
  Stats extracted:`));
12409
- console.log(import_chalk11.default.dim(` Words: ${stats.word_count}`));
12410
- console.log(import_chalk11.default.dim(` Sentences: ${stats.sentence_count}`));
12411
- console.log(import_chalk11.default.dim(` Avg sentence: ${stats.avg_sentence_length} words`));
12412
- console.log(import_chalk11.default.dim(` Case style: ${stats.case_style}`));
12413
- console.log(import_chalk11.default.dim(` Argument pattern: ${stats.argument_pattern}`));
13022
+ console.log(import_chalk12.default.dim(` Words: ${stats.word_count}`));
13023
+ console.log(import_chalk12.default.dim(` Sentences: ${stats.sentence_count}`));
13024
+ console.log(import_chalk12.default.dim(` Avg sentence: ${stats.avg_sentence_length} words`));
13025
+ console.log(import_chalk12.default.dim(` Case style: ${stats.case_style}`));
13026
+ console.log(import_chalk12.default.dim(` Argument pattern: ${stats.argument_pattern}`));
12414
13027
  if (token) {
12415
- console.log(import_chalk11.default.cyan("\nGenerating profile on server..."));
13028
+ console.log(import_chalk12.default.cyan("\nGenerating profile on server..."));
12416
13029
  const response = await authenticatedRequest(
12417
13030
  cliApiUrl("/cli/profiles/new"),
12418
13031
  {
@@ -12428,25 +13041,25 @@ Stats extracted:`));
12428
13041
  const data = response.data;
12429
13042
  ensureHyvDir();
12430
13043
  writeCachedProfile(name, data.content || generateLocalProfileFromStats(name, stats));
12431
- console.log(import_chalk11.default.green(`
13044
+ console.log(import_chalk12.default.green(`
12432
13045
  \u2713 Profile created: ${name}`));
12433
13046
  } else {
12434
- console.log(import_chalk11.default.yellow("\nServer unavailable. Creating local profile..."));
13047
+ console.log(import_chalk12.default.yellow("\nServer unavailable. Creating local profile..."));
12435
13048
  ensureHyvDir();
12436
13049
  writeCachedProfile(name, generateLocalProfileFromStats(name, stats));
12437
- console.log(import_chalk11.default.green(`
13050
+ console.log(import_chalk12.default.green(`
12438
13051
  \u2713 Local profile created: ${name}`));
12439
13052
  }
12440
13053
  } else {
12441
- console.log(import_chalk11.default.yellow("\nNot authenticated. Creating local profile..."));
13054
+ console.log(import_chalk12.default.yellow("\nNot authenticated. Creating local profile..."));
12442
13055
  ensureHyvDir();
12443
13056
  writeCachedProfile(name, generateLocalProfileFromStats(name, stats));
12444
- console.log(import_chalk11.default.green(`
13057
+ console.log(import_chalk12.default.green(`
12445
13058
  \u2713 Local profile created: ${name}`));
12446
13059
  }
12447
13060
  }
12448
13061
  async function generateExtractionPrompt(name) {
12449
- console.log(import_chalk11.default.bold(`
13062
+ console.log(import_chalk12.default.bold(`
12450
13063
  Generating extraction prompt for: ${name}
12451
13064
  `));
12452
13065
  const prompt = `I need you to analyze writing samples and create a voice profile.
@@ -12468,33 +13081,33 @@ function registerImportCommand(program3) {
12468
13081
  program3.command("import").description("Import a voice profile from file").argument("<name>", "Profile name").argument("<file>", "Profile markdown file").action(async (name, file) => {
12469
13082
  try {
12470
13083
  assertSafeProfileName(name);
12471
- const filePath = path12.resolve(file);
12472
- if (!fs11.existsSync(filePath)) {
12473
- console.error(import_chalk11.default.red(`File not found: ${filePath}`));
13084
+ const filePath = path13.resolve(file);
13085
+ if (!fs12.existsSync(filePath)) {
13086
+ console.error(import_chalk12.default.red(`File not found: ${filePath}`));
12474
13087
  process.exit(1);
12475
13088
  }
12476
- const content = fs11.readFileSync(filePath, "utf-8");
13089
+ const content = fs12.readFileSync(filePath, "utf-8");
12477
13090
  ensureHyvDir();
12478
13091
  writeCachedProfile(name, content);
12479
- console.log(import_chalk11.default.green(`
13092
+ console.log(import_chalk12.default.green(`
12480
13093
  \u2713 Profile imported: ${name}`));
12481
13094
  } catch (error) {
12482
- console.error(import_chalk11.default.red(`Error: ${error.message}`));
13095
+ console.error(import_chalk12.default.red(`Error: ${error.message}`));
12483
13096
  process.exit(1);
12484
13097
  }
12485
13098
  });
12486
13099
  }
12487
13100
  function askQuestion(question) {
12488
- return new Promise((resolve14) => {
12489
- const readline2 = require("readline");
12490
- const rl = readline2.createInterface({
13101
+ return new Promise((resolve15) => {
13102
+ const readline3 = require("readline");
13103
+ const rl = readline3.createInterface({
12491
13104
  input: process.stdin,
12492
13105
  output: process.stdout
12493
13106
  });
12494
- rl.question(import_chalk11.default.cyan(` ${question}
13107
+ rl.question(import_chalk12.default.cyan(` ${question}
12495
13108
  > `), (answer) => {
12496
13109
  rl.close();
12497
- resolve14(answer.trim());
13110
+ resolve15(answer.trim());
12498
13111
  });
12499
13112
  });
12500
13113
  }
@@ -12558,7 +13171,7 @@ function generateLocalProfileFromStats(name, stats) {
12558
13171
  }
12559
13172
 
12560
13173
  // src/commands/plan.ts
12561
- var import_chalk12 = __toESM(require_source());
13174
+ var import_chalk13 = __toESM(require_source());
12562
13175
  init_config();
12563
13176
  init_auth();
12564
13177
  var import_open2 = __toESM(require_open());
@@ -12573,10 +13186,10 @@ function registerPlanCommand(program3) {
12573
13186
  }
12574
13187
  const token = getToken();
12575
13188
  if (!token) {
12576
- console.log(import_chalk12.default.yellow("\nNot authenticated \u2014 free tier still works.\n"));
12577
- console.log(import_chalk12.default.dim(" hyv scan draft.md | hyv welcome | npx @holdyourvoice/hyv scan draft.md"));
13189
+ console.log(import_chalk13.default.yellow("\nNot authenticated \u2014 free tier still works.\n"));
13190
+ console.log(import_chalk13.default.dim(" hyv scan draft.md | hyv welcome | npx @holdyourvoice/hyv scan draft.md"));
12578
13191
  printFreePaidMatrix({ compact: true });
12579
- console.log(import_chalk12.default.dim(" Run `hyv init` for profiles + learning.\n"));
13192
+ console.log(import_chalk13.default.dim(" Run `hyv init` for profiles + learning.\n"));
12580
13193
  return;
12581
13194
  }
12582
13195
  if (options.upgrade) {
@@ -12589,20 +13202,20 @@ function registerPlanCommand(program3) {
12589
13202
  await showPlan();
12590
13203
  }
12591
13204
  } catch (error) {
12592
- console.error(import_chalk12.default.red(`
13205
+ console.error(import_chalk13.default.red(`
12593
13206
  Error: ${error.message}`));
12594
13207
  process.exit(1);
12595
13208
  }
12596
13209
  });
12597
13210
  }
12598
13211
  async function showPlan() {
12599
- console.log(import_chalk12.default.bold("\nSubscription Plan\n"));
13212
+ console.log(import_chalk13.default.bold("\nSubscription Plan\n"));
12600
13213
  const response = await authenticatedRequest(
12601
13214
  cliApiUrl("/cli/heartbeat"),
12602
13215
  { method: "GET" }
12603
13216
  );
12604
13217
  if (response.status !== 200) {
12605
- console.log(import_chalk12.default.yellow("Could not fetch plan info."));
13218
+ console.log(import_chalk13.default.yellow("Could not fetch plan info."));
12606
13219
  return;
12607
13220
  }
12608
13221
  const data = response.data;
@@ -12620,27 +13233,27 @@ async function showPlan() {
12620
13233
  team: "$29/mo",
12621
13234
  agency: "Custom"
12622
13235
  };
12623
- console.log(import_chalk12.default.dim("Plan:"), import_chalk12.default.bold(planNames[plan] || plan));
12624
- console.log(import_chalk12.default.dim("Price:"), planPrices[plan] || "-");
12625
- console.log(import_chalk12.default.dim("Status:"), data.subscription_status || "none");
13236
+ console.log(import_chalk13.default.dim("Plan:"), import_chalk13.default.bold(planNames[plan] || plan));
13237
+ console.log(import_chalk13.default.dim("Price:"), planPrices[plan] || "-");
13238
+ console.log(import_chalk13.default.dim("Status:"), data.subscription_status || "none");
12626
13239
  if (license) {
12627
- console.log(import_chalk12.default.dim("License:"), license.key_hint);
13240
+ console.log(import_chalk13.default.dim("License:"), license.key_hint);
12628
13241
  }
12629
13242
  const access = await getAccessState();
12630
- console.log(import_chalk12.default.bold("\nFree tier (always)"));
12631
- console.log(import_chalk12.default.dim(" local scan, fix, check, mcp, all web tools \u2014 hyv welcome"));
13243
+ console.log(import_chalk13.default.bold("\nFree tier (always)"));
13244
+ console.log(import_chalk13.default.dim(" local scan, fix, check, mcp, all web tools \u2014 hyv welcome"));
12632
13245
  if (plan === "none" || !access.hasPaidPlan) {
12633
- console.log(import_chalk12.default.bold("\nUpgrade unlocks"));
12634
- console.log(import_chalk12.default.dim(" profiles, learning loop, hybrid analysis, rich rewrites"));
12635
- console.log(import_chalk12.default.dim(` ${PRICING_URL}`));
12636
- console.log(import_chalk12.default.dim("\n hyv plan --upgrade | hyv plan --free for full matrix"));
13246
+ console.log(import_chalk13.default.bold("\nUpgrade unlocks"));
13247
+ console.log(import_chalk13.default.dim(" profiles, learning loop, hybrid analysis, rich rewrites"));
13248
+ console.log(import_chalk13.default.dim(` ${PRICING_URL}`));
13249
+ console.log(import_chalk13.default.dim("\n hyv plan --upgrade | hyv plan --free for full matrix"));
12637
13250
  } else {
12638
13251
  console.log("\nManage your subscription:");
12639
- console.log(import_chalk12.default.dim(" hyv plan --manage"));
13252
+ console.log(import_chalk13.default.dim(" hyv plan --manage"));
12640
13253
  }
12641
13254
  }
12642
13255
  async function upgradePlan() {
12643
- console.log(import_chalk12.default.cyan("\nOpening checkout...\n"));
13256
+ console.log(import_chalk13.default.cyan("\nOpening checkout...\n"));
12644
13257
  const response = await authenticatedRequest(
12645
13258
  cliApiUrl("/cli/subscribe"),
12646
13259
  {
@@ -12652,20 +13265,20 @@ async function upgradePlan() {
12652
13265
  const data = response.data;
12653
13266
  const checkoutUrl = data.checkout_url;
12654
13267
  if (checkoutUrl) {
12655
- console.log(import_chalk12.default.dim("Opening browser..."));
13268
+ console.log(import_chalk13.default.dim("Opening browser..."));
12656
13269
  await (0, import_open2.default)(assertSafeOpenUrl(checkoutUrl));
12657
- console.log(import_chalk12.default.green("\n\u2713 Checkout opened in browser"));
12658
- console.log(import_chalk12.default.dim("Complete the checkout to activate your plan."));
13270
+ console.log(import_chalk13.default.green("\n\u2713 Checkout opened in browser"));
13271
+ console.log(import_chalk13.default.dim("Complete the checkout to activate your plan."));
12659
13272
  } else {
12660
- console.log(import_chalk12.default.yellow("No checkout URL received."));
13273
+ console.log(import_chalk13.default.yellow("No checkout URL received."));
12661
13274
  }
12662
13275
  } else {
12663
- console.log(import_chalk12.default.yellow("Could not create checkout session."));
12664
- console.log(import_chalk12.default.dim("Visit https://holdyourvoice.com/pricing to subscribe."));
13276
+ console.log(import_chalk13.default.yellow("Could not create checkout session."));
13277
+ console.log(import_chalk13.default.dim("Visit https://holdyourvoice.com/pricing to subscribe."));
12665
13278
  }
12666
13279
  }
12667
13280
  async function openBillingPortal() {
12668
- console.log(import_chalk12.default.cyan("\nOpening billing portal...\n"));
13281
+ console.log(import_chalk13.default.cyan("\nOpening billing portal...\n"));
12669
13282
  const response = await authenticatedRequest(
12670
13283
  cliApiUrl("/cli/subscribe/manage"),
12671
13284
  { method: "POST" }
@@ -12674,41 +13287,41 @@ async function openBillingPortal() {
12674
13287
  const data = response.data;
12675
13288
  const portalUrl = data.portal_url;
12676
13289
  if (portalUrl) {
12677
- console.log(import_chalk12.default.dim("Opening browser..."));
13290
+ console.log(import_chalk13.default.dim("Opening browser..."));
12678
13291
  await (0, import_open2.default)(assertSafeOpenUrl(portalUrl));
12679
- console.log(import_chalk12.default.green("\n\u2713 Billing portal opened"));
13292
+ console.log(import_chalk13.default.green("\n\u2713 Billing portal opened"));
12680
13293
  } else {
12681
- console.log(import_chalk12.default.yellow("No portal URL received."));
13294
+ console.log(import_chalk13.default.yellow("No portal URL received."));
12682
13295
  }
12683
13296
  } else {
12684
- console.log(import_chalk12.default.yellow("Could not open billing portal."));
12685
- console.log(import_chalk12.default.dim("Visit https://holdyourvoice.com/dashboard to manage billing."));
13297
+ console.log(import_chalk13.default.yellow("Could not open billing portal."));
13298
+ console.log(import_chalk13.default.dim("Visit https://holdyourvoice.com/dashboard to manage billing."));
12686
13299
  }
12687
13300
  }
12688
13301
  async function downgradePlan() {
12689
- console.log(import_chalk12.default.yellow("\nDowngrade Plan\n"));
13302
+ console.log(import_chalk13.default.yellow("\nDowngrade Plan\n"));
12690
13303
  console.log("To downgrade your plan, please visit:");
12691
- console.log(import_chalk12.default.dim(" https://holdyourvoice.com/dashboard"));
13304
+ console.log(import_chalk13.default.dim(" https://holdyourvoice.com/dashboard"));
12692
13305
  console.log("\nOr contact support at shashank@holdyourvoice.com");
12693
13306
  }
12694
13307
 
12695
13308
  // src/commands/scan.ts
12696
- var import_chalk15 = __toESM(require_source());
13309
+ var import_chalk16 = __toESM(require_source());
12697
13310
  init_pipeline();
12698
13311
  init_local_profile();
12699
13312
  init_access();
12700
13313
 
12701
13314
  // src/lib/output.ts
12702
- var import_chalk13 = __toESM(require_source());
13315
+ var import_chalk14 = __toESM(require_source());
12703
13316
  var c = {
12704
- dim: (s) => import_chalk13.default.dim(s),
12705
- bold: (s) => import_chalk13.default.bold(s),
12706
- accent: (s) => import_chalk13.default.hex("#C4441A")(s),
12707
- green: (s) => import_chalk13.default.green(s),
12708
- red: (s) => import_chalk13.default.red(s),
12709
- yellow: (s) => import_chalk13.default.yellow(s),
12710
- cyan: (s) => import_chalk13.default.cyan(s),
12711
- white: (s) => import_chalk13.default.white(s)
13317
+ dim: (s) => import_chalk14.default.dim(s),
13318
+ bold: (s) => import_chalk14.default.bold(s),
13319
+ accent: (s) => import_chalk14.default.hex("#C4441A")(s),
13320
+ green: (s) => import_chalk14.default.green(s),
13321
+ red: (s) => import_chalk14.default.red(s),
13322
+ yellow: (s) => import_chalk14.default.yellow(s),
13323
+ cyan: (s) => import_chalk14.default.cyan(s),
13324
+ white: (s) => import_chalk14.default.white(s)
12712
13325
  };
12713
13326
  function logo() {
12714
13327
  return `${c.bold("hold your ")}${c.accent("voice")}`;
@@ -12856,12 +13469,12 @@ async function runHybridAnalysis(text, profile, opts = {}) {
12856
13469
  }
12857
13470
 
12858
13471
  // src/commands/history.ts
12859
- var import_chalk14 = __toESM(require_source());
12860
- var fs12 = __toESM(require("fs"));
12861
- var path13 = __toESM(require("path"));
13472
+ var import_chalk15 = __toESM(require_source());
13473
+ var fs13 = __toESM(require("fs"));
13474
+ var path14 = __toESM(require("path"));
12862
13475
  init_config();
12863
- var HISTORY_DIR = path13.join(HYV_DIR, "history");
12864
- var HISTORY_FILE = path13.join(HISTORY_DIR, "scans.jsonl");
13476
+ var HISTORY_DIR = path14.join(HYV_DIR, "history");
13477
+ var HISTORY_FILE = path14.join(HISTORY_DIR, "scans.jsonl");
12865
13478
  function logScan(entry) {
12866
13479
  try {
12867
13480
  ensureHyvDir();
@@ -12871,9 +13484,9 @@ function logScan(entry) {
12871
13484
  }
12872
13485
  function readHistory() {
12873
13486
  try {
12874
- if (!fs12.existsSync(HISTORY_FILE))
13487
+ if (!fs13.existsSync(HISTORY_FILE))
12875
13488
  return [];
12876
- const lines = fs12.readFileSync(HISTORY_FILE, "utf-8").trim().split("\n").filter(Boolean);
13489
+ const lines = fs13.readFileSync(HISTORY_FILE, "utf-8").trim().split("\n").filter(Boolean);
12877
13490
  return lines.map((l) => JSON.parse(l));
12878
13491
  } catch {
12879
13492
  return [];
@@ -12905,10 +13518,10 @@ function registerHistoryCommand(program3) {
12905
13518
  program3.command("history").description("Show past scan scores and track improvement").option("--limit <n>", "Number of entries to show", "20").option("--file <path>", "Filter by file path").option("--since <date>", "Show entries since (e.g., 7d, 1m, 2024-01-01)").option("--chart", "Show ASCII sparkline chart").option("--clear", "Clear history").option("--format <type>", "Output format (text, json)", "text").action(async (options) => {
12906
13519
  try {
12907
13520
  if (options.clear) {
12908
- if (fs12.existsSync(HISTORY_FILE)) {
12909
- fs12.unlinkSync(HISTORY_FILE);
13521
+ if (fs13.existsSync(HISTORY_FILE)) {
13522
+ fs13.unlinkSync(HISTORY_FILE);
12910
13523
  }
12911
- console.log(import_chalk14.default.green("\n\u2713 History cleared"));
13524
+ console.log(import_chalk15.default.green("\n\u2713 History cleared"));
12912
13525
  return;
12913
13526
  }
12914
13527
  let entries = readHistory();
@@ -12922,14 +13535,14 @@ function registerHistoryCommand(program3) {
12922
13535
  const limit = parseInt(options.limit, 10);
12923
13536
  entries = entries.slice(-limit);
12924
13537
  if (entries.length === 0) {
12925
- console.log(import_chalk14.default.dim("\nNo scan history yet. Run hyv scan or hyv score to start tracking."));
13538
+ console.log(import_chalk15.default.dim("\nNo scan history yet. Run hyv scan or hyv score to start tracking."));
12926
13539
  return;
12927
13540
  }
12928
13541
  if (options.format === "json") {
12929
13542
  console.log(JSON.stringify(entries, null, 2));
12930
13543
  return;
12931
13544
  }
12932
- console.log(import_chalk14.default.bold(`
13545
+ console.log(import_chalk15.default.bold(`
12933
13546
  score history (last ${entries.length} scans)
12934
13547
  `));
12935
13548
  if (options.chart) {
@@ -12941,7 +13554,7 @@ function registerHistoryCommand(program3) {
12941
13554
  if (min !== max) {
12942
13555
  console.log(` ${min} \u2524`);
12943
13556
  }
12944
- console.log(import_chalk14.default.dim(` \u2514${"\u2500".repeat(sl.length + 1)}`));
13557
+ console.log(import_chalk15.default.dim(` \u2514${"\u2500".repeat(sl.length + 1)}`));
12945
13558
  console.log("");
12946
13559
  }
12947
13560
  const scores = entries.map((e) => e.score);
@@ -12954,22 +13567,22 @@ function registerHistoryCommand(program3) {
12954
13567
  const firstAvg = firstHalf.reduce((s, e) => s + e.score, 0) / (firstHalf.length || 1);
12955
13568
  const secondAvg = secondHalf.reduce((s, e) => s + e.score, 0) / (secondHalf.length || 1);
12956
13569
  const trend = Math.round(secondAvg - firstAvg);
12957
- const trendStr = trend > 0 ? import_chalk14.default.green(`\u2191 improving (+${trend})`) : trend < 0 ? import_chalk14.default.red(`\u2193 declining (${trend})`) : import_chalk14.default.dim("\u2192 stable");
12958
- console.log(import_chalk14.default.dim(` avg: ${avg} best: ${best.score} worst: ${worst.score}`));
12959
- console.log(import_chalk14.default.dim(` trend: ${trendStr}`));
12960
- console.log(import_chalk14.default.bold("\n recent:\n"));
13570
+ const trendStr = trend > 0 ? import_chalk15.default.green(`\u2191 improving (+${trend})`) : trend < 0 ? import_chalk15.default.red(`\u2193 declining (${trend})`) : import_chalk15.default.dim("\u2192 stable");
13571
+ console.log(import_chalk15.default.dim(` avg: ${avg} best: ${best.score} worst: ${worst.score}`));
13572
+ console.log(import_chalk15.default.dim(` trend: ${trendStr}`));
13573
+ console.log(import_chalk15.default.bold("\n recent:\n"));
12961
13574
  const recent = entries.slice(-10).reverse();
12962
13575
  for (const entry of recent) {
12963
13576
  const time = new Date(entry.timestamp).toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit" });
12964
- const icon = entry.issues === 0 ? import_chalk14.default.green("\u2713") : import_chalk14.default.red("\u25CF");
12965
- const score = entry.score < 60 ? import_chalk14.default.red(`${entry.score}/100`) : entry.score < 80 ? import_chalk14.default.yellow(`${entry.score}/100`) : import_chalk14.default.green(`${entry.score}/100`);
12966
- const issues = entry.issues > 0 ? import_chalk14.default.red(`${entry.issues} issues`) : import_chalk14.default.dim("clean");
12967
- const file = path13.basename(entry.file).slice(0, 25).padEnd(25);
12968
- console.log(` ${import_chalk14.default.dim(time)} ${icon} ${file} ${score} ${issues}`);
13577
+ const icon = entry.issues === 0 ? import_chalk15.default.green("\u2713") : import_chalk15.default.red("\u25CF");
13578
+ const score = entry.score < 60 ? import_chalk15.default.red(`${entry.score}/100`) : entry.score < 80 ? import_chalk15.default.yellow(`${entry.score}/100`) : import_chalk15.default.green(`${entry.score}/100`);
13579
+ const issues = entry.issues > 0 ? import_chalk15.default.red(`${entry.issues} issues`) : import_chalk15.default.dim("clean");
13580
+ const file = path14.basename(entry.file).slice(0, 25).padEnd(25);
13581
+ console.log(` ${import_chalk15.default.dim(time)} ${icon} ${file} ${score} ${issues}`);
12969
13582
  }
12970
13583
  console.log("");
12971
13584
  } catch (error) {
12972
- console.error(import_chalk14.default.red(`Error: ${error.message}`));
13585
+ console.error(import_chalk15.default.red(`Error: ${error.message}`));
12973
13586
  process.exit(1);
12974
13587
  }
12975
13588
  });
@@ -12984,7 +13597,7 @@ function registerScanCommand(program3) {
12984
13597
  const profile = await loadProfileForCommand(options.profile);
12985
13598
  const { text, path: filePath } = readText(file);
12986
13599
  if (!text.trim()) {
12987
- console.warn(import_chalk15.default.yellow("File is empty or contains only whitespace."));
13600
+ console.warn(import_chalk16.default.yellow("File is empty or contains only whitespace."));
12988
13601
  process.exit(0);
12989
13602
  }
12990
13603
  const ignoreRules = options.ignore ? new Set(options.ignore.split(",").map((r) => r.trim())) : /* @__PURE__ */ new Set();
@@ -13027,26 +13640,26 @@ function registerScanCommand(program3) {
13027
13640
  }, null, 2));
13028
13641
  } else {
13029
13642
  if (signals.length === 0) {
13030
- console.log(import_chalk15.default.green(`
13643
+ console.log(import_chalk16.default.green(`
13031
13644
  \u2713 No issues found in ${filePath}`));
13032
- console.log(import_chalk15.default.dim(` score: ${score}/100`));
13645
+ console.log(import_chalk16.default.dim(` score: ${score}/100`));
13033
13646
  } else {
13034
- console.log(import_chalk15.default.bold(`
13647
+ console.log(import_chalk16.default.bold(`
13035
13648
  hyv scan ${filePath}`));
13036
13649
  if (profile) {
13037
- console.log(import_chalk15.default.dim(` profile: ${profile.slug || profile.name}${hasRichProfile(profile) ? " (full)" : ""}`));
13650
+ console.log(import_chalk16.default.dim(` profile: ${profile.slug || profile.name}${hasRichProfile(profile) ? " (full)" : ""}`));
13038
13651
  }
13039
13652
  if (analysis.message) {
13040
- console.log(import_chalk15.default.dim(` engine: ${analysis.message}`));
13653
+ console.log(import_chalk16.default.dim(` engine: ${analysis.message}`));
13041
13654
  }
13042
13655
  console.log("");
13043
13656
  printGroupedSignals(signals, profile);
13044
- console.log(import_chalk15.default.yellow(`
13657
+ console.log(import_chalk16.default.yellow(`
13045
13658
  ${signals.length} issues (${signals.filter((s) => s.severity === "red").length} red, ${signals.filter((s) => s.severity === "yellow").length} yellow)`));
13046
- console.log(import_chalk15.default.dim(` score: ${score}/100`));
13047
- console.log(import_chalk15.default.dim(`
13659
+ console.log(import_chalk16.default.dim(` score: ${score}/100`));
13660
+ console.log(import_chalk16.default.dim(`
13048
13661
  fix: hyv fix ${file}`));
13049
- console.log(import_chalk15.default.dim(` diff: hyv diff ${file}
13662
+ console.log(import_chalk16.default.dim(` diff: hyv diff ${file}
13050
13663
  `));
13051
13664
  }
13052
13665
  await maybeShowLimitedModeHint(!!profile);
@@ -13064,17 +13677,17 @@ hyv scan ${filePath}`));
13064
13677
  process.exit(2);
13065
13678
  }
13066
13679
  } catch (error) {
13067
- console.error(import_chalk15.default.red(`Error: ${error.message}`));
13680
+ console.error(import_chalk16.default.red(`Error: ${error.message}`));
13068
13681
  process.exit(1);
13069
13682
  }
13070
13683
  });
13071
13684
  }
13072
13685
 
13073
13686
  // src/commands/doctor.ts
13074
- var import_chalk16 = __toESM(require_source());
13075
- var fs14 = __toESM(require("fs"));
13076
- var path15 = __toESM(require("path"));
13077
- var os5 = __toESM(require("os"));
13687
+ var import_chalk17 = __toESM(require_source());
13688
+ var fs15 = __toESM(require("fs"));
13689
+ var path16 = __toESM(require("path"));
13690
+ var os6 = __toESM(require("os"));
13078
13691
  init_config();
13079
13692
  init_auth();
13080
13693
  init_access();
@@ -13085,15 +13698,15 @@ init_version();
13085
13698
  var import_child_process2 = require("child_process");
13086
13699
 
13087
13700
  // src/lib/cli-entry.ts
13088
- var fs13 = __toESM(require("fs"));
13089
- var path14 = __toESM(require("path"));
13701
+ var fs14 = __toESM(require("fs"));
13702
+ var path15 = __toESM(require("path"));
13090
13703
  function resolveCliEntry() {
13091
13704
  const candidates = [
13092
- path14.resolve(process.argv[1] || ""),
13093
- path14.resolve(__dirname, "index.js"),
13094
- path14.resolve(__dirname, "..", "dist", "index.js")
13705
+ path15.resolve(process.argv[1] || ""),
13706
+ path15.resolve(__dirname, "index.js"),
13707
+ path15.resolve(__dirname, "..", "dist", "index.js")
13095
13708
  ];
13096
- return candidates.find((p) => p && fs13.existsSync(p)) || null;
13709
+ return candidates.find((p) => p && fs14.existsSync(p)) || null;
13097
13710
  }
13098
13711
  function mcpServerCommand() {
13099
13712
  const entry = resolveCliEntry();
@@ -13112,7 +13725,7 @@ async function testMcpStdioSubprocess() {
13112
13725
  const entry = resolveCliEntry();
13113
13726
  if (!entry)
13114
13727
  return { ok: false };
13115
- return new Promise((resolve14) => {
13728
+ return new Promise((resolve15) => {
13116
13729
  const child = (0, import_child_process2.spawn)(process.execPath, [entry, "mcp"], {
13117
13730
  stdio: ["pipe", "pipe", "pipe"],
13118
13731
  env: { ...process.env, HYV_POSTINSTALL_QUIET: "1" }
@@ -13128,7 +13741,7 @@ async function testMcpStdioSubprocess() {
13128
13741
  child.kill();
13129
13742
  } catch {
13130
13743
  }
13131
- resolve14({ ok, toolCount });
13744
+ resolve15({ ok, toolCount });
13132
13745
  };
13133
13746
  const timeout = setTimeout(() => finish(false), 1e4);
13134
13747
  const handleLine = (line) => {
@@ -13179,20 +13792,20 @@ async function testMcpStdioSubprocess() {
13179
13792
  }
13180
13793
 
13181
13794
  // src/commands/doctor.ts
13182
- var HOME = os5.homedir();
13795
+ var HOME = os6.homedir();
13183
13796
  var IS_WIN = process.platform === "win32";
13184
13797
  function claudeDesktopDir() {
13185
13798
  if (IS_WIN)
13186
- return path15.join(HOME, "AppData", "Roaming", "Claude");
13799
+ return path16.join(HOME, "AppData", "Roaming", "Claude");
13187
13800
  if (process.platform === "linux")
13188
- return path15.join(HOME, ".config", "Claude");
13189
- return path15.join(HOME, "Library", "Application Support", "Claude");
13801
+ return path16.join(HOME, ".config", "Claude");
13802
+ return path16.join(HOME, "Library", "Application Support", "Claude");
13190
13803
  }
13191
13804
  function isOwnerOnlyFile(filePath) {
13192
13805
  try {
13193
- if (!fs14.existsSync(filePath))
13806
+ if (!fs15.existsSync(filePath))
13194
13807
  return true;
13195
- const mode = fs14.statSync(filePath).mode & 511;
13808
+ const mode = fs15.statSync(filePath).mode & 511;
13196
13809
  return (mode & 63) === 0;
13197
13810
  } catch {
13198
13811
  return false;
@@ -13200,9 +13813,9 @@ function isOwnerOnlyFile(filePath) {
13200
13813
  }
13201
13814
  function readMcpHyv(configFile) {
13202
13815
  try {
13203
- if (!fs14.existsSync(configFile))
13816
+ if (!fs15.existsSync(configFile))
13204
13817
  return false;
13205
- const cfg = JSON.parse(fs14.readFileSync(configFile, "utf-8"));
13818
+ const cfg = JSON.parse(fs15.readFileSync(configFile, "utf-8"));
13206
13819
  return Boolean(cfg.mcpServers?.hyv);
13207
13820
  } catch {
13208
13821
  return false;
@@ -13210,188 +13823,188 @@ function readMcpHyv(configFile) {
13210
13823
  }
13211
13824
  function registerDoctorCommand(program3) {
13212
13825
  program3.command("doctor").description("Diagnose CLI health: engine, cache, auth, agents").option("--fix-agents", "Re-run agent config copy (idempotent)").action(async (opts) => {
13213
- console.log(import_chalk16.default.bold("\nhold your voice \u2014 doctor\n"));
13826
+ console.log(import_chalk17.default.bold("\nhold your voice \u2014 doctor\n"));
13214
13827
  let issues = 0;
13215
13828
  let fixed = 0;
13216
- console.log(import_chalk16.default.dim(`engine: ${getEngineLabel()} (node ${process.version})`));
13829
+ console.log(import_chalk17.default.dim(`engine: ${getEngineLabel()} (node ${process.version})`));
13217
13830
  if (isLocalOnlyMode()) {
13218
- console.log(import_chalk16.default.yellow(" local-only mode: HYV_LOCAL_ONLY is set"));
13831
+ console.log(import_chalk17.default.yellow(" local-only mode: HYV_LOCAL_ONLY is set"));
13219
13832
  }
13220
13833
  const access = await getAccessState();
13221
13834
  const profile = await loadProfileForCommand();
13222
- console.log(import_chalk16.default.dim(`mode: ${formatModeLabel(access, hasRichProfile(profile))}`));
13223
- console.log(import_chalk16.default.dim("tip: run hyv welcome for free capabilities\n"));
13224
- console.log(import_chalk16.default.dim("checking cli installation..."));
13835
+ console.log(import_chalk17.default.dim(`mode: ${formatModeLabel(access, hasRichProfile(profile))}`));
13836
+ console.log(import_chalk17.default.dim("tip: run hyv welcome for free capabilities\n"));
13837
+ console.log(import_chalk17.default.dim("checking cli installation..."));
13225
13838
  const cliPath = process.argv[1];
13226
- if (cliPath && fs14.existsSync(cliPath)) {
13227
- console.log(import_chalk16.default.green(" \u2713 cli installed"));
13839
+ if (cliPath && fs15.existsSync(cliPath)) {
13840
+ console.log(import_chalk17.default.green(" \u2713 cli installed"));
13228
13841
  } else {
13229
- console.log(import_chalk16.default.red(" \u2717 cli not found"));
13842
+ console.log(import_chalk17.default.red(" \u2717 cli not found"));
13230
13843
  issues++;
13231
13844
  }
13232
- console.log(import_chalk16.default.dim("checking .hyv directory..."));
13233
- if (fs14.existsSync(HYV_DIR)) {
13234
- console.log(import_chalk16.default.green(" \u2713 .hyv directory exists"));
13845
+ console.log(import_chalk17.default.dim("checking .hyv directory..."));
13846
+ if (fs15.existsSync(HYV_DIR)) {
13847
+ console.log(import_chalk17.default.green(" \u2713 .hyv directory exists"));
13235
13848
  } else {
13236
- console.log(import_chalk16.default.yellow(" ! .hyv directory missing \u2014 creating..."));
13237
- fs14.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
13849
+ console.log(import_chalk17.default.yellow(" ! .hyv directory missing \u2014 creating..."));
13850
+ fs15.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
13238
13851
  fixed++;
13239
13852
  }
13240
- console.log(import_chalk16.default.dim("checking cache..."));
13853
+ console.log(import_chalk17.default.dim("checking cache..."));
13241
13854
  const diskProfiles = listDiskCachedProfiles();
13242
- const syncMeta = path15.join(CACHE_DIR, "sync-meta.json");
13243
- if (diskProfiles.length > 0 || fs14.existsSync(syncMeta)) {
13244
- console.log(import_chalk16.default.green(` \u2713 profile cache (${diskProfiles.length} full profile(s))`));
13245
- if (fs14.existsSync(syncMeta)) {
13855
+ const syncMeta = path16.join(CACHE_DIR, "sync-meta.json");
13856
+ if (diskProfiles.length > 0 || fs15.existsSync(syncMeta)) {
13857
+ console.log(import_chalk17.default.green(` \u2713 profile cache (${diskProfiles.length} full profile(s))`));
13858
+ if (fs15.existsSync(syncMeta)) {
13246
13859
  try {
13247
- const meta = JSON.parse(fs14.readFileSync(syncMeta, "utf-8"));
13248
- console.log(import_chalk16.default.dim(` last sync: ${meta.synced_at || "unknown"}`));
13860
+ const meta = JSON.parse(fs15.readFileSync(syncMeta, "utf-8"));
13861
+ console.log(import_chalk17.default.dim(` last sync: ${meta.synced_at || "unknown"}`));
13249
13862
  } catch {
13250
13863
  }
13251
13864
  }
13252
13865
  } else {
13253
- console.log(import_chalk16.default.dim(" - no full profile cache (free local engine still works)"));
13866
+ console.log(import_chalk17.default.dim(" - no full profile cache (free local engine still works)"));
13254
13867
  }
13255
- console.log(import_chalk16.default.dim("checking file permissions..."));
13256
- if (fs14.existsSync(HYV_DIR)) {
13257
- const hyvMode = fs14.statSync(HYV_DIR).mode & 511;
13868
+ console.log(import_chalk17.default.dim("checking file permissions..."));
13869
+ if (fs15.existsSync(HYV_DIR)) {
13870
+ const hyvMode = fs15.statSync(HYV_DIR).mode & 511;
13258
13871
  if ((hyvMode & 63) === 0) {
13259
- console.log(import_chalk16.default.green(" \u2713 .hyv directory permissions"));
13872
+ console.log(import_chalk17.default.green(" \u2713 .hyv directory permissions"));
13260
13873
  } else {
13261
- console.log(import_chalk16.default.yellow(" ! .hyv directory is world/group accessible"));
13262
- console.log(import_chalk16.default.dim(" run: chmod 700 ~/.hyv"));
13874
+ console.log(import_chalk17.default.yellow(" ! .hyv directory is world/group accessible"));
13875
+ console.log(import_chalk17.default.dim(" run: chmod 700 ~/.hyv"));
13263
13876
  issues++;
13264
13877
  }
13265
13878
  }
13266
- if (fs14.existsSync(AUTH_FILE)) {
13879
+ if (fs15.existsSync(AUTH_FILE)) {
13267
13880
  if (isOwnerOnlyFile(AUTH_FILE)) {
13268
- console.log(import_chalk16.default.green(" \u2713 auth.json permissions"));
13881
+ console.log(import_chalk17.default.green(" \u2713 auth.json permissions"));
13269
13882
  } else {
13270
- console.log(import_chalk16.default.yellow(" ! auth.json is world/group readable"));
13271
- console.log(import_chalk16.default.dim(" run: chmod 600 ~/.hyv/auth.json"));
13883
+ console.log(import_chalk17.default.yellow(" ! auth.json is world/group readable"));
13884
+ console.log(import_chalk17.default.dim(" run: chmod 600 ~/.hyv/auth.json"));
13272
13885
  issues++;
13273
13886
  }
13274
13887
  }
13275
- console.log(import_chalk16.default.dim("checking authentication..."));
13888
+ console.log(import_chalk17.default.dim("checking authentication..."));
13276
13889
  if (isInitialized()) {
13277
13890
  const auth = readAuth();
13278
13891
  if (auth) {
13279
- console.log(import_chalk16.default.green(" \u2713 authenticated"));
13280
- console.log(import_chalk16.default.dim(` email: ${auth.user?.email || "unknown"}`));
13892
+ console.log(import_chalk17.default.green(" \u2713 authenticated"));
13893
+ console.log(import_chalk17.default.dim(` email: ${auth.user?.email || "unknown"}`));
13281
13894
  const session = await checkSession();
13282
13895
  if (session.valid) {
13283
- console.log(import_chalk16.default.green(" \u2713 session valid"));
13284
- console.log(import_chalk16.default.dim(` plan: ${session.plan || "none"}`));
13896
+ console.log(import_chalk17.default.green(" \u2713 session valid"));
13897
+ console.log(import_chalk17.default.dim(` plan: ${session.plan || "none"}`));
13285
13898
  } else {
13286
- console.log(import_chalk16.default.red(" \u2717 session expired"));
13287
- console.log(import_chalk16.default.dim(" run: hyv init"));
13899
+ console.log(import_chalk17.default.red(" \u2717 session expired"));
13900
+ console.log(import_chalk17.default.dim(" run: hyv init"));
13288
13901
  issues++;
13289
13902
  }
13290
13903
  } else {
13291
- console.log(import_chalk16.default.red(" \u2717 auth data missing"));
13904
+ console.log(import_chalk17.default.red(" \u2717 auth data missing"));
13292
13905
  issues++;
13293
13906
  }
13294
13907
  } else {
13295
- console.log(import_chalk16.default.yellow(" ! not authenticated (free local scan works)"));
13296
- console.log(import_chalk16.default.dim(" run: hyv init for profiles + learning"));
13908
+ console.log(import_chalk17.default.yellow(" ! not authenticated (free local scan works)"));
13909
+ console.log(import_chalk17.default.dim(" run: hyv init for profiles + learning"));
13297
13910
  }
13298
- console.log(import_chalk16.default.dim("checking voice profile..."));
13299
- const voiceMd = path15.join(HYV_DIR, "voice.md");
13300
- const hasVoiceMd = fs14.existsSync(voiceMd) && fs14.readFileSync(voiceMd, "utf-8").trim().length > 50;
13301
- const profileFiles = fs14.existsSync(PROFILES_DIR) ? fs14.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md") && f !== "voice.md") : [];
13911
+ console.log(import_chalk17.default.dim("checking voice profile..."));
13912
+ const voiceMd = path16.join(HYV_DIR, "voice.md");
13913
+ const hasVoiceMd = fs15.existsSync(voiceMd) && fs15.readFileSync(voiceMd, "utf-8").trim().length > 50;
13914
+ const profileFiles = fs15.existsSync(PROFILES_DIR) ? fs15.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md") && f !== "voice.md") : [];
13302
13915
  if (hasVoiceMd || profileFiles.length > 0 || diskProfiles.length > 0) {
13303
13916
  if (hasVoiceMd)
13304
- console.log(import_chalk16.default.green(" \u2713 voice.md exists"));
13917
+ console.log(import_chalk17.default.green(" \u2713 voice.md exists"));
13305
13918
  if (profileFiles.length > 0)
13306
- console.log(import_chalk16.default.green(` \u2713 ${profileFiles.length} markdown profile(s)`));
13919
+ console.log(import_chalk17.default.green(` \u2713 ${profileFiles.length} markdown profile(s)`));
13307
13920
  if (diskProfiles.length > 0)
13308
- console.log(import_chalk16.default.green(` \u2713 ${diskProfiles.length} full cached profile(s)`));
13921
+ console.log(import_chalk17.default.green(` \u2713 ${diskProfiles.length} full cached profile(s)`));
13309
13922
  } else {
13310
- console.log(import_chalk16.default.yellow(" ! no voice profile (optional for free scan)"));
13311
- console.log(import_chalk16.default.dim(" run: hyv new <name> or hyv init"));
13312
- }
13313
- console.log(import_chalk16.default.dim("checking agent configurations..."));
13314
- const cursorLegacyRule = path15.join(HOME, ".cursor", "rules", "hyv.md");
13315
- const cursorRule = path15.join(HOME, ".cursor", "rules", "hyv.mdc");
13316
- if (fs14.existsSync(cursorLegacyRule) && fs14.existsSync(cursorRule)) {
13317
- console.log(import_chalk16.default.yellow(" ! stale cursor rule ~/.cursor/rules/hyv.md (use hyv.mdc)"));
13318
- console.log(import_chalk16.default.dim(" run: rm ~/.cursor/rules/hyv.md (or hyv doctor --fix-agents)"));
13923
+ console.log(import_chalk17.default.yellow(" ! no voice profile (optional for free scan)"));
13924
+ console.log(import_chalk17.default.dim(" run: hyv new <name> or hyv init"));
13925
+ }
13926
+ console.log(import_chalk17.default.dim("checking agent configurations..."));
13927
+ const cursorLegacyRule = path16.join(HOME, ".cursor", "rules", "hyv.md");
13928
+ const cursorRule = path16.join(HOME, ".cursor", "rules", "hyv.mdc");
13929
+ if (fs15.existsSync(cursorLegacyRule) && fs15.existsSync(cursorRule)) {
13930
+ console.log(import_chalk17.default.yellow(" ! stale cursor rule ~/.cursor/rules/hyv.md (use hyv.mdc)"));
13931
+ console.log(import_chalk17.default.dim(" run: rm ~/.cursor/rules/hyv.md (or hyv doctor --fix-agents)"));
13319
13932
  issues++;
13320
13933
  }
13321
13934
  const agentChecks = [
13322
- { name: "claude desktop mcp", ok: readMcpHyv(path15.join(claudeDesktopDir(), "claude_desktop_config.json")) },
13323
- { name: "cursor mcp", ok: readMcpHyv(path15.join(HOME, ".cursor", "mcp.json")) },
13324
- { name: "cursor rule", ok: fs14.existsSync(cursorRule) },
13325
- { name: "claude code command", ok: fs14.existsSync(path15.join(HOME, ".claude", "commands", "hyv.md")) },
13326
- { name: "claude code skill", ok: fs14.existsSync(path15.join(HOME, ".claude", "skills", "hold-your-voice", "SKILL.md")) },
13327
- { name: "codex agents", ok: fs14.existsSync(path15.join(HOME, ".codex", "AGENTS.md")) && fs14.readFileSync(path15.join(HOME, ".codex", "AGENTS.md"), "utf-8").includes("hyv") },
13328
- { name: "command code skill", ok: fs14.existsSync(path15.join(HOME, ".commandcode", "skills", "hyv", "SKILL.md")) }
13935
+ { name: "claude desktop mcp", ok: readMcpHyv(path16.join(claudeDesktopDir(), "claude_desktop_config.json")) },
13936
+ { name: "cursor mcp", ok: readMcpHyv(path16.join(HOME, ".cursor", "mcp.json")) },
13937
+ { name: "cursor rule", ok: fs15.existsSync(cursorRule) },
13938
+ { name: "claude code command", ok: fs15.existsSync(path16.join(HOME, ".claude", "commands", "hyv.md")) },
13939
+ { name: "claude code skill", ok: fs15.existsSync(path16.join(HOME, ".claude", "skills", "hold-your-voice", "SKILL.md")) },
13940
+ { name: "codex agents", ok: fs15.existsSync(path16.join(HOME, ".codex", "AGENTS.md")) && fs15.readFileSync(path16.join(HOME, ".codex", "AGENTS.md"), "utf-8").includes("hyv") },
13941
+ { name: "command code skill", ok: fs15.existsSync(path16.join(HOME, ".commandcode", "skills", "hyv", "SKILL.md")) }
13329
13942
  ];
13330
13943
  for (const agent of agentChecks) {
13331
13944
  if (agent.ok) {
13332
- console.log(import_chalk16.default.green(` \u2713 ${agent.name}`));
13945
+ console.log(import_chalk17.default.green(` \u2713 ${agent.name}`));
13333
13946
  } else {
13334
- console.log(import_chalk16.default.dim(` - ${agent.name} not configured`));
13947
+ console.log(import_chalk17.default.dim(` - ${agent.name} not configured`));
13335
13948
  }
13336
13949
  }
13337
13950
  if (opts.fixAgents) {
13338
13951
  try {
13339
- const pkgDir = path15.resolve(__dirname, "..");
13340
- const { setupAgents } = require(path15.join(pkgDir, "scripts", "postinstall-lib.js"));
13952
+ const pkgDir = path16.resolve(__dirname, "..");
13953
+ const { setupAgents } = require(path16.join(pkgDir, "scripts", "postinstall-lib.js"));
13341
13954
  const result = setupAgents({ pkgDir, quiet: true });
13342
- console.log(import_chalk16.default.green(` \u2713 re-ran agent setup (${result.configured.join(", ") || "no changes"})`));
13955
+ console.log(import_chalk17.default.green(` \u2713 re-ran agent setup (${result.configured.join(", ") || "no changes"})`));
13343
13956
  if (result.warnings.length) {
13344
- console.log(import_chalk16.default.yellow(` notes: ${result.warnings.join("; ")}`));
13957
+ console.log(import_chalk17.default.yellow(` notes: ${result.warnings.join("; ")}`));
13345
13958
  }
13346
13959
  fixed++;
13347
13960
  } catch (err) {
13348
- console.log(import_chalk16.default.yellow(` ! could not re-run agent setup: ${err.message}`));
13961
+ console.log(import_chalk17.default.yellow(` ! could not re-run agent setup: ${err.message}`));
13349
13962
  }
13350
13963
  }
13351
- console.log(import_chalk16.default.dim("checking mcp server..."));
13352
- const claudeMcp = readMcpHyv(path15.join(claudeDesktopDir(), "claude_desktop_config.json"));
13353
- const cursorMcp = readMcpHyv(path15.join(HOME, ".cursor", "mcp.json"));
13964
+ console.log(import_chalk17.default.dim("checking mcp server..."));
13965
+ const claudeMcp = readMcpHyv(path16.join(claudeDesktopDir(), "claude_desktop_config.json"));
13966
+ const cursorMcp = readMcpHyv(path16.join(HOME, ".cursor", "mcp.json"));
13354
13967
  if (claudeMcp || cursorMcp) {
13355
13968
  if (claudeMcp)
13356
- console.log(import_chalk16.default.green(" \u2713 mcp configured for claude desktop"));
13969
+ console.log(import_chalk17.default.green(" \u2713 mcp configured for claude desktop"));
13357
13970
  if (cursorMcp)
13358
- console.log(import_chalk16.default.green(" \u2713 mcp configured for cursor"));
13971
+ console.log(import_chalk17.default.green(" \u2713 mcp configured for cursor"));
13359
13972
  } else {
13360
- console.log(import_chalk16.default.yellow(" ! mcp not configured \u2014 run: hyv doctor --fix-agents or hyv mcp --setup"));
13973
+ console.log(import_chalk17.default.yellow(" ! mcp not configured \u2014 run: hyv doctor --fix-agents or hyv mcp --setup"));
13361
13974
  issues++;
13362
13975
  }
13363
13976
  try {
13364
13977
  const stdio = await testMcpStdioSubprocess();
13365
13978
  if (stdio.ok && (stdio.toolCount || 0) >= 10) {
13366
- console.log(import_chalk16.default.green(` \u2713 mcp stdio subprocess healthy (${stdio.toolCount} tools)`));
13979
+ console.log(import_chalk17.default.green(` \u2713 mcp stdio subprocess healthy (${stdio.toolCount} tools)`));
13367
13980
  } else if (stdio.ok) {
13368
- console.log(import_chalk16.default.yellow(` ! mcp stdio ok but only ${stdio.toolCount || 0} tools`));
13981
+ console.log(import_chalk17.default.yellow(` ! mcp stdio ok but only ${stdio.toolCount || 0} tools`));
13369
13982
  issues++;
13370
13983
  } else {
13371
- console.log(import_chalk16.default.red(" \u2717 mcp stdio subprocess failed"));
13372
- console.log(import_chalk16.default.dim(" run: hyv mcp --test"));
13984
+ console.log(import_chalk17.default.red(" \u2717 mcp stdio subprocess failed"));
13985
+ console.log(import_chalk17.default.dim(" run: hyv mcp --test"));
13373
13986
  issues++;
13374
13987
  }
13375
13988
  } catch (err) {
13376
- console.log(import_chalk16.default.red(` \u2717 mcp stdio probe failed: ${err.message}`));
13989
+ console.log(import_chalk17.default.red(` \u2717 mcp stdio probe failed: ${err.message}`));
13377
13990
  issues++;
13378
13991
  }
13379
13992
  console.log("");
13380
13993
  if (issues === 0 && fixed === 0) {
13381
- console.log(import_chalk16.default.green("\u2713 everything looks good!"));
13994
+ console.log(import_chalk17.default.green("\u2713 everything looks good!"));
13382
13995
  } else if (fixed > 0) {
13383
- console.log(import_chalk16.default.green(`\u2713 fixed ${fixed} issue(s)`));
13996
+ console.log(import_chalk17.default.green(`\u2713 fixed ${fixed} issue(s)`));
13384
13997
  if (issues > 0)
13385
- console.log(import_chalk16.default.yellow(`! ${issues} issue(s) remaining`));
13998
+ console.log(import_chalk17.default.yellow(`! ${issues} issue(s) remaining`));
13386
13999
  } else {
13387
- console.log(import_chalk16.default.yellow(`! ${issues} issue(s) found`));
14000
+ console.log(import_chalk17.default.yellow(`! ${issues} issue(s) found`));
13388
14001
  }
13389
- console.log(import_chalk16.default.dim("\nfree scan: hyv scan draft.md | full tour: hyv welcome\n"));
14002
+ console.log(import_chalk17.default.dim("\nfree scan: hyv scan draft.md | full tour: hyv welcome\n"));
13390
14003
  });
13391
14004
  }
13392
14005
 
13393
14006
  // src/commands/rename.ts
13394
- var import_chalk17 = __toESM(require_source());
14007
+ var import_chalk18 = __toESM(require_source());
13395
14008
  init_config();
13396
14009
  init_auth();
13397
14010
  function registerRenameCommand(program3) {
@@ -13399,26 +14012,26 @@ function registerRenameCommand(program3) {
13399
14012
  try {
13400
14013
  const trimmedName = newName.trim();
13401
14014
  if (!trimmedName) {
13402
- console.log(import_chalk17.default.red("\nerror: what do you want to name your profile?\n"));
13403
- console.log(import_chalk17.default.dim(' example: hyv rename "my brand voice"\n'));
14015
+ console.log(import_chalk18.default.red("\nerror: what do you want to name your profile?\n"));
14016
+ console.log(import_chalk18.default.dim(' example: hyv rename "my brand voice"\n'));
13404
14017
  process.exit(1);
13405
14018
  return;
13406
14019
  }
13407
14020
  if (trimmedName.length > 100) {
13408
- console.log(import_chalk17.default.red("\nerror: name is too long \u2014 keep it under 100 characters\n"));
14021
+ console.log(import_chalk18.default.red("\nerror: name is too long \u2014 keep it under 100 characters\n"));
13409
14022
  process.exit(1);
13410
14023
  return;
13411
14024
  }
13412
14025
  if (!/^[a-zA-Z0-9\s\-_'&.]+$/.test(trimmedName)) {
13413
- console.log(import_chalk17.default.red("\nerror: name contains invalid characters\n"));
13414
- console.log(import_chalk17.default.dim(" allowed: letters, numbers, spaces, hyphens, underscores, apostrophes\n"));
14026
+ console.log(import_chalk18.default.red("\nerror: name contains invalid characters\n"));
14027
+ console.log(import_chalk18.default.dim(" allowed: letters, numbers, spaces, hyphens, underscores, apostrophes\n"));
13415
14028
  process.exit(1);
13416
14029
  return;
13417
14030
  }
13418
14031
  const token = getToken();
13419
14032
  if (!token) {
13420
- console.log(import_chalk17.default.red("\nerror: you're not signed in yet\n"));
13421
- console.log(import_chalk17.default.dim(" run: hyv init\n"));
14033
+ console.log(import_chalk18.default.red("\nerror: you're not signed in yet\n"));
14034
+ console.log(import_chalk18.default.dim(" run: hyv init\n"));
13422
14035
  process.exit(1);
13423
14036
  return;
13424
14037
  }
@@ -13434,16 +14047,16 @@ function registerRenameCommand(program3) {
13434
14047
  );
13435
14048
  if (response.status === 200) {
13436
14049
  const data = response.data;
13437
- console.log(import_chalk17.default.green(`
14050
+ console.log(import_chalk18.default.green(`
13438
14051
  \u2713 profile renamed to ${data.profile?.name || trimmedName}
13439
14052
  `));
13440
14053
  } else {
13441
- console.log(import_chalk17.default.red(`
14054
+ console.log(import_chalk18.default.red(`
13442
14055
  error: server returned ${response.status}
13443
14056
  `));
13444
14057
  }
13445
14058
  } catch (error) {
13446
- console.error(import_chalk17.default.red(`
14059
+ console.error(import_chalk18.default.red(`
13447
14060
  error: ${error.message}
13448
14061
  `));
13449
14062
  process.exit(1);
@@ -13452,46 +14065,46 @@ error: ${error.message}
13452
14065
  }
13453
14066
 
13454
14067
  // src/commands/fix.ts
13455
- var import_chalk19 = __toESM(require_source());
14068
+ var import_chalk20 = __toESM(require_source());
13456
14069
  init_pipeline();
13457
14070
  init_local_profile();
13458
14071
  init_access();
13459
14072
  init_config();
13460
14073
 
13461
14074
  // src/lib/destructive-write.ts
13462
- var fs15 = __toESM(require("fs"));
13463
- var path16 = __toESM(require("path"));
13464
- var readline = __toESM(require("readline"));
13465
- var import_chalk18 = __toESM(require_source());
14075
+ var fs16 = __toESM(require("fs"));
14076
+ var path17 = __toESM(require("path"));
14077
+ var readline2 = __toESM(require("readline"));
14078
+ var import_chalk19 = __toESM(require_source());
13466
14079
  async function confirmDestructiveWrite(options) {
13467
14080
  if (options.yes)
13468
14081
  return true;
13469
14082
  const label = options.target ? `${options.action} (${options.target})` : options.action;
13470
14083
  if (!process.stdin.isTTY) {
13471
- console.error(import_chalk18.default.red("\nRefusing destructive write without confirmation (non-interactive)."));
13472
- console.error(import_chalk18.default.dim(" Re-run with --yes to create .bak backups and proceed.\n"));
14084
+ console.error(import_chalk19.default.red("\nRefusing destructive write without confirmation (non-interactive)."));
14085
+ console.error(import_chalk19.default.dim(" Re-run with --yes to create .bak backups and proceed.\n"));
13473
14086
  return false;
13474
14087
  }
13475
14088
  const question = `
13476
14089
  ${label}
13477
14090
  A .bak backup will be created. Proceed? [y/N] `;
13478
- const answer = await new Promise((resolve14) => {
13479
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
14091
+ const answer = await new Promise((resolve15) => {
14092
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
13480
14093
  rl.question(question, (value) => {
13481
14094
  rl.close();
13482
- resolve14(value.trim().toLowerCase());
14095
+ resolve15(value.trim().toLowerCase());
13483
14096
  });
13484
14097
  });
13485
14098
  return answer === "y" || answer === "yes";
13486
14099
  }
13487
14100
  function writeInPlaceWithBackup(filePath, content) {
13488
14101
  const backupPath = filePath + ".bak";
13489
- fs15.copyFileSync(filePath, backupPath);
13490
- fs15.writeFileSync(filePath, content);
14102
+ fs16.copyFileSync(filePath, backupPath);
14103
+ fs16.writeFileSync(filePath, content);
13491
14104
  return backupPath;
13492
14105
  }
13493
14106
  function backupBasename(filePath) {
13494
- return path16.basename(filePath) + ".bak";
14107
+ return path17.basename(filePath) + ".bak";
13495
14108
  }
13496
14109
 
13497
14110
  // src/commands/fix.ts
@@ -13505,9 +14118,9 @@ function registerFixCommand(program3) {
13505
14118
  if (options.format === "json") {
13506
14119
  console.log(JSON.stringify({ file: filePath, autoFixes: 0, llmIssues: result.stats.needsLLM, changes: [] }));
13507
14120
  } else {
13508
- console.log(import_chalk19.default.green("\n\u2713 No auto-fixable issues found."));
14121
+ console.log(import_chalk20.default.green("\n\u2713 No auto-fixable issues found."));
13509
14122
  if (result.stats.needsLLM > 0) {
13510
- console.log(import_chalk19.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
14123
+ console.log(import_chalk20.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
13511
14124
  }
13512
14125
  }
13513
14126
  return;
@@ -13521,16 +14134,16 @@ function registerFixCommand(program3) {
13521
14134
  fixed: options.dryRun ? void 0 : result.fixed
13522
14135
  }, null, 2));
13523
14136
  } else {
13524
- console.log(import_chalk19.default.dim(`
14137
+ console.log(import_chalk20.default.dim(`
13525
14138
  hyv fix ${filePath}
13526
14139
  `));
13527
14140
  for (const change of result.changes) {
13528
- console.log(import_chalk19.default.dim(` Line ${change.line}: `) + import_chalk19.default.red(change.before) + import_chalk19.default.dim(" \u2192 ") + import_chalk19.default.green(change.after));
14141
+ console.log(import_chalk20.default.dim(` Line ${change.line}: `) + import_chalk20.default.red(change.before) + import_chalk20.default.dim(" \u2192 ") + import_chalk20.default.green(change.after));
13529
14142
  }
13530
- console.log(import_chalk19.default.green(`
14143
+ console.log(import_chalk20.default.green(`
13531
14144
  \u2713 ${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} applied`));
13532
14145
  if (result.stats.needsLLM > 0) {
13533
- console.log(import_chalk19.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
14146
+ console.log(import_chalk20.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
13534
14147
  }
13535
14148
  }
13536
14149
  if (!options.dryRun) {
@@ -13544,7 +14157,7 @@ hyv fix ${filePath}
13544
14157
  process.exit(1);
13545
14158
  }
13546
14159
  const backupPath = writeInPlaceWithBackup(filePath, result.fixed);
13547
- console.log(import_chalk19.default.dim(` Written to ${filePath} (backup: ${backupBasename(filePath)})`));
14160
+ console.log(import_chalk20.default.dim(` Written to ${filePath} (backup: ${backupBasename(filePath)})`));
13548
14161
  saveLastEditSession({
13549
14162
  original_path: filePath,
13550
14163
  edited_path: filePath,
@@ -13552,7 +14165,7 @@ hyv fix ${filePath}
13552
14165
  edited_text: result.fixed,
13553
14166
  profile: options.profile
13554
14167
  });
13555
- console.log(import_chalk19.default.dim(" Tip: hyv reinforce --last to teach your profile from this edit"));
14168
+ console.log(import_chalk20.default.dim(" Tip: hyv reinforce --last to teach your profile from this edit"));
13556
14169
  } else if (filePath === "stdin" || !options.inPlace) {
13557
14170
  process.stdout.write("\n" + result.fixed + "\n");
13558
14171
  if (filePath !== "stdin") {
@@ -13568,14 +14181,14 @@ hyv fix ${filePath}
13568
14181
  }
13569
14182
  await maybeShowLimitedModeHint(!!profile);
13570
14183
  } catch (error) {
13571
- console.error(import_chalk19.default.red(`Error: ${error.message}`));
14184
+ console.error(import_chalk20.default.red(`Error: ${error.message}`));
13572
14185
  process.exit(1);
13573
14186
  }
13574
14187
  });
13575
14188
  }
13576
14189
 
13577
14190
  // src/commands/check.ts
13578
- var import_chalk20 = __toESM(require_source());
14191
+ var import_chalk21 = __toESM(require_source());
13579
14192
  init_pipeline();
13580
14193
  init_local_profile();
13581
14194
  init_access();
@@ -13585,20 +14198,20 @@ function registerCheckCommand(program3) {
13585
14198
  const profile = await loadProfileForCommand(options.profile);
13586
14199
  let inputText = text;
13587
14200
  if (text === "-") {
13588
- const fs26 = require("fs");
14201
+ const fs27 = require("fs");
13589
14202
  if (process.stdin.isTTY) {
13590
- console.error(import_chalk20.default.red("No input provided. Pipe content or pass text as argument."));
14203
+ console.error(import_chalk21.default.red("No input provided. Pipe content or pass text as argument."));
13591
14204
  process.exit(1);
13592
14205
  }
13593
- inputText = fs26.readFileSync(0, "utf-8");
14206
+ inputText = fs27.readFileSync(0, "utf-8");
13594
14207
  }
13595
14208
  if (!inputText.trim()) {
13596
- console.error(import_chalk20.default.red("No text provided."));
14209
+ console.error(import_chalk21.default.red("No text provided."));
13597
14210
  process.exit(1);
13598
14211
  }
13599
- const fs25 = require("fs");
13600
- if (text !== "-" && fs25.existsSync(text)) {
13601
- console.log(import_chalk20.default.yellow(`"${text}" looks like a file. Did you mean: hyv scan ${text}`));
14212
+ const fs26 = require("fs");
14213
+ if (text !== "-" && fs26.existsSync(text)) {
14214
+ console.log(import_chalk21.default.yellow(`"${text}" looks like a file. Did you mean: hyv scan ${text}`));
13602
14215
  process.exit(1);
13603
14216
  }
13604
14217
  const result = runPipeline(inputText, profile, false);
@@ -13622,30 +14235,30 @@ function registerCheckCommand(program3) {
13622
14235
  }, null, 2));
13623
14236
  } else {
13624
14237
  if (result.stats.totalSignals === 0) {
13625
- console.log(import_chalk20.default.green("\n\u2713 Clean \u2014 no AI patterns found."));
13626
- console.log(import_chalk20.default.dim(` score: ${result.score}/100`));
14238
+ console.log(import_chalk21.default.green("\n\u2713 Clean \u2014 no AI patterns found."));
14239
+ console.log(import_chalk21.default.dim(` score: ${result.score}/100`));
13627
14240
  } else {
13628
14241
  console.log("");
13629
14242
  printGroupedSignals(result.signalMap.signals.slice(0, 15), profile);
13630
14243
  if (result.signalMap.signals.length > 15) {
13631
- console.log(import_chalk20.default.dim(` ... and ${result.signalMap.signals.length - 15} more`));
14244
+ console.log(import_chalk21.default.dim(` ... and ${result.signalMap.signals.length - 15} more`));
13632
14245
  }
13633
- console.log(import_chalk20.default.yellow(`
14246
+ console.log(import_chalk21.default.yellow(`
13634
14247
  ${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow)`));
13635
- console.log(import_chalk20.default.dim(` score: ${result.score}/100`));
14248
+ console.log(import_chalk21.default.dim(` score: ${result.score}/100`));
13636
14249
  }
13637
14250
  await maybeShowLimitedModeHint(!!profile);
13638
14251
  }
13639
14252
  process.exit(result.stats.totalSignals > 0 ? 1 : 0);
13640
14253
  } catch (error) {
13641
- console.error(import_chalk20.default.red(`Error: ${error.message}`));
14254
+ console.error(import_chalk21.default.red(`Error: ${error.message}`));
13642
14255
  process.exit(1);
13643
14256
  }
13644
14257
  });
13645
14258
  }
13646
14259
 
13647
14260
  // src/commands/score.ts
13648
- var import_chalk21 = __toESM(require_source());
14261
+ var import_chalk22 = __toESM(require_source());
13649
14262
  init_pipeline();
13650
14263
  init_local_profile();
13651
14264
  function registerScoreCommand(program3) {
@@ -13670,14 +14283,14 @@ function registerScoreCommand(program3) {
13670
14283
  process.exit(1);
13671
14284
  }
13672
14285
  } catch (error) {
13673
- console.error(import_chalk21.default.red(`Error: ${error.message}`));
14286
+ console.error(import_chalk22.default.red(`Error: ${error.message}`));
13674
14287
  process.exit(1);
13675
14288
  }
13676
14289
  });
13677
14290
  }
13678
14291
 
13679
14292
  // src/commands/diff.ts
13680
- var import_chalk22 = __toESM(require_source());
14293
+ var import_chalk23 = __toESM(require_source());
13681
14294
  init_pipeline();
13682
14295
  init_local_profile();
13683
14296
  function registerDiffCommand(program3) {
@@ -13687,9 +14300,9 @@ function registerDiffCommand(program3) {
13687
14300
  const { text, path: filePath } = readText(file);
13688
14301
  const result = runPipeline(text, profile, true);
13689
14302
  if (result.changes.length === 0) {
13690
- console.log(import_chalk22.default.green("\n\u2713 No auto-fixable changes."));
14303
+ console.log(import_chalk23.default.green("\n\u2713 No auto-fixable changes."));
13691
14304
  if (result.stats.needsLLM > 0) {
13692
- console.log(import_chalk22.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite`));
14305
+ console.log(import_chalk23.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite`));
13693
14306
  }
13694
14307
  return;
13695
14308
  }
@@ -13704,42 +14317,42 @@ function registerDiffCommand(program3) {
13704
14317
  const originalLines = text.split("\n");
13705
14318
  const fixedLines = result.fixed.split("\n");
13706
14319
  const contextN = parseInt(options.context, 10);
13707
- console.log(import_chalk22.default.dim(`--- ${filePath}`));
13708
- console.log(import_chalk22.default.dim(`+++ ${filePath} (fixed)`));
14320
+ console.log(import_chalk23.default.dim(`--- ${filePath}`));
14321
+ console.log(import_chalk23.default.dim(`+++ ${filePath} (fixed)`));
13709
14322
  for (const change of result.changes) {
13710
14323
  const lineIdx = change.line - 1;
13711
14324
  const start = Math.max(0, lineIdx - contextN);
13712
14325
  const end = Math.min(originalLines.length, lineIdx + contextN + 1);
13713
- console.log(import_chalk22.default.dim(`@@ -${start + 1},${end - start} +${start + 1},${end - start} @@`));
14326
+ console.log(import_chalk23.default.dim(`@@ -${start + 1},${end - start} +${start + 1},${end - start} @@`));
13714
14327
  for (let i = start; i < end; i++) {
13715
14328
  if (i === lineIdx) {
13716
- console.log(import_chalk22.default.red(`-${originalLines[i]}`));
13717
- console.log(import_chalk22.default.green(`+${fixedLines[i]}`));
14329
+ console.log(import_chalk23.default.red(`-${originalLines[i]}`));
14330
+ console.log(import_chalk23.default.green(`+${fixedLines[i]}`));
13718
14331
  } else {
13719
- console.log(import_chalk22.default.dim(` ${originalLines[i]}`));
14332
+ console.log(import_chalk23.default.dim(` ${originalLines[i]}`));
13720
14333
  }
13721
14334
  }
13722
14335
  }
13723
- console.log(import_chalk22.default.green(`
14336
+ console.log(import_chalk23.default.green(`
13724
14337
  ${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} available`));
13725
- console.log(import_chalk22.default.dim(` run: hyv fix ${file} -i to apply`));
14338
+ console.log(import_chalk23.default.dim(` run: hyv fix ${file} -i to apply`));
13726
14339
  if (options.apply && filePath !== "stdin") {
13727
- const fs25 = require("fs");
13728
- const path24 = require("path");
14340
+ const fs26 = require("fs");
14341
+ const path25 = require("path");
13729
14342
  const backupPath = filePath + ".bak";
13730
- fs25.copyFileSync(filePath, backupPath);
13731
- fs25.writeFileSync(filePath, result.fixed);
13732
- console.log(import_chalk22.default.green(` \u2713 Applied. Backup: ${path24.basename(backupPath)}`));
14343
+ fs26.copyFileSync(filePath, backupPath);
14344
+ fs26.writeFileSync(filePath, result.fixed);
14345
+ console.log(import_chalk23.default.green(` \u2713 Applied. Backup: ${path25.basename(backupPath)}`));
13733
14346
  }
13734
14347
  } catch (error) {
13735
- console.error(import_chalk22.default.red(`Error: ${error.message}`));
14348
+ console.error(import_chalk23.default.red(`Error: ${error.message}`));
13736
14349
  process.exit(1);
13737
14350
  }
13738
14351
  });
13739
14352
  }
13740
14353
 
13741
14354
  // src/commands/rules.ts
13742
- var import_chalk23 = __toESM(require_source());
14355
+ var import_chalk24 = __toESM(require_source());
13743
14356
  init_config();
13744
14357
  var RULE_CATALOG = [
13745
14358
  // AI Overused Words
@@ -13833,7 +14446,7 @@ function registerRulesCommand(program3) {
13833
14446
  for (const id of ids)
13834
14447
  disabledRules.delete(id);
13835
14448
  writeConfig({ ...config, disabled_rules: [...disabledRules] });
13836
- console.log(import_chalk23.default.green(`
14449
+ console.log(import_chalk24.default.green(`
13837
14450
  \u2713 Enabled: ${ids.join(", ")}`));
13838
14451
  return;
13839
14452
  }
@@ -13842,13 +14455,13 @@ function registerRulesCommand(program3) {
13842
14455
  for (const id of ids)
13843
14456
  disabledRules.add(id);
13844
14457
  writeConfig({ ...config, disabled_rules: [...disabledRules] });
13845
- console.log(import_chalk23.default.green(`
14458
+ console.log(import_chalk24.default.green(`
13846
14459
  \u2713 Disabled: ${ids.join(", ")}`));
13847
14460
  return;
13848
14461
  }
13849
14462
  if (options.reset) {
13850
14463
  writeConfig({ ...config, disabled_rules: [] });
13851
- console.log(import_chalk23.default.green("\n\u2713 All rules reset to default (enabled)"));
14464
+ console.log(import_chalk24.default.green("\n\u2713 All rules reset to default (enabled)"));
13852
14465
  return;
13853
14466
  }
13854
14467
  let rules = [...RULE_CATALOG];
@@ -13876,36 +14489,36 @@ function registerRulesCommand(program3) {
13876
14489
  }
13877
14490
  console.log("");
13878
14491
  for (const [cat, catRules] of categories) {
13879
- console.log(import_chalk23.default.bold(` ${cat} rules (${catRules.length})
14492
+ console.log(import_chalk24.default.bold(` ${cat} rules (${catRules.length})
13880
14493
  `));
13881
- console.log(import_chalk23.default.dim(" ID Sev AutoFix Status"));
13882
- console.log(import_chalk23.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
14494
+ console.log(import_chalk24.default.dim(" ID Sev AutoFix Status"));
14495
+ console.log(import_chalk24.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"));
13883
14496
  for (const rule of catRules) {
13884
- const sev = rule.severity === "red" ? import_chalk23.default.red("red") : import_chalk23.default.yellow("yel");
13885
- const fix = rule.autoFixable ? import_chalk23.default.green(" \u2713") : import_chalk23.default.dim(" \u2717");
13886
- const enabled2 = disabledRules.has(rule.id) ? import_chalk23.default.red(" \u2717 disabled") : import_chalk23.default.green(" \u2713 enabled");
14497
+ const sev = rule.severity === "red" ? import_chalk24.default.red("red") : import_chalk24.default.yellow("yel");
14498
+ const fix = rule.autoFixable ? import_chalk24.default.green(" \u2713") : import_chalk24.default.dim(" \u2717");
14499
+ const enabled2 = disabledRules.has(rule.id) ? import_chalk24.default.red(" \u2717 disabled") : import_chalk24.default.green(" \u2713 enabled");
13887
14500
  const id = rule.id.padEnd(24);
13888
- console.log(` ${import_chalk23.default.dim(id)} ${sev} ${fix} ${enabled2}`);
14501
+ console.log(` ${import_chalk24.default.dim(id)} ${sev} ${fix} ${enabled2}`);
13889
14502
  }
13890
14503
  console.log("");
13891
14504
  }
13892
14505
  const enabled = rules.filter((r) => !disabledRules.has(r.id)).length;
13893
14506
  const disabled = rules.length - enabled;
13894
- console.log(import_chalk23.default.dim(` ${rules.length} rules, ${enabled} enabled, ${disabled} disabled`));
13895
- console.log(import_chalk23.default.dim(` toggle: hyv rules --disable <id>`));
13896
- console.log(import_chalk23.default.dim(` reset: hyv rules --reset
14507
+ console.log(import_chalk24.default.dim(` ${rules.length} rules, ${enabled} enabled, ${disabled} disabled`));
14508
+ console.log(import_chalk24.default.dim(` toggle: hyv rules --disable <id>`));
14509
+ console.log(import_chalk24.default.dim(` reset: hyv rules --reset
13897
14510
  `));
13898
14511
  } catch (error) {
13899
- console.error(import_chalk23.default.red(`Error: ${error.message}`));
14512
+ console.error(import_chalk24.default.red(`Error: ${error.message}`));
13900
14513
  process.exit(1);
13901
14514
  }
13902
14515
  });
13903
14516
  }
13904
14517
 
13905
14518
  // src/commands/batch.ts
13906
- var import_chalk24 = __toESM(require_source());
13907
- var fs16 = __toESM(require("fs"));
13908
- var path17 = __toESM(require("path"));
14519
+ var import_chalk25 = __toESM(require_source());
14520
+ var fs17 = __toESM(require("fs"));
14521
+ var path18 = __toESM(require("path"));
13909
14522
  init_pipeline();
13910
14523
  init_local_profile();
13911
14524
  function registerBatchCommand(program3) {
@@ -13918,7 +14531,7 @@ function registerBatchCommand(program3) {
13918
14531
  nodir: true
13919
14532
  });
13920
14533
  if (files.length === 0) {
13921
- console.log(import_chalk24.default.yellow(`
14534
+ console.log(import_chalk25.default.yellow(`
13922
14535
  No files matching: ${pattern}`));
13923
14536
  return;
13924
14537
  }
@@ -13932,16 +14545,16 @@ No files matching: ${pattern}`));
13932
14545
  process.exit(1);
13933
14546
  }
13934
14547
  if (options.format === "text") {
13935
- console.log(import_chalk24.default.dim(`
14548
+ console.log(import_chalk25.default.dim(`
13936
14549
  scanning ${files.length} file${files.length === 1 ? "" : "s"}...
13937
14550
  `));
13938
14551
  }
13939
14552
  const results = [];
13940
14553
  for (const file of files) {
13941
- const absPath = path17.resolve(file);
13942
- if (!fs16.existsSync(absPath))
14554
+ const absPath = path18.resolve(file);
14555
+ if (!fs17.existsSync(absPath))
13943
14556
  continue;
13944
- const text = fs16.readFileSync(absPath, "utf-8");
14557
+ const text = fs17.readFileSync(absPath, "utf-8");
13945
14558
  const result = runPipeline(text, profile, options.fix || false);
13946
14559
  results.push({
13947
14560
  file,
@@ -13971,23 +14584,23 @@ No files matching: ${pattern}`));
13971
14584
  }
13972
14585
  } else {
13973
14586
  for (const r of results) {
13974
- const icon = r.issues > 0 ? import_chalk24.default.red("\u25CF") : import_chalk24.default.green("\u25CB");
13975
- const score = r.score < 60 ? import_chalk24.default.red(r.score) : r.score < 80 ? import_chalk24.default.yellow(r.score) : import_chalk24.default.green(r.score);
13976
- const issueStr = r.issues > 0 ? import_chalk24.default.red(`${r.issues} issues`) : import_chalk24.default.green("clean");
14587
+ const icon = r.issues > 0 ? import_chalk25.default.red("\u25CF") : import_chalk25.default.green("\u25CB");
14588
+ const score = r.score < 60 ? import_chalk25.default.red(r.score) : r.score < 80 ? import_chalk25.default.yellow(r.score) : import_chalk25.default.green(r.score);
14589
+ const issueStr = r.issues > 0 ? import_chalk25.default.red(`${r.issues} issues`) : import_chalk25.default.green("clean");
13977
14590
  console.log(` ${icon} ${r.file.padEnd(40)} ${issueStr.padEnd(20)} score: ${score}`);
13978
14591
  }
13979
14592
  const withIssues = results.filter((r) => r.issues > 0).length;
13980
14593
  const totalIssues = results.reduce((sum, r) => sum + r.issues, 0);
13981
14594
  const worst = results.reduce((min, r) => r.score < min.score ? r : min, results[0]);
13982
- console.log(import_chalk24.default.dim(`
14595
+ console.log(import_chalk25.default.dim(`
13983
14596
  \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`));
13984
- console.log(import_chalk24.default.dim(` ${results.length} files, ${withIssues} with issues, ${totalIssues} total issues`));
14597
+ console.log(import_chalk25.default.dim(` ${results.length} files, ${withIssues} with issues, ${totalIssues} total issues`));
13985
14598
  if (withIssues > 0) {
13986
- console.log(import_chalk24.default.dim(` worst: ${worst.file} (${worst.score}/100)`));
14599
+ console.log(import_chalk25.default.dim(` worst: ${worst.file} (${worst.score}/100)`));
13987
14600
  }
13988
14601
  if (options.fix && options.inPlace) {
13989
14602
  const fixed = results.filter((r) => r.autoFixes > 0);
13990
- console.log(import_chalk24.default.green(`
14603
+ console.log(import_chalk25.default.green(`
13991
14604
  \u2713 ${fixed.reduce((sum, r) => sum + r.autoFixes, 0)} auto-fixes applied across ${fixed.length} files`));
13992
14605
  }
13993
14606
  }
@@ -13999,25 +14612,25 @@ No files matching: ${pattern}`));
13999
14612
  if (belowThreshold)
14000
14613
  process.exit(1);
14001
14614
  } catch (error) {
14002
- console.error(import_chalk24.default.red(`Error: ${error.message}`));
14615
+ console.error(import_chalk25.default.red(`Error: ${error.message}`));
14003
14616
  process.exit(1);
14004
14617
  }
14005
14618
  });
14006
14619
  }
14007
14620
 
14008
14621
  // src/commands/watch.ts
14009
- var import_chalk25 = __toESM(require_source());
14010
- var fs17 = __toESM(require("fs"));
14011
- var path18 = __toESM(require("path"));
14622
+ var import_chalk26 = __toESM(require_source());
14623
+ var fs18 = __toESM(require("fs"));
14624
+ var path19 = __toESM(require("path"));
14012
14625
  init_pipeline();
14013
14626
  init_local_profile();
14014
14627
  function registerWatchCommand(program3) {
14015
14628
  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) => {
14016
14629
  try {
14017
14630
  const profile = await loadProfileForCommand(options.profile);
14018
- const absPath = path18.resolve(file);
14019
- if (!fs17.existsSync(absPath)) {
14020
- console.error(import_chalk25.default.red(`File not found: ${absPath}`));
14631
+ const absPath = path19.resolve(file);
14632
+ if (!fs18.existsSync(absPath)) {
14633
+ console.error(import_chalk26.default.red(`File not found: ${absPath}`));
14021
14634
  process.exit(1);
14022
14635
  }
14023
14636
  if (options.command === "fix") {
@@ -14029,59 +14642,59 @@ function registerWatchCommand(program3) {
14029
14642
  if (!confirmed)
14030
14643
  process.exit(1);
14031
14644
  }
14032
- console.log(import_chalk25.default.dim(`
14645
+ console.log(import_chalk26.default.dim(`
14033
14646
  watching ${absPath}`));
14034
- console.log(import_chalk25.default.dim(`command: ${options.command} debounce: ${options.debounce}ms`));
14035
- console.log(import_chalk25.default.dim("ctrl+c to stop\n"));
14647
+ console.log(import_chalk26.default.dim(`command: ${options.command} debounce: ${options.debounce}ms`));
14648
+ console.log(import_chalk26.default.dim("ctrl+c to stop\n"));
14036
14649
  let debounceTimer = null;
14037
14650
  const debounceMs = parseInt(options.debounce, 10);
14038
14651
  let scanCount = 0;
14039
14652
  let lastScore = 0;
14040
14653
  let ignoreWatchUntil = 0;
14041
14654
  const runScan = () => {
14042
- const text = fs17.readFileSync(absPath, "utf-8");
14655
+ const text = fs18.readFileSync(absPath, "utf-8");
14043
14656
  const result = runPipeline(text, profile, options.command === "fix");
14044
14657
  scanCount++;
14045
14658
  const now = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
14046
- console.log(import_chalk25.default.dim(`[${now}] saved \u2014 ${options.command}ing...`));
14659
+ console.log(import_chalk26.default.dim(`[${now}] saved \u2014 ${options.command}ing...`));
14047
14660
  if (options.command === "score") {
14048
14661
  const score = result.score;
14049
- const color = score < 60 ? import_chalk25.default.red : score < 80 ? import_chalk25.default.yellow : import_chalk25.default.green;
14662
+ const color = score < 60 ? import_chalk26.default.red : score < 80 ? import_chalk26.default.yellow : import_chalk26.default.green;
14050
14663
  console.log(` ${color(score + "/100")}`);
14051
14664
  if (lastScore > 0 && score !== lastScore) {
14052
14665
  const delta = score - lastScore;
14053
- console.log(import_chalk25.default.dim(` ${delta > 0 ? "\u2191" : "\u2193"} ${Math.abs(delta)} from last scan`));
14666
+ console.log(import_chalk26.default.dim(` ${delta > 0 ? "\u2191" : "\u2193"} ${Math.abs(delta)} from last scan`));
14054
14667
  }
14055
14668
  lastScore = score;
14056
14669
  } else if (options.command === "fix") {
14057
14670
  if (result.changes.length > 0) {
14058
14671
  for (const change of result.changes) {
14059
- console.log(import_chalk25.default.dim(` Line ${change.line}: `) + import_chalk25.default.red(change.before) + import_chalk25.default.dim(" \u2192 ") + import_chalk25.default.green(change.after));
14672
+ console.log(import_chalk26.default.dim(` Line ${change.line}: `) + import_chalk26.default.red(change.before) + import_chalk26.default.dim(" \u2192 ") + import_chalk26.default.green(change.after));
14060
14673
  }
14061
14674
  ignoreWatchUntil = Date.now() + debounceMs + 200;
14062
14675
  writeInPlaceWithBackup(absPath, result.fixed);
14063
- console.log(import_chalk25.default.green(` \u2713 ${result.changes.length} auto-fixes applied`));
14676
+ console.log(import_chalk26.default.green(` \u2713 ${result.changes.length} auto-fixes applied`));
14064
14677
  } else {
14065
- console.log(import_chalk25.default.green(" \u2713 no auto-fixable issues"));
14678
+ console.log(import_chalk26.default.green(" \u2713 no auto-fixable issues"));
14066
14679
  }
14067
14680
  } else {
14068
14681
  if (result.stats.totalSignals === 0) {
14069
- console.log(import_chalk25.default.green(` \u2713 clean \u2014 score: ${result.score}/100`));
14682
+ console.log(import_chalk26.default.green(` \u2713 clean \u2014 score: ${result.score}/100`));
14070
14683
  } else {
14071
- console.log(import_chalk25.default.yellow(` ${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow) score: ${result.score}/100`));
14684
+ console.log(import_chalk26.default.yellow(` ${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow) score: ${result.score}/100`));
14072
14685
  for (const signal of result.signalMap.signals.slice(0, 3)) {
14073
- const sev = signal.severity === "red" ? import_chalk25.default.red("\u25CF") : import_chalk25.default.yellow("\u25CB");
14686
+ const sev = signal.severity === "red" ? import_chalk26.default.red("\u25CF") : import_chalk26.default.yellow("\u25CB");
14074
14687
  console.log(` ${sev} line ${signal.line}: ${signal.id} \u2014 ${signal.suggestion}`);
14075
14688
  }
14076
14689
  if (result.signalMap.signals.length > 3) {
14077
- console.log(import_chalk25.default.dim(` ... and ${result.signalMap.signals.length - 3} more`));
14690
+ console.log(import_chalk26.default.dim(` ... and ${result.signalMap.signals.length - 3} more`));
14078
14691
  }
14079
14692
  }
14080
14693
  }
14081
14694
  console.log("");
14082
14695
  };
14083
14696
  runScan();
14084
- fs17.watch(absPath, (eventType) => {
14697
+ fs18.watch(absPath, (eventType) => {
14085
14698
  if (eventType !== "change")
14086
14699
  return;
14087
14700
  if (Date.now() < ignoreWatchUntil)
@@ -14091,22 +14704,22 @@ watching ${absPath}`));
14091
14704
  debounceTimer = setTimeout(runScan, debounceMs);
14092
14705
  });
14093
14706
  process.on("SIGINT", () => {
14094
- console.log(import_chalk25.default.dim(`
14707
+ console.log(import_chalk26.default.dim(`
14095
14708
  ${scanCount} scans completed. exiting.`));
14096
14709
  process.exit(0);
14097
14710
  });
14098
14711
  await new Promise(() => {
14099
14712
  });
14100
14713
  } catch (error) {
14101
- console.error(import_chalk25.default.red(`Error: ${error.message}`));
14714
+ console.error(import_chalk26.default.red(`Error: ${error.message}`));
14102
14715
  process.exit(1);
14103
14716
  }
14104
14717
  });
14105
14718
  }
14106
14719
 
14107
14720
  // src/commands/demo.ts
14108
- var import_chalk26 = __toESM(require_source());
14109
- var fs18 = __toESM(require("fs"));
14721
+ var import_chalk27 = __toESM(require_source());
14722
+ var fs19 = __toESM(require("fs"));
14110
14723
  var SAMPLES = {
14111
14724
  linkedin: {
14112
14725
  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.
@@ -14196,46 +14809,46 @@ function registerDemoCommand(program3) {
14196
14809
  const style = options.clean ? "clean" : options.style;
14197
14810
  const sample = SAMPLES[style];
14198
14811
  if (!sample) {
14199
- console.error(import_chalk26.default.red(`Unknown style: ${options.style}. Valid: ${Object.keys(SAMPLES).filter((k) => k !== "clean").join(", ")}`));
14812
+ console.error(import_chalk27.default.red(`Unknown style: ${options.style}. Valid: ${Object.keys(SAMPLES).filter((k) => k !== "clean").join(", ")}`));
14200
14813
  process.exit(1);
14201
14814
  }
14202
14815
  if (options.output) {
14203
14816
  const outputPath = require("path").resolve(options.output);
14204
- fs18.writeFileSync(outputPath, sample.text);
14205
- console.log(import_chalk26.default.green(`
14817
+ fs19.writeFileSync(outputPath, sample.text);
14818
+ console.log(import_chalk27.default.green(`
14206
14819
  \u2713 Sample written to ${outputPath}`));
14207
14820
  if (sample.patterns.length > 0) {
14208
- console.log(import_chalk26.default.dim(` Contains ${sample.patterns.length} AI patterns: ${sample.patterns.slice(0, 5).join(", ")}...`));
14821
+ console.log(import_chalk27.default.dim(` Contains ${sample.patterns.length} AI patterns: ${sample.patterns.slice(0, 5).join(", ")}...`));
14209
14822
  }
14210
- console.log(import_chalk26.default.dim(`
14823
+ console.log(import_chalk27.default.dim(`
14211
14824
  Scan it: hyv scan ${options.output}`));
14212
- console.log(import_chalk26.default.dim(` Fix it: hyv fix ${options.output}`));
14825
+ console.log(import_chalk27.default.dim(` Fix it: hyv fix ${options.output}`));
14213
14826
  } else {
14214
- console.log(import_chalk26.default.bold(`
14827
+ console.log(import_chalk27.default.bold(`
14215
14828
  \u2500\u2500\u2500 sample (${style}) \u2500\u2500\u2500
14216
14829
  `));
14217
14830
  console.log(sample.text);
14218
- console.log(import_chalk26.default.dim(`
14831
+ console.log(import_chalk27.default.dim(`
14219
14832
  \u2500\u2500\u2500 end \u2500\u2500\u2500`));
14220
14833
  if (sample.patterns.length > 0) {
14221
- console.log(import_chalk26.default.dim(`
14834
+ console.log(import_chalk27.default.dim(`
14222
14835
  ${sample.patterns.length} AI patterns embedded: ${sample.patterns.slice(0, 8).join(", ")}...`));
14223
14836
  }
14224
- console.log(import_chalk26.default.dim(`
14837
+ console.log(import_chalk27.default.dim(`
14225
14838
  Save to file: hyv demo --output demo.md`));
14226
- console.log(import_chalk26.default.dim(` Scan it: hyv scan demo.md`));
14227
- console.log(import_chalk26.default.dim(` Fix it: hyv fix demo.md
14839
+ console.log(import_chalk27.default.dim(` Scan it: hyv scan demo.md`));
14840
+ console.log(import_chalk27.default.dim(` Fix it: hyv fix demo.md
14228
14841
  `));
14229
14842
  }
14230
14843
  } catch (error) {
14231
- console.error(import_chalk26.default.red(`Error: ${error.message}`));
14844
+ console.error(import_chalk27.default.red(`Error: ${error.message}`));
14232
14845
  process.exit(1);
14233
14846
  }
14234
14847
  });
14235
14848
  }
14236
14849
 
14237
14850
  // src/commands/open.ts
14238
- var import_chalk27 = __toESM(require_source());
14851
+ var import_chalk28 = __toESM(require_source());
14239
14852
  var PAGES = {
14240
14853
  dashboard: "https://holdyourvoice.com/dashboard",
14241
14854
  profiles: "https://holdyourvoice.com/dashboard",
@@ -14252,12 +14865,12 @@ function registerOpenCommand(program3) {
14252
14865
  return;
14253
14866
  }
14254
14867
  const open3 = (await Promise.resolve().then(() => __toESM(require_open()))).default;
14255
- console.log(import_chalk27.default.dim(`
14868
+ console.log(import_chalk28.default.dim(`
14256
14869
  Opening ${url}...`));
14257
14870
  await open3(url);
14258
- console.log(import_chalk27.default.green(" \u2713 Opened in browser\n"));
14871
+ console.log(import_chalk28.default.green(" \u2713 Opened in browser\n"));
14259
14872
  } catch (error) {
14260
- console.error(import_chalk27.default.red(`Error: ${error.message}`));
14873
+ console.error(import_chalk28.default.red(`Error: ${error.message}`));
14261
14874
  process.exit(1);
14262
14875
  }
14263
14876
  });
@@ -14267,46 +14880,67 @@ function registerOpenCommand(program3) {
14267
14880
  init_welcome();
14268
14881
 
14269
14882
  // src/lib/onboarding.ts
14270
- var fs19 = __toESM(require("fs"));
14271
- var path19 = __toESM(require("path"));
14272
- var os6 = __toESM(require("os"));
14273
- var hyvDir = path19.join(os6.homedir(), ".hyv");
14274
- var onboardingFile = path19.join(hyvDir, "onboarding-complete.json");
14883
+ var fs20 = __toESM(require("fs"));
14884
+ var path20 = __toESM(require("path"));
14885
+ var os7 = __toESM(require("os"));
14886
+ var hyvDir = path20.join(os7.homedir(), ".hyv");
14887
+ var onboardingFile = path20.join(hyvDir, "onboarding-complete.json");
14275
14888
  function hasCompletedOnboarding() {
14276
14889
  try {
14277
- return fs19.existsSync(onboardingFile);
14890
+ return fs20.existsSync(onboardingFile);
14278
14891
  } catch {
14279
14892
  return false;
14280
14893
  }
14281
14894
  }
14282
14895
  function markOnboardingComplete(version) {
14283
- if (!fs19.existsSync(hyvDir))
14284
- fs19.mkdirSync(hyvDir, { recursive: true });
14285
- fs19.writeFileSync(
14896
+ if (!fs20.existsSync(hyvDir))
14897
+ fs20.mkdirSync(hyvDir, { recursive: true });
14898
+ fs20.writeFileSync(
14286
14899
  onboardingFile,
14287
14900
  JSON.stringify({ version, completed_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
14288
14901
  );
14289
14902
  }
14290
14903
 
14291
14904
  // src/commands/welcome.ts
14292
- var fs20 = __toESM(require("fs"));
14293
- var path20 = __toESM(require("path"));
14905
+ init_welcome_flow();
14906
+ var fs21 = __toESM(require("fs"));
14907
+ var path21 = __toESM(require("path"));
14294
14908
  function registerWelcomeCommand(program3) {
14295
14909
  const pkgVersion3 = (() => {
14296
14910
  try {
14297
- const pkgPath3 = path20.resolve(__dirname, "..", "package.json");
14298
- return JSON.parse(fs20.readFileSync(pkgPath3, "utf-8")).version;
14911
+ const pkgPath3 = path21.resolve(__dirname, "..", "package.json");
14912
+ return JSON.parse(fs21.readFileSync(pkgPath3, "utf-8")).version;
14299
14913
  } catch {
14300
14914
  return "0.0.0";
14301
14915
  }
14302
14916
  })();
14303
- const action = (opts) => {
14917
+ program3.command("welcome").description("Set up your voice: name \u2192 samples \u2192 test \u2192 signup").option("--guide", "Show LLM/terminal guide (no prompts)").option("--extract-prompt [name]", "Print chat extraction prompt for agents").option("--step <n>", "Show guide for step 1\u20134", (v) => parseInt(v, 10)).option("--no-demo", "Skip sample scan line in guide mode").option("--quiet", "Suppress output").action(async (opts) => {
14304
14918
  if (opts.quiet)
14305
14919
  return;
14306
- printWelcome({ skipDemo: opts.demo === false });
14307
- markOnboardingComplete(pkgVersion3);
14308
- };
14309
- program3.command("welcome").description("Quick start: 4 steps, test scan, subscribe prompt").option("--no-demo", "Skip the sample scan line").option("--quiet", "Suppress output").action(action);
14920
+ const fromPostinstall = process.env.HYV_POSTINSTALL_ONBOARDING === "1";
14921
+ if (opts.extractPrompt !== void 0) {
14922
+ const name = typeof opts.extractPrompt === "string" ? opts.extractPrompt : "my-voice";
14923
+ console.log("\n" + buildVoiceExtractionPrompt(name) + "\n");
14924
+ markOnboardingComplete(pkgVersion3);
14925
+ return;
14926
+ }
14927
+ if (opts.step && opts.step >= 1 && opts.step <= 4) {
14928
+ console.log("\n" + getMcpWelcomeResponse({ step: opts.step }) + "\n");
14929
+ markOnboardingComplete(pkgVersion3);
14930
+ return;
14931
+ }
14932
+ if (opts.guide || fromPostinstall || !process.stdin.isTTY) {
14933
+ printWelcome({ guide: true, skipDemo: opts.demo === false });
14934
+ markOnboardingComplete(pkgVersion3);
14935
+ return;
14936
+ }
14937
+ try {
14938
+ await runInteractiveWelcome();
14939
+ markOnboardingComplete(pkgVersion3);
14940
+ } catch {
14941
+ process.exit(1);
14942
+ }
14943
+ });
14310
14944
  program3.command("free").description("List everything free in the CLI and on the web").action(() => {
14311
14945
  console.log("\n" + formatFreeToolsList() + "\n");
14312
14946
  });
@@ -14314,12 +14948,15 @@ function registerWelcomeCommand(program3) {
14314
14948
  function getDemoText() {
14315
14949
  return formatDemoResult(runWelcomeDemo());
14316
14950
  }
14317
- function getWelcomeText() {
14318
- return buildWelcomeMessage({ skipDemo: false });
14951
+ function getWelcomeText(args2) {
14952
+ if (args2?.mode || args2?.step) {
14953
+ return getMcpWelcomeResponse(args2);
14954
+ }
14955
+ return buildWelcomeGuide({ forLlm: true });
14319
14956
  }
14320
14957
 
14321
14958
  // src/commands/content.ts
14322
- var import_chalk28 = __toESM(require_source());
14959
+ var import_chalk29 = __toESM(require_source());
14323
14960
  init_free_paid();
14324
14961
  var TEMPLATES = {
14325
14962
  cursor: {
@@ -14393,30 +15030,30 @@ ${COMMUNITY_URL}`
14393
15030
  function registerContentCommand(program3) {
14394
15031
  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) => {
14395
15032
  if (opts.list) {
14396
- console.log(import_chalk28.default.bold("\nhyv content templates\n"));
15033
+ console.log(import_chalk29.default.bold("\nhyv content templates\n"));
14397
15034
  for (const [key, t2] of Object.entries(TEMPLATES)) {
14398
- console.log(import_chalk28.default.dim(` ${key}`) + ` \u2014 ${t2.title}`);
15035
+ console.log(import_chalk29.default.dim(` ${key}`) + ` \u2014 ${t2.title}`);
14399
15036
  }
14400
15037
  console.log("");
14401
15038
  return;
14402
15039
  }
14403
15040
  const t = TEMPLATES[topic] || TEMPLATES.cursor;
14404
- console.log(import_chalk28.default.bold(`
15041
+ console.log(import_chalk29.default.bold(`
14405
15042
  ${t.title}
14406
15043
  `));
14407
15044
  console.log(t.body);
14408
- console.log(import_chalk28.default.dim("\nBlog: https://holdyourvoice.com/blog\n"));
15045
+ console.log(import_chalk29.default.dim("\nBlog: https://holdyourvoice.com/blog\n"));
14409
15046
  });
14410
15047
  }
14411
15048
 
14412
15049
  // src/commands/upgrade.ts
14413
- var import_chalk29 = __toESM(require_source());
15050
+ var import_chalk30 = __toESM(require_source());
14414
15051
  var import_child_process3 = require("child_process");
14415
15052
  init_version();
14416
15053
  function registerUpgradeCommand(program3) {
14417
15054
  program3.command("upgrade").description("Upgrade to the latest @holdyourvoice/hyv").option("--check", "Only check if an update is available").action(async (opts) => {
14418
15055
  const current = getCliVersion();
14419
- console.log(import_chalk29.default.dim(`
15056
+ console.log(import_chalk30.default.dim(`
14420
15057
  Current: ${getEngineLabel()}
14421
15058
  `));
14422
15059
  let latest = current;
@@ -14424,38 +15061,38 @@ Current: ${getEngineLabel()}
14424
15061
  const out = (0, import_child_process3.execSync)("npm view @holdyourvoice/hyv version", { encoding: "utf-8", timeout: 15e3 });
14425
15062
  latest = out.trim();
14426
15063
  } catch {
14427
- console.log(import_chalk29.default.yellow("Could not reach npm registry. Try: npm i -g @holdyourvoice/hyv@latest"));
15064
+ console.log(import_chalk30.default.yellow("Could not reach npm registry. Try: npm i -g @holdyourvoice/hyv@latest"));
14428
15065
  return;
14429
15066
  }
14430
15067
  const cmp = compareSemver(current, latest);
14431
15068
  if (cmp === 0) {
14432
- console.log(import_chalk29.default.green("\u2713 You are on the latest version."));
15069
+ console.log(import_chalk30.default.green("\u2713 You are on the latest version."));
14433
15070
  return;
14434
15071
  }
14435
15072
  if (cmp > 0) {
14436
- console.log(import_chalk29.default.green(`\u2713 You are ahead of npm (published: ${latest}).`));
15073
+ console.log(import_chalk30.default.green(`\u2713 You are ahead of npm (published: ${latest}).`));
14437
15074
  return;
14438
15075
  }
14439
- console.log(import_chalk29.default.cyan(`Update available: ${current} \u2192 ${latest}`));
15076
+ console.log(import_chalk30.default.cyan(`Update available: ${current} \u2192 ${latest}`));
14440
15077
  if (opts.check) {
14441
- console.log(import_chalk29.default.dim("\nRun: hyv upgrade or npm i -g @holdyourvoice/hyv@latest\n"));
15078
+ console.log(import_chalk30.default.dim("\nRun: hyv upgrade or npm i -g @holdyourvoice/hyv@latest\n"));
14442
15079
  return;
14443
15080
  }
14444
- console.log(import_chalk29.default.dim("Installing..."));
15081
+ console.log(import_chalk30.default.dim("Installing..."));
14445
15082
  try {
14446
15083
  (0, import_child_process3.execSync)("npm i -g @holdyourvoice/hyv@latest", { stdio: "inherit" });
14447
- console.log(import_chalk29.default.green("\n\u2713 Upgraded successfully. Restart your terminal.\n"));
15084
+ console.log(import_chalk30.default.green("\n\u2713 Upgraded successfully. Restart your terminal.\n"));
14448
15085
  } catch {
14449
- console.log(import_chalk29.default.red("\nUpgrade failed. Run manually: npm i -g @holdyourvoice/hyv@latest\n"));
15086
+ console.log(import_chalk30.default.red("\nUpgrade failed. Run manually: npm i -g @holdyourvoice/hyv@latest\n"));
14450
15087
  process.exit(1);
14451
15088
  }
14452
15089
  });
14453
15090
  }
14454
15091
 
14455
15092
  // src/mcp.ts
14456
- var fs21 = __toESM(require("fs"));
14457
- var path21 = __toESM(require("path"));
14458
- var os7 = __toESM(require("os"));
15093
+ var fs22 = __toESM(require("fs"));
15094
+ var path22 = __toESM(require("path"));
15095
+ var os8 = __toESM(require("os"));
14459
15096
  init_classifier();
14460
15097
  init_autofix();
14461
15098
  init_validator();
@@ -14466,20 +15103,20 @@ init_welcome();
14466
15103
  init_config();
14467
15104
  init_telemetry();
14468
15105
  init_access();
14469
- var VOICE_MD = path21.join(os7.homedir(), ".hyv", "voice.md");
15106
+ var VOICE_MD = path22.join(os8.homedir(), ".hyv", "voice.md");
14470
15107
  var MAX_RESPONSE_CHARS = 12e4;
14471
15108
  var MAX_SCAN_FILE_BYTES = 2 * 1024 * 1024;
14472
15109
  function readScanFile(filePath) {
14473
- const cwdReal = fs21.realpathSync(process.cwd());
14474
- const resolved = fs21.realpathSync(path21.resolve(filePath));
14475
- if (!resolved.startsWith(cwdReal + path21.sep) && resolved !== cwdReal) {
15110
+ const cwdReal = fs22.realpathSync(process.cwd());
15111
+ const resolved = fs22.realpathSync(path22.resolve(filePath));
15112
+ if (!resolved.startsWith(cwdReal + path22.sep) && resolved !== cwdReal) {
14476
15113
  throw new Error(`file must be in the current working directory (${cwdReal})`);
14477
15114
  }
14478
- const stat = fs21.statSync(resolved);
15115
+ const stat = fs22.statSync(resolved);
14479
15116
  if (stat.size > MAX_SCAN_FILE_BYTES) {
14480
15117
  throw new Error(`file too large (max ${MAX_SCAN_FILE_BYTES} bytes)`);
14481
15118
  }
14482
- return fs21.readFileSync(resolved, "utf-8");
15119
+ return fs22.readFileSync(resolved, "utf-8");
14483
15120
  }
14484
15121
  function toolResultPayload(result) {
14485
15122
  const isError = result.startsWith("Error:") || result.startsWith("Unknown tool:");
@@ -14488,10 +15125,10 @@ function toolResultPayload(result) {
14488
15125
  ...isError ? { isError: true } : {}
14489
15126
  };
14490
15127
  }
14491
- var pkgPath = path21.resolve(__dirname, "..", "package.json");
15128
+ var pkgPath = path22.resolve(__dirname, "..", "package.json");
14492
15129
  var pkgVersion = (() => {
14493
15130
  try {
14494
- return JSON.parse(fs21.readFileSync(pkgPath, "utf-8")).version;
15131
+ return JSON.parse(fs22.readFileSync(pkgPath, "utf-8")).version;
14495
15132
  } catch {
14496
15133
  return "2.7.1";
14497
15134
  }
@@ -14720,8 +15357,8 @@ ${result.fixed}`;
14720
15357
  return `Error: ${err.message}`;
14721
15358
  }
14722
15359
  }
14723
- function toolWelcome() {
14724
- return getWelcomeText();
15360
+ function toolWelcome(args2 = {}) {
15361
+ return getWelcomeText(args2);
14725
15362
  }
14726
15363
  function toolListFreeTools() {
14727
15364
  return formatFreeToolsList();
@@ -14825,8 +15462,15 @@ async function toolClean(args2) {
14825
15462
  var TOOLS = [
14826
15463
  {
14827
15464
  name: "hyv_welcome",
14828
- description: "Show the Hold Your Voice quick start: 4 steps (install, scan, init, mcp), a sample scan line, and subscribe prompt. Call when the user is new or asks how to get started.",
14829
- inputSchema: { type: "object", properties: {} }
15465
+ description: "Profile-first onboarding: name \u2192 samples \u2192 test draft \u2192 signup. Call when the user is new. Use step (1-4) for one step, mode extract_prompt for chat voice extraction.",
15466
+ inputSchema: {
15467
+ type: "object",
15468
+ properties: {
15469
+ step: { type: "number", description: "Guide for step 1=name, 2=samples, 3=test, 4=signup" },
15470
+ mode: { type: "string", description: "Set to extract_prompt for LLM chat extraction prompt" },
15471
+ profile: { type: "string", description: "Profile name for extraction prompt" }
15472
+ }
15473
+ }
14830
15474
  },
14831
15475
  {
14832
15476
  name: "hyv_list_free_tools",
@@ -14963,15 +15607,15 @@ var PROMPTS = [
14963
15607
  ];
14964
15608
  var HYV_STATUS_PROMPT = `Use hold your voice MCP tools:
14965
15609
 
14966
- 1. Call hyv_welcome or hyv_list_free_tools so the user knows what is free.
14967
- 2. Call hyv_profiles to list voice profiles (cached local or account).
14968
- 3. For writing: hyv_scan (fast local) or hyv_analyze (hybrid when paid) \u2192 hyv_fix \u2192 hyv_clean or hyv_rewrite \u2192 hyv_validate.
14969
- 4. After the user accepts a big edit, suggest hyv reinforce --last (CLI) to strengthen their profile.
14970
- 5. Summarize: mode (free local vs paid profile), profiles available, next step (hyv init if no profile).
15610
+ 1. New user \u2192 hyv_welcome (or step 1\u20134). Step 2: hyv_welcome mode=extract_prompt to build profile from chat.
15611
+ 2. Save profile \u2192 user runs hyv import <name> file.md (or you draft markdown for them).
15612
+ 3. hyv_profiles \u2014 list local/account profiles.
15613
+ 4. Writing: hyv_scan \u2192 hyv_fix / hyv_rewrite \u2192 hyv_validate.
15614
+ 5. Ready to pay \u2192 hyv init + hyv plan --upgrade (signup last, not first).
14971
15615
 
14972
15616
  Keep the answer compact.`;
14973
15617
  var TOOL_HANDLERS = {
14974
- hyv_welcome: () => toolWelcome(),
15618
+ hyv_welcome: toolWelcome,
14975
15619
  hyv_list_free_tools: () => toolListFreeTools(),
14976
15620
  hyv_demo: () => toolDemo(),
14977
15621
  hyv_rewrite: toolRewrite,
@@ -15065,7 +15709,7 @@ async function handleRequest(line) {
15065
15709
  }
15066
15710
  async function startMcpServer() {
15067
15711
  const access = await getAccessState().catch(() => null);
15068
- if (fs21.existsSync(VOICE_MD)) {
15712
+ if (fs22.existsSync(VOICE_MD)) {
15069
15713
  mcpLog("info", `voice profile: ${VOICE_MD}`);
15070
15714
  } else {
15071
15715
  mcpLog("info", "free local engine ready \u2014 no voice profile yet");
@@ -15097,142 +15741,142 @@ async function startMcpServer() {
15097
15741
  }
15098
15742
 
15099
15743
  // src/lib/mcp-setup.ts
15100
- var import_chalk30 = __toESM(require_source());
15101
- var fs22 = __toESM(require("fs"));
15102
- var path22 = __toESM(require("path"));
15103
- var os8 = __toESM(require("os"));
15744
+ var import_chalk31 = __toESM(require_source());
15745
+ var fs23 = __toESM(require("fs"));
15746
+ var path23 = __toESM(require("path"));
15747
+ var os9 = __toESM(require("os"));
15104
15748
  init_version();
15105
- var HOME2 = os8.homedir();
15749
+ var HOME2 = os9.homedir();
15106
15750
  var IS_WIN2 = process.platform === "win32";
15107
15751
  function claudeDesktopConfigPath() {
15108
15752
  if (IS_WIN2)
15109
- return path22.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
15753
+ return path23.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
15110
15754
  if (process.platform === "linux")
15111
- return path22.join(HOME2, ".config", "Claude", "claude_desktop_config.json");
15112
- return path22.join(HOME2, "Library", "Application Support", "Claude", "claude_desktop_config.json");
15755
+ return path23.join(HOME2, ".config", "Claude", "claude_desktop_config.json");
15756
+ return path23.join(HOME2, "Library", "Application Support", "Claude", "claude_desktop_config.json");
15113
15757
  }
15114
15758
  function printMcpSetup() {
15115
- console.log(import_chalk30.default.bold("\nhold your voice \u2014 mcp setup\n"));
15116
- console.log(import_chalk30.default.dim(` ${getEngineLabel()}
15759
+ console.log(import_chalk31.default.bold("\nhold your voice \u2014 mcp setup\n"));
15760
+ console.log(import_chalk31.default.dim(` ${getEngineLabel()}
15117
15761
  `));
15118
- console.log(import_chalk30.default.bold("Claude Desktop"));
15119
- console.log(import_chalk30.default.dim(" Add to claude_desktop_config.json \u2192 mcpServers.hyv:"));
15120
- console.log(import_chalk30.default.cyan(` ${mcpServerSnippet().split("\n").join("\n ")}`));
15121
- console.log(import_chalk30.default.dim(` Config: ${claudeDesktopConfigPath()}
15762
+ console.log(import_chalk31.default.bold("Claude Desktop"));
15763
+ console.log(import_chalk31.default.dim(" Add to claude_desktop_config.json \u2192 mcpServers.hyv:"));
15764
+ console.log(import_chalk31.default.cyan(` ${mcpServerSnippet().split("\n").join("\n ")}`));
15765
+ console.log(import_chalk31.default.dim(` Config: ${claudeDesktopConfigPath()}
15122
15766
  `));
15123
- console.log(import_chalk30.default.bold("Cursor"));
15124
- console.log(import_chalk30.default.dim(" MCP: ~/.cursor/mcp.json \u2192 mcpServers.hyv (same JSON as above)"));
15125
- console.log(import_chalk30.default.dim(" Rule: ~/.cursor/rules/hyv.mdc (alwaysApply \u2014 auto via postinstall)\n"));
15126
- console.log(import_chalk30.default.bold("Claude Code"));
15127
- console.log(import_chalk30.default.dim(" Command: ~/.claude/commands/hyv.md"));
15128
- console.log(import_chalk30.default.dim(" Skill: ~/.claude/skills/hold-your-voice/SKILL.md\n"));
15129
- console.log(import_chalk30.default.bold("Windsurf"));
15130
- console.log(import_chalk30.default.dim(" Rule: ~/.windsurf/rules/hyv.md (trigger: always_on)\n"));
15131
- console.log(import_chalk30.default.bold("Codex"));
15132
- console.log(import_chalk30.default.dim(" Instructions: ~/.codex/AGENTS.md (merged on install)\n"));
15133
- console.log(import_chalk30.default.bold("Command Code"));
15134
- console.log(import_chalk30.default.dim(" Skill: ~/.commandcode/skills/hyv/SKILL.md\n"));
15135
- console.log(import_chalk30.default.bold("ChatGPT"));
15136
- console.log(import_chalk30.default.dim(" hyv mcp --setup-chatgpt\n"));
15137
- console.log(import_chalk30.default.bold("Auto-configure"));
15138
- console.log(import_chalk30.default.dim(" hyv doctor --fix-agents"));
15139
- console.log(import_chalk30.default.dim(" HYV_AUTO_CONFIGURE_AGENTS=0 npm i -g @holdyourvoice/hyv (skip)\n"));
15140
- console.log(import_chalk30.default.bold("Verify"));
15141
- console.log(import_chalk30.default.dim(" hyv mcp --test"));
15142
- console.log(import_chalk30.default.dim(" HYV_TELEMETRY=1 hyv mcp (optional usage logging to ~/.hyv/telemetry/)\n"));
15767
+ console.log(import_chalk31.default.bold("Cursor"));
15768
+ console.log(import_chalk31.default.dim(" MCP: ~/.cursor/mcp.json \u2192 mcpServers.hyv (same JSON as above)"));
15769
+ console.log(import_chalk31.default.dim(" Rule: ~/.cursor/rules/hyv.mdc (alwaysApply \u2014 auto via postinstall)\n"));
15770
+ console.log(import_chalk31.default.bold("Claude Code"));
15771
+ console.log(import_chalk31.default.dim(" Command: ~/.claude/commands/hyv.md"));
15772
+ console.log(import_chalk31.default.dim(" Skill: ~/.claude/skills/hold-your-voice/SKILL.md\n"));
15773
+ console.log(import_chalk31.default.bold("Windsurf"));
15774
+ console.log(import_chalk31.default.dim(" Rule: ~/.windsurf/rules/hyv.md (trigger: always_on)\n"));
15775
+ console.log(import_chalk31.default.bold("Codex"));
15776
+ console.log(import_chalk31.default.dim(" Instructions: ~/.codex/AGENTS.md (merged on install)\n"));
15777
+ console.log(import_chalk31.default.bold("Command Code"));
15778
+ console.log(import_chalk31.default.dim(" Skill: ~/.commandcode/skills/hyv/SKILL.md\n"));
15779
+ console.log(import_chalk31.default.bold("ChatGPT"));
15780
+ console.log(import_chalk31.default.dim(" hyv mcp --setup-chatgpt\n"));
15781
+ console.log(import_chalk31.default.bold("Auto-configure"));
15782
+ console.log(import_chalk31.default.dim(" hyv doctor --fix-agents"));
15783
+ console.log(import_chalk31.default.dim(" HYV_AUTO_CONFIGURE_AGENTS=0 npm i -g @holdyourvoice/hyv (skip)\n"));
15784
+ console.log(import_chalk31.default.bold("Verify"));
15785
+ console.log(import_chalk31.default.dim(" hyv mcp --test"));
15786
+ console.log(import_chalk31.default.dim(" HYV_TELEMETRY=1 hyv mcp (optional usage logging to ~/.hyv/telemetry/)\n"));
15143
15787
  }
15144
15788
  async function runMcpSelfTest() {
15145
- console.log(import_chalk30.default.bold("\nhold your voice \u2014 mcp self-test\n"));
15146
- console.log(import_chalk30.default.dim(` ${getEngineLabel()}
15789
+ console.log(import_chalk31.default.bold("\nhold your voice \u2014 mcp self-test\n"));
15790
+ console.log(import_chalk31.default.dim(` ${getEngineLabel()}
15147
15791
  `));
15148
15792
  let passed = 0;
15149
15793
  let failed = 0;
15150
15794
  const tools = getMcpToolNames();
15151
15795
  if (tools.length >= 10) {
15152
- console.log(import_chalk30.default.green(` \u2713 ${tools.length} MCP tools registered`));
15796
+ console.log(import_chalk31.default.green(` \u2713 ${tools.length} MCP tools registered`));
15153
15797
  passed++;
15154
15798
  } else {
15155
- console.log(import_chalk30.default.red(` \u2717 expected 10+ tools, got ${tools.length}`));
15799
+ console.log(import_chalk31.default.red(` \u2717 expected 10+ tools, got ${tools.length}`));
15156
15800
  failed++;
15157
15801
  }
15158
15802
  const required = ["hyv_welcome", "hyv_scan", "hyv_analyze", "hyv_clean", "hyv_validate", "hyv_list_free_tools"];
15159
15803
  for (const name of required) {
15160
15804
  if (tools.includes(name)) {
15161
- console.log(import_chalk30.default.green(` \u2713 tool: ${name}`));
15805
+ console.log(import_chalk31.default.green(` \u2713 tool: ${name}`));
15162
15806
  passed++;
15163
15807
  } else {
15164
- console.log(import_chalk30.default.red(` \u2717 missing tool: ${name}`));
15808
+ console.log(import_chalk31.default.red(` \u2717 missing tool: ${name}`));
15165
15809
  failed++;
15166
15810
  }
15167
15811
  }
15168
15812
  try {
15169
15813
  const welcome = await invokeMcpTool("hyv_welcome", {});
15170
15814
  if (welcome.includes("Hold Your Voice") || welcome.includes("hold your voice")) {
15171
- console.log(import_chalk30.default.green(" \u2713 hyv_welcome responds"));
15815
+ console.log(import_chalk31.default.green(" \u2713 hyv_welcome responds"));
15172
15816
  passed++;
15173
15817
  } else {
15174
- console.log(import_chalk30.default.red(" \u2717 hyv_welcome unexpected output"));
15818
+ console.log(import_chalk31.default.red(" \u2717 hyv_welcome unexpected output"));
15175
15819
  failed++;
15176
15820
  }
15177
15821
  } catch (e) {
15178
- console.log(import_chalk30.default.red(` \u2717 hyv_welcome failed: ${e.message}`));
15822
+ console.log(import_chalk31.default.red(` \u2717 hyv_welcome failed: ${e.message}`));
15179
15823
  failed++;
15180
15824
  }
15181
15825
  try {
15182
15826
  const demo = await invokeMcpTool("hyv_demo", {});
15183
15827
  if (demo.includes("Score") || demo.includes("issues")) {
15184
- console.log(import_chalk30.default.green(" \u2713 hyv_demo pipeline works"));
15828
+ console.log(import_chalk31.default.green(" \u2713 hyv_demo pipeline works"));
15185
15829
  passed++;
15186
15830
  } else {
15187
- console.log(import_chalk30.default.red(" \u2717 hyv_demo unexpected output"));
15831
+ console.log(import_chalk31.default.red(" \u2717 hyv_demo unexpected output"));
15188
15832
  failed++;
15189
15833
  }
15190
15834
  } catch (e) {
15191
- console.log(import_chalk30.default.red(` \u2717 hyv_demo failed: ${e.message}`));
15835
+ console.log(import_chalk31.default.red(` \u2717 hyv_demo failed: ${e.message}`));
15192
15836
  failed++;
15193
15837
  }
15194
- const voiceMd = path22.join(HOME2, ".hyv", "voice.md");
15195
- if (fs22.existsSync(voiceMd)) {
15196
- console.log(import_chalk30.default.green(" \u2713 voice profile found"));
15838
+ const voiceMd = path23.join(HOME2, ".hyv", "voice.md");
15839
+ if (fs23.existsSync(voiceMd)) {
15840
+ console.log(import_chalk31.default.green(" \u2713 voice profile found"));
15197
15841
  passed++;
15198
15842
  } else {
15199
- console.log(import_chalk30.default.dim(" - no voice.md (free local engine still works)"));
15843
+ console.log(import_chalk31.default.dim(" - no voice.md (free local engine still works)"));
15200
15844
  }
15201
15845
  if (getDemoText().length > 100) {
15202
- console.log(import_chalk30.default.green(" \u2713 demo content available"));
15846
+ console.log(import_chalk31.default.green(" \u2713 demo content available"));
15203
15847
  passed++;
15204
15848
  } else {
15205
- console.log(import_chalk30.default.red(" \u2717 demo content missing"));
15849
+ console.log(import_chalk31.default.red(" \u2717 demo content missing"));
15206
15850
  failed++;
15207
15851
  }
15208
15852
  try {
15209
15853
  const stdio = await testMcpStdioSubprocess();
15210
15854
  if (stdio.ok && (stdio.toolCount || 0) >= 10) {
15211
- console.log(import_chalk30.default.green(` \u2713 stdio MCP subprocess responds (${stdio.toolCount} tools)`));
15855
+ console.log(import_chalk31.default.green(` \u2713 stdio MCP subprocess responds (${stdio.toolCount} tools)`));
15212
15856
  passed++;
15213
15857
  } else if (stdio.ok) {
15214
- console.log(import_chalk30.default.yellow(` ! stdio MCP subprocess ok but only ${stdio.toolCount || 0} tools`));
15858
+ console.log(import_chalk31.default.yellow(` ! stdio MCP subprocess ok but only ${stdio.toolCount || 0} tools`));
15215
15859
  failed++;
15216
15860
  } else {
15217
- console.log(import_chalk30.default.red(" \u2717 stdio MCP subprocess failed"));
15861
+ console.log(import_chalk31.default.red(" \u2717 stdio MCP subprocess failed"));
15218
15862
  failed++;
15219
15863
  }
15220
15864
  } catch (e) {
15221
- console.log(import_chalk30.default.red(` \u2717 stdio MCP subprocess failed: ${e.message}`));
15865
+ console.log(import_chalk31.default.red(` \u2717 stdio MCP subprocess failed: ${e.message}`));
15222
15866
  failed++;
15223
15867
  }
15224
15868
  console.log("");
15225
15869
  if (failed === 0) {
15226
- console.log(import_chalk30.default.green(`\u2713 all checks passed (${passed})`));
15227
- console.log(import_chalk30.default.dim("\nStart server: hyv mcp\n"));
15870
+ console.log(import_chalk31.default.green(`\u2713 all checks passed (${passed})`));
15871
+ console.log(import_chalk31.default.dim("\nStart server: hyv mcp\n"));
15228
15872
  return true;
15229
15873
  }
15230
- console.log(import_chalk30.default.yellow(`! ${failed} check(s) failed, ${passed} passed`));
15874
+ console.log(import_chalk31.default.yellow(`! ${failed} check(s) failed, ${passed} passed`));
15231
15875
  return false;
15232
15876
  }
15233
15877
 
15234
15878
  // src/commands/export.ts
15235
- var fs23 = __toESM(require("fs"));
15879
+ var fs24 = __toESM(require("fs"));
15236
15880
  init_api();
15237
15881
  var FORMATS = {
15238
15882
  claude: {
@@ -15319,7 +15963,7 @@ async function exportCommand(format, opts) {
15319
15963
  const prompt = fmt.wrap(detail.profile, detail.body);
15320
15964
  if (opts.output) {
15321
15965
  const outFile = opts.output.replace("{name}", profile.slug || profile.name);
15322
- fs23.writeFileSync(outFile, prompt);
15966
+ fs24.writeFileSync(outFile, prompt);
15323
15967
  results.push({ profile: profile.name, file: outFile });
15324
15968
  } else {
15325
15969
  results.push({ profile: profile.name, prompt });
@@ -15356,13 +16000,13 @@ async function exportCommand(format, opts) {
15356
16000
  // src/index.ts
15357
16001
  init_access();
15358
16002
  init_welcome();
15359
- var fs24 = __toESM(require("fs"));
15360
- var path23 = __toESM(require("path"));
16003
+ var fs25 = __toESM(require("fs"));
16004
+ var path24 = __toESM(require("path"));
15361
16005
  var program2 = new Command();
15362
- var pkgPath2 = path23.resolve(__dirname, "..", "package.json");
16006
+ var pkgPath2 = path24.resolve(__dirname, "..", "package.json");
15363
16007
  var pkgVersion2 = (() => {
15364
16008
  try {
15365
- return JSON.parse(fs24.readFileSync(pkgPath2, "utf-8")).version;
16009
+ return JSON.parse(fs25.readFileSync(pkgPath2, "utf-8")).version;
15366
16010
  } catch {
15367
16011
  return "2.7.1";
15368
16012
  }
@@ -15406,15 +16050,15 @@ program2.command("mcp").description("Start MCP server (for Claude Desktop and ot
15406
16050
  return;
15407
16051
  }
15408
16052
  if (opts.setupChatgpt) {
15409
- console.log(import_chalk31.default.bold("\nhold your voice \u2014 chatgpt setup\n"));
16053
+ console.log(import_chalk32.default.bold("\nhold your voice \u2014 chatgpt setup\n"));
15410
16054
  console.log("To connect HYV to ChatGPT:");
15411
- console.log(import_chalk31.default.dim(" 1. Go to ") + import_chalk31.default.cyan("https://chatgpt.com/#settings/Connectors"));
15412
- console.log(import_chalk31.default.dim(" 2. Add a new connector"));
15413
- console.log(import_chalk31.default.dim(" 3. For local MCP, use: ") + import_chalk31.default.cyan("hyv mcp"));
15414
- console.log(import_chalk31.default.dim(" 4. ChatGPT Desktop supports stdio MCP servers"));
16055
+ console.log(import_chalk32.default.dim(" 1. Go to ") + import_chalk32.default.cyan("https://chatgpt.com/#settings/Connectors"));
16056
+ console.log(import_chalk32.default.dim(" 2. Add a new connector"));
16057
+ console.log(import_chalk32.default.dim(" 3. For local MCP, use: ") + import_chalk32.default.cyan("hyv mcp"));
16058
+ console.log(import_chalk32.default.dim(" 4. ChatGPT Desktop supports stdio MCP servers"));
15415
16059
  console.log("");
15416
- console.log(import_chalk31.default.dim("Note: The remote HTTP MCP endpoint is not yet available."));
15417
- console.log(import_chalk31.default.dim("Use the local stdio MCP server with Claude Desktop or Claude Code instead."));
16060
+ console.log(import_chalk32.default.dim("Note: The remote HTTP MCP endpoint is not yet available."));
16061
+ console.log(import_chalk32.default.dim("Use the local stdio MCP server with Claude Desktop or Claude Code instead."));
15418
16062
  return;
15419
16063
  }
15420
16064
  startMcpServer();
@@ -15424,9 +16068,8 @@ program2.command("export").description("Export voice profile for LLMs").argument
15424
16068
  });
15425
16069
  var args = process.argv.slice(2);
15426
16070
  if (args.length === 0) {
15427
- const firstRun = !hasCompletedOnboarding();
15428
- printWelcome({ condensed: !firstRun });
15429
- if (firstRun)
16071
+ printWelcome({ condensed: true });
16072
+ if (!hasCompletedOnboarding())
15430
16073
  markOnboardingComplete(pkgVersion2);
15431
16074
  process.exit(0);
15432
16075
  }