@holdyourvoice/hyv 2.9.20 → 2.9.22

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 (2) hide show
  1. package/dist/index.js +197 -130
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4727,6 +4727,41 @@ var init_profile_meta = __esm({
4727
4727
  });
4728
4728
 
4729
4729
  // src/lib/config.ts
4730
+ var config_exports = {};
4731
+ __export(config_exports, {
4732
+ API_BASE: () => API_BASE,
4733
+ AUTH_FILE: () => AUTH_FILE,
4734
+ CACHE_DIR: () => CACHE_DIR2,
4735
+ CONFIG_FILE: () => CONFIG_FILE,
4736
+ HYV_DIR: () => HYV_DIR,
4737
+ LAST_SESSION_FILE: () => LAST_SESSION_FILE,
4738
+ PROFILES_DIR: () => PROFILES_DIR,
4739
+ QUEUE_DIR: () => QUEUE_DIR,
4740
+ appendSecureLine: () => appendSecureLine,
4741
+ assertSafeOAuthUrl: () => assertSafeOAuthUrl,
4742
+ assertSafeOpenUrl: () => assertSafeOpenUrl,
4743
+ assertSafeProfileName: () => assertSafeProfileName,
4744
+ clearAuth: () => clearAuth,
4745
+ clearQueuedSignals: () => clearQueuedSignals,
4746
+ cliApiUrl: () => cliApiUrl,
4747
+ ensureHyvDir: () => ensureHyvDir,
4748
+ getQueuedSignals: () => getQueuedSignals,
4749
+ getToken: () => getToken,
4750
+ isInitialized: () => isInitialized,
4751
+ listCachedProfiles: () => listCachedProfiles,
4752
+ profilePathForName: () => profilePathForName,
4753
+ queueSignal: () => queueSignal,
4754
+ readAuth: () => readAuth,
4755
+ readCachedProfile: () => readCachedProfile,
4756
+ readConfig: () => readConfig,
4757
+ readLastEditSession: () => readLastEditSession,
4758
+ saveLastEditSession: () => saveLastEditSession,
4759
+ toSafeProfileCacheKey: () => toSafeProfileCacheKey,
4760
+ writeAuth: () => writeAuth,
4761
+ writeCachedProfile: () => writeCachedProfile,
4762
+ writeConfig: () => writeConfig,
4763
+ writeSecureFile: () => writeSecureFile
4764
+ });
4730
4765
  function validateApiBase(raw) {
4731
4766
  const trimmed = raw.replace(/\/$/, "");
4732
4767
  let parsed;
@@ -4822,6 +4857,10 @@ function ensureHyvDir() {
4822
4857
  }
4823
4858
  }
4824
4859
  }
4860
+ function writeSecureFile(filePath, content) {
4861
+ ensureHyvDir();
4862
+ fs3.writeFileSync(filePath, content, { mode: 384 });
4863
+ }
4825
4864
  function appendSecureLine(filePath, line, dir) {
4826
4865
  if (dir) {
4827
4866
  if (!fs3.existsSync(dir))
@@ -4866,6 +4905,11 @@ function writeAuth(auth) {
4866
4905
  }
4867
4906
  fs3.writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), { mode: 384 });
4868
4907
  }
