@holdyourvoice/hyv 2.8.9 → 2.9.0
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 +13 -0
- package/agents/claude-code.md +1 -1
- package/agents/cursor.md +1 -1
- package/agents/generic.md +2 -2
- package/dist/index.js +1705 -1062
- package/package.json +1 -1
- package/scripts/postinstall.js +1 -1
package/dist/index.js
CHANGED
|
@@ -973,8 +973,8 @@ var require_command = __commonJS({
|
|
|
973
973
|
"node_modules/commander/lib/command.js"(exports2) {
|
|
974
974
|
var EventEmitter = require("node:events").EventEmitter;
|
|
975
975
|
var childProcess = require("node:child_process");
|
|
976
|
-
var
|
|
977
|
-
var
|
|
976
|
+
var path25 = require("node:path");
|
|
977
|
+
var fs26 = require("node:fs");
|
|
978
978
|
var process2 = require("node:process");
|
|
979
979
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
980
980
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1916,13 +1916,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1916
1916
|
let launchWithNode = false;
|
|
1917
1917
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1918
1918
|
function findFile(baseDir, baseName) {
|
|
1919
|
-
const localBin =
|
|
1920
|
-
if (
|
|
1919
|
+
const localBin = path25.resolve(baseDir, baseName);
|
|
1920
|
+
if (fs26.existsSync(localBin))
|
|
1921
1921
|
return localBin;
|
|
1922
|
-
if (sourceExt.includes(
|
|
1922
|
+
if (sourceExt.includes(path25.extname(baseName)))
|
|
1923
1923
|
return void 0;
|
|
1924
1924
|
const foundExt = sourceExt.find(
|
|
1925
|
-
(ext) =>
|
|
1925
|
+
(ext) => fs26.existsSync(`${localBin}${ext}`)
|
|
1926
1926
|
);
|
|
1927
1927
|
if (foundExt)
|
|
1928
1928
|
return `${localBin}${foundExt}`;
|
|
@@ -1935,21 +1935,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1935
1935
|
if (this._scriptPath) {
|
|
1936
1936
|
let resolvedScriptPath;
|
|
1937
1937
|
try {
|
|
1938
|
-
resolvedScriptPath =
|
|
1938
|
+
resolvedScriptPath = fs26.realpathSync(this._scriptPath);
|
|
1939
1939
|
} catch (err) {
|
|
1940
1940
|
resolvedScriptPath = this._scriptPath;
|
|
1941
1941
|
}
|
|
1942
|
-
executableDir =
|
|
1943
|
-
|
|
1942
|
+
executableDir = path25.resolve(
|
|
1943
|
+
path25.dirname(resolvedScriptPath),
|
|
1944
1944
|
executableDir
|
|
1945
1945
|
);
|
|
1946
1946
|
}
|
|
1947
1947
|
if (executableDir) {
|
|
1948
1948
|
let localFile = findFile(executableDir, executableFile);
|
|
1949
1949
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1950
|
-
const legacyName =
|
|
1950
|
+
const legacyName = path25.basename(
|
|
1951
1951
|
this._scriptPath,
|
|
1952
|
-
|
|
1952
|
+
path25.extname(this._scriptPath)
|
|
1953
1953
|
);
|
|
1954
1954
|
if (legacyName !== this._name) {
|
|
1955
1955
|
localFile = findFile(
|
|
@@ -1960,7 +1960,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1960
1960
|
}
|
|
1961
1961
|
executableFile = localFile || executableFile;
|
|
1962
1962
|
}
|
|
1963
|
-
launchWithNode = sourceExt.includes(
|
|
1963
|
+
launchWithNode = sourceExt.includes(path25.extname(executableFile));
|
|
1964
1964
|
let proc;
|
|
1965
1965
|
if (process2.platform !== "win32") {
|
|
1966
1966
|
if (launchWithNode) {
|
|
@@ -2817,7 +2817,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2817
2817
|
* @return {Command}
|
|
2818
2818
|
*/
|
|
2819
2819
|
nameFromFilename(filename) {
|
|
2820
|
-
this._name =
|
|
2820
|
+
this._name = path25.basename(filename, path25.extname(filename));
|
|
2821
2821
|
return this;
|
|
2822
2822
|
}
|
|
2823
2823
|
/**
|
|
@@ -2831,10 +2831,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2831
2831
|
* @param {string} [path]
|
|
2832
2832
|
* @return {(string|null|Command)}
|
|
2833
2833
|
*/
|
|
2834
|
-
executableDir(
|
|
2835
|
-
if (
|
|
2834
|
+
executableDir(path26) {
|
|
2835
|
+
if (path26 === void 0)
|
|
2836
2836
|
return this._executableDir;
|
|
2837
|
-
this._executableDir =
|
|
2837
|
+
this._executableDir = path26;
|
|
2838
2838
|
return this;
|
|
2839
2839
|
}
|
|
2840
2840
|
/**
|
|
@@ -3934,15 +3934,15 @@ var require_route = __commonJS({
|
|
|
3934
3934
|
};
|
|
3935
3935
|
}
|
|
3936
3936
|
function wrapConversion(toModel, graph) {
|
|
3937
|
-
const
|
|
3937
|
+
const path25 = [graph[toModel].parent, toModel];
|
|
3938
3938
|
let fn = conversions[graph[toModel].parent][toModel];
|
|
3939
3939
|
let cur = graph[toModel].parent;
|
|
3940
3940
|
while (graph[cur].parent) {
|
|
3941
|
-
|
|
3941
|
+
path25.unshift(graph[cur].parent);
|
|
3942
3942
|
fn = link(conversions[graph[cur].parent][cur], fn);
|
|
3943
3943
|
cur = graph[cur].parent;
|
|
3944
3944
|
}
|
|
3945
|
-
fn.conversion =
|
|
3945
|
+
fn.conversion = path25;
|
|
3946
3946
|
return fn;
|
|
3947
3947
|
}
|
|
3948
3948
|
module2.exports = function(fromModel) {
|
|
@@ -4182,7 +4182,7 @@ var require_has_flag = __commonJS({
|
|
|
4182
4182
|
var require_supports_color = __commonJS({
|
|
4183
4183
|
"node_modules/supports-color/index.js"(exports2, module2) {
|
|
4184
4184
|
"use strict";
|
|
4185
|
-
var
|
|
4185
|
+
var os10 = require("os");
|
|
4186
4186
|
var tty = require("tty");
|
|
4187
4187
|
var hasFlag = require_has_flag();
|
|
4188
4188
|
var { env } = process;
|
|
@@ -4230,7 +4230,7 @@ var require_supports_color = __commonJS({
|
|
|
4230
4230
|
return min;
|
|
4231
4231
|
}
|
|
4232
4232
|
if (process.platform === "win32") {
|
|
4233
|
-
const osRelease =
|
|
4233
|
+
const osRelease = os10.release().split(".");
|
|
4234
4234
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
4235
4235
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
4236
4236
|
}
|
|
@@ -4381,14 +4381,14 @@ var require_templates = __commonJS({
|
|
|
4381
4381
|
}
|
|
4382
4382
|
return results;
|
|
4383
4383
|
}
|
|
4384
|
-
function buildStyle(
|
|
4384
|
+
function buildStyle(chalk33, styles) {
|
|
4385
4385
|
const enabled = {};
|
|
4386
4386
|
for (const layer of styles) {
|
|
4387
4387
|
for (const style of layer.styles) {
|
|
4388
4388
|
enabled[style[0]] = layer.inverse ? null : style.slice(1);
|
|
4389
4389
|
}
|
|
4390
4390
|
}
|
|
4391
|
-
let current =
|
|
4391
|
+
let current = chalk33;
|
|
4392
4392
|
for (const [styleName, styles2] of Object.entries(enabled)) {
|
|
4393
4393
|
if (!Array.isArray(styles2)) {
|
|
4394
4394
|
continue;
|
|
@@ -4400,7 +4400,7 @@ var require_templates = __commonJS({
|
|
|
4400
4400
|
}
|
|
4401
4401
|
return current;
|
|
4402
4402
|
}
|
|
4403
|
-
module2.exports = (
|
|
4403
|
+
module2.exports = (chalk33, temporary) => {
|
|
4404
4404
|
const styles = [];
|
|
4405
4405
|
const chunks = [];
|
|
4406
4406
|
let chunk = [];
|
|
@@ -4410,13 +4410,13 @@ var require_templates = __commonJS({
|
|
|
4410
4410
|
} else if (style) {
|
|
4411
4411
|
const string = chunk.join("");
|
|
4412
4412
|
chunk = [];
|
|
4413
|
-
chunks.push(styles.length === 0 ? string : buildStyle(
|
|
4413
|
+
chunks.push(styles.length === 0 ? string : buildStyle(chalk33, styles)(string));
|
|
4414
4414
|
styles.push({ inverse, styles: parseStyle(style) });
|
|
4415
4415
|
} else if (close) {
|
|
4416
4416
|
if (styles.length === 0) {
|
|
4417
4417
|
throw new Error("Found extraneous } in Chalk template literal");
|
|
4418
4418
|
}
|
|
4419
|
-
chunks.push(buildStyle(
|
|
4419
|
+
chunks.push(buildStyle(chalk33, styles)(chunk.join("")));
|
|
4420
4420
|
chunk = [];
|
|
4421
4421
|
styles.pop();
|
|
4422
4422
|
} else {
|
|
@@ -4464,16 +4464,16 @@ var require_source = __commonJS({
|
|
|
4464
4464
|
}
|
|
4465
4465
|
};
|
|
4466
4466
|
var chalkFactory = (options) => {
|
|
4467
|
-
const
|
|
4468
|
-
applyOptions(
|
|
4469
|
-
|
|
4470
|
-
Object.setPrototypeOf(
|
|
4471
|
-
Object.setPrototypeOf(
|
|
4472
|
-
|
|
4467
|
+
const chalk34 = {};
|
|
4468
|
+
applyOptions(chalk34, options);
|
|
4469
|
+
chalk34.template = (...arguments_) => chalkTag(chalk34.template, ...arguments_);
|
|
4470
|
+
Object.setPrototypeOf(chalk34, Chalk.prototype);
|
|
4471
|
+
Object.setPrototypeOf(chalk34.template, chalk34);
|
|
4472
|
+
chalk34.template.constructor = () => {
|
|
4473
4473
|
throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.");
|
|
4474
4474
|
};
|
|
4475
|
-
|
|
4476
|
-
return
|
|
4475
|
+
chalk34.template.Instance = ChalkClass;
|
|
4476
|
+
return chalk34.template;
|
|
4477
4477
|
};
|
|
4478
4478
|
function Chalk(options) {
|
|
4479
4479
|
return chalkFactory(options);
|
|
@@ -4584,7 +4584,7 @@ var require_source = __commonJS({
|
|
|
4584
4584
|
return openAll + string + closeAll;
|
|
4585
4585
|
};
|
|
4586
4586
|
var template;
|
|
4587
|
-
var chalkTag = (
|
|
4587
|
+
var chalkTag = (chalk34, ...strings) => {
|
|
4588
4588
|
const [firstString] = strings;
|
|
4589
4589
|
if (!isArray(firstString) || !isArray(firstString.raw)) {
|
|
4590
4590
|
return strings.join(" ");
|
|
@@ -4600,18 +4600,52 @@ var require_source = __commonJS({
|
|
|
4600
4600
|
if (template === void 0) {
|
|
4601
4601
|
template = require_templates();
|
|
4602
4602
|
}
|
|
4603
|
-
return template(
|
|
4603
|
+
return template(chalk34, parts.join(""));
|
|
4604
4604
|
};
|
|
4605
4605
|
Object.defineProperties(Chalk.prototype, styles);
|
|
4606
|
-
var
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
module2.exports =
|
|
4606
|
+
var chalk33 = Chalk();
|
|
4607
|
+
chalk33.supportsColor = stdoutColor;
|
|
4608
|
+
chalk33.stderr = Chalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
4609
|
+
chalk33.stderr.supportsColor = stderrColor;
|
|
4610
|
+
module2.exports = chalk33;
|
|
4611
4611
|
}
|
|
4612
4612
|
});
|
|
4613
4613
|
|
|
4614
4614
|
// src/lib/config.ts
|
|
4615
|
+
var config_exports = {};
|
|
4616
|
+
__export(config_exports, {
|
|
4617
|
+
API_BASE: () => API_BASE,
|
|
4618
|
+
AUTH_FILE: () => AUTH_FILE,
|
|
4619
|
+
CACHE_DIR: () => CACHE_DIR,
|
|
4620
|
+
CONFIG_FILE: () => CONFIG_FILE,
|
|
4621
|
+
HYV_DIR: () => HYV_DIR,
|
|
4622
|
+
LAST_SESSION_FILE: () => LAST_SESSION_FILE,
|
|
4623
|
+
PROFILES_DIR: () => PROFILES_DIR,
|
|
4624
|
+
QUEUE_DIR: () => QUEUE_DIR,
|
|
4625
|
+
appendSecureLine: () => appendSecureLine,
|
|
4626
|
+
assertSafeOAuthUrl: () => assertSafeOAuthUrl,
|
|
4627
|
+
assertSafeOpenUrl: () => assertSafeOpenUrl,
|
|
4628
|
+
assertSafeProfileName: () => assertSafeProfileName,
|
|
4629
|
+
clearAuth: () => clearAuth,
|
|
4630
|
+
clearQueuedSignals: () => clearQueuedSignals,
|
|
4631
|
+
cliApiUrl: () => cliApiUrl,
|
|
4632
|
+
ensureHyvDir: () => ensureHyvDir,
|
|
4633
|
+
getQueuedSignals: () => getQueuedSignals,
|
|
4634
|
+
getToken: () => getToken,
|
|
4635
|
+
isInitialized: () => isInitialized,
|
|
4636
|
+
listCachedProfiles: () => listCachedProfiles,
|
|
4637
|
+
profilePathForName: () => profilePathForName,
|
|
4638
|
+
queueSignal: () => queueSignal,
|
|
4639
|
+
readAuth: () => readAuth,
|
|
4640
|
+
readCachedProfile: () => readCachedProfile,
|
|
4641
|
+
readConfig: () => readConfig,
|
|
4642
|
+
readLastEditSession: () => readLastEditSession,
|
|
4643
|
+
saveLastEditSession: () => saveLastEditSession,
|
|
4644
|
+
writeAuth: () => writeAuth,
|
|
4645
|
+
writeCachedProfile: () => writeCachedProfile,
|
|
4646
|
+
writeConfig: () => writeConfig,
|
|
4647
|
+
writeSecureFile: () => writeSecureFile
|
|
4648
|
+
});
|
|
4615
4649
|
function validateApiBase(raw) {
|
|
4616
4650
|
const trimmed = raw.replace(/\/$/, "");
|
|
4617
4651
|
let parsed;
|
|
@@ -4647,6 +4681,27 @@ function assertSafeOpenUrl(url) {
|
|
|
4647
4681
|
}
|
|
4648
4682
|
return url;
|
|
4649
4683
|
}
|
|
4684
|
+
function assertSafeOAuthUrl(url) {
|
|
4685
|
+
let parsed;
|
|
4686
|
+
try {
|
|
4687
|
+
parsed = new URL(url);
|
|
4688
|
+
} catch {
|
|
4689
|
+
throw new Error("Invalid URL");
|
|
4690
|
+
}
|
|
4691
|
+
if (parsed.protocol !== "https:") {
|
|
4692
|
+
throw new Error("Only https:// URLs can be opened");
|
|
4693
|
+
}
|
|
4694
|
+
if (ALLOWED_API_HOSTS.has(parsed.hostname)) {
|
|
4695
|
+
return url;
|
|
4696
|
+
}
|
|
4697
|
+
if (ALLOWED_OAUTH_HOSTS.has(parsed.hostname)) {
|
|
4698
|
+
if (parsed.hostname === "accounts.google.com" && !parsed.pathname.startsWith("/o/oauth2/")) {
|
|
4699
|
+
throw new Error(`Unexpected OAuth path: ${parsed.pathname}`);
|
|
4700
|
+
}
|
|
4701
|
+
return url;
|
|
4702
|
+
}
|
|
4703
|
+
throw new Error(`URL host not allowed: ${parsed.hostname}`);
|
|
4704
|
+
}
|
|
4650
4705
|
function assertSafeProfileName(name) {
|
|
4651
4706
|
const normalized = String(name || "").trim();
|
|
4652
4707
|
if (!normalized)
|
|
@@ -4682,6 +4737,10 @@ function ensureHyvDir() {
|
|
|
4682
4737
|
}
|
|
4683
4738
|
}
|
|
4684
4739
|
}
|
|
4740
|
+
function writeSecureFile(filePath, content) {
|
|
4741
|
+
ensureHyvDir();
|
|
4742
|
+
fs.writeFileSync(filePath, content, { mode: 384 });
|
|
4743
|
+
}
|
|
4685
4744
|
function appendSecureLine(filePath, line, dir) {
|
|
4686
4745
|
if (dir) {
|
|
4687
4746
|
if (!fs.existsSync(dir))
|
|
@@ -4720,6 +4779,11 @@ function writeAuth(auth) {
|
|
|
4720
4779
|
ensureHyvDir();
|
|
4721
4780
|
fs.writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), { mode: 384 });
|
|
4722
4781
|
}
|
|
4782
|
+
function clearAuth() {
|
|
4783
|
+
if (fs.existsSync(AUTH_FILE)) {
|
|
4784
|
+
fs.unlinkSync(AUTH_FILE);
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4723
4787
|
function readConfig() {
|
|
4724
4788
|
try {
|
|
4725
4789
|
if (!fs.existsSync(CONFIG_FILE))
|
|
@@ -4790,6 +4854,17 @@ function queueSignal(signal) {
|
|
|
4790
4854
|
const filePath = path.join(QUEUE_DIR, `${id}.json`);
|
|
4791
4855
|
fs.writeFileSync(filePath, JSON.stringify(signal, null, 2), { mode: 384 });
|
|
4792
4856
|
}
|
|
4857
|
+
function clearQueuedSignals() {
|
|
4858
|
+
try {
|
|
4859
|
+
if (!fs.existsSync(QUEUE_DIR))
|
|
4860
|
+
return;
|
|
4861
|
+
const files = fs.readdirSync(QUEUE_DIR).filter((f) => f.endsWith(".json"));
|
|
4862
|
+
for (const f of files) {
|
|
4863
|
+
fs.unlinkSync(path.join(QUEUE_DIR, f));
|
|
4864
|
+
}
|
|
4865
|
+
} catch {
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4793
4868
|
function saveLastEditSession(session) {
|
|
4794
4869
|
ensureHyvDir();
|
|
4795
4870
|
const data = { ...session, saved_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -4804,7 +4879,7 @@ function readLastEditSession() {
|
|
|
4804
4879
|
return null;
|
|
4805
4880
|
}
|
|
4806
4881
|
}
|
|
4807
|
-
var fs, path, os, HYV_DIR, AUTH_FILE, CONFIG_FILE, PROFILES_DIR, CACHE_DIR, QUEUE_DIR, LAST_SESSION_FILE, ALLOWED_API_HOSTS, API_BASE, PROFILE_NAME_RE;
|
|
4882
|
+
var fs, path, os, HYV_DIR, AUTH_FILE, CONFIG_FILE, PROFILES_DIR, CACHE_DIR, QUEUE_DIR, LAST_SESSION_FILE, ALLOWED_API_HOSTS, ALLOWED_OAUTH_HOSTS, API_BASE, PROFILE_NAME_RE;
|
|
4808
4883
|
var init_config = __esm({
|
|
4809
4884
|
"src/lib/config.ts"() {
|
|
4810
4885
|
"use strict";
|
|
@@ -4825,6 +4900,7 @@ var init_config = __esm({
|
|
|
4825
4900
|
"localhost",
|
|
4826
4901
|
"127.0.0.1"
|
|
4827
4902
|
]);
|
|
4903
|
+
ALLOWED_OAUTH_HOSTS = /* @__PURE__ */ new Set(["accounts.google.com"]);
|
|
4828
4904
|
API_BASE = validateApiBase(process.env.HYV_API_URL || "https://holdyourvoice.com");
|
|
4829
4905
|
PROFILE_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,62}$/i;
|
|
4830
4906
|
}
|
|
@@ -4834,11 +4910,11 @@ var init_config = __esm({
|
|
|
4834
4910
|
var require_is_docker = __commonJS({
|
|
4835
4911
|
"node_modules/is-docker/index.js"(exports2, module2) {
|
|
4836
4912
|
"use strict";
|
|
4837
|
-
var
|
|
4913
|
+
var fs26 = require("fs");
|
|
4838
4914
|
var isDocker;
|
|
4839
4915
|
function hasDockerEnv() {
|
|
4840
4916
|
try {
|
|
4841
|
-
|
|
4917
|
+
fs26.statSync("/.dockerenv");
|
|
4842
4918
|
return true;
|
|
4843
4919
|
} catch (_) {
|
|
4844
4920
|
return false;
|
|
@@ -4846,7 +4922,7 @@ var require_is_docker = __commonJS({
|
|
|
4846
4922
|
}
|
|
4847
4923
|
function hasDockerCGroup() {
|
|
4848
4924
|
try {
|
|
4849
|
-
return
|
|
4925
|
+
return fs26.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
|
|
4850
4926
|
} catch (_) {
|
|
4851
4927
|
return false;
|
|
4852
4928
|
}
|
|
@@ -4864,21 +4940,21 @@ var require_is_docker = __commonJS({
|
|
|
4864
4940
|
var require_is_wsl = __commonJS({
|
|
4865
4941
|
"node_modules/is-wsl/index.js"(exports2, module2) {
|
|
4866
4942
|
"use strict";
|
|
4867
|
-
var
|
|
4868
|
-
var
|
|
4943
|
+
var os10 = require("os");
|
|
4944
|
+
var fs26 = require("fs");
|
|
4869
4945
|
var isDocker = require_is_docker();
|
|
4870
4946
|
var isWsl = () => {
|
|
4871
4947
|
if (process.platform !== "linux") {
|
|
4872
4948
|
return false;
|
|
4873
4949
|
}
|
|
4874
|
-
if (
|
|
4950
|
+
if (os10.release().toLowerCase().includes("microsoft")) {
|
|
4875
4951
|
if (isDocker()) {
|
|
4876
4952
|
return false;
|
|
4877
4953
|
}
|
|
4878
4954
|
return true;
|
|
4879
4955
|
}
|
|
4880
4956
|
try {
|
|
4881
|
-
return
|
|
4957
|
+
return fs26.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
|
|
4882
4958
|
} catch (_) {
|
|
4883
4959
|
return false;
|
|
4884
4960
|
}
|
|
@@ -4917,17 +4993,17 @@ var require_define_lazy_prop = __commonJS({
|
|
|
4917
4993
|
// node_modules/open/index.js
|
|
4918
4994
|
var require_open = __commonJS({
|
|
4919
4995
|
"node_modules/open/index.js"(exports2, module2) {
|
|
4920
|
-
var
|
|
4996
|
+
var path25 = require("path");
|
|
4921
4997
|
var childProcess = require("child_process");
|
|
4922
|
-
var { promises:
|
|
4998
|
+
var { promises: fs26, constants: fsConstants } = require("fs");
|
|
4923
4999
|
var isWsl = require_is_wsl();
|
|
4924
5000
|
var isDocker = require_is_docker();
|
|
4925
5001
|
var defineLazyProperty = require_define_lazy_prop();
|
|
4926
|
-
var localXdgOpenPath =
|
|
5002
|
+
var localXdgOpenPath = path25.join(__dirname, "xdg-open");
|
|
4927
5003
|
var { platform, arch } = process;
|
|
4928
5004
|
var hasContainerEnv = () => {
|
|
4929
5005
|
try {
|
|
4930
|
-
|
|
5006
|
+
fs26.statSync("/run/.containerenv");
|
|
4931
5007
|
return true;
|
|
4932
5008
|
} catch {
|
|
4933
5009
|
return false;
|
|
@@ -4950,14 +5026,14 @@ var require_open = __commonJS({
|
|
|
4950
5026
|
const configFilePath = "/etc/wsl.conf";
|
|
4951
5027
|
let isConfigFileExists = false;
|
|
4952
5028
|
try {
|
|
4953
|
-
await
|
|
5029
|
+
await fs26.access(configFilePath, fsConstants.F_OK);
|
|
4954
5030
|
isConfigFileExists = true;
|
|
4955
5031
|
} catch {
|
|
4956
5032
|
}
|
|
4957
5033
|
if (!isConfigFileExists) {
|
|
4958
5034
|
return defaultMountPoint;
|
|
4959
5035
|
}
|
|
4960
|
-
const configContent = await
|
|
5036
|
+
const configContent = await fs26.readFile(configFilePath, { encoding: "utf8" });
|
|
4961
5037
|
const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
|
|
4962
5038
|
if (!configMountPoint) {
|
|
4963
5039
|
return defaultMountPoint;
|
|
@@ -5057,7 +5133,7 @@ var require_open = __commonJS({
|
|
|
5057
5133
|
const isBundled = !__dirname || __dirname === "/";
|
|
5058
5134
|
let exeLocalXdgOpen = false;
|
|
5059
5135
|
try {
|
|
5060
|
-
await
|
|
5136
|
+
await fs26.access(localXdgOpenPath, fsConstants.X_OK);
|
|
5061
5137
|
exeLocalXdgOpen = true;
|
|
5062
5138
|
} catch {
|
|
5063
5139
|
}
|
|
@@ -5080,14 +5156,14 @@ var require_open = __commonJS({
|
|
|
5080
5156
|
}
|
|
5081
5157
|
const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
|
|
5082
5158
|
if (options.wait) {
|
|
5083
|
-
return new Promise((
|
|
5159
|
+
return new Promise((resolve15, reject) => {
|
|
5084
5160
|
subprocess.once("error", reject);
|
|
5085
5161
|
subprocess.once("close", (exitCode) => {
|
|
5086
5162
|
if (!options.allowNonzeroExitCode && exitCode > 0) {
|
|
5087
5163
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
5088
5164
|
return;
|
|
5089
5165
|
}
|
|
5090
|
-
|
|
5166
|
+
resolve15(subprocess);
|
|
5091
5167
|
});
|
|
5092
5168
|
});
|
|
5093
5169
|
}
|
|
@@ -5218,16 +5294,16 @@ function compareSemver(a, b) {
|
|
|
5218
5294
|
return 0;
|
|
5219
5295
|
}
|
|
5220
5296
|
function fetchLatestNpmVersion(timeoutMs = 8e3) {
|
|
5221
|
-
return new Promise((
|
|
5297
|
+
return new Promise((resolve15) => {
|
|
5222
5298
|
(0, import_child_process.execFile)(
|
|
5223
5299
|
"npm",
|
|
5224
5300
|
["view", "@holdyourvoice/hyv", "version"],
|
|
5225
5301
|
{ timeout: timeoutMs, encoding: "utf-8" },
|
|
5226
5302
|
(err, stdout) => {
|
|
5227
5303
|
if (err || !stdout?.trim())
|
|
5228
|
-
|
|
5304
|
+
resolve15(null);
|
|
5229
5305
|
else
|
|
5230
|
-
|
|
5306
|
+
resolve15(stdout.trim());
|
|
5231
5307
|
}
|
|
5232
5308
|
);
|
|
5233
5309
|
});
|
|
@@ -5291,7 +5367,7 @@ function escapeHtml(value) {
|
|
|
5291
5367
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5292
5368
|
}
|
|
5293
5369
|
function request(url, options = {}) {
|
|
5294
|
-
return new Promise((
|
|
5370
|
+
return new Promise((resolve15, reject) => {
|
|
5295
5371
|
const urlObj = new URL(url);
|
|
5296
5372
|
const isHttps = urlObj.protocol === "https:";
|
|
5297
5373
|
const client = isHttps ? https : http;
|
|
@@ -5312,9 +5388,9 @@ function request(url, options = {}) {
|
|
|
5312
5388
|
res.on("data", (chunk) => data += chunk);
|
|
5313
5389
|
res.on("end", () => {
|
|
5314
5390
|
try {
|
|
5315
|
-
|
|
5391
|
+
resolve15({ status: res.statusCode || 0, data: JSON.parse(data) });
|
|
5316
5392
|
} catch {
|
|
5317
|
-
|
|
5393
|
+
resolve15({ status: res.statusCode || 0, data });
|
|
5318
5394
|
}
|
|
5319
5395
|
});
|
|
5320
5396
|
});
|
|
@@ -5372,9 +5448,9 @@ async function authenticateWithLicense(licenseKey) {
|
|
|
5372
5448
|
}
|
|
5373
5449
|
async function authenticateWithBrowser() {
|
|
5374
5450
|
const server = http.createServer();
|
|
5375
|
-
const port = await new Promise((
|
|
5451
|
+
const port = await new Promise((resolve15) => {
|
|
5376
5452
|
server.listen(0, "127.0.0.1", () => {
|
|
5377
|
-
|
|
5453
|
+
resolve15(server.address().port);
|
|
5378
5454
|
});
|
|
5379
5455
|
});
|
|
5380
5456
|
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
@@ -5392,8 +5468,8 @@ async function authenticateWithBrowser() {
|
|
|
5392
5468
|
throw new Error("Authentication server did not return OAuth state");
|
|
5393
5469
|
}
|
|
5394
5470
|
console.log(import_chalk.default.cyan("\nOpening browser for authentication..."));
|
|
5395
|
-
await (0, import_open.default)(
|
|
5396
|
-
const authData = await new Promise((
|
|
5471
|
+
await (0, import_open.default)(assertSafeOAuthUrl(auth_url));
|
|
5472
|
+
const authData = await new Promise((resolve15, reject) => {
|
|
5397
5473
|
const timeout = setTimeout(() => {
|
|
5398
5474
|
server.close();
|
|
5399
5475
|
reject(new Error("Authentication timeout. Please try again."));
|
|
@@ -5441,7 +5517,7 @@ async function authenticateWithBrowser() {
|
|
|
5441
5517
|
`);
|
|
5442
5518
|
clearTimeout(timeout);
|
|
5443
5519
|
server.close();
|
|
5444
|
-
|
|
5520
|
+
resolve15(data);
|
|
5445
5521
|
} catch (error) {
|
|
5446
5522
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
5447
5523
|
res.end(`<h1>Authentication failed</h1><p>${escapeHtml(error.message)}</p>`);
|
|
@@ -5673,11 +5749,11 @@ __export(api_exports, {
|
|
|
5673
5749
|
apiPost: () => apiPost,
|
|
5674
5750
|
requireSubscription: () => requireSubscription
|
|
5675
5751
|
});
|
|
5676
|
-
async function request2(method,
|
|
5752
|
+
async function request2(method, path25, body) {
|
|
5677
5753
|
const token = await getValidToken();
|
|
5678
5754
|
if (!token)
|
|
5679
5755
|
throw new Error("you're not signed in yet. run: hyv init");
|
|
5680
|
-
const url = `${API_BASE}${
|
|
5756
|
+
const url = `${API_BASE}${path25}`;
|
|
5681
5757
|
const opts = {
|
|
5682
5758
|
method,
|
|
5683
5759
|
headers: {
|
|
@@ -5702,11 +5778,11 @@ async function request2(method, path24, body) {
|
|
|
5702
5778
|
}
|
|
5703
5779
|
return res.json();
|
|
5704
5780
|
}
|
|
5705
|
-
function apiGet(
|
|
5706
|
-
return request2("GET",
|
|
5781
|
+
function apiGet(path25) {
|
|
5782
|
+
return request2("GET", path25);
|
|
5707
5783
|
}
|
|
5708
|
-
function apiPost(
|
|
5709
|
-
return request2("POST",
|
|
5784
|
+
function apiPost(path25, body) {
|
|
5785
|
+
return request2("POST", path25, body);
|
|
5710
5786
|
}
|
|
5711
5787
|
async function requireSubscription() {
|
|
5712
5788
|
const { requirePaidFeature: requirePaidFeature2 } = await Promise.resolve().then(() => (init_access(), access_exports));
|
|
@@ -7278,34 +7354,940 @@ function runPipeline(text, profile, applyFixes = false) {
|
|
|
7278
7354
|
};
|
|
7279
7355
|
}
|
|
7280
7356
|
function readText(source) {
|
|
7281
|
-
const
|
|
7282
|
-
const
|
|
7357
|
+
const fs26 = require("fs");
|
|
7358
|
+
const path25 = require("path");
|
|
7283
7359
|
if (source === "-") {
|
|
7284
7360
|
if (process.stdin.isTTY) {
|
|
7285
7361
|
console.error("No input provided. Pipe content or specify a file.");
|
|
7286
7362
|
process.exit(1);
|
|
7287
7363
|
}
|
|
7288
|
-
return { text:
|
|
7364
|
+
return { text: fs26.readFileSync(0, "utf-8"), path: "stdin" };
|
|
7365
|
+
}
|
|
7366
|
+
const resolved = path25.resolve(source);
|
|
7367
|
+
if (!fs26.existsSync(resolved)) {
|
|
7368
|
+
console.error(`File not found: ${resolved}`);
|
|
7369
|
+
process.exit(1);
|
|
7370
|
+
}
|
|
7371
|
+
const stat = fs26.statSync(resolved);
|
|
7372
|
+
if (stat.isDirectory()) {
|
|
7373
|
+
console.error(`${resolved} is a directory, not a file.`);
|
|
7374
|
+
process.exit(1);
|
|
7375
|
+
}
|
|
7376
|
+
return { text: fs26.readFileSync(resolved, "utf-8"), path: resolved };
|
|
7377
|
+
}
|
|
7378
|
+
var init_pipeline = __esm({
|
|
7379
|
+
"src/lib/pipeline.ts"() {
|
|
7380
|
+
"use strict";
|
|
7381
|
+
init_signals();
|
|
7382
|
+
init_classifier();
|
|
7383
|
+
init_autofix();
|
|
7384
|
+
init_validator();
|
|
7385
|
+
}
|
|
7386
|
+
});
|
|
7387
|
+
|
|
7388
|
+
// src/lib/patterns.ts
|
|
7389
|
+
function scanLine(line, lineNum, filePath) {
|
|
7390
|
+
const findings = [];
|
|
7391
|
+
for (const pat of ALL_PATTERNS) {
|
|
7392
|
+
pat.regex.lastIndex = 0;
|
|
7393
|
+
let match;
|
|
7394
|
+
while ((match = pat.regex.exec(line)) !== null) {
|
|
7395
|
+
findings.push({
|
|
7396
|
+
file: filePath,
|
|
7397
|
+
line: lineNum,
|
|
7398
|
+
column: match.index + 1,
|
|
7399
|
+
pattern: pat.id,
|
|
7400
|
+
category: pat.category,
|
|
7401
|
+
severity: pat.severity,
|
|
7402
|
+
excerpt: highlightMatch2(line.trim(), match.index, match[0].length),
|
|
7403
|
+
suggestion: pat.suggestion
|
|
7404
|
+
});
|
|
7405
|
+
}
|
|
7406
|
+
}
|
|
7407
|
+
return findings;
|
|
7408
|
+
}
|
|
7409
|
+
function highlightMatch2(line, start, len) {
|
|
7410
|
+
const before = line.slice(0, start);
|
|
7411
|
+
const match = line.slice(start, start + len);
|
|
7412
|
+
const after = line.slice(start + len);
|
|
7413
|
+
return `${before}\xAB${match}\xBB${after}`.slice(0, 120);
|
|
7414
|
+
}
|
|
7415
|
+
var AI_OVERUSED2, FORMULAIC2, HEDGING2, STRUCTURE2, ENGAGEMENT_BAIT2, AI_CRINGE2, INSIDER_CLAIMS2, FORMATTING, OGILVY2, ALL_PATTERNS;
|
|
7416
|
+
var init_patterns = __esm({
|
|
7417
|
+
"src/lib/patterns.ts"() {
|
|
7418
|
+
"use strict";
|
|
7419
|
+
AI_OVERUSED2 = [
|
|
7420
|
+
{ id: "ai.delve", category: "ai-slop", severity: "red", regex: /\bdelve\b/gi, suggestion: "use a specific verb: dig, explore, look at" },
|
|
7421
|
+
{ id: "ai.leverage", category: "ai-slop", severity: "red", regex: /\bleverage\b/gi, suggestion: "use: use, apply, build on" },
|
|
7422
|
+
{ id: "ai.utilize", category: "ai-slop", severity: "red", regex: /\butilize\b/gi, suggestion: "use: use" },
|
|
7423
|
+
{ id: "ai.tapestry", category: "ai-slop", severity: "red", regex: /\btapestry\b/gi, suggestion: "be specific about what you mean" },
|
|
7424
|
+
{ id: "ai.holistic", category: "ai-slop", severity: "red", regex: /\bholistic\b/gi, suggestion: "describe the actual approach" },
|
|
7425
|
+
{ id: "ai.robust", category: "ai-slop", severity: "yellow", regex: /\brobust\b/gi, suggestion: "say what actually makes it strong" },
|
|
7426
|
+
{ id: "ai.pivotal", category: "ai-slop", severity: "yellow", regex: /\bpivotal\b/gi, suggestion: "say why it matters specifically" },
|
|
7427
|
+
{ id: "ai.foster", category: "ai-slop", severity: "yellow", regex: /\bfoster\b/gi, suggestion: "use: build, grow, encourage, support" },
|
|
7428
|
+
{ id: "ai.harness", category: "ai-slop", severity: "yellow", regex: /\bharness\b/gi, suggestion: "use: use, apply, work with" },
|
|
7429
|
+
{ id: "ai.illuminate", category: "ai-slop", severity: "yellow", regex: /\billuminate\b/gi, suggestion: "use: show, explain, highlight" },
|
|
7430
|
+
{ id: "ai.ever-evolving", category: "ai-slop", severity: "red", regex: /\b(?:ever[\s-]evolving|ever[\s-]changing)\b/gi, suggestion: "cut this \u2014 it says nothing" },
|
|
7431
|
+
{ id: "ai.fast-paced", category: "ai-slop", severity: "red", regex: /\bfast[\s-]paced\b/gi, suggestion: "cut this \u2014 every industry says this" },
|
|
7432
|
+
{ id: "ai.game-changer", category: "ai-slop", severity: "red", regex: /\bgame[\s-]changer\b/gi, suggestion: "explain what actually changed" },
|
|
7433
|
+
{ id: "ai.paradigm", category: "ai-slop", severity: "red", regex: /\bparadigm\b/gi, suggestion: "describe the actual shift" },
|
|
7434
|
+
{ id: "ai.synergy", category: "ai-slop", severity: "red", regex: /\bsynergy\b/gi, suggestion: "describe what works together and why" },
|
|
7435
|
+
{ id: "ai.ecosystem", category: "ai-slop", severity: "yellow", regex: /\becosystem\b/gi, suggestion: "name the specific tools/partners/platforms" },
|
|
7436
|
+
{ id: "ai.seamless", category: "ai-slop", severity: "yellow", regex: /\bseamless\b/gi, suggestion: "describe how it actually works" },
|
|
7437
|
+
{ id: "ai.actionable", category: "ai-slop", severity: "yellow", regex: /\bactionable\b/gi, suggestion: "just give the action, don't label it" },
|
|
7438
|
+
{ id: "ai.granular", category: "ai-slop", severity: "yellow", regex: /\bgranular\b/gi, suggestion: "say: specific, detailed, or name the level" },
|
|
7439
|
+
{ id: "ai.impactful", category: "ai-slop", severity: "yellow", regex: /\bimpactful\b/gi, suggestion: "describe the actual impact" },
|
|
7440
|
+
{ id: "ai.landscape", category: "ai-slop", severity: "red", regex: /\blandscape\b/gi, suggestion: "name the specific market/field/area" },
|
|
7441
|
+
{ id: "ai.realm", category: "ai-slop", severity: "red", regex: /\brealm\b/gi, suggestion: "name the specific domain" },
|
|
7442
|
+
{ id: "ai.straightforward", category: "ai-slop", severity: "yellow", regex: /\bstraightforward\b/gi, suggestion: "just explain it directly" }
|
|
7443
|
+
];
|
|
7444
|
+
FORMULAIC2 = [
|
|
7445
|
+
{ id: "formula.firstly", category: "ai-slop", severity: "yellow", regex: /\bfirstly\b/gi, suggestion: 'just start \u2014 "firstly" is filler' },
|
|
7446
|
+
{ id: "formula.secondly", category: "ai-slop", severity: "yellow", regex: /\bsecondly\b/gi, suggestion: 'just continue \u2014 "secondly" is filler' },
|
|
7447
|
+
{ id: "formula.lastly", category: "ai-slop", severity: "yellow", regex: /\blastly\b/gi, suggestion: 'just end \u2014 "lastly" is filler' },
|
|
7448
|
+
{ id: "formula.moreover", category: "ai-slop", severity: "yellow", regex: /\bmoreover\b/gi, suggestion: "just add the point" },
|
|
7449
|
+
{ id: "formula.furthermore", category: "ai-slop", severity: "yellow", regex: /\bfurthermore\b/gi, suggestion: "just add the point" },
|
|
7450
|
+
{ id: "formula.in-conclusion", category: "ai-slop", severity: "red", regex: /\bin conclusion\b/gi, suggestion: "just end. readers know it's the end." },
|
|
7451
|
+
{ id: "formula.in-summary", category: "ai-slop", severity: "red", regex: /\bin summary\b/gi, suggestion: "just summarize. the label is redundant." },
|
|
7452
|
+
{ id: "formula.it-is-important", category: "ai-slop", severity: "yellow", regex: /\bit is important to note\b/gi, suggestion: "just note it. skip the preamble." },
|
|
7453
|
+
{ id: "formula.at-the-end", category: "ai-slop", severity: "yellow", regex: /\bat the end of the day\b/gi, suggestion: "cut this \u2014 it means nothing" },
|
|
7454
|
+
{ id: "formula.needless-to-say", category: "ai-slop", severity: "yellow", regex: /\bneedless to say\b/gi, suggestion: "if it's needless, don't say it" },
|
|
7455
|
+
{ id: "formula.it-goes-without", category: "ai-slop", severity: "yellow", regex: /\bit goes without saying\b/gi, suggestion: "then don't say it" },
|
|
7456
|
+
{ id: "formula.in-today", category: "ai-slop", severity: "red", regex: /\bin today'?s\b/gi, suggestion: "start with your actual point instead" },
|
|
7457
|
+
{ id: "formula.lets-dive", category: "ai-slop", severity: "red", regex: /\blet'?s (?:dive|jump|dig|delve)\b/gi, suggestion: "just start. no diving needed." },
|
|
7458
|
+
{ id: "formula.without-further", category: "ai-slop", severity: "red", regex: /\bwithout further ado\b/gi, suggestion: "cut this \u2014 just get to it" },
|
|
7459
|
+
{ id: "formula.its-worth-noting", category: "ai-slop", severity: "yellow", regex: /\bit'?s worth noting\b/gi, suggestion: "just note it directly" },
|
|
7460
|
+
{ id: "formula.moving-forward", category: "ai-slop", severity: "yellow", regex: /\bmoving forward\b/gi, suggestion: "just say what happens next" },
|
|
7461
|
+
{ id: "formula.to-put-in-perspective", category: "ai-slop", severity: "yellow", regex: /\bto put this in perspective\b/gi, suggestion: "just give the perspective directly" },
|
|
7462
|
+
{ id: "formula.what-makes-interesting", category: "ai-slop", severity: "yellow", regex: /\bwhat makes this particularly interesting\b/gi, suggestion: "just say the interesting thing" },
|
|
7463
|
+
{ id: "formula.implications", category: "ai-slop", severity: "yellow", regex: /\bthe implications here are\b/gi, suggestion: "state the implications directly" },
|
|
7464
|
+
{ id: "formula.in-other-words", category: "ai-slop", severity: "red", regex: /\bin other words\b/gi, suggestion: "say it once, well" }
|
|
7465
|
+
];
|
|
7466
|
+
HEDGING2 = [
|
|
7467
|
+
{ id: "hedge.some-might", category: "voice-drift", severity: "yellow", regex: /\bsome might say\b/gi, suggestion: "commit to the claim or drop it" },
|
|
7468
|
+
{ id: "hedge.arguably", category: "voice-drift", severity: "yellow", regex: /\barguably\b/gi, suggestion: "commit. say it or don't." },
|
|
7469
|
+
{ id: "hedge.worth-noting", category: "voice-drift", severity: "yellow", regex: /\bit'?s worth noting\b/gi, suggestion: "just note it directly" },
|
|
7470
|
+
{ id: "hedge.to-some-extent", category: "voice-drift", severity: "yellow", regex: /\bto some extent\b/gi, suggestion: "be specific about the extent" },
|
|
7471
|
+
{ id: "hedge.perhaps", category: "voice-drift", severity: "yellow", regex: /\bperhaps\b/gi, suggestion: "commit or cut" },
|
|
7472
|
+
{ id: "hedge.it-seems", category: "voice-drift", severity: "yellow", regex: /\bit seems\b/gi, suggestion: "state it directly" }
|
|
7473
|
+
];
|
|
7474
|
+
STRUCTURE2 = [
|
|
7475
|
+
{ id: "struct.antithesis", category: "structure", severity: "yellow", regex: /\bnot (?:just|only) .{3,50}, but .{3,50}/gi, suggestion: "this antithesis pattern is an AI tell \u2014 restructure" },
|
|
7476
|
+
{ id: "struct.more-than-just", category: "structure", severity: "yellow", regex: /\bmore than just\b/gi, suggestion: "say what it IS, not what it isn't" },
|
|
7477
|
+
{ id: "struct.in-order-to", category: "structure", severity: "yellow", regex: /\bin order to\b/gi, suggestion: 'just use "to"' },
|
|
7478
|
+
{ id: "struct.due-to-the-fact", category: "structure", severity: "yellow", regex: /\bdue to the fact that\b/gi, suggestion: 'use "because"' },
|
|
7479
|
+
{ id: "struct.for-the-purpose", category: "structure", severity: "yellow", regex: /\bfor the purpose of\b/gi, suggestion: 'use "to"' },
|
|
7480
|
+
{ id: "struct.which-is-another", category: "structure", severity: "red", regex: /\bwhich is another way of saying\b/gi, suggestion: "just say the thing directly" },
|
|
7481
|
+
{ id: "struct.this-is-why", category: "structure", severity: "yellow", regex: /\bthis is (?:also )?(?:why|how|where|what\b)/gi, suggestion: "signpost claims are AI tells \u2014 just make the point" },
|
|
7482
|
+
{ id: "struct.heres-where", category: "structure", severity: "yellow", regex: /\bhere'?s (?:where|why|what|the part|the (?:harder|real|actual|main|bigger) problem)\b/gi, suggestion: "just make the point without the signpost" },
|
|
7483
|
+
{ id: "struct.rhetorical-truth", category: "structure", severity: "yellow", regex: /\b(?:the\s+)?(?:uncomfortable|hard|harsh|brutal|real|honest)\s+(?:truth|reality)\b/gi, suggestion: "state the fact directly, skip the framing" },
|
|
7484
|
+
{ id: "struct.lesson-setup", category: "structure", severity: "yellow", regex: /\bhere'?s what .{3,60} taught\b/gi, suggestion: "just share the lesson" },
|
|
7485
|
+
// THE BIG ONE — antithesis negation pattern (Voice DNA: FATAL)
|
|
7486
|
+
{ id: "struct.this-isnt-x-this-is-y", category: "structure", severity: "red", regex: /\bthis isn'?t .{2,40}\.?\s*(?:this is|it'?s) .{2,40}/gi, suggestion: "FATAL: delete the negation, just state the positive claim" },
|
|
7487
|
+
{ id: "struct.not-x-y", category: "structure", severity: "red", regex: /\bnot .{2,30}\.?\s*.{2,30}\b/gi, suggestion: 'the "Not X. Y." pattern is an AI tell \u2014 just state Y' },
|
|
7488
|
+
{ id: "struct.forget-x", category: "structure", severity: "red", regex: /\bforget .{2,40}\.?\s*(?:this is|it'?s|you need)/gi, suggestion: "don't negate \u2014 just state what you mean" }
|
|
7489
|
+
];
|
|
7490
|
+
ENGAGEMENT_BAIT2 = [
|
|
7491
|
+
{ id: "bait.let-that-sink", category: "engagement-bait", severity: "red", regex: /\blet that sink in\b/gi, suggestion: "cut the sink. make your point and move on." },
|
|
7492
|
+
{ id: "bait.read-that-again", category: "engagement-bait", severity: "red", regex: /\bread that again\b/gi, suggestion: "if it needs repeating, repeat it yourself" },
|
|
7493
|
+
{ id: "bait.full-stop", category: "engagement-bait", severity: "red", regex: /\bfull stop\b/gi, suggestion: "the period already does this job" },
|
|
7494
|
+
{ id: "bait.this-changes-everything", category: "engagement-bait", severity: "red", regex: /\bthis changes everything\b/gi, suggestion: "prove it with specifics" },
|
|
7495
|
+
{ id: "bait.paying-attention", category: "engagement-bait", severity: "red", regex: /\bare you paying attention\b/gi, suggestion: "don't patronize the reader" },
|
|
7496
|
+
{ id: "bait.not-ready", category: "engagement-bait", severity: "red", regex: /\byou'?re not ready for this\b/gi, suggestion: "just deliver the content" }
|
|
7497
|
+
];
|
|
7498
|
+
AI_CRINGE2 = [
|
|
7499
|
+
{ id: "cringe.supercharge", category: "ai-cringe", severity: "red", regex: /\bsupercharge\b/gi, suggestion: "describe what it actually does" },
|
|
7500
|
+
{ id: "cringe.unlock", category: "ai-cringe", severity: "yellow", regex: /\bunlock\b/gi, suggestion: "describe what they get access to" },
|
|
7501
|
+
{ id: "cringe.future-proof", category: "ai-cringe", severity: "red", regex: /\bfuture[\s-]proof\b/gi, suggestion: "explain what specifically makes it durable" },
|
|
7502
|
+
{ id: "cringe.10x", category: "ai-cringe", severity: "red", regex: /\b10x\b/gi, suggestion: "use the actual numbers" },
|
|
7503
|
+
{ id: "cringe.ai-revolution", category: "ai-cringe", severity: "red", regex: /\bthe ai revolution\b/gi, suggestion: "describe the specific change" },
|
|
7504
|
+
{ id: "cringe.age-of-ai", category: "ai-cringe", severity: "red", regex: /\bin the age of ai\b/gi, suggestion: "just talk about what's happening now" },
|
|
7505
|
+
{ id: "cringe.happy-to-help", category: "ai-cringe", severity: "red", regex: /\bi'?d be happy to help\b/gi, suggestion: "just help. don't announce it." }
|
|
7506
|
+
];
|
|
7507
|
+
INSIDER_CLAIMS2 = [
|
|
7508
|
+
{ id: "insider.nobody-talking", category: "insider-claim", severity: "red", regex: /\bhere'?s the part nobody'?s? talking about\b/gi, suggestion: "just say the thing. the framing is noise." },
|
|
7509
|
+
{ id: "insider.nobody-tells", category: "insider-claim", severity: "red", regex: /\bwhat nobody tells you\b/gi, suggestion: "just tell them." },
|
|
7510
|
+
{ id: "insider.most-people", category: "insider-claim", severity: "yellow", regex: /\bmost people don'?t realize\b/gi, suggestion: "just explain it. skip the setup." },
|
|
7511
|
+
{ id: "insider.nobody-realizes", category: "insider-claim", severity: "red", regex: /\bnobody (?:realizes|talks about|mentions)\b/gi, suggestion: "if nobody talks about it, just talk about it" }
|
|
7512
|
+
];
|
|
7513
|
+
FORMATTING = [
|
|
7514
|
+
{ id: "format.em-dash", category: "formatting", severity: "red", regex: /\u2014/g, suggestion: "NO em dashes. use commas, periods, colons, semicolons, or parentheses." }
|
|
7515
|
+
];
|
|
7516
|
+
OGILVY2 = [
|
|
7517
|
+
// Jargon — words that hide lack of understanding
|
|
7518
|
+
{ id: "ogilvy.jargon-utilize", category: "ai-slop", severity: "red", regex: /\butilize[sd]?\b/gi, suggestion: 'Ogilvy: use "use" instead' },
|
|
7519
|
+
{ id: "ogilvy.jargon-leverage", category: "ai-slop", severity: "red", regex: /\bleverage[ds]?\b/gi, suggestion: "Ogilvy: say what you actually mean" },
|
|
7520
|
+
{ id: "ogilvy.jargon-synergy", category: "ai-slop", severity: "red", regex: /\bsynerg(?:y|ies|istic)\b/gi, suggestion: "Ogilvy: describe what works together and why" },
|
|
7521
|
+
{ id: "ogilvy.jargon-bandwidth", category: "ai-slop", severity: "yellow", regex: /\bbandwidth\b/gi, suggestion: 'Ogilvy: say "time" or "capacity"' },
|
|
7522
|
+
{ id: "ogilvy.jargon-circle-back", category: "ai-slop", severity: "red", regex: /\bcircle back\b/gi, suggestion: 'Ogilvy: say "follow up" or "talk later"' },
|
|
7523
|
+
{ id: "ogilvy.jargon-low-hanging", category: "ai-slop", severity: "red", regex: /\blow-hanging fruit\b/gi, suggestion: "Ogilvy: name the specific easy win" },
|
|
7524
|
+
{ id: "ogilvy.jargon-move-the-needle", category: "ai-slop", severity: "red", regex: /\bmove the needle\b/gi, suggestion: "Ogilvy: describe the actual impact" },
|
|
7525
|
+
{ id: "ogilvy.jargon-touch-base", category: "ai-slop", severity: "red", regex: /\btouch base\b/gi, suggestion: 'Ogilvy: say "talk" or "meet"' },
|
|
7526
|
+
{ id: "ogilvy.jargon-take-it-offline", category: "ai-slop", severity: "red", regex: /\btake it offline\b/gi, suggestion: 'Ogilvy: say "discuss later"' },
|
|
7527
|
+
{ id: "ogilvy.jargon-deep-dive", category: "ai-slop", severity: "yellow", regex: /\bdeep dive\b/gi, suggestion: 'Ogilvy: say "look closely at" or "examine"' },
|
|
7528
|
+
// Throat-clearing openers
|
|
7529
|
+
{ id: "ogilvy.preamble-i-want-to", category: "voice-drift", severity: "yellow", regex: /^\s*i want to (?:share|talk about|discuss|mention)\b/gi, suggestion: "Ogilvy: just say it. skip the preamble." },
|
|
7530
|
+
{ id: "ogilvy.preamble-just-wanted", category: "voice-drift", severity: "yellow", regex: /^\s*(?:i just wanted|i wanted to)\b/gi, suggestion: "Ogilvy: just say it." }
|
|
7531
|
+
];
|
|
7532
|
+
ALL_PATTERNS = [
|
|
7533
|
+
...AI_OVERUSED2,
|
|
7534
|
+
...FORMULAIC2,
|
|
7535
|
+
...HEDGING2,
|
|
7536
|
+
...STRUCTURE2,
|
|
7537
|
+
...ENGAGEMENT_BAIT2,
|
|
7538
|
+
...AI_CRINGE2,
|
|
7539
|
+
...INSIDER_CLAIMS2,
|
|
7540
|
+
...FORMATTING,
|
|
7541
|
+
...OGILVY2
|
|
7542
|
+
];
|
|
7543
|
+
}
|
|
7544
|
+
});
|
|
7545
|
+
|
|
7546
|
+
// src/lib/scan.ts
|
|
7547
|
+
function words(text) {
|
|
7548
|
+
return (text || "").match(/[a-zA-Z][a-zA-Z0-9']*/g) || [];
|
|
7549
|
+
}
|
|
7550
|
+
function sentences(text) {
|
|
7551
|
+
return (text || "").split(/(?<=[.!?])\s+|\n{2,}/).map((s) => s.trim()).filter((s) => words(s).length > 0);
|
|
7552
|
+
}
|
|
7553
|
+
function paragraphs(text) {
|
|
7554
|
+
return (text || "").split(/\n\s*\n/).map((p) => p.trim()).filter((p) => words(p).length >= 6);
|
|
7555
|
+
}
|
|
7556
|
+
function lineStyleHits(line) {
|
|
7557
|
+
const low = (line || "").trim().toLowerCase();
|
|
7558
|
+
if (!low)
|
|
7559
|
+
return [];
|
|
7560
|
+
const hits = [];
|
|
7561
|
+
const lineWords = low.match(/[a-z']+/g) || [];
|
|
7562
|
+
const abstractCount = lineWords.filter((w) => ABSTRACT_STYLE_WORDS.has(w)).length;
|
|
7563
|
+
if (abstractCount >= 3 && !/\b(?:for example|for instance|such as)\b|\d/i.test(low)) {
|
|
7564
|
+
hits.push({
|
|
7565
|
+
line: 0,
|
|
7566
|
+
// Will be set by caller
|
|
7567
|
+
rule: "abstract_noun_cluster",
|
|
7568
|
+
phrase: line.trim().slice(0, 160)
|
|
7569
|
+
});
|
|
7570
|
+
}
|
|
7571
|
+
if (GENERIC_OPENERS.test(low)) {
|
|
7572
|
+
hits.push({
|
|
7573
|
+
line: 0,
|
|
7574
|
+
rule: "generic_opening_generalization",
|
|
7575
|
+
phrase: line.trim().slice(0, 160)
|
|
7576
|
+
});
|
|
7577
|
+
}
|
|
7578
|
+
if (QUESTION_OPENER.test(low)) {
|
|
7579
|
+
hits.push({
|
|
7580
|
+
line: 0,
|
|
7581
|
+
rule: "voice_question_opener",
|
|
7582
|
+
phrase: "opens with a question instead of a concrete observation"
|
|
7583
|
+
});
|
|
7584
|
+
}
|
|
7585
|
+
if (LESSON_OPENER.test(low)) {
|
|
7586
|
+
hits.push({
|
|
7587
|
+
line: 0,
|
|
7588
|
+
rule: "voice_lesson_opener",
|
|
7589
|
+
phrase: "opens with a lesson or inspirational claim"
|
|
7590
|
+
});
|
|
7591
|
+
}
|
|
7592
|
+
return hits;
|
|
7593
|
+
}
|
|
7594
|
+
function scanText(text) {
|
|
7595
|
+
const hits = [];
|
|
7596
|
+
const safeText = text || "";
|
|
7597
|
+
for (const rule of AI_PATTERN_RULES) {
|
|
7598
|
+
const regex = new RegExp(rule.pattern.source, rule.pattern.flags.replace("g", "") + "g");
|
|
7599
|
+
let match;
|
|
7600
|
+
let lastIndex = 0;
|
|
7601
|
+
while ((match = regex.exec(safeText)) !== null) {
|
|
7602
|
+
const snippet = match[0].trim();
|
|
7603
|
+
if (!snippet) {
|
|
7604
|
+
if (match.index === regex.lastIndex)
|
|
7605
|
+
regex.lastIndex++;
|
|
7606
|
+
continue;
|
|
7607
|
+
}
|
|
7608
|
+
const lineNo = safeText.slice(0, match.index).split("\n").length;
|
|
7609
|
+
hits.push({
|
|
7610
|
+
line: lineNo,
|
|
7611
|
+
rule: rule.id,
|
|
7612
|
+
phrase: snippet.slice(0, 160)
|
|
7613
|
+
});
|
|
7614
|
+
if (match.index === regex.lastIndex)
|
|
7615
|
+
regex.lastIndex++;
|
|
7616
|
+
}
|
|
7617
|
+
}
|
|
7618
|
+
const lines = safeText.split("\n");
|
|
7619
|
+
for (let i = 0; i < lines.length; i++) {
|
|
7620
|
+
const lineNum = i + 1;
|
|
7621
|
+
const lineText = lines[i];
|
|
7622
|
+
const patternHits = scanLine(lineText, lineNum, "inline");
|
|
7623
|
+
for (const hit of patternHits) {
|
|
7624
|
+
hits.push({ line: hit.line, rule: hit.pattern, severity: hit.severity, phrase: hit.excerpt || "", text: lineText.trim().slice(0, 120) });
|
|
7625
|
+
}
|
|
7626
|
+
const lineHits = lineStyleHits(lineText);
|
|
7627
|
+
for (const hit of lineHits) {
|
|
7628
|
+
hit.line = lineNum;
|
|
7629
|
+
hit.text = lineText.trim().slice(0, 240);
|
|
7630
|
+
hits.push(hit);
|
|
7631
|
+
}
|
|
7632
|
+
}
|
|
7633
|
+
const sentenceHits = [];
|
|
7634
|
+
for (let i = 0; i < lines.length; i++) {
|
|
7635
|
+
const lineSentences = lines[i].split(/(?<=[.!?])\s+/);
|
|
7636
|
+
for (const sentence of lineSentences) {
|
|
7637
|
+
const wordCount = words(sentence).length;
|
|
7638
|
+
if (wordCount > 0) {
|
|
7639
|
+
sentenceHits.push({ line: i + 1, text: sentence.trim(), wordCount });
|
|
7640
|
+
}
|
|
7641
|
+
}
|
|
7642
|
+
}
|
|
7643
|
+
for (let i = 0; i < sentenceHits.length - 2; i++) {
|
|
7644
|
+
const window = sentenceHits.slice(i, i + 3);
|
|
7645
|
+
if (window.every((s) => s.wordCount <= 5)) {
|
|
7646
|
+
hits.push({
|
|
7647
|
+
line: window[0].line,
|
|
7648
|
+
rule: "voice_staccato_triplet",
|
|
7649
|
+
phrase: "three short sentences in a row reads like performance",
|
|
7650
|
+
text: window[0].text
|
|
7651
|
+
});
|
|
7652
|
+
break;
|
|
7653
|
+
}
|
|
7654
|
+
}
|
|
7655
|
+
return hits.sort((a, b) => {
|
|
7656
|
+
if (a.line !== b.line)
|
|
7657
|
+
return a.line - b.line;
|
|
7658
|
+
return a.rule.localeCompare(b.rule);
|
|
7659
|
+
});
|
|
7660
|
+
}
|
|
7661
|
+
var AI_PATTERN_RULES, ABSTRACT_STYLE_WORDS, GENERIC_OPENERS, QUESTION_OPENER, LESSON_OPENER;
|
|
7662
|
+
var init_scan = __esm({
|
|
7663
|
+
"src/lib/scan.ts"() {
|
|
7664
|
+
"use strict";
|
|
7665
|
+
init_patterns();
|
|
7666
|
+
AI_PATTERN_RULES = [
|
|
7667
|
+
{
|
|
7668
|
+
id: "ai_antithesis",
|
|
7669
|
+
pattern: /\b(?:it'?s|isn'?t|is\s+not)\s+not\s+just\b.{0,80}\b(?:it'?s|but)\b/i
|
|
7670
|
+
},
|
|
7671
|
+
{
|
|
7672
|
+
id: "not_just_but",
|
|
7673
|
+
pattern: /\bnot\s+just\b.{3,80}\bbut\s+(?:also\s+)?/i
|
|
7674
|
+
},
|
|
7675
|
+
{
|
|
7676
|
+
id: "more_than_just",
|
|
7677
|
+
pattern: /\bmore\s+than\s+just\b/i
|
|
7678
|
+
},
|
|
7679
|
+
{
|
|
7680
|
+
id: "rhetorical_truth_setup",
|
|
7681
|
+
pattern: /\b(?:the\s+)?(?:uncomfortable|hard|harsh|brutal|ugly|unsexy|real|honest)\s+(?:truth|reality)\b/i
|
|
7682
|
+
},
|
|
7683
|
+
{
|
|
7684
|
+
id: "truth_is",
|
|
7685
|
+
pattern: /\bthe\s+truth\s+is\b/i
|
|
7686
|
+
},
|
|
7687
|
+
{
|
|
7688
|
+
id: "lesson_setup",
|
|
7689
|
+
pattern: /\b(?:here'?s\s+)?what\s+.{3,80}\s+(?:taught|teaches)\s+(?:me|us|you|everyone)\b/i
|
|
7690
|
+
},
|
|
7691
|
+
{
|
|
7692
|
+
id: "negation_cascade",
|
|
7693
|
+
pattern: /\b(?:no|not)\s+\w[^.!?\n]{0,80}[.!?][ \t]*\n?[ \t]*(?:no|not)\s+\w[^.!?\n]{0,80}[.!?][ \t]*\n?[ \t]*(?:no|not)\s+\w/i
|
|
7694
|
+
},
|
|
7695
|
+
{
|
|
7696
|
+
id: "formulaic_connector",
|
|
7697
|
+
pattern: /\b(?:firstly|secondly|thirdly|lastly|moreover|furthermore|in conclusion|to summarize|to sum up|in summary|it is important to note|it should be noted)\b/i
|
|
7698
|
+
},
|
|
7699
|
+
{
|
|
7700
|
+
id: "ai_words",
|
|
7701
|
+
pattern: /\b(?:delve|underscore|testament|intricate|multifaceted|cornerstone|landscape|foster|harness|leverage|tapestry|illuminate|pivotal|elevate|empower|seamlessly|revolutionize|supercharge|transformative|holistic|comprehensive|innovative|impactful|meaningful|utilize|paradigm|navigate|endeavor|realm|profound|encapsulate|synergy|robust|facilitate|bolster|streamline|differentiate|myriad|unlock|transform)\b/i
|
|
7702
|
+
},
|
|
7703
|
+
{
|
|
7704
|
+
id: "em_dash",
|
|
7705
|
+
pattern: /\u2014/
|
|
7706
|
+
},
|
|
7707
|
+
{
|
|
7708
|
+
id: "signpost_claim",
|
|
7709
|
+
pattern: /\bthis\s+is\s+(?:also\s+)?(?:why|how|where|what\b|what\s+happens\s+when)\b|\b(?:here'?s|here\s+is)\s+(?:where|why|what|the\s+part|the\s+(?:harder|real|actual|main|bigger)\s+problem)\b/i
|
|
7710
|
+
},
|
|
7711
|
+
{
|
|
7712
|
+
id: "generic_buyer_psychology",
|
|
7713
|
+
pattern: /\bpeople\s+don'?t\s+just\s+buy\b|\bpeople\s+buy\s+the\s+feeling\b/i
|
|
7714
|
+
},
|
|
7715
|
+
{
|
|
7716
|
+
id: "founder_cadence_restatement",
|
|
7717
|
+
pattern: /\bwhich\s+is\s+another\s+way\s+of\s+saying\b|\bin\s+other\s+words\b/i
|
|
7718
|
+
},
|
|
7719
|
+
{
|
|
7720
|
+
id: "founder_cadence_moment_becomes",
|
|
7721
|
+
pattern: /\b(?:the\s+)?moment\b.{3,80}\bbecomes?\b|\bbecomes?\s+(?:dangerous|useful|interesting|real|obvious)\s+the\s+moment\b/i
|
|
7722
|
+
},
|
|
7723
|
+
{
|
|
7724
|
+
id: "founder_cadence_same_better",
|
|
7725
|
+
pattern: /\bsame\s+[^.!?\n]{1,35}[.!?]\s*(?:better|nicer|cleaner|calmer|safer)\s+[^.!?\n]{1,35}[.!?]?/i
|
|
7726
|
+
}
|
|
7727
|
+
];
|
|
7728
|
+
ABSTRACT_STYLE_WORDS = /* @__PURE__ */ new Set([
|
|
7729
|
+
"alignment",
|
|
7730
|
+
"authenticity",
|
|
7731
|
+
"awareness",
|
|
7732
|
+
"clarity",
|
|
7733
|
+
"confidence",
|
|
7734
|
+
"consistency",
|
|
7735
|
+
"differentiation",
|
|
7736
|
+
"execution",
|
|
7737
|
+
"framework",
|
|
7738
|
+
"identity",
|
|
7739
|
+
"messaging",
|
|
7740
|
+
"narrative",
|
|
7741
|
+
"personality",
|
|
7742
|
+
"positioning",
|
|
7743
|
+
"preference",
|
|
7744
|
+
"presence",
|
|
7745
|
+
"recall",
|
|
7746
|
+
"relevance",
|
|
7747
|
+
"resonance",
|
|
7748
|
+
"signal",
|
|
7749
|
+
"strategy",
|
|
7750
|
+
"trust",
|
|
7751
|
+
"utility",
|
|
7752
|
+
"value"
|
|
7753
|
+
]);
|
|
7754
|
+
GENERIC_OPENERS = /^(?:most|many)\s+(?:brands|teams|people|founders|companies)\b/i;
|
|
7755
|
+
QUESTION_OPENER = /^(?:have you|do you|did you|what if|why do|how do)\b/i;
|
|
7756
|
+
LESSON_OPENER = /^(?:the most important thing|the key to|success is|if you want to|what i learned)\b/i;
|
|
7757
|
+
}
|
|
7758
|
+
});
|
|
7759
|
+
|
|
7760
|
+
// src/lib/welcome-flow.ts
|
|
7761
|
+
function readWelcomeState() {
|
|
7762
|
+
try {
|
|
7763
|
+
if (!fs11.existsSync(STATE_FILE)) {
|
|
7764
|
+
return { completed_steps: [], updated_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
7765
|
+
}
|
|
7766
|
+
return JSON.parse(fs11.readFileSync(STATE_FILE, "utf-8"));
|
|
7767
|
+
} catch {
|
|
7768
|
+
return { completed_steps: [], updated_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
7769
|
+
}
|
|
7770
|
+
}
|
|
7771
|
+
function writeWelcomeState(patch) {
|
|
7772
|
+
ensureHyvDir();
|
|
7773
|
+
const next = {
|
|
7774
|
+
...readWelcomeState(),
|
|
7775
|
+
...patch,
|
|
7776
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
7777
|
+
};
|
|
7778
|
+
fs11.writeFileSync(STATE_FILE, JSON.stringify(next, null, 2), { mode: 384 });
|
|
7779
|
+
return next;
|
|
7780
|
+
}
|
|
7781
|
+
function markStepComplete(step) {
|
|
7782
|
+
const state = readWelcomeState();
|
|
7783
|
+
if (!state.completed_steps.includes(step)) {
|
|
7784
|
+
writeWelcomeState({ completed_steps: [...state.completed_steps, step] });
|
|
7785
|
+
}
|
|
7786
|
+
}
|
|
7787
|
+
function divider() {
|
|
7788
|
+
return import_chalk11.default.dim(" " + "\u2500".repeat(44));
|
|
7789
|
+
}
|
|
7790
|
+
function formatFlowOverview() {
|
|
7791
|
+
const lines = [divider(), ""];
|
|
7792
|
+
for (const step of FLOW_STEPS) {
|
|
7793
|
+
lines.push(` ${import_chalk11.default.bold(String(step.n))} ${step.title}`);
|
|
7794
|
+
lines.push(import_chalk11.default.dim(` ${step.hint}`));
|
|
7795
|
+
lines.push("");
|
|
7796
|
+
}
|
|
7797
|
+
lines.push(divider());
|
|
7798
|
+
return lines.join("\n");
|
|
7799
|
+
}
|
|
7800
|
+
function buildVoiceExtractionPrompt(profileName) {
|
|
7801
|
+
return `You are helping set up Hold Your Voice for "${profileName}".
|
|
7802
|
+
|
|
7803
|
+
Read this user's messages in our conversation (and any writing they shared). Extract how they actually write \u2014 not a generic brand persona.
|
|
7804
|
+
|
|
7805
|
+
Return ONLY a markdown voice profile with these sections:
|
|
7806
|
+
|
|
7807
|
+
# Voice Profile: ${profileName}
|
|
7808
|
+
|
|
7809
|
+
## Voice keywords
|
|
7810
|
+
8\u201310 traits with a short note each
|
|
7811
|
+
|
|
7812
|
+
## Signature
|
|
7813
|
+
Sentence rhythm, typical openings, tone, formality
|
|
7814
|
+
|
|
7815
|
+
## Anchor passages
|
|
7816
|
+
3 short original-style lines written in their voice (invented but faithful)
|
|
7817
|
+
|
|
7818
|
+
## Never list
|
|
7819
|
+
Phrases that sound unlike them or generic AI slop
|
|
7820
|
+
|
|
7821
|
+
## Voice rules
|
|
7822
|
+
5 concrete rules for rewriting in their voice
|
|
7823
|
+
|
|
7824
|
+
Use lowercase section headings after the title. Be specific. No filler.`;
|
|
7825
|
+
}
|
|
7826
|
+
function buildStepGuide(step, profileName) {
|
|
7827
|
+
const name = profileName || readWelcomeState().profile_name || "<name>";
|
|
7828
|
+
switch (step) {
|
|
7829
|
+
case 1:
|
|
7830
|
+
return [
|
|
7831
|
+
"### Step 1 \u2014 name your profile",
|
|
7832
|
+
"",
|
|
7833
|
+
"Ask the user what to call their voice profile (short slug, e.g. `my-voice`).",
|
|
7834
|
+
"",
|
|
7835
|
+
`Then save locally: \`hyv import ${name} profile.md\` after step 2, or note the name for later.`
|
|
7836
|
+
].join("\n");
|
|
7837
|
+
case 2:
|
|
7838
|
+
return [
|
|
7839
|
+
"### Step 2 \u2014 add writing samples",
|
|
7840
|
+
"",
|
|
7841
|
+
"Pick one:",
|
|
7842
|
+
"- **paste** \u2014 user pastes newsletters, posts, or docs",
|
|
7843
|
+
"- **folder** \u2014 `hyv new <name> --from-samples ./writing`",
|
|
7844
|
+
"- **link** \u2014 user shares a URL; fetch readable text if you can",
|
|
7845
|
+
"- **from chat** \u2014 run the extraction prompt below on their prior messages",
|
|
7846
|
+
"",
|
|
7847
|
+
"**Extraction prompt (for chat / agent):**",
|
|
7848
|
+
"",
|
|
7849
|
+
buildVoiceExtractionPrompt(name),
|
|
7850
|
+
"",
|
|
7851
|
+
"Let the user edit the profile markdown, then save:",
|
|
7852
|
+
`\`hyv import ${name} ./voice-profile.md\``
|
|
7853
|
+
].join("\n");
|
|
7854
|
+
case 3:
|
|
7855
|
+
return [
|
|
7856
|
+
"### Step 3 \u2014 test on a draft",
|
|
7857
|
+
"",
|
|
7858
|
+
"Run a free local scan:",
|
|
7859
|
+
"`hyv scan draft.md` or MCP `hyv_scan` on pasted text.",
|
|
7860
|
+
"",
|
|
7861
|
+
"Optional rewrite (needs profile):",
|
|
7862
|
+
`\`hyv rewrite draft.md --profile ${name}\``,
|
|
7863
|
+
"",
|
|
7864
|
+
"Show score + top issues. Ask if it feels right."
|
|
7865
|
+
].join("\n");
|
|
7866
|
+
case 4:
|
|
7867
|
+
return [
|
|
7868
|
+
"### Step 4 \u2014 save & unlock",
|
|
7869
|
+
"",
|
|
7870
|
+
"When they are happy with the local profile:",
|
|
7871
|
+
"1. `hyv init` \u2014 browser signup",
|
|
7872
|
+
"2. `hyv plan --upgrade` \u2014 first month $1, unlocks learning + sync",
|
|
7873
|
+
"",
|
|
7874
|
+
`Pricing: ${PRICING_URL}`,
|
|
7875
|
+
"",
|
|
7876
|
+
"Local scan stays free forever."
|
|
7877
|
+
].join("\n");
|
|
7878
|
+
default:
|
|
7879
|
+
return buildWelcomeGuide({ profileName });
|
|
7880
|
+
}
|
|
7881
|
+
}
|
|
7882
|
+
function buildWelcomeGuide(opts = {}) {
|
|
7883
|
+
const lines = [
|
|
7884
|
+
WELCOME_TAGLINE,
|
|
7885
|
+
"",
|
|
7886
|
+
formatFlowOverview().replace(/\u001b\[[0-9;]*m/g, ""),
|
|
7887
|
+
""
|
|
7888
|
+
];
|
|
7889
|
+
if (opts.forLlm) {
|
|
7890
|
+
lines.push("## For agents \u2014 run these steps in order");
|
|
7891
|
+
lines.push("");
|
|
7892
|
+
for (let i = 1; i <= 4; i += 1) {
|
|
7893
|
+
lines.push(buildStepGuide(i, opts.profileName));
|
|
7894
|
+
lines.push("");
|
|
7895
|
+
}
|
|
7896
|
+
lines.push("Call `hyv_welcome` with `step: 2` and `mode: extract_prompt` for the chat extraction prompt.");
|
|
7897
|
+
return lines.join("\n");
|
|
7898
|
+
}
|
|
7899
|
+
const state = readWelcomeState();
|
|
7900
|
+
const name = opts.profileName || state.profile_name;
|
|
7901
|
+
if (name) {
|
|
7902
|
+
lines.push(`current profile: ${name}`);
|
|
7903
|
+
lines.push("");
|
|
7904
|
+
}
|
|
7905
|
+
lines.push("start in terminal:");
|
|
7906
|
+
lines.push(" hyv welcome walk through all 4 steps");
|
|
7907
|
+
lines.push(" hyv welcome --guide show this guide (for agents / scripts)");
|
|
7908
|
+
lines.push("");
|
|
7909
|
+
lines.push("or jump in:");
|
|
7910
|
+
lines.push(" hyv import <name> <file.md> save a profile");
|
|
7911
|
+
lines.push(" hyv scan draft.md test a draft");
|
|
7912
|
+
lines.push(" hyv init signup when ready");
|
|
7913
|
+
lines.push("");
|
|
7914
|
+
return lines.join("\n");
|
|
7915
|
+
}
|
|
7916
|
+
function buildWelcomeHeader(opts = {}) {
|
|
7917
|
+
if (opts.condensed) {
|
|
7918
|
+
return [
|
|
7919
|
+
WELCOME_TAGLINE,
|
|
7920
|
+
"",
|
|
7921
|
+
" hyv welcome set up your voice (4 steps)",
|
|
7922
|
+
" hyv scan test any draft free",
|
|
7923
|
+
""
|
|
7924
|
+
].join("\n");
|
|
7925
|
+
}
|
|
7926
|
+
return [WELCOME_TAGLINE, "", formatFlowOverview(), ""].join("\n");
|
|
7927
|
+
}
|
|
7928
|
+
function extractStats(samples) {
|
|
7929
|
+
const combined = samples.map((s) => s.text).join("\n\n");
|
|
7930
|
+
const allWords = words(combined);
|
|
7931
|
+
const allSentences = sentences(combined);
|
|
7932
|
+
const allParagraphs = paragraphs(combined);
|
|
7933
|
+
const sentenceLengths = allSentences.map((s) => words(s).length);
|
|
7934
|
+
const avgSentence = sentenceLengths.length ? Math.round(sentenceLengths.reduce((a, b) => a + b, 0) / sentenceLengths.length) : 0;
|
|
7935
|
+
const paragraphLengths = allParagraphs.map((p) => sentences(p).length);
|
|
7936
|
+
const avgParagraph = paragraphLengths.length ? Math.round(paragraphLengths.reduce((a, b) => a + b, 0) / paragraphLengths.length) : 0;
|
|
7937
|
+
const starters = allSentences.map((s) => {
|
|
7938
|
+
const match = s.trim().match(/[A-Za-z]/);
|
|
7939
|
+
return match ? match[0] : "";
|
|
7940
|
+
}).filter(Boolean);
|
|
7941
|
+
const lowerCount = starters.filter((c2) => c2 === c2.toLowerCase()).length;
|
|
7942
|
+
const caseStyle = starters.length && lowerCount / starters.length > 0.85 ? "mostly lowercase" : "standard";
|
|
7943
|
+
return {
|
|
7944
|
+
word_count: allWords.length,
|
|
7945
|
+
sentence_count: allSentences.length,
|
|
7946
|
+
paragraph_count: allParagraphs.length,
|
|
7947
|
+
avg_sentence_length: avgSentence,
|
|
7948
|
+
avg_paragraph_length: avgParagraph,
|
|
7949
|
+
case_style: caseStyle,
|
|
7950
|
+
sample_count: samples.length
|
|
7951
|
+
};
|
|
7952
|
+
}
|
|
7953
|
+
function profileMarkdownFromSamples(name, samples) {
|
|
7954
|
+
const stats = extractStats(samples);
|
|
7955
|
+
const excerpt = samples.map((s) => s.text.trim().slice(0, 400)).filter(Boolean).slice(0, 3).map((t, i) => `### sample ${i + 1}
|
|
7956
|
+
${t}${t.length >= 400 ? "\u2026" : ""}`).join("\n\n");
|
|
7957
|
+
return `# Voice Profile: ${name}
|
|
7958
|
+
|
|
7959
|
+
## Signature
|
|
7960
|
+
- average sentence: ${stats.avg_sentence_length} words
|
|
7961
|
+
- case style: ${stats.case_style}
|
|
7962
|
+
- analyzed ${stats.sample_count} sample(s), ${stats.word_count} words total
|
|
7963
|
+
|
|
7964
|
+
## Anchor passages
|
|
7965
|
+
${excerpt || "_add more samples to sharpen anchors_"}
|
|
7966
|
+
|
|
7967
|
+
## Never list
|
|
7968
|
+
- delve / dive in / landscape
|
|
7969
|
+
- it's important to note
|
|
7970
|
+
- holistic / robust / leverage
|
|
7971
|
+
|
|
7972
|
+
## Voice rules
|
|
7973
|
+
1. match the ${stats.avg_sentence_length}-word sentence rhythm
|
|
7974
|
+
2. keep ${stats.case_style} casing
|
|
7975
|
+
3. open with something concrete, not a throat-clear
|
|
7976
|
+
4. cut ai filler; keep the user's actual opinions
|
|
7977
|
+
5. rewrite should sound like the samples above
|
|
7978
|
+
|
|
7979
|
+
---
|
|
7980
|
+
_created via hyv welcome_
|
|
7981
|
+
`;
|
|
7982
|
+
}
|
|
7983
|
+
function profileMarkdownFromChat(name, extracted) {
|
|
7984
|
+
const body = extracted.trim();
|
|
7985
|
+
if (body.toLowerCase().includes("# voice profile"))
|
|
7986
|
+
return body;
|
|
7987
|
+
return `# Voice Profile: ${name}
|
|
7988
|
+
|
|
7989
|
+
${body}
|
|
7990
|
+
|
|
7991
|
+
---
|
|
7992
|
+
_created via hyv welcome (chat extract)_
|
|
7993
|
+
`;
|
|
7994
|
+
}
|
|
7995
|
+
function saveLocalProfile(name, content) {
|
|
7996
|
+
const safe = assertSafeProfileName(name);
|
|
7997
|
+
ensureHyvDir();
|
|
7998
|
+
writeCachedProfile(safe, content);
|
|
7999
|
+
const config = readConfig();
|
|
8000
|
+
writeConfig({ ...config, default_profile: safe, profile: safe });
|
|
8001
|
+
writeWelcomeState({ profile_name: safe });
|
|
8002
|
+
markStepComplete("name");
|
|
8003
|
+
markStepComplete("samples");
|
|
8004
|
+
return path12.join(os5.homedir(), ".hyv", "profiles", `${safe}.md`);
|
|
8005
|
+
}
|
|
8006
|
+
function collectSamplesFromFolder(dir) {
|
|
8007
|
+
const dirPath = path12.resolve(dir);
|
|
8008
|
+
if (!fs11.existsSync(dirPath))
|
|
8009
|
+
throw new Error(`folder not found: ${dirPath}`);
|
|
8010
|
+
const files = fs11.readdirSync(dirPath).filter((f) => /\.(md|txt|markdown)$/i.test(f)).map((f) => path12.join(dirPath, f));
|
|
8011
|
+
if (!files.length)
|
|
8012
|
+
throw new Error("no .md or .txt files in folder");
|
|
8013
|
+
const samples = [];
|
|
8014
|
+
for (const file of files) {
|
|
8015
|
+
const text = fs11.readFileSync(file, "utf-8");
|
|
8016
|
+
if (text.trim())
|
|
8017
|
+
samples.push({ path: file, text });
|
|
8018
|
+
}
|
|
8019
|
+
if (!samples.length)
|
|
8020
|
+
throw new Error("no readable text in folder");
|
|
8021
|
+
return samples;
|
|
8022
|
+
}
|
|
8023
|
+
function fetchUrlText(url) {
|
|
8024
|
+
return new Promise((resolve15, reject) => {
|
|
8025
|
+
let parsed;
|
|
8026
|
+
try {
|
|
8027
|
+
parsed = new URL(url);
|
|
8028
|
+
} catch {
|
|
8029
|
+
reject(new Error("invalid url"));
|
|
8030
|
+
return;
|
|
8031
|
+
}
|
|
8032
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
8033
|
+
reject(new Error("only http(s) links supported"));
|
|
8034
|
+
return;
|
|
8035
|
+
}
|
|
8036
|
+
const client = parsed.protocol === "https:" ? https2 : http2;
|
|
8037
|
+
const req = client.get(
|
|
8038
|
+
url,
|
|
8039
|
+
{ headers: { "User-Agent": "hyv-cli/welcome" }, timeout: 15e3 },
|
|
8040
|
+
(res) => {
|
|
8041
|
+
if ((res.statusCode || 0) >= 400) {
|
|
8042
|
+
reject(new Error(`http ${res.statusCode}`));
|
|
8043
|
+
return;
|
|
8044
|
+
}
|
|
8045
|
+
let data = "";
|
|
8046
|
+
res.on("data", (chunk) => {
|
|
8047
|
+
data += chunk;
|
|
8048
|
+
if (data.length > 5e5)
|
|
8049
|
+
req.destroy(new Error("response too large"));
|
|
8050
|
+
});
|
|
8051
|
+
res.on("end", () => {
|
|
8052
|
+
const text = data.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
8053
|
+
resolve15(text.slice(0, 12e3));
|
|
8054
|
+
});
|
|
8055
|
+
}
|
|
8056
|
+
);
|
|
8057
|
+
req.on("error", reject);
|
|
8058
|
+
req.on("timeout", () => {
|
|
8059
|
+
req.destroy();
|
|
8060
|
+
reject(new Error("request timeout"));
|
|
8061
|
+
});
|
|
8062
|
+
});
|
|
8063
|
+
}
|
|
8064
|
+
async function collectSamplesFromLink(url) {
|
|
8065
|
+
const text = await fetchUrlText(url);
|
|
8066
|
+
if (!text || words(text).length < 40) {
|
|
8067
|
+
throw new Error("could not extract enough text from link \u2014 try paste instead");
|
|
8068
|
+
}
|
|
8069
|
+
return [{ path: url, text }];
|
|
8070
|
+
}
|
|
8071
|
+
function askLine(prompt) {
|
|
8072
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
8073
|
+
return new Promise((resolve15) => {
|
|
8074
|
+
rl.question(prompt, (answer) => {
|
|
8075
|
+
rl.close();
|
|
8076
|
+
resolve15(answer.trim());
|
|
8077
|
+
});
|
|
8078
|
+
});
|
|
8079
|
+
}
|
|
8080
|
+
async function askMultiline(intro) {
|
|
8081
|
+
console.log(intro);
|
|
8082
|
+
console.log(import_chalk11.default.dim(" paste below, then type --- on its own line when done\n"));
|
|
8083
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
8084
|
+
const lines = [];
|
|
8085
|
+
return new Promise((resolve15) => {
|
|
8086
|
+
rl.on("line", (line) => {
|
|
8087
|
+
if (line.trim() === "---") {
|
|
8088
|
+
rl.close();
|
|
8089
|
+
resolve15(lines.join("\n").trim());
|
|
8090
|
+
return;
|
|
8091
|
+
}
|
|
8092
|
+
lines.push(line);
|
|
8093
|
+
});
|
|
8094
|
+
});
|
|
8095
|
+
}
|
|
8096
|
+
async function askYesNo(prompt, defaultNo = true) {
|
|
8097
|
+
const suffix = defaultNo ? " [y/N] " : " [Y/n] ";
|
|
8098
|
+
const answer = (await askLine(import_chalk11.default.cyan(prompt + suffix))).toLowerCase();
|
|
8099
|
+
if (!answer)
|
|
8100
|
+
return !defaultNo;
|
|
8101
|
+
return answer.startsWith("y");
|
|
8102
|
+
}
|
|
8103
|
+
function formatScanResult(text, profileName) {
|
|
8104
|
+
const result = runPipeline(text, profileName, false);
|
|
8105
|
+
const tells = result.signalMap.signals.slice(0, 3).map((s) => s.id).join(", ");
|
|
8106
|
+
const tail = tells ? ` \u2014 e.g. ${tells}` : "";
|
|
8107
|
+
return `${result.score}/100, ${result.signalMap.signals.length} issues${tail}`;
|
|
8108
|
+
}
|
|
8109
|
+
async function stepName() {
|
|
8110
|
+
console.log(import_chalk11.default.bold("\nstep 1 \xB7 name your profile\n"));
|
|
8111
|
+
const name = await askLine(import_chalk11.default.cyan(" profile name (e.g. my-voice): "));
|
|
8112
|
+
return assertSafeProfileName(name);
|
|
8113
|
+
}
|
|
8114
|
+
async function stepSamples(profileName) {
|
|
8115
|
+
console.log(import_chalk11.default.bold("\nstep 2 \xB7 add writing samples\n"));
|
|
8116
|
+
console.log(" how do you want to add samples?\n");
|
|
8117
|
+
console.log(import_chalk11.default.dim(" [1] paste text"));
|
|
8118
|
+
console.log(import_chalk11.default.dim(" [2] folder of files"));
|
|
8119
|
+
console.log(import_chalk11.default.dim(" [3] link to your writing"));
|
|
8120
|
+
console.log(import_chalk11.default.dim(" [4] extract from this chat (agent / llm)\n"));
|
|
8121
|
+
const choice = await askLine(import_chalk11.default.cyan(" choice [1-4]: ")) || "1";
|
|
8122
|
+
let content = "";
|
|
8123
|
+
if (choice === "2") {
|
|
8124
|
+
const folder = await askLine(import_chalk11.default.cyan(" folder path: "));
|
|
8125
|
+
const samples = collectSamplesFromFolder(folder);
|
|
8126
|
+
console.log(import_chalk11.default.dim(`
|
|
8127
|
+
read ${samples.length} file(s)`));
|
|
8128
|
+
content = profileMarkdownFromSamples(profileName, samples);
|
|
8129
|
+
writeWelcomeState({ samples_source: "folder" });
|
|
8130
|
+
} else if (choice === "3") {
|
|
8131
|
+
const url = await askLine(import_chalk11.default.cyan(" https://\u2026 "));
|
|
8132
|
+
try {
|
|
8133
|
+
const samples = await collectSamplesFromLink(url);
|
|
8134
|
+
console.log(import_chalk11.default.dim(`
|
|
8135
|
+
fetched ${words(samples[0].text).length} words`));
|
|
8136
|
+
content = profileMarkdownFromSamples(profileName, samples);
|
|
8137
|
+
writeWelcomeState({ samples_source: "link" });
|
|
8138
|
+
} catch (err) {
|
|
8139
|
+
console.log(import_chalk11.default.yellow(`
|
|
8140
|
+
${err.message}`));
|
|
8141
|
+
content = `# Voice Profile: ${profileName}
|
|
8142
|
+
|
|
8143
|
+
## source
|
|
8144
|
+
${url}
|
|
8145
|
+
|
|
8146
|
+
_Add pasted samples to train this profile._
|
|
8147
|
+
`;
|
|
8148
|
+
writeWelcomeState({ samples_source: "link" });
|
|
8149
|
+
}
|
|
8150
|
+
} else if (choice === "4") {
|
|
8151
|
+
console.log(import_chalk11.default.bold("\n extraction prompt \u2014 paste this into your llm / agent:\n"));
|
|
8152
|
+
console.log(import_chalk11.default.dim("\u2500".repeat(50)));
|
|
8153
|
+
console.log(buildVoiceExtractionPrompt(profileName));
|
|
8154
|
+
console.log(import_chalk11.default.dim("\u2500".repeat(50)));
|
|
8155
|
+
console.log("");
|
|
8156
|
+
const extracted = await askMultiline(" paste the profile markdown the agent returned:");
|
|
8157
|
+
if (!extracted)
|
|
8158
|
+
throw new Error("no profile content pasted");
|
|
8159
|
+
content = profileMarkdownFromChat(profileName, extracted);
|
|
8160
|
+
writeWelcomeState({ samples_source: "chat" });
|
|
8161
|
+
} else {
|
|
8162
|
+
const pasted = await askMultiline(" paste newsletters, posts, or any writing in your voice:");
|
|
8163
|
+
if (!pasted || words(pasted).length < 30) {
|
|
8164
|
+
throw new Error("need at least ~30 words of sample text");
|
|
8165
|
+
}
|
|
8166
|
+
content = profileMarkdownFromSamples(profileName, [{ path: "paste", text: pasted }]);
|
|
8167
|
+
writeWelcomeState({ samples_source: "paste" });
|
|
8168
|
+
}
|
|
8169
|
+
const saved = saveLocalProfile(profileName, content);
|
|
8170
|
+
console.log(import_chalk11.default.green(`
|
|
8171
|
+
\u2713 profile saved`));
|
|
8172
|
+
console.log(import_chalk11.default.dim(` ${saved}`));
|
|
8173
|
+
}
|
|
8174
|
+
async function stepTest(profileName) {
|
|
8175
|
+
console.log(import_chalk11.default.bold("\nstep 3 \xB7 test on a draft\n"));
|
|
8176
|
+
console.log(import_chalk11.default.dim(" paste a draft, or enter a file path (.md / .txt)\n"));
|
|
8177
|
+
const input = await askLine(import_chalk11.default.cyan(" draft or path: "));
|
|
8178
|
+
if (!input) {
|
|
8179
|
+
console.log(import_chalk11.default.dim(" skipped \u2014 run `hyv scan draft.md` anytime"));
|
|
8180
|
+
return;
|
|
7289
8181
|
}
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
8182
|
+
let text = input;
|
|
8183
|
+
const maybePath = path12.resolve(input);
|
|
8184
|
+
if (fs11.existsSync(maybePath) && fs11.statSync(maybePath).isFile()) {
|
|
8185
|
+
text = fs11.readFileSync(maybePath, "utf-8");
|
|
8186
|
+
}
|
|
8187
|
+
const summary = formatScanResult(text, profileName);
|
|
8188
|
+
console.log(import_chalk11.default.green(`
|
|
8189
|
+
scan: ${summary}`));
|
|
8190
|
+
console.log(import_chalk11.default.dim(` try: hyv rewrite ${input.includes("/") ? input : "draft.md"} --profile ${profileName}`));
|
|
8191
|
+
markStepComplete("test");
|
|
8192
|
+
}
|
|
8193
|
+
async function stepSignup(profileName) {
|
|
8194
|
+
console.log(import_chalk11.default.bold("\nstep 4 \xB7 save & unlock\n"));
|
|
8195
|
+
console.log(" local profile is ready. signup syncs it and unlocks learning.\n");
|
|
8196
|
+
console.log(import_chalk11.default.dim(` free forever: hyv scan, fix, check, mcp`));
|
|
8197
|
+
console.log(import_chalk11.default.dim(` paid: profiles that learn, hybrid rewrite, dashboard
|
|
8198
|
+
`));
|
|
8199
|
+
const ready = await askYesNo(" create your account now?");
|
|
8200
|
+
if (!ready) {
|
|
8201
|
+
console.log(import_chalk11.default.dim("\n whenever you are ready:"));
|
|
8202
|
+
console.log(import_chalk11.default.dim(" hyv init"));
|
|
8203
|
+
console.log(import_chalk11.default.dim(" hyv plan --upgrade"));
|
|
8204
|
+
console.log(import_chalk11.default.dim(` ${PRICING_URL}
|
|
8205
|
+
`));
|
|
8206
|
+
return;
|
|
7294
8207
|
}
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
8208
|
+
if (!isInitialized() || !getToken()) {
|
|
8209
|
+
console.log(import_chalk11.default.cyan("\n opening browser for signup...\n"));
|
|
8210
|
+
await authenticateWithBrowser();
|
|
8211
|
+
}
|
|
8212
|
+
const content = fs11.readFileSync(
|
|
8213
|
+
path12.join(os5.homedir(), ".hyv", "profiles", `${profileName}.md`),
|
|
8214
|
+
"utf-8"
|
|
8215
|
+
);
|
|
8216
|
+
try {
|
|
8217
|
+
const response = await authenticatedRequest(cliApiUrl("/cli/profiles/new"), {
|
|
8218
|
+
method: "POST",
|
|
8219
|
+
body: { name: profileName, content, source: "welcome" }
|
|
8220
|
+
});
|
|
8221
|
+
if (response.status === 200) {
|
|
8222
|
+
console.log(import_chalk11.default.green(" \u2713 profile synced to your account"));
|
|
8223
|
+
} else {
|
|
8224
|
+
console.log(import_chalk11.default.yellow(" profile saved locally \u2014 sync again with `hyv sync` after upgrade"));
|
|
8225
|
+
}
|
|
8226
|
+
} catch {
|
|
8227
|
+
console.log(import_chalk11.default.yellow(" profile saved locally \u2014 run `hyv sync` after you upgrade"));
|
|
8228
|
+
}
|
|
8229
|
+
console.log(import_chalk11.default.cyan("\n opening pricing..."));
|
|
8230
|
+
const { default: open3 } = await Promise.resolve().then(() => __toESM(require_open()));
|
|
8231
|
+
const { assertSafeOpenUrl: assertSafeOpenUrl2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
8232
|
+
await open3(assertSafeOpenUrl2(PRICING_URL));
|
|
8233
|
+
markStepComplete("signup");
|
|
8234
|
+
console.log(import_chalk11.default.dim("\n or run: hyv plan --upgrade\n"));
|
|
8235
|
+
}
|
|
8236
|
+
async function runInteractiveWelcome() {
|
|
8237
|
+
recordEvent("welcome_interactive");
|
|
8238
|
+
console.log("\n" + buildWelcomeHeader());
|
|
8239
|
+
try {
|
|
8240
|
+
const profileName = await stepName();
|
|
8241
|
+
await stepSamples(profileName);
|
|
8242
|
+
await stepTest(profileName);
|
|
8243
|
+
await stepSignup(profileName);
|
|
8244
|
+
console.log(import_chalk11.default.green("done \u2014 your voice profile is ready.\n"));
|
|
8245
|
+
console.log(import_chalk11.default.dim(" hyv scan draft.md"));
|
|
8246
|
+
console.log(import_chalk11.default.dim(` hyv rewrite draft.md --profile ${profileName}`));
|
|
8247
|
+
console.log(import_chalk11.default.dim(" hyv mcp --setup\n"));
|
|
8248
|
+
} catch (err) {
|
|
8249
|
+
console.error(import_chalk11.default.red(`
|
|
8250
|
+
${err.message || "welcome flow stopped"}
|
|
8251
|
+
`));
|
|
8252
|
+
console.log(import_chalk11.default.dim(" resume anytime: hyv welcome\n"));
|
|
8253
|
+
process.exitCode = 1;
|
|
7299
8254
|
}
|
|
7300
|
-
return { text: fs25.readFileSync(resolved, "utf-8"), path: resolved };
|
|
7301
8255
|
}
|
|
7302
|
-
|
|
7303
|
-
|
|
8256
|
+
function getMcpWelcomeResponse(args2) {
|
|
8257
|
+
if (args2.mode === "extract_prompt") {
|
|
8258
|
+
const name = args2.profile || readWelcomeState().profile_name || "my-voice";
|
|
8259
|
+
return buildVoiceExtractionPrompt(name);
|
|
8260
|
+
}
|
|
8261
|
+
if (args2.step && args2.step >= 1 && args2.step <= 4) {
|
|
8262
|
+
return [WELCOME_TAGLINE, "", buildStepGuide(args2.step, args2.profile)].join("\n");
|
|
8263
|
+
}
|
|
8264
|
+
return buildWelcomeGuide({ profileName: args2.profile, forLlm: true });
|
|
8265
|
+
}
|
|
8266
|
+
var import_chalk11, fs11, http2, https2, os5, path12, readline, WELCOME_TAGLINE, FLOW_STEPS, STATE_FILE;
|
|
8267
|
+
var init_welcome_flow = __esm({
|
|
8268
|
+
"src/lib/welcome-flow.ts"() {
|
|
7304
8269
|
"use strict";
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
8270
|
+
import_chalk11 = __toESM(require_source());
|
|
8271
|
+
fs11 = __toESM(require("fs"));
|
|
8272
|
+
http2 = __toESM(require("http"));
|
|
8273
|
+
https2 = __toESM(require("https"));
|
|
8274
|
+
os5 = __toESM(require("os"));
|
|
8275
|
+
path12 = __toESM(require("path"));
|
|
8276
|
+
readline = __toESM(require("readline"));
|
|
8277
|
+
init_pipeline();
|
|
8278
|
+
init_config();
|
|
8279
|
+
init_auth();
|
|
8280
|
+
init_scan();
|
|
8281
|
+
init_free_paid();
|
|
8282
|
+
init_telemetry();
|
|
8283
|
+
WELCOME_TAGLINE = "Hold Your Voice \u2014 make your AI agents sound exactly like you.";
|
|
8284
|
+
FLOW_STEPS = [
|
|
8285
|
+
{ n: 1, key: "name", title: "name your profile", hint: "pick a short name \u2014 e.g. my-voice" },
|
|
8286
|
+
{ n: 2, key: "samples", title: "add writing samples", hint: "paste, folder, link, or extract from chat" },
|
|
8287
|
+
{ n: 3, key: "test", title: "test on a draft", hint: "scan or rewrite to see it work" },
|
|
8288
|
+
{ n: 4, key: "signup", title: "save & unlock", hint: "signup syncs your profile and unlocks learning" }
|
|
8289
|
+
];
|
|
8290
|
+
STATE_FILE = path12.join(os5.homedir(), ".hyv", "welcome-state.json");
|
|
7309
8291
|
}
|
|
7310
8292
|
});
|
|
7311
8293
|
|
|
@@ -7313,11 +8295,13 @@ var init_pipeline = __esm({
|
|
|
7313
8295
|
var welcome_exports = {};
|
|
7314
8296
|
__export(welcome_exports, {
|
|
7315
8297
|
DEMO_TEXT: () => DEMO_TEXT,
|
|
7316
|
-
|
|
8298
|
+
FLOW_STEPS: () => FLOW_STEPS,
|
|
8299
|
+
WELCOME_TAGLINE: () => WELCOME_TAGLINE,
|
|
7317
8300
|
buildWelcomeMessage: () => buildWelcomeMessage,
|
|
7318
8301
|
formatCompactDemoResult: () => formatCompactDemoResult,
|
|
7319
8302
|
formatDemoResult: () => formatDemoResult,
|
|
7320
8303
|
formatFreeToolsList: () => formatFreeToolsList,
|
|
8304
|
+
plainFlowOverview: () => plainFlowOverview,
|
|
7321
8305
|
printWelcome: () => printWelcome,
|
|
7322
8306
|
runWelcomeDemo: () => runWelcomeDemo
|
|
7323
8307
|
});
|
|
@@ -7329,9 +8313,7 @@ function runWelcomeDemo() {
|
|
|
7329
8313
|
issueCount: signals.length,
|
|
7330
8314
|
red: result.stats.red,
|
|
7331
8315
|
yellow: result.stats.yellow,
|
|
7332
|
-
topIssues: signals.slice(0, 6).map(
|
|
7333
|
-
(s) => `line ${s.line}: ${s.id} \u2014 ${s.suggestion}`
|
|
7334
|
-
)
|
|
8316
|
+
topIssues: signals.slice(0, 6).map((s) => `line ${s.line}: ${s.id} \u2014 ${s.suggestion}`)
|
|
7335
8317
|
};
|
|
7336
8318
|
}
|
|
7337
8319
|
function formatDemoResult(demo) {
|
|
@@ -7351,7 +8333,7 @@ function formatCompactDemoResult(demo) {
|
|
|
7351
8333
|
return m ? m[1] : i.split(" \u2014 ")[0];
|
|
7352
8334
|
}).join(", ");
|
|
7353
8335
|
const tail = tells ? ` \u2014 e.g. ${tells}` : "";
|
|
7354
|
-
return `
|
|
8336
|
+
return `sample scan: ${demo.score}/100, ${demo.issueCount} issues${tail}`;
|
|
7355
8337
|
}
|
|
7356
8338
|
function formatFreeToolsList() {
|
|
7357
8339
|
const cli = FREE_CLI_COMMANDS.map((c2) => ` \u2022 ${c2}`).join("\n");
|
|
@@ -7370,54 +8352,48 @@ function formatFreeToolsList() {
|
|
|
7370
8352
|
].join("\n");
|
|
7371
8353
|
}
|
|
7372
8354
|
function buildWelcomeMessage(opts = {}) {
|
|
7373
|
-
if (opts.
|
|
7374
|
-
return
|
|
7375
|
-
"hold your voice \u2014 make your AI agents sound exactly like you.",
|
|
7376
|
-
"",
|
|
7377
|
-
" hyv scan draft.md test a draft",
|
|
7378
|
-
" hyv init build your voice",
|
|
7379
|
-
" hyv mcp --setup connect cursor / claude",
|
|
7380
|
-
"",
|
|
7381
|
-
` profiles + learning \u2192 ${PRICING_URL}`
|
|
7382
|
-
].join("\n");
|
|
8355
|
+
if (opts.guide) {
|
|
8356
|
+
return buildWelcomeGuide();
|
|
7383
8357
|
}
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
"",
|
|
7387
|
-
"4 steps:",
|
|
7388
|
-
""
|
|
7389
|
-
];
|
|
7390
|
-
for (const step of ONBOARDING_STEPS) {
|
|
7391
|
-
lines.push(` ${step.n}. ${step.title}`);
|
|
7392
|
-
lines.push(` ${step.cmd}`);
|
|
7393
|
-
lines.push(` ${step.hint}`);
|
|
7394
|
-
lines.push("");
|
|
8358
|
+
if (opts.condensed) {
|
|
8359
|
+
return buildWelcomeHeader({ condensed: true });
|
|
7395
8360
|
}
|
|
8361
|
+
const lines = [buildWelcomeHeader(), ""];
|
|
7396
8362
|
if (!opts.skipDemo) {
|
|
7397
8363
|
const demo = runWelcomeDemo();
|
|
7398
8364
|
recordEvent("welcome_demo", { score: demo.score, issues: demo.issueCount });
|
|
7399
|
-
lines.push(" try it now
|
|
8365
|
+
lines.push(" try it now");
|
|
7400
8366
|
lines.push(` ${formatCompactDemoResult(demo)}`);
|
|
7401
8367
|
lines.push(" hyv scan draft.md");
|
|
7402
8368
|
lines.push("");
|
|
7403
8369
|
}
|
|
7404
|
-
lines.push("
|
|
7405
|
-
lines.push(
|
|
7406
|
-
lines.push("
|
|
8370
|
+
lines.push(" set up your voice");
|
|
8371
|
+
lines.push(" hyv welcome");
|
|
8372
|
+
lines.push("");
|
|
8373
|
+
lines.push(` unlock learning \u2192 ${PRICING_URL}`);
|
|
7407
8374
|
lines.push("");
|
|
7408
8375
|
return lines.join("\n");
|
|
7409
8376
|
}
|
|
7410
8377
|
function printWelcome(opts = {}) {
|
|
7411
|
-
recordEvent("welcome_view", { condensed: !!opts.condensed });
|
|
8378
|
+
recordEvent("welcome_view", { condensed: !!opts.condensed, guide: !!opts.guide });
|
|
8379
|
+
if (opts.guide) {
|
|
8380
|
+
console.log("\n" + buildWelcomeGuide());
|
|
8381
|
+
return;
|
|
8382
|
+
}
|
|
7412
8383
|
console.log("\n" + buildWelcomeMessage(opts));
|
|
7413
8384
|
}
|
|
7414
|
-
|
|
8385
|
+
function plainFlowOverview() {
|
|
8386
|
+
return formatFlowOverview().replace(/\u001b\[[0-9;]*m/g, "");
|
|
8387
|
+
}
|
|
8388
|
+
var DEMO_TEXT;
|
|
7415
8389
|
var init_welcome = __esm({
|
|
7416
8390
|
"src/lib/welcome.ts"() {
|
|
7417
8391
|
"use strict";
|
|
7418
8392
|
init_pipeline();
|
|
7419
8393
|
init_free_paid();
|
|
7420
8394
|
init_telemetry();
|
|
8395
|
+
init_welcome_flow();
|
|
8396
|
+
init_welcome_flow();
|
|
7421
8397
|
DEMO_TEXT = `In today's fast-paced digital landscape, it's important to note that leveraging the right tools is not just nice to have, but essential for success. Let's delve into the holistic approach that will transform your workflow.
|
|
7422
8398
|
|
|
7423
8399
|
Firstly, you need to harness the power of seamless integration. Moreover, this robust ecosystem will foster innovation and unlock new opportunities for growth.
|
|
@@ -7425,12 +8401,6 @@ Firstly, you need to harness the power of seamless integration. Moreover, this r
|
|
|
7425
8401
|
Here's the uncomfortable truth: most teams are not equipped to handle the paradigm shift. But arguably, those who embrace this cutting-edge technology will have a game-changer advantage.
|
|
7426
8402
|
|
|
7427
8403
|
In conclusion, the key to success is to empower your team with actionable insights and foster a culture of continuous improvement.`;
|
|
7428
|
-
ONBOARDING_STEPS = [
|
|
7429
|
-
{ n: 1, title: "install", cmd: "npm i -g @holdyourvoice/hyv@latest", hint: "one command, works in every terminal and agent" },
|
|
7430
|
-
{ n: 2, title: "test a draft", cmd: "hyv scan draft.md", hint: "free local scan \u2014 flags ai tells offline" },
|
|
7431
|
-
{ n: 3, title: "build your voice", cmd: "hyv init", hint: "profile from your writing \u2014 unlocks rewrite + learning" },
|
|
7432
|
-
{ n: 4, title: "connect your agent", cmd: "hyv mcp --setup", hint: "cursor, claude code, codex \u2014 tools inside the chat" }
|
|
7433
|
-
];
|
|
7434
8404
|
}
|
|
7435
8405
|
});
|
|
7436
8406
|
|
|
@@ -8305,15 +9275,15 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
|
|
|
8305
9275
|
g.minimatch.escape = vi.escape;
|
|
8306
9276
|
g.minimatch.unescape = Ei.unescape;
|
|
8307
9277
|
});
|
|
8308
|
-
var
|
|
9278
|
+
var fs26 = R((Wt) => {
|
|
8309
9279
|
"use strict";
|
|
8310
9280
|
Object.defineProperty(Wt, "__esModule", { value: true });
|
|
8311
9281
|
Wt.LRUCache = void 0;
|
|
8312
9282
|
var er = typeof performance == "object" && performance && typeof performance.now == "function" ? performance : Date, as = /* @__PURE__ */ new Set(), ge = typeof process == "object" && process ? process : {}, ls = (n, t, e, s) => {
|
|
8313
9283
|
typeof ge.emitWarning == "function" ? ge.emitWarning(n, t, e, s) : console.error(`[${e}] ${t}: ${n}`);
|
|
8314
|
-
}, Lt = globalThis.AbortController,
|
|
9284
|
+
}, Lt = globalThis.AbortController, os10 = globalThis.AbortSignal;
|
|
8315
9285
|
if (typeof Lt > "u") {
|
|
8316
|
-
|
|
9286
|
+
os10 = class {
|
|
8317
9287
|
onabort;
|
|
8318
9288
|
_onabort = [];
|
|
8319
9289
|
reason;
|
|
@@ -8325,7 +9295,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
|
|
|
8325
9295
|
constructor() {
|
|
8326
9296
|
t();
|
|
8327
9297
|
}
|
|
8328
|
-
signal = new
|
|
9298
|
+
signal = new os10();
|
|
8329
9299
|
abort(e) {
|
|
8330
9300
|
if (!this.signal.aborted) {
|
|
8331
9301
|
this.signal.reason = e, this.signal.aborted = true;
|
|
@@ -9274,7 +10244,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
|
|
|
9274
10244
|
};
|
|
9275
10245
|
Object.defineProperty(_, "__esModule", { value: true });
|
|
9276
10246
|
_.PathScurry = _.Path = _.PathScurryDarwin = _.PathScurryPosix = _.PathScurryWin32 = _.PathScurryBase = _.PathPosix = _.PathWin32 = _.PathBase = _.ChildrenCache = _.ResolveCache = void 0;
|
|
9277
|
-
var Qt =
|
|
10247
|
+
var Qt = fs26(), Yt = require("node:path"), yr = require("node:url"), pt = require("fs"), Sr = br(require("node:fs")), vr = pt.realpathSync.native, Ht = require("node:fs/promises"), bs = Oe(), mt = { lstatSync: pt.lstatSync, readdir: pt.readdir, readdirSync: pt.readdirSync, readlinkSync: pt.readlinkSync, realpathSync: vr, promises: { lstat: Ht.lstat, readdir: Ht.readdir, readlink: Ht.readlink, realpath: Ht.realpath } }, _s = (n) => !n || n === mt || n === Sr ? mt : { ...mt, ...n, promises: { ...mt.promises, ...n.promises || {} } }, Os = /^\\\\\?\\([a-z]:)\\?$/i, Er = (n) => n.replace(/\//g, "\\").replace(Os, "$1\\"), _r = /[\\\/]/, N = 0, xs = 1, Ts = 2, G = 4, Cs = 6, Rs = 8, Q = 10, As = 12, j = 15, dt = ~j, xe = 16, ys = 32, gt = 64, W = 128, Vt = 256, Xt = 512, Ss = gt | W | Xt, Or = 1023, Te = (n) => n.isFile() ? Rs : n.isDirectory() ? G : n.isSymbolicLink() ? Q : n.isCharacterDevice() ? Ts : n.isBlockDevice() ? Cs : n.isSocket() ? As : n.isFIFO() ? xs : N, vs = new Qt.LRUCache({ max: 2 ** 12 }), wt = (n) => {
|
|
9278
10248
|
let t = vs.get(n);
|
|
9279
10249
|
if (t)
|
|
9280
10250
|
return t;
|
|
@@ -10793,7 +11763,7 @@ var {
|
|
|
10793
11763
|
} = import_index.default;
|
|
10794
11764
|
|
|
10795
11765
|
// src/index.ts
|
|
10796
|
-
var
|
|
11766
|
+
var import_chalk32 = __toESM(require_source());
|
|
10797
11767
|
|
|
10798
11768
|
// src/commands/init.ts
|
|
10799
11769
|
var import_chalk4 = __toESM(require_source());
|
|
@@ -10942,7 +11912,7 @@ function registerInitCommand(program3) {
|
|
|
10942
11912
|
const mcpResults = configureMcpForDesktop();
|
|
10943
11913
|
const { printPaidUnlockReminder: printPaidUnlockReminder2 } = await Promise.resolve().then(() => (init_marketing_hints(), marketing_hints_exports));
|
|
10944
11914
|
console.log("\nNext steps:");
|
|
10945
|
-
console.log(import_chalk4.default.dim(" 1.
|
|
11915
|
+
console.log(import_chalk4.default.dim(" 1. Set up your voice: hyv welcome"));
|
|
10946
11916
|
console.log(import_chalk4.default.dim(" 2. Scan a draft: hyv scan draft.md"));
|
|
10947
11917
|
console.log(import_chalk4.default.dim(" 3. Rewrite with voice: hyv rewrite draft.md"));
|
|
10948
11918
|
printPaidUnlockReminder2();
|
|
@@ -11482,435 +12452,77 @@ function registerRewriteCommand(program3) {
|
|
|
11482
12452
|
program3.command("rewrite").description("Generate rewrite prompt for LLM").argument("<file>", "File to rewrite (or - for stdin)").option("--profile <name>", "Voice profile to use").option("--format <type>", "Content format (linkedin, blog, email)").option("--constraints <text>", "Additional constraints").option("--no-scan", "Skip local scan").option("--output <file>", "Write prompt to file instead of stdout").option("--dry-run", "Show what profile data would be injected").action(async (file, options) => {
|
|
11483
12453
|
try {
|
|
11484
12454
|
const profile = await loadProfileForCommand(options.profile, { allowServerFetch: true });
|
|
11485
|
-
if (options.profile && !profile) {
|
|
11486
|
-
await requirePaidFeature("premiumPrompts");
|
|
11487
|
-
}
|
|
11488
|
-
const { text: draftContent, path: draftPath } = readText(file);
|
|
11489
|
-
const result = runPipeline(draftContent, profile, false);
|
|
11490
|
-
const promptResult = buildRewritePrompt({
|
|
11491
|
-
draftPath,
|
|
11492
|
-
draftContent,
|
|
11493
|
-
profileName: options.profile,
|
|
11494
|
-
profile,
|
|
11495
|
-
format: options.format,
|
|
11496
|
-
constraints: options.constraints,
|
|
11497
|
-
skipScan: options.scan === false
|
|
11498
|
-
});
|
|
11499
|
-
console.log(import_chalk9.default.dim(`
|
|
11500
|
-
Draft: ${draftPath}`));
|
|
11501
|
-
console.log(import_chalk9.default.dim(`Profile: ${promptResult.profileUsed || "none (using generic rules)"}`));
|
|
11502
|
-
if (profile && hasRichProfile(profile)) {
|
|
11503
|
-
console.log(import_chalk9.default.dim(` never-list: ${profile.never_list?.length || 0} learned: ${profile.learned_patterns?.length || 0}`));
|
|
11504
|
-
}
|
|
11505
|
-
console.log(import_chalk9.default.dim(`Issues found: ${result.stats.totalSignals}`));
|
|
11506
|
-
console.log(import_chalk9.default.dim(`Score: ${result.score}/100`));
|
|
11507
|
-
if (options.dryRun && profile) {
|
|
11508
|
-
console.log(import_chalk9.default.bold("\n--- Profile injection preview ---\n"));
|
|
11509
|
-
console.log(import_chalk9.default.dim(profile.body?.slice(0, 500) + (profile.body?.length > 500 ? "..." : "")));
|
|
11510
|
-
console.log("");
|
|
11511
|
-
}
|
|
11512
|
-
if (result.stats.totalSignals === 0) {
|
|
11513
|
-
console.log(import_chalk9.default.green("\n\u2713 No issues found. Draft looks clean!"));
|
|
11514
|
-
return;
|
|
11515
|
-
}
|
|
11516
|
-
console.log(import_chalk9.default.bold("\nIssues:"));
|
|
11517
|
-
for (const signal of result.signalMap.signals.slice(0, 10)) {
|
|
11518
|
-
const sev = signal.severity === "red" ? import_chalk9.default.red("\u25CF") : import_chalk9.default.yellow("\u25CB");
|
|
11519
|
-
const fix = signal.autoFixable ? import_chalk9.default.dim(" [auto-fixable]") : "";
|
|
11520
|
-
console.log(` ${sev} line ${signal.line}: ${import_chalk9.default.dim(signal.id)} \u2014 ${signal.suggestion}${fix}`);
|
|
11521
|
-
}
|
|
11522
|
-
if (result.signalMap.signals.length > 10) {
|
|
11523
|
-
console.log(import_chalk9.default.dim(` ... and ${result.signalMap.signals.length - 10} more`));
|
|
11524
|
-
}
|
|
11525
|
-
if (result.stats.autoFixed > 0) {
|
|
11526
|
-
console.log(import_chalk9.default.green(`
|
|
11527
|
-
${result.stats.autoFixed} issues can be auto-fixed \u2014 run: hyv fix ${file}`));
|
|
11528
|
-
}
|
|
11529
|
-
if (options.output) {
|
|
11530
|
-
const outputPath = path10.resolve(options.output);
|
|
11531
|
-
fs9.writeFileSync(outputPath, promptResult.prompt);
|
|
11532
|
-
console.log(import_chalk9.default.green(`
|
|
11533
|
-
\u2713 Prompt written to ${outputPath}`));
|
|
11534
|
-
} else {
|
|
11535
|
-
console.log(import_chalk9.default.bold("\n--- Rewrite Prompt ---\n"));
|
|
11536
|
-
console.log(promptResult.prompt);
|
|
11537
|
-
console.log(import_chalk9.default.dim("\n--- End Prompt ---\n"));
|
|
11538
|
-
console.log(import_chalk9.default.dim("Copy this prompt and paste it into your LLM."));
|
|
11539
|
-
console.log(import_chalk9.default.dim("Or pipe directly: hyv rewrite draft.md | pbcopy"));
|
|
11540
|
-
}
|
|
11541
|
-
await maybeShowLimitedModeHint(!!profile);
|
|
11542
|
-
} catch (error) {
|
|
11543
|
-
console.error(import_chalk9.default.red(`Error: ${error.message}`));
|
|
11544
|
-
process.exit(1);
|
|
11545
|
-
}
|
|
11546
|
-
});
|
|
11547
|
-
}
|
|
11548
|
-
|
|
11549
|
-
// src/commands/learning.ts
|
|
11550
|
-
var import_chalk10 = __toESM(require_source());
|
|
11551
|
-
var fs10 = __toESM(require("fs"));
|
|
11552
|
-
var path11 = __toESM(require("path"));
|
|
11553
|
-
|
|
11554
|
-
// src/lib/patterns.ts
|
|
11555
|
-
var AI_OVERUSED2 = [
|
|
11556
|
-
{ id: "ai.delve", category: "ai-slop", severity: "red", regex: /\bdelve\b/gi, suggestion: "use a specific verb: dig, explore, look at" },
|
|
11557
|
-
{ id: "ai.leverage", category: "ai-slop", severity: "red", regex: /\bleverage\b/gi, suggestion: "use: use, apply, build on" },
|
|
11558
|
-
{ id: "ai.utilize", category: "ai-slop", severity: "red", regex: /\butilize\b/gi, suggestion: "use: use" },
|
|
11559
|
-
{ id: "ai.tapestry", category: "ai-slop", severity: "red", regex: /\btapestry\b/gi, suggestion: "be specific about what you mean" },
|
|
11560
|
-
{ id: "ai.holistic", category: "ai-slop", severity: "red", regex: /\bholistic\b/gi, suggestion: "describe the actual approach" },
|
|
11561
|
-
{ id: "ai.robust", category: "ai-slop", severity: "yellow", regex: /\brobust\b/gi, suggestion: "say what actually makes it strong" },
|
|
11562
|
-
{ id: "ai.pivotal", category: "ai-slop", severity: "yellow", regex: /\bpivotal\b/gi, suggestion: "say why it matters specifically" },
|
|
11563
|
-
{ id: "ai.foster", category: "ai-slop", severity: "yellow", regex: /\bfoster\b/gi, suggestion: "use: build, grow, encourage, support" },
|
|
11564
|
-
{ id: "ai.harness", category: "ai-slop", severity: "yellow", regex: /\bharness\b/gi, suggestion: "use: use, apply, work with" },
|
|
11565
|
-
{ id: "ai.illuminate", category: "ai-slop", severity: "yellow", regex: /\billuminate\b/gi, suggestion: "use: show, explain, highlight" },
|
|
11566
|
-
{ id: "ai.ever-evolving", category: "ai-slop", severity: "red", regex: /\b(?:ever[\s-]evolving|ever[\s-]changing)\b/gi, suggestion: "cut this \u2014 it says nothing" },
|
|
11567
|
-
{ id: "ai.fast-paced", category: "ai-slop", severity: "red", regex: /\bfast[\s-]paced\b/gi, suggestion: "cut this \u2014 every industry says this" },
|
|
11568
|
-
{ id: "ai.game-changer", category: "ai-slop", severity: "red", regex: /\bgame[\s-]changer\b/gi, suggestion: "explain what actually changed" },
|
|
11569
|
-
{ id: "ai.paradigm", category: "ai-slop", severity: "red", regex: /\bparadigm\b/gi, suggestion: "describe the actual shift" },
|
|
11570
|
-
{ id: "ai.synergy", category: "ai-slop", severity: "red", regex: /\bsynergy\b/gi, suggestion: "describe what works together and why" },
|
|
11571
|
-
{ id: "ai.ecosystem", category: "ai-slop", severity: "yellow", regex: /\becosystem\b/gi, suggestion: "name the specific tools/partners/platforms" },
|
|
11572
|
-
{ id: "ai.seamless", category: "ai-slop", severity: "yellow", regex: /\bseamless\b/gi, suggestion: "describe how it actually works" },
|
|
11573
|
-
{ id: "ai.actionable", category: "ai-slop", severity: "yellow", regex: /\bactionable\b/gi, suggestion: "just give the action, don't label it" },
|
|
11574
|
-
{ id: "ai.granular", category: "ai-slop", severity: "yellow", regex: /\bgranular\b/gi, suggestion: "say: specific, detailed, or name the level" },
|
|
11575
|
-
{ id: "ai.impactful", category: "ai-slop", severity: "yellow", regex: /\bimpactful\b/gi, suggestion: "describe the actual impact" },
|
|
11576
|
-
{ id: "ai.landscape", category: "ai-slop", severity: "red", regex: /\blandscape\b/gi, suggestion: "name the specific market/field/area" },
|
|
11577
|
-
{ id: "ai.realm", category: "ai-slop", severity: "red", regex: /\brealm\b/gi, suggestion: "name the specific domain" },
|
|
11578
|
-
{ id: "ai.straightforward", category: "ai-slop", severity: "yellow", regex: /\bstraightforward\b/gi, suggestion: "just explain it directly" }
|
|
11579
|
-
];
|
|
11580
|
-
var FORMULAIC2 = [
|
|
11581
|
-
{ id: "formula.firstly", category: "ai-slop", severity: "yellow", regex: /\bfirstly\b/gi, suggestion: 'just start \u2014 "firstly" is filler' },
|
|
11582
|
-
{ id: "formula.secondly", category: "ai-slop", severity: "yellow", regex: /\bsecondly\b/gi, suggestion: 'just continue \u2014 "secondly" is filler' },
|
|
11583
|
-
{ id: "formula.lastly", category: "ai-slop", severity: "yellow", regex: /\blastly\b/gi, suggestion: 'just end \u2014 "lastly" is filler' },
|
|
11584
|
-
{ id: "formula.moreover", category: "ai-slop", severity: "yellow", regex: /\bmoreover\b/gi, suggestion: "just add the point" },
|
|
11585
|
-
{ id: "formula.furthermore", category: "ai-slop", severity: "yellow", regex: /\bfurthermore\b/gi, suggestion: "just add the point" },
|
|
11586
|
-
{ id: "formula.in-conclusion", category: "ai-slop", severity: "red", regex: /\bin conclusion\b/gi, suggestion: "just end. readers know it's the end." },
|
|
11587
|
-
{ id: "formula.in-summary", category: "ai-slop", severity: "red", regex: /\bin summary\b/gi, suggestion: "just summarize. the label is redundant." },
|
|
11588
|
-
{ id: "formula.it-is-important", category: "ai-slop", severity: "yellow", regex: /\bit is important to note\b/gi, suggestion: "just note it. skip the preamble." },
|
|
11589
|
-
{ id: "formula.at-the-end", category: "ai-slop", severity: "yellow", regex: /\bat the end of the day\b/gi, suggestion: "cut this \u2014 it means nothing" },
|
|
11590
|
-
{ id: "formula.needless-to-say", category: "ai-slop", severity: "yellow", regex: /\bneedless to say\b/gi, suggestion: "if it's needless, don't say it" },
|
|
11591
|
-
{ id: "formula.it-goes-without", category: "ai-slop", severity: "yellow", regex: /\bit goes without saying\b/gi, suggestion: "then don't say it" },
|
|
11592
|
-
{ id: "formula.in-today", category: "ai-slop", severity: "red", regex: /\bin today'?s\b/gi, suggestion: "start with your actual point instead" },
|
|
11593
|
-
{ id: "formula.lets-dive", category: "ai-slop", severity: "red", regex: /\blet'?s (?:dive|jump|dig|delve)\b/gi, suggestion: "just start. no diving needed." },
|
|
11594
|
-
{ id: "formula.without-further", category: "ai-slop", severity: "red", regex: /\bwithout further ado\b/gi, suggestion: "cut this \u2014 just get to it" },
|
|
11595
|
-
{ id: "formula.its-worth-noting", category: "ai-slop", severity: "yellow", regex: /\bit'?s worth noting\b/gi, suggestion: "just note it directly" },
|
|
11596
|
-
{ id: "formula.moving-forward", category: "ai-slop", severity: "yellow", regex: /\bmoving forward\b/gi, suggestion: "just say what happens next" },
|
|
11597
|
-
{ id: "formula.to-put-in-perspective", category: "ai-slop", severity: "yellow", regex: /\bto put this in perspective\b/gi, suggestion: "just give the perspective directly" },
|
|
11598
|
-
{ id: "formula.what-makes-interesting", category: "ai-slop", severity: "yellow", regex: /\bwhat makes this particularly interesting\b/gi, suggestion: "just say the interesting thing" },
|
|
11599
|
-
{ id: "formula.implications", category: "ai-slop", severity: "yellow", regex: /\bthe implications here are\b/gi, suggestion: "state the implications directly" },
|
|
11600
|
-
{ id: "formula.in-other-words", category: "ai-slop", severity: "red", regex: /\bin other words\b/gi, suggestion: "say it once, well" }
|
|
11601
|
-
];
|
|
11602
|
-
var HEDGING2 = [
|
|
11603
|
-
{ id: "hedge.some-might", category: "voice-drift", severity: "yellow", regex: /\bsome might say\b/gi, suggestion: "commit to the claim or drop it" },
|
|
11604
|
-
{ id: "hedge.arguably", category: "voice-drift", severity: "yellow", regex: /\barguably\b/gi, suggestion: "commit. say it or don't." },
|
|
11605
|
-
{ id: "hedge.worth-noting", category: "voice-drift", severity: "yellow", regex: /\bit'?s worth noting\b/gi, suggestion: "just note it directly" },
|
|
11606
|
-
{ id: "hedge.to-some-extent", category: "voice-drift", severity: "yellow", regex: /\bto some extent\b/gi, suggestion: "be specific about the extent" },
|
|
11607
|
-
{ id: "hedge.perhaps", category: "voice-drift", severity: "yellow", regex: /\bperhaps\b/gi, suggestion: "commit or cut" },
|
|
11608
|
-
{ id: "hedge.it-seems", category: "voice-drift", severity: "yellow", regex: /\bit seems\b/gi, suggestion: "state it directly" }
|
|
11609
|
-
];
|
|
11610
|
-
var STRUCTURE2 = [
|
|
11611
|
-
{ id: "struct.antithesis", category: "structure", severity: "yellow", regex: /\bnot (?:just|only) .{3,50}, but .{3,50}/gi, suggestion: "this antithesis pattern is an AI tell \u2014 restructure" },
|
|
11612
|
-
{ id: "struct.more-than-just", category: "structure", severity: "yellow", regex: /\bmore than just\b/gi, suggestion: "say what it IS, not what it isn't" },
|
|
11613
|
-
{ id: "struct.in-order-to", category: "structure", severity: "yellow", regex: /\bin order to\b/gi, suggestion: 'just use "to"' },
|
|
11614
|
-
{ id: "struct.due-to-the-fact", category: "structure", severity: "yellow", regex: /\bdue to the fact that\b/gi, suggestion: 'use "because"' },
|
|
11615
|
-
{ id: "struct.for-the-purpose", category: "structure", severity: "yellow", regex: /\bfor the purpose of\b/gi, suggestion: 'use "to"' },
|
|
11616
|
-
{ id: "struct.which-is-another", category: "structure", severity: "red", regex: /\bwhich is another way of saying\b/gi, suggestion: "just say the thing directly" },
|
|
11617
|
-
{ id: "struct.this-is-why", category: "structure", severity: "yellow", regex: /\bthis is (?:also )?(?:why|how|where|what\b)/gi, suggestion: "signpost claims are AI tells \u2014 just make the point" },
|
|
11618
|
-
{ id: "struct.heres-where", category: "structure", severity: "yellow", regex: /\bhere'?s (?:where|why|what|the part|the (?:harder|real|actual|main|bigger) problem)\b/gi, suggestion: "just make the point without the signpost" },
|
|
11619
|
-
{ id: "struct.rhetorical-truth", category: "structure", severity: "yellow", regex: /\b(?:the\s+)?(?:uncomfortable|hard|harsh|brutal|real|honest)\s+(?:truth|reality)\b/gi, suggestion: "state the fact directly, skip the framing" },
|
|
11620
|
-
{ id: "struct.lesson-setup", category: "structure", severity: "yellow", regex: /\bhere'?s what .{3,60} taught\b/gi, suggestion: "just share the lesson" },
|
|
11621
|
-
// THE BIG ONE — antithesis negation pattern (Voice DNA: FATAL)
|
|
11622
|
-
{ id: "struct.this-isnt-x-this-is-y", category: "structure", severity: "red", regex: /\bthis isn'?t .{2,40}\.?\s*(?:this is|it'?s) .{2,40}/gi, suggestion: "FATAL: delete the negation, just state the positive claim" },
|
|
11623
|
-
{ id: "struct.not-x-y", category: "structure", severity: "red", regex: /\bnot .{2,30}\.?\s*.{2,30}\b/gi, suggestion: 'the "Not X. Y." pattern is an AI tell \u2014 just state Y' },
|
|
11624
|
-
{ id: "struct.forget-x", category: "structure", severity: "red", regex: /\bforget .{2,40}\.?\s*(?:this is|it'?s|you need)/gi, suggestion: "don't negate \u2014 just state what you mean" }
|
|
11625
|
-
];
|
|
11626
|
-
var ENGAGEMENT_BAIT2 = [
|
|
11627
|
-
{ id: "bait.let-that-sink", category: "engagement-bait", severity: "red", regex: /\blet that sink in\b/gi, suggestion: "cut the sink. make your point and move on." },
|
|
11628
|
-
{ id: "bait.read-that-again", category: "engagement-bait", severity: "red", regex: /\bread that again\b/gi, suggestion: "if it needs repeating, repeat it yourself" },
|
|
11629
|
-
{ id: "bait.full-stop", category: "engagement-bait", severity: "red", regex: /\bfull stop\b/gi, suggestion: "the period already does this job" },
|
|
11630
|
-
{ id: "bait.this-changes-everything", category: "engagement-bait", severity: "red", regex: /\bthis changes everything\b/gi, suggestion: "prove it with specifics" },
|
|
11631
|
-
{ id: "bait.paying-attention", category: "engagement-bait", severity: "red", regex: /\bare you paying attention\b/gi, suggestion: "don't patronize the reader" },
|
|
11632
|
-
{ id: "bait.not-ready", category: "engagement-bait", severity: "red", regex: /\byou'?re not ready for this\b/gi, suggestion: "just deliver the content" }
|
|
11633
|
-
];
|
|
11634
|
-
var AI_CRINGE2 = [
|
|
11635
|
-
{ id: "cringe.supercharge", category: "ai-cringe", severity: "red", regex: /\bsupercharge\b/gi, suggestion: "describe what it actually does" },
|
|
11636
|
-
{ id: "cringe.unlock", category: "ai-cringe", severity: "yellow", regex: /\bunlock\b/gi, suggestion: "describe what they get access to" },
|
|
11637
|
-
{ id: "cringe.future-proof", category: "ai-cringe", severity: "red", regex: /\bfuture[\s-]proof\b/gi, suggestion: "explain what specifically makes it durable" },
|
|
11638
|
-
{ id: "cringe.10x", category: "ai-cringe", severity: "red", regex: /\b10x\b/gi, suggestion: "use the actual numbers" },
|
|
11639
|
-
{ id: "cringe.ai-revolution", category: "ai-cringe", severity: "red", regex: /\bthe ai revolution\b/gi, suggestion: "describe the specific change" },
|
|
11640
|
-
{ id: "cringe.age-of-ai", category: "ai-cringe", severity: "red", regex: /\bin the age of ai\b/gi, suggestion: "just talk about what's happening now" },
|
|
11641
|
-
{ id: "cringe.happy-to-help", category: "ai-cringe", severity: "red", regex: /\bi'?d be happy to help\b/gi, suggestion: "just help. don't announce it." }
|
|
11642
|
-
];
|
|
11643
|
-
var INSIDER_CLAIMS2 = [
|
|
11644
|
-
{ id: "insider.nobody-talking", category: "insider-claim", severity: "red", regex: /\bhere'?s the part nobody'?s? talking about\b/gi, suggestion: "just say the thing. the framing is noise." },
|
|
11645
|
-
{ id: "insider.nobody-tells", category: "insider-claim", severity: "red", regex: /\bwhat nobody tells you\b/gi, suggestion: "just tell them." },
|
|
11646
|
-
{ id: "insider.most-people", category: "insider-claim", severity: "yellow", regex: /\bmost people don'?t realize\b/gi, suggestion: "just explain it. skip the setup." },
|
|
11647
|
-
{ id: "insider.nobody-realizes", category: "insider-claim", severity: "red", regex: /\bnobody (?:realizes|talks about|mentions)\b/gi, suggestion: "if nobody talks about it, just talk about it" }
|
|
11648
|
-
];
|
|
11649
|
-
var FORMATTING = [
|
|
11650
|
-
{ id: "format.em-dash", category: "formatting", severity: "red", regex: /\u2014/g, suggestion: "NO em dashes. use commas, periods, colons, semicolons, or parentheses." }
|
|
11651
|
-
];
|
|
11652
|
-
var OGILVY2 = [
|
|
11653
|
-
// Jargon — words that hide lack of understanding
|
|
11654
|
-
{ id: "ogilvy.jargon-utilize", category: "ai-slop", severity: "red", regex: /\butilize[sd]?\b/gi, suggestion: 'Ogilvy: use "use" instead' },
|
|
11655
|
-
{ id: "ogilvy.jargon-leverage", category: "ai-slop", severity: "red", regex: /\bleverage[ds]?\b/gi, suggestion: "Ogilvy: say what you actually mean" },
|
|
11656
|
-
{ id: "ogilvy.jargon-synergy", category: "ai-slop", severity: "red", regex: /\bsynerg(?:y|ies|istic)\b/gi, suggestion: "Ogilvy: describe what works together and why" },
|
|
11657
|
-
{ id: "ogilvy.jargon-bandwidth", category: "ai-slop", severity: "yellow", regex: /\bbandwidth\b/gi, suggestion: 'Ogilvy: say "time" or "capacity"' },
|
|
11658
|
-
{ id: "ogilvy.jargon-circle-back", category: "ai-slop", severity: "red", regex: /\bcircle back\b/gi, suggestion: 'Ogilvy: say "follow up" or "talk later"' },
|
|
11659
|
-
{ id: "ogilvy.jargon-low-hanging", category: "ai-slop", severity: "red", regex: /\blow-hanging fruit\b/gi, suggestion: "Ogilvy: name the specific easy win" },
|
|
11660
|
-
{ id: "ogilvy.jargon-move-the-needle", category: "ai-slop", severity: "red", regex: /\bmove the needle\b/gi, suggestion: "Ogilvy: describe the actual impact" },
|
|
11661
|
-
{ id: "ogilvy.jargon-touch-base", category: "ai-slop", severity: "red", regex: /\btouch base\b/gi, suggestion: 'Ogilvy: say "talk" or "meet"' },
|
|
11662
|
-
{ id: "ogilvy.jargon-take-it-offline", category: "ai-slop", severity: "red", regex: /\btake it offline\b/gi, suggestion: 'Ogilvy: say "discuss later"' },
|
|
11663
|
-
{ id: "ogilvy.jargon-deep-dive", category: "ai-slop", severity: "yellow", regex: /\bdeep dive\b/gi, suggestion: 'Ogilvy: say "look closely at" or "examine"' },
|
|
11664
|
-
// Throat-clearing openers
|
|
11665
|
-
{ id: "ogilvy.preamble-i-want-to", category: "voice-drift", severity: "yellow", regex: /^\s*i want to (?:share|talk about|discuss|mention)\b/gi, suggestion: "Ogilvy: just say it. skip the preamble." },
|
|
11666
|
-
{ id: "ogilvy.preamble-just-wanted", category: "voice-drift", severity: "yellow", regex: /^\s*(?:i just wanted|i wanted to)\b/gi, suggestion: "Ogilvy: just say it." }
|
|
11667
|
-
];
|
|
11668
|
-
var ALL_PATTERNS = [
|
|
11669
|
-
...AI_OVERUSED2,
|
|
11670
|
-
...FORMULAIC2,
|
|
11671
|
-
...HEDGING2,
|
|
11672
|
-
...STRUCTURE2,
|
|
11673
|
-
...ENGAGEMENT_BAIT2,
|
|
11674
|
-
...AI_CRINGE2,
|
|
11675
|
-
...INSIDER_CLAIMS2,
|
|
11676
|
-
...FORMATTING,
|
|
11677
|
-
...OGILVY2
|
|
11678
|
-
];
|
|
11679
|
-
function scanLine(line, lineNum, filePath) {
|
|
11680
|
-
const findings = [];
|
|
11681
|
-
for (const pat of ALL_PATTERNS) {
|
|
11682
|
-
pat.regex.lastIndex = 0;
|
|
11683
|
-
let match;
|
|
11684
|
-
while ((match = pat.regex.exec(line)) !== null) {
|
|
11685
|
-
findings.push({
|
|
11686
|
-
file: filePath,
|
|
11687
|
-
line: lineNum,
|
|
11688
|
-
column: match.index + 1,
|
|
11689
|
-
pattern: pat.id,
|
|
11690
|
-
category: pat.category,
|
|
11691
|
-
severity: pat.severity,
|
|
11692
|
-
excerpt: highlightMatch2(line.trim(), match.index, match[0].length),
|
|
11693
|
-
suggestion: pat.suggestion
|
|
11694
|
-
});
|
|
11695
|
-
}
|
|
11696
|
-
}
|
|
11697
|
-
return findings;
|
|
11698
|
-
}
|
|
11699
|
-
function highlightMatch2(line, start, len) {
|
|
11700
|
-
const before = line.slice(0, start);
|
|
11701
|
-
const match = line.slice(start, start + len);
|
|
11702
|
-
const after = line.slice(start + len);
|
|
11703
|
-
return `${before}\xAB${match}\xBB${after}`.slice(0, 120);
|
|
11704
|
-
}
|
|
11705
|
-
|
|
11706
|
-
// src/lib/scan.ts
|
|
11707
|
-
var AI_PATTERN_RULES = [
|
|
11708
|
-
{
|
|
11709
|
-
id: "ai_antithesis",
|
|
11710
|
-
pattern: /\b(?:it'?s|isn'?t|is\s+not)\s+not\s+just\b.{0,80}\b(?:it'?s|but)\b/i
|
|
11711
|
-
},
|
|
11712
|
-
{
|
|
11713
|
-
id: "not_just_but",
|
|
11714
|
-
pattern: /\bnot\s+just\b.{3,80}\bbut\s+(?:also\s+)?/i
|
|
11715
|
-
},
|
|
11716
|
-
{
|
|
11717
|
-
id: "more_than_just",
|
|
11718
|
-
pattern: /\bmore\s+than\s+just\b/i
|
|
11719
|
-
},
|
|
11720
|
-
{
|
|
11721
|
-
id: "rhetorical_truth_setup",
|
|
11722
|
-
pattern: /\b(?:the\s+)?(?:uncomfortable|hard|harsh|brutal|ugly|unsexy|real|honest)\s+(?:truth|reality)\b/i
|
|
11723
|
-
},
|
|
11724
|
-
{
|
|
11725
|
-
id: "truth_is",
|
|
11726
|
-
pattern: /\bthe\s+truth\s+is\b/i
|
|
11727
|
-
},
|
|
11728
|
-
{
|
|
11729
|
-
id: "lesson_setup",
|
|
11730
|
-
pattern: /\b(?:here'?s\s+)?what\s+.{3,80}\s+(?:taught|teaches)\s+(?:me|us|you|everyone)\b/i
|
|
11731
|
-
},
|
|
11732
|
-
{
|
|
11733
|
-
id: "negation_cascade",
|
|
11734
|
-
pattern: /\b(?:no|not)\s+\w[^.!?\n]{0,80}[.!?][ \t]*\n?[ \t]*(?:no|not)\s+\w[^.!?\n]{0,80}[.!?][ \t]*\n?[ \t]*(?:no|not)\s+\w/i
|
|
11735
|
-
},
|
|
11736
|
-
{
|
|
11737
|
-
id: "formulaic_connector",
|
|
11738
|
-
pattern: /\b(?:firstly|secondly|thirdly|lastly|moreover|furthermore|in conclusion|to summarize|to sum up|in summary|it is important to note|it should be noted)\b/i
|
|
11739
|
-
},
|
|
11740
|
-
{
|
|
11741
|
-
id: "ai_words",
|
|
11742
|
-
pattern: /\b(?:delve|underscore|testament|intricate|multifaceted|cornerstone|landscape|foster|harness|leverage|tapestry|illuminate|pivotal|elevate|empower|seamlessly|revolutionize|supercharge|transformative|holistic|comprehensive|innovative|impactful|meaningful|utilize|paradigm|navigate|endeavor|realm|profound|encapsulate|synergy|robust|facilitate|bolster|streamline|differentiate|myriad|unlock|transform)\b/i
|
|
11743
|
-
},
|
|
11744
|
-
{
|
|
11745
|
-
id: "em_dash",
|
|
11746
|
-
pattern: /\u2014/
|
|
11747
|
-
},
|
|
11748
|
-
{
|
|
11749
|
-
id: "signpost_claim",
|
|
11750
|
-
pattern: /\bthis\s+is\s+(?:also\s+)?(?:why|how|where|what\b|what\s+happens\s+when)\b|\b(?:here'?s|here\s+is)\s+(?:where|why|what|the\s+part|the\s+(?:harder|real|actual|main|bigger)\s+problem)\b/i
|
|
11751
|
-
},
|
|
11752
|
-
{
|
|
11753
|
-
id: "generic_buyer_psychology",
|
|
11754
|
-
pattern: /\bpeople\s+don'?t\s+just\s+buy\b|\bpeople\s+buy\s+the\s+feeling\b/i
|
|
11755
|
-
},
|
|
11756
|
-
{
|
|
11757
|
-
id: "founder_cadence_restatement",
|
|
11758
|
-
pattern: /\bwhich\s+is\s+another\s+way\s+of\s+saying\b|\bin\s+other\s+words\b/i
|
|
11759
|
-
},
|
|
11760
|
-
{
|
|
11761
|
-
id: "founder_cadence_moment_becomes",
|
|
11762
|
-
pattern: /\b(?:the\s+)?moment\b.{3,80}\bbecomes?\b|\bbecomes?\s+(?:dangerous|useful|interesting|real|obvious)\s+the\s+moment\b/i
|
|
11763
|
-
},
|
|
11764
|
-
{
|
|
11765
|
-
id: "founder_cadence_same_better",
|
|
11766
|
-
pattern: /\bsame\s+[^.!?\n]{1,35}[.!?]\s*(?:better|nicer|cleaner|calmer|safer)\s+[^.!?\n]{1,35}[.!?]?/i
|
|
11767
|
-
}
|
|
11768
|
-
];
|
|
11769
|
-
var ABSTRACT_STYLE_WORDS = /* @__PURE__ */ new Set([
|
|
11770
|
-
"alignment",
|
|
11771
|
-
"authenticity",
|
|
11772
|
-
"awareness",
|
|
11773
|
-
"clarity",
|
|
11774
|
-
"confidence",
|
|
11775
|
-
"consistency",
|
|
11776
|
-
"differentiation",
|
|
11777
|
-
"execution",
|
|
11778
|
-
"framework",
|
|
11779
|
-
"identity",
|
|
11780
|
-
"messaging",
|
|
11781
|
-
"narrative",
|
|
11782
|
-
"personality",
|
|
11783
|
-
"positioning",
|
|
11784
|
-
"preference",
|
|
11785
|
-
"presence",
|
|
11786
|
-
"recall",
|
|
11787
|
-
"relevance",
|
|
11788
|
-
"resonance",
|
|
11789
|
-
"signal",
|
|
11790
|
-
"strategy",
|
|
11791
|
-
"trust",
|
|
11792
|
-
"utility",
|
|
11793
|
-
"value"
|
|
11794
|
-
]);
|
|
11795
|
-
var GENERIC_OPENERS = /^(?:most|many)\s+(?:brands|teams|people|founders|companies)\b/i;
|
|
11796
|
-
var QUESTION_OPENER = /^(?:have you|do you|did you|what if|why do|how do)\b/i;
|
|
11797
|
-
var LESSON_OPENER = /^(?:the most important thing|the key to|success is|if you want to|what i learned)\b/i;
|
|
11798
|
-
function words(text) {
|
|
11799
|
-
return (text || "").match(/[a-zA-Z][a-zA-Z0-9']*/g) || [];
|
|
11800
|
-
}
|
|
11801
|
-
function sentences(text) {
|
|
11802
|
-
return (text || "").split(/(?<=[.!?])\s+|\n{2,}/).map((s) => s.trim()).filter((s) => words(s).length > 0);
|
|
11803
|
-
}
|
|
11804
|
-
function paragraphs(text) {
|
|
11805
|
-
return (text || "").split(/\n\s*\n/).map((p) => p.trim()).filter((p) => words(p).length >= 6);
|
|
11806
|
-
}
|
|
11807
|
-
function lineStyleHits(line) {
|
|
11808
|
-
const low = (line || "").trim().toLowerCase();
|
|
11809
|
-
if (!low)
|
|
11810
|
-
return [];
|
|
11811
|
-
const hits = [];
|
|
11812
|
-
const lineWords = low.match(/[a-z']+/g) || [];
|
|
11813
|
-
const abstractCount = lineWords.filter((w) => ABSTRACT_STYLE_WORDS.has(w)).length;
|
|
11814
|
-
if (abstractCount >= 3 && !/\b(?:for example|for instance|such as)\b|\d/i.test(low)) {
|
|
11815
|
-
hits.push({
|
|
11816
|
-
line: 0,
|
|
11817
|
-
// Will be set by caller
|
|
11818
|
-
rule: "abstract_noun_cluster",
|
|
11819
|
-
phrase: line.trim().slice(0, 160)
|
|
11820
|
-
});
|
|
11821
|
-
}
|
|
11822
|
-
if (GENERIC_OPENERS.test(low)) {
|
|
11823
|
-
hits.push({
|
|
11824
|
-
line: 0,
|
|
11825
|
-
rule: "generic_opening_generalization",
|
|
11826
|
-
phrase: line.trim().slice(0, 160)
|
|
11827
|
-
});
|
|
11828
|
-
}
|
|
11829
|
-
if (QUESTION_OPENER.test(low)) {
|
|
11830
|
-
hits.push({
|
|
11831
|
-
line: 0,
|
|
11832
|
-
rule: "voice_question_opener",
|
|
11833
|
-
phrase: "opens with a question instead of a concrete observation"
|
|
11834
|
-
});
|
|
11835
|
-
}
|
|
11836
|
-
if (LESSON_OPENER.test(low)) {
|
|
11837
|
-
hits.push({
|
|
11838
|
-
line: 0,
|
|
11839
|
-
rule: "voice_lesson_opener",
|
|
11840
|
-
phrase: "opens with a lesson or inspirational claim"
|
|
11841
|
-
});
|
|
11842
|
-
}
|
|
11843
|
-
return hits;
|
|
11844
|
-
}
|
|
11845
|
-
function scanText(text) {
|
|
11846
|
-
const hits = [];
|
|
11847
|
-
const safeText = text || "";
|
|
11848
|
-
for (const rule of AI_PATTERN_RULES) {
|
|
11849
|
-
const regex = new RegExp(rule.pattern.source, rule.pattern.flags.replace("g", "") + "g");
|
|
11850
|
-
let match;
|
|
11851
|
-
let lastIndex = 0;
|
|
11852
|
-
while ((match = regex.exec(safeText)) !== null) {
|
|
11853
|
-
const snippet = match[0].trim();
|
|
11854
|
-
if (!snippet) {
|
|
11855
|
-
if (match.index === regex.lastIndex)
|
|
11856
|
-
regex.lastIndex++;
|
|
11857
|
-
continue;
|
|
12455
|
+
if (options.profile && !profile) {
|
|
12456
|
+
await requirePaidFeature("premiumPrompts");
|
|
11858
12457
|
}
|
|
11859
|
-
const
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
12458
|
+
const { text: draftContent, path: draftPath } = readText(file);
|
|
12459
|
+
const result = runPipeline(draftContent, profile, false);
|
|
12460
|
+
const promptResult = buildRewritePrompt({
|
|
12461
|
+
draftPath,
|
|
12462
|
+
draftContent,
|
|
12463
|
+
profileName: options.profile,
|
|
12464
|
+
profile,
|
|
12465
|
+
format: options.format,
|
|
12466
|
+
constraints: options.constraints,
|
|
12467
|
+
skipScan: options.scan === false
|
|
11864
12468
|
});
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
for (let i = 0; i < lines.length; i++) {
|
|
11871
|
-
const lineNum = i + 1;
|
|
11872
|
-
const lineText = lines[i];
|
|
11873
|
-
const patternHits = scanLine(lineText, lineNum, "inline");
|
|
11874
|
-
for (const hit of patternHits) {
|
|
11875
|
-
hits.push({ line: hit.line, rule: hit.pattern, severity: hit.severity, phrase: hit.excerpt || "", text: lineText.trim().slice(0, 120) });
|
|
11876
|
-
}
|
|
11877
|
-
const lineHits = lineStyleHits(lineText);
|
|
11878
|
-
for (const hit of lineHits) {
|
|
11879
|
-
hit.line = lineNum;
|
|
11880
|
-
hit.text = lineText.trim().slice(0, 240);
|
|
11881
|
-
hits.push(hit);
|
|
11882
|
-
}
|
|
11883
|
-
}
|
|
11884
|
-
const sentenceHits = [];
|
|
11885
|
-
for (let i = 0; i < lines.length; i++) {
|
|
11886
|
-
const lineSentences = lines[i].split(/(?<=[.!?])\s+/);
|
|
11887
|
-
for (const sentence of lineSentences) {
|
|
11888
|
-
const wordCount = words(sentence).length;
|
|
11889
|
-
if (wordCount > 0) {
|
|
11890
|
-
sentenceHits.push({ line: i + 1, text: sentence.trim(), wordCount });
|
|
12469
|
+
console.log(import_chalk9.default.dim(`
|
|
12470
|
+
Draft: ${draftPath}`));
|
|
12471
|
+
console.log(import_chalk9.default.dim(`Profile: ${promptResult.profileUsed || "none (using generic rules)"}`));
|
|
12472
|
+
if (profile && hasRichProfile(profile)) {
|
|
12473
|
+
console.log(import_chalk9.default.dim(` never-list: ${profile.never_list?.length || 0} learned: ${profile.learned_patterns?.length || 0}`));
|
|
11891
12474
|
}
|
|
12475
|
+
console.log(import_chalk9.default.dim(`Issues found: ${result.stats.totalSignals}`));
|
|
12476
|
+
console.log(import_chalk9.default.dim(`Score: ${result.score}/100`));
|
|
12477
|
+
if (options.dryRun && profile) {
|
|
12478
|
+
console.log(import_chalk9.default.bold("\n--- Profile injection preview ---\n"));
|
|
12479
|
+
console.log(import_chalk9.default.dim(profile.body?.slice(0, 500) + (profile.body?.length > 500 ? "..." : "")));
|
|
12480
|
+
console.log("");
|
|
12481
|
+
}
|
|
12482
|
+
if (result.stats.totalSignals === 0) {
|
|
12483
|
+
console.log(import_chalk9.default.green("\n\u2713 No issues found. Draft looks clean!"));
|
|
12484
|
+
return;
|
|
12485
|
+
}
|
|
12486
|
+
console.log(import_chalk9.default.bold("\nIssues:"));
|
|
12487
|
+
for (const signal of result.signalMap.signals.slice(0, 10)) {
|
|
12488
|
+
const sev = signal.severity === "red" ? import_chalk9.default.red("\u25CF") : import_chalk9.default.yellow("\u25CB");
|
|
12489
|
+
const fix = signal.autoFixable ? import_chalk9.default.dim(" [auto-fixable]") : "";
|
|
12490
|
+
console.log(` ${sev} line ${signal.line}: ${import_chalk9.default.dim(signal.id)} \u2014 ${signal.suggestion}${fix}`);
|
|
12491
|
+
}
|
|
12492
|
+
if (result.signalMap.signals.length > 10) {
|
|
12493
|
+
console.log(import_chalk9.default.dim(` ... and ${result.signalMap.signals.length - 10} more`));
|
|
12494
|
+
}
|
|
12495
|
+
if (result.stats.autoFixed > 0) {
|
|
12496
|
+
console.log(import_chalk9.default.green(`
|
|
12497
|
+
${result.stats.autoFixed} issues can be auto-fixed \u2014 run: hyv fix ${file}`));
|
|
12498
|
+
}
|
|
12499
|
+
if (options.output) {
|
|
12500
|
+
const outputPath = path10.resolve(options.output);
|
|
12501
|
+
fs9.writeFileSync(outputPath, promptResult.prompt);
|
|
12502
|
+
console.log(import_chalk9.default.green(`
|
|
12503
|
+
\u2713 Prompt written to ${outputPath}`));
|
|
12504
|
+
} else {
|
|
12505
|
+
console.log(import_chalk9.default.bold("\n--- Rewrite Prompt ---\n"));
|
|
12506
|
+
console.log(promptResult.prompt);
|
|
12507
|
+
console.log(import_chalk9.default.dim("\n--- End Prompt ---\n"));
|
|
12508
|
+
console.log(import_chalk9.default.dim("Copy this prompt and paste it into your LLM."));
|
|
12509
|
+
console.log(import_chalk9.default.dim("Or pipe directly: hyv rewrite draft.md | pbcopy"));
|
|
12510
|
+
}
|
|
12511
|
+
await maybeShowLimitedModeHint(!!profile);
|
|
12512
|
+
} catch (error) {
|
|
12513
|
+
console.error(import_chalk9.default.red(`Error: ${error.message}`));
|
|
12514
|
+
process.exit(1);
|
|
11892
12515
|
}
|
|
11893
|
-
}
|
|
11894
|
-
for (let i = 0; i < sentenceHits.length - 2; i++) {
|
|
11895
|
-
const window = sentenceHits.slice(i, i + 3);
|
|
11896
|
-
if (window.every((s) => s.wordCount <= 5)) {
|
|
11897
|
-
hits.push({
|
|
11898
|
-
line: window[0].line,
|
|
11899
|
-
rule: "voice_staccato_triplet",
|
|
11900
|
-
phrase: "three short sentences in a row reads like performance",
|
|
11901
|
-
text: window[0].text
|
|
11902
|
-
});
|
|
11903
|
-
break;
|
|
11904
|
-
}
|
|
11905
|
-
}
|
|
11906
|
-
return hits.sort((a, b) => {
|
|
11907
|
-
if (a.line !== b.line)
|
|
11908
|
-
return a.line - b.line;
|
|
11909
|
-
return a.rule.localeCompare(b.rule);
|
|
11910
12516
|
});
|
|
11911
12517
|
}
|
|
11912
12518
|
|
|
12519
|
+
// src/commands/learning.ts
|
|
12520
|
+
var import_chalk10 = __toESM(require_source());
|
|
12521
|
+
var fs10 = __toESM(require("fs"));
|
|
12522
|
+
var path11 = __toESM(require("path"));
|
|
12523
|
+
|
|
11913
12524
|
// src/lib/diff.ts
|
|
12525
|
+
init_scan();
|
|
11914
12526
|
function linesChanged(origLine, accLine) {
|
|
11915
12527
|
return origLine.trim() !== accLine.trim();
|
|
11916
12528
|
}
|
|
@@ -12228,11 +12840,12 @@ function printProfileImpact(impact, data) {
|
|
|
12228
12840
|
}
|
|
12229
12841
|
|
|
12230
12842
|
// src/commands/onboarding.ts
|
|
12231
|
-
var
|
|
12232
|
-
var
|
|
12233
|
-
var
|
|
12843
|
+
var import_chalk12 = __toESM(require_source());
|
|
12844
|
+
var fs12 = __toESM(require("fs"));
|
|
12845
|
+
var path13 = __toESM(require("path"));
|
|
12234
12846
|
init_config();
|
|
12235
12847
|
init_auth();
|
|
12848
|
+
init_scan();
|
|
12236
12849
|
var UNIVERSAL_QUESTIONS = [
|
|
12237
12850
|
{ key: "tone", question: "How would you describe your tone? (e.g., casual, professional, witty, direct)" },
|
|
12238
12851
|
{ key: "audience", question: "Who is your audience? (e.g., founders, developers, marketers)" },
|
|
@@ -12259,7 +12872,7 @@ var INDUSTRY_QUESTIONS = {
|
|
|
12259
12872
|
{ key: "length", question: "Do you prefer short punchy content or longer pieces?" }
|
|
12260
12873
|
]
|
|
12261
12874
|
};
|
|
12262
|
-
function
|
|
12875
|
+
function extractStats2(samples) {
|
|
12263
12876
|
const combined = samples.map((s) => s.text).join("\n\n");
|
|
12264
12877
|
const allWords = words(combined);
|
|
12265
12878
|
const allSentences = sentences(combined);
|
|
@@ -12315,7 +12928,7 @@ function registerOnboardingCommands(program3) {
|
|
|
12315
12928
|
await flashcardOnboarding(name, token);
|
|
12316
12929
|
}
|
|
12317
12930
|
} catch (error) {
|
|
12318
|
-
console.error(
|
|
12931
|
+
console.error(import_chalk12.default.red(`
|
|
12319
12932
|
Error: ${error.message}`));
|
|
12320
12933
|
process.exit(1);
|
|
12321
12934
|
}
|
|
@@ -12325,16 +12938,16 @@ Error: ${error.message}`));
|
|
|
12325
12938
|
async function flashcardOnboarding(name, token) {
|
|
12326
12939
|
const { printWelcome: printWelcome2 } = await Promise.resolve().then(() => (init_welcome(), welcome_exports));
|
|
12327
12940
|
printWelcome2({ condensed: true, skipDemo: true });
|
|
12328
|
-
console.log(
|
|
12941
|
+
console.log(import_chalk12.default.bold(`Creating voice profile: ${name}
|
|
12329
12942
|
`));
|
|
12330
|
-
console.log(
|
|
12943
|
+
console.log(import_chalk12.default.dim("Answer these questions to define your voice.\n"));
|
|
12331
12944
|
const answers = {};
|
|
12332
12945
|
for (const q of UNIVERSAL_QUESTIONS) {
|
|
12333
12946
|
const answer = await askQuestion(q.question);
|
|
12334
12947
|
answers[q.key] = answer;
|
|
12335
12948
|
}
|
|
12336
12949
|
const industry = answers.format?.toLowerCase().includes("code") || answers.audience?.toLowerCase().includes("developer") ? "tech" : answers.format?.toLowerCase().includes("market") || answers.audience?.toLowerCase().includes("market") ? "marketing" : "default";
|
|
12337
|
-
console.log(
|
|
12950
|
+
console.log(import_chalk12.default.dim(`
|
|
12338
12951
|
Industry: ${industry}
|
|
12339
12952
|
`));
|
|
12340
12953
|
for (const q of INDUSTRY_QUESTIONS[industry]) {
|
|
@@ -12342,7 +12955,7 @@ Industry: ${industry}
|
|
|
12342
12955
|
answers[q.key] = answer;
|
|
12343
12956
|
}
|
|
12344
12957
|
if (token) {
|
|
12345
|
-
console.log(
|
|
12958
|
+
console.log(import_chalk12.default.cyan("\nGenerating profile on server..."));
|
|
12346
12959
|
const response = await authenticatedRequest(
|
|
12347
12960
|
cliApiUrl("/cli/profiles/new"),
|
|
12348
12961
|
{
|
|
@@ -12359,60 +12972,60 @@ Industry: ${industry}
|
|
|
12359
12972
|
const profileContent = data.content || generateLocalProfile(name, answers);
|
|
12360
12973
|
ensureHyvDir();
|
|
12361
12974
|
writeCachedProfile(name, profileContent);
|
|
12362
|
-
console.log(
|
|
12975
|
+
console.log(import_chalk12.default.green(`
|
|
12363
12976
|
\u2713 Profile created: ${name}`));
|
|
12364
|
-
console.log(
|
|
12977
|
+
console.log(import_chalk12.default.dim("Run `hyv profiles` to see your profiles."));
|
|
12365
12978
|
} else {
|
|
12366
|
-
console.log(
|
|
12979
|
+
console.log(import_chalk12.default.yellow("\nServer unavailable. Creating local profile..."));
|
|
12367
12980
|
const profileContent = generateLocalProfile(name, answers);
|
|
12368
12981
|
ensureHyvDir();
|
|
12369
12982
|
writeCachedProfile(name, profileContent);
|
|
12370
|
-
console.log(
|
|
12983
|
+
console.log(import_chalk12.default.green(`
|
|
12371
12984
|
\u2713 Local profile created: ${name}`));
|
|
12372
12985
|
}
|
|
12373
12986
|
} else {
|
|
12374
|
-
console.log(
|
|
12987
|
+
console.log(import_chalk12.default.yellow("\nNot authenticated. Creating local profile..."));
|
|
12375
12988
|
const profileContent = generateLocalProfile(name, answers);
|
|
12376
12989
|
ensureHyvDir();
|
|
12377
12990
|
writeCachedProfile(name, profileContent);
|
|
12378
|
-
console.log(
|
|
12991
|
+
console.log(import_chalk12.default.green(`
|
|
12379
12992
|
\u2713 Local profile created: ${name}`));
|
|
12380
12993
|
}
|
|
12381
12994
|
}
|
|
12382
12995
|
async function createFromSamples(name, sampleDir, token) {
|
|
12383
|
-
const dirPath =
|
|
12384
|
-
if (!
|
|
12996
|
+
const dirPath = path13.resolve(sampleDir);
|
|
12997
|
+
if (!fs12.existsSync(dirPath)) {
|
|
12385
12998
|
throw new Error(`Directory not found: ${dirPath}`);
|
|
12386
12999
|
}
|
|
12387
|
-
console.log(
|
|
13000
|
+
console.log(import_chalk12.default.bold(`
|
|
12388
13001
|
Creating voice profile: ${name}`));
|
|
12389
|
-
console.log(
|
|
13002
|
+
console.log(import_chalk12.default.dim(`Reading samples from: ${dirPath}
|
|
12390
13003
|
`));
|
|
12391
|
-
const files =
|
|
13004
|
+
const files = fs12.readdirSync(dirPath).filter((f) => f.endsWith(".md") || f.endsWith(".txt")).map((f) => path13.join(dirPath, f));
|
|
12392
13005
|
if (files.length === 0) {
|
|
12393
13006
|
throw new Error("No .md or .txt files found in directory");
|
|
12394
13007
|
}
|
|
12395
13008
|
const samples = [];
|
|
12396
13009
|
for (const file of files) {
|
|
12397
|
-
const text =
|
|
13010
|
+
const text = fs12.readFileSync(file, "utf-8");
|
|
12398
13011
|
if (text.trim().length > 0) {
|
|
12399
13012
|
samples.push({ path: file, text });
|
|
12400
|
-
console.log(
|
|
13013
|
+
console.log(import_chalk12.default.dim(` \u2022 ${path13.basename(file)} (${words(text).length} words)`));
|
|
12401
13014
|
}
|
|
12402
13015
|
}
|
|
12403
13016
|
if (samples.length === 0) {
|
|
12404
13017
|
throw new Error("No readable text found in files");
|
|
12405
13018
|
}
|
|
12406
|
-
const stats =
|
|
12407
|
-
console.log(
|
|
13019
|
+
const stats = extractStats2(samples);
|
|
13020
|
+
console.log(import_chalk12.default.dim(`
|
|
12408
13021
|
Stats extracted:`));
|
|
12409
|
-
console.log(
|
|
12410
|
-
console.log(
|
|
12411
|
-
console.log(
|
|
12412
|
-
console.log(
|
|
12413
|
-
console.log(
|
|
13022
|
+
console.log(import_chalk12.default.dim(` Words: ${stats.word_count}`));
|
|
13023
|
+
console.log(import_chalk12.default.dim(` Sentences: ${stats.sentence_count}`));
|
|
13024
|
+
console.log(import_chalk12.default.dim(` Avg sentence: ${stats.avg_sentence_length} words`));
|
|
13025
|
+
console.log(import_chalk12.default.dim(` Case style: ${stats.case_style}`));
|
|
13026
|
+
console.log(import_chalk12.default.dim(` Argument pattern: ${stats.argument_pattern}`));
|
|
12414
13027
|
if (token) {
|
|
12415
|
-
console.log(
|
|
13028
|
+
console.log(import_chalk12.default.cyan("\nGenerating profile on server..."));
|
|
12416
13029
|
const response = await authenticatedRequest(
|
|
12417
13030
|
cliApiUrl("/cli/profiles/new"),
|
|
12418
13031
|
{
|
|
@@ -12428,25 +13041,25 @@ Stats extracted:`));
|
|
|
12428
13041
|
const data = response.data;
|
|
12429
13042
|
ensureHyvDir();
|
|
12430
13043
|
writeCachedProfile(name, data.content || generateLocalProfileFromStats(name, stats));
|
|
12431
|
-
console.log(
|
|
13044
|
+
console.log(import_chalk12.default.green(`
|
|
12432
13045
|
\u2713 Profile created: ${name}`));
|
|
12433
13046
|
} else {
|
|
12434
|
-
console.log(
|
|
13047
|
+
console.log(import_chalk12.default.yellow("\nServer unavailable. Creating local profile..."));
|
|
12435
13048
|
ensureHyvDir();
|
|
12436
13049
|
writeCachedProfile(name, generateLocalProfileFromStats(name, stats));
|
|
12437
|
-
console.log(
|
|
13050
|
+
console.log(import_chalk12.default.green(`
|
|
12438
13051
|
\u2713 Local profile created: ${name}`));
|
|
12439
13052
|
}
|
|
12440
13053
|
} else {
|
|
12441
|
-
console.log(
|
|
13054
|
+
console.log(import_chalk12.default.yellow("\nNot authenticated. Creating local profile..."));
|
|
12442
13055
|
ensureHyvDir();
|
|
12443
13056
|
writeCachedProfile(name, generateLocalProfileFromStats(name, stats));
|
|
12444
|
-
console.log(
|
|
13057
|
+
console.log(import_chalk12.default.green(`
|
|
12445
13058
|
\u2713 Local profile created: ${name}`));
|
|
12446
13059
|
}
|
|
12447
13060
|
}
|
|
12448
13061
|
async function generateExtractionPrompt(name) {
|
|
12449
|
-
console.log(
|
|
13062
|
+
console.log(import_chalk12.default.bold(`
|
|
12450
13063
|
Generating extraction prompt for: ${name}
|
|
12451
13064
|
`));
|
|
12452
13065
|
const prompt = `I need you to analyze writing samples and create a voice profile.
|
|
@@ -12468,33 +13081,33 @@ function registerImportCommand(program3) {
|
|
|
12468
13081
|
program3.command("import").description("Import a voice profile from file").argument("<name>", "Profile name").argument("<file>", "Profile markdown file").action(async (name, file) => {
|
|
12469
13082
|
try {
|
|
12470
13083
|
assertSafeProfileName(name);
|
|
12471
|
-
const filePath =
|
|
12472
|
-
if (!
|
|
12473
|
-
console.error(
|
|
13084
|
+
const filePath = path13.resolve(file);
|
|
13085
|
+
if (!fs12.existsSync(filePath)) {
|
|
13086
|
+
console.error(import_chalk12.default.red(`File not found: ${filePath}`));
|
|
12474
13087
|
process.exit(1);
|
|
12475
13088
|
}
|
|
12476
|
-
const content =
|
|
13089
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
12477
13090
|
ensureHyvDir();
|
|
12478
13091
|
writeCachedProfile(name, content);
|
|
12479
|
-
console.log(
|
|
13092
|
+
console.log(import_chalk12.default.green(`
|
|
12480
13093
|
\u2713 Profile imported: ${name}`));
|
|
12481
13094
|
} catch (error) {
|
|
12482
|
-
console.error(
|
|
13095
|
+
console.error(import_chalk12.default.red(`Error: ${error.message}`));
|
|
12483
13096
|
process.exit(1);
|
|
12484
13097
|
}
|
|
12485
13098
|
});
|
|
12486
13099
|
}
|
|
12487
13100
|
function askQuestion(question) {
|
|
12488
|
-
return new Promise((
|
|
12489
|
-
const
|
|
12490
|
-
const rl =
|
|
13101
|
+
return new Promise((resolve15) => {
|
|
13102
|
+
const readline3 = require("readline");
|
|
13103
|
+
const rl = readline3.createInterface({
|
|
12491
13104
|
input: process.stdin,
|
|
12492
13105
|
output: process.stdout
|
|
12493
13106
|
});
|
|
12494
|
-
rl.question(
|
|
13107
|
+
rl.question(import_chalk12.default.cyan(` ${question}
|
|
12495
13108
|
> `), (answer) => {
|
|
12496
13109
|
rl.close();
|
|
12497
|
-
|
|
13110
|
+
resolve15(answer.trim());
|
|
12498
13111
|
});
|
|
12499
13112
|
});
|
|
12500
13113
|
}
|
|
@@ -12558,7 +13171,7 @@ function generateLocalProfileFromStats(name, stats) {
|
|
|
12558
13171
|
}
|
|
12559
13172
|
|
|
12560
13173
|
// src/commands/plan.ts
|
|
12561
|
-
var
|
|
13174
|
+
var import_chalk13 = __toESM(require_source());
|
|
12562
13175
|
init_config();
|
|
12563
13176
|
init_auth();
|
|
12564
13177
|
var import_open2 = __toESM(require_open());
|
|
@@ -12573,10 +13186,10 @@ function registerPlanCommand(program3) {
|
|
|
12573
13186
|
}
|
|
12574
13187
|
const token = getToken();
|
|
12575
13188
|
if (!token) {
|
|
12576
|
-
console.log(
|
|
12577
|
-
console.log(
|
|
13189
|
+
console.log(import_chalk13.default.yellow("\nNot authenticated \u2014 free tier still works.\n"));
|
|
13190
|
+
console.log(import_chalk13.default.dim(" hyv scan draft.md | hyv welcome | npx @holdyourvoice/hyv scan draft.md"));
|
|
12578
13191
|
printFreePaidMatrix({ compact: true });
|
|
12579
|
-
console.log(
|
|
13192
|
+
console.log(import_chalk13.default.dim(" Run `hyv init` for profiles + learning.\n"));
|
|
12580
13193
|
return;
|
|
12581
13194
|
}
|
|
12582
13195
|
if (options.upgrade) {
|
|
@@ -12589,20 +13202,20 @@ function registerPlanCommand(program3) {
|
|
|
12589
13202
|
await showPlan();
|
|
12590
13203
|
}
|
|
12591
13204
|
} catch (error) {
|
|
12592
|
-
console.error(
|
|
13205
|
+
console.error(import_chalk13.default.red(`
|
|
12593
13206
|
Error: ${error.message}`));
|
|
12594
13207
|
process.exit(1);
|
|
12595
13208
|
}
|
|
12596
13209
|
});
|
|
12597
13210
|
}
|
|
12598
13211
|
async function showPlan() {
|
|
12599
|
-
console.log(
|
|
13212
|
+
console.log(import_chalk13.default.bold("\nSubscription Plan\n"));
|
|
12600
13213
|
const response = await authenticatedRequest(
|
|
12601
13214
|
cliApiUrl("/cli/heartbeat"),
|
|
12602
13215
|
{ method: "GET" }
|
|
12603
13216
|
);
|
|
12604
13217
|
if (response.status !== 200) {
|
|
12605
|
-
console.log(
|
|
13218
|
+
console.log(import_chalk13.default.yellow("Could not fetch plan info."));
|
|
12606
13219
|
return;
|
|
12607
13220
|
}
|
|
12608
13221
|
const data = response.data;
|
|
@@ -12620,27 +13233,27 @@ async function showPlan() {
|
|
|
12620
13233
|
team: "$29/mo",
|
|
12621
13234
|
agency: "Custom"
|
|
12622
13235
|
};
|
|
12623
|
-
console.log(
|
|
12624
|
-
console.log(
|
|
12625
|
-
console.log(
|
|
13236
|
+
console.log(import_chalk13.default.dim("Plan:"), import_chalk13.default.bold(planNames[plan] || plan));
|
|
13237
|
+
console.log(import_chalk13.default.dim("Price:"), planPrices[plan] || "-");
|
|
13238
|
+
console.log(import_chalk13.default.dim("Status:"), data.subscription_status || "none");
|
|
12626
13239
|
if (license) {
|
|
12627
|
-
console.log(
|
|
13240
|
+
console.log(import_chalk13.default.dim("License:"), license.key_hint);
|
|
12628
13241
|
}
|
|
12629
13242
|
const access = await getAccessState();
|
|
12630
|
-
console.log(
|
|
12631
|
-
console.log(
|
|
13243
|
+
console.log(import_chalk13.default.bold("\nFree tier (always)"));
|
|
13244
|
+
console.log(import_chalk13.default.dim(" local scan, fix, check, mcp, all web tools \u2014 hyv welcome"));
|
|
12632
13245
|
if (plan === "none" || !access.hasPaidPlan) {
|
|
12633
|
-
console.log(
|
|
12634
|
-
console.log(
|
|
12635
|
-
console.log(
|
|
12636
|
-
console.log(
|
|
13246
|
+
console.log(import_chalk13.default.bold("\nUpgrade unlocks"));
|
|
13247
|
+
console.log(import_chalk13.default.dim(" profiles, learning loop, hybrid analysis, rich rewrites"));
|
|
13248
|
+
console.log(import_chalk13.default.dim(` ${PRICING_URL}`));
|
|
13249
|
+
console.log(import_chalk13.default.dim("\n hyv plan --upgrade | hyv plan --free for full matrix"));
|
|
12637
13250
|
} else {
|
|
12638
13251
|
console.log("\nManage your subscription:");
|
|
12639
|
-
console.log(
|
|
13252
|
+
console.log(import_chalk13.default.dim(" hyv plan --manage"));
|
|
12640
13253
|
}
|
|
12641
13254
|
}
|
|
12642
13255
|
async function upgradePlan() {
|
|
12643
|
-
console.log(
|
|
13256
|
+
console.log(import_chalk13.default.cyan("\nOpening checkout...\n"));
|
|
12644
13257
|
const response = await authenticatedRequest(
|
|
12645
13258
|
cliApiUrl("/cli/subscribe"),
|
|
12646
13259
|
{
|
|
@@ -12652,20 +13265,20 @@ async function upgradePlan() {
|
|
|
12652
13265
|
const data = response.data;
|
|
12653
13266
|
const checkoutUrl = data.checkout_url;
|
|
12654
13267
|
if (checkoutUrl) {
|
|
12655
|
-
console.log(
|
|
13268
|
+
console.log(import_chalk13.default.dim("Opening browser..."));
|
|
12656
13269
|
await (0, import_open2.default)(assertSafeOpenUrl(checkoutUrl));
|
|
12657
|
-
console.log(
|
|
12658
|
-
console.log(
|
|
13270
|
+
console.log(import_chalk13.default.green("\n\u2713 Checkout opened in browser"));
|
|
13271
|
+
console.log(import_chalk13.default.dim("Complete the checkout to activate your plan."));
|
|
12659
13272
|
} else {
|
|
12660
|
-
console.log(
|
|
13273
|
+
console.log(import_chalk13.default.yellow("No checkout URL received."));
|
|
12661
13274
|
}
|
|
12662
13275
|
} else {
|
|
12663
|
-
console.log(
|
|
12664
|
-
console.log(
|
|
13276
|
+
console.log(import_chalk13.default.yellow("Could not create checkout session."));
|
|
13277
|
+
console.log(import_chalk13.default.dim("Visit https://holdyourvoice.com/pricing to subscribe."));
|
|
12665
13278
|
}
|
|
12666
13279
|
}
|
|
12667
13280
|
async function openBillingPortal() {
|
|
12668
|
-
console.log(
|
|
13281
|
+
console.log(import_chalk13.default.cyan("\nOpening billing portal...\n"));
|
|
12669
13282
|
const response = await authenticatedRequest(
|
|
12670
13283
|
cliApiUrl("/cli/subscribe/manage"),
|
|
12671
13284
|
{ method: "POST" }
|
|
@@ -12674,41 +13287,41 @@ async function openBillingPortal() {
|
|
|
12674
13287
|
const data = response.data;
|
|
12675
13288
|
const portalUrl = data.portal_url;
|
|
12676
13289
|
if (portalUrl) {
|
|
12677
|
-
console.log(
|
|
13290
|
+
console.log(import_chalk13.default.dim("Opening browser..."));
|
|
12678
13291
|
await (0, import_open2.default)(assertSafeOpenUrl(portalUrl));
|
|
12679
|
-
console.log(
|
|
13292
|
+
console.log(import_chalk13.default.green("\n\u2713 Billing portal opened"));
|
|
12680
13293
|
} else {
|
|
12681
|
-
console.log(
|
|
13294
|
+
console.log(import_chalk13.default.yellow("No portal URL received."));
|
|
12682
13295
|
}
|
|
12683
13296
|
} else {
|
|
12684
|
-
console.log(
|
|
12685
|
-
console.log(
|
|
13297
|
+
console.log(import_chalk13.default.yellow("Could not open billing portal."));
|
|
13298
|
+
console.log(import_chalk13.default.dim("Visit https://holdyourvoice.com/dashboard to manage billing."));
|
|
12686
13299
|
}
|
|
12687
13300
|
}
|
|
12688
13301
|
async function downgradePlan() {
|
|
12689
|
-
console.log(
|
|
13302
|
+
console.log(import_chalk13.default.yellow("\nDowngrade Plan\n"));
|
|
12690
13303
|
console.log("To downgrade your plan, please visit:");
|
|
12691
|
-
console.log(
|
|
13304
|
+
console.log(import_chalk13.default.dim(" https://holdyourvoice.com/dashboard"));
|
|
12692
13305
|
console.log("\nOr contact support at shashank@holdyourvoice.com");
|
|
12693
13306
|
}
|
|
12694
13307
|
|
|
12695
13308
|
// src/commands/scan.ts
|
|
12696
|
-
var
|
|
13309
|
+
var import_chalk16 = __toESM(require_source());
|
|
12697
13310
|
init_pipeline();
|
|
12698
13311
|
init_local_profile();
|
|
12699
13312
|
init_access();
|
|
12700
13313
|
|
|
12701
13314
|
// src/lib/output.ts
|
|
12702
|
-
var
|
|
13315
|
+
var import_chalk14 = __toESM(require_source());
|
|
12703
13316
|
var c = {
|
|
12704
|
-
dim: (s) =>
|
|
12705
|
-
bold: (s) =>
|
|
12706
|
-
accent: (s) =>
|
|
12707
|
-
green: (s) =>
|
|
12708
|
-
red: (s) =>
|
|
12709
|
-
yellow: (s) =>
|
|
12710
|
-
cyan: (s) =>
|
|
12711
|
-
white: (s) =>
|
|
13317
|
+
dim: (s) => import_chalk14.default.dim(s),
|
|
13318
|
+
bold: (s) => import_chalk14.default.bold(s),
|
|
13319
|
+
accent: (s) => import_chalk14.default.hex("#C4441A")(s),
|
|
13320
|
+
green: (s) => import_chalk14.default.green(s),
|
|
13321
|
+
red: (s) => import_chalk14.default.red(s),
|
|
13322
|
+
yellow: (s) => import_chalk14.default.yellow(s),
|
|
13323
|
+
cyan: (s) => import_chalk14.default.cyan(s),
|
|
13324
|
+
white: (s) => import_chalk14.default.white(s)
|
|
12712
13325
|
};
|
|
12713
13326
|
function logo() {
|
|
12714
13327
|
return `${c.bold("hold your ")}${c.accent("voice")}`;
|
|
@@ -12856,12 +13469,12 @@ async function runHybridAnalysis(text, profile, opts = {}) {
|
|
|
12856
13469
|
}
|
|
12857
13470
|
|
|
12858
13471
|
// src/commands/history.ts
|
|
12859
|
-
var
|
|
12860
|
-
var
|
|
12861
|
-
var
|
|
13472
|
+
var import_chalk15 = __toESM(require_source());
|
|
13473
|
+
var fs13 = __toESM(require("fs"));
|
|
13474
|
+
var path14 = __toESM(require("path"));
|
|
12862
13475
|
init_config();
|
|
12863
|
-
var HISTORY_DIR =
|
|
12864
|
-
var HISTORY_FILE =
|
|
13476
|
+
var HISTORY_DIR = path14.join(HYV_DIR, "history");
|
|
13477
|
+
var HISTORY_FILE = path14.join(HISTORY_DIR, "scans.jsonl");
|
|
12865
13478
|
function logScan(entry) {
|
|
12866
13479
|
try {
|
|
12867
13480
|
ensureHyvDir();
|
|
@@ -12871,9 +13484,9 @@ function logScan(entry) {
|
|
|
12871
13484
|
}
|
|
12872
13485
|
function readHistory() {
|
|
12873
13486
|
try {
|
|
12874
|
-
if (!
|
|
13487
|
+
if (!fs13.existsSync(HISTORY_FILE))
|
|
12875
13488
|
return [];
|
|
12876
|
-
const lines =
|
|
13489
|
+
const lines = fs13.readFileSync(HISTORY_FILE, "utf-8").trim().split("\n").filter(Boolean);
|
|
12877
13490
|
return lines.map((l) => JSON.parse(l));
|
|
12878
13491
|
} catch {
|
|
12879
13492
|
return [];
|
|
@@ -12905,10 +13518,10 @@ function registerHistoryCommand(program3) {
|
|
|
12905
13518
|
program3.command("history").description("Show past scan scores and track improvement").option("--limit <n>", "Number of entries to show", "20").option("--file <path>", "Filter by file path").option("--since <date>", "Show entries since (e.g., 7d, 1m, 2024-01-01)").option("--chart", "Show ASCII sparkline chart").option("--clear", "Clear history").option("--format <type>", "Output format (text, json)", "text").action(async (options) => {
|
|
12906
13519
|
try {
|
|
12907
13520
|
if (options.clear) {
|
|
12908
|
-
if (
|
|
12909
|
-
|
|
13521
|
+
if (fs13.existsSync(HISTORY_FILE)) {
|
|
13522
|
+
fs13.unlinkSync(HISTORY_FILE);
|
|
12910
13523
|
}
|
|
12911
|
-
console.log(
|
|
13524
|
+
console.log(import_chalk15.default.green("\n\u2713 History cleared"));
|
|
12912
13525
|
return;
|
|
12913
13526
|
}
|
|
12914
13527
|
let entries = readHistory();
|
|
@@ -12922,14 +13535,14 @@ function registerHistoryCommand(program3) {
|
|
|
12922
13535
|
const limit = parseInt(options.limit, 10);
|
|
12923
13536
|
entries = entries.slice(-limit);
|
|
12924
13537
|
if (entries.length === 0) {
|
|
12925
|
-
console.log(
|
|
13538
|
+
console.log(import_chalk15.default.dim("\nNo scan history yet. Run hyv scan or hyv score to start tracking."));
|
|
12926
13539
|
return;
|
|
12927
13540
|
}
|
|
12928
13541
|
if (options.format === "json") {
|
|
12929
13542
|
console.log(JSON.stringify(entries, null, 2));
|
|
12930
13543
|
return;
|
|
12931
13544
|
}
|
|
12932
|
-
console.log(
|
|
13545
|
+
console.log(import_chalk15.default.bold(`
|
|
12933
13546
|
score history (last ${entries.length} scans)
|
|
12934
13547
|
`));
|
|
12935
13548
|
if (options.chart) {
|
|
@@ -12941,7 +13554,7 @@ function registerHistoryCommand(program3) {
|
|
|
12941
13554
|
if (min !== max) {
|
|
12942
13555
|
console.log(` ${min} \u2524`);
|
|
12943
13556
|
}
|
|
12944
|
-
console.log(
|
|
13557
|
+
console.log(import_chalk15.default.dim(` \u2514${"\u2500".repeat(sl.length + 1)}`));
|
|
12945
13558
|
console.log("");
|
|
12946
13559
|
}
|
|
12947
13560
|
const scores = entries.map((e) => e.score);
|
|
@@ -12954,22 +13567,22 @@ function registerHistoryCommand(program3) {
|
|
|
12954
13567
|
const firstAvg = firstHalf.reduce((s, e) => s + e.score, 0) / (firstHalf.length || 1);
|
|
12955
13568
|
const secondAvg = secondHalf.reduce((s, e) => s + e.score, 0) / (secondHalf.length || 1);
|
|
12956
13569
|
const trend = Math.round(secondAvg - firstAvg);
|
|
12957
|
-
const trendStr = trend > 0 ?
|
|
12958
|
-
console.log(
|
|
12959
|
-
console.log(
|
|
12960
|
-
console.log(
|
|
13570
|
+
const trendStr = trend > 0 ? import_chalk15.default.green(`\u2191 improving (+${trend})`) : trend < 0 ? import_chalk15.default.red(`\u2193 declining (${trend})`) : import_chalk15.default.dim("\u2192 stable");
|
|
13571
|
+
console.log(import_chalk15.default.dim(` avg: ${avg} best: ${best.score} worst: ${worst.score}`));
|
|
13572
|
+
console.log(import_chalk15.default.dim(` trend: ${trendStr}`));
|
|
13573
|
+
console.log(import_chalk15.default.bold("\n recent:\n"));
|
|
12961
13574
|
const recent = entries.slice(-10).reverse();
|
|
12962
13575
|
for (const entry of recent) {
|
|
12963
13576
|
const time = new Date(entry.timestamp).toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit" });
|
|
12964
|
-
const icon = entry.issues === 0 ?
|
|
12965
|
-
const score = entry.score < 60 ?
|
|
12966
|
-
const issues = entry.issues > 0 ?
|
|
12967
|
-
const file =
|
|
12968
|
-
console.log(` ${
|
|
13577
|
+
const icon = entry.issues === 0 ? import_chalk15.default.green("\u2713") : import_chalk15.default.red("\u25CF");
|
|
13578
|
+
const score = entry.score < 60 ? import_chalk15.default.red(`${entry.score}/100`) : entry.score < 80 ? import_chalk15.default.yellow(`${entry.score}/100`) : import_chalk15.default.green(`${entry.score}/100`);
|
|
13579
|
+
const issues = entry.issues > 0 ? import_chalk15.default.red(`${entry.issues} issues`) : import_chalk15.default.dim("clean");
|
|
13580
|
+
const file = path14.basename(entry.file).slice(0, 25).padEnd(25);
|
|
13581
|
+
console.log(` ${import_chalk15.default.dim(time)} ${icon} ${file} ${score} ${issues}`);
|
|
12969
13582
|
}
|
|
12970
13583
|
console.log("");
|
|
12971
13584
|
} catch (error) {
|
|
12972
|
-
console.error(
|
|
13585
|
+
console.error(import_chalk15.default.red(`Error: ${error.message}`));
|
|
12973
13586
|
process.exit(1);
|
|
12974
13587
|
}
|
|
12975
13588
|
});
|
|
@@ -12984,7 +13597,7 @@ function registerScanCommand(program3) {
|
|
|
12984
13597
|
const profile = await loadProfileForCommand(options.profile);
|
|
12985
13598
|
const { text, path: filePath } = readText(file);
|
|
12986
13599
|
if (!text.trim()) {
|
|
12987
|
-
console.warn(
|
|
13600
|
+
console.warn(import_chalk16.default.yellow("File is empty or contains only whitespace."));
|
|
12988
13601
|
process.exit(0);
|
|
12989
13602
|
}
|
|
12990
13603
|
const ignoreRules = options.ignore ? new Set(options.ignore.split(",").map((r) => r.trim())) : /* @__PURE__ */ new Set();
|
|
@@ -13027,26 +13640,26 @@ function registerScanCommand(program3) {
|
|
|
13027
13640
|
}, null, 2));
|
|
13028
13641
|
} else {
|
|
13029
13642
|
if (signals.length === 0) {
|
|
13030
|
-
console.log(
|
|
13643
|
+
console.log(import_chalk16.default.green(`
|
|
13031
13644
|
\u2713 No issues found in ${filePath}`));
|
|
13032
|
-
console.log(
|
|
13645
|
+
console.log(import_chalk16.default.dim(` score: ${score}/100`));
|
|
13033
13646
|
} else {
|
|
13034
|
-
console.log(
|
|
13647
|
+
console.log(import_chalk16.default.bold(`
|
|
13035
13648
|
hyv scan ${filePath}`));
|
|
13036
13649
|
if (profile) {
|
|
13037
|
-
console.log(
|
|
13650
|
+
console.log(import_chalk16.default.dim(` profile: ${profile.slug || profile.name}${hasRichProfile(profile) ? " (full)" : ""}`));
|
|
13038
13651
|
}
|
|
13039
13652
|
if (analysis.message) {
|
|
13040
|
-
console.log(
|
|
13653
|
+
console.log(import_chalk16.default.dim(` engine: ${analysis.message}`));
|
|
13041
13654
|
}
|
|
13042
13655
|
console.log("");
|
|
13043
13656
|
printGroupedSignals(signals, profile);
|
|
13044
|
-
console.log(
|
|
13657
|
+
console.log(import_chalk16.default.yellow(`
|
|
13045
13658
|
${signals.length} issues (${signals.filter((s) => s.severity === "red").length} red, ${signals.filter((s) => s.severity === "yellow").length} yellow)`));
|
|
13046
|
-
console.log(
|
|
13047
|
-
console.log(
|
|
13659
|
+
console.log(import_chalk16.default.dim(` score: ${score}/100`));
|
|
13660
|
+
console.log(import_chalk16.default.dim(`
|
|
13048
13661
|
fix: hyv fix ${file}`));
|
|
13049
|
-
console.log(
|
|
13662
|
+
console.log(import_chalk16.default.dim(` diff: hyv diff ${file}
|
|
13050
13663
|
`));
|
|
13051
13664
|
}
|
|
13052
13665
|
await maybeShowLimitedModeHint(!!profile);
|
|
@@ -13064,17 +13677,17 @@ hyv scan ${filePath}`));
|
|
|
13064
13677
|
process.exit(2);
|
|
13065
13678
|
}
|
|
13066
13679
|
} catch (error) {
|
|
13067
|
-
console.error(
|
|
13680
|
+
console.error(import_chalk16.default.red(`Error: ${error.message}`));
|
|
13068
13681
|
process.exit(1);
|
|
13069
13682
|
}
|
|
13070
13683
|
});
|
|
13071
13684
|
}
|
|
13072
13685
|
|
|
13073
13686
|
// src/commands/doctor.ts
|
|
13074
|
-
var
|
|
13075
|
-
var
|
|
13076
|
-
var
|
|
13077
|
-
var
|
|
13687
|
+
var import_chalk17 = __toESM(require_source());
|
|
13688
|
+
var fs15 = __toESM(require("fs"));
|
|
13689
|
+
var path16 = __toESM(require("path"));
|
|
13690
|
+
var os6 = __toESM(require("os"));
|
|
13078
13691
|
init_config();
|
|
13079
13692
|
init_auth();
|
|
13080
13693
|
init_access();
|
|
@@ -13085,15 +13698,15 @@ init_version();
|
|
|
13085
13698
|
var import_child_process2 = require("child_process");
|
|
13086
13699
|
|
|
13087
13700
|
// src/lib/cli-entry.ts
|
|
13088
|
-
var
|
|
13089
|
-
var
|
|
13701
|
+
var fs14 = __toESM(require("fs"));
|
|
13702
|
+
var path15 = __toESM(require("path"));
|
|
13090
13703
|
function resolveCliEntry() {
|
|
13091
13704
|
const candidates = [
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
|
|
13705
|
+
path15.resolve(process.argv[1] || ""),
|
|
13706
|
+
path15.resolve(__dirname, "index.js"),
|
|
13707
|
+
path15.resolve(__dirname, "..", "dist", "index.js")
|
|
13095
13708
|
];
|
|
13096
|
-
return candidates.find((p) => p &&
|
|
13709
|
+
return candidates.find((p) => p && fs14.existsSync(p)) || null;
|
|
13097
13710
|
}
|
|
13098
13711
|
function mcpServerCommand() {
|
|
13099
13712
|
const entry = resolveCliEntry();
|
|
@@ -13112,7 +13725,7 @@ async function testMcpStdioSubprocess() {
|
|
|
13112
13725
|
const entry = resolveCliEntry();
|
|
13113
13726
|
if (!entry)
|
|
13114
13727
|
return { ok: false };
|
|
13115
|
-
return new Promise((
|
|
13728
|
+
return new Promise((resolve15) => {
|
|
13116
13729
|
const child = (0, import_child_process2.spawn)(process.execPath, [entry, "mcp"], {
|
|
13117
13730
|
stdio: ["pipe", "pipe", "pipe"],
|
|
13118
13731
|
env: { ...process.env, HYV_POSTINSTALL_QUIET: "1" }
|
|
@@ -13128,7 +13741,7 @@ async function testMcpStdioSubprocess() {
|
|
|
13128
13741
|
child.kill();
|
|
13129
13742
|
} catch {
|
|
13130
13743
|
}
|
|
13131
|
-
|
|
13744
|
+
resolve15({ ok, toolCount });
|
|
13132
13745
|
};
|
|
13133
13746
|
const timeout = setTimeout(() => finish(false), 1e4);
|
|
13134
13747
|
const handleLine = (line) => {
|
|
@@ -13179,20 +13792,20 @@ async function testMcpStdioSubprocess() {
|
|
|
13179
13792
|
}
|
|
13180
13793
|
|
|
13181
13794
|
// src/commands/doctor.ts
|
|
13182
|
-
var HOME =
|
|
13795
|
+
var HOME = os6.homedir();
|
|
13183
13796
|
var IS_WIN = process.platform === "win32";
|
|
13184
13797
|
function claudeDesktopDir() {
|
|
13185
13798
|
if (IS_WIN)
|
|
13186
|
-
return
|
|
13799
|
+
return path16.join(HOME, "AppData", "Roaming", "Claude");
|
|
13187
13800
|
if (process.platform === "linux")
|
|
13188
|
-
return
|
|
13189
|
-
return
|
|
13801
|
+
return path16.join(HOME, ".config", "Claude");
|
|
13802
|
+
return path16.join(HOME, "Library", "Application Support", "Claude");
|
|
13190
13803
|
}
|
|
13191
13804
|
function isOwnerOnlyFile(filePath) {
|
|
13192
13805
|
try {
|
|
13193
|
-
if (!
|
|
13806
|
+
if (!fs15.existsSync(filePath))
|
|
13194
13807
|
return true;
|
|
13195
|
-
const mode =
|
|
13808
|
+
const mode = fs15.statSync(filePath).mode & 511;
|
|
13196
13809
|
return (mode & 63) === 0;
|
|
13197
13810
|
} catch {
|
|
13198
13811
|
return false;
|
|
@@ -13200,9 +13813,9 @@ function isOwnerOnlyFile(filePath) {
|
|
|
13200
13813
|
}
|
|
13201
13814
|
function readMcpHyv(configFile) {
|
|
13202
13815
|
try {
|
|
13203
|
-
if (!
|
|
13816
|
+
if (!fs15.existsSync(configFile))
|
|
13204
13817
|
return false;
|
|
13205
|
-
const cfg = JSON.parse(
|
|
13818
|
+
const cfg = JSON.parse(fs15.readFileSync(configFile, "utf-8"));
|
|
13206
13819
|
return Boolean(cfg.mcpServers?.hyv);
|
|
13207
13820
|
} catch {
|
|
13208
13821
|
return false;
|
|
@@ -13210,188 +13823,188 @@ function readMcpHyv(configFile) {
|
|
|
13210
13823
|
}
|
|
13211
13824
|
function registerDoctorCommand(program3) {
|
|
13212
13825
|
program3.command("doctor").description("Diagnose CLI health: engine, cache, auth, agents").option("--fix-agents", "Re-run agent config copy (idempotent)").action(async (opts) => {
|
|
13213
|
-
console.log(
|
|
13826
|
+
console.log(import_chalk17.default.bold("\nhold your voice \u2014 doctor\n"));
|
|
13214
13827
|
let issues = 0;
|
|
13215
13828
|
let fixed = 0;
|
|
13216
|
-
console.log(
|
|
13829
|
+
console.log(import_chalk17.default.dim(`engine: ${getEngineLabel()} (node ${process.version})`));
|
|
13217
13830
|
if (isLocalOnlyMode()) {
|
|
13218
|
-
console.log(
|
|
13831
|
+
console.log(import_chalk17.default.yellow(" local-only mode: HYV_LOCAL_ONLY is set"));
|
|
13219
13832
|
}
|
|
13220
13833
|
const access = await getAccessState();
|
|
13221
13834
|
const profile = await loadProfileForCommand();
|
|
13222
|
-
console.log(
|
|
13223
|
-
console.log(
|
|
13224
|
-
console.log(
|
|
13835
|
+
console.log(import_chalk17.default.dim(`mode: ${formatModeLabel(access, hasRichProfile(profile))}`));
|
|
13836
|
+
console.log(import_chalk17.default.dim("tip: run hyv welcome for free capabilities\n"));
|
|
13837
|
+
console.log(import_chalk17.default.dim("checking cli installation..."));
|
|
13225
13838
|
const cliPath = process.argv[1];
|
|
13226
|
-
if (cliPath &&
|
|
13227
|
-
console.log(
|
|
13839
|
+
if (cliPath && fs15.existsSync(cliPath)) {
|
|
13840
|
+
console.log(import_chalk17.default.green(" \u2713 cli installed"));
|
|
13228
13841
|
} else {
|
|
13229
|
-
console.log(
|
|
13842
|
+
console.log(import_chalk17.default.red(" \u2717 cli not found"));
|
|
13230
13843
|
issues++;
|
|
13231
13844
|
}
|
|
13232
|
-
console.log(
|
|
13233
|
-
if (
|
|
13234
|
-
console.log(
|
|
13845
|
+
console.log(import_chalk17.default.dim("checking .hyv directory..."));
|
|
13846
|
+
if (fs15.existsSync(HYV_DIR)) {
|
|
13847
|
+
console.log(import_chalk17.default.green(" \u2713 .hyv directory exists"));
|
|
13235
13848
|
} else {
|
|
13236
|
-
console.log(
|
|
13237
|
-
|
|
13849
|
+
console.log(import_chalk17.default.yellow(" ! .hyv directory missing \u2014 creating..."));
|
|
13850
|
+
fs15.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
|
|
13238
13851
|
fixed++;
|
|
13239
13852
|
}
|
|
13240
|
-
console.log(
|
|
13853
|
+
console.log(import_chalk17.default.dim("checking cache..."));
|
|
13241
13854
|
const diskProfiles = listDiskCachedProfiles();
|
|
13242
|
-
const syncMeta =
|
|
13243
|
-
if (diskProfiles.length > 0 ||
|
|
13244
|
-
console.log(
|
|
13245
|
-
if (
|
|
13855
|
+
const syncMeta = path16.join(CACHE_DIR, "sync-meta.json");
|
|
13856
|
+
if (diskProfiles.length > 0 || fs15.existsSync(syncMeta)) {
|
|
13857
|
+
console.log(import_chalk17.default.green(` \u2713 profile cache (${diskProfiles.length} full profile(s))`));
|
|
13858
|
+
if (fs15.existsSync(syncMeta)) {
|
|
13246
13859
|
try {
|
|
13247
|
-
const meta = JSON.parse(
|
|
13248
|
-
console.log(
|
|
13860
|
+
const meta = JSON.parse(fs15.readFileSync(syncMeta, "utf-8"));
|
|
13861
|
+
console.log(import_chalk17.default.dim(` last sync: ${meta.synced_at || "unknown"}`));
|
|
13249
13862
|
} catch {
|
|
13250
13863
|
}
|
|
13251
13864
|
}
|
|
13252
13865
|
} else {
|
|
13253
|
-
console.log(
|
|
13866
|
+
console.log(import_chalk17.default.dim(" - no full profile cache (free local engine still works)"));
|
|
13254
13867
|
}
|
|
13255
|
-
console.log(
|
|
13256
|
-
if (
|
|
13257
|
-
const hyvMode =
|
|
13868
|
+
console.log(import_chalk17.default.dim("checking file permissions..."));
|
|
13869
|
+
if (fs15.existsSync(HYV_DIR)) {
|
|
13870
|
+
const hyvMode = fs15.statSync(HYV_DIR).mode & 511;
|
|
13258
13871
|
if ((hyvMode & 63) === 0) {
|
|
13259
|
-
console.log(
|
|
13872
|
+
console.log(import_chalk17.default.green(" \u2713 .hyv directory permissions"));
|
|
13260
13873
|
} else {
|
|
13261
|
-
console.log(
|
|
13262
|
-
console.log(
|
|
13874
|
+
console.log(import_chalk17.default.yellow(" ! .hyv directory is world/group accessible"));
|
|
13875
|
+
console.log(import_chalk17.default.dim(" run: chmod 700 ~/.hyv"));
|
|
13263
13876
|
issues++;
|
|
13264
13877
|
}
|
|
13265
13878
|
}
|
|
13266
|
-
if (
|
|
13879
|
+
if (fs15.existsSync(AUTH_FILE)) {
|
|
13267
13880
|
if (isOwnerOnlyFile(AUTH_FILE)) {
|
|
13268
|
-
console.log(
|
|
13881
|
+
console.log(import_chalk17.default.green(" \u2713 auth.json permissions"));
|
|
13269
13882
|
} else {
|
|
13270
|
-
console.log(
|
|
13271
|
-
console.log(
|
|
13883
|
+
console.log(import_chalk17.default.yellow(" ! auth.json is world/group readable"));
|
|
13884
|
+
console.log(import_chalk17.default.dim(" run: chmod 600 ~/.hyv/auth.json"));
|
|
13272
13885
|
issues++;
|
|
13273
13886
|
}
|
|
13274
13887
|
}
|
|
13275
|
-
console.log(
|
|
13888
|
+
console.log(import_chalk17.default.dim("checking authentication..."));
|
|
13276
13889
|
if (isInitialized()) {
|
|
13277
13890
|
const auth = readAuth();
|
|
13278
13891
|
if (auth) {
|
|
13279
|
-
console.log(
|
|
13280
|
-
console.log(
|
|
13892
|
+
console.log(import_chalk17.default.green(" \u2713 authenticated"));
|
|
13893
|
+
console.log(import_chalk17.default.dim(` email: ${auth.user?.email || "unknown"}`));
|
|
13281
13894
|
const session = await checkSession();
|
|
13282
13895
|
if (session.valid) {
|
|
13283
|
-
console.log(
|
|
13284
|
-
console.log(
|
|
13896
|
+
console.log(import_chalk17.default.green(" \u2713 session valid"));
|
|
13897
|
+
console.log(import_chalk17.default.dim(` plan: ${session.plan || "none"}`));
|
|
13285
13898
|
} else {
|
|
13286
|
-
console.log(
|
|
13287
|
-
console.log(
|
|
13899
|
+
console.log(import_chalk17.default.red(" \u2717 session expired"));
|
|
13900
|
+
console.log(import_chalk17.default.dim(" run: hyv init"));
|
|
13288
13901
|
issues++;
|
|
13289
13902
|
}
|
|
13290
13903
|
} else {
|
|
13291
|
-
console.log(
|
|
13904
|
+
console.log(import_chalk17.default.red(" \u2717 auth data missing"));
|
|
13292
13905
|
issues++;
|
|
13293
13906
|
}
|
|
13294
13907
|
} else {
|
|
13295
|
-
console.log(
|
|
13296
|
-
console.log(
|
|
13908
|
+
console.log(import_chalk17.default.yellow(" ! not authenticated (free local scan works)"));
|
|
13909
|
+
console.log(import_chalk17.default.dim(" run: hyv init for profiles + learning"));
|
|
13297
13910
|
}
|
|
13298
|
-
console.log(
|
|
13299
|
-
const voiceMd =
|
|
13300
|
-
const hasVoiceMd =
|
|
13301
|
-
const profileFiles =
|
|
13911
|
+
console.log(import_chalk17.default.dim("checking voice profile..."));
|
|
13912
|
+
const voiceMd = path16.join(HYV_DIR, "voice.md");
|
|
13913
|
+
const hasVoiceMd = fs15.existsSync(voiceMd) && fs15.readFileSync(voiceMd, "utf-8").trim().length > 50;
|
|
13914
|
+
const profileFiles = fs15.existsSync(PROFILES_DIR) ? fs15.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".md") && f !== "voice.md") : [];
|
|
13302
13915
|
if (hasVoiceMd || profileFiles.length > 0 || diskProfiles.length > 0) {
|
|
13303
13916
|
if (hasVoiceMd)
|
|
13304
|
-
console.log(
|
|
13917
|
+
console.log(import_chalk17.default.green(" \u2713 voice.md exists"));
|
|
13305
13918
|
if (profileFiles.length > 0)
|
|
13306
|
-
console.log(
|
|
13919
|
+
console.log(import_chalk17.default.green(` \u2713 ${profileFiles.length} markdown profile(s)`));
|
|
13307
13920
|
if (diskProfiles.length > 0)
|
|
13308
|
-
console.log(
|
|
13921
|
+
console.log(import_chalk17.default.green(` \u2713 ${diskProfiles.length} full cached profile(s)`));
|
|
13309
13922
|
} else {
|
|
13310
|
-
console.log(
|
|
13311
|
-
console.log(
|
|
13312
|
-
}
|
|
13313
|
-
console.log(
|
|
13314
|
-
const cursorLegacyRule =
|
|
13315
|
-
const cursorRule =
|
|
13316
|
-
if (
|
|
13317
|
-
console.log(
|
|
13318
|
-
console.log(
|
|
13923
|
+
console.log(import_chalk17.default.yellow(" ! no voice profile (optional for free scan)"));
|
|
13924
|
+
console.log(import_chalk17.default.dim(" run: hyv new <name> or hyv init"));
|
|
13925
|
+
}
|
|
13926
|
+
console.log(import_chalk17.default.dim("checking agent configurations..."));
|
|
13927
|
+
const cursorLegacyRule = path16.join(HOME, ".cursor", "rules", "hyv.md");
|
|
13928
|
+
const cursorRule = path16.join(HOME, ".cursor", "rules", "hyv.mdc");
|
|
13929
|
+
if (fs15.existsSync(cursorLegacyRule) && fs15.existsSync(cursorRule)) {
|
|
13930
|
+
console.log(import_chalk17.default.yellow(" ! stale cursor rule ~/.cursor/rules/hyv.md (use hyv.mdc)"));
|
|
13931
|
+
console.log(import_chalk17.default.dim(" run: rm ~/.cursor/rules/hyv.md (or hyv doctor --fix-agents)"));
|
|
13319
13932
|
issues++;
|
|
13320
13933
|
}
|
|
13321
13934
|
const agentChecks = [
|
|
13322
|
-
{ name: "claude desktop mcp", ok: readMcpHyv(
|
|
13323
|
-
{ name: "cursor mcp", ok: readMcpHyv(
|
|
13324
|
-
{ name: "cursor rule", ok:
|
|
13325
|
-
{ name: "claude code command", ok:
|
|
13326
|
-
{ name: "claude code skill", ok:
|
|
13327
|
-
{ name: "codex agents", ok:
|
|
13328
|
-
{ name: "command code skill", ok:
|
|
13935
|
+
{ name: "claude desktop mcp", ok: readMcpHyv(path16.join(claudeDesktopDir(), "claude_desktop_config.json")) },
|
|
13936
|
+
{ name: "cursor mcp", ok: readMcpHyv(path16.join(HOME, ".cursor", "mcp.json")) },
|
|
13937
|
+
{ name: "cursor rule", ok: fs15.existsSync(cursorRule) },
|
|
13938
|
+
{ name: "claude code command", ok: fs15.existsSync(path16.join(HOME, ".claude", "commands", "hyv.md")) },
|
|
13939
|
+
{ name: "claude code skill", ok: fs15.existsSync(path16.join(HOME, ".claude", "skills", "hold-your-voice", "SKILL.md")) },
|
|
13940
|
+
{ name: "codex agents", ok: fs15.existsSync(path16.join(HOME, ".codex", "AGENTS.md")) && fs15.readFileSync(path16.join(HOME, ".codex", "AGENTS.md"), "utf-8").includes("hyv") },
|
|
13941
|
+
{ name: "command code skill", ok: fs15.existsSync(path16.join(HOME, ".commandcode", "skills", "hyv", "SKILL.md")) }
|
|
13329
13942
|
];
|
|
13330
13943
|
for (const agent of agentChecks) {
|
|
13331
13944
|
if (agent.ok) {
|
|
13332
|
-
console.log(
|
|
13945
|
+
console.log(import_chalk17.default.green(` \u2713 ${agent.name}`));
|
|
13333
13946
|
} else {
|
|
13334
|
-
console.log(
|
|
13947
|
+
console.log(import_chalk17.default.dim(` - ${agent.name} not configured`));
|
|
13335
13948
|
}
|
|
13336
13949
|
}
|
|
13337
13950
|
if (opts.fixAgents) {
|
|
13338
13951
|
try {
|
|
13339
|
-
const pkgDir =
|
|
13340
|
-
const { setupAgents } = require(
|
|
13952
|
+
const pkgDir = path16.resolve(__dirname, "..");
|
|
13953
|
+
const { setupAgents } = require(path16.join(pkgDir, "scripts", "postinstall-lib.js"));
|
|
13341
13954
|
const result = setupAgents({ pkgDir, quiet: true });
|
|
13342
|
-
console.log(
|
|
13955
|
+
console.log(import_chalk17.default.green(` \u2713 re-ran agent setup (${result.configured.join(", ") || "no changes"})`));
|
|
13343
13956
|
if (result.warnings.length) {
|
|
13344
|
-
console.log(
|
|
13957
|
+
console.log(import_chalk17.default.yellow(` notes: ${result.warnings.join("; ")}`));
|
|
13345
13958
|
}
|
|
13346
13959
|
fixed++;
|
|
13347
13960
|
} catch (err) {
|
|
13348
|
-
console.log(
|
|
13961
|
+
console.log(import_chalk17.default.yellow(` ! could not re-run agent setup: ${err.message}`));
|
|
13349
13962
|
}
|
|
13350
13963
|
}
|
|
13351
|
-
console.log(
|
|
13352
|
-
const claudeMcp = readMcpHyv(
|
|
13353
|
-
const cursorMcp = readMcpHyv(
|
|
13964
|
+
console.log(import_chalk17.default.dim("checking mcp server..."));
|
|
13965
|
+
const claudeMcp = readMcpHyv(path16.join(claudeDesktopDir(), "claude_desktop_config.json"));
|
|
13966
|
+
const cursorMcp = readMcpHyv(path16.join(HOME, ".cursor", "mcp.json"));
|
|
13354
13967
|
if (claudeMcp || cursorMcp) {
|
|
13355
13968
|
if (claudeMcp)
|
|
13356
|
-
console.log(
|
|
13969
|
+
console.log(import_chalk17.default.green(" \u2713 mcp configured for claude desktop"));
|
|
13357
13970
|
if (cursorMcp)
|
|
13358
|
-
console.log(
|
|
13971
|
+
console.log(import_chalk17.default.green(" \u2713 mcp configured for cursor"));
|
|
13359
13972
|
} else {
|
|
13360
|
-
console.log(
|
|
13973
|
+
console.log(import_chalk17.default.yellow(" ! mcp not configured \u2014 run: hyv doctor --fix-agents or hyv mcp --setup"));
|
|
13361
13974
|
issues++;
|
|
13362
13975
|
}
|
|
13363
13976
|
try {
|
|
13364
13977
|
const stdio = await testMcpStdioSubprocess();
|
|
13365
13978
|
if (stdio.ok && (stdio.toolCount || 0) >= 10) {
|
|
13366
|
-
console.log(
|
|
13979
|
+
console.log(import_chalk17.default.green(` \u2713 mcp stdio subprocess healthy (${stdio.toolCount} tools)`));
|
|
13367
13980
|
} else if (stdio.ok) {
|
|
13368
|
-
console.log(
|
|
13981
|
+
console.log(import_chalk17.default.yellow(` ! mcp stdio ok but only ${stdio.toolCount || 0} tools`));
|
|
13369
13982
|
issues++;
|
|
13370
13983
|
} else {
|
|
13371
|
-
console.log(
|
|
13372
|
-
console.log(
|
|
13984
|
+
console.log(import_chalk17.default.red(" \u2717 mcp stdio subprocess failed"));
|
|
13985
|
+
console.log(import_chalk17.default.dim(" run: hyv mcp --test"));
|
|
13373
13986
|
issues++;
|
|
13374
13987
|
}
|
|
13375
13988
|
} catch (err) {
|
|
13376
|
-
console.log(
|
|
13989
|
+
console.log(import_chalk17.default.red(` \u2717 mcp stdio probe failed: ${err.message}`));
|
|
13377
13990
|
issues++;
|
|
13378
13991
|
}
|
|
13379
13992
|
console.log("");
|
|
13380
13993
|
if (issues === 0 && fixed === 0) {
|
|
13381
|
-
console.log(
|
|
13994
|
+
console.log(import_chalk17.default.green("\u2713 everything looks good!"));
|
|
13382
13995
|
} else if (fixed > 0) {
|
|
13383
|
-
console.log(
|
|
13996
|
+
console.log(import_chalk17.default.green(`\u2713 fixed ${fixed} issue(s)`));
|
|
13384
13997
|
if (issues > 0)
|
|
13385
|
-
console.log(
|
|
13998
|
+
console.log(import_chalk17.default.yellow(`! ${issues} issue(s) remaining`));
|
|
13386
13999
|
} else {
|
|
13387
|
-
console.log(
|
|
14000
|
+
console.log(import_chalk17.default.yellow(`! ${issues} issue(s) found`));
|
|
13388
14001
|
}
|
|
13389
|
-
console.log(
|
|
14002
|
+
console.log(import_chalk17.default.dim("\nfree scan: hyv scan draft.md | full tour: hyv welcome\n"));
|
|
13390
14003
|
});
|
|
13391
14004
|
}
|
|
13392
14005
|
|
|
13393
14006
|
// src/commands/rename.ts
|
|
13394
|
-
var
|
|
14007
|
+
var import_chalk18 = __toESM(require_source());
|
|
13395
14008
|
init_config();
|
|
13396
14009
|
init_auth();
|
|
13397
14010
|
function registerRenameCommand(program3) {
|
|
@@ -13399,26 +14012,26 @@ function registerRenameCommand(program3) {
|
|
|
13399
14012
|
try {
|
|
13400
14013
|
const trimmedName = newName.trim();
|
|
13401
14014
|
if (!trimmedName) {
|
|
13402
|
-
console.log(
|
|
13403
|
-
console.log(
|
|
14015
|
+
console.log(import_chalk18.default.red("\nerror: what do you want to name your profile?\n"));
|
|
14016
|
+
console.log(import_chalk18.default.dim(' example: hyv rename "my brand voice"\n'));
|
|
13404
14017
|
process.exit(1);
|
|
13405
14018
|
return;
|
|
13406
14019
|
}
|
|
13407
14020
|
if (trimmedName.length > 100) {
|
|
13408
|
-
console.log(
|
|
14021
|
+
console.log(import_chalk18.default.red("\nerror: name is too long \u2014 keep it under 100 characters\n"));
|
|
13409
14022
|
process.exit(1);
|
|
13410
14023
|
return;
|
|
13411
14024
|
}
|
|
13412
14025
|
if (!/^[a-zA-Z0-9\s\-_'&.]+$/.test(trimmedName)) {
|
|
13413
|
-
console.log(
|
|
13414
|
-
console.log(
|
|
14026
|
+
console.log(import_chalk18.default.red("\nerror: name contains invalid characters\n"));
|
|
14027
|
+
console.log(import_chalk18.default.dim(" allowed: letters, numbers, spaces, hyphens, underscores, apostrophes\n"));
|
|
13415
14028
|
process.exit(1);
|
|
13416
14029
|
return;
|
|
13417
14030
|
}
|
|
13418
14031
|
const token = getToken();
|
|
13419
14032
|
if (!token) {
|
|
13420
|
-
console.log(
|
|
13421
|
-
console.log(
|
|
14033
|
+
console.log(import_chalk18.default.red("\nerror: you're not signed in yet\n"));
|
|
14034
|
+
console.log(import_chalk18.default.dim(" run: hyv init\n"));
|
|
13422
14035
|
process.exit(1);
|
|
13423
14036
|
return;
|
|
13424
14037
|
}
|
|
@@ -13434,16 +14047,16 @@ function registerRenameCommand(program3) {
|
|
|
13434
14047
|
);
|
|
13435
14048
|
if (response.status === 200) {
|
|
13436
14049
|
const data = response.data;
|
|
13437
|
-
console.log(
|
|
14050
|
+
console.log(import_chalk18.default.green(`
|
|
13438
14051
|
\u2713 profile renamed to ${data.profile?.name || trimmedName}
|
|
13439
14052
|
`));
|
|
13440
14053
|
} else {
|
|
13441
|
-
console.log(
|
|
14054
|
+
console.log(import_chalk18.default.red(`
|
|
13442
14055
|
error: server returned ${response.status}
|
|
13443
14056
|
`));
|
|
13444
14057
|
}
|
|
13445
14058
|
} catch (error) {
|
|
13446
|
-
console.error(
|
|
14059
|
+
console.error(import_chalk18.default.red(`
|
|
13447
14060
|
error: ${error.message}
|
|
13448
14061
|
`));
|
|
13449
14062
|
process.exit(1);
|
|
@@ -13452,46 +14065,46 @@ error: ${error.message}
|
|
|
13452
14065
|
}
|
|
13453
14066
|
|
|
13454
14067
|
// src/commands/fix.ts
|
|
13455
|
-
var
|
|
14068
|
+
var import_chalk20 = __toESM(require_source());
|
|
13456
14069
|
init_pipeline();
|
|
13457
14070
|
init_local_profile();
|
|
13458
14071
|
init_access();
|
|
13459
14072
|
init_config();
|
|
13460
14073
|
|
|
13461
14074
|
// src/lib/destructive-write.ts
|
|
13462
|
-
var
|
|
13463
|
-
var
|
|
13464
|
-
var
|
|
13465
|
-
var
|
|
14075
|
+
var fs16 = __toESM(require("fs"));
|
|
14076
|
+
var path17 = __toESM(require("path"));
|
|
14077
|
+
var readline2 = __toESM(require("readline"));
|
|
14078
|
+
var import_chalk19 = __toESM(require_source());
|
|
13466
14079
|
async function confirmDestructiveWrite(options) {
|
|
13467
14080
|
if (options.yes)
|
|
13468
14081
|
return true;
|
|
13469
14082
|
const label = options.target ? `${options.action} (${options.target})` : options.action;
|
|
13470
14083
|
if (!process.stdin.isTTY) {
|
|
13471
|
-
console.error(
|
|
13472
|
-
console.error(
|
|
14084
|
+
console.error(import_chalk19.default.red("\nRefusing destructive write without confirmation (non-interactive)."));
|
|
14085
|
+
console.error(import_chalk19.default.dim(" Re-run with --yes to create .bak backups and proceed.\n"));
|
|
13473
14086
|
return false;
|
|
13474
14087
|
}
|
|
13475
14088
|
const question = `
|
|
13476
14089
|
${label}
|
|
13477
14090
|
A .bak backup will be created. Proceed? [y/N] `;
|
|
13478
|
-
const answer = await new Promise((
|
|
13479
|
-
const rl =
|
|
14091
|
+
const answer = await new Promise((resolve15) => {
|
|
14092
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
13480
14093
|
rl.question(question, (value) => {
|
|
13481
14094
|
rl.close();
|
|
13482
|
-
|
|
14095
|
+
resolve15(value.trim().toLowerCase());
|
|
13483
14096
|
});
|
|
13484
14097
|
});
|
|
13485
14098
|
return answer === "y" || answer === "yes";
|
|
13486
14099
|
}
|
|
13487
14100
|
function writeInPlaceWithBackup(filePath, content) {
|
|
13488
14101
|
const backupPath = filePath + ".bak";
|
|
13489
|
-
|
|
13490
|
-
|
|
14102
|
+
fs16.copyFileSync(filePath, backupPath);
|
|
14103
|
+
fs16.writeFileSync(filePath, content);
|
|
13491
14104
|
return backupPath;
|
|
13492
14105
|
}
|
|
13493
14106
|
function backupBasename(filePath) {
|
|
13494
|
-
return
|
|
14107
|
+
return path17.basename(filePath) + ".bak";
|
|
13495
14108
|
}
|
|
13496
14109
|
|
|
13497
14110
|
// src/commands/fix.ts
|
|
@@ -13505,9 +14118,9 @@ function registerFixCommand(program3) {
|
|
|
13505
14118
|
if (options.format === "json") {
|
|
13506
14119
|
console.log(JSON.stringify({ file: filePath, autoFixes: 0, llmIssues: result.stats.needsLLM, changes: [] }));
|
|
13507
14120
|
} else {
|
|
13508
|
-
console.log(
|
|
14121
|
+
console.log(import_chalk20.default.green("\n\u2713 No auto-fixable issues found."));
|
|
13509
14122
|
if (result.stats.needsLLM > 0) {
|
|
13510
|
-
console.log(
|
|
14123
|
+
console.log(import_chalk20.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
|
|
13511
14124
|
}
|
|
13512
14125
|
}
|
|
13513
14126
|
return;
|
|
@@ -13521,16 +14134,16 @@ function registerFixCommand(program3) {
|
|
|
13521
14134
|
fixed: options.dryRun ? void 0 : result.fixed
|
|
13522
14135
|
}, null, 2));
|
|
13523
14136
|
} else {
|
|
13524
|
-
console.log(
|
|
14137
|
+
console.log(import_chalk20.default.dim(`
|
|
13525
14138
|
hyv fix ${filePath}
|
|
13526
14139
|
`));
|
|
13527
14140
|
for (const change of result.changes) {
|
|
13528
|
-
console.log(
|
|
14141
|
+
console.log(import_chalk20.default.dim(` Line ${change.line}: `) + import_chalk20.default.red(change.before) + import_chalk20.default.dim(" \u2192 ") + import_chalk20.default.green(change.after));
|
|
13529
14142
|
}
|
|
13530
|
-
console.log(
|
|
14143
|
+
console.log(import_chalk20.default.green(`
|
|
13531
14144
|
\u2713 ${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} applied`));
|
|
13532
14145
|
if (result.stats.needsLLM > 0) {
|
|
13533
|
-
console.log(
|
|
14146
|
+
console.log(import_chalk20.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
|
|
13534
14147
|
}
|
|
13535
14148
|
}
|
|
13536
14149
|
if (!options.dryRun) {
|
|
@@ -13544,7 +14157,7 @@ hyv fix ${filePath}
|
|
|
13544
14157
|
process.exit(1);
|
|
13545
14158
|
}
|
|
13546
14159
|
const backupPath = writeInPlaceWithBackup(filePath, result.fixed);
|
|
13547
|
-
console.log(
|
|
14160
|
+
console.log(import_chalk20.default.dim(` Written to ${filePath} (backup: ${backupBasename(filePath)})`));
|
|
13548
14161
|
saveLastEditSession({
|
|
13549
14162
|
original_path: filePath,
|
|
13550
14163
|
edited_path: filePath,
|
|
@@ -13552,7 +14165,7 @@ hyv fix ${filePath}
|
|
|
13552
14165
|
edited_text: result.fixed,
|
|
13553
14166
|
profile: options.profile
|
|
13554
14167
|
});
|
|
13555
|
-
console.log(
|
|
14168
|
+
console.log(import_chalk20.default.dim(" Tip: hyv reinforce --last to teach your profile from this edit"));
|
|
13556
14169
|
} else if (filePath === "stdin" || !options.inPlace) {
|
|
13557
14170
|
process.stdout.write("\n" + result.fixed + "\n");
|
|
13558
14171
|
if (filePath !== "stdin") {
|
|
@@ -13568,14 +14181,14 @@ hyv fix ${filePath}
|
|
|
13568
14181
|
}
|
|
13569
14182
|
await maybeShowLimitedModeHint(!!profile);
|
|
13570
14183
|
} catch (error) {
|
|
13571
|
-
console.error(
|
|
14184
|
+
console.error(import_chalk20.default.red(`Error: ${error.message}`));
|
|
13572
14185
|
process.exit(1);
|
|
13573
14186
|
}
|
|
13574
14187
|
});
|
|
13575
14188
|
}
|
|
13576
14189
|
|
|
13577
14190
|
// src/commands/check.ts
|
|
13578
|
-
var
|
|
14191
|
+
var import_chalk21 = __toESM(require_source());
|
|
13579
14192
|
init_pipeline();
|
|
13580
14193
|
init_local_profile();
|
|
13581
14194
|
init_access();
|
|
@@ -13585,20 +14198,20 @@ function registerCheckCommand(program3) {
|
|
|
13585
14198
|
const profile = await loadProfileForCommand(options.profile);
|
|
13586
14199
|
let inputText = text;
|
|
13587
14200
|
if (text === "-") {
|
|
13588
|
-
const
|
|
14201
|
+
const fs27 = require("fs");
|
|
13589
14202
|
if (process.stdin.isTTY) {
|
|
13590
|
-
console.error(
|
|
14203
|
+
console.error(import_chalk21.default.red("No input provided. Pipe content or pass text as argument."));
|
|
13591
14204
|
process.exit(1);
|
|
13592
14205
|
}
|
|
13593
|
-
inputText =
|
|
14206
|
+
inputText = fs27.readFileSync(0, "utf-8");
|
|
13594
14207
|
}
|
|
13595
14208
|
if (!inputText.trim()) {
|
|
13596
|
-
console.error(
|
|
14209
|
+
console.error(import_chalk21.default.red("No text provided."));
|
|
13597
14210
|
process.exit(1);
|
|
13598
14211
|
}
|
|
13599
|
-
const
|
|
13600
|
-
if (text !== "-" &&
|
|
13601
|
-
console.log(
|
|
14212
|
+
const fs26 = require("fs");
|
|
14213
|
+
if (text !== "-" && fs26.existsSync(text)) {
|
|
14214
|
+
console.log(import_chalk21.default.yellow(`"${text}" looks like a file. Did you mean: hyv scan ${text}`));
|
|
13602
14215
|
process.exit(1);
|
|
13603
14216
|
}
|
|
13604
14217
|
const result = runPipeline(inputText, profile, false);
|
|
@@ -13622,30 +14235,30 @@ function registerCheckCommand(program3) {
|
|
|
13622
14235
|
}, null, 2));
|
|
13623
14236
|
} else {
|
|
13624
14237
|
if (result.stats.totalSignals === 0) {
|
|
13625
|
-
console.log(
|
|
13626
|
-
console.log(
|
|
14238
|
+
console.log(import_chalk21.default.green("\n\u2713 Clean \u2014 no AI patterns found."));
|
|
14239
|
+
console.log(import_chalk21.default.dim(` score: ${result.score}/100`));
|
|
13627
14240
|
} else {
|
|
13628
14241
|
console.log("");
|
|
13629
14242
|
printGroupedSignals(result.signalMap.signals.slice(0, 15), profile);
|
|
13630
14243
|
if (result.signalMap.signals.length > 15) {
|
|
13631
|
-
console.log(
|
|
14244
|
+
console.log(import_chalk21.default.dim(` ... and ${result.signalMap.signals.length - 15} more`));
|
|
13632
14245
|
}
|
|
13633
|
-
console.log(
|
|
14246
|
+
console.log(import_chalk21.default.yellow(`
|
|
13634
14247
|
${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow)`));
|
|
13635
|
-
console.log(
|
|
14248
|
+
console.log(import_chalk21.default.dim(` score: ${result.score}/100`));
|
|
13636
14249
|
}
|
|
13637
14250
|
await maybeShowLimitedModeHint(!!profile);
|
|
13638
14251
|
}
|
|
13639
14252
|
process.exit(result.stats.totalSignals > 0 ? 1 : 0);
|
|
13640
14253
|
} catch (error) {
|
|
13641
|
-
console.error(
|
|
14254
|
+
console.error(import_chalk21.default.red(`Error: ${error.message}`));
|
|
13642
14255
|
process.exit(1);
|
|
13643
14256
|
}
|
|
13644
14257
|
});
|
|
13645
14258
|
}
|
|
13646
14259
|
|
|
13647
14260
|
// src/commands/score.ts
|
|
13648
|
-
var
|
|
14261
|
+
var import_chalk22 = __toESM(require_source());
|
|
13649
14262
|
init_pipeline();
|
|
13650
14263
|
init_local_profile();
|
|
13651
14264
|
function registerScoreCommand(program3) {
|
|
@@ -13670,14 +14283,14 @@ function registerScoreCommand(program3) {
|
|
|
13670
14283
|
process.exit(1);
|
|
13671
14284
|
}
|
|
13672
14285
|
} catch (error) {
|
|
13673
|
-
console.error(
|
|
14286
|
+
console.error(import_chalk22.default.red(`Error: ${error.message}`));
|
|
13674
14287
|
process.exit(1);
|
|
13675
14288
|
}
|
|
13676
14289
|
});
|
|
13677
14290
|
}
|
|
13678
14291
|
|
|
13679
14292
|
// src/commands/diff.ts
|
|
13680
|
-
var
|
|
14293
|
+
var import_chalk23 = __toESM(require_source());
|
|
13681
14294
|
init_pipeline();
|
|
13682
14295
|
init_local_profile();
|
|
13683
14296
|
function registerDiffCommand(program3) {
|
|
@@ -13687,9 +14300,9 @@ function registerDiffCommand(program3) {
|
|
|
13687
14300
|
const { text, path: filePath } = readText(file);
|
|
13688
14301
|
const result = runPipeline(text, profile, true);
|
|
13689
14302
|
if (result.changes.length === 0) {
|
|
13690
|
-
console.log(
|
|
14303
|
+
console.log(import_chalk23.default.green("\n\u2713 No auto-fixable changes."));
|
|
13691
14304
|
if (result.stats.needsLLM > 0) {
|
|
13692
|
-
console.log(
|
|
14305
|
+
console.log(import_chalk23.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite`));
|
|
13693
14306
|
}
|
|
13694
14307
|
return;
|
|
13695
14308
|
}
|
|
@@ -13704,42 +14317,42 @@ function registerDiffCommand(program3) {
|
|
|
13704
14317
|
const originalLines = text.split("\n");
|
|
13705
14318
|
const fixedLines = result.fixed.split("\n");
|
|
13706
14319
|
const contextN = parseInt(options.context, 10);
|
|
13707
|
-
console.log(
|
|
13708
|
-
console.log(
|
|
14320
|
+
console.log(import_chalk23.default.dim(`--- ${filePath}`));
|
|
14321
|
+
console.log(import_chalk23.default.dim(`+++ ${filePath} (fixed)`));
|
|
13709
14322
|
for (const change of result.changes) {
|
|
13710
14323
|
const lineIdx = change.line - 1;
|
|
13711
14324
|
const start = Math.max(0, lineIdx - contextN);
|
|
13712
14325
|
const end = Math.min(originalLines.length, lineIdx + contextN + 1);
|
|
13713
|
-
console.log(
|
|
14326
|
+
console.log(import_chalk23.default.dim(`@@ -${start + 1},${end - start} +${start + 1},${end - start} @@`));
|
|
13714
14327
|
for (let i = start; i < end; i++) {
|
|
13715
14328
|
if (i === lineIdx) {
|
|
13716
|
-
console.log(
|
|
13717
|
-
console.log(
|
|
14329
|
+
console.log(import_chalk23.default.red(`-${originalLines[i]}`));
|
|
14330
|
+
console.log(import_chalk23.default.green(`+${fixedLines[i]}`));
|
|
13718
14331
|
} else {
|
|
13719
|
-
console.log(
|
|
14332
|
+
console.log(import_chalk23.default.dim(` ${originalLines[i]}`));
|
|
13720
14333
|
}
|
|
13721
14334
|
}
|
|
13722
14335
|
}
|
|
13723
|
-
console.log(
|
|
14336
|
+
console.log(import_chalk23.default.green(`
|
|
13724
14337
|
${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} available`));
|
|
13725
|
-
console.log(
|
|
14338
|
+
console.log(import_chalk23.default.dim(` run: hyv fix ${file} -i to apply`));
|
|
13726
14339
|
if (options.apply && filePath !== "stdin") {
|
|
13727
|
-
const
|
|
13728
|
-
const
|
|
14340
|
+
const fs26 = require("fs");
|
|
14341
|
+
const path25 = require("path");
|
|
13729
14342
|
const backupPath = filePath + ".bak";
|
|
13730
|
-
|
|
13731
|
-
|
|
13732
|
-
console.log(
|
|
14343
|
+
fs26.copyFileSync(filePath, backupPath);
|
|
14344
|
+
fs26.writeFileSync(filePath, result.fixed);
|
|
14345
|
+
console.log(import_chalk23.default.green(` \u2713 Applied. Backup: ${path25.basename(backupPath)}`));
|
|
13733
14346
|
}
|
|
13734
14347
|
} catch (error) {
|
|
13735
|
-
console.error(
|
|
14348
|
+
console.error(import_chalk23.default.red(`Error: ${error.message}`));
|
|
13736
14349
|
process.exit(1);
|
|
13737
14350
|
}
|
|
13738
14351
|
});
|
|
13739
14352
|
}
|
|
13740
14353
|
|
|
13741
14354
|
// src/commands/rules.ts
|
|
13742
|
-
var
|
|
14355
|
+
var import_chalk24 = __toESM(require_source());
|
|
13743
14356
|
init_config();
|
|
13744
14357
|
var RULE_CATALOG = [
|
|
13745
14358
|
// AI Overused Words
|
|
@@ -13833,7 +14446,7 @@ function registerRulesCommand(program3) {
|
|
|
13833
14446
|
for (const id of ids)
|
|
13834
14447
|
disabledRules.delete(id);
|
|
13835
14448
|
writeConfig({ ...config, disabled_rules: [...disabledRules] });
|
|
13836
|
-
console.log(
|
|
14449
|
+
console.log(import_chalk24.default.green(`
|
|
13837
14450
|
\u2713 Enabled: ${ids.join(", ")}`));
|
|
13838
14451
|
return;
|
|
13839
14452
|
}
|
|
@@ -13842,13 +14455,13 @@ function registerRulesCommand(program3) {
|
|
|
13842
14455
|
for (const id of ids)
|
|
13843
14456
|
disabledRules.add(id);
|
|
13844
14457
|
writeConfig({ ...config, disabled_rules: [...disabledRules] });
|
|
13845
|
-
console.log(
|
|
14458
|
+
console.log(import_chalk24.default.green(`
|
|
13846
14459
|
\u2713 Disabled: ${ids.join(", ")}`));
|
|
13847
14460
|
return;
|
|
13848
14461
|
}
|
|
13849
14462
|
if (options.reset) {
|
|
13850
14463
|
writeConfig({ ...config, disabled_rules: [] });
|
|
13851
|
-
console.log(
|
|
14464
|
+
console.log(import_chalk24.default.green("\n\u2713 All rules reset to default (enabled)"));
|
|
13852
14465
|
return;
|
|
13853
14466
|
}
|
|
13854
14467
|
let rules = [...RULE_CATALOG];
|
|
@@ -13876,36 +14489,36 @@ function registerRulesCommand(program3) {
|
|
|
13876
14489
|
}
|
|
13877
14490
|
console.log("");
|
|
13878
14491
|
for (const [cat, catRules] of categories) {
|
|
13879
|
-
console.log(
|
|
14492
|
+
console.log(import_chalk24.default.bold(` ${cat} rules (${catRules.length})
|
|
13880
14493
|
`));
|
|
13881
|
-
console.log(
|
|
13882
|
-
console.log(
|
|
14494
|
+
console.log(import_chalk24.default.dim(" ID Sev AutoFix Status"));
|
|
14495
|
+
console.log(import_chalk24.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
13883
14496
|
for (const rule of catRules) {
|
|
13884
|
-
const sev = rule.severity === "red" ?
|
|
13885
|
-
const fix = rule.autoFixable ?
|
|
13886
|
-
const enabled2 = disabledRules.has(rule.id) ?
|
|
14497
|
+
const sev = rule.severity === "red" ? import_chalk24.default.red("red") : import_chalk24.default.yellow("yel");
|
|
14498
|
+
const fix = rule.autoFixable ? import_chalk24.default.green(" \u2713") : import_chalk24.default.dim(" \u2717");
|
|
14499
|
+
const enabled2 = disabledRules.has(rule.id) ? import_chalk24.default.red(" \u2717 disabled") : import_chalk24.default.green(" \u2713 enabled");
|
|
13887
14500
|
const id = rule.id.padEnd(24);
|
|
13888
|
-
console.log(` ${
|
|
14501
|
+
console.log(` ${import_chalk24.default.dim(id)} ${sev} ${fix} ${enabled2}`);
|
|
13889
14502
|
}
|
|
13890
14503
|
console.log("");
|
|
13891
14504
|
}
|
|
13892
14505
|
const enabled = rules.filter((r) => !disabledRules.has(r.id)).length;
|
|
13893
14506
|
const disabled = rules.length - enabled;
|
|
13894
|
-
console.log(
|
|
13895
|
-
console.log(
|
|
13896
|
-
console.log(
|
|
14507
|
+
console.log(import_chalk24.default.dim(` ${rules.length} rules, ${enabled} enabled, ${disabled} disabled`));
|
|
14508
|
+
console.log(import_chalk24.default.dim(` toggle: hyv rules --disable <id>`));
|
|
14509
|
+
console.log(import_chalk24.default.dim(` reset: hyv rules --reset
|
|
13897
14510
|
`));
|
|
13898
14511
|
} catch (error) {
|
|
13899
|
-
console.error(
|
|
14512
|
+
console.error(import_chalk24.default.red(`Error: ${error.message}`));
|
|
13900
14513
|
process.exit(1);
|
|
13901
14514
|
}
|
|
13902
14515
|
});
|
|
13903
14516
|
}
|
|
13904
14517
|
|
|
13905
14518
|
// src/commands/batch.ts
|
|
13906
|
-
var
|
|
13907
|
-
var
|
|
13908
|
-
var
|
|
14519
|
+
var import_chalk25 = __toESM(require_source());
|
|
14520
|
+
var fs17 = __toESM(require("fs"));
|
|
14521
|
+
var path18 = __toESM(require("path"));
|
|
13909
14522
|
init_pipeline();
|
|
13910
14523
|
init_local_profile();
|
|
13911
14524
|
function registerBatchCommand(program3) {
|
|
@@ -13918,7 +14531,7 @@ function registerBatchCommand(program3) {
|
|
|
13918
14531
|
nodir: true
|
|
13919
14532
|
});
|
|
13920
14533
|
if (files.length === 0) {
|
|
13921
|
-
console.log(
|
|
14534
|
+
console.log(import_chalk25.default.yellow(`
|
|
13922
14535
|
No files matching: ${pattern}`));
|
|
13923
14536
|
return;
|
|
13924
14537
|
}
|
|
@@ -13932,16 +14545,16 @@ No files matching: ${pattern}`));
|
|
|
13932
14545
|
process.exit(1);
|
|
13933
14546
|
}
|
|
13934
14547
|
if (options.format === "text") {
|
|
13935
|
-
console.log(
|
|
14548
|
+
console.log(import_chalk25.default.dim(`
|
|
13936
14549
|
scanning ${files.length} file${files.length === 1 ? "" : "s"}...
|
|
13937
14550
|
`));
|
|
13938
14551
|
}
|
|
13939
14552
|
const results = [];
|
|
13940
14553
|
for (const file of files) {
|
|
13941
|
-
const absPath =
|
|
13942
|
-
if (!
|
|
14554
|
+
const absPath = path18.resolve(file);
|
|
14555
|
+
if (!fs17.existsSync(absPath))
|
|
13943
14556
|
continue;
|
|
13944
|
-
const text =
|
|
14557
|
+
const text = fs17.readFileSync(absPath, "utf-8");
|
|
13945
14558
|
const result = runPipeline(text, profile, options.fix || false);
|
|
13946
14559
|
results.push({
|
|
13947
14560
|
file,
|
|
@@ -13971,23 +14584,23 @@ No files matching: ${pattern}`));
|
|
|
13971
14584
|
}
|
|
13972
14585
|
} else {
|
|
13973
14586
|
for (const r of results) {
|
|
13974
|
-
const icon = r.issues > 0 ?
|
|
13975
|
-
const score = r.score < 60 ?
|
|
13976
|
-
const issueStr = r.issues > 0 ?
|
|
14587
|
+
const icon = r.issues > 0 ? import_chalk25.default.red("\u25CF") : import_chalk25.default.green("\u25CB");
|
|
14588
|
+
const score = r.score < 60 ? import_chalk25.default.red(r.score) : r.score < 80 ? import_chalk25.default.yellow(r.score) : import_chalk25.default.green(r.score);
|
|
14589
|
+
const issueStr = r.issues > 0 ? import_chalk25.default.red(`${r.issues} issues`) : import_chalk25.default.green("clean");
|
|
13977
14590
|
console.log(` ${icon} ${r.file.padEnd(40)} ${issueStr.padEnd(20)} score: ${score}`);
|
|
13978
14591
|
}
|
|
13979
14592
|
const withIssues = results.filter((r) => r.issues > 0).length;
|
|
13980
14593
|
const totalIssues = results.reduce((sum, r) => sum + r.issues, 0);
|
|
13981
14594
|
const worst = results.reduce((min, r) => r.score < min.score ? r : min, results[0]);
|
|
13982
|
-
console.log(
|
|
14595
|
+
console.log(import_chalk25.default.dim(`
|
|
13983
14596
|
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`));
|
|
13984
|
-
console.log(
|
|
14597
|
+
console.log(import_chalk25.default.dim(` ${results.length} files, ${withIssues} with issues, ${totalIssues} total issues`));
|
|
13985
14598
|
if (withIssues > 0) {
|
|
13986
|
-
console.log(
|
|
14599
|
+
console.log(import_chalk25.default.dim(` worst: ${worst.file} (${worst.score}/100)`));
|
|
13987
14600
|
}
|
|
13988
14601
|
if (options.fix && options.inPlace) {
|
|
13989
14602
|
const fixed = results.filter((r) => r.autoFixes > 0);
|
|
13990
|
-
console.log(
|
|
14603
|
+
console.log(import_chalk25.default.green(`
|
|
13991
14604
|
\u2713 ${fixed.reduce((sum, r) => sum + r.autoFixes, 0)} auto-fixes applied across ${fixed.length} files`));
|
|
13992
14605
|
}
|
|
13993
14606
|
}
|
|
@@ -13999,25 +14612,25 @@ No files matching: ${pattern}`));
|
|
|
13999
14612
|
if (belowThreshold)
|
|
14000
14613
|
process.exit(1);
|
|
14001
14614
|
} catch (error) {
|
|
14002
|
-
console.error(
|
|
14615
|
+
console.error(import_chalk25.default.red(`Error: ${error.message}`));
|
|
14003
14616
|
process.exit(1);
|
|
14004
14617
|
}
|
|
14005
14618
|
});
|
|
14006
14619
|
}
|
|
14007
14620
|
|
|
14008
14621
|
// src/commands/watch.ts
|
|
14009
|
-
var
|
|
14010
|
-
var
|
|
14011
|
-
var
|
|
14622
|
+
var import_chalk26 = __toESM(require_source());
|
|
14623
|
+
var fs18 = __toESM(require("fs"));
|
|
14624
|
+
var path19 = __toESM(require("path"));
|
|
14012
14625
|
init_pipeline();
|
|
14013
14626
|
init_local_profile();
|
|
14014
14627
|
function registerWatchCommand(program3) {
|
|
14015
14628
|
program3.command("watch").description("Watch a file and re-scan on every save").argument("<file>", "File to watch").option("--command <cmd>", "What to run on change: scan, score, fix", "scan").option("--debounce <ms>", "Delay after save before scanning", "300").option("--notify", "OS notification when issues found").option("-y, --yes", "Confirm destructive auto-fix writes without prompting").option("--profile <name>", "Voice profile").action(async (file, options) => {
|
|
14016
14629
|
try {
|
|
14017
14630
|
const profile = await loadProfileForCommand(options.profile);
|
|
14018
|
-
const absPath =
|
|
14019
|
-
if (!
|
|
14020
|
-
console.error(
|
|
14631
|
+
const absPath = path19.resolve(file);
|
|
14632
|
+
if (!fs18.existsSync(absPath)) {
|
|
14633
|
+
console.error(import_chalk26.default.red(`File not found: ${absPath}`));
|
|
14021
14634
|
process.exit(1);
|
|
14022
14635
|
}
|
|
14023
14636
|
if (options.command === "fix") {
|
|
@@ -14029,59 +14642,59 @@ function registerWatchCommand(program3) {
|
|
|
14029
14642
|
if (!confirmed)
|
|
14030
14643
|
process.exit(1);
|
|
14031
14644
|
}
|
|
14032
|
-
console.log(
|
|
14645
|
+
console.log(import_chalk26.default.dim(`
|
|
14033
14646
|
watching ${absPath}`));
|
|
14034
|
-
console.log(
|
|
14035
|
-
console.log(
|
|
14647
|
+
console.log(import_chalk26.default.dim(`command: ${options.command} debounce: ${options.debounce}ms`));
|
|
14648
|
+
console.log(import_chalk26.default.dim("ctrl+c to stop\n"));
|
|
14036
14649
|
let debounceTimer = null;
|
|
14037
14650
|
const debounceMs = parseInt(options.debounce, 10);
|
|
14038
14651
|
let scanCount = 0;
|
|
14039
14652
|
let lastScore = 0;
|
|
14040
14653
|
let ignoreWatchUntil = 0;
|
|
14041
14654
|
const runScan = () => {
|
|
14042
|
-
const text =
|
|
14655
|
+
const text = fs18.readFileSync(absPath, "utf-8");
|
|
14043
14656
|
const result = runPipeline(text, profile, options.command === "fix");
|
|
14044
14657
|
scanCount++;
|
|
14045
14658
|
const now = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
14046
|
-
console.log(
|
|
14659
|
+
console.log(import_chalk26.default.dim(`[${now}] saved \u2014 ${options.command}ing...`));
|
|
14047
14660
|
if (options.command === "score") {
|
|
14048
14661
|
const score = result.score;
|
|
14049
|
-
const color = score < 60 ?
|
|
14662
|
+
const color = score < 60 ? import_chalk26.default.red : score < 80 ? import_chalk26.default.yellow : import_chalk26.default.green;
|
|
14050
14663
|
console.log(` ${color(score + "/100")}`);
|
|
14051
14664
|
if (lastScore > 0 && score !== lastScore) {
|
|
14052
14665
|
const delta = score - lastScore;
|
|
14053
|
-
console.log(
|
|
14666
|
+
console.log(import_chalk26.default.dim(` ${delta > 0 ? "\u2191" : "\u2193"} ${Math.abs(delta)} from last scan`));
|
|
14054
14667
|
}
|
|
14055
14668
|
lastScore = score;
|
|
14056
14669
|
} else if (options.command === "fix") {
|
|
14057
14670
|
if (result.changes.length > 0) {
|
|
14058
14671
|
for (const change of result.changes) {
|
|
14059
|
-
console.log(
|
|
14672
|
+
console.log(import_chalk26.default.dim(` Line ${change.line}: `) + import_chalk26.default.red(change.before) + import_chalk26.default.dim(" \u2192 ") + import_chalk26.default.green(change.after));
|
|
14060
14673
|
}
|
|
14061
14674
|
ignoreWatchUntil = Date.now() + debounceMs + 200;
|
|
14062
14675
|
writeInPlaceWithBackup(absPath, result.fixed);
|
|
14063
|
-
console.log(
|
|
14676
|
+
console.log(import_chalk26.default.green(` \u2713 ${result.changes.length} auto-fixes applied`));
|
|
14064
14677
|
} else {
|
|
14065
|
-
console.log(
|
|
14678
|
+
console.log(import_chalk26.default.green(" \u2713 no auto-fixable issues"));
|
|
14066
14679
|
}
|
|
14067
14680
|
} else {
|
|
14068
14681
|
if (result.stats.totalSignals === 0) {
|
|
14069
|
-
console.log(
|
|
14682
|
+
console.log(import_chalk26.default.green(` \u2713 clean \u2014 score: ${result.score}/100`));
|
|
14070
14683
|
} else {
|
|
14071
|
-
console.log(
|
|
14684
|
+
console.log(import_chalk26.default.yellow(` ${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow) score: ${result.score}/100`));
|
|
14072
14685
|
for (const signal of result.signalMap.signals.slice(0, 3)) {
|
|
14073
|
-
const sev = signal.severity === "red" ?
|
|
14686
|
+
const sev = signal.severity === "red" ? import_chalk26.default.red("\u25CF") : import_chalk26.default.yellow("\u25CB");
|
|
14074
14687
|
console.log(` ${sev} line ${signal.line}: ${signal.id} \u2014 ${signal.suggestion}`);
|
|
14075
14688
|
}
|
|
14076
14689
|
if (result.signalMap.signals.length > 3) {
|
|
14077
|
-
console.log(
|
|
14690
|
+
console.log(import_chalk26.default.dim(` ... and ${result.signalMap.signals.length - 3} more`));
|
|
14078
14691
|
}
|
|
14079
14692
|
}
|
|
14080
14693
|
}
|
|
14081
14694
|
console.log("");
|
|
14082
14695
|
};
|
|
14083
14696
|
runScan();
|
|
14084
|
-
|
|
14697
|
+
fs18.watch(absPath, (eventType) => {
|
|
14085
14698
|
if (eventType !== "change")
|
|
14086
14699
|
return;
|
|
14087
14700
|
if (Date.now() < ignoreWatchUntil)
|
|
@@ -14091,22 +14704,22 @@ watching ${absPath}`));
|
|
|
14091
14704
|
debounceTimer = setTimeout(runScan, debounceMs);
|
|
14092
14705
|
});
|
|
14093
14706
|
process.on("SIGINT", () => {
|
|
14094
|
-
console.log(
|
|
14707
|
+
console.log(import_chalk26.default.dim(`
|
|
14095
14708
|
${scanCount} scans completed. exiting.`));
|
|
14096
14709
|
process.exit(0);
|
|
14097
14710
|
});
|
|
14098
14711
|
await new Promise(() => {
|
|
14099
14712
|
});
|
|
14100
14713
|
} catch (error) {
|
|
14101
|
-
console.error(
|
|
14714
|
+
console.error(import_chalk26.default.red(`Error: ${error.message}`));
|
|
14102
14715
|
process.exit(1);
|
|
14103
14716
|
}
|
|
14104
14717
|
});
|
|
14105
14718
|
}
|
|
14106
14719
|
|
|
14107
14720
|
// src/commands/demo.ts
|
|
14108
|
-
var
|
|
14109
|
-
var
|
|
14721
|
+
var import_chalk27 = __toESM(require_source());
|
|
14722
|
+
var fs19 = __toESM(require("fs"));
|
|
14110
14723
|
var SAMPLES = {
|
|
14111
14724
|
linkedin: {
|
|
14112
14725
|
text: `In today's fast-paced digital landscape, it's important to note that leveraging the right tools is not just nice to have, but essential for success. Let's delve into the holistic approach that will transform your workflow.
|
|
@@ -14196,46 +14809,46 @@ function registerDemoCommand(program3) {
|
|
|
14196
14809
|
const style = options.clean ? "clean" : options.style;
|
|
14197
14810
|
const sample = SAMPLES[style];
|
|
14198
14811
|
if (!sample) {
|
|
14199
|
-
console.error(
|
|
14812
|
+
console.error(import_chalk27.default.red(`Unknown style: ${options.style}. Valid: ${Object.keys(SAMPLES).filter((k) => k !== "clean").join(", ")}`));
|
|
14200
14813
|
process.exit(1);
|
|
14201
14814
|
}
|
|
14202
14815
|
if (options.output) {
|
|
14203
14816
|
const outputPath = require("path").resolve(options.output);
|
|
14204
|
-
|
|
14205
|
-
console.log(
|
|
14817
|
+
fs19.writeFileSync(outputPath, sample.text);
|
|
14818
|
+
console.log(import_chalk27.default.green(`
|
|
14206
14819
|
\u2713 Sample written to ${outputPath}`));
|
|
14207
14820
|
if (sample.patterns.length > 0) {
|
|
14208
|
-
console.log(
|
|
14821
|
+
console.log(import_chalk27.default.dim(` Contains ${sample.patterns.length} AI patterns: ${sample.patterns.slice(0, 5).join(", ")}...`));
|
|
14209
14822
|
}
|
|
14210
|
-
console.log(
|
|
14823
|
+
console.log(import_chalk27.default.dim(`
|
|
14211
14824
|
Scan it: hyv scan ${options.output}`));
|
|
14212
|
-
console.log(
|
|
14825
|
+
console.log(import_chalk27.default.dim(` Fix it: hyv fix ${options.output}`));
|
|
14213
14826
|
} else {
|
|
14214
|
-
console.log(
|
|
14827
|
+
console.log(import_chalk27.default.bold(`
|
|
14215
14828
|
\u2500\u2500\u2500 sample (${style}) \u2500\u2500\u2500
|
|
14216
14829
|
`));
|
|
14217
14830
|
console.log(sample.text);
|
|
14218
|
-
console.log(
|
|
14831
|
+
console.log(import_chalk27.default.dim(`
|
|
14219
14832
|
\u2500\u2500\u2500 end \u2500\u2500\u2500`));
|
|
14220
14833
|
if (sample.patterns.length > 0) {
|
|
14221
|
-
console.log(
|
|
14834
|
+
console.log(import_chalk27.default.dim(`
|
|
14222
14835
|
${sample.patterns.length} AI patterns embedded: ${sample.patterns.slice(0, 8).join(", ")}...`));
|
|
14223
14836
|
}
|
|
14224
|
-
console.log(
|
|
14837
|
+
console.log(import_chalk27.default.dim(`
|
|
14225
14838
|
Save to file: hyv demo --output demo.md`));
|
|
14226
|
-
console.log(
|
|
14227
|
-
console.log(
|
|
14839
|
+
console.log(import_chalk27.default.dim(` Scan it: hyv scan demo.md`));
|
|
14840
|
+
console.log(import_chalk27.default.dim(` Fix it: hyv fix demo.md
|
|
14228
14841
|
`));
|
|
14229
14842
|
}
|
|
14230
14843
|
} catch (error) {
|
|
14231
|
-
console.error(
|
|
14844
|
+
console.error(import_chalk27.default.red(`Error: ${error.message}`));
|
|
14232
14845
|
process.exit(1);
|
|
14233
14846
|
}
|
|
14234
14847
|
});
|
|
14235
14848
|
}
|
|
14236
14849
|
|
|
14237
14850
|
// src/commands/open.ts
|
|
14238
|
-
var
|
|
14851
|
+
var import_chalk28 = __toESM(require_source());
|
|
14239
14852
|
var PAGES = {
|
|
14240
14853
|
dashboard: "https://holdyourvoice.com/dashboard",
|
|
14241
14854
|
profiles: "https://holdyourvoice.com/dashboard",
|
|
@@ -14252,12 +14865,12 @@ function registerOpenCommand(program3) {
|
|
|
14252
14865
|
return;
|
|
14253
14866
|
}
|
|
14254
14867
|
const open3 = (await Promise.resolve().then(() => __toESM(require_open()))).default;
|
|
14255
|
-
console.log(
|
|
14868
|
+
console.log(import_chalk28.default.dim(`
|
|
14256
14869
|
Opening ${url}...`));
|
|
14257
14870
|
await open3(url);
|
|
14258
|
-
console.log(
|
|
14871
|
+
console.log(import_chalk28.default.green(" \u2713 Opened in browser\n"));
|
|
14259
14872
|
} catch (error) {
|
|
14260
|
-
console.error(
|
|
14873
|
+
console.error(import_chalk28.default.red(`Error: ${error.message}`));
|
|
14261
14874
|
process.exit(1);
|
|
14262
14875
|
}
|
|
14263
14876
|
});
|
|
@@ -14267,46 +14880,67 @@ function registerOpenCommand(program3) {
|
|
|
14267
14880
|
init_welcome();
|
|
14268
14881
|
|
|
14269
14882
|
// src/lib/onboarding.ts
|
|
14270
|
-
var
|
|
14271
|
-
var
|
|
14272
|
-
var
|
|
14273
|
-
var hyvDir =
|
|
14274
|
-
var onboardingFile =
|
|
14883
|
+
var fs20 = __toESM(require("fs"));
|
|
14884
|
+
var path20 = __toESM(require("path"));
|
|
14885
|
+
var os7 = __toESM(require("os"));
|
|
14886
|
+
var hyvDir = path20.join(os7.homedir(), ".hyv");
|
|
14887
|
+
var onboardingFile = path20.join(hyvDir, "onboarding-complete.json");
|
|
14275
14888
|
function hasCompletedOnboarding() {
|
|
14276
14889
|
try {
|
|
14277
|
-
return
|
|
14890
|
+
return fs20.existsSync(onboardingFile);
|
|
14278
14891
|
} catch {
|
|
14279
14892
|
return false;
|
|
14280
14893
|
}
|
|
14281
14894
|
}
|
|
14282
14895
|
function markOnboardingComplete(version) {
|
|
14283
|
-
if (!
|
|
14284
|
-
|
|
14285
|
-
|
|
14896
|
+
if (!fs20.existsSync(hyvDir))
|
|
14897
|
+
fs20.mkdirSync(hyvDir, { recursive: true });
|
|
14898
|
+
fs20.writeFileSync(
|
|
14286
14899
|
onboardingFile,
|
|
14287
14900
|
JSON.stringify({ version, completed_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
|
|
14288
14901
|
);
|
|
14289
14902
|
}
|
|
14290
14903
|
|
|
14291
14904
|
// src/commands/welcome.ts
|
|
14292
|
-
|
|
14293
|
-
var
|
|
14905
|
+
init_welcome_flow();
|
|
14906
|
+
var fs21 = __toESM(require("fs"));
|
|
14907
|
+
var path21 = __toESM(require("path"));
|
|
14294
14908
|
function registerWelcomeCommand(program3) {
|
|
14295
14909
|
const pkgVersion3 = (() => {
|
|
14296
14910
|
try {
|
|
14297
|
-
const pkgPath3 =
|
|
14298
|
-
return JSON.parse(
|
|
14911
|
+
const pkgPath3 = path21.resolve(__dirname, "..", "package.json");
|
|
14912
|
+
return JSON.parse(fs21.readFileSync(pkgPath3, "utf-8")).version;
|
|
14299
14913
|
} catch {
|
|
14300
14914
|
return "0.0.0";
|
|
14301
14915
|
}
|
|
14302
14916
|
})();
|
|
14303
|
-
|
|
14917
|
+
program3.command("welcome").description("Set up your voice: name \u2192 samples \u2192 test \u2192 signup").option("--guide", "Show LLM/terminal guide (no prompts)").option("--extract-prompt [name]", "Print chat extraction prompt for agents").option("--step <n>", "Show guide for step 1\u20134", (v) => parseInt(v, 10)).option("--no-demo", "Skip sample scan line in guide mode").option("--quiet", "Suppress output").action(async (opts) => {
|
|
14304
14918
|
if (opts.quiet)
|
|
14305
14919
|
return;
|
|
14306
|
-
|
|
14307
|
-
|
|
14308
|
-
|
|
14309
|
-
|
|
14920
|
+
const fromPostinstall = process.env.HYV_POSTINSTALL_ONBOARDING === "1";
|
|
14921
|
+
if (opts.extractPrompt !== void 0) {
|
|
14922
|
+
const name = typeof opts.extractPrompt === "string" ? opts.extractPrompt : "my-voice";
|
|
14923
|
+
console.log("\n" + buildVoiceExtractionPrompt(name) + "\n");
|
|
14924
|
+
markOnboardingComplete(pkgVersion3);
|
|
14925
|
+
return;
|
|
14926
|
+
}
|
|
14927
|
+
if (opts.step && opts.step >= 1 && opts.step <= 4) {
|
|
14928
|
+
console.log("\n" + getMcpWelcomeResponse({ step: opts.step }) + "\n");
|
|
14929
|
+
markOnboardingComplete(pkgVersion3);
|
|
14930
|
+
return;
|
|
14931
|
+
}
|
|
14932
|
+
if (opts.guide || fromPostinstall || !process.stdin.isTTY) {
|
|
14933
|
+
printWelcome({ guide: true, skipDemo: opts.demo === false });
|
|
14934
|
+
markOnboardingComplete(pkgVersion3);
|
|
14935
|
+
return;
|
|
14936
|
+
}
|
|
14937
|
+
try {
|
|
14938
|
+
await runInteractiveWelcome();
|
|
14939
|
+
markOnboardingComplete(pkgVersion3);
|
|
14940
|
+
} catch {
|
|
14941
|
+
process.exit(1);
|
|
14942
|
+
}
|
|
14943
|
+
});
|
|
14310
14944
|
program3.command("free").description("List everything free in the CLI and on the web").action(() => {
|
|
14311
14945
|
console.log("\n" + formatFreeToolsList() + "\n");
|
|
14312
14946
|
});
|
|
@@ -14314,12 +14948,15 @@ function registerWelcomeCommand(program3) {
|
|
|
14314
14948
|
function getDemoText() {
|
|
14315
14949
|
return formatDemoResult(runWelcomeDemo());
|
|
14316
14950
|
}
|
|
14317
|
-
function getWelcomeText() {
|
|
14318
|
-
|
|
14951
|
+
function getWelcomeText(args2) {
|
|
14952
|
+
if (args2?.mode || args2?.step) {
|
|
14953
|
+
return getMcpWelcomeResponse(args2);
|
|
14954
|
+
}
|
|
14955
|
+
return buildWelcomeGuide({ forLlm: true });
|
|
14319
14956
|
}
|
|
14320
14957
|
|
|
14321
14958
|
// src/commands/content.ts
|
|
14322
|
-
var
|
|
14959
|
+
var import_chalk29 = __toESM(require_source());
|
|
14323
14960
|
init_free_paid();
|
|
14324
14961
|
var TEMPLATES = {
|
|
14325
14962
|
cursor: {
|
|
@@ -14393,30 +15030,30 @@ ${COMMUNITY_URL}`
|
|
|
14393
15030
|
function registerContentCommand(program3) {
|
|
14394
15031
|
program3.command("content").description("Blog outlines, CI snippets, and share templates").argument("[topic]", "cursor | agents | ci | share", "cursor").option("--list", "List available templates").action((topic, opts) => {
|
|
14395
15032
|
if (opts.list) {
|
|
14396
|
-
console.log(
|
|
15033
|
+
console.log(import_chalk29.default.bold("\nhyv content templates\n"));
|
|
14397
15034
|
for (const [key, t2] of Object.entries(TEMPLATES)) {
|
|
14398
|
-
console.log(
|
|
15035
|
+
console.log(import_chalk29.default.dim(` ${key}`) + ` \u2014 ${t2.title}`);
|
|
14399
15036
|
}
|
|
14400
15037
|
console.log("");
|
|
14401
15038
|
return;
|
|
14402
15039
|
}
|
|
14403
15040
|
const t = TEMPLATES[topic] || TEMPLATES.cursor;
|
|
14404
|
-
console.log(
|
|
15041
|
+
console.log(import_chalk29.default.bold(`
|
|
14405
15042
|
${t.title}
|
|
14406
15043
|
`));
|
|
14407
15044
|
console.log(t.body);
|
|
14408
|
-
console.log(
|
|
15045
|
+
console.log(import_chalk29.default.dim("\nBlog: https://holdyourvoice.com/blog\n"));
|
|
14409
15046
|
});
|
|
14410
15047
|
}
|
|
14411
15048
|
|
|
14412
15049
|
// src/commands/upgrade.ts
|
|
14413
|
-
var
|
|
15050
|
+
var import_chalk30 = __toESM(require_source());
|
|
14414
15051
|
var import_child_process3 = require("child_process");
|
|
14415
15052
|
init_version();
|
|
14416
15053
|
function registerUpgradeCommand(program3) {
|
|
14417
15054
|
program3.command("upgrade").description("Upgrade to the latest @holdyourvoice/hyv").option("--check", "Only check if an update is available").action(async (opts) => {
|
|
14418
15055
|
const current = getCliVersion();
|
|
14419
|
-
console.log(
|
|
15056
|
+
console.log(import_chalk30.default.dim(`
|
|
14420
15057
|
Current: ${getEngineLabel()}
|
|
14421
15058
|
`));
|
|
14422
15059
|
let latest = current;
|
|
@@ -14424,38 +15061,38 @@ Current: ${getEngineLabel()}
|
|
|
14424
15061
|
const out = (0, import_child_process3.execSync)("npm view @holdyourvoice/hyv version", { encoding: "utf-8", timeout: 15e3 });
|
|
14425
15062
|
latest = out.trim();
|
|
14426
15063
|
} catch {
|
|
14427
|
-
console.log(
|
|
15064
|
+
console.log(import_chalk30.default.yellow("Could not reach npm registry. Try: npm i -g @holdyourvoice/hyv@latest"));
|
|
14428
15065
|
return;
|
|
14429
15066
|
}
|
|
14430
15067
|
const cmp = compareSemver(current, latest);
|
|
14431
15068
|
if (cmp === 0) {
|
|
14432
|
-
console.log(
|
|
15069
|
+
console.log(import_chalk30.default.green("\u2713 You are on the latest version."));
|
|
14433
15070
|
return;
|
|
14434
15071
|
}
|
|
14435
15072
|
if (cmp > 0) {
|
|
14436
|
-
console.log(
|
|
15073
|
+
console.log(import_chalk30.default.green(`\u2713 You are ahead of npm (published: ${latest}).`));
|
|
14437
15074
|
return;
|
|
14438
15075
|
}
|
|
14439
|
-
console.log(
|
|
15076
|
+
console.log(import_chalk30.default.cyan(`Update available: ${current} \u2192 ${latest}`));
|
|
14440
15077
|
if (opts.check) {
|
|
14441
|
-
console.log(
|
|
15078
|
+
console.log(import_chalk30.default.dim("\nRun: hyv upgrade or npm i -g @holdyourvoice/hyv@latest\n"));
|
|
14442
15079
|
return;
|
|
14443
15080
|
}
|
|
14444
|
-
console.log(
|
|
15081
|
+
console.log(import_chalk30.default.dim("Installing..."));
|
|
14445
15082
|
try {
|
|
14446
15083
|
(0, import_child_process3.execSync)("npm i -g @holdyourvoice/hyv@latest", { stdio: "inherit" });
|
|
14447
|
-
console.log(
|
|
15084
|
+
console.log(import_chalk30.default.green("\n\u2713 Upgraded successfully. Restart your terminal.\n"));
|
|
14448
15085
|
} catch {
|
|
14449
|
-
console.log(
|
|
15086
|
+
console.log(import_chalk30.default.red("\nUpgrade failed. Run manually: npm i -g @holdyourvoice/hyv@latest\n"));
|
|
14450
15087
|
process.exit(1);
|
|
14451
15088
|
}
|
|
14452
15089
|
});
|
|
14453
15090
|
}
|
|
14454
15091
|
|
|
14455
15092
|
// src/mcp.ts
|
|
14456
|
-
var
|
|
14457
|
-
var
|
|
14458
|
-
var
|
|
15093
|
+
var fs22 = __toESM(require("fs"));
|
|
15094
|
+
var path22 = __toESM(require("path"));
|
|
15095
|
+
var os8 = __toESM(require("os"));
|
|
14459
15096
|
init_classifier();
|
|
14460
15097
|
init_autofix();
|
|
14461
15098
|
init_validator();
|
|
@@ -14466,20 +15103,20 @@ init_welcome();
|
|
|
14466
15103
|
init_config();
|
|
14467
15104
|
init_telemetry();
|
|
14468
15105
|
init_access();
|
|
14469
|
-
var VOICE_MD =
|
|
15106
|
+
var VOICE_MD = path22.join(os8.homedir(), ".hyv", "voice.md");
|
|
14470
15107
|
var MAX_RESPONSE_CHARS = 12e4;
|
|
14471
15108
|
var MAX_SCAN_FILE_BYTES = 2 * 1024 * 1024;
|
|
14472
15109
|
function readScanFile(filePath) {
|
|
14473
|
-
const cwdReal =
|
|
14474
|
-
const resolved =
|
|
14475
|
-
if (!resolved.startsWith(cwdReal +
|
|
15110
|
+
const cwdReal = fs22.realpathSync(process.cwd());
|
|
15111
|
+
const resolved = fs22.realpathSync(path22.resolve(filePath));
|
|
15112
|
+
if (!resolved.startsWith(cwdReal + path22.sep) && resolved !== cwdReal) {
|
|
14476
15113
|
throw new Error(`file must be in the current working directory (${cwdReal})`);
|
|
14477
15114
|
}
|
|
14478
|
-
const stat =
|
|
15115
|
+
const stat = fs22.statSync(resolved);
|
|
14479
15116
|
if (stat.size > MAX_SCAN_FILE_BYTES) {
|
|
14480
15117
|
throw new Error(`file too large (max ${MAX_SCAN_FILE_BYTES} bytes)`);
|
|
14481
15118
|
}
|
|
14482
|
-
return
|
|
15119
|
+
return fs22.readFileSync(resolved, "utf-8");
|
|
14483
15120
|
}
|
|
14484
15121
|
function toolResultPayload(result) {
|
|
14485
15122
|
const isError = result.startsWith("Error:") || result.startsWith("Unknown tool:");
|
|
@@ -14488,10 +15125,10 @@ function toolResultPayload(result) {
|
|
|
14488
15125
|
...isError ? { isError: true } : {}
|
|
14489
15126
|
};
|
|
14490
15127
|
}
|
|
14491
|
-
var pkgPath =
|
|
15128
|
+
var pkgPath = path22.resolve(__dirname, "..", "package.json");
|
|
14492
15129
|
var pkgVersion = (() => {
|
|
14493
15130
|
try {
|
|
14494
|
-
return JSON.parse(
|
|
15131
|
+
return JSON.parse(fs22.readFileSync(pkgPath, "utf-8")).version;
|
|
14495
15132
|
} catch {
|
|
14496
15133
|
return "2.7.1";
|
|
14497
15134
|
}
|
|
@@ -14720,8 +15357,8 @@ ${result.fixed}`;
|
|
|
14720
15357
|
return `Error: ${err.message}`;
|
|
14721
15358
|
}
|
|
14722
15359
|
}
|
|
14723
|
-
function toolWelcome() {
|
|
14724
|
-
return getWelcomeText();
|
|
15360
|
+
function toolWelcome(args2 = {}) {
|
|
15361
|
+
return getWelcomeText(args2);
|
|
14725
15362
|
}
|
|
14726
15363
|
function toolListFreeTools() {
|
|
14727
15364
|
return formatFreeToolsList();
|
|
@@ -14825,8 +15462,15 @@ async function toolClean(args2) {
|
|
|
14825
15462
|
var TOOLS = [
|
|
14826
15463
|
{
|
|
14827
15464
|
name: "hyv_welcome",
|
|
14828
|
-
description: "
|
|
14829
|
-
inputSchema: {
|
|
15465
|
+
description: "Profile-first onboarding: name \u2192 samples \u2192 test draft \u2192 signup. Call when the user is new. Use step (1-4) for one step, mode extract_prompt for chat voice extraction.",
|
|
15466
|
+
inputSchema: {
|
|
15467
|
+
type: "object",
|
|
15468
|
+
properties: {
|
|
15469
|
+
step: { type: "number", description: "Guide for step 1=name, 2=samples, 3=test, 4=signup" },
|
|
15470
|
+
mode: { type: "string", description: "Set to extract_prompt for LLM chat extraction prompt" },
|
|
15471
|
+
profile: { type: "string", description: "Profile name for extraction prompt" }
|
|
15472
|
+
}
|
|
15473
|
+
}
|
|
14830
15474
|
},
|
|
14831
15475
|
{
|
|
14832
15476
|
name: "hyv_list_free_tools",
|
|
@@ -14963,15 +15607,15 @@ var PROMPTS = [
|
|
|
14963
15607
|
];
|
|
14964
15608
|
var HYV_STATUS_PROMPT = `Use hold your voice MCP tools:
|
|
14965
15609
|
|
|
14966
|
-
1.
|
|
14967
|
-
2.
|
|
14968
|
-
3.
|
|
14969
|
-
4.
|
|
14970
|
-
5.
|
|
15610
|
+
1. New user \u2192 hyv_welcome (or step 1\u20134). Step 2: hyv_welcome mode=extract_prompt to build profile from chat.
|
|
15611
|
+
2. Save profile \u2192 user runs hyv import <name> file.md (or you draft markdown for them).
|
|
15612
|
+
3. hyv_profiles \u2014 list local/account profiles.
|
|
15613
|
+
4. Writing: hyv_scan \u2192 hyv_fix / hyv_rewrite \u2192 hyv_validate.
|
|
15614
|
+
5. Ready to pay \u2192 hyv init + hyv plan --upgrade (signup last, not first).
|
|
14971
15615
|
|
|
14972
15616
|
Keep the answer compact.`;
|
|
14973
15617
|
var TOOL_HANDLERS = {
|
|
14974
|
-
hyv_welcome:
|
|
15618
|
+
hyv_welcome: toolWelcome,
|
|
14975
15619
|
hyv_list_free_tools: () => toolListFreeTools(),
|
|
14976
15620
|
hyv_demo: () => toolDemo(),
|
|
14977
15621
|
hyv_rewrite: toolRewrite,
|
|
@@ -15065,7 +15709,7 @@ async function handleRequest(line) {
|
|
|
15065
15709
|
}
|
|
15066
15710
|
async function startMcpServer() {
|
|
15067
15711
|
const access = await getAccessState().catch(() => null);
|
|
15068
|
-
if (
|
|
15712
|
+
if (fs22.existsSync(VOICE_MD)) {
|
|
15069
15713
|
mcpLog("info", `voice profile: ${VOICE_MD}`);
|
|
15070
15714
|
} else {
|
|
15071
15715
|
mcpLog("info", "free local engine ready \u2014 no voice profile yet");
|
|
@@ -15097,142 +15741,142 @@ async function startMcpServer() {
|
|
|
15097
15741
|
}
|
|
15098
15742
|
|
|
15099
15743
|
// src/lib/mcp-setup.ts
|
|
15100
|
-
var
|
|
15101
|
-
var
|
|
15102
|
-
var
|
|
15103
|
-
var
|
|
15744
|
+
var import_chalk31 = __toESM(require_source());
|
|
15745
|
+
var fs23 = __toESM(require("fs"));
|
|
15746
|
+
var path23 = __toESM(require("path"));
|
|
15747
|
+
var os9 = __toESM(require("os"));
|
|
15104
15748
|
init_version();
|
|
15105
|
-
var HOME2 =
|
|
15749
|
+
var HOME2 = os9.homedir();
|
|
15106
15750
|
var IS_WIN2 = process.platform === "win32";
|
|
15107
15751
|
function claudeDesktopConfigPath() {
|
|
15108
15752
|
if (IS_WIN2)
|
|
15109
|
-
return
|
|
15753
|
+
return path23.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
|
|
15110
15754
|
if (process.platform === "linux")
|
|
15111
|
-
return
|
|
15112
|
-
return
|
|
15755
|
+
return path23.join(HOME2, ".config", "Claude", "claude_desktop_config.json");
|
|
15756
|
+
return path23.join(HOME2, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
15113
15757
|
}
|
|
15114
15758
|
function printMcpSetup() {
|
|
15115
|
-
console.log(
|
|
15116
|
-
console.log(
|
|
15759
|
+
console.log(import_chalk31.default.bold("\nhold your voice \u2014 mcp setup\n"));
|
|
15760
|
+
console.log(import_chalk31.default.dim(` ${getEngineLabel()}
|
|
15117
15761
|
`));
|
|
15118
|
-
console.log(
|
|
15119
|
-
console.log(
|
|
15120
|
-
console.log(
|
|
15121
|
-
console.log(
|
|
15762
|
+
console.log(import_chalk31.default.bold("Claude Desktop"));
|
|
15763
|
+
console.log(import_chalk31.default.dim(" Add to claude_desktop_config.json \u2192 mcpServers.hyv:"));
|
|
15764
|
+
console.log(import_chalk31.default.cyan(` ${mcpServerSnippet().split("\n").join("\n ")}`));
|
|
15765
|
+
console.log(import_chalk31.default.dim(` Config: ${claudeDesktopConfigPath()}
|
|
15122
15766
|
`));
|
|
15123
|
-
console.log(
|
|
15124
|
-
console.log(
|
|
15125
|
-
console.log(
|
|
15126
|
-
console.log(
|
|
15127
|
-
console.log(
|
|
15128
|
-
console.log(
|
|
15129
|
-
console.log(
|
|
15130
|
-
console.log(
|
|
15131
|
-
console.log(
|
|
15132
|
-
console.log(
|
|
15133
|
-
console.log(
|
|
15134
|
-
console.log(
|
|
15135
|
-
console.log(
|
|
15136
|
-
console.log(
|
|
15137
|
-
console.log(
|
|
15138
|
-
console.log(
|
|
15139
|
-
console.log(
|
|
15140
|
-
console.log(
|
|
15141
|
-
console.log(
|
|
15142
|
-
console.log(
|
|
15767
|
+
console.log(import_chalk31.default.bold("Cursor"));
|
|
15768
|
+
console.log(import_chalk31.default.dim(" MCP: ~/.cursor/mcp.json \u2192 mcpServers.hyv (same JSON as above)"));
|
|
15769
|
+
console.log(import_chalk31.default.dim(" Rule: ~/.cursor/rules/hyv.mdc (alwaysApply \u2014 auto via postinstall)\n"));
|
|
15770
|
+
console.log(import_chalk31.default.bold("Claude Code"));
|
|
15771
|
+
console.log(import_chalk31.default.dim(" Command: ~/.claude/commands/hyv.md"));
|
|
15772
|
+
console.log(import_chalk31.default.dim(" Skill: ~/.claude/skills/hold-your-voice/SKILL.md\n"));
|
|
15773
|
+
console.log(import_chalk31.default.bold("Windsurf"));
|
|
15774
|
+
console.log(import_chalk31.default.dim(" Rule: ~/.windsurf/rules/hyv.md (trigger: always_on)\n"));
|
|
15775
|
+
console.log(import_chalk31.default.bold("Codex"));
|
|
15776
|
+
console.log(import_chalk31.default.dim(" Instructions: ~/.codex/AGENTS.md (merged on install)\n"));
|
|
15777
|
+
console.log(import_chalk31.default.bold("Command Code"));
|
|
15778
|
+
console.log(import_chalk31.default.dim(" Skill: ~/.commandcode/skills/hyv/SKILL.md\n"));
|
|
15779
|
+
console.log(import_chalk31.default.bold("ChatGPT"));
|
|
15780
|
+
console.log(import_chalk31.default.dim(" hyv mcp --setup-chatgpt\n"));
|
|
15781
|
+
console.log(import_chalk31.default.bold("Auto-configure"));
|
|
15782
|
+
console.log(import_chalk31.default.dim(" hyv doctor --fix-agents"));
|
|
15783
|
+
console.log(import_chalk31.default.dim(" HYV_AUTO_CONFIGURE_AGENTS=0 npm i -g @holdyourvoice/hyv (skip)\n"));
|
|
15784
|
+
console.log(import_chalk31.default.bold("Verify"));
|
|
15785
|
+
console.log(import_chalk31.default.dim(" hyv mcp --test"));
|
|
15786
|
+
console.log(import_chalk31.default.dim(" HYV_TELEMETRY=1 hyv mcp (optional usage logging to ~/.hyv/telemetry/)\n"));
|
|
15143
15787
|
}
|
|
15144
15788
|
async function runMcpSelfTest() {
|
|
15145
|
-
console.log(
|
|
15146
|
-
console.log(
|
|
15789
|
+
console.log(import_chalk31.default.bold("\nhold your voice \u2014 mcp self-test\n"));
|
|
15790
|
+
console.log(import_chalk31.default.dim(` ${getEngineLabel()}
|
|
15147
15791
|
`));
|
|
15148
15792
|
let passed = 0;
|
|
15149
15793
|
let failed = 0;
|
|
15150
15794
|
const tools = getMcpToolNames();
|
|
15151
15795
|
if (tools.length >= 10) {
|
|
15152
|
-
console.log(
|
|
15796
|
+
console.log(import_chalk31.default.green(` \u2713 ${tools.length} MCP tools registered`));
|
|
15153
15797
|
passed++;
|
|
15154
15798
|
} else {
|
|
15155
|
-
console.log(
|
|
15799
|
+
console.log(import_chalk31.default.red(` \u2717 expected 10+ tools, got ${tools.length}`));
|
|
15156
15800
|
failed++;
|
|
15157
15801
|
}
|
|
15158
15802
|
const required = ["hyv_welcome", "hyv_scan", "hyv_analyze", "hyv_clean", "hyv_validate", "hyv_list_free_tools"];
|
|
15159
15803
|
for (const name of required) {
|
|
15160
15804
|
if (tools.includes(name)) {
|
|
15161
|
-
console.log(
|
|
15805
|
+
console.log(import_chalk31.default.green(` \u2713 tool: ${name}`));
|
|
15162
15806
|
passed++;
|
|
15163
15807
|
} else {
|
|
15164
|
-
console.log(
|
|
15808
|
+
console.log(import_chalk31.default.red(` \u2717 missing tool: ${name}`));
|
|
15165
15809
|
failed++;
|
|
15166
15810
|
}
|
|
15167
15811
|
}
|
|
15168
15812
|
try {
|
|
15169
15813
|
const welcome = await invokeMcpTool("hyv_welcome", {});
|
|
15170
15814
|
if (welcome.includes("Hold Your Voice") || welcome.includes("hold your voice")) {
|
|
15171
|
-
console.log(
|
|
15815
|
+
console.log(import_chalk31.default.green(" \u2713 hyv_welcome responds"));
|
|
15172
15816
|
passed++;
|
|
15173
15817
|
} else {
|
|
15174
|
-
console.log(
|
|
15818
|
+
console.log(import_chalk31.default.red(" \u2717 hyv_welcome unexpected output"));
|
|
15175
15819
|
failed++;
|
|
15176
15820
|
}
|
|
15177
15821
|
} catch (e) {
|
|
15178
|
-
console.log(
|
|
15822
|
+
console.log(import_chalk31.default.red(` \u2717 hyv_welcome failed: ${e.message}`));
|
|
15179
15823
|
failed++;
|
|
15180
15824
|
}
|
|
15181
15825
|
try {
|
|
15182
15826
|
const demo = await invokeMcpTool("hyv_demo", {});
|
|
15183
15827
|
if (demo.includes("Score") || demo.includes("issues")) {
|
|
15184
|
-
console.log(
|
|
15828
|
+
console.log(import_chalk31.default.green(" \u2713 hyv_demo pipeline works"));
|
|
15185
15829
|
passed++;
|
|
15186
15830
|
} else {
|
|
15187
|
-
console.log(
|
|
15831
|
+
console.log(import_chalk31.default.red(" \u2717 hyv_demo unexpected output"));
|
|
15188
15832
|
failed++;
|
|
15189
15833
|
}
|
|
15190
15834
|
} catch (e) {
|
|
15191
|
-
console.log(
|
|
15835
|
+
console.log(import_chalk31.default.red(` \u2717 hyv_demo failed: ${e.message}`));
|
|
15192
15836
|
failed++;
|
|
15193
15837
|
}
|
|
15194
|
-
const voiceMd =
|
|
15195
|
-
if (
|
|
15196
|
-
console.log(
|
|
15838
|
+
const voiceMd = path23.join(HOME2, ".hyv", "voice.md");
|
|
15839
|
+
if (fs23.existsSync(voiceMd)) {
|
|
15840
|
+
console.log(import_chalk31.default.green(" \u2713 voice profile found"));
|
|
15197
15841
|
passed++;
|
|
15198
15842
|
} else {
|
|
15199
|
-
console.log(
|
|
15843
|
+
console.log(import_chalk31.default.dim(" - no voice.md (free local engine still works)"));
|
|
15200
15844
|
}
|
|
15201
15845
|
if (getDemoText().length > 100) {
|
|
15202
|
-
console.log(
|
|
15846
|
+
console.log(import_chalk31.default.green(" \u2713 demo content available"));
|
|
15203
15847
|
passed++;
|
|
15204
15848
|
} else {
|
|
15205
|
-
console.log(
|
|
15849
|
+
console.log(import_chalk31.default.red(" \u2717 demo content missing"));
|
|
15206
15850
|
failed++;
|
|
15207
15851
|
}
|
|
15208
15852
|
try {
|
|
15209
15853
|
const stdio = await testMcpStdioSubprocess();
|
|
15210
15854
|
if (stdio.ok && (stdio.toolCount || 0) >= 10) {
|
|
15211
|
-
console.log(
|
|
15855
|
+
console.log(import_chalk31.default.green(` \u2713 stdio MCP subprocess responds (${stdio.toolCount} tools)`));
|
|
15212
15856
|
passed++;
|
|
15213
15857
|
} else if (stdio.ok) {
|
|
15214
|
-
console.log(
|
|
15858
|
+
console.log(import_chalk31.default.yellow(` ! stdio MCP subprocess ok but only ${stdio.toolCount || 0} tools`));
|
|
15215
15859
|
failed++;
|
|
15216
15860
|
} else {
|
|
15217
|
-
console.log(
|
|
15861
|
+
console.log(import_chalk31.default.red(" \u2717 stdio MCP subprocess failed"));
|
|
15218
15862
|
failed++;
|
|
15219
15863
|
}
|
|
15220
15864
|
} catch (e) {
|
|
15221
|
-
console.log(
|
|
15865
|
+
console.log(import_chalk31.default.red(` \u2717 stdio MCP subprocess failed: ${e.message}`));
|
|
15222
15866
|
failed++;
|
|
15223
15867
|
}
|
|
15224
15868
|
console.log("");
|
|
15225
15869
|
if (failed === 0) {
|
|
15226
|
-
console.log(
|
|
15227
|
-
console.log(
|
|
15870
|
+
console.log(import_chalk31.default.green(`\u2713 all checks passed (${passed})`));
|
|
15871
|
+
console.log(import_chalk31.default.dim("\nStart server: hyv mcp\n"));
|
|
15228
15872
|
return true;
|
|
15229
15873
|
}
|
|
15230
|
-
console.log(
|
|
15874
|
+
console.log(import_chalk31.default.yellow(`! ${failed} check(s) failed, ${passed} passed`));
|
|
15231
15875
|
return false;
|
|
15232
15876
|
}
|
|
15233
15877
|
|
|
15234
15878
|
// src/commands/export.ts
|
|
15235
|
-
var
|
|
15879
|
+
var fs24 = __toESM(require("fs"));
|
|
15236
15880
|
init_api();
|
|
15237
15881
|
var FORMATS = {
|
|
15238
15882
|
claude: {
|
|
@@ -15319,7 +15963,7 @@ async function exportCommand(format, opts) {
|
|
|
15319
15963
|
const prompt = fmt.wrap(detail.profile, detail.body);
|
|
15320
15964
|
if (opts.output) {
|
|
15321
15965
|
const outFile = opts.output.replace("{name}", profile.slug || profile.name);
|
|
15322
|
-
|
|
15966
|
+
fs24.writeFileSync(outFile, prompt);
|
|
15323
15967
|
results.push({ profile: profile.name, file: outFile });
|
|
15324
15968
|
} else {
|
|
15325
15969
|
results.push({ profile: profile.name, prompt });
|
|
@@ -15356,13 +16000,13 @@ async function exportCommand(format, opts) {
|
|
|
15356
16000
|
// src/index.ts
|
|
15357
16001
|
init_access();
|
|
15358
16002
|
init_welcome();
|
|
15359
|
-
var
|
|
15360
|
-
var
|
|
16003
|
+
var fs25 = __toESM(require("fs"));
|
|
16004
|
+
var path24 = __toESM(require("path"));
|
|
15361
16005
|
var program2 = new Command();
|
|
15362
|
-
var pkgPath2 =
|
|
16006
|
+
var pkgPath2 = path24.resolve(__dirname, "..", "package.json");
|
|
15363
16007
|
var pkgVersion2 = (() => {
|
|
15364
16008
|
try {
|
|
15365
|
-
return JSON.parse(
|
|
16009
|
+
return JSON.parse(fs25.readFileSync(pkgPath2, "utf-8")).version;
|
|
15366
16010
|
} catch {
|
|
15367
16011
|
return "2.7.1";
|
|
15368
16012
|
}
|
|
@@ -15406,15 +16050,15 @@ program2.command("mcp").description("Start MCP server (for Claude Desktop and ot
|
|
|
15406
16050
|
return;
|
|
15407
16051
|
}
|
|
15408
16052
|
if (opts.setupChatgpt) {
|
|
15409
|
-
console.log(
|
|
16053
|
+
console.log(import_chalk32.default.bold("\nhold your voice \u2014 chatgpt setup\n"));
|
|
15410
16054
|
console.log("To connect HYV to ChatGPT:");
|
|
15411
|
-
console.log(
|
|
15412
|
-
console.log(
|
|
15413
|
-
console.log(
|
|
15414
|
-
console.log(
|
|
16055
|
+
console.log(import_chalk32.default.dim(" 1. Go to ") + import_chalk32.default.cyan("https://chatgpt.com/#settings/Connectors"));
|
|
16056
|
+
console.log(import_chalk32.default.dim(" 2. Add a new connector"));
|
|
16057
|
+
console.log(import_chalk32.default.dim(" 3. For local MCP, use: ") + import_chalk32.default.cyan("hyv mcp"));
|
|
16058
|
+
console.log(import_chalk32.default.dim(" 4. ChatGPT Desktop supports stdio MCP servers"));
|
|
15415
16059
|
console.log("");
|
|
15416
|
-
console.log(
|
|
15417
|
-
console.log(
|
|
16060
|
+
console.log(import_chalk32.default.dim("Note: The remote HTTP MCP endpoint is not yet available."));
|
|
16061
|
+
console.log(import_chalk32.default.dim("Use the local stdio MCP server with Claude Desktop or Claude Code instead."));
|
|
15418
16062
|
return;
|
|
15419
16063
|
}
|
|
15420
16064
|
startMcpServer();
|
|
@@ -15424,9 +16068,8 @@ program2.command("export").description("Export voice profile for LLMs").argument
|
|
|
15424
16068
|
});
|
|
15425
16069
|
var args = process.argv.slice(2);
|
|
15426
16070
|
if (args.length === 0) {
|
|
15427
|
-
|
|
15428
|
-
|
|
15429
|
-
if (firstRun)
|
|
16071
|
+
printWelcome({ condensed: true });
|
|
16072
|
+
if (!hasCompletedOnboarding())
|
|
15430
16073
|
markOnboardingComplete(pkgVersion2);
|
|
15431
16074
|
process.exit(0);
|
|
15432
16075
|
}
|