@holdyourvoice/hyv 2.9.9 → 2.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/index.js +242 -190
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable CLI changes. Also mirrored to [holdyourvoice.com/changelog](https://holdyourvoice.com/changelog) for user-facing releases.
4
4
 
5
+ ## [2.9.10] — 2026-06-12
6
+
7
+ ### Fixed
8
+ - **Data safety** — welcome profile sync no longer overwrites enriched dashboard profiles (keywords, signature, rules, flashcard onboarding)
9
+ - Local profile cache backs up to `.hyv.bak` before overwrite; refuses empty content replacing real data
10
+ - `hyv sync` skips empty server profile payloads instead of wiping local cache
11
+
5
12
  ## [2.9.9] — 2026-06-12
6
13
 
7
14
  ### Fixed
package/dist/index.js CHANGED
@@ -974,7 +974,7 @@ var require_command = __commonJS({
974
974
  var EventEmitter = require("node:events").EventEmitter;
975
975
  var childProcess = require("node:child_process");
976
976
  var path26 = require("node:path");
977
- var fs28 = require("node:fs");
977
+ var fs29 = 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();
@@ -1917,12 +1917,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
1917
1917
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1918
1918
  function findFile(baseDir, baseName) {
1919
1919
  const localBin = path26.resolve(baseDir, baseName);
1920
- if (fs28.existsSync(localBin))
1920
+ if (fs29.existsSync(localBin))
1921
1921
  return localBin;
1922
1922
  if (sourceExt.includes(path26.extname(baseName)))
1923
1923
  return void 0;
1924
1924
  const foundExt = sourceExt.find(
1925
- (ext) => fs28.existsSync(`${localBin}${ext}`)
1925
+ (ext) => fs29.existsSync(`${localBin}${ext}`)
1926
1926
  );
1927
1927
  if (foundExt)
1928
1928
  return `${localBin}${foundExt}`;
@@ -1935,7 +1935,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1935
1935
  if (this._scriptPath) {
1936
1936
  let resolvedScriptPath;
1937
1937
  try {
1938
- resolvedScriptPath = fs28.realpathSync(this._scriptPath);
1938
+ resolvedScriptPath = fs29.realpathSync(this._scriptPath);
1939
1939
  } catch (err) {
1940
1940
  resolvedScriptPath = this._scriptPath;
1941
1941
  }
@@ -4611,6 +4611,44 @@ var require_source = __commonJS({
4611
4611
  }
4612
4612
  });
4613
4613
 
4614
+ // src/lib/data-safety.ts
4615
+ function shouldReplaceCachedContent(existing, incoming) {
4616
+ const next = incoming.trim();
4617
+ if (!next) {
4618
+ return !existing?.trim();
4619
+ }
4620
+ return true;
4621
+ }
4622
+ function backupProfileFileIfExists(profilePath) {
4623
+ if (!fs.existsSync(profilePath)) {
4624
+ return false;
4625
+ }
4626
+ try {
4627
+ fs.copyFileSync(profilePath, `${profilePath}.hyv.bak`);
4628
+ return true;
4629
+ } catch {
4630
+ return false;
4631
+ }
4632
+ }
4633
+ function safeWriteProfileFile(profilePath, content, existing) {
4634
+ if (!shouldReplaceCachedContent(existing, content)) {
4635
+ return { written: false, backedUp: false, reason: "empty-would-erase" };
4636
+ }
4637
+ if (existing !== null && existing === content) {
4638
+ return { written: false, backedUp: false, reason: "unchanged" };
4639
+ }
4640
+ const backedUp = backupProfileFileIfExists(profilePath);
4641
+ fs.writeFileSync(profilePath, content, { mode: 384 });
4642
+ return { written: true, backedUp };
4643
+ }
4644
+ var fs;
4645
+ var init_data_safety = __esm({
4646
+ "src/lib/data-safety.ts"() {
4647
+ "use strict";
4648
+ fs = __toESM(require("fs"));
4649
+ }
4650
+ });
4651
+
4614
4652
  // src/lib/config.ts
4615
4653
  function validateApiBase(raw) {
4616
4654
  const trimmed = raw.replace(/\/$/, "");
@@ -4697,11 +4735,11 @@ function profilePathForName(name) {
4697
4735
  function ensureHyvDir() {
4698
4736
  const dirs = [HYV_DIR, PROFILES_DIR, CACHE_DIR, QUEUE_DIR];
4699
4737
  for (const dir of dirs) {
4700
- if (!fs.existsSync(dir)) {
4701
- fs.mkdirSync(dir, { recursive: true, mode: 448 });
4738
+ if (!fs2.existsSync(dir)) {
4739
+ fs2.mkdirSync(dir, { recursive: true, mode: 448 });
4702
4740
  } else {
4703
4741
  try {
4704
- fs.chmodSync(dir, 448);
4742
+ fs2.chmodSync(dir, 448);
4705
4743
  } catch {
4706
4744
  }
4707
4745
  }
@@ -4709,33 +4747,33 @@ function ensureHyvDir() {
4709
4747
  }
4710
4748
  function appendSecureLine(filePath, line, dir) {
4711
4749
  if (dir) {
4712
- if (!fs.existsSync(dir))
4713
- fs.mkdirSync(dir, { recursive: true, mode: 448 });
4750
+ if (!fs2.existsSync(dir))
4751
+ fs2.mkdirSync(dir, { recursive: true, mode: 448 });
4714
4752
  else {
4715
4753
  try {
4716
- fs.chmodSync(dir, 448);
4754
+ fs2.chmodSync(dir, 448);
4717
4755
  } catch {
4718
4756
  }
4719
4757
  }
4720
4758
  }
4721
- if (!fs.existsSync(filePath)) {
4722
- fs.writeFileSync(filePath, line, { mode: 384 });
4759
+ if (!fs2.existsSync(filePath)) {
4760
+ fs2.writeFileSync(filePath, line, { mode: 384 });
4723
4761
  return;
4724
4762
  }
4725
4763
  try {
4726
- fs.chmodSync(filePath, 384);
4764
+ fs2.chmodSync(filePath, 384);
4727
4765
  } catch {
4728
4766
  }
4729
- fs.appendFileSync(filePath, line);
4767
+ fs2.appendFileSync(filePath, line);
4730
4768
  }
4731
4769
  function isInitialized() {
4732
- return fs.existsSync(AUTH_FILE);
4770
+ return fs2.existsSync(AUTH_FILE);
4733
4771
  }
4734
4772
  function readAuth() {
4735
4773
  try {
4736
- if (!fs.existsSync(AUTH_FILE))
4774
+ if (!fs2.existsSync(AUTH_FILE))
4737
4775
  return null;
4738
- const data = fs.readFileSync(AUTH_FILE, "utf-8");
4776
+ const data = fs2.readFileSync(AUTH_FILE, "utf-8");
4739
4777
  return JSON.parse(data);
4740
4778
  } catch {
4741
4779
  return null;
@@ -4743,13 +4781,13 @@ function readAuth() {
4743
4781
  }
4744
4782
  function writeAuth(auth) {
4745
4783
  ensureHyvDir();
4746
- fs.writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), { mode: 384 });
4784
+ fs2.writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), { mode: 384 });
4747
4785
  }
4748
4786
  function readConfig() {
4749
4787
  try {
4750
- if (!fs.existsSync(CONFIG_FILE))
4788
+ if (!fs2.existsSync(CONFIG_FILE))
4751
4789
  return {};
4752
- const data = fs.readFileSync(CONFIG_FILE, "utf-8");
4790
+ const data = fs2.readFileSync(CONFIG_FILE, "utf-8");
4753
4791
  return JSON.parse(data);
4754
4792
  } catch {
4755
4793
  return {};
@@ -4757,7 +4795,7 @@ function readConfig() {
4757
4795
  }
4758
4796
  function writeConfig(config) {
4759
4797
  ensureHyvDir();
4760
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 384 });
4798
+ fs2.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 384 });
4761
4799
  }
4762
4800
  function getToken() {
4763
4801
  const auth = readAuth();
@@ -4773,9 +4811,9 @@ function getToken() {
4773
4811
  }
4774
4812
  function listCachedProfiles() {
4775
4813
  try {
4776
- if (!fs.existsSync(PROFILES_DIR))
4814
+ if (!fs2.existsSync(PROFILES_DIR))
4777
4815
  return [];
4778
- return fs.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
4816
+ return fs2.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
4779
4817
  } catch {
4780
4818
  return [];
4781
4819
  }
@@ -4783,9 +4821,9 @@ function listCachedProfiles() {
4783
4821
  function readCachedProfile(name) {
4784
4822
  const profilePath = profilePathForName(name);
4785
4823
  try {
4786
- if (!fs.existsSync(profilePath))
4824
+ if (!fs2.existsSync(profilePath))
4787
4825
  return null;
4788
- return fs.readFileSync(profilePath, "utf-8");
4826
+ return fs2.readFileSync(profilePath, "utf-8");
4789
4827
  } catch (err) {
4790
4828
  if (err.message?.includes("Invalid profile name"))
4791
4829
  throw err;
@@ -4794,15 +4832,19 @@ function readCachedProfile(name) {
4794
4832
  }
4795
4833
  function writeCachedProfile(name, content) {
4796
4834
  const profilePath = profilePathForName(name);
4797
- fs.writeFileSync(profilePath, content, { mode: 384 });
4835
+ const existing = fs2.existsSync(profilePath) ? fs2.readFileSync(profilePath, "utf-8") : null;
4836
+ const result = safeWriteProfileFile(profilePath, content, existing);
4837
+ if (!result.written && result.reason === "empty-would-erase") {
4838
+ throw new Error("Refusing to replace profile with empty content");
4839
+ }
4798
4840
  }
4799
4841
  function getQueuedSignals() {
4800
4842
  try {
4801
- if (!fs.existsSync(QUEUE_DIR))
4843
+ if (!fs2.existsSync(QUEUE_DIR))
4802
4844
  return [];
4803
- const files = fs.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
4845
+ const files = fs2.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
4804
4846
  return files.map((f) => {
4805
- const data = fs.readFileSync(path.join(QUEUE_DIR, f), "utf-8");
4847
+ const data = fs2.readFileSync(path.join(QUEUE_DIR, f), "utf-8");
4806
4848
  return JSON.parse(data);
4807
4849
  });
4808
4850
  } catch {
@@ -4813,29 +4855,30 @@ function queueSignal(signal) {
4813
4855
  ensureHyvDir();
4814
4856
  const id = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
4815
4857
  const filePath = path.join(QUEUE_DIR, `${id}.json`);
4816
- fs.writeFileSync(filePath, JSON.stringify(signal, null, 2), { mode: 384 });
4858
+ fs2.writeFileSync(filePath, JSON.stringify(signal, null, 2), { mode: 384 });
4817
4859
  }
4818
4860
  function saveLastEditSession(session) {
4819
4861
  ensureHyvDir();
4820
4862
  const data = { ...session, saved_at: (/* @__PURE__ */ new Date()).toISOString() };
4821
- fs.writeFileSync(LAST_SESSION_FILE, JSON.stringify(data, null, 2), { mode: 384 });
4863
+ fs2.writeFileSync(LAST_SESSION_FILE, JSON.stringify(data, null, 2), { mode: 384 });
4822
4864
  }
4823
4865
  function readLastEditSession() {
4824
4866
  try {
4825
- if (!fs.existsSync(LAST_SESSION_FILE))
4867
+ if (!fs2.existsSync(LAST_SESSION_FILE))
4826
4868
  return null;
4827
- return JSON.parse(fs.readFileSync(LAST_SESSION_FILE, "utf-8"));
4869
+ return JSON.parse(fs2.readFileSync(LAST_SESSION_FILE, "utf-8"));
4828
4870
  } catch {
4829
4871
  return null;
4830
4872
  }
4831
4873
  }
4832
- var fs, path, os, HYV_DIR, AUTH_FILE, CONFIG_FILE, PROFILES_DIR, CACHE_DIR, QUEUE_DIR, LAST_SESSION_FILE, ALLOWED_API_HOSTS, ALLOWED_OAUTH_HOSTS, API_BASE, PROFILE_NAME_RE;
4874
+ var fs2, path, os, HYV_DIR, AUTH_FILE, CONFIG_FILE, PROFILES_DIR, CACHE_DIR, QUEUE_DIR, LAST_SESSION_FILE, ALLOWED_API_HOSTS, ALLOWED_OAUTH_HOSTS, API_BASE, PROFILE_NAME_RE;
4833
4875
  var init_config = __esm({
4834
4876
  "src/lib/config.ts"() {
4835
4877
  "use strict";
4836
- fs = __toESM(require("fs"));
4878
+ fs2 = __toESM(require("fs"));
4837
4879
  path = __toESM(require("path"));
4838
4880
  os = __toESM(require("os"));
4881
+ init_data_safety();
4839
4882
  HYV_DIR = path.join(os.homedir(), ".hyv");
4840
4883
  AUTH_FILE = path.join(HYV_DIR, "auth.json");
4841
4884
  CONFIG_FILE = path.join(HYV_DIR, "config.json");
@@ -4860,11 +4903,11 @@ var init_config = __esm({
4860
4903
  var require_is_docker = __commonJS({
4861
4904
  "node_modules/is-docker/index.js"(exports2, module2) {
4862
4905
  "use strict";
4863
- var fs28 = require("fs");
4906
+ var fs29 = require("fs");
4864
4907
  var isDocker;
4865
4908
  function hasDockerEnv() {
4866
4909
  try {
4867
- fs28.statSync("/.dockerenv");
4910
+ fs29.statSync("/.dockerenv");
4868
4911
  return true;
4869
4912
  } catch (_2) {
4870
4913
  return false;
@@ -4872,7 +4915,7 @@ var require_is_docker = __commonJS({
4872
4915
  }
4873
4916
  function hasDockerCGroup() {
4874
4917
  try {
4875
- return fs28.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4918
+ return fs29.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4876
4919
  } catch (_2) {
4877
4920
  return false;
4878
4921
  }
@@ -4891,7 +4934,7 @@ var require_is_wsl = __commonJS({
4891
4934
  "node_modules/is-wsl/index.js"(exports2, module2) {
4892
4935
  "use strict";
4893
4936
  var os12 = require("os");
4894
- var fs28 = require("fs");
4937
+ var fs29 = require("fs");
4895
4938
  var isDocker = require_is_docker();
4896
4939
  var isWsl = () => {
4897
4940
  if (process.platform !== "linux") {
@@ -4904,7 +4947,7 @@ var require_is_wsl = __commonJS({
4904
4947
  return true;
4905
4948
  }
4906
4949
  try {
4907
- return fs28.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
4950
+ return fs29.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
4908
4951
  } catch (_2) {
4909
4952
  return false;
4910
4953
  }
@@ -4945,7 +4988,7 @@ var require_open = __commonJS({
4945
4988
  "node_modules/open/index.js"(exports2, module2) {
4946
4989
  var path26 = require("path");
4947
4990
  var childProcess = require("child_process");
4948
- var { promises: fs28, constants: fsConstants } = require("fs");
4991
+ var { promises: fs29, constants: fsConstants } = require("fs");
4949
4992
  var isWsl = require_is_wsl();
4950
4993
  var isDocker = require_is_docker();
4951
4994
  var defineLazyProperty = require_define_lazy_prop();
@@ -4953,7 +4996,7 @@ var require_open = __commonJS({
4953
4996
  var { platform, arch } = process;
4954
4997
  var hasContainerEnv = () => {
4955
4998
  try {
4956
- fs28.statSync("/run/.containerenv");
4999
+ fs29.statSync("/run/.containerenv");
4957
5000
  return true;
4958
5001
  } catch {
4959
5002
  return false;
@@ -4976,14 +5019,14 @@ var require_open = __commonJS({
4976
5019
  const configFilePath = "/etc/wsl.conf";
4977
5020
  let isConfigFileExists = false;
4978
5021
  try {
4979
- await fs28.access(configFilePath, fsConstants.F_OK);
5022
+ await fs29.access(configFilePath, fsConstants.F_OK);
4980
5023
  isConfigFileExists = true;
4981
5024
  } catch {
4982
5025
  }
4983
5026
  if (!isConfigFileExists) {
4984
5027
  return defaultMountPoint;
4985
5028
  }
4986
- const configContent = await fs28.readFile(configFilePath, { encoding: "utf8" });
5029
+ const configContent = await fs29.readFile(configFilePath, { encoding: "utf8" });
4987
5030
  const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
4988
5031
  if (!configMountPoint) {
4989
5032
  return defaultMountPoint;
@@ -5083,7 +5126,7 @@ var require_open = __commonJS({
5083
5126
  const isBundled = !__dirname || __dirname === "/";
5084
5127
  let exeLocalXdgOpen = false;
5085
5128
  try {
5086
- await fs28.access(localXdgOpenPath, fsConstants.X_OK);
5129
+ await fs29.access(localXdgOpenPath, fsConstants.X_OK);
5087
5130
  exeLocalXdgOpen = true;
5088
5131
  } catch {
5089
5132
  }
@@ -5289,14 +5332,14 @@ function pkgRoot() {
5289
5332
  // src/lib/ → cli/
5290
5333
  ];
5291
5334
  for (const root of candidates) {
5292
- if (fs2.existsSync(path2.join(root, "package.json")))
5335
+ if (fs3.existsSync(path2.join(root, "package.json")))
5293
5336
  return root;
5294
5337
  }
5295
5338
  return path2.resolve(__dirname, "..");
5296
5339
  }
5297
5340
  function getCliVersion() {
5298
5341
  try {
5299
- const pkg = JSON.parse(fs2.readFileSync(path2.join(PKG_ROOT, "package.json"), "utf-8"));
5342
+ const pkg = JSON.parse(fs3.readFileSync(path2.join(PKG_ROOT, "package.json"), "utf-8"));
5300
5343
  return pkg.version || "unknown";
5301
5344
  } catch {
5302
5345
  return "unknown";
@@ -5304,8 +5347,8 @@ function getCliVersion() {
5304
5347
  }
5305
5348
  function getRulesVersion() {
5306
5349
  try {
5307
- if (fs2.existsSync(RULES_MANIFEST)) {
5308
- return JSON.parse(fs2.readFileSync(RULES_MANIFEST, "utf-8")).version || "unknown";
5350
+ if (fs3.existsSync(RULES_MANIFEST)) {
5351
+ return JSON.parse(fs3.readFileSync(RULES_MANIFEST, "utf-8")).version || "unknown";
5309
5352
  }
5310
5353
  } catch {
5311
5354
  }
@@ -5343,11 +5386,11 @@ function fetchLatestNpmVersion(timeoutMs = 8e3) {
5343
5386
  );
5344
5387
  });
5345
5388
  }
5346
- var fs2, path2, import_child_process, PKG_ROOT, RULES_MANIFEST;
5389
+ var fs3, path2, import_child_process, PKG_ROOT, RULES_MANIFEST;
5347
5390
  var init_version = __esm({
5348
5391
  "src/lib/version.ts"() {
5349
5392
  "use strict";
5350
- fs2 = __toESM(require("fs"));
5393
+ fs3 = __toESM(require("fs"));
5351
5394
  path2 = __toESM(require("path"));
5352
5395
  import_child_process = require("child_process");
5353
5396
  PKG_ROOT = pkgRoot();
@@ -5973,9 +6016,9 @@ function getDiskCachePath(slug) {
5973
6016
  function loadFromDiskCache(slug) {
5974
6017
  try {
5975
6018
  const cachePath = getDiskCachePath(slug);
5976
- if (!fs3.existsSync(cachePath))
6019
+ if (!fs4.existsSync(cachePath))
5977
6020
  return null;
5978
- const content = fs3.readFileSync(cachePath, "utf-8");
6021
+ const content = fs4.readFileSync(cachePath, "utf-8");
5979
6022
  const entry = JSON.parse(content);
5980
6023
  if (!entry.data || !entry.timestamp)
5981
6024
  return null;
@@ -5986,10 +6029,10 @@ function loadFromDiskCache(slug) {
5986
6029
  }
5987
6030
  function saveToDiskCache(slug, entry) {
5988
6031
  try {
5989
- if (!fs3.existsSync(DISK_CACHE_DIR)) {
5990
- fs3.mkdirSync(DISK_CACHE_DIR, { recursive: true });
6032
+ if (!fs4.existsSync(DISK_CACHE_DIR)) {
6033
+ fs4.mkdirSync(DISK_CACHE_DIR, { recursive: true });
5991
6034
  }
5992
- fs3.writeFileSync(getDiskCachePath(slug), JSON.stringify(entry), "utf-8");
6035
+ fs4.writeFileSync(getDiskCachePath(slug), JSON.stringify(entry), "utf-8");
5993
6036
  } catch {
5994
6037
  }
5995
6038
  }
@@ -6007,13 +6050,13 @@ function buildMinimalProfile(slug, body) {
6007
6050
  cadence: {}
6008
6051
  };
6009
6052
  }
6010
- var fs3, path4, os3, CACHE_TTL, memoryCache, DISK_CACHE_DIR;
6053
+ var fs4, path4, os3, CACHE_TTL, memoryCache, DISK_CACHE_DIR;
6011
6054
  var init_profile = __esm({
6012
6055
  "src/lib/profile.ts"() {
6013
6056
  "use strict";
6014
6057
  init_api();
6015
6058
  init_config();
6016
- fs3 = __toESM(require("fs"));
6059
+ fs4 = __toESM(require("fs"));
6017
6060
  path4 = __toESM(require("path"));
6018
6061
  os3 = __toESM(require("os"));
6019
6062
  CACHE_TTL = 5 * 60 * 1e3;
@@ -6038,9 +6081,9 @@ function cachePathForSlug(slug) {
6038
6081
  function loadProfileFromDiskCache(slug) {
6039
6082
  try {
6040
6083
  const cachePath = cachePathForSlug(slug);
6041
- if (!fs4.existsSync(cachePath))
6084
+ if (!fs5.existsSync(cachePath))
6042
6085
  return void 0;
6043
- const entry = JSON.parse(fs4.readFileSync(cachePath, "utf-8"));
6086
+ const entry = JSON.parse(fs5.readFileSync(cachePath, "utf-8"));
6044
6087
  if (entry?.data?.body)
6045
6088
  return entry.data;
6046
6089
  } catch {
@@ -6064,9 +6107,9 @@ function profileFromMarkdown(slug, body) {
6064
6107
  function getProfileCacheAge(slug) {
6065
6108
  try {
6066
6109
  const cachePath = cachePathForSlug(slug);
6067
- if (!fs4.existsSync(cachePath))
6110
+ if (!fs5.existsSync(cachePath))
6068
6111
  return null;
6069
- const entry = JSON.parse(fs4.readFileSync(cachePath, "utf-8"));
6112
+ const entry = JSON.parse(fs5.readFileSync(cachePath, "utf-8"));
6070
6113
  return entry.timestamp || null;
6071
6114
  } catch {
6072
6115
  return null;
@@ -6074,9 +6117,9 @@ function getProfileCacheAge(slug) {
6074
6117
  }
6075
6118
  function listDiskCachedProfiles() {
6076
6119
  try {
6077
- if (!fs4.existsSync(DISK_CACHE_DIR2))
6120
+ if (!fs5.existsSync(DISK_CACHE_DIR2))
6078
6121
  return [];
6079
- return fs4.readdirSync(DISK_CACHE_DIR2).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
6122
+ return fs5.readdirSync(DISK_CACHE_DIR2).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
6080
6123
  } catch {
6081
6124
  return [];
6082
6125
  }
@@ -6133,11 +6176,11 @@ function hasRichProfile(profile) {
6133
6176
  return false;
6134
6177
  return (profile.never_list?.length || 0) > 0 || (profile.learned_patterns?.length || 0) > 0 || (profile.drift_snapshot?.length || 0) > 0 || (profile.voice_rules?.length || 0) > 0;
6135
6178
  }
6136
- var fs4, path5, os4, DISK_CACHE_DIR2;
6179
+ var fs5, path5, os4, DISK_CACHE_DIR2;
6137
6180
  var init_local_profile = __esm({
6138
6181
  "src/lib/local-profile.ts"() {
6139
6182
  "use strict";
6140
- fs4 = __toESM(require("fs"));
6183
+ fs5 = __toESM(require("fs"));
6141
6184
  path5 = __toESM(require("path"));
6142
6185
  os4 = __toESM(require("os"));
6143
6186
  init_config();
@@ -6235,19 +6278,19 @@ var init_profile_parse = __esm({
6235
6278
  function loadSupplementalPatterns() {
6236
6279
  const p = path8.join(CACHE_DIR, "supplemental-rules.json");
6237
6280
  try {
6238
- if (!fs7.existsSync(p))
6281
+ if (!fs8.existsSync(p))
6239
6282
  return [];
6240
- const data = JSON.parse(fs7.readFileSync(p, "utf-8"));
6283
+ const data = JSON.parse(fs8.readFileSync(p, "utf-8"));
6241
6284
  return Array.isArray(data.patterns) ? data.patterns : [];
6242
6285
  } catch {
6243
6286
  return [];
6244
6287
  }
6245
6288
  }
6246
- var fs7, path8, BUNDLED_MANIFEST;
6289
+ var fs8, path8, BUNDLED_MANIFEST;
6247
6290
  var init_rules_loader = __esm({
6248
6291
  "src/lib/rules-loader.ts"() {
6249
6292
  "use strict";
6250
- fs7 = __toESM(require("fs"));
6293
+ fs8 = __toESM(require("fs"));
6251
6294
  path8 = __toESM(require("path"));
6252
6295
  init_config();
6253
6296
  BUNDLED_MANIFEST = path8.resolve(__dirname, "..", "..", "assets", "detection-rules.json");
@@ -7282,7 +7325,7 @@ function Ht(n7) {
7282
7325
  return isNaN(n7) ? n7.charCodeAt(0) : parseInt(n7, 10);
7283
7326
  }
7284
7327
  function ps(n7) {
7285
- return n7.replace(as, fe).replace(ls, ue).replace(cs, qt).replace(fs8, de).replace(us, pe);
7328
+ return n7.replace(as, fe).replace(ls, ue).replace(cs, qt).replace(fs9, de).replace(us, pe);
7286
7329
  }
7287
7330
  function ms(n7) {
7288
7331
  return n7.replace(is, "\\").replace(rs, "{").replace(ns, "}").replace(os5, ",").replace(hs, ".");
@@ -7385,7 +7428,7 @@ function Ut(n7, t = {}) {
7385
7428
  function es(n7, t = {}) {
7386
7429
  return new I(n7, t).iterate();
7387
7430
  }
7388
- var import_node_url, import_node_path, import_node_url2, import_fs, xi, import_promises, import_node_events, import_node_stream, import_node_string_decoder, Gt, ce, ss, fe, ue, qt, de, pe, is, rs, ns, os5, hs, as, ls, cs, fs8, us, ds, at, Ss, lt, Es, we, ye, W, xs, be, vs, Ct, Cs, Ts, As, ks, Kt, Se, Ee, Q, tt, O, Rs, Os, Fs, Ds, Ms, Ns, _s, Ls, Ws, Ps, js, Is, zs, Bs, Us, $s, Gs, Hs, Ce, Te, Ae, xe, qs, A, Ks, Vs, Ys, Xs, Js, N, Zs, ke, Qs, ti, ve, ei, D, si, Oe, Vt, Fe, At, Re, ii, q, De, Tt, ri, ft, Ne, oi, hi, ai, G, H, K, kt, ut, Rt, _e, Ot, Le, P, et, v, dt, st, C, F, T, Yt, Ft, k, x, Xt, Jt, We, Zt, B, Qt, Dt, pt, Y, M, mt, li, ci, fi, ui, Mt, te, di, pi, V, vi, wt, Ue, $e, Ri, Oi, L, Ge, He, U, qe, Ke, X, Ve, _, gt, se, je, yt, j, Nt, Lt, Ie, Fi, ie, ze, bt, Be, _t, Wt, ne, Ye, R, Pt, jt, It, it, rt, St, Cr, Xe, Di, Mi, Ni, nt, _i, ot, oe, he, ae, Et, Li, zt, xt, vt, Pi, I, le, ji, Ii, zi, Bi, Ui, Ze;
7431
+ var import_node_url, import_node_path, import_node_url2, import_fs, xi, import_promises, import_node_events, import_node_stream, import_node_string_decoder, Gt, ce, ss, fe, ue, qt, de, pe, is, rs, ns, os5, hs, as, ls, cs, fs9, us, ds, at, Ss, lt, Es, we, ye, W, xs, be, vs, Ct, Cs, Ts, As, ks, Kt, Se, Ee, Q, tt, O, Rs, Os, Fs, Ds, Ms, Ns, _s, Ls, Ws, Ps, js, Is, zs, Bs, Us, $s, Gs, Hs, Ce, Te, Ae, xe, qs, A, Ks, Vs, Ys, Xs, Js, N, Zs, ke, Qs, ti, ve, ei, D, si, Oe, Vt, Fe, At, Re, ii, q, De, Tt, ri, ft, Ne, oi, hi, ai, G, H, K, kt, ut, Rt, _e, Ot, Le, P, et, v, dt, st, C, F, T, Yt, Ft, k, x, Xt, Jt, We, Zt, B, Qt, Dt, pt, Y, M, mt, li, ci, fi, ui, Mt, te, di, pi, V, vi, wt, Ue, $e, Ri, Oi, L, Ge, He, U, qe, Ke, X, Ve, _, gt, se, je, yt, j, Nt, Lt, Ie, Fi, ie, ze, bt, Be, _t, Wt, ne, Ye, R, Pt, jt, It, it, rt, St, Cr, Xe, Di, Mi, Ni, nt, _i, ot, oe, he, ae, Et, Li, zt, xt, vt, Pi, I, le, ji, Ii, zi, Bi, Ui, Ze;
7389
7432
  var init_index_min = __esm({
7390
7433
  "node_modules/glob/dist/esm/index.min.js"() {
7391
7434
  import_node_url = require("node:url");
@@ -7437,7 +7480,7 @@ var init_index_min = __esm({
7437
7480
  as = /\\\\/g;
7438
7481
  ls = /\\{/g;
7439
7482
  cs = /\\}/g;
7440
- fs8 = /\\,/g;
7483
+ fs9 = /\\,/g;
7441
7484
  us = /\\./g;
7442
7485
  ds = 1e5;
7443
7486
  at = (n7) => {
@@ -10672,7 +10715,7 @@ function extractPdf(filePath) {
10672
10715
  if (out.trim())
10673
10716
  return out.trim();
10674
10717
  }
10675
- const raw = extractPdfRaw(fs9.readFileSync(filePath));
10718
+ const raw = extractPdfRaw(fs10.readFileSync(filePath));
10676
10719
  if (raw.trim().length > 20)
10677
10720
  return raw;
10678
10721
  throw new Error("could not extract text from .pdf \u2014 try pasting the draft instead");
@@ -10695,10 +10738,10 @@ function extractSkillArchive(filePath) {
10695
10738
  }
10696
10739
  function extractTextFromFile(filePath) {
10697
10740
  const resolved = path9.resolve(filePath);
10698
- if (!fs9.existsSync(resolved)) {
10741
+ if (!fs10.existsSync(resolved)) {
10699
10742
  throw new Error(`file not found: ${resolved}`);
10700
10743
  }
10701
- const stat = fs9.statSync(resolved);
10744
+ const stat = fs10.statSync(resolved);
10702
10745
  if (!stat.isFile()) {
10703
10746
  throw new Error(`not a file: ${resolved}`);
10704
10747
  }
@@ -10707,10 +10750,10 @@ function extractTextFromFile(filePath) {
10707
10750
  case ".md":
10708
10751
  case ".txt":
10709
10752
  case ".markdown":
10710
- return fs9.readFileSync(resolved, "utf-8");
10753
+ return fs10.readFileSync(resolved, "utf-8");
10711
10754
  case ".html":
10712
10755
  case ".htm":
10713
- return stripHtml(fs9.readFileSync(resolved, "utf-8"));
10756
+ return stripHtml(fs10.readFileSync(resolved, "utf-8"));
10714
10757
  case ".docx":
10715
10758
  return extractDocx(resolved);
10716
10759
  case ".pdf":
@@ -10731,10 +10774,10 @@ function listDocumentsInDir(dirPath) {
10731
10774
  }
10732
10775
  function collectDocumentsFromPath(inputPath) {
10733
10776
  const resolved = path9.resolve(inputPath.trim().replace(/^~(?=$|\/)/, os6.homedir()));
10734
- if (!fs9.existsSync(resolved)) {
10777
+ if (!fs10.existsSync(resolved)) {
10735
10778
  throw new Error(`path not found: ${resolved}`);
10736
10779
  }
10737
- const stat = fs9.statSync(resolved);
10780
+ const stat = fs10.statSync(resolved);
10738
10781
  const files = stat.isFile() ? [resolved] : listDocumentsInDir(resolved);
10739
10782
  if (!files.length) {
10740
10783
  throw new Error(
@@ -10762,17 +10805,17 @@ function readDraftInput(input) {
10762
10805
  if (!trimmed)
10763
10806
  throw new Error("empty draft");
10764
10807
  const maybePath = path9.resolve(trimmed.replace(/^~(?=$|\/)/, os6.homedir()));
10765
- if (fs9.existsSync(maybePath) && fs9.statSync(maybePath).isFile()) {
10808
+ if (fs10.existsSync(maybePath) && fs10.statSync(maybePath).isFile()) {
10766
10809
  return { text: extractTextFromFile(maybePath), source: maybePath };
10767
10810
  }
10768
10811
  return { text: trimmed, source: "paste" };
10769
10812
  }
10770
- var import_child_process2, fs9, os6, path9, SUPPORTED_EXTENSIONS, SAMPLE_GLOB, GLOB_IGNORE;
10813
+ var import_child_process2, fs10, os6, path9, SUPPORTED_EXTENSIONS, SAMPLE_GLOB, GLOB_IGNORE;
10771
10814
  var init_document_text = __esm({
10772
10815
  "src/lib/document-text.ts"() {
10773
10816
  "use strict";
10774
10817
  import_child_process2 = require("child_process");
10775
- fs9 = __toESM(require("fs"));
10818
+ fs10 = __toESM(require("fs"));
10776
10819
  os6 = __toESM(require("os"));
10777
10820
  path9 = __toESM(require("path"));
10778
10821
  init_index_min();
@@ -10849,21 +10892,21 @@ function runPipeline(text, profile, applyFixes = false) {
10849
10892
  };
10850
10893
  }
10851
10894
  function readText(source) {
10852
- const fs28 = require("fs");
10895
+ const fs29 = require("fs");
10853
10896
  const path26 = require("path");
10854
10897
  if (source === "-") {
10855
10898
  if (process.stdin.isTTY) {
10856
10899
  console.error("No input provided. Pipe content or specify a file.");
10857
10900
  process.exit(1);
10858
10901
  }
10859
- return { text: fs28.readFileSync(0, "utf-8"), path: "stdin" };
10902
+ return { text: fs29.readFileSync(0, "utf-8"), path: "stdin" };
10860
10903
  }
10861
10904
  const resolved = path26.resolve(source);
10862
- if (!fs28.existsSync(resolved)) {
10905
+ if (!fs29.existsSync(resolved)) {
10863
10906
  console.error(`File not found: ${resolved}`);
10864
10907
  process.exit(1);
10865
10908
  }
10866
- const stat = fs28.statSync(resolved);
10909
+ const stat = fs29.statSync(resolved);
10867
10910
  if (stat.isDirectory()) {
10868
10911
  console.error(`${resolved} is a directory, not a file.`);
10869
10912
  process.exit(1);
@@ -11315,10 +11358,10 @@ var init_terminal_ui = __esm({
11315
11358
  // src/lib/welcome-flow.ts
11316
11359
  function readWelcomeState() {
11317
11360
  try {
11318
- if (!fs13.existsSync(STATE_FILE)) {
11361
+ if (!fs14.existsSync(STATE_FILE)) {
11319
11362
  return { completed_steps: [], updated_at: (/* @__PURE__ */ new Date()).toISOString() };
11320
11363
  }
11321
- return JSON.parse(fs13.readFileSync(STATE_FILE, "utf-8"));
11364
+ return JSON.parse(fs14.readFileSync(STATE_FILE, "utf-8"));
11322
11365
  } catch {
11323
11366
  return { completed_steps: [], updated_at: (/* @__PURE__ */ new Date()).toISOString() };
11324
11367
  }
@@ -11330,7 +11373,7 @@ function writeWelcomeState(patch) {
11330
11373
  ...patch,
11331
11374
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
11332
11375
  };
11333
- fs13.writeFileSync(STATE_FILE, JSON.stringify(next, null, 2), { mode: 384 });
11376
+ fs14.writeFileSync(STATE_FILE, JSON.stringify(next, null, 2), { mode: 384 });
11334
11377
  return next;
11335
11378
  }
11336
11379
  function markStepComplete(step) {
@@ -11781,7 +11824,7 @@ async function stepSignup(profileName) {
11781
11824
  } else {
11782
11825
  console.log(import_chalk12.default.dim("\n already signed in \u2014 syncing profile..."));
11783
11826
  }
11784
- const content = fs13.readFileSync(
11827
+ const content = fs14.readFileSync(
11785
11828
  path13.join(os7.homedir(), ".hyv", "profiles", `${profileName}.md`),
11786
11829
  "utf-8"
11787
11830
  );
@@ -11794,7 +11837,12 @@ async function stepSignup(profileName) {
11794
11837
  })
11795
11838
  );
11796
11839
  if (response.status === 200) {
11797
- console.log(import_chalk12.default.green(" \u2713 profile synced to your account"));
11840
+ const body = response.data;
11841
+ if (body.preserved) {
11842
+ console.log(import_chalk12.default.green(" \u2713 account linked \u2014 kept your existing server profile"));
11843
+ } else {
11844
+ console.log(import_chalk12.default.green(" \u2713 profile synced to your account"));
11845
+ }
11798
11846
  } else {
11799
11847
  const detail = response.data?.error;
11800
11848
  console.log(import_chalk12.default.yellow(
@@ -11842,12 +11890,12 @@ function getMcpWelcomeResponse(args2) {
11842
11890
  }
11843
11891
  return buildWelcomeGuide({ profileName: args2.profile, forLlm: true });
11844
11892
  }
11845
- var import_chalk12, fs13, http2, https2, os7, path13, readline, WELCOME_TAGLINE, FLOW_STEPS, STATE_FILE;
11893
+ var import_chalk12, fs14, http2, https2, os7, path13, readline, WELCOME_TAGLINE, FLOW_STEPS, STATE_FILE;
11846
11894
  var init_welcome_flow = __esm({
11847
11895
  "src/lib/welcome-flow.ts"() {
11848
11896
  "use strict";
11849
11897
  import_chalk12 = __toESM(require_source());
11850
- fs13 = __toESM(require("fs"));
11898
+ fs14 = __toESM(require("fs"));
11851
11899
  http2 = __toESM(require("http"));
11852
11900
  https2 = __toESM(require("https"));
11853
11901
  os7 = __toESM(require("os"));
@@ -12858,7 +12906,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
12858
12906
  g.minimatch.escape = vi2.escape;
12859
12907
  g.minimatch.unescape = Ei2.unescape;
12860
12908
  });
12861
- var fs28 = R2((Wt2) => {
12909
+ var fs29 = R2((Wt2) => {
12862
12910
  "use strict";
12863
12911
  Object.defineProperty(Wt2, "__esModule", { value: true });
12864
12912
  Wt2.LRUCache = void 0;
@@ -13827,7 +13875,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
13827
13875
  };
13828
13876
  Object.defineProperty(_2, "__esModule", { value: true });
13829
13877
  _2.PathScurry = _2.Path = _2.PathScurryDarwin = _2.PathScurryPosix = _2.PathScurryWin32 = _2.PathScurryBase = _2.PathPosix = _2.PathWin32 = _2.PathBase = _2.ChildrenCache = _2.ResolveCache = void 0;
13830
- var Qt2 = fs28(), Yt2 = require("node:path"), yr = require("node:url"), pt2 = require("fs"), Sr = br(require("node:fs")), vr = pt2.realpathSync.native, Ht2 = require("node:fs/promises"), bs2 = Oe2(), mt2 = { lstatSync: pt2.lstatSync, readdir: pt2.readdir, readdirSync: pt2.readdirSync, readlinkSync: pt2.readlinkSync, realpathSync: vr, promises: { lstat: Ht2.lstat, readdir: Ht2.readdir, readlink: Ht2.readlink, realpath: Ht2.realpath } }, _s2 = (n7) => !n7 || n7 === mt2 || n7 === Sr ? mt2 : { ...mt2, ...n7, promises: { ...mt2.promises, ...n7.promises || {} } }, Os2 = /^\\\\\?\\([a-z]:)\\?$/i, Er = (n7) => n7.replace(/\//g, "\\").replace(Os2, "$1\\"), _r = /[\\\/]/, N2 = 0, xs2 = 1, Ts2 = 2, G2 = 4, Cs2 = 6, Rs2 = 8, Q2 = 10, As2 = 12, j2 = 15, dt2 = ~j2, xe2 = 16, ys2 = 32, gt2 = 64, W2 = 128, Vt2 = 256, Xt2 = 512, Ss2 = gt2 | W2 | Xt2, Or = 1023, Te2 = (n7) => n7.isFile() ? Rs2 : n7.isDirectory() ? G2 : n7.isSymbolicLink() ? Q2 : n7.isCharacterDevice() ? Ts2 : n7.isBlockDevice() ? Cs2 : n7.isSocket() ? As2 : n7.isFIFO() ? xs2 : N2, vs2 = new Qt2.LRUCache({ max: 2 ** 12 }), wt2 = (n7) => {
13878
+ var Qt2 = fs29(), Yt2 = require("node:path"), yr = require("node:url"), pt2 = require("fs"), Sr = br(require("node:fs")), vr = pt2.realpathSync.native, Ht2 = require("node:fs/promises"), bs2 = Oe2(), mt2 = { lstatSync: pt2.lstatSync, readdir: pt2.readdir, readdirSync: pt2.readdirSync, readlinkSync: pt2.readlinkSync, realpathSync: vr, promises: { lstat: Ht2.lstat, readdir: Ht2.readdir, readlink: Ht2.readlink, realpath: Ht2.realpath } }, _s2 = (n7) => !n7 || n7 === mt2 || n7 === Sr ? mt2 : { ...mt2, ...n7, promises: { ...mt2.promises, ...n7.promises || {} } }, Os2 = /^\\\\\?\\([a-z]:)\\?$/i, Er = (n7) => n7.replace(/\//g, "\\").replace(Os2, "$1\\"), _r = /[\\\/]/, N2 = 0, xs2 = 1, Ts2 = 2, G2 = 4, Cs2 = 6, Rs2 = 8, Q2 = 10, As2 = 12, j2 = 15, dt2 = ~j2, xe2 = 16, ys2 = 32, gt2 = 64, W2 = 128, Vt2 = 256, Xt2 = 512, Ss2 = gt2 | W2 | Xt2, Or = 1023, Te2 = (n7) => n7.isFile() ? Rs2 : n7.isDirectory() ? G2 : n7.isSymbolicLink() ? Q2 : n7.isCharacterDevice() ? Ts2 : n7.isBlockDevice() ? Cs2 : n7.isSocket() ? As2 : n7.isFIFO() ? xs2 : N2, vs2 = new Qt2.LRUCache({ max: 2 ** 12 }), wt2 = (n7) => {
13831
13879
  let t = vs2.get(n7);
13832
13880
  if (t)
13833
13881
  return t;
@@ -15522,7 +15570,7 @@ init_version();
15522
15570
 
15523
15571
  // src/lib/queue-sync.ts
15524
15572
  var import_chalk6 = __toESM(require_source());
15525
- var fs5 = __toESM(require("fs"));
15573
+ var fs6 = __toESM(require("fs"));
15526
15574
  var path6 = __toESM(require("path"));
15527
15575
  init_config();
15528
15576
  init_auth();
@@ -15532,13 +15580,13 @@ async function flushQueuedSignals() {
15532
15580
  return { sent: 0, failed: 0 };
15533
15581
  let sent = 0;
15534
15582
  let failed = 0;
15535
- if (!fs5.existsSync(QUEUE_DIR))
15583
+ if (!fs6.existsSync(QUEUE_DIR))
15536
15584
  return { sent: 0, failed: 0 };
15537
- const files = fs5.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
15585
+ const files = fs6.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
15538
15586
  for (const file of files) {
15539
15587
  const filePath = path6.join(QUEUE_DIR, file);
15540
15588
  try {
15541
- const signal = JSON.parse(fs5.readFileSync(filePath, "utf-8"));
15589
+ const signal = JSON.parse(fs6.readFileSync(filePath, "utf-8"));
15542
15590
  let ok = false;
15543
15591
  if (signal.type === "reinforce") {
15544
15592
  const res = await authenticatedRequest(cliApiUrl("/cli/learning/reinforce"), {
@@ -15559,7 +15607,7 @@ async function flushQueuedSignals() {
15559
15607
  ok = res.status === 200;
15560
15608
  }
15561
15609
  if (ok) {
15562
- fs5.unlinkSync(filePath);
15610
+ fs6.unlinkSync(filePath);
15563
15611
  sent++;
15564
15612
  } else {
15565
15613
  failed++;
@@ -15716,7 +15764,7 @@ init_config();
15716
15764
  init_auth();
15717
15765
  init_access();
15718
15766
  init_profile();
15719
- var fs6 = __toESM(require("fs"));
15767
+ var fs7 = __toESM(require("fs"));
15720
15768
  var path7 = __toESM(require("path"));
15721
15769
  function registerSyncCommand(program3) {
15722
15770
  program3.command("sync").description("Sync profiles and rules from server").option("--force", "Force re-download even if cache is fresh").action(async (options) => {
@@ -15736,17 +15784,21 @@ function registerSyncCommand(program3) {
15736
15784
  ensureHyvDir();
15737
15785
  let profileCount = 0;
15738
15786
  for (const profile of data.profiles) {
15787
+ if (!profile.content?.trim()) {
15788
+ console.log(import_chalk8.default.yellow(` ! skipped empty profile cache: ${profile.name}`));
15789
+ continue;
15790
+ }
15739
15791
  const cacheKey = toSafeProfileCacheKey(profile.slug || profile.name);
15740
15792
  writeCachedProfile(cacheKey, profile.content);
15741
15793
  profileCount++;
15742
15794
  }
15743
15795
  const rulesPath = path7.join(CACHE_DIR, "rules.md");
15744
- fs6.writeFileSync(rulesPath, data.rules, { mode: 384 });
15796
+ fs7.writeFileSync(rulesPath, data.rules, { mode: 384 });
15745
15797
  const promptPath = path7.join(CACHE_DIR, "prompt-template.md");
15746
- fs6.writeFileSync(promptPath, data.prompt_template, { mode: 384 });
15798
+ fs7.writeFileSync(promptPath, data.prompt_template, { mode: 384 });
15747
15799
  if (data.detection_rules) {
15748
15800
  const rulesJson = path7.join(CACHE_DIR, "detection-rules.json");
15749
- fs6.writeFileSync(rulesJson, JSON.stringify(data.detection_rules, null, 2), { mode: 384 });
15801
+ fs7.writeFileSync(rulesJson, JSON.stringify(data.detection_rules, null, 2), { mode: 384 });
15750
15802
  }
15751
15803
  for (const profile of data.profiles) {
15752
15804
  try {
@@ -15763,7 +15815,7 @@ function registerSyncCommand(program3) {
15763
15815
  console.log(import_chalk8.default.yellow(` ! ${queueResult.failed} queued signal(s) could not send`));
15764
15816
  }
15765
15817
  const metaPath = path7.join(CACHE_DIR, "sync-meta.json");
15766
- fs6.writeFileSync(metaPath, JSON.stringify({
15818
+ fs7.writeFileSync(metaPath, JSON.stringify({
15767
15819
  synced_at: data.synced_at,
15768
15820
  profile_count: profileCount,
15769
15821
  plan: data.plan
@@ -15868,12 +15920,12 @@ Profile evolution: ${slug}
15868
15920
 
15869
15921
  // src/commands/rewrite.ts
15870
15922
  var import_chalk9 = __toESM(require_source());
15871
- var fs11 = __toESM(require("fs"));
15923
+ var fs12 = __toESM(require("fs"));
15872
15924
  var path11 = __toESM(require("path"));
15873
15925
  init_pipeline();
15874
15926
 
15875
15927
  // src/lib/prompt.ts
15876
- var fs10 = __toESM(require("fs"));
15928
+ var fs11 = __toESM(require("fs"));
15877
15929
  var path10 = __toESM(require("path"));
15878
15930
  init_config();
15879
15931
  init_signals();
@@ -15881,8 +15933,8 @@ init_profile_parse();
15881
15933
  function loadPromptTemplate() {
15882
15934
  const templatePath = path10.join(CACHE_DIR, "prompt-template.md");
15883
15935
  try {
15884
- if (fs10.existsSync(templatePath)) {
15885
- return fs10.readFileSync(templatePath, "utf-8");
15936
+ if (fs11.existsSync(templatePath)) {
15937
+ return fs11.readFileSync(templatePath, "utf-8");
15886
15938
  }
15887
15939
  } catch {
15888
15940
  }
@@ -16070,7 +16122,7 @@ Draft: ${draftPath}`));
16070
16122
  }
16071
16123
  if (options.output) {
16072
16124
  const outputPath = path11.resolve(options.output);
16073
- fs11.writeFileSync(outputPath, promptResult.prompt);
16125
+ fs12.writeFileSync(outputPath, promptResult.prompt);
16074
16126
  console.log(import_chalk9.default.green(`
16075
16127
  \u2713 Prompt written to ${outputPath}`));
16076
16128
  } else {
@@ -16090,7 +16142,7 @@ Draft: ${draftPath}`));
16090
16142
 
16091
16143
  // src/commands/learning.ts
16092
16144
  var import_chalk10 = __toESM(require_source());
16093
- var fs12 = __toESM(require("fs"));
16145
+ var fs13 = __toESM(require("fs"));
16094
16146
  var path12 = __toESM(require("path"));
16095
16147
 
16096
16148
  // src/lib/diff.ts
@@ -16237,16 +16289,16 @@ function registerLearningCommands(program3) {
16237
16289
  }
16238
16290
  origPath = path12.resolve(original);
16239
16291
  editPath = path12.resolve(edited);
16240
- if (!fs12.existsSync(origPath)) {
16292
+ if (!fs13.existsSync(origPath)) {
16241
16293
  console.error(import_chalk10.default.red(`Original file not found: ${origPath}`));
16242
16294
  process.exit(1);
16243
16295
  }
16244
- if (!fs12.existsSync(editPath)) {
16296
+ if (!fs13.existsSync(editPath)) {
16245
16297
  console.error(import_chalk10.default.red(`Edited file not found: ${editPath}`));
16246
16298
  process.exit(1);
16247
16299
  }
16248
- origText = fs12.readFileSync(origPath, "utf-8");
16249
- editText = fs12.readFileSync(editPath, "utf-8");
16300
+ origText = fs13.readFileSync(origPath, "utf-8");
16301
+ editText = fs13.readFileSync(editPath, "utf-8");
16250
16302
  saveLastEditSession({
16251
16303
  original_path: origPath,
16252
16304
  edited_path: editPath,
@@ -16413,7 +16465,7 @@ function printProfileImpact(impact, data) {
16413
16465
 
16414
16466
  // src/commands/onboarding.ts
16415
16467
  var import_chalk13 = __toESM(require_source());
16416
- var fs14 = __toESM(require("fs"));
16468
+ var fs15 = __toESM(require("fs"));
16417
16469
  var path14 = __toESM(require("path"));
16418
16470
  init_config();
16419
16471
  init_auth();
@@ -16566,20 +16618,20 @@ Industry: ${industry}
16566
16618
  }
16567
16619
  async function createFromSamples(name, sampleDir, token) {
16568
16620
  const dirPath = path14.resolve(sampleDir);
16569
- if (!fs14.existsSync(dirPath)) {
16621
+ if (!fs15.existsSync(dirPath)) {
16570
16622
  throw new Error(`Directory not found: ${dirPath}`);
16571
16623
  }
16572
16624
  console.log(import_chalk13.default.bold(`
16573
16625
  Creating voice profile: ${name}`));
16574
16626
  console.log(import_chalk13.default.dim(`Reading samples from: ${dirPath}
16575
16627
  `));
16576
- const files = fs14.readdirSync(dirPath).filter((f) => f.endsWith(".md") || f.endsWith(".txt")).map((f) => path14.join(dirPath, f));
16628
+ const files = fs15.readdirSync(dirPath).filter((f) => f.endsWith(".md") || f.endsWith(".txt")).map((f) => path14.join(dirPath, f));
16577
16629
  if (files.length === 0) {
16578
16630
  throw new Error("No .md or .txt files found in directory");
16579
16631
  }
16580
16632
  const samples = [];
16581
16633
  for (const file of files) {
16582
- const text = fs14.readFileSync(file, "utf-8");
16634
+ const text = fs15.readFileSync(file, "utf-8");
16583
16635
  if (text.trim().length > 0) {
16584
16636
  samples.push({ path: file, text });
16585
16637
  console.log(import_chalk13.default.dim(` \u2022 ${path14.basename(file)} (${words(text).length} words)`));
@@ -16654,11 +16706,11 @@ function registerImportCommand(program3) {
16654
16706
  try {
16655
16707
  assertSafeProfileName(name);
16656
16708
  const filePath = path14.resolve(file);
16657
- if (!fs14.existsSync(filePath)) {
16709
+ if (!fs15.existsSync(filePath)) {
16658
16710
  console.error(import_chalk13.default.red(`File not found: ${filePath}`));
16659
16711
  process.exit(1);
16660
16712
  }
16661
- const content = fs14.readFileSync(filePath, "utf-8");
16713
+ const content = fs15.readFileSync(filePath, "utf-8");
16662
16714
  ensureHyvDir();
16663
16715
  writeCachedProfile(name, content);
16664
16716
  console.log(import_chalk13.default.green(`
@@ -17029,7 +17081,7 @@ async function runHybridAnalysis(text, profile, opts = {}) {
17029
17081
 
17030
17082
  // src/commands/history.ts
17031
17083
  var import_chalk16 = __toESM(require_source());
17032
- var fs15 = __toESM(require("fs"));
17084
+ var fs16 = __toESM(require("fs"));
17033
17085
  var path15 = __toESM(require("path"));
17034
17086
  init_config();
17035
17087
  var HISTORY_DIR = path15.join(HYV_DIR, "history");
@@ -17043,9 +17095,9 @@ function logScan(entry) {
17043
17095
  }
17044
17096
  function readHistory() {
17045
17097
  try {
17046
- if (!fs15.existsSync(HISTORY_FILE))
17098
+ if (!fs16.existsSync(HISTORY_FILE))
17047
17099
  return [];
17048
- const lines = fs15.readFileSync(HISTORY_FILE, "utf-8").trim().split("\n").filter(Boolean);
17100
+ const lines = fs16.readFileSync(HISTORY_FILE, "utf-8").trim().split("\n").filter(Boolean);
17049
17101
  return lines.map((l) => JSON.parse(l));
17050
17102
  } catch {
17051
17103
  return [];
@@ -17077,8 +17129,8 @@ function registerHistoryCommand(program3) {
17077
17129
  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) => {
17078
17130
  try {
17079
17131
  if (options.clear) {
17080
- if (fs15.existsSync(HISTORY_FILE)) {
17081
- fs15.unlinkSync(HISTORY_FILE);
17132
+ if (fs16.existsSync(HISTORY_FILE)) {
17133
+ fs16.unlinkSync(HISTORY_FILE);
17082
17134
  }
17083
17135
  console.log(import_chalk16.default.green("\n\u2713 History cleared"));
17084
17136
  return;
@@ -17244,7 +17296,7 @@ hyv scan ${filePath}`));
17244
17296
 
17245
17297
  // src/commands/doctor.ts
17246
17298
  var import_chalk18 = __toESM(require_source());
17247
- var fs17 = __toESM(require("fs"));
17299
+ var fs18 = __toESM(require("fs"));
17248
17300
  var path17 = __toESM(require("path"));
17249
17301
  var os8 = __toESM(require("os"));
17250
17302
  init_config();
@@ -17257,7 +17309,7 @@ init_version();
17257
17309
  var import_child_process3 = require("child_process");
17258
17310
 
17259
17311
  // src/lib/cli-entry.ts
17260
- var fs16 = __toESM(require("fs"));
17312
+ var fs17 = __toESM(require("fs"));
17261
17313
  var path16 = __toESM(require("path"));
17262
17314
  function resolveCliEntry() {
17263
17315
  const candidates = [
@@ -17265,7 +17317,7 @@ function resolveCliEntry() {
17265
17317
  path16.resolve(__dirname, "index.js"),
17266
17318
  path16.resolve(__dirname, "..", "dist", "index.js")
17267
17319
  ];
17268
- return candidates.find((p) => p && fs16.existsSync(p)) || null;
17320
+ return candidates.find((p) => p && fs17.existsSync(p)) || null;
17269
17321
  }
17270
17322
  function mcpServerCommand() {
17271
17323
  const entry = resolveCliEntry();
@@ -17362,9 +17414,9 @@ function claudeDesktopDir() {
17362
17414
  }
17363
17415
  function isOwnerOnlyFile(filePath) {
17364
17416
  try {
17365
- if (!fs17.existsSync(filePath))
17417
+ if (!fs18.existsSync(filePath))
17366
17418
  return true;
17367
- const mode = fs17.statSync(filePath).mode & 511;
17419
+ const mode = fs18.statSync(filePath).mode & 511;
17368
17420
  return (mode & 63) === 0;
17369
17421
  } catch {
17370
17422
  return false;
@@ -17372,9 +17424,9 @@ function isOwnerOnlyFile(filePath) {
17372
17424
  }
17373
17425
  function readMcpHyv(configFile) {
17374
17426
  try {
17375
- if (!fs17.existsSync(configFile))
17427
+ if (!fs18.existsSync(configFile))
17376
17428
  return false;
17377
- const cfg = JSON.parse(fs17.readFileSync(configFile, "utf-8"));
17429
+ const cfg = JSON.parse(fs18.readFileSync(configFile, "utf-8"));
17378
17430
  return Boolean(cfg.mcpServers?.hyv);
17379
17431
  } catch {
17380
17432
  return false;
@@ -17395,28 +17447,28 @@ function registerDoctorCommand(program3) {
17395
17447
  console.log(import_chalk18.default.dim("tip: run hyv welcome for free capabilities\n"));
17396
17448
  console.log(import_chalk18.default.dim("checking cli installation..."));
17397
17449
  const cliPath = process.argv[1];
17398
- if (cliPath && fs17.existsSync(cliPath)) {
17450
+ if (cliPath && fs18.existsSync(cliPath)) {
17399
17451
  console.log(import_chalk18.default.green(" \u2713 cli installed"));
17400
17452
  } else {
17401
17453
  console.log(import_chalk18.default.red(" \u2717 cli not found"));
17402
17454
  issues++;
17403
17455
  }
17404
17456
  console.log(import_chalk18.default.dim("checking .hyv directory..."));
17405
- if (fs17.existsSync(HYV_DIR)) {
17457
+ if (fs18.existsSync(HYV_DIR)) {
17406
17458
  console.log(import_chalk18.default.green(" \u2713 .hyv directory exists"));
17407
17459
  } else {
17408
17460
  console.log(import_chalk18.default.yellow(" ! .hyv directory missing \u2014 creating..."));
17409
- fs17.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
17461
+ fs18.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
17410
17462
  fixed++;
17411
17463
  }
17412
17464
  console.log(import_chalk18.default.dim("checking cache..."));
17413
17465
  const diskProfiles = listDiskCachedProfiles();
17414
17466
  const syncMeta = path17.join(CACHE_DIR, "sync-meta.json");
17415
- if (diskProfiles.length > 0 || fs17.existsSync(syncMeta)) {
17467
+ if (diskProfiles.length > 0 || fs18.existsSync(syncMeta)) {
17416
17468
  console.log(import_chalk18.default.green(` \u2713 profile cache (${diskProfiles.length} full profile(s))`));
17417
- if (fs17.existsSync(syncMeta)) {
17469
+ if (fs18.existsSync(syncMeta)) {
17418
17470
  try {
17419
- const meta = JSON.parse(fs17.readFileSync(syncMeta, "utf-8"));
17471
+ const meta = JSON.parse(fs18.readFileSync(syncMeta, "utf-8"));
17420
17472
  console.log(import_chalk18.default.dim(` last sync: ${meta.synced_at || "unknown"}`));
17421
17473
  } catch {
17422
17474
  }
@@ -17425,8 +17477,8 @@ function registerDoctorCommand(program3) {
17425
17477
  console.log(import_chalk18.default.dim(" - no full profile cache (free local engine still works)"));
17426
17478
  }
17427
17479
  console.log(import_chalk18.default.dim("checking file permissions..."));
17428
- if (fs17.existsSync(HYV_DIR)) {
17429
- const hyvMode = fs17.statSync(HYV_DIR).mode & 511;
17480
+ if (fs18.existsSync(HYV_DIR)) {
17481
+ const hyvMode = fs18.statSync(HYV_DIR).mode & 511;
17430
17482
  if ((hyvMode & 63) === 0) {
17431
17483
  console.log(import_chalk18.default.green(" \u2713 .hyv directory permissions"));
17432
17484
  } else {
@@ -17435,7 +17487,7 @@ function registerDoctorCommand(program3) {
17435
17487
  issues++;
17436
17488
  }
17437
17489
  }
17438
- if (fs17.existsSync(AUTH_FILE)) {
17490
+ if (fs18.existsSync(AUTH_FILE)) {
17439
17491
  if (isOwnerOnlyFile(AUTH_FILE)) {
17440
17492
  console.log(import_chalk18.default.green(" \u2713 auth.json permissions"));
17441
17493
  } else {
@@ -17469,8 +17521,8 @@ function registerDoctorCommand(program3) {
17469
17521
  }
17470
17522
  console.log(import_chalk18.default.dim("checking voice profile..."));
17471
17523
  const voiceMd = path17.join(HYV_DIR, "voice.md");
17472
- const hasVoiceMd = fs17.existsSync(voiceMd) && fs17.readFileSync(voiceMd, "utf-8").trim().length > 50;
17473
- const profileFiles = fs17.existsSync(PROFILES_DIR) ? fs17.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md") && f !== "voice.md") : [];
17524
+ const hasVoiceMd = fs18.existsSync(voiceMd) && fs18.readFileSync(voiceMd, "utf-8").trim().length > 50;
17525
+ const profileFiles = fs18.existsSync(PROFILES_DIR) ? fs18.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md") && f !== "voice.md") : [];
17474
17526
  if (hasVoiceMd || profileFiles.length > 0 || diskProfiles.length > 0) {
17475
17527
  if (hasVoiceMd)
17476
17528
  console.log(import_chalk18.default.green(" \u2713 voice.md exists"));
@@ -17485,7 +17537,7 @@ function registerDoctorCommand(program3) {
17485
17537
  console.log(import_chalk18.default.dim("checking agent configurations..."));
17486
17538
  const cursorLegacyRule = path17.join(HOME, ".cursor", "rules", "hyv.md");
17487
17539
  const cursorRule = path17.join(HOME, ".cursor", "rules", "hyv.mdc");
17488
- if (fs17.existsSync(cursorLegacyRule) && fs17.existsSync(cursorRule)) {
17540
+ if (fs18.existsSync(cursorLegacyRule) && fs18.existsSync(cursorRule)) {
17489
17541
  console.log(import_chalk18.default.yellow(" ! stale cursor rule ~/.cursor/rules/hyv.md (use hyv.mdc)"));
17490
17542
  console.log(import_chalk18.default.dim(" run: rm ~/.cursor/rules/hyv.md (or hyv doctor --fix-agents)"));
17491
17543
  issues++;
@@ -17493,11 +17545,11 @@ function registerDoctorCommand(program3) {
17493
17545
  const agentChecks = [
17494
17546
  { name: "claude desktop mcp", ok: readMcpHyv(path17.join(claudeDesktopDir(), "claude_desktop_config.json")) },
17495
17547
  { name: "cursor mcp", ok: readMcpHyv(path17.join(HOME, ".cursor", "mcp.json")) },
17496
- { name: "cursor rule", ok: fs17.existsSync(cursorRule) },
17497
- { name: "claude code command", ok: fs17.existsSync(path17.join(HOME, ".claude", "commands", "hyv.md")) },
17498
- { name: "claude code skill", ok: fs17.existsSync(path17.join(HOME, ".claude", "skills", "hold-your-voice", "SKILL.md")) },
17499
- { name: "codex agents", ok: fs17.existsSync(path17.join(HOME, ".codex", "AGENTS.md")) && fs17.readFileSync(path17.join(HOME, ".codex", "AGENTS.md"), "utf-8").includes("hyv") },
17500
- { name: "command code skill", ok: fs17.existsSync(path17.join(HOME, ".commandcode", "skills", "hyv", "SKILL.md")) }
17548
+ { name: "cursor rule", ok: fs18.existsSync(cursorRule) },
17549
+ { name: "claude code command", ok: fs18.existsSync(path17.join(HOME, ".claude", "commands", "hyv.md")) },
17550
+ { name: "claude code skill", ok: fs18.existsSync(path17.join(HOME, ".claude", "skills", "hold-your-voice", "SKILL.md")) },
17551
+ { name: "codex agents", ok: fs18.existsSync(path17.join(HOME, ".codex", "AGENTS.md")) && fs18.readFileSync(path17.join(HOME, ".codex", "AGENTS.md"), "utf-8").includes("hyv") },
17552
+ { name: "command code skill", ok: fs18.existsSync(path17.join(HOME, ".commandcode", "skills", "hyv", "SKILL.md")) }
17501
17553
  ];
17502
17554
  for (const agent of agentChecks) {
17503
17555
  if (agent.ok) {
@@ -17631,7 +17683,7 @@ init_access();
17631
17683
  init_config();
17632
17684
 
17633
17685
  // src/lib/destructive-write.ts
17634
- var fs18 = __toESM(require("fs"));
17686
+ var fs19 = __toESM(require("fs"));
17635
17687
  var path18 = __toESM(require("path"));
17636
17688
  var readline2 = __toESM(require("readline"));
17637
17689
  var import_chalk20 = __toESM(require_source());
@@ -17658,8 +17710,8 @@ A .bak backup will be created. Proceed? [y/N] `;
17658
17710
  }
17659
17711
  function writeInPlaceWithBackup(filePath, content) {
17660
17712
  const backupPath = filePath + ".bak";
17661
- fs18.copyFileSync(filePath, backupPath);
17662
- fs18.writeFileSync(filePath, content);
17713
+ fs19.copyFileSync(filePath, backupPath);
17714
+ fs19.writeFileSync(filePath, content);
17663
17715
  return backupPath;
17664
17716
  }
17665
17717
  function backupBasename(filePath) {
@@ -17757,19 +17809,19 @@ function registerCheckCommand(program3) {
17757
17809
  const profile = await loadProfileForCommand(options.profile);
17758
17810
  let inputText = text;
17759
17811
  if (text === "-") {
17760
- const fs29 = require("fs");
17812
+ const fs30 = require("fs");
17761
17813
  if (process.stdin.isTTY) {
17762
17814
  console.error(import_chalk22.default.red("No input provided. Pipe content or pass text as argument."));
17763
17815
  process.exit(1);
17764
17816
  }
17765
- inputText = fs29.readFileSync(0, "utf-8");
17817
+ inputText = fs30.readFileSync(0, "utf-8");
17766
17818
  }
17767
17819
  if (!inputText.trim()) {
17768
17820
  console.error(import_chalk22.default.red("No text provided."));
17769
17821
  process.exit(1);
17770
17822
  }
17771
- const fs28 = require("fs");
17772
- if (text !== "-" && fs28.existsSync(text)) {
17823
+ const fs29 = require("fs");
17824
+ if (text !== "-" && fs29.existsSync(text)) {
17773
17825
  console.log(import_chalk22.default.yellow(`"${text}" looks like a file. Did you mean: hyv scan ${text}`));
17774
17826
  process.exit(1);
17775
17827
  }
@@ -17896,11 +17948,11 @@ function registerDiffCommand(program3) {
17896
17948
  ${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} available`));
17897
17949
  console.log(import_chalk24.default.dim(` run: hyv fix ${file} -i to apply`));
17898
17950
  if (options.apply && filePath !== "stdin") {
17899
- const fs28 = require("fs");
17951
+ const fs29 = require("fs");
17900
17952
  const path26 = require("path");
17901
17953
  const backupPath = filePath + ".bak";
17902
- fs28.copyFileSync(filePath, backupPath);
17903
- fs28.writeFileSync(filePath, result.fixed);
17954
+ fs29.copyFileSync(filePath, backupPath);
17955
+ fs29.writeFileSync(filePath, result.fixed);
17904
17956
  console.log(import_chalk24.default.green(` \u2713 Applied. Backup: ${path26.basename(backupPath)}`));
17905
17957
  }
17906
17958
  } catch (error) {
@@ -18076,7 +18128,7 @@ function registerRulesCommand(program3) {
18076
18128
 
18077
18129
  // src/commands/batch.ts
18078
18130
  var import_chalk26 = __toESM(require_source());
18079
- var fs19 = __toESM(require("fs"));
18131
+ var fs20 = __toESM(require("fs"));
18080
18132
  var path19 = __toESM(require("path"));
18081
18133
  init_pipeline();
18082
18134
  init_local_profile();
@@ -18111,9 +18163,9 @@ No files matching: ${pattern}`));
18111
18163
  const results = [];
18112
18164
  for (const file of files) {
18113
18165
  const absPath = path19.resolve(file);
18114
- if (!fs19.existsSync(absPath))
18166
+ if (!fs20.existsSync(absPath))
18115
18167
  continue;
18116
- const text = fs19.readFileSync(absPath, "utf-8");
18168
+ const text = fs20.readFileSync(absPath, "utf-8");
18117
18169
  const result = runPipeline(text, profile, options.fix || false);
18118
18170
  results.push({
18119
18171
  file,
@@ -18179,7 +18231,7 @@ No files matching: ${pattern}`));
18179
18231
 
18180
18232
  // src/commands/watch.ts
18181
18233
  var import_chalk27 = __toESM(require_source());
18182
- var fs20 = __toESM(require("fs"));
18234
+ var fs21 = __toESM(require("fs"));
18183
18235
  var path20 = __toESM(require("path"));
18184
18236
  init_pipeline();
18185
18237
  init_local_profile();
@@ -18188,7 +18240,7 @@ function registerWatchCommand(program3) {
18188
18240
  try {
18189
18241
  const profile = await loadProfileForCommand(options.profile);
18190
18242
  const absPath = path20.resolve(file);
18191
- if (!fs20.existsSync(absPath)) {
18243
+ if (!fs21.existsSync(absPath)) {
18192
18244
  console.error(import_chalk27.default.red(`File not found: ${absPath}`));
18193
18245
  process.exit(1);
18194
18246
  }
@@ -18211,7 +18263,7 @@ watching ${absPath}`));
18211
18263
  let lastScore = 0;
18212
18264
  let ignoreWatchUntil = 0;
18213
18265
  const runScan = () => {
18214
- const text = fs20.readFileSync(absPath, "utf-8");
18266
+ const text = fs21.readFileSync(absPath, "utf-8");
18215
18267
  const result = runPipeline(text, profile, options.command === "fix");
18216
18268
  scanCount++;
18217
18269
  const now = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
@@ -18253,7 +18305,7 @@ watching ${absPath}`));
18253
18305
  console.log("");
18254
18306
  };
18255
18307
  runScan();
18256
- fs20.watch(absPath, (eventType) => {
18308
+ fs21.watch(absPath, (eventType) => {
18257
18309
  if (eventType !== "change")
18258
18310
  return;
18259
18311
  if (Date.now() < ignoreWatchUntil)
@@ -18278,7 +18330,7 @@ watching ${absPath}`));
18278
18330
 
18279
18331
  // src/commands/demo.ts
18280
18332
  var import_chalk28 = __toESM(require_source());
18281
- var fs21 = __toESM(require("fs"));
18333
+ var fs22 = __toESM(require("fs"));
18282
18334
  var SAMPLES = {
18283
18335
  linkedin: {
18284
18336
  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.
@@ -18373,7 +18425,7 @@ function registerDemoCommand(program3) {
18373
18425
  }
18374
18426
  if (options.output) {
18375
18427
  const outputPath = require("path").resolve(options.output);
18376
- fs21.writeFileSync(outputPath, sample.text);
18428
+ fs22.writeFileSync(outputPath, sample.text);
18377
18429
  console.log(import_chalk28.default.green(`
18378
18430
  \u2713 Sample written to ${outputPath}`));
18379
18431
  if (sample.patterns.length > 0) {
@@ -18440,22 +18492,22 @@ function registerOpenCommand(program3) {
18440
18492
  init_welcome();
18441
18493
 
18442
18494
  // src/lib/onboarding.ts
18443
- var fs22 = __toESM(require("fs"));
18495
+ var fs23 = __toESM(require("fs"));
18444
18496
  var path21 = __toESM(require("path"));
18445
18497
  var os9 = __toESM(require("os"));
18446
18498
  var hyvDir = path21.join(os9.homedir(), ".hyv");
18447
18499
  var onboardingFile = path21.join(hyvDir, "onboarding-complete.json");
18448
18500
  function hasCompletedOnboarding() {
18449
18501
  try {
18450
- return fs22.existsSync(onboardingFile);
18502
+ return fs23.existsSync(onboardingFile);
18451
18503
  } catch {
18452
18504
  return false;
18453
18505
  }
18454
18506
  }
18455
18507
  function markOnboardingComplete(version) {
18456
- if (!fs22.existsSync(hyvDir))
18457
- fs22.mkdirSync(hyvDir, { recursive: true });
18458
- fs22.writeFileSync(
18508
+ if (!fs23.existsSync(hyvDir))
18509
+ fs23.mkdirSync(hyvDir, { recursive: true });
18510
+ fs23.writeFileSync(
18459
18511
  onboardingFile,
18460
18512
  JSON.stringify({ version, completed_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
18461
18513
  );
@@ -18463,13 +18515,13 @@ function markOnboardingComplete(version) {
18463
18515
 
18464
18516
  // src/commands/welcome.ts
18465
18517
  init_welcome_flow();
18466
- var fs23 = __toESM(require("fs"));
18518
+ var fs24 = __toESM(require("fs"));
18467
18519
  var path22 = __toESM(require("path"));
18468
18520
  function registerWelcomeCommand(program3) {
18469
18521
  const pkgVersion3 = (() => {
18470
18522
  try {
18471
18523
  const pkgPath3 = path22.resolve(__dirname, "..", "package.json");
18472
- return JSON.parse(fs23.readFileSync(pkgPath3, "utf-8")).version;
18524
+ return JSON.parse(fs24.readFileSync(pkgPath3, "utf-8")).version;
18473
18525
  } catch {
18474
18526
  return "0.0.0";
18475
18527
  }
@@ -18650,7 +18702,7 @@ Current: ${getEngineLabel()}
18650
18702
  }
18651
18703
 
18652
18704
  // src/mcp.ts
18653
- var fs24 = __toESM(require("fs"));
18705
+ var fs25 = __toESM(require("fs"));
18654
18706
  var path23 = __toESM(require("path"));
18655
18707
  var os10 = __toESM(require("os"));
18656
18708
  init_classifier();
@@ -18667,16 +18719,16 @@ var VOICE_MD = path23.join(os10.homedir(), ".hyv", "voice.md");
18667
18719
  var MAX_RESPONSE_CHARS = 12e4;
18668
18720
  var MAX_SCAN_FILE_BYTES = 2 * 1024 * 1024;
18669
18721
  function readScanFile(filePath) {
18670
- const cwdReal = fs24.realpathSync(process.cwd());
18671
- const resolved = fs24.realpathSync(path23.resolve(filePath));
18722
+ const cwdReal = fs25.realpathSync(process.cwd());
18723
+ const resolved = fs25.realpathSync(path23.resolve(filePath));
18672
18724
  if (!resolved.startsWith(cwdReal + path23.sep) && resolved !== cwdReal) {
18673
18725
  throw new Error(`file must be in the current working directory (${cwdReal})`);
18674
18726
  }
18675
- const stat = fs24.statSync(resolved);
18727
+ const stat = fs25.statSync(resolved);
18676
18728
  if (stat.size > MAX_SCAN_FILE_BYTES) {
18677
18729
  throw new Error(`file too large (max ${MAX_SCAN_FILE_BYTES} bytes)`);
18678
18730
  }
18679
- return fs24.readFileSync(resolved, "utf-8");
18731
+ return fs25.readFileSync(resolved, "utf-8");
18680
18732
  }
18681
18733
  function toolResultPayload(result) {
18682
18734
  const isError = result.startsWith("Error:") || result.startsWith("Unknown tool:");
@@ -18688,7 +18740,7 @@ function toolResultPayload(result) {
18688
18740
  var pkgPath = path23.resolve(__dirname, "..", "package.json");
18689
18741
  var pkgVersion = (() => {
18690
18742
  try {
18691
- return JSON.parse(fs24.readFileSync(pkgPath, "utf-8")).version;
18743
+ return JSON.parse(fs25.readFileSync(pkgPath, "utf-8")).version;
18692
18744
  } catch {
18693
18745
  return "2.7.1";
18694
18746
  }
@@ -19269,7 +19321,7 @@ async function handleRequest(line) {
19269
19321
  }
19270
19322
  async function startMcpServer() {
19271
19323
  const access = await getAccessState().catch(() => null);
19272
- if (fs24.existsSync(VOICE_MD)) {
19324
+ if (fs25.existsSync(VOICE_MD)) {
19273
19325
  mcpLog("info", `voice profile: ${VOICE_MD}`);
19274
19326
  } else {
19275
19327
  mcpLog("info", "free local engine ready \u2014 no voice profile yet");
@@ -19302,7 +19354,7 @@ async function startMcpServer() {
19302
19354
 
19303
19355
  // src/lib/mcp-setup.ts
19304
19356
  var import_chalk32 = __toESM(require_source());
19305
- var fs25 = __toESM(require("fs"));
19357
+ var fs26 = __toESM(require("fs"));
19306
19358
  var path24 = __toESM(require("path"));
19307
19359
  var os11 = __toESM(require("os"));
19308
19360
  init_version();
@@ -19396,7 +19448,7 @@ async function runMcpSelfTest() {
19396
19448
  failed++;
19397
19449
  }
19398
19450
  const voiceMd = path24.join(HOME2, ".hyv", "voice.md");
19399
- if (fs25.existsSync(voiceMd)) {
19451
+ if (fs26.existsSync(voiceMd)) {
19400
19452
  console.log(import_chalk32.default.green(" \u2713 voice profile found"));
19401
19453
  passed++;
19402
19454
  } else {
@@ -19436,7 +19488,7 @@ async function runMcpSelfTest() {
19436
19488
  }
19437
19489
 
19438
19490
  // src/commands/export.ts
19439
- var fs26 = __toESM(require("fs"));
19491
+ var fs27 = __toESM(require("fs"));
19440
19492
  init_api();
19441
19493
  var FORMATS = {
19442
19494
  claude: {
@@ -19523,7 +19575,7 @@ async function exportCommand(format, opts) {
19523
19575
  const prompt = fmt.wrap(detail.profile, detail.body);
19524
19576
  if (opts.output) {
19525
19577
  const outFile = opts.output.replace("{name}", profile.slug || profile.name);
19526
- fs26.writeFileSync(outFile, prompt);
19578
+ fs27.writeFileSync(outFile, prompt);
19527
19579
  results.push({ profile: profile.name, file: outFile });
19528
19580
  } else {
19529
19581
  results.push({ profile: profile.name, prompt });
@@ -19560,13 +19612,13 @@ async function exportCommand(format, opts) {
19560
19612
  // src/index.ts
19561
19613
  init_access();
19562
19614
  init_welcome();
19563
- var fs27 = __toESM(require("fs"));
19615
+ var fs28 = __toESM(require("fs"));
19564
19616
  var path25 = __toESM(require("path"));
19565
19617
  var program2 = new Command();
19566
19618
  var pkgPath2 = path25.resolve(__dirname, "..", "package.json");
19567
19619
  var pkgVersion2 = (() => {
19568
19620
  try {
19569
- return JSON.parse(fs27.readFileSync(pkgPath2, "utf-8")).version;
19621
+ return JSON.parse(fs28.readFileSync(pkgPath2, "utf-8")).version;
19570
19622
  } catch {
19571
19623
  return "2.7.1";
19572
19624
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holdyourvoice/hyv",
3
- "version": "2.9.9",
3
+ "version": "2.9.10",
4
4
  "description": "Free local AI writing scan for cursor & claude. MCP server, 220+ pattern detection, voice profiles. npx @holdyourvoice/hyv welcome",
5
5
  "main": "dist/index.js",
6
6
  "bin": {