@holdyourvoice/hyv 2.9.3 → 2.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable CLI changes. Also mirrored to [holdyourvoice.com/changelog](https://holdyourvoice.com/changelog) for user-facing releases.
4
4
 
5
+ ## [2.9.5] — 2026-06-12
6
+
7
+ ### Fixed
8
+ - Post-signup browser opens `/app/billing` (live dashboard) instead of `/dashboard` (404)
9
+ - Worker redirects `/dashboard` → `/app` for old links
10
+
11
+ ## [2.9.4] — 2026-06-12
12
+
13
+ ### Fixed
14
+ - Welcome step 4 — one sign-in only; opens dashboard billing tab (no second Google login on marketing site)
15
+ - `hyv plan --upgrade` opens authenticated dashboard billing instead of public pricing page
16
+
17
+ ### Changed
18
+ - Step 4 copy tightened; spinners while account + profile sync run
19
+
5
20
  ## [2.9.3] — 2026-06-12
6
21
 
7
22
  ### Fixed
package/dist/index.js CHANGED
@@ -4624,7 +4624,7 @@ __export(config_exports, {
4624
4624
  QUEUE_DIR: () => QUEUE_DIR,
4625
4625
  appendSecureLine: () => appendSecureLine,
4626
4626
  assertSafeOAuthUrl: () => assertSafeOAuthUrl,
4627
- assertSafeOpenUrl: () => assertSafeOpenUrl,
4627
+ assertSafeOpenUrl: () => assertSafeOpenUrl2,
4628
4628
  assertSafeProfileName: () => assertSafeProfileName,
4629
4629
  clearAuth: () => clearAuth,
4630
4630
  clearQueuedSignals: () => clearQueuedSignals,
@@ -4666,7 +4666,7 @@ function cliApiUrl(apiPath) {
4666
4666
  const p = apiPath.startsWith("/") ? apiPath : `/${apiPath}`;
4667
4667
  return `${API_BASE}${p}`;
4668
4668
  }
4669
- function assertSafeOpenUrl(url) {
4669
+ function assertSafeOpenUrl2(url) {
4670
4670
  let parsed;
4671
4671
  try {
4672
4672
  parsed = new URL(url);
@@ -5355,6 +5355,7 @@ __export(auth_exports, {
5355
5355
  authenticatedRequest: () => authenticatedRequest,
5356
5356
  checkSession: () => checkSession,
5357
5357
  getValidToken: () => getValidToken,
5358
+ openAuthenticatedDashboard: () => openAuthenticatedDashboard,
5358
5359
  refreshToken: () => refreshToken,
5359
5360
  verifyOAuthState: () => verifyOAuthState
5360
5361
  });
@@ -5543,6 +5544,22 @@ async function authenticateWithBrowser() {
5543
5544
  writeAuth(authData);
5544
5545
  return authData;
5545
5546
  }
5547
+ async function openAuthenticatedDashboard(opts = {}) {
5548
+ const response = await authenticatedRequest(cliApiUrl("/cli/auth/web-handoff"), {
5549
+ method: "POST",
5550
+ body: {
5551
+ next: opts.next || "/app/billing"
5552
+ }
5553
+ });
5554
+ if (response.status !== 200) {
5555
+ throw new Error("Could not open dashboard \u2014 try https://holdyourvoice.com/dashboard");
5556
+ }
5557
+ const { url } = response.data;
5558
+ if (!url) {
5559
+ throw new Error("Dashboard handoff URL missing");
5560
+ }
5561
+ await (0, import_open.default)(assertSafeOpenUrl(url));
5562
+ }
5546
5563
  async function refreshToken(tokenOverride) {
5547
5564
  const token = tokenOverride || readAuth()?.token;
5548
5565
  if (!token)
@@ -5602,6 +5619,17 @@ var init_auth = __esm({
5602
5619
  });
5603
5620
 
5604
5621
  // src/lib/free-paid.ts
5622
+ var free_paid_exports = {};
5623
+ __export(free_paid_exports, {
5624
+ COMMUNITY_URL: () => COMMUNITY_URL,
5625
+ DASHBOARD_BILLING_URL: () => DASHBOARD_BILLING_URL,
5626
+ FREE_CLI_COMMANDS: () => FREE_CLI_COMMANDS,
5627
+ FREE_WEB_TOOLS: () => FREE_WEB_TOOLS,
5628
+ NPX_EXAMPLES: () => NPX_EXAMPLES,
5629
+ PAID_FEATURES: () => PAID_FEATURES,
5630
+ PRICING_URL: () => PRICING_URL,
5631
+ printFreePaidMatrix: () => printFreePaidMatrix
5632
+ });
5605
5633
  function printFreePaidMatrix(opts = {}) {
5606
5634
  if (opts.compact) {
5607
5635
  console.log(import_chalk2.default.bold("\nFree forever (local)"));
@@ -5634,7 +5662,7 @@ function printFreePaidMatrix(opts = {}) {
5634
5662
  First month $1 \u2192 ${PRICING_URL}`));
5635
5663
  console.log(import_chalk2.default.dim(" hyv init | hyv plan --upgrade\n"));
5636
5664
  }
5637
- var import_chalk2, NPX_EXAMPLES, FREE_CLI_COMMANDS, PAID_FEATURES, FREE_WEB_TOOLS, COMMUNITY_URL, PRICING_URL;
5665
+ var import_chalk2, NPX_EXAMPLES, FREE_CLI_COMMANDS, PAID_FEATURES, FREE_WEB_TOOLS, COMMUNITY_URL, PRICING_URL, DASHBOARD_BILLING_URL;
5638
5666
  var init_free_paid = __esm({
5639
5667
  "src/lib/free-paid.ts"() {
5640
5668
  "use strict";
@@ -5682,6 +5710,7 @@ var init_free_paid = __esm({
5682
5710
  ];
5683
5711
  COMMUNITY_URL = "https://holdyourvoice.com/community";
5684
5712
  PRICING_URL = "https://holdyourvoice.com/#pricing";
5713
+ DASHBOARD_BILLING_URL = "https://holdyourvoice.com/app/billing";
5685
5714
  }
5686
5715
  });
5687
5716
 
@@ -11452,12 +11481,12 @@ function buildStepGuide(step, profileName) {
11452
11481
  return [
11453
11482
  "### Step 4 \u2014 save & unlock",
11454
11483
  "",
11455
- "Explain warmly: their profile is local-only until they sign up. A free account backs it up and syncs across machines.",
11484
+ "Explain warmly: local profile until signup. One browser sign-in via CLI \u2014 then open dashboard billing (no second login).",
11456
11485
  "Paid unlock is **$1 for the first month** \u2014 profiles that learn, hybrid rewrite, dashboard.",
11457
11486
  "Scanning/fix/MCP stay free forever without an account.",
11458
11487
  "",
11459
- "1. `hyv init` \u2014 browser signup",
11460
- "2. `hyv plan --upgrade` \u2014 $1 first month",
11488
+ "1. `hyv init` or welcome step 4 \u2014 single Google sign-in from terminal",
11489
+ "2. Dashboard opens on billing tab \u2014 user picks plan there",
11461
11490
  "",
11462
11491
  `Pricing: ${PRICING_URL}`
11463
11492
  ].join("\n");
@@ -11784,14 +11813,13 @@ async function stepTest(profileName) {
11784
11813
  }
11785
11814
  async function stepSignup(profileName) {
11786
11815
  console.log(import_chalk12.default.bold("\nstep 4 \xB7 save & unlock\n"));
11787
- console.log(" your voice profile is saved locally \u2014 that's enough to scan and test.");
11788
- console.log(" but it won't follow you to another machine, and it won't get sharper");
11789
- console.log(" every time you rewrite.\n");
11790
- console.log(" a free account backs up your profile and syncs it everywhere you use hyv.");
11791
- console.log(" upgrade when you're ready: " + import_chalk12.default.bold("$1 for your first month") + ", then full");
11792
- console.log(" access to profiles that learn, hybrid rewrite, and your dashboard.\n");
11816
+ console.log(" your profile works on this machine. scan anything, anytime \u2014 free forever.");
11817
+ console.log("");
11818
+ console.log(" create a free account to back it up and sync everywhere.");
11819
+ console.log(" then unlock learning for " + import_chalk12.default.bold("$1 your first month") + " \u2014 profiles that");
11820
+ console.log(" get sharper every rewrite, hybrid rewrites, and your dashboard.\n");
11793
11821
  console.log(import_chalk12.default.dim(" free forever (no account): scan, fix, check, mcp"));
11794
- console.log(import_chalk12.default.dim(" paid unlock: learning loop, rich rewrites, sync across devices\n"));
11822
+ console.log(import_chalk12.default.dim(" $1 first month: learning loop, rich rewrites, sync across devices\n"));
11795
11823
  const ready = await askYesNo(" create your free account now? ($1 first month to unlock everything)");
11796
11824
  if (!ready) {
11797
11825
  console.log(import_chalk12.default.dim("\n no rush \u2014 your profile stays on this machine."));
@@ -11803,18 +11831,25 @@ async function stepSignup(profileName) {
11803
11831
  return;
11804
11832
  }
11805
11833
  if (!isInitialized() || !getToken()) {
11806
- console.log(import_chalk12.default.cyan("\n opening browser for signup...\n"));
11807
- await authenticateWithBrowser();
11834
+ console.log(import_chalk12.default.cyan("\n opening browser for signup (one sign-in)...\n"));
11835
+ await withSpinner("creating your account\u2026", () => authenticateWithBrowser());
11836
+ await briefPause();
11837
+ console.log(import_chalk12.default.green(" \u2713 account created"));
11838
+ } else {
11839
+ console.log(import_chalk12.default.dim("\n already signed in \u2014 syncing profile..."));
11808
11840
  }
11809
11841
  const content = fs13.readFileSync(
11810
11842
  path13.join(os7.homedir(), ".hyv", "profiles", `${profileName}.md`),
11811
11843
  "utf-8"
11812
11844
  );
11813
11845
  try {
11814
- const response = await authenticatedRequest(cliApiUrl("/cli/profiles/new"), {
11815
- method: "POST",
11816
- body: { name: profileName, content, source: "welcome" }
11817
- });
11846
+ const response = await withSpinner(
11847
+ "syncing profile\u2026",
11848
+ () => authenticatedRequest(cliApiUrl("/cli/profiles/new"), {
11849
+ method: "POST",
11850
+ body: { name: profileName, content, source: "welcome" }
11851
+ })
11852
+ );
11818
11853
  if (response.status === 200) {
11819
11854
  console.log(import_chalk12.default.green(" \u2713 profile synced to your account"));
11820
11855
  } else {
@@ -11823,12 +11858,19 @@ async function stepSignup(profileName) {
11823
11858
  } catch {
11824
11859
  console.log(import_chalk12.default.yellow(" profile saved locally \u2014 run `hyv sync` after you upgrade"));
11825
11860
  }
11826
- console.log(import_chalk12.default.cyan("\n opening pricing \u2014 $1 first month to unlock learning + sync..."));
11827
- const { default: open3 } = await Promise.resolve().then(() => __toESM(require_open()));
11828
- const { assertSafeOpenUrl: assertSafeOpenUrl2 } = await Promise.resolve().then(() => (init_config(), config_exports));
11829
- await open3(assertSafeOpenUrl2(PRICING_URL));
11861
+ try {
11862
+ console.log(import_chalk12.default.cyan("\n opening billing in your dashboard ($1 first month)..."));
11863
+ await withSpinner("opening dashboard\u2026", () => openAuthenticatedDashboard({ next: "/app/billing" }));
11864
+ await briefPause();
11865
+ } catch {
11866
+ const { DASHBOARD_BILLING_URL: DASHBOARD_BILLING_URL2 } = await Promise.resolve().then(() => (init_free_paid(), free_paid_exports));
11867
+ const { assertSafeOpenUrl: assertSafeOpenUrl3 } = await Promise.resolve().then(() => (init_config(), config_exports));
11868
+ const { default: open3 } = await Promise.resolve().then(() => __toESM(require_open()));
11869
+ console.log(import_chalk12.default.yellow(" handoff unavailable \u2014 opening billing (sign in if prompted)..."));
11870
+ await open3(assertSafeOpenUrl3(DASHBOARD_BILLING_URL2));
11871
+ }
11830
11872
  markStepComplete("signup");
11831
- console.log(import_chalk12.default.dim("\n or run: hyv plan --upgrade\n"));
11873
+ console.log(import_chalk12.default.dim("\n you're signed in \u2014 pick a plan in billing, no second login.\n"));
11832
11874
  }
11833
11875
  async function runInteractiveWelcome() {
11834
11876
  recordEvent("welcome_interactive");
@@ -16854,28 +16896,15 @@ async function showPlan() {
16854
16896
  }
16855
16897
  }
16856
16898
  async function upgradePlan() {
16857
- console.log(import_chalk14.default.cyan("\nOpening checkout...\n"));
16858
- const response = await authenticatedRequest(
16859
- cliApiUrl("/cli/subscribe"),
16860
- {
16861
- method: "POST",
16862
- body: { plan: "individual" }
16863
- }
16864
- );
16865
- if (response.status === 200) {
16866
- const data = response.data;
16867
- const checkoutUrl = data.checkout_url;
16868
- if (checkoutUrl) {
16869
- console.log(import_chalk14.default.dim("Opening browser..."));
16870
- await (0, import_open2.default)(assertSafeOpenUrl(checkoutUrl));
16871
- console.log(import_chalk14.default.green("\n\u2713 Checkout opened in browser"));
16872
- console.log(import_chalk14.default.dim("Complete the checkout to activate your plan."));
16873
- } else {
16874
- console.log(import_chalk14.default.yellow("No checkout URL received."));
16875
- }
16876
- } else {
16877
- console.log(import_chalk14.default.yellow("Could not create checkout session."));
16878
- console.log(import_chalk14.default.dim("Visit https://holdyourvoice.com/pricing to subscribe."));
16899
+ console.log(import_chalk14.default.cyan("\nOpening billing in your dashboard ($1 first month)...\n"));
16900
+ try {
16901
+ await openAuthenticatedDashboard({ next: "/app/billing" });
16902
+ console.log(import_chalk14.default.green("\n\u2713 Dashboard opened \u2014 you're already signed in"));
16903
+ console.log(import_chalk14.default.dim("Pick a plan in billing. No second login."));
16904
+ } catch {
16905
+ console.log(import_chalk14.default.yellow("Could not open dashboard automatically."));
16906
+ console.log(import_chalk14.default.dim(`Visit ${DASHBOARD_BILLING_URL} after running hyv init`));
16907
+ console.log(import_chalk14.default.dim(`Or see plans: ${PRICING_URL}`));
16879
16908
  }
16880
16909
  }
16881
16910
  async function openBillingPortal() {
@@ -16889,7 +16918,7 @@ async function openBillingPortal() {
16889
16918
  const portalUrl = data.portal_url;
16890
16919
  if (portalUrl) {
16891
16920
  console.log(import_chalk14.default.dim("Opening browser..."));
16892
- await (0, import_open2.default)(assertSafeOpenUrl(portalUrl));
16921
+ await (0, import_open2.default)(assertSafeOpenUrl2(portalUrl));
16893
16922
  console.log(import_chalk14.default.green("\n\u2713 Billing portal opened"));
16894
16923
  } else {
16895
16924
  console.log(import_chalk14.default.yellow("No portal URL received."));
@@ -18453,7 +18482,7 @@ var import_chalk29 = __toESM(require_source());
18453
18482
  var PAGES = {
18454
18483
  dashboard: "https://holdyourvoice.com/dashboard",
18455
18484
  profiles: "https://holdyourvoice.com/dashboard",
18456
- pricing: "https://holdyourvoice.com/pricing",
18485
+ pricing: "https://holdyourvoice.com/app/billing",
18457
18486
  settings: "https://holdyourvoice.com/dashboard"
18458
18487
  };
18459
18488
  function registerOpenCommand(program3) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holdyourvoice/hyv",
3
- "version": "2.9.3",
3
+ "version": "2.9.5",
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": {