agentvibes 5.6.9 → 5.7.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/.agentvibes/config.json +3 -38
- package/.claude/commands/agent-vibes/provider.md +0 -0
- package/.claude/config/audio-effects.cfg +1 -1
- package/.claude/config/background-music-position.txt +6 -8
- package/.claude/config/reverb-level.txt +0 -0
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/bmad-tts-injector.sh +49 -21
- package/.claude/hooks/provider-commands.sh +16 -4
- package/.claude/hooks/provider-manager.sh +38 -0
- package/.claude/hooks/stop.sh +2 -27
- package/.claude/hooks/voice-manager.sh +50 -2
- package/.claude/hooks-windows/play-tts.ps1 +34 -1
- package/.claude/hooks-windows/tts-watcher.ps1 +122 -0
- package/.claude/piper-voices-dir.txt +1 -1
- package/.mcp.json +13 -33
- package/README.md +6 -8
- package/RELEASE_NOTES.md +32 -0
- package/bin/agent-vibes +39 -39
- package/package.json +1 -1
- package/src/bmad-detector.js +85 -71
- package/src/cli/list-personalities.js +110 -110
- package/src/cli/list-voices.js +114 -114
- package/src/commands/bmad-voices.js +394 -394
- package/src/commands/install-mcp.js +476 -476
- package/src/console/brand-colors.js +13 -13
- package/src/console/constants/personalities.js +44 -44
- package/src/console/tabs/help-tab.js +314 -314
- package/src/console/tabs/readme-tab.js +272 -272
- package/src/console/widgets/destroy-list.js +25 -25
- package/src/console/widgets/notice.js +55 -55
- package/src/console/widgets/personality-picker.js +213 -213
- package/src/i18n/de.js +202 -202
- package/src/i18n/es.js +202 -202
- package/src/i18n/fr.js +202 -202
- package/src/i18n/hi.js +202 -202
- package/src/i18n/ja.js +202 -202
- package/src/i18n/ko.js +202 -202
- package/src/i18n/pt.js +202 -202
- package/src/i18n/strings.js +54 -54
- package/src/i18n/zh-CN.js +202 -202
- package/src/installer/language-screen.js +31 -31
- package/src/installer/music-file-input.js +304 -304
- package/src/installer.js +70 -7
- package/src/services/agent-voice-store.js +59 -12
- package/src/services/config-service.js +264 -264
- package/src/services/language-service.js +47 -47
- package/src/services/provider-service.js +143 -143
- package/src/utils/audio-duration-validator.js +298 -298
- package/src/utils/audio-format-validator.js +277 -277
- package/src/utils/dependency-checker.js +469 -469
- package/src/utils/file-ownership-verifier.js +358 -358
- package/src/utils/list-formatter.js +194 -194
- package/src/utils/music-file-validator.js +285 -285
- package/src/utils/preview-list-prompt.js +136 -136
- package/src/utils/secure-music-storage.js +412 -412
- package/.agentvibes/LITE-MODE.md +0 -236
- package/.agentvibes/README.md +0 -136
- package/.agentvibes/backup/session-start-tts.sh.20251210_212814 +0 -141
- package/.agentvibes/backups/agents/analyst_20260204_144958.md +0 -78
- package/.agentvibes/backups/agents/architect_20260204_144958.md +0 -72
- package/.agentvibes/backups/agents/dev_20260204_144958.md +0 -74
- package/.agentvibes/backups/agents/pm_20260204_144958.md +0 -72
- package/.agentvibes/backups/agents/quick-flow-solo-dev_20260204_144958.md +0 -64
- package/.agentvibes/backups/agents/sm_20260204_144958.md +0 -87
- package/.agentvibes/backups/agents/tea_20260204_144958.md +0 -79
- package/.agentvibes/backups/agents/tech-writer_20260204_144958.md +0 -82
- package/.agentvibes/backups/agents/ux-designer_20260204_144958.md +0 -80
- package/.agentvibes/config/README-personality-defaults.md +0 -162
- package/.agentvibes/config/agentvibes.json +0 -1
- package/.agentvibes/config/mode.txt +0 -1
- package/.agentvibes/config/personality-voice-defaults.default.json +0 -21
- package/.agentvibes/config/save-audio.txt +0 -1
- package/.agentvibes/config/voice-metadata.json +0 -160
- package/.agentvibes/hooks/help.sh +0 -191
- package/.agentvibes/hooks/post-tool-use-lite.sh +0 -111
- package/.agentvibes/hooks/save-audio-manager.sh +0 -162
- package/.agentvibes/hooks/session-start-full-optimized.sh +0 -102
- package/.agentvibes/hooks/session-start-full.sh +0 -142
- package/.agentvibes/hooks/session-start-lite-v2.sh +0 -34
- package/.agentvibes/hooks/session-start-lite.sh +0 -29
- package/.agentvibes/hooks/stop-lite.sh +0 -115
- package/.agentvibes/hooks/switch-mode.sh +0 -215
- package/.agentvibes/output-styles/audio-summary.md +0 -30
- package/.claude/audio/voice-samples/piper/alan.wav +0 -0
- package/.claude/audio/voice-samples/piper/amy.wav +0 -0
- package/.claude/audio/voice-samples/piper/charlotte.wav +0 -0
- package/.claude/audio/voice-samples/piper/joe.wav +0 -0
- package/.claude/audio/voice-samples/piper/john.wav +0 -0
- package/.claude/audio/voice-samples/piper/katherine.wav +0 -0
- package/.claude/audio/voice-samples/piper/kristin.wav +0 -0
- package/.claude/audio/voice-samples/piper/linda.wav +0 -0
- package/.claude/audio/voice-samples/piper/marcus.wav +0 -0
- package/.claude/audio/voice-samples/piper/ryan.wav +0 -0
- package/.claude/hooks/post-response.sh +0 -41
- package/bin/ensure-soprano-running.sh +0 -43
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[](https://github.com/paulpreibisch/AgentVibes/actions/workflows/publish.yml)
|
|
12
12
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
13
13
|
|
|
14
|
-
**Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v5.
|
|
14
|
+
**Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v5.7.0
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
@@ -40,17 +40,15 @@ Whether you're coding in Claude Code, chatting in Claude Desktop, using Warp Ter
|
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
43
|
-
## 🌟 NEW IN v5.
|
|
43
|
+
## 🌟 NEW IN v5.7.0 — BMAD v6.6 Support + Windows Auto-Restart Watcher
|
|
44
44
|
|
|
45
|
-
**
|
|
45
|
+
**BMAD v6.6.0:** AgentVibes now detects the new `.claude/skills/*/agents/` agent structure, correctly handles globally-installed BMAD at `~/_bmad`, and gracefully skips v6.6+ plain-Markdown agents during TTS injection instead of erroring. The BMAD tab now shows detection correctly for global installs.
|
|
46
46
|
|
|
47
|
-
**
|
|
47
|
+
**Windows watcher:** `tts-watcher.ps1` is now a standalone file at `~/.agentvibes/tts-watcher.ps1`. Running `npx agentvibes update` now copies the latest watcher **and** restarts it automatically — both the file and the process are updated in one step, no manual restart needed.
|
|
48
48
|
|
|
49
|
-
**
|
|
49
|
+
**Windows provider:** `play-tts.ps1` now respects the `ProviderOverride` from the Linux server config when receiving remote audio.
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
## v5.6.8 — WSL Voice Routing Fixed + Session Lifecycle Reliability
|
|
51
|
+
## v5.6.9 — Reverb & Background Music Silent in NPX Installs
|
|
54
52
|
|
|
55
53
|
**WSL users:** AgentVibes was playing `en_US-lessac-medium` regardless of your configured voice. Fixed — Piper is now found in non-interactive shells by explicitly prepending `~/.local/bin` to `PATH` before the binary check.
|
|
56
54
|
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# AgentVibes Release Notes
|
|
2
2
|
|
|
3
|
+
## 🎭 v5.7.0 — BMAD v6.6 Support + Windows Auto-Restart Watcher
|
|
4
|
+
|
|
5
|
+
**Released:** 2026-05-11
|
|
6
|
+
|
|
7
|
+
### 🆕 BMAD v6.6.0 Compatibility
|
|
8
|
+
|
|
9
|
+
BMAD v6.6 restructured where agents live — they moved from `_bmad/bmm/agents/` to `.claude/skills/*/agents/`. AgentVibes now detects and scans these new paths correctly.
|
|
10
|
+
|
|
11
|
+
**TTS injection** gracefully skips v6.6+ agents (which use plain Markdown without XML/YAML activation sections) instead of throwing errors. The install summary now clearly reports how many agents were skipped vs. modified.
|
|
12
|
+
|
|
13
|
+
**BMAD tab detection** now finds globally-installed BMAD at `~/_bmad` (home-dir install) in addition to project-local installs. Previously the BMAD tab showed "Not detected" even when BMAD was installed globally.
|
|
14
|
+
|
|
15
|
+
**Security:** The installer's path validation now correctly permits BMAD paths under the user's home directory, fixing a false-positive "Invalid BMAD path" error for global installs.
|
|
16
|
+
|
|
17
|
+
### 🆕 Windows TTS Watcher — Standalone File + Auto-Restart
|
|
18
|
+
|
|
19
|
+
`tts-watcher.ps1` is now extracted to `~/.agentvibes/tts-watcher.ps1` as a standalone file. Running `npx agentvibes update` now copies the latest watcher to that location **and** automatically restarts it — so both the watcher script itself and its running process are updated in one step. No manual file replacement or restart needed after updates.
|
|
20
|
+
|
|
21
|
+
### 🐛 Windows Provider Override Respected on Laptop
|
|
22
|
+
|
|
23
|
+
`play-tts.ps1` now reads the `ProviderOverride` setting from the Linux-side config when receiving audio via SSH. Previously the laptop always used its locally-configured provider even if the server specified a different one.
|
|
24
|
+
|
|
25
|
+
### 🐛 Sample Command Added to Voice Manager
|
|
26
|
+
|
|
27
|
+
`voice-manager.sh sample` was missing its handler — calling it fell through to the usage/exit path silently. Fixed.
|
|
28
|
+
|
|
29
|
+
### 🐛 Preview SSH Routing Detects Correct Endpoint
|
|
30
|
+
|
|
31
|
+
`provider-manager.sh` now includes `detect_routing_llm()` which checks `AGENTVIBES_LLM_KEY` then `transport-config.json` for the first `mode=remote` entry, so preview audio reaches the correct SSH host.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
3
35
|
## 🔇 v5.6.9 — Reverb & Background Music Silent in NPX Installs
|
|
4
36
|
|
|
5
37
|
**Released:** 2026-05-09
|
package/bin/agent-vibes
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* AgentVibes - Beautiful ElevenLabs TTS voice commands for Claude Code
|
|
5
|
-
* This file ensures proper execution when run via npx
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { execFileSync } from 'node:child_process';
|
|
9
|
-
import path from 'node:path';
|
|
10
|
-
import fs from 'node:fs';
|
|
11
|
-
import { fileURLToPath } from 'node:url';
|
|
12
|
-
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = path.dirname(__filename);
|
|
15
|
-
|
|
16
|
-
// Check if we're running in an npx temporary directory
|
|
17
|
-
const isNpxExecution = __dirname.includes('_npx') || __dirname.includes('.npm');
|
|
18
|
-
|
|
19
|
-
// Get CLI arguments
|
|
20
|
-
const arguments_ = process.argv.slice(2);
|
|
21
|
-
|
|
22
|
-
// Route through the TUI console (agentvibes.js) which handles install/config/etc
|
|
23
|
-
const installerPath = path.join(__dirname, 'agentvibes.js');
|
|
24
|
-
|
|
25
|
-
if (!fs.existsSync(installerPath)) {
|
|
26
|
-
console.error('Error: Could not find installer.js at', installerPath);
|
|
27
|
-
console.error('Current directory:', __dirname);
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
// Security: Use execFileSync with array args to prevent command injection
|
|
33
|
-
// Arguments are passed as array elements, not string interpolation
|
|
34
|
-
execFileSync('node', [installerPath, ...arguments_], {
|
|
35
|
-
stdio: 'inherit',
|
|
36
|
-
cwd: path.dirname(__dirname),
|
|
37
|
-
});
|
|
38
|
-
} catch (error) {
|
|
39
|
-
process.exit(error.status || 1);
|
|
40
|
-
}
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AgentVibes - Beautiful ElevenLabs TTS voice commands for Claude Code
|
|
5
|
+
* This file ensures proper execution when run via npx
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execFileSync } from 'node:child_process';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
|
|
16
|
+
// Check if we're running in an npx temporary directory
|
|
17
|
+
const isNpxExecution = __dirname.includes('_npx') || __dirname.includes('.npm');
|
|
18
|
+
|
|
19
|
+
// Get CLI arguments
|
|
20
|
+
const arguments_ = process.argv.slice(2);
|
|
21
|
+
|
|
22
|
+
// Route through the TUI console (agentvibes.js) which handles install/config/etc
|
|
23
|
+
const installerPath = path.join(__dirname, 'agentvibes.js');
|
|
24
|
+
|
|
25
|
+
if (!fs.existsSync(installerPath)) {
|
|
26
|
+
console.error('Error: Could not find installer.js at', installerPath);
|
|
27
|
+
console.error('Current directory:', __dirname);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
// Security: Use execFileSync with array args to prevent command injection
|
|
33
|
+
// Arguments are passed as array elements, not string interpolation
|
|
34
|
+
execFileSync('node', [installerPath, ...arguments_], {
|
|
35
|
+
stdio: 'inherit',
|
|
36
|
+
cwd: path.dirname(__dirname),
|
|
37
|
+
});
|
|
38
|
+
} catch (error) {
|
|
39
|
+
process.exit(error.status || 1);
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "agentvibes",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.7.0",
|
|
5
5
|
"description": "Now your AI Agents can finally talk back! Professional TTS voice for Claude Code, Claude Desktop (via MCP), and Clawdbot with multi-provider support.",
|
|
6
6
|
"homepage": "https://agentvibes.org",
|
|
7
7
|
"keywords": [
|
package/src/bmad-detector.js
CHANGED
|
@@ -1,71 +1,85 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import fs from 'node:fs/promises';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
* @
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import yaml from 'js-yaml';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Detect BMAD installation and version
|
|
8
|
+
* @param {string} targetDir - Directory to check
|
|
9
|
+
* @returns {Promise<Object>} Detection result with version info
|
|
10
|
+
*/
|
|
11
|
+
export async function detectBMAD(targetDir) {
|
|
12
|
+
// Check v6 first (newer version).
|
|
13
|
+
// Search order: project-local variants first, then home-dir (_bmad is the current BMAD installer default)
|
|
14
|
+
const homeDir = os.homedir();
|
|
15
|
+
const v6Candidates = [
|
|
16
|
+
// Project-local checks first
|
|
17
|
+
{ manifest: path.join(targetDir, '.bmad/_cfg/manifest.yaml'), bmadPath: '.bmad' },
|
|
18
|
+
{ manifest: path.join(targetDir, 'bmad/_cfg/manifest.yaml'), bmadPath: 'bmad' },
|
|
19
|
+
{ manifest: path.join(targetDir, '_bmad/_cfg/manifest.yaml'), bmadPath: '_bmad' },
|
|
20
|
+
{ manifest: path.join(targetDir, '_bmad/_config/manifest.yaml'), bmadPath: '_bmad' },
|
|
21
|
+
// Home-dir installs (global BMAD not inside a project) — detected but NOT injected
|
|
22
|
+
{ manifest: path.join(homeDir, '_bmad/_config/manifest.yaml'), bmadPath: '_bmad', root: homeDir, isGlobal: true },
|
|
23
|
+
{ manifest: path.join(homeDir, '_bmad/_cfg/manifest.yaml'), bmadPath: '_bmad', root: homeDir, isGlobal: true },
|
|
24
|
+
{ manifest: path.join(homeDir, '.bmad/_cfg/manifest.yaml'), bmadPath: '.bmad', root: homeDir, isGlobal: true },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
let v6Manifest = null;
|
|
28
|
+
let bmadPath = null;
|
|
29
|
+
let bmadRoot = targetDir;
|
|
30
|
+
let isGlobal = false;
|
|
31
|
+
|
|
32
|
+
for (const candidate of v6Candidates) {
|
|
33
|
+
try {
|
|
34
|
+
await fs.access(candidate.manifest);
|
|
35
|
+
v6Manifest = candidate.manifest;
|
|
36
|
+
bmadPath = candidate.bmadPath;
|
|
37
|
+
bmadRoot = candidate.root ?? targetDir;
|
|
38
|
+
isGlobal = candidate.isGlobal ?? false;
|
|
39
|
+
break;
|
|
40
|
+
} catch { /* try next */ }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (bmadPath) {
|
|
44
|
+
try {
|
|
45
|
+
const manifestContent = await fs.readFile(v6Manifest, 'utf8');
|
|
46
|
+
const manifest = yaml.load(manifestContent);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
version: 6,
|
|
50
|
+
detailedVersion: manifest.installation?.version || '6.0.0-alpha.x',
|
|
51
|
+
manifestPath: v6Manifest,
|
|
52
|
+
configPath: path.join(bmadRoot, bmadPath, 'core/config.yaml'),
|
|
53
|
+
bmadPath: path.join(bmadRoot, bmadPath),
|
|
54
|
+
installed: true,
|
|
55
|
+
isGlobal,
|
|
56
|
+
};
|
|
57
|
+
} catch {}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check v4 (legacy)
|
|
61
|
+
const v4Manifest = path.join(targetDir, '.bmad-core/install-manifest.yaml');
|
|
62
|
+
try {
|
|
63
|
+
await fs.access(v4Manifest);
|
|
64
|
+
return {
|
|
65
|
+
version: 4,
|
|
66
|
+
detailedVersion: '4.x',
|
|
67
|
+
manifestPath: v4Manifest,
|
|
68
|
+
configPath: path.join(targetDir, '.bmad-core/config.yaml'),
|
|
69
|
+
bmadPath: path.join(targetDir, '.bmad-core'),
|
|
70
|
+
installed: true
|
|
71
|
+
};
|
|
72
|
+
} catch {}
|
|
73
|
+
|
|
74
|
+
// Not installed
|
|
75
|
+
return { version: null, installed: false };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get BMAD configuration file path for detected version
|
|
80
|
+
* @param {Object} detection - Result from detectBMAD()
|
|
81
|
+
* @returns {string|null} Path to config.yaml or null
|
|
82
|
+
*/
|
|
83
|
+
export function getBMADConfigPath(detection) {
|
|
84
|
+
return detection.installed ? detection.configPath : null;
|
|
85
|
+
}
|
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Personality List Display - Beautiful multi-column personality listing
|
|
4
|
-
* Called by personality-manager.sh to display personalities with boxen formatting
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { formatPersonalitiesList } from '../utils/list-formatter.js';
|
|
8
|
-
import fs from 'fs';
|
|
9
|
-
import path from 'path';
|
|
10
|
-
import os from 'os';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Get personality description from markdown file
|
|
14
|
-
*/
|
|
15
|
-
function getPersonalityDescription(filePath) {
|
|
16
|
-
try {
|
|
17
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
18
|
-
|
|
19
|
-
// Try to extract description from frontmatter or first paragraph
|
|
20
|
-
const descMatch = content.match(/description:\s*(.+)/i);
|
|
21
|
-
if (descMatch) {
|
|
22
|
-
return descMatch[1].trim();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Try to get first line after frontmatter
|
|
26
|
-
const lines = content.split('\n');
|
|
27
|
-
let inFrontmatter = false;
|
|
28
|
-
let frontmatterCount = 0;
|
|
29
|
-
|
|
30
|
-
for (const line of lines) {
|
|
31
|
-
if (line.trim() === '---') {
|
|
32
|
-
frontmatterCount++;
|
|
33
|
-
inFrontmatter = frontmatterCount === 1;
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (!inFrontmatter && frontmatterCount >= 2 && line.trim()) {
|
|
38
|
-
// First non-empty line after frontmatter
|
|
39
|
-
return line.trim().replace(/^#+\s*/, '').substring(0, 50);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return '';
|
|
44
|
-
} catch (error) {
|
|
45
|
-
return '';
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Get all personalities from directory
|
|
51
|
-
*/
|
|
52
|
-
function getPersonalities(personalitiesDir, currentPersonality) {
|
|
53
|
-
const personalities = [];
|
|
54
|
-
|
|
55
|
-
if (!fs.existsSync(personalitiesDir)) {
|
|
56
|
-
return personalities;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const files = fs.readdirSync(personalitiesDir);
|
|
60
|
-
for (const file of files) {
|
|
61
|
-
if (file.endsWith('.md')) {
|
|
62
|
-
const name = path.basename(file, '.md');
|
|
63
|
-
const filePath = path.join(personalitiesDir, file);
|
|
64
|
-
const description = getPersonalityDescription(filePath);
|
|
65
|
-
|
|
66
|
-
personalities.push({
|
|
67
|
-
name,
|
|
68
|
-
description,
|
|
69
|
-
current: name === currentPersonality
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Add special 'random' option
|
|
75
|
-
personalities.push({
|
|
76
|
-
name: 'random',
|
|
77
|
-
description: 'Picks randomly each time',
|
|
78
|
-
current: currentPersonality === 'random'
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
return personalities.sort((a, b) => {
|
|
82
|
-
// Keep 'random' at the end
|
|
83
|
-
if (a.name === 'random') return 1;
|
|
84
|
-
if (b.name === 'random') return -1;
|
|
85
|
-
return a.name.localeCompare(b.name);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Main function
|
|
91
|
-
*/
|
|
92
|
-
function main() {
|
|
93
|
-
const args = process.argv.slice(2);
|
|
94
|
-
|
|
95
|
-
// Parse arguments
|
|
96
|
-
const personalitiesDir = args[0] || path.join(os.homedir(), '.claude', 'personalities');
|
|
97
|
-
const currentPersonality = args[1] || 'normal';
|
|
98
|
-
|
|
99
|
-
const personalities = getPersonalities(personalitiesDir, currentPersonality);
|
|
100
|
-
|
|
101
|
-
// Display with boxen
|
|
102
|
-
const output = formatPersonalitiesList(personalities, {
|
|
103
|
-
columns: 2,
|
|
104
|
-
showUsage: true
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
console.log(output);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Personality List Display - Beautiful multi-column personality listing
|
|
4
|
+
* Called by personality-manager.sh to display personalities with boxen formatting
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { formatPersonalitiesList } from '../utils/list-formatter.js';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import os from 'os';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get personality description from markdown file
|
|
14
|
+
*/
|
|
15
|
+
function getPersonalityDescription(filePath) {
|
|
16
|
+
try {
|
|
17
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
18
|
+
|
|
19
|
+
// Try to extract description from frontmatter or first paragraph
|
|
20
|
+
const descMatch = content.match(/description:\s*(.+)/i);
|
|
21
|
+
if (descMatch) {
|
|
22
|
+
return descMatch[1].trim();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Try to get first line after frontmatter
|
|
26
|
+
const lines = content.split('\n');
|
|
27
|
+
let inFrontmatter = false;
|
|
28
|
+
let frontmatterCount = 0;
|
|
29
|
+
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
if (line.trim() === '---') {
|
|
32
|
+
frontmatterCount++;
|
|
33
|
+
inFrontmatter = frontmatterCount === 1;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!inFrontmatter && frontmatterCount >= 2 && line.trim()) {
|
|
38
|
+
// First non-empty line after frontmatter
|
|
39
|
+
return line.trim().replace(/^#+\s*/, '').substring(0, 50);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return '';
|
|
44
|
+
} catch (error) {
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get all personalities from directory
|
|
51
|
+
*/
|
|
52
|
+
function getPersonalities(personalitiesDir, currentPersonality) {
|
|
53
|
+
const personalities = [];
|
|
54
|
+
|
|
55
|
+
if (!fs.existsSync(personalitiesDir)) {
|
|
56
|
+
return personalities;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const files = fs.readdirSync(personalitiesDir);
|
|
60
|
+
for (const file of files) {
|
|
61
|
+
if (file.endsWith('.md')) {
|
|
62
|
+
const name = path.basename(file, '.md');
|
|
63
|
+
const filePath = path.join(personalitiesDir, file);
|
|
64
|
+
const description = getPersonalityDescription(filePath);
|
|
65
|
+
|
|
66
|
+
personalities.push({
|
|
67
|
+
name,
|
|
68
|
+
description,
|
|
69
|
+
current: name === currentPersonality
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Add special 'random' option
|
|
75
|
+
personalities.push({
|
|
76
|
+
name: 'random',
|
|
77
|
+
description: 'Picks randomly each time',
|
|
78
|
+
current: currentPersonality === 'random'
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return personalities.sort((a, b) => {
|
|
82
|
+
// Keep 'random' at the end
|
|
83
|
+
if (a.name === 'random') return 1;
|
|
84
|
+
if (b.name === 'random') return -1;
|
|
85
|
+
return a.name.localeCompare(b.name);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Main function
|
|
91
|
+
*/
|
|
92
|
+
function main() {
|
|
93
|
+
const args = process.argv.slice(2);
|
|
94
|
+
|
|
95
|
+
// Parse arguments
|
|
96
|
+
const personalitiesDir = args[0] || path.join(os.homedir(), '.claude', 'personalities');
|
|
97
|
+
const currentPersonality = args[1] || 'normal';
|
|
98
|
+
|
|
99
|
+
const personalities = getPersonalities(personalitiesDir, currentPersonality);
|
|
100
|
+
|
|
101
|
+
// Display with boxen
|
|
102
|
+
const output = formatPersonalitiesList(personalities, {
|
|
103
|
+
columns: 2,
|
|
104
|
+
showUsage: true
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
console.log(output);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main();
|