settings.json written by Notepad or VS Code on Windows can carry a UTF-8 BOM. JSON.parse then throws SyntaxError, the outer catch swallows it, hasStatusline stays false, and the statusline setup nudge is never emitted. Strip the leading BOM before parsing, matching the existing handling in ponytail-mode-tracker.js. (#96 added a null guard but not BOM stripping.)
75 lines
2.5 KiB
JavaScript
75 lines
2.5 KiB
JavaScript
#!/usr/bin/env node
|
|
// ponytail — Claude Code SessionStart activation hook
|
|
//
|
|
// Runs on every session start:
|
|
// 1. Writes flag file at ~/.claude/.ponytail-active (statusline reads this)
|
|
// 2. Emits ponytail ruleset as hidden SessionStart context
|
|
// 3. Detects missing statusline config and emits setup nudge
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { getDefaultMode, getClaudeDir } = require('./ponytail-config');
|
|
const { getPonytailInstructions } = require('./ponytail-instructions');
|
|
const {
|
|
clearMode,
|
|
isCodex,
|
|
setMode,
|
|
writeHookOutput,
|
|
} = require('./ponytail-runtime');
|
|
|
|
const claudeDir = getClaudeDir();
|
|
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
|
|
const mode = getDefaultMode();
|
|
|
|
// "off" mode — skip activation entirely, don't write flag or emit rules
|
|
if (mode === 'off') {
|
|
clearMode();
|
|
writeHookOutput('SessionStart', 'off', isCodex ? '' : 'OK');
|
|
process.exit(0);
|
|
}
|
|
|
|
// 1. Write flag file
|
|
try {
|
|
setMode(mode);
|
|
} catch (e) {
|
|
// Silent fail -- flag is best-effort, don't block the hook
|
|
}
|
|
|
|
// 2. Emit the ponytail ruleset, filtered to the active intensity level.
|
|
let output = getPonytailInstructions(mode);
|
|
|
|
// 3. Detect missing statusline config — nudge Claude to help set it up
|
|
if (!isCodex) try {
|
|
let hasStatusline = false;
|
|
if (fs.existsSync(settingsPath)) {
|
|
// Strip UTF-8 BOM some editors prepend on Windows (breaks JSON.parse)
|
|
const raw = fs.readFileSync(settingsPath, 'utf8').replace(/^\uFEFF/, '');
|
|
const settings = JSON.parse(raw);
|
|
if (settings.statusLine) {
|
|
hasStatusline = true;
|
|
}
|
|
}
|
|
|
|
if (!hasStatusline) {
|
|
const isWindows = process.platform === 'win32';
|
|
const scriptName = isWindows ? 'ponytail-statusline.ps1' : 'ponytail-statusline.sh';
|
|
const scriptPath = path.join(__dirname, scriptName);
|
|
const command = isWindows
|
|
? `powershell -ExecutionPolicy Bypass -File "${scriptPath}"`
|
|
: `bash "${scriptPath}"`;
|
|
const statusLineSnippet =
|
|
'"statusLine": { "type": "command", "command": ' + JSON.stringify(command) + ' }';
|
|
output += "\n\n" +
|
|
"STATUSLINE SETUP NEEDED: The ponytail plugin includes a statusline badge showing active mode " +
|
|
"(e.g. [PONYTAIL], [PONYTAIL:ULTRA]). It is not configured yet. " +
|
|
"To enable, add this to ~/.claude/settings.json: " +
|
|
statusLineSnippet + " " +
|
|
"Proactively offer to set this up for the user on first interaction.";
|
|
}
|
|
} catch (e) {
|
|
// Silent fail — don't block session start over statusline detection
|
|
}
|
|
|
|
writeHookOutput('SessionStart', mode, output);
|