@holdyourvoice/hyv 2.9.10 → 2.9.12
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 +12 -0
- package/README.md +12 -0
- package/dist/index.js +563 -370
- package/package.json +3 -1
- package/scripts/install.ps1 +89 -0
- package/scripts/install.sh +155 -0
- package/scripts/postinstall-lib.js +163 -7
- package/scripts/postinstall.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holdyourvoice/hyv",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.12",
|
|
4
4
|
"description": "Free local AI writing scan for cursor & claude. MCP server, 220+ pattern detection, voice profiles. npx @holdyourvoice/hyv welcome",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -59,6 +59,8 @@
|
|
|
59
59
|
"dist/",
|
|
60
60
|
"scripts/postinstall.js",
|
|
61
61
|
"scripts/postinstall-lib.js",
|
|
62
|
+
"scripts/install.sh",
|
|
63
|
+
"scripts/install.ps1",
|
|
62
64
|
"scripts/check-no-duplicates.js",
|
|
63
65
|
"assets/",
|
|
64
66
|
"skills/",
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# hold your voice — install node (if needed) + @holdyourvoice/hyv
|
|
2
|
+
param(
|
|
3
|
+
[switch]$EnsureNodeOnly
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
$ErrorActionPreference = 'Stop'
|
|
7
|
+
|
|
8
|
+
$HyvPkg = '@holdyourvoice/hyv@latest'
|
|
9
|
+
$MinNodeMajor = 18
|
|
10
|
+
|
|
11
|
+
function Write-Log([string]$Message) { Write-Host " $Message" }
|
|
12
|
+
|
|
13
|
+
function Test-NodeOk {
|
|
14
|
+
if (-not (Get-Command node -ErrorAction SilentlyContinue)) { return $false }
|
|
15
|
+
$major = [int](node -p "parseInt(process.versions.node.split('.')[0], 10)")
|
|
16
|
+
return $major -ge $MinNodeMajor
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function Refresh-Path {
|
|
20
|
+
$machine = [Environment]::GetEnvironmentVariable('Path', 'Machine')
|
|
21
|
+
$user = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
22
|
+
if ($machine -or $user) {
|
|
23
|
+
$env:Path = @($machine, $user) -join ';'
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function Install-Node {
|
|
28
|
+
if (Get-Command winget -ErrorAction SilentlyContinue) {
|
|
29
|
+
Write-Log 'installing node via winget...'
|
|
30
|
+
winget install OpenJS.NodeJS.LTS --accept-package-agreements --accept-source-agreements
|
|
31
|
+
Refresh-Path
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
if (Get-Command choco -ErrorAction SilentlyContinue) {
|
|
35
|
+
Write-Log 'installing node via chocolatey...'
|
|
36
|
+
choco install nodejs-lts -y
|
|
37
|
+
Refresh-Path
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
$lts = '22.16.0'
|
|
41
|
+
$msi = "node-v$lts-x64.msi"
|
|
42
|
+
$url = "https://nodejs.org/dist/v$lts/$msi"
|
|
43
|
+
$tmp = Join-Path $env:TEMP "hyv-node-$msi"
|
|
44
|
+
Write-Log "downloading node $lts..."
|
|
45
|
+
Invoke-WebRequest -Uri $url -OutFile $tmp -UseBasicParsing
|
|
46
|
+
Write-Log 'installing node...'
|
|
47
|
+
Start-Process msiexec.exe -ArgumentList "/i `"$tmp`" /qn" -Wait
|
|
48
|
+
Refresh-Path
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function Ensure-Node {
|
|
52
|
+
Refresh-Path
|
|
53
|
+
if (Test-NodeOk) {
|
|
54
|
+
Write-Log "node $(node -v) found"
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
Write-Log "node $MinNodeMajor+ not found — installing automatically..."
|
|
58
|
+
Install-Node
|
|
59
|
+
Refresh-Path
|
|
60
|
+
if (-not (Test-NodeOk)) {
|
|
61
|
+
throw 'node install finished but node is still not on PATH — open a new terminal and run this script again'
|
|
62
|
+
}
|
|
63
|
+
Write-Log "node $(node -v) ready"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function Install-Hyv {
|
|
67
|
+
if (-not (Get-Command npm -ErrorAction SilentlyContinue)) {
|
|
68
|
+
throw 'npm not found after node install'
|
|
69
|
+
}
|
|
70
|
+
Write-Log 'installing hold your voice...'
|
|
71
|
+
npm i -g $HyvPkg
|
|
72
|
+
Refresh-Path
|
|
73
|
+
if (Get-Command hyv -ErrorAction SilentlyContinue) {
|
|
74
|
+
Write-Log 'running hyv welcome...'
|
|
75
|
+
hyv welcome
|
|
76
|
+
} else {
|
|
77
|
+
Write-Host ' ! hyv installed but not on PATH — open a new terminal and run: hyv welcome' -ForegroundColor Yellow
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Write-Host ''
|
|
82
|
+
Write-Host 'hold your voice — installer'
|
|
83
|
+
Write-Host ''
|
|
84
|
+
Ensure-Node
|
|
85
|
+
if ($EnsureNodeOnly) { exit 0 }
|
|
86
|
+
Install-Hyv
|
|
87
|
+
Write-Host ''
|
|
88
|
+
Write-Host ' ✓ done — hyv is ready'
|
|
89
|
+
Write-Host ''
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# hold your voice — install node (if needed) + @holdyourvoice/hyv
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
HYV_PKG="@holdyourvoice/hyv@latest"
|
|
6
|
+
MIN_NODE_MAJOR=18
|
|
7
|
+
NODE_LTS="22.16.0"
|
|
8
|
+
ENSURE_NODE_ONLY=0
|
|
9
|
+
|
|
10
|
+
for arg in "$@"; do
|
|
11
|
+
case "$arg" in
|
|
12
|
+
--ensure-node-only) ENSURE_NODE_ONLY=1 ;;
|
|
13
|
+
esac
|
|
14
|
+
done
|
|
15
|
+
|
|
16
|
+
log() { printf ' %s\n' "$*"; }
|
|
17
|
+
warn() { printf ' ! %s\n' "$*" >&2; }
|
|
18
|
+
|
|
19
|
+
node_major() {
|
|
20
|
+
node -p "parseInt(process.versions.node.split('.')[0], 10)" 2>/dev/null || echo 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
node_ok() {
|
|
24
|
+
command -v node >/dev/null 2>&1 || return 1
|
|
25
|
+
[[ "$(node_major)" -ge "$MIN_NODE_MAJOR" ]]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
refresh_path() {
|
|
29
|
+
export PATH="/usr/local/bin:/opt/homebrew/bin:${HOME}/.local/share/fnm:${PATH}"
|
|
30
|
+
hash -r 2>/dev/null || true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
install_node_fnm() {
|
|
34
|
+
export FNM_DIR="${FNM_DIR:-${HOME}/.local/share/fnm}"
|
|
35
|
+
export FNM_VERSION="${FNM_VERSION:-v1.38.1}"
|
|
36
|
+
mkdir -p "$FNM_DIR"
|
|
37
|
+
if ! command -v fnm >/dev/null 2>&1; then
|
|
38
|
+
log "installing fnm (user-level node manager)..."
|
|
39
|
+
curl -fsSL https://fnm.vercel.app/install | bash -s -- --install-no-use
|
|
40
|
+
fi
|
|
41
|
+
refresh_path
|
|
42
|
+
if ! command -v fnm >/dev/null 2>&1 && [[ -x "${FNM_DIR}/fnm" ]]; then
|
|
43
|
+
export PATH="${FNM_DIR}:${PATH}"
|
|
44
|
+
fi
|
|
45
|
+
command -v fnm >/dev/null 2>&1 || return 1
|
|
46
|
+
eval "$(fnm env)"
|
|
47
|
+
fnm install "$NODE_LTS"
|
|
48
|
+
fnm default "$NODE_LTS"
|
|
49
|
+
eval "$(fnm env)"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
install_node_mac() {
|
|
53
|
+
if command -v brew >/dev/null 2>&1; then
|
|
54
|
+
log "installing node via homebrew..."
|
|
55
|
+
brew install node
|
|
56
|
+
refresh_path
|
|
57
|
+
return 0
|
|
58
|
+
fi
|
|
59
|
+
if install_node_fnm; then
|
|
60
|
+
refresh_path
|
|
61
|
+
return 0
|
|
62
|
+
fi
|
|
63
|
+
local arch pkg
|
|
64
|
+
arch="$(uname -m)"
|
|
65
|
+
case "$arch" in
|
|
66
|
+
arm64) pkg="node-v${NODE_LTS}.pkg" ;;
|
|
67
|
+
x86_64) pkg="node-v${NODE_LTS}.pkg" ;;
|
|
68
|
+
*) warn "unsupported mac architecture: $arch"; return 1 ;;
|
|
69
|
+
esac
|
|
70
|
+
local url="https://nodejs.org/dist/v${NODE_LTS}/${pkg}"
|
|
71
|
+
local tmp="/tmp/hyv-node-v${NODE_LTS}.pkg"
|
|
72
|
+
log "downloading node ${NODE_LTS}..."
|
|
73
|
+
curl -fsSL "$url" -o "$tmp"
|
|
74
|
+
log "installing node (may ask for your password)..."
|
|
75
|
+
sudo installer -pkg "$tmp" -target /
|
|
76
|
+
refresh_path
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
install_node_linux() {
|
|
80
|
+
if install_node_fnm; then
|
|
81
|
+
refresh_path
|
|
82
|
+
return 0
|
|
83
|
+
fi
|
|
84
|
+
if command -v apt-get >/dev/null 2>&1; then
|
|
85
|
+
log "installing node via apt (nodesource)..."
|
|
86
|
+
curl -fsSL "https://deb.nodesource.com/setup_${NODE_LTS%%.*}.x" | sudo -E bash -
|
|
87
|
+
sudo apt-get install -y nodejs
|
|
88
|
+
refresh_path
|
|
89
|
+
return 0
|
|
90
|
+
fi
|
|
91
|
+
if command -v dnf >/dev/null 2>&1; then
|
|
92
|
+
log "installing node via dnf..."
|
|
93
|
+
sudo dnf install -y nodejs npm
|
|
94
|
+
refresh_path
|
|
95
|
+
return 0
|
|
96
|
+
fi
|
|
97
|
+
warn "could not auto-install node on this linux distro — install node ${MIN_NODE_MAJOR}+ manually"
|
|
98
|
+
return 1
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
install_node() {
|
|
102
|
+
case "$(uname -s)" in
|
|
103
|
+
Darwin) install_node_mac ;;
|
|
104
|
+
Linux) install_node_linux ;;
|
|
105
|
+
*)
|
|
106
|
+
warn "unsupported platform for auto node install"
|
|
107
|
+
return 1
|
|
108
|
+
;;
|
|
109
|
+
esac
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
ensure_node() {
|
|
113
|
+
refresh_path
|
|
114
|
+
if node_ok; then
|
|
115
|
+
log "node $(node -v) found"
|
|
116
|
+
return 0
|
|
117
|
+
fi
|
|
118
|
+
log "node ${MIN_NODE_MAJOR}+ not found — installing automatically..."
|
|
119
|
+
install_node
|
|
120
|
+
refresh_path
|
|
121
|
+
if node_ok; then
|
|
122
|
+
log "node $(node -v) ready"
|
|
123
|
+
return 0
|
|
124
|
+
fi
|
|
125
|
+
warn "node install finished but node is still not on PATH — open a new terminal and run this script again"
|
|
126
|
+
return 1
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
install_hyv() {
|
|
130
|
+
if ! command -v npm >/dev/null 2>&1; then
|
|
131
|
+
warn "npm not found after node install"
|
|
132
|
+
return 1
|
|
133
|
+
fi
|
|
134
|
+
log "installing hold your voice..."
|
|
135
|
+
npm i -g "$HYV_PKG"
|
|
136
|
+
refresh_path
|
|
137
|
+
if command -v hyv >/dev/null 2>&1; then
|
|
138
|
+
log "running hyv welcome..."
|
|
139
|
+
hyv welcome
|
|
140
|
+
else
|
|
141
|
+
warn "hyv installed but not on PATH — open a new terminal and run: hyv welcome"
|
|
142
|
+
fi
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
main() {
|
|
146
|
+
printf '\nhold your voice — installer\n\n'
|
|
147
|
+
ensure_node
|
|
148
|
+
if [[ "$ENSURE_NODE_ONLY" -eq 1 ]]; then
|
|
149
|
+
exit 0
|
|
150
|
+
fi
|
|
151
|
+
install_hyv
|
|
152
|
+
printf '\n ✓ done — hyv is ready\n\n'
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
main "$@"
|
|
@@ -83,16 +83,79 @@ function resolveHyvMcpCommand(pkgDir) {
|
|
|
83
83
|
return { command: 'hyv', args: ['mcp'] };
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
/** OpenCode local MCP uses a command array: [node, entry, mcp] */
|
|
87
|
+
function resolveHyvMcpCommandArray(pkgDir) {
|
|
88
|
+
const { command, args } = resolveHyvMcpCommand(pkgDir);
|
|
89
|
+
return [command, ...args];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parseJsonc(text) {
|
|
93
|
+
const noBlock = text.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
94
|
+
const noLine = noBlock.replace(/^\s*\/\/.*$/gm, '');
|
|
95
|
+
return JSON.parse(noLine);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function stringifyJsonc(config, originalText) {
|
|
99
|
+
const usesTrailingComma = /,\s*[}\]]/.test(originalText || '');
|
|
100
|
+
const space = (originalText || '').includes(': ') ? 2 : 2;
|
|
101
|
+
let out = JSON.stringify(config, null, space);
|
|
102
|
+
if (usesTrailingComma) {
|
|
103
|
+
out = out.replace(/,\n(\s*[}\]])/g, ',\n$1');
|
|
104
|
+
}
|
|
105
|
+
return out.endsWith('\n') ? out : `${out}\n`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function readJsonObjectFromFile(file, parser) {
|
|
109
|
+
if (!fs.existsSync(file)) return { config: {}, original: '' };
|
|
110
|
+
const original = fs.readFileSync(file, 'utf-8');
|
|
111
|
+
if (!original.trim()) return { config: {}, original };
|
|
112
|
+
try {
|
|
113
|
+
const parsed = parser(original);
|
|
114
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
115
|
+
return { ok: false, reason: 'parse-error', error: 'expected json object' };
|
|
116
|
+
}
|
|
117
|
+
return { config: parsed, original };
|
|
118
|
+
} catch (err) {
|
|
119
|
+
return { ok: false, reason: 'parse-error', error: err.message };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function mergeJsoncConfigFile(configFile, mutator, { backup = true } = {}) {
|
|
87
124
|
fs.mkdirSync(path.dirname(configFile), { recursive: true });
|
|
88
|
-
|
|
89
|
-
if (
|
|
125
|
+
const read = readJsonObjectFromFile(configFile, parseJsonc);
|
|
126
|
+
if (read.ok === false) return read;
|
|
127
|
+
let { config, original } = read;
|
|
128
|
+
const next = mutator(config) || config;
|
|
129
|
+
if (backup && fs.existsSync(configFile)) {
|
|
90
130
|
try {
|
|
91
|
-
|
|
92
|
-
} catch
|
|
93
|
-
|
|
131
|
+
fs.copyFileSync(configFile, `${configFile}.hyv.bak`);
|
|
132
|
+
} catch {
|
|
133
|
+
// non-fatal
|
|
94
134
|
}
|
|
95
135
|
}
|
|
136
|
+
fs.writeFileSync(configFile, original ? stringifyJsonc(next, original) : `${JSON.stringify(next, null, 2)}\n`);
|
|
137
|
+
return { ok: true };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function antigravityMcpConfigPath(home, isWin) {
|
|
141
|
+
return path.join(home, '.gemini', 'config', 'mcp_config.json');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function opencodeConfigDir(home, isWin) {
|
|
145
|
+
if (isWin) return path.join(home, '.config', 'opencode');
|
|
146
|
+
if (process.platform === 'linux') return path.join(home, '.config', 'opencode');
|
|
147
|
+
return path.join(home, '.config', 'opencode');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function chatgptHelperDir(home) {
|
|
151
|
+
return path.join(home, '.chatgpt');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function mergeJsonConfigFile(configFile, mutator, { backup = true } = {}) {
|
|
155
|
+
fs.mkdirSync(path.dirname(configFile), { recursive: true });
|
|
156
|
+
const read = readJsonObjectFromFile(configFile, (text) => JSON.parse(text));
|
|
157
|
+
if (read.ok === false) return read;
|
|
158
|
+
let { config, original } = read;
|
|
96
159
|
const next = mutator(config) || config;
|
|
97
160
|
if (backup && fs.existsSync(configFile)) {
|
|
98
161
|
try {
|
|
@@ -101,7 +164,7 @@ function mergeJsonConfigFile(configFile, mutator, { backup = true } = {}) {
|
|
|
101
164
|
// non-fatal
|
|
102
165
|
}
|
|
103
166
|
}
|
|
104
|
-
fs.writeFileSync(configFile, JSON.stringify(next, null, 2));
|
|
167
|
+
fs.writeFileSync(configFile, `${JSON.stringify(next, null, 2)}\n`);
|
|
105
168
|
return { ok: true };
|
|
106
169
|
}
|
|
107
170
|
|
|
@@ -274,6 +337,93 @@ function setupAgents({ pkgDir, home = require('os').homedir(), quiet = false })
|
|
|
274
337
|
warnings.push(`command code: ${err.message}`);
|
|
275
338
|
}
|
|
276
339
|
|
|
340
|
+
// Antigravity — ~/.gemini/config/mcp_config.json (absolute paths for GUI launch)
|
|
341
|
+
try {
|
|
342
|
+
const agFile = antigravityMcpConfigPath(home, isWin);
|
|
343
|
+
const mcpCmd = resolveHyvMcpCommand(pkgDir);
|
|
344
|
+
const result = mergeJsonConfigFile(agFile, (config) => {
|
|
345
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
346
|
+
if (!config.mcpServers.hyv) {
|
|
347
|
+
config.mcpServers.hyv = { command: mcpCmd.command, args: mcpCmd.args };
|
|
348
|
+
configured.push('antigravity mcp');
|
|
349
|
+
}
|
|
350
|
+
return config;
|
|
351
|
+
});
|
|
352
|
+
if (!result.ok) warnings.push(`antigravity: could not update mcp config (${result.reason})`);
|
|
353
|
+
} catch (err) {
|
|
354
|
+
warnings.push(`antigravity: ${err.message}`);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// OpenCode — ~/.config/opencode/opencode.jsonc + AGENTS.md
|
|
358
|
+
try {
|
|
359
|
+
const ocDir = opencodeConfigDir(home, isWin);
|
|
360
|
+
const ocFile = path.join(ocDir, 'opencode.jsonc');
|
|
361
|
+
const cmdArr = resolveHyvMcpCommandArray(pkgDir);
|
|
362
|
+
const ocResult = mergeJsoncConfigFile(ocFile, (config) => {
|
|
363
|
+
if (!config.mcp) config.mcp = {};
|
|
364
|
+
if (!config.mcp.hyv) {
|
|
365
|
+
config.mcp.hyv = { type: 'local', command: cmdArr, enabled: true };
|
|
366
|
+
configured.push('opencode mcp');
|
|
367
|
+
}
|
|
368
|
+
return config;
|
|
369
|
+
});
|
|
370
|
+
if (!ocResult.ok) warnings.push(`opencode: could not update opencode.jsonc (${ocResult.reason})`);
|
|
371
|
+
|
|
372
|
+
const agentsFile = path.join(ocDir, 'AGENTS.md');
|
|
373
|
+
const genericSrc = path.join(pkgDir, 'agents', 'generic.md');
|
|
374
|
+
let existing = '';
|
|
375
|
+
if (fs.existsSync(agentsFile)) existing = fs.readFileSync(agentsFile, 'utf-8');
|
|
376
|
+
const addition = fs.existsSync(genericSrc) ? fs.readFileSync(genericSrc, 'utf-8') : '';
|
|
377
|
+
if (addition && shouldUpgradeAgent(agentsFile, agentsMarker, pkgVersion)) {
|
|
378
|
+
const merged = mergeAgentsMd(existing, addition, 'hold-your-voice');
|
|
379
|
+
fs.mkdirSync(ocDir, { recursive: true });
|
|
380
|
+
fs.writeFileSync(agentsFile, merged);
|
|
381
|
+
configured.push('opencode agents');
|
|
382
|
+
} else if (!fs.existsSync(agentsFile) && addition) {
|
|
383
|
+
fs.mkdirSync(ocDir, { recursive: true });
|
|
384
|
+
fs.writeFileSync(agentsFile, `${addition.trim()}\n`);
|
|
385
|
+
configured.push('opencode agents');
|
|
386
|
+
}
|
|
387
|
+
} catch (err) {
|
|
388
|
+
warnings.push(`opencode: ${err.message}`);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ChatGPT — connector helper (UI has no config file; write exact values for manual add)
|
|
392
|
+
try {
|
|
393
|
+
const cgDir = chatgptHelperDir(home);
|
|
394
|
+
fs.mkdirSync(cgDir, { recursive: true });
|
|
395
|
+
const mcpCmd = resolveHyvMcpCommand(pkgDir);
|
|
396
|
+
const connectorGuide = [
|
|
397
|
+
'hold your voice — chatgpt desktop mcp connector',
|
|
398
|
+
'',
|
|
399
|
+
'chatgpt has no auto-config file. add this connector once in the chatgpt desktop app:',
|
|
400
|
+
'',
|
|
401
|
+
'1. open chatgpt desktop (not the browser)',
|
|
402
|
+
'2. settings → connectors → add connector',
|
|
403
|
+
'3. name: hold your voice',
|
|
404
|
+
'4. command: hyv',
|
|
405
|
+
'5. arguments: mcp',
|
|
406
|
+
'',
|
|
407
|
+
'if the connector fails to start, use absolute paths instead:',
|
|
408
|
+
` command: ${mcpCmd.command}`,
|
|
409
|
+
` arguments: ${mcpCmd.args.join(' ')}`,
|
|
410
|
+
'',
|
|
411
|
+
'then restart chatgpt desktop and ask: scan this with hold your voice',
|
|
412
|
+
'',
|
|
413
|
+
'more help: hyv mcp --setup-chatgpt',
|
|
414
|
+
'',
|
|
415
|
+
].join('\n');
|
|
416
|
+
const guideFile = path.join(cgDir, 'hyv-mcp-connector.txt');
|
|
417
|
+
fs.writeFileSync(guideFile, connectorGuide);
|
|
418
|
+
configured.push('chatgpt connector guide');
|
|
419
|
+
|
|
420
|
+
const chatgptSrc = path.join(pkgDir, 'agents', 'chatgpt.md');
|
|
421
|
+
const instrFile = path.join(cgDir, 'hyv-instructions.txt');
|
|
422
|
+
if (fs.existsSync(chatgptSrc)) installAgent(chatgptSrc, instrFile);
|
|
423
|
+
} catch (err) {
|
|
424
|
+
warnings.push(`chatgpt: ${err.message}`);
|
|
425
|
+
}
|
|
426
|
+
|
|
277
427
|
// Generic reference copy
|
|
278
428
|
try {
|
|
279
429
|
const genericSrc = path.join(pkgDir, 'agents', 'generic.md');
|
|
@@ -300,9 +450,15 @@ module.exports = {
|
|
|
300
450
|
hasCompletedOnboarding,
|
|
301
451
|
markOnboardingComplete,
|
|
302
452
|
resolveHyvMcpCommand,
|
|
453
|
+
resolveHyvMcpCommandArray,
|
|
454
|
+
parseJsonc,
|
|
303
455
|
mergeJsonConfigFile,
|
|
456
|
+
mergeJsoncConfigFile,
|
|
304
457
|
toCursorMdc,
|
|
305
458
|
toWindsurfRule,
|
|
306
459
|
setupAgents,
|
|
307
460
|
claudeDesktopDir,
|
|
461
|
+
antigravityMcpConfigPath,
|
|
462
|
+
opencodeConfigDir,
|
|
463
|
+
chatgptHelperDir,
|
|
308
464
|
};
|
package/scripts/postinstall.js
CHANGED
|
@@ -19,6 +19,7 @@ function print(msg) {
|
|
|
19
19
|
print('');
|
|
20
20
|
print(' ✓ hold your voice installed — free local scan ready');
|
|
21
21
|
print(' next: hyv welcome | hyv scan draft.md | hyv mcp --setup');
|
|
22
|
+
print(' no node yet? curl -fsSL https://holdyourvoice.com/install.sh | bash');
|
|
22
23
|
print('');
|
|
23
24
|
|
|
24
25
|
const { configured, warnings } = setupAgents({ pkgDir, quiet });
|