@holdyourvoice/hyv 2.9.21 → 2.9.23

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.
@@ -0,0 +1,80 @@
1
+ # Hold Your Voice — Agent Instructions
2
+
3
+ The user has a voice profile that defines their unique writing style. Your job is to make ALL written content match their voice automatically — without waiting for them to mention "voice" or "hyv".
4
+
5
+ ## Auto-trigger — do these automatically
6
+
7
+ **Before delivering any written content (emails, replies, posts, docs, messages, or prose):**
8
+ 1. Run `hyv_scan` for a fast local check (free, offline, works without a profile)
9
+ 2. Run `hyv_clean` for the full scan → fix → validate pipeline, or `hyv_fix` then `hyv_rewrite`
10
+ 3. Run `hyv_validate` to verify the output matches their voice
11
+
12
+ **When the user asks you to write, draft, compose, edit, improve, or rewrite anything:**
13
+ 1. Write your draft normally
14
+ 2. Run `hyv_scan` or `hyv_clean` on it
15
+ 3. Run `hyv_rewrite` to match their voice profile
16
+ 4. Deliver the voice-matched version
17
+
18
+ **After the user accepts a substantial edit:**
19
+ - Suggest they run `hyv reinforce --last` so their profile learns from the change
20
+
21
+ ## Which tool to use
22
+
23
+ | Tool | What it does | When to use |
24
+ |------|-------------|-------------|
25
+ | `hyv_welcome` | Onboarding guide (name → samples → test → signup) | New users, first time |
26
+ | `hyv_mcp_setup` | Status, integrate apps, ChatGPT connector | Check/refresh setup |
27
+ | `hyv_profiles` | List voice profiles | See which profiles are available |
28
+ | `hyv_scan` | Fast local AI-pattern scan (free, 220+ patterns) | Before any prose delivery |
29
+ | `hyv_analyze` | Full hybrid scan (local + server, paid) | Important drafts |
30
+ | `hyv_clean` | Scan → fix → validate → rewrite in one call | Best single-call pipeline |
31
+ | `hyv_fix` | Auto-fix deterministic patterns (no LLM) | Before rewrite |
32
+ | `hyv_rewrite` | Profile-aware rewrite prompt | After scan/fix |
33
+ | `hyv_validate` | Verify output matches voice profile | After rewrite |
34
+ | `hyv_check` | Quick pass/fail | Spot checks |
35
+ | `hyv_score` | 0-100 voice score | Quantify quality |
36
+ | `hyv_diff` | Before/after comparison | Review changes |
37
+ | `hyv_list_free_tools` | List all free capabilities | What's available |
38
+
39
+ ## Voice profiles
40
+
41
+ - Run `hyv_profiles` first if unsure which profile is active.
42
+ - Terminal profiles (from `hyv welcome`) are already local — no extra step.
43
+ - Dashboard profiles sync after the user runs `hyv init` once (MCP auto-downloads).
44
+ - Pass `profile: "<slug>"` on tools only when using a non-default profile.
45
+ - If no profile exists yet, free local scan still works — call `hyv_welcome` to onboard.
46
+
47
+ ## Rewrite rules
48
+
49
+ 1. **Never** use AI-slop words: delve, leverage, tapestry, foster, harness, pivotal, innovative, cutting-edge, robust, holistic, nuanced, resonate, unpack, navigate, landscape, ecosystem, transformative, paradigm, revolutionize, unlock, supercharge, game-changer
50
+ 2. **Never** use formulaic connectors: firstly, secondly, moreover, furthermore, in conclusion, to conclude, notably
51
+ 3. **Never** use hedging: arguably, perhaps, some might say, it's worth noting, it's important to note
52
+ 4. **Never** use signpost claims: this is why, this is how, here's where, here's why
53
+ 5. **Never** use antithesis patterns: "not just X, but Y", "more than just", "it's not about X, it's about Y"
54
+ 6. **Match** the user's sentence rhythm, vocabulary, and tone from their voice profile
55
+ 7. **Preserve** roughness that carries voice — don't over-polish
56
+ 8. **Open** from concrete observations, not generic setups
57
+ 9. **Fix only** flagged lines — preserve clean lines exactly
58
+ 10. **Never add** sections, hooks, CTAs, markdown, or commentary
59
+
60
+ ## MCP setup (no terminal needed)
61
+
62
+ - `hyv_mcp_setup` with `action=status` — see what's integrated and onboarding progress
63
+ - `hyv_mcp_setup` with `action=integrate` — refresh Cursor/Claude configs (`force=true` after hyv upgrade)
64
+ - `hyv_mcp_setup` with `action=chatgpt` — ChatGPT remote OAuth steps (recommended for paid users)
65
+ - `hyv_welcome` — finish voice onboarding inside this chat
66
+
67
+ ## CLI fallback (when MCP tools aren't available)
68
+
69
+ ```bash
70
+ echo "text to check" | hyv scan -
71
+ hyv fix file.md # apply auto-fixes without LLM
72
+ hyv rewrite file.md # generate rewrite prompt
73
+ hyv check "inline text" # quick spot-check
74
+ hyv score file.md # just a 0-100 number
75
+ hyv diff file.md # show proposed fixes
76
+ hyv profiles # list profiles
77
+ hyv reinforce --last # learn from last edit
78
+ hyv mcp --setup # view all MCP configs
79
+ hyv mcp --test # verify everything works
80
+ ```
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() };
@@ -17087,7 +17142,28 @@ function registerImportCommand(program3) {
17087
17142
  ensureHyvDir();
17088
17143
  writeCachedProfile(name, content);
17089
17144
  console.log(import_chalk13.default.green(`
17090
- \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
+ }
17091
17167
  } catch (error) {
17092
17168
  console.error(import_chalk13.default.red(`Error: ${error.message}`));
17093
17169
  process.exit(1);
@@ -19319,6 +19395,69 @@ function formatMcpIntegrateReportText(result, opts = {}) {
19319
19395
  }
19320
19396
  return lines.join("\n");
19321
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
+ }
19322
19461
 
19323
19462
  // src/mcp.ts
19324
19463
  init_mcp_profile_hydrate();
@@ -19758,7 +19897,7 @@ var TOOLS = [
19758
19897
  },
19759
19898
  {
19760
19899
  name: "hyv_scan",
19761
- 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.",
19762
19901
  inputSchema: {
19763
19902
  type: "object",
19764
19903
  properties: {
@@ -19971,6 +20110,16 @@ async function startMcpServer() {
19971
20110
  mcpLog("info", `account profile sync skipped: ${r.error}`);
19972
20111
  }).catch(() => {
19973
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
+ }
19974
20123
  let buffer = "";
19975
20124
  process.stdin.setEncoding("utf-8");
19976
20125
  let requestChain = Promise.resolve();
@@ -20335,7 +20484,7 @@ registerOpenCommand(program2);
20335
20484
  registerWelcomeCommand(program2);
20336
20485
  registerContentCommand(program2);
20337
20486
  registerUpgradeCommand(program2);
20338
- 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) => {
20339
20488
  if (opts.setup) {
20340
20489
  printMcpSetup();
20341
20490
  return;
@@ -20394,6 +20543,10 @@ program2.command("mcp").description("Start MCP server (for Claude Desktop and ot
20394
20543
  console.log("");
20395
20544
  return;
20396
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"));
20397
20550
  startMcpServer();
20398
20551
  });
20399
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.21",
3
+ "version": "2.9.23",
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": {
@@ -572,7 +572,7 @@ function setupAgents({
572
572
  const codexDir = path.join(home, '.codex');
573
573
  fs.mkdirSync(codexDir, { recursive: true });
574
574
  const agentsFile = path.join(codexDir, 'AGENTS.md');
575
- const src = path.join(pkgDir, 'agents', 'codex.md');
575
+ const src = path.join(pkgDir, 'agents', 'AGENTS.md');
576
576
  let existing = '';
577
577
  if (fs.existsSync(agentsFile)) existing = fs.readFileSync(agentsFile, 'utf-8');
578
578
  const addition = fs.existsSync(src) ? fs.readFileSync(src, 'utf-8') : '';
@@ -648,10 +648,10 @@ function setupAgents({
648
648
  if (!ocResult.ok) warnings.push(`opencode: could not update opencode.jsonc (${ocResult.reason})`);
649
649
 
650
650
  const agentsFile = path.join(ocDir, 'AGENTS.md');
651
- const genericSrc = path.join(pkgDir, 'agents', 'generic.md');
651
+ const agentsSrc = path.join(pkgDir, 'agents', 'AGENTS.md');
652
652
  let existing = '';
653
653
  if (fs.existsSync(agentsFile)) existing = fs.readFileSync(agentsFile, 'utf-8');
654
- const addition = fs.existsSync(genericSrc) ? fs.readFileSync(genericSrc, 'utf-8') : '';
654
+ const addition = fs.existsSync(agentsSrc) ? fs.readFileSync(agentsSrc, 'utf-8') : '';
655
655
  if (addition && shouldUpgradeAgent(agentsFile, agentsMarker, pkgVersion)) {
656
656
  const merged = mergeAgentsMd(existing, addition, 'hold-your-voice');
657
657
  fs.mkdirSync(ocDir, { recursive: true });
@@ -730,8 +730,8 @@ function setupAgents({
730
730
 
731
731
  // Generic reference copy (always when auto-configure runs)
732
732
  try {
733
- const genericSrc = path.join(pkgDir, 'agents', 'generic.md');
734
- const genericDest = path.join(hyvDir, 'agents', 'generic.md');
733
+ const genericSrc = path.join(pkgDir, 'agents', 'AGENTS.md');
734
+ const genericDest = path.join(hyvDir, 'agents', 'AGENTS.md');
735
735
  if (fs.existsSync(genericSrc)) installAgent(genericSrc, genericDest);
736
736
  } catch {
737
737
  // non-fatal