@holdyourvoice/hyv 2.9.10 → 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 +12 -0
  2. package/dist/index.js +464 -346
  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 fs29 = 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 (fs29.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) => fs29.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 = fs29.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
  }
@@ -4619,12 +4619,12 @@ function shouldReplaceCachedContent(existing, incoming) {
4619
4619
  }
4620
4620
  return true;
4621
4621
  }
4622
- function backupProfileFileIfExists(profilePath) {
4623
- if (!fs.existsSync(profilePath)) {
4622
+ function backupFileIfExists(filePath) {
4623
+ if (!fs.existsSync(filePath)) {
4624
4624
  return false;
4625
4625
  }
4626
4626
  try {
4627
- fs.copyFileSync(profilePath, `${profilePath}.hyv.bak`);
4627
+ fs.copyFileSync(filePath, `${filePath}.hyv.bak`);
4628
4628
  return true;
4629
4629
  } catch {
4630
4630
  return false;
@@ -4637,10 +4637,21 @@ function safeWriteProfileFile(profilePath, content, existing) {
4637
4637
  if (existing !== null && existing === content) {
4638
4638
  return { written: false, backedUp: false, reason: "unchanged" };
4639
4639
  }
4640
- const backedUp = backupProfileFileIfExists(profilePath);
4640
+ const backedUp = backupFileIfExists(profilePath);
4641
4641
  fs.writeFileSync(profilePath, content, { mode: 384 });
4642
4642
  return { written: true, backedUp };
4643
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
+ }
4644
4655
  var fs;
4645
4656
  var init_data_safety = __esm({
4646
4657
  "src/lib/data-safety.ts"() {
@@ -4649,6 +4660,72 @@ var init_data_safety = __esm({
4649
4660
  }
4650
4661
  });
4651
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
+
4652
4729
  // src/lib/config.ts
4653
4730
  function validateApiBase(raw) {
4654
4731
  const trimmed = raw.replace(/\/$/, "");
@@ -4725,21 +4802,21 @@ function toSafeProfileCacheKey(name) {
4725
4802
  function profilePathForName(name) {
4726
4803
  const safe = assertSafeProfileName(name);
4727
4804
  ensureHyvDir();
4728
- const profilePath = path.resolve(PROFILES_DIR, `${safe}.md`);
4729
- 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;
4730
4807
  if (!profilePath.startsWith(profilesRoot)) {
4731
4808
  throw new Error("Invalid profile name");
4732
4809
  }
4733
4810
  return profilePath;
4734
4811
  }
4735
4812
  function ensureHyvDir() {
4736
- const dirs = [HYV_DIR, PROFILES_DIR, CACHE_DIR, QUEUE_DIR];
4813
+ const dirs = [HYV_DIR, PROFILES_DIR, CACHE_DIR2, QUEUE_DIR];
4737
4814
  for (const dir of dirs) {
4738
- if (!fs2.existsSync(dir)) {
4739
- fs2.mkdirSync(dir, { recursive: true, mode: 448 });
4815
+ if (!fs3.existsSync(dir)) {
4816
+ fs3.mkdirSync(dir, { recursive: true, mode: 448 });
4740
4817
  } else {
4741
4818
  try {
4742
- fs2.chmodSync(dir, 448);
4819
+ fs3.chmodSync(dir, 448);
4743
4820
  } catch {
4744
4821
  }
4745
4822
  }
@@ -4747,33 +4824,33 @@ function ensureHyvDir() {
4747
4824
  }
4748
4825
  function appendSecureLine(filePath, line, dir) {
4749
4826
  if (dir) {
4750
- if (!fs2.existsSync(dir))
4751
- fs2.mkdirSync(dir, { recursive: true, mode: 448 });
4827
+ if (!fs3.existsSync(dir))
4828
+ fs3.mkdirSync(dir, { recursive: true, mode: 448 });
4752
4829
  else {
4753
4830
  try {
4754
- fs2.chmodSync(dir, 448);
4831
+ fs3.chmodSync(dir, 448);
4755
4832
  } catch {
4756
4833
  }
4757
4834
  }
4758
4835
  }
4759
- if (!fs2.existsSync(filePath)) {
4760
- fs2.writeFileSync(filePath, line, { mode: 384 });
4836
+ if (!fs3.existsSync(filePath)) {
4837
+ fs3.writeFileSync(filePath, line, { mode: 384 });
4761
4838
  return;
4762
4839
  }
4763
4840
  try {
4764
- fs2.chmodSync(filePath, 384);
4841
+ fs3.chmodSync(filePath, 384);
4765
4842
  } catch {
4766
4843
  }
4767
- fs2.appendFileSync(filePath, line);
4844
+ fs3.appendFileSync(filePath, line);
4768
4845
  }
4769
4846
  function isInitialized() {
4770
- return fs2.existsSync(AUTH_FILE);
4847
+ return fs3.existsSync(AUTH_FILE);
4771
4848
  }
4772
4849
  function readAuth() {
4773
4850
  try {
4774
- if (!fs2.existsSync(AUTH_FILE))
4851
+ if (!fs3.existsSync(AUTH_FILE))
4775
4852
  return null;
4776
- const data = fs2.readFileSync(AUTH_FILE, "utf-8");
4853
+ const data = fs3.readFileSync(AUTH_FILE, "utf-8");
4777
4854
  return JSON.parse(data);
4778
4855
  } catch {
4779
4856
  return null;
@@ -4781,13 +4858,19 @@ function readAuth() {
4781
4858
  }
4782
4859
  function writeAuth(auth) {
4783
4860
  ensureHyvDir();
4784
- fs2.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 });
4785
4868
  }
4786
4869
  function readConfig() {
4787
4870
  try {
4788
- if (!fs2.existsSync(CONFIG_FILE))
4871
+ if (!fs3.existsSync(CONFIG_FILE))
4789
4872
  return {};
4790
- const data = fs2.readFileSync(CONFIG_FILE, "utf-8");
4873
+ const data = fs3.readFileSync(CONFIG_FILE, "utf-8");
4791
4874
  return JSON.parse(data);
4792
4875
  } catch {
4793
4876
  return {};
@@ -4795,7 +4878,7 @@ function readConfig() {
4795
4878
  }
4796
4879
  function writeConfig(config) {
4797
4880
  ensureHyvDir();
4798
- fs2.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 384 });
4881
+ fs3.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 384 });
4799
4882
  }
4800
4883
  function getToken() {
4801
4884
  const auth = readAuth();
@@ -4811,9 +4894,9 @@ function getToken() {
4811
4894
  }
4812
4895
  function listCachedProfiles() {
4813
4896
  try {
4814
- if (!fs2.existsSync(PROFILES_DIR))
4897
+ if (!fs3.existsSync(PROFILES_DIR))
4815
4898
  return [];
4816
- return fs2.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", ""));
4817
4900
  } catch {
4818
4901
  return [];
4819
4902
  }
@@ -4821,9 +4904,9 @@ function listCachedProfiles() {
4821
4904
  function readCachedProfile(name) {
4822
4905
  const profilePath = profilePathForName(name);
4823
4906
  try {
4824
- if (!fs2.existsSync(profilePath))
4907
+ if (!fs3.existsSync(profilePath))
4825
4908
  return null;
4826
- return fs2.readFileSync(profilePath, "utf-8");
4909
+ return fs3.readFileSync(profilePath, "utf-8");
4827
4910
  } catch (err) {
4828
4911
  if (err.message?.includes("Invalid profile name"))
4829
4912
  throw err;
@@ -4832,19 +4915,22 @@ function readCachedProfile(name) {
4832
4915
  }
4833
4916
  function writeCachedProfile(name, content) {
4834
4917
  const profilePath = profilePathForName(name);
4835
- const existing = fs2.existsSync(profilePath) ? fs2.readFileSync(profilePath, "utf-8") : null;
4918
+ const existing = fs3.existsSync(profilePath) ? fs3.readFileSync(profilePath, "utf-8") : null;
4836
4919
  const result = safeWriteProfileFile(profilePath, content, existing);
4837
4920
  if (!result.written && result.reason === "empty-would-erase") {
4838
4921
  throw new Error("Refusing to replace profile with empty content");
4839
4922
  }
4923
+ if (result.written) {
4924
+ setProfileMeta(name, { local_saved_at: (/* @__PURE__ */ new Date()).toISOString() });
4925
+ }
4840
4926
  }
4841
4927
  function getQueuedSignals() {
4842
4928
  try {
4843
- if (!fs2.existsSync(QUEUE_DIR))
4929
+ if (!fs3.existsSync(QUEUE_DIR))
4844
4930
  return [];
4845
- const files = fs2.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
4931
+ const files = fs3.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
4846
4932
  return files.map((f) => {
4847
- const data = fs2.readFileSync(path.join(QUEUE_DIR, f), "utf-8");
4933
+ const data = fs3.readFileSync(path2.join(QUEUE_DIR, f), "utf-8");
4848
4934
  return JSON.parse(data);
4849
4935
  });
4850
4936
  } catch {
@@ -4854,38 +4940,39 @@ function getQueuedSignals() {
4854
4940
  function queueSignal(signal) {
4855
4941
  ensureHyvDir();
4856
4942
  const id = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
4857
- const filePath = path.join(QUEUE_DIR, `${id}.json`);
4858
- fs2.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 });
4859
4945
  }
4860
4946
  function saveLastEditSession(session) {
4861
4947
  ensureHyvDir();
4862
4948
  const data = { ...session, saved_at: (/* @__PURE__ */ new Date()).toISOString() };
4863
- fs2.writeFileSync(LAST_SESSION_FILE, JSON.stringify(data, null, 2), { mode: 384 });
4949
+ fs3.writeFileSync(LAST_SESSION_FILE, JSON.stringify(data, null, 2), { mode: 384 });
4864
4950
  }
4865
4951
  function readLastEditSession() {
4866
4952
  try {
4867
- if (!fs2.existsSync(LAST_SESSION_FILE))
4953
+ if (!fs3.existsSync(LAST_SESSION_FILE))
4868
4954
  return null;
4869
- return JSON.parse(fs2.readFileSync(LAST_SESSION_FILE, "utf-8"));
4955
+ return JSON.parse(fs3.readFileSync(LAST_SESSION_FILE, "utf-8"));
4870
4956
  } catch {
4871
4957
  return null;
4872
4958
  }
4873
4959
  }
4874
- var fs2, path, os, HYV_DIR, AUTH_FILE, CONFIG_FILE, PROFILES_DIR, CACHE_DIR, QUEUE_DIR, LAST_SESSION_FILE, ALLOWED_API_HOSTS, ALLOWED_OAUTH_HOSTS, API_BASE, PROFILE_NAME_RE;
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;
4875
4961
  var init_config = __esm({
4876
4962
  "src/lib/config.ts"() {
4877
4963
  "use strict";
4878
- fs2 = __toESM(require("fs"));
4879
- path = __toESM(require("path"));
4880
- os = __toESM(require("os"));
4964
+ fs3 = __toESM(require("fs"));
4965
+ path2 = __toESM(require("path"));
4966
+ os2 = __toESM(require("os"));
4881
4967
  init_data_safety();
4882
- HYV_DIR = path.join(os.homedir(), ".hyv");
4883
- AUTH_FILE = path.join(HYV_DIR, "auth.json");
4884
- CONFIG_FILE = path.join(HYV_DIR, "config.json");
4885
- PROFILES_DIR = path.join(HYV_DIR, "profiles");
4886
- CACHE_DIR = path.join(HYV_DIR, "cache");
4887
- QUEUE_DIR = path.join(HYV_DIR, "queue");
4888
- LAST_SESSION_FILE = path.join(HYV_DIR, "last-session.json");
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");
4889
4976
  ALLOWED_API_HOSTS = /* @__PURE__ */ new Set([
4890
4977
  "holdyourvoice.com",
4891
4978
  "www.holdyourvoice.com",
@@ -4903,11 +4990,11 @@ var init_config = __esm({
4903
4990
  var require_is_docker = __commonJS({
4904
4991
  "node_modules/is-docker/index.js"(exports2, module2) {
4905
4992
  "use strict";
4906
- var fs29 = require("fs");
4993
+ var fs30 = require("fs");
4907
4994
  var isDocker;
4908
4995
  function hasDockerEnv() {
4909
4996
  try {
4910
- fs29.statSync("/.dockerenv");
4997
+ fs30.statSync("/.dockerenv");
4911
4998
  return true;
4912
4999
  } catch (_2) {
4913
5000
  return false;
@@ -4915,7 +5002,7 @@ var require_is_docker = __commonJS({
4915
5002
  }
4916
5003
  function hasDockerCGroup() {
4917
5004
  try {
4918
- return fs29.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
5005
+ return fs30.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4919
5006
  } catch (_2) {
4920
5007
  return false;
4921
5008
  }
@@ -4933,21 +5020,21 @@ var require_is_docker = __commonJS({
4933
5020
  var require_is_wsl = __commonJS({
4934
5021
  "node_modules/is-wsl/index.js"(exports2, module2) {
4935
5022
  "use strict";
4936
- var os12 = require("os");
4937
- var fs29 = require("fs");
5023
+ var os13 = require("os");
5024
+ var fs30 = require("fs");
4938
5025
  var isDocker = require_is_docker();
4939
5026
  var isWsl = () => {
4940
5027
  if (process.platform !== "linux") {
4941
5028
  return false;
4942
5029
  }
4943
- if (os12.release().toLowerCase().includes("microsoft")) {
5030
+ if (os13.release().toLowerCase().includes("microsoft")) {
4944
5031
  if (isDocker()) {
4945
5032
  return false;
4946
5033
  }
4947
5034
  return true;
4948
5035
  }
4949
5036
  try {
4950
- return fs29.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
5037
+ return fs30.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
4951
5038
  } catch (_2) {
4952
5039
  return false;
4953
5040
  }
@@ -4986,17 +5073,17 @@ var require_define_lazy_prop = __commonJS({
4986
5073
  // node_modules/open/index.js
4987
5074
  var require_open = __commonJS({
4988
5075
  "node_modules/open/index.js"(exports2, module2) {
4989
- var path26 = require("path");
5076
+ var path27 = require("path");
4990
5077
  var childProcess = require("child_process");
4991
- var { promises: fs29, constants: fsConstants } = require("fs");
5078
+ var { promises: fs30, constants: fsConstants } = require("fs");
4992
5079
  var isWsl = require_is_wsl();
4993
5080
  var isDocker = require_is_docker();
4994
5081
  var defineLazyProperty = require_define_lazy_prop();
4995
- var localXdgOpenPath = path26.join(__dirname, "xdg-open");
5082
+ var localXdgOpenPath = path27.join(__dirname, "xdg-open");
4996
5083
  var { platform, arch } = process;
4997
5084
  var hasContainerEnv = () => {
4998
5085
  try {
4999
- fs29.statSync("/run/.containerenv");
5086
+ fs30.statSync("/run/.containerenv");
5000
5087
  return true;
5001
5088
  } catch {
5002
5089
  return false;
@@ -5019,14 +5106,14 @@ var require_open = __commonJS({
5019
5106
  const configFilePath = "/etc/wsl.conf";
5020
5107
  let isConfigFileExists = false;
5021
5108
  try {
5022
- await fs29.access(configFilePath, fsConstants.F_OK);
5109
+ await fs30.access(configFilePath, fsConstants.F_OK);
5023
5110
  isConfigFileExists = true;
5024
5111
  } catch {
5025
5112
  }
5026
5113
  if (!isConfigFileExists) {
5027
5114
  return defaultMountPoint;
5028
5115
  }
5029
- const configContent = await fs29.readFile(configFilePath, { encoding: "utf8" });
5116
+ const configContent = await fs30.readFile(configFilePath, { encoding: "utf8" });
5030
5117
  const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
5031
5118
  if (!configMountPoint) {
5032
5119
  return defaultMountPoint;
@@ -5126,7 +5213,7 @@ var require_open = __commonJS({
5126
5213
  const isBundled = !__dirname || __dirname === "/";
5127
5214
  let exeLocalXdgOpen = false;
5128
5215
  try {
5129
- await fs29.access(localXdgOpenPath, fsConstants.X_OK);
5216
+ await fs30.access(localXdgOpenPath, fsConstants.X_OK);
5130
5217
  exeLocalXdgOpen = true;
5131
5218
  } catch {
5132
5219
  }
@@ -5326,20 +5413,20 @@ var init_free_paid = __esm({
5326
5413
  // src/lib/version.ts
5327
5414
  function pkgRoot() {
5328
5415
  const candidates = [
5329
- path2.resolve(__dirname, ".."),
5416
+ path3.resolve(__dirname, ".."),
5330
5417
  // dist/ or src/lib/ → cli/
5331
- path2.resolve(__dirname, "..", "..")
5418
+ path3.resolve(__dirname, "..", "..")
5332
5419
  // src/lib/ → cli/
5333
5420
  ];
5334
5421
  for (const root of candidates) {
5335
- if (fs3.existsSync(path2.join(root, "package.json")))
5422
+ if (fs4.existsSync(path3.join(root, "package.json")))
5336
5423
  return root;
5337
5424
  }
5338
- return path2.resolve(__dirname, "..");
5425
+ return path3.resolve(__dirname, "..");
5339
5426
  }
5340
5427
  function getCliVersion() {
5341
5428
  try {
5342
- const pkg = JSON.parse(fs3.readFileSync(path2.join(PKG_ROOT, "package.json"), "utf-8"));
5429
+ const pkg = JSON.parse(fs4.readFileSync(path3.join(PKG_ROOT, "package.json"), "utf-8"));
5343
5430
  return pkg.version || "unknown";
5344
5431
  } catch {
5345
5432
  return "unknown";
@@ -5347,8 +5434,8 @@ function getCliVersion() {
5347
5434
  }
5348
5435
  function getRulesVersion() {
5349
5436
  try {
5350
- if (fs3.existsSync(RULES_MANIFEST)) {
5351
- return JSON.parse(fs3.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";
5352
5439
  }
5353
5440
  } catch {
5354
5441
  }
@@ -5386,15 +5473,15 @@ function fetchLatestNpmVersion(timeoutMs = 8e3) {
5386
5473
  );
5387
5474
  });
5388
5475
  }
5389
- var fs3, path2, import_child_process, PKG_ROOT, RULES_MANIFEST;
5476
+ var fs4, path3, import_child_process, PKG_ROOT, RULES_MANIFEST;
5390
5477
  var init_version = __esm({
5391
5478
  "src/lib/version.ts"() {
5392
5479
  "use strict";
5393
- fs3 = __toESM(require("fs"));
5394
- path2 = __toESM(require("path"));
5480
+ fs4 = __toESM(require("fs"));
5481
+ path3 = __toESM(require("path"));
5395
5482
  import_child_process = require("child_process");
5396
5483
  PKG_ROOT = pkgRoot();
5397
- RULES_MANIFEST = path2.join(PKG_ROOT, "assets", "detection-rules.json");
5484
+ RULES_MANIFEST = path3.join(PKG_ROOT, "assets", "detection-rules.json");
5398
5485
  }
5399
5486
  });
5400
5487
 
@@ -5754,15 +5841,15 @@ function recordEvent(event, meta) {
5754
5841
  } catch {
5755
5842
  }
5756
5843
  }
5757
- var path3, os2, TELEMETRY_DIR, TELEMETRY_FILE;
5844
+ var path4, os3, TELEMETRY_DIR, TELEMETRY_FILE;
5758
5845
  var init_telemetry = __esm({
5759
5846
  "src/lib/telemetry.ts"() {
5760
5847
  "use strict";
5761
- path3 = __toESM(require("path"));
5762
- os2 = __toESM(require("os"));
5848
+ path4 = __toESM(require("path"));
5849
+ os3 = __toESM(require("os"));
5763
5850
  init_config();
5764
- TELEMETRY_DIR = path3.join(os2.homedir(), ".hyv", "telemetry");
5765
- 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");
5766
5853
  }
5767
5854
  });
5768
5855
 
@@ -5773,11 +5860,11 @@ __export(api_exports, {
5773
5860
  apiPost: () => apiPost,
5774
5861
  requireSubscription: () => requireSubscription
5775
5862
  });
5776
- async function request2(method, path26, body) {
5863
+ async function request2(method, path27, body) {
5777
5864
  const token = await getValidToken();
5778
5865
  if (!token)
5779
5866
  throw new Error("you're not signed in yet. run: hyv init");
5780
- const url = `${API_BASE}${path26}`;
5867
+ const url = `${API_BASE}${path27}`;
5781
5868
  const opts = {
5782
5869
  method,
5783
5870
  headers: {
@@ -5802,11 +5889,11 @@ async function request2(method, path26, body) {
5802
5889
  }
5803
5890
  return res.json();
5804
5891
  }
5805
- function apiGet(path26) {
5806
- return request2("GET", path26);
5892
+ function apiGet(path27) {
5893
+ return request2("GET", path27);
5807
5894
  }
5808
- function apiPost(path26, body) {
5809
- return request2("POST", path26, body);
5895
+ function apiPost(path27, body) {
5896
+ return request2("POST", path27, body);
5810
5897
  }
5811
5898
  async function requireSubscription() {
5812
5899
  const { requirePaidFeature: requirePaidFeature2 } = await Promise.resolve().then(() => (init_access(), access_exports));
@@ -5990,7 +6077,7 @@ async function fetchAndCache(slug) {
5990
6077
  };
5991
6078
  const entry = { data: profile, timestamp: Date.now(), version: 1 };
5992
6079
  memoryCache.set(slug, entry);
5993
- saveToDiskCache(slug, entry);
6080
+ saveToDiskCache(slug, entry, profile.updated_at);
5994
6081
  return profile;
5995
6082
  } catch (err) {
5996
6083
  const fallback = loadFromDiskCache(slug);
@@ -6011,14 +6098,14 @@ async function refreshInBackground(slug) {
6011
6098
  }
6012
6099
  }
6013
6100
  function getDiskCachePath(slug) {
6014
- 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`);
6015
6102
  }
6016
6103
  function loadFromDiskCache(slug) {
6017
6104
  try {
6018
6105
  const cachePath = getDiskCachePath(slug);
6019
- if (!fs4.existsSync(cachePath))
6106
+ if (!fs5.existsSync(cachePath))
6020
6107
  return null;
6021
- const content = fs4.readFileSync(cachePath, "utf-8");
6108
+ const content = fs5.readFileSync(cachePath, "utf-8");
6022
6109
  const entry = JSON.parse(content);
6023
6110
  if (!entry.data || !entry.timestamp)
6024
6111
  return null;
@@ -6027,12 +6114,24 @@ function loadFromDiskCache(slug) {
6027
6114
  return null;
6028
6115
  }
6029
6116
  }
6030
- function saveToDiskCache(slug, entry) {
6117
+ function saveToDiskCache(slug, entry, serverUpdatedAt) {
6031
6118
  try {
6032
- if (!fs4.existsSync(DISK_CACHE_DIR)) {
6033
- fs4.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 });
6034
6134
  }
6035
- fs4.writeFileSync(getDiskCachePath(slug), JSON.stringify(entry), "utf-8");
6036
6135
  } catch {
6037
6136
  }
6038
6137
  }
@@ -6050,18 +6149,20 @@ function buildMinimalProfile(slug, body) {
6050
6149
  cadence: {}
6051
6150
  };
6052
6151
  }
6053
- var fs4, path4, os3, CACHE_TTL, memoryCache, DISK_CACHE_DIR;
6152
+ var fs5, path5, os4, CACHE_TTL, memoryCache, DISK_CACHE_DIR;
6054
6153
  var init_profile = __esm({
6055
6154
  "src/lib/profile.ts"() {
6056
6155
  "use strict";
6057
6156
  init_api();
6058
6157
  init_config();
6059
- fs4 = __toESM(require("fs"));
6060
- path4 = __toESM(require("path"));
6061
- 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"));
6062
6163
  CACHE_TTL = 5 * 60 * 1e3;
6063
6164
  memoryCache = /* @__PURE__ */ new Map();
6064
- DISK_CACHE_DIR = path4.join(os3.homedir(), ".hyv", "cache", "profiles");
6165
+ DISK_CACHE_DIR = path5.join(os4.homedir(), ".hyv", "cache", "profiles");
6065
6166
  }
6066
6167
  });
6067
6168
 
@@ -6076,14 +6177,14 @@ __export(local_profile_exports, {
6076
6177
  loadProfileFromDiskCache: () => loadProfileFromDiskCache
6077
6178
  });
6078
6179
  function cachePathForSlug(slug) {
6079
- 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`);
6080
6181
  }
6081
6182
  function loadProfileFromDiskCache(slug) {
6082
6183
  try {
6083
6184
  const cachePath = cachePathForSlug(slug);
6084
- if (!fs5.existsSync(cachePath))
6185
+ if (!fs6.existsSync(cachePath))
6085
6186
  return void 0;
6086
- const entry = JSON.parse(fs5.readFileSync(cachePath, "utf-8"));
6187
+ const entry = JSON.parse(fs6.readFileSync(cachePath, "utf-8"));
6087
6188
  if (entry?.data?.body)
6088
6189
  return entry.data;
6089
6190
  } catch {
@@ -6107,9 +6208,9 @@ function profileFromMarkdown(slug, body) {
6107
6208
  function getProfileCacheAge(slug) {
6108
6209
  try {
6109
6210
  const cachePath = cachePathForSlug(slug);
6110
- if (!fs5.existsSync(cachePath))
6211
+ if (!fs6.existsSync(cachePath))
6111
6212
  return null;
6112
- const entry = JSON.parse(fs5.readFileSync(cachePath, "utf-8"));
6213
+ const entry = JSON.parse(fs6.readFileSync(cachePath, "utf-8"));
6113
6214
  return entry.timestamp || null;
6114
6215
  } catch {
6115
6216
  return null;
@@ -6117,9 +6218,9 @@ function getProfileCacheAge(slug) {
6117
6218
  }
6118
6219
  function listDiskCachedProfiles() {
6119
6220
  try {
6120
- if (!fs5.existsSync(DISK_CACHE_DIR2))
6221
+ if (!fs6.existsSync(DISK_CACHE_DIR2))
6121
6222
  return [];
6122
- return fs5.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", ""));
6123
6224
  } catch {
6124
6225
  return [];
6125
6226
  }
@@ -6176,17 +6277,17 @@ function hasRichProfile(profile) {
6176
6277
  return false;
6177
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;
6178
6279
  }
6179
- var fs5, path5, os4, DISK_CACHE_DIR2;
6280
+ var fs6, path6, os5, DISK_CACHE_DIR2;
6180
6281
  var init_local_profile = __esm({
6181
6282
  "src/lib/local-profile.ts"() {
6182
6283
  "use strict";
6183
- fs5 = __toESM(require("fs"));
6184
- path5 = __toESM(require("path"));
6185
- os4 = __toESM(require("os"));
6284
+ fs6 = __toESM(require("fs"));
6285
+ path6 = __toESM(require("path"));
6286
+ os5 = __toESM(require("os"));
6186
6287
  init_config();
6187
6288
  init_config();
6188
6289
  init_profile();
6189
- DISK_CACHE_DIR2 = path5.join(os4.homedir(), ".hyv", "cache", "profiles");
6290
+ DISK_CACHE_DIR2 = path6.join(os5.homedir(), ".hyv", "cache", "profiles");
6190
6291
  }
6191
6292
  });
6192
6293
 
@@ -6276,24 +6377,24 @@ var init_profile_parse = __esm({
6276
6377
 
6277
6378
  // src/lib/rules-loader.ts
6278
6379
  function loadSupplementalPatterns() {
6279
- const p = path8.join(CACHE_DIR, "supplemental-rules.json");
6380
+ const p = path9.join(CACHE_DIR2, "supplemental-rules.json");
6280
6381
  try {
6281
- if (!fs8.existsSync(p))
6382
+ if (!fs9.existsSync(p))
6282
6383
  return [];
6283
- const data = JSON.parse(fs8.readFileSync(p, "utf-8"));
6384
+ const data = JSON.parse(fs9.readFileSync(p, "utf-8"));
6284
6385
  return Array.isArray(data.patterns) ? data.patterns : [];
6285
6386
  } catch {
6286
6387
  return [];
6287
6388
  }
6288
6389
  }
6289
- var fs8, path8, BUNDLED_MANIFEST;
6390
+ var fs9, path9, BUNDLED_MANIFEST;
6290
6391
  var init_rules_loader = __esm({
6291
6392
  "src/lib/rules-loader.ts"() {
6292
6393
  "use strict";
6293
- fs8 = __toESM(require("fs"));
6294
- path8 = __toESM(require("path"));
6394
+ fs9 = __toESM(require("fs"));
6395
+ path9 = __toESM(require("path"));
6295
6396
  init_config();
6296
- BUNDLED_MANIFEST = path8.resolve(__dirname, "..", "..", "assets", "detection-rules.json");
6397
+ BUNDLED_MANIFEST = path9.resolve(__dirname, "..", "..", "assets", "detection-rules.json");
6297
6398
  }
6298
6399
  });
6299
6400
 
@@ -7325,10 +7426,10 @@ function Ht(n7) {
7325
7426
  return isNaN(n7) ? n7.charCodeAt(0) : parseInt(n7, 10);
7326
7427
  }
7327
7428
  function ps(n7) {
7328
- return n7.replace(as, fe).replace(ls, ue).replace(cs, qt).replace(fs9, de).replace(us, pe);
7429
+ return n7.replace(as, fe).replace(ls, ue).replace(cs, qt).replace(fs10, de).replace(us, pe);
7329
7430
  }
7330
7431
  function ms(n7) {
7331
- 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, ".");
7332
7433
  }
7333
7434
  function me(n7) {
7334
7435
  if (!n7)
@@ -7428,7 +7529,7 @@ function Ut(n7, t = {}) {
7428
7529
  function es(n7, t = {}) {
7429
7530
  return new I(n7, t).iterate();
7430
7531
  }
7431
- var import_node_url, import_node_path, import_node_url2, import_fs, xi, import_promises, import_node_events, import_node_stream, import_node_string_decoder, Gt, ce, ss, fe, ue, qt, de, pe, is, rs, ns, os5, hs, as, ls, cs, fs9, us, ds, at, Ss, lt, Es, we, ye, W, xs, be, vs, Ct, Cs, Ts, As, ks, Kt, Se, Ee, Q, tt, O, Rs, Os, Fs, Ds, Ms, Ns, _s, Ls, Ws, Ps, js, Is, zs, Bs, Us, $s, Gs, Hs, Ce, Te, Ae, xe, qs, A, Ks, Vs, Ys, Xs, Js, N, Zs, ke, Qs, ti, ve, ei, D, si, Oe, Vt, Fe, At, Re, ii, q, De, Tt, ri, ft, Ne, oi, hi, ai, G, H, K, kt, ut, Rt, _e, Ot, Le, P, et, v, dt, st, C, F, T, Yt, Ft, k, x, Xt, Jt, We, Zt, B, Qt, Dt, pt, Y, M, mt, li, ci, fi, ui, Mt, te, di, pi, V, vi, wt, Ue, $e, Ri, Oi, L, Ge, He, U, qe, Ke, X, Ve, _, gt, se, je, yt, j, Nt, Lt, Ie, Fi, ie, ze, bt, Be, _t, Wt, ne, Ye, R, Pt, jt, It, it, rt, St, Cr, Xe, Di, Mi, Ni, nt, _i, ot, oe, he, ae, Et, Li, zt, xt, vt, Pi, I, le, ji, Ii, zi, Bi, Ui, Ze;
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;
7432
7533
  var init_index_min = __esm({
7433
7534
  "node_modules/glob/dist/esm/index.min.js"() {
7434
7535
  import_node_url = require("node:url");
@@ -7475,12 +7576,12 @@ var init_index_min = __esm({
7475
7576
  is = new RegExp(fe, "g");
7476
7577
  rs = new RegExp(ue, "g");
7477
7578
  ns = new RegExp(qt, "g");
7478
- os5 = new RegExp(de, "g");
7579
+ os6 = new RegExp(de, "g");
7479
7580
  hs = new RegExp(pe, "g");
7480
7581
  as = /\\\\/g;
7481
7582
  ls = /\\{/g;
7482
7583
  cs = /\\}/g;
7483
- fs9 = /\\,/g;
7584
+ fs10 = /\\,/g;
7484
7585
  us = /\\./g;
7485
7586
  ds = 1e5;
7486
7587
  at = (n7) => {
@@ -10626,7 +10727,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s))
10626
10727
 
10627
10728
  // src/lib/document-text.ts
10628
10729
  function isSupportedDocument(filePath) {
10629
- return SUPPORTED_EXTENSIONS.has(path9.extname(filePath).toLowerCase());
10730
+ return SUPPORTED_EXTENSIONS.has(path10.extname(filePath).toLowerCase());
10630
10731
  }
10631
10732
  function stripHtml(html) {
10632
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();
@@ -10715,7 +10816,7 @@ function extractPdf(filePath) {
10715
10816
  if (out.trim())
10716
10817
  return out.trim();
10717
10818
  }
10718
- const raw = extractPdfRaw(fs10.readFileSync(filePath));
10819
+ const raw = extractPdfRaw(fs11.readFileSync(filePath));
10719
10820
  if (raw.trim().length > 20)
10720
10821
  return raw;
10721
10822
  throw new Error("could not extract text from .pdf \u2014 try pasting the draft instead");
@@ -10737,23 +10838,23 @@ function extractSkillArchive(filePath) {
10737
10838
  return parts.join("\n\n");
10738
10839
  }
10739
10840
  function extractTextFromFile(filePath) {
10740
- const resolved = path9.resolve(filePath);
10741
- if (!fs10.existsSync(resolved)) {
10841
+ const resolved = path10.resolve(filePath);
10842
+ if (!fs11.existsSync(resolved)) {
10742
10843
  throw new Error(`file not found: ${resolved}`);
10743
10844
  }
10744
- const stat = fs10.statSync(resolved);
10845
+ const stat = fs11.statSync(resolved);
10745
10846
  if (!stat.isFile()) {
10746
10847
  throw new Error(`not a file: ${resolved}`);
10747
10848
  }
10748
- const ext = path9.extname(resolved).toLowerCase();
10849
+ const ext = path10.extname(resolved).toLowerCase();
10749
10850
  switch (ext) {
10750
10851
  case ".md":
10751
10852
  case ".txt":
10752
10853
  case ".markdown":
10753
- return fs10.readFileSync(resolved, "utf-8");
10854
+ return fs11.readFileSync(resolved, "utf-8");
10754
10855
  case ".html":
10755
10856
  case ".htm":
10756
- return stripHtml(fs10.readFileSync(resolved, "utf-8"));
10857
+ return stripHtml(fs11.readFileSync(resolved, "utf-8"));
10757
10858
  case ".docx":
10758
10859
  return extractDocx(resolved);
10759
10860
  case ".pdf":
@@ -10773,11 +10874,11 @@ function listDocumentsInDir(dirPath) {
10773
10874
  }).filter(isSupportedDocument);
10774
10875
  }
10775
10876
  function collectDocumentsFromPath(inputPath) {
10776
- const resolved = path9.resolve(inputPath.trim().replace(/^~(?=$|\/)/, os6.homedir()));
10777
- if (!fs10.existsSync(resolved)) {
10877
+ const resolved = path10.resolve(inputPath.trim().replace(/^~(?=$|\/)/, os7.homedir()));
10878
+ if (!fs11.existsSync(resolved)) {
10778
10879
  throw new Error(`path not found: ${resolved}`);
10779
10880
  }
10780
- const stat = fs10.statSync(resolved);
10881
+ const stat = fs11.statSync(resolved);
10781
10882
  const files = stat.isFile() ? [resolved] : listDocumentsInDir(resolved);
10782
10883
  if (!files.length) {
10783
10884
  throw new Error(
@@ -10804,20 +10905,20 @@ function readDraftInput(input) {
10804
10905
  const trimmed = input.trim();
10805
10906
  if (!trimmed)
10806
10907
  throw new Error("empty draft");
10807
- const maybePath = path9.resolve(trimmed.replace(/^~(?=$|\/)/, os6.homedir()));
10808
- if (fs10.existsSync(maybePath) && fs10.statSync(maybePath).isFile()) {
10908
+ const maybePath = path10.resolve(trimmed.replace(/^~(?=$|\/)/, os7.homedir()));
10909
+ if (fs11.existsSync(maybePath) && fs11.statSync(maybePath).isFile()) {
10809
10910
  return { text: extractTextFromFile(maybePath), source: maybePath };
10810
10911
  }
10811
10912
  return { text: trimmed, source: "paste" };
10812
10913
  }
10813
- var import_child_process2, fs10, os6, path9, SUPPORTED_EXTENSIONS, SAMPLE_GLOB, GLOB_IGNORE;
10914
+ var import_child_process2, fs11, os7, path10, SUPPORTED_EXTENSIONS, SAMPLE_GLOB, GLOB_IGNORE;
10814
10915
  var init_document_text = __esm({
10815
10916
  "src/lib/document-text.ts"() {
10816
10917
  "use strict";
10817
10918
  import_child_process2 = require("child_process");
10818
- fs10 = __toESM(require("fs"));
10819
- os6 = __toESM(require("os"));
10820
- path9 = __toESM(require("path"));
10919
+ fs11 = __toESM(require("fs"));
10920
+ os7 = __toESM(require("os"));
10921
+ path10 = __toESM(require("path"));
10821
10922
  init_index_min();
10822
10923
  SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
10823
10924
  ".md",
@@ -10892,21 +10993,21 @@ function runPipeline(text, profile, applyFixes = false) {
10892
10993
  };
10893
10994
  }
10894
10995
  function readText(source) {
10895
- const fs29 = require("fs");
10896
- const path26 = require("path");
10996
+ const fs30 = require("fs");
10997
+ const path27 = require("path");
10897
10998
  if (source === "-") {
10898
10999
  if (process.stdin.isTTY) {
10899
11000
  console.error("No input provided. Pipe content or specify a file.");
10900
11001
  process.exit(1);
10901
11002
  }
10902
- return { text: fs29.readFileSync(0, "utf-8"), path: "stdin" };
11003
+ return { text: fs30.readFileSync(0, "utf-8"), path: "stdin" };
10903
11004
  }
10904
- const resolved = path26.resolve(source);
10905
- if (!fs29.existsSync(resolved)) {
11005
+ const resolved = path27.resolve(source);
11006
+ if (!fs30.existsSync(resolved)) {
10906
11007
  console.error(`File not found: ${resolved}`);
10907
11008
  process.exit(1);
10908
11009
  }
10909
- const stat = fs29.statSync(resolved);
11010
+ const stat = fs30.statSync(resolved);
10910
11011
  if (stat.isDirectory()) {
10911
11012
  console.error(`${resolved} is a directory, not a file.`);
10912
11013
  process.exit(1);
@@ -11358,10 +11459,10 @@ var init_terminal_ui = __esm({
11358
11459
  // src/lib/welcome-flow.ts
11359
11460
  function readWelcomeState() {
11360
11461
  try {
11361
- if (!fs14.existsSync(STATE_FILE)) {
11462
+ if (!fs15.existsSync(STATE_FILE)) {
11362
11463
  return { completed_steps: [], updated_at: (/* @__PURE__ */ new Date()).toISOString() };
11363
11464
  }
11364
- return JSON.parse(fs14.readFileSync(STATE_FILE, "utf-8"));
11465
+ return JSON.parse(fs15.readFileSync(STATE_FILE, "utf-8"));
11365
11466
  } catch {
11366
11467
  return { completed_steps: [], updated_at: (/* @__PURE__ */ new Date()).toISOString() };
11367
11468
  }
@@ -11373,7 +11474,7 @@ function writeWelcomeState(patch) {
11373
11474
  ...patch,
11374
11475
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
11375
11476
  };
11376
- fs14.writeFileSync(STATE_FILE, JSON.stringify(next, null, 2), { mode: 384 });
11477
+ fs15.writeFileSync(STATE_FILE, JSON.stringify(next, null, 2), { mode: 384 });
11377
11478
  return next;
11378
11479
  }
11379
11480
  function markStepComplete(step) {
@@ -11602,7 +11703,7 @@ function saveLocalProfile(name, content) {
11602
11703
  writeWelcomeState({ profile_name: safe });
11603
11704
  markStepComplete("name");
11604
11705
  markStepComplete("samples");
11605
- return path13.join(os7.homedir(), ".hyv", "profiles", `${safe}.md`);
11706
+ return path14.join(os8.homedir(), ".hyv", "profiles", `${safe}.md`);
11606
11707
  }
11607
11708
  function fetchUrlText(url) {
11608
11709
  return new Promise((resolve15, reject) => {
@@ -11824,8 +11925,8 @@ async function stepSignup(profileName) {
11824
11925
  } else {
11825
11926
  console.log(import_chalk12.default.dim("\n already signed in \u2014 syncing profile..."));
11826
11927
  }
11827
- const content = fs14.readFileSync(
11828
- path13.join(os7.homedir(), ".hyv", "profiles", `${profileName}.md`),
11928
+ const content = fs15.readFileSync(
11929
+ path14.join(os8.homedir(), ".hyv", "profiles", `${profileName}.md`),
11829
11930
  "utf-8"
11830
11931
  );
11831
11932
  try {
@@ -11890,16 +11991,16 @@ function getMcpWelcomeResponse(args2) {
11890
11991
  }
11891
11992
  return buildWelcomeGuide({ profileName: args2.profile, forLlm: true });
11892
11993
  }
11893
- var import_chalk12, fs14, 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;
11894
11995
  var init_welcome_flow = __esm({
11895
11996
  "src/lib/welcome-flow.ts"() {
11896
11997
  "use strict";
11897
11998
  import_chalk12 = __toESM(require_source());
11898
- fs14 = __toESM(require("fs"));
11999
+ fs15 = __toESM(require("fs"));
11899
12000
  http2 = __toESM(require("http"));
11900
12001
  https2 = __toESM(require("https"));
11901
- os7 = __toESM(require("os"));
11902
- path13 = __toESM(require("path"));
12002
+ os8 = __toESM(require("os"));
12003
+ path14 = __toESM(require("path"));
11903
12004
  readline = __toESM(require("readline"));
11904
12005
  init_pipeline();
11905
12006
  init_document_text();
@@ -11918,7 +12019,7 @@ var init_welcome_flow = __esm({
11918
12019
  { n: 3, key: "test", title: "test on a draft", hint: "optional \u2014 scan or rewrite a draft" },
11919
12020
  { n: 4, key: "signup", title: "save & unlock", hint: "signup syncs your profile and unlocks learning" }
11920
12021
  ];
11921
- STATE_FILE = path13.join(os7.homedir(), ".hyv", "welcome-state.json");
12022
+ STATE_FILE = path14.join(os8.homedir(), ".hyv", "welcome-state.json");
11922
12023
  }
11923
12024
  });
11924
12025
 
@@ -12906,15 +13007,15 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
12906
13007
  g.minimatch.escape = vi2.escape;
12907
13008
  g.minimatch.unescape = Ei2.unescape;
12908
13009
  });
12909
- var fs29 = R2((Wt2) => {
13010
+ var fs30 = R2((Wt2) => {
12910
13011
  "use strict";
12911
13012
  Object.defineProperty(Wt2, "__esModule", { value: true });
12912
13013
  Wt2.LRUCache = void 0;
12913
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) => {
12914
13015
  typeof ge2.emitWarning == "function" ? ge2.emitWarning(n7, t, e, s) : console.error(`[${e}] ${t}: ${n7}`);
12915
- }, Lt2 = globalThis.AbortController, os12 = globalThis.AbortSignal;
13016
+ }, Lt2 = globalThis.AbortController, os13 = globalThis.AbortSignal;
12916
13017
  if (typeof Lt2 > "u") {
12917
- os12 = class {
13018
+ os13 = class {
12918
13019
  onabort;
12919
13020
  _onabort = [];
12920
13021
  reason;
@@ -12926,7 +13027,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
12926
13027
  constructor() {
12927
13028
  t();
12928
13029
  }
12929
- signal = new os12();
13030
+ signal = new os13();
12930
13031
  abort(e) {
12931
13032
  if (!this.signal.aborted) {
12932
13033
  this.signal.reason = e, this.signal.aborted = true;
@@ -13875,7 +13976,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
13875
13976
  };
13876
13977
  Object.defineProperty(_2, "__esModule", { value: true });
13877
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;
13878
- var Qt2 = fs29(), Yt2 = require("node:path"), yr = require("node:url"), pt2 = require("fs"), Sr = br(require("node:fs")), vr = pt2.realpathSync.native, Ht2 = require("node:fs/promises"), bs2 = Oe2(), mt2 = { lstatSync: pt2.lstatSync, readdir: pt2.readdir, readdirSync: pt2.readdirSync, readlinkSync: pt2.readlinkSync, realpathSync: vr, promises: { lstat: Ht2.lstat, readdir: Ht2.readdir, readlink: Ht2.readlink, realpath: Ht2.realpath } }, _s2 = (n7) => !n7 || n7 === mt2 || n7 === Sr ? mt2 : { ...mt2, ...n7, promises: { ...mt2.promises, ...n7.promises || {} } }, Os2 = /^\\\\\?\\([a-z]:)\\?$/i, Er = (n7) => n7.replace(/\//g, "\\").replace(Os2, "$1\\"), _r = /[\\\/]/, N2 = 0, xs2 = 1, Ts2 = 2, G2 = 4, Cs2 = 6, Rs2 = 8, Q2 = 10, As2 = 12, j2 = 15, dt2 = ~j2, xe2 = 16, ys2 = 32, gt2 = 64, W2 = 128, Vt2 = 256, Xt2 = 512, Ss2 = gt2 | W2 | Xt2, Or = 1023, Te2 = (n7) => n7.isFile() ? Rs2 : n7.isDirectory() ? G2 : n7.isSymbolicLink() ? Q2 : n7.isCharacterDevice() ? Ts2 : n7.isBlockDevice() ? Cs2 : n7.isSocket() ? As2 : n7.isFIFO() ? xs2 : N2, vs2 = new Qt2.LRUCache({ max: 2 ** 12 }), wt2 = (n7) => {
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) => {
13879
13980
  let t = vs2.get(n7);
13880
13981
  if (t)
13881
13982
  return t;
@@ -15570,8 +15671,8 @@ init_version();
15570
15671
 
15571
15672
  // src/lib/queue-sync.ts
15572
15673
  var import_chalk6 = __toESM(require_source());
15573
- var fs6 = __toESM(require("fs"));
15574
- var path6 = __toESM(require("path"));
15674
+ var fs7 = __toESM(require("fs"));
15675
+ var path7 = __toESM(require("path"));
15575
15676
  init_config();
15576
15677
  init_auth();
15577
15678
  async function flushQueuedSignals() {
@@ -15580,13 +15681,13 @@ async function flushQueuedSignals() {
15580
15681
  return { sent: 0, failed: 0 };
15581
15682
  let sent = 0;
15582
15683
  let failed = 0;
15583
- if (!fs6.existsSync(QUEUE_DIR))
15684
+ if (!fs7.existsSync(QUEUE_DIR))
15584
15685
  return { sent: 0, failed: 0 };
15585
- const files = fs6.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
15686
+ const files = fs7.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
15586
15687
  for (const file of files) {
15587
- const filePath = path6.join(QUEUE_DIR, file);
15688
+ const filePath = path7.join(QUEUE_DIR, file);
15588
15689
  try {
15589
- const signal = JSON.parse(fs6.readFileSync(filePath, "utf-8"));
15690
+ const signal = JSON.parse(fs7.readFileSync(filePath, "utf-8"));
15590
15691
  let ok = false;
15591
15692
  if (signal.type === "reinforce") {
15592
15693
  const res = await authenticatedRequest(cliApiUrl("/cli/learning/reinforce"), {
@@ -15607,7 +15708,7 @@ async function flushQueuedSignals() {
15607
15708
  ok = res.status === 200;
15608
15709
  }
15609
15710
  if (ok) {
15610
- fs6.unlinkSync(filePath);
15711
+ fs7.unlinkSync(filePath);
15611
15712
  sent++;
15612
15713
  } else {
15613
15714
  failed++;
@@ -15764,8 +15865,9 @@ init_config();
15764
15865
  init_auth();
15765
15866
  init_access();
15766
15867
  init_profile();
15767
- var fs7 = __toESM(require("fs"));
15768
- var path7 = __toESM(require("path"));
15868
+ init_profile_meta();
15869
+ var fs8 = __toESM(require("fs"));
15870
+ var path8 = __toESM(require("path"));
15769
15871
  function registerSyncCommand(program3) {
15770
15872
  program3.command("sync").description("Sync profiles and rules from server").option("--force", "Force re-download even if cache is fresh").action(async (options) => {
15771
15873
  try {
@@ -15783,27 +15885,40 @@ function registerSyncCommand(program3) {
15783
15885
  const data = response.data;
15784
15886
  ensureHyvDir();
15785
15887
  let profileCount = 0;
15888
+ let skippedNewer = 0;
15786
15889
  for (const profile of data.profiles) {
15787
15890
  if (!profile.content?.trim()) {
15788
15891
  console.log(import_chalk8.default.yellow(` ! skipped empty profile cache: ${profile.name}`));
15789
15892
  continue;
15790
15893
  }
15791
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
+ }
15792
15900
  writeCachedProfile(cacheKey, profile.content);
15901
+ setProfileMeta(cacheKey, { server_updated_at: profile.updated_at });
15793
15902
  profileCount++;
15794
15903
  }
15795
- const rulesPath = path7.join(CACHE_DIR, "rules.md");
15796
- fs7.writeFileSync(rulesPath, data.rules, { mode: 384 });
15797
- const promptPath = path7.join(CACHE_DIR, "prompt-template.md");
15798
- fs7.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 });
15799
15908
  if (data.detection_rules) {
15800
- const rulesJson = path7.join(CACHE_DIR, "detection-rules.json");
15801
- fs7.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 });
15802
15911
  }
15803
15912
  for (const profile of data.profiles) {
15804
15913
  try {
15805
15914
  const cacheKey = toSafeProfileCacheKey(profile.slug || profile.name);
15806
- 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
+ });
15807
15922
  } catch {
15808
15923
  }
15809
15924
  }
@@ -15814,8 +15929,8 @@ function registerSyncCommand(program3) {
15814
15929
  if (queueResult.failed > 0) {
15815
15930
  console.log(import_chalk8.default.yellow(` ! ${queueResult.failed} queued signal(s) could not send`));
15816
15931
  }
15817
- const metaPath = path7.join(CACHE_DIR, "sync-meta.json");
15818
- fs7.writeFileSync(metaPath, JSON.stringify({
15932
+ const metaPath = path8.join(CACHE_DIR2, "sync-meta.json");
15933
+ fs8.writeFileSync(metaPath, JSON.stringify({
15819
15934
  synced_at: data.synced_at,
15820
15935
  profile_count: profileCount,
15821
15936
  plan: data.plan
@@ -15823,6 +15938,9 @@ function registerSyncCommand(program3) {
15823
15938
  console.log(import_chalk8.default.green(`
15824
15939
  \u2713 Synced successfully!`));
15825
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
+ }
15826
15944
  console.log(import_chalk8.default.dim(` Rules: cached`));
15827
15945
  console.log(import_chalk8.default.dim(` Prompt template: cached`));
15828
15946
  console.log(import_chalk8.default.dim(` Plan: ${data.plan}`));
@@ -15920,21 +16038,21 @@ Profile evolution: ${slug}
15920
16038
 
15921
16039
  // src/commands/rewrite.ts
15922
16040
  var import_chalk9 = __toESM(require_source());
15923
- var fs12 = __toESM(require("fs"));
15924
- var path11 = __toESM(require("path"));
16041
+ var fs13 = __toESM(require("fs"));
16042
+ var path12 = __toESM(require("path"));
15925
16043
  init_pipeline();
15926
16044
 
15927
16045
  // src/lib/prompt.ts
15928
- var fs11 = __toESM(require("fs"));
15929
- var path10 = __toESM(require("path"));
16046
+ var fs12 = __toESM(require("fs"));
16047
+ var path11 = __toESM(require("path"));
15930
16048
  init_config();
15931
16049
  init_signals();
15932
16050
  init_profile_parse();
15933
16051
  function loadPromptTemplate() {
15934
- const templatePath = path10.join(CACHE_DIR, "prompt-template.md");
16052
+ const templatePath = path11.join(CACHE_DIR2, "prompt-template.md");
15935
16053
  try {
15936
- if (fs11.existsSync(templatePath)) {
15937
- return fs11.readFileSync(templatePath, "utf-8");
16054
+ if (fs12.existsSync(templatePath)) {
16055
+ return fs12.readFileSync(templatePath, "utf-8");
15938
16056
  }
15939
16057
  } catch {
15940
16058
  }
@@ -16121,8 +16239,8 @@ Draft: ${draftPath}`));
16121
16239
  ${result.stats.autoFixed} issues can be auto-fixed \u2014 run: hyv fix ${file}`));
16122
16240
  }
16123
16241
  if (options.output) {
16124
- const outputPath = path11.resolve(options.output);
16125
- fs12.writeFileSync(outputPath, promptResult.prompt);
16242
+ const outputPath = path12.resolve(options.output);
16243
+ fs13.writeFileSync(outputPath, promptResult.prompt);
16126
16244
  console.log(import_chalk9.default.green(`
16127
16245
  \u2713 Prompt written to ${outputPath}`));
16128
16246
  } else {
@@ -16142,8 +16260,8 @@ Draft: ${draftPath}`));
16142
16260
 
16143
16261
  // src/commands/learning.ts
16144
16262
  var import_chalk10 = __toESM(require_source());
16145
- var fs13 = __toESM(require("fs"));
16146
- var path12 = __toESM(require("path"));
16263
+ var fs14 = __toESM(require("fs"));
16264
+ var path13 = __toESM(require("path"));
16147
16265
 
16148
16266
  // src/lib/diff.ts
16149
16267
  init_scan();
@@ -16287,18 +16405,18 @@ function registerLearningCommands(program3) {
16287
16405
  console.error(import_chalk10.default.red("Provide <original> <edited> or use --last"));
16288
16406
  process.exit(1);
16289
16407
  }
16290
- origPath = path12.resolve(original);
16291
- editPath = path12.resolve(edited);
16292
- if (!fs13.existsSync(origPath)) {
16408
+ origPath = path13.resolve(original);
16409
+ editPath = path13.resolve(edited);
16410
+ if (!fs14.existsSync(origPath)) {
16293
16411
  console.error(import_chalk10.default.red(`Original file not found: ${origPath}`));
16294
16412
  process.exit(1);
16295
16413
  }
16296
- if (!fs13.existsSync(editPath)) {
16414
+ if (!fs14.existsSync(editPath)) {
16297
16415
  console.error(import_chalk10.default.red(`Edited file not found: ${editPath}`));
16298
16416
  process.exit(1);
16299
16417
  }
16300
- origText = fs13.readFileSync(origPath, "utf-8");
16301
- editText = fs13.readFileSync(editPath, "utf-8");
16418
+ origText = fs14.readFileSync(origPath, "utf-8");
16419
+ editText = fs14.readFileSync(editPath, "utf-8");
16302
16420
  saveLastEditSession({
16303
16421
  original_path: origPath,
16304
16422
  edited_path: editPath,
@@ -16465,8 +16583,8 @@ function printProfileImpact(impact, data) {
16465
16583
 
16466
16584
  // src/commands/onboarding.ts
16467
16585
  var import_chalk13 = __toESM(require_source());
16468
- var fs15 = __toESM(require("fs"));
16469
- var path14 = __toESM(require("path"));
16586
+ var fs16 = __toESM(require("fs"));
16587
+ var path15 = __toESM(require("path"));
16470
16588
  init_config();
16471
16589
  init_auth();
16472
16590
  init_scan();
@@ -16617,24 +16735,24 @@ Industry: ${industry}
16617
16735
  }
16618
16736
  }
16619
16737
  async function createFromSamples(name, sampleDir, token) {
16620
- const dirPath = path14.resolve(sampleDir);
16621
- if (!fs15.existsSync(dirPath)) {
16738
+ const dirPath = path15.resolve(sampleDir);
16739
+ if (!fs16.existsSync(dirPath)) {
16622
16740
  throw new Error(`Directory not found: ${dirPath}`);
16623
16741
  }
16624
16742
  console.log(import_chalk13.default.bold(`
16625
16743
  Creating voice profile: ${name}`));
16626
16744
  console.log(import_chalk13.default.dim(`Reading samples from: ${dirPath}
16627
16745
  `));
16628
- const files = fs15.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));
16629
16747
  if (files.length === 0) {
16630
16748
  throw new Error("No .md or .txt files found in directory");
16631
16749
  }
16632
16750
  const samples = [];
16633
16751
  for (const file of files) {
16634
- const text = fs15.readFileSync(file, "utf-8");
16752
+ const text = fs16.readFileSync(file, "utf-8");
16635
16753
  if (text.trim().length > 0) {
16636
16754
  samples.push({ path: file, text });
16637
- 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)`));
16638
16756
  }
16639
16757
  }
16640
16758
  if (samples.length === 0) {
@@ -16705,12 +16823,12 @@ function registerImportCommand(program3) {
16705
16823
  program3.command("import").description("Import a voice profile from file").argument("<name>", "Profile name").argument("<file>", "Profile markdown file").action(async (name, file) => {
16706
16824
  try {
16707
16825
  assertSafeProfileName(name);
16708
- const filePath = path14.resolve(file);
16709
- if (!fs15.existsSync(filePath)) {
16826
+ const filePath = path15.resolve(file);
16827
+ if (!fs16.existsSync(filePath)) {
16710
16828
  console.error(import_chalk13.default.red(`File not found: ${filePath}`));
16711
16829
  process.exit(1);
16712
16830
  }
16713
- const content = fs15.readFileSync(filePath, "utf-8");
16831
+ const content = fs16.readFileSync(filePath, "utf-8");
16714
16832
  ensureHyvDir();
16715
16833
  writeCachedProfile(name, content);
16716
16834
  console.log(import_chalk13.default.green(`
@@ -17081,11 +17199,11 @@ async function runHybridAnalysis(text, profile, opts = {}) {
17081
17199
 
17082
17200
  // src/commands/history.ts
17083
17201
  var import_chalk16 = __toESM(require_source());
17084
- var fs16 = __toESM(require("fs"));
17085
- var path15 = __toESM(require("path"));
17202
+ var fs17 = __toESM(require("fs"));
17203
+ var path16 = __toESM(require("path"));
17086
17204
  init_config();
17087
- var HISTORY_DIR = path15.join(HYV_DIR, "history");
17088
- 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");
17089
17207
  function logScan(entry) {
17090
17208
  try {
17091
17209
  ensureHyvDir();
@@ -17095,9 +17213,9 @@ function logScan(entry) {
17095
17213
  }
17096
17214
  function readHistory() {
17097
17215
  try {
17098
- if (!fs16.existsSync(HISTORY_FILE))
17216
+ if (!fs17.existsSync(HISTORY_FILE))
17099
17217
  return [];
17100
- const lines = fs16.readFileSync(HISTORY_FILE, "utf-8").trim().split("\n").filter(Boolean);
17218
+ const lines = fs17.readFileSync(HISTORY_FILE, "utf-8").trim().split("\n").filter(Boolean);
17101
17219
  return lines.map((l) => JSON.parse(l));
17102
17220
  } catch {
17103
17221
  return [];
@@ -17129,8 +17247,8 @@ function registerHistoryCommand(program3) {
17129
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) => {
17130
17248
  try {
17131
17249
  if (options.clear) {
17132
- if (fs16.existsSync(HISTORY_FILE)) {
17133
- fs16.unlinkSync(HISTORY_FILE);
17250
+ if (fs17.existsSync(HISTORY_FILE)) {
17251
+ fs17.unlinkSync(HISTORY_FILE);
17134
17252
  }
17135
17253
  console.log(import_chalk16.default.green("\n\u2713 History cleared"));
17136
17254
  return;
@@ -17188,7 +17306,7 @@ function registerHistoryCommand(program3) {
17188
17306
  const icon = entry.issues === 0 ? import_chalk16.default.green("\u2713") : import_chalk16.default.red("\u25CF");
17189
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`);
17190
17308
  const issues = entry.issues > 0 ? import_chalk16.default.red(`${entry.issues} issues`) : import_chalk16.default.dim("clean");
17191
- const file = path15.basename(entry.file).slice(0, 25).padEnd(25);
17309
+ const file = path16.basename(entry.file).slice(0, 25).padEnd(25);
17192
17310
  console.log(` ${import_chalk16.default.dim(time)} ${icon} ${file} ${score} ${issues}`);
17193
17311
  }
17194
17312
  console.log("");
@@ -17296,9 +17414,9 @@ hyv scan ${filePath}`));
17296
17414
 
17297
17415
  // src/commands/doctor.ts
17298
17416
  var import_chalk18 = __toESM(require_source());
17299
- var fs18 = __toESM(require("fs"));
17300
- var path17 = __toESM(require("path"));
17301
- var os8 = __toESM(require("os"));
17417
+ var fs19 = __toESM(require("fs"));
17418
+ var path18 = __toESM(require("path"));
17419
+ var os9 = __toESM(require("os"));
17302
17420
  init_config();
17303
17421
  init_auth();
17304
17422
  init_access();
@@ -17309,15 +17427,15 @@ init_version();
17309
17427
  var import_child_process3 = require("child_process");
17310
17428
 
17311
17429
  // src/lib/cli-entry.ts
17312
- var fs17 = __toESM(require("fs"));
17313
- var path16 = __toESM(require("path"));
17430
+ var fs18 = __toESM(require("fs"));
17431
+ var path17 = __toESM(require("path"));
17314
17432
  function resolveCliEntry() {
17315
17433
  const candidates = [
17316
- path16.resolve(process.argv[1] || ""),
17317
- path16.resolve(__dirname, "index.js"),
17318
- 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")
17319
17437
  ];
17320
- return candidates.find((p) => p && fs17.existsSync(p)) || null;
17438
+ return candidates.find((p) => p && fs18.existsSync(p)) || null;
17321
17439
  }
17322
17440
  function mcpServerCommand() {
17323
17441
  const entry = resolveCliEntry();
@@ -17403,20 +17521,20 @@ async function testMcpStdioSubprocess() {
17403
17521
  }
17404
17522
 
17405
17523
  // src/commands/doctor.ts
17406
- var HOME = os8.homedir();
17524
+ var HOME = os9.homedir();
17407
17525
  var IS_WIN = process.platform === "win32";
17408
17526
  function claudeDesktopDir() {
17409
17527
  if (IS_WIN)
17410
- return path17.join(HOME, "AppData", "Roaming", "Claude");
17528
+ return path18.join(HOME, "AppData", "Roaming", "Claude");
17411
17529
  if (process.platform === "linux")
17412
- return path17.join(HOME, ".config", "Claude");
17413
- return path17.join(HOME, "Library", "Application Support", "Claude");
17530
+ return path18.join(HOME, ".config", "Claude");
17531
+ return path18.join(HOME, "Library", "Application Support", "Claude");
17414
17532
  }
17415
17533
  function isOwnerOnlyFile(filePath) {
17416
17534
  try {
17417
- if (!fs18.existsSync(filePath))
17535
+ if (!fs19.existsSync(filePath))
17418
17536
  return true;
17419
- const mode = fs18.statSync(filePath).mode & 511;
17537
+ const mode = fs19.statSync(filePath).mode & 511;
17420
17538
  return (mode & 63) === 0;
17421
17539
  } catch {
17422
17540
  return false;
@@ -17424,9 +17542,9 @@ function isOwnerOnlyFile(filePath) {
17424
17542
  }
17425
17543
  function readMcpHyv(configFile) {
17426
17544
  try {
17427
- if (!fs18.existsSync(configFile))
17545
+ if (!fs19.existsSync(configFile))
17428
17546
  return false;
17429
- const cfg = JSON.parse(fs18.readFileSync(configFile, "utf-8"));
17547
+ const cfg = JSON.parse(fs19.readFileSync(configFile, "utf-8"));
17430
17548
  return Boolean(cfg.mcpServers?.hyv);
17431
17549
  } catch {
17432
17550
  return false;
@@ -17447,28 +17565,28 @@ function registerDoctorCommand(program3) {
17447
17565
  console.log(import_chalk18.default.dim("tip: run hyv welcome for free capabilities\n"));
17448
17566
  console.log(import_chalk18.default.dim("checking cli installation..."));
17449
17567
  const cliPath = process.argv[1];
17450
- if (cliPath && fs18.existsSync(cliPath)) {
17568
+ if (cliPath && fs19.existsSync(cliPath)) {
17451
17569
  console.log(import_chalk18.default.green(" \u2713 cli installed"));
17452
17570
  } else {
17453
17571
  console.log(import_chalk18.default.red(" \u2717 cli not found"));
17454
17572
  issues++;
17455
17573
  }
17456
17574
  console.log(import_chalk18.default.dim("checking .hyv directory..."));
17457
- if (fs18.existsSync(HYV_DIR)) {
17575
+ if (fs19.existsSync(HYV_DIR)) {
17458
17576
  console.log(import_chalk18.default.green(" \u2713 .hyv directory exists"));
17459
17577
  } else {
17460
17578
  console.log(import_chalk18.default.yellow(" ! .hyv directory missing \u2014 creating..."));
17461
- fs18.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
17579
+ fs19.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
17462
17580
  fixed++;
17463
17581
  }
17464
17582
  console.log(import_chalk18.default.dim("checking cache..."));
17465
17583
  const diskProfiles = listDiskCachedProfiles();
17466
- const syncMeta = path17.join(CACHE_DIR, "sync-meta.json");
17467
- if (diskProfiles.length > 0 || fs18.existsSync(syncMeta)) {
17584
+ const syncMeta = path18.join(CACHE_DIR2, "sync-meta.json");
17585
+ if (diskProfiles.length > 0 || fs19.existsSync(syncMeta)) {
17468
17586
  console.log(import_chalk18.default.green(` \u2713 profile cache (${diskProfiles.length} full profile(s))`));
17469
- if (fs18.existsSync(syncMeta)) {
17587
+ if (fs19.existsSync(syncMeta)) {
17470
17588
  try {
17471
- const meta = JSON.parse(fs18.readFileSync(syncMeta, "utf-8"));
17589
+ const meta = JSON.parse(fs19.readFileSync(syncMeta, "utf-8"));
17472
17590
  console.log(import_chalk18.default.dim(` last sync: ${meta.synced_at || "unknown"}`));
17473
17591
  } catch {
17474
17592
  }
@@ -17477,8 +17595,8 @@ function registerDoctorCommand(program3) {
17477
17595
  console.log(import_chalk18.default.dim(" - no full profile cache (free local engine still works)"));
17478
17596
  }
17479
17597
  console.log(import_chalk18.default.dim("checking file permissions..."));
17480
- if (fs18.existsSync(HYV_DIR)) {
17481
- const hyvMode = fs18.statSync(HYV_DIR).mode & 511;
17598
+ if (fs19.existsSync(HYV_DIR)) {
17599
+ const hyvMode = fs19.statSync(HYV_DIR).mode & 511;
17482
17600
  if ((hyvMode & 63) === 0) {
17483
17601
  console.log(import_chalk18.default.green(" \u2713 .hyv directory permissions"));
17484
17602
  } else {
@@ -17487,7 +17605,7 @@ function registerDoctorCommand(program3) {
17487
17605
  issues++;
17488
17606
  }
17489
17607
  }
17490
- if (fs18.existsSync(AUTH_FILE)) {
17608
+ if (fs19.existsSync(AUTH_FILE)) {
17491
17609
  if (isOwnerOnlyFile(AUTH_FILE)) {
17492
17610
  console.log(import_chalk18.default.green(" \u2713 auth.json permissions"));
17493
17611
  } else {
@@ -17520,9 +17638,9 @@ function registerDoctorCommand(program3) {
17520
17638
  console.log(import_chalk18.default.dim(" run: hyv init for profiles + learning"));
17521
17639
  }
17522
17640
  console.log(import_chalk18.default.dim("checking voice profile..."));
17523
- const voiceMd = path17.join(HYV_DIR, "voice.md");
17524
- const hasVoiceMd = fs18.existsSync(voiceMd) && fs18.readFileSync(voiceMd, "utf-8").trim().length > 50;
17525
- const profileFiles = fs18.existsSync(PROFILES_DIR) ? fs18.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md") && f !== "voice.md") : [];
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") : [];
17526
17644
  if (hasVoiceMd || profileFiles.length > 0 || diskProfiles.length > 0) {
17527
17645
  if (hasVoiceMd)
17528
17646
  console.log(import_chalk18.default.green(" \u2713 voice.md exists"));
@@ -17535,21 +17653,21 @@ function registerDoctorCommand(program3) {
17535
17653
  console.log(import_chalk18.default.dim(" run: hyv new <name> or hyv init"));
17536
17654
  }
17537
17655
  console.log(import_chalk18.default.dim("checking agent configurations..."));
17538
- const cursorLegacyRule = path17.join(HOME, ".cursor", "rules", "hyv.md");
17539
- const cursorRule = path17.join(HOME, ".cursor", "rules", "hyv.mdc");
17540
- if (fs18.existsSync(cursorLegacyRule) && fs18.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)) {
17541
17659
  console.log(import_chalk18.default.yellow(" ! stale cursor rule ~/.cursor/rules/hyv.md (use hyv.mdc)"));
17542
17660
  console.log(import_chalk18.default.dim(" run: rm ~/.cursor/rules/hyv.md (or hyv doctor --fix-agents)"));
17543
17661
  issues++;
17544
17662
  }
17545
17663
  const agentChecks = [
17546
- { name: "claude desktop mcp", ok: readMcpHyv(path17.join(claudeDesktopDir(), "claude_desktop_config.json")) },
17547
- { name: "cursor mcp", ok: readMcpHyv(path17.join(HOME, ".cursor", "mcp.json")) },
17548
- { name: "cursor rule", ok: fs18.existsSync(cursorRule) },
17549
- { name: "claude code command", ok: fs18.existsSync(path17.join(HOME, ".claude", "commands", "hyv.md")) },
17550
- { name: "claude code skill", ok: fs18.existsSync(path17.join(HOME, ".claude", "skills", "hold-your-voice", "SKILL.md")) },
17551
- { name: "codex agents", ok: fs18.existsSync(path17.join(HOME, ".codex", "AGENTS.md")) && fs18.readFileSync(path17.join(HOME, ".codex", "AGENTS.md"), "utf-8").includes("hyv") },
17552
- { name: "command code skill", ok: fs18.existsSync(path17.join(HOME, ".commandcode", "skills", "hyv", "SKILL.md")) }
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")) }
17553
17671
  ];
17554
17672
  for (const agent of agentChecks) {
17555
17673
  if (agent.ok) {
@@ -17560,8 +17678,8 @@ function registerDoctorCommand(program3) {
17560
17678
  }
17561
17679
  if (opts.fixAgents) {
17562
17680
  try {
17563
- const pkgDir = path17.resolve(__dirname, "..");
17564
- 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"));
17565
17683
  const result = setupAgents({ pkgDir, quiet: true });
17566
17684
  console.log(import_chalk18.default.green(` \u2713 re-ran agent setup (${result.configured.join(", ") || "no changes"})`));
17567
17685
  if (result.warnings.length) {
@@ -17573,8 +17691,8 @@ function registerDoctorCommand(program3) {
17573
17691
  }
17574
17692
  }
17575
17693
  console.log(import_chalk18.default.dim("checking mcp server..."));
17576
- const claudeMcp = readMcpHyv(path17.join(claudeDesktopDir(), "claude_desktop_config.json"));
17577
- 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"));
17578
17696
  if (claudeMcp || cursorMcp) {
17579
17697
  if (claudeMcp)
17580
17698
  console.log(import_chalk18.default.green(" \u2713 mcp configured for claude desktop"));
@@ -17683,8 +17801,8 @@ init_access();
17683
17801
  init_config();
17684
17802
 
17685
17803
  // src/lib/destructive-write.ts
17686
- var fs19 = __toESM(require("fs"));
17687
- var path18 = __toESM(require("path"));
17804
+ var fs20 = __toESM(require("fs"));
17805
+ var path19 = __toESM(require("path"));
17688
17806
  var readline2 = __toESM(require("readline"));
17689
17807
  var import_chalk20 = __toESM(require_source());
17690
17808
  async function confirmDestructiveWrite(options) {
@@ -17710,12 +17828,12 @@ A .bak backup will be created. Proceed? [y/N] `;
17710
17828
  }
17711
17829
  function writeInPlaceWithBackup(filePath, content) {
17712
17830
  const backupPath = filePath + ".bak";
17713
- fs19.copyFileSync(filePath, backupPath);
17714
- fs19.writeFileSync(filePath, content);
17831
+ fs20.copyFileSync(filePath, backupPath);
17832
+ fs20.writeFileSync(filePath, content);
17715
17833
  return backupPath;
17716
17834
  }
17717
17835
  function backupBasename(filePath) {
17718
- return path18.basename(filePath) + ".bak";
17836
+ return path19.basename(filePath) + ".bak";
17719
17837
  }
17720
17838
 
17721
17839
  // src/commands/fix.ts
@@ -17809,19 +17927,19 @@ function registerCheckCommand(program3) {
17809
17927
  const profile = await loadProfileForCommand(options.profile);
17810
17928
  let inputText = text;
17811
17929
  if (text === "-") {
17812
- const fs30 = require("fs");
17930
+ const fs31 = require("fs");
17813
17931
  if (process.stdin.isTTY) {
17814
17932
  console.error(import_chalk22.default.red("No input provided. Pipe content or pass text as argument."));
17815
17933
  process.exit(1);
17816
17934
  }
17817
- inputText = fs30.readFileSync(0, "utf-8");
17935
+ inputText = fs31.readFileSync(0, "utf-8");
17818
17936
  }
17819
17937
  if (!inputText.trim()) {
17820
17938
  console.error(import_chalk22.default.red("No text provided."));
17821
17939
  process.exit(1);
17822
17940
  }
17823
- const fs29 = require("fs");
17824
- if (text !== "-" && fs29.existsSync(text)) {
17941
+ const fs30 = require("fs");
17942
+ if (text !== "-" && fs30.existsSync(text)) {
17825
17943
  console.log(import_chalk22.default.yellow(`"${text}" looks like a file. Did you mean: hyv scan ${text}`));
17826
17944
  process.exit(1);
17827
17945
  }
@@ -17948,12 +18066,12 @@ function registerDiffCommand(program3) {
17948
18066
  ${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} available`));
17949
18067
  console.log(import_chalk24.default.dim(` run: hyv fix ${file} -i to apply`));
17950
18068
  if (options.apply && filePath !== "stdin") {
17951
- const fs29 = require("fs");
17952
- const path26 = require("path");
18069
+ const fs30 = require("fs");
18070
+ const path27 = require("path");
17953
18071
  const backupPath = filePath + ".bak";
17954
- fs29.copyFileSync(filePath, backupPath);
17955
- fs29.writeFileSync(filePath, result.fixed);
17956
- 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)}`));
17957
18075
  }
17958
18076
  } catch (error) {
17959
18077
  console.error(import_chalk24.default.red(`Error: ${error.message}`));
@@ -18128,8 +18246,8 @@ function registerRulesCommand(program3) {
18128
18246
 
18129
18247
  // src/commands/batch.ts
18130
18248
  var import_chalk26 = __toESM(require_source());
18131
- var fs20 = __toESM(require("fs"));
18132
- var path19 = __toESM(require("path"));
18249
+ var fs21 = __toESM(require("fs"));
18250
+ var path20 = __toESM(require("path"));
18133
18251
  init_pipeline();
18134
18252
  init_local_profile();
18135
18253
  function registerBatchCommand(program3) {
@@ -18162,10 +18280,10 @@ No files matching: ${pattern}`));
18162
18280
  }
18163
18281
  const results = [];
18164
18282
  for (const file of files) {
18165
- const absPath = path19.resolve(file);
18166
- if (!fs20.existsSync(absPath))
18283
+ const absPath = path20.resolve(file);
18284
+ if (!fs21.existsSync(absPath))
18167
18285
  continue;
18168
- const text = fs20.readFileSync(absPath, "utf-8");
18286
+ const text = fs21.readFileSync(absPath, "utf-8");
18169
18287
  const result = runPipeline(text, profile, options.fix || false);
18170
18288
  results.push({
18171
18289
  file,
@@ -18231,16 +18349,16 @@ No files matching: ${pattern}`));
18231
18349
 
18232
18350
  // src/commands/watch.ts
18233
18351
  var import_chalk27 = __toESM(require_source());
18234
- var fs21 = __toESM(require("fs"));
18235
- var path20 = __toESM(require("path"));
18352
+ var fs22 = __toESM(require("fs"));
18353
+ var path21 = __toESM(require("path"));
18236
18354
  init_pipeline();
18237
18355
  init_local_profile();
18238
18356
  function registerWatchCommand(program3) {
18239
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) => {
18240
18358
  try {
18241
18359
  const profile = await loadProfileForCommand(options.profile);
18242
- const absPath = path20.resolve(file);
18243
- if (!fs21.existsSync(absPath)) {
18360
+ const absPath = path21.resolve(file);
18361
+ if (!fs22.existsSync(absPath)) {
18244
18362
  console.error(import_chalk27.default.red(`File not found: ${absPath}`));
18245
18363
  process.exit(1);
18246
18364
  }
@@ -18263,7 +18381,7 @@ watching ${absPath}`));
18263
18381
  let lastScore = 0;
18264
18382
  let ignoreWatchUntil = 0;
18265
18383
  const runScan = () => {
18266
- const text = fs21.readFileSync(absPath, "utf-8");
18384
+ const text = fs22.readFileSync(absPath, "utf-8");
18267
18385
  const result = runPipeline(text, profile, options.command === "fix");
18268
18386
  scanCount++;
18269
18387
  const now = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
@@ -18305,7 +18423,7 @@ watching ${absPath}`));
18305
18423
  console.log("");
18306
18424
  };
18307
18425
  runScan();
18308
- fs21.watch(absPath, (eventType) => {
18426
+ fs22.watch(absPath, (eventType) => {
18309
18427
  if (eventType !== "change")
18310
18428
  return;
18311
18429
  if (Date.now() < ignoreWatchUntil)
@@ -18330,7 +18448,7 @@ watching ${absPath}`));
18330
18448
 
18331
18449
  // src/commands/demo.ts
18332
18450
  var import_chalk28 = __toESM(require_source());
18333
- var fs22 = __toESM(require("fs"));
18451
+ var fs23 = __toESM(require("fs"));
18334
18452
  var SAMPLES = {
18335
18453
  linkedin: {
18336
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.
@@ -18425,7 +18543,7 @@ function registerDemoCommand(program3) {
18425
18543
  }
18426
18544
  if (options.output) {
18427
18545
  const outputPath = require("path").resolve(options.output);
18428
- fs22.writeFileSync(outputPath, sample.text);
18546
+ fs23.writeFileSync(outputPath, sample.text);
18429
18547
  console.log(import_chalk28.default.green(`
18430
18548
  \u2713 Sample written to ${outputPath}`));
18431
18549
  if (sample.patterns.length > 0) {
@@ -18492,22 +18610,22 @@ function registerOpenCommand(program3) {
18492
18610
  init_welcome();
18493
18611
 
18494
18612
  // src/lib/onboarding.ts
18495
- var fs23 = __toESM(require("fs"));
18496
- var path21 = __toESM(require("path"));
18497
- var os9 = __toESM(require("os"));
18498
- var hyvDir = path21.join(os9.homedir(), ".hyv");
18499
- 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");
18500
18618
  function hasCompletedOnboarding() {
18501
18619
  try {
18502
- return fs23.existsSync(onboardingFile);
18620
+ return fs24.existsSync(onboardingFile);
18503
18621
  } catch {
18504
18622
  return false;
18505
18623
  }
18506
18624
  }
18507
18625
  function markOnboardingComplete(version) {
18508
- if (!fs23.existsSync(hyvDir))
18509
- fs23.mkdirSync(hyvDir, { recursive: true });
18510
- fs23.writeFileSync(
18626
+ if (!fs24.existsSync(hyvDir))
18627
+ fs24.mkdirSync(hyvDir, { recursive: true });
18628
+ fs24.writeFileSync(
18511
18629
  onboardingFile,
18512
18630
  JSON.stringify({ version, completed_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
18513
18631
  );
@@ -18515,13 +18633,13 @@ function markOnboardingComplete(version) {
18515
18633
 
18516
18634
  // src/commands/welcome.ts
18517
18635
  init_welcome_flow();
18518
- var fs24 = __toESM(require("fs"));
18519
- var path22 = __toESM(require("path"));
18636
+ var fs25 = __toESM(require("fs"));
18637
+ var path23 = __toESM(require("path"));
18520
18638
  function registerWelcomeCommand(program3) {
18521
18639
  const pkgVersion3 = (() => {
18522
18640
  try {
18523
- const pkgPath3 = path22.resolve(__dirname, "..", "package.json");
18524
- return JSON.parse(fs24.readFileSync(pkgPath3, "utf-8")).version;
18641
+ const pkgPath3 = path23.resolve(__dirname, "..", "package.json");
18642
+ return JSON.parse(fs25.readFileSync(pkgPath3, "utf-8")).version;
18525
18643
  } catch {
18526
18644
  return "0.0.0";
18527
18645
  }
@@ -18702,9 +18820,9 @@ Current: ${getEngineLabel()}
18702
18820
  }
18703
18821
 
18704
18822
  // src/mcp.ts
18705
- var fs25 = __toESM(require("fs"));
18706
- var path23 = __toESM(require("path"));
18707
- var os10 = __toESM(require("os"));
18823
+ var fs26 = __toESM(require("fs"));
18824
+ var path24 = __toESM(require("path"));
18825
+ var os11 = __toESM(require("os"));
18708
18826
  init_classifier();
18709
18827
  init_autofix();
18710
18828
  init_validator();
@@ -18715,20 +18833,20 @@ init_welcome();
18715
18833
  init_config();
18716
18834
  init_telemetry();
18717
18835
  init_access();
18718
- var VOICE_MD = path23.join(os10.homedir(), ".hyv", "voice.md");
18836
+ var VOICE_MD = path24.join(os11.homedir(), ".hyv", "voice.md");
18719
18837
  var MAX_RESPONSE_CHARS = 12e4;
18720
18838
  var MAX_SCAN_FILE_BYTES = 2 * 1024 * 1024;
18721
18839
  function readScanFile(filePath) {
18722
- const cwdReal = fs25.realpathSync(process.cwd());
18723
- const resolved = fs25.realpathSync(path23.resolve(filePath));
18724
- 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) {
18725
18843
  throw new Error(`file must be in the current working directory (${cwdReal})`);
18726
18844
  }
18727
- const stat = fs25.statSync(resolved);
18845
+ const stat = fs26.statSync(resolved);
18728
18846
  if (stat.size > MAX_SCAN_FILE_BYTES) {
18729
18847
  throw new Error(`file too large (max ${MAX_SCAN_FILE_BYTES} bytes)`);
18730
18848
  }
18731
- return fs25.readFileSync(resolved, "utf-8");
18849
+ return fs26.readFileSync(resolved, "utf-8");
18732
18850
  }
18733
18851
  function toolResultPayload(result) {
18734
18852
  const isError = result.startsWith("Error:") || result.startsWith("Unknown tool:");
@@ -18737,10 +18855,10 @@ function toolResultPayload(result) {
18737
18855
  ...isError ? { isError: true } : {}
18738
18856
  };
18739
18857
  }
18740
- var pkgPath = path23.resolve(__dirname, "..", "package.json");
18858
+ var pkgPath = path24.resolve(__dirname, "..", "package.json");
18741
18859
  var pkgVersion = (() => {
18742
18860
  try {
18743
- return JSON.parse(fs25.readFileSync(pkgPath, "utf-8")).version;
18861
+ return JSON.parse(fs26.readFileSync(pkgPath, "utf-8")).version;
18744
18862
  } catch {
18745
18863
  return "2.7.1";
18746
18864
  }
@@ -19321,7 +19439,7 @@ async function handleRequest(line) {
19321
19439
  }
19322
19440
  async function startMcpServer() {
19323
19441
  const access = await getAccessState().catch(() => null);
19324
- if (fs25.existsSync(VOICE_MD)) {
19442
+ if (fs26.existsSync(VOICE_MD)) {
19325
19443
  mcpLog("info", `voice profile: ${VOICE_MD}`);
19326
19444
  } else {
19327
19445
  mcpLog("info", "free local engine ready \u2014 no voice profile yet");
@@ -19354,18 +19472,18 @@ async function startMcpServer() {
19354
19472
 
19355
19473
  // src/lib/mcp-setup.ts
19356
19474
  var import_chalk32 = __toESM(require_source());
19357
- var fs26 = __toESM(require("fs"));
19358
- var path24 = __toESM(require("path"));
19359
- var os11 = __toESM(require("os"));
19475
+ var fs27 = __toESM(require("fs"));
19476
+ var path25 = __toESM(require("path"));
19477
+ var os12 = __toESM(require("os"));
19360
19478
  init_version();
19361
- var HOME2 = os11.homedir();
19479
+ var HOME2 = os12.homedir();
19362
19480
  var IS_WIN2 = process.platform === "win32";
19363
19481
  function claudeDesktopConfigPath() {
19364
19482
  if (IS_WIN2)
19365
- return path24.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
19483
+ return path25.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
19366
19484
  if (process.platform === "linux")
19367
- return path24.join(HOME2, ".config", "Claude", "claude_desktop_config.json");
19368
- 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");
19369
19487
  }
19370
19488
  function printMcpSetup() {
19371
19489
  console.log(import_chalk32.default.bold("\nhold your voice \u2014 mcp setup\n"));
@@ -19447,8 +19565,8 @@ async function runMcpSelfTest() {
19447
19565
  console.log(import_chalk32.default.red(` \u2717 hyv_demo failed: ${e.message}`));
19448
19566
  failed++;
19449
19567
  }
19450
- const voiceMd = path24.join(HOME2, ".hyv", "voice.md");
19451
- if (fs26.existsSync(voiceMd)) {
19568
+ const voiceMd = path25.join(HOME2, ".hyv", "voice.md");
19569
+ if (fs27.existsSync(voiceMd)) {
19452
19570
  console.log(import_chalk32.default.green(" \u2713 voice profile found"));
19453
19571
  passed++;
19454
19572
  } else {
@@ -19488,7 +19606,7 @@ async function runMcpSelfTest() {
19488
19606
  }
19489
19607
 
19490
19608
  // src/commands/export.ts
19491
- var fs27 = __toESM(require("fs"));
19609
+ var fs28 = __toESM(require("fs"));
19492
19610
  init_api();
19493
19611
  var FORMATS = {
19494
19612
  claude: {
@@ -19575,7 +19693,7 @@ async function exportCommand(format, opts) {
19575
19693
  const prompt = fmt.wrap(detail.profile, detail.body);
19576
19694
  if (opts.output) {
19577
19695
  const outFile = opts.output.replace("{name}", profile.slug || profile.name);
19578
- fs27.writeFileSync(outFile, prompt);
19696
+ fs28.writeFileSync(outFile, prompt);
19579
19697
  results.push({ profile: profile.name, file: outFile });
19580
19698
  } else {
19581
19699
  results.push({ profile: profile.name, prompt });
@@ -19612,13 +19730,13 @@ async function exportCommand(format, opts) {
19612
19730
  // src/index.ts
19613
19731
  init_access();
19614
19732
  init_welcome();
19615
- var fs28 = __toESM(require("fs"));
19616
- var path25 = __toESM(require("path"));
19733
+ var fs29 = __toESM(require("fs"));
19734
+ var path26 = __toESM(require("path"));
19617
19735
  var program2 = new Command();
19618
- var pkgPath2 = path25.resolve(__dirname, "..", "package.json");
19736
+ var pkgPath2 = path26.resolve(__dirname, "..", "package.json");
19619
19737
  var pkgVersion2 = (() => {
19620
19738
  try {
19621
- return JSON.parse(fs28.readFileSync(pkgPath2, "utf-8")).version;
19739
+ return JSON.parse(fs29.readFileSync(pkgPath2, "utf-8")).version;
19622
19740
  } catch {
19623
19741
  return "2.7.1";
19624
19742
  }