agentvibes 4.6.3 → 4.6.6
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/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/audio-processor.sh +433 -433
- package/.claude/hooks/play-tts-macos.sh +368 -368
- package/.claude/hooks/play-tts-piper.sh +679 -679
- package/.claude/hooks/play-tts-soprano.sh +356 -356
- package/.clawdbot/skill/SKILL.md +237 -241
- package/CLAUDE.md +41 -0
- package/README.md +2162 -2121
- package/RELEASE_NOTES.md +60 -0
- package/bin/agentvibes.js +15 -0
- package/package.json +1 -1
- package/src/console/app.js +41 -19
- package/src/console/tabs/agents-tab.js +5 -5
- package/src/console/tabs/help-tab.js +1 -1
- package/src/console/tabs/install-tab.js +5 -8
- package/src/console/tabs/music-tab.js +1 -1
- package/src/console/tabs/readme-tab.js +4 -1
- package/src/console/tabs/receiver-tab.js +1 -1
- package/src/console/tabs/settings-tab.js +93 -106
- package/src/console/tabs/voices-tab.js +1 -1
- package/src/installer.js +76 -27
- package/.claude/config/background-music-enabled.txt +0 -1
|
@@ -751,7 +751,7 @@ export function createSettingsTab(screen, services) {
|
|
|
751
751
|
|
|
752
752
|
const box = blessed.box({
|
|
753
753
|
parent: screen,
|
|
754
|
-
top:
|
|
754
|
+
top: 5, // Below header (rows 0-3) + tab bar (row 4)
|
|
755
755
|
left: 0,
|
|
756
756
|
width: '100%',
|
|
757
757
|
bottom: 2, // Above context footer + GitHub footer
|
|
@@ -1760,37 +1760,57 @@ export function createSettingsTab(screen, services) {
|
|
|
1760
1760
|
style: { fg: COLORS.valueFg, bg: COLORS.contentBg },
|
|
1761
1761
|
});
|
|
1762
1762
|
|
|
1763
|
-
|
|
1763
|
+
// Visible list height — cap at 10 rows (fits standard 24-row terminals with headers)
|
|
1764
|
+
const LANG_LIST_HEIGHT = Math.min(SUPPORTED_LANGUAGES.length, 10);
|
|
1764
1765
|
|
|
1765
|
-
|
|
1766
|
+
// blessed.list: natively focusable, handles selection highlight, scrolling.
|
|
1767
|
+
// keys:true needed so keypress events propagate to the element (keyable=true).
|
|
1768
|
+
// We immediately removeAllListeners('keypress') to strip blessed's built-in up/down nav —
|
|
1769
|
+
// our manual .key() handlers (registered on 'key down'/'key up') are the sole navigators.
|
|
1770
|
+
const languageList = blessed.list({
|
|
1766
1771
|
parent: box,
|
|
1767
1772
|
top: 7,
|
|
1768
1773
|
left: 4,
|
|
1769
|
-
width:
|
|
1770
|
-
height:
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
+
width: 44,
|
|
1775
|
+
height: LANG_LIST_HEIGHT + 2, // +2 for border
|
|
1776
|
+
keys: true,
|
|
1777
|
+
mouse: true,
|
|
1778
|
+
tags: false,
|
|
1779
|
+
items: SUPPORTED_LANGUAGES.map(l => l.name),
|
|
1780
|
+
style: {
|
|
1781
|
+
selected: { bg: 'green', fg: 'white', bold: true },
|
|
1782
|
+
item: { fg: 'white' },
|
|
1783
|
+
border: { fg: 'cyan' },
|
|
1784
|
+
focus: { border: { fg: 'yellow' } },
|
|
1785
|
+
},
|
|
1786
|
+
border: { type: 'line' },
|
|
1787
|
+
scrollable: true,
|
|
1788
|
+
scrollbar: { style: { bg: 'blue' } },
|
|
1774
1789
|
});
|
|
1790
|
+
// Strip blessed's built-in keypress nav (up/down/j/k/etc.) — our .key() handlers take over.
|
|
1791
|
+
// .key() registers on 'key <name>' events (not 'keypress'), so they survive this removal.
|
|
1792
|
+
languageList.removeAllListeners('keypress');
|
|
1775
1793
|
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1794
|
+
// Hint shown below the list
|
|
1795
|
+
const langHint = blessed.text({
|
|
1796
|
+
parent: box,
|
|
1797
|
+
top: 7 + LANG_LIST_HEIGHT + 3,
|
|
1798
|
+
left: 4,
|
|
1799
|
+
content: '{gray-fg}↑↓ navigate · Enter to apply{/gray-fg}',
|
|
1800
|
+
tags: true,
|
|
1801
|
+
style: { bg: COLORS.contentBg },
|
|
1802
|
+
});
|
|
1784
1803
|
|
|
1804
|
+
// Apply button — kept for mouse users; keyboard users just press Enter on the list
|
|
1785
1805
|
const langApplyBtn = _createButton(box, screen, '✓ Apply Language', COLORS, () => {
|
|
1786
|
-
const selected = SUPPORTED_LANGUAGES[
|
|
1806
|
+
const selected = SUPPORTED_LANGUAGES[languageList.selected ?? 0];
|
|
1787
1807
|
if (selected && services.languageService) {
|
|
1788
1808
|
services.languageService.setLang(selected.value);
|
|
1789
1809
|
refreshLanguageDisplay();
|
|
1790
1810
|
_showNotice(screen, `Language: ${selected.name}`);
|
|
1791
1811
|
}
|
|
1792
1812
|
}, { bg: '#2e7d32' });
|
|
1793
|
-
langApplyBtn.top = 7 +
|
|
1813
|
+
langApplyBtn.top = 7 + LANG_LIST_HEIGHT + 5;
|
|
1794
1814
|
langApplyBtn.left = 4;
|
|
1795
1815
|
|
|
1796
1816
|
function refreshLanguageDisplay() {
|
|
@@ -1798,19 +1818,11 @@ export function createSettingsTab(screen, services) {
|
|
|
1798
1818
|
const found = SUPPORTED_LANGUAGES.find(l => l.value === currentLang);
|
|
1799
1819
|
languageCurrentValue.setContent(found ? found.name : currentLang);
|
|
1800
1820
|
const idx = SUPPORTED_LANGUAGES.findIndex(l => l.value === currentLang);
|
|
1801
|
-
if (idx >= 0)
|
|
1802
|
-
_renderLangList();
|
|
1821
|
+
if (idx >= 0) languageList.select(idx);
|
|
1803
1822
|
screen.render();
|
|
1804
1823
|
}
|
|
1805
1824
|
|
|
1806
|
-
// Key navigation
|
|
1807
|
-
box.key(['up', 'down'], (ch, key) => {
|
|
1808
|
-
if (_activeSubTab !== 'language') return;
|
|
1809
|
-
if (key.name === 'up') _langListIdx = Math.max(0, _langListIdx - 1);
|
|
1810
|
-
if (key.name === 'down') _langListIdx = Math.min(SUPPORTED_LANGUAGES.length - 1, _langListIdx + 1);
|
|
1811
|
-
_renderLangList();
|
|
1812
|
-
screen.render();
|
|
1813
|
-
});
|
|
1825
|
+
// Key navigation wired after _navigateRow is defined (see below)
|
|
1814
1826
|
|
|
1815
1827
|
// -------------------------------------------------------------------------
|
|
1816
1828
|
// Display state + button-level focus navigation (story 7.6)
|
|
@@ -1847,8 +1859,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1847
1859
|
language: [
|
|
1848
1860
|
languageSectionHeader,
|
|
1849
1861
|
languageCurrentLabel, languageCurrentValue,
|
|
1850
|
-
languageList,
|
|
1851
|
-
langApplyBtn,
|
|
1862
|
+
languageList, langHint, langApplyBtn,
|
|
1852
1863
|
],
|
|
1853
1864
|
};
|
|
1854
1865
|
|
|
@@ -1858,7 +1869,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1858
1869
|
effects: [[reverbChangeBtn, reverbTestBtn], [trackChangeBtn, musicToggleBtn, musicTestBtn], [volumeChangeBtn]],
|
|
1859
1870
|
personality: [[verbosityChangeBtn], [personalityChangeBtn, personalityTestBtn], [introEditBtn, introClearBtn]],
|
|
1860
1871
|
output: [[audioDstChangeBtn], [audioSshEditBtn, audioStreamModeBtn]],
|
|
1861
|
-
language: [[langApplyBtn]],
|
|
1872
|
+
language: [[languageList], [langApplyBtn]],
|
|
1862
1873
|
};
|
|
1863
1874
|
|
|
1864
1875
|
const _subTabItemsArray = SUB_TABS.map(id => _subTabItemsMap[id]);
|
|
@@ -1906,7 +1917,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1906
1917
|
|
|
1907
1918
|
const _buttons = [
|
|
1908
1919
|
_subTabItemsMap.voice, _subTabItemsMap.effects,
|
|
1909
|
-
_subTabItemsMap.personality, _subTabItemsMap.output,
|
|
1920
|
+
_subTabItemsMap.personality, _subTabItemsMap.output, _subTabItemsMap.language,
|
|
1910
1921
|
switchBtn, changeBtn, playBtn,
|
|
1911
1922
|
reverbChangeBtn, reverbTestBtn,
|
|
1912
1923
|
trackChangeBtn, musicToggleBtn, musicTestBtn,
|
|
@@ -1914,6 +1925,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1914
1925
|
verbosityChangeBtn, personalityChangeBtn, personalityTestBtn,
|
|
1915
1926
|
introEditBtn, introClearBtn,
|
|
1916
1927
|
audioDstChangeBtn, audioSshEditBtn, audioStreamModeBtn,
|
|
1928
|
+
languageList, langApplyBtn,
|
|
1917
1929
|
fullPreviewBtn,
|
|
1918
1930
|
saveGloballyBtn, saveLocallyBtn, cancelChangesBtn,
|
|
1919
1931
|
];
|
|
@@ -2030,74 +2042,6 @@ export function createSettingsTab(screen, services) {
|
|
|
2030
2042
|
return;
|
|
2031
2043
|
}
|
|
2032
2044
|
|
|
2033
|
-
// _rows layout: [0]=sub-tab bar, [1..lastContent]=per-tab rows, then 3 shared bottom rows
|
|
2034
|
-
const BOTTOM_ROWS = 2; // [fullPreviewBtn], [saveGlobally+saveLocally+cancelChanges]
|
|
2035
|
-
const lastContentIdx = _rows.length - BOTTOM_ROWS - 1;
|
|
2036
|
-
|
|
2037
|
-
// Cross-tab forward: ↓ from the effective last visible content row → jump to next sub-tab
|
|
2038
|
-
if (delta > 0 && rowIdx >= 1 && rowIdx <= lastContentIdx) {
|
|
2039
|
-
let isEffectiveLast = true;
|
|
2040
|
-
for (let r = rowIdx + 1; r <= lastContentIdx; r++) {
|
|
2041
|
-
if (_isRowVisible(_rows[r])) { isEffectiveLast = false; break; }
|
|
2042
|
-
}
|
|
2043
|
-
if (isEffectiveLast) {
|
|
2044
|
-
const tabIdx = SUB_TABS.indexOf(_activeSubTab);
|
|
2045
|
-
if (tabIdx < SUB_TABS.length - 1) {
|
|
2046
|
-
const nextTab = SUB_TABS[tabIdx + 1];
|
|
2047
|
-
_showSubTab(nextTab, true);
|
|
2048
|
-
const firstRow = _rowsBySubTab[nextTab].find(row => _isRowVisible(row));
|
|
2049
|
-
if (firstRow) {
|
|
2050
|
-
const btn = _firstVisibleBtn(firstRow);
|
|
2051
|
-
_currentIdx = _buttons.indexOf(btn);
|
|
2052
|
-
_focusButton(btn);
|
|
2053
|
-
return;
|
|
2054
|
-
}
|
|
2055
|
-
}
|
|
2056
|
-
// On the last sub-tab: fall through to normal (go to fullPreviewBtn)
|
|
2057
|
-
}
|
|
2058
|
-
}
|
|
2059
|
-
|
|
2060
|
-
// Cross-tab backward: ↑ from first content row (row 1) → jump to previous sub-tab's last row
|
|
2061
|
-
if (delta < 0 && rowIdx === 1) {
|
|
2062
|
-
const tabIdx = SUB_TABS.indexOf(_activeSubTab);
|
|
2063
|
-
if (tabIdx > 0) {
|
|
2064
|
-
const prevTab = SUB_TABS[tabIdx - 1];
|
|
2065
|
-
_showSubTab(prevTab, true);
|
|
2066
|
-
const prevRows = _rowsBySubTab[prevTab];
|
|
2067
|
-
let lastRow = null;
|
|
2068
|
-
for (let i = prevRows.length - 1; i >= 0; i--) {
|
|
2069
|
-
if (_isRowVisible(prevRows[i])) { lastRow = prevRows[i]; break; }
|
|
2070
|
-
}
|
|
2071
|
-
if (lastRow) {
|
|
2072
|
-
const btn = _firstVisibleBtn(lastRow);
|
|
2073
|
-
_currentIdx = _buttons.indexOf(btn);
|
|
2074
|
-
_focusButton(btn);
|
|
2075
|
-
return;
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
// First sub-tab: fall through (goes to sub-tab bar at row 0)
|
|
2079
|
-
}
|
|
2080
|
-
|
|
2081
|
-
// In the bottom save row: ↓ navigates to the next visible sibling within the row
|
|
2082
|
-
// rather than wrapping to row 0 (sub-tab bar) via modulo.
|
|
2083
|
-
const lastRowIdx = _rows.length - 1;
|
|
2084
|
-
if (delta > 0 && rowIdx === lastRowIdx) {
|
|
2085
|
-
const row = _rows[lastRowIdx];
|
|
2086
|
-
const posInRow = row.indexOf(focused);
|
|
2087
|
-
for (let i = posInRow + 1; i < row.length; i++) {
|
|
2088
|
-
if (!row[i].hidden) {
|
|
2089
|
-
_currentIdx = _buttons.indexOf(row[i]);
|
|
2090
|
-
_focusButton(row[i]);
|
|
2091
|
-
return;
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
// Last sibling in the row — wrap up to sub-tab bar (row 0)
|
|
2095
|
-
const topBtn = _firstVisibleBtn(_rows[0]);
|
|
2096
|
-
_currentIdx = _buttons.indexOf(topBtn);
|
|
2097
|
-
_focusButton(topBtn);
|
|
2098
|
-
return;
|
|
2099
|
-
}
|
|
2100
|
-
|
|
2101
2045
|
// Skip rows where ALL buttons are hidden (e.g. SSH alias row when destination is local).
|
|
2102
2046
|
// Use _firstVisibleBtn so we land on the first visible button in a mixed row.
|
|
2103
2047
|
let attempts = 0;
|
|
@@ -2105,17 +2049,56 @@ export function createSettingsTab(screen, services) {
|
|
|
2105
2049
|
rowIdx = (rowIdx + delta + _rows.length) % _rows.length;
|
|
2106
2050
|
attempts++;
|
|
2107
2051
|
} while (!_isRowVisible(_rows[rowIdx]) && attempts < _rows.length);
|
|
2108
|
-
|
|
2052
|
+
// When landing on the sub-tab bar (row 0), focus the ACTIVE sub-tab item, not the first one
|
|
2053
|
+
const btn = rowIdx === 0
|
|
2054
|
+
? (_subTabItemsMap[_activeSubTab] ?? _firstVisibleBtn(_rows[0]))
|
|
2055
|
+
: _firstVisibleBtn(_rows[rowIdx]);
|
|
2109
2056
|
_currentIdx = _buttons.indexOf(btn);
|
|
2110
2057
|
_focusButton(btn);
|
|
2111
2058
|
}
|
|
2112
2059
|
|
|
2113
2060
|
for (const btn of _buttons) {
|
|
2114
|
-
btn.key(['down'],
|
|
2115
|
-
|
|
2061
|
+
btn.key(['down'], () => {
|
|
2062
|
+
if (btn === languageList) return; // languageList has its own boundary-aware down handler
|
|
2063
|
+
_navigateRow(1);
|
|
2064
|
+
});
|
|
2065
|
+
btn.key(['up'], () => {
|
|
2066
|
+
if (btn === languageList) return; // languageList has its own boundary-aware up handler
|
|
2067
|
+
_navigateRow(-1);
|
|
2068
|
+
});
|
|
2116
2069
|
btn.key(['escape'], () => { if (typeof focusMainTabBar === 'function') setTimeout(() => focusMainTabBar(), 0); });
|
|
2117
2070
|
}
|
|
2118
2071
|
|
|
2072
|
+
// Language list — fully manual navigation (keys:false on the list disables blessed's built-in
|
|
2073
|
+
// so only our handlers run, giving us clean boundary detection without double-move issues).
|
|
2074
|
+
languageList.key(['down'], () => {
|
|
2075
|
+
const cur = languageList.selected ?? 0;
|
|
2076
|
+
if (cur >= SUPPORTED_LANGUAGES.length - 1) {
|
|
2077
|
+
_navigateRow(1); // past last item → Apply button
|
|
2078
|
+
} else {
|
|
2079
|
+
languageList.select(cur + 1);
|
|
2080
|
+
screen.render();
|
|
2081
|
+
}
|
|
2082
|
+
});
|
|
2083
|
+
languageList.key(['up'], () => {
|
|
2084
|
+
const cur = languageList.selected ?? 0;
|
|
2085
|
+
if (cur <= 0) {
|
|
2086
|
+
_navigateRow(-1); // past first item → sub-tab bar
|
|
2087
|
+
} else {
|
|
2088
|
+
languageList.select(cur - 1);
|
|
2089
|
+
screen.render();
|
|
2090
|
+
}
|
|
2091
|
+
});
|
|
2092
|
+
languageList.key(['enter', 'return', 'space'], () => {
|
|
2093
|
+
const selected = SUPPORTED_LANGUAGES[languageList.selected ?? 0];
|
|
2094
|
+
if (selected && services.languageService) {
|
|
2095
|
+
services.languageService.setLang(selected.value);
|
|
2096
|
+
refreshLanguageDisplay();
|
|
2097
|
+
_showNotice(screen, `Language: ${selected.name}`);
|
|
2098
|
+
}
|
|
2099
|
+
});
|
|
2100
|
+
languageList.key(['escape'], () => { if (typeof focusMainTabBar === 'function') setTimeout(() => focusMainTabBar(), 0); });
|
|
2101
|
+
|
|
2119
2102
|
// ← / → within content rows — uses _buttonGroups (static); sub-tab bar has its own wiring
|
|
2120
2103
|
const _rows = []; // populated dynamically by _showSubTab()
|
|
2121
2104
|
|
|
@@ -2129,8 +2112,9 @@ export function createSettingsTab(screen, services) {
|
|
|
2129
2112
|
[introEditBtn, introClearBtn],
|
|
2130
2113
|
[audioDstChangeBtn],
|
|
2131
2114
|
[audioSshEditBtn, audioStreamModeBtn],
|
|
2132
|
-
[
|
|
2133
|
-
[
|
|
2115
|
+
[languageList],
|
|
2116
|
+
[langApplyBtn],
|
|
2117
|
+
[fullPreviewBtn, saveGloballyBtn, saveLocallyBtn, cancelChangesBtn],
|
|
2134
2118
|
];
|
|
2135
2119
|
|
|
2136
2120
|
for (const row of _buttonGroups) {
|
|
@@ -2425,9 +2409,12 @@ export function createSettingsTab(screen, services) {
|
|
|
2425
2409
|
},
|
|
2426
2410
|
|
|
2427
2411
|
onFocus() {
|
|
2412
|
+
// Land on the active sub-tab bar item so the user can ↑↓ from there.
|
|
2428
2413
|
// Use _focusButton (not raw .focus()) so olines get invalidated before render,
|
|
2429
2414
|
// preventing the ghost-duplicate-row artifact on initial tab activation.
|
|
2430
|
-
|
|
2415
|
+
const activeSubTabItem = _subTabItemsMap[_activeSubTab];
|
|
2416
|
+
_currentIdx = _buttons.indexOf(activeSubTabItem);
|
|
2417
|
+
_focusButton(activeSubTabItem);
|
|
2431
2418
|
},
|
|
2432
2419
|
|
|
2433
2420
|
onBlur() {
|
package/src/installer.js
CHANGED
|
@@ -2669,25 +2669,54 @@ function showWelcome() {
|
|
|
2669
2669
|
|
|
2670
2670
|
/**
|
|
2671
2671
|
* Display latest release information box
|
|
2672
|
-
*
|
|
2672
|
+
* Reads the first section from RELEASE_NOTES.md so it's always current.
|
|
2673
|
+
* Falls back to a minimal static string if the file is missing.
|
|
2673
2674
|
*/
|
|
2674
2675
|
function getReleaseInfoBoxen() {
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2676
|
+
try {
|
|
2677
|
+
const notesPath = path.join(__dirname, '..', 'RELEASE_NOTES.md');
|
|
2678
|
+
const raw = fsSync.readFileSync(notesPath, 'utf8');
|
|
2679
|
+
const lines = raw.split('\n');
|
|
2680
|
+
|
|
2681
|
+
// Find the first ## heading (latest release section)
|
|
2682
|
+
const startIdx = lines.findIndex(l => l.startsWith('## '));
|
|
2683
|
+
if (startIdx < 0) return '';
|
|
2684
|
+
|
|
2685
|
+
// Collect lines until the next ## heading (or end of file)
|
|
2686
|
+
const sectionLines = [];
|
|
2687
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
2688
|
+
if (i !== startIdx && lines[i].startsWith('## ')) break;
|
|
2689
|
+
sectionLines.push(lines[i]);
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
// Strip markdown syntax from a line for plain display
|
|
2693
|
+
const stripMd = (s) => s
|
|
2694
|
+
.replace(/^#{1,6}\s*/, '') // ## headings
|
|
2695
|
+
.replace(/\*\*([^*]+)\*\*/g, '$1') // **bold**
|
|
2696
|
+
.replace(/`([^`]+)`/g, '$1') // `code`
|
|
2697
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1'); // [text](url)
|
|
2698
|
+
|
|
2699
|
+
// Render: heading in cyan, bullets as gray, skip blank/hr lines at end
|
|
2700
|
+
return sectionLines
|
|
2701
|
+
.map((line, i) => {
|
|
2702
|
+
if (i === 0) return chalk.cyan.bold(stripMd(line));
|
|
2703
|
+
if (line.startsWith('### ')) return chalk.green.bold(stripMd(line));
|
|
2704
|
+
if (line.startsWith('- ')) return chalk.gray(' ' + stripMd(line));
|
|
2705
|
+
if (line === '---') return '';
|
|
2706
|
+
return chalk.gray(stripMd(line));
|
|
2707
|
+
})
|
|
2708
|
+
.join('\n')
|
|
2709
|
+
.trimEnd() + '\n\n' +
|
|
2710
|
+
chalk.gray('📖 Full Release Notes: RELEASE_NOTES.md\n') +
|
|
2711
|
+
chalk.gray('🌐 Website: https://agentvibes.org\n') +
|
|
2712
|
+
chalk.gray('📦 Repository: https://github.com/paulpreibisch/AgentVibes\n\n') +
|
|
2713
|
+
chalk.gray('Co-created by Paul Preibisch with Claude AI\n') +
|
|
2714
|
+
chalk.gray('Copyright © 2026 Paul Preibisch | Apache-2.0 License');
|
|
2715
|
+
} catch {
|
|
2716
|
+
return chalk.cyan.bold(`📦 AgentVibes v${VERSION}\n`) +
|
|
2717
|
+
chalk.gray('📖 Full Release Notes: RELEASE_NOTES.md\n') +
|
|
2718
|
+
chalk.gray('🌐 Website: https://agentvibes.org');
|
|
2719
|
+
}
|
|
2691
2720
|
}
|
|
2692
2721
|
|
|
2693
2722
|
/**
|
|
@@ -4126,6 +4155,12 @@ async function checkAndInstallPiperWindows(targetDir, options) {
|
|
|
4126
4155
|
return;
|
|
4127
4156
|
}
|
|
4128
4157
|
|
|
4158
|
+
// Also check PATH — piper may be installed outside the standard location
|
|
4159
|
+
if (isPiperInstalled()) {
|
|
4160
|
+
console.log(chalk.green('✓ Piper TTS is already available in PATH\n'));
|
|
4161
|
+
return;
|
|
4162
|
+
}
|
|
4163
|
+
|
|
4129
4164
|
spinner.start('Downloading Piper TTS for Windows...');
|
|
4130
4165
|
try {
|
|
4131
4166
|
await fs.mkdir(piperDir, { recursive: true });
|
|
@@ -4237,7 +4272,7 @@ async function handleMcpConfiguration(targetDir, options) {
|
|
|
4237
4272
|
chalk.white('to your ') + chalk.cyan('mcpServers') + chalk.white(' section:'),
|
|
4238
4273
|
{
|
|
4239
4274
|
padding: 1,
|
|
4240
|
-
margin: 1,
|
|
4275
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4241
4276
|
borderStyle: 'round',
|
|
4242
4277
|
borderColor: 'yellow',
|
|
4243
4278
|
}
|
|
@@ -4260,7 +4295,7 @@ async function handleMcpConfiguration(targetDir, options) {
|
|
|
4260
4295
|
chalk.cyan.bold('https://github.com/paulpreibisch/AgentVibes#mcp-server'),
|
|
4261
4296
|
{
|
|
4262
4297
|
padding: 1,
|
|
4263
|
-
margin: 1,
|
|
4298
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4264
4299
|
borderStyle: 'round',
|
|
4265
4300
|
borderColor: 'cyan',
|
|
4266
4301
|
}
|
|
@@ -4280,7 +4315,7 @@ async function handleMcpConfiguration(targetDir, options) {
|
|
|
4280
4315
|
chalk.white('No ') + chalk.cyan('.mcp.json') + chalk.white(' found in this project.'),
|
|
4281
4316
|
{
|
|
4282
4317
|
padding: 1,
|
|
4283
|
-
margin: 1,
|
|
4318
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4284
4319
|
borderStyle: 'round',
|
|
4285
4320
|
borderColor: 'cyan',
|
|
4286
4321
|
}
|
|
@@ -4315,12 +4350,15 @@ async function handleMcpConfiguration(targetDir, options) {
|
|
|
4315
4350
|
chalk.green('The MCP server is now installed and ready to use!'),
|
|
4316
4351
|
{
|
|
4317
4352
|
padding: 1,
|
|
4318
|
-
margin: 1,
|
|
4353
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4319
4354
|
borderStyle: 'double',
|
|
4320
4355
|
borderColor: 'green',
|
|
4321
4356
|
}
|
|
4322
4357
|
)
|
|
4323
4358
|
);
|
|
4359
|
+
|
|
4360
|
+
// Show the installed JSON so users can see exactly what was written
|
|
4361
|
+
console.log(chalk.gray(JSON.stringify(mcpConfig, null, 2)) + '\n');
|
|
4324
4362
|
} catch (error) {
|
|
4325
4363
|
console.log(chalk.red(`\n✗ Failed to create .mcp.json: ${error.message}`));
|
|
4326
4364
|
console.log(chalk.gray(' You can create it manually with the config shown below.\n'));
|
|
@@ -4337,7 +4375,7 @@ async function handleMcpConfiguration(targetDir, options) {
|
|
|
4337
4375
|
chalk.white('Create a ') + chalk.cyan('.mcp.json') + chalk.white(' file in your project with:'),
|
|
4338
4376
|
{
|
|
4339
4377
|
padding: 1,
|
|
4340
|
-
margin: 1,
|
|
4378
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4341
4379
|
borderStyle: 'round',
|
|
4342
4380
|
borderColor: 'cyan',
|
|
4343
4381
|
}
|
|
@@ -4366,7 +4404,7 @@ async function handleMcpConfiguration(targetDir, options) {
|
|
|
4366
4404
|
chalk.cyan.bold('https://github.com/paulpreibisch/AgentVibes#mcp-server'),
|
|
4367
4405
|
{
|
|
4368
4406
|
padding: 1,
|
|
4369
|
-
margin: 1,
|
|
4407
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4370
4408
|
borderStyle: 'round',
|
|
4371
4409
|
borderColor: 'cyan',
|
|
4372
4410
|
}
|
|
@@ -5314,6 +5352,12 @@ Troubleshooting:
|
|
|
5314
5352
|
await fs.writeFile(langConfigPath, lang, { mode: 0o600 });
|
|
5315
5353
|
}
|
|
5316
5354
|
|
|
5355
|
+
// Default translation to auto: syncs with BMAD communication_language if set, otherwise no translation
|
|
5356
|
+
const translateFile = path.join(claudeDir, 'tts-translate-to.txt');
|
|
5357
|
+
try { await fs.access(translateFile); } catch {
|
|
5358
|
+
await fs.writeFile(translateFile, 'auto', { mode: 0o600 });
|
|
5359
|
+
}
|
|
5360
|
+
|
|
5317
5361
|
// Apply verbosity, personality, pretext
|
|
5318
5362
|
await fs.writeFile(path.join(claudeDir, 'tts-verbosity.txt'), userConfig.verbosity);
|
|
5319
5363
|
if (userConfig.personality && userConfig.personality !== 'none') {
|
|
@@ -5344,12 +5388,17 @@ Troubleshooting:
|
|
|
5344
5388
|
await createDefaultBmadVoiceAssignmentsProactive(targetDir);
|
|
5345
5389
|
await handleBmadIntegration(targetDir, { ...options, yes: true });
|
|
5346
5390
|
|
|
5347
|
-
spinner.succeed(chalk.green('AgentVibes installed successfully!'));
|
|
5348
|
-
|
|
5349
5391
|
if (options.nonInteractive || process.env.AGENT_VIBES_NON_INTERACTIVE === '1') {
|
|
5350
|
-
console.log(`[AV] Installation complete`);
|
|
5351
5392
|
console.log(`[AV] Provider: ${selectedProvider} | Location: ${targetDir}/.claude/ | Version: ${VERSION}`);
|
|
5352
|
-
}
|
|
5393
|
+
}
|
|
5394
|
+
|
|
5395
|
+
console.log('');
|
|
5396
|
+
spinner.succeed(chalk.green('AgentVibes installed successfully!'));
|
|
5397
|
+
console.log('');
|
|
5398
|
+
console.log(chalk.magenta(' \u2661 Sponsor this Developer github.com/sponsors/paulpreibisch'));
|
|
5399
|
+
console.log('');
|
|
5400
|
+
|
|
5401
|
+
if (!(options.nonInteractive || process.env.AGENT_VIBES_NON_INTERACTIVE === '1')) {
|
|
5353
5402
|
// Clean final summary
|
|
5354
5403
|
console.log('');
|
|
5355
5404
|
console.log(chalk.green.bold(' ✅ Installation Complete'));
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
true
|