@holdyourvoice/hyv 2.9.9 → 2.9.11

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 +19 -0
  2. package/dist/index.js +513 -343
  3. package/package.json +1 -1
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 path26 = require("node:path");
977
- var fs28 = require("node:fs");
976
+ var path27 = require("node:path");
977
+ var fs30 = 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 = path26.resolve(baseDir, baseName);
1920
- if (fs28.existsSync(localBin))
1919
+ const localBin = path27.resolve(baseDir, baseName);
1920
+ if (fs30.existsSync(localBin))
1921
1921
  return localBin;
1922
- if (sourceExt.includes(path26.extname(baseName)))
1922
+ if (sourceExt.includes(path27.extname(baseName)))
1923
1923
  return void 0;
1924
1924
  const foundExt = sourceExt.find(
1925
- (ext) => fs28.existsSync(`${localBin}${ext}`)
1925
+ (ext) => fs30.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 = fs28.realpathSync(this._scriptPath);
1938
+ resolvedScriptPath = fs30.realpathSync(this._scriptPath);
1939
1939
  } catch (err) {
1940
1940
  resolvedScriptPath = this._scriptPath;
1941
1941
  }
1942
- executableDir = path26.resolve(
1943
- path26.dirname(resolvedScriptPath),
1942
+ executableDir = path27.resolve(
1943
+ path27.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 = path26.basename(
1950
+ const legacyName = path27.basename(
1951
1951
  this._scriptPath,
1952
- path26.extname(this._scriptPath)
1952
+ path27.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(path26.extname(executableFile));
1963
+ launchWithNode = sourceExt.includes(path27.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 = path26.basename(filename, path26.extname(filename));
2820
+ this._name = path27.basename(filename, path27.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(path27) {
2835
- if (path27 === void 0)
2834
+ executableDir(path28) {
2835
+ if (path28 === void 0)
2836
2836
  return this._executableDir;
2837
- this._executableDir = path27;
2837
+ this._executableDir = path28;
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 path26 = [graph[toModel].parent, toModel];
3937
+ const path27 = [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
- path26.unshift(graph[cur].parent);
3941
+ path27.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 = path26;
3945
+ fn.conversion = path27;
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 os12 = require("os");
4185
+ var os13 = 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 = os12.release().split(".");
4233
+ const osRelease = os13.release().split(".");
4234
4234
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
4235
4235
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
4236
4236
  }
@@ -4611,6 +4611,121 @@ 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 backupFileIfExists(filePath) {
4623
+ if (!fs.existsSync(filePath)) {
4624
+ return false;
4625
+ }
4626
+ try {
4627
+ fs.copyFileSync(filePath, `${filePath}.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 = backupFileIfExists(profilePath);
4641
+ fs.writeFileSync(profilePath, content, { mode: 384 });
4642
+ return { written: true, backedUp };
4643
+ }
4644
+ function safeWriteJsonFile(filePath, content, existing) {
4645
+ if (!shouldReplaceCachedContent(existing, content)) {
4646
+ return { written: false, backedUp: false, reason: "empty-would-erase" };
4647
+ }
4648
+ if (existing !== null && existing === content) {
4649
+ return { written: false, backedUp: false, reason: "unchanged" };
4650
+ }
4651
+ const backedUp = backupFileIfExists(filePath);
4652
+ fs.writeFileSync(filePath, content, { mode: 384 });
4653
+ return { written: true, backedUp };
4654
+ }
4655
+ var fs;
4656
+ var init_data_safety = __esm({
4657
+ "src/lib/data-safety.ts"() {
4658
+ "use strict";
4659
+ fs = __toESM(require("fs"));
4660
+ }
4661
+ });
4662
+
4663
+ // src/lib/profile-meta.ts
4664
+ function ensureCacheDir() {
4665
+ if (!fs2.existsSync(CACHE_DIR)) {
4666
+ fs2.mkdirSync(CACHE_DIR, { recursive: true, mode: 448 });
4667
+ }
4668
+ }
4669
+ function readStore() {
4670
+ try {
4671
+ if (!fs2.existsSync(META_FILE))
4672
+ return {};
4673
+ return JSON.parse(fs2.readFileSync(META_FILE, "utf-8"));
4674
+ } catch {
4675
+ return {};
4676
+ }
4677
+ }
4678
+ function writeStore(store) {
4679
+ ensureCacheDir();
4680
+ fs2.writeFileSync(META_FILE, JSON.stringify(store, null, 2), { mode: 384 });
4681
+ }
4682
+ function getProfileMeta(cacheKey) {
4683
+ return readStore()[cacheKey];
4684
+ }
4685
+ function setProfileMeta(cacheKey, entry) {
4686
+ const store = readStore();
4687
+ store[cacheKey] = {
4688
+ ...store[cacheKey],
4689
+ ...entry,
4690
+ local_saved_at: entry.local_saved_at || (/* @__PURE__ */ new Date()).toISOString()
4691
+ };
4692
+ writeStore(store);
4693
+ }
4694
+ function parseIsoMs(iso) {
4695
+ if (!iso)
4696
+ return 0;
4697
+ const t = Date.parse(iso);
4698
+ return Number.isNaN(t) ? 0 : t;
4699
+ }
4700
+ function shouldApplyServerProfileUpdate(serverUpdatedAt, cacheKey, force = false) {
4701
+ if (force)
4702
+ return true;
4703
+ const meta = getProfileMeta(cacheKey);
4704
+ if (!meta)
4705
+ return true;
4706
+ const serverMs = parseIsoMs(serverUpdatedAt);
4707
+ const knownServerMs = parseIsoMs(meta.server_updated_at);
4708
+ const localMs = parseIsoMs(meta.local_saved_at);
4709
+ if (localMs > serverMs && localMs >= knownServerMs) {
4710
+ return false;
4711
+ }
4712
+ if (knownServerMs > serverMs) {
4713
+ return false;
4714
+ }
4715
+ return true;
4716
+ }
4717
+ var fs2, os, path, CACHE_DIR, META_FILE;
4718
+ var init_profile_meta = __esm({
4719
+ "src/lib/profile-meta.ts"() {
4720
+ "use strict";
4721
+ fs2 = __toESM(require("fs"));
4722
+ os = __toESM(require("os"));
4723
+ path = __toESM(require("path"));
4724
+ CACHE_DIR = path.join(os.homedir(), ".hyv", "cache");
4725
+ META_FILE = path.join(CACHE_DIR, "profile-meta.json");
4726
+ }
4727
+ });
4728
+
4614
4729
  // src/lib/config.ts
4615
4730
  function validateApiBase(raw) {
4616
4731
  const trimmed = raw.replace(/\/$/, "");
@@ -4687,21 +4802,21 @@ function toSafeProfileCacheKey(name) {
4687
4802
  function profilePathForName(name) {
4688
4803
  const safe = assertSafeProfileName(name);
4689
4804
  ensureHyvDir();
4690
- const profilePath = path.resolve(PROFILES_DIR, `${safe}.md`);
4691
- const profilesRoot = path.resolve(PROFILES_DIR) + path.sep;
4805
+ const profilePath = path2.resolve(PROFILES_DIR, `${safe}.md`);
4806
+ const profilesRoot = path2.resolve(PROFILES_DIR) + path2.sep;
4692
4807
  if (!profilePath.startsWith(profilesRoot)) {
4693
4808
  throw new Error("Invalid profile name");
4694
4809
  }
4695
4810
  return profilePath;
4696
4811
  }
4697
4812
  function ensureHyvDir() {
4698
- const dirs = [HYV_DIR, PROFILES_DIR, CACHE_DIR, QUEUE_DIR];
4813
+ const dirs = [HYV_DIR, PROFILES_DIR, CACHE_DIR2, QUEUE_DIR];
4699
4814
  for (const dir of dirs) {
4700
- if (!fs.existsSync(dir)) {
4701
- fs.mkdirSync(dir, { recursive: true, mode: 448 });
4815
+ if (!fs3.existsSync(dir)) {
4816
+ fs3.mkdirSync(dir, { recursive: true, mode: 448 });
4702
4817
  } else {
4703
4818
  try {
4704
- fs.chmodSync(dir, 448);
4819
+ fs3.chmodSync(dir, 448);
4705
4820
  } catch {
4706
4821
  }
4707
4822
  }
@@ -4709,33 +4824,33 @@ function ensureHyvDir() {
4709
4824
  }
4710
4825
  function appendSecureLine(filePath, line, dir) {
4711
4826
  if (dir) {
4712
- if (!fs.existsSync(dir))
4713
- fs.mkdirSync(dir, { recursive: true, mode: 448 });
4827
+ if (!fs3.existsSync(dir))
4828
+ fs3.mkdirSync(dir, { recursive: true, mode: 448 });
4714
4829
  else {
4715
4830
  try {
4716
- fs.chmodSync(dir, 448);
4831
+ fs3.chmodSync(dir, 448);
4717
4832
  } catch {
4718
4833
  }
4719
4834
  }
4720
4835
  }
4721
- if (!fs.existsSync(filePath)) {
4722
- fs.writeFileSync(filePath, line, { mode: 384 });
4836
+ if (!fs3.existsSync(filePath)) {
4837
+ fs3.writeFileSync(filePath, line, { mode: 384 });
4723
4838
  return;
4724
4839
  }
4725
4840
  try {
4726
- fs.chmodSync(filePath, 384);
4841
+ fs3.chmodSync(filePath, 384);
4727
4842
  } catch {
4728
4843
  }
4729
- fs.appendFileSync(filePath, line);
4844
+ fs3.appendFileSync(filePath, line);
4730
4845
  }
4731
4846
  function isInitialized() {
4732
- return fs.existsSync(AUTH_FILE);
4847
+ return fs3.existsSync(AUTH_FILE);
4733
4848
  }
4734
4849
  function readAuth() {
4735
4850
  try {
4736
- if (!fs.existsSync(AUTH_FILE))
4851
+ if (!fs3.existsSync(AUTH_FILE))
4737
4852
  return null;
4738
- const data = fs.readFileSync(AUTH_FILE, "utf-8");
4853
+ const data = fs3.readFileSync(AUTH_FILE, "utf-8");
4739
4854
  return JSON.parse(data);
4740
4855
  } catch {
4741
4856
  return null;
@@ -4743,13 +4858,19 @@ function readAuth() {
4743
4858
  }
4744
4859
  function writeAuth(auth) {
4745
4860
  ensureHyvDir();
4746
- fs.writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), { mode: 384 });
4861
+ if (fs3.existsSync(AUTH_FILE)) {
4862
+ try {
4863
+ fs3.copyFileSync(AUTH_FILE, `${AUTH_FILE}.hyv.bak`);
4864
+ } catch {
4865
+ }
4866
+ }
4867
+ fs3.writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), { mode: 384 });
4747
4868
  }
4748
4869
  function readConfig() {
4749
4870
  try {
4750
- if (!fs.existsSync(CONFIG_FILE))
4871
+ if (!fs3.existsSync(CONFIG_FILE))
4751
4872
  return {};
4752
- const data = fs.readFileSync(CONFIG_FILE, "utf-8");
4873
+ const data = fs3.readFileSync(CONFIG_FILE, "utf-8");
4753
4874
  return JSON.parse(data);
4754
4875
  } catch {
4755
4876
  return {};
@@ -4757,7 +4878,7 @@ function readConfig() {
4757
4878
  }
4758
4879
  function writeConfig(config) {
4759
4880
  ensureHyvDir();
4760
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 384 });
4881
+ fs3.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 384 });
4761
4882
  }
4762
4883
  function getToken() {
4763
4884
  const auth = readAuth();
@@ -4773,9 +4894,9 @@ function getToken() {
4773
4894
  }
4774
4895
  function listCachedProfiles() {
4775
4896
  try {
4776
- if (!fs.existsSync(PROFILES_DIR))
4897
+ if (!fs3.existsSync(PROFILES_DIR))
4777
4898
  return [];
4778
- return fs.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
4899
+ return fs3.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
4779
4900
  } catch {
4780
4901
  return [];
4781
4902
  }
@@ -4783,9 +4904,9 @@ function listCachedProfiles() {
4783
4904
  function readCachedProfile(name) {
4784
4905
  const profilePath = profilePathForName(name);
4785
4906
  try {
4786
- if (!fs.existsSync(profilePath))
4907
+ if (!fs3.existsSync(profilePath))
4787
4908
  return null;
4788
- return fs.readFileSync(profilePath, "utf-8");
4909
+ return fs3.readFileSync(profilePath, "utf-8");
4789
4910
  } catch (err) {
4790
4911
  if (err.message?.includes("Invalid profile name"))
4791
4912
  throw err;
@@ -4794,15 +4915,22 @@ function readCachedProfile(name) {
4794
4915
  }
4795
4916
  function writeCachedProfile(name, content) {
4796
4917
  const profilePath = profilePathForName(name);
4797
- fs.writeFileSync(profilePath, content, { mode: 384 });
4918
+ const existing = fs3.existsSync(profilePath) ? fs3.readFileSync(profilePath, "utf-8") : null;
4919
+ const result = safeWriteProfileFile(profilePath, content, existing);
4920
+ if (!result.written && result.reason === "empty-would-erase") {
4921
+ throw new Error("Refusing to replace profile with empty content");
4922
+ }
4923
+ if (result.written) {
4924
+ setProfileMeta(name, { local_saved_at: (/* @__PURE__ */ new Date()).toISOString() });
4925
+ }
4798
4926
  }
4799
4927
  function getQueuedSignals() {
4800
4928
  try {
4801
- if (!fs.existsSync(QUEUE_DIR))
4929
+ if (!fs3.existsSync(QUEUE_DIR))
4802
4930
  return [];
4803
- const files = fs.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
4931
+ const files = fs3.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
4804
4932
  return files.map((f) => {
4805
- const data = fs.readFileSync(path.join(QUEUE_DIR, f), "utf-8");
4933
+ const data = fs3.readFileSync(path2.join(QUEUE_DIR, f), "utf-8");
4806
4934
  return JSON.parse(data);
4807
4935
  });
4808
4936
  } catch {
@@ -4812,37 +4940,39 @@ function getQueuedSignals() {
4812
4940
  function queueSignal(signal) {
4813
4941
  ensureHyvDir();
4814
4942
  const id = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
4815
- const filePath = path.join(QUEUE_DIR, `${id}.json`);
4816
- fs.writeFileSync(filePath, JSON.stringify(signal, null, 2), { mode: 384 });
4943
+ const filePath = path2.join(QUEUE_DIR, `${id}.json`);
4944
+ fs3.writeFileSync(filePath, JSON.stringify(signal, null, 2), { mode: 384 });
4817
4945
  }
4818
4946
  function saveLastEditSession(session) {
4819
4947
  ensureHyvDir();
4820
4948
  const data = { ...session, saved_at: (/* @__PURE__ */ new Date()).toISOString() };
4821
- fs.writeFileSync(LAST_SESSION_FILE, JSON.stringify(data, null, 2), { mode: 384 });
4949
+ fs3.writeFileSync(LAST_SESSION_FILE, JSON.stringify(data, null, 2), { mode: 384 });
4822
4950
  }
4823
4951
  function readLastEditSession() {
4824
4952
  try {
4825
- if (!fs.existsSync(LAST_SESSION_FILE))
4953
+ if (!fs3.existsSync(LAST_SESSION_FILE))
4826
4954
  return null;
4827
- return JSON.parse(fs.readFileSync(LAST_SESSION_FILE, "utf-8"));
4955
+ return JSON.parse(fs3.readFileSync(LAST_SESSION_FILE, "utf-8"));
4828
4956
  } catch {
4829
4957
  return null;
4830
4958
  }
4831
4959
  }
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;
4960
+ var fs3, path2, os2, HYV_DIR, AUTH_FILE, CONFIG_FILE, PROFILES_DIR, CACHE_DIR2, QUEUE_DIR, LAST_SESSION_FILE, ALLOWED_API_HOSTS, ALLOWED_OAUTH_HOSTS, API_BASE, PROFILE_NAME_RE;
4833
4961
  var init_config = __esm({
4834
4962
  "src/lib/config.ts"() {
4835
4963
  "use strict";
4836
- fs = __toESM(require("fs"));
4837
- path = __toESM(require("path"));
4838
- os = __toESM(require("os"));
4839
- HYV_DIR = path.join(os.homedir(), ".hyv");
4840
- AUTH_FILE = path.join(HYV_DIR, "auth.json");
4841
- CONFIG_FILE = path.join(HYV_DIR, "config.json");
4842
- PROFILES_DIR = path.join(HYV_DIR, "profiles");
4843
- CACHE_DIR = path.join(HYV_DIR, "cache");
4844
- QUEUE_DIR = path.join(HYV_DIR, "queue");
4845
- LAST_SESSION_FILE = path.join(HYV_DIR, "last-session.json");
4964
+ fs3 = __toESM(require("fs"));
4965
+ path2 = __toESM(require("path"));
4966
+ os2 = __toESM(require("os"));
4967
+ init_data_safety();
4968
+ init_profile_meta();
4969
+ HYV_DIR = path2.join(os2.homedir(), ".hyv");
4970
+ AUTH_FILE = path2.join(HYV_DIR, "auth.json");
4971
+ CONFIG_FILE = path2.join(HYV_DIR, "config.json");
4972
+ PROFILES_DIR = path2.join(HYV_DIR, "profiles");
4973
+ CACHE_DIR2 = path2.join(HYV_DIR, "cache");
4974
+ QUEUE_DIR = path2.join(HYV_DIR, "queue");
4975
+ LAST_SESSION_FILE = path2.join(HYV_DIR, "last-session.json");
4846
4976
  ALLOWED_API_HOSTS = /* @__PURE__ */ new Set([
4847
4977
  "holdyourvoice.com",
4848
4978
  "www.holdyourvoice.com",
@@ -4860,11 +4990,11 @@ var init_config = __esm({
4860
4990
  var require_is_docker = __commonJS({
4861
4991
  "node_modules/is-docker/index.js"(exports2, module2) {
4862
4992
  "use strict";
4863
- var fs28 = require("fs");
4993
+ var fs30 = require("fs");
4864
4994
  var isDocker;
4865
4995
  function hasDockerEnv() {
4866
4996
  try {
4867
- fs28.statSync("/.dockerenv");
4997
+ fs30.statSync("/.dockerenv");
4868
4998
  return true;
4869
4999
  } catch (_2) {
4870
5000
  return false;
@@ -4872,7 +5002,7 @@ var require_is_docker = __commonJS({
4872
5002
  }
4873
5003
  function hasDockerCGroup() {
4874
5004
  try {
4875
- return fs28.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
5005
+ return fs30.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4876
5006
  } catch (_2) {
4877
5007
  return false;
4878
5008
  }
@@ -4890,21 +5020,21 @@ var require_is_docker = __commonJS({
4890
5020
  var require_is_wsl = __commonJS({
4891
5021
  "node_modules/is-wsl/index.js"(exports2, module2) {
4892
5022
  "use strict";
4893
- var os12 = require("os");
4894
- var fs28 = require("fs");
5023
+ var os13 = require("os");
5024
+ var fs30 = require("fs");
4895
5025
  var isDocker = require_is_docker();
4896
5026
  var isWsl = () => {
4897
5027
  if (process.platform !== "linux") {
4898
5028
  return false;
4899
5029
  }
4900
- if (os12.release().toLowerCase().includes("microsoft")) {
5030
+ if (os13.release().toLowerCase().includes("microsoft")) {
4901
5031
  if (isDocker()) {
4902
5032
  return false;
4903
5033
  }
4904
5034
  return true;
4905
5035
  }
4906
5036
  try {
4907
- return fs28.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
5037
+ return fs30.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
4908
5038
  } catch (_2) {
4909
5039
  return false;
4910
5040
  }
@@ -4943,17 +5073,17 @@ var require_define_lazy_prop = __commonJS({
4943
5073
  // node_modules/open/index.js
4944
5074
  var require_open = __commonJS({
4945
5075
  "node_modules/open/index.js"(exports2, module2) {
4946
- var path26 = require("path");
5076
+ var path27 = require("path");
4947
5077
  var childProcess = require("child_process");
4948
- var { promises: fs28, constants: fsConstants } = require("fs");
5078
+ var { promises: fs30, constants: fsConstants } = require("fs");
4949
5079
  var isWsl = require_is_wsl();
4950
5080
  var isDocker = require_is_docker();
4951
5081
  var defineLazyProperty = require_define_lazy_prop();
4952
- var localXdgOpenPath = path26.join(__dirname, "xdg-open");
5082
+ var localXdgOpenPath = path27.join(__dirname, "xdg-open");
4953
5083
  var { platform, arch } = process;
4954
5084
  var hasContainerEnv = () => {
4955
5085
  try {
4956
- fs28.statSync("/run/.containerenv");
5086
+ fs30.statSync("/run/.containerenv");
4957
5087
  return true;
4958
5088
  } catch {
4959
5089
  return false;
@@ -4976,14 +5106,14 @@ var require_open = __commonJS({
4976
5106
  const configFilePath = "/etc/wsl.conf";
4977
5107
  let isConfigFileExists = false;
4978
5108
  try {
4979
- await fs28.access(configFilePath, fsConstants.F_OK);
5109
+ await fs30.access(configFilePath, fsConstants.F_OK);
4980
5110
  isConfigFileExists = true;
4981
5111
  } catch {
4982
5112
  }
4983
5113
  if (!isConfigFileExists) {
4984
5114
  return defaultMountPoint;
4985
5115
  }
4986
- const configContent = await fs28.readFile(configFilePath, { encoding: "utf8" });
5116
+ const configContent = await fs30.readFile(configFilePath, { encoding: "utf8" });
4987
5117
  const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
4988
5118
  if (!configMountPoint) {
4989
5119
  return defaultMountPoint;
@@ -5083,7 +5213,7 @@ var require_open = __commonJS({
5083
5213
  const isBundled = !__dirname || __dirname === "/";
5084
5214
  let exeLocalXdgOpen = false;
5085
5215
  try {
5086
- await fs28.access(localXdgOpenPath, fsConstants.X_OK);
5216
+ await fs30.access(localXdgOpenPath, fsConstants.X_OK);
5087
5217
  exeLocalXdgOpen = true;
5088
5218
  } catch {
5089
5219
  }
@@ -5283,20 +5413,20 @@ var init_free_paid = __esm({
5283
5413
  // src/lib/version.ts
5284
5414
  function pkgRoot() {
5285
5415
  const candidates = [
5286
- path2.resolve(__dirname, ".."),
5416
+ path3.resolve(__dirname, ".."),
5287
5417
  // dist/ or src/lib/ → cli/
5288
- path2.resolve(__dirname, "..", "..")
5418
+ path3.resolve(__dirname, "..", "..")
5289
5419
  // src/lib/ → cli/
5290
5420
  ];
5291
5421
  for (const root of candidates) {
5292
- if (fs2.existsSync(path2.join(root, "package.json")))
5422
+ if (fs4.existsSync(path3.join(root, "package.json")))
5293
5423
  return root;
5294
5424
  }
5295
- return path2.resolve(__dirname, "..");
5425
+ return path3.resolve(__dirname, "..");
5296
5426
  }
5297
5427
  function getCliVersion() {
5298
5428
  try {
5299
- const pkg = JSON.parse(fs2.readFileSync(path2.join(PKG_ROOT, "package.json"), "utf-8"));
5429
+ const pkg = JSON.parse(fs4.readFileSync(path3.join(PKG_ROOT, "package.json"), "utf-8"));
5300
5430
  return pkg.version || "unknown";
5301
5431
  } catch {
5302
5432
  return "unknown";
@@ -5304,8 +5434,8 @@ function getCliVersion() {
5304
5434
  }
5305
5435
  function getRulesVersion() {
5306
5436
  try {
5307
- if (fs2.existsSync(RULES_MANIFEST)) {
5308
- return JSON.parse(fs2.readFileSync(RULES_MANIFEST, "utf-8")).version || "unknown";
5437
+ if (fs4.existsSync(RULES_MANIFEST)) {
5438
+ return JSON.parse(fs4.readFileSync(RULES_MANIFEST, "utf-8")).version || "unknown";
5309
5439
  }
5310
5440
  } catch {
5311
5441
  }
@@ -5343,15 +5473,15 @@ function fetchLatestNpmVersion(timeoutMs = 8e3) {
5343
5473
  );
5344
5474
  });
5345
5475
  }
5346
- var fs2, path2, import_child_process, PKG_ROOT, RULES_MANIFEST;
5476
+ var fs4, path3, import_child_process, PKG_ROOT, RULES_MANIFEST;
5347
5477
  var init_version = __esm({
5348
5478
  "src/lib/version.ts"() {
5349
5479
  "use strict";
5350
- fs2 = __toESM(require("fs"));
5351
- path2 = __toESM(require("path"));
5480
+ fs4 = __toESM(require("fs"));
5481
+ path3 = __toESM(require("path"));
5352
5482
  import_child_process = require("child_process");
5353
5483
  PKG_ROOT = pkgRoot();
5354
- RULES_MANIFEST = path2.join(PKG_ROOT, "assets", "detection-rules.json");
5484
+ RULES_MANIFEST = path3.join(PKG_ROOT, "assets", "detection-rules.json");
5355
5485
  }
5356
5486
  });
5357
5487
 
@@ -5711,15 +5841,15 @@ function recordEvent(event, meta) {
5711
5841
  } catch {
5712
5842
  }
5713
5843
  }
5714
- var path3, os2, TELEMETRY_DIR, TELEMETRY_FILE;
5844
+ var path4, os3, TELEMETRY_DIR, TELEMETRY_FILE;
5715
5845
  var init_telemetry = __esm({
5716
5846
  "src/lib/telemetry.ts"() {
5717
5847
  "use strict";
5718
- path3 = __toESM(require("path"));
5719
- os2 = __toESM(require("os"));
5848
+ path4 = __toESM(require("path"));
5849
+ os3 = __toESM(require("os"));
5720
5850
  init_config();
5721
- TELEMETRY_DIR = path3.join(os2.homedir(), ".hyv", "telemetry");
5722
- TELEMETRY_FILE = path3.join(TELEMETRY_DIR, "events.jsonl");
5851
+ TELEMETRY_DIR = path4.join(os3.homedir(), ".hyv", "telemetry");
5852
+ TELEMETRY_FILE = path4.join(TELEMETRY_DIR, "events.jsonl");
5723
5853
  }
5724
5854
  });
5725
5855
 
@@ -5730,11 +5860,11 @@ __export(api_exports, {
5730
5860
  apiPost: () => apiPost,
5731
5861
  requireSubscription: () => requireSubscription
5732
5862
  });
5733
- async function request2(method, path26, body) {
5863
+ async function request2(method, path27, body) {
5734
5864
  const token = await getValidToken();
5735
5865
  if (!token)
5736
5866
  throw new Error("you're not signed in yet. run: hyv init");
5737
- const url = `${API_BASE}${path26}`;
5867
+ const url = `${API_BASE}${path27}`;
5738
5868
  const opts = {
5739
5869
  method,
5740
5870
  headers: {
@@ -5759,11 +5889,11 @@ async function request2(method, path26, body) {
5759
5889
  }
5760
5890
  return res.json();
5761
5891
  }
5762
- function apiGet(path26) {
5763
- return request2("GET", path26);
5892
+ function apiGet(path27) {
5893
+ return request2("GET", path27);
5764
5894
  }
5765
- function apiPost(path26, body) {
5766
- return request2("POST", path26, body);
5895
+ function apiPost(path27, body) {
5896
+ return request2("POST", path27, body);
5767
5897
  }
5768
5898
  async function requireSubscription() {
5769
5899
  const { requirePaidFeature: requirePaidFeature2 } = await Promise.resolve().then(() => (init_access(), access_exports));
@@ -5947,7 +6077,7 @@ async function fetchAndCache(slug) {
5947
6077
  };
5948
6078
  const entry = { data: profile, timestamp: Date.now(), version: 1 };
5949
6079
  memoryCache.set(slug, entry);
5950
- saveToDiskCache(slug, entry);
6080
+ saveToDiskCache(slug, entry, profile.updated_at);
5951
6081
  return profile;
5952
6082
  } catch (err) {
5953
6083
  const fallback = loadFromDiskCache(slug);
@@ -5968,14 +6098,14 @@ async function refreshInBackground(slug) {
5968
6098
  }
5969
6099
  }
5970
6100
  function getDiskCachePath(slug) {
5971
- return path4.join(DISK_CACHE_DIR, `${slug.replace(/[^a-z0-9-]/gi, "_")}.json`);
6101
+ return path5.join(DISK_CACHE_DIR, `${slug.replace(/[^a-z0-9-]/gi, "_")}.json`);
5972
6102
  }
5973
6103
  function loadFromDiskCache(slug) {
5974
6104
  try {
5975
6105
  const cachePath = getDiskCachePath(slug);
5976
- if (!fs3.existsSync(cachePath))
6106
+ if (!fs5.existsSync(cachePath))
5977
6107
  return null;
5978
- const content = fs3.readFileSync(cachePath, "utf-8");
6108
+ const content = fs5.readFileSync(cachePath, "utf-8");
5979
6109
  const entry = JSON.parse(content);
5980
6110
  if (!entry.data || !entry.timestamp)
5981
6111
  return null;
@@ -5984,12 +6114,24 @@ function loadFromDiskCache(slug) {
5984
6114
  return null;
5985
6115
  }
5986
6116
  }
5987
- function saveToDiskCache(slug, entry) {
6117
+ function saveToDiskCache(slug, entry, serverUpdatedAt) {
5988
6118
  try {
5989
- if (!fs3.existsSync(DISK_CACHE_DIR)) {
5990
- fs3.mkdirSync(DISK_CACHE_DIR, { recursive: true });
6119
+ if (!fs5.existsSync(DISK_CACHE_DIR)) {
6120
+ fs5.mkdirSync(DISK_CACHE_DIR, { recursive: true });
6121
+ }
6122
+ const cachePath = getDiskCachePath(slug);
6123
+ const existing = fs5.existsSync(cachePath) ? fs5.readFileSync(cachePath, "utf-8") : null;
6124
+ const incoming = JSON.stringify(entry);
6125
+ const localUpdated = getProfileMeta(slug)?.server_updated_at;
6126
+ const serverMs = parseIsoMs(serverUpdatedAt || entry.data.updated_at);
6127
+ const localMs = parseIsoMs(localUpdated);
6128
+ if (existing && localMs > serverMs && serverMs > 0) {
6129
+ return;
6130
+ }
6131
+ const result = safeWriteJsonFile(cachePath, incoming, existing);
6132
+ if (result.written && serverUpdatedAt) {
6133
+ setProfileMeta(slug, { server_updated_at: serverUpdatedAt });
5991
6134
  }
5992
- fs3.writeFileSync(getDiskCachePath(slug), JSON.stringify(entry), "utf-8");
5993
6135
  } catch {
5994
6136
  }
5995
6137
  }
@@ -6007,18 +6149,20 @@ function buildMinimalProfile(slug, body) {
6007
6149
  cadence: {}
6008
6150
  };
6009
6151
  }
6010
- var fs3, path4, os3, CACHE_TTL, memoryCache, DISK_CACHE_DIR;
6152
+ var fs5, path5, os4, CACHE_TTL, memoryCache, DISK_CACHE_DIR;
6011
6153
  var init_profile = __esm({
6012
6154
  "src/lib/profile.ts"() {
6013
6155
  "use strict";
6014
6156
  init_api();
6015
6157
  init_config();
6016
- fs3 = __toESM(require("fs"));
6017
- path4 = __toESM(require("path"));
6018
- os3 = __toESM(require("os"));
6158
+ init_data_safety();
6159
+ init_profile_meta();
6160
+ fs5 = __toESM(require("fs"));
6161
+ path5 = __toESM(require("path"));
6162
+ os4 = __toESM(require("os"));
6019
6163
  CACHE_TTL = 5 * 60 * 1e3;
6020
6164
  memoryCache = /* @__PURE__ */ new Map();
6021
- DISK_CACHE_DIR = path4.join(os3.homedir(), ".hyv", "cache", "profiles");
6165
+ DISK_CACHE_DIR = path5.join(os4.homedir(), ".hyv", "cache", "profiles");
6022
6166
  }
6023
6167
  });
6024
6168
 
@@ -6033,14 +6177,14 @@ __export(local_profile_exports, {
6033
6177
  loadProfileFromDiskCache: () => loadProfileFromDiskCache
6034
6178
  });
6035
6179
  function cachePathForSlug(slug) {
6036
- return path5.join(DISK_CACHE_DIR2, `${slug.replace(/[^a-z0-9-]/gi, "_")}.json`);
6180
+ return path6.join(DISK_CACHE_DIR2, `${slug.replace(/[^a-z0-9-]/gi, "_")}.json`);
6037
6181
  }
6038
6182
  function loadProfileFromDiskCache(slug) {
6039
6183
  try {
6040
6184
  const cachePath = cachePathForSlug(slug);
6041
- if (!fs4.existsSync(cachePath))
6185
+ if (!fs6.existsSync(cachePath))
6042
6186
  return void 0;
6043
- const entry = JSON.parse(fs4.readFileSync(cachePath, "utf-8"));
6187
+ const entry = JSON.parse(fs6.readFileSync(cachePath, "utf-8"));
6044
6188
  if (entry?.data?.body)
6045
6189
  return entry.data;
6046
6190
  } catch {
@@ -6064,9 +6208,9 @@ function profileFromMarkdown(slug, body) {
6064
6208
  function getProfileCacheAge(slug) {
6065
6209
  try {
6066
6210
  const cachePath = cachePathForSlug(slug);
6067
- if (!fs4.existsSync(cachePath))
6211
+ if (!fs6.existsSync(cachePath))
6068
6212
  return null;
6069
- const entry = JSON.parse(fs4.readFileSync(cachePath, "utf-8"));
6213
+ const entry = JSON.parse(fs6.readFileSync(cachePath, "utf-8"));
6070
6214
  return entry.timestamp || null;
6071
6215
  } catch {
6072
6216
  return null;
@@ -6074,9 +6218,9 @@ function getProfileCacheAge(slug) {
6074
6218
  }
6075
6219
  function listDiskCachedProfiles() {
6076
6220
  try {
6077
- if (!fs4.existsSync(DISK_CACHE_DIR2))
6221
+ if (!fs6.existsSync(DISK_CACHE_DIR2))
6078
6222
  return [];
6079
- return fs4.readdirSync(DISK_CACHE_DIR2).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
6223
+ return fs6.readdirSync(DISK_CACHE_DIR2).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
6080
6224
  } catch {
6081
6225
  return [];
6082
6226
  }
@@ -6133,17 +6277,17 @@ function hasRichProfile(profile) {
6133
6277
  return false;
6134
6278
  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
6279
  }
6136
- var fs4, path5, os4, DISK_CACHE_DIR2;
6280
+ var fs6, path6, os5, DISK_CACHE_DIR2;
6137
6281
  var init_local_profile = __esm({
6138
6282
  "src/lib/local-profile.ts"() {
6139
6283
  "use strict";
6140
- fs4 = __toESM(require("fs"));
6141
- path5 = __toESM(require("path"));
6142
- os4 = __toESM(require("os"));
6284
+ fs6 = __toESM(require("fs"));
6285
+ path6 = __toESM(require("path"));
6286
+ os5 = __toESM(require("os"));
6143
6287
  init_config();
6144
6288
  init_config();
6145
6289
  init_profile();
6146
- DISK_CACHE_DIR2 = path5.join(os4.homedir(), ".hyv", "cache", "profiles");
6290
+ DISK_CACHE_DIR2 = path6.join(os5.homedir(), ".hyv", "cache", "profiles");
6147
6291
  }
6148
6292
  });
6149
6293
 
@@ -6233,24 +6377,24 @@ var init_profile_parse = __esm({
6233
6377
 
6234
6378
  // src/lib/rules-loader.ts
6235
6379
  function loadSupplementalPatterns() {
6236
- const p = path8.join(CACHE_DIR, "supplemental-rules.json");
6380
+ const p = path9.join(CACHE_DIR2, "supplemental-rules.json");
6237
6381
  try {
6238
- if (!fs7.existsSync(p))
6382
+ if (!fs9.existsSync(p))
6239
6383
  return [];
6240
- const data = JSON.parse(fs7.readFileSync(p, "utf-8"));
6384
+ const data = JSON.parse(fs9.readFileSync(p, "utf-8"));
6241
6385
  return Array.isArray(data.patterns) ? data.patterns : [];
6242
6386
  } catch {
6243
6387
  return [];
6244
6388
  }
6245
6389
  }
6246
- var fs7, path8, BUNDLED_MANIFEST;
6390
+ var fs9, path9, BUNDLED_MANIFEST;
6247
6391
  var init_rules_loader = __esm({
6248
6392
  "src/lib/rules-loader.ts"() {
6249
6393
  "use strict";
6250
- fs7 = __toESM(require("fs"));
6251
- path8 = __toESM(require("path"));
6394
+ fs9 = __toESM(require("fs"));
6395
+ path9 = __toESM(require("path"));
6252
6396
  init_config();
6253
- BUNDLED_MANIFEST = path8.resolve(__dirname, "..", "..", "assets", "detection-rules.json");
6397
+ BUNDLED_MANIFEST = path9.resolve(__dirname, "..", "..", "assets", "detection-rules.json");
6254
6398
  }
6255
6399
  });
6256
6400
 
@@ -7282,10 +7426,10 @@ function Ht(n7) {
7282
7426
  return isNaN(n7) ? n7.charCodeAt(0) : parseInt(n7, 10);
7283
7427
  }
7284
7428
  function ps(n7) {
7285
- return n7.replace(as, fe).replace(ls, ue).replace(cs, qt).replace(fs8, de).replace(us, pe);
7429
+ return n7.replace(as, fe).replace(ls, ue).replace(cs, qt).replace(fs10, de).replace(us, pe);
7286
7430
  }
7287
7431
  function ms(n7) {
7288
- return n7.replace(is, "\\").replace(rs, "{").replace(ns, "}").replace(os5, ",").replace(hs, ".");
7432
+ return n7.replace(is, "\\").replace(rs, "{").replace(ns, "}").replace(os6, ",").replace(hs, ".");
7289
7433
  }
7290
7434
  function me(n7) {
7291
7435
  if (!n7)
@@ -7385,7 +7529,7 @@ function Ut(n7, t = {}) {
7385
7529
  function es(n7, t = {}) {
7386
7530
  return new I(n7, t).iterate();
7387
7531
  }
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;
7532
+ 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, os6, hs, as, ls, cs, fs10, 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
7533
  var init_index_min = __esm({
7390
7534
  "node_modules/glob/dist/esm/index.min.js"() {
7391
7535
  import_node_url = require("node:url");
@@ -7432,12 +7576,12 @@ var init_index_min = __esm({
7432
7576
  is = new RegExp(fe, "g");
7433
7577
  rs = new RegExp(ue, "g");
7434
7578
  ns = new RegExp(qt, "g");
7435
- os5 = new RegExp(de, "g");
7579
+ os6 = new RegExp(de, "g");
7436
7580
  hs = new RegExp(pe, "g");
7437
7581
  as = /\\\\/g;
7438
7582
  ls = /\\{/g;
7439
7583
  cs = /\\}/g;
7440
- fs8 = /\\,/g;
7584
+ fs10 = /\\,/g;
7441
7585
  us = /\\./g;
7442
7586
  ds = 1e5;
7443
7587
  at = (n7) => {
@@ -10583,7 +10727,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s))
10583
10727
 
10584
10728
  // src/lib/document-text.ts
10585
10729
  function isSupportedDocument(filePath) {
10586
- return SUPPORTED_EXTENSIONS.has(path9.extname(filePath).toLowerCase());
10730
+ return SUPPORTED_EXTENSIONS.has(path10.extname(filePath).toLowerCase());
10587
10731
  }
10588
10732
  function stripHtml(html) {
10589
10733
  return html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<[^>]+>/g, " ").replace(/&nbsp;/gi, " ").replace(/&amp;/gi, "&").replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, '"').replace(/&#39;/gi, "'").replace(/\s+\n/g, "\n").replace(/[ \t]+/g, " ").replace(/\n{3,}/g, "\n\n").trim();
@@ -10672,7 +10816,7 @@ function extractPdf(filePath) {
10672
10816
  if (out.trim())
10673
10817
  return out.trim();
10674
10818
  }
10675
- const raw = extractPdfRaw(fs9.readFileSync(filePath));
10819
+ const raw = extractPdfRaw(fs11.readFileSync(filePath));
10676
10820
  if (raw.trim().length > 20)
10677
10821
  return raw;
10678
10822
  throw new Error("could not extract text from .pdf \u2014 try pasting the draft instead");
@@ -10694,23 +10838,23 @@ function extractSkillArchive(filePath) {
10694
10838
  return parts.join("\n\n");
10695
10839
  }
10696
10840
  function extractTextFromFile(filePath) {
10697
- const resolved = path9.resolve(filePath);
10698
- if (!fs9.existsSync(resolved)) {
10841
+ const resolved = path10.resolve(filePath);
10842
+ if (!fs11.existsSync(resolved)) {
10699
10843
  throw new Error(`file not found: ${resolved}`);
10700
10844
  }
10701
- const stat = fs9.statSync(resolved);
10845
+ const stat = fs11.statSync(resolved);
10702
10846
  if (!stat.isFile()) {
10703
10847
  throw new Error(`not a file: ${resolved}`);
10704
10848
  }
10705
- const ext = path9.extname(resolved).toLowerCase();
10849
+ const ext = path10.extname(resolved).toLowerCase();
10706
10850
  switch (ext) {
10707
10851
  case ".md":
10708
10852
  case ".txt":
10709
10853
  case ".markdown":
10710
- return fs9.readFileSync(resolved, "utf-8");
10854
+ return fs11.readFileSync(resolved, "utf-8");
10711
10855
  case ".html":
10712
10856
  case ".htm":
10713
- return stripHtml(fs9.readFileSync(resolved, "utf-8"));
10857
+ return stripHtml(fs11.readFileSync(resolved, "utf-8"));
10714
10858
  case ".docx":
10715
10859
  return extractDocx(resolved);
10716
10860
  case ".pdf":
@@ -10730,11 +10874,11 @@ function listDocumentsInDir(dirPath) {
10730
10874
  }).filter(isSupportedDocument);
10731
10875
  }
10732
10876
  function collectDocumentsFromPath(inputPath) {
10733
- const resolved = path9.resolve(inputPath.trim().replace(/^~(?=$|\/)/, os6.homedir()));
10734
- if (!fs9.existsSync(resolved)) {
10877
+ const resolved = path10.resolve(inputPath.trim().replace(/^~(?=$|\/)/, os7.homedir()));
10878
+ if (!fs11.existsSync(resolved)) {
10735
10879
  throw new Error(`path not found: ${resolved}`);
10736
10880
  }
10737
- const stat = fs9.statSync(resolved);
10881
+ const stat = fs11.statSync(resolved);
10738
10882
  const files = stat.isFile() ? [resolved] : listDocumentsInDir(resolved);
10739
10883
  if (!files.length) {
10740
10884
  throw new Error(
@@ -10761,20 +10905,20 @@ function readDraftInput(input) {
10761
10905
  const trimmed = input.trim();
10762
10906
  if (!trimmed)
10763
10907
  throw new Error("empty draft");
10764
- const maybePath = path9.resolve(trimmed.replace(/^~(?=$|\/)/, os6.homedir()));
10765
- if (fs9.existsSync(maybePath) && fs9.statSync(maybePath).isFile()) {
10908
+ const maybePath = path10.resolve(trimmed.replace(/^~(?=$|\/)/, os7.homedir()));
10909
+ if (fs11.existsSync(maybePath) && fs11.statSync(maybePath).isFile()) {
10766
10910
  return { text: extractTextFromFile(maybePath), source: maybePath };
10767
10911
  }
10768
10912
  return { text: trimmed, source: "paste" };
10769
10913
  }
10770
- var import_child_process2, fs9, os6, path9, SUPPORTED_EXTENSIONS, SAMPLE_GLOB, GLOB_IGNORE;
10914
+ var import_child_process2, fs11, os7, path10, SUPPORTED_EXTENSIONS, SAMPLE_GLOB, GLOB_IGNORE;
10771
10915
  var init_document_text = __esm({
10772
10916
  "src/lib/document-text.ts"() {
10773
10917
  "use strict";
10774
10918
  import_child_process2 = require("child_process");
10775
- fs9 = __toESM(require("fs"));
10776
- os6 = __toESM(require("os"));
10777
- path9 = __toESM(require("path"));
10919
+ fs11 = __toESM(require("fs"));
10920
+ os7 = __toESM(require("os"));
10921
+ path10 = __toESM(require("path"));
10778
10922
  init_index_min();
10779
10923
  SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
10780
10924
  ".md",
@@ -10849,21 +10993,21 @@ function runPipeline(text, profile, applyFixes = false) {
10849
10993
  };
10850
10994
  }
10851
10995
  function readText(source) {
10852
- const fs28 = require("fs");
10853
- const path26 = require("path");
10996
+ const fs30 = require("fs");
10997
+ const path27 = require("path");
10854
10998
  if (source === "-") {
10855
10999
  if (process.stdin.isTTY) {
10856
11000
  console.error("No input provided. Pipe content or specify a file.");
10857
11001
  process.exit(1);
10858
11002
  }
10859
- return { text: fs28.readFileSync(0, "utf-8"), path: "stdin" };
11003
+ return { text: fs30.readFileSync(0, "utf-8"), path: "stdin" };
10860
11004
  }
10861
- const resolved = path26.resolve(source);
10862
- if (!fs28.existsSync(resolved)) {
11005
+ const resolved = path27.resolve(source);
11006
+ if (!fs30.existsSync(resolved)) {
10863
11007
  console.error(`File not found: ${resolved}`);
10864
11008
  process.exit(1);
10865
11009
  }
10866
- const stat = fs28.statSync(resolved);
11010
+ const stat = fs30.statSync(resolved);
10867
11011
  if (stat.isDirectory()) {
10868
11012
  console.error(`${resolved} is a directory, not a file.`);
10869
11013
  process.exit(1);
@@ -11315,10 +11459,10 @@ var init_terminal_ui = __esm({
11315
11459
  // src/lib/welcome-flow.ts
11316
11460
  function readWelcomeState() {
11317
11461
  try {
11318
- if (!fs13.existsSync(STATE_FILE)) {
11462
+ if (!fs15.existsSync(STATE_FILE)) {
11319
11463
  return { completed_steps: [], updated_at: (/* @__PURE__ */ new Date()).toISOString() };
11320
11464
  }
11321
- return JSON.parse(fs13.readFileSync(STATE_FILE, "utf-8"));
11465
+ return JSON.parse(fs15.readFileSync(STATE_FILE, "utf-8"));
11322
11466
  } catch {
11323
11467
  return { completed_steps: [], updated_at: (/* @__PURE__ */ new Date()).toISOString() };
11324
11468
  }
@@ -11330,7 +11474,7 @@ function writeWelcomeState(patch) {
11330
11474
  ...patch,
11331
11475
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
11332
11476
  };
11333
- fs13.writeFileSync(STATE_FILE, JSON.stringify(next, null, 2), { mode: 384 });
11477
+ fs15.writeFileSync(STATE_FILE, JSON.stringify(next, null, 2), { mode: 384 });
11334
11478
  return next;
11335
11479
  }
11336
11480
  function markStepComplete(step) {
@@ -11559,7 +11703,7 @@ function saveLocalProfile(name, content) {
11559
11703
  writeWelcomeState({ profile_name: safe });
11560
11704
  markStepComplete("name");
11561
11705
  markStepComplete("samples");
11562
- return path13.join(os7.homedir(), ".hyv", "profiles", `${safe}.md`);
11706
+ return path14.join(os8.homedir(), ".hyv", "profiles", `${safe}.md`);
11563
11707
  }
11564
11708
  function fetchUrlText(url) {
11565
11709
  return new Promise((resolve15, reject) => {
@@ -11781,8 +11925,8 @@ async function stepSignup(profileName) {
11781
11925
  } else {
11782
11926
  console.log(import_chalk12.default.dim("\n already signed in \u2014 syncing profile..."));
11783
11927
  }
11784
- const content = fs13.readFileSync(
11785
- path13.join(os7.homedir(), ".hyv", "profiles", `${profileName}.md`),
11928
+ const content = fs15.readFileSync(
11929
+ path14.join(os8.homedir(), ".hyv", "profiles", `${profileName}.md`),
11786
11930
  "utf-8"
11787
11931
  );
11788
11932
  try {
@@ -11794,7 +11938,12 @@ async function stepSignup(profileName) {
11794
11938
  })
11795
11939
  );
11796
11940
  if (response.status === 200) {
11797
- console.log(import_chalk12.default.green(" \u2713 profile synced to your account"));
11941
+ const body = response.data;
11942
+ if (body.preserved) {
11943
+ console.log(import_chalk12.default.green(" \u2713 account linked \u2014 kept your existing server profile"));
11944
+ } else {
11945
+ console.log(import_chalk12.default.green(" \u2713 profile synced to your account"));
11946
+ }
11798
11947
  } else {
11799
11948
  const detail = response.data?.error;
11800
11949
  console.log(import_chalk12.default.yellow(
@@ -11842,16 +11991,16 @@ function getMcpWelcomeResponse(args2) {
11842
11991
  }
11843
11992
  return buildWelcomeGuide({ profileName: args2.profile, forLlm: true });
11844
11993
  }
11845
- var import_chalk12, fs13, http2, https2, os7, path13, readline, WELCOME_TAGLINE, FLOW_STEPS, STATE_FILE;
11994
+ var import_chalk12, fs15, http2, https2, os8, path14, readline, WELCOME_TAGLINE, FLOW_STEPS, STATE_FILE;
11846
11995
  var init_welcome_flow = __esm({
11847
11996
  "src/lib/welcome-flow.ts"() {
11848
11997
  "use strict";
11849
11998
  import_chalk12 = __toESM(require_source());
11850
- fs13 = __toESM(require("fs"));
11999
+ fs15 = __toESM(require("fs"));
11851
12000
  http2 = __toESM(require("http"));
11852
12001
  https2 = __toESM(require("https"));
11853
- os7 = __toESM(require("os"));
11854
- path13 = __toESM(require("path"));
12002
+ os8 = __toESM(require("os"));
12003
+ path14 = __toESM(require("path"));
11855
12004
  readline = __toESM(require("readline"));
11856
12005
  init_pipeline();
11857
12006
  init_document_text();
@@ -11870,7 +12019,7 @@ var init_welcome_flow = __esm({
11870
12019
  { n: 3, key: "test", title: "test on a draft", hint: "optional \u2014 scan or rewrite a draft" },
11871
12020
  { n: 4, key: "signup", title: "save & unlock", hint: "signup syncs your profile and unlocks learning" }
11872
12021
  ];
11873
- STATE_FILE = path13.join(os7.homedir(), ".hyv", "welcome-state.json");
12022
+ STATE_FILE = path14.join(os8.homedir(), ".hyv", "welcome-state.json");
11874
12023
  }
11875
12024
  });
11876
12025
 
@@ -12858,15 +13007,15 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
12858
13007
  g.minimatch.escape = vi2.escape;
12859
13008
  g.minimatch.unescape = Ei2.unescape;
12860
13009
  });
12861
- var fs28 = R2((Wt2) => {
13010
+ var fs30 = R2((Wt2) => {
12862
13011
  "use strict";
12863
13012
  Object.defineProperty(Wt2, "__esModule", { value: true });
12864
13013
  Wt2.LRUCache = void 0;
12865
13014
  var er = typeof performance == "object" && performance && typeof performance.now == "function" ? performance : Date, as2 = /* @__PURE__ */ new Set(), ge2 = typeof process == "object" && process ? process : {}, ls2 = (n7, t, e, s) => {
12866
13015
  typeof ge2.emitWarning == "function" ? ge2.emitWarning(n7, t, e, s) : console.error(`[${e}] ${t}: ${n7}`);
12867
- }, Lt2 = globalThis.AbortController, os12 = globalThis.AbortSignal;
13016
+ }, Lt2 = globalThis.AbortController, os13 = globalThis.AbortSignal;
12868
13017
  if (typeof Lt2 > "u") {
12869
- os12 = class {
13018
+ os13 = class {
12870
13019
  onabort;
12871
13020
  _onabort = [];
12872
13021
  reason;
@@ -12878,7 +13027,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
12878
13027
  constructor() {
12879
13028
  t();
12880
13029
  }
12881
- signal = new os12();
13030
+ signal = new os13();
12882
13031
  abort(e) {
12883
13032
  if (!this.signal.aborted) {
12884
13033
  this.signal.reason = e, this.signal.aborted = true;
@@ -13827,7 +13976,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
13827
13976
  };
13828
13977
  Object.defineProperty(_2, "__esModule", { value: true });
13829
13978
  _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) => {
13979
+ var Qt2 = fs30(), 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
13980
  let t = vs2.get(n7);
13832
13981
  if (t)
13833
13982
  return t;
@@ -15522,8 +15671,8 @@ init_version();
15522
15671
 
15523
15672
  // src/lib/queue-sync.ts
15524
15673
  var import_chalk6 = __toESM(require_source());
15525
- var fs5 = __toESM(require("fs"));
15526
- var path6 = __toESM(require("path"));
15674
+ var fs7 = __toESM(require("fs"));
15675
+ var path7 = __toESM(require("path"));
15527
15676
  init_config();
15528
15677
  init_auth();
15529
15678
  async function flushQueuedSignals() {
@@ -15532,13 +15681,13 @@ async function flushQueuedSignals() {
15532
15681
  return { sent: 0, failed: 0 };
15533
15682
  let sent = 0;
15534
15683
  let failed = 0;
15535
- if (!fs5.existsSync(QUEUE_DIR))
15684
+ if (!fs7.existsSync(QUEUE_DIR))
15536
15685
  return { sent: 0, failed: 0 };
15537
- const files = fs5.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
15686
+ const files = fs7.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
15538
15687
  for (const file of files) {
15539
- const filePath = path6.join(QUEUE_DIR, file);
15688
+ const filePath = path7.join(QUEUE_DIR, file);
15540
15689
  try {
15541
- const signal = JSON.parse(fs5.readFileSync(filePath, "utf-8"));
15690
+ const signal = JSON.parse(fs7.readFileSync(filePath, "utf-8"));
15542
15691
  let ok = false;
15543
15692
  if (signal.type === "reinforce") {
15544
15693
  const res = await authenticatedRequest(cliApiUrl("/cli/learning/reinforce"), {
@@ -15559,7 +15708,7 @@ async function flushQueuedSignals() {
15559
15708
  ok = res.status === 200;
15560
15709
  }
15561
15710
  if (ok) {
15562
- fs5.unlinkSync(filePath);
15711
+ fs7.unlinkSync(filePath);
15563
15712
  sent++;
15564
15713
  } else {
15565
15714
  failed++;
@@ -15716,8 +15865,9 @@ init_config();
15716
15865
  init_auth();
15717
15866
  init_access();
15718
15867
  init_profile();
15719
- var fs6 = __toESM(require("fs"));
15720
- var path7 = __toESM(require("path"));
15868
+ init_profile_meta();
15869
+ var fs8 = __toESM(require("fs"));
15870
+ var path8 = __toESM(require("path"));
15721
15871
  function registerSyncCommand(program3) {
15722
15872
  program3.command("sync").description("Sync profiles and rules from server").option("--force", "Force re-download even if cache is fresh").action(async (options) => {
15723
15873
  try {
@@ -15735,23 +15885,40 @@ function registerSyncCommand(program3) {
15735
15885
  const data = response.data;
15736
15886
  ensureHyvDir();
15737
15887
  let profileCount = 0;
15888
+ let skippedNewer = 0;
15738
15889
  for (const profile of data.profiles) {
15890
+ if (!profile.content?.trim()) {
15891
+ console.log(import_chalk8.default.yellow(` ! skipped empty profile cache: ${profile.name}`));
15892
+ continue;
15893
+ }
15739
15894
  const cacheKey = toSafeProfileCacheKey(profile.slug || profile.name);
15895
+ if (!shouldApplyServerProfileUpdate(profile.updated_at, cacheKey, options.force)) {
15896
+ skippedNewer++;
15897
+ console.log(import_chalk8.default.yellow(` ! kept newer local profile: ${profile.name}`));
15898
+ continue;
15899
+ }
15740
15900
  writeCachedProfile(cacheKey, profile.content);
15901
+ setProfileMeta(cacheKey, { server_updated_at: profile.updated_at });
15741
15902
  profileCount++;
15742
15903
  }
15743
- const rulesPath = path7.join(CACHE_DIR, "rules.md");
15744
- fs6.writeFileSync(rulesPath, data.rules, { mode: 384 });
15745
- const promptPath = path7.join(CACHE_DIR, "prompt-template.md");
15746
- fs6.writeFileSync(promptPath, data.prompt_template, { mode: 384 });
15904
+ const rulesPath = path8.join(CACHE_DIR2, "rules.md");
15905
+ fs8.writeFileSync(rulesPath, data.rules, { mode: 384 });
15906
+ const promptPath = path8.join(CACHE_DIR2, "prompt-template.md");
15907
+ fs8.writeFileSync(promptPath, data.prompt_template, { mode: 384 });
15747
15908
  if (data.detection_rules) {
15748
- const rulesJson = path7.join(CACHE_DIR, "detection-rules.json");
15749
- fs6.writeFileSync(rulesJson, JSON.stringify(data.detection_rules, null, 2), { mode: 384 });
15909
+ const rulesJson = path8.join(CACHE_DIR2, "detection-rules.json");
15910
+ fs8.writeFileSync(rulesJson, JSON.stringify(data.detection_rules, null, 2), { mode: 384 });
15750
15911
  }
15751
15912
  for (const profile of data.profiles) {
15752
15913
  try {
15753
15914
  const cacheKey = toSafeProfileCacheKey(profile.slug || profile.name);
15754
- await loadFullProfile(cacheKey, { forceRefresh: true });
15915
+ if (!shouldApplyServerProfileUpdate(profile.updated_at, cacheKey, options.force)) {
15916
+ continue;
15917
+ }
15918
+ await loadFullProfile(cacheKey, {
15919
+ forceRefresh: true,
15920
+ serverUpdatedAt: profile.updated_at
15921
+ });
15755
15922
  } catch {
15756
15923
  }
15757
15924
  }
@@ -15762,8 +15929,8 @@ function registerSyncCommand(program3) {
15762
15929
  if (queueResult.failed > 0) {
15763
15930
  console.log(import_chalk8.default.yellow(` ! ${queueResult.failed} queued signal(s) could not send`));
15764
15931
  }
15765
- const metaPath = path7.join(CACHE_DIR, "sync-meta.json");
15766
- fs6.writeFileSync(metaPath, JSON.stringify({
15932
+ const metaPath = path8.join(CACHE_DIR2, "sync-meta.json");
15933
+ fs8.writeFileSync(metaPath, JSON.stringify({
15767
15934
  synced_at: data.synced_at,
15768
15935
  profile_count: profileCount,
15769
15936
  plan: data.plan
@@ -15771,6 +15938,9 @@ function registerSyncCommand(program3) {
15771
15938
  console.log(import_chalk8.default.green(`
15772
15939
  \u2713 Synced successfully!`));
15773
15940
  console.log(import_chalk8.default.dim(` Profiles: ${profileCount}`));
15941
+ if (skippedNewer > 0) {
15942
+ console.log(import_chalk8.default.dim(` Kept local (newer): ${skippedNewer} \u2014 use --force to overwrite`));
15943
+ }
15774
15944
  console.log(import_chalk8.default.dim(` Rules: cached`));
15775
15945
  console.log(import_chalk8.default.dim(` Prompt template: cached`));
15776
15946
  console.log(import_chalk8.default.dim(` Plan: ${data.plan}`));
@@ -15868,21 +16038,21 @@ Profile evolution: ${slug}
15868
16038
 
15869
16039
  // src/commands/rewrite.ts
15870
16040
  var import_chalk9 = __toESM(require_source());
15871
- var fs11 = __toESM(require("fs"));
15872
- var path11 = __toESM(require("path"));
16041
+ var fs13 = __toESM(require("fs"));
16042
+ var path12 = __toESM(require("path"));
15873
16043
  init_pipeline();
15874
16044
 
15875
16045
  // src/lib/prompt.ts
15876
- var fs10 = __toESM(require("fs"));
15877
- var path10 = __toESM(require("path"));
16046
+ var fs12 = __toESM(require("fs"));
16047
+ var path11 = __toESM(require("path"));
15878
16048
  init_config();
15879
16049
  init_signals();
15880
16050
  init_profile_parse();
15881
16051
  function loadPromptTemplate() {
15882
- const templatePath = path10.join(CACHE_DIR, "prompt-template.md");
16052
+ const templatePath = path11.join(CACHE_DIR2, "prompt-template.md");
15883
16053
  try {
15884
- if (fs10.existsSync(templatePath)) {
15885
- return fs10.readFileSync(templatePath, "utf-8");
16054
+ if (fs12.existsSync(templatePath)) {
16055
+ return fs12.readFileSync(templatePath, "utf-8");
15886
16056
  }
15887
16057
  } catch {
15888
16058
  }
@@ -16069,8 +16239,8 @@ Draft: ${draftPath}`));
16069
16239
  ${result.stats.autoFixed} issues can be auto-fixed \u2014 run: hyv fix ${file}`));
16070
16240
  }
16071
16241
  if (options.output) {
16072
- const outputPath = path11.resolve(options.output);
16073
- fs11.writeFileSync(outputPath, promptResult.prompt);
16242
+ const outputPath = path12.resolve(options.output);
16243
+ fs13.writeFileSync(outputPath, promptResult.prompt);
16074
16244
  console.log(import_chalk9.default.green(`
16075
16245
  \u2713 Prompt written to ${outputPath}`));
16076
16246
  } else {
@@ -16090,8 +16260,8 @@ Draft: ${draftPath}`));
16090
16260
 
16091
16261
  // src/commands/learning.ts
16092
16262
  var import_chalk10 = __toESM(require_source());
16093
- var fs12 = __toESM(require("fs"));
16094
- var path12 = __toESM(require("path"));
16263
+ var fs14 = __toESM(require("fs"));
16264
+ var path13 = __toESM(require("path"));
16095
16265
 
16096
16266
  // src/lib/diff.ts
16097
16267
  init_scan();
@@ -16235,18 +16405,18 @@ function registerLearningCommands(program3) {
16235
16405
  console.error(import_chalk10.default.red("Provide <original> <edited> or use --last"));
16236
16406
  process.exit(1);
16237
16407
  }
16238
- origPath = path12.resolve(original);
16239
- editPath = path12.resolve(edited);
16240
- if (!fs12.existsSync(origPath)) {
16408
+ origPath = path13.resolve(original);
16409
+ editPath = path13.resolve(edited);
16410
+ if (!fs14.existsSync(origPath)) {
16241
16411
  console.error(import_chalk10.default.red(`Original file not found: ${origPath}`));
16242
16412
  process.exit(1);
16243
16413
  }
16244
- if (!fs12.existsSync(editPath)) {
16414
+ if (!fs14.existsSync(editPath)) {
16245
16415
  console.error(import_chalk10.default.red(`Edited file not found: ${editPath}`));
16246
16416
  process.exit(1);
16247
16417
  }
16248
- origText = fs12.readFileSync(origPath, "utf-8");
16249
- editText = fs12.readFileSync(editPath, "utf-8");
16418
+ origText = fs14.readFileSync(origPath, "utf-8");
16419
+ editText = fs14.readFileSync(editPath, "utf-8");
16250
16420
  saveLastEditSession({
16251
16421
  original_path: origPath,
16252
16422
  edited_path: editPath,
@@ -16413,8 +16583,8 @@ function printProfileImpact(impact, data) {
16413
16583
 
16414
16584
  // src/commands/onboarding.ts
16415
16585
  var import_chalk13 = __toESM(require_source());
16416
- var fs14 = __toESM(require("fs"));
16417
- var path14 = __toESM(require("path"));
16586
+ var fs16 = __toESM(require("fs"));
16587
+ var path15 = __toESM(require("path"));
16418
16588
  init_config();
16419
16589
  init_auth();
16420
16590
  init_scan();
@@ -16565,24 +16735,24 @@ Industry: ${industry}
16565
16735
  }
16566
16736
  }
16567
16737
  async function createFromSamples(name, sampleDir, token) {
16568
- const dirPath = path14.resolve(sampleDir);
16569
- if (!fs14.existsSync(dirPath)) {
16738
+ const dirPath = path15.resolve(sampleDir);
16739
+ if (!fs16.existsSync(dirPath)) {
16570
16740
  throw new Error(`Directory not found: ${dirPath}`);
16571
16741
  }
16572
16742
  console.log(import_chalk13.default.bold(`
16573
16743
  Creating voice profile: ${name}`));
16574
16744
  console.log(import_chalk13.default.dim(`Reading samples from: ${dirPath}
16575
16745
  `));
16576
- const files = fs14.readdirSync(dirPath).filter((f) => f.endsWith(".md") || f.endsWith(".txt")).map((f) => path14.join(dirPath, f));
16746
+ const files = fs16.readdirSync(dirPath).filter((f) => f.endsWith(".md") || f.endsWith(".txt")).map((f) => path15.join(dirPath, f));
16577
16747
  if (files.length === 0) {
16578
16748
  throw new Error("No .md or .txt files found in directory");
16579
16749
  }
16580
16750
  const samples = [];
16581
16751
  for (const file of files) {
16582
- const text = fs14.readFileSync(file, "utf-8");
16752
+ const text = fs16.readFileSync(file, "utf-8");
16583
16753
  if (text.trim().length > 0) {
16584
16754
  samples.push({ path: file, text });
16585
- console.log(import_chalk13.default.dim(` \u2022 ${path14.basename(file)} (${words(text).length} words)`));
16755
+ console.log(import_chalk13.default.dim(` \u2022 ${path15.basename(file)} (${words(text).length} words)`));
16586
16756
  }
16587
16757
  }
16588
16758
  if (samples.length === 0) {
@@ -16653,12 +16823,12 @@ function registerImportCommand(program3) {
16653
16823
  program3.command("import").description("Import a voice profile from file").argument("<name>", "Profile name").argument("<file>", "Profile markdown file").action(async (name, file) => {
16654
16824
  try {
16655
16825
  assertSafeProfileName(name);
16656
- const filePath = path14.resolve(file);
16657
- if (!fs14.existsSync(filePath)) {
16826
+ const filePath = path15.resolve(file);
16827
+ if (!fs16.existsSync(filePath)) {
16658
16828
  console.error(import_chalk13.default.red(`File not found: ${filePath}`));
16659
16829
  process.exit(1);
16660
16830
  }
16661
- const content = fs14.readFileSync(filePath, "utf-8");
16831
+ const content = fs16.readFileSync(filePath, "utf-8");
16662
16832
  ensureHyvDir();
16663
16833
  writeCachedProfile(name, content);
16664
16834
  console.log(import_chalk13.default.green(`
@@ -17029,11 +17199,11 @@ async function runHybridAnalysis(text, profile, opts = {}) {
17029
17199
 
17030
17200
  // src/commands/history.ts
17031
17201
  var import_chalk16 = __toESM(require_source());
17032
- var fs15 = __toESM(require("fs"));
17033
- var path15 = __toESM(require("path"));
17202
+ var fs17 = __toESM(require("fs"));
17203
+ var path16 = __toESM(require("path"));
17034
17204
  init_config();
17035
- var HISTORY_DIR = path15.join(HYV_DIR, "history");
17036
- var HISTORY_FILE = path15.join(HISTORY_DIR, "scans.jsonl");
17205
+ var HISTORY_DIR = path16.join(HYV_DIR, "history");
17206
+ var HISTORY_FILE = path16.join(HISTORY_DIR, "scans.jsonl");
17037
17207
  function logScan(entry) {
17038
17208
  try {
17039
17209
  ensureHyvDir();
@@ -17043,9 +17213,9 @@ function logScan(entry) {
17043
17213
  }
17044
17214
  function readHistory() {
17045
17215
  try {
17046
- if (!fs15.existsSync(HISTORY_FILE))
17216
+ if (!fs17.existsSync(HISTORY_FILE))
17047
17217
  return [];
17048
- const lines = fs15.readFileSync(HISTORY_FILE, "utf-8").trim().split("\n").filter(Boolean);
17218
+ const lines = fs17.readFileSync(HISTORY_FILE, "utf-8").trim().split("\n").filter(Boolean);
17049
17219
  return lines.map((l) => JSON.parse(l));
17050
17220
  } catch {
17051
17221
  return [];
@@ -17077,8 +17247,8 @@ function registerHistoryCommand(program3) {
17077
17247
  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
17248
  try {
17079
17249
  if (options.clear) {
17080
- if (fs15.existsSync(HISTORY_FILE)) {
17081
- fs15.unlinkSync(HISTORY_FILE);
17250
+ if (fs17.existsSync(HISTORY_FILE)) {
17251
+ fs17.unlinkSync(HISTORY_FILE);
17082
17252
  }
17083
17253
  console.log(import_chalk16.default.green("\n\u2713 History cleared"));
17084
17254
  return;
@@ -17136,7 +17306,7 @@ function registerHistoryCommand(program3) {
17136
17306
  const icon = entry.issues === 0 ? import_chalk16.default.green("\u2713") : import_chalk16.default.red("\u25CF");
17137
17307
  const score = entry.score < 60 ? import_chalk16.default.red(`${entry.score}/100`) : entry.score < 80 ? import_chalk16.default.yellow(`${entry.score}/100`) : import_chalk16.default.green(`${entry.score}/100`);
17138
17308
  const issues = entry.issues > 0 ? import_chalk16.default.red(`${entry.issues} issues`) : import_chalk16.default.dim("clean");
17139
- const file = path15.basename(entry.file).slice(0, 25).padEnd(25);
17309
+ const file = path16.basename(entry.file).slice(0, 25).padEnd(25);
17140
17310
  console.log(` ${import_chalk16.default.dim(time)} ${icon} ${file} ${score} ${issues}`);
17141
17311
  }
17142
17312
  console.log("");
@@ -17244,9 +17414,9 @@ hyv scan ${filePath}`));
17244
17414
 
17245
17415
  // src/commands/doctor.ts
17246
17416
  var import_chalk18 = __toESM(require_source());
17247
- var fs17 = __toESM(require("fs"));
17248
- var path17 = __toESM(require("path"));
17249
- var os8 = __toESM(require("os"));
17417
+ var fs19 = __toESM(require("fs"));
17418
+ var path18 = __toESM(require("path"));
17419
+ var os9 = __toESM(require("os"));
17250
17420
  init_config();
17251
17421
  init_auth();
17252
17422
  init_access();
@@ -17257,15 +17427,15 @@ init_version();
17257
17427
  var import_child_process3 = require("child_process");
17258
17428
 
17259
17429
  // src/lib/cli-entry.ts
17260
- var fs16 = __toESM(require("fs"));
17261
- var path16 = __toESM(require("path"));
17430
+ var fs18 = __toESM(require("fs"));
17431
+ var path17 = __toESM(require("path"));
17262
17432
  function resolveCliEntry() {
17263
17433
  const candidates = [
17264
- path16.resolve(process.argv[1] || ""),
17265
- path16.resolve(__dirname, "index.js"),
17266
- path16.resolve(__dirname, "..", "dist", "index.js")
17434
+ path17.resolve(process.argv[1] || ""),
17435
+ path17.resolve(__dirname, "index.js"),
17436
+ path17.resolve(__dirname, "..", "dist", "index.js")
17267
17437
  ];
17268
- return candidates.find((p) => p && fs16.existsSync(p)) || null;
17438
+ return candidates.find((p) => p && fs18.existsSync(p)) || null;
17269
17439
  }
17270
17440
  function mcpServerCommand() {
17271
17441
  const entry = resolveCliEntry();
@@ -17351,20 +17521,20 @@ async function testMcpStdioSubprocess() {
17351
17521
  }
17352
17522
 
17353
17523
  // src/commands/doctor.ts
17354
- var HOME = os8.homedir();
17524
+ var HOME = os9.homedir();
17355
17525
  var IS_WIN = process.platform === "win32";
17356
17526
  function claudeDesktopDir() {
17357
17527
  if (IS_WIN)
17358
- return path17.join(HOME, "AppData", "Roaming", "Claude");
17528
+ return path18.join(HOME, "AppData", "Roaming", "Claude");
17359
17529
  if (process.platform === "linux")
17360
- return path17.join(HOME, ".config", "Claude");
17361
- return path17.join(HOME, "Library", "Application Support", "Claude");
17530
+ return path18.join(HOME, ".config", "Claude");
17531
+ return path18.join(HOME, "Library", "Application Support", "Claude");
17362
17532
  }
17363
17533
  function isOwnerOnlyFile(filePath) {
17364
17534
  try {
17365
- if (!fs17.existsSync(filePath))
17535
+ if (!fs19.existsSync(filePath))
17366
17536
  return true;
17367
- const mode = fs17.statSync(filePath).mode & 511;
17537
+ const mode = fs19.statSync(filePath).mode & 511;
17368
17538
  return (mode & 63) === 0;
17369
17539
  } catch {
17370
17540
  return false;
@@ -17372,9 +17542,9 @@ function isOwnerOnlyFile(filePath) {
17372
17542
  }
17373
17543
  function readMcpHyv(configFile) {
17374
17544
  try {
17375
- if (!fs17.existsSync(configFile))
17545
+ if (!fs19.existsSync(configFile))
17376
17546
  return false;
17377
- const cfg = JSON.parse(fs17.readFileSync(configFile, "utf-8"));
17547
+ const cfg = JSON.parse(fs19.readFileSync(configFile, "utf-8"));
17378
17548
  return Boolean(cfg.mcpServers?.hyv);
17379
17549
  } catch {
17380
17550
  return false;
@@ -17395,28 +17565,28 @@ function registerDoctorCommand(program3) {
17395
17565
  console.log(import_chalk18.default.dim("tip: run hyv welcome for free capabilities\n"));
17396
17566
  console.log(import_chalk18.default.dim("checking cli installation..."));
17397
17567
  const cliPath = process.argv[1];
17398
- if (cliPath && fs17.existsSync(cliPath)) {
17568
+ if (cliPath && fs19.existsSync(cliPath)) {
17399
17569
  console.log(import_chalk18.default.green(" \u2713 cli installed"));
17400
17570
  } else {
17401
17571
  console.log(import_chalk18.default.red(" \u2717 cli not found"));
17402
17572
  issues++;
17403
17573
  }
17404
17574
  console.log(import_chalk18.default.dim("checking .hyv directory..."));
17405
- if (fs17.existsSync(HYV_DIR)) {
17575
+ if (fs19.existsSync(HYV_DIR)) {
17406
17576
  console.log(import_chalk18.default.green(" \u2713 .hyv directory exists"));
17407
17577
  } else {
17408
17578
  console.log(import_chalk18.default.yellow(" ! .hyv directory missing \u2014 creating..."));
17409
- fs17.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
17579
+ fs19.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
17410
17580
  fixed++;
17411
17581
  }
17412
17582
  console.log(import_chalk18.default.dim("checking cache..."));
17413
17583
  const diskProfiles = listDiskCachedProfiles();
17414
- const syncMeta = path17.join(CACHE_DIR, "sync-meta.json");
17415
- if (diskProfiles.length > 0 || fs17.existsSync(syncMeta)) {
17584
+ const syncMeta = path18.join(CACHE_DIR2, "sync-meta.json");
17585
+ if (diskProfiles.length > 0 || fs19.existsSync(syncMeta)) {
17416
17586
  console.log(import_chalk18.default.green(` \u2713 profile cache (${diskProfiles.length} full profile(s))`));
17417
- if (fs17.existsSync(syncMeta)) {
17587
+ if (fs19.existsSync(syncMeta)) {
17418
17588
  try {
17419
- const meta = JSON.parse(fs17.readFileSync(syncMeta, "utf-8"));
17589
+ const meta = JSON.parse(fs19.readFileSync(syncMeta, "utf-8"));
17420
17590
  console.log(import_chalk18.default.dim(` last sync: ${meta.synced_at || "unknown"}`));
17421
17591
  } catch {
17422
17592
  }
@@ -17425,8 +17595,8 @@ function registerDoctorCommand(program3) {
17425
17595
  console.log(import_chalk18.default.dim(" - no full profile cache (free local engine still works)"));
17426
17596
  }
17427
17597
  console.log(import_chalk18.default.dim("checking file permissions..."));
17428
- if (fs17.existsSync(HYV_DIR)) {
17429
- const hyvMode = fs17.statSync(HYV_DIR).mode & 511;
17598
+ if (fs19.existsSync(HYV_DIR)) {
17599
+ const hyvMode = fs19.statSync(HYV_DIR).mode & 511;
17430
17600
  if ((hyvMode & 63) === 0) {
17431
17601
  console.log(import_chalk18.default.green(" \u2713 .hyv directory permissions"));
17432
17602
  } else {
@@ -17435,7 +17605,7 @@ function registerDoctorCommand(program3) {
17435
17605
  issues++;
17436
17606
  }
17437
17607
  }
17438
- if (fs17.existsSync(AUTH_FILE)) {
17608
+ if (fs19.existsSync(AUTH_FILE)) {
17439
17609
  if (isOwnerOnlyFile(AUTH_FILE)) {
17440
17610
  console.log(import_chalk18.default.green(" \u2713 auth.json permissions"));
17441
17611
  } else {
@@ -17468,9 +17638,9 @@ function registerDoctorCommand(program3) {
17468
17638
  console.log(import_chalk18.default.dim(" run: hyv init for profiles + learning"));
17469
17639
  }
17470
17640
  console.log(import_chalk18.default.dim("checking voice profile..."));
17471
- 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") : [];
17641
+ const voiceMd = path18.join(HYV_DIR, "voice.md");
17642
+ const hasVoiceMd = fs19.existsSync(voiceMd) && fs19.readFileSync(voiceMd, "utf-8").trim().length > 50;
17643
+ const profileFiles = fs19.existsSync(PROFILES_DIR) ? fs19.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md") && f !== "voice.md") : [];
17474
17644
  if (hasVoiceMd || profileFiles.length > 0 || diskProfiles.length > 0) {
17475
17645
  if (hasVoiceMd)
17476
17646
  console.log(import_chalk18.default.green(" \u2713 voice.md exists"));
@@ -17483,21 +17653,21 @@ function registerDoctorCommand(program3) {
17483
17653
  console.log(import_chalk18.default.dim(" run: hyv new <name> or hyv init"));
17484
17654
  }
17485
17655
  console.log(import_chalk18.default.dim("checking agent configurations..."));
17486
- const cursorLegacyRule = path17.join(HOME, ".cursor", "rules", "hyv.md");
17487
- const cursorRule = path17.join(HOME, ".cursor", "rules", "hyv.mdc");
17488
- if (fs17.existsSync(cursorLegacyRule) && fs17.existsSync(cursorRule)) {
17656
+ const cursorLegacyRule = path18.join(HOME, ".cursor", "rules", "hyv.md");
17657
+ const cursorRule = path18.join(HOME, ".cursor", "rules", "hyv.mdc");
17658
+ if (fs19.existsSync(cursorLegacyRule) && fs19.existsSync(cursorRule)) {
17489
17659
  console.log(import_chalk18.default.yellow(" ! stale cursor rule ~/.cursor/rules/hyv.md (use hyv.mdc)"));
17490
17660
  console.log(import_chalk18.default.dim(" run: rm ~/.cursor/rules/hyv.md (or hyv doctor --fix-agents)"));
17491
17661
  issues++;
17492
17662
  }
17493
17663
  const agentChecks = [
17494
- { name: "claude desktop mcp", ok: readMcpHyv(path17.join(claudeDesktopDir(), "claude_desktop_config.json")) },
17495
- { 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")) }
17664
+ { name: "claude desktop mcp", ok: readMcpHyv(path18.join(claudeDesktopDir(), "claude_desktop_config.json")) },
17665
+ { name: "cursor mcp", ok: readMcpHyv(path18.join(HOME, ".cursor", "mcp.json")) },
17666
+ { name: "cursor rule", ok: fs19.existsSync(cursorRule) },
17667
+ { name: "claude code command", ok: fs19.existsSync(path18.join(HOME, ".claude", "commands", "hyv.md")) },
17668
+ { name: "claude code skill", ok: fs19.existsSync(path18.join(HOME, ".claude", "skills", "hold-your-voice", "SKILL.md")) },
17669
+ { name: "codex agents", ok: fs19.existsSync(path18.join(HOME, ".codex", "AGENTS.md")) && fs19.readFileSync(path18.join(HOME, ".codex", "AGENTS.md"), "utf-8").includes("hyv") },
17670
+ { name: "command code skill", ok: fs19.existsSync(path18.join(HOME, ".commandcode", "skills", "hyv", "SKILL.md")) }
17501
17671
  ];
17502
17672
  for (const agent of agentChecks) {
17503
17673
  if (agent.ok) {
@@ -17508,8 +17678,8 @@ function registerDoctorCommand(program3) {
17508
17678
  }
17509
17679
  if (opts.fixAgents) {
17510
17680
  try {
17511
- const pkgDir = path17.resolve(__dirname, "..");
17512
- const { setupAgents } = require(path17.join(pkgDir, "scripts", "postinstall-lib.js"));
17681
+ const pkgDir = path18.resolve(__dirname, "..");
17682
+ const { setupAgents } = require(path18.join(pkgDir, "scripts", "postinstall-lib.js"));
17513
17683
  const result = setupAgents({ pkgDir, quiet: true });
17514
17684
  console.log(import_chalk18.default.green(` \u2713 re-ran agent setup (${result.configured.join(", ") || "no changes"})`));
17515
17685
  if (result.warnings.length) {
@@ -17521,8 +17691,8 @@ function registerDoctorCommand(program3) {
17521
17691
  }
17522
17692
  }
17523
17693
  console.log(import_chalk18.default.dim("checking mcp server..."));
17524
- const claudeMcp = readMcpHyv(path17.join(claudeDesktopDir(), "claude_desktop_config.json"));
17525
- const cursorMcp = readMcpHyv(path17.join(HOME, ".cursor", "mcp.json"));
17694
+ const claudeMcp = readMcpHyv(path18.join(claudeDesktopDir(), "claude_desktop_config.json"));
17695
+ const cursorMcp = readMcpHyv(path18.join(HOME, ".cursor", "mcp.json"));
17526
17696
  if (claudeMcp || cursorMcp) {
17527
17697
  if (claudeMcp)
17528
17698
  console.log(import_chalk18.default.green(" \u2713 mcp configured for claude desktop"));
@@ -17631,8 +17801,8 @@ init_access();
17631
17801
  init_config();
17632
17802
 
17633
17803
  // src/lib/destructive-write.ts
17634
- var fs18 = __toESM(require("fs"));
17635
- var path18 = __toESM(require("path"));
17804
+ var fs20 = __toESM(require("fs"));
17805
+ var path19 = __toESM(require("path"));
17636
17806
  var readline2 = __toESM(require("readline"));
17637
17807
  var import_chalk20 = __toESM(require_source());
17638
17808
  async function confirmDestructiveWrite(options) {
@@ -17658,12 +17828,12 @@ A .bak backup will be created. Proceed? [y/N] `;
17658
17828
  }
17659
17829
  function writeInPlaceWithBackup(filePath, content) {
17660
17830
  const backupPath = filePath + ".bak";
17661
- fs18.copyFileSync(filePath, backupPath);
17662
- fs18.writeFileSync(filePath, content);
17831
+ fs20.copyFileSync(filePath, backupPath);
17832
+ fs20.writeFileSync(filePath, content);
17663
17833
  return backupPath;
17664
17834
  }
17665
17835
  function backupBasename(filePath) {
17666
- return path18.basename(filePath) + ".bak";
17836
+ return path19.basename(filePath) + ".bak";
17667
17837
  }
17668
17838
 
17669
17839
  // src/commands/fix.ts
@@ -17757,19 +17927,19 @@ function registerCheckCommand(program3) {
17757
17927
  const profile = await loadProfileForCommand(options.profile);
17758
17928
  let inputText = text;
17759
17929
  if (text === "-") {
17760
- const fs29 = require("fs");
17930
+ const fs31 = require("fs");
17761
17931
  if (process.stdin.isTTY) {
17762
17932
  console.error(import_chalk22.default.red("No input provided. Pipe content or pass text as argument."));
17763
17933
  process.exit(1);
17764
17934
  }
17765
- inputText = fs29.readFileSync(0, "utf-8");
17935
+ inputText = fs31.readFileSync(0, "utf-8");
17766
17936
  }
17767
17937
  if (!inputText.trim()) {
17768
17938
  console.error(import_chalk22.default.red("No text provided."));
17769
17939
  process.exit(1);
17770
17940
  }
17771
- const fs28 = require("fs");
17772
- if (text !== "-" && fs28.existsSync(text)) {
17941
+ const fs30 = require("fs");
17942
+ if (text !== "-" && fs30.existsSync(text)) {
17773
17943
  console.log(import_chalk22.default.yellow(`"${text}" looks like a file. Did you mean: hyv scan ${text}`));
17774
17944
  process.exit(1);
17775
17945
  }
@@ -17896,12 +18066,12 @@ function registerDiffCommand(program3) {
17896
18066
  ${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} available`));
17897
18067
  console.log(import_chalk24.default.dim(` run: hyv fix ${file} -i to apply`));
17898
18068
  if (options.apply && filePath !== "stdin") {
17899
- const fs28 = require("fs");
17900
- const path26 = require("path");
18069
+ const fs30 = require("fs");
18070
+ const path27 = require("path");
17901
18071
  const backupPath = filePath + ".bak";
17902
- fs28.copyFileSync(filePath, backupPath);
17903
- fs28.writeFileSync(filePath, result.fixed);
17904
- console.log(import_chalk24.default.green(` \u2713 Applied. Backup: ${path26.basename(backupPath)}`));
18072
+ fs30.copyFileSync(filePath, backupPath);
18073
+ fs30.writeFileSync(filePath, result.fixed);
18074
+ console.log(import_chalk24.default.green(` \u2713 Applied. Backup: ${path27.basename(backupPath)}`));
17905
18075
  }
17906
18076
  } catch (error) {
17907
18077
  console.error(import_chalk24.default.red(`Error: ${error.message}`));
@@ -18076,8 +18246,8 @@ function registerRulesCommand(program3) {
18076
18246
 
18077
18247
  // src/commands/batch.ts
18078
18248
  var import_chalk26 = __toESM(require_source());
18079
- var fs19 = __toESM(require("fs"));
18080
- var path19 = __toESM(require("path"));
18249
+ var fs21 = __toESM(require("fs"));
18250
+ var path20 = __toESM(require("path"));
18081
18251
  init_pipeline();
18082
18252
  init_local_profile();
18083
18253
  function registerBatchCommand(program3) {
@@ -18110,10 +18280,10 @@ No files matching: ${pattern}`));
18110
18280
  }
18111
18281
  const results = [];
18112
18282
  for (const file of files) {
18113
- const absPath = path19.resolve(file);
18114
- if (!fs19.existsSync(absPath))
18283
+ const absPath = path20.resolve(file);
18284
+ if (!fs21.existsSync(absPath))
18115
18285
  continue;
18116
- const text = fs19.readFileSync(absPath, "utf-8");
18286
+ const text = fs21.readFileSync(absPath, "utf-8");
18117
18287
  const result = runPipeline(text, profile, options.fix || false);
18118
18288
  results.push({
18119
18289
  file,
@@ -18179,16 +18349,16 @@ No files matching: ${pattern}`));
18179
18349
 
18180
18350
  // src/commands/watch.ts
18181
18351
  var import_chalk27 = __toESM(require_source());
18182
- var fs20 = __toESM(require("fs"));
18183
- var path20 = __toESM(require("path"));
18352
+ var fs22 = __toESM(require("fs"));
18353
+ var path21 = __toESM(require("path"));
18184
18354
  init_pipeline();
18185
18355
  init_local_profile();
18186
18356
  function registerWatchCommand(program3) {
18187
18357
  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) => {
18188
18358
  try {
18189
18359
  const profile = await loadProfileForCommand(options.profile);
18190
- const absPath = path20.resolve(file);
18191
- if (!fs20.existsSync(absPath)) {
18360
+ const absPath = path21.resolve(file);
18361
+ if (!fs22.existsSync(absPath)) {
18192
18362
  console.error(import_chalk27.default.red(`File not found: ${absPath}`));
18193
18363
  process.exit(1);
18194
18364
  }
@@ -18211,7 +18381,7 @@ watching ${absPath}`));
18211
18381
  let lastScore = 0;
18212
18382
  let ignoreWatchUntil = 0;
18213
18383
  const runScan = () => {
18214
- const text = fs20.readFileSync(absPath, "utf-8");
18384
+ const text = fs22.readFileSync(absPath, "utf-8");
18215
18385
  const result = runPipeline(text, profile, options.command === "fix");
18216
18386
  scanCount++;
18217
18387
  const now = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
@@ -18253,7 +18423,7 @@ watching ${absPath}`));
18253
18423
  console.log("");
18254
18424
  };
18255
18425
  runScan();
18256
- fs20.watch(absPath, (eventType) => {
18426
+ fs22.watch(absPath, (eventType) => {
18257
18427
  if (eventType !== "change")
18258
18428
  return;
18259
18429
  if (Date.now() < ignoreWatchUntil)
@@ -18278,7 +18448,7 @@ watching ${absPath}`));
18278
18448
 
18279
18449
  // src/commands/demo.ts
18280
18450
  var import_chalk28 = __toESM(require_source());
18281
- var fs21 = __toESM(require("fs"));
18451
+ var fs23 = __toESM(require("fs"));
18282
18452
  var SAMPLES = {
18283
18453
  linkedin: {
18284
18454
  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 +18543,7 @@ function registerDemoCommand(program3) {
18373
18543
  }
18374
18544
  if (options.output) {
18375
18545
  const outputPath = require("path").resolve(options.output);
18376
- fs21.writeFileSync(outputPath, sample.text);
18546
+ fs23.writeFileSync(outputPath, sample.text);
18377
18547
  console.log(import_chalk28.default.green(`
18378
18548
  \u2713 Sample written to ${outputPath}`));
18379
18549
  if (sample.patterns.length > 0) {
@@ -18440,22 +18610,22 @@ function registerOpenCommand(program3) {
18440
18610
  init_welcome();
18441
18611
 
18442
18612
  // src/lib/onboarding.ts
18443
- var fs22 = __toESM(require("fs"));
18444
- var path21 = __toESM(require("path"));
18445
- var os9 = __toESM(require("os"));
18446
- var hyvDir = path21.join(os9.homedir(), ".hyv");
18447
- var onboardingFile = path21.join(hyvDir, "onboarding-complete.json");
18613
+ var fs24 = __toESM(require("fs"));
18614
+ var path22 = __toESM(require("path"));
18615
+ var os10 = __toESM(require("os"));
18616
+ var hyvDir = path22.join(os10.homedir(), ".hyv");
18617
+ var onboardingFile = path22.join(hyvDir, "onboarding-complete.json");
18448
18618
  function hasCompletedOnboarding() {
18449
18619
  try {
18450
- return fs22.existsSync(onboardingFile);
18620
+ return fs24.existsSync(onboardingFile);
18451
18621
  } catch {
18452
18622
  return false;
18453
18623
  }
18454
18624
  }
18455
18625
  function markOnboardingComplete(version) {
18456
- if (!fs22.existsSync(hyvDir))
18457
- fs22.mkdirSync(hyvDir, { recursive: true });
18458
- fs22.writeFileSync(
18626
+ if (!fs24.existsSync(hyvDir))
18627
+ fs24.mkdirSync(hyvDir, { recursive: true });
18628
+ fs24.writeFileSync(
18459
18629
  onboardingFile,
18460
18630
  JSON.stringify({ version, completed_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
18461
18631
  );
@@ -18463,13 +18633,13 @@ function markOnboardingComplete(version) {
18463
18633
 
18464
18634
  // src/commands/welcome.ts
18465
18635
  init_welcome_flow();
18466
- var fs23 = __toESM(require("fs"));
18467
- var path22 = __toESM(require("path"));
18636
+ var fs25 = __toESM(require("fs"));
18637
+ var path23 = __toESM(require("path"));
18468
18638
  function registerWelcomeCommand(program3) {
18469
18639
  const pkgVersion3 = (() => {
18470
18640
  try {
18471
- const pkgPath3 = path22.resolve(__dirname, "..", "package.json");
18472
- return JSON.parse(fs23.readFileSync(pkgPath3, "utf-8")).version;
18641
+ const pkgPath3 = path23.resolve(__dirname, "..", "package.json");
18642
+ return JSON.parse(fs25.readFileSync(pkgPath3, "utf-8")).version;
18473
18643
  } catch {
18474
18644
  return "0.0.0";
18475
18645
  }
@@ -18650,9 +18820,9 @@ Current: ${getEngineLabel()}
18650
18820
  }
18651
18821
 
18652
18822
  // src/mcp.ts
18653
- var fs24 = __toESM(require("fs"));
18654
- var path23 = __toESM(require("path"));
18655
- var os10 = __toESM(require("os"));
18823
+ var fs26 = __toESM(require("fs"));
18824
+ var path24 = __toESM(require("path"));
18825
+ var os11 = __toESM(require("os"));
18656
18826
  init_classifier();
18657
18827
  init_autofix();
18658
18828
  init_validator();
@@ -18663,20 +18833,20 @@ init_welcome();
18663
18833
  init_config();
18664
18834
  init_telemetry();
18665
18835
  init_access();
18666
- var VOICE_MD = path23.join(os10.homedir(), ".hyv", "voice.md");
18836
+ var VOICE_MD = path24.join(os11.homedir(), ".hyv", "voice.md");
18667
18837
  var MAX_RESPONSE_CHARS = 12e4;
18668
18838
  var MAX_SCAN_FILE_BYTES = 2 * 1024 * 1024;
18669
18839
  function readScanFile(filePath) {
18670
- const cwdReal = fs24.realpathSync(process.cwd());
18671
- const resolved = fs24.realpathSync(path23.resolve(filePath));
18672
- if (!resolved.startsWith(cwdReal + path23.sep) && resolved !== cwdReal) {
18840
+ const cwdReal = fs26.realpathSync(process.cwd());
18841
+ const resolved = fs26.realpathSync(path24.resolve(filePath));
18842
+ if (!resolved.startsWith(cwdReal + path24.sep) && resolved !== cwdReal) {
18673
18843
  throw new Error(`file must be in the current working directory (${cwdReal})`);
18674
18844
  }
18675
- const stat = fs24.statSync(resolved);
18845
+ const stat = fs26.statSync(resolved);
18676
18846
  if (stat.size > MAX_SCAN_FILE_BYTES) {
18677
18847
  throw new Error(`file too large (max ${MAX_SCAN_FILE_BYTES} bytes)`);
18678
18848
  }
18679
- return fs24.readFileSync(resolved, "utf-8");
18849
+ return fs26.readFileSync(resolved, "utf-8");
18680
18850
  }
18681
18851
  function toolResultPayload(result) {
18682
18852
  const isError = result.startsWith("Error:") || result.startsWith("Unknown tool:");
@@ -18685,10 +18855,10 @@ function toolResultPayload(result) {
18685
18855
  ...isError ? { isError: true } : {}
18686
18856
  };
18687
18857
  }
18688
- var pkgPath = path23.resolve(__dirname, "..", "package.json");
18858
+ var pkgPath = path24.resolve(__dirname, "..", "package.json");
18689
18859
  var pkgVersion = (() => {
18690
18860
  try {
18691
- return JSON.parse(fs24.readFileSync(pkgPath, "utf-8")).version;
18861
+ return JSON.parse(fs26.readFileSync(pkgPath, "utf-8")).version;
18692
18862
  } catch {
18693
18863
  return "2.7.1";
18694
18864
  }
@@ -19269,7 +19439,7 @@ async function handleRequest(line) {
19269
19439
  }
19270
19440
  async function startMcpServer() {
19271
19441
  const access = await getAccessState().catch(() => null);
19272
- if (fs24.existsSync(VOICE_MD)) {
19442
+ if (fs26.existsSync(VOICE_MD)) {
19273
19443
  mcpLog("info", `voice profile: ${VOICE_MD}`);
19274
19444
  } else {
19275
19445
  mcpLog("info", "free local engine ready \u2014 no voice profile yet");
@@ -19302,18 +19472,18 @@ async function startMcpServer() {
19302
19472
 
19303
19473
  // src/lib/mcp-setup.ts
19304
19474
  var import_chalk32 = __toESM(require_source());
19305
- var fs25 = __toESM(require("fs"));
19306
- var path24 = __toESM(require("path"));
19307
- var os11 = __toESM(require("os"));
19475
+ var fs27 = __toESM(require("fs"));
19476
+ var path25 = __toESM(require("path"));
19477
+ var os12 = __toESM(require("os"));
19308
19478
  init_version();
19309
- var HOME2 = os11.homedir();
19479
+ var HOME2 = os12.homedir();
19310
19480
  var IS_WIN2 = process.platform === "win32";
19311
19481
  function claudeDesktopConfigPath() {
19312
19482
  if (IS_WIN2)
19313
- return path24.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
19483
+ return path25.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
19314
19484
  if (process.platform === "linux")
19315
- return path24.join(HOME2, ".config", "Claude", "claude_desktop_config.json");
19316
- return path24.join(HOME2, "Library", "Application Support", "Claude", "claude_desktop_config.json");
19485
+ return path25.join(HOME2, ".config", "Claude", "claude_desktop_config.json");
19486
+ return path25.join(HOME2, "Library", "Application Support", "Claude", "claude_desktop_config.json");
19317
19487
  }
19318
19488
  function printMcpSetup() {
19319
19489
  console.log(import_chalk32.default.bold("\nhold your voice \u2014 mcp setup\n"));
@@ -19395,8 +19565,8 @@ async function runMcpSelfTest() {
19395
19565
  console.log(import_chalk32.default.red(` \u2717 hyv_demo failed: ${e.message}`));
19396
19566
  failed++;
19397
19567
  }
19398
- const voiceMd = path24.join(HOME2, ".hyv", "voice.md");
19399
- if (fs25.existsSync(voiceMd)) {
19568
+ const voiceMd = path25.join(HOME2, ".hyv", "voice.md");
19569
+ if (fs27.existsSync(voiceMd)) {
19400
19570
  console.log(import_chalk32.default.green(" \u2713 voice profile found"));
19401
19571
  passed++;
19402
19572
  } else {
@@ -19436,7 +19606,7 @@ async function runMcpSelfTest() {
19436
19606
  }
19437
19607
 
19438
19608
  // src/commands/export.ts
19439
- var fs26 = __toESM(require("fs"));
19609
+ var fs28 = __toESM(require("fs"));
19440
19610
  init_api();
19441
19611
  var FORMATS = {
19442
19612
  claude: {
@@ -19523,7 +19693,7 @@ async function exportCommand(format, opts) {
19523
19693
  const prompt = fmt.wrap(detail.profile, detail.body);
19524
19694
  if (opts.output) {
19525
19695
  const outFile = opts.output.replace("{name}", profile.slug || profile.name);
19526
- fs26.writeFileSync(outFile, prompt);
19696
+ fs28.writeFileSync(outFile, prompt);
19527
19697
  results.push({ profile: profile.name, file: outFile });
19528
19698
  } else {
19529
19699
  results.push({ profile: profile.name, prompt });
@@ -19560,13 +19730,13 @@ async function exportCommand(format, opts) {
19560
19730
  // src/index.ts
19561
19731
  init_access();
19562
19732
  init_welcome();
19563
- var fs27 = __toESM(require("fs"));
19564
- var path25 = __toESM(require("path"));
19733
+ var fs29 = __toESM(require("fs"));
19734
+ var path26 = __toESM(require("path"));
19565
19735
  var program2 = new Command();
19566
- var pkgPath2 = path25.resolve(__dirname, "..", "package.json");
19736
+ var pkgPath2 = path26.resolve(__dirname, "..", "package.json");
19567
19737
  var pkgVersion2 = (() => {
19568
19738
  try {
19569
- return JSON.parse(fs27.readFileSync(pkgPath2, "utf-8")).version;
19739
+ return JSON.parse(fs29.readFileSync(pkgPath2, "utf-8")).version;
19570
19740
  } catch {
19571
19741
  return "2.7.1";
19572
19742
  }