4908
+ function clearAuth() {
4909
+ if (fs3.existsSync(AUTH_FILE)) {
4910
+ fs3.unlinkSync(AUTH_FILE);
4911
+ }
4912
+ }
4869
4913
  function readConfig() {
4870
4914
  try {
4871
4915
  if (!fs3.existsSync(CONFIG_FILE))
@@ -4943,6 +4987,17 @@ function queueSignal(signal) {
4943
4987
  const filePath = path2.join(QUEUE_DIR, `${id}.json`);
4944
4988
  fs3.writeFileSync(filePath, JSON.stringify(signal, null, 2), { mode: 384 });
4945
4989
  }
4990
+ function clearQueuedSignals() {
4991
+ try {
4992
+ if (!fs3.existsSync(QUEUE_DIR))
4993
+ return;
4994
+ const files = fs3.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
4995
+ for (const f of files) {
4996
+ fs3.unlinkSync(path2.join(QUEUE_DIR, f));
4997
+ }
4998
+ } catch {
4999
+ }
5000
+ }
4946
5001
  function saveLastEditSession(session) {
4947
5002
  ensureHyvDir();
4948
5003
  const data = { ...session, saved_at: (/* @__PURE__ */ new Date()).toISOString() };
@@ -5236,14 +5291,14 @@ var require_open = __commonJS({
5236
5291
  }
5237
5292
  const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
5238
5293
  if (options.wait) {
5239
- return new Promise((resolve16, reject) => {
5294
+ return new Promise((resolve17, reject) => {
5240
5295
  subprocess.once("error", reject);
5241
5296
  subprocess.once("close", (exitCode) => {
5242
5297
  if (!options.allowNonzeroExitCode && exitCode > 0) {
5243
5298
  reject(new Error(`Exited with code ${exitCode}`));
5244
5299
  return;
5245
5300
  }
5246
- resolve16(subprocess);
5301
+ resolve17(subprocess);
5247
5302
  });
5248
5303
  });
5249
5304
  }
@@ -5406,7 +5461,7 @@ var init_free_paid = __esm({
5406
5461
  ];
5407
5462
  COMMUNITY_URL = "https://holdyourvoice.com/community";
5408
5463
  PRICING_URL = "https://holdyourvoice.com/#pricing";
5409
- DASHBOARD_BILLING_URL = "https://holdyourvoice.com/app/billing";
5464
+ DASHBOARD_BILLING_URL = "https://holdyourvoice.com/dashboard?tab=billing";
5410
5465
  }
5411
5466
  });
5412
5467
 
@@ -5459,16 +5514,16 @@ function compareSemver(a, b) {
5459
5514
  return 0;
5460
5515
  }
5461
5516
  function fetchLatestNpmVersion(timeoutMs = 8e3) {
5462
- return new Promise((resolve16) => {
5517
+ return new Promise((resolve17) => {
5463
5518
  (0, import_child_process.execFile)(
5464
5519
  "npm",
5465
5520
  ["view", "@holdyourvoice/hyv", "version"],
5466
5521
  { timeout: timeoutMs, encoding: "utf-8" },
5467
5522
  (err, stdout) => {
5468
5523
  if (err || !stdout?.trim())
5469
- resolve16(null);
5524
+ resolve17(null);
5470
5525
  else
5471
- resolve16(stdout.trim());
5526
+ resolve17(stdout.trim());
5472
5527
  }
5473
5528
  );
5474
5529
  });
@@ -5533,7 +5588,7 @@ function escapeHtml(value) {
5533
5588
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5534
5589
  }
5535
5590
  function request(url, options = {}) {
5536
- return new Promise((resolve16, reject) => {
5591
+ return new Promise((resolve17, reject) => {
5537
5592
  const urlObj = new URL(url);
5538
5593
  const isHttps = urlObj.protocol === "https:";
5539
5594
  const client = isHttps ? https : http;
@@ -5554,9 +5609,9 @@ function request(url, options = {}) {
5554
5609
  res.on("data", (chunk) => data += chunk);
5555
5610
  res.on("end", () => {
5556
5611
  try {
5557
- resolve16({ status: res.statusCode || 0, data: JSON.parse(data) });
5612
+ resolve17({ status: res.statusCode || 0, data: JSON.parse(data) });
5558
5613
  } catch {
5559
- resolve16({ status: res.statusCode || 0, data });
5614
+ resolve17({ status: res.statusCode || 0, data });
5560
5615
  }
5561
5616
  });
5562
5617
  });
@@ -5614,9 +5669,9 @@ async function authenticateWithLicense(licenseKey) {
5614
5669
  }
5615
5670
  async function authenticateWithBrowser() {
5616
5671
  const server = http.createServer();
5617
- const port = await new Promise((resolve16) => {
5672
+ const port = await new Promise((resolve17) => {
5618
5673
  server.listen(0, "127.0.0.1", () => {
5619
- resolve16(server.address().port);
5674
+ resolve17(server.address().port);
5620
5675
  });
5621
5676
  });
5622
5677
  const redirectUri = `http://127.0.0.1:${port}/callback`;
@@ -5635,11 +5690,11 @@ async function authenticateWithBrowser() {
5635
5690
  }
5636
5691
  console.log(import_chalk2.default.cyan("\nOpening browser for authentication..."));
5637
5692
  await (0, import_open.default)(assertSafeOAuthUrl(auth_url));
5638
- const authData = await new Promise((resolve16, reject) => {
5693
+ const authData = await new Promise((resolve17, reject) => {
5639
5694
  const timeout = setTimeout(() => {
5640
5695
  server.close();
5641
5696
  reject(new Error(
5642
- 'Authentication timeout \u2014 browser did not return to the local callback.\nThis usually means Windows Defender Firewall blocked the local server.\nFix: run in PowerShell as Admin:\n New-NetFirewallRule -DisplayName "hyv-cli" -Direction Inbound -LocalPort ' + port + " -Protocol TCP -Action Allow\nOr use the manual API key path: hyv init --api-key <key>"
5697
+ 'Authentication timeout \u2014 browser did not return to the local callback.\nThis usually means Windows Defender Firewall blocked the local server.\nFix: run in PowerShell as Admin:\n New-NetFirewallRule -DisplayName "hyv-cli" -Direction Inbound -LocalPort ' + port + " -Protocol TCP -Action Allow\nOr use a license key: hyv init <license-key>"
5643
5698
  ));
5644
5699
  }, 12e4);
5645
5700
  server.on("request", async (req, res) => {
@@ -5694,7 +5749,7 @@ async function authenticateWithBrowser() {
5694
5749
  `);
5695
5750
  clearTimeout(timeout);
5696
5751
  server.close();
5697
- resolve16(data);
5752
+ resolve17(data);
5698
5753
  } catch (error) {
5699
5754
  res.writeHead(500, { "Content-Type": "text/html" });
5700
5755
  res.end(`<h1>Authentication failed</h1><p>${escapeHtml(error.message)}</p>`);
@@ -5712,7 +5767,7 @@ async function authenticateWithBrowser() {
5712
5767
  return authData;
5713
5768
  }
5714
5769
  async function openAuthenticatedDashboard(opts = {}) {
5715
- const nextPath = opts.next || "/app/billing";
5770
+ const nextPath = opts.next || "/dashboard?tab=billing";
5716
5771
  try {
5717
5772
  const response = await authenticatedRequest(cliApiUrl("/cli/auth/web-handoff"), {
5718
5773
  method: "POST",
@@ -5727,7 +5782,7 @@ async function openAuthenticatedDashboard(opts = {}) {
5727
5782
  }
5728
5783
  } catch {
5729
5784
  }
5730
- const billingUrl = nextPath === "/app/billing" ? DASHBOARD_BILLING_URL : assertSafeOpenUrl(`https://holdyourvoice.com${nextPath}`);
5785
+ const billingUrl = nextPath === "/dashboard?tab=billing" ? DASHBOARD_BILLING_URL : assertSafeOpenUrl(`https://holdyourvoice.com${nextPath}`);
5731
5786
  await (0, import_open.default)(billingUrl);
5732
5787
  }
5733
5788
  async function refreshToken(tokenOverride) {
@@ -5879,7 +5934,7 @@ async function request2(method, path28, body) {
5879
5934
  if (res.status === 401)
5880
5935
  throw new Error("your session expired. run: hyv init to sign in again.");
5881
5936
  if (res.status === 403)
5882
- throw new Error("you don't have access to this feature. check your plan at holdyourvoice.com/app/billing");
5937
+ throw new Error("you don't have access to this feature. check your plan at holdyourvoice.com/dashboard?tab=billing");
5883
5938
  if (!res.ok) {
5884
5939
  throw new Error(`something went wrong (${res.status}). try again or contact support.`);
5885
5940
  }
@@ -6360,8 +6415,7 @@ async function requirePaidFeature(feature) {
6360
6415
  throw new Error("can't reach the server. check your internet connection and try again.");
6361
6416
  }
6362
6417
  const plan = (data.plan || "none").toLowerCase();
6363
- const subStatus = String(data.subscription_status || data.status || "").toLowerCase();
6364
- if (!data.plan || plan === "none" || plan === "free" || plan === "expired" || subStatus === "trialing" || subStatus === "none") {
6418
+ if (!data.plan || plan === "none" || plan === "free" || plan === "expired") {
6365
6419
  throw new Error(FEATURE_MESSAGES[feature]);
6366
6420
  }
6367
6421
  }
@@ -11526,7 +11580,7 @@ function isInteractiveTTY() {
11526
11580
  return Boolean(process.stdin.isTTY && process.stdout.isTTY && !process.env.CI);
11527
11581
  }
11528
11582
  function briefPause(ms2 = 280) {
11529
- return new Promise((resolve16) => setTimeout(resolve16, ms2));
11583
+ return new Promise((resolve17) => setTimeout(resolve17, ms2));
11530
11584
  }
11531
11585
  async function withSpinner(label, fn, opts = {}) {
11532
11586
  const minMs = opts.minMs ?? 450;
@@ -11846,7 +11900,7 @@ function saveLocalProfile(name, content) {
11846
11900
  return path14.join(os9.homedir(), ".hyv", "profiles", `${safe}.md`);
11847
11901
  }
11848
11902
  function fetchUrlText(url) {
11849
- return new Promise((resolve16, reject) => {
11903
+ return new Promise((resolve17, reject) => {
11850
11904
  let parsed;
11851
11905
  try {
11852
11906
  parsed = new URL(url);
@@ -11875,7 +11929,7 @@ function fetchUrlText(url) {
11875
11929
  });
11876
11930
  res.on("end", () => {
11877
11931
  const text = data.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
11878
- resolve16(text.slice(0, 12e3));
11932
+ resolve17(text.slice(0, 12e3));
11879
11933
  });
11880
11934
  }
11881
11935
  );
@@ -11895,10 +11949,10 @@ async function collectSamplesFromLink(url) {
11895
11949
  }
11896
11950
  function askLine(prompt) {
11897
11951
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
11898
- return new Promise((resolve16) => {
11952
+ return new Promise((resolve17) => {
11899
11953
  rl.question(prompt, (answer) => {
11900
11954
  rl.close();
11901
- resolve16(answer.trim());
11955
+ resolve17(answer.trim());
11902
11956
  });
11903
11957
  });
11904
11958
  }
@@ -11907,11 +11961,11 @@ async function askMultiline(intro) {
11907
11961
  console.log(import_chalk12.default.dim(" paste below, then type --- on its own line when done\n"));
11908
11962
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
11909
11963
  const lines = [];
11910
- return new Promise((resolve16) => {
11964
+ return new Promise((resolve17) => {
11911
11965
  rl.on("line", (line) => {
11912
11966
  if (line.trim() === "---") {
11913
11967
  rl.close();
11914
- resolve16(lines.join("\n").trim());
11968
+ resolve17(lines.join("\n").trim());
11915
11969
  return;
11916
11970
  }
11917
11971
  lines.push(line);
@@ -12125,7 +12179,7 @@ async function stepSignup(profileName) {
12125
12179
  }
12126
12180
  const upgrade = await askYesNo(" open billing to upgrade?", false);
12127
12181
  if (upgrade) {
12128
- await withSpinner("opening billing\u2026", () => openAuthenticatedDashboard({ next: "/app/billing" }));
12182
+ await withSpinner("opening billing\u2026", () => openAuthenticatedDashboard({ next: "/dashboard?tab=billing" }));
12129
12183
  await briefPause();
12130
12184
  }
12131
12185
  markStepComplete("signup");
@@ -12149,7 +12203,7 @@ async function stepSignup(profileName) {
12149
12203
  }
12150
12204
  await syncProfileToAccount(profileName);
12151
12205
  console.log(import_chalk12.default.cyan("\n opening billing in your dashboard ($1 first month)..."));
12152
- await withSpinner("opening billing\u2026", () => openAuthenticatedDashboard({ next: "/app/billing" }));
12206
+ await withSpinner("opening billing\u2026", () => openAuthenticatedDashboard({ next: "/dashboard?tab=billing" }));
12153
12207
  await briefPause();
12154
12208
  markStepComplete("signup");
12155
12209
  console.log(import_chalk12.default.dim("\n you're signed in \u2014 pick a plan in billing, no second login.\n"));
@@ -12365,7 +12419,7 @@ __export(billing_upgrade_exports, {
12365
12419
  });
12366
12420
  async function openBillingUpgrade(opts = {}) {
12367
12421
  const plan = opts.plan || "individual";
12368
- const next = `/app/billing?checkout=${plan}`;
12422
+ const next = `/dashboard?tab=billing&checkout=${plan}`;
12369
12423
  const token = getToken();
12370
12424
  if (!token) {
12371
12425
  console.log(import_chalk14.default.cyan("\nSign in to upgrade \u2014 opening browser...\n"));
@@ -15769,103 +15823,18 @@ var import_chalk35 = __toESM(require_source());
15769
15823
 
15770
15824
  // src/commands/init.ts
15771
15825
  var import_chalk4 = __toESM(require_source());
15772
- var nodeFs = __toESM(require("fs"));
15773
15826
  var nodePath = __toESM(require("path"));
15774
- var nodeOs = __toESM(require("os"));
15775
15827
  init_config();
15776
15828
  init_auth();
15777
15829
  function configureMcpForDesktop() {
15778
- const results = [];
15779
- const home = nodeOs.homedir();
15780
- const isWin = process.platform === "win32";
15781
- const claudeDir = isWin ? nodePath.join(home, "AppData", "Roaming", "Claude") : nodePath.join(home, "Library", "Application Support", "Claude");
15782
- const claudeConfig = nodePath.join(claudeDir, "claude_desktop_config.json");
15783
- if (nodeFs.existsSync(claudeDir)) {
15784
- try {
15785
- let cfg = {};
15786
- if (nodeFs.existsSync(claudeConfig))
15787
- cfg = JSON.parse(nodeFs.readFileSync(claudeConfig, "utf-8"));
15788
- if (!cfg.mcpServers)
15789
- cfg.mcpServers = {};
15790
- if (!cfg.mcpServers.hyv) {
15791
- cfg.mcpServers.hyv = { command: "hyv", args: ["mcp"] };
15792
- nodeFs.writeFileSync(claudeConfig, JSON.stringify(cfg, null, 2));
15793
- results.push("Claude Desktop");
15794
- }
15795
- } catch {
15796
- }
15797
- }
15798
- const claudeCodeDir = nodePath.join(home, ".claude", "commands");
15799
- if (nodeFs.existsSync(nodePath.dirname(claudeCodeDir))) {
15800
- try {
15801
- nodeFs.mkdirSync(claudeCodeDir, { recursive: true });
15802
- const cmdFile = nodePath.join(claudeCodeDir, "hyv.md");
15803
- if (!nodeFs.existsSync(cmdFile)) {
15804
- const src = findAgentFile("claude-code.md");
15805
- if (src) {
15806
- nodeFs.copyFileSync(src, cmdFile);
15807
- results.push("Claude Code");
15808
- }
15809
- }
15810
- } catch {
15811
- }
15812
- }
15813
- const cursorDir = nodePath.join(home, ".cursor");
15814
- if (nodeFs.existsSync(cursorDir)) {
15815
- try {
15816
- const rulesFile = nodePath.join(cursorDir, "rules", "hyv.md");
15817
- nodeFs.mkdirSync(nodePath.dirname(rulesFile), { recursive: true });
15818
- if (!nodeFs.existsSync(rulesFile)) {
15819
- const src = findAgentFile("cursor.md");
15820
- if (src) {
15821
- nodeFs.copyFileSync(src, rulesFile);
15822
- results.push("Cursor");
15823
- }
15824
- }
15825
- } catch {
15826
- }
15827
- }
15828
- const wsDir = isWin ? nodePath.join(home, "AppData", "Roaming", "Windsurf") : nodePath.join(home, ".windsurf");
15829
- if (nodeFs.existsSync(wsDir)) {
15830
- try {
15831
- const rulesFile = nodePath.join(wsDir, "rules", "hyv.md");
15832
- nodeFs.mkdirSync(nodePath.dirname(rulesFile), { recursive: true });
15833
- if (!nodeFs.existsSync(rulesFile)) {
15834
- const src = findAgentFile("windsurf.md");
15835
- if (src) {
15836
- nodeFs.copyFileSync(src, rulesFile);
15837
- results.push("Windsurf");
15838
- }
15839
- }
15840
- } catch {
15841
- }
15842
- }
15843
15830
  try {
15844
- const chatgptDir = nodePath.join(home, ".chatgpt");
15845
- nodeFs.mkdirSync(chatgptDir, { recursive: true });
15846
- const instrFile = nodePath.join(chatgptDir, "hyv-instructions.txt");
15847
- if (!nodeFs.existsSync(instrFile)) {
15848
- const src = findAgentFile("chatgpt.md");
15849
- if (src) {
15850
- nodeFs.copyFileSync(src, instrFile);
15851
- results.push("ChatGPT");
15852
- }
15853
- }
15831
+ const pkgDir = nodePath.resolve(__dirname, "..");
15832
+ const { integrateDetectedAgents } = require(nodePath.join(pkgDir, "scripts", "postinstall-lib"));
15833
+ const result = integrateDetectedAgents({ pkgDir, quiet: true, force: false });
15834
+ return result.configured || [];
15854
15835
  } catch {
15836
+ return [];
15855
15837
  }
15856
- return results;
15857
- }
15858
- function findAgentFile(filename) {
15859
- const candidates = [
15860
- nodePath.join(nodePath.dirname(__filename || ""), "..", "agents", filename),
15861
- nodePath.join(process.cwd(), "agents", filename),
15862
- nodePath.join(nodeOs.homedir(), ".hyv", "agents", filename)
15863
- ];
15864
- for (const p of candidates) {
15865
- if (nodeFs.existsSync(p))
15866
- return p;
15867
- }
15868
- return null;
15869
15838
  }
15870
15839
  function registerInitCommand(program3) {
15871
15840
  program3.command("init").description("Authenticate with Hold Your Voice").argument("[license-key]", "License key (skip browser flow)").option("--no-browser", "Skip browser-based authentication").action(async (licenseKey, options) => {
@@ -17173,7 +17142,28 @@ function registerImportCommand(program3) {
17173
17142
  ensureHyvDir();
17174
17143
  writeCachedProfile(name, content);
17175
17144
  console.log(import_chalk13.default.green(`
17176
- \u2713 Profile imported: ${name}`));
17145
+ \u2713 Profile imported locally: ${name}`));
17146
+ const { getToken: getToken2 } = await Promise.resolve().then(() => (init_config(), config_exports));
17147
+ const { authenticatedRequest: authenticatedRequest2, cliApiUrl: cliApiUrl2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
17148
+ const token = getToken2();
17149
+ if (token) {
17150
+ console.log(import_chalk13.default.cyan("\nSyncing profile to your account..."));
17151
+ try {
17152
+ const syncResponse = await authenticatedRequest2(
17153
+ cliApiUrl2("/cli/profiles/new"),
17154
+ { method: "POST", body: { name, content, source: "import" } }
17155
+ );
17156
+ if (syncResponse.status === 200) {
17157
+ console.log(import_chalk13.default.green(" \u2713 synced to your account"));
17158
+ } else {
17159
+ console.log(import_chalk13.default.yellow(" profile saved locally \u2014 run `hyv sync` to push to your account"));
17160
+ }
17161
+ } catch {
17162
+ console.log(import_chalk13.default.yellow(" profile saved locally \u2014 run `hyv sync` to push to your account"));
17163
+ }
17164
+ } else {
17165
+ console.log(import_chalk13.default.dim("\n not signed in \u2014 profile saved locally. run `hyv init` then `hyv sync` to back it up."));
17166
+ }
17177
17167
  } catch (error) {
17178
17168
  console.error(import_chalk13.default.red(`Error: ${error.message}`));
17179
17169
  process.exit(1);
@@ -17181,7 +17171,7 @@ function registerImportCommand(program3) {
17181
17171
  });
17182
17172
  }
17183
17173
  function askQuestion(question) {
17184
- return new Promise((resolve16) => {
17174
+ return new Promise((resolve17) => {
17185
17175
  const readline3 = require("readline");
17186
17176
  const rl = readline3.createInterface({
17187
17177
  input: process.stdin,
@@ -17190,7 +17180,7 @@ function askQuestion(question) {
17190
17180
  rl.question(import_chalk13.default.cyan(` ${question}
17191
17181
  > `), (answer) => {
17192
17182
  rl.close();
17193
- resolve16(answer.trim());
17183
+ resolve17(answer.trim());
17194
17184
  });
17195
17185
  });
17196
17186
  }
@@ -17787,7 +17777,7 @@ async function testMcpStdioSubprocess() {
17787
17777
  const entry = resolveCliEntry();
17788
17778
  if (!entry)
17789
17779
  return { ok: false };
17790
- return new Promise((resolve16) => {
17780
+ return new Promise((resolve17) => {
17791
17781
  const child = (0, import_child_process4.spawn)(process.execPath, [entry, "mcp"], {
17792
17782
  stdio: ["pipe", "pipe", "pipe"],
17793
17783
  env: { ...process.env, HYV_POSTINSTALL_QUIET: "1" }
@@ -17803,7 +17793,7 @@ async function testMcpStdioSubprocess() {
17803
17793
  child.kill();
17804
17794
  } catch {
17805
17795
  }
17806
- resolve16({ ok, toolCount });
17796
+ resolve17({ ok, toolCount });
17807
17797
  };
17808
17798
  const timeout = setTimeout(() => finish(false), 1e4);
17809
17799
  const handleLine = (line) => {
@@ -18208,11 +18198,11 @@ async function confirmDestructiveWrite(options) {
18208
18198
  const question = `
18209
18199
  ${label}
18210
18200
  A .bak backup will be created. Proceed? [y/N] `;
18211
- const answer = await new Promise((resolve16) => {
18201
+ const answer = await new Promise((resolve17) => {
18212
18202
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
18213
18203
  rl.question(question, (value) => {
18214
18204
  rl.close();
18215
- resolve16(value.trim().toLowerCase());
18205
+ resolve17(value.trim().toLowerCase());
18216
18206
  });
18217
18207
  });
18218
18208
  return answer === "y" || answer === "yes";
@@ -18972,9 +18962,9 @@ var import_chalk30 = __toESM(require_source());
18972
18962
  var PAGES = {
18973
18963
  dashboard: "https://holdyourvoice.com/dashboard",
18974
18964
  profiles: "https://holdyourvoice.com/dashboard?tab=profiles",
18975
- pricing: "https://holdyourvoice.com/app/billing",
18965
+ pricing: "https://holdyourvoice.com/dashboard?tab=billing",
18976
18966
  settings: "https://holdyourvoice.com/dashboard",
18977
- billing: "https://holdyourvoice.com/app/billing"
18967
+ billing: "https://holdyourvoice.com/dashboard?tab=billing"
18978
18968
  };
18979
18969
  function registerOpenCommand(program3) {
18980
18970
  program3.command("open").description("Open the web dashboard in your browser").option("--page <path>", "Page: dashboard, profiles, pricing, settings", "dashboard").option("--profile <name>", "Deep-link to a specific profile").option("--no-browser", "Print URL only, don't open").action(async (options) => {
@@ -19405,6 +19395,69 @@ function formatMcpIntegrateReportText(result, opts = {}) {
19405
19395
  }
19406
19396
  return lines.join("\n");
19407
19397
  }
19398
+ function printMcpIntegrateReport(result, opts = {}) {
19399
+ const { agentIdForConfiguredLabel, isAgentIntegrated } = loadPostinstallLib();
19400
+ const configuredIds = new Set(
19401
+ result.configured.map((label) => agentIdForConfiguredLabel(label)).filter(Boolean)
19402
+ );
19403
+ console.log(import_chalk33.default.bold("\nhold your voice \u2014 mcp integration\n"));
19404
+ console.log(import_chalk33.default.dim(` ${getEngineLabel()}
19405
+ `));
19406
+ const welcome = readWelcomeState();
19407
+ const profiles = listLocalProfileNames2();
19408
+ if (profiles.length > 0) {
19409
+ console.log(import_chalk33.default.dim(` voice profiles: ${profiles.join(", ")}`));
19410
+ } else {
19411
+ console.log(import_chalk33.default.yellow(" no voice profile yet \u2014 mcp still works (free scan). finish onboarding in terminal or in-chat via hyv_welcome"));
19412
+ }
19413
+ if (welcome.completed_steps?.length) {
19414
+ console.log(import_chalk33.default.dim(` welcome progress: ${welcome.completed_steps.join(", ")}`));
19415
+ }
19416
+ console.log("");
19417
+ if (result.detected.length === 0) {
19418
+ console.log(import_chalk33.default.yellow(" no supported ai apps detected on this machine."));
19419
+ console.log(import_chalk33.default.dim("\n install cursor, claude desktop, chatgpt desktop, or another supported app,"));
19420
+ console.log(import_chalk33.default.dim(" then run hyv mcp again \u2014 or hyv doctor --fix-agents to configure everything."));
19421
+ console.log(import_chalk33.default.dim("\n in chatgpt/claude/cursor: call hyv_welcome to onboard without the terminal."));
19422
+ console.log(import_chalk33.default.dim(" chatgpt desktop (paid): developer mode \u2192 new app \u2192 https://holdyourvoice.com/mcp + oauth"));
19423
+ console.log(import_chalk33.default.dim(" full steps: hyv mcp --setup-chatgpt\n"));
19424
+ return;
19425
+ }
19426
+ console.log(import_chalk33.default.bold("detected apps"));
19427
+ for (const app of result.detected) {
19428
+ const justConfigured = configuredIds.has(app.id);
19429
+ const integrated = isAgentIntegrated(app.id) || justConfigured;
19430
+ const mark = app.integration === "manual" ? import_chalk33.default.yellow("\u25CB") : integrated ? import_chalk33.default.green("\u2713") : import_chalk33.default.cyan("\u2192");
19431
+ const status = app.integration === "manual" ? "manual connector \u2014 see ~/.chatgpt/hyv-mcp-connector.txt" : justConfigured ? opts.force ? "updated now" : "configured now" : integrated ? "integrated" : "needs setup";
19432
+ console.log(` ${mark} ${app.label} \u2014 ${status}`);
19433
+ console.log(import_chalk33.default.dim(` ${app.reason} \xB7 ${app.integration}`));
19434
+ }
19435
+ if (result.configured.length > 0) {
19436
+ console.log(import_chalk33.default.green(`
19437
+ wired hyv into: ${result.configured.join(", ")}`));
19438
+ }
19439
+ if (result.warnings.length > 0) {
19440
+ console.log(import_chalk33.default.yellow("\n notes:"));
19441
+ for (const warning of result.warnings) {
19442
+ console.log(import_chalk33.default.yellow(` ! ${warning}`));
19443
+ }
19444
+ }
19445
+ const mcpApps = result.detected.filter((a) => a.integration === "mcp");
19446
+ const manualApps = result.detected.filter((a) => a.integration === "manual");
19447
+ console.log(import_chalk33.default.bold("\nnext steps"));
19448
+ console.log(import_chalk33.default.dim(" in this app: hyv_welcome (onboarding) \xB7 hyv_mcp_setup (status/refresh) \xB7 hyv_demo (try a scan)"));
19449
+ if (mcpApps.length > 0) {
19450
+ console.log(import_chalk33.default.dim(" restart apps that use mcp (cursor, claude desktop, antigravity, opencode)"));
19451
+ }
19452
+ if (manualApps.length > 0) {
19453
+ console.log(import_chalk33.default.dim(" chatgpt (recommended): developer mode \u2192 new app \u2192 https://holdyourvoice.com/mcp + oauth"));
19454
+ console.log(import_chalk33.default.dim(" chatgpt (free fallback): settings \u2192 connectors \u2192 command hyv, arguments mcp"));
19455
+ console.log(import_chalk33.default.dim(" full steps: hyv mcp --setup-chatgpt"));
19456
+ }
19457
+ console.log(import_chalk33.default.dim(" refresh stale configs after upgrade: hyv mcp --force"));
19458
+ console.log(import_chalk33.default.dim(" verify: hyv mcp --test"));
19459
+ console.log(import_chalk33.default.dim(" all hosts: hyv doctor --verify-hosts\n"));
19460
+ }
19408
19461
 
19409
19462
  // src/mcp.ts
19410
19463
  init_mcp_profile_hydrate();
@@ -19844,7 +19897,7 @@ var TOOLS = [
19844
19897
  },
19845
19898
  {
19846
19899
  name: "hyv_scan",
19847
- description: "Fast local scan for 80+ AI patterns + profile never-list/learned rules. FREE, offline. ALWAYS scan before finalizing written content.",
19900
+ description: "Fast local scan for 80+ AI patterns + profile never-list/learned rules. FREE, offline. ALWAYS scan before finalizing any written content \u2014 emails, posts, docs, replies, messages, or prose.",
19848
19901
  inputSchema: {
19849
19902
  type: "object",
19850
19903
  properties: {
@@ -20057,6 +20110,16 @@ async function startMcpServer() {
20057
20110
  mcpLog("info", `account profile sync skipped: ${r.error}`);
20058
20111
  }).catch(() => {
20059
20112
  });
20113
+ try {
20114
+ const result = runMcpAutoIntegrate({ force: false });
20115
+ if (result.configured.length > 0) {
20116
+ mcpLog("info", `mcp auto-configured: ${result.configured.join(", ")}`);
20117
+ }
20118
+ if (result.detected.length > 0 && result.configured.length === 0) {
20119
+ mcpLog("info", `detected apps (already configured): ${result.detected.map((a) => a.label).join(", ")}`);
20120
+ }
20121
+ } catch {
20122
+ }
20060
20123
  let buffer = "";
20061
20124
  process.stdin.setEncoding("utf-8");
20062
20125
  let requestChain = Promise.resolve();
@@ -20421,7 +20484,7 @@ registerOpenCommand(program2);
20421
20484
  registerWelcomeCommand(program2);
20422
20485
  registerContentCommand(program2);
20423
20486
  registerUpgradeCommand(program2);
20424
- program2.command("mcp").description("Start MCP server (for Claude Desktop and other MCP hosts)").option("--setup", "Show MCP setup for Claude Desktop, Cursor, Windsurf, Codex").option("--test", "Run MCP health check (tools, profile, stdio)").option("--setup-chatgpt", "Show ChatGPT connector setup instructions").action(async (opts) => {
20487
+ program2.command("mcp").description("Detect installed apps, configure MCP, and start the MCP server").option("--setup", "Show MCP setup for Claude Desktop, Cursor, Windsurf, Codex").option("--test", "Run MCP health check (tools, profile, stdio)").option("--setup-chatgpt", "Show ChatGPT connector setup instructions").option("--force", "Force re-configure MCP even if already integrated").action(async (opts) => {
20425
20488
  if (opts.setup) {
20426
20489
  printMcpSetup();
20427
20490
  return;
@@ -20480,6 +20543,10 @@ program2.command("mcp").description("Start MCP server (for Claude Desktop and ot
20480
20543
  console.log("");
20481
20544
  return;
20482
20545
  }
20546
+ console.log(import_chalk35.default.bold("\nhold your voice \u2014 detecting and configuring mcp...\n"));
20547
+ const result = runMcpAutoIntegrate({ force: Boolean(opts.force) });
20548
+ printMcpIntegrateReport(result, { force: Boolean(opts.force) });
20549
+ console.log(import_chalk35.default.dim("\nstarting MCP server (press Ctrl+C to stop)...\n"));
20483
20550
  startMcpServer();
20484
20551
  });
20485
20552
  program2.command("export").description("Export voice profile for LLMs").argument("[format]", "Export format (claude, chatgpt, generic, cursor)", "claude").option("--output <file>", "Write to file instead of stdout").option("--json", "Output as JSON").action(async (format, opts) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holdyourvoice/hyv",
3
- "version": "2.9.20",
3
+ "version": "2.9.22",
4
4
  "description": "Free local AI writing scan for cursor & claude. MCP server, 220+ pattern detection, voice profiles. npx @holdyourvoice/hyv welcome",
5
5
  "main": "dist/index.js",
6
6
  "bin": {