@holdyourvoice/hyv 2.8.10 → 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 +8 -0
- package/agents/claude-code.md +1 -1
- package/agents/cursor.md +1 -1
- package/agents/generic.md +2 -2
- package/dist/index.js +1681 -1060
- 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;
|
|
@@ -4703,6 +4737,10 @@ function ensureHyvDir() {
|
|
|
4703
4737
|
}
|
|
4704
4738
|
}
|
|
4705
4739
|
}
|
|
4740
|
+
function writeSecureFile(filePath, content) {
|
|
4741
|
+
ensureHyvDir();
|
|
4742
|
+
fs.writeFileSync(filePath, content, { mode: 384 });
|
|
4743
|
+
}
|
|
4706
4744
|
function appendSecureLine(filePath, line, dir) {
|
|
4707
4745
|
if (dir) {
|
|
4708
4746
|
if (!fs.existsSync(dir))
|
|
@@ -4741,6 +4779,11 @@ function writeAuth(auth) {
|
|
|
4741
4779
|
ensureHyvDir();
|
|
4742
4780
|
fs.writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), { mode: 384 });
|
|
4743
4781
|
}
|
|
4782
|
+
function clearAuth() {
|
|
4783
|
+
if (fs.existsSync(AUTH_FILE)) {
|
|
4784
|
+
fs.unlinkSync(AUTH_FILE);
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4744
4787
|
function readConfig() {
|
|
4745
4788
|
try {
|
|
4746
4789
|
if (!fs.existsSync(CONFIG_FILE))
|
|
@@ -4811,6 +4854,17 @@ function queueSignal(signal) {
|
|
|
4811
4854
|
const filePath = path.join(QUEUE_DIR, `${id}.json`);
|
|
4812
4855
|
fs.writeFileSync(filePath, JSON.stringify(signal, null, 2), { mode: 384 });
|
|
4813
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
|
+
}
|
|
4814
4868
|
function saveLastEditSession(session) {
|
|
4815
4869
|
ensureHyvDir();
|
|
4816
4870
|
const data = { ...session, saved_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -4856,11 +4910,11 @@ var init_config = __esm({
|
|
|
4856
4910
|
var require_is_docker = __commonJS({
|
|
4857
4911
|
"node_modules/is-docker/index.js"(exports2, module2) {
|
|
4858
4912
|
"use strict";
|
|
4859
|
-
var
|
|
4913
|
+
var fs26 = require("fs");
|
|
4860
4914
|
var isDocker;
|
|
4861
4915
|
function hasDockerEnv() {
|
|
4862
4916
|
try {
|
|
4863
|
-
|
|
4917
|
+
fs26.statSync("/.dockerenv");
|
|
4864
4918
|
return true;
|
|
4865
4919
|
} catch (_) {
|
|
4866
4920
|
return false;
|
|
@@ -4868,7 +4922,7 @@ var require_is_docker = __commonJS({
|
|
|
4868
4922
|
}
|
|
4869
4923
|
function hasDockerCGroup() {
|
|
4870
4924
|
try {
|
|
4871
|
-
return
|
|
4925
|
+
return fs26.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
|
|
4872
4926
|
} catch (_) {
|
|
4873
4927
|
return false;
|
|
4874
4928
|
}
|
|
@@ -4886,21 +4940,21 @@ var require_is_docker = __commonJS({
|
|
|
4886
4940
|
var require_is_wsl = __commonJS({
|
|
4887
4941
|
"node_modules/is-wsl/index.js"(exports2, module2) {
|
|
4888
4942
|
"use strict";
|
|
4889
|
-
var
|
|
4890
|
-
var
|
|
4943
|
+
var os10 = require("os");
|
|
4944
|
+
var fs26 = require("fs");
|
|
4891
4945
|
var isDocker = require_is_docker();
|
|
4892
4946
|
var isWsl = () => {
|
|
4893
4947
|
if (process.platform !== "linux") {
|
|
4894
4948
|
return false;
|
|
4895
4949
|
}
|
|
4896
|
-
if (
|
|
4950
|
+
if (os10.release().toLowerCase().includes("microsoft")) {
|
|
4897
4951
|
if (isDocker()) {
|
|
4898
4952
|
return false;
|
|
4899
4953
|
}
|
|
4900
4954
|
return true;
|
|
4901
4955
|
}
|
|
4902
4956
|
try {
|
|
4903
|
-
return
|
|
4957
|
+
return fs26.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
|
|
4904
4958
|
} catch (_) {
|
|
4905
4959
|
return false;
|
|
4906
4960
|
}
|
|
@@ -4939,17 +4993,17 @@ var require_define_lazy_prop = __commonJS({
|
|
|
4939
4993
|
// node_modules/open/index.js
|
|
4940
4994
|
var require_open = __commonJS({
|
|
4941
4995
|
"node_modules/open/index.js"(exports2, module2) {
|
|
4942
|
-
var
|
|
4996
|
+
var path25 = require("path");
|
|
4943
4997
|
var childProcess = require("child_process");
|
|
4944
|
-
var { promises:
|
|
4998
|
+
var { promises: fs26, constants: fsConstants } = require("fs");
|
|
4945
4999
|
var isWsl = require_is_wsl();
|
|
4946
5000
|
var isDocker = require_is_docker();
|
|
4947
5001
|
var defineLazyProperty = require_define_lazy_prop();
|
|
4948
|
-
var localXdgOpenPath =
|
|
5002
|
+
var localXdgOpenPath = path25.join(__dirname, "xdg-open");
|
|
4949
5003
|
var { platform, arch } = process;
|
|
4950
5004
|
var hasContainerEnv = () => {
|
|
4951
5005
|
try {
|
|
4952
|
-
|
|
5006
|
+
fs26.statSync("/run/.containerenv");
|
|
4953
5007
|
return true;
|
|
4954
5008
|
} catch {
|
|
4955
5009
|
return false;
|
|
@@ -4972,14 +5026,14 @@ var require_open = __commonJS({
|
|
|
4972
5026
|
const configFilePath = "/etc/wsl.conf";
|
|
4973
5027
|
let isConfigFileExists = false;
|
|
4974
5028
|
try {
|
|
4975
|
-
await
|
|
5029
|
+
await fs26.access(configFilePath, fsConstants.F_OK);
|
|
4976
5030
|
isConfigFileExists = true;
|
|
4977
5031
|
} catch {
|
|
4978
5032
|
}
|
|
4979
5033
|
if (!isConfigFileExists) {
|
|
4980
5034
|
return defaultMountPoint;
|
|
4981
5035
|
}
|
|
4982
|
-
const configContent = await
|
|
5036
|
+
const configContent = await fs26.readFile(configFilePath, { encoding: "utf8" });
|
|
4983
5037
|
const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
|
|
4984
5038
|
if (!configMountPoint) {
|
|
4985
5039
|
return defaultMountPoint;
|
|
@@ -5079,7 +5133,7 @@ var require_open = __commonJS({
|
|
|
5079
5133
|
const isBundled = !__dirname || __dirname === "/";
|
|
5080
5134
|
let exeLocalXdgOpen = false;
|
|
5081
5135
|
try {
|
|
5082
|
-
await
|
|
5136
|
+
await fs26.access(localXdgOpenPath, fsConstants.X_OK);
|
|
5083
5137
|
exeLocalXdgOpen = true;
|
|
5084
5138
|
} catch {
|
|
5085
5139
|
}
|
|
@@ -5102,14 +5156,14 @@ var require_open = __commonJS({
|
|
|
5102
5156
|
}
|
|
5103
5157
|
const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
|
|
5104
5158
|
if (options.wait) {
|
|
5105
|
-
return new Promise((
|
|
5159
|
+
return new Promise((resolve15, reject) => {
|
|
5106
5160
|
subprocess.once("error", reject);
|
|
5107
5161
|
subprocess.once("close", (exitCode) => {
|
|
5108
5162
|
if (!options.allowNonzeroExitCode && exitCode > 0) {
|
|
5109
5163
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
5110
5164
|
return;
|
|
5111
5165
|
}
|
|
5112
|
-
|
|
5166
|
+
resolve15(subprocess);
|
|
5113
5167
|
});
|
|
5114
5168
|
});
|
|
5115
5169
|
}
|
|
@@ -5240,16 +5294,16 @@ function compareSemver(a, b) {
|
|
|
5240
5294
|
return 0;
|
|
5241
5295
|
}
|
|
5242
5296
|
function fetchLatestNpmVersion(timeoutMs = 8e3) {
|
|
5243
|
-
return new Promise((
|
|
5297
|
+
return new Promise((resolve15) => {
|
|
5244
5298
|
(0, import_child_process.execFile)(
|
|
5245
5299
|
"npm",
|
|
5246
5300
|
["view", "@holdyourvoice/hyv", "version"],
|
|
5247
5301
|
{ timeout: timeoutMs, encoding: "utf-8" },
|
|
5248
5302
|
(err, stdout) => {
|
|
5249
5303
|
if (err || !stdout?.trim())
|
|
5250
|
-
|
|
5304
|
+
resolve15(null);
|
|
5251
5305
|
else
|
|
5252
|
-
|
|
5306
|
+
resolve15(stdout.trim());
|
|
5253
5307
|
}
|
|
5254
5308
|
);
|
|
5255
5309
|
});
|
|
@@ -5313,7 +5367,7 @@ function escapeHtml(value) {
|
|
|
5313
5367
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5314
5368
|
}
|
|
5315
5369
|
function request(url, options = {}) {
|
|
5316
|
-
return new Promise((
|
|
5370
|
+
return new Promise((resolve15, reject) => {
|
|
5317
5371
|
const urlObj = new URL(url);
|
|
5318
5372
|
const isHttps = urlObj.protocol === "https:";
|
|
5319
5373
|
const client = isHttps ? https : http;
|
|
@@ -5334,9 +5388,9 @@ function request(url, options = {}) {
|
|
|
5334
5388
|
res.on("data", (chunk) => data += chunk);
|
|
5335
5389
|
res.on("end", () => {
|
|
5336
5390
|
try {
|
|
5337
|
-
|
|
5391
|
+
resolve15({ status: res.statusCode || 0, data: JSON.parse(data) });
|
|
5338
5392
|
} catch {
|
|
5339
|
-
|
|
5393
|
+
resolve15({ status: res.statusCode || 0, data });
|
|
5340
5394
|
}
|
|
5341
5395
|
});
|
|
5342
5396
|
});
|
|
@@ -5394,9 +5448,9 @@ async function authenticateWithLicense(licenseKey) {
|
|
|
5394
5448
|
}
|
|
5395
5449
|
async function authenticateWithBrowser() {
|
|
5396
5450
|
const server = http.createServer();
|
|
5397
|
-
const port = await new Promise((
|
|
5451
|
+
const port = await new Promise((resolve15) => {
|
|
5398
5452
|
server.listen(0, "127.0.0.1", () => {
|
|
5399
|
-
|
|
5453
|
+
resolve15(server.address().port);
|
|
5400
5454
|
});
|
|
5401
5455
|
});
|
|
5402
5456
|
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
@@ -5415,7 +5469,7 @@ async function authenticateWithBrowser() {
|
|
|
5415
5469
|
}
|
|
5416
5470
|
console.log(import_chalk.default.cyan("\nOpening browser for authentication..."));
|
|
5417
5471
|
await (0, import_open.default)(assertSafeOAuthUrl(auth_url));
|
|
5418
|
-
const authData = await new Promise((
|
|
5472
|
+
const authData = await new Promise((resolve15, reject) => {
|
|
5419
5473
|
const timeout = setTimeout(() => {
|
|
5420
5474
|
server.close();
|
|
5421
5475
|
reject(new Error("Authentication timeout. Please try again."));
|
|
@@ -5463,7 +5517,7 @@ async function authenticateWithBrowser() {
|
|
|
5463
5517
|
`);
|
|
5464
5518
|
clearTimeout(timeout);
|
|
5465
5519
|
server.close();
|
|
5466
|
-
|
|
5520
|
+
resolve15(data);
|
|
5467
5521
|
} catch (error) {
|
|
5468
5522
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
5469
5523
|
res.end(`<h1>Authentication failed</h1><p>${escapeHtml(error.message)}</p>`);
|
|
@@ -5695,11 +5749,11 @@ __export(api_exports, {
|
|
|
5695
5749
|
apiPost: () => apiPost,
|
|
5696
5750
|
requireSubscription: () => requireSubscription
|
|
5697
5751
|
});
|
|
5698
|
-
async function request2(method,
|
|
5752
|
+
async function request2(method, path25, body) {
|
|
5699
5753
|
const token = await getValidToken();
|
|
5700
5754
|
if (!token)
|
|
5701
5755
|
throw new Error("you're not signed in yet. run: hyv init");
|
|
5702
|
-
const url = `${API_BASE}${
|
|
5756
|
+
const url = `${API_BASE}${path25}`;
|
|
5703
5757
|
const opts = {
|
|
5704
5758
|
method,
|
|
5705
5759
|
headers: {
|
|
@@ -5724,11 +5778,11 @@ async function request2(method, path24, body) {
|
|
|
5724
5778
|
}
|
|
5725
5779
|
return res.json();
|
|
5726
5780
|
}
|
|
5727
|
-
function apiGet(
|
|
5728
|
-
return request2("GET",
|
|
5781
|
+
function apiGet(path25) {
|
|
5782
|
+
return request2("GET", path25);
|
|
5729
5783
|
}
|
|
5730
|
-
function apiPost(
|
|
5731
|
-
return request2("POST",
|
|
5784
|
+
function apiPost(path25, body) {
|
|
5785
|
+
return request2("POST", path25, body);
|
|
5732
5786
|
}
|
|
5733
5787
|
async function requireSubscription() {
|
|
5734
5788
|
const { requirePaidFeature: requirePaidFeature2 } = await Promise.resolve().then(() => (init_access(), access_exports));
|
|
@@ -7300,34 +7354,940 @@ function runPipeline(text, profile, applyFixes = false) {
|
|
|
7300
7354
|
};
|
|
7301
7355
|
}
|
|
7302
7356
|
function readText(source) {
|
|
7303
|
-
const
|
|
7304
|
-
const
|
|
7357
|
+
const fs26 = require("fs");
|
|
7358
|
+
const path25 = require("path");
|
|
7305
7359
|
if (source === "-") {
|
|
7306
7360
|
if (process.stdin.isTTY) {
|
|
7307
7361
|
console.error("No input provided. Pipe content or specify a file.");
|
|
7308
7362
|
process.exit(1);
|
|
7309
7363
|
}
|
|
7310
|
-
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;
|
|
7311
8181
|
}
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
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;
|
|
7316
8207
|
}
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
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;
|
|
7321
8254
|
}
|
|
7322
|
-
return { text: fs25.readFileSync(resolved, "utf-8"), path: resolved };
|
|
7323
8255
|
}
|
|
7324
|
-
|
|
7325
|
-
|
|
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"() {
|
|
7326
8269
|
"use strict";
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
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");
|
|
7331
8291
|
}
|
|
7332
8292
|
});
|
|
7333
8293
|
|
|
@@ -7335,11 +8295,13 @@ var init_pipeline = __esm({
|
|
|
7335
8295
|
var welcome_exports = {};
|
|
7336
8296
|
__export(welcome_exports, {
|
|
7337
8297
|
DEMO_TEXT: () => DEMO_TEXT,
|
|
7338
|
-
|
|
8298
|
+
FLOW_STEPS: () => FLOW_STEPS,
|
|
8299
|
+
WELCOME_TAGLINE: () => WELCOME_TAGLINE,
|
|
7339
8300
|
buildWelcomeMessage: () => buildWelcomeMessage,
|
|
7340
8301
|
formatCompactDemoResult: () => formatCompactDemoResult,
|
|
7341
8302
|
formatDemoResult: () => formatDemoResult,
|
|
7342
8303
|
formatFreeToolsList: () => formatFreeToolsList,
|
|
8304
|
+
plainFlowOverview: () => plainFlowOverview,
|
|
7343
8305
|
printWelcome: () => printWelcome,
|
|
7344
8306
|
runWelcomeDemo: () => runWelcomeDemo
|
|
7345
8307
|
});
|
|
@@ -7351,9 +8313,7 @@ function runWelcomeDemo() {
|
|
|
7351
8313
|
issueCount: signals.length,
|
|
7352
8314
|
red: result.stats.red,
|
|
7353
8315
|
yellow: result.stats.yellow,
|
|
7354
|
-
topIssues: signals.slice(0, 6).map(
|
|
7355
|
-
(s) => `line ${s.line}: ${s.id} \u2014 ${s.suggestion}`
|
|
7356
|
-
)
|
|
8316
|
+
topIssues: signals.slice(0, 6).map((s) => `line ${s.line}: ${s.id} \u2014 ${s.suggestion}`)
|
|
7357
8317
|
};
|
|
7358
8318
|
}
|
|
7359
8319
|
function formatDemoResult(demo) {
|
|
@@ -7373,7 +8333,7 @@ function formatCompactDemoResult(demo) {
|
|
|
7373
8333
|
return m ? m[1] : i.split(" \u2014 ")[0];
|
|
7374
8334
|
}).join(", ");
|
|
7375
8335
|
const tail = tells ? ` \u2014 e.g. ${tells}` : "";
|
|
7376
|
-
return `
|
|
8336
|
+
return `sample scan: ${demo.score}/100, ${demo.issueCount} issues${tail}`;
|
|
7377
8337
|
}
|
|
7378
8338
|
function formatFreeToolsList() {
|
|
7379
8339
|
const cli = FREE_CLI_COMMANDS.map((c2) => ` \u2022 ${c2}`).join("\n");
|
|
@@ -7392,54 +8352,48 @@ function formatFreeToolsList() {
|
|
|
7392
8352
|
].join("\n");
|
|
7393
8353
|
}
|
|
7394
8354
|
function buildWelcomeMessage(opts = {}) {
|
|
7395
|
-
if (opts.
|
|
7396
|
-
return
|
|
7397
|
-
"hold your voice \u2014 make your AI agents sound exactly like you.",
|
|
7398
|
-
"",
|
|
7399
|
-
" hyv scan draft.md test a draft",
|
|
7400
|
-
" hyv init build your voice",
|
|
7401
|
-
" hyv mcp --setup connect cursor / claude",
|
|
7402
|
-
"",
|
|
7403
|
-
` profiles + learning \u2192 ${PRICING_URL}`
|
|
7404
|
-
].join("\n");
|
|
8355
|
+
if (opts.guide) {
|
|
8356
|
+
return buildWelcomeGuide();
|
|
7405
8357
|
}
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
"",
|
|
7409
|
-
"4 steps:",
|
|
7410
|
-
""
|
|
7411
|
-
];
|
|
7412
|
-
for (const step of ONBOARDING_STEPS) {
|
|
7413
|
-
lines.push(` ${step.n}. ${step.title}`);
|
|
7414
|
-
lines.push(` ${step.cmd}`);
|
|
7415
|
-
lines.push(` ${step.hint}`);
|
|
7416
|
-
lines.push("");
|
|
8358
|
+
if (opts.condensed) {
|
|
8359
|
+
return buildWelcomeHeader({ condensed: true });
|
|
7417
8360
|
}
|
|
8361
|
+
const lines = [buildWelcomeHeader(), ""];
|
|
7418
8362
|
if (!opts.skipDemo) {
|
|
7419
8363
|
const demo = runWelcomeDemo();
|
|
7420
8364
|
recordEvent("welcome_demo", { score: demo.score, issues: demo.issueCount });
|
|
7421
|
-
lines.push(" try it now
|
|
8365
|
+
lines.push(" try it now");
|
|
7422
8366
|
lines.push(` ${formatCompactDemoResult(demo)}`);
|
|
7423
8367
|
lines.push(" hyv scan draft.md");
|
|
7424
8368
|
lines.push("");
|
|
7425
8369
|
}
|
|
7426
|
-
lines.push("
|
|
7427
|
-
lines.push(
|
|
7428
|
-
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}`);
|
|
7429
8374
|
lines.push("");
|
|
7430
8375
|
return lines.join("\n");
|
|
7431
8376
|
}
|
|
7432
8377
|
function printWelcome(opts = {}) {
|
|
7433
|
-
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
|
+
}
|
|
7434
8383
|
console.log("\n" + buildWelcomeMessage(opts));
|
|
7435
8384
|
}
|
|
7436
|
-
|
|
8385
|
+
function plainFlowOverview() {
|
|
8386
|
+
return formatFlowOverview().replace(/\u001b\[[0-9;]*m/g, "");
|
|
8387
|
+
}
|
|
8388
|
+
var DEMO_TEXT;
|
|
7437
8389
|
var init_welcome = __esm({
|
|
7438
8390
|
"src/lib/welcome.ts"() {
|
|
7439
8391
|
"use strict";
|
|
7440
8392
|
init_pipeline();
|
|
7441
8393
|
init_free_paid();
|
|
7442
8394
|
init_telemetry();
|
|
8395
|
+
init_welcome_flow();
|
|
8396
|
+
init_welcome_flow();
|
|
7443
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.
|
|
7444
8398
|
|
|
7445
8399
|
Firstly, you need to harness the power of seamless integration. Moreover, this robust ecosystem will foster innovation and unlock new opportunities for growth.
|
|
@@ -7447,12 +8401,6 @@ Firstly, you need to harness the power of seamless integration. Moreover, this r
|
|
|
7447
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.
|
|
7448
8402
|
|
|
7449
8403
|
In conclusion, the key to success is to empower your team with actionable insights and foster a culture of continuous improvement.`;
|
|
7450
|
-
ONBOARDING_STEPS = [
|
|
7451
|
-
{ n: 1, title: "install", cmd: "npm i -g @holdyourvoice/hyv@latest", hint: "one command, works in every terminal and agent" },
|
|
7452
|
-
{ n: 2, title: "test a draft", cmd: "hyv scan draft.md", hint: "free local scan \u2014 flags ai tells offline" },
|
|
7453
|
-
{ n: 3, title: "build your voice", cmd: "hyv init", hint: "profile from your writing \u2014 unlocks rewrite + learning" },
|
|
7454
|
-
{ n: 4, title: "connect your agent", cmd: "hyv mcp --setup", hint: "cursor, claude code, codex \u2014 tools inside the chat" }
|
|
7455
|
-
];
|
|
7456
8404
|
}
|
|
7457
8405
|
});
|
|
7458
8406
|
|
|
@@ -8327,15 +9275,15 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
|
|
|
8327
9275
|
g.minimatch.escape = vi.escape;
|
|
8328
9276
|
g.minimatch.unescape = Ei.unescape;
|
|
8329
9277
|
});
|
|
8330
|
-
var
|
|
9278
|
+
var fs26 = R((Wt) => {
|
|
8331
9279
|
"use strict";
|
|
8332
9280
|
Object.defineProperty(Wt, "__esModule", { value: true });
|
|
8333
9281
|
Wt.LRUCache = void 0;
|
|
8334
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) => {
|
|
8335
9283
|
typeof ge.emitWarning == "function" ? ge.emitWarning(n, t, e, s) : console.error(`[${e}] ${t}: ${n}`);
|
|
8336
|
-
}, Lt = globalThis.AbortController,
|
|
9284
|
+
}, Lt = globalThis.AbortController, os10 = globalThis.AbortSignal;
|
|
8337
9285
|
if (typeof Lt > "u") {
|
|
8338
|
-
|
|
9286
|
+
os10 = class {
|
|
8339
9287
|
onabort;
|
|
8340
9288
|
_onabort = [];
|
|
8341
9289
|
reason;
|
|
@@ -8347,7 +9295,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
|
|
|
8347
9295
|
constructor() {
|
|
8348
9296
|
t();
|
|
8349
9297
|
}
|
|
8350
|
-
signal = new
|
|
9298
|
+
signal = new os10();
|
|
8351
9299
|
abort(e) {
|
|
8352
9300
|
if (!this.signal.aborted) {
|
|
8353
9301
|
this.signal.reason = e, this.signal.aborted = true;
|
|
@@ -9296,7 +10244,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s))
|
|
|
9296
10244
|
};
|
|
9297
10245
|
Object.defineProperty(_, "__esModule", { value: true });
|
|
9298
10246
|
_.PathScurry = _.Path = _.PathScurryDarwin = _.PathScurryPosix = _.PathScurryWin32 = _.PathScurryBase = _.PathPosix = _.PathWin32 = _.PathBase = _.ChildrenCache = _.ResolveCache = void 0;
|
|
9299
|
-
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) => {
|
|
9300
10248
|
let t = vs.get(n);
|
|
9301
10249
|
if (t)
|
|
9302
10250
|
return t;
|
|
@@ -10815,7 +11763,7 @@ var {
|
|
|
10815
11763
|
} = import_index.default;
|
|
10816
11764
|
|
|
10817
11765
|
// src/index.ts
|
|
10818
|
-
var
|
|
11766
|
+
var import_chalk32 = __toESM(require_source());
|
|
10819
11767
|
|
|
10820
11768
|
// src/commands/init.ts
|
|
10821
11769
|
var import_chalk4 = __toESM(require_source());
|
|
@@ -10964,7 +11912,7 @@ function registerInitCommand(program3) {
|
|
|
10964
11912
|
const mcpResults = configureMcpForDesktop();
|
|
10965
11913
|
const { printPaidUnlockReminder: printPaidUnlockReminder2 } = await Promise.resolve().then(() => (init_marketing_hints(), marketing_hints_exports));
|
|
10966
11914
|
console.log("\nNext steps:");
|
|
10967
|
-
console.log(import_chalk4.default.dim(" 1.
|
|
11915
|
+
console.log(import_chalk4.default.dim(" 1. Set up your voice: hyv welcome"));
|
|
10968
11916
|
console.log(import_chalk4.default.dim(" 2. Scan a draft: hyv scan draft.md"));
|
|
10969
11917
|
console.log(import_chalk4.default.dim(" 3. Rewrite with voice: hyv rewrite draft.md"));
|
|
10970
11918
|
printPaidUnlockReminder2();
|
|
@@ -11504,435 +12452,77 @@ function registerRewriteCommand(program3) {
|
|
|
11504
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) => {
|
|
11505
12453
|
try {
|
|
11506
12454
|
const profile = await loadProfileForCommand(options.profile, { allowServerFetch: true });
|
|
11507
|
-
if (options.profile && !profile) {
|
|
11508
|
-
await requirePaidFeature("premiumPrompts");
|
|
11509
|
-
}
|
|
11510
|
-
const { text: draftContent, path: draftPath } = readText(file);
|
|
11511
|
-
const result = runPipeline(draftContent, profile, false);
|
|
11512
|
-
const promptResult = buildRewritePrompt({
|
|
11513
|
-
draftPath,
|
|
11514
|
-
draftContent,
|
|
11515
|
-
profileName: options.profile,
|
|
11516
|
-
profile,
|
|
11517
|
-
format: options.format,
|
|
11518
|
-
constraints: options.constraints,
|
|
11519
|
-
skipScan: options.scan === false
|
|
11520
|
-
});
|
|
11521
|
-
console.log(import_chalk9.default.dim(`
|
|
11522
|
-
Draft: ${draftPath}`));
|
|
11523
|
-
console.log(import_chalk9.default.dim(`Profile: ${promptResult.profileUsed || "none (using generic rules)"}`));
|
|
11524
|
-
if (profile && hasRichProfile(profile)) {
|
|
11525
|
-
console.log(import_chalk9.default.dim(` never-list: ${profile.never_list?.length || 0} learned: ${profile.learned_patterns?.length || 0}`));
|
|
11526
|
-
}
|
|
11527
|
-
console.log(import_chalk9.default.dim(`Issues found: ${result.stats.totalSignals}`));
|
|
11528
|
-
console.log(import_chalk9.default.dim(`Score: ${result.score}/100`));
|
|
11529
|
-
if (options.dryRun && profile) {
|
|
11530
|
-
console.log(import_chalk9.default.bold("\n--- Profile injection preview ---\n"));
|
|
11531
|
-
console.log(import_chalk9.default.dim(profile.body?.slice(0, 500) + (profile.body?.length > 500 ? "..." : "")));
|
|
11532
|
-
console.log("");
|
|
11533
|
-
}
|
|
11534
|
-
if (result.stats.totalSignals === 0) {
|
|
11535
|
-
console.log(import_chalk9.default.green("\n\u2713 No issues found. Draft looks clean!"));
|
|
11536
|
-
return;
|
|
11537
|
-
}
|
|
11538
|
-
console.log(import_chalk9.default.bold("\nIssues:"));
|
|
11539
|
-
for (const signal of result.signalMap.signals.slice(0, 10)) {
|
|
11540
|
-
const sev = signal.severity === "red" ? import_chalk9.default.red("\u25CF") : import_chalk9.default.yellow("\u25CB");
|
|
11541
|
-
const fix = signal.autoFixable ? import_chalk9.default.dim(" [auto-fixable]") : "";
|
|
11542
|
-
console.log(` ${sev} line ${signal.line}: ${import_chalk9.default.dim(signal.id)} \u2014 ${signal.suggestion}${fix}`);
|
|
11543
|
-
}
|
|
11544
|
-
if (result.signalMap.signals.length > 10) {
|
|
11545
|
-
console.log(import_chalk9.default.dim(` ... and ${result.signalMap.signals.length - 10} more`));
|
|
11546
|
-
}
|
|
11547
|
-
if (result.stats.autoFixed > 0) {
|
|
11548
|
-
console.log(import_chalk9.default.green(`
|
|
11549
|
-
${result.stats.autoFixed} issues can be auto-fixed \u2014 run: hyv fix ${file}`));
|
|
11550
|
-
}
|
|
11551
|
-
if (options.output) {
|
|
11552
|
-
const outputPath = path10.resolve(options.output);
|
|
11553
|
-
fs9.writeFileSync(outputPath, promptResult.prompt);
|
|
11554
|
-
console.log(import_chalk9.default.green(`
|
|
11555
|
-
\u2713 Prompt written to ${outputPath}`));
|
|
11556
|
-
} else {
|
|
11557
|
-
console.log(import_chalk9.default.bold("\n--- Rewrite Prompt ---\n"));
|
|
11558
|
-
console.log(promptResult.prompt);
|
|
11559
|
-
console.log(import_chalk9.default.dim("\n--- End Prompt ---\n"));
|
|
11560
|
-
console.log(import_chalk9.default.dim("Copy this prompt and paste it into your LLM."));
|
|
11561
|
-
console.log(import_chalk9.default.dim("Or pipe directly: hyv rewrite draft.md | pbcopy"));
|
|
11562
|
-
}
|
|
11563
|
-
await maybeShowLimitedModeHint(!!profile);
|
|
11564
|
-
} catch (error) {
|
|
11565
|
-
console.error(import_chalk9.default.red(`Error: ${error.message}`));
|
|
11566
|
-
process.exit(1);
|
|
11567
|
-
}
|
|
11568
|
-
});
|
|
11569
|
-
}
|
|
11570
|
-
|
|
11571
|
-
// src/commands/learning.ts
|
|
11572
|
-
var import_chalk10 = __toESM(require_source());
|
|
11573
|
-
var fs10 = __toESM(require("fs"));
|
|
11574
|
-
var path11 = __toESM(require("path"));
|
|
11575
|
-
|
|
11576
|
-
// src/lib/patterns.ts
|
|
11577
|
-
var AI_OVERUSED2 = [
|
|
11578
|
-
{ id: "ai.delve", category: "ai-slop", severity: "red", regex: /\bdelve\b/gi, suggestion: "use a specific verb: dig, explore, look at" },
|
|
11579
|
-
{ id: "ai.leverage", category: "ai-slop", severity: "red", regex: /\bleverage\b/gi, suggestion: "use: use, apply, build on" },
|
|
11580
|
-
{ id: "ai.utilize", category: "ai-slop", severity: "red", regex: /\butilize\b/gi, suggestion: "use: use" },
|
|
11581
|
-
{ id: "ai.tapestry", category: "ai-slop", severity: "red", regex: /\btapestry\b/gi, suggestion: "be specific about what you mean" },
|
|
11582
|
-
{ id: "ai.holistic", category: "ai-slop", severity: "red", regex: /\bholistic\b/gi, suggestion: "describe the actual approach" },
|
|
11583
|
-
{ id: "ai.robust", category: "ai-slop", severity: "yellow", regex: /\brobust\b/gi, suggestion: "say what actually makes it strong" },
|
|
11584
|
-
{ id: "ai.pivotal", category: "ai-slop", severity: "yellow", regex: /\bpivotal\b/gi, suggestion: "say why it matters specifically" },
|
|
11585
|
-
{ id: "ai.foster", category: "ai-slop", severity: "yellow", regex: /\bfoster\b/gi, suggestion: "use: build, grow, encourage, support" },
|
|
11586
|
-
{ id: "ai.harness", category: "ai-slop", severity: "yellow", regex: /\bharness\b/gi, suggestion: "use: use, apply, work with" },
|
|
11587
|
-
{ id: "ai.illuminate", category: "ai-slop", severity: "yellow", regex: /\billuminate\b/gi, suggestion: "use: show, explain, highlight" },
|
|
11588
|
-
{ 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" },
|
|
11589
|
-
{ id: "ai.fast-paced", category: "ai-slop", severity: "red", regex: /\bfast[\s-]paced\b/gi, suggestion: "cut this \u2014 every industry says this" },
|
|
11590
|
-
{ id: "ai.game-changer", category: "ai-slop", severity: "red", regex: /\bgame[\s-]changer\b/gi, suggestion: "explain what actually changed" },
|
|
11591
|
-
{ id: "ai.paradigm", category: "ai-slop", severity: "red", regex: /\bparadigm\b/gi, suggestion: "describe the actual shift" },
|
|
11592
|
-
{ id: "ai.synergy", category: "ai-slop", severity: "red", regex: /\bsynergy\b/gi, suggestion: "describe what works together and why" },
|
|
11593
|
-
{ id: "ai.ecosystem", category: "ai-slop", severity: "yellow", regex: /\becosystem\b/gi, suggestion: "name the specific tools/partners/platforms" },
|
|
11594
|
-
{ id: "ai.seamless", category: "ai-slop", severity: "yellow", regex: /\bseamless\b/gi, suggestion: "describe how it actually works" },
|
|
11595
|
-
{ id: "ai.actionable", category: "ai-slop", severity: "yellow", regex: /\bactionable\b/gi, suggestion: "just give the action, don't label it" },
|
|
11596
|
-
{ id: "ai.granular", category: "ai-slop", severity: "yellow", regex: /\bgranular\b/gi, suggestion: "say: specific, detailed, or name the level" },
|
|
11597
|
-
{ id: "ai.impactful", category: "ai-slop", severity: "yellow", regex: /\bimpactful\b/gi, suggestion: "describe the actual impact" },
|
|
11598
|
-
{ id: "ai.landscape", category: "ai-slop", severity: "red", regex: /\blandscape\b/gi, suggestion: "name the specific market/field/area" },
|
|
11599
|
-
{ id: "ai.realm", category: "ai-slop", severity: "red", regex: /\brealm\b/gi, suggestion: "name the specific domain" },
|
|
11600
|
-
{ id: "ai.straightforward", category: "ai-slop", severity: "yellow", regex: /\bstraightforward\b/gi, suggestion: "just explain it directly" }
|
|
11601
|
-
];
|
|
11602
|
-
var FORMULAIC2 = [
|
|
11603
|
-
{ id: "formula.firstly", category: "ai-slop", severity: "yellow", regex: /\bfirstly\b/gi, suggestion: 'just start \u2014 "firstly" is filler' },
|
|
11604
|
-
{ id: "formula.secondly", category: "ai-slop", severity: "yellow", regex: /\bsecondly\b/gi, suggestion: 'just continue \u2014 "secondly" is filler' },
|
|
11605
|
-
{ id: "formula.lastly", category: "ai-slop", severity: "yellow", regex: /\blastly\b/gi, suggestion: 'just end \u2014 "lastly" is filler' },
|
|
11606
|
-
{ id: "formula.moreover", category: "ai-slop", severity: "yellow", regex: /\bmoreover\b/gi, suggestion: "just add the point" },
|
|
11607
|
-
{ id: "formula.furthermore", category: "ai-slop", severity: "yellow", regex: /\bfurthermore\b/gi, suggestion: "just add the point" },
|
|
11608
|
-
{ id: "formula.in-conclusion", category: "ai-slop", severity: "red", regex: /\bin conclusion\b/gi, suggestion: "just end. readers know it's the end." },
|
|
11609
|
-
{ id: "formula.in-summary", category: "ai-slop", severity: "red", regex: /\bin summary\b/gi, suggestion: "just summarize. the label is redundant." },
|
|
11610
|
-
{ 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." },
|
|
11611
|
-
{ 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" },
|
|
11612
|
-
{ 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" },
|
|
11613
|
-
{ id: "formula.it-goes-without", category: "ai-slop", severity: "yellow", regex: /\bit goes without saying\b/gi, suggestion: "then don't say it" },
|
|
11614
|
-
{ id: "formula.in-today", category: "ai-slop", severity: "red", regex: /\bin today'?s\b/gi, suggestion: "start with your actual point instead" },
|
|
11615
|
-
{ id: "formula.lets-dive", category: "ai-slop", severity: "red", regex: /\blet'?s (?:dive|jump|dig|delve)\b/gi, suggestion: "just start. no diving needed." },
|
|
11616
|
-
{ id: "formula.without-further", category: "ai-slop", severity: "red", regex: /\bwithout further ado\b/gi, suggestion: "cut this \u2014 just get to it" },
|
|
11617
|
-
{ id: "formula.its-worth-noting", category: "ai-slop", severity: "yellow", regex: /\bit'?s worth noting\b/gi, suggestion: "just note it directly" },
|
|
11618
|
-
{ id: "formula.moving-forward", category: "ai-slop", severity: "yellow", regex: /\bmoving forward\b/gi, suggestion: "just say what happens next" },
|
|
11619
|
-
{ 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" },
|
|
11620
|
-
{ id: "formula.what-makes-interesting", category: "ai-slop", severity: "yellow", regex: /\bwhat makes this particularly interesting\b/gi, suggestion: "just say the interesting thing" },
|
|
11621
|
-
{ id: "formula.implications", category: "ai-slop", severity: "yellow", regex: /\bthe implications here are\b/gi, suggestion: "state the implications directly" },
|
|
11622
|
-
{ id: "formula.in-other-words", category: "ai-slop", severity: "red", regex: /\bin other words\b/gi, suggestion: "say it once, well" }
|
|
11623
|
-
];
|
|
11624
|
-
var HEDGING2 = [
|
|
11625
|
-
{ id: "hedge.some-might", category: "voice-drift", severity: "yellow", regex: /\bsome might say\b/gi, suggestion: "commit to the claim or drop it" },
|
|
11626
|
-
{ id: "hedge.arguably", category: "voice-drift", severity: "yellow", regex: /\barguably\b/gi, suggestion: "commit. say it or don't." },
|
|
11627
|
-
{ id: "hedge.worth-noting", category: "voice-drift", severity: "yellow", regex: /\bit'?s worth noting\b/gi, suggestion: "just note it directly" },
|
|
11628
|
-
{ id: "hedge.to-some-extent", category: "voice-drift", severity: "yellow", regex: /\bto some extent\b/gi, suggestion: "be specific about the extent" },
|
|
11629
|
-
{ id: "hedge.perhaps", category: "voice-drift", severity: "yellow", regex: /\bperhaps\b/gi, suggestion: "commit or cut" },
|
|
11630
|
-
{ id: "hedge.it-seems", category: "voice-drift", severity: "yellow", regex: /\bit seems\b/gi, suggestion: "state it directly" }
|
|
11631
|
-
];
|
|
11632
|
-
var STRUCTURE2 = [
|
|
11633
|
-
{ 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" },
|
|
11634
|
-
{ 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" },
|
|
11635
|
-
{ id: "struct.in-order-to", category: "structure", severity: "yellow", regex: /\bin order to\b/gi, suggestion: 'just use "to"' },
|
|
11636
|
-
{ id: "struct.due-to-the-fact", category: "structure", severity: "yellow", regex: /\bdue to the fact that\b/gi, suggestion: 'use "because"' },
|
|
11637
|
-
{ id: "struct.for-the-purpose", category: "structure", severity: "yellow", regex: /\bfor the purpose of\b/gi, suggestion: 'use "to"' },
|
|
11638
|
-
{ id: "struct.which-is-another", category: "structure", severity: "red", regex: /\bwhich is another way of saying\b/gi, suggestion: "just say the thing directly" },
|
|
11639
|
-
{ 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" },
|
|
11640
|
-
{ 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" },
|
|
11641
|
-
{ 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" },
|
|
11642
|
-
{ id: "struct.lesson-setup", category: "structure", severity: "yellow", regex: /\bhere'?s what .{3,60} taught\b/gi, suggestion: "just share the lesson" },
|
|
11643
|
-
// THE BIG ONE — antithesis negation pattern (Voice DNA: FATAL)
|
|
11644
|
-
{ 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" },
|
|
11645
|
-
{ 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' },
|
|
11646
|
-
{ 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" }
|
|
11647
|
-
];
|
|
11648
|
-
var ENGAGEMENT_BAIT2 = [
|
|
11649
|
-
{ 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." },
|
|
11650
|
-
{ id: "bait.read-that-again", category: "engagement-bait", severity: "red", regex: /\bread that again\b/gi, suggestion: "if it needs repeating, repeat it yourself" },
|
|
11651
|
-
{ id: "bait.full-stop", category: "engagement-bait", severity: "red", regex: /\bfull stop\b/gi, suggestion: "the period already does this job" },
|
|
11652
|
-
{ id: "bait.this-changes-everything", category: "engagement-bait", severity: "red", regex: /\bthis changes everything\b/gi, suggestion: "prove it with specifics" },
|
|
11653
|
-
{ id: "bait.paying-attention", category: "engagement-bait", severity: "red", regex: /\bare you paying attention\b/gi, suggestion: "don't patronize the reader" },
|
|
11654
|
-
{ id: "bait.not-ready", category: "engagement-bait", severity: "red", regex: /\byou'?re not ready for this\b/gi, suggestion: "just deliver the content" }
|
|
11655
|
-
];
|
|
11656
|
-
var AI_CRINGE2 = [
|
|
11657
|
-
{ id: "cringe.supercharge", category: "ai-cringe", severity: "red", regex: /\bsupercharge\b/gi, suggestion: "describe what it actually does" },
|
|
11658
|
-
{ id: "cringe.unlock", category: "ai-cringe", severity: "yellow", regex: /\bunlock\b/gi, suggestion: "describe what they get access to" },
|
|
11659
|
-
{ id: "cringe.future-proof", category: "ai-cringe", severity: "red", regex: /\bfuture[\s-]proof\b/gi, suggestion: "explain what specifically makes it durable" },
|
|
11660
|
-
{ id: "cringe.10x", category: "ai-cringe", severity: "red", regex: /\b10x\b/gi, suggestion: "use the actual numbers" },
|
|
11661
|
-
{ id: "cringe.ai-revolution", category: "ai-cringe", severity: "red", regex: /\bthe ai revolution\b/gi, suggestion: "describe the specific change" },
|
|
11662
|
-
{ 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" },
|
|
11663
|
-
{ 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." }
|
|
11664
|
-
];
|
|
11665
|
-
var INSIDER_CLAIMS2 = [
|
|
11666
|
-
{ 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." },
|
|
11667
|
-
{ id: "insider.nobody-tells", category: "insider-claim", severity: "red", regex: /\bwhat nobody tells you\b/gi, suggestion: "just tell them." },
|
|
11668
|
-
{ id: "insider.most-people", category: "insider-claim", severity: "yellow", regex: /\bmost people don'?t realize\b/gi, suggestion: "just explain it. skip the setup." },
|
|
11669
|
-
{ 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" }
|
|
11670
|
-
];
|
|
11671
|
-
var FORMATTING = [
|
|
11672
|
-
{ id: "format.em-dash", category: "formatting", severity: "red", regex: /\u2014/g, suggestion: "NO em dashes. use commas, periods, colons, semicolons, or parentheses." }
|
|
11673
|
-
];
|
|
11674
|
-
var OGILVY2 = [
|
|
11675
|
-
// Jargon — words that hide lack of understanding
|
|
11676
|
-
{ id: "ogilvy.jargon-utilize", category: "ai-slop", severity: "red", regex: /\butilize[sd]?\b/gi, suggestion: 'Ogilvy: use "use" instead' },
|
|
11677
|
-
{ id: "ogilvy.jargon-leverage", category: "ai-slop", severity: "red", regex: /\bleverage[ds]?\b/gi, suggestion: "Ogilvy: say what you actually mean" },
|
|
11678
|
-
{ id: "ogilvy.jargon-synergy", category: "ai-slop", severity: "red", regex: /\bsynerg(?:y|ies|istic)\b/gi, suggestion: "Ogilvy: describe what works together and why" },
|
|
11679
|
-
{ id: "ogilvy.jargon-bandwidth", category: "ai-slop", severity: "yellow", regex: /\bbandwidth\b/gi, suggestion: 'Ogilvy: say "time" or "capacity"' },
|
|
11680
|
-
{ id: "ogilvy.jargon-circle-back", category: "ai-slop", severity: "red", regex: /\bcircle back\b/gi, suggestion: 'Ogilvy: say "follow up" or "talk later"' },
|
|
11681
|
-
{ id: "ogilvy.jargon-low-hanging", category: "ai-slop", severity: "red", regex: /\blow-hanging fruit\b/gi, suggestion: "Ogilvy: name the specific easy win" },
|
|
11682
|
-
{ id: "ogilvy.jargon-move-the-needle", category: "ai-slop", severity: "red", regex: /\bmove the needle\b/gi, suggestion: "Ogilvy: describe the actual impact" },
|
|
11683
|
-
{ id: "ogilvy.jargon-touch-base", category: "ai-slop", severity: "red", regex: /\btouch base\b/gi, suggestion: 'Ogilvy: say "talk" or "meet"' },
|
|
11684
|
-
{ id: "ogilvy.jargon-take-it-offline", category: "ai-slop", severity: "red", regex: /\btake it offline\b/gi, suggestion: 'Ogilvy: say "discuss later"' },
|
|
11685
|
-
{ id: "ogilvy.jargon-deep-dive", category: "ai-slop", severity: "yellow", regex: /\bdeep dive\b/gi, suggestion: 'Ogilvy: say "look closely at" or "examine"' },
|
|
11686
|
-
// Throat-clearing openers
|
|
11687
|
-
{ 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." },
|
|
11688
|
-
{ 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." }
|
|
11689
|
-
];
|
|
11690
|
-
var ALL_PATTERNS = [
|
|
11691
|
-
...AI_OVERUSED2,
|
|
11692
|
-
...FORMULAIC2,
|
|
11693
|
-
...HEDGING2,
|
|
11694
|
-
...STRUCTURE2,
|
|
11695
|
-
...ENGAGEMENT_BAIT2,
|
|
11696
|
-
...AI_CRINGE2,
|
|
11697
|
-
...INSIDER_CLAIMS2,
|
|
11698
|
-
...FORMATTING,
|
|
11699
|
-
...OGILVY2
|
|
11700
|
-
];
|
|
11701
|
-
function scanLine(line, lineNum, filePath) {
|
|
11702
|
-
const findings = [];
|
|
11703
|
-
for (const pat of ALL_PATTERNS) {
|
|
11704
|
-
pat.regex.lastIndex = 0;
|
|
11705
|
-
let match;
|
|
11706
|
-
while ((match = pat.regex.exec(line)) !== null) {
|
|
11707
|
-
findings.push({
|
|
11708
|
-
file: filePath,
|
|
11709
|
-
line: lineNum,
|
|
11710
|
-
column: match.index + 1,
|
|
11711
|
-
pattern: pat.id,
|
|
11712
|
-
category: pat.category,
|
|
11713
|
-
severity: pat.severity,
|
|
11714
|
-
excerpt: highlightMatch2(line.trim(), match.index, match[0].length),
|
|
11715
|
-
suggestion: pat.suggestion
|
|
11716
|
-
});
|
|
11717
|
-
}
|
|
11718
|
-
}
|
|
11719
|
-
return findings;
|
|
11720
|
-
}
|
|
11721
|
-
function highlightMatch2(line, start, len) {
|
|
11722
|
-
const before = line.slice(0, start);
|
|
11723
|
-
const match = line.slice(start, start + len);
|
|
11724
|
-
const after = line.slice(start + len);
|
|
11725
|
-
return `${before}\xAB${match}\xBB${after}`.slice(0, 120);
|
|
11726
|
-
}
|
|
11727
|
-
|
|
11728
|
-
// src/lib/scan.ts
|
|
11729
|
-
var AI_PATTERN_RULES = [
|
|
11730
|
-
{
|
|
11731
|
-
id: "ai_antithesis",
|
|
11732
|
-
pattern: /\b(?:it'?s|isn'?t|is\s+not)\s+not\s+just\b.{0,80}\b(?:it'?s|but)\b/i
|
|
11733
|
-
},
|
|
11734
|
-
{
|
|
11735
|
-
id: "not_just_but",
|
|
11736
|
-
pattern: /\bnot\s+just\b.{3,80}\bbut\s+(?:also\s+)?/i
|
|
11737
|
-
},
|
|
11738
|
-
{
|
|
11739
|
-
id: "more_than_just",
|
|
11740
|
-
pattern: /\bmore\s+than\s+just\b/i
|
|
11741
|
-
},
|
|
11742
|
-
{
|
|
11743
|
-
id: "rhetorical_truth_setup",
|
|
11744
|
-
pattern: /\b(?:the\s+)?(?:uncomfortable|hard|harsh|brutal|ugly|unsexy|real|honest)\s+(?:truth|reality)\b/i
|
|
11745
|
-
},
|
|
11746
|
-
{
|
|
11747
|
-
id: "truth_is",
|
|
11748
|
-
pattern: /\bthe\s+truth\s+is\b/i
|
|
11749
|
-
},
|
|
11750
|
-
{
|
|
11751
|
-
id: "lesson_setup",
|
|
11752
|
-
pattern: /\b(?:here'?s\s+)?what\s+.{3,80}\s+(?:taught|teaches)\s+(?:me|us|you|everyone)\b/i
|
|
11753
|
-
},
|
|
11754
|
-
{
|
|
11755
|
-
id: "negation_cascade",
|
|
11756
|
-
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
|
|
11757
|
-
},
|
|
11758
|
-
{
|
|
11759
|
-
id: "formulaic_connector",
|
|
11760
|
-
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
|
|
11761
|
-
},
|
|
11762
|
-
{
|
|
11763
|
-
id: "ai_words",
|
|
11764
|
-
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
|
|
11765
|
-
},
|
|
11766
|
-
{
|
|
11767
|
-
id: "em_dash",
|
|
11768
|
-
pattern: /\u2014/
|
|
11769
|
-
},
|
|
11770
|
-
{
|
|
11771
|
-
id: "signpost_claim",
|
|
11772
|
-
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
|
|
11773
|
-
},
|
|
11774
|
-
{
|
|
11775
|
-
id: "generic_buyer_psychology",
|
|
11776
|
-
pattern: /\bpeople\s+don'?t\s+just\s+buy\b|\bpeople\s+buy\s+the\s+feeling\b/i
|
|
11777
|
-
},
|
|
11778
|
-
{
|
|
11779
|
-
id: "founder_cadence_restatement",
|
|
11780
|
-
pattern: /\bwhich\s+is\s+another\s+way\s+of\s+saying\b|\bin\s+other\s+words\b/i
|
|
11781
|
-
},
|
|
11782
|
-
{
|
|
11783
|
-
id: "founder_cadence_moment_becomes",
|
|
11784
|
-
pattern: /\b(?:the\s+)?moment\b.{3,80}\bbecomes?\b|\bbecomes?\s+(?:dangerous|useful|interesting|real|obvious)\s+the\s+moment\b/i
|
|
11785
|
-
},
|
|
11786
|
-
{
|
|
11787
|
-
id: "founder_cadence_same_better",
|
|
11788
|
-
pattern: /\bsame\s+[^.!?\n]{1,35}[.!?]\s*(?:better|nicer|cleaner|calmer|safer)\s+[^.!?\n]{1,35}[.!?]?/i
|
|
11789
|
-
}
|
|
11790
|
-
];
|
|
11791
|
-
var ABSTRACT_STYLE_WORDS = /* @__PURE__ */ new Set([
|
|
11792
|
-
"alignment",
|
|
11793
|
-
"authenticity",
|
|
11794
|
-
"awareness",
|
|
11795
|
-
"clarity",
|
|
11796
|
-
"confidence",
|
|
11797
|
-
"consistency",
|
|
11798
|
-
"differentiation",
|
|
11799
|
-
"execution",
|
|
11800
|
-
"framework",
|
|
11801
|
-
"identity",
|
|
11802
|
-
"messaging",
|
|
11803
|
-
"narrative",
|
|
11804
|
-
"personality",
|
|
11805
|
-
"positioning",
|
|
11806
|
-
"preference",
|
|
11807
|
-
"presence",
|
|
11808
|
-
"recall",
|
|
11809
|
-
"relevance",
|
|
11810
|
-
"resonance",
|
|
11811
|
-
"signal",
|
|
11812
|
-
"strategy",
|
|
11813
|
-
"trust",
|
|
11814
|
-
"utility",
|
|
11815
|
-
"value"
|
|
11816
|
-
]);
|
|
11817
|
-
var GENERIC_OPENERS = /^(?:most|many)\s+(?:brands|teams|people|founders|companies)\b/i;
|
|
11818
|
-
var QUESTION_OPENER = /^(?:have you|do you|did you|what if|why do|how do)\b/i;
|
|
11819
|
-
var LESSON_OPENER = /^(?:the most important thing|the key to|success is|if you want to|what i learned)\b/i;
|
|
11820
|
-
function words(text) {
|
|
11821
|
-
return (text || "").match(/[a-zA-Z][a-zA-Z0-9']*/g) || [];
|
|
11822
|
-
}
|
|
11823
|
-
function sentences(text) {
|
|
11824
|
-
return (text || "").split(/(?<=[.!?])\s+|\n{2,}/).map((s) => s.trim()).filter((s) => words(s).length > 0);
|
|
11825
|
-
}
|
|
11826
|
-
function paragraphs(text) {
|
|
11827
|
-
return (text || "").split(/\n\s*\n/).map((p) => p.trim()).filter((p) => words(p).length >= 6);
|
|
11828
|
-
}
|
|
11829
|
-
function lineStyleHits(line) {
|
|
11830
|
-
const low = (line || "").trim().toLowerCase();
|
|
11831
|
-
if (!low)
|
|
11832
|
-
return [];
|
|
11833
|
-
const hits = [];
|
|
11834
|
-
const lineWords = low.match(/[a-z']+/g) || [];
|
|
11835
|
-
const abstractCount = lineWords.filter((w) => ABSTRACT_STYLE_WORDS.has(w)).length;
|
|
11836
|
-
if (abstractCount >= 3 && !/\b(?:for example|for instance|such as)\b|\d/i.test(low)) {
|
|
11837
|
-
hits.push({
|
|
11838
|
-
line: 0,
|
|
11839
|
-
// Will be set by caller
|
|
11840
|
-
rule: "abstract_noun_cluster",
|
|
11841
|
-
phrase: line.trim().slice(0, 160)
|
|
11842
|
-
});
|
|
11843
|
-
}
|
|
11844
|
-
if (GENERIC_OPENERS.test(low)) {
|
|
11845
|
-
hits.push({
|
|
11846
|
-
line: 0,
|
|
11847
|
-
rule: "generic_opening_generalization",
|
|
11848
|
-
phrase: line.trim().slice(0, 160)
|
|
11849
|
-
});
|
|
11850
|
-
}
|
|
11851
|
-
if (QUESTION_OPENER.test(low)) {
|
|
11852
|
-
hits.push({
|
|
11853
|
-
line: 0,
|
|
11854
|
-
rule: "voice_question_opener",
|
|
11855
|
-
phrase: "opens with a question instead of a concrete observation"
|
|
11856
|
-
});
|
|
11857
|
-
}
|
|
11858
|
-
if (LESSON_OPENER.test(low)) {
|
|
11859
|
-
hits.push({
|
|
11860
|
-
line: 0,
|
|
11861
|
-
rule: "voice_lesson_opener",
|
|
11862
|
-
phrase: "opens with a lesson or inspirational claim"
|
|
11863
|
-
});
|
|
11864
|
-
}
|
|
11865
|
-
return hits;
|
|
11866
|
-
}
|
|
11867
|
-
function scanText(text) {
|
|
11868
|
-
const hits = [];
|
|
11869
|
-
const safeText = text || "";
|
|
11870
|
-
for (const rule of AI_PATTERN_RULES) {
|
|
11871
|
-
const regex = new RegExp(rule.pattern.source, rule.pattern.flags.replace("g", "") + "g");
|
|
11872
|
-
let match;
|
|
11873
|
-
let lastIndex = 0;
|
|
11874
|
-
while ((match = regex.exec(safeText)) !== null) {
|
|
11875
|
-
const snippet = match[0].trim();
|
|
11876
|
-
if (!snippet) {
|
|
11877
|
-
if (match.index === regex.lastIndex)
|
|
11878
|
-
regex.lastIndex++;
|
|
11879
|
-
continue;
|
|
12455
|
+
if (options.profile && !profile) {
|
|
12456
|
+
await requirePaidFeature("premiumPrompts");
|
|
11880
12457
|
}
|
|
11881
|
-
const
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
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
|
|
11886
12468
|
});
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
for (let i = 0; i < lines.length; i++) {
|
|
11893
|
-
const lineNum = i + 1;
|
|
11894
|
-
const lineText = lines[i];
|
|
11895
|
-
const patternHits = scanLine(lineText, lineNum, "inline");
|
|
11896
|
-
for (const hit of patternHits) {
|
|
11897
|
-
hits.push({ line: hit.line, rule: hit.pattern, severity: hit.severity, phrase: hit.excerpt || "", text: lineText.trim().slice(0, 120) });
|
|
11898
|
-
}
|
|
11899
|
-
const lineHits = lineStyleHits(lineText);
|
|
11900
|
-
for (const hit of lineHits) {
|
|
11901
|
-
hit.line = lineNum;
|
|
11902
|
-
hit.text = lineText.trim().slice(0, 240);
|
|
11903
|
-
hits.push(hit);
|
|
11904
|
-
}
|
|
11905
|
-
}
|
|
11906
|
-
const sentenceHits = [];
|
|
11907
|
-
for (let i = 0; i < lines.length; i++) {
|
|
11908
|
-
const lineSentences = lines[i].split(/(?<=[.!?])\s+/);
|
|
11909
|
-
for (const sentence of lineSentences) {
|
|
11910
|
-
const wordCount = words(sentence).length;
|
|
11911
|
-
if (wordCount > 0) {
|
|
11912
|
-
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}`));
|
|
11913
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);
|
|
11914
12515
|
}
|
|
11915
|
-
}
|
|
11916
|
-
for (let i = 0; i < sentenceHits.length - 2; i++) {
|
|
11917
|
-
const window = sentenceHits.slice(i, i + 3);
|
|
11918
|
-
if (window.every((s) => s.wordCount <= 5)) {
|
|
11919
|
-
hits.push({
|
|
11920
|
-
line: window[0].line,
|
|
11921
|
-
rule: "voice_staccato_triplet",
|
|
11922
|
-
phrase: "three short sentences in a row reads like performance",
|
|
11923
|
-
text: window[0].text
|
|
11924
|
-
});
|
|
11925
|
-
break;
|
|
11926
|
-
}
|
|
11927
|
-
}
|
|
11928
|
-
return hits.sort((a, b) => {
|
|
11929
|
-
if (a.line !== b.line)
|
|
11930
|
-
return a.line - b.line;
|
|
11931
|
-
return a.rule.localeCompare(b.rule);
|
|
11932
12516
|
});
|
|
11933
12517
|
}
|
|
11934
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
|
+
|
|
11935
12524
|
// src/lib/diff.ts
|
|
12525
|
+
init_scan();
|
|
11936
12526
|
function linesChanged(origLine, accLine) {
|
|
11937
12527
|
return origLine.trim() !== accLine.trim();
|
|
11938
12528
|
}
|
|
@@ -12250,11 +12840,12 @@ function printProfileImpact(impact, data) {
|
|
|
12250
12840
|
}
|
|
12251
12841
|
|
|
12252
12842
|
// src/commands/onboarding.ts
|
|
12253
|
-
var
|
|
12254
|
-
var
|
|
12255
|
-
var
|
|
12843
|
+
var import_chalk12 = __toESM(require_source());
|
|
12844
|
+
var fs12 = __toESM(require("fs"));
|
|
12845
|
+
var path13 = __toESM(require("path"));
|
|
12256
12846
|
init_config();
|
|
12257
12847
|
init_auth();
|
|
12848
|
+
init_scan();
|
|
12258
12849
|
var UNIVERSAL_QUESTIONS = [
|
|
12259
12850
|
{ key: "tone", question: "How would you describe your tone? (e.g., casual, professional, witty, direct)" },
|
|
12260
12851
|
{ key: "audience", question: "Who is your audience? (e.g., founders, developers, marketers)" },
|
|
@@ -12281,7 +12872,7 @@ var INDUSTRY_QUESTIONS = {
|
|
|
12281
12872
|
{ key: "length", question: "Do you prefer short punchy content or longer pieces?" }
|
|
12282
12873
|
]
|
|
12283
12874
|
};
|
|
12284
|
-
function
|
|
12875
|
+
function extractStats2(samples) {
|
|
12285
12876
|
const combined = samples.map((s) => s.text).join("\n\n");
|
|
12286
12877
|
const allWords = words(combined);
|
|
12287
12878
|
const allSentences = sentences(combined);
|
|
@@ -12337,7 +12928,7 @@ function registerOnboardingCommands(program3) {
|
|
|
12337
12928
|
await flashcardOnboarding(name, token);
|
|
12338
12929
|
}
|
|
12339
12930
|
} catch (error) {
|
|
12340
|
-
console.error(
|
|
12931
|
+
console.error(import_chalk12.default.red(`
|
|
12341
12932
|
Error: ${error.message}`));
|
|
12342
12933
|
process.exit(1);
|
|
12343
12934
|
}
|
|
@@ -12347,16 +12938,16 @@ Error: ${error.message}`));
|
|
|
12347
12938
|
async function flashcardOnboarding(name, token) {
|
|
12348
12939
|
const { printWelcome: printWelcome2 } = await Promise.resolve().then(() => (init_welcome(), welcome_exports));
|
|
12349
12940
|
printWelcome2({ condensed: true, skipDemo: true });
|
|
12350
|
-
console.log(
|
|
12941
|
+
console.log(import_chalk12.default.bold(`Creating voice profile: ${name}
|
|
12351
12942
|
`));
|
|
12352
|
-
console.log(
|
|
12943
|
+
console.log(import_chalk12.default.dim("Answer these questions to define your voice.\n"));
|
|
12353
12944
|
const answers = {};
|
|
12354
12945
|
for (const q of UNIVERSAL_QUESTIONS) {
|
|
12355
12946
|
const answer = await askQuestion(q.question);
|
|
12356
12947
|
answers[q.key] = answer;
|
|
12357
12948
|
}
|
|
12358
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";
|
|
12359
|
-
console.log(
|
|
12950
|
+
console.log(import_chalk12.default.dim(`
|
|
12360
12951
|
Industry: ${industry}
|
|
12361
12952
|
`));
|
|
12362
12953
|
for (const q of INDUSTRY_QUESTIONS[industry]) {
|
|
@@ -12364,7 +12955,7 @@ Industry: ${industry}
|
|
|
12364
12955
|
answers[q.key] = answer;
|
|
12365
12956
|
}
|
|
12366
12957
|
if (token) {
|
|
12367
|
-
console.log(
|
|
12958
|
+
console.log(import_chalk12.default.cyan("\nGenerating profile on server..."));
|
|
12368
12959
|
const response = await authenticatedRequest(
|
|
12369
12960
|
cliApiUrl("/cli/profiles/new"),
|
|
12370
12961
|
{
|
|
@@ -12381,60 +12972,60 @@ Industry: ${industry}
|
|
|
12381
12972
|
const profileContent = data.content || generateLocalProfile(name, answers);
|
|
12382
12973
|
ensureHyvDir();
|
|
12383
12974
|
writeCachedProfile(name, profileContent);
|
|
12384
|
-
console.log(
|
|
12975
|
+
console.log(import_chalk12.default.green(`
|
|
12385
12976
|
\u2713 Profile created: ${name}`));
|
|
12386
|
-
console.log(
|
|
12977
|
+
console.log(import_chalk12.default.dim("Run `hyv profiles` to see your profiles."));
|
|
12387
12978
|
} else {
|
|
12388
|
-
console.log(
|
|
12979
|
+
console.log(import_chalk12.default.yellow("\nServer unavailable. Creating local profile..."));
|
|
12389
12980
|
const profileContent = generateLocalProfile(name, answers);
|
|
12390
12981
|
ensureHyvDir();
|
|
12391
12982
|
writeCachedProfile(name, profileContent);
|
|
12392
|
-
console.log(
|
|
12983
|
+
console.log(import_chalk12.default.green(`
|
|
12393
12984
|
\u2713 Local profile created: ${name}`));
|
|
12394
12985
|
}
|
|
12395
12986
|
} else {
|
|
12396
|
-
console.log(
|
|
12987
|
+
console.log(import_chalk12.default.yellow("\nNot authenticated. Creating local profile..."));
|
|
12397
12988
|
const profileContent = generateLocalProfile(name, answers);
|
|
12398
12989
|
ensureHyvDir();
|
|
12399
12990
|
writeCachedProfile(name, profileContent);
|
|
12400
|
-
console.log(
|
|
12991
|
+
console.log(import_chalk12.default.green(`
|
|
12401
12992
|
\u2713 Local profile created: ${name}`));
|
|
12402
12993
|
}
|
|
12403
12994
|
}
|
|
12404
12995
|
async function createFromSamples(name, sampleDir, token) {
|
|
12405
|
-
const dirPath =
|
|
12406
|
-
if (!
|
|
12996
|
+
const dirPath = path13.resolve(sampleDir);
|
|
12997
|
+
if (!fs12.existsSync(dirPath)) {
|
|
12407
12998
|
throw new Error(`Directory not found: ${dirPath}`);
|
|
12408
12999
|
}
|
|
12409
|
-
console.log(
|
|
13000
|
+
console.log(import_chalk12.default.bold(`
|
|
12410
13001
|
Creating voice profile: ${name}`));
|
|
12411
|
-
console.log(
|
|
13002
|
+
console.log(import_chalk12.default.dim(`Reading samples from: ${dirPath}
|
|
12412
13003
|
`));
|
|
12413
|
-
const files =
|
|
13004
|
+
const files = fs12.readdirSync(dirPath).filter((f) => f.endsWith(".md") || f.endsWith(".txt")).map((f) => path13.join(dirPath, f));
|
|
12414
13005
|
if (files.length === 0) {
|
|
12415
13006
|
throw new Error("No .md or .txt files found in directory");
|
|
12416
13007
|
}
|
|
12417
13008
|
const samples = [];
|
|
12418
13009
|
for (const file of files) {
|
|
12419
|
-
const text =
|
|
13010
|
+
const text = fs12.readFileSync(file, "utf-8");
|
|
12420
13011
|
if (text.trim().length > 0) {
|
|
12421
13012
|
samples.push({ path: file, text });
|
|
12422
|
-
console.log(
|
|
13013
|
+
console.log(import_chalk12.default.dim(` \u2022 ${path13.basename(file)} (${words(text).length} words)`));
|
|
12423
13014
|
}
|
|
12424
13015
|
}
|
|
12425
13016
|
if (samples.length === 0) {
|
|
12426
13017
|
throw new Error("No readable text found in files");
|
|
12427
13018
|
}
|
|
12428
|
-
const stats =
|
|
12429
|
-
console.log(
|
|
13019
|
+
const stats = extractStats2(samples);
|
|
13020
|
+
console.log(import_chalk12.default.dim(`
|
|
12430
13021
|
Stats extracted:`));
|
|
12431
|
-
console.log(
|
|
12432
|
-
console.log(
|
|
12433
|
-
console.log(
|
|
12434
|
-
console.log(
|
|
12435
|
-
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}`));
|
|
12436
13027
|
if (token) {
|
|
12437
|
-
console.log(
|
|
13028
|
+
console.log(import_chalk12.default.cyan("\nGenerating profile on server..."));
|
|
12438
13029
|
const response = await authenticatedRequest(
|
|
12439
13030
|
cliApiUrl("/cli/profiles/new"),
|
|
12440
13031
|
{
|
|
@@ -12450,25 +13041,25 @@ Stats extracted:`));
|
|
|
12450
13041
|
const data = response.data;
|
|
12451
13042
|
ensureHyvDir();
|
|
12452
13043
|
writeCachedProfile(name, data.content || generateLocalProfileFromStats(name, stats));
|
|
12453
|
-
console.log(
|
|
13044
|
+
console.log(import_chalk12.default.green(`
|
|
12454
13045
|
\u2713 Profile created: ${name}`));
|
|
12455
13046
|
} else {
|
|
12456
|
-
console.log(
|
|
13047
|
+
console.log(import_chalk12.default.yellow("\nServer unavailable. Creating local profile..."));
|
|
12457
13048
|
ensureHyvDir();
|
|
12458
13049
|
writeCachedProfile(name, generateLocalProfileFromStats(name, stats));
|
|
12459
|
-
console.log(
|
|
13050
|
+
console.log(import_chalk12.default.green(`
|
|
12460
13051
|
\u2713 Local profile created: ${name}`));
|
|
12461
13052
|
}
|
|
12462
13053
|
} else {
|
|
12463
|
-
console.log(
|
|
13054
|
+
console.log(import_chalk12.default.yellow("\nNot authenticated. Creating local profile..."));
|
|
12464
13055
|
ensureHyvDir();
|
|
12465
13056
|
writeCachedProfile(name, generateLocalProfileFromStats(name, stats));
|
|
12466
|
-
console.log(
|
|
13057
|
+
console.log(import_chalk12.default.green(`
|
|
12467
13058
|
\u2713 Local profile created: ${name}`));
|
|
12468
13059
|
}
|
|
12469
13060
|
}
|
|
12470
13061
|
async function generateExtractionPrompt(name) {
|
|
12471
|
-
console.log(
|
|
13062
|
+
console.log(import_chalk12.default.bold(`
|
|
12472
13063
|
Generating extraction prompt for: ${name}
|
|
12473
13064
|
`));
|
|
12474
13065
|
const prompt = `I need you to analyze writing samples and create a voice profile.
|
|
@@ -12490,33 +13081,33 @@ function registerImportCommand(program3) {
|
|
|
12490
13081
|
program3.command("import").description("Import a voice profile from file").argument("<name>", "Profile name").argument("<file>", "Profile markdown file").action(async (name, file) => {
|
|
12491
13082
|
try {
|
|
12492
13083
|
assertSafeProfileName(name);
|
|
12493
|
-
const filePath =
|
|
12494
|
-
if (!
|
|
12495
|
-
console.error(
|
|
13084
|
+
const filePath = path13.resolve(file);
|
|
13085
|
+
if (!fs12.existsSync(filePath)) {
|
|
13086
|
+
console.error(import_chalk12.default.red(`File not found: ${filePath}`));
|
|
12496
13087
|
process.exit(1);
|
|
12497
13088
|
}
|
|
12498
|
-
const content =
|
|
13089
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
12499
13090
|
ensureHyvDir();
|
|
12500
13091
|
writeCachedProfile(name, content);
|
|
12501
|
-
console.log(
|
|
13092
|
+
console.log(import_chalk12.default.green(`
|
|
12502
13093
|
\u2713 Profile imported: ${name}`));
|
|
12503
13094
|
} catch (error) {
|
|
12504
|
-
console.error(
|
|
13095
|
+
console.error(import_chalk12.default.red(`Error: ${error.message}`));
|
|
12505
13096
|
process.exit(1);
|
|
12506
13097
|
}
|
|
12507
13098
|
});
|
|
12508
13099
|
}
|
|
12509
13100
|
function askQuestion(question) {
|
|
12510
|
-
return new Promise((
|
|
12511
|
-
const
|
|
12512
|
-
const rl =
|
|
13101
|
+
return new Promise((resolve15) => {
|
|
13102
|
+
const readline3 = require("readline");
|
|
13103
|
+
const rl = readline3.createInterface({
|
|
12513
13104
|
input: process.stdin,
|
|
12514
13105
|
output: process.stdout
|
|
12515
13106
|
});
|
|
12516
|
-
rl.question(
|
|
13107
|
+
rl.question(import_chalk12.default.cyan(` ${question}
|
|
12517
13108
|
> `), (answer) => {
|
|
12518
13109
|
rl.close();
|
|
12519
|
-
|
|
13110
|
+
resolve15(answer.trim());
|
|
12520
13111
|
});
|
|
12521
13112
|
});
|
|
12522
13113
|
}
|
|
@@ -12580,7 +13171,7 @@ function generateLocalProfileFromStats(name, stats) {
|
|
|
12580
13171
|
}
|
|
12581
13172
|
|
|
12582
13173
|
// src/commands/plan.ts
|
|
12583
|
-
var
|
|
13174
|
+
var import_chalk13 = __toESM(require_source());
|
|
12584
13175
|
init_config();
|
|
12585
13176
|
init_auth();
|
|
12586
13177
|
var import_open2 = __toESM(require_open());
|
|
@@ -12595,10 +13186,10 @@ function registerPlanCommand(program3) {
|
|
|
12595
13186
|
}
|
|
12596
13187
|
const token = getToken();
|
|
12597
13188
|
if (!token) {
|
|
12598
|
-
console.log(
|
|
12599
|
-
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"));
|
|
12600
13191
|
printFreePaidMatrix({ compact: true });
|
|
12601
|
-
console.log(
|
|
13192
|
+
console.log(import_chalk13.default.dim(" Run `hyv init` for profiles + learning.\n"));
|
|
12602
13193
|
return;
|
|
12603
13194
|
}
|
|
12604
13195
|
if (options.upgrade) {
|
|
@@ -12611,20 +13202,20 @@ function registerPlanCommand(program3) {
|
|
|
12611
13202
|
await showPlan();
|
|
12612
13203
|
}
|
|
12613
13204
|
} catch (error) {
|
|
12614
|
-
console.error(
|
|
13205
|
+
console.error(import_chalk13.default.red(`
|
|
12615
13206
|
Error: ${error.message}`));
|
|
12616
13207
|
process.exit(1);
|
|
12617
13208
|
}
|
|
12618
13209
|
});
|
|
12619
13210
|
}
|
|
12620
13211
|
async function showPlan() {
|
|
12621
|
-
console.log(
|
|
13212
|
+
console.log(import_chalk13.default.bold("\nSubscription Plan\n"));
|
|
12622
13213
|
const response = await authenticatedRequest(
|
|
12623
13214
|
cliApiUrl("/cli/heartbeat"),
|
|
12624
13215
|
{ method: "GET" }
|
|
12625
13216
|
);
|
|
12626
13217
|
if (response.status !== 200) {
|
|
12627
|
-
console.log(
|
|
13218
|
+
console.log(import_chalk13.default.yellow("Could not fetch plan info."));
|
|
12628
13219
|
return;
|
|
12629
13220
|
}
|
|
12630
13221
|
const data = response.data;
|
|
@@ -12642,27 +13233,27 @@ async function showPlan() {
|
|
|
12642
13233
|
team: "$29/mo",
|
|
12643
13234
|
agency: "Custom"
|
|
12644
13235
|
};
|
|
12645
|
-
console.log(
|
|
12646
|
-
console.log(
|
|
12647
|
-
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");
|
|
12648
13239
|
if (license) {
|
|
12649
|
-
console.log(
|
|
13240
|
+
console.log(import_chalk13.default.dim("License:"), license.key_hint);
|
|
12650
13241
|
}
|
|
12651
13242
|
const access = await getAccessState();
|
|
12652
|
-
console.log(
|
|
12653
|
-
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"));
|
|
12654
13245
|
if (plan === "none" || !access.hasPaidPlan) {
|
|
12655
|
-
console.log(
|
|
12656
|
-
console.log(
|
|
12657
|
-
console.log(
|
|
12658
|
-
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"));
|
|
12659
13250
|
} else {
|
|
12660
13251
|
console.log("\nManage your subscription:");
|
|
12661
|
-
console.log(
|
|
13252
|
+
console.log(import_chalk13.default.dim(" hyv plan --manage"));
|
|
12662
13253
|
}
|
|
12663
13254
|
}
|
|
12664
13255
|
async function upgradePlan() {
|
|
12665
|
-
console.log(
|
|
13256
|
+
console.log(import_chalk13.default.cyan("\nOpening checkout...\n"));
|
|
12666
13257
|
const response = await authenticatedRequest(
|
|
12667
13258
|
cliApiUrl("/cli/subscribe"),
|
|
12668
13259
|
{
|
|
@@ -12674,20 +13265,20 @@ async function upgradePlan() {
|
|
|
12674
13265
|
const data = response.data;
|
|
12675
13266
|
const checkoutUrl = data.checkout_url;
|
|
12676
13267
|
if (checkoutUrl) {
|
|
12677
|
-
console.log(
|
|
13268
|
+
console.log(import_chalk13.default.dim("Opening browser..."));
|
|
12678
13269
|
await (0, import_open2.default)(assertSafeOpenUrl(checkoutUrl));
|
|
12679
|
-
console.log(
|
|
12680
|
-
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."));
|
|
12681
13272
|
} else {
|
|
12682
|
-
console.log(
|
|
13273
|
+
console.log(import_chalk13.default.yellow("No checkout URL received."));
|
|
12683
13274
|
}
|
|
12684
13275
|
} else {
|
|
12685
|
-
console.log(
|
|
12686
|
-
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."));
|
|
12687
13278
|
}
|
|
12688
13279
|
}
|
|
12689
13280
|
async function openBillingPortal() {
|
|
12690
|
-
console.log(
|
|
13281
|
+
console.log(import_chalk13.default.cyan("\nOpening billing portal...\n"));
|
|
12691
13282
|
const response = await authenticatedRequest(
|
|
12692
13283
|
cliApiUrl("/cli/subscribe/manage"),
|
|
12693
13284
|
{ method: "POST" }
|
|
@@ -12696,41 +13287,41 @@ async function openBillingPortal() {
|
|
|
12696
13287
|
const data = response.data;
|
|
12697
13288
|
const portalUrl = data.portal_url;
|
|
12698
13289
|
if (portalUrl) {
|
|
12699
|
-
console.log(
|
|
13290
|
+
console.log(import_chalk13.default.dim("Opening browser..."));
|
|
12700
13291
|
await (0, import_open2.default)(assertSafeOpenUrl(portalUrl));
|
|
12701
|
-
console.log(
|
|
13292
|
+
console.log(import_chalk13.default.green("\n\u2713 Billing portal opened"));
|
|
12702
13293
|
} else {
|
|
12703
|
-
console.log(
|
|
13294
|
+
console.log(import_chalk13.default.yellow("No portal URL received."));
|
|
12704
13295
|
}
|
|
12705
13296
|
} else {
|
|
12706
|
-
console.log(
|
|
12707
|
-
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."));
|
|
12708
13299
|
}
|
|
12709
13300
|
}
|
|
12710
13301
|
async function downgradePlan() {
|
|
12711
|
-
console.log(
|
|
13302
|
+
console.log(import_chalk13.default.yellow("\nDowngrade Plan\n"));
|
|
12712
13303
|
console.log("To downgrade your plan, please visit:");
|
|
12713
|
-
console.log(
|
|
13304
|
+
console.log(import_chalk13.default.dim(" https://holdyourvoice.com/dashboard"));
|
|
12714
13305
|
console.log("\nOr contact support at shashank@holdyourvoice.com");
|
|
12715
13306
|
}
|
|
12716
13307
|
|
|
12717
13308
|
// src/commands/scan.ts
|
|
12718
|
-
var
|
|
13309
|
+
var import_chalk16 = __toESM(require_source());
|
|
12719
13310
|
init_pipeline();
|
|
12720
13311
|
init_local_profile();
|
|
12721
13312
|
init_access();
|
|
12722
13313
|
|
|
12723
13314
|
// src/lib/output.ts
|
|
12724
|
-
var
|
|
13315
|
+
var import_chalk14 = __toESM(require_source());
|
|
12725
13316
|
var c = {
|
|
12726
|
-
dim: (s) =>
|
|
12727
|
-
bold: (s) =>
|
|
12728
|
-
accent: (s) =>
|
|
12729
|
-
green: (s) =>
|
|
12730
|
-
red: (s) =>
|
|
12731
|
-
yellow: (s) =>
|
|
12732
|
-
cyan: (s) =>
|
|
12733
|
-
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)
|
|
12734
13325
|
};
|
|
12735
13326
|
function logo() {
|
|
12736
13327
|
return `${c.bold("hold your ")}${c.accent("voice")}`;
|
|
@@ -12878,12 +13469,12 @@ async function runHybridAnalysis(text, profile, opts = {}) {
|
|
|
12878
13469
|
}
|
|
12879
13470
|
|
|
12880
13471
|
// src/commands/history.ts
|
|
12881
|
-
var
|
|
12882
|
-
var
|
|
12883
|
-
var
|
|
13472
|
+
var import_chalk15 = __toESM(require_source());
|
|
13473
|
+
var fs13 = __toESM(require("fs"));
|
|
13474
|
+
var path14 = __toESM(require("path"));
|
|
12884
13475
|
init_config();
|
|
12885
|
-
var HISTORY_DIR =
|
|
12886
|
-
var HISTORY_FILE =
|
|
13476
|
+
var HISTORY_DIR = path14.join(HYV_DIR, "history");
|
|
13477
|
+
var HISTORY_FILE = path14.join(HISTORY_DIR, "scans.jsonl");
|
|
12887
13478
|
function logScan(entry) {
|
|
12888
13479
|
try {
|
|
12889
13480
|
ensureHyvDir();
|
|
@@ -12893,9 +13484,9 @@ function logScan(entry) {
|
|
|
12893
13484
|
}
|
|
12894
13485
|
function readHistory() {
|
|
12895
13486
|
try {
|
|
12896
|
-
if (!
|
|
13487
|
+
if (!fs13.existsSync(HISTORY_FILE))
|
|
12897
13488
|
return [];
|
|
12898
|
-
const lines =
|
|
13489
|
+
const lines = fs13.readFileSync(HISTORY_FILE, "utf-8").trim().split("\n").filter(Boolean);
|
|
12899
13490
|
return lines.map((l) => JSON.parse(l));
|
|
12900
13491
|
} catch {
|
|
12901
13492
|
return [];
|
|
@@ -12927,10 +13518,10 @@ function registerHistoryCommand(program3) {
|
|
|
12927
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) => {
|
|
12928
13519
|
try {
|
|
12929
13520
|
if (options.clear) {
|
|
12930
|
-
if (
|
|
12931
|
-
|
|
13521
|
+
if (fs13.existsSync(HISTORY_FILE)) {
|
|
13522
|
+
fs13.unlinkSync(HISTORY_FILE);
|
|
12932
13523
|
}
|
|
12933
|
-
console.log(
|
|
13524
|
+
console.log(import_chalk15.default.green("\n\u2713 History cleared"));
|
|
12934
13525
|
return;
|
|
12935
13526
|
}
|
|
12936
13527
|
let entries = readHistory();
|
|
@@ -12944,14 +13535,14 @@ function registerHistoryCommand(program3) {
|
|
|
12944
13535
|
const limit = parseInt(options.limit, 10);
|
|
12945
13536
|
entries = entries.slice(-limit);
|
|
12946
13537
|
if (entries.length === 0) {
|
|
12947
|
-
console.log(
|
|
13538
|
+
console.log(import_chalk15.default.dim("\nNo scan history yet. Run hyv scan or hyv score to start tracking."));
|
|
12948
13539
|
return;
|
|
12949
13540
|
}
|
|
12950
13541
|
if (options.format === "json") {
|
|
12951
13542
|
console.log(JSON.stringify(entries, null, 2));
|
|
12952
13543
|
return;
|
|
12953
13544
|
}
|
|
12954
|
-
console.log(
|
|
13545
|
+
console.log(import_chalk15.default.bold(`
|
|
12955
13546
|
score history (last ${entries.length} scans)
|
|
12956
13547
|
`));
|
|
12957
13548
|
if (options.chart) {
|
|
@@ -12963,7 +13554,7 @@ function registerHistoryCommand(program3) {
|
|
|
12963
13554
|
if (min !== max) {
|
|
12964
13555
|
console.log(` ${min} \u2524`);
|
|
12965
13556
|
}
|
|
12966
|
-
console.log(
|
|
13557
|
+
console.log(import_chalk15.default.dim(` \u2514${"\u2500".repeat(sl.length + 1)}`));
|
|
12967
13558
|
console.log("");
|
|
12968
13559
|
}
|
|
12969
13560
|
const scores = entries.map((e) => e.score);
|
|
@@ -12976,22 +13567,22 @@ function registerHistoryCommand(program3) {
|
|
|
12976
13567
|
const firstAvg = firstHalf.reduce((s, e) => s + e.score, 0) / (firstHalf.length || 1);
|
|
12977
13568
|
const secondAvg = secondHalf.reduce((s, e) => s + e.score, 0) / (secondHalf.length || 1);
|
|
12978
13569
|
const trend = Math.round(secondAvg - firstAvg);
|
|
12979
|
-
const trendStr = trend > 0 ?
|
|
12980
|
-
console.log(
|
|
12981
|
-
console.log(
|
|
12982
|
-
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"));
|
|
12983
13574
|
const recent = entries.slice(-10).reverse();
|
|
12984
13575
|
for (const entry of recent) {
|
|
12985
13576
|
const time = new Date(entry.timestamp).toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit" });
|
|
12986
|
-
const icon = entry.issues === 0 ?
|
|
12987
|
-
const score = entry.score < 60 ?
|
|
12988
|
-
const issues = entry.issues > 0 ?
|
|
12989
|
-
const file =
|
|
12990
|
-
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}`);
|
|
12991
13582
|
}
|
|
12992
13583
|
console.log("");
|
|
12993
13584
|
} catch (error) {
|
|
12994
|
-
console.error(
|
|
13585
|
+
console.error(import_chalk15.default.red(`Error: ${error.message}`));
|
|
12995
13586
|
process.exit(1);
|
|
12996
13587
|
}
|
|
12997
13588
|
});
|
|
@@ -13006,7 +13597,7 @@ function registerScanCommand(program3) {
|
|
|
13006
13597
|
const profile = await loadProfileForCommand(options.profile);
|
|
13007
13598
|
const { text, path: filePath } = readText(file);
|
|
13008
13599
|
if (!text.trim()) {
|
|
13009
|
-
console.warn(
|
|
13600
|
+
console.warn(import_chalk16.default.yellow("File is empty or contains only whitespace."));
|
|
13010
13601
|
process.exit(0);
|
|
13011
13602
|
}
|
|
13012
13603
|
const ignoreRules = options.ignore ? new Set(options.ignore.split(",").map((r) => r.trim())) : /* @__PURE__ */ new Set();
|
|
@@ -13049,26 +13640,26 @@ function registerScanCommand(program3) {
|
|
|
13049
13640
|
}, null, 2));
|
|
13050
13641
|
} else {
|
|
13051
13642
|
if (signals.length === 0) {
|
|
13052
|
-
console.log(
|
|
13643
|
+
console.log(import_chalk16.default.green(`
|
|
13053
13644
|
\u2713 No issues found in ${filePath}`));
|
|
13054
|
-
console.log(
|
|
13645
|
+
console.log(import_chalk16.default.dim(` score: ${score}/100`));
|
|
13055
13646
|
} else {
|
|
13056
|
-
console.log(
|
|
13647
|
+
console.log(import_chalk16.default.bold(`
|
|
13057
13648
|
hyv scan ${filePath}`));
|
|
13058
13649
|
if (profile) {
|
|
13059
|
-
console.log(
|
|
13650
|
+
console.log(import_chalk16.default.dim(` profile: ${profile.slug || profile.name}${hasRichProfile(profile) ? " (full)" : ""}`));
|
|
13060
13651
|
}
|
|
13061
13652
|
if (analysis.message) {
|
|
13062
|
-
console.log(
|
|
13653
|
+
console.log(import_chalk16.default.dim(` engine: ${analysis.message}`));
|
|
13063
13654
|
}
|
|
13064
13655
|
console.log("");
|
|
13065
13656
|
printGroupedSignals(signals, profile);
|
|
13066
|
-
console.log(
|
|
13657
|
+
console.log(import_chalk16.default.yellow(`
|
|
13067
13658
|
${signals.length} issues (${signals.filter((s) => s.severity === "red").length} red, ${signals.filter((s) => s.severity === "yellow").length} yellow)`));
|
|
13068
|
-
console.log(
|
|
13069
|
-
console.log(
|
|
13659
|
+
console.log(import_chalk16.default.dim(` score: ${score}/100`));
|
|
13660
|
+
console.log(import_chalk16.default.dim(`
|
|
13070
13661
|
fix: hyv fix ${file}`));
|
|
13071
|
-
console.log(
|
|
13662
|
+
console.log(import_chalk16.default.dim(` diff: hyv diff ${file}
|
|
13072
13663
|
`));
|
|
13073
13664
|
}
|
|
13074
13665
|
await maybeShowLimitedModeHint(!!profile);
|
|
@@ -13086,17 +13677,17 @@ hyv scan ${filePath}`));
|
|
|
13086
13677
|
process.exit(2);
|
|
13087
13678
|
}
|
|
13088
13679
|
} catch (error) {
|
|
13089
|
-
console.error(
|
|
13680
|
+
console.error(import_chalk16.default.red(`Error: ${error.message}`));
|
|
13090
13681
|
process.exit(1);
|
|
13091
13682
|
}
|
|
13092
13683
|
});
|
|
13093
13684
|
}
|
|
13094
13685
|
|
|
13095
13686
|
// src/commands/doctor.ts
|
|
13096
|
-
var
|
|
13097
|
-
var
|
|
13098
|
-
var
|
|
13099
|
-
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"));
|
|
13100
13691
|
init_config();
|
|
13101
13692
|
init_auth();
|
|
13102
13693
|
init_access();
|
|
@@ -13107,15 +13698,15 @@ init_version();
|
|
|
13107
13698
|
var import_child_process2 = require("child_process");
|
|
13108
13699
|
|
|
13109
13700
|
// src/lib/cli-entry.ts
|
|
13110
|
-
var
|
|
13111
|
-
var
|
|
13701
|
+
var fs14 = __toESM(require("fs"));
|
|
13702
|
+
var path15 = __toESM(require("path"));
|
|
13112
13703
|
function resolveCliEntry() {
|
|
13113
13704
|
const candidates = [
|
|
13114
|
-
|
|
13115
|
-
|
|
13116
|
-
|
|
13705
|
+
path15.resolve(process.argv[1] || ""),
|
|
13706
|
+
path15.resolve(__dirname, "index.js"),
|
|
13707
|
+
path15.resolve(__dirname, "..", "dist", "index.js")
|
|
13117
13708
|
];
|
|
13118
|
-
return candidates.find((p) => p &&
|
|
13709
|
+
return candidates.find((p) => p && fs14.existsSync(p)) || null;
|
|
13119
13710
|
}
|
|
13120
13711
|
function mcpServerCommand() {
|
|
13121
13712
|
const entry = resolveCliEntry();
|
|
@@ -13134,7 +13725,7 @@ async function testMcpStdioSubprocess() {
|
|
|
13134
13725
|
const entry = resolveCliEntry();
|
|
13135
13726
|
if (!entry)
|
|
13136
13727
|
return { ok: false };
|
|
13137
|
-
return new Promise((
|
|
13728
|
+
return new Promise((resolve15) => {
|
|
13138
13729
|
const child = (0, import_child_process2.spawn)(process.execPath, [entry, "mcp"], {
|
|
13139
13730
|
stdio: ["pipe", "pipe", "pipe"],
|
|
13140
13731
|
env: { ...process.env, HYV_POSTINSTALL_QUIET: "1" }
|
|
@@ -13150,7 +13741,7 @@ async function testMcpStdioSubprocess() {
|
|
|
13150
13741
|
child.kill();
|
|
13151
13742
|
} catch {
|
|
13152
13743
|
}
|
|
13153
|
-
|
|
13744
|
+
resolve15({ ok, toolCount });
|
|
13154
13745
|
};
|
|
13155
13746
|
const timeout = setTimeout(() => finish(false), 1e4);
|
|
13156
13747
|
const handleLine = (line) => {
|
|
@@ -13201,20 +13792,20 @@ async function testMcpStdioSubprocess() {
|
|
|
13201
13792
|
}
|
|
13202
13793
|
|
|
13203
13794
|
// src/commands/doctor.ts
|
|
13204
|
-
var HOME =
|
|
13795
|
+
var HOME = os6.homedir();
|
|
13205
13796
|
var IS_WIN = process.platform === "win32";
|
|
13206
13797
|
function claudeDesktopDir() {
|
|
13207
13798
|
if (IS_WIN)
|
|
13208
|
-
return
|
|
13799
|
+
return path16.join(HOME, "AppData", "Roaming", "Claude");
|
|
13209
13800
|
if (process.platform === "linux")
|
|
13210
|
-
return
|
|
13211
|
-
return
|
|
13801
|
+
return path16.join(HOME, ".config", "Claude");
|
|
13802
|
+
return path16.join(HOME, "Library", "Application Support", "Claude");
|
|
13212
13803
|
}
|
|
13213
13804
|
function isOwnerOnlyFile(filePath) {
|
|
13214
13805
|
try {
|
|
13215
|
-
if (!
|
|
13806
|
+
if (!fs15.existsSync(filePath))
|
|
13216
13807
|
return true;
|
|
13217
|
-
const mode =
|
|
13808
|
+
const mode = fs15.statSync(filePath).mode & 511;
|
|
13218
13809
|
return (mode & 63) === 0;
|
|
13219
13810
|
} catch {
|
|
13220
13811
|
return false;
|
|
@@ -13222,9 +13813,9 @@ function isOwnerOnlyFile(filePath) {
|
|
|
13222
13813
|
}
|
|
13223
13814
|
function readMcpHyv(configFile) {
|
|
13224
13815
|
try {
|
|
13225
|
-
if (!
|
|
13816
|
+
if (!fs15.existsSync(configFile))
|
|
13226
13817
|
return false;
|
|
13227
|
-
const cfg = JSON.parse(
|
|
13818
|
+
const cfg = JSON.parse(fs15.readFileSync(configFile, "utf-8"));
|
|
13228
13819
|
return Boolean(cfg.mcpServers?.hyv);
|
|
13229
13820
|
} catch {
|
|
13230
13821
|
return false;
|
|
@@ -13232,188 +13823,188 @@ function readMcpHyv(configFile) {
|
|
|
13232
13823
|
}
|
|
13233
13824
|
function registerDoctorCommand(program3) {
|
|
13234
13825
|
program3.command("doctor").description("Diagnose CLI health: engine, cache, auth, agents").option("--fix-agents", "Re-run agent config copy (idempotent)").action(async (opts) => {
|
|
13235
|
-
console.log(
|
|
13826
|
+
console.log(import_chalk17.default.bold("\nhold your voice \u2014 doctor\n"));
|
|
13236
13827
|
let issues = 0;
|
|
13237
13828
|
let fixed = 0;
|
|
13238
|
-
console.log(
|
|
13829
|
+
console.log(import_chalk17.default.dim(`engine: ${getEngineLabel()} (node ${process.version})`));
|
|
13239
13830
|
if (isLocalOnlyMode()) {
|
|
13240
|
-
console.log(
|
|
13831
|
+
console.log(import_chalk17.default.yellow(" local-only mode: HYV_LOCAL_ONLY is set"));
|
|
13241
13832
|
}
|
|
13242
13833
|
const access = await getAccessState();
|
|
13243
13834
|
const profile = await loadProfileForCommand();
|
|
13244
|
-
console.log(
|
|
13245
|
-
console.log(
|
|
13246
|
-
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..."));
|
|
13247
13838
|
const cliPath = process.argv[1];
|
|
13248
|
-
if (cliPath &&
|
|
13249
|
-
console.log(
|
|
13839
|
+
if (cliPath && fs15.existsSync(cliPath)) {
|
|
13840
|
+
console.log(import_chalk17.default.green(" \u2713 cli installed"));
|
|
13250
13841
|
} else {
|
|
13251
|
-
console.log(
|
|
13842
|
+
console.log(import_chalk17.default.red(" \u2717 cli not found"));
|
|
13252
13843
|
issues++;
|
|
13253
13844
|
}
|
|
13254
|
-
console.log(
|
|
13255
|
-
if (
|
|
13256
|
-
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"));
|
|
13257
13848
|
} else {
|
|
13258
|
-
console.log(
|
|
13259
|
-
|
|
13849
|
+
console.log(import_chalk17.default.yellow(" ! .hyv directory missing \u2014 creating..."));
|
|
13850
|
+
fs15.mkdirSync(HYV_DIR, { recursive: true, mode: 448 });
|
|
13260
13851
|
fixed++;
|
|
13261
13852
|
}
|
|
13262
|
-
console.log(
|
|
13853
|
+
console.log(import_chalk17.default.dim("checking cache..."));
|
|
13263
13854
|
const diskProfiles = listDiskCachedProfiles();
|
|
13264
|
-
const syncMeta =
|
|
13265
|
-
if (diskProfiles.length > 0 ||
|
|
13266
|
-
console.log(
|
|
13267
|
-
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)) {
|
|
13268
13859
|
try {
|
|
13269
|
-
const meta = JSON.parse(
|
|
13270
|
-
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"}`));
|
|
13271
13862
|
} catch {
|
|
13272
13863
|
}
|
|
13273
13864
|
}
|
|
13274
13865
|
} else {
|
|
13275
|
-
console.log(
|
|
13866
|
+
console.log(import_chalk17.default.dim(" - no full profile cache (free local engine still works)"));
|
|
13276
13867
|
}
|
|
13277
|
-
console.log(
|
|
13278
|
-
if (
|
|
13279
|
-
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;
|
|
13280
13871
|
if ((hyvMode & 63) === 0) {
|
|
13281
|
-
console.log(
|
|
13872
|
+
console.log(import_chalk17.default.green(" \u2713 .hyv directory permissions"));
|
|
13282
13873
|
} else {
|
|
13283
|
-
console.log(
|
|
13284
|
-
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"));
|
|
13285
13876
|
issues++;
|
|
13286
13877
|
}
|
|
13287
13878
|
}
|
|
13288
|
-
if (
|
|
13879
|
+
if (fs15.existsSync(AUTH_FILE)) {
|
|
13289
13880
|
if (isOwnerOnlyFile(AUTH_FILE)) {
|
|
13290
|
-
console.log(
|
|
13881
|
+
console.log(import_chalk17.default.green(" \u2713 auth.json permissions"));
|
|
13291
13882
|
} else {
|
|
13292
|
-
console.log(
|
|
13293
|
-
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"));
|
|
13294
13885
|
issues++;
|
|
13295
13886
|
}
|
|
13296
13887
|
}
|
|
13297
|
-
console.log(
|
|
13888
|
+
console.log(import_chalk17.default.dim("checking authentication..."));
|
|
13298
13889
|
if (isInitialized()) {
|
|
13299
13890
|
const auth = readAuth();
|
|
13300
13891
|
if (auth) {
|
|
13301
|
-
console.log(
|
|
13302
|
-
console.log(
|
|
13892
|
+
console.log(import_chalk17.default.green(" \u2713 authenticated"));
|
|
13893
|
+
console.log(import_chalk17.default.dim(` email: ${auth.user?.email || "unknown"}`));
|
|
13303
13894
|
const session = await checkSession();
|
|
13304
13895
|
if (session.valid) {
|
|
13305
|
-
console.log(
|
|
13306
|
-
console.log(
|
|
13896
|
+
console.log(import_chalk17.default.green(" \u2713 session valid"));
|
|
13897
|
+
console.log(import_chalk17.default.dim(` plan: ${session.plan || "none"}`));
|
|
13307
13898
|
} else {
|
|
13308
|
-
console.log(
|
|
13309
|
-
console.log(
|
|
13899
|
+
console.log(import_chalk17.default.red(" \u2717 session expired"));
|
|
13900
|
+
console.log(import_chalk17.default.dim(" run: hyv init"));
|
|
13310
13901
|
issues++;
|
|
13311
13902
|
}
|
|
13312
13903
|
} else {
|
|
13313
|
-
console.log(
|
|
13904
|
+
console.log(import_chalk17.default.red(" \u2717 auth data missing"));
|
|
13314
13905
|
issues++;
|
|
13315
13906
|
}
|
|
13316
13907
|
} else {
|
|
13317
|
-
console.log(
|
|
13318
|
-
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"));
|
|
13319
13910
|
}
|
|
13320
|
-
console.log(
|
|
13321
|
-
const voiceMd =
|
|
13322
|
-
const hasVoiceMd =
|
|
13323
|
-
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") : [];
|
|
13324
13915
|
if (hasVoiceMd || profileFiles.length > 0 || diskProfiles.length > 0) {
|
|
13325
13916
|
if (hasVoiceMd)
|
|
13326
|
-
console.log(
|
|
13917
|
+
console.log(import_chalk17.default.green(" \u2713 voice.md exists"));
|
|
13327
13918
|
if (profileFiles.length > 0)
|
|
13328
|
-
console.log(
|
|
13919
|
+
console.log(import_chalk17.default.green(` \u2713 ${profileFiles.length} markdown profile(s)`));
|
|
13329
13920
|
if (diskProfiles.length > 0)
|
|
13330
|
-
console.log(
|
|
13921
|
+
console.log(import_chalk17.default.green(` \u2713 ${diskProfiles.length} full cached profile(s)`));
|
|
13331
13922
|
} else {
|
|
13332
|
-
console.log(
|
|
13333
|
-
console.log(
|
|
13334
|
-
}
|
|
13335
|
-
console.log(
|
|
13336
|
-
const cursorLegacyRule =
|
|
13337
|
-
const cursorRule =
|
|
13338
|
-
if (
|
|
13339
|
-
console.log(
|
|
13340
|
-
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)"));
|
|
13341
13932
|
issues++;
|
|
13342
13933
|
}
|
|
13343
13934
|
const agentChecks = [
|
|
13344
|
-
{ name: "claude desktop mcp", ok: readMcpHyv(
|
|
13345
|
-
{ name: "cursor mcp", ok: readMcpHyv(
|
|
13346
|
-
{ name: "cursor rule", ok:
|
|
13347
|
-
{ name: "claude code command", ok:
|
|
13348
|
-
{ name: "claude code skill", ok:
|
|
13349
|
-
{ name: "codex agents", ok:
|
|
13350
|
-
{ 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")) }
|
|
13351
13942
|
];
|
|
13352
13943
|
for (const agent of agentChecks) {
|
|
13353
13944
|
if (agent.ok) {
|
|
13354
|
-
console.log(
|
|
13945
|
+
console.log(import_chalk17.default.green(` \u2713 ${agent.name}`));
|
|
13355
13946
|
} else {
|
|
13356
|
-
console.log(
|
|
13947
|
+
console.log(import_chalk17.default.dim(` - ${agent.name} not configured`));
|
|
13357
13948
|
}
|
|
13358
13949
|
}
|
|
13359
13950
|
if (opts.fixAgents) {
|
|
13360
13951
|
try {
|
|
13361
|
-
const pkgDir =
|
|
13362
|
-
const { setupAgents } = require(
|
|
13952
|
+
const pkgDir = path16.resolve(__dirname, "..");
|
|
13953
|
+
const { setupAgents } = require(path16.join(pkgDir, "scripts", "postinstall-lib.js"));
|
|
13363
13954
|
const result = setupAgents({ pkgDir, quiet: true });
|
|
13364
|
-
console.log(
|
|
13955
|
+
console.log(import_chalk17.default.green(` \u2713 re-ran agent setup (${result.configured.join(", ") || "no changes"})`));
|
|
13365
13956
|
if (result.warnings.length) {
|
|
13366
|
-
console.log(
|
|
13957
|
+
console.log(import_chalk17.default.yellow(` notes: ${result.warnings.join("; ")}`));
|
|
13367
13958
|
}
|
|
13368
13959
|
fixed++;
|
|
13369
13960
|
} catch (err) {
|
|
13370
|
-
console.log(
|
|
13961
|
+
console.log(import_chalk17.default.yellow(` ! could not re-run agent setup: ${err.message}`));
|
|
13371
13962
|
}
|
|
13372
13963
|
}
|
|
13373
|
-
console.log(
|
|
13374
|
-
const claudeMcp = readMcpHyv(
|
|
13375
|
-
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"));
|
|
13376
13967
|
if (claudeMcp || cursorMcp) {
|
|
13377
13968
|
if (claudeMcp)
|
|
13378
|
-
console.log(
|
|
13969
|
+
console.log(import_chalk17.default.green(" \u2713 mcp configured for claude desktop"));
|
|
13379
13970
|
if (cursorMcp)
|
|
13380
|
-
console.log(
|
|
13971
|
+
console.log(import_chalk17.default.green(" \u2713 mcp configured for cursor"));
|
|
13381
13972
|
} else {
|
|
13382
|
-
console.log(
|
|
13973
|
+
console.log(import_chalk17.default.yellow(" ! mcp not configured \u2014 run: hyv doctor --fix-agents or hyv mcp --setup"));
|
|
13383
13974
|
issues++;
|
|
13384
13975
|
}
|
|
13385
13976
|
try {
|
|
13386
13977
|
const stdio = await testMcpStdioSubprocess();
|
|
13387
13978
|
if (stdio.ok && (stdio.toolCount || 0) >= 10) {
|
|
13388
|
-
console.log(
|
|
13979
|
+
console.log(import_chalk17.default.green(` \u2713 mcp stdio subprocess healthy (${stdio.toolCount} tools)`));
|
|
13389
13980
|
} else if (stdio.ok) {
|
|
13390
|
-
console.log(
|
|
13981
|
+
console.log(import_chalk17.default.yellow(` ! mcp stdio ok but only ${stdio.toolCount || 0} tools`));
|
|
13391
13982
|
issues++;
|
|
13392
13983
|
} else {
|
|
13393
|
-
console.log(
|
|
13394
|
-
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"));
|
|
13395
13986
|
issues++;
|
|
13396
13987
|
}
|
|
13397
13988
|
} catch (err) {
|
|
13398
|
-
console.log(
|
|
13989
|
+
console.log(import_chalk17.default.red(` \u2717 mcp stdio probe failed: ${err.message}`));
|
|
13399
13990
|
issues++;
|
|
13400
13991
|
}
|
|
13401
13992
|
console.log("");
|
|
13402
13993
|
if (issues === 0 && fixed === 0) {
|
|
13403
|
-
console.log(
|
|
13994
|
+
console.log(import_chalk17.default.green("\u2713 everything looks good!"));
|
|
13404
13995
|
} else if (fixed > 0) {
|
|
13405
|
-
console.log(
|
|
13996
|
+
console.log(import_chalk17.default.green(`\u2713 fixed ${fixed} issue(s)`));
|
|
13406
13997
|
if (issues > 0)
|
|
13407
|
-
console.log(
|
|
13998
|
+
console.log(import_chalk17.default.yellow(`! ${issues} issue(s) remaining`));
|
|
13408
13999
|
} else {
|
|
13409
|
-
console.log(
|
|
14000
|
+
console.log(import_chalk17.default.yellow(`! ${issues} issue(s) found`));
|
|
13410
14001
|
}
|
|
13411
|
-
console.log(
|
|
14002
|
+
console.log(import_chalk17.default.dim("\nfree scan: hyv scan draft.md | full tour: hyv welcome\n"));
|
|
13412
14003
|
});
|
|
13413
14004
|
}
|
|
13414
14005
|
|
|
13415
14006
|
// src/commands/rename.ts
|
|
13416
|
-
var
|
|
14007
|
+
var import_chalk18 = __toESM(require_source());
|
|
13417
14008
|
init_config();
|
|
13418
14009
|
init_auth();
|
|
13419
14010
|
function registerRenameCommand(program3) {
|
|
@@ -13421,26 +14012,26 @@ function registerRenameCommand(program3) {
|
|
|
13421
14012
|
try {
|
|
13422
14013
|
const trimmedName = newName.trim();
|
|
13423
14014
|
if (!trimmedName) {
|
|
13424
|
-
console.log(
|
|
13425
|
-
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'));
|
|
13426
14017
|
process.exit(1);
|
|
13427
14018
|
return;
|
|
13428
14019
|
}
|
|
13429
14020
|
if (trimmedName.length > 100) {
|
|
13430
|
-
console.log(
|
|
14021
|
+
console.log(import_chalk18.default.red("\nerror: name is too long \u2014 keep it under 100 characters\n"));
|
|
13431
14022
|
process.exit(1);
|
|
13432
14023
|
return;
|
|
13433
14024
|
}
|
|
13434
14025
|
if (!/^[a-zA-Z0-9\s\-_'&.]+$/.test(trimmedName)) {
|
|
13435
|
-
console.log(
|
|
13436
|
-
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"));
|
|
13437
14028
|
process.exit(1);
|
|
13438
14029
|
return;
|
|
13439
14030
|
}
|
|
13440
14031
|
const token = getToken();
|
|
13441
14032
|
if (!token) {
|
|
13442
|
-
console.log(
|
|
13443
|
-
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"));
|
|
13444
14035
|
process.exit(1);
|
|
13445
14036
|
return;
|
|
13446
14037
|
}
|
|
@@ -13456,16 +14047,16 @@ function registerRenameCommand(program3) {
|
|
|
13456
14047
|
);
|
|
13457
14048
|
if (response.status === 200) {
|
|
13458
14049
|
const data = response.data;
|
|
13459
|
-
console.log(
|
|
14050
|
+
console.log(import_chalk18.default.green(`
|
|
13460
14051
|
\u2713 profile renamed to ${data.profile?.name || trimmedName}
|
|
13461
14052
|
`));
|
|
13462
14053
|
} else {
|
|
13463
|
-
console.log(
|
|
14054
|
+
console.log(import_chalk18.default.red(`
|
|
13464
14055
|
error: server returned ${response.status}
|
|
13465
14056
|
`));
|
|
13466
14057
|
}
|
|
13467
14058
|
} catch (error) {
|
|
13468
|
-
console.error(
|
|
14059
|
+
console.error(import_chalk18.default.red(`
|
|
13469
14060
|
error: ${error.message}
|
|
13470
14061
|
`));
|
|
13471
14062
|
process.exit(1);
|
|
@@ -13474,46 +14065,46 @@ error: ${error.message}
|
|
|
13474
14065
|
}
|
|
13475
14066
|
|
|
13476
14067
|
// src/commands/fix.ts
|
|
13477
|
-
var
|
|
14068
|
+
var import_chalk20 = __toESM(require_source());
|
|
13478
14069
|
init_pipeline();
|
|
13479
14070
|
init_local_profile();
|
|
13480
14071
|
init_access();
|
|
13481
14072
|
init_config();
|
|
13482
14073
|
|
|
13483
14074
|
// src/lib/destructive-write.ts
|
|
13484
|
-
var
|
|
13485
|
-
var
|
|
13486
|
-
var
|
|
13487
|
-
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());
|
|
13488
14079
|
async function confirmDestructiveWrite(options) {
|
|
13489
14080
|
if (options.yes)
|
|
13490
14081
|
return true;
|
|
13491
14082
|
const label = options.target ? `${options.action} (${options.target})` : options.action;
|
|
13492
14083
|
if (!process.stdin.isTTY) {
|
|
13493
|
-
console.error(
|
|
13494
|
-
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"));
|
|
13495
14086
|
return false;
|
|
13496
14087
|
}
|
|
13497
14088
|
const question = `
|
|
13498
14089
|
${label}
|
|
13499
14090
|
A .bak backup will be created. Proceed? [y/N] `;
|
|
13500
|
-
const answer = await new Promise((
|
|
13501
|
-
const rl =
|
|
14091
|
+
const answer = await new Promise((resolve15) => {
|
|
14092
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
13502
14093
|
rl.question(question, (value) => {
|
|
13503
14094
|
rl.close();
|
|
13504
|
-
|
|
14095
|
+
resolve15(value.trim().toLowerCase());
|
|
13505
14096
|
});
|
|
13506
14097
|
});
|
|
13507
14098
|
return answer === "y" || answer === "yes";
|
|
13508
14099
|
}
|
|
13509
14100
|
function writeInPlaceWithBackup(filePath, content) {
|
|
13510
14101
|
const backupPath = filePath + ".bak";
|
|
13511
|
-
|
|
13512
|
-
|
|
14102
|
+
fs16.copyFileSync(filePath, backupPath);
|
|
14103
|
+
fs16.writeFileSync(filePath, content);
|
|
13513
14104
|
return backupPath;
|
|
13514
14105
|
}
|
|
13515
14106
|
function backupBasename(filePath) {
|
|
13516
|
-
return
|
|
14107
|
+
return path17.basename(filePath) + ".bak";
|
|
13517
14108
|
}
|
|
13518
14109
|
|
|
13519
14110
|
// src/commands/fix.ts
|
|
@@ -13527,9 +14118,9 @@ function registerFixCommand(program3) {
|
|
|
13527
14118
|
if (options.format === "json") {
|
|
13528
14119
|
console.log(JSON.stringify({ file: filePath, autoFixes: 0, llmIssues: result.stats.needsLLM, changes: [] }));
|
|
13529
14120
|
} else {
|
|
13530
|
-
console.log(
|
|
14121
|
+
console.log(import_chalk20.default.green("\n\u2713 No auto-fixable issues found."));
|
|
13531
14122
|
if (result.stats.needsLLM > 0) {
|
|
13532
|
-
console.log(
|
|
14123
|
+
console.log(import_chalk20.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
|
|
13533
14124
|
}
|
|
13534
14125
|
}
|
|
13535
14126
|
return;
|
|
@@ -13543,16 +14134,16 @@ function registerFixCommand(program3) {
|
|
|
13543
14134
|
fixed: options.dryRun ? void 0 : result.fixed
|
|
13544
14135
|
}, null, 2));
|
|
13545
14136
|
} else {
|
|
13546
|
-
console.log(
|
|
14137
|
+
console.log(import_chalk20.default.dim(`
|
|
13547
14138
|
hyv fix ${filePath}
|
|
13548
14139
|
`));
|
|
13549
14140
|
for (const change of result.changes) {
|
|
13550
|
-
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));
|
|
13551
14142
|
}
|
|
13552
|
-
console.log(
|
|
14143
|
+
console.log(import_chalk20.default.green(`
|
|
13553
14144
|
\u2713 ${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} applied`));
|
|
13554
14145
|
if (result.stats.needsLLM > 0) {
|
|
13555
|
-
console.log(
|
|
14146
|
+
console.log(import_chalk20.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite \u2014 run: hyv rewrite ${file}`));
|
|
13556
14147
|
}
|
|
13557
14148
|
}
|
|
13558
14149
|
if (!options.dryRun) {
|
|
@@ -13566,7 +14157,7 @@ hyv fix ${filePath}
|
|
|
13566
14157
|
process.exit(1);
|
|
13567
14158
|
}
|
|
13568
14159
|
const backupPath = writeInPlaceWithBackup(filePath, result.fixed);
|
|
13569
|
-
console.log(
|
|
14160
|
+
console.log(import_chalk20.default.dim(` Written to ${filePath} (backup: ${backupBasename(filePath)})`));
|
|
13570
14161
|
saveLastEditSession({
|
|
13571
14162
|
original_path: filePath,
|
|
13572
14163
|
edited_path: filePath,
|
|
@@ -13574,7 +14165,7 @@ hyv fix ${filePath}
|
|
|
13574
14165
|
edited_text: result.fixed,
|
|
13575
14166
|
profile: options.profile
|
|
13576
14167
|
});
|
|
13577
|
-
console.log(
|
|
14168
|
+
console.log(import_chalk20.default.dim(" Tip: hyv reinforce --last to teach your profile from this edit"));
|
|
13578
14169
|
} else if (filePath === "stdin" || !options.inPlace) {
|
|
13579
14170
|
process.stdout.write("\n" + result.fixed + "\n");
|
|
13580
14171
|
if (filePath !== "stdin") {
|
|
@@ -13590,14 +14181,14 @@ hyv fix ${filePath}
|
|
|
13590
14181
|
}
|
|
13591
14182
|
await maybeShowLimitedModeHint(!!profile);
|
|
13592
14183
|
} catch (error) {
|
|
13593
|
-
console.error(
|
|
14184
|
+
console.error(import_chalk20.default.red(`Error: ${error.message}`));
|
|
13594
14185
|
process.exit(1);
|
|
13595
14186
|
}
|
|
13596
14187
|
});
|
|
13597
14188
|
}
|
|
13598
14189
|
|
|
13599
14190
|
// src/commands/check.ts
|
|
13600
|
-
var
|
|
14191
|
+
var import_chalk21 = __toESM(require_source());
|
|
13601
14192
|
init_pipeline();
|
|
13602
14193
|
init_local_profile();
|
|
13603
14194
|
init_access();
|
|
@@ -13607,20 +14198,20 @@ function registerCheckCommand(program3) {
|
|
|
13607
14198
|
const profile = await loadProfileForCommand(options.profile);
|
|
13608
14199
|
let inputText = text;
|
|
13609
14200
|
if (text === "-") {
|
|
13610
|
-
const
|
|
14201
|
+
const fs27 = require("fs");
|
|
13611
14202
|
if (process.stdin.isTTY) {
|
|
13612
|
-
console.error(
|
|
14203
|
+
console.error(import_chalk21.default.red("No input provided. Pipe content or pass text as argument."));
|
|
13613
14204
|
process.exit(1);
|
|
13614
14205
|
}
|
|
13615
|
-
inputText =
|
|
14206
|
+
inputText = fs27.readFileSync(0, "utf-8");
|
|
13616
14207
|
}
|
|
13617
14208
|
if (!inputText.trim()) {
|
|
13618
|
-
console.error(
|
|
14209
|
+
console.error(import_chalk21.default.red("No text provided."));
|
|
13619
14210
|
process.exit(1);
|
|
13620
14211
|
}
|
|
13621
|
-
const
|
|
13622
|
-
if (text !== "-" &&
|
|
13623
|
-
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}`));
|
|
13624
14215
|
process.exit(1);
|
|
13625
14216
|
}
|
|
13626
14217
|
const result = runPipeline(inputText, profile, false);
|
|
@@ -13644,30 +14235,30 @@ function registerCheckCommand(program3) {
|
|
|
13644
14235
|
}, null, 2));
|
|
13645
14236
|
} else {
|
|
13646
14237
|
if (result.stats.totalSignals === 0) {
|
|
13647
|
-
console.log(
|
|
13648
|
-
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`));
|
|
13649
14240
|
} else {
|
|
13650
14241
|
console.log("");
|
|
13651
14242
|
printGroupedSignals(result.signalMap.signals.slice(0, 15), profile);
|
|
13652
14243
|
if (result.signalMap.signals.length > 15) {
|
|
13653
|
-
console.log(
|
|
14244
|
+
console.log(import_chalk21.default.dim(` ... and ${result.signalMap.signals.length - 15} more`));
|
|
13654
14245
|
}
|
|
13655
|
-
console.log(
|
|
14246
|
+
console.log(import_chalk21.default.yellow(`
|
|
13656
14247
|
${result.stats.totalSignals} issues (${result.stats.red} red, ${result.stats.yellow} yellow)`));
|
|
13657
|
-
console.log(
|
|
14248
|
+
console.log(import_chalk21.default.dim(` score: ${result.score}/100`));
|
|
13658
14249
|
}
|
|
13659
14250
|
await maybeShowLimitedModeHint(!!profile);
|
|
13660
14251
|
}
|
|
13661
14252
|
process.exit(result.stats.totalSignals > 0 ? 1 : 0);
|
|
13662
14253
|
} catch (error) {
|
|
13663
|
-
console.error(
|
|
14254
|
+
console.error(import_chalk21.default.red(`Error: ${error.message}`));
|
|
13664
14255
|
process.exit(1);
|
|
13665
14256
|
}
|
|
13666
14257
|
});
|
|
13667
14258
|
}
|
|
13668
14259
|
|
|
13669
14260
|
// src/commands/score.ts
|
|
13670
|
-
var
|
|
14261
|
+
var import_chalk22 = __toESM(require_source());
|
|
13671
14262
|
init_pipeline();
|
|
13672
14263
|
init_local_profile();
|
|
13673
14264
|
function registerScoreCommand(program3) {
|
|
@@ -13692,14 +14283,14 @@ function registerScoreCommand(program3) {
|
|
|
13692
14283
|
process.exit(1);
|
|
13693
14284
|
}
|
|
13694
14285
|
} catch (error) {
|
|
13695
|
-
console.error(
|
|
14286
|
+
console.error(import_chalk22.default.red(`Error: ${error.message}`));
|
|
13696
14287
|
process.exit(1);
|
|
13697
14288
|
}
|
|
13698
14289
|
});
|
|
13699
14290
|
}
|
|
13700
14291
|
|
|
13701
14292
|
// src/commands/diff.ts
|
|
13702
|
-
var
|
|
14293
|
+
var import_chalk23 = __toESM(require_source());
|
|
13703
14294
|
init_pipeline();
|
|
13704
14295
|
init_local_profile();
|
|
13705
14296
|
function registerDiffCommand(program3) {
|
|
@@ -13709,9 +14300,9 @@ function registerDiffCommand(program3) {
|
|
|
13709
14300
|
const { text, path: filePath } = readText(file);
|
|
13710
14301
|
const result = runPipeline(text, profile, true);
|
|
13711
14302
|
if (result.changes.length === 0) {
|
|
13712
|
-
console.log(
|
|
14303
|
+
console.log(import_chalk23.default.green("\n\u2713 No auto-fixable changes."));
|
|
13713
14304
|
if (result.stats.needsLLM > 0) {
|
|
13714
|
-
console.log(
|
|
14305
|
+
console.log(import_chalk23.default.dim(` ${result.stats.needsLLM} issues need LLM rewrite`));
|
|
13715
14306
|
}
|
|
13716
14307
|
return;
|
|
13717
14308
|
}
|
|
@@ -13726,42 +14317,42 @@ function registerDiffCommand(program3) {
|
|
|
13726
14317
|
const originalLines = text.split("\n");
|
|
13727
14318
|
const fixedLines = result.fixed.split("\n");
|
|
13728
14319
|
const contextN = parseInt(options.context, 10);
|
|
13729
|
-
console.log(
|
|
13730
|
-
console.log(
|
|
14320
|
+
console.log(import_chalk23.default.dim(`--- ${filePath}`));
|
|
14321
|
+
console.log(import_chalk23.default.dim(`+++ ${filePath} (fixed)`));
|
|
13731
14322
|
for (const change of result.changes) {
|
|
13732
14323
|
const lineIdx = change.line - 1;
|
|
13733
14324
|
const start = Math.max(0, lineIdx - contextN);
|
|
13734
14325
|
const end = Math.min(originalLines.length, lineIdx + contextN + 1);
|
|
13735
|
-
console.log(
|
|
14326
|
+
console.log(import_chalk23.default.dim(`@@ -${start + 1},${end - start} +${start + 1},${end - start} @@`));
|
|
13736
14327
|
for (let i = start; i < end; i++) {
|
|
13737
14328
|
if (i === lineIdx) {
|
|
13738
|
-
console.log(
|
|
13739
|
-
console.log(
|
|
14329
|
+
console.log(import_chalk23.default.red(`-${originalLines[i]}`));
|
|
14330
|
+
console.log(import_chalk23.default.green(`+${fixedLines[i]}`));
|
|
13740
14331
|
} else {
|
|
13741
|
-
console.log(
|
|
14332
|
+
console.log(import_chalk23.default.dim(` ${originalLines[i]}`));
|
|
13742
14333
|
}
|
|
13743
14334
|
}
|
|
13744
14335
|
}
|
|
13745
|
-
console.log(
|
|
14336
|
+
console.log(import_chalk23.default.green(`
|
|
13746
14337
|
${result.changes.length} auto-fix${result.changes.length === 1 ? "" : "es"} available`));
|
|
13747
|
-
console.log(
|
|
14338
|
+
console.log(import_chalk23.default.dim(` run: hyv fix ${file} -i to apply`));
|
|
13748
14339
|
if (options.apply && filePath !== "stdin") {
|
|
13749
|
-
const
|
|
13750
|
-
const
|
|
14340
|
+
const fs26 = require("fs");
|
|
14341
|
+
const path25 = require("path");
|
|
13751
14342
|
const backupPath = filePath + ".bak";
|
|
13752
|
-
|
|
13753
|
-
|
|
13754
|
-
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)}`));
|
|
13755
14346
|
}
|
|
13756
14347
|
} catch (error) {
|
|
13757
|
-
console.error(
|
|
14348
|
+
console.error(import_chalk23.default.red(`Error: ${error.message}`));
|
|
13758
14349
|
process.exit(1);
|
|
13759
14350
|
}
|
|
13760
14351
|
});
|
|
13761
14352
|
}
|
|
13762
14353
|
|
|
13763
14354
|
// src/commands/rules.ts
|
|
13764
|
-
var
|
|
14355
|
+
var import_chalk24 = __toESM(require_source());
|
|
13765
14356
|
init_config();
|
|
13766
14357
|
var RULE_CATALOG = [
|
|
13767
14358
|
// AI Overused Words
|
|
@@ -13855,7 +14446,7 @@ function registerRulesCommand(program3) {
|
|
|
13855
14446
|
for (const id of ids)
|
|
13856
14447
|
disabledRules.delete(id);
|
|
13857
14448
|
writeConfig({ ...config, disabled_rules: [...disabledRules] });
|
|
13858
|
-
console.log(
|
|
14449
|
+
console.log(import_chalk24.default.green(`
|
|
13859
14450
|
\u2713 Enabled: ${ids.join(", ")}`));
|
|
13860
14451
|
return;
|
|
13861
14452
|
}
|
|
@@ -13864,13 +14455,13 @@ function registerRulesCommand(program3) {
|
|
|
13864
14455
|
for (const id of ids)
|
|
13865
14456
|
disabledRules.add(id);
|
|
13866
14457
|
writeConfig({ ...config, disabled_rules: [...disabledRules] });
|
|
13867
|
-
console.log(
|
|
14458
|
+
console.log(import_chalk24.default.green(`
|
|
13868
14459
|
\u2713 Disabled: ${ids.join(", ")}`));
|
|
13869
14460
|
return;
|
|
13870
14461
|
}
|
|
13871
14462
|
if (options.reset) {
|
|
13872
14463
|
writeConfig({ ...config, disabled_rules: [] });
|
|
13873
|
-
console.log(
|
|
14464
|
+
console.log(import_chalk24.default.green("\n\u2713 All rules reset to default (enabled)"));
|
|
13874
14465
|
return;
|
|
13875
14466
|
}
|
|
13876
14467
|
let rules = [...RULE_CATALOG];
|
|
@@ -13898,36 +14489,36 @@ function registerRulesCommand(program3) {
|
|
|
13898
14489
|
}
|
|
13899
14490
|
console.log("");
|
|
13900
14491
|
for (const [cat, catRules] of categories) {
|
|
13901
|
-
console.log(
|
|
14492
|
+
console.log(import_chalk24.default.bold(` ${cat} rules (${catRules.length})
|
|
13902
14493
|
`));
|
|
13903
|
-
console.log(
|
|
13904
|
-
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"));
|
|
13905
14496
|
for (const rule of catRules) {
|
|
13906
|
-
const sev = rule.severity === "red" ?
|
|
13907
|
-
const fix = rule.autoFixable ?
|
|
13908
|
-
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");
|
|
13909
14500
|
const id = rule.id.padEnd(24);
|
|
13910
|
-
console.log(` ${
|
|
14501
|
+
console.log(` ${import_chalk24.default.dim(id)} ${sev} ${fix} ${enabled2}`);
|
|
13911
14502
|
}
|
|
13912
14503
|
console.log("");
|
|
13913
14504
|
}
|
|
13914
14505
|
const enabled = rules.filter((r) => !disabledRules.has(r.id)).length;
|
|
13915
14506
|
const disabled = rules.length - enabled;
|
|
13916
|
-
console.log(
|
|
13917
|
-
console.log(
|
|
13918
|
-
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
|
|
13919
14510
|
`));
|
|
13920
14511
|
} catch (error) {
|
|
13921
|
-
console.error(
|
|
14512
|
+
console.error(import_chalk24.default.red(`Error: ${error.message}`));
|
|
13922
14513
|
process.exit(1);
|
|
13923
14514
|
}
|
|
13924
14515
|
});
|
|
13925
14516
|
}
|
|
13926
14517
|
|
|
13927
14518
|
// src/commands/batch.ts
|
|
13928
|
-
var
|
|
13929
|
-
var
|
|
13930
|
-
var
|
|
14519
|
+
var import_chalk25 = __toESM(require_source());
|
|
14520
|
+
var fs17 = __toESM(require("fs"));
|
|
14521
|
+
var path18 = __toESM(require("path"));
|
|
13931
14522
|
init_pipeline();
|
|
13932
14523
|
init_local_profile();
|
|
13933
14524
|
function registerBatchCommand(program3) {
|
|
@@ -13940,7 +14531,7 @@ function registerBatchCommand(program3) {
|
|
|
13940
14531
|
nodir: true
|
|
13941
14532
|
});
|
|
13942
14533
|
if (files.length === 0) {
|
|
13943
|
-
console.log(
|
|
14534
|
+
console.log(import_chalk25.default.yellow(`
|
|
13944
14535
|
No files matching: ${pattern}`));
|
|
13945
14536
|
return;
|
|
13946
14537
|
}
|
|
@@ -13954,16 +14545,16 @@ No files matching: ${pattern}`));
|
|
|
13954
14545
|
process.exit(1);
|
|
13955
14546
|
}
|
|
13956
14547
|
if (options.format === "text") {
|
|
13957
|
-
console.log(
|
|
14548
|
+
console.log(import_chalk25.default.dim(`
|
|
13958
14549
|
scanning ${files.length} file${files.length === 1 ? "" : "s"}...
|
|
13959
14550
|
`));
|
|
13960
14551
|
}
|
|
13961
14552
|
const results = [];
|
|
13962
14553
|
for (const file of files) {
|
|
13963
|
-
const absPath =
|
|
13964
|
-
if (!
|
|
14554
|
+
const absPath = path18.resolve(file);
|
|
14555
|
+
if (!fs17.existsSync(absPath))
|
|
13965
14556
|
continue;
|
|
13966
|
-
const text =
|
|
14557
|
+
const text = fs17.readFileSync(absPath, "utf-8");
|
|
13967
14558
|
const result = runPipeline(text, profile, options.fix || false);
|
|
13968
14559
|
results.push({
|
|
13969
14560
|
file,
|
|
@@ -13993,23 +14584,23 @@ No files matching: ${pattern}`));
|
|
|
13993
14584
|
}
|
|
13994
14585
|
} else {
|
|
13995
14586
|
for (const r of results) {
|
|
13996
|
-
const icon = r.issues > 0 ?
|
|
13997
|
-
const score = r.score < 60 ?
|
|
13998
|
-
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");
|
|
13999
14590
|
console.log(` ${icon} ${r.file.padEnd(40)} ${issueStr.padEnd(20)} score: ${score}`);
|
|
14000
14591
|
}
|
|
14001
14592
|
const withIssues = results.filter((r) => r.issues > 0).length;
|
|
14002
14593
|
const totalIssues = results.reduce((sum, r) => sum + r.issues, 0);
|
|
14003
14594
|
const worst = results.reduce((min, r) => r.score < min.score ? r : min, results[0]);
|
|
14004
|
-
console.log(
|
|
14595
|
+
console.log(import_chalk25.default.dim(`
|
|
14005
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`));
|
|
14006
|
-
console.log(
|
|
14597
|
+
console.log(import_chalk25.default.dim(` ${results.length} files, ${withIssues} with issues, ${totalIssues} total issues`));
|
|
14007
14598
|
if (withIssues > 0) {
|
|
14008
|
-
console.log(
|
|
14599
|
+
console.log(import_chalk25.default.dim(` worst: ${worst.file} (${worst.score}/100)`));
|
|
14009
14600
|
}
|
|
14010
14601
|
if (options.fix && options.inPlace) {
|
|
14011
14602
|
const fixed = results.filter((r) => r.autoFixes > 0);
|
|
14012
|
-
console.log(
|
|
14603
|
+
console.log(import_chalk25.default.green(`
|
|
14013
14604
|
\u2713 ${fixed.reduce((sum, r) => sum + r.autoFixes, 0)} auto-fixes applied across ${fixed.length} files`));
|
|
14014
14605
|
}
|
|
14015
14606
|
}
|
|
@@ -14021,25 +14612,25 @@ No files matching: ${pattern}`));
|
|
|
14021
14612
|
if (belowThreshold)
|
|
14022
14613
|
process.exit(1);
|
|
14023
14614
|
} catch (error) {
|
|
14024
|
-
console.error(
|
|
14615
|
+
console.error(import_chalk25.default.red(`Error: ${error.message}`));
|
|
14025
14616
|
process.exit(1);
|
|
14026
14617
|
}
|
|
14027
14618
|
});
|
|
14028
14619
|
}
|
|
14029
14620
|
|
|
14030
14621
|
// src/commands/watch.ts
|
|
14031
|
-
var
|
|
14032
|
-
var
|
|
14033
|
-
var
|
|
14622
|
+
var import_chalk26 = __toESM(require_source());
|
|
14623
|
+
var fs18 = __toESM(require("fs"));
|
|
14624
|
+
var path19 = __toESM(require("path"));
|
|
14034
14625
|
init_pipeline();
|
|
14035
14626
|
init_local_profile();
|
|
14036
14627
|
function registerWatchCommand(program3) {
|
|
14037
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) => {
|
|
14038
14629
|
try {
|
|
14039
14630
|
const profile = await loadProfileForCommand(options.profile);
|
|
14040
|
-
const absPath =
|
|
14041
|
-
if (!
|
|
14042
|
-
console.error(
|
|
14631
|
+
const absPath = path19.resolve(file);
|
|
14632
|
+
if (!fs18.existsSync(absPath)) {
|
|
14633
|
+
console.error(import_chalk26.default.red(`File not found: ${absPath}`));
|
|
14043
14634
|
process.exit(1);
|
|
14044
14635
|
}
|
|
14045
14636
|
if (options.command === "fix") {
|
|
@@ -14051,59 +14642,59 @@ function registerWatchCommand(program3) {
|
|
|
14051
14642
|
if (!confirmed)
|
|
14052
14643
|
process.exit(1);
|
|
14053
14644
|
}
|
|
14054
|
-
console.log(
|
|
14645
|
+
console.log(import_chalk26.default.dim(`
|
|
14055
14646
|
watching ${absPath}`));
|
|
14056
|
-
console.log(
|
|
14057
|
-
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"));
|
|
14058
14649
|
let debounceTimer = null;
|
|
14059
14650
|
const debounceMs = parseInt(options.debounce, 10);
|
|
14060
14651
|
let scanCount = 0;
|
|
14061
14652
|
let lastScore = 0;
|
|
14062
14653
|
let ignoreWatchUntil = 0;
|
|
14063
14654
|
const runScan = () => {
|
|
14064
|
-
const text =
|
|
14655
|
+
const text = fs18.readFileSync(absPath, "utf-8");
|
|
14065
14656
|
const result = runPipeline(text, profile, options.command === "fix");
|
|
14066
14657
|
scanCount++;
|
|
14067
14658
|
const now = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
14068
|
-
console.log(
|
|
14659
|
+
console.log(import_chalk26.default.dim(`[${now}] saved \u2014 ${options.command}ing...`));
|
|
14069
14660
|
if (options.command === "score") {
|
|
14070
14661
|
const score = result.score;
|
|
14071
|
-
const color = score < 60 ?
|
|
14662
|
+
const color = score < 60 ? import_chalk26.default.red : score < 80 ? import_chalk26.default.yellow : import_chalk26.default.green;
|
|
14072
14663
|
console.log(` ${color(score + "/100")}`);
|
|
14073
14664
|
if (lastScore > 0 && score !== lastScore) {
|
|
14074
14665
|
const delta = score - lastScore;
|
|
14075
|
-
console.log(
|
|
14666
|
+
console.log(import_chalk26.default.dim(` ${delta > 0 ? "\u2191" : "\u2193"} ${Math.abs(delta)} from last scan`));
|
|
14076
14667
|
}
|
|
14077
14668
|
lastScore = score;
|
|
14078
14669
|
} else if (options.command === "fix") {
|
|
14079
14670
|
if (result.changes.length > 0) {
|
|
14080
14671
|
for (const change of result.changes) {
|
|
14081
|
-
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));
|
|
14082
14673
|
}
|
|
14083
14674
|
ignoreWatchUntil = Date.now() + debounceMs + 200;
|
|
14084
14675
|
writeInPlaceWithBackup(absPath, result.fixed);
|
|
14085
|
-
console.log(
|
|
14676
|
+
console.log(import_chalk26.default.green(` \u2713 ${result.changes.length} auto-fixes applied`));
|
|
14086
14677
|
} else {
|
|
14087
|
-
console.log(
|
|
14678
|
+
console.log(import_chalk26.default.green(" \u2713 no auto-fixable issues"));
|
|
14088
14679
|
}
|
|
14089
14680
|
} else {
|
|
14090
14681
|
if (result.stats.totalSignals === 0) {
|
|
14091
|
-
console.log(
|
|
14682
|
+
console.log(import_chalk26.default.green(` \u2713 clean \u2014 score: ${result.score}/100`));
|
|
14092
14683
|
} else {
|
|
14093
|
-
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`));
|
|
14094
14685
|
for (const signal of result.signalMap.signals.slice(0, 3)) {
|
|
14095
|
-
const sev = signal.severity === "red" ?
|
|
14686
|
+
const sev = signal.severity === "red" ? import_chalk26.default.red("\u25CF") : import_chalk26.default.yellow("\u25CB");
|
|
14096
14687
|
console.log(` ${sev} line ${signal.line}: ${signal.id} \u2014 ${signal.suggestion}`);
|
|
14097
14688
|
}
|
|
14098
14689
|
if (result.signalMap.signals.length > 3) {
|
|
14099
|
-
console.log(
|
|
14690
|
+
console.log(import_chalk26.default.dim(` ... and ${result.signalMap.signals.length - 3} more`));
|
|
14100
14691
|
}
|
|
14101
14692
|
}
|
|
14102
14693
|
}
|
|
14103
14694
|
console.log("");
|
|
14104
14695
|
};
|
|
14105
14696
|
runScan();
|
|
14106
|
-
|
|
14697
|
+
fs18.watch(absPath, (eventType) => {
|
|
14107
14698
|
if (eventType !== "change")
|
|
14108
14699
|
return;
|
|
14109
14700
|
if (Date.now() < ignoreWatchUntil)
|
|
@@ -14113,22 +14704,22 @@ watching ${absPath}`));
|
|
|
14113
14704
|
debounceTimer = setTimeout(runScan, debounceMs);
|
|
14114
14705
|
});
|
|
14115
14706
|
process.on("SIGINT", () => {
|
|
14116
|
-
console.log(
|
|
14707
|
+
console.log(import_chalk26.default.dim(`
|
|
14117
14708
|
${scanCount} scans completed. exiting.`));
|
|
14118
14709
|
process.exit(0);
|
|
14119
14710
|
});
|
|
14120
14711
|
await new Promise(() => {
|
|
14121
14712
|
});
|
|
14122
14713
|
} catch (error) {
|
|
14123
|
-
console.error(
|
|
14714
|
+
console.error(import_chalk26.default.red(`Error: ${error.message}`));
|
|
14124
14715
|
process.exit(1);
|
|
14125
14716
|
}
|
|
14126
14717
|
});
|
|
14127
14718
|
}
|
|
14128
14719
|
|
|
14129
14720
|
// src/commands/demo.ts
|
|
14130
|
-
var
|
|
14131
|
-
var
|
|
14721
|
+
var import_chalk27 = __toESM(require_source());
|
|
14722
|
+
var fs19 = __toESM(require("fs"));
|
|
14132
14723
|
var SAMPLES = {
|
|
14133
14724
|
linkedin: {
|
|
14134
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.
|
|
@@ -14218,46 +14809,46 @@ function registerDemoCommand(program3) {
|
|
|
14218
14809
|
const style = options.clean ? "clean" : options.style;
|
|
14219
14810
|
const sample = SAMPLES[style];
|
|
14220
14811
|
if (!sample) {
|
|
14221
|
-
console.error(
|
|
14812
|
+
console.error(import_chalk27.default.red(`Unknown style: ${options.style}. Valid: ${Object.keys(SAMPLES).filter((k) => k !== "clean").join(", ")}`));
|
|
14222
14813
|
process.exit(1);
|
|
14223
14814
|
}
|
|
14224
14815
|
if (options.output) {
|
|
14225
14816
|
const outputPath = require("path").resolve(options.output);
|
|
14226
|
-
|
|
14227
|
-
console.log(
|
|
14817
|
+
fs19.writeFileSync(outputPath, sample.text);
|
|
14818
|
+
console.log(import_chalk27.default.green(`
|
|
14228
14819
|
\u2713 Sample written to ${outputPath}`));
|
|
14229
14820
|
if (sample.patterns.length > 0) {
|
|
14230
|
-
console.log(
|
|
14821
|
+
console.log(import_chalk27.default.dim(` Contains ${sample.patterns.length} AI patterns: ${sample.patterns.slice(0, 5).join(", ")}...`));
|
|
14231
14822
|
}
|
|
14232
|
-
console.log(
|
|
14823
|
+
console.log(import_chalk27.default.dim(`
|
|
14233
14824
|
Scan it: hyv scan ${options.output}`));
|
|
14234
|
-
console.log(
|
|
14825
|
+
console.log(import_chalk27.default.dim(` Fix it: hyv fix ${options.output}`));
|
|
14235
14826
|
} else {
|
|
14236
|
-
console.log(
|
|
14827
|
+
console.log(import_chalk27.default.bold(`
|
|
14237
14828
|
\u2500\u2500\u2500 sample (${style}) \u2500\u2500\u2500
|
|
14238
14829
|
`));
|
|
14239
14830
|
console.log(sample.text);
|
|
14240
|
-
console.log(
|
|
14831
|
+
console.log(import_chalk27.default.dim(`
|
|
14241
14832
|
\u2500\u2500\u2500 end \u2500\u2500\u2500`));
|
|
14242
14833
|
if (sample.patterns.length > 0) {
|
|
14243
|
-
console.log(
|
|
14834
|
+
console.log(import_chalk27.default.dim(`
|
|
14244
14835
|
${sample.patterns.length} AI patterns embedded: ${sample.patterns.slice(0, 8).join(", ")}...`));
|
|
14245
14836
|
}
|
|
14246
|
-
console.log(
|
|
14837
|
+
console.log(import_chalk27.default.dim(`
|
|
14247
14838
|
Save to file: hyv demo --output demo.md`));
|
|
14248
|
-
console.log(
|
|
14249
|
-
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
|
|
14250
14841
|
`));
|
|
14251
14842
|
}
|
|
14252
14843
|
} catch (error) {
|
|
14253
|
-
console.error(
|
|
14844
|
+
console.error(import_chalk27.default.red(`Error: ${error.message}`));
|
|
14254
14845
|
process.exit(1);
|
|
14255
14846
|
}
|
|
14256
14847
|
});
|
|
14257
14848
|
}
|
|
14258
14849
|
|
|
14259
14850
|
// src/commands/open.ts
|
|
14260
|
-
var
|
|
14851
|
+
var import_chalk28 = __toESM(require_source());
|
|
14261
14852
|
var PAGES = {
|
|
14262
14853
|
dashboard: "https://holdyourvoice.com/dashboard",
|
|
14263
14854
|
profiles: "https://holdyourvoice.com/dashboard",
|
|
@@ -14274,12 +14865,12 @@ function registerOpenCommand(program3) {
|
|
|
14274
14865
|
return;
|
|
14275
14866
|
}
|
|
14276
14867
|
const open3 = (await Promise.resolve().then(() => __toESM(require_open()))).default;
|
|
14277
|
-
console.log(
|
|
14868
|
+
console.log(import_chalk28.default.dim(`
|
|
14278
14869
|
Opening ${url}...`));
|
|
14279
14870
|
await open3(url);
|
|
14280
|
-
console.log(
|
|
14871
|
+
console.log(import_chalk28.default.green(" \u2713 Opened in browser\n"));
|
|
14281
14872
|
} catch (error) {
|
|
14282
|
-
console.error(
|
|
14873
|
+
console.error(import_chalk28.default.red(`Error: ${error.message}`));
|
|
14283
14874
|
process.exit(1);
|
|
14284
14875
|
}
|
|
14285
14876
|
});
|
|
@@ -14289,46 +14880,67 @@ function registerOpenCommand(program3) {
|
|
|
14289
14880
|
init_welcome();
|
|
14290
14881
|
|
|
14291
14882
|
// src/lib/onboarding.ts
|
|
14292
|
-
var
|
|
14293
|
-
var
|
|
14294
|
-
var
|
|
14295
|
-
var hyvDir =
|
|
14296
|
-
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");
|
|
14297
14888
|
function hasCompletedOnboarding() {
|
|
14298
14889
|
try {
|
|
14299
|
-
return
|
|
14890
|
+
return fs20.existsSync(onboardingFile);
|
|
14300
14891
|
} catch {
|
|
14301
14892
|
return false;
|
|
14302
14893
|
}
|
|
14303
14894
|
}
|
|
14304
14895
|
function markOnboardingComplete(version) {
|
|
14305
|
-
if (!
|
|
14306
|
-
|
|
14307
|
-
|
|
14896
|
+
if (!fs20.existsSync(hyvDir))
|
|
14897
|
+
fs20.mkdirSync(hyvDir, { recursive: true });
|
|
14898
|
+
fs20.writeFileSync(
|
|
14308
14899
|
onboardingFile,
|
|
14309
14900
|
JSON.stringify({ version, completed_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
|
|
14310
14901
|
);
|
|
14311
14902
|
}
|
|
14312
14903
|
|
|
14313
14904
|
// src/commands/welcome.ts
|
|
14314
|
-
|
|
14315
|
-
var
|
|
14905
|
+
init_welcome_flow();
|
|
14906
|
+
var fs21 = __toESM(require("fs"));
|
|
14907
|
+
var path21 = __toESM(require("path"));
|
|
14316
14908
|
function registerWelcomeCommand(program3) {
|
|
14317
14909
|
const pkgVersion3 = (() => {
|
|
14318
14910
|
try {
|
|
14319
|
-
const pkgPath3 =
|
|
14320
|
-
return JSON.parse(
|
|
14911
|
+
const pkgPath3 = path21.resolve(__dirname, "..", "package.json");
|
|
14912
|
+
return JSON.parse(fs21.readFileSync(pkgPath3, "utf-8")).version;
|
|
14321
14913
|
} catch {
|
|
14322
14914
|
return "0.0.0";
|
|
14323
14915
|
}
|
|
14324
14916
|
})();
|
|
14325
|
-
|
|
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) => {
|
|
14326
14918
|
if (opts.quiet)
|
|
14327
14919
|
return;
|
|
14328
|
-
|
|
14329
|
-
|
|
14330
|
-
|
|
14331
|
-
|
|
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
|
+
});
|
|
14332
14944
|
program3.command("free").description("List everything free in the CLI and on the web").action(() => {
|
|
14333
14945
|
console.log("\n" + formatFreeToolsList() + "\n");
|
|
14334
14946
|
});
|
|
@@ -14336,12 +14948,15 @@ function registerWelcomeCommand(program3) {
|
|
|
14336
14948
|
function getDemoText() {
|
|
14337
14949
|
return formatDemoResult(runWelcomeDemo());
|
|
14338
14950
|
}
|
|
14339
|
-
function getWelcomeText() {
|
|
14340
|
-
|
|
14951
|
+
function getWelcomeText(args2) {
|
|
14952
|
+
if (args2?.mode || args2?.step) {
|
|
14953
|
+
return getMcpWelcomeResponse(args2);
|
|
14954
|
+
}
|
|
14955
|
+
return buildWelcomeGuide({ forLlm: true });
|
|
14341
14956
|
}
|
|
14342
14957
|
|
|
14343
14958
|
// src/commands/content.ts
|
|
14344
|
-
var
|
|
14959
|
+
var import_chalk29 = __toESM(require_source());
|
|
14345
14960
|
init_free_paid();
|
|
14346
14961
|
var TEMPLATES = {
|
|
14347
14962
|
cursor: {
|
|
@@ -14415,30 +15030,30 @@ ${COMMUNITY_URL}`
|
|
|
14415
15030
|
function registerContentCommand(program3) {
|
|
14416
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) => {
|
|
14417
15032
|
if (opts.list) {
|
|
14418
|
-
console.log(
|
|
15033
|
+
console.log(import_chalk29.default.bold("\nhyv content templates\n"));
|
|
14419
15034
|
for (const [key, t2] of Object.entries(TEMPLATES)) {
|
|
14420
|
-
console.log(
|
|
15035
|
+
console.log(import_chalk29.default.dim(` ${key}`) + ` \u2014 ${t2.title}`);
|
|
14421
15036
|
}
|
|
14422
15037
|
console.log("");
|
|
14423
15038
|
return;
|
|
14424
15039
|
}
|
|
14425
15040
|
const t = TEMPLATES[topic] || TEMPLATES.cursor;
|
|
14426
|
-
console.log(
|
|
15041
|
+
console.log(import_chalk29.default.bold(`
|
|
14427
15042
|
${t.title}
|
|
14428
15043
|
`));
|
|
14429
15044
|
console.log(t.body);
|
|
14430
|
-
console.log(
|
|
15045
|
+
console.log(import_chalk29.default.dim("\nBlog: https://holdyourvoice.com/blog\n"));
|
|
14431
15046
|
});
|
|
14432
15047
|
}
|
|
14433
15048
|
|
|
14434
15049
|
// src/commands/upgrade.ts
|
|
14435
|
-
var
|
|
15050
|
+
var import_chalk30 = __toESM(require_source());
|
|
14436
15051
|
var import_child_process3 = require("child_process");
|
|
14437
15052
|
init_version();
|
|
14438
15053
|
function registerUpgradeCommand(program3) {
|
|
14439
15054
|
program3.command("upgrade").description("Upgrade to the latest @holdyourvoice/hyv").option("--check", "Only check if an update is available").action(async (opts) => {
|
|
14440
15055
|
const current = getCliVersion();
|
|
14441
|
-
console.log(
|
|
15056
|
+
console.log(import_chalk30.default.dim(`
|
|
14442
15057
|
Current: ${getEngineLabel()}
|
|
14443
15058
|
`));
|
|
14444
15059
|
let latest = current;
|
|
@@ -14446,38 +15061,38 @@ Current: ${getEngineLabel()}
|
|
|
14446
15061
|
const out = (0, import_child_process3.execSync)("npm view @holdyourvoice/hyv version", { encoding: "utf-8", timeout: 15e3 });
|
|
14447
15062
|
latest = out.trim();
|
|
14448
15063
|
} catch {
|
|
14449
|
-
console.log(
|
|
15064
|
+
console.log(import_chalk30.default.yellow("Could not reach npm registry. Try: npm i -g @holdyourvoice/hyv@latest"));
|
|
14450
15065
|
return;
|
|
14451
15066
|
}
|
|
14452
15067
|
const cmp = compareSemver(current, latest);
|
|
14453
15068
|
if (cmp === 0) {
|
|
14454
|
-
console.log(
|
|
15069
|
+
console.log(import_chalk30.default.green("\u2713 You are on the latest version."));
|
|
14455
15070
|
return;
|
|
14456
15071
|
}
|
|
14457
15072
|
if (cmp > 0) {
|
|
14458
|
-
console.log(
|
|
15073
|
+
console.log(import_chalk30.default.green(`\u2713 You are ahead of npm (published: ${latest}).`));
|
|
14459
15074
|
return;
|
|
14460
15075
|
}
|
|
14461
|
-
console.log(
|
|
15076
|
+
console.log(import_chalk30.default.cyan(`Update available: ${current} \u2192 ${latest}`));
|
|
14462
15077
|
if (opts.check) {
|
|
14463
|
-
console.log(
|
|
15078
|
+
console.log(import_chalk30.default.dim("\nRun: hyv upgrade or npm i -g @holdyourvoice/hyv@latest\n"));
|
|
14464
15079
|
return;
|
|
14465
15080
|
}
|
|
14466
|
-
console.log(
|
|
15081
|
+
console.log(import_chalk30.default.dim("Installing..."));
|
|
14467
15082
|
try {
|
|
14468
15083
|
(0, import_child_process3.execSync)("npm i -g @holdyourvoice/hyv@latest", { stdio: "inherit" });
|
|
14469
|
-
console.log(
|
|
15084
|
+
console.log(import_chalk30.default.green("\n\u2713 Upgraded successfully. Restart your terminal.\n"));
|
|
14470
15085
|
} catch {
|
|
14471
|
-
console.log(
|
|
15086
|
+
console.log(import_chalk30.default.red("\nUpgrade failed. Run manually: npm i -g @holdyourvoice/hyv@latest\n"));
|
|
14472
15087
|
process.exit(1);
|
|
14473
15088
|
}
|
|
14474
15089
|
});
|
|
14475
15090
|
}
|
|
14476
15091
|
|
|
14477
15092
|
// src/mcp.ts
|
|
14478
|
-
var
|
|
14479
|
-
var
|
|
14480
|
-
var
|
|
15093
|
+
var fs22 = __toESM(require("fs"));
|
|
15094
|
+
var path22 = __toESM(require("path"));
|
|
15095
|
+
var os8 = __toESM(require("os"));
|
|
14481
15096
|
init_classifier();
|
|
14482
15097
|
init_autofix();
|
|
14483
15098
|
init_validator();
|
|
@@ -14488,20 +15103,20 @@ init_welcome();
|
|
|
14488
15103
|
init_config();
|
|
14489
15104
|
init_telemetry();
|
|
14490
15105
|
init_access();
|
|
14491
|
-
var VOICE_MD =
|
|
15106
|
+
var VOICE_MD = path22.join(os8.homedir(), ".hyv", "voice.md");
|
|
14492
15107
|
var MAX_RESPONSE_CHARS = 12e4;
|
|
14493
15108
|
var MAX_SCAN_FILE_BYTES = 2 * 1024 * 1024;
|
|
14494
15109
|
function readScanFile(filePath) {
|
|
14495
|
-
const cwdReal =
|
|
14496
|
-
const resolved =
|
|
14497
|
-
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) {
|
|
14498
15113
|
throw new Error(`file must be in the current working directory (${cwdReal})`);
|
|
14499
15114
|
}
|
|
14500
|
-
const stat =
|
|
15115
|
+
const stat = fs22.statSync(resolved);
|
|
14501
15116
|
if (stat.size > MAX_SCAN_FILE_BYTES) {
|
|
14502
15117
|
throw new Error(`file too large (max ${MAX_SCAN_FILE_BYTES} bytes)`);
|
|
14503
15118
|
}
|
|
14504
|
-
return
|
|
15119
|
+
return fs22.readFileSync(resolved, "utf-8");
|
|
14505
15120
|
}
|
|
14506
15121
|
function toolResultPayload(result) {
|
|
14507
15122
|
const isError = result.startsWith("Error:") || result.startsWith("Unknown tool:");
|
|
@@ -14510,10 +15125,10 @@ function toolResultPayload(result) {
|
|
|
14510
15125
|
...isError ? { isError: true } : {}
|
|
14511
15126
|
};
|
|
14512
15127
|
}
|
|
14513
|
-
var pkgPath =
|
|
15128
|
+
var pkgPath = path22.resolve(__dirname, "..", "package.json");
|
|
14514
15129
|
var pkgVersion = (() => {
|
|
14515
15130
|
try {
|
|
14516
|
-
return JSON.parse(
|
|
15131
|
+
return JSON.parse(fs22.readFileSync(pkgPath, "utf-8")).version;
|
|
14517
15132
|
} catch {
|
|
14518
15133
|
return "2.7.1";
|
|
14519
15134
|
}
|
|
@@ -14742,8 +15357,8 @@ ${result.fixed}`;
|
|
|
14742
15357
|
return `Error: ${err.message}`;
|
|
14743
15358
|
}
|
|
14744
15359
|
}
|
|
14745
|
-
function toolWelcome() {
|
|
14746
|
-
return getWelcomeText();
|
|
15360
|
+
function toolWelcome(args2 = {}) {
|
|
15361
|
+
return getWelcomeText(args2);
|
|
14747
15362
|
}
|
|
14748
15363
|
function toolListFreeTools() {
|
|
14749
15364
|
return formatFreeToolsList();
|
|
@@ -14847,8 +15462,15 @@ async function toolClean(args2) {
|
|
|
14847
15462
|
var TOOLS = [
|
|
14848
15463
|
{
|
|
14849
15464
|
name: "hyv_welcome",
|
|
14850
|
-
description: "
|
|
14851
|
-
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
|
+
}
|
|
14852
15474
|
},
|
|
14853
15475
|
{
|
|
14854
15476
|
name: "hyv_list_free_tools",
|
|
@@ -14985,15 +15607,15 @@ var PROMPTS = [
|
|
|
14985
15607
|
];
|
|
14986
15608
|
var HYV_STATUS_PROMPT = `Use hold your voice MCP tools:
|
|
14987
15609
|
|
|
14988
|
-
1.
|
|
14989
|
-
2.
|
|
14990
|
-
3.
|
|
14991
|
-
4.
|
|
14992
|
-
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).
|
|
14993
15615
|
|
|
14994
15616
|
Keep the answer compact.`;
|
|
14995
15617
|
var TOOL_HANDLERS = {
|
|
14996
|
-
hyv_welcome:
|
|
15618
|
+
hyv_welcome: toolWelcome,
|
|
14997
15619
|
hyv_list_free_tools: () => toolListFreeTools(),
|
|
14998
15620
|
hyv_demo: () => toolDemo(),
|
|
14999
15621
|
hyv_rewrite: toolRewrite,
|
|
@@ -15087,7 +15709,7 @@ async function handleRequest(line) {
|
|
|
15087
15709
|
}
|
|
15088
15710
|
async function startMcpServer() {
|
|
15089
15711
|
const access = await getAccessState().catch(() => null);
|
|
15090
|
-
if (
|
|
15712
|
+
if (fs22.existsSync(VOICE_MD)) {
|
|
15091
15713
|
mcpLog("info", `voice profile: ${VOICE_MD}`);
|
|
15092
15714
|
} else {
|
|
15093
15715
|
mcpLog("info", "free local engine ready \u2014 no voice profile yet");
|
|
@@ -15119,142 +15741,142 @@ async function startMcpServer() {
|
|
|
15119
15741
|
}
|
|
15120
15742
|
|
|
15121
15743
|
// src/lib/mcp-setup.ts
|
|
15122
|
-
var
|
|
15123
|
-
var
|
|
15124
|
-
var
|
|
15125
|
-
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"));
|
|
15126
15748
|
init_version();
|
|
15127
|
-
var HOME2 =
|
|
15749
|
+
var HOME2 = os9.homedir();
|
|
15128
15750
|
var IS_WIN2 = process.platform === "win32";
|
|
15129
15751
|
function claudeDesktopConfigPath() {
|
|
15130
15752
|
if (IS_WIN2)
|
|
15131
|
-
return
|
|
15753
|
+
return path23.join(HOME2, "AppData", "Roaming", "Claude", "claude_desktop_config.json");
|
|
15132
15754
|
if (process.platform === "linux")
|
|
15133
|
-
return
|
|
15134
|
-
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");
|
|
15135
15757
|
}
|
|
15136
15758
|
function printMcpSetup() {
|
|
15137
|
-
console.log(
|
|
15138
|
-
console.log(
|
|
15759
|
+
console.log(import_chalk31.default.bold("\nhold your voice \u2014 mcp setup\n"));
|
|
15760
|
+
console.log(import_chalk31.default.dim(` ${getEngineLabel()}
|
|
15139
15761
|
`));
|
|
15140
|
-
console.log(
|
|
15141
|
-
console.log(
|
|
15142
|
-
console.log(
|
|
15143
|
-
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()}
|
|
15144
15766
|
`));
|
|
15145
|
-
console.log(
|
|
15146
|
-
console.log(
|
|
15147
|
-
console.log(
|
|
15148
|
-
console.log(
|
|
15149
|
-
console.log(
|
|
15150
|
-
console.log(
|
|
15151
|
-
console.log(
|
|
15152
|
-
console.log(
|
|
15153
|
-
console.log(
|
|
15154
|
-
console.log(
|
|
15155
|
-
console.log(
|
|
15156
|
-
console.log(
|
|
15157
|
-
console.log(
|
|
15158
|
-
console.log(
|
|
15159
|
-
console.log(
|
|
15160
|
-
console.log(
|
|
15161
|
-
console.log(
|
|
15162
|
-
console.log(
|
|
15163
|
-
console.log(
|
|
15164
|
-
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"));
|
|
15165
15787
|
}
|
|
15166
15788
|
async function runMcpSelfTest() {
|
|
15167
|
-
console.log(
|
|
15168
|
-
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()}
|
|
15169
15791
|
`));
|
|
15170
15792
|
let passed = 0;
|
|
15171
15793
|
let failed = 0;
|
|
15172
15794
|
const tools = getMcpToolNames();
|
|
15173
15795
|
if (tools.length >= 10) {
|
|
15174
|
-
console.log(
|
|
15796
|
+
console.log(import_chalk31.default.green(` \u2713 ${tools.length} MCP tools registered`));
|
|
15175
15797
|
passed++;
|
|
15176
15798
|
} else {
|
|
15177
|
-
console.log(
|
|
15799
|
+
console.log(import_chalk31.default.red(` \u2717 expected 10+ tools, got ${tools.length}`));
|
|
15178
15800
|
failed++;
|
|
15179
15801
|
}
|
|
15180
15802
|
const required = ["hyv_welcome", "hyv_scan", "hyv_analyze", "hyv_clean", "hyv_validate", "hyv_list_free_tools"];
|
|
15181
15803
|
for (const name of required) {
|
|
15182
15804
|
if (tools.includes(name)) {
|
|
15183
|
-
console.log(
|
|
15805
|
+
console.log(import_chalk31.default.green(` \u2713 tool: ${name}`));
|
|
15184
15806
|
passed++;
|
|
15185
15807
|
} else {
|
|
15186
|
-
console.log(
|
|
15808
|
+
console.log(import_chalk31.default.red(` \u2717 missing tool: ${name}`));
|
|
15187
15809
|
failed++;
|
|
15188
15810
|
}
|
|
15189
15811
|
}
|
|
15190
15812
|
try {
|
|
15191
15813
|
const welcome = await invokeMcpTool("hyv_welcome", {});
|
|
15192
15814
|
if (welcome.includes("Hold Your Voice") || welcome.includes("hold your voice")) {
|
|
15193
|
-
console.log(
|
|
15815
|
+
console.log(import_chalk31.default.green(" \u2713 hyv_welcome responds"));
|
|
15194
15816
|
passed++;
|
|
15195
15817
|
} else {
|
|
15196
|
-
console.log(
|
|
15818
|
+
console.log(import_chalk31.default.red(" \u2717 hyv_welcome unexpected output"));
|
|
15197
15819
|
failed++;
|
|
15198
15820
|
}
|
|
15199
15821
|
} catch (e) {
|
|
15200
|
-
console.log(
|
|
15822
|
+
console.log(import_chalk31.default.red(` \u2717 hyv_welcome failed: ${e.message}`));
|
|
15201
15823
|
failed++;
|
|
15202
15824
|
}
|
|
15203
15825
|
try {
|
|
15204
15826
|
const demo = await invokeMcpTool("hyv_demo", {});
|
|
15205
15827
|
if (demo.includes("Score") || demo.includes("issues")) {
|
|
15206
|
-
console.log(
|
|
15828
|
+
console.log(import_chalk31.default.green(" \u2713 hyv_demo pipeline works"));
|
|
15207
15829
|
passed++;
|
|
15208
15830
|
} else {
|
|
15209
|
-
console.log(
|
|
15831
|
+
console.log(import_chalk31.default.red(" \u2717 hyv_demo unexpected output"));
|
|
15210
15832
|
failed++;
|
|
15211
15833
|
}
|
|
15212
15834
|
} catch (e) {
|
|
15213
|
-
console.log(
|
|
15835
|
+
console.log(import_chalk31.default.red(` \u2717 hyv_demo failed: ${e.message}`));
|
|
15214
15836
|
failed++;
|
|
15215
15837
|
}
|
|
15216
|
-
const voiceMd =
|
|
15217
|
-
if (
|
|
15218
|
-
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"));
|
|
15219
15841
|
passed++;
|
|
15220
15842
|
} else {
|
|
15221
|
-
console.log(
|
|
15843
|
+
console.log(import_chalk31.default.dim(" - no voice.md (free local engine still works)"));
|
|
15222
15844
|
}
|
|
15223
15845
|
if (getDemoText().length > 100) {
|
|
15224
|
-
console.log(
|
|
15846
|
+
console.log(import_chalk31.default.green(" \u2713 demo content available"));
|
|
15225
15847
|
passed++;
|
|
15226
15848
|
} else {
|
|
15227
|
-
console.log(
|
|
15849
|
+
console.log(import_chalk31.default.red(" \u2717 demo content missing"));
|
|
15228
15850
|
failed++;
|
|
15229
15851
|
}
|
|
15230
15852
|
try {
|
|
15231
15853
|
const stdio = await testMcpStdioSubprocess();
|
|
15232
15854
|
if (stdio.ok && (stdio.toolCount || 0) >= 10) {
|
|
15233
|
-
console.log(
|
|
15855
|
+
console.log(import_chalk31.default.green(` \u2713 stdio MCP subprocess responds (${stdio.toolCount} tools)`));
|
|
15234
15856
|
passed++;
|
|
15235
15857
|
} else if (stdio.ok) {
|
|
15236
|
-
console.log(
|
|
15858
|
+
console.log(import_chalk31.default.yellow(` ! stdio MCP subprocess ok but only ${stdio.toolCount || 0} tools`));
|
|
15237
15859
|
failed++;
|
|
15238
15860
|
} else {
|
|
15239
|
-
console.log(
|
|
15861
|
+
console.log(import_chalk31.default.red(" \u2717 stdio MCP subprocess failed"));
|
|
15240
15862
|
failed++;
|
|
15241
15863
|
}
|
|
15242
15864
|
} catch (e) {
|
|
15243
|
-
console.log(
|
|
15865
|
+
console.log(import_chalk31.default.red(` \u2717 stdio MCP subprocess failed: ${e.message}`));
|
|
15244
15866
|
failed++;
|
|
15245
15867
|
}
|
|
15246
15868
|
console.log("");
|
|
15247
15869
|
if (failed === 0) {
|
|
15248
|
-
console.log(
|
|
15249
|
-
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"));
|
|
15250
15872
|
return true;
|
|
15251
15873
|
}
|
|
15252
|
-
console.log(
|
|
15874
|
+
console.log(import_chalk31.default.yellow(`! ${failed} check(s) failed, ${passed} passed`));
|
|
15253
15875
|
return false;
|
|
15254
15876
|
}
|
|
15255
15877
|
|
|
15256
15878
|
// src/commands/export.ts
|
|
15257
|
-
var
|
|
15879
|
+
var fs24 = __toESM(require("fs"));
|
|
15258
15880
|
init_api();
|
|
15259
15881
|
var FORMATS = {
|
|
15260
15882
|
claude: {
|
|
@@ -15341,7 +15963,7 @@ async function exportCommand(format, opts) {
|
|
|
15341
15963
|
const prompt = fmt.wrap(detail.profile, detail.body);
|
|
15342
15964
|
if (opts.output) {
|
|
15343
15965
|
const outFile = opts.output.replace("{name}", profile.slug || profile.name);
|
|
15344
|
-
|
|
15966
|
+
fs24.writeFileSync(outFile, prompt);
|
|
15345
15967
|
results.push({ profile: profile.name, file: outFile });
|
|
15346
15968
|
} else {
|
|
15347
15969
|
results.push({ profile: profile.name, prompt });
|
|
@@ -15378,13 +16000,13 @@ async function exportCommand(format, opts) {
|
|
|
15378
16000
|
// src/index.ts
|
|
15379
16001
|
init_access();
|
|
15380
16002
|
init_welcome();
|
|
15381
|
-
var
|
|
15382
|
-
var
|
|
16003
|
+
var fs25 = __toESM(require("fs"));
|
|
16004
|
+
var path24 = __toESM(require("path"));
|
|
15383
16005
|
var program2 = new Command();
|
|
15384
|
-
var pkgPath2 =
|
|
16006
|
+
var pkgPath2 = path24.resolve(__dirname, "..", "package.json");
|
|
15385
16007
|
var pkgVersion2 = (() => {
|
|
15386
16008
|
try {
|
|
15387
|
-
return JSON.parse(
|
|
16009
|
+
return JSON.parse(fs25.readFileSync(pkgPath2, "utf-8")).version;
|
|
15388
16010
|
} catch {
|
|
15389
16011
|
return "2.7.1";
|
|
15390
16012
|
}
|
|
@@ -15428,15 +16050,15 @@ program2.command("mcp").description("Start MCP server (for Claude Desktop and ot
|
|
|
15428
16050
|
return;
|
|
15429
16051
|
}
|
|
15430
16052
|
if (opts.setupChatgpt) {
|
|
15431
|
-
console.log(
|
|
16053
|
+
console.log(import_chalk32.default.bold("\nhold your voice \u2014 chatgpt setup\n"));
|
|
15432
16054
|
console.log("To connect HYV to ChatGPT:");
|
|
15433
|
-
console.log(
|
|
15434
|
-
console.log(
|
|
15435
|
-
console.log(
|
|
15436
|
-
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"));
|
|
15437
16059
|
console.log("");
|
|
15438
|
-
console.log(
|
|
15439
|
-
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."));
|
|
15440
16062
|
return;
|
|
15441
16063
|
}
|
|
15442
16064
|
startMcpServer();
|
|
@@ -15446,9 +16068,8 @@ program2.command("export").description("Export voice profile for LLMs").argument
|
|
|
15446
16068
|
});
|
|
15447
16069
|
var args = process.argv.slice(2);
|
|
15448
16070
|
if (args.length === 0) {
|
|
15449
|
-
|
|
15450
|
-
|
|
15451
|
-
if (firstRun)
|
|
16071
|
+
printWelcome({ condensed: true });
|
|
16072
|
+
if (!hasCompletedOnboarding())
|
|
15452
16073
|
markOnboardingComplete(pkgVersion2);
|
|
15453
16074
|
process.exit(0);
|
|
15454
16075
|
}
|