@holdyourvoice/hyv 2.8.10 → 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;
@@ -4703,6 +4737,10 @@ function ensureHyvDir() {
4703
4737
  }
4704
4738
  }
4705
4739
  }
4740
+ function writeSecureFile(filePath, content) {
4741
+ ensureHyvDir();
4742
+ fs.writeFileSync(filePath, content, { mode: 384 });
4743
+ }
4706
4744
  function appendSecureLine(filePath, line, dir) {
4707
4745
  if (dir) {
4708
4746
  if (!fs.existsSync(dir))
@@ -4741,6 +4779,11 @@ function writeAuth(auth) {
4741
4779
  ensureHyvDir();
4742
4780
  fs.writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), { mode: 384 });
4743
4781
  }
4782
+ function clearAuth() {
4783
+ if (fs.existsSync(AUTH_FILE)) {
4784
+ fs.unlinkSync(AUTH_FILE);
4785
+ }
4786
+ }
4744
4787
  function readConfig() {
4745
4788
  try {
4746
4789
  if (!fs.existsSync(CONFIG_FILE))
@@ -4811,6 +4854,17 @@ function queueSignal(signal) {
4811
4854
  const filePath = path.join(QUEUE_DIR, `${id}.json`);
4812
4855
  fs.writeFileSync(filePath, JSON.stringify(signal, null, 2), { mode: 384 });
4813
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
+ }
4814
4868
  function saveLastEditSession(session) {
4815
4869
  ensureHyvDir();
4816
4870
  const data = { ...session, saved_at: (/* @__PURE__ */ new Date()).toISOString() };
@@ -4856,11 +4910,11 @@ var init_config = __esm({
4856
4910
  var require_is_docker = __commonJS({
4857
4911
  "node_modules/is-docker/index.js"(exports2, module2) {
4858
4912
  "use strict";
4859
- var fs25 = require("fs");
4913
+ var fs26 = require("fs");
4860
4914
  var isDocker;
4861
4915
  function hasDockerEnv() {
4862
4916
  try {
4863
- fs25.statSync("/.dockerenv");
4917
+ fs26.statSync("/.dockerenv");
4864
4918
  return true;
4865
4919
  } catch (_) {
4866
4920
  return false;
@@ -4868,7 +4922,7 @@ var require_is_docker = __commonJS({
4868
4922
  }
4869
4923
  function hasDockerCGroup() {
4870
4924
  try {
4871
- return fs25.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4925
+ return fs26.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4872
4926
  } catch (_) {
4873
4927
  return false;
4874
4928
  }
@@ -4886,21 +4940,21 @@ var require_is_docker = __commonJS({
4886
4940
  var require_is_wsl = __commonJS({
4887
4941
  "node_modules/is-wsl/index.js"(exports2, module2) {
4888
4942
  "use strict";
4889
- var os9 = require("os");
4890
- var fs25 = require("fs");
4943
+ var os10 = require("os");
4944
+ var fs26 = require("fs");
4891
4945
  var isDocker = require_is_docker();
4892
4946
  var isWsl = () => {
4893
4947
  if (process.platform !== "linux") {
4894
4948
  return false;
4895
4949
  }
4896
- if (os9.release().toLowerCase().includes("microsoft")) {
4950
+ if (os10.release().toLowerCase().includes("microsoft")) {
4897
4951
  if (isDocker()) {
4898
4952
  return false;
4899
4953
  }
4900
4954
  return true;
4901
4955
  }
4902
4956
  try {
4903
- return fs25.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
4957
+ return fs26.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
4904
4958
  } catch (_) {
4905
4959
  return false;
4906
4960
  }
@@ -4939,17 +4993,17 @@ var require_define_lazy_prop = __commonJS({
4939
4993
  // node_modules/open/index.js
4940
4994
  var require_open = __commonJS({
4941
4995
  "node_modules/open/index.js"(exports2, module2) {
4942
- var path24 = require("path");
4996
+ var path25 = require("path");
4943
4997
  var childProcess = require("child_process");
4944
- var { promises: fs25, constants: fsConstants } = require("fs");
4998
+ var { promises: fs26, constants: fsConstants } = require("fs");
4945
4999
  var isWsl = require_is_wsl();
4946
5000
  var isDocker = require_is_docker();
4947
5001
  var defineLazyProperty = require_define_lazy_prop();
4948
- var localXdgOpenPath = path24.join(__dirname, "xdg-open");
5002
+ var localXdgOpenPath = path25.join(__dirname, "xdg-open");
4949
5003
  var { platform, arch } = process;
4950
5004
  var hasContainerEnv = () => {
4951
5005
  try {
4952
- fs25.statSync("/run/.containerenv");
5006
+ fs26.statSync("/run/.containerenv");
4953
5007
  return true;
4954
5008
  } catch {
4955
5009
  return false;
@@ -4972,14 +5026,14 @@ var require_open = __commonJS({
4972
5026
  const configFilePath = "/etc/wsl.conf";
4973
5027
  let isConfigFileExists = false;
4974
5028
  try {
4975
- await fs25.access(configFilePath, fsConstants.F_OK);
5029
+ await fs26.access(configFilePath, fsConstants.F_OK);
4976
5030
  isConfigFileExists = true;
4977
5031
  } catch {
4978
5032
  }
4979
5033
  if (!isConfigFileExists) {
4980
5034
  return defaultMountPoint;
4981
5035
  }
4982
- const configContent = await fs25.readFile(configFilePath, { encoding: "utf8" });
5036
+ const configContent = await fs26.readFile(configFilePath, { encoding: "utf8" });
4983
5037
  const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
4984
5038
  if (!configMountPoint) {
4985
5039
  return defaultMountPoint;
@@ -5079,7 +5133,7 @@ var require_open = __commonJS({
5079
5133
  const isBundled = !__dirname || __dirname === "/";
5080
5134
  let exeLocalXdgOpen = false;
5081
5135
  try {
5082
- await fs25.access(localXdgOpenPath, fsConstants.X_OK);
5136
+ await fs26.access(localXdgOpenPath, fsConstants.X_OK);
5083
5137
  exeLocalXdgOpen = true;
5084
5138
  } catch {
5085
5139
  }
@@ -5102,14 +5156,14 @@ var require_open = __commonJS({
5102
5156
  }
5103
5157
  const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
5104
5158
  if (options.wait) {
5105
- return new Promise((resolve14, reject) => {
5159
+ return new Promise((resolve15, reject) => {
5106
5160
  subprocess.once("error", reject);
5107
5161
  subprocess.once("close", (exitCode) => {
5108
5162
  if (!options.allowNonzeroExitCode && exitCode > 0) {
5109
5163
  reject(new Error(`Exited with code ${exitCode}`));
5110
5164
  return;
5111
5165
  }
5112
- resolve14(subprocess);
5166
+ resolve15(subprocess);
5113
5167
  });
5114
5168
  });
5115
5169
  }
@@ -5240,16 +5294,16 @@ function compareSemver(a, b) {
5240
5294
  return 0;
5241
5295
  }
5242
5296
  function fetchLatestNpmVersion(timeoutMs = 8e3) {
5243
- return new Promise((resolve14) => {
5297
+ return new Promise((resolve15) => {
5244
5298
  (0, import_child_process.execFile)(
5245
5299
  "npm",
5246
5300
  ["view", "@holdyourvoice/hyv", "version"],
5247
5301
  { timeout: timeoutMs, encoding: "utf-8" },
5248
5302
  (err, stdout) => {
5249
5303
  if (err || !stdout?.trim())
5250
- resolve14(null);
5304
+ resolve15(null);
5251
5305
  else
5252
- resolve14(stdout.trim());
5306
+ resolve15(stdout.trim());
5253
5307
  }
5254
5308
  );
5255
5309
  });
@@ -5313,7 +5367,7 @@ function escapeHtml(value) {
5313
5367
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5314
5368
  }
5315
5369
  function request(url, options = {}) {
5316
- return new Promise((resolve14, reject) => {
5370
+ return new Promise((resolve15, reject) => {
5317
5371
  const urlObj = new URL(url);
5318
5372
  const isHttps = urlObj.protocol === "https:";
5319
5373
  const client = isHttps ? https : http;
@@ -5334,9 +5388,9 @@ function request(url, options = {}) {
5334
5388
  res.on("data", (chunk) => data += chunk);
5335
5389
  res.on("end", () => {
5336
5390
  try {
5337
- resolve14({ status: res.statusCode || 0, data: JSON.parse(data) });
5391
+ resolve15({ status: res.statusCode || 0, data: JSON.parse(data) });
5338
5392
  } catch {
5339
- resolve14({ status: res.statusCode || 0, data });
5393
+ resolve15({ status: res.statusCode || 0, data });
5340
5394
  }
5341
5395
  });
5342
5396
  });
@@ -5394,9 +5448,9 @@ async function authenticateWithLicense(licenseKey) {
5394
5448
  }
5395
5449
  async function authenticateWithBrowser() {
5396
5450
  const server = http.createServer();
5397
- const port = await new Promise((resolve14) => {
5451
+ const port = await new Promise((resolve15) => {
5398
5452
  server.listen(0, "127.0.0.1", () => {
5399
- resolve14(server.address().port);
5453
+ resolve15(server.address().port);
5400
5454
  });
5401
5455
  });
5402
5456
  const redirectUri = `http://127.0.0.1:${port}/callback`;
@@ -5415,7 +5469,7 @@ async function authenticateWithBrowser() {
5415
5469
  }
5416
5470
  console.log(import_chalk.default.cyan("\nOpening browser for authentication..."));
5417
5471
  await (0, import_open.default)(assertSafeOAuthUrl(auth_url));
5418
- const authData = await new Promise((resolve14, reject) => {
5472
+ const authData = await new Promise((resolve15, reject) => {
5419
5473
  const timeout = setTimeout(() => {
5420
5474
  server.close();
5421
5475
  reject(new Error("Authentication timeout. Please try again."));
@@ -5463,7 +5517,7 @@ async function authenticateWithBrowser() {
5463
5517
  `);
5464
5518
  clearTimeout(timeout);
5465
5519
  server.close();
5466
- resolve14(data);
5520
+ resolve15(data);
5467
5521
  } catch (error) {
5468
5522
  res.writeHead(500, { "Content-Type": "text/html" });
5469
5523
  res.end(`<h1>Authentication failed</h1><p>${escapeHtml(error.message)}</p>`);
@@ -5695,11 +5749,11 @@ __export(api_exports, {
5695
5749
  apiPost: () => apiPost,
5696
5750
  requireSubscription: () => requireSubscription
5697
5751
  });
5698
- async function request2(method, path24, body) {
5752
+ async function request2(method, path25, body) {
5699
5753
  const token = await getValidToken();
5700
5754
  if (!token)
5701
5755
  throw new Error("you're not signed in yet. run: hyv init");
5702
- const url = `${API_BASE}${path24}`;
5756
+ const url = `${API_BASE}${path25}`;
5703
5757
  const opts = {
5704
5758
  method,
5705
5759
  headers: {
@@ -5724,11 +5778,11 @@ async function request2(method, path24, body) {
5724
5778
  }
5725
5779
  return res.json();
5726
5780
  }
5727
- function apiGet(path24) {
5728
- return request2("GET", path24);
5781
+ function apiGet(path25) {
5782
+ return request2("GET", path25);
5729
5783
  }
5730
- function apiPost(path24, body) {
5731
- return request2("POST", path24, body);
5784
+ function apiPost(path25, body) {
5785
+ return request2("POST", path25, body);
5732
5786
  }
5733
5787
  async function requireSubscription() {
5734
5788
  const { requirePaidFeature: requirePaidFeature2 } = await Promise.resolve().then(() => (init_access(), access_exports));
@@ -7300,34 +7354,940 @@ function runPipeline(text, profile, applyFixes = false) {
7300
7354
  };
7301
7355
  }
7302
7356
  function readText(source) {
7303
- const fs25 = require("fs");
7304
- const path24 = require("path");
7357
+ const fs26 = require("fs");
7358
+ const path25 = require("path");
7305
7359
  if (source === "-") {
7306
7360
  if (process.stdin.isTTY) {
7307
7361
  console.error("No input provided. Pipe content or specify a file.");
7308
7362
  process.exit(1);
7309
7363
  }
7310
- 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;
7311
8181
  }
7312
- const resolved = path24.resolve(source);
7313
- if (!fs25.existsSync(resolved)) {
7314
- console.error(`File not found: ${resolved}`);
7315
- 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;
7316
8207
  }
7317
- const stat = fs25.statSync(resolved);
7318
- if (stat.isDirectory()) {
7319
- console.error(`${resolved} is a directory, not a file.`);
7320
- 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;
7321
8254
  }
7322
- return { text: fs25.readFileSync(resolved, "utf-8"), path: resolved };
7323
8255
  }
7324
- var init_pipeline = __esm({
7325
- "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"() {
7326
8269
  "use strict";
7327
- init_signals();
7328
- init_classifier();
7329
- init_autofix();
7330
- 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");
7331
8291
  }
7332
8292
  });
7333
8293
 
@@ -7335,11 +8295,13 @@ var init_pipeline = __esm({
7335
8295
  var welcome_exports = {};
7336
8296
  __export(welcome_exports, {
7337
8297
  DEMO_TEXT: () => DEMO_TEXT,
7338
- ONBOARDING_STEPS: () => ONBOARDING_STEPS,
8298
+ FLOW_STEPS: () => FLOW_STEPS,
8299
+ WELCOME_TAGLINE: () => WELCOME_TAGLINE,
7339
8300
  buildWelcomeMessage: () => buildWelcomeMessage,
7340
8301
  formatCompactDemoResult: () => formatCompactDemoResult,
7341
8302
  formatDemoResult: () => formatDemoResult,
7342
8303
  formatFreeToolsList: () => formatFreeToolsList,
8304
+ plainFlowOverview: () => plainFlowOverview,
7343
8305
  printWelcome: () => printWelcome,
7344
8306
  runWelcomeDemo: () => runWelcomeDemo
7345
8307
  });
@@ -7351,9 +8313,7 @@ function runWelcomeDemo() {
7351
8313
  issueCount: signals.length,
7352
8314
  red: result.stats.red,
7353
8315
  yellow: result.stats.yellow,
7354
- topIssues: signals.slice(0, 6).map(
7355
- (s) => `line ${s.line}: ${s.id} \u2014 ${s.suggestion}`
7356
- )
8316
+ topIssues: signals.slice(0, 6).map((s) => `line ${s.line}: ${s.id} \u2014 ${s.suggestion}`)
7357
8317
  };
7358
8318
  }
7359
8319
  function formatDemoResult(demo) {
@@ -7373,7 +8333,7 @@ function formatCompactDemoResult(demo) {
7373
8333
  return m ? m[1] : i.split(" \u2014 ")[0];
7374
8334
  }).join(", ");
7375
8335
  const tail = tells ? ` \u2014 e.g. ${tells}` : "";
7376
- return `quick demo: ${demo.score}/100, ${demo.issueCount} issues${tail}`;
8336
+ return `sample scan: ${demo.score}/100, ${demo.issueCount} issues${tail}`;
7377
8337
  }
7378
8338
  function formatFreeToolsList() {
7379
8339
  const cli = FREE_CLI_COMMANDS.map((c2) => ` \u2022 ${c2}`).join("\n");
@@ -7392,54 +8352,48 @@ function formatFreeToolsList() {
7392
8352
  ].join("\n");
7393
8353
  }
7394
8354
  function buildWelcomeMessage(opts = {}) {
7395
- if (opts.condensed) {
7396
- return [
7397
- "hold your voice \u2014 make your AI agents sound exactly like you.",
7398
- "",
7399
- " hyv scan draft.md test a draft",
7400
- " hyv init build your voice",
7401
- " hyv mcp --setup connect cursor / claude",
7402
- "",
7403
- ` profiles + learning \u2192 ${PRICING_URL}`
7404
- ].join("\n");
8355
+ if (opts.guide) {
8356
+ return buildWelcomeGuide();
7405
8357
  }
7406
- const lines = [
7407
- "Hold Your Voice \u2014 make your AI agents sound exactly like you.",
7408
- "",
7409
- "4 steps:",
7410
- ""
7411
- ];
7412
- for (const step of ONBOARDING_STEPS) {
7413
- lines.push(` ${step.n}. ${step.title}`);
7414
- lines.push(` ${step.cmd}`);
7415
- lines.push(` ${step.hint}`);
7416
- lines.push("");
8358
+ if (opts.condensed) {
8359
+ return buildWelcomeHeader({ condensed: true });
7417
8360
  }
8361
+ const lines = [buildWelcomeHeader(), ""];
7418
8362
  if (!opts.skipDemo) {
7419
8363
  const demo = runWelcomeDemo();
7420
8364
  recordEvent("welcome_demo", { score: demo.score, issues: demo.issueCount });
7421
- lines.push(" try it now:");
8365
+ lines.push(" try it now");
7422
8366
  lines.push(` ${formatCompactDemoResult(demo)}`);
7423
8367
  lines.push(" hyv scan draft.md");
7424
8368
  lines.push("");
7425
8369
  }
7426
- lines.push("Ready for voice profiles that learn from every edit?");
7427
- lines.push(` hyv plan --upgrade \u2192 ${PRICING_URL}`);
7428
- 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}`);
7429
8374
  lines.push("");
7430
8375
  return lines.join("\n");
7431
8376
  }
7432
8377
  function printWelcome(opts = {}) {
7433
- 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
+ }
7434
8383
  console.log("\n" + buildWelcomeMessage(opts));
7435
8384
  }
7436
- var DEMO_TEXT, ONBOARDING_STEPS;
8385
+ function plainFlowOverview() {
8386
+ return formatFlowOverview().replace(/\u001b\[[0-9;]*m/g, "");
8387
+ }
8388
+ var DEMO_TEXT;
7437
8389
  var init_welcome = __esm({
7438
8390
  "src/lib/welcome.ts"() {
7439
8391
  "use strict";
7440
8392
  init_pipeline();
7441
8393
  init_free_paid();
7442
8394
  init_telemetry();
8395
+ init_welcome_flow();
8396
+ init_welcome_flow();
7443
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.
7444
8398
 
7445
8399
  Firstly, you need to harness the power of seamless integration. Moreover, this robust ecosystem will foster innovation and unlock new opportunities for growth.
@@ -7447,12 +8401,6 @@ Firstly, you need to harness the power of seamless integration. Moreover, this r
7447
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.
7448
8402
 
7449
8403
  In conclusion, the key to success is to empower your team with actionable insights and foster a culture of continuous improvement.`;
7450
- ONBOARDING_STEPS = [
7451
- { n: 1, title: "install", cmd: "npm i -g @holdyourvoice/hyv@latest", hint: "one command, works in every terminal and agent" },
7452
- { n: 2, title: "test a draft", cmd: "hyv scan draft.md", hint: "free local scan \u2014 flags ai tells offline" },
7453
- { n: 3, title: "build your voice", cmd: "hyv init", hint: "profile from your writing \u2014 unlocks rewrite + learning" },
7454
- { n: 4, title: "connect your agent", cmd: "hyv mcp --setup", hint: "cursor, claude code, codex \u2014 tools inside the chat" }
7455
- ];
7456
8404
  }
7457
8405
  });
7458
8406
 
@@ -8327,15 +9275,15 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
8327
9275
  g.minimatch.escape = vi.escape;
8328
9276
  g.minimatch.unescape = Ei.unescape;
8329
9277
  });
8330
- var fs25 = R((Wt) => {
9278
+ var fs26 = R((Wt) => {
8331
9279
  "use strict";
8332
9280
  Object.defineProperty(Wt, "__esModule", { value: true });
8333
9281
  Wt.LRUCache = void 0;
8334
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) => {
8335
9283
  typeof ge.emitWarning == "function" ? ge.emitWarning(n, t, e, s) : console.error(`[${e}] ${t}: ${n}`);
8336
- }, Lt = globalThis.AbortController, os9 = globalThis.AbortSignal;
9284
+ }, Lt = globalThis.AbortController, os10 = globalThis.AbortSignal;
8337
9285
  if (typeof Lt > "u") {
8338
- os9 = class {
9286
+ os10 = class {
8339
9287
  onabort;
8340
9288
  _onabort = [];
8341
9289
  reason;
@@ -8347,7 +9295,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
8347
9295
  constructor() {
8348
9296
  t();
8349
9297
  }
8350
- signal = new os9();
9298
+ signal = new os10();
8351
9299
  abort(e) {
8352
9300
  if (!this.signal.aborted) {
8353
9301
  this.signal.reason = e, this.signal.aborted = true;
@@ -9296,7 +10244,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
9296
10244
  };
9297
10245
  Object.defineProperty(_, "__esModule", { value: true });
9298
10246
  _.PathScurry = _.Path = _.PathScurryDarwin = _.PathScurryPosix = _.PathScurryWin32 = _.PathScurryBase = _.PathPosix = _.PathWin32 = _.PathBase = _.ChildrenCache = _.ResolveCache = void 0;
9299
- var Qt = fs25(), Yt = require("node:path"), yr = require("node:url"), pt = require("fs"), Sr = br(require("node:fs")), vr = pt.realpathSync.native, Ht = require("node:fs/promises"), bs = Oe(), mt = { lstatSync: pt.lstatSync, readdir: pt.readdir, readdirSync: pt.readdirSync, readlinkSync: pt.readlinkSync, realpathSync: vr, promises: { lstat: Ht.lstat, readdir: Ht.readdir, readlink: Ht.readlink, realpath: Ht.realpath } }, _s = (n) => !n || n === mt || n === Sr ? mt : { ...mt, ...n, promises: { ...mt.promises, ...n.promises || {} } }, Os = /^\\\\\?\\([a-z]:)\\?$/i, Er = (n) => n.replace(/\//g, "\\").replace(Os, "$1\\"), _r = /[\\\/]/, N = 0, xs = 1, Ts = 2, G = 4, Cs = 6, Rs = 8, Q = 10, As = 12, j = 15, dt = ~j, xe = 16, ys = 32, gt = 64, W = 128, Vt = 256, Xt = 512, Ss = gt | W | Xt, Or = 1023, Te = (n) => n.isFile() ? Rs : n.isDirectory() ? G : n.isSymbolicLink() ? Q : n.isCharacterDevice() ? Ts : n.isBlockDevice() ? Cs : n.isSocket() ? As : n.isFIFO() ? xs : N, vs = new Qt.LRUCache({ max: 2 ** 12 }), wt = (n) => {
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) => {
9300
10248
  let t = vs.get(n);
9301
10249
  if (t)
9302
10250
  return t;
@@ -10815,7 +11763,7 @@ var {
10815
11763
  } = import_index.default;
10816
11764
 
10817
11765
  // src/index.ts
10818
- var import_chalk31 = __toESM(require_source());
11766
+ var import_chalk32 = __toESM(require_source());
10819
11767
 
10820
11768
  // src/commands/init.ts
10821
11769
  var import_chalk4 = __toESM(require_source());
@@ -10964,7 +11912,7 @@ function registerInitCommand(program3) {
10964
11912
  const mcpResults = configureMcpForDesktop();
10965
11913
  const { printPaidUnlockReminder: printPaidUnlockReminder2 } = await Promise.resolve().then(() => (init_marketing_hints(), marketing_hints_exports));
10966
11914
  console.log("\nNext steps:");
10967
- 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"));
10968
11916
  console.log(import_chalk4.default.dim(" 2. Scan a draft: hyv scan draft.md"));
10969
11917
  console.log(import_chalk4.default.dim(" 3. Rewrite with voice: hyv rewrite draft.md"));
10970
11918
  printPaidUnlockReminder2();
@@ -11504,435 +12452,77 @@ function registerRewriteCommand(program3) {
11504
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) => {
11505
12453
  try {
11506
12454
  const profile = await loadProfileForCommand(options.profile, { allowServerFetch: true });
11507
- if (options.profile && !profile) {
11508
- await requirePaidFeature("premiumPrompts");
11509
- }
11510
- const { text: draftContent, path: draftPath } = readText(file);
11511
- const result = runPipeline(draftContent, profile, false);
11512
- const promptResult = buildRewritePrompt({
11513
- draftPath,
11514
- draftContent,
11515
- profileName: options.profile,
11516
- profile,
11517
- format: options.format,
11518
- constraints: options.constraints,
11519
- skipScan: options.scan === false
11520
- });
11521
- console.log(import_chalk9.default.dim(`
11522
- Draft: ${draftPath}`));
11523
- console.log(import_chalk9.default.dim(`Profile: ${promptResult.profileUsed || "none (using generic rules)"}`));
11524
- if (profile && hasRichProfile(profile)) {
11525
- console.log(import_chalk9.default.dim(` never-list: ${profile.never_list?.length || 0} learned: ${profile.learned_patterns?.length || 0}`));
11526
- }
11527
- console.log(import_chalk9.default.dim(`Issues found: ${result.stats.totalSignals}`));
11528
- console.log(import_chalk9.default.dim(`Score: ${result.score}/100`));
11529
- if (options.dryRun && profile) {
11530
- console.log(import_chalk9.default.bold("\n--- Profile injection preview ---\n"));
11531
- console.log(import_chalk9.default.dim(profile.body?.slice(0, 500) + (profile.body?.length > 500 ? "..." : "")));
11532
- console.log("");
11533
- }
11534
- if (result.stats.totalSignals === 0) {
11535
- console.log(import_chalk9.default.green("\n\u2713 No issues found. Draft looks clean!"));
11536
- return;
11537
- }
11538
- console.log(import_chalk9.default.bold("\nIssues:"));
11539
- for (const signal of result.signalMap.signals.slice(0, 10)) {
11540
- const sev = signal.severity === "red" ? import_chalk9.default.red("\u25CF") : import_chalk9.default.yellow("\u25CB");
11541
- const fix = signal.autoFixable ? import_chalk9.default.dim(" [auto-fixable]") : "";
11542
- console.log(` ${sev} line ${signal.line}: ${import_chalk9.default.dim(signal.id)} \u2014 ${signal.suggestion}${fix}`);
11543
- }
11544
- if (result.signalMap.signals.length > 10) {
11545
- console.log(import_chalk9.default.dim(` ... and ${result.signalMap.signals.length - 10} more`));
11546
- }
11547
- if (result.stats.autoFixed > 0) {
11548
- console.log(import_chalk9.default.green(`
11549
- ${result.stats.autoFixed} issues can be auto-fixed \u2014 run: hyv fix ${file}`));
11550
- }
11551
- if (options.output) {
11552
- const outputPath = path10.resolve(options.output);
11553
- fs9.writeFileSync(outputPath, promptResult.prompt);
11554
- console.log(import_chalk9.default.green(`
11555
- \u2713 Prompt written to ${outputPath}`));
11556
- } else {
11557
- console.log(import_chalk9.default.bold("\n--- Rewrite Prompt ---\n"));
11558
- console.log(promptResult.prompt);
11559
- console.log(import_chalk9.default.dim("\n--- End Prompt ---\n"));
11560
- console.log(import_chalk9.default.dim("Copy this prompt and paste it into your LLM."));
11561
- console.log(import_chalk9.default.dim("Or pipe directly: hyv rewrite draft.md | pbcopy"));
11562
- }
11563
- await maybeShowLimitedModeHint(!!profile);
11564
- } catch (error) {
11565
- console.error(import_chalk9.default.red(`Error: ${error.message}`));
11566
- process.exit(1);
11567
- }
11568
- });
11569
- }
11570
-
11571
- // src/commands/learning.ts
11572
- var import_chalk10 = __toESM(require_source());
11573
- var fs10 = __toESM(require("fs"));
11574
- var path11 = __toESM(require("path"));
11575
-
11576
- // src/lib/patterns.ts
11577
- var AI_OVERUSED2 = [
11578
- { id: "ai.delve", category: "ai-slop", severity: "red", regex: /\bdelve\b/gi, suggestion: "use a specific verb: dig, explore, look at" },
11579
- { id: "ai.leverage", category: "ai-slop", severity: "red", regex: /\bleverage\b/gi, suggestion: "use: use, apply, build on" },
11580
- { id: "ai.utilize", category: "ai-slop", severity: "red", regex: /\butilize\b/gi, suggestion: "use: use" },
11581
- { id: "ai.tapestry", category: "ai-slop", severity: "red", regex: /\btapestry\b/gi, suggestion: "be specific about what you mean" },
11582
- { id: "ai.holistic", category: "ai-slop", severity: "red", regex: /\bholistic\b/gi, suggestion: "describe the actual approach" },
11583
- { id: "ai.robust", category: "ai-slop", severity: "yellow", regex: /\brobust\b/gi, suggestion: "say what actually makes it strong" },
11584
- { id: "ai.pivotal", category: "ai-slop", severity: "yellow", regex: /\bpivotal\b/gi, suggestion: "say why it matters specifically" },
11585
- { id: "ai.foster", category: "ai-slop", severity: "yellow", regex: /\bfoster\b/gi, suggestion: "use: build, grow, encourage, support" },
11586
- { id: "ai.harness", category: "ai-slop", severity: "yellow", regex: /\bharness\b/gi, suggestion: "use: use, apply, work with" },
11587
- { id: "ai.illuminate", category: "ai-slop", severity: "yellow", regex: /\billuminate\b/gi, suggestion: "use: show, explain, highlight" },
11588
- { 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" },
11589
- { id: "ai.fast-paced", category: "ai-slop", severity: "red", regex: /\bfast[\s-]paced\b/gi, suggestion: "cut this \u2014 every industry says this" },
11590
- { id: "ai.game-changer", category: "ai-slop", severity: "red", regex: /\bgame[\s-]changer\b/gi, suggestion: "explain what actually changed" },
11591
- { id: "ai.paradigm", category: "ai-slop", severity: "red", regex: /\bparadigm\b/gi, suggestion: "describe the actual shift" },
11592
- { id: "ai.synergy", category: "ai-slop", severity: "red", regex: /\bsynergy\b/gi, suggestion: "describe what works together and why" },
11593
- { id: "ai.ecosystem", category: "ai-slop", severity: "yellow", regex: /\becosystem\b/gi, suggestion: "name the specific tools/partners/platforms" },
11594
- { id: "ai.seamless", category: "ai-slop", severity: "yellow", regex: /\bseamless\b/gi, suggestion: "describe how it actually works" },
11595
- { id: "ai.actionable", category: "ai-slop", severity: "yellow", regex: /\bactionable\b/gi, suggestion: "just give the action, don't label it" },
11596
- { id: "ai.granular", category: "ai-slop", severity: "yellow", regex: /\bgranular\b/gi, suggestion: "say: specific, detailed, or name the level" },
11597
- { id: "ai.impactful", category: "ai-slop", severity: "yellow", regex: /\bimpactful\b/gi, suggestion: "describe the actual impact" },
11598
- { id: "ai.landscape", category: "ai-slop", severity: "red", regex: /\blandscape\b/gi, suggestion: "name the specific market/field/area" },
11599
- { id: "ai.realm", category: "ai-slop", severity: "red", regex: /\brealm\b/gi, suggestion: "name the specific domain" },
11600
- { id: "ai.straightforward", category: "ai-slop", severity: "yellow", regex: /\bstraightforward\b/gi, suggestion: "just explain it directly" }
11601
- ];
11602
- var FORMULAIC2 = [
11603
- { id: "formula.firstly", category: "ai-slop", severity: "yellow", regex: /\bfirstly\b/gi, suggestion: 'just start \u2014 "firstly" is filler' },
11604
- { id: "formula.secondly", category: "ai-slop", severity: "yellow", regex: /\bsecondly\b/gi, suggestion: 'just continue \u2014 "secondly" is filler' },
11605
- { id: "formula.lastly", category: "ai-slop", severity: "yellow", regex: /\blastly\b/gi, suggestion: 'just end \u2014 "lastly" is filler' },
11606
- { id: "formula.moreover", category: "ai-slop", severity: "yellow", regex: /\bmoreover\b/gi, suggestion: "just add the point" },
11607
- { id: "formula.furthermore", category: "ai-slop", severity: "yellow", regex: /\bfurthermore\b/gi, suggestion: "just add the point" },
11608
- { id: "formula.in-conclusion", category: "ai-slop", severity: "red", regex: /\bin conclusion\b/gi, suggestion: "just end. readers know it's the end." },
11609
- { id: "formula.in-summary", category: "ai-slop", severity: "red", regex: /\bin summary\b/gi, suggestion: "just summarize. the label is redundant." },
11610
- { 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." },
11611
- { 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" },
11612
- { 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" },
11613
- { id: "formula.it-goes-without", category: "ai-slop", severity: "yellow", regex: /\bit goes without saying\b/gi, suggestion: "then don't say it" },
11614
- { id: "formula.in-today", category: "ai-slop", severity: "red", regex: /\bin today'?s\b/gi, suggestion: "start with your actual point instead" },
11615
- { id: "formula.lets-dive", category: "ai-slop", severity: "red", regex: /\blet'?s (?:dive|jump|dig|delve)\b/gi, suggestion: "just start. no diving needed." },
11616
- { id: "formula.without-further", category: "ai-slop", severity: "red", regex: /\bwithout further ado\b/gi, suggestion: "cut this \u2014 just get to it" },
11617
- { id: "formula.its-worth-noting", category: "ai-slop", severity: "yellow", regex: /\bit'?s worth noting\b/gi, suggestion: "just note it directly" },
11618
- { id: "formula.moving-forward", category: "ai-slop", severity: "yellow", regex: /\bmoving forward\b/gi, suggestion: "just say what happens next" },
11619
- { 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" },
11620
- { id: "formula.what-makes-interesting", category: "ai-slop", severity: "yellow", regex: /\bwhat makes this particularly interesting\b/gi, suggestion: "just say the interesting thing" },
11621
- { id: "formula.implications", category: "ai-slop", severity: "yellow", regex: /\bthe implications here are\b/gi, suggestion: "state the implications directly" },
11622
- { id: "formula.in-other-words", category: "ai-slop", severity: "red", regex: /\bin other words\b/gi, suggestion: "say it once, well" }
11623
- ];
11624
- var HEDGING2 = [
11625
- { id: "hedge.some-might", category: "voice-drift", severity: "yellow", regex: /\bsome might say\b/gi, suggestion: "commit to the claim or drop it" },
11626
- { id: "hedge.arguably", category: "voice-drift", severity: "yellow", regex: /\barguably\b/gi, suggestion: "commit. say it or don't." },
11627
- { id: "hedge.worth-noting", category: "voice-drift", severity: "yellow", regex: /\bit'?s worth noting\b/gi, suggestion: "just note it directly" },
11628
- { id: "hedge.to-some-extent", category: "voice-drift", severity: "yellow", regex: /\bto some extent\b/gi, suggestion: "be specific about the extent" },
11629
- { id: "hedge.perhaps", category: "voice-drift", severity: "yellow", regex: /\bperhaps\b/gi, suggestion: "commit or cut" },
11630
- { id: "hedge.it-seems", category: "voice-drift", severity: "yellow", regex: /\bit seems\b/gi, suggestion: "state it directly" }
11631
- ];
11632
- var STRUCTURE2 = [
11633
- { 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" },
11634
- { 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" },
11635
- { id: "struct.in-order-to", category: "structure", severity: "yellow", regex: /\bin order to\b/gi, suggestion: 'just use "to"' },
11636
- { id: "struct.due-to-the-fact", category: "structure", severity: "yellow", regex: /\bdue to the fact that\b/gi, suggestion: 'use "because"' },
11637
- { id: "struct.for-the-purpose", category: "structure", severity: "yellow", regex: /\bfor the purpose of\b/gi, suggestion: 'use "to"' },
11638
- { id: "struct.which-is-another", category: "structure", severity: "red", regex: /\bwhich is another way of saying\b/gi, suggestion: "just say the thing directly" },
11639
- { 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" },
11640
- { 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" },
11641
- { 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" },
11642
- { id: "struct.lesson-setup", category: "structure", severity: "yellow", regex: /\bhere'?s what .{3,60} taught\b/gi, suggestion: "just share the lesson" },
11643
- // THE BIG ONE — antithesis negation pattern (Voice DNA: FATAL)
11644
- { 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" },
11645
- { 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' },
11646
- { 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" }
11647
- ];
11648
- var ENGAGEMENT_BAIT2 = [
11649
- { 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." },
11650
- { id: "bait.read-that-again", category: "engagement-bait", severity: "red", regex: /\bread that again\b/gi, suggestion: "if it needs repeating, repeat it yourself" },
11651
- { id: "bait.full-stop", category: "engagement-bait", severity: "red", regex: /\bfull stop\b/gi, suggestion: "the period already does this job" },
11652
- { id: "bait.this-changes-everything", category: "engagement-bait", severity: "red", regex: /\bthis changes everything\b/gi, suggestion: "prove it with specifics" },
11653
- { id: "bait.paying-attention", category: "engagement-bait", severity: "red", regex: /\bare you paying attention\b/gi, suggestion: "don't patronize the reader" },
11654
- { id: "bait.not-ready", category: "engagement-bait", severity: "red", regex: /\byou'?re not ready for this\b/gi, suggestion: "just deliver the content" }
11655
- ];
11656
- var AI_CRINGE2 = [
11657
- { id: "cringe.supercharge", category: "ai-cringe", severity: "red", regex: /\bsupercharge\b/gi, suggestion: "describe what it actually does" },
11658
- { id: "cringe.unlock", category: "ai-cringe", severity: "yellow", regex: /\bunlock\b/gi, suggestion: "describe what they get access to" },
11659
- { id: "cringe.future-proof", category: "ai-cringe", severity: "red", regex: /\bfuture[\s-]proof\b/gi, suggestion: "explain what specifically makes it durable" },
11660
- { id: "cringe.10x", category: "ai-cringe", severity: "red", regex: /\b10x\b/gi, suggestion: "use the actual numbers" },
11661
- { id: "cringe.ai-revolution", category: "ai-cringe", severity: "red", regex: /\bthe ai revolution\b/gi, suggestion: "describe the specific change" },
11662
- { 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" },
11663
- { 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." }
11664
- ];
11665
- var INSIDER_CLAIMS2 = [
11666
- { 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." },
11667
- { id: "insider.nobody-tells", category: "insider-claim", severity: "red", regex: /\bwhat nobody tells you\b/gi, suggestion: "just tell them." },
11668
- { id: "insider.most-people", category: "insider-claim", severity: "yellow", regex: /\bmost people don'?t realize\b/gi, suggestion: "just explain it. skip the setup." },
11669
- { 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" }
11670
- ];
11671
- var FORMATTING = [
11672
- { id: "format.em-dash", category: "formatting", severity: "red", regex: /\u2014/g, suggestion: "NO em dashes. use commas, periods, colons, semicolons, or parentheses." }
11673
- ];
11674
- var OGILVY2 = [
11675
- // Jargon — words that hide lack of understanding
11676
- { id: "ogilvy.jargon-utilize", category: "ai-slop", severity: "red", regex: /\butilize[sd]?\b/gi, suggestion: 'Ogilvy: use "use" instead' },
11677
- { id: "ogilvy.jargon-leverage", category: "ai-slop", severity: "red", regex: /\bleverage[ds]?\b/gi, suggestion: "Ogilvy: say what you actually mean" },
11678
- { id: "ogilvy.jargon-synergy", category: "ai-slop", severity: "red", regex: /\bsynerg(?:y|ies|istic)\b/gi, suggestion: "Ogilvy: describe what works together and why" },
11679
- { id: "ogilvy.jargon-bandwidth", category: "ai-slop", severity: "yellow", regex: /\bbandwidth\b/gi, suggestion: 'Ogilvy: say "time" or "capacity"' },
11680
- { id: "ogilvy.jargon-circle-back", category: "ai-slop", severity: "red", regex: /\bcircle back\b/gi, suggestion: 'Ogilvy: say "follow up" or "talk later"' },
11681
- { id: "ogilvy.jargon-low-hanging", category: "ai-slop", severity: "red", regex: /\blow-hanging fruit\b/gi, suggestion: "Ogilvy: name the specific easy win" },
11682
- { id: "ogilvy.jargon-move-the-needle", category: "ai-slop", severity: "red", regex: /\bmove the needle\b/gi, suggestion: "Ogilvy: describe the actual impact" },
11683
- { id: "ogilvy.jargon-touch-base", category: "ai-slop", severity: "red", regex: /\btouch base\b/gi, suggestion: 'Ogilvy: say "talk" or "meet"' },
11684
- { id: "ogilvy.jargon-take-it-offline", category: "ai-slop", severity: "red", regex: /\btake it offline\b/gi, suggestion: 'Ogilvy: say "discuss later"' },
11685
- { id: "ogilvy.jargon-deep-dive", category: "ai-slop", severity: "yellow", regex: /\bdeep dive\b/gi, suggestion: 'Ogilvy: say "look closely at" or "examine"' },
11686
- // Throat-clearing openers
11687
- { 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." },
11688
- { 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." }
11689
- ];
11690
- var ALL_PATTERNS = [
11691
- ...AI_OVERUSED2,
11692
- ...FORMULAIC2,
11693
- ...HEDGING2,
11694
- ...STRUCTURE2,
11695
- ...ENGAGEMENT_BAIT2,
11696
- ...AI_CRINGE2,
11697
- ...INSIDER_CLAIMS2,
11698
- ...FORMATTING,
11699
- ...OGILVY2
11700
- ];
11701
- function scanLine(line, lineNum, filePath) {
11702
- const findings = [];
11703
- for (const pat of ALL_PATTERNS) {
11704
- pat.regex.lastIndex = 0;
11705
- let match;
11706
- while ((match = pat.regex.exec(line)) !== null) {
11707
- findings.push({
11708
- file: filePath,
11709
- line: lineNum,
11710
- column: match.index + 1,
11711
- pattern: pat.id,
11712
- category: pat.category,
11713
- severity: pat.severity,
11714
- excerpt: highlightMatch2(line.trim(), match.index, match[0].length),
11715
- suggestion: pat.suggestion
11716
- });
11717
- }
11718
- }
11719
- return findings;
11720
- }
11721
- function highlightMatch2(line, start, len) {
11722
- const before = line.slice(0, start);
11723
- const match = line.slice(start, start + len);
11724
- const after = line.slice(start + len);
11725
- return `${before}\xAB${match}\xBB${after}`.slice(0, 120);
11726
- }
11727
-
11728
- // src/lib/scan.ts
11729
- var AI_PATTERN_RULES = [
11730
- {
11731
- id: "ai_antithesis",
11732
- pattern: /\b(?:it'?s|isn'?t|is\s+not)\s+not\s+just\b.{0,80}\b(?:it'?s|but)\b/i
11733
- },
11734
- {
11735
- id: "not_just_but",
11736
- pattern: /\bnot\s+just\b.{3,80}\bbut\s+(?:also\s+)?/i
11737
- },
11738
- {
11739
- id: "more_than_just",
11740
- pattern: /\bmore\s+than\s+just\b/i
11741
- },
11742
- {
11743
- id: "rhetorical_truth_setup",
11744
- pattern: /\b(?:the\s+)?(?:uncomfortable|hard|harsh|brutal|ugly|unsexy|real|honest)\s+(?:truth|reality)\b/i
11745
- },
11746
- {
11747
- id: "truth_is",
11748
- pattern: /\bthe\s+truth\s+is\b/i
11749
- },
11750
- {
11751
- id: "lesson_setup",
11752
- pattern: /\b(?:here'?s\s+)?what\s+.{3,80}\s+(?:taught|teaches)\s+(?:me|us|you|everyone)\b/i
11753
- },
11754
- {
11755
- id: "negation_cascade",
11756
- 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
11757
- },
11758
- {
11759
- id: "formulaic_connector",
11760
- 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
11761
- },
11762
- {
11763
- id: "ai_words",
11764
- 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
11765
- },
11766
- {
11767
- id: "em_dash",
11768
- pattern: /\u2014/
11769
- },
11770
- {
11771
- id: "signpost_claim",
11772
- 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
11773
- },
11774
- {
11775
- id: "generic_buyer_psychology",
11776
- pattern: /\bpeople\s+don'?t\s+just\s+buy\b|\bpeople\s+buy\s+the\s+feeling\b/i
11777
- },
11778
- {
11779
- id: "founder_cadence_restatement",
11780
- pattern: /\bwhich\s+is\s+another\s+way\s+of\s+saying\b|\bin\s+other\s+words\b/i
11781
- },
11782
- {
11783
- id: "founder_cadence_moment_becomes",
11784
- pattern: /\b(?:the\s+)?moment\b.{3,80}\bbecomes?\b|\bbecomes?\s+(?:dangerous|useful|interesting|real|obvious)\s+the\s+moment\b/i
11785
- },
11786
- {
11787
- id: "founder_cadence_same_better",
11788
- pattern: /\bsame\s+[^.!?\n]{1,35}[.!?]\s*(?:better|nicer|cleaner|calmer|safer)\s+[^.!?\n]{1,35}[.!?]?/i
11789
- }
11790
- ];
11791
- var ABSTRACT_STYLE_WORDS = /* @__PURE__ */ new Set([
11792
- "alignment",
11793
- "authenticity",
11794
- "awareness",
11795
- "clarity",
11796
- "confidence",
11797
- "consistency",
11798
- "differentiation",
11799
- "execution",
11800
- "framework",
11801
- "identity",
11802
- "messaging",
11803
- "narrative",
11804
- "personality",
11805
- "positioning",
11806
- "preference",
11807
- "presence",
11808
- "recall",
11809
- "relevance",
11810
- "resonance",
11811
- "signal",
11812
- "strategy",
11813
- "trust",
11814
- "utility",
11815
- "value"
11816
- ]);
11817
- var GENERIC_OPENERS = /^(?:most|many)\s+(?:brands|teams|people|founders|companies)\b/i;
11818
- var QUESTION_OPENER = /^(?:have you|do you|did you|what if|why do|how do)\b/i;
11819
- var LESSON_OPENER = /^(?:the most important thing|the key to|success is|if you want to|what i learned)\b/i;
11820
- function words(text) {
11821
- return (text || "").match(/[a-zA-Z][a-zA-Z0-9']*/g) || [];
11822
- }
11823
- function sentences(text) {
11824
- return (text || "").split(/(?<=[.!?])\s+|\n{2,}/).map((s) => s.trim()).filter((s) => words(s).length > 0);
11825
- }
11826
- function paragraphs(text) {
11827
- return (text || "").split(/\n\s*\n/).map((p) => p.trim()).filter((p) => words(p).length >= 6);
11828
- }
11829
- function lineStyleHits(line) {
11830
- const low = (line || "").trim().toLowerCase();
11831
- if (!low)
11832
- return [];
11833
- const hits = [];
11834
- const lineWords = low.match(/[a-z']+/g) || [];
11835
- const abstractCount = lineWords.filter((w) => ABSTRACT_STYLE_WORDS.has(w)).length;
11836
- if (abstractCount >= 3 && !/\b(?:for example|for instance|such as)\b|\d/i.test(low)) {
11837
- hits.push({
11838
- line: 0,
11839
- // Will be set by caller
11840
- rule: "abstract_noun_cluster",
11841
- phrase: line.trim().slice(0, 160)
11842
- });
11843
- }
11844
- if (GENERIC_OPENERS.test(low)) {
11845
- hits.push({
11846
- line: 0,
11847
- rule: "generic_opening_generalization",
11848
- phrase: line.trim().slice(0, 160)
11849
- });
11850
- }
11851
- if (QUESTION_OPENER.test(low)) {
11852
- hits.push({
11853
- line: 0,
11854
- rule: "voice_question_opener",
11855
- phrase: "opens with a question instead of a concrete observation"
11856
- });
11857
- }
11858
- if (LESSON_OPENER.test(low)) {
11859
- hits.push({
11860
- line: 0,
11861
- rule: "voice_lesson_opener",
11862
- phrase: "opens with a lesson or inspirational claim"
11863
- });
11864
- }
11865
- return hits;
11866
- }
11867
- function scanText(text) {
11868
- const hits = [];
11869
- const safeText = text || "";
11870
- for (const rule of AI_PATTERN_RULES) {
11871
- const regex = new RegExp(rule.pattern.source, rule.pattern.flags.replace("g", "") + "g");
11872
- let match;
11873
- let lastIndex = 0;
11874
- while ((match = regex.exec(safeText)) !== null) {
11875
- const snippet = match[0].trim();
11876
- if (!snippet) {
11877
- if (match.index === regex.lastIndex)
11878
- regex.lastIndex++;
11879
- continue;
12455
+ if (options.profile && !profile) {
12456
+ await requirePaidFeature("premiumPrompts");
11880
12457
  }
11881
- const lineNo = safeText.slice(0, match.index).split("\n").length;
11882
- hits.push({
11883
- line: lineNo,
11884
- rule: rule.id,
11885
- 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
11886
12468
  });
11887
- if (match.index === regex.lastIndex)
11888
- regex.lastIndex++;
11889
- }
11890
- }
11891
- const lines = safeText.split("\n");
11892
- for (let i = 0; i < lines.length; i++) {
11893
- const lineNum = i + 1;
11894
- const lineText = lines[i];
11895
- const patternHits = scanLine(lineText, lineNum, "inline");
11896
- for (const hit of patternHits) {
11897
- hits.push({ line: hit.line, rule: hit.pattern, severity: hit.severity, phrase: hit.excerpt || "", text: lineText.trim().slice(0, 120) });
11898
- }
11899
- const lineHits = lineStyleHits(lineText);
11900
- for (const hit of lineHits) {
11901
- hit.line = lineNum;
11902
- hit.text = lineText.trim().slice(0, 240);
11903
- hits.push(hit);
11904
- }
11905
- }
11906
- const sentenceHits = [];
11907
- for (let i = 0; i < lines.length; i++) {
11908
- const lineSentences = lines[i].split(/(?<=[.!?])\s+/);
11909
- for (const sentence of lineSentences) {
11910
- const wordCount = words(sentence).length;
11911
- if (wordCount > 0) {
11912
- 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}`));
11913
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);
11914
12515
  }
11915
- }
11916
- for (let i = 0; i < sentenceHits.length - 2; i++) {
11917
- const window = sentenceHits.slice(i, i + 3);
11918
- if (window.every((s) => s.wordCount <= 5)) {
11919
- hits.push({
11920
- line: window[0].line,
11921
- rule: "voice_staccato_triplet",
11922
- phrase: "three short sentences in a row reads like performance",
11923
- text: window[0].text
11924
- });
11925
- break;
11926
- }
11927
- }
11928
- return hits.sort((a, b) => {
11929
- if (a.line !== b.line)
11930
- return a.line - b.line;
11931
- return a.rule.localeCompare(b.rule);
11932
12516
  });
11933
12517
  }
11934
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
+
11935
12524
  // src/lib/diff.ts
12525
+ init_scan();
11936
12526
  function linesChanged(origLine, accLine) {
11937
12527
  return origLine.trim() !== accLine.trim();
11938
12528
  }
@@ -12250,11 +12840,12 @@ function printProfileImpact(impact, data) {
12250
12840
  }
12251
12841
 
12252
12842
  // src/commands/onboarding.ts
12253
- var import_chalk11 = __toESM(require_source());
12254
- var fs11 = __toESM(require("fs"));
12255
- var path12 = __toESM(require("path"));
12843
+ var import_chalk12 = __toESM(require_source());
12844
+ var fs12 = __toESM(require("fs"));
12845
+ var path13 = __toESM(require("path"));
12256
12846
  init_config();
12257
12847
  init_auth();
12848
+ init_scan();
12258
12849
  var UNIVERSAL_QUESTIONS = [
12259
12850
  { key: "tone", question: "How would you describe your tone? (e.g., casual, professional, witty, direct)" },
12260
12851
  { key: "audience", question: "Who is your audience? (e.g., founders, developers, marketers)" },
@@ -12281,7 +12872,7 @@ var INDUSTRY_QUESTIONS = {
12281
12872
  { key: "length", question: "Do you prefer short punchy content or longer pieces?" }
12282
12873
  ]
12283
12874
  };
12284
- function extractStats(samples) {
12875
+ function extractStats2(samples) {
12285
12876
  const combined = samples.map((s) => s.text).join("\n\n");
12286
12877
  const allWords = words(combined);
12287
12878
  const allSentences = sentences(combined);
@@ -12337,7 +12928,7 @@ function registerOnboardingCommands(program3) {
12337
12928
  await flashcardOnboarding(name, token);
12338
12929
  }
12339
12930
  } catch (error) {
12340
- console.error(import_chalk11.default.red(`
12931
+ console.error(import_chalk12.default.red(`
12341
12932
  Error: ${error.message}`));
12342
12933
  process.exit(1);
12343
12934
  }
@@ -12347,16 +12938,16 @@ Error: ${error.message}`));
12347
12938
  async function flashcardOnboarding(name, token) {
12348
12939
  const { printWelcome: printWelcome2 } = await Promise.resolve().then(() => (init_welcome(), welcome_exports));
12349
12940
  printWelcome2({ condensed: true, skipDemo: true });
12350
- console.log(import_chalk11.default.bold(`Creating voice profile: ${name}
12941
+ console.log(import_chalk12.default.bold(`Creating voice profile: ${name}
12351
12942
  `));
12352
- 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"));
12353
12944
  const answers = {};
12354
12945
  for (const q of UNIVERSAL_QUESTIONS) {
12355
12946
  const answer = await askQuestion(q.question);
12356
12947
  answers[q.key] = answer;
12357
12948
  }
12358
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";
12359
- console.log(import_chalk11.default.dim(`
12950
+ console.log(import_chalk12.default.dim(`
12360
12951
  Industry: ${industry}
12361
12952
  `));
12362
12953
  for (const q of INDUSTRY_QUESTIONS[industry]) {
@@ -12364,7 +12955,7 @@ Industry: ${industry}
12364
12955
  answers[q.key] = answer;
12365
12956
  }
12366
12957
  if (token) {
12367
- console.log(import_chalk11.default.cyan("\nGenerating profile on server..."));
12958
+ console.log(import_chalk12.default.cyan("\nGenerating profile on server..."));
12368
12959
  const response = await authenticatedRequest(
12369
12960
  cliApiUrl("/cli/profiles/new"),
12370
12961
  {
@@ -12381,60 +12972,60 @@ Industry: ${industry}
12381
12972
  const profileContent = data.content || generateLocalProfile(name, answers);
12382
12973
  ensureHyvDir();
12383
12974
  writeCachedProfile(name, profileContent);
12384
- console.log(import_chalk11.default.green(`
12975
+ console.log(import_chalk12.default.green(`
12385
12976
  \u2713 Profile created: ${name}`));
12386
- 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."));
12387
12978
  } else {
12388
- console.log(import_chalk11.default.yellow("\nServer unavailable. Creating local profile..."));
12979
+ console.log(import_chalk12.default.yellow("\nServer unavailable. Creating local profile..."));
12389
12980
  const profileContent = generateLocalProfile(name, answers);
12390
12981
  ensureHyvDir();
12391
12982
  writeCachedProfile(name, profileContent);
12392
- console.log(import_chalk11.default.green(`
12983
+ console.log(import_chalk12.default.green(`
12393
12984
  \u2713 Local profile created: ${name}`));
12394
12985
  }
12395
12986
  } else {
12396
- console.log(import_chalk11.default.yellow("\nNot authenticated. Creating local profile..."));
12987
+ console.log(import_chalk12.default.yellow("\nNot authenticated. Creating local profile..."));
12397
12988
  const profileContent = generateLocalProfile(name, answers);
12398
12989
  ensureHyvDir();
12399
12990
  writeCachedProfile(name, profileContent);
12400
- console.log(import_chalk11.default.green(`
12991
+ console.log(import_chalk12.default.green(`
12401
12992
  \u2713 Local profile created: ${name}`));
12402
12993
  }
12403
12994
  }
12404
12995
  async function createFromSamples(name, sampleDir, token) {
12405
- const dirPath = path12.resolve(sampleDir);
12406
- if (!fs11.existsSync(dirPath)) {
12996
+ const dirPath = path13.resolve(sampleDir);
12997
+ if (!fs12.existsSync(dirPath)) {
12407
12998
  throw new Error(`Directory not found: ${dirPath}`);
12408
12999
  }
12409
- console.log(import_chalk11.default.bold(`
13000
+ console.log(import_chalk12.default.bold(`
12410
13001
  Creating voice profile: ${name}`));
12411
- console.log(import_chalk11.default.dim(`Reading samples from: ${dirPath}
13002
+ console.log(import_chalk12.default.dim(`Reading samples from: ${dirPath}
12412
13003
  `));
12413
- 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));
12414
13005
  if (files.length === 0) {
12415
13006
  throw new Error("No .md or .txt files found in directory");
12416
13007
  }
12417
13008
  const samples = [];
12418
13009
  for (const file of files) {
12419
- const text = fs11.readFileSync(file, "utf-8");
13010
+ const text = fs12.readFileSync(file, "utf-8");
12420
13011
  if (text.trim().length > 0) {
12421
13012
  samples.push({ path: file, text });
12422
- 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)`));
12423
13014
  }
12424
13015
  }
12425
13016
  if (samples.length === 0) {
12426
13017
  throw new Error("No readable text found in files");
12427
13018
  }
12428
- const stats = extractStats(samples);
12429
- console.log(import_chalk11.default.dim(`
13019
+ const stats = extractStats2(samples);
13020
+ console.log(import_chalk12.default.dim(`
12430
13021
  Stats extracted:`));
12431
- console.log(import_chalk11.default.dim(` Words: ${stats.word_count}`));
12432
- console.log(import_chalk11.default.dim(` Sentences: ${stats.sentence_count}`));
12433
- console.log(import_chalk11.default.dim(` Avg sentence: ${stats.avg_sentence_length} words`));
12434
- console.log(import_chalk11.default.dim(` Case style: ${stats.case_style}`));
12435
- 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}`));
12436
13027
  if (token) {
12437
- console.log(import_chalk11.default.cyan("\nGenerating profile on server..."));
13028
+ console.log(import_chalk12.default.cyan("\nGenerating profile on server..."));
12438
13029
  const response = await authenticatedRequest(
12439
13030
  cliApiUrl("/cli/profiles/new"),
12440
13031
  {
@@ -12450,25 +13041,25 @@ Stats extracted:`));
12450
13041
  const data = response.data;
12451
13042
  ensureHyvDir();
12452
13043
  writeCachedProfile(name, data.content || generateLocalProfileFromStats(name, stats));
12453
- console.log(import_chalk11.default.green(`
13044
+ console.log(import_chalk12.default.green(`
12454
13045
  \u2713 Profile created: ${name}`));
12455
13046
  } else {
12456
- console.log(import_chalk11.default.yellow("\nServer unavailable. Creating local profile..."));
13047
+ console.log(import_chalk12.default.yellow("\nServer unavailable. Creating local profile..."));
12457
13048
  ensureHyvDir();
12458
13049
  writeCachedProfile(name, generateLocalProfileFromStats(name, stats));
12459
- console.log(import_chalk11.default.green(`
13050
+ console.log(import_chalk12.default.green(`
12460
13051
  \u2713 Local profile created: ${name}`));
12461
13052
  }
12462
13053
  } else {
12463
- console.log(import_chalk11.default.yellow("\nNot authenticated. Creating local profile..."));
13054
+ console.log(import_chalk12.default.yellow("\nNot authenticated. Creating local profile..."));
12464
13055
  ensureHyvDir();
12465
13056
  writeCachedProfile(name, generateLocalProfileFromStats(name, stats));
12466
- console.log(import_chalk11.default.green(`
13057
+ console.log(import_chalk12.default.green(`
12467
13058
  \u2713 Local profile created: ${name}`));
12468
13059
  }
12469
13060
  }
12470
13061
  async function generateExtractionPrompt(name) {
12471
- console.log(import_chalk11.default.bold(`
13062
+ console.log(import_chalk12.default.bold(`
12472
13063
  Generating extraction prompt for: ${name}
12473
13064
  `));
12474
13065
  const prompt = `I need you to analyze writing samples and create a voice profile.
@@ -12490,33 +13081,33 @@ function registerImportCommand(program3) {
12490
13081
  program3.command("import").description("Import a voice profile from file").argument("<name>", "Profile name").argument("<file>", "Profile markdown file").action(async (name, file) => {
12491
13082
  try {
12492
13083
  assertSafeProfileName(name);
12493
- const filePath = path12.resolve(file);
12494
- if (!fs11.existsSync(filePath)) {
12495
- 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}`));
12496
13087
  process.exit(1);
12497
13088
  }
12498
- const content = fs11.readFileSync(filePath, "utf-8");
13089
+ const content = fs12.readFileSync(filePath, "utf-8");
12499
13090
  ensureHyvDir();
12500
13091
  writeCachedProfile(name, content);
12501
- console.log(import_chalk11.default.green(`
13092
+ console.log(import_chalk12.default.green(`
12502
13093
  \u2713 Profile imported: ${name}`));
12503
13094
  } catch (error) {
12504
- console.error(import_chalk11.default.red(`Error: ${error.message}`));
13095
+ console.error(import_chalk12.default.red(`Error: ${error.message}`));
12505
13096
  process.exit(1);
12506
13097
  }
12507
13098
  });
12508
13099
  }
12509
13100
  function askQuestion(question) {
12510
- return new Promise((resolve14) => {
12511
- const readline2 = require("readline");
12512
- const rl = readline2.createInterface({
13101
+ return new Promise((resolve15) => {
13102
+ const readline3 = require("readline");
13103
+ const rl = readline3.createInterface({
12513
13104
  input: process.stdin,
12514
13105
  output: process.stdout
12515
13106
  });
12516
- rl.question(import_chalk11.default.cyan(` ${question}
13107
+ rl.question(import_chalk12.default.cyan(` ${question}
12517
13108
  > `), (answer) => {
12518
13109
  rl.close();
12519
- resolve14(answer.trim());
13110
+ resolve15(answer.trim());
12520
13111
  });
12521
13112
  });
12522
13113
  }
@@ -12580,7 +13171,7 @@ function generateLocalProfileFromStats(name, stats) {
12580
13171
  }
12581
13172
 
12582
13173
  // src/commands/plan.ts
12583
- var import_chalk12 = __toESM(require_source());
13174
+ var import_chalk13 = __toESM(require_source());
12584
13175
  init_config();
12585
13176
  init_auth();
12586
13177
  var import_open2 = __toESM(require_open());
@@ -12595,10 +13186,10 @@ function registerPlanCommand(program3) {
12595
13186
  }
12596
13187
  const token = getToken();
12597
13188
  if (!token) {
12598
- console.log(import_chalk12.default.yellow("\nNot authenticated \u2014 free tier still works.\n"));
12599
- 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"));
12600
13191
  printFreePaidMatrix({ compact: true });
12601
- 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"));
12602
13193
  return;
12603
13194
  }
12604
13195
  if (options.upgrade) {
@@ -12611,20 +13202,20 @@ function registerPlanCommand(program3) {
12611
13202
  await showPlan();
12612
13203
  }
12613
13204
  } catch (error) {
12614
- console.error(import_chalk12.default.red(`
13205
+ console.error(import_chalk13.default.red(`
12615
13206
  Error: ${error.message}`));
12616
13207
  process.exit(1);
12617
13208
  }
12618
13209
  });
12619
13210
  }
12620
13211
  async function showPlan() {
12621
- console.log(import_chalk12.default.bold("\nSubscription Plan\n"));
13212
+ console.log(import_chalk13.default.bold("\nSubscription Plan\n"));
12622
13213
  const response = await authenticatedRequest(
12623
13214
  cliApiUrl("/cli/heartbeat"),
12624
13215
  { method: "GET" }
12625
13216
  );
12626
13217
  if (response.status !== 200) {
12627
- console.log(import_chalk12.default.yellow("Could not fetch plan info."));
13218
+ console.log(import_chalk13.default.yellow("Could not fetch plan info."));
12628
13219
  return;
12629
13220
  }
12630
13221
  const data = response.data;
@@ -12642,27 +13233,27 @@ async function showPlan() {
12642
13233
  team: "$29/mo",
12643
13234
  agency: "Custom"
12644
13235
  };
12645
- console.log(import_chalk12.default.dim("Plan:"), import_chalk12.default.bold(planNames[plan] || plan));
12646
- console.log(import_chalk12.default.dim("Price:"), planPrices[plan] || "-");
12647
- 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");
12648
13239
  if (license) {
12649
- console.log(import_chalk12.default.dim("License:"), license.key_hint);
13240
+ console.log(import_chalk13.default.dim("License:"), license.key_hint);
12650
13241
  }
12651
13242
  const access = await getAccessState();
12652
- console.log(import_chalk12.default.bold("\nFree tier (always)"));
12653
- 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"));
12654
13245
  if (plan === "none" || !access.hasPaidPlan) {
12655
- console.log(import_chalk12.default.bold("\nUpgrade unlocks"));
12656
- console.log(import_chalk12.default.dim(" profiles, learning loop, hybrid analysis, rich rewrites"));
12657
- console.log(import_chalk12.default.dim(` ${PRICING_URL}`));
12658
- 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"));
12659
13250
  } else {
12660
13251
  console.log("\nManage your subscription:");
12661
- console.log(import_chalk12.default.dim(" hyv plan --manage"));
13252
+ console.log(import_chalk13.default.dim(" hyv plan --manage"));
12662
13253
  }
12663
13254
  }
12664
13255
  async function upgradePlan() {
12665
- console.log(import_chalk12.default.cyan("\nOpening checkout...\n"));
13256
+ console.log(import_chalk13.default.cyan("\nOpening checkout...\n"));
12666
13257
  const response = await authenticatedRequest(
12667
13258
  cliApiUrl("/cli/subscribe"),
12668
13259
  {
@@ -12674,20 +13265,20 @@ async function upgradePlan() {
12674
13265
  const data = response.data;
12675
13266
  const checkoutUrl = data.checkout_url;
12676
13267
  if (checkoutUrl) {
12677
- console.log(import_chalk12.default.dim("Opening browser..."));
13268
+ console.log(import_chalk13.default.dim("Opening browser..."));
12678
13269
  await (0, import_open2.default)(assertSafeOpenUrl(checkoutUrl));
12679
- console.log(import_chalk12.default.green("\n\u2713 Checkout opened in browser"));
12680
- 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."));
12681
13272
  } else {
12682
- console.log(import_chalk12.default.yellow("No checkout URL received."));
13273
+ console.log(import_chalk13.default.yellow("No checkout URL received."));
12683
13274
  }
12684
13275
  } else {
12685
- console.log(import_chalk12.default.yellow("Could not create checkout session."));
12686
- 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."));
12687
13278
  }
12688
13279
  }
12689
13280
  async function openBillingPortal() {
12690
- console.log(import_chalk12.default.cyan("\nOpening billing portal...\n"));
13281
+ console.log(import_chalk13.default.cyan("\nOpening billing portal...\n"));
12691
13282
  const response = await authenticatedRequest(
12692
13283
  cliApiUrl("/cli/subscribe/manage"),
12693
13284
  { method: "POST" }
@@ -12696,41 +13287,41 @@ async function openBillingPortal() {
12696
13287
  const data = response.data;
12697
13288
  const portalUrl = data.portal_url;
12698
13289
  if (portalUrl) {
12699
- console.log(import_chalk12.default.dim("Opening browser..."));
13290
+ console.log(import_chalk13.default.dim("Opening browser..."));
12700
13291
  await (0, import_open2.default)(assertSafeOpenUrl(portalUrl));
12701
- console.log(import_chalk12.default.green("\n\u2713 Billing portal opened"));
13292
+ console.log(import_chalk13.default.green("\n\u2713 Billing portal opened"));
12702
13293
  } else {
12703
- console.log(import_chalk12.default.yellow("No portal URL received."));
13294
+ console.log(import_chalk13.default.yellow("No portal URL received."));
12704
13295
  }
12705
13296
  } else {
12706
- console.log(import_chalk12.default.yellow("Could not open billing portal."));
12707
- 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."));
12708
13299
  }
12709
13300
  }
12710
13301
  async function downgradePlan() {
12711
- console.log(import_chalk12.default.yellow("\nDowngrade Plan\n"));
13302
+ console.log(import_chalk13.default.yellow("\nDowngrade Plan\n"));
12712
13303
  console.log("To downgrade your plan, please visit:");
12713
- console.log(import_chalk12.default.dim(" https://holdyourvoice.com/dashboard"));
13304
+ console.log(import_chalk13.default.dim(" https://holdyourvoice.com/dashboard"));
12714
13305
  console.log("\nOr contact support at shashank@holdyourvoice.com");
12715
13306
  }
12716
13307
 
12717
13308
  // src/commands/scan.ts
12718
- var import_chalk15 = __toESM(require_source());
13309
+ var import_chalk16 = __toESM(require_source());
12719
13310
  init_pipeline();
12720
13311
  init_local_profile();
12721
13312
  init_access();
12722
13313
 
12723
13314
  // src/lib/output.ts
12724
- var import_chalk13 = __toESM(require_source());
13315
+ var import_chalk14 = __toESM(require_source());
12725
13316
  var c = {
12726
- dim: (s) => import_chalk13.default.dim(s),
12727
- bold: (s) => import_chalk13.default.bold(s),
12728
- accent: (s) => import_chalk13.default.hex("#C4441A")(s),
12729
- green: (s) => import_chalk13.default.green(s),
12730
- red: (s) => import_chalk13.default.red(s),
12731
- yellow: (s) => import_chalk13.default.yellow(s),
12732
- cyan: (s) => import_chalk13.default.cyan(s),
12733
- 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)
12734
13325
  };
12735
13326
  function logo() {
12736
13327
  return `${c.bold("hold your ")}${c.accent("voice")}`;
@@ -12878,12 +13469,12 @@ async function runHybridAnalysis(text, profile, opts = {}) {
12878
13469
  }
12879
13470
 
12880
13471
  // src/commands/history.ts
12881
- var import_chalk14 = __toESM(require_source());
12882
- var fs12 = __toESM(require("fs"));
12883
- var path13 = __toESM(require("path"));
13472
+ var import_chalk15 = __toESM(require_source());
13473
+ var fs13 = __toESM(require("fs"));
13474
+ var path14 = __toESM(require("path"));
12884
13475
  init_config();
12885
- var HISTORY_DIR = path13.join(HYV_DIR, "history");
12886
- 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");
12887
13478
  function logScan(entry) {
12888
13479
  try {
12889
13480
  ensureHyvDir();
@@ -12893,9 +13484,9 @@ function logScan(entry) {
12893
13484
  }
12894
13485
  function readHistory() {
12895
13486
  try {
12896
- if (!fs12.existsSync(HISTORY_FILE))
13487
+ if (!fs13.existsSync(HISTORY_FILE))
12897
13488
  return [];
12898
- 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);
12899
13490
  return lines.map((l) => JSON.parse(l));
12900
13491
  } catch {
12901
13492
  return [];
@@ -12927,10 +13518,10 @@ function registerHistoryCommand(program3) {
12927
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) => {
12928
13519
  try {
12929
13520
  if (options.clear) {
12930
- if (fs12.existsSync(HISTORY_FILE)) {
12931
- fs12.unlinkSync(HISTORY_FILE);
13521
+ if (fs13.existsSync(HISTORY_FILE)) {
13522
+ fs13.unlinkSync(HISTORY_FILE);
12932
13523
  }
12933
- console.log(import_chalk14.default.green("\n\u2713 History cleared"));
13524
+ console.log(import_chalk15.default.green("\n\u2713 History cleared"));
12934
13525
  return;
12935
13526
  }
12936
13527
  let entries = readHistory();
@@ -12944,14 +13535,14 @@ function registerHistoryCommand(program3) {
12944
13535
  const limit = parseInt(options.limit, 10);
12945
13536
  entries = entries.slice(-limit);
12946
13537
  if (entries.length === 0) {
12947
- 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."));
12948
13539
  return;
12949
13540
  }
12950
13541
  if (options.format === "json") {
12951
13542
  console.log(JSON.stringify(entries, null, 2));
12952
13543
  return;
12953
13544
  }
12954
- console.log(import_chalk14.default.bold(`
13545
+ console.log(import_chalk15.default.bold(`
12955
13546
  score history (last ${entries.length} scans)
12956
13547
  `));
12957
13548
  if (options.chart) {
@@ -12963,7 +13554,7 @@ function registerHistoryCommand(program3) {
12963
13554
  if (min !== max) {
12964
13555
  console.log(` ${min} \u2524`);
12965
13556
  }
12966
- 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)}`));
12967
13558
  console.log("");
12968
13559
  }
12969
13560
  const scores = entries.map((e) => e.score);
@@ -12976,22 +13567,22 @@ function registerHistoryCommand(program3) {
12976
13567
  const firstAvg = firstHalf.reduce((s, e) => s + e.score, 0) / (firstHalf.length || 1);
12977
13568
  const secondAvg = secondHalf.reduce((s, e) => s + e.score, 0) / (secondHalf.length || 1);
12978
13569
  const trend = Math.round(secondAvg - firstAvg);
12979
- 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");
12980
- console.log(import_chalk14.default.dim(` avg: ${avg} best: ${best.score} worst: ${worst.score}`));
12981
- console.log(import_chalk14.default.dim(` trend: ${trendStr}`));
12982
- 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"));
12983
13574
  const recent = entries.slice(-10).reverse();
12984
13575
  for (const entry of recent) {
12985
13576
  const time = new Date(entry.timestamp).toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit" });
12986
- const icon = entry.issues === 0 ? import_chalk14.default.green("\u2713") : import_chalk14.default.red("\u25CF");
12987
- 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`);
12988
- const issues = entry.issues > 0 ? import_chalk14.default.red(`${entry.issues} issues`) : import_chalk14.default.dim("clean");
12989
- const file = path13.basename(entry.file).slice(0, 25).padEnd(25);
12990
- 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}`);
12991
13582
  }
12992
13583
  console.log("");
12993
13584
  } catch (error) {
12994
- console.error(import_chalk14.default.red(`Error: ${error.message}`));
13585
+ console.error(import_chalk15.default.red(`Error: ${error.message}`));
12995
13586
  process.exit(1);
12996
13587
  }
12997
13588
  });
@@ -13006,7 +13597,7 @@ function registerScanCommand(program3) {
13006
13597
  const profile = await loadProfileForCommand(options.profile);
13007
13598
  const { text, path: filePath } = readText(file);
13008
13599
  if (!text.trim()) {
13009
- 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."));
13010
13601
  process.exit(0);
13011
13602
  }
13012
13603
  const ignoreRules = options.ignore ? new Set(options.ignore.split(",").map((r) => r.trim())) : /* @__PURE__ */ new Set();
@@ -13049,26 +13640,26 @@ function registerScanCommand(program3) {
13049
13640
  }, null, 2));
13050
13641
  } else {
13051
13642
  if (signals.length === 0) {
13052
- console.log(import_chalk15.default.green(`
13643
+ console.log(import_chalk16.default.green(`
13053
13644
  \u2713 No issues found in ${filePath}`));
13054
- console.log(import_chalk15.default.dim(` score: ${score}/100`));
13645
+ console.log(import_chalk16.default.dim(` score: ${score}/100`));
13055
13646
  } else {
13056
- console.log(import_chalk15.default.bold(`
13647
+ console.log(import_chalk16.default.bold(`
13057
13648
  hyv scan ${filePath}`));
13058
13649
  if (profile) {
13059
- 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)" : ""}`));
13060
13651
  }
13061
13652
  if (analysis.message) {
13062
- console.log(import_chalk15.default.dim(` engine: ${analysis.message}`));
13653
+ console.log(import_chalk16.default.dim(` engine: ${analysis.message}`));
13063
13654
  }
13064
13655
  console.log("");
13065
13656
  printGroupedSignals(signals, profile);
13066
- console.log(import_chalk15.default.yellow(`
13657
+ console.log(import_chalk16.default.yellow(`
13067
13658
  ${signals.length} issues (${signals.filter((s) => s.severity === "red").length} red, ${signals.filter((s) => s.severity === "yellow").length} yellow)`));
13068
- console.log(import_chalk15.default.dim(` score: ${score}/100`));
13069
- console.log(import_chalk15.default.dim(`
13659
+ console.log(import_chalk16.default.dim(` score: ${score}/100`));
13660
+ console.log(import_chalk16.default.dim(`
13070
13661
  fix: hyv fix ${file}`));
13071
- console.log(import_chalk15.default.dim(` diff: hyv diff ${file}
13662
+ console.log(import_chalk16.default.dim(` diff: hyv diff ${file}
13072
13663
  `));
13073
13664
  }
13074
13665
  await maybeShowLimitedModeHint(!!profile);
@@ -13086,17 +13677,17 @@ hyv scan ${filePath}`));
13086
13677
  process.exit(2);
13087
13678
  }
13088
13679
  } catch (error) {
13089
- console.error(import_chalk15.default.red(`Error: ${error.message}`));
13680
+ console.error(import_chalk16.default.red(`Error: ${error.message}`));
13090
13681
  process.exit(1);
13091
13682
  }
13092
13683
  });
13093
13684
  }
13094
13685
 
13095
13686
  // src/commands/doctor.ts
13096
- var import_chalk16 = __toESM(require_source());
13097
- var fs14 = __toESM(require("fs"));
13098
- var path15 = __toESM(require("path"));
13099
- 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"));
13100
13691
  init_config();
13101
13692
  init_auth();
13102
13693
  init_access();
@@ -13107,15 +13698,15 @@ init_version();
13107
13698
  var import_child_process2 = require("child_process");
13108
13699
 
13109
13700
  // src/lib/cli-entry.ts
13110
- var fs13 = __toESM(require("fs"));
13111
- var path14 = __toESM(require("path"));
13701
+ var fs14 = __toESM(require("fs"));
13702
+ var path15 = __toESM(require("path"));
13112
13703
  function resolveCliEntry() {
13113
13704
  const candidates = [
13114
- path14.resolve(process.argv[1] || ""),
13115
- path14.resolve(__dirname, "index.js"),
13116
- 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")
13117
13708
  ];
13118
- return candidates.find((p) => p && fs13.existsSync(p)) || null;
13709
+ return candidates.find((p) => p && fs14.existsSync(p)) || null;
13119
13710
  }
13120
13711
  function mcpServerCommand() {
13121
13712
  const entry = resolveCliEntry();
@@ -13134,7 +13725,7 @@ async function testMcpStdioSubprocess() {
13134
13725
  const entry = resolveCliEntry();
13135
13726
  if (!entry)
13136
13727
  return { ok: false };
13137
- return new Promise((resolve14) => {
13728
+ return new Promise((resolve15) => {
13138
13729
  const child = (0, import_child_process2.spawn)(process.execPath, [entry, "mcp"], {
13139
13730
  stdio: ["pipe", "pipe", "pipe"],
13140
13731
  env: { ...process.env, HYV_POSTINSTALL_QUIET: "1" }
@@ -13150,7 +13741,7 @@ async function testMcpStdioSubprocess() {
13150
13741
  child.kill();
13151
13742
  } catch {
13152
13743
  }
13153
- resolve14({ ok, toolCount });
13744
+ resolve15({ ok, toolCount });
13154
13745
  };
13155
13746
  const timeout = setTimeout(() => finish(false), 1e4);
13156
13747
  const handleLine = (line) => {
@@ -13201,20 +13792,20 @@ async function testMcpStdioSubprocess() {
13201
13792
  }
13202
13793
 
13203
13794
  // src/commands/doctor.ts
13204
- var HOME = os5.homedir();
13795
+ var HOME = os6.homedir();
13205
13796
  var IS_WIN = process.platform === "win32";
13206
13797
  function claudeDesktopDir() {
13207
13798
  if (IS_WIN)
13208
- return path15.join(HOME, "AppData", "Roaming", "Claude");
13799
+ return path16.join(HOME, "AppData", "Roaming", "Claude");
13209
13800
  if (process.platform === "linux")
13210
- return path15.join(HOME, ".config", "Claude");
13211
- return path15.join(HOME, "Library", "Application Support", "Claude");
13801
+ return path16.join(HOME, ".config", "Claude");
13802
+ return path16.join(HOME, "Library", "Application Support", "Claude");
13212
13803
  }
13213
13804
  function isOwnerOnlyFile(filePath) {
13214
13805
  try {
13215
- if (!fs14.existsSync(filePath))
13806
+ if (!fs15.existsSync(filePath))
13216
13807
  return true;
13217
- const mode = fs14.statSync(filePath).mode & 511;
13808
+ const mode = fs15.statSync(filePath).mode & 511;
13218
13809
  return (mode & 63) === 0;
13219
13810
  } catch {
13220
13811
  return false;
@@ -13222,9 +13813,9 @@ function isOwnerOnlyFile(filePath) {
13222
13813
  }
13223
13814
  function readMcpHyv(configFile) {
13224
13815
  try {
13225
- if (!fs14.existsSync(configFile))
13816
+ if (!fs15.existsSync(configFile))
13226
13817
  return false;
13227
- const cfg = JSON.parse(fs14.readFileSync(configFile, "utf-8"));
13818
+ const cfg = JSON.parse(fs15.readFileSync(configFile, "utf-8"));
13228
13819
  return Boolean(cfg.mcpServers?.hyv);
13229
13820
  } catch {
13230
13821
  return false;
@@ -13232,188 +13823,188 @@ function readMcpHyv(configFile) {
13232
13823
  }
13233
13824
  function registerDoctorCommand(program3) {
13234
13825
  program3.command("doctor").description("Diagnose CLI health: engine, cache, auth, agents").option("--fix-agents", "Re-run agent config copy (idempotent)").action(async (opts) => {
13235
- 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"));
13236
13827
  let issues = 0;
13237
13828
  let fixed = 0;
13238
- console.log(import_chalk16.default.dim(`engine: ${getEngineLabel()} (node ${process.version})`));
13829
+ console.log(import_chalk17.default.dim(`engine: ${getEngineLabel()} (node ${process.version})`));
13239
13830
  if (isLocalOnlyMode()) {
13240
- 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"));
13241
13832
  }
13242
13833
  const access = await getAccessState();
13243
13834
  const profile = await loadProfileForCommand();
13244
- console.log(import_chalk16.default.dim(`mode: ${formatModeLabel(access, hasRichProfile(profile))}`));
13245
- console.log(import_chalk16.default.dim("tip: run hyv welcome for free capabilities\n"));
13246
- 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..."));
13247
13838
  const cliPath = process.argv[1];
13248
- if (cliPath && fs14.existsSync(cliPath)) {
13249
- 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"));
13250
13841
  } else {
13251
- console.log(import_chalk16.default.red(" \u2717 cli not found"));
13842
+ console.log(import_chalk17.default.red(" \u2717 cli not found"));
13252
13843
  issues++;
13253
13844
  }
13254
- console.log(import_chalk16.default.dim("checking .hyv directory..."));
13255
- if (fs14.existsSync(HYV_DIR)) {
13256
- 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"));
13257
13848
  } else {
13258
- console.log(import_chalk16.default.yellow(" ! .hyv directory missing \u2014 creating..."));
13259
- 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 });
13260
13851
  fixed++;
13261
13852
  }
13262
- console.log(import_chalk16.default.dim("checking cache..."));
13853
+ console.log(import_chalk17.default.dim("checking cache..."));
13263
13854
  const diskProfiles = listDiskCachedProfiles();
13264
- const syncMeta = path15.join(CACHE_DIR, "sync-meta.json");
13265
- if (diskProfiles.length > 0 || fs14.existsSync(syncMeta)) {
13266
- console.log(import_chalk16.default.green(` \u2713 profile cache (${diskProfiles.length} full profile(s))`));
13267
- 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)) {
13268
13859
  try {
13269
- const meta = JSON.parse(fs14.readFileSync(syncMeta, "utf-8"));
13270
- 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"}`));
13271
13862
  } catch {
13272
13863
  }
13273
13864
  }
13274
13865
  } else {
13275
- 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)"));
13276
13867
  }
13277
- console.log(import_chalk16.default.dim("checking file permissions..."));
13278
- if (fs14.existsSync(HYV_DIR)) {
13279
- const hyvMode = fs14.statSync(HYV_DIR).mode & 511;
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;
13280
13871
  if ((hyvMode & 63) === 0) {
13281
- console.log(import_chalk16.default.green(" \u2713 .hyv directory permissions"));
13872
+ console.log(import_chalk17.default.green(" \u2713 .hyv directory permissions"));
13282
13873
  } else {
13283
- console.log(import_chalk16.default.yellow(" ! .hyv directory is world/group accessible"));
13284
- console.log(import_chalk16.default.dim(" run: chmod 700 ~/.hyv"));
13874
+ console.log(import_chalk17.default.yellow(" ! .hyv directory is world/group accessible"));
13875
+ console.log(import_chalk17.default.dim(" run: chmod 700 ~/.hyv"));
13285
13876
  issues++;
13286
13877
  }
13287
13878
  }
13288
- if (fs14.existsSync(AUTH_FILE)) {
13879
+ if (fs15.existsSync(AUTH_FILE)) {
13289
13880
  if (isOwnerOnlyFile(AUTH_FILE)) {
13290
- console.log(import_chalk16.default.green(" \u2713 auth.json permissions"));
13881
+ console.log(import_chalk17.default.green(" \u2713 auth.json permissions"));
13291
13882
  } else {
13292
- console.log(import_chalk16.default.yellow(" ! auth.json is world/group readable"));
13293
- console.log(import_chalk16.default.dim(" run: chmod 600 ~/.hyv/auth.json"));
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"));
13294
13885
  issues++;
13295
13886
  }
13296
13887
  }
13297
- console.log(import_chalk16.default.dim("checking authentication..."));
13888
+ console.log(import_chalk17.default.dim("checking authentication..."));
13298
13889
  if (isInitialized()) {
13299
13890
  const auth = readAuth();
13300
13891
  if (auth) {
13301
- console.log(import_chalk16.default.green(" \u2713 authenticated"));
13302
- 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"}`));
13303
13894
  const session = await checkSession();
13304
13895
  if (session.valid) {
13305
- console.log(import_chalk16.default.green(" \u2713 session valid"));
13306
- 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"}`));
13307
13898
  } else {
13308
- console.log(import_chalk16.default.red(" \u2717 session expired"));
13309
- 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"));
13310
13901
  issues++;
13311
13902
  }
13312
13903
  } else {
13313
- console.log(import_chalk16.default.red(" \u2717 auth data missing"));
13904
+ console.log(import_chalk17.default.red(" \u2717 auth data missing"));
13314
13905
  issues++;
13315
13906
  }
13316
13907
  } else {
13317
- console.log(import_chalk16.default.yellow(" ! not authenticated (free local scan works)"));
13318
- 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"));
13319
13910
  }
13320
- console.log(import_chalk16.default.dim("checking voice profile..."));
13321
- const voiceMd = path15.join(HYV_DIR, "voice.md");
13322
- const hasVoiceMd = fs14.existsSync(voiceMd) && fs14.readFileSync(voiceMd, "utf-8").trim().length > 50;
13323
- const profileFiles = fs14.existsSync(PROFILES_DIR) ? fs14.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md") && f !== "voice.md") : [];
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") : [];
13324
13915
  if (hasVoiceMd || profileFiles.length > 0 || diskProfiles.length > 0) {
13325
13916
  if (hasVoiceMd)
13326
- console.log(import_chalk16.default.green(" \u2713 voice.md exists"));
13917
+ console.log(import_chalk17.default.green(" \u2713 voice.md exists"));
13327
13918
  if (profileFiles.length > 0)
13328
- 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)`));
13329
13920
  if (diskProfiles.length > 0)
13330
- 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)`));
13331
13922
  } else {
13332
- console.log(import_chalk16.default.yellow(" ! no voice profile (optional for free scan)"));
13333
- console.log(import_chalk16.default.dim(" run: hyv new <name> or hyv init"));
13334
- }
13335
- console.log(import_chalk16.default.dim("checking agent configurations..."));
13336
- const cursorLegacyRule = path15.join(HOME, ".cursor", "rules", "hyv.md");
13337
- const cursorRule = path15.join(HOME, ".cursor", "rules", "hyv.mdc");
13338
- if (fs14.existsSync(cursorLegacyRule) && fs14.existsSync(cursorRule)) {
13339
- console.log(import_chalk16.default.yellow(" ! stale cursor rule ~/.cursor/rules/hyv.md (use hyv.mdc)"));
13340
- console.log(import_chalk16.default.dim(" run: rm ~/.cursor/rules/hyv.md (or hyv doctor --fix-agents)"));
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)"));
13341
13932
  issues++;
13342
13933
  }
13343
13934
  const agentChecks = [
13344
- { name: "claude desktop mcp", ok: readMcpHyv(path15.join(claudeDesktopDir(), "claude_desktop_config.json")) },
13345
- { name: "cursor mcp", ok: readMcpHyv(path15.join(HOME, ".cursor", "mcp.json")) },
13346
- { name: "cursor rule", ok: fs14.existsSync(cursorRule) },
13347
- { name: "claude code command", ok: fs14.existsSync(path15.join(HOME, ".claude", "commands", "hyv.md")) },
13348
- { name: "claude code skill", ok: fs14.existsSync(path15.join(HOME, ".claude", "skills", "hold-your-voice", "SKILL.md")) },
13349
- { name: "codex agents", ok: fs14.existsSync(path15.join(HOME, ".codex", "AGENTS.md")) && fs14.readFileSync(path15.join(HOME, ".codex", "AGENTS.md"), "utf-8").includes("hyv") },
13350
- { name: "command code skill", ok: fs14.existsSync(path15.join(HOME, ".commandcode", "skills", "hyv", "SKILL.md")) }
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")) }
13351
13942
  ];
13352
13943
  for (const agent of agentChecks) {
13353
13944
  if (agent.ok) {
13354
- console.log(import_chalk16.default.green(` \u2713 ${agent.name}`));
13945
+ console.log(import_chalk17.default.green(` \u2713 ${agent.name}`));
13355
13946
  } else {
13356
- console.log(import_chalk16.default.dim(` - ${agent.name} not configured`));
13947
+ console.log(import_chalk17.default.dim(` - ${agent.name} not configured`));
13357
13948
  }
13358
13949
  }
13359
13950
  if (opts.fixAgents) {
13360
13951
  try {
13361
- const pkgDir = path15.resolve(__dirname, "..");
13362
- 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"));
13363
13954
  const result = setupAgents({ pkgDir, quiet: true });
13364
- 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"})`));
13365
13956
  if (result.warnings.length) {
13366
- console.log(import_chalk16.default.yellow(` notes: ${result.warnings.join("; ")}`));
13957
+ console.log(import_chalk17.default.yellow(` notes: ${result.warnings.join("; ")}`));
13367
13958
  }
13368
13959
  fixed++;
13369
13960
  } catch (err) {
13370
- 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}`));
13371
13962
  }
13372
13963
  }
13373
- console.log(import_chalk16.default.dim("checking mcp server..."));
13374
- const claudeMcp = readMcpHyv(path15.join(claudeDesktopDir(), "claude_desktop_config.json"));
13375
- 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"));
13376
13967
  if (claudeMcp || cursorMcp) {
13377
13968
  if (claudeMcp)
13378
- 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"));
13379
13970
  if (cursorMcp)
13380
- console.log(import_chalk16.default.green(" \u2713 mcp configured for cursor"));
13971
+ console.log(import_chalk17.default.green(" \u2713 mcp configured for cursor"));
13381
13972
  } else {
13382
- 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"));
13383
13974
  issues++;
13384
13975
  }
13385
13976
  try {
13386
13977
  const stdio = await testMcpStdioSubprocess();
13387
13978
  if (stdio.ok && (stdio.toolCount || 0) >= 10) {
13388
- 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)`));
13389
13980
  } else if (stdio.ok) {
13390
- 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`));
13391
13982
  issues++;
13392
13983
  } else {
13393
- console.log(import_chalk16.default.red(" \u2717 mcp stdio subprocess failed"));
13394
- 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"));
13395
13986
  issues++;
13396
13987
  }
13397
13988
  } catch (err) {
13398
- 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}`));
13399
13990
  issues++;
13400
13991
  }
13401
13992
  console.log("");
13402
13993
  if (issues === 0 && fixed === 0) {
13403
- console.log(import_chalk16.default.green("\u2713 everything looks good!"));
13994
+ console.log(import_chalk17.default.green("\u2713 everything looks good!"));
13404
13995
  } else if (fixed > 0) {
13405
- console.log(import_chalk16.default.green(`\u2713 fixed ${fixed} issue(s)`));
13996
+ console.log(import_chalk17.default.green(`\u2713 fixed ${fixed} issue(s)`));
13406
13997
  if (issues > 0)
13407
- console.log(import_chalk16.default.yellow(`! ${issues} issue(s) remaining`));
13998
+ console.log(import_chalk17.default.yellow(`! ${issues} issue(s) remaining`));
13408
13999
  } else {
13409
- console.log(import_chalk16.default.yellow(`! ${issues} issue(s) found`));
14000
+ console.log(import_chalk17.default.yellow(`! ${issues} issue(s) found`));
13410
14001
  }
13411
- 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"));
13412
14003
  });
13413
14004
  }
13414
14005
 
13415
14006
  // src/commands/rename.ts
13416
- var import_chalk17 = __toESM(require_source());
14007
+ var import_chalk18 = __toESM(require_source());
13417
14008
  init_config();
13418
14009
  init_auth();
13419
14010
  function registerRenameCommand(program3) {
@@ -13421,26 +14012,26 @@ function registerRenameCommand(program3) {
13421
14012
  try {
13422
14013
  const trimmedName = newName.trim();
13423
14014
  if (!trimmedName) {
13424
- console.log(import_chalk17.default.red("\nerror: what do you want to name your profile?\n"));
13425
- 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'));
13426
14017
  process.exit(1);
13427
14018
  return;
13428
14019
  }
13429
14020
  if (trimmedName.length > 100) {
13430
- 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"));
13431
14022
  process.exit(1);
13432
14023
  return;
13433
14024
  }
13434
14025
  if (!/^[a-zA-Z0-9\s\-_'&.]+$/.test(trimmedName)) {
13435
- console.log(import_chalk17.default.red("\nerror: name contains invalid characters\n"));
13436
- 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"));
13437
14028
  process.exit(1);
13438
14029
  return;
13439
14030
  }
13440
14031
  const token = getToken();
13441
14032
  if (!token) {
13442
- console.log(import_chalk17.default.red("\nerror: you're not signed in yet\n"));
13443
- 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"));
13444
14035
  process.exit(1);
13445
14036
  return;
13446
14037
  }
@@ -13456,16 +14047,16 @@ function registerRenameCommand(program3) {
13456
14047
  );
13457
14048
  if (response.status === 200) {
13458
14049
  const data = response.data;
13459
- console.log(import_chalk17.default.green(`
14050
+ console.log(import_chalk18.default.green(`
13460
14051
  \u2713 profile renamed to ${data.profile?.name || trimmedName}
13461
14052
  `));
13462
14053
  } else {
13463
- console.log(import_chalk17.default.red(`
14054
+ console.log(import_chalk18.default.red(`
13464
14055
  error: server returned ${response.status}
13465
14056
  `));
13466
14057
  }
13467
14058
  } catch (error) {
13468
- console.error(import_chalk17.default.red(`
14059
+ console.error(import_chalk18.default.red(`
13469
14060
  error: ${error.message}
13470
14061
  `));
13471
14062
  process.exit(1);
@@ -13474,46 +14065,46 @@ error: ${error.message}
13474
14065
  }
13475
14066
 
13476
14067
  // src/commands/fix.ts
13477
- var import_chalk19 = __toESM(require_source());
14068
+ var import_chalk20 = __toESM(require_source());
13478
14069
  init_pipeline();
13479
14070
  init_local_profile();
13480
14071
  init_access();
13481
14072
  init_config();
13482
14073
 
13483
14074
  // src/lib/destructive-write.ts
13484
- var fs15 = __toESM(require("fs"));
13485
- var path16 = __toESM(require("path"));
13486
- var readline = __toESM(require("readline"));
13487
- var import_chalk18 = __toESM(require_source());
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());
13488
14079
  async function confirmDestructiveWrite(options) {
13489
14080
  if (options.yes)
13490
14081
  return true;
13491
14082
  const label = options.target ? `${options.action} (${options.target})` : options.action;
13492
14083
  if (!process.stdin.isTTY) {
13493
- console.error(import_chalk18.default.red("\nRefusing destructive write without confirmation (non-interactive)."));
13494
- console.error(import_chalk18.default.dim(" Re-run with --yes to create .bak backups and proceed.\n"));
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"));
13495
14086
  return false;
13496
14087
  }
13497
14088
  const question = `
13498
14089
  ${label}
13499
14090
  A .bak backup will be created. Proceed? [y/N] `;
13500
- const answer = await new Promise((resolve14) => {
13501
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
14091
+ const answer = await new Promise((resolve15) => {
14092
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
13502
14093
  rl.question(question, (value) => {
13503
14094
  rl.close();
13504
- resolve14(value.trim().toLowerCase());
14095
+ resolve15(value.trim().toLowerCase());
13505
14096
  });
13506
14097
  });
13507
14098
  return answer === "y" || answer === "yes";
13508
14099
  }
13509
14100
  function writeInPlaceWithBackup(filePath, content) {
13510
14101
  const backupPath = filePath + ".bak";
13511
- fs15.copyFileSync(filePath, backupPath);
13512
- fs15.writeFileSync(filePath, content);
14102
+ fs16.copyFileSync(filePath, backupPath);
14103
+ fs16.writeFileSync(filePath, content);
13513
14104
  return backupPath;
13514
14105
  }
13515
14106
  function backupBasename(filePath) {
13516
- return path16.basename(filePath) + ".bak";
14107
+ return path17.basename(filePath) + ".bak";
13517
14108
  }
13518
14109
 
13519
14110
  // src/commands/fix.ts
@@ -13527,9 +14118,9 @@ function registerFixCommand(program3) {
13527
14118
  if (options.format === "json") {
13528
14119
  console.log(JSON.stringify({ file: filePath, autoFixes: 0, llmIssues: result.stats.needsLLM, changes: [] }));
13529
14120
  } else {
13530
- 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."));
13531
14122
  if (result.stats.needsLLM > 0) {
13532
- 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}`));
13533
14124
  }
13534
14125
  }
13535
14126
  return;
@@ -13543,16 +14134,16 @@ function registerFixCommand(program3) {
13543
14134
  fixed: options.dryRun ? void 0 : result.fixed
13544
14135
  }, null, 2));
13545
14136
  } else {
13546
- console.log(import_chalk19.default.dim(`
14137
+ console.log(import_chalk20.default.dim(`
13547
14138
  hyv fix ${filePath}
13548
14139
  `));
13549
14140
  for (const change of result.changes) {
13550
- console.log(import_chalk19.default.dim(` Line ${change.line}: `) + import_chalk19.default.red(change.before) + import_chalk19.default.dim(" \u2192 ") + import_chalk19.default.green(change.after));
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));
13551
14142
  }
13552
- console.log(import_chalk19.default.green(`
14143
+ console.log(import_chalk20.default.green(`
13553
14144
  \u2713 ${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} applied`));
13554
14145
  if (result.stats.needsLLM > 0) {
13555
- 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}`));
13556
14147
  }
13557
14148
  }
13558
14149
  if (!options.dryRun) {
@@ -13566,7 +14157,7 @@ hyv fix ${filePath}
13566
14157
  process.exit(1);
13567
14158
  }
13568
14159
  const backupPath = writeInPlaceWithBackup(filePath, result.fixed);
13569
- 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)})`));
13570
14161
  saveLastEditSession({
13571
14162
  original_path: filePath,
13572
14163
  edited_path: filePath,
@@ -13574,7 +14165,7 @@ hyv fix ${filePath}
13574
14165
  edited_text: result.fixed,
13575
14166
  profile: options.profile
13576
14167
  });
13577
- 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"));
13578
14169
  } else if (filePath === "stdin" || !options.inPlace) {
13579
14170
  process.stdout.write("\n" + result.fixed + "\n");
13580
14171
  if (filePath !== "stdin") {
@@ -13590,14 +14181,14 @@ hyv fix ${filePath}
13590
14181
  }
13591
14182
  await maybeShowLimitedModeHint(!!profile);
13592
14183
  } catch (error) {
13593
- console.error(import_chalk19.default.red(`Error: ${error.message}`));
14184
+ console.error(import_chalk20.default.red(`Error: ${error.message}`));
13594
14185
  process.exit(1);
13595
14186
  }
13596
14187
  });
13597
14188
  }
13598
14189
 
13599
14190
  // src/commands/check.ts
13600
- var import_chalk20 = __toESM(require_source());
14191
+ var import_chalk21 = __toESM(require_source());
13601
14192
  init_pipeline();
13602
14193
  init_local_profile();
13603
14194
  init_access();
@@ -13607,20 +14198,20 @@ function registerCheckCommand(program3) {
13607
14198
  const profile = await loadProfileForCommand(options.profile);
13608
14199
  let inputText = text;
13609
14200
  if (text === "-") {
13610
- const fs26 = require("fs");
14201
+ const fs27 = require("fs");
13611
14202
  if (process.stdin.isTTY) {
13612
- 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."));
13613
14204
  process.exit(1);
13614
14205
  }
13615
- inputText = fs26.readFileSync(0, "utf-8");
14206
+ inputText = fs27.readFileSync(0, "utf-8");
13616
14207
  }
13617
14208
  if (!inputText.trim()) {
13618
- console.error(import_chalk20.default.red("No text provided."));
14209
+ console.error(import_chalk21.default.red("No text provided."));
13619
14210
  process.exit(1);
13620
14211
  }
13621
- const fs25 = require("fs");
13622
- if (text !== "-" && fs25.existsSync(text)) {
13623
- console.log(import_chalk20.default.yellow(`"${text}" looks like a file. Did you mean: hyv scan ${text}`));
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}`));
13624
14215
  process.exit(1);
13625
14216
  }
13626
14217
  const result = runPipeline(inputText, profile, false);
@@ -13644,30 +14235,30 @@ function registerCheckCommand(program3) {
13644
14235
  }, null, 2));
13645
14236
  } else {
13646
14237
  if (result.stats.totalSignals === 0) {
13647
- console.log(import_chalk20.default.green("\n\u2713 Clean \u2014 no AI patterns found."));
13648
- console.log(import_chalk20.default.dim(` score: ${result.score}/100`));
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`));
13649
14240
  } else {
13650
14241
  console.log("");
13651
14242
  printGroupedSignals(result.signalMap.signals.slice(0, 15), profile);
13652
14243
  if (result.signalMap.signals.length > 15) {
13653
- 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`));
13654
14245
  }
13655
- console.log(import_chalk20.default.yellow(`
14246
+ console.log(import_chalk21.default.yellow(`
13656
14247
  ${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow)`));
13657
- console.log(import_chalk20.default.dim(` score: ${result.score}/100`));
14248
+ console.log(import_chalk21.default.dim(` score: ${result.score}/100`));
13658
14249
  }
13659
14250
  await maybeShowLimitedModeHint(!!profile);
13660
14251
  }
13661
14252
  process.exit(result.stats.totalSignals > 0 ? 1 : 0);
13662
14253
  } catch (error) {
13663
- console.error(import_chalk20.default.red(`Error: ${error.message}`));
14254
+ console.error(import_chalk21.default.red(`Error: ${error.message}`));
13664
14255
  process.exit(1);
13665
14256
  }
13666
14257
  });
13667
14258
  }
13668
14259
 
13669
14260
  // src/commands/score.ts
13670
- var import_chalk21 = __toESM(require_source());
14261
+ var import_chalk22 = __toESM(require_source());
13671
14262
  init_pipeline();
13672
14263
  init_local_profile();
13673
14264
  function registerScoreCommand(program3) {
@@ -13692,14 +14283,14 @@ function registerScoreCommand(program3) {
13692
14283
  process.exit(1);
13693
14284
  }
13694
14285
  } catch (error) {
13695
- console.error(import_chalk21.default.red(`Error: ${error.message}`));
14286
+ console.error(import_chalk22.default.red(`Error: ${error.message}`));
13696
14287
  process.exit(1);
13697
14288
  }
13698
14289
  });
13699
14290
  }
13700
14291
 
13701
14292
  // src/commands/diff.ts
13702
- var import_chalk22 = __toESM(require_source());
14293
+ var import_chalk23 = __toESM(require_source());
13703
14294
  init_pipeline();
13704
14295
  init_local_profile();
13705
14296
  function registerDiffCommand(program3) {
@@ -13709,9 +14300,9 @@ function registerDiffCommand(program3) {
13709
14300
  const { text, path: filePath } = readText(file);
13710
14301
  const result = runPipeline(text, profile, true);
13711
14302
  if (result.changes.length === 0) {
13712
- 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."));
13713
14304
  if (result.stats.needsLLM > 0) {
13714
- 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`));
13715
14306
  }
13716
14307
  return;
13717
14308
  }
@@ -13726,42 +14317,42 @@ function registerDiffCommand(program3) {
13726
14317
  const originalLines = text.split("\n");
13727
14318
  const fixedLines = result.fixed.split("\n");
13728
14319
  const contextN = parseInt(options.context, 10);
13729
- console.log(import_chalk22.default.dim(`--- ${filePath}`));
13730
- 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)`));
13731
14322
  for (const change of result.changes) {
13732
14323
  const lineIdx = change.line - 1;
13733
14324
  const start = Math.max(0, lineIdx - contextN);
13734
14325
  const end = Math.min(originalLines.length, lineIdx + contextN + 1);
13735
- 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} @@`));
13736
14327
  for (let i = start; i < end; i++) {
13737
14328
  if (i === lineIdx) {
13738
- console.log(import_chalk22.default.red(`-${originalLines[i]}`));
13739
- 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]}`));
13740
14331
  } else {
13741
- console.log(import_chalk22.default.dim(` ${originalLines[i]}`));
14332
+ console.log(import_chalk23.default.dim(` ${originalLines[i]}`));
13742
14333
  }
13743
14334
  }
13744
14335
  }
13745
- console.log(import_chalk22.default.green(`
14336
+ console.log(import_chalk23.default.green(`
13746
14337
  ${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} available`));
13747
- 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`));
13748
14339
  if (options.apply && filePath !== "stdin") {
13749
- const fs25 = require("fs");
13750
- const path24 = require("path");
14340
+ const fs26 = require("fs");
14341
+ const path25 = require("path");
13751
14342
  const backupPath = filePath + ".bak";
13752
- fs25.copyFileSync(filePath, backupPath);
13753
- fs25.writeFileSync(filePath, result.fixed);
13754
- 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)}`));
13755
14346
  }
13756
14347
  } catch (error) {
13757
- console.error(import_chalk22.default.red(`Error: ${error.message}`));
14348
+ console.error(import_chalk23.default.red(`Error: ${error.message}`));
13758
14349
  process.exit(1);
13759
14350
  }
13760
14351
  });
13761
14352
  }
13762
14353
 
13763
14354
  // src/commands/rules.ts
13764
- var import_chalk23 = __toESM(require_source());
14355
+ var import_chalk24 = __toESM(require_source());
13765
14356
  init_config();
13766
14357
  var RULE_CATALOG = [
13767
14358
  // AI Overused Words
@@ -13855,7 +14446,7 @@ function registerRulesCommand(program3) {
13855
14446
  for (const id of ids)
13856
14447
  disabledRules.delete(id);
13857
14448
  writeConfig({ ...config, disabled_rules: [...disabledRules] });
13858
- console.log(import_chalk23.default.green(`
14449
+ console.log(import_chalk24.default.green(`
13859
14450
  \u2713 Enabled: ${ids.join(", ")}`));
13860
14451
  return;
13861
14452
  }
@@ -13864,13 +14455,13 @@ function registerRulesCommand(program3) {
13864
14455
  for (const id of ids)
13865
14456
  disabledRules.add(id);
13866
14457
  writeConfig({ ...config, disabled_rules: [...disabledRules] });
13867
- console.log(import_chalk23.default.green(`
14458
+ console.log(import_chalk24.default.green(`
13868
14459
  \u2713 Disabled: ${ids.join(", ")}`));
13869
14460
  return;
13870
14461
  }
13871
14462
  if (options.reset) {
13872
14463
  writeConfig({ ...config, disabled_rules: [] });
13873
- 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)"));
13874
14465
  return;
13875
14466
  }
13876
14467
  let rules = [...RULE_CATALOG];
@@ -13898,36 +14489,36 @@ function registerRulesCommand(program3) {
13898
14489
  }
13899
14490
  console.log("");
13900
14491
  for (const [cat, catRules] of categories) {
13901
- console.log(import_chalk23.default.bold(` ${cat} rules (${catRules.length})
14492
+ console.log(import_chalk24.default.bold(` ${cat} rules (${catRules.length})
13902
14493
  `));
13903
- console.log(import_chalk23.default.dim(" ID Sev AutoFix Status"));
13904
- console.log(import_chalk23.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
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"));
13905
14496
  for (const rule of catRules) {
13906
- const sev = rule.severity === "red" ? import_chalk23.default.red("red") : import_chalk23.default.yellow("yel");
13907
- const fix = rule.autoFixable ? import_chalk23.default.green(" \u2713") : import_chalk23.default.dim(" \u2717");
13908
- const enabled2 = disabledRules.has(rule.id) ? import_chalk23.default.red(" \u2717 disabled") : import_chalk23.default.green(" \u2713 enabled");
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");
13909
14500
  const id = rule.id.padEnd(24);
13910
- console.log(` ${import_chalk23.default.dim(id)} ${sev} ${fix} ${enabled2}`);
14501
+ console.log(` ${import_chalk24.default.dim(id)} ${sev} ${fix} ${enabled2}`);
13911
14502
  }
13912
14503
  console.log("");
13913
14504
  }
13914
14505
  const enabled = rules.filter((r) => !disabledRules.has(r.id)).length;
13915
14506
  const disabled = rules.length - enabled;
13916
- console.log(import_chalk23.default.dim(` ${rules.length} rules, ${enabled} enabled, ${disabled} disabled`));
13917
- console.log(import_chalk23.default.dim(` toggle: hyv rules --disable <id>`));
13918
- console.log(import_chalk23.default.dim(` reset: hyv rules --reset
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
13919
14510
  `));
13920
14511
  } catch (error) {
13921
- console.error(import_chalk23.default.red(`Error: ${error.message}`));
14512
+ console.error(import_chalk24.default.red(`Error: ${error.message}`));
13922
14513
  process.exit(1);
13923
14514
  }
13924
14515
  });
13925
14516
  }
13926
14517
 
13927
14518
  // src/commands/batch.ts
13928
- var import_chalk24 = __toESM(require_source());
13929
- var fs16 = __toESM(require("fs"));
13930
- var path17 = __toESM(require("path"));
14519
+ var import_chalk25 = __toESM(require_source());
14520
+ var fs17 = __toESM(require("fs"));
14521
+ var path18 = __toESM(require("path"));
13931
14522
  init_pipeline();
13932
14523
  init_local_profile();
13933
14524
  function registerBatchCommand(program3) {
@@ -13940,7 +14531,7 @@ function registerBatchCommand(program3) {
13940
14531
  nodir: true
13941
14532
  });
13942
14533
  if (files.length === 0) {
13943
- console.log(import_chalk24.default.yellow(`
14534
+ console.log(import_chalk25.default.yellow(`
13944
14535
  No files matching: ${pattern}`));
13945
14536
  return;
13946
14537
  }
@@ -13954,16 +14545,16 @@ No files matching: ${pattern}`));
13954
14545
  process.exit(1);
13955
14546
  }
13956
14547
  if (options.format === "text") {
13957
- console.log(import_chalk24.default.dim(`
14548
+ console.log(import_chalk25.default.dim(`
13958
14549
  scanning ${files.length} file${files.length === 1 ? "" : "s"}...
13959
14550
  `));
13960
14551
  }
13961
14552
  const results = [];
13962
14553
  for (const file of files) {
13963
- const absPath = path17.resolve(file);
13964
- if (!fs16.existsSync(absPath))
14554
+ const absPath = path18.resolve(file);
14555
+ if (!fs17.existsSync(absPath))
13965
14556
  continue;
13966
- const text = fs16.readFileSync(absPath, "utf-8");
14557
+ const text = fs17.readFileSync(absPath, "utf-8");
13967
14558
  const result = runPipeline(text, profile, options.fix || false);
13968
14559
  results.push({
13969
14560
  file,
@@ -13993,23 +14584,23 @@ No files matching: ${pattern}`));
13993
14584
  }
13994
14585
  } else {
13995
14586
  for (const r of results) {
13996
- const icon = r.issues > 0 ? import_chalk24.default.red("\u25CF") : import_chalk24.default.green("\u25CB");
13997
- const score = r.score < 60 ? import_chalk24.default.red(r.score) : r.score < 80 ? import_chalk24.default.yellow(r.score) : import_chalk24.default.green(r.score);
13998
- const issueStr = r.issues > 0 ? import_chalk24.default.red(`${r.issues} issues`) : import_chalk24.default.green("clean");
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");
13999
14590
  console.log(` ${icon} ${r.file.padEnd(40)} ${issueStr.padEnd(20)} score: ${score}`);
14000
14591
  }
14001
14592
  const withIssues = results.filter((r) => r.issues > 0).length;
14002
14593
  const totalIssues = results.reduce((sum, r) => sum + r.issues, 0);
14003
14594
  const worst = results.reduce((min, r) => r.score < min.score ? r : min, results[0]);
14004
- console.log(import_chalk24.default.dim(`
14595
+ console.log(import_chalk25.default.dim(`
14005
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`));
14006
- 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`));
14007
14598
  if (withIssues > 0) {
14008
- 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)`));
14009
14600
  }
14010
14601
  if (options.fix && options.inPlace) {
14011
14602
  const fixed = results.filter((r) => r.autoFixes > 0);
14012
- console.log(import_chalk24.default.green(`
14603
+ console.log(import_chalk25.default.green(`
14013
14604
  \u2713 ${fixed.reduce((sum, r) => sum + r.autoFixes, 0)} auto-fixes applied across ${fixed.length} files`));
14014
14605
  }
14015
14606
  }
@@ -14021,25 +14612,25 @@ No files matching: ${pattern}`));
14021
14612
  if (belowThreshold)
14022
14613
  process.exit(1);
14023
14614
  } catch (error) {
14024
- console.error(import_chalk24.default.red(`Error: ${error.message}`));
14615
+ console.error(import_chalk25.default.red(`Error: ${error.message}`));
14025
14616
  process.exit(1);
14026
14617
  }
14027
14618
  });
14028
14619
  }
14029
14620
 
14030
14621
  // src/commands/watch.ts
14031
- var import_chalk25 = __toESM(require_source());
14032
- var fs17 = __toESM(require("fs"));
14033
- var path18 = __toESM(require("path"));
14622
+ var import_chalk26 = __toESM(require_source());
14623
+ var fs18 = __toESM(require("fs"));
14624
+ var path19 = __toESM(require("path"));
14034
14625
  init_pipeline();
14035
14626
  init_local_profile();
14036
14627
  function registerWatchCommand(program3) {
14037
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) => {
14038
14629
  try {
14039
14630
  const profile = await loadProfileForCommand(options.profile);
14040
- const absPath = path18.resolve(file);
14041
- if (!fs17.existsSync(absPath)) {
14042
- 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}`));
14043
14634
  process.exit(1);
14044
14635
  }
14045
14636
  if (options.command === "fix") {
@@ -14051,59 +14642,59 @@ function registerWatchCommand(program3) {
14051
14642
  if (!confirmed)
14052
14643
  process.exit(1);
14053
14644
  }
14054
- console.log(import_chalk25.default.dim(`
14645
+ console.log(import_chalk26.default.dim(`
14055
14646
  watching ${absPath}`));
14056
- console.log(import_chalk25.default.dim(`command: ${options.command} debounce: ${options.debounce}ms`));
14057
- console.log(import_chalk25.default.dim("ctrl+c to stop\n"));
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"));
14058
14649
  let debounceTimer = null;
14059
14650
  const debounceMs = parseInt(options.debounce, 10);
14060
14651
  let scanCount = 0;
14061
14652
  let lastScore = 0;
14062
14653
  let ignoreWatchUntil = 0;
14063
14654
  const runScan = () => {
14064
- const text = fs17.readFileSync(absPath, "utf-8");
14655
+ const text = fs18.readFileSync(absPath, "utf-8");
14065
14656
  const result = runPipeline(text, profile, options.command === "fix");
14066
14657
  scanCount++;
14067
14658
  const now = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
14068
- 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...`));
14069
14660
  if (options.command === "score") {
14070
14661
  const score = result.score;
14071
- 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;
14072
14663
  console.log(` ${color(score + "/100")}`);
14073
14664
  if (lastScore > 0 && score !== lastScore) {
14074
14665
  const delta = score - lastScore;
14075
- 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`));
14076
14667
  }
14077
14668
  lastScore = score;
14078
14669
  } else if (options.command === "fix") {
14079
14670
  if (result.changes.length > 0) {
14080
14671
  for (const change of result.changes) {
14081
- console.log(import_chalk25.default.dim(` Line ${change.line}: `) + import_chalk25.default.red(change.before) + import_chalk25.default.dim(" \u2192 ") + import_chalk25.default.green(change.after));
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));
14082
14673
  }
14083
14674
  ignoreWatchUntil = Date.now() + debounceMs + 200;
14084
14675
  writeInPlaceWithBackup(absPath, result.fixed);
14085
- 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`));
14086
14677
  } else {
14087
- console.log(import_chalk25.default.green(" \u2713 no auto-fixable issues"));
14678
+ console.log(import_chalk26.default.green(" \u2713 no auto-fixable issues"));
14088
14679
  }
14089
14680
  } else {
14090
14681
  if (result.stats.totalSignals === 0) {
14091
- 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`));
14092
14683
  } else {
14093
- 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`));
14094
14685
  for (const signal of result.signalMap.signals.slice(0, 3)) {
14095
- 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");
14096
14687
  console.log(` ${sev} line ${signal.line}: ${signal.id} \u2014 ${signal.suggestion}`);
14097
14688
  }
14098
14689
  if (result.signalMap.signals.length > 3) {
14099
- 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`));
14100
14691
  }
14101
14692
  }
14102
14693
  }
14103
14694
  console.log("");
14104
14695
  };
14105
14696
  runScan();
14106
- fs17.watch(absPath, (eventType) => {
14697
+ fs18.watch(absPath, (eventType) => {
14107
14698
  if (eventType !== "change")
14108
14699
  return;
14109
14700
  if (Date.now() < ignoreWatchUntil)
@@ -14113,22 +14704,22 @@ watching ${absPath}`));
14113
14704
  debounceTimer = setTimeout(runScan, debounceMs);
14114
14705
  });
14115
14706
  process.on("SIGINT", () => {
14116
- console.log(import_chalk25.default.dim(`
14707
+ console.log(import_chalk26.default.dim(`
14117
14708
  ${scanCount} scans completed. exiting.`));
14118
14709
  process.exit(0);
14119
14710
  });
14120
14711
  await new Promise(() => {
14121
14712
  });
14122
14713
  } catch (error) {
14123
- console.error(import_chalk25.default.red(`Error: ${error.message}`));
14714
+ console.error(import_chalk26.default.red(`Error: ${error.message}`));
14124
14715
  process.exit(1);
14125
14716
  }
14126
14717
  });
14127
14718
  }
14128
14719
 
14129
14720
  // src/commands/demo.ts
14130
- var import_chalk26 = __toESM(require_source());
14131
- var fs18 = __toESM(require("fs"));
14721
+ var import_chalk27 = __toESM(require_source());
14722
+ var fs19 = __toESM(require("fs"));
14132
14723
  var SAMPLES = {
14133
14724
  linkedin: {
14134
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.
@@ -14218,46 +14809,46 @@ function registerDemoCommand(program3) {
14218
14809
  const style = options.clean ? "clean" : options.style;
14219
14810
  const sample = SAMPLES[style];
14220
14811
  if (!sample) {
14221
- 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(", ")}`));
14222
14813
  process.exit(1);
14223
14814
  }
14224
14815
  if (options.output) {
14225
14816
  const outputPath = require("path").resolve(options.output);
14226
- fs18.writeFileSync(outputPath, sample.text);
14227
- console.log(import_chalk26.default.green(`
14817
+ fs19.writeFileSync(outputPath, sample.text);
14818
+ console.log(import_chalk27.default.green(`
14228
14819
  \u2713 Sample written to ${outputPath}`));
14229
14820
  if (sample.patterns.length > 0) {
14230
- 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(", ")}...`));
14231
14822
  }
14232
- console.log(import_chalk26.default.dim(`
14823
+ console.log(import_chalk27.default.dim(`
14233
14824
  Scan it: hyv scan ${options.output}`));
14234
- 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}`));
14235
14826
  } else {
14236
- console.log(import_chalk26.default.bold(`
14827
+ console.log(import_chalk27.default.bold(`
14237
14828
  \u2500\u2500\u2500 sample (${style}) \u2500\u2500\u2500
14238
14829
  `));
14239
14830
  console.log(sample.text);
14240
- console.log(import_chalk26.default.dim(`
14831
+ console.log(import_chalk27.default.dim(`
14241
14832
  \u2500\u2500\u2500 end \u2500\u2500\u2500`));
14242
14833
  if (sample.patterns.length > 0) {
14243
- console.log(import_chalk26.default.dim(`
14834
+ console.log(import_chalk27.default.dim(`
14244
14835
  ${sample.patterns.length} AI patterns embedded: ${sample.patterns.slice(0, 8).join(", ")}...`));
14245
14836
  }
14246
- console.log(import_chalk26.default.dim(`
14837
+ console.log(import_chalk27.default.dim(`
14247
14838
  Save to file: hyv demo --output demo.md`));
14248
- console.log(import_chalk26.default.dim(` Scan it: hyv scan demo.md`));
14249
- console.log(import_chalk26.default.dim(` Fix it: hyv fix demo.md
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
14250
14841
  `));
14251
14842
  }
14252
14843
  } catch (error) {
14253
- console.error(import_chalk26.default.red(`Error: ${error.message}`));
14844
+ console.error(import_chalk27.default.red(`Error: ${error.message}`));
14254
14845
  process.exit(1);
14255
14846
  }
14256
14847
  });
14257
14848
  }
14258
14849
 
14259
14850
  // src/commands/open.ts
14260
- var import_chalk27 = __toESM(require_source());
14851
+ var import_chalk28 = __toESM(require_source());
14261
14852
  var PAGES = {
14262
14853
  dashboard: "https://holdyourvoice.com/dashboard",
14263
14854
  profiles: "https://holdyourvoice.com/dashboard",
@@ -14274,12 +14865,12 @@ function registerOpenCommand(program3) {
14274
14865
  return;
14275
14866
  }
14276
14867
  const open3 = (await Promise.resolve().then(() => __toESM(require_open()))).default;
14277
- console.log(import_chalk27.default.dim(`
14868
+ console.log(import_chalk28.default.dim(`
14278
14869
  Opening ${url}...`));
14279
14870
  await open3(url);
14280
- console.log(import_chalk27.default.green(" \u2713 Opened in browser\n"));
14871
+ console.log(import_chalk28.default.green(" \u2713 Opened in browser\n"));
14281
14872
  } catch (error) {
14282
- console.error(import_chalk27.default.red(`Error: ${error.message}`));
14873
+ console.error(import_chalk28.default.red(`Error: ${error.message}`));
14283
14874
  process.exit(1);
14284
14875
  }
14285
14876
  });
@@ -14289,46 +14880,67 @@ function registerOpenCommand(program3) {
14289
14880
  init_welcome();
14290
14881
 
14291
14882
  // src/lib/onboarding.ts
14292
- var fs19 = __toESM(require("fs"));
14293
- var path19 = __toESM(require("path"));
14294
- var os6 = __toESM(require("os"));
14295
- var hyvDir = path19.join(os6.homedir(), ".hyv");
14296
- 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");
14297
14888
  function hasCompletedOnboarding() {
14298
14889
  try {
14299
- return fs19.existsSync(onboardingFile);
14890
+ return fs20.existsSync(onboardingFile);
14300
14891
  } catch {
14301
14892
  return false;
14302
14893
  }
14303
14894
  }
14304
14895
  function markOnboardingComplete(version) {
14305
- if (!fs19.existsSync(hyvDir))
14306
- fs19.mkdirSync(hyvDir, { recursive: true });
14307
- fs19.writeFileSync(
14896
+ if (!fs20.existsSync(hyvDir))
14897
+ fs20.mkdirSync(hyvDir, { recursive: true });
14898
+ fs20.writeFileSync(
14308
14899
  onboardingFile,
14309
14900
  JSON.stringify({ version, completed_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
14310
14901
  );
14311
14902
  }
14312
14903
 
14313
14904
  // src/commands/welcome.ts
14314
- var fs20 = __toESM(require("fs"));
14315
- var path20 = __toESM(require("path"));
14905
+ init_welcome_flow();
14906
+ var fs21 = __toESM(require("fs"));
14907
+ var path21 = __toESM(require("path"));
14316
14908
  function registerWelcomeCommand(program3) {
14317
14909
  const pkgVersion3 = (() => {
14318
14910
  try {
14319
- const pkgPath3 = path20.resolve(__dirname, "..", "package.json");
14320
- 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;
14321
14913
  } catch {
14322
14914
  return "0.0.0";
14323
14915
  }
14324
14916
  })();
14325
- 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) => {
14326
14918
  if (opts.quiet)
14327
14919
  return;
14328
- printWelcome({ skipDemo: opts.demo === false });
14329
- markOnboardingComplete(pkgVersion3);
14330
- };
14331
- 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
+ });
14332
14944
  program3.command("free").description("List everything free in the CLI and on the web").action(() => {
14333
14945
  console.log("\n" + formatFreeToolsList() + "\n");
14334
14946
  });
@@ -14336,12 +14948,15 @@ function registerWelcomeCommand(program3) {
14336
14948
  function getDemoText() {
14337
14949
  return formatDemoResult(runWelcomeDemo());
14338
14950
  }
14339
- function getWelcomeText() {
14340
- return buildWelcomeMessage({ skipDemo: false });
14951
+ function getWelcomeText(args2) {
14952
+ if (args2?.mode || args2?.step) {
14953
+ return getMcpWelcomeResponse(args2);
14954
+ }
14955
+ return buildWelcomeGuide({ forLlm: true });
14341
14956
  }
14342
14957
 
14343
14958
  // src/commands/content.ts
14344
- var import_chalk28 = __toESM(require_source());
14959
+ var import_chalk29 = __toESM(require_source());
14345
14960
  init_free_paid();
14346
14961
  var TEMPLATES = {
14347
14962
  cursor: {
@@ -14415,30 +15030,30 @@ ${COMMUNITY_URL}`
14415
15030
  function registerContentCommand(program3) {
14416
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) => {
14417
15032
  if (opts.list) {
14418
- console.log(import_chalk28.default.bold("\nhyv content templates\n"));
15033
+ console.log(import_chalk29.default.bold("\nhyv content templates\n"));
14419
15034
  for (const [key, t2] of Object.entries(TEMPLATES)) {
14420
- console.log(import_chalk28.default.dim(` ${key}`) + ` \u2014 ${t2.title}`);
15035
+ console.log(import_chalk29.default.dim(` ${key}`) + ` \u2014 ${t2.title}`);
14421
15036
  }
14422
15037
  console.log("");
14423
15038
  return;
14424
15039
  }
14425
15040
  const t = TEMPLATES[topic] || TEMPLATES.cursor;
14426
- console.log(import_chalk28.default.bold(`
15041
+ console.log(import_chalk29.default.bold(`
14427
15042
  ${t.title}
14428
15043
  `));
14429
15044
  console.log(t.body);
14430
- 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"));
14431
15046
  });
14432
15047
  }
14433
15048
 
14434
15049
  // src/commands/upgrade.ts
14435
- var import_chalk29 = __toESM(require_source());
15050
+ var import_chalk30 = __toESM(require_source());
14436
15051
  var import_child_process3 = require("child_process");
14437
15052
  init_version();
14438
15053
  function registerUpgradeCommand(program3) {
14439
15054
  program3.command("upgrade").description("Upgrade to the latest @holdyourvoice/hyv").option("--check", "Only check if an update is available").action(async (opts) => {
14440
15055
  const current = getCliVersion();
14441
- console.log(import_chalk29.default.dim(`
15056
+ console.log(import_chalk30.default.dim(`
14442
15057
  Current: ${getEngineLabel()}
14443
15058
  `));
14444
15059
  let latest = current;
@@ -14446,38 +15061,38 @@ Current: ${getEngineLabel()}
14446
15061
  const out = (0, import_child_process3.execSync)("npm view @holdyourvoice/hyv version", { encoding: "utf-8", timeout: 15e3 });
14447
15062
  latest = out.trim();
14448
15063
  } catch {
14449
- 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"));
14450
15065
  return;
14451
15066
  }
14452
15067
  const cmp = compareSemver(current, latest);
14453
15068
  if (cmp === 0) {
14454
- 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."));
14455
15070
  return;
14456
15071
  }
14457
15072
  if (cmp > 0) {
14458
- 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}).`));
14459
15074
  return;
14460
15075
  }
14461
- console.log(import_chalk29.default.cyan(`Update available: ${current} \u2192 ${latest}`));
15076
+ console.log(import_chalk30.default.cyan(`Update available: ${current} \u2192 ${latest}`));
14462
15077
  if (opts.check) {
14463
- 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"));
14464
15079
  return;
14465
15080
  }
14466
- console.log(import_chalk29.default.dim("Installing..."));
15081
+ console.log(import_chalk30.default.dim("Installing..."));
14467
15082
  try {
14468
15083
  (0, import_child_process3.execSync)("npm i -g @holdyourvoice/hyv@latest", { stdio: "inherit" });
14469
- console.log(import_chalk29.default.green("\n\u2713 Upgraded successfully. Restart your terminal.\n"));
15084
+ console.log(import_chalk30.default.green("\n\u2713 Upgraded successfully. Restart your terminal.\n"));
14470
15085
  } catch {
14471
- 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"));
14472
15087
  process.exit(1);
14473
15088
  }
14474
15089
  });
14475
15090
  }
14476
15091
 
14477
15092
  // src/mcp.ts
14478
- var fs21 = __toESM(require("fs"));
14479
- var path21 = __toESM(require("path"));
14480
- var os7 = __toESM(require("os"));
15093
+ var fs22 = __toESM(require("fs"));
15094
+ var path22 = __toESM(require("path"));
15095
+ var os8 = __toESM(require("os"));
14481
15096
  init_classifier();
14482
15097
  init_autofix();
14483
15098
  init_validator();
@@ -14488,20 +15103,20 @@ init_welcome();
14488
15103
  init_config();
14489
15104
  init_telemetry();
14490
15105
  init_access();
14491
- var VOICE_MD = path21.join(os7.homedir(), ".hyv", "voice.md");
15106
+ var VOICE_MD = path22.join(os8.homedir(), ".hyv", "voice.md");
14492
15107
  var MAX_RESPONSE_CHARS = 12e4;
14493
15108
  var MAX_SCAN_FILE_BYTES = 2 * 1024 * 1024;
14494
15109
  function readScanFile(filePath) {
14495
- const cwdReal = fs21.realpathSync(process.cwd());
14496
- const resolved = fs21.realpathSync(path21.resolve(filePath));
14497
- 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) {
14498
15113
  throw new Error(`file must be in the current working directory (${cwdReal})`);
14499
15114
  }
14500
- const stat = fs21.statSync(resolved);
15115
+ const stat = fs22.statSync(resolved);
14501
15116
  if (stat.size > MAX_SCAN_FILE_BYTES) {
14502
15117
  throw new Error(`file too large (max ${MAX_SCAN_FILE_BYTES} bytes)`);
14503
15118
  }
14504
- return fs21.readFileSync(resolved, "utf-8");
15119
+ return fs22.readFileSync(resolved, "utf-8");
14505
15120
  }
14506
15121
  function toolResultPayload(result) {
14507
15122
  const isError = result.startsWith("Error:") || result.startsWith("Unknown tool:");
@@ -14510,10 +15125,10 @@ function toolResultPayload(result) {
14510
15125
  ...isError ? { isError: true } : {}
14511
15126
  };
14512
15127
  }
14513
- var pkgPath = path21.resolve(__dirname, "..", "package.json");
15128
+ var pkgPath = path22.resolve(__dirname, "..", "package.json");
14514
15129
  var pkgVersion = (() => {
14515
15130
  try {
14516
- return JSON.parse(fs21.readFileSync(pkgPath, "utf-8")).version;
15131
+ return JSON.parse(fs22.readFileSync(pkgPath, "utf-8")).version;
14517
15132
  } catch {
14518
15133
  return "2.7.1";
14519
15134
  }
@@ -14742,8 +15357,8 @@ ${result.fixed}`;
14742
15357
  return `Error: ${err.message}`;
14743
15358
  }
14744
15359
  }
14745
- function toolWelcome() {
14746
- return getWelcomeText();
15360
+ function toolWelcome(args2 = {}) {
15361
+ return getWelcomeText(args2);
14747
15362
  }
14748
15363
  function toolListFreeTools() {
14749
15364
  return formatFreeToolsList();
@@ -14847,8 +15462,15 @@ async function toolClean(args2) {
14847
15462
  var TOOLS = [
14848
15463
  {
14849
15464
  name: "hyv_welcome",
14850
- 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.",
14851
- 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
+ }
14852
15474
  },
14853
15475
  {
14854
15476
  name: "hyv_list_free_tools",
@@ -14985,15 +15607,15 @@ var PROMPTS = [
14985
15607
  ];
14986
15608
  var HYV_STATUS_PROMPT = `Use hold your voice MCP tools:
14987
15609
 
14988
- 1. Call hyv_welcome or hyv_list_free_tools so the user knows what is free.
14989
- 2. Call hyv_profiles to list voice profiles (cached local or account).
14990
- 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.
14991
- 4. After the user accepts a big edit, suggest hyv reinforce --last (CLI) to strengthen their profile.
14992
- 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).
14993
15615
 
14994
15616
  Keep the answer compact.`;
14995
15617
  var TOOL_HANDLERS = {
14996
- hyv_welcome: () => toolWelcome(),
15618
+ hyv_welcome: toolWelcome,
14997
15619
  hyv_list_free_tools: () => toolListFreeTools(),
14998
15620
  hyv_demo: () => toolDemo(),
14999
15621
  hyv_rewrite: toolRewrite,
@@ -15087,7 +15709,7 @@ async function handleRequest(line) {
15087
15709
  }
15088
15710
  async function startMcpServer() {
15089
15711
  const access = await getAccessState().catch(() => null);
15090
- if (fs21.existsSync(VOICE_MD)) {
15712
+ if (fs22.existsSync(VOICE_MD)) {
15091
15713
  mcpLog("info", `voice profile: ${VOICE_MD}`);
15092
15714
  } else {
15093
15715
  mcpLog("info", "free local engine ready \u2014 no voice profile yet");
@@ -15119,142 +15741,142 @@ async function startMcpServer() {
15119
15741
  }
15120
15742
 
15121
15743
  // src/lib/mcp-setup.ts
15122
- var import_chalk30 = __toESM(require_source());
15123
- var fs22 = __toESM(require("fs"));
15124
- var path22 = __toESM(require("path"));
15125
- 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"));
15126
15748
  init_version();
15127
- var HOME2 = os8.homedir();
15749
+ var HOME2 = os9.homedir();
15128
15750
  var IS_WIN2 = process.platform === "win32";
15129
15751
  function claudeDesktopConfigPath() {
15130
15752
  if (IS_WIN2)
15131
- return path22.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
15753
+ return path23.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
15132
15754
  if (process.platform === "linux")
15133
- return path22.join(HOME2, ".config", "Claude", "claude_desktop_config.json");
15134
- 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");
15135
15757
  }
15136
15758
  function printMcpSetup() {
15137
- console.log(import_chalk30.default.bold("\nhold your voice \u2014 mcp setup\n"));
15138
- 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()}
15139
15761
  `));
15140
- console.log(import_chalk30.default.bold("Claude Desktop"));
15141
- console.log(import_chalk30.default.dim(" Add to claude_desktop_config.json \u2192 mcpServers.hyv:"));
15142
- console.log(import_chalk30.default.cyan(` ${mcpServerSnippet().split("\n").join("\n ")}`));
15143
- console.log(import_chalk30.default.dim(` Config: ${claudeDesktopConfigPath()}
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()}
15144
15766
  `));
15145
- console.log(import_chalk30.default.bold("Cursor"));
15146
- console.log(import_chalk30.default.dim(" MCP: ~/.cursor/mcp.json \u2192 mcpServers.hyv (same JSON as above)"));
15147
- console.log(import_chalk30.default.dim(" Rule: ~/.cursor/rules/hyv.mdc (alwaysApply \u2014 auto via postinstall)\n"));
15148
- console.log(import_chalk30.default.bold("Claude Code"));
15149
- console.log(import_chalk30.default.dim(" Command: ~/.claude/commands/hyv.md"));
15150
- console.log(import_chalk30.default.dim(" Skill: ~/.claude/skills/hold-your-voice/SKILL.md\n"));
15151
- console.log(import_chalk30.default.bold("Windsurf"));
15152
- console.log(import_chalk30.default.dim(" Rule: ~/.windsurf/rules/hyv.md (trigger: always_on)\n"));
15153
- console.log(import_chalk30.default.bold("Codex"));
15154
- console.log(import_chalk30.default.dim(" Instructions: ~/.codex/AGENTS.md (merged on install)\n"));
15155
- console.log(import_chalk30.default.bold("Command Code"));
15156
- console.log(import_chalk30.default.dim(" Skill: ~/.commandcode/skills/hyv/SKILL.md\n"));
15157
- console.log(import_chalk30.default.bold("ChatGPT"));
15158
- console.log(import_chalk30.default.dim(" hyv mcp --setup-chatgpt\n"));
15159
- console.log(import_chalk30.default.bold("Auto-configure"));
15160
- console.log(import_chalk30.default.dim(" hyv doctor --fix-agents"));
15161
- console.log(import_chalk30.default.dim(" HYV_AUTO_CONFIGURE_AGENTS=0 npm i -g @holdyourvoice/hyv (skip)\n"));
15162
- console.log(import_chalk30.default.bold("Verify"));
15163
- console.log(import_chalk30.default.dim(" hyv mcp --test"));
15164
- console.log(import_chalk30.default.dim(" HYV_TELEMETRY=1 hyv mcp (optional usage logging to ~/.hyv/telemetry/)\n"));
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"));
15165
15787
  }
15166
15788
  async function runMcpSelfTest() {
15167
- console.log(import_chalk30.default.bold("\nhold your voice \u2014 mcp self-test\n"));
15168
- 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()}
15169
15791
  `));
15170
15792
  let passed = 0;
15171
15793
  let failed = 0;
15172
15794
  const tools = getMcpToolNames();
15173
15795
  if (tools.length >= 10) {
15174
- 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`));
15175
15797
  passed++;
15176
15798
  } else {
15177
- 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}`));
15178
15800
  failed++;
15179
15801
  }
15180
15802
  const required = ["hyv_welcome", "hyv_scan", "hyv_analyze", "hyv_clean", "hyv_validate", "hyv_list_free_tools"];
15181
15803
  for (const name of required) {
15182
15804
  if (tools.includes(name)) {
15183
- console.log(import_chalk30.default.green(` \u2713 tool: ${name}`));
15805
+ console.log(import_chalk31.default.green(` \u2713 tool: ${name}`));
15184
15806
  passed++;
15185
15807
  } else {
15186
- console.log(import_chalk30.default.red(` \u2717 missing tool: ${name}`));
15808
+ console.log(import_chalk31.default.red(` \u2717 missing tool: ${name}`));
15187
15809
  failed++;
15188
15810
  }
15189
15811
  }
15190
15812
  try {
15191
15813
  const welcome = await invokeMcpTool("hyv_welcome", {});
15192
15814
  if (welcome.includes("Hold Your Voice") || welcome.includes("hold your voice")) {
15193
- console.log(import_chalk30.default.green(" \u2713 hyv_welcome responds"));
15815
+ console.log(import_chalk31.default.green(" \u2713 hyv_welcome responds"));
15194
15816
  passed++;
15195
15817
  } else {
15196
- console.log(import_chalk30.default.red(" \u2717 hyv_welcome unexpected output"));
15818
+ console.log(import_chalk31.default.red(" \u2717 hyv_welcome unexpected output"));
15197
15819
  failed++;
15198
15820
  }
15199
15821
  } catch (e) {
15200
- 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}`));
15201
15823
  failed++;
15202
15824
  }
15203
15825
  try {
15204
15826
  const demo = await invokeMcpTool("hyv_demo", {});
15205
15827
  if (demo.includes("Score") || demo.includes("issues")) {
15206
- console.log(import_chalk30.default.green(" \u2713 hyv_demo pipeline works"));
15828
+ console.log(import_chalk31.default.green(" \u2713 hyv_demo pipeline works"));
15207
15829
  passed++;
15208
15830
  } else {
15209
- console.log(import_chalk30.default.red(" \u2717 hyv_demo unexpected output"));
15831
+ console.log(import_chalk31.default.red(" \u2717 hyv_demo unexpected output"));
15210
15832
  failed++;
15211
15833
  }
15212
15834
  } catch (e) {
15213
- 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}`));
15214
15836
  failed++;
15215
15837
  }
15216
- const voiceMd = path22.join(HOME2, ".hyv", "voice.md");
15217
- if (fs22.existsSync(voiceMd)) {
15218
- console.log(import_chalk30.default.green(" \u2713 voice profile found"));
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"));
15219
15841
  passed++;
15220
15842
  } else {
15221
- 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)"));
15222
15844
  }
15223
15845
  if (getDemoText().length > 100) {
15224
- console.log(import_chalk30.default.green(" \u2713 demo content available"));
15846
+ console.log(import_chalk31.default.green(" \u2713 demo content available"));
15225
15847
  passed++;
15226
15848
  } else {
15227
- console.log(import_chalk30.default.red(" \u2717 demo content missing"));
15849
+ console.log(import_chalk31.default.red(" \u2717 demo content missing"));
15228
15850
  failed++;
15229
15851
  }
15230
15852
  try {
15231
15853
  const stdio = await testMcpStdioSubprocess();
15232
15854
  if (stdio.ok && (stdio.toolCount || 0) >= 10) {
15233
- 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)`));
15234
15856
  passed++;
15235
15857
  } else if (stdio.ok) {
15236
- 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`));
15237
15859
  failed++;
15238
15860
  } else {
15239
- console.log(import_chalk30.default.red(" \u2717 stdio MCP subprocess failed"));
15861
+ console.log(import_chalk31.default.red(" \u2717 stdio MCP subprocess failed"));
15240
15862
  failed++;
15241
15863
  }
15242
15864
  } catch (e) {
15243
- 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}`));
15244
15866
  failed++;
15245
15867
  }
15246
15868
  console.log("");
15247
15869
  if (failed === 0) {
15248
- console.log(import_chalk30.default.green(`\u2713 all checks passed (${passed})`));
15249
- 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"));
15250
15872
  return true;
15251
15873
  }
15252
- 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`));
15253
15875
  return false;
15254
15876
  }
15255
15877
 
15256
15878
  // src/commands/export.ts
15257
- var fs23 = __toESM(require("fs"));
15879
+ var fs24 = __toESM(require("fs"));
15258
15880
  init_api();
15259
15881
  var FORMATS = {
15260
15882
  claude: {
@@ -15341,7 +15963,7 @@ async function exportCommand(format, opts) {
15341
15963
  const prompt = fmt.wrap(detail.profile, detail.body);
15342
15964
  if (opts.output) {
15343
15965
  const outFile = opts.output.replace("{name}", profile.slug || profile.name);
15344
- fs23.writeFileSync(outFile, prompt);
15966
+ fs24.writeFileSync(outFile, prompt);
15345
15967
  results.push({ profile: profile.name, file: outFile });
15346
15968
  } else {
15347
15969
  results.push({ profile: profile.name, prompt });
@@ -15378,13 +16000,13 @@ async function exportCommand(format, opts) {
15378
16000
  // src/index.ts
15379
16001
  init_access();
15380
16002
  init_welcome();
15381
- var fs24 = __toESM(require("fs"));
15382
- var path23 = __toESM(require("path"));
16003
+ var fs25 = __toESM(require("fs"));
16004
+ var path24 = __toESM(require("path"));
15383
16005
  var program2 = new Command();
15384
- var pkgPath2 = path23.resolve(__dirname, "..", "package.json");
16006
+ var pkgPath2 = path24.resolve(__dirname, "..", "package.json");
15385
16007
  var pkgVersion2 = (() => {
15386
16008
  try {
15387
- return JSON.parse(fs24.readFileSync(pkgPath2, "utf-8")).version;
16009
+ return JSON.parse(fs25.readFileSync(pkgPath2, "utf-8")).version;
15388
16010
  } catch {
15389
16011
  return "2.7.1";
15390
16012
  }
@@ -15428,15 +16050,15 @@ program2.command("mcp").description("Start MCP server (for Claude Desktop and ot
15428
16050
  return;
15429
16051
  }
15430
16052
  if (opts.setupChatgpt) {
15431
- 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"));
15432
16054
  console.log("To connect HYV to ChatGPT:");
15433
- console.log(import_chalk31.default.dim(" 1. Go to ") + import_chalk31.default.cyan("https://chatgpt.com/#settings/Connectors"));
15434
- console.log(import_chalk31.default.dim(" 2. Add a new connector"));
15435
- console.log(import_chalk31.default.dim(" 3. For local MCP, use: ") + import_chalk31.default.cyan("hyv mcp"));
15436
- console.log(import_chalk31.default.dim(" 4. ChatGPT Desktop supports stdio MCP servers"));
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"));
15437
16059
  console.log("");
15438
- console.log(import_chalk31.default.dim("Note: The remote HTTP MCP endpoint is not yet available."));
15439
- console.log(import_chalk31.default.dim("Use the local stdio MCP server with Claude Desktop or Claude Code instead."));
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."));
15440
16062
  return;
15441
16063
  }
15442
16064
  startMcpServer();
@@ -15446,9 +16068,8 @@ program2.command("export").description("Export voice profile for LLMs").argument
15446
16068
  });
15447
16069
  var args = process.argv.slice(2);
15448
16070
  if (args.length === 0) {
15449
- const firstRun = !hasCompletedOnboarding();
15450
- printWelcome({ condensed: !firstRun });
15451
- if (firstRun)
16071
+ printWelcome({ condensed: true });
16072
+ if (!hasCompletedOnboarding())
15452
16073
  markOnboardingComplete(pkgVersion2);
15453
16074
  process.exit(0);
15454
16075
  }