@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.
- package/dist/index.js +197 -130
- 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((
|
|
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
|
-
|
|
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/
|
|
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((
|
|
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
|
-
|
|
5524
|
+
resolve17(null);
|
|
5470
5525
|
else
|
|
5471
|
-
|
|
5526
|
+
resolve17(stdout.trim());
|
|
5472
5527
|
}
|
|
5473
5528
|
);
|
|
5474
5529
|
});
|
|
@@ -5533,7 +5588,7 @@ function escapeHtml(value) {
|
|
|
5533
5588
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5534
5589
|
}
|
|
5535
5590
|
function request(url, options = {}) {
|
|
5536
|
-
return new Promise((
|
|
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
|
-
|
|
5612
|
+
resolve17({ status: res.statusCode || 0, data: JSON.parse(data) });
|
|
5558
5613
|
} catch {
|
|
5559
|
-
|
|
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((
|
|
5672
|
+
const port = await new Promise((resolve17) => {
|
|
5618
5673
|
server.listen(0, "127.0.0.1", () => {
|
|
5619
|
-
|
|
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((
|
|
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
|
|
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
|
-
|
|
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 || "/
|
|
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 === "/
|
|
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/
|
|
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
|
-
|
|
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((
|
|
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((
|
|
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
|
-
|
|
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((
|
|
11952
|
+
return new Promise((resolve17) => {
|
|
11899
11953
|
rl.question(prompt, (answer) => {
|
|
11900
11954
|
rl.close();
|
|
11901
|
-
|
|
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((
|
|
11964
|
+
return new Promise((resolve17) => {
|
|
11911
11965
|
rl.on("line", (line) => {
|
|
11912
11966
|
if (line.trim() === "---") {
|
|
11913
11967
|
rl.close();
|
|
11914
|
-
|
|
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: "/
|
|
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: "/
|
|
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 = `/
|
|
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
|
|
15845
|
-
|
|
15846
|
-
const
|
|
15847
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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/
|
|
18965
|
+
pricing: "https://holdyourvoice.com/dashboard?tab=billing",
|
|
18976
18966
|
settings: "https://holdyourvoice.com/dashboard",
|
|
18977
|
-
billing: "https://holdyourvoice.com/
|
|
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("
|
|
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.
|
|
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": {
